From 1c8ea5b6e19fce275e907f31ea165b9f6d734c53 Mon Sep 17 00:00:00 2001 From: Hao Chen Date: Fri, 20 Oct 2023 23:03:45 +0000 Subject: [PATCH] Implement cc_cmake_snapshot Bug: 302718225 Test: cd build/soong/cc && go test Test: https://r.android.com/2803466 Change-Id: Ie7df635233ed68c40d917ea1f83f9fd4b5bfe729 --- cc/Android.bp | 9 + cc/cc.go | 10 + cc/cmake_ext_add_aidl_library.txt | 47 +++ cc/cmake_ext_append_flags.txt | 10 + cc/cmake_main.txt | 29 ++ cc/cmake_module_aidl.txt | 8 + cc/cmake_module_cc.txt | 35 ++ cc/cmake_snapshot.go | 509 ++++++++++++++++++++++++++++++ cc/cmake_snapshot_test.go | 69 ++++ cc/compiler.go | 4 + cc/linker.go | 4 + cc/testing.go | 23 +- 12 files changed, 746 insertions(+), 11 deletions(-) create mode 100644 cc/cmake_ext_add_aidl_library.txt create mode 100644 cc/cmake_ext_append_flags.txt create mode 100644 cc/cmake_main.txt create mode 100644 cc/cmake_module_aidl.txt create mode 100644 cc/cmake_module_cc.txt create mode 100644 cc/cmake_snapshot.go create mode 100644 cc/cmake_snapshot_test.go diff --git a/cc/Android.bp b/cc/Android.bp index 9ce89330e..3bbcaa92e 100644 --- a/cc/Android.bp +++ b/cc/Android.bp @@ -51,6 +51,7 @@ bootstrap_go_package { "vndk.go", "vndk_prebuilt.go", + "cmake_snapshot.go", "cmakelists.go", "compdb.go", "compiler.go", @@ -92,6 +93,7 @@ bootstrap_go_package { "binary_test.go", "cc_test.go", "cc_test_only_property_test.go", + "cmake_snapshot_test.go", "compiler_test.go", "gen_test.go", "genrule_test.go", @@ -109,5 +111,12 @@ bootstrap_go_package { "tidy_test.go", "vendor_public_library_test.go", ], + embedSrcs: [ + "cmake_ext_add_aidl_library.txt", + "cmake_ext_append_flags.txt", + "cmake_main.txt", + "cmake_module_aidl.txt", + "cmake_module_cc.txt", + ], pluginFor: ["soong_build"], } diff --git a/cc/cc.go b/cc/cc.go index e3954d7a8..627d75898 100644 --- a/cc/cc.go +++ b/cc/cc.go @@ -352,6 +352,10 @@ type BaseProperties struct { // for building binaries that are started before APEXes are activated. Bootstrap *bool + // Allows this module to be included in CMake release snapshots to be built outside of Android + // build system and source tree. + Cmake_snapshot_supported *bool + // Even if DeviceConfig().VndkUseCoreVariant() is set, this module must use vendor variant. // see soong/cc/config/vndk.go MustUseVendorVariant bool `blueprint:"mutated"` @@ -587,6 +591,7 @@ type compiler interface { compilerDeps(ctx DepsContext, deps Deps) Deps compilerFlags(ctx ModuleContext, flags Flags, deps PathDeps) Flags compilerProps() []interface{} + baseCompilerProps() BaseCompilerProperties appendCflags([]string) appendAsflags([]string) @@ -601,6 +606,7 @@ type linker interface { linkerDeps(ctx DepsContext, deps Deps) Deps linkerFlags(ctx ModuleContext, flags Flags) Flags linkerProps() []interface{} + baseLinkerProps() BaseLinkerProperties useClangLld(actx ModuleContext) bool link(ctx ModuleContext, flags Flags, deps PathDeps, objs Objects) android.Path @@ -2155,6 +2161,10 @@ func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: deps.GeneratedSources.Strings()}) + if Bool(c.Properties.Cmake_snapshot_supported) { + android.SetProvider(ctx, cmakeSnapshotSourcesProvider, android.GlobFiles(ctx, ctx.ModuleDir()+"/**/*", nil)) + } + android.CollectDependencyAconfigFiles(ctx, &c.mergedAconfigFiles) c.maybeInstall(ctx, apexInfo) diff --git a/cc/cmake_ext_add_aidl_library.txt b/cc/cmake_ext_add_aidl_library.txt new file mode 100644 index 000000000..dcf805a78 --- /dev/null +++ b/cc/cmake_ext_add_aidl_library.txt @@ -0,0 +1,47 @@ +function(add_aidl_library NAME LANG SOURCES AIDLFLAGS) + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.20") + cmake_policy(SET CMP0116 NEW) + endif() + + set(GEN_DIR "${CMAKE_CURRENT_BINARY_DIR}/.intermediates/${NAME}-source") + set(GEN_SOURCES) + foreach(SOURCE ${SOURCES}) + get_filename_component(SOURCE_WE ${SOURCE} NAME_WE) + get_filename_component(SOURCE_ABSOLUTE ${SOURCE} ABSOLUTE) + get_filename_component(SOURCE_DIR ${SOURCE_ABSOLUTE} DIRECTORY) + set(GEN_SOURCE "${GEN_DIR}/${SOURCE_WE}.cpp") + set(DEPFILE_ARG) + if (NOT ${CMAKE_GENERATOR} MATCHES "Unix Makefiles") + set(DEPFILE_ARG DEPFILE "${GEN_SOURCE}.d") + endif() + add_custom_command( + OUTPUT "${GEN_SOURCE}" + MAIN_DEPENDENCY "${SOURCE_ABSOLUTE}" + ${DEPFILE_ARG} + COMMAND "${AIDL_BIN}" + ARGS + --lang=${LANG} + --include="${SOURCE_DIR}" + --dep="${GEN_SOURCE}.d" + --out="${GEN_DIR}" + --header_out="${GEN_DIR}/include" + --ninja + --structured + --min_sdk_version=current + ${AIDLFLAGS} + "${SOURCE_ABSOLUTE}" + ) + list(APPEND GEN_SOURCES "${GEN_SOURCE}") + endforeach() + + add_library(${NAME} ${GEN_SOURCES}) + + target_include_directories(${NAME} + PUBLIC + "${GEN_DIR}/include" + "${ANDROID_BUILD_TOP}/frameworks/native/libs/binder/ndk/include_${LANG}" + ) + target_link_libraries(${NAME} + libbinder_sdk + ) +endfunction() diff --git a/cc/cmake_ext_append_flags.txt b/cc/cmake_ext_append_flags.txt new file mode 100644 index 000000000..2cfb1aca9 --- /dev/null +++ b/cc/cmake_ext_append_flags.txt @@ -0,0 +1,10 @@ +include(CheckCXXCompilerFlag) + +macro(append_cxx_flags_if_supported VAR) + foreach(FLAG ${ARGN}) + check_cxx_compiler_flag(${FLAG} HAS_FLAG${FLAG}) + if(${HAS_FLAG${FLAG}}) + list(APPEND ${VAR} ${FLAG}) + endif() + endforeach() +endmacro() diff --git a/cc/cmake_main.txt b/cc/cmake_main.txt new file mode 100644 index 000000000..deb1de1f8 --- /dev/null +++ b/cc/cmake_main.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.18) +project(<<.M.Name>> CXX) +set(CMAKE_CXX_STANDARD 20) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake") +include(AddAidlLibrary) +include(AppendCxxFlagsIfSupported) + +if (NOT ANDROID_BUILD_TOP) + set(ANDROID_BUILD_TOP "${CMAKE_CURRENT_SOURCE_DIR}") +endif() + +set(PREBUILTS_BIN_DIR "${CMAKE_CURRENT_SOURCE_DIR}/prebuilts/host/linux-x86/bin") +if (NOT AIDL_BIN) + find_program(AIDL_BIN aidl REQUIRED HINTS "${PREBUILTS_BIN_DIR}") +endif() + +<> + +<> +find_package(<<.>> REQUIRED) +<> +<> +add_subdirectory("${ANDROID_BUILD_TOP}/<<.>>" "<<.>>/build" EXCLUDE_FROM_ALL) +<> +add_compile_options(${<<.M.Name>>_CFLAGS}) +<> +add_subdirectory(<<$moduleDir>>) +<> diff --git a/cc/cmake_module_aidl.txt b/cc/cmake_module_aidl.txt new file mode 100644 index 000000000..4509a88eb --- /dev/null +++ b/cc/cmake_module_aidl.txt @@ -0,0 +1,8 @@ +# <<.M.Name>> + +<> + +<> + +add_aidl_library(<<.M.Name>> <<(getCompilerProperties .M).AidlInterface.Lang>> "${<<.M.Name>>_SRCS}" "${<<.M.Name>>_AIDLFLAGS}") +add_library(android::<<.M.Name>> ALIAS <<.M.Name>>) diff --git a/cc/cmake_module_cc.txt b/cc/cmake_module_cc.txt new file mode 100644 index 000000000..571f27ce5 --- /dev/null +++ b/cc/cmake_module_cc.txt @@ -0,0 +1,35 @@ +<<$srcs := getSources .M>> +<<$includeDirs := getIncludeDirs .Ctx .M>> +<<$cflags := (getCompilerProperties .M).Cflags>> +<<$deps := mapLibraries (concat5 +(getLinkerProperties .M).Whole_static_libs +(getLinkerProperties .M).Static_libs +(getLinkerProperties .M).Shared_libs +(getLinkerProperties .M).Header_libs +(getExtraLibs .M) +) .Pprop.LibraryMapping>> + +# <<.M.Name>> +<> +<> +add_<>(<<.M.Name>> ${<<.M.Name>>_SRCS}) +<<- else>> +add_<>(<<.M.Name>> INTERFACE) +<<- end>> +add_<>(android::<<.M.Name>> ALIAS <<.M.Name>>) +<> + +<<- if $includeDirs>> +<> +target_include_directories(<<.M.Name>> <>PUBLIC<>INTERFACE<> ${<<.M.Name>>_INCLUDES}) +<> + +<<- if and $srcs $cflags>> +<> +target_compile_options(<<.M.Name>> PRIVATE ${<<.M.Name>>_CFLAGS}) +<> + +<<- if $deps>> +<> +target_link_libraries(<<.M.Name>> <>INTERFACE <> ${<<.M.Name>>_DEPENDENCIES}) +<> diff --git a/cc/cmake_snapshot.go b/cc/cmake_snapshot.go new file mode 100644 index 000000000..3ac7db18a --- /dev/null +++ b/cc/cmake_snapshot.go @@ -0,0 +1,509 @@ +// 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(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 { + lib = "android::" + lib + } + if lib == "" { + continue + } + mappedLibs = append(mappedLibs, lib) + } + sort.Strings(mappedLibs) + mappedLibs = slices.Compact(mappedLibs) + return mappedLibs + }, + } + + 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) + dep, ok := dep_a.(*Module) + if !ok { + return false // not a cc module + } + if visited := visitedModules[moduleName]; visited { + return false // visit only once + } + visitedModules[moduleName] = true + 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 "executable" + } + panic(fmt.Sprintf("Unexpected module type: %T", m.compiler)) +} + +func getExtraLibs(m *Module) []string { + switch decorator := m.linker.(type) { + case *testBinary: + if decorator.testDecorator.gtest() { + return []string{"libgtest"} + } + } + 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) + } + return nil +} + +// 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.InitAndroidModule(module) + return module +} + +func init() { + android.InitRegistrationContext.RegisterModuleType("cc_cmake_snapshot", CmakeSnapshotFactory) +} diff --git a/cc/cmake_snapshot_test.go b/cc/cmake_snapshot_test.go new file mode 100644 index 000000000..cda187f1d --- /dev/null +++ b/cc/cmake_snapshot_test.go @@ -0,0 +1,69 @@ +// 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 ( + "strings" + "testing" + + "android/soong/android" +) + +func wasGenerated(t *testing.T, m *android.TestingModule, fileName string, ruleType string) { + t.Helper() + ruleName := m.Output(fileName).Rule.String() + if !strings.HasSuffix(ruleName, ruleType) { + t.Errorf("Main Cmake file wasn't generated, expected rule %v, found %v", ruleType, ruleName) + } +} + +func TestEmptyCmakeSnapshot(t *testing.T) { + t.Parallel() + result := PrepareForIntegrationTestWithCc.RunTestWithBp(t, ` + cc_cmake_snapshot { + name: "foo", + modules: [], + prebuilts: ["libc++"], + include_sources: true, + }`) + + snapshotModule := result.ModuleForTests("foo", "") + + wasGenerated(t, &snapshotModule, "CMakeLists.txt", "rawFileCopy") + wasGenerated(t, &snapshotModule, "foo.zip", "") +} + +func TestCmakeSnapshotWithBinary(t *testing.T) { + t.Parallel() + xtra := android.FixtureAddTextFile("some/module/Android.bp", ` + cc_binary { + name: "foo_binary", + host_supported: true, + cmake_snapshot_supported: true, + } + `) + result := android.GroupFixturePreparers(PrepareForIntegrationTestWithCc, xtra).RunTestWithBp(t, ` + cc_cmake_snapshot { + name: "foo", + modules: [ + "foo_binary", + ], + include_sources: true, + }`) + + snapshotModule := result.ModuleForTests("foo", "") + + wasGenerated(t, &snapshotModule, "some/module/CMakeLists.txt", "rawFileCopy") +} diff --git a/cc/compiler.go b/cc/compiler.go index a1b329e2c..aee584daf 100644 --- a/cc/compiler.go +++ b/cc/compiler.go @@ -278,6 +278,10 @@ func (compiler *baseCompiler) compilerProps() []interface{} { return []interface{}{&compiler.Properties, &compiler.Proto} } +func (compiler *baseCompiler) baseCompilerProps() BaseCompilerProperties { + return compiler.Properties +} + func includeBuildDirectory(prop *bool) bool { return proptools.BoolDefault(prop, true) } diff --git a/cc/linker.go b/cc/linker.go index 9686697c8..4cb8486b9 100644 --- a/cc/linker.go +++ b/cc/linker.go @@ -287,6 +287,10 @@ func (linker *baseLinker) linkerProps() []interface{} { return []interface{}{&linker.Properties, &linker.dynamicProperties} } +func (linker *baseLinker) baseLinkerProps() BaseLinkerProperties { + return linker.Properties +} + func (linker *baseLinker) linkerDeps(ctx DepsContext, deps Deps) Deps { deps.WholeStaticLibs = append(deps.WholeStaticLibs, linker.Properties.Whole_static_libs...) deps.HeaderLibs = append(deps.HeaderLibs, linker.Properties.Header_libs...) diff --git a/cc/testing.go b/cc/testing.go index 20c435aca..5773d3115 100644 --- a/cc/testing.go +++ b/cc/testing.go @@ -35,6 +35,7 @@ func RegisterRequiredBuildComponentsForTest(ctx android.RegistrationContext) { ctx.RegisterModuleType("prebuilt_build_tool", android.NewPrebuiltBuildTool) ctx.RegisterModuleType("cc_benchmark", BenchmarkFactory) + ctx.RegisterModuleType("cc_cmake_snapshot", CmakeSnapshotFactory) ctx.RegisterModuleType("cc_object", ObjectFactory) ctx.RegisterModuleType("cc_genrule", GenRuleFactory) ctx.RegisterModuleType("ndk_prebuilt_shared_stl", NdkPrebuiltSharedStlFactory) @@ -570,17 +571,17 @@ var PrepareForTestWithCcDefaultModules = android.GroupFixturePreparers( // Additional files needed in tests that disallow non-existent source. android.MockFS{ - "defaults/cc/common/libc.map.txt": nil, - "defaults/cc/common/libdl.map.txt": nil, - "defaults/cc/common/libft2.map.txt": nil, - "defaults/cc/common/libm.map.txt": nil, - "defaults/cc/common/ndk_libc++_shared": nil, - "defaults/cc/common/crtbegin_so.c": nil, - "defaults/cc/common/crtbegin.c": nil, - "defaults/cc/common/crtend_so.c": nil, - "defaults/cc/common/crtend.c": nil, - "defaults/cc/common/crtbrand.c": nil, - "external/compiler-rt/lib/cfi/cfi_blocklist.txt": nil, + "defaults/cc/common/libc.map.txt": nil, + "defaults/cc/common/libdl.map.txt": nil, + "defaults/cc/common/libft2.map.txt": nil, + "defaults/cc/common/libm.map.txt": nil, + "defaults/cc/common/ndk_libc++_shared": nil, + "defaults/cc/common/crtbegin_so.c": nil, + "defaults/cc/common/crtbegin.c": nil, + "defaults/cc/common/crtend_so.c": nil, + "defaults/cc/common/crtend.c": nil, + "defaults/cc/common/crtbrand.c": nil, + "external/compiler-rt/lib/cfi/cfi_blocklist.txt": nil, "defaults/cc/common/libclang_rt.ubsan_minimal.android_arm64.a": nil, "defaults/cc/common/libclang_rt.ubsan_minimal.android_arm.a": nil,