diff --git a/android/bazel_handler.go b/android/bazel_handler.go index 5804a46ed..5e8a1839e 100644 --- a/android/bazel_handler.go +++ b/android/bazel_handler.go @@ -91,10 +91,6 @@ type configKey struct { osType OsType } -func (c configKey) String() string { - return fmt.Sprintf("%s::%s", c.arch, c.osType) -} - // Map key to describe bazel cquery requests. type cqueryKey struct { label string @@ -102,11 +98,6 @@ type cqueryKey struct { configKey configKey } -func (c cqueryKey) String() string { - return fmt.Sprintf("cquery(%s,%s,%s)", c.label, c.requestType.Name(), c.configKey) - -} - // BazelContext is a context object useful for interacting with Bazel during // the course of a build. Use of Bazel to evaluate part of the build graph // is referred to as a "mixed build". (Some modules are managed by Soong, @@ -132,9 +123,6 @@ type BazelContext interface { // TODO(b/232976601): Remove. GetPythonBinary(label string, cfgKey configKey) (string, error) - // Returns the results of the GetApexInfo query (including output files) - GetApexInfo(label string, cfgkey configKey) (cquery.ApexCqueryInfo, error) - // ** end Cquery Results Retrieval Functions // Issues commands to Bazel to receive results for all cquery requests @@ -198,7 +186,6 @@ type MockBazelContext struct { LabelToOutputFiles map[string][]string LabelToCcInfo map[string]cquery.CcInfo LabelToPythonBinary map[string]string - LabelToApexInfo map[string]cquery.ApexCqueryInfo } func (m MockBazelContext) QueueBazelRequest(_ string, _ cqueryRequest, _ configKey) { @@ -220,10 +207,6 @@ func (m MockBazelContext) GetPythonBinary(label string, _ configKey) (string, er return result, nil } -func (n MockBazelContext) GetApexInfo(_ string, _ configKey) (cquery.ApexCqueryInfo, error) { - panic("unimplemented") -} - func (m MockBazelContext) InvokeBazel(_ Config) error { panic("unimplemented") } @@ -278,14 +261,6 @@ func (bazelCtx *bazelContext) GetPythonBinary(label string, cfgKey configKey) (s return "", fmt.Errorf("no bazel response found for %v", key) } -func (bazelCtx *bazelContext) GetApexInfo(label string, cfgKey configKey) (cquery.ApexCqueryInfo, error) { - key := cqueryKey{label, cquery.GetApexInfo, cfgKey} - if rawString, ok := bazelCtx.results[key]; ok { - return cquery.GetApexInfo.ParseResult(strings.TrimSpace(rawString)), nil - } - return cquery.ApexCqueryInfo{}, fmt.Errorf("no bazel response found for %v", key) -} - func (n noopBazelContext) QueueBazelRequest(_ string, _ cqueryRequest, _ configKey) { panic("unimplemented") } @@ -302,10 +277,6 @@ func (n noopBazelContext) GetPythonBinary(_ string, _ configKey) (string, error) panic("unimplemented") } -func (n noopBazelContext) GetApexInfo(_ string, _ configKey) (cquery.ApexCqueryInfo, error) { - panic("unimplemented") -} - func (n noopBazelContext) InvokeBazel(_ Config) error { panic("unimplemented") } @@ -430,9 +401,11 @@ func (r *builtinBazelRunner) issueBazelCommand(paths *bazelPaths, runName bazel. cmdFlags := []string{ "--output_base=" + absolutePath(paths.outputBase), command.command, - command.expression, + } + cmdFlags = append(cmdFlags, command.expression) + cmdFlags = append(cmdFlags, // TODO(asmundak): is it needed in every build? - "--profile=" + shared.BazelMetricsFilename(paths, runName), + "--profile="+shared.BazelMetricsFilename(paths, runName), // Set default platforms to canonicalized values for mixed builds requests. // If these are set in the bazelrc, they will have values that are @@ -453,23 +426,21 @@ func (r *builtinBazelRunner) issueBazelCommand(paths *bazelPaths, runName bazel. // Suppress noise "--ui_event_filters=-INFO", - "--noshow_progress"} + "--noshow_progress") cmdFlags = append(cmdFlags, extraFlags...) bazelCmd := exec.Command(paths.bazelPath, cmdFlags...) bazelCmd.Dir = absolutePath(paths.syntheticWorkspaceDir()) - extraEnv := []string{ - "HOME=" + paths.homeDir, + bazelCmd.Env = append(os.Environ(), + "HOME="+paths.homeDir, pwdPrefix(), - "BUILD_DIR=" + absolutePath(paths.soongOutDir), + "BUILD_DIR="+absolutePath(paths.soongOutDir), // Make OUT_DIR absolute here so tools/bazel.sh uses the correct // OUT_DIR at /out, instead of /out/soong/workspace/out. - "OUT_DIR=" + absolutePath(paths.outDir()), + "OUT_DIR="+absolutePath(paths.outDir()), // Disables local host detection of gcc; toolchain information is defined // explicitly in BUILD files. - "BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1", - } - bazelCmd.Env = append(os.Environ(), extraEnv...) + "BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1") stderr := &bytes.Buffer{} bazelCmd.Stderr = stderr @@ -680,15 +651,6 @@ def get_arch(target): fail("expected platform name of the form 'android_' or 'linux_', but was " + str(platforms)) return "UNKNOWN" -def json_for_file(key, file): - return '"' + key + '":"' + file.path + '"' - -def json_for_files(key, files): - return '"' + key + '":[' + ",".join(['"' + f.path + '"' for f in files]) + ']' - -def json_for_labels(key, ll): - return '"' + key + '":[' + ",".join(['"' + str(x) + '"' for x in ll]) + ']' - def format(target): id_string = str(target.label) + "|" + get_arch(target) @@ -766,7 +728,7 @@ func (context *bazelContext) InvokeBazel(config Config) error { cqueryOutput, cqueryErr, err := context.issueBazelCommand(context.paths, bazel.CqueryBuildRootRunName, cqueryCmd, "--output=starlark", "--starlark:file="+absolutePath(cqueryFileRelpath)) if err != nil { - _ = ioutil.WriteFile(filepath.Join(soongInjectionPath, "cquery.out"), []byte(cqueryOutput), 0666) + err = ioutil.WriteFile(filepath.Join(soongInjectionPath, "cquery.out"), []byte(cqueryOutput), 0666) } if err != nil { return err diff --git a/apex/apex.go b/apex/apex.go index dd32fcbd3..d1462d0af 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -17,7 +17,6 @@ package apex import ( - "android/soong/bazel/cquery" "fmt" "path/filepath" "regexp" @@ -1804,181 +1803,6 @@ func (f fsType) string() string { } } -var _ android.MixedBuildBuildable = (*apexBundle)(nil) - -func (a *apexBundle) IsMixedBuildSupported(ctx android.BaseModuleContext) bool { - return ctx.ModuleType() == "apex" && a.properties.ApexType == imageApex -} - -func (a *apexBundle) QueueBazelCall(ctx android.BaseModuleContext) { - bazelCtx := ctx.Config().BazelContext - bazelCtx.QueueBazelRequest(a.GetBazelLabel(ctx, a), cquery.GetApexInfo, android.GetConfigKey(ctx)) -} - -func (a *apexBundle) ProcessBazelQueryResponse(ctx android.ModuleContext) { - if !a.commonBuildActions(ctx) { - return - } - - a.setApexTypeAndSuffix(ctx) - a.setPayloadFsType(ctx) - a.setSystemLibLink(ctx) - - if a.properties.ApexType != zipApex { - a.compatSymlinks = makeCompatSymlinks(a.BaseModuleName(), ctx, a.primaryApexType) - } - - bazelCtx := ctx.Config().BazelContext - outputs, err := bazelCtx.GetApexInfo(a.GetBazelLabel(ctx, a), android.GetConfigKey(ctx)) - if err != nil { - ctx.ModuleErrorf(err.Error()) - return - } - a.installDir = android.PathForModuleInstall(ctx, "apex") - a.outputApexFile = android.PathForBazelOut(ctx, outputs.SignedOutput) - a.outputFile = a.outputApexFile - a.setCompression(ctx) - - a.publicKeyFile = android.PathForBazelOut(ctx, outputs.BundleKeyPair[0]) - a.privateKeyFile = android.PathForBazelOut(ctx, outputs.BundleKeyPair[1]) - a.containerCertificateFile = android.PathForBazelOut(ctx, outputs.ContainerKeyPair[0]) - a.containerPrivateKeyFile = android.PathForBazelOut(ctx, outputs.ContainerKeyPair[1]) - apexType := a.properties.ApexType - switch apexType { - case imageApex: - // TODO(asmundak): Bazel does not create these files yet. - // b/190817312 - a.htmlGzNotice = android.PathForBazelOut(ctx, "NOTICE.html.gz") - // b/239081457 - a.bundleModuleFile = android.PathForBazelOut(ctx, a.Name()+apexType.suffix()+"-base.zip") - // b/239081455 - a.nativeApisUsedByModuleFile = android.ModuleOutPath(android.PathForBazelOut(ctx, a.Name()+"_using.txt")) - // b/239081456 - a.nativeApisBackedByModuleFile = android.ModuleOutPath(android.PathForBazelOut(ctx, a.Name()+"_backing.txt")) - // b/239084755 - a.javaApisUsedByModuleFile = android.ModuleOutPath(android.PathForBazelOut(ctx, a.Name()+"_using.xml")) - a.installedFile = ctx.InstallFile(a.installDir, a.Name()+a.installSuffix(), a.outputFile, - a.compatSymlinks.Paths()...) - default: - panic(fmt.Errorf("unexpected apex_type for the ProcessBazelQuery: %v", a.properties.ApexType)) - } - - /* - TODO(asmundak): compared to building an APEX with Soong, building it with Bazel does not - return filesInfo and requiredDeps fields (in the Soong build the latter is updated). - Fix this, as these fields are subsequently used in apex/androidmk.go and in apex/builder/go - To find out what Soong build puts there, run: - vctx := visitorContext{handleSpecialLibs: !android.Bool(a.properties.Ignore_system_library_special_case)} - ctx.WalkDepsBlueprint(func(child, parent blueprint.Module) bool { - return a.depVisitor(&vctx, ctx, child, parent) - }) - vctx.normalizeFileInfo() - */ - -} - -func (a *apexBundle) setCompression(ctx android.ModuleContext) { - a.isCompressed = (a.properties.ApexType == imageApex) && - ((ctx.Config().CompressedApex() && - proptools.BoolDefault(a.overridableProperties.Compressible, false) && - !a.testApex && !ctx.Config().UnbundledBuildApps()) || - a.testOnlyShouldForceCompression()) -} - -func (a apexBundle) installSuffix() string { - if a.isCompressed { - return imageCapexSuffix - } - return imageApexSuffix -} - -func (a *apexBundle) setSystemLibLink(ctx android.ModuleContext) { - // Optimization. If we are building bundled APEX, for the files that are gathered due to the - // transitive dependencies, don't place them inside the APEX, but place a symlink pointing - // the same library in the system partition, thus effectively sharing the same libraries - // across the APEX boundary. For unbundled APEX, all the gathered files are actually placed - // in the APEX. - a.linkToSystemLib = !ctx.Config().UnbundledBuild() && a.installable() - - // APEXes targeting other than system/system_ext partitions use vendor/product variants. - // So we can't link them to /system/lib libs which are core variants. - if a.SocSpecific() || a.DeviceSpecific() || (a.ProductSpecific() && ctx.Config().EnforceProductPartitionInterface()) { - a.linkToSystemLib = false - } - - forced := ctx.Config().ForceApexSymlinkOptimization() - updatable := a.Updatable() || a.FutureUpdatable() - - // We don't need the optimization for updatable APEXes, as it might give false signal - // to the system health when the APEXes are still bundled (b/149805758). - if !forced && updatable && a.properties.ApexType == imageApex { - a.linkToSystemLib = false - } - - // We also don't want the optimization for host APEXes, because it doesn't make sense. - if ctx.Host() { - a.linkToSystemLib = false - } -} - -func (a *apexBundle) setPayloadFsType(ctx android.ModuleContext) { - switch proptools.StringDefault(a.properties.Payload_fs_type, ext4FsType) { - case ext4FsType: - a.payloadFsType = ext4 - case f2fsFsType: - a.payloadFsType = f2fs - case erofsFsType: - a.payloadFsType = erofs - default: - ctx.PropertyErrorf("payload_fs_type", "%q is not a valid filesystem for apex [ext4, f2fs, erofs]", *a.properties.Payload_fs_type) - } -} - -func (a *apexBundle) setApexTypeAndSuffix(ctx android.ModuleContext) { - // Set suffix and primaryApexType depending on the ApexType - buildFlattenedAsDefault := ctx.Config().FlattenApex() - switch a.properties.ApexType { - case imageApex: - if buildFlattenedAsDefault { - a.suffix = imageApexSuffix - } else { - a.suffix = "" - a.primaryApexType = true - - if ctx.Config().InstallExtraFlattenedApexes() { - a.requiredDeps = append(a.requiredDeps, a.Name()+flattenedSuffix) - } - } - case zipApex: - if proptools.String(a.properties.Payload_type) == "zip" { - a.suffix = "" - a.primaryApexType = true - } else { - a.suffix = zipApexSuffix - } - case flattenedApex: - if buildFlattenedAsDefault { - a.suffix = "" - a.primaryApexType = true - } else { - a.suffix = flattenedSuffix - } - } -} - -func (a *apexBundle) commonBuildActions(ctx android.ModuleContext) bool { - a.checkApexAvailability(ctx) - a.checkUpdatable(ctx) - a.CheckMinSdkVersion(ctx) - a.checkStaticLinkingToStubLibraries(ctx) - a.checkStaticExecutables(ctx) - if len(a.properties.Tests) > 0 && !a.testApex { - ctx.PropertyErrorf("tests", "property allowed only in apex_test module type") - return false - } - return true -} - type visitorContext struct { // all the files that will be included in this APEX filesInfo []apexFile @@ -2364,9 +2188,16 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext, func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { //////////////////////////////////////////////////////////////////////////////////////////// // 1) do some validity checks such as apex_available, min_sdk_version, etc. - if !a.commonBuildActions(ctx) { + a.checkApexAvailability(ctx) + a.checkUpdatable(ctx) + a.CheckMinSdkVersion(ctx) + a.checkStaticLinkingToStubLibraries(ctx) + a.checkStaticExecutables(ctx) + if len(a.properties.Tests) > 0 && !a.testApex { + ctx.PropertyErrorf("tests", "property allowed only in apex_test module type") return } + //////////////////////////////////////////////////////////////////////////////////////////// // 2) traverse the dependency tree to collect apexFile structs from them. @@ -2388,9 +2219,74 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { a.installDir = android.PathForModuleInstall(ctx, "apex") a.filesInfo = vctx.filesInfo - a.setApexTypeAndSuffix(ctx) - a.setPayloadFsType(ctx) - a.setSystemLibLink(ctx) + // Set suffix and primaryApexType depending on the ApexType + buildFlattenedAsDefault := ctx.Config().FlattenApex() + switch a.properties.ApexType { + case imageApex: + if buildFlattenedAsDefault { + a.suffix = imageApexSuffix + } else { + a.suffix = "" + a.primaryApexType = true + + if ctx.Config().InstallExtraFlattenedApexes() { + a.requiredDeps = append(a.requiredDeps, a.Name()+flattenedSuffix) + } + } + case zipApex: + if proptools.String(a.properties.Payload_type) == "zip" { + a.suffix = "" + a.primaryApexType = true + } else { + a.suffix = zipApexSuffix + } + case flattenedApex: + if buildFlattenedAsDefault { + a.suffix = "" + a.primaryApexType = true + } else { + a.suffix = flattenedSuffix + } + } + + switch proptools.StringDefault(a.properties.Payload_fs_type, ext4FsType) { + case ext4FsType: + a.payloadFsType = ext4 + case f2fsFsType: + a.payloadFsType = f2fs + case erofsFsType: + a.payloadFsType = erofs + default: + ctx.PropertyErrorf("payload_fs_type", "%q is not a valid filesystem for apex [ext4, f2fs, erofs]", *a.properties.Payload_fs_type) + } + + // Optimization. If we are building bundled APEX, for the files that are gathered due to the + // transitive dependencies, don't place them inside the APEX, but place a symlink pointing + // the same library in the system partition, thus effectively sharing the same libraries + // across the APEX boundary. For unbundled APEX, all the gathered files are actually placed + // in the APEX. + a.linkToSystemLib = !ctx.Config().UnbundledBuild() && a.installable() + + // APEXes targeting other than system/system_ext partitions use vendor/product variants. + // So we can't link them to /system/lib libs which are core variants. + if a.SocSpecific() || a.DeviceSpecific() || (a.ProductSpecific() && ctx.Config().EnforceProductPartitionInterface()) { + a.linkToSystemLib = false + } + + forced := ctx.Config().ForceApexSymlinkOptimization() + updatable := a.Updatable() || a.FutureUpdatable() + + // We don't need the optimization for updatable APEXes, as it might give false signal + // to the system health when the APEXes are still bundled (b/149805758). + if !forced && updatable && a.properties.ApexType == imageApex { + a.linkToSystemLib = false + } + + // We also don't want the optimization for host APEXes, because it doesn't make sense. + if ctx.Host() { + a.linkToSystemLib = false + } + if a.properties.ApexType != zipApex { a.compatSymlinks = makeCompatSymlinks(a.BaseModuleName(), ctx, a.primaryApexType) } diff --git a/apex/builder.go b/apex/builder.go index 95435f520..1956b4447 100644 --- a/apex/builder.go +++ b/apex/builder.go @@ -806,8 +806,8 @@ func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) { return } - a.setCompression(ctx) - if a.isCompressed { + if apexType == imageApex && (compressionEnabled || a.testOnlyShouldForceCompression()) { + a.isCompressed = true unsignedCompressedOutputFile := android.PathForModuleOut(ctx, a.Name()+imageCapexSuffix+".unsigned") compressRule := android.NewRuleBuilder(pctx, ctx) @@ -837,12 +837,17 @@ func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) { a.outputFile = signedCompressedOutputFile } + installSuffix := suffix + if a.isCompressed { + installSuffix = imageCapexSuffix + } + if !a.installable() { a.SkipInstall() } // Install to $OUT/soong/{target,host}/.../apex. - a.installedFile = ctx.InstallFile(a.installDir, a.Name()+a.installSuffix(), a.outputFile, + a.installedFile = ctx.InstallFile(a.installDir, a.Name()+installSuffix, a.outputFile, a.compatSymlinks.Paths()...) // installed-files.txt is dist'ed diff --git a/bazel/aquery.go b/bazel/aquery.go index 418b14321..ae2b107e1 100644 --- a/bazel/aquery.go +++ b/bazel/aquery.go @@ -574,7 +574,7 @@ func (a *aqueryArtifactHandler) getOutputPaths(actionEntry action) (outputPaths // expandTemplateContent substitutes the tokens in a template. func expandTemplateContent(actionEntry action) string { - var replacerString []string + replacerString := []string{} for _, pair := range actionEntry.Substitutions { value := pair.Value if val, ok := templateActionOverriddenTokens[pair.Key]; ok { @@ -647,7 +647,7 @@ func expandPathFragment(id pathFragmentId, pathFragmentsMap map[pathFragmentId]p } labels = append([]string{currFragment.Label}, labels...) if currId == currFragment.ParentId { - return "", fmt.Errorf("fragment cannot refer to itself as parent %#v", currFragment) + return "", fmt.Errorf("Fragment cannot refer to itself as parent %#v", currFragment) } currId = currFragment.ParentId } diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go index d32e61969..f5435f25f 100644 --- a/bazel/cquery/request_type.go +++ b/bazel/cquery/request_type.go @@ -1,7 +1,6 @@ package cquery import ( - "encoding/json" "fmt" "strings" ) @@ -10,7 +9,6 @@ var ( GetOutputFiles = &getOutputFilesRequestType{} GetPythonBinary = &getPythonBinaryRequestType{} GetCcInfo = &getCcInfoType{} - GetApexInfo = &getApexInfoType{} ) type CcInfo struct { @@ -181,7 +179,7 @@ func (g getCcInfoType) ParseResult(rawString string) (CcInfo, error) { const expectedLen = 10 splitString := strings.Split(rawString, "|") if len(splitString) != expectedLen { - return CcInfo{}, fmt.Errorf("expected %d items, got %q", expectedLen, splitString) + return CcInfo{}, fmt.Errorf("Expected %d items, got %q", expectedLen, splitString) } outputFilesString := splitString[0] ccObjectsString := splitString[1] @@ -217,54 +215,6 @@ func (g getCcInfoType) ParseResult(rawString string) (CcInfo, error) { }, nil } -// Query Bazel for the artifacts generated by the apex modules. -type getApexInfoType struct{} - -// Name returns a string name for this request type. Such request type names must be unique, -// and must only consist of alphanumeric characters. -func (g getApexInfoType) Name() string { - return "getApexInfo" -} - -// StarlarkFunctionBody returns a starlark function body to process this request type. -// The returned string is the body of a Starlark function which obtains -// all request-relevant information about a target and returns a string containing -// this information. The function should have the following properties: -// - `target` is the only parameter to this function (a configured target). -// - The return value must be a string. -// - The function body should not be indented outside of its own scope. -func (g getApexInfoType) StarlarkFunctionBody() string { - return `info = providers(target)["//build/bazel/rules/apex:apex.bzl%ApexInfo"] -return "{%s}" % ",".join([ - json_for_file("signed_output", info.signed_output), - json_for_file("unsigned_output", info.unsigned_output), - json_for_labels("provides_native_libs", info.provides_native_libs), - json_for_labels("requires_native_libs", info.requires_native_libs), - json_for_files("bundle_key_pair", info.bundle_key_pair), - json_for_files("container_key_pair", info.container_key_pair) - ])` -} - -type ApexCqueryInfo struct { - SignedOutput string `json:"signed_output"` - UnsignedOutput string `json:"unsigned_output"` - ProvidesLibs []string `json:"provides_native_libs"` - RequiresLibs []string `json:"requires_native_libs"` - BundleKeyPair []string `json:"bundle_key_pair"` - ContainerKeyPair []string `json:"container_key_pair"` -} - -// ParseResult returns a value obtained by parsing the result of the request's Starlark function. -// The given rawString must correspond to the string output which was created by evaluating the -// Starlark given in StarlarkFunctionBody. -func (g getApexInfoType) ParseResult(rawString string) ApexCqueryInfo { - var info ApexCqueryInfo - if err := json.Unmarshal([]byte(rawString), &info); err != nil { - panic(fmt.Errorf("cannot parse cquery result '%s': %s", rawString, err)) - } - return info -} - // splitOrEmpty is a modification of strings.Split() that returns an empty list // if the given string is empty. func splitOrEmpty(s string, sep string) []string { diff --git a/bazel/cquery/request_type_test.go b/bazel/cquery/request_type_test.go index 34248ce81..606e285bb 100644 --- a/bazel/cquery/request_type_test.go +++ b/bazel/cquery/request_type_test.go @@ -148,13 +148,13 @@ func TestGetCcInfoParseResults(t *testing.T) { description: "too few result splits", input: "|", expectedOutput: CcInfo{}, - expectedErrorMessage: fmt.Sprintf("expected %d items, got %q", expectedSplits, []string{"", ""}), + expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", expectedSplits, []string{"", ""}), }, { description: "too many result splits", input: strings.Repeat("|", expectedSplits+1), // 2 too many expectedOutput: CcInfo{}, - expectedErrorMessage: fmt.Sprintf("expected %d items, got %q", expectedSplits, make([]string, expectedSplits+2)), + expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", expectedSplits, make([]string, expectedSplits+2)), }, } for _, tc := range testCases { @@ -167,40 +167,3 @@ func TestGetCcInfoParseResults(t *testing.T) { } } } - -func TestGetApexInfoParseResults(t *testing.T) { - testCases := []struct { - description string - input string - expectedOutput ApexCqueryInfo - }{ - { - description: "no result", - input: "{}", - expectedOutput: ApexCqueryInfo{}, - }, - { - description: "one result", - input: `{"signed_output":"my.apex",` + - `"unsigned_output":"my.apex.unsigned",` + - `"requires_native_libs":["//bionic/libc:libc","//bionic/libdl:libdl"],` + - `"bundle_key_pair":["foo.pem","foo.privkey"],` + - `"container_key_pair":["foo.x509.pem", "foo.pk8"],` + - `"provides_native_libs":[]}`, - expectedOutput: ApexCqueryInfo{ - SignedOutput: "my.apex", - UnsignedOutput: "my.apex.unsigned", - RequiresLibs: []string{"//bionic/libc:libc", "//bionic/libdl:libdl"}, - ProvidesLibs: []string{}, - BundleKeyPair: []string{"foo.pem", "foo.privkey"}, - ContainerKeyPair: []string{"foo.x509.pem", "foo.pk8"}, - }, - }, - } - for _, tc := range testCases { - actualOutput := GetApexInfo.ParseResult(tc.input) - if !reflect.DeepEqual(tc.expectedOutput, actualOutput) { - t.Errorf("%q: expected %#v != actual %#v", tc.description, tc.expectedOutput, actualOutput) - } - } -}