Home | History | Annotate | Download | only in common
      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