Skip to content

Commit ce0f275

Browse files
authored
Merge pull request #1956 from larsewi/package-msi
ENT-12600: package-msi: documented & refactored script
2 parents fd886fb + 3d4b3ed commit ce0f275

File tree

1 file changed

+110
-31
lines changed

1 file changed

+110
-31
lines changed

build-scripts/package-msi

Lines changed: 110 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,46 @@
1-
#!/bin/sh -x
1+
#!/bin/sh
2+
#
3+
# package-msi: Build Windows MSI installer package for CFEngine using WiX Toolset
4+
# This script creates a Windows installer (.msi) file for CFEngine Nova
5+
# It handles both x86 (32-bit) and x64 (64-bit) architectures
26

7+
# Source common build functions and environment configuration
38
. "$(dirname "$0")"/functions
49
. detect-environment
510
. compile-options
611
. version
712

13+
# Path where WiX toolset binaries will be installed
814
WIXPATH="$HOME/wix"
915

10-
# First see if wix tools are installed, if not, do so
16+
# Check if WiX tools are installed, install them if not present
17+
log_debug "Checking for WiX tools at $WIXPATH"
1118
if [ -f "$WIXPATH/candle.exe" ] && [ -f "$WIXPATH/light.exe" ]; then
12-
echo "Wix Tools are installed at $WIXPATH"
19+
log_debug "WiX tools found - candle.exe and light.exe are present"
1320
else
21+
log_debug "WiX tools not found - starting installation"
1422
(
15-
# Fetch some prerequisites
23+
# Download prerequisites from build artifacts cache server
1624
cd /tmp || exit
1725
echo '
1826
get /export/images/windows/wix310-binaries.zip
1927
get /export/images/windows/wine-folder.tar.xz
2028
' | sftp -b - jenkins_sftp_cache@build-artifacts-cache.cloud.cfengine.com
2129

22-
# check checksums
30+
# Verify downloaded files
2331
sha256sum -c - <<EOF || exit 42
2432
493145b3fac22bdf8c55142a9f96ef8136d56b38d78a2322f13f1ba11f9cf2f8 wix310-binaries.zip
2533
3510fd8c4ecb4a9c479dfe43849183c666f9e41b019fc7135dc8735d0032d16e wine-folder.tar.xz
2634
EOF
27-
# Install Wix tools
35+
# Install WiX Toolset binaries
2836
mkdir -p "$WIXPATH"
2937
cd "$WIXPATH" || exit
3038
unzip /tmp/wix310-binaries.zip
3139
chown "$USER":"$USER" -R "$WIXPATH"
3240

33-
# Extract pre-installed Wine .NET tree
34-
# This file was generated by using a fresh Wine installation and running "winetricks dotnet45".
41+
# Extract pre-configured Wine environment with .NET Framework
42+
# This archive contains a Wine prefix with dotnet45 already installed
43+
# (created by running "winetricks dotnet45" on a fresh Wine installation)
3544
cd "$HOME" || exit
3645
tar -xJf /tmp/wine-folder.tar.xz
3746
chown "$USER":"$USER" -R "$HOME"/.wine
@@ -41,52 +50,77 @@ EOF
4150
)
4251
fi
4352

44-
# Wine can handle these tools under the following conditions:
53+
# Wine configuration requirements for running WiX tools:
4554
# * You must use Wine 32-bit (wine:i386)
4655
# * The host must have run "winetricks dotnet45" and clicked through all the
4756
# installations.
4857
#
49-
# This was tested with Wix tools 3.10.
58+
# This was tested with WiX tools 3.10.
59+
# Define WiX tool commands - candle.exe compiles .wxs files, light.exe links them
60+
#
61+
# TODO: ENT-13249: candle.exe and light.exe have been replaced with the single WiX build command.
5062
CANDLE="wine $WIXPATH/candle.exe"
5163
LIGHT="wine $WIXPATH/light.exe"
5264

