• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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