• 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 and hwasan export both sanitized and unsanitized variants for static and header
97			// Always use unsanitized variants of them.
98			for _, t := range []SanitizerType{scs, Hwasan} {
99				if !sanitizable.Shared() && sanitizable.IsSanitizerEnabled(t) {
100					return false
101				}
102			}
103			// cfi also exports both variants. But for static, we capture both.
104			// This is because cfi static libraries can't be linked from non-cfi modules,
105			// and vice versa. This isn't the case for scs and hwasan sanitizers.
106			if !sanitizable.Static() && !sanitizable.Shared() && sanitizable.IsSanitizerEnabled(cfi) {
107				return false
108			}
109		}
110		if sanitizable.Static() {
111			return sanitizable.OutputFile().Valid() && !isPrivate(image, m)
112		}
113		if sanitizable.Shared() || sanitizable.Rlib() {
114			if !sanitizable.OutputFile().Valid() {
115				return false
116			}
117			if includeVndk(image) {
118				if !sanitizable.IsVndk() {
119					return true
120				}
121				return sanitizable.IsVndkExt()
122			}
123		}
124		return true
125	}
126
127	// Binaries and Objects
128	if m.Binary() || m.Object() {
129		return m.OutputFile().Valid()
130	}
131
132	return false
133}
134
135// Extend the snapshot.SnapshotJsonFlags to include cc specific fields.
136type snapshotJsonFlags struct {
137	snapshot.SnapshotJsonFlags
138	// library flags
139	ExportedDirs       []string `json:",omitempty"`
140	ExportedSystemDirs []string `json:",omitempty"`
141	ExportedFlags      []string `json:",omitempty"`
142	Sanitize           string   `json:",omitempty"`
143	SanitizeMinimalDep bool     `json:",omitempty"`
144	SanitizeUbsanDep   bool     `json:",omitempty"`
145
146	// binary flags
147	Symlinks         []string `json:",omitempty"`
148	StaticExecutable bool     `json:",omitempty"`
149	InstallInRoot    bool     `json:",omitempty"`
150
151	// dependencies
152	SharedLibs  []string `json:",omitempty"`
153	StaticLibs  []string `json:",omitempty"`
154	RuntimeLibs []string `json:",omitempty"`
155
156	// extra config files
157	InitRc         []string `json:",omitempty"`
158	VintfFragments []string `json:",omitempty"`
159}
160
161var ccSnapshotAction snapshot.GenerateSnapshotAction = func(s snapshot.SnapshotSingleton, ctx android.SingletonContext, snapshotArchDir string) android.Paths {
162	/*
163		Vendor snapshot zipped artifacts directory structure for cc modules:
164		{SNAPSHOT_ARCH}/
165			arch-{TARGET_ARCH}-{TARGET_ARCH_VARIANT}/
166				shared/
167					(.so shared libraries)
168				static/
169					(.a static libraries)
170				header/
171					(header only libraries)
172				binary/
173					(executable binaries)
174				object/
175					(.o object files)
176			arch-{TARGET_2ND_ARCH}-{TARGET_2ND_ARCH_VARIANT}/
177				shared/
178					(.so shared libraries)
179				static/
180					(.a static libraries)
181				header/
182					(header only libraries)
183				binary/
184					(executable binaries)
185				object/
186					(.o object files)
187			NOTICE_FILES/
188				(notice files, e.g. libbase.txt)
189			configs/
190				(config files, e.g. init.rc files, vintf_fragments.xml files, etc.)
191			include/
192				(header files of same directory structure with source tree)
193	*/
194
195	var snapshotOutputs android.Paths
196
197	includeDir := filepath.Join(snapshotArchDir, "include")
198	configsDir := filepath.Join(snapshotArchDir, "configs")
199	noticeDir := filepath.Join(snapshotArchDir, "NOTICE_FILES")
200
201	installedNotices := make(map[string]bool)
202	installedConfigs := make(map[string]bool)
203
204	var headers android.Paths
205
206	copyFile := func(ctx android.SingletonContext, path android.Path, out string, fake bool) android.OutputPath {
207		if fake {
208			// All prebuilt binaries and headers are installed by copyFile function. This makes a fake
209			// snapshot just touch prebuilts and headers, rather than installing real files.
210			return snapshot.WriteStringToFileRule(ctx, "", out)
211		} else {
212			return snapshot.CopyFileRule(pctx, ctx, path, out)
213		}
214	}
215
216	// installSnapshot function copies prebuilt file (.so, .a, or executable) and json flag file.
217	// For executables, init_rc and vintf_fragments files are also copied.
218	installSnapshot := func(m LinkableInterface, fake bool) android.Paths {
219		targetArch := "arch-" + m.Target().Arch.ArchType.String()
220		if m.Target().Arch.ArchVariant != "" {
221			targetArch += "-" + m.Target().Arch.ArchVariant
222		}
223
224		var ret android.Paths
225
226		prop := snapshotJsonFlags{}
227
228		// Common properties among snapshots.
229		prop.ModuleName = ctx.ModuleName(m)
230		if supportsVndkExt(s.Image) && m.IsVndkExt() {
231			// vndk exts are installed to /vendor/lib(64)?/vndk(-sp)?
232			if m.IsVndkSp() {
233				prop.RelativeInstallPath = "vndk-sp"
234			} else {
235				prop.RelativeInstallPath = "vndk"
236			}
237		} else {
238			prop.RelativeInstallPath = m.RelativeInstallPath()
239		}
240		prop.RuntimeLibs = m.SnapshotRuntimeLibs()
241		prop.Required = m.RequiredModuleNames()
242		for _, path := range m.InitRc() {
243			prop.InitRc = append(prop.InitRc, filepath.Join("configs", path.Base()))
244		}
245		for _, path := range m.VintfFragments() {
246			prop.VintfFragments = append(prop.VintfFragments, filepath.Join("configs", path.Base()))
247		}
248
249		// install config files. ignores any duplicates.
250		for _, path := range append(m.InitRc(), m.VintfFragments()...) {
251			out := filepath.Join(configsDir, path.Base())
252			if !installedConfigs[out] {
253				installedConfigs[out] = true
254				ret = append(ret, copyFile(ctx, path, out, fake))
255			}
256		}
257
258		var propOut string
259
260		if m.IsSnapshotLibrary() {
261			exporterInfo := ctx.ModuleProvider(m.Module(), FlagExporterInfoProvider).(FlagExporterInfo)
262
263			// library flags
264			prop.ExportedFlags = exporterInfo.Flags
265			for _, dir := range exporterInfo.IncludeDirs {
266				prop.ExportedDirs = append(prop.ExportedDirs, filepath.Join("include", dir.String()))
267			}
268			for _, dir := range exporterInfo.SystemIncludeDirs {
269				prop.ExportedSystemDirs = append(prop.ExportedSystemDirs, filepath.Join("include", dir.String()))
270			}
271
272			// shared libs dependencies aren't meaningful on static or header libs
273			if m.Shared() {
274				prop.SharedLibs = m.SnapshotSharedLibs()
275			}
276			// static libs dependencies are required to collect the NOTICE files.
277			prop.StaticLibs = m.SnapshotStaticLibs()
278			if sanitizable, ok := m.(PlatformSanitizeable); ok {
279				if sanitizable.Static() && sanitizable.SanitizePropDefined() {
280					prop.SanitizeMinimalDep = sanitizable.MinimalRuntimeDep() || sanitizable.MinimalRuntimeNeeded()
281					prop.SanitizeUbsanDep = sanitizable.UbsanRuntimeDep() || sanitizable.UbsanRuntimeNeeded()
282				}
283			}
284
285			var libType string
286			if m.Static() {
287				libType = "static"
288			} else if m.Shared() {
289				libType = "shared"
290			} else if m.Rlib() {
291				libType = "rlib"
292			} else {
293				libType = "header"
294			}
295
296			var stem string
297
298			// install .a or .so
299			if libType != "header" {
300				libPath := m.OutputFile().Path()
301				stem = libPath.Base()
302				if sanitizable, ok := m.(PlatformSanitizeable); ok {
303					if (sanitizable.Static() || sanitizable.Rlib()) && sanitizable.SanitizePropDefined() && sanitizable.IsSanitizerEnabled(cfi) {
304						// both cfi and non-cfi variant for static libraries can exist.
305						// attach .cfi to distinguish between cfi and non-cfi.
306						// e.g. libbase.a -> libbase.cfi.a
307						ext := filepath.Ext(stem)
308						stem = strings.TrimSuffix(stem, ext) + ".cfi" + ext
309						prop.Sanitize = "cfi"
310						prop.ModuleName += ".cfi"
311					}
312				}
313				snapshotLibOut := filepath.Join(snapshotArchDir, targetArch, libType, stem)
314				ret = append(ret, copyFile(ctx, libPath, snapshotLibOut, fake))
315			} else {
316				stem = ctx.ModuleName(m)
317			}
318
319			propOut = filepath.Join(snapshotArchDir, targetArch, libType, stem+".json")
320		} else if m.Binary() {
321			// binary flags
322			prop.Symlinks = m.Symlinks()
323			prop.StaticExecutable = m.StaticExecutable()
324			prop.InstallInRoot = m.InstallInRoot()
325			prop.SharedLibs = m.SnapshotSharedLibs()
326			// static libs dependencies are required to collect the NOTICE files.
327			prop.StaticLibs = m.SnapshotStaticLibs()
328			// install bin
329			binPath := m.OutputFile().Path()
330			snapshotBinOut := filepath.Join(snapshotArchDir, targetArch, "binary", binPath.Base())
331			ret = append(ret, copyFile(ctx, binPath, snapshotBinOut, fake))
332			propOut = snapshotBinOut + ".json"
333		} else if m.Object() {
334			// object files aren't installed to the device, so their names can conflict.
335			// Use module name as stem.
336			objPath := m.OutputFile().Path()
337			snapshotObjOut := filepath.Join(snapshotArchDir, targetArch, "object",
338				ctx.ModuleName(m)+filepath.Ext(objPath.Base()))
339			ret = append(ret, copyFile(ctx, objPath, snapshotObjOut, fake))
340			propOut = snapshotObjOut + ".json"
341		} else {
342			ctx.Errorf("unknown module %q in vendor snapshot", m.String())
343			return nil
344		}
345
346		j, err := json.Marshal(prop)
347		if err != nil {
348			ctx.Errorf("json marshal to %q failed: %#v", propOut, err)
349			return nil
350		}
351		ret = append(ret, snapshot.WriteStringToFileRule(ctx, string(j), propOut))
352
353		return ret
354	}
355
356	ctx.VisitAllModules(func(module android.Module) {
357		m, ok := module.(LinkableInterface)
358		if !ok {
359			return
360		}
361
362		moduleDir := ctx.ModuleDir(module)
363		inProprietaryPath := s.Image.IsProprietaryPath(moduleDir, ctx.DeviceConfig())
364		apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
365
366		if s.Image.ExcludeFromSnapshot(m) {
367			if inProprietaryPath {
368				// Error: exclude_from_vendor_snapshot applies
369				// to framework-path modules only.
370				ctx.Errorf("module %q in vendor proprietary path %q may not use \"exclude_from_vendor_snapshot: true\"", m.String(), moduleDir)
371				return
372			}
373		}
374
375		if !isSnapshotAware(ctx.DeviceConfig(), m, inProprietaryPath, apexInfo, s.Image) {
376			return
377		}
378
379		// If we are using directed snapshot and a module is not included in the
380		// list, we will still include the module as if it was a fake module.
381		// The reason is that soong needs all the dependencies to be present, even
382		// if they are not using during the build.
383		installAsFake := s.Fake
384		if s.Image.ExcludeFromDirectedSnapshot(ctx.DeviceConfig(), m.BaseModuleName()) {
385			installAsFake = true
386		}
387
388		// installSnapshot installs prebuilts and json flag files
389		snapshotOutputs = append(snapshotOutputs, installSnapshot(m, installAsFake)...)
390		// just gather headers and notice files here, because they are to be deduplicated
391		if m.IsSnapshotLibrary() {
392			headers = append(headers, m.SnapshotHeaders()...)
393		}
394
395		if len(m.EffectiveLicenseFiles()) > 0 {
396			noticeName := ctx.ModuleName(m) + ".txt"
397			noticeOut := filepath.Join(noticeDir, noticeName)
398			// skip already copied notice file
399			if !installedNotices[noticeOut] {
400				installedNotices[noticeOut] = true
401				snapshotOutputs = append(snapshotOutputs, combineNoticesRule(ctx, m.EffectiveLicenseFiles(), noticeOut))
402			}
403		}
404	})
405
406	// install all headers after removing duplicates
407	for _, header := range android.FirstUniquePaths(headers) {
408		snapshotOutputs = append(snapshotOutputs, copyFile(ctx, header, filepath.Join(includeDir, header.String()), s.Fake))
409	}
410
411	return snapshotOutputs
412}
413
414func init() {
415	snapshot.RegisterSnapshotAction(ccSnapshotAction)
416}
417