Implement cc_cmake_snapshot
Bug: 302718225
Test: cd build/soong/cc && go test
Test: https://r.android.com/2803466
Change-Id: Ie7df635233ed68c40d917ea1f83f9fd4b5bfe729
diff --git a/cc/Android.bp b/cc/Android.bp
index 9ce8933..3bbcaa9 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -51,6 +51,7 @@
"vndk.go",
"vndk_prebuilt.go",
+ "cmake_snapshot.go",
"cmakelists.go",
"compdb.go",
"compiler.go",
@@ -92,6 +93,7 @@
"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 @@
"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 e3954d7..627d758 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -352,6 +352,10 @@
// 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 @@
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 @@
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 @@
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 0000000..dcf805a
--- /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 0000000..2cfb1ac
--- /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 0000000..deb1de1
--- /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()
+
+<<cflagsList .M.Name "_CFLAGS" .M.Properties.Cflags .M.Properties.Unportable_flags .M.Properties.Cflags_ignored>>
+
+<<range .Pprop.SystemPackages ->>
+find_package(<<.>> REQUIRED)
+<<end >>
+<<range .Pprop.PregeneratedPackages ->>
+add_subdirectory("${ANDROID_BUILD_TOP}/<<.>>" "<<.>>/build" EXCLUDE_FROM_ALL)
+<<end>>
+add_compile_options(${<<.M.Name>>_CFLAGS})
+<<range $moduleDir, $value := .ModuleDirs ->>
+add_subdirectory(<<$moduleDir>>)
+<<end>>
diff --git a/cc/cmake_module_aidl.txt b/cc/cmake_module_aidl.txt
new file mode 100644
index 0000000..4509a88
--- /dev/null
+++ b/cc/cmake_module_aidl.txt
@@ -0,0 +1,8 @@
+# <<.M.Name>>
+
+<<setList .M.Name "_SRCS" "${ANDROID_BUILD_TOP}/" (getCompilerProperties .M).AidlInterface.Sources>>
+
+<<setList .M.Name "_AIDLFLAGS" "" (getCompilerProperties .M).AidlInterface.Flags>>
+
+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 0000000..571f27c
--- /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>>
+<<if $srcs>>
+<<setList .M.Name "_SRCS" "${ANDROID_BUILD_TOP}/" (toStrings $srcs)>>
+add_<<getModuleType .M>>(<<.M.Name>> ${<<.M.Name>>_SRCS})
+<<- else>>
+add_<<getModuleType .M>>(<<.M.Name>> INTERFACE)
+<<- end>>
+add_<<getModuleType .M>>(android::<<.M.Name>> ALIAS <<.M.Name>>)
+<<print "">>
+
+<<- if $includeDirs>>
+<<setList .M.Name "_INCLUDES" "${ANDROID_BUILD_TOP}/" $includeDirs>>
+target_include_directories(<<.M.Name>> <<if $srcs>>PUBLIC<<else>>INTERFACE<<end>> ${<<.M.Name>>_INCLUDES})
+<<end>>
+
+<<- if and $srcs $cflags>>
+<<cflagsList .M.Name "_CFLAGS" $cflags .Snapshot.Properties.Unportable_flags .Snapshot.Properties.Cflags_ignored>>
+target_compile_options(<<.M.Name>> PRIVATE ${<<.M.Name>>_CFLAGS})
+<<end>>
+
+<<- if $deps>>
+<<setList .M.Name "_DEPENDENCIES" "" $deps>>
+target_link_libraries(<<.M.Name>> <<if not $srcs>>INTERFACE <<end ->> ${<<.M.Name>>_DEPENDENCIES})
+<<end>>
diff --git a/cc/cmake_snapshot.go b/cc/cmake_snapshot.go
new file mode 100644
index 0000000..3ac7db1
--- /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 0000000..cda187f
--- /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 a1b329e..aee584d 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -278,6 +278,10 @@
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 9686697..4cb8486 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -287,6 +287,10 @@
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 20c435a..5773d31 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -35,6 +35,7 @@
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 @@
// 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,