diff options
Diffstat (limited to 'python')
-rw-r--r-- | python/binary.go | 6 | ||||
-rw-r--r-- | python/python.go | 93 | ||||
-rw-r--r-- | python/scripts/main.py | 38 |
3 files changed, 136 insertions, 1 deletions
diff --git a/python/binary.go b/python/binary.go index af7d30faa..f894299f9 100644 --- a/python/binary.go +++ b/python/binary.go @@ -125,6 +125,7 @@ func (p *PythonBinaryModule) GenerateAndroidBuildActions(ctx android.ModuleConte func (p *PythonBinaryModule) buildBinary(ctx android.ModuleContext) { embeddedLauncher := p.isEmbeddedLauncherEnabled() depsSrcsZips := p.collectPathsFromTransitiveDeps(ctx, embeddedLauncher) + bundleSharedLibs := p.collectSharedLibDeps(ctx) main := "" if p.autorun() { main = p.getPyMainFile(ctx, p.srcsPathMappings) @@ -149,6 +150,11 @@ func (p *PythonBinaryModule) buildBinary(ctx android.ModuleContext) { srcsZips = append(srcsZips, p.srcsZip) } srcsZips = append(srcsZips, depsSrcsZips...) + if ctx.Host() && len(bundleSharedLibs) > 0 { + // only bundle shared libs for host binaries + sharedLibZip := p.zipSharedLibs(ctx, bundleSharedLibs) + srcsZips = append(srcsZips, sharedLibZip) + } p.installSource = registerBuildActionForParFile(ctx, embeddedLauncher, launcherPath, "python3", main, p.getStem(ctx), srcsZips) diff --git a/python/python.go b/python/python.go index 10c11ada0..e2786b8d6 100644 --- a/python/python.go +++ b/python/python.go @@ -20,11 +20,13 @@ import ( "fmt" "path/filepath" "regexp" + "sort" "strings" "android/soong/cc" "github.com/google/blueprint" + "github.com/google/blueprint/depset" "github.com/google/blueprint/proptools" "android/soong/android" @@ -36,6 +38,7 @@ type PythonLibraryInfo struct { SrcsZip android.Path PrecompiledSrcsZip android.Path PkgPath string + BundleSharedLibs android.Paths } var PythonLibraryInfoProvider = blueprint.NewProvider[PythonLibraryInfo]() @@ -105,6 +108,10 @@ type BaseProperties struct { // list of the Python libraries compatible both with Python2 and Python3. Libs []string `android:"arch_variant"` + // TODO: b/403060602 - add unit tests for this property and related code + // list of shared libraries that should be packaged with the python code for this module. + Shared_libs []string `android:"arch_variant"` + Version struct { // Python2-specific properties, including whether Python2 is supported for this module // and version-specific sources, exclusions and dependencies. @@ -158,6 +165,10 @@ type PythonLibraryModule struct { precompiledSrcsZip android.Path sourceProperties android.SourceProperties + + // The shared libraries that should be bundled with the python code for + // any standalone python binaries that depend on this module. + bundleSharedLibs android.Paths } // newModule generates new Python base module @@ -197,6 +208,10 @@ func (p *PythonLibraryModule) getBaseProperties() *BaseProperties { return &p.properties } +func (p *PythonLibraryModule) getBundleSharedLibs() android.Paths { + return p.bundleSharedLibs +} + func (p *PythonLibraryModule) init() android.Module { p.AddProperties(&p.properties, &p.protoProperties, &p.sourceProperties) android.InitAndroidArchModule(p, p.hod, p.multilib) @@ -224,6 +239,7 @@ type installDependencyTag struct { var ( pythonLibTag = dependencyTag{name: "pythonLib"} javaDataTag = dependencyTag{name: "javaData"} + sharedLibTag = dependencyTag{name: "sharedLib"} // The python interpreter, with soong module name "py3-launcher" or "py3-launcher-autorun". launcherTag = dependencyTag{name: "launcher"} launcherSharedLibTag = installDependencyTag{name: "launcherSharedLib"} @@ -288,6 +304,12 @@ func (p *PythonLibraryModule) DepsMutator(ctx android.BottomUpMutatorContext) { javaDataVariation := []blueprint.Variation{{"arch", android.Common.String()}} ctx.AddVariationDependencies(javaDataVariation, javaDataTag, p.properties.Java_data...) + if ctx.Host() { + ctx.AddVariationDependencies(ctx.Config().BuildOSTarget.Variations(), sharedLibTag, p.properties.Shared_libs...) + } else if len(p.properties.Shared_libs) > 0 { + ctx.PropertyErrorf("shared_libs", "shared_libs is not supported for device builds") + } + p.AddDepsOnPythonLauncherAndStdlib(ctx, hostStdLibTag, hostLauncherTag, hostlauncherSharedLibTag, false, ctx.Config().BuildOSTarget) } @@ -377,6 +399,25 @@ func (p *PythonLibraryModule) GenerateAndroidBuildActions(ctx android.ModuleCont expandedData = append(expandedData, android.OutputFilesForModule(ctx, javaData, "")...) } + var directImplementationDeps android.Paths + var transitiveImplementationDeps []depset.DepSet[android.Path] + ctx.VisitDirectDepsProxyWithTag(sharedLibTag, func(dep android.ModuleProxy) { + sharedLibInfo, _ := android.OtherModuleProvider(ctx, dep, cc.SharedLibraryInfoProvider) + if sharedLibInfo.SharedLibrary != nil { + expandedData = append(expandedData, android.OutputFilesForModule(ctx, dep, "")...) + directImplementationDeps = append(directImplementationDeps, android.OutputFilesForModule(ctx, dep, "")...) + if info, ok := android.OtherModuleProvider(ctx, dep, cc.ImplementationDepInfoProvider); ok { + transitiveImplementationDeps = append(transitiveImplementationDeps, info.ImplementationDeps) + p.bundleSharedLibs = append(p.bundleSharedLibs, info.ImplementationDeps.ToList()...) + } + } else { + ctx.PropertyErrorf("shared_libs", "%q of type %q is not supported", dep.Name(), ctx.OtherModuleType(dep)) + } + }) + android.SetProvider(ctx, cc.ImplementationDepInfoProvider, &cc.ImplementationDepInfo{ + ImplementationDeps: depset.New(depset.PREORDER, directImplementationDeps, transitiveImplementationDeps), + }) + // Validate pkg_path property pkgPath := String(p.properties.Pkg_path) if pkgPath != "" { @@ -408,6 +449,7 @@ func (p *PythonLibraryModule) GenerateAndroidBuildActions(ctx android.ModuleCont SrcsZip: p.getSrcsZip(), PkgPath: p.getPkgPath(), PrecompiledSrcsZip: p.getPrecompiledSrcsZip(), + BundleSharedLibs: p.getBundleSharedLibs(), }) } @@ -684,6 +726,57 @@ func (p *PythonLibraryModule) collectPathsFromTransitiveDeps(ctx android.ModuleC return result } +func (p *PythonLibraryModule) collectSharedLibDeps(ctx android.ModuleContext) android.Paths { + seen := make(map[android.Module]bool) + + var result android.Paths + + ctx.WalkDepsProxy(func(child, _ android.ModuleProxy) bool { + // we only collect dependencies tagged as python library deps + if ctx.OtherModuleDependencyTag(child) != pythonLibTag { + return false + } + if seen[child] { + return false + } + seen[child] = true + dep, isLibrary := android.OtherModuleProvider(ctx, child, PythonLibraryInfoProvider) + if isLibrary { + result = append(result, dep.BundleSharedLibs...) + } + return true + }) + return result +} + +func (p *PythonLibraryModule) zipSharedLibs(ctx android.ModuleContext, bundleSharedLibs android.Paths) android.Path { + // sort the paths to keep the output deterministic + sort.Slice(bundleSharedLibs, func(i, j int) bool { + return bundleSharedLibs[i].String() < bundleSharedLibs[j].String() + }) + + parArgs := []string{"-symlinks=false", "-P lib64"} + paths := android.Paths{} + for _, path := range bundleSharedLibs { + // specify relative root of file in following -f arguments + parArgs = append(parArgs, `-C `+filepath.Dir(path.String())) + parArgs = append(parArgs, `-f `+path.String()) + paths = append(paths, path) + } + srcsZip := android.PathForModuleOut(ctx, ctx.ModuleName()+".sharedlibs.srcszip") + ctx.Build(pctx, android.BuildParams{ + Rule: zip, + Description: "bundle shared libraries for python binary", + Output: srcsZip, + Implicits: paths, + Args: map[string]string{ + "args": strings.Join(parArgs, " "), + }, + }) + return srcsZip +} + + // chckForDuplicateOutputPath checks whether outputPath has already been included in map m, which // would result in two files being placed in the same location. // If there is a duplicate path, an error is thrown and true is returned diff --git a/python/scripts/main.py b/python/scripts/main.py index 225dbe4c1..35cdfc47e 100644 --- a/python/scripts/main.py +++ b/python/scripts/main.py @@ -1,5 +1,13 @@ + +import os import runpy +import shutil import sys +import tempfile +import zipfile + +from pathlib import PurePath + sys.argv[0] = __loader__.archive @@ -9,4 +17,32 @@ sys.argv[0] = __loader__.archive # when people try to use it. sys.executable = None -runpy._run_module_as_main("ENTRY_POINT", alter_argv=False) +# Extract the shared libraries from the zip file into a temporary directory. +# This works around the limitations of dynamic linker. Some Python libraries +# reference the .so files relatively and so extracting only the .so files +# does not work, so we extract the entire parent directory of the .so files to a +# tempdir and then add that to sys.path. +tempdir = None +with zipfile.ZipFile(__loader__.archive) as z: + # any root so files or root directories that contain so files will be + # extracted to the tempdir so the linker load them, this minimizes the + # number of files that need to be extracted to a tempdir + extract_paths = {} + for member in z.infolist(): + if member.filename.endswith('.so'): + extract_paths[PurePath(member.filename).parts[0]] = member.filename + if extract_paths: + tempdir = tempfile.mkdtemp() + for member in z.infolist(): + if not PurePath(member.filename).parts[0] in extract_paths.keys(): + continue + if member.is_dir(): + os.makedirs(os.path.join(tempdir, member.filename)) + else: + z.extract(member, tempdir) + sys.path.insert(0, tempdir) +try: + runpy._run_module_as_main("ENTRY_POINT", alter_argv=False) +finally: + if tempdir is not None: + shutil.rmtree(tempdir) |