diff --git a/src/rustup-win-installer/msi/rustup.wxs b/src/rustup-win-installer/msi/rustup.wxs index 3d375f6f4e..ef69f132ad 100644 --- a/src/rustup-win-installer/msi/rustup.wxs +++ b/src/rustup-win-installer/msi/rustup.wxs @@ -8,14 +8,24 @@ + + = 601)]]> + + - - + + + + + + + + @@ -35,8 +45,6 @@ - - @@ -46,36 +54,78 @@ - + + + + + + + + + + + - - - - + + + + + + + + + + + + + CARGO_HOME + NOT CARGO_HOME + + + RUSTUP_HOME + NOT RUSTUP_HOME + + + + + + + + + + + + + + + RUSTUP_EXE_EXISTS AND RUSTUP_DIRECTORY_EXISTS + + + INSTALLLOCATION_REGISTRY + NOT INSTALLLOCATION_REGISTRY + - - - - + (can use arbitrary string that encodes all necessary properties and will be parsed from Rust) --> + + + - - - NOT Installed NOT Installed + Installed AND (NOT UPGRADINGPRODUCTCODE) Installed AND (NOT UPGRADINGPRODUCTCODE) diff --git a/src/rustup-win-installer/msi/test-install.ps1 b/src/rustup-win-installer/msi/test-install.ps1 new file mode 100644 index 0000000000..6ac7aa9213 --- /dev/null +++ b/src/rustup-win-installer/msi/test-install.ps1 @@ -0,0 +1,25 @@ +# This script can be used for manually testing the MSI installer. It is not used for AppVeyor CI. + +$env:RUSTFLAGS="-Zunstable-options -Ctarget-feature=+crt-static" + +pushd ..\..\.. +# Build rustup.exe +cargo build --release --target i686-pc-windows-msvc --features msi-installed +popd +if($LastExitCode -ne 0) { exit $LastExitCode } +pushd .. +# Build the CA library +cargo build --release --target i686-pc-windows-msvc +popd +if($LastExitCode -ne 0) { exit $LastExitCode } +# Build the MSI +.\build.ps1 -Target i686-pc-windows-msvc +if($LastExitCode -ne 0) { exit $LastExitCode } +# Run the MSI with logging +$OLD_CARGO_HOME = $env:CARGO_HOME +$OLD_RUSTUP_HOME = $env:RUSTUP_HOME +$env:CARGO_HOME = "$env:USERPROFILE\.cargo-test" +$env:RUSTUP_HOME = "$env:USERPROFILE\.rustup-test" +Start-Process msiexec -ArgumentList "/i target\rustup.msi /L*V target\Install.log" -Wait +$env:CARGO_HOME = $OLD_CARGO_HOME +$env:RUSTUP_HOME = $OLD_RUSTUP_HOME \ No newline at end of file diff --git a/src/rustup-win-installer/msi/test-uninstall.ps1 b/src/rustup-win-installer/msi/test-uninstall.ps1 new file mode 100644 index 0000000000..50e9d688c0 --- /dev/null +++ b/src/rustup-win-installer/msi/test-uninstall.ps1 @@ -0,0 +1,10 @@ +# Uninstall currently installed version of rustup. Does the same thing as `rustup self uninstall`. + +$key = 'HKCU:\SOFTWARE\rustup' +$productCode = (Get-ItemProperty -Path $key -Name InstalledProductCode).InstalledProductCode + +# No need to set CARGO_HOME, because the installation directory is stored in the registry +$OLD_RUSTUP_HOME = $env:RUSTUP_HOME +$env:RUSTUP_HOME = "$env:USERPROFILE\.rustup-test" +msiexec /x "$productCode" /L*V "target\Uninstall.log" +$env:RUSTUP_HOME = $OLD_RUSTUP_HOME \ No newline at end of file diff --git a/src/rustup-win-installer/src/lib.rs b/src/rustup-win-installer/src/lib.rs index 2f2aea0da1..90d2e3bb07 100644 --- a/src/rustup-win-installer/src/lib.rs +++ b/src/rustup-win-installer/src/lib.rs @@ -5,6 +5,7 @@ extern crate rustup; use std::ffi::CString; use std::path::PathBuf; +use std::collections::HashMap; use ::winapi::{HRESULT, PCSTR, UINT, LPCWSTR, LPWSTR, LPVOID}; pub type MSIHANDLE = u32; @@ -17,18 +18,6 @@ pub const LOGMSG_STANDARD: i32 = 2; static TOOLS: &'static [&'static str] = &["rustc", "rustdoc", "cargo", "rust-lldb", "rust-gdb", "rls"]; -#[no_mangle] -/// This is run as an `immediate` action early in the install sequence -pub unsafe extern "system" fn RustupSetInstallLocation(hInstall: MSIHANDLE) -> UINT { - // TODO: error handling (get rid of unwrap) - let name = CString::new("RustupSetInstallLocation").unwrap(); - let hr = WcaInitialize(hInstall, name.as_ptr()); - //let path = ::rustup::utils::cargo_home().unwrap(); - let path = PathBuf::from(::std::env::var_os("USERPROFILE").unwrap()).join(".rustup-test"); - set_property("RustupInstallLocation", path.to_str().unwrap()); - WcaFinalize(hr) -} - #[no_mangle] /// This is be run as a `deferred` action after `InstallFiles` on install and upgrade pub unsafe extern "system" fn RustupInstall(hInstall: MSIHANDLE) -> UINT { @@ -36,12 +25,13 @@ pub unsafe extern "system" fn RustupInstall(hInstall: MSIHANDLE) -> UINT { let hr = WcaInitialize(hInstall, name.as_ptr()); // For deferred custom actions, all data must be passed through the `CustomActionData` property let custom_action_data = get_property("CustomActionData"); - // TODO: use rustup_utils::cargo_home() or pass through CustomActionData - let path = PathBuf::from(::std::env::var_os("USERPROFILE").unwrap()).join(".rustup-test"); + let parsed_ca_data = parse_custom_action_data(&custom_action_data); + let path = PathBuf::from(parsed_ca_data.get("INSTALLLOCATION").unwrap()); let bin_path = path.join("bin"); let rustup_path = bin_path.join("rustup.exe"); let exe_installed = rustup_path.exists(); log(&format!("Hello World from RustupInstall, confirming that rustup.exe has been installed: {}! CustomActionData: {}", exe_installed, custom_action_data)); + log(&format!("Parsed CA data: {:?}", parsed_ca_data)); for tool in TOOLS { let ref tool_path = bin_path.join(&format!("{}.exe", tool)); ::rustup::utils::hardlink_file(&rustup_path, tool_path); @@ -57,15 +47,26 @@ pub unsafe extern "system" fn RustupUninstall(hInstall: MSIHANDLE) -> UINT { let hr = WcaInitialize(hInstall, name.as_ptr()); // For deferred custom actions, all data must be passed through the `CustomActionData` property let custom_action_data = get_property("CustomActionData"); - // TODO: use rustup_utils::cargo_home() or pass through CustomActionData - let path = PathBuf::from(::std::env::var_os("USERPROFILE").unwrap()).join(".rustup-test"); + let parsed_ca_data = parse_custom_action_data(&custom_action_data); + let path = PathBuf::from(parsed_ca_data.get("INSTALLLOCATION").unwrap()); let exe_deleted = !path.join("bin").join("rustup.exe").exists(); log(&format!("Hello World from RustupUninstall, confirming that rustup.exe has been deleted: {}! CustomActionData: {}", exe_deleted, custom_action_data)); - // TODO: Remove .cargo and .rustup - ::rustup::utils::remove_dir("rustup-test", &path, &|_| {}); + log(&format!("Parsed CA data: {:?}", parsed_ca_data)); + ::rustup::utils::remove_dir("cargo_home", &path, &|_| {}); + // TODO: also remove RUSTUP_HOME + //::rustup::utils::remove_dir("rustup_home", &rustup_home, &|_| {}); WcaFinalize(hr) } +fn parse_custom_action_data(ca_data: &str) -> HashMap<&str, &str> { + let mut map = HashMap::new(); + for v in ca_data.split(";") { + let idx = v.find('=').unwrap(); + map.insert(&v[..idx], &v[(idx + 1)..]); + } + map +} + // wrapper for WcaGetProperty (TODO: error handling) fn get_property(name: &str) -> String { let encoded_name = to_wide_chars(name); @@ -76,14 +77,6 @@ fn get_property(name: &str) -> String { result } -// wrapper for WcaSetProperty -fn set_property(name: &str, value: &str) -> HRESULT { - let encoded_name = to_wide_chars(name); - let encoded_value = to_wide_chars(value); - unsafe { WcaSetProperty(encoded_name.as_ptr(), encoded_value.as_ptr()) } -} - - fn log(message: &str) { let msg = CString::new(message).unwrap(); unsafe { WcaLog(LOGMSG_STANDARD, msg.as_ptr()) } @@ -109,7 +102,6 @@ extern "system" { fn WcaInitialize(hInstall: MSIHANDLE, szCustomActionLogName: PCSTR) -> HRESULT; fn WcaFinalize(iReturnValue: HRESULT) -> UINT; fn WcaGetProperty(wzProperty: LPCWSTR, ppwzData: *mut LPWSTR) -> HRESULT; // see documentation for MsiGetProperty - fn WcaSetProperty(wzPropertyName: LPCWSTR, wzPropertyValue: LPCWSTR) -> HRESULT; fn StrFree(p: LPVOID) -> HRESULT; }