// Copyright 2021 Google Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package cc import ( "android/soong/android" "android/soong/bazel" "path/filepath" "strings" ) // bp2build functions and helpers for converting cc_* modules to Bazel. func init() { android.DepsBp2BuildMutators(RegisterDepsBp2Build) } func RegisterDepsBp2Build(ctx android.RegisterMutatorsContext) { ctx.BottomUp("cc_bp2build_deps", depsBp2BuildMutator) } // A naive deps mutator to add deps on all modules across all combinations of // target props for cc modules. This is needed to make module -> bazel label // resolution work in the bp2build mutator later. This is probably // the wrong way to do it, but it works. // // TODO(jingwen): can we create a custom os mutator in depsBp2BuildMutator to do this? func depsBp2BuildMutator(ctx android.BottomUpMutatorContext) { module, ok := ctx.Module().(*Module) if !ok { // Not a cc module return } if !module.ConvertWithBp2build(ctx) { return } var allDeps []string for _, p := range module.GetTargetProperties(&BaseLinkerProperties{}) { // arch specific linker props if baseLinkerProps, ok := p.(*BaseLinkerProperties); ok { allDeps = append(allDeps, baseLinkerProps.Header_libs...) allDeps = append(allDeps, baseLinkerProps.Export_header_lib_headers...) allDeps = append(allDeps, baseLinkerProps.Static_libs...) allDeps = append(allDeps, baseLinkerProps.Whole_static_libs...) } } for _, p := range module.GetArchProperties(ctx, &BaseLinkerProperties{}) { // arch specific linker props if baseLinkerProps, ok := p.(*BaseLinkerProperties); ok { allDeps = append(allDeps, baseLinkerProps.Header_libs...) allDeps = append(allDeps, baseLinkerProps.Export_header_lib_headers...) allDeps = append(allDeps, baseLinkerProps.Static_libs...) allDeps = append(allDeps, baseLinkerProps.Whole_static_libs...) } } // Deps in the static: { .. } and shared: { .. } props of a cc_library. if lib, ok := module.compiler.(*libraryDecorator); ok { allDeps = append(allDeps, lib.SharedProperties.Shared.Static_libs...) allDeps = append(allDeps, lib.SharedProperties.Shared.Whole_static_libs...) allDeps = append(allDeps, lib.SharedProperties.Shared.Shared_libs...) allDeps = append(allDeps, lib.SharedProperties.Shared.System_shared_libs...) allDeps = append(allDeps, lib.StaticProperties.Static.Static_libs...) allDeps = append(allDeps, lib.StaticProperties.Static.Whole_static_libs...) allDeps = append(allDeps, lib.StaticProperties.Static.Shared_libs...) allDeps = append(allDeps, lib.StaticProperties.Static.System_shared_libs...) } ctx.AddDependency(module, nil, android.SortedUniqueStrings(allDeps)...) } type sharedAttributes struct { copts bazel.StringListAttribute srcs bazel.LabelListAttribute staticDeps bazel.LabelListAttribute dynamicDeps bazel.LabelListAttribute wholeArchiveDeps bazel.LabelListAttribute } // bp2buildParseSharedProps returns the attributes for the shared variant of a cc_library. func bp2BuildParseSharedProps(ctx android.TopDownMutatorContext, module *Module) sharedAttributes { lib, ok := module.compiler.(*libraryDecorator) if !ok { return sharedAttributes{} } copts := bazel.StringListAttribute{Value: lib.SharedProperties.Shared.Cflags} srcs := bazel.LabelListAttribute{ Value: android.BazelLabelForModuleSrc(ctx, lib.SharedProperties.Shared.Srcs)} staticDeps := bazel.LabelListAttribute{ Value: android.BazelLabelForModuleDeps(ctx, lib.SharedProperties.Shared.Static_libs)} dynamicDeps := bazel.LabelListAttribute{ Value: android.BazelLabelForModuleDeps(ctx, lib.SharedProperties.Shared.Shared_libs)} wholeArchiveDeps := bazel.LabelListAttribute{ Value: android.BazelLabelForModuleDeps(ctx, lib.SharedProperties.Shared.Whole_static_libs)} return sharedAttributes{ copts: copts, srcs: srcs, staticDeps: staticDeps, dynamicDeps: dynamicDeps, wholeArchiveDeps: wholeArchiveDeps, } } type staticAttributes struct { copts bazel.StringListAttribute srcs bazel.LabelListAttribute staticDeps bazel.LabelListAttribute dynamicDeps bazel.LabelListAttribute wholeArchiveDeps bazel.LabelListAttribute } // bp2buildParseStaticProps returns the attributes for the static variant of a cc_library. func bp2BuildParseStaticProps(ctx android.TopDownMutatorContext, module *Module) staticAttributes { lib, ok := module.compiler.(*libraryDecorator) if !ok { return staticAttributes{} } copts := bazel.StringListAttribute{Value: lib.StaticProperties.Static.Cflags} srcs := bazel.LabelListAttribute{ Value: android.BazelLabelForModuleSrc(ctx, lib.StaticProperties.Static.Srcs)} staticDeps := bazel.LabelListAttribute{ Value: android.BazelLabelForModuleDeps(ctx, lib.StaticProperties.Static.Static_libs)} dynamicDeps := bazel.LabelListAttribute{ Value: android.BazelLabelForModuleDeps(ctx, lib.StaticProperties.Static.Shared_libs)} wholeArchiveDeps := bazel.LabelListAttribute{ Value: android.BazelLabelForModuleDeps(ctx, lib.StaticProperties.Static.Whole_static_libs)} return staticAttributes{ copts: copts, srcs: srcs, staticDeps: staticDeps, dynamicDeps: dynamicDeps, wholeArchiveDeps: wholeArchiveDeps, } } // Convenience struct to hold all attributes parsed from compiler properties. type compilerAttributes struct { copts bazel.StringListAttribute srcs bazel.LabelListAttribute includes bazel.StringListAttribute } // bp2BuildParseCompilerProps returns copts, srcs and hdrs and other attributes. func bp2BuildParseCompilerProps(ctx android.TopDownMutatorContext, module *Module) compilerAttributes { var srcs bazel.LabelListAttribute var copts bazel.StringListAttribute // Creates the -I flag for a directory, while making the directory relative // to the exec root for Bazel to work. includeFlag := func(dir string) string { // filepath.Join canonicalizes the path, i.e. it takes care of . or .. elements. return "-I" + filepath.Join(ctx.ModuleDir(), dir) } // Parse the list of module-relative include directories (-I). parseLocalIncludeDirs := func(baseCompilerProps *BaseCompilerProperties) []string { // include_dirs are root-relative, not module-relative. includeDirs := bp2BuildMakePathsRelativeToModule(ctx, baseCompilerProps.Include_dirs) return append(includeDirs, baseCompilerProps.Local_include_dirs...) } // Parse the list of copts. parseCopts := func(baseCompilerProps *BaseCompilerProperties) []string { var copts []string for _, flag := range append(baseCompilerProps.Cflags, baseCompilerProps.Cppflags...) { // Soong's cflags can contain spaces, like `-include header.h`. For // Bazel's copts, split them up to be compatible with the // no_copts_tokenization feature. copts = append(copts, strings.Split(flag, " ")...) } for _, dir := range parseLocalIncludeDirs(baseCompilerProps) { copts = append(copts, includeFlag(dir)) } return copts } // baseSrcs contain the list of src files that are used for every configuration. var baseSrcs []string // baseExcludeSrcs contain the list of src files that are excluded for every configuration. var baseExcludeSrcs []string // baseSrcsLabelList is a clone of the base srcs LabelList, used for computing the // arch or os specific srcs later. var baseSrcsLabelList bazel.LabelList // Parse srcs from an arch or OS's props value, taking the base srcs and // exclude srcs into account. parseSrcs := func(baseCompilerProps *BaseCompilerProperties) bazel.LabelList { // Combine the base srcs and arch-specific srcs allSrcs := append(baseSrcs, baseCompilerProps.Srcs...) // Combine the base exclude_srcs and configuration-specific exclude_srcs allExcludeSrcs := append(baseExcludeSrcs, baseCompilerProps.Exclude_srcs...) return android.BazelLabelForModuleSrcExcludes(ctx, allSrcs, allExcludeSrcs) } for _, props := range module.compiler.compilerProps() { if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok { srcs.Value = parseSrcs(baseCompilerProps) copts.Value = parseCopts(baseCompilerProps) // Used for arch-specific srcs later. baseSrcs = baseCompilerProps.Srcs baseExcludeSrcs = baseCompilerProps.Exclude_srcs baseSrcsLabelList = parseSrcs(baseCompilerProps) break } } // Handle include_build_directory prop. If the property is true, then the // target has access to all headers recursively in the package, and has // "-I" in its copts. if c, ok := module.compiler.(*baseCompiler); ok && c.includeBuildDirectory() { copts.Value = append(copts.Value, includeFlag(".")) } else if c, ok := module.compiler.(*libraryDecorator); ok && c.includeBuildDirectory() { copts.Value = append(copts.Value, includeFlag(".")) } for arch, props := range module.GetArchProperties(ctx, &BaseCompilerProperties{}) { if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok { // If there's arch specific srcs or exclude_srcs, generate a select entry for it. // TODO(b/186153868): do this for OS specific srcs and exclude_srcs too. if len(baseCompilerProps.Srcs) > 0 || len(baseCompilerProps.Exclude_srcs) > 0 { srcsList := parseSrcs(baseCompilerProps) srcs.SetValueForArch(arch.Name, srcsList) // The base srcs value should not contain any arch-specific excludes. srcs.Value = bazel.SubtractBazelLabelList(srcs.Value, bazel.LabelList{Includes: srcsList.Excludes}) } copts.SetValueForArch(arch.Name, parseCopts(baseCompilerProps)) } } // After going through all archs, delete the duplicate files in the arch // values that are already in the base srcs.Value. for arch, props := range module.GetArchProperties(ctx, &BaseCompilerProperties{}) { if _, ok := props.(*BaseCompilerProperties); ok { srcs.SetValueForArch(arch.Name, bazel.SubtractBazelLabelList(srcs.GetValueForArch(arch.Name), srcs.Value)) } } // Now that the srcs.Value list is finalized, compare it with the original // list, and put the difference into the default condition for the arch // select. defaultsSrcs := bazel.SubtractBazelLabelList(baseSrcsLabelList, srcs.Value) // TODO(b/186153868): handle the case with multiple variant types, e.g. when arch and os are both used. srcs.SetValueForArch(bazel.CONDITIONS_DEFAULT, defaultsSrcs) // Handle OS specific props. for os, props := range module.GetTargetProperties(&BaseCompilerProperties{}) { if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok { srcsList := parseSrcs(baseCompilerProps) // TODO(b/186153868): add support for os-specific srcs and exclude_srcs srcs.SetValueForOS(os.Name, bazel.SubtractBazelLabelList(srcsList, baseSrcsLabelList)) copts.SetValueForOS(os.Name, parseCopts(baseCompilerProps)) } } return compilerAttributes{ srcs: srcs, copts: copts, } } // Convenience struct to hold all attributes parsed from linker properties. type linkerAttributes struct { deps bazel.LabelListAttribute dynamicDeps bazel.LabelListAttribute wholeArchiveDeps bazel.LabelListAttribute linkopts bazel.StringListAttribute versionScript bazel.LabelAttribute } // FIXME(b/187655838): Use the existing linkerFlags() function instead of duplicating logic here func getBp2BuildLinkerFlags(linkerProperties *BaseLinkerProperties) []string { flags := linkerProperties.Ldflags if !BoolDefault(linkerProperties.Pack_relocations, true) { flags = append(flags, "-Wl,--pack-dyn-relocs=none") } return flags } // bp2BuildParseLinkerProps parses the linker properties of a module, including // configurable attribute values. func bp2BuildParseLinkerProps(ctx android.TopDownMutatorContext, module *Module) linkerAttributes { var deps bazel.LabelListAttribute var dynamicDeps bazel.LabelListAttribute var wholeArchiveDeps bazel.LabelListAttribute var linkopts bazel.StringListAttribute var versionScript bazel.LabelAttribute for _, linkerProps := range module.linker.linkerProps() { if baseLinkerProps, ok := linkerProps.(*BaseLinkerProperties); ok { libs := baseLinkerProps.Header_libs libs = append(libs, baseLinkerProps.Export_header_lib_headers...) libs = append(libs, baseLinkerProps.Static_libs...) wholeArchiveLibs := baseLinkerProps.Whole_static_libs libs = android.SortedUniqueStrings(libs) deps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, libs)) linkopts.Value = getBp2BuildLinkerFlags(baseLinkerProps) wholeArchiveDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, wholeArchiveLibs)) if baseLinkerProps.Version_script != nil { versionScript.Value = android.BazelLabelForModuleSrcSingle(ctx, *baseLinkerProps.Version_script) } sharedLibs := baseLinkerProps.Shared_libs dynamicDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, sharedLibs)) break } } for arch, p := range module.GetArchProperties(ctx, &BaseLinkerProperties{}) { if baseLinkerProps, ok := p.(*BaseLinkerProperties); ok { libs := baseLinkerProps.Header_libs libs = append(libs, baseLinkerProps.Export_header_lib_headers...) libs = append(libs, baseLinkerProps.Static_libs...) wholeArchiveLibs := baseLinkerProps.Whole_static_libs libs = android.SortedUniqueStrings(libs) deps.SetValueForArch(arch.Name, android.BazelLabelForModuleDeps(ctx, libs)) linkopts.SetValueForArch(arch.Name, getBp2BuildLinkerFlags(baseLinkerProps)) wholeArchiveDeps.SetValueForArch(arch.Name, android.BazelLabelForModuleDeps(ctx, wholeArchiveLibs)) if baseLinkerProps.Version_script != nil { versionScript.SetValueForArch(arch.Name, android.BazelLabelForModuleSrcSingle(ctx, *baseLinkerProps.Version_script)) } sharedLibs := baseLinkerProps.Shared_libs dynamicDeps.SetValueForArch(arch.Name, android.BazelLabelForModuleDeps(ctx, sharedLibs)) } } for os, p := range module.GetTargetProperties(&BaseLinkerProperties{}) { if baseLinkerProps, ok := p.(*BaseLinkerProperties); ok { libs := baseLinkerProps.Header_libs libs = append(libs, baseLinkerProps.Export_header_lib_headers...) libs = append(libs, baseLinkerProps.Static_libs...) wholeArchiveLibs := baseLinkerProps.Whole_static_libs libs = android.SortedUniqueStrings(libs) wholeArchiveDeps.SetValueForOS(os.Name, android.BazelLabelForModuleDeps(ctx, wholeArchiveLibs)) deps.SetValueForOS(os.Name, android.BazelLabelForModuleDeps(ctx, libs)) linkopts.SetValueForOS(os.Name, getBp2BuildLinkerFlags(baseLinkerProps)) sharedLibs := baseLinkerProps.Shared_libs dynamicDeps.SetValueForOS(os.Name, android.BazelLabelForModuleDeps(ctx, sharedLibs)) } } return linkerAttributes{ deps: deps, dynamicDeps: dynamicDeps, wholeArchiveDeps: wholeArchiveDeps, linkopts: linkopts, versionScript: versionScript, } } // Relativize a list of root-relative paths with respect to the module's // directory. // // include_dirs Soong prop are root-relative (b/183742505), but // local_include_dirs, export_include_dirs and export_system_include_dirs are // module dir relative. This function makes a list of paths entirely module dir // relative. // // For the `include` attribute, Bazel wants the paths to be relative to the // module. func bp2BuildMakePathsRelativeToModule(ctx android.BazelConversionPathContext, paths []string) []string { var relativePaths []string for _, path := range paths { // Semantics of filepath.Rel: join(ModuleDir, rel(ModuleDir, path)) == path relativePath, err := filepath.Rel(ctx.ModuleDir(), path) if err != nil { panic(err) } relativePaths = append(relativePaths, relativePath) } return relativePaths } // bp2BuildParseExportedIncludes creates a string list attribute contains the // exported included directories of a module. func bp2BuildParseExportedIncludes(ctx android.TopDownMutatorContext, module *Module) bazel.StringListAttribute { libraryDecorator := module.linker.(*libraryDecorator) // Export_system_include_dirs and export_include_dirs are already module dir // relative, so they don't need to be relativized like include_dirs, which // are root-relative. includeDirs := libraryDecorator.flagExporter.Properties.Export_system_include_dirs includeDirs = append(includeDirs, libraryDecorator.flagExporter.Properties.Export_include_dirs...) includeDirsAttribute := bazel.MakeStringListAttribute(includeDirs) for arch, props := range module.GetArchProperties(ctx, &FlagExporterProperties{}) { if flagExporterProperties, ok := props.(*FlagExporterProperties); ok { archIncludeDirs := flagExporterProperties.Export_system_include_dirs archIncludeDirs = append(archIncludeDirs, flagExporterProperties.Export_include_dirs...) // To avoid duplicate includes when base includes + arch includes are combined // FIXME: This doesn't take conflicts between arch and os includes into account archIncludeDirs = bazel.SubtractStrings(archIncludeDirs, includeDirs) if len(archIncludeDirs) > 0 { includeDirsAttribute.SetValueForArch(arch.Name, archIncludeDirs) } } } for os, props := range module.GetTargetProperties(&FlagExporterProperties{}) { if flagExporterProperties, ok := props.(*FlagExporterProperties); ok { osIncludeDirs := flagExporterProperties.Export_system_include_dirs osIncludeDirs = append(osIncludeDirs, flagExporterProperties.Export_include_dirs...) // To avoid duplicate includes when base includes + os includes are combined // FIXME: This doesn't take conflicts between arch and os includes into account osIncludeDirs = bazel.SubtractStrings(osIncludeDirs, includeDirs) if len(osIncludeDirs) > 0 { includeDirsAttribute.SetValueForOS(os.Name, osIncludeDirs) } } } return includeDirsAttribute }