2017-02-27 19:12:13 +01:00
// 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 "Base" module type for building Python program.
import (
"fmt"
"path/filepath"
"regexp"
"strings"
2021-09-17 22:30:21 +02:00
"android/soong/bazel"
2021-11-01 20:32:43 +01:00
2017-02-27 19:12:13 +01:00
"github.com/google/blueprint"
2017-07-12 21:55:28 +02:00
"github.com/google/blueprint/proptools"
2017-02-27 19:12:13 +01:00
"android/soong/android"
)
func init ( ) {
2021-03-17 22:57:08 +01:00
registerPythonMutators ( android . InitRegistrationContext )
}
func registerPythonMutators ( ctx android . RegistrationContext ) {
ctx . PreDepsMutators ( RegisterPythonPreDepsMutators )
2020-06-13 01:38:45 +02:00
}
2020-11-17 00:42:51 +01:00
// Exported to support other packages using Python modules in tests.
2020-06-13 01:38:45 +02:00
func RegisterPythonPreDepsMutators ( ctx android . RegisterMutatorsContext ) {
2020-11-23 04:37:44 +01:00
ctx . BottomUp ( "python_version" , versionSplitMutator ( ) ) . Parallel ( )
2017-02-27 19:12:13 +01:00
}
2020-11-17 00:42:51 +01:00
// the version-specific properties that apply to python modules.
2017-07-12 21:55:28 +02:00
type VersionProperties struct {
2020-11-17 00:42:51 +01:00
// whether the module is required to be built with this version.
// Defaults to true for Python 3, and false otherwise.
2021-09-17 23:48:05 +02:00
Enabled * bool
2017-02-27 19:12:13 +01:00
2020-11-17 00:42:51 +01:00
// list of source files specific to this Python version.
// Using the syntax ":module", srcs may reference the outputs of other modules that produce source files,
// e.g. genrule or filegroup.
2019-03-05 07:35:41 +01:00
Srcs [ ] string ` android:"path,arch_variant" `
2017-07-12 21:55:28 +02:00
2020-11-17 00:42:51 +01:00
// list of source files that should not be used to build the Python module for this version.
// This is most useful to remove files that are not common to all Python versions.
2019-03-05 07:35:41 +01:00
Exclude_srcs [ ] string ` android:"path,arch_variant" `
2017-02-27 19:12:13 +01:00
2020-11-17 00:42:51 +01:00
// list of the Python libraries used only for this Python version.
2017-07-12 21:55:28 +02:00
Libs [ ] string ` android:"arch_variant" `
2020-11-17 00:42:51 +01:00
// whether the binary is required to be built with embedded launcher for this version, defaults to false.
2021-09-17 23:48:05 +02:00
Embedded_launcher * bool // TODO(b/174041232): Remove this property
2017-02-27 19:12:13 +01:00
}
2020-11-17 00:42:51 +01:00
// properties that apply to all python modules
2017-07-12 21:55:28 +02:00
type BaseProperties struct {
2017-02-27 19:12:13 +01:00
// the package path prefix within the output artifact at which to place the source/data
// files of the current module.
// eg. Pkg_path = "a/b/c"; Other packages can reference this module by using
// (from a.b.c import ...) statement.
2018-05-31 21:49:33 +02:00
// if left unspecified, all the source/data files path is unchanged within zip file.
2021-09-17 23:48:05 +02:00
Pkg_path * string
2017-07-12 21:55:28 +02:00
// true, if the Python module is used internally, eg, Python std libs.
2021-09-17 23:48:05 +02:00
Is_internal * bool
2017-02-27 19:12:13 +01:00
// list of source (.py) files compatible both with Python2 and Python3 used to compile the
// Python module.
// srcs may reference the outputs of other modules that produce source files like genrule
// or filegroup using the syntax ":module".
// Srcs has to be non-empty.
2019-03-05 07:35:41 +01:00
Srcs [ ] string ` android:"path,arch_variant" `
2017-07-12 21:55:28 +02:00
// list of source files that should not be used to build the C/C++ module.
// This is most useful in the arch/multilib variants to remove non-common files
2019-03-05 07:35:41 +01:00
Exclude_srcs [ ] string ` android:"path,arch_variant" `
2017-02-27 19:12:13 +01:00
// list of files or filegroup modules that provide data that should be installed alongside
// the test. the file extension can be arbitrary except for (.py).
2019-03-05 07:35:41 +01:00
Data [ ] string ` android:"path,arch_variant" `
2017-02-27 19:12:13 +01:00
2020-11-23 05:12:45 +01:00
// list of java modules that provide data that should be installed alongside the test.
Java_data [ ] string
2017-02-27 19:12:13 +01:00
// list of the Python libraries compatible both with Python2 and Python3.
2017-07-12 21:55:28 +02:00
Libs [ ] string ` android:"arch_variant" `
2017-02-27 19:12:13 +01:00
Version struct {
2020-11-17 00:42:51 +01:00
// Python2-specific properties, including whether Python2 is supported for this module
// and version-specific sources, exclusions and dependencies.
2017-07-12 21:55:28 +02:00
Py2 VersionProperties ` android:"arch_variant" `
2017-02-27 19:12:13 +01:00
2020-11-17 00:42:51 +01:00
// Python3-specific properties, including whether Python3 is supported for this module
// and version-specific sources, exclusions and dependencies.
2017-07-12 21:55:28 +02:00
Py3 VersionProperties ` android:"arch_variant" `
} ` android:"arch_variant" `
2017-02-27 19:12:13 +01:00
// the actual version each module uses after variations created.
// this property name is hidden from users' perspectives, and soong will populate it during
// runtime.
2017-07-12 21:55:28 +02:00
Actual_version string ` blueprint:"mutated" `
2020-10-30 23:44:09 +01:00
2020-11-17 00:42:51 +01:00
// whether the module is required to be built with actual_version.
// this is set by the python version mutator based on version-specific properties
2020-10-30 23:44:09 +01:00
Enabled * bool ` blueprint:"mutated" `
2020-11-17 00:42:51 +01:00
// whether the binary is required to be built with embedded launcher for this actual_version.
// this is set by the python version mutator based on version-specific properties
2020-10-30 23:44:09 +01:00
Embedded_launcher * bool ` blueprint:"mutated" `
2017-02-27 19:12:13 +01:00
}
2021-09-17 22:30:21 +02:00
type baseAttributes struct {
// TODO(b/200311466): Probably not translate b/c Bazel has no good equiv
//Pkg_path bazel.StringAttribute
// TODO: Related to Pkg_bath and similarLy gated
//Is_internal bazel.BoolAttribute
// Combines Srcs and Exclude_srcs
Srcs bazel . LabelListAttribute
Deps bazel . LabelListAttribute
// Combines Data and Java_data (invariant)
2022-06-02 21:11:12 +02:00
Data bazel . LabelListAttribute
Imports bazel . StringListAttribute
2021-09-17 22:30:21 +02:00
}
2020-11-17 00:42:51 +01:00
// Used to store files of current module after expanding dependencies
2017-02-27 19:12:13 +01:00
type pathMapping struct {
dest string
src android . Path
}
2017-07-12 21:55:28 +02:00
type Module struct {
2017-02-27 19:12:13 +01:00
android . ModuleBase
2017-07-21 02:43:37 +02:00
android . DefaultableModuleBase
2021-03-08 13:32:28 +01:00
android . BazelModuleBase
2017-02-27 19:12:13 +01:00
2017-12-23 01:12:00 +01:00
properties BaseProperties
protoProperties android . ProtoProperties
2017-07-12 21:55:28 +02:00
// initialize before calling Init
hod android . HostOrDeviceSupported
multilib android . Multilib
2020-11-17 00:42:51 +01:00
// interface used to bootstrap .par executable when embedded_launcher is true
// this should be set by Python modules which are runnable, e.g. binaries and tests
// bootstrapper might be nil (e.g. Python library module).
2017-07-12 21:55:28 +02:00
bootstrapper bootstrapper
2020-11-17 00:42:51 +01:00
// interface that implements functions required for installation
// this should be set by Python modules which are runnable, e.g. binaries and tests
// installer might be nil (e.g. Python library module).
2017-07-12 21:55:28 +02:00
installer installer
2017-02-27 19:12:13 +01:00
// the Python files of current module after expanding source dependencies.
// pathMapping: <dest: runfile_path, src: source_path>
srcsPathMappings [ ] pathMapping
// the data files of current module after expanding source dependencies.
// pathMapping: <dest: runfile_path, src: source_path>
dataPathMappings [ ] pathMapping
2017-12-18 22:20:23 +01:00
// the zip filepath for zipping current module source/data files.
srcsZip android . Path
2017-07-12 21:55:28 +02:00
2017-12-18 22:20:23 +01:00
// dependency modules' zip filepath for zipping current module source/data files.
depsSrcsZips android . Paths
2017-07-12 21:55:28 +02:00
// (.intermediate) module output path as installation source.
installSource android . OptionalPath
2020-11-17 00:42:51 +01:00
// Map to ensure sub-part of the AndroidMk for this module is only added once
2017-05-10 22:37:54 +02:00
subAndroidMkOnce map [ subAndroidMkProvider ] bool
2017-02-27 19:12:13 +01:00
}
2020-11-17 00:42:51 +01:00
// newModule generates new Python base module
2017-07-12 21:55:28 +02:00
func newModule ( hod android . HostOrDeviceSupported , multilib android . Multilib ) * Module {
return & Module {
hod : hod ,
multilib : multilib ,
}
2017-02-27 19:12:13 +01:00
}
2021-09-17 22:30:21 +02:00
func ( m * Module ) makeArchVariantBaseAttributes ( ctx android . TopDownMutatorContext ) baseAttributes {
var attrs baseAttributes
archVariantBaseProps := m . GetArchVariantProperties ( ctx , & BaseProperties { } )
for axis , configToProps := range archVariantBaseProps {
for config , props := range configToProps {
if baseProps , ok := props . ( * BaseProperties ) ; ok {
attrs . Srcs . SetSelectValue ( axis , config ,
android . BazelLabelForModuleSrcExcludes ( ctx , baseProps . Srcs , baseProps . Exclude_srcs ) )
attrs . Deps . SetSelectValue ( axis , config ,
android . BazelLabelForModuleDeps ( ctx , baseProps . Libs ) )
data := android . BazelLabelForModuleSrc ( ctx , baseProps . Data )
data . Append ( android . BazelLabelForModuleSrc ( ctx , baseProps . Java_data ) )
attrs . Data . SetSelectValue ( axis , config , data )
}
}
}
2022-05-13 00:37:02 +02:00
partitionedSrcs := bazel . PartitionLabelListAttribute ( ctx , & attrs . Srcs , bazel . LabelPartitions {
"proto" : android . ProtoSrcLabelPartition ,
"py" : bazel . LabelPartition { Keep_remainder : true } ,
} )
attrs . Srcs = partitionedSrcs [ "py" ]
if ! partitionedSrcs [ "proto" ] . IsEmpty ( ) {
protoInfo , _ := android . Bp2buildProtoProperties ( ctx , & m . ModuleBase , partitionedSrcs [ "proto" ] )
protoLabel := bazel . Label { Label : ":" + protoInfo . Name }
pyProtoLibraryName := m . Name ( ) + "_py_proto"
ctx . CreateBazelTargetModule ( bazel . BazelTargetModuleProperties {
Rule_class : "py_proto_library" ,
Bzl_load_location : "//build/bazel/rules/python:py_proto.bzl" ,
} , android . CommonAttributes {
Name : pyProtoLibraryName ,
} , & bazelPythonProtoLibraryAttributes {
Deps : bazel . MakeSingleLabelListAttribute ( protoLabel ) ,
} )
attrs . Deps . Add ( bazel . MakeLabelAttribute ( ":" + pyProtoLibraryName ) )
}
2022-06-02 21:11:12 +02:00
// Bazel normally requires `import path.from.top.of.tree` statements in
// python code, but with soong you can directly import modules from libraries.
// Add "imports" attributes to the bazel library so it matches soong's behavior.
imports := "."
if m . properties . Pkg_path != nil {
// TODO(b/215119317) This is a hack to handle the fact that we don't convert
// pkg_path properly right now. If the folder structure that contains this
// Android.bp file matches pkg_path, we can set imports to an appropriate
// number of ../..s to emulate moving the files under a pkg_path folder.
pkg_path := filepath . Clean ( * m . properties . Pkg_path )
if strings . HasPrefix ( pkg_path , "/" ) {
ctx . ModuleErrorf ( "pkg_path cannot start with a /: %s" , pkg_path )
}
if ! strings . HasSuffix ( ctx . ModuleDir ( ) , "/" + pkg_path ) && ctx . ModuleDir ( ) != pkg_path {
ctx . ModuleErrorf ( "Currently, bp2build only supports pkg_paths that are the same as the folders the Android.bp file is in. pkg_path: %s, module directory: %s" , pkg_path , ctx . ModuleDir ( ) )
}
numFolders := strings . Count ( pkg_path , "/" ) + 1
dots := make ( [ ] string , numFolders )
for i := 0 ; i < numFolders ; i ++ {
dots [ i ] = ".."
}
imports = strings . Join ( dots , "/" )
}
attrs . Imports = bazel . MakeStringListAttribute ( [ ] string { imports } )
2021-09-17 22:30:21 +02:00
return attrs
}
2020-11-17 00:42:51 +01:00
// bootstrapper interface should be implemented for runnable modules, e.g. binary and test
2017-07-12 21:55:28 +02:00
type bootstrapper interface {
bootstrapperProps ( ) [ ] interface { }
2017-12-18 22:20:23 +01:00
bootstrap ( ctx android . ModuleContext , ActualVersion string , embeddedLauncher bool ,
srcsPathMappings [ ] pathMapping , srcsZip android . Path ,
depsSrcsZips android . Paths ) android . OptionalPath
2019-02-15 08:17:08 +01:00
autorun ( ) bool
2017-05-10 22:37:54 +02:00
}
2020-11-17 00:42:51 +01:00
// installer interface should be implemented for installable modules, e.g. binary and test
2017-05-10 22:37:54 +02:00
type installer interface {
install ( ctx android . ModuleContext , path android . Path )
2018-11-06 10:30:35 +01:00
setAndroidMkSharedLibs ( sharedLibs [ ] string )
2017-05-10 22:37:54 +02:00
}
2020-11-17 00:42:51 +01:00
// interface implemented by Python modules to provide source and data mappings and zip to python
// modules that depend on it
type pythonDependency interface {
getSrcsPathMappings ( ) [ ] pathMapping
getDataPathMappings ( ) [ ] pathMapping
getSrcsZip ( ) android . Path
2017-07-12 21:55:28 +02:00
}
2020-11-17 00:42:51 +01:00
// getSrcsPathMappings gets this module's path mapping of src source path : runfiles destination
func ( p * Module ) getSrcsPathMappings ( ) [ ] pathMapping {
2017-02-27 19:12:13 +01:00
return p . srcsPathMappings
}
2020-11-17 00:42:51 +01:00
// getSrcsPathMappings gets this module's path mapping of data source path : runfiles destination
func ( p * Module ) getDataPathMappings ( ) [ ] pathMapping {
2017-02-27 19:12:13 +01:00
return p . dataPathMappings
}
2020-11-17 00:42:51 +01:00
// getSrcsZip returns the filepath where the current module's source/data files are zipped.
func ( p * Module ) getSrcsZip ( ) android . Path {
2017-12-18 22:20:23 +01:00
return p . srcsZip
2017-02-27 19:12:13 +01:00
}
2020-11-17 00:42:51 +01:00
var _ pythonDependency = ( * Module ) ( nil )
2017-02-27 19:12:13 +01:00
2020-11-24 17:36:14 +01:00
var _ android . AndroidMkEntriesProvider = ( * Module ) ( nil )
2017-02-27 19:12:13 +01:00
2020-11-17 00:42:51 +01:00
func ( p * Module ) init ( additionalProps ... interface { } ) android . Module {
2017-12-23 01:12:00 +01:00
p . AddProperties ( & p . properties , & p . protoProperties )
2020-11-17 00:42:51 +01:00
// Add additional properties for bootstrapping/installation
// This is currently tied to the bootstrapper interface;
// however, these are a combination of properties for the installation and bootstrapping of a module
2017-07-12 21:55:28 +02:00
if p . bootstrapper != nil {
p . AddProperties ( p . bootstrapper . bootstrapperProps ( ) ... )
}
2017-02-27 19:12:13 +01:00
2017-07-12 21:55:28 +02:00
android . InitAndroidArchModule ( p , p . hod , p . multilib )
2017-07-21 02:43:37 +02:00
android . InitDefaultableModule ( p )
2017-06-24 00:06:31 +02:00
2017-07-12 21:55:28 +02:00
return p
2017-02-27 19:12:13 +01:00
}
2020-11-17 00:42:51 +01:00
// Python-specific tag to transfer information on the purpose of a dependency.
// This is used when adding a dependency on a module, which can later be accessed when visiting
// dependencies.
2017-07-12 21:55:28 +02:00
type dependencyTag struct {
2017-02-27 19:12:13 +01:00
blueprint . BaseDependencyTag
2017-07-12 21:55:28 +02:00
name string
2017-02-27 19:12:13 +01:00
}
2020-11-17 00:42:51 +01:00
// Python-specific tag that indicates that installed files of this module should depend on installed
// files of the dependency
2020-11-11 03:12:15 +01:00
type installDependencyTag struct {
blueprint . BaseDependencyTag
2020-11-17 00:42:51 +01:00
// embedding this struct provides the installation dependency requirement
2020-11-11 03:12:15 +01:00
android . InstallAlwaysNeededDependencyTag
name string
}
2017-02-27 19:12:13 +01:00
var (
2018-11-06 10:30:35 +01:00
pythonLibTag = dependencyTag { name : "pythonLib" }
2020-11-23 05:12:45 +01:00
javaDataTag = dependencyTag { name : "javaData" }
2018-11-06 10:30:35 +01:00
launcherTag = dependencyTag { name : "launcher" }
2020-11-11 03:12:15 +01:00
launcherSharedLibTag = installDependencyTag { name : "launcherSharedLib" }
2020-11-17 00:42:51 +01:00
pathComponentRegexp = regexp . MustCompile ( ` ^[a-zA-Z_][a-zA-Z0-9_-]*$ ` )
2018-11-06 10:30:35 +01:00
pyExt = ".py"
protoExt = ".proto"
pyVersion2 = "PY2"
pyVersion3 = "PY3"
2020-11-17 00:42:51 +01:00
internalPath = "internal"
2017-02-27 19:12:13 +01:00
)
2020-11-17 00:42:51 +01:00
// versionSplitMutator creates version variants for modules and appends the version-specific
// properties for a given variant to the properties in the variant module
2017-02-27 19:12:13 +01:00
func versionSplitMutator ( ) func ( android . BottomUpMutatorContext ) {
return func ( mctx android . BottomUpMutatorContext ) {
2017-07-12 21:55:28 +02:00
if base , ok := mctx . Module ( ) . ( * Module ) ; ok {
2017-02-27 19:12:13 +01:00
versionNames := [ ] string { }
2020-11-17 00:42:51 +01:00
// collect version specific properties, so that we can merge version-specific properties
// into the module's overall properties
2020-10-30 23:44:09 +01:00
versionProps := [ ] VersionProperties { }
2020-06-13 01:38:45 +02:00
// PY3 is first so that we alias the PY3 variant rather than PY2 if both
// are available
2020-11-17 00:42:51 +01:00
if proptools . BoolDefault ( base . properties . Version . Py3 . Enabled , true ) {
2017-02-27 19:12:13 +01:00
versionNames = append ( versionNames , pyVersion3 )
2020-10-30 23:44:09 +01:00
versionProps = append ( versionProps , base . properties . Version . Py3 )
2017-02-27 19:12:13 +01:00
}
2020-11-17 00:42:51 +01:00
if proptools . BoolDefault ( base . properties . Version . Py2 . Enabled , false ) {
2020-06-13 01:38:45 +02:00
versionNames = append ( versionNames , pyVersion2 )
2020-10-30 23:44:09 +01:00
versionProps = append ( versionProps , base . properties . Version . Py2 )
2020-06-13 01:38:45 +02:00
}
2020-11-23 04:37:44 +01:00
modules := mctx . CreateLocalVariations ( versionNames ... )
2020-11-17 00:42:51 +01:00
// Alias module to the first variant
2020-06-13 01:38:45 +02:00
if len ( versionNames ) > 0 {
mctx . AliasVariation ( versionNames [ 0 ] )
}
2017-02-27 19:12:13 +01:00
for i , v := range versionNames {
// set the actual version for Python module.
2017-07-12 21:55:28 +02:00
modules [ i ] . ( * Module ) . properties . Actual_version = v
2020-11-17 00:42:51 +01:00
// append versioned properties for the Python module to the overall properties
2020-10-30 23:44:09 +01:00
err := proptools . AppendMatchingProperties ( [ ] interface { } { & modules [ i ] . ( * Module ) . properties } , & versionProps [ i ] , nil )
if err != nil {
panic ( err )
}
2017-02-27 19:12:13 +01:00
}
}
}
}
2020-11-17 00:42:51 +01:00
// HostToolPath returns a path if appropriate such that this module can be used as a host tool,
// fulfilling HostToolProvider interface.
2017-12-07 00:13:10 +01:00
func ( p * Module ) HostToolPath ( ) android . OptionalPath {
2021-08-10 22:42:03 +02:00
if p . installer != nil {
if bin , ok := p . installer . ( * binaryDecorator ) ; ok {
// TODO: This should only be set when building host binaries -- tests built for device would be
// setting this incorrectly.
return android . OptionalPathForPath ( bin . path )
}
2017-12-07 00:13:10 +01:00
}
2021-08-10 22:42:03 +02:00
return android . OptionalPath { }
2017-12-07 00:13:10 +01:00
}
2020-11-17 00:42:51 +01:00
// OutputFiles returns output files based on given tag, returns an error if tag is unsupported.
2020-06-22 20:52:59 +02:00
func ( p * Module ) OutputFiles ( tag string ) ( android . Paths , error ) {
switch tag {
case "" :
if outputFile := p . installSource ; outputFile . Valid ( ) {
return android . Paths { outputFile . Path ( ) } , nil
}
return android . Paths { } , nil
default :
return nil , fmt . Errorf ( "unsupported module reference tag %q" , tag )
}
}
2020-10-30 23:44:09 +01:00
func ( p * Module ) isEmbeddedLauncherEnabled ( ) bool {
2020-11-17 00:42:51 +01:00
return p . installer != nil && Bool ( p . properties . Embedded_launcher )
2017-07-12 21:55:28 +02:00
}
2020-11-17 00:42:51 +01:00
func anyHasExt ( paths [ ] string , ext string ) bool {
for _ , p := range paths {
if filepath . Ext ( p ) == ext {
2017-12-23 01:12:00 +01:00
return true
}
}
return false
}
2020-11-17 00:42:51 +01:00
func ( p * Module ) anySrcHasExt ( ctx android . BottomUpMutatorContext , ext string ) bool {
return anyHasExt ( p . properties . Srcs , ext )
2017-12-23 01:12:00 +01:00
}
2020-11-17 00:42:51 +01:00
// DepsMutator mutates dependencies for this module:
2022-08-16 19:27:33 +02:00
// - handles proto dependencies,
// - if required, specifies launcher and adds launcher dependencies,
// - applies python version mutations to Python dependencies
2017-07-12 21:55:28 +02:00
func ( p * Module ) DepsMutator ( ctx android . BottomUpMutatorContext ) {
2019-03-29 03:30:56 +01:00
android . ProtoDeps ( ctx , & p . protoProperties )
2020-11-23 04:37:44 +01:00
versionVariation := [ ] blueprint . Variation {
{ "python_version" , p . properties . Actual_version } ,
}
2020-11-17 00:42:51 +01:00
// If sources contain a proto file, add dependency on libprotobuf-python
if p . anySrcHasExt ( ctx , protoExt ) && p . Name ( ) != "libprotobuf-python" {
2020-11-23 04:37:44 +01:00
ctx . AddVariationDependencies ( versionVariation , pythonLibTag , "libprotobuf-python" )
2017-12-23 01:12:00 +01:00
}
2020-11-17 00:42:51 +01:00
// Add python library dependencies for this python version variation
2020-11-23 04:37:44 +01:00
ctx . AddVariationDependencies ( versionVariation , pythonLibTag , android . LastUniqueStrings ( p . properties . Libs ) ... )
2020-10-30 23:44:09 +01:00
2020-11-17 00:42:51 +01:00
// If this module will be installed and has an embedded launcher, we need to add dependencies for:
// * standard library
// * launcher
// * shared dependencies of the launcher
if p . installer != nil && p . isEmbeddedLauncherEnabled ( ) {
var stdLib string
var launcherModule string
// Add launcher shared lib dependencies. Ideally, these should be
// derived from the `shared_libs` property of the launcher. However, we
// cannot read the property at this stage and it will be too late to add
// dependencies later.
launcherSharedLibDeps := [ ] string {
"libsqlite" ,
}
// Add launcher-specific dependencies for bionic
if ctx . Target ( ) . Os . Bionic ( ) {
launcherSharedLibDeps = append ( launcherSharedLibDeps , "libc" , "libdl" , "libm" )
}
2022-03-08 20:56:27 +01:00
if ctx . Target ( ) . Os == android . LinuxMusl && ! ctx . Config ( ) . HostStaticBinaries ( ) {
launcherSharedLibDeps = append ( launcherSharedLibDeps , "libc_musl" )
}
2017-07-12 21:55:28 +02:00
2020-11-17 00:42:51 +01:00
switch p . properties . Actual_version {
case pyVersion2 :
stdLib = "py2-stdlib"
2019-02-15 08:17:08 +01:00
2020-11-17 00:42:51 +01:00
launcherModule = "py2-launcher"
2019-02-15 08:17:08 +01:00
if p . bootstrapper . autorun ( ) {
launcherModule = "py2-launcher-autorun"
}
2022-03-08 20:56:27 +01:00
2020-11-17 00:42:51 +01:00
launcherSharedLibDeps = append ( launcherSharedLibDeps , "libc++" )
2017-07-12 21:55:28 +02:00
2020-11-17 00:42:51 +01:00
case pyVersion3 :
stdLib = "py3-stdlib"
2017-07-12 21:55:28 +02:00
2020-11-17 00:42:51 +01:00
launcherModule = "py3-launcher"
2019-11-05 04:21:04 +01:00
if p . bootstrapper . autorun ( ) {
launcherModule = "py3-launcher-autorun"
}
2022-03-08 20:56:27 +01:00
if ctx . Config ( ) . HostStaticBinaries ( ) && ctx . Target ( ) . Os == android . LinuxMusl {
launcherModule += "-static"
}
2019-11-05 04:21:04 +01:00
2020-01-21 07:08:20 +01:00
if ctx . Device ( ) {
2020-11-17 00:42:51 +01:00
launcherSharedLibDeps = append ( launcherSharedLibDeps , "liblog" )
2019-11-05 04:21:04 +01:00
}
2020-11-17 00:42:51 +01:00
default :
panic ( fmt . Errorf ( "unknown Python Actual_version: %q for module: %q." ,
p . properties . Actual_version , ctx . ModuleName ( ) ) )
2017-07-12 21:55:28 +02:00
}
2020-11-17 00:42:51 +01:00
ctx . AddVariationDependencies ( versionVariation , pythonLibTag , stdLib )
ctx . AddFarVariationDependencies ( ctx . Target ( ) . Variations ( ) , launcherTag , launcherModule )
ctx . AddFarVariationDependencies ( ctx . Target ( ) . Variations ( ) , launcherSharedLibTag , launcherSharedLibDeps ... )
2017-02-27 19:12:13 +01:00
}
2020-11-23 05:12:45 +01:00
// Emulate the data property for java_data but with the arch variation overridden to "common"
// so that it can point to java modules.
javaDataVariation := [ ] blueprint . Variation { { "arch" , android . Common . String ( ) } }
ctx . AddVariationDependencies ( javaDataVariation , javaDataTag , p . properties . Java_data ... )
2017-02-27 19:12:13 +01:00
}
2017-07-12 21:55:28 +02:00
func ( p * Module ) GenerateAndroidBuildActions ( ctx android . ModuleContext ) {
2020-11-17 00:42:51 +01:00
p . generatePythonBuildActions ( ctx )
2017-05-10 22:37:54 +02:00
2020-11-17 00:42:51 +01:00
// Only Python binary and test modules have non-empty bootstrapper.
2017-07-12 21:55:28 +02:00
if p . bootstrapper != nil {
2020-11-17 00:42:51 +01:00
// if the module is being installed, we need to collect all transitive dependencies to embed in
// the final par
p . collectPathsFromTransitiveDeps ( ctx )
// bootstrap the module, including resolving main file, getting launcher path, and
// registering actions to build the par file
// bootstrap returns the binary output path
2017-07-12 21:55:28 +02:00
p . installSource = p . bootstrapper . bootstrap ( ctx , p . properties . Actual_version ,
2020-11-17 00:42:51 +01:00
p . isEmbeddedLauncherEnabled ( ) , p . srcsPathMappings , p . srcsZip , p . depsSrcsZips )
2017-07-12 21:55:28 +02:00
}
2020-11-17 00:42:51 +01:00
// Only Python binary and test modules have non-empty installer.
2018-11-06 10:30:35 +01:00
if p . installer != nil {
var sharedLibs [ ] string
2020-11-17 00:42:51 +01:00
// if embedded launcher is enabled, we need to collect the shared library depenendencies of the
// launcher
2021-05-03 22:35:32 +02:00
for _ , dep := range ctx . GetDirectDepsWithTag ( launcherSharedLibTag ) {
sharedLibs = append ( sharedLibs , ctx . OtherModuleName ( dep ) )
}
2018-11-06 10:30:35 +01:00
p . installer . setAndroidMkSharedLibs ( sharedLibs )
2020-11-17 00:42:51 +01:00
// Install the par file from installSource
2018-11-06 10:30:35 +01:00
if p . installSource . Valid ( ) {
p . installer . install ( ctx , p . installSource . Path ( ) )
}
2017-05-10 22:37:54 +02:00
}
2017-02-27 19:12:13 +01:00
}
2020-11-17 00:42:51 +01:00
// generatePythonBuildActions performs build actions common to all Python modules
func ( p * Module ) generatePythonBuildActions ( ctx android . ModuleContext ) {
expandedSrcs := android . PathsForModuleSrcExcludes ( ctx , p . properties . Srcs , p . properties . Exclude_srcs )
2019-02-15 08:17:08 +01:00
requiresSrcs := true
if p . bootstrapper != nil && ! p . bootstrapper . autorun ( ) {
requiresSrcs = false
}
if len ( expandedSrcs ) == 0 && requiresSrcs {
2017-02-27 19:12:13 +01:00
ctx . ModuleErrorf ( "doesn't have any source files!" )
}
// expand data files from "data" property.
2019-03-06 07:25:09 +01:00
expandedData := android . PathsForModuleSrc ( ctx , p . properties . Data )
2017-02-27 19:12:13 +01:00
2020-11-23 05:12:45 +01:00
// Emulate the data property for java_data dependencies.
for _ , javaData := range ctx . GetDirectDepsWithTag ( javaDataTag ) {
expandedData = append ( expandedData , android . OutputFilesForModule ( ctx , javaData , "" ) ... )
}
2020-11-17 00:42:51 +01:00
// Validate pkg_path property
2017-12-18 22:20:23 +01:00
pkgPath := String ( p . properties . Pkg_path )
if pkgPath != "" {
2020-11-17 00:42:51 +01:00
// TODO: export validation from android/paths.go handling to replace this duplicated functionality
2017-12-18 22:20:23 +01:00
pkgPath = filepath . Clean ( String ( p . properties . Pkg_path ) )
if pkgPath == ".." || strings . HasPrefix ( pkgPath , "../" ) ||
strings . HasPrefix ( pkgPath , "/" ) {
2017-07-12 21:55:28 +02:00
ctx . PropertyErrorf ( "pkg_path" ,
"%q must be a relative path contained in par file." ,
2017-11-09 06:20:04 +01:00
String ( p . properties . Pkg_path ) )
2017-07-12 21:55:28 +02:00
return
}
2020-11-17 00:42:51 +01:00
}
// If property Is_internal is set, prepend pkgPath with internalPath
if proptools . BoolDefault ( p . properties . Is_internal , false ) {
pkgPath = filepath . Join ( internalPath , pkgPath )
2017-02-27 19:12:13 +01:00
}
2020-11-17 00:42:51 +01:00
// generate src:destination path mappings for this module
2017-12-18 22:20:23 +01:00
p . genModulePathMappings ( ctx , pkgPath , expandedSrcs , expandedData )
2017-02-27 19:12:13 +01:00
2020-11-17 00:42:51 +01:00
// generate the zipfile of all source and data files
2017-12-18 22:20:23 +01:00
p . srcsZip = p . createSrcsZip ( ctx , pkgPath )
2017-02-27 19:12:13 +01:00
}
2020-11-17 00:42:51 +01:00
func isValidPythonPath ( path string ) error {
identifiers := strings . Split ( strings . TrimSuffix ( path , filepath . Ext ( path ) ) , "/" )
for _ , token := range identifiers {
if ! pathComponentRegexp . MatchString ( token ) {
return fmt . Errorf ( "the path %q contains invalid subpath %q. " +
"Subpaths must be at least one character long. " +
"The first character must an underscore or letter. " +
"Following characters may be any of: letter, digit, underscore, hyphen." ,
path , token )
}
}
return nil
}
// For this module, generate unique pathMappings: <dest: runfiles_path, src: source_path>
// for python/data files expanded from properties.
2017-12-18 22:20:23 +01:00
func ( p * Module ) genModulePathMappings ( ctx android . ModuleContext , pkgPath string ,
2017-02-27 19:12:13 +01:00
expandedSrcs , expandedData android . Paths ) {
// fetch <runfiles_path, source_path> pairs from "src" and "data" properties to
2017-12-23 01:12:00 +01:00
// check current module duplicates.
2017-02-27 19:12:13 +01:00
destToPySrcs := make ( map [ string ] string )
destToPyData := make ( map [ string ] string )
for _ , s := range expandedSrcs {
2017-12-23 01:12:00 +01:00
if s . Ext ( ) != pyExt && s . Ext ( ) != protoExt {
ctx . PropertyErrorf ( "srcs" , "found non (.py|.proto) file: %q!" , s . String ( ) )
2017-02-27 19:12:13 +01:00
continue
}
2017-12-18 22:20:23 +01:00
runfilesPath := filepath . Join ( pkgPath , s . Rel ( ) )
2020-11-17 00:42:51 +01:00
if err := isValidPythonPath ( runfilesPath ) ; err != nil {
ctx . PropertyErrorf ( "srcs" , err . Error ( ) )
2017-02-27 19:12:13 +01:00
}
2020-11-17 00:42:51 +01:00
if ! checkForDuplicateOutputPath ( ctx , destToPySrcs , runfilesPath , s . String ( ) , p . Name ( ) , p . Name ( ) ) {
p . srcsPathMappings = append ( p . srcsPathMappings , pathMapping { dest : runfilesPath , src : s } )
2017-02-27 19:12:13 +01:00
}
}
for _ , d := range expandedData {
2017-12-23 01:12:00 +01:00
if d . Ext ( ) == pyExt || d . Ext ( ) == protoExt {
ctx . PropertyErrorf ( "data" , "found (.py|.proto) file: %q!" , d . String ( ) )
2017-02-27 19:12:13 +01:00
continue
}
2017-12-18 22:20:23 +01:00
runfilesPath := filepath . Join ( pkgPath , d . Rel ( ) )
2020-11-17 00:42:51 +01:00
if ! checkForDuplicateOutputPath ( ctx , destToPyData , runfilesPath , d . String ( ) , p . Name ( ) , p . Name ( ) ) {
2017-02-27 19:12:13 +01:00
p . dataPathMappings = append ( p . dataPathMappings ,
pathMapping { dest : runfilesPath , src : d } )
}
}
}
2020-11-17 00:42:51 +01:00
// createSrcsZip registers build actions to zip current module's sources and data.
2017-12-18 22:20:23 +01:00
func ( p * Module ) createSrcsZip ( ctx android . ModuleContext , pkgPath string ) android . Path {
2017-02-27 19:12:13 +01:00
relativeRootMap := make ( map [ string ] android . Paths )
pathMappings := append ( p . srcsPathMappings , p . dataPathMappings ... )
2017-12-23 01:12:00 +01:00
var protoSrcs android . Paths
2020-11-17 00:42:51 +01:00
// "srcs" or "data" properties may contain filegroup so it might happen that
// the root directory for each source path is different.
2017-02-27 19:12:13 +01:00
for _ , path := range pathMappings {
2020-11-17 00:42:51 +01:00
// handle proto sources separately
2017-12-23 01:12:00 +01:00
if path . src . Ext ( ) == protoExt {
protoSrcs = append ( protoSrcs , path . src )
2017-02-27 19:12:13 +01:00
} else {
2017-12-23 01:12:00 +01:00
var relativeRoot string
relativeRoot = strings . TrimSuffix ( path . src . String ( ) , path . src . Rel ( ) )
if v , found := relativeRootMap [ relativeRoot ] ; found {
relativeRootMap [ relativeRoot ] = append ( v , path . src )
} else {
relativeRootMap [ relativeRoot ] = android . Paths { path . src }
}
2017-02-27 19:12:13 +01:00
}
}
2017-12-23 01:12:00 +01:00
var zips android . Paths
if len ( protoSrcs ) > 0 {
2019-03-28 22:45:07 +01:00
protoFlags := android . GetProtoFlags ( ctx , & p . protoProperties )
protoFlags . OutTypeFlag = "--python_out"
2022-10-22 01:07:56 +02:00
if pkgPath != "" {
2022-09-19 20:19:52 +02:00
pkgPathStagingDir := android . PathForModuleGen ( ctx , "protos_staged_for_pkg_path" )
rule := android . NewRuleBuilder ( pctx , ctx )
var stagedProtoSrcs android . Paths
for _ , srcFile := range protoSrcs {
stagedProtoSrc := pkgPathStagingDir . Join ( ctx , pkgPath , srcFile . Rel ( ) )
rule . Command ( ) . Text ( "mkdir -p" ) . Flag ( filepath . Base ( stagedProtoSrc . String ( ) ) )
rule . Command ( ) . Text ( "cp -f" ) . Input ( srcFile ) . Output ( stagedProtoSrc )
stagedProtoSrcs = append ( stagedProtoSrcs , stagedProtoSrc )
}
rule . Build ( "stage_protos_for_pkg_path" , "Stage protos for pkg_path" )
protoSrcs = stagedProtoSrcs
}
2017-12-23 01:12:00 +01:00
for _ , srcFile := range protoSrcs {
2022-10-22 01:07:56 +02:00
zip := genProto ( ctx , srcFile , protoFlags )
2017-12-23 01:12:00 +01:00
zips = append ( zips , zip )
2017-12-18 22:20:23 +01:00
}
2017-02-27 19:12:13 +01:00
}
2017-12-23 01:12:00 +01:00
if len ( relativeRootMap ) > 0 {
// in order to keep stable order of soong_zip params, we sort the keys here.
2020-11-17 00:42:51 +01:00
roots := android . SortedStringKeys ( relativeRootMap )
2017-12-23 01:12:00 +01:00
2022-06-02 21:11:12 +02:00
// Use -symlinks=false so that the symlinks in the bazel output directory are followed
parArgs := [ ] string { "-symlinks=false" }
2018-05-22 23:50:18 +02:00
if pkgPath != "" {
2020-11-17 00:42:51 +01:00
// use package path as path prefix
2018-05-22 23:50:18 +02:00
parArgs = append ( parArgs , ` -P ` + pkgPath )
}
2020-11-17 00:42:51 +01:00
paths := android . Paths { }
for _ , root := range roots {
// specify relative root of file in following -f arguments
parArgs = append ( parArgs , ` -C ` + root )
for _ , path := range relativeRootMap [ root ] {
2017-12-23 01:12:00 +01:00
parArgs = append ( parArgs , ` -f ` + path . String ( ) )
2020-11-17 00:42:51 +01:00
paths = append ( paths , path )
2017-12-23 01:12:00 +01:00
}
}
2017-12-18 22:20:23 +01:00
2017-12-23 01:12:00 +01:00
origSrcsZip := android . PathForModuleOut ( ctx , ctx . ModuleName ( ) + ".py.srcszip" )
ctx . Build ( pctx , android . BuildParams {
Rule : zip ,
Description : "python library archive" ,
Output : origSrcsZip ,
2020-11-17 00:42:51 +01:00
// as zip rule does not use $in, there is no real need to distinguish between Inputs and Implicits
Implicits : paths ,
2017-12-23 01:12:00 +01:00
Args : map [ string ] string {
"args" : strings . Join ( parArgs , " " ) ,
} ,
} )
zips = append ( zips , origSrcsZip )
}
2020-11-17 00:42:51 +01:00
// we may have multiple zips due to separate handling of proto source files
2017-12-23 01:12:00 +01:00
if len ( zips ) == 1 {
return zips [ 0 ]
} else {
combinedSrcsZip := android . PathForModuleOut ( ctx , ctx . ModuleName ( ) + ".srcszip" )
ctx . Build ( pctx , android . BuildParams {
Rule : combineZip ,
Description : "combine python library archive" ,
Output : combinedSrcsZip ,
Inputs : zips ,
} )
return combinedSrcsZip
}
2017-02-27 19:12:13 +01:00
}
2020-11-17 00:42:51 +01:00
// isPythonLibModule returns whether the given module is a Python library Module or not
2017-07-12 21:55:28 +02:00
func isPythonLibModule ( module blueprint . Module ) bool {
if m , ok := module . ( * Module ) ; ok {
2021-11-01 20:32:43 +01:00
return m . isLibrary ( )
2017-07-12 21:55:28 +02:00
}
return false
}
2021-11-01 20:32:43 +01:00
// This is distinguished by the fact that Python libraries are not installable, while other Python
// modules are.
func ( p * Module ) isLibrary ( ) bool {
// Python library has no bootstrapper or installer
return p . bootstrapper == nil && p . installer == nil
}
func ( p * Module ) isBinary ( ) bool {
_ , ok := p . bootstrapper . ( * binaryDecorator )
return ok
}
2020-11-17 00:42:51 +01:00
// collectPathsFromTransitiveDeps checks for source/data files for duplicate paths
// for module and its transitive dependencies and collects list of data/source file
// zips for transitive dependencies.
func ( p * Module ) collectPathsFromTransitiveDeps ( ctx android . ModuleContext ) {
2017-02-27 19:12:13 +01:00
// fetch <runfiles_path, source_path> pairs from "src" and "data" properties to
// check duplicates.
destToPySrcs := make ( map [ string ] string )
destToPyData := make ( map [ string ] string )
for _ , path := range p . srcsPathMappings {
destToPySrcs [ path . dest ] = path . src . String ( )
}
for _ , path := range p . dataPathMappings {
destToPyData [ path . dest ] = path . src . String ( )
}
2018-06-21 22:03:07 +02:00
seen := make ( map [ android . Module ] bool )
2017-02-27 19:12:13 +01:00
// visit all its dependencies in depth first.
2018-06-21 22:03:07 +02:00
ctx . WalkDeps ( func ( child , parent android . Module ) bool {
2020-11-17 00:42:51 +01:00
// we only collect dependencies tagged as python library deps
2018-06-21 22:03:07 +02:00
if ctx . OtherModuleDependencyTag ( child ) != pythonLibTag {
return false
2017-02-27 19:12:13 +01:00
}
2018-06-21 22:03:07 +02:00
if seen [ child ] {
return false
}
seen [ child ] = true
2017-12-23 01:12:00 +01:00
// Python modules only can depend on Python libraries.
2018-06-21 22:03:07 +02:00
if ! isPythonLibModule ( child ) {
2020-11-17 00:42:51 +01:00
ctx . PropertyErrorf ( "libs" ,
2017-07-12 21:55:28 +02:00
"the dependency %q of module %q is not Python library!" ,
2021-08-20 23:02:43 +02:00
ctx . OtherModuleName ( child ) , ctx . ModuleName ( ) )
2017-07-12 21:55:28 +02:00
}
2020-11-17 00:42:51 +01:00
// collect source and data paths, checking that there are no duplicate output file conflicts
if dep , ok := child . ( pythonDependency ) ; ok {
srcs := dep . getSrcsPathMappings ( )
2017-02-27 19:12:13 +01:00
for _ , path := range srcs {
2020-11-17 00:42:51 +01:00
checkForDuplicateOutputPath ( ctx , destToPySrcs ,
path . dest , path . src . String ( ) , ctx . ModuleName ( ) , ctx . OtherModuleName ( child ) )
2017-02-27 19:12:13 +01:00
}
2020-11-17 00:42:51 +01:00
data := dep . getDataPathMappings ( )
2017-02-27 19:12:13 +01:00
for _ , path := range data {
2020-11-17 00:42:51 +01:00
checkForDuplicateOutputPath ( ctx , destToPyData ,
2018-06-21 22:03:07 +02:00
path . dest , path . src . String ( ) , ctx . ModuleName ( ) , ctx . OtherModuleName ( child ) )
2017-02-27 19:12:13 +01:00
}
2020-11-17 00:42:51 +01:00
p . depsSrcsZips = append ( p . depsSrcsZips , dep . getSrcsZip ( ) )
2017-02-27 19:12:13 +01:00
}
2018-06-21 22:03:07 +02:00
return true
2017-02-27 19:12:13 +01:00
} )
}
2020-11-17 00:42:51 +01:00
// 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
// Otherwise, outputPath: srcPath is added to m and returns false
func checkForDuplicateOutputPath ( ctx android . ModuleContext , m map [ string ] string , outputPath , srcPath , curModule , otherModule string ) bool {
if oldSrcPath , found := m [ outputPath ] ; found {
2018-05-31 21:49:33 +02:00
ctx . ModuleErrorf ( "found two files to be placed at the same location within zip %q." +
2017-02-27 19:12:13 +01:00
" First file: in module %s at path %q." +
" Second file: in module %s at path %q." ,
2020-11-17 00:42:51 +01:00
outputPath , curModule , oldSrcPath , otherModule , srcPath )
return true
2017-02-27 19:12:13 +01:00
}
2020-11-17 00:42:51 +01:00
m [ outputPath ] = srcPath
2017-02-27 19:12:13 +01:00
2020-11-17 00:42:51 +01:00
return false
2017-02-27 19:12:13 +01:00
}
2017-11-09 06:20:04 +01:00
2020-11-17 00:42:51 +01:00
// InstallInData returns true as Python is not supported in the system partition
2017-12-01 21:00:31 +01:00
func ( p * Module ) InstallInData ( ) bool {
return true
}
2021-11-01 20:32:43 +01:00
func ( p * Module ) ConvertWithBp2build ( ctx android . TopDownMutatorContext ) {
if p . isLibrary ( ) {
pythonLibBp2Build ( ctx , p )
} else if p . isBinary ( ) {
pythonBinaryBp2Build ( ctx , p )
}
}
2017-11-09 06:20:04 +01:00
var Bool = proptools . Bool
2019-02-15 08:17:08 +01:00
var BoolDefault = proptools . BoolDefault
2017-11-09 06:20:04 +01:00
var String = proptools . String