Skip to content

Commit 39a2014

Browse files
authored
Merge pull request #635 from Boddlnagg/msi-installer
[WIP] MSI Installer
2 parents 95f38e8 + c12368e commit 39a2014

17 files changed

+400
-28
lines changed

Cargo.lock

+33-25
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+6
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ rustls-backend = ["download/rustls-backend"]
2828
# Include in the default set to disable self-update and uninstall.
2929
no-self-update = []
3030

31+
# Used to change behavior of self-update and uninstall if installed via MSI
32+
msi-installed = []
33+
3134
[dependencies]
3235
rustup-dist = { path = "src/rustup-dist", version = "0.5.0" }
3336
rustup-utils = { path = "src/rustup-utils", version = "0.5.0" }
@@ -59,6 +62,9 @@ kernel32-sys = "0.2.1"
5962
rustup-mock = { path = "src/rustup-mock", version = "0.5.0" }
6063
lazy_static = "0.1.15"
6164

65+
[workspace]
66+
members = ["src/ca-loader", "src/download", "src/rustup-dist", "src/rustup-mock", "src/rustup-utils", "src/rustup-win-installer"]
67+
6268
[lib]
6369
name = "rustup"
6470
path = "src/rustup/lib.rs"

appveyor.yml

+18-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
environment:
22
matrix:
3+
- TARGET: i686-pc-windows-msvc
4+
BUILD_MSI: 1
35
- TARGET: i686-pc-windows-gnu
46
MINGW_URL: https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/4.9.2/threads-win32/dwarf/i686-4.9.2-release-win32-dwarf-rt_v4-rev4.7z/download
57
MINGW_ARCHIVE: i686-4.9.2-release-win32-dwarf-rt_v4-rev4.7z
@@ -34,6 +36,10 @@ install:
3436
- if defined MINGW_ARCHIVE curl -L --retry 4 "%MINGW_URL%" -o "%MINGW_ARCHIVE%"
3537
- if defined MINGW_ARCHIVE 7z x -y "%MINGW_ARCHIVE%" > nul
3638
- if defined MINGW_ARCHIVE set PATH=%CD%\%MINGW_DIR%\bin;C:\msys64\usr\bin;%PATH%
39+
40+
# set cargo features for MSI if requested (otherwise empty string)
41+
- set FEATURES=
42+
- if defined BUILD_MSI set FEATURES=--features msi-installed
3743

3844
# let's see what we got
3945
- where gcc rustc cargo
@@ -44,9 +50,18 @@ install:
4450
build: false
4551

4652
test_script:
47-
- cargo build --release --target %TARGET%
48-
- cargo test --release -p rustup-dist --target %TARGET%
49-
- cargo test --release --target %TARGET%
53+
- cargo build --release --target %TARGET% %FEATURES%
54+
- cargo test --release -p rustup-dist --target %TARGET% %FEATURES%
55+
- cargo test --release --target %TARGET% %FEATURES%
56+
- ps: |
57+
if($env:BUILD_MSI) {
58+
cd src\rustup-win-installer
59+
cargo build --release --target $env:TARGET
60+
cd msi
61+
.\build.ps1 -Target $env:TARGET
62+
cd ..\..\..
63+
if($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) }
64+
}
5065
5166
notifications:
5267
- provider: Webhook

ci/prepare-deploy-appveyor.ps1

+5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ if ($env:APPVEYOR_REPO_BRANCH -eq "auto") {
66
exit 0
77
}
88

9+
# Don't do anything for MSI (yet)
10+
if ($env:BUILD_MSI) {
11+
exit 0
12+
}
13+
914
# Copy rustup-init to rustup-setup for backwards compatibility
1015
cp target\${env:TARGET}\release\rustup-init.exe target\${env:TARGET}release\rustup-setup.exe
1116

src/rustup-cli/self_update.rs

+43
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,19 @@ pub fn uninstall(no_prompt: bool) -> Result<()> {
571571
err!("you should probably use your system package manager to uninstall rustup");
572572
process::exit(1);
573573
}
574+
575+
if cfg!(feature = "msi-installed") {
576+
// Get the product code of the MSI installer from the registry
577+
// and spawn `msiexec /x`, then exit immediately
578+
let product_code = try!(get_msi_product_code());
579+
try!(Command::new("msiexec")
580+
.arg("/x")
581+
.arg(product_code)
582+
.spawn()
583+
.chain_err(|| ErrorKind::WindowsUninstallMadness));
584+
process::exit(0);
585+
}
586+
574587
let ref cargo_home = try!(utils::cargo_home());
575588

