xref: /aosp_15_r20/build/soong/android/license_metadata.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
1// Copyright 2021 Google Inc. All rights reserved.
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 android
16
17import (
18	"github.com/google/blueprint/depset"
19	"sort"
20	"strings"
21
22	"github.com/google/blueprint"
23	"github.com/google/blueprint/proptools"
24)
25
26var (
27	_ = pctx.HostBinToolVariable("licenseMetadataCmd", "build_license_metadata")
28
29	licenseMetadataRule = pctx.AndroidStaticRule("licenseMetadataRule", blueprint.RuleParams{
30		Command:        "${licenseMetadataCmd} -o $out @${out}.rsp",
31		CommandDeps:    []string{"${licenseMetadataCmd}"},
32		Rspfile:        "${out}.rsp",
33		RspfileContent: "${args}",
34	}, "args")
35)
36
37func buildLicenseMetadata(ctx *moduleContext, licenseMetadataFile WritablePath) {
38	base := ctx.Module().base()
39
40	if !base.Enabled(ctx) {
41		return
42	}
43
44	if exemptFromRequiredApplicableLicensesProperty(ctx.Module()) {
45		return
46	}
47
48	var outputFiles Paths
49	if outputFiles, err := outputFilesForModule(ctx, ctx.Module(), ""); err == nil {
50		outputFiles = PathsIfNonNil(outputFiles...)
51	}
52
53	// Only pass the last installed file to isContainerFromFileExtensions so a *.zip file in test data
54	// doesn't mark the whole module as a container.
55	var installFiles InstallPaths
56	if len(ctx.installFiles) > 0 {
57		installFiles = InstallPaths{ctx.installFiles[len(ctx.installFiles)-1]}
58	}
59
60	isContainer := isContainerFromFileExtensions(installFiles, outputFiles)
61
62	var allDepMetadataFiles Paths
63	var allDepMetadataArgs []string
64	var allDepOutputFiles Paths
65	var allDepMetadataDepSets []depset.DepSet[Path]
66
67	ctx.VisitDirectDeps(func(dep Module) {
68		if !dep.Enabled(ctx) {
69			return
70		}
71
72		// Defaults add properties and dependencies that get processed on their own.
73		if ctx.OtherModuleDependencyTag(dep) == DefaultsDepTag {
74			return
75		}
76		// The required dependencies just say modules A and B should be installed together.
77		// It doesn't mean that one is built using the other.
78		if ctx.OtherModuleDependencyTag(dep) == RequiredDepTag {
79			return
80		}
81
82		if info, ok := OtherModuleProvider(ctx, dep, LicenseMetadataProvider); ok {
83			allDepMetadataFiles = append(allDepMetadataFiles, info.LicenseMetadataPath)
84			if isContainer || isInstallDepNeeded(dep, ctx.OtherModuleDependencyTag(dep)) {
85				allDepMetadataDepSets = append(allDepMetadataDepSets, info.LicenseMetadataDepSet)
86			}
87
88			depAnnotations := licenseAnnotationsFromTag(ctx.OtherModuleDependencyTag(dep))
89
90			allDepMetadataArgs = append(allDepMetadataArgs, info.LicenseMetadataPath.String()+depAnnotations)
91
92			if depInstallFiles := OtherModuleProviderOrDefault(ctx, dep, InstallFilesProvider).InstallFiles; len(depInstallFiles) > 0 {
93				allDepOutputFiles = append(allDepOutputFiles, depInstallFiles.Paths()...)
94			} else if depOutputFiles, err := outputFilesForModule(ctx, dep, ""); err == nil {
95				depOutputFiles = PathsIfNonNil(depOutputFiles...)
96				allDepOutputFiles = append(allDepOutputFiles, depOutputFiles...)
97			}
98		}
99	})
100
101	allDepMetadataFiles = SortedUniquePaths(allDepMetadataFiles)
102	sort.Strings(allDepMetadataArgs)
103	allDepOutputFiles = SortedUniquePaths(allDepOutputFiles)
104
105	var orderOnlyDeps Paths
106	var args []string
107
108	if n := ctx.ModuleName(); n != "" {
109		args = append(args,
110			"-mn "+proptools.NinjaAndShellEscape(n))
111	}
112
113	if t := ctx.ModuleType(); t != "" {
114		args = append(args,
115			"-mt "+proptools.NinjaAndShellEscape(t))
116	}
117
118	args = append(args,
119		"-r "+proptools.NinjaAndShellEscape(ctx.ModuleDir()),
120		"-mc UNKNOWN")
121
122	if p := base.commonProperties.Effective_package_name; p != nil {
123		args = append(args,
124			`-p `+proptools.NinjaAndShellEscapeIncludingSpaces(*p))
125	}
126
127	args = append(args,
128		JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(base.commonProperties.Effective_license_kinds), "-k "))
129
130	args = append(args,
131		JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(base.commonProperties.Effective_license_conditions), "-c "))
132
133	args = append(args,
134		JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(base.commonProperties.Effective_license_text.Strings()), "-n "))
135
136	if isContainer {
137		transitiveDeps := Paths(depset.New[Path](depset.TOPOLOGICAL, nil, allDepMetadataDepSets).ToList())
138		args = append(args,
139			JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(transitiveDeps.Strings()), "-d "))
140		orderOnlyDeps = append(orderOnlyDeps, transitiveDeps...)
141	} else {
142		args = append(args,
143			JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(allDepMetadataArgs), "-d "))
144		orderOnlyDeps = append(orderOnlyDeps, allDepMetadataFiles...)
145	}
146
147	args = append(args,
148		JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(allDepOutputFiles.Strings()), "-s "))
149
150	// Install map
151	args = append(args,
152		JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(ctx.licenseInstallMap), "-m "))
153
154	// Built files
155	if len(outputFiles) > 0 {
156		args = append(args,
157			JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(outputFiles.Strings()), "-t "))
158	}
159
160	// Installed files
161	args = append(args,
162		JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(ctx.installFiles.Strings()), "-i "))
163
164	if isContainer {
165		args = append(args, "--is_container")
166	}
167
168	ctx.Build(pctx, BuildParams{
169		Rule:        licenseMetadataRule,
170		Output:      licenseMetadataFile,
171		OrderOnly:   orderOnlyDeps,
172		Description: "license metadata",
173		Args: map[string]string{
174			"args": strings.Join(args, " "),
175		},
176	})
177
178	SetProvider(ctx, LicenseMetadataProvider, &LicenseMetadataInfo{
179		LicenseMetadataPath:   licenseMetadataFile,
180		LicenseMetadataDepSet: depset.New(depset.TOPOLOGICAL, Paths{licenseMetadataFile}, allDepMetadataDepSets),
181	})
182}
183
184func isContainerFromFileExtensions(installPaths InstallPaths, builtPaths Paths) bool {
185	var paths Paths
186	if len(installPaths) > 0 {
187		paths = installPaths.Paths()
188	} else {
189		paths = builtPaths
190	}
191
192	for _, path := range paths {
193		switch path.Ext() {
194		case ".zip", ".tar", ".tgz", ".tar.gz", ".img", ".srcszip", ".apex", ".capex":
195			return true
196		}
197	}
198
199	return false
200}
201
202// LicenseMetadataProvider is used to propagate license metadata paths between modules.
203var LicenseMetadataProvider = blueprint.NewProvider[*LicenseMetadataInfo]()
204
205// LicenseMetadataInfo stores the license metadata path for a module.
206type LicenseMetadataInfo struct {
207	LicenseMetadataPath   Path
208	LicenseMetadataDepSet depset.DepSet[Path]
209}
210
211// licenseAnnotationsFromTag returns the LicenseAnnotations for a tag (if any) converted into
212// a string, or an empty string if there are none.
213func licenseAnnotationsFromTag(tag blueprint.DependencyTag) string {
214	if annoTag, ok := tag.(LicenseAnnotationsDependencyTag); ok {
215		annos := annoTag.LicenseAnnotations()
216		if len(annos) > 0 {
217			annoStrings := make([]string, len(annos))
218			for i, s := range annos {
219				annoStrings[i] = string(s)
220			}
221			return ":" + strings.Join(annoStrings, ",")
222		}
223	}
224	return ""
225}
226
227// LicenseAnnotationsDependencyTag is implemented by dependency tags in order to provide a
228// list of license dependency annotations.
229type LicenseAnnotationsDependencyTag interface {
230	LicenseAnnotations() []LicenseAnnotation
231}
232
233// LicenseAnnotation is an enum of annotations that can be applied to dependencies for propagating
234// license information.
235type LicenseAnnotation string
236
237const (
238	// LicenseAnnotationSharedDependency should be returned by LicenseAnnotations implementations
239	// of dependency tags when the usage of the dependency is dynamic, for example a shared library
240	// linkage for native modules or as a classpath library for java modules.
241	//
242	// Dependency tags that need to always return LicenseAnnotationSharedDependency
243	// can embed LicenseAnnotationSharedDependencyTag to implement LicenseAnnotations.
244	LicenseAnnotationSharedDependency LicenseAnnotation = "dynamic"
245
246	// LicenseAnnotationToolchain should be returned by LicenseAnnotations implementations of
247	// dependency tags when the dependency is used as a toolchain.
248	//
249	// Dependency tags that need to always return LicenseAnnotationToolchain
250	// can embed LicenseAnnotationToolchainDependencyTag to implement LicenseAnnotations.
251	LicenseAnnotationToolchain LicenseAnnotation = "toolchain"
252)
253
254// LicenseAnnotationSharedDependencyTag can be embedded in a dependency tag to implement
255// LicenseAnnotations that always returns LicenseAnnotationSharedDependency.
256type LicenseAnnotationSharedDependencyTag struct{}
257
258func (LicenseAnnotationSharedDependencyTag) LicenseAnnotations() []LicenseAnnotation {
259	return []LicenseAnnotation{LicenseAnnotationSharedDependency}
260}
261
262// LicenseAnnotationToolchainDependencyTag can be embedded in a dependency tag to implement
263// LicenseAnnotations that always returns LicenseAnnotationToolchain.
264type LicenseAnnotationToolchainDependencyTag struct{}
265
266func (LicenseAnnotationToolchainDependencyTag) LicenseAnnotations() []LicenseAnnotation {
267	return []LicenseAnnotation{LicenseAnnotationToolchain}
268}
269