finish and clean up

This commit is contained in:
tensor-programming 2019-08-07 17:37:41 -04:00
parent 7de67f6dd9
commit d049fd38bc
4 changed files with 87 additions and 613 deletions

View File

@ -1,3 +0,0 @@
use semver::Version;
use Platform;
use WIX;

View File

@ -1,35 +1,15 @@
#[warn(dead_code)]
use super::common;
use super::settings::Settings;
use super::wix;
use crate::ResultExt;
use cab;
use dirs;
use msi;
use slog::Drain;
use slog_term;
use std;
use std::collections::{BTreeMap, HashMap, HashSet};
use std::env;
use std::ffi::OsStr;
use std::fs;
use std::io::{self, Write};
use std::path::{Path, PathBuf};
use uuid::Uuid;
use std::collections::BTreeMap;
type Package = msi::Package<fs::File>;
// Don't add more files to a cabinet folder that already has this many bytes:
const CABINET_FOLDER_SIZE_LIMIT: u64 = 0x8000;
// The maximum number of resource files we'll put in one cabinet:
const CABINET_MAX_FILES: usize = 1000;
// The maximum number of data bytes we'll put in one cabinet:
const CABINET_MAX_SIZE: u64 = 0x1000_0000;
// File table attribute indicating that a file is "vital":
const FILE_ATTR_VITAL: u16 = 0x200;
// The name of the installer package's sole Feature:
const MAIN_FEATURE_NAME: &str = "MainFeature";
use std::path::PathBuf;
// Info about a resource file (including the main executable) in the bundle.
struct ResourceInfo {
@ -57,152 +37,28 @@ struct DirectoryInfo {
files: Vec<String>,
}
// Info about a CAB archive within the installer package.
struct CabinetInfo {
// The stream name for this cabinet.
name: String,
// The resource files that are in this cabinet.
resources: Vec<ResourceInfo>,
}
pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
common::print_warning("MSI bundle support is still experimental.")?;
let msi_name = format!("{}.msi", settings.bundle_name());
common::print_bundling(&msi_name)?;
let base_dir = settings.project_out_directory().join("bundle/msi");
let msi_path = base_dir.join(&msi_name);
let mut package =
new_empty_package(&msi_path).chain_err(|| "Failed to initialize MSI package")?;
// Generate package metadata:
// let guid = generate_package_guid(settings);
// set_summary_info(&mut package, guid, settings);
// create_property_table(&mut package, guid, settings)
// .chain_err(|| "Failed to generate Property table")?;
// Copy resource files into package:
let mut resources =
collect_resource_info(settings).chain_err(|| "Failed to collect resource file information")?;
let _directories = collect_directory_info(settings, &mut resources)
.chain_err(|| "Failed to collect resource directory information")?;
let cabinets = divide_resources_into_cabinets(resources);
generate_resource_cabinets(&mut package, &cabinets)
.chain_err(|| "Failed to generate resource cabinets")?;
let decorator = slog_term::TermDecorator::new().build();
let drain = slog_term::CompactFormat::new(decorator).build();
let drain = std::sync::Mutex::new(drain).fuse();
let logger = slog::Logger::root(drain, o!());
let wix_path = PathBuf::from("./WixTools");
wix::get_and_extract_wix(&logger, &wix_path)?;
if !wix_path.exists() {
wix::get_and_extract_wix(&logger, &wix_path)?;
}
wix::build_wix_app_installer(&logger, &settings, &wix_path, base_dir)?;
let msi_path = wix::build_wix_app_installer(&logger, &settings, &wix_path)?;
// Set up installer database tables:
// create_directory_table(&mut package, &directories)
// .chain_err(|| "Failed to generate Directory table")?;
// create_feature_table(&mut package, settings).chain_err(|| "Failed to generate Feature table")?;
// create_component_table(&mut package, guid, &directories)
// .chain_err(|| "Failed to generate Component table")?;
// create_feature_components_table(&mut package, &directories)
// .chain_err(|| "Failed to generate FeatureComponents table")?;
// create_media_table(&mut package, &cabinets).chain_err(|| "Failed to generate Media table")?;
// create_file_table(&mut package, &cabinets).chain_err(|| "Failed to generate File table")?;
// // TODO: Create other needed tables.
// // Create app icon:
// package.create_table(
// "Icon",
// vec![
// msi::Column::build("Name").primary_key().id_string(72),
// msi::Column::build("Data").binary(),
// ],
// )?;
// let icon_name = format!("{}.ico", settings.binary_name());
// {
// let stream_name = format!("Icon.{}", icon_name);
// let mut stream = package.write_stream(&stream_name)?;
// create_app_icon(&mut stream, settings)?;
// }
// package.insert_rows(msi::Insert::into("Icon").row(vec![
// msi::Value::Str(icon_name.clone()),
// msi::Value::from("Name"),
// ]))?;
// package.flush()?;
Ok(vec![msi_path])
}
fn new_empty_package(msi_path: &Path) -> crate::Result<Package> {
if let Some(parent) = msi_path.parent() {
fs::create_dir_all(&parent).chain_err(|| format!("Failed to create directory {:?}", parent))?;
}
let msi_file = fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(true)
.open(msi_path)
.chain_err(|| format!("Failed to create file {:?}", msi_path))?;
let package = msi::Package::create(msi::PackageType::Installer, msi_file)?;
Ok(package)
}
// Populates the summary metadata for the package from the bundle settings.
fn set_summary_info(package: &mut Package, package_guid: Uuid, settings: &Settings) {
let summary_info = package.summary_info_mut();
summary_info.set_creation_time_to_now();
summary_info.set_subject(settings.bundle_name().to_string());
summary_info.set_uuid(package_guid);
summary_info.set_comments(settings.short_description().to_string());
if let Some(authors) = settings.authors_comma_separated() {
summary_info.set_author(authors);
}
let creating_app = format!("cargo-bundle v{}", crate_version!());
summary_info.set_creating_application(creating_app);
}
// Creates and populates the `Property` database table for the package.
fn create_property_table(
package: &mut Package,
package_guid: Uuid,
settings: &Settings,
) -> crate::Result<()> {
let authors = settings.authors_comma_separated().unwrap_or(String::new());
package.create_table(
"Property",
vec![
msi::Column::build("Property").primary_key().id_string(72),
msi::Column::build("Value").text_string(0),
],
)?;
package.insert_rows(
msi::Insert::into("Property")
.row(vec![
msi::Value::from("Manufacturer"),
msi::Value::Str(authors),
])
.row(vec![
msi::Value::from("ProductCode"),
msi::Value::from(package_guid),
])
.row(vec![
msi::Value::from("ProductLanguage"),
msi::Value::from(msi::Language::from_tag("en-US")),
])
.row(vec![
msi::Value::from("ProductName"),
msi::Value::from(settings.bundle_name()),
])
.row(vec![
msi::Value::from("ProductVersion"),
msi::Value::from(settings.version_string()),
]),
)?;
Ok(())
}
// Returns a list of `ResourceInfo` structs for the binary executable and all
// the resource files that should be included in the package.
fn collect_resource_info(settings: &Settings) -> crate::Result<Vec<ResourceInfo>> {
@ -284,339 +140,3 @@ fn collect_directory_info(
}
Ok(dir_map.into_iter().map(|(_k, v)| v).collect())
}
// Divides up the list of resource into some number of cabinets, subject to a
// few contraints: 1) no one cabinet will have two resources with the same
// filename, 2) no one cabinet will have more than `CABINET_MAX_FILES` files
// in it, and 3) no one cabinet will contain mroe than `CABINET_MAX_SIZE`
// bytes of data (unless that cabinet consists of a single file that is
// already bigger than that).
fn divide_resources_into_cabinets(mut resources: Vec<ResourceInfo>) -> Vec<CabinetInfo> {
let mut cabinets = Vec::new();
while !resources.is_empty() {
let mut filenames = HashSet::<String>::new();
let mut total_size = 0;
let mut leftovers = Vec::<ResourceInfo>::new();
let mut cabinet = CabinetInfo {
name: format!("rsrc{:04}.cab", cabinets.len()),
resources: Vec::new(),
};
for resource in resources.into_iter() {
if cabinet.resources.len() >= CABINET_MAX_FILES
|| (!cabinet.resources.is_empty() && total_size + resource.size > CABINET_MAX_SIZE)
|| filenames.contains(&resource.filename)
{
leftovers.push(resource);
} else {
filenames.insert(resource.filename.clone());
total_size += resource.size;
cabinet.resources.push(resource);
}
}
cabinets.push(cabinet);
resources = leftovers;
}
cabinets
}
// Creates the CAB archives within the package that contain the binary
// execuable and all the resource files.
fn generate_resource_cabinets(
package: &mut Package,
cabinets: &[CabinetInfo],
) -> crate::Result<()> {
for cabinet_info in cabinets.iter() {
let mut builder = cab::CabinetBuilder::new();
let mut file_map = HashMap::<String, &Path>::new();
let mut resource_index: usize = 0;
while resource_index < cabinet_info.resources.len() {
let folder = builder.add_folder(cab::CompressionType::MsZip);
let mut folder_size: u64 = 0;
while resource_index < cabinet_info.resources.len() && folder_size < CABINET_FOLDER_SIZE_LIMIT
{
let resource = &cabinet_info.resources[resource_index];
folder_size += resource.size;
folder.add_file(resource.filename.as_str());
debug_assert!(!file_map.contains_key(&resource.filename));
file_map.insert(resource.filename.clone(), &resource.source_path);
resource_index += 1;
}
}
let stream = package.write_stream(cabinet_info.name.as_str())?;
let mut cabinet_writer = builder.build(stream)?;
while let Some(mut file_writer) = cabinet_writer.next_file()? {
debug_assert!(file_map.contains_key(file_writer.file_name()));
let file_path = file_map.get(file_writer.file_name()).unwrap();
let mut file = fs::File::open(file_path)?;
io::copy(&mut file, &mut file_writer)?;
}
cabinet_writer.finish()?;
}
Ok(())
}
// Creates and populates the `Directory` database table for the package.
fn create_directory_table(
package: &mut Package,
directories: &[DirectoryInfo],
) -> crate::Result<()> {
package.create_table(
"Directory",
vec![
msi::Column::build("Directory").primary_key().id_string(72),
msi::Column::build("Directory_Parent")
.nullable()
.foreign_key("Directory", 1)
.id_string(72),
msi::Column::build("DefaultDir")
.category(msi::Category::DefaultDir)
.string(255),
],
)?;
let mut rows = Vec::new();
for directory in directories.iter() {
rows.push(vec![
msi::Value::Str(directory.key.clone()),
msi::Value::Str(directory.parent_key.clone()),
msi::Value::Str(directory.name.clone()),
]);
}
package.insert_rows(
msi::Insert::into("Directory")
.row(vec![
msi::Value::from("TARGETDIR"),
msi::Value::Null,
msi::Value::from("SourceDir"),
])
.row(vec![
msi::Value::from("ProgramFilesFolder"),
msi::Value::from("TARGETDIR"),
msi::Value::from("."),
])
.rows(rows),
)?;
Ok(())
}
// Creates and populates the `Feature` database table for the package. The
// package will have a single main feature that installs everything.
fn create_feature_table(package: &mut Package, settings: &Settings) -> crate::Result<()> {
package.create_table(
"Feature",
vec![
msi::Column::build("Feature").primary_key().id_string(38),
msi::Column::build("Feature_Parent")
.nullable()
.foreign_key("Feature", 1)
.id_string(38),
msi::Column::build("Title").nullable().text_string(64),
msi::Column::build("Description")
.nullable()
.text_string(255),
msi::Column::build("Display")
.nullable()
.range(0, 0x7fff)
.int16(),
msi::Column::build("Level").range(0, 0x7fff).int16(),
msi::Column::build("Directory_")
.nullable()
.foreign_key("Directory", 1)
.id_string(72),
msi::Column::build("Attributes").int16(),
],
)?;
package.insert_rows(msi::Insert::into("Feature").row(vec![
msi::Value::from(MAIN_FEATURE_NAME),
msi::Value::Null,
msi::Value::from(settings.bundle_name()),
msi::Value::Null,
msi::Value::Int(1),
msi::Value::Int(3),
msi::Value::from("INSTALLDIR"),
msi::Value::Int(0),
]))?;
Ok(())
}
// Creates and populates the `Component` database table for the package. One
// component is created for each subdirectory under in the install dir.
fn create_component_table(
package: &mut Package,
package_guid: Uuid,
directories: &[DirectoryInfo],
) -> crate::Result<()> {
package.create_table(
"Component",
vec![
msi::Column::build("Component").primary_key().id_string(72),
msi::Column::build("ComponentId")
.nullable()
.category(msi::Category::Guid)
.string(38),
msi::Column::build("Directory_")
.nullable()
.foreign_key("Directory", 1)
.id_string(72),
msi::Column::build("Attributes").int16(),
msi::Column::build("Condition")
.nullable()
.category(msi::Category::Condition)
.string(255),
msi::Column::build("KeyPath").nullable().id_string(72),
],
)?;
let mut rows = Vec::new();
for directory in directories.iter() {
if !directory.files.is_empty() {
let hash_input = directory.files.join("/");
rows.push(vec![
msi::Value::Str(directory.key.clone()),
msi::Value::from(Uuid::new_v5(&package_guid, &hash_input)),
msi::Value::Str(directory.key.clone()),
msi::Value::Int(0),
msi::Value::Null,
msi::Value::Str(directory.files[0].clone()),
]);
}
}
package.insert_rows(msi::Insert::into("Component").rows(rows))?;
Ok(())
}
// Creates and populates the `FeatureComponents` database table for the
// package. All components are added to the package's single main feature.
fn create_feature_components_table(
package: &mut Package,
directories: &[DirectoryInfo],
) -> crate::Result<()> {
package.create_table(
"FeatureComponents",
vec![
msi::Column::build("Feature_")
.primary_key()
.foreign_key("Component", 1)
.id_string(38),
msi::Column::build("Component_")
.primary_key()
.foreign_key("Component", 1)
.id_string(72),
],
)?;
let mut rows = Vec::new();
for directory in directories.iter() {
if !directory.files.is_empty() {
rows.push(vec![
msi::Value::from(MAIN_FEATURE_NAME),
msi::Value::Str(directory.key.clone()),
]);
}
}
package.insert_rows(msi::Insert::into("FeatureComponents").rows(rows))?;
Ok(())
}
// Creates and populates the `Media` database table for the package, with one
// entry for each CAB archive within the package.
fn create_media_table(package: &mut Package, cabinets: &[CabinetInfo]) -> crate::Result<()> {
package.create_table(
"Media",
vec![
msi::Column::build("DiskId")
.primary_key()
.range(1, 0x7fff)
.int16(),
msi::Column::build("LastSequence").range(0, 0x7fff).int16(),
msi::Column::build("DiskPrompt").nullable().text_string(64),
msi::Column::build("Cabinet")
.nullable()
.category(msi::Category::Cabinet)
.string(255),
msi::Column::build("VolumeLabel").nullable().text_string(32),
msi::Column::build("Source")
.nullable()
.category(msi::Category::Property)
.string(32),
],
)?;
let mut disk_id: i32 = 0;
let mut last_seq: i32 = 0;
let mut rows = Vec::new();
for cabinet in cabinets.iter() {
disk_id += 1;
last_seq += cabinet.resources.len() as i32;
rows.push(vec![
msi::Value::Int(disk_id),
msi::Value::Int(last_seq),
msi::Value::Null,
msi::Value::Str(format!("#{}", cabinet.name)),
msi::Value::Null,
msi::Value::Null,
]);
}
package.insert_rows(msi::Insert::into("Media").rows(rows))?;
Ok(())
}
// Creates and populates the `File` database table for the package, with one
// entry for each resource file to be installed (including the main
// executable).
fn create_file_table(package: &mut Package, cabinets: &[CabinetInfo]) -> crate::Result<()> {
package.create_table(
"File",
vec![
msi::Column::build("File").primary_key().id_string(72),
msi::Column::build("Component_")
.foreign_key("Component", 1)
.id_string(72),
msi::Column::build("FileName")
.category(msi::Category::Filename)
.string(255),
msi::Column::build("FileSize").range(0, 0x7fffffff).int32(),
msi::Column::build("Version")
.nullable()
.category(msi::Category::Version)
.string(72),
msi::Column::build("Language")
.nullable()
.category(msi::Category::Language)
.string(20),
msi::Column::build("Attributes")
.nullable()
.range(0, 0x7fff)
.int16(),
msi::Column::build("Sequence").range(1, 0x7fff).int16(),
],
)?;
let mut rows = Vec::new();
let mut sequence: i32 = 1;
for cabinet in cabinets.iter() {
for resource in cabinet.resources.iter() {
rows.push(vec![
msi::Value::Str(format!("r{:04}", sequence)),
msi::Value::Str(resource.component_key.clone()),
msi::Value::Str(resource.filename.clone()),
msi::Value::Int(resource.size as i32),
msi::Value::Null,
msi::Value::Null,
msi::Value::from(FILE_ATTR_VITAL),
msi::Value::Int(sequence),
]);
sequence += 1;
}
}
package.insert_rows(msi::Insert::into("File").rows(rows))?;
Ok(())
}
fn create_app_icon<W: Write>(writer: &mut W, settings: &Settings) -> crate::Result<()> {
// Prefer ICO files.
for icon_path in settings.icon_files() {
let icon_path = icon_path?;
if icon_path.extension() == Some(OsStr::new("ico")) {
io::copy(&mut fs::File::open(icon_path)?, writer)?;
return Ok(());
}
}
// TODO: Convert from other formats.
Ok(())
}

