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