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