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 common
16
17import (
18	"bytes"
19	"fmt"
20	"io"
21	"io/ioutil"
22	"os"
23	"path/filepath"
24	"sort"
25
26	"android/soong"
27
28	"github.com/google/blueprint"
29)
30
31func init() {
32	soong.RegisterSingletonType("androidmk", AndroidMkSingleton)
33}
34
35type AndroidMkDataProvider interface {
36	AndroidMk() (AndroidMkData, error)
37}
38
39type AndroidMkData struct {
40	Class      string
41	SubName    string
42	OutputFile OptionalPath
43	Disabled   bool
44
45	Custom func(w io.Writer, name, prefix string) error
46
47	Extra []func(w io.Writer, outputFile Path) error
48}
49
50func AndroidMkSingleton() blueprint.Singleton {
51	return &androidMkSingleton{}
52}
53
54type androidMkSingleton struct{}
55
56func (c *androidMkSingleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
57	if !ctx.Config().(Config).EmbeddedInMake() {
58		return
59	}
60
61	ctx.SetNinjaBuildDir(pctx, filepath.Join(ctx.Config().(Config).buildDir, ".."))
62
63	var androidMkModulesList []AndroidModule
64
65	ctx.VisitAllModules(func(module blueprint.Module) {
66		if amod, ok := module.(AndroidModule); ok {
67			androidMkModulesList = append(androidMkModulesList, amod)
68		}
69	})
70
71	sort.Sort(AndroidModulesByName{androidMkModulesList, ctx})
72
73	transMk := PathForOutput(ctx, "Android.mk")
74	if ctx.Failed() {
75		return
76	}
77
78	err := translateAndroidMk(ctx, transMk.String(), androidMkModulesList)
79	if err != nil {
80		ctx.Errorf(err.Error())
81	}
82
83	ctx.Build(pctx, blueprint.BuildParams{
84		Rule:     blueprint.Phony,
85		Outputs:  []string{transMk.String()},
86		Optional: true,
87	})
88}
89
90func translateAndroidMk(ctx blueprint.SingletonContext, mkFile string, mods []AndroidModule) error {
91	buf := &bytes.Buffer{}
92
93	fmt.Fprintln(buf, "LOCAL_PATH := $(TOP)")
94	fmt.Fprintln(buf, "LOCAL_MODULE_MAKEFILE := $(lastword $(MAKEFILE_LIST))")
95
96	for _, mod := range mods {
97		err := translateAndroidMkModule(ctx, buf, mod)
98		if err != nil {
99			os.Remove(mkFile)
100			return err
101		}
102	}
103
104	// Don't write to the file if it hasn't changed
105	if _, err := os.Stat(mkFile); !os.IsNotExist(err) {
106		if data, err := ioutil.ReadFile(mkFile); err == nil {
107			matches := buf.Len() == len(data)
108
109			if matches {
110				for i, value := range buf.Bytes() {
111					if value != data[i] {
112						matches = false
113						break
114					}
115				}
116			}
117
118			if matches {
119				return nil
120			}
121		}
122	}
123
124	return ioutil.WriteFile(mkFile, buf.Bytes(), 0666)
125}
126
127func translateAndroidMkModule(ctx blueprint.SingletonContext, w io.Writer, mod blueprint.Module) error {
128	name := ctx.ModuleName(mod)
129
130	provider, ok := mod.(AndroidMkDataProvider)
131	if !ok {
132		return nil
133	}
134
135	amod := mod.(AndroidModule).base()
136	data, err := provider.AndroidMk()
137	if err != nil {
138		return err
139	}
140
141	if !amod.Enabled() {
142		return err
143	}
144
145	if data.SubName != "" {
146		name += "_" + data.SubName
147	}
148
149	hostCross := false
150	if amod.Host() && amod.HostType() != CurrentHostType() {
151		hostCross = true
152	}
153
154	if data.Custom != nil {
155		prefix := ""
156		if amod.Host() {
157			if hostCross {
158				prefix = "HOST_CROSS_"
159			} else {
160				prefix = "HOST_"
161			}
162			if amod.Arch().ArchType != ctx.Config().(Config).HostArches[amod.HostType()][0].ArchType {
163				prefix = "2ND_" + prefix
164			}
165		} else {
166			prefix = "TARGET_"
167			if amod.Arch().ArchType != ctx.Config().(Config).DeviceArches[0].ArchType {
168				prefix = "2ND_" + prefix
169			}
170		}
171
172		return data.Custom(w, name, prefix)
173	}
174
175	if data.Disabled {
176		return nil
177	}
178
179	if !data.OutputFile.Valid() {
180		return err
181	}
182
183	fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
184	fmt.Fprintln(w, "LOCAL_MODULE :=", name)
185	fmt.Fprintln(w, "LOCAL_MODULE_CLASS :=", data.Class)
186	fmt.Fprintln(w, "LOCAL_MULTILIB :=", amod.commonProperties.Compile_multilib)
187	fmt.Fprintln(w, "LOCAL_SRC_FILES :=", data.OutputFile.String())
188
189	archStr := amod.Arch().ArchType.String()
190	if amod.Host() {
191		if hostCross {
192			fmt.Fprintln(w, "LOCAL_MODULE_HOST_CROSS_ARCH :=", archStr)
193		} else {
194			fmt.Fprintln(w, "LOCAL_MODULE_HOST_ARCH :=", archStr)
195
196			// TODO: this isn't true for every module, only dependencies of ACP
197			fmt.Fprintln(w, "LOCAL_ACP_UNAVAILABLE := true")
198		}
199		fmt.Fprintln(w, "LOCAL_MODULE_HOST_OS :=", amod.HostType().String())
200		fmt.Fprintln(w, "LOCAL_IS_HOST_MODULE := true")
201	} else {
202		fmt.Fprintln(w, "LOCAL_MODULE_TARGET_ARCH :=", archStr)
203	}
204
205	for _, extra := range data.Extra {
206		err = extra(w, data.OutputFile.Path())
207		if err != nil {
208			return err
209		}
210	}
211
212	fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
213
214	return err
215}
216