Skip to content

Commit

Permalink
Merge pull request #635 from Boddlnagg/msi-installer
Browse files Browse the repository at this point in the history
[WIP] MSI Installer
  • Loading branch information
brson authored Aug 17, 2016
2 parents 95f38e8 + c12368e commit 39a2014
Show file tree
Hide file tree
Showing 17 changed files with 400 additions and 28 deletions.
58 changes: 33 additions & 25 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ rustls-backend = ["download/rustls-backend"]
# Include in the default set to disable self-update and uninstall.
no-self-update = []

# Used to change behavior of self-update and uninstall if installed via MSI
msi-installed = []

[dependencies]
rustup-dist = { path = "src/rustup-dist", version = "0.5.0" }
rustup-utils = { path = "src/rustup-utils", version = "0.5.0" }
Expand Down Expand Up @@ -59,6 +62,9 @@ kernel32-sys = "0.2.1"
rustup-mock = { path = "src/rustup-mock", version = "0.5.0" }
lazy_static = "0.1.15"

[workspace]
members = ["src/ca-loader", "src/download", "src/rustup-dist", "src/rustup-mock", "src/rustup-utils", "src/rustup-win-installer"]

[lib]
name = "rustup"
path = "src/rustup/lib.rs"
Expand Down
21 changes: 18 additions & 3 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
environment:
matrix:
- TARGET: i686-pc-windows-msvc
BUILD_MSI: 1
- TARGET: i686-pc-windows-gnu
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
MINGW_ARCHIVE: i686-4.9.2-release-win32-dwarf-rt_v4-rev4.7z
Expand Down Expand Up @@ -34,6 +36,10 @@ install:
- if defined MINGW_ARCHIVE curl -L --retry 4 "%MINGW_URL%" -o "%MINGW_ARCHIVE%"
- if defined MINGW_ARCHIVE 7z x -y "%MINGW_ARCHIVE%" > nul
- if defined MINGW_ARCHIVE set PATH=%CD%\%MINGW_DIR%\bin;C:\msys64\usr\bin;%PATH%

# set cargo features for MSI if requested (otherwise empty string)
- set FEATURES=
- if defined BUILD_MSI set FEATURES=--features msi-installed

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

test_script:
- cargo build --release --target %TARGET%
- cargo test --release -p rustup-dist --target %TARGET%
- cargo test --release --target %TARGET%
- cargo build --release --target %TARGET% %FEATURES%
- cargo test --release -p rustup-dist --target %TARGET% %FEATURES%
- cargo test --release --target %TARGET% %FEATURES%
- ps: |
if($env:BUILD_MSI) {
cd src\rustup-win-installer
cargo build --release --target $env:TARGET
cd msi
.\build.ps1 -Target $env:TARGET
cd ..\..\..
if($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) }
}
notifications:
- provider: Webhook
Expand Down
5 changes: 5 additions & 0 deletions ci/prepare-deploy-appveyor.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ if ($env:APPVEYOR_REPO_BRANCH -eq "auto") {
exit 0
}

# Don't do anything for MSI (yet)
if ($env:BUILD_MSI) {
exit 0
}

# Copy rustup-init to rustup-setup for backwards compatibility
cp target\${env:TARGET}\release\rustup-init.exe target\${env:TARGET}release\rustup-setup.exe

Expand Down
43 changes: 43 additions & 0 deletions src/rustup-cli/self_update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,19 @@ pub fn uninstall(no_prompt: bool) -> Result<()> {
err!("you should probably use your system package manager to uninstall rustup");
process::exit(1);
}

if cfg!(feature = "msi-installed") {
// Get the product code of the MSI installer from the registry
// and spawn `msiexec /x`, then exit immediately
let product_code = try!(get_msi_product_code());
try!(Command::new("msiexec")
.arg("/x")
.arg(product_code)
.spawn()
.chain_err(|| ErrorKind::WindowsUninstallMadness));
process::exit(0);
}

let ref cargo_home = try!(utils::cargo_home());

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

#[cfg(not(feature = "msi-installed"))]
fn get_msi_product_code() -> Result<String> {
unreachable!()
}

#[cfg(feature = "msi-installed")]
fn get_msi_product_code() -> Result<String> {
use winreg::RegKey;
use winapi::*;

let root = RegKey::predef(HKEY_CURRENT_USER);
let environment = root.open_subkey_with_flags("SOFTWARE\\rustup", KEY_READ);

match environment {
Ok(env) => {
match env.get_value("InstalledProductCode") {
Ok(val) => {
Ok(val)
}
Err(e) => {
Err(e).chain_err(|| ErrorKind::WindowsUninstallMadness)
}
}
}
Err(e) => {
Err(e).chain_err(|| ErrorKind::WindowsUninstallMadness)
}
}
}