View File

@ -20,30 +20,20 @@
<Package Id="*"
Keywords="Installer"
InstallerVersion="450"
InstallerVersion="200"
Languages="1033"
Compressed="yes"
InstallScope="perMachine"
SummaryCodepage="1252"/>
<MajorUpgrade
Schedule="afterInstallInitialize"
DowngradeErrorMessage="A newer version of [ProductName] is already installed. Setup will now exit."/>
<Media Id="1" Cabinet="app.cab" EmbedCab="yes" />
<Media Id="1" Cabinet="media1.cab" EmbedCab="yes"/>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="$(var.PlatformProgramFilesFolder)" Name="PFiles">
<Directory Id="APPLICATIONFOLDER" Name="{{{product_name}}}">
<Component Id="Path" Guid="{{{path_component_guid}}}" Win64="$(var.Win64)" KeyPath="yes">
<Environment
Id="PATH"
Name="PATH"
Value="[Bin]"
Permanent="no"
Part="last"
Action="set"
System="yes"/>
<File Id="PathFile" Source="{{{app_exe_source}}}" />
</Component>
</Directory>
</Directory>
@ -58,7 +48,7 @@
AllowAdvertise="no"
Display="expand"
Absent="disallow">
<ComponentGroupRef Id="AppFiles"/>
<Feature
Id="Environment"
Title="PATH Environment Variable"
@ -71,9 +61,5 @@
<SetProperty Id="ARPINSTALLLOCATION" Value="[APPLICATIONFOLDER]" After="CostFinalize"/>
<UI>
<UIRef Id="WixUI_FeatureTree"/>
</UI>
</Product>
</Wix>

