Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] MSI Installer #635

Merged
merged 10 commits into from
Aug 17, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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