Merge "Revert "Implement mixed builds for apex modules."" am: 0bb7f0494e am: 2fb081fd28

Original change: https://android-review.googlesource.com/c/platform/build/soong/+/2165026

Change-Id: I6c9ce0f6d60cd00c467515df52b172c6b5ab0c95
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Martin Stjernholm
2022-07-26 15:04:28 +00:00
committed by Automerger Merge Worker
6 changed files with 100 additions and 324 deletions

View File

@@ -91,10 +91,6 @@ type configKey struct {
osType OsType osType OsType
} }
func (c configKey) String() string {
return fmt.Sprintf("%s::%s", c.arch, c.osType)
}
// Map key to describe bazel cquery requests. // Map key to describe bazel cquery requests.
type cqueryKey struct { type cqueryKey struct {
label string label string
@@ -102,11 +98,6 @@ type cqueryKey struct {
configKey configKey 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 // 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 // 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, // is referred to as a "mixed build". (Some modules are managed by Soong,
@@ -132,9 +123,6 @@ type BazelContext interface {
// TODO(b/232976601): Remove. // TODO(b/232976601): Remove.
GetPythonBinary(label string, cfgKey configKey) (string, error) 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 // ** end Cquery Results Retrieval Functions
// Issues commands to Bazel to receive results for all cquery requests // Issues commands to Bazel to receive results for all cquery requests
@@ -198,7 +186,6 @@ type MockBazelContext struct {
LabelToOutputFiles map[string][]string LabelToOutputFiles map[string][]string
LabelToCcInfo map[string]cquery.CcInfo LabelToCcInfo map[string]cquery.CcInfo
LabelToPythonBinary map[string]string LabelToPythonBinary map[string]string
LabelToApexInfo map[string]cquery.ApexCqueryInfo
} }
func (m MockBazelContext) QueueBazelRequest(_ string, _ cqueryRequest, _ configKey) { func (m MockBazelContext) QueueBazelRequest(_ string, _ cqueryRequest, _ configKey) {
@@ -220,10 +207,6 @@ func (m MockBazelContext) GetPythonBinary(label string, _ configKey) (string, er
return result, nil return result, nil
} }
func (n MockBazelContext) GetApexInfo(_ string, _ configKey) (cquery.ApexCqueryInfo, error) {
panic("unimplemented")
}
func (m MockBazelContext) InvokeBazel(_ Config) error { func (m MockBazelContext) InvokeBazel(_ Config) error {
panic("unimplemented") 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) 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) { func (n noopBazelContext) QueueBazelRequest(_ string, _ cqueryRequest, _ configKey) {
panic("unimplemented") panic("unimplemented")
} }
@@ -302,10 +277,6 @@ func (n noopBazelContext) GetPythonBinary(_ string, _ configKey) (string, error)
panic("unimplemented") panic("unimplemented")
} }
func (n noopBazelContext) GetApexInfo(_ string, _ configKey) (cquery.ApexCqueryInfo, error) {
panic("unimplemented")
}
func (n noopBazelContext) InvokeBazel(_ Config) error { func (n noopBazelContext) InvokeBazel(_ Config) error {
panic("unimplemented") panic("unimplemented")
} }
@@ -430,9 +401,11 @@ func (r *builtinBazelRunner) issueBazelCommand(paths *bazelPaths, runName bazel.
cmdFlags := []string{ cmdFlags := []string{
"--output_base=" + absolutePath(paths.outputBase), "--output_base=" + absolutePath(paths.outputBase),
command.command, command.command,
command.expression, }
cmdFlags = append(cmdFlags, command.expression)
cmdFlags = append(cmdFlags,
// TODO(asmundak): is it needed in every build? // 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. // Set default platforms to canonicalized values for mixed builds requests.
// If these are set in the bazelrc, they will have values that are // 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 // Suppress noise
"--ui_event_filters=-INFO", "--ui_event_filters=-INFO",
"--noshow_progress"} "--noshow_progress")
cmdFlags = append(cmdFlags, extraFlags...) cmdFlags = append(cmdFlags, extraFlags...)
bazelCmd := exec.Command(paths.bazelPath, cmdFlags...) bazelCmd := exec.Command(paths.bazelPath, cmdFlags...)
bazelCmd.Dir = absolutePath(paths.syntheticWorkspaceDir()) bazelCmd.Dir = absolutePath(paths.syntheticWorkspaceDir())
extraEnv := []string{ bazelCmd.Env = append(os.Environ(),
"HOME=" + paths.homeDir, "HOME="+paths.homeDir,
pwdPrefix(), pwdPrefix(),
"BUILD_DIR=" + absolutePath(paths.soongOutDir), "BUILD_DIR="+absolutePath(paths.soongOutDir),
// Make OUT_DIR absolute here so tools/bazel.sh uses the correct // Make OUT_DIR absolute here so tools/bazel.sh uses the correct
// OUT_DIR at <root>/out, instead of <root>/out/soong/workspace/out. // OUT_DIR at <root>/out, instead of <root>/out/soong/workspace/out.
"OUT_DIR=" + absolutePath(paths.outDir()), "OUT_DIR="+absolutePath(paths.outDir()),
// Disables local host detection of gcc; toolchain information is defined // Disables local host detection of gcc; toolchain information is defined
// explicitly in BUILD files. // explicitly in BUILD files.
"BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1", "BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1")
}
bazelCmd.Env = append(os.Environ(), extraEnv...)
stderr := &bytes.Buffer{} stderr := &bytes.Buffer{}
bazelCmd.Stderr = stderr bazelCmd.Stderr = stderr
@@ -680,15 +651,6 @@ def get_arch(target):
fail("expected platform name of the form 'android_<arch>' or 'linux_<arch>', but was " + str(platforms)) fail("expected platform name of the form 'android_<arch>' or 'linux_<arch>', but was " + str(platforms))
return "UNKNOWN" 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): def format(target):
id_string = str(target.label) + "|" + get_arch(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, cqueryOutput, cqueryErr, err := context.issueBazelCommand(context.paths, bazel.CqueryBuildRootRunName, cqueryCmd,
"--output=starlark", "--starlark:file="+absolutePath(cqueryFileRelpath)) "--output=starlark", "--starlark:file="+absolutePath(cqueryFileRelpath))
if err != nil { 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 { if err != nil {
return err return err

View File

@@ -17,7 +17,6 @@
package apex package apex
import ( import (
"android/soong/bazel/cquery"
"fmt" "fmt"
"path/filepath" "path/filepath"
"regexp" "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 { type visitorContext struct {
// all the files that will be included in this APEX // all the files that will be included in this APEX
filesInfo []apexFile filesInfo []apexFile
@@ -2364,9 +2188,16 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext,
func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
//////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////
// 1) do some validity checks such as apex_available, min_sdk_version, etc. // 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 return
} }
//////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////
// 2) traverse the dependency tree to collect apexFile structs from them. // 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.installDir = android.PathForModuleInstall(ctx, "apex")
a.filesInfo = vctx.filesInfo a.filesInfo = vctx.filesInfo
a.setApexTypeAndSuffix(ctx) // Set suffix and primaryApexType depending on the ApexType
a.setPayloadFsType(ctx) buildFlattenedAsDefault := ctx.Config().FlattenApex()
a.setSystemLibLink(ctx) 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 { if a.properties.ApexType != zipApex {
a.compatSymlinks = makeCompatSymlinks(a.BaseModuleName(), ctx, a.primaryApexType) a.compatSymlinks = makeCompatSymlinks(a.BaseModuleName(), ctx, a.primaryApexType)
} }

View File

@@ -806,8 +806,8 @@ func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) {
return return
} }
a.setCompression(ctx) if apexType == imageApex && (compressionEnabled || a.testOnlyShouldForceCompression()) {
if a.isCompressed { a.isCompressed = true
unsignedCompressedOutputFile := android.PathForModuleOut(ctx, a.Name()+imageCapexSuffix+".unsigned") unsignedCompressedOutputFile := android.PathForModuleOut(ctx, a.Name()+imageCapexSuffix+".unsigned")
compressRule := android.NewRuleBuilder(pctx, ctx) compressRule := android.NewRuleBuilder(pctx, ctx)
@@ -837,12 +837,17 @@ func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) {
a.outputFile = signedCompressedOutputFile a.outputFile = signedCompressedOutputFile
} }
installSuffix := suffix
if a.isCompressed {
installSuffix = imageCapexSuffix
}
if !a.installable() { if !a.installable() {
a.SkipInstall() a.SkipInstall()
} }
// Install to $OUT/soong/{target,host}/.../apex. // 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()...) a.compatSymlinks.Paths()...)
// installed-files.txt is dist'ed // installed-files.txt is dist'ed

View File

@@ -574,7 +574,7 @@ func (a *aqueryArtifactHandler) getOutputPaths(actionEntry action) (outputPaths
// expandTemplateContent substitutes the tokens in a template. // expandTemplateContent substitutes the tokens in a template.
func expandTemplateContent(actionEntry action) string { func expandTemplateContent(actionEntry action) string {
var replacerString []string replacerString := []string{}
for _, pair := range actionEntry.Substitutions { for _, pair := range actionEntry.Substitutions {
value := pair.Value value := pair.Value
if val, ok := templateActionOverriddenTokens[pair.Key]; ok { 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...) labels = append([]string{currFragment.Label}, labels...)
if currId == currFragment.ParentId { 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 currId = currFragment.ParentId
} }

View File

@@ -1,7 +1,6 @@
package cquery package cquery
import ( import (
"encoding/json"
"fmt" "fmt"
"strings" "strings"
) )
@@ -10,7 +9,6 @@ var (
GetOutputFiles = &getOutputFilesRequestType{} GetOutputFiles = &getOutputFilesRequestType{}
GetPythonBinary = &getPythonBinaryRequestType{} GetPythonBinary = &getPythonBinaryRequestType{}
GetCcInfo = &getCcInfoType{} GetCcInfo = &getCcInfoType{}
GetApexInfo = &getApexInfoType{}
) )
type CcInfo struct { type CcInfo struct {
@@ -181,7 +179,7 @@ func (g getCcInfoType) ParseResult(rawString string) (CcInfo, error) {
const expectedLen = 10 const expectedLen = 10
splitString := strings.Split(rawString, "|") splitString := strings.Split(rawString, "|")
if len(splitString) != expectedLen { 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] outputFilesString := splitString[0]
ccObjectsString := splitString[1] ccObjectsString := splitString[1]
@@ -217,54 +215,6 @@ func (g getCcInfoType) ParseResult(rawString string) (CcInfo, error) {
}, nil }, 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 // splitOrEmpty is a modification of strings.Split() that returns an empty list
// if the given string is empty. // if the given string is empty.
func splitOrEmpty(s string, sep string) []string { func splitOrEmpty(s string, sep string) []string {

View File

@@ -148,13 +148,13 @@ func TestGetCcInfoParseResults(t *testing.T) {
description: "too few result splits", description: "too few result splits",
input: "|", input: "|",
expectedOutput: CcInfo{}, 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", description: "too many result splits",
input: strings.Repeat("|", expectedSplits+1), // 2 too many input: strings.Repeat("|", expectedSplits+1), // 2 too many
expectedOutput: CcInfo{}, 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 { 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)
}
}
}