diff options
author | Nan Zhang <nanzhang@google.com> | 2017-02-27 10:12:13 -0800 |
---|---|---|
committer | Nan Zhang <nanzhang@google.com> | 2017-05-05 13:27:56 -0700 |
commit | db0b9a3cf3c9965929c988f1292f892bfc5deec5 (patch) | |
tree | fe145e5c2d864983915eaf98e1f8e4648e40c355 /python/binary.go | |
parent | 7c34c4c8eba1ec0d5b8cf50926e40aee3291604e (diff) | |
download | build_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.go | 235 |
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 + +} |