github.com/westcoastroms/westcoastroms-build@v0.0.0-20190928114312-2350e5a73030/build/soong/python/binary.go (about)

     1  // Copyright 2017 Google Inc. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package python
    16  
    17  // This file contains the module types for building Python binary.
    18  
    19  import (
    20  	"fmt"
    21  	"path/filepath"
    22  	"strings"
    23  
    24  	"android/soong/android"
    25  )
    26  
    27  func init() {
    28  	android.RegisterModuleType("python_binary_host", PythonBinaryHostFactory)
    29  }
    30  
    31  type BinaryProperties struct {
    32  	// the name of the source file that is the main entry point of the program.
    33  	// this file must also be listed in srcs.
    34  	// If left unspecified, module name is used instead.
    35  	// If name doesn’t match any filename in srcs, main must be specified.
    36  	Main *string `android:"arch_variant"`
    37  
    38  	// set the name of the output binary.
    39  	Stem *string `android:"arch_variant"`
    40  
    41  	// append to the name of the output binary.
    42  	Suffix *string `android:"arch_variant"`
    43  
    44  	// list of compatibility suites (for example "cts", "vts") that the module should be
    45  	// installed into.
    46  	Test_suites []string `android:"arch_variant"`
    47  }
    48  
    49  type binaryDecorator struct {
    50  	binaryProperties BinaryProperties
    51  
    52  	*pythonInstaller
    53  }
    54  
    55  type IntermPathProvider interface {
    56  	IntermPathForModuleOut() android.OptionalPath
    57  }
    58  
    59  var (
    60  	stubTemplateHost = "build/soong/python/scripts/stub_template_host.txt"
    61  )
    62  
    63  func NewBinary(hod android.HostOrDeviceSupported) (*Module, *binaryDecorator) {
    64  	module := newModule(hod, android.MultilibFirst)
    65  	decorator := &binaryDecorator{pythonInstaller: NewPythonInstaller("bin", "")}
    66  
    67  	module.bootstrapper = decorator
    68  	module.installer = decorator
    69  
    70  	return module, decorator
    71  }
    72  
    73  func PythonBinaryHostFactory() android.Module {
    74  	module, _ := NewBinary(android.HostSupportedNoCross)
    75  
    76  	return module.Init()
    77  }
    78  
    79  func (binary *binaryDecorator) bootstrapperProps() []interface{} {
    80  	return []interface{}{&binary.binaryProperties}
    81  }
    82  
    83  func (binary *binaryDecorator) bootstrap(ctx android.ModuleContext, actual_version string,
    84  	embedded_launcher bool, srcsPathMappings []pathMapping, parSpec parSpec,
    85  	depsPyRunfiles []string, depsParSpecs []parSpec) android.OptionalPath {
    86  	// no Python source file for compiling .par file.
    87  	if len(srcsPathMappings) == 0 {
    88  		return android.OptionalPath{}
    89  	}
    90  
    91  	// the runfiles packages needs to be populated with "__init__.py".
    92  	newPyPkgs := []string{}
    93  	// the set to de-duplicate the new Python packages above.
    94  	newPyPkgSet := make(map[string]bool)
    95  	// the runfiles dirs have been treated as packages.
    96  	existingPyPkgSet := make(map[string]bool)
    97  
    98  	wholePyRunfiles := []string{}
    99  	for _, path := range srcsPathMappings {
   100  		wholePyRunfiles = append(wholePyRunfiles, path.dest)
   101  	}
   102  	wholePyRunfiles = append(wholePyRunfiles, depsPyRunfiles...)
   103  
   104  	// find all the runfiles dirs which have been treated as packages.
   105  	for _, path := range wholePyRunfiles {
   106  		if filepath.Base(path) != initFileName {
   107  			continue
   108  		}
   109  		existingPyPkg := PathBeforeLastSlash(path)
   110  		if _, found := existingPyPkgSet[existingPyPkg]; found {
   111  			panic(fmt.Errorf("found init file path duplicates: %q for module: %q.",
   112  				path, ctx.ModuleName()))
   113  		} else {
   114  			existingPyPkgSet[existingPyPkg] = true
   115  		}
   116  		parentPath := PathBeforeLastSlash(existingPyPkg)
   117  		populateNewPyPkgs(parentPath, existingPyPkgSet, newPyPkgSet, &newPyPkgs)
   118  	}
   119  
   120  	// create new packages under runfiles tree.
   121  	for _, path := range wholePyRunfiles {
   122  		if filepath.Base(path) == initFileName {
   123  			continue
   124  		}
   125  		parentPath := PathBeforeLastSlash(path)
   126  		populateNewPyPkgs(parentPath, existingPyPkgSet, newPyPkgSet, &newPyPkgs)
   127  	}
   128  
   129  	main := binary.getPyMainFile(ctx, srcsPathMappings)
   130  	if main == "" {
   131  		return android.OptionalPath{}
   132  	}
   133  
   134  	var launcher_path android.Path
   135  	if embedded_launcher {
   136  		ctx.VisitDirectDepsWithTag(launcherTag, func(m android.Module) {
   137  			if provider, ok := m.(IntermPathProvider); ok {
   138  				if launcher_path != nil {
   139  					panic(fmt.Errorf("launcher path was found before: %q",
   140  						launcher_path))
   141  				}
   142  				launcher_path = provider.IntermPathForModuleOut().Path()
   143  			}
   144  		})
   145  	}
   146  
   147  	binFile := registerBuildActionForParFile(ctx, embedded_launcher, launcher_path,
   148  		binary.getHostInterpreterName(ctx, actual_version),
   149  		main, binary.getStem(ctx), newPyPkgs, append(depsParSpecs, parSpec))
   150  
   151  	return android.OptionalPathForPath(binFile)
   152  }
   153  
   154  // get host interpreter name.
   155  func (binary *binaryDecorator) getHostInterpreterName(ctx android.ModuleContext,
   156  	actual_version string) string {
   157  	var interp string
   158  	switch actual_version {
   159  	case pyVersion2:
   160  		interp = "python2.7"
   161  	case pyVersion3:
   162  		interp = "python3"
   163  	default:
   164  		panic(fmt.Errorf("unknown Python actualVersion: %q for module: %q.",
   165  			actual_version, ctx.ModuleName()))
   166  	}
   167  
   168  	return interp
   169  }
   170  
   171  // find main program path within runfiles tree.
   172  func (binary *binaryDecorator) getPyMainFile(ctx android.ModuleContext,
   173  	srcsPathMappings []pathMapping) string {
   174  	var main string
   175  	if String(binary.binaryProperties.Main) == "" {
   176  		main = ctx.ModuleName() + pyExt
   177  	} else {
   178  		main = String(binary.binaryProperties.Main)
   179  	}
   180  
   181  	for _, path := range srcsPathMappings {
   182  		if main == path.src.Rel() {
   183  			return path.dest
   184  		}
   185  	}
   186  	ctx.PropertyErrorf("main", "%q is not listed in srcs.", main)
   187  
   188  	return ""
   189  }
   190  
   191  func (binary *binaryDecorator) getStem(ctx android.ModuleContext) string {
   192  	stem := ctx.ModuleName()
   193  	if String(binary.binaryProperties.Stem) != "" {
   194  		stem = String(binary.binaryProperties.Stem)
   195  	}
   196  
   197  	return stem + String(binary.binaryProperties.Suffix)
   198  }
   199  
   200  // Sets the given directory and all its ancestor directories as Python packages.
   201  func populateNewPyPkgs(pkgPath string, existingPyPkgSet,
   202  	newPyPkgSet map[string]bool, newPyPkgs *[]string) {
   203  	for pkgPath != "" {
   204  		if _, found := existingPyPkgSet[pkgPath]; found {
   205  			break
   206  		}
   207  		if _, found := newPyPkgSet[pkgPath]; !found {
   208  			newPyPkgSet[pkgPath] = true
   209  			*newPyPkgs = append(*newPyPkgs, pkgPath)
   210  			// Gets its ancestor directory by trimming last slash.
   211  			pkgPath = PathBeforeLastSlash(pkgPath)
   212  		} else {
   213  			break
   214  		}
   215  	}
   216  }
   217  
   218  // filepath.Dir("abc") -> "." and filepath.Dir("/abc") -> "/". However,
   219  // the PathBeforeLastSlash() will return "" for both cases above.
   220  func PathBeforeLastSlash(path string) string {
   221  	if idx := strings.LastIndex(path, "/"); idx != -1 {
   222  		return path[:idx]
   223  	}
   224  	return ""
   225  }