Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3dd2ff28 authored by Colin Cross's avatar Colin Cross
Browse files

Build license metadata files in Soong

Soong has enough information to build the license metadata files
without resorting to the fixups required in Make.

Bug: 207445310
Test: m checkbuild
Change-Id: I8e74108376162b8fdb87ba098ebe94350aa1f7c4
parent 1c35f243
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@ bootstrap_go_package {
        "image.go",
        "license.go",
        "license_kind.go",
        "license_metadata.go",
        "license_sdk_member.go",
        "licenses.go",
        "makefile_goal.go",
+6 −0
Original line number Diff line number Diff line
@@ -474,6 +474,7 @@ type fillInEntriesContext interface {
	ModuleDir(module blueprint.Module) string
	Config() Config
	ModuleProvider(module blueprint.Module, provider blueprint.ProviderKey) interface{}
	ModuleHasProvider(module blueprint.Module, provider blueprint.ProviderKey) bool
}

func (a *AndroidMkEntries) fillInEntries(ctx fillInEntriesContext, mod blueprint.Module) {
@@ -609,6 +610,11 @@ func (a *AndroidMkEntries) fillInEntries(ctx fillInEntriesContext, mod blueprint
		}
	}

	if ctx.ModuleHasProvider(mod, LicenseMetadataProvider) {
		licenseMetadata := ctx.ModuleProvider(mod, LicenseMetadataProvider).(*LicenseMetadataInfo)
		a.SetPath("LOCAL_SOONG_LICENSE_METADATA", licenseMetadata.LicenseMetadataPath)
	}

	extraCtx := &androidMkExtraEntriesContext{
		ctx: ctx,
		mod: mod,
+231 −0
Original line number Diff line number Diff line
// 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 (
	"fmt"
	"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) {
	base := ctx.Module().base()

	if !base.Enabled() {
		return
	}

	if exemptFromRequiredApplicableLicensesProperty(ctx.Module()) {
		return
	}

	var allDepMetadataFiles Paths
	var allDepMetadataArgs []string
	var allDepOutputFiles Paths

	ctx.VisitDirectDepsBlueprint(func(bpdep blueprint.Module) {
		dep, _ := bpdep.(Module)
		if dep == nil {
			return
		}
		if !dep.Enabled() {
			return
		}

		if ctx.OtherModuleHasProvider(dep, LicenseMetadataProvider) {
			info := ctx.OtherModuleProvider(dep, LicenseMetadataProvider).(*LicenseMetadataInfo)
			allDepMetadataFiles = append(allDepMetadataFiles, info.LicenseMetadataPath)

			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 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.NinjaAndShellEscape(*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 "))

	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
	var outputFiles Paths
	if outputFileProducer, ok := ctx.Module().(OutputFileProducer); ok {
		outputFiles, _ = outputFileProducer.OutputFiles("")
		outputFiles = PathsIfNonNil(outputFiles...)
	}

	if len(outputFiles) > 0 {
		args = append(args,
			JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(outputFiles.Strings()), "-t "))
	} else {
		args = append(args, fmt.Sprintf("-t //%s:%s", ctx.ModuleDir(), ctx.ModuleName()))
	}

	// Installed files
	args = append(args,
		JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(base.installFiles.Strings()), "-i "))

	isContainer := isContainerFromFileExtensions(base.installFiles, outputFiles)
	if isContainer {
		args = append(args, "--is_container")
	}

	licenseMetadataFile := PathForModuleOut(ctx, "meta_lic")

	ctx.Build(pctx, BuildParams{
		Rule:        licenseMetadataRule,
		Output:      licenseMetadataFile,
		OrderOnly:   orderOnlyDeps,
		Description: "license metadata",
		Args: map[string]string{
			"args": strings.Join(args, " "),
		},
	})

	ctx.SetProvider(LicenseMetadataProvider, &LicenseMetadataInfo{
		LicenseMetadataPath: licenseMetadataFile,
	})
}

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
}

// 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.
	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"
)

// 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}
}
+12 −0
Original line number Diff line number Diff line
@@ -1230,6 +1230,10 @@ type ModuleBase struct {

	initRcPaths         Paths
	vintfFragmentsPaths Paths

	// set of dependency module:location mappings used to populate the license metadata for
	// apex containers.
	licenseInstallMap []string
}

