blob: 5235c9ef4ef02c3edbcf1720cde1f3796478c151 [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"
27 "android/soong/java"
28)
29
30var pctx = android.NewPackageContext("android/soong/sdk")
31
32// generatedFile abstracts operations for writing contents into a file and emit a build rule
33// for the file.
34type generatedFile struct {
35 path android.OutputPath
36 content strings.Builder
37}
38
39func newGeneratedFile(ctx android.ModuleContext, name string) *generatedFile {
40 return &generatedFile{
41 path: android.PathForModuleOut(ctx, name).OutputPath,
42 }
43}
44
45func (gf *generatedFile) printfln(format string, args ...interface{}) {
46 // ninja consumes newline characters in rspfile_content. Prevent it by
47 // escaping the backslash in the newline character. The extra backshash
48 // is removed when the rspfile is written to the actual script file
49 fmt.Fprintf(&(gf.content), format+"\\n", args...)
50}
51
52func (gf *generatedFile) build(pctx android.PackageContext, ctx android.BuilderContext, implicits android.Paths) {
53 rb := android.NewRuleBuilder()
54 // convert \\n to \n
55 rb.Command().
56 Implicits(implicits).
57 Text("echo").Text(proptools.ShellEscape(gf.content.String())).
58 Text("| sed 's/\\\\n/\\n/g' >").Output(gf.path)
59 rb.Command().
60 Text("chmod a+x").Output(gf.path)
61 rb.Build(pctx, ctx, gf.path.Base(), "Build "+gf.path.Base())
62}
63
64func (s *sdk) javaMemberNames(ctx android.ModuleContext) []string {
65 result := []string{}
66 ctx.VisitDirectDeps(func(m android.Module) {
67 if _, ok := m.(*java.Library); ok {
68 result = append(result, m.Name())
69 }
70 })
71 return result
72}
73
74// buildAndroidBp creates the blueprint file that defines prebuilt modules for each of
75// the SDK members, and the sdk_snapshot module for the specified version
76func (s *sdk) buildAndroidBp(ctx android.ModuleContext, version string) android.OutputPath {
77 bp := newGeneratedFile(ctx, "blueprint-"+version+".sh")
78
79 makePrebuiltName := func(name string) string {
80 return ctx.ModuleName() + "_" + name + string(android.SdkVersionSeparator) + version
81 }
82
83 javaLibs := s.javaMemberNames(ctx)
84 for _, name := range javaLibs {
85 prebuiltName := makePrebuiltName(name)
86 jar := filepath.Join("java", name, "stub.jar")
87
88 bp.printfln("java_import {")
89 bp.printfln(" name: %q,", prebuiltName)
90 bp.printfln(" jars: [%q],", jar)
91 bp.printfln(" sdk_member_name: %q,", name)
92 bp.printfln("}")
93 bp.printfln("")
94
95 // This module is for the case when the source tree for the unversioned module
96 // doesn't exist (i.e. building in an unbundled tree). "prefer:" is set to false
97 // so that this module does not eclipse the unversioned module if it exists.
98 bp.printfln("java_import {")
99 bp.printfln(" name: %q,", name)
100 bp.printfln(" jars: [%q],", jar)
101 bp.printfln(" prefer: false,")
102 bp.printfln("}")
103 bp.printfln("")
104
105 }
106
107 // TODO(jiyong): emit cc_prebuilt_library_shared for the native libs
108
109 bp.printfln("sdk_snapshot {")
110 bp.printfln(" name: %q,", ctx.ModuleName()+string(android.SdkVersionSeparator)+version)
111 bp.printfln(" java_libs: [")
112 for _, n := range javaLibs {
113 bp.printfln(" %q,", makePrebuiltName(n))
114 }
115 bp.printfln(" ],")
116 // TODO(jiyong): emit native_shared_libs
117 bp.printfln("}")
118 bp.printfln("")
119
120 bp.build(pctx, ctx, nil)
121 return bp.path
122}
123
124func (s *sdk) buildScript(ctx android.ModuleContext, version string) android.OutputPath {
125 sh := newGeneratedFile(ctx, "update_prebuilt-"+version+".sh")
126
127 snapshotRoot := filepath.Join(ctx.ModuleDir(), version)
128 aidlIncludeDir := filepath.Join(snapshotRoot, "aidl")
129 javaStubsDir := filepath.Join(snapshotRoot, "java")
130
131 sh.printfln("#!/bin/bash")
132 sh.printfln("echo Updating snapshot of %s in %s", ctx.ModuleName(), snapshotRoot)
133 sh.printfln("pushd $ANDROID_BUILD_TOP > /dev/null")
134 sh.printfln("rm -rf %s", snapshotRoot)
135 sh.printfln("mkdir -p %s", aidlIncludeDir)
136 sh.printfln("mkdir -p %s", javaStubsDir)
137 // TODO(jiyong): mkdir the 'native' dir
138
139 var implicits android.Paths
140 ctx.VisitDirectDeps(func(m android.Module) {
141 if javaLib, ok := m.(*java.Library); ok {
142 headerJars := javaLib.HeaderJars()
143 if len(headerJars) != 1 {
144 panic(fmt.Errorf("there must be only one header jar from %q", m.Name()))
145 }
146 implicits = append(implicits, headerJars...)
147
148 exportedAidlIncludeDirs := javaLib.AidlIncludeDirs()
149 for _, dir := range exportedAidlIncludeDirs {
150 // Using tar to copy with the directory structure
151 // TODO(jiyong): copy parcelable declarations only
152 sh.printfln("find %s -name \"*.aidl\" | tar cf - -T - | (cd %s; tar xf -)",
153 dir.String(), aidlIncludeDir)
154 }
155
156 copiedHeaderJar := filepath.Join(javaStubsDir, m.Name(), "stub.jar")
157 sh.printfln("mkdir -p $(dirname %s) && cp %s %s",
158 copiedHeaderJar, headerJars[0].String(), copiedHeaderJar)
159 }
160 // TODO(jiyong): emit the commands for copying the headers and stub libraries for native libs
161 })
162
163 bp := s.buildAndroidBp(ctx, version)
164 implicits = append(implicits, bp)
165 sh.printfln("cp %s %s", bp.String(), filepath.Join(snapshotRoot, "Android.bp"))
166
167 sh.printfln("popd > /dev/null")
168 sh.printfln("rm -- \"$0\"") // self deleting so that stale script is not used
169 sh.printfln("echo Done")
170
171 sh.build(pctx, ctx, implicits)
172 return sh.path
173}
174
175func (s *sdk) buildSnapshotGenerationScripts(ctx android.ModuleContext) {
176 if s.snapshot() {
177 // we don't need a script for sdk_snapshot.. as they are frozen
178 return
179 }
180
181 // script to update the 'current' snapshot
182 s.updateScript = s.buildScript(ctx, "current")
183
184 versions := s.frozenVersions(ctx)
185 newVersion := "1"
186 if len(versions) >= 1 {
187 lastVersion := versions[len(versions)-1]
188 lastVersionNum, err := strconv.Atoi(lastVersion)
189 if err != nil {
190 panic(err)
191 return
192 }
193 newVersion = strconv.Itoa(lastVersionNum + 1)
194 }
195 // script to create a new frozen version of snapshot
196 s.freezeScript = s.buildScript(ctx, newVersion)
197}
198
199func (s *sdk) androidMkEntriesForScript() android.AndroidMkEntries {
200 if s.snapshot() {
201 // we don't need a script for sdk_snapshot.. as they are frozen
202 return android.AndroidMkEntries{}
203 }
204
205 entries := android.AndroidMkEntries{
206 Class: "FAKE",
207 // TODO(jiyong): remove this? but androidmk.go expects OutputFile to be specified anyway
208 OutputFile: android.OptionalPathForPath(s.updateScript),
209 Include: "$(BUILD_SYSTEM)/base_rules.mk",
210 ExtraEntries: []android.AndroidMkExtraEntriesFunc{
211 func(entries *android.AndroidMkEntries) {
212 entries.AddStrings("LOCAL_ADDITIONAL_DEPENDENCIES",
213 s.updateScript.String(), s.freezeScript.String())
214 },
215 },
216 ExtraFooters: []android.AndroidMkExtraFootersFunc{
217 func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
218 fmt.Fprintln(w, "$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)")
219 fmt.Fprintln(w, " touch $@")
220 fmt.Fprintln(w, " echo ##################################################")
221 fmt.Fprintln(w, " echo To update current SDK: execute", s.updateScript.String())
222 fmt.Fprintln(w, " echo To freeze current SDK: execute", s.freezeScript.String())
223 fmt.Fprintln(w, " echo ##################################################")
224 },
225 },
226 }
227 return entries
228}