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