Home | History | Annotate | Download | only in java
      1 // Copyright 2018 Google Inc. All rights reserved.
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //     http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 package java
     16 
     17 import (
     18 	"android/soong/android"
     19 	"android/soong/genrule"
     20 	"fmt"
     21 	"io"
     22 	"path"
     23 	"path/filepath"
     24 	"sort"
     25 	"strings"
     26 	"sync"
     27 
     28 	"github.com/google/blueprint"
     29 	"github.com/google/blueprint/proptools"
     30 )
     31 
     32 var (
     33 	sdkStubsLibrarySuffix = ".stubs"
     34 	sdkSystemApiSuffix    = ".system"
     35 	sdkTestApiSuffix      = ".test"
     36 	sdkDocsSuffix         = ".docs"
     37 	sdkXmlFileSuffix      = ".xml"
     38 )
     39 
     40 type stubsLibraryDependencyTag struct {
     41 	blueprint.BaseDependencyTag
     42 	name string
     43 }
     44 
     45 type syspropLibraryInterface interface {
     46 	SyspropJavaModule() *SdkLibrary
     47 }
     48 
     49 var (
     50 	publicApiStubsTag = dependencyTag{name: "public"}
     51 	systemApiStubsTag = dependencyTag{name: "system"}
     52 	testApiStubsTag   = dependencyTag{name: "test"}
     53 	publicApiFileTag  = dependencyTag{name: "publicApi"}
     54 	systemApiFileTag  = dependencyTag{name: "systemApi"}
     55 	testApiFileTag    = dependencyTag{name: "testApi"}
     56 )
     57 
     58 type apiScope int
     59 
     60 const (
     61 	apiScopePublic apiScope = iota
     62 	apiScopeSystem
     63 	apiScopeTest
     64 )
     65 
     66 var (
     67 	javaSdkLibrariesLock sync.Mutex
     68 )
     69 
     70 // java_sdk_library is to make a Java library that implements optional platform APIs to apps.
     71 // It is actually a wrapper of several modules: 1) stubs library that clients are linked against
     72 // to, 2) droiddoc module that internally generates API stubs source files, 3) the real runtime
     73 // shared library that implements the APIs, and 4) XML file for adding the runtime lib to the
     74 // classpath at runtime if requested via <uses-library>.
     75 //
     76 // TODO: these are big features that are currently missing
     77 // 1) disallowing linking to the runtime shared lib
     78 // 2) HTML generation
     79 
     80 func init() {
     81 	android.RegisterModuleType("java_sdk_library", SdkLibraryFactory)
     82 
     83 	android.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
     84 		ctx.TopDown("java_sdk_library", SdkLibraryMutator).Parallel()
     85 	})
     86 
     87 	android.RegisterMakeVarsProvider(pctx, func(ctx android.MakeVarsContext) {
     88 		javaSdkLibraries := javaSdkLibraries(ctx.Config())
     89 		sort.Strings(*javaSdkLibraries)
     90 		ctx.Strict("JAVA_SDK_LIBRARIES", strings.Join(*javaSdkLibraries, " "))
     91 	})
     92 }
     93 
     94 type sdkLibraryProperties struct {
     95 	// list of optional source files that are part of API but not part of runtime library.
     96 	Api_srcs []string `android:"arch_variant"`
     97 
     98 	// List of Java libraries that will be in the classpath when building stubs
     99 	Stub_only_libs []string `android:"arch_variant"`
    100 
    101 	// list of package names that will be documented and publicized as API
    102 	Api_packages []string
    103 
    104 	// list of package names that must be hidden from the API
    105 	Hidden_api_packages []string
    106 
    107 	// local files that are used within user customized droiddoc options.
    108 	Droiddoc_option_files []string
    109 
    110 	// additional droiddoc options
    111 	// Available variables for substitution:
    112 	//
    113 	//  $(location <label>): the path to the droiddoc_option_files with name <label>
    114 	Droiddoc_options []string
    115 
    116 	// the java library (in classpath) for documentation that provides java srcs and srcjars.
    117 	Srcs_lib *string
    118 
    119 	// the base dirs under srcs_lib will be scanned for java srcs.
    120 	Srcs_lib_whitelist_dirs []string
    121 
    122 	// the sub dirs under srcs_lib_whitelist_dirs will be scanned for java srcs.
    123 	// Defaults to "android.annotation".
    124 	Srcs_lib_whitelist_pkgs []string
    125 
    126 	// a list of top-level directories containing files to merge qualifier annotations
    127 	// (i.e. those intended to be included in the stubs written) from.
    128 	Merge_annotations_dirs []string
    129 
    130 	// a list of top-level directories containing Java stub files to merge show/hide annotations from.
    131 	Merge_inclusion_annotations_dirs []string
    132 
    133 	// If set to true, the path of dist files is apistubs/core. Defaults to false.
    134 	Core_lib *bool
    135 
    136 	// don't create dist rules.
    137 	No_dist *bool `blueprint:"mutated"`
    138 
    139 	// TODO: determines whether to create HTML doc or not
    140 	//Html_doc *bool
    141 }
    142 
    143 type SdkLibrary struct {
    144 	Library
    145 
    146 	sdkLibraryProperties sdkLibraryProperties
    147 
    148 	publicApiStubsPath android.Paths
    149 	systemApiStubsPath android.Paths
    150 	testApiStubsPath   android.Paths
    151 
    152 	publicApiStubsImplPath android.Paths
    153 	systemApiStubsImplPath android.Paths
    154 	testApiStubsImplPath   android.Paths
    155 
    156 	publicApiFilePath android.Path
    157 	systemApiFilePath android.Path
    158 	testApiFilePath   android.Path
    159 }
    160 
    161 var _ Dependency = (*SdkLibrary)(nil)
    162 var _ SdkLibraryDependency = (*SdkLibrary)(nil)
    163 
    164 func (module *SdkLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
    165 	useBuiltStubs := !ctx.Config().UnbundledBuildUsePrebuiltSdks()
    166 	// Add dependencies to the stubs library
    167 	if useBuiltStubs {
    168 		ctx.AddVariationDependencies(nil, publicApiStubsTag, module.stubsName(apiScopePublic))
    169 	}
    170 	ctx.AddVariationDependencies(nil, publicApiFileTag, module.docsName(apiScopePublic))
    171 
    172 	if !Bool(module.properties.No_standard_libs) {
    173 		if useBuiltStubs {
    174 			ctx.AddVariationDependencies(nil, systemApiStubsTag, module.stubsName(apiScopeSystem))
    175 			ctx.AddVariationDependencies(nil, testApiStubsTag, module.stubsName(apiScopeTest))
    176 		}
    177 		ctx.AddVariationDependencies(nil, systemApiFileTag, module.docsName(apiScopeSystem))
    178 		ctx.AddVariationDependencies(nil, testApiFileTag, module.docsName(apiScopeTest))
    179 	}
    180 
    181 	module.Library.deps(ctx)
    182 }
    183 
    184 func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
    185 	module.Library.GenerateAndroidBuildActions(ctx)
    186 
    187 	// Record the paths to the header jars of the library (stubs and impl).
    188 	// When this java_sdk_library is dependened from others via "libs" property,
    189 	// the recorded paths will be returned depending on the link type of the caller.
    190 	ctx.VisitDirectDeps(func(to android.Module) {
    191 		otherName := ctx.OtherModuleName(to)
    192 		tag := ctx.OtherModuleDependencyTag(to)
    193 
    194 		if lib, ok := to.(Dependency); ok {
    195 			switch tag {
    196 			case publicApiStubsTag:
    197 				module.publicApiStubsPath = lib.HeaderJars()
    198 				module.publicApiStubsImplPath = lib.ImplementationJars()
    199 			case systemApiStubsTag:
    200 				module.systemApiStubsPath = lib.HeaderJars()
    201 				module.systemApiStubsImplPath = lib.ImplementationJars()
    202 			case testApiStubsTag:
    203 				module.testApiStubsPath = lib.HeaderJars()
    204 				module.testApiStubsImplPath = lib.ImplementationJars()
    205 			}
    206 		}
    207 		if doc, ok := to.(ApiFilePath); ok {
    208 			switch tag {
    209 			case publicApiFileTag:
    210 				module.publicApiFilePath = doc.ApiFilePath()
    211 			case systemApiFileTag:
    212 				module.systemApiFilePath = doc.ApiFilePath()
    213 			case testApiFileTag:
    214 				module.testApiFilePath = doc.ApiFilePath()
    215 			default:
    216 				ctx.ModuleErrorf("depends on module %q of unknown tag %q", otherName, tag)
    217 			}
    218 		}
    219 	})
    220 }
    221 
    222 func (module *SdkLibrary) AndroidMk() android.AndroidMkData {
    223 	data := module.Library.AndroidMk()
    224 	data.Required = append(data.Required, module.xmlFileName())
    225 
    226 	data.Custom = func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
    227 		android.WriteAndroidMkData(w, data)
    228 
    229 		module.Library.AndroidMkHostDex(w, name, data)
    230 		if !Bool(module.sdkLibraryProperties.No_dist) {
    231 			// Create a phony module that installs the impl library, for the case when this lib is
    232 			// in PRODUCT_PACKAGES.
    233 			owner := module.ModuleBase.Owner()
    234 			if owner == "" {
    235 				if Bool(module.sdkLibraryProperties.Core_lib) {
    236 					owner = "core"
    237 				} else {
    238 					owner = "android"
    239 				}
    240 			}
    241 			// Create dist rules to install the stubs libs to the dist dir
    242 			if len(module.publicApiStubsPath) == 1 {
    243 				fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
    244 					module.publicApiStubsImplPath.Strings()[0]+
    245 					":"+path.Join("apistubs", owner, "public",
    246 					module.BaseModuleName()+".jar")+")")
    247 			}
    248 			if len(module.systemApiStubsPath) == 1 {
    249 				fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
    250 					module.systemApiStubsImplPath.Strings()[0]+
    251 					":"+path.Join("apistubs", owner, "system",
    252 					module.BaseModuleName()+".jar")+")")
    253 			}
    254 			if len(module.testApiStubsPath) == 1 {
    255 				fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
    256 					module.testApiStubsImplPath.Strings()[0]+
    257 					":"+path.Join("apistubs", owner, "test",
    258 					module.BaseModuleName()+".jar")+")")
    259 			}
    260 			if module.publicApiFilePath != nil {
    261 				fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
    262 					module.publicApiFilePath.String()+
    263 					":"+path.Join("apistubs", owner, "public", "api",
    264 					module.BaseModuleName()+".txt")+")")
    265 			}
    266 			if module.systemApiFilePath != nil {
    267 				fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
    268 					module.systemApiFilePath.String()+
    269 					":"+path.Join("apistubs", owner, "system", "api",
    270 					module.BaseModuleName()+".txt")+")")
    271 			}
    272 			if module.testApiFilePath != nil {
    273 				fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
    274 					module.testApiFilePath.String()+
    275 					":"+path.Join("apistubs", owner, "test", "api",
    276 					module.BaseModuleName()+".txt")+")")
    277 			}
    278 		}
    279 	}
    280 	return data
    281 }
    282 
    283 // Module name of the stubs library
    284 func (module *SdkLibrary) stubsName(apiScope apiScope) string {
    285 	stubsName := module.BaseModuleName() + sdkStubsLibrarySuffix
    286 	switch apiScope {
    287 	case apiScopeSystem:
    288 		stubsName = stubsName + sdkSystemApiSuffix
    289 	case apiScopeTest:
    290 		stubsName = stubsName + sdkTestApiSuffix
    291 	}
    292 	return stubsName
    293 }
    294 
    295 // Module name of the docs
    296 func (module *SdkLibrary) docsName(apiScope apiScope) string {
    297 	docsName := module.BaseModuleName() + sdkDocsSuffix
    298 	switch apiScope {
    299 	case apiScopeSystem:
    300 		docsName = docsName + sdkSystemApiSuffix
    301 	case apiScopeTest:
    302 		docsName = docsName + sdkTestApiSuffix
    303 	}
    304 	return docsName
    305 }
    306 
    307 // Module name of the runtime implementation library
    308 func (module *SdkLibrary) implName() string {
    309 	return module.BaseModuleName()
    310 }
    311 
    312 // File path to the runtime implementation library
    313 func (module *SdkLibrary) implPath() string {
    314 	partition := "system"
    315 	if module.SocSpecific() {
    316 		partition = "vendor"
    317 	} else if module.DeviceSpecific() {
    318 		partition = "odm"
    319 	} else if module.ProductSpecific() {
    320 		partition = "product"
    321 	}
    322 	return "/" + partition + "/framework/" + module.implName() + ".jar"
    323 }
    324 
    325 // Module name of the XML file for the lib
    326 func (module *SdkLibrary) xmlFileName() string {
    327 	return module.BaseModuleName() + sdkXmlFileSuffix
    328 }
    329 
    330 // SDK version that the stubs library is built against. Note that this is always
    331 // *current. Older stubs library built with a numberd SDK version is created from
    332 // the prebuilt jar.
    333 func (module *SdkLibrary) sdkVersion(apiScope apiScope) string {
    334 	switch apiScope {
    335 	case apiScopePublic:
    336 		return "current"
    337 	case apiScopeSystem:
    338 		return "system_current"
    339 	case apiScopeTest:
    340 		return "test_current"
    341 	default:
    342 		return "current"
    343 	}
    344 }
    345 
    346 // $(INTERNAL_PLATFORM_<apiTagName>_API_FILE) points to the generated
    347 // api file for the current source
    348 // TODO: remove this when apicheck is done in soong
    349 func (module *SdkLibrary) apiTagName(apiScope apiScope) string {
    350 	apiTagName := strings.Replace(strings.ToUpper(module.BaseModuleName()), ".", "_", -1)
    351 	switch apiScope {
    352 	case apiScopeSystem:
    353 		apiTagName = apiTagName + "_SYSTEM"
    354 	case apiScopeTest:
    355 		apiTagName = apiTagName + "_TEST"
    356 	}
    357 	return apiTagName
    358 }
    359 
    360 func (module *SdkLibrary) latestApiFilegroupName(apiScope apiScope) string {
    361 	name := ":" + module.BaseModuleName() + ".api."
    362 	switch apiScope {
    363 	case apiScopePublic:
    364 		name = name + "public"
    365 	case apiScopeSystem:
    366 		name = name + "system"
    367 	case apiScopeTest:
    368 		name = name + "test"
    369 	}
    370 	name = name + ".latest"
    371 	return name
    372 }
    373 
    374 func (module *SdkLibrary) latestRemovedApiFilegroupName(apiScope apiScope) string {
    375 	name := ":" + module.BaseModuleName() + "-removed.api."
    376 	switch apiScope {
    377 	case apiScopePublic:
    378 		name = name + "public"
    379 	case apiScopeSystem:
    380 		name = name + "system"
    381 	case apiScopeTest:
    382 		name = name + "test"
    383 	}
    384 	name = name + ".latest"
    385 	return name
    386 }
    387 
    388 // Creates a static java library that has API stubs
    389 func (module *SdkLibrary) createStubsLibrary(mctx android.TopDownMutatorContext, apiScope apiScope) {
    390 	props := struct {
    391 		Name              *string
    392 		Srcs              []string
    393 		Sdk_version       *string
    394 		Libs              []string
    395 		Soc_specific      *bool
    396 		Device_specific   *bool
    397 		Product_specific  *bool
    398 		Compile_dex       *bool
    399 		No_standard_libs  *bool
    400 		System_modules    *string
    401 		Java_version      *string
    402 		Product_variables struct {
    403 			Unbundled_build struct {
    404 				Enabled *bool
    405 			}
    406 			Pdk struct {
    407 				Enabled *bool
    408 			}
    409 		}
    410 		Openjdk9 struct {
    411 			Srcs       []string
    412 			Javacflags []string
    413 		}
    414 	}{}
    415 
    416 	props.Name = proptools.StringPtr(module.stubsName(apiScope))
    417 	// sources are generated from the droiddoc
    418 	props.Srcs = []string{":" + module.docsName(apiScope)}
    419 	props.Sdk_version = proptools.StringPtr(module.sdkVersion(apiScope))
    420 	props.Libs = module.sdkLibraryProperties.Stub_only_libs
    421 	// Unbundled apps will use the prebult one from /prebuilts/sdk
    422 	if mctx.Config().UnbundledBuildUsePrebuiltSdks() {
    423 		props.Product_variables.Unbundled_build.Enabled = proptools.BoolPtr(false)
    424 	}
    425 	props.Product_variables.Pdk.Enabled = proptools.BoolPtr(false)
    426 	props.No_standard_libs = module.Library.Module.properties.No_standard_libs
    427 	props.System_modules = module.Library.Module.deviceProperties.System_modules
    428 	props.Openjdk9.Srcs = module.Library.Module.properties.Openjdk9.Srcs
    429 	props.Openjdk9.Javacflags = module.Library.Module.properties.Openjdk9.Javacflags
    430 	props.Java_version = module.Library.Module.properties.Java_version
    431 	if module.Library.Module.deviceProperties.Compile_dex != nil {
    432 		props.Compile_dex = module.Library.Module.deviceProperties.Compile_dex
    433 	}
    434 
    435 	if module.SocSpecific() {
    436 		props.Soc_specific = proptools.BoolPtr(true)
    437 	} else if module.DeviceSpecific() {
    438 		props.Device_specific = proptools.BoolPtr(true)
    439 	} else if module.ProductSpecific() {
    440 		props.Product_specific = proptools.BoolPtr(true)
    441 	}
    442 
    443 	mctx.CreateModule(android.ModuleFactoryAdaptor(LibraryFactory), &props)
    444 }
    445 
    446 // Creates a droiddoc module that creates stubs source files from the given full source
    447 // files
    448 func (module *SdkLibrary) createDocs(mctx android.TopDownMutatorContext, apiScope apiScope) {
    449 	props := struct {
    450 		Name                             *string
    451 		Srcs                             []string
    452 		Installable                      *bool
    453 		Srcs_lib                         *string
    454 		Srcs_lib_whitelist_dirs          []string
    455 		Srcs_lib_whitelist_pkgs          []string
    456 		Libs                             []string
    457 		Arg_files                        []string
    458 		Args                             *string
    459 		Api_tag_name                     *string
    460 		Api_filename                     *string
    461 		Removed_api_filename             *string
    462 		No_standard_libs                 *bool
    463 		Java_version                     *string
    464 		Merge_annotations_dirs           []string
    465 		Merge_inclusion_annotations_dirs []string
    466 		Check_api                        struct {
    467 			Current                   ApiToCheck
    468 			Last_released             ApiToCheck
    469 			Ignore_missing_latest_api *bool
    470 		}
    471 		Aidl struct {
    472 			Include_dirs       []string
    473 			Local_include_dirs []string
    474 		}
    475 	}{}
    476 
    477 	props.Name = proptools.StringPtr(module.docsName(apiScope))
    478 	props.Srcs = append(props.Srcs, module.Library.Module.properties.Srcs...)
    479 	props.Srcs = append(props.Srcs, module.sdkLibraryProperties.Api_srcs...)
    480 	props.Installable = proptools.BoolPtr(false)
    481 	// A droiddoc module has only one Libs property and doesn't distinguish between
    482 	// shared libs and static libs. So we need to add both of these libs to Libs property.
    483 	props.Libs = module.Library.Module.properties.Libs
    484 	props.Libs = append(props.Libs, module.Library.Module.properties.Static_libs...)
    485 	props.Aidl.Include_dirs = module.Library.Module.deviceProperties.Aidl.Include_dirs
    486 	props.Aidl.Local_include_dirs = module.Library.Module.deviceProperties.Aidl.Local_include_dirs
    487 	props.No_standard_libs = module.Library.Module.properties.No_standard_libs
    488 	props.Java_version = module.Library.Module.properties.Java_version
    489 
    490 	props.Merge_annotations_dirs = module.sdkLibraryProperties.Merge_annotations_dirs
    491 	props.Merge_inclusion_annotations_dirs = module.sdkLibraryProperties.Merge_inclusion_annotations_dirs
    492 
    493 	droiddocArgs := " --stub-packages " + strings.Join(module.sdkLibraryProperties.Api_packages, ":") +
    494 		" " + android.JoinWithPrefix(module.sdkLibraryProperties.Hidden_api_packages, " --hide-package ") +
    495 		" " + android.JoinWithPrefix(module.sdkLibraryProperties.Droiddoc_options, " ") +
    496 		" --hide MissingPermission --hide BroadcastBehavior " +
    497 		"--hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol " +
    498 		"--hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo"
    499 
    500 	switch apiScope {
    501 	case apiScopeSystem:
    502 		droiddocArgs = droiddocArgs + " -showAnnotation android.annotation.SystemApi"
    503 	case apiScopeTest:
    504 		droiddocArgs = droiddocArgs + " -showAnnotation android.annotation.TestApi"
    505 	}
    506 	props.Arg_files = module.sdkLibraryProperties.Droiddoc_option_files
    507 	props.Args = proptools.StringPtr(droiddocArgs)
    508 
    509 	// List of APIs identified from the provided source files are created. They are later
    510 	// compared against to the not-yet-released (a.k.a current) list of APIs and to the
    511 	// last-released (a.k.a numbered) list of API.
    512 	currentApiFileName := "current.txt"
    513 	removedApiFileName := "removed.txt"
    514 	switch apiScope {
    515 	case apiScopeSystem:
    516 		currentApiFileName = "system-" + currentApiFileName
    517 		removedApiFileName = "system-" + removedApiFileName
    518 	case apiScopeTest:
    519 		currentApiFileName = "test-" + currentApiFileName
    520 		removedApiFileName = "test-" + removedApiFileName
    521 	}
    522 	currentApiFileName = path.Join("api", currentApiFileName)
    523 	removedApiFileName = path.Join("api", removedApiFileName)
    524 	// TODO(jiyong): remove these three props
    525 	props.Api_tag_name = proptools.StringPtr(module.apiTagName(apiScope))
    526 	props.Api_filename = proptools.StringPtr(currentApiFileName)
    527 	props.Removed_api_filename = proptools.StringPtr(removedApiFileName)
    528 
    529 	// check against the not-yet-release API
    530 	props.Check_api.Current.Api_file = proptools.StringPtr(currentApiFileName)
    531 	props.Check_api.Current.Removed_api_file = proptools.StringPtr(removedApiFileName)
    532 
    533 	// check against the latest released API
    534 	props.Check_api.Last_released.Api_file = proptools.StringPtr(
    535 		module.latestApiFilegroupName(apiScope))
    536 	props.Check_api.Last_released.Removed_api_file = proptools.StringPtr(
    537 		module.latestRemovedApiFilegroupName(apiScope))
    538 	props.Check_api.Ignore_missing_latest_api = proptools.BoolPtr(true)
    539 	props.Srcs_lib = module.sdkLibraryProperties.Srcs_lib
    540 	props.Srcs_lib_whitelist_dirs = module.sdkLibraryProperties.Srcs_lib_whitelist_dirs
    541 	props.Srcs_lib_whitelist_pkgs = module.sdkLibraryProperties.Srcs_lib_whitelist_pkgs
    542 
    543 	mctx.CreateModule(android.ModuleFactoryAdaptor(DroidstubsFactory), &props)
    544 }
    545 
    546 // Creates the xml file that publicizes the runtime library
    547 func (module *SdkLibrary) createXmlFile(mctx android.TopDownMutatorContext) {
    548 	template := `
    549 <?xml version="1.0" encoding="utf-8"?>
    550 <!-- Copyright (C) 2018 The Android Open Source Project
    551 
    552      Licensed under the Apache License, Version 2.0 (the "License");
    553      you may not use this file except in compliance with the License.
    554      You may obtain a copy of the License at
    555   
    556           http://www.apache.org/licenses/LICENSE-2.0
    557   
    558      Unless required by applicable law or agreed to in writing, software
    559      distributed under the License is distributed on an "AS IS" BASIS,
    560      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    561      See the License for the specific language governing permissions and
    562      limitations under the License.
    563 -->
    564 
    565 <permissions>
    566     <library name="%s" file="%s"/>
    567 </permissions>
    568 `
    569 	// genrule to generate the xml file content from the template above
    570 	// TODO: preserve newlines in the generate xml file. Newlines are being squashed
    571 	// in the ninja file. Do we need to have an external tool for this?
    572 	xmlContent := fmt.Sprintf(template, module.BaseModuleName(), module.implPath())
    573 	genruleProps := struct {
    574 		Name *string
    575 		Cmd  *string
    576 		Out  []string
    577 	}{}
    578 	genruleProps.Name = proptools.StringPtr(module.xmlFileName() + "-gen")
    579 	genruleProps.Cmd = proptools.StringPtr("echo '" + xmlContent + "' > $(out)")
    580 	genruleProps.Out = []string{module.xmlFileName()}
    581 	mctx.CreateModule(android.ModuleFactoryAdaptor(genrule.GenRuleFactory), &genruleProps)
    582 
    583 	// creates a prebuilt_etc module to actually place the xml file under
    584 	// <partition>/etc/permissions
    585 	etcProps := struct {
    586 		Name             *string
    587 		Src              *string
    588 		Sub_dir          *string
    589 		Soc_specific     *bool
    590 		Device_specific  *bool
    591 		Product_specific *bool
    592 	}{}
    593 	etcProps.Name = proptools.StringPtr(module.xmlFileName())
    594 	etcProps.Src = proptools.StringPtr(":" + module.xmlFileName() + "-gen")
    595 	etcProps.Sub_dir = proptools.StringPtr("permissions")
    596 	if module.SocSpecific() {
    597 		etcProps.Soc_specific = proptools.BoolPtr(true)
    598 	} else if module.DeviceSpecific() {
    599 		etcProps.Device_specific = proptools.BoolPtr(true)
    600 	} else if module.ProductSpecific() {
    601 		etcProps.Product_specific = proptools.BoolPtr(true)
    602 	}
    603 	mctx.CreateModule(android.ModuleFactoryAdaptor(android.PrebuiltEtcFactory), &etcProps)
    604 }
    605 
    606 func (module *SdkLibrary) PrebuiltJars(ctx android.BaseContext, sdkVersion string) android.Paths {
    607 	var api, v string
    608 	if sdkVersion == "" {
    609 		api = "system"
    610 		v = "current"
    611 	} else if strings.Contains(sdkVersion, "_") {
    612 		t := strings.Split(sdkVersion, "_")
    613 		api = t[0]
    614 		v = t[1]
    615 	} else {
    616 		api = "public"
    617 		v = sdkVersion
    618 	}
    619 	dir := filepath.Join("prebuilts", "sdk", v, api)
    620 	jar := filepath.Join(dir, module.BaseModuleName()+".jar")
    621 	jarPath := android.ExistentPathForSource(ctx, jar)
    622 	if !jarPath.Valid() {
    623 		ctx.PropertyErrorf("sdk_library", "invalid sdk version %q, %q does not exist", v, jar)
    624 		return nil
    625 	}
    626 	return android.Paths{jarPath.Path()}
    627 }
    628 
    629 // to satisfy SdkLibraryDependency interface
    630 func (module *SdkLibrary) SdkHeaderJars(ctx android.BaseContext, sdkVersion string) android.Paths {
    631 	// This module is just a wrapper for the stubs.
    632 	if ctx.Config().UnbundledBuildUsePrebuiltSdks() {
    633 		return module.PrebuiltJars(ctx, sdkVersion)
    634 	} else {
    635 		if strings.HasPrefix(sdkVersion, "system_") {
    636 			return module.systemApiStubsPath
    637 		} else if sdkVersion == "" {
    638 			return module.Library.HeaderJars()
    639 		} else {
    640 			return module.publicApiStubsPath
    641 		}
    642 	}
    643 }
    644 
    645 // to satisfy SdkLibraryDependency interface
    646 func (module *SdkLibrary) SdkImplementationJars(ctx android.BaseContext, sdkVersion string) android.Paths {
    647 	// This module is just a wrapper for the stubs.
    648 	if ctx.Config().UnbundledBuildUsePrebuiltSdks() {
    649 		return module.PrebuiltJars(ctx, sdkVersion)
    650 	} else {
    651 		if strings.HasPrefix(sdkVersion, "system_") {
    652 			return module.systemApiStubsImplPath
    653 		} else if sdkVersion == "" {
    654 			return module.Library.ImplementationJars()
    655 		} else {
    656 			return module.publicApiStubsImplPath
    657 		}
    658 	}
    659 }
    660 
    661 func (module *SdkLibrary) SetNoDist() {
    662 	module.sdkLibraryProperties.No_dist = proptools.BoolPtr(true)
    663 }
    664 
    665 var javaSdkLibrariesKey = android.NewOnceKey("javaSdkLibraries")
    666 
    667 func javaSdkLibraries(config android.Config) *[]string {
    668 	return config.Once(javaSdkLibrariesKey, func() interface{} {
    669 		return &[]string{}
    670 	}).(*[]string)
    671 }
    672 
    673 // For a java_sdk_library module, create internal modules for stubs, docs,
    674 // runtime libs and xml file. If requested, the stubs and docs are created twice
    675 // once for public API level and once for system API level
    676 func SdkLibraryMutator(mctx android.TopDownMutatorContext) {
    677 	if module, ok := mctx.Module().(*SdkLibrary); ok {
    678 		module.createInternalModules(mctx)
    679 	} else if module, ok := mctx.Module().(syspropLibraryInterface); ok {
    680 		module.SyspropJavaModule().createInternalModules(mctx)
    681 	}
    682 }
    683 
    684 func (module *SdkLibrary) createInternalModules(mctx android.TopDownMutatorContext) {
    685 	if len(module.Library.Module.properties.Srcs) == 0 {
    686 		mctx.PropertyErrorf("srcs", "java_sdk_library must specify srcs")
    687 	}
    688 
    689 	if len(module.sdkLibraryProperties.Api_packages) == 0 {
    690 		mctx.PropertyErrorf("api_packages", "java_sdk_library must specify api_packages")
    691 	}
    692 
    693 	missing_current_api := false
    694 
    695 	for _, scope := range []string{"", "system-", "test-"} {
    696 		for _, api := range []string{"current.txt", "removed.txt"} {
    697 			path := path.Join(mctx.ModuleDir(), "api", scope+api)
    698 			p := android.ExistentPathForSource(mctx, path)
    699 			if !p.Valid() {
    700 				mctx.ModuleErrorf("Current api file %#v doesn't exist", path)
    701 				missing_current_api = true
    702 			}
    703 		}
    704 	}
    705 
    706 	if missing_current_api {
    707 		script := "build/soong/scripts/gen-java-current-api-files.sh"
    708 		p := android.ExistentPathForSource(mctx, script)
    709 
    710 		if !p.Valid() {
    711 			panic(fmt.Sprintf("script file %s doesn't exist", script))
    712 		}
    713 
    714 		mctx.ModuleErrorf("One or more current api files are missing. "+
    715 			"You can update them by:\n"+
    716 			"%s %q && m update-api", script, mctx.ModuleDir())
    717 		return
    718 	}
    719 
    720 	// for public API stubs
    721 	module.createStubsLibrary(mctx, apiScopePublic)
    722 	module.createDocs(mctx, apiScopePublic)
    723 
    724 	if !Bool(module.properties.No_standard_libs) {
    725 		// for system API stubs
    726 		module.createStubsLibrary(mctx, apiScopeSystem)
    727 		module.createDocs(mctx, apiScopeSystem)
    728 
    729 		// for test API stubs
    730 		module.createStubsLibrary(mctx, apiScopeTest)
    731 		module.createDocs(mctx, apiScopeTest)
    732 
    733 		// for runtime
    734 		module.createXmlFile(mctx)
    735 	}
    736 
    737 	// record java_sdk_library modules so that they are exported to make
    738 	javaSdkLibraries := javaSdkLibraries(mctx.Config())
    739 	javaSdkLibrariesLock.Lock()
    740 	defer javaSdkLibrariesLock.Unlock()
    741 	*javaSdkLibraries = append(*javaSdkLibraries, module.BaseModuleName())
    742 }
    743 
    744 func (module *SdkLibrary) InitSdkLibraryProperties() {
    745 	module.AddProperties(
    746 		&module.sdkLibraryProperties,
    747 		&module.Library.Module.properties,
    748 		&module.Library.Module.dexpreoptProperties,
    749 		&module.Library.Module.deviceProperties,
    750 		&module.Library.Module.protoProperties,
    751 	)
    752 
    753 	module.Library.Module.properties.Installable = proptools.BoolPtr(true)
    754 	module.Library.Module.deviceProperties.IsSDKLibrary = true
    755 }
    756 
    757 func SdkLibraryFactory() android.Module {
    758 	module := &SdkLibrary{}
    759 	module.InitSdkLibraryProperties()
    760 	InitJavaModule(module, android.HostAndDeviceSupported)
    761 	return module
    762 }
    763