blob: cd3f2a1a8fccd435a22f33f87e52c957e78ec535 [file] [log] [blame]
Cole Faust4d247e62023-01-23 10:14:58 -08001// Copyright 2023 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
15package python
16
17import (
Cole Faust4d247e62023-01-23 10:14:58 -080018 "path/filepath"
19 "strings"
20
21 "github.com/google/blueprint/proptools"
22
23 "android/soong/android"
24 "android/soong/bazel"
25)
26
27type bazelPythonLibraryAttributes struct {
28 Srcs bazel.LabelListAttribute
29 Deps bazel.LabelListAttribute
30 Imports bazel.StringListAttribute
31 Srcs_version *string
32}
33
34type bazelPythonProtoLibraryAttributes struct {
35 Deps bazel.LabelListAttribute
36}
37
38type baseAttributes struct {
39 // TODO(b/200311466): Probably not translate b/c Bazel has no good equiv
40 //Pkg_path bazel.StringAttribute
41 // TODO: Related to Pkg_bath and similarLy gated
42 //Is_internal bazel.BoolAttribute
43 // Combines Srcs and Exclude_srcs
44 Srcs bazel.LabelListAttribute
45 Deps bazel.LabelListAttribute
46 // Combines Data and Java_data (invariant)
47 Data bazel.LabelListAttribute
48 Imports bazel.StringListAttribute
49}
50
51func (m *PythonLibraryModule) makeArchVariantBaseAttributes(ctx android.TopDownMutatorContext) baseAttributes {
52 var attrs baseAttributes
53 archVariantBaseProps := m.GetArchVariantProperties(ctx, &BaseProperties{})
54 for axis, configToProps := range archVariantBaseProps {
55 for config, props := range configToProps {
56 if baseProps, ok := props.(*BaseProperties); ok {
57 attrs.Srcs.SetSelectValue(axis, config,
58 android.BazelLabelForModuleSrcExcludes(ctx, baseProps.Srcs, baseProps.Exclude_srcs))
59 attrs.Deps.SetSelectValue(axis, config,
60 android.BazelLabelForModuleDeps(ctx, baseProps.Libs))
61 data := android.BazelLabelForModuleSrc(ctx, baseProps.Data)
62 data.Append(android.BazelLabelForModuleSrc(ctx, baseProps.Java_data))
63 attrs.Data.SetSelectValue(axis, config, data)
64 }
65 }
66 }
67
68 partitionedSrcs := bazel.PartitionLabelListAttribute(ctx, &attrs.Srcs, bazel.LabelPartitions{
69 "proto": android.ProtoSrcLabelPartition,
70 "py": bazel.LabelPartition{Keep_remainder: true},
71 })
72 attrs.Srcs = partitionedSrcs["py"]
73
74 if !partitionedSrcs["proto"].IsEmpty() {
75 protoInfo, _ := android.Bp2buildProtoProperties(ctx, &m.ModuleBase, partitionedSrcs["proto"])
76 protoLabel := bazel.Label{Label: ":" + protoInfo.Name}
77
78 pyProtoLibraryName := m.Name() + "_py_proto"
79 ctx.CreateBazelTargetModule(bazel.BazelTargetModuleProperties{
80 Rule_class: "py_proto_library",
81 Bzl_load_location: "//build/bazel/rules/python:py_proto.bzl",
82 }, android.CommonAttributes{
83 Name: pyProtoLibraryName,
84 }, &bazelPythonProtoLibraryAttributes{
85 Deps: bazel.MakeSingleLabelListAttribute(protoLabel),
86 })
87
88 attrs.Deps.Add(bazel.MakeLabelAttribute(":" + pyProtoLibraryName))
89 }
90
91 // Bazel normally requires `import path.from.top.of.tree` statements in
92 // python code, but with soong you can directly import modules from libraries.
93 // Add "imports" attributes to the bazel library so it matches soong's behavior.
94 imports := "."
95 if m.properties.Pkg_path != nil {
96 // TODO(b/215119317) This is a hack to handle the fact that we don't convert
97 // pkg_path properly right now. If the folder structure that contains this
98 // Android.bp file matches pkg_path, we can set imports to an appropriate
99 // number of ../..s to emulate moving the files under a pkg_path folder.
100 pkg_path := filepath.Clean(*m.properties.Pkg_path)
101 if strings.HasPrefix(pkg_path, "/") {
102 ctx.ModuleErrorf("pkg_path cannot start with a /: %s", pkg_path)
103 }
104
105 if !strings.HasSuffix(ctx.ModuleDir(), "/"+pkg_path) && ctx.ModuleDir() != pkg_path {
106 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())
107 }
108 numFolders := strings.Count(pkg_path, "/") + 1
109 dots := make([]string, numFolders)
110 for i := 0; i < numFolders; i++ {
111 dots[i] = ".."
112 }
113 imports = strings.Join(dots, "/")
114 }
115 attrs.Imports = bazel.MakeStringListAttribute([]string{imports})
116
117 return attrs
118}
119
Cole Faustd82f0362023-04-12 17:32:19 -0700120func (m *PythonLibraryModule) bp2buildPythonVersion(ctx android.TopDownMutatorContext) *string {
Cole Faust4d247e62023-01-23 10:14:58 -0800121 py3Enabled := proptools.BoolDefault(m.properties.Version.Py3.Enabled, true)
122 py2Enabled := proptools.BoolDefault(m.properties.Version.Py2.Enabled, false)
Cole Faust4d247e62023-01-23 10:14:58 -0800123 if py2Enabled && !py3Enabled {
Cole Faustd82f0362023-04-12 17:32:19 -0700124 return &pyVersion2
Cole Faust4d247e62023-01-23 10:14:58 -0800125 } else if !py2Enabled && py3Enabled {
Cole Faustd82f0362023-04-12 17:32:19 -0700126 return &pyVersion3
Cole Faust4d247e62023-01-23 10:14:58 -0800127 } else if !py2Enabled && !py3Enabled {
128 ctx.ModuleErrorf("bp2build converter doesn't understand having neither py2 nor py3 enabled")
Cole Faustd82f0362023-04-12 17:32:19 -0700129 return &pyVersion3
Cole Faust4d247e62023-01-23 10:14:58 -0800130 } else {
Cole Faustd82f0362023-04-12 17:32:19 -0700131 return &pyVersion2And3
Cole Faust4d247e62023-01-23 10:14:58 -0800132 }
Cole Faust4d247e62023-01-23 10:14:58 -0800133}
134
135type bazelPythonBinaryAttributes struct {
136 Main *bazel.Label
137 Srcs bazel.LabelListAttribute
138 Deps bazel.LabelListAttribute
139 Python_version *string
140 Imports bazel.StringListAttribute
141}
142
Cole Faustd82f0362023-04-12 17:32:19 -0700143func (p *PythonLibraryModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
144 // TODO(b/182306917): this doesn't fully handle all nested props versioned
145 // by the python version, which would have been handled by the version split
146 // mutator. This is sufficient for very simple python_library modules under
147 // Bionic.
148 baseAttrs := p.makeArchVariantBaseAttributes(ctx)
149 pyVersion := p.bp2buildPythonVersion(ctx)
150 if *pyVersion == pyVersion2And3 {
151 // Libraries default to python 2 and 3
152 pyVersion = nil
153 }
154
155 attrs := &bazelPythonLibraryAttributes{
156 Srcs: baseAttrs.Srcs,
157 Deps: baseAttrs.Deps,
158 Srcs_version: pyVersion,
159 Imports: baseAttrs.Imports,
160 }
161
162 props := bazel.BazelTargetModuleProperties{
163 // Use the native py_library rule.
164 Rule_class: "py_library",
165 }
166
167 ctx.CreateBazelTargetModule(props, android.CommonAttributes{
168 Name: p.Name(),
169 Data: baseAttrs.Data,
170 }, attrs)
171}
172
173func (p *PythonBinaryModule) bp2buildBinaryProperties(ctx android.TopDownMutatorContext) (*bazelPythonBinaryAttributes, bazel.LabelListAttribute) {
Cole Faust4d247e62023-01-23 10:14:58 -0800174 // TODO(b/182306917): this doesn't fully handle all nested props versioned
175 // by the python version, which would have been handled by the version split
176 // mutator. This is sufficient for very simple python_binary_host modules
177 // under Bionic.
Cole Faustd82f0362023-04-12 17:32:19 -0700178
179 baseAttrs := p.makeArchVariantBaseAttributes(ctx)
180 pyVersion := p.bp2buildPythonVersion(ctx)
181 if *pyVersion == pyVersion3 {
182 // Binaries default to python 3
183 pyVersion = nil
184 } else if *pyVersion == pyVersion2And3 {
185 ctx.ModuleErrorf("error for '%s' module: bp2build's python_binary_host converter "+
186 "does not support converting a module that is enabled for both Python 2 and 3 at the "+
187 "same time.", p.Name())
Cole Faust4d247e62023-01-23 10:14:58 -0800188 }
189
Cole Faust4d247e62023-01-23 10:14:58 -0800190 attrs := &bazelPythonBinaryAttributes{
191 Main: nil,
192 Srcs: baseAttrs.Srcs,
193 Deps: baseAttrs.Deps,
Cole Faustd82f0362023-04-12 17:32:19 -0700194 Python_version: pyVersion,
Cole Faust4d247e62023-01-23 10:14:58 -0800195 Imports: baseAttrs.Imports,
196 }
197
Cole Faustd82f0362023-04-12 17:32:19 -0700198 // main is optional.
199 if p.binaryProperties.Main != nil {
200 main := android.BazelLabelForModuleSrcSingle(ctx, *p.binaryProperties.Main)
201 attrs.Main = &main
Cole Faust4d247e62023-01-23 10:14:58 -0800202 }
Cole Faustd82f0362023-04-12 17:32:19 -0700203 return attrs, baseAttrs.Data
204}
205
206func (p *PythonBinaryModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
207 attrs, data := p.bp2buildBinaryProperties(ctx)
Cole Faust4d247e62023-01-23 10:14:58 -0800208
209 props := bazel.BazelTargetModuleProperties{
210 // Use the native py_binary rule.
211 Rule_class: "py_binary",
212 }
213
214 ctx.CreateBazelTargetModule(props, android.CommonAttributes{
Cole Faustd82f0362023-04-12 17:32:19 -0700215 Name: p.Name(),
216 Data: data,
Cole Faust4d247e62023-01-23 10:14:58 -0800217 }, attrs)
218}
219
Cole Faustd82f0362023-04-12 17:32:19 -0700220func (p *PythonTestModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
221 // Python tests are currently exactly the same as binaries, but with a different module type
222 attrs, data := p.bp2buildBinaryProperties(ctx)
Cole Faust4d247e62023-01-23 10:14:58 -0800223
Cole Faustd82f0362023-04-12 17:32:19 -0700224 props := bazel.BazelTargetModuleProperties{
225 // Use the native py_binary rule.
226 Rule_class: "py_test",
227 }
Cole Faust4d247e62023-01-23 10:14:58 -0800228
Cole Faustd82f0362023-04-12 17:32:19 -0700229 ctx.CreateBazelTargetModule(props, android.CommonAttributes{
230 Name: p.Name(),
231 Data: data,
232 }, attrs)
Cole Faust4d247e62023-01-23 10:14:58 -0800233}