Skip to content

Commit 7f9684f

Browse files
Add support to splitgraph.spec for building multi-file executables
- Pass `--onedir` argument intead of `-F` when calling pyinstaller Since spec file is just a Python file, add conditional logic to use Python to create the appropriate `EXE()` and `COLLECT()` TOCs in the spec file based on presence of `--onedir` flag. Existing behavior is retained by default and will always build a single-file executable, except when the `--onedir` flag is contained in `sys.argv` of the `pyinstaller` command that is processing the spec.
1 parent 9442dbb commit 7f9684f

File tree

3 files changed

+64
-26
lines changed

3 files changed

+64
-26
lines changed

.github/workflows/build_and_test_and_release.yml

+13-4
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ jobs:
88
runs-on: ubuntu-18.04
99
if: "!contains(github.event.head_commit.message, '[skip ci]')"
1010
env:
11-
COMPOSE_VERSION: '1.25.4'
12-
POETRY_VERSION: '1.1.6'
11+
COMPOSE_VERSION: "1.25.4"
12+
POETRY_VERSION: "1.1.6"
1313
DOCKER_REPO: splitgraph
1414
DOCKER_ENGINE_IMAGE: engine
1515
DOCKER_TAG: development
@@ -23,7 +23,7 @@ jobs:
2323
- name: Setup Python 3.8
2424
uses: actions/setup-python@v2
2525
with:
26-
python-version: '3.8'
26+
python-version: "3.8"
2727
- uses: actions/cache@v1
2828
with:
2929
path: ~/.cache/pip
@@ -212,7 +212,16 @@ jobs:
212212
with:
213213
name: sgr-osx
214214
path: dist/sgr
215-
215+
- name: Build the multi-file binary
216+
run: |
217+
pyinstaller --clean --noconfirm --onedir splitgraph.osx.spec
218+
dist/sgr-pkg/sgr --version
219+
cd dist/sgr-pkg && tar zcvf ../sgr.tgz .
220+
- name: Upload multi-file binary.gz as artifact
221+
uses: actions/upload-artifact@v2
222+
with:
223+
name: sgr-osx.tgz
224+
path: dist/sgr.tgz
216225

217226
upload_release:
218227
runs-on: ubuntu-18.04

install.sh

+5-7
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,11 @@ _install_binary () {
7777
_check_sgr_exists
7878

7979
URL="https://github.com/splitgraph/splitgraph/releases/download/v${SGR_VERSION}"/$BINARY
80-
echo "Installing the sgr binary from $URL into $INSTALL_DIR"
81-
mkdir -p "$INSTALL_DIR"
82-
curl -fsL "$URL" > "$INSTALL_DIR/sgr"
83-
chmod +x "$INSTALL_DIR/sgr"
84-
"$INSTALL_DIR/sgr" --version
85-
echo "sgr binary installed."
86-
echo
80+
# on OS X, splitgraph.spec is called with --onedir to output .tgz of exe and shlibs
81+
if [ "$BINARY" == "sgr-osx-x86_64.tgz" ] ; then
82+
echo "Installing the compressed sgr binary and deps from $URL into $INSTALL_DIR"
83+
echo "Installing sgr binary and deps into $INSTALL_DIR/pkg"
84+
fi
8785

8886
}
8987

splitgraph.spec

+46-15
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,23 @@
33
# * LD_LIBRARY_PATH=`echo $(python3-config --prefix)/lib` pyinstaller -F splitgraph.spec produces a single sgr binary in the dist/ folder
44
# with libc being the only dynamic dependency (python interpreter included)
55
# * can also do poetry install && poetry run pyinstaller -F splitgraph.spec to build the binary inside of the poetry's venv.
6+
# * specifying `--onedir` instead of `-F` will compile a multi-file executable with COLLECT()
7+
# * e.g. : pyinstaller --clean --noconfirm --onedir splitgraph.spec
68

9+
import sys
710
import os
811
import importlib
912

13+
# Pass --onedir or -D to build a multi-file executable (dir with shlibs + exe)
14+
# Note: This is the same flag syntax as pyinstaller uses, but when using a
15+
# .spec file with pyinstaller, the flag is normally ignored, so we
16+
# explicitly check for it here. This way we can pass different arguments
17+
# to EXE() and conditionally call COLLECTION() without duplicating code.
18+
MAKE_EXE_COLLECTION = False
19+
if "--onedir" in sys.argv or "-D" in sys.argv:
20+
print("splitgraph.spec : --onedir was specified. Will build a multi-file executable...")
21+
MAKE_EXE_COLLECTION = True
22+
1023
block_cipher = None
1124

1225
datas = []
@@ -43,18 +56,36 @@ a = Analysis(
4356
a.datas += Tree("./splitgraph/resources", "splitgraph/resources")
4457

4558
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
46-
exe = EXE(
47-
pyz,
48-
a.scripts,
49-
a.binaries,
50-
a.zipfiles,
51-
a.datas,
52-
[],
53-
name="sgr",
54-
debug=False,
55-
bootloader_ignore_signals=False,
56-
strip=False,
57-
upx=True,
58-
runtime_tmpdir=None,
59-
console=True,
60-
)
59+
60+
# Note: `exe` global is injected by pyinstaller. Can see the code for EXE at:
61+
# https://github.com/pyinstaller/pyinstaller/blob/15f23a8a89be5453b3520df8fc3e667346e103a6/PyInstaller/building/api.py
62+
63+
# TOCS to include in EXE() when in single-file mode (default, i.e. no --onedir flag)
64+
all_tocs = [a.scripts, a.binaries, a.zipfiles, a.datas]
65+
# TOCS to include in EXE() when in multi-file mode (i.e., --onedir flag)
66+
exe_tocs = [a.scripts]
67+
# TOCS to include in COLL() when in multi-file mode (i.e., --onedir flag)
68+
coll_tocs = [a.binaries, a.zipfiles, a.datas]
69+
70+
# When compiling single-file executable, we include every TOC in the EXE
71+
# When compiling multi-file executable, include some TOC in EXE, and rest in COLL
72+
exe_args = [pyz, *exe_tocs, []] if MAKE_EXE_COLLECTION else [pyz, *all_tocs, []]
73+
74+
exe_kwargs_base = {
75+
"name": "sgr",
76+
"debug": False,
77+
"bootloader_ignore_signals": False,
78+
"strip_binaries": False,
79+
"runtime_tmpdir": None,
80+
"console": True,
81+
}
82+
# In multi-file mode, we exclude_binaries from EXE since they will be in COLL
83+
exe_kwargs_onedir = {**exe_kwargs_base, "upx": False, "exclude_binaries": True}
84+
# In single-file mode, we set upx: true because it works. (It might actually work in multi-file mode too)
85+
exe_kwargs_onefile = {**exe_kwargs_base, "upx": True, "exclude_binaries": False}
86+
exe_kwargs = exe_kwargs_onedir if MAKE_EXE_COLLECTION else exe_kwargs_onefile
87+
88+
exe = EXE(*exe_args, **exe_kwargs)
89+
90+
if MAKE_EXE_COLLECTION:
91+
coll = COLLECT(exe, *coll_tocs, name="sgr-pkg", strip=False, upx=False)

0 commit comments

Comments
 (0)