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