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"
"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" `
2024-04-11 18:10:00 +02: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
}
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
}
2023-01-23 19:14:58 +01:00
type PythonLibraryModule struct {
2017-02-27 19:12:13 +01:00
android . ModuleBase
2017-07-21 02:43:37 +02:00
android . DefaultableModuleBase
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
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
2023-01-24 20:48:08 +01:00
// The zip file containing the current module's source/data files.
2017-12-18 22:20:23 +01:00
srcsZip android . Path
2023-01-24 20:48:08 +01:00
// The zip file containing the current module's source/data files, with the
// source files precompiled.
precompiledSrcsZip android . Path
2017-02-27 19:12:13 +01:00
}
2020-11-17 00:42:51 +01:00
// newModule generates new Python base module
2023-01-23 19:14:58 +01:00
func newModule ( hod android . HostOrDeviceSupported , multilib android . Multilib ) * PythonLibraryModule {
return & PythonLibraryModule {
2017-07-12 21:55:28 +02:00
hod : hod ,
multilib : multilib ,
}
2017-02-27 19:12:13 +01: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
2023-01-24 20:48:08 +01:00
getPrecompiledSrcsZip ( ) android . Path
2023-08-16 04:17:03 +02:00
getPkgPath ( ) string
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
2023-01-23 19:14:58 +01:00
func ( p * PythonLibraryModule ) 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
2023-01-23 19:14:58 +01:00
func ( p * PythonLibraryModule ) 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.
2023-01-23 19:14:58 +01:00
func ( p * PythonLibraryModule ) getSrcsZip ( ) android . Path {
2017-12-18 22:20:23 +01:00
return p . srcsZip
2017-02-27 19:12:13 +01:00
}
2023-01-24 20:48:08 +01:00
// getSrcsZip returns the filepath where the current module's source/data files are zipped.
func ( p * PythonLibraryModule ) getPrecompiledSrcsZip ( ) android . Path {
return p . precompiledSrcsZip
}
2023-08-16 04:17:03 +02:00
// getPkgPath returns the pkg_path value
func ( p * PythonLibraryModule ) getPkgPath ( ) string {
return String ( p . properties . Pkg_path )
}
2023-01-23 19:14:58 +01:00
func ( p * PythonLibraryModule ) getBaseProperties ( ) * BaseProperties {
return & p . properties
}
2017-02-27 19:12:13 +01:00
2023-01-23 19:14:58 +01:00
var _ pythonDependency = ( * PythonLibraryModule ) ( nil )
2017-02-27 19:12:13 +01:00
2023-01-23 19:14:58 +01:00
func ( p * PythonLibraryModule ) init ( ) android . Module {
2017-12-23 01:12:00 +01:00
p . AddProperties ( & p . properties , & p . protoProperties )
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-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 (
2023-01-24 20:48:08 +01:00
pythonLibTag = dependencyTag { name : "pythonLib" }
javaDataTag = dependencyTag { name : "javaData" }
// The python interpreter, with soong module name "py3-launcher" or "py3-launcher-autorun".
2023-02-14 00:17:40 +01:00
launcherTag = dependencyTag { name : "launcher" }
launcherSharedLibTag = installDependencyTag { name : "launcherSharedLib" }
2023-01-24 20:48:08 +01:00
// The python interpreter built for host so that we can precompile python sources.
// This only works because the precompiled sources don't vary by architecture.
// The soong module name is "py3-launcher".
2023-02-14 00:17:40 +01:00
hostLauncherTag = dependencyTag { name : "hostLauncher" }
hostlauncherSharedLibTag = dependencyTag { name : "hostlauncherSharedLib" }
hostStdLibTag = dependencyTag { name : "hostStdLib" }
pathComponentRegexp = regexp . MustCompile ( ` ^[a-zA-Z_][a-zA-Z0-9_-]*$ ` )
pyExt = ".py"
protoExt = ".proto"
pyVersion2 = "PY2"
pyVersion3 = "PY3"
2023-04-13 02:32:19 +02:00
pyVersion2And3 = "PY2ANDPY3"
2023-02-14 00:17:40 +01:00
internalPath = "internal"
2017-02-27 19:12:13 +01:00
)
2023-01-23 19:14:58 +01:00
type basePropertiesProvider interface {
getBaseProperties ( ) * BaseProperties
}
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 ) {
2023-01-23 19:14:58 +01:00
if base , ok := mctx . Module ( ) . ( basePropertiesProvider ) ; ok {
props := base . getBaseProperties ( )
var 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
2023-01-23 19:14:58 +01:00
var 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
2023-01-23 19:14:58 +01:00
if proptools . BoolDefault ( props . Version . Py3 . Enabled , true ) {
2017-02-27 19:12:13 +01:00
versionNames = append ( versionNames , pyVersion3 )
2023-01-23 19:14:58 +01:00
versionProps = append ( versionProps , props . Version . Py3 )
2017-02-27 19:12:13 +01:00
}
2023-01-23 19:14:58 +01:00
if proptools . BoolDefault ( props . Version . Py2 . Enabled , false ) {
2022-09-10 04:39:25 +02:00
if ! mctx . DeviceConfig ( ) . BuildBrokenUsesSoongPython2Modules ( ) &&
mctx . ModuleName ( ) != "py2-cmd" &&
mctx . ModuleName ( ) != "py2-stdlib" {
mctx . PropertyErrorf ( "version.py2.enabled" , "Python 2 is no longer supported, please convert to python 3. This error can be temporarily overridden by setting BUILD_BROKEN_USES_SOONG_PYTHON2_MODULES := true in the product configuration" )
}
2020-06-13 01:38:45 +02:00
versionNames = append ( versionNames , pyVersion2 )
2023-01-23 19:14:58 +01:00
versionProps = append ( versionProps , props . 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.
2023-01-23 19:14:58 +01:00
newProps := modules [ i ] . ( basePropertiesProvider ) . getBaseProperties ( )
newProps . Actual_version = v
2020-11-17 00:42:51 +01:00
// append versioned properties for the Python module to the overall properties
2023-01-23 19:14:58 +01:00
err := proptools . AppendMatchingProperties ( [ ] interface { } { newProps } , & versionProps [ i ] , nil )
2020-10-30 23:44:09 +01:00
if err != nil {
panic ( err )
}
2017-02-27 19:12:13 +01: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
}
2023-01-23 19:14:58 +01:00
func ( p * PythonLibraryModule ) anySrcHasExt ( ctx android . BottomUpMutatorContext , ext string ) bool {
2020-11-17 00:42:51 +01:00
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
2023-01-23 19:14:58 +01:00
func ( p * PythonLibraryModule ) 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-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 ... )
2023-01-24 20:48:08 +01:00
2023-02-14 00:17:40 +01:00
p . AddDepsOnPythonLauncherAndStdlib ( ctx , hostStdLibTag , hostLauncherTag , hostlauncherSharedLibTag , false , ctx . Config ( ) . BuildOSTarget )
2023-01-24 20:48:08 +01:00
}
2023-02-14 00:17:40 +01:00
// AddDepsOnPythonLauncherAndStdlib will make the current module depend on the python stdlib,
// launcher (interpreter), and the launcher's shared libraries. If autorun is true, it will use
// the autorun launcher instead of the regular one. This function acceps a targetForDeps argument
// as the target to use for these dependencies. For embedded launcher python binaries, the launcher
// that will be embedded will be under the same target as the python module itself. But when
// precompiling python code, we need to get the python launcher built for host, even if we're
// compiling the python module for device, so we pass a different target to this function.
2023-01-24 20:48:08 +01:00
func ( p * PythonLibraryModule ) AddDepsOnPythonLauncherAndStdlib ( ctx android . BottomUpMutatorContext ,
2023-02-14 00:17:40 +01:00
stdLibTag , launcherTag , launcherSharedLibTag blueprint . DependencyTag ,
2023-01-24 20:48:08 +01:00
autorun bool , targetForDeps android . Target ) {
var stdLib string
var launcherModule string
2023-02-14 00:17:40 +01:00
// Add launcher shared lib dependencies. Ideally, these should be
// derived from the `shared_libs` property of the launcher. TODO: read these from
// the python launcher itself using ctx.OtherModuleProvider() or similar on the result
// of ctx.AddFarVariationDependencies()
launcherSharedLibDeps := [ ] string {
"libsqlite" ,
}
// Add launcher-specific dependencies for bionic
if targetForDeps . Os . Bionic ( ) {
launcherSharedLibDeps = append ( launcherSharedLibDeps , "libc" , "libdl" , "libm" )
}
if targetForDeps . Os == android . LinuxMusl && ! ctx . Config ( ) . HostStaticBinaries ( ) {
launcherSharedLibDeps = append ( launcherSharedLibDeps , "libc_musl" )
}
2023-01-24 20:48:08 +01:00
switch p . properties . Actual_version {
case pyVersion2 :
stdLib = "py2-stdlib"
launcherModule = "py2-launcher"
if autorun {
launcherModule = "py2-launcher-autorun"
}
2023-02-14 00:17:40 +01:00
launcherSharedLibDeps = append ( launcherSharedLibDeps , "libc++" )
2023-01-24 20:48:08 +01:00
case pyVersion3 :
2023-08-16 04:17:03 +02:00
var prebuiltStdLib bool
if targetForDeps . Os . Bionic ( ) {
prebuiltStdLib = false
} else if ctx . Config ( ) . VendorConfig ( "cpython3" ) . Bool ( "force_build_host" ) {
prebuiltStdLib = false
} else {
prebuiltStdLib = true
}
if prebuiltStdLib {
stdLib = "py3-stdlib-prebuilt"
} else {
stdLib = "py3-stdlib"
}
2023-01-24 20:48:08 +01:00
launcherModule = "py3-launcher"
if autorun {
launcherModule = "py3-launcher-autorun"
}
if ctx . Config ( ) . HostStaticBinaries ( ) && targetForDeps . Os == android . LinuxMusl {
launcherModule += "-static"
}
2023-02-14 00:17:40 +01:00
if ctx . Device ( ) {
launcherSharedLibDeps = append ( launcherSharedLibDeps , "liblog" )
}
2023-01-24 20:48:08 +01:00
default :
panic ( fmt . Errorf ( "unknown Python Actual_version: %q for module: %q." ,
p . properties . Actual_version , ctx . ModuleName ( ) ) )
}
targetVariations := targetForDeps . Variations ( )
if ctx . ModuleName ( ) != stdLib {
stdLibVariations := make ( [ ] blueprint . Variation , 0 , len ( targetVariations ) + 1 )
stdLibVariations = append ( stdLibVariations , blueprint . Variation { Mutator : "python_version" , Variation : p . properties . Actual_version } )
stdLibVariations = append ( stdLibVariations , targetVariations ... )
// Using AddFarVariationDependencies for all of these because they can be for a different
// platform, like if the python module itself was being compiled for device, we may want
// the python interpreter built for host so that we can precompile python sources.
ctx . AddFarVariationDependencies ( stdLibVariations , stdLibTag , stdLib )
}
ctx . AddFarVariationDependencies ( targetVariations , launcherTag , launcherModule )
2023-02-14 00:17:40 +01:00
ctx . AddFarVariationDependencies ( targetVariations , launcherSharedLibTag , launcherSharedLibDeps ... )
2017-02-27 19:12:13 +01:00
}
2023-01-23 19:14:58 +01:00
// GenerateAndroidBuildActions performs build actions common to all Python modules
func ( p * PythonLibraryModule ) GenerateAndroidBuildActions ( ctx android . ModuleContext ) {
2020-11-17 00:42:51 +01:00
expandedSrcs := android . PathsForModuleSrcExcludes ( ctx , p . properties . Srcs , p . properties . Exclude_srcs )
2023-12-14 00:19:49 +01:00
android . SetProvider ( ctx , blueprint . SrcsFileProviderKey , blueprint . SrcsFileProviderData { SrcPaths : expandedSrcs . Strings ( ) } )
2017-02-27 19:12:13 +01:00
// 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 )
2023-08-25 00:59:16 +02:00
p . precompiledSrcsZip = p . precompileSrcs ( ctx )
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.
2023-01-23 19:14:58 +01:00
func ( p * PythonLibraryModule ) 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 )
2023-08-16 04:17:03 +02:00
// Disable path checks for the stdlib, as it includes a "." in the version string
isInternal := proptools . BoolDefault ( p . properties . Is_internal , false )
2017-02-27 19:12:13 +01:00
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 ( ) )
2023-08-16 04:17:03 +02:00
if ! isInternal {
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.
2023-01-23 19:14:58 +01:00
func ( p * PythonLibraryModule ) createSrcsZip ( ctx android . ModuleContext , pkgPath string ) android . Path {
2017-02-27 19:12:13 +01:00
relativeRootMap := make ( map [ string ] android . Paths )
2017-12-23 01:12:00 +01:00
var protoSrcs android . Paths
2023-01-24 20:48:08 +01:00
addPathMapping := func ( path pathMapping ) {
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 {
2023-01-23 19:14:58 +01:00
relativeRoot := strings . TrimSuffix ( path . src . String ( ) , path . src . Rel ( ) )
relativeRootMap [ relativeRoot ] = append ( relativeRootMap [ relativeRoot ] , path . src )
2017-02-27 19:12:13 +01:00
}
}
2023-01-24 20:48:08 +01:00
// "srcs" or "data" properties may contain filegroups so it might happen that
// the root directory for each source path is different.
for _ , path := range p . srcsPathMappings {
addPathMapping ( path )
}
for _ , path := range p . dataPathMappings {
addPathMapping ( path )
}
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 ( "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.
2023-03-01 01:02:16 +01:00
roots := android . SortedKeys ( 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
}
2023-01-24 20:48:08 +01:00
func ( p * PythonLibraryModule ) precompileSrcs ( ctx android . ModuleContext ) android . Path {
// To precompile the python sources, we need a python interpreter and stdlib built
// for host. We then use those to compile the python sources, which may be used on either
// host of device. Python bytecode is architecture agnostic, so we're essentially
// "cross compiling" for device here purely by virtue of host and device python bytecode
// being the same.
var stdLib android . Path
2023-08-16 04:17:03 +02:00
var stdLibPkg string
2023-01-24 20:48:08 +01:00
var launcher android . Path
2023-08-16 04:17:03 +02:00
if proptools . BoolDefault ( p . properties . Is_internal , false ) {
2023-01-24 20:48:08 +01:00
stdLib = p . srcsZip
2023-08-16 04:17:03 +02:00
stdLibPkg = p . getPkgPath ( )
2023-01-24 20:48:08 +01:00
} else {
ctx . VisitDirectDepsWithTag ( hostStdLibTag , func ( module android . Module ) {
if dep , ok := module . ( pythonDependency ) ; ok {
stdLib = dep . getPrecompiledSrcsZip ( )
2023-08-16 04:17:03 +02:00
stdLibPkg = dep . getPkgPath ( )
2023-01-24 20:48:08 +01:00
}
} )
}
ctx . VisitDirectDepsWithTag ( hostLauncherTag , func ( module android . Module ) {
if dep , ok := module . ( IntermPathProvider ) ; ok {
optionalLauncher := dep . IntermPathForModuleOut ( )
if optionalLauncher . Valid ( ) {
launcher = optionalLauncher . Path ( )
}
2023-02-14 00:17:40 +01:00
}
} )
var launcherSharedLibs android . Paths
var ldLibraryPath [ ] string
ctx . VisitDirectDepsWithTag ( hostlauncherSharedLibTag , func ( module android . Module ) {
if dep , ok := module . ( IntermPathProvider ) ; ok {
optionalPath := dep . IntermPathForModuleOut ( )
if optionalPath . Valid ( ) {
launcherSharedLibs = append ( launcherSharedLibs , optionalPath . Path ( ) )
ldLibraryPath = append ( ldLibraryPath , filepath . Dir ( optionalPath . Path ( ) . String ( ) ) )
2023-01-24 20:48:08 +01:00
}
}
} )
out := android . PathForModuleOut ( ctx , ctx . ModuleName ( ) + ".srcszipprecompiled" )
if stdLib == nil || launcher == nil {
// This shouldn't happen in a real build because we'll error out when adding dependencies
// on the stdlib and launcher if they don't exist. But some tests set
// AllowMissingDependencies.
return out
}
ctx . Build ( pctx , android . BuildParams {
Rule : precompile ,
Input : p . srcsZip ,
Output : out ,
Implicits : launcherSharedLibs ,
Description : "Precompile the python sources of " + ctx . ModuleName ( ) ,
Args : map [ string ] string {
"stdlibZip" : stdLib . String ( ) ,
2023-08-16 04:17:03 +02:00
"stdlibPkg" : stdLibPkg ,
2023-01-24 20:48:08 +01:00
"launcher" : launcher . String ( ) ,
"ldLibraryPath" : strings . Join ( ldLibraryPath , ":" ) ,
} ,
} )
return out
}
2023-01-23 19:14:58 +01:00
// isPythonLibModule returns whether the given module is a Python library PythonLibraryModule or not
2017-07-12 21:55:28 +02:00
func isPythonLibModule ( module blueprint . Module ) bool {
2023-01-23 19:14:58 +01:00
if _ , ok := module . ( * PythonLibraryModule ) ; ok {
if _ , ok := module . ( * PythonBinaryModule ) ; ! ok {
return true
}
2017-07-12 21:55:28 +02:00
}
return false
}
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.
2023-01-24 20:48:08 +01:00
func ( p * PythonLibraryModule ) collectPathsFromTransitiveDeps ( ctx android . ModuleContext , precompiled bool ) android . Paths {
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 )
2023-01-23 19:14:58 +01:00
var result android . Paths
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
}
2023-01-24 20:48:08 +01:00
if precompiled {
result = append ( result , dep . getPrecompiledSrcsZip ( ) )
} else {
result = append ( result , 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
} )
2023-01-23 19:14:58 +01:00
return result
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
2023-01-23 19:14:58 +01:00
func ( p * PythonLibraryModule ) InstallInData ( ) bool {
2017-12-01 21:00:31 +01:00
return true
}
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