| // Copyright 2021 Google Inc. All rights reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package android |
| |
| import ( |
| "sort" |
| "strings" |
| |
| "github.com/google/blueprint" |
| "github.com/google/blueprint/proptools" |
| ) |
| |
| var ( |
| _ = pctx.HostBinToolVariable("licenseMetadataCmd", "build_license_metadata") |
| |
| licenseMetadataRule = pctx.AndroidStaticRule("licenseMetadataRule", blueprint.RuleParams{ |
| Command: "${licenseMetadataCmd} -o $out @${out}.rsp", |
| CommandDeps: []string{"${licenseMetadataCmd}"}, |
| Rspfile: "${out}.rsp", |
| RspfileContent: "${args}", |
| }, "args") |
| ) |
| |
| func buildLicenseMetadata(ctx ModuleContext, licenseMetadataFile WritablePath) { |
| base := ctx.Module().base() |
| |
| if !base.Enabled(ctx) { |
| return |
| } |
| |
| if exemptFromRequiredApplicableLicensesProperty(ctx.Module()) { |
| return |
| } |
| |
| var outputFiles Paths |
| if outputFileProducer, ok := ctx.Module().(OutputFileProducer); ok { |
| outputFiles, _ = outputFileProducer.OutputFiles("") |
| outputFiles = PathsIfNonNil(outputFiles...) |
| } |
| |
| // Only pass the last installed file to isContainerFromFileExtensions so a *.zip file in test data |
| // doesn't mark the whole module as a container. |
| var installFiles InstallPaths |
| if len(base.installFiles) > 0 { |
| installFiles = InstallPaths{base.installFiles[len(base.installFiles)-1]} |
| } |
| |
| isContainer := isContainerFromFileExtensions(installFiles, outputFiles) |
| |
| var allDepMetadataFiles Paths |
| var allDepMetadataArgs []string |
| var allDepOutputFiles Paths |
| var allDepMetadataDepSets []*DepSet[Path] |
| |
| ctx.VisitDirectDepsBlueprint(func(bpdep blueprint.Module) { |
| dep, _ := bpdep.(Module) |
| if dep == nil { |
| return |
| } |
| if !dep.Enabled(ctx) { |
| return |
| } |
| |
| // Defaults add properties and dependencies that get processed on their own. |
| if ctx.OtherModuleDependencyTag(dep) == DefaultsDepTag { |
| return |
| } |
| // The required dependencies just say modules A and B should be installed together. |
| // It doesn't mean that one is built using the other. |
| if ctx.OtherModuleDependencyTag(dep) == RequiredDepTag { |
| return |
| } |
| |
| if info, ok := OtherModuleProvider(ctx, dep, LicenseMetadataProvider); ok { |
| allDepMetadataFiles = append(allDepMetadataFiles, info.LicenseMetadataPath) |
| if isContainer || isInstallDepNeeded(dep, ctx.OtherModuleDependencyTag(dep)) { |
| allDepMetadataDepSets = append(allDepMetadataDepSets, info.LicenseMetadataDepSet) |
| } |
| |
| depAnnotations := licenseAnnotationsFromTag(ctx.OtherModuleDependencyTag(dep)) |
| |
| allDepMetadataArgs = append(allDepMetadataArgs, info.LicenseMetadataPath.String()+depAnnotations) |
| |
| if depInstallFiles := dep.base().installFiles; len(depInstallFiles) > 0 { |
| allDepOutputFiles = append(allDepOutputFiles, depInstallFiles.Paths()...) |
| } else if depOutputFiles, err := outputFilesForModule(ctx, dep, ""); err == nil { |
| depOutputFiles = PathsIfNonNil(depOutputFiles...) |
| allDepOutputFiles = append(allDepOutputFiles, depOutputFiles...) |
| } |
| } |
| }) |
| |
| allDepMetadataFiles = SortedUniquePaths(allDepMetadataFiles) |
| sort.Strings(allDepMetadataArgs) |
| allDepOutputFiles = SortedUniquePaths(allDepOutputFiles) |
| |
| var orderOnlyDeps Paths |
| var args []string |
| |
| if n := ctx.ModuleName(); n != "" { |
| args = append(args, |
| "-mn "+proptools.NinjaAndShellEscape(n)) |
| } |
| |
| if t := ctx.ModuleType(); t != "" { |
| args = append(args, |
| "-mt "+proptools.NinjaAndShellEscape(t)) |
| } |
| |
| args = append(args, |
| "-r "+proptools.NinjaAndShellEscape(ctx.ModuleDir()), |
| "-mc UNKNOWN") |
| |
| if p := base.commonProperties.Effective_package_name; p != nil { |
| args = append(args, |
| `-p `+proptools.NinjaAndShellEscapeIncludingSpaces(*p)) |
| } |
| |
| args = append(args, |
| JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(base.commonProperties.Effective_license_kinds), "-k ")) |
| |
| args = append(args, |
| JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(base.commonProperties.Effective_license_conditions), "-c ")) |
| |
| args = append(args, |
| JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(base.commonProperties.Effective_license_text.Strings()), "-n ")) |
| |
| if isContainer { |
| transitiveDeps := Paths(NewDepSet[Path](TOPOLOGICAL, nil, allDepMetadataDepSets).ToList()) |
| args = append(args, |
| JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(transitiveDeps.Strings()), "-d ")) |
| orderOnlyDeps = append(orderOnlyDeps, transitiveDeps...) |
| } else { |
| args = append(args, |
| JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(allDepMetadataArgs), "-d ")) |
| orderOnlyDeps = append(orderOnlyDeps, allDepMetadataFiles...) |
| } |
| |
| args = append(args, |
| JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(allDepOutputFiles.Strings()), "-s ")) |
| |
| // Install map |
| args = append(args, |
| JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(base.licenseInstallMap), "-m ")) |
| |
| // Built files |
| if len(outputFiles) > 0 { |
| args = append(args, |
| JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(outputFiles.Strings()), "-t ")) |
| } |
| |
| // Installed files |
| args = append(args, |
| JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(base.installFiles.Strings()), "-i ")) |
| |
| if isContainer { |
| args = append(args, "--is_container") |
| } |
| |
| ctx.Build(pctx, BuildParams{ |
| Rule: licenseMetadataRule, |
| Output: licenseMetadataFile, |
| OrderOnly: orderOnlyDeps, |
| Description: "license metadata", |
| Args: map[string]string{ |
| "args": strings.Join(args, " "), |
| }, |
| }) |
| |
| SetProvider(ctx, LicenseMetadataProvider, &LicenseMetadataInfo{ |
| LicenseMetadataPath: licenseMetadataFile, |
| LicenseMetadataDepSet: NewDepSet(TOPOLOGICAL, Paths{licenseMetadataFile}, allDepMetadataDepSets), |
| }) |
| } |
| |
| func isContainerFromFileExtensions(installPaths InstallPaths, builtPaths Paths) bool { |
| var paths Paths |
| if len(installPaths) > 0 { |
| paths = installPaths.Paths() |
| } else { |
| paths = builtPaths |
| } |
| |
| for _, path := range paths { |
| switch path.Ext() { |
| case ".zip", ".tar", ".tgz", ".tar.gz", ".img", ".srcszip", ".apex", ".capex": |
| return true |
| } |
| } |
| |
| return false |
| } |
| |
| // LicenseMetadataProvider is used to propagate license metadata paths between modules. |
| var LicenseMetadataProvider = blueprint.NewProvider[*LicenseMetadataInfo]() |
| |
| // LicenseMetadataInfo stores the license metadata path for a module. |
| type LicenseMetadataInfo struct { |
| LicenseMetadataPath Path |
| LicenseMetadataDepSet *DepSet[Path] |
| } |
| |
| // licenseAnnotationsFromTag returns the LicenseAnnotations for a tag (if any) converted into |
| // a string, or an empty string if there are none. |
| func licenseAnnotationsFromTag(tag blueprint.DependencyTag) string { |
| if annoTag, ok := tag.(LicenseAnnotationsDependencyTag); ok { |
| annos := annoTag.LicenseAnnotations() |
| if len(annos) > 0 { |
| annoStrings := make([]string, len(annos)) |
| for i, s := range annos { |
| annoStrings[i] = string(s) |
| } |
| return ":" + strings.Join(annoStrings, ",") |
| } |
| } |
| return "" |
| } |
| |
| // LicenseAnnotationsDependencyTag is implemented by dependency tags in order to provide a |
| // list of license dependency annotations. |
| type LicenseAnnotationsDependencyTag interface { |
| LicenseAnnotations() []LicenseAnnotation |
| } |
| |
| // LicenseAnnotation is an enum of annotations that can be applied to dependencies for propagating |
| // license information. |
| type LicenseAnnotation string |
| |
| const ( |
| // LicenseAnnotationSharedDependency should be returned by LicenseAnnotations implementations |
| // of dependency tags when the usage of the dependency is dynamic, for example a shared library |
| // linkage for native modules or as a classpath library for java modules. |
| // |
| // Dependency tags that need to always return LicenseAnnotationSharedDependency |
| // can embed LicenseAnnotationSharedDependencyTag to implement LicenseAnnotations. |
| LicenseAnnotationSharedDependency LicenseAnnotation = "dynamic" |
| |
| // LicenseAnnotationToolchain should be returned by LicenseAnnotations implementations of |
| // dependency tags when the dependency is used as a toolchain. |
| // |
| // Dependency tags that need to always return LicenseAnnotationToolchain |
| // can embed LicenseAnnotationToolchainDependencyTag to implement LicenseAnnotations. |
| LicenseAnnotationToolchain LicenseAnnotation = "toolchain" |
| ) |
| |
| // LicenseAnnotationSharedDependencyTag can be embedded in a dependency tag to implement |
| // LicenseAnnotations that always returns LicenseAnnotationSharedDependency. |
| type LicenseAnnotationSharedDependencyTag struct{} |
| |
| func (LicenseAnnotationSharedDependencyTag) LicenseAnnotations() []LicenseAnnotation { |
| return []LicenseAnnotation{LicenseAnnotationSharedDependency} |
| } |
| |
| // LicenseAnnotationToolchainDependencyTag can be embedded in a dependency tag to implement |
| // LicenseAnnotations that always returns LicenseAnnotationToolchain. |
| type LicenseAnnotationToolchainDependencyTag struct{} |
| |
| func (LicenseAnnotationToolchainDependencyTag) LicenseAnnotations() []LicenseAnnotation { |
| return []LicenseAnnotation{LicenseAnnotationToolchain} |
| } |