// 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} }