blob: 13a9275cef5629bed5d193b4855670622971872e [file] [log] [blame]
Jiyong Parkc678ad32018-04-10 13:07:10 +09001// 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
15package java
16
17import (
18 "android/soong/android"
19 "android/soong/genrule"
20 "fmt"
Jiyong Park82484c02018-04-23 21:41:26 +090021 "io"
Jiyong Parkc678ad32018-04-10 13:07:10 +090022 "path"
Jiyong Park82484c02018-04-23 21:41:26 +090023 "sort"
Jiyong Parkc678ad32018-04-10 13:07:10 +090024 "strings"
Jiyong Park82484c02018-04-23 21:41:26 +090025 "sync"
Jiyong Parkc678ad32018-04-10 13:07:10 +090026
27 "github.com/google/blueprint"
28 "github.com/google/blueprint/proptools"
29)
30
31var (
32 sdkStubsLibrarySuffix = ".stubs"
33 sdkSystemApiSuffix = ".system"
34 sdkDocsSuffix = ".docs"
35 sdkImplLibrarySuffix = ".impl"
36 sdkXmlFileSuffix = ".xml"
37)
38
39type stubsLibraryDependencyTag struct {
40 blueprint.BaseDependencyTag
41 name string
42}
43
44var (
45 publicApiStubsTag = dependencyTag{name: "public"}
46 systemApiStubsTag = dependencyTag{name: "system"}
47)
48
Jiyong Park82484c02018-04-23 21:41:26 +090049var (
50 javaSdkLibrariesLock sync.Mutex
51)
52
Jiyong Parkc678ad32018-04-10 13:07:10 +090053// java_sdk_library is to make a Java library that implements optional platform APIs to apps.
54// It is actually a wrapper of several modules: 1) stubs library that clients are linked against
55// to, 2) droiddoc module that internally generates API stubs source files, 3) the real runtime
56// shared library that implements the APIs, and 4) XML file for adding the runtime lib to the
57// classpath at runtime if requested via <uses-library>.
58//
59// TODO: these are big features that are currently missing
60// 1) check for API consistency
61// 2) install stubs libs as the dist artifacts
62// 3) ensuring that apps have appropriate <uses-library> tag
63// 4) disallowing linking to the runtime shared lib
64// 5) HTML generation
65
66func init() {
67 android.RegisterModuleType("java_sdk_library", sdkLibraryFactory)
68
69 android.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
70 ctx.TopDown("java_sdk_library", sdkLibraryMutator).Parallel()
71 })
Jiyong Park82484c02018-04-23 21:41:26 +090072
73 android.RegisterMakeVarsProvider(pctx, func(ctx android.MakeVarsContext) {
74 javaSdkLibraries := javaSdkLibraries(ctx.Config())
75 sort.Strings(*javaSdkLibraries)
76 ctx.Strict("JAVA_SDK_LIBRARIES", strings.Join(*javaSdkLibraries, " "))
77 })
Jiyong Parkc678ad32018-04-10 13:07:10 +090078}
79
80type sdkLibraryProperties struct {
81 // list of source files used to compile the Java module. May be .java, .logtags, .proto,
82 // or .aidl files.
83 Srcs []string `android:"arch_variant"`
84
85 // list of of java libraries that will be in the classpath
86 Libs []string `android:"arch_variant"`
87
88 // list of java libraries that will be compiled into the resulting runtime jar.
89 // These libraries are not compiled into the stubs jar.
90 Static_libs []string `android:"arch_variant"`
91
92 // list of package names that will be documented and publicized as API
93 Api_packages []string
94
95 // TODO: determines whether to create HTML doc or not
96 //Html_doc *bool
97}
98
99type sdkLibrary struct {
100 android.ModuleBase
101 android.DefaultableModuleBase
102
103 properties sdkLibraryProperties
104
105 publicApiStubsPath android.Paths
106 systemApiStubsPath android.Paths
107}
108
109func (module *sdkLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
110 // Add dependencies to the stubs library
111 ctx.AddDependency(ctx.Module(), publicApiStubsTag, module.stubsName(false))
112 ctx.AddDependency(ctx.Module(), systemApiStubsTag, module.stubsName(true))
113}
114
115func (module *sdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
116 // Record the paths to the header jars of the stubs library.
117 // When this java_sdk_library is dependened from others via "libs" property,
118 // the recorded paths will be returned depending on the link type of the caller.
119 ctx.VisitDirectDeps(func(to android.Module) {
120 otherName := ctx.OtherModuleName(to)
121 tag := ctx.OtherModuleDependencyTag(to)
122
123 if stubs, ok := to.(Dependency); ok {
124 switch tag {
125 case publicApiStubsTag:
126 module.publicApiStubsPath = stubs.HeaderJars()
127 case systemApiStubsTag:
128 module.systemApiStubsPath = stubs.HeaderJars()
129 default:
130 ctx.ModuleErrorf("depends on module %q of unknown tag %q", otherName, tag)
131 }
132 }
133 })
134}
135
Jiyong Park82484c02018-04-23 21:41:26 +0900136func (module *sdkLibrary) AndroidMk() android.AndroidMkData {
137 // Create a phony module that installs the impl library, for the case when this lib is
138 // in PRODUCT_PACKAGES.
139 return android.AndroidMkData{
140 Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
141 fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
142 fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
143 fmt.Fprintln(w, "LOCAL_MODULE :=", name)
144 fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES := "+module.implName())
145 fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)")
146 },
147 }
148}
149
Jiyong Parkc678ad32018-04-10 13:07:10 +0900150// Module name of the stubs library
151func (module *sdkLibrary) stubsName(forSystemApi bool) string {
152 stubsName := module.BaseModuleName() + sdkStubsLibrarySuffix
153 if forSystemApi {
154 stubsName = stubsName + sdkSystemApiSuffix
155 }
156 return stubsName
157}
158
159// Module name of the docs
160func (module *sdkLibrary) docsName(forSystemApi bool) string {
161 docsName := module.BaseModuleName() + sdkDocsSuffix
162 if forSystemApi {
163 docsName = docsName + sdkSystemApiSuffix
164 }
165 return docsName
166}
167
168// Module name of the runtime implementation library
169func (module *sdkLibrary) implName() string {
170 return module.BaseModuleName() + sdkImplLibrarySuffix
171}
172
173// File path to the runtime implementation library
174func (module *sdkLibrary) implPath() string {
175 partition := "system"
176 if module.SocSpecific() {
177 partition = "vendor"
178 } else if module.DeviceSpecific() {
179 partition = "odm"
180 } else if module.ProductSpecific() {
181 partition = "product"
182 }
183 return "/" + partition + "/framework/" + module.implName() + ".jar"
184}
185
186// Module name of the XML file for the lib
187func (module *sdkLibrary) xmlFileName() string {
188 return module.BaseModuleName() + sdkXmlFileSuffix
189}
190
191// SDK version that the stubs library is built against. Note that this is always
192// *current. Older stubs library built with a numberd SDK version is created from
193// the prebuilt jar.
194func (module *sdkLibrary) sdkVersion(forSystemApi bool) string {
195 if forSystemApi {
196 return "system_current"
197 } else {
198 return "current"
199 }
200}
201
202// $(INTERNAL_PLATFORM_<apiTagName>_API_FILE) points to the generated
203// api file for the current source
204// TODO: remove this when apicheck is done in soong
205func (module *sdkLibrary) apiTagName(forSystemApi bool) string {
206 apiTagName := strings.Replace(strings.ToUpper(module.BaseModuleName()), ".", "_", -1)
207 if forSystemApi {
208 apiTagName = apiTagName + "_SYSTEM"
209 }
210 return apiTagName
211}
212
213// returns the path (relative to this module) to the API txt file. Files are located
214// ./<api_dir>/<api_level>.txt where <api_level> is either current, system-current, removed,
215// or system-removed.
216func (module *sdkLibrary) apiFilePath(apiLevel string, forSystemApi bool) string {
217 apiDir := "api"
218 apiFile := apiLevel
219 if forSystemApi {
220 apiFile = "system-" + apiFile
221 }
222 apiFile = apiFile + ".txt"
223
224 return path.Join(apiDir, apiFile)
225}
226
227// Creates a static java library that has API stubs
228func (module *sdkLibrary) createStubsLibrary(mctx android.TopDownMutatorContext, forSystemApi bool) {
229 props := struct {
230 Name *string
231 Srcs []string
232 Sdk_version *string
233 Soc_specific *bool
234 Device_specific *bool
235 Product_specific *bool
236 Product_variables struct {
237 Unbundled_build struct {
238 Enabled *bool
239 }
Jiyong Park82484c02018-04-23 21:41:26 +0900240 Pdk struct {
241 Enabled *bool
242 }
Jiyong Parkc678ad32018-04-10 13:07:10 +0900243 }
244 }{}
245
246 props.Name = proptools.StringPtr(module.stubsName(forSystemApi))
247 // sources are generated from the droiddoc
248 props.Srcs = []string{":" + module.docsName(forSystemApi)}
249 props.Sdk_version = proptools.StringPtr(module.sdkVersion(forSystemApi))
250 // Unbundled apps will use the prebult one from /prebuilts/sdk
251 props.Product_variables.Unbundled_build.Enabled = proptools.BoolPtr(false)
Jiyong Park82484c02018-04-23 21:41:26 +0900252 props.Product_variables.Pdk.Enabled = proptools.BoolPtr(false)
Jiyong Parkc678ad32018-04-10 13:07:10 +0900253
254 if module.SocSpecific() {
255 props.Soc_specific = proptools.BoolPtr(true)
256 } else if module.DeviceSpecific() {
257 props.Device_specific = proptools.BoolPtr(true)
258 } else if module.ProductSpecific() {
259 props.Product_specific = proptools.BoolPtr(true)
260 }
261
262 mctx.CreateModule(android.ModuleFactoryAdaptor(LibraryFactory(false)), &props)
263}
264
265// Creates a droiddoc module that creates stubs source files from the given full source
266// files
267func (module *sdkLibrary) createDocs(mctx android.TopDownMutatorContext, forSystemApi bool) {
268 props := struct {
269 Name *string
270 Srcs []string
271 Custom_template *string
272 Installable *bool
273 Srcs_lib *string
274 Srcs_lib_whitelist_dirs []string
275 Srcs_lib_whitelist_pkgs []string
276 Libs []string
277 Args *string
278 Api_tag_name *string
279 Api_filename *string
280 Removed_api_filename *string
281 }{}
282
283 props.Name = proptools.StringPtr(module.docsName(forSystemApi))
284 props.Srcs = module.properties.Srcs
285 props.Custom_template = proptools.StringPtr("droiddoc-templates-sdk")
286 props.Installable = proptools.BoolPtr(false)
287 props.Libs = module.properties.Libs
288
289 droiddocArgs := " -hide 110 -hide 111 -hide 113 -hide 121 -hide 125 -hide 126 -hide 127 -hide 128" +
290 " -stubpackages " + strings.Join(module.properties.Api_packages, ":") +
291 " -nodocs"
292 if forSystemApi {
293 droiddocArgs = droiddocArgs + " -showAnnotation android.annotation.SystemApi"
294 }
295 props.Args = proptools.StringPtr(droiddocArgs)
296
297 // List of APIs identified from the provided source files are created. They are later
298 // compared against to the not-yet-released (a.k.a current) list of APIs and to the
299 // last-released (a.k.a numbered) list of API.
300 // TODO: If any incompatible change is detected, break the build
301 currentApiFileName := "current.txt"
302 removedApiFileName := "removed.txt"
303 if forSystemApi {
304 currentApiFileName = "system-" + currentApiFileName
305 removedApiFileName = "system-" + removedApiFileName
306 }
307 currentApiFileName = path.Join("api", currentApiFileName)
308 removedApiFileName = path.Join("api", removedApiFileName)
309 props.Api_tag_name = proptools.StringPtr(module.apiTagName(forSystemApi))
310 // Note: the exact names of these two are not important because they are always
311 // referenced by the make variable $(INTERNAL_PLATFORM_<TAG_NAME>_API_FILE)
312 props.Api_filename = proptools.StringPtr(currentApiFileName)
313 props.Removed_api_filename = proptools.StringPtr(removedApiFileName)
314
Jiyong Park82484c02018-04-23 21:41:26 +0900315 // Include the part of the framework source. This is required for the case when
316 // API class is extending from the framework class. In that case, doclava needs
317 // to know whether the base class is hidden or not. Since that information is
318 // encoded as @hide string in the comment, we need source files for the classes,
319 // not the compiled ones. Also there are rare cases where part of SDK library is
320 // implemented in the framework (e.g. org.apache.http.legacy). In that case,
321 // we need framework source to make API stubs, though the sources are not
322 // required to build the runtime library.
Jiyong Parkc678ad32018-04-10 13:07:10 +0900323 props.Srcs_lib = proptools.StringPtr("framework")
324 props.Srcs_lib_whitelist_dirs = []string{"core/java"}
Jiyong Park82484c02018-04-23 21:41:26 +0900325 props.Srcs_lib_whitelist_pkgs = module.properties.Api_packages
326 // Add android.annotation package to give access to the framework-defined
327 // annotations such as SystemApi, NonNull, etc.
328 props.Srcs_lib_whitelist_pkgs = append(props.Srcs_lib_whitelist_pkgs, "android.annotation")
329 // These libs are required by doclava to parse the framework sources add via
330 // Src_lib and Src_lib_whitelist_* properties just above.
Jiyong Parkc678ad32018-04-10 13:07:10 +0900331 // If we don't add them to the classpath, errors messages are generated by doclava,
332 // though they don't break the build.
Jiyong Park82484c02018-04-23 21:41:26 +0900333 props.Libs = append(props.Libs, "conscrypt", "bouncycastle", "okhttp", "framework")
Jiyong Parkc678ad32018-04-10 13:07:10 +0900334
335 mctx.CreateModule(android.ModuleFactoryAdaptor(DroiddocFactory), &props)
336}
337
338// Creates the runtime library. This is not directly linkable from other modules.
339func (module *sdkLibrary) createImplLibrary(mctx android.TopDownMutatorContext) {
340 props := struct {
341 Name *string
342 Srcs []string
343 Libs []string
344 Static_libs []string
345 Soc_specific *bool
346 Device_specific *bool
347 Product_specific *bool
348 Required []string
349 }{}
350
351 props.Name = proptools.StringPtr(module.implName())
352 props.Srcs = module.properties.Srcs
353 props.Libs = module.properties.Libs
354 props.Static_libs = module.properties.Static_libs
355 // XML file is installed along with the impl lib
356 props.Required = []string{module.xmlFileName()}
357
358 if module.SocSpecific() {
359 props.Soc_specific = proptools.BoolPtr(true)
360 } else if module.DeviceSpecific() {
361 props.Device_specific = proptools.BoolPtr(true)
362 } else if module.ProductSpecific() {
363 props.Product_specific = proptools.BoolPtr(true)
364 }
365
366 mctx.CreateModule(android.ModuleFactoryAdaptor(LibraryFactory(true)), &props)
367}
368
369// Creates the xml file that publicizes the runtime library
370func (module *sdkLibrary) createXmlFile(mctx android.TopDownMutatorContext) {
371 template := `
372<?xml version="1.0" encoding="utf-8"?>
373<!-- Copyright (C) 2018 The Android Open Source Project
374
375 Licensed under the Apache License, Version 2.0 (the "License");
376 you may not use this file except in compliance with the License.
377 You may obtain a copy of the License at
378
379 http://www.apache.org/licenses/LICENSE-2.0
380
381 Unless required by applicable law or agreed to in writing, software
382 distributed under the License is distributed on an "AS IS" BASIS,
383 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
384 See the License for the specific language governing permissions and
385 limitations under the License.
386-->
387
388<permissions>
389 <library name="%s" file="%s"/>
390</permissions>
391`
392 // genrule to generate the xml file content from the template above
393 // TODO: preserve newlines in the generate xml file. Newlines are being squashed
394 // in the ninja file. Do we need to have an external tool for this?
395 xmlContent := fmt.Sprintf(template, module.BaseModuleName(), module.implPath())
396 genruleProps := struct {
397 Name *string
398 Cmd *string
399 Out []string
400 }{}
401 genruleProps.Name = proptools.StringPtr(module.xmlFileName() + "-gen")
402 genruleProps.Cmd = proptools.StringPtr("echo '" + xmlContent + "' > $(out)")
403 genruleProps.Out = []string{module.xmlFileName()}
404 mctx.CreateModule(android.ModuleFactoryAdaptor(genrule.GenRuleFactory), &genruleProps)
405
406 // creates a prebuilt_etc module to actually place the xml file under
407 // <partition>/etc/permissions
408 etcProps := struct {
409 Name *string
410 Srcs []string
411 Sub_dir *string
412 Soc_specific *bool
413 Device_specific *bool
414 Product_specific *bool
415 }{}
416 etcProps.Name = proptools.StringPtr(module.xmlFileName())
417 etcProps.Srcs = []string{":" + module.xmlFileName() + "-gen"}
418 etcProps.Sub_dir = proptools.StringPtr("permissions")
419 if module.SocSpecific() {
420 etcProps.Soc_specific = proptools.BoolPtr(true)
421 } else if module.DeviceSpecific() {
422 etcProps.Device_specific = proptools.BoolPtr(true)
423 } else if module.ProductSpecific() {
424 etcProps.Product_specific = proptools.BoolPtr(true)
425 }
426 mctx.CreateModule(android.ModuleFactoryAdaptor(android.PrebuiltEtcFactory), &etcProps)
427}
428
429// to satisfy SdkLibraryDependency interface
430func (module *sdkLibrary) HeaderJars(linkType linkType) android.Paths {
431 // This module is just a wrapper for the stubs.
432 if linkType == javaSystem || linkType == javaPlatform {
433 return module.systemApiStubsPath
434 } else {
435 return module.publicApiStubsPath
436 }
437}
438
Jiyong Park82484c02018-04-23 21:41:26 +0900439func javaSdkLibraries(config android.Config) *[]string {
440 return config.Once("javaSdkLibraries", func() interface{} {
441 return &[]string{}
442 }).(*[]string)
443}
444
Jiyong Parkc678ad32018-04-10 13:07:10 +0900445// For a java_sdk_library module, create internal modules for stubs, docs,
446// runtime libs and xml file. If requested, the stubs and docs are created twice
447// once for public API level and once for system API level
448func sdkLibraryMutator(mctx android.TopDownMutatorContext) {
449 if module, ok := mctx.Module().(*sdkLibrary); ok {
450 // for public API stubs
451 module.createStubsLibrary(mctx, false)
452 module.createDocs(mctx, false)
453
454 // for system API stubs
455 module.createStubsLibrary(mctx, true)
456 module.createDocs(mctx, true)
457
458 // for runtime
459 module.createXmlFile(mctx)
460 module.createImplLibrary(mctx)
Jiyong Park82484c02018-04-23 21:41:26 +0900461
462 // record java_sdk_library modules so that they are exported to make
463 javaSdkLibraries := javaSdkLibraries(mctx.Config())
464 javaSdkLibrariesLock.Lock()
465 defer javaSdkLibrariesLock.Unlock()
466 *javaSdkLibraries = append(*javaSdkLibraries, module.BaseModuleName())
Jiyong Parkc678ad32018-04-10 13:07:10 +0900467 }
468}
469
470func sdkLibraryFactory() android.Module {
471 module := &sdkLibrary{}
472 module.AddProperties(&module.properties)
473 android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
474 android.InitDefaultableModule(module)
475 return module
476}