-
Notifications
You must be signed in to change notification settings - Fork 71
Expand file tree
/
Copy pathproject.spec
More file actions
520 lines (464 loc) · 17.7 KB
/
project.spec
File metadata and controls
520 lines (464 loc) · 17.7 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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
# -*- mode: python ; coding: utf-8 -*-
import sys
import os
from PyInstaller.utils.hooks import collect_all, collect_data_files, collect_submodules
from pathlib import Path
import glob
import importlib.util
import sysconfig
block_cipher = None
# Collect all data and binaries from critical packages
datas = []
binaries = []
hiddenimports = []
# Function to detect conda environment and CUDA version
def get_conda_cuda_libs():
"""Detect and collect CUDA/cuDNN libraries from the active conda environment"""
cuda_binaries = []
# Get the conda environment path
conda_prefix = os.environ.get('CONDA_PREFIX', sys.prefix)
print(f"Conda environment detected: {conda_prefix}")
# Detect CUDA version from environment path or libraries
cuda_version = None
if 'cu118' in conda_prefix or 'cuda118' in conda_prefix:
cuda_version = '11.8'
elif 'cu122' in conda_prefix or 'cuda122' in conda_prefix:
cuda_version = '12.2'
elif 'cu128' in conda_prefix or 'cuda128' in conda_prefix:
cuda_version = '12.8'
else:
# Try to detect from cudart version
cudart_files = glob.glob(os.path.join(conda_prefix, 'lib', 'libcudart.so.*'))
if cudart_files:
cudart_file = os.path.basename(cudart_files[0])
if '11.8' in cudart_file:
cuda_version = '11.8'
elif '12.2' in cudart_file:
cuda_version = '12.2'
elif '12.8' in cudart_file:
cuda_version = '12.8'
print(f"Detected CUDA version: {cuda_version}")
# Library paths to check - Windows uses different paths than Linux
if sys.platform == 'win32':
lib_dirs = [
os.path.join(conda_prefix, 'Library', 'bin'), # Primary location for Windows DLLs
os.path.join(conda_prefix, 'bin'), # Alternative location
os.path.join(conda_prefix, 'DLLs'), # Python DLLs location
]
# Also check Python site-packages for ONNX Runtime libraries
import site
site_packages = site.getsitepackages()
for sp in site_packages:
if conda_prefix in sp:
onnx_capi_path = os.path.join(sp, 'onnxruntime', 'capi')
if os.path.exists(onnx_capi_path):
lib_dirs.append(onnx_capi_path)
print(f" Added ONNX Runtime path: {onnx_capi_path}")
# Windows CUDA library patterns with version numbers
cuda_libs_patterns = [
# CUDA Runtime
'cudart64_*.dll',
'cudart32_*.dll', # 32-bit variant if exists
# cuBLAS
'cublas64_*.dll',
'cublasLt64_*.dll',
# cuDNN libraries - critical for deep learning
'cudnn64_*.dll',
'cudnn_ops_infer64_*.dll',
'cudnn_ops_train64_*.dll',
'cudnn_cnn_infer64_*.dll',
'cudnn_cnn_train64_*.dll',
'cudnn_adv_infer64_*.dll',
'cudnn_adv_train64_*.dll',
# For newer cuDNN versions (9.x)
'cudnn*.dll',
# cuFFT
'cufft64_*.dll',
'cufftw64_*.dll',
# cuRAND
'curand64_*.dll',
# cuSPARSE
'cusparse64_*.dll',
# cuSOLVER
'cusolver64_*.dll',
'cusolverMg64_*.dll',
# NVRTC
'nvrtc64_*.dll',
'nvrtc-builtins64_*.dll',
# NVIDIA Tools Extension
'nvToolsExt64_*.dll',
# Additional potential libraries
'nppc64_*.dll',
'nppif64_*.dll',
'npps64_*.dll',
# ONNX Runtime GPU dependencies (important!)
'onnxruntime_providers_cuda.dll',
'onnxruntime_providers_tensorrt.dll',
'onnxruntime_providers_shared.dll',
# Python binding for ONNX Runtime
'onnxruntime_pybind11_state*.pyd',
]
else:
# Linux/Unix library paths
lib_dirs = [
os.path.join(conda_prefix, 'lib'),
os.path.join(conda_prefix, 'lib', 'stubs'),
]
# Also check Python site-packages for ONNX Runtime libraries
# This is crucial for finding libonnxruntime_providers_cuda.so, etc.
import site
# Try to get the site-packages directory in the conda environment
python_version = f"python{sys.version_info.major}.{sys.version_info.minor}"
site_packages_paths = [
os.path.join(conda_prefix, 'lib', python_version, 'site-packages'),
os.path.join(conda_prefix, 'lib', 'python3.10', 'site-packages'), # Fallback for CI
os.path.join(conda_prefix, 'lib', 'python3.11', 'site-packages'), # Alternative version
]
for sp_path in site_packages_paths:
onnx_capi_path = os.path.join(sp_path, 'onnxruntime', 'capi')
if os.path.exists(onnx_capi_path):
lib_dirs.append(onnx_capi_path)
print(f" Added ONNX Runtime path: {onnx_capi_path}")
break
# Linux CUDA library patterns
cuda_libs_patterns = [
'libcudart.so*',
'libcublas.so*',
'libcublasLt.so*',
'libcudnn*.so*',
'libcufft.so*',
'libcufftw.so*',
'libcurand.so*',
'libcusparse.so*',
'libcusolver.so*',
'libnvrtc.so*',
'libnvToolsExt.so*',
# ONNX Runtime GPU dependencies
'libonnxruntime_providers_cuda.so*',
'libonnxruntime_providers_tensorrt.so*',
'libonnxruntime_providers_shared.so*',
# Also check without 'lib' prefix (for files in capi directory)
'onnxruntime_providers_cuda.so*',
'onnxruntime_providers_tensorrt.so*',
'onnxruntime_providers_shared.so*',
# Python extension module
'onnxruntime_pybind11_state*.so',
]
# Collect all matching libraries
for lib_dir in lib_dirs:
if not os.path.exists(lib_dir):
continue
for pattern in cuda_libs_patterns:
for lib_file in glob.glob(os.path.join(lib_dir, pattern)):
if os.path.isfile(lib_file) and not os.path.islink(lib_file):
# Add to binaries list with destination directory
dest_dir = '.'
if 'stubs' in lib_file:
dest_dir = 'stubs'
cuda_binaries.append((lib_file, dest_dir))
print(f" Including CUDA library: {os.path.basename(lib_file)}")
return cuda_binaries
def _tree(root, prefix=''):
result = []
root = os.path.normpath(root)
for dirpath, _dirnames, filenames in os.walk(root):
rel = os.path.relpath(dirpath, root)
dest = prefix if rel == '.' else os.path.join(prefix, rel)
for filename in filenames:
result.append((os.path.join(dirpath, filename), dest))
return result
def get_rocm_sdk_datas():
"""Collect ROCm runtime + libraries wheels if installed (Windows only).
We bundle these into the app so end users don't need a separate ROCm install.
This follows Strategy A from AMD_ROCM_ACTION_PLAN.md: one libs family per ZIP.
"""
if sys.platform != 'win32':
return []
# Build a list of site-packages roots to probe.
roots: list[str] = []
conda_prefix = os.environ.get('CONDA_PREFIX', sys.prefix)
roots.append(os.path.join(conda_prefix, 'Lib', 'site-packages'))
try:
import site
roots.extend(site.getsitepackages())
except Exception:
pass
try:
paths = sysconfig.get_paths()
roots.extend([paths.get('purelib', ''), paths.get('platlib', '')])
except Exception:
pass
# De-duplicate while preserving order.
seen = set()
uniq_roots: list[Path] = []
for r in roots:
if not r:
continue
if r in seen:
continue
seen.add(r)
p = Path(r)
if p.exists():
uniq_roots.append(p)
core_dir: Path | None = None
libs_dir: Path | None = None
for sp in uniq_roots:
if core_dir is None:
candidate = sp / '_rocm_sdk_core'
if candidate.exists():
core_dir = candidate
if libs_dir is None:
# Prefer a pre-renamed/custom directory if present.
candidate_custom = sp / '_rocm_sdk_libraries_custom'
if candidate_custom.exists():
libs_dir = candidate_custom
else:
# Otherwise pick the first installed family wheel.
matches = sorted(sp.glob('_rocm_sdk_libraries_*'))
for m in matches:
if m.name == '_rocm_sdk_libraries_custom':
continue
libs_dir = m
break
rocm_datas = []
if core_dir is not None:
core_bin = core_dir / 'bin'
if core_bin.exists():
print(f"Including ROCm core bin from: {core_bin}")
rocm_datas += _tree(str(core_bin), prefix=os.path.join('_rocm_sdk_core', 'bin'))
else:
print(f"Including ROCm core dir from: {core_dir}")
rocm_datas += _tree(str(core_dir), prefix='_rocm_sdk_core')
else:
print("ROCm core dir not found; skipping ROCm runtime bundling")
if libs_dir is not None:
# Always map into the directory name ctranslate2 expects on Windows.
print(f"Including ROCm libraries from: {libs_dir}")
rocm_datas += _tree(str(libs_dir), prefix='_rocm_sdk_libraries_custom')
else:
print("ROCm libraries dir not found; skipping ROCm libraries bundling")
return rocm_datas
# Collect CUDA/cuDNN libraries
cuda_binaries = get_conda_cuda_libs()
binaries += cuda_binaries
# Collect ROCm runtime + libraries if present (AMD HIP builds)
datas += get_rocm_sdk_datas()
# Collect CTranslate2 (the actual inference engine for faster-whisper)
try:
ctranslate2_datas, ctranslate2_binaries, ctranslate2_hiddenimports = collect_all('ctranslate2')
datas += ctranslate2_datas
binaries += ctranslate2_binaries
hiddenimports += ctranslate2_hiddenimports
except Exception as e:
# Don't hard-fail builds if CTranslate2 can't be imported on the builder
# (e.g., missing GPU driver). The module itself is still a dependency of
# infer.py and should be pulled in by Analysis; this only affects extras.
print(f"Warning: could not collect ctranslate2 extras: {e}")
# Collect faster-whisper
faster_whisper_datas, faster_whisper_binaries, faster_whisper_hiddenimports = collect_all('faster_whisper')
datas += faster_whisper_datas
binaries += faster_whisper_binaries
hiddenimports += faster_whisper_hiddenimports
# Collect transformers (needed for tokenizers)
transformers_datas, transformers_binaries, transformers_hiddenimports = collect_all('transformers')
datas += transformers_datas
binaries += transformers_binaries
hiddenimports += transformers_hiddenimports
# Collect onnxruntime for VAD model
# Note: The Python module is always 'onnxruntime' regardless of whether
# you installed onnxruntime-gpu or onnxruntime via pip
onnx_collected = False
onnx_package = 'onnxruntime' # Module name is always 'onnxruntime'
try:
onnx_datas, onnx_binaries, onnx_hiddenimports = collect_all(onnx_package)
datas += onnx_datas
binaries += onnx_binaries
hiddenimports += onnx_hiddenimports
print(f"Collected {onnx_package} successfully")
onnx_collected = True
# Explicitly add ONNX Runtime capi libraries if not already included
try:
import importlib.util
spec = importlib.util.find_spec(onnx_package)
if spec and spec.origin:
onnx_path = os.path.dirname(spec.origin)
capi_path = os.path.join(onnx_path, 'capi')
if os.path.exists(capi_path):
print(f" Found ONNX Runtime capi directory: {capi_path}")
for file in os.listdir(capi_path):
if file.endswith(('.so', '.dll', '.pyd', '.dylib')):
src = os.path.join(capi_path, file)
# Add to root directory of the bundle
binaries.append((src, '.'))
print(f" Added capi library: {file}")
except Exception as e:
print(f" Warning: Could not collect capi libraries: {e}")
except Exception as e:
print(f"Could not collect {onnx_package}: {e}")
onnx_collected = False
if not onnx_collected:
print("WARNING: Could not collect any ONNX Runtime package")
# Collect librosa for audio processing
librosa_datas, librosa_binaries, librosa_hiddenimports = collect_all('librosa')
datas += librosa_datas
binaries += librosa_binaries
hiddenimports += librosa_hiddenimports
# Add numpy
numpy_datas, numpy_binaries, numpy_hiddenimports = collect_all('numpy')
datas += numpy_datas
binaries += numpy_binaries
hiddenimports += numpy_hiddenimports
# Add other necessary packages
for package in ['pyjson5', 'scipy', 'soundfile', 'audioread', 'resampy', 'numba', 'av', 'tokenizers']:
try:
pkg_datas, pkg_binaries, pkg_hiddenimports = collect_all(package)
datas += pkg_datas
binaries += pkg_binaries
hiddenimports += pkg_hiddenimports
except:
pass
# Collect setuptools and pkg_resources data to fix missing modules
try:
from PyInstaller.utils.hooks import collect_data_files
setuptools_datas = collect_data_files('setuptools')
datas += setuptools_datas
pkg_resources_datas = collect_data_files('pkg_resources')
datas += pkg_resources_datas
except:
pass
# Explicitly collect backports module to fix ModuleNotFoundError
try:
backports_datas, backports_binaries, backports_hiddenimports = collect_all('backports')
datas += backports_datas
binaries += backports_binaries
hiddenimports += backports_hiddenimports
print("Collected backports module successfully")
except Exception as e:
print(f"Could not collect backports module: {e}")
# Try alternative collection method
try:
import backports
import os
backports_path = os.path.dirname(backports.__file__)
datas.append((backports_path, 'backports'))
print(f"Added backports from path: {backports_path}")
except:
print("Warning: backports module not found - may need to be installed")
# Add hidden imports for modules that might not be detected automatically
hiddenimports += [
'ctranslate2',
'transformers.models',
'transformers.models.whisper',
'transformers.tokenization_utils',
'transformers.tokenization_utils_base',
'tokenizers',
'tokenizers.implementations',
'tokenizers.models',
'tokenizers.pre_tokenizers',
'tokenizers.processors',
'onnxruntime.capi',
'onnxruntime.capi._pybind_state',
'librosa.core',
'librosa.feature',
'scipy.special._ufuncs_cxx',
'scipy.linalg._fblas',
'scipy.linalg._flapack',
'scipy.linalg._cythonized_array_utils',
'scipy.linalg._solve_toeplitz',
'scipy.linalg._matfuncs_sqrtm_triu',
'scipy.linalg._decomp_lu_cython',
'scipy.linalg._matfuncs_expm',
'scipy.linalg.cython_blas',
'scipy.linalg.cython_lapack',
'numba.core',
'numba.cuda',
'av.audio',
'av.container',
'av.stream',
'pkg_resources.extern',
'pkg_resources._vendor',
'packaging',
'packaging.version',
'packaging.specifiers',
'packaging.requirements',
'backports', # Fix for ModuleNotFoundError
'backports.functools_lru_cache', # Common backports module
'setuptools._vendor.jaraco', # Include jaraco modules
'setuptools._vendor.jaraco.text',
'setuptools._vendor.jaraco.context',
'setuptools._vendor.jaraco.functools',
'code', # For interactive console with --console option
'readline', # For better console experience (if available)
'rlcompleter', # For tab completion in console
]
# Add ONNX Runtime provider modules only when present.
def _maybe_add_hiddenimport(mod: str) -> None:
try:
if importlib.util.find_spec(mod) is not None:
hiddenimports.append(mod)
except Exception:
pass
_maybe_add_hiddenimport('onnxruntime.capi.onnxruntime_providers_cuda')
_maybe_add_hiddenimport('onnxruntime.capi.onnxruntime_providers_tensorrt')
_maybe_add_hiddenimport('onnxruntime.capi.onnxruntime_providers_shared')
# Add project data files
# Note: models directory is excluded and handled separately by CI
datas += [
('src/faster_whisper_transwithai_chickenrice', 'faster_whisper_transwithai_chickenrice'),
('locales', 'locales'), # Include the locales directory with translations
]
a = Analysis(
['infer.py'],
pathex=[],
binaries=binaries,
datas=datas,
hiddenimports=hiddenimports,
hookspath=[], # PyInstaller hooks contrib should be auto-detected
hooksconfig={},
runtime_hooks=['runtime_hook.py'], # Add runtime hook to set KMP_DUPLICATE_LIB_OK
excludes=[
'matplotlib',
'tkinter',
'PyQt5',
'PyQt6',
'PySide2',
'PySide6',
'notebook',
'jupyter',
'IPython',
'pytest',
],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
[],
exclude_binaries=True,
name='infer',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=False,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon='transwithai.ico' if os.path.exists('transwithai.ico') else None,
)
coll = COLLECT(
exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=False,
upx_exclude=[],
name='faster_whisper_transwithai_chickenrice',
)