1 // Copyright 2016 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 android 16 17 import ( 18 "bytes" 19 "fmt" 20 "io/ioutil" 21 "os" 22 "strconv" 23 24 "github.com/google/blueprint/proptools" 25 ) 26 27 func init() { 28 RegisterMakeVarsProvider(pctx, androidMakeVarsProvider) 29 } 30 31 func androidMakeVarsProvider(ctx MakeVarsContext) { 32 ctx.Strict("MIN_SUPPORTED_SDK_VERSION", strconv.Itoa(ctx.Config().MinSupportedSdkVersion())) 33 } 34 35 /////////////////////////////////////////////////////////////////////////////// 36 // Interface for other packages to use to declare make variables 37 type MakeVarsContext interface { 38 Config() Config 39 DeviceConfig() DeviceConfig 40 SingletonContext() SingletonContext 41 42 // Verify the make variable matches the Soong version, fail the build 43 // if it does not. If the make variable is empty, just set it. 44 Strict(name, ninjaStr string) 45 // Check to see if the make variable matches the Soong version, warn if 46 // it does not. If the make variable is empty, just set it. 47 Check(name, ninjaStr string) 48 49 // These are equivalent to the above, but sort the make and soong 50 // variables before comparing them. They also show the unique entries 51 // in each list when displaying the difference, instead of the entire 52 // string. 53 StrictSorted(name, ninjaStr string) 54 CheckSorted(name, ninjaStr string) 55 56 // Evaluates a ninja string and returns the result. Used if more 57 // complicated modification needs to happen before giving it to Make. 58 Eval(ninjaStr string) (string, error) 59 60 // These are equivalent to Strict and Check, but do not attempt to 61 // evaluate the values before writing them to the Makefile. They can 62 // be used when all ninja variables have already been evaluated through 63 // Eval(). 64 StrictRaw(name, value string) 65 CheckRaw(name, value string) 66 } 67 68 type MakeVarsProvider func(ctx MakeVarsContext) 69 70 func RegisterMakeVarsProvider(pctx PackageContext, provider MakeVarsProvider) { 71 makeVarsProviders = append(makeVarsProviders, makeVarsProvider{pctx, provider}) 72 } 73 74 /////////////////////////////////////////////////////////////////////////////// 75 76 func init() { 77 RegisterSingletonType("makevars", makeVarsSingletonFunc) 78 } 79 80 func makeVarsSingletonFunc() Singleton { 81 return &makeVarsSingleton{} 82 } 83 84 type makeVarsSingleton struct{} 85 86 type makeVarsProvider struct { 87 pctx PackageContext 88 call MakeVarsProvider 89 } 90 91 var makeVarsProviders []makeVarsProvider 92 93 type makeVarsContext struct { 94 config Config 95 ctx SingletonContext 96 pctx PackageContext 97 vars []makeVarsVariable 98 } 99 100 var _ MakeVarsContext = &makeVarsContext{} 101 102 type makeVarsVariable struct { 103 name string 104 value string 105 sort bool 106 strict bool 107 } 108 109 func (s *makeVarsSingleton) GenerateBuildActions(ctx SingletonContext) { 110 if !ctx.Config().EmbeddedInMake() { 111 return 112 } 113 114 outFile := PathForOutput(ctx, "make_vars"+proptools.String(ctx.Config().productVariables.Make_suffix)+".mk").String() 115 116 if ctx.Failed() { 117 return 118 } 119 120 vars := []makeVarsVariable{} 121 for _, provider := range makeVarsProviders { 122 mctx := &makeVarsContext{ 123 config: ctx.Config(), 124 ctx: ctx, 125 pctx: provider.pctx, 126 } 127 128 provider.call(mctx) 129 130 vars = append(vars, mctx.vars...) 131 } 132 133 if ctx.Failed() { 134 return 135 } 136 137 outBytes := s.writeVars(vars) 138 139 if _, err := os.Stat(outFile); err == nil { 140 if data, err := ioutil.ReadFile(outFile); err == nil { 141 if bytes.Equal(data, outBytes) { 142 return 143 } 144 } 145 } 146 147 if err := ioutil.WriteFile(outFile, outBytes, 0666); err != nil { 148 ctx.Errorf(err.Error()) 149 } 150 } 151 152 func (s *makeVarsSingleton) writeVars(vars []makeVarsVariable) []byte { 153 buf := &bytes.Buffer{} 154 155 fmt.Fprintln(buf, `# Autogenerated file 156 157 # Compares SOONG_$(1) against $(1), and warns if they are not equal. 158 # 159 # If the original variable is empty, then just set it to the SOONG_ version. 160 # 161 # $(1): Name of the variable to check 162 # $(2): If not-empty, sort the values before comparing 163 # $(3): Extra snippet to run if it does not match 164 define soong-compare-var 165 ifneq ($$($(1)),) 166 my_val_make := $$(strip $(if $(2),$$(sort $$($(1))),$$($(1)))) 167 my_val_soong := $(if $(2),$$(sort $$(SOONG_$(1))),$$(SOONG_$(1))) 168 ifneq ($$(my_val_make),$$(my_val_soong)) 169 $$(warning $(1) does not match between Make and Soong:) 170 $(if $(2),$$(warning Make adds: $$(filter-out $$(my_val_soong),$$(my_val_make))),$$(warning Make : $$(my_val_make))) 171 $(if $(2),$$(warning Soong adds: $$(filter-out $$(my_val_make),$$(my_val_soong))),$$(warning Soong: $$(my_val_soong))) 172 $(3) 173 endif 174 my_val_make := 175 my_val_soong := 176 else 177 $(1) := $$(SOONG_$(1)) 178 endif 179 .KATI_READONLY := $(1) SOONG_$(1) 180 endef 181 182 my_check_failed := false 183 184 `) 185 186 // Write all the strict checks out first so that if one of them errors, 187 // we get all of the strict errors printed, but not the non-strict 188 // warnings. 189 for _, v := range vars { 190 if !v.strict { 191 continue 192 } 193 194 sort := "" 195 if v.sort { 196 sort = "true" 197 } 198 199 fmt.Fprintf(buf, "SOONG_%s := %s\n", v.name, v.value) 200 fmt.Fprintf(buf, "$(eval $(call soong-compare-var,%s,%s,my_check_failed := true))\n\n", v.name, sort) 201 } 202 203 fmt.Fprintln(buf, ` 204 ifneq ($(my_check_failed),false) 205 $(error Soong variable check failed) 206 endif 207 my_check_failed := 208 209 210 `) 211 212 for _, v := range vars { 213 if v.strict { 214 continue 215 } 216 217 sort := "" 218 if v.sort { 219 sort = "true" 220 } 221 222 fmt.Fprintf(buf, "SOONG_%s := %s\n", v.name, v.value) 223 fmt.Fprintf(buf, "$(eval $(call soong-compare-var,%s,%s))\n\n", v.name, sort) 224 } 225 226 fmt.Fprintln(buf, "\nsoong-compare-var :=") 227 228 return buf.Bytes() 229 } 230 231 func (c *makeVarsContext) Config() Config { 232 return c.config 233 } 234 235 func (c *makeVarsContext) DeviceConfig() DeviceConfig { 236 return DeviceConfig{c.config.deviceConfig} 237 } 238 239 func (c *makeVarsContext) SingletonContext() SingletonContext { 240 return c.ctx 241 } 242 243 func (c *makeVarsContext) Eval(ninjaStr string) (string, error) { 244 return c.ctx.Eval(c.pctx, ninjaStr) 245 } 246 247 func (c *makeVarsContext) addVariableRaw(name, value string, strict, sort bool) { 248 c.vars = append(c.vars, makeVarsVariable{ 249 name: name, 250 value: value, 251 strict: strict, 252 sort: sort, 253 }) 254 } 255 256 func (c *makeVarsContext) addVariable(name, ninjaStr string, strict, sort bool) { 257 value, err := c.Eval(ninjaStr) 258 if err != nil { 259 c.ctx.Errorf(err.Error()) 260 } 261 c.addVariableRaw(name, value, strict, sort) 262 } 263 264 func (c *makeVarsContext) Strict(name, ninjaStr string) { 265 c.addVariable(name, ninjaStr, true, false) 266 } 267 func (c *makeVarsContext) StrictSorted(name, ninjaStr string) { 268 c.addVariable(name, ninjaStr, true, true) 269 } 270 func (c *makeVarsContext) StrictRaw(name, value string) { 271 c.addVariableRaw(name, value, true, false) 272 } 273 274 func (c *makeVarsContext) Check(name, ninjaStr string) { 275 c.addVariable(name, ninjaStr, false, false) 276 } 277 func (c *makeVarsContext) CheckSorted(name, ninjaStr string) { 278 c.addVariable(name, ninjaStr, false, true) 279 } 280 func (c *makeVarsContext) CheckRaw(name, value string) { 281 c.addVariableRaw(name, value, false, false) 282 } 283