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