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