binary.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  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. package python
  15. // This file contains the module types for building Python binary.
  16. import (
  17. "fmt"
  18. "android/soong/android"
  19. "android/soong/bazel"
  20. "github.com/google/blueprint/proptools"
  21. )
  22. func init() {
  23. registerPythonBinaryComponents(android.InitRegistrationContext)
  24. android.RegisterBp2BuildMutator("python_binary_host", PythonBinaryBp2Build)
  25. }
  26. func registerPythonBinaryComponents(ctx android.RegistrationContext) {
  27. ctx.RegisterModuleType("python_binary_host", PythonBinaryHostFactory)
  28. }
  29. type bazelPythonBinaryAttributes struct {
  30. Main string
  31. Srcs bazel.LabelList
  32. Data bazel.LabelList
  33. Python_version string
  34. }
  35. type bazelPythonBinary struct {
  36. android.BazelTargetModuleBase
  37. bazelPythonBinaryAttributes
  38. }
  39. func BazelPythonBinaryFactory() android.Module {
  40. module := &bazelPythonBinary{}
  41. module.AddProperties(&module.bazelPythonBinaryAttributes)
  42. android.InitBazelTargetModule(module)
  43. return module
  44. }
  45. func (m *bazelPythonBinary) Name() string {
  46. return m.BaseModuleName()
  47. }
  48. func (m *bazelPythonBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
  49. func PythonBinaryBp2Build(ctx android.TopDownMutatorContext) {
  50. m, ok := ctx.Module().(*Module)
  51. if !ok || !m.ConvertWithBp2build() {
  52. return
  53. }
  54. // a Module can be something other than a python_binary_host
  55. if ctx.ModuleType() != "python_binary_host" {
  56. return
  57. }
  58. var main string
  59. for _, propIntf := range m.GetProperties() {
  60. if props, ok := propIntf.(*BinaryProperties); ok {
  61. // main is optional.
  62. if props.Main != nil {
  63. main = *props.Main
  64. break
  65. }
  66. }
  67. }
  68. // TODO(b/182306917): this doesn't fully handle all nested props versioned
  69. // by the python version, which would have been handled by the version split
  70. // mutator. This is sufficient for very simple python_binary_host modules
  71. // under Bionic.
  72. py3Enabled := proptools.BoolDefault(m.properties.Version.Py3.Enabled, false)
  73. py2Enabled := proptools.BoolDefault(m.properties.Version.Py2.Enabled, false)
  74. var python_version string
  75. if py3Enabled && py2Enabled {
  76. panic(fmt.Errorf(
  77. "error for '%s' module: bp2build's python_binary_host converter does not support "+
  78. "converting a module that is enabled for both Python 2 and 3 at the same time.", m.Name()))
  79. } else if py2Enabled {
  80. python_version = "PY2"
  81. } else {
  82. // do nothing, since python_version defaults to PY3.
  83. }
  84. attrs := &bazelPythonBinaryAttributes{
  85. Main: main,
  86. Srcs: android.BazelLabelForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs),
  87. Data: android.BazelLabelForModuleSrc(ctx, m.properties.Data),
  88. Python_version: python_version,
  89. }
  90. props := bazel.BazelTargetModuleProperties{
  91. // Use the native py_binary rule.
  92. Rule_class: "py_binary",
  93. }
  94. ctx.CreateBazelTargetModule(BazelPythonBinaryFactory, m.Name(), props, attrs)
  95. }
  96. type BinaryProperties struct {
  97. // the name of the source file that is the main entry point of the program.
  98. // this file must also be listed in srcs.
  99. // If left unspecified, module name is used instead.
  100. // If name doesn’t match any filename in srcs, main must be specified.
  101. Main *string `android:"arch_variant"`
  102. // set the name of the output binary.
  103. Stem *string `android:"arch_variant"`
  104. // append to the name of the output binary.
  105. Suffix *string `android:"arch_variant"`
  106. // list of compatibility suites (for example "cts", "vts") that the module should be
  107. // installed into.
  108. Test_suites []string `android:"arch_variant"`
  109. // whether to use `main` when starting the executable. The default is true, when set to
  110. // false it will act much like the normal `python` executable, but with the sources and
  111. // libraries automatically included in the PYTHONPATH.
  112. Autorun *bool `android:"arch_variant"`
  113. // Flag to indicate whether or not to create test config automatically. If AndroidTest.xml
  114. // doesn't exist next to the Android.bp, this attribute doesn't need to be set to true
  115. // explicitly.
  116. Auto_gen_config *bool
  117. }
  118. type binaryDecorator struct {
  119. binaryProperties BinaryProperties
  120. *pythonInstaller
  121. }
  122. type IntermPathProvider interface {
  123. IntermPathForModuleOut() android.OptionalPath
  124. }
  125. var (
  126. StubTemplateHost = "build/soong/python/scripts/stub_template_host.txt"
  127. )
  128. func NewBinary(hod android.HostOrDeviceSupported) (*Module, *binaryDecorator) {
  129. module := newModule(hod, android.MultilibFirst)
  130. decorator := &binaryDecorator{pythonInstaller: NewPythonInstaller("bin", "")}
  131. module.bootstrapper = decorator
  132. module.installer = decorator
  133. return module, decorator
  134. }
  135. func PythonBinaryHostFactory() android.Module {
  136. module, _ := NewBinary(android.HostSupported)
  137. android.InitBazelModule(module)
  138. return module.init()
  139. }
  140. func (binary *binaryDecorator) autorun() bool {
  141. return BoolDefault(binary.binaryProperties.Autorun, true)
  142. }
  143. func (binary *binaryDecorator) bootstrapperProps() []interface{} {
  144. return []interface{}{&binary.binaryProperties}
  145. }
  146. func (binary *binaryDecorator) bootstrap(ctx android.ModuleContext, actualVersion string,
  147. embeddedLauncher bool, srcsPathMappings []pathMapping, srcsZip android.Path,
  148. depsSrcsZips android.Paths) android.OptionalPath {
  149. main := ""
  150. if binary.autorun() {
  151. main = binary.getPyMainFile(ctx, srcsPathMappings)
  152. }
  153. var launcherPath android.OptionalPath
  154. if embeddedLauncher {
  155. ctx.VisitDirectDepsWithTag(launcherTag, func(m android.Module) {
  156. if provider, ok := m.(IntermPathProvider); ok {
  157. if launcherPath.Valid() {
  158. panic(fmt.Errorf("launcher path was found before: %q",
  159. launcherPath))
  160. }
  161. launcherPath = provider.IntermPathForModuleOut()
  162. }
  163. })
  164. }
  165. binFile := registerBuildActionForParFile(ctx, embeddedLauncher, launcherPath,
  166. binary.getHostInterpreterName(ctx, actualVersion),
  167. main, binary.getStem(ctx), append(android.Paths{srcsZip}, depsSrcsZips...))
  168. return android.OptionalPathForPath(binFile)
  169. }
  170. // get host interpreter name.
  171. func (binary *binaryDecorator) getHostInterpreterName(ctx android.ModuleContext,
  172. actualVersion string) string {
  173. var interp string
  174. switch actualVersion {
  175. case pyVersion2:
  176. interp = "python2.7"
  177. case pyVersion3:
  178. interp = "python3"
  179. default:
  180. panic(fmt.Errorf("unknown Python actualVersion: %q for module: %q.",
  181. actualVersion, ctx.ModuleName()))
  182. }
  183. return interp
  184. }
  185. // find main program path within runfiles tree.
  186. func (binary *binaryDecorator) getPyMainFile(ctx android.ModuleContext,
  187. srcsPathMappings []pathMapping) string {
  188. var main string
  189. if String(binary.binaryProperties.Main) == "" {
  190. main = ctx.ModuleName() + pyExt
  191. } else {
  192. main = String(binary.binaryProperties.Main)
  193. }
  194. for _, path := range srcsPathMappings {
  195. if main == path.src.Rel() {
  196. return path.dest
  197. }
  198. }
  199. ctx.PropertyErrorf("main", "%q is not listed in srcs.", main)
  200. return ""
  201. }
  202. func (binary *binaryDecorator) getStem(ctx android.ModuleContext) string {
  203. stem := ctx.ModuleName()
  204. if String(binary.binaryProperties.Stem) != "" {
  205. stem = String(binary.binaryProperties.Stem)
  206. }
  207. return stem + String(binary.binaryProperties.Suffix)
  208. }