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