• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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