View File

@ -16,17 +16,19 @@ pub const WIX_URL: &str =
"https://github.com/wixtoolset/wix3/releases/download/wix3111rtm/wix311-binaries.zip";
pub const WIX_SHA256: &str = "37f0a533b0978a454efb5dc3bd3598becf9660aaf4287e55bf68ca6b527d051d";
const VC_REDIST_X86_URL: &str =
"https://download.visualstudio.microsoft.com/download/pr/c8edbb87-c7ec-4500-a461-71e8912d25e9/99ba493d660597490cbb8b3211d2cae4/vc_redist.x86.exe";
// For Cross Platform Complilation.
const VC_REDIST_X86_SHA256: &str =
"3a43e8a55a3f3e4b73d01872c16d47a19dd825756784f4580187309e7d1fcb74";
// const VC_REDIST_X86_URL: &str =
// "https://download.visualstudio.microsoft.com/download/pr/c8edbb87-c7ec-4500-a461-71e8912d25e9/99ba493d660597490cbb8b3211d2cae4/vc_redist.x86.exe";
const VC_REDIST_X64_URL: &str =
"https://download.visualstudio.microsoft.com/download/pr/9e04d214-5a9d-4515-9960-3d71398d98c3/1e1e62ab57bbb4bf5199e8ce88f040be/vc_redist.x64.exe";
// const VC_REDIST_X86_SHA256: &str =
// "3a43e8a55a3f3e4b73d01872c16d47a19dd825756784f4580187309e7d1fcb74";
const VC_REDIST_X64_SHA256: &str =
"d6cd2445f68815fe02489fafe0127819e44851e26dfbe702612bc0d223cbbc2b";
// const VC_REDIST_X64_URL: &str =
// "https://download.visualstudio.microsoft.com/download/pr/9e04d214-5a9d-4515-9960-3d71398d98c3/1e1e62ab57bbb4bf5199e8ce88f040be/vc_redist.x64.exe";
// const VC_REDIST_X64_SHA256: &str =
// "d6cd2445f68815fe02489fafe0127819e44851e26dfbe702612bc0d223cbbc2b";
// A v4 UUID that was generated specifically for cargo-bundle, to be used as a
// namespace for generating v5 UUIDs from bundle identifier strings.
@ -72,11 +74,7 @@ fn download_and_verify(logger: &Logger, url: &str, hash: &str) -> Result<Vec<u8>
}
fn app_installer_dir(settings: &Settings) -> PathBuf {
let arch = match settings.binary_arch() {
"i686-pc-windows-msvc" => "x86",
"x86_64-pc-windows-msvc" => "amd64",
target => panic!("unsupported target: {}", target),
};
let arch = "x64";
settings.project_out_directory().to_path_buf().join(format!(
"{}.{}.msi",
@ -126,56 +124,58 @@ pub fn get_and_extract_wix(logger: &Logger, path: &Path) -> Result<(), String> {
extract_zip(&data, path)
}
fn run_heat_exe(
logger: &Logger,
wix_toolset_path: &Path,
build_path: &Path,
harvest_dir: &Path,
platform: &str,
) -> Result<(), String> {
let mut args = vec!["dir"];
// For if bundler needs DLL files.
let harvest_str = harvest_dir.display().to_string();
// fn run_heat_exe(
// logger: &Logger,
// wix_toolset_path: &Path,
// build_path: &Path,
// harvest_dir: &Path,
// platform: &str,
// ) -> Result<(), String> {
// let mut args = vec!["dir"];
args.push(&harvest_str);
args.push("-platform");
args.push(platform);
args.push("-cg");
args.push("AppFiles");
args.push("-dr");
args.push("APPLICATIONFOLDER");
args.push("-gg");
args.push("-srd");
args.push("-out");
args.push("appdir.wxs");
args.push("-var");
args.push("var.SourceDir");
// let harvest_str = harvest_dir.display().to_string();
let heat_exe = wix_toolset_path.join("head.exe");
// args.push(&harvest_str);
// args.push("-platform");
// args.push(platform);
// args.push("-cg");
// args.push("AppFiles");
// args.push("-dr");
// args.push("APPLICATIONFOLDER");
// args.push("-gg");
// args.push("-srd");
// args.push("-out");
// args.push("appdir.wxs");
// args.push("-var");
// args.push("var.SourceDir");
let mut cmd = Command::new(&heat_exe)
.args(&args)
.stdout(Stdio::piped())
.current_dir(build_path)
.spawn()
.expect("error running heat.exe");
// let heat_exe = wix_toolset_path.join("heat.exe");
{
let stdout = cmd.stdout.as_mut().unwrap();
let reader = BufReader::new(stdout);
// let mut cmd = Command::new(&heat_exe)
// .args(&args)
// .stdout(Stdio::piped())
// .current_dir(build_path)
// .spawn()
// .expect("error running heat.exe");
for line in reader.lines() {
info!(logger, "{}", line.unwrap());
}
}
// {
// let stdout = cmd.stdout.as_mut().unwrap();
// let reader = BufReader::new(stdout);
let status = cmd.wait().unwrap();
if status.success() {
Ok(())
} else {
Err("error running heat.exe".to_string())
}
}
// for line in reader.lines() {
// info!(logger, "{}", line.unwrap());
// }
// }
// let status = cmd.wait().unwrap();
// if status.success() {
// Ok(())
// } else {
// Err("error running heat.exe".to_string())
// }
// }
fn run_candle(
settings: &Settings,
@ -187,10 +187,6 @@ fn run_candle(
let arch = "x64";
let args = vec![
"-ext".to_string(),
"WixBalExtension".to_string(),
"-ext".to_string(),
"WixUtilExtension".to_string(),
"-arch".to_string(),
arch.to_string(),
wxs_file_name.to_string(),
@ -229,7 +225,7 @@ fn run_light(
build_path: &Path,
wixobjs: &[&str],
output_path: &Path,
) -> Result<(), String> {
) -> Result<PathBuf, String> {
let light_exe = wix_toolset_path.join("light.exe");
let mut args: Vec<String> = vec!["-o".to_string(), output_path.display().to_string()];
@ -257,7 +253,7 @@ fn run_light(
let status = cmd.wait().unwrap();
if status.success() {
Ok(())
Ok(output_path.to_path_buf())
} else {
Err("error running light.exe".to_string())
}
@ -267,15 +263,10 @@ pub fn build_wix_app_installer(
logger: &Logger,
settings: &Settings,
wix_toolset_path: &Path,
current_dir: PathBuf,
) -> Result<(), String> {
let arch = match settings.binary_arch() {
"i686-pc-windows-msvc" => "x86",
"x86_64-pc-windows-msvc" => "x64",
target => return Err(format!("unsupported target: {}", target)),
};
) -> Result<PathBuf, String> {
let arch = "x64";
info!(logger, "Target: {}", settings.binary_arch());
info!(logger, "Target: {}", arch);
let output_path = settings.project_out_directory().join("wix").join(arch);
@ -283,36 +274,24 @@ pub fn build_wix_app_installer(
data.insert("product_name", settings.bundle_name());
data.insert("version", settings.version_string());
let upgrade_code = if arch == "x86" {
Uuid::new_v5(
&uuid::NAMESPACE_DNS,
format!("{}.app.x64", &settings.bundle_name()).as_str(),
)
.to_string()
} else if arch == "x64" {
Uuid::new_v5(
&uuid::NAMESPACE_DNS,
format!("{}.app.x64", &settings.bundle_name()).as_str(),
)
.to_string()
} else {
return Err(format!("unsupported target: {}", arch));
};
let manufacturer = settings.bundle_identifier().to_string();
data.insert("manufacturer", manufacturer.as_str());
let upgrade_code = Uuid::new_v5(
&uuid::NAMESPACE_DNS,
format!("{}.app.x64", &settings.binary_name()).as_str(),
)
.to_string();
data.insert("upgrade_code", &upgrade_code);
data.insert("upgrade_code", &upgrade_code.as_str());
let path_guid = generate_package_guid(settings).to_string();
data.insert("path_component_guid", &path_guid.as_str());
let app_exe_name = settings
.binary_path()
.file_name()
.unwrap()
.to_string_lossy()
.to_string();
let app_exe_name = settings.binary_name().to_string();
data.insert("app_exe_name", &app_exe_name);
let app_exe_source = settings.binary_path().display().to_string();
data.insert("app_exe_source", &app_exe_source);
let temp = HANDLEBARS
@ -328,23 +307,15 @@ pub fn build_wix_app_installer(
let main_wxs_path = output_path.join("main.wxs");
write(&main_wxs_path, temp).or_else(|e| Err(e.to_string()))?;
run_heat_exe(
logger,
&wix_toolset_path,
&output_path,
&Settings::get_workspace_dir(&current_dir),
arch,
)?;
let input_basenames = vec!["main", "appdir"];
let input_basenames = vec!["main"];
for basename in &input_basenames {
let wxs = format!("{}.wxs", basename);
run_candle(settings, logger, &wix_toolset_path, &output_path, &wxs)?;
}
let wixobjs = vec!["main.wixobj", "appdir.wixobj"];
run_light(
let wixobjs = vec!["main.wixobj"];
let target = run_light(
logger,
&wix_toolset_path,
&output_path,
@ -352,5 +323,5 @@ pub fn build_wix_app_installer(
&app_installer_dir(settings),
)?;
Ok(())
Ok(target)
}