Skip to content

Commit 63bf962

Browse files
authored
[build] Fix Windows installers (#3113).
Use the OpenSSL Universal installer on Windows to build on x86/x64 host targeting Arm64 systems.
1 parent d69b065 commit 63bf962

File tree

4 files changed

+184
-147
lines changed

4 files changed

+184
-147
lines changed

scripts/win-installer/README.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ These initial steps need to be executed once only.
1818

1919
- Prerequisite 2: Install OpenSSL for Windows, 64-bit Intel, 32-bit Intel, 64-bit Arm.
2020
This can be done automatically by running the PowerShell script `install-openssl.ps1`.
21+
If you are interested in more information about OpenSSL for Windows, scroll to the
22+
appendix at the end of this page.
2123

2224
- Prerequisite 3: Install NSIS, the NullSoft Installation Scripting system.
2325
This can be done automatically by running the PowerShell script `install-nsis.ps1`.
@@ -128,3 +130,80 @@ This directory contains the following files:
128130
| libsrt.nsi | NSIS installation script (used to build the installer).
129131
| libsrt.props | Visual Studio property files to use libsrt (embedded in the installer).
130132
| README.md | This text file.
133+
134+
## Appendix: Using OpenSSL on Windows
135+
136+
The SRT protocol uses encryption. Internally, `libsrt` can use multiple cryptographic
137+
libraries but OpenSSL is the default choice on all platforms, including Windows.
138+
139+
OpenSSL is not part of the base installation of a Windows platform and should be
140+
separately installed. OpenSSL is neither "Windows-friendly" and the OpenSSL team
141+
does not provide Windows binaries.
142+
143+
The production of OpenSSL binaries for Windows is delegated to Shining Light
144+
Productions (http://slproweb.com/). Binaries for all architectures are available
145+
here: http://slproweb.com/products/Win32OpenSSL.html
146+
147+
### How to grab OpenSSL installers for Windows
148+
149+
This is automatically done by the PowerShell script `install-openssl.ps1` in
150+
this directory. Let's see what's behind the scene.
151+
152+
First, the script determines which installers should be downloaded and where
153+
to get them from.
154+
155+
Initially, the HTML for the [slproweb](http://slproweb.com/products/Win32OpenSSL.html)
156+
page was built on server-side. Our script grabbed the URL content, parsed the HTML
157+
and extracted the URL of the binaries for the latest OpenSSL packages from the
158+
"href" of the links.
159+
160+
At some point in 2024, the site changed policy. The Web page was no longer
161+
built on server-side, but on client-side. The downloaded HTML contains some
162+
JavaScript which dynamically builds the URL of the binaries for the latest
163+
OpenSSL binaries. The previous method now finds no valid package URL from
164+
the downloaded page (a full browser is needed to execute the JavaScript).
165+
The JavaScript downloads a JSON file which contains all references to
166+
the OpenSSL binaries.
167+
168+
Therefore, the script `install-openssl.ps1` now uses the same method:
169+
download that JSON file and parse it.
170+
171+
### Multi-architecture builds
172+
173+
The `libsrt` installer contains static libraries for all three architectures
174+
which are supported by Windows: x86, x64, Arm64. The corresponding static
175+
libraries for OpenSSL are also needed and included in the installer.
176+
177+
Because Visual Studio can build libraries and executables for all three
178+
architectures, on any build system, we need to install the OpenSSL headers
179+
and libraries for all three architectures, on the build system.
180+
181+
This is what the script `install-openssl.ps1` does. However, the way to
182+
do this has changed over time.
183+
184+
As a starting point, there are three installers, one per architecture.
185+
All installers are x86 executables which can be run on any Windows system
186+
(Arm64 Windows emulates x86 and x64 when necessary). Additionally, the
187+
three sets of files are installed side by side, without overlap.
188+
189+
- x86: `C:\Program Files (x86)\OpenSSL-Win32`
190+
- x64: `C:\Program Files\OpenSSL-Win64`
191+
- Arm64: `C:\Program Files\OpenSSL-Win64-ARM`
192+
193+
So, the script `install-openssl.ps1` installed them all and `libsrt` could
194+
be built for all architecture.
195+
196+
However, it works only on Arm64 systems. On x86 and x64 system, the OpenSSL
197+
installer for Arm64 fails. The installer executable can run but there is an
198+
explicit check in the installation process to abort when running on Intel
199+
systems.
200+
201+
Since most Windows build servers are Intel systems, this approach was no
202+
longer appropriate.
203+
204+
At some point, the producers of the OpenSSL binaries acknowledged the problem
205+
and they created an additional form of installer: the "universal" one. This
206+
package can be installed on any system and the binaries and headers are installed
207+
for all architectures, in one single location.
208+
209+
- Universal: `C:\Program Files (x86)\OpenSSL-WinUniversal`

scripts/win-installer/build-win-installer.ps1

Lines changed: 67 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@
2121
the defaut version is a detailed version number (most recent version, number
2222
of commits since then, short commit SHA).
2323
24+
.PARAMETER NoBuild
25+
26+
Do not rebuild the SRT libraries. Assume that they are already built.
27+
Only build the installer.
28+
2429
.PARAMETER NoPause
2530
2631
Do not wait for the user to press <enter> at end of execution. By default,
@@ -31,6 +36,7 @@
3136
[CmdletBinding()]
3237
param(
3338
[string]$Version = "",
39+
[switch]$NoBuild = $false,
3440
[switch]$NoPause = $false
3541
)
3642
Write-Output "Building the SRT static libraries installer for Windows"
@@ -89,54 +95,33 @@ Write-Output "Windows version info: $VersionInfo"
8995
#-----------------------------------------------------------------------------
9096

9197
# Locate OpenSSL root from local installation.
92-
# After version 3.something, OpenSSL has changed its installation layout.
93-
# We have to look for OpenSSL libraries in various locations.
94-
$SSL = @{
95-
"x64" = @{
96-
"alt" = "Win64";
97-
"bits" = 64;
98-
"root" = "C:\Program Files\OpenSSL-Win64"
99-
};
100-
"Win32" = @{
101-
"alt" = "x86";
102-
"bits" = 32;
103-
"root" = "C:\Program Files (x86)\OpenSSL-Win32"
104-
}
105-
"ARM64" = @{
106-
"alt" = "arm64";
107-
"bits" = 64;
108-
"root" = "C:\Program Files\OpenSSL-Win64-ARM"
109-
};
110-
}
98+
# We now requires the "universal" OpenSSL installer (see README.md).
99+
# The universal OpenSSL installer is installed in $SSL.
100+
# $ARCH translates between VC "platform" names to subdirectory names inside $SSL.
101+
# $LIBDIR translates between VC "configuration" names to library subdirectory names.
102+
103+
$SSL = "C:\Program Files (x86)\OpenSSL-WinUniversal"
104+
$ARCH = @{x64 = "x64"; Win32 = "x86"; ARM64 = "arm64"}
105+
$LIBDIR = @{Release = "MD"; Debug = "MDd"}
106+
$LIBFILE = @{crypto = "libcrypto_static.lib"; ssl = "libssl_static.lib"}
107+
108+
function ssl-include($pf) { return "$SSL\include\$($ARCH.$pf)" }
109+
function ssl-libdir($pf, $conf) { return "$SSL\lib\VC\$($ARCH.$pf)\$($LIBDIR.$conf)" }
110+
function ssl-lib($pf, $conf, $lib) { return "$(ssl-libdir $pf $conf)\$($LIBFILE.$lib)" }
111111

112112
# Verify OpenSSL directories and static libraries.
113113
Write-Output "Searching OpenSSL libraries ..."
114114
$Missing = 0
115-
foreach ($arch in $SSL.Keys) {
116-
$root = $SSL[$arch]["root"]
117-
$bits = $SSL[$arch]["bits"]
118-
$alt = $SSL[$arch]["alt"]
119-
if (-not (Test-Path $root)) {
120-
Write-Output "**** Missing $root"
115+
foreach ($Platform in $SSL.Keys) {
116+
if (-not (Test-Path -PathType Container $(ssl-include $Platform))) {
117+
Write-Output "**** Missing $(ssl-include $Platform)"
121118
$Missing = $Missing + 1
122119
}
123-
else {
124-
foreach ($lib in @("ssl", "crypto")) {
125-
foreach ($conf in @("MD", "MDd")) {
126-
$name = "lib${lib}${conf}"
127-
$SSL[$arch][$name] = ""
128-
foreach ($try in @("$root\lib\VC\static\lib${lib}${bits}${conf}.lib",
129-
"$root\lib\VC\${arch}\${conf}\lib${lib}_static.lib",
130-
"$root\lib\VC\${alt}\${conf}\lib${lib}_static.lib")) {
131-
if (Test-Path $try) {
132-
$SSL[$arch][$name] = $try
133-
break
134-
}
135-
}
136-
if (-not $SSL[$arch][$name]) {
137-
Write-Output "**** OpenSSL static library for $name not found"
138-
$Missing = $Missing + 1
139-
}
120+
foreach ($lib in $LIBFILE.Keys) {
121+
foreach ($conf in $LIBDIR.Keys) {
122+
if (-not (Test-Path $(ssl-lib $Platform $conf $lib))) {
123+
Write-Output "**** Missing $(ssl-lib $Platform $conf $lib)"
124+
$Missing = $Missing + 1
140125
}
141126
}
142127
}
@@ -187,46 +172,40 @@ Write-Output "NSIS: $NSIS"
187172
# Configure and build SRT library using CMake on all architectures.
188173
#-----------------------------------------------------------------------------
189174

190-
foreach ($Platform in $SSL.Keys) {
191-
192-
# Build directory. Cleanup to force a fresh cmake config.
193-
$BuildDir = "$TmpDir\build.$Platform"
194-
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue $BuildDir
195-
[void](New-Item -Path $BuildDir -ItemType Directory -Force)
196-
197-
# Run CMake.
198-
# Note: In previous versions of CMake, it was necessary to specify where
199-
# OpenSSL was located using OPENSSL_ROOT_DIR, OPENSSL_LIBRARIES, and
200-
# OPENSSL_INCLUDE_DIR. Starting with CMake 3.29.0, this is no longer
201-
# necessary because CMake knows where to find the OpenSSL binaries from
202-
# slproweb. Additionally, for some unkown reason, defining the OPENSSL_xxx
203-
# variables no longer works with Arm64 libraries, even though the path to
204-
# that version is correct. So, it's better to let CMake find OpenSSL by itself.
205-
Write-Output "Configuring build for platform $Platform ..."
206-
$SRoot = $SSL[$Platform]["root"]
207-
$LibSSL = $SSL[$Platform]["libsslMD"]
208-
$LibCrypto = $SSL[$Platform]["libcryptoMD"]
209-
& $CMake -S $RepoDir -B $BuildDir -A $Platform -DENABLE_STDCXX_SYNC=ON
210-
211-
# Patch version string in version.h
212-
Get-Content "$BuildDir\version.h" |
213-
ForEach-Object {
214-
$_ -replace "#define *SRT_VERSION_STRING .*","#define SRT_VERSION_STRING `"$Version`""
215-
} |
216-
Out-File "$BuildDir\version.new" -Encoding ascii
217-
Move-Item "$BuildDir\version.new" "$BuildDir\version.h" -Force
218-
219-
# Compile SRT.
220-
Write-Output "Building for platform $Platform ..."
221-
foreach ($Conf in @("Release", "Debug")) {
222-
& $MSBuild "$BuildDir\SRT.sln" /nologo /maxcpucount /property:Configuration=$Conf /property:Platform=$Platform /target:srt_static
175+
if (-not $NoBuild) {
176+
foreach ($Platform in $ARCH.Keys) {
177+
# Build directory. Cleanup to force a fresh cmake config.
178+
$BuildDir = "$TmpDir\build.$Platform"
179+
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue $BuildDir
180+
[void](New-Item -Path $BuildDir -ItemType Directory -Force)
181+
182+
# Run CMake.
183+
Write-Output "Configuring build for platform $Platform ..."
184+
& $CMake -S $RepoDir -B $BuildDir -A $Platform `
185+
-DENABLE_STDCXX_SYNC=ON `
186+
-DOPENSSL_INCLUDE_DIR="$(ssl-include $Platform)" `
187+
-DOPENSSL_ROOT_DIR="$(ssl-libdir $Platform Release)"
188+
189+
# Patch version string in version.h
190+
Get-Content "$BuildDir\version.h" |
191+
ForEach-Object {
192+
$_ -replace "#define *SRT_VERSION_STRING .*","#define SRT_VERSION_STRING `"$Version`""
193+
} |
194+
Out-File "$BuildDir\version.new" -Encoding ascii
195+
Move-Item "$BuildDir\version.new" "$BuildDir\version.h" -Force
196+
197+
# Compile SRT.
198+
Write-Output "Building for platform $Platform ..."
199+
foreach ($Conf in $LIBDIR.Keys) {
200+
& $MSBuild "$BuildDir\SRT.sln" /nologo /maxcpucount /property:Configuration=$Conf /property:Platform=$Platform /target:srt_static
201+
}
223202
}
224203
}
225204

226205
# Verify the presence of compiled libraries.
227206
Write-Output "Checking compiled libraries ..."
228207
$Missing = 0
229-
foreach ($Conf in @("Release", "Debug")) {
208+
foreach ($Conf in $LIBDIR.Keys) {
230209
foreach ($Platform in $SSL.Keys) {
231210
$Path = "$TmpDir\build.$Platform\$Conf\srt_static.lib"
232211
if (-not (Test-Path $Path)) {
@@ -254,18 +233,18 @@ Write-Output "Building installer ..."
254233
/DOutDir="$OutDir" `
255234
/DBuildRoot="$TmpDir" `
256235
/DRepoDir="$RepoDir" `
257-
/DlibsslWin32MD="$($SSL.Win32.libsslMD)" `
258-
/DlibsslWin32MDd="$($SSL.Win32.libsslMDd)" `
259-
/DlibcryptoWin32MD="$($SSL.Win32.libcryptoMD)" `
260-
/DlibcryptoWin32MDd="$($SSL.Win32.libcryptoMDd)" `
261-
/DlibsslWin64MD="$($SSL.x64.libsslMD)" `
262-
/DlibsslWin64MDd="$($SSL.x64.libsslMDd)" `
263-
/DlibcryptoWin64MD="$($SSL.x64.libcryptoMD)" `
264-
/DlibcryptoWin64MDd="$($SSL.x64.libcryptoMDd)" `
265-
/DlibsslArm64MD="$($SSL.ARM64.libsslMD)" `
266-
/DlibsslArm64MDd="$($SSL.ARM64.libsslMDd)" `
267-
/DlibcryptoArm64MD="$($SSL.ARM64.libcryptoMD)" `
268-
/DlibcryptoArm64MDd="$($SSL.ARM64.libcryptoMDd)" `
236+
/DlibsslWin32Release="$(ssl-lib Win32 Release ssl)" `
237+
/DlibsslWin32Debug="$(ssl-lib Win32 Debug ssl)" `
238+
/DlibcryptoWin32Release="$(ssl-lib Win32 Release crypto)" `
239+
/DlibcryptoWin32Debug="$(ssl-lib Win32 Debug crypto)" `
240+
/DlibsslWin64Release="$(ssl-lib x64 Release ssl)" `
241+
/DlibsslWin64Debug="$(ssl-lib x64 Debug ssl)" `
242+
/DlibcryptoWin64Release="$(ssl-lib x64 Release crypto)" `
243+
/DlibcryptoWin64Debug="$(ssl-lib x64 Debug crypto)" `
244+
/DlibsslArm64Release="$(ssl-lib Arm64 Release ssl)" `
245+
/DlibsslArm64Debug="$(ssl-lib Arm64 Debug ssl)" `
246+
/DlibcryptoArm64Release="$(ssl-lib Arm64 Release crypto)" `
247+
/DlibcryptoArm64Debug="$(ssl-lib Arm64 Debug crypto)" `
269248
"$ScriptDir\libsrt.nsi"
270249

271250
if (-not (Test-Path $InstallExe)) {

scripts/win-installer/install-openssl.ps1

Lines changed: 26 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -37,24 +37,7 @@ param(
3737

3838
Write-Output "OpenSSL download and installation procedure"
3939

40-
# A bit of history: Where to load OpenSSL binaries from?
41-
#
42-
# The packages are built by "slproweb". The binaries are downloadable from
43-
# their Web page at: http://slproweb.com/products/Win32OpenSSL.html
44-
#
45-
# Initially, the HTML for this page was built on server-side. This script
46-
# grabbed the URL content, parse the HTML and extracted the URL of the
47-
# binaries for the latest OpenSSL packages from the "href" of the links.
48-
#
49-
# At some point in 2024, the site changed policy. The Web page is no longer
50-
# built on server-side, but on client-side. The downloaded HTML contains some
51-
# JavaScript which dynamically builds the URL of the binaries for the latest
52-
# OpenSSL binaries. The previous method now finds no valid package URL from
53-
# the downloaded page (a full browser is needed to execute the JavaScript).
54-
# The JavaScript downloads a JSON file which contains all references to
55-
# the OpenSSL binaries. This script now uses the same method: download that
56-
# JSON file and parse it.
57-
40+
# The list of OpenSSL packages is available in a JSON file. See more details in README.md.
5841
$PackageList = "https://github.com/slproweb/opensslhashes/raw/master/win32_openssl_hashes.json"
5942

6043
# A function to exit this script.
@@ -101,40 +84,36 @@ if ($status -ne 1 -and $status -ne 2) {
10184
}
10285
$config = ConvertFrom-Json $Response.Content
10386

104-
# Download and install MSI packages for 32 and 64-bit Intel, 64-bit Arm.
105-
foreach ($conf in @(@{arch="intel"; bits=32}, @{arch="intel"; bits=64}, @{arch="arm"; bits=64})) {
106-
107-
# Get the URL of the MSI installer from the JSON config.
108-
$Url = $config.files | Get-Member | ForEach-Object {
109-
$name = $_.name
110-
$info = $config.files.$($_.name)
111-
if (-not $info.light -and $info.installer -like "msi" -and $info.bits -eq $conf.bits -and $info.arch -like $conf.arch) {
112-
$info.url
113-
}
114-
} | Select-Object -Last 1
115-
if (-not $Url) {
116-
Exit-Script "#### No MSI installer found for $($conf.bits)-bit $($conf.arch)"
87+
# Find the URL of the latest "universal" installer in the JSON config file.
88+
$Url = $config.files | Get-Member | ForEach-Object {
89+
$name = $_.name
90+
$info = $config.files.$($_.name)
91+
if (-not $info.light -and $info.installer -like "exe" -and $info.arch -like "universal") {
92+
$info.url
11793
}
94+
} | Select-Object -Last 1
95+
if (-not $Url) {
96+
Exit-Script "#### No universal installer found"
97+
}
11898

119-
$MsiName = (Split-Path -Leaf $Url)
120-
$MsiPath = "$TmpDir\$MsiName"
99+
$ExeName = (Split-Path -Leaf $Url)
100+
$ExePath = "$TmpDir\$ExeName"
121101

122-
if (-not $ForceDownload -and (Test-Path $MsiPath)) {
123-
Write-Output "$MsiName already downloaded, use -ForceDownload to download again"
124-
}
125-
else {
126-
Write-Output "Downloading $Url ..."
127-
Invoke-WebRequest -UseBasicParsing -UserAgent Download -Uri $Url -OutFile $MsiPath
128-
}
102+
if (-not $ForceDownload -and (Test-Path $ExePath)) {
103+
Write-Output "$ExeName already downloaded, use -ForceDownload to download again"
104+
}
105+
else {
106+
Write-Output "Downloading $Url ..."
107+
Invoke-WebRequest -UseBasicParsing -UserAgent Download -Uri $Url -OutFile $ExePath
108+
}
129109

130-
if (-not (Test-Path $MsiPath)) {
131-
Exit-Script "$Url download failed"
132-
}
110+
if (-not (Test-Path $ExePath)) {
111+
Exit-Script "$Url download failed"
112+
}
133113

134-
if (-not $NoInstall) {
135-
Write-Output "Installing $MsiName"
136-
Start-Process msiexec.exe -ArgumentList @("/i", $MsiPath, "/qn", "/norestart") -Wait
137-
}
114+
if (-not $NoInstall) {
115+
Write-Output "Installing $ExeName"
116+
Start-Process -FilePath $ExePath -ArgumentList @("/VERYSILENT", "/SUPPRESSMSGBOXES", "/NORESTART", "/ALLUSERS") -Wait
138117
}
139118

140119
Exit-Script

0 commit comments

Comments
 (0)