blob: ce6082799f033db5df90e9ea5d5cdf6059538e9a [file] [log] [blame]
Jiyong Park9b409bc2019-10-11 14:59:13 +09001// Copyright (C) 2019 The Android Open Source Project
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 sdk
16
17import (
18 "fmt"
19 "io"
20 "path/filepath"
21 "strconv"
22 "strings"
23
24 "github.com/google/blueprint/proptools"
25
26 "android/soong/android"
Jiyong Park73c54ee2019-10-22 20:31:18 +090027 "android/soong/cc"
Jiyong Park9b409bc2019-10-11 14:59:13 +090028 "android/soong/java"
29)
30
31var pctx = android.NewPackageContext("android/soong/sdk")
32
33// generatedFile abstracts operations for writing contents into a file and emit a build rule
34// for the file.
35type generatedFile struct {
Jiyong Park73c54ee2019-10-22 20:31:18 +090036 path android.OutputPath
37 content strings.Builder
38 indentLevel int
Jiyong Park9b409bc2019-10-11 14:59:13 +090039}
40
41func newGeneratedFile(ctx android.ModuleContext, name string) *generatedFile {
42 return &generatedFile{
Jiyong Park73c54ee2019-10-22 20:31:18 +090043 path: android.PathForModuleOut(ctx, name).OutputPath,
44 indentLevel: 0,
Jiyong Park9b409bc2019-10-11 14:59:13 +090045 }
46}
47
Jiyong Park73c54ee2019-10-22 20:31:18 +090048func (gf *generatedFile) indent() {
49 gf.indentLevel++
50}
51
52func (gf *generatedFile) dedent() {
53 gf.indentLevel--
54}
55
Jiyong Park9b409bc2019-10-11 14:59:13 +090056func (gf *generatedFile) printfln(format string, args ...interface{}) {
57 // ninja consumes newline characters in rspfile_content. Prevent it by
58 // escaping the backslash in the newline character. The extra backshash
59 // is removed when the rspfile is written to the actual script file
Jiyong Park73c54ee2019-10-22 20:31:18 +090060 fmt.Fprintf(&(gf.content), strings.Repeat(" ", gf.indentLevel)+format+"\\n", args...)
Jiyong Park9b409bc2019-10-11 14:59:13 +090061}
62
63func (gf *generatedFile) build(pctx android.PackageContext, ctx android.BuilderContext, implicits android.Paths) {
64 rb := android.NewRuleBuilder()
65 // convert \\n to \n
66 rb.Command().
67 Implicits(implicits).
68 Text("echo").Text(proptools.ShellEscape(gf.content.String())).
69 Text("| sed 's/\\\\n/\\n/g' >").Output(gf.path)
70 rb.Command().
71 Text("chmod a+x").Output(gf.path)
72 rb.Build(pctx, ctx, gf.path.Base(), "Build "+gf.path.Base())
73}
74
Jiyong Park73c54ee2019-10-22 20:31:18 +090075func (s *sdk) javaLibs(ctx android.ModuleContext) []*java.Library {
76 result := []*java.Library{}
Jiyong Park9b409bc2019-10-11 14:59:13 +090077 ctx.VisitDirectDeps(func(m android.Module) {
Jiyong Park73c54ee2019-10-22 20:31:18 +090078 if j, ok := m.(*java.Library); ok {
79 result = append(result, j)
Jiyong Park9b409bc2019-10-11 14:59:13 +090080 }
81 })
82 return result
83}
84
Jiyong Park73c54ee2019-10-22 20:31:18 +090085// archSpecificNativeLibInfo represents an arch-specific variant of a native lib
86type archSpecificNativeLibInfo struct {
87 name string
88 archType string
89 exportedIncludeDirs android.Paths
90 exportedSystemIncludeDirs android.Paths
91 exportedFlags []string
92 outputFile android.Path
93}
Jiyong Park9b409bc2019-10-11 14:59:13 +090094
Jiyong Park73c54ee2019-10-22 20:31:18 +090095func (lib *archSpecificNativeLibInfo) signature() string {
96 return fmt.Sprintf("%v %v %v %v",
97 lib.name,
98 lib.exportedIncludeDirs.Strings(),
99 lib.exportedSystemIncludeDirs.Strings(),
100 lib.exportedFlags)
101}
102
103// nativeLibInfo represents a collection of arch-specific modules having the same name
104type nativeLibInfo struct {
105 name string
106 archVariants []archSpecificNativeLibInfo
107 // hasArchSpecificFlags is set to true if modules for each architecture all have the same
108 // include dirs, flags, etc, in which case only those of the first arch is selected.
109 hasArchSpecificFlags bool
110}
111
112// nativeMemberInfos collects all cc.Modules that are member of an SDK.
113func (s *sdk) nativeMemberInfos(ctx android.ModuleContext) []*nativeLibInfo {
114 infoMap := make(map[string]*nativeLibInfo)
115
116 // Collect cc.Modules
117 ctx.VisitDirectDeps(func(m android.Module) {
118 ccModule, ok := m.(*cc.Module)
119 if !ok {
120 return
121 }
122 depName := ctx.OtherModuleName(m)
123
124 if _, ok := infoMap[depName]; !ok {
125 infoMap[depName] = &nativeLibInfo{name: depName}
126 }
127
128 info := infoMap[depName]
129 info.archVariants = append(info.archVariants, archSpecificNativeLibInfo{
130 name: ccModule.BaseModuleName(),
131 archType: ccModule.Target().Arch.ArchType.String(),
132 exportedIncludeDirs: ccModule.ExportedIncludeDirs(),
133 exportedSystemIncludeDirs: ccModule.ExportedSystemIncludeDirs(),
134 exportedFlags: ccModule.ExportedFlags(),
135 outputFile: ccModule.OutputFile().Path(),
136 })
137 })
138
139 // Determine if include dirs and flags for each module are different across arch-specific
140 // modules or not. And set hasArchSpecificFlags accordingly
141 for _, info := range infoMap {
142 // by default, include paths and flags are assumed to be the same across arches
143 info.hasArchSpecificFlags = false
144 oldSignature := ""
145 for _, av := range info.archVariants {
146 newSignature := av.signature()
147 if oldSignature == "" {
148 oldSignature = newSignature
149 }
150 if oldSignature != newSignature {
151 info.hasArchSpecificFlags = true
152 break
153 }
154 }
Jiyong Park9b409bc2019-10-11 14:59:13 +0900155 }
156
Jiyong Park73c54ee2019-10-22 20:31:18 +0900157 var list []*nativeLibInfo
158 for _, v := range infoMap {
159 list = append(list, v)
160 }
161 return list
162}
Jiyong Park9b409bc2019-10-11 14:59:13 +0900163
Jiyong Park73c54ee2019-10-22 20:31:18 +0900164// SDK directory structure
165// <sdk_root>/
166// Android.bp : definition of a 'sdk' module is here. This is a hand-made one.
167// <api_ver>/ : below this directory are all auto-generated
168// Android.bp : definition of 'sdk_snapshot' module is here
169// aidl/
170// frameworks/base/core/..../IFoo.aidl : an exported AIDL file
171// java/
172// java/<module_name>/stub.jar : a stub jar for a java library 'module_name'
173// include/
174// bionic/libc/include/stdlib.h : an exported header file
175// include_gen/
176// com/android/.../IFoo.h : a generated header file
177// <arch>/include/ : arch-specific exported headers
178// <arch>/include_gen/ : arch-specific generated headers
179// <arch>/lib/
180// libFoo.so : a stub library
181
182const (
183 aidlIncludeDir = "aidl"
184 javaStubDir = "java"
185 javaStubFile = "stub.jar"
186 nativeIncludeDir = "include"
187 nativeGeneratedIncludeDir = "include_gen"
188 nativeStubDir = "lib"
189 nativeStubFileSuffix = ".so"
190)
191
192// path to the stub file of a java library. Relative to <sdk_root>/<api_dir>
193func javaStubFilePathFor(javaLib *java.Library) string {
194 return filepath.Join(javaStubDir, javaLib.Name(), javaStubFile)
195}
196
197// path to the stub file of a native shared library. Relative to <sdk_root>/<api_dir>
198func nativeStubFilePathFor(lib archSpecificNativeLibInfo) string {
199 return filepath.Join(lib.archType,
200 nativeStubDir, lib.name+nativeStubFileSuffix)
201}
202
203// paths to the include dirs of a native shared library. Relative to <sdk_root>/<api_dir>
204func nativeIncludeDirPathsFor(ctx android.ModuleContext, lib archSpecificNativeLibInfo,
205 systemInclude bool, archSpecific bool) []string {
206 var result []string
207 buildDir := ctx.Config().BuildDir()
208 var includeDirs []android.Path
209 if !systemInclude {
210 includeDirs = lib.exportedIncludeDirs
211 } else {
212 includeDirs = lib.exportedSystemIncludeDirs
213 }
214 for _, dir := range includeDirs {
215 var path string
216 if gen := strings.HasPrefix(dir.String(), buildDir); gen {
217 path = filepath.Join(nativeGeneratedIncludeDir, dir.Rel())
218 } else {
219 path = filepath.Join(nativeIncludeDir, dir.String())
220 }
221 if archSpecific {
222 path = filepath.Join(lib.archType, path)
223 }
224 result = append(result, path)
225 }
226 return result
227}
228
229// A name that uniquely identifies an prebuilt SDK member for a version of SDK snapshot
230// This isn't visible to users, so could be changed in future.
231func versionedSdkMemberName(ctx android.ModuleContext, memberName string, version string) string {
232 return ctx.ModuleName() + "_" + memberName + string(android.SdkVersionSeparator) + version
233}
234
235// arm64, arm, x86, x86_64, etc.
236func archTypeOf(module android.Module) string {
237 return module.Target().Arch.ArchType.String()
238}
239
240// buildAndroidBp creates the blueprint file that defines prebuilt modules for each of
241// the SDK members, and the entire sdk_snapshot module for the specified version
242func (s *sdk) buildAndroidBp(ctx android.ModuleContext, version string) android.OutputPath {
243 bp := newGeneratedFile(ctx, "blueprint-"+version+".bp")
244 bp.printfln("// This is auto-generated. DO NOT EDIT.")
245 bp.printfln("")
246
247 javaLibModules := s.javaLibs(ctx)
248 for _, m := range javaLibModules {
249 name := m.Name()
Jiyong Park9b409bc2019-10-11 14:59:13 +0900250 bp.printfln("java_import {")
Jiyong Park73c54ee2019-10-22 20:31:18 +0900251 bp.indent()
252 bp.printfln("name: %q,", versionedSdkMemberName(ctx, name, version))
253 bp.printfln("sdk_member_name: %q,", name)
254 bp.printfln("jars: [%q],", javaStubFilePathFor(m))
255 bp.dedent()
Jiyong Park9b409bc2019-10-11 14:59:13 +0900256 bp.printfln("}")
257 bp.printfln("")
258
259 // This module is for the case when the source tree for the unversioned module
260 // doesn't exist (i.e. building in an unbundled tree). "prefer:" is set to false
261 // so that this module does not eclipse the unversioned module if it exists.
262 bp.printfln("java_import {")
Jiyong Park73c54ee2019-10-22 20:31:18 +0900263 bp.indent()
264 bp.printfln("name: %q,", name)
265 bp.printfln("jars: [%q],", javaStubFilePathFor(m))
266 bp.printfln("prefer: false,")
267 bp.dedent()
Jiyong Park9b409bc2019-10-11 14:59:13 +0900268 bp.printfln("}")
269 bp.printfln("")
Jiyong Park9b409bc2019-10-11 14:59:13 +0900270 }
271
Jiyong Park73c54ee2019-10-22 20:31:18 +0900272 nativeLibInfos := s.nativeMemberInfos(ctx)
273 for _, info := range nativeLibInfos {
274 bp.printfln("cc_prebuilt_library_shared {")
275 bp.indent()
276 bp.printfln("name: %q,", versionedSdkMemberName(ctx, info.name, version))
277 bp.printfln("sdk_member_name: %q,", info.name)
278
279 // a function for emitting include dirs
280 printExportedDirsForNativeLibs := func(lib archSpecificNativeLibInfo, systemInclude bool) {
281 includeDirs := nativeIncludeDirPathsFor(ctx, lib, systemInclude, info.hasArchSpecificFlags)
282 if len(includeDirs) == 0 {
283 return
284 }
285 if !systemInclude {
286 bp.printfln("export_include_dirs: [")
287 } else {
288 bp.printfln("export_system_include_dirs: [")
289 }
290 bp.indent()
291 for _, dir := range includeDirs {
292 bp.printfln("%q,", dir)
293 }
294 bp.dedent()
295 bp.printfln("],")
296 }
297
298 if !info.hasArchSpecificFlags {
299 printExportedDirsForNativeLibs(info.archVariants[0], false /*systemInclude*/)
300 printExportedDirsForNativeLibs(info.archVariants[0], true /*systemInclude*/)
301 }
302
303 bp.printfln("arch: {")
304 bp.indent()
305 for _, av := range info.archVariants {
306 bp.printfln("%s: {", av.archType)
307 bp.indent()
308 bp.printfln("srcs: [%q],", nativeStubFilePathFor(av))
309 if info.hasArchSpecificFlags {
310 // export_* properties are added inside the arch: {<arch>: {...}} block
311 printExportedDirsForNativeLibs(av, false /*systemInclude*/)
312 printExportedDirsForNativeLibs(av, true /*systemInclude*/)
313 }
314 bp.dedent()
315 bp.printfln("},") // <arch>
316 }
317 bp.dedent()
318 bp.printfln("},") // arch
319 bp.printfln("stl: \"none\",")
320 bp.printfln("system_shared_libs: [],")
321 bp.dedent()
322 bp.printfln("}") // cc_prebuilt_library_shared
323 bp.printfln("")
324 }
Jiyong Park9b409bc2019-10-11 14:59:13 +0900325
326 bp.printfln("sdk_snapshot {")
Jiyong Park73c54ee2019-10-22 20:31:18 +0900327 bp.indent()
328 bp.printfln("name: %q,", ctx.ModuleName()+string(android.SdkVersionSeparator)+version)
329 if len(javaLibModules) > 0 {
330 bp.printfln("java_libs: [")
331 bp.indent()
332 for _, m := range javaLibModules {
333 bp.printfln("%q,", versionedSdkMemberName(ctx, m.Name(), version))
334 }
335 bp.dedent()
336 bp.printfln("],") // java_libs
Jiyong Park9b409bc2019-10-11 14:59:13 +0900337 }
Jiyong Park73c54ee2019-10-22 20:31:18 +0900338 if len(nativeLibInfos) > 0 {
339 bp.printfln("native_shared_libs: [")
340 bp.indent()
341 for _, info := range nativeLibInfos {
342 bp.printfln("%q,", versionedSdkMemberName(ctx, info.name, version))
343 }
344 bp.dedent()
345 bp.printfln("],") // native_shared_libs
346 }
347 bp.dedent()
348 bp.printfln("}") // sdk_snapshot
Jiyong Park9b409bc2019-10-11 14:59:13 +0900349 bp.printfln("")
350
351 bp.build(pctx, ctx, nil)
352 return bp.path
353}
354
355func (s *sdk) buildScript(ctx android.ModuleContext, version string) android.OutputPath {
356 sh := newGeneratedFile(ctx, "update_prebuilt-"+version+".sh")
Jiyong Park73c54ee2019-10-22 20:31:18 +0900357 buildDir := ctx.Config().BuildDir()
Jiyong Park9b409bc2019-10-11 14:59:13 +0900358
Jiyong Park73c54ee2019-10-22 20:31:18 +0900359 snapshotPath := func(paths ...string) string {
360 return filepath.Join(ctx.ModuleDir(), version, filepath.Join(paths...))
361 }
Jiyong Park9b409bc2019-10-11 14:59:13 +0900362
Jiyong Park73c54ee2019-10-22 20:31:18 +0900363 // TODO(jiyong) instead of creating script, create a zip file having the Android.bp, the headers,
364 // and the stubs and put it to the dist directory. The dist'ed zip file then would be downloaded,
365 // unzipped and then uploaded to gerrit again.
Jiyong Park9b409bc2019-10-11 14:59:13 +0900366 sh.printfln("#!/bin/bash")
Jiyong Park73c54ee2019-10-22 20:31:18 +0900367 sh.printfln("echo Updating snapshot of %s in %s", ctx.ModuleName(), snapshotPath())
Jiyong Park9b409bc2019-10-11 14:59:13 +0900368 sh.printfln("pushd $ANDROID_BUILD_TOP > /dev/null")
Jiyong Park73c54ee2019-10-22 20:31:18 +0900369 sh.printfln("mkdir -p %s", snapshotPath(aidlIncludeDir))
370 sh.printfln("mkdir -p %s", snapshotPath(javaStubDir))
371 sh.printfln("mkdir -p %s", snapshotPath(nativeIncludeDir))
372 sh.printfln("mkdir -p %s", snapshotPath(nativeGeneratedIncludeDir))
373 for _, target := range ctx.MultiTargets() {
374 arch := target.Arch.ArchType.String()
375 sh.printfln("mkdir -p %s", snapshotPath(arch, nativeStubDir))
376 sh.printfln("mkdir -p %s", snapshotPath(arch, nativeIncludeDir))
377 sh.printfln("mkdir -p %s", snapshotPath(arch, nativeGeneratedIncludeDir))
378 }
Jiyong Park9b409bc2019-10-11 14:59:13 +0900379
380 var implicits android.Paths
Jiyong Park73c54ee2019-10-22 20:31:18 +0900381 for _, m := range s.javaLibs(ctx) {
382 headerJars := m.HeaderJars()
383 if len(headerJars) != 1 {
384 panic(fmt.Errorf("there must be only one header jar from %q", m.Name()))
Jiyong Park9b409bc2019-10-11 14:59:13 +0900385 }
Jiyong Park73c54ee2019-10-22 20:31:18 +0900386 implicits = append(implicits, headerJars...)
387
388 exportedAidlIncludeDirs := m.AidlIncludeDirs()
389 for _, dir := range exportedAidlIncludeDirs {
390 // Using tar to copy with the directory structure
391 // TODO(jiyong): copy parcelable declarations only
392 sh.printfln("find %s -name \"*.aidl\" | tar cf - -T - | (cd %s; tar xf -)",
393 dir.String(), snapshotPath(aidlIncludeDir))
394 }
395
396 copyTarget := snapshotPath(javaStubFilePathFor(m))
397 sh.printfln("mkdir -p %s && cp %s %s",
398 filepath.Dir(copyTarget), headerJars[0].String(), copyTarget)
399 }
400
401 nativeLibInfos := s.nativeMemberInfos(ctx)
402 for _, info := range nativeLibInfos {
403
404 // a function for emitting include dirs
405 printExportedDirCopyCommandsForNativeLibs := func(lib archSpecificNativeLibInfo) {
406 includeDirs := lib.exportedIncludeDirs
407 includeDirs = append(includeDirs, lib.exportedSystemIncludeDirs...)
408 if len(includeDirs) == 0 {
409 return
410 }
411 for _, dir := range includeDirs {
412 gen := strings.HasPrefix(dir.String(), buildDir)
413 targetDir := nativeIncludeDir
414 if gen {
415 targetDir = nativeGeneratedIncludeDir
416 }
417 if info.hasArchSpecificFlags {
418 targetDir = filepath.Join(lib.archType, targetDir)
419 }
420 targetDir = snapshotPath(targetDir)
421
422 sourceDirRoot := "."
423 sourceDirRel := dir.String()
424 if gen {
425 // ex) out/soong/.intermediate/foo/bar/gen/aidl
426 sourceDirRoot = strings.TrimSuffix(dir.String(), dir.Rel())
427 sourceDirRel = dir.Rel()
428 }
429 // TODO(jiyong) copy headers having other suffixes
430 sh.printfln("(cd %s; find %s -name \"*.h\" | tar cf - -T - ) | (cd %s; tar xf -)",
431 sourceDirRoot, sourceDirRel, targetDir)
432 }
433 }
434
435 if !info.hasArchSpecificFlags {
436 printExportedDirCopyCommandsForNativeLibs(info.archVariants[0])
437 }
438
439 // for each architecture
440 for _, av := range info.archVariants {
441 stub := av.outputFile
442 implicits = append(implicits, stub)
443 copiedStub := snapshotPath(nativeStubFilePathFor(av))
444 sh.printfln("cp %s %s", stub.String(), copiedStub)
445
446 if info.hasArchSpecificFlags {
447 printExportedDirCopyCommandsForNativeLibs(av)
448 }
449 }
450 }
Jiyong Park9b409bc2019-10-11 14:59:13 +0900451
452 bp := s.buildAndroidBp(ctx, version)
453 implicits = append(implicits, bp)
Jiyong Park73c54ee2019-10-22 20:31:18 +0900454 sh.printfln("cp %s %s", bp.String(), snapshotPath("Android.bp"))
Jiyong Park9b409bc2019-10-11 14:59:13 +0900455
456 sh.printfln("popd > /dev/null")
457 sh.printfln("rm -- \"$0\"") // self deleting so that stale script is not used
458 sh.printfln("echo Done")
459
460 sh.build(pctx, ctx, implicits)
461 return sh.path
462}
463
464func (s *sdk) buildSnapshotGenerationScripts(ctx android.ModuleContext) {
465 if s.snapshot() {
466 // we don't need a script for sdk_snapshot.. as they are frozen
467 return
468 }
469
470 // script to update the 'current' snapshot
471 s.updateScript = s.buildScript(ctx, "current")
472
473 versions := s.frozenVersions(ctx)
474 newVersion := "1"
475 if len(versions) >= 1 {
476 lastVersion := versions[len(versions)-1]
477 lastVersionNum, err := strconv.Atoi(lastVersion)
478 if err != nil {
479 panic(err)
480 return
481 }
482 newVersion = strconv.Itoa(lastVersionNum + 1)
483 }
484 // script to create a new frozen version of snapshot
485 s.freezeScript = s.buildScript(ctx, newVersion)
486}
487
488func (s *sdk) androidMkEntriesForScript() android.AndroidMkEntries {
489 if s.snapshot() {
490 // we don't need a script for sdk_snapshot.. as they are frozen
491 return android.AndroidMkEntries{}
492 }
493
494 entries := android.AndroidMkEntries{
495 Class: "FAKE",
496 // TODO(jiyong): remove this? but androidmk.go expects OutputFile to be specified anyway
497 OutputFile: android.OptionalPathForPath(s.updateScript),
498 Include: "$(BUILD_SYSTEM)/base_rules.mk",
499 ExtraEntries: []android.AndroidMkExtraEntriesFunc{
500 func(entries *android.AndroidMkEntries) {
501 entries.AddStrings("LOCAL_ADDITIONAL_DEPENDENCIES",
502 s.updateScript.String(), s.freezeScript.String())
503 },
504 },
505 ExtraFooters: []android.AndroidMkExtraFootersFunc{
506 func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
507 fmt.Fprintln(w, "$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)")
508 fmt.Fprintln(w, " touch $@")
509 fmt.Fprintln(w, " echo ##################################################")
Jiyong Park73c54ee2019-10-22 20:31:18 +0900510 fmt.Fprintln(w, " echo To update current SDK: execute", filepath.Join("\\$$ANDROID_BUILD_TOP", s.updateScript.String()))
511 fmt.Fprintln(w, " echo To freeze current SDK: execute", filepath.Join("\\$$ANDROID_BUILD_TOP", s.freezeScript.String()))
Jiyong Park9b409bc2019-10-11 14:59:13 +0900512 fmt.Fprintln(w, " echo ##################################################")
513 },
514 },
515 }
516 return entries
517}