Skip to content

Commit c66bc7b

Browse files
pkierskicztomczak
authored andcommitted
Support for Darwin in pyinstaller (cztomczak#502)
1 parent 3ca4678 commit c66bc7b

File tree

2 files changed

+68
-38
lines changed

2 files changed

+68
-38
lines changed

examples/pyinstaller/hook-cefpython3.py

Lines changed: 65 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""
22
This is PyInstaller hook file for CEF Python. This file
3-
helps pyinstaller find CEF Python dependencies that are
3+
helps PyInstaller find CEF Python dependencies that are
44
required to run final executable.
55
66
See PyInstaller docs for hooks:
@@ -13,7 +13,8 @@
1313
import re
1414
import sys
1515
import PyInstaller
16-
from PyInstaller.utils.hooks import is_module_satisfies
16+
from PyInstaller.utils.hooks import is_module_satisfies, get_package_paths
17+
from PyInstaller.compat import is_win, is_darwin, is_linux, is_py2
1718
from PyInstaller import log as logging
1819

1920
# Constants
@@ -24,24 +25,20 @@
2425
# TODO: use this code to work cross-platform:
2526
# > from PyInstaller.utils.hooks import get_package_paths
2627
# > get_package_paths("cefpython3")
27-
CEFPYTHON3_DIR = os.path.join(
28-
os.path.dirname(sys.executable),
29-
'Lib', 'site-packages', 'cefpython3')
3028

31-
if platform.system() == "Windows":
32-
CYTHON_MODULE_EXT = ".pyd"
33-
else:
34-
CYTHON_MODULE_EXT = ".so"
29+
CEFPYTHON3_DIR = get_package_paths("cefpython3")[1]
30+
31+
CYTHON_MODULE_EXT = ".pyd" if is_win else ".so"
3532

3633
# Globals
3734
logger = logging.getLogger(__name__)
3835

3936

4037
# Functions
4138
def check_platforms():
42-
if platform.system() != "Windows":
43-
raise SystemExit("Error: Currently only Windows platform is "
44-
" supported, see Issue #135.")
39+
if not is_win and not is_darwin:
40+
raise SystemExit("Error: Currently only Windows and Darwin "
41+
"platforms are supported, see Issue #135.")
4542

4643

4744
def check_pyinstaller_version():
@@ -51,7 +48,7 @@ def check_pyinstaller_version():
5148
# Example version string for dev version of pyinstaller:
5249
# > 3.3.dev0+g5dc9557c
5350
version = PyInstaller.__version__
54-
match = re.search(r"^\d+\.\d+", version)
51+
match = re.search(r"^\d+\.\d+(\.\d+)?", version)
5552
if not (match.group(0) >= PYINSTALLER_MIN_VERSION):
5653
raise SystemExit("Error: pyinstaller %s or higher is required"
5754
% PYINSTALLER_MIN_VERSION)
@@ -97,8 +94,8 @@ def get_excluded_cefpython_modules():
9794

9895

9996
def get_cefpython3_datas():
100-
"""Returning all cefpython binaries as DATAS, because
101-
pyinstaller does strange things and fails if these are
97+
"""Returning almost all of cefpython binaries as DATAS (see exception
98+
below), because pyinstaller does strange things and fails if these are
10299
returned as BINARIES. It first updates manifest in .dll files:
103100
>> Updating manifest in chrome_elf.dll
104101
@@ -114,32 +111,61 @@ def get_cefpython3_datas():
114111
as pyinstaller would fail to find binary depdendencies on
115112
these files.
116113
114+
One exception is subprocess (subprocess.exe on Windows) executable
115+
file, which is passed to pyinstaller as BINARIES in order to collect
116+
its dependecies.
117+
117118
DATAS are in format: tuple(full_path, dest_subdir).
118119
"""
119120
ret = list()
120121

122+
if is_win:
123+
cefdatadir = "."
124+
elif is_darwin:
125+
cefdatadir = "."
126+
else:
127+
assert False, "Unsupported system {}".format(platform.system())
128+
121129
# Binaries, licenses and readmes in the cefpython3/ directory
122130
for filename in os.listdir(CEFPYTHON3_DIR):
123131
# Ignore Cython modules which are already handled by
124132
# pyinstaller automatically.
125-
if filename[:-4] in get_cefpython_modules():
133+
if filename[:-len(CYTHON_MODULE_EXT)] in get_cefpython_modules():
126134
continue
135+
127136
# CEF binaries and datas
128-
if filename[-4:] in [".exe", ".dll", ".so", ".pak", ".dat", ".bin",
129-
".txt"]\
130-
or filename in ["License", "subprocess"]:
131-
logger.info("Include cefpython3 data: %s" % filename)
132-
ret.append((os.path.join(CEFPYTHON3_DIR, filename),
133-
""))
134-
135-
# The .pak files in cefpython3/locales/ directory
136-
locales_dir = os.path.join(CEFPYTHON3_DIR, "locales")
137-
assert os.path.exists(locales_dir), "locales/ dir not found in cefpython3"
138-
for filename in os.listdir(locales_dir):
139-
logger.info("Include cefpython3 data: %s/%s" % (
140-
os.path.basename(locales_dir), filename))
141-
ret.append((os.path.join(locales_dir, filename),
142-
"locales"))
137+
extension = os.path.splitext(filename)[1]
138+
if extension in \
139+
[".exe", ".dll", ".pak", ".dat", ".bin", ".txt", ".so", ".plist"] \
140+
or filename.lower().startswith("license"):
141+
logger.info("Include cefpython3 data: {}".format(filename))
142+
ret.append((os.path.join(CEFPYTHON3_DIR, filename), cefdatadir))
143+
144+
if is_darwin:
145+
# "Chromium Embedded Framework.framework/Resources" with subdirectories
146+
# is required. Contain .pak files and locales (each locale in separate
147+
# subdirectory).
148+
resources_subdir = \
149+
os.path.join("Chromium Embedded Framework.framework", "Resources")
150+
base_path = os.path.join(CEFPYTHON3_DIR, resources_subdir)
151+
assert os.path.exists(base_path), \
152+
"{} dir not found in cefpython3".format(resources_subdir)
153+
for path, dirs, files in os.walk(base_path):
154+
for file in files:
155+
absolute_file_path = os.path.join(path, file)
156+
dest_path = os.path.relpath(path, CEFPYTHON3_DIR)
157+
ret.append((absolute_file_path, dest_path))
158+
logger.info("Include cefpython3 data: {}".format(dest_path))
159+
elif is_win:
160+
# The .pak files in cefpython3/locales/ directory
161+
locales_dir = os.path.join(CEFPYTHON3_DIR, "locales")
162+
assert os.path.exists(locales_dir), \
163+
"locales/ dir not found in cefpython3"
164+
for filename in os.listdir(locales_dir):
165+
logger.info("Include cefpython3 data: {}/{}".format(
166+
os.path.basename(locales_dir), filename))
167+
ret.append((os.path.join(locales_dir, filename),
168+
os.path.join(cefdatadir, "locales")))
143169
return ret
144170

145171

@@ -162,7 +188,6 @@ def get_cefpython3_datas():
162188
# TODO: Write a tool script that would find such imports in
163189
# .pyx files automatically.
164190
hiddenimports = [
165-
"base64",
166191
"codecs",
167192
"copy",
168193
"datetime",
@@ -179,16 +204,21 @@ def get_cefpython3_datas():
179204
"urllib",
180205
"weakref",
181206
]
182-
if sys.version_info.major == 2:
207+
if is_py2:
183208
hiddenimports += [
184209
"urlparse",
185210
]
186211

187212
# Excluded modules
188213
excludedimports = get_excluded_cefpython_modules()
189214

190-
# Include binaries
191-
binaries = []
215+
# Include binaries requiring to collect its dependencies
216+
if is_darwin:
217+
binaries = [(os.path.join(CEFPYTHON3_DIR, "subprocess"), ".")]
218+
elif is_win:
219+
binaries = [(os.path.join(CEFPYTHON3_DIR, "subprocess.exe"), ".")]
220+
else:
221+
binaries = []
192222

193223
# Include datas
194224
datas = get_cefpython3_datas()

examples/pyinstaller/pyinstaller.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@
2929

3030
def main():
3131
# Platforms supported
32-
if platform.system() != "Windows":
33-
raise SystemExit("Error: Only Windows platform is currently "
34-
"supported. See Issue #135 for details.")
32+
if platform.system() not in ["Windows", "Darwin"]:
33+
raise SystemExit("Error: Only Windows and Darwin platforms are "
34+
"currently supported. See Issue #135 for details.")
3535

3636
# Make sure nothing is cached from previous build.
3737
# Delete the build/ and dist/ directories.

0 commit comments

Comments
 (0)