• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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	"fmt"
18	"path/filepath"
19	"strings"
20
21	"android/soong/android"
22	"android/soong/bazel"
23
24	"github.com/google/blueprint"
25
26	"github.com/google/blueprint/proptools"
27)
28
29const (
30	cSrcPartition     = "c"
31	asSrcPartition    = "as"
32	cppSrcPartition   = "cpp"
33	protoSrcPartition = "proto"
34)
35
36// staticOrSharedAttributes are the Bazel-ified versions of StaticOrSharedProperties --
37// properties which apply to either the shared or static version of a cc_library module.
38type staticOrSharedAttributes struct {
39	Srcs    bazel.LabelListAttribute
40	Srcs_c  bazel.LabelListAttribute
41	Srcs_as bazel.LabelListAttribute
42	Hdrs    bazel.LabelListAttribute
43	Copts   bazel.StringListAttribute
44
45	Deps                              bazel.LabelListAttribute
46	Implementation_deps               bazel.LabelListAttribute
47	Dynamic_deps                      bazel.LabelListAttribute
48	Implementation_dynamic_deps       bazel.LabelListAttribute
49	Whole_archive_deps                bazel.LabelListAttribute
50	Implementation_whole_archive_deps bazel.LabelListAttribute
51
52	System_dynamic_deps bazel.LabelListAttribute
53
54	Enabled bazel.BoolAttribute
55
56	sdkAttributes
57}
58
59// groupSrcsByExtension partitions `srcs` into groups based on file extension.
60func groupSrcsByExtension(ctx android.BazelConversionPathContext, srcs bazel.LabelListAttribute) bazel.PartitionToLabelListAttribute {
61	// Convert filegroup dependencies into extension-specific filegroups filtered in the filegroup.bzl
62	// macro.
63	addSuffixForFilegroup := func(suffix string) bazel.LabelMapper {
64		return func(ctx bazel.OtherModuleContext, label bazel.Label) (string, bool) {
65			m, exists := ctx.ModuleFromName(label.OriginalModuleName)
66			labelStr := label.Label
67			if !exists || !android.IsFilegroup(ctx, m) {
68				return labelStr, false
69			}
70			return labelStr + suffix, true
71		}
72	}
73
74	// TODO(b/190006308): Handle language detection of sources in a Bazel rule.
75	labels := bazel.LabelPartitions{
76		protoSrcPartition: android.ProtoSrcLabelPartition,
77		cSrcPartition:     bazel.LabelPartition{Extensions: []string{".c"}, LabelMapper: addSuffixForFilegroup("_c_srcs")},
78		asSrcPartition:    bazel.LabelPartition{Extensions: []string{".s", ".S"}, LabelMapper: addSuffixForFilegroup("_as_srcs")},
79		// C++ is the "catch-all" group, and comprises generated sources because we don't
80		// know the language of these sources until the genrule is executed.
81		cppSrcPartition: bazel.LabelPartition{Extensions: []string{".cpp", ".cc", ".cxx", ".mm"}, LabelMapper: addSuffixForFilegroup("_cpp_srcs"), Keep_remainder: true},
82	}
83
84	return bazel.PartitionLabelListAttribute(ctx, &srcs, labels)
85}
86
87// bp2BuildParseLibProps returns the attributes for a variant of a cc_library.
88func bp2BuildParseLibProps(ctx android.BazelConversionPathContext, module *Module, isStatic bool) staticOrSharedAttributes {
89	lib, ok := module.compiler.(*libraryDecorator)
90	if !ok {
91		return staticOrSharedAttributes{}
92	}
93	return bp2buildParseStaticOrSharedProps(ctx, module, lib, isStatic)
94}
95
96// bp2buildParseSharedProps returns the attributes for the shared variant of a cc_library.
97func bp2BuildParseSharedProps(ctx android.BazelConversionPathContext, module *Module) staticOrSharedAttributes {
98	return bp2BuildParseLibProps(ctx, module, false)
99}
100
101// bp2buildParseStaticProps returns the attributes for the static variant of a cc_library.
102func bp2BuildParseStaticProps(ctx android.BazelConversionPathContext, module *Module) staticOrSharedAttributes {
103	return bp2BuildParseLibProps(ctx, module, true)
104}
105
106type depsPartition struct {
107	export         bazel.LabelList
108	implementation bazel.LabelList
109}
110
111type bazelLabelForDepsFn func(android.BazelConversionPathContext, []string) bazel.LabelList
112
113func maybePartitionExportedAndImplementationsDeps(ctx android.BazelConversionPathContext, exportsDeps bool, allDeps, exportedDeps []string, fn bazelLabelForDepsFn) depsPartition {
114	if !exportsDeps {
115		return depsPartition{
116			implementation: fn(ctx, allDeps),
117		}
118	}
119
120	implementation, export := android.FilterList(allDeps, exportedDeps)
121
122	return depsPartition{
123		export:         fn(ctx, export),
124		implementation: fn(ctx, implementation),
125	}
126}
127
128type bazelLabelForDepsExcludesFn func(android.BazelConversionPathContext, []string, []string) bazel.LabelList
129
130func maybePartitionExportedAndImplementationsDepsExcludes(ctx android.BazelConversionPathContext, exportsDeps bool, allDeps, excludes, exportedDeps []string, fn bazelLabelForDepsExcludesFn) depsPartition {
131	if !exportsDeps {
132		return depsPartition{
133			implementation: fn(ctx, allDeps, excludes),
134		}
135	}
136	implementation, export := android.FilterList(allDeps, exportedDeps)
137
138	return depsPartition{
139		export:         fn(ctx, export, excludes),
140		implementation: fn(ctx, implementation, excludes),
141	}
142}
143
144// Parses properties common to static and shared libraries. Also used for prebuilt libraries.
145func bp2buildParseStaticOrSharedProps(ctx android.BazelConversionPathContext, module *Module, lib *libraryDecorator, isStatic bool) staticOrSharedAttributes {
146	attrs := staticOrSharedAttributes{}
147
148	setAttrs := func(axis bazel.ConfigurationAxis, config string, props StaticOrSharedProperties) {
149		attrs.Copts.SetSelectValue(axis, config, parseCommandLineFlags(props.Cflags, filterOutStdFlag))
150		attrs.Srcs.SetSelectValue(axis, config, android.BazelLabelForModuleSrc(ctx, props.Srcs))
151		attrs.System_dynamic_deps.SetSelectValue(axis, config, bazelLabelForSharedDeps(ctx, props.System_shared_libs))
152
153		staticDeps := maybePartitionExportedAndImplementationsDeps(ctx, true, props.Static_libs, props.Export_static_lib_headers, bazelLabelForStaticDeps)
154		attrs.Deps.SetSelectValue(axis, config, staticDeps.export)
155		attrs.Implementation_deps.SetSelectValue(axis, config, staticDeps.implementation)
156
157		sharedDeps := maybePartitionExportedAndImplementationsDeps(ctx, true, props.Shared_libs, props.Export_shared_lib_headers, bazelLabelForSharedDeps)
158		attrs.Dynamic_deps.SetSelectValue(axis, config, sharedDeps.export)
159		attrs.Implementation_dynamic_deps.SetSelectValue(axis, config, sharedDeps.implementation)
160
161		attrs.Whole_archive_deps.SetSelectValue(axis, config, bazelLabelForWholeDeps(ctx, props.Whole_static_libs))
162		attrs.Enabled.SetSelectValue(axis, config, props.Enabled)
163	}
164	// system_dynamic_deps distinguishes between nil/empty list behavior:
165	//    nil -> use default values
166	//    empty list -> no values specified
167	attrs.System_dynamic_deps.ForceSpecifyEmptyList = true
168
169	if isStatic {
170		for axis, configToProps := range module.GetArchVariantProperties(ctx, &StaticProperties{}) {
171			for config, props := range configToProps {
172				if staticOrSharedProps, ok := props.(*StaticProperties); ok {
173					setAttrs(axis, config, staticOrSharedProps.Static)
174				}
175			}
176		}
177	} else {
178		for axis, configToProps := range module.GetArchVariantProperties(ctx, &SharedProperties{}) {
179			for config, props := range configToProps {
180				if staticOrSharedProps, ok := props.(*SharedProperties); ok {
181					setAttrs(axis, config, staticOrSharedProps.Shared)
182				}
183			}
184		}
185	}
186
187	partitionedSrcs := groupSrcsByExtension(ctx, attrs.Srcs)
188	attrs.Srcs = partitionedSrcs[cppSrcPartition]
189	attrs.Srcs_c = partitionedSrcs[cSrcPartition]
190	attrs.Srcs_as = partitionedSrcs[asSrcPartition]
191
192	if !partitionedSrcs[protoSrcPartition].IsEmpty() {
193		// TODO(b/208815215): determine whether this is used and add support if necessary
194		ctx.ModuleErrorf("Migrating static/shared only proto srcs is not currently supported")
195	}
196
197	return attrs
198}
199
200// Convenience struct to hold all attributes parsed from prebuilt properties.
201type prebuiltAttributes struct {
202	Src     bazel.LabelAttribute
203	Enabled bazel.BoolAttribute
204}
205
206// NOTE: Used outside of Soong repo project, in the clangprebuilts.go bootstrap_go_package
207func Bp2BuildParsePrebuiltLibraryProps(ctx android.BazelConversionPathContext, module *Module, isStatic bool) prebuiltAttributes {
208	manySourceFileError := func(axis bazel.ConfigurationAxis, config string) {
209		ctx.ModuleErrorf("Bp2BuildParsePrebuiltLibraryProps: Expected at most one source file for %s %s\n", axis, config)
210	}
211	var srcLabelAttribute bazel.LabelAttribute
212
213	parseSrcs := func(ctx android.BazelConversionPathContext, axis bazel.ConfigurationAxis, config string, srcs []string) {
214		if len(srcs) > 1 {
215			manySourceFileError(axis, config)
216			return
217		} else if len(srcs) == 0 {
218			return
219		}
220		if srcLabelAttribute.SelectValue(axis, config) != nil {
221			manySourceFileError(axis, config)
222			return
223		}
224
225		src := android.BazelLabelForModuleSrcSingle(ctx, srcs[0])
226		srcLabelAttribute.SetSelectValue(axis, config, src)
227	}
228
229	bp2BuildPropParseHelper(ctx, module, &prebuiltLinkerProperties{}, func(axis bazel.ConfigurationAxis, config string, props interface{}) {
230		if prebuiltLinkerProperties, ok := props.(*prebuiltLinkerProperties); ok {
231			parseSrcs(ctx, axis, config, prebuiltLinkerProperties.Srcs)
232		}
233	})
234
235	var enabledLabelAttribute bazel.BoolAttribute
236	parseAttrs := func(axis bazel.ConfigurationAxis, config string, props StaticOrSharedProperties) {
237		if props.Enabled != nil {
238			enabledLabelAttribute.SetSelectValue(axis, config, props.Enabled)
239		}
240		parseSrcs(ctx, axis, config, props.Srcs)
241	}
242
243	if isStatic {
244		bp2BuildPropParseHelper(ctx, module, &StaticProperties{}, func(axis bazel.ConfigurationAxis, config string, props interface{}) {
245			if staticProperties, ok := props.(*StaticProperties); ok {
246				parseAttrs(axis, config, staticProperties.Static)
247			}
248		})
249	} else {
250		bp2BuildPropParseHelper(ctx, module, &SharedProperties{}, func(axis bazel.ConfigurationAxis, config string, props interface{}) {
251			if sharedProperties, ok := props.(*SharedProperties); ok {
252				parseAttrs(axis, config, sharedProperties.Shared)
253			}
254		})
255	}
256
257	return prebuiltAttributes{
258		Src:     srcLabelAttribute,
259		Enabled: enabledLabelAttribute,
260	}
261}
262
263func bp2BuildPropParseHelper(ctx android.ArchVariantContext, module *Module, propsType interface{}, parseFunc func(axis bazel.ConfigurationAxis, config string, props interface{})) {
264	for axis, configToProps := range module.GetArchVariantProperties(ctx, propsType) {
265		for config, props := range configToProps {
266			parseFunc(axis, config, props)
267		}
268	}
269}
270
271type baseAttributes struct {
272	compilerAttributes
273	linkerAttributes
274
275	protoDependency *bazel.LabelAttribute
276}
277
278// Convenience struct to hold all attributes parsed from compiler properties.
279type compilerAttributes struct {
280	// Options for all languages
281	copts bazel.StringListAttribute
282	// Assembly options and sources
283	asFlags bazel.StringListAttribute
284	asSrcs  bazel.LabelListAttribute
285	// C options and sources
286	conlyFlags bazel.StringListAttribute
287	cSrcs      bazel.LabelListAttribute
288	// C++ options and sources
289	cppFlags bazel.StringListAttribute
290	srcs     bazel.LabelListAttribute
291
292	hdrs bazel.LabelListAttribute
293
294	rtti bazel.BoolAttribute
295
296	// Not affected by arch variants
297	stl    *string
298	cStd   *string
299	cppStd *string
300
301	localIncludes    bazel.StringListAttribute
302	absoluteIncludes bazel.StringListAttribute
303
304	includes BazelIncludes
305
306	protoSrcs bazel.LabelListAttribute
307
308	stubsSymbolFile *string
309	stubsVersions   bazel.StringListAttribute
310}
311
312type filterOutFn func(string) bool
313
314func filterOutStdFlag(flag string) bool {
315	return strings.HasPrefix(flag, "-std=")
316}
317
318func parseCommandLineFlags(soongFlags []string, filterOut filterOutFn) []string {
319	var result []string
320	for _, flag := range soongFlags {
321		if filterOut != nil && filterOut(flag) {
322			continue
323		}
324		// Soong's cflags can contain spaces, like `-include header.h`. For
325		// Bazel's copts, split them up to be compatible with the
326		// no_copts_tokenization feature.
327		result = append(result, strings.Split(flag, " ")...)
328	}
329	return result
330}
331
332func (ca *compilerAttributes) bp2buildForAxisAndConfig(ctx android.BazelConversionPathContext, axis bazel.ConfigurationAxis, config string, props *BaseCompilerProperties) {
333	// If there's arch specific srcs or exclude_srcs, generate a select entry for it.
334	// TODO(b/186153868): do this for OS specific srcs and exclude_srcs too.
335	if srcsList, ok := parseSrcs(ctx, props); ok {
336		ca.srcs.SetSelectValue(axis, config, srcsList)
337	}
338
339	localIncludeDirs := props.Local_include_dirs
340	if axis == bazel.NoConfigAxis {
341		ca.cStd, ca.cppStd = bp2buildResolveCppStdValue(props.C_std, props.Cpp_std, props.Gnu_extensions)
342		if includeBuildDirectory(props.Include_build_directory) {
343			localIncludeDirs = append(localIncludeDirs, ".")
344		}
345	}
346
347	ca.absoluteIncludes.SetSelectValue(axis, config, props.Include_dirs)
348	ca.localIncludes.SetSelectValue(axis, config, localIncludeDirs)
349
350	// In Soong, cflags occur on the command line before -std=<val> flag, resulting in the value being
351	// overridden. In Bazel we always allow overriding, via flags; however, this can cause
352	// incompatibilities, so we remove "-std=" flags from Cflag properties while leaving it in other
353	// cases.
354	ca.copts.SetSelectValue(axis, config, parseCommandLineFlags(props.Cflags, filterOutStdFlag))
355	ca.asFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Asflags, nil))
356	ca.conlyFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Conlyflags, nil))
357	ca.cppFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Cppflags, nil))
358	ca.rtti.SetSelectValue(axis, config, props.Rtti)
359}
360
361func (ca *compilerAttributes) convertStlProps(ctx android.ArchVariantContext, module *Module) {
362	stlPropsByArch := module.GetArchVariantProperties(ctx, &StlProperties{})
363	for _, configToProps := range stlPropsByArch {
364		for _, props := range configToProps {
365			if stlProps, ok := props.(*StlProperties); ok {
366				if stlProps.Stl == nil {
367					continue
368				}
369				if ca.stl == nil {
370					ca.stl = stlProps.Stl
371				} else if ca.stl != stlProps.Stl {
372					ctx.ModuleErrorf("Unsupported conversion: module with different stl for different variants: %s and %s", *ca.stl, stlProps.Stl)
373				}
374			}
375		}
376	}
377}
378
379func (ca *compilerAttributes) convertProductVariables(ctx android.BazelConversionPathContext, productVariableProps android.ProductConfigProperties) {
380	productVarPropNameToAttribute := map[string]*bazel.StringListAttribute{
381		"Cflags":   &ca.copts,
382		"Asflags":  &ca.asFlags,
383		"CppFlags": &ca.cppFlags,
384	}
385	for propName, attr := range productVarPropNameToAttribute {
386		if productConfigProps, exists := productVariableProps[propName]; exists {
387			for productConfigProp, prop := range productConfigProps {
388				flags, ok := prop.([]string)
389				if !ok {
390					ctx.ModuleErrorf("Could not convert product variable %s property", proptools.PropertyNameForField(propName))
391				}
392				newFlags, _ := bazel.TryVariableSubstitutions(flags, productConfigProp.Name)
393				attr.SetSelectValue(productConfigProp.ConfigurationAxis(), productConfigProp.SelectKey(), newFlags)
394			}
395		}
396	}
397}
398
399func (ca *compilerAttributes) finalize(ctx android.BazelConversionPathContext, implementationHdrs bazel.LabelListAttribute) {
400	ca.srcs.ResolveExcludes()
401	partitionedSrcs := groupSrcsByExtension(ctx, ca.srcs)
402
403	ca.protoSrcs = partitionedSrcs[protoSrcPartition]
404
405	for p, lla := range partitionedSrcs {
406		// if there are no sources, there is no need for headers
407		if lla.IsEmpty() {
408			continue
409		}
410		lla.Append(implementationHdrs)
411		partitionedSrcs[p] = lla
412	}
413
414	ca.srcs = partitionedSrcs[cppSrcPartition]
415	ca.cSrcs = partitionedSrcs[cSrcPartition]
416	ca.asSrcs = partitionedSrcs[asSrcPartition]
417
418	ca.absoluteIncludes.DeduplicateAxesFromBase()
419	ca.localIncludes.DeduplicateAxesFromBase()
420}
421
422// Parse srcs from an arch or OS's props value.
423func parseSrcs(ctx android.BazelConversionPathContext, props *BaseCompilerProperties) (bazel.LabelList, bool) {
424	anySrcs := false
425	// Add srcs-like dependencies such as generated files.
426	// First create a LabelList containing these dependencies, then merge the values with srcs.
427	generatedSrcsLabelList := android.BazelLabelForModuleDepsExcludes(ctx, props.Generated_sources, props.Exclude_generated_sources)
428	if len(props.Generated_sources) > 0 || len(props.Exclude_generated_sources) > 0 {
429		anySrcs = true
430	}
431
432	allSrcsLabelList := android.BazelLabelForModuleSrcExcludes(ctx, props.Srcs, props.Exclude_srcs)
433	if len(props.Srcs) > 0 || len(props.Exclude_srcs) > 0 {
434		anySrcs = true
435	}
436	return bazel.AppendBazelLabelLists(allSrcsLabelList, generatedSrcsLabelList), anySrcs
437}
438
439func bp2buildResolveCppStdValue(c_std *string, cpp_std *string, gnu_extensions *bool) (*string, *string) {
440	var cStdVal, cppStdVal string
441	// If c{,pp}std properties are not specified, don't generate them in the BUILD file.
442	// Defaults are handled by the toolchain definition.
443	// However, if gnu_extensions is false, then the default gnu-to-c version must be specified.
444	if cpp_std != nil {
445		cppStdVal = parseCppStd(cpp_std)
446	} else if gnu_extensions != nil && !*gnu_extensions {
447		cppStdVal = "c++17"
448	}
449	if c_std != nil {
450		cStdVal = parseCStd(c_std)
451	} else if gnu_extensions != nil && !*gnu_extensions {
452		cStdVal = "c99"
453	}
454
455	cStdVal, cppStdVal = maybeReplaceGnuToC(gnu_extensions, cStdVal, cppStdVal)
456	var c_std_prop, cpp_std_prop *string
457	if cStdVal != "" {
458		c_std_prop = &cStdVal
459	}
460	if cppStdVal != "" {
461		cpp_std_prop = &cppStdVal
462	}
463
464	return c_std_prop, cpp_std_prop
465}
466
467// packageFromLabel extracts package from a fully-qualified or relative Label and whether the label
468// is fully-qualified.
469// e.g. fully-qualified "//a/b:foo" -> "a/b", true, relative: ":bar" -> ".", false
470func packageFromLabel(label string) (string, bool) {
471	split := strings.Split(label, ":")
472	if len(split) != 2 {
473		return "", false
474	}
475	if split[0] == "" {
476		return ".", false
477	}
478	// remove leading "//"
479	return split[0][2:], true
480}
481
482// includesFromLabelList extracts relative/absolute includes from a bazel.LabelList>
483func includesFromLabelList(labelList bazel.LabelList) (relative, absolute []string) {
484	for _, hdr := range labelList.Includes {
485		if pkg, hasPkg := packageFromLabel(hdr.Label); hasPkg {
486			absolute = append(absolute, pkg)
487		} else if pkg != "" {
488			relative = append(relative, pkg)
489		}
490	}
491	return relative, absolute
492}
493
494// bp2BuildParseBaseProps returns all compiler, linker, library attributes of a cc module..
495func bp2BuildParseBaseProps(ctx android.Bp2buildMutatorContext, module *Module) baseAttributes {
496	archVariantCompilerProps := module.GetArchVariantProperties(ctx, &BaseCompilerProperties{})
497	archVariantLinkerProps := module.GetArchVariantProperties(ctx, &BaseLinkerProperties{})
498	archVariantLibraryProperties := module.GetArchVariantProperties(ctx, &LibraryProperties{})
499
500	var implementationHdrs bazel.LabelListAttribute
501
502	axisToConfigs := map[bazel.ConfigurationAxis]map[string]bool{}
503	allAxesAndConfigs := func(cp android.ConfigurationAxisToArchVariantProperties) {
504		for axis, configMap := range cp {
505			if _, ok := axisToConfigs[axis]; !ok {
506				axisToConfigs[axis] = map[string]bool{}
507			}
508			for config, _ := range configMap {
509				axisToConfigs[axis][config] = true
510			}
511		}
512	}
513	allAxesAndConfigs(archVariantCompilerProps)
514	allAxesAndConfigs(archVariantLinkerProps)
515	allAxesAndConfigs(archVariantLibraryProperties)
516
517	compilerAttrs := compilerAttributes{}
518	linkerAttrs := linkerAttributes{}
519
520	for axis, configs := range axisToConfigs {
521		for config, _ := range configs {
522			var allHdrs []string
523			if baseCompilerProps, ok := archVariantCompilerProps[axis][config].(*BaseCompilerProperties); ok {
524				allHdrs = baseCompilerProps.Generated_headers
525
526				(&compilerAttrs).bp2buildForAxisAndConfig(ctx, axis, config, baseCompilerProps)
527			}
528
529			var exportHdrs []string
530
531			if baseLinkerProps, ok := archVariantLinkerProps[axis][config].(*BaseLinkerProperties); ok {
532				exportHdrs = baseLinkerProps.Export_generated_headers
533
534				(&linkerAttrs).bp2buildForAxisAndConfig(ctx, module.Binary(), axis, config, baseLinkerProps)
535			}
536			headers := maybePartitionExportedAndImplementationsDeps(ctx, !module.Binary(), allHdrs, exportHdrs, android.BazelLabelForModuleDeps)
537			implementationHdrs.SetSelectValue(axis, config, headers.implementation)
538			compilerAttrs.hdrs.SetSelectValue(axis, config, headers.export)
539
540			exportIncludes, exportAbsoluteIncludes := includesFromLabelList(headers.export)
541			compilerAttrs.includes.Includes.SetSelectValue(axis, config, exportIncludes)
542			compilerAttrs.includes.AbsoluteIncludes.SetSelectValue(axis, config, exportAbsoluteIncludes)
543
544			includes, absoluteIncludes := includesFromLabelList(headers.implementation)
545			currAbsoluteIncludes := compilerAttrs.absoluteIncludes.SelectValue(axis, config)
546			currAbsoluteIncludes = android.FirstUniqueStrings(append(currAbsoluteIncludes, absoluteIncludes...))
547			compilerAttrs.absoluteIncludes.SetSelectValue(axis, config, currAbsoluteIncludes)
548			currIncludes := compilerAttrs.localIncludes.SelectValue(axis, config)
549			currIncludes = android.FirstUniqueStrings(append(currIncludes, includes...))
550			compilerAttrs.localIncludes.SetSelectValue(axis, config, currIncludes)
551
552			if libraryProps, ok := archVariantLibraryProperties[axis][config].(*LibraryProperties); ok {
553				if axis == bazel.NoConfigAxis {
554					compilerAttrs.stubsSymbolFile = libraryProps.Stubs.Symbol_file
555					compilerAttrs.stubsVersions.SetSelectValue(axis, config, libraryProps.Stubs.Versions)
556				}
557			}
558		}
559	}
560
561	compilerAttrs.convertStlProps(ctx, module)
562	(&linkerAttrs).convertStripProps(ctx, module)
563
564	productVariableProps := android.ProductVariableProperties(ctx)
565
566	(&compilerAttrs).convertProductVariables(ctx, productVariableProps)
567	(&linkerAttrs).convertProductVariables(ctx, productVariableProps)
568
569	(&compilerAttrs).finalize(ctx, implementationHdrs)
570	(&linkerAttrs).finalize(ctx)
571
572	protoDep := bp2buildProto(ctx, module, compilerAttrs.protoSrcs)
573
574	// bp2buildProto will only set wholeStaticLib or implementationWholeStaticLib, but we don't know
575	// which. This will add the newly generated proto library to the appropriate attribute and nothing
576	// to the other
577	(&linkerAttrs).wholeArchiveDeps.Add(protoDep.wholeStaticLib)
578	(&linkerAttrs).implementationWholeArchiveDeps.Add(protoDep.implementationWholeStaticLib)
579
580	return baseAttributes{
581		compilerAttrs,
582		linkerAttrs,
583		protoDep.protoDep,
584	}
585}
586
587func bp2BuildParseSdkAttributes(module *Module) sdkAttributes {
588	return sdkAttributes{
589		Sdk_version:     module.Properties.Sdk_version,
590		Min_sdk_version: module.Properties.Min_sdk_version,
591	}
592}
593
594type sdkAttributes struct {
595	Sdk_version     *string
596	Min_sdk_version *string
597}
598
599// Convenience struct to hold all attributes parsed from linker properties.
600type linkerAttributes struct {
601	deps                             bazel.LabelListAttribute
602	implementationDeps               bazel.LabelListAttribute
603	dynamicDeps                      bazel.LabelListAttribute
604	implementationDynamicDeps        bazel.LabelListAttribute
605	wholeArchiveDeps                 bazel.LabelListAttribute
606	implementationWholeArchiveDeps   bazel.LabelListAttribute
607	systemDynamicDeps                bazel.LabelListAttribute
608	usedSystemDynamicDepAsDynamicDep map[string]bool
609
610	linkCrt                       bazel.BoolAttribute
611	useLibcrt                     bazel.BoolAttribute
612	useVersionLib                 bazel.BoolAttribute
613	linkopts                      bazel.StringListAttribute
614	additionalLinkerInputs        bazel.LabelListAttribute
615	stripKeepSymbols              bazel.BoolAttribute
616	stripKeepSymbolsAndDebugFrame bazel.BoolAttribute
617	stripKeepSymbolsList          bazel.StringListAttribute
618	stripAll                      bazel.BoolAttribute
619	stripNone                     bazel.BoolAttribute
620	features                      bazel.StringListAttribute
621}
622
623var (
624	soongSystemSharedLibs = []string{"libc", "libm", "libdl"}
625)
626
627func (la *linkerAttributes) bp2buildForAxisAndConfig(ctx android.BazelConversionPathContext, isBinary bool, axis bazel.ConfigurationAxis, config string, props *BaseLinkerProperties) {
628	// Use a single variable to capture usage of nocrt in arch variants, so there's only 1 error message for this module
629	var axisFeatures []string
630
631	wholeStaticLibs := android.FirstUniqueStrings(props.Whole_static_libs)
632	la.wholeArchiveDeps.SetSelectValue(axis, config, bazelLabelForWholeDepsExcludes(ctx, wholeStaticLibs, props.Exclude_static_libs))
633	// Excludes to parallel Soong:
634	// https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/linker.go;l=247-249;drc=088b53577dde6e40085ffd737a1ae96ad82fc4b0
635	staticLibs := android.FirstUniqueStrings(android.RemoveListFromList(props.Static_libs, wholeStaticLibs))
636
637	staticDeps := maybePartitionExportedAndImplementationsDepsExcludes(ctx, !isBinary, staticLibs, props.Exclude_static_libs, props.Export_static_lib_headers, bazelLabelForStaticDepsExcludes)
638
639	headerLibs := android.FirstUniqueStrings(props.Header_libs)
640	hDeps := maybePartitionExportedAndImplementationsDeps(ctx, !isBinary, headerLibs, props.Export_header_lib_headers, bazelLabelForHeaderDeps)
641
642	(&hDeps.export).Append(staticDeps.export)
643	la.deps.SetSelectValue(axis, config, hDeps.export)
644
645	(&hDeps.implementation).Append(staticDeps.implementation)
646	la.implementationDeps.SetSelectValue(axis, config, hDeps.implementation)
647
648	systemSharedLibs := props.System_shared_libs
649	// systemSharedLibs distinguishes between nil/empty list behavior:
650	//    nil -> use default values
651	//    empty list -> no values specified
652	if len(systemSharedLibs) > 0 {
653		systemSharedLibs = android.FirstUniqueStrings(systemSharedLibs)
654	}
655	la.systemDynamicDeps.SetSelectValue(axis, config, bazelLabelForSharedDeps(ctx, systemSharedLibs))
656
657	sharedLibs := android.FirstUniqueStrings(props.Shared_libs)
658	excludeSharedLibs := props.Exclude_shared_libs
659	usedSystem := android.FilterListPred(sharedLibs, func(s string) bool {
660		return android.InList(s, soongSystemSharedLibs) && !android.InList(s, excludeSharedLibs)
661	})
662	for _, el := range usedSystem {
663		if la.usedSystemDynamicDepAsDynamicDep == nil {
664			la.usedSystemDynamicDepAsDynamicDep = map[string]bool{}
665		}
666		la.usedSystemDynamicDepAsDynamicDep[el] = true
667	}
668
669	sharedDeps := maybePartitionExportedAndImplementationsDepsExcludes(ctx, !isBinary, sharedLibs, props.Exclude_shared_libs, props.Export_shared_lib_headers, bazelLabelForSharedDepsExcludes)
670	la.dynamicDeps.SetSelectValue(axis, config, sharedDeps.export)
671	la.implementationDynamicDeps.SetSelectValue(axis, config, sharedDeps.implementation)
672
673	if !BoolDefault(props.Pack_relocations, packRelocationsDefault) {
674		axisFeatures = append(axisFeatures, "disable_pack_relocations")
675	}
676
677	if Bool(props.Allow_undefined_symbols) {
678		axisFeatures = append(axisFeatures, "-no_undefined_symbols")
679	}
680
681	var linkerFlags []string
682	if len(props.Ldflags) > 0 {
683		linkerFlags = append(linkerFlags, proptools.NinjaEscapeList(props.Ldflags)...)
684		// binaries remove static flag if -shared is in the linker flags
685		if isBinary && android.InList("-shared", linkerFlags) {
686			axisFeatures = append(axisFeatures, "-static_flag")
687		}
688	}
689	if props.Version_script != nil {
690		label := android.BazelLabelForModuleSrcSingle(ctx, *props.Version_script)
691		la.additionalLinkerInputs.SetSelectValue(axis, config, bazel.LabelList{Includes: []bazel.Label{label}})
692		linkerFlags = append(linkerFlags, fmt.Sprintf("-Wl,--version-script,$(location %s)", label.Label))
693	}
694	la.linkopts.SetSelectValue(axis, config, linkerFlags)
695	la.useLibcrt.SetSelectValue(axis, config, props.libCrt())
696
697	if axis == bazel.NoConfigAxis {
698		la.useVersionLib.SetSelectValue(axis, config, props.Use_version_lib)
699	}
700
701	// it's very unlikely for nocrt to be arch variant, so bp2build doesn't support it.
702	if props.crt() != nil {
703		if axis == bazel.NoConfigAxis {
704			la.linkCrt.SetSelectValue(axis, config, props.crt())
705		} else if axis == bazel.ArchConfigurationAxis {
706			ctx.ModuleErrorf("nocrt is not supported for arch variants")
707		}
708	}
709
710	if axisFeatures != nil {
711		la.features.SetSelectValue(axis, config, axisFeatures)
712	}
713}
714
715func (la *linkerAttributes) convertStripProps(ctx android.BazelConversionPathContext, module *Module) {
716	for axis, configToProps := range module.GetArchVariantProperties(ctx, &StripProperties{}) {
717		for config, props := range configToProps {
718			if stripProperties, ok := props.(*StripProperties); ok {
719				la.stripKeepSymbols.SetSelectValue(axis, config, stripProperties.Strip.Keep_symbols)
720				la.stripKeepSymbolsList.SetSelectValue(axis, config, stripProperties.Strip.Keep_symbols_list)
721				la.stripKeepSymbolsAndDebugFrame.SetSelectValue(axis, config, stripProperties.Strip.Keep_symbols_and_debug_frame)
722				la.stripAll.SetSelectValue(axis, config, stripProperties.Strip.All)
723				la.stripNone.SetSelectValue(axis, config, stripProperties.Strip.None)
724			}
725		}
726	}
727}
728
729func (la *linkerAttributes) convertProductVariables(ctx android.BazelConversionPathContext, productVariableProps android.ProductConfigProperties) {
730
731	type productVarDep struct {
732		// the name of the corresponding excludes field, if one exists
733		excludesField string
734		// reference to the bazel attribute that should be set for the given product variable config
735		attribute *bazel.LabelListAttribute
736
737		depResolutionFunc func(ctx android.BazelConversionPathContext, modules, excludes []string) bazel.LabelList
738	}
739
740	productVarToDepFields := map[string]productVarDep{
741		// product variables do not support exclude_shared_libs
742		"Shared_libs":       {attribute: &la.implementationDynamicDeps, depResolutionFunc: bazelLabelForSharedDepsExcludes},
743		"Static_libs":       {"Exclude_static_libs", &la.implementationDeps, bazelLabelForStaticDepsExcludes},
744		"Whole_static_libs": {"Exclude_static_libs", &la.wholeArchiveDeps, bazelLabelForWholeDepsExcludes},
745	}
746
747	for name, dep := range productVarToDepFields {
748		props, exists := productVariableProps[name]
749		excludeProps, excludesExists := productVariableProps[dep.excludesField]
750		// if neither an include or excludes property exists, then skip it
751		if !exists && !excludesExists {
752			continue
753		}
754		// Collect all the configurations that an include or exclude property exists for.
755		// We want to iterate all configurations rather than either the include or exclude because, for a
756		// particular configuration, we may have either only an include or an exclude to handle.
757		productConfigProps := make(map[android.ProductConfigProperty]bool, len(props)+len(excludeProps))
758		for p := range props {
759			productConfigProps[p] = true
760		}
761		for p := range excludeProps {
762			productConfigProps[p] = true
763		}
764
765		for productConfigProp := range productConfigProps {
766			prop, includesExists := props[productConfigProp]
767			excludesProp, excludesExists := excludeProps[productConfigProp]
768			var includes, excludes []string
769			var ok bool
770			// if there was no includes/excludes property, casting fails and that's expected
771			if includes, ok = prop.([]string); includesExists && !ok {
772				ctx.ModuleErrorf("Could not convert product variable %s property", name)
773			}
774			if excludes, ok = excludesProp.([]string); excludesExists && !ok {
775				ctx.ModuleErrorf("Could not convert product variable %s property", dep.excludesField)
776			}
777
778			dep.attribute.EmitEmptyList = productConfigProp.AlwaysEmit()
779			dep.attribute.SetSelectValue(
780				productConfigProp.ConfigurationAxis(),
781				productConfigProp.SelectKey(),
782				dep.depResolutionFunc(ctx, android.FirstUniqueStrings(includes), excludes),
783			)
784		}
785	}
786}
787
788func (la *linkerAttributes) finalize(ctx android.BazelConversionPathContext) {
789	// if system dynamic deps have the default value, any use of a system dynamic library used will
790	// result in duplicate library errors for bionic OSes. Here, we explicitly exclude those libraries
791	// from bionic OSes.
792	if la.systemDynamicDeps.IsNil() && len(la.usedSystemDynamicDepAsDynamicDep) > 0 {
793		toRemove := bazelLabelForSharedDeps(ctx, android.SortedStringKeys(la.usedSystemDynamicDepAsDynamicDep))
794		la.dynamicDeps.Exclude(bazel.OsConfigurationAxis, "android", toRemove)
795		la.dynamicDeps.Exclude(bazel.OsConfigurationAxis, "linux_bionic", toRemove)
796		la.implementationDynamicDeps.Exclude(bazel.OsConfigurationAxis, "android", toRemove)
797		la.implementationDynamicDeps.Exclude(bazel.OsConfigurationAxis, "linux_bionic", toRemove)
798	}
799
800	la.deps.ResolveExcludes()
801	la.implementationDeps.ResolveExcludes()
802	la.dynamicDeps.ResolveExcludes()
803	la.implementationDynamicDeps.ResolveExcludes()
804	la.wholeArchiveDeps.ResolveExcludes()
805	la.systemDynamicDeps.ForceSpecifyEmptyList = true
806
807}
808
809// Relativize a list of root-relative paths with respect to the module's
810// directory.
811//
812// include_dirs Soong prop are root-relative (b/183742505), but
813// local_include_dirs, export_include_dirs and export_system_include_dirs are
814// module dir relative. This function makes a list of paths entirely module dir
815// relative.
816//
817// For the `include` attribute, Bazel wants the paths to be relative to the
818// module.
819func bp2BuildMakePathsRelativeToModule(ctx android.BazelConversionPathContext, paths []string) []string {
820	var relativePaths []string
821	for _, path := range paths {
822		// Semantics of filepath.Rel: join(ModuleDir, rel(ModuleDir, path)) == path
823		relativePath, err := filepath.Rel(ctx.ModuleDir(), path)
824		if err != nil {
825			panic(err)
826		}
827		relativePaths = append(relativePaths, relativePath)
828	}
829	return relativePaths
830}
831
832// BazelIncludes contains information about -I and -isystem paths from a module converted to Bazel
833// attributes.
834type BazelIncludes struct {
835	AbsoluteIncludes bazel.StringListAttribute
836	Includes         bazel.StringListAttribute
837	SystemIncludes   bazel.StringListAttribute
838}
839
840func bp2BuildParseExportedIncludes(ctx android.BazelConversionPathContext, module *Module, existingIncludes BazelIncludes) BazelIncludes {
841	libraryDecorator := module.linker.(*libraryDecorator)
842	return bp2BuildParseExportedIncludesHelper(ctx, module, libraryDecorator, &existingIncludes)
843}
844
845// Bp2buildParseExportedIncludesForPrebuiltLibrary returns a BazelIncludes with Bazel-ified values
846// to export includes from the underlying module's properties.
847func Bp2BuildParseExportedIncludesForPrebuiltLibrary(ctx android.BazelConversionPathContext, module *Module) BazelIncludes {
848	prebuiltLibraryLinker := module.linker.(*prebuiltLibraryLinker)
849	libraryDecorator := prebuiltLibraryLinker.libraryDecorator
850	return bp2BuildParseExportedIncludesHelper(ctx, module, libraryDecorator, nil)
851}
852
853// bp2BuildParseExportedIncludes creates a string list attribute contains the
854// exported included directories of a module.
855func bp2BuildParseExportedIncludesHelper(ctx android.BazelConversionPathContext, module *Module, libraryDecorator *libraryDecorator, includes *BazelIncludes) BazelIncludes {
856	var exported BazelIncludes
857	if includes != nil {
858		exported = *includes
859	} else {
860		exported = BazelIncludes{}
861	}
862	for axis, configToProps := range module.GetArchVariantProperties(ctx, &FlagExporterProperties{}) {
863		for config, props := range configToProps {
864			if flagExporterProperties, ok := props.(*FlagExporterProperties); ok {
865				if len(flagExporterProperties.Export_include_dirs) > 0 {
866					exported.Includes.SetSelectValue(axis, config, android.FirstUniqueStrings(append(exported.Includes.SelectValue(axis, config), flagExporterProperties.Export_include_dirs...)))
867				}
868				if len(flagExporterProperties.Export_system_include_dirs) > 0 {
869					exported.SystemIncludes.SetSelectValue(axis, config, android.FirstUniqueStrings(append(exported.SystemIncludes.SelectValue(axis, config), flagExporterProperties.Export_system_include_dirs...)))
870				}
871			}
872		}
873	}
874	exported.AbsoluteIncludes.DeduplicateAxesFromBase()
875	exported.Includes.DeduplicateAxesFromBase()
876	exported.SystemIncludes.DeduplicateAxesFromBase()
877
878	return exported
879}
880
881func bazelLabelForStaticModule(ctx android.BazelConversionPathContext, m blueprint.Module) string {
882	label := android.BazelModuleLabel(ctx, m)
883	if ccModule, ok := m.(*Module); ok && ccModule.typ() == fullLibrary && !android.GenerateCcLibraryStaticOnly(m.Name()) {
884		label += "_bp2build_cc_library_static"
885	}
886	return label
887}
888
889func bazelLabelForSharedModule(ctx android.BazelConversionPathContext, m blueprint.Module) string {
890	// cc_library, at it's root name, propagates the shared library, which depends on the static
891	// library.
892	return android.BazelModuleLabel(ctx, m)
893}
894
895func bazelLabelForStaticWholeModuleDeps(ctx android.BazelConversionPathContext, m blueprint.Module) string {
896	label := bazelLabelForStaticModule(ctx, m)
897	if aModule, ok := m.(android.Module); ok {
898		if android.IsModulePrebuilt(aModule) {
899			label += "_alwayslink"
900		}
901	}
902	return label
903}
904
905func bazelLabelForWholeDeps(ctx android.BazelConversionPathContext, modules []string) bazel.LabelList {
906	return android.BazelLabelForModuleDepsWithFn(ctx, modules, bazelLabelForStaticWholeModuleDeps)
907}
908
909func bazelLabelForWholeDepsExcludes(ctx android.BazelConversionPathContext, modules, excludes []string) bazel.LabelList {
910	return android.BazelLabelForModuleDepsExcludesWithFn(ctx, modules, excludes, bazelLabelForStaticWholeModuleDeps)
911}
912
913func bazelLabelForStaticDepsExcludes(ctx android.BazelConversionPathContext, modules, excludes []string) bazel.LabelList {
914	return android.BazelLabelForModuleDepsExcludesWithFn(ctx, modules, excludes, bazelLabelForStaticModule)
915}
916
917func bazelLabelForStaticDeps(ctx android.BazelConversionPathContext, modules []string) bazel.LabelList {
918	return android.BazelLabelForModuleDepsWithFn(ctx, modules, bazelLabelForStaticModule)
919}
920
921func bazelLabelForSharedDeps(ctx android.BazelConversionPathContext, modules []string) bazel.LabelList {
922	return android.BazelLabelForModuleDepsWithFn(ctx, modules, bazelLabelForSharedModule)
923}
924
925func bazelLabelForHeaderDeps(ctx android.BazelConversionPathContext, modules []string) bazel.LabelList {
926	// This is not elegant, but bp2build's shared library targets only propagate
927	// their header information as part of the normal C++ provider.
928	return bazelLabelForSharedDeps(ctx, modules)
929}
930
931func bazelLabelForSharedDepsExcludes(ctx android.BazelConversionPathContext, modules, excludes []string) bazel.LabelList {
932	return android.BazelLabelForModuleDepsExcludesWithFn(ctx, modules, excludes, bazelLabelForSharedModule)
933}
934
935type binaryLinkerAttrs struct {
936	Linkshared *bool
937}
938
939func bp2buildBinaryLinkerProps(ctx android.BazelConversionPathContext, m *Module) binaryLinkerAttrs {
940	attrs := binaryLinkerAttrs{}
941	archVariantProps := m.GetArchVariantProperties(ctx, &BinaryLinkerProperties{})
942	for axis, configToProps := range archVariantProps {
943		for _, p := range configToProps {
944			props := p.(*BinaryLinkerProperties)
945			staticExecutable := props.Static_executable
946			if axis == bazel.NoConfigAxis {
947				if linkBinaryShared := !proptools.Bool(staticExecutable); !linkBinaryShared {
948					attrs.Linkshared = &linkBinaryShared
949				}
950			} else if staticExecutable != nil {
951				// TODO(b/202876379): Static_executable is arch-variant; however, linkshared is a
952				// nonconfigurable attribute. Only 4 AOSP modules use this feature, defer handling
953				ctx.ModuleErrorf("bp2build cannot migrate a module with arch/target-specific static_executable values")
954			}
955		}
956	}
957
958	return attrs
959}
960