blob: d3089368704901210795519a5b5881d1fe5e6a61 [file] [log] [blame]
Dan Willemsen218f6562015-07-08 18:13:11 -07001// 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 "io"
20 "io/ioutil"
21 "os"
22 "path/filepath"
23 "sort"
24
25 "android/soong"
26
27 "github.com/google/blueprint"
28)
29
30func init() {
31 soong.RegisterSingletonType("androidmk", AndroidMkSingleton)
32}
33
34type AndroidMkDataProvider interface {
35 AndroidMk() AndroidMkData
36}
37
38type AndroidMkData struct {
39 Class string
40 OutputFile string
41
42 Custom func(w io.Writer, name, prefix string)
43
44 Extra func(name, prefix, outputFile string, arch Arch) []string
45}
46
47func AndroidMkSingleton() blueprint.Singleton {
48 return &androidMkSingleton{}
49}
50
51type androidMkSingleton struct{}
52
53func (c *androidMkSingleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
54 fileModules := make(map[string][]blueprint.Module)
55 hasBPFile := make(map[string]bool)
56 bpFiles := []string{}
57
58 ctx.SetNinjaBuildDir(pctx, filepath.Join(ctx.Config().(Config).BuildDir(), ".."))
59
60 ctx.VisitAllModules(func(module blueprint.Module) {
61 if _, ok := module.(AndroidModule); ok {
62 bpFile := ctx.BlueprintFile(module)
63
64 if !hasBPFile[bpFile] {
65 hasBPFile[bpFile] = true
66 bpFiles = append(bpFiles, bpFile)
67 }
68
69 fileModules[bpFile] = append(fileModules[bpFile], module)
70 }
71 })
72
73 // Gather list of eligible Android modules for translation
74 androidMkModules := make(map[blueprint.Module]bool)
75 var validBpFiles []string
76 srcDir := ctx.Config().(Config).SrcDir()
77 intermediatesDir := filepath.Join(ctx.Config().(Config).IntermediatesDir(), "androidmk")
78 sort.Strings(bpFiles)
79 for _, origBp := range bpFiles {
80 mkFile := filepath.Join(srcDir, filepath.Dir(origBp), "Android.mk")
81
82 files, err := Glob(ctx, intermediatesDir, mkFile, nil)
83 if err != nil {
84 ctx.Errorf("glob: %s", err.Error())
85 continue
86 }
87
88 // Existing Android.mk file, use that instead
89 if len(files) > 0 {
90 for _, file := range files {
91 ctx.AddNinjaFileDeps(file)
92 }
93 continue
94 }
95
96 validBpFiles = append(validBpFiles, origBp)
97
98 for _, mod := range fileModules[origBp] {
99 androidMkModules[mod] = true
100 }
101 }
102
103 // Validate that all modules have proper dependencies
104 androidMkModulesList := make([]AndroidModule, 0, len(androidMkModules))
105 for mod := range androidMkModules {
106 ctx.VisitDepsDepthFirstIf(mod, isAndroidModule, func(module blueprint.Module) {
107 if !androidMkModules[module] {
108 ctx.Errorf("Module %q missing dependency for Android.mk: %q", ctx.ModuleName(mod), ctx.ModuleName(module))
109 }
110 })
111 if amod, ok := mod.(AndroidModule); ok {
112 androidMkModulesList = append(androidMkModulesList, amod)
113 }
114 }
115
116 transMk := filepath.Join(ctx.Config().(Config).BuildDir(), "Android.mk")
117
118 err := translateAndroidMk(ctx, transMk, androidMkModulesList)
119 if err != nil {
120 ctx.Errorf(err.Error())
121 }
122
123 ctx.Build(pctx, blueprint.BuildParams{
124 Rule: blueprint.Phony,
125 Outputs: []string{transMk},
126 Optional: true,
127 })
128}
129
130func translateAndroidMk(ctx blueprint.SingletonContext, mkFile string, mods []AndroidModule) error {
131 buf := &bytes.Buffer{}
132
133 io.WriteString(buf, "LOCAL_PATH := $(TOP)\n")
134 io.WriteString(buf, "LOCAL_MODULE_MAKEFILE := $(lastword $(MAKEFILE_LIST))\n")
135
136 for _, mod := range mods {
137 err := translateAndroidMkModule(ctx, buf, mod)
138 if err != nil {
139 os.Remove(mkFile)
140 return err
141 }
142 }
143
144 // Don't write to the file if it hasn't changed
145 if _, err := os.Stat(mkFile); !os.IsNotExist(err) {
146 if data, err := ioutil.ReadFile(mkFile); err == nil {
147 matches := buf.Len() == len(data)
148
149 if matches {
150 for i, value := range buf.Bytes() {
151 if value != data[i] {
152 matches = false
153 break
154 }
155 }
156 }
157
158 if matches {
159 return nil
160 }
161 }
162 }
163
164 return ioutil.WriteFile(mkFile, buf.Bytes(), 0666)
165}
166
167func translateAndroidMkModule(ctx blueprint.SingletonContext, w io.Writer, mod blueprint.Module) error {
168 if mod != ctx.PrimaryModule(mod) {
169 // These will be handled by the primary module
170 return nil
171 }
172
173 name := ctx.ModuleName(mod)
174
175 type hostClass struct {
176 host bool
177 class string
178 multilib string
179 }
180
181 type archSrc struct {
182 arch Arch
183 src string
184 extra []string
185 }
186
187 srcs := make(map[hostClass][]archSrc)
188 var modules []hostClass
189
190 ctx.VisitAllModuleVariants(mod, func(m blueprint.Module) {
191 provider, ok := m.(AndroidMkDataProvider)
192 if !ok {
193 return
194 }
195
196 amod := m.(AndroidModule).base()
197 data := provider.AndroidMk()
198
199 arch := amod.commonProperties.CompileArch
200
201 prefix := ""
202 if amod.HostOrDevice() == Host {
203 if arch.ArchType != ctx.Config().(Config).HostArches[amod.HostType()][0].ArchType {
204 prefix = "2ND_"
205 }
206 } else {
207 if arch.ArchType != ctx.Config().(Config).DeviceArches[0].ArchType {
208 prefix = "2ND_"
209 }
210 }
211
212 if data.Custom != nil {
213 data.Custom(w, name, prefix)
214 return
215 }
216
217 hC := hostClass{
218 host: amod.HostOrDevice() == Host,
219 class: data.Class,
220 multilib: amod.commonProperties.Compile_multilib,
221 }
222
223 src := archSrc{
224 arch: arch,
225 src: data.OutputFile,
226 }
227
228 if data.Extra != nil {
229 src.extra = data.Extra(name, prefix, src.src, arch)
230 }
231
232 if srcs[hC] == nil {
233 modules = append(modules, hC)
234 }
235 srcs[hC] = append(srcs[hC], src)
236 })
237
238 for _, hC := range modules {
239 archSrcs := srcs[hC]
240
241 io.WriteString(w, "\ninclude $(CLEAR_VARS)\n")
242 io.WriteString(w, "LOCAL_MODULE := "+name+"\n")
243 io.WriteString(w, "LOCAL_MODULE_CLASS := "+hC.class+"\n")
244 io.WriteString(w, "LOCAL_MULTILIB := "+hC.multilib+"\n")
245
246 printed := make(map[string]bool)
247 for _, src := range archSrcs {
248 io.WriteString(w, "LOCAL_SRC_FILES_"+src.arch.ArchType.String()+" := "+src.src+"\n")
249
250 for _, extra := range src.extra {
251 if !printed[extra] {
252 printed[extra] = true
253 io.WriteString(w, extra+"\n")
254 }
255 }
256 }
257
258 if hC.host {
259 // TODO: this isn't true for every module
260 io.WriteString(w, "LOCAL_ACP_UNAVAILABLE := true\n")
261
262 io.WriteString(w, "LOCAL_IS_HOST_MODULE := true\n")
263 }
264 io.WriteString(w, "include $(BUILD_PREBUILT)\n")
265 }
266
267 return nil
268}