blob: a122509197f03e79e4401ccb1d104890241ba57f [file] [log] [blame]
Hao Chen1c8ea5b2023-10-20 23:03:45 +00001// Copyright 2024 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package cc
16
17import (
Hao Chen1c8ea5b2023-10-20 23:03:45 +000018 "bytes"
19 _ "embed"
20 "fmt"
21 "path/filepath"
22 "slices"
23 "sort"
24 "strings"
25 "text/template"
26
mrziwangf95cfa62024-06-18 10:11:39 -070027 "android/soong/android"
28
Hao Chen1c8ea5b2023-10-20 23:03:45 +000029 "github.com/google/blueprint"
30 "github.com/google/blueprint/proptools"
31)
32
33const veryVerbose bool = false
34
35//go:embed cmake_main.txt
36var templateCmakeMainRaw string
37var templateCmakeMain *template.Template = parseTemplate(templateCmakeMainRaw)
38
39//go:embed cmake_module_cc.txt
40var templateCmakeModuleCcRaw string
41var templateCmakeModuleCc *template.Template = parseTemplate(templateCmakeModuleCcRaw)
42
43//go:embed cmake_module_aidl.txt
44var templateCmakeModuleAidlRaw string
45var templateCmakeModuleAidl *template.Template = parseTemplate(templateCmakeModuleAidlRaw)
46
47//go:embed cmake_ext_add_aidl_library.txt
48var cmakeExtAddAidlLibrary string
49
50//go:embed cmake_ext_append_flags.txt
51var cmakeExtAppendFlags string
52
53var defaultUnportableFlags []string = []string{
54 "-Wno-class-memaccess",
55 "-Wno-exit-time-destructors",
56 "-Wno-inconsistent-missing-override",
57 "-Wreorder-init-list",
58 "-Wno-reorder-init-list",
59 "-Wno-restrict",
60 "-Wno-stringop-overread",
61 "-Wno-subobject-linkage",
62}
63
64var ignoredSystemLibs []string = []string{
Tomasz Wasilczyk2493fcc2024-06-20 15:29:09 -070065 "crtbegin_dynamic",
66 "crtend_android",
67 "libc",
Hao Chen1c8ea5b2023-10-20 23:03:45 +000068 "libc++",
69 "libc++_static",
Tomasz Wasilczyk7cec7e62024-07-01 11:31:47 -070070 "libc_musl",
71 "libc_musl_crtbegin_so",
72 "libc_musl_crtbegin_static",
73 "libc_musl_crtend",
74 "libc_musl_crtend_so",
Tomasz Wasilczyk2493fcc2024-06-20 15:29:09 -070075 "libdl",
76 "libm",
Hao Chen1c8ea5b2023-10-20 23:03:45 +000077 "prebuilt_libclang_rt.builtins",
78 "prebuilt_libclang_rt.ubsan_minimal",
79}
80
81// Mapping entry between Android's library name and the one used when building outside Android tree.
82type LibraryMappingProperty struct {
83 // Android library name.
84 Android_name string
85
86 // Library name used when building outside Android.
87 Mapped_name string
88
89 // If the make file is already present in Android source tree, specify its location.
90 Package_pregenerated string
91
92 // If the package is expected to be installed on the build host OS, specify its name.
93 Package_system string
94}
95
96type CmakeSnapshotProperties struct {
97 // Modules to add to the snapshot package. Their dependencies are pulled in automatically.
98 Modules []string
99
100 // Host prebuilts to bundle with the snapshot. These are tools needed to build outside Android.
101 Prebuilts []string
102
103 // Global cflags to add when building outside Android.
104 Cflags []string
105
106 // Flags to skip when building outside Android.
107 Cflags_ignored []string
108
109 // Mapping between library names used in Android tree and externally.
110 Library_mapping []LibraryMappingProperty
111
112 // List of cflags that are not portable between compilers that could potentially be used to
113 // build a generated package. If left empty, it's initialized with a default list.
114 Unportable_flags []string
115
116 // Whether to include source code as part of the snapshot package.
117 Include_sources bool
118}
119
120var cmakeSnapshotSourcesProvider = blueprint.NewProvider[android.Paths]()
121
122type CmakeSnapshot struct {
123 android.ModuleBase
124
125 Properties CmakeSnapshotProperties
126
127 zipPath android.WritablePath
128}
129
130type cmakeProcessedProperties struct {
131 LibraryMapping map[string]LibraryMappingProperty
132 PregeneratedPackages []string
133 SystemPackages []string
134}
135
136type cmakeSnapshotDependencyTag struct {
137 blueprint.BaseDependencyTag
138 name string
139}
140
141var (
142 cmakeSnapshotModuleTag = cmakeSnapshotDependencyTag{name: "cmake-snapshot-module"}
143 cmakeSnapshotPrebuiltTag = cmakeSnapshotDependencyTag{name: "cmake-snapshot-prebuilt"}
144)
145
146func parseTemplate(templateContents string) *template.Template {
147 funcMap := template.FuncMap{
148 "setList": func(name string, nameSuffix string, itemPrefix string, items []string) string {
149 var list strings.Builder
150 list.WriteString("set(" + name + nameSuffix)
151 templateListBuilder(&list, itemPrefix, items)
152 return list.String()
153 },
154 "toStrings": func(files android.Paths) []string {
Colin Cross7de1db72024-06-25 14:36:24 -0700155 return files.Strings()
Hao Chen1c8ea5b2023-10-20 23:03:45 +0000156 },
157 "concat5": func(list1 []string, list2 []string, list3 []string, list4 []string, list5 []string) []string {
158 return append(append(append(append(list1, list2...), list3...), list4...), list5...)
159 },
160 "cflagsList": func(name string, nameSuffix string, flags []string,
161 unportableFlags []string, ignoredFlags []string) string {
162 if len(unportableFlags) == 0 {
163 unportableFlags = defaultUnportableFlags
164 }
165
166 var filteredPortable []string
167 var filteredUnportable []string
168 for _, flag := range flags {
169 if slices.Contains(ignoredFlags, flag) {
170 continue
171 } else if slices.Contains(unportableFlags, flag) {
172 filteredUnportable = append(filteredUnportable, flag)
173 } else {
174 filteredPortable = append(filteredPortable, flag)
175 }
176 }
177
178 var list strings.Builder
179
180 list.WriteString("set(" + name + nameSuffix)
181 templateListBuilder(&list, "", filteredPortable)
182
183 if len(filteredUnportable) > 0 {
184 list.WriteString("\nappend_cxx_flags_if_supported(" + name + nameSuffix)
185 templateListBuilder(&list, "", filteredUnportable)
186 }
187
188 return list.String()
189 },
190 "getSources": func(m *Module) android.Paths {
191 return m.compiler.(CompiledInterface).Srcs()
192 },
193 "getModuleType": getModuleType,
194 "getCompilerProperties": func(m *Module) BaseCompilerProperties {
195 return m.compiler.baseCompilerProps()
196 },
Cole Fauste96c16a2024-06-13 14:51:14 -0700197 "getCflagsProperty": func(ctx android.ModuleContext, m *Module) []string {
198 cflags := m.compiler.baseCompilerProps().Cflags
199 return cflags.GetOrDefault(ctx, nil)
200 },
Hao Chen1c8ea5b2023-10-20 23:03:45 +0000201 "getLinkerProperties": func(m *Module) BaseLinkerProperties {
202 return m.linker.baseLinkerProps()
203 },
204 "getExtraLibs": getExtraLibs,
205 "getIncludeDirs": getIncludeDirs,
Tomasz Wasilczyk1e831bf2024-05-10 15:15:21 -0700206 "mapLibraries": func(ctx android.ModuleContext, m *Module, libs []string, mapping map[string]LibraryMappingProperty) []string {
Hao Chen1c8ea5b2023-10-20 23:03:45 +0000207 var mappedLibs []string
208 for _, lib := range libs {
209 mappedLib, exists := mapping[lib]
210 if exists {
211 lib = mappedLib.Mapped_name
212 } else {
Tomasz Wasilczyk1e831bf2024-05-10 15:15:21 -0700213 if !ctx.OtherModuleExists(lib) {
214 ctx.OtherModuleErrorf(m, "Dependency %s doesn't exist", lib)
215 }
Hao Chen1c8ea5b2023-10-20 23:03:45 +0000216 lib = "android::" + lib
217 }
218 if lib == "" {
219 continue
220 }
221 mappedLibs = append(mappedLibs, lib)
222 }
223 sort.Strings(mappedLibs)
224 mappedLibs = slices.Compact(mappedLibs)
225 return mappedLibs
226 },
Tomasz Wasilczyk1e831bf2024-05-10 15:15:21 -0700227 "getAidlSources": func(m *Module) []string {
228 aidlInterface := m.compiler.baseCompilerProps().AidlInterface
229 aidlRoot := aidlInterface.AidlRoot + string(filepath.Separator)
230 if aidlInterface.AidlRoot == "" {
231 aidlRoot = ""
232 }
233 var sources []string
234 for _, src := range aidlInterface.Sources {
235 if !strings.HasPrefix(src, aidlRoot) {
236 panic(fmt.Sprintf("Aidl source '%v' doesn't start with '%v'", src, aidlRoot))
237 }
238 sources = append(sources, src[len(aidlRoot):])
239 }
240 return sources
241 },
Hao Chen1c8ea5b2023-10-20 23:03:45 +0000242 }
243
244 return template.Must(template.New("").Delims("<<", ">>").Funcs(funcMap).Parse(templateContents))
245}
246
247func sliceWithPrefix(prefix string, slice []string) []string {
248 output := make([]string, len(slice))
249 for i, elem := range slice {
250 output[i] = prefix + elem
251 }
252 return output
253}
254
255func templateListBuilder(builder *strings.Builder, itemPrefix string, items []string) {
256 if len(items) > 0 {
257 builder.WriteString("\n")
258 for _, item := range items {
259 builder.WriteString(" " + itemPrefix + item + "\n")
260 }
261 }
262 builder.WriteString(")")
263}
264
265func executeTemplate(templ *template.Template, buffer *bytes.Buffer, data any) string {
266 buffer.Reset()
267 if err := templ.Execute(buffer, data); err != nil {
268 panic(err)
269 }
270 output := strings.TrimSpace(buffer.String())
271 buffer.Reset()
272 return output
273}
274
275func (m *CmakeSnapshot) DepsMutator(ctx android.BottomUpMutatorContext) {
Tomasz Wasilczyk9288b512024-06-27 09:22:12 -0700276 hostVariations := ctx.Config().BuildOSTarget.Variations()
277 ctx.AddVariationDependencies(hostVariations, cmakeSnapshotModuleTag, m.Properties.Modules...)
Tomasz Wasilczyk2493fcc2024-06-20 15:29:09 -0700278
279 if len(m.Properties.Prebuilts) > 0 {
280 prebuilts := append(m.Properties.Prebuilts, "libc++")
Tomasz Wasilczyk9288b512024-06-27 09:22:12 -0700281 ctx.AddVariationDependencies(hostVariations, cmakeSnapshotPrebuiltTag, prebuilts...)
Tomasz Wasilczyk2493fcc2024-06-20 15:29:09 -0700282 }
Hao Chen1c8ea5b2023-10-20 23:03:45 +0000283}
284
285func (m *CmakeSnapshot) GenerateAndroidBuildActions(ctx android.ModuleContext) {
286 var templateBuffer bytes.Buffer
287 var pprop cmakeProcessedProperties
288 m.zipPath = android.PathForModuleOut(ctx, ctx.ModuleName()+".zip")
289
290 // Process Library_mapping for more efficient lookups
291 pprop.LibraryMapping = map[string]LibraryMappingProperty{}
292 for _, elem := range m.Properties.Library_mapping {
293 pprop.LibraryMapping[elem.Android_name] = elem
294
295 if elem.Package_pregenerated != "" {
296 pprop.PregeneratedPackages = append(pprop.PregeneratedPackages, elem.Package_pregenerated)
297 }
298 sort.Strings(pprop.PregeneratedPackages)
299 pprop.PregeneratedPackages = slices.Compact(pprop.PregeneratedPackages)
300
301 if elem.Package_system != "" {
302 pprop.SystemPackages = append(pprop.SystemPackages, elem.Package_system)
303 }
304 sort.Strings(pprop.SystemPackages)
305 pprop.SystemPackages = slices.Compact(pprop.SystemPackages)
306 }
307
308 // Generating CMakeLists.txt rules for all modules in dependency tree
309 moduleDirs := map[string][]string{}
310 sourceFiles := map[string]android.Path{}
311 visitedModules := map[string]bool{}
312 var pregeneratedModules []*Module
313 ctx.WalkDeps(func(dep_a android.Module, parent android.Module) bool {
314 moduleName := ctx.OtherModuleName(dep_a)
Hao Chen1c8ea5b2023-10-20 23:03:45 +0000315 if visited := visitedModules[moduleName]; visited {
316 return false // visit only once
317 }
318 visitedModules[moduleName] = true
Tomasz Wasilczyk1e831bf2024-05-10 15:15:21 -0700319 dep, ok := dep_a.(*Module)
320 if !ok {
321 return false // not a cc module
322 }
Hao Chen1c8ea5b2023-10-20 23:03:45 +0000323 if mapping, ok := pprop.LibraryMapping[moduleName]; ok {
324 if mapping.Package_pregenerated != "" {
325 pregeneratedModules = append(pregeneratedModules, dep)
326 }
327 return false // mapped to system or pregenerated (we'll handle these later)
328 }
329 if ctx.OtherModuleDependencyTag(dep) == cmakeSnapshotPrebuiltTag {
330 return false // we'll handle cmakeSnapshotPrebuiltTag later
331 }
332 if slices.Contains(ignoredSystemLibs, moduleName) {
333 return false // system libs built in-tree for Android
334 }
335 if dep.compiler == nil {
336 return false // unsupported module type (e.g. prebuilt)
337 }
338 isAidlModule := dep.compiler.baseCompilerProps().AidlInterface.Lang != ""
339
340 if !proptools.Bool(dep.Properties.Cmake_snapshot_supported) {
341 ctx.OtherModulePropertyErrorf(dep, "cmake_snapshot_supported",
342 "CMake snapshots not supported, despite being a dependency for %s",
343 ctx.OtherModuleName(parent))
344 return false
345 }
346
347 if veryVerbose {
348 fmt.Println("WalkDeps: " + ctx.OtherModuleName(parent) + " -> " + moduleName)
349 }
350
351 // Generate CMakeLists.txt fragment for this module
352 templateToUse := templateCmakeModuleCc
353 if isAidlModule {
354 templateToUse = templateCmakeModuleAidl
355 }
356 moduleFragment := executeTemplate(templateToUse, &templateBuffer, struct {
357 Ctx *android.ModuleContext
358 M *Module
359 Snapshot *CmakeSnapshot
360 Pprop *cmakeProcessedProperties
361 }{
362 &ctx,
363 dep,
364 m,
365 &pprop,
366 })
367 moduleDir := ctx.OtherModuleDir(dep)
368 moduleDirs[moduleDir] = append(moduleDirs[moduleDir], moduleFragment)
369
370 if m.Properties.Include_sources {
371 files, _ := android.OtherModuleProvider(ctx, dep, cmakeSnapshotSourcesProvider)
372 for _, file := range files {
373 sourceFiles[file.String()] = file
374 }
375 }
376
377 // if it's AIDL module, no need to dive into their dependencies
378 return !isAidlModule
379 })
380
381 // Enumerate sources for pregenerated modules
382 if m.Properties.Include_sources {
383 for _, dep := range pregeneratedModules {
384 if !proptools.Bool(dep.Properties.Cmake_snapshot_supported) {
385 ctx.OtherModulePropertyErrorf(dep, "cmake_snapshot_supported",
386 "Pregenerated CMake snapshots not supported, despite being requested for %s",
387 ctx.ModuleName())
388 continue
389 }
390
391 files, _ := android.OtherModuleProvider(ctx, dep, cmakeSnapshotSourcesProvider)
392 for _, file := range files {
393 sourceFiles[file.String()] = file
394 }
395 }
396 }
397
398 // Merging CMakeLists.txt contents for every module directory
399 var makefilesList android.Paths
Colin Cross7de1db72024-06-25 14:36:24 -0700400 for _, moduleDir := range android.SortedKeys(moduleDirs) {
401 fragments := moduleDirs[moduleDir]
Hao Chen1c8ea5b2023-10-20 23:03:45 +0000402 moduleCmakePath := android.PathForModuleGen(ctx, moduleDir, "CMakeLists.txt")
403 makefilesList = append(makefilesList, moduleCmakePath)
404 sort.Strings(fragments)
405 android.WriteFileRule(ctx, moduleCmakePath, strings.Join(fragments, "\n\n\n"))
406 }
407
408 // Generating top-level CMakeLists.txt
409 mainCmakePath := android.PathForModuleGen(ctx, "CMakeLists.txt")
410 makefilesList = append(makefilesList, mainCmakePath)
411 mainContents := executeTemplate(templateCmakeMain, &templateBuffer, struct {
412 Ctx *android.ModuleContext
413 M *CmakeSnapshot
414 ModuleDirs map[string][]string
415 Pprop *cmakeProcessedProperties
416 }{
417 &ctx,
418 m,
419 moduleDirs,
420 &pprop,
421 })
422 android.WriteFileRule(ctx, mainCmakePath, mainContents)
423
424 // Generating CMake extensions
425 extPath := android.PathForModuleGen(ctx, "cmake", "AppendCxxFlagsIfSupported.cmake")
426 makefilesList = append(makefilesList, extPath)
427 android.WriteFileRuleVerbatim(ctx, extPath, cmakeExtAppendFlags)
428 extPath = android.PathForModuleGen(ctx, "cmake", "AddAidlLibrary.cmake")
429 makefilesList = append(makefilesList, extPath)
430 android.WriteFileRuleVerbatim(ctx, extPath, cmakeExtAddAidlLibrary)
431
432 // Generating the final zip file
433 zipRule := android.NewRuleBuilder(pctx, ctx)
434 zipCmd := zipRule.Command().
435 BuiltTool("soong_zip").
436 FlagWithOutput("-o ", m.zipPath)
437
438 // Packaging all sources into the zip file
439 if m.Properties.Include_sources {
440 var sourcesList android.Paths
Colin Cross7de1db72024-06-25 14:36:24 -0700441 for _, file := range android.SortedKeys(sourceFiles) {
442 path := sourceFiles[file]
443 sourcesList = append(sourcesList, path)
Hao Chen1c8ea5b2023-10-20 23:03:45 +0000444 }
445
446 sourcesRspFile := android.PathForModuleObj(ctx, ctx.ModuleName()+"_sources.rsp")
447 zipCmd.FlagWithRspFileInputList("-r ", sourcesRspFile, sourcesList)
448 }
449
450 // Packaging all make files into the zip file
451 makefilesRspFile := android.PathForModuleObj(ctx, ctx.ModuleName()+"_makefiles.rsp")
452 zipCmd.
453 FlagWithArg("-C ", android.PathForModuleGen(ctx).OutputPath.String()).
454 FlagWithRspFileInputList("-r ", makefilesRspFile, makefilesList)
455
456 // Packaging all prebuilts into the zip file
457 if len(m.Properties.Prebuilts) > 0 {
458 var prebuiltsList android.Paths
459
460 ctx.VisitDirectDepsWithTag(cmakeSnapshotPrebuiltTag, func(dep android.Module) {
461 for _, file := range dep.FilesToInstall() {
462 prebuiltsList = append(prebuiltsList, file)
463 }
464 })
465
466 prebuiltsRspFile := android.PathForModuleObj(ctx, ctx.ModuleName()+"_prebuilts.rsp")
467 zipCmd.
468 FlagWithArg("-C ", android.PathForArbitraryOutput(ctx).String()).
469 FlagWithArg("-P ", "prebuilts").
470 FlagWithRspFileInputList("-r ", prebuiltsRspFile, prebuiltsList)
471 }
472
473 // Finish generating the final zip file
474 zipRule.Build(m.zipPath.String(), "archiving "+ctx.ModuleName())
Hao Chen1c8ea5b2023-10-20 23:03:45 +0000475
mrziwangf95cfa62024-06-18 10:11:39 -0700476 ctx.SetOutputFiles(android.Paths{m.zipPath}, "")
Hao Chen1c8ea5b2023-10-20 23:03:45 +0000477}
478
479func (m *CmakeSnapshot) AndroidMkEntries() []android.AndroidMkEntries {
480 return []android.AndroidMkEntries{{
481 Class: "DATA",
482 OutputFile: android.OptionalPathForPath(m.zipPath),
483 ExtraEntries: []android.AndroidMkExtraEntriesFunc{
484 func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
485 entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
486 },
487 },
488 }}
489}
490
491func getModuleType(m *Module) string {
492 switch m.linker.(type) {
493 case *binaryDecorator:
494 return "executable"
495 case *libraryDecorator:
496 return "library"
497 case *testBinary:
Tomasz Wasilczykc3177e02024-06-10 14:38:45 -0700498 return "test"
Tomasz Wasilczyk6e2b8c02024-05-30 07:48:40 -0700499 case *benchmarkDecorator:
Tomasz Wasilczykc3177e02024-06-10 14:38:45 -0700500 return "test"
Hao Chen1c8ea5b2023-10-20 23:03:45 +0000501 }
Tomasz Wasilczyk6e2b8c02024-05-30 07:48:40 -0700502 panic(fmt.Sprintf("Unexpected module type: %T", m.linker))
Hao Chen1c8ea5b2023-10-20 23:03:45 +0000503}
504
505func getExtraLibs(m *Module) []string {
506 switch decorator := m.linker.(type) {
507 case *testBinary:
508 if decorator.testDecorator.gtest() {
Tomasz Wasilczyk6e2b8c02024-05-30 07:48:40 -0700509 return []string{
510 "libgtest",
511 "libgtest_main",
512 }
Hao Chen1c8ea5b2023-10-20 23:03:45 +0000513 }
Tomasz Wasilczyk6e2b8c02024-05-30 07:48:40 -0700514 case *benchmarkDecorator:
515 return []string{"libgoogle-benchmark"}
Hao Chen1c8ea5b2023-10-20 23:03:45 +0000516 }
517 return nil
518}
519
520func getIncludeDirs(ctx android.ModuleContext, m *Module) []string {
521 moduleDir := ctx.OtherModuleDir(m) + string(filepath.Separator)
522 switch decorator := m.compiler.(type) {
523 case *libraryDecorator:
Aleks Todorovc9becde2024-06-10 12:51:53 +0100524 return sliceWithPrefix(moduleDir, decorator.flagExporter.Properties.Export_include_dirs.GetOrDefault(ctx, nil))
Hao Chen1c8ea5b2023-10-20 23:03:45 +0000525 }
526 return nil
527}
528
Tomasz Wasilczykd848dcc2024-05-10 09:16:37 -0700529func cmakeSnapshotLoadHook(ctx android.LoadHookContext) {
530 props := struct {
531 Target struct {
532 Darwin struct {
533 Enabled *bool
534 }
535 Windows struct {
536 Enabled *bool
537 }
538 }
539 }{}
540 props.Target.Darwin.Enabled = proptools.BoolPtr(false)
541 props.Target.Windows.Enabled = proptools.BoolPtr(false)
542 ctx.AppendProperties(&props)
543}
544
Hao Chen1c8ea5b2023-10-20 23:03:45 +0000545// cmake_snapshot allows defining source packages for release outside of Android build tree.
546// As a result of cmake_snapshot module build, a zip file is generated with CMake build definitions
547// for selected source modules, their dependencies and optionally also the source code itself.
548func CmakeSnapshotFactory() android.Module {
549 module := &CmakeSnapshot{}
550 module.AddProperties(&module.Properties)
Tomasz Wasilczykd848dcc2024-05-10 09:16:37 -0700551 android.AddLoadHook(module, cmakeSnapshotLoadHook)
552 android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
Hao Chen1c8ea5b2023-10-20 23:03:45 +0000553 return module
554}
555
556func init() {
557 android.InitRegistrationContext.RegisterModuleType("cc_cmake_snapshot", CmakeSnapshotFactory)
558}