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 16import ( 17 "encoding/json" 18 "path/filepath" 19 "strings" 20 21 "android/soong/android" 22 "android/soong/snapshot" 23) 24 25// This file defines how to capture cc modules into snapshot package. 26 27// Checks if the target image would contain VNDK 28func includeVndk(image snapshot.SnapshotImage) bool { 29 if image.ImageName() == snapshot.VendorSnapshotImageName { 30 return true 31 } 32 33 return false 34} 35 36// Check if the module is VNDK private 37func isPrivate(image snapshot.SnapshotImage, m LinkableInterface) bool { 38 if image.ImageName() == snapshot.VendorSnapshotImageName && m.IsVndkPrivate() { 39 return true 40 } 41 42 return false 43} 44 45// Checks if target image supports VNDK Ext 46func supportsVndkExt(image snapshot.SnapshotImage) bool { 47 if image.ImageName() == snapshot.VendorSnapshotImageName { 48 return true 49 } 50 51 return false 52} 53 54// Determines if the module is a candidate for snapshot. 55func isSnapshotAware(cfg android.DeviceConfig, m LinkableInterface, inProprietaryPath bool, apexInfo android.ApexInfo, image snapshot.SnapshotImage) bool { 56 if !m.Enabled() || m.HiddenFromMake() { 57 return false 58 } 59 // When android/prebuilt.go selects between source and prebuilt, it sets 60 // HideFromMake on the other one to avoid duplicate install rules in make. 61 if m.IsHideFromMake() { 62 return false 63 } 64 // skip proprietary modules, but (for the vendor snapshot only) 65 // include all VNDK (static) 66 if inProprietaryPath && (!includeVndk(image) || !m.IsVndk()) { 67 return false 68 } 69 // If the module would be included based on its path, check to see if 70 // the module is marked to be excluded. If so, skip it. 71 if image.ExcludeFromSnapshot(m) { 72 return false 73 } 74 if m.Target().Os.Class != android.Device { 75 return false 76 } 77 if m.Target().NativeBridge == android.NativeBridgeEnabled { 78 return false 79 } 80 // the module must be installed in target image 81 if !apexInfo.IsForPlatform() || m.IsSnapshotPrebuilt() || !image.InImage(m)() { 82 return false 83 } 84 // skip kernel_headers which always depend on vendor 85 if m.KernelHeadersDecorator() { 86 return false 87 } 88 89 if m.IsLlndk() { 90 return false 91 } 92 93 // Libraries 94 if sanitizable, ok := m.(PlatformSanitizeable); ok && sanitizable.IsSnapshotLibrary() { 95 if sanitizable.SanitizePropDefined() { 96 // scs exports both sanitized and unsanitized variants for static and header 97 // Always use unsanitized variant of it. 98 if !sanitizable.Shared() && sanitizable.IsSanitizerEnabled(scs) { 99 return false 100 } 101 // cfi and hwasan also export both variants. But for static, we capture both. 102 // This is because cfi static libraries can't be linked from non-cfi modules, 103 // and vice versa. 104 // hwasan is captured as well to support hwasan build. 105 if !sanitizable.Static() && 106 !sanitizable.Shared() && 107 (sanitizable.IsSanitizerEnabled(cfi) || sanitizable.IsSanitizerEnabled(Hwasan)) { 108 return false 109 } 110 } 111 if sanitizable.Static() { 112 return sanitizable.OutputFile().Valid() && !isPrivate(image, m) 113 } 114 if sanitizable.Shared() || sanitizable.Rlib() { 115 if !sanitizable.OutputFile().Valid() { 116 return false 117 } 118 if includeVndk(image) { 119 if !sanitizable.IsVndk() { 120 return true 121 } 122 return sanitizable.IsVndkExt() 123 } 124 } 125 return true 126 } 127 128 // Binaries and Objects 129 if m.Binary() || m.Object() { 130 return m.OutputFile().Valid() 131 } 132 133 return false 134} 135 136// Extend the snapshot.SnapshotJsonFlags to include cc specific fields. 137type snapshotJsonFlags struct { 138 snapshot.SnapshotJsonFlags 139 // library flags 140 ExportedDirs []string `json:",omitempty"` 141 ExportedSystemDirs []string `json:",omitempty"` 142 ExportedFlags []string `json:",omitempty"` 143 Sanitize string `json:",omitempty"` 144 SanitizeMinimalDep bool `json:",omitempty"` 145 SanitizeUbsanDep bool `json:",omitempty"` 146 147 // binary flags 148 Symlinks []string `json:",omitempty"` 149 StaticExecutable bool `json:",omitempty"` 150 InstallInRoot bool `json:",omitempty"` 151 152 // dependencies 153 SharedLibs []string `json:",omitempty"` 154 StaticLibs []string `json:",omitempty"` 155 RuntimeLibs []string `json:",omitempty"` 156 157 // extra config files 158 InitRc []string `json:",omitempty"` 159 VintfFragments []string `json:",omitempty"` 160 MinSdkVersion string `json:",omitempty"` 161} 162 163var ccSnapshotAction snapshot.GenerateSnapshotAction = func(s snapshot.SnapshotSingleton, ctx android.SingletonContext, snapshotArchDir string) snapshot.SnapshotPaths { 164 /* 165 Vendor snapshot zipped artifacts directory structure for cc modules: 166 {SNAPSHOT_ARCH}/ 167 arch-{TARGET_ARCH}-{TARGET_ARCH_VARIANT}/ 168 shared/ 169 (.so shared libraries) 170 static/ 171 (.a static libraries) 172 header/ 173 (header only libraries) 174 binary/ 175 (executable binaries) 176 object/ 177 (.o object files) 178 arch-{TARGET_2ND_ARCH}-{TARGET_2ND_ARCH_VARIANT}/ 179 shared/ 180 (.so shared libraries) 181 static/ 182 (.a static libraries) 183 header/ 184 (header only libraries) 185 binary/ 186 (executable binaries) 187 object/ 188 (.o object files) 189 NOTICE_FILES/ 190 (notice files, e.g. libbase.txt) 191 configs/ 192 (config files, e.g. init.rc files, vintf_fragments.xml files, etc.) 193 include/ 194 (header files of same directory structure with source tree) 195 */ 196 197 var snapshotOutputs android.Paths 198 var snapshotNotices android.Paths 199 200 includeDir := filepath.Join(snapshotArchDir, "include") 201 configsDir := filepath.Join(snapshotArchDir, "configs") 202 203 installedNotices := make(map[string]bool) 204 installedConfigs := make(map[string]bool) 205 206 var headers android.Paths 207 208 copyFile := func(ctx android.SingletonContext, path android.Path, out string, fake bool) android.OutputPath { 209 if fake { 210 // All prebuilt binaries and headers are installed by copyFile function. This makes a fake 211 // snapshot just touch prebuilts and headers, rather than installing real files. 212 return snapshot.WriteStringToFileRule(ctx, "", out) 213 } else { 214 return snapshot.CopyFileRule(pctx, ctx, path, out) 215 } 216 } 217 218 // installSnapshot function copies prebuilt file (.so, .a, or executable) and json flag file. 219 // For executables, init_rc and vintf_fragments files are also copied. 220 installSnapshot := func(m LinkableInterface, fake bool) android.Paths { 221 targetArch := "arch-" + m.Target().Arch.ArchType.String() 222 if m.Target().Arch.ArchVariant != "" { 223 targetArch += "-" + m.Target().Arch.ArchVariant 224 } 225 226 var ret android.Paths 227 228 prop := snapshotJsonFlags{} 229 230 // Common properties among snapshots. 231 prop.InitBaseSnapshotPropsWithName(m, ctx.ModuleName(m)) 232 if supportsVndkExt(s.Image) && m.IsVndkExt() { 233 // vndk exts are installed to /vendor/lib(64)?/vndk(-sp)? 234 if m.IsVndkSp() { 235 prop.RelativeInstallPath = "vndk-sp" 236 } else { 237 prop.RelativeInstallPath = "vndk" 238 } 239 } else { 240 prop.RelativeInstallPath = m.RelativeInstallPath() 241 } 242 prop.RuntimeLibs = m.SnapshotRuntimeLibs() 243 prop.Required = m.RequiredModuleNames() 244 if o, ok := m.(overridable); ok { 245 prop.Overrides = o.overriddenModules() 246 } 247 for _, path := range m.InitRc() { 248 prop.InitRc = append(prop.InitRc, filepath.Join("configs", path.Base())) 249 } 250 for _, path := range m.VintfFragments() { 251 prop.VintfFragments = append(prop.VintfFragments, filepath.Join("configs", path.Base())) 252 } 253 if m.IsPrebuilt() { 254 prop.MinSdkVersion = "apex_inherit" 255 } else { 256 prop.MinSdkVersion = m.MinSdkVersion() 257 } 258 259 // install config files. ignores any duplicates. 260 for _, path := range append(m.InitRc(), m.VintfFragments()...) { 261 out := filepath.Join(configsDir, path.Base()) 262 if !installedConfigs[out] { 263 installedConfigs[out] = true 264 ret = append(ret, copyFile(ctx, path, out, fake)) 265 } 266 } 267 268 var propOut string 269 270 if m.IsSnapshotLibrary() { 271 exporterInfo := ctx.ModuleProvider(m.Module(), FlagExporterInfoProvider).(FlagExporterInfo) 272 273 // library flags 274 prop.ExportedFlags = exporterInfo.Flags 275 for _, dir := range exporterInfo.IncludeDirs { 276 prop.ExportedDirs = append(prop.ExportedDirs, filepath.Join("include", dir.String())) 277 } 278 for _, dir := range exporterInfo.SystemIncludeDirs { 279 prop.ExportedSystemDirs = append(prop.ExportedSystemDirs, filepath.Join("include", dir.String())) 280 } 281 282 // shared libs dependencies aren't meaningful on static or header libs 283 if m.Shared() { 284 prop.SharedLibs = m.SnapshotSharedLibs() 285 } 286 // static libs dependencies are required to collect the NOTICE files. 287 prop.StaticLibs = m.SnapshotStaticLibs() 288 if sanitizable, ok := m.(PlatformSanitizeable); ok { 289 if sanitizable.Static() && sanitizable.SanitizePropDefined() { 290 prop.SanitizeMinimalDep = sanitizable.MinimalRuntimeDep() || sanitizable.MinimalRuntimeNeeded() 291 prop.SanitizeUbsanDep = sanitizable.UbsanRuntimeDep() || sanitizable.UbsanRuntimeNeeded() 292 } 293 } 294 295 var libType string 296 if m.Static() { 297 libType = "static" 298 } else if m.Shared() { 299 libType = "shared" 300 } else if m.Rlib() { 301 libType = "rlib" 302 } else { 303 libType = "header" 304 } 305 306 var stem string 307 308 // install .a or .so 309 if libType != "header" { 310 libPath := m.OutputFile().Path() 311 stem = libPath.Base() 312 if sanitizable, ok := m.(PlatformSanitizeable); ok { 313 if (sanitizable.Static() || sanitizable.Rlib()) && sanitizable.SanitizePropDefined() { 314 if sanitizable.IsSanitizerEnabled(cfi) { 315 // both cfi and non-cfi variant for static libraries can exist. 316 // attach .cfi to distinguish between cfi and non-cfi. 317 // e.g. libbase.a -> libbase.cfi.a 318 ext := filepath.Ext(stem) 319 stem = strings.TrimSuffix(stem, ext) + ".cfi" + ext 320 prop.Sanitize = "cfi" 321 prop.ModuleName += ".cfi" 322 } else if sanitizable.IsSanitizerEnabled(Hwasan) { 323 // Same for the hwasan 324 ext := filepath.Ext(stem) 325 stem = strings.TrimSuffix(stem, ext) + ".hwasan" + ext 326 prop.Sanitize = "hwasan" 327 prop.ModuleName += ".hwasan" 328 } 329 } 330 } 331 snapshotLibOut := filepath.Join(snapshotArchDir, targetArch, libType, m.RelativeInstallPath(), stem) 332 ret = append(ret, copyFile(ctx, libPath, snapshotLibOut, fake)) 333 } else { 334 stem = ctx.ModuleName(m) 335 } 336 337 propOut = filepath.Join(snapshotArchDir, targetArch, libType, m.RelativeInstallPath(), stem+".json") 338 } else if m.Binary() { 339 // binary flags 340 prop.Symlinks = m.Symlinks() 341 prop.StaticExecutable = m.StaticExecutable() 342 prop.InstallInRoot = m.InstallInRoot() 343 prop.SharedLibs = m.SnapshotSharedLibs() 344 // static libs dependencies are required to collect the NOTICE files. 345 prop.StaticLibs = m.SnapshotStaticLibs() 346 // install bin 347 binPath := m.OutputFile().Path() 348 snapshotBinOut := filepath.Join(snapshotArchDir, targetArch, "binary", binPath.Base()) 349 ret = append(ret, copyFile(ctx, binPath, snapshotBinOut, fake)) 350 propOut = snapshotBinOut + ".json" 351 } else if m.Object() { 352 // object files aren't installed to the device, so their names can conflict. 353 // Use module name as stem. 354 objPath := m.OutputFile().Path() 355 snapshotObjOut := filepath.Join(snapshotArchDir, targetArch, "object", 356 ctx.ModuleName(m)+filepath.Ext(objPath.Base())) 357 ret = append(ret, copyFile(ctx, objPath, snapshotObjOut, fake)) 358 propOut = snapshotObjOut + ".json" 359 } else { 360 ctx.Errorf("unknown module %q in vendor snapshot", m.String()) 361 return nil 362 } 363 364 j, err := json.Marshal(prop) 365 if err != nil { 366 ctx.Errorf("json marshal to %q failed: %#v", propOut, err) 367 return nil 368 } 369 ret = append(ret, snapshot.WriteStringToFileRule(ctx, string(j), propOut)) 370 371 return ret 372 } 373 374 ctx.VisitAllModules(func(module android.Module) { 375 m, ok := module.(LinkableInterface) 376 if !ok { 377 return 378 } 379 380 moduleDir := ctx.ModuleDir(module) 381 inProprietaryPath := s.Image.IsProprietaryPath(moduleDir, ctx.DeviceConfig()) 382 apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo) 383 384 if s.Image.ExcludeFromSnapshot(m) { 385 if inProprietaryPath { 386 // Error: exclude_from_vendor_snapshot applies 387 // to framework-path modules only. 388 ctx.Errorf("module %q in vendor proprietary path %q may not use \"exclude_from_vendor_snapshot: true\"", m.String(), moduleDir) 389 return 390 } 391 } 392 393 if !isSnapshotAware(ctx.DeviceConfig(), m, inProprietaryPath, apexInfo, s.Image) { 394 return 395 } 396 397 // If we are using directed snapshot and a module is not included in the 398 // list, we will still include the module as if it was a fake module. 399 // The reason is that soong needs all the dependencies to be present, even 400 // if they are not using during the build. 401 installAsFake := s.Fake 402 if s.Image.ExcludeFromDirectedSnapshot(ctx.DeviceConfig(), m.BaseModuleName()) { 403 installAsFake = true 404 } 405 406 // installSnapshot installs prebuilts and json flag files 407 snapshotOutputs = append(snapshotOutputs, installSnapshot(m, installAsFake)...) 408 // just gather headers and notice files here, because they are to be deduplicated 409 if m.IsSnapshotLibrary() { 410 headers = append(headers, m.SnapshotHeaders()...) 411 } 412 413 for _, notice := range m.EffectiveLicenseFiles() { 414 if _, ok := installedNotices[notice.String()]; !ok { 415 installedNotices[notice.String()] = true 416 snapshotNotices = append(snapshotNotices, notice) 417 } 418 } 419 }) 420 421 // install all headers after removing duplicates 422 for _, header := range android.FirstUniquePaths(headers) { 423 snapshotOutputs = append(snapshotOutputs, copyFile(ctx, header, filepath.Join(includeDir, header.String()), s.Fake)) 424 } 425 426 return snapshot.SnapshotPaths{OutputFiles: snapshotOutputs, NoticeFiles: snapshotNotices} 427} 428 429func init() { 430 snapshot.RegisterSnapshotAction(ccSnapshotAction) 431} 432