blob: 869cfea09be2b53760cf0a7303593696f9e2849a [file] [log] [blame]
Colin Cross30e076a2015-04-13 13:58:27 -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 java
16
17// This file contains the module types for compiling Android apps.
18
19import (
20 "os"
21 "path/filepath"
22 "strings"
23
24 "github.com/google/blueprint"
25 "github.com/google/blueprint/pathtools"
26
27 "android/soong/common"
28)
29
30// AAR prebuilts
31// AndroidManifest.xml merging
32// package splits
33
34type AndroidApp struct {
35 javaBase
36
37 appProperties struct {
38 // certificate: path to a certificate, or the name of a certificate in the default
39 // certificate directory, or blank to use the default product certificate
40 Certificate string
41
42 // additional_certificates: paths to extra certificates to sign the apk with
43 Additional_certificates []string
44
45 // export_package_resources: If set, create package-export.apk, which other packages can
46 // use to get PRODUCT-agnostic resource data like IDs and type definitions.
47 Export_package_resources bool
48
49 // aaptflags: flags passed to aapt when creating the apk
50 Aaptflags []string
51
52 // package_splits: list of resource labels to generate individual resource packages
53 Package_splits []string
54
55 // asset_dirs: list of directories relative to the Blueprints file containing assets.
56 // Defaults to "assets"
57 Asset_dirs []string
58
59 // android_resource_dirs: list of directories relative to the Blueprints file containing
60 // Java resources
61 Android_resource_dirs []string
62 }
63
64 aaptJavaFileList string
65 exportPackage string
66}
67
68func (a *AndroidApp) JavaDynamicDependencies(ctx common.AndroidDynamicDependerModuleContext) []string {
69 deps := a.javaBase.JavaDynamicDependencies(ctx)
70
71 if !a.properties.No_standard_libraries {
72 switch a.properties.Sdk_version { // TODO: Res_sdk_version?
73 case "current", "system_current", "":
74 deps = append(deps, "framework-res")
75 default:
76 // We'll already have a dependency on an sdk prebuilt android.jar
77 }
78 }
79
80 return deps
81}
82
83func (a *AndroidApp) GenerateJavaBuildActions(ctx common.AndroidModuleContext) {
84 aaptFlags, aaptDeps, hasResources := a.aaptFlags(ctx)
85
86 if hasResources {
87 // First generate R.java so we can build the .class files
88 aaptRJavaFlags := append([]string(nil), aaptFlags...)
89
90 publicResourcesFile, proguardOptionsFile, aaptJavaFileList :=
91 CreateResourceJavaFiles(ctx, aaptRJavaFlags, aaptDeps)
92 a.aaptJavaFileList = aaptJavaFileList
93 a.ExtraSrcLists = append(a.ExtraSrcLists, aaptJavaFileList)
94
95 if a.appProperties.Export_package_resources {
96 aaptPackageFlags := append([]string(nil), aaptFlags...)
97 var hasProduct bool
98 for _, f := range aaptPackageFlags {
99 if strings.HasPrefix(f, "--product") {
100 hasProduct = true
101 break
102 }
103 }
104
105 if !hasProduct {
106 aaptPackageFlags = append(aaptPackageFlags,
107 "--product "+ctx.AConfig().ProductAaptCharacteristics())
108 }
109 a.exportPackage = CreateExportPackage(ctx, aaptPackageFlags, aaptDeps)
110 ctx.CheckbuildFile(a.exportPackage)
111 }
112 ctx.CheckbuildFile(publicResourcesFile)
113 ctx.CheckbuildFile(proguardOptionsFile)
114 ctx.CheckbuildFile(aaptJavaFileList)
115 }
116
117 // apps manifests are handled by aapt, don't let javaBase see them
118 a.properties.Manifest = ""
119
120 //if !ctx.ContainsProperty("proguard.enabled") {
121 // a.properties.Proguard.Enabled = true
122 //}
123
124 a.javaBase.GenerateJavaBuildActions(ctx)
125
126 aaptPackageFlags := append([]string(nil), aaptFlags...)
127 var hasProduct bool
128 for _, f := range aaptPackageFlags {
129 if strings.HasPrefix(f, "--product") {
130 hasProduct = true
131 break
132 }
133 }
134
135 if !hasProduct {
136 aaptPackageFlags = append(aaptPackageFlags,
137 "--product "+ctx.AConfig().ProductAaptCharacteristics())
138 }
139
140 certificate := a.appProperties.Certificate
141 if certificate == "" {
142 certificate = ctx.AConfig().DefaultAppCertificate()
143 } else if dir, _ := filepath.Split(certificate); dir == "" {
144 certificate = filepath.Join(ctx.AConfig().DefaultAppCertificateDir(), certificate)
145 } else {
146 certificate = filepath.Join(ctx.AConfig().SrcDir(), certificate)
147 }
148
149 certificates := []string{certificate}
150 for _, c := range a.appProperties.Additional_certificates {
151 certificates = append(certificates, filepath.Join(ctx.AConfig().SrcDir(), c))
152 }
153
154 a.outputFile = CreateAppPackage(ctx, aaptPackageFlags, a.outputFile, certificates)
155 ctx.InstallFileName("app", ctx.ModuleName()+".apk", a.outputFile)
156}
157
158var aaptIgnoreFilenames = []string{
159 ".svn",
160 ".git",
161 ".ds_store",
162 "*.scc",
163 ".*",
164 "CVS",
165 "thumbs.db",
166 "picasa.ini",
167 "*~",
168}
169
170func (a *AndroidApp) aaptFlags(ctx common.AndroidModuleContext) ([]string, []string, bool) {
171 aaptFlags := a.appProperties.Aaptflags
172 hasVersionCode := false
173 hasVersionName := false
174 for _, f := range aaptFlags {
175 if strings.HasPrefix(f, "--version-code") {
176 hasVersionCode = true
177 } else if strings.HasPrefix(f, "--version-name") {
178 hasVersionName = true
179 }
180 }
181
182 if true /* is not a test */ {
183 aaptFlags = append(aaptFlags, "-z")
184 }
185
186 assetDirs := a.appProperties.Asset_dirs
187 if len(assetDirs) == 0 {
188 defaultAssetDir := filepath.Join(common.ModuleSrcDir(ctx), "assets")
189 if _, err := os.Stat(defaultAssetDir); err == nil {
190 assetDirs = []string{defaultAssetDir}
191 } else {
192 // Default asset directory doesn't exist, add a dep on the parent directory to
193 // regenerate the manifest if it is created later
194 // TODO: use glob to avoid rerunning whole regenerate if a different file is created?
195 ctx.AddNinjaFileDeps(common.ModuleSrcDir(ctx))
196 }
197 } else {
198 assetDirs = pathtools.PrefixPaths(assetDirs, common.ModuleSrcDir(ctx))
199 }
200
201 resourceDirs := a.appProperties.Android_resource_dirs
202 if len(resourceDirs) == 0 {
203 defaultResourceDir := filepath.Join(common.ModuleSrcDir(ctx), "res")
204 if _, err := os.Stat(defaultResourceDir); err == nil {
205 resourceDirs = []string{defaultResourceDir}
206 } else {
207 // Default resource directory doesn't exist, add a dep on the parent directory to
208 // regenerate the manifest if it is created later
209 // TODO: use glob to avoid rerunning whole regenerate if a different file is created?
210 ctx.AddNinjaFileDeps(common.ModuleSrcDir(ctx))
211 }
212 } else {
213 resourceDirs = pathtools.PrefixPaths(resourceDirs, common.ModuleSrcDir(ctx))
214 }
215
216 rootSrcDir := ctx.AConfig().SrcDir()
217 var overlayResourceDirs []string
218 // For every resource directory, check if there is an overlay directory with the same path.
219 // If found, it will be prepended to the list of resource directories.
220 for _, overlayDir := range ctx.AConfig().ResourceOverlays() {
221 for _, resourceDir := range resourceDirs {
222 relResourceDir, err := filepath.Rel(rootSrcDir, resourceDir)
223 if err != nil {
224 ctx.ModuleErrorf("resource directory %q is not in source tree", resourceDir)
225 continue
226 }
227 overlayResourceDir := filepath.Join(overlayDir, relResourceDir)
228 if _, err := os.Stat(overlayResourceDir); err == nil {
229 overlayResourceDirs = append(overlayResourceDirs, overlayResourceDir)
230 } else {
231 // Overlay resource directory doesn't exist, add a dep to regenerate the manifest if
232 // it is created later
233 ctx.AddNinjaFileDeps(overlayResourceDir)
234 }
235 }
236 }
237
238 if len(overlayResourceDirs) > 0 {
239 resourceDirs = append(overlayResourceDirs, resourceDirs...)
240 }
241
242 // aapt needs to rerun if any files are added or modified in the assets or resource directories,
243 // use glob to create a filelist.
244 var aaptDeps []string
245 var hasResources bool
246 for _, d := range resourceDirs {
247 newDeps := common.Glob(ctx, filepath.Join(d, "**/*"), aaptIgnoreFilenames)
248 aaptDeps = append(aaptDeps, newDeps...)
249 if len(newDeps) > 0 {
250 hasResources = true
251 }
252 }
253 for _, d := range assetDirs {
254 newDeps := common.Glob(ctx, filepath.Join(d, "**/*"), aaptIgnoreFilenames)
255 aaptDeps = append(aaptDeps, newDeps...)
256 }
257
258 manifestFile := a.properties.Manifest
259 if manifestFile == "" {
260 manifestFile = "AndroidManifest.xml"
261 }
262
263 manifestFile = filepath.Join(common.ModuleSrcDir(ctx), manifestFile)
264 aaptDeps = append(aaptDeps, manifestFile)
265
266 aaptFlags = append(aaptFlags, "-M "+manifestFile)
267 aaptFlags = append(aaptFlags, common.JoinWithPrefix(assetDirs, "-A "))
268 aaptFlags = append(aaptFlags, common.JoinWithPrefix(resourceDirs, "-S "))
269
270 ctx.VisitDirectDeps(func(module blueprint.Module) {
271 var depFile string
272 if sdkDep, ok := module.(sdkDependency); ok {
273 depFile = sdkDep.ClasspathFile()
274 } else if javaDep, ok := module.(JavaDependency); ok {
275 if ctx.OtherModuleName(module) == "framework-res" {
276 depFile = javaDep.(*javaBase).module.(*AndroidApp).exportPackage
277 }
278 }
279 if depFile != "" {
280 aaptFlags = append(aaptFlags, "-I "+depFile)
281 aaptDeps = append(aaptDeps, depFile)
282 }
283 })
284
285 sdkVersion := a.properties.Sdk_version
286 if sdkVersion == "" {
287 sdkVersion = ctx.AConfig().PlatformSdkVersion()
288 }
289
290 aaptFlags = append(aaptFlags, "--min-sdk-version "+sdkVersion)
291 aaptFlags = append(aaptFlags, "--target-sdk-version "+sdkVersion)
292
293 if !hasVersionCode {
294 aaptFlags = append(aaptFlags, "--version-code "+ctx.AConfig().PlatformSdkVersion())
295 }
296
297 if !hasVersionName {
298 aaptFlags = append(aaptFlags,
299 "--version-name "+ctx.AConfig().PlatformVersion()+"-"+ctx.AConfig().BuildNumber())
300 }
301
302 // TODO: LOCAL_PACKAGE_OVERRIDES
303 // $(addprefix --rename-manifest-package , $(PRIVATE_MANIFEST_PACKAGE_NAME)) \
304
305 // TODO: LOCAL_INSTRUMENTATION_FOR
306 // $(addprefix --rename-instrumentation-target-package , $(PRIVATE_MANIFEST_INSTRUMENTATION_FOR))
307
308 return aaptFlags, aaptDeps, hasResources
309}
310
311func AndroidAppFactory() (blueprint.Module, []interface{}) {
312 module := &AndroidApp{}
313
314 module.properties.Dex = true
315
316 return NewJavaBase(&module.javaBase, module, common.DeviceSupported, &module.appProperties)
317}