1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
|
// Copyright (C) 2024 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package fsgen
import (
"android/soong/android"
"android/soong/etc"
"fmt"
"path/filepath"
"strings"
"github.com/google/blueprint/proptools"
)
type srcBaseFileInstallBaseFileTuple struct {
srcBaseFile string
installBaseFile string
}
// prebuilt src files grouped by the install partitions.
// Each groups are a mapping of the relative install path to the name of the files
type prebuiltSrcGroupByInstallPartition struct {
system map[string][]srcBaseFileInstallBaseFileTuple
system_ext map[string][]srcBaseFileInstallBaseFileTuple
product map[string][]srcBaseFileInstallBaseFileTuple
vendor map[string][]srcBaseFileInstallBaseFileTuple
recovery map[string][]srcBaseFileInstallBaseFileTuple
}
func newPrebuiltSrcGroupByInstallPartition() *prebuiltSrcGroupByInstallPartition {
return &prebuiltSrcGroupByInstallPartition{
system: map[string][]srcBaseFileInstallBaseFileTuple{},
system_ext: map[string][]srcBaseFileInstallBaseFileTuple{},
product: map[string][]srcBaseFileInstallBaseFileTuple{},
vendor: map[string][]srcBaseFileInstallBaseFileTuple{},
recovery: map[string][]srcBaseFileInstallBaseFileTuple{},
}
}
func isSubdirectory(parent, child string) bool {
rel, err := filepath.Rel(parent, child)
if err != nil {
return false
}
return !strings.HasPrefix(rel, "..")
}
func appendIfCorrectInstallPartition(partitionToInstallPathList []partitionToInstallPath, destPath, srcPath string, srcGroup *prebuiltSrcGroupByInstallPartition) {
for _, part := range partitionToInstallPathList {
partition := part.name
installPath := part.installPath
if isSubdirectory(installPath, destPath) {
relativeInstallPath, _ := filepath.Rel(installPath, destPath)
relativeInstallDir := filepath.Dir(relativeInstallPath)
var srcMap map[string][]srcBaseFileInstallBaseFileTuple
switch partition {
case "system":
srcMap = srcGroup.system
case "system_ext":
srcMap = srcGroup.system_ext
case "product":
srcMap = srcGroup.product
case "vendor":
srcMap = srcGroup.vendor
case "recovery":
srcMap = srcGroup.recovery
}
if srcMap != nil {
srcMap[relativeInstallDir] = append(srcMap[relativeInstallDir], srcBaseFileInstallBaseFileTuple{
srcBaseFile: filepath.Base(srcPath),
installBaseFile: filepath.Base(destPath),
})
}
return
}
}
}
// Create a map of source files to the list of destination files from PRODUCT_COPY_FILES entries.
// Note that the value of the map is a list of string, given that a single source file can be
// copied to multiple files.
// This function also checks the existence of the source files, and validates that there is no
// multiple source files copying to the same dest file.
func uniqueExistingProductCopyFileMap(ctx android.LoadHookContext) map[string][]string {
seen := make(map[string]bool)
filtered := make(map[string][]string)
for _, copyFilePair := range ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.ProductCopyFiles {
srcDestList := strings.Split(copyFilePair, ":")
if len(srcDestList) < 2 {
ctx.ModuleErrorf("PRODUCT_COPY_FILES must follow the format \"src:dest\", got: %s", copyFilePair)
}
src, dest := srcDestList[0], srcDestList[1]
// Some downstream branches use absolute path as entries in PRODUCT_COPY_FILES.
// Convert them to relative path from top and check if they do not escape the tree root.
relSrc := android.ToRelativeSourcePath(ctx, src)
if _, ok := seen[dest]; !ok {
if optionalPath := android.ExistentPathForSource(ctx, relSrc); optionalPath.Valid() {
seen[dest] = true
filtered[relSrc] = append(filtered[relSrc], dest)
}
}
}
return filtered
}
type partitionToInstallPath struct {
name string
installPath string
}
func processProductCopyFiles(ctx android.LoadHookContext) map[string]*prebuiltSrcGroupByInstallPartition {
// Filter out duplicate dest entries and non existing src entries
productCopyFileMap := uniqueExistingProductCopyFileMap(ctx)
// System is intentionally added at the last to consider the scenarios where
// non-system partitions are installed as part of the system partition
partitionToInstallPathList := []partitionToInstallPath{
{name: "recovery", installPath: "recovery/root"},
{name: "vendor", installPath: ctx.DeviceConfig().VendorPath()},
{name: "product", installPath: ctx.DeviceConfig().ProductPath()},
{name: "system_ext", installPath: ctx.DeviceConfig().SystemExtPath()},
{name: "system", installPath: "system"},
}
groupedSources := map[string]*prebuiltSrcGroupByInstallPartition{}
for _, src := range android.SortedKeys(productCopyFileMap) {
destFiles := productCopyFileMap[src]
srcFileDir := filepath.Dir(src)
if _, ok := groupedSources[srcFileDir]; !ok {
groupedSources[srcFileDir] = newPrebuiltSrcGroupByInstallPartition()
}
for _, dest := range destFiles {
appendIfCorrectInstallPartition(partitionToInstallPathList, dest, filepath.Base(src), groupedSources[srcFileDir])
}
}
return groupedSources
}
type prebuiltModuleProperties struct {
Name *string
Soc_specific *bool
Product_specific *bool
System_ext_specific *bool
Recovery *bool
Ramdisk *bool
Srcs []string
No_full_install *bool
NamespaceExportedToMake bool
Visibility []string
}
// Split relative_install_path to a separate struct, because it is not supported for every
// modules listed in [etcInstallPathToFactoryMap]
type prebuiltSubdirProperties struct {
// If the base file name of the src and dst all match, dsts property does not need to be
// set, and only relative_install_path can be set.
Relative_install_path *string
}
// Split install_in_root to a separate struct as it is part of rootProperties instead of
// properties
type prebuiltInstallInRootProperties struct {
Install_in_root *bool
}
var (
etcInstallPathToFactoryList = map[string]android.ModuleFactory{
"": etc.PrebuiltRootFactory,
"avb": etc.PrebuiltAvbFactory,
"bin": etc.PrebuiltBinaryFactory,
"bt_firmware": etc.PrebuiltBtFirmwareFactory,
"cacerts": etc.PrebuiltEtcCaCertsFactory,
"dsp": etc.PrebuiltDSPFactory,
"etc": etc.PrebuiltEtcFactory,
"etc/dsp": etc.PrebuiltDSPFactory,
"etc/firmware": etc.PrebuiltFirmwareFactory,
"firmware": etc.PrebuiltFirmwareFactory,
"gpu": etc.PrebuiltGPUFactory,
"first_stage_ramdisk": etc.PrebuiltFirstStageRamdiskFactory,
"fonts": etc.PrebuiltFontFactory,
"framework": etc.PrebuiltFrameworkFactory,
"lib": etc.PrebuiltRenderScriptBitcodeFactory,
"lib64": etc.PrebuiltRenderScriptBitcodeFactory,
"lib/rfsa": etc.PrebuiltRFSAFactory,
"media": etc.PrebuiltMediaFactory,
"odm": etc.PrebuiltOdmFactory,
"optee": etc.PrebuiltOpteeFactory,
"overlay": etc.PrebuiltOverlayFactory,
"priv-app": etc.PrebuiltPrivAppFactory,
"radio": etc.PrebuiltRadioFactory,
"sbin": etc.PrebuiltSbinFactory,
"system": etc.PrebuiltSystemFactory,
"res": etc.PrebuiltResFactory,
"rfs": etc.PrebuiltRfsFactory,
"tee": etc.PrebuiltTeeFactory,
"tts": etc.PrebuiltVoicepackFactory,
"tvconfig": etc.PrebuiltTvConfigFactory,
"tvservice": etc.PrebuiltTvServiceFactory,
"usr/share": etc.PrebuiltUserShareFactory,
"usr/hyphen-data": etc.PrebuiltUserHyphenDataFactory,
"usr/keylayout": etc.PrebuiltUserKeyLayoutFactory,
"usr/keychars": etc.PrebuiltUserKeyCharsFactory,
"usr/srec": etc.PrebuiltUserSrecFactory,
"usr/idc": etc.PrebuiltUserIdcFactory,
"vendor": etc.PrebuiltVendorFactory,
"vendor_dlkm": etc.PrebuiltVendorDlkmFactory,
"vendor_overlay": etc.PrebuiltVendorOverlayFactory,
"wallpaper": etc.PrebuiltWallpaperFactory,
"wlc_upt": etc.PrebuiltWlcUptFactory,
}
)
func generatedPrebuiltEtcModuleName(partition, srcDir, destDir string, count int) string {
// generated module name follows the pattern:
// <install partition>-<src file path>-<relative install path from partition root>-<number>
// Note that all path separators are replaced with "_" in the name
moduleName := partition
if !android.InList(srcDir, []string{"", "."}) {
moduleName += fmt.Sprintf("-%s", strings.ReplaceAll(srcDir, string(filepath.Separator), "_"))
}
if !android.InList(destDir, []string{"", "."}) {
moduleName += fmt.Sprintf("-%s", strings.ReplaceAll(destDir, string(filepath.Separator), "_"))
}
moduleName += fmt.Sprintf("-%d", count)
return moduleName
}
func groupDestFilesBySrc(destFiles []srcBaseFileInstallBaseFileTuple) (ret map[string][]srcBaseFileInstallBaseFileTuple, maxLen int) {
ret = map[string][]srcBaseFileInstallBaseFileTuple{}
maxLen = 0
for _, tuple := range destFiles {
if _, ok := ret[tuple.srcBaseFile]; !ok {
ret[tuple.srcBaseFile] = []srcBaseFileInstallBaseFileTuple{}
}
ret[tuple.srcBaseFile] = append(ret[tuple.srcBaseFile], tuple)
maxLen = max(maxLen, len(ret[tuple.srcBaseFile]))
}
return ret, maxLen
}
func prebuiltEtcModuleProps(ctx android.LoadHookContext, moduleName, partition, destDir string) prebuiltModuleProperties {
moduleProps := prebuiltModuleProperties{}
moduleProps.Name = proptools.StringPtr(moduleName)
// Set partition specific properties
switch partition {
case "system_ext":
moduleProps.System_ext_specific = proptools.BoolPtr(true)
case "product":
moduleProps.Product_specific = proptools.BoolPtr(true)
case "vendor":
moduleProps.Soc_specific = proptools.BoolPtr(true)
case "recovery":
// To match the logic in modulePartition() in android/paths.go
if ctx.DeviceConfig().BoardUsesRecoveryAsBoot() && strings.HasPrefix(destDir, "first_stage_ramdisk") {
moduleProps.Ramdisk = proptools.BoolPtr(true)
} else {
moduleProps.Recovery = proptools.BoolPtr(true)
}
}
moduleProps.No_full_install = proptools.BoolPtr(true)
moduleProps.NamespaceExportedToMake = true
moduleProps.Visibility = []string{"//visibility:public"}
return moduleProps
}
func createPrebuiltEtcModulesInDirectory(ctx android.LoadHookContext, partition, srcDir, destDir string, destFiles []srcBaseFileInstallBaseFileTuple) (moduleNames []string) {
groupedDestFiles, maxLen := groupDestFilesBySrc(destFiles)
// Find out the most appropriate module type to generate
var etcInstallPathKey string
for _, etcInstallPath := range android.SortedKeys(etcInstallPathToFactoryList) {
// Do not break when found but iterate until the end to find a module with more
// specific install path
if strings.HasPrefix(destDir, etcInstallPath) {
etcInstallPathKey = etcInstallPath
}
}
moduleFactory := etcInstallPathToFactoryList[etcInstallPathKey]
relDestDirFromInstallDirBase, _ := filepath.Rel(etcInstallPathKey, destDir)
for fileIndex := range maxLen {
srcTuple := []srcBaseFileInstallBaseFileTuple{}
for _, srcFile := range android.SortedKeys(groupedDestFiles) {
groupedDestFile := groupedDestFiles[srcFile]
if len(groupedDestFile) > fileIndex {
srcTuple = append(srcTuple, groupedDestFile[fileIndex])
}
}
moduleName := generatedPrebuiltEtcModuleName(partition, srcDir, destDir, fileIndex)
moduleProps := prebuiltEtcModuleProps(ctx, moduleName, partition, destDir)
modulePropsPtr := &moduleProps
propsList := []interface{}{modulePropsPtr}
allCopyFileNamesUnchanged := true
var srcBaseFiles, installBaseFiles []string
for _, tuple := range srcTuple {
if tuple.srcBaseFile != tuple.installBaseFile {
allCopyFileNamesUnchanged = false
}
srcBaseFiles = append(srcBaseFiles, tuple.srcBaseFile)
installBaseFiles = append(installBaseFiles, tuple.installBaseFile)
}
// Recovery partition-installed modules are installed to `recovery/root/system` by
// default (See modulePartition() in android/paths.go). If the destination file
// directory is not `recovery/root/system/...`, it should set install_in_root to true
// to prevent being installed in `recovery/root/system`.
if partition == "recovery" && !strings.HasPrefix(destDir, "system") {
propsList = append(propsList, &prebuiltInstallInRootProperties{
Install_in_root: proptools.BoolPtr(true),
})
// Discard any previously picked module and force it to prebuilt_{root,any} as
// they are the only modules allowed to specify the `install_in_root` property.
etcInstallPathKey = ""
relDestDirFromInstallDirBase = destDir
}
// Set appropriate srcs, dsts, and releative_install_path based on
// the source and install file names
modulePropsPtr.Srcs = srcBaseFiles
// prebuilt_root should only be used in very limited cases in prebuilt_etc moddule gen, where:
// - all source file names are identical to the installed file names, and
// - all source files are installed in root, not the subdirectories of root
// prebuilt_root currently does not have a good way to specify the names of the multiple
// installed files, and prebuilt_root does not allow installing files at a subdirectory
// of the root.
// Use prebuilt_any instead of prebuilt_root if either of the conditions are not met as
// a fallback behavior.
if etcInstallPathKey == "" {
if !(allCopyFileNamesUnchanged && android.InList(relDestDirFromInstallDirBase, []string{"", "."})) {
moduleFactory = etc.PrebuiltAnyFactory
}
}
if allCopyFileNamesUnchanged {
// Specify relative_install_path if it is not installed in the base directory of the module.
// In case of prebuilt_{root,any} this is equivalent to the root of the partition.
if !android.InList(relDestDirFromInstallDirBase, []string{"", "."}) {
propsList = append(propsList, &prebuiltSubdirProperties{
Relative_install_path: proptools.StringPtr(relDestDirFromInstallDirBase),
})
}
} else {
dsts := proptools.NewConfigurable[[]string](nil, nil)
for _, installBaseFile := range installBaseFiles {
dsts.AppendSimpleValue([]string{filepath.Join(relDestDirFromInstallDirBase, installBaseFile)})
}
propsList = append(propsList, &etc.PrebuiltDstsProperties{
Dsts: dsts,
})
}
ctx.CreateModuleInDirectory(moduleFactory, srcDir, propsList...)
moduleNames = append(moduleNames, moduleName)
}
return moduleNames
}
func createPrebuiltEtcModulesForPartition(ctx android.LoadHookContext, partition, srcDir string, destDirFilesMap map[string][]srcBaseFileInstallBaseFileTuple) (ret []string) {
for _, destDir := range android.SortedKeys(destDirFilesMap) {
ret = append(ret, createPrebuiltEtcModulesInDirectory(ctx, partition, srcDir, destDir, destDirFilesMap[destDir])...)
}
return ret
}
// Creates prebuilt_* modules based on the install paths and returns the list of generated
// module names
func createPrebuiltEtcModules(ctx android.LoadHookContext) (ret []string) {
groupedSources := processProductCopyFiles(ctx)
for _, srcDir := range android.SortedKeys(groupedSources) {
groupedSource := groupedSources[srcDir]
ret = append(ret, createPrebuiltEtcModulesForPartition(ctx, "system", srcDir, groupedSource.system)...)
ret = append(ret, createPrebuiltEtcModulesForPartition(ctx, "system_ext", srcDir, groupedSource.system_ext)...)
ret = append(ret, createPrebuiltEtcModulesForPartition(ctx, "product", srcDir, groupedSource.product)...)
ret = append(ret, createPrebuiltEtcModulesForPartition(ctx, "vendor", srcDir, groupedSource.vendor)...)
ret = append(ret, createPrebuiltEtcModulesForPartition(ctx, "recovery", srcDir, groupedSource.recovery)...)
}
return ret
}
func createAvbpubkeyModule(ctx android.LoadHookContext) bool {
avbKeyPath := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.BoardAvbKeyPath
if avbKeyPath == "" {
return false
}
ctx.CreateModuleInDirectory(
etc.AvbpubkeyModuleFactory,
".",
&struct {
Name *string
Product_specific *bool
Private_key *string
No_full_install *bool
Visibility []string
}{
Name: proptools.StringPtr("system_other_avbpubkey"),
Product_specific: proptools.BoolPtr(true),
Private_key: proptools.StringPtr(avbKeyPath),
No_full_install: proptools.BoolPtr(true),
Visibility: []string{"//visibility:public"},
},
)
return true
}
|