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

Commit 803474e5 authored by Jingwen Chen's avatar Jingwen Chen Committed by Anton Hansson
Browse files

Support multiple dists per Android.bp module, and dist output selection.



This CL adds "dists" to the base property struct to support multiple
dist file configurations, and generic tag support to dist tagged outputs
of modules.

Fixes: b/152834186
Test: soong tests and `m sdk dist`

Change-Id: I80c86bc9b7b09e671f640a4480c45d438bdd9a2a
Merged-In: I80c86bc9b7b09e671f640a4480c45d438bdd9a2a
Exempt-From-Owner-Approval: cherry-pick from master
Signed-off-by: default avatarJingwen Chen <jingwen@google.com>
(cherry picked from commit 40fd90ae)
parent 6568ae45
Loading
Loading
Loading
Loading
+83 −27
Original line number Diff line number Diff line
@@ -46,7 +46,7 @@ type AndroidMkDataProvider interface {
type AndroidMkData struct {
	Class           string
	SubName         string
	DistFile        OptionalPath
	DistFiles       TaggedDistFiles
	OutputFile      OptionalPath
	Disabled        bool
	Include         string
@@ -72,7 +72,7 @@ type AndroidMkEntriesProvider interface {
type AndroidMkEntries struct {
	Class           string
	SubName         string
	DistFile        OptionalPath
	DistFiles       TaggedDistFiles
	OutputFile      OptionalPath
	Disabled        bool
	Include         string
@@ -176,54 +176,110 @@ func (a *AndroidMkEntries) AddStrings(name string, value ...string) {
	a.EntryMap[name] = append(a.EntryMap[name], value...)
}

func (a *AndroidMkEntries) fillInEntries(config Config, bpPath string, mod blueprint.Module) {
	a.EntryMap = make(map[string][]string)
// Compute the list of Make strings to declare phone goals and dist-for-goals
// calls from the module's dist and dists properties.
func (a *AndroidMkEntries) GetDistForGoals(mod blueprint.Module) []string {
	amod := mod.(Module).base()
	name := amod.BaseModuleName()

	if a.Include == "" {
		a.Include = "$(BUILD_PREBUILT)"
	var ret []string

	availableTaggedDists := TaggedDistFiles{}
	if a.DistFiles != nil && len(a.DistFiles[""]) > 0 {
		availableTaggedDists = a.DistFiles
	} else if a.OutputFile.Valid() {
		availableTaggedDists = MakeDefaultDistFiles(a.OutputFile.Path())
	}
	a.Required = append(a.Required, amod.commonProperties.Required...)
	a.Host_required = append(a.Host_required, amod.commonProperties.Host_required...)
	a.Target_required = append(a.Target_required, amod.commonProperties.Target_required...)

	// Fill in the header part.
	if len(amod.commonProperties.Dist.Targets) > 0 {
		distFile := a.DistFile
		if !distFile.Valid() {
			distFile = a.OutputFile
	// Iterate over this module's dist structs, merged from the dist and dists properties.
	for _, dist := range amod.Dists() {
		// Get the list of goals this dist should be enabled for. e.g. sdk, droidcore
		goals := strings.Join(dist.Targets, " ")

		// Get the tag representing the output files to be dist'd. e.g. ".jar", ".proguard_map"
		var tag string
		if dist.Tag == nil {
			// If the dist struct does not specify a tag, use the default output files tag.
			tag = ""
		} else {
			tag = *dist.Tag
		}

		// Get the paths of the output files to be dist'd, represented by the tag.
		// Can be an empty list.
		tagPaths := availableTaggedDists[tag]
		if len(tagPaths) == 0 {
			// Nothing to dist for this tag, continue to the next dist.
			continue
		}

		if len(tagPaths) > 1 && (dist.Dest != nil || dist.Suffix != nil) {
			errorMessage := "Cannot apply dest/suffix for more than one dist " +
				"file for %s goals in module %s. The list of dist files, " +
				"which should have a single element, is:\n%s"
			panic(fmt.Errorf(errorMessage, goals, name, tagPaths))
		}

		ret = append(ret, fmt.Sprintf(".PHONY: %s\n", goals))

		// Create dist-for-goals calls for each path in the dist'd files.
		for _, path := range tagPaths {
			// It's possible that the Path is nil from errant modules. Be defensive here.
			if path == nil {
				tagName := "default" // for error message readability
				if dist.Tag != nil {
					tagName = *dist.Tag
				}
				panic(fmt.Errorf("Dist file should not be nil for the %s tag in %s", tagName, name))
			}
		if distFile.Valid() {
			dest := filepath.Base(distFile.String())

			if amod.commonProperties.Dist.Dest != nil {
			dest := filepath.Base(path.String())

			if dist.Dest != nil {
				var err error
				if dest, err = validateSafePath(*amod.commonProperties.Dist.Dest); err != nil {
				if dest, err = validateSafePath(*dist.Dest); err != nil {
					// This was checked in ModuleBase.GenerateBuildActions
					panic(err)
				}
			}

			if amod.commonProperties.Dist.Suffix != nil {
			if dist.Suffix != nil {
				ext := filepath.Ext(dest)
				suffix := *amod.commonProperties.Dist.Suffix
				suffix := *dist.Suffix
				dest = strings.TrimSuffix(dest, ext) + suffix + ext
			}

			if amod.commonProperties.Dist.Dir != nil {
			if dist.Dir != nil {
				var err error
				if dest, err = validateSafePath(*amod.commonProperties.Dist.Dir, dest); err != nil {
				if dest, err = validateSafePath(*dist.Dir, dest); err != nil {
					// This was checked in ModuleBase.GenerateBuildActions
					panic(err)
				}
			}

			goals := strings.Join(amod.commonProperties.Dist.Targets, " ")
			fmt.Fprintln(&a.header, ".PHONY:", goals)
			fmt.Fprintf(&a.header, "$(call dist-for-goals,%s,%s:%s)\n",
				goals, distFile.String(), dest)
			ret = append(
				ret,
				fmt.Sprintf("$(call dist-for-goals,%s,%s:%s)\n", goals, path.String(), dest))
		}
	}

	return ret
}

func (a *AndroidMkEntries) fillInEntries(config Config, bpPath string, mod blueprint.Module) {
	a.EntryMap = make(map[string][]string)
	amod := mod.(Module).base()
	name := amod.BaseModuleName()

	if a.Include == "" {
		a.Include = "$(BUILD_PREBUILT)"
	}
	a.Required = append(a.Required, amod.commonProperties.Required...)
	a.Host_required = append(a.Host_required, amod.commonProperties.Host_required...)
	a.Target_required = append(a.Target_required, amod.commonProperties.Target_required...)

	for _, distString := range a.GetDistForGoals(mod) {
		fmt.Fprintf(&a.header, distString)
	}

	fmt.Fprintln(&a.header, "\ninclude $(CLEAR_VARS)")
@@ -469,7 +525,7 @@ func (data *AndroidMkData) fillInData(config Config, bpPath string, mod blueprin
	entries := AndroidMkEntries{
		Class:           data.Class,
		SubName:         data.SubName,
		DistFile:        data.DistFile,
		DistFiles:       data.DistFiles,
		OutputFile:      data.OutputFile,
		Disabled:        data.Disabled,
		Include:         data.Include,
+180 −1
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
package android

import (
	"fmt"
	"io"
	"reflect"
	"testing"
@@ -23,9 +24,11 @@ import (
type customModule struct {
	ModuleBase
	data      AndroidMkData
	distFiles TaggedDistFiles
}

func (m *customModule) GenerateAndroidBuildActions(ctx ModuleContext) {
	m.distFiles = m.GenerateTaggedDistFiles(ctx)
}

func (m *customModule) AndroidMk() AndroidMkData {
@@ -36,6 +39,26 @@ func (m *customModule) AndroidMk() AndroidMkData {
	}
}

func (m *customModule) OutputFiles(tag string) (Paths, error) {
	switch tag {
	case "":
		return PathsForTesting("one.out"), nil
	case ".multiple":
		return PathsForTesting("two.out", "three/four.out"), nil
	default:
		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
	}
}

func (m *customModule) AndroidMkEntries() []AndroidMkEntries {
	return []AndroidMkEntries{
		{
			Class:     "CUSTOM_MODULE",
			DistFiles: m.distFiles,
		},
	}
}

func customModuleFactory() Module {
	module := &customModule{}
	InitAndroidModule(module)
@@ -76,3 +99,159 @@ func TestAndroidMkSingleton_PassesUpdatedAndroidMkDataToCustomCallback(t *testin
	assertEqual([]string{"baz"}, m.data.Host_required)
	assertEqual([]string{"qux"}, m.data.Target_required)
}

func TestGetDistForGoals(t *testing.T) {
	testCases := []struct {
		bp                     string
		expectedAndroidMkLines []string
	}{
		{
			bp: `
			custom {
				name: "foo",
				dist: {
					targets: ["my_goal"]
				}
			}
			`,
			expectedAndroidMkLines: []string{
				".PHONY: my_goal\n",
				"$(call dist-for-goals,my_goal,one.out:one.out)\n",
			},
		},
		{
			bp: `
			custom {
				name: "foo",
				dists: [
					{
						targets: ["my_goal"],
					},
					{
						targets: ["my_second_goal", "my_third_goal"],
					},
				],
			}
			`,
			expectedAndroidMkLines: []string{
				".PHONY: my_goal\n",
				"$(call dist-for-goals,my_goal,one.out:one.out)\n",
				".PHONY: my_second_goal my_third_goal\n",
				"$(call dist-for-goals,my_second_goal my_third_goal,one.out:one.out)\n",
			},
		},
		{
			bp: `
			custom {
				name: "foo",
				dist: {
					targets: ["my_goal"],
				},
				dists: [
					{
						targets: ["my_second_goal", "my_third_goal"],
					},
				],
			}
			`,
			expectedAndroidMkLines: []string{
				".PHONY: my_second_goal my_third_goal\n",
				"$(call dist-for-goals,my_second_goal my_third_goal,one.out:one.out)\n",
				".PHONY: my_goal\n",
				"$(call dist-for-goals,my_goal,one.out:one.out)\n",
			},
		},
		{
			bp: `
			custom {
				name: "foo",
				dist: {
					targets: ["my_goal", "my_other_goal"],
					tag: ".multiple",
				},
				dists: [
					{
						targets: ["my_second_goal"],
						tag: ".multiple",
					},
					{
						targets: ["my_third_goal"],
						dir: "test/dir",
					},
					{
						targets: ["my_fourth_goal"],
						suffix: ".suffix",
					},
					{
						targets: ["my_fifth_goal"],
						dest: "new-name",
					},
					{
						targets: ["my_sixth_goal"],
						dest: "new-name",
						dir: "some/dir",
						suffix: ".suffix",
					},
				],
			}
			`,
			expectedAndroidMkLines: []string{
				".PHONY: my_second_goal\n",
				"$(call dist-for-goals,my_second_goal,two.out:two.out)\n",
				"$(call dist-for-goals,my_second_goal,three/four.out:four.out)\n",
				".PHONY: my_third_goal\n",
				"$(call dist-for-goals,my_third_goal,one.out:test/dir/one.out)\n",
				".PHONY: my_fourth_goal\n",
				"$(call dist-for-goals,my_fourth_goal,one.out:one.suffix.out)\n",
				".PHONY: my_fifth_goal\n",
				"$(call dist-for-goals,my_fifth_goal,one.out:new-name)\n",
				".PHONY: my_sixth_goal\n",
				"$(call dist-for-goals,my_sixth_goal,one.out:some/dir/new-name.suffix)\n",
				".PHONY: my_goal my_other_goal\n",
				"$(call dist-for-goals,my_goal my_other_goal,two.out:two.out)\n",
				"$(call dist-for-goals,my_goal my_other_goal,three/four.out:four.out)\n",
			},
		},
	}

	for _, testCase := range testCases {
		config := TestConfig(buildDir, nil, testCase.bp, nil)
		config.inMake = true // Enable androidmk Singleton

		ctx := NewTestContext()
		ctx.RegisterSingletonType("androidmk", AndroidMkSingleton)
		ctx.RegisterModuleType("custom", customModuleFactory)
		ctx.Register(config)

		_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
		FailIfErrored(t, errs)
		_, errs = ctx.PrepareBuildActions(config)
		FailIfErrored(t, errs)

		module := ctx.ModuleForTests("foo", "").Module().(*customModule)
		entries := AndroidMkEntriesForTest(t, config, "", module)
		if len(entries) != 1 {
			t.Errorf("Expected a single AndroidMk entry, got %d", len(entries))
		}
		androidMkLines := entries[0].GetDistForGoals(module)

		if len(androidMkLines) != len(testCase.expectedAndroidMkLines) {
			t.Errorf(
				"Expected %d AndroidMk lines, got %d:\n%v",
				len(testCase.expectedAndroidMkLines),
				len(androidMkLines),
				androidMkLines,
			)
		}
		for idx, line := range androidMkLines {
			expectedLine := testCase.expectedAndroidMkLines[idx]
			if line != expectedLine {
				t.Errorf(
					"Expected AndroidMk line to be '%s', got '%s'",
					line,
					expectedLine,
				)
			}
		}
	}
}
+71 −16
Original line number Diff line number Diff line
@@ -301,6 +301,28 @@ func newPackageId(pkg string) qualifiedModuleName {
	return qualifiedModuleName{pkg: pkg, name: ""}
}

type Dist struct {
	// Copy the output of this module to the $DIST_DIR when `dist` is specified on the
	// command line and any of these targets are also on the command line, or otherwise
	// built
	Targets []string `android:"arch_variant"`

	// The name of the output artifact. This defaults to the basename of the output of
	// the module.
	Dest *string `android:"arch_variant"`

	// The directory within the dist directory to store the artifact. Defaults to the
	// top level directory ("").
	Dir *string `android:"arch_variant"`

	// A suffix to add to the artifact file name (before any extension).
	Suffix *string `android:"arch_variant"`

	// A string tag to select the OutputFiles associated with the tag. Defaults to the
	// the empty "" string.
	Tag *string `android:"arch_variant"`
}

type nameProperties struct {
	// The name of the module.  Must be unique across all modules.
	Name *string
@@ -440,23 +462,13 @@ type commonProperties struct {
	// relative path to a file to include in the list of notices for the device
	Notice *string `android:"path"`

	Dist struct {
		// copy the output of this module to the $DIST_DIR when `dist` is specified on the
		// command line and  any of these targets are also on the command line, or otherwise
		// built
		Targets []string `android:"arch_variant"`
	// configuration to distribute output files from this module to the distribution
	// directory (default: $OUT/dist, configurable with $DIST_DIR)
	Dist Dist `android:"arch_variant"`

		// The name of the output artifact. This defaults to the basename of the output of
		// the module.
		Dest *string `android:"arch_variant"`

		// The directory within the dist directory to store the artifact. Defaults to the
		// top level directory ("").
		Dir *string `android:"arch_variant"`

		// A suffix to add to the artifact file name (before any extension).
		Suffix *string `android:"arch_variant"`
	} `android:"arch_variant"`
	// a list of configurations to distribute output files from this module to the
	// distribution directory (default: $OUT/dist, configurable with $DIST_DIR)
	Dists []Dist `android:"arch_variant"`

	// The OsType of artifacts that this module variant is responsible for creating.
	//
@@ -523,6 +535,14 @@ type commonProperties struct {
	ImageVariation string `blueprint:"mutated"`
}

// A map of OutputFile tag keys to Paths, for disting purposes.
type TaggedDistFiles map[string]Paths

func MakeDefaultDistFiles(paths ...Path) TaggedDistFiles {
	// The default OutputFile tag is the empty "" string.
	return TaggedDistFiles{"": paths}
}

type hostAndDeviceProperties struct {
	// If set to true, build a variant of the module for the host.  Defaults to false.
	Host_supported *bool
@@ -801,6 +821,41 @@ func (m *ModuleBase) visibilityProperties() []visibilityProperty {
	return m.visibilityPropertyInfo
}

func (m *ModuleBase) Dists() []Dist {
	if len(m.commonProperties.Dist.Targets) > 0 {
		// Make a copy of the underlying Dists slice to protect against
		// backing array modifications with repeated calls to this method.
		distsCopy := append([]Dist(nil), m.commonProperties.Dists...)
		return append(distsCopy, m.commonProperties.Dist)
	} else {
		return m.commonProperties.Dists
	}
}

func (m *ModuleBase) GenerateTaggedDistFiles(ctx BaseModuleContext) TaggedDistFiles {
	distFiles := make(TaggedDistFiles)
	for _, dist := range m.Dists() {
		var tag string
		var distFilesForTag Paths
		if dist.Tag == nil {
			tag = ""
		} else {
			tag = *dist.Tag
		}
		distFilesForTag, err := m.base().module.(OutputFileProducer).OutputFiles(tag)
		if err != nil {
			ctx.PropertyErrorf("dist.tag", "%s", err.Error())
		}
		for _, distFile := range distFilesForTag {
			if distFile != nil && !distFiles[tag].containsPath(distFile) {
				distFiles[tag] = append(distFiles[tag], distFile)
			}
		}
	}

	return distFiles
}

func (m *ModuleBase) Target() Target {
	return m.commonProperties.CompileTarget
}
+9 −0
Original line number Diff line number Diff line
@@ -211,6 +211,15 @@ func (p OptionalPath) String() string {
// Paths is a slice of Path objects, with helpers to operate on the collection.
type Paths []Path

func (paths Paths) containsPath(path Path) bool {
	for _, p := range paths {
		if p == path {
			return true
		}
	}
	return false
}

// PathsForSource returns Paths rooted from SrcDir
func PathsForSource(ctx PathContext, paths []string) Paths {
	ret := make(Paths, len(paths))
+5 −2
Original line number Diff line number Diff line
@@ -241,7 +241,10 @@ func (library *libraryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries
		entries.Class = "HEADER_LIBRARIES"
	}

	entries.DistFile = library.distFile
	if library.distFile != nil {
		entries.DistFiles = android.MakeDefaultDistFiles(library.distFile)
	}

	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
		library.androidMkWriteExportedFlags(entries)
		library.androidMkEntriesWriteAdditionalDependenciesForSourceAbiDiff(entries)
@@ -310,7 +313,7 @@ func (binary *binaryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *a
	ctx.subAndroidMk(entries, binary.baseInstaller)

	entries.Class = "EXECUTABLES"
	entries.DistFile = binary.distFile
	entries.DistFiles = binary.distFiles
	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
		entries.SetString("LOCAL_SOONG_UNSTRIPPED_BINARY", binary.unstrippedOutputFile.String())
		if len(binary.symlinks) > 0 {
Loading