From 3dd2ff28ed41ecd3363b7c04c5d3401fe49c4e7c Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 8 Nov 2021 11:52:49 -0800 Subject: [PATCH] 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 --- android/Android.bp | 1 + android/androidmk.go | 6 + android/license_metadata.go | 231 ++++++++++++++++++++++++++++++++++++ android/module.go | 12 ++ apex/androidmk.go | 13 +- apex/builder.go | 35 ++++-- java/sdk_library.go | 11 +- 7 files changed, 286 insertions(+), 23 deletions(-) create mode 100644 android/license_metadata.go diff --git a/android/Android.bp b/android/Android.bp index a20aedc70..da369592a 100644 --- a/android/Android.bp +++ b/android/Android.bp @@ -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", diff --git a/android/androidmk.go b/android/androidmk.go index 0adc2a6eb..85f9ded45 100644 --- a/android/androidmk.go +++ b/android/androidmk.go @@ -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, diff --git a/android/license_metadata.go b/android/license_metadata.go new file mode 100644 index 000000000..3bc53d6a4 --- /dev/null +++ b/android/license_metadata.go @@ -0,0 +1,231 @@ +// 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} +} diff --git a/android/module.go b/android/module.go index 767f9f47f..df01dc583 100644 --- a/android/module.go +++ b/android/module.go @@ -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 diff --git a/apex/androidmk.go b/apex/androidmk.go index 7764b6b8d..f001fa265 100644 --- a/apex/androidmk.go +++ b/apex/androidmk.go @@ -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()) diff --git a/apex/builder.go b/apex/builder.go index 0880e2bab..5910784b2 100644 --- a/apex/builder.go +++ b/apex/builder.go @@ -434,7 +434,10 @@ func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) { // Avoid creating duplicate build rules for multi-installed APEXes. if proptools.BoolDefault(a.properties.Multi_install_skip_symbol_files, false) { installSymbolFiles = false + } + // set of dependency module:location mappings + installMapSet := make(map[string]bool) // TODO(jiyong): use the RuleBuilder var copyCommands []string @@ -442,7 +445,6 @@ func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) { pathWhenActivated := android.PathForModuleInPartitionInstall(ctx, "apex", apexName) for _, fi := range a.filesInfo { destPath := imageDir.Join(ctx, fi.path()).String() - var installedPath android.InstallPath // Prepare the destination path destPathDir := filepath.Dir(destPath) if fi.class == appSet { @@ -450,6 +452,8 @@ func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) { } copyCommands = append(copyCommands, "mkdir -p "+destPathDir) + installMapPath := fi.builtFile + // Copy the built file to the directory. But if the symlink optimization is turned // on, place a symlink to the corresponding file in /system partition instead. if a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform() { @@ -457,6 +461,7 @@ func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) { pathOnDevice := filepath.Join("/system", fi.path()) copyCommands = append(copyCommands, "ln -sfn "+pathOnDevice+" "+destPath) } else { + var installedPath android.InstallPath if fi.class == appSet { copyCommands = append(copyCommands, fmt.Sprintf("unzip -qDD -d %s %s", destPathDir, @@ -475,17 +480,19 @@ func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) { if installSymbolFiles { implicitInputs = append(implicitInputs, installedPath) } - } - // Create additional symlinks pointing the file inside the APEX (if any). Note that - // this is independent from the symlink optimization. - for _, symlinkPath := range fi.symlinkPaths() { - symlinkDest := imageDir.Join(ctx, symlinkPath).String() - copyCommands = append(copyCommands, "ln -sfn "+filepath.Base(destPath)+" "+symlinkDest) - if installSymbolFiles { - installedSymlink := ctx.InstallSymlink(pathWhenActivated.Join(ctx, filepath.Dir(symlinkPath)), filepath.Base(symlinkPath), installedPath) - implicitInputs = append(implicitInputs, installedSymlink) + // Create additional symlinks pointing the file inside the APEX (if any). Note that + // this is independent from the symlink optimization. + for _, symlinkPath := range fi.symlinkPaths() { + symlinkDest := imageDir.Join(ctx, symlinkPath).String() + copyCommands = append(copyCommands, "ln -sfn "+filepath.Base(destPath)+" "+symlinkDest) + if installSymbolFiles { + installedSymlink := ctx.InstallSymlink(pathWhenActivated.Join(ctx, filepath.Dir(symlinkPath)), filepath.Base(symlinkPath), installedPath) + implicitInputs = append(implicitInputs, installedSymlink) + } } + + installMapPath = installedPath } // Copy the test files (if any) @@ -502,6 +509,8 @@ func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) { copyCommands = append(copyCommands, "cp -f "+d.SrcPath.String()+" "+dataDest) implicitInputs = append(implicitInputs, d.SrcPath) } + + installMapSet[installMapPath.String()+":"+fi.installDir+"/"+fi.builtFile.Base()] = true } implicitInputs = append(implicitInputs, a.manifestPbOut) if installSymbolFiles { @@ -510,6 +519,12 @@ func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) { implicitInputs = append(implicitInputs, installedManifest, installedKey) } + if len(installMapSet) > 0 { + var installs []string + installs = append(installs, android.SortedStringKeys(installMapSet)...) + a.SetLicenseInstallMap(installs) + } + //////////////////////////////////////////////////////////////////////////////////////////// // Step 1.a: Write the list of files in this APEX to a txt file and compare it against // the allowed list given via the allowed_files property. Build fails when the two lists diff --git a/java/sdk_library.go b/java/sdk_library.go index 3065d57eb..daf932e55 100644 --- a/java/sdk_library.go +++ b/java/sdk_library.go @@ -1206,11 +1206,16 @@ func (module *SdkLibrary) DepsMutator(ctx android.BottomUpMutatorContext) { func (module *SdkLibrary) OutputFiles(tag string) (android.Paths, error) { paths, err := module.commonOutputFiles(tag) - if paths == nil && err == nil { - return module.Library.OutputFiles(tag) - } else { + if paths != nil || err != nil { return paths, err } + if module.requiresRuntimeImplementationLibrary() { + return module.Library.OutputFiles(tag) + } + if tag == "" { + return nil, nil + } + return nil, fmt.Errorf("unsupported module reference tag %q", tag) } func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {