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	"github.com/google/blueprint/proptools"
24)
25
26func init() {
27	PreDepsMutators(func(ctx RegisterMutatorsContext) {
28		ctx.BottomUp("variable", variableMutator).Parallel()
29	})
30}
31
32type variableProperties struct {
33	Product_variables struct {
34		Platform_sdk_version struct {
35			Asflags []string
36		}
37
38		// unbundled_build is a catch-all property to annotate modules that don't build in one or
39		// more unbundled branches, usually due to dependencies missing from the manifest.
40		Unbundled_build struct {
41			Enabled *bool `android:"arch_variant"`
42		} `android:"arch_variant"`
43
44		Brillo struct {
45			Cflags         []string
46			Version_script *string `android:"arch_variant"`
47		} `android:"arch_variant"`
48
49		Malloc_not_svelte struct {
50			Cflags []string
51		}
52
53		Safestack struct {
54			Cflags []string `android:"arch_variant"`
55		} `android:"arch_variant"`
56
57		Binder32bit struct {
58			Cflags []string
59		}
60
61		// treble is true when a build is a Treble compliant device.  This is automatically set when
62		// a build is shipped with Android O, but can be overriden.  This controls such things as
63		// the sepolicy split and enabling the Treble linker namespaces.
64		Treble struct {
65			Cflags []string
66		}
67
68		Override_rs_driver struct {
69			Cflags []string
70		}
71
72		// debuggable is true for eng and userdebug builds, and can be used to turn on additional
73		// debugging features that don't significantly impact runtime behavior.  userdebug builds
74		// are used for dogfooding and performance testing, and should be as similar to user builds
75		// as possible.
76		Debuggable struct {
77			Cflags   []string
78			Cppflags []string
79			Init_rc  []string
80		}
81
82		// eng is true for -eng builds, and can be used to turn on additionaly heavyweight debugging
83		// features.
84		Eng struct {
85			Cflags   []string
86			Cppflags []string
87		}
88	} `android:"arch_variant"`
89}
90
91var zeroProductVariables variableProperties
92
93type productVariables struct {
94	// Suffix to add to generated Makefiles
95	Make_suffix *string `json:",omitempty"`
96
97	Platform_sdk_version *int `json:",omitempty"`
98
99	DeviceName        *string   `json:",omitempty"`
100	DeviceArch        *string   `json:",omitempty"`
101	DeviceArchVariant *string   `json:",omitempty"`
102	DeviceCpuVariant  *string   `json:",omitempty"`
103	DeviceAbi         *[]string `json:",omitempty"`
104	DeviceUsesClang   *bool     `json:",omitempty"`
105	DeviceVndkVersion *string   `json:",omitempty"`
106
107	DeviceSecondaryArch        *string   `json:",omitempty"`
108	DeviceSecondaryArchVariant *string   `json:",omitempty"`
109	DeviceSecondaryCpuVariant  *string   `json:",omitempty"`
110	DeviceSecondaryAbi         *[]string `json:",omitempty"`
111
112	HostArch          *string `json:",omitempty"`
113	HostSecondaryArch *string `json:",omitempty"`
114
115	CrossHost              *string `json:",omitempty"`
116	CrossHostArch          *string `json:",omitempty"`
117	CrossHostSecondaryArch *string `json:",omitempty"`
118
119	Allow_missing_dependencies *bool `json:",omitempty"`
120	Unbundled_build            *bool `json:",omitempty"`
121	Brillo                     *bool `json:",omitempty"`
122	Malloc_not_svelte          *bool `json:",omitempty"`
123	Safestack                  *bool `json:",omitempty"`
124	HostStaticBinaries         *bool `json:",omitempty"`
125	Binder32bit                *bool `json:",omitempty"`
126	UseGoma                    *bool `json:",omitempty"`
127	Debuggable                 *bool `json:",omitempty"`
128	Eng                        *bool `json:",omitempty"`
129	EnableCFI                  *bool `json:",omitempty"`
130	Treble                     *bool `json:",omitempty"`
131
132	VendorPath *string `json:",omitempty"`
133
134	ClangTidy  *bool   `json:",omitempty"`
135	TidyChecks *string `json:",omitempty"`
136
137	NativeCoverage       *bool     `json:",omitempty"`
138	CoveragePaths        *[]string `json:",omitempty"`
139	CoverageExcludePaths *[]string `json:",omitempty"`
140
141	DevicePrefer32BitExecutables *bool `json:",omitempty"`
142	HostPrefer32BitExecutables   *bool `json:",omitempty"`
143
144	SanitizeHost       []string `json:",omitempty"`
145	SanitizeDevice     []string `json:",omitempty"`
146	SanitizeDeviceArch []string `json:",omitempty"`
147
148	ArtUseReadBarrier *bool `json:",omitempty"`
149
150	BtConfigIncludeDir *string `json:",omitempty"`
151
152	Override_rs_driver *string `json:",omitempty"`
153}
154
155func boolPtr(v bool) *bool {
156	return &v
157}
158
159func intPtr(v int) *int {
160	return &v
161}
162
163func stringPtr(v string) *string {
164	return &v
165}
166
167func (v *productVariables) SetDefaultConfig() {
168	*v = productVariables{
169		Platform_sdk_version:       intPtr(24),
170		HostArch:                   stringPtr("x86_64"),
171		HostSecondaryArch:          stringPtr("x86"),
172		DeviceName:                 stringPtr("flounder"),
173		DeviceArch:                 stringPtr("arm64"),
174		DeviceArchVariant:          stringPtr("armv8-a"),
175		DeviceCpuVariant:           stringPtr("denver64"),
176		DeviceAbi:                  &[]string{"arm64-v8a"},
177		DeviceUsesClang:            boolPtr(true),
178		DeviceSecondaryArch:        stringPtr("arm"),
179		DeviceSecondaryArchVariant: stringPtr("armv7-a-neon"),
180		DeviceSecondaryCpuVariant:  stringPtr("denver"),
181		DeviceSecondaryAbi:         &[]string{"armeabi-v7a"},
182		Malloc_not_svelte:          boolPtr(false),
183		Safestack:                  boolPtr(false),
184	}
185
186	if runtime.GOOS == "linux" {
187		v.CrossHost = stringPtr("windows")
188		v.CrossHostArch = stringPtr("x86")
189		v.CrossHostSecondaryArch = stringPtr("x86_64")
190	}
191}
192
193func variableMutator(mctx BottomUpMutatorContext) {
194	var module Module
195	var ok bool
196	if module, ok = mctx.Module().(Module); !ok {
197		return
198	}
199
200	// TODO: depend on config variable, create variants, propagate variants up tree
201	a := module.base()
202	variableValues := reflect.ValueOf(&a.variableProperties.Product_variables).Elem()
203	zeroValues := reflect.ValueOf(zeroProductVariables.Product_variables)
204
205	for i := 0; i < variableValues.NumField(); i++ {
206		variableValue := variableValues.Field(i)
207		zeroValue := zeroValues.Field(i)
208		name := variableValues.Type().Field(i).Name
209		property := "product_variables." + proptools.PropertyNameForField(name)
210
211		// Check that the variable was set for the product
212		val := reflect.ValueOf(mctx.Config().(Config).ProductVariables).FieldByName(name)
213		if !val.IsValid() || val.Kind() != reflect.Ptr || val.IsNil() {
214			continue
215		}
216
217		val = val.Elem()
218
219		// For bools, check that the value is true
220		if val.Kind() == reflect.Bool && val.Bool() == false {
221			continue
222		}
223
224		// Check if any properties were set for the module
225		if reflect.DeepEqual(variableValue.Interface(), zeroValue.Interface()) {
226			continue
227		}
228
229		a.setVariableProperties(mctx, property, variableValue, val.Interface())
230	}
231}
232
233func (a *ModuleBase) setVariableProperties(ctx BottomUpMutatorContext,
234	prefix string, productVariablePropertyValue reflect.Value, variableValue interface{}) {
235
236	printfIntoProperties(ctx, prefix, productVariablePropertyValue, variableValue)
237
238	err := proptools.AppendMatchingProperties(a.generalProperties,
239		productVariablePropertyValue.Addr().Interface(), nil)
240	if err != nil {
241		if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
242			ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
243		} else {
244			panic(err)
245		}
246	}
247}
248
249func printfIntoPropertiesError(ctx BottomUpMutatorContext, prefix string,
250	productVariablePropertyValue reflect.Value, i int, err error) {
251
252	field := productVariablePropertyValue.Type().Field(i).Name
253	property := prefix + "." + proptools.PropertyNameForField(field)
254	ctx.PropertyErrorf(property, "%s", err)
255}
256
257func printfIntoProperties(ctx BottomUpMutatorContext, prefix string,
258	productVariablePropertyValue reflect.Value, variableValue interface{}) {
259
260	for i := 0; i < productVariablePropertyValue.NumField(); i++ {
261		propertyValue := productVariablePropertyValue.Field(i)
262		kind := propertyValue.Kind()
263		if kind == reflect.Ptr {
264			if propertyValue.IsNil() {
265				continue
266			}
267			propertyValue = propertyValue.Elem()
268		}
269		switch propertyValue.Kind() {
270		case reflect.String:
271			err := printfIntoProperty(propertyValue, variableValue)
272			if err != nil {
273				printfIntoPropertiesError(ctx, prefix, productVariablePropertyValue, i, err)
274			}
275		case reflect.Slice:
276			for j := 0; j < propertyValue.Len(); j++ {
277				err := printfIntoProperty(propertyValue.Index(j), variableValue)
278				if err != nil {
279					printfIntoPropertiesError(ctx, prefix, productVariablePropertyValue, i, err)
280				}
281			}
282		case reflect.Bool:
283			// Nothing
284		case reflect.Struct:
285			printfIntoProperties(ctx, prefix, propertyValue, variableValue)
286		default:
287			panic(fmt.Errorf("unsupported field kind %q", propertyValue.Kind()))
288		}
289	}
290}
291
292func printfIntoProperty(propertyValue reflect.Value, variableValue interface{}) error {
293	s := propertyValue.String()
294
295	count := strings.Count(s, "%")
296	if count == 0 {
297		return nil
298	}
299
300	if count > 1 {
301		return fmt.Errorf("product variable properties only support a single '%%'")
302	}
303
304	if strings.Contains(s, "%d") {
305		switch v := variableValue.(type) {
306		case int:
307			// Nothing
308		case bool:
309			if v {
310				variableValue = 1
311			} else {
312				variableValue = 0
313			}
314		default:
315			return fmt.Errorf("unsupported type %T for %%d", variableValue)
316		}
317	} else if strings.Contains(s, "%s") {
318		switch variableValue.(type) {
319		case string:
320			// Nothing
321		default:
322			return fmt.Errorf("unsupported type %T for %%s", variableValue)
323		}
324	} else {
325		return fmt.Errorf("unsupported %% in product variable property")
326	}
327
328	propertyValue.Set(reflect.ValueOf(fmt.Sprintf(s, variableValue)))
329
330	return nil
331}
332