blob: d952a4cb15afc40738dacd68807464ce6cf8811f [file] [log] [blame]
Inseob Kim8471cda2019-11-15 09:59:12 +09001// Copyright 2020 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.
14package cc
15
16import (
17 "encoding/json"
18 "path/filepath"
19 "sort"
20 "strings"
21
22 "github.com/google/blueprint/proptools"
23
24 "android/soong/android"
25)
26
27func init() {
28 android.RegisterSingletonType("vendor-snapshot", VendorSnapshotSingleton)
29}
30
31func VendorSnapshotSingleton() android.Singleton {
32 return &vendorSnapshotSingleton{}
33}
34
35type vendorSnapshotSingleton struct {
36 vendorSnapshotZipFile android.OptionalPath
37}
38
39var (
40 // Modules under following directories are ignored. They are OEM's and vendor's
41 // proprietary modules(device/, vendor/, and hardware/).
42 // TODO(b/65377115): Clean up these with more maintainable way
43 vendorProprietaryDirs = []string{
44 "device",
45 "vendor",
46 "hardware",
47 }
48
49 // Modules under following directories are included as they are in AOSP,
50 // although hardware/ is normally for vendor's own.
51 // TODO(b/65377115): Clean up these with more maintainable way
52 aospDirsUnderProprietary = []string{
53 "hardware/interfaces",
54 "hardware/libhardware",
55 "hardware/libhardware_legacy",
56 "hardware/ril",
57 }
58)
59
60// Determine if a dir under source tree is an SoC-owned proprietary directory, such as
61// device/, vendor/, etc.
62func isVendorProprietaryPath(dir string) bool {
63 for _, p := range vendorProprietaryDirs {
64 if strings.HasPrefix(dir, p) {
65 // filter out AOSP defined directories, e.g. hardware/interfaces/
66 aosp := false
67 for _, p := range aospDirsUnderProprietary {
68 if strings.HasPrefix(dir, p) {
69 aosp = true
70 break
71 }
72 }
73 if !aosp {
74 return true
75 }
76 }
77 }
78 return false
79}
80
81// Determine if a module is going to be included in vendor snapshot or not.
82//
83// Targets of vendor snapshot are "vendor: true" or "vendor_available: true" modules in
84// AOSP. They are not guaranteed to be compatible with older vendor images. (e.g. might
85// depend on newer VNDK) So they are captured as vendor snapshot To build older vendor
86// image and newer system image altogether.
87func isVendorSnapshotModule(ctx android.SingletonContext, m *Module) bool {
88 if !m.Enabled() {
89 return false
90 }
91 // skip proprietary modules, but include all VNDK (static)
92 if isVendorProprietaryPath(ctx.ModuleDir(m)) && !m.IsVndk() {
93 return false
94 }
95 if m.Target().Os.Class != android.Device {
96 return false
97 }
98 if m.Target().NativeBridge == android.NativeBridgeEnabled {
99 return false
100 }
101 // the module must be installed in /vendor
102 if !m.installable() || m.isSnapshotPrebuilt() || !m.inVendor() {
103 return false
104 }
105 // exclude test modules
106 if _, ok := m.linker.(interface{ gtest() bool }); ok {
107 return false
108 }
109 // TODO(b/65377115): add full support for sanitizer
110 if m.sanitize != nil && !m.sanitize.isUnsanitizedVariant() {
111 return false
112 }
113
114 // Libraries
115 if l, ok := m.linker.(snapshotLibraryInterface); ok {
116 if l.static() {
117 return proptools.BoolDefault(m.VendorProperties.Vendor_available, true)
118 }
119 if l.shared() {
120 return !m.IsVndk()
121 }
122 return true
123 }
124
125 // Binaries
126 _, ok := m.linker.(*binaryDecorator)
127 if !ok {
128 if _, ok := m.linker.(*prebuiltBinaryLinker); !ok {
129 return false
130 }
131 }
132 return proptools.BoolDefault(m.VendorProperties.Vendor_available, true)
133}
134
135func (c *vendorSnapshotSingleton) GenerateBuildActions(ctx android.SingletonContext) {
136 // BOARD_VNDK_VERSION must be set to 'current' in order to generate a vendor snapshot.
137 if ctx.DeviceConfig().VndkVersion() != "current" {
138 return
139 }
140
141 var snapshotOutputs android.Paths
142
143 /*
144 Vendor snapshot zipped artifacts directory structure:
145 {SNAPSHOT_ARCH}/
146 arch-{TARGET_ARCH}-{TARGET_ARCH_VARIANT}/
147 shared/
148 (.so shared libraries)
149 static/
150 (.a static libraries)
151 header/
152 (header only libraries)
153 binary/
154 (executable binaries)
155 arch-{TARGET_2ND_ARCH}-{TARGET_2ND_ARCH_VARIANT}/
156 shared/
157 (.so shared libraries)
158 static/
159 (.a static libraries)
160 header/
161 (header only libraries)
162 binary/
163 (executable binaries)
164 NOTICE_FILES/
165 (notice files, e.g. libbase.txt)
166 configs/
167 (config files, e.g. init.rc files, vintf_fragments.xml files, etc.)
168 include/
169 (header files of same directory structure with source tree)
170 */
171
172 snapshotDir := "vendor-snapshot"
173 snapshotArchDir := filepath.Join(snapshotDir, ctx.DeviceConfig().DeviceArch())
174
175 includeDir := filepath.Join(snapshotArchDir, "include")
176 configsDir := filepath.Join(snapshotArchDir, "configs")
177 noticeDir := filepath.Join(snapshotArchDir, "NOTICE_FILES")
178
179 installedNotices := make(map[string]bool)
180 installedConfigs := make(map[string]bool)
181
182 var headers android.Paths
183
184 type vendorSnapshotLibraryInterface interface {
185 exportedFlagsProducer
186 libraryInterface
187 }
188
189 var _ vendorSnapshotLibraryInterface = (*prebuiltLibraryLinker)(nil)
190 var _ vendorSnapshotLibraryInterface = (*libraryDecorator)(nil)
191
192 installSnapshot := func(m *Module) android.Paths {
193 targetArch := "arch-" + m.Target().Arch.ArchType.String()
194 if m.Target().Arch.ArchVariant != "" {
195 targetArch += "-" + m.Target().Arch.ArchVariant
196 }
197
198 var ret android.Paths
199
200 prop := struct {
201 ModuleName string `json:",omitempty"`
202 RelativeInstallPath string `json:",omitempty"`
203
204 // library flags
205 ExportedDirs []string `json:",omitempty"`
206 ExportedSystemDirs []string `json:",omitempty"`
207 ExportedFlags []string `json:",omitempty"`
208 SanitizeMinimalDep bool `json:",omitempty"`
209 SanitizeUbsanDep bool `json:",omitempty"`
210
211 // binary flags
212 Symlinks []string `json:",omitempty"`
213
214 // dependencies
215 SharedLibs []string `json:",omitempty"`
216 RuntimeLibs []string `json:",omitempty"`
217 Required []string `json:",omitempty"`
218
219 // extra config files
220 InitRc []string `json:",omitempty"`
221 VintfFragments []string `json:",omitempty"`
222 }{}
223
224 // Common properties among snapshots.
225 prop.ModuleName = ctx.ModuleName(m)
226 prop.RelativeInstallPath = m.RelativeInstallPath()
227 prop.RuntimeLibs = m.Properties.SnapshotRuntimeLibs
228 prop.Required = m.RequiredModuleNames()
229 for _, path := range m.InitRc() {
230 prop.InitRc = append(prop.InitRc, filepath.Join("configs", path.Base()))
231 }
232 for _, path := range m.VintfFragments() {
233 prop.VintfFragments = append(prop.VintfFragments, filepath.Join("configs", path.Base()))
234 }
235
236 // install config files. ignores any duplicates.
237 for _, path := range append(m.InitRc(), m.VintfFragments()...) {
238 out := filepath.Join(configsDir, path.Base())
239 if !installedConfigs[out] {
240 installedConfigs[out] = true
241 ret = append(ret, copyFile(ctx, path, out))
242 }
243 }
244
245 var propOut string
246
247 if l, ok := m.linker.(vendorSnapshotLibraryInterface); ok {
248 // library flags
249 prop.ExportedFlags = l.exportedFlags()
250 for _, dir := range l.exportedDirs() {
251 prop.ExportedDirs = append(prop.ExportedDirs, filepath.Join("include", dir.String()))
252 }
253 for _, dir := range l.exportedSystemDirs() {
254 prop.ExportedSystemDirs = append(prop.ExportedSystemDirs, filepath.Join("include", dir.String()))
255 }
256 // shared libs dependencies aren't meaningful on static or header libs
257 if l.shared() {
258 prop.SharedLibs = m.Properties.SnapshotSharedLibs
259 }
260 if l.static() && m.sanitize != nil {
261 prop.SanitizeMinimalDep = m.sanitize.Properties.MinimalRuntimeDep || enableMinimalRuntime(m.sanitize)
262 prop.SanitizeUbsanDep = m.sanitize.Properties.UbsanRuntimeDep || enableUbsanRuntime(m.sanitize)
263 }
264
265 var libType string
266 if l.static() {
267 libType = "static"
268 } else if l.shared() {
269 libType = "shared"
270 } else {
271 libType = "header"
272 }
273
274 var stem string
275
276 // install .a or .so
277 if libType != "header" {
278 libPath := m.outputFile.Path()
279 stem = libPath.Base()
280 snapshotLibOut := filepath.Join(snapshotArchDir, targetArch, libType, stem)
281 ret = append(ret, copyFile(ctx, libPath, snapshotLibOut))
282 } else {
283 stem = ctx.ModuleName(m)
284 }
285
286 propOut = filepath.Join(snapshotArchDir, targetArch, libType, stem+".json")
287 } else {
288 // binary flags
289 prop.Symlinks = m.Symlinks()
290 prop.SharedLibs = m.Properties.SnapshotSharedLibs
291
292 // install bin
293 binPath := m.outputFile.Path()
294 snapshotBinOut := filepath.Join(snapshotArchDir, targetArch, "binary", binPath.Base())
295 ret = append(ret, copyFile(ctx, binPath, snapshotBinOut))
296 propOut = snapshotBinOut + ".json"
297 }
298
299 j, err := json.Marshal(prop)
300 if err != nil {
301 ctx.Errorf("json marshal to %q failed: %#v", propOut, err)
302 return nil
303 }
304 ret = append(ret, writeStringToFile(ctx, string(j), propOut))
305
306 return ret
307 }
308
309 ctx.VisitAllModules(func(module android.Module) {
310 m, ok := module.(*Module)
311 if !ok || !isVendorSnapshotModule(ctx, m) {
312 return
313 }
314
315 snapshotOutputs = append(snapshotOutputs, installSnapshot(m)...)
316 if l, ok := m.linker.(vendorSnapshotLibraryInterface); ok {
317 headers = append(headers, exportedHeaders(ctx, l)...)
318 }
319
320 if m.NoticeFile().Valid() {
321 noticeName := ctx.ModuleName(m) + ".txt"
322 noticeOut := filepath.Join(noticeDir, noticeName)
323 // skip already copied notice file
324 if !installedNotices[noticeOut] {
325 installedNotices[noticeOut] = true
326 snapshotOutputs = append(snapshotOutputs, copyFile(
327 ctx, m.NoticeFile().Path(), noticeOut))
328 }
329 }
330 })
331
332 // install all headers after removing duplicates
333 for _, header := range android.FirstUniquePaths(headers) {
334 snapshotOutputs = append(snapshotOutputs, copyFile(
335 ctx, header, filepath.Join(includeDir, header.String())))
336 }
337
338 // All artifacts are ready. Sort them to normalize ninja and then zip.
339 sort.Slice(snapshotOutputs, func(i, j int) bool {
340 return snapshotOutputs[i].String() < snapshotOutputs[j].String()
341 })
342
343 zipPath := android.PathForOutput(ctx, snapshotDir, "vendor-"+ctx.Config().DeviceName()+".zip")
344 zipRule := android.NewRuleBuilder()
345
346 // filenames in rspfile from FlagWithRspFileInputList might be single-quoted. Remove it with tr
347 snapshotOutputList := android.PathForOutput(ctx, snapshotDir, "vendor-"+ctx.Config().DeviceName()+"_list")
348 zipRule.Command().
349 Text("tr").
350 FlagWithArg("-d ", "\\'").
351 FlagWithRspFileInputList("< ", snapshotOutputs).
352 FlagWithOutput("> ", snapshotOutputList)
353
354 zipRule.Temporary(snapshotOutputList)
355
356 zipRule.Command().
357 BuiltTool(ctx, "soong_zip").
358 FlagWithOutput("-o ", zipPath).
359 FlagWithArg("-C ", android.PathForOutput(ctx, snapshotDir).String()).
360 FlagWithInput("-l ", snapshotOutputList)
361
362 zipRule.Build(pctx, ctx, zipPath.String(), "vendor snapshot "+zipPath.String())
363 zipRule.DeleteTemporaryFiles()
364 c.vendorSnapshotZipFile = android.OptionalPathForPath(zipPath)
365}
366
367func (c *vendorSnapshotSingleton) MakeVars(ctx android.MakeVarsContext) {
368 ctx.Strict("SOONG_VENDOR_SNAPSHOT_ZIP", c.vendorSnapshotZipFile.String())
369}