// 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" "path/filepath" "strings" "android/soong/android" ) func init() { android.RegisterModuleType("python_binary_host", PythonBinaryHostFactory) } type BinaryProperties 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 `android:"arch_variant"` // set the name of the output binary. Stem *string `android:"arch_variant"` // append to the name of the output binary. Suffix *string `android:"arch_variant"` // list of compatibility suites (for example "cts", "vts") that the module should be // installed into. Test_suites []string `android:"arch_variant"` } type binaryDecorator struct { binaryProperties BinaryProperties *pythonInstaller } type IntermPathProvider interface { IntermPathForModuleOut() android.OptionalPath } var ( stubTemplateHost = "build/soong/python/scripts/stub_template_host.txt" ) func NewBinary(hod android.HostOrDeviceSupported) (*Module, *binaryDecorator) { module := newModule(hod, android.MultilibFirst) decorator := &binaryDecorator{pythonInstaller: NewPythonInstaller("bin", "")} module.bootstrapper = decorator module.installer = decorator return module, decorator } func PythonBinaryHostFactory() android.Module { module, _ := NewBinary(android.HostSupportedNoCross) return module.Init() } 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) } main := binary.getPyMainFile(ctx, srcsPathMappings) if main == "" { return android.OptionalPath{} } var launcher_path android.Path if embedded_launcher { ctx.VisitDirectDepsWithTag(launcherTag, func(m android.Module) { if provider, ok := m.(IntermPathProvider); ok { if launcher_path != nil { panic(fmt.Errorf("launcher path was found before: %q", launcher_path)) } launcher_path = provider.IntermPathForModuleOut().Path() } }) } binFile := registerBuildActionForParFile(ctx, embedded_launcher, launcher_path, binary.getHostInterpreterName(ctx, actual_version), main, binary.getStem(ctx), newPyPkgs, append(depsParSpecs, parSpec)) return android.OptionalPathForPath(binFile) } // get host interpreter name. func (binary *binaryDecorator) getHostInterpreterName(ctx android.ModuleContext, actual_version string) string { var interp string switch actual_version { case pyVersion2: interp = "python2.7" case pyVersion3: interp = "python3" default: panic(fmt.Errorf("unknown Python actualVersion: %q for module: %q.", actual_version, ctx.ModuleName())) } return interp } // find main program path within runfiles tree. func (binary *binaryDecorator) getPyMainFile(ctx android.ModuleContext, srcsPathMappings []pathMapping) string { var main string if String(binary.binaryProperties.Main) == "" { main = ctx.ModuleName() + pyExt } else { main = String(binary.binaryProperties.Main) } for _, path := range srcsPathMappings { if main == path.src.Rel() { return path.dest } } ctx.PropertyErrorf("main", "%q is not listed in srcs.", main) return "" } func (binary *binaryDecorator) getStem(ctx android.ModuleContext) string { stem := ctx.ModuleName() if String(binary.binaryProperties.Stem) != "" { stem = String(binary.binaryProperties.Stem) } 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 "" }