1// Copyright 2021 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.
14package cc
15
16import (
17	"android/soong/android"
18	"android/soong/bazel"
19	"path/filepath"
20	"strings"
21)
22
23// bp2build functions and helpers for converting cc_* modules to Bazel.
24
25func init() {
26	android.DepsBp2BuildMutators(RegisterDepsBp2Build)
27}
28
29func RegisterDepsBp2Build(ctx android.RegisterMutatorsContext) {
30	ctx.BottomUp("cc_bp2build_deps", depsBp2BuildMutator)
31}
32
33// A naive deps mutator to add deps on all modules across all combinations of
34// target props for cc modules. This is needed to make module -> bazel label
35// resolution work in the bp2build mutator later. This is probably
36// the wrong way to do it, but it works.
37//
38// TODO(jingwen): can we create a custom os mutator in depsBp2BuildMutator to do this?
39func depsBp2BuildMutator(ctx android.BottomUpMutatorContext) {
40	module, ok := ctx.Module().(*Module)
41	if !ok {
42		// Not a cc module
43		return
44	}
45
46	if !module.ConvertWithBp2build(ctx) {
47		return
48	}
49
50	var allDeps []string
51
52	for _, p := range module.GetTargetProperties(&BaseLinkerProperties{}) {
53		// arch specific linker props
54		if baseLinkerProps, ok := p.(*BaseLinkerProperties); ok {
55			allDeps = append(allDeps, baseLinkerProps.Header_libs...)
56			allDeps = append(allDeps, baseLinkerProps.Export_header_lib_headers...)
57			allDeps = append(allDeps, baseLinkerProps.Static_libs...)
58			allDeps = append(allDeps, baseLinkerProps.Whole_static_libs...)
59		}
60	}
61
62	for _, p := range module.GetArchProperties(ctx, &BaseLinkerProperties{}) {
63		// arch specific linker props
64		if baseLinkerProps, ok := p.(*BaseLinkerProperties); ok {
65			allDeps = append(allDeps, baseLinkerProps.Header_libs...)
66			allDeps = append(allDeps, baseLinkerProps.Export_header_lib_headers...)
67			allDeps = append(allDeps, baseLinkerProps.Static_libs...)
68			allDeps = append(allDeps, baseLinkerProps.Whole_static_libs...)
69		}
70	}
71
72	// Deps in the static: { .. } and shared: { .. } props of a cc_library.
73	if lib, ok := module.compiler.(*libraryDecorator); ok {
74		allDeps = append(allDeps, lib.SharedProperties.Shared.Static_libs...)
75		allDeps = append(allDeps, lib.SharedProperties.Shared.Whole_static_libs...)
76		allDeps = append(allDeps, lib.SharedProperties.Shared.Shared_libs...)
77		allDeps = append(allDeps, lib.SharedProperties.Shared.System_shared_libs...)
78
79		allDeps = append(allDeps, lib.StaticProperties.Static.Static_libs...)
80		allDeps = append(allDeps, lib.StaticProperties.Static.Whole_static_libs...)
81		allDeps = append(allDeps, lib.StaticProperties.Static.Shared_libs...)
82		allDeps = append(allDeps, lib.StaticProperties.Static.System_shared_libs...)
83	}
84
85	ctx.AddDependency(module, nil, android.SortedUniqueStrings(allDeps)...)
86}
87
88type sharedAttributes struct {
89	copts            bazel.StringListAttribute
90	srcs             bazel.LabelListAttribute
91	staticDeps       bazel.LabelListAttribute
92	dynamicDeps      bazel.LabelListAttribute
93	wholeArchiveDeps bazel.LabelListAttribute
94}
95
96// bp2buildParseSharedProps returns the attributes for the shared variant of a cc_library.
97func bp2BuildParseSharedProps(ctx android.TopDownMutatorContext, module *Module) sharedAttributes {
98	lib, ok := module.compiler.(*libraryDecorator)
99	if !ok {
100		return sharedAttributes{}
101	}
102
103	copts := bazel.StringListAttribute{Value: lib.SharedProperties.Shared.Cflags}
104
105	srcs := bazel.LabelListAttribute{
106		Value: android.BazelLabelForModuleSrc(ctx, lib.SharedProperties.Shared.Srcs)}
107
108	staticDeps := bazel.LabelListAttribute{
109		Value: android.BazelLabelForModuleDeps(ctx, lib.SharedProperties.Shared.Static_libs)}
110
111	dynamicDeps := bazel.LabelListAttribute{
112		Value: android.BazelLabelForModuleDeps(ctx, lib.SharedProperties.Shared.Shared_libs)}
113
114	wholeArchiveDeps := bazel.LabelListAttribute{
115		Value: android.BazelLabelForModuleDeps(ctx, lib.SharedProperties.Shared.Whole_static_libs)}
116
117	return sharedAttributes{
118		copts:            copts,
119		srcs:             srcs,
120		staticDeps:       staticDeps,
121		dynamicDeps:      dynamicDeps,
122		wholeArchiveDeps: wholeArchiveDeps,
123	}
124}
125
126type staticAttributes struct {
127	copts            bazel.StringListAttribute
128	srcs             bazel.LabelListAttribute
129	staticDeps       bazel.LabelListAttribute
130	dynamicDeps      bazel.LabelListAttribute
131	wholeArchiveDeps bazel.LabelListAttribute
132}
133
134// bp2buildParseStaticProps returns the attributes for the static variant of a cc_library.
135func bp2BuildParseStaticProps(ctx android.TopDownMutatorContext, module *Module) staticAttributes {
136	lib, ok := module.compiler.(*libraryDecorator)
137	if !ok {
138		return staticAttributes{}
139	}
140
141	copts := bazel.StringListAttribute{Value: lib.StaticProperties.Static.Cflags}
142
143	srcs := bazel.LabelListAttribute{
144		Value: android.BazelLabelForModuleSrc(ctx, lib.StaticProperties.Static.Srcs)}
145
146	staticDeps := bazel.LabelListAttribute{
147		Value: android.BazelLabelForModuleDeps(ctx, lib.StaticProperties.Static.Static_libs)}
148
149	dynamicDeps := bazel.LabelListAttribute{
150		Value: android.BazelLabelForModuleDeps(ctx, lib.StaticProperties.Static.Shared_libs)}
151
152	wholeArchiveDeps := bazel.LabelListAttribute{
153		Value: android.BazelLabelForModuleDeps(ctx, lib.StaticProperties.Static.Whole_static_libs)}
154
155	return staticAttributes{
156		copts:            copts,
157		srcs:             srcs,
158		staticDeps:       staticDeps,
159		dynamicDeps:      dynamicDeps,
160		wholeArchiveDeps: wholeArchiveDeps,
161	}
162}
163
164// Convenience struct to hold all attributes parsed from compiler properties.
165type compilerAttributes struct {
166	copts    bazel.StringListAttribute
167	srcs     bazel.LabelListAttribute
168	includes bazel.StringListAttribute
169}
170
171// bp2BuildParseCompilerProps returns copts, srcs and hdrs and other attributes.
172func bp2BuildParseCompilerProps(ctx android.TopDownMutatorContext, module *Module) compilerAttributes {
173	var srcs bazel.LabelListAttribute
174	var copts bazel.StringListAttribute
175
176	// Creates the -I flag for a directory, while making the directory relative
177	// to the exec root for Bazel to work.
178	includeFlag := func(dir string) string {
179		// filepath.Join canonicalizes the path, i.e. it takes care of . or .. elements.
180		return "-I" + filepath.Join(ctx.ModuleDir(), dir)
181	}
182
183	// Parse the list of module-relative include directories (-I).
184	parseLocalIncludeDirs := func(baseCompilerProps *BaseCompilerProperties) []string {
185		// include_dirs are root-relative, not module-relative.
186		includeDirs := bp2BuildMakePathsRelativeToModule(ctx, baseCompilerProps.Include_dirs)
187		return append(includeDirs, baseCompilerProps.Local_include_dirs...)
188	}
189
190	// Parse the list of copts.
191	parseCopts := func(baseCompilerProps *BaseCompilerProperties) []string {
192		var copts []string
193		for _, flag := range append(baseCompilerProps.Cflags, baseCompilerProps.Cppflags...) {
194			// Soong's cflags can contain spaces, like `-include header.h`. For
195			// Bazel's copts, split them up to be compatible with the
196			// no_copts_tokenization feature.
197			copts = append(copts, strings.Split(flag, " ")...)
198		}
199		for _, dir := range parseLocalIncludeDirs(baseCompilerProps) {
200			copts = append(copts, includeFlag(dir))
201		}
202		return copts
203	}
204
205	// baseSrcs contain the list of src files that are used for every configuration.
206	var baseSrcs []string
207	// baseExcludeSrcs contain the list of src files that are excluded for every configuration.
208	var baseExcludeSrcs []string
209	// baseSrcsLabelList is a clone of the base srcs LabelList, used for computing the
210	// arch or os specific srcs later.
211	var baseSrcsLabelList bazel.LabelList
212
213	// Parse srcs from an arch or OS's props value, taking the base srcs and
214	// exclude srcs into account.
215	parseSrcs := func(baseCompilerProps *BaseCompilerProperties) bazel.LabelList {
216		// Combine the base srcs and arch-specific srcs
217		allSrcs := append(baseSrcs, baseCompilerProps.Srcs...)
218		// Combine the base exclude_srcs and configuration-specific exclude_srcs
219		allExcludeSrcs := append(baseExcludeSrcs, baseCompilerProps.Exclude_srcs...)
220		return android.BazelLabelForModuleSrcExcludes(ctx, allSrcs, allExcludeSrcs)
221	}
222
223	for _, props := range module.compiler.compilerProps() {
224		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
225			srcs.Value = parseSrcs(baseCompilerProps)
226			copts.Value = parseCopts(baseCompilerProps)
227
228			// Used for arch-specific srcs later.
229			baseSrcs = baseCompilerProps.Srcs
230			baseExcludeSrcs = baseCompilerProps.Exclude_srcs
231			baseSrcsLabelList = parseSrcs(baseCompilerProps)
232			break
233		}
234	}
235
236	// Handle include_build_directory prop. If the property is true, then the
237	// target has access to all headers recursively in the package, and has
238	// "-I<module-dir>" in its copts.
239	if c, ok := module.compiler.(*baseCompiler); ok && c.includeBuildDirectory() {
240		copts.Value = append(copts.Value, includeFlag("."))
241	} else if c, ok := module.compiler.(*libraryDecorator); ok && c.includeBuildDirectory() {
242		copts.Value = append(copts.Value, includeFlag("."))
243	}
244
245	for arch, props := range module.GetArchProperties(ctx, &BaseCompilerProperties{}) {
246		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
247			// If there's arch specific srcs or exclude_srcs, generate a select entry for it.
248			// TODO(b/186153868): do this for OS specific srcs and exclude_srcs too.
249			if len(baseCompilerProps.Srcs) > 0 || len(baseCompilerProps.Exclude_srcs) > 0 {
250				srcsList := parseSrcs(baseCompilerProps)
251				srcs.SetValueForArch(arch.Name, srcsList)
252				// The base srcs value should not contain any arch-specific excludes.
253				srcs.Value = bazel.SubtractBazelLabelList(srcs.Value, bazel.LabelList{Includes: srcsList.Excludes})
254			}
255
256			copts.SetValueForArch(arch.Name, parseCopts(baseCompilerProps))
257		}
258	}
259
260	// After going through all archs, delete the duplicate files in the arch
261	// values that are already in the base srcs.Value.
262	for arch, props := range module.GetArchProperties(ctx, &BaseCompilerProperties{}) {
263		if _, ok := props.(*BaseCompilerProperties); ok {
264			srcs.SetValueForArch(arch.Name, bazel.SubtractBazelLabelList(srcs.GetValueForArch(arch.Name), srcs.Value))
265		}
266	}
267
268	// Now that the srcs.Value list is finalized, compare it with the original
269	// list, and put the difference into the default condition for the arch
270	// select.
271	defaultsSrcs := bazel.SubtractBazelLabelList(baseSrcsLabelList, srcs.Value)
272	// TODO(b/186153868): handle the case with multiple variant types, e.g. when arch and os are both used.
273	srcs.SetValueForArch(bazel.CONDITIONS_DEFAULT, defaultsSrcs)
274
275	// Handle OS specific props.
276	for os, props := range module.GetTargetProperties(&BaseCompilerProperties{}) {
277		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
278			srcsList := parseSrcs(baseCompilerProps)
279			// TODO(b/186153868): add support for os-specific srcs and exclude_srcs
280			srcs.SetValueForOS(os.Name, bazel.SubtractBazelLabelList(srcsList, baseSrcsLabelList))
281			copts.SetValueForOS(os.Name, parseCopts(baseCompilerProps))
282		}
283	}
284
285	return compilerAttributes{
286		srcs:  srcs,
287		copts: copts,
288	}
289}
290
291// Convenience struct to hold all attributes parsed from linker properties.
292type linkerAttributes struct {
293	deps             bazel.LabelListAttribute
294	dynamicDeps      bazel.LabelListAttribute
295	wholeArchiveDeps bazel.LabelListAttribute
296	linkopts         bazel.StringListAttribute
297	versionScript    bazel.LabelAttribute
298}
299
300// FIXME(b/187655838): Use the existing linkerFlags() function instead of duplicating logic here
301func getBp2BuildLinkerFlags(linkerProperties *BaseLinkerProperties) []string {
302	flags := linkerProperties.Ldflags
303	if !BoolDefault(linkerProperties.Pack_relocations, true) {
304		flags = append(flags, "-Wl,--pack-dyn-relocs=none")
305	}
306	return flags
307}
308
309// bp2BuildParseLinkerProps parses the linker properties of a module, including
310// configurable attribute values.
311func bp2BuildParseLinkerProps(ctx android.TopDownMutatorContext, module *Module) linkerAttributes {
312	var deps bazel.LabelListAttribute
313	var dynamicDeps bazel.LabelListAttribute
314	var wholeArchiveDeps bazel.LabelListAttribute
315	var linkopts bazel.StringListAttribute
316	var versionScript bazel.LabelAttribute
317
318	for _, linkerProps := range module.linker.linkerProps() {
319		if baseLinkerProps, ok := linkerProps.(*BaseLinkerProperties); ok {
320			libs := baseLinkerProps.Header_libs
321			libs = append(libs, baseLinkerProps.Export_header_lib_headers...)
322			libs = append(libs, baseLinkerProps.Static_libs...)
323			wholeArchiveLibs := baseLinkerProps.Whole_static_libs
324			libs = android.SortedUniqueStrings(libs)
325			deps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, libs))
326			linkopts.Value = getBp2BuildLinkerFlags(baseLinkerProps)
327			wholeArchiveDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, wholeArchiveLibs))
328
329			if baseLinkerProps.Version_script != nil {
330				versionScript.Value = android.BazelLabelForModuleSrcSingle(ctx, *baseLinkerProps.Version_script)
331			}
332
333			sharedLibs := baseLinkerProps.Shared_libs
334			dynamicDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, sharedLibs))
335
336			break
337		}
338	}
339
340	for arch, p := range module.GetArchProperties(ctx, &BaseLinkerProperties{}) {
341		if baseLinkerProps, ok := p.(*BaseLinkerProperties); ok {
342			libs := baseLinkerProps.Header_libs
343			libs = append(libs, baseLinkerProps.Export_header_lib_headers...)
344			libs = append(libs, baseLinkerProps.Static_libs...)
345			wholeArchiveLibs := baseLinkerProps.Whole_static_libs
346			libs = android.SortedUniqueStrings(libs)
347			deps.SetValueForArch(arch.Name, android.BazelLabelForModuleDeps(ctx, libs))
348			linkopts.SetValueForArch(arch.Name, getBp2BuildLinkerFlags(baseLinkerProps))
349			wholeArchiveDeps.SetValueForArch(arch.Name, android.BazelLabelForModuleDeps(ctx, wholeArchiveLibs))
350
351			if baseLinkerProps.Version_script != nil {
352				versionScript.SetValueForArch(arch.Name,
353					android.BazelLabelForModuleSrcSingle(ctx, *baseLinkerProps.Version_script))
354			}
355
356			sharedLibs := baseLinkerProps.Shared_libs
357			dynamicDeps.SetValueForArch(arch.Name, android.BazelLabelForModuleDeps(ctx, sharedLibs))
358		}
359	}
360
361	for os, p := range module.GetTargetProperties(&BaseLinkerProperties{}) {
362		if baseLinkerProps, ok := p.(*BaseLinkerProperties); ok {
363			libs := baseLinkerProps.Header_libs
364			libs = append(libs, baseLinkerProps.Export_header_lib_headers...)
365			libs = append(libs, baseLinkerProps.Static_libs...)
366			wholeArchiveLibs := baseLinkerProps.Whole_static_libs
367			libs = android.SortedUniqueStrings(libs)
368			wholeArchiveDeps.SetValueForOS(os.Name, android.BazelLabelForModuleDeps(ctx, wholeArchiveLibs))
369			deps.SetValueForOS(os.Name, android.BazelLabelForModuleDeps(ctx, libs))
370
371			linkopts.SetValueForOS(os.Name, getBp2BuildLinkerFlags(baseLinkerProps))
372
373			sharedLibs := baseLinkerProps.Shared_libs
374			dynamicDeps.SetValueForOS(os.Name, android.BazelLabelForModuleDeps(ctx, sharedLibs))
375		}
376	}
377
378	return linkerAttributes{
379		deps:             deps,
380		dynamicDeps:      dynamicDeps,
381		wholeArchiveDeps: wholeArchiveDeps,
382		linkopts:         linkopts,
383		versionScript:    versionScript,
384	}
385}
386
387// Relativize a list of root-relative paths with respect to the module's
388// directory.
389//
390// include_dirs Soong prop are root-relative (b/183742505), but
391// local_include_dirs, export_include_dirs and export_system_include_dirs are
392// module dir relative. This function makes a list of paths entirely module dir
393// relative.
394//
395// For the `include` attribute, Bazel wants the paths to be relative to the
396// module.
397func bp2BuildMakePathsRelativeToModule(ctx android.BazelConversionPathContext, paths []string) []string {
398	var relativePaths []string
399	for _, path := range paths {
400		// Semantics of filepath.Rel: join(ModuleDir, rel(ModuleDir, path)) == path
401		relativePath, err := filepath.Rel(ctx.ModuleDir(), path)
402		if err != nil {
403			panic(err)
404		}
405		relativePaths = append(relativePaths, relativePath)
406	}
407	return relativePaths
408}
409
410// bp2BuildParseExportedIncludes creates a string list attribute contains the
411// exported included directories of a module.
412func bp2BuildParseExportedIncludes(ctx android.TopDownMutatorContext, module *Module) bazel.StringListAttribute {
413	libraryDecorator := module.linker.(*libraryDecorator)
414
415	// Export_system_include_dirs and export_include_dirs are already module dir
416	// relative, so they don't need to be relativized like include_dirs, which
417	// are root-relative.
418	includeDirs := libraryDecorator.flagExporter.Properties.Export_system_include_dirs
419	includeDirs = append(includeDirs, libraryDecorator.flagExporter.Properties.Export_include_dirs...)
420	includeDirsAttribute := bazel.MakeStringListAttribute(includeDirs)
421
422	for arch, props := range module.GetArchProperties(ctx, &FlagExporterProperties{}) {
423		if flagExporterProperties, ok := props.(*FlagExporterProperties); ok {
424			archIncludeDirs := flagExporterProperties.Export_system_include_dirs
425			archIncludeDirs = append(archIncludeDirs, flagExporterProperties.Export_include_dirs...)
426
427			// To avoid duplicate includes when base includes + arch includes are combined
428			// FIXME: This doesn't take conflicts between arch and os includes into account
429			archIncludeDirs = bazel.SubtractStrings(archIncludeDirs, includeDirs)
430
431			if len(archIncludeDirs) > 0 {
432				includeDirsAttribute.SetValueForArch(arch.Name, archIncludeDirs)
433			}
434		}
435	}
436
437	for os, props := range module.GetTargetProperties(&FlagExporterProperties{}) {
438		if flagExporterProperties, ok := props.(*FlagExporterProperties); ok {
439			osIncludeDirs := flagExporterProperties.Export_system_include_dirs
440			osIncludeDirs = append(osIncludeDirs, flagExporterProperties.Export_include_dirs...)
441
442			// To avoid duplicate includes when base includes + os includes are combined
443			// FIXME: This doesn't take conflicts between arch and os includes into account
444			osIncludeDirs = bazel.SubtractStrings(osIncludeDirs, includeDirs)
445
446			if len(osIncludeDirs) > 0 {
447				includeDirsAttribute.SetValueForOS(os.Name, osIncludeDirs)
448			}
449		}
450	}
451
452	return includeDirsAttribute
453}
454