#[cfg(unix)]
fn delete_rustup_and_cargo_home() -> Result<()> {
let ref cargo_home = try!(utils::cargo_home());
Expand Down
13 changes: 13 additions & 0 deletions src/rustup-win-installer/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "rustup-win-installer"
version = "0.5.0"
authors = ["Patrick Reisert"]
build = "build.rs"

[lib]
name = "rustup_msi"
crate-type = ["cdylib"]

[dependencies]
winapi = "0.2"
rustup = { path = "../../", version = "0.5.0" }
13 changes: 13 additions & 0 deletions src/rustup-win-installer/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use std::env;

fn main() {
println!("cargo:rustc-link-lib=static=wcautil");
println!("cargo:rustc-link-lib=static=dutil");
println!("cargo:rustc-link-lib=dylib=msi");
println!("cargo:rustc-link-lib=dylib=user32");
println!("cargo:rustc-link-lib=dylib=mincore");

let wix_path = env::var("WIX").unwrap();
// x86 target is hard-coded because we only build an x86 installer (works just fine on x64)
println!("cargo:rustc-link-search=native={}SDK\\VS2015\\lib\\x86", wix_path);
}
Binary file added src/rustup-win-installer/msi/banner.bmp
Binary file not shown.
Binary file added src/rustup-win-installer/msi/banner.xcf
Binary file not shown.
20 changes: 20 additions & 0 deletions src/rustup-win-installer/msi/build.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
param(
[Parameter(Mandatory=$true)]
[string] $Target
)

$manifest = cargo read-manifest --manifest-path ..\..\..\Cargo.toml | ConvertFrom-Json
$version = $manifest.version.Split(".")
$env:CFG_VER_MAJOR = $version[0]
$env:CFG_VER_MINOR = $version[1]
$env:CFG_VER_PATCH = $version[2]

foreach($file in Get-ChildItem *.wxs) {
$in = $file.Name
$out = $($file.Name.Replace(".wxs",".wixobj"))
&"$($env:WIX)bin\candle.exe" -nologo -arch x86 "-dTARGET=$Target" -ext WixUIExtension -ext WixUtilExtension -out "target\$out" $in
if ($LASTEXITCODE -ne 0) { exit 1 }
}

# ICE57 wrongly complains about per-machine data in per-user install, because it doesn't know that INSTALLLOCATION is in per-user directory
&"$($env:WIX)\bin\light.exe" -nologo -ext WixUIExtension -ext WixUtilExtension -out "target\rustup.msi" -sice:ICE57 $(Get-ChildItem target\*.wixobj)
Binary file added src/rustup-win-installer/msi/dialogbg.bmp
Binary file not shown.
Binary file added src/rustup-win-installer/msi/dialogbg.xcf
Binary file not shown.
Binary file added src/rustup-win-installer/msi/rust-logo.ico
Binary file not shown.
86 changes: 86 additions & 0 deletions src/rustup-win-installer/msi/rustup.wxs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<!-- TODO: Change paths and names accordingly -->
<?define TargetPath="..\..\..\target\$(var.TARGET)\release"?>
<?define RustupCustomActionDll="$(var.TargetPath)\rustup_msi.dll"?>
<?define RustupExe="$(var.TargetPath)\rustup-init.exe"?>

<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">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perUser" />

<!-- TODO: How to configure updates? `AllowDowngrades` automatically removes previously installed versions, no matter what version they have -->
<MajorUpgrade AllowDowngrades="yes" />

<!-- Specifies a single cab file to be embedded in the installer's .msi. -->
<MediaTemplate EmbedCab="yes" CompressionLevel="high" />

<Feature Id="ProductFeature" Title="rustup" Level="1">
<ComponentRef Id="CompleteInstallation" />
</Feature>

<!-- Set some metadata that will appear in the "Installed Programs" list -->
<Property Id="ARPCONTACT" Value="rustup" />
<Property Id="ARPCOMMENTS" Value="rustup – The Rust Toolchain Installer" />
<Property Id="ARPURLINFOABOUT" Value="http://www.rustup.rs" />
<!--<Property Id="ARPHELPLINK" Value="http://www.rustup.rs" />-->
<Property Id="ARPPRODUCTICON" Value="rust.ico" />

<!-- Disable Modify and Repair options (our custom actions based install model does not support repairing) -->
<Property Id="ARPNOMODIFY" Value="1" />
<Property Id="ARPNOREPAIR" Value="1" />

<Icon Id="rust.ico" SourceFile="rust-logo.ico"/>

<!-- Reference the UI defined in ui.wxs -->
<UIRef Id="CustomUI" />
<WixVariable Id="WixUIDialogBmp" Value="dialogbg.bmp" />
<WixVariable Id="WixUIBannerBmp" Value="banner.bmp" />
<!-- TODO: Include/generate license file -->
<!--<WixVariable Id="WixUILicenseRtf" Value="LICENSE.rtf" />-->

<Directory Id="TARGETDIR" Name="SourceDir">
<!-- `INSTALLLOCATION` will be set by custom action -->
<Directory Id="INSTALLLOCATION">
<Directory Id="INSTALLLOCATION_BINARY" Name="bin"/>
</Directory>
</Directory>

<DirectoryRef Id="INSTALLLOCATION_BINARY">
<Component Id="CompleteInstallation" Guid="df2ab9f7-7888-465c-98dd-bb58cbca68f7">
<!-- Write the product code to the registry, so we can use it to run the uninstaller -->
<RegistryKey Root="HKCU" Key="Software\rustup">
<RegistryValue Name="InstalledProductCode" Type="string" Value="[ProductCode]" KeyPath="yes" />
</RegistryKey>
<!-- Install the main rustup.exe binary -->
<File Source="$(var.RustupExe)" Name="rustup.exe"/>
<!-- Append to PATH environment variable -->
<Environment Id="PATH" Name="PATH" Value="[INSTALLLOCATION_BINARY]" Permanent="no" Part="first" Action="set" System="no" />
</Component>
</DirectoryRef>

<!-- Register the DLL containing the custom actions as an embedded binary -->
<Binary Id="RustupCustomActionDll" SourceFile="$(var.RustupCustomActionDll)"/>
<!-- Use a type 51 custom action to send options to deferred custom action `RustupInstall`
(can use arbitrary value that encodes all necessary properties and will be parsed from Rust) -->
<CustomAction Id="SetInstallOptions" Property="RustupInstall" Value="... we can pass arbitrary options here ..." />
<CustomAction Id="RustupSetInstallLocation" BinaryKey="RustupCustomActionDll" DllEntry="RustupSetInstallLocation" Execute="immediate" Return="check" Impersonate="yes"/>
<!-- Propagate the value of `RustupInstallLocation` (set by custom action) to `INSTALLLOCATION` -->
<CustomAction Id="AssignInstallLocation" Directory="INSTALLLOCATION" Value="[RustupInstallLocation]"/>
<CustomAction Id="RustupInstall" BinaryKey="RustupCustomActionDll" DllEntry="RustupInstall" Execute="deferred" Return="check" Impersonate="yes"/>
<CustomAction Id="RustupUninstall" BinaryKey="RustupCustomActionDll" DllEntry="RustupUninstall" Execute="deferred" Return="check" Impersonate="yes"/>

<InstallExecuteSequence>
<DisableRollback Before="InstallInitialize"/>
<Custom Action="RustupSetInstallLocation" After="CostFinalize"/>
<Custom Action="AssignInstallLocation" After="RustupSetInstallLocation"/>
<Custom Action="SetInstallOptions" Before="InstallInitialize">NOT Installed</Custom>
<Custom Action="RustupInstall" After="InstallFiles">NOT Installed</Custom>
<!-- Run RustupUninstall only on true uninstall, not on upgrade -->
<Custom Action="RustupUninstall" After="RemoveFiles">Installed AND (NOT UPGRADINGPRODUCTCODE)</Custom>
</InstallExecuteSequence>

<!-- Send a WM_SETTINGCHANGE message to tell processes like explorer to update their
environments so any new command prompts get the updated %PATH% -->
<CustomActionRef Id="WixBroadcastEnvironmentChange" />
</Product>
</Wix>
41 changes: 41 additions & 0 deletions src/rustup-win-installer/msi/ui.wxs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Based on WixUI_Advanced
-->

<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<UI Id="CustomUI">
<TextStyle Id="WixUI_Font_Normal" FaceName="Tahoma" Size="8" />
<TextStyle Id="WixUI_Font_Bigger" FaceName="Tahoma" Size="12" />
<TextStyle Id="WixUI_Font_Title" FaceName="Tahoma" Size="9" Bold="yes" />

<Property Id="DefaultUIFont" Value="WixUI_Font_Normal" />
<Property Id="WixUI_Mode" Value="InstallDir" />

<DialogRef Id="DiskCostDlg" />
<DialogRef Id="ErrorDlg" />
<DialogRef Id="FatalError" />
<DialogRef Id="FilesInUse" />
<DialogRef Id="MsiRMFilesInUse" />
<DialogRef Id="PrepareDlg" />
<DialogRef Id="ProgressDlg" />
<DialogRef Id="ResumeDlg" />
<DialogRef Id="UserExit" />

<Publish Dialog="ExitDialog" Control="Finish" Event="EndDialog" Value="Return" Order="999">1</Publish>

<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg">1</Publish>

<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" Order="1">1</Publish>
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="MaintenanceTypeDlg" Order="2">Installed AND NOT PATCH</Publish>

<Publish Dialog="MaintenanceWelcomeDlg" Control="Next" Event="NewDialog" Value="MaintenanceTypeDlg">1</Publish>

<Publish Dialog="MaintenanceTypeDlg" Control="RepairButton" Event="NewDialog" Value="VerifyReadyDlg">1</Publish>
<Publish Dialog="MaintenanceTypeDlg" Control="RemoveButton" Event="NewDialog" Value="VerifyReadyDlg">1</Publish>
<Publish Dialog="MaintenanceTypeDlg" Control="Back" Event="NewDialog" Value="MaintenanceWelcomeDlg">1</Publish>
</UI>
<UIRef Id="WixUI_Common" />
</Fragment>
</Wix>
Loading

0 comments on commit 39a2014

Please sign in to comment.