diff options
Diffstat (limited to 'java/base.go')
-rw-r--r-- | java/base.go | 1787 |
1 files changed, 1787 insertions, 0 deletions
diff --git a/java/base.go b/java/base.go new file mode 100644 index 000000000..df70efb44 --- /dev/null +++ b/java/base.go @@ -0,0 +1,1787 @@ +// Copyright 2021 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 ( + "fmt" + "path/filepath" + "strconv" + "strings" + + "github.com/google/blueprint/pathtools" + "github.com/google/blueprint/proptools" + + "android/soong/android" + "android/soong/dexpreopt" + "android/soong/java/config" +) + +// This file contains the definition and the implementation of the base module that most +// source-based Java module structs embed. + +// TODO: +// Autogenerated files: +// Renderscript +// Post-jar passes: +// Proguard +// Rmtypedefs +// DroidDoc +// Findbugs + +// Properties that are common to most Java modules, i.e. whether it's a host or device module. +type CommonProperties struct { + // list of source files used to compile the Java module. May be .java, .kt, .logtags, .proto, + // or .aidl files. + Srcs []string `android:"path,arch_variant"` + + // list Kotlin of source files containing Kotlin code that should be treated as common code in + // a codebase that supports Kotlin multiplatform. See + // https://kotlinlang.org/docs/reference/multiplatform.html. May be only be .kt files. + Common_srcs []string `android:"path,arch_variant"` + + // list of source files that should not be used to build the Java module. + // This is most useful in the arch/multilib variants to remove non-common files + Exclude_srcs []string `android:"path,arch_variant"` + + // list of directories containing Java resources + Java_resource_dirs []string `android:"arch_variant"` + + // list of directories that should be excluded from java_resource_dirs + Exclude_java_resource_dirs []string `android:"arch_variant"` + + // list of files to use as Java resources + Java_resources []string `android:"path,arch_variant"` + + // list of files that should be excluded from java_resources and java_resource_dirs + Exclude_java_resources []string `android:"path,arch_variant"` + + // list of module-specific flags that will be used for javac compiles + Javacflags []string `android:"arch_variant"` + + // list of module-specific flags that will be used for kotlinc compiles + Kotlincflags []string `android:"arch_variant"` + + // list 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 jar + Static_libs []string `android:"arch_variant"` + + // manifest file to be included in resulting jar + Manifest *string `android:"path"` + + // if not blank, run jarjar using the specified rules file + Jarjar_rules *string `android:"path,arch_variant"` + + // If not blank, set the java version passed to javac as -source and -target + Java_version *string + + // If set to true, allow this module to be dexed and installed on devices. Has no + // effect on host modules, which are always considered installable. + Installable *bool + + // If set to true, include sources used to compile the module in to the final jar + Include_srcs *bool + + // If not empty, classes are restricted to the specified packages and their sub-packages. + // This restriction is checked after applying jarjar rules and including static libs. + Permitted_packages []string + + // List of modules to use as annotation processors + Plugins []string + + // List of modules to export to libraries that directly depend on this library as annotation + // processors. Note that if the plugins set generates_api: true this will disable the turbine + // optimization on modules that depend on this module, which will reduce parallelism and cause + // more recompilation. + Exported_plugins []string + + // The number of Java source entries each Javac instance can process + Javac_shard_size *int64 + + // Add host jdk tools.jar to bootclasspath + Use_tools_jar *bool + + Openjdk9 struct { + // List of source files that should only be used when passing -source 1.9 or higher + Srcs []string `android:"path"` + + // List of javac flags that should only be used when passing -source 1.9 or higher + Javacflags []string + } + + // When compiling language level 9+ .java code in packages that are part of + // a system module, patch_module names the module that your sources and + // dependencies should be patched into. The Android runtime currently + // doesn't implement the JEP 261 module system so this option is only + // supported at compile time. It should only be needed to compile tests in + // packages that exist in libcore and which are inconvenient to move + // elsewhere. + Patch_module *string `android:"arch_variant"` + + Jacoco struct { + // List of classes to include for instrumentation with jacoco to collect coverage + // information at runtime when building with coverage enabled. If unset defaults to all + // classes. + // Supports '*' as the last character of an entry in the list as a wildcard match. + // If preceded by '.' it matches all classes in the package and subpackages, otherwise + // it matches classes in the package that have the class name as a prefix. + Include_filter []string + + // List of classes to exclude from instrumentation with jacoco to collect coverage + // information at runtime when building with coverage enabled. Overrides classes selected + // by the include_filter property. + // Supports '*' as the last character of an entry in the list as a wildcard match. + // If preceded by '.' it matches all classes in the package and subpackages, otherwise + // it matches classes in the package that have the class name as a prefix. + Exclude_filter []string + } + + Errorprone struct { + // List of javac flags that should only be used when running errorprone. + Javacflags []string + + // List of java_plugin modules that provide extra errorprone checks. + Extra_check_modules []string + } + + Proto struct { + // List of extra options that will be passed to the proto generator. + Output_params []string + } + + Instrument bool `blueprint:"mutated"` + + // List of files to include in the META-INF/services folder of the resulting jar. + Services []string `android:"path,arch_variant"` + + // If true, package the kotlin stdlib into the jar. Defaults to true. + Static_kotlin_stdlib *bool `android:"arch_variant"` + + // A list of java_library instances that provide additional hiddenapi annotations for the library. + Hiddenapi_additional_annotations []string +} + +// Properties that are specific to device modules. Host module factories should not add these when +// constructing a new module. +type DeviceProperties struct { + // if not blank, set to the version of the sdk to compile against. + // Defaults to compiling against the current platform. + Sdk_version *string + + // if not blank, set the minimum version of the sdk that the compiled artifacts will run against. + // Defaults to sdk_version if not set. + Min_sdk_version *string + + // if not blank, set the targetSdkVersion in the AndroidManifest.xml. + // Defaults to sdk_version if not set. + Target_sdk_version *string + + // Whether to compile against the platform APIs instead of an SDK. + // If true, then sdk_version must be empty. The value of this field + // is ignored when module's type isn't android_app. + Platform_apis *bool + + Aidl struct { + // Top level directories to pass to aidl tool + Include_dirs []string + + // Directories rooted at the Android.bp file to pass to aidl tool + Local_include_dirs []string + + // directories that should be added as include directories for any aidl sources of modules + // that depend on this module, as well as to aidl for this module. + Export_include_dirs []string + + // whether to generate traces (for systrace) for this interface + Generate_traces *bool + + // whether to generate Binder#GetTransaction name method. + Generate_get_transaction_name *bool + + // list of flags that will be passed to the AIDL compiler + Flags []string + } + + // If true, export a copy of the module as a -hostdex module for host testing. + Hostdex *bool + + Target struct { + Hostdex struct { + // Additional required dependencies to add to -hostdex modules. + Required []string + } + } + + // When targeting 1.9 and above, override the modules to use with --system, + // otherwise provides defaults libraries to add to the bootclasspath. + System_modules *string + + // set the name of the output + Stem *string + + IsSDKLibrary bool `blueprint:"mutated"` + + // If true, generate the signature file of APK Signing Scheme V4, along side the signed APK file. + // Defaults to false. + V4_signature *bool + + // Only for libraries created by a sysprop_library module, SyspropPublicStub is the name of the + // public stubs library. + SyspropPublicStub string `blueprint:"mutated"` +} + +// Functionality common to Module and Import +// +// It is embedded in Module so its functionality can be used by methods in Module +// but it is currently only initialized by Import and Library. +type embeddableInModuleAndImport struct { + + // Functionality related to this being used as a component of a java_sdk_library. + EmbeddableSdkLibraryComponent +} + +func (e *embeddableInModuleAndImport) initModuleAndImport(module android.Module) { + e.initSdkLibraryComponent(module) +} + +// Module/Import's DepIsInSameApex(...) delegates to this method. +// +// This cannot implement DepIsInSameApex(...) directly as that leads to ambiguity with +// the one provided by ApexModuleBase. +func (e *embeddableInModuleAndImport) depIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool { + // dependencies other than the static linkage are all considered crossing APEX boundary + if staticLibTag == ctx.OtherModuleDependencyTag(dep) { + return true + } + return false +} + +// Module contains the properties and members used by all java module types +type Module struct { + android.ModuleBase + android.DefaultableModuleBase + android.ApexModuleBase + android.SdkBase + + // Functionality common to Module and Import. + embeddableInModuleAndImport + + properties CommonProperties + protoProperties android.ProtoProperties + deviceProperties DeviceProperties + + // jar file containing header classes including static library dependencies, suitable for + // inserting into the bootclasspath/classpath of another compile + headerJarFile android.Path + + // jar file containing implementation classes including static library dependencies but no + // resources + implementationJarFile android.Path + + // jar file containing only resources including from static library dependencies + resourceJar android.Path + + // args and dependencies to package source files into a srcjar + srcJarArgs []string + srcJarDeps android.Paths + + // jar file containing implementation classes and resources including static library + // dependencies + implementationAndResourcesJar android.Path + + // output file containing classes.dex and resources + dexJarFile android.Path + + // output file containing uninstrumented classes that will be instrumented by jacoco + jacocoReportClassesFile android.Path + + // output file of the module, which may be a classes jar or a dex jar + outputFile android.Path + extraOutputFiles android.Paths + + exportAidlIncludeDirs android.Paths + + logtagsSrcs android.Paths + + // installed file for binary dependency + installFile android.Path + + // list of .java files and srcjars that was passed to javac + compiledJavaSrcs android.Paths + compiledSrcJars android.Paths + + // manifest file to use instead of properties.Manifest + overrideManifest android.OptionalPath + + // map of SDK version to class loader context + classLoaderContexts dexpreopt.ClassLoaderContextMap + + // list of plugins that this java module is exporting + exportedPluginJars android.Paths + + // list of plugins that this java module is exporting + exportedPluginClasses []string + + // if true, the exported plugins generate API and require disabling turbine. + exportedDisableTurbine bool + + // list of source files, collected from srcFiles with unique java and all kt files, + // will be used by android.IDEInfo struct + expandIDEInfoCompiledSrcs []string + + // expanded Jarjar_rules + expandJarjarRules android.Path + + // list of additional targets for checkbuild + additionalCheckedModules android.Paths + + // Extra files generated by the module type to be added as java resources. + extraResources android.Paths + + hiddenAPI + dexer + dexpreopter + usesLibrary + linter + + // list of the xref extraction files + kytheFiles android.Paths + + // Collect the module directory for IDE info in java/jdeps.go. + modulePaths []string + + hideApexVariantFromMake bool + + sdkVersion android.SdkSpec + minSdkVersion android.SdkSpec +} + +func (j *Module) CheckStableSdkVersion(ctx android.BaseModuleContext) error { + sdkVersion := j.SdkVersion(ctx) + if sdkVersion.Stable() { + return nil + } + if sdkVersion.Kind == android.SdkCorePlatform { + if useLegacyCorePlatformApiByName(j.BaseModuleName()) { + return fmt.Errorf("non stable SDK %v - uses legacy core platform", sdkVersion) + } else { + // Treat stable core platform as stable. + return nil + } + } else { + return fmt.Errorf("non stable SDK %v", sdkVersion) + } +} + +// checkSdkVersions enforces restrictions around SDK dependencies. +func (j *Module) checkSdkVersions(ctx android.ModuleContext) { + if j.RequiresStableAPIs(ctx) { + if sc, ok := ctx.Module().(android.SdkContext); ok { + if !sc.SdkVersion(ctx).Specified() { + ctx.PropertyErrorf("sdk_version", + "sdk_version must have a value when the module is located at vendor or product(only if PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE is set).") + } + } + } + + // Make sure this module doesn't statically link to modules with lower-ranked SDK link type. + // See rank() for details. + ctx.VisitDirectDeps(func(module android.Module) { + tag := ctx.OtherModuleDependencyTag(module) + switch module.(type) { + // TODO(satayev): cover other types as well, e.g. imports + case *Library, *AndroidLibrary: + switch tag { + case bootClasspathTag, libTag, staticLibTag, java9LibTag: + j.checkSdkLinkType(ctx, module.(moduleWithSdkDep), tag.(dependencyTag)) + } + } + }) +} + +func (j *Module) checkPlatformAPI(ctx android.ModuleContext) { + if sc, ok := ctx.Module().(android.SdkContext); ok { + usePlatformAPI := proptools.Bool(j.deviceProperties.Platform_apis) + sdkVersionSpecified := sc.SdkVersion(ctx).Specified() + if usePlatformAPI && sdkVersionSpecified { + ctx.PropertyErrorf("platform_apis", "platform_apis must be false when sdk_version is not empty.") + } else if !usePlatformAPI && !sdkVersionSpecified { + ctx.PropertyErrorf("platform_apis", "platform_apis must be true when sdk_version is empty.") + } + + } +} + +func (j *Module) addHostProperties() { + j.AddProperties( + &j.properties, + &j.protoProperties, + &j.usesLibraryProperties, + ) +} + +func (j *Module) addHostAndDeviceProperties() { + j.addHostProperties() + j.AddProperties( + &j.deviceProperties, + &j.dexer.dexProperties, + &j.dexpreoptProperties, + &j.linter.properties, + ) +} + +func (j *Module) OutputFiles(tag string) (android.Paths, error) { + switch tag { + case "": + return append(android.Paths{j.outputFile}, j.extraOutputFiles...), nil + case android.DefaultDistTag: + return android.Paths{j.outputFile}, nil + case ".jar": + return android.Paths{j.implementationAndResourcesJar}, nil + case ".proguard_map": + if j.dexer.proguardDictionary.Valid() { + return android.Paths{j.dexer.proguardDictionary.Path()}, nil + } + return nil, fmt.Errorf("%q was requested, but no output file was found.", tag) + default: + return nil, fmt.Errorf("unsupported module reference tag %q", tag) + } +} + +var _ android.OutputFileProducer = (*Module)(nil) + +func InitJavaModule(module android.DefaultableModule, hod android.HostOrDeviceSupported) { + initJavaModule(module, hod, false) +} + +func InitJavaModuleMultiTargets(module android.DefaultableModule, hod android.HostOrDeviceSupported) { + initJavaModule(module, hod, true) +} + +func initJavaModule(module android.DefaultableModule, hod android.HostOrDeviceSupported, multiTargets bool) { + multilib := android.MultilibCommon + if multiTargets { + android.InitAndroidMultiTargetsArchModule(module, hod, multilib) + } else { + android.InitAndroidArchModule(module, hod, multilib) + } + android.InitDefaultableModule(module) +} + +func (j *Module) shouldInstrument(ctx android.BaseModuleContext) bool { + return j.properties.Instrument && + ctx.Config().IsEnvTrue("EMMA_INSTRUMENT") && + ctx.DeviceConfig().JavaCoverageEnabledForPath(ctx.ModuleDir()) +} + +func (j *Module) shouldInstrumentStatic(ctx android.BaseModuleContext) bool { + return j.shouldInstrument(ctx) && + (ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_STATIC") || + ctx.Config().UnbundledBuild()) +} + +func (j *Module) shouldInstrumentInApex(ctx android.BaseModuleContext) bool { + // Force enable the instrumentation for java code that is built for APEXes ... + // except for the jacocoagent itself (because instrumenting jacocoagent using jacocoagent + // doesn't make sense) or framework libraries (e.g. libraries found in the InstrumentFrameworkModules list) unless EMMA_INSTRUMENT_FRAMEWORK is true. + apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) + isJacocoAgent := ctx.ModuleName() == "jacocoagent" + if j.DirectlyInAnyApex() && !isJacocoAgent && !apexInfo.IsForPlatform() { + if !inList(ctx.ModuleName(), config.InstrumentFrameworkModules) { + return true + } else if ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") { + return true + } + } + return false +} + +func (j *Module) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec { + return android.SdkSpecFrom(ctx, String(j.deviceProperties.Sdk_version)) +} + +func (j *Module) SystemModules() string { + return proptools.String(j.deviceProperties.System_modules) +} + +func (j *Module) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec { + if j.deviceProperties.Min_sdk_version != nil { + return android.SdkSpecFrom(ctx, *j.deviceProperties.Min_sdk_version) + } + return j.SdkVersion(ctx) +} + +func (j *Module) MinSdkVersionString() string { + return j.minSdkVersion.Raw +} + +func (j *Module) TargetSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec { + if j.deviceProperties.Target_sdk_version != nil { + return android.SdkSpecFrom(ctx, *j.deviceProperties.Target_sdk_version) + } + return j.SdkVersion(ctx) +} + +func (j *Module) AvailableFor(what string) bool { + if what == android.AvailableToPlatform && Bool(j.deviceProperties.Hostdex) { + // Exception: for hostdex: true libraries, the platform variant is created + // even if it's not marked as available to platform. In that case, the platform + // variant is used only for the hostdex and not installed to the device. + return true + } + return j.ApexModuleBase.AvailableFor(what) +} + +func (j *Module) deps(ctx android.BottomUpMutatorContext) { + if ctx.Device() { + j.linter.deps(ctx) + + sdkDeps(ctx, android.SdkContext(j), j.dexer) + + if j.deviceProperties.SyspropPublicStub != "" { + // This is a sysprop implementation library that has a corresponding sysprop public + // stubs library, and a dependency on it so that dependencies on the implementation can + // be forwarded to the public stubs library when necessary. + ctx.AddVariationDependencies(nil, syspropPublicStubDepTag, j.deviceProperties.SyspropPublicStub) + } + } + + libDeps := ctx.AddVariationDependencies(nil, libTag, j.properties.Libs...) + ctx.AddVariationDependencies(nil, staticLibTag, j.properties.Static_libs...) + + // Add dependency on libraries that provide additional hidden api annotations. + ctx.AddVariationDependencies(nil, hiddenApiAnnotationsTag, j.properties.Hiddenapi_additional_annotations...) + + if ctx.DeviceConfig().VndkVersion() != "" && ctx.Config().EnforceInterPartitionJavaSdkLibrary() { + // Require java_sdk_library at inter-partition java dependency to ensure stable + // interface between partitions. If inter-partition java_library dependency is detected, + // raise build error because java_library doesn't have a stable interface. + // + // Inputs: + // PRODUCT_ENFORCE_INTER_PARTITION_JAVA_SDK_LIBRARY + // if true, enable enforcement + // PRODUCT_INTER_PARTITION_JAVA_LIBRARY_ALLOWLIST + // exception list of java_library names to allow inter-partition dependency + for idx := range j.properties.Libs { + if libDeps[idx] == nil { + continue + } + + if javaDep, ok := libDeps[idx].(javaSdkLibraryEnforceContext); ok { + // java_sdk_library is always allowed at inter-partition dependency. + // So, skip check. + if _, ok := javaDep.(*SdkLibrary); ok { + continue + } + + j.checkPartitionsForJavaDependency(ctx, "libs", javaDep) + } + } + } + + // For library dependencies that are component libraries (like stubs), add the implementation + // as a dependency (dexpreopt needs to be against the implementation library, not stubs). + for _, dep := range libDeps { + if dep != nil { + if component, ok := dep.(SdkLibraryComponentDependency); ok { + if lib := component.OptionalSdkLibraryImplementation(); lib != nil { + ctx.AddVariationDependencies(nil, usesLibTag, *lib) + } + } + } + } + + ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), pluginTag, j.properties.Plugins...) + ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), errorpronePluginTag, j.properties.Errorprone.Extra_check_modules...) + ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), exportedPluginTag, j.properties.Exported_plugins...) + + android.ProtoDeps(ctx, &j.protoProperties) + if j.hasSrcExt(".proto") { + protoDeps(ctx, &j.protoProperties) + } + + if j.hasSrcExt(".kt") { + // TODO(ccross): move this to a mutator pass that can tell if generated sources contain + // Kotlin files + ctx.AddVariationDependencies(nil, kotlinStdlibTag, + "kotlin-stdlib", "kotlin-stdlib-jdk7", "kotlin-stdlib-jdk8") + if len(j.properties.Plugins) > 0 { + ctx.AddVariationDependencies(nil, kotlinAnnotationsTag, "kotlin-annotations") + } + } + + // Framework libraries need special handling in static coverage builds: they should not have + // static dependency on jacoco, otherwise there would be multiple conflicting definitions of + // the same jacoco classes coming from different bootclasspath jars. + if inList(ctx.ModuleName(), config.InstrumentFrameworkModules) { + if ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") { + j.properties.Instrument = true + } + } else if j.shouldInstrumentStatic(ctx) { + ctx.AddVariationDependencies(nil, staticLibTag, "jacocoagent") + } +} + +func hasSrcExt(srcs []string, ext string) bool { + for _, src := range srcs { + if filepath.Ext(src) == ext { + return true + } + } + + return false +} + +func (j *Module) hasSrcExt(ext string) bool { + return hasSrcExt(j.properties.Srcs, ext) +} + +func (j *Module) aidlFlags(ctx android.ModuleContext, aidlPreprocess android.OptionalPath, + aidlIncludeDirs android.Paths) (string, android.Paths) { + + aidlIncludes := android.PathsForModuleSrc(ctx, j.deviceProperties.Aidl.Local_include_dirs) + aidlIncludes = append(aidlIncludes, + android.PathsForModuleSrc(ctx, j.deviceProperties.Aidl.Export_include_dirs)...) + aidlIncludes = append(aidlIncludes, + android.PathsForSource(ctx, j.deviceProperties.Aidl.Include_dirs)...) + + var flags []string + var deps android.Paths + + flags = append(flags, j.deviceProperties.Aidl.Flags...) + + if aidlPreprocess.Valid() { + flags = append(flags, "-p"+aidlPreprocess.String()) + deps = append(deps, aidlPreprocess.Path()) + } else if len(aidlIncludeDirs) > 0 { + flags = append(flags, android.JoinWithPrefix(aidlIncludeDirs.Strings(), "-I")) + } + + if len(j.exportAidlIncludeDirs) > 0 { + flags = append(flags, android.JoinWithPrefix(j.exportAidlIncludeDirs.Strings(), "-I")) + } + + if len(aidlIncludes) > 0 { + flags = append(flags, android.JoinWithPrefix(aidlIncludes.Strings(), "-I")) + } + + flags = append(flags, "-I"+android.PathForModuleSrc(ctx).String()) + if src := android.ExistentPathForSource(ctx, ctx.ModuleDir(), "src"); src.Valid() { + flags = append(flags, "-I"+src.String()) + } + + if Bool(j.deviceProperties.Aidl.Generate_traces) { + flags = append(flags, "-t") + } + + if Bool(j.deviceProperties.Aidl.Generate_get_transaction_name) { + flags = append(flags, "--transaction_names") + } + + return strings.Join(flags, " "), deps +} + +func (j *Module) collectBuilderFlags(ctx android.ModuleContext, deps deps) javaBuilderFlags { + + var flags javaBuilderFlags + + // javaVersion flag. + flags.javaVersion = getJavaVersion(ctx, String(j.properties.Java_version), android.SdkContext(j)) + + if ctx.Config().RunErrorProne() { + if config.ErrorProneClasspath == nil && ctx.Config().TestProductVariables == nil { + ctx.ModuleErrorf("cannot build with Error Prone, missing external/error_prone?") + } + + errorProneFlags := []string{ + "-Xplugin:ErrorProne", + "${config.ErrorProneChecks}", + } + errorProneFlags = append(errorProneFlags, j.properties.Errorprone.Javacflags...) + + flags.errorProneExtraJavacFlags = "${config.ErrorProneFlags} " + + "'" + strings.Join(errorProneFlags, " ") + "'" + flags.errorProneProcessorPath = classpath(android.PathsForSource(ctx, config.ErrorProneClasspath)) + } + + // classpath + flags.bootClasspath = append(flags.bootClasspath, deps.bootClasspath...) + flags.classpath = append(flags.classpath, deps.classpath...) + flags.java9Classpath = append(flags.java9Classpath, deps.java9Classpath...) + flags.processorPath = append(flags.processorPath, deps.processorPath...) + flags.errorProneProcessorPath = append(flags.errorProneProcessorPath, deps.errorProneProcessorPath...) + + flags.processors = append(flags.processors, deps.processorClasses...) + flags.processors = android.FirstUniqueStrings(flags.processors) + + if len(flags.bootClasspath) == 0 && ctx.Host() && !flags.javaVersion.usesJavaModules() && + decodeSdkDep(ctx, android.SdkContext(j)).hasStandardLibs() { + // Give host-side tools a version of OpenJDK's standard libraries + // close to what they're targeting. As of Dec 2017, AOSP is only + // bundling OpenJDK 8 and 9, so nothing < 8 is available. + // + // When building with OpenJDK 8, the following should have no + // effect since those jars would be available by default. + // + // When building with OpenJDK 9 but targeting a version < 1.8, + // putting them on the bootclasspath means that: + // a) code can't (accidentally) refer to OpenJDK 9 specific APIs + // b) references to existing APIs are not reinterpreted in an + // OpenJDK 9-specific way, eg. calls to subclasses of + // java.nio.Buffer as in http://b/70862583 + java8Home := ctx.Config().Getenv("ANDROID_JAVA8_HOME") + flags.bootClasspath = append(flags.bootClasspath, + android.PathForSource(ctx, java8Home, "jre/lib/jce.jar"), + android.PathForSource(ctx, java8Home, "jre/lib/rt.jar")) + if Bool(j.properties.Use_tools_jar) { + flags.bootClasspath = append(flags.bootClasspath, + android.PathForSource(ctx, java8Home, "lib/tools.jar")) + } + } + + // systemModules + flags.systemModules = deps.systemModules + + // aidl flags. + flags.aidlFlags, flags.aidlDeps = j.aidlFlags(ctx, deps.aidlPreprocess, deps.aidlIncludeDirs) + + return flags +} + +func (j *Module) collectJavacFlags( + ctx android.ModuleContext, flags javaBuilderFlags, srcFiles android.Paths) javaBuilderFlags { + // javac flags. + javacFlags := j.properties.Javacflags + + if ctx.Config().MinimizeJavaDebugInfo() && !ctx.Host() { + // For non-host binaries, override the -g flag passed globally to remove + // local variable debug info to reduce disk and memory usage. + javacFlags = append(javacFlags, "-g:source,lines") + } + javacFlags = append(javacFlags, "-Xlint:-dep-ann") + + if flags.javaVersion.usesJavaModules() { + javacFlags = append(javacFlags, j.properties.Openjdk9.Javacflags...) + + if j.properties.Patch_module != nil { + // Manually specify build directory in case it is not under the repo root. + // (javac doesn't seem to expand into symbolic links when searching for patch-module targets, so + // just adding a symlink under the root doesn't help.) + patchPaths := []string{".", ctx.Config().BuildDir()} + + // b/150878007 + // + // Workaround to support *Bazel-executed* JDK9 javac in Bazel's + // execution root for --patch-module. If this javac command line is + // invoked within Bazel's execution root working directory, the top + // level directories (e.g. libcore/, tools/, frameworks/) are all + // symlinks. JDK9 javac does not traverse into symlinks, which causes + // --patch-module to fail source file lookups when invoked in the + // execution root. + // + // Short of patching javac or enumerating *all* directories as possible + // input dirs, manually add the top level dir of the source files to be + // compiled. + topLevelDirs := map[string]bool{} + for _, srcFilePath := range srcFiles { + srcFileParts := strings.Split(srcFilePath.String(), "/") + // Ignore source files that are already in the top level directory + // as well as generated files in the out directory. The out + // directory may be an absolute path, which means srcFileParts[0] is the + // empty string, so check that as well. Note that "out" in Bazel's execution + // root is *not* a symlink, which doesn't cause problems for --patch-modules + // anyway, so it's fine to not apply this workaround for generated + // source files. + if len(srcFileParts) > 1 && + srcFileParts[0] != "" && + srcFileParts[0] != "out" { + topLevelDirs[srcFileParts[0]] = true + } + } + patchPaths = append(patchPaths, android.SortedStringKeys(topLevelDirs)...) + + classPath := flags.classpath.FormJavaClassPath("") + if classPath != "" { + patchPaths = append(patchPaths, classPath) + } + javacFlags = append( + javacFlags, + "--patch-module="+String(j.properties.Patch_module)+"="+strings.Join(patchPaths, ":")) + } + } + + if len(javacFlags) > 0 { + // optimization. + ctx.Variable(pctx, "javacFlags", strings.Join(javacFlags, " ")) + flags.javacFlags = "$javacFlags" + } + + return flags +} + +func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { + j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.deviceProperties.Aidl.Export_include_dirs) + + deps := j.collectDeps(ctx) + flags := j.collectBuilderFlags(ctx, deps) + + if flags.javaVersion.usesJavaModules() { + j.properties.Srcs = append(j.properties.Srcs, j.properties.Openjdk9.Srcs...) + } + srcFiles := android.PathsForModuleSrcExcludes(ctx, j.properties.Srcs, j.properties.Exclude_srcs) + if hasSrcExt(srcFiles.Strings(), ".proto") { + flags = protoFlags(ctx, &j.properties, &j.protoProperties, flags) + } + + kotlinCommonSrcFiles := android.PathsForModuleSrcExcludes(ctx, j.properties.Common_srcs, nil) + if len(kotlinCommonSrcFiles.FilterOutByExt(".kt")) > 0 { + ctx.PropertyErrorf("common_srcs", "common_srcs must be .kt files") + } + + srcFiles = j.genSources(ctx, srcFiles, flags) + + // Collect javac flags only after computing the full set of srcFiles to + // ensure that the --patch-module lookup paths are complete. + flags = j.collectJavacFlags(ctx, flags, srcFiles) + + srcJars := srcFiles.FilterByExt(".srcjar") + srcJars = append(srcJars, deps.srcJars...) + if aaptSrcJar != nil { + srcJars = append(srcJars, aaptSrcJar) + } + + if j.properties.Jarjar_rules != nil { + j.expandJarjarRules = android.PathForModuleSrc(ctx, *j.properties.Jarjar_rules) + } + + jarName := ctx.ModuleName() + ".jar" + + javaSrcFiles := srcFiles.FilterByExt(".java") + var uniqueSrcFiles android.Paths + set := make(map[string]bool) + for _, v := range javaSrcFiles { + if _, found := set[v.String()]; !found { + set[v.String()] = true + uniqueSrcFiles = append(uniqueSrcFiles, v) + } + } + + // Collect .java files for AIDEGen + j.expandIDEInfoCompiledSrcs = append(j.expandIDEInfoCompiledSrcs, uniqueSrcFiles.Strings()...) + + var kotlinJars android.Paths + + if srcFiles.HasExt(".kt") { + // user defined kotlin flags. + kotlincFlags := j.properties.Kotlincflags + CheckKotlincFlags(ctx, kotlincFlags) + + // Dogfood the JVM_IR backend. + kotlincFlags = append(kotlincFlags, "-Xuse-ir") + + // If there are kotlin files, compile them first but pass all the kotlin and java files + // kotlinc will use the java files to resolve types referenced by the kotlin files, but + // won't emit any classes for them. + kotlincFlags = append(kotlincFlags, "-no-stdlib") + if ctx.Device() { + kotlincFlags = append(kotlincFlags, "-no-jdk") + } + if len(kotlincFlags) > 0 { + // optimization. + ctx.Variable(pctx, "kotlincFlags", strings.Join(kotlincFlags, " ")) + flags.kotlincFlags += "$kotlincFlags" + } + + var kotlinSrcFiles android.Paths + kotlinSrcFiles = append(kotlinSrcFiles, uniqueSrcFiles...) + kotlinSrcFiles = append(kotlinSrcFiles, srcFiles.FilterByExt(".kt")...) + + // Collect .kt files for AIDEGen + j.expandIDEInfoCompiledSrcs = append(j.expandIDEInfoCompiledSrcs, srcFiles.FilterByExt(".kt").Strings()...) + j.expandIDEInfoCompiledSrcs = append(j.expandIDEInfoCompiledSrcs, kotlinCommonSrcFiles.Strings()...) + + flags.classpath = append(flags.classpath, deps.kotlinStdlib...) + flags.classpath = append(flags.classpath, deps.kotlinAnnotations...) + + flags.kotlincClasspath = append(flags.kotlincClasspath, flags.bootClasspath...) + flags.kotlincClasspath = append(flags.kotlincClasspath, flags.classpath...) + + if len(flags.processorPath) > 0 { + // Use kapt for annotation processing + kaptSrcJar := android.PathForModuleOut(ctx, "kapt", "kapt-sources.jar") + kaptResJar := android.PathForModuleOut(ctx, "kapt", "kapt-res.jar") + kotlinKapt(ctx, kaptSrcJar, kaptResJar, kotlinSrcFiles, kotlinCommonSrcFiles, srcJars, flags) + srcJars = append(srcJars, kaptSrcJar) + kotlinJars = append(kotlinJars, kaptResJar) + // Disable annotation processing in javac, it's already been handled by kapt + flags.processorPath = nil + flags.processors = nil + } + + kotlinJar := android.PathForModuleOut(ctx, "kotlin", jarName) + kotlinCompile(ctx, kotlinJar, kotlinSrcFiles, kotlinCommonSrcFiles, srcJars, flags) + if ctx.Failed() { + return + } + + // Make javac rule depend on the kotlinc rule + flags.classpath = append(flags.classpath, kotlinJar) + + kotlinJars = append(kotlinJars, kotlinJar) + // Jar kotlin classes into the final jar after javac + if BoolDefault(j.properties.Static_kotlin_stdlib, true) { + kotlinJars = append(kotlinJars, deps.kotlinStdlib...) + } + } + + jars := append(android.Paths(nil), kotlinJars...) + + // Store the list of .java files that was passed to javac + j.compiledJavaSrcs = uniqueSrcFiles + j.compiledSrcJars = srcJars + + enableSharding := false + var headerJarFileWithoutJarjar android.Path + if ctx.Device() && !ctx.Config().IsEnvFalse("TURBINE_ENABLED") && !deps.disableTurbine { + if j.properties.Javac_shard_size != nil && *(j.properties.Javac_shard_size) > 0 { + enableSharding = true + // Formerly, there was a check here that prevented annotation processors + // from being used when sharding was enabled, as some annotation processors + // do not function correctly in sharded environments. It was removed to + // allow for the use of annotation processors that do function correctly + // with sharding enabled. See: b/77284273. + } + headerJarFileWithoutJarjar, j.headerJarFile = + j.compileJavaHeader(ctx, uniqueSrcFiles, srcJars, deps, flags, jarName, kotlinJars) + if ctx.Failed() { + return + } + } + if len(uniqueSrcFiles) > 0 || len(srcJars) > 0 { + var extraJarDeps android.Paths + if ctx.Config().RunErrorProne() { + // If error-prone is enabled, add an additional rule to compile the java files into + // a separate set of classes (so that they don't overwrite the normal ones and require + // a rebuild when error-prone is turned off). + // TODO(ccross): Once we always compile with javac9 we may be able to conditionally + // enable error-prone without affecting the output class files. + errorprone := android.PathForModuleOut(ctx, "errorprone", jarName) + RunErrorProne(ctx, errorprone, uniqueSrcFiles, srcJars, flags) + extraJarDeps = append(extraJarDeps, errorprone) + } + + if enableSharding { + flags.classpath = append(flags.classpath, headerJarFileWithoutJarjar) + shardSize := int(*(j.properties.Javac_shard_size)) + var shardSrcs []android.Paths + if len(uniqueSrcFiles) > 0 { + shardSrcs = android.ShardPaths(uniqueSrcFiles, shardSize) + for idx, shardSrc := range shardSrcs { + classes := j.compileJavaClasses(ctx, jarName, idx, shardSrc, + nil, flags, extraJarDeps) + jars = append(jars, classes) + } + } + if len(srcJars) > 0 { + classes := j.compileJavaClasses(ctx, jarName, len(shardSrcs), + nil, srcJars, flags, extraJarDeps) + jars = append(jars, classes) + } + } else { + classes := j.compileJavaClasses(ctx, jarName, -1, uniqueSrcFiles, srcJars, flags, extraJarDeps) + jars = append(jars, classes) + } + if ctx.Failed() { + return + } + } + + j.srcJarArgs, j.srcJarDeps = resourcePathsToJarArgs(srcFiles), srcFiles + + var includeSrcJar android.WritablePath + if Bool(j.properties.Include_srcs) { + includeSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+".srcjar") + TransformResourcesToJar(ctx, includeSrcJar, j.srcJarArgs, j.srcJarDeps) + } + + dirArgs, dirDeps := ResourceDirsToJarArgs(ctx, j.properties.Java_resource_dirs, + j.properties.Exclude_java_resource_dirs, j.properties.Exclude_java_resources) + fileArgs, fileDeps := ResourceFilesToJarArgs(ctx, j.properties.Java_resources, j.properties.Exclude_java_resources) + extraArgs, extraDeps := resourcePathsToJarArgs(j.extraResources), j.extraResources + + var resArgs []string + var resDeps android.Paths + + resArgs = append(resArgs, dirArgs...) + resDeps = append(resDeps, dirDeps...) + + resArgs = append(resArgs, fileArgs...) + resDeps = append(resDeps, fileDeps...) + + resArgs = append(resArgs, extraArgs...) + resDeps = append(resDeps, extraDeps...) + + if len(resArgs) > 0 { + resourceJar := android.PathForModuleOut(ctx, "res", jarName) + TransformResourcesToJar(ctx, resourceJar, resArgs, resDeps) + j.resourceJar = resourceJar + if ctx.Failed() { + return + } + } + + var resourceJars android.Paths + if j.resourceJar != nil { + resourceJars = append(resourceJars, j.resourceJar) + } + if Bool(j.properties.Include_srcs) { + resourceJars = append(resourceJars, includeSrcJar) + } + resourceJars = append(resourceJars, deps.staticResourceJars...) + + if len(resourceJars) > 1 { + combinedJar := android.PathForModuleOut(ctx, "res-combined", jarName) + TransformJarsToJar(ctx, combinedJar, "for resources", resourceJars, android.OptionalPath{}, + false, nil, nil) + j.resourceJar = combinedJar + } else if len(resourceJars) == 1 { + j.resourceJar = resourceJars[0] + } + + if len(deps.staticJars) > 0 { + jars = append(jars, deps.staticJars...) + } + + manifest := j.overrideManifest + if !manifest.Valid() && j.properties.Manifest != nil { + manifest = android.OptionalPathForPath(android.PathForModuleSrc(ctx, *j.properties.Manifest)) + } + + services := android.PathsForModuleSrc(ctx, j.properties.Services) + if len(services) > 0 { + servicesJar := android.PathForModuleOut(ctx, "services", jarName) + var zipargs []string + for _, file := range services { + serviceFile := file.String() + zipargs = append(zipargs, "-C", filepath.Dir(serviceFile), "-f", serviceFile) + } + rule := zip + args := map[string]string{ + "jarArgs": "-P META-INF/services/ " + strings.Join(proptools.NinjaAndShellEscapeList(zipargs), " "), + } + if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_ZIP") { + rule = zipRE + args["implicits"] = strings.Join(services.Strings(), ",") + } + ctx.Build(pctx, android.BuildParams{ + Rule: rule, + Output: servicesJar, + Implicits: services, + Args: args, + }) + jars = append(jars, servicesJar) + } + + // Combine the classes built from sources, any manifests, and any static libraries into + // classes.jar. If there is only one input jar this step will be skipped. + var outputFile android.OutputPath + + if len(jars) == 1 && !manifest.Valid() { + // Optimization: skip the combine step as there is nothing to do + // TODO(ccross): this leaves any module-info.class files, but those should only come from + // prebuilt dependencies until we support modules in the platform build, so there shouldn't be + // any if len(jars) == 1. + + // Transform the single path to the jar into an OutputPath as that is required by the following + // code. + if moduleOutPath, ok := jars[0].(android.ModuleOutPath); ok { + // The path contains an embedded OutputPath so reuse that. + outputFile = moduleOutPath.OutputPath + } else if outputPath, ok := jars[0].(android.OutputPath); ok { + // The path is an OutputPath so reuse it directly. + outputFile = outputPath + } else { + // The file is not in the out directory so create an OutputPath into which it can be copied + // and which the following code can use to refer to it. + combinedJar := android.PathForModuleOut(ctx, "combined", jarName) + ctx.Build(pctx, android.BuildParams{ + Rule: android.Cp, + Input: jars[0], + Output: combinedJar, + }) + outputFile = combinedJar.OutputPath + } + } else { + combinedJar := android.PathForModuleOut(ctx, "combined", jarName) + TransformJarsToJar(ctx, combinedJar, "for javac", jars, manifest, + false, nil, nil) + outputFile = combinedJar.OutputPath + } + + // jarjar implementation jar if necessary + if j.expandJarjarRules != nil { + // Transform classes.jar into classes-jarjar.jar + jarjarFile := android.PathForModuleOut(ctx, "jarjar", jarName).OutputPath + TransformJarJar(ctx, jarjarFile, outputFile, j.expandJarjarRules) + outputFile = jarjarFile + + // jarjar resource jar if necessary + if j.resourceJar != nil { + resourceJarJarFile := android.PathForModuleOut(ctx, "res-jarjar", jarName) + TransformJarJar(ctx, resourceJarJarFile, j.resourceJar, j.expandJarjarRules) + j.resourceJar = resourceJarJarFile + } + + if ctx.Failed() { + return + } + } + + // Check package restrictions if necessary. + if len(j.properties.Permitted_packages) > 0 { + // Check packages and copy to package-checked file. + pkgckFile := android.PathForModuleOut(ctx, "package-check.stamp") + CheckJarPackages(ctx, pkgckFile, outputFile, j.properties.Permitted_packages) + j.additionalCheckedModules = append(j.additionalCheckedModules, pkgckFile) + + if ctx.Failed() { + return + } + } + + j.implementationJarFile = outputFile + if j.headerJarFile == nil { + j.headerJarFile = j.implementationJarFile + } + + if j.shouldInstrumentInApex(ctx) { + j.properties.Instrument = true + } + + // enforce syntax check to jacoco filters for any build (http://b/183622051) + specs := j.jacocoModuleToZipCommand(ctx) + if ctx.Failed() { + return + } + + if j.shouldInstrument(ctx) { + outputFile = j.instrument(ctx, flags, outputFile, jarName, specs) + } + + // merge implementation jar with resources if necessary + implementationAndResourcesJar := outputFile + if j.resourceJar != nil { + jars := android.Paths{j.resourceJar, implementationAndResourcesJar} + combinedJar := android.PathForModuleOut(ctx, "withres", jarName).OutputPath + TransformJarsToJar(ctx, combinedJar, "for resources", jars, manifest, + false, nil, nil) + implementationAndResourcesJar = combinedJar + } + + j.implementationAndResourcesJar = implementationAndResourcesJar + + // Enable dex compilation for the APEX variants, unless it is disabled explicitly + apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) + if j.DirectlyInAnyApex() && !apexInfo.IsForPlatform() { + if j.dexProperties.Compile_dex == nil { + j.dexProperties.Compile_dex = proptools.BoolPtr(true) + } + if j.deviceProperties.Hostdex == nil { + j.deviceProperties.Hostdex = proptools.BoolPtr(true) + } + } + + if ctx.Device() && (Bool(j.properties.Installable) || Bool(j.dexProperties.Compile_dex)) { + if j.hasCode(ctx) { + if j.shouldInstrumentStatic(ctx) { + j.dexer.extraProguardFlagFiles = append(j.dexer.extraProguardFlagFiles, + android.PathForSource(ctx, "build/make/core/proguard.jacoco.flags")) + } + // Dex compilation + var dexOutputFile android.OutputPath + dexOutputFile = j.dexer.compileDex(ctx, flags, j.MinSdkVersion(ctx), outputFile, jarName) + if ctx.Failed() { + return + } + + // merge dex jar with resources if necessary + if j.resourceJar != nil { + jars := android.Paths{dexOutputFile, j.resourceJar} + combinedJar := android.PathForModuleOut(ctx, "dex-withres", jarName).OutputPath + TransformJarsToJar(ctx, combinedJar, "for dex resources", jars, android.OptionalPath{}, + false, nil, nil) + if *j.dexProperties.Uncompress_dex { + combinedAlignedJar := android.PathForModuleOut(ctx, "dex-withres-aligned", jarName).OutputPath + TransformZipAlign(ctx, combinedAlignedJar, combinedJar) + dexOutputFile = combinedAlignedJar + } else { + dexOutputFile = combinedJar + } + } + + // Initialize the hiddenapi structure. + j.initHiddenAPI(ctx, dexOutputFile, j.implementationJarFile, j.dexProperties.Uncompress_dex) + + // Encode hidden API flags in dex file, if needed. + dexOutputFile = j.hiddenAPIEncodeDex(ctx, dexOutputFile) + + j.dexJarFile = dexOutputFile + + // Dexpreopting + j.dexpreopt(ctx, dexOutputFile) + + outputFile = dexOutputFile + } else { + // There is no code to compile into a dex jar, make sure the resources are propagated + // to the APK if this is an app. + outputFile = implementationAndResourcesJar + j.dexJarFile = j.resourceJar + } + + if ctx.Failed() { + return + } + } else { + outputFile = implementationAndResourcesJar + } + + if ctx.Device() { + lintSDKVersionString := func(sdkSpec android.SdkSpec) string { + if v := sdkSpec.ApiLevel; !v.IsPreview() { + return v.String() + } else { + return ctx.Config().DefaultAppTargetSdk(ctx).String() + } + } + + j.linter.name = ctx.ModuleName() + j.linter.srcs = srcFiles + j.linter.srcJars = srcJars + j.linter.classpath = append(append(android.Paths(nil), flags.bootClasspath...), flags.classpath...) + j.linter.classes = j.implementationJarFile + j.linter.minSdkVersion = lintSDKVersionString(j.MinSdkVersion(ctx)) + j.linter.targetSdkVersion = lintSDKVersionString(j.TargetSdkVersion(ctx)) + j.linter.compileSdkVersion = lintSDKVersionString(j.SdkVersion(ctx)) + j.linter.compileSdkKind = j.SdkVersion(ctx).Kind + j.linter.javaLanguageLevel = flags.javaVersion.String() + j.linter.kotlinLanguageLevel = "1.3" + if !apexInfo.IsForPlatform() && ctx.Config().UnbundledBuildApps() { + j.linter.buildModuleReportZip = true + } + j.linter.lint(ctx) + } + + ctx.CheckbuildFile(outputFile) + + ctx.SetProvider(JavaInfoProvider, JavaInfo{ + HeaderJars: android.PathsIfNonNil(j.headerJarFile), + ImplementationAndResourcesJars: android.PathsIfNonNil(j.implementationAndResourcesJar), + ImplementationJars: android.PathsIfNonNil(j.implementationJarFile), + ResourceJars: android.PathsIfNonNil(j.resourceJar), + AidlIncludeDirs: j.exportAidlIncludeDirs, + SrcJarArgs: j.srcJarArgs, + SrcJarDeps: j.srcJarDeps, + ExportedPlugins: j.exportedPluginJars, + ExportedPluginClasses: j.exportedPluginClasses, + ExportedPluginDisableTurbine: j.exportedDisableTurbine, + JacocoReportClassesFile: j.jacocoReportClassesFile, + }) + + // Save the output file with no relative path so that it doesn't end up in a subdirectory when used as a resource + j.outputFile = outputFile.WithoutRel() +} + +func (j *Module) compileJavaClasses(ctx android.ModuleContext, jarName string, idx int, + srcFiles, srcJars android.Paths, flags javaBuilderFlags, extraJarDeps android.Paths) android.WritablePath { + + kzipName := pathtools.ReplaceExtension(jarName, "kzip") + if idx >= 0 { + kzipName = strings.TrimSuffix(jarName, filepath.Ext(jarName)) + strconv.Itoa(idx) + ".kzip" + jarName += strconv.Itoa(idx) + } + + classes := android.PathForModuleOut(ctx, "javac", jarName).OutputPath + TransformJavaToClasses(ctx, classes, idx, srcFiles, srcJars, flags, extraJarDeps) + + if ctx.Config().EmitXrefRules() { + extractionFile := android.PathForModuleOut(ctx, kzipName) + emitXrefRule(ctx, extractionFile, idx, srcFiles, srcJars, flags, extraJarDeps) + j.kytheFiles = append(j.kytheFiles, extractionFile) + } + + return classes +} + +// Check for invalid kotlinc flags. Only use this for flags explicitly passed by the user, +// since some of these flags may be used internally. +func CheckKotlincFlags(ctx android.ModuleContext, flags []string) { + for _, flag := range flags { + flag = strings.TrimSpace(flag) + + if !strings.HasPrefix(flag, "-") { + ctx.PropertyErrorf("kotlincflags", "Flag `%s` must start with `-`", flag) + } else if strings.HasPrefix(flag, "-Xintellij-plugin-root") { + ctx.PropertyErrorf("kotlincflags", + "Bad flag: `%s`, only use internal compiler for consistency.", flag) + } else if inList(flag, config.KotlincIllegalFlags) { + ctx.PropertyErrorf("kotlincflags", "Flag `%s` already used by build system", flag) + } else if flag == "-include-runtime" { + ctx.PropertyErrorf("kotlincflags", "Bad flag: `%s`, do not include runtime.", flag) + } else { + args := strings.Split(flag, " ") + if args[0] == "-kotlin-home" { + ctx.PropertyErrorf("kotlincflags", + "Bad flag: `%s`, kotlin home already set to default (path to kotlinc in the repo).", flag) + } + } + } +} + +func (j *Module) compileJavaHeader(ctx android.ModuleContext, srcFiles, srcJars android.Paths, + deps deps, flags javaBuilderFlags, jarName string, + extraJars android.Paths) (headerJar, jarjarHeaderJar android.Path) { + + var jars android.Paths + if len(srcFiles) > 0 || len(srcJars) > 0 { + // Compile java sources into turbine.jar. + turbineJar := android.PathForModuleOut(ctx, "turbine", jarName) + TransformJavaToHeaderClasses(ctx, turbineJar, srcFiles, srcJars, flags) + if ctx.Failed() { + return nil, nil + } + jars = append(jars, turbineJar) + } + + jars = append(jars, extraJars...) + + // Combine any static header libraries into classes-header.jar. If there is only + // one input jar this step will be skipped. + jars = append(jars, deps.staticHeaderJars...) + + // we cannot skip the combine step for now if there is only one jar + // since we have to strip META-INF/TRANSITIVE dir from turbine.jar + combinedJar := android.PathForModuleOut(ctx, "turbine-combined", jarName) + TransformJarsToJar(ctx, combinedJar, "for turbine", jars, android.OptionalPath{}, + false, nil, []string{"META-INF/TRANSITIVE"}) + headerJar = combinedJar + jarjarHeaderJar = combinedJar + + if j.expandJarjarRules != nil { + // Transform classes.jar into classes-jarjar.jar + jarjarFile := android.PathForModuleOut(ctx, "turbine-jarjar", jarName) + TransformJarJar(ctx, jarjarFile, headerJar, j.expandJarjarRules) + jarjarHeaderJar = jarjarFile + if ctx.Failed() { + return nil, nil + } + } + + return headerJar, jarjarHeaderJar +} + +func (j *Module) instrument(ctx android.ModuleContext, flags javaBuilderFlags, + classesJar android.Path, jarName string, specs string) android.OutputPath { + + jacocoReportClassesFile := android.PathForModuleOut(ctx, "jacoco-report-classes", jarName) + instrumentedJar := android.PathForModuleOut(ctx, "jacoco", jarName).OutputPath + + jacocoInstrumentJar(ctx, instrumentedJar, jacocoReportClassesFile, classesJar, specs) + + j.jacocoReportClassesFile = jacocoReportClassesFile + + return instrumentedJar +} + +func (j *Module) HeaderJars() android.Paths { + if j.headerJarFile == nil { + return nil + } + return android.Paths{j.headerJarFile} +} + +func (j *Module) ImplementationJars() android.Paths { + if j.implementationJarFile == nil { + return nil + } + return android.Paths{j.implementationJarFile} +} + +func (j *Module) DexJarBuildPath() android.Path { + return j.dexJarFile +} + +func (j *Module) DexJarInstallPath() android.Path { + return j.installFile +} + +func (j *Module) ImplementationAndResourcesJars() android.Paths { + if j.implementationAndResourcesJar == nil { + return nil + } + return android.Paths{j.implementationAndResourcesJar} +} + +func (j *Module) AidlIncludeDirs() android.Paths { + // exportAidlIncludeDirs is type android.Paths already + return j.exportAidlIncludeDirs +} + +func (j *Module) ClassLoaderContexts() dexpreopt.ClassLoaderContextMap { + return j.classLoaderContexts +} + +// Collect information for opening IDE project files in java/jdeps.go. +func (j *Module) IDEInfo(dpInfo *android.IdeInfo) { + dpInfo.Deps = append(dpInfo.Deps, j.CompilerDeps()...) + dpInfo.Srcs = append(dpInfo.Srcs, j.expandIDEInfoCompiledSrcs...) + dpInfo.SrcJars = append(dpInfo.SrcJars, j.compiledSrcJars.Strings()...) + dpInfo.Aidl_include_dirs = append(dpInfo.Aidl_include_dirs, j.deviceProperties.Aidl.Include_dirs...) + if j.expandJarjarRules != nil { + dpInfo.Jarjar_rules = append(dpInfo.Jarjar_rules, j.expandJarjarRules.String()) + } + dpInfo.Paths = append(dpInfo.Paths, j.modulePaths...) +} + +func (j *Module) CompilerDeps() []string { + jdeps := []string{} + jdeps = append(jdeps, j.properties.Libs...) + jdeps = append(jdeps, j.properties.Static_libs...) + return jdeps +} + +func (j *Module) hasCode(ctx android.ModuleContext) bool { + srcFiles := android.PathsForModuleSrcExcludes(ctx, j.properties.Srcs, j.properties.Exclude_srcs) + return len(srcFiles) > 0 || len(ctx.GetDirectDepsWithTag(staticLibTag)) > 0 +} + +// Implements android.ApexModule +func (j *Module) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool { + return j.depIsInSameApex(ctx, dep) +} + +// Implements android.ApexModule +func (j *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext, + sdkVersion android.ApiLevel) error { + sdkSpec := j.MinSdkVersion(ctx) + if !sdkSpec.Specified() { + return fmt.Errorf("min_sdk_version is not specified") + } + if sdkSpec.Kind == android.SdkCore { + return nil + } + ver, err := sdkSpec.EffectiveVersion(ctx) + if err != nil { + return err + } + if ver.GreaterThan(sdkVersion) { + return fmt.Errorf("newer SDK(%v)", ver) + } + return nil +} + +func (j *Module) Stem() string { + return proptools.StringDefault(j.deviceProperties.Stem, j.Name()) +} + +func (j *Module) JacocoReportClassesFile() android.Path { + return j.jacocoReportClassesFile +} + +func (j *Module) IsInstallable() bool { + return Bool(j.properties.Installable) +} + +type sdkLinkType int + +const ( + // TODO(jiyong) rename these for better readability. Make the allowed + // and disallowed link types explicit + // order is important here. See rank() + javaCore sdkLinkType = iota + javaSdk + javaSystem + javaModule + javaSystemServer + javaPlatform +) + +func (lt sdkLinkType) String() string { + switch lt { + case javaCore: + return "core Java API" + case javaSdk: + return "Android API" + case javaSystem: + return "system API" + case javaModule: + return "module API" + case javaSystemServer: + return "system server API" + case javaPlatform: + return "private API" + default: + panic(fmt.Errorf("unrecognized linktype: %d", lt)) + } +} + +// rank determines the total order among sdkLinkType. An SDK link type of rank A can link to +// another SDK link type of rank B only when B <= A. For example, a module linking to Android SDK +// can't statically depend on modules that use Platform API. +func (lt sdkLinkType) rank() int { + return int(lt) +} + +type moduleWithSdkDep interface { + android.Module + getSdkLinkType(ctx android.BaseModuleContext, name string) (ret sdkLinkType, stubs bool) +} + +func (m *Module) getSdkLinkType(ctx android.BaseModuleContext, name string) (ret sdkLinkType, stubs bool) { + switch name { + case "core.current.stubs", "legacy.core.platform.api.stubs", "stable.core.platform.api.stubs", + "stub-annotations", "private-stub-annotations-jar", + "core-lambda-stubs", "core-generated-annotation-stubs": + return javaCore, true + case "android_stubs_current": + return javaSdk, true + case "android_system_stubs_current": + return javaSystem, true + case "android_module_lib_stubs_current": + return javaModule, true + case "android_system_server_stubs_current": + return javaSystemServer, true + case "android_test_stubs_current": + return javaSystem, true + } + + if stub, linkType := moduleStubLinkType(name); stub { + return linkType, true + } + + ver := m.SdkVersion(ctx) + switch ver.Kind { + case android.SdkCore: + return javaCore, false + case android.SdkSystem: + return javaSystem, false + case android.SdkPublic: + return javaSdk, false + case android.SdkModule: + return javaModule, false + case android.SdkSystemServer: + return javaSystemServer, false + case android.SdkPrivate, android.SdkNone, android.SdkCorePlatform, android.SdkTest: + return javaPlatform, false + } + + if !ver.Valid() { + panic(fmt.Errorf("sdk_version is invalid. got %q", ver.Raw)) + } + return javaSdk, false +} + +// checkSdkLinkType make sures the given dependency doesn't have a lower SDK link type rank than +// this module's. See the comment on rank() for details and an example. +func (j *Module) checkSdkLinkType( + ctx android.ModuleContext, dep moduleWithSdkDep, tag dependencyTag) { + if ctx.Host() { + return + } + + myLinkType, stubs := j.getSdkLinkType(ctx, ctx.ModuleName()) + if stubs { + return + } + depLinkType, _ := dep.getSdkLinkType(ctx, ctx.OtherModuleName(dep)) + + if myLinkType.rank() < depLinkType.rank() { + ctx.ModuleErrorf("compiles against %v, but dependency %q is compiling against %v. "+ + "In order to fix this, consider adjusting sdk_version: OR platform_apis: "+ + "property of the source or target module so that target module is built "+ + "with the same or smaller API set when compared to the source.", + myLinkType, ctx.OtherModuleName(dep), depLinkType) + } +} + +func (j *Module) collectDeps(ctx android.ModuleContext) deps { + var deps deps + + if ctx.Device() { + sdkDep := decodeSdkDep(ctx, android.SdkContext(j)) + if sdkDep.invalidVersion { + ctx.AddMissingDependencies(sdkDep.bootclasspath) + ctx.AddMissingDependencies(sdkDep.java9Classpath) + } else if sdkDep.useFiles { + // sdkDep.jar is actually equivalent to turbine header.jar. + deps.classpath = append(deps.classpath, sdkDep.jars...) + deps.aidlPreprocess = sdkDep.aidl + } else { + deps.aidlPreprocess = sdkDep.aidl + } + } + + sdkLinkType, _ := j.getSdkLinkType(ctx, ctx.ModuleName()) + + ctx.VisitDirectDeps(func(module android.Module) { + otherName := ctx.OtherModuleName(module) + tag := ctx.OtherModuleDependencyTag(module) + + if IsJniDepTag(tag) { + // Handled by AndroidApp.collectAppDeps + return + } + if tag == certificateTag { + // Handled by AndroidApp.collectAppDeps + return + } + + if dep, ok := module.(SdkLibraryDependency); ok { + switch tag { + case libTag: + deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.SdkVersion(ctx))...) + case staticLibTag: + ctx.ModuleErrorf("dependency on java_sdk_library %q can only be in libs", otherName) + } + } else if ctx.OtherModuleHasProvider(module, JavaInfoProvider) { + dep := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo) + if sdkLinkType != javaPlatform && + ctx.OtherModuleHasProvider(module, SyspropPublicStubInfoProvider) { + // dep is a sysprop implementation library, but this module is not linking against + // the platform, so it gets the sysprop public stubs library instead. Replace + // dep with the JavaInfo from the SyspropPublicStubInfoProvider. + syspropDep := ctx.OtherModuleProvider(module, SyspropPublicStubInfoProvider).(SyspropPublicStubInfo) + dep = syspropDep.JavaInfo + } + switch tag { + case bootClasspathTag: + deps.bootClasspath = append(deps.bootClasspath, dep.HeaderJars...) + case libTag, instrumentationForTag: + deps.classpath = append(deps.classpath, dep.HeaderJars...) + deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs...) + addPlugins(&deps, dep.ExportedPlugins, dep.ExportedPluginClasses...) + deps.disableTurbine = deps.disableTurbine || dep.ExportedPluginDisableTurbine + case java9LibTag: + deps.java9Classpath = append(deps.java9Classpath, dep.HeaderJars...) + case staticLibTag: + deps.classpath = append(deps.classpath, dep.HeaderJars...) + deps.staticJars = append(deps.staticJars, dep.ImplementationJars...) + deps.staticHeaderJars = append(deps.staticHeaderJars, dep.HeaderJars...) + deps.staticResourceJars = append(deps.staticResourceJars, dep.ResourceJars...) + deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs...) + addPlugins(&deps, dep.ExportedPlugins, dep.ExportedPluginClasses...) + // Turbine doesn't run annotation processors, so any module that uses an + // annotation processor that generates API is incompatible with the turbine + // optimization. + deps.disableTurbine = deps.disableTurbine || dep.ExportedPluginDisableTurbine + case pluginTag: + if plugin, ok := module.(*Plugin); ok { + if plugin.pluginProperties.Processor_class != nil { + addPlugins(&deps, dep.ImplementationAndResourcesJars, *plugin.pluginProperties.Processor_class) + } else { + addPlugins(&deps, dep.ImplementationAndResourcesJars) + } + // Turbine doesn't run annotation processors, so any module that uses an + // annotation processor that generates API is incompatible with the turbine + // optimization. + deps.disableTurbine = deps.disableTurbine || Bool(plugin.pluginProperties.Generates_api) + } else { + ctx.PropertyErrorf("plugins", "%q is not a java_plugin module", otherName) + } + case errorpronePluginTag: + if _, ok := module.(*Plugin); ok { + deps.errorProneProcessorPath = append(deps.errorProneProcessorPath, dep.ImplementationAndResourcesJars...) + } else { + ctx.PropertyErrorf("plugins", "%q is not a java_plugin module", otherName) + } + case exportedPluginTag: + if plugin, ok := module.(*Plugin); ok { + j.exportedPluginJars = append(j.exportedPluginJars, dep.ImplementationAndResourcesJars...) + if plugin.pluginProperties.Processor_class != nil { + j.exportedPluginClasses = append(j.exportedPluginClasses, *plugin.pluginProperties.Processor_class) + } + // Turbine doesn't run annotation processors, so any module that uses an + // annotation processor that generates API is incompatible with the turbine + // optimization. + j.exportedDisableTurbine = Bool(plugin.pluginProperties.Generates_api) + } else { + ctx.PropertyErrorf("exported_plugins", "%q is not a java_plugin module", otherName) + } + case kotlinStdlibTag: + deps.kotlinStdlib = append(deps.kotlinStdlib, dep.HeaderJars...) + case kotlinAnnotationsTag: + deps.kotlinAnnotations = dep.HeaderJars + case syspropPublicStubDepTag: + // This is a sysprop implementation library, forward the JavaInfoProvider from + // the corresponding sysprop public stub library as SyspropPublicStubInfoProvider. + ctx.SetProvider(SyspropPublicStubInfoProvider, SyspropPublicStubInfo{ + JavaInfo: dep, + }) + } + } else if dep, ok := module.(android.SourceFileProducer); ok { + switch tag { + case libTag: + checkProducesJars(ctx, dep) + deps.classpath = append(deps.classpath, dep.Srcs()...) + case staticLibTag: + checkProducesJars(ctx, dep) + deps.classpath = append(deps.classpath, dep.Srcs()...) + deps.staticJars = append(deps.staticJars, dep.Srcs()...) + deps.staticHeaderJars = append(deps.staticHeaderJars, dep.Srcs()...) + } + } else { + switch tag { + case bootClasspathTag: + // If a system modules dependency has been added to the bootclasspath + // then add its libs to the bootclasspath. + sm := module.(SystemModulesProvider) + deps.bootClasspath = append(deps.bootClasspath, sm.HeaderJars()...) + + case systemModulesTag: + if deps.systemModules != nil { + panic("Found two system module dependencies") + } + sm := module.(SystemModulesProvider) + outputDir, outputDeps := sm.OutputDirAndDeps() + deps.systemModules = &systemModules{outputDir, outputDeps} + } + } + + addCLCFromDep(ctx, module, j.classLoaderContexts) + }) + + return deps +} + +func addPlugins(deps *deps, pluginJars android.Paths, pluginClasses ...string) { + deps.processorPath = append(deps.processorPath, pluginJars...) + deps.processorClasses = append(deps.processorClasses, pluginClasses...) +} + +// TODO(b/132357300) Generalize SdkLibrarComponentDependency to non-SDK libraries and merge with +// this interface. +type ProvidesUsesLib interface { + ProvidesUsesLib() *string +} + +func (j *Module) ProvidesUsesLib() *string { + return j.usesLibraryProperties.Provides_uses_lib +} + +type ModuleWithStem interface { + Stem() string +} + +var _ ModuleWithStem = (*Module)(nil) |