diff options
| -rw-r--r-- | doc/reference/modules/wix-module.qdoc | 228 | ||||
| -rw-r--r-- | share/qbs/imports/qbs/base/BurnSetupPackage.qbs | 4 | ||||
| -rw-r--r-- | share/qbs/imports/qbs/base/WindowsInstallerPackage.qbs | 4 | ||||
| -rw-r--r-- | share/qbs/modules/wix/WiXModule.qbs | 371 | ||||
| -rw-r--r-- | tests/manual/WiXInstallers/ExampleScript.bat | 1 | ||||
| -rwxr-xr-x | tests/manual/WiXInstallers/QbsBootstrapper.wxs | 10 | ||||
| -rwxr-xr-x | tests/manual/WiXInstallers/QbsSetup.wxs | 34 | ||||
| -rw-r--r-- | tests/manual/WiXInstallers/WiXInstallers.qbs | 18 |
8 files changed, 670 insertions, 0 deletions
diff --git a/doc/reference/modules/wix-module.qdoc b/doc/reference/modules/wix-module.qdoc new file mode 100644 index 000000000..d515511f5 --- /dev/null +++ b/doc/reference/modules/wix-module.qdoc @@ -0,0 +1,228 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2013 Petroules Corporation. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Build Suite. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +/*! + \contentspage index.html + \page wix-module.html + \ingroup list-of-modules + + \title Module wix + \brief Provides Windows Installer XML Toolset support. + + The \c wix module contains properties and rules for building MSI and + Burn setup packages with the Windows Installer XML Toolset. + + This module is only available on Windows. + + \section1 General Properties + + + \section2 debugInformation + + \table + \row \li \b{Type:} \li \c{bool} + \row \li \b{Default:} \li \c{qbs.debugInformation} + \endtable + + Generate debug information. See \c{qbs.debugInformation}. + + \section2 defines + + \table + \row \li \b{Type:} \li \c{stringList} + \row \li \b{Default:} \li \c{undefined} + \endtable + + List of preprocessor macros that get passed to the compiler. + To set macro values use the following syntax: + \code + cpp.defines: ["USE_COLORS=1", 'COLOR_STR="blanched almond"'] + \endcode + + \section2 enableQbsDefines + + \table + \row \li \b{Type:} \li \c{bool} + \row \li \b{Default:} \li \c{true} + \endtable + + Whether to define preprocessor macros corresponding to values from the project and product + objects. When building a 64-bit package, the preprocessor variable \c{Win64} will also be + defined. + + \section2 visualStudioCompatibility + + \table + \row \li \b{Type:} \li \c{bool} + \row \li \b{Default:} \li \c{true} + \endtable + + Passes most of the same preprocessor macros to the compiler as Visual Studio does. + This allows easier authoring WiX files that are compatible with both QBS and MSBuild. + + \section2 includePaths + + \table + \row \li \b{Type:} \li \c{pathList} + \row \li \b{Default:} \li \c{undefined} + \endtable + + List of include paths. Relative paths are considered to be relative to the .qbs product file + they are used in. + + \section2 treatWarningsAsErrors + + \table + \row \li \b{Type:} \li \c{bool} + \row \li \b{Default:} \li \c{false} + \endtable + + Warnings will be handled as errors and cause the build to fail. + + \section2 warningLevel + + \table + \row \li \b{Type:} \li \c{string} + \row \li \b{Allowed Values:} \li \c{"none"}, \c{"normal"}, \c{"pedantic"} + \row \li \b{Default:} \li \c{"normal"} + \endtable + + Severity of warnings to emit. The higher the level, the more warnings will be shown. + + \section2 verboseOutput + + \table + \row \li \b{Type:} \li \c{bool} + \row \li \b{Default:} \li \c{false} + \endtable + + Whether to display verbose output from the compiler and linker. + + \section2 compilerFlags + + \table + \row \li \b{Type:} \li \c{stringList} + \row \li \b{Default:} \li undefined + \endtable + + Additional flags for the Candle compiler. + + \section2 linkerFlags + + \table + \row \li \b{Type:} \li \c{stringList} + \row \li \b{Default:} \li undefined + \endtable + + Additional flags for the Light linker. + + \section2 cultures + + \table + \row \li \b{Type:} \li \c{stringList} + \row \li \b{Default:} \li undefined + \endtable + + List of localizations to include in the MSI. Use \c{undefined} to include all localizations. + + \section2 extensions + + \table + \row \li \b{Type:} \li \c{stringList} + \row \li \b{Default:} \li \c{["WixBalExtension"]} if the product type is a Burn setup bundle, + otherwise \c{undefined}. + \row \li \b{Possible Values:} \li \c{"WixBalExtension"}, + \c{"WixComPlusExtension"}, + \c{"WixDependencyExtension"}, + \c{"WixDifxAppExtension"}, + \c{"WixDirectXExtension"}, + \c{"WixFirewallExtension"}, + \c{"WixGamingExtension"}, + \c{"WixIisExtension"}, + \c{"WixMsmqExtension"}, + \c{"WixNetFxExtension"}, + \c{"WixPSExtension"}, + \c{"WixSqlExtension"}, + \c{"WixTagExtension"}, + \c{"WixUIExtension"}, + \c{"WixUtilExtension"}, + \c{"WixVSExtension"}, + custom assemblies + \endtable + + List of extension assemblies to link into the output. + + \section2 toolchainInstallPath + + \table + \row \li \b{Type:} \li \c{path} + \row \li \b{Default:} \li determined automatically + \endtable + + WiX installation directory. Determined by searching the \c{WIX} environment variable, + known registry keys, and known installation paths until a match is found. + This should not normally need to be changed. + + \section2 compilerName + + \table + \row \li \b{Type:} \li \c{string} + \row \li \b{Default:} \li \c{"candle.exe"} + \endtable + + Name of the compiler binary. This should not normally need to be changed. + + \section2 compilerPath + + \table + \row \li \b{Type:} \li \c{string} + \row \li \b{Default:} \li \c{compilerName} + \endtable + + Directory where the compiler binary is located. This should not normally need to be changed. + + \section2 linkerName + + \table + \row \li \b{Type:} \li \c{string} + \row \li \b{Default:} \li \c{"light.exe"} + \endtable + + Name of the linker binary. This should not normally need to be changed. + + \section2 linkerPath + + \table + \row \li \b{Type:} \li \c{string} + \row \li \b{Default:} \li \c{linkerName} + \endtable + + Directory where the linker binary is located. This should not normally need to be changed. +*/ diff --git a/share/qbs/imports/qbs/base/BurnSetupPackage.qbs b/share/qbs/imports/qbs/base/BurnSetupPackage.qbs new file mode 100644 index 000000000..308fcc10b --- /dev/null +++ b/share/qbs/imports/qbs/base/BurnSetupPackage.qbs @@ -0,0 +1,4 @@ +Product { + Depends { name: "wix" } + type: ["burnsetup", "application"] +} diff --git a/share/qbs/imports/qbs/base/WindowsInstallerPackage.qbs b/share/qbs/imports/qbs/base/WindowsInstallerPackage.qbs new file mode 100644 index 000000000..27b3f68cc --- /dev/null +++ b/share/qbs/imports/qbs/base/WindowsInstallerPackage.qbs @@ -0,0 +1,4 @@ +Product { + Depends { name: "wix" } + type: ["msi"] +} diff --git a/share/qbs/modules/wix/WiXModule.qbs b/share/qbs/modules/wix/WiXModule.qbs new file mode 100644 index 000000000..487287402 --- /dev/null +++ b/share/qbs/modules/wix/WiXModule.qbs @@ -0,0 +1,371 @@ +import qbs 1.0 +import qbs.File +import qbs.FileInfo +import "../utils.js" as ModUtils + +Module { + condition: qbs.hostOS.contains("windows") && qbs.targetOS.contains("windows") + + property path toolchainInstallPath: { + // First try the WIX environment variable + var wixEnv = qbs.getenv("WIX"); + if (wixEnv) + return wixEnv; + + // Couldn't find in environment, now attempt to locate the latest version by checking all known versions + var versions = [ "4.0", "3.8", "3.7", "3.6", "3.5", "3.0", "2.0" ]; + for (var i in versions) { + // First try the registry... + //var regInstallPath = registryValue("HKLM/SOFTWARE/Wow6432Node/Microsoft/Windows Installer XML/" + versions[i] + "/InstallFolder"); + //if (regInstallPath) + // return regInstallPath; + + // Then try the known filesystem path + var path = FileInfo.joinPaths(qbs.getenv("PROGRAMFILES(X86)"), "WiX Toolset v" + versions[i]); + if (File.exists(path)) + return path; + } + } + + property string compilerName: "candle.exe" + property string compilerPath: compilerName + property string linkerName: "light.exe" + property string linkerPath: linkerName + + property string warningLevel: "normal" + PropertyOptions { + name: "warningLevel" + allowedValues: ["none", "normal", "pedantic"] + } + + property bool debugInformation: qbs.debugInformation + property bool treatWarningsAsErrors: false + property bool verboseOutput: false + PropertyOptions { + name: "verboseOutput" + description: "display verbose output from the compiler and linker" + } + + property bool visualStudioCompatibility: true + PropertyOptions { + name: "visualStudioCompatibility" + description: "whether to define most of the same variables as " + + "Visual Studio when using the Candle compiler" + } + + property bool enableQbsDefines: true + PropertyOptions { + name: "enableQbsDefines" + description: "built-in variables that are defined when using the Candle compiler" + } + + property pathList includePaths + PropertyOptions { + name: "includePaths" + description: "directories to add to the include search path" + } + + property stringList defines + PropertyOptions { + name: "defines" + description: "variables that are defined when using the Candle compiler" + } + + property stringList compilerFlags + PropertyOptions { + name: "compilerFlags" + description: "additional flags for the Candle compiler" + } + + property stringList linkerFlags + PropertyOptions { + name: "linkerFlags" + description: "additional flags for the Light linker" + } + + property stringList cultures + PropertyOptions { + name: "cultures" + description: "the list of localizations to build the MSI for; leave undefined to build all localizations" + } + + property stringList extensions: type.contains("burnsetup") ? ["WixBalExtension"] : [] // default to WiX Standard Bootstrapper extension + + // private properties + property string targetSuffix: { + if (type.contains("msi")) { + return windowsInstallerSuffix; + } else if (type.contains("burnsetup")) { + return executableSuffix; + } + } + + property string executableSuffix: ".exe" + property string windowsInstallerSuffix: ".msi" + + validate: { + if (!toolchainInstallPath) + throw "wix.toolchainInstallPath is not defined. Set wix.toolchainInstallPath in your profile."; + } + + setupBuildEnvironment: { + var v = new ModUtils.EnvironmentVariable("PATH", ";", true); + v.prepend(toolchainInstallPath + "/bin"); + v.set(); + } + + // WiX Include Files + FileTagger { + pattern: "*.wxi" + fileTags: ["wxi"] + } + + // WiX Localization Files + FileTagger { + pattern: "*.wxl" + fileTags: ["wxl"] + } + + // WiX Source Files + FileTagger { + pattern: "*.wxs" + fileTags: ["wxs"] + } + + Rule { + id: candleCompiler + inputs: ["wxs"] + + Artifact { + fileTags: ["wixobj"] + fileName: ".obj/" + product.name + "/" + input.baseDir.replace(':', '') + "/" + FileInfo.baseName(input.fileName) + ".wixobj" + } + + prepare: { + var i; + var args = ["-nologo"]; + + if (ModUtils.moduleProperty(product, "warningLevel") === "none") { + args.push("-sw"); + } else { + if (ModUtils.moduleProperty(product, "warningLevel") === "pedantic") { + args.push("-pedantic"); + } + + if (ModUtils.moduleProperty(product, "treatWarningsAsErrors")) { + args.push("-wx"); + } + } + + if (ModUtils.moduleProperty(product, "verboseOutput")) { + args.push("-v"); + } + + var arch = product.moduleProperty("qbs", "architecture"); + if (!["x86", "x86_64", "ia64", "arm"].contains(arch)) { + // http://wixtoolset.org/documentation/manual/v3/xsd/wix/package.html + throw("WiX: unsupported architecture '" + arch + "'"); + } + + // QBS uses "x86_64", Microsoft uses "x64" + if (arch === "x86_64") { + arch = "x64"; + } + + // Visual Studio defines these variables along with various solution and project names and paths; + // we'll pass most of them to ease compatibility between QBS and WiX projects originally created + // using Visual Studio. The only definitions we don't pass are the ones which make no sense at all + // in QBS, like the solution and project directories since they do not exist. + if (ModUtils.moduleProperty(product, "visualStudioCompatibility")) { + var toolchain = product.moduleProperty("cpp", "toolchain"); + if (toolchain && toolchain.contains("msvc")) { + var vcDir = product.moduleProperty("cpp", "toolchainInstallPath").replace(/[\\/]bin$/i, ""); + var vcRootDir = vcDir.replace(/[\\/]VC$/i, ""); + args.push("-dDevEnvDir=" + FileInfo.toWindowsSeparators(FileInfo.joinPaths(vcRootDir, 'Common7/IDE'))); + } + + var buildVariant = ModUtils.moduleProperty(product, "buildVariant"); + if (buildVariant === "debug") { + args.push("-dDebug"); + args.push("-dConfiguration=Debug"); + } else if (buildVariant === "release") { + // VS doesn't define "Release" + args.push("-dConfiguration=Release"); + } + + var productTargetExt = ModUtils.moduleProperty(product, "targetSuffix"); + if (!productTargetExt) { + throw("WiX: Unsupported product type '" + product.type + "'"); + } + + var builtBinaryFilePath = FileInfo.joinPaths(product.buildDirectory, product.destinationDirectory, product.targetName + productTargetExt); + args.push("-dOutDir=" + FileInfo.toWindowsSeparators(FileInfo.path(builtBinaryFilePath))); // in VS, relative to the project file by default + + args.push("-dPlatform=" + arch); + + args.push("-dProjectName=" + project.name); + + args.push("-dTargetDir=" + FileInfo.toWindowsSeparators(FileInfo.path(builtBinaryFilePath))); // in VS, an absolute path + args.push("-dTargetExt=" + productTargetExt); + args.push("-dTargetFileName=" + product.targetName + productTargetExt); + args.push("-dTargetName=" + product.targetName); + args.push("-dTargetPath=" + FileInfo.toWindowsSeparators(builtBinaryFilePath)); + } + + var includePaths = ModUtils.moduleProperty(product, "includePaths"); + for (i in includePaths) { + args.push("-I" + includePaths[i]); + } + + var enableQbsDefines = ModUtils.moduleProperty(product, "enableQbsDefines") + if (enableQbsDefines) { + var map = { + "project.": project, + "product.": product + }; + + for (var prefix in map) { + var obj = map[prefix]; + for (var prop in obj) { + var val = obj[prop]; + if (typeof val !== 'function' && typeof val !== 'object' && !prop.startsWith("_")) { + args.push("-d" + prefix + prop + "=" + val); + } + } + } + + // Helper define for alternating between 32-bit and 64-bit logic + if (arch === "x64" || arch === "ia64") { + args.push("-dWin64"); + } + } + + // User-supplied defines + var defines = ModUtils.moduleProperty(product, "defines"); + for (i in defines) { + args.push("-d" + defines[i]); + } + + // User-supplied flags + var flags = ModUtils.moduleProperty(product, "compilerFlags"); + for (i in flags) { + args.push(flags[i]); + } + + args.push("-out"); + args.push(FileInfo.toWindowsSeparators(output.fileName)); + args.push("-arch"); + args.push(arch); + + var extensions = ModUtils.moduleProperty(product, "extensions"); + for (i in extensions) { + args.push("-ext"); + args.push(extensions[i]); + } + + args.push(FileInfo.toWindowsSeparators(inputs.wxs[0].fileName)); + + var cmd = new Command(ModUtils.moduleProperty(product, "compilerPath"), args); + cmd.description = "compiling " + FileInfo.fileName(inputs.wxs[0].fileName); + cmd.highlight = "compiler"; + cmd.workingDirectory = FileInfo.path(output.fileName); + return cmd; + } + } + + Rule { + id: lightLinker + multiplex: true + inputs: ["wixobj", "wxl"] + + Artifact { + condition: product.type.contains("burnsetup") + fileTags: ["burnsetup"] + fileName: product.destinationDirectory + "/" + product.targetName + ModUtils.moduleProperty(product, "executableSuffix") + } + + Artifact { + condition: product.type.contains("msi") + fileTags: ["msi"] + fileName: product.destinationDirectory + "/" + product.targetName + ModUtils.moduleProperty(product, "windowsInstallerSuffix") + } + + Artifact { + condition: product.moduleProperty("qbs", "debugInformation") // ### QBS-412 + fileTags: ["wixpdb"] + fileName: product.destinationDirectory + "/" + product.targetName + ".wixpdb" + } + + prepare: { + var i; + var primaryOutput; + if (product.type.contains("burnsetup")) { + primaryOutput = outputs.burnsetup[0]; + } else if (product.type.contains("msi")) { + primaryOutput = outputs.msi[0]; + } else { + throw("WiX: Unsupported product type '" + product.type + "'"); + } + + var args = ["-nologo"]; + + if (ModUtils.moduleProperty(product, "warningLevel") === "none") { + args.push("-sw"); + } else { + if (ModUtils.moduleProperty(product, "warningLevel") === "pedantic") { + args.push("-pedantic"); + } + + if (ModUtils.moduleProperty(product, "treatWarningsAsErrors")) { + args.push("-wx"); + } + } + + if (ModUtils.moduleProperty(product, "verboseOutput")) { + args.push("-v"); + } + + args.push("-out"); + args.push(FileInfo.toWindowsSeparators(primaryOutput.fileName)); + + if (ModUtils.moduleProperty(product, "debugInformation")) { + args.push("-pdbout"); + args.push(FileInfo.toWindowsSeparators(outputs.wixpdb[0].fileName)); + } else { + args.push("-spdb"); + } + + var extensions = ModUtils.moduleProperty(product, "extensions"); + for (i in extensions) { + args.push("-ext"); + args.push(extensions[i]); + } + + for (i in inputs.wxl) { + args.push("-loc"); + args.push(outputs.wxl[i].fileName); + } + + if (product.type.contains("msi")) { + var cultures = ModUtils.moduleProperty(product, "cultures"); + args.push("-cultures:" + (cultures ? cultures.join(";") : "null")); + } + + // User-supplied flags + var flags = ModUtils.moduleProperty(product, "linkerFlags"); + for (i in flags) { + args.push(flags[i]); + } + + for (i in inputs.wixobj) { + args.push(inputs.wixobj[i].fileName); + } + + var cmd = new Command(ModUtils.moduleProperty(product, "linkerPath"), args); + cmd.description = "linking " + FileInfo.fileName(input.fileName); + cmd.highlight = "linker"; + cmd.workingDirectory = FileInfo.path(primaryOutput.fileName); + return cmd; + } + } +} diff --git a/tests/manual/WiXInstallers/ExampleScript.bat b/tests/manual/WiXInstallers/ExampleScript.bat new file mode 100644 index 000000000..3af583cd8 --- /dev/null +++ b/tests/manual/WiXInstallers/ExampleScript.bat @@ -0,0 +1 @@ +echo Hello world! diff --git a/tests/manual/WiXInstallers/QbsBootstrapper.wxs b/tests/manual/WiXInstallers/QbsBootstrapper.wxs new file mode 100755 index 000000000..272f6af5b --- /dev/null +++ b/tests/manual/WiXInstallers/QbsBootstrapper.wxs @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"> + <Bundle Name="QbsBootstrapper" Version="1.0.0.0" Manufacturer="Qt Project" UpgradeCode="7b05b159-c9ce-477c-9fb5-7fce3cd50396"> + <BootstrapperApplicationRef Id="WixStandardBootstrapperApplication.RtfLicense" /> + + <Chain> + <MsiPackage SourceFile="$(var.msiName)" /> + </Chain> + </Bundle> +</Wix> diff --git a/tests/manual/WiXInstallers/QbsSetup.wxs b/tests/manual/WiXInstallers/QbsSetup.wxs new file mode 100755 index 000000000..8f97ff667 --- /dev/null +++ b/tests/manual/WiXInstallers/QbsSetup.wxs @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"> + <Product Id="*" Name="QbsSetup" Language="1033" Version="1.0.0.0" Manufacturer="Qt Project" UpgradeCode="f60f643e-b002-44d5-b3f4-edafd078314c"> + <Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" /> + + <MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." /> + <MediaTemplate /> + + <Feature Id="ProductFeature" Title="QbsSetup" Level="1"> + <ComponentGroupRef Id="ProductComponents" /> + </Feature> + </Product> + + <Fragment> + <Directory Id="TARGETDIR" Name="SourceDir"> + <?ifdef Win64 ?> + <?define PlatformProgramFilesFolder = "ProgramFiles64Folder" ?> + <?else ?> + <?define PlatformProgramFilesFolder = "ProgramFilesFolder" ?> + <?endif ?> + <Directory Id="$(var.PlatformProgramFilesFolder)"> + <Directory Id="INSTALLFOLDER" Name="QbsSetup" /> + </Directory> + </Directory> + </Fragment> + + <Fragment> + <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER"> + <Component Id="ProductComponent"> + <File Source="$(var.project.path)/$(var.scriptName)" /> + </Component> + </ComponentGroup> + </Fragment> +</Wix> diff --git a/tests/manual/WiXInstallers/WiXInstallers.qbs b/tests/manual/WiXInstallers/WiXInstallers.qbs new file mode 100644 index 000000000..4080a2450 --- /dev/null +++ b/tests/manual/WiXInstallers/WiXInstallers.qbs @@ -0,0 +1,18 @@ +import qbs + +Project { + WindowsInstallerPackage { + name: "QbsSetup" + targetName: "qbs-" + qbs.architecture + files: ["QbsSetup.wxs", "ExampleScript.bat"] + wix.defines: ["scriptName=ExampleScript.bat"] + } + + BurnSetupPackage { + Depends { name: "QbsSetup" } + name: "QbsBootstrapper" + targetName: "qbs-setup-" + qbs.architecture + files: ["QbsBootstrapper.wxs"] + wix.defines: ["msiName=QbsSetup.msi"] + } +} |