65+
# Determine build directory name based on Jenkins job or version/arch
66+
log_debug "Determining build directory name (JOB_NAME=$JOB_NAME, VERSION=$VERSION, ARCH=$ARCH)"
5367
if [ -z "$JOB_NAME" ]; then
5468
DIRNAME=build-$VERSION-$ARCH
69+
log_debug "No JOB_NAME set, using DIRNAME=$DIRNAME"
5570
else
71+
# Extract first part of Jenkins job name (before the slash)
5672
DIRNAME=$(echo "${JOB_NAME}" | sed 's/\(.*\)\/.*/\1/g')
73+
log_debug "Using Jenkins job name, DIRNAME=$DIRNAME"
5774
fi
5875

76+
# Set up packaging directory paths
5977
PKGD=$BASEDIR/packaging/cfengine-nova/pkg
6078
P=$PKGD/$DIRNAME
6179

80+
# pre() - Prepare the packaging directory structure and copy all required files
81+
# This function sets up the directory layout for the MSI package build
6282
pre() {
83+
log_debug "Preparing packaging directory structure"
84+
85+
# Clean up any existing packaging directory and create fresh structure
86+
log_debug "Removing existing packaging directory: $PKGD"
6387
rm -rf "$PKGD"
6488
mkdir -p "$P"/bin
6589
mkdir -p "$P"/ssl
6690

91+
# Copy CFEngine binaries and SSL files from build prefix
92+
log_debug "Copying binaries from $BUILDPREFIX/bin to $P/bin"
6793
cp -a "$BUILDPREFIX"/bin/* "$P"/bin
94+
log_debug "Copying SSL files from $BUILDPREFIX/ssl to $P/ssl"
6895
cp -a "$BUILDPREFIX"/ssl/* "$P"/ssl
96+
log_debug "Copying dist binaries from $BASEDIR/cfengine/dist$BUILDPREFIX/bin"
6997
cp -a "$BASEDIR"/cfengine/dist"$BUILDPREFIX"/bin/* "$P"/bin
7098

71-
# TODO make not hard-coded paths for debian/ubuntu installations of mingw
72-
# I couldn't find an easy way to dynamically find the path to this dll so hard-coded
73-
# it is. :(
99+
# Copy MinGW runtime DLL and architecture-specific event DLL
100+
# TODO: make not hard-coded paths for debian/ubuntu installations of mingw
101+
# These paths are currently hard-coded as there's no easy way to dynamically find them
102+
log_debug "Copying MinGW runtime DLLs for architecture: $ARCH"
74103
case "$ARCH" in
75104
x86)
105+
# 32-bit: Copy i686 MinGW pthread DLL and 32-bit event DLL
76106
cp -a /usr/i686-w64-mingw32/lib/libwinpthread-1.dll "$P"/bin/libwinpthread-1.dll
77107
cp -a "$BASEDIR"/enterprise/libcfenterprise/cf.events.i686.dll "$P"/bin/cf.events.dll
78108
;;
79109
x64)
110+
# 64-bit: Copy x86_64 MinGW pthread DLL and 64-bit event DLL
80111
cp -a /usr/x86_64-w64-mingw32/lib/libwinpthread-1.dll "$P"/bin/libwinpthread-1.dll
81112
cp -a "$BASEDIR"/enterprise/libcfenterprise/cf.events.x86_64.dll "$P"/bin/cf.events.dll
82113
;;
83114
*)
84-
echo "Unknown architecture: $ARCH"
115+
log_error "Unknown architecture: $ARCH"
85116
exit 1
86117
;;
87118
esac
88119

120+
# Copy WiX source file for MSI generation
89121
cp "$BASEDIR"/buildscripts/packaging/cfengine-nova/cfengine-nova.wxs "$P"
122+
123+
# Handle OpenSSL library naming differences between architectures
90124
# OpenSSL libs have different names on x32 and x64 platforms:
91125
# on 32-bit platforms: libcrypto_1_1.dll and libssl_1_1.dll
92126
# on 64-bit platforms: libcrypto_1_1_x64.dll and libssl_1_1_x64.dll
@@ -97,46 +131,72 @@ pre() {
97131
fi
98132
}
99133

134+
# candle() - Compile WiX source file (.wxs) into WiX object file (.wixobj)
135+
# Parameters:
136+
# $1 - REVISION: The version number for the MSI package
137+
# Defines preprocessor variables for the WiX compilation:
138+
# CfSourceDir: Current directory containing files to package
139+
# CfVersion: Version number for the package
140+
# CfArch: Architecture (x86 or x64)
100141
candle() {
101142
REVISION="$1"
143+
log_debug "Running candle with REVISION=$REVISION, ARCH=$ARCH"
102144
$CANDLE -dCfSourceDir=. -dCfVersion="$REVISION" -dCfArch="$ARCH" cfengine-nova.wxs
103145
}
104146

147+
# light() - Link WiX object file into final MSI package
148+
# Uses WiX linker to create the MSI from compiled object file
149+
# Options:
150+
# -sval: Suppress validation (speeds up build)
151+
# -sice:ICE20: Suppress ICE20 validation (makes sure you have the necessary dialogs defined to handle things like showing a friendly message when the user cancels the install)
152+
# -ext WixUtilExtension: Include WiX utility extension for additional functionality
105153
light() {
154+
log_debug "Running light to link WiX object into MSI"
106155
$LIGHT -sval -sice:ICE20 -ext WixUtilExtension cfengine-nova.wixobj
107156
}
108157

158+
# package() - Main packaging function that creates the MSI installer
109159
package() {
160+
log_debug "Creating MSI installer"
110161
cd "$P"
111162

163+
# Determine the package revision number based on build configuration
164+
log_debug "Determining package revision (BUILD_TYPE=$BUILD_TYPE, VERSION=$VERSION)"
112165
if [ -z "$EXPLICIT_VERSION" ]; then
113-
# First make sure VERSION does not contain a 4th dot, since on windows we'll
166+
# First make sure VERSION does not contain a 4th dot, since on Windows we'll
114167
# add one later (plus they can only be numeric); so convert
115168
# 3.10.0a.abcdef to 3.10.0a.
116169
VERSION=$(echo "$VERSION" | sed -e 's/\([^.]*\.[^.]*\.[^.]*\).*/\1/')
117170

118171
case $BUILD_TYPE in
119172
RELEASE)
173+
# For release builds, use VERSION.RELEASE or VERSION.1
120174
if [ -z "$EXPLICIT_RELEASE" ]; then
121175
REVISION="$VERSION.1"
122176
else
123177
REVISION="$VERSION.$EXPLICIT_RELEASE"
124178
fi
125179
;;
126180
DEBUG | CODE_COVERAGE)
181+
# For debug/coverage builds, use main version with build number
182+
# Strip anything after ~ (e.g., 3.10.0~build1 becomes 3.10.0)
127183
MAIN_VERSION=${VERSION%\~*}
128184
REVISION=$MAIN_VERSION.$BUILD_NUMBER
129185
;;
130186
esac
131187
else
188+
# Use explicitly provided version with release number
132189
if [ -z "$EXPLICIT_RELEASE" ]; then
133190
REVISION="$EXPLICIT_VERSION.1"
134191
else
135192
REVISION="$EXPLICIT_VERSION.$EXPLICIT_RELEASE"
136193
fi
137194
fi
138195

