Bundle VC runtime DLLs for Windows installer

- Support user-level install (no admin required)
- Bundle MSVC runtime DLLs from deps/vcruntime
- Offer system-wide VCRedist install when running as admin
- Add portable ZIP artifact (windows_zip target)
- Add combined windows_all target for installer + ZIP
This commit is contained in:
Andrew Rabert committed 2025-12-26 03:49:48 -05:00
1 parent 01a50a8bab
commit 747275054e
4 files changed
+118 -65

No files matched your search

+1
View File
@@ -6,6 +6,7 @@ CMakeLists.txt.user
CMakeLists.txt.user.*
build/*
build_deps/*
deps/*
build_vs_debug/*
.idea
.install_pkg
+21
View File
@@ -48,6 +48,27 @@ else()
message(STATUS "Not using separate ANGLE deps")
endif()
# Bundle MSVC runtime DLLs (pre-extracted by setup-windows.bat)
set(VCRUNTIME_DIR "@PROJECT_SOURCE_DIR@/deps/vcruntime")
set(VCREDIST_EXE "@PROJECT_SOURCE_DIR@/deps/vc_redist.x64.exe")
if(EXISTS "${VCRUNTIME_DIR}" AND EXISTS "${VCREDIST_EXE}")
# Copy runtime DLLs to redist subfolder
file(MAKE_DIRECTORY "${CMAKE_INSTALL_PREFIX}/redist")
file(GLOB RUNTIME_DLLS "${VCRUNTIME_DIR}/*.dll")
foreach(dll_path ${RUNTIME_DLLS})
get_filename_component(dll_name "${dll_path}" NAME)
file(INSTALL "${dll_path}" DESTINATION "${CMAKE_INSTALL_PREFIX}/redist")
message(STATUS "Bundled runtime: ${dll_name}")
endforeach()
# Copy VCRedist installer for bundling
file(INSTALL "${VCREDIST_EXE}" DESTINATION "${CMAKE_INSTALL_PREFIX}")
message(STATUS "Bundled VCRedist installer: vc_redist.x64.exe")
else()
message(WARNING "VCRuntime DLLs not found. Run setup-windows.bat first.")
endif()
if(@DO_SIGN@)
file(GLOB_RECURSE EXES ${CMAKE_INSTALL_PREFIX}/*.exe ${CMAKE_INSTALL_PREFIX}/*.dll)
set(ENV{errorlevel} 1)
+32
View File
@@ -59,4 +59,36 @@ add_custom_target(JellyfinDesktopInstaller
# Alias target for convenience
add_custom_target(windows_package DEPENDS JellyfinDesktopInstaller)
# Create portable ZIP archive (includes bundled runtime DLLs in root)
set(ZIP_OUTPUT_NAME "${INSTALLER_BASE_NAME}.zip")
set(ZIP_STAGING_DIR "${CMAKE_CURRENT_BINARY_DIR}/portable")
# Configure the prepare script
configure_file(
${PROJECT_SOURCE_DIR}/CMakeModules/PreparePortableZip.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/PreparePortableZip.cmake
@ONLY
)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${ZIP_OUTPUT_NAME}
DEPENDS innosetup_install ${CMAKE_CURRENT_BINARY_DIR}/PreparePortableZip.cmake
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/PreparePortableZip.cmake
COMMAND ${CMAKE_COMMAND} -E tar cf ${CMAKE_CURRENT_BINARY_DIR}/${ZIP_OUTPUT_NAME} --format=zip .
WORKING_DIRECTORY ${ZIP_STAGING_DIR}
COMMENT "Building portable ZIP: ${ZIP_OUTPUT_NAME}"
VERBATIM
)
add_custom_target(JellyfinDesktopZip
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${ZIP_OUTPUT_NAME}
)
add_custom_target(windows_zip DEPENDS JellyfinDesktopZip)
# Combined target for both installer and ZIP
add_custom_target(windows_all DEPENDS JellyfinDesktopInstaller JellyfinDesktopZip)
message(STATUS "Windows installer target configured: windows_package")
message(STATUS "Windows portable ZIP target configured: windows_zip")
message(STATUS "Windows combined target configured: windows_all")
+64 -65
View File
@@ -12,7 +12,6 @@
#define MyAppExeName "Jellyfin Desktop.exe"
#define MyAppGUID "{{a78bea4a-5bd0-4aa3-bdf3-579b4f58a921}"
#define DeployPath "@CMAKE_INSTALL_PREFIX@"
#define VCRedistURL "https://aka.ms/vs/17/release/vc_redist.x64.exe"
[Setup]
; NOTE: The value of AppId uniquely identifies this application.
@@ -32,7 +31,8 @@ DisableProgramGroupPage=yes
MinVersion=10.0
ArchitecturesInstallIn64BitMode=x64compatible
ArchitecturesAllowed=x64compatible
PrivilegesRequired=admin
PrivilegesRequired=lowest
PrivilegesRequiredOverridesAllowed=dialog
OutputDir=.
OutputBaseFilename=JellyfinDesktop-{#MyAppVersion}-windows-x64
SetupIconFile=@PROJECT_SOURCE_DIR@/bundle/win/jellyfin.ico
@@ -56,15 +56,20 @@ Name: "english"; MessagesFile: "compiler:Default.isl"
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"
[Files]
Source: "{#DeployPath}\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
; Main application files (exclude redist folder - handled separately)
Source: "{#DeployPath}\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; Excludes: "redist,redist\*,vc_redist.x64.exe"
; Bundled VC runtime DLLs - only copied if needed (see Code section)
Source: "{#DeployPath}\redist\*.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: NeedsBundledRuntime
; Bundled VCRedist installer - extracted to temp for potential system-wide install
Source: "{#DeployPath}\vc_redist.x64.exe"; DestDir: "{tmp}"; Flags: ignoreversion deleteafterinstall; Check: ShouldExtractVCRedist
[Icons]
Name: "{autoprograms}\Jellyfin Desktop"; Filename: "{app}\{#MyAppExeName}"; Comment: "Jellyfin desktop client"; WorkingDir: "{app}"
Name: "{autodesktop}\Jellyfin Desktop"; Filename: "{app}\{#MyAppExeName}"; Comment: "Jellyfin desktop client"; WorkingDir: "{app}"; Tasks: desktopicon
[Run]
; Install VCRedist - will be downloaded if needed
Filename: "{tmp}\vcredist_x64.exe"; Parameters: "/install /passive /norestart"; StatusMsg: "Installing Visual C++ Redistributable..."; Flags: waituntilterminated; Check: VCRedistNeedsInstall
; Install VCRedist if downloaded
Filename: "{tmp}\vc_redist.x64.exe"; Parameters: "/install /passive /norestart"; StatusMsg: "Installing Visual C++ Redistributable..."; Flags: waituntilterminated; Check: ShouldInstallVCRedist
; Launch app after install
Filename: "{app}\{#MyAppExeName}"; Description: "Launch Jellyfin Desktop"; Flags: nowait postinstall skipifsilent
@@ -72,91 +77,85 @@ Filename: "{app}\{#MyAppExeName}"; Description: "Launch Jellyfin Desktop"; Flags
const
PF_AVX2_INSTRUCTIONS_AVAILABLE = 40;
var
VCRedistNeedsInstallFlag: Boolean;
function IsProcessorFeaturePresent(ProcessorFeature: DWORD): BOOL;
external 'IsProcessorFeaturePresent@kernel32.dll stdcall';
function VCRedistNeedsInstall: Boolean;
begin
Result := VCRedistNeedsInstallFlag;
end;
function CheckVCRedist(): Boolean;
var
UCRTBasePath: String;
MSVCPPath: String;
begin
UCRTBasePath := ExpandConstant('{sys}\ucrtbase.dll');
MSVCPPath := ExpandConstant('{sys}\msvcp140.dll');
RemoveUserData: Boolean;
VCRedistNeeded: Boolean;
InstallVCRedist: Boolean;
UseBundledRuntime: Boolean;
// Good if ucrtbase.dll exists
if FileExists(UCRTBasePath) then
begin
Result := True;
Log('VCRedist OK: ucrtbase.dll found');
end
// Bad if msvcp140.dll exists but ucrtbase.dll doesn't (corrupted install)
else if FileExists(MSVCPPath) then
begin
Result := False;
Log('Bad VCRedist: msvcp140.dll exists but ucrtbase.dll missing');
end
else
begin
Result := False;
Log('VCRedist not installed');
end;
function HasSystemVCRuntime: Boolean;
begin
Result := FileExists(ExpandConstant('{sys}\vcruntime140.dll'));
end;
function NeedsBundledRuntime: Boolean;
begin
// Use bundled DLLs if: system doesn't have VCRedist AND (user install OR user declined VCRedist install)
Result := UseBundledRuntime;
end;
function ShouldExtractVCRedist: Boolean;
begin
// Extract bundled VCRedist to temp if: admin install AND system missing VCRedist AND user wants to install it
Result := InstallVCRedist;
end;
function ShouldInstallVCRedist: Boolean;
begin
// Run VCRedist installer if user chose to install it
Result := InstallVCRedist;
end;
function InitializeSetup(): Boolean;
begin
Result := True;
// Check VCRedist and set flag for download
VCRedistNeedsInstallFlag := not CheckVCRedist();
if VCRedistNeedsInstallFlag then
Log('VCRedist will be installed during setup');
VCRedistNeeded := not HasSystemVCRuntime;
InstallVCRedist := False;
UseBundledRuntime := False;
end;
function PrepareToInstall(var NeedsRestart: Boolean): String;
var
DownloadPage: TDownloadWizardPage;
VCRedistPath: String;
begin
Result := '';
NeedsRestart := False;
// Download VCRedist if needed
if VCRedistNeedsInstallFlag then
if not VCRedistNeeded then
begin
VCRedistPath := ExpandConstant('{tmp}\vcredist_x64.exe');
Log('VCRedist already installed on system');
Exit;
end;
if not FileExists(VCRedistPath) then
// Check if running as admin
if IsAdmin then
begin
// Offer to install VCRedist system-wide
if MsgBox('Visual C++ Redistributable is not installed.' + #13#10 + #13#10 +
'Would you like to install it system-wide?' + #13#10 +
'(Recommended - receives security updates via Windows Update)' + #13#10 + #13#10 +
'If you choose No, bundled runtime libraries will be used instead.',
mbConfirmation, MB_YESNO) = IDYES then
begin
DownloadPage := CreateDownloadPage('Downloading Dependencies', 'Downloading required dependencies...', nil);
DownloadPage.Clear;
DownloadPage.Add('{#VCRedistURL}', 'vcredist_x64.exe', '');
DownloadPage.Show;
try
DownloadPage.Download;
except
Result := 'Failed to download Visual C++ Redistributable.' + #13#10 +
'Please check your internet connection and try again.';
Exit;
finally
DownloadPage.Hide;
end;
InstallVCRedist := True;
Log('Will install VCRedist system-wide');
end
else
begin
// User declined, use bundled
Log('User declined VCRedist install, using bundled runtime');
UseBundledRuntime := True;
end;
end
else
begin
// Non-admin install, use bundled
Log('Non-admin install, using bundled runtime');
UseBundledRuntime := True;
end;
end;
var
RemoveUserData: Boolean;
procedure CurStepChanged(CurStep: TSetupStep);
var
FallbackPath: String;