diff --git a/Android.bp b/Android.bp index f2289da96..5396664be 100644 --- a/Android.bp +++ b/Android.bp @@ -57,6 +57,7 @@ bootstrap_go_package { "android/package_ctx.go", "android/paths.go", "android/prebuilt.go", + "android/prebuilt_etc.go", "android/proto.go", "android/register.go", "android/singleton.go", @@ -232,6 +233,7 @@ bootstrap_go_package { "java/java.go", "java/java_resources.go", "java/proto.go", + "java/sdk_library.go", "java/system_modules.go", ], testSrcs: [ diff --git a/android/module.go b/android/module.go index 3bd256a42..552d16517 100644 --- a/android/module.go +++ b/android/module.go @@ -501,6 +501,22 @@ func (a *ModuleBase) DeviceSupported() bool { *a.hostAndDeviceProperties.Device_supported) } +func (a *ModuleBase) Platform() bool { + return !a.DeviceSpecific() && !a.SocSpecific() && !a.ProductSpecific() +} + +func (a *ModuleBase) DeviceSpecific() bool { + return Bool(a.commonProperties.Device_specific) +} + +func (a *ModuleBase) SocSpecific() bool { + return Bool(a.commonProperties.Vendor) || Bool(a.commonProperties.Proprietary) || Bool(a.commonProperties.Soc_specific) +} + +func (a *ModuleBase) ProductSpecific() bool { + return Bool(a.commonProperties.Product_specific) +} + func (a *ModuleBase) Enabled() bool { if a.commonProperties.Enabled == nil { return !a.Os().DefaultDisabled diff --git a/android/prebuilt_etc.go b/android/prebuilt_etc.go new file mode 100644 index 000000000..fee2c4949 --- /dev/null +++ b/android/prebuilt_etc.go @@ -0,0 +1,91 @@ +// Copyright 2016 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 android + +import ( + "fmt" + "io" +) + +// prebuilt_etc is for prebuilts that will be installed to +// /etc/ + +func init() { + RegisterModuleType("prebuilt_etc", PrebuiltEtcFactory) +} + +type prebuiltEtcProperties struct { + // Source file of this prebuilt. + Srcs []string `android:"arch_variant"` + + // optional subdirectory under which this file is installed into + Sub_dir *string `android:"arch_variant"` +} + +type prebuiltEtc struct { + ModuleBase + prebuilt Prebuilt + + properties prebuiltEtcProperties + + sourceFilePath Path + installDirPath OutputPath +} + +func (p *prebuiltEtc) Prebuilt() *Prebuilt { + return &p.prebuilt +} + +func (p *prebuiltEtc) DepsMutator(ctx BottomUpMutatorContext) { + if len(p.properties.Srcs) == 0 { + ctx.PropertyErrorf("srcs", "missing prebuilt source file") + } + + if len(p.properties.Srcs) > 1 { + ctx.PropertyErrorf("srcs", "multiple prebuilt source files") + } + + // To support ":modulename" in src + ExtractSourceDeps(ctx, &(p.properties.Srcs)[0]) +} + +func (p *prebuiltEtc) GenerateAndroidBuildActions(ctx ModuleContext) { + p.sourceFilePath = ctx.ExpandSource(p.properties.Srcs[0], "srcs") + p.installDirPath = PathForModuleInstall(ctx, "etc", String(p.properties.Sub_dir)) +} + +func (p *prebuiltEtc) AndroidMk() AndroidMkData { + return AndroidMkData{ + Custom: func(w io.Writer, name, prefix, moduleDir string, data AndroidMkData) { + fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)") + fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir) + fmt.Fprintln(w, "LOCAL_MODULE :=", name) + fmt.Fprintln(w, "LOCAL_MODULE_CLASS := ETC") + fmt.Fprintln(w, "LOCAL_MODULE_TAGS := optional") + fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", p.sourceFilePath.String()) + fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", "$(OUT_DIR)/"+p.installDirPath.RelPathString()) + fmt.Fprintln(w, "include $(BUILD_PREBUILT)") + }, + } +} + +func PrebuiltEtcFactory() Module { + module := &prebuiltEtc{} + module.AddProperties(&module.properties) + + InitPrebuiltModule(module, &(module.properties.Srcs)) + InitAndroidModule(module) + return module +} diff --git a/java/droiddoc.go b/java/droiddoc.go index 0224da5ae..07004873e 100644 --- a/java/droiddoc.go +++ b/java/droiddoc.go @@ -306,6 +306,20 @@ func (j *Javadoc) collectDeps(ctx android.ModuleContext) deps { default: panic(fmt.Errorf("unknown dependency %q for %q", otherName, ctx.ModuleName())) } + case SdkLibraryDependency: + switch tag { + case libTag: + sdkVersion := String(j.properties.Sdk_version) + linkType := javaSdk + if strings.HasPrefix(sdkVersion, "system_") || strings.HasPrefix(sdkVersion, "test_") { + linkType = javaSystem + } else if sdkVersion == "" { + linkType = javaPlatform + } + deps.classpath = append(deps.classpath, dep.HeaderJars(linkType)...) + default: + ctx.ModuleErrorf("dependency on java_sdk_library %q can only be in libs", otherName) + } case android.SourceFileProducer: switch tag { case libTag: diff --git a/java/java.go b/java/java.go index 277cdafd9..8c2312476 100644 --- a/java/java.go +++ b/java/java.go @@ -292,6 +292,10 @@ type Dependency interface { AidlIncludeDirs() android.Paths } +type SdkLibraryDependency interface { + HeaderJars(linkType linkType) android.Paths +} + type SrcDependency interface { CompiledSrcs() android.Paths CompiledSrcJars() android.Paths @@ -729,6 +733,13 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { } deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...) + case SdkLibraryDependency: + switch tag { + case libTag: + deps.classpath = append(deps.classpath, dep.HeaderJars(getLinkType(j, ctx.ModuleName()))...) + default: + ctx.ModuleErrorf("dependency on java_sdk_library %q can only be in libs", otherName) + } case android.SourceFileProducer: switch tag { case libTag: diff --git a/java/java_test.go b/java/java_test.go index d4f2be19e..de514e048 100644 --- a/java/java_test.go +++ b/java/java_test.go @@ -83,9 +83,13 @@ func testContext(config android.Config, bp string, ctx.RegisterModuleType("droiddoc", android.ModuleFactoryAdaptor(DroiddocFactory)) ctx.RegisterModuleType("droiddoc_host", android.ModuleFactoryAdaptor(DroiddocHostFactory)) ctx.RegisterModuleType("droiddoc_template", android.ModuleFactoryAdaptor(DroiddocTemplateFactory)) + ctx.RegisterModuleType("java_sdk_library", android.ModuleFactoryAdaptor(sdkLibraryFactory)) ctx.PreArchMutators(android.RegisterPrebuiltsPreArchMutators) ctx.PreArchMutators(android.RegisterPrebuiltsPostDepsMutators) ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators) + ctx.PreArchMutators(func(ctx android.RegisterMutatorsContext) { + ctx.TopDown("java_sdk_library", sdkLibraryMutator).Parallel() + }) ctx.RegisterPreSingletonType("overlay", android.SingletonFactoryAdaptor(OverlaySingletonFactory)) ctx.Register() @@ -998,3 +1002,60 @@ func TestExcludeFileGroupInSrcs(t *testing.T) { t.Errorf(`foo inputs %v != ["java-fg/c.java"]`, javac.Inputs) } } + +func TestJavaSdkLibrary(t *testing.T) { + ctx := testJava(t, ` + droiddoc_template { + name: "droiddoc-templates-sdk", + path: ".", + } + java_library { + name: "conscrypt", + } + java_library { + name: "bouncycastle", + } + java_sdk_library { + name: "foo", + srcs: ["a.java", "b.java"], + api_packages: ["foo"], + } + java_sdk_library { + name: "bar", + srcs: ["a.java", "b.java"], + api_packages: ["bar"], + } + java_library { + name: "baz", + srcs: ["c.java"], + libs: ["foo", "bar"], + sdk_version: "system_current", + } + `) + + // check the existence of the internal modules + ctx.ModuleForTests("foo", "android_common") + ctx.ModuleForTests("foo"+sdkStubsLibrarySuffix, "android_common") + ctx.ModuleForTests("foo"+sdkStubsLibrarySuffix+sdkSystemApiSuffix, "android_common") + ctx.ModuleForTests("foo"+sdkDocsSuffix, "android_common") + ctx.ModuleForTests("foo"+sdkDocsSuffix+sdkSystemApiSuffix, "android_common") + ctx.ModuleForTests("foo"+sdkImplLibrarySuffix, "android_common") + ctx.ModuleForTests("foo"+sdkXmlFileSuffix, "") + + bazJavac := ctx.ModuleForTests("baz", "android_common").Rule("javac") + // tests if baz is actually linked to the stubs lib + if !strings.Contains(bazJavac.Args["classpath"], "foo.stubs.system.jar") { + t.Errorf("baz javac classpath %v does not contain %q", bazJavac.Args["classpath"], + "foo.stubs.system.jar") + } + // ... and not to the impl lib + if strings.Contains(bazJavac.Args["classpath"], "foo.impl.jar") { + t.Errorf("baz javac classpath %v should not contain %q", bazJavac.Args["classpath"], + "foo.impl.jar") + } + // test if baz is not linked to the system variant of foo + if strings.Contains(bazJavac.Args["classpath"], "foo.stubs.jar") { + t.Errorf("baz javac classpath %v should not contain %q", bazJavac.Args["classpath"], + "foo.stubs.jar") + } +} diff --git a/java/sdk_library.go b/java/sdk_library.go new file mode 100644 index 000000000..301ec6120 --- /dev/null +++ b/java/sdk_library.go @@ -0,0 +1,425 @@ +// Copyright 2018 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 + +import ( + "android/soong/android" + "android/soong/genrule" + "fmt" + "path" + "strings" + + "github.com/google/blueprint" + "github.com/google/blueprint/proptools" +) + +var ( + sdkStubsLibrarySuffix = ".stubs" + sdkSystemApiSuffix = ".system" + sdkDocsSuffix = ".docs" + sdkImplLibrarySuffix = ".impl" + sdkXmlFileSuffix = ".xml" +) + +type stubsLibraryDependencyTag struct { + blueprint.BaseDependencyTag + name string +} + +var ( + publicApiStubsTag = dependencyTag{name: "public"} + systemApiStubsTag = dependencyTag{name: "system"} +) + +// java_sdk_library is to make a Java library that implements optional platform APIs to apps. +// It is actually a wrapper of several modules: 1) stubs library that clients are linked against +// to, 2) droiddoc module that internally generates API stubs source files, 3) the real runtime +// shared library that implements the APIs, and 4) XML file for adding the runtime lib to the +// classpath at runtime if requested via . +// +// TODO: these are big features that are currently missing +// 1) check for API consistency +// 2) install stubs libs as the dist artifacts +// 3) ensuring that apps have appropriate tag +// 4) disallowing linking to the runtime shared lib +// 5) HTML generation + +func init() { + android.RegisterModuleType("java_sdk_library", sdkLibraryFactory) + + android.PreArchMutators(func(ctx android.RegisterMutatorsContext) { + ctx.TopDown("java_sdk_library", sdkLibraryMutator).Parallel() + }) +} + +type sdkLibraryProperties struct { + // list of source files used to compile the Java module. May be .java, .logtags, .proto, + // or .aidl files. + Srcs []string `android:"arch_variant"` + + // list of of java libraries that will be in the classpath + Libs []string `android:"arch_variant"` + + // list of java libraries that will be compiled into the resulting runtime jar. + // These libraries are not compiled into the stubs jar. + Static_libs []string `android:"arch_variant"` + + // list of package names that will be documented and publicized as API + Api_packages []string + + // TODO: determines whether to create HTML doc or not + //Html_doc *bool +} + +type sdkLibrary struct { + android.ModuleBase + android.DefaultableModuleBase + + properties sdkLibraryProperties + + publicApiStubsPath android.Paths + systemApiStubsPath android.Paths +} + +func (module *sdkLibrary) DepsMutator(ctx android.BottomUpMutatorContext) { + // Add dependencies to the stubs library + ctx.AddDependency(ctx.Module(), publicApiStubsTag, module.stubsName(false)) + ctx.AddDependency(ctx.Module(), systemApiStubsTag, module.stubsName(true)) +} + +func (module *sdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { + // Record the paths to the header jars of the stubs library. + // When this java_sdk_library is dependened from others via "libs" property, + // the recorded paths will be returned depending on the link type of the caller. + ctx.VisitDirectDeps(func(to android.Module) { + otherName := ctx.OtherModuleName(to) + tag := ctx.OtherModuleDependencyTag(to) + + if stubs, ok := to.(Dependency); ok { + switch tag { + case publicApiStubsTag: + module.publicApiStubsPath = stubs.HeaderJars() + case systemApiStubsTag: + module.systemApiStubsPath = stubs.HeaderJars() + default: + ctx.ModuleErrorf("depends on module %q of unknown tag %q", otherName, tag) + } + } + }) +} + +// Module name of the stubs library +func (module *sdkLibrary) stubsName(forSystemApi bool) string { + stubsName := module.BaseModuleName() + sdkStubsLibrarySuffix + if forSystemApi { + stubsName = stubsName + sdkSystemApiSuffix + } + return stubsName +} + +// Module name of the docs +func (module *sdkLibrary) docsName(forSystemApi bool) string { + docsName := module.BaseModuleName() + sdkDocsSuffix + if forSystemApi { + docsName = docsName + sdkSystemApiSuffix + } + return docsName +} + +// Module name of the runtime implementation library +func (module *sdkLibrary) implName() string { + return module.BaseModuleName() + sdkImplLibrarySuffix +} + +// File path to the runtime implementation library +func (module *sdkLibrary) implPath() string { + partition := "system" + if module.SocSpecific() { + partition = "vendor" + } else if module.DeviceSpecific() { + partition = "odm" + } else if module.ProductSpecific() { + partition = "product" + } + return "/" + partition + "/framework/" + module.implName() + ".jar" +} + +// Module name of the XML file for the lib +func (module *sdkLibrary) xmlFileName() string { + return module.BaseModuleName() + sdkXmlFileSuffix +} + +// SDK version that the stubs library is built against. Note that this is always +// *current. Older stubs library built with a numberd SDK version is created from +// the prebuilt jar. +func (module *sdkLibrary) sdkVersion(forSystemApi bool) string { + if forSystemApi { + return "system_current" + } else { + return "current" + } +} + +// $(INTERNAL_PLATFORM__API_FILE) points to the generated +// api file for the current source +// TODO: remove this when apicheck is done in soong +func (module *sdkLibrary) apiTagName(forSystemApi bool) string { + apiTagName := strings.Replace(strings.ToUpper(module.BaseModuleName()), ".", "_", -1) + if forSystemApi { + apiTagName = apiTagName + "_SYSTEM" + } + return apiTagName +} + +// returns the path (relative to this module) to the API txt file. Files are located +// .//.txt where is either current, system-current, removed, +// or system-removed. +func (module *sdkLibrary) apiFilePath(apiLevel string, forSystemApi bool) string { + apiDir := "api" + apiFile := apiLevel + if forSystemApi { + apiFile = "system-" + apiFile + } + apiFile = apiFile + ".txt" + + return path.Join(apiDir, apiFile) +} + +// Creates a static java library that has API stubs +func (module *sdkLibrary) createStubsLibrary(mctx android.TopDownMutatorContext, forSystemApi bool) { + props := struct { + Name *string + Srcs []string + Sdk_version *string + Soc_specific *bool + Device_specific *bool + Product_specific *bool + Product_variables struct { + Unbundled_build struct { + Enabled *bool + } + } + }{} + + props.Name = proptools.StringPtr(module.stubsName(forSystemApi)) + // sources are generated from the droiddoc + props.Srcs = []string{":" + module.docsName(forSystemApi)} + props.Sdk_version = proptools.StringPtr(module.sdkVersion(forSystemApi)) + // Unbundled apps will use the prebult one from /prebuilts/sdk + props.Product_variables.Unbundled_build.Enabled = proptools.BoolPtr(false) + + if module.SocSpecific() { + props.Soc_specific = proptools.BoolPtr(true) + } else if module.DeviceSpecific() { + props.Device_specific = proptools.BoolPtr(true) + } else if module.ProductSpecific() { + props.Product_specific = proptools.BoolPtr(true) + } + + mctx.CreateModule(android.ModuleFactoryAdaptor(LibraryFactory(false)), &props) +} + +// Creates a droiddoc module that creates stubs source files from the given full source +// files +func (module *sdkLibrary) createDocs(mctx android.TopDownMutatorContext, forSystemApi bool) { + props := struct { + Name *string + Srcs []string + Custom_template *string + Installable *bool + Srcs_lib *string + Srcs_lib_whitelist_dirs []string + Srcs_lib_whitelist_pkgs []string + Libs []string + Args *string + Api_tag_name *string + Api_filename *string + Removed_api_filename *string + }{} + + props.Name = proptools.StringPtr(module.docsName(forSystemApi)) + props.Srcs = module.properties.Srcs + props.Custom_template = proptools.StringPtr("droiddoc-templates-sdk") + props.Installable = proptools.BoolPtr(false) + props.Libs = module.properties.Libs + + droiddocArgs := " -hide 110 -hide 111 -hide 113 -hide 121 -hide 125 -hide 126 -hide 127 -hide 128" + + " -stubpackages " + strings.Join(module.properties.Api_packages, ":") + + " -nodocs" + if forSystemApi { + droiddocArgs = droiddocArgs + " -showAnnotation android.annotation.SystemApi" + } + props.Args = proptools.StringPtr(droiddocArgs) + + // List of APIs identified from the provided source files are created. They are later + // compared against to the not-yet-released (a.k.a current) list of APIs and to the + // last-released (a.k.a numbered) list of API. + // TODO: If any incompatible change is detected, break the build + currentApiFileName := "current.txt" + removedApiFileName := "removed.txt" + if forSystemApi { + currentApiFileName = "system-" + currentApiFileName + removedApiFileName = "system-" + removedApiFileName + } + currentApiFileName = path.Join("api", currentApiFileName) + removedApiFileName = path.Join("api", removedApiFileName) + props.Api_tag_name = proptools.StringPtr(module.apiTagName(forSystemApi)) + // Note: the exact names of these two are not important because they are always + // referenced by the make variable $(INTERNAL_PLATFORM__API_FILE) + props.Api_filename = proptools.StringPtr(currentApiFileName) + props.Removed_api_filename = proptools.StringPtr(removedApiFileName) + + // Includes the main framework source to ensure that doclava has access to the + // visibility information for the base classes of the mock classes. Without it + // otherwise hidden methods could be visible. + // TODO: remove the need for this + props.Srcs_lib = proptools.StringPtr("framework") + props.Srcs_lib_whitelist_dirs = []string{"core/java"} + props.Srcs_lib_whitelist_pkgs = []string{"android"} + // These libs are required by doclava to parse the sources from framework. + // If we don't add them to the classpath, errors messages are generated by doclava, + // though they don't break the build. + props.Libs = append(props.Libs, "conscrypt", "bouncycastle", "okhttp") + + mctx.CreateModule(android.ModuleFactoryAdaptor(DroiddocFactory), &props) +} + +// Creates the runtime library. This is not directly linkable from other modules. +func (module *sdkLibrary) createImplLibrary(mctx android.TopDownMutatorContext) { + props := struct { + Name *string + Srcs []string + Libs []string + Static_libs []string + Soc_specific *bool + Device_specific *bool + Product_specific *bool + Required []string + }{} + + props.Name = proptools.StringPtr(module.implName()) + props.Srcs = module.properties.Srcs + props.Libs = module.properties.Libs + props.Static_libs = module.properties.Static_libs + // XML file is installed along with the impl lib + props.Required = []string{module.xmlFileName()} + + if module.SocSpecific() { + props.Soc_specific = proptools.BoolPtr(true) + } else if module.DeviceSpecific() { + props.Device_specific = proptools.BoolPtr(true) + } else if module.ProductSpecific() { + props.Product_specific = proptools.BoolPtr(true) + } + + mctx.CreateModule(android.ModuleFactoryAdaptor(LibraryFactory(true)), &props) +} + +// Creates the xml file that publicizes the runtime library +func (module *sdkLibrary) createXmlFile(mctx android.TopDownMutatorContext) { + template := ` + + + + + + +` + // genrule to generate the xml file content from the template above + // TODO: preserve newlines in the generate xml file. Newlines are being squashed + // in the ninja file. Do we need to have an external tool for this? + xmlContent := fmt.Sprintf(template, module.BaseModuleName(), module.implPath()) + genruleProps := struct { + Name *string + Cmd *string + Out []string + }{} + genruleProps.Name = proptools.StringPtr(module.xmlFileName() + "-gen") + genruleProps.Cmd = proptools.StringPtr("echo '" + xmlContent + "' > $(out)") + genruleProps.Out = []string{module.xmlFileName()} + mctx.CreateModule(android.ModuleFactoryAdaptor(genrule.GenRuleFactory), &genruleProps) + + // creates a prebuilt_etc module to actually place the xml file under + // /etc/permissions + etcProps := struct { + Name *string + Srcs []string + Sub_dir *string + Soc_specific *bool + Device_specific *bool + Product_specific *bool + }{} + etcProps.Name = proptools.StringPtr(module.xmlFileName()) + etcProps.Srcs = []string{":" + module.xmlFileName() + "-gen"} + etcProps.Sub_dir = proptools.StringPtr("permissions") + if module.SocSpecific() { + etcProps.Soc_specific = proptools.BoolPtr(true) + } else if module.DeviceSpecific() { + etcProps.Device_specific = proptools.BoolPtr(true) + } else if module.ProductSpecific() { + etcProps.Product_specific = proptools.BoolPtr(true) + } + mctx.CreateModule(android.ModuleFactoryAdaptor(android.PrebuiltEtcFactory), &etcProps) +} + +// to satisfy SdkLibraryDependency interface +func (module *sdkLibrary) HeaderJars(linkType linkType) android.Paths { + // This module is just a wrapper for the stubs. + if linkType == javaSystem || linkType == javaPlatform { + return module.systemApiStubsPath + } else { + return module.publicApiStubsPath + } +} + +// For a java_sdk_library module, create internal modules for stubs, docs, +// runtime libs and xml file. If requested, the stubs and docs are created twice +// once for public API level and once for system API level +func sdkLibraryMutator(mctx android.TopDownMutatorContext) { + if module, ok := mctx.Module().(*sdkLibrary); ok { + // for public API stubs + module.createStubsLibrary(mctx, false) + module.createDocs(mctx, false) + + // for system API stubs + module.createStubsLibrary(mctx, true) + module.createDocs(mctx, true) + + // for runtime + module.createXmlFile(mctx) + module.createImplLibrary(mctx) + } +} + +func sdkLibraryFactory() android.Module { + module := &sdkLibrary{} + module.AddProperties(&module.properties) + android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon) + android.InitDefaultableModule(module) + return module +}