1// 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 16// This file contains singletons to capture vendor and recovery snapshot. They consist of prebuilt 17// modules under AOSP so older vendor and recovery can be built with a newer system in a single 18// source tree. 19 20import ( 21 "encoding/json" 22 "path/filepath" 23 "sort" 24 "strings" 25 26 "android/soong/android" 27) 28 29var vendorSnapshotSingleton = snapshotSingleton{ 30 "vendor", 31 "SOONG_VENDOR_SNAPSHOT_ZIP", 32 android.OptionalPath{}, 33 true, 34 vendorSnapshotImageSingleton, 35 false, /* fake */ 36} 37 38var vendorFakeSnapshotSingleton = snapshotSingleton{ 39 "vendor", 40 "SOONG_VENDOR_FAKE_SNAPSHOT_ZIP", 41 android.OptionalPath{}, 42 true, 43 vendorSnapshotImageSingleton, 44 true, /* fake */ 45} 46 47var recoverySnapshotSingleton = snapshotSingleton{ 48 "recovery", 49 "SOONG_RECOVERY_SNAPSHOT_ZIP", 50 android.OptionalPath{}, 51 false, 52 recoverySnapshotImageSingleton, 53 false, /* fake */ 54} 55 56func VendorSnapshotSingleton() android.Singleton { 57 return &vendorSnapshotSingleton 58} 59 60func VendorFakeSnapshotSingleton() android.Singleton { 61 return &vendorFakeSnapshotSingleton 62} 63 64func RecoverySnapshotSingleton() android.Singleton { 65 return &recoverySnapshotSingleton 66} 67 68type snapshotSingleton struct { 69 // Name, e.g., "vendor", "recovery", "ramdisk". 70 name string 71 72 // Make variable that points to the snapshot file, e.g., 73 // "SOONG_RECOVERY_SNAPSHOT_ZIP". 74 makeVar string 75 76 // Path to the snapshot zip file. 77 snapshotZipFile android.OptionalPath 78 79 // Whether the image supports VNDK extension modules. 80 supportsVndkExt bool 81 82 // Implementation of the image interface specific to the image 83 // associated with this snapshot (e.g., specific to the vendor image, 84 // recovery image, etc.). 85 image snapshotImage 86 87 // Whether this singleton is for fake snapshot or not. 88 // Fake snapshot is a snapshot whose prebuilt binaries and headers are empty. 89 // It is much faster to generate, and can be used to inspect dependencies. 90 fake bool 91} 92 93// Determine if a dir under source tree is an SoC-owned proprietary directory based 94// on vendor snapshot configuration 95// Examples: device/, vendor/ 96func isVendorProprietaryPath(dir string, deviceConfig android.DeviceConfig) bool { 97 return VendorSnapshotSingleton().(*snapshotSingleton).image.isProprietaryPath(dir, deviceConfig) 98} 99 100// Determine if a dir under source tree is an SoC-owned proprietary directory based 101// on recovery snapshot configuration 102// Examples: device/, vendor/ 103func isRecoveryProprietaryPath(dir string, deviceConfig android.DeviceConfig) bool { 104 return RecoverySnapshotSingleton().(*snapshotSingleton).image.isProprietaryPath(dir, deviceConfig) 105} 106 107func isVendorProprietaryModule(ctx android.BaseModuleContext) bool { 108 // Any module in a vendor proprietary path is a vendor proprietary 109 // module. 110 if isVendorProprietaryPath(ctx.ModuleDir(), ctx.DeviceConfig()) { 111 return true 112 } 113 114 // However if the module is not in a vendor proprietary path, it may 115 // still be a vendor proprietary module. This happens for cc modules 116 // that are excluded from the vendor snapshot, and it means that the 117 // vendor has assumed control of the framework-provided module. 118 if c, ok := ctx.Module().(LinkableInterface); ok { 119 if c.ExcludeFromVendorSnapshot() { 120 return true 121 } 122 } 123 124 return false 125} 126 127func isRecoveryProprietaryModule(ctx android.BaseModuleContext) bool { 128 129 // Any module in a recovery proprietary path is a recovery proprietary 130 // module. 131 if isRecoveryProprietaryPath(ctx.ModuleDir(), ctx.DeviceConfig()) { 132 return true 133 } 134 135 // However if the module is not in a recovery proprietary path, it may 136 // still be a recovery proprietary module. This happens for cc modules 137 // that are excluded from the recovery snapshot, and it means that the 138 // vendor has assumed control of the framework-provided module. 139 140 if c, ok := ctx.Module().(LinkableInterface); ok { 141 if c.ExcludeFromRecoverySnapshot() { 142 return true 143 } 144 } 145 146 return false 147} 148 149// Determines if the module is a candidate for snapshot. 150func isSnapshotAware(cfg android.DeviceConfig, m LinkableInterface, inProprietaryPath bool, apexInfo android.ApexInfo, image snapshotImage) bool { 151 if !m.Enabled() || m.HiddenFromMake() { 152 return false 153 } 154 // When android/prebuilt.go selects between source and prebuilt, it sets 155 // HideFromMake on the other one to avoid duplicate install rules in make. 156 if m.IsHideFromMake() { 157 return false 158 } 159 // skip proprietary modules, but (for the vendor snapshot only) 160 // include all VNDK (static) 161 if inProprietaryPath && (!image.includeVndk() || !m.IsVndk()) { 162 return false 163 } 164 // If the module would be included based on its path, check to see if 165 // the module is marked to be excluded. If so, skip it. 166 if image.excludeFromSnapshot(m) { 167 return false 168 } 169 if m.Target().Os.Class != android.Device { 170 return false 171 } 172 if m.Target().NativeBridge == android.NativeBridgeEnabled { 173 return false 174 } 175 // the module must be installed in target image 176 if !apexInfo.IsForPlatform() || m.IsSnapshotPrebuilt() || !image.inImage(m)() { 177 return false 178 } 179 // skip kernel_headers which always depend on vendor 180 if m.KernelHeadersDecorator() { 181 return false 182 } 183 184 if m.IsLlndk() { 185 return false 186 } 187 188 // Libraries 189 if sanitizable, ok := m.(PlatformSanitizeable); ok && sanitizable.IsSnapshotLibrary() { 190 if sanitizable.SanitizePropDefined() { 191 // scs and hwasan export both sanitized and unsanitized variants for static and header 192 // Always use unsanitized variants of them. 193 for _, t := range []SanitizerType{scs, Hwasan} { 194 if !sanitizable.Shared() && sanitizable.IsSanitizerEnabled(t) { 195 return false 196 } 197 } 198 // cfi also exports both variants. But for static, we capture both. 199 // This is because cfi static libraries can't be linked from non-cfi modules, 200 // and vice versa. This isn't the case for scs and hwasan sanitizers. 201 if !sanitizable.Static() && !sanitizable.Shared() && sanitizable.IsSanitizerEnabled(cfi) { 202 return false 203 } 204 } 205 if sanitizable.Static() { 206 return sanitizable.OutputFile().Valid() && !image.private(m) 207 } 208 if sanitizable.Shared() { 209 if !sanitizable.OutputFile().Valid() { 210 return false 211 } 212 if image.includeVndk() { 213 if !sanitizable.IsVndk() { 214 return true 215 } 216 return sanitizable.IsVndkExt() 217 } 218 } 219 return true 220 } 221 222 // Binaries and Objects 223 if m.Binary() || m.Object() { 224 return m.OutputFile().Valid() 225 } 226 227 return false 228} 229 230// This is to be saved as .json files, which is for development/vendor_snapshot/update.py. 231// These flags become Android.bp snapshot module properties. 232type snapshotJsonFlags struct { 233 ModuleName string `json:",omitempty"` 234 RelativeInstallPath string `json:",omitempty"` 235 236 // library flags 237 ExportedDirs []string `json:",omitempty"` 238 ExportedSystemDirs []string `json:",omitempty"` 239 ExportedFlags []string `json:",omitempty"` 240 Sanitize string `json:",omitempty"` 241 SanitizeMinimalDep bool `json:",omitempty"` 242 SanitizeUbsanDep bool `json:",omitempty"` 243 244 // binary flags 245 Symlinks []string `json:",omitempty"` 246 247 // dependencies 248 SharedLibs []string `json:",omitempty"` 249 RuntimeLibs []string `json:",omitempty"` 250 Required []string `json:",omitempty"` 251 252 // extra config files 253 InitRc []string `json:",omitempty"` 254 VintfFragments []string `json:",omitempty"` 255} 256 257func (c *snapshotSingleton) GenerateBuildActions(ctx android.SingletonContext) { 258 if !c.image.shouldGenerateSnapshot(ctx) { 259 return 260 } 261 262 var snapshotOutputs android.Paths 263 264 /* 265 Vendor snapshot zipped artifacts directory structure: 266 {SNAPSHOT_ARCH}/ 267 arch-{TARGET_ARCH}-{TARGET_ARCH_VARIANT}/ 268 shared/ 269 (.so shared libraries) 270 static/ 271 (.a static libraries) 272 header/ 273 (header only libraries) 274 binary/ 275 (executable binaries) 276 object/ 277 (.o object files) 278 arch-{TARGET_2ND_ARCH}-{TARGET_2ND_ARCH_VARIANT}/ 279 shared/ 280 (.so shared libraries) 281 static/ 282 (.a static libraries) 283 header/ 284 (header only libraries) 285 binary/ 286 (executable binaries) 287 object/ 288 (.o object files) 289 NOTICE_FILES/ 290 (notice files, e.g. libbase.txt) 291 configs/ 292 (config files, e.g. init.rc files, vintf_fragments.xml files, etc.) 293 include/ 294 (header files of same directory structure with source tree) 295 */ 296 297 snapshotDir := c.name + "-snapshot" 298 if c.fake { 299 // If this is a fake snapshot singleton, place all files under fake/ subdirectory to avoid 300 // collision with real snapshot files 301 snapshotDir = filepath.Join("fake", snapshotDir) 302 } 303 snapshotArchDir := filepath.Join(snapshotDir, ctx.DeviceConfig().DeviceArch()) 304 305 includeDir := filepath.Join(snapshotArchDir, "include") 306 configsDir := filepath.Join(snapshotArchDir, "configs") 307 noticeDir := filepath.Join(snapshotArchDir, "NOTICE_FILES") 308 309 installedNotices := make(map[string]bool) 310 installedConfigs := make(map[string]bool) 311 312 var headers android.Paths 313 314 copyFile := func(ctx android.SingletonContext, path android.Path, out string, fake bool) android.OutputPath { 315 if fake { 316 // All prebuilt binaries and headers are installed by copyFile function. This makes a fake 317 // snapshot just touch prebuilts and headers, rather than installing real files. 318 return writeStringToFileRule(ctx, "", out) 319 } else { 320 return copyFileRule(ctx, path, out) 321 } 322 } 323 324 // installSnapshot function copies prebuilt file (.so, .a, or executable) and json flag file. 325 // For executables, init_rc and vintf_fragments files are also copied. 326 installSnapshot := func(m LinkableInterface, fake bool) android.Paths { 327 targetArch := "arch-" + m.Target().Arch.ArchType.String() 328 if m.Target().Arch.ArchVariant != "" { 329 targetArch += "-" + m.Target().Arch.ArchVariant 330 } 331 332 var ret android.Paths 333 334 prop := snapshotJsonFlags{} 335 336 // Common properties among snapshots. 337 prop.ModuleName = ctx.ModuleName(m) 338 if c.supportsVndkExt && m.IsVndkExt() { 339 // vndk exts are installed to /vendor/lib(64)?/vndk(-sp)? 340 if m.IsVndkSp() { 341 prop.RelativeInstallPath = "vndk-sp" 342 } else { 343 prop.RelativeInstallPath = "vndk" 344 } 345 } else { 346 prop.RelativeInstallPath = m.RelativeInstallPath() 347 } 348 prop.RuntimeLibs = m.SnapshotRuntimeLibs() 349 prop.Required = m.RequiredModuleNames() 350 for _, path := range m.InitRc() { 351 prop.InitRc = append(prop.InitRc, filepath.Join("configs", path.Base())) 352 } 353 for _, path := range m.VintfFragments() { 354 prop.VintfFragments = append(prop.VintfFragments, filepath.Join("configs", path.Base())) 355 } 356 357 // install config files. ignores any duplicates. 358 for _, path := range append(m.InitRc(), m.VintfFragments()...) { 359 out := filepath.Join(configsDir, path.Base()) 360 if !installedConfigs[out] { 361 installedConfigs[out] = true 362 ret = append(ret, copyFile(ctx, path, out, fake)) 363 } 364 } 365 366 var propOut string 367 368 if m.IsSnapshotLibrary() { 369 exporterInfo := ctx.ModuleProvider(m.Module(), FlagExporterInfoProvider).(FlagExporterInfo) 370 371 // library flags 372 prop.ExportedFlags = exporterInfo.Flags 373 for _, dir := range exporterInfo.IncludeDirs { 374 prop.ExportedDirs = append(prop.ExportedDirs, filepath.Join("include", dir.String())) 375 } 376 for _, dir := range exporterInfo.SystemIncludeDirs { 377 prop.ExportedSystemDirs = append(prop.ExportedSystemDirs, filepath.Join("include", dir.String())) 378 } 379 380 // shared libs dependencies aren't meaningful on static or header libs 381 if m.Shared() { 382 prop.SharedLibs = m.SnapshotSharedLibs() 383 } 384 if sanitizable, ok := m.(PlatformSanitizeable); ok { 385 if sanitizable.Static() && sanitizable.SanitizePropDefined() { 386 prop.SanitizeMinimalDep = sanitizable.MinimalRuntimeDep() || sanitizable.MinimalRuntimeNeeded() 387 prop.SanitizeUbsanDep = sanitizable.UbsanRuntimeDep() || sanitizable.UbsanRuntimeNeeded() 388 } 389 } 390 391 var libType string 392 if m.Static() { 393 libType = "static" 394 } else if m.Shared() { 395 libType = "shared" 396 } else { 397 libType = "header" 398 } 399 400 var stem string 401 402 // install .a or .so 403 if libType != "header" { 404 libPath := m.OutputFile().Path() 405 stem = libPath.Base() 406 if sanitizable, ok := m.(PlatformSanitizeable); ok { 407 if sanitizable.Static() && sanitizable.SanitizePropDefined() && sanitizable.IsSanitizerEnabled(cfi) { 408 // both cfi and non-cfi variant for static libraries can exist. 409 // attach .cfi to distinguish between cfi and non-cfi. 410 // e.g. libbase.a -> libbase.cfi.a 411 ext := filepath.Ext(stem) 412 stem = strings.TrimSuffix(stem, ext) + ".cfi" + ext 413 prop.Sanitize = "cfi" 414 prop.ModuleName += ".cfi" 415 } 416 } 417 snapshotLibOut := filepath.Join(snapshotArchDir, targetArch, libType, stem) 418 ret = append(ret, copyFile(ctx, libPath, snapshotLibOut, fake)) 419 } else { 420 stem = ctx.ModuleName(m) 421 } 422 423 propOut = filepath.Join(snapshotArchDir, targetArch, libType, stem+".json") 424 } else if m.Binary() { 425 // binary flags 426 prop.Symlinks = m.Symlinks() 427 prop.SharedLibs = m.SnapshotSharedLibs() 428 429 // install bin 430 binPath := m.OutputFile().Path() 431 snapshotBinOut := filepath.Join(snapshotArchDir, targetArch, "binary", binPath.Base()) 432 ret = append(ret, copyFile(ctx, binPath, snapshotBinOut, fake)) 433 propOut = snapshotBinOut + ".json" 434 } else if m.Object() { 435 // object files aren't installed to the device, so their names can conflict. 436 // Use module name as stem. 437 objPath := m.OutputFile().Path() 438 snapshotObjOut := filepath.Join(snapshotArchDir, targetArch, "object", 439 ctx.ModuleName(m)+filepath.Ext(objPath.Base())) 440 ret = append(ret, copyFile(ctx, objPath, snapshotObjOut, fake)) 441 propOut = snapshotObjOut + ".json" 442 } else { 443 ctx.Errorf("unknown module %q in vendor snapshot", m.String()) 444 return nil 445 } 446 447 j, err := json.Marshal(prop) 448 if err != nil { 449 ctx.Errorf("json marshal to %q failed: %#v", propOut, err) 450 return nil 451 } 452 ret = append(ret, writeStringToFileRule(ctx, string(j), propOut)) 453 454 return ret 455 } 456 457 ctx.VisitAllModules(func(module android.Module) { 458 m, ok := module.(LinkableInterface) 459 if !ok { 460 return 461 } 462 463 moduleDir := ctx.ModuleDir(module) 464 inProprietaryPath := c.image.isProprietaryPath(moduleDir, ctx.DeviceConfig()) 465 apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo) 466 467 if c.image.excludeFromSnapshot(m) { 468 if inProprietaryPath { 469 // Error: exclude_from_vendor_snapshot applies 470 // to framework-path modules only. 471 ctx.Errorf("module %q in vendor proprietary path %q may not use \"exclude_from_vendor_snapshot: true\"", m.String(), moduleDir) 472 return 473 } 474 } 475 476 if !isSnapshotAware(ctx.DeviceConfig(), m, inProprietaryPath, apexInfo, c.image) { 477 return 478 } 479 480 // If we are using directed snapshot and a module is not included in the 481 // list, we will still include the module as if it was a fake module. 482 // The reason is that soong needs all the dependencies to be present, even 483 // if they are not using during the build. 484 installAsFake := c.fake 485 if c.image.excludeFromDirectedSnapshot(ctx.DeviceConfig(), m.BaseModuleName()) { 486 installAsFake = true 487 } 488 489 // installSnapshot installs prebuilts and json flag files 490 snapshotOutputs = append(snapshotOutputs, installSnapshot(m, installAsFake)...) 491 // just gather headers and notice files here, because they are to be deduplicated 492 if m.IsSnapshotLibrary() { 493 headers = append(headers, m.SnapshotHeaders()...) 494 } 495 496 if len(m.NoticeFiles()) > 0 { 497 noticeName := ctx.ModuleName(m) + ".txt" 498 noticeOut := filepath.Join(noticeDir, noticeName) 499 // skip already copied notice file 500 if !installedNotices[noticeOut] { 501 installedNotices[noticeOut] = true 502 snapshotOutputs = append(snapshotOutputs, combineNoticesRule(ctx, m.NoticeFiles(), noticeOut)) 503 } 504 } 505 }) 506 507 // install all headers after removing duplicates 508 for _, header := range android.FirstUniquePaths(headers) { 509 snapshotOutputs = append(snapshotOutputs, copyFile(ctx, header, filepath.Join(includeDir, header.String()), c.fake)) 510 } 511 512 // All artifacts are ready. Sort them to normalize ninja and then zip. 513 sort.Slice(snapshotOutputs, func(i, j int) bool { 514 return snapshotOutputs[i].String() < snapshotOutputs[j].String() 515 }) 516 517 zipPath := android.PathForOutput( 518 ctx, 519 snapshotDir, 520 c.name+"-"+ctx.Config().DeviceName()+".zip") 521 zipRule := android.NewRuleBuilder(pctx, ctx) 522 523 // filenames in rspfile from FlagWithRspFileInputList might be single-quoted. Remove it with tr 524 snapshotOutputList := android.PathForOutput( 525 ctx, 526 snapshotDir, 527 c.name+"-"+ctx.Config().DeviceName()+"_list") 528 rspFile := snapshotOutputList.ReplaceExtension(ctx, "rsp") 529 zipRule.Command(). 530 Text("tr"). 531 FlagWithArg("-d ", "\\'"). 532 FlagWithRspFileInputList("< ", rspFile, snapshotOutputs). 533 FlagWithOutput("> ", snapshotOutputList) 534 535 zipRule.Temporary(snapshotOutputList) 536 537 zipRule.Command(). 538 BuiltTool("soong_zip"). 539 FlagWithOutput("-o ", zipPath). 540 FlagWithArg("-C ", android.PathForOutput(ctx, snapshotDir).String()). 541 FlagWithInput("-l ", snapshotOutputList) 542 543 zipRule.Build(zipPath.String(), c.name+" snapshot "+zipPath.String()) 544 zipRule.DeleteTemporaryFiles() 545 c.snapshotZipFile = android.OptionalPathForPath(zipPath) 546} 547 548func (c *snapshotSingleton) MakeVars(ctx android.MakeVarsContext) { 549 ctx.Strict( 550 c.makeVar, 551 c.snapshotZipFile.String()) 552} 553