• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2015 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 android
16
17import (
18	"fmt"
19	"reflect"
20	"runtime"
21	"strings"
22
23	"android/soong/android/soongconfig"
24	"android/soong/bazel"
25
26	"github.com/google/blueprint/proptools"
27)
28
29func init() {
30	registerVariableBuildComponents(InitRegistrationContext)
31}
32
33func registerVariableBuildComponents(ctx RegistrationContext) {
34	ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
35		ctx.BottomUp("variable", VariableMutator).Parallel()
36	})
37}
38
39var PrepareForTestWithVariables = FixtureRegisterWithContext(registerVariableBuildComponents)
40
41type variableProperties struct {
42	Product_variables struct {
43		Platform_sdk_version struct {
44			Asflags []string
45			Cflags  []string
46			Cmd     *string
47		}
48
49		Platform_sdk_version_or_codename struct {
50			Java_resource_dirs []string
51		}
52
53		Platform_sdk_extension_version struct {
54			Cmd *string
55		}
56
57		Platform_version_name struct {
58			Base_dir *string
59		}
60
61		// unbundled_build is a catch-all property to annotate modules that don't build in one or
62		// more unbundled branches, usually due to dependencies missing from the manifest.
63		Unbundled_build struct {
64			Enabled *bool `android:"arch_variant"`
65		} `android:"arch_variant"`
66
67		Malloc_not_svelte struct {
68			Cflags              []string `android:"arch_variant"`
69			Shared_libs         []string `android:"arch_variant"`
70			Whole_static_libs   []string `android:"arch_variant"`
71			Exclude_static_libs []string `android:"arch_variant"`
72			Srcs                []string `android:"arch_variant"`
73			Header_libs         []string `android:"arch_variant"`
74		} `android:"arch_variant"`
75
76		Malloc_zero_contents struct {
77			Cflags []string `android:"arch_variant"`
78		} `android:"arch_variant"`
79
80		Malloc_pattern_fill_contents struct {
81			Cflags []string `android:"arch_variant"`
82		} `android:"arch_variant"`
83
84		Safestack struct {
85			Cflags []string `android:"arch_variant"`
86		} `android:"arch_variant"`
87
88		Binder32bit struct {
89			Cflags []string
90		}
91
92		Override_rs_driver struct {
93			Cflags []string
94		}
95
96		// treble_linker_namespaces is true when the system/vendor linker namespace separation is
97		// enabled.
98		Treble_linker_namespaces struct {
99			Cflags []string
100		}
101		// enforce_vintf_manifest is true when a device is required to have a vintf manifest.
102		Enforce_vintf_manifest struct {
103			Cflags []string
104		}
105
106		// debuggable is true for eng and userdebug builds, and can be used to turn on additional
107		// debugging features that don't significantly impact runtime behavior.  userdebug builds
108		// are used for dogfooding and performance testing, and should be as similar to user builds
109		// as possible.
110		Debuggable struct {
111			Cflags          []string
112			Cppflags        []string
113			Init_rc         []string
114			Required        []string
115			Host_required   []string
116			Target_required []string
117			Strip           struct {
118				All                          *bool
119				Keep_symbols                 *bool
120				Keep_symbols_and_debug_frame *bool
121			}
122			Static_libs       []string
123			Whole_static_libs []string
124			Shared_libs       []string
125
126			Cmdline []string
127
128			Srcs         []string
129			Exclude_srcs []string
130		}
131
132		// eng is true for -eng builds, and can be used to turn on additional heavyweight debugging
133		// features.
134		Eng struct {
135			Cflags   []string
136			Cppflags []string
137			Lto      struct {
138				Never *bool
139			}
140			Sanitize struct {
141				Address *bool
142			}
143			Optimize struct {
144				Enabled *bool
145			}
146		}
147
148		Pdk struct {
149			Enabled *bool `android:"arch_variant"`
150		} `android:"arch_variant"`
151
152		Uml struct {
153			Cppflags []string
154		}
155
156		Arc struct {
157			Cflags            []string `android:"arch_variant"`
158			Exclude_srcs      []string `android:"arch_variant"`
159			Header_libs       []string `android:"arch_variant"`
160			Include_dirs      []string `android:"arch_variant"`
161			Shared_libs       []string `android:"arch_variant"`
162			Static_libs       []string `android:"arch_variant"`
163			Srcs              []string `android:"arch_variant"`
164			Whole_static_libs []string `android:"arch_variant"`
165		} `android:"arch_variant"`
166
167		Flatten_apex struct {
168			Enabled *bool
169		}
170
171		Native_coverage struct {
172			Src          *string  `android:"arch_variant"`
173			Srcs         []string `android:"arch_variant"`
174			Exclude_srcs []string `android:"arch_variant"`
175		} `android:"arch_variant"`
176	} `android:"arch_variant"`
177}
178
179var defaultProductVariables interface{} = variableProperties{}
180
181type productVariables struct {
182	// Suffix to add to generated Makefiles
183	Make_suffix *string `json:",omitempty"`
184
185	BuildId         *string `json:",omitempty"`
186	BuildNumberFile *string `json:",omitempty"`
187
188	Platform_version_name                     *string  `json:",omitempty"`
189	Platform_sdk_version                      *int     `json:",omitempty"`
190	Platform_sdk_codename                     *string  `json:",omitempty"`
191	Platform_sdk_version_or_codename          *string  `json:",omitempty"`
192	Platform_sdk_final                        *bool    `json:",omitempty"`
193	Platform_sdk_extension_version            *int     `json:",omitempty"`
194	Platform_base_sdk_extension_version       *int     `json:",omitempty"`
195	Platform_version_active_codenames         []string `json:",omitempty"`
196	Platform_vndk_version                     *string  `json:",omitempty"`
197	Platform_systemsdk_versions               []string `json:",omitempty"`
198	Platform_security_patch                   *string  `json:",omitempty"`
199	Platform_preview_sdk_version              *string  `json:",omitempty"`
200	Platform_min_supported_target_sdk_version *string  `json:",omitempty"`
201	Platform_base_os                          *string  `json:",omitempty"`
202	Platform_version_last_stable              *string  `json:",omitempty"`
203
204	DeviceName                            *string  `json:",omitempty"`
205	DeviceProduct                         *string  `json:",omitempty"`
206	DeviceArch                            *string  `json:",omitempty"`
207	DeviceArchVariant                     *string  `json:",omitempty"`
208	DeviceCpuVariant                      *string  `json:",omitempty"`
209	DeviceAbi                             []string `json:",omitempty"`
210	DeviceVndkVersion                     *string  `json:",omitempty"`
211	DeviceCurrentApiLevelForVendorModules *string  `json:",omitempty"`
212	DeviceSystemSdkVersions               []string `json:",omitempty"`
213
214	RecoverySnapshotVersion *string `json:",omitempty"`
215
216	DeviceSecondaryArch        *string  `json:",omitempty"`
217	DeviceSecondaryArchVariant *string  `json:",omitempty"`
218	DeviceSecondaryCpuVariant  *string  `json:",omitempty"`
219	DeviceSecondaryAbi         []string `json:",omitempty"`
220
221	NativeBridgeArch         *string  `json:",omitempty"`
222	NativeBridgeArchVariant  *string  `json:",omitempty"`
223	NativeBridgeCpuVariant   *string  `json:",omitempty"`
224	NativeBridgeAbi          []string `json:",omitempty"`
225	NativeBridgeRelativePath *string  `json:",omitempty"`
226
227	NativeBridgeSecondaryArch         *string  `json:",omitempty"`
228	NativeBridgeSecondaryArchVariant  *string  `json:",omitempty"`
229	NativeBridgeSecondaryCpuVariant   *string  `json:",omitempty"`
230	NativeBridgeSecondaryAbi          []string `json:",omitempty"`
231	NativeBridgeSecondaryRelativePath *string  `json:",omitempty"`
232
233	HostArch          *string `json:",omitempty"`
234	HostSecondaryArch *string `json:",omitempty"`
235	HostMusl          *bool   `json:",omitempty"`
236
237	CrossHost              *string `json:",omitempty"`
238	CrossHostArch          *string `json:",omitempty"`
239	CrossHostSecondaryArch *string `json:",omitempty"`
240
241	DeviceResourceOverlays     []string `json:",omitempty"`
242	ProductResourceOverlays    []string `json:",omitempty"`
243	EnforceRROTargets          []string `json:",omitempty"`
244	EnforceRROExcludedOverlays []string `json:",omitempty"`
245
246	AAPTCharacteristics *string  `json:",omitempty"`
247	AAPTConfig          []string `json:",omitempty"`
248	AAPTPreferredConfig *string  `json:",omitempty"`
249	AAPTPrebuiltDPI     []string `json:",omitempty"`
250
251	DefaultAppCertificate *string `json:",omitempty"`
252
253	AppsDefaultVersionName *string `json:",omitempty"`
254
255	Allow_missing_dependencies   *bool    `json:",omitempty"`
256	Unbundled_build              *bool    `json:",omitempty"`
257	Unbundled_build_apps         []string `json:",omitempty"`
258	Unbundled_build_image        *bool    `json:",omitempty"`
259	Always_use_prebuilt_sdks     *bool    `json:",omitempty"`
260	Skip_boot_jars_check         *bool    `json:",omitempty"`
261	Malloc_not_svelte            *bool    `json:",omitempty"`
262	Malloc_zero_contents         *bool    `json:",omitempty"`
263	Malloc_pattern_fill_contents *bool    `json:",omitempty"`
264	Safestack                    *bool    `json:",omitempty"`
265	HostStaticBinaries           *bool    `json:",omitempty"`
266	Binder32bit                  *bool    `json:",omitempty"`
267	UseGoma                      *bool    `json:",omitempty"`
268	UseRBE                       *bool    `json:",omitempty"`
269	UseRBEJAVAC                  *bool    `json:",omitempty"`
270	UseRBER8                     *bool    `json:",omitempty"`
271	UseRBED8                     *bool    `json:",omitempty"`
272	Debuggable                   *bool    `json:",omitempty"`
273	Eng                          *bool    `json:",omitempty"`
274	Treble_linker_namespaces     *bool    `json:",omitempty"`
275	Enforce_vintf_manifest       *bool    `json:",omitempty"`
276	Uml                          *bool    `json:",omitempty"`
277	Arc                          *bool    `json:",omitempty"`
278	MinimizeJavaDebugInfo        *bool    `json:",omitempty"`
279
280	Check_elf_files *bool `json:",omitempty"`
281
282	UncompressPrivAppDex             *bool    `json:",omitempty"`
283	ModulesLoadedByPrivilegedModules []string `json:",omitempty"`
284
285	BootJars     ConfiguredJarList `json:",omitempty"`
286	ApexBootJars ConfiguredJarList `json:",omitempty"`
287
288	IntegerOverflowExcludePaths []string `json:",omitempty"`
289
290	EnableCFI       *bool    `json:",omitempty"`
291	CFIExcludePaths []string `json:",omitempty"`
292	CFIIncludePaths []string `json:",omitempty"`
293
294	DisableScudo *bool `json:",omitempty"`
295
296	MemtagHeapExcludePaths      []string `json:",omitempty"`
297	MemtagHeapAsyncIncludePaths []string `json:",omitempty"`
298	MemtagHeapSyncIncludePaths  []string `json:",omitempty"`
299
300	VendorPath    *string `json:",omitempty"`
301	OdmPath       *string `json:",omitempty"`
302	ProductPath   *string `json:",omitempty"`
303	SystemExtPath *string `json:",omitempty"`
304
305	ClangTidy  *bool   `json:",omitempty"`
306	TidyChecks *string `json:",omitempty"`
307
308	JavaCoveragePaths        []string `json:",omitempty"`
309	JavaCoverageExcludePaths []string `json:",omitempty"`
310
311	GcovCoverage                *bool    `json:",omitempty"`
312	ClangCoverage               *bool    `json:",omitempty"`
313	NativeCoveragePaths         []string `json:",omitempty"`
314	NativeCoverageExcludePaths  []string `json:",omitempty"`
315	ClangCoverageContinuousMode *bool    `json:",omitempty"`
316
317	// Set by NewConfig
318	Native_coverage *bool `json:",omitempty"`
319
320	SanitizeHost       []string `json:",omitempty"`
321	SanitizeDevice     []string `json:",omitempty"`
322	SanitizeDeviceDiag []string `json:",omitempty"`
323	SanitizeDeviceArch []string `json:",omitempty"`
324
325	ArtUseReadBarrier *bool `json:",omitempty"`
326
327	BtConfigIncludeDir *string `json:",omitempty"`
328
329	Override_rs_driver *string `json:",omitempty"`
330
331	DeviceKernelHeaders []string `json:",omitempty"`
332
333	ExtraVndkVersions []string `json:",omitempty"`
334
335	NamespacesToExport []string `json:",omitempty"`
336
337	AfdoAdditionalProfileDirs []string `json:",omitempty"`
338	PgoAdditionalProfileDirs  []string `json:",omitempty"`
339
340	VndkUseCoreVariant         *bool `json:",omitempty"`
341	VndkSnapshotBuildArtifacts *bool `json:",omitempty"`
342
343	DirectedVendorSnapshot bool            `json:",omitempty"`
344	VendorSnapshotModules  map[string]bool `json:",omitempty"`
345
346	DirectedRecoverySnapshot bool            `json:",omitempty"`
347	RecoverySnapshotModules  map[string]bool `json:",omitempty"`
348
349	VendorSnapshotDirsIncluded   []string `json:",omitempty"`
350	VendorSnapshotDirsExcluded   []string `json:",omitempty"`
351	RecoverySnapshotDirsExcluded []string `json:",omitempty"`
352	RecoverySnapshotDirsIncluded []string `json:",omitempty"`
353	HostFakeSnapshotEnabled      bool     `json:",omitempty"`
354
355	BoardVendorSepolicyDirs           []string `json:",omitempty"`
356	BoardOdmSepolicyDirs              []string `json:",omitempty"`
357	BoardReqdMaskPolicy               []string `json:",omitempty"`
358	BoardPlatVendorPolicy             []string `json:",omitempty"`
359	BoardSystemExtPublicPrebuiltDirs  []string `json:",omitempty"`
360	BoardSystemExtPrivatePrebuiltDirs []string `json:",omitempty"`
361	BoardProductPublicPrebuiltDirs    []string `json:",omitempty"`
362	BoardProductPrivatePrebuiltDirs   []string `json:",omitempty"`
363	SystemExtPublicSepolicyDirs       []string `json:",omitempty"`
364	SystemExtPrivateSepolicyDirs      []string `json:",omitempty"`
365	BoardSepolicyM4Defs               []string `json:",omitempty"`
366
367	BoardSepolicyVers       *string `json:",omitempty"`
368	PlatformSepolicyVersion *string `json:",omitempty"`
369	TotSepolicyVersion      *string `json:",omitempty"`
370
371	SystemExtSepolicyPrebuiltApiDir *string `json:",omitempty"`
372	ProductSepolicyPrebuiltApiDir   *string `json:",omitempty"`
373
374	PlatformSepolicyCompatVersions []string `json:",omitempty"`
375
376	VendorVars map[string]map[string]string `json:",omitempty"`
377
378	Ndk_abis *bool `json:",omitempty"`
379
380	Flatten_apex                 *bool `json:",omitempty"`
381	ForceApexSymlinkOptimization *bool `json:",omitempty"`
382	CompressedApex               *bool `json:",omitempty"`
383	Aml_abis                     *bool `json:",omitempty"`
384
385	DexpreoptGlobalConfig *string `json:",omitempty"`
386
387	WithDexpreopt bool `json:",omitempty"`
388
389	ManifestPackageNameOverrides []string `json:",omitempty"`
390	CertificateOverrides         []string `json:",omitempty"`
391	PackageNameOverrides         []string `json:",omitempty"`
392
393	ApexGlobalMinSdkVersionOverride *string `json:",omitempty"`
394
395	EnforceSystemCertificate          *bool    `json:",omitempty"`
396	EnforceSystemCertificateAllowList []string `json:",omitempty"`
397
398	ProductHiddenAPIStubs       []string `json:",omitempty"`
399	ProductHiddenAPIStubsSystem []string `json:",omitempty"`
400	ProductHiddenAPIStubsTest   []string `json:",omitempty"`
401
402	ProductPublicSepolicyDirs  []string `json:",omitempty"`
403	ProductPrivateSepolicyDirs []string `json:",omitempty"`
404
405	ProductVndkVersion *string `json:",omitempty"`
406
407	TargetFSConfigGen []string `json:",omitempty"`
408
409	MissingUsesLibraries []string `json:",omitempty"`
410
411	EnforceProductPartitionInterface *bool `json:",omitempty"`
412
413	EnforceInterPartitionJavaSdkLibrary *bool    `json:",omitempty"`
414	InterPartitionJavaLibraryAllowList  []string `json:",omitempty"`
415
416	InstallExtraFlattenedApexes *bool `json:",omitempty"`
417
418	BoardUsesRecoveryAsBoot *bool `json:",omitempty"`
419
420	BoardKernelBinaries                []string `json:",omitempty"`
421	BoardKernelModuleInterfaceVersions []string `json:",omitempty"`
422
423	BoardMoveRecoveryResourcesToVendorBoot *bool `json:",omitempty"`
424
425	PrebuiltHiddenApiDir *string `json:",omitempty"`
426
427	ShippingApiLevel *string `json:",omitempty"`
428
429	BuildBrokenEnforceSyspropOwner     bool     `json:",omitempty"`
430	BuildBrokenTrebleSyspropNeverallow bool     `json:",omitempty"`
431	BuildBrokenVendorPropertyNamespace bool     `json:",omitempty"`
432	BuildBrokenInputDirModules         []string `json:",omitempty"`
433
434	BuildDebugfsRestrictionsEnabled bool `json:",omitempty"`
435
436	RequiresInsecureExecmemForSwiftshader bool `json:",omitempty"`
437
438	SelinuxIgnoreNeverallows bool `json:",omitempty"`
439
440	SepolicySplit bool `json:",omitempty"`
441
442	SepolicyFreezeTestExtraDirs         []string `json:",omitempty"`
443	SepolicyFreezeTestExtraPrebuiltDirs []string `json:",omitempty"`
444
445	GenerateAidlNdkPlatformBackend bool `json:",omitempty"`
446
447	ForceMultilibFirstOnDevice bool `json:",omitempty"`
448
449	IncludeTags []string `json:",omitempty"`
450}
451
452func boolPtr(v bool) *bool {
453	return &v
454}
455
456func intPtr(v int) *int {
457	return &v
458}
459
460func stringPtr(v string) *string {
461	return &v
462}
463
464func (v *productVariables) SetDefaultConfig() {
465	*v = productVariables{
466		BuildNumberFile: stringPtr("build_number.txt"),
467
468		Platform_version_name:             stringPtr("S"),
469		Platform_sdk_version:              intPtr(30),
470		Platform_sdk_codename:             stringPtr("S"),
471		Platform_sdk_final:                boolPtr(false),
472		Platform_version_active_codenames: []string{"S"},
473		Platform_vndk_version:             stringPtr("S"),
474
475		HostArch:                   stringPtr("x86_64"),
476		HostSecondaryArch:          stringPtr("x86"),
477		DeviceName:                 stringPtr("generic_arm64"),
478		DeviceProduct:              stringPtr("aosp_arm-eng"),
479		DeviceArch:                 stringPtr("arm64"),
480		DeviceArchVariant:          stringPtr("armv8-a"),
481		DeviceCpuVariant:           stringPtr("generic"),
482		DeviceAbi:                  []string{"arm64-v8a"},
483		DeviceSecondaryArch:        stringPtr("arm"),
484		DeviceSecondaryArchVariant: stringPtr("armv8-a"),
485		DeviceSecondaryCpuVariant:  stringPtr("generic"),
486		DeviceSecondaryAbi:         []string{"armeabi-v7a", "armeabi"},
487
488		AAPTConfig:          []string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"},
489		AAPTPreferredConfig: stringPtr("xhdpi"),
490		AAPTCharacteristics: stringPtr("nosdcard"),
491		AAPTPrebuiltDPI:     []string{"xhdpi", "xxhdpi"},
492
493		Malloc_not_svelte:            boolPtr(true),
494		Malloc_zero_contents:         boolPtr(true),
495		Malloc_pattern_fill_contents: boolPtr(false),
496		Safestack:                    boolPtr(false),
497
498		BootJars:     ConfiguredJarList{apexes: []string{}, jars: []string{}},
499		ApexBootJars: ConfiguredJarList{apexes: []string{}, jars: []string{}},
500	}
501
502	if runtime.GOOS == "linux" {
503		v.CrossHost = stringPtr("windows")
504		v.CrossHostArch = stringPtr("x86")
505		v.CrossHostSecondaryArch = stringPtr("x86_64")
506	}
507}
508
509// ProductConfigContext requires the access to the Module to get product config properties.
510type ProductConfigContext interface {
511	Module() Module
512}
513
514// ProductConfigProperty contains the information for a single property (may be a struct) paired
515// with the appropriate ProductConfigVariable.
516type ProductConfigProperty struct {
517	// The name of the product variable, e.g. "safestack", "malloc_not_svelte",
518	// "board"
519	Name string
520
521	// Namespace of the variable, if this is a soong_config_module_type variable
522	// e.g. "acme", "ANDROID", "vendor_name"
523	Namespace string
524
525	// Unique configuration to identify this product config property (i.e. a
526	// primary key), as just using the product variable name is not sufficient.
527	//
528	// For product variables, this is the product variable name + optional
529	// archvariant information. e.g.
530	//
531	// product_variables: {
532	//     foo: {
533	//         cflags: ["-Dfoo"],
534	//     },
535	// },
536	//
537	// FullConfig would be "foo".
538	//
539	// target: {
540	//     android: {
541	//         product_variables: {
542	//             foo: {
543	//                 cflags: ["-Dfoo-android"],
544	//             },
545	//         },
546	//     },
547	// },
548	//
549	// FullConfig would be "foo-android".
550	//
551	// For soong config variables, this is the namespace + product variable name
552	// + value of the variable, if applicable. The value can also be
553	// conditions_default.
554	//
555	// e.g.
556	//
557	// soong_config_variables: {
558	//     feature1: {
559	//         conditions_default: {
560	//             cflags: ["-DDEFAULT1"],
561	//         },
562	//         cflags: ["-DFEATURE1"],
563	//     },
564	// }
565	//
566	// where feature1 is created in the "acme" namespace, so FullConfig would be
567	// "acme__feature1" and "acme__feature1__conditions_default".
568	//
569	// e.g.
570	//
571	// soong_config_variables: {
572	//     board: {
573	//         soc_a: {
574	//             cflags: ["-DSOC_A"],
575	//         },
576	//         soc_b: {
577	//             cflags: ["-DSOC_B"],
578	//         },
579	//         soc_c: {},
580	//         conditions_default: {
581	//             cflags: ["-DSOC_DEFAULT"]
582	//         },
583	//     },
584	// }
585	//
586	// where board is created in the "acme" namespace, so FullConfig would be
587	// "acme__board__soc_a", "acme__board__soc_b", and
588	// "acme__board__conditions_default"
589	FullConfig string
590}
591
592func (p *ProductConfigProperty) AlwaysEmit() bool {
593	return p.Namespace != ""
594}
595
596func (p *ProductConfigProperty) ConfigurationAxis() bazel.ConfigurationAxis {
597	if p.Namespace == "" {
598		return bazel.ProductVariableConfigurationAxis(p.FullConfig)
599	} else {
600		// Soong config variables can be uniquely identified by the namespace
601		// (e.g. acme, android) and the product variable name (e.g. board, size)
602		return bazel.ProductVariableConfigurationAxis(p.Namespace + "__" + p.Name)
603	}
604}
605
606// SelectKey returns the literal string that represents this variable in a BUILD
607// select statement.
608func (p *ProductConfigProperty) SelectKey() string {
609	if p.Namespace == "" {
610		return strings.ToLower(p.FullConfig)
611	}
612
613	if p.FullConfig == bazel.ConditionsDefaultConfigKey {
614		return bazel.ConditionsDefaultConfigKey
615	}
616
617	value := p.FullConfig
618	if value == p.Name {
619		value = ""
620	}
621
622	// e.g. acme__feature1, android__board__soc_a
623	selectKey := strings.ToLower(strings.Join([]string{p.Namespace, p.Name}, "__"))
624	if value != "" {
625		selectKey = strings.ToLower(strings.Join([]string{selectKey, value}, "__"))
626	}
627
628	return selectKey
629}
630
631// ProductConfigProperties is a map of maps to group property values according
632// their property name and the product config variable they're set under.
633//
634// The outer map key is the name of the property, like "cflags".
635//
636// The inner map key is a ProductConfigProperty, which is a struct of product
637// variable name, namespace, and the "full configuration" of the product
638// variable.
639//
640// e.g. product variable name: board, namespace: acme, full config: vendor_chip_foo
641//
642// The value of the map is the interface{} representing the value of the
643// property, like ["-DDEFINES"] for cflags.
644type ProductConfigProperties map[string]map[ProductConfigProperty]interface{}
645
646// ProductVariableProperties returns a ProductConfigProperties containing only the properties which
647// have been set for the module in the given context.
648func ProductVariableProperties(ctx BazelConversionPathContext) ProductConfigProperties {
649	module := ctx.Module()
650	moduleBase := module.base()
651
652	productConfigProperties := ProductConfigProperties{}
653
654	if moduleBase.variableProperties != nil {
655		productVariablesProperty := proptools.FieldNameForProperty("product_variables")
656		productVariableValues(
657			productVariablesProperty,
658			moduleBase.variableProperties,
659			"",
660			"",
661			&productConfigProperties)
662
663		for _, configToProps := range moduleBase.GetArchVariantProperties(ctx, moduleBase.variableProperties) {
664			for config, props := range configToProps {
665				// GetArchVariantProperties is creating an instance of the requested type
666				// and productVariablesValues expects an interface, so no need to cast
667				productVariableValues(
668					productVariablesProperty,
669					props,
670					"",
671					config,
672					&productConfigProperties)
673			}
674		}
675	}
676
677	if m, ok := module.(Bazelable); ok && m.namespacedVariableProps() != nil {
678		for namespace, namespacedVariableProps := range m.namespacedVariableProps() {
679			for _, namespacedVariableProp := range namespacedVariableProps {
680				productVariableValues(
681					soongconfig.SoongConfigProperty,
682					namespacedVariableProp,
683					namespace,
684					"",
685					&productConfigProperties)
686			}
687		}
688	}
689
690	productConfigProperties.zeroValuesForNamespacedVariables()
691
692	return productConfigProperties
693}
694
695// zeroValuesForNamespacedVariables ensures that selects that contain __only__
696// conditions default values have zero values set for the other non-default
697// values for that select statement.
698//
699// If the ProductConfigProperties map contains these items, as parsed from the .bp file:
700//
701// library_linking_strategy: {
702//     prefer_static: {
703//         static_libs: [
704//             "lib_a",
705//             "lib_b",
706//         ],
707//     },
708//     conditions_default: {
709//         shared_libs: [
710//             "lib_a",
711//             "lib_b",
712//         ],
713//     },
714// },
715//
716// Static_libs {Library_linking_strategy ANDROID prefer_static} [lib_a lib_b]
717// Shared_libs {Library_linking_strategy ANDROID conditions_default} [lib_a lib_b]
718//
719// We need to add this:
720//
721// Shared_libs {Library_linking_strategy ANDROID prefer_static} []
722//
723// so that the following gets generated for the "dynamic_deps" attribute,
724// instead of putting lib_a and lib_b directly into dynamic_deps without a
725// select:
726//
727// dynamic_deps = select({
728//     "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": [],
729//     "//conditions:default": [
730//         "//foo/bar:lib_a",
731//         "//foo/bar:lib_b",
732//     ],
733// }),
734func (props *ProductConfigProperties) zeroValuesForNamespacedVariables() {
735	// A map of product config properties to the zero values of their respective
736	// property value.
737	zeroValues := make(map[ProductConfigProperty]interface{})
738
739	// A map of prop names (e.g. cflags) to product config properties where the
740	// (prop name, ProductConfigProperty) tuple contains a non-conditions_default key.
741	//
742	// e.g.
743	//
744	// prefer_static: {
745	//     static_libs: [
746	//         "lib_a",
747	//         "lib_b",
748	//     ],
749	// },
750	// conditions_default: {
751	//     shared_libs: [
752	//         "lib_a",
753	//         "lib_b",
754	//     ],
755	// },
756	//
757	// The tuple of ("static_libs", prefer_static) would be in this map.
758	hasNonDefaultValue := make(map[string]map[ProductConfigProperty]bool)
759
760	// Iterate over all added soong config variables.
761	for propName, v := range *props {
762		for p, intf := range v {
763			if p.Namespace == "" {
764				// If there's no namespace, this isn't a soong config variable,
765				// i.e. this is a product variable. product variables have no
766				// conditions_defaults, so skip them.
767				continue
768			}
769			if p.FullConfig == bazel.ConditionsDefaultConfigKey {
770				// Skip conditions_defaults.
771				continue
772			}
773			if hasNonDefaultValue[propName] == nil {
774				hasNonDefaultValue[propName] = make(map[ProductConfigProperty]bool)
775				hasNonDefaultValue[propName][p] = false
776			}
777			// Create the zero value of the variable.
778			if _, exists := zeroValues[p]; !exists {
779				zeroValue := reflect.Zero(reflect.ValueOf(intf).Type()).Interface()
780				if zeroValue == nil {
781					panic(fmt.Errorf("Expected non-nil zero value for product/config variable %+v\n", intf))
782				}
783				zeroValues[p] = zeroValue
784			}
785			hasNonDefaultValue[propName][p] = true
786		}
787	}
788
789	for propName := range *props {
790		for p, zeroValue := range zeroValues {
791			// Ignore variables that already have a non-default value for that axis
792			if exists, _ := hasNonDefaultValue[propName][p]; !exists {
793				// fmt.Println(propName, p.Namespace, p.Name, p.FullConfig, zeroValue)
794				// Insert the zero value for this propname + product config value.
795				props.AddProductConfigProperty(
796					propName,
797					p.Namespace,
798					p.Name,
799					p.FullConfig,
800					zeroValue,
801				)
802			}
803		}
804	}
805}
806
807func (p *ProductConfigProperties) AddProductConfigProperty(
808	propertyName, namespace, productVariableName, config string, property interface{}) {
809	if (*p)[propertyName] == nil {
810		(*p)[propertyName] = make(map[ProductConfigProperty]interface{})
811	}
812
813	productConfigProp := ProductConfigProperty{
814		Namespace:  namespace,           // e.g. acme, android
815		Name:       productVariableName, // e.g. size, feature1, feature2, FEATURE3, board
816		FullConfig: config,              // e.g. size, feature1-x86, size__conditions_default
817	}
818
819	if existing, ok := (*p)[propertyName][productConfigProp]; ok && namespace != "" {
820		switch dst := existing.(type) {
821		case []string:
822			if src, ok := property.([]string); ok {
823				dst = append(dst, src...)
824				(*p)[propertyName][productConfigProp] = dst
825			}
826		default:
827			panic(fmt.Errorf("TODO: handle merging value %s", existing))
828		}
829	} else {
830		(*p)[propertyName][productConfigProp] = property
831	}
832}
833
834var (
835	conditionsDefaultField string = proptools.FieldNameForProperty(bazel.ConditionsDefaultConfigKey)
836)
837
838// maybeExtractConfigVarProp attempts to read this value as a config var struct
839// wrapped by interfaces and ptrs. If it's not the right type, the second return
840// value is false.
841func maybeExtractConfigVarProp(v reflect.Value) (reflect.Value, bool) {
842	if v.Kind() == reflect.Interface {
843		// The conditions_default value can be either
844		// 1) an ptr to an interface of a struct (bool config variables and product variables)
845		// 2) an interface of 1) (config variables with nested structs, like string vars)
846		v = v.Elem()
847	}
848	if v.Kind() != reflect.Ptr {
849		return v, false
850	}
851	v = reflect.Indirect(v)
852	if v.Kind() == reflect.Interface {
853		// Extract the struct from the interface
854		v = v.Elem()
855	}
856
857	if !v.IsValid() {
858		return v, false
859	}
860
861	if v.Kind() != reflect.Struct {
862		return v, false
863	}
864	return v, true
865}
866
867func (productConfigProperties *ProductConfigProperties) AddProductConfigProperties(namespace, suffix string, variableValues reflect.Value) {
868	// variableValues can either be a product_variables or
869	// soong_config_variables struct.
870	//
871	// Example of product_variables:
872	//
873	// product_variables: {
874	//     malloc_not_svelte: {
875	//         shared_libs: ["malloc_not_svelte_shared_lib"],
876	//         whole_static_libs: ["malloc_not_svelte_whole_static_lib"],
877	//         exclude_static_libs: [
878	//             "malloc_not_svelte_static_lib_excludes",
879	//             "malloc_not_svelte_whole_static_lib_excludes",
880	//         ],
881	//     },
882	// },
883	//
884	// Example of soong_config_variables:
885	//
886	// soong_config_variables: {
887	//      feature1: {
888	//        	conditions_default: {
889	//               ...
890	//          },
891	//          cflags: ...
892	//      },
893	//      feature2: {
894	//          cflags: ...
895	//        	conditions_default: {
896	//               ...
897	//          },
898	//      },
899	//      board: {
900	//         soc_a: {
901	//             ...
902	//         },
903	//         soc_a: {
904	//             ...
905	//         },
906	//         soc_c: {},
907	//         conditions_default: {
908	//              ...
909	//         },
910	//      },
911	// }
912	for i := 0; i < variableValues.NumField(); i++ {
913		// e.g. Platform_sdk_version, Unbundled_build, Malloc_not_svelte, etc.
914		productVariableName := variableValues.Type().Field(i).Name
915
916		variableValue := variableValues.Field(i)
917		// Check if any properties were set for the module
918		if variableValue.IsZero() {
919			// e.g. feature1: {}, malloc_not_svelte: {}
920			continue
921		}
922
923		// Unlike product variables, config variables require a few more
924		// indirections to extract the struct from the reflect.Value.
925		if v, ok := maybeExtractConfigVarProp(variableValue); ok {
926			variableValue = v
927		}
928
929		for j := 0; j < variableValue.NumField(); j++ {
930			property := variableValue.Field(j)
931			// If the property wasn't set, no need to pass it along
932			if property.IsZero() {
933				continue
934			}
935
936			// e.g. Asflags, Cflags, Enabled, etc.
937			propertyName := variableValue.Type().Field(j).Name
938
939			if v, ok := maybeExtractConfigVarProp(property); ok {
940				// The field is a struct, which is used by:
941				// 1) soong_config_string_variables
942				//
943				// soc_a: {
944				//     cflags: ...,
945				// }
946				//
947				// soc_b: {
948				//     cflags: ...,
949				// }
950				//
951				// 2) conditions_default structs for all soong config variable types.
952				//
953				// conditions_default: {
954				//     cflags: ...,
955				//     static_libs: ...
956				// }
957				field := v
958				for k := 0; k < field.NumField(); k++ {
959					// Iterate over fields of this struct prop.
960					if field.Field(k).IsZero() {
961						continue
962					}
963					// config can also be "conditions_default".
964					config := proptools.PropertyNameForField(propertyName)
965					actualPropertyName := field.Type().Field(k).Name
966
967					productConfigProperties.AddProductConfigProperty(
968						actualPropertyName,  // e.g. cflags, static_libs
969						namespace,           // e.g. acme, android
970						productVariableName, // e.g. size, feature1, FEATURE2, board
971						config,
972						field.Field(k).Interface(), // e.g. ["-DDEFAULT"], ["foo", "bar"]
973					)
974				}
975			} else if property.Kind() != reflect.Interface {
976				// If not an interface, then this is not a conditions_default or
977				// a struct prop. That is, this is a regular product variable,
978				// or a bool/value config variable.
979				config := productVariableName + suffix
980				productConfigProperties.AddProductConfigProperty(
981					propertyName,
982					namespace,
983					productVariableName,
984					config,
985					property.Interface(),
986				)
987			}
988		}
989	}
990}
991
992// productVariableValues uses reflection to convert a property struct for
993// product_variables and soong_config_variables to structs that can be generated
994// as select statements.
995func productVariableValues(
996	fieldName string, variableProps interface{}, namespace, suffix string, productConfigProperties *ProductConfigProperties) {
997	if suffix != "" {
998		suffix = "-" + suffix
999	}
1000
1001	// variableValues represent the product_variables or soong_config_variables struct.
1002	variableValues := reflect.ValueOf(variableProps).Elem().FieldByName(fieldName)
1003	productConfigProperties.AddProductConfigProperties(namespace, suffix, variableValues)
1004}
1005
1006func VariableMutator(mctx BottomUpMutatorContext) {
1007	var module Module
1008	var ok bool
1009	if module, ok = mctx.Module().(Module); !ok {
1010		return
1011	}
1012
1013	// TODO: depend on config variable, create variants, propagate variants up tree
1014	a := module.base()
1015
1016	if a.variableProperties == nil {
1017		return
1018	}
1019
1020	variableValues := reflect.ValueOf(a.variableProperties).Elem().FieldByName("Product_variables")
1021
1022	productVariables := reflect.ValueOf(mctx.Config().productVariables)
1023
1024	for i := 0; i < variableValues.NumField(); i++ {
1025		variableValue := variableValues.Field(i)
1026		name := variableValues.Type().Field(i).Name
1027		property := "product_variables." + proptools.PropertyNameForField(name)
1028
1029		// Check that the variable was set for the product
1030		val := productVariables.FieldByName(name)
1031		if !val.IsValid() || val.Kind() != reflect.Ptr || val.IsNil() {
1032			continue
1033		}
1034
1035		val = val.Elem()
1036
1037		// For bools, check that the value is true
1038		if val.Kind() == reflect.Bool && val.Bool() == false {
1039			continue
1040		}
1041
1042		// Check if any properties were set for the module
1043		if variableValue.IsZero() {
1044			continue
1045		}
1046		a.setVariableProperties(mctx, property, variableValue, val.Interface())
1047	}
1048}
1049
1050func (m *ModuleBase) setVariableProperties(ctx BottomUpMutatorContext,
1051	prefix string, productVariablePropertyValue reflect.Value, variableValue interface{}) {
1052
1053	printfIntoProperties(ctx, prefix, productVariablePropertyValue, variableValue)
1054
1055	err := proptools.AppendMatchingProperties(m.GetProperties(),
1056		productVariablePropertyValue.Addr().Interface(), nil)
1057	if err != nil {
1058		if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
1059			ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
1060		} else {
1061			panic(err)
1062		}
1063	}
1064}
1065
1066func printfIntoPropertiesError(ctx BottomUpMutatorContext, prefix string,
1067	productVariablePropertyValue reflect.Value, i int, err error) {
1068
1069	field := productVariablePropertyValue.Type().Field(i).Name
1070	property := prefix + "." + proptools.PropertyNameForField(field)
1071	ctx.PropertyErrorf(property, "%s", err)
1072}
1073
1074func printfIntoProperties(ctx BottomUpMutatorContext, prefix string,
1075	productVariablePropertyValue reflect.Value, variableValue interface{}) {
1076
1077	for i := 0; i < productVariablePropertyValue.NumField(); i++ {
1078		propertyValue := productVariablePropertyValue.Field(i)
1079		kind := propertyValue.Kind()
1080		if kind == reflect.Ptr {
1081			if propertyValue.IsNil() {
1082				continue
1083			}
1084			propertyValue = propertyValue.Elem()
1085		}
1086		switch propertyValue.Kind() {
1087		case reflect.String:
1088			err := printfIntoProperty(propertyValue, variableValue)
1089			if err != nil {
1090				printfIntoPropertiesError(ctx, prefix, productVariablePropertyValue, i, err)
1091			}
1092		case reflect.Slice:
1093			for j := 0; j < propertyValue.Len(); j++ {
1094				err := printfIntoProperty(propertyValue.Index(j), variableValue)
1095				if err != nil {
1096					printfIntoPropertiesError(ctx, prefix, productVariablePropertyValue, i, err)
1097				}
1098			}
1099		case reflect.Bool:
1100			// Nothing
1101		case reflect.Struct:
1102			printfIntoProperties(ctx, prefix, propertyValue, variableValue)
1103		default:
1104			panic(fmt.Errorf("unsupported field kind %q", propertyValue.Kind()))
1105		}
1106	}
1107}
1108
1109func printfIntoProperty(propertyValue reflect.Value, variableValue interface{}) error {
1110	s := propertyValue.String()
1111
1112	count := strings.Count(s, "%")
1113	if count == 0 {
1114		return nil
1115	}
1116
1117	if count > 1 {
1118		return fmt.Errorf("product variable properties only support a single '%%'")
1119	}
1120
1121	if strings.Contains(s, "%d") {
1122		switch v := variableValue.(type) {
1123		case int:
1124			// Nothing
1125		case bool:
1126			if v {
1127				variableValue = 1
1128			} else {
1129				variableValue = 0
1130			}
1131		default:
1132			return fmt.Errorf("unsupported type %T for %%d", variableValue)
1133		}
1134	} else if strings.Contains(s, "%s") {
1135		switch variableValue.(type) {
1136		case string:
1137			// Nothing
1138		default:
1139			return fmt.Errorf("unsupported type %T for %%s", variableValue)
1140		}
1141	} else {
1142		return fmt.Errorf("unsupported %% in product variable property")
1143	}
1144
1145	propertyValue.Set(reflect.ValueOf(fmt.Sprintf(s, variableValue)))
1146
1147	return nil
1148}
1149
1150var variablePropTypeMap OncePer
1151
1152// sliceToTypeArray takes a slice of property structs and returns a reflection created array containing the
1153// reflect.Types of each property struct.  The result can be used as a key in a map.
1154func sliceToTypeArray(s []interface{}) interface{} {
1155	// Create an array using reflection whose length is the length of the input slice
1156	ret := reflect.New(reflect.ArrayOf(len(s), reflect.TypeOf(reflect.TypeOf(0)))).Elem()
1157	for i, e := range s {
1158		ret.Index(i).Set(reflect.ValueOf(reflect.TypeOf(e)))
1159	}
1160	return ret.Interface()
1161}
1162
1163func initProductVariableModule(m Module) {
1164	base := m.base()
1165
1166	// Allow tests to override the default product variables
1167	if base.variableProperties == nil {
1168		base.variableProperties = defaultProductVariables
1169	}
1170	// Filter the product variables properties to the ones that exist on this module
1171	base.variableProperties = createVariableProperties(m.GetProperties(), base.variableProperties)
1172	if base.variableProperties != nil {
1173		m.AddProperties(base.variableProperties)
1174	}
1175}
1176
1177// createVariableProperties takes the list of property structs for a module and returns a property struct that
1178// contains the product variable properties that exist in the property structs, or nil if there are none.  It
1179// caches the result.
1180func createVariableProperties(moduleTypeProps []interface{}, productVariables interface{}) interface{} {
1181	// Convert the moduleTypeProps to an array of reflect.Types that can be used as a key in the OncePer.
1182	key := sliceToTypeArray(moduleTypeProps)
1183
1184	// Use the variablePropTypeMap OncePer to cache the result for each set of property struct types.
1185	typ, _ := variablePropTypeMap.Once(NewCustomOnceKey(key), func() interface{} {
1186		// Compute the filtered property struct type.
1187		return createVariablePropertiesType(moduleTypeProps, productVariables)
1188	}).(reflect.Type)
1189
1190	if typ == nil {
1191		return nil
1192	}
1193
1194	// Create a new pointer to a filtered property struct.
1195	return reflect.New(typ).Interface()
1196}
1197
1198// createVariablePropertiesType creates a new type that contains only the product variable properties that exist in
1199// a list of property structs.
1200func createVariablePropertiesType(moduleTypeProps []interface{}, productVariables interface{}) reflect.Type {
1201	typ, _ := proptools.FilterPropertyStruct(reflect.TypeOf(productVariables),
1202		func(field reflect.StructField, prefix string) (bool, reflect.StructField) {
1203			// Filter function, returns true if the field should be in the resulting struct
1204			if prefix == "" {
1205				// Keep the top level Product_variables field
1206				return true, field
1207			}
1208			_, rest := splitPrefix(prefix)
1209			if rest == "" {
1210				// Keep the 2nd level field (i.e. Product_variables.Eng)
1211				return true, field
1212			}
1213
1214			// Strip off the first 2 levels of the prefix
1215			_, prefix = splitPrefix(rest)
1216
1217			for _, p := range moduleTypeProps {
1218				if fieldExistsByNameRecursive(reflect.TypeOf(p).Elem(), prefix, field.Name) {
1219					// Keep any fields that exist in one of the property structs
1220					return true, field
1221				}
1222			}
1223
1224			return false, field
1225		})
1226	return typ
1227}
1228
1229func splitPrefix(prefix string) (first, rest string) {
1230	index := strings.IndexByte(prefix, '.')
1231	if index == -1 {
1232		return prefix, ""
1233	}
1234	return prefix[:index], prefix[index+1:]
1235}
1236
1237func fieldExistsByNameRecursive(t reflect.Type, prefix, name string) bool {
1238	if t.Kind() != reflect.Struct {
1239		panic(fmt.Errorf("fieldExistsByNameRecursive can only be called on a reflect.Struct"))
1240	}
1241
1242	if prefix != "" {
1243		split := strings.SplitN(prefix, ".", 2)
1244		firstPrefix := split[0]
1245		rest := ""
1246		if len(split) > 1 {
1247			rest = split[1]
1248		}
1249		f, exists := t.FieldByName(firstPrefix)
1250		if !exists {
1251			return false
1252		}
1253		ft := f.Type
1254		if ft.Kind() == reflect.Ptr {
1255			ft = ft.Elem()
1256		}
1257		if ft.Kind() != reflect.Struct {
1258			panic(fmt.Errorf("field %q in %q is not a struct", firstPrefix, t))
1259		}
1260		return fieldExistsByNameRecursive(ft, rest, name)
1261	} else {
1262		_, exists := t.FieldByName(name)
1263		return exists
1264	}
1265}
1266