139-
# convert any alphabets in revison to int as no alphabets are allowed in windows package version
196+
# Convert alphabetic characters to numeric
197+
# MSI version numbers must be purely numeric
198+
# Example: 3.10.0a becomes 3.10.097 (where 'a' = ASCII 97)
199+
log_debug "Transforming $REVISION to only numeric characters by replacing alphabet characters to their ASCII value"
140200
while true; do
141201
alphabet=$(echo "$REVISION" | sed -e 's/[^a-zA-Z]*\([a-zA-Z]*\).*/\1/')
142202
if [ -n "$alphabet" ]; then
@@ -147,24 +207,28 @@ package() {
147207
fi
148208
done
149209

150-
# ensure all revision components are 4 digits max (MSI allows each part to be up to 65534, but we limit it to 9999)
210+
# Ensure each version component is max 4 digits
211+
# MSI allows up to 65534 per component, but we limit to 9999 for simplicity
151212
REVISION=$(echo "$REVISION" | tr '.' '\n' | sed 's/\(....\).*/\1/' | tr '\n' '.' | sed 's/\.$//')
152213

153-
# ensure revision has exactly 4 components - add .0.0.0 at the end and cut unneded parts
214+
# MSI requires exactly 4 version components (Major.Minor.Build.Revision)
215+
# Pad with zeros if needed, then truncate to exactly 4 components
154216
REVISION=$(echo "$REVISION".0.0.0 | cut -d '.' -f 1-4)
155217

156-
# Wix tools have a ridiculously short maximum file length of 128 characters.
157-
# This is easily exceeded when Jenkins workspace paths are involved. Luckily
158-
# we can shortcut the paths by using Windows drive letters to point to a
159-
# directory closer to the source files. Wine will automatically use the
160-
# shortest one.
218+
# Work around WiX's 128-character path length limitation
219+
# Long Jenkins workspace paths often exceed this limit
220+
# Solution: Create a Wine drive letter symlink to current directory
221+
# This provides a shorter path like D:\ instead of /home/user/very/long/path
161222

162-
# Find a free drive letter. Z: is usually / in Wine, but apart from that it's
163-
# more likely to find free letters at the bottom end, so count backwards.
223+
# Find an available drive letter for Wine mapping
224+
# Start from Y: and work backwards (Z: is typically mapped to root /)
225+
log_debug "Finding available Wine drive letter for path limitation workaround"
164226
WORKSPACE_DRIVE=
165227
for letter in y x w v u t s r q p o n m l k j i h g f e d; do
166228
if [ ! -e "$HOME"/.wine/dosdevices/$letter: ]; then
167229
WORKSPACE_DRIVE=$letter:
230+
log_debug "Using Wine drive letter $WORKSPACE_DRIVE mapped to $PWD"
231+
# Create symlink from Wine drive letter to current directory
168232
ln -s "$PWD" "$HOME"/.wine/dosdevices/$WORKSPACE_DRIVE
169233
break
170234
fi
@@ -175,43 +239,58 @@ package() {
175239
exit 2
176240
fi
177241

242+
# Run WiX compilation and linking steps
243+
log_debug "Starting WiX compilation and linking with final REVISION=$REVISION"
178244
ret=0
179245
candle "$REVISION" && light || ret=$?
180246

181-
# Make sure the drive letter from above is cleaned up in case this build slave
182-
# is reused.
247+
# Clean up Wine drive letter mapping to prevent conflicts in future builds
183248
rm -f "$HOME"/.wine/dosdevices/$WORKSPACE_DRIVE
184249

185250
return $ret
186251
}
187252

253+
# post() - Post-processing function to finalize the MSI package
254+
# Moves the generated MSI to the output directory and renames it appropriately
188255
post() {
256+
log_debug "Finalizing MSI package"
257+
# Create output directory and move the MSI file there
189258
mkdir -p "$BASEDIR"/cfengine-nova
190259
mv "$P"/cfengine-nova.msi "$BASEDIR"/cfengine-nova
191260
cd "$BASEDIR"/cfengine-nova
192261

193-
# For some reason candle is giving file no permissions at all.
262+
# Fix file permissions - WiX/Wine sometimes creates files with no permissions
194263
chmod 644 cfengine-nova.msi
195264

265+
# Determine package name based on build type
196266
case $BUILD_TYPE in
197267
RELEASE)
268+
# Release builds: cfengine-nova-VERSION-RELEASE
198269
PKGNAME="cfengine-nova-$VERSION-${EXPLICIT_RELEASE:-1}"
199270
;;
200271
DEBUG | CODE_COVERAGE)
272+
# Debug/coverage builds: cfengine-nova-VERSION-BUILD_NUMBER
201273
PKGNAME="cfengine-nova-$VERSION-$BUILD_NUMBER"
202274
;;
203275
esac
204276

277+
# Rename MSI file with architecture suffix
278+
log_debug "Renaming MSI file for architecture $ARCH with PKGNAME=$PKGNAME"
205279
case $ARCH in
206280
x86)
281+
# 32-bit: Add i686 suffix
207282
mv cfengine-nova.msi "$PKGNAME-i686.msi"
208283
;;
209284
x64)
285+
# 64-bit: Add x86_64 suffix
210286
mv cfengine-nova.msi "$PKGNAME-x86_64.msi"
211287
;;
212288
esac
213289
}
214290

215-
pre
216-
package
217-
post
291+
# Main execution flow:
292+
log_debug "--- Starting MSI package build process ---"
293+
pre # Prepare packaging directory and copy files
294+
package # Build the MSI using WiX tools
295+
post # Finalize and rename the MSI package
296+
log_debug "--- MSI package build completed ----"

0 commit comments

Comments
 (0)