Skip to content

Commit 6a83cb3

Browse files
committed
Add C++ launcher build script and update project structure
1 parent b6896e9 commit 6a83cb3

File tree

9 files changed

+190
-61
lines changed

9 files changed

+190
-61
lines changed

.env

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
CI_VER=0.17.0
2-
CI_WPI=Winpython64-3.9.10.0dot.exe
2+
CI_WPI=Winpython64-3.9.10.0dot.exe
3+
VCVARS_PATH="C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat"

.gitignore

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,8 @@ doctmp/
99
releases/
1010
DataLab-WinPython*/
1111
dependencies*.txt
12-
nsis/icons/*.ico
13-
nsis/images/*.bmp
1412
packages/
13+
launchers/
1514
Win*.exe
1615
WPy*/
1716

.vscode/tasks.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,33 @@
7979
"clear": false
8080
}
8181
},
82+
{
83+
"label": "Build launchers",
84+
"type": "shell",
85+
"command": "cmd",
86+
"args": [
87+
"/c",
88+
"build_launchers.bat"
89+
],
90+
"options": {
91+
"cwd": "scripts",
92+
"env": {
93+
"UNATTENDED": "1",
94+
}
95+
},
96+
"group": {
97+
"kind": "build",
98+
"isDefault": true
99+
},
100+
"presentation": {
101+
"echo": true,
102+
"reveal": "always",
103+
"focus": false,
104+
"panel": "shared",
105+
"showReuseMessage": true,
106+
"clear": false
107+
}
108+
},
82109
{
83110
"label": "Build installer",
84111
"type": "shell",
@@ -116,6 +143,7 @@
116143
"dependsOn": [
117144
"Clean up",
118145
"Download",
146+
"Build launchers",
119147
"Build distribution",
120148
"Build installer",
121149
]

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ Technologies
3030
DataLab-WinPython is based on the following technologies:
3131

3232
* Windows batch scripts (to minimize prerequisites)
33-
* NSIS for the launcher executable
33+
* C++ for the launcher executable (to minimize dependencies)
3434
* WiX Toolset for the installer
3535
* WinPython Python distribution
3636
* Python package manager `pip`

nsis/launcher.nsi

Lines changed: 0 additions & 44 deletions
This file was deleted.

scripts/build_distribution.bat

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -62,19 +62,9 @@ for /d %%d in (patches\*) do (
6262
@REM rd /s /q "%%d"
6363
@REM )
6464

65-
@REM Create additional launchers
65+
@REM Copy launchers (generated by build_launchers.bat) to the distribution
6666
@REM ===========================================================================
67-
@REM Iterate over all .bat files in "executables" folder:
68-
set NSIS_WORKDIR=$EXEDIR\scripts
69-
set NSIS_COMMAND=wscript.exe
70-
for %%f in (executables\*.bat) do (
71-
@REM Copy each .bat file to the "scripts" folder:
72-
copy "executables\%%~nxf" "%ROOTPATH%\dist\%CI_DST%\scripts"
73-
@REM Set NSIS_ICON, NSIS_OUTFILE and NSIS_PARAMS for each .bat file:
74-
set NSIS_ICON=%ROOTPATH%\executables\%%~nf.ico
75-
set NSIS_OUTFILE=%ROOTPATH%\dist\%CI_DST%\%%~nf.exe
76-
set NSIS_PARAMS=Noshell.vbs %%~nxf
77-
"C:\Program Files (x86)\NSIS\makensis.exe" nsis\launcher.nsi
78-
)
67+
xcopy /s /y executables\*.bat "dist\%CI_DST%\scripts"
68+
xcopy /s /y launchers\*.exe "dist\%CI_DST%"
7969

8070
call %FUNC% EndOfScript

scripts/build_launchers.bat

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
@echo off
2+
setlocal EnableDelayedExpansion
3+
4+
call %~dp0utils GetScriptPath SCRIPTPATH
5+
call %FUNC% SetEnvVars
6+
set ROOTPATH=%SCRIPTPATH%\..\
7+
cd %ROOTPATH%
8+
9+
:: Check if MSVC environment is already initialized
10+
if not defined VSINSTALLDIR (
11+
echo Initializing MSVC environment...
12+
call %VCVARS_PATH%
13+
if errorlevel 1 (
14+
echo [ERROR] Failed to initialize MSVC environment.
15+
exit /b 1
16+
)
17+
)
18+
19+
:: Paths
20+
set SOURCE_FILE=src\launcher_template.cpp
21+
set OUTPUT_DIR=launchers
22+
23+
:: Ensure output directory exists
24+
if not exist "%OUTPUT_DIR%" mkdir "%OUTPUT_DIR%"
25+
26+
:: Walk through .bat files in the current directory
27+
for %%B in (executables\*.bat) do (
28+
echo Processing %%B...
29+
30+
:: Derive the base name and paths
31+
set "BASE_NAME=%%~nB"
32+
set "BAT_FILE=%%~dpB%%~nxB"
33+
set "ICON_FILE=%%~dpB%%~nB.ico"
34+
set "RESOURCE_FILE=%OUTPUT_DIR%\%%~nB.rc"
35+
set "RESOURCE_OBJ=%OUTPUT_DIR%\%%~nB.res"
36+
set "LAUNCHER_EXE=%OUTPUT_DIR%\%%~nB.exe"
37+
38+
:: Check if the icon exists
39+
if exist "%%~dpB%%~nB.ico" (
40+
echo Icon found: %%~dpB%%~nB.ico
41+
) else (
42+
echo No icon found for %%B. Using default icon.
43+
set "ICON_FILE=default.ico"
44+
)
45+
46+
:: Create resource file
47+
echo Creating resource file...
48+
> "!RESOURCE_FILE!" echo IDI_ICON1 ICON "!ICON_FILE!"
49+
:: Compile resource
50+
echo Compiling resource...
51+
rc /fo "%OUTPUT_DIR%\%%~nB.res" "!RESOURCE_FILE!"
52+
53+
:: Compile the launcher executable
54+
echo Compiling launcher executable...
55+
cl /EHsc /O2 /DUNICODE /W4 "%SOURCE_FILE%" "!RESOURCE_OBJ!" ^
56+
/Fe"!LAUNCHER_EXE!" ^
57+
/DLAUNCH_TARGET=\"%%~nxB\" ^
58+
User32.lib ^
59+
/link /SUBSYSTEM:WINDOWS
60+
61+
:: Remove intermediate .obj file
62+
del /q "launcher_template.obj"
63+
64+
if errorlevel 1 (
65+
echo [ERROR] Failed to build launcher for %%B.
66+
exit /b 1
67+
)
68+
69+
if exist "!LAUNCHER_EXE!" (
70+
echo [SUCCESS] Launcher created: !LAUNCHER_EXE!
71+
) else (
72+
echo [ERROR] Failed to build launcher for %%B.
73+
exit /b 1
74+
)
75+
)
76+
77+
echo All launchers processed.
78+
exit /b 0

scripts/clean_up.bat

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ call %FUNC% SetEnvVars
1010
set ROOTPATH=%SCRIPTPATH%\..\
1111
cd %ROOTPATH%
1212

13+
if exist *.obj del /s /q *.obj
1314
if exist "tmp" ( rmdir /s /q "tmp" )
1415
if exist "dist" ( rmdir /s /q "dist" )
1516
if exist "packages" ( rmdir /s /q "packages" )
17+
if exist "launchers" ( rmdir /s /q "launchers" )
1618

1719
call %FUNC% EndOfScript

src/launcher_template.cpp

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
DataLab-WinPython launcher script
3+
---------------------------------
4+
5+
Licensed under the terms of the BSD 3-Clause
6+
(see ../LICENSE for details)
7+
8+
*/
9+
10+
#include <windows.h>
11+
#include <string>
12+
13+
int WINAPI WinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nShowCmd*/) {
14+
// Get the path to the current executable
15+
wchar_t exePath[MAX_PATH];
16+
GetModuleFileNameW(NULL, exePath, MAX_PATH);
17+
18+
// Determine the directory of the executable
19+
std::wstring exeDir = exePath;
20+
exeDir = exeDir.substr(0, exeDir.find_last_of(L"\\/"));
21+
22+
// Define the path to the "scripts" directory
23+
std::wstring scriptsDir = exeDir + L"\\scripts";
24+
25+
// Check if the "scripts" directory exists
26+
DWORD attributes = GetFileAttributesW(scriptsDir.c_str());
27+
if (attributes == INVALID_FILE_ATTRIBUTES || !(attributes & FILE_ATTRIBUTE_DIRECTORY)) {
28+
MessageBoxW(NULL, L"The 'scripts' directory does not exist. Please ensure it is in the same folder as the launcher.",
29+
L"Launcher Error", MB_ICONERROR);
30+
return 1;
31+
}
32+
33+
// Set the working directory to the "scripts" folder
34+
if (!SetCurrentDirectoryW(scriptsDir.c_str())) {
35+
MessageBoxW(NULL, L"Failed to set the working directory to 'scripts'.",
36+
L"Launcher Error", MB_ICONERROR);
37+
return 1;
38+
}
39+
40+
// Define the command to run
41+
std::wstring target = L"cmd.exe /c \"" LAUNCH_TARGET L"\"";
42+
43+
// Configure the process startup info
44+
STARTUPINFO si = { sizeof(si) };
45+
si.dwFlags = STARTF_USESHOWWINDOW; // Prevent the window from appearing
46+
si.wShowWindow = SW_HIDE; // Hide the command window
47+
48+
PROCESS_INFORMATION pi = {};
49+
50+
// Start the process with CREATE_NO_WINDOW flag
51+
if (!CreateProcessW(
52+
NULL, // Application name (NULL because we pass the command in the command line)
53+
&target[0], // Command line
54+
NULL, // Process security attributes
55+
NULL, // Thread security attributes
56+
FALSE, // Inherit handles
57+
CREATE_NO_WINDOW, // Flags to prevent creating a window
58+
NULL, // Environment block (NULL to inherit parent)
59+
NULL, // Current directory (NULL to use the parent process's current directory)
60+
&si, // Startup info
61+
&pi // Process information
62+
)) {
63+
MessageBoxW(NULL, L"Failed to launch the script.", L"Launcher Error", MB_ICONERROR);
64+
return 1;
65+
}
66+
67+
// Wait for the script to finish
68+
WaitForSingleObject(pi.hProcess, INFINITE);
69+
70+
// Cleanup
71+
CloseHandle(pi.hProcess);
72+
CloseHandle(pi.hThread);
73+
74+
return 0;
75+
}

0 commit comments

Comments
 (0)