Repository navigation
Expand file tree
/
Copy pathbuild_app.spec
More file actions
178 lines (167 loc) · 6.73 KB
/
Copy pathbuild_app.spec
File metadata and controls
178 lines (167 loc) · 6.73 KB
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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# ─────────────────────────────────────────────────────────────────────────────
# FIMsim — PyInstaller spec file
# Usage (from the lisflood_prep_app/ directory):
# pip install pyinstaller
# pyinstaller build_app.spec
#
# Output: dist/FIMsim/ (folder mode — fast start-up, easiest to distribute)
# Zip that folder on Mac/Linux, or run Inno Setup on Windows.
# ─────────────────────────────────────────────────────────────────────────────
import sys
from pathlib import Path
from PyInstaller.utils.hooks import collect_all, collect_data_files, collect_submodules
ROOT = Path(SPECPATH) # absolute path to lisflood_prep_app/
IS_WIN = sys.platform == "win32"
IS_MAC = sys.platform == "darwin"
# ── Collect packages that ship native binaries or data ────────────────────────
datas = []
binaries = []
hidden = []
for pkg in [
"rasterio", # GDAL binaries + proj
"pyproj", # PROJ data (proj.db, etc.)
"pyogrio", # GDAL-based vector I/O (geopandas backend)
"shapely", # GEOS binaries
"geopandas", # data files
"matplotlib", # fonts, style sheets, backends
"h5py", # HDF5 shared lib
"gmsh", # gmsh shared lib + API
"scipy", # LAPACK/BLAS etc.
"numpy", # OpenBLAS
"xarray",
"zarr",
"s3fs",
"fsspec",
"numcodecs",
"pynhd",
"pygeoogc",
"requests",
"certifi", # SSL certs (important for HTTPS downloads)
"charset_normalizer",
"pandas",
"openpyxl",
"PyQt6",
"matplotlib.backends",
"netCDF4", # NWM forecast downloads
"h5netcdf",
"fimserve", # OWP HAND-FIM mode
"curve2flood", # ARC-Curve2Flood mode
"arc", # automated-rating-curve (ARC-Curve2Flood mode)
"osgeo", # GDAL python bindings (used by arc + curve2flood)
]:
d, b, h = collect_all(pkg)
datas += d
binaries += b
hidden += h
# ── App data files ────────────────────────────────────────────────────────────
datas += [
# GeoJSON boundary files
(str(ROOT / "data" / "us_states.geojson"), "data"),
(str(ROOT / "data" / "us_huc6.geojson"), "data"),
(str(ROOT / "data" / "us_huc8.geojson"), "data"),
# App assets (logo, workflow diagram)
(str(ROOT / "assets"), "assets"),
# Bundled HTML user manual (opened by every mode's Help button)
(str(ROOT / "manual_site"), "manual_site"),
]
# ── Hidden imports that collect_all misses ────────────────────────────────────
hidden += [
# PyQt6 extras
"PyQt6.QtWidgets", "PyQt6.QtCore", "PyQt6.QtGui",
"PyQt6.QtNetwork", "PyQt6.QtSvg", "PyQt6.QtPrintSupport",
# matplotlib Qt backend
"matplotlib.backends.backend_qtagg",
"matplotlib.backends.backend_qt",
# scipy sub-modules used by the app
"scipy.ndimage", "scipy.ndimage._ni_support",
"scipy.spatial", "scipy.spatial.distance",
"scipy.interpolate",
# network / async
"aiohttp", "aiofiles",
# stdlib
"json", "pathlib", "ssl", "math", "concurrent.futures",
"urllib.request", "urllib.parse", "datetime", "shutil",
"email", "email.mime", "email.mime.multipart",
"xml", "xml.etree", "xml.etree.ElementTree",
# zarr / numcodecs internals
"numcodecs.blosc", "numcodecs.lz4", "numcodecs.zstd",
"numcodecs.compat_ext",
# xarray
"xarray.backends", "xarray.backends.zarr",
# pynhd / pygeoogc
"pynhd.core", "pynhd.nhdplus_derived", "pynhd.waterdata",
"pygeoogc.core",
# netCDF4 / h5netcdf (NWM retrospective downloads)
"netCDF4", "h5netcdf", "h5netcdf.legacyapi",
# aiofiles (async file I/O used by pynhd / pygeoogc)
"aiofiles",
# App modules — guarantee all core + gui submodules are included
"core.arc_manning", "core.arc_orchestrate", "core.arc_run",
"core.triton_manning", "core.triton_bc", "core.triton_hydro",
"core.triton_cfg", "core.triton_orchestrate",
"core.orchestrate", "core.bci", "core.bdy", "core.par",
"core.dem", "core.manning", "core.nlcd", "core.nwm_discharge",
"core.flowline_mode", "core.multi_aoi", "core.aoi", "core.context",
"core.export", "core.hand", "core.crs_utils",
"core.nhd_utils", "core.river_lookup", "core.aoi_info",
"core.nwm_forecast", "core.FIMserv_api",
# OWP HAND-FIM / ARC runtime packages (dynamically imported)
"fimserve.plot.nwmfid", "curve2flood", "arc",
]
# ── PyInstaller Analysis ──────────────────────────────────────────────────────
a = Analysis(
[str(ROOT / "main.py")],
pathex=[str(ROOT)],
binaries=binaries,
datas=datas,
hiddenimports=list(set(hidden)),
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[
"tkinter", "IPython", "jupyter", "notebook",
"PyQt5", "PySide2", "PySide6", # avoid Qt version conflicts
"wx", "gi", # other GUI toolkits
],
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data)
exe = EXE(
pyz,
a.scripts,
[],
exclude_binaries=True,
name="FIMsim",
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=False, # UPX can break Qt/GDAL binaries — keep off
console=False, # no terminal window
argv_emulation=False,
target_arch=None,
icon=str(ROOT / "assets" / "icon.ico") if (ROOT / "assets" / "icon.ico").exists() else None,
)
coll = COLLECT(
exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=False,
name="FIMsim",
)
# ── macOS .app bundle ─────────────────────────────────────────────────────────
if IS_MAC:
app = BUNDLE(
coll,
name="FIMsim.app",
icon=str(ROOT / "assets" / "icon.icns") if (ROOT / "assets" / "icon.icns").exists() else None,
bundle_identifier="edu.ua.fimsim",
info_plist={
"CFBundleDisplayName": "FIMsim",
"CFBundleShortVersionString": "1.1.0",
"CFBundleVersion": "1.1.0",
"NSHighResolutionCapable": True,
"NSRequiresAquaSystemAppearance": False,
},
)