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

Commit 1db85407 authored by Nan Zhang's avatar Nan Zhang
Browse files

Remove timestamp based filelist file for tracking Python dependencies

Each Python module will generate a zip file containing source & data
files. The Python binary will collect all its dependencies and use
merge_zips to merge each zip file to create a final .par file.

Test: m -j checkbuild && real examples:
Bug: b/70568913

Change-Id: I9ff232d461d33e1c06026e7dcb5b124bf02c3ce5
parent 19e62e5e
Loading
Loading
Loading
Loading
+24 −3
Original line number Diff line number Diff line
@@ -63,6 +63,7 @@ var (
	zipsToNotStrip   = make(zipsToNotStripSet)
	stripDirEntries  = flag.Bool("D", false, "strip directory entries from the output zip file")
	manifest         = flag.String("m", "", "manifest file to insert in jar")
	pyMain           = flag.String("pm", "", "__main__.py file to insert in par")
	entrypoint       = flag.String("e", "", "par entrypoint file to insert in par")
	ignoreDuplicates = flag.Bool("ignore-duplicates", false, "take each entry from the first zip it exists in and don't warn")
)
@@ -75,7 +76,7 @@ func init() {

func main() {
	flag.Usage = func() {
		fmt.Fprintln(os.Stderr, "usage: merge_zips [-jpsD] [-m manifest] [-e entrypoint] output [inputs...]")
		fmt.Fprintln(os.Stderr, "usage: merge_zips [-jpsD] [-m manifest] [-e entrypoint] [-pm __main__.py] output [inputs...]")
		flag.PrintDefaults()
	}

@@ -125,8 +126,12 @@ func main() {
		log.Fatal(errors.New("must specify -p when specifying a entrypoint via -e"))
	}

	if *pyMain != "" && !*emulatePar {
		log.Fatal(errors.New("must specify -p when specifying a Python __main__.py via -pm"))
	}

	// do merge
	err = mergeZips(readers, writer, *manifest, *entrypoint, *sortEntries, *emulateJar, *emulatePar,
	err = mergeZips(readers, writer, *manifest, *entrypoint, *pyMain, *sortEntries, *emulateJar, *emulatePar,
		*stripDirEntries, *ignoreDuplicates)
	if err != nil {
		log.Fatal(err)
@@ -218,7 +223,7 @@ type fileMapping struct {
	source zipSource
}

func mergeZips(readers []namedZipReader, writer *zip.Writer, manifest, entrypoint string,
func mergeZips(readers []namedZipReader, writer *zip.Writer, manifest, entrypoint, pyMain string,
	sortEntries, emulateJar, emulatePar, stripDirEntries, ignoreDuplicates bool) error {

	sourceByDest := make(map[string]zipSource, 0)
@@ -268,6 +273,22 @@ func mergeZips(readers []namedZipReader, writer *zip.Writer, manifest, entrypoin
		addMapping("entry_point.txt", fileSource)
	}

	if pyMain != "" {
		buf, err := ioutil.ReadFile(pyMain)
		if err != nil {
			return err
		}
		fh := &zip.FileHeader{
			Name:               "__main__.py",
			Method:             zip.Store,
			UncompressedSize64: uint64(len(buf)),
		}
		fh.SetMode(0700)
		fh.SetModTime(jar.DefaultTime)
		fileSource := bufferEntry{fh, buf}
		addMapping("__main__.py", fileSource)
	}

	if emulatePar {
		// the runfiles packages needs to be populated with "__init__.py".
		newPyPkgs := []string{}
+14 −88
Original line number Diff line number Diff line
@@ -18,8 +18,6 @@ package python

import (
	"fmt"
	"path/filepath"
	"strings"

	"android/soong/android"
)
@@ -80,89 +78,44 @@ func (binary *binaryDecorator) bootstrapperProps() []interface{} {
	return []interface{}{&binary.binaryProperties}
}

func (binary *binaryDecorator) bootstrap(ctx android.ModuleContext, actual_version string,
	embedded_launcher bool, srcsPathMappings []pathMapping, parSpec parSpec,
	depsPyRunfiles []string, depsParSpecs []parSpec) android.OptionalPath {
	// no Python source file for compiling .par file.
	if len(srcsPathMappings) == 0 {
		return android.OptionalPath{}
	}

	// the runfiles packages needs to be populated with "__init__.py".
	newPyPkgs := []string{}
	// the set to de-duplicate the new Python packages above.
	newPyPkgSet := make(map[string]bool)
	// the runfiles dirs have been treated as packages.
	existingPyPkgSet := make(map[string]bool)

	wholePyRunfiles := []string{}
	for _, path := range srcsPathMappings {
		wholePyRunfiles = append(wholePyRunfiles, path.dest)
	}
	wholePyRunfiles = append(wholePyRunfiles, depsPyRunfiles...)

	// find all the runfiles dirs which have been treated as packages.
	for _, path := range wholePyRunfiles {
		if filepath.Base(path) != initFileName {
			continue
		}
		existingPyPkg := PathBeforeLastSlash(path)
		if _, found := existingPyPkgSet[existingPyPkg]; found {
			panic(fmt.Errorf("found init file path duplicates: %q for module: %q.",
				path, ctx.ModuleName()))
		} else {
			existingPyPkgSet[existingPyPkg] = true
		}
		parentPath := PathBeforeLastSlash(existingPyPkg)
		populateNewPyPkgs(parentPath, existingPyPkgSet, newPyPkgSet, &newPyPkgs)
	}

	// create new packages under runfiles tree.
	for _, path := range wholePyRunfiles {
		if filepath.Base(path) == initFileName {
			continue
		}
		parentPath := PathBeforeLastSlash(path)
		populateNewPyPkgs(parentPath, existingPyPkgSet, newPyPkgSet, &newPyPkgs)
	}
func (binary *binaryDecorator) bootstrap(ctx android.ModuleContext, actualVersion string,
	embeddedLauncher bool, srcsPathMappings []pathMapping, srcsZip android.Path,
	depsSrcsZips android.Paths) android.OptionalPath {

	main := binary.getPyMainFile(ctx, srcsPathMappings)
	if main == "" {
		return android.OptionalPath{}
	}

	var launcher_path android.Path
	if embedded_launcher {
	var launcherPath android.Path
	if embeddedLauncher {
		ctx.VisitDirectDepsWithTag(launcherTag, func(m android.Module) {
			if provider, ok := m.(IntermPathProvider); ok {
				if launcher_path != nil {
				if launcherPath != nil {
					panic(fmt.Errorf("launcher path was found before: %q",
						launcher_path))
						launcherPath))
				}
				launcher_path = provider.IntermPathForModuleOut().Path()
				launcherPath = provider.IntermPathForModuleOut().Path()
			}
		})
	}

	binFile := registerBuildActionForParFile(ctx, embedded_launcher, launcher_path,
		binary.getHostInterpreterName(ctx, actual_version),
		main, binary.getStem(ctx), newPyPkgs, append(depsParSpecs, parSpec))
	binFile := registerBuildActionForParFile(ctx, embeddedLauncher, launcherPath,
		binary.getHostInterpreterName(ctx, actualVersion),
		main, binary.getStem(ctx), append(android.Paths{srcsZip}, depsSrcsZips...))

	return android.OptionalPathForPath(binFile)
}

// get host interpreter name.
func (binary *binaryDecorator) getHostInterpreterName(ctx android.ModuleContext,
	actual_version string) string {
	actualVersion string) string {
	var interp string
	switch actual_version {
	switch actualVersion {
	case pyVersion2:
		interp = "python2.7"
	case pyVersion3:
		interp = "python3"
	default:
		panic(fmt.Errorf("unknown Python actualVersion: %q for module: %q.",
			actual_version, ctx.ModuleName()))
			actualVersion, ctx.ModuleName()))
	}

	return interp
@@ -196,30 +149,3 @@ func (binary *binaryDecorator) getStem(ctx android.ModuleContext) string {

	return stem + String(binary.binaryProperties.Suffix)
}

// Sets the given directory and all its ancestor directories as Python packages.
func populateNewPyPkgs(pkgPath string, existingPyPkgSet,
	newPyPkgSet map[string]bool, newPyPkgs *[]string) {
	for pkgPath != "" {
		if _, found := existingPyPkgSet[pkgPath]; found {
			break
		}
		if _, found := newPyPkgSet[pkgPath]; !found {
			newPyPkgSet[pkgPath] = true
			*newPyPkgs = append(*newPyPkgs, pkgPath)
			// Gets its ancestor directory by trimming last slash.
			pkgPath = PathBeforeLastSlash(pkgPath)
		} else {
			break
		}
	}
}

// filepath.Dir("abc") -> "." and filepath.Dir("/abc") -> "/". However,
// the PathBeforeLastSlash() will return "" for both cases above.
func PathBeforeLastSlash(path string) string {
	if idx := strings.LastIndex(path, "/"); idx != -1 {
		return path[:idx]
	}
	return ""
}
+42 −105
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@ package python
// This file contains Ninja build actions for building Python program.

import (
	"fmt"
	"strings"

	"android/soong/android"
@@ -29,25 +28,30 @@ import (
var (
	pctx = android.NewPackageContext("android/soong/python")

	host_par = pctx.AndroidStaticRule("host_par",
	zip = pctx.AndroidStaticRule("zip",
		blueprint.RuleParams{
			Command: `touch $initFile && ` +
				`sed -e 's/%interpreter%/$interp/g' -e 's/%main%/$main/g' $template > $stub && ` +
				`$parCmd -o $parFile $parArgs && echo '#!/usr/bin/env python' | cat - $parFile > $out && ` +
				`chmod +x $out && (rm -f $initFile; rm -f $stub; rm -f $parFile)`,
			CommandDeps: []string{"$parCmd", "$template"},
			Command:     `$parCmd -o $out $args`,
			CommandDeps: []string{"$parCmd"},
		},
		"initFile", "interp", "main", "template", "stub", "parCmd", "parFile", "parArgs")
		"args")

	embedded_par = pctx.AndroidStaticRule("embedded_par",
	hostPar = pctx.AndroidStaticRule("hostPar",
		blueprint.RuleParams{
			Command: `touch $initFile && ` +
				`echo '$main' > $entry_point && ` +
				`$parCmd -o $parFile $parArgs && cat $launcher | cat - $parFile > $out && ` +
				`chmod +x $out && (rm -f $initFile; rm -f $entry_point; rm -f $parFile)`,
			CommandDeps: []string{"$parCmd"},
			Command: `sed -e 's/%interpreter%/$interp/g' -e 's/%main%/$main/g' $template > $stub && ` +
				`$mergeParCmd -p -pm $stub $mergedZip $srcsZips && echo '#!/usr/bin/env python' | cat - $mergedZip > $out && ` +
				`chmod +x $out && (rm -f $stub; rm -f $mergedZip)`,
			CommandDeps: []string{"$mergeParCmd"},
		},
		"interp", "main", "template", "stub", "mergedZip", "srcsZips")

	embeddedPar = pctx.AndroidStaticRule("embeddedPar",
		blueprint.RuleParams{
			Command: `echo '$main' > $entryPoint &&` +
				`$mergeParCmd -p -e $entryPoint $mergedZip $srcsZips && cat $launcher | cat - $mergedZip > $out && ` +
				`chmod +x $out && (rm -f $entryPoint; rm -f $mergedZip)`,
			CommandDeps: []string{"$mergeParCmd"},
		},
		"initFile", "main", "entry_point", "parCmd", "parFile", "parArgs", "launcher")
		"main", "entryPoint", "mergedZip", "srcsZips", "launcher")
)

func init() {
@@ -55,98 +59,36 @@ func init() {
	pctx.Import("android/soong/common")

	pctx.HostBinToolVariable("parCmd", "soong_zip")
	pctx.HostBinToolVariable("mergeParCmd", "merge_zips")
}

type fileListSpec struct {
	fileList     android.Path
	relativeRoot string
}

type parSpec struct {
	rootPrefix string

	fileListSpecs []fileListSpec
}
func registerBuildActionForParFile(ctx android.ModuleContext, embeddedLauncher bool,
	launcherPath android.Path, interpreter, main, binName string,
	srcsZips android.Paths) android.Path {

func (p parSpec) soongParArgs() string {
	ret := `-P ` + p.rootPrefix

	for _, spec := range p.fileListSpecs {
		ret += ` -C ` + spec.relativeRoot + ` -l ` + spec.fileList.String()
	}

	return ret
}

func registerBuildActionForModuleFileList(ctx android.ModuleContext,
	name string, files android.Paths) android.Path {
	fileList := android.PathForModuleOut(ctx, name+".list")

	content := []string{}
	for _, file := range files {
		content = append(content, file.String())
	}

	ctx.Build(pctx, android.BuildParams{
		Rule:        android.WriteFile,
		Description: "generate " + fileList.Rel(),
		Output:      fileList,
		Implicits:   files,
		Args: map[string]string{
			"content": strings.Join(content, `\n`),
		},
	})

	return fileList
}

func registerBuildActionForParFile(ctx android.ModuleContext, embedded_launcher bool,
	launcher_path android.Path, interpreter, main, binName string,
	newPyPkgs []string, parSpecs []parSpec) android.Path {

	// .intermediate output path for __init__.py
	initFile := android.PathForModuleOut(ctx, initFileName).String()

	// .intermediate output path for par file.
	parFile := android.PathForModuleOut(ctx, binName+parFileExt)
	// .intermediate output path for merged zip file.
	mergedZip := android.PathForModuleOut(ctx, binName+".mergedzip")

	// .intermediate output path for bin executable.
	binFile := android.PathForModuleOut(ctx, binName)

	// implicit dependency for parFile build action.
	implicits := android.Paths{}
	for _, p := range parSpecs {
		for _, f := range p.fileListSpecs {
			implicits = append(implicits, f.fileList)
		}
	}

	parArgs := []string{}
	parArgs = append(parArgs, `-P "" `+`-C `+strings.TrimSuffix(initFile, initFileName)+` -f `+initFile)
	for _, pkg := range newPyPkgs {
		parArgs = append(parArgs, `-P `+pkg+` -f `+initFile)
	}
	for _, p := range parSpecs {
		parArgs = append(parArgs, p.soongParArgs())
	}
	implicits := srcsZips

	if !embedded_launcher {
	if !embeddedLauncher {
		// the path of stub_template_host.txt from source tree.
		template := android.PathForSource(ctx, stubTemplateHost)
		implicits = append(implicits, template)

		// intermediate output path for __main__.py
		stub := android.PathForModuleOut(ctx, mainFileName).String()

		// added stub file to the soong_zip args.
		parArgs = append(parArgs, `-P "" `+`-C `+strings.TrimSuffix(stub, mainFileName)+` -f `+stub)

		ctx.Build(pctx, android.BuildParams{
			Rule:        host_par,
			Rule:        hostPar,
			Description: "host python archive",
			Output:      binFile,
			Implicits:   implicits,
			Args: map[string]string{
				"initFile": initFile,
				"interp": strings.Replace(interpreter, "/", `\/`, -1),
				// we need remove "runfiles/" suffix since stub script starts
				// searching for main file in each sub-dir of "runfiles" directory tree.
@@ -154,33 +96,28 @@ func registerBuildActionForParFile(ctx android.ModuleContext, embedded_launcher
					"/", `\/`, -1),
				"template":  template.String(),
				"stub":      stub,
				"parFile":  parFile.String(),
				"parArgs":  strings.Join(parArgs, " "),
				"mergedZip": mergedZip.String(),
				"srcsZips":  strings.Join(srcsZips.Strings(), " "),
			},
		})
	} else {
		// added launcher_path to the implicits Ninja dependencies.
		implicits = append(implicits, launcher_path)
		// added launcherPath to the implicits Ninja dependencies.
		implicits = append(implicits, launcherPath)

		// .intermediate output path for entry_point.txt
		entryPoint := android.PathForModuleOut(ctx, entryPointFile).String()

		// added entry_point file to the soong_zip args.
		parArgs = append(parArgs, `-P "" `+`-C `+fmt.Sprintf(
			"%q", strings.TrimSuffix(entryPoint, entryPointFile))+` -f `+entryPoint)

		ctx.Build(pctx, android.BuildParams{
			Rule:        embedded_par,
			Rule:        embeddedPar,
			Description: "embedded python archive",
			Output:      binFile,
			Implicits:   implicits,
			Args: map[string]string{
				"initFile":    initFile,
				"main":       main,
				"entry_point": entryPoint,
				"parFile":     parFile.String(),
				"parArgs":     strings.Join(parArgs, " "),
				"launcher":    launcher_path.String(),
				"entryPoint": entryPoint,
				"mergedZip":  mergedZip.String(),
				"srcsZips":   strings.Join(srcsZips.Strings(), " "),
				"launcher":   launcherPath.String(),
			},
		})
	}
+51 −49
Original line number Diff line number Diff line
@@ -132,18 +132,15 @@ type Module struct {
	// pathMapping: <dest: runfile_path, src: source_path>
	dataPathMappings []pathMapping

	// soong_zip arguments of all its dependencies.
	depsParSpecs []parSpec
	// the zip filepath for zipping current module source/data files.
	srcsZip android.Path

	// Python runfiles paths of all its dependencies.
	depsPyRunfiles []string
	// dependency modules' zip filepath for zipping current module source/data files.
	depsSrcsZips android.Paths

	// (.intermediate) module output path as installation source.
	installSource android.OptionalPath

	// the soong_zip arguments for zipping current module source/data files.
	parSpec parSpec

	subAndroidMkOnce map[subAndroidMkProvider]bool
}

@@ -156,9 +153,9 @@ func newModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Mo

type bootstrapper interface {
	bootstrapperProps() []interface{}
	bootstrap(ctx android.ModuleContext, Actual_version string, embedded_launcher bool,
		srcsPathMappings []pathMapping, parSpec parSpec,
		depsPyRunfiles []string, depsParSpecs []parSpec) android.OptionalPath
	bootstrap(ctx android.ModuleContext, ActualVersion string, embeddedLauncher bool,
		srcsPathMappings []pathMapping, srcsZip android.Path,
		depsSrcsZips android.Paths) android.OptionalPath
}

type installer interface {
@@ -168,7 +165,7 @@ type installer interface {
type PythonDependency interface {
	GetSrcsPathMappings() []pathMapping
	GetDataPathMappings() []pathMapping
	GetParSpec() parSpec
	GetSrcsZip() android.Path
}

func (p *Module) GetSrcsPathMappings() []pathMapping {
@@ -179,8 +176,8 @@ func (p *Module) GetDataPathMappings() []pathMapping {
	return p.dataPathMappings
}

func (p *Module) GetParSpec() parSpec {
	return p.parSpec
func (p *Module) GetSrcsZip() android.Path {
	return p.srcsZip
}

var _ PythonDependency = (*Module)(nil)
@@ -339,13 +336,12 @@ func (p *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) {
	if p.bootstrapper != nil {
		// TODO(nanzhang): Since embedded launcher is not supported for Python3 for now,
		// so we initialize "embedded_launcher" to false.
		embedded_launcher := false
		embeddedLauncher := false
		if p.properties.Actual_version == pyVersion2 {
			embedded_launcher = p.isEmbeddedLauncherEnabled(pyVersion2)
			embeddedLauncher = p.isEmbeddedLauncherEnabled(pyVersion2)
		}
		p.installSource = p.bootstrapper.bootstrap(ctx, p.properties.Actual_version,
			embedded_launcher, p.srcsPathMappings, p.parSpec, p.depsPyRunfiles,
			p.depsParSpecs)
			embeddedLauncher, p.srcsPathMappings, p.srcsZip, p.depsSrcsZips)
	}

	if p.installer != nil && p.installSource.Valid() {
@@ -378,11 +374,11 @@ func (p *Module) GeneratePythonBuildActions(ctx android.ModuleContext) {
	expandedData := ctx.ExpandSources(p.properties.Data, nil)

	// sanitize pkg_path.
	pkg_path := String(p.properties.Pkg_path)
	if pkg_path != "" {
		pkg_path = filepath.Clean(String(p.properties.Pkg_path))
		if pkg_path == ".." || strings.HasPrefix(pkg_path, "../") ||
			strings.HasPrefix(pkg_path, "/") {
	pkgPath := String(p.properties.Pkg_path)
	if pkgPath != "" {
		pkgPath = filepath.Clean(String(p.properties.Pkg_path))
		if pkgPath == ".." || strings.HasPrefix(pkgPath, "../") ||
			strings.HasPrefix(pkgPath, "/") {
			ctx.PropertyErrorf("pkg_path",
				"%q must be a relative path contained in par file.",
				String(p.properties.Pkg_path))
@@ -390,31 +386,31 @@ func (p *Module) GeneratePythonBuildActions(ctx android.ModuleContext) {
		}
		if p.properties.Is_internal != nil && *p.properties.Is_internal {
			// pkg_path starts from "internal/" implicitly.
			pkg_path = filepath.Join(internal, pkg_path)
			pkgPath = filepath.Join(internal, pkgPath)
		} else {
			// pkg_path starts from "runfiles/" implicitly.
			pkg_path = filepath.Join(runFiles, pkg_path)
			pkgPath = filepath.Join(runFiles, pkgPath)
		}
	} else {
		if p.properties.Is_internal != nil && *p.properties.Is_internal {
			// pkg_path starts from "runfiles/" implicitly.
			pkg_path = internal
			pkgPath = internal
		} else {
			// pkg_path starts from "runfiles/" implicitly.
			pkg_path = runFiles
			pkgPath = runFiles
		}
	}

	p.genModulePathMappings(ctx, pkg_path, expandedSrcs, expandedData)

	p.parSpec = p.dumpFileList(ctx, pkg_path)
	p.genModulePathMappings(ctx, pkgPath, expandedSrcs, expandedData)

	p.uniqWholeRunfilesTree(ctx)

	p.srcsZip = p.createSrcsZip(ctx, pkgPath)
}

// generate current module unique pathMappings: <dest: runfiles_path, src: source_path>
// for python/data files.
func (p *Module) genModulePathMappings(ctx android.ModuleContext, pkg_path string,
func (p *Module) genModulePathMappings(ctx android.ModuleContext, pkgPath string,
	expandedSrcs, expandedData android.Paths) {
	// fetch <runfiles_path, source_path> pairs from "src" and "data" properties to
	// check duplicates.
@@ -426,7 +422,7 @@ func (p *Module) genModulePathMappings(ctx android.ModuleContext, pkg_path strin
			ctx.PropertyErrorf("srcs", "found non (.py) file: %q!", s.String())
			continue
		}
		runfilesPath := filepath.Join(pkg_path, s.Rel())
		runfilesPath := filepath.Join(pkgPath, s.Rel())
		identifiers := strings.Split(strings.TrimSuffix(runfilesPath, pyExt), "/")
		for _, token := range identifiers {
			if !pyIdentifierRegexp.MatchString(token) {
@@ -445,7 +441,7 @@ func (p *Module) genModulePathMappings(ctx android.ModuleContext, pkg_path strin
			ctx.PropertyErrorf("data", "found (.py) file: %q!", d.String())
			continue
		}
		runfilesPath := filepath.Join(pkg_path, d.Rel())
		runfilesPath := filepath.Join(pkgPath, d.Rel())
		if fillInMap(ctx, destToPyData, runfilesPath, d.String(), p.Name(), p.Name()) {
			p.dataPathMappings = append(p.dataPathMappings,
				pathMapping{dest: runfilesPath, src: d})
@@ -454,12 +450,9 @@ func (p *Module) genModulePathMappings(ctx android.ModuleContext, pkg_path strin

}

// register build actions to dump filelist to disk.
func (p *Module) dumpFileList(ctx android.ModuleContext, pkg_path string) parSpec {
// register build actions to zip current module's sources.
func (p *Module) createSrcsZip(ctx android.ModuleContext, pkgPath string) android.Path {
	relativeRootMap := make(map[string]android.Paths)
	// the soong_zip params in order to pack current module's Python/data files.
	ret := parSpec{rootPrefix: pkg_path}

	pathMappings := append(p.srcsPathMappings, p.dataPathMappings...)

	// "srcs" or "data" properties may have filegroup so it might happen that
@@ -482,15 +475,29 @@ func (p *Module) dumpFileList(ctx android.ModuleContext, pkg_path string) parSpe
	}
	sort.Strings(keys)

	parArgs := []string{}
	parArgs = append(parArgs, `-P `+pkgPath)
	implicits := android.Paths{}
	for _, k := range keys {
		// use relative root as filelist name.
		fileListPath := registerBuildActionForModuleFileList(
			ctx, strings.Replace(k, "/", "_", -1), relativeRootMap[k])
		ret.fileListSpecs = append(ret.fileListSpecs,
			fileListSpec{fileList: fileListPath, relativeRoot: k})
	}
		parArgs = append(parArgs, `-C `+k)
		for _, path := range relativeRootMap[k] {
			parArgs = append(parArgs, `-f `+path.String())
			implicits = append(implicits, path)
		}
	}

	srcsZip := android.PathForModuleOut(ctx, ctx.ModuleName()+".zip")
	ctx.Build(pctx, android.BuildParams{
		Rule:        zip,
		Description: "python library archive",
		Output:      srcsZip,
		Implicits:   implicits,
		Args: map[string]string{
			"args": strings.Join(parArgs, " "),
		},
	})

	return ret
	return srcsZip
}

func isPythonLibModule(module blueprint.Module) bool {
@@ -537,9 +544,6 @@ func (p *Module) uniqWholeRunfilesTree(ctx android.ModuleContext) {
					ctx.OtherModuleName(module)) {
					continue
				}
				// binary needs the Python runfiles paths from all its
				// dependencies to fill __init__.py in each runfiles dir.
				p.depsPyRunfiles = append(p.depsPyRunfiles, path.dest)
			}
			data := dep.GetDataPathMappings()
			for _, path := range data {
@@ -547,9 +551,7 @@ func (p *Module) uniqWholeRunfilesTree(ctx android.ModuleContext) {
					path.dest, path.src.String(), ctx.ModuleName(),
					ctx.OtherModuleName(module))
			}
			// binary needs the soong_zip arguments from all its
			// dependencies to generate executable par file.
			p.depsParSpecs = append(p.depsParSpecs, dep.GetParSpec())
			p.depsSrcsZips = append(p.depsSrcsZips, dep.GetSrcsZip())
		}
	})
}
+26 −42
Original line number Diff line number Diff line
@@ -32,9 +32,8 @@ type pyModule struct {
	name          string
	actualVersion string
	pyRunfiles    []string
	depsPyRunfiles []string
	parSpec        string
	depsParSpecs   []string
	srcsZip       string
	depsSrcsZips  []string
}

var (
@@ -313,14 +312,10 @@ var (
						"runfiles/e/default_py3.py",
						"runfiles/e/file4.py",
					},
					depsPyRunfiles: []string{
						"runfiles/a/b/file1.py",
						"runfiles/c/d/file2.py",
					},
					parSpec: "-P runfiles/e -C dir/ -l @prefix@/.intermediates/dir/bin/PY3/dir_.list",
					depsParSpecs: []string{
						"-P runfiles/a/b -C dir/ -l @prefix@/.intermediates/dir/lib5/PY3/dir_.list",
						"-P runfiles/c/d -C dir/ -l @prefix@/.intermediates/dir/lib6/PY3/dir_.list",
					srcsZip: "@prefix@/.intermediates/dir/bin/PY3/bin.zip",
					depsSrcsZips: []string{
						"@prefix@/.intermediates/dir/lib5/PY3/lib5.zip",
						"@prefix@/.intermediates/dir/lib6/PY3/lib6.zip",
					},
				},
			},
@@ -356,8 +351,9 @@ func TestPythonModule(t *testing.T) {
					testErrs = append(testErrs,
						expectModule(t, ctx, buildDir, e.name,
							e.actualVersion,
							e.pyRunfiles, e.depsPyRunfiles,
							e.parSpec, e.depsParSpecs)...)
							e.srcsZip,
							e.pyRunfiles,
							e.depsSrcsZips)...)
				}
			}
			fail(t, testErrs)
@@ -388,9 +384,8 @@ func expectErrors(t *testing.T, actErrs []error, expErrs []string) (testErrs []e
	return
}

func expectModule(t *testing.T, ctx *android.TestContext, buildDir, name, variant string,
	expPyRunfiles, expDepsPyRunfiles []string,
	expParSpec string, expDepsParSpecs []string) (testErrs []error) {
func expectModule(t *testing.T, ctx *android.TestContext, buildDir, name, variant, expectedSrcsZip string,
	expectedPyRunfiles, expectedDepsSrcsZips []string) (testErrs []error) {
	module := ctx.ModuleForTests(name, variant)

	base, baseOk := module.Module().(*Module)
@@ -398,47 +393,36 @@ func expectModule(t *testing.T, ctx *android.TestContext, buildDir, name, varian
		t.Fatalf("%s is not Python module!", name)
	}

	actPyRunfiles := []string{}
	actualPyRunfiles := []string{}
	for _, path := range base.srcsPathMappings {
		actPyRunfiles = append(actPyRunfiles, path.dest)
		actualPyRunfiles = append(actualPyRunfiles, path.dest)
	}

	if !reflect.DeepEqual(actPyRunfiles, expPyRunfiles) {
	if !reflect.DeepEqual(actualPyRunfiles, expectedPyRunfiles) {
		testErrs = append(testErrs, errors.New(fmt.Sprintf(
			`binary "%s" variant "%s" has unexpected pyRunfiles: %q!`,
			base.Name(),
			base.properties.Actual_version,
			actPyRunfiles)))
			actualPyRunfiles)))
	}

	if !reflect.DeepEqual(base.depsPyRunfiles, expDepsPyRunfiles) {
	if base.srcsZip.String() != strings.Replace(expectedSrcsZip, "@prefix@", buildDir, 1) {
		testErrs = append(testErrs, errors.New(fmt.Sprintf(
			`binary "%s" variant "%s" has unexpected depsPyRunfiles: %q!`,
			`binary "%s" variant "%s" has unexpected srcsZip: %q!`,
			base.Name(),
			base.properties.Actual_version,
			base.depsPyRunfiles)))
			base.srcsZip)))
	}

	if base.parSpec.soongParArgs() != strings.Replace(expParSpec, "@prefix@", buildDir, 1) {
		testErrs = append(testErrs, errors.New(fmt.Sprintf(
			`binary "%s" variant "%s" has unexpected parSpec: %q!`,
			base.Name(),
			base.properties.Actual_version,
			base.parSpec.soongParArgs())))
	for i, _ := range expectedDepsSrcsZips {
		expectedDepsSrcsZips[i] = strings.Replace(expectedDepsSrcsZips[i], "@prefix@", buildDir, 1)
	}

	actDepsParSpecs := []string{}
	for i, p := range base.depsParSpecs {
		actDepsParSpecs = append(actDepsParSpecs, p.soongParArgs())
		expDepsParSpecs[i] = strings.Replace(expDepsParSpecs[i], "@prefix@", buildDir, 1)
	}

	if !reflect.DeepEqual(actDepsParSpecs, expDepsParSpecs) {
	if !reflect.DeepEqual(base.depsSrcsZips.Strings(), expectedDepsSrcsZips) {
		testErrs = append(testErrs, errors.New(fmt.Sprintf(
			`binary "%s" variant "%s" has unexpected depsParSpecs: %q!`,
			`binary "%s" variant "%s" has unexpected depsSrcsZips: %q!`,
			base.Name(),
			base.properties.Actual_version,
			actDepsParSpecs)))
			base.depsSrcsZips)))
	}

	return