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