mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
Refactor wxs into a dedicated handlebar template
This commit is contained in:
@@ -1,16 +1,12 @@
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Text;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml;
|
||||
using Markdig;
|
||||
using MarkdigExtensions.RtfRenderer;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NuGet.Versioning;
|
||||
using Velopack.Core;
|
||||
using Velopack.Core.Abstractions;
|
||||
using Velopack.NuGet;
|
||||
using Velopack.Packaging.Windows.Msi;
|
||||
using Velopack.Util;
|
||||
using Velopack.Windows;
|
||||
|
||||
@@ -95,23 +91,16 @@ public class WindowsPackCommandRunner : PackageBuilder<WindowsPackOptions>
|
||||
protected string GetShortcutLocations()
|
||||
{
|
||||
if (String.IsNullOrWhiteSpace(Options.Shortcuts))
|
||||
return null;
|
||||
return "None";
|
||||
|
||||
try {
|
||||
var shortcuts = GetShortcuts();
|
||||
var flags = GetShortcuts();
|
||||
var names = Enum.GetValues(typeof(ShortcutLocation))
|
||||
.Cast<ShortcutLocation>()
|
||||
.Where(f => f != ShortcutLocation.None && flags.HasFlag(f))
|
||||
.Select(f => f.ToString())
|
||||
.ToList();
|
||||
|
||||
if (shortcuts.Count == 0)
|
||||
return null;
|
||||
|
||||
var shortcutString = string.Join(",", shortcuts.Select(x => x.ToString()));
|
||||
Log.Debug($"Shortcut Locations: {shortcutString}");
|
||||
return shortcutString;
|
||||
} catch (Exception ex) {
|
||||
throw new UserInfoException(
|
||||
$"Invalid shortcut locations '{Options.Shortcuts}'. " +
|
||||
$"Valid values for comma delimited list are: {string.Join(", ", Enum.GetNames(typeof(ShortcutLocation)))}." +
|
||||
$"Error was {ex.Message}");
|
||||
}
|
||||
return names.Count > 0 ? string.Join(",", names) : "None";
|
||||
}
|
||||
|
||||
protected string GetRuntimeDependencies()
|
||||
@@ -183,20 +172,11 @@ public class WindowsPackCommandRunner : PackageBuilder<WindowsPackOptions>
|
||||
return String.Join(",", validated);
|
||||
}
|
||||
|
||||
protected override Task CreateSetupPackage(Action<int> progress, string releasePkg, string packDir, string targetSetupExe, Func<string, VelopackAssetType, string> createAsset)
|
||||
protected override Task CreateSetupPackage(Action<int> progress, string releasePkg, string packDir, string targetSetupExe,
|
||||
Func<string, VelopackAssetType, string> createAsset)
|
||||
{
|
||||
void setupExeProgress(int x)
|
||||
{
|
||||
if (Options.BuildMsi) {
|
||||
progress(x / 2);
|
||||
} else {
|
||||
progress(x);
|
||||
}
|
||||
}
|
||||
void msiProgress(int value)
|
||||
{
|
||||
progress(50 + value / 2);
|
||||
}
|
||||
var setupExeProgress = Options.BuildMsi ? CoreUtil.CreateProgressDelegate(progress, 0, 50) : progress;
|
||||
var msiProgress = CoreUtil.CreateProgressDelegate(progress, 50, 100);
|
||||
|
||||
var bundledZip = new ZipPackage(releasePkg);
|
||||
IoUtil.Retry(() => File.Copy(HelperFile.SetupPath, targetSetupExe, true));
|
||||
@@ -216,7 +196,7 @@ public class WindowsPackCommandRunner : PackageBuilder<WindowsPackOptions>
|
||||
setupExeProgress(50);
|
||||
Log.Debug("Signing Setup bundle");
|
||||
SignFilesImpl(CoreUtil.CreateProgressDelegate(setupExeProgress, 50, 100), targetSetupExe);
|
||||
Log.Debug($"Setup bundle created '{Path.GetFileName(targetSetupExe)}'.");
|
||||
Log.Info($"Setup bundle created '{Path.GetFileName(targetSetupExe)}'.");
|
||||
setupExeProgress(100);
|
||||
|
||||
if (Options.BuildMsi && VelopackRuntimeInfo.IsWindows) {
|
||||
@@ -346,67 +326,8 @@ public class WindowsPackCommandRunner : PackageBuilder<WindowsPackOptions>
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
private void CompileWixTemplateToMsi(Action<int> progress,
|
||||
DirectoryInfo portableDirectory, string msiFilePath)
|
||||
private void CompileWixTemplateToMsi(Action<int> progress, DirectoryInfo portableDirectory, string msiFilePath)
|
||||
{
|
||||
bool packageAs64Bit =
|
||||
Options.TargetRuntime.Architecture is not RuntimeCpu.x86;
|
||||
|
||||
Log.Info($"Compiling msi installer in {(packageAs64Bit ? "64-bit" : "32-bit")} mode");
|
||||
|
||||
var outputDirectory = portableDirectory.Parent.CreateSubdirectory("msi");
|
||||
var culture = CultureInfo.GetCultureInfo("en-US").TextInfo.ANSICodePage;
|
||||
|
||||
// WiX Identifiers may contain ASCII characters A-Z, a-z, digits, underscores (_), or
|
||||
// periods(.). Every identifier must begin with either a letter or an underscore.
|
||||
var wixId = Regex.Replace(Options.PackId, @"[^\w\.]", "_");
|
||||
if (char.GetUnicodeCategory(wixId[0]) == UnicodeCategory.DecimalDigitNumber)
|
||||
wixId = "_" + wixId;
|
||||
|
||||
var msiVersion = Options.MsiVersionOverride;
|
||||
if (string.IsNullOrWhiteSpace(msiVersion)) {
|
||||
var parsedVersion = SemanticVersion.Parse(Options.PackVersion);
|
||||
msiVersion = $"{parsedVersion.Major}.{parsedVersion.Minor}.{parsedVersion.Patch}.0";
|
||||
}
|
||||
|
||||
static string SanitizeDirectoryString(string name)
|
||||
=> string.Join("_", name.Split(Path.GetInvalidPathChars()));
|
||||
|
||||
static string FormatXmlMessage(string message)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(message))
|
||||
return "";
|
||||
|
||||
StringBuilder sb = new();
|
||||
XmlWriterSettings settings = new() {
|
||||
ConformanceLevel = ConformanceLevel.Fragment,
|
||||
NewLineHandling = NewLineHandling.None,
|
||||
};
|
||||
using XmlWriter writer = XmlWriter.Create(sb, settings);
|
||||
writer.WriteString(message);
|
||||
writer.Flush();
|
||||
var rv = sb.ToString();
|
||||
rv = rv.Replace("\r", " ").Replace("\n", " ");
|
||||
return rv;
|
||||
}
|
||||
|
||||
static string GetFileContent(string filePath)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(filePath))
|
||||
return "";
|
||||
string fileContents = File.ReadAllText(filePath, Encoding.UTF8);
|
||||
return fileContents;
|
||||
}
|
||||
|
||||
static string RenderMarkdownAsPlainText(string markdown)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(markdown))
|
||||
return "";
|
||||
return Markdown.ToPlainText(markdown);
|
||||
}
|
||||
|
||||
string licenseFile = null;
|
||||
|
||||
string GetLicenseRtfFile()
|
||||
{
|
||||
string license = Options.InstLicenseRtf;
|
||||
@@ -416,8 +337,7 @@ public class WindowsPackCommandRunner : PackageBuilder<WindowsPackOptions>
|
||||
|
||||
license = Options.InstLicense;
|
||||
if (!string.IsNullOrWhiteSpace(license)) {
|
||||
|
||||
licenseFile = Path.Combine(outputDirectory.FullName, wixId + "_license.rtf");
|
||||
var licenseFile = Path.Combine(portableDirectory.Parent!.FullName, "license.rtf");
|
||||
using var writer = new StreamWriter(licenseFile);
|
||||
var renderer = new RtfRenderer(writer);
|
||||
renderer.StartDocument();
|
||||
@@ -429,287 +349,9 @@ public class WindowsPackCommandRunner : PackageBuilder<WindowsPackOptions>
|
||||
return null;
|
||||
}
|
||||
|
||||
var shortcuts = GetShortcuts().ToHashSet();
|
||||
string title = GetEffectiveTitle();
|
||||
string authors = GetEffectiveAuthors();
|
||||
string stub = GetPortableStubFileName();
|
||||
string conclusionMessage = FormatXmlMessage(RenderMarkdownAsPlainText(GetFileContent(Options.InstConclusion)));
|
||||
string license = GetLicenseRtfFile();
|
||||
bool hasLicense = !string.IsNullOrWhiteSpace(license);
|
||||
bool showLocationDialog = Options.InstLocation == InstallLocation.Either;
|
||||
string bannerImage = string.IsNullOrWhiteSpace(Options.MsiBanner) ? HelperFile.WixAssetsTopBanner : Options.MsiBanner;
|
||||
string dialogImage = string.IsNullOrWhiteSpace(Options.MsiLogo) ? HelperFile.WixAssetsDialogBackground : Options.MsiLogo;
|
||||
|
||||
string wixPackage = $$"""
|
||||
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:ui="http://wixtoolset.org/schemas/v4/wxs/ui">
|
||||
<Package Name="{{title}}"
|
||||
Manufacturer="{{authors}}"
|
||||
Version="{{msiVersion}}"
|
||||
Codepage="{{culture}}"
|
||||
Language="1033"
|
||||
Scope="perUserOrMachine"
|
||||
UpgradeCode="{{GuidUtil.CreateGuidFromHash($"{Options.PackId}:UpgradeCode")}}"
|
||||
>
|
||||
<Media Id="1" Cabinet="app.cab" EmbedCab="yes" />
|
||||
<StandardDirectory Id="TARGETDIR">
|
||||
<Directory Id="INSTALLFOLDER" Name="{{SanitizeDirectoryString(title)}}" ComponentGuidGenerationSeed="{{GuidUtil.CreateGuidFromHash($"{Options.PackId}:INSTALLFOLDER")}}">
|
||||
<Directory Name="current" />
|
||||
<Directory Id="PACKAGES_DIR" Name="packages" />
|
||||
</Directory>
|
||||
</StandardDirectory>
|
||||
{{(shortcuts.Contains(ShortcutLocation.Desktop) ? $"""
|
||||
<StandardDirectory Id="DesktopFolder">
|
||||
<Component Id="ApplicationDesktopShortcut">
|
||||
<Shortcut Id="ApplicationDesktopShortcut"
|
||||
Name="{title}"
|
||||
Description="Desktop shortcut for {title}"
|
||||
Target="[INSTALLFOLDER]{stub}"
|
||||
WorkingDirectory="INSTALLFOLDER"/>
|
||||
<RemoveFolder Id="CleanUpDesktopShortcut" Directory="INSTALLFOLDER" On="uninstall"/>
|
||||
<RegistryValue Root="HKCU" Key="Software\{SanitizeDirectoryString(authors)}\{Options.PackId}.DesktopShortcut" Name="installed" Type="integer" Value="1" KeyPath="yes"/>
|
||||
</Component>
|
||||
</StandardDirectory>
|
||||
""" : "")}}
|
||||
{{(shortcuts.Contains(ShortcutLocation.StartMenu) ? $"""
|
||||
<StandardDirectory Id="StartMenuFolder">
|
||||
<Component Id="ApplicationStartMenuShortcut">
|
||||
<Shortcut Id="ApplicationStartMenuShortcut"
|
||||
Name="{title}"
|
||||
Description="Start Menu shortcut for {title}"
|
||||
Target="[INSTALLFOLDER]{stub}"
|
||||
WorkingDirectory="INSTALLFOLDER"/>
|
||||
<RemoveFolder Id="CleanUpStartMenuShortcut" Directory="INSTALLFOLDER" On="uninstall"/>
|
||||
<RegistryValue Root="HKCU" Key="Software\{SanitizeDirectoryString(authors)}\{Options.PackId}.StartMenuShortcut" Name="installed" Type="integer" Value="1" KeyPath="yes"/>
|
||||
</Component>
|
||||
</StandardDirectory>
|
||||
""" : "")}}
|
||||
|
||||
{{(!string.IsNullOrWhiteSpace(Options.Icon) ? $"""
|
||||
<Icon Id="appicon" SourceFile="{Options.Icon}"/>
|
||||
<Property Id="ARPPRODUCTICON" Value="appicon" />
|
||||
""" : "")}}
|
||||
|
||||
{{(hasLicense ? $"""
|
||||
<WixVariable
|
||||
Id="WixUILicenseRtf"
|
||||
Value="{license}"
|
||||
/>
|
||||
""" : "")}}
|
||||
|
||||
<WixVariable
|
||||
Id="WixUIBannerBmp"
|
||||
Value="{{bannerImage}}"
|
||||
/>
|
||||
|
||||
<WixVariable
|
||||
Id="WixUIDialogBmp"
|
||||
Value="{{dialogImage}}"
|
||||
/>
|
||||
|
||||
<!-- Message on last screen after install -->
|
||||
{{(!string.IsNullOrWhiteSpace(conclusionMessage) ? $"""
|
||||
<Property Id="WIXUI_EXITDIALOGOPTIONALTEXT" Value="{conclusionMessage}" />
|
||||
""" : "")}}
|
||||
|
||||
<!-- Default checked state of launch app check box to true -->
|
||||
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOX" Value="1" />
|
||||
|
||||
|
||||
<!-- Check box for launching -->
|
||||
<Property
|
||||
Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT"
|
||||
Value="Launch {{title}}"
|
||||
/>
|
||||
|
||||
<Property Id="WixAppFolder" Value="WixPerMachineFolder" />
|
||||
<Property Id="ApplicationFolderName" Value="{{SanitizeDirectoryString(Options.PackId)}}" />
|
||||
{{(Options.InstLocation == InstallLocation.Either ? """
|
||||
<WixVariable Id="override WixUISupportPerUser" Value="1" />
|
||||
<WixVariable Id="override WixUISupportPerMachine" Value="1" />
|
||||
""" : "")}}
|
||||
|
||||
<UI>
|
||||
<ui:WixUI
|
||||
Id="WixUI_Velopack"
|
||||
InstallDirectory="INSTALLFOLDER"
|
||||
/>
|
||||
|
||||
<Publish Dialog="ExitDialog"
|
||||
Control="Finish"
|
||||
Event="DoAction"
|
||||
Value="LaunchApplication"
|
||||
Condition="WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed" />
|
||||
</UI>
|
||||
|
||||
<Files Include="{{portableDirectory.FullName}}\**" />
|
||||
|
||||
<CustomAction Id="RemoveAppDirectory" Directory="INSTALLFOLDER" Impersonate="no" ExeCommand="cmd.exe /C rmdir /S /Q "[INSTALLFOLDER]"" Execute="deferred" Return="ignore" />
|
||||
<CustomAction Id="RemoveTempDirectory" Directory="TempFolder" Impersonate="yes" ExeCommand="cmd.exe /C rmdir /S /Q "%TEMP%\velopack_{{Options.PackId}}"" Execute="deferred" Return="ignore" />
|
||||
<CustomAction Id="LaunchApplication" Directory="INSTALLFOLDER" Impersonate="yes" ExeCommand=""[INSTALLFOLDER]{{stub}}"" Execute="immediate" Return="ignore" />
|
||||
|
||||
<InstallExecuteSequence>
|
||||
<Custom Action="RemoveAppDirectory" Before="RemoveFolders" Condition="(REMOVE="ALL") AND (NOT UPGRADINGPRODUCTCODE)" />
|
||||
<Custom Action="RemoveTempDirectory" Before="InstallFinalize" Condition="(REMOVE="ALL") AND (NOT UPGRADINGPRODUCTCODE)" />
|
||||
</InstallExecuteSequence>
|
||||
</Package>
|
||||
|
||||
<!-- https://github.com/wixtoolset/wix/blob/v5.0.2/src/ext/UI/wixlib/WixUI_Advanced.wxs -->
|
||||
<?foreach WIXUIARCH in X86;X64;A64 ?>
|
||||
<Fragment>
|
||||
<UI Id="WixUI_Velopack_$(WIXUIARCH)">
|
||||
{{(hasLicense ? $"""
|
||||
<Publish Dialog="LicenseAgreementDlg" Control="Print" Event="DoAction" Value="WixUIPrintEula_$(WIXUIARCH)" />
|
||||
""" : "")}}
|
||||
<Publish Dialog="BrowseDlg" Control="OK" Event="DoAction" Value="WixUIValidatePath_$(WIXUIARCH)" Order="3" Condition="NOT WIXUI_DONTVALIDATEPATH" />
|
||||
{{(showLocationDialog ? """
|
||||
<Publish Dialog="InstallScopeDlg" Control="Next" Event="DoAction" Value="WixUIValidatePath_$(WIXUIARCH)" Order="7" Condition="NOT WIXUI_DONTVALIDATEPATH" />
|
||||
""" : "")}}
|
||||
</UI>
|
||||
|
||||
<UIRef Id="WixUI_Velopack" />
|
||||
</Fragment>
|
||||
<?endforeach?>
|
||||
|
||||
<Fragment>
|
||||
<PropertyRef Id="ApplicationFolderName" />
|
||||
|
||||
<UI Id="file WixUI_Velopack">
|
||||
<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" />
|
||||
|
||||
<DialogRef Id="BrowseDlg" />
|
||||
<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="BrowseDlg" Control="OK" Event="SpawnDialog" Value="InvalidDirDlg" Order="4" Condition="NOT WIXUI_DONTVALIDATEPATH AND WIXUI_INSTALLDIR_VALID<>"1"" />
|
||||
|
||||
<Publish Dialog="ExitDialog" Control="Finish" Event="EndDialog" Value="Return" Order="999" />
|
||||
|
||||
{{(hasLicense ? """
|
||||
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="LicenseAgreementDlg" Condition="NOT Installed" />
|
||||
""" : showLocationDialog ?
|
||||
"""
|
||||
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="InstallScopeDlg" Condition="NOT Installed" />
|
||||
""" : """
|
||||
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg" Condition="NOT Installed" />
|
||||
"""
|
||||
)}}
|
||||
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg" Condition="Installed AND PATCH" />
|
||||
|
||||
{{(hasLicense ? $"""
|
||||
<Publish Dialog="LicenseAgreementDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" />
|
||||
<Publish Dialog="LicenseAgreementDlg" Control="Next" Event="NewDialog" Value="{(showLocationDialog ? "InstallScopeDlg" : "VerifyReadyDlg")}" Condition="LicenseAccepted = "1"" />
|
||||
""" : "")}}
|
||||
|
||||
{{Options.InstLocation switch {
|
||||
InstallLocation.Either => $$"""
|
||||
<Publish Dialog="InstallScopeDlg" Control="Back" Event="NewDialog" Value="{{(hasLicense ? "LicenseAgreementDlg" : "WelcomeDlg")}}" />
|
||||
<Publish Dialog="InstallScopeDlg" Control="Next" Property="WixAppFolder" Value="WixPerUserFolder" Order="1" Condition="!(wix.WixUISupportPerUser) AND NOT Privileged" />
|
||||
<Publish Dialog="InstallScopeDlg" Control="Next" Property="ALLUSERS" Value="{}" Order="2" Condition="WixAppFolder = "WixPerUserFolder"" />
|
||||
<Publish Dialog="InstallScopeDlg" Control="Next" Property="ALLUSERS" Value="1" Order="3" Condition="WixAppFolder = "WixPerMachineFolder"" />
|
||||
<Publish Dialog="InstallScopeDlg" Control="Next" Property="INSTALLFOLDER" Value="[LocalAppDataFolder][ApplicationFolderName]" Order=" 4" Condition="WixAppFolder = "WixPerUserFolder"" />
|
||||
<Publish Dialog="InstallScopeDlg" Control="Next" Property="INSTALLFOLDER" Value="[{{(packageAs64Bit ? "ProgramFiles64Folder" : "ProgramFilesFolder")}}][ApplicationFolderName]" Order="5" Condition="WixAppFolder = "WixPerMachineFolder"" />
|
||||
<Publish Dialog="InstallScopeDlg" Control="Next" Event="SetTargetPath" Value="INSTALLFOLDER" Order="6" />
|
||||
<Publish Dialog="InstallScopeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg" Order="7" />
|
||||
<Publish Dialog="InstallScopeDlg" Control="Next" Event="DoAction" Value="FindRelatedProducts" Order="8" />
|
||||
""",
|
||||
InstallLocation.PerUser => """
|
||||
<Publish Dialog="WelcomeDlg" Control="Next" Property="ALLUSERS" Value="{}" Order="1" Condition="WixAppFolder = "WixPerUserFolder"" />
|
||||
<Publish Dialog="WelcomeDlg" Control="Next" Property="INSTALLFOLDER" Value="[LocalAppDataFolder][ApplicationFolderName]" Order=" 2" />
|
||||
<Publish Dialog="WelcomeDlg" Control="Next" Event="SetTargetPath" Value="INSTALLFOLDER" Order="3" />
|
||||
""",
|
||||
InstallLocation.PerMachine => $$"""
|
||||
<Publish Dialog="WelcomeDlg" Control="Next" Property="ALLUSERS" Value="1" Order="1" Condition="WixAppFolder = "WixPerMachineFolder"" />
|
||||
<Publish Dialog="WelcomeDlg" Control="Next" Property="INSTALLFOLDER" Value="[{{(packageAs64Bit ? "ProgramFiles64Folder" : "ProgramFilesFolder")}}][ApplicationFolderName]" Order="5" Condition="WixAppFolder = "WixPerMachineFolder"" />
|
||||
<Publish Dialog="WelcomeDlg" Control="Next" Event="SetTargetPath" Value="INSTALLFOLDER" Order="3" />
|
||||
""",
|
||||
_ => ""
|
||||
}}}
|
||||
|
||||
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="{{(showLocationDialog ? "InstallScopeDlg" : hasLicense ? "LicenseAgreementDlg" : "WelcomeDlg")}}" Order="1" Condition="NOT Installed" />
|
||||
|
||||
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="MaintenanceTypeDlg" Order="2" Condition="Installed AND NOT PATCH" />
|
||||
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" Order="2" Condition="Installed AND PATCH" />
|
||||
|
||||
<Publish Dialog="MaintenanceWelcomeDlg" Control="Next" Event="NewDialog" Value="MaintenanceTypeDlg" />
|
||||
|
||||
<Publish Dialog="MaintenanceTypeDlg" Control="RepairButton" Event="NewDialog" Value="VerifyReadyDlg" />
|
||||
<Publish Dialog="MaintenanceTypeDlg" Control="RemoveButton" Event="NewDialog" Value="VerifyReadyDlg" />
|
||||
<Publish Dialog="MaintenanceTypeDlg" Control="Back" Event="NewDialog" Value="MaintenanceWelcomeDlg" />
|
||||
|
||||
<Property Id="ARPNOMODIFY" Value="1" />
|
||||
</UI>
|
||||
|
||||
<UIRef Id="WixUI_Common" />
|
||||
</Fragment>
|
||||
</Wix>
|
||||
""";
|
||||
|
||||
string welcomeMessage = FormatXmlMessage(RenderMarkdownAsPlainText(GetFileContent(Options.InstWelcome)));
|
||||
string readmeMessage = FormatXmlMessage(RenderMarkdownAsPlainText(GetFileContent(Options.InstReadme)));
|
||||
|
||||
string localizedStrings = $"""
|
||||
<WixLocalization Culture="en-US" Codepage="1252" xmlns="http://wixtoolset.org/schemas/v4/wxl">
|
||||
{(!string.IsNullOrWhiteSpace(welcomeMessage) ? $"""
|
||||
<!-- Message on first welcome dialog; covers both initial install and update -->
|
||||
<String
|
||||
Id="WelcomeDlgDescription"
|
||||
Value="{welcomeMessage}"
|
||||
/>
|
||||
<String
|
||||
Id="WelcomeUpdateDlgDescriptionUpdate"
|
||||
Value="{welcomeMessage}"
|
||||
/>
|
||||
""" : "")}
|
||||
|
||||
{(!string.IsNullOrWhiteSpace(readmeMessage) ? $"""
|
||||
<!-- Message on the completion dialog (last screen after install) -->
|
||||
<String
|
||||
Id="VerifyReadyDlgInstallText"
|
||||
Value="{readmeMessage}"
|
||||
/>
|
||||
""" : "")}
|
||||
</WixLocalization>
|
||||
""";
|
||||
|
||||
|
||||
var wxs = Path.Combine(outputDirectory.FullName, wixId + ".wxs");
|
||||
var localization = Path.Combine(outputDirectory.FullName, wixId + "_en-US.wxs");
|
||||
try {
|
||||
File.WriteAllText(wxs, wixPackage, Encoding.UTF8);
|
||||
File.WriteAllText(localization, localizedStrings, Encoding.UTF8);
|
||||
|
||||
progress(30);
|
||||
|
||||
Log.Info("Compiling WiX Template");
|
||||
|
||||
List<string> wixExtensions = [HelperFile.WixUiExtPath];
|
||||
|
||||
//When localization is supported in Velopack, we will need to add -culture here:
|
||||
//https://docs.firegiant.com/wix/tools/wixext/wixui/
|
||||
var buildCommand = $"\"{HelperFile.WixPath}\" build -platform {(packageAs64Bit ? "x64" : "x86")} -outputType Package -pdbType none {string.Join(" ", wixExtensions.Select(x => $"-ext \"{x}\""))} -loc \"{localization}\" -out \"{msiFilePath}\" \"{wxs}\"";
|
||||
|
||||
_ = Exe.RunHostedCommand(buildCommand);
|
||||
|
||||
progress(90);
|
||||
|
||||
} finally {
|
||||
IoUtil.DeleteFileOrDirectoryHard(wxs, throwOnFailure: false);
|
||||
IoUtil.DeleteFileOrDirectoryHard(localization, throwOnFailure: false);
|
||||
if (licenseFile is not null) {
|
||||
IoUtil.DeleteFileOrDirectoryHard(licenseFile, throwOnFailure: false);
|
||||
}
|
||||
}
|
||||
progress(100);
|
||||
var licenseRtfPath = GetLicenseRtfFile();
|
||||
var templateData = MsiBuilder.ConvertOptionsToTemplateData(portableDirectory, GetShortcuts(), licenseRtfPath, GetRuntimeDependencies(), Options);
|
||||
MsiBuilder.CompileWixMsi(Log, templateData, progress, msiFilePath);
|
||||
}
|
||||
|
||||
protected override string[] GetMainExeSearchPaths(string packDirectory, string mainExeName)
|
||||
@@ -722,14 +364,24 @@ public class WindowsPackCommandRunner : PackageBuilder<WindowsPackOptions>
|
||||
|
||||
private string GetPortableStubFileName() => (Options.PackTitle ?? Options.PackId) + ".exe";
|
||||
|
||||
private IReadOnlyList<ShortcutLocation> GetShortcuts() => [.. Options.Shortcuts.Split([',', ';'], StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(x => x.Trim())
|
||||
.Select(x => {
|
||||
if (Enum.TryParse(x, true, out ShortcutLocation location)) {
|
||||
return location;
|
||||
}
|
||||
return ShortcutLocation.None;
|
||||
})
|
||||
.Where(x => x != ShortcutLocation.None)
|
||||
];
|
||||
private ShortcutLocation GetShortcuts()
|
||||
{
|
||||
var items = Options.Shortcuts
|
||||
.Split([',', ';'], StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(x => x.Trim());
|
||||
|
||||
ShortcutLocation result = ShortcutLocation.None;
|
||||
|
||||
foreach (var item in items) {
|
||||
if (Enum.TryParse<ShortcutLocation>(item, true, out var loc)) {
|
||||
result |= loc;
|
||||
} else {
|
||||
throw new UserInfoException(
|
||||
$"Invalid shortcut locations '{Options.Shortcuts}'. " +
|
||||
$"Valid values for comma delimited list are: {string.Join(", ", Enum.GetNames(typeof(ShortcutLocation)))}.");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
128
src/vpk/Velopack.Packaging.Windows/Msi/MsiBuilder.cs
Normal file
128
src/vpk/Velopack.Packaging.Windows/Msi/MsiBuilder.cs
Normal file
@@ -0,0 +1,128 @@
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using HandlebarsDotNet;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NuGet.Versioning;
|
||||
using Velopack.Core;
|
||||
using Velopack.Packaging.Windows.Commands;
|
||||
using Velopack.Util;
|
||||
using Velopack.Windows;
|
||||
|
||||
namespace Velopack.Packaging.Windows.Msi;
|
||||
|
||||
public static class MsiBuilder
|
||||
{
|
||||
public static (string mainTemplate, string enLocale) GenerateWixTemplate(MsiTemplateData data)
|
||||
{
|
||||
if (data == null)
|
||||
throw new ArgumentNullException(nameof(data));
|
||||
|
||||
var templateContent = GetResourceContent("MsiTemplate.hbs");
|
||||
var localeContent = GetResourceContent("MsiLocale_en_US.hbs");
|
||||
|
||||
var template = Handlebars.Compile(templateContent);
|
||||
var locale = Handlebars.Compile(localeContent);
|
||||
|
||||
return (template(data), locale(data));
|
||||
}
|
||||
|
||||
public static MsiTemplateData ConvertOptionsToTemplateData(DirectoryInfo portableDir, ShortcutLocation shortcuts, string licenseRtfPath, string runtimeDeps,
|
||||
WindowsPackOptions options)
|
||||
{
|
||||
// WiX Identifiers may contain ASCII characters A-Z, a-z, digits, underscores (_), or
|
||||
// periods(.). Every identifier must begin with either a letter or an underscore.
|
||||
var wixId = Regex.Replace(options.PackId, @"[^\w\.]", "_");
|
||||
if (char.GetUnicodeCategory(wixId[0]) == UnicodeCategory.DecimalDigitNumber)
|
||||
wixId = "_" + wixId;
|
||||
|
||||
var msiVersion = options.MsiVersionOverride;
|
||||
if (string.IsNullOrWhiteSpace(msiVersion)) {
|
||||
var parsedVersion = SemanticVersion.Parse(options.PackVersion);
|
||||
msiVersion = $"{parsedVersion.Major}.{parsedVersion.Minor}.{parsedVersion.Patch}.0";
|
||||
}
|
||||
|
||||
string welcomeMessage = MsiUtil.FormatXmlMessage(MsiUtil.RenderMarkdownAsPlainText(MsiUtil.GetFileContent(options.InstWelcome)));
|
||||
string readmeMessage = MsiUtil.FormatXmlMessage(MsiUtil.RenderMarkdownAsPlainText(MsiUtil.GetFileContent(options.InstReadme)));
|
||||
string conclusionMessage = MsiUtil.FormatXmlMessage(MsiUtil.RenderMarkdownAsPlainText(MsiUtil.GetFileContent(options.InstConclusion)));
|
||||
|
||||
return new MsiTemplateData() {
|
||||
WixId = wixId,
|
||||
AppId = options.PackId,
|
||||
AppPublisher = options.PackAuthors ?? options.PackId,
|
||||
AppTitle = options.PackTitle ?? options.PackId,
|
||||
AppMsiVersion = msiVersion,
|
||||
SourceDirectoryPath = portableDir.FullName,
|
||||
Is64Bit = options.TargetRuntime.Architecture is not RuntimeCpu.x86,
|
||||
CultureLCID = CultureInfo.GetCultureInfo("en-US").TextInfo.ANSICodePage,
|
||||
InstallForAllUsers = options.InstLocation.HasFlag(InstallLocation.PerMachine),
|
||||
InstallForCurrentUser = options.InstLocation.HasFlag(InstallLocation.PerUser),
|
||||
UpgradeCodeGuid = GuidUtil.CreateGuidFromHash($"{options.PackId}:UpgradeCode").ToString(),
|
||||
ComponentGenerationSeedGuid = GuidUtil.CreateGuidFromHash($"{options.PackId}:INSTALLFOLDER").ToString(),
|
||||
IconPath = options.Icon,
|
||||
StubFileName = (options.PackTitle ?? options.PackId) + ".exe",
|
||||
DesktopShortcut = shortcuts.HasFlag(ShortcutLocation.Desktop),
|
||||
StartMenuShortcut = shortcuts.HasFlag(ShortcutLocation.StartMenu),
|
||||
RustNativeModulePath = HelperFile.GetWixNativeModulePath(options.TargetRuntime),
|
||||
SideBannerImagePath = options.MsiBanner ?? HelperFile.WixAssetsDialogBackground,
|
||||
TopBannerImagePath = options.MsiLogo ?? HelperFile.WixAssetsTopBanner,
|
||||
RuntimeDependencies = runtimeDeps,
|
||||
ConclusionMessage = conclusionMessage,
|
||||
ReadmeMessage = readmeMessage,
|
||||
WelcomeMessage = welcomeMessage,
|
||||
LicenseRtfFilePath = licenseRtfPath,
|
||||
};
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
public static void CompileWixMsi(ILogger Log, MsiTemplateData data, Action<int> progress, string outputFilePath)
|
||||
{
|
||||
bool as64Bit = data.Is64Bit;
|
||||
Log.Info($"Configuring WiX in {(as64Bit ? "64-bit" : "32-bit")} mode");
|
||||
|
||||
var _1 = TempUtil.GetTempDirectory(out var outputDir);
|
||||
var wixId = data.WixId;
|
||||
var wxsPath = Path.Combine(outputDir, wixId + ".wxs");
|
||||
var localizationPath = Path.Combine(outputDir, wixId + "_en-US.wxs");
|
||||
|
||||
var (wxsContent, localizationContent) = GenerateWixTemplate(data);
|
||||
|
||||
File.WriteAllText(wxsPath, wxsContent, Encoding.UTF8);
|
||||
File.WriteAllText(localizationPath, localizationContent, Encoding.UTF8);
|
||||
|
||||
progress(30);
|
||||
|
||||
Log.Info("Compiling WiX Template");
|
||||
|
||||
List<string> wixExtensions = [HelperFile.WixUiExtPath];
|
||||
|
||||
//When localization is supported in Velopack, we will need to add -culture here:
|
||||
//https://docs.firegiant.com/wix/tools/wixext/wixui/
|
||||
var buildCommand =
|
||||
$"\"{HelperFile.WixPath}\" build -platform {(as64Bit ? "x64" : "x86")} -outputType Package " +
|
||||
$"-pdbType none {string.Join(" ", wixExtensions.Select(x => $"-ext \"{x}\""))} -loc \"{localizationPath}\" -out \"{outputFilePath}\" \"{wxsPath}\"";
|
||||
|
||||
_ = Exe.RunHostedCommand(buildCommand);
|
||||
|
||||
progress(100);
|
||||
}
|
||||
|
||||
private static string GetResourceContent(string resourceName)
|
||||
{
|
||||
var assy = Assembly.GetExecutingAssembly();
|
||||
|
||||
string[] manifestResourceNames = assy.GetManifestResourceNames();
|
||||
string resourceNameFull = manifestResourceNames.SingleOrDefault(name => name.EndsWith(resourceName));
|
||||
if (string.IsNullOrEmpty(resourceNameFull))
|
||||
throw new InvalidOperationException($"Resource '{resourceName}' not found in assembly. Available resources: {string.Join(", ", manifestResourceNames)}");
|
||||
|
||||
using var stream = assy.GetManifestResourceStream(resourceNameFull);
|
||||
if (stream == null)
|
||||
throw new InvalidOperationException($"Resource '{resourceName}' not found in assembly.");
|
||||
|
||||
using var reader = new StreamReader(stream);
|
||||
return reader.ReadToEnd();
|
||||
}
|
||||
}
|
||||
60
src/vpk/Velopack.Packaging.Windows/Msi/MsiTemplateData.cs
Normal file
60
src/vpk/Velopack.Packaging.Windows/Msi/MsiTemplateData.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
namespace Velopack.Packaging.Windows.Msi;
|
||||
|
||||
public class MsiTemplateData
|
||||
{
|
||||
public string WixId;
|
||||
public string SourceDirectoryPath;
|
||||
public string RustNativeModulePath;
|
||||
public bool Is64Bit;
|
||||
public int CultureLCID;
|
||||
public string UpgradeCodeGuid;
|
||||
public string ComponentGenerationSeedGuid;
|
||||
|
||||
public string ProgramFilesFolderName => Is64Bit
|
||||
? "[ProgramFiles64Folder]"
|
||||
: "[ProgramFilesFolder]";
|
||||
|
||||
public string AppId;
|
||||
public string AppTitle;
|
||||
public string AppTitleSanitized => MsiUtil.SanitizeDirectoryString(AppTitle);
|
||||
public string AppPublisher;
|
||||
public string AppPublisherSanitized => MsiUtil.SanitizeDirectoryString(AppPublisher);
|
||||
public string AppMsiVersion;
|
||||
|
||||
public string StubFileName;
|
||||
public string RuntimeDependencies;
|
||||
public bool DesktopShortcut;
|
||||
public bool StartMenuShortcut;
|
||||
|
||||
public bool InstallLocationEither => InstallForAllUsers && InstallForCurrentUser;
|
||||
public bool InstallLocationAllUsersOnly => InstallForAllUsers && !InstallForCurrentUser;
|
||||
public bool InstallLocationCurrentUserOnly => !InstallForAllUsers && InstallForCurrentUser;
|
||||
public bool InstallForAllUsers;
|
||||
public bool InstallForCurrentUser;
|
||||
|
||||
public bool HasIcon => !string.IsNullOrWhiteSpace(IconPath) && File.Exists(IconPath);
|
||||
public string IconPath;
|
||||
|
||||
public bool HasLicense => !string.IsNullOrWhiteSpace(LicenseRtfFilePath);
|
||||
public string LicenseRtfFilePath;
|
||||
|
||||
public bool HasConclusionMessage => !string.IsNullOrWhiteSpace(ConclusionMessage);
|
||||
public string ConclusionMessage;
|
||||
|
||||
public bool HasWelcomeMessage => !string.IsNullOrWhiteSpace(WelcomeMessage);
|
||||
public string WelcomeMessage;
|
||||
|
||||
public bool HasReadmeMessage => !string.IsNullOrWhiteSpace(ReadmeMessage);
|
||||
public string ReadmeMessage;
|
||||
|
||||
public bool HasTopBannerImage => !string.IsNullOrWhiteSpace(TopBannerImagePath) && File.Exists(TopBannerImagePath);
|
||||
public string TopBannerImagePath;
|
||||
|
||||
public bool HasSideBannerImage => !string.IsNullOrWhiteSpace(SideBannerImagePath) && File.Exists(SideBannerImagePath);
|
||||
public string SideBannerImagePath;
|
||||
|
||||
public string WelcomeNextPage => HasLicense ? "LicenseAgreementDlg" : LicenseNextPage;
|
||||
public string LicenseNextPage => InstallLocationEither ? "InstallScopeDlg" : "VerifyReadyDlg";
|
||||
public string InstallScopePrevPage => HasLicense ? "LicenseAgreementDlg" : "WelcomeDlg";
|
||||
public string VerifyReadyPrevPage => InstallLocationEither ? "InstallScopeDlg" : InstallScopePrevPage;
|
||||
}
|
||||
56
src/vpk/Velopack.Packaging.Windows/Msi/MsiUtil.cs
Normal file
56
src/vpk/Velopack.Packaging.Windows/Msi/MsiUtil.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using Markdig;
|
||||
using MarkdigExtensions.RtfRenderer;
|
||||
|
||||
namespace Velopack.Packaging.Windows.Msi;
|
||||
|
||||
public static class MsiUtil
|
||||
{
|
||||
public static string SanitizeDirectoryString(string name)
|
||||
=> string.Join("_", name.Split(Path.GetInvalidPathChars()));
|
||||
|
||||
public static string FormatXmlMessage(string message)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(message))
|
||||
return "";
|
||||
|
||||
StringBuilder sb = new();
|
||||
XmlWriterSettings settings = new() {
|
||||
ConformanceLevel = ConformanceLevel.Fragment,
|
||||
NewLineHandling = NewLineHandling.None,
|
||||
};
|
||||
using XmlWriter writer = XmlWriter.Create(sb, settings);
|
||||
writer.WriteString(message);
|
||||
writer.Flush();
|
||||
var rv = sb.ToString();
|
||||
rv = rv.Replace("\r", " ").Replace("\n", " ");
|
||||
return rv;
|
||||
}
|
||||
|
||||
public static string GetFileContent(string filePath)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(filePath))
|
||||
return "";
|
||||
string fileContents = File.ReadAllText(filePath, Encoding.UTF8);
|
||||
return fileContents;
|
||||
}
|
||||
|
||||
public static string RenderMarkdownAsPlainText(string markdown)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(markdown))
|
||||
return "";
|
||||
return Markdown.ToPlainText(markdown);
|
||||
}
|
||||
|
||||
public static string RenderMarkdownAsRtf(string markdown)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
using var writer = new StringWriter(builder);
|
||||
var renderer = new RtfRenderer(writer);
|
||||
renderer.StartDocument();
|
||||
_ = Markdown.Convert(markdown, renderer);
|
||||
renderer.CloseDocument();
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<!--suppress CheckEmptyScriptTag -->
|
||||
<WixLocalization Culture="en-US" Codepage="1252" xmlns="http://wixtoolset.org/schemas/v4/wxl">
|
||||
<String Id="InstallPrerequisitesDlgInstallTitle"
|
||||
Value="Missing System Requirements"/>
|
||||
<String Id="InstallPrerequisitesDlgInstallText"
|
||||
Value="There are several prerequisites which need to be installed before setup can continue: "/>
|
||||
<String Id="InstallPrerequisitesDlgContinue"
|
||||
Value="Continue"/>
|
||||
|
||||
<!-- Message on first welcome dialog; covers both initial install and update -->
|
||||
{{#if HasWelcomeMessage}}
|
||||
<String Id="WelcomeDlgDescription"
|
||||
Value="{welcomeMessage}"/>
|
||||
<String Id="WelcomeUpdateDlgDescriptionUpdate"
|
||||
Value="{welcomeMessage}"/>
|
||||
{{/if}}
|
||||
|
||||
<!-- Message on the completion dialog (last screen after install) -->
|
||||
{{#if HasReadmeMessage}}
|
||||
<String Id="VerifyReadyDlgInstallText"
|
||||
Value="{readmeMessage}"/>
|
||||
{{/if}}
|
||||
</WixLocalization>
|
||||
285
src/vpk/Velopack.Packaging.Windows/Msi/Templates/MsiTemplate.hbs
Normal file
285
src/vpk/Velopack.Packaging.Windows/Msi/Templates/MsiTemplate.hbs
Normal file
@@ -0,0 +1,285 @@
|
||||
<!--suppress CheckEmptyScriptTag -->
|
||||
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:ui="http://wixtoolset.org/schemas/v4/wxs/ui">
|
||||
<Package Name="{{AppTitle}}"
|
||||
Manufacturer="{{AppPublisher}}"
|
||||
Version="{{AppMsiVersion}}"
|
||||
Codepage="{{CultureLCID}}"
|
||||
Language="1033"
|
||||
Scope="perUserOrMachine"
|
||||
UpgradeCode="{{UpgradeCodeGuid}}">
|
||||
|
||||
<Media Id="1" Cabinet="app.cab" EmbedCab="yes"/>
|
||||
<StandardDirectory Id="TARGETDIR">
|
||||
<Directory Id="INSTALLFOLDER" Name="{{AppTitleSanitized}}"
|
||||
ComponentGuidGenerationSeed="{{ComponentGenerationSeedGuid}}">
|
||||
<Directory Name="current"/>
|
||||
<Directory Id="PACKAGES_DIR" Name="packages"/>
|
||||
</Directory>
|
||||
</StandardDirectory>
|
||||
|
||||
{{#if DesktopShortcut}}
|
||||
<StandardDirectory Id="DesktopFolder">
|
||||
<Component Id="ApplicationDesktopShortcut">
|
||||
<Shortcut Id="ApplicationDesktopShortcut"
|
||||
Name="{{AppTitle}}"
|
||||
Description="Desktop shortcut for {{AppTitle}}"
|
||||
Target="[INSTALLFOLDER]{{StubFileName}}"
|
||||
WorkingDirectory="INSTALLFOLDER"/>
|
||||
<RemoveFolder Id="CleanUpDesktopShortcut" Directory="INSTALLFOLDER" On="uninstall"/>
|
||||
<RegistryValue Root="HKCU"
|
||||
Key="Software\{{AppPublisherSanitized}}\{{AppId}}.DesktopShortcut"
|
||||
Name="installed" Type="integer" Value="1" KeyPath="yes"/>
|
||||
</Component>
|
||||
</StandardDirectory>
|
||||
{{/if}}
|
||||
|
||||
{{#if StartMenuShortcut}}
|
||||
<StandardDirectory Id="StartMenuFolder">
|
||||
<Component Id="ApplicationStartMenuShortcut">
|
||||
<Shortcut Id="ApplicationStartMenuShortcut"
|
||||
Name="{{AppTitle}}"
|
||||
Description="Start Menu shortcut for {{AppTitle}}"
|
||||
Target="[INSTALLFOLDER]{{StubFileName}}"
|
||||
WorkingDirectory="INSTALLFOLDER"/>
|
||||
<RemoveFolder Id="CleanUpStartMenuShortcut" Directory="INSTALLFOLDER" On="uninstall"/>
|
||||
<RegistryValue Root="HKCU"
|
||||
Key="Software\{{AppPublisherSanitized}}\{{AppId}}.StartMenuShortcut"
|
||||
Name="installed" Type="integer" Value="1" KeyPath="yes"/>
|
||||
</Component>
|
||||
</StandardDirectory>
|
||||
{{/if}}
|
||||
|
||||
{{#if HasIcon}}
|
||||
<Icon Id="appicon" SourceFile="{{IconPath}}"/>
|
||||
<Property Id="ARPPRODUCTICON" Value="appicon"/>
|
||||
{{/if}}
|
||||
|
||||
{{#if HasLicense}}
|
||||
<WixVariable Id="WixUILicenseRtf" Value="{{LicenseRtfFilePath}}"/>
|
||||
{{/if}}
|
||||
|
||||
{{#if HasTopBannerImage}}
|
||||
<WixVariable Id="WixUIBannerBmp" Value="{{TopBannerImagePath}}"/>
|
||||
{{/if}}
|
||||
|
||||
{{#if HasSideBannerImage}}
|
||||
<WixVariable Id="WixUIDialogBmp" Value="{{SideBannerImagePath}}"/>
|
||||
{{/if}}
|
||||
|
||||
<!-- Message on last screen after install -->
|
||||
{{#if HasConclusionMessage}}
|
||||
<Property Id="WIXUI_EXITDIALOGOPTIONALTEXT" Value="{{ConclusionMessage}}"/>
|
||||
{{/if}}
|
||||
|
||||
<!-- Default checked state of launch app check box to true -->
|
||||
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOX" Value="1"/>
|
||||
|
||||
<!-- Check box for launching -->
|
||||
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT" Value="Launch {{AppTitle}}"/>
|
||||
|
||||
<Property Id="WixAppFolder" Value="WixPerMachineFolder"/>
|
||||
<Property Id="ApplicationFolderName" Value="{{AppId}}"/>
|
||||
|
||||
{{#if InstallLocationEither}}
|
||||
<WixVariable Id="override WixUISupportPerUser" Value="1"/>
|
||||
<WixVariable Id="override WixUISupportPerMachine" Value="1"/>
|
||||
{{/if}}
|
||||
|
||||
<UI>
|
||||
<ui:WixUI Id="WixUI_Velopack"
|
||||
InstallDirectory="INSTALLFOLDER"/>
|
||||
|
||||
<Publish Dialog="ExitDialog"
|
||||
Control="Finish"
|
||||
Event="DoAction"
|
||||
Value="LaunchApplication"
|
||||
Condition="WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed"/>
|
||||
</UI>
|
||||
|
||||
<Files Include="{{SourceDirectoryPath}}\**"/>
|
||||
|
||||
<CustomAction Id="RemoveAppDirectory" Directory="INSTALLFOLDER" Impersonate="no"
|
||||
ExeCommand="cmd.exe /C rmdir /S /Q "[INSTALLFOLDER]"" Execute="deferred"
|
||||
Return="ignore"/>
|
||||
<CustomAction Id="RemoveTempDirectory" Directory="TempFolder" Impersonate="yes"
|
||||
ExeCommand="cmd.exe /C rmdir /S /Q "%TEMP%\velopack_{{AppId}}""
|
||||
Execute="deferred" Return="ignore"/>
|
||||
<CustomAction Id="LaunchApplication" Directory="INSTALLFOLDER" Impersonate="yes"
|
||||
ExeCommand=""[INSTALLFOLDER]{{StubFileName}}"" Execute="immediate" Return="ignore"/>
|
||||
|
||||
<!-- Add our custom Rust module for custom actions -->
|
||||
<Binary Id="RustDll" SourceFile="{{RustNativeModulePath}}"/>
|
||||
<Property Id="RustRuntimeDependencies" Value="{{RuntimeDependencies}}"/>
|
||||
<CustomAction Id="RustBootstrap" BinaryRef="RustDll" DllEntry="Bootstrap" Execute="immediate" Return="check"/>
|
||||
<CustomAction Id="RustCheckMissing" BinaryRef="RustDll" DllEntry="CheckMissing" Execute="immediate"
|
||||
Return="check"/>
|
||||
|
||||
<InstallExecuteSequence>
|
||||
<Custom Action="RemoveAppDirectory" Before="RemoveFolders"
|
||||
Condition="(REMOVE="ALL") AND (NOT UPGRADINGPRODUCTCODE)"/>
|
||||
<Custom Action="RemoveTempDirectory" Before="InstallFinalize"
|
||||
Condition="(REMOVE="ALL") AND (NOT UPGRADINGPRODUCTCODE)"/>
|
||||
<Custom Action="RustBootstrap" Before="InstallInitialize" Condition="(REMOVE="")"/>
|
||||
</InstallExecuteSequence>
|
||||
</Package>
|
||||
|
||||
<!-- https://github.com/wixtoolset/wix/blob/v5.0.2/src/ext/UI/wixlib/WixUI_Advanced.wxs -->
|
||||
<?foreach WIXUIARCH in X86;X64;A64 ?>
|
||||
<Fragment>
|
||||
<UI Id="WixUI_Velopack_$(WIXUIARCH)">
|
||||
{{#if HasLicense}}
|
||||
<Publish Dialog="LicenseAgreementDlg" Control="Print" Event="DoAction"
|
||||
Value="WixUIPrintEula_$(WIXUIARCH)"/>
|
||||
{{/if}}
|
||||
|
||||
<Publish Dialog="BrowseDlg" Control="OK" Event="DoAction" Value="WixUIValidatePath_$(WIXUIARCH)" Order="3"
|
||||
Condition="NOT WIXUI_DONTVALIDATEPATH"/>
|
||||
|
||||
{{#if InstallLocationEither}}
|
||||
<Publish Dialog="InstallScopeDlg" Control="Next" Event="DoAction" Value="WixUIValidatePath_$(WIXUIARCH)"
|
||||
Order="7" Condition="NOT WIXUI_DONTVALIDATEPATH"/>
|
||||
{{/if}}
|
||||
</UI>
|
||||
|
||||
<UIRef Id="WixUI_Velopack"/>
|
||||
</Fragment>
|
||||
<?endforeach?>
|
||||
|
||||
<Fragment>
|
||||
<PropertyRef Id="ApplicationFolderName"/>
|
||||
|
||||
<UI Id="file WixUI_Velopack">
|
||||
<TextStyle Id="WixUI_Font_Normal" FaceName="Segoe UI" Size="8"/>
|
||||
<TextStyle Id="WixUI_Font_Bigger" FaceName="Segoe UI" Size="12"/>
|
||||
<TextStyle Id="WixUI_Font_Title" FaceName="Segoe UI" Size="9" Bold="yes"/>
|
||||
|
||||
<Property Id="DefaultUIFont" Value="WixUI_Font_Normal"/>
|
||||
|
||||
<Dialog Id="InstallPrerequisitesDlg" Width="370" Height="270" Title="!(loc.VerifyReadyDlg_Title)">
|
||||
|
||||
<Control Id="InstallTitle" Type="Text" X="15" Y="15" Width="300" Height="15" Transparent="yes"
|
||||
NoPrefix="yes" Text="{\WixUI_Font_Title}!(loc.InstallPrerequisitesDlgInstallTitle)"/>
|
||||
<Control Id="InstallText" Type="Text" X="25" Y="70" Width="320" Height="80"
|
||||
Text="!(loc.InstallPrerequisitesDlgInstallText) [MISSING_DEPENDENCIES]"/>
|
||||
<Control Id="InstallProgressBar" Type="ProgressBar" X="25" Y="180" Width="320" Height="16"
|
||||
Property="MISSING_DEPENDENCIES_PROGRESS"/>
|
||||
<!-- MISSING_DEPENDENCIES_STARTED, MISSING_DEPENDENCIES_COMPLETE, MISSING_DEPENDENCIES_PROGRESS, MISSING_DEPENDENCIES -->
|
||||
|
||||
<Control Id="Continue" Type="PushButton" ElevationShield="yes" X="212" Y="243" Width="80" Height="17"
|
||||
Hidden="yes" Disabled="yes" Text="!(loc.InstallPrerequisitesDlgContinue)"
|
||||
ShowCondition="MISSING_DEPENDENCIES_COMPLETE" EnableCondition="MISSING_DEPENDENCIES_COMPLETE">
|
||||
<Publish Event="EndDialog" Value="Return" Condition="OutOfDiskSpace <> 1"/>
|
||||
<Publish Event="SpawnDialog" Value="OutOfRbDiskDlg"
|
||||
Condition="OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND (PROMPTROLLBACKCOST="P" OR NOT PROMPTROLLBACKCOST)"/>
|
||||
<Publish Event="EndDialog" Value="Return"
|
||||
Condition="OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND PROMPTROLLBACKCOST="D""/>
|
||||
<Publish Event="EnableRollback" Value="False"
|
||||
Condition="OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND PROMPTROLLBACKCOST="D""/>
|
||||
<Publish Event="SpawnDialog" Value="OutOfDiskDlg"
|
||||
Condition="(OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 1) OR (OutOfDiskSpace = 1 AND PROMPTROLLBACKCOST="F")"/>
|
||||
</Control>
|
||||
|
||||
<Control Id="Install" Type="PushButton" ElevationShield="yes" X="212" Y="243" Width="80" Height="17"
|
||||
Default="yes" Hidden="yes" Disabled="yes" Text="!(loc.VerifyReadyDlgInstall)"
|
||||
ShowCondition="NOT MISSING_DEPENDENCIES_COMPLETE"
|
||||
EnableCondition="NOT MISSING_DEPENDENCIES_STARTED">
|
||||
|
||||
</Control>
|
||||
|
||||
<Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Cancel="yes"
|
||||
Text="!(loc.WixUICancel)">
|
||||
<Publish Event="SpawnDialog" Value="CancelDlg"/>
|
||||
</Control>
|
||||
<Control Id="Back" Type="PushButton" X="156" Y="243" Width="56" Height="17" Text="!(loc.WixUIBack)"/>
|
||||
<Control Id="BannerBitmap" Type="Bitmap" X="0" Y="0" Width="370" Height="44" TabSkip="no"
|
||||
Text="!(loc.VerifyReadyDlgBannerBitmap)"/>
|
||||
<Control Id="BannerLine" Type="Line" X="0" Y="44" Width="373" Height="0"/>
|
||||
<Control Id="BottomLine" Type="Line" X="0" Y="234" Width="373" Height="0"/>
|
||||
|
||||
</Dialog>
|
||||
|
||||
<DialogRef Id="BrowseDlg"/>
|
||||
<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="BrowseDlg" Control="OK" Event="SpawnDialog" Value="InvalidDirDlg" Order="4"
|
||||
Condition="NOT WIXUI_DONTVALIDATEPATH AND WIXUI_INSTALLDIR_VALID<>"1""/>
|
||||
|
||||
<Publish Dialog="ExitDialog" Control="Finish" Event="EndDialog" Value="Return" Order="999"/>
|
||||
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="{{WelcomeNextPage}}"
|
||||
Condition="NOT Installed"/>
|
||||
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg"
|
||||
Condition="Installed AND PATCH"/>
|
||||
|
||||
{{#if HasLicense}}
|
||||
<Publish Dialog="LicenseAgreementDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg"/>
|
||||
<Publish Dialog="LicenseAgreementDlg" Control="Next" Event="NewDialog" Value="{{LicenseNextPage}}"
|
||||
Condition="LicenseAccepted = "1""/>
|
||||
{{/if}}
|
||||
|
||||
{{#if InstallLocationEither}}
|
||||
<Publish Dialog="InstallScopeDlg" Control="Back" Event="NewDialog" Value="{{InstallScopePrevPage}}"/>
|
||||
<Publish Dialog="InstallScopeDlg" Control="Next" Property="WixAppFolder" Value="WixPerUserFolder"
|
||||
Order="1" Condition="!(wix.WixUISupportPerUser) AND NOT Privileged"/>
|
||||
<Publish Dialog="InstallScopeDlg" Control="Next" Property="ALLUSERS" Value="{}" Order="2"
|
||||
Condition="WixAppFolder = "WixPerUserFolder""/>
|
||||
<Publish Dialog="InstallScopeDlg" Control="Next" Property="ALLUSERS" Value="1" Order="3"
|
||||
Condition="WixAppFolder = "WixPerMachineFolder""/>
|
||||
<Publish Dialog="InstallScopeDlg" Control="Next" Property="INSTALLFOLDER"
|
||||
Value="[LocalAppDataFolder][ApplicationFolderName]" Order=" 4"
|
||||
Condition="WixAppFolder = "WixPerUserFolder""/>
|
||||
<Publish Dialog="InstallScopeDlg" Control="Next" Property="INSTALLFOLDER"
|
||||
Value="{{ProgramFilesFolderName}}[ApplicationFolderName]" Order="5"
|
||||
Condition="WixAppFolder = "WixPerMachineFolder""/>
|
||||
<Publish Dialog="InstallScopeDlg" Control="Next" Event="SetTargetPath" Value="INSTALLFOLDER" Order="6"/>
|
||||
<Publish Dialog="InstallScopeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg" Order="7"/>
|
||||
<Publish Dialog="InstallScopeDlg" Control="Next" Event="DoAction" Value="FindRelatedProducts"
|
||||
Order="8"/>
|
||||
{{/if}}
|
||||
|
||||
{{#if InstallLocationCurrentUserOnly}}
|
||||
<Publish Dialog="WelcomeDlg" Control="Next" Property="ALLUSERS" Value="{}" Order="1"
|
||||
Condition="WixAppFolder = "WixPerUserFolder""/>
|
||||
<Publish Dialog="WelcomeDlg" Control="Next" Property="INSTALLFOLDER"
|
||||
Value="[LocalAppDataFolder][ApplicationFolderName]" Order=" 2"/>
|
||||
<Publish Dialog="WelcomeDlg" Control="Next" Event="SetTargetPath" Value="INSTALLFOLDER" Order="3"/>
|
||||
{{/if}}
|
||||
|
||||
{{#if InstallLocationAllUsersOnly}}
|
||||
<Publish Dialog="WelcomeDlg" Control="Next" Property="ALLUSERS" Value="1" Order="1"
|
||||
Condition="WixAppFolder = "WixPerMachineFolder""/>
|
||||
<Publish Dialog="WelcomeDlg" Control="Next" Property="INSTALLFOLDER"
|
||||
Value="{{ProgramFilesFolderName}}[ApplicationFolderName]" Order="5"
|
||||
Condition="WixAppFolder = "WixPerMachineFolder""/>
|
||||
<Publish Dialog="WelcomeDlg" Control="Next" Event="SetTargetPath" Value="INSTALLFOLDER" Order="3"/>
|
||||
{{/if}}
|
||||
|
||||
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="{{VerifyReadyPrevPage}}" Order="1"
|
||||
Condition="NOT Installed"/>
|
||||
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="MaintenanceTypeDlg" Order="2"
|
||||
Condition="Installed AND NOT PATCH"/>
|
||||
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" Order="2"
|
||||
Condition="Installed AND PATCH"/>
|
||||
|
||||
<!-- <Publish Dialog="VerifyReadyDlg" Control="Install" Event="DoAction" Value="RustCheckMissing" Order="1"/>-->
|
||||
<!-- <Publish Dialog="VerifyReadyDlg" Control="Install" Event="NewDialog" Value="InstallPrerequisitesDlg"-->
|
||||
<!-- Order="1"/>-->
|
||||
|
||||
<Publish Dialog="MaintenanceWelcomeDlg" Control="Next" Event="NewDialog" Value="MaintenanceTypeDlg"/>
|
||||
|
||||
<Publish Dialog="MaintenanceTypeDlg" Control="RepairButton" Event="NewDialog" Value="VerifyReadyDlg"/>
|
||||
<Publish Dialog="MaintenanceTypeDlg" Control="RemoveButton" Event="NewDialog" Value="VerifyReadyDlg"/>
|
||||
<Publish Dialog="MaintenanceTypeDlg" Control="Back" Event="NewDialog" Value="MaintenanceWelcomeDlg"/>
|
||||
|
||||
<Property Id="ARPNOMODIFY" Value="1"/>
|
||||
</UI>
|
||||
|
||||
<UIRef Id="WixUI_Common"/>
|
||||
</Fragment>
|
||||
</Wix>
|
||||
@@ -17,6 +17,12 @@
|
||||
<PackageReference Include="AsmResolver.PE.Win32Resources" Version="5.5.1" />
|
||||
<PackageReference Include="MarkdigExtensions.RtfRenderer" Version="1.1.0" />
|
||||
<PackageReference Include="Microsoft.Security.Extensions" Version="1.4.0" />
|
||||
<PackageReference Include="Handlebars.Net" Version="2.1.6" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Msi\Templates\MsiLocale_en_US.hbs" />
|
||||
<EmbeddedResource Include="Msi\Templates\MsiTemplate.hbs" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
namespace Velopack.Packaging;
|
||||
|
||||
[Flags]
|
||||
public enum InstallLocation
|
||||
{
|
||||
Either,
|
||||
PerUser,
|
||||
PerMachine,
|
||||
None = 0,
|
||||
PerUser = 1 << 0,
|
||||
PerMachine = 1 << 1,
|
||||
Either = PerUser | PerMachine,
|
||||
}
|
||||
Reference in New Issue
Block a user