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