-
Notifications
You must be signed in to change notification settings - Fork 25
Expand file tree
/
Copy pathbuild.bat
More file actions
executable file
·464 lines (398 loc) · 14.7 KB
/
Copy pathbuild.bat
File metadata and controls
executable file
·464 lines (398 loc) · 14.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
@echo off
setlocal enabledelayedexpansion
REM Enhanced build script for p4-mcp-server (Windows Batch)
REM Creates virtual environment, installs dependencies, and builds executable
REM Parse command line arguments first
set COMMAND=%1
if "%COMMAND%"=="" set COMMAND=build
REM Check for help commands immediately
if /i "%COMMAND%"=="help" goto :show_usage
if /i "%COMMAND%"=="-h" goto :show_usage
if /i "%COMMAND%"=="--help" goto :show_usage
REM Detect available Python command
set PYTHON_CMD=
where py >nul 2>&1 && set PYTHON_CMD=py
if not defined PYTHON_CMD (
where python >nul 2>&1 && set PYTHON_CMD=python
)
if not defined PYTHON_CMD (
where python3 >nul 2>&1 && set PYTHON_CMD=python3
)
if not defined PYTHON_CMD (
echo [ERROR] Python 3 is not installed or not in PATH
exit /b 1
)
set "SCRIPT_DIR=%~dp0"
REM Version configuration
REM pyproject.toml's [project] version is the single source of truth.
set "VERSION_FILE=%SCRIPT_DIR%p4mcp\_version.py"
if "%RELEASE_VERSION%"=="" (
set "PYPROJECT_FILE=%SCRIPT_DIR%pyproject.toml"
if exist "!PYPROJECT_FILE!" (
set "VER_RAW="
for /f "usebackq tokens=1* delims==" %%A in (`findstr /r /c:"^version *= *" "!PYPROJECT_FILE!"`) do (
if not defined VER_RAW set VER_RAW=%%B
)
if defined VER_RAW (
set "VER_STR=!VER_RAW: =!"
set "VER_STR=!VER_STR:'=!"
set "VER_STR=!VER_STR:"=!"
for /f "tokens=1 delims=#;" %%V in ("!VER_STR!") do set "RELEASE_VERSION=%%V"
) else (
call :log_warning "Could not parse version from pyproject.toml"
)
) else (
call :log_warning "Version file not found: !PYPROJECT_FILE!"
)
)
REM Fail loudly rather than baking a stale default: a malformed pyproject.toml
REM or a missing version key must stop the build, not produce a wrong-version
REM binary. Set RELEASE_VERSION in the environment to override the source.
if "%RELEASE_VERSION%"=="" (
call :log_error "Could not determine version from pyproject.toml"
exit /b 1
)
set VERSION=%RELEASE_VERSION%
REM Version baked into the frozen binary. Defaults to RELEASE_VERSION, but CI
REM (buildtools\buildscripts\compile.bat) overrides it with a build-number-qualified
REM value while keeping RELEASE_VERSION for archive naming and p4python matching.
if not defined BAKE_VERSION set BAKE_VERSION=%RELEASE_VERSION%
set ARCHIVE_NAME=p4-mcp-server-%VERSION%.zip
set VENV_DIR=%SCRIPT_DIR%.venv
set EXECUTABLE_PATH=%SCRIPT_DIR%dist\p4-mcp-server\p4-mcp-server.exe
set ARCHIVE_NAME=p4-mcp-server-%VERSION%.zip
REM Shift to get remaining arguments for executable
shift
set EXECUTABLE_ARGS=
:parse_args
if "%1"=="" goto :args_done
if "%1"=="--" (
shift
goto :parse_args
)
set EXECUTABLE_ARGS=%EXECUTABLE_ARGS% %1
shift
goto :parse_args
:args_done
REM Main script logic with improved logging
if /i "%COMMAND%"=="build" (
call :log_info "Selected command: build"
goto :full_build
)
if /i "%COMMAND%"=="run" (
call :log_info "Selected command: run"
goto :run_executable
)
if /i "%COMMAND%"=="clean" (
call :log_info "Selected command: clean"
goto :clean_all
)
if /i "%COMMAND%"=="setup" (
call :log_info "Selected command: setup"
goto :setup_only
)
if /i "%COMMAND%"=="package" (
call :log_info "Selected command: package"
goto :package_build
)
echo [91m❌ Unknown command: %COMMAND%[0m
goto :show_usage
:show_usage
echo Usage: build.bat [build^|run^|clean^|setup^|package]
echo.
echo Commands:
echo build Setup venv, install deps, and build executable
echo run Run the built executable (builds first if needed)
echo clean Clean build artifacts and virtual environment
echo setup Only setup virtual environment and install dependencies
echo package Build and create versioned zip archive (cleans build/dist/.venv after successful packaging)
echo.
echo Examples:
echo build.bat build
echo build.bat run
echo build.bat setup
echo build.bat clean
echo build.bat package
goto :eof
:log_info
echo [INFO] %~1
goto :eof
:log_success
echo [SUCCESS] %~1
goto :eof
:log_warning
echo [WARNING] %~1
goto :eof
:log_error
echo [ERROR] %~1
goto :eof
:check_python
call :log_info "Checking Python installation..."
%PYTHON_CMD% --version >nul 2>&1
if errorlevel 1 (
call :log_error "Python 3 is not installed or not in PATH"
exit /b 1
)
for /f "tokens=2" %%i in ('"%PYTHON_CMD% --version 2>&1"') do set PYTHON_VERSION=%%i
call :log_info "Using Python %PYTHON_VERSION%"
call :log_info "Building version: %VERSION%"
goto :eof
:setup_venv
call :log_info "Setting up virtual environment..."
REM Remove existing venv if it exists
if exist "%VENV_DIR%" (
call :log_warning "Removing existing virtual environment..."
rmdir /s /q "%VENV_DIR%"
)
REM Create new virtual environment
call :log_info "Creating virtual environment..."
%PYTHON_CMD% -m venv "%VENV_DIR%"
if errorlevel 1 (
call :log_error "Failed to create virtual environment"
exit /b 1
)
REM Upgrade pip
call :log_info "Upgrading pip..."
"%VENV_DIR%\Scripts\python.exe" -m pip install --upgrade pip
if errorlevel 1 (
call :log_error "Failed to upgrade pip"
exit /b 1
)
call :log_success "Virtual environment created and activated"
goto :eof
:install_dependencies
call :log_info "Installing dependencies from requirements.txt..."
REM Determine latest p4python version matching RELEASE_VERSION
set REL_VERSION=%RELEASE_VERSION%
if "%REL_VERSION%"=="" set REL_VERSION=2025.1
if not exist "requirements.txt" (
call :log_error "requirements.txt file not found in current directory"
exit /b 1
)
REM Install dependencies
"%VENV_DIR%\Scripts\pip.exe" install -r requirements.txt
if errorlevel 1 (
call :log_error "Failed to install dependencies"
exit /b 1
)
set PYTHON_EXE=%VENV_DIR%\Scripts\python.exe
set PIP_EXE=%VENV_DIR%\Scripts\pip.exe
REM Check if pip exists before trying to use it
if not exist "%PIP_EXE%" (
call :log_warning "Pip not found in virtual environment. Skipping p4python version detection."
set P4PYTHON_VERSION=
) else (
REM Try to get p4python versions - use a simpler approach
"%PIP_EXE%" index versions p4python >temp_pip_output.txt 2>nul
if errorlevel 1 (
call :log_warning "Could not query p4python versions. Skipping version detection."
set P4PYTHON_VERSION=
if exist temp_pip_output.txt del temp_pip_output.txt
) else (
REM Parse the output to find matching version
"%PYTHON_EXE%" -c "import re; rel='%REL_VERSION%'; content=open('temp_pip_output.txt').read(); vers=re.findall(r'([0-9]+\.[0-9]+\.[0-9]+)', content); match=[v for v in vers if v.startswith(rel)]; result=sorted(match)[-1] if match else ''; print(result)" > temp_version.txt 2>nul
if errorlevel 1 (
call :log_warning "Could not parse p4python versions. Skipping version detection."
set P4PYTHON_VERSION=
) else (
set /p P4PYTHON_VERSION=<temp_version.txt
)
if exist temp_pip_output.txt del temp_pip_output.txt
if exist temp_version.txt del temp_version.txt
)
)
echo Release version: %REL_VERSION%
echo P4Python version: %P4PYTHON_VERSION%
REM Install p4python if version found
if "%P4PYTHON_VERSION%"=="" (
call :log_warning "Could not determine P4Python version from release. Building with latest p4python version"
) else (
"%VENV_DIR%\Scripts\pip.exe" install p4python==%P4PYTHON_VERSION%
if errorlevel 1 (
call :log_error "Failed to install p4python"
exit /b 1
)
)
REM Check if PyInstaller is installed
"%VENV_DIR%\Scripts\python.exe" -c "import PyInstaller" >nul 2>&1
if errorlevel 1 (
call :log_info "Installing PyInstaller..."
"%VENV_DIR%\Scripts\pip.exe" install pyinstaller
if errorlevel 1 (
call :log_error "Failed to install PyInstaller"
exit /b 1
)
)
call :log_success "Dependencies installed successfully"
goto :eof
:build_executable
call :log_info "Building p4-mcp-server with PyInstaller..."
REM Check if the spec file exists
if not exist "p4-mcp-server.spec" (
call :log_error "p4-mcp-server.spec file not found in current directory"
exit /b 1
)
REM Clean previous build artifacts (but keep venv)
call :log_info "Cleaning previous build artifacts..."
if exist "build" rmdir /s /q "build"
if exist "dist" rmdir /s /q "dist"
REM Remove the "P4MCP.spec" and binary if they exist
if exist "P4MCP.spec" (
call :log_info "Removing existing P4MCP.spec..."
del "P4MCP.spec"
)
if exist "p4mcp\telemetry\P4MCP.exe" (
call :log_info "Removing existing P4MCP binary..."
del "p4mcp\telemetry\P4MCP.exe"
)
REM Build standalone binary for consent_ui.py
call :log_info "Building standalone binary for consent_ui.py..."
"%VENV_DIR%\Scripts\python.exe" -m PyInstaller --onefile --noconsole --distpath p4mcp\telemetry --name "P4MCP" p4mcp\telemetry\consent_ui.py
if errorlevel 1 (
call :log_error "Failed to build consent_ui binary"
exit /b 1
)
if exist "p4mcp\telemetry\P4MCP.exe" (
call :log_success "Standalone P4MCP binary created at p4mcp\telemetry\P4MCP.exe"
) else (
call :log_error "Failed to build consent_ui binary"
exit /b 1
)
REM Bake the version into _version.py so the frozen binary reports the right
REM version (importlib.metadata can't read package metadata inside a PyInstaller
REM bundle). Batch has no trap, so :restore_version is invoked explicitly before
REM every exit path below (success and failure). The baked file mirrors the
REM build.sh structure: triple-quoted docstring followed by the assignment.
if exist "%VERSION_FILE%" (
REM A leftover .bak from a previously interrupted run means _version.py is
REM already baked; restore it first so the new backup is the canonical form.
if exist "%VERSION_FILE%.bak" move /y "%VERSION_FILE%.bak" "%VERSION_FILE%" >nul
call :log_info "Baking version %BAKE_VERSION% into %VERSION_FILE% for the frozen build..."
copy /y "%VERSION_FILE%" "%VERSION_FILE%.bak" >nul
> "%VERSION_FILE%" echo """Single source of truth for the package version.
>> "%VERSION_FILE%" echo.
>> "%VERSION_FILE%" echo This file was rewritten by build.bat to bake the static version into the frozen
>> "%VERSION_FILE%" echo PyInstaller binary. build.bat restores the importlib.metadata form once done.
>> "%VERSION_FILE%" echo """
>> "%VERSION_FILE%" echo __version__ = "%BAKE_VERSION%"
)
REM Run PyInstaller for main app
call :log_info "Running PyInstaller for main app..."
"%VENV_DIR%\Scripts\python.exe" -m PyInstaller p4-mcp-server.spec
if errorlevel 1 (
call :restore_version
call :log_error "PyInstaller build failed"
exit /b 1
)
call :restore_version
call :log_info "PyInstaller build completed. Checking for executable..."
REM Check if build was successful
if exist "%EXECUTABLE_PATH%" (
call :log_success "Build successful!"
REM Only show this message if not packaging
if /i not "%COMMAND%"=="package" (
call :log_info "Executable created at: %EXECUTABLE_PATH%"
echo.
call :log_info "To run the server:"
echo .\dist\p4-mcp-server\p4-mcp-server.exe [arguments]
echo.
)
) else (
call :log_error "Build failed!"
exit /b 1
)
goto :eof
:restore_version
REM Restore _version.py to its importlib.metadata form from the backup.
if exist "%VERSION_FILE%.bak" (
move /y "%VERSION_FILE%.bak" "%VERSION_FILE%" >nul
)
goto :eof
:create_package
call :log_info "Creating versioned zip archive %ARCHIVE_NAME% ..."
if not exist "%EXECUTABLE_PATH%" (
call :log_error "Executable not found. Please build first."
exit /b 1
)
set ARCHIVE_PATH=%SCRIPT_DIR%%ARCHIVE_NAME%
REM Remove existing archive if it exists
if exist "%ARCHIVE_PATH%" (
call :log_warning "Removing existing archive: %ARCHIVE_NAME%"
del /f "%ARCHIVE_PATH%"
)
REM Create zip archive with only the executable
powershell -Command "Compress-Archive -Path '%SCRIPT_DIR%dist\p4-mcp-server' -DestinationPath '%ARCHIVE_PATH%' -Force"
if errorlevel 1 (
call :log_error "Failed to create package"
exit /b 1
)
if exist "%ARCHIVE_PATH%" (
call :log_success "Package %ARCHIVE_NAME% created successfully!"
call :log_info "Package Path: %ARCHIVE_PATH%"
REM Show file size (avoid 'Missing operand')
for %%i in ("%ARCHIVE_PATH%") do set "ARCHIVE_SIZE=%%~zi"
if defined ARCHIVE_SIZE (
set /a ARCHIVE_SIZE_KB=!ARCHIVE_SIZE!/1024
call :log_info "Package size: !ARCHIVE_SIZE_KB! KB"
) else (
call :log_warning "Could not determine package size."
)
REM Clean up build, dist, .venv directories and P4MCP.spec and P4MCP binary after successful packaging
if exist "build" rmdir /s /q "build"
if exist "dist" rmdir /s /q "dist"
if exist ".venv" rmdir /s /q ".venv"
if exist "P4MCP.spec" del "P4MCP.spec"
if exist "p4mcp\telemetry\P4MCP.exe" del "p4mcp\telemetry\P4MCP.exe"
call :log_info "build/dist and .venv directories cleaned up after packaging!"
) else (
call :log_error "Failed to create package!"
exit /b 1
)
goto :eof
:run_executable
if not exist "%EXECUTABLE_PATH%" (
call :log_warning "Executable not found. Building first..."
call :full_build
if errorlevel 1 exit /b 1
)
call :log_info "Running p4-mcp-server with arguments:%EXECUTABLE_ARGS%"
echo [INFO] Command: "%EXECUTABLE_PATH%" %EXECUTABLE_ARGS%
"%EXECUTABLE_PATH%" %EXECUTABLE_ARGS%
goto :eof
:clean_all
call :log_info "Cleaning all build artifacts and virtual environment..."
if exist "build" rmdir /s /q "build"
if exist "dist" rmdir /s /q "dist"
if exist "%VENV_DIR%" rmdir /s /q "%VENV_DIR%"
REM Also remove any existing archives
for %%f in ("%SCRIPT_DIR%p4-mcp-server-*.zip") do del /f "%%f"
call :log_success "Clean complete!"
goto :eof
:full_build
call :check_python
if errorlevel 1 exit /b 1
call :setup_venv
if errorlevel 1 exit /b 1
call :install_dependencies
if errorlevel 1 exit /b 1
call :build_executable
if errorlevel 1 exit /b 1
goto :eof
:setup_only
call :check_python
if errorlevel 1 exit /b 1
call :setup_venv
if errorlevel 1 exit /b 1
call :install_dependencies
if errorlevel 1 exit /b 1
call :log_success "Setup complete! Virtual environment ready at: %VENV_DIR%"
echo.
call :log_info "To activate the virtual environment manually, run:"
echo %VENV_DIR%\Scripts\activate.bat
goto :eof
:package_build
call :full_build
if errorlevel 1 exit /b 1
call :create_package
if errorlevel 1 exit /b 1
goto :eof