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