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	"encoding/json"
19	"fmt"
20	"io/ioutil"
21	"os"
22	"path/filepath"
23	"runtime"
24	"strconv"
25	"strings"
26	"sync"
27
28	"github.com/google/blueprint/proptools"
29)
30
31var Bool = proptools.Bool
32var String = proptools.String
33
34// The configuration file name
35const configFileName = "soong.config"
36const productVariablesFileName = "soong.variables"
37
38// A FileConfigurableOptions contains options which can be configured by the
39// config file. These will be included in the config struct.
40type FileConfigurableOptions struct {
41	Mega_device *bool `json:",omitempty"`
42	Ndk_abis    *bool `json:",omitempty"`
43	Host_bionic *bool `json:",omitempty"`
44}
45
46func (f *FileConfigurableOptions) SetDefaultConfig() {
47	*f = FileConfigurableOptions{}
48}
49
50// A Config object represents the entire build configuration for Android.
51type Config struct {
52	*config
53}
54
55// A DeviceConfig object represents the configuration for a particular device being built.  For
56// now there will only be one of these, but in the future there may be multiple devices being
57// built
58type DeviceConfig struct {
59	*deviceConfig
60}
61
62type config struct {
63	FileConfigurableOptions
64	ProductVariables productVariables
65
66	ConfigFileName           string
67	ProductVariablesFileName string
68
69	Targets        map[OsClass][]Target
70	BuildOsVariant string
71
72	deviceConfig *deviceConfig
73
74	srcDir   string // the path of the root source directory
75	buildDir string // the path of the build output directory
76
77	envLock   sync.Mutex
78	envDeps   map[string]string
79	envFrozen bool
80
81	inMake bool
82
83	OncePer
84}
85
86type deviceConfig struct {
87	config  *config
88	targets []Arch
89	OncePer
90}
91
92type jsonConfigurable interface {
93	SetDefaultConfig()
94}
95
96func loadConfig(config *config) error {
97	err := loadFromConfigFile(&config.FileConfigurableOptions, config.ConfigFileName)
98	if err != nil {
99		return err
100	}
101
102	return loadFromConfigFile(&config.ProductVariables, config.ProductVariablesFileName)
103}
104
105// loads configuration options from a JSON file in the cwd.
106func loadFromConfigFile(configurable jsonConfigurable, filename string) error {
107	// Try to open the file
108	configFileReader, err := os.Open(filename)
109	defer configFileReader.Close()
110	if os.IsNotExist(err) {
111		// Need to create a file, so that blueprint & ninja don't get in
112		// a dependency tracking loop.
113		// Make a file-configurable-options with defaults, write it out using
114		// a json writer.
115		configurable.SetDefaultConfig()
116		err = saveToConfigFile(configurable, filename)
117		if err != nil {
118			return err
119		}
120	} else {
121		// Make a decoder for it
122		jsonDecoder := json.NewDecoder(configFileReader)
123		err = jsonDecoder.Decode(configurable)
124		if err != nil {
125			return fmt.Errorf("config file: %s did not parse correctly: "+err.Error(), filename)
126		}
127	}
128
129	// No error
130	return nil
131}
132
133// atomically writes the config file in case two copies of soong_build are running simultaneously
134// (for example, docs generation and ninja manifest generation)
135func saveToConfigFile(config jsonConfigurable, filename string) error {
136	data, err := json.MarshalIndent(&config, "", "    ")
137	if err != nil {
138		return fmt.Errorf("cannot marshal config data: %s", err.Error())
139	}
140
141	f, err := ioutil.TempFile(filepath.Dir(filename), "config")
142	if err != nil {
143		return fmt.Errorf("cannot create empty config file %s: %s\n", filename, err.Error())
144	}
145	defer os.Remove(f.Name())
146	defer f.Close()
147
148	_, err = f.Write(data)
149	if err != nil {
150		return fmt.Errorf("default config file: %s could not be written: %s", filename, err.Error())
151	}
152
153	_, err = f.WriteString("\n")
154	if err != nil {
155		return fmt.Errorf("default config file: %s could not be written: %s", filename, err.Error())
156	}
157
158	f.Close()
159	os.Rename(f.Name(), filename)
160
161	return nil
162}
163
164// TestConfig returns a Config object suitable for using for tests
165func TestConfig(buildDir string) Config {
166	return Config{&config{
167		buildDir: buildDir,
168	}}
169}
170
171// New creates a new Config object.  The srcDir argument specifies the path to
172// the root source directory. It also loads the config file, if found.
173func NewConfig(srcDir, buildDir string) (Config, error) {
174	// Make a config with default options
175	config := &config{
176		ConfigFileName:           filepath.Join(buildDir, configFileName),
177		ProductVariablesFileName: filepath.Join(buildDir, productVariablesFileName),
178
179		srcDir:   srcDir,
180		buildDir: buildDir,
181
182		deviceConfig: &deviceConfig{},
183	}
184
185	deviceConfig := &deviceConfig{
186		config: config,
187	}
188
189	config.deviceConfig = deviceConfig
190
191	// Sanity check the build and source directories. This won't catch strange
192	// configurations with symlinks, but at least checks the obvious cases.
193	absBuildDir, err := filepath.Abs(buildDir)
194	if err != nil {
195		return Config{}, err
196	}
197
198	absSrcDir, err := filepath.Abs(srcDir)
199	if err != nil {
200		return Config{}, err
201	}
202
203	if strings.HasPrefix(absSrcDir, absBuildDir) {
204		return Config{}, fmt.Errorf("Build dir must not contain source directory")
205	}
206
207	// Load any configurable options from the configuration file
208	err = loadConfig(config)
209	if err != nil {
210		return Config{}, err
211	}
212
213	inMakeFile := filepath.Join(buildDir, ".soong.in_make")
214	if _, err := os.Stat(inMakeFile); err == nil {
215		config.inMake = true
216	}
217
218	targets, err := decodeTargetProductVariables(config)
219	if err != nil {
220		return Config{}, err
221	}
222
223	var archConfig []archConfig
224	if Bool(config.Mega_device) {
225		archConfig = getMegaDeviceConfig()
226	} else if Bool(config.Ndk_abis) {
227		archConfig = getNdkAbisConfig()
228	}
229
230	if archConfig != nil {
231		deviceTargets, err := decodeArchSettings(archConfig)
232		if err != nil {
233			return Config{}, err
234		}
235		targets[Device] = deviceTargets
236	}
237
238	config.Targets = targets
239	config.BuildOsVariant = targets[Host][0].String()
240
241	return Config{config}, nil
242}
243
244func (c *config) RemoveAbandonedFiles() bool {
245	return false
246}
247
248func (c *config) BlueprintToolLocation() string {
249	return filepath.Join(c.buildDir, "host", c.PrebuiltOS(), "bin")
250}
251
252// PrebuiltOS returns the name of the host OS used in prebuilts directories
253func (c *config) PrebuiltOS() string {
254	switch runtime.GOOS {
255	case "linux":
256		return "linux-x86"
257	case "darwin":
258		return "darwin-x86"
259	default:
260		panic("Unknown GOOS")
261	}
262}
263
264// GoRoot returns the path to the root directory of the Go toolchain.
265func (c *config) GoRoot() string {
266	return fmt.Sprintf("%s/prebuilts/go/%s", c.srcDir, c.PrebuiltOS())
267}
268
269func (c *config) CpPreserveSymlinksFlags() string {
270	switch runtime.GOOS {
271	case "darwin":
272		return "-R"
273	case "linux":
274		return "-d"
275	default:
276		return ""
277	}
278}
279
280func (c *config) Getenv(key string) string {
281	var val string
282	var exists bool
283	c.envLock.Lock()
284	defer c.envLock.Unlock()
285	if c.envDeps == nil {
286		c.envDeps = make(map[string]string)
287	}
288	if val, exists = c.envDeps[key]; !exists {
289		if c.envFrozen {
290			panic("Cannot access new environment variables after envdeps are frozen")
291		}
292		val = os.Getenv(key)
293		c.envDeps[key] = val
294	}
295	return val
296}
297
298func (c *config) GetenvWithDefault(key string, defaultValue string) string {
299	ret := c.Getenv(key)
300	if ret == "" {
301		return defaultValue
302	}
303	return ret
304}
305
306func (c *config) IsEnvTrue(key string) bool {
307	value := c.Getenv(key)
308	return value == "1" || value == "y" || value == "yes" || value == "on" || value == "true"
309}
310
311func (c *config) IsEnvFalse(key string) bool {
312	value := c.Getenv(key)
313	return value == "0" || value == "n" || value == "no" || value == "off" || value == "false"
314}
315
316func (c *config) EnvDeps() map[string]string {
317	c.envLock.Lock()
318	defer c.envLock.Unlock()
319	c.envFrozen = true
320	return c.envDeps
321}
322
323func (c *config) EmbeddedInMake() bool {
324	return c.inMake
325}
326
327// DeviceName returns the name of the current device target
328// TODO: take an AndroidModuleContext to select the device name for multi-device builds
329func (c *config) DeviceName() string {
330	return *c.ProductVariables.DeviceName
331}
332
333func (c *config) DeviceUsesClang() bool {
334	if c.ProductVariables.DeviceUsesClang != nil {
335		return *c.ProductVariables.DeviceUsesClang
336	}
337	return true
338}
339
340func (c *config) ResourceOverlays() []SourcePath {
341	return nil
342}
343
344func (c *config) PlatformVersion() string {
345	return "M"
346}
347
348func (c *config) PlatformSdkVersionInt() int {
349	return *c.ProductVariables.Platform_sdk_version
350}
351
352func (c *config) PlatformSdkVersion() string {
353	return strconv.Itoa(c.PlatformSdkVersionInt())
354}
355
356func (c *config) BuildNumber() string {
357	return "000000"
358}
359
360func (c *config) ProductAaptConfig() []string {
361	return []string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"}
362}
363
364func (c *config) ProductAaptPreferredConfig() string {
365	return "xhdpi"
366}
367
368func (c *config) ProductAaptCharacteristics() string {
369	return "nosdcard"
370}
371
372func (c *config) DefaultAppCertificateDir(ctx PathContext) SourcePath {
373	return PathForSource(ctx, "build/target/product/security")
374}
375
376func (c *config) DefaultAppCertificate(ctx PathContext) SourcePath {
377	return c.DefaultAppCertificateDir(ctx).Join(ctx, "testkey")
378}
379
380func (c *config) AllowMissingDependencies() bool {
381	return Bool(c.ProductVariables.Allow_missing_dependencies)
382}
383
384func (c *config) DevicePrefer32BitExecutables() bool {
385	return Bool(c.ProductVariables.DevicePrefer32BitExecutables)
386}
387
388func (c *config) SkipDeviceInstall() bool {
389	return c.EmbeddedInMake() || Bool(c.Mega_device)
390}
391
392func (c *config) SanitizeHost() []string {
393	return append([]string(nil), c.ProductVariables.SanitizeHost...)
394}
395
396func (c *config) SanitizeDevice() []string {
397	return append([]string(nil), c.ProductVariables.SanitizeDevice...)
398}
399
400func (c *config) SanitizeDeviceArch() []string {
401	return append([]string(nil), c.ProductVariables.SanitizeDeviceArch...)
402}
403
404func (c *config) EnableCFI() bool {
405	return Bool(c.ProductVariables.EnableCFI)
406}
407
408func (c *config) Android64() bool {
409	for _, t := range c.Targets[Device] {
410		if t.Arch.ArchType.Multilib == "lib64" {
411			return true
412		}
413	}
414
415	return false
416}
417
418func (c *config) UseGoma() bool {
419	return Bool(c.ProductVariables.UseGoma)
420}
421
422func (c *config) ClangTidy() bool {
423	return Bool(c.ProductVariables.ClangTidy)
424}
425
426func (c *config) TidyChecks() string {
427	if c.ProductVariables.TidyChecks == nil {
428		return ""
429	}
430	return *c.ProductVariables.TidyChecks
431}
432
433func (c *config) LibartImgHostBaseAddress() string {
434	return "0x60000000"
435}
436
437func (c *config) LibartImgDeviceBaseAddress() string {
438	switch c.Targets[Device][0].Arch.ArchType {
439	default:
440		return "0x70000000"
441	case Mips, Mips64:
442		return "0x64000000"
443	}
444}
445
446func (c *config) ArtUseReadBarrier() bool {
447	return Bool(c.ProductVariables.ArtUseReadBarrier)
448}
449
450func (c *deviceConfig) Arches() []Arch {
451	var arches []Arch
452	for _, target := range c.config.Targets[Device] {
453		arches = append(arches, target.Arch)
454	}
455	return arches
456}
457
458func (c *deviceConfig) VendorPath() string {
459	if c.config.ProductVariables.VendorPath != nil {
460		return *c.config.ProductVariables.VendorPath
461	}
462	return "vendor"
463}
464
465func (c *deviceConfig) CompileVndk() bool {
466	if c.config.ProductVariables.DeviceVndkVersion == nil {
467		return false
468	}
469	return *c.config.ProductVariables.DeviceVndkVersion == "current"
470}
471
472func (c *deviceConfig) BtConfigIncludeDir() string {
473	return String(c.config.ProductVariables.BtConfigIncludeDir)
474}
475
476func (c *deviceConfig) NativeCoverageEnabled() bool {
477	return Bool(c.config.ProductVariables.NativeCoverage)
478}
479
480func (c *deviceConfig) CoverageEnabledForPath(path string) bool {
481	coverage := false
482	if c.config.ProductVariables.CoveragePaths != nil {
483		for _, prefix := range *c.config.ProductVariables.CoveragePaths {
484			if strings.HasPrefix(path, prefix) {
485				coverage = true
486				break
487			}
488		}
489	}
490	if coverage && c.config.ProductVariables.CoverageExcludePaths != nil {
491		for _, prefix := range *c.config.ProductVariables.CoverageExcludePaths {
492			if strings.HasPrefix(path, prefix) {
493				coverage = false
494				break
495			}
496		}
497	}
498	return coverage
499}
500