blob: ad4f6d46a63b65560ae98cb39a02687d720abaff [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
15package common
16
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)
47}
48
49type MakeVarsProvider func(ctx MakeVarsContext)
50
51func RegisterMakeVarsProvider(pctx blueprint.PackageContext, provider MakeVarsProvider) {
52 makeVarsProviders = append(makeVarsProviders, makeVarsProvider{pctx, provider})
53}
54
55///////////////////////////////////////////////////////////////////////////////
56
57func init() {
58 soong.RegisterSingletonType("makevars", makeVarsSingletonFunc)
59}
60
61func makeVarsSingletonFunc() blueprint.Singleton {
62 return &makeVarsSingleton{}
63}
64
65type makeVarsSingleton struct{}
66
67type makeVarsProvider struct {
68 pctx blueprint.PackageContext
69 call MakeVarsProvider
70}
71
72var makeVarsProviders []makeVarsProvider
73
74type makeVarsContext struct {
75 config Config
76 ctx blueprint.SingletonContext
77 pctx blueprint.PackageContext
78 vars []makeVarsVariable
79}
80
81var _ MakeVarsContext = &makeVarsContext{}
82
83type makeVarsVariable struct {
84 name string
85 value string
86 sort bool
87 strict bool
88}
89
90func (s *makeVarsSingleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
91 config := ctx.Config().(Config)
92
93 if !config.EmbeddedInMake() {
94 return
95 }
96
97 outFile := PathForOutput(ctx, "make_vars"+proptools.String(config.ProductVariables.Make_suffix)+".mk").String()
98
99 if ctx.Failed() {
100 return
101 }
102
103 vars := []makeVarsVariable{}
104 for _, provider := range makeVarsProviders {
105 mctx := &makeVarsContext{
106 config: config,
107 ctx: ctx,
108 pctx: provider.pctx,
109 }
110
111 provider.call(mctx)
112
113 vars = append(vars, mctx.vars...)
114 }
115
116 if ctx.Failed() {
117 return
118 }
119
120 outBytes := s.writeVars(vars)
121
122 if _, err := os.Stat(outFile); err == nil {
123 if data, err := ioutil.ReadFile(outFile); err == nil {
124 if bytes.Equal(data, outBytes) {
125 return
126 }
127 }
128 }
129
130 if err := ioutil.WriteFile(outFile, outBytes, 0666); err != nil {
131 ctx.Errorf(err.Error())
132 }
133}
134
135func (s *makeVarsSingleton) writeVars(vars []makeVarsVariable) []byte {
136 buf := &bytes.Buffer{}
137
138 fmt.Fprintln(buf, `# Autogenerated file
139
140# Compares SOONG_$(1) against $(1), and warns if they are not equal.
141#
142# If the original variable is empty, then just set it to the SOONG_ version.
143#
144# $(1): Name of the variable to check
145# $(2): If not-empty, sort the values before comparing
146# $(3): Extra snippet to run if it does not match
147define soong-compare-var
148ifneq ($$($(1)),)
149 my_val_make := $(if $(2),$$(sort $$($(1))),$$($(1)))
150 my_val_soong := $(if $(2),$$(sort $$(SOONG_$(1))),$$(SOONG_$(1)))
151 ifneq ($$(my_val_make),$$(my_val_soong))
152 $$(warning $(1) does not match between Make and Soong:)
153 $(if $(2),$$(warning Make adds: $$(filter-out $$(my_val_soong),$$(my_val_make))),$$(warning Make : $$(my_val_make)))
154 $(if $(2),$$(warning Soong adds: $$(filter-out $$(my_val_make),$$(my_val_soong))),$$(warning Soong: $$(my_val_soong)))
155 $(3)
156 endif
157 my_val_make :=
158 my_val_soong :=
159else
160 $(1) := $$(SOONG_$(1))
161endif
162endef
163
164my_check_failed := false
165
166`)
167
168 // Write all the strict checks out first so that if one of them errors,
169 // we get all of the strict errors printed, but not the non-strict
170 // warnings.
171 for _, v := range vars {
172 if !v.strict {
173 continue
174 }
175
176 sort := ""
177 if v.sort {
178 sort = "true"
179 }
180
181 fmt.Fprintf(buf, "SOONG_%s := %s\n", v.name, v.value)
182 fmt.Fprintf(buf, "$(eval $(call soong-compare-var,%s,%s,my_check_failed := true))\n\n", v.name, sort)
183 }
184
185 fmt.Fprintln(buf, `
186ifneq ($(my_check_failed),false)
187 $(error Soong variable check failed)
188endif
189my_check_failed :=
190
191
192`)
193
194 for _, v := range vars {
195 if v.strict {
196 continue
197 }
198
199 sort := ""
200 if v.sort {
201 sort = "true"
202 }
203
204 fmt.Fprintf(buf, "SOONG_%s := %s\n", v.name, v.value)
205 fmt.Fprintf(buf, "$(eval $(call soong-compare-var,%s,%s))\n\n", v.name, sort)
206 }
207
208 fmt.Fprintln(buf, "\nsoong-compare-var :=")
209
210 return buf.Bytes()
211}
212
213func (c *makeVarsContext) Config() Config {
214 return c.config
215}
216
217func (c *makeVarsContext) addVariable(name, ninjaStr string, strict, sort bool) {
218 value, err := c.ctx.Eval(c.pctx, ninjaStr)
219 if err != nil {
220 c.ctx.Errorf(err.Error())
221 }
222 c.vars = append(c.vars, makeVarsVariable{
223 name: name,
224 value: value,
225 strict: strict,
226 sort: sort,
227 })
228}
229
230func (c *makeVarsContext) Strict(name, ninjaStr string) {
231 c.addVariable(name, ninjaStr, true, false)
232}
233func (c *makeVarsContext) StrictSorted(name, ninjaStr string) {
234 c.addVariable(name, ninjaStr, true, true)
235}
236
237func (c *makeVarsContext) Check(name, ninjaStr string) {
238 c.addVariable(name, ninjaStr, false, false)
239}
240func (c *makeVarsContext) CheckSorted(name, ninjaStr string) {
241 c.addVariable(name, ninjaStr, false, true)
242}