platform_build_soong/android/license_metadata.go
Cole Faust 0e0d749062 Make the enabled property configurable
This allows using select statements with it.

Ignore-AOSP-First: This needs to be in a topic with changes in interal-only repositories, I'll cherrypick to aosp after.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: I6f3efaaa3d82505e38a91ee4ba0e18e404360191
2024-04-30 10:56:12 -07:00

272 lines
9.1 KiB
Go

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