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