1// Copyright 2015 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"
21	"io/ioutil"
22	"os"
23	"path/filepath"
24	"sort"
25	"strings"
26
27	"github.com/google/blueprint"
28	"github.com/google/blueprint/bootstrap"
29)
30
31func init() {
32	RegisterAndroidMkBuildComponents(InitRegistrationContext)
33}
34
35func RegisterAndroidMkBuildComponents(ctx RegistrationContext) {
36	ctx.RegisterSingletonType("androidmk", AndroidMkSingleton)
37}
38
39// Deprecated: consider using AndroidMkEntriesProvider instead, especially if you're not going to
40// use the Custom function.
41type AndroidMkDataProvider interface {
42	AndroidMk() AndroidMkData
43	BaseModuleName() string
44}
45
46type AndroidMkData struct {
47	Class           string
48	SubName         string
49	DistFiles       TaggedDistFiles
50	OutputFile      OptionalPath
51	Disabled        bool
52	Include         string
53	Required        []string
54	Host_required   []string
55	Target_required []string
56
57	Custom func(w io.Writer, name, prefix, moduleDir string, data AndroidMkData)
58
59	Extra []AndroidMkExtraFunc
60
61	Entries AndroidMkEntries
62}
63
64type AndroidMkExtraFunc func(w io.Writer, outputFile Path)
65
66// Allows modules to customize their Android*.mk output.
67type AndroidMkEntriesProvider interface {
68	AndroidMkEntries() []AndroidMkEntries
69	BaseModuleName() string
70}
71
72type AndroidMkEntries struct {
73	Class           string
74	SubName         string
75	DistFiles       TaggedDistFiles
76	OutputFile      OptionalPath
77	Disabled        bool
78	Include         string
79	Required        []string
80	Host_required   []string
81	Target_required []string
82
83	header bytes.Buffer
84	footer bytes.Buffer
85
86	ExtraEntries []AndroidMkExtraEntriesFunc
87	ExtraFooters []AndroidMkExtraFootersFunc
88
89	EntryMap   map[string][]string
90	entryOrder []string
91}
92
93type AndroidMkExtraEntriesFunc func(entries *AndroidMkEntries)
94type AndroidMkExtraFootersFunc func(w io.Writer, name, prefix, moduleDir string, entries *AndroidMkEntries)
95
96func (a *AndroidMkEntries) SetString(name, value string) {
97	if _, ok := a.EntryMap[name]; !ok {
98		a.entryOrder = append(a.entryOrder, name)
99	}
100	a.EntryMap[name] = []string{value}
101}
102
103func (a *AndroidMkEntries) SetPath(name string, path Path) {
104	if _, ok := a.EntryMap[name]; !ok {
105		a.entryOrder = append(a.entryOrder, name)
106	}
107	a.EntryMap[name] = []string{path.String()}
108}
109
110func (a *AndroidMkEntries) SetOptionalPath(name string, path OptionalPath) {
111	if path.Valid() {
112		a.SetPath(name, path.Path())
113	}
114}
115
116func (a *AndroidMkEntries) AddPath(name string, path Path) {
117	if _, ok := a.EntryMap[name]; !ok {
118		a.entryOrder = append(a.entryOrder, name)
119	}
120	a.EntryMap[name] = append(a.EntryMap[name], path.String())
121}
122
123func (a *AndroidMkEntries) AddOptionalPath(name string, path OptionalPath) {
124	if path.Valid() {
125		a.AddPath(name, path.Path())
126	}
127}
128
129func (a *AndroidMkEntries) SetPaths(name string, paths Paths) {
130	if _, ok := a.EntryMap[name]; !ok {
131		a.entryOrder = append(a.entryOrder, name)
132	}
133	a.EntryMap[name] = paths.Strings()
134}
135
136func (a *AndroidMkEntries) SetOptionalPaths(name string, paths Paths) {
137	if len(paths) > 0 {
138		a.SetPaths(name, paths)
139	}
140}
141
142func (a *AndroidMkEntries) AddPaths(name string, paths Paths) {
143	if _, ok := a.EntryMap[name]; !ok {
144		a.entryOrder = append(a.entryOrder, name)
145	}
146	a.EntryMap[name] = append(a.EntryMap[name], paths.Strings()...)
147}
148
149func (a *AndroidMkEntries) SetBoolIfTrue(name string, flag bool) {
150	if flag {
151		if _, ok := a.EntryMap[name]; !ok {
152			a.entryOrder = append(a.entryOrder, name)
153		}
154		a.EntryMap[name] = []string{"true"}
155	}
156}
157
158func (a *AndroidMkEntries) SetBool(name string, flag bool) {
159	if _, ok := a.EntryMap[name]; !ok {
160		a.entryOrder = append(a.entryOrder, name)
161	}
162	if flag {
163		a.EntryMap[name] = []string{"true"}
164	} else {
165		a.EntryMap[name] = []string{"false"}
166	}
167}
168
169func (a *AndroidMkEntries) AddStrings(name string, value ...string) {
170	if len(value) == 0 {
171		return
172	}
173	if _, ok := a.EntryMap[name]; !ok {
174		a.entryOrder = append(a.entryOrder, name)
175	}
176	a.EntryMap[name] = append(a.EntryMap[name], value...)
177}
178
179// Compute the list of Make strings to declare phone goals and dist-for-goals
180// calls from the module's dist and dists properties.
181func (a *AndroidMkEntries) GetDistForGoals(mod blueprint.Module) []string {
182	amod := mod.(Module).base()
183	name := amod.BaseModuleName()
184
185	var ret []string
186
187	availableTaggedDists := TaggedDistFiles{}
188	if a.DistFiles != nil {
189		availableTaggedDists = a.DistFiles
190	} else if a.OutputFile.Valid() {
191		availableTaggedDists = MakeDefaultDistFiles(a.OutputFile.Path())
192	}
193
194	// Iterate over this module's dist structs, merged from the dist and dists properties.
195	for _, dist := range amod.Dists() {
196		// Get the list of goals this dist should be enabled for. e.g. sdk, droidcore
197		goals := strings.Join(dist.Targets, " ")
198
199		// Get the tag representing the output files to be dist'd. e.g. ".jar", ".proguard_map"
200		var tag string
201		if dist.Tag == nil {
202			// If the dist struct does not specify a tag, use the default output files tag.
203			tag = ""
204		} else {
205			tag = *dist.Tag
206		}
207
208		// Get the paths of the output files to be dist'd, represented by the tag.
209		// Can be an empty list.
210		tagPaths := availableTaggedDists[tag]
211		if len(tagPaths) == 0 {
212			// Nothing to dist for this tag, continue to the next dist.
213			continue
214		}
215
216		if len(tagPaths) > 1 && (dist.Dest != nil || dist.Suffix != nil) {
217			errorMessage := "Cannot apply dest/suffix for more than one dist " +
218				"file for %s goals in module %s. The list of dist files, " +
219				"which should have a single element, is:\n%s"
220			panic(fmt.Errorf(errorMessage, goals, name, tagPaths))
221		}
222
223		ret = append(ret, fmt.Sprintf(".PHONY: %s\n", goals))
224
225		// Create dist-for-goals calls for each path in the dist'd files.
226		for _, path := range tagPaths {
227			// It's possible that the Path is nil from errant modules. Be defensive here.
228			if path == nil {
229				tagName := "default" // for error message readability
230				if dist.Tag != nil {
231					tagName = *dist.Tag
232				}
233				panic(fmt.Errorf("Dist file should not be nil for the %s tag in %s", tagName, name))
234			}
235
236			dest := filepath.Base(path.String())
237
238			if dist.Dest != nil {
239				var err error
240				if dest, err = validateSafePath(*dist.Dest); err != nil {
241					// This was checked in ModuleBase.GenerateBuildActions
242					panic(err)
243				}
244			}
245
246			if dist.Suffix != nil {
247				ext := filepath.Ext(dest)
248				suffix := *dist.Suffix
249				dest = strings.TrimSuffix(dest, ext) + suffix + ext
250			}
251
252			if dist.Dir != nil {
253				var err error
254				if dest, err = validateSafePath(*dist.Dir, dest); err != nil {
255					// This was checked in ModuleBase.GenerateBuildActions
256					panic(err)
257				}
258			}
259
260			ret = append(
261				ret,
262				fmt.Sprintf("$(call dist-for-goals,%s,%s:%s)\n", goals, path.String(), dest))
263		}
264	}
265
266	return ret
267}
268
269func (a *AndroidMkEntries) fillInEntries(config Config, bpPath string, mod blueprint.Module) {
270	a.EntryMap = make(map[string][]string)
271	amod := mod.(Module).base()
272	name := amod.BaseModuleName()
273
274	if a.Include == "" {
275		a.Include = "$(BUILD_PREBUILT)"
276	}
277	a.Required = append(a.Required, amod.commonProperties.Required...)
278	a.Host_required = append(a.Host_required, amod.commonProperties.Host_required...)
279	a.Target_required = append(a.Target_required, amod.commonProperties.Target_required...)
280
281	for _, distString := range a.GetDistForGoals(mod) {
282		fmt.Fprintf(&a.header, distString)
283	}
284
285	fmt.Fprintln(&a.header, "\ninclude $(CLEAR_VARS)")
286
287	// Collect make variable assignment entries.
288	a.SetString("LOCAL_PATH", filepath.Dir(bpPath))
289	a.SetString("LOCAL_MODULE", name+a.SubName)
290	a.SetString("LOCAL_MODULE_CLASS", a.Class)
291	a.SetString("LOCAL_PREBUILT_MODULE_FILE", a.OutputFile.String())
292	a.AddStrings("LOCAL_REQUIRED_MODULES", a.Required...)
293	a.AddStrings("LOCAL_HOST_REQUIRED_MODULES", a.Host_required...)
294	a.AddStrings("LOCAL_TARGET_REQUIRED_MODULES", a.Target_required...)
295
296	if am, ok := mod.(ApexModule); ok {
297		a.SetBoolIfTrue("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", am.NotAvailableForPlatform())
298	}
299
300	archStr := amod.Arch().ArchType.String()
301	host := false
302	switch amod.Os().Class {
303	case Host:
304		// Make cannot identify LOCAL_MODULE_HOST_ARCH:= common.
305		if amod.Arch().ArchType != Common {
306			a.SetString("LOCAL_MODULE_HOST_ARCH", archStr)
307		}
308		host = true
309	case HostCross:
310		// Make cannot identify LOCAL_MODULE_HOST_CROSS_ARCH:= common.
311		if amod.Arch().ArchType != Common {
312			a.SetString("LOCAL_MODULE_HOST_CROSS_ARCH", archStr)
313		}
314		host = true
315	case Device:
316		// Make cannot identify LOCAL_MODULE_TARGET_ARCH:= common.
317		if amod.Arch().ArchType != Common {
318			if amod.Target().NativeBridge {
319				hostArchStr := amod.Target().NativeBridgeHostArchName
320				if hostArchStr != "" {
321					a.SetString("LOCAL_MODULE_TARGET_ARCH", hostArchStr)
322				}
323			} else {
324				a.SetString("LOCAL_MODULE_TARGET_ARCH", archStr)
325			}
326		}
327
328		a.AddStrings("LOCAL_INIT_RC", amod.commonProperties.Init_rc...)
329		a.AddStrings("LOCAL_VINTF_FRAGMENTS", amod.commonProperties.Vintf_fragments...)
330		a.SetBoolIfTrue("LOCAL_PROPRIETARY_MODULE", Bool(amod.commonProperties.Proprietary))
331		if Bool(amod.commonProperties.Vendor) || Bool(amod.commonProperties.Soc_specific) {
332			a.SetString("LOCAL_VENDOR_MODULE", "true")
333		}
334		a.SetBoolIfTrue("LOCAL_ODM_MODULE", Bool(amod.commonProperties.Device_specific))
335		a.SetBoolIfTrue("LOCAL_PRODUCT_MODULE", Bool(amod.commonProperties.Product_specific))
336		a.SetBoolIfTrue("LOCAL_SYSTEM_EXT_MODULE", Bool(amod.commonProperties.System_ext_specific))
337		if amod.commonProperties.Owner != nil {
338			a.SetString("LOCAL_MODULE_OWNER", *amod.commonProperties.Owner)
339		}
340	}
341
342	if len(amod.noticeFiles) > 0 {
343		a.SetString("LOCAL_NOTICE_FILE", strings.Join(amod.noticeFiles.Strings(), " "))
344	}
345
346	if host {
347		makeOs := amod.Os().String()
348		if amod.Os() == Linux || amod.Os() == LinuxBionic {
349			makeOs = "linux"
350		}
351		a.SetString("LOCAL_MODULE_HOST_OS", makeOs)
352		a.SetString("LOCAL_IS_HOST_MODULE", "true")
353	}
354
355	prefix := ""
356	if amod.ArchSpecific() {
357		switch amod.Os().Class {
358		case Host:
359			prefix = "HOST_"
360		case HostCross:
361			prefix = "HOST_CROSS_"
362		case Device:
363			prefix = "TARGET_"
364
365		}
366
367		if amod.Arch().ArchType != config.Targets[amod.Os()][0].Arch.ArchType {
368			prefix = "2ND_" + prefix
369		}
370	}
371	for _, extra := range a.ExtraEntries {
372		extra(a)
373	}
374
375	// Write to footer.
376	fmt.Fprintln(&a.footer, "include "+a.Include)
377	blueprintDir := filepath.Dir(bpPath)
378	for _, footerFunc := range a.ExtraFooters {
379		footerFunc(&a.footer, name, prefix, blueprintDir, a)
380	}
381}
382
383func (a *AndroidMkEntries) write(w io.Writer) {
384	if a.Disabled {
385		return
386	}
387
388	if !a.OutputFile.Valid() {
389		return
390	}
391
392	w.Write(a.header.Bytes())
393	for _, name := range a.entryOrder {
394		fmt.Fprintln(w, name+" := "+strings.Join(a.EntryMap[name], " "))
395	}
396	w.Write(a.footer.Bytes())
397}
398
399func (a *AndroidMkEntries) FooterLinesForTests() []string {
400	return strings.Split(string(a.footer.Bytes()), "\n")
401}
402
403func AndroidMkSingleton() Singleton {
404	return &androidMkSingleton{}
405}
406
407type androidMkSingleton struct{}
408
409func (c *androidMkSingleton) GenerateBuildActions(ctx SingletonContext) {
410	if !ctx.Config().EmbeddedInMake() {
411		return
412	}
413
414	var androidMkModulesList []blueprint.Module
415
416	ctx.VisitAllModulesBlueprint(func(module blueprint.Module) {
417		androidMkModulesList = append(androidMkModulesList, module)
418	})
419
420	sort.SliceStable(androidMkModulesList, func(i, j int) bool {
421		return ctx.ModuleName(androidMkModulesList[i]) < ctx.ModuleName(androidMkModulesList[j])
422	})
423
424	transMk := PathForOutput(ctx, "Android"+String(ctx.Config().productVariables.Make_suffix)+".mk")
425	if ctx.Failed() {
426		return
427	}
428
429	err := translateAndroidMk(ctx, absolutePath(transMk.String()), androidMkModulesList)
430	if err != nil {
431		ctx.Errorf(err.Error())
432	}
433
434	ctx.Build(pctx, BuildParams{
435		Rule:   blueprint.Phony,
436		Output: transMk,
437	})
438}
439
440func translateAndroidMk(ctx SingletonContext, mkFile string, mods []blueprint.Module) error {
441	buf := &bytes.Buffer{}
442
443	fmt.Fprintln(buf, "LOCAL_MODULE_MAKEFILE := $(lastword $(MAKEFILE_LIST))")
444
445	type_stats := make(map[string]int)
446	for _, mod := range mods {
447		err := translateAndroidMkModule(ctx, buf, mod)
448		if err != nil {
449			os.Remove(mkFile)
450			return err
451		}
452
453		if amod, ok := mod.(Module); ok && ctx.PrimaryModule(amod) == amod {
454			type_stats[ctx.ModuleType(amod)] += 1
455		}
456	}
457
458	keys := []string{}
459	fmt.Fprintln(buf, "\nSTATS.SOONG_MODULE_TYPE :=")
460	for k := range type_stats {
461		keys = append(keys, k)
462	}
463	sort.Strings(keys)
464	for _, mod_type := range keys {
465		fmt.Fprintln(buf, "STATS.SOONG_MODULE_TYPE +=", mod_type)
466		fmt.Fprintf(buf, "STATS.SOONG_MODULE_TYPE.%s := %d\n", mod_type, type_stats[mod_type])
467	}
468
469	// Don't write to the file if it hasn't changed
470	if _, err := os.Stat(absolutePath(mkFile)); !os.IsNotExist(err) {
471		if data, err := ioutil.ReadFile(absolutePath(mkFile)); err == nil {
472			matches := buf.Len() == len(data)
473
474			if matches {
475				for i, value := range buf.Bytes() {
476					if value != data[i] {
477						matches = false
478						break
479					}
480				}
481			}
482
483			if matches {
484				return nil
485			}
486		}
487	}
488
489	return ioutil.WriteFile(absolutePath(mkFile), buf.Bytes(), 0666)
490}
491
492func translateAndroidMkModule(ctx SingletonContext, w io.Writer, mod blueprint.Module) error {
493	defer func() {
494		if r := recover(); r != nil {
495			panic(fmt.Errorf("%s in translateAndroidMkModule for module %s variant %s",
496				r, ctx.ModuleName(mod), ctx.ModuleSubDir(mod)))
497		}
498	}()
499
500	switch x := mod.(type) {
501	case AndroidMkDataProvider:
502		return translateAndroidModule(ctx, w, mod, x)
503	case bootstrap.GoBinaryTool:
504		return translateGoBinaryModule(ctx, w, mod, x)
505	case AndroidMkEntriesProvider:
506		return translateAndroidMkEntriesModule(ctx, w, mod, x)
507	default:
508		return nil
509	}
510}
511
512func translateGoBinaryModule(ctx SingletonContext, w io.Writer, mod blueprint.Module,
513	goBinary bootstrap.GoBinaryTool) error {
514
515	name := ctx.ModuleName(mod)
516	fmt.Fprintln(w, ".PHONY:", name)
517	fmt.Fprintln(w, name+":", goBinary.InstallPath())
518	fmt.Fprintln(w, "")
519
520	return nil
521}
522
523func (data *AndroidMkData) fillInData(config Config, bpPath string, mod blueprint.Module) {
524	// Get the preamble content through AndroidMkEntries logic.
525	data.Entries = AndroidMkEntries{
526		Class:           data.Class,
527		SubName:         data.SubName,
528		DistFiles:       data.DistFiles,
529		OutputFile:      data.OutputFile,
530		Disabled:        data.Disabled,
531		Include:         data.Include,
532		Required:        data.Required,
533		Host_required:   data.Host_required,
534		Target_required: data.Target_required,
535	}
536	data.Entries.fillInEntries(config, bpPath, mod)
537
538	// copy entries back to data since it is used in Custom
539	data.Required = data.Entries.Required
540	data.Host_required = data.Entries.Host_required
541	data.Target_required = data.Entries.Target_required
542}
543
544func translateAndroidModule(ctx SingletonContext, w io.Writer, mod blueprint.Module,
545	provider AndroidMkDataProvider) error {
546
547	amod := mod.(Module).base()
548	if shouldSkipAndroidMkProcessing(amod) {
549		return nil
550	}
551
552	data := provider.AndroidMk()
553	if data.Include == "" {
554		data.Include = "$(BUILD_PREBUILT)"
555	}
556
557	data.fillInData(ctx.Config(), ctx.BlueprintFile(mod), mod)
558
559	prefix := ""
560	if amod.ArchSpecific() {
561		switch amod.Os().Class {
562		case Host:
563			prefix = "HOST_"
564		case HostCross:
565			prefix = "HOST_CROSS_"
566		case Device:
567			prefix = "TARGET_"
568
569		}
570
571		if amod.Arch().ArchType != ctx.Config().Targets[amod.Os()][0].Arch.ArchType {
572			prefix = "2ND_" + prefix
573		}
574	}
575
576	name := provider.BaseModuleName()
577	blueprintDir := filepath.Dir(ctx.BlueprintFile(mod))
578
579	if data.Custom != nil {
580		data.Custom(w, name, prefix, blueprintDir, data)
581	} else {
582		WriteAndroidMkData(w, data)
583	}
584
585	return nil
586}
587
588func WriteAndroidMkData(w io.Writer, data AndroidMkData) {
589	if data.Disabled {
590		return
591	}
592
593	if !data.OutputFile.Valid() {
594		return
595	}
596
597	// write preamble via Entries
598	data.Entries.footer = bytes.Buffer{}
599	data.Entries.write(w)
600
601	for _, extra := range data.Extra {
602		extra(w, data.OutputFile.Path())
603	}
604
605	fmt.Fprintln(w, "include "+data.Include)
606}
607
608func translateAndroidMkEntriesModule(ctx SingletonContext, w io.Writer, mod blueprint.Module,
609	provider AndroidMkEntriesProvider) error {
610	if shouldSkipAndroidMkProcessing(mod.(Module).base()) {
611		return nil
612	}
613
614	for _, entries := range provider.AndroidMkEntries() {
615		entries.fillInEntries(ctx.Config(), ctx.BlueprintFile(mod), mod)
616		entries.write(w)
617	}
618
619	return nil
620}
621
622func shouldSkipAndroidMkProcessing(module *ModuleBase) bool {
623	if !module.commonProperties.NamespaceExportedToMake {
624		// TODO(jeffrygaston) do we want to validate that there are no modules being
625		// exported to Kati that depend on this module?
626		return true
627	}
628
629	return !module.Enabled() ||
630		module.commonProperties.SkipInstall ||
631		// Make does not understand LinuxBionic
632		module.Os() == LinuxBionic
633}
634