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 15 package common 16 17 import ( 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 29 var Bool = proptools.Bool 30 31 // The configuration file name 32 const configFileName = "soong.config" 33 const 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. 37 type FileConfigurableOptions struct { 38 Mega_device *bool `json:",omitempty"` 39 } 40 41 func (f *FileConfigurableOptions) SetDefaultConfig() { 42 *f = FileConfigurableOptions{} 43 } 44 45 type Config struct { 46 *config 47 } 48 49 // A config object represents the entire build configuration for Android. 50 type 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 70 type jsonConfigurable interface { 71 SetDefaultConfig() 72 } 73 74 func 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. 84 func 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 111 func 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. 138 func 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 196 func (c *config) RemoveAbandonedFiles() bool { 197 return false 198 } 199 200 // PrebuiltOS returns the name of the host OS used in prebuilts directories 201 func (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. 213 func (c *config) GoRoot() string { 214 return fmt.Sprintf("%s/prebuilts/go/%s", c.srcDir, c.PrebuiltOS()) 215 } 216 217 func (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 228 func (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 243 func (c *config) EnvDeps() map[string]string { 244 c.envLock.Lock() 245 c.envFrozen = true 246 c.envLock.Unlock() 247 return c.envDeps 248 } 249 250 func (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 256 func (c *config) DeviceName() string { 257 return *c.ProductVariables.DeviceName 258 } 259 260 func (c *config) DeviceUsesClang() bool { 261 if c.ProductVariables.DeviceUsesClang != nil { 262 return *c.ProductVariables.DeviceUsesClang 263 } 264 return true 265 } 266 267 func (c *config) ResourceOverlays() []SourcePath { 268 return nil 269 } 270 271 func (c *config) PlatformVersion() string { 272 return "M" 273 } 274 275 func (c *config) PlatformSdkVersion() string { 276 return "22" 277 } 278 279 func (c *config) BuildNumber() string { 280 return "000000" 281 } 282 283 func (c *config) ProductAaptConfig() []string { 284 return []string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"} 285 } 286 287 func (c *config) ProductAaptPreferredConfig() string { 288 return "xhdpi" 289 } 290 291 func (c *config) ProductAaptCharacteristics() string { 292 return "nosdcard" 293 } 294 295 func (c *config) DefaultAppCertificateDir(ctx PathContext) SourcePath { 296 return PathForSource(ctx, "build/target/product/security") 297 } 298 299 func (c *config) DefaultAppCertificate(ctx PathContext) SourcePath { 300 return c.DefaultAppCertificateDir(ctx).Join(ctx, "testkey") 301 } 302 303 func (c *config) AllowMissingDependencies() bool { 304 return Bool(c.ProductVariables.Allow_missing_dependencies) 305 } 306 307 func (c *config) SkipDeviceInstall() bool { 308 return c.EmbeddedInMake() || Bool(c.Mega_device) 309 } 310