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 common
16
17import (
18	"encoding/json"
19	"fmt"
20	"os"
21	"path/filepath"
22	"runtime"
23	"strings"
24	"sync"
25
26	"github.com/google/blueprint/proptools"
27)
28
29var Bool = proptools.Bool
30
31// The configuration file name
32const configFileName = "soong.config"
33const productVariablesFileName = "soong.variables"
34
35// A FileConfigurableOptions contains options which can be configured by the
36// config file. These will be included in the config struct.
37type FileConfigurableOptions struct {
38	Mega_device *bool `json:",omitempty"`
39}
40
41func (f *FileConfigurableOptions) SetDefaultConfig() {
42	*f = FileConfigurableOptions{}
43}
44
45type Config struct {
46	*config
47}
48
49// A config object represents the entire build configuration for Android.
50type config struct {
51	FileConfigurableOptions
52	ProductVariables productVariables
53
54	ConfigFileName           string
55	ProductVariablesFileName string
56
57	DeviceArches []Arch
58	HostArches   map[HostType][]Arch
59
60	srcDir   string // the path of the root source directory
61	buildDir string // the path of the build output directory
62
63	envLock   sync.Mutex
64	envDeps   map[string]string
65	envFrozen bool
66
67	inMake bool
68}
69
70type jsonConfigurable interface {
71	SetDefaultConfig()
72}
73
74func loadConfig(config *config) error {
75	err := loadFromConfigFile(&config.FileConfigurableOptions, config.ConfigFileName)
76	if err != nil {
77		return err
78	}
79
80	return loadFromConfigFile(&config.ProductVariables, config.ProductVariablesFileName)
81}
82
83// loads configuration options from a JSON file in the cwd.
84func loadFromConfigFile(configurable jsonConfigurable, filename string) error {
85	// Try to open the file
86	configFileReader, err := os.Open(filename)
87	defer configFileReader.Close()
88	if os.IsNotExist(err) {
89		// Need to create a file, so that blueprint & ninja don't get in
90		// a dependency tracking loop.
91		// Make a file-configurable-options with defaults, write it out using
92		// a json writer.
93		configurable.SetDefaultConfig()
94		err = saveToConfigFile(configurable, filename)
95		if err != nil {
96			return err
97		}
98	} else {
99		// Make a decoder for it
100		jsonDecoder := json.NewDecoder(configFileReader)
101		err = jsonDecoder.Decode(configurable)
102		if err != nil {
103			return fmt.Errorf("config file: %s did not parse correctly: "+err.Error(), filename)
104		}
105	}
106
107	// No error
108	return nil
109}
110
111func saveToConfigFile(config jsonConfigurable, filename string) error {
112	data, err := json.MarshalIndent(&config, "", "    ")
113	if err != nil {
114		return fmt.Errorf("cannot marshal config data: %s", err.Error())
115	}
116
117	configFileWriter, err := os.Create(filename)
118	if err != nil {
119		return fmt.Errorf("cannot create empty config file %s: %s\n", filename, err.Error())
120	}
121	defer configFileWriter.Close()
122
123	_, err = configFileWriter.Write(data)
124	if err != nil {
125		return fmt.Errorf("default config file: %s could not be written: %s", filename, err.Error())
126	}
127
128	_, err = configFileWriter.WriteString("\n")
129	if err != nil {
130		return fmt.Errorf("default config file: %s could not be written: %s", filename, err.Error())
131	}
132
133	return nil
134}
135
136// New creates a new Config object.  The srcDir argument specifies the path to
137// the root source directory. It also loads the config file, if found.
138func NewConfig(srcDir, buildDir string) (Config, error) {
139	// Make a config with default options
140	config := Config{
141		config: &config{
142			ConfigFileName:           filepath.Join(buildDir, configFileName),
143			ProductVariablesFileName: filepath.Join(buildDir, productVariablesFileName),
144
145			srcDir:   srcDir,
146			buildDir: buildDir,
147			envDeps:  make(map[string]string),
148		},
149	}
150
151	// Sanity check the build and source directories. This won't catch strange
152	// configurations with symlinks, but at least checks the obvious cases.
153	absBuildDir, err := filepath.Abs(buildDir)
154	if err != nil {
155		return Config{}, err
156	}
157
158	absSrcDir, err := filepath.Abs(srcDir)
159	if err != nil {
160		return Config{}, err
161	}
162
163	if strings.HasPrefix(absSrcDir, absBuildDir) {
164		return Config{}, fmt.Errorf("Build dir must not contain source directory")
165	}
166
167	// Load any configurable options from the configuration file
168	err = loadConfig(config.config)
169	if err != nil {
170		return Config{}, err
171	}
172
173	inMakeFile := filepath.Join(buildDir, ".soong.in_make")
174	if _, err := os.Stat(inMakeFile); err == nil {
175		config.inMake = true
176	}
177
178	hostArches, deviceArches, err := decodeArchProductVariables(config.ProductVariables)
179	if err != nil {
180		return Config{}, err
181	}
182
183	if Bool(config.Mega_device) {
184		deviceArches, err = decodeMegaDevice()
185		if err != nil {
186			return Config{}, err
187		}
188	}
189
190	config.HostArches = hostArches
191	config.DeviceArches = deviceArches
192
193	return config, nil
194}
195
196func (c *config) RemoveAbandonedFiles() bool {
197	return false
198}
199
200// PrebuiltOS returns the name of the host OS used in prebuilts directories
201func (c *config) PrebuiltOS() string {
202	switch runtime.GOOS {
203	case "linux":
204		return "linux-x86"
205	case "darwin":
206		return "darwin-x86"
207	default:
208		panic("Unknown GOOS")
209	}
210}
211
212// GoRoot returns the path to the root directory of the Go toolchain.
213func (c *config) GoRoot() string {
214	return fmt.Sprintf("%s/prebuilts/go/%s", c.srcDir, c.PrebuiltOS())
215}
216
217func (c *config) CpPreserveSymlinksFlags() string {
218	switch runtime.GOOS {
219	case "darwin":
220		return "-R"
221	case "linux":
222		return "-d"
223	default:
224		return ""
225	}
226}
227
228func (c *config) Getenv(key string) string {
229	var val string
230	var exists bool
231	c.envLock.Lock()
232	if val, exists = c.envDeps[key]; !exists {
233		if c.envFrozen {
234			panic("Cannot access new environment variables after envdeps are frozen")
235		}
236		val = os.Getenv(key)
237		c.envDeps[key] = val
238	}
239	c.envLock.Unlock()
240	return val
241}
242
243func (c *config) EnvDeps() map[string]string {
244	c.envLock.Lock()
245	c.envFrozen = true
246	c.envLock.Unlock()
247	return c.envDeps
248}
249
250func (c *config) EmbeddedInMake() bool {
251	return c.inMake
252}
253
254// DeviceName returns the name of the current device target
255// TODO: take an AndroidModuleContext to select the device name for multi-device builds
256func (c *config) DeviceName() string {
257	return *c.ProductVariables.DeviceName
258}
259
260func (c *config) DeviceUsesClang() bool {
261	if c.ProductVariables.DeviceUsesClang != nil {
262		return *c.ProductVariables.DeviceUsesClang
263	}
264	return true
265}
266
267func (c *config) ResourceOverlays() []SourcePath {
268	return nil
269}
270
271func (c *config) PlatformVersion() string {
272	return "M"
273}
274
275func (c *config) PlatformSdkVersion() string {
276	return "22"
277}
278
279func (c *config) BuildNumber() string {
280	return "000000"
281}
282
283func (c *config) ProductAaptConfig() []string {
284	return []string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"}
285}
286
287func (c *config) ProductAaptPreferredConfig() string {
288	return "xhdpi"
289}
290
291func (c *config) ProductAaptCharacteristics() string {
292	return "nosdcard"
293}
294
295func (c *config) DefaultAppCertificateDir(ctx PathContext) SourcePath {
296	return PathForSource(ctx, "build/target/product/security")
297}
298
299func (c *config) DefaultAppCertificate(ctx PathContext) SourcePath {
300	return c.DefaultAppCertificateDir(ctx).Join(ctx, "testkey")
301}
302
303func (c *config) AllowMissingDependencies() bool {
304	return Bool(c.ProductVariables.Allow_missing_dependencies)
305}
306
307func (c *config) SkipDeviceInstall() bool {
308	return c.EmbeddedInMake() || Bool(c.Mega_device)
309}
310