xref: /aosp_15_r20/build/soong/android/gen_notice.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
1// Copyright 2020 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	"fmt"
19	"path/filepath"
20	"strings"
21
22	"github.com/google/blueprint/proptools"
23)
24
25func init() {
26	RegisterGenNoticeBuildComponents(InitRegistrationContext)
27}
28
29// Register the gen_notice module type.
30func RegisterGenNoticeBuildComponents(ctx RegistrationContext) {
31	ctx.RegisterParallelSingletonType("gen_notice_build_rules", GenNoticeBuildRulesFactory)
32	ctx.RegisterModuleType("gen_notice", GenNoticeFactory)
33}
34
35type genNoticeBuildRules struct{}
36
37func (s *genNoticeBuildRules) GenerateBuildActions(ctx SingletonContext) {
38	ctx.VisitAllModules(func(m Module) {
39		gm, ok := m.(*genNoticeModule)
40		if !ok {
41			return
42		}
43		if len(gm.missing) > 0 {
44			missingReferencesRule(ctx, gm)
45			return
46		}
47		out := BuildNoticeTextOutputFromLicenseMetadata
48		if proptools.Bool(gm.properties.Xml) {
49			out = BuildNoticeXmlOutputFromLicenseMetadata
50		} else if proptools.Bool(gm.properties.Html) {
51			out = BuildNoticeHtmlOutputFromLicenseMetadata
52		}
53		defaultName := ""
54		if len(gm.properties.For) > 0 {
55			defaultName = gm.properties.For[0]
56		}
57
58		modules := make([]Module, 0)
59		for _, name := range gm.properties.For {
60			mods := ctx.ModuleVariantsFromName(gm, name)
61			for _, mod := range mods {
62				if mod == nil {
63					continue
64				}
65				if !mod.Enabled(ctx) { // don't depend on variants without build rules
66					continue
67				}
68				modules = append(modules, mod)
69			}
70		}
71		if ctx.Failed() {
72			return
73		}
74		out(ctx, gm.output, ctx.ModuleName(gm),
75			proptools.StringDefault(gm.properties.ArtifactName, defaultName),
76			[]string{
77				filepath.Join(ctx.Config().OutDir(), "target", "product", ctx.Config().DeviceName()) + "/",
78				ctx.Config().OutDir() + "/",
79				ctx.Config().SoongOutDir() + "/",
80			}, modules...)
81	})
82}
83
84func GenNoticeBuildRulesFactory() Singleton {
85	return &genNoticeBuildRules{}
86}
87
88type genNoticeProperties struct {
89	// For specifies the modules for which to generate a notice file.
90	For []string
91	// ArtifactName specifies the internal name to use for the notice file.
92	// It appears in the "used by:" list for targets whose entire name is stripped by --strip_prefix.
93	ArtifactName *string
94	// Stem specifies the base name of the output file.
95	Stem *string `android:"arch_variant"`
96	// Html indicates an html-format file is needed. The default is text. Can be Html or Xml but not both.
97	Html *bool
98	// Xml indicates an xml-format file is needed. The default is text. Can be Html or Xml but not both.
99	Xml *bool
100	// Gzipped indicates the output file must be compressed with gzip. Will append .gz to suffix if not there.
101	Gzipped *bool
102	// Suffix specifies the file extension to use. Defaults to .html for html, .xml for xml, or no extension for text.
103	Suffix *string
104	// Visibility specifies where this license can be used
105	Visibility []string
106}
107
108type genNoticeModule struct {
109	ModuleBase
110	DefaultableModuleBase
111
112	properties genNoticeProperties
113
114	output  OutputPath
115	missing []string
116}
117
118func (m *genNoticeModule) DepsMutator(ctx BottomUpMutatorContext) {
119	if ctx.ContainsProperty("licenses") {
120		ctx.PropertyErrorf("licenses", "not supported on \"gen_notice\" modules")
121	}
122	if proptools.Bool(m.properties.Html) && proptools.Bool(m.properties.Xml) {
123		ctx.ModuleErrorf("can be html or xml but not both")
124	}
125	if !ctx.Config().AllowMissingDependencies() {
126		var missing []string
127		// Verify the modules for which to generate notices exist.
128		for _, otherMod := range m.properties.For {
129			if !ctx.OtherModuleExists(otherMod) {
130				missing = append(missing, otherMod)
131			}
132		}
133		if len(missing) == 1 {
134			ctx.PropertyErrorf("for", "no %q module exists", missing[0])
135		} else if len(missing) > 1 {
136			ctx.PropertyErrorf("for", "modules \"%s\" do not exist", strings.Join(missing, "\", \""))
137		}
138	}
139}
140
141func (m *genNoticeModule) getStem() string {
142	stem := m.base().BaseModuleName()
143	if m.properties.Stem != nil {
144		stem = proptools.String(m.properties.Stem)
145	}
146	return stem
147}
148
149func (m *genNoticeModule) getSuffix() string {
150	suffix := ""
151	if m.properties.Suffix == nil {
152		if proptools.Bool(m.properties.Html) {
153			suffix = ".html"
154		} else if proptools.Bool(m.properties.Xml) {
155			suffix = ".xml"
156		}
157	} else {
158		suffix = proptools.String(m.properties.Suffix)
159	}
160	if proptools.Bool(m.properties.Gzipped) && !strings.HasSuffix(suffix, ".gz") {
161		suffix += ".gz"
162	}
163	return suffix
164}
165
166func (m *genNoticeModule) GenerateAndroidBuildActions(ctx ModuleContext) {
167	if ctx.Config().AllowMissingDependencies() {
168		// Verify the modules for which to generate notices exist.
169		for _, otherMod := range m.properties.For {
170			if !ctx.OtherModuleExists(otherMod) {
171				m.missing = append(m.missing, otherMod)
172			}
173		}
174		m.missing = append(m.missing, ctx.GetMissingDependencies()...)
175		m.missing = FirstUniqueStrings(m.missing)
176	}
177	out := m.getStem() + m.getSuffix()
178	m.output = PathForModuleOut(ctx, out).OutputPath
179	ctx.SetOutputFiles(Paths{m.output}, "")
180}
181
182func GenNoticeFactory() Module {
183	module := &genNoticeModule{}
184
185	base := module.base()
186	module.AddProperties(&base.nameProperties, &module.properties)
187
188	// The visibility property needs to be checked and parsed by the visibility module.
189	setPrimaryVisibilityProperty(module, "visibility", &module.properties.Visibility)
190
191	InitAndroidArchModule(module, DeviceSupported, MultilibCommon)
192	InitDefaultableModule(module)
193
194	return module
195}
196
197var _ AndroidMkEntriesProvider = (*genNoticeModule)(nil)
198
199// Implements AndroidMkEntriesProvider
200func (m *genNoticeModule) AndroidMkEntries() []AndroidMkEntries {
201	return []AndroidMkEntries{AndroidMkEntries{
202		Class:      "ETC",
203		OutputFile: OptionalPathForPath(m.output),
204	}}
205}
206
207// missingReferencesRule emits an ErrorRule for missing module references.
208func missingReferencesRule(ctx BuilderContext, m *genNoticeModule) {
209	if len(m.missing) < 1 {
210		panic(fmt.Errorf("missing references rule requested with no missing references"))
211	}
212
213	ctx.Build(pctx, BuildParams{
214		Rule:        ErrorRule,
215		Output:      m.output,
216		Description: "notice for " + proptools.StringDefault(m.properties.ArtifactName, "container"),
217		Args: map[string]string{
218			"error": m.Name() + " references missing module(s): " + strings.Join(m.missing, ", "),
219		},
220	})
221}
222