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