Skip to content

Commit 1757639

Browse files
pyalexadchia
authored andcommitted
fix: Build platform specific python packages with ci-build-wheel (#2555)
* ci build wheel Signed-off-by: pyalex <[email protected]> * compile go only with env var Signed-off-by: pyalex <[email protected]> * filter go extensions Signed-off-by: pyalex <[email protected]> * copy full directory on inplace build_ext Signed-off-by: pyalex <[email protected]> * copytree from distutils Signed-off-by: pyalex <[email protected]>
1 parent 1c523bf commit 1757639

File tree

3 files changed

+158
-120
lines changed

3 files changed

+158
-120
lines changed

.github/workflows/publish.yml

Lines changed: 91 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -126,95 +126,121 @@ jobs:
126126
run: ./infra/scripts/helm/push-helm-charts.sh $VERSION_WITHOUT_PREFIX
127127

128128
publish-python-sdk:
129+
runs-on: ubuntu-latest
130+
needs: [build-python-sdk, build-python-sdk-no-telemetry, build-python-sdk-macos-py310]
131+
steps:
132+
- uses: actions/download-artifact@v2
133+
with:
134+
name: wheels
135+
path: dist
136+
- uses: pypa/[email protected]
137+
with:
138+
user: __token__
139+
password: ${{ secrets.PYPI_PASSWORD }}
140+
141+
142+
build-python-sdk:
143+
name: Build wheels on ${{ matrix.os }}
129144
runs-on: ${{ matrix.os }}
130145
strategy:
131-
fail-fast: false
132146
matrix:
133-
python-version: [ "3.7", "3.8", "3.9", "3.10" ]
134-
os: [ ubuntu-latest, macOS-latest ]
135-
compile-go: [ True ]
136-
include:
137-
- python-version: "3.7"
138-
os: ubuntu-latest
139-
compile-go: False
140-
env:
141-
TWINE_USERNAME: __token__
142-
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
143-
COMPILE_GO: ${{ matrix.compile-go }}
147+
os: [ ubuntu-latest, macos-10.15 ]
148+
144149
steps:
145150
- uses: actions/checkout@v2
146-
- name: Setup Python
147-
id: setup-python
148-
uses: actions/setup-python@v2
151+
152+
- name: Build wheels
153+
uses: pypa/cibuildwheel@v2.4.0
149154
with:
150-
python-version: ${{ matrix.python-version }}
151-
architecture: x64
152-
- name: Setup Go
153-
id: setup-go
154-
uses: actions/setup-go@v2
155+
package-dir: sdk/python
156+
env:
157+
CIBW_BUILD: "cp3*_x86_64"
158+
CIBW_SKIP: "cp36-* *-musllinux_x86_64 cp310-macosx_x86_64"
159+
CIBW_ARCHS: "native"
160+
CIBW_ENVIRONMENT: >
161+
COMPILE_GO=True
162+
CIBW_BEFORE_ALL_LINUX: |
163+
yum install -y golang
164+
CIBW_BEFORE_ALL_MACOS: |
165+
curl -o python.pkg https://www.python.org/ftp/python/3.9.12/python-3.9.12-macosx10.9.pkg
166+
sudo installer -pkg python.pkg -target /
167+
CIBW_BEFORE_BUILD: |
168+
make install-protoc-dependencies
169+
make install-go-proto-dependencies
170+
make install-go-ci-dependencies
171+
172+
- uses: actions/upload-artifact@v2
155173
with:
156-
go-version: 1.17.7
157-
- name: Upgrade pip version
158-
run: |
159-
pip install --upgrade "pip>=21.3.1"
160-
- name: Install pip-tools
161-
run: pip install pip-tools
162-
- name: Install dependencies
163-
run: make install-python-ci-dependencies PYTHON=${{ matrix.python-version }}
164-
- name: Publish Python Package
165-
run: |
166-
cd sdk/python
167-
python3 -m pip install --user --upgrade setuptools wheel twine
168-
python3 setup.py sdist bdist_wheel
169-
python3 -m twine upload --verbose dist/*.whl
174+
name: wheels
175+
path: ./wheelhouse/*.whl
176+
170177

171-
publish-python-sdk-no-telemetry:
178+
build-python-sdk-no-telemetry:
179+
name: Build no telemetry wheels on ${{ matrix.os }}
172180
runs-on: ${{ matrix.os }}
173181
strategy:
174-
fail-fast: false
175182
matrix:
176-
python-version: [ "3.7", "3.8", "3.9", "3.10" ]
177-
os: [ ubuntu-latest, macOS-latest ]
178-
compile-go: [ True ]
179-
include:
180-
- python-version: "3.7"
181-
os: ubuntu-latest
182-
compile-go: False
183+
os: [ ubuntu-latest, macos-10.15 ]
183184
needs: get-version
185+
steps:
186+
- uses: actions/checkout@v2
187+
- run: |
188+
cd sdk/python
189+
sed -i.bak 's/DEFAULT_FEAST_USAGE_VALUE = "True"/DEFAULT_FEAST_USAGE_VALUE = "False"/g' feast/constants.py
190+
sed -i.bak 's/NAME = "feast"/NAME = "feast-no-telemetry"/g' setup.py
191+
- name: Build wheels
192+
uses: pypa/[email protected]
193+
with:
194+
package-dir: sdk/python
195+
env:
196+
CIBW_BUILD: "cp3*_x86_64"
197+
CIBW_SKIP: "cp36-* *-musllinux_x86_64 cp310-macosx_x86_64"
198+
CIBW_ARCHS: "native"
199+
CIBW_ENVIRONMENT: >
200+
COMPILE_GO=True SETUPTOOLS_SCM_PRETEND_VERSION="${{ needs.get-version.outputs.version_without_prefix }}"
201+
CIBW_BEFORE_ALL_LINUX: |
202+
yum install -y golang
203+
CIBW_BEFORE_ALL_MACOS: |
204+
curl -o python.pkg https://www.python.org/ftp/python/3.9.12/python-3.9.12-macosx10.9.pkg
205+
sudo installer -pkg python.pkg -target /
206+
CIBW_BEFORE_BUILD: |
207+
make install-protoc-dependencies
208+
make install-go-proto-dependencies
209+
make install-go-ci-dependencies
210+
211+
- uses: actions/upload-artifact@v2
212+
with:
213+
name: wheels
214+
path: ./wheelhouse/*.whl
215+
216+
build-python-sdk-macos-py310:
217+
runs-on: macos-10.15
184218
env:
185-
TWINE_USERNAME: __token__
186-
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
187-
COMPILE_GO: ${{ matrix.compile-go }}
219+
COMPILE_GO: True
188220
steps:
189221
- uses: actions/checkout@v2
190222
- name: Setup Python
191223
id: setup-python
192224
uses: actions/setup-python@v2
193225
with:
194-
python-version: ${{ matrix.python-version }}
226+
python-version: "3.10"
195227
architecture: x64
196-
- name: Setup Go
197-
id: setup-go
198-
uses: actions/setup-go@v2
199-
with:
200-
go-version: 1.17.7
201-
- name: Upgrade pip version
202-
run: |
203-
pip install --upgrade "pip>=21.3.1"
204-
- name: Install pip-tools
205-
run: pip install pip-tools
206228
- name: Install dependencies
207-
run: make install-python-ci-dependencies PYTHON=${{ matrix.python-version }}
208-
- name: Publish Python Package
209-
env:
210-
SETUPTOOLS_SCM_PRETEND_VERSION: ${{ needs.get-version.outputs.version_without_prefix }}
229+
run: |
230+
pip install -U pip setuptools wheel twine
231+
make install-protoc-dependencies
232+
make install-go-proto-dependencies
233+
make install-go-ci-dependencies
234+
- name: Build
211235
run: |
212236
cd sdk/python
213-
sed -i 's/DEFAULT_FEAST_USAGE_VALUE = "True"/DEFAULT_FEAST_USAGE_VALUE = "False"/g' feast/constants.py
214-
sed -i 's/NAME = "feast"/NAME = "feast-no-telemetry"/g' setup.py
215-
python3 -m pip install --user --upgrade setuptools wheel twine
216237
python3 setup.py sdist bdist_wheel
217-
python3 -m twine upload --verbose dist/*.whl
238+
239+
- uses: actions/upload-artifact@v2
240+
with:
241+
name: wheels
242+
path: sdk/python/dist/*
243+
218244

219245
publish-java-sdk:
220246
container: maven:3.6-jdk-11

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,15 +145,15 @@ install-go-ci-dependencies:
145145
go get github.com/go-python/gopy
146146
go install golang.org/x/tools/cmd/goimports
147147
go install github.com/go-python/gopy
148+
python -m pip install pybindgen==0.22.0
148149

149150
install-protoc-dependencies:
150-
pip install grpcio-tools==1.34.0
151+
pip install grpcio-tools==1.44.0 mypy-protobuf==3.1.0
151152

152153
compile-protos-go: install-go-proto-dependencies install-protoc-dependencies
153154
cd sdk/python && python setup.py build_go_protos
154155

155156
compile-go-lib: install-go-proto-dependencies install-go-ci-dependencies
156-
python -m pip install pybindgen==0.22.0
157157
cd sdk/python && python setup.py build_go_lib
158158

159159
# Needs feast package to setup the feature store

sdk/python/setup.py

Lines changed: 65 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -11,29 +11,33 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14+
import copy
1415
import glob
16+
import json
1517
import os
1618
import pathlib
1719
import re
1820
import shutil
1921
import subprocess
2022
import sys
2123
from distutils.cmd import Command
24+
from distutils.dir_util import copy_tree
2225
from pathlib import Path
2326
from subprocess import CalledProcessError
2427

25-
from setuptools import find_packages
28+
from setuptools import find_packages, Extension
2629

2730
try:
2831
from setuptools import setup
2932
from setuptools.command.build_py import build_py
33+
from setuptools.command.build_ext import build_ext as _build_ext
3034
from setuptools.command.develop import develop
3135
from setuptools.command.install import install
32-
from setuptools.dist import Distribution
36+
3337
except ImportError:
3438
from distutils.command.build_py import build_py
39+
from distutils.command.build_ext import build_ext as _build_ext
3540
from distutils.core import setup
36-
from distutils.dist import Distribution
3741

3842
NAME = "feast"
3943
DESCRIPTION = "Python SDK for Feast"
@@ -189,7 +193,7 @@ class BuildPythonProtosCommand(Command):
189193

190194
def initialize_options(self):
191195
self.python_protoc = [
192-
"python",
196+
sys.executable,
193197
"-m",
194198
"grpc_tools.protoc",
195199
] # find_executable("protoc")
@@ -292,7 +296,7 @@ class BuildGoProtosCommand(Command):
292296

293297
def initialize_options(self):
294298
self.go_protoc = [
295-
"python",
299+
sys.executable,
296300
"-m",
297301
"grpc_tools.protoc",
298302
] # find_executable("protoc")
@@ -331,45 +335,6 @@ def run(self):
331335
self._generate_go_protos(f"feast/{sub_folder}/*.proto")
332336

333337

334-
class BuildGoEmbeddedCommand(Command):
335-
description = "Builds Go embedded library"
336-
user_options = []
337-
338-
def initialize_options(self) -> None:
339-
self.path_val = _generate_path_with_gopath()
340-
341-
self.go_env = {}
342-
for var in ("GOCACHE", "GOPATH"):
343-
self.go_env[var] = subprocess \
344-
.check_output(["go", "env", var]) \
345-
.decode("utf-8") \
346-
.strip()
347-
348-
def finalize_options(self) -> None:
349-
pass
350-
351-
def _compile_embedded_lib(self):
352-
print("Compile embedded go")
353-
subprocess.check_call([
354-
"gopy",
355-
"build",
356-
"-output",
357-
"feast/embedded_go/lib",
358-
"-vm",
359-
# Path of current python executable
360-
sys.executable,
361-
"-no-make",
362-
"github.com/feast-dev/feast/go/embedded"
363-
], env={
364-
"PATH": self.path_val,
365-
"CGO_LDFLAGS_ALLOW": ".*",
366-
**self.go_env,
367-
})
368-
369-
def run(self):
370-
self._compile_embedded_lib()
371-
372-
373338
class BuildCommand(build_py):
374339
"""Custom build command."""
375340

@@ -378,7 +343,7 @@ def run(self):
378343
if os.getenv("COMPILE_GO", "false").lower() == "true":
379344
_ensure_go_and_proto_toolchain()
380345
self.run_command("build_go_protos")
381-
self.run_command("build_go_lib")
346+
382347
build_py.run(self)
383348

384349

@@ -390,15 +355,61 @@ def run(self):
390355
if os.getenv("COMPILE_GO", "false").lower() == "true":
391356
_ensure_go_and_proto_toolchain()
392357
self.run_command("build_go_protos")
393-
self.run_command("build_go_lib")
358+
394359
develop.run(self)
395360

396361

397-
class BinaryDistribution(Distribution):
398-
"""Distribution which forces a binary package with platform name
399-
when go compilation is enabled"""
400-
def has_ext_modules(self):
401-
return os.getenv("COMPILE_GO", "false").lower() == "true"
362+
class build_ext(_build_ext):
363+
def finalize_options(self) -> None:
364+
super().finalize_options()
365+
if os.getenv("COMPILE_GO", "false").lower() == "false":
366+
self.extensions = [e for e in self.extensions if not self._is_go_ext(e)]
367+
368+
def _is_go_ext(self, ext: Extension):
369+
return any(source.endswith('.go') or source.startswith('github') for source in ext.sources)
370+
371+
def build_extension(self, ext: Extension):
372+
if not self._is_go_ext(ext):
373+
# the base class may mutate `self.compiler`
374+
compiler = copy.deepcopy(self.compiler)
375+
self.compiler, compiler = compiler, self.compiler
376+
try:
377+
return _build_ext.build_extension(self, ext)
378+
finally:
379+
self.compiler, compiler = compiler, self.compiler
380+
381+
bin_path = _generate_path_with_gopath()
382+
go_env = json.loads(
383+
subprocess.check_output(["go", "env", "-json"]).decode("utf-8").strip()
384+
)
385+
386+
destination = os.path.dirname(os.path.abspath(self.get_ext_fullpath(ext.name)))
387+
subprocess.check_call([
388+
"gopy",
389+
"build",
390+
"-output",
391+
destination,
392+
"-vm",
393+
sys.executable,
394+
"-no-make",
395+
*ext.sources
396+
], env={
397+
"PATH": bin_path,
398+
"CGO_LDFLAGS_ALLOW": ".*",
399+
**go_env,
400+
})
401+
402+
def copy_extensions_to_source(self):
403+
build_py = self.get_finalized_command('build_py')
404+
for ext in self.extensions:
405+
fullname = self.get_ext_fullname(ext.name)
406+
modpath = fullname.split('.')
407+
package = '.'.join(modpath[:-1])
408+
package_dir = build_py.get_package_dir(package)
409+
src = os.path.join(self.build_lib, package_dir)
410+
411+
# copy whole directory
412+
copy_tree(src, package_dir)
402413

403414

404415
setup(
@@ -453,9 +464,10 @@ def has_ext_modules(self):
453464
cmdclass={
454465
"build_python_protos": BuildPythonProtosCommand,
455466
"build_go_protos": BuildGoProtosCommand,
456-
"build_go_lib": BuildGoEmbeddedCommand,
457467
"build_py": BuildCommand,
458468
"develop": DevelopCommand,
469+
"build_ext": build_ext,
459470
},
460-
distclass=BinaryDistribution, # generate wheel with platform-specific name
471+
ext_modules=[Extension('feast.embedded_go.lib._embedded',
472+
["github.com/feast-dev/feast/go/embedded"])],
461473
)

0 commit comments

Comments
 (0)