c3177e0ffc
Bug: 302718225 Test: atest binder_sdk_test Change-Id: Id841d73d11f3b0c75f7bcd523be8ff69300a503e
551 lines
17 KiB
Go
551 lines
17 KiB
Go
// Copyright 2024 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"
|
|
"bytes"
|
|
_ "embed"
|
|
"fmt"
|
|
"path/filepath"
|
|
"slices"
|
|
"sort"
|
|
"strings"
|
|
"text/template"
|
|
|
|
"github.com/google/blueprint"
|
|
"github.com/google/blueprint/proptools"
|
|
)
|
|
|
|
const veryVerbose bool = false
|
|
|
|
//go:embed cmake_main.txt
|
|
var templateCmakeMainRaw string
|
|
var templateCmakeMain *template.Template = parseTemplate(templateCmakeMainRaw)
|
|
|
|
//go:embed cmake_module_cc.txt
|
|
var templateCmakeModuleCcRaw string
|
|
var templateCmakeModuleCc *template.Template = parseTemplate(templateCmakeModuleCcRaw)
|
|
|
|
//go:embed cmake_module_aidl.txt
|
|
var templateCmakeModuleAidlRaw string
|
|
var templateCmakeModuleAidl *template.Template = parseTemplate(templateCmakeModuleAidlRaw)
|
|
|
|
//go:embed cmake_ext_add_aidl_library.txt
|
|
var cmakeExtAddAidlLibrary string
|
|
|
|
//go:embed cmake_ext_append_flags.txt
|
|
var cmakeExtAppendFlags string
|
|
|
|
var defaultUnportableFlags []string = []string{
|
|
"-Wno-class-memaccess",
|
|
"-Wno-exit-time-destructors",
|
|
"-Wno-inconsistent-missing-override",
|
|
"-Wreorder-init-list",
|
|
"-Wno-reorder-init-list",
|
|
"-Wno-restrict",
|
|
"-Wno-stringop-overread",
|
|
"-Wno-subobject-linkage",
|
|
}
|
|
|
|
var ignoredSystemLibs []string = []string{
|
|
"libc++",
|
|
"libc++_static",
|
|
"prebuilt_libclang_rt.builtins",
|
|
"prebuilt_libclang_rt.ubsan_minimal",
|
|
}
|
|
|
|
// Mapping entry between Android's library name and the one used when building outside Android tree.
|
|
type LibraryMappingProperty struct {
|
|
// Android library name.
|
|
Android_name string
|
|
|
|
// Library name used when building outside Android.
|
|
Mapped_name string
|
|
|
|
// If the make file is already present in Android source tree, specify its location.
|
|
Package_pregenerated string
|
|
|
|
// If the package is expected to be installed on the build host OS, specify its name.
|
|
Package_system string
|
|
}
|
|
|
|
type CmakeSnapshotProperties struct {
|
|
// Modules to add to the snapshot package. Their dependencies are pulled in automatically.
|
|
Modules []string
|
|
|
|
// Host prebuilts to bundle with the snapshot. These are tools needed to build outside Android.
|
|
Prebuilts []string
|
|
|
|
// Global cflags to add when building outside Android.
|
|
Cflags []string
|
|
|
|
// Flags to skip when building outside Android.
|
|
Cflags_ignored []string
|
|
|
|
// Mapping between library names used in Android tree and externally.
|
|
Library_mapping []LibraryMappingProperty
|
|
|
|
// List of cflags that are not portable between compilers that could potentially be used to
|
|
// build a generated package. If left empty, it's initialized with a default list.
|
|
Unportable_flags []string
|
|
|
|
// Whether to include source code as part of the snapshot package.
|
|
Include_sources bool
|
|
}
|
|
|
|
var cmakeSnapshotSourcesProvider = blueprint.NewProvider[android.Paths]()
|
|
|
|
type CmakeSnapshot struct {
|
|
android.ModuleBase
|
|
|
|
Properties CmakeSnapshotProperties
|
|
|
|
zipPath android.WritablePath
|
|
}
|
|
|
|
type cmakeProcessedProperties struct {
|
|
LibraryMapping map[string]LibraryMappingProperty
|
|
PregeneratedPackages []string
|
|
SystemPackages []string
|
|
}
|
|
|
|
type cmakeSnapshotDependencyTag struct {
|
|
blueprint.BaseDependencyTag
|
|
name string
|
|
}
|
|
|
|
var (
|
|
cmakeSnapshotModuleTag = cmakeSnapshotDependencyTag{name: "cmake-snapshot-module"}
|
|
cmakeSnapshotPrebuiltTag = cmakeSnapshotDependencyTag{name: "cmake-snapshot-prebuilt"}
|
|
)
|
|
|
|
func parseTemplate(templateContents string) *template.Template {
|
|
funcMap := template.FuncMap{
|
|
"setList": func(name string, nameSuffix string, itemPrefix string, items []string) string {
|
|
var list strings.Builder
|
|
list.WriteString("set(" + name + nameSuffix)
|
|
templateListBuilder(&list, itemPrefix, items)
|
|
return list.String()
|
|
},
|
|
"toStrings": func(files android.Paths) []string {
|
|
strings := make([]string, len(files))
|
|
for idx, file := range files {
|
|
strings[idx] = file.String()
|
|
}
|
|
return strings
|
|
},
|
|
"concat5": func(list1 []string, list2 []string, list3 []string, list4 []string, list5 []string) []string {
|
|
return append(append(append(append(list1, list2...), list3...), list4...), list5...)
|
|
},
|
|
"cflagsList": func(name string, nameSuffix string, flags []string,
|
|
unportableFlags []string, ignoredFlags []string) string {
|
|
if len(unportableFlags) == 0 {
|
|
unportableFlags = defaultUnportableFlags
|
|
}
|
|
|
|
var filteredPortable []string
|
|
var filteredUnportable []string
|
|
for _, flag := range flags {
|
|
if slices.Contains(ignoredFlags, flag) {
|
|
continue
|
|
} else if slices.Contains(unportableFlags, flag) {
|
|
filteredUnportable = append(filteredUnportable, flag)
|
|
} else {
|
|
filteredPortable = append(filteredPortable, flag)
|
|
}
|
|
}
|
|
|
|
var list strings.Builder
|
|
|
|
list.WriteString("set(" + name + nameSuffix)
|
|
templateListBuilder(&list, "", filteredPortable)
|
|
|
|
if len(filteredUnportable) > 0 {
|
|
list.WriteString("\nappend_cxx_flags_if_supported(" + name + nameSuffix)
|
|
templateListBuilder(&list, "", filteredUnportable)
|
|
}
|
|
|
|
return list.String()
|
|
},
|
|
"getSources": func(m *Module) android.Paths {
|
|
return m.compiler.(CompiledInterface).Srcs()
|
|
},
|
|
"getModuleType": getModuleType,
|
|
"getCompilerProperties": func(m *Module) BaseCompilerProperties {
|
|
return m.compiler.baseCompilerProps()
|
|
},
|
|
"getLinkerProperties": func(m *Module) BaseLinkerProperties {
|
|
return m.linker.baseLinkerProps()
|
|
},
|
|
"getExtraLibs": getExtraLibs,
|
|
"getIncludeDirs": getIncludeDirs,
|
|
"mapLibraries": func(ctx android.ModuleContext, m *Module, libs []string, mapping map[string]LibraryMappingProperty) []string {
|
|
var mappedLibs []string
|
|
for _, lib := range libs {
|
|
mappedLib, exists := mapping[lib]
|
|
if exists {
|
|
lib = mappedLib.Mapped_name
|
|
} else {
|
|
if !ctx.OtherModuleExists(lib) {
|
|
ctx.OtherModuleErrorf(m, "Dependency %s doesn't exist", lib)
|
|
}
|
|
lib = "android::" + lib
|
|
}
|
|
if lib == "" {
|
|
continue
|
|
}
|
|
mappedLibs = append(mappedLibs, lib)
|
|
}
|
|
sort.Strings(mappedLibs)
|
|
mappedLibs = slices.Compact(mappedLibs)
|
|
return mappedLibs
|
|
},
|
|
"getAidlSources": func(m *Module) []string {
|
|
aidlInterface := m.compiler.baseCompilerProps().AidlInterface
|
|
aidlRoot := aidlInterface.AidlRoot + string(filepath.Separator)
|
|
if aidlInterface.AidlRoot == "" {
|
|
aidlRoot = ""
|
|
}
|
|
var sources []string
|
|
for _, src := range aidlInterface.Sources {
|
|
if !strings.HasPrefix(src, aidlRoot) {
|
|
panic(fmt.Sprintf("Aidl source '%v' doesn't start with '%v'", src, aidlRoot))
|
|
}
|
|
sources = append(sources, src[len(aidlRoot):])
|
|
}
|
|
return sources
|
|
},
|
|
}
|
|
|
|
return template.Must(template.New("").Delims("<<", ">>").Funcs(funcMap).Parse(templateContents))
|
|
}
|
|
|
|
func sliceWithPrefix(prefix string, slice []string) []string {
|
|
output := make([]string, len(slice))
|
|
for i, elem := range slice {
|
|
output[i] = prefix + elem
|
|
}
|
|
return output
|
|
}
|
|
|
|
func templateListBuilder(builder *strings.Builder, itemPrefix string, items []string) {
|
|
if len(items) > 0 {
|
|
builder.WriteString("\n")
|
|
for _, item := range items {
|
|
builder.WriteString(" " + itemPrefix + item + "\n")
|
|
}
|
|
}
|
|
builder.WriteString(")")
|
|
}
|
|
|
|
func executeTemplate(templ *template.Template, buffer *bytes.Buffer, data any) string {
|
|
buffer.Reset()
|
|
if err := templ.Execute(buffer, data); err != nil {
|
|
panic(err)
|
|
}
|
|
output := strings.TrimSpace(buffer.String())
|
|
buffer.Reset()
|
|
return output
|
|
}
|
|
|
|
func (m *CmakeSnapshot) DepsMutator(ctx android.BottomUpMutatorContext) {
|
|
variations := []blueprint.Variation{
|
|
{"os", "linux_glibc"},
|
|
{"arch", "x86_64"},
|
|
}
|
|
ctx.AddVariationDependencies(variations, cmakeSnapshotModuleTag, m.Properties.Modules...)
|
|
ctx.AddVariationDependencies(variations, cmakeSnapshotPrebuiltTag, m.Properties.Prebuilts...)
|
|
}
|
|
|
|
func (m *CmakeSnapshot) GenerateAndroidBuildActions(ctx android.ModuleContext) {
|
|
var templateBuffer bytes.Buffer
|
|
var pprop cmakeProcessedProperties
|
|
m.zipPath = android.PathForModuleOut(ctx, ctx.ModuleName()+".zip")
|
|
|
|
// Process Library_mapping for more efficient lookups
|
|
pprop.LibraryMapping = map[string]LibraryMappingProperty{}
|
|
for _, elem := range m.Properties.Library_mapping {
|
|
pprop.LibraryMapping[elem.Android_name] = elem
|
|
|
|
if elem.Package_pregenerated != "" {
|
|
pprop.PregeneratedPackages = append(pprop.PregeneratedPackages, elem.Package_pregenerated)
|
|
}
|
|
sort.Strings(pprop.PregeneratedPackages)
|
|
pprop.PregeneratedPackages = slices.Compact(pprop.PregeneratedPackages)
|
|
|
|
if elem.Package_system != "" {
|
|
pprop.SystemPackages = append(pprop.SystemPackages, elem.Package_system)
|
|
}
|
|
sort.Strings(pprop.SystemPackages)
|
|
pprop.SystemPackages = slices.Compact(pprop.SystemPackages)
|
|
}
|
|
|
|
// Generating CMakeLists.txt rules for all modules in dependency tree
|
|
moduleDirs := map[string][]string{}
|
|
sourceFiles := map[string]android.Path{}
|
|
visitedModules := map[string]bool{}
|
|
var pregeneratedModules []*Module
|
|
ctx.WalkDeps(func(dep_a android.Module, parent android.Module) bool {
|
|
moduleName := ctx.OtherModuleName(dep_a)
|
|
if visited := visitedModules[moduleName]; visited {
|
|
return false // visit only once
|
|
}
|
|
visitedModules[moduleName] = true
|
|
dep, ok := dep_a.(*Module)
|
|
if !ok {
|
|
return false // not a cc module
|
|
}
|
|
if mapping, ok := pprop.LibraryMapping[moduleName]; ok {
|
|
if mapping.Package_pregenerated != "" {
|
|
pregeneratedModules = append(pregeneratedModules, dep)
|
|
}
|
|
return false // mapped to system or pregenerated (we'll handle these later)
|
|
}
|
|
if ctx.OtherModuleDependencyTag(dep) == cmakeSnapshotPrebuiltTag {
|
|
return false // we'll handle cmakeSnapshotPrebuiltTag later
|
|
}
|
|
if slices.Contains(ignoredSystemLibs, moduleName) {
|
|
return false // system libs built in-tree for Android
|
|
}
|
|
if dep.compiler == nil {
|
|
return false // unsupported module type (e.g. prebuilt)
|
|
}
|
|
isAidlModule := dep.compiler.baseCompilerProps().AidlInterface.Lang != ""
|
|
|
|
if !proptools.Bool(dep.Properties.Cmake_snapshot_supported) {
|
|
ctx.OtherModulePropertyErrorf(dep, "cmake_snapshot_supported",
|
|
"CMake snapshots not supported, despite being a dependency for %s",
|
|
ctx.OtherModuleName(parent))
|
|
return false
|
|
}
|
|
|
|
if veryVerbose {
|
|
fmt.Println("WalkDeps: " + ctx.OtherModuleName(parent) + " -> " + moduleName)
|
|
}
|
|
|
|
// Generate CMakeLists.txt fragment for this module
|
|
templateToUse := templateCmakeModuleCc
|
|
if isAidlModule {
|
|
templateToUse = templateCmakeModuleAidl
|
|
}
|
|
moduleFragment := executeTemplate(templateToUse, &templateBuffer, struct {
|
|
Ctx *android.ModuleContext
|
|
M *Module
|
|
Snapshot *CmakeSnapshot
|
|
Pprop *cmakeProcessedProperties
|
|
}{
|
|
&ctx,
|
|
dep,
|
|
m,
|
|
&pprop,
|
|
})
|
|
moduleDir := ctx.OtherModuleDir(dep)
|
|
moduleDirs[moduleDir] = append(moduleDirs[moduleDir], moduleFragment)
|
|
|
|
if m.Properties.Include_sources {
|
|
files, _ := android.OtherModuleProvider(ctx, dep, cmakeSnapshotSourcesProvider)
|
|
for _, file := range files {
|
|
sourceFiles[file.String()] = file
|
|
}
|
|
}
|
|
|
|
// if it's AIDL module, no need to dive into their dependencies
|
|
return !isAidlModule
|
|
})
|
|
|
|
// Enumerate sources for pregenerated modules
|
|
if m.Properties.Include_sources {
|
|
for _, dep := range pregeneratedModules {
|
|
if !proptools.Bool(dep.Properties.Cmake_snapshot_supported) {
|
|
ctx.OtherModulePropertyErrorf(dep, "cmake_snapshot_supported",
|
|
"Pregenerated CMake snapshots not supported, despite being requested for %s",
|
|
ctx.ModuleName())
|
|
continue
|
|
}
|
|
|
|
files, _ := android.OtherModuleProvider(ctx, dep, cmakeSnapshotSourcesProvider)
|
|
for _, file := range files {
|
|
sourceFiles[file.String()] = file
|
|
}
|
|
}
|
|
}
|
|
|
|
// Merging CMakeLists.txt contents for every module directory
|
|
var makefilesList android.Paths
|
|
for moduleDir, fragments := range moduleDirs {
|
|
moduleCmakePath := android.PathForModuleGen(ctx, moduleDir, "CMakeLists.txt")
|
|
makefilesList = append(makefilesList, moduleCmakePath)
|
|
sort.Strings(fragments)
|
|
android.WriteFileRule(ctx, moduleCmakePath, strings.Join(fragments, "\n\n\n"))
|
|
}
|
|
|
|
// Generating top-level CMakeLists.txt
|
|
mainCmakePath := android.PathForModuleGen(ctx, "CMakeLists.txt")
|
|
makefilesList = append(makefilesList, mainCmakePath)
|
|
mainContents := executeTemplate(templateCmakeMain, &templateBuffer, struct {
|
|
Ctx *android.ModuleContext
|
|
M *CmakeSnapshot
|
|
ModuleDirs map[string][]string
|
|
Pprop *cmakeProcessedProperties
|
|
}{
|
|
&ctx,
|
|
m,
|
|
moduleDirs,
|
|
&pprop,
|
|
})
|
|
android.WriteFileRule(ctx, mainCmakePath, mainContents)
|
|
|
|
// Generating CMake extensions
|
|
extPath := android.PathForModuleGen(ctx, "cmake", "AppendCxxFlagsIfSupported.cmake")
|
|
makefilesList = append(makefilesList, extPath)
|
|
android.WriteFileRuleVerbatim(ctx, extPath, cmakeExtAppendFlags)
|
|
extPath = android.PathForModuleGen(ctx, "cmake", "AddAidlLibrary.cmake")
|
|
makefilesList = append(makefilesList, extPath)
|
|
android.WriteFileRuleVerbatim(ctx, extPath, cmakeExtAddAidlLibrary)
|
|
|
|
// Generating the final zip file
|
|
zipRule := android.NewRuleBuilder(pctx, ctx)
|
|
zipCmd := zipRule.Command().
|
|
BuiltTool("soong_zip").
|
|
FlagWithOutput("-o ", m.zipPath)
|
|
|
|
// Packaging all sources into the zip file
|
|
if m.Properties.Include_sources {
|
|
var sourcesList android.Paths
|
|
for _, file := range sourceFiles {
|
|
sourcesList = append(sourcesList, file)
|
|
}
|
|
|
|
sourcesRspFile := android.PathForModuleObj(ctx, ctx.ModuleName()+"_sources.rsp")
|
|
zipCmd.FlagWithRspFileInputList("-r ", sourcesRspFile, sourcesList)
|
|
}
|
|
|
|
// Packaging all make files into the zip file
|
|
makefilesRspFile := android.PathForModuleObj(ctx, ctx.ModuleName()+"_makefiles.rsp")
|
|
zipCmd.
|
|
FlagWithArg("-C ", android.PathForModuleGen(ctx).OutputPath.String()).
|
|
FlagWithRspFileInputList("-r ", makefilesRspFile, makefilesList)
|
|
|
|
// Packaging all prebuilts into the zip file
|
|
if len(m.Properties.Prebuilts) > 0 {
|
|
var prebuiltsList android.Paths
|
|
|
|
ctx.VisitDirectDepsWithTag(cmakeSnapshotPrebuiltTag, func(dep android.Module) {
|
|
for _, file := range dep.FilesToInstall() {
|
|
prebuiltsList = append(prebuiltsList, file)
|
|
}
|
|
})
|
|
|
|
prebuiltsRspFile := android.PathForModuleObj(ctx, ctx.ModuleName()+"_prebuilts.rsp")
|
|
zipCmd.
|
|
FlagWithArg("-C ", android.PathForArbitraryOutput(ctx).String()).
|
|
FlagWithArg("-P ", "prebuilts").
|
|
FlagWithRspFileInputList("-r ", prebuiltsRspFile, prebuiltsList)
|
|
}
|
|
|
|
// Finish generating the final zip file
|
|
zipRule.Build(m.zipPath.String(), "archiving "+ctx.ModuleName())
|
|
}
|
|
|
|
func (m *CmakeSnapshot) OutputFiles(tag string) (android.Paths, error) {
|
|
switch tag {
|
|
case "":
|
|
return android.Paths{m.zipPath}, nil
|
|
default:
|
|
return nil, fmt.Errorf("unsupported module reference tag %q", tag)
|
|
}
|
|
}
|
|
|
|
func (m *CmakeSnapshot) AndroidMkEntries() []android.AndroidMkEntries {
|
|
return []android.AndroidMkEntries{{
|
|
Class: "DATA",
|
|
OutputFile: android.OptionalPathForPath(m.zipPath),
|
|
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
|
|
func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
|
|
entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
|
|
},
|
|
},
|
|
}}
|
|
}
|
|
|
|
func getModuleType(m *Module) string {
|
|
switch m.linker.(type) {
|
|
case *binaryDecorator:
|
|
return "executable"
|
|
case *libraryDecorator:
|
|
return "library"
|
|
case *testBinary:
|
|
return "test"
|
|
case *benchmarkDecorator:
|
|
return "test"
|
|
}
|
|
panic(fmt.Sprintf("Unexpected module type: %T", m.linker))
|
|
}
|
|
|
|
func getExtraLibs(m *Module) []string {
|
|
switch decorator := m.linker.(type) {
|
|
case *testBinary:
|
|
if decorator.testDecorator.gtest() {
|
|
return []string{
|
|
"libgtest",
|
|
"libgtest_main",
|
|
}
|
|
}
|
|
case *benchmarkDecorator:
|
|
return []string{"libgoogle-benchmark"}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func getIncludeDirs(ctx android.ModuleContext, m *Module) []string {
|
|
moduleDir := ctx.OtherModuleDir(m) + string(filepath.Separator)
|
|
switch decorator := m.compiler.(type) {
|
|
case *libraryDecorator:
|
|
return sliceWithPrefix(moduleDir, decorator.flagExporter.Properties.Export_include_dirs.GetOrDefault(ctx, nil))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func cmakeSnapshotLoadHook(ctx android.LoadHookContext) {
|
|
props := struct {
|
|
Target struct {
|
|
Darwin struct {
|
|
Enabled *bool
|
|
}
|
|
Windows struct {
|
|
Enabled *bool
|
|
}
|
|
}
|
|
}{}
|
|
props.Target.Darwin.Enabled = proptools.BoolPtr(false)
|
|
props.Target.Windows.Enabled = proptools.BoolPtr(false)
|
|
ctx.AppendProperties(&props)
|
|
}
|
|
|
|
// cmake_snapshot allows defining source packages for release outside of Android build tree.
|
|
// As a result of cmake_snapshot module build, a zip file is generated with CMake build definitions
|
|
// for selected source modules, their dependencies and optionally also the source code itself.
|
|
func CmakeSnapshotFactory() android.Module {
|
|
module := &CmakeSnapshot{}
|
|
module.AddProperties(&module.Properties)
|
|
android.AddLoadHook(module, cmakeSnapshotLoadHook)
|
|
android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
|
|
return module
|
|
}
|
|
|
|
func init() {
|
|
android.InitRegistrationContext.RegisterModuleType("cc_cmake_snapshot", CmakeSnapshotFactory)
|
|
}
|