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