// A struct containing all relevant information about a Bazel target converted via bp2build.
@@ -1774,6 +1778,12 @@ func (m *ModuleBase) VintfFragments() Paths {
	return append(Paths{}, m.vintfFragmentsPaths...)
}

// SetLicenseInstallMap stores the set of dependency module:location mappings for files in an
// apex container for use when generation the license metadata file.
func (m *ModuleBase) SetLicenseInstallMap(installMap []string) {
	m.licenseInstallMap = append(m.licenseInstallMap, installMap...)
}

func (m *ModuleBase) generateModuleTarget(ctx ModuleContext) {
	var allInstalledFiles InstallPaths
	var allCheckbuildFiles Paths
@@ -2049,6 +2059,8 @@ func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext)
	m.installFilesDepSet = newInstallPathsDepSet(m.installFiles, dependencyInstallFiles)
	m.packagingSpecsDepSet = newPackagingSpecsDepSet(m.packagingSpecs, dependencyPackagingSpecs)

	buildLicenseMetadata(ctx)

	m.buildParams = ctx.buildParams
	m.ruleParams = ctx.ruleParams
	m.variables = ctx.variables
+3 −10
Original line number Diff line number Diff line
@@ -309,19 +309,17 @@ func (a *apexBundle) androidMkForFiles(w io.Writer, apexBundleName, apexName, mo
	return moduleNames
}

func (a *apexBundle) writeRequiredModules(w io.Writer, apexBundleName string) {
func (a *apexBundle) writeRequiredModules(w io.Writer) {
	var required []string
	var targetRequired []string
	var hostRequired []string
	required = append(required, a.RequiredModuleNames()...)
	targetRequired = append(targetRequired, a.TargetRequiredModuleNames()...)
	hostRequired = append(hostRequired, a.HostRequiredModuleNames()...)
	installMapSet := make(map[string]bool) // set of dependency module:location mappings
	for _, fi := range a.filesInfo {
		required = append(required, fi.requiredModuleNames...)
		targetRequired = append(targetRequired, fi.targetRequiredModuleNames...)
		hostRequired = append(hostRequired, fi.hostRequiredModuleNames...)
		installMapSet[a.fullModuleName(apexBundleName, &fi)+":"+fi.installDir+"/"+fi.builtFile.Base()] = true
	}

	if len(required) > 0 {
@@ -333,11 +331,6 @@ func (a *apexBundle) writeRequiredModules(w io.Writer, apexBundleName string) {
	if len(hostRequired) > 0 {
		fmt.Fprintln(w, "LOCAL_HOST_REQUIRED_MODULES +=", strings.Join(hostRequired, " "))
	}
	if len(installMapSet) > 0 {
		var installs []string
		installs = append(installs, android.SortedStringKeys(installMapSet)...)
		fmt.Fprintln(w, "LOCAL_LICENSE_INSTALL_MAP +=", strings.Join(installs, " "))
	}
}

func (a *apexBundle) androidMkForType() android.AndroidMkData {
@@ -359,7 +352,7 @@ func (a *apexBundle) androidMkForType() android.AndroidMkData {
				if len(moduleNames) > 0 {
					fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", strings.Join(moduleNames, " "))
				}
				a.writeRequiredModules(w, name)
				a.writeRequiredModules(w)
				fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)")

			} else {
@@ -401,7 +394,7 @@ func (a *apexBundle) androidMkForType() android.AndroidMkData {
				if len(a.requiredDeps) > 0 {
					fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(a.requiredDeps, " "))
				}
				a.writeRequiredModules(w, name)
				a.writeRequiredModules(w)

				if a.mergedNotices.Merged.Valid() {
					fmt.Fprintln(w, "LOCAL_NOTICE_FILE :=", a.mergedNotices.Merged.Path().String())
Loading