576589
if !cargo_home.join(&format!("bin/rustup{}", EXE_SUFFIX)).exists() {
@@ -647,6 +660,36 @@ pub fn uninstall(no_prompt: bool) -> Result<()> {
647660
process::exit(0);
648661
}
649662

663+
#[cfg(not(feature = "msi-installed"))]
664+
fn get_msi_product_code() -> Result<String> {
665+
unreachable!()
666+
}
667+
668+
#[cfg(feature = "msi-installed")]
669+
fn get_msi_product_code() -> Result<String> {
670+
use winreg::RegKey;
671+
use winapi::*;
672+
673+
let root = RegKey::predef(HKEY_CURRENT_USER);
674+
let environment = root.open_subkey_with_flags("SOFTWARE\\rustup", KEY_READ);
675+
676+
match environment {
677+
Ok(env) => {
678+
match env.get_value("InstalledProductCode") {
679+
Ok(val) => {
680+
Ok(val)
681+
}
682+
Err(e) => {
683+
Err(e).chain_err(|| ErrorKind::WindowsUninstallMadness)
684+
}
685+
}
686+
}
687+
Err(e) => {
688+
Err(e).chain_err(|| ErrorKind::WindowsUninstallMadness)
689+
}
690+
}
691+
}
692+
650693
#[cfg(unix)]
651694
fn delete_rustup_and_cargo_home() -> Result<()> {
652695
let ref cargo_home = try!(utils::cargo_home());

src/rustup-win-installer/Cargo.toml

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[package]
2+
name = "rustup-win-installer"
3+
version = "0.5.0"
4+
authors = ["Patrick Reisert"]
5+
build = "build.rs"
6+
7+
[lib]
8+
name = "rustup_msi"
9+
crate-type = ["cdylib"]
10+
11+
[dependencies]
12+
winapi = "0.2"
13+
rustup = { path = "../../", version = "0.5.0" }

src/rustup-win-installer/build.rs

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
use std::env;
2+
3+
fn main() {
4+
println!("cargo:rustc-link-lib=static=wcautil");
5+
println!("cargo:rustc-link-lib=static=dutil");
6+
println!("cargo:rustc-link-lib=dylib=msi");
7+
println!("cargo:rustc-link-lib=dylib=user32");
8+
println!("cargo:rustc-link-lib=dylib=mincore");
9+
10+
let wix_path = env::var("WIX").unwrap();
11+
// x86 target is hard-coded because we only build an x86 installer (works just fine on x64)
12+
println!("cargo:rustc-link-search=native={}SDK\\VS2015\\lib\\x86", wix_path);
13+
}
112 KB
Binary file not shown.
145 KB
Binary file not shown.
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
param(
2+
[Parameter(Mandatory=$true)]
3+
[string] $Target
4+
)
5+
6+
$manifest = cargo read-manifest --manifest-path ..\..\..\Cargo.toml | ConvertFrom-Json
7+
$version = $manifest.version.Split(".")
8+
$env:CFG_VER_MAJOR = $version[0]
9+
$env:CFG_VER_MINOR = $version[1]
10+
$env:CFG_VER_PATCH = $version[2]
11+
12+
foreach($file in Get-ChildItem *.wxs) {
13+
$in = $file.Name
14+
$out = $($file.Name.Replace(".wxs",".wixobj"))
15+
&"$($env:WIX)bin\candle.exe" -nologo -arch x86 "-dTARGET=$Target" -ext WixUIExtension -ext WixUtilExtension -out "target\$out" $in
16+
if ($LASTEXITCODE -ne 0) { exit 1 }
17+
}
18+
19+
# ICE57 wrongly complains about per-machine data in per-user install, because it doesn't know that INSTALLLOCATION is in per-user directory
20+
&"$($env:WIX)\bin\light.exe" -nologo -ext WixUIExtension -ext WixUtilExtension -out "target\rustup.msi" -sice:ICE57 $(Get-ChildItem target\*.wixobj)
601 KB
Binary file not shown.
211 KB
Binary file not shown.
361 KB
Binary file not shown.
+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
3+
<!-- TODO: Change paths and names accordingly -->
4+
<?define TargetPath="..\..\..\target\$(var.TARGET)\release"?>
5+
<?define RustupCustomActionDll="$(var.TargetPath)\rustup_msi.dll"?>
6+
<?define RustupExe="$(var.TargetPath)\rustup-init.exe"?>
7+
8+
<Product Id="*" Name="rustup" Language="1033" Version="$(env.CFG_VER_MAJOR).$(env.CFG_VER_MINOR).$(env.CFG_VER_PATCH).0" Manufacturer="The Rust Project Developers" UpgradeCode="09acbb1c-7123-44ac-b2a9-4a04b28ced11">
9+
<Package InstallerVersion="200" Compressed="yes" InstallScope="perUser" />
10+
11+
<!-- TODO: How to configure updates? `AllowDowngrades` automatically removes previously installed versions, no matter what version they have -->
12+
<MajorUpgrade AllowDowngrades="yes" />
13+
14+
<!-- Specifies a single cab file to be embedded in the installer's .msi. -->
15+
<MediaTemplate EmbedCab="yes" CompressionLevel="high" />
16+
17+
<Feature Id="ProductFeature" Title="rustup" Level="1">
18+
<ComponentRef Id="CompleteInstallation" />
19+
</Feature>
20+
21+
<!-- Set some metadata that will appear in the "Installed Programs" list -->
22+
<Property Id="ARPCONTACT" Value="rustup" />
23+
<Property Id="ARPCOMMENTS" Value="rustup – The Rust Toolchain Installer" />
24+
<Property Id="ARPURLINFOABOUT" Value="http://www.rustup.rs" />
25+
<!--<Property Id="ARPHELPLINK" Value="http://www.rustup.rs" />-->
26+
<Property Id="ARPPRODUCTICON" Value="rust.ico" />
27+
28+
<!-- Disable Modify and Repair options (our custom actions based install model does not support repairing) -->
29+
<Property Id="ARPNOMODIFY" Value="1" />
30+
<Property Id="ARPNOREPAIR" Value="1" />
31+
32+
<Icon Id="rust.ico" SourceFile="rust-logo.ico"/>
33+
34+
<!-- Reference the UI defined in ui.wxs -->
35+
<UIRef Id="CustomUI" />
36+
<WixVariable Id="WixUIDialogBmp" Value="dialogbg.bmp" />
37+
<WixVariable Id="WixUIBannerBmp" Value="banner.bmp" />
38+
<!-- TODO: Include/generate license file -->
39+
<!--<WixVariable Id="WixUILicenseRtf" Value="LICENSE.rtf" />-->
40+
41+
<Directory Id="TARGETDIR" Name="SourceDir">
42+
<!-- `INSTALLLOCATION` will be set by custom action -->
43+
<Directory Id="INSTALLLOCATION">
44+
<Directory Id="INSTALLLOCATION_BINARY" Name="bin"/>
45+
</Directory>
46+
</Directory>
47+
48+
<DirectoryRef Id="INSTALLLOCATION_BINARY">
49+
<Component Id="CompleteInstallation" Guid="df2ab9f7-7888-465c-98dd-bb58cbca68f7">
50+
<!-- Write the product code to the registry, so we can use it to run the uninstaller -->
51+
<RegistryKey Root="HKCU" Key="Software\rustup">
52+
<RegistryValue Name="InstalledProductCode" Type="string" Value="[ProductCode]" KeyPath="yes" />
53+
</RegistryKey>
54+
<!-- Install the main rustup.exe binary -->
55+
<File Source="$(var.RustupExe)" Name="rustup.exe"/>
56+
<!-- Append to PATH environment variable -->
57+
<Environment Id="PATH" Name="PATH" Value="[INSTALLLOCATION_BINARY]" Permanent="no" Part="first" Action="set" System="no" />
58+
</Component>
59+
</DirectoryRef>
60+
61+
<!-- Register the DLL containing the custom actions as an embedded binary -->
62+
<Binary Id="RustupCustomActionDll" SourceFile="$(var.RustupCustomActionDll)"/>
63+
<!-- Use a type 51 custom action to send options to deferred custom action `RustupInstall`
64+
(can use arbitrary value that encodes all necessary properties and will be parsed from Rust) -->
65+
<CustomAction Id="SetInstallOptions" Property="RustupInstall" Value="... we can pass arbitrary options here ..." />
66+
<CustomAction Id="RustupSetInstallLocation" BinaryKey="RustupCustomActionDll" DllEntry="RustupSetInstallLocation" Execute="immediate" Return="check" Impersonate="yes"/>
67+
<!-- Propagate the value of `RustupInstallLocation` (set by custom action) to `INSTALLLOCATION` -->
68+
<CustomAction Id="AssignInstallLocation" Directory="INSTALLLOCATION" Value="[RustupInstallLocation]"/>
69+
<CustomAction Id="RustupInstall" BinaryKey="RustupCustomActionDll" DllEntry="RustupInstall" Execute="deferred" Return="check" Impersonate="yes"/>
70+
<CustomAction Id="RustupUninstall" BinaryKey="RustupCustomActionDll" DllEntry="RustupUninstall" Execute="deferred" Return="check" Impersonate="yes"/>
71+
72+
<InstallExecuteSequence>
73+
<DisableRollback Before="InstallInitialize"/>
74+
<Custom Action="RustupSetInstallLocation" After="CostFinalize"/>
75+
<Custom Action="AssignInstallLocation" After="RustupSetInstallLocation"/>
76+
<Custom Action="SetInstallOptions" Before="InstallInitialize">NOT Installed</Custom>
77+
<Custom Action="RustupInstall" After="InstallFiles">NOT Installed</Custom>
78+
<!-- Run RustupUninstall only on true uninstall, not on upgrade -->
79+
<Custom Action="RustupUninstall" After="RemoveFiles">Installed AND (NOT UPGRADINGPRODUCTCODE)</Custom>
80+
</InstallExecuteSequence>
81+
82+
<!-- Send a WM_SETTINGCHANGE message to tell processes like explorer to update their
83+
environments so any new command prompts get the updated %PATH% -->
84+
<CustomActionRef Id="WixBroadcastEnvironmentChange" />
85+
</Product>
86+
</Wix>

src/rustup-win-installer/msi/ui.wxs

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
Based on WixUI_Advanced
4+
-->
5+
6+
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
7+
<Fragment>
8+
<UI Id="CustomUI">
9+
<TextStyle Id="WixUI_Font_Normal" FaceName="Tahoma" Size="8" />
10+
<TextStyle Id="WixUI_Font_Bigger" FaceName="Tahoma" Size="12" />
11+
<TextStyle Id="WixUI_Font_Title" FaceName="Tahoma" Size="9" Bold="yes" />
12+
13+
<Property Id="DefaultUIFont" Value="WixUI_Font_Normal" />
14+
<Property Id="WixUI_Mode" Value="InstallDir" />
15+
16+
<DialogRef Id="DiskCostDlg" />
17+
<DialogRef Id="ErrorDlg" />
18+
<DialogRef Id="FatalError" />
19+
<DialogRef Id="FilesInUse" />
20+
<DialogRef Id="MsiRMFilesInUse" />
21+
<DialogRef Id="PrepareDlg" />
22+
<DialogRef Id="ProgressDlg" />
23+
<DialogRef Id="ResumeDlg" />
24+
<DialogRef Id="UserExit" />
25+
26+
<Publish Dialog="ExitDialog" Control="Finish" Event="EndDialog" Value="Return" Order="999">1</Publish>
27+
28+
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg">1</Publish>
29+
30+
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" Order="1">1</Publish>
31+
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="MaintenanceTypeDlg" Order="2">Installed AND NOT PATCH</Publish>
32+
33+
<Publish Dialog="MaintenanceWelcomeDlg" Control="Next" Event="NewDialog" Value="MaintenanceTypeDlg">1</Publish>
34+
35+
<Publish Dialog="MaintenanceTypeDlg" Control="RepairButton" Event="NewDialog" Value="VerifyReadyDlg">1</Publish>
36+
<Publish Dialog="MaintenanceTypeDlg" Control="RemoveButton" Event="NewDialog" Value="VerifyReadyDlg">1</Publish>
37+
<Publish Dialog="MaintenanceTypeDlg" Control="Back" Event="NewDialog" Value="MaintenanceWelcomeDlg">1</Publish>
38+
</UI>
39+
<UIRef Id="WixUI_Common" />
40+
</Fragment>
41+
</Wix>

0 commit comments

Comments
 (0)