1// Copyright (C) 2025 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 filesystem 16 17import ( 18 "android/soong/android" 19 20 "github.com/google/blueprint" 21 "github.com/google/blueprint/proptools" 22) 23 24var ( 25 copyStagingDirRule = pctx.AndroidStaticRule("copy_staging_dir", blueprint.RuleParams{ 26 Command: "rsync -a --checksum $dir/ $dest && touch $out", 27 }, "dir", "dest") 28) 29 30func (a *androidDevice) copyToProductOut(ctx android.ModuleContext, builder *android.RuleBuilder, src android.Path, dest string) { 31 destPath := android.PathForModuleInPartitionInstall(ctx, "").Join(ctx, dest) 32 builder.Command().Text("rsync").Flag("-a").Flag("--checksum").Input(src).Text(destPath.String()) 33} 34 35func (a *androidDevice) copyFilesToProductOutForSoongOnly(ctx android.ModuleContext) android.Path { 36 filesystemInfos := a.getFsInfos(ctx) 37 38 var deps android.Paths 39 var depsNoImg android.Paths // subset of deps without any img files. used for sbom creation. 40 41 for _, partition := range android.SortedKeys(filesystemInfos) { 42 info := filesystemInfos[partition] 43 imgInstallPath := android.PathForModuleInPartitionInstall(ctx, "", partition+".img") 44 ctx.Build(pctx, android.BuildParams{ 45 Rule: android.Cp, 46 Input: info.Output, 47 Output: imgInstallPath, 48 }) 49 50 // Make it so doing `m <moduleName>` or `m <partitionType>image` will copy the files to 51 // PRODUCT_OUT 52 if partition == "system_ext" { 53 partition = "systemext" 54 } 55 partition = partition + "image" 56 ctx.Phony(info.ModuleName, imgInstallPath) 57 ctx.Phony(partition, imgInstallPath) 58 for _, fip := range info.FullInstallPaths { 59 // TODO: Directories. But maybe they're not necessary? Adevice doesn't care 60 // about empty directories, still need to check if adb sync does. 61 if !fip.IsDir { 62 if !fip.RequiresFullInstall { 63 // Some modules set requires_full_install: false, which causes their staging 64 // directory file to not be installed. This is usually because the file appears 65 // in both PRODUCT_COPY_FILES and a soong module for the handwritten soong system 66 // image. In this case, that module's installed files would conflict with the 67 // PRODUCT_COPY_FILES. However, in soong-only builds, we don't automatically 68 // create rules for PRODUCT_COPY_FILES unless they're needed in the partition. 69 // So in that case, nothing is creating the installed path. Create them now 70 // if that's the case. 71 if fip.SymlinkTarget == "" { 72 ctx.Build(pctx, android.BuildParams{ 73 Rule: android.CpWithBash, 74 Input: fip.SourcePath, 75 Output: fip.FullInstallPath, 76 Args: map[string]string{ 77 // Preserve timestamps for adb sync, so that this installed file's 78 // timestamp matches the timestamp in the filesystem's intermediate 79 // staging dir 80 "cpFlags": "-p", 81 }, 82 }) 83 } else { 84 ctx.Build(pctx, android.BuildParams{ 85 Rule: android.SymlinkWithBash, 86 Output: fip.FullInstallPath, 87 Args: map[string]string{ 88 "fromPath": fip.SymlinkTarget, 89 }, 90 }) 91 } 92 } 93 ctx.Phony(info.ModuleName, fip.FullInstallPath) 94 ctx.Phony(partition, fip.FullInstallPath) 95 deps = append(deps, fip.FullInstallPath) 96 depsNoImg = append(depsNoImg, fip.FullInstallPath) 97 ctx.Phony("sync_"+partition, fip.FullInstallPath) 98 ctx.Phony("sync", fip.FullInstallPath) 99 } 100 } 101 102 deps = append(deps, imgInstallPath) 103 } 104 105 a.createComplianceMetadataTimestamp(ctx, depsNoImg) 106 107 // List all individual files to be copied to PRODUCT_OUT here 108 if a.deviceProps.Bootloader != nil { 109 bootloaderInstallPath := android.PathForModuleInPartitionInstall(ctx, "", "bootloader") 110 ctx.Build(pctx, android.BuildParams{ 111 Rule: android.Cp, 112 Input: android.PathForModuleSrc(ctx, *a.deviceProps.Bootloader), 113 Output: bootloaderInstallPath, 114 }) 115 deps = append(deps, bootloaderInstallPath) 116 } 117 118 copyBootImg := func(prop *string, type_ string) { 119 if proptools.String(prop) != "" { 120 partition := ctx.GetDirectDepWithTag(*prop, filesystemDepTag) 121 if info, ok := android.OtherModuleProvider(ctx, partition, BootimgInfoProvider); ok { 122 installPath := android.PathForModuleInPartitionInstall(ctx, "", type_+".img") 123 ctx.Build(pctx, android.BuildParams{ 124 Rule: android.Cp, 125 Input: info.Output, 126 Output: installPath, 127 }) 128 deps = append(deps, installPath) 129 } else { 130 ctx.ModuleErrorf("%s does not set BootimgInfo\n", *prop) 131 } 132 } 133 } 134 135 copyBootImg(a.partitionProps.Init_boot_partition_name, "init_boot") 136 copyBootImg(a.partitionProps.Boot_partition_name, "boot") 137 copyBootImg(a.partitionProps.Vendor_boot_partition_name, "vendor_boot") 138 139 for _, vbmetaModName := range a.partitionProps.Vbmeta_partitions { 140 partition := ctx.GetDirectDepWithTag(vbmetaModName, filesystemDepTag) 141 if info, ok := android.OtherModuleProvider(ctx, partition, vbmetaPartitionProvider); ok { 142 installPath := android.PathForModuleInPartitionInstall(ctx, "", info.Name+".img") 143 ctx.Build(pctx, android.BuildParams{ 144 Rule: android.Cp, 145 Input: info.Output, 146 Output: installPath, 147 }) 148 deps = append(deps, installPath) 149 } else { 150 ctx.ModuleErrorf("%s does not set vbmetaPartitionProvider\n", vbmetaModName) 151 } 152 } 153 154 if proptools.String(a.partitionProps.Super_partition_name) != "" { 155 partition := ctx.GetDirectDepWithTag(*a.partitionProps.Super_partition_name, superPartitionDepTag) 156 if info, ok := android.OtherModuleProvider(ctx, partition, SuperImageProvider); ok { 157 installPath := android.PathForModuleInPartitionInstall(ctx, "", "super.img") 158 ctx.Build(pctx, android.BuildParams{ 159 Rule: android.Cp, 160 Input: info.SuperImage, 161 Output: installPath, 162 }) 163 deps = append(deps, installPath) 164 } else { 165 ctx.ModuleErrorf("%s does not set SuperImageProvider\n", *a.partitionProps.Super_partition_name) 166 } 167 } 168 169 if proptools.String(a.deviceProps.Android_info) != "" { 170 installPath := android.PathForModuleInPartitionInstall(ctx, "", "android-info.txt") 171 ctx.Build(pctx, android.BuildParams{ 172 Rule: android.Cp, 173 Input: android.PathForModuleSrc(ctx, *a.deviceProps.Android_info), 174 Output: installPath, 175 }) 176 deps = append(deps, installPath) 177 } 178 179 copyToProductOutTimestamp := android.PathForModuleOut(ctx, "product_out_copy_timestamp") 180 ctx.Build(pctx, android.BuildParams{ 181 Rule: android.Touch, 182 Output: copyToProductOutTimestamp, 183 Implicits: deps, 184 }) 185 186 emptyFile := android.PathForModuleOut(ctx, "empty_file") 187 android.WriteFileRule(ctx, emptyFile, "") 188 189 // TODO: We don't have these tests building in soong yet. Add phonies for them so that CI builds 190 // that try to build them don't error out. 191 ctx.Phony("continuous_instrumentation_tests", emptyFile) 192 ctx.Phony("continuous_native_tests", emptyFile) 193 ctx.Phony("device-tests", emptyFile) 194 ctx.Phony("device-platinum-tests", emptyFile) 195 ctx.Phony("platform_tests", emptyFile) 196 197 return copyToProductOutTimestamp 198} 199 200// createComplianceMetadataTimestampForSoongOnly creates a timestamp file in m --soong-only 201// this timestamp file depends on installed files of the main `android_device`. 202// Any changes to installed files of the main `android_device` will retrigger SBOM generation 203func (a *androidDevice) createComplianceMetadataTimestamp(ctx android.ModuleContext, installedFiles android.Paths) { 204 ctx.Build(pctx, android.BuildParams{ 205 Rule: android.Touch, 206 Implicits: installedFiles, 207 Output: android.PathForOutput(ctx, "compliance-metadata", ctx.Config().DeviceProduct(), "installed_files.stamp"), 208 }) 209} 210 211// Returns a mapping from partition type -> FilesystemInfo. This includes filesystems that are 212// nested inside of other partitions, such as the partitions inside super.img, or ramdisk inside 213// of boot. 214func (a *androidDevice) getFsInfos(ctx android.ModuleContext) map[string]FilesystemInfo { 215 type propToType struct { 216 prop *string 217 ty string 218 } 219 220 filesystemInfos := make(map[string]FilesystemInfo) 221 222 partitionDefinitions := []propToType{ 223 propToType{a.partitionProps.System_partition_name, "system"}, 224 propToType{a.partitionProps.System_ext_partition_name, "system_ext"}, 225 propToType{a.partitionProps.Product_partition_name, "product"}, 226 propToType{a.partitionProps.Vendor_partition_name, "vendor"}, 227 propToType{a.partitionProps.Odm_partition_name, "odm"}, 228 propToType{a.partitionProps.Recovery_partition_name, "recovery"}, 229 propToType{a.partitionProps.System_dlkm_partition_name, "system_dlkm"}, 230 propToType{a.partitionProps.Vendor_dlkm_partition_name, "vendor_dlkm"}, 231 propToType{a.partitionProps.Odm_dlkm_partition_name, "odm_dlkm"}, 232 propToType{a.partitionProps.Userdata_partition_name, "userdata"}, 233 // filesystemInfo from init_boot and vendor_boot actually are re-exports of the ramdisk 234 // images inside of them 235 propToType{a.partitionProps.Init_boot_partition_name, "ramdisk"}, 236 propToType{a.partitionProps.Vendor_boot_partition_name, "vendor_ramdisk"}, 237 } 238 for _, partitionDefinition := range partitionDefinitions { 239 if proptools.String(partitionDefinition.prop) != "" { 240 partition := ctx.GetDirectDepWithTag(*partitionDefinition.prop, filesystemDepTag) 241 if info, ok := android.OtherModuleProvider(ctx, partition, FilesystemProvider); ok { 242 filesystemInfos[partitionDefinition.ty] = info 243 } else { 244 ctx.ModuleErrorf("Super partition %s does not set FilesystemProvider\n", partition.Name()) 245 } 246 } 247 } 248 if a.partitionProps.Super_partition_name != nil { 249 superPartition := ctx.GetDirectDepWithTag(*a.partitionProps.Super_partition_name, superPartitionDepTag) 250 if info, ok := android.OtherModuleProvider(ctx, superPartition, SuperImageProvider); ok { 251 for partition := range info.SubImageInfo { 252 filesystemInfos[partition] = info.SubImageInfo[partition] 253 } 254 } else { 255 ctx.ModuleErrorf("Super partition %s does not set SuperImageProvider\n", superPartition.Name()) 256 } 257 } 258 259 return filesystemInfos 260} 261