diff --git a/Blueprints b/Blueprints index 2acc065eb..3953dbf9b 100644 --- a/Blueprints +++ b/Blueprints @@ -143,6 +143,8 @@ bootstrap_go_package { "soong-common", ], srcs: [ + "java/app_builder.go", + "java/app.go", "java/builder.go", "java/gen.go", "java/java.go", diff --git a/androidmk/cmd/androidmk/android.go b/androidmk/cmd/androidmk/android.go index 3423d7901..934ea0c8f 100644 --- a/androidmk/cmd/androidmk/android.go +++ b/androidmk/cmd/androidmk/android.go @@ -22,6 +22,8 @@ var stringProperties = map[string]string{ "LOCAL_NDK_STL_VARIANT": "stl", "LOCAL_JAR_MANIFEST": "manifest", "LOCAL_JARJAR_RULES": "jarjar_rules", + "LOCAL_CERTIFICATE": "certificate", + "LOCAL_PACKAGE_NAME": "name", } var listProperties = map[string]string{ @@ -50,6 +52,8 @@ var listProperties = map[string]string{ "LOCAL_JAVA_LIBRARIES": "java_libs", "LOCAL_STATIC_JAVA_LIBRARIES": "java_static_libs", "LOCAL_AIDL_INCLUDES": "aidl_includes", + "LOCAL_AAPT_FLAGS": "aaptflags", + "LOCAL_PACKAGE_SPLITS": "package_splits", } var boolProperties = map[string]string{ @@ -63,6 +67,8 @@ var boolProperties = map[string]string{ "LOCAL_RTTI_FLAG": "rtti", "LOCAL_NO_STANDARD_LIBRARIES": "no_standard_libraries", + + "LOCAL_EXPORT_PACKAGE_RESOURCES": "export_package_resources", } var deleteProperties = map[string]struct{}{ @@ -136,6 +142,7 @@ var moduleTypes = map[string]string{ "BUILD_STATIC_JAVA_LIBRARY": "java_library_static", "BUILD_HOST_JAVA_LIBRARY": "java_library_host", "BUILD_HOST_DALVIK_JAVA_LIBRARY": "java_library_host_dalvik", + "BUILD_PACKAGE": "android_app", "BUILD_PREBUILT": "prebuilt", } diff --git a/build.ninja.in b/build.ninja.in index 74939d977..a46053f08 100644 --- a/build.ninja.in +++ b/build.ninja.in @@ -53,7 +53,7 @@ rule g.bootstrap.link # Variant: # Type: bootstrap_go_binary # Factory: github.com/google/blueprint/bootstrap.newGoBinaryModule -# Defined: build/soong/Blueprints:157:1 +# Defined: build/soong/Blueprints:159:1 build .bootstrap/androidmk/obj/androidmk.a: g.bootstrap.gc $ ${g.bootstrap.srcDir}/build/soong/androidmk/cmd/androidmk/android.go $ @@ -79,7 +79,7 @@ default .bootstrap/bin/androidmk # Variant: # Type: bootstrap_go_package # Factory: github.com/google/blueprint/bootstrap.newGoPackageModule -# Defined: build/soong/Blueprints:170:1 +# Defined: build/soong/Blueprints:172:1 build .bootstrap/androidmk-parser/pkg/android/soong/androidmk/parser.a: $ g.bootstrap.gc $ @@ -424,6 +424,8 @@ default .bootstrap/soong-glob/pkg/android/soong/glob.a # Defined: build/soong/Blueprints:137:1 build .bootstrap/soong-java/pkg/android/soong/java.a: g.bootstrap.gc $ + ${g.bootstrap.srcDir}/build/soong/java/app_builder.go $ + ${g.bootstrap.srcDir}/build/soong/java/app.go $ ${g.bootstrap.srcDir}/build/soong/java/builder.go $ ${g.bootstrap.srcDir}/build/soong/java/gen.go $ ${g.bootstrap.srcDir}/build/soong/java/java.go $ diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go index d17fbed5a..389e5a299 100644 --- a/cmd/soong_build/main.go +++ b/cmd/soong_build/main.go @@ -68,6 +68,7 @@ func main() { ctx.RegisterModuleType("java_binary_host", java.JavaBinaryHostFactory) ctx.RegisterModuleType("prebuilt_java_library", java.JavaPrebuiltFactory) ctx.RegisterModuleType("prebuilt_sdk", java.SdkPrebuiltFactory) + ctx.RegisterModuleType("android_app", java.AndroidAppFactory) // Mutators ctx.RegisterEarlyMutator("arch", common.ArchMutator) diff --git a/common/config.go b/common/config.go index 5d761e66c..10ed4f083 100644 --- a/common/config.go +++ b/common/config.go @@ -44,7 +44,7 @@ type Config struct { type config struct { FileConfigurableOptions - srcDir string // the path of the root source directory + srcDir string // the path of the root source directory envLock sync.Mutex envDeps map[string]string @@ -217,3 +217,39 @@ func (c *config) HostJavaDir() string { func (c *config) HostJavaTool(tool string) (string, error) { return filepath.Join(c.HostJavaDir(), tool), nil } + +func (c *config) ResourceOverlays() []string { + return nil +} + +func (c *config) PlatformVersion() string { + return "M" +} + +func (c *config) PlatformSdkVersion() string { + return "22" +} + +func (c *config) BuildNumber() string { + return "000000" +} + +func (c *config) ProductAaptConfig() []string { + return []string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"} +} + +func (c *config) ProductAaptPreferredConfig() string { + return "xhdpi" +} + +func (c *config) ProductAaptCharacteristics() string { + return "nosdcard" +} + +func (c *config) DefaultAppCertificateDir() string { + return filepath.Join(c.SrcDir(), "build/target/product/security") +} + +func (c *config) DefaultAppCertificate() string { + return filepath.Join(c.DefaultAppCertificateDir(), "testkey") +} diff --git a/java/app.go b/java/app.go new file mode 100644 index 000000000..869cfea09 --- /dev/null +++ b/java/app.go @@ -0,0 +1,317 @@ +// Copyright 2015 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package java + +// This file contains the module types for compiling Android apps. + +import ( + "os" + "path/filepath" + "strings" + + "github.com/google/blueprint" + "github.com/google/blueprint/pathtools" + + "android/soong/common" +) + +// AAR prebuilts +// AndroidManifest.xml merging +// package splits + +type AndroidApp struct { + javaBase + + appProperties struct { + // certificate: path to a certificate, or the name of a certificate in the default + // certificate directory, or blank to use the default product certificate + Certificate string + + // additional_certificates: paths to extra certificates to sign the apk with + Additional_certificates []string + + // export_package_resources: If set, create package-export.apk, which other packages can + // use to get PRODUCT-agnostic resource data like IDs and type definitions. + Export_package_resources bool + + // aaptflags: flags passed to aapt when creating the apk + Aaptflags []string + + // package_splits: list of resource labels to generate individual resource packages + Package_splits []string + + // asset_dirs: list of directories relative to the Blueprints file containing assets. + // Defaults to "assets" + Asset_dirs []string + + // android_resource_dirs: list of directories relative to the Blueprints file containing + // Java resources + Android_resource_dirs []string + } + + aaptJavaFileList string + exportPackage string +} + +func (a *AndroidApp) JavaDynamicDependencies(ctx common.AndroidDynamicDependerModuleContext) []string { + deps := a.javaBase.JavaDynamicDependencies(ctx) + + if !a.properties.No_standard_libraries { + switch a.properties.Sdk_version { // TODO: Res_sdk_version? + case "current", "system_current", "": + deps = append(deps, "framework-res") + default: + // We'll already have a dependency on an sdk prebuilt android.jar + } + } + + return deps +} + +func (a *AndroidApp) GenerateJavaBuildActions(ctx common.AndroidModuleContext) { + aaptFlags, aaptDeps, hasResources := a.aaptFlags(ctx) + + if hasResources { + // First generate R.java so we can build the .class files + aaptRJavaFlags := append([]string(nil), aaptFlags...) + + publicResourcesFile, proguardOptionsFile, aaptJavaFileList := + CreateResourceJavaFiles(ctx, aaptRJavaFlags, aaptDeps) + a.aaptJavaFileList = aaptJavaFileList + a.ExtraSrcLists = append(a.ExtraSrcLists, aaptJavaFileList) + + if a.appProperties.Export_package_resources { + aaptPackageFlags := append([]string(nil), aaptFlags...) + var hasProduct bool + for _, f := range aaptPackageFlags { + if strings.HasPrefix(f, "--product") { + hasProduct = true + break + } + } + + if !hasProduct { + aaptPackageFlags = append(aaptPackageFlags, + "--product "+ctx.AConfig().ProductAaptCharacteristics()) + } + a.exportPackage = CreateExportPackage(ctx, aaptPackageFlags, aaptDeps) + ctx.CheckbuildFile(a.exportPackage) + } + ctx.CheckbuildFile(publicResourcesFile) + ctx.CheckbuildFile(proguardOptionsFile) + ctx.CheckbuildFile(aaptJavaFileList) + } + + // apps manifests are handled by aapt, don't let javaBase see them + a.properties.Manifest = "" + + //if !ctx.ContainsProperty("proguard.enabled") { + // a.properties.Proguard.Enabled = true + //} + + a.javaBase.GenerateJavaBuildActions(ctx) + + aaptPackageFlags := append([]string(nil), aaptFlags...) + var hasProduct bool + for _, f := range aaptPackageFlags { + if strings.HasPrefix(f, "--product") { + hasProduct = true + break + } + } + + if !hasProduct { + aaptPackageFlags = append(aaptPackageFlags, + "--product "+ctx.AConfig().ProductAaptCharacteristics()) + } + + certificate := a.appProperties.Certificate + if certificate == "" { + certificate = ctx.AConfig().DefaultAppCertificate() + } else if dir, _ := filepath.Split(certificate); dir == "" { + certificate = filepath.Join(ctx.AConfig().DefaultAppCertificateDir(), certificate) + } else { + certificate = filepath.Join(ctx.AConfig().SrcDir(), certificate) + } + + certificates := []string{certificate} + for _, c := range a.appProperties.Additional_certificates { + certificates = append(certificates, filepath.Join(ctx.AConfig().SrcDir(), c)) + } + + a.outputFile = CreateAppPackage(ctx, aaptPackageFlags, a.outputFile, certificates) + ctx.InstallFileName("app", ctx.ModuleName()+".apk", a.outputFile) +} + +var aaptIgnoreFilenames = []string{ + ".svn", + ".git", + ".ds_store", + "*.scc", + ".*", + "CVS", + "thumbs.db", + "picasa.ini", + "*~", +} + +func (a *AndroidApp) aaptFlags(ctx common.AndroidModuleContext) ([]string, []string, bool) { + aaptFlags := a.appProperties.Aaptflags + hasVersionCode := false + hasVersionName := false + for _, f := range aaptFlags { + if strings.HasPrefix(f, "--version-code") { + hasVersionCode = true + } else if strings.HasPrefix(f, "--version-name") { + hasVersionName = true + } + } + + if true /* is not a test */ { + aaptFlags = append(aaptFlags, "-z") + } + + assetDirs := a.appProperties.Asset_dirs + if len(assetDirs) == 0 { + defaultAssetDir := filepath.Join(common.ModuleSrcDir(ctx), "assets") + if _, err := os.Stat(defaultAssetDir); err == nil { + assetDirs = []string{defaultAssetDir} + } else { + // Default asset directory doesn't exist, add a dep on the parent directory to + // regenerate the manifest if it is created later + // TODO: use glob to avoid rerunning whole regenerate if a different file is created? + ctx.AddNinjaFileDeps(common.ModuleSrcDir(ctx)) + } + } else { + assetDirs = pathtools.PrefixPaths(assetDirs, common.ModuleSrcDir(ctx)) + } + + resourceDirs := a.appProperties.Android_resource_dirs + if len(resourceDirs) == 0 { + defaultResourceDir := filepath.Join(common.ModuleSrcDir(ctx), "res") + if _, err := os.Stat(defaultResourceDir); err == nil { + resourceDirs = []string{defaultResourceDir} + } else { + // Default resource directory doesn't exist, add a dep on the parent directory to + // regenerate the manifest if it is created later + // TODO: use glob to avoid rerunning whole regenerate if a different file is created? + ctx.AddNinjaFileDeps(common.ModuleSrcDir(ctx)) + } + } else { + resourceDirs = pathtools.PrefixPaths(resourceDirs, common.ModuleSrcDir(ctx)) + } + + rootSrcDir := ctx.AConfig().SrcDir() + var overlayResourceDirs []string + // For every resource directory, check if there is an overlay directory with the same path. + // If found, it will be prepended to the list of resource directories. + for _, overlayDir := range ctx.AConfig().ResourceOverlays() { + for _, resourceDir := range resourceDirs { + relResourceDir, err := filepath.Rel(rootSrcDir, resourceDir) + if err != nil { + ctx.ModuleErrorf("resource directory %q is not in source tree", resourceDir) + continue + } + overlayResourceDir := filepath.Join(overlayDir, relResourceDir) + if _, err := os.Stat(overlayResourceDir); err == nil { + overlayResourceDirs = append(overlayResourceDirs, overlayResourceDir) + } else { + // Overlay resource directory doesn't exist, add a dep to regenerate the manifest if + // it is created later + ctx.AddNinjaFileDeps(overlayResourceDir) + } + } + } + + if len(overlayResourceDirs) > 0 { + resourceDirs = append(overlayResourceDirs, resourceDirs...) + } + + // aapt needs to rerun if any files are added or modified in the assets or resource directories, + // use glob to create a filelist. + var aaptDeps []string + var hasResources bool + for _, d := range resourceDirs { + newDeps := common.Glob(ctx, filepath.Join(d, "**/*"), aaptIgnoreFilenames) + aaptDeps = append(aaptDeps, newDeps...) + if len(newDeps) > 0 { + hasResources = true + } + } + for _, d := range assetDirs { + newDeps := common.Glob(ctx, filepath.Join(d, "**/*"), aaptIgnoreFilenames) + aaptDeps = append(aaptDeps, newDeps...) + } + + manifestFile := a.properties.Manifest + if manifestFile == "" { + manifestFile = "AndroidManifest.xml" + } + + manifestFile = filepath.Join(common.ModuleSrcDir(ctx), manifestFile) + aaptDeps = append(aaptDeps, manifestFile) + + aaptFlags = append(aaptFlags, "-M "+manifestFile) + aaptFlags = append(aaptFlags, common.JoinWithPrefix(assetDirs, "-A ")) + aaptFlags = append(aaptFlags, common.JoinWithPrefix(resourceDirs, "-S ")) + + ctx.VisitDirectDeps(func(module blueprint.Module) { + var depFile string + if sdkDep, ok := module.(sdkDependency); ok { + depFile = sdkDep.ClasspathFile() + } else if javaDep, ok := module.(JavaDependency); ok { + if ctx.OtherModuleName(module) == "framework-res" { + depFile = javaDep.(*javaBase).module.(*AndroidApp).exportPackage + } + } + if depFile != "" { + aaptFlags = append(aaptFlags, "-I "+depFile) + aaptDeps = append(aaptDeps, depFile) + } + }) + + sdkVersion := a.properties.Sdk_version + if sdkVersion == "" { + sdkVersion = ctx.AConfig().PlatformSdkVersion() + } + + aaptFlags = append(aaptFlags, "--min-sdk-version "+sdkVersion) + aaptFlags = append(aaptFlags, "--target-sdk-version "+sdkVersion) + + if !hasVersionCode { + aaptFlags = append(aaptFlags, "--version-code "+ctx.AConfig().PlatformSdkVersion()) + } + + if !hasVersionName { + aaptFlags = append(aaptFlags, + "--version-name "+ctx.AConfig().PlatformVersion()+"-"+ctx.AConfig().BuildNumber()) + } + + // TODO: LOCAL_PACKAGE_OVERRIDES + // $(addprefix --rename-manifest-package , $(PRIVATE_MANIFEST_PACKAGE_NAME)) \ + + // TODO: LOCAL_INSTRUMENTATION_FOR + // $(addprefix --rename-instrumentation-target-package , $(PRIVATE_MANIFEST_INSTRUMENTATION_FOR)) + + return aaptFlags, aaptDeps, hasResources +} + +func AndroidAppFactory() (blueprint.Module, []interface{}) { + module := &AndroidApp{} + + module.properties.Dex = true + + return NewJavaBase(&module.javaBase, module, common.DeviceSupported, &module.appProperties) +} diff --git a/java/app_builder.go b/java/app_builder.go new file mode 100644 index 000000000..69c6fe1dc --- /dev/null +++ b/java/app_builder.go @@ -0,0 +1,172 @@ +// Copyright 2015 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package java + +// This file generates the final rules for compiling all Java. All properties related to +// compiling should have been translated into javaBuilderFlags or another argument to the Transform* +// functions. + +import ( + "path/filepath" + "strings" + + "github.com/google/blueprint" + + "android/soong/common" +) + +var ( + aaptCreateResourceJavaFile = pctx.StaticRule("aaptCreateResourceJavaFile", + blueprint.RuleParams{ + Command: `rm -rf "$javaDir" && mkdir -p "$javaDir" && ` + + `$aaptCmd package -m $aaptFlags -P $publicResourcesFile -G $proguardOptionsFile ` + + `-J $javaDir || ( rm -rf "$javaDir/*"; exit 41 ) && ` + + `find $javaDir -name "*.java" > $javaFileList`, + Description: "aapt create R.java $out", + }, + "aaptFlags", "publicResourcesFile", "proguardOptionsFile", "javaDir", "javaFileList") + + aaptCreateAssetsPackage = pctx.StaticRule("aaptCreateAssetsPackage", + blueprint.RuleParams{ + Command: `$aaptCmd package $aaptFlags -F $out`, + Description: "aapt export package $out", + }, + "aaptFlags", "publicResourcesFile", "proguardOptionsFile", "javaDir", "javaFileList") + + aaptAddResources = pctx.StaticRule("aaptAddResources", + blueprint.RuleParams{ + // TODO: add-jni-shared-libs-to-package + Command: `cp -f $in $out.tmp && $aaptCmd package -u $aaptFlags -F $out.tmp && mv $out.tmp $out`, + Description: "aapt package $out", + }, + "aaptFlags") + + zipalign = pctx.StaticRule("zipalign", + blueprint.RuleParams{ + Command: `$zipalignCmd -f $zipalignFlags 4 $in $out`, + Description: "zipalign $out", + }, + "zipalignFlags") + + signapk = pctx.StaticRule("signapk", + blueprint.RuleParams{ + Command: `java -jar $signapkCmd $certificates $in $out`, + Description: "signapk $out", + }, + "certificates") + + androidManifestMerger = pctx.StaticRule("androidManifestMerger", + blueprint.RuleParams{ + Command: "java -classpath $androidManifestMergerCmd com.android.manifmerger.Main merge " + + "--main $in --libs $libsManifests --out $out", + Description: "merge manifest files $out", + }, + "libsManifests") +) + +func init() { + pctx.StaticVariable("androidManifestMergerCmd", "${srcDir}/prebuilts/devtools/tools/lib/manifest-merger.jar") + pctx.VariableFunc("aaptCmd", func(c interface{}) (string, error) { + return c.(common.Config).HostBinTool("aapt") + }) + pctx.VariableFunc("zipalignCmd", func(c interface{}) (string, error) { + return c.(common.Config).HostBinTool("zipalign") + }) + pctx.VariableFunc("signapkCmd", func(c interface{}) (string, error) { + return c.(common.Config).HostJavaTool("signapk.jar") + }) +} + +func CreateResourceJavaFiles(ctx common.AndroidModuleContext, flags []string, + deps []string) (string, string, string) { + javaDir := filepath.Join(common.ModuleGenDir(ctx), "R") + javaFileList := filepath.Join(common.ModuleOutDir(ctx), "R.filelist") + publicResourcesFile := filepath.Join(common.ModuleOutDir(ctx), "public_resources.xml") + proguardOptionsFile := filepath.Join(common.ModuleOutDir(ctx), "proguard.options") + + ctx.Build(pctx, blueprint.BuildParams{ + Rule: aaptCreateResourceJavaFile, + Outputs: []string{publicResourcesFile, proguardOptionsFile, javaFileList}, + Implicits: deps, + Args: map[string]string{ + "aaptFlags": strings.Join(flags, " "), + "publicResourcesFile": publicResourcesFile, + "proguardOptionsFile": proguardOptionsFile, + "javaDir": javaDir, + "javaFileList": javaFileList, + }, + }) + + return publicResourcesFile, proguardOptionsFile, javaFileList +} + +func CreateExportPackage(ctx common.AndroidModuleContext, flags []string, deps []string) string { + outputFile := filepath.Join(common.ModuleOutDir(ctx), "package-export.apk") + + ctx.Build(pctx, blueprint.BuildParams{ + Rule: aaptCreateAssetsPackage, + Outputs: []string{outputFile}, + Implicits: deps, + Args: map[string]string{ + "aaptFlags": strings.Join(flags, " "), + }, + }) + + return outputFile +} + +func CreateAppPackage(ctx common.AndroidModuleContext, flags []string, jarFile string, + certificates []string) string { + + resourceApk := filepath.Join(common.ModuleOutDir(ctx), "resources.apk") + + ctx.Build(pctx, blueprint.BuildParams{ + Rule: aaptAddResources, + Outputs: []string{resourceApk}, + Inputs: []string{jarFile}, + Args: map[string]string{ + "aaptFlags": strings.Join(flags, " "), + }, + }) + + signedApk := filepath.Join(common.ModuleOutDir(ctx), "signed.apk") + + var certificateArgs []string + for _, c := range certificates { + certificateArgs = append(certificateArgs, c+".x509.pem", c+".pk8") + } + + ctx.Build(pctx, blueprint.BuildParams{ + Rule: signapk, + Outputs: []string{signedApk}, + Inputs: []string{resourceApk}, + Args: map[string]string{ + "certificates": strings.Join(certificateArgs, " "), + }, + }) + + outputFile := filepath.Join(common.ModuleOutDir(ctx), "package.apk") + + ctx.Build(pctx, blueprint.BuildParams{ + Rule: zipalign, + Outputs: []string{outputFile}, + Inputs: []string{signedApk}, + Args: map[string]string{ + "zipalignFlags": "", + }, + }) + + return outputFile +} diff --git a/root.bp b/root.bp index 413d94460..831f8df3a 100644 --- a/root.bp +++ b/root.bp @@ -5,8 +5,7 @@ subdirs = [ "bionic", "dalvik", "external/*", - "frameworks/base/libs/*", - "frameworks/base/tools/*", + "frameworks/base", "frameworks/native/libs/*", "hardware/*", "libcore", @@ -14,4 +13,6 @@ subdirs = [ "prebuilts/ndk", "prebuilts/sdk", "system/core/*", + "packages/apps/HTMLViewer", + "build/tools/*", ]