aboutsummaryrefslogtreecommitdiffstats
path: root/python/binary.go
diff options
context:
space:
mode:
authorNan Zhang <nanzhang@google.com>2017-02-27 10:12:13 -0800
committerNan Zhang <nanzhang@google.com>2017-05-05 13:27:56 -0700
commitdb0b9a3cf3c9965929c988f1292f892bfc5deec5 (patch)
treefe145e5c2d864983915eaf98e1f8e4648e40c355 /python/binary.go
parent7c34c4c8eba1ec0d5b8cf50926e40aee3291604e (diff)
downloadbuild_soong-db0b9a3cf3c9965929c988f1292f892bfc5deec5.tar.gz
build_soong-db0b9a3cf3c9965929c988f1292f892bfc5deec5.tar.bz2
build_soong-db0b9a3cf3c9965929c988f1292f892bfc5deec5.zip
Supported python build in host side.
The base module handles all the common functionalites, such as version compatibilty check, version variations split, source file format check, source/data file duplicate check. The library/binary module focuses on how to generate binary build actions, such as setting up stub script, zipping, filling in __init__.py in runfiles dir tree. Bug: b/31676493 Test: go test under python package Change-Id: I06608369f350f7195873d459e1c8d1bdb811e77e
Diffstat (limited to 'python/binary.go')
-rw-r--r--python/binary.go235
1 files changed, 235 insertions, 0 deletions
diff --git a/python/binary.go b/python/binary.go
new file mode 100644
index 00000000..4b4ccc28
--- /dev/null
+++ b/python/binary.go
@@ -0,0 +1,235 @@
+// Copyright 2017 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 python
+
+// This file contains the module types for building Python binary.
+
+import (
+ "fmt"
+ "io"
+ "path/filepath"
+ "strings"
+
+ "github.com/google/blueprint"
+
+ "android/soong/android"
+)
+
+func init() {
+ android.RegisterModuleType("python_binary_host", PythonBinaryHostFactory)
+}
+
+type PythonBinaryProperties struct {
+ // the name of the source file that is the main entry point of the program.
+ // this file must also be listed in srcs.
+ // If left unspecified, module name is used instead.
+ // If name doesn’t match any filename in srcs, main must be specified.
+ Main string
+
+ // set the name of the output binary.
+ Stem string
+
+ // append to the name of the output binary.
+ Suffix string
+}
+
+type PythonBinary struct {
+ pythonBaseModule
+
+ binaryProperties PythonBinaryProperties
+
+ // soong_zip arguments from all its dependencies.
+ depsParSpecs []parSpec
+
+ // Python runfiles paths from all its dependencies.
+ depsPyRunfiles []string
+
+ // the installation path for Python binary.
+ installPath android.OutputPath
+}
+
+var _ PythonSubModule = (*PythonBinary)(nil)
+
+var (
+ stubTemplateHost = "build/soong/python/scripts/stub_template_host.txt"
+)
+
+func PythonBinaryHostFactory() (blueprint.Module, []interface{}) {
+ module := &PythonBinary{}
+
+ return InitPythonBaseModule(&module.pythonBaseModule, module, android.HostSupportedNoCross,
+ &module.binaryProperties)
+}
+
+func (p *PythonBinary) GeneratePythonBuildActions(ctx android.ModuleContext) {
+ p.pythonBaseModule.GeneratePythonBuildActions(ctx)
+
+ // no Python source file for compiling par file.
+ if len(p.pythonBaseModule.srcsPathMappings) == 0 && len(p.depsPyRunfiles) == 0 {
+ return
+ }
+
+ // 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 p.pythonBaseModule.srcsPathMappings {
+ wholePyRunfiles = append(wholePyRunfiles, path.dest)
+ }
+ wholePyRunfiles = append(wholePyRunfiles, p.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)
+ }
+
+ main := p.getPyMainFile(ctx)
+ if main == "" {
+ return
+ }
+ interp := p.getInterpreter(ctx)
+ if interp == "" {
+ return
+ }
+
+ // we need remove "runfiles/" suffix since stub script starts
+ // searching for main file in each sub-dir of "runfiles" directory tree.
+ binFile := registerBuildActionForParFile(ctx, p.getInterpreter(ctx),
+ strings.TrimPrefix(main, runFiles+"/"), p.getStem(ctx),
+ newPyPkgs, append(p.depsParSpecs, p.pythonBaseModule.parSpec))
+
+ // install par file.
+ p.installPath = ctx.InstallFile(
+ android.PathForModuleInstall(ctx, "bin"), binFile)
+}
+
+// get interpreter path.
+func (p *PythonBinary) getInterpreter(ctx android.ModuleContext) string {
+ var interp string
+ switch p.pythonBaseModule.properties.ActualVersion {
+ case pyVersion2:
+ interp = "python2"
+ case pyVersion3:
+ interp = "python3"
+ default:
+ panic(fmt.Errorf("unknown Python actualVersion: %q for module: %q.",
+ p.properties.ActualVersion, ctx.ModuleName()))
+ }
+
+ return interp
+}
+
+// find main program path within runfiles tree.
+func (p *PythonBinary) getPyMainFile(ctx android.ModuleContext) string {
+ var main string
+ if p.binaryProperties.Main == "" {
+ main = p.BaseModuleName() + pyExt
+ } else {
+ main = p.binaryProperties.Main
+ }
+
+ for _, path := range p.pythonBaseModule.srcsPathMappings {
+ if main == path.src.Rel() {
+ return path.dest
+ }
+ }
+ ctx.PropertyErrorf("main", "%q is not listed in srcs.", main)
+
+ return ""
+}
+
+func (p *PythonBinary) getStem(ctx android.ModuleContext) string {
+ stem := ctx.ModuleName()
+ if p.binaryProperties.Stem != "" {
+ stem = p.binaryProperties.Stem
+ }
+
+ return stem + p.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 ""
+}
+
+func (p *PythonBinary) GeneratePythonAndroidMk() (ret android.AndroidMkData, err error) {
+ // Soong installation is only supported for host modules. Have Make
+ // installation trigger Soong installation.
+ if p.pythonBaseModule.Target().Os.Class == android.Host {
+ ret.OutputFile = android.OptionalPathForPath(p.installPath)
+ }
+ ret.Class = "EXECUTABLES"
+
+ ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) error {
+ path := p.installPath.RelPathString()
+ dir, file := filepath.Split(path)
+ stem := strings.TrimSuffix(file, filepath.Ext(file))
+
+ fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+filepath.Ext(file))
+ fmt.Fprintln(w, "LOCAL_MODULE_PATH := $(OUT_DIR)/"+filepath.Clean(dir))
+ fmt.Fprintln(w, "LOCAL_MODULE_STEM := "+stem)
+
+ return nil
+ })
+
+ return
+
+}