blob: 821b00d8bdd940ff2857f4ec02e59236d022b374 [file]
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst or https://cmake.org/licensing for details. */
#if defined(__CYGWIN__)
// For S_IWRITE symbol
# define _DEFAULT_SOURCE
#endif
#include "cmWIXFilesSourceWriter.h"
#include "cm_sys_stat.h"
#include "cmCMakeToWixPath.h"
#include "cmInstalledFile.h"
#include "cmSystemTools.h"
#include "cmWIXAccessControlList.h"
#ifdef _WIN32
# include "cmsys/Encoding.hxx"
#endif
cmWIXFilesSourceWriter::cmWIXFilesSourceWriter(
unsigned long wixVersion, cmCPackLog* logger, std::string const& filename,
GuidType componentGuidType, cmWIXInstallScope installScope,
std::string componentKeysRegistryPath)
: cmWIXSourceWriter(wixVersion, logger, filename, componentGuidType)
, ComponentKeysRegistryPath(std::move(componentKeysRegistryPath))
{
switch (installScope) {
case cmWIXInstallScope::PER_USER:
this->PerUserInstall = true;
break;
case cmWIXInstallScope::PER_MACHINE:
case cmWIXInstallScope::NONE:
this->PerUserInstall = false;
break;
default:
cmCPackLogger(cmCPackLog::LOG_ERROR,
"Unhandled install scope value, this is a CPack Bug.");
break;
}
}
void cmWIXFilesSourceWriter::EmitShortcut(std::string const& id,
cmWIXShortcut const& shortcut,
std::string const& shortcutPrefix,
size_t shortcutIndex)
{
std::ostringstream shortcutId;
shortcutId << shortcutPrefix << id;
if (shortcutIndex > 0) {
shortcutId << "_" << shortcutIndex;
}
std::string fileId = std::string("CM_F") + id;
BeginElement("Shortcut");
AddAttribute("Id", shortcutId.str());
AddAttribute("Name", shortcut.label);
std::string target = "[#" + fileId + "]";
AddAttribute("Target", target);
AddAttribute("WorkingDirectory", shortcut.workingDirectoryId);
EndElement("Shortcut");
}
void cmWIXFilesSourceWriter::EmitRemoveFolder(std::string const& id)
{
BeginElement("RemoveFolder");
AddAttribute("Id", id);
AddAttribute("On", "uninstall");
EndElement("RemoveFolder");
}
void cmWIXFilesSourceWriter::EmitInstallRegistryValue(
std::string const& registryKey, std::string const& cpackComponentName,
std::string const& suffix)
{
std::string valueName;
if (!cpackComponentName.empty()) {
valueName = cpackComponentName + "_";
}
valueName += "installed";
valueName += suffix;
BeginElement("RegistryValue");
AddAttribute("Root", "HKCU");
AddAttribute("Key", registryKey);
AddAttribute("Name", valueName);
AddAttribute("Type", "integer");
AddAttribute("Value", "1");
AddAttribute("KeyPath", "yes");
EndElement("RegistryValue");
}
void cmWIXFilesSourceWriter::EmitUninstallShortcut(
std::string const& packageName)
{
BeginElement("Shortcut");
AddAttribute("Id", "UNINSTALL");
AddAttribute("Name", "Uninstall " + packageName);
AddAttribute("Description", "Uninstalls " + packageName);
AddAttribute("Target", "[SystemFolder]msiexec.exe");
AddAttribute("Arguments", "/x [ProductCode]");
EndElement("Shortcut");
}
std::string cmWIXFilesSourceWriter::EmitComponentCreateFolder(
std::string const& directoryId, std::string const& guid,
cmInstalledFile const* installedFile)
{
std::string componentId = std::string("CM_C_EMPTY_") + directoryId;
BeginElement("DirectoryRef");
AddAttribute("Id", directoryId);
BeginElement("Component");
AddAttribute("Id", componentId);
AddAttribute("Guid", guid);
BeginElement("CreateFolder");
if (installedFile) {
cmWIXAccessControlList acl(Logger, *installedFile, *this);
acl.Apply();
}
EndElement("CreateFolder");
EndElement("Component");
EndElement("DirectoryRef");
return componentId;
}
std::string cmWIXFilesSourceWriter::EmitComponentFile(
std::string const& directoryId, std::string const& id,
std::string const& filePath, cmWIXPatch& patch,
cmInstalledFile const* installedFile, int diskId)
{
std::string componentId = std::string("CM_C") + id;
std::string fileId = std::string("CM_F") + id;
// Wix doesn't support automatic GUIDs for components which have both
// registry entries and files.
std::string guid = this->PerUserInstall
? CreateCmakeGeneratedGuidFromComponentId(componentId)
: CreateGuidFromComponentId(componentId);
BeginElement("DirectoryRef");
AddAttribute("Id", directoryId);
BeginElement("Component");
AddAttribute("Id", componentId);
AddAttribute("Guid", guid);
if (diskId) {
AddAttribute("DiskId", std::to_string(diskId));
}
if (installedFile) {
if (installedFile->GetPropertyAsBool("CPACK_NEVER_OVERWRITE")) {
AddAttribute("NeverOverwrite", "yes");
}
if (installedFile->GetPropertyAsBool("CPACK_PERMANENT")) {
AddAttribute("Permanent", "yes");
}
}
patch.ApplyFragment(componentId, *this);
if (this->PerUserInstall) {
// For perUser installs, MSI requires using a registry entry as the key
// path for components.
BeginElement("RegistryValue");
AddAttribute("Root", "HKCU");
AddAttribute("Key", this->ComponentKeysRegistryPath);
AddAttribute("Name", fileId);
AddAttribute("Type", "string");
AddAttribute("Value", "1");
AddAttribute("KeyPath", "yes");
EndElement("RegistryValue");
}
BeginElement("File");
AddAttribute("Id", fileId);
std::string sourcePath = CMakeToWixPath(filePath);
#ifdef _WIN32
// WiX cannot handle long paths natively, but since v4,
// it supports long paths via UNC prefixes.
if (this->WixVersion >= 4) {
sourcePath = cmsys::Encoding::ToNarrow(
cmsys::Encoding::ToWindowsExtendedPath(sourcePath));
}
#endif
AddAttribute("Source", sourcePath);
if (!this->PerUserInstall) {
AddAttribute("KeyPath", "yes");
}
mode_t fileMode = 0;
cmSystemTools::GetPermissions(filePath.c_str(), fileMode);
if (!(fileMode & S_IWRITE)) {
AddAttribute("ReadOnly", "yes");
}
patch.ApplyFragment(fileId, *this);
if (installedFile) {
cmWIXAccessControlList acl(Logger, *installedFile, *this);
acl.Apply();
}
EndElement("File");
EndElement("Component");
EndElement("DirectoryRef");
return componentId;
}