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