diff options
-rw-r--r-- | android/mutator.go | 5 | ||||
-rw-r--r-- | android/queryview.go | 46 | ||||
-rw-r--r-- | android/register.go | 15 | ||||
-rw-r--r-- | apex/apex.go | 2 | ||||
-rw-r--r-- | apex/apex_test.go | 51 | ||||
-rw-r--r-- | cc/builder.go | 5 | ||||
-rw-r--r-- | cc/cc.go | 13 | ||||
-rw-r--r-- | cc/config/vndk.go | 1 | ||||
-rw-r--r-- | cc/strip.go | 22 | ||||
-rw-r--r-- | cmd/soong_build/main.go | 39 | ||||
-rw-r--r-- | dexpreopt/class_loader_context.go | 6 | ||||
-rw-r--r-- | dexpreopt/class_loader_context_test.go | 2 | ||||
-rw-r--r-- | java/aar.go | 11 | ||||
-rw-r--r-- | java/app_test.go | 14 | ||||
-rw-r--r-- | java/dexpreopt_bootjars.go | 4 | ||||
-rw-r--r-- | java/hiddenapi_singleton.go | 7 | ||||
-rw-r--r-- | java/java.go | 41 | ||||
-rw-r--r-- | python/binary.go | 2 | ||||
-rw-r--r-- | python/library.go | 4 | ||||
-rw-r--r-- | python/python.go | 372 | ||||
-rw-r--r-- | python/python_test.go | 4 | ||||
-rw-r--r-- | python/test.go | 4 | ||||
-rw-r--r-- | rust/config/allowed_list.go | 1 | ||||
-rw-r--r-- | rust/project_json.go | 25 | ||||
-rw-r--r-- | rust/project_json_test.go | 95 | ||||
-rw-r--r-- | ui/build/bazel.go | 166 | ||||
-rw-r--r-- | ui/build/ninja.go | 9 |
27 files changed, 658 insertions, 308 deletions
diff --git a/android/mutator.go b/android/mutator.go index 7a104772f..31edea331 100644 --- a/android/mutator.go +++ b/android/mutator.go @@ -44,6 +44,11 @@ func registerMutatorsToContext(ctx *blueprint.Context, mutators []*mutator) { } } +func registerMutatorsForBazelConversion(ctx *blueprint.Context) { + // FIXME(b/171263886): Start bringing in mutators to make the Bionic + // module subgraph suitable for automated conversion. +} + func registerMutators(ctx *blueprint.Context, preArch, preDeps, postDeps, finalDeps []RegisterMutatorFunc) { mctx := ®isterMutatorsContext{} diff --git a/android/queryview.go b/android/queryview.go index 970ae0116..1b7e77dd6 100644 --- a/android/queryview.go +++ b/android/queryview.go @@ -26,15 +26,40 @@ import ( // for calling the soong_build primary builder in the main build.ninja file. func init() { RegisterSingletonType("bazel_queryview", BazelQueryViewSingleton) + RegisterSingletonType("bazel_converter", BazelConverterSingleton) } +// BazelQueryViewSingleton is the singleton responsible for registering the +// soong_build build statement that will convert the Soong module graph after +// applying *all* mutators, enabing the feature to query the final state of the +// Soong graph. This mode is meant for querying the build graph state, and not meant +// for generating BUILD files to be checked in. func BazelQueryViewSingleton() Singleton { return &bazelQueryViewSingleton{} } +// BazelConverterSingleton is the singleton responsible for registering the soong_build +// build statement that will convert the Soong module graph by applying an alternate +// pipeline of mutators, with the goal of reaching semantic equivalence between the original +// Blueprint and final BUILD files. Using this mode, the goal is to be able to +// build with these BUILD files directly in the source tree. +func BazelConverterSingleton() Singleton { + return &bazelConverterSingleton{} +} + type bazelQueryViewSingleton struct{} +type bazelConverterSingleton struct{} + +func generateBuildActionsForBazelConversion(ctx SingletonContext, converterMode bool) { + name := "queryview" + additionalEnvVars := "" + descriptionTemplate := "[EXPERIMENTAL, PRE-PRODUCTION] Creating the Bazel QueryView workspace with %s at $outDir" + if converterMode { + name = "bp2build" + additionalEnvVars = "CONVERT_TO_BAZEL=true" + descriptionTemplate = "[EXPERIMENTAL, PRE-PRODUCTION] Converting all Android.bp to Bazel BUILD files with %s at $outDir" + } -func (c *bazelQueryViewSingleton) GenerateBuildActions(ctx SingletonContext) { // Create a build and rule statement, using the Bazel QueryView's WORKSPACE // file as the output file marker. var deps Paths @@ -42,22 +67,23 @@ func (c *bazelQueryViewSingleton) GenerateBuildActions(ctx SingletonContext) { deps = append(deps, moduleListFilePath) deps = append(deps, pathForBuildToolDep(ctx, ctx.Config().ProductVariablesFileName)) - bazelQueryViewDirectory := PathForOutput(ctx, "queryview") + bazelQueryViewDirectory := PathForOutput(ctx, name) bazelQueryViewWorkspaceFile := bazelQueryViewDirectory.Join(ctx, "WORKSPACE") primaryBuilder := primaryBuilderPath(ctx) bazelQueryView := ctx.Rule(pctx, "bazelQueryView", blueprint.RuleParams{ Command: fmt.Sprintf( "rm -rf ${outDir}/* && "+ - "%s --bazel_queryview_dir ${outDir} %s && "+ + "%s %s --bazel_queryview_dir ${outDir} %s && "+ "echo WORKSPACE: `cat %s` > ${outDir}/.queryview-depfile.d", + additionalEnvVars, primaryBuilder.String(), strings.Join(os.Args[1:], " "), moduleListFilePath.String(), // Use the contents of Android.bp.list as the depfile. ), CommandDeps: []string{primaryBuilder.String()}, Description: fmt.Sprintf( - "[EXPERIMENTAL, PRE-PRODUCTION] Creating the Bazel QueryView workspace with %s at $outDir", + descriptionTemplate, primaryBuilder.Base()), Deps: blueprint.DepsGCC, Depfile: "${outDir}/.queryview-depfile.d", @@ -73,6 +99,14 @@ func (c *bazelQueryViewSingleton) GenerateBuildActions(ctx SingletonContext) { }, }) - // Add a phony target for building the Bazel QueryView - ctx.Phony("queryview", bazelQueryViewWorkspaceFile) + // Add a phony target for generating the workspace + ctx.Phony(name, bazelQueryViewWorkspaceFile) +} + +func (c *bazelQueryViewSingleton) GenerateBuildActions(ctx SingletonContext) { + generateBuildActionsForBazelConversion(ctx, false) +} + +func (c *bazelConverterSingleton) GenerateBuildActions(ctx SingletonContext) { + generateBuildActionsForBazelConversion(ctx, true) } diff --git a/android/register.go b/android/register.go index 08e47b330..b26f9b97a 100644 --- a/android/register.go +++ b/android/register.go @@ -90,6 +90,21 @@ func NewContext(config Config) *Context { return ctx } +// RegisterForBazelConversion registers an alternate shadow pipeline of +// singletons, module types and mutators to register for converting Blueprint +// files to semantically equivalent BUILD files. +func (ctx *Context) RegisterForBazelConversion() { + for _, t := range moduleTypes { + ctx.RegisterModuleType(t.name, ModuleFactoryAdaptor(t.factory)) + } + + bazelConverterSingleton := singleton{"bp2build", BazelConverterSingleton} + ctx.RegisterSingletonType(bazelConverterSingleton.name, + SingletonFactoryAdaptor(ctx, bazelConverterSingleton.factory)) + + registerMutatorsForBazelConversion(ctx.Context) +} + func (ctx *Context) Register() { for _, t := range preSingletons { ctx.RegisterPreSingletonType(t.name, SingletonFactoryAdaptor(ctx, t.factory)) diff --git a/apex/apex.go b/apex/apex.go index 72f3db1ae..7ab74541f 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -1666,7 +1666,7 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { // system libraries. if !am.DirectlyInAnyApex() { // we need a module name for Make - name := cc.ImplementationModuleName(ctx) + name := cc.ImplementationModuleNameForMake(ctx) if !proptools.Bool(a.properties.Use_vendor) { // we don't use subName(.vendor) for a "use_vendor: true" apex diff --git a/apex/apex_test.go b/apex/apex_test.go index cc05fd433..0b67ef577 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -6186,6 +6186,57 @@ func TestNonPreferredPrebuiltDependency(t *testing.T) { `) } +func TestPreferredPrebuiltSharedLibDep(t *testing.T) { + ctx, config := testApex(t, ` + apex { + name: "myapex", + key: "myapex.key", + native_shared_libs: ["mylib"], + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + cc_library { + name: "mylib", + srcs: ["mylib.cpp"], + apex_available: ["myapex"], + shared_libs: ["otherlib"], + system_shared_libs: [], + } + + cc_library { + name: "otherlib", + srcs: ["mylib.cpp"], + stubs: { + versions: ["current"], + }, + } + + cc_prebuilt_library_shared { + name: "otherlib", + prefer: true, + srcs: ["prebuilt.so"], + stubs: { + versions: ["current"], + }, + } + `) + + ab := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle) + data := android.AndroidMkDataForTest(t, config, "", ab) + var builder strings.Builder + data.Custom(&builder, ab.BaseModuleName(), "TARGET_", "", data) + androidMk := builder.String() + + // The make level dependency needs to be on otherlib - prebuilt_otherlib isn't + // a thing there. + ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES += otherlib\n") +} + func TestMain(m *testing.M) { run := func() int { setUp() diff --git a/cc/builder.go b/cc/builder.go index 439e3721b..5545a5b05 100644 --- a/cc/builder.go +++ b/cc/builder.go @@ -568,8 +568,11 @@ func transformSourceToObj(ctx android.ModuleContext, subdir string, srcFiles and ccCmd = "clang++" moduleFlags = cppflags moduleToolingFlags = toolingCppflags + case ".h", ".hpp": + ctx.PropertyErrorf("srcs", "Header file %s is not supported, instead use export_include_dirs or local_include_dirs.", srcFile) + continue default: - ctx.ModuleErrorf("File %s has unknown extension", srcFile) + ctx.PropertyErrorf("srcs", "File %s has unknown extension. Supported extensions: .s, .S, .c, .cpp, .cc, .cxx, .mm", srcFile) continue } @@ -1099,6 +1099,19 @@ func (c *Module) ImplementationModuleName(ctx android.BaseModuleContext) string return name } +// Similar to ImplementationModuleName, but uses the Make variant of the module +// name as base name, for use in AndroidMk output. E.g. for a prebuilt module +// where the Soong name is prebuilt_foo, this returns foo (which works in Make +// under the premise that the prebuilt module overrides its source counterpart +// if it is exposed to Make). +func (c *Module) ImplementationModuleNameForMake(ctx android.BaseModuleContext) string { + name := c.BaseModuleName() + if versioned, ok := c.linker.(versionedInterface); ok { + name = versioned.implementationModuleName(name) + } + return name +} + func (c *Module) bootstrap() bool { return Bool(c.Properties.Bootstrap) } diff --git a/cc/config/vndk.go b/cc/config/vndk.go index 563ce7638..a54845203 100644 --- a/cc/config/vndk.go +++ b/cc/config/vndk.go @@ -21,7 +21,6 @@ var VndkMustUseVendorVariantList = []string{ "android.hardware.automotive.occupant_awareness-ndk_platform", "android.hardware.light-ndk_platform", "android.hardware.identity-ndk_platform", - "android.hardware.keymint-ndk_platform", "android.hardware.keymint-unstable-ndk_platform", "android.hardware.nfc@1.2", "android.hardware.power-ndk_platform", diff --git a/cc/strip.go b/cc/strip.go index 1f10a745c..b1f34bb89 100644 --- a/cc/strip.go +++ b/cc/strip.go @@ -23,19 +23,23 @@ import ( // StripProperties defines the type of stripping applied to the module. type StripProperties struct { Strip struct { - // whether to disable all stripping. + // none forces all stripping to be disabled. + // Device modules default to stripping enabled leaving mini debuginfo. + // Host modules default to stripping disabled, but can be enabled by setting any other + // strip boolean property. None *bool `android:"arch_variant"` - // whether to strip everything, including the mini debug info. + // all forces stripping everything, including the mini debug info. All *bool `android:"arch_variant"` - // whether to keep the symbols. + // keep_symbols enables stripping but keeps all symbols. Keep_symbols *bool `android:"arch_variant"` - // keeps only the symbols defined here. + // keep_symbols_list specifies a list of symbols to keep if keep_symbols is enabled. + // If it is unset then all symbols are kept. Keep_symbols_list []string `android:"arch_variant"` - // whether to keep the symbols and the debug frames. + // keep_symbols_and_debug_frame enables stripping but keeps all symbols and debug frames. Keep_symbols_and_debug_frame *bool `android:"arch_variant"` } `android:"arch_variant"` } @@ -47,8 +51,12 @@ type Stripper struct { // NeedsStrip determines if stripping is required for a module. func (stripper *Stripper) NeedsStrip(actx android.ModuleContext) bool { - // TODO(ccross): enable host stripping when Kati is enabled? Make never had support for stripping host binaries. - return (!actx.Config().KatiEnabled() || actx.Device()) && !Bool(stripper.StripProperties.Strip.None) + forceDisable := Bool(stripper.StripProperties.Strip.None) + defaultEnable := (!actx.Config().KatiEnabled() || actx.Device()) + forceEnable := Bool(stripper.StripProperties.Strip.All) || + Bool(stripper.StripProperties.Strip.Keep_symbols) || + Bool(stripper.StripProperties.Strip.Keep_symbols_and_debug_frame) + return !forceDisable && (forceEnable || defaultEnable) } func (stripper *Stripper) strip(actx android.ModuleContext, in android.Path, out android.ModuleOutPath, diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go index d758de294..907bed3c7 100644 --- a/cmd/soong_build/main.go +++ b/cmd/soong_build/main.go @@ -51,10 +51,22 @@ func newNameResolver(config android.Config) *android.NameResolver { return android.NewNameResolver(exportFilter) } +// bazelConversionRequested checks that the user is intending to convert +// Blueprint to Bazel BUILD files. +func bazelConversionRequested(configuration android.Config) bool { + return configuration.IsEnvTrue("CONVERT_TO_BAZEL") +} + func newContext(srcDir string, configuration android.Config) *android.Context { ctx := android.NewContext(configuration) - ctx.Register() - if !shouldPrepareBuildActions() { + if bazelConversionRequested(configuration) { + // Register an alternate set of singletons and mutators for bazel + // conversion for Bazel conversion. + ctx.RegisterForBazelConversion() + } else { + ctx.Register() + } + if !shouldPrepareBuildActions(configuration) { configuration.SetStopBefore(bootstrap.StopBeforePrepareBuildActions) } ctx.SetNameInterface(newNameResolver(configuration)) @@ -114,6 +126,8 @@ func main() { ctx = newContext(srcDir, configuration) bootstrap.Main(ctx.Context, configuration, extraNinjaDeps...) } + + // Convert the Soong module graph into Bazel BUILD files. if bazelQueryViewDir != "" { if err := createBazelQueryView(ctx, bazelQueryViewDir); err != nil { fmt.Fprintf(os.Stderr, "%s", err) @@ -130,7 +144,7 @@ func main() { // TODO(ccross): make this a command line argument. Requires plumbing through blueprint // to affect the command line of the primary builder. - if shouldPrepareBuildActions() { + if shouldPrepareBuildActions(configuration) { metricsFile := filepath.Join(bootstrap.BuildDir, "soong_build_metrics.pb") err := android.WriteMetrics(configuration, metricsFile) if err != nil { @@ -140,8 +154,19 @@ func main() { } } -func shouldPrepareBuildActions() bool { - // If we're writing soong_docs or queryview, don't write build.ninja or - // collect metrics. - return docFile == "" && bazelQueryViewDir == "" +// shouldPrepareBuildActions reads configuration and flags if build actions +// should be generated. +func shouldPrepareBuildActions(configuration android.Config) bool { + // Generating Soong docs + if docFile != "" { + return false + } + + // Generating a directory for Soong query (queryview) + if bazelQueryViewDir != "" { + return false + } + + // Generating a directory for converted Bazel BUILD files + return !bazelConversionRequested(configuration) } diff --git a/dexpreopt/class_loader_context.go b/dexpreopt/class_loader_context.go index 375921729..deaf77fe4 100644 --- a/dexpreopt/class_loader_context.go +++ b/dexpreopt/class_loader_context.go @@ -437,7 +437,11 @@ func validateClassLoaderContextRec(sdkVer int, clcs []*ClassLoaderContext) (bool if sdkVer == AnySdkVersion { // Return error if dexpreopt doesn't know paths to one of the <uses-library> // dependencies. In the future we may need to relax this and just disable dexpreopt. - return false, fmt.Errorf("invalid path for <uses-library> \"%s\"", clc.Name) + if clc.Host == nil { + return false, fmt.Errorf("invalid build path for <uses-library> \"%s\"", clc.Name) + } else { + return false, fmt.Errorf("invalid install path for <uses-library> \"%s\"", clc.Name) + } } else { // No error for compatibility libraries, as Soong doesn't know if they are needed // (this depends on the targetSdkVersion in the manifest), but the CLC is invalid. diff --git a/dexpreopt/class_loader_context_test.go b/dexpreopt/class_loader_context_test.go index df6856390..be7d4c63b 100644 --- a/dexpreopt/class_loader_context_test.go +++ b/dexpreopt/class_loader_context_test.go @@ -195,7 +195,7 @@ func TestCLCMaybeAdd(t *testing.T) { // But class loader context in such cases should raise an error on validation. t.Run("validate", func(t *testing.T) { _, err := validateClassLoaderContext(m) - checkError(t, err, "invalid path for <uses-library> \"a\"") + checkError(t, err, "invalid build path for <uses-library> \"a\"") }) } diff --git a/java/aar.go b/java/aar.go index 1940d7f7b..3b6b34e27 100644 --- a/java/aar.go +++ b/java/aar.go @@ -407,6 +407,7 @@ func aaptLibs(ctx android.ModuleContext, sdkContext sdkContext, classLoaderConte ctx.VisitDirectDeps(func(module android.Module) { depName := ctx.OtherModuleName(module) + depTag := ctx.OtherModuleDependencyTag(module) var exportPackage android.Path aarDep, _ := module.(AndroidLibraryDependency) @@ -414,7 +415,7 @@ func aaptLibs(ctx android.ModuleContext, sdkContext sdkContext, classLoaderConte exportPackage = aarDep.ExportPackage() } - switch ctx.OtherModuleDependencyTag(module) { + switch depTag { case instrumentationForTag: // Nothing, instrumentationForTag is treated as libTag for javac but not for aapt2. case libTag: @@ -439,7 +440,6 @@ func aaptLibs(ctx android.ModuleContext, sdkContext sdkContext, classLoaderConte transitiveStaticLibs = append(transitiveStaticLibs, aarDep.ExportedStaticPackages()...) transitiveStaticLibs = append(transitiveStaticLibs, exportPackage) transitiveStaticLibManifests = append(transitiveStaticLibManifests, aarDep.ExportedManifests()...) - classLoaderContexts.AddContextMap(aarDep.ClassLoaderContexts(), depName) if aarDep.ExportedAssets().Valid() { assets = append(assets, aarDep.ExportedAssets().Path()) } @@ -458,11 +458,8 @@ func aaptLibs(ctx android.ModuleContext, sdkContext sdkContext, classLoaderConte } } - // Add nested dependencies after processing the direct dependency: if it is a <uses-library>, - // nested context is added as its subcontext, and should not be re-added at the top-level. - if dep, ok := module.(Dependency); ok { - classLoaderContexts.AddContextMap(dep.ClassLoaderContexts(), depName) - } + // Merge dep's CLC after processing the dep itself (which may add its own <uses-library>). + maybeAddCLCFromDep(module, depTag, depName, classLoaderContexts) }) deps = append(deps, sharedLibs...) diff --git a/java/app_test.go b/java/app_test.go index c9abc6177..ef5e84dd2 100644 --- a/java/app_test.go +++ b/java/app_test.go @@ -2754,6 +2754,13 @@ func TestUsesLibraries(t *testing.T) { } java_sdk_library { + name: "fred", + srcs: ["a.java"], + api_packages: ["fred"], + sdk_version: "current", + } + + java_sdk_library { name: "bar", srcs: ["a.java"], api_packages: ["bar"], @@ -2777,7 +2784,12 @@ func TestUsesLibraries(t *testing.T) { name: "app", srcs: ["a.java"], libs: ["qux", "quuz.stubs"], - static_libs: ["static-runtime-helper"], + static_libs: [ + "static-runtime-helper", + // statically linked component libraries should not pull their SDK libraries, + // so "fred" should not be added to class loader context + "fred.stubs", + ], uses_libs: ["foo"], sdk_version: "current", optional_uses_libs: [ diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go index f16ddf1df..da7f291a3 100644 --- a/java/dexpreopt_bootjars.go +++ b/java/dexpreopt_bootjars.go @@ -506,7 +506,7 @@ func buildBootImage(ctx android.SingletonContext, image *bootImageConfig) *bootI m := image.modules.Jar(i) if ctx.Config().AllowMissingDependencies() { missingDeps = append(missingDeps, m) - bootDexJars[i] = android.PathForOutput(ctx, "missing") + bootDexJars[i] = android.PathForOutput(ctx, "missing/module", m, "from/apex", image.modules.Apex(i)) } else { ctx.Errorf("failed to find a dex jar path for module '%s'"+ ", note that some jars may be filtered out by module constraints", m) @@ -779,7 +779,7 @@ func bootFrameworkProfileRule(ctx android.SingletonContext, image *bootImageConf bootFrameworkProfile = path.Path() } else { missingDeps = append(missingDeps, defaultProfile) - bootFrameworkProfile = android.PathForOutput(ctx, "missing") + bootFrameworkProfile = android.PathForOutput(ctx, "missing", defaultProfile) } profile := image.dir.Join(ctx, "boot.bprof") diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go index ce8410ed4..419dc3424 100644 --- a/java/hiddenapi_singleton.go +++ b/java/hiddenapi_singleton.go @@ -177,12 +177,13 @@ func stubFlagsRule(ctx android.SingletonContext) { for moduleList, pathList := range moduleListToPathList { for i := range pathList { if pathList[i] == nil { - pathList[i] = android.PathForOutput(ctx, "missing") + moduleName := (*moduleList)[i] + pathList[i] = android.PathForOutput(ctx, "missing/module", moduleName) if ctx.Config().AllowMissingDependencies() { - missingDeps = append(missingDeps, (*moduleList)[i]) + missingDeps = append(missingDeps, moduleName) } else { ctx.Errorf("failed to find dex jar path for module %q", - (*moduleList)[i]) + moduleName) } } } diff --git a/java/java.go b/java/java.go index 3d121ccea..d44719e99 100644 --- a/java/java.go +++ b/java/java.go @@ -1081,7 +1081,6 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { switch tag { case libTag: deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.sdkVersion())...) - // names of sdk libs that are directly depended are exported j.classLoaderContexts.MaybeAddContext(ctx, dep.OptionalImplicitSdkLibrary(), dep.DexJarBuildPath(), dep.DexJarInstallPath()) case staticLibTag: @@ -1093,7 +1092,6 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { deps.bootClasspath = append(deps.bootClasspath, dep.HeaderJars()...) case libTag, instrumentationForTag: deps.classpath = append(deps.classpath, dep.HeaderJars()...) - // sdk lib names from dependencies are re-exported j.classLoaderContexts.AddContextMap(dep.ClassLoaderContexts(), otherName) deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...) pluginJars, pluginClasses, disableTurbine := dep.ExportedPlugins() @@ -1106,8 +1104,6 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { deps.staticJars = append(deps.staticJars, dep.ImplementationJars()...) deps.staticHeaderJars = append(deps.staticHeaderJars, dep.HeaderJars()...) deps.staticResourceJars = append(deps.staticResourceJars, dep.ResourceJars()...) - // sdk lib names from dependencies are re-exported - j.classLoaderContexts.AddContextMap(dep.ClassLoaderContexts(), otherName) deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...) pluginJars, pluginClasses, disableTurbine := dep.ExportedPlugins() addPlugins(&deps, pluginJars, pluginClasses...) @@ -1182,6 +1178,9 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { deps.systemModules = &systemModules{outputDir, outputDeps} } } + + // Merge dep's CLC after processing the dep itself (which may add its own <uses-library>). + maybeAddCLCFromDep(module, tag, otherName, j.classLoaderContexts) }) return deps @@ -2815,8 +2814,6 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { switch tag { case libTag, staticLibTag: flags.classpath = append(flags.classpath, dep.HeaderJars()...) - // sdk lib names from dependencies are re-exported - j.classLoaderContexts.AddContextMap(dep.ClassLoaderContexts(), otherName) case bootClasspathTag: flags.bootClasspath = append(flags.bootClasspath, dep.HeaderJars()...) } @@ -2824,10 +2821,12 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { switch tag { case libTag: flags.classpath = append(flags.classpath, dep.SdkHeaderJars(ctx, j.sdkVersion())...) - // names of sdk libs that are directly depended are exported j.classLoaderContexts.AddContext(ctx, otherName, dep.DexJarBuildPath(), dep.DexJarInstallPath()) } } + + // Merge dep's CLC after processing the dep itself (which may add its own <uses-library>). + maybeAddCLCFromDep(module, tag, otherName, j.classLoaderContexts) }) var installFile android.Path @@ -3248,3 +3247,31 @@ var Bool = proptools.Bool var BoolDefault = proptools.BoolDefault var String = proptools.String var inList = android.InList + +// Add class loader context of a given dependency to the given class loader context, provided that +// all the necessary conditions are met. +func maybeAddCLCFromDep(depModule android.Module, depTag blueprint.DependencyTag, + depName string, clcMap dexpreopt.ClassLoaderContextMap) { + + if dep, ok := depModule.(Dependency); ok { + if depTag == libTag { + // Ok, propagate <uses-library> through non-static library dependencies. + } else if depTag == staticLibTag { + // Propagate <uses-library> through static library dependencies, unless it is a + // component library (such as stubs). Component libraries have a dependency on their + // SDK library, which should not be pulled just because of a static component library. + if comp, isComp := depModule.(SdkLibraryComponentDependency); isComp { + if compName := comp.OptionalImplicitSdkLibrary(); compName != nil { + dep = nil + } + } + } else { + // Don't propagate <uses-library> for other dependency tags. + dep = nil + } + + if dep != nil { + clcMap.AddContextMap(dep.ClassLoaderContexts(), depName) + } + } +} diff --git a/python/binary.go b/python/binary.go index 1d2400efd..416a7eec0 100644 --- a/python/binary.go +++ b/python/binary.go @@ -81,7 +81,7 @@ func NewBinary(hod android.HostOrDeviceSupported) (*Module, *binaryDecorator) { func PythonBinaryHostFactory() android.Module { module, _ := NewBinary(android.HostSupported) - return module.Init() + return module.init() } func (binary *binaryDecorator) autorun() bool { diff --git a/python/library.go b/python/library.go index 0c8d61313..b724d2b9f 100644 --- a/python/library.go +++ b/python/library.go @@ -28,11 +28,11 @@ func init() { func PythonLibraryHostFactory() android.Module { module := newModule(android.HostSupported, android.MultilibFirst) - return module.Init() + return module.init() } func PythonLibraryFactory() android.Module { module := newModule(android.HostAndDeviceSupported, android.MultilibBoth) - return module.Init() + return module.init() } diff --git a/python/python.go b/python/python.go index 7e376c608..b3e3d13b2 100644 --- a/python/python.go +++ b/python/python.go @@ -20,7 +20,6 @@ import ( "fmt" "path/filepath" "regexp" - "sort" "strings" "github.com/google/blueprint" @@ -33,33 +32,34 @@ func init() { android.PreDepsMutators(RegisterPythonPreDepsMutators) } +// Exported to support other packages using Python modules in tests. func RegisterPythonPreDepsMutators(ctx android.RegisterMutatorsContext) { ctx.BottomUp("python_version", versionSplitMutator()).Parallel() } -// the version properties that apply to python libraries and binaries. +// the version-specific properties that apply to python modules. type VersionProperties struct { - // true, if the module is required to be built with this version. + // whether the module is required to be built with this version. + // Defaults to true for Python 3, and false otherwise. Enabled *bool `android:"arch_variant"` - // non-empty list of .py files under this strict Python version. - // srcs may reference the outputs of other modules that produce source files like genrule - // or filegroup using the syntax ":module". + // list of source files specific to this Python version. + // Using the syntax ":module", srcs may reference the outputs of other modules that produce source files, + // e.g. genrule or filegroup. Srcs []string `android:"path,arch_variant"` - // list of source files that should not be used to build the Python module. - // This is most useful in the arch/multilib variants to remove non-common files + // list of source files that should not be used to build the Python module for this version. + // This is most useful to remove files that are not common to all Python versions. Exclude_srcs []string `android:"path,arch_variant"` - // list of the Python libraries under this Python version. + // list of the Python libraries used only for this Python version. Libs []string `android:"arch_variant"` - // true, if the binary is required to be built with embedded launcher. - // TODO(nanzhang): Remove this flag when embedded Python3 is supported later. - Embedded_launcher *bool `android:"arch_variant"` + // whether the binary is required to be built with embedded launcher for this version, defaults to false. + Embedded_launcher *bool `android:"arch_variant"` // TODO(b/174041232): Remove this property } -// properties that apply to python libraries and binaries. +// properties that apply to all python modules type BaseProperties struct { // the package path prefix within the output artifact at which to place the source/data // files of the current module. @@ -93,10 +93,12 @@ type BaseProperties struct { Libs []string `android:"arch_variant"` Version struct { - // all the "srcs" or Python dependencies that are to be used only for Python2. + // Python2-specific properties, including whether Python2 is supported for this module + // and version-specific sources, exclusions and dependencies. Py2 VersionProperties `android:"arch_variant"` - // all the "srcs" or Python dependencies that are to be used only for Python3. + // Python3-specific properties, including whether Python3 is supported for this module + // and version-specific sources, exclusions and dependencies. Py3 VersionProperties `android:"arch_variant"` } `android:"arch_variant"` @@ -105,14 +107,16 @@ type BaseProperties struct { // runtime. Actual_version string `blueprint:"mutated"` - // true, if the module is required to be built with actual_version. + // whether the module is required to be built with actual_version. + // this is set by the python version mutator based on version-specific properties Enabled *bool `blueprint:"mutated"` - // true, if the binary is required to be built with embedded launcher. - // TODO(nanzhang): Remove this flag when embedded Python3 is supported later. + // whether the binary is required to be built with embedded launcher for this actual_version. + // this is set by the python version mutator based on version-specific properties Embedded_launcher *bool `blueprint:"mutated"` } +// Used to store files of current module after expanding dependencies type pathMapping struct { dest string src android.Path @@ -129,11 +133,14 @@ type Module struct { hod android.HostOrDeviceSupported multilib android.Multilib - // the bootstrapper is used to bootstrap .par executable. - // bootstrapper might be nil (Python library module). + // interface used to bootstrap .par executable when embedded_launcher is true + // this should be set by Python modules which are runnable, e.g. binaries and tests + // bootstrapper might be nil (e.g. Python library module). bootstrapper bootstrapper - // the installer might be nil. + // interface that implements functions required for installation + // this should be set by Python modules which are runnable, e.g. binaries and tests + // installer might be nil (e.g. Python library module). installer installer // the Python files of current module after expanding source dependencies. @@ -153,9 +160,11 @@ type Module struct { // (.intermediate) module output path as installation source. installSource android.OptionalPath + // Map to ensure sub-part of the AndroidMk for this module is only added once subAndroidMkOnce map[subAndroidMkProvider]bool } +// newModule generates new Python base module func newModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module { return &Module{ hod: hod, @@ -163,6 +172,7 @@ func newModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Mo } } +// bootstrapper interface should be implemented for runnable modules, e.g. binary and test type bootstrapper interface { bootstrapperProps() []interface{} bootstrap(ctx android.ModuleContext, ActualVersion string, embeddedLauncher bool, @@ -172,36 +182,45 @@ type bootstrapper interface { autorun() bool } +// installer interface should be implemented for installable modules, e.g. binary and test type installer interface { install(ctx android.ModuleContext, path android.Path) setAndroidMkSharedLibs(sharedLibs []string) } -type PythonDependency interface { - GetSrcsPathMappings() []pathMapping - GetDataPathMappings() []pathMapping - GetSrcsZip() android.Path +// interface implemented by Python modules to provide source and data mappings and zip to python +// modules that depend on it +type pythonDependency interface { + getSrcsPathMappings() []pathMapping + getDataPathMappings() []pathMapping + getSrcsZip() android.Path } -func (p *Module) GetSrcsPathMappings() []pathMapping { +// getSrcsPathMappings gets this module's path mapping of src source path : runfiles destination +func (p *Module) getSrcsPathMappings() []pathMapping { return p.srcsPathMappings } -func (p *Module) GetDataPathMappings() []pathMapping { +// getSrcsPathMappings gets this module's path mapping of data source path : runfiles destination +func (p *Module) getDataPathMappings() []pathMapping { return p.dataPathMappings } -func (p *Module) GetSrcsZip() android.Path { +// getSrcsZip returns the filepath where the current module's source/data files are zipped. +func (p *Module) getSrcsZip() android.Path { return p.srcsZip } -var _ PythonDependency = (*Module)(nil) +var _ pythonDependency = (*Module)(nil) var _ android.AndroidMkEntriesProvider = (*Module)(nil) -func (p *Module) Init() android.Module { - +func (p *Module) init(additionalProps ...interface{}) android.Module { p.AddProperties(&p.properties, &p.protoProperties) + + // Add additional properties for bootstrapping/installation + // This is currently tied to the bootstrapper interface; + // however, these are a combination of properties for the installation and bootstrapping of a module if p.bootstrapper != nil { p.AddProperties(p.bootstrapper.bootstrapperProps()...) } @@ -212,13 +231,19 @@ func (p *Module) Init() android.Module { return p } +// Python-specific tag to transfer information on the purpose of a dependency. +// This is used when adding a dependency on a module, which can later be accessed when visiting +// dependencies. type dependencyTag struct { blueprint.BaseDependencyTag name string } +// Python-specific tag that indicates that installed files of this module should depend on installed +// files of the dependency type installDependencyTag struct { blueprint.BaseDependencyTag + // embedding this struct provides the installation dependency requirement android.InstallAlwaysNeededDependencyTag name string } @@ -228,7 +253,7 @@ var ( javaDataTag = dependencyTag{name: "javaData"} launcherTag = dependencyTag{name: "launcher"} launcherSharedLibTag = installDependencyTag{name: "launcherSharedLib"} - pyIdentifierRegexp = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_-]*$`) + pathComponentRegexp = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_-]*$`) pyExt = ".py" protoExt = ".proto" pyVersion2 = "PY2" @@ -237,35 +262,37 @@ var ( mainFileName = "__main__.py" entryPointFile = "entry_point.txt" parFileExt = ".zip" - internal = "internal" + internalPath = "internal" ) -// create version variants for modules. +// versionSplitMutator creates version variants for modules and appends the version-specific +// properties for a given variant to the properties in the variant module func versionSplitMutator() func(android.BottomUpMutatorContext) { return func(mctx android.BottomUpMutatorContext) { if base, ok := mctx.Module().(*Module); ok { versionNames := []string{} + // collect version specific properties, so that we can merge version-specific properties + // into the module's overall properties versionProps := []VersionProperties{} // PY3 is first so that we alias the PY3 variant rather than PY2 if both // are available - if !(base.properties.Version.Py3.Enabled != nil && - *(base.properties.Version.Py3.Enabled) == false) { + if proptools.BoolDefault(base.properties.Version.Py3.Enabled, true) { versionNames = append(versionNames, pyVersion3) versionProps = append(versionProps, base.properties.Version.Py3) } - if base.properties.Version.Py2.Enabled != nil && - *(base.properties.Version.Py2.Enabled) == true { + if proptools.BoolDefault(base.properties.Version.Py2.Enabled, false) { versionNames = append(versionNames, pyVersion2) versionProps = append(versionProps, base.properties.Version.Py2) } modules := mctx.CreateLocalVariations(versionNames...) + // Alias module to the first variant if len(versionNames) > 0 { mctx.AliasVariation(versionNames[0]) } for i, v := range versionNames { // set the actual version for Python module. modules[i].(*Module).properties.Actual_version = v - // append versioned properties for the Python module + // append versioned properties for the Python module to the overall properties err := proptools.AppendMatchingProperties([]interface{}{&modules[i].(*Module).properties}, &versionProps[i], nil) if err != nil { panic(err) @@ -275,14 +302,19 @@ func versionSplitMutator() func(android.BottomUpMutatorContext) { } } +// HostToolPath returns a path if appropriate such that this module can be used as a host tool, +// fulfilling HostToolProvider interface. func (p *Module) HostToolPath() android.OptionalPath { if p.installer == nil { // python_library is just meta module, and doesn't have any installer. return android.OptionalPath{} } + // TODO: This should only be set when building host binaries -- tests built for device would be + // setting this incorrectly. return android.OptionalPathForPath(p.installer.(*binaryDecorator).path) } +// OutputFiles returns output files based on given tag, returns an error if tag is unsupported. func (p *Module) OutputFiles(tag string) (android.Paths, error) { switch tag { case "": @@ -296,12 +328,12 @@ func (p *Module) OutputFiles(tag string) (android.Paths, error) { } func (p *Module) isEmbeddedLauncherEnabled() bool { - return Bool(p.properties.Embedded_launcher) + return p.installer != nil && Bool(p.properties.Embedded_launcher) } -func hasSrcExt(srcs []string, ext string) bool { - for _, src := range srcs { - if filepath.Ext(src) == ext { +func anyHasExt(paths []string, ext string) bool { + for _, p := range paths { + if filepath.Ext(p) == ext { return true } } @@ -309,10 +341,14 @@ func hasSrcExt(srcs []string, ext string) bool { return false } -func (p *Module) hasSrcExt(ctx android.BottomUpMutatorContext, ext string) bool { - return hasSrcExt(p.properties.Srcs, ext) +func (p *Module) anySrcHasExt(ctx android.BottomUpMutatorContext, ext string) bool { + return anyHasExt(p.properties.Srcs, ext) } +// DepsMutator mutates dependencies for this module: +// * handles proto dependencies, +// * if required, specifies launcher and adds launcher dependencies, +// * applies python version mutations to Python dependencies func (p *Module) DepsMutator(ctx android.BottomUpMutatorContext) { android.ProtoDeps(ctx, &p.protoProperties) @@ -320,66 +356,61 @@ func (p *Module) DepsMutator(ctx android.BottomUpMutatorContext) { {"python_version", p.properties.Actual_version}, } - if p.hasSrcExt(ctx, protoExt) && p.Name() != "libprotobuf-python" { + // If sources contain a proto file, add dependency on libprotobuf-python + if p.anySrcHasExt(ctx, protoExt) && p.Name() != "libprotobuf-python" { ctx.AddVariationDependencies(versionVariation, pythonLibTag, "libprotobuf-python") } + + // Add python library dependencies for this python version variation ctx.AddVariationDependencies(versionVariation, pythonLibTag, android.LastUniqueStrings(p.properties.Libs)...) - switch p.properties.Actual_version { - case pyVersion2: + // If this module will be installed and has an embedded launcher, we need to add dependencies for: + // * standard library + // * launcher + // * shared dependencies of the launcher + if p.installer != nil && p.isEmbeddedLauncherEnabled() { + var stdLib string + var launcherModule string + // Add launcher shared lib dependencies. Ideally, these should be + // derived from the `shared_libs` property of the launcher. However, we + // cannot read the property at this stage and it will be too late to add + // dependencies later. + launcherSharedLibDeps := []string{ + "libsqlite", + } + // Add launcher-specific dependencies for bionic + if ctx.Target().Os.Bionic() { + launcherSharedLibDeps = append(launcherSharedLibDeps, "libc", "libdl", "libm") + } - if p.bootstrapper != nil && p.isEmbeddedLauncherEnabled() { - ctx.AddVariationDependencies(versionVariation, pythonLibTag, "py2-stdlib") + switch p.properties.Actual_version { + case pyVersion2: + stdLib = "py2-stdlib" - launcherModule := "py2-launcher" + launcherModule = "py2-launcher" if p.bootstrapper.autorun() { launcherModule = "py2-launcher-autorun" } - ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherTag, launcherModule) - - // Add py2-launcher shared lib dependencies. Ideally, these should be - // derived from the `shared_libs` property of "py2-launcher". However, we - // cannot read the property at this stage and it will be too late to add - // dependencies later. - ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag, "libsqlite") - ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag, "libc++") - - if ctx.Target().Os.Bionic() { - ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag, - "libc", "libdl", "libm") - } - } + launcherSharedLibDeps = append(launcherSharedLibDeps, "libc++") - case pyVersion3: + case pyVersion3: + stdLib = "py3-stdlib" - if p.bootstrapper != nil && p.isEmbeddedLauncherEnabled() { - ctx.AddVariationDependencies(versionVariation, pythonLibTag, "py3-stdlib") - - launcherModule := "py3-launcher" + launcherModule = "py3-launcher" if p.bootstrapper.autorun() { launcherModule = "py3-launcher-autorun" } - ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherTag, launcherModule) - - // Add py3-launcher shared lib dependencies. Ideally, these should be - // derived from the `shared_libs` property of "py3-launcher". However, we - // cannot read the property at this stage and it will be too late to add - // dependencies later. - ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag, "libsqlite") if ctx.Device() { - ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag, - "liblog") - } - - if ctx.Target().Os.Bionic() { - ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag, - "libc", "libdl", "libm") + launcherSharedLibDeps = append(launcherSharedLibDeps, "liblog") } + default: + panic(fmt.Errorf("unknown Python Actual_version: %q for module: %q.", + p.properties.Actual_version, ctx.ModuleName())) } - default: - panic(fmt.Errorf("unknown Python Actual_version: %q for module: %q.", - p.properties.Actual_version, ctx.ModuleName())) + ctx.AddVariationDependencies(versionVariation, pythonLibTag, stdLib) + ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherTag, launcherModule) + ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag, launcherSharedLibDeps...) } // Emulate the data property for java_data but with the arch variation overridden to "common" @@ -389,19 +420,25 @@ func (p *Module) DepsMutator(ctx android.BottomUpMutatorContext) { } func (p *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) { - p.GeneratePythonBuildActions(ctx) + p.generatePythonBuildActions(ctx) - // Only Python binaries and test has non-empty bootstrapper. + // Only Python binary and test modules have non-empty bootstrapper. if p.bootstrapper != nil { - p.walkTransitiveDeps(ctx) - embeddedLauncher := false - embeddedLauncher = p.isEmbeddedLauncherEnabled() + // if the module is being installed, we need to collect all transitive dependencies to embed in + // the final par + p.collectPathsFromTransitiveDeps(ctx) + // bootstrap the module, including resolving main file, getting launcher path, and + // registering actions to build the par file + // bootstrap returns the binary output path p.installSource = p.bootstrapper.bootstrap(ctx, p.properties.Actual_version, - embeddedLauncher, p.srcsPathMappings, p.srcsZip, p.depsSrcsZips) + p.isEmbeddedLauncherEnabled(), p.srcsPathMappings, p.srcsZip, p.depsSrcsZips) } + // Only Python binary and test modules have non-empty installer. if p.installer != nil { var sharedLibs []string + // if embedded launcher is enabled, we need to collect the shared library depenendencies of the + // launcher ctx.VisitDirectDeps(func(dep android.Module) { if ctx.OtherModuleDependencyTag(dep) == launcherSharedLibTag { sharedLibs = append(sharedLibs, ctx.OtherModuleName(dep)) @@ -409,18 +446,16 @@ func (p *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) { }) p.installer.setAndroidMkSharedLibs(sharedLibs) + // Install the par file from installSource if p.installSource.Valid() { p.installer.install(ctx, p.installSource.Path()) } } - } -func (p *Module) GeneratePythonBuildActions(ctx android.ModuleContext) { - // expand python files from "srcs" property. - srcs := p.properties.Srcs - exclude_srcs := p.properties.Exclude_srcs - expandedSrcs := android.PathsForModuleSrcExcludes(ctx, srcs, exclude_srcs) +// generatePythonBuildActions performs build actions common to all Python modules +func (p *Module) generatePythonBuildActions(ctx android.ModuleContext) { + expandedSrcs := android.PathsForModuleSrcExcludes(ctx, p.properties.Srcs, p.properties.Exclude_srcs) requiresSrcs := true if p.bootstrapper != nil && !p.bootstrapper.autorun() { requiresSrcs = false @@ -437,9 +472,10 @@ func (p *Module) GeneratePythonBuildActions(ctx android.ModuleContext) { expandedData = append(expandedData, android.OutputFilesForModule(ctx, javaData, "")...) } - // sanitize pkg_path. + // Validate pkg_path property pkgPath := String(p.properties.Pkg_path) if pkgPath != "" { + // TODO: export validation from android/paths.go handling to replace this duplicated functionality pkgPath = filepath.Clean(String(p.properties.Pkg_path)) if pkgPath == ".." || strings.HasPrefix(pkgPath, "../") || strings.HasPrefix(pkgPath, "/") { @@ -448,22 +484,35 @@ func (p *Module) GeneratePythonBuildActions(ctx android.ModuleContext) { String(p.properties.Pkg_path)) return } - if p.properties.Is_internal != nil && *p.properties.Is_internal { - pkgPath = filepath.Join(internal, pkgPath) - } - } else { - if p.properties.Is_internal != nil && *p.properties.Is_internal { - pkgPath = internal - } + } + // If property Is_internal is set, prepend pkgPath with internalPath + if proptools.BoolDefault(p.properties.Is_internal, false) { + pkgPath = filepath.Join(internalPath, pkgPath) } + // generate src:destination path mappings for this module p.genModulePathMappings(ctx, pkgPath, expandedSrcs, expandedData) + // generate the zipfile of all source and data files p.srcsZip = p.createSrcsZip(ctx, pkgPath) } -// generate current module unique pathMappings: <dest: runfiles_path, src: source_path> -// for python/data files. +func isValidPythonPath(path string) error { + identifiers := strings.Split(strings.TrimSuffix(path, filepath.Ext(path)), "/") + for _, token := range identifiers { + if !pathComponentRegexp.MatchString(token) { + return fmt.Errorf("the path %q contains invalid subpath %q. "+ + "Subpaths must be at least one character long. "+ + "The first character must an underscore or letter. "+ + "Following characters may be any of: letter, digit, underscore, hyphen.", + path, token) + } + } + return nil +} + +// For this module, generate unique pathMappings: <dest: runfiles_path, src: source_path> +// for python/data files expanded from properties. func (p *Module) genModulePathMappings(ctx android.ModuleContext, pkgPath string, expandedSrcs, expandedData android.Paths) { // fetch <runfiles_path, source_path> pairs from "src" and "data" properties to @@ -477,17 +526,11 @@ func (p *Module) genModulePathMappings(ctx android.ModuleContext, pkgPath string continue } runfilesPath := filepath.Join(pkgPath, s.Rel()) - identifiers := strings.Split(strings.TrimSuffix(runfilesPath, - filepath.Ext(runfilesPath)), "/") - for _, token := range identifiers { - if !pyIdentifierRegexp.MatchString(token) { - ctx.PropertyErrorf("srcs", "the path %q contains invalid token %q.", - runfilesPath, token) - } + if err := isValidPythonPath(runfilesPath); err != nil { + ctx.PropertyErrorf("srcs", err.Error()) } - if fillInMap(ctx, destToPySrcs, runfilesPath, s.String(), p.Name(), p.Name()) { - p.srcsPathMappings = append(p.srcsPathMappings, - pathMapping{dest: runfilesPath, src: s}) + if !checkForDuplicateOutputPath(ctx, destToPySrcs, runfilesPath, s.String(), p.Name(), p.Name()) { + p.srcsPathMappings = append(p.srcsPathMappings, pathMapping{dest: runfilesPath, src: s}) } } @@ -497,22 +540,23 @@ func (p *Module) genModulePathMappings(ctx android.ModuleContext, pkgPath string continue } runfilesPath := filepath.Join(pkgPath, d.Rel()) - if fillInMap(ctx, destToPyData, runfilesPath, d.String(), p.Name(), p.Name()) { + if !checkForDuplicateOutputPath(ctx, destToPyData, runfilesPath, d.String(), p.Name(), p.Name()) { p.dataPathMappings = append(p.dataPathMappings, pathMapping{dest: runfilesPath, src: d}) } } } -// register build actions to zip current module's sources. +// createSrcsZip registers build actions to zip current module's sources and data. func (p *Module) createSrcsZip(ctx android.ModuleContext, pkgPath string) android.Path { relativeRootMap := make(map[string]android.Paths) pathMappings := append(p.srcsPathMappings, p.dataPathMappings...) var protoSrcs android.Paths - // "srcs" or "data" properties may have filegroup so it might happen that - // the relative root for each source path is different. + // "srcs" or "data" properties may contain filegroup so it might happen that + // the root directory for each source path is different. for _, path := range pathMappings { + // handle proto sources separately if path.src.Ext() == protoExt { protoSrcs = append(protoSrcs, path.src) } else { @@ -537,24 +581,21 @@ func (p *Module) createSrcsZip(ctx android.ModuleContext, pkgPath string) androi } if len(relativeRootMap) > 0 { - var keys []string - // in order to keep stable order of soong_zip params, we sort the keys here. - for k := range relativeRootMap { - keys = append(keys, k) - } - sort.Strings(keys) + roots := android.SortedStringKeys(relativeRootMap) parArgs := []string{} if pkgPath != "" { + // use package path as path prefix parArgs = append(parArgs, `-P `+pkgPath) } - implicits := android.Paths{} - for _, k := range keys { - parArgs = append(parArgs, `-C `+k) - for _, path := range relativeRootMap[k] { + paths := android.Paths{} + for _, root := range roots { + // specify relative root of file in following -f arguments + parArgs = append(parArgs, `-C `+root) + for _, path := range relativeRootMap[root] { parArgs = append(parArgs, `-f `+path.String()) - implicits = append(implicits, path) + paths = append(paths, path) } } @@ -563,13 +604,15 @@ func (p *Module) createSrcsZip(ctx android.ModuleContext, pkgPath string) androi Rule: zip, Description: "python library archive", Output: origSrcsZip, - Implicits: implicits, + // as zip rule does not use $in, there is no real need to distinguish between Inputs and Implicits + Implicits: paths, Args: map[string]string{ "args": strings.Join(parArgs, " "), }, }) zips = append(zips, origSrcsZip) } + // we may have multiple zips due to separate handling of proto source files if len(zips) == 1 { return zips[0] } else { @@ -584,25 +627,27 @@ func (p *Module) createSrcsZip(ctx android.ModuleContext, pkgPath string) androi } } +// isPythonLibModule returns whether the given module is a Python library Module or not +// This is distinguished by the fact that Python libraries are not installable, while other Python +// modules are. func isPythonLibModule(module blueprint.Module) bool { if m, ok := module.(*Module); ok { - // Python library has no bootstrapper or installer. - if m.bootstrapper != nil || m.installer != nil { - return false + // Python library has no bootstrapper or installer + if m.bootstrapper == nil && m.installer == nil { + return true } - return true } return false } -// check Python source/data files duplicates for whole runfiles tree since Python binary/test -// need collect and zip all srcs of whole transitive dependencies to a final par file. -func (p *Module) walkTransitiveDeps(ctx android.ModuleContext) { +// collectPathsFromTransitiveDeps checks for source/data files for duplicate paths +// for module and its transitive dependencies and collects list of data/source file +// zips for transitive dependencies. +func (p *Module) collectPathsFromTransitiveDeps(ctx android.ModuleContext) { // fetch <runfiles_path, source_path> pairs from "src" and "data" properties to // check duplicates. destToPySrcs := make(map[string]string) destToPyData := make(map[string]string) - for _, path := range p.srcsPathMappings { destToPySrcs[path.dest] = path.src.String() } @@ -614,6 +659,7 @@ func (p *Module) walkTransitiveDeps(ctx android.ModuleContext) { // visit all its dependencies in depth first. ctx.WalkDeps(func(child, parent android.Module) bool { + // we only collect dependencies tagged as python library deps if ctx.OtherModuleDependencyTag(child) != pythonLibTag { return false } @@ -623,44 +669,46 @@ func (p *Module) walkTransitiveDeps(ctx android.ModuleContext) { seen[child] = true // Python modules only can depend on Python libraries. if !isPythonLibModule(child) { - panic(fmt.Errorf( + ctx.PropertyErrorf("libs", "the dependency %q of module %q is not Python library!", - ctx.ModuleName(), ctx.OtherModuleName(child))) + ctx.ModuleName(), ctx.OtherModuleName(child)) } - if dep, ok := child.(PythonDependency); ok { - srcs := dep.GetSrcsPathMappings() + // collect source and data paths, checking that there are no duplicate output file conflicts + if dep, ok := child.(pythonDependency); ok { + srcs := dep.getSrcsPathMappings() for _, path := range srcs { - if !fillInMap(ctx, destToPySrcs, - path.dest, path.src.String(), ctx.ModuleName(), ctx.OtherModuleName(child)) { - continue - } + checkForDuplicateOutputPath(ctx, destToPySrcs, + path.dest, path.src.String(), ctx.ModuleName(), ctx.OtherModuleName(child)) } - data := dep.GetDataPathMappings() + data := dep.getDataPathMappings() for _, path := range data { - fillInMap(ctx, destToPyData, + checkForDuplicateOutputPath(ctx, destToPyData, path.dest, path.src.String(), ctx.ModuleName(), ctx.OtherModuleName(child)) } - p.depsSrcsZips = append(p.depsSrcsZips, dep.GetSrcsZip()) + p.depsSrcsZips = append(p.depsSrcsZips, dep.getSrcsZip()) } return true }) } -func fillInMap(ctx android.ModuleContext, m map[string]string, - key, value, curModule, otherModule string) bool { - if oldValue, found := m[key]; found { +// chckForDuplicateOutputPath checks whether outputPath has already been included in map m, which +// would result in two files being placed in the same location. +// If there is a duplicate path, an error is thrown and true is returned +// Otherwise, outputPath: srcPath is added to m and returns false +func checkForDuplicateOutputPath(ctx android.ModuleContext, m map[string]string, outputPath, srcPath, curModule, otherModule string) bool { + if oldSrcPath, found := m[outputPath]; found { ctx.ModuleErrorf("found two files to be placed at the same location within zip %q."+ " First file: in module %s at path %q."+ " Second file: in module %s at path %q.", - key, curModule, oldValue, otherModule, value) - return false - } else { - m[key] = value + outputPath, curModule, oldSrcPath, otherModule, srcPath) + return true } + m[outputPath] = srcPath - return true + return false } +// InstallInData returns true as Python is not supported in the system partition func (p *Module) InstallInData() bool { return true } diff --git a/python/python_test.go b/python/python_test.go index 64bc4f6b8..5c4efa763 100644 --- a/python/python_test.go +++ b/python/python_test.go @@ -44,7 +44,7 @@ var ( pkgPathErrTemplate = moduleVariantErrTemplate + "pkg_path: %q must be a relative path contained in par file." badIdentifierErrTemplate = moduleVariantErrTemplate + - "srcs: the path %q contains invalid token %q." + "srcs: the path %q contains invalid subpath %q." dupRunfileErrTemplate = moduleVariantErrTemplate + "found two files to be placed at the same location within zip %q." + " First file: in module %s at path %q." + @@ -370,7 +370,7 @@ func expectErrors(t *testing.T, actErrs []error, expErrs []string) (testErrs []e } else { sort.Strings(expErrs) for i, v := range actErrStrs { - if v != expErrs[i] { + if !strings.Contains(v, expErrs[i]) { testErrs = append(testErrs, errors.New(v)) } } diff --git a/python/test.go b/python/test.go index 4df71c11b..b7cd4756a 100644 --- a/python/test.go +++ b/python/test.go @@ -108,12 +108,12 @@ func NewTest(hod android.HostOrDeviceSupported) *Module { func PythonTestHostFactory() android.Module { module := NewTest(android.HostSupportedNoCross) - return module.Init() + return module.init() } func PythonTestFactory() android.Module { module := NewTest(android.HostAndDeviceSupported) module.multilib = android.MultilibBoth - return module.Init() + return module.init() } diff --git a/rust/config/allowed_list.go b/rust/config/allowed_list.go index e6643f58a..df31d6023 100644 --- a/rust/config/allowed_list.go +++ b/rust/config/allowed_list.go @@ -13,6 +13,7 @@ var ( "external/rust", "external/vm_tools/p9", "frameworks/native/libs/binder/rust", + "packages/modules/DnsResolver", "packages/modules/Virtualization", "prebuilts/rust", "system/bt", diff --git a/rust/project_json.go b/rust/project_json.go index 8d9e50ca9..c4d60ad53 100644 --- a/rust/project_json.go +++ b/rust/project_json.go @@ -75,12 +75,16 @@ func init() { android.RegisterSingletonType("rust_project_generator", rustProjectGeneratorSingleton) } -// librarySource finds the main source file (.rs) for a crate. -func librarySource(ctx android.SingletonContext, rModule *Module, rustLib *libraryDecorator) (string, bool) { - srcs := rustLib.baseCompiler.Properties.Srcs +// crateSource finds the main source file (.rs) for a crate. +func crateSource(ctx android.SingletonContext, rModule *Module, comp *baseCompiler) (string, bool) { + srcs := comp.Properties.Srcs if len(srcs) != 0 { return path.Join(ctx.ModuleDir(rModule), srcs[0]), true } + rustLib, ok := rModule.compiler.(*libraryDecorator) + if !ok { + return "", false + } if !rustLib.source() { return "", false } @@ -132,8 +136,15 @@ func (singleton *projectGeneratorSingleton) appendLibraryAndDeps(ctx android.Sin if rModule.compiler == nil { return 0, "", false } - rustLib, ok := rModule.compiler.(*libraryDecorator) - if !ok { + var comp *baseCompiler + switch c := rModule.compiler.(type) { + case *libraryDecorator: + comp = c.baseCompiler + case *binaryDecorator: + comp = c.baseCompiler + case *testDecorator: + comp = c.binaryDecorator.baseCompiler + default: return 0, "", false } moduleName := ctx.ModuleName(module) @@ -146,12 +157,12 @@ func (singleton *projectGeneratorSingleton) appendLibraryAndDeps(ctx android.Sin return cInfo.ID, crateName, true } crate := rustProjectCrate{Deps: make([]rustProjectDep, 0), Cfgs: make([]string, 0)} - rootModule, ok := librarySource(ctx, rModule, rustLib) + rootModule, ok := crateSource(ctx, rModule, comp) if !ok { return 0, "", false } crate.RootModule = rootModule - crate.Edition = rustLib.baseCompiler.edition() + crate.Edition = comp.edition() deps := make(map[string]int) singleton.mergeDependencies(ctx, module, &crate, deps) diff --git a/rust/project_json_test.go b/rust/project_json_test.go index 16699c19a..aff16978b 100644 --- a/rust/project_json_test.go +++ b/rust/project_json_test.go @@ -67,6 +67,37 @@ func validateJsonCrates(t *testing.T, rawContent []byte) []interface{} { return crates } +// validateCrate ensures that a crate can be parsed as a map. +func validateCrate(t *testing.T, crate interface{}) map[string]interface{} { + c, ok := crate.(map[string]interface{}) + if !ok { + t.Fatalf("Unexpected type for crate: %v", c) + } + return c +} + +// validateDependencies parses the dependencies for a crate. It returns a list +// of the dependencies name. +func validateDependencies(t *testing.T, crate map[string]interface{}) []string { + var dependencies []string + deps, ok := crate["deps"].([]interface{}) + if !ok { + t.Errorf("Unexpected format for deps: %v", crate["deps"]) + } + for _, dep := range deps { + d, ok := dep.(map[string]interface{}) + if !ok { + t.Errorf("Unexpected format for dependency: %v", dep) + } + name, ok := d["name"].(string) + if !ok { + t.Errorf("Dependency is missing the name key: %v", d) + } + dependencies = append(dependencies, name) + } + return dependencies +} + func TestProjectJsonDep(t *testing.T) { bp := ` rust_library { @@ -85,6 +116,29 @@ func TestProjectJsonDep(t *testing.T) { validateJsonCrates(t, jsonContent) } +func TestProjectJsonBinary(t *testing.T) { + bp := ` + rust_binary { + name: "liba", + srcs: ["a/src/lib.rs"], + crate_name: "a" + } + ` + jsonContent := testProjectJson(t, bp) + crates := validateJsonCrates(t, jsonContent) + for _, c := range crates { + crate := validateCrate(t, c) + rootModule, ok := crate["root_module"].(string) + if !ok { + t.Fatalf("Unexpected type for root_module: %v", crate["root_module"]) + } + if rootModule == "a/src/lib.rs" { + return + } + } + t.Errorf("Entry for binary %q not found: %s", "a", jsonContent) +} + func TestProjectJsonBindGen(t *testing.T) { bp := ` rust_library { @@ -116,10 +170,7 @@ func TestProjectJsonBindGen(t *testing.T) { jsonContent := testProjectJson(t, bp) crates := validateJsonCrates(t, jsonContent) for _, c := range crates { - crate, ok := c.(map[string]interface{}) - if !ok { - t.Fatalf("Unexpected type for crate: %v", c) - } + crate := validateCrate(t, c) rootModule, ok := crate["root_module"].(string) if !ok { t.Fatalf("Unexpected type for root_module: %v", crate["root_module"]) @@ -133,16 +184,8 @@ func TestProjectJsonBindGen(t *testing.T) { } // Check that libbindings1 does not depend on itself. if strings.Contains(rootModule, "libbindings1") { - deps, ok := crate["deps"].([]interface{}) - if !ok { - t.Errorf("Unexpected format for deps: %v", crate["deps"]) - } - for _, dep := range deps { - d, ok := dep.(map[string]interface{}) - if !ok { - t.Errorf("Unexpected format for dep: %v", dep) - } - if d["name"] == "bindings1" { + for _, depName := range validateDependencies(t, crate) { + if depName == "bindings1" { t.Errorf("libbindings1 depends on itself") } } @@ -171,20 +214,18 @@ func TestProjectJsonMultiVersion(t *testing.T) { ` jsonContent := testProjectJson(t, bp) crates := validateJsonCrates(t, jsonContent) - for _, crate := range crates { - c := crate.(map[string]interface{}) - if c["root_module"] == "b/src/lib.rs" { - deps, ok := c["deps"].([]interface{}) - if !ok { - t.Errorf("Unexpected format for deps: %v", c["deps"]) - } + for _, c := range crates { + crate := validateCrate(t, c) + rootModule, ok := crate["root_module"].(string) + if !ok { + t.Fatalf("Unexpected type for root_module: %v", crate["root_module"]) + } + // Make sure that b has 2 different dependencies. + if rootModule == "b/src/lib.rs" { aCount := 0 - for _, dep := range deps { - d, ok := dep.(map[string]interface{}) - if !ok { - t.Errorf("Unexpected format for dep: %v", dep) - } - if d["name"] == "a" { + deps := validateDependencies(t, crate) + for _, depName := range deps { + if depName == "a" { aCount++ } } diff --git a/ui/build/bazel.go b/ui/build/bazel.go index 2d36f67a4..23b14ee1b 100644 --- a/ui/build/bazel.go +++ b/ui/build/bazel.go @@ -15,6 +15,8 @@ package build import ( + "bytes" + "fmt" "io/ioutil" "os" "path/filepath" @@ -24,6 +26,29 @@ import ( "android/soong/ui/metrics" ) +func getBazelInfo(ctx Context, config Config, bazelExecutable string, bazelEnv map[string]string, query string) string { + infoCmd := Command(ctx, config, "bazel", bazelExecutable) + + if extraStartupArgs, ok := infoCmd.Environment.Get("BAZEL_STARTUP_ARGS"); ok { + infoCmd.Args = append(infoCmd.Args, strings.Fields(extraStartupArgs)...) + } + + // Obtain the output directory path in the execution root. + infoCmd.Args = append(infoCmd.Args, + "info", + query, + ) + + for k, v := range bazelEnv { + infoCmd.Environment.Set(k, v) + } + + infoCmd.Dir = filepath.Join(config.OutDir(), "..") + + queryResult := strings.TrimSpace(string(infoCmd.OutputOrFatal())) + return queryResult +} + // Main entry point to construct the Bazel build command line, environment // variables and post-processing steps (e.g. converge output directories) func runBazel(ctx Context, config Config) { @@ -43,14 +68,18 @@ func runBazel(ctx Context, config Config) { // Environment variables are the primary mechanism to pass information from // soong_ui configuration or context to Bazel. - // + bazelEnv := make(map[string]string) + // Use *_NINJA variables to pass the root-relative path of the combined, // kati-generated, soong-generated, and packaging Ninja files to Bazel. // Bazel reads these from the lunch() repository rule. - config.environ.Set("COMBINED_NINJA", config.CombinedNinjaFile()) - config.environ.Set("KATI_NINJA", config.KatiBuildNinjaFile()) - config.environ.Set("PACKAGE_NINJA", config.KatiPackageNinjaFile()) - config.environ.Set("SOONG_NINJA", config.SoongNinjaFile()) + bazelEnv["COMBINED_NINJA"] = config.CombinedNinjaFile() + bazelEnv["KATI_NINJA"] = config.KatiBuildNinjaFile() + bazelEnv["PACKAGE_NINJA"] = config.KatiPackageNinjaFile() + bazelEnv["SOONG_NINJA"] = config.SoongNinjaFile() + + bazelEnv["DIST_DIR"] = config.DistDir() + bazelEnv["SHELL"] = "/bin/bash" // `tools/bazel` is the default entry point for executing Bazel in the AOSP // source tree. @@ -76,16 +105,36 @@ func runBazel(ctx Context, config Config) { "--slim_profile=true", ) - // Append custom build flags to the Bazel command. Changes to these flags - // may invalidate Bazel's analysis cache. - if extraBuildArgs, ok := cmd.Environment.Get("BAZEL_BUILD_ARGS"); ok { - cmd.Args = append(cmd.Args, strings.Fields(extraBuildArgs)...) - } + if config.UseRBE() { + for _, envVar := range []string{ + // RBE client + "RBE_compare", + "RBE_exec_strategy", + "RBE_invocation_id", + "RBE_log_dir", + "RBE_platform", + "RBE_remote_accept_cache", + "RBE_remote_update_cache", + "RBE_server_address", + // TODO: remove old FLAG_ variables. + "FLAG_compare", + "FLAG_exec_root", + "FLAG_exec_strategy", + "FLAG_invocation_id", + "FLAG_log_dir", + "FLAG_platform", + "FLAG_remote_accept_cache", + "FLAG_remote_update_cache", + "FLAG_server_address", + } { + cmd.Args = append(cmd.Args, + "--action_env="+envVar) + } - // Append the label of the default ninja_build target. - cmd.Args = append(cmd.Args, - "//:"+config.TargetProduct()+"-"+config.TargetBuildVariant(), - ) + // We need to calculate --RBE_exec_root ourselves + ctx.Println("Getting Bazel execution_root...") + cmd.Args = append(cmd.Args, "--action_env=RBE_exec_root="+getBazelInfo(ctx, config, bazelExecutable, bazelEnv, "execution_root")) + } // Ensure that the PATH environment variable value used in the action // environment is the restricted set computed from soong_ui, and not a @@ -95,15 +144,33 @@ func runBazel(ctx Context, config Config) { cmd.Args = append(cmd.Args, "--action_env=PATH="+pathEnvValue) } - cmd.Environment.Set("DIST_DIR", config.DistDir()) - cmd.Environment.Set("SHELL", "/bin/bash") + // Append custom build flags to the Bazel command. Changes to these flags + // may invalidate Bazel's analysis cache. + // These should be appended as the final args, so that they take precedence. + if extraBuildArgs, ok := cmd.Environment.Get("BAZEL_BUILD_ARGS"); ok { + cmd.Args = append(cmd.Args, strings.Fields(extraBuildArgs)...) + } - // Print the full command line for debugging purposes. - ctx.Println(cmd.Cmd) + // Append the label of the default ninja_build target. + cmd.Args = append(cmd.Args, + "//:"+config.TargetProduct()+"-"+config.TargetBuildVariant(), + ) // Execute the command at the root of the directory. cmd.Dir = filepath.Join(config.OutDir(), "..") - ctx.Status.Status("Starting Bazel..") + + for k, v := range bazelEnv { + cmd.Environment.Set(k, v) + } + + // Make a human-readable version of the bazelEnv map + bazelEnvStringBuffer := new(bytes.Buffer) + for k, v := range bazelEnv { + fmt.Fprintf(bazelEnvStringBuffer, "%s=%s ", k, v) + } + + // Print the full command line (including environment variables) for debugging purposes. + ctx.Println("Bazel command line: " + bazelEnvStringBuffer.String() + cmd.Cmd.String() + "\n") // Execute the build command. cmd.RunAndStreamOrFatal() @@ -113,28 +180,13 @@ func runBazel(ctx Context, config Config) { // Ensure that the $OUT_DIR contains the expected set of files by symlinking // the files from the execution root's output direction into $OUT_DIR. - // Obtain the Bazel output directory for ninja_build. - infoCmd := Command(ctx, config, "bazel", bazelExecutable) - - if extraStartupArgs, ok := infoCmd.Environment.Get("BAZEL_STARTUP_ARGS"); ok { - infoCmd.Args = append(infoCmd.Args, strings.Fields(extraStartupArgs)...) - } - - // Obtain the output directory path in the execution root. - infoCmd.Args = append(infoCmd.Args, - "info", - "output_path", - ) - - infoCmd.Environment.Set("DIST_DIR", config.DistDir()) - infoCmd.Environment.Set("SHELL", "/bin/bash") - infoCmd.Dir = filepath.Join(config.OutDir(), "..") - ctx.Status.Status("Getting Bazel Info..") - outputBasePath := string(infoCmd.OutputOrFatal()) + ctx.Println("Getting Bazel output_path...") + outputBasePath := getBazelInfo(ctx, config, bazelExecutable, bazelEnv, "output_path") // TODO: Don't hardcode out/ as the bazel output directory. This is // currently hardcoded as ninja_build.output_root. bazelNinjaBuildOutputRoot := filepath.Join(outputBasePath, "..", "out") + ctx.Println("Creating output symlinks..") symlinkOutdir(ctx, config, bazelNinjaBuildOutputRoot, ".") } @@ -147,27 +199,39 @@ func symlinkOutdir(ctx Context, config Config, rootPath string, relativePath str if err != nil { ctx.Fatal(err) } + for _, f := range files { + // The original Bazel file path destPath := filepath.Join(destDir, f.Name()) + + // The desired Soong file path srcPath := filepath.Join(config.OutDir(), relativePath, f.Name()) - if statResult, err := os.Stat(srcPath); err == nil { - if statResult.Mode().IsDir() && f.IsDir() { - // Directory under OutDir already exists, so recurse on its contents. + + destLstatResult, destLstatErr := os.Lstat(destPath) + if destLstatErr != nil { + ctx.Fatalf("Unable to Lstat dest %s: %s", destPath, destLstatErr) + } + + srcLstatResult, srcLstatErr := os.Lstat(srcPath) + + if srcLstatErr == nil { + if srcLstatResult.IsDir() && destLstatResult.IsDir() { + // src and dest are both existing dirs - recurse on the dest dir contents... symlinkOutdir(ctx, config, rootPath, filepath.Join(relativePath, f.Name())) - } else if !statResult.Mode().IsDir() && !f.IsDir() { - // File exists both in source and destination, and it's not a directory - // in either location. Do nothing. - // This can arise for files which are generated under OutDir outside of - // soong_build, such as .bootstrap files. } else { - // File is a directory in one location but not the other. Raise an error. - ctx.Fatalf("Could not link %s to %s due to conflict", srcPath, destPath) + // Ignore other pre-existing src files (could be pre-existing files, directories, symlinks, ...) + // This can arise for files which are generated under OutDir outside of soong_build, such as .bootstrap files. + // FIXME: This might cause a problem later e.g. if a symlink in the build graph changes... } - } else if os.IsNotExist(err) { - // Create symlink srcPath -> fullDestPath. - os.Symlink(destPath, srcPath) } else { - ctx.Fatalf("Unable to stat %s: %s", srcPath, err) + if !os.IsNotExist(srcLstatErr) { + ctx.Fatalf("Unable to Lstat src %s: %s", srcPath, srcLstatErr) + } + + // src does not exist, so try to create a src -> dest symlink (i.e. a Soong path -> Bazel path symlink) + if symlinkErr := os.Symlink(destPath, srcPath); symlinkErr != nil { + ctx.Fatalf("Unable to create symlink %s -> %s due to error %s", srcPath, destPath, symlinkErr) + } } } } diff --git a/ui/build/ninja.go b/ui/build/ninja.go index ffd1ab947..779976653 100644 --- a/ui/build/ninja.go +++ b/ui/build/ninja.go @@ -139,15 +139,6 @@ func runNinja(ctx Context, config Config) { // b/147197813 - used by art-check-debug-apex-gen "EMMA_INSTRUMENT_FRAMEWORK", - // Goma -- gomacc may not need all of these - "GOMA_DIR", - "GOMA_DISABLED", - "GOMA_FAIL_FAST", - "GOMA_FALLBACK", - "GOMA_GCE_SERVICE_ACCOUNT", - "GOMA_TMP_DIR", - "GOMA_USE_LOCAL", - // RBE client "RBE_compare", "RBE_exec_root", |