Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
import glob as gb_
import os as os_
import os.path as ph_
import shutil as sh_
import subprocess as sp_
import sys as sy_
import zipfile as zp_
from os import name as OS_NAME
from os import pathsep as PATH_SEP
from typing import Sequence, Union
import pprint as pp_
import PyInstaller.__main__
PY_SCRIPT = "nutrimorph.py"
PARAMETERS = "parameters.py"
BRICK_FOLDER = "brick"
RES_PATH_CMPS = ("data",)
SRC_PATH_CMPS = ("..",)
README_PREFIX = "README"
RES_PREFIX = "DIO"
DEP_LIST_DOC = f"{README_PREFIX}-REQUIREMENTS.txt"
REQUIREMENTS_MARKER = "Requires:"
DIST_ROOT_FOLDER = "__dist__"
BUILD_ROOT_FOLDER = "__build__"
PER_OS_DIST_FOLDER = f"NutriMorph-4-{OS_NAME.upper()}"
DST_RES_FOLDER = "data"
def InBaseFolder(path: Union[str, Sequence[str]]) -> str:
#
if isinstance(path, str):
return ph_.realpath(ph_.join(*SRC_PATH_CMPS, path))
else:
return ph_.realpath(ph_.join(*SRC_PATH_CMPS, *path))
SCRIPT_NAME = ph_.splitext(PY_SCRIPT)[0]
PER_OS_DIST_FOLDER_PATH = ph_.join(DIST_ROOT_FOLDER, PER_OS_DIST_FOLDER)
PER_OS_and_SCRIPT_DIST_PATH = ph_.join(PER_OS_DIST_FOLDER_PATH, SCRIPT_NAME)
PER_OS_BUILD_FOLDER_PATH = ph_.join(BUILD_ROOT_FOLDER, OS_NAME)
PY_SCRIPT_PATH = InBaseFolder(PY_SCRIPT)
PARAMETERS_PATH = InBaseFolder(PARAMETERS)
BRICK_FOLDER_PATH = InBaseFolder(BRICK_FOLDER)
RES_FOLDER_PATH = InBaseFolder(ph_.join(*RES_PATH_CMPS))
FRANGI3_PREFIX = "frangi3"
if OS_NAME == "nt":
# In Wine on Linux, realpath does not resolve symbolic links (2020-02-03)
FRANGI3_PY_FOLDER = r"G:\home\eric\Code\brick\def\frangi3\frangi_py"
assert ph_.isdir(FRANGI3_PY_FOLDER)
else:
FRANGI3_PY_FOLDER = ph_.dirname(
ph_.realpath(ph_.join(BRICK_FOLDER_PATH, "processing", f"{FRANGI3_PREFIX}.py"))
)
DATA_ELEMENTS = (
(BRICK_FOLDER_PATH, BRICK_FOLDER),
(PARAMETERS_PATH, "."),
)
BINARY_ELEMENTS = (
(f"{ph_.join(FRANGI3_PY_FOLDER, FRANGI3_PREFIX)}-{OS_NAME}.so", "."),
)
arguments = [
f"--distpath={PER_OS_DIST_FOLDER_PATH}",
f"--specpath={PER_OS_BUILD_FOLDER_PATH}",
f"--workpath={PER_OS_BUILD_FOLDER_PATH}",
"--noconfirm",
"--onedir",
PY_SCRIPT_PATH,
]
for elm_type, elm_list in zip(
("--add-data", "--add-binary"), (DATA_ELEMENTS, BINARY_ELEMENTS)
):
for element in elm_list:
arguments.insert(
arguments.__len__() - 1, f"{elm_type}={element[0]}{PATH_SEP}{element[1]}"
)
for resource in gb_.glob(ph_.join(RES_FOLDER_PATH, f"{RES_PREFIX}*.tif")):
arguments.insert(
arguments.__len__() - 1, f"--add-data={resource}{PATH_SEP}{DST_RES_FOLDER}"
)
dependencies = []
with open(DEP_LIST_DOC) as doc_accessor:
for line in doc_accessor:
if (line.__len__() == 0) or line.isspace() or line.startswith("#"):
continue
module = line.split("=")[0].rstrip()
dependencies.append(module)
deps_info = sp_.check_output(
[sy_.executable, "-m", "pip", "show", module]
).decode()
sub_deps = ()
for dep_info in deps_info.split("\n")[:-1]:
# [:-1]: Excludes the last, empty line
if dep_info.startswith(REQUIREMENTS_MARKER):
dep_info_wo_marker = dep_info[REQUIREMENTS_MARKER.__len__() :]
if "," in dep_info_wo_marker:
sub_deps = dep_info_wo_marker.split(",")
elif not dep_info_wo_marker.isspace():
sub_deps = [dep_info_wo_marker]
break
for sub_dep in sub_deps:
dependencies.append(sub_dep.strip())
dependencies = sorted(set(dependencies))
for dep in dependencies:
arguments.insert(arguments.__len__() - 1, f"--hidden-import={dep}")
excluded_modules_w_version = sp_.check_output(
[sy_.executable, "-m", "pip", "freeze"]
).decode()
excluded_modules = []
for module in excluded_modules_w_version.split("\n")[:-1]:
# [:-1]: Excludes the last, empty line
excluded_modules.append(module.split("=")[0])
excluded_modules = set(excluded_modules) - set(dependencies)
for module in excluded_modules:
arguments.insert(arguments.__len__() - 1, f"--exclude-module={module}")
for readme in gb_.glob(InBaseFolder(f"{README_PREFIX}*")):
readme = ph_.realpath(readme)
arguments.insert(arguments.__len__() - 1, f"--add-data={readme}{PATH_SEP}.")
if OS_NAME == "nt":
arguments.insert(arguments.__len__() - 1, "--console")
print("--- Launching PyInstaller with arguments:")
pp_.pprint(arguments)
PyInstaller.__main__.run(arguments)
print("--- Moving/copying elements into place")
if OS_NAME == "nt":
sh_.copy(f"{SCRIPT_NAME}.bat", PER_OS_DIST_FOLDER_PATH)
else:
print(f" /!\\ TODO: MAKE a SHELL SCRIPT for {OS_NAME.upper()} and COPY IT")
sh_.move(ph_.join(PER_OS_and_SCRIPT_DIST_PATH, PARAMETERS), PER_OS_DIST_FOLDER_PATH)
sh_.move(ph_.join(PER_OS_and_SCRIPT_DIST_PATH, DST_RES_FOLDER), PER_OS_DIST_FOLDER_PATH)
for readme in gb_.glob(ph_.join(PER_OS_and_SCRIPT_DIST_PATH, f"{README_PREFIX}*")):
sh_.move(readme, PER_OS_DIST_FOLDER_PATH)
print("--- Creating ZIP archive")
with zp_.ZipFile(
ph_.join(DIST_ROOT_FOLDER, f"{PER_OS_DIST_FOLDER}.zip"),
mode="w",
compression=zp_.ZIP_DEFLATED,
compresslevel=9,
) as archive:
for folder, _, documents in os_.walk(PER_OS_DIST_FOLDER_PATH):
for document in documents:
doc_path = ph_.join(folder, document)
archive.write(
doc_path, arcname=ph_.relpath(doc_path, start=DIST_ROOT_FOLDER)
)