blob: c0f0571cf2cbd864963acf0d949026f44fdfcb04 [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 Wasilczyk2493fcc2024-06-20 15:29:09 -070070 "libdl",
71 "libm",
Hao Chen1c8ea5b2023-10-20 23:03:45 +000072 "prebuilt_libclang_rt.builtins",
73 "prebuilt_libclang_rt.ubsan_minimal",
74}
75
76// Mapping entry between Android's library name and the one used when building outside Android tree.
77type LibraryMappingProperty struct {
78 // Android library name.
79 Android_name string
80
81 // Library name used when building outside Android.
82 Mapped_name string
83
84 // If the make file is already present in Android source tree, specify its location.
85 Package_pregenerated string
86
87 // If the package is expected to be installed on the build host OS, specify its name.
88 Package_system string
89}
90
91type CmakeSnapshotProperties struct {
92 // Modules to add to the snapshot package. Their dependencies are pulled in automatically.
93 Modules []string
94
95 // Host prebuilts to bundle with the snapshot. These are tools needed to build outside Android.
96 Prebuilts []string
97
98 // Global cflags to add when building outside Android.
99 Cflags []string
100
101 // Flags to skip when building outside Android.
102 Cflags_ignored []string
103
104 // Mapping between library names used in Android tree and externally.
105 Library_mapping []LibraryMappingProperty
106
107 // List of cflags that are not portable between compilers that could potentially be used to
108 // build a generated package. If left empty, it's initialized with a default list.
109 Unportable_flags []string
110
111 // Whether to include source code as part of the snapshot package.
112 Include_sources bool
113}
114
115var cmakeSnapshotSourcesProvider = blueprint.NewProvider[android.Paths]()
116
117type CmakeSnapshot struct {
118 android.ModuleBase
119
120 Properties CmakeSnapshotProperties
121
122 zipPath android.WritablePath
123}
124
125type cmakeProcessedProperties struct {
126 LibraryMapping map[string]LibraryMappingProperty
127 PregeneratedPackages []string
128 SystemPackages []string
129}
130
131type cmakeSnapshotDependencyTag struct {
132 blueprint.BaseDependencyTag
133 name string
134}
135
136var (
137 cmakeSnapshotModuleTag = cmakeSnapshotDependencyTag{name: "cmake-snapshot-module"}
138 cmakeSnapshotPrebuiltTag = cmakeSnapshotDependencyTag{name: "cmake-snapshot-prebuilt"}
139)
140
141func parseTemplate(templateContents string) *template.Template {
142 funcMap := template.FuncMap{
143 "setList": func(name string, nameSuffix string, itemPrefix string, items []string) string {
144 var list strings.Builder
145 list.WriteString("set(" + name + nameSuffix)
146 templateListBuilder(&list, itemPrefix, items)
147 return list.String()
148 },
149 "toStrings": func(files android.Paths) []string {
Colin Cross7de1db72024-06-25 14:36:24 -0700150 return files.Strings()
Hao Chen1c8ea5b2023-10-20 23:03:45 +0000151 },
152 "concat5": func(list1 []string, list2 []string, list3 []string, list4 []string, list5 []string) []string {
153 return append(append(append(append(list1, list2...), list3...), list4...), list5...)
154 },
155 "cflagsList": func(name string, nameSuffix string, flags []string,
156 unportableFlags []string, ignoredFlags []string) string {
157 if len(unportableFlags) == 0 {
158 unportableFlags = defaultUnportableFlags
159 }
160
161 var filteredPortable []string
162 var filteredUnportable []string
163 for _, flag := range flags {
164 if slices.Contains(ignoredFlags, flag) {
165 continue
166 } else if slices.Contains(unportableFlags, flag) {
167 filteredUnportable = append(filteredUnportable, flag)
168 } else {
169 filteredPortable = append(filteredPortable, flag)
170 }
171 }
172
173 var list strings.Builder
174
175 list.WriteString("set(" + name + nameSuffix)
176 templateListBuilder(&list, "", filteredPortable)
177
178 if len(filteredUnportable) > 0 {
179 list.WriteString("\nappend_cxx_flags_if_supported(" + name + nameSuffix)
180 templateListBuilder(&list, "", filteredUnportable)
181 }
182
183 return list.String()
184 },
185 "getSources": func(m *Module) android.Paths {
186 return m.compiler.(CompiledInterface).Srcs()
187 },
188 "getModuleType": getModuleType,
189 "getCompilerProperties": func(m *Module) BaseCompilerProperties {
190 return m.compiler.baseCompilerProps()
191 },
Cole Fauste96c16a2024-06-13 14:51:14 -0700192 "getCflagsProperty": func(ctx android.ModuleContext, m *Module) []string {
193 cflags := m.compiler.baseCompilerProps().Cflags
194 return cflags.GetOrDefault(ctx, nil)
195 },
Hao Chen1c8ea5b2023-10-20 23:03:45 +0000196 "getLinkerProperties": func(m *Module) BaseLinkerProperties {
197 return m.linker.baseLinkerProps()
198 },
199 "getExtraLibs": getExtraLibs,
200 "getIncludeDirs": getIncludeDirs,
Tomasz Wasilczyk1e831bf2024-05-10 15:15:21 -0700201 "mapLibraries": func(ctx android.ModuleContext, m *Module, libs []string, mapping map[string]LibraryMappingProperty) []string {
Hao Chen1c8ea5b2023-10-20 23:03:45 +0000202 var mappedLibs []string
203 for _, lib := range libs {
204 mappedLib, exists := mapping[lib]
205 if exists {
206 lib = mappedLib.Mapped_name
207 } else {
Tomasz Wasilczyk1e831bf2024-05-10 15:15:21 -0700208 if !ctx.OtherModuleExists(lib) {
209 ctx.OtherModuleErrorf(m, "Dependency %s doesn't exist", lib)
210 }
Hao Chen1c8ea5b2023-10-20 23:03:45 +0000211 lib = "android::" + lib
212 }
213 if lib == "" {
214 continue
215 }
216 mappedLibs = append(mappedLibs, lib)
217 }
218 sort.Strings(mappedLibs)
219 mappedLibs = slices.Compact(mappedLibs)
220 return mappedLibs
221 },
Tomasz Wasilczyk1e831bf2024-05-10 15:15:21 -0700222 "getAidlSources": func(m *Module) []string {
223 aidlInterface := m.compiler.baseCompilerProps().AidlInterface
224 aidlRoot := aidlInterface.AidlRoot + string(filepath.Separator)
225 if aidlInterface.AidlRoot == "" {
226 aidlRoot = ""
227 }
228 var sources []string
229 for _, src := range aidlInterface.Sources {
230 if !strings.HasPrefix(src, aidlRoot) {
231 panic(fmt.Sprintf("Aidl source '%v' doesn't start with '%v'", src, aidlRoot))
232 }
233 sources = append(sources, src[len(aidlRoot):])
234 }
235 return sources
236 },
Hao Chen1c8ea5b2023-10-20 23:03:45 +0000237 }
238
239 return template.Must(template.New("").Delims("<<", ">>").Funcs(funcMap).Parse(templateContents))
240}
241
242func sliceWithPrefix(prefix string, slice []string) []string {
243 output := make([]string, len(slice))
244 for i, elem := range slice {
245 output[i] = prefix + elem
246 }
247 return output
248}
249
250func templateListBuilder(builder *strings.Builder, itemPrefix string, items []string) {
251 if len(items) > 0 {
252 builder.WriteString("\n")
253 for _, item := range items {
254 builder.WriteString(" " + itemPrefix + item + "\n")
255 }
256 }
257 builder.WriteString(")")
258}
259
260func executeTemplate(templ *template.Template, buffer *bytes.Buffer, data any) string {
261 buffer.Reset()
262 if err := templ.Execute(buffer, data); err != nil {
263 panic(err)
264 }
265 output := strings.TrimSpace(buffer.String())
266 buffer.Reset()
267 return output
268}
269
270func (m *CmakeSnapshot) DepsMutator(ctx android.BottomUpMutatorContext) {
271 variations := []blueprint.Variation{
272 {"os", "linux_glibc"},
273 {"arch", "x86_64"},
274 }
275 ctx.AddVariationDependencies(variations, cmakeSnapshotModuleTag, m.Properties.Modules...)
Tomasz Wasilczyk2493fcc2024-06-20 15:29:09 -0700276
277 if len(m.Properties.Prebuilts) > 0 {
278 prebuilts := append(m.Properties.Prebuilts, "libc++")
279 ctx.AddVariationDependencies(variations, cmakeSnapshotPrebuiltTag, prebuilts...)
280 }
Hao Chen1c8ea5b2023-10-20 23:03:45 +0000281}
282
283func (m *CmakeSnapshot) GenerateAndroidBuildActions(ctx android.ModuleContext) {
284 var templateBuffer bytes.Buffer
285 var pprop cmakeProcessedProperties
286 m.zipPath = android.PathForModuleOut(ctx, ctx.ModuleName()+".zip")
287
288 // Process Library_mapping for more efficient lookups
289 pprop.LibraryMapping = map[string]LibraryMappingProperty{}
290 for _, elem := range m.Properties.Library_mapping {
291 pprop.LibraryMapping[elem.Android_name] = elem
292
293 if elem.Package_pregenerated != "" {
294 pprop.PregeneratedPackages = append(pprop.PregeneratedPackages, elem.Package_pregenerated)
295 }
296 sort.Strings(pprop.PregeneratedPackages)
297 pprop.PregeneratedPackages = slices.Compact(pprop.PregeneratedPackages)
298
299 if elem.Package_system != "" {
300 pprop.SystemPackages = append(pprop.SystemPackages, elem.Package_system)
301 }
302 sort.Strings(pprop.SystemPackages)
303 pprop.SystemPackages = slices.Compact(pprop.SystemPackages)
304 }
305
306 // Generating CMakeLists.txt rules for all modules in dependency tree
307 moduleDirs := map[string][]string{}
308 sourceFiles := map[string]android.Path{}
309 visitedModules := map[string]bool{}
310 var pregeneratedModules []*Module
311 ctx.WalkDeps(func(dep_a android.Module, parent android.Module) bool {
312 moduleName := ctx.OtherModuleName(dep_a)
Hao Chen1c8ea5b2023-10-20 23:03:45 +0000313 if visited := visitedModules[moduleName]; visited {
314 return false // visit only once
315 }
316 visitedModules[moduleName] = true
Tomasz Wasilczyk1e831bf2024-05-10 15:15:21 -0700317 dep, ok := dep_a.(*Module)
318 if !ok {
319 return false // not a cc module
320 }
Hao Chen1c8ea5b2023-10-20 23:03:45 +0000321 if mapping, ok := pprop.LibraryMapping[moduleName]; ok {
322 if mapping.Package_pregenerated != "" {
323 pregeneratedModules = append(pregeneratedModules, dep)
324 }
325 return false // mapped to system or pregenerated (we'll handle these later)
326 }
327 if ctx.OtherModuleDependencyTag(dep) == cmakeSnapshotPrebuiltTag {
328 return false // we'll handle cmakeSnapshotPrebuiltTag later
329 }
330 if slices.Contains(ignoredSystemLibs, moduleName) {
331 return false // system libs built in-tree for Android
332 }
333 if dep.compiler == nil {
334 return false // unsupported module type (e.g. prebuilt)
335 }
336 isAidlModule := dep.compiler.baseCompilerProps().AidlInterface.Lang != ""
337
338 if !proptools.Bool(dep.Properties.Cmake_snapshot_supported) {
339 ctx.OtherModulePropertyErrorf(dep, "cmake_snapshot_supported",
340 "CMake snapshots not supported, despite being a dependency for %s",
341 ctx.OtherModuleName(parent))
342 return false
343 }
344
345 if veryVerbose {
346 fmt.Println("WalkDeps: " + ctx.OtherModuleName(parent) + " -> " + moduleName)
347 }
348
349 // Generate CMakeLists.txt fragment for this module
350 templateToUse := templateCmakeModuleCc
351 if isAidlModule {
352 templateToUse = templateCmakeModuleAidl
353 }
354 moduleFragment := executeTemplate(templateToUse, &templateBuffer, struct {
355 Ctx *android.ModuleContext
356 M *Module
357 Snapshot *CmakeSnapshot
358 Pprop *cmakeProcessedProperties
359 }{
360 &ctx,
361 dep,
362 m,
363 &pprop,
364 })
365 moduleDir := ctx.OtherModuleDir(dep)
366 moduleDirs[moduleDir] = append(moduleDirs[moduleDir], moduleFragment)
367
368 if m.Properties.Include_sources {
369 files, _ := android.OtherModuleProvider(ctx, dep, cmakeSnapshotSourcesProvider)
370 for _, file := range files {
371 sourceFiles[file.String()] = file
372 }
373 }
374
375 // if it's AIDL module, no need to dive into their dependencies
376 return !isAidlModule
377 })
378
379 // Enumerate sources for pregenerated modules
380 if m.Properties.Include_sources {
381 for _, dep := range pregeneratedModules {
382 if !proptools.Bool(dep.Properties.Cmake_snapshot_supported) {
383 ctx.OtherModulePropertyErrorf(dep, "cmake_snapshot_supported",
384 "Pregenerated CMake snapshots not supported, despite being requested for %s",
385 ctx.ModuleName())
386 continue
387 }
388
389 files, _ := android.OtherModuleProvider(ctx, dep, cmakeSnapshotSourcesProvider)
390 for _, file := range files {
391 sourceFiles[file.String()] = file
392 }
393 }
394 }
395
396 // Merging CMakeLists.txt contents for every module directory
397 var makefilesList android.Paths
Colin Cross7de1db72024-06-25 14:36:24 -0700398 for _, moduleDir := range android.SortedKeys(moduleDirs) {
399 fragments := moduleDirs[moduleDir]
Hao Chen1c8ea5b2023-10-20 23:03:45 +0000400 moduleCmakePath := android.PathForModuleGen(ctx, moduleDir, "CMakeLists.txt")
401 makefilesList = append(makefilesList, moduleCmakePath)
402 sort.Strings(fragments)
403 android.WriteFileRule(ctx, moduleCmakePath, strings.Join(fragments, "\n\n\n"))
404 }
405
406 // Generating top-level CMakeLists.txt
407 mainCmakePath := android.PathForModuleGen(ctx, "CMakeLists.txt")
408 makefilesList = append(makefilesList, mainCmakePath)
409 mainContents := executeTemplate(templateCmakeMain, &templateBuffer, struct {
410 Ctx *android.ModuleContext
411 M *CmakeSnapshot
412 ModuleDirs map[string][]string
413 Pprop *cmakeProcessedProperties
414 }{
415 &ctx,
416 m,
417 moduleDirs,
418 &pprop,
419 })
420 android.WriteFileRule(ctx, mainCmakePath, mainContents)
421
422 // Generating CMake extensions
423 extPath := android.PathForModuleGen(ctx, "cmake", "AppendCxxFlagsIfSupported.cmake")
424 makefilesList = append(makefilesList, extPath)
425 android.WriteFileRuleVerbatim(ctx, extPath, cmakeExtAppendFlags)
426 extPath = android.PathForModuleGen(ctx, "cmake", "AddAidlLibrary.cmake")
427 makefilesList = append(makefilesList, extPath)
428 android.WriteFileRuleVerbatim(ctx, extPath, cmakeExtAddAidlLibrary)
429
430 // Generating the final zip file
431 zipRule := android.NewRuleBuilder(pctx, ctx)
432 zipCmd := zipRule.Command().
433 BuiltTool("soong_zip").
434 FlagWithOutput("-o ", m.zipPath)
435
436 // Packaging all sources into the zip file
437 if m.Properties.Include_sources {
438 var sourcesList android.Paths
Colin Cross7de1db72024-06-25 14:36:24 -0700439 for _, file := range android.SortedKeys(sourceFiles) {
440 path := sourceFiles[file]
441 sourcesList = append(sourcesList, path)
Hao Chen1c8ea5b2023-10-20 23:03:45 +0000442 }
443
444 sourcesRspFile := android.PathForModuleObj(ctx, ctx.ModuleName()+"_sources.rsp")
445 zipCmd.FlagWithRspFileInputList("-r ", sourcesRspFile, sourcesList)
446 }
447
448 // Packaging all make files into the zip file
449 makefilesRspFile := android.PathForModuleObj(ctx, ctx.ModuleName()+"_makefiles.rsp")
450 zipCmd.
451 FlagWithArg("-C ", android.PathForModuleGen(ctx).OutputPath.String()).
452 FlagWithRspFileInputList("-r ", makefilesRspFile, makefilesList)
453
454 // Packaging all prebuilts into the zip file
455 if len(m.Properties.Prebuilts) > 0 {
456 var prebuiltsList android.Paths
457
458 ctx.VisitDirectDepsWithTag(cmakeSnapshotPrebuiltTag, func(dep android.Module) {
459 for _, file := range dep.FilesToInstall() {
460 prebuiltsList = append(prebuiltsList, file)
461 }
462 })
463
464 prebuiltsRspFile := android.PathForModuleObj(ctx, ctx.ModuleName()+"_prebuilts.rsp")
465 zipCmd.
466 FlagWithArg("-C ", android.PathForArbitraryOutput(ctx).String()).
467 FlagWithArg("-P ", "prebuilts").
468 FlagWithRspFileInputList("-r ", prebuiltsRspFile, prebuiltsList)
469 }
470
471 // Finish generating the final zip file
472 zipRule.Build(m.zipPath.String(), "archiving "+ctx.ModuleName())
Hao Chen1c8ea5b2023-10-20 23:03:45 +0000473
mrziwangf95cfa62024-06-18 10:11:39 -0700474 ctx.SetOutputFiles(android.Paths{m.zipPath}, "")
Hao Chen1c8ea5b2023-10-20 23:03:45 +0000475}
476
477func (m *CmakeSnapshot) AndroidMkEntries() []android.AndroidMkEntries {
478 return []android.AndroidMkEntries{{
479 Class: "DATA",
480 OutputFile: android.OptionalPathForPath(m.zipPath),
481 ExtraEntries: []android.AndroidMkExtraEntriesFunc{
482 func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
483 entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
484 },
485 },
486 }}
487}
488
489func getModuleType(m *Module) string {
490 switch m.linker.(type) {
491 case *binaryDecorator:
492 return "executable"
493 case *libraryDecorator:
494 return "library"
495 case *testBinary:
Tomasz Wasilczykc3177e02024-06-10 14:38:45 -0700496 return "test"
Tomasz Wasilczyk6e2b8c02024-05-30 07:48:40 -0700497 case *benchmarkDecorator:
Tomasz Wasilczykc3177e02024-06-10 14:38:45 -0700498 return "test"
Hao Chen1c8ea5b2023-10-20 23:03:45 +0000499 }
Tomasz Wasilczyk6e2b8c02024-05-30 07:48:40 -0700500 panic(fmt.Sprintf("Unexpected module type: %T", m.linker))
Hao Chen1c8ea5b2023-10-20 23:03:45 +0000501}
502
503func getExtraLibs(m *Module) []string {
504 switch decorator := m.linker.(type) {
505 case *testBinary:
506 if decorator.testDecorator.gtest() {
Tomasz Wasilczyk6e2b8c02024-05-30 07:48:40 -0700507 return []string{
508 "libgtest",
509 "libgtest_main",
510 }
Hao Chen1c8ea5b2023-10-20 23:03:45 +0000511 }
Tomasz Wasilczyk6e2b8c02024-05-30 07:48:40 -0700512 case *benchmarkDecorator:
513 return []string{"libgoogle-benchmark"}
Hao Chen1c8ea5b2023-10-20 23:03:45 +0000514 }
515 return nil
516}
517
518func getIncludeDirs(ctx android.ModuleContext, m *Module) []string {
519 moduleDir := ctx.OtherModuleDir(m) + string(filepath.Separator)
520 switch decorator := m.compiler.(type) {
521 case *libraryDecorator:
Aleks Todorovc9becde2024-06-10 12:51:53 +0100522 return sliceWithPrefix(moduleDir, decorator.flagExporter.Properties.Export_include_dirs.GetOrDefault(ctx, nil))
Hao Chen1c8ea5b2023-10-20 23:03:45 +0000523 }
524 return nil
525}
526
Tomasz Wasilczykd848dcc2024-05-10 09:16:37 -0700527func cmakeSnapshotLoadHook(ctx android.LoadHookContext) {
528 props := struct {
529 Target struct {
530 Darwin struct {
531 Enabled *bool
532 }
533 Windows struct {
534 Enabled *bool
535 }
536 }
537 }{}
538 props.Target.Darwin.Enabled = proptools.BoolPtr(false)
539 props.Target.Windows.Enabled = proptools.BoolPtr(false)
540 ctx.AppendProperties(&props)
541}
542
Hao Chen1c8ea5b2023-10-20 23:03:45 +0000543// cmake_snapshot allows defining source packages for release outside of Android build tree.
544// As a result of cmake_snapshot module build, a zip file is generated with CMake build definitions
545// for selected source modules, their dependencies and optionally also the source code itself.
546func CmakeSnapshotFactory() android.Module {
547 module := &CmakeSnapshot{}
548 module.AddProperties(&module.Properties)
Tomasz Wasilczykd848dcc2024-05-10 09:16:37 -0700549 android.AddLoadHook(module, cmakeSnapshotLoadHook)
550 android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
Hao Chen1c8ea5b2023-10-20 23:03:45 +0000551 return module
552}
553
554func init() {
555 android.InitRegistrationContext.RegisterModuleType("cc_cmake_snapshot", CmakeSnapshotFactory)
556}