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