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