aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/reference/items/depends.qdoc2
-rw-r--r--doc/reference/items/group.qdoc11
-rw-r--r--doc/reference/items/product.qdoc13
-rw-r--r--doc/reference/items/project.qdoc11
-rw-r--r--doc/reference/items/rule.qdoc12
-rw-r--r--doc/reference/modules/nodejs-module.qdoc54
-rw-r--r--doc/reference/modules/qt-modules.qdoc38
-rw-r--r--doc/reference/modules/typescript-module.qdoc210
-rw-r--r--qbs_version.pri2
-rw-r--r--share/qbs/imports/qbs/BundleTools/bundle-tools.js355
-rw-r--r--share/qbs/imports/qbs/DarwinTools/darwin-tools.js (renamed from share/qbs/modules/cpp/darwin-tools.js)56
-rw-r--r--share/qbs/imports/qbs/FileInfo/fileinfo.js35
-rw-r--r--share/qbs/imports/qbs/ModUtils/utils.js309
-rw-r--r--share/qbs/imports/qbs/PathTools/path-tools.js (renamed from share/qbs/modules/cpp/path-tools.js)68
-rw-r--r--share/qbs/imports/qbs/Probes/PathProbe.qbs10
-rw-r--r--share/qbs/imports/qbs/Probes/PkgConfigProbe.qbs1
-rw-r--r--share/qbs/imports/qbs/Probes/utils.js18
-rw-r--r--share/qbs/imports/qbs/UnixUtils/unix-utils.js15
-rw-r--r--share/qbs/imports/qbs/WindowsUtils/windows-utils.js (renamed from share/qbs/modules/cpp/windows.js)0
-rw-r--r--share/qbs/imports/qbs/base/NodeJSApplication.qbs3
-rw-r--r--share/qbs/modules/cpp/DarwinGCC.qbs16
-rw-r--r--share/qbs/modules/cpp/GenericGCC.qbs42
-rw-r--r--share/qbs/modules/cpp/bundle-tools.js330
-rw-r--r--share/qbs/modules/cpp/gcc.js72
-rw-r--r--share/qbs/modules/cpp/ios-gcc.qbs4
-rw-r--r--share/qbs/modules/cpp/msvc.js35
-rw-r--r--share/qbs/modules/cpp/windows-mingw.qbs9
-rw-r--r--share/qbs/modules/cpp/windows-msvc.qbs31
-rw-r--r--share/qbs/modules/ib/IBModule.qbs6
-rw-r--r--share/qbs/modules/nodejs/NodeJS.qbs56
-rwxr-xr-xshare/qbs/modules/nsis/NSISModule.qbs19
-rw-r--r--share/qbs/modules/qbs/common.qbs59
-rw-r--r--share/qbs/modules/typescript/TypeScriptModule.qbs266
-rw-r--r--share/qbs/modules/wix/WiXModule.qbs33
-rw-r--r--src/app/app.pri11
-rw-r--r--src/app/apps.qbs1
-rw-r--r--src/app/apptemplate.qbs2
-rw-r--r--src/app/qbs-setup-qt/setupqt.cpp1
-rw-r--r--src/app/qbs-setup-toolchains/probe.cpp13
-rw-r--r--src/lib/corelib/api/project.cpp9
-rw-r--r--src/lib/corelib/api/projectdata.cpp6
-rw-r--r--src/lib/corelib/api/projectdata.h1
-rw-r--r--src/lib/corelib/api/projectdata_p.h1
-rw-r--r--src/lib/corelib/api/runenvironment.cpp25
-rw-r--r--src/lib/corelib/buildgraph/executor.cpp6
-rw-r--r--src/lib/corelib/buildgraph/rulesapplicator.cpp5
-rw-r--r--src/lib/corelib/buildgraph/transformer.cpp7
-rw-r--r--src/lib/corelib/language/asttools.cpp2
-rw-r--r--src/lib/corelib/language/asttools.h2
-rw-r--r--src/lib/corelib/language/builtindeclarations.cpp13
-rw-r--r--src/lib/corelib/language/item.cpp8
-rw-r--r--src/lib/corelib/language/item.h1
-rw-r--r--src/lib/corelib/language/itemreaderastvisitor.cpp2
-rw-r--r--src/lib/corelib/language/itemreaderastvisitor.h2
-rw-r--r--src/lib/corelib/language/language.cpp13
-rw-r--r--src/lib/corelib/language/language.h1
-rw-r--r--src/lib/corelib/language/loader.cpp6
-rw-r--r--src/lib/corelib/language/moduleloader.cpp60
-rw-r--r--src/lib/corelib/language/moduleloader.h12
-rw-r--r--src/lib/corelib/language/projectresolver.cpp74
-rw-r--r--src/lib/corelib/language/projectresolver.h3
-rw-r--r--src/lib/corelib/language/testdata/erroneous/conflicting_fileTagsFilter.qbs13
-rw-r--r--src/lib/corelib/language/testdata/profilevaluesandoverriddenvalues.qbs5
-rw-r--r--src/lib/corelib/language/tst_language.cpp4
-rw-r--r--src/lib/corelib/tools/settings.cpp27
-rw-r--r--src/lib/qtprofilesetup/qtprofilesetup.cpp25
-rw-r--r--src/lib/qtprofilesetup/qtprofilesetup.h1
-rw-r--r--src/lib/qtprofilesetup/templates/core.qbs85
-rw-r--r--src/lib/qtprofilesetup/templates/gui.qbs4
-rw-r--r--tests/auto/api/testdata/is-runnable/project.qbs10
-rw-r--r--tests/auto/api/tst_api.cpp22
-rw-r--r--tests/auto/api/tst_api.h1
-rw-r--r--tests/auto/blackbox/testdata/build-directories/input.txt0
-rw-r--r--tests/auto/blackbox/testdata/build-directories/project.qbs49
-rw-r--r--tests/auto/blackbox/testdata/dynamicRuleOutputs/before/genlexer.qbs6
-rw-r--r--tests/auto/blackbox/testdata/nodejs/hello.js3
-rw-r--r--tests/auto/blackbox/testdata/nodejs/hello.qbs7
-rw-r--r--tests/auto/blackbox/testdata/simpleProbe/main.cpp1
-rw-r--r--tests/auto/blackbox/testdata/simpleProbe/simpleProbe.qbs31
-rw-r--r--tests/auto/blackbox/testdata/typescript/animals.ts21
-rw-r--r--tests/auto/blackbox/testdata/typescript/extra.js3
-rw-r--r--tests/auto/blackbox/testdata/typescript/foo.ts5
-rw-r--r--tests/auto/blackbox/testdata/typescript/main.ts19
-rw-r--r--tests/auto/blackbox/testdata/typescript/typescript.qbs34
-rw-r--r--tests/auto/blackbox/tst_blackbox.cpp308
-rw-r--r--tests/auto/blackbox/tst_blackbox.h3
-rw-r--r--version.js2
87 files changed, 2148 insertions, 1029 deletions
diff --git a/doc/reference/items/depends.qdoc b/doc/reference/items/depends.qdoc
index ce633e5fa..17816a455 100644
--- a/doc/reference/items/depends.qdoc
+++ b/doc/reference/items/depends.qdoc
@@ -35,7 +35,7 @@
\ingroup list-of-items
\title Depends Item
- \brief Represents dependencies between between products and modules.
+ \brief Represents dependencies between products and modules.
A \c Depends item can appear inside a \l{Product Item} or \l{Module Item}.
diff --git a/doc/reference/items/group.qdoc b/doc/reference/items/group.qdoc
index 5c03a4c97..89acd08f7 100644
--- a/doc/reference/items/group.qdoc
+++ b/doc/reference/items/group.qdoc
@@ -60,7 +60,7 @@
name: "Files to install"
qbs.install: true
qbs.installDir: "share"
- files "runtime_resource.txt"
+ files: "runtime_resource.txt"
}
}
\endcode
@@ -129,10 +129,11 @@
\li overrideTags
\li bool
\li true
- \li If the same file appears in the files list of both the top level of a product and
- a group and this property is true, then the file tags set via the group
- replace the ones set via the product. If it is false, the "group tags" are added to
- the "product tags".
+ \li Determines how tags on files that are listed both at the top level of
+ a product and a group are handled.
+ If this property is true, then the file tags set via the group
+ replace the ones set via the product.
+ If it is false, the "group tags" are added to the "product tags".
\row
\li excludeFiles
\li list
diff --git a/doc/reference/items/product.qdoc b/doc/reference/items/product.qdoc
index 295b3df67..28737c9b9 100644
--- a/doc/reference/items/product.qdoc
+++ b/doc/reference/items/product.qdoc
@@ -97,8 +97,9 @@
\row
\li destinationDirectory
\li string
- \li "."
- \li The directory where the target artifacts will be located. Relative to the build directory.
+ \li product.buildDirectory
+ \li The directory where the target artifacts will be located. If a relative path is
+ given, the base directory will be \c project.buildDirectory.
\row
\li files
\li stringList
@@ -122,7 +123,7 @@
\li stringList
\li project.qbsSearchPaths
\li See the documentation of the \l {Project Item} property of the same name.
- Setting this property here will overwrite the default value inherited from
+ Setting this property here will override the default value inherited from
the project, so use the \c concat() function if you want to add something.
\row
\li version
@@ -132,7 +133,7 @@
Info.plist files in OS X and iOS application and framework bundles, for example.
\endtable
- The following properties are automatically set by \QBS and usually are not changed by the user:
+ The following properties are automatically set by \QBS and cannot be changed by the user:
\table
\header
@@ -141,12 +142,12 @@
\li Description
\row
\li buildDirectory
- \li string
+ \li path
\li The build directory for this product. This is the directory where generated files
are placed.
\row
\li sourceDirectory
- \li string
+ \li path
\li The source directory for this product. This is the directory of the file where this
product is defined.
\endtable
diff --git a/doc/reference/items/project.qdoc b/doc/reference/items/project.qdoc
index f2a4710d1..c1a62bc40 100644
--- a/doc/reference/items/project.qdoc
+++ b/doc/reference/items/project.qdoc
@@ -62,6 +62,11 @@
\li Default
\li Description
\row
+ \li buildDirectory
+ \li path
+ \li n/a
+ \li The build directory of the top-level project. This property is read-only.
+ \row
\li name
\li string
\li basename of the file the project is defined in
@@ -85,5 +90,11 @@
\li empty
\li A list of files from which to import products. This is equivalent to defining
the respective \c Product items directly under this \c Project item.
+ \row
+ \li sourceDirectory
+ \li path
+ \li n/a
+ \li The directory where the file containing the top-level \c Project item is located.
+ This property is read-only.
\endtable
*/
diff --git a/doc/reference/items/rule.qdoc b/doc/reference/items/rule.qdoc
index 91dcc6137..9a174e49c 100644
--- a/doc/reference/items/rule.qdoc
+++ b/doc/reference/items/rule.qdoc
@@ -39,10 +39,10 @@
A \e {multiplex rule} creates one \e transformer that takes all
input artifacts with the matching input file tag and creates
one or more artifacts (e.g. C++ linker).
- A \e {non-multiplex rule} creates one transformer per matching input file (e.g. C++
- compiler).
- As a real-world example of a non-multiplex rule, here is a simplified version of \QBS' rule for
- transforming C++ sources into object files using gcc:
+ A \e {simplex rule} creates one transformer per matching input file
+ (e.g. C++ compiler).
+ As a real-world example of a simplex rule, here is a simplified version
+ of \QBS' rule for transforming C++ sources into object files using gcc:
\code
Rule {
id: compiler
@@ -51,7 +51,7 @@
Artifact {
fileTags: ['obj']
- fileName: '.obj/' + product.name + '/' + input.baseDir + '/' + input.fileName + '.o'
+ fileName: '.obj/' + input.baseDir + '/' + input.fileName + '.o'
}
prepare: {
@@ -79,7 +79,7 @@
args.push(output.filePath);
var compilerPath = ModUtils.moduleProperty(product, 'compilerPath');
var cmd = new Command(compilerPath, args);
- cmd.description = 'compiling ' + FileInfo.fileName(input.filePath);
+ cmd.description = 'compiling ' + input.fileName;
cmd.highlight = 'compiler';
return cmd;
}
diff --git a/doc/reference/modules/nodejs-module.qdoc b/doc/reference/modules/nodejs-module.qdoc
new file mode 100644
index 000000000..1e2d7bab1
--- /dev/null
+++ b/doc/reference/modules/nodejs-module.qdoc
@@ -0,0 +1,54 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 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 nodejs-module.html
+ \ingroup list-of-modules
+
+ \title Module nodejs
+ \brief Provides Node.js support.
+
+ The \c nodejs module contains support for running \l{http://nodejs.org}{Node.js}
+ applications from \QBS.
+
+ \section1 General Properties
+
+
+ \section2 applicationFile
+
+ \table
+ \row \li \b{Type:} \li \c{path}
+ \row \li \b{Default:} \li \c{undefined}
+ \endtable
+
+ The input JavaScript file whose corresponding output will be executed when running the Node.js
+ application. If this property is \c{undefined}, the product will not be runnable.
+*/
diff --git a/doc/reference/modules/qt-modules.qdoc b/doc/reference/modules/qt-modules.qdoc
index 7f29be65a..01e2226b9 100644
--- a/doc/reference/modules/qt-modules.qdoc
+++ b/doc/reference/modules/qt-modules.qdoc
@@ -408,6 +408,25 @@
Specifies whether QML debugging support should be compiled into your binaries.
+ \section3 qmlImportsPath
+
+ \table
+ \row \li \b{Type:} \li \c{string}
+ \row \li \b{Default:} \li set by \c{qbs-setup-qt}
+ \endtable
+
+ The absolute path to the directory where Qt's QML imports are installed.
+
+ \section3 qmlPath
+
+ \table
+ \row \li \b{Type:} \li \c{string}
+ \row \li \b{Default:} \li set by \c{qbs-setup-qt}
+ \endtable
+
+ The absolute path to the directory where Qt's QML files are installed.
+ This property is undefined for Qt4.
+
\section2 gui Properties
\section3 uicName
@@ -430,4 +449,23 @@
Specifies whether QML debugging support should be compiled into your binaries.
+ \section3 qmlImportsPath
+
+ \table
+ \row \li \b{Type:} \li \c{string}
+ \row \li \b{Default:} \li set by \c{qbs-setup-qt}
+ \endtable
+
+ The absolute path to the directory where Qt's QML imports are installed.
+
+ \section3 qmlPath
+
+ \table
+ \row \li \b{Type:} \li \c{string}
+ \row \li \b{Default:} \li set by \c{qbs-setup-qt}
+ \endtable
+
+ The absolute path to the directory where Qt's QML files are installed.
+ This property is undefined for Qt4.
+
*/
diff --git a/doc/reference/modules/typescript-module.qdoc b/doc/reference/modules/typescript-module.qdoc
new file mode 100644
index 000000000..b7a969a93
--- /dev/null
+++ b/doc/reference/modules/typescript-module.qdoc
@@ -0,0 +1,210 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 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 typescript-module.html
+ \ingroup list-of-modules
+
+ \title Module typescript
+ \brief Provides TypeScript support.
+
+ The \c typescript module contains properties and rules for building
+ \l{http://www.typescriptlang.org}{TypeScript} applications and may be used in combination with
+ the \l {Module nodejs} {nodejs} module to run TypeScript applications directly from \QBS.
+
+ \section1 General Properties
+
+
+ \section2 warningLevel
+
+ \table
+ \row \li \b{Type:} \li \c{string}
+ \row \li \b{Allowed Values:} \li \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.
+ \c{pedantic} causes the TypeScript to emit warnings on expressions and declarations with an
+ implied 'any' type.
+
+ \section2 targetVersion
+
+ \table
+ \row \li \b{Type:} \li \c{string}
+ \row \li \b{Allowed Values:} \li \c{"ES3"}, \c{"ES5"}
+ \row \li \b{Default:} \li \c{undefined}
+ \endtable
+
+ ECMAScript target version for generated JavaScript code. \c{undefined} uses the TypeScript
+ compiler default, which is currently \c{"ES3"}.
+
+ \section2 moduleLoader
+
+ \table
+ \row \li \b{Type:} \li \c{string}
+ \row \li \b{Allowed Values:} \li \c{"commonjs"}, \c{"amd"}
+ \row \li \b{Default:} \li \c{undefined}
+ \endtable
+
+ If TypeScript modules are being used, the JavaScript module loading mechanism to use in the
+ generated JavaScript code. \c{undefined} indicates modules are not being used.
+
+ \section2 stripComments
+
+ \table
+ \row \li \b{Type:} \li \c{bool}
+ \row \li \b{Default:} \li \c{!qbs.debugInformation}
+ \endtable
+
+ Whether to remove comments from the generated JavaScript files.
+
+ \section2 generateDeclarations
+
+ \table
+ \row \li \b{Type:} \li \c{bool}
+ \row \li \b{Default:} \li \c{false}
+ \endtable
+
+ Whether to generate corresponding .d.ts files during compilation; these are TypeScript's
+ equivalent of header files.
+
+ \section2 generateSourceMaps
+
+ \table
+ \row \li \b{Type:} \li \c{bool}
+ \row \li \b{Default:} \li \c{qbs.debugInformation}
+ \endtable
+
+ Whether to generate corresponding .map files during compilation.
+
+ \section2 compilerFlags
+
+ \table
+ \row \li \b{Type:} \li \c{stringList}
+ \row \li \b{Default:} \li undefined
+ \endtable
+
+ Additional flags for the TypeScript compiler.
+
+ \section2 singleFile
+
+ \table
+ \row \li \b{Type:} \li \c{bool}
+ \row \li \b{Default:} \li \c{false}
+ \endtable
+
+ Whether to compile all TypeScript source files to a single JavaScript output file. The default
+ is to compile each TypeScript file to a corresponding JavaScript file. This property is
+ incompatible with \c{moduleLoader}.
+
+ \section2 version
+
+ \table
+ \row \li \b{Type:} \li \c{string}
+ \row \li \b{Default:} \li \c{undefined}
+ \endtable
+
+ The TypeScript version. Consists of four numbers separated by dots, for instance "1.0.0.0".
+
+ \section2 versionMajor
+
+ \table
+ \row \li \b{Type:} \li \c{int}
+ \row \li \b{Default:} \li \c{versionParts[0]}
+ \endtable
+
+ The TypeScript major version.
+
+ \section2 versionMinor
+
+ \table
+ \row \li \b{Type:} \li \c{int}
+ \row \li \b{Default:} \li \c{versionParts[1]}
+ \endtable
+
+ The TypeScript minor version.
+
+ \section2 versionParts
+
+ \table
+ \row \li \b{Type:} \li \c{list}
+ \row \li \b{Default:} \li \c{empty}
+ \endtable
+
+ The TypeScript version as a list. For instance, TypeScript version 1.0 would correspond to a
+ value of \c[1, 0, 0, 0].
+
+ \section2 versionPatch
+
+ \table
+ \row \li \b{Type:} \li \c{int}
+ \row \li \b{Default:} \li \c{versionParts[2]}
+ \endtable
+
+ The TypeScript patch level.
+
+ \section2 versionBuild
+
+ \table
+ \row \li \b{Type:} \li \c{int}
+ \row \li \b{Default:} \li \c{versionParts[3]}
+ \endtable
+
+ The fourth TypeScript version number component.
+
+ \section2 toolchainInstallPath
+
+ \table
+ \row \li \b{Type:} \li \c{path}
+ \row \li \b{Default:} \li \c{undefined}
+ \endtable
+
+ TypeScript installation directory. This should not normally need to be changed provided that
+ \c{tsc} is already available by searching the PATH environment variable.
+
+ \section2 compilerName
+
+ \table
+ \row \li \b{Type:} \li \c{string}
+ \row \li \b{Default:} \li \c{"tsc"}
+ \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.
+*/
diff --git a/qbs_version.pri b/qbs_version.pri
index 7e8806517..4ef0da176 100644
--- a/qbs_version.pri
+++ b/qbs_version.pri
@@ -1,3 +1,3 @@
-QBS_VERSION = 1.2.1
+QBS_VERSION = 1.3.0
QBS_VERSION_MAJ = $$section(QBS_VERSION, ., 0, 0)
DEFINES += QBS_VERSION=\\\"$$QBS_VERSION\\\"
diff --git a/share/qbs/imports/qbs/BundleTools/bundle-tools.js b/share/qbs/imports/qbs/BundleTools/bundle-tools.js
new file mode 100644
index 000000000..a2950a365
--- /dev/null
+++ b/share/qbs/imports/qbs/BundleTools/bundle-tools.js
@@ -0,0 +1,355 @@
+// NOTE: QBS and Xcode's "target" and "product" names are reversed
+
+var PropertyList = loadExtension("qbs.PropertyList");
+
+function isBundleProduct(product) {
+ return product.type.contains("applicationbundle")
+ || product.type.contains("frameworkbundle")
+ || product.type.contains("bundle")
+ || product.type.contains("inapppurchase");
+}
+
+/**
+ * Returns the package creator code for the given product based on its type.
+ */
+function packageType(product) {
+ if (product.type.contains("application") || product.type.contains("applicationbundle"))
+ return "APPL";
+ else if (product.type.contains("frameworkbundle"))
+ return "FMWK";
+ else if (product.type.contains("bundle"))
+ return "BNDL";
+
+ throw ("Unsupported product type " + product.type + ". "
+ + "Must be in {application, applicationbundle, frameworkbundle, bundle}.");
+}
+
+function infoPlistContents(infoPlistFilePath) {
+ if (infoPlistFilePath === undefined)
+ return undefined;
+
+ var plist = new PropertyList();
+ try {
+ plist.readFromFile(infoPlistFilePath);
+ return JSON.parse(plist.toJSONString());
+ } finally {
+ plist.clear();
+ }
+}
+
+function infoPlistFormat(infoPlistFilePath) {
+ if (infoPlistFilePath === undefined)
+ return undefined;
+
+ var plist = new PropertyList();
+ try {
+ plist.readFromFile(infoPlistFilePath);
+ return plist.format();
+ } finally {
+ plist.clear();
+ }
+}
+
+/**
+ * Returns the main bundle directory.
+ * @param version only used for framework bundles.
+ * @note Xcode equivalent: CONTENTS_FOLDER_PATH
+ */
+function contentsFolderPath(product, version) {
+ var path = wrapperName(product);
+
+ if (product.type.contains("frameworkbundle"))
+ path += "/Versions/" + (version || frameworkVersion(product));
+ else if (!isShallowBundle(product))
+ path += "/Contents";
+
+ return path;
+}
+
+/**
+ * Returns the directory for documentation files.
+ * @param version only used for framework bundles.
+ * @note Xcode equivalent: DOCUMENTATION_FOLDER_PATH
+ */
+function documentationFolderPath(product, localizationName, version) {
+ var path = localizedResourcesFolderPath(product, localizationName, version);
+ if (!product.type.contains("inapppurchase"))
+ path += "/Documentation";
+ return path;
+}
+
+/**
+ * Returns the destination directory for auxiliary executables.
+ * @param version only used for framework bundles.
+ * @note Xcode equivalent: EXECUTABLES_FOLDER_PATH
+ */
+function executablesFolderPath(product, localizationName, version) {
+ if (product.type.contains("frameworkbundle"))
+ return localizedResourcesFolderPath(product, localizationName, version);
+ else
+ return _contentsFolderSubDirPath(product, "Executables", version);
+}
+
+/**
+ * Returns the destination directory for the primary executable.
+ * @param version only used for framework bundles.
+ * @note Xcode equivalent: EXECUTABLE_FOLDER_PATH
+ */
+function executableFolderPath(product, version) {
+ var path = contentsFolderPath(product, version);
+ if (!isShallowBundle(product)
+ && !product.type.contains("frameworkbundle")
+ && !product.type.contains("inapppurchase"))
+ path += "/MacOS";
+
+ return path;
+}
+
+/**
+ * Returns the path to the bundle's primary executable file.
+ * @param version only used for framework bundles.
+ * @note Xcode equivalent: EXECUTABLE_PATH
+ */
+function executablePath(product, version) {
+ return executableFolderPath(product, version) + "/" + productName(product);
+}
+
+/**
+ * Returns the major version number or letter corresponding to the bundle version.
+ * @note Xcode equivalent: FRAMEWORK_VERSION
+ */
+function frameworkVersion(product) {
+ if (!product.type.contains("frameworkbundle"))
+ throw "Product type must be a frameworkbundle, was " + product.type;
+
+ var n = parseInt(product.version, 10);
+ return isNaN(n) ? 'A' : n;
+}
+
+/**
+ * Returns the directory containing frameworks used by the bundle's executables.
+ * @param version only used for framework bundles.
+ * @note Xcode equivalent: FRAMEWORKS_FOLDER_PATH
+ */
+function frameworksFolderPath(product, version) {
+ return _contentsFolderSubDirPath(product, "Frameworks", version);
+}
+
+/**
+ * Returns the path to the bundle's main information property list.
+ * @param version only used for framework bundles.
+ * @note Xcode equivalent: INFOPLIST_PATH
+ */
+function infoPlistPath(product, version) {
+ var path;
+ if (product.type.contains("application"))
+ path = ".tmp";
+ else if (product.type.contains("frameworkbundle"))
+ path = unlocalizedResourcesFolderPath(product, version);
+ else if (product.type.contains("inapppurchase"))
+ path = wrapperName(product);
+ else
+ path = contentsFolderPath(product, version);
+
+ return path + "/" + _infoFileNames(product)[0];
+}
+
+/**
+ * Returns the path to the strings file corresponding to the bundle's main information property list.
+ * @param version only used for framework bundles.
+ * @note Xcode equivalent: INFOSTRINGS_PATH
+ */
+function infoStringsPath(product, localizationName, version) {
+ return localizedResourcesFolderPath(product, localizationName, version) + "/" + _infoFileNames(product)[1];
+}
+
+/**
+ * Returns the path to the bundle's resources directory for the given localization.
+ * @param version only used for framework bundles.
+ * @note Xcode equivalent: LOCALIZED_RESOURCES_FOLDER_PATH
+ */
+function localizedResourcesFolderPath(product, localizationName, version) {
+ if (typeof localizationName !== "string")
+ throw("'" + localizationName + "' is not a valid localization name");
+
+ return unlocalizedResourcesFolderPath(product, version) + "/" + localizationName + ".lproj";
+}
+
+/**
+ * Returns the path to the bundle's PkgInfo (package info) file.
+ * @note Xcode equivalent: PKGINFO_PATH
+ */
+function pkgInfoPath(product) {
+ var path = (product.type.contains("frameworkbundle"))
+ ? wrapperName(product)
+ : contentsFolderPath(product);
+ return path + "/PkgInfo";
+}
+
+/**
+ * Returns the directory containing plugins used by the bundle's executables.
+ * @param version only used for framework bundles.
+ * @note Xcode equivalent: PLUGINS_FOLDER_PATH
+ */
+function pluginsFolderPath(product, version) {
+ if (product.type.contains("frameworkbundle"))
+ return unlocalizedResourcesFolderPath(product, version);
+
+ return _contentsFolderSubDirPath(product, "PlugIns", version);
+}
+
+/**
+ * Returns the directory containing private header files for the framework.
+ * @param version only used for framework bundles.
+ * @note Xcode equivalent: PRIVATE_HEADERS_FOLDER_PATH
+ */
+function privateHeadersFolderPath(product, version) {
+ return _contentsFolderSubDirPath(product, "PrivateHeaders", version);
+}
+
+/**
+ * Returns the name of the product (in Xcode terms) which corresponds to the target name in Qbs terms.
+ * @note Xcode equivalent: PRODUCT_NAME
+ */
+function productName(product) {
+ return product.targetName;
+}
+
+/**
+ * Returns the directory containing public header files for the framework.
+ * @param version only used for framework bundles.
+ * @note Xcode equivalent: PUBLIC_HEADERS_FOLDER_PATH
+ */
+function publicHeadersFolderPath(product, version) {
+ return _contentsFolderSubDirPath(product, "Headers", version);
+}
+
+/**
+ * Returns the directory containing script files associated with the bundle.
+ * @param version only used for framework bundles.
+ * @note Xcode equivalent: SCRIPTS_FOLDER_PATH
+ */
+function scriptsFolderPath(product, version) {
+ return unlocalizedResourcesFolderPath(product, version) + "/Scripts";
+}
+
+/**
+ * Returns whether the bundle is a shallow bundle.
+ * This controls the presence or absence of the Contents, MacOS and Resources folders.
+ * iOS tends to store the majority of files in its bundles in the main directory.
+ * @note Xcode equivalent: SHALLOW_BUNDLE
+ */
+function isShallowBundle(product) {
+ return product.moduleProperty("qbs", "targetOS").contains("ios")
+ && product.type.contains("applicationbundle");
+}
+
+/**
+ * Returns the directory containing sub-frameworks that may be shared with other applications.
+ * @param version only used for framework bundles.
+ * @note Xcode equivalent: SHARED_FRAMEWORKS_FOLDER_PATH
+ */
+function sharedFrameworksFolderPath(product, version) {
+ return _contentsFolderSubDirPath(product, "SharedFrameworks", version);
+}
+
+/**
+ * Returns the directory containing supporting files that may be shared with other applications.
+ * @param version only used for framework bundles.
+ * @note Xcode equivalent: SHARED_SUPPORT_FOLDER_PATH
+ */
+function sharedSupportFolderPath(product, version) {
+ if (product.type.contains("frameworkbundle"))
+ return unlocalizedResourcesFolderPath(product, version);
+
+ return _contentsFolderSubDirPath(product, "SharedSupport", version);
+}
+
+/**
+ * Returns the directory containing resource files that are not specific to any given localization.
+ * @note Xcode equivalent: UNLOCALIZED_RESOURCES_FOLDER_PATH
+ */
+function unlocalizedResourcesFolderPath(product, version) {
+ if (isShallowBundle(product))
+ return contentsFolderPath(product, version);
+
+ return _contentsFolderSubDirPath(product, "Resources", version);
+}
+
+/**
+ * Returns the path to the bundle's version.plist file.
+ * @param version only used for framework bundles.
+ * @note Xcode equivalent: VERSIONPLIST_PATH
+ */
+function versionPlistPath(product, version) {
+ var path = (product.type.contains("frameworkbundle"))
+ ? unlocalizedResourcesFolderPath(product, version)
+ : contentsFolderPath(product, version);
+ return path + "/version.plist";
+}
+
+/**
+ * Returns the file extension of the bundle directory - app, framework, bundle, etc.
+ * @note Xcode equivalent: WRAPPER_EXTENSION
+ */
+function wrapperExtension(product) {
+ if (product.type.contains("applicationbundle")) {
+ return "app";
+ } else if (product.type.contains("frameworkbundle")) {
+ return "framework";
+ } else if (product.type.contains("inapppurchase")) {
+ return "";
+ } else if (product.type.contains("bundle")) {
+ // Potentially: kext, prefPane, qlgenerator, saver, mdimporter, or a custom extension
+ var bundleExtension = ModUtils.moduleProperty(product, "bundleExtension");
+
+ // default to bundle if none was specified by the user
+ return bundleExtension || "bundle";
+ } else {
+ throw ("Unsupported bundle product type " + product.type + ". "
+ + "Must be in {applicationbundle, frameworkbundle, bundle, inapppurchase}.");
+ }
+}
+
+/**
+ * Returns the name of the bundle directory - the product name plus the bundle extension.
+ * @note Xcode equivalent: WRAPPER_NAME
+ */
+function wrapperName(product) {
+ return productName(product) + wrapperSuffix(product);
+}
+
+/**
+ * Returns the suffix of the bundle directory, that is, its extension prefixed with a '.',
+ * or an empty string if the extension is also an empty string.
+ * @note Xcode equivalent: WRAPPER_SUFFIX
+ */
+function wrapperSuffix(product) {
+ var ext = wrapperExtension(product);
+ return ext ? ("." + ext) : "";
+}
+
+// Private helper functions
+
+/**
+ * In-App purchase content bundles use virtually no subfolders of Contents;
+ * this is a convenience method to avoid repeating that logic everywhere.
+ * @param version only used for framework bundles.
+ */
+function _contentsFolderSubDirPath(product, subdirectoryName, version) {
+ var path = contentsFolderPath(product, version);
+ if (!product.type.contains("inapppurchase"))
+ path += "/" + subdirectoryName;
+ return path;
+}
+
+/**
+ * Returns a list containing the filename of the bundle's main information
+ * property list and filename of the corresponding strings file.
+ */
+function _infoFileNames(product) {
+ if (product.type.contains("inapppurchase"))
+ return ["ContentInfo.plist", "ContentInfo.strings"];
+ else
+ return ["Info.plist", "InfoPlist.strings"];
+}
diff --git a/share/qbs/modules/cpp/darwin-tools.js b/share/qbs/imports/qbs/DarwinTools/darwin-tools.js
index ef99bc8ca..3c30aba50 100644
--- a/share/qbs/modules/cpp/darwin-tools.js
+++ b/share/qbs/imports/qbs/DarwinTools/darwin-tools.js
@@ -1,28 +1,30 @@
var FileInfo = loadExtension("qbs.FileInfo");
-// replace chars non safe for a domain name (rfc1034) with a "-"
-function rfc1034(inStr)
-{
- return inStr.replace(/[^-A-Za-z0-9.]/g,'-');
+/**
+ * Replace characters unsafe for use in a domain name with a '-' character (RFC 1034).
+ */
+function rfc1034(inStr) {
+ return inStr.replace(/[^-A-Za-z0-9.]/g, '-');
}
-// Returns the localization of a resource at the given path,
-// or undefined if the path does not contain an {xx}.lproj path segment
-function localizationKey(path)
-{
+/**
+ * Returns the localization of the resource at the given path,
+ * or undefined if the path does not contain an {xx}.lproj path segment.
+ */
+function localizationKey(path) {
return _resourceFileProperties(path)[0];
}
-// Returns the path of a localized resource at the given path,
-// relative to its containing {xx}.lproj directory, or '.'
-// if the resource is unlocalized (not contained in an lproj directory)
-function relativeResourcePath(path)
-{
+/**
+ * Returns the path of a localized resource at the given path,
+ * relative to its containing {xx}.lproj directory, or '.'
+ * if the resource is unlocalized (not contained in an lproj directory).
+ */
+function relativeResourcePath(path) {
return _resourceFileProperties(path)[1];
}
-function _resourceFileProperties(path)
-{
+function _resourceFileProperties(path) {
var lprojKey = ".lproj/";
var lproj = path.indexOf(lprojKey);
if (lproj >= 0) {
@@ -38,13 +40,15 @@ function _resourceFileProperties(path)
return [ undefined, '.' ];
}
-// perform replacements in env recursively
-// JSON.stringify(expandPlistEnvironmentVariables({a:"$(x)3$$(y)",b:{t:"%$(y) $(k)"}},
-// {x:"X",y:"Y"}, true))
-// Warning undefined variable k in variable expansion
-// => {"a":"X3$Y","b":{"t":"%Y $(k)"}}
-function expandPlistEnvironmentVariables(obj, env, warn)
-{
+/**
+ * Recursively perform variable replacements in an environment dictionary.
+ *
+ * JSON.stringify(expandPlistEnvironmentVariables({a:"$(x)3$$(y)",b:{t:"%$(y) $(k)"}},
+ * {x:"X",y:"Y"}, true))
+ * Warning undefined variable k in variable expansion
+ * => {"a":"X3$Y","b":{"t":"%Y $(k)"}}
+ */
+function expandPlistEnvironmentVariables(obj, env, warn) {
// Possible syntaxes for wrapping an environment variable name
var syntaxes = [
{"open": "${", "close": "}"},
@@ -52,8 +56,10 @@ function expandPlistEnvironmentVariables(obj, env, warn)
{"open": "@", "close": "@"}
];
- // Finds the first index of a replacement starting with one of the supported syntaxes
- // This is needed so we don't do recursive substitutions
+ /**
+ * Finds the first index of a replacement starting with one of the supported syntaxes
+ * This is needed so we don't do recursive substitutions
+ */
function indexOfReplacementStart(syntaxes, str, offset) {
var syntax;
var idx = str.length;
@@ -70,7 +76,7 @@ function expandPlistEnvironmentVariables(obj, env, warn)
function expandRecursive(obj, env, checked) {
checked.push(obj);
for (var key in obj) {
- var value =obj[key];
+ var value = obj[key];
var type = typeof(value);
if (type === "object") {
if (checked.indexOf(value) !== -1)
diff --git a/share/qbs/imports/qbs/FileInfo/fileinfo.js b/share/qbs/imports/qbs/FileInfo/fileinfo.js
index f04212f1e..fe672c546 100644
--- a/share/qbs/imports/qbs/FileInfo/fileinfo.js
+++ b/share/qbs/imports/qbs/FileInfo/fileinfo.js
@@ -1,9 +1,12 @@
+var _windowsAbsolutePathPattern = new RegExp("^[a-z,A-Z]:[/,\\\\]");
+var _removeDoubleSlashesPattern = new RegExp("/{2,}", "g");
+
function path(fp) {
if (fp === '/')
return fp;
// Yes, this will be wrong for "clever" unix users calling their directory 'c:'. Boohoo.
- if (fp.length === 3 && fp.slice(-2) === ':/')
+ if (fp.length === 3 && fp.slice(-2) === ":/")
return fp;
var last = fp.lastIndexOf('/');
@@ -27,17 +30,16 @@ function baseName(fph) {
function completeBaseName(fph) {
var fn = fileName(fph);
- var last = fn.lastIndexOf(".");
+ var last = fn.lastIndexOf('.');
if (last < 0)
return fn;
else
return fn.slice(0, last);
}
-function relativePath(base, rel)
-{
+function relativePath(base, rel) {
var basel = base.split('/');
- var rell = rel.split('/');
+ var rell = rel.split('/');
var i;
for (i = basel.length; i-- >= 0;) {
if (basel[i] === '.' || basel[i] === '')
@@ -56,7 +58,7 @@ function relativePath(base, rel)
var r = [];
for (; i < basel.length; i++)
- r.push('..');
+ r.push("..");
for (; j < rell.length; j++)
r.push(rell[j]);
@@ -64,34 +66,27 @@ function relativePath(base, rel)
return r.join('/');
}
-var windowsAbsolutePathPattern = new RegExp("^[a-z,A-Z]:[/,\\\\]")
-function isAbsolutePath(path)
-{
+function isAbsolutePath(path) {
if (!path)
return false;
- return (path.charAt(0) === '/' || windowsAbsolutePathPattern.test(path));
+ return (path.charAt(0) === '/' || _windowsAbsolutePathPattern.test(path));
}
-function toWindowsSeparators(str)
-{
+function toWindowsSeparators(str) {
return str.toString().replace(/\//g, '\\');
}
-function fromWindowsSeparators(str)
-{
+function fromWindowsSeparators(str) {
return str.toString().replace(/\\/g, '/');
}
-var removeDoubleSlashesPattern = new RegExp("/{2,}", "g")
-
-function joinPaths()
-{
+function joinPaths() {
function pathFilter(path) {
- return path && typeof path === 'string';
+ return path && typeof path === "string";
}
var paths = Array.prototype.slice.call(arguments, 0).filter(pathFilter);
var joinedPaths = paths.join('/');
- return joinedPaths.replace(removeDoubleSlashesPattern, "/")
+ return joinedPaths.replace(_removeDoubleSlashesPattern, '/');
}
diff --git a/share/qbs/imports/qbs/ModUtils/utils.js b/share/qbs/imports/qbs/ModUtils/utils.js
index 7b6310664..017155616 100644
--- a/share/qbs/imports/qbs/ModUtils/utils.js
+++ b/share/qbs/imports/qbs/ModUtils/utils.js
@@ -1,16 +1,11 @@
-//
-// utility functions for modules
-//
-
-/*!
- * Given a list of file tags, returns the file tag (one of [c, cpp, objc, objcpp])
- * corresponding to the C-family language the file should be compiled as.
- *
- * If no such tag is found, undefined is returned. If more than one match is
- * found, an exception is thrown.
- */
-function fileTagForTargetLanguage(fileTags)
-{
+/**
+ * Given a list of file tags, returns the file tag (one of [c, cpp, objc, objcpp])
+ * corresponding to the C-family language the file should be compiled as.
+ *
+ * If no such tag is found, undefined is returned. If more than one match is
+ * found, an exception is thrown.
+ */
+function fileTagForTargetLanguage(fileTags) {
var srcTags = ["c", "cpp", "objc", "objcpp", "asm", "asm_cpp"];
var pchTags = ["c_pch", "cpp_pch", "objc_pch", "objcpp_pch"];
@@ -34,45 +29,44 @@ function fileTagForTargetLanguage(fileTags)
return foundTagCount == 1 ? canonicalTag : undefined;
}
-/*
- * Returns the name of a language-specific property given the file tag
- * for that property, and the base property name.
- *
- * If \a fileTag is undefined, the language-agnostic property name is returned.
- *
- * \param propertyName flags, platformFlags, precompiledHeader
- * \param fileTag c, cpp, objc, objcpp
- */
-function languagePropertyName(propertyName, fileTag)
-{
+/**
+ * Returns the name of a language-specific property given the file tag
+ * for that property, and the base property name.
+ *
+ * If \a fileTag is undefined, the language-agnostic property name is returned.
+ *
+ * @param propertyName flags, platformFlags, precompiledHeader
+ * @param fileTag c, cpp, objc, objcpp
+ */
+function languagePropertyName(propertyName, fileTag) {
if (!fileTag)
- fileTag = 'common';
+ fileTag = "common";
var map = {
- 'c': {
- 'flags': 'cFlags',
- 'platformFlags': 'platformCFlags',
- 'precompiledHeader': 'cPrecompiledHeader'
+ "c": {
+ "flags": "cFlags",
+ "platformFlags": "platformCFlags",
+ "precompiledHeader": "cPrecompiledHeader"
},
- 'cpp': {
- 'flags': 'cxxFlags',
- 'platformFlags': 'platformCxxFlags',
- 'precompiledHeader': 'cxxPrecompiledHeader'
+ "cpp": {
+ "flags": "cxxFlags",
+ "platformFlags": "platformCxxFlags",
+ "precompiledHeader": "cxxPrecompiledHeader"
},
- 'objc': {
- 'flags': 'objcFlags',
- 'platformFlags': 'platformObjcFlags',
- 'precompiledHeader': 'objcPrecompiledHeader'
+ "objc": {
+ "flags": "objcFlags",
+ "platformFlags": "platformObjcFlags",
+ "precompiledHeader": "objcPrecompiledHeader"
},
- 'objcpp': {
- 'flags': 'objcxxFlags',
- 'platformFlags': 'platformObjcxxFlags',
- 'precompiledHeader': 'objcxxPrecompiledHeader'
+ "objcpp": {
+ "flags": "objcxxFlags",
+ "platformFlags": "platformObjcxxFlags",
+ "precompiledHeader": "objcxxPrecompiledHeader"
},
- 'common': {
- 'flags': 'commonCompilerFlags',
- 'platformFlags': 'platformCommonCompilerFlags',
- 'precompiledHeader': 'precompiledHeader'
+ "common": {
+ "flags": "commonCompilerFlags",
+ "platformFlags": "platformCommonCompilerFlags",
+ "precompiledHeader": "precompiledHeader"
}
};
@@ -83,93 +77,200 @@ function languagePropertyName(propertyName, fileTag)
return lang[propertyName] || propertyName;
}
-function moduleProperties(config, key, langFilter)
-{
+function moduleProperties(config, key, langFilter) {
return config.moduleProperties(config.moduleName, languagePropertyName(key, langFilter))
}
-function modulePropertiesFromArtifacts(product, artifacts, moduleName, propertyName, langFilter)
-{
+function modulePropertiesFromArtifacts(product, artifacts, moduleName, propertyName, langFilter) {
var result = product.moduleProperties(moduleName, languagePropertyName(propertyName, langFilter))
for (var i in artifacts)
result = result.concat(artifacts[i].moduleProperties(moduleName, languagePropertyName(propertyName, langFilter)))
return result
}
-function moduleProperty(product, propertyName, langFilter)
-{
+function moduleProperty(product, propertyName, langFilter) {
return product.moduleProperty(product.moduleName, languagePropertyName(propertyName, langFilter))
}
-function dumpProperty(key, value, level)
-{
- var indent = ''
- for (var k=0; k < level; ++k)
- indent += ' '
- print(indent + key + ': ' + value)
+function dumpProperty(key, value, level) {
+ var indent = "";
+ for (var k = 0; k < level; ++k)
+ indent += " ";
+ print(indent + key + ": " + value);
}
-function traverseObject(obj, func, level)
-{
+function traverseObject(obj, func, level) {
if (!level)
- level = 0
- var i, children = {}
+ level = 0;
+ var i, children = {};
for (i in obj) {
if (typeof(obj[i]) === "object" && !(obj[i] instanceof Array))
- children[i] = obj[i]
+ children[i] = obj[i];
else
- func.apply(this, [i, obj[i], level])
+ func.apply(this, [i, obj[i], level]);
}
- level++
+ level++;
for (i in children) {
- func.apply(this, [i, children[i], level - 1])
- traverseObject(children[i], func, level)
+ func.apply(this, [i, children[i], level - 1]);
+ traverseObject(children[i], func, level);
}
- level--
+ level--;
}
-function dumpObject(obj, description)
-{
+function dumpObject(obj, description) {
if (!description)
- description = 'object dump'
- print('+++++++++ ' + description + ' +++++++++')
- traverseObject(obj, dumpProperty)
+ description = "object dump";
+ print("+++++++++ " + description + " +++++++++");
+ traverseObject(obj, dumpProperty);
}
-
-//////////////////////////////////////////////////////////
-// The EnvironmentVariable class
-//
-function EnvironmentVariable(name, separator, convertPathSeparators)
-{
- if (!name)
- throw "EnvironmentVariable c'tor needs a name as first argument."
- this.name = name
- this.value = getEnv(name).toString()
- this.separator = separator || ''
- this.convertPathSeparators = convertPathSeparators || false
+function concatAll() {
+ var result = [];
+ for (var i = 0; i < arguments.length; ++i) {
+ var arg = arguments[i];
+ if (arg === undefined)
+ continue;
+ else if (arg instanceof Array)
+ result = result.concat(arg);
+ else
+ result.push(arg);
+ }
+ return result;
}
-EnvironmentVariable.prototype.prepend = function(v)
-{
- if (this.value.length > 0 && this.value.charAt(0) !== this.separator)
- this.value = this.separator + this.value
- if (this.convertPathSeparators)
- v = FileInfo.toWindowsSeparators(v)
- this.value = v + this.value
-}
+var EnvironmentVariable = (function () {
+ function EnvironmentVariable(name, separator, convertPathSeparators) {
+ if (!name)
+ throw "EnvironmentVariable c'tor needs a name as first argument.";
+ this.name = name;
+ this.value = getEnv(name).toString();
+ this.separator = separator || "";
+ this.convertPathSeparators = convertPathSeparators || false;
+ }
+ EnvironmentVariable.prototype.prepend = function (v) {
+ if (this.value.length > 0 && this.value.charAt(0) !== this.separator)
+ this.value = this.separator + this.value;
+ if (this.convertPathSeparators)
+ v = FileInfo.toWindowsSeparators(v);
+ this.value = v + this.value;
+ };
-EnvironmentVariable.prototype.append = function(v)
-{
- if (this.value.length > 0)
- this.value += this.separator
- if (this.convertPathSeparators)
- v = FileInfo.toWindowsSeparators(v)
- this.value += v
-}
+ EnvironmentVariable.prototype.append = function (v) {
+ if (this.value.length > 0)
+ this.value += this.separator;
+ if (this.convertPathSeparators)
+ v = FileInfo.toWindowsSeparators(v);
+ this.value += v;
+ };
-EnvironmentVariable.prototype.set = function()
-{
- putEnv(this.name, this.value)
-}
+ EnvironmentVariable.prototype.set = function () {
+ putEnv(this.name, this.value);
+ };
+ return EnvironmentVariable;
+})();
+
+var PropertyValidator = (function () {
+ function PropertyValidator(moduleName) {
+ this.requiredProperties = {};
+ this.propertyValidators = [];
+ if (!moduleName)
+ throw "PropertyValidator c'tor needs a module name as a first argument.";
+ this.moduleName = moduleName;
+ }
+ PropertyValidator.prototype.setRequiredProperty = function (propertyName, propertyValue, message) {
+ this.requiredProperties[propertyName] = { propertyValue: propertyValue, message: message };
+ };
+
+ PropertyValidator.prototype.addRangeValidator = function (propertyName, propertyValue, min, max, allowFloats) {
+ var message = [];
+ if (min !== undefined)
+ message.push(">= " + min);
+ if (max !== undefined)
+ message.push("<= " + max);
+
+ this.addCustomValidator(propertyName, propertyValue, function (value) {
+ if (typeof value !== "number")
+ return false;
+ if (!allowFloats && value % 1 !== 0)
+ return false;
+ if (min !== undefined && value < min)
+ return false;
+ if (max !== undefined && value > max)
+ return false;
+ return true;
+ }, "must be " + (!allowFloats ? "an integer " : "") + message.join(" and "));
+ };
+ PropertyValidator.prototype.addVersionValidator = function (propertyName, propertyValue, minComponents, maxComponents, allowSuffixes) {
+ if (minComponents !== undefined && (typeof minComponents !== "number" || minComponents % 1 !== 0 || minComponents < 1))
+ throw "minComponents must be at least 1";
+ if (maxComponents !== undefined && (typeof maxComponents !== "number" || maxComponents % 1 !== 0 || maxComponents < minComponents))
+ throw "maxComponents must be >= minComponents";
+
+ this.addCustomValidator(propertyName, propertyValue, function (value) {
+ if (typeof value !== "string")
+ return false;
+ return value && value.match("^[0-9]+(\\.[0-9]+){" + ((minComponents - 1) || 0) + "," + ((maxComponents - 1) || "") + "}" + (!allowSuffixes ? "$" : "")) !== null;
+ }, "must be a version number with " + minComponents + " to " + maxComponents + " components");
+ };
+
+ PropertyValidator.prototype.addCustomValidator = function (propertyName, propertyValue, validator, message) {
+ this.propertyValidators.push({
+ propertyName: propertyName,
+ propertyValue: propertyValue,
+ validator: validator,
+ message: message
+ });
+ };
+
+ PropertyValidator.prototype.validate = function (throwOnError) {
+ var i;
+ var lines;
+
+ // Find any missing properties
+ var missingProperties = {};
+ for (i in this.requiredProperties) {
+ var propValue = this.requiredProperties[i].propertyValue;
+ if (propValue === undefined || propValue === null || propValue === "") {
+ missingProperties[i] = this.requiredProperties[i];
+ }
+ }
+
+ // Find any properties that don't satisfy their validator function
+ var invalidProperties = {};
+ for (var j = 0; j < this.propertyValidators.length; ++j) {
+ var v = this.propertyValidators[j];
+ if (!v.validator(v.propertyValue)) {
+ invalidProperties[v.propertyName] = v.message;
+ }
+ }
+
+ var errorMessage = "";
+ if (Object.keys(missingProperties).length > 0) {
+ errorMessage += "The following properties are not set. Set them in your profile:\n";
+ lines = [];
+ for (i in missingProperties) {
+ var obj = missingProperties[i];
+ lines.push(this.moduleName + "." + i + ((obj && obj.message) ? (": " + obj.message) : ""));
+ }
+ errorMessage += lines.join("\n");
+ }
+
+ if (Object.keys(invalidProperties).length > 0) {
+ if (errorMessage)
+ errorMessage += "\n";
+ errorMessage += "The following properties have invalid values:\n";
+ lines = [];
+ for (i in invalidProperties) {
+ lines.push(this.moduleName + "." + i + ": " + invalidProperties[i]);
+ }
+ errorMessage += lines.join("\n");
+ }
+
+ if (throwOnError !== false && errorMessage.length > 0)
+ throw errorMessage;
+
+ return errorMessage.length == 0;
+ };
+ return PropertyValidator;
+})();
diff --git a/share/qbs/modules/cpp/path-tools.js b/share/qbs/imports/qbs/PathTools/path-tools.js
index 17df91af5..b6c25a555 100644
--- a/share/qbs/modules/cpp/path-tools.js
+++ b/share/qbs/imports/qbs/PathTools/path-tools.js
@@ -1,37 +1,32 @@
-var BundleTools = loadFile("bundle-tools.js");
+var BundleTools = loadExtension("qbs.BundleTools");
-function applicationFileName()
-{
- return ModUtils.moduleProperty(product, "executablePrefix")
+function applicationFileName(product) {
+ return product.moduleProperty("cpp", "executablePrefix")
+ product.targetName
- + ModUtils.moduleProperty(product, "executableSuffix");
+ + product.moduleProperty("cpp", "executableSuffix");
}
-function applicationFilePath()
-{
+function applicationFilePath(product) {
if (BundleTools.isBundleProduct(product))
return BundleTools.executablePath(product);
else
- return applicationFileName();
+ return applicationFileName(product);
}
-function staticLibraryFileName()
-{
- return ModUtils.moduleProperty(product, "staticLibraryPrefix")
+function staticLibraryFileName(product) {
+ return product.moduleProperty("cpp", "staticLibraryPrefix")
+ product.targetName
- + ModUtils.moduleProperty(product, "staticLibrarySuffix");
+ + product.moduleProperty("cpp", "staticLibrarySuffix");
}
-function staticLibraryFilePath()
-{
+function staticLibraryFilePath(product) {
if (BundleTools.isBundleProduct(product))
return BundleTools.executablePath(product);
else
- return staticLibraryFileName();
+ return staticLibraryFileName(product);
}
-function dynamicLibraryFileName(version, maxParts)
-{
+function dynamicLibraryFileName(product, version, maxParts) {
// If no override version was given, use the product's version
// We specifically want to differentiate between undefined and i.e.
// empty string as empty string should be taken to mean "no version"
@@ -48,7 +43,7 @@ function dynamicLibraryFileName(version, maxParts)
version = version.split('.').slice(0, maxParts).join('.');
// Start with prefix + name (i.e. libqbs, qbs)
- var fileName = ModUtils.moduleProperty(product, "dynamicLibraryPrefix") + product.targetName;
+ var fileName = product.moduleProperty("cpp", "dynamicLibraryPrefix") + product.targetName;
// For Darwin platforms, append the version number if there is one (i.e. libqbs.1.0.0)
var targetOS = product.moduleProperty("qbs", "targetOS");
@@ -58,7 +53,7 @@ function dynamicLibraryFileName(version, maxParts)
}
// Append the suffix (i.e. libqbs.1.0.0.dylib, libqbs.so, qbs.dll)
- fileName += ModUtils.moduleProperty(product, "dynamicLibrarySuffix");
+ fileName += product.moduleProperty("cpp", "dynamicLibrarySuffix");
// For non-Darwin Unix platforms, append the version number if there is one (i.e. libqbs.so.1.0.0)
if (version && targetOS.contains("unix") && !targetOS.contains("darwin"))
@@ -67,33 +62,44 @@ function dynamicLibraryFileName(version, maxParts)
return fileName;
}
-function dynamicLibraryFilePath(version, maxParts)
-{
+function dynamicLibraryFilePath(product, version, maxParts) {
if (BundleTools.isBundleProduct(product))
return BundleTools.executablePath(product, version);
else
- return dynamicLibraryFileName(version, maxParts);
+ return dynamicLibraryFileName(product, version, maxParts);
}
-function importLibraryFilePath()
-{
- return ModUtils.moduleProperty(product, "dynamicLibraryPrefix")
+function importLibraryFilePath(product) {
+ return product.moduleProperty("cpp", "dynamicLibraryPrefix")
+ product.targetName
- + ModUtils.moduleProperty(product, "dynamicLibraryImportSuffix");
+ + product.moduleProperty("cpp", "dynamicLibraryImportSuffix");
}
// DWARF_DSYM_FILE_NAME
// Filename of the target's corresponding dSYM file
-function dwarfDsymFileName()
-{
+function dwarfDsymFileName(product) {
if (BundleTools.isBundleProduct(product))
return BundleTools.wrapperName(product) + ".dSYM";
else if (product.type.contains("application"))
- return applicationFileName() + ".dSYM";
+ return applicationFileName(product) + ".dSYM";
else if (product.type.contains("dynamiclibrary"))
- return dynamicLibraryFileName() + ".dSYM";
+ return dynamicLibraryFileName(product) + ".dSYM";
else if (product.type.contains("staticlibrary"))
- return staticLibraryFileName() + ".dSYM";
+ return staticLibraryFileName(product) + ".dSYM";
else
return product.targetName + ".dSYM";
}
+
+// Returns whether the string looks like a library filename
+function isLibraryFileName(product, fileName, prefix, suffixes, isShared) {
+ var suffix, i;
+ var os = product.moduleProperty("qbs", "targetOS");
+ for (i = 0; i < suffixes.length; ++i) {
+ suffix = suffixes[i];
+ if (isShared && os.contains("unix") && !os.contains("darwin"))
+ suffix += "(\\.[0-9]+){0,3}";
+ if (fileName.match("^" + prefix + ".+?\\" + suffix + "$"))
+ return true;
+ }
+ return false;
+}
diff --git a/share/qbs/imports/qbs/Probes/PathProbe.qbs b/share/qbs/imports/qbs/Probes/PathProbe.qbs
index 26871dc22..943007792 100644
--- a/share/qbs/imports/qbs/Probes/PathProbe.qbs
+++ b/share/qbs/imports/qbs/Probes/PathProbe.qbs
@@ -1,7 +1,7 @@
import qbs 1.0
import qbs.File
import qbs.FileInfo
-import "utils.js" as Utils
+import qbs.ModUtils
Probe {
// Inputs
@@ -21,19 +21,19 @@ Probe {
configure: {
if (!names)
throw '"names" must be specified';
- var _names = Utils.concatAll(names);
+ var _names = ModUtils.concatAll(names);
if (nameFilter)
_names = _names.map(nameFilter);
// FIXME: Suggest how to obtain paths from system
- var _paths = Utils.concatAll(pathPrefixes, platformPaths);
+ var _paths = ModUtils.concatAll(pathPrefixes, platformPaths);
// FIXME: Add getenv support
- var envs = Utils.concatAll(platformEnvironmentPaths, environmentPaths);
+ var envs = ModUtils.concatAll(platformEnvironmentPaths, environmentPaths);
for (var i = 0; i < envs.length; ++i) {
var value = qbs.getEnv(envs[i]) || '';
if (value.length > 0)
_paths = _paths.concat(value.split(qbs.pathListSeparator));
}
- var _suffixes = Utils.concatAll('', pathSuffixes);
+ var _suffixes = ModUtils.concatAll('', pathSuffixes);
for (i = 0; i < _names.length; ++i) {
for (var j = 0; j < _paths.length; ++j) {
for (var k = 0; k < _suffixes.length; ++k) {
diff --git a/share/qbs/imports/qbs/Probes/PkgConfigProbe.qbs b/share/qbs/imports/qbs/Probes/PkgConfigProbe.qbs
index 07509034d..7eafc355c 100644
--- a/share/qbs/imports/qbs/Probes/PkgConfigProbe.qbs
+++ b/share/qbs/imports/qbs/Probes/PkgConfigProbe.qbs
@@ -1,7 +1,6 @@
import qbs 1.0
import qbs.Process
import qbs.FileInfo
-import "utils.js" as Utils
Probe {
// Inputs
diff --git a/share/qbs/imports/qbs/Probes/utils.js b/share/qbs/imports/qbs/Probes/utils.js
deleted file mode 100644
index f8b7e28c5..000000000
--- a/share/qbs/imports/qbs/Probes/utils.js
+++ /dev/null
@@ -1,18 +0,0 @@
-//
-// utility functions for probes
-//
-
-function concatAll()
-{
- var result = [];
- for (var i = 0; i < arguments.length; ++i) {
- var arg = arguments[i];
- if (arg === undefined)
- continue;
- else if (arg instanceof Array)
- result = result.concat(arg);
- else
- result.push(arg);
- }
- return result;
-}
diff --git a/share/qbs/imports/qbs/UnixUtils/unix-utils.js b/share/qbs/imports/qbs/UnixUtils/unix-utils.js
new file mode 100644
index 000000000..261d1bb5b
--- /dev/null
+++ b/share/qbs/imports/qbs/UnixUtils/unix-utils.js
@@ -0,0 +1,15 @@
+function soname(product, outputFileName) {
+ function majorVersion(version, defaultValue) {
+ var n = parseInt(version, 10);
+ return isNaN(n) ? defaultValue : n;
+ }
+
+ if (product.version) {
+ var major = majorVersion(product.version);
+ if (major) {
+ return outputFileName.substr(0, outputFileName.length - product.version.length)
+ + major;
+ }
+ }
+ return outputFileName;
+}
diff --git a/share/qbs/modules/cpp/windows.js b/share/qbs/imports/qbs/WindowsUtils/windows-utils.js
index 35414eed7..35414eed7 100644
--- a/share/qbs/modules/cpp/windows.js
+++ b/share/qbs/imports/qbs/WindowsUtils/windows-utils.js
diff --git a/share/qbs/imports/qbs/base/NodeJSApplication.qbs b/share/qbs/imports/qbs/base/NodeJSApplication.qbs
new file mode 100644
index 000000000..d51024bdc
--- /dev/null
+++ b/share/qbs/imports/qbs/base/NodeJSApplication.qbs
@@ -0,0 +1,3 @@
+Product {
+ Depends { name: "nodejs" }
+}
diff --git a/share/qbs/modules/cpp/DarwinGCC.qbs b/share/qbs/modules/cpp/DarwinGCC.qbs
index 181e5902e..fc496a1bb 100644
--- a/share/qbs/modules/cpp/DarwinGCC.qbs
+++ b/share/qbs/modules/cpp/DarwinGCC.qbs
@@ -1,12 +1,12 @@
import qbs 1.0
+import qbs.BundleTools
+import qbs.DarwinTools
import qbs.File
+import qbs.PathTools
import qbs.Process
import qbs.PropertyList
import qbs.TextFile
import qbs.ModUtils
-import "bundle-tools.js" as BundleTools
-import "darwin-tools.js" as DarwinTools
-import 'path-tools.js' as PathTools
UnixGCC {
condition: false
@@ -16,10 +16,10 @@ UnixGCC {
validate: {
if (qbs.sysroot) {
- if (!xcodeSdkName)
- throw "cpp.xcodeSdkName not set. Set cpp.xcodeSdkName in your profile.";
- if (!xcodeSdkVersion)
- throw "cpp.xcodeSdkVersion not set. Set cpp.xcodeSdkVersion in your profile.";
+ var validator = new ModUtils.PropertyValidator("cpp");
+ validator.setRequiredProperty("xcodeSdkName", xcodeSdkName);
+ validator.setRequiredProperty("xcodeSdkVersion", xcodeSdkVersion);
+ validator.validate();
}
}
@@ -318,7 +318,7 @@ UnixGCC {
inputs: ["application"]
Artifact {
- fileName: product.destinationDirectory + "/" + PathTools.dwarfDsymFileName()
+ fileName: product.destinationDirectory + "/" + PathTools.dwarfDsymFileName(product)
fileTags: ["application_dsym"]
}
diff --git a/share/qbs/modules/cpp/GenericGCC.qbs b/share/qbs/modules/cpp/GenericGCC.qbs
index 342a6b70c..d20b69b35 100644
--- a/share/qbs/modules/cpp/GenericGCC.qbs
+++ b/share/qbs/modules/cpp/GenericGCC.qbs
@@ -2,9 +2,11 @@ import qbs 1.0
import qbs.File
import qbs.FileInfo
import qbs.ModUtils
+import qbs.PathTools
import qbs.Process
+import qbs.UnixUtils
+import qbs.WindowsUtils
import 'gcc.js' as Gcc
-import 'path-tools.js' as PathTools
CppModule {
condition: false
@@ -67,32 +69,32 @@ CppModule {
usings: ["dynamiclibrary_copy", "staticlibrary", "frameworkbundle"]
Artifact {
- fileName: product.destinationDirectory + "/" + PathTools.dynamicLibraryFilePath()
+ fileName: product.destinationDirectory + "/" + PathTools.dynamicLibraryFilePath(product)
fileTags: ["dynamiclibrary"]
}
// libfoo
Artifact {
- fileName: product.destinationDirectory + "/" + PathTools.dynamicLibraryFileName(undefined, 0)
+ fileName: product.destinationDirectory + "/" + PathTools.dynamicLibraryFileName(product, undefined, 0)
fileTags: ["dynamiclibrary_symlink"]
}
// libfoo.1
Artifact {
- fileName: product.destinationDirectory + "/" + PathTools.dynamicLibraryFileName(undefined, 1)
+ fileName: product.destinationDirectory + "/" + PathTools.dynamicLibraryFileName(product, undefined, 1)
fileTags: ["dynamiclibrary_symlink"]
}
// libfoo.1.0
Artifact {
- fileName: product.destinationDirectory + "/" + PathTools.dynamicLibraryFileName(undefined, 2)
+ fileName: product.destinationDirectory + "/" + PathTools.dynamicLibraryFileName(product, undefined, 2)
fileTags: ["dynamiclibrary_symlink"]
}
// Copy of dynamic lib for smart re-linking.
Artifact {
fileName: product.destinationDirectory + "/.socopy/"
- + PathTools.dynamicLibraryFilePath()
+ + PathTools.dynamicLibraryFilePath(product)
fileTags: ["dynamiclibrary_copy"]
alwaysUpdated: false
cpp.transitiveSOs: {
@@ -110,7 +112,7 @@ CppModule {
prepare: {
// Actual linker command.
- var libFilePath = outputs["dynamiclibrary"][0].filePath;
+ var lib = outputs["dynamiclibrary"][0];
var platformLinkerFlags = ModUtils.moduleProperties(product, 'platformLinkerFlags');
var linkerFlags = ModUtils.moduleProperties(product, 'linkerFlags');
var commands = [];
@@ -121,13 +123,13 @@ CppModule {
args = args.concat([
'-Wl,--hash-style=gnu',
'-Wl,--as-needed',
- '-Wl,-soname=' + Gcc.soname(product, libFilePath)
+ '-Wl,-soname=' + UnixUtils.soname(product, lib.fileName)
]);
} else if (product.moduleProperty("qbs", "targetOS").contains('darwin')) {
var installNamePrefix = product.moduleProperty("cpp", "installNamePrefix");
if (installNamePrefix !== undefined)
args.push("-Wl,-install_name,"
- + installNamePrefix + FileInfo.fileName(libFilePath));
+ + installNamePrefix + lib.fileName);
args.push("-Wl,-headerpad_max_install_names");
}
args = args.concat(platformLinkerFlags);
@@ -144,11 +146,11 @@ CppModule {
}
args.push('-o');
- args.push(libFilePath);
+ args.push(lib.filePath);
args = args.concat(Gcc.linkerFlags(product, inputs));
args = args.concat(Gcc.additionalCompilerAndLinkerFlags(product));
var cmd = new Command(ModUtils.moduleProperty(product, "linkerPath"), args);
- cmd.description = 'linking ' + FileInfo.fileName(libFilePath);
+ cmd.description = 'linking ' + lib.fileName;
cmd.highlight = 'linker';
cmd.responseFileUsagePrefix = '@';
commands.push(cmd);
@@ -205,12 +207,12 @@ CppModule {
var links = outputs["dynamiclibrary_symlink"];
var symlinkCount = links.length;
for (var i = 0; i < symlinkCount; ++i) {
- cmd = new Command("ln", ["-sf", FileInfo.fileName(libFilePath),
+ cmd = new Command("ln", ["-sf", lib.fileName,
links[i].filePath]);
cmd.highlight = "filegen";
cmd.description = "creating symbolic link '"
- + FileInfo.fileName(links[i].filePath) + "'";
- cmd.workingDirectory = FileInfo.path(libFilePath);
+ + links[i].fileName + "'";
+ cmd.workingDirectory = FileInfo.path(lib.filePath);
commands.push(cmd);
}
}
@@ -225,7 +227,7 @@ CppModule {
usings: ["dynamiclibrary", "staticlibrary", "frameworkbundle"]
Artifact {
- fileName: product.destinationDirectory + "/" + PathTools.staticLibraryFilePath()
+ fileName: product.destinationDirectory + "/" + PathTools.staticLibraryFilePath(product)
fileTags: ["staticlibrary"]
cpp.staticLibraries: {
var result = []
@@ -250,7 +252,7 @@ CppModule {
for (var i in inputs.obj)
args.push(inputs.obj[i].filePath);
var cmd = new Command(ModUtils.moduleProperty(product, "archiverPath"), args);
- cmd.description = 'creating ' + FileInfo.fileName(output.filePath);
+ cmd.description = 'creating ' + output.fileName;
cmd.highlight = 'linker'
cmd.responseFileUsagePrefix = '@';
return cmd;
@@ -271,7 +273,7 @@ CppModule {
usings: ["dynamiclibrary_copy", "staticlibrary", "frameworkbundle"]
Artifact {
- fileName: product.destinationDirectory + "/" + PathTools.applicationFilePath()
+ fileName: product.destinationDirectory + "/" + PathTools.applicationFilePath(product)
fileTags: ["application"]
}
@@ -300,7 +302,7 @@ CppModule {
var minimumWindowsVersion = ModUtils.moduleProperty(product, "minimumWindowsVersion");
if (minimumWindowsVersion) {
- var subsystemVersion = Windows.getWindowsVersionInFormat(minimumWindowsVersion, 'subsystem');
+ var subsystemVersion = WindowsUtils.getWindowsVersionInFormat(minimumWindowsVersion, 'subsystem');
if (subsystemVersion) {
var major = subsystemVersion.split('.')[0];
var minor = subsystemVersion.split('.')[1];
@@ -325,7 +327,7 @@ CppModule {
args = args.concat(Gcc.linkerFlags(product, inputs));
args = args.concat(Gcc.additionalCompilerAndLinkerFlags(product));
var cmd = new Command(ModUtils.moduleProperty(product, "linkerPath"), args);
- cmd.description = 'linking ' + FileInfo.fileName(output.filePath);
+ cmd.description = 'linking ' + output.fileName;
cmd.highlight = 'linker'
cmd.responseFileUsagePrefix = '@';
return cmd;
@@ -340,7 +342,7 @@ CppModule {
Artifact {
fileTags: ["obj"]
- fileName: ".obj/" + product.name + "/" + input.baseDir + "/" + input.fileName + ".o"
+ fileName: ".obj/" + input.baseDir + "/" + input.fileName + ".o"
}
prepare: {
diff --git a/share/qbs/modules/cpp/bundle-tools.js b/share/qbs/modules/cpp/bundle-tools.js
deleted file mode 100644
index dca1b2c51..000000000
--- a/share/qbs/modules/cpp/bundle-tools.js
+++ /dev/null
@@ -1,330 +0,0 @@
-// NOTE: QBS and Xcode's "target" and "product" names are reversed
-
-var PropertyList = loadExtension("qbs.PropertyList");
-
-function isBundleProduct(product)
-{
- return product.type.contains("applicationbundle")
- || product.type.contains("frameworkbundle")
- || product.type.contains("bundle")
- || product.type.contains("inapppurchase");
-}
-
-// Returns the package creator code for the given product based on its type
-function packageType(product)
-{
- if (product.type.contains("application") || product.type.contains("applicationbundle"))
- return 'APPL';
- else if (product.type.contains("frameworkbundle"))
- return 'FMWK';
- else if (product.type.contains("bundle"))
- return 'BNDL';
-
- throw ("Unsupported product type " + product.type + ". "
- + "Must be in {application, applicationbundle, frameworkbundle, bundle}.");
-}
-
-function infoPlistContents(infoPlistFilePath)
-{
- if (infoPlistFilePath === undefined)
- return undefined;
-
- var plist = new PropertyList();
- try {
- plist.readFromFile(infoPlistFilePath);
- return JSON.parse(plist.toJSONString());
- } finally {
- plist.clear();
- }
-}
-
-function infoPlistFormat(infoPlistFilePath)
-{
- if (infoPlistFilePath === undefined)
- return undefined;
-
- var plist = new PropertyList();
- try {
- plist.readFromFile(infoPlistFilePath);
- return plist.format();
- } finally {
- plist.clear();
- }
-}
-
-// CONTENTS_FOLDER_PATH
-// Main bundle directory
-// the version parameter is only used for framework bundles
-function contentsFolderPath(product, version)
-{
- var path = wrapperName(product);
-
- if (product.type.contains("frameworkbundle"))
- path += "/Versions/" + (version || frameworkVersion(product));
- else if (!isShallowBundle(product))
- path += "/Contents";
-
- return path;
-}
-
-// DOCUMENTATION_FOLDER_PATH
-// Directory for documentation files
-// the version parameter is only used for framework bundles
-function documentationFolderPath(product, localizationName, version)
-{
- var path = localizedResourcesFolderPath(product, localizationName, version);
- if (!product.type.contains("inapppurchase"))
- path += "/Documentation";
- return path;
-}
-
-// EXECUTABLES_FOLDER_PATH
-// Destination directory for auxiliary executables
-// the version parameter is only used for framework bundles
-function executablesFolderPath(product, localizationName, version)
-{
- if (product.type.contains("frameworkbundle"))
- return localizedResourcesFolderPath(product, localizationName, version);
- else
- return _contentsFolderSubDirPath(product, "Executables", version);
-}
-
-// EXECUTABLE_FOLDER_PATH
-// Destination directory for the primary executable
-// the version parameter is only used for framework bundles
-function executableFolderPath(product, version)
-{
- var path = contentsFolderPath(product, version);
- if (!isShallowBundle(product)
- && !product.type.contains("frameworkbundle")
- && !product.type.contains("inapppurchase"))
- path += "/MacOS";
-
- return path;
-}
-
-// EXECUTABLE_PATH
-// Path to the bundle's primary executable file
-// the version parameter is only used for framework bundles
-function executablePath(product, version)
-{
- return executableFolderPath(product, version) + "/" + productName(product);
-}
-
-// FRAMEWORK_VERSION
-// Major version number or letter corresponding to the bundle version
-function frameworkVersion(product)
-{
- if (!product.type.contains("frameworkbundle"))
- throw "Product type must be a frameworkbundle, was " + product.type;
-
- var n = parseInt(product.version, 10);
- return isNaN(n) ? 'A' : n;
-}
-
-// FRAMEWORKS_FOLDER_PATH
-// Directory containing frameworks used by the bundle's executables
-// the version parameter is only used for framework bundles
-function frameworksFolderPath(product, version)
-{
- return _contentsFolderSubDirPath(product, "Frameworks", version);
-}
-
-// INFOPLIST_PATH
-// Path to the bundle's main information property list
-// the version parameter is only used for framework bundles
-function infoPlistPath(product, version)
-{
- var path;
- if (product.type.contains("application"))
- path = ".tmp/" + product.name;
- else if (product.type.contains("frameworkbundle"))
- path = unlocalizedResourcesFolderPath(product, version);
- else if (product.type.contains("inapppurchase"))
- path = wrapperName(product);
- else
- path = contentsFolderPath(product, version);
-
- return path + "/" + _infoFileNames(product)[0];
-}
-
-// INFOSTRINGS_PATH
-// Path to the strings file corresponding to the bundle's main information property list
-// the version parameter is only used for framework bundles
-function infoStringsPath(product, localizationName, version)
-{
- return localizedResourcesFolderPath(product, localizationName, version) + "/" + _infoFileNames(product)[1];
-}
-
-// LOCALIZED_RESOURCES_FOLDER_PATH
-// Path to the bundle's resources directory for the given localization
-// the version parameter is only used for framework bundles
-function localizedResourcesFolderPath(product, localizationName, version)
-{
- if (typeof localizationName !== "string")
- throw("'" + localizationName + "' is not a valid localization name");
-
- return unlocalizedResourcesFolderPath(product, version) + "/" + localizationName + ".lproj";
-}
-
-// PKGINFO_PATH
-// Path to the bundle's PkgInfo file
-function pkgInfoPath(product)
-{
- var path = (product.type.contains("frameworkbundle"))
- ? wrapperName(product)
- : contentsFolderPath(product);
- return path + "/PkgInfo";
-}
-
-// PLUGINS_FOLDER_PATH
-// Directory containing plugins used by the bundle's executables
-// the version parameter is only used for framework bundles
-function pluginsFolderPath(product, version)
-{
- if (product.type.contains("frameworkbundle"))
- return unlocalizedResourcesFolderPath(product, version);
-
- return _contentsFolderSubDirPath(product, "PlugIns", version);
-}
-
-// PRIVATE_HEADERS_FOLDER_PATH
-// Directory containing private header files for the framework
-// the version parameter is only used for framework bundles
-function privateHeadersFolderPath(product, version)
-{
- return _contentsFolderSubDirPath(product, "PrivateHeaders", version);
-}
-
-// PRODUCT_NAME
-// The name of the product (in Xcode terms) which corresponds to the target name in QBS terms
-function productName(product)
-{
- return product.targetName;
-}
-
-// PUBLIC_HEADERS_FOLDER_PATH
-// Directory containing public header files for the framework
-// the version parameter is only used for framework bundles
-function publicHeadersFolderPath(product, version)
-{
- return _contentsFolderSubDirPath(product, "Headers", version);
-}
-
-// SCRIPTS_FOLDER_PATH
-// Directory containing script files associated with the bundle
-// the version parameter is only used for framework bundles
-function scriptsFolderPath(product, version)
-{
- return unlocalizedResourcesFolderPath(product, version) + "/Scripts";
-}
-
-// SHALLOW_BUNDLE
-// Controls the presence or absence of the Contents, MacOS and Resources folders
-// iOS tends to store the majority of files in its bundles in the main directory
-function isShallowBundle(product)
-{
- return product.moduleProperty("qbs", "targetOS").contains("ios")
- && product.type.contains("applicationbundle");
-}
-
-// SHARED_FRAMEWORKS_FOLDER_PATH
-// Directory containing sub-frameworks that may be shared with other applications
-// the version parameter is only used for framework bundles
-function sharedFrameworksFolderPath(product, version)
-{
- return _contentsFolderSubDirPath(product, "SharedFrameworks", version);
-}
-
-// SHARED_SUPPORT_FOLDER_PATH
-// Directory containing supporting files that may be shared with other applications
-// the version parameter is only used for framework bundles
-function sharedSupportFolderPath(product, version)
-{
- if (product.type.contains("frameworkbundle"))
- return unlocalizedResourcesFolderPath(product, version);
-
- return _contentsFolderSubDirPath(product, "SharedSupport", version);
-}
-
-// UNLOCALIZED_RESOURCES_FOLDER_PATH
-// Directory containing resource files that are not specific to any given localization
-function unlocalizedResourcesFolderPath(product, version)
-{
- if (isShallowBundle(product))
- return contentsFolderPath(product, version);
-
- return _contentsFolderSubDirPath(product, "Resources", version);
-}
-
-// VERSIONPLIST_PATH
-// Directory containing the bundle's version.plist file
-// the version parameter is only used for framework bundles
-function versionPlistPath(product, version)
-{
- var path = (product.type.contains("frameworkbundle"))
- ? unlocalizedResourcesFolderPath(product, version)
- : contentsFolderPath(product, version);
- return path + "/version.plist";
-}
-
-// WRAPPER_EXTENSION
-// The file extension of the bundle directory - app, framework, bundle, etc.
-function wrapperExtension(product)
-{
- if (product.type.contains("applicationbundle")) {
- return "app";
- } else if (product.type.contains("frameworkbundle")) {
- return "framework";
- } else if (product.type.contains("inapppurchase")) {
- return "";
- } else if (product.type.contains("bundle")) {
- // Potentially: kext, prefPane, qlgenerator, saver, mdimporter, or a custom extension
- var bundleExtension = ModUtils.moduleProperty(product, "bundleExtension");
-
- // default to bundle if none was specified by the user
- return bundleExtension || "bundle";
- } else {
- throw ("Unsupported bundle product type " + product.type + ". "
- + "Must be in {applicationbundle, frameworkbundle, bundle, inapppurchase}.");
- }
-}
-
-// WRAPPER_NAME
-// The name of the bundle directory - the product name plus the bundle extension
-function wrapperName(product)
-{
- return productName(product) + wrapperSuffix(product);
-}
-
-// WRAPPER_SUFFIX
-// The suffix of the bundle directory, that is, its extension prefixed with a '.',
-// or an empty string if the extension is also an empty string
-function wrapperSuffix(product)
-{
- var ext = wrapperExtension(product);
- return ext ? ("." + ext) : "";
-}
-
-// Private helper functions
-
-// In-App purchase content bundles use virtually no subfolders of Contents;
-// this is a convenience method to avoid repeating that logic everywhere
-// the version parameter is only used for framework bundles
-function _contentsFolderSubDirPath(product, subdirectoryName, version)
-{
- var path = contentsFolderPath(product, version);
- if (!product.type.contains("inapppurchase"))
- path += "/" + subdirectoryName;
- return path;
-}
-
-// Returns a list containing the filename of the bundle's main information
-// property list and filename of the corresponding strings file
-function _infoFileNames(product)
-{
- if (product.type.contains("inapppurchase"))
- return ["ContentInfo.plist", "ContentInfo.strings"];
- else
- return ["Info.plist", "InfoPlist.strings"];
-}
diff --git a/share/qbs/modules/cpp/gcc.js b/share/qbs/modules/cpp/gcc.js
index 6fd5c49de..9045da4e6 100644
--- a/share/qbs/modules/cpp/gcc.js
+++ b/share/qbs/modules/cpp/gcc.js
@@ -1,7 +1,7 @@
-var Windows = loadFile("windows.js");
+var PathTools = loadExtension("qbs.PathTools");
+var WindowsUtils = loadExtension("qbs.WindowsUtils");
-function linkerFlags(product, inputs)
-{
+function linkerFlags(product, inputs) {
var libraryPaths = ModUtils.moduleProperties(product, 'libraryPaths');
var dynamicLibraries = ModUtils.moduleProperties(product, "dynamicLibraries");
var staticLibraries = ModUtils.modulePropertiesFromArtifacts(product, inputs.staticlibrary, 'cpp', 'staticLibraries');
@@ -38,8 +38,8 @@ function linkerFlags(product, inputs)
prefix = ModUtils.moduleProperty(product, "staticLibraryPrefix");
suffixes = ModUtils.moduleProperty(product, "supportedStaticLibrarySuffixes");
for (i in staticLibraries) {
- if (isLibraryFileName(product, FileInfo.fileName(staticLibraries[i]), prefix, suffixes,
- false)) {
+ if (PathTools.isLibraryFileName(product, FileInfo.fileName(staticLibraries[i]), prefix,
+ suffixes, false)) {
args.push(staticLibraries[i]);
} else {
args.push('-l' + staticLibraries[i]);
@@ -49,8 +49,8 @@ function linkerFlags(product, inputs)
prefix = ModUtils.moduleProperty(product, "dynamicLibraryPrefix");
suffix = ModUtils.moduleProperty(product, "dynamicLibrarySuffix");
for (i in dynamicLibraries) {
- if (isLibraryFileName(product, FileInfo.fileName(dynamicLibraries[i]), prefix, [suffix],
- true)) {
+ if (PathTools.isLibraryFileName(product, FileInfo.fileName(dynamicLibraries[i]), prefix,
+ [suffix], true)) {
args.push(dynamicLibraries[i]);
} else {
args.push('-l' + dynamicLibraries[i]);
@@ -92,21 +92,6 @@ function linkerFlags(product, inputs)
return args;
}
-// Returns whether the string looks like a library filename
-function isLibraryFileName(product, fileName, prefix, suffixes, isShared)
-{
- var suffix, i;
- var os = product.moduleProperty("qbs", "targetOS");
- for (i = 0; i < suffixes.length; ++i) {
- suffix = suffixes[i];
- if (isShared && os.contains("unix") && !os.contains("darwin"))
- suffix += "(\\.[0-9]+){0,3}";
- if (fileName.match("^" + prefix + ".+?\\" + suffix + "$"))
- return true;
- }
- return false;
-}
-
// for compiler AND linker
function configFlags(config) {
var args = [];
@@ -143,15 +128,9 @@ function configFlags(config) {
return args;
}
-function removePrefixAndSuffix(str, prefix, suffix)
-{
- return str.substr(prefix.length, str.length - prefix.length - suffix.length);
-}
-
// ### what we actually need here is something like product.usedFileTags
// that contains all fileTags that have been used when applying the rules.
-function additionalCompilerFlags(product, input, output)
-{
+function additionalCompilerFlags(product, input, output) {
var includePaths = ModUtils.moduleProperties(input, 'includePaths');
var frameworkPaths = ModUtils.moduleProperties(product, 'frameworkPaths');
var systemIncludePaths = ModUtils.moduleProperties(input, 'systemIncludePaths');
@@ -212,7 +191,7 @@ function additionalCompilerFlags(product, input, output)
var minimumWindowsVersion = ModUtils.moduleProperty(product, "minimumWindowsVersion");
if (minimumWindowsVersion && product.moduleProperty("qbs", "targetOS").contains("windows")) {
- var hexVersion = Windows.getWindowsVersionInFormat(minimumWindowsVersion, 'hex');
+ var hexVersion = WindowsUtils.getWindowsVersionInFormat(minimumWindowsVersion, 'hex');
if (hexVersion) {
var versionDefs = [ 'WINVER', '_WIN32_WINNT', '_WIN32_WINDOWS' ];
for (i in versionDefs)
@@ -247,28 +226,8 @@ function additionalCompilerAndLinkerFlags(product) {
return args
}
-function majorVersion(version, defaultValue)
-{
- var n = parseInt(product.version, 10);
- return isNaN(n) ? defaultValue : n;
-}
-
-function soname(product, outputFilePath)
-{
- var outputFileName = FileInfo.fileName(outputFilePath);
- if (product.version) {
- var major = majorVersion(product.version);
- if (major) {
- return outputFileName.substr(0, outputFileName.length - product.version.length)
- + major;
- }
- }
- return outputFileName;
-}
-
// Returns the GCC language name equivalent to fileTag, accepted by the -x argument
-function languageName(fileTag)
-{
+function languageName(fileTag) {
if (fileTag === 'c')
return 'c';
else if (fileTag === 'cpp')
@@ -283,8 +242,7 @@ function languageName(fileTag)
return 'assembler-with-cpp';
}
-function prepareCompiler(project, product, inputs, outputs, input, output)
-{
+function prepareCompiler(project, product, inputs, outputs, input, output) {
var i, c;
// Determine which C-language we're compiling
@@ -342,7 +300,7 @@ function prepareCompiler(project, product, inputs, outputs, input, output)
}
var cmd = new Command(compilerPath, args);
- cmd.description = (pchOutput ? 'pre' : '') + 'compiling ' + FileInfo.fileName(input.filePath);
+ cmd.description = (pchOutput ? 'pre' : '') + 'compiling ' + input.fileName;
if (pchOutput)
cmd.description += ' (' + tag + ')';
cmd.highlight = "compiler";
@@ -351,13 +309,11 @@ function prepareCompiler(project, product, inputs, outputs, input, output)
}
// Concatenates two arrays of library names and preserves the dependency order that ld needs.
-function concatLibs(libs, deplibs)
-{
+function concatLibs(libs, deplibs) {
var r = [];
var s = {};
- function addLibs(lst)
- {
+ function addLibs(lst) {
for (var i = lst.length; --i >= 0;) {
var lib = lst[i];
if (!s[lib]) {
diff --git a/share/qbs/modules/cpp/ios-gcc.qbs b/share/qbs/modules/cpp/ios-gcc.qbs
index fae3a21fa..aeb1ad767 100644
--- a/share/qbs/modules/cpp/ios-gcc.qbs
+++ b/share/qbs/modules/cpp/ios-gcc.qbs
@@ -1,8 +1,8 @@
import qbs 1.0
+import qbs.BundleTools
+import qbs.DarwinTools
import qbs.File
import qbs.ModUtils
-import 'darwin-tools.js' as DarwinTools
-import 'bundle-tools.js' as BundleTools
DarwinGCC {
condition: qbs.hostOS.contains('osx') && qbs.targetOS.contains('ios') && qbs.toolchain.contains('gcc')
diff --git a/share/qbs/modules/cpp/msvc.js b/share/qbs/modules/cpp/msvc.js
index 7ae2e6d55..3eb805556 100644
--- a/share/qbs/modules/cpp/msvc.js
+++ b/share/qbs/modules/cpp/msvc.js
@@ -1,7 +1,4 @@
-var Windows = loadFile("windows.js");
-
-function prepareCompiler(product, input, outputs, platformDefines, defines, includePaths, systemIncludePaths, cFlags, cxxFlags)
-{
+function prepareCompiler(product, input, outputs, platformDefines, defines, includePaths, systemIncludePaths, cFlags, cxxFlags) {
var i;
var optimization = ModUtils.moduleProperty(input, "optimization")
var debugInformation = ModUtils.moduleProperty(input, "debugInformation")
@@ -47,7 +44,7 @@ function prepareCompiler(product, input, outputs, platformDefines, defines, incl
var minimumWindowsVersion = ModUtils.moduleProperty(product, "minimumWindowsVersion");
if (minimumWindowsVersion) {
- var hexVersion = Windows.getWindowsVersionInFormat(minimumWindowsVersion, 'hex');
+ var hexVersion = WindowsUtils.getWindowsVersionInFormat(minimumWindowsVersion, 'hex');
if (hexVersion) {
var versionDefs = [ 'WINVER', '_WIN32_WINNT', '_WIN32_WINDOWS' ];
for (i in versionDefs) {
@@ -85,7 +82,7 @@ function prepareCompiler(product, input, outputs, platformDefines, defines, incl
// use PCH
var pchHeaderName = FileInfo.toWindowsSeparators(pch);
var pchName = FileInfo.toWindowsSeparators(ModUtils.moduleProperty(product, "precompiledHeaderDir")
- + "\\.obj\\" + product.name + "\\" + product.name + "_" + tag + ".pch");
+ + "\\.obj\\" + product.name + "_" + tag + ".pch");
args.push("/FI" + pchHeaderName);
args.push("/Yu" + pchHeaderName);
args.push("/Fp" + pchName);
@@ -105,22 +102,21 @@ function prepareCompiler(product, input, outputs, platformDefines, defines, incl
args = wrapperArgs.concat(args);
}
var cmd = new Command(compilerPath, args)
- cmd.description = (pchOutput ? 'pre' : '') + 'compiling ' + FileInfo.fileName(input.filePath);
+ cmd.description = (pchOutput ? 'pre' : '') + 'compiling ' + input.fileName;
if (pchOutput)
cmd.description += ' (' + tag + ')';
cmd.highlight = "compiler";
- cmd.workingDirectory = product.buildDirectory + "/.obj/" + product.name;
+ cmd.workingDirectory = product.buildDirectory + "/.obj";
cmd.responseFileUsagePrefix = '@';
// cl.exe outputs the cpp file name. We filter that out.
cmd.stdoutFilterFunction = "function(output) {";
cmd.stdoutFilterFunction += "return output.replace(/"
- + FileInfo.fileName(input.filePath) + "\\r\\n/g, '');";
+ + input.fileName + "\\r\\n/g, '');";
cmd.stdoutFilterFunction += "}";
return cmd;
}
-function prepareLinker(product, inputs, outputs, libraryPaths, dynamicLibraries, staticLibraries, linkerFlags)
-{
+function prepareLinker(product, inputs, outputs, libraryPaths, dynamicLibraries, staticLibraries, linkerFlags) {
var i;
var linkDLL = (outputs.dynamiclibrary ? true : false)
var primaryOutput = (linkDLL ? outputs.dynamiclibrary[0] : outputs.application[0])
@@ -147,7 +143,8 @@ function prepareLinker(product, inputs, outputs, libraryPaths, dynamicLibraries,
}
if (minimumWindowsVersion) {
- var subsystemVersion = Windows.getWindowsVersionInFormat(minimumWindowsVersion, 'subsystem');
+ var subsystemVersion = WindowsUtils.getWindowsVersionInFormat(minimumWindowsVersion,
+ 'subsystem');
if (subsystemVersion) {
subsystemSwitch += ',' + subsystemVersion;
args.push('/OSVERSION:' + subsystemVersion);
@@ -165,7 +162,7 @@ function prepareLinker(product, inputs, outputs, libraryPaths, dynamicLibraries,
linkerOutputNativeFilePath
= FileInfo.toWindowsSeparators(
FileInfo.path(primaryOutput.filePath) + "/intermediate."
- + FileInfo.fileName(primaryOutput.filePath));
+ + primaryOutput.fileName);
manifestFileName = linkerOutputNativeFilePath + ".manifest";
args.push('/MANIFEST', '/MANIFESTFILE:' + manifestFileName)
} else {
@@ -202,15 +199,13 @@ function prepareLinker(product, inputs, outputs, libraryPaths, dynamicLibraries,
var commands = [];
var cmd = new Command(product.moduleProperty("cpp", "linkerPath"), args)
- cmd.description = 'linking ' + FileInfo.fileName(primaryOutput.filePath)
+ cmd.description = 'linking ' + primaryOutput.fileName;
cmd.highlight = 'linker';
cmd.workingDirectory = FileInfo.path(primaryOutput.filePath)
cmd.responseFileUsagePrefix = '@';
- cmd.stdoutFilterFunction =
- function(output)
- {
- return output.replace(/^ +Creating library.*\r\n$/, "");
- };
+ cmd.stdoutFilterFunction = function(output) {
+ return output.replace(/^ +Creating library.*\r\n$/, "");
+ };
commands.push(cmd);
if (generateManifestFiles) {
@@ -228,7 +223,7 @@ function prepareLinker(product, inputs, outputs, libraryPaths, dynamicLibraries,
"/outputresource:" + outputNativeFilePath + ";#" + (linkDLL ? "2" : "1")
]
cmd = new Command("mt.exe", args)
- cmd.description = 'embedding manifest into ' + FileInfo.fileName(primaryOutput.filePath)
+ cmd.description = 'embedding manifest into ' + primaryOutput.fileName;
cmd.highlight = 'linker';
cmd.workingDirectory = FileInfo.path(primaryOutput.filePath)
commands.push(cmd);
diff --git a/share/qbs/modules/cpp/windows-mingw.qbs b/share/qbs/modules/cpp/windows-mingw.qbs
index 5e6fb40ae..ea66d61f4 100644
--- a/share/qbs/modules/cpp/windows-mingw.qbs
+++ b/share/qbs/modules/cpp/windows-mingw.qbs
@@ -1,7 +1,6 @@
import qbs 1.0
-import qbs.FileInfo
import qbs.ModUtils
-import "windows.js" as Windows
+import qbs.WindowsUtils
GenericGCC {
condition: qbs.targetOS.contains("windows") && qbs.toolchain.contains("mingw")
@@ -13,7 +12,7 @@ GenericGCC {
dynamicLibrarySuffix: ".dll"
executableSuffix: ".exe"
windowsApiCharacterSet: "unicode"
- platformDefines: base.concat(Windows.characterSetDefines(windowsApiCharacterSet))
+ platformDefines: base.concat(WindowsUtils.characterSetDefines(windowsApiCharacterSet))
compilerDefines: ['__GNUC__', 'WIN32', '_WIN32']
property string windresName: 'windres'
@@ -40,7 +39,7 @@ GenericGCC {
inputs: ["rc"]
Artifact {
- fileName: ".obj/" + product.name + "/" + input.baseDir.replace(':', '') + "/" + input.completeBaseName + "_res.o"
+ fileName: ".obj/" + input.baseDir.replace(':', '') + "/" + input.completeBaseName + "_res.o"
fileTags: ["obj"]
}
@@ -70,7 +69,7 @@ GenericGCC {
args = args.concat(['-i', input.filePath, '-o', output.filePath]);
var cmd = new Command(ModUtils.moduleProperty(product, "windresPath"), args);
- cmd.description = 'compiling ' + FileInfo.fileName(input.filePath);
+ cmd.description = 'compiling ' + input.fileName;
cmd.highlight = 'compiler';
return cmd;
}
diff --git a/share/qbs/modules/cpp/windows-msvc.qbs b/share/qbs/modules/cpp/windows-msvc.qbs
index cd4ac01a2..2af9065a6 100644
--- a/share/qbs/modules/cpp/windows-msvc.qbs
+++ b/share/qbs/modules/cpp/windows-msvc.qbs
@@ -2,9 +2,9 @@ import qbs 1.0
import qbs.File
import qbs.FileInfo
import qbs.ModUtils
-import 'windows.js' as Windows
+import qbs.PathTools
+import qbs.WindowsUtils
import 'msvc.js' as MSVC
-import 'path-tools.js' as PathTools
CppModule {
condition: qbs.hostOS.contains('windows') && qbs.targetOS.contains('windows') && qbs.toolchain.contains('msvc')
@@ -12,7 +12,7 @@ CppModule {
id: module
windowsApiCharacterSet: "unicode"
- platformDefines: base.concat(Windows.characterSetDefines(windowsApiCharacterSet))
+ platformDefines: base.concat(WindowsUtils.characterSetDefines(windowsApiCharacterSet))
compilerDefines: ['_WIN32']
warningLevel: "default"
compilerName: "cl.exe"
@@ -20,7 +20,6 @@ CppModule {
property bool generateManifestFiles: true
property path toolchainInstallPath
- property path windowsSDKPath // ### remove in 1.3
architecture: qbs.architecture
staticLibraryPrefix: ""
dynamicLibraryPrefix: ""
@@ -38,12 +37,12 @@ CppModule {
fileName: {
var completeBaseName = FileInfo.completeBaseName(product.moduleProperty("cpp",
"cPrecompiledHeader"));
- return ".obj/" + product.name + "/" + completeBaseName + '_c.obj'
+ return ".obj/" + completeBaseName + '_c.obj'
}
}
Artifact {
fileTags: ['c_pch']
- fileName: ".obj/" + product.name + "/" + product.name + '_c.pch'
+ fileName: ".obj/" + product.name + '_c.pch'
}
prepare: {
var platformDefines = ModUtils.moduleProperty(input, 'platformDefines');
@@ -65,12 +64,12 @@ CppModule {
fileName: {
var completeBaseName = FileInfo.completeBaseName(product.moduleProperty("cpp",
"cxxPrecompiledHeader"));
- return ".obj/" + product.name + "/" + completeBaseName + '_cpp.obj'
+ return ".obj/" + completeBaseName + '_cpp.obj'
}
}
Artifact {
fileTags: ['cpp_pch']
- fileName: ".obj/" + product.name + "/" + product.name + '_cpp.pch'
+ fileName: ".obj/" + product.name + '_cpp.pch'
}
prepare: {
var platformDefines = ModUtils.moduleProperty(input, 'platformDefines');
@@ -91,7 +90,7 @@ CppModule {
Artifact {
fileTags: ['obj']
- fileName: ".obj/" + product.name + "/" + input.baseDir.replace(':', '') + "/" + input.fileName + ".obj"
+ fileName: ".obj/" + input.baseDir.replace(':', '') + "/" + input.fileName + ".obj"
}
prepare: {
@@ -114,7 +113,7 @@ CppModule {
usings: ['staticlibrary', 'dynamiclibrary_import']
Artifact {
fileTags: ["application"]
- fileName: product.destinationDirectory + "/" + PathTools.applicationFilePath()
+ fileName: product.destinationDirectory + "/" + PathTools.applicationFilePath(product)
}
prepare: {
@@ -135,12 +134,12 @@ CppModule {
Artifact {
fileTags: ["dynamiclibrary"]
- fileName: product.destinationDirectory + "/" + PathTools.dynamicLibraryFilePath()
+ fileName: product.destinationDirectory + "/" + PathTools.dynamicLibraryFilePath(product)
}
Artifact {
fileTags: ["dynamiclibrary_import"]
- fileName: product.destinationDirectory + "/" + PathTools.importLibraryFilePath()
+ fileName: product.destinationDirectory + "/" + PathTools.importLibraryFilePath(product)
alwaysUpdated: false
}
@@ -162,7 +161,7 @@ CppModule {
Artifact {
fileTags: ["staticlibrary"]
- fileName: product.destinationDirectory + "/" + PathTools.staticLibraryFilePath()
+ fileName: product.destinationDirectory + "/" + PathTools.staticLibraryFilePath(product)
cpp.staticLibraries: {
var result = []
for (var i in inputs.staticlibrary) {
@@ -184,7 +183,7 @@ CppModule {
args.push(fileName)
}
var cmd = new Command("lib.exe", args);
- cmd.description = 'creating ' + FileInfo.fileName(output.filePath)
+ cmd.description = 'creating ' + output.fileName;
cmd.highlight = 'linker';
cmd.workingDirectory = FileInfo.path(output.filePath)
cmd.responseFileUsagePrefix = '@';
@@ -201,7 +200,7 @@ CppModule {
inputs: ["rc"]
Artifact {
- fileName: ".obj/" + product.name + "/" + input.baseDir.replace(':', '') + "/" + input.completeBaseName + ".res"
+ fileName: ".obj/" + input.baseDir.replace(':', '') + "/" + input.completeBaseName + ".res"
fileTags: ["obj"]
}
@@ -231,7 +230,7 @@ CppModule {
args = args.concat(['/fo', output.filePath, input.filePath]);
var cmd = new Command('rc', args);
- cmd.description = 'compiling ' + FileInfo.fileName(input.filePath);
+ cmd.description = 'compiling ' + input.fileName;
cmd.highlight = 'compiler';
// Remove the first two lines of stdout. That's the logo.
diff --git a/share/qbs/modules/ib/IBModule.qbs b/share/qbs/modules/ib/IBModule.qbs
index 21117103d..c8037f243 100644
--- a/share/qbs/modules/ib/IBModule.qbs
+++ b/share/qbs/modules/ib/IBModule.qbs
@@ -1,10 +1,10 @@
import qbs 1.0
+import qbs.BundleTools
+import qbs.DarwinTools
import qbs.FileInfo
import qbs.ModUtils
import qbs.Process
import qbs.PropertyList
-import "../cpp/bundle-tools.js" as BundleTools
-import "../cpp/darwin-tools.js" as DarwinTools
Module {
condition: qbs.hostOS.contains("darwin") && qbs.targetOS.contains("darwin")
@@ -126,7 +126,7 @@ Module {
args.push(input.filePath);
var cmd = new Command("ibtool", args);
- cmd.description = 'ibtool ' + FileInfo.fileName(input.filePath);
+ cmd.description = 'ibtool ' + input.fileName;
// Also display the language name of the XIB being compiled if it has one
var localizationKey = DarwinTools.localizationKey(input.filePath);
diff --git a/share/qbs/modules/nodejs/NodeJS.qbs b/share/qbs/modules/nodejs/NodeJS.qbs
new file mode 100644
index 000000000..a18d5d98b
--- /dev/null
+++ b/share/qbs/modules/nodejs/NodeJS.qbs
@@ -0,0 +1,56 @@
+import qbs
+import qbs.File
+import qbs.FileInfo
+import qbs.ModUtils
+
+Module {
+ // JavaScript files which have been "processed" - currently this simply means "copied to output
+ // directory" but might later include minification and obfuscation processing
+ additionalProductTypes: ["nodejs_processed_js"]
+
+ property path applicationFile
+ PropertyOptions {
+ name: "applicationFile"
+ description: "file whose corresponding output will be executed when running the Node.js app"
+ }
+
+ setupRunEnvironment: {
+ var v = new ModUtils.EnvironmentVariable("NODE_PATH", qbs.pathListSeparator, qbs.hostOS.contains("windows"));
+ // can't use product.buildDirectory here, but RunEnvironment always sets the working
+ // directory to the directory containing the target file so we can exploit this for now
+ v.prepend(".");
+ v.set();
+ }
+
+ FileTagger {
+ patterns: ["*.js"]
+ fileTags: ["js"]
+ }
+
+ Rule {
+ inputs: ["js"]
+
+ outputArtifacts: {
+ var tags = ["nodejs_processed_js"];
+ if (input.fileTags.contains("application_js") ||
+ product.moduleProperty("nodejs", "applicationFile") === input.filePath)
+ tags.push("application");
+
+ return [{
+ filePath: product.destinationDirectory + '/' + input.fileName,
+ fileTags: tags
+ }];
+ }
+
+ outputFileTags: ["nodejs_processed_js", "application"]
+
+ prepare: {
+ var cmd = new JavaScriptCommand();
+ cmd.description = "copying " + input.fileName;
+ cmd.sourceCode = function() {
+ File.copy(input.filePath, output.filePath);
+ };
+ return cmd;
+ }
+ }
+}
diff --git a/share/qbs/modules/nsis/NSISModule.qbs b/share/qbs/modules/nsis/NSISModule.qbs
index f0756178e..ad8b84238 100755
--- a/share/qbs/modules/nsis/NSISModule.qbs
+++ b/share/qbs/modules/nsis/NSISModule.qbs
@@ -70,10 +70,23 @@ Module {
}
validate: {
+ var validator = new ModUtils.PropertyValidator("nsis");
+
// Only *require* the toolchain install path on Windows
// On other (Unix-like) operating systems it'll probably be in the PATH
- if (qbs.hostOS.contains("windows") && !toolchainInstallPath)
- throw "nsis.toolchainInstallPath is not defined. Set nsis.toolchainInstallPath in your profile.";
+ if (qbs.targetOS.contains("windows"))
+ validator.setRequiredProperty("toolchainInstallPath", toolchainInstallPath);
+
+ validator.setRequiredProperty("versionMajor", versionMajor);
+ validator.setRequiredProperty("versionMinor", versionMinor);
+ validator.setRequiredProperty("versionPatch", versionPatch);
+ validator.setRequiredProperty("versionBuild", versionBuild);
+ validator.addVersionValidator("version", version, 2, 4);
+ validator.addRangeValidator("versionMajor", versionMajor, 1);
+ validator.addRangeValidator("versionMinor", versionMinor, 0);
+ validator.addRangeValidator("versionPatch", versionPatch, 0);
+ validator.addRangeValidator("versionBuild", versionBuild, 0);
+ validator.validate();
}
setupBuildEnvironment: {
@@ -180,7 +193,7 @@ Module {
var inputFileNames = [];
for (i in inputs.nsi) {
- inputFileNames.push(FileInfo.fileName(inputs.nsi[i].filePath));
+ inputFileNames.push(inputs.nsi[i].fileName);
if (product.moduleProperty("qbs", "hostOS").contains("windows")) {
args.push(FileInfo.toWindowsSeparators(inputs.nsi[i].filePath));
} else {
diff --git a/share/qbs/modules/qbs/common.qbs b/share/qbs/modules/qbs/common.qbs
index 76ec20b9b..0425d70d5 100644
--- a/share/qbs/modules/qbs/common.qbs
+++ b/share/qbs/modules/qbs/common.qbs
@@ -1,5 +1,6 @@
import qbs 1.0
import qbs.FileInfo
+import qbs.ModUtils
Module {
property string buildVariant: "debug"
@@ -8,10 +9,10 @@ Module {
property string optimization: (buildVariant == "debug" ? "none" : "fast")
property stringList hostOS: getHostOS()
property string hostOSVersion: {
- if (hostOS.contains("osx")) {
+ if (hostOS && hostOS.contains("osx")) {
return getNativeSetting("/System/Library/CoreServices/ServerVersion.plist", "ProductVersion") ||
getNativeSetting("/System/Library/CoreServices/SystemVersion.plist", "ProductVersion");
- } else if (hostOS.contains("windows")) {
+ } else if (hostOS && hostOS.contains("windows")) {
var version = getNativeSetting("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion", "CurrentVersion");
return version + "." + hostOSBuildVersion;
}
@@ -38,7 +39,11 @@ Module {
property stringList toolchain
property string architecture
- property string endianness
+ property string endianness: {
+ if (["x86", "x86_64"].contains(architecture))
+ return "little";
+ }
+
PropertyOptions {
name: "endianness"
allowedValues: ["big", "little", "mixed"]
@@ -63,34 +68,34 @@ Module {
}
validate: {
- if (!architecture) {
- throw new Error("qbs.architecture is not set. "
- + "You might want to re-run 'qbs setup-toolchains'.");
- }
+ var validator = new ModUtils.PropertyValidator("qbs");
+ validator.setRequiredProperty("architecture", architecture,
+ "you might want to re-run 'qbs-setup-toolchains'");
+ validator.setRequiredProperty("hostOS", hostOS);
+ validator.setRequiredProperty("targetOS", targetOS);
+ if (hostOS && (hostOS.contains("windows") || hostOS.contains("osx"))) {
+ validator.setRequiredProperty("hostOSVersion", hostOSVersion,
+ "could not detect host operating system version; " +
+ "verify that system files and registry keys have not " +
+ "been modified.");
+ if (hostOSVersion)
+ validator.addVersionValidator("hostOSVersion", hostOSVersion, 2, 4);
- var canonicalArch = canonicalArchitecture(architecture);
- if (architecture !== canonicalArch) {
- throw "qbs.architecture '" + architecture + "' is invalid. " +
- "You must use the canonical name '" + canonicalArch + "'";
+ validator.setRequiredProperty("hostOSBuildVersion", hostOSBuildVersion,
+ "could not detect host operating system build version; " +
+ "verify that system files or registry have not been " +
+ "tampered with.");
}
- if (hostOS.contains("windows") || hostOS.contains("osx")) {
- if (!hostOSVersion) {
- throw "Could not detect host operating system version; " +
- "verify that system files or registry have not been " +
- "tampered with.";
- }
+ validator.addCustomValidator("architecture", architecture, function (value) {
+ return architecture === canonicalArchitecture(architecture);
+ }, "'" + architecture + "' is invalid. You must use the canonical name '" +
+ canonicalArchitecture(architecture) + "'");
- if (!/^[0-9]+(\.[0-9]+){1,3}$/.test(hostOSVersion)) {
- throw "qbs.hostOSVersion is in an invalid format; it must be of the form x.y or " +
- "x.y.z or x.y.z.w where x, y, z and w are positive integers.";
- }
+ validator.addCustomValidator("endianness", endianness, function (value) {
+ return ["big", "little", "mixed"].indexOf(value) !== -1;
+ }, "must be in [big, little, mixed]");
- if (!hostOSBuildVersion) {
- throw "Could not detect host operating system build version; " +
- "verify that system files or registry have not been " +
- "tampered with.";
- }
- }
+ validator.validate();
}
}
diff --git a/share/qbs/modules/typescript/TypeScriptModule.qbs b/share/qbs/modules/typescript/TypeScriptModule.qbs
new file mode 100644
index 000000000..8f80b7ce6
--- /dev/null
+++ b/share/qbs/modules/typescript/TypeScriptModule.qbs
@@ -0,0 +1,266 @@
+import qbs
+import qbs.File
+import qbs.FileInfo
+import qbs.ModUtils
+import qbs.Process
+
+Module {
+ Depends { name: "nodejs" }
+
+ additionalProductTypes: ["compiled_typescript"]
+
+ property path toolchainInstallPath
+ property string version: {
+ var p = new Process();
+ p.exec(compilerPath, ["--version"]);
+ var match = p.readStdOut().match(/^Version ([0-9]+(\.[0-9]+){1,3})\n$/);
+ if (match !== null)
+ return match[1];
+ }
+
+ property var versionParts: version ? version.split('.').map(function(item) { return parseInt(item, 10); }) : []
+ property int versionMajor: versionParts[0]
+ property int versionMinor: versionParts[1]
+ property int versionPatch: versionParts[2]
+ property int versionBuild: versionParts[3]
+
+ property string compilerName: "tsc"
+ property string compilerPath: FileInfo.joinPaths(toolchainInstallPath, compilerName)
+
+ property string warningLevel: "normal"
+ PropertyOptions {
+ name: "warningLevel"
+ description: "pedantic to warn on expressions and declarations with an implied 'any' type"
+ allowedValues: ["normal", "pedantic"]
+ }
+
+ property string targetVersion
+ PropertyOptions {
+ name: "targetVersion"
+ description: "ECMAScript target version"
+ allowedValues: ["ES3", "ES5"]
+ }
+
+ property string moduleLoader
+ PropertyOptions {
+ name: "moduleLoader"
+ allowedValues: ["commonjs", "amd"]
+ }
+
+ property bool stripComments: !qbs.debugInformation
+ PropertyOptions {
+ name: "stripComments"
+ description: "whether to remove comments from the generated output"
+ }
+
+ property bool generateDeclarations: false
+ PropertyOptions {
+ name: "generateDeclarations"
+ description: "whether to generate corresponding .d.ts files during compilation"
+ }
+
+ // In release mode, nodejs can/should default-enable minification and obfuscation,
+ // making the source maps useless, so these default settings work out fine
+ property bool generateSourceMaps: qbs.debugInformation
+ PropertyOptions {
+ name: "generateSourceMaps"
+ description: "whether to generate corresponding .map files during compilation"
+ }
+
+ property stringList compilerFlags
+ PropertyOptions {
+ name: "compilerFlags"
+ description: "additional flags for the TypeScript compiler"
+ }
+
+ property bool singleFile: false
+ PropertyOptions {
+ name: "singleFile"
+ description: "whether to compile all source files to a single output file"
+ }
+
+ validate: {
+ var validator = new ModUtils.PropertyValidator("typescript");
+ validator.setRequiredProperty("version", version);
+ validator.setRequiredProperty("versionMajor", versionMajor);
+ validator.setRequiredProperty("versionMinor", versionMinor);
+ validator.setRequiredProperty("versionPatch", versionPatch);
+ validator.setRequiredProperty("versionBuild", versionBuild);
+ validator.addVersionValidator("version", version, 4, 4);
+ validator.addRangeValidator("versionMajor", versionMajor, 1);
+ validator.addRangeValidator("versionMinor", versionMinor, 0);
+ validator.addRangeValidator("versionPatch", versionPatch, 0);
+ validator.addRangeValidator("versionBuild", versionBuild, 0);
+ validator.validate();
+ }
+
+ setupBuildEnvironment: {
+ if (toolchainInstallPath) {
+ var v = new ModUtils.EnvironmentVariable("PATH", qbs.pathListSeparator, qbs.hostOS.contains("windows"));
+ v.prepend(toolchainInstallPath);
+ v.set();
+ }
+ }
+
+ // TypeScript declaration files
+ FileTagger {
+ patterns: ["*.d.ts"]
+ fileTags: ["typescript_declaration"]
+ }
+
+ // TypeScript source files
+ FileTagger {
+ patterns: ["*.ts"]
+ fileTags: ["typescript"]
+ }
+
+ Rule {
+ id: typescriptCompiler
+ multiplex: true
+ inputs: ["typescript"]
+ usings: ["typescript_declaration"]
+
+ outputArtifacts: {
+ var artifacts = [];
+
+ if (product.moduleProperty("typescript", "singleFile")) {
+ var jsTags = ["js", "compiled_typescript"];
+
+ // We could check
+ // if (product.moduleProperty("nodejs", "applicationFile") === inputs.typescript[i].filePath)
+ // but since we're compiling to a single file there's no need to state it explicitly
+ jsTags.push("application_js");
+
+ var filePath = FileInfo.joinPaths(product.destinationDirectory, product.targetName);
+
+ artifacts.push({fileTags: jsTags,
+ filePath: FileInfo.joinPaths(".obj", product.targetName, "typescript", filePath + ".js")});
+ artifacts.push({condition: product.moduleProperty("typescript", "generateDeclarations"), // ### QBS-412
+ fileTags: ["typescript_declaration"],
+ filePath: filePath + ".d.ts"});
+ artifacts.push({condition: product.moduleProperty("typescript", "generateSourceMaps"), // ### QBS-412
+ fileTags: ["source_map"],
+ filePath: filePath + ".js.map"});
+ } else {
+ for (var i = 0; i < inputs.typescript.length; ++i) {
+ var jsTags = ["js", "compiled_typescript"];
+ if (product.moduleProperty("nodejs", "applicationFile") === inputs.typescript[i].filePath)
+ jsTags.push("application_js");
+
+ var filePath = FileInfo.joinPaths(product.destinationDirectory, FileInfo.baseName(inputs.typescript[i].filePath));
+
+ artifacts.push({fileTags: jsTags,
+ filePath: FileInfo.joinPaths(".obj", product.targetName, "typescript", filePath + ".js")});
+ artifacts.push({condition: product.moduleProperty("typescript", "generateDeclarations"), // ### QBS-412
+ fileTags: ["typescript_declaration"],
+ filePath: filePath + ".d.ts"});
+ artifacts.push({condition: product.moduleProperty("typescript", "generateSourceMaps"), // ### QBS-412
+ fileTags: ["source_map"],
+ filePath: filePath + ".js.map"});
+ }
+ }
+
+ return artifacts;
+ }
+
+ outputFileTags: {
+ var fileTags = ["js", "compiled_typescript"];
+ if (product.moduleProperty("nodejs", "applicationFile"))
+ fileTags.push("application_js");
+ if (product.moduleProperty("typescript", "generateDeclarations"))
+ fileTags.push("typescript_declaration");
+ if (product.moduleProperty("typescript", "generateSourceMaps"))
+ fileTags.push("source_map");
+ return fileTags;
+ }
+
+ prepare: {
+ var i;
+ var args = [];
+
+ var primaryOutput = outputs.compiled_typescript[0];
+
+ if (ModUtils.moduleProperty(product, "warningLevel") === "pedantic") {
+ args.push("--noImplicitAny");
+ }
+
+ var targetVersion = ModUtils.moduleProperty(product, "targetVersion");
+ if (targetVersion) {
+ args.push("--target");
+ args.push(targetVersion);
+ }
+
+ var moduleLoader = ModUtils.moduleProperty(product, "moduleLoader");
+ if (moduleLoader) {
+ if (ModUtils.moduleProperty(product, "singleFile")) {
+ throw("typescript.singleFile cannot be true when typescript.moduleLoader is set");
+ }
+
+ args.push("--module");
+ args.push(moduleLoader);
+ }
+
+ if (ModUtils.moduleProperty(product, "stripComments")) {
+ args.push("--removeComments");
+ }
+
+ if (ModUtils.moduleProperty(product, "generateDeclarations")) {
+ args.push("--declaration");
+ }
+
+ if (ModUtils.moduleProperty(product, "generateSourceMaps")) {
+ args.push("--sourcemap");
+ }
+
+ // User-supplied flags
+ var flags = ModUtils.moduleProperty(product, "compilerFlags");
+ for (i in flags) {
+ args.push(flags[i]);
+ }
+
+ args.push("--outDir");
+ args.push(product.buildDirectory);
+
+ if (ModUtils.moduleProperty(product, "singleFile")) {
+ args.push("--out");
+ args.push(primaryOutput.filePath);
+ }
+
+ if (inputs.typescript_declaration) {
+ for (i = 0; i < inputs.typescript_declaration.length; ++i) {
+ args.push(inputs.typescript_declaration[i].filePath);
+ }
+ }
+
+ for (i = 0; i < inputs.typescript.length; ++i) {
+ args.push(inputs.typescript[i].filePath);
+ }
+
+ var cmd, cmds = [];
+
+ cmd = new Command(ModUtils.moduleProperty(product, "compilerPath"), args);
+ cmd.description = "compiling " + (ModUtils.moduleProperty(product, "singleFile")
+ ? primaryOutput.fileName
+ : inputs.typescript.map(function(obj) {
+ return obj.fileName; }).join(", "));
+ cmd.highlight = "compiler";
+ cmds.push(cmd);
+
+ // Move all the compiled TypeScript files to the proper intermediate directory
+ cmd = new JavaScriptCommand();
+ cmd.silent = true;
+ cmd.outDir = product.buildDirectory;
+ cmd.sourceCode = function() {
+ for (i = 0; i < outputs.compiled_typescript.length; ++i) {
+ var fp = outputs.compiled_typescript[i].filePath;
+ var originalFilePath = FileInfo.joinPaths(outDir, FileInfo.fileName(fp));
+ File.copy(originalFilePath, fp);
+ File.remove(originalFilePath);
+ }
+ };
+ cmds.push(cmd);
+
+ return cmds;
+ }
+ }
+}
diff --git a/share/qbs/modules/wix/WiXModule.qbs b/share/qbs/modules/wix/WiXModule.qbs
index 02642b874..39a56f5f8 100644
--- a/share/qbs/modules/wix/WiXModule.qbs
+++ b/share/qbs/modules/wix/WiXModule.qbs
@@ -105,19 +105,20 @@ Module {
}
validate: {
- if (!toolchainInstallPath)
- throw "wix.toolchainInstallPath is not defined. Set wix.toolchainInstallPath in your profile.";
-
- if (!toolchainInstallRoot)
- throw "wix.toolchainInstallRoot is not defined. Set wix.toolchainInstallRoot in your profile."
-
- if (!version)
- throw "wix.version is not defined. Set wix.version in your profile.";
-
- if (!/^[0-9]+(\.[0-9]+){1,3}$/.test(version)) {
- throw "wix.version is in an invalid format; it must be of the form x.y.z.w " +
- "where x, y, z and w are positive integers.";
- }
+ var validator = new ModUtils.PropertyValidator("wix");
+ validator.setRequiredProperty("toolchainInstallPath", toolchainInstallPath);
+ validator.setRequiredProperty("toolchainInstallRoot", toolchainInstallRoot);
+ validator.setRequiredProperty("version", version);
+ validator.setRequiredProperty("versionMajor", versionMajor);
+ validator.setRequiredProperty("versionMinor", versionMinor);
+ validator.setRequiredProperty("versionPatch", versionPatch);
+ validator.setRequiredProperty("versionBuild", versionBuild);
+ validator.addVersionValidator("version", version, 4, 4);
+ validator.addRangeValidator("versionMajor", versionMajor, 1);
+ validator.addRangeValidator("versionMinor", versionMinor, 0);
+ validator.addRangeValidator("versionPatch", versionPatch, 0);
+ validator.addRangeValidator("versionBuild", versionBuild, 0);
+ validator.validate();
}
setupBuildEnvironment: {
@@ -151,7 +152,7 @@ Module {
Artifact {
fileTags: ["wixobj"]
- fileName: ".obj/" + product.name + "/" + input.baseDir.replace(':', '') + "/" + FileInfo.baseName(input.fileName) + ".wixobj"
+ fileName: ".obj/" + input.baseDir.replace(':', '') + "/" + FileInfo.baseName(input.fileName) + ".wixobj"
}
prepare: {
@@ -279,7 +280,7 @@ Module {
args.push(FileInfo.toWindowsSeparators(inputs.wxs[0].filePath));
var cmd = new Command(ModUtils.moduleProperty(product, "compilerPath"), args);
- cmd.description = "compiling " + FileInfo.fileName(inputs.wxs[0].filePath);
+ cmd.description = "compiling " + inputs.wxs[0].fileName;
cmd.highlight = "compiler";
cmd.workingDirectory = FileInfo.path(output.filePath);
return cmd;
@@ -376,7 +377,7 @@ Module {
}
var cmd = new Command(ModUtils.moduleProperty(product, "linkerPath"), args);
- cmd.description = "linking " + FileInfo.fileName(primaryOutput.filePath);
+ cmd.description = "linking " + primaryOutput.fileName;
cmd.highlight = "linker";
cmd.workingDirectory = FileInfo.path(primaryOutput.filePath);
return cmd;
diff --git a/src/app/app.pri b/src/app/app.pri
index 8dc6dc95b..3a86e5a0f 100644
--- a/src/app/app.pri
+++ b/src/app/app.pri
@@ -1,6 +1,12 @@
QT = core
TEMPLATE = app
-DESTDIR = ../../../bin
+!isEmpty(QBS_APPS_DESTDIR):DESTDIR = $${QBS_APPS_DESTDIR}
+else:DESTDIR = ../../../bin
+
+!isEmpty(QBS_APPS_RPATH_DIR) {
+ linux-*:QMAKE_LFLAGS += -Wl,-z,origin \'-Wl,-rpath,$${QBS_APPS_RPATH_DIR}\'
+ macx:QMAKE_LFLAGS += -Wl,-rpath,$${QBS_APPS_RPATH_DIR}
+}
CONFIG += console
CONFIG -= app_bundle
@@ -8,5 +14,6 @@ CONFIG -= app_bundle
include($${PWD}/../lib/corelib/use_corelib.pri)
include($${PWD}/shared/logging/logging.pri)
-target.path = $${QBS_INSTALL_PREFIX}/bin
+!isEmpty(QBS_APPS_INSTALL_DIR):target.path = $${QBS_APPS_INSTALL_DIR}
+else:target.path = $${QBS_INSTALL_PREFIX}/bin
INSTALLS += target
diff --git a/src/app/apps.qbs b/src/app/apps.qbs
index 56d8d3289..f3a87f81e 100644
--- a/src/app/apps.qbs
+++ b/src/app/apps.qbs
@@ -1,6 +1,7 @@
import qbs
Project {
+ property string appInstallDir: "bin"
references: [
"config/config.qbs",
"config-ui/config-ui.qbs",
diff --git a/src/app/apptemplate.qbs b/src/app/apptemplate.qbs
index 2d87a0233..7ebe33b2d 100644
--- a/src/app/apptemplate.qbs
+++ b/src/app/apptemplate.qbs
@@ -13,7 +13,7 @@ Product {
Group {
fileTagsFilter: product.type
qbs.install: true
- qbs.installDir: "bin"
+ qbs.installDir: project.appInstallDir
}
Group {
name: "logging"
diff --git a/src/app/qbs-setup-qt/setupqt.cpp b/src/app/qbs-setup-qt/setupqt.cpp
index b36272713..c7b1ea16d 100644
--- a/src/app/qbs-setup-qt/setupqt.cpp
+++ b/src/app/qbs-setup-qt/setupqt.cpp
@@ -180,6 +180,7 @@ QtEnvironment SetupQt::fetchEnvironment(const QString &qmakePath)
qtEnvironment.binaryPath = pathQueryValue(queryOutput, "QT_INSTALL_BINS");
qtEnvironment.documentationPath = pathQueryValue(queryOutput, "QT_INSTALL_DOCS");
qtEnvironment.pluginPath = pathQueryValue(queryOutput, "QT_INSTALL_PLUGINS");
+ qtEnvironment.qmlPath = pathQueryValue(queryOutput, "QT_INSTALL_QML");
qtEnvironment.qmlImportPath = pathQueryValue(queryOutput, "QT_INSTALL_IMPORTS");
qtEnvironment.qtVersion = QString::fromLocal8Bit(queryOutput.value("QT_VERSION"));
diff --git a/src/app/qbs-setup-toolchains/probe.cpp b/src/app/qbs-setup-toolchains/probe.cpp
index 63793dc63..4c35c4b65 100644
--- a/src/app/qbs-setup-toolchains/probe.cpp
+++ b/src/app/qbs-setup-toolchains/probe.cpp
@@ -250,9 +250,18 @@ void probe(Settings *settings)
void createProfile(const QString &profileName, const QString &toolchainType,
const QString &compilerFilePath, Settings *settings)
{
+ QFileInfo compiler(compilerFilePath);
+ if (compilerFilePath == compiler.fileName() && !compiler.exists())
+ compiler = QFileInfo(findExecutable(compilerFilePath));
+
+ if (!compiler.exists()) {
+ throw qbs::ErrorInfo(Tr::tr("Compiler '%1' not found")
+ .arg(compilerFilePath));
+ }
+
QStringList toolchainTypes;
if (toolchainType.isEmpty())
- toolchainTypes = toolchainTypeFromCompilerName(QFileInfo(compilerFilePath).fileName());
+ toolchainTypes = toolchainTypeFromCompilerName(compiler.fileName());
else
toolchainTypes = completeToolchainList(toolchainType);
@@ -262,7 +271,7 @@ void createProfile(const QString &profileName, const QString &toolchainType,
}
if (toolchainTypes.contains(QLatin1String("gcc")))
- createGccProfile(compilerFilePath, settings, toolchainTypes, profileName);
+ createGccProfile(compiler.absoluteFilePath(), settings, toolchainTypes, profileName);
else
throw qbs::ErrorInfo(Tr::tr("Cannot create profile: Unknown toolchain type."));
}
diff --git a/src/lib/corelib/api/project.cpp b/src/lib/corelib/api/project.cpp
index bea570aa3..dce270ef1 100644
--- a/src/lib/corelib/api/project.cpp
+++ b/src/lib/corelib/api/project.cpp
@@ -585,6 +585,14 @@ void ProjectPrivate::prepareChangeToProject()
retrieveProjectData(m_projectData, internalProject);
}
+static bool productIsRunnable(const ResolvedProductConstPtr &product)
+{
+ return product->fileTags.contains("application")
+ || (product->fileTags.contains("applicationbundle")
+ && product->moduleProperties->qbsPropertyValue(QLatin1String("targetOS"))
+ .toStringList().contains(QLatin1String("darwin")));
+}
+
void ProjectPrivate::retrieveProjectData(ProjectData &projectData,
const ResolvedProjectConstPtr &internalProject)
{
@@ -596,6 +604,7 @@ void ProjectPrivate::retrieveProjectData(ProjectData &projectData,
product.d->name = resolvedProduct->name;
product.d->location = resolvedProduct->location;
product.d->isEnabled = resolvedProduct->enabled;
+ product.d->isRunnable = productIsRunnable(resolvedProduct);
foreach (const GroupPtr &resolvedGroup, resolvedProduct->groups)
product.d->groups << createGroupDataFromGroup(resolvedGroup);
if (resolvedProduct->enabled) {
diff --git a/src/lib/corelib/api/projectdata.cpp b/src/lib/corelib/api/projectdata.cpp
index a5cc6093e..31ba1ef33 100644
--- a/src/lib/corelib/api/projectdata.cpp
+++ b/src/lib/corelib/api/projectdata.cpp
@@ -414,6 +414,12 @@ bool ProductData::isEnabled() const
return d->isEnabled;
}
+bool ProductData::isRunnable() const
+{
+ QBS_ASSERT(isValid(), return false);
+ return d->isRunnable;
+}
+
bool operator==(const ProductData &lhs, const ProductData &rhs)
{
return lhs.name() == rhs.name()
diff --git a/src/lib/corelib/api/projectdata.h b/src/lib/corelib/api/projectdata.h
index ba0016d0c..1d7625b82 100644
--- a/src/lib/corelib/api/projectdata.h
+++ b/src/lib/corelib/api/projectdata.h
@@ -176,6 +176,7 @@ public:
QList<TargetArtifact> targetArtifacts() const;
QList<GroupData> groups() const;
bool isEnabled() const;
+ bool isRunnable() const;
private:
QExplicitlySharedDataPointer<Internal::ProductDataPrivate> d;
diff --git a/src/lib/corelib/api/projectdata_p.h b/src/lib/corelib/api/projectdata_p.h
index 2eda4d932..b0006e89a 100644
--- a/src/lib/corelib/api/projectdata_p.h
+++ b/src/lib/corelib/api/projectdata_p.h
@@ -84,6 +84,7 @@ public:
QList<GroupData> groups;
QList<TargetArtifact> targetArtifacts;
bool isEnabled;
+ bool isRunnable;
bool isValid;
};
diff --git a/src/lib/corelib/api/runenvironment.cpp b/src/lib/corelib/api/runenvironment.cpp
index b0be216f8..386a1769d 100644
--- a/src/lib/corelib/api/runenvironment.cpp
+++ b/src/lib/corelib/api/runenvironment.cpp
@@ -129,6 +129,21 @@ int RunEnvironment::runShell()
return system(command.toLocal8Bit().constData());
}
+static QString findExecutable(const QStringList &fileNames)
+{
+ const QStringList path = QString::fromLocal8Bit(qgetenv("PATH"))
+ .split(HostOsInfo::pathListSeparator(), QString::SkipEmptyParts);
+
+ foreach (const QString &fileName, fileNames) {
+ foreach (const QString &ppath, path) {
+ const QString fullPath = ppath + QLatin1Char('/') + fileName;
+ if (QFileInfo(fullPath).exists())
+ return QDir::cleanPath(fullPath);
+ }
+ }
+ return QString();
+}
+
int RunEnvironment::runTarget(const QString &targetBin, const QStringList &arguments)
{
const QStringList targetOS = PropertyFinder().propertyValue(
@@ -154,6 +169,15 @@ int RunEnvironment::runTarget(const QString &targetBin, const QStringList &argum
}
}
+ if (completeSuffix == QLatin1String("js")) {
+ // The Node.js binary is called nodejs on Debian/Ubuntu-family operating systems due to a
+ // conflict with another package containing a binary named node
+ targetExecutable = findExecutable(QStringList()
+ << QLatin1String("nodejs")
+ << QLatin1String("node"));
+ targetArguments.prepend(targetBin);
+ }
+
// Only check if the target is executable if we're not running it through another
// known application such as msiexec or wine, as we can't check in this case anyways
if (targetBin == targetExecutable && !QFileInfo(targetExecutable).isExecutable()) {
@@ -166,6 +190,7 @@ int RunEnvironment::runTarget(const QString &targetBin, const QStringList &argum
d->logger.qbsInfo() << Tr::tr("Starting target '%1'.").arg(QDir::toNativeSeparators(targetBin));
QProcess process;
+ process.setWorkingDirectory(QFileInfo(targetBin).absolutePath());
process.setProcessEnvironment(d->resolvedProduct->runEnvironment);
process.setProcessChannelMode(QProcess::ForwardedChannels);
process.start(targetExecutable, targetArguments);
diff --git a/src/lib/corelib/buildgraph/executor.cpp b/src/lib/corelib/buildgraph/executor.cpp
index 024decbc7..283958220 100644
--- a/src/lib/corelib/buildgraph/executor.cpp
+++ b/src/lib/corelib/buildgraph/executor.cpp
@@ -791,8 +791,10 @@ void Executor::runTransformer(const TransformerPtr &transformer)
for (; it != transformer->outputs.end(); ++it) {
Artifact *output = *it;
QDir outDir = QFileInfo(output->filePath()).absoluteDir();
- if (!outDir.exists())
- outDir.mkpath(QLatin1String("."));
+ if (!outDir.exists() && !outDir.mkpath(QLatin1String("."))) {
+ throw ErrorInfo(tr("Failed to create directory '%1'.")
+ .arg(QDir::toNativeSeparators(outDir.absolutePath())));
+ }
}
}
diff --git a/src/lib/corelib/buildgraph/rulesapplicator.cpp b/src/lib/corelib/buildgraph/rulesapplicator.cpp
index 7cbcbf997..d80c10432 100644
--- a/src/lib/corelib/buildgraph/rulesapplicator.cpp
+++ b/src/lib/corelib/buildgraph/rulesapplicator.cpp
@@ -307,7 +307,7 @@ Artifact *RulesApplicator::createOutputArtifactFromRuleArtifact(
QScriptValue scriptValue = engine()->evaluate(ruleArtifact->fileName);
if (Q_UNLIKELY(engine()->hasErrorOrException(scriptValue)))
throw ErrorInfo(Tr::tr("Error in Rule.Artifact fileName: ") + scriptValue.toString());
- QString outputPath = scriptValue.toString();
+ QString outputPath = FileInfo::resolvePath(m_product->buildDirectory(), scriptValue.toString());
return createOutputArtifact(outputPath, ruleArtifact->fileTags, ruleArtifact->alwaysUpdated,
inputArtifacts);
}
@@ -428,7 +428,8 @@ Artifact *RulesApplicator::createOutputArtifactFromScriptValue(const QScriptValu
const ArtifactSet &inputArtifacts)
{
QBS_CHECK(obj.isObject());
- const QString filePath = obj.property(QLatin1String("filePath")).toVariant().toString();
+ const QString filePath = FileInfo::resolvePath(m_product->buildDirectory(),
+ obj.property(QLatin1String("filePath")).toVariant().toString());
const FileTags fileTags = FileTags::fromStringList(
obj.property(QLatin1String("fileTags")).toVariant().toStringList());
const QVariant alwaysUpdatedVar = obj.property(QLatin1String("alwaysUpdated")).toVariant();
diff --git a/src/lib/corelib/buildgraph/transformer.cpp b/src/lib/corelib/buildgraph/transformer.cpp
index 492feeb0c..c82962ed7 100644
--- a/src/lib/corelib/buildgraph/transformer.cpp
+++ b/src/lib/corelib/buildgraph/transformer.cpp
@@ -54,12 +54,7 @@ QScriptValue Transformer::translateFileConfig(QScriptEngine *scriptEngine, Artif
{
QScriptValue obj = scriptEngine->newObject();
ModuleProperties::init(obj, artifact);
-
- // ### undeprecate "fileName" and turn into a real file name in qbs 1.3
- ScriptEngine *qbsScriptEngine = static_cast<ScriptEngine *>(scriptEngine);
- qbsScriptEngine->setDeprecatedProperty(obj, QLatin1String("fileName"),
- QLatin1String("filePath"), artifact->filePath());
-
+ obj.setProperty(QLatin1String("fileName"), artifact->fileName());
obj.setProperty(QLatin1String("filePath"), artifact->filePath());
const QStringList fileTags = artifact->fileTags.toStringList();
obj.setProperty(QLatin1String("fileTags"), scriptEngine->toScriptValue(fileTags));
diff --git a/src/lib/corelib/language/asttools.cpp b/src/lib/corelib/language/asttools.cpp
index f1aec7d9b..838bb2e31 100644
--- a/src/lib/corelib/language/asttools.cpp
+++ b/src/lib/corelib/language/asttools.cpp
@@ -41,7 +41,7 @@ QStringList toStringList(QbsQmlJS::AST::UiQualifiedId *qid)
return result;
}
-CodeLocation toCodeLocation(const QString &filePath, QbsQmlJS::AST::SourceLocation location)
+CodeLocation toCodeLocation(const QString &filePath, const QbsQmlJS::AST::SourceLocation &location)
{
return CodeLocation(filePath, location.startLine, location.startColumn);
}
diff --git a/src/lib/corelib/language/asttools.h b/src/lib/corelib/language/asttools.h
index 9a84c2ade..7bbbb583b 100644
--- a/src/lib/corelib/language/asttools.h
+++ b/src/lib/corelib/language/asttools.h
@@ -38,7 +38,7 @@ namespace qbs {
namespace Internal {
QStringList toStringList(QbsQmlJS::AST::UiQualifiedId *qid);
-CodeLocation toCodeLocation(const QString &filePath, QbsQmlJS::AST::SourceLocation location);
+CodeLocation toCodeLocation(const QString &filePath, const QbsQmlJS::AST::SourceLocation &location);
QString textOf(const QString &source, QbsQmlJS::AST::Node *node);
QStringRef textRefOf(const QString &source, QbsQmlJS::AST::Node *node);
diff --git a/src/lib/corelib/language/builtindeclarations.cpp b/src/lib/corelib/language/builtindeclarations.cpp
index 02046e358..3ff60944b 100644
--- a/src/lib/corelib/language/builtindeclarations.cpp
+++ b/src/lib/corelib/language/builtindeclarations.cpp
@@ -109,6 +109,11 @@ static PropertyDeclaration nameProperty()
return PropertyDeclaration(QLatin1String("name"), PropertyDeclaration::String);
}
+static PropertyDeclaration buildDirProperty()
+{
+ return PropertyDeclaration(QLatin1String("buildDirectory"), PropertyDeclaration::Path);
+}
+
static PropertyDeclaration prepareScriptProperty()
{
PropertyDeclaration decl(QLatin1String("prepare"), PropertyDeclaration::Verbatim);
@@ -155,10 +160,6 @@ void BuiltinDeclarations::addExportItem()
void BuiltinDeclarations::addFileTaggerItem()
{
ItemDeclaration item(QLatin1String("FileTagger"));
-
- // TODO: Remove in 1.3
- item << PropertyDeclaration(QLatin1String("pattern"), PropertyDeclaration::StringList);
-
item << PropertyDeclaration(QLatin1String("patterns"), PropertyDeclaration::StringList);
item << PropertyDeclaration(QLatin1String("fileTags"), PropertyDeclaration::Variant);
insert(item);
@@ -245,9 +246,9 @@ void BuiltinDeclarations::addProductItem()
item << nameProperty();
decl = PropertyDeclaration(QLatin1String("targetName"), PropertyDeclaration::String);
decl.setInitialValueSource(QLatin1String("name"));
+ item << buildDirProperty();
item << decl;
decl = PropertyDeclaration(QLatin1String("destinationDirectory"), PropertyDeclaration::String);
- decl.setInitialValueSource(QLatin1String("'.'"));
item << decl;
item << PropertyDeclaration(QLatin1String("consoleApplication"),
PropertyDeclaration::Boolean);
@@ -272,6 +273,8 @@ void BuiltinDeclarations::addProjectItem()
<< QLatin1String("Rule"));
item << nameProperty();
item << conditionProperty();
+ item << buildDirProperty();
+ item << PropertyDeclaration(QLatin1String("sourceDirectory"), PropertyDeclaration::Path);
item << PropertyDeclaration(QLatin1String("references"), PropertyDeclaration::Variant,
PropertyDeclaration::PropertyNotAvailableInConfig);
item << PropertyDeclaration(QLatin1String("qbsSearchPaths"),
diff --git a/src/lib/corelib/language/item.cpp b/src/lib/corelib/language/item.cpp
index 7b42df5e6..8b1dcb72a 100644
--- a/src/lib/corelib/language/item.cpp
+++ b/src/lib/corelib/language/item.cpp
@@ -122,6 +122,14 @@ JSSourceValuePtr Item::sourceProperty(const QString &name) const
return v.staticCast<JSSourceValue>();
}
+VariantValuePtr Item::variantProperty(const QString &name) const
+{
+ ValuePtr v = property(name);
+ if (!v || v->type() != Value::VariantValueType)
+ return VariantValuePtr();
+ return v.staticCast<VariantValue>();
+}
+
const PropertyDeclaration Item::propertyDeclaration(const QString &name) const
{
const PropertyDeclaration decl = m_propertyDeclarations.value(name);
diff --git a/src/lib/corelib/language/item.h b/src/lib/corelib/language/item.h
index 866927a0e..8859957b0 100644
--- a/src/lib/corelib/language/item.h
+++ b/src/lib/corelib/language/item.h
@@ -102,6 +102,7 @@ public:
ValuePtr property(const QString &name) const;
ItemValuePtr itemProperty(const QString &name, bool create = false);
JSSourceValuePtr sourceProperty(const QString &name) const;
+ VariantValuePtr variantProperty(const QString &name) const;
void setPropertyObserver(ItemObserver *observer) const;
void setProperty(const QString &name, const ValuePtr &value);
void setPropertyDeclaration(const QString &name, const PropertyDeclaration &declaration);
diff --git a/src/lib/corelib/language/itemreaderastvisitor.cpp b/src/lib/corelib/language/itemreaderastvisitor.cpp
index e824271e5..82cede787 100644
--- a/src/lib/corelib/language/itemreaderastvisitor.cpp
+++ b/src/lib/corelib/language/itemreaderastvisitor.cpp
@@ -449,7 +449,7 @@ bool ItemReaderASTVisitor::visitStatement(AST::Statement *statement)
return false;
}
-CodeLocation ItemReaderASTVisitor::toCodeLocation(AST::SourceLocation location) const
+CodeLocation ItemReaderASTVisitor::toCodeLocation(const AST::SourceLocation &location) const
{
return CodeLocation(m_file->filePath(), location.startLine, location.startColumn);
}
diff --git a/src/lib/corelib/language/itemreaderastvisitor.h b/src/lib/corelib/language/itemreaderastvisitor.h
index dae481c3b..5d12aac35 100644
--- a/src/lib/corelib/language/itemreaderastvisitor.h
+++ b/src/lib/corelib/language/itemreaderastvisitor.h
@@ -60,7 +60,7 @@ public:
private:
bool visitStatement(QbsQmlJS::AST::Statement *statement);
- CodeLocation toCodeLocation(QbsQmlJS::AST::SourceLocation location) const;
+ CodeLocation toCodeLocation(const QbsQmlJS::AST::SourceLocation &location) const;
void checkDuplicateBinding(Item *item, const QStringList &bindingName,
const QbsQmlJS::AST::SourceLocation &sourceLocation);
Item *targetItemForBinding(Item *item, const QStringList &binding,
diff --git a/src/lib/corelib/language/language.cpp b/src/lib/corelib/language/language.cpp
index eb5ad9652..4974a3e68 100644
--- a/src/lib/corelib/language/language.cpp
+++ b/src/lib/corelib/language/language.cpp
@@ -585,11 +585,6 @@ static QProcessEnvironment getProcessEnvironment(ScriptEngine *engine, EnvType e
const QScriptValue getEnvValue = engine->newFunction(js_getEnv, 1);
const QScriptValue putEnvValue = engine->newFunction(js_putEnv, 1);
-
- // TODO: Remove in 1.3
- scope.setProperty(QLatin1String("getenv"), getEnvValue);
- scope.setProperty(QLatin1String("putenv"), putEnvValue);
-
scope.setProperty(QLatin1String("getEnv"), getEnvValue);
scope.setProperty(QLatin1String("putEnv"), putEnvValue);
@@ -817,6 +812,14 @@ QStringList ResolvedProduct::generatedFiles(const QString &baseFile, const FileT
return QStringList();
}
+QString ResolvedProduct::buildDirectory() const
+{
+ const QString result = productProperties.value(QLatin1String("buildDirectory")).toString();
+ QBS_CHECK(!result.isEmpty());
+ return result;
+}
+
+
ResolvedProject::ResolvedProject() : enabled(true), m_topLevelProject(0)
{
}
diff --git a/src/lib/corelib/language/language.h b/src/lib/corelib/language/language.h
index 780af8f47..a76c8ad26 100644
--- a/src/lib/corelib/language/language.h
+++ b/src/lib/corelib/language/language.h
@@ -397,6 +397,7 @@ public:
TopLevelProject *topLevelProject() const;
QStringList generatedFiles(const QString &baseFile, const FileTags &tags) const;
+ QString buildDirectory() const;
private:
ResolvedProduct();
diff --git a/src/lib/corelib/language/loader.cpp b/src/lib/corelib/language/loader.cpp
index 62c1aadde..622cfa073 100644
--- a/src/lib/corelib/language/loader.cpp
+++ b/src/lib/corelib/language/loader.cpp
@@ -120,11 +120,7 @@ TopLevelProjectPtr Loader::loadProject(const SetupProjectParameters &parameters)
cancelationTimer.start(1000);
}
- ModuleLoaderResult loadResult
- = m_moduleLoader->load(parameters.projectFilePath(),
- parameters.overriddenValuesTree(),
- parameters.buildConfigurationTree(),
- true);
+ ModuleLoaderResult loadResult = m_moduleLoader->load(parameters);
const TopLevelProjectPtr project = m_projectResolver->resolve(loadResult, parameters);
// E.g. if the top-level project is disabled.
diff --git a/src/lib/corelib/language/moduleloader.cpp b/src/lib/corelib/language/moduleloader.cpp
index abe09814c..3dc7df283 100644
--- a/src/lib/corelib/language/moduleloader.cpp
+++ b/src/lib/corelib/language/moduleloader.cpp
@@ -36,6 +36,7 @@
#include "item.h"
#include "itemreader.h"
#include "scriptengine.h"
+#include "value.h"
#include <language/language.h>
#include <language/scriptengine.h>
#include <logging/logger.h>
@@ -101,15 +102,12 @@ void ModuleLoader::setSearchPaths(const QStringList &searchPaths)
}
}
-ModuleLoaderResult ModuleLoader::load(const QString &filePath,
- const QVariantMap &overriddenProperties, const QVariantMap &buildConfigProperties,
- bool wrapWithProjectItem)
+ModuleLoaderResult ModuleLoader::load(const SetupProjectParameters &parameters)
{
if (m_logger.traceEnabled())
- m_logger.qbsTrace() << "[MODLDR] load" << filePath;
+ m_logger.qbsTrace() << "[MODLDR] load" << parameters.projectFilePath();
+ m_parameters = parameters;
m_reader->clearItemCache();
- m_overriddenProperties = overriddenProperties;
- m_buildConfigProperties = buildConfigProperties;
m_validItemPropertyNamesPerItem.clear();
m_disabledItems.clear();
@@ -117,14 +115,21 @@ ModuleLoaderResult ModuleLoader::load(const QString &filePath,
m_pool = result.itemPool.data();
m_reader->setPool(m_pool);
- Item *root = m_reader->readFile(filePath);
+ Item *root = m_reader->readFile(parameters.projectFilePath());
if (!root)
return ModuleLoaderResult();
- if (wrapWithProjectItem && root->typeName() != QLatin1String("Project"))
+ if (root->typeName() != QLatin1String("Project"))
root = wrapWithProject(root);
- handleProject(&result, root, QSet<QString>() << QDir::cleanPath(filePath));
+ const QString buildDirectory
+ = TopLevelProject::deriveBuildDirectory(parameters.buildRoot(),
+ TopLevelProject::deriveId(parameters.finalBuildConfigurationTree()));
+ root->setProperty(QLatin1String("sourceDirectory"),
+ VariantValue::create(QFileInfo(root->file()->filePath()).absolutePath()));
+ root->setProperty(QLatin1String("buildDirectory"), VariantValue::create(buildDirectory));
+ handleProject(&result, root, buildDirectory,
+ QSet<QString>() << QDir::cleanPath(parameters.projectFilePath()));
result.root = root;
result.qbsFiles = m_reader->filesRead();
return result;
@@ -198,19 +203,20 @@ private:
};
void ModuleLoader::handleProject(ModuleLoaderResult *loadResult, Item *item,
- const QSet<QString> &referencedFilePaths)
+ const QString &buildDirectory, const QSet<QString> &referencedFilePaths)
{
if (!checkItemCondition(item))
return;
ProjectContext projectContext;
projectContext.result = loadResult;
+ projectContext.buildDirectory = buildDirectory;
projectContext.localModuleSearchPath = FileInfo::resolvePath(item->file()->dirPath(),
moduleSearchSubDir);
ProductContext dummyProductContext;
dummyProductContext.project = &projectContext;
loadBaseModule(&dummyProductContext, item);
- overrideItemProperties(item, QLatin1String("project"), m_overriddenProperties);
+ overrideItemProperties(item, QLatin1String("project"), m_parameters.overriddenValuesTree());
projectContext.extraSearchPaths = readExtraSearchPaths(item);
m_reader->pushExtraSearchPaths(projectContext.extraSearchPaths);
@@ -228,7 +234,7 @@ void ModuleLoader::handleProject(ModuleLoaderResult *loadResult, Item *item,
handleSubProject(&projectContext, child, referencedFilePaths);
} else if (child->typeName() == QLatin1String("Project")) {
copyProperties(item, child);
- handleProject(loadResult, child, referencedFilePaths);
+ handleProject(loadResult, child, buildDirectory, referencedFilePaths);
}
}
@@ -267,7 +273,7 @@ void ModuleLoader::handleProject(ModuleLoaderResult *loadResult, Item *item,
handleProduct(&projectContext, subItem);
} else if (subItem->typeName() == QLatin1String("Project")) {
copyProperties(item, subItem);
- handleProject(loadResult, subItem,
+ handleProject(loadResult, subItem, buildDirectory,
QSet<QString>(referencedFilePaths) << absReferencePath);
} else {
throw ErrorInfo(Tr::tr("The top-level item of a file in a \"references\" list must be "
@@ -290,6 +296,7 @@ void ModuleLoader::handleProduct(ProjectContext *projectContext, Item *item)
if (m_logger.traceEnabled())
m_logger.qbsTrace() << "[MODLDR] handleProduct " << item->file()->filePath();
+ initProductProperties(projectContext, item);
ProductContext productContext;
productContext.project = projectContext;
bool extraSearchPathsSet = false;
@@ -332,6 +339,23 @@ void ModuleLoader::handleProduct(ProjectContext *projectContext, Item *item)
m_reader->popExtraSearchPaths();
}
+void ModuleLoader::initProductProperties(const ProjectContext *project, Item *item)
+{
+ QString productName = m_evaluator->stringValue(item, QLatin1String("name"));
+ if (productName.isEmpty()) {
+ productName = FileInfo::completeBaseName(item->file()->filePath());
+ item->setProperty(QLatin1String("name"), VariantValue::create(productName));
+ }
+
+ item->setProperty(QLatin1String("buildDirectory"),
+ VariantValue::create(
+ FileInfo::resolvePath(project->buildDirectory, productName)));
+
+ item->setProperty(QLatin1String("sourceDirectory"),
+ VariantValue::create(
+ QFileInfo(item->file()->filePath()).absolutePath()));
+}
+
void ModuleLoader::handleSubProject(ModuleLoader::ProjectContext *projectContext, Item *item,
const QSet<QString> &referencedFilePaths)
{
@@ -380,7 +404,7 @@ void ModuleLoader::handleSubProject(ModuleLoader::ProjectContext *projectContext
Item::addChild(item, loadedItem);
item->setScope(projectContext->scope);
- handleProject(projectContext->result, loadedItem,
+ handleProject(projectContext->result, loadedItem, projectContext->buildDirectory,
QSet<QString>(referencedFilePaths) << subProjectFilePath);
}
@@ -792,7 +816,7 @@ Item *ModuleLoader::loadModuleFile(ProductContext *productContext, const QString
// Module properties that are defined in the profile are used as default values.
const QVariantMap profileModuleProperties
- = m_buildConfigProperties.value(fullModuleName).toMap();
+ = m_parameters.buildConfigurationTree().value(fullModuleName).toMap();
for (QVariantMap::const_iterator vmit = profileModuleProperties.begin();
vmit != profileModuleProperties.end(); ++vmit)
{
@@ -827,7 +851,6 @@ void ModuleLoader::setupBaseModulePrototype(Item *prototype)
BuiltinValue::create(BuiltinValue::GetNativeSettingFunction));
const BuiltinValuePtr getEnvValue = BuiltinValue::create(BuiltinValue::GetEnvFunction);
prototype->setProperty(QLatin1String("getEnv"), getEnvValue);
- prototype->setProperty(QLatin1String("getenv"), getEnvValue); // TODO: Remove in 1.3.
prototype->setProperty(QLatin1String("getHostOS"),
BuiltinValue::create(BuiltinValue::GetHostOSFunction));
prototype->setProperty(QLatin1String("canonicalArchitecture"),
@@ -913,7 +936,8 @@ void ModuleLoader::instantiateModule(ProductContext *productContext, Item *insta
}
// override module properties given on the command line
- const QVariantMap userModuleProperties = m_overriddenProperties.value(fullName).toMap();
+ const QVariantMap userModuleProperties
+ = m_parameters.overriddenValuesTree().value(fullName).toMap();
for (QVariantMap::const_iterator vmit = userModuleProperties.begin();
vmit != userModuleProperties.end(); ++vmit) {
if (Q_UNLIKELY(!moduleInstance->hasProperty(vmit.key()))) {
@@ -993,7 +1017,7 @@ void ModuleLoader::checkCancelation() const
{
if (m_progressObserver && m_progressObserver->canceled()) {
throw ErrorInfo(Tr::tr("Project resolving canceled for configuration %1.")
- .arg(TopLevelProject::deriveId(m_buildConfigProperties)));
+ .arg(TopLevelProject::deriveId(m_parameters.buildConfigurationTree())));
}
}
diff --git a/src/lib/corelib/language/moduleloader.h b/src/lib/corelib/language/moduleloader.h
index f6f62a2c3..5df1747fc 100644
--- a/src/lib/corelib/language/moduleloader.h
+++ b/src/lib/corelib/language/moduleloader.h
@@ -33,6 +33,7 @@
#include "forward_decls.h"
#include "itempool.h"
#include <logging/logger.h>
+#include <tools/setupprojectparameters.h>
#include <QMap>
#include <QSet>
@@ -98,9 +99,7 @@ public:
void setSearchPaths(const QStringList &searchPaths);
Evaluator *evaluator() const { return m_evaluator; }
- ModuleLoaderResult load(const QString &filePath,
- const QVariantMap &overriddenProperties, const QVariantMap &buildConfigProperties,
- bool wrapWithProjectItem = false);
+ ModuleLoaderResult load(const SetupProjectParameters &parameters);
static QString fullModuleName(const QStringList &moduleName);
static void overrideItemProperties(Item *item, const QString &buildConfigKey,
@@ -124,6 +123,7 @@ private:
{
public:
ModuleLoaderResult *result;
+ QString buildDirectory;
QString localModuleSearchPath;
};
@@ -146,9 +146,10 @@ private:
typedef QPair<Item *, ModuleLoaderResult::ProductInfo::Dependency> ProductDependencyResult;
typedef QList<ProductDependencyResult> ProductDependencyResults;
- void handleProject(ModuleLoaderResult *loadResult, Item *item,
+ void handleProject(ModuleLoaderResult *loadResult, Item *item, const QString &buildDirectory,
const QSet<QString> &referencedFilePaths);
void handleProduct(ProjectContext *projectContext, Item *item);
+ void initProductProperties(const ProjectContext *project, Item *item);
void handleSubProject(ProjectContext *projectContext, Item *item,
const QSet<QString> &referencedFilePaths);
void createAdditionalModuleInstancesInProduct(ProductContext *productContext);
@@ -198,8 +199,7 @@ private:
QMap<QString, QStringList> m_moduleDirListCache;
QHash<Item *, QSet<QString> > m_validItemPropertyNamesPerItem;
QSet<Item *> m_disabledItems;
- QVariantMap m_overriddenProperties;
- QVariantMap m_buildConfigProperties;
+ SetupProjectParameters m_parameters;
};
} // namespace Internal
diff --git a/src/lib/corelib/language/projectresolver.cpp b/src/lib/corelib/language/projectresolver.cpp
index fec042e1e..4fe03b7cb 100644
--- a/src/lib/corelib/language/projectresolver.cpp
+++ b/src/lib/corelib/language/projectresolver.cpp
@@ -274,23 +274,17 @@ void ProjectResolver::resolveProduct(Item *item, ProjectContext *projectContext)
ProductContext productContext;
m_productContext = &productContext;
productContext.item = item;
- const QString productSourceDirectory = QFileInfo(item->file()->filePath()).absolutePath();
- item->setProperty(QLatin1String("sourceDirectory"),
- VariantValue::create(productSourceDirectory));
- item->setProperty(QLatin1String("buildDirectory"), VariantValue::create(projectContext
- ->project->topLevelProject()->buildDirectory));
ResolvedProductPtr product = ResolvedProduct::create();
product->project = projectContext->project;
m_productItemMap.insert(product, item);
projectContext->project->products += product;
productContext.product = product;
product->name = m_evaluator->stringValue(item, QLatin1String("name"));
- if (product->name.isEmpty()) {
- product->name = FileInfo::completeBaseName(item->file()->filePath());
- item->setProperty(QLatin1String("name"), VariantValue::create(product->name));
- }
m_logger.qbsTrace() << "[PR] resolveProduct " << product->name;
+ // product->buildDirectory() isn't valid yet, because the productProperties map is not ready.
+ productContext.buildDirectory = m_evaluator->stringValue(item, QLatin1String("buildDirectory"));
+
if (std::find_if(item->modules().begin(), item->modules().end(),
ModuleNameEquals(product->name)) != item->modules().end()) {
throw ErrorInfo(
@@ -314,11 +308,20 @@ void ProjectResolver::resolveProduct(Item *item, ProjectContext *projectContext)
product->fileTags = m_evaluator->fileTagsValue(item, QLatin1String("type"));
product->targetName = m_evaluator->stringValue(item, QLatin1String("targetName"));
- product->sourceDirectory = productSourceDirectory;
- product->destinationDirectory
- = m_evaluator->stringValue(item, QLatin1String("destinationDirectory"));
+ product->sourceDirectory = m_evaluator->stringValue(item, QLatin1String("sourceDirectory"));
+ const QString destDirKey = QLatin1String("destinationDirectory");
+ product->destinationDirectory = m_evaluator->stringValue(item, destDirKey);
+
+ if (product->destinationDirectory.isEmpty()) {
+ product->destinationDirectory = productContext.buildDirectory;
+ } else {
+ product->destinationDirectory = FileInfo::resolvePath(
+ product->topLevelProject()->buildDirectory,
+ product->destinationDirectory);
+ }
product->location = item->location();
product->productProperties = createProductConfig();
+ product->productProperties.insert(destDirKey, product->destinationDirectory);
QVariantMap moduleProperties;
moduleProperties.insert(QLatin1String("modules"),
product->productProperties.take(QLatin1String("modules")));
@@ -384,15 +387,6 @@ void ProjectResolver::resolveModule(const QStringList &moduleName, Item *item,
m_productContext->additionalFileTags +=
m_evaluator->fileTagsValue(item, QLatin1String("additionalProductTypes"));
- // TODO: Remove in 1.3.
- bool additionalProductFileTagsWasSet;
- const QStringList additionalProductFileTags = m_evaluator->stringListValue(item, QLatin1String("additionalProductFileTags"),
- &additionalProductFileTagsWasSet);
- if (additionalProductFileTagsWasSet) {
- m_logger.printWarning(ErrorInfo(Tr::tr("The 'additionalProductFileTags' property is deprecated. Please "
- "use 'additionalProductTypes' instead."), item->location()));
- m_productContext->additionalFileTags += FileTags::fromStringList(additionalProductFileTags);
- }
foreach (const Item::Module &m, item->modules())
module->moduleDependencies += ModuleLoader::fullModuleName(m.name);
@@ -463,6 +457,20 @@ void ProjectResolver::resolveGroup(Item *item, ProjectContext *projectContext)
if (Q_UNLIKELY(!files.isEmpty()))
throw ErrorInfo(Tr::tr("Group.files and Group.fileTagsFilters are exclusive."),
item->location());
+
+ ProductContext::ArtifactPropertiesInfo apinfo
+ = m_productContext->artifactPropertiesPerFilter.value(fileTagsFilter);
+ if (apinfo.first) {
+ if (apinfo.second.fileName() == item->location().fileName()) {
+ ErrorInfo error(Tr::tr("Conflicting fileTagsFilter in Group items."));
+ error.append(Tr::tr("First item"), apinfo.second);
+ error.append(Tr::tr("Second item"), item->location());
+ throw error;
+ }
+
+ // Discard any Group with the same fileTagsFilter that was defined in a base file.
+ m_productContext->product->artifactProperties.removeAll(apinfo.first);
+ }
if (!isEnabled)
return;
ArtifactPropertiesPtr aprops = ArtifactProperties::create();
@@ -471,6 +479,8 @@ void ProjectResolver::resolveGroup(Item *item, ProjectContext *projectContext)
cfg->setValue(evaluateModuleValues(item));
aprops->setPropertyMapInternal(cfg);
m_productContext->product->artifactProperties += aprops;
+ m_productContext->artifactPropertiesPerFilter.insert(fileTagsFilter,
+ ProductContext::ArtifactPropertiesInfo(aprops, item->location()));
return;
}
if (Q_UNLIKELY(files.isEmpty() && !item->hasProperty(QLatin1String("files")))) {
@@ -710,24 +720,14 @@ void ProjectResolver::resolveFileTagger(Item *item, ProjectContext *projectConte
checkCancelation();
QList<FileTaggerConstPtr> &fileTaggers = m_productContext
? m_productContext->product->fileTaggers : projectContext->fileTaggers;
- QStringList patterns = m_evaluator->stringListValue(item, QLatin1String("patterns"));
+ const QStringList patterns = m_evaluator->stringListValue(item, QLatin1String("patterns"));
+ if (patterns.isEmpty())
+ throw ErrorInfo(Tr::tr("FileTagger.patterns must be a non-empty list."), item->location());
+
const FileTags fileTags = m_evaluator->fileTagsValue(item, QLatin1String("fileTags"));
if (fileTags.isEmpty())
throw ErrorInfo(Tr::tr("FileTagger.fileTags must not be empty."), item->location());
- // TODO: Remove in 1.3.
- bool patternWasSet;
- const QStringList oldPatterns = m_evaluator->stringListValue(item, QLatin1String("pattern"),
- &patternWasSet);
- if (patternWasSet) {
- m_logger.printWarning(ErrorInfo(Tr::tr("The 'pattern' property is deprecated. Please "
- "use 'patterns' instead."), item->location()));
- patterns << oldPatterns;
- }
-
- if (patterns.isEmpty())
- throw ErrorInfo(Tr::tr("FileTagger.patterns must be a non-empty list."), item->location());
-
foreach (const QString &pattern, patterns) {
if (pattern.isEmpty())
throw ErrorInfo(Tr::tr("A FileTagger pattern must not be empty."), item->location());
@@ -760,8 +760,8 @@ void ProjectResolver::resolveTransformer(Item *item, ProjectContext *projectCont
QString fileName = m_evaluator->stringValue(child, QLatin1String("fileName"));
if (Q_UNLIKELY(fileName.isEmpty()))
throw ErrorInfo(Tr::tr("Artifact fileName must not be empty."));
- artifact->absoluteFilePath = FileInfo::resolvePath(m_productContext->product->topLevelProject()->buildDirectory,
- fileName);
+ artifact->absoluteFilePath
+ = FileInfo::resolvePath(m_productContext->buildDirectory, fileName);
artifact->fileTags = m_evaluator->fileTagsValue(child, QLatin1String("fileTags"));
if (artifact->fileTags.isEmpty())
artifact->fileTags.insert(unknownFileTag());
diff --git a/src/lib/corelib/language/projectresolver.h b/src/lib/corelib/language/projectresolver.h
index 636fd7299..630786db7 100644
--- a/src/lib/corelib/language/projectresolver.h
+++ b/src/lib/corelib/language/projectresolver.h
@@ -77,8 +77,11 @@ private:
struct ProductContext
{
ResolvedProductPtr product;
+ QString buildDirectory;
FileTags additionalFileTags;
Item *item;
+ typedef QPair<ArtifactPropertiesPtr, CodeLocation> ArtifactPropertiesInfo;
+ QHash<QStringList, ArtifactPropertiesInfo> artifactPropertiesPerFilter;
};
struct ModuleContext
diff --git a/src/lib/corelib/language/testdata/erroneous/conflicting_fileTagsFilter.qbs b/src/lib/corelib/language/testdata/erroneous/conflicting_fileTagsFilter.qbs
new file mode 100644
index 000000000..54b3343e4
--- /dev/null
+++ b/src/lib/corelib/language/testdata/erroneous/conflicting_fileTagsFilter.qbs
@@ -0,0 +1,13 @@
+import qbs 1.0
+
+Application {
+ Group {
+ fileTagsFilter: "application"
+ qbs.install: true
+ }
+ Group {
+ fileTagsFilter: "application"
+ qbs.install: false
+ }
+}
+
diff --git a/src/lib/corelib/language/testdata/profilevaluesandoverriddenvalues.qbs b/src/lib/corelib/language/testdata/profilevaluesandoverriddenvalues.qbs
index cc1b7b2a2..0ba76c6f6 100644
--- a/src/lib/corelib/language/testdata/profilevaluesandoverriddenvalues.qbs
+++ b/src/lib/corelib/language/testdata/profilevaluesandoverriddenvalues.qbs
@@ -2,14 +2,15 @@ import qbs 1.0
Project {
Application {
- name: {
+ name: "product1"
+ type: {
if (!(dummy.cFlags instanceof Array))
throw new Error("dummy.cFlags: Array type expected.");
if (!(dummy.cxxFlags instanceof Array))
throw new Error("dummy.cxxFlags: Array type expected.");
if (!(dummy.defines instanceof Array))
throw new Error("dummy.defines: Array type expected.");
- return "product1";
+ return "application";
}
Depends { name: "dummy" }
// dummy.cxxFlags is set via profile and is not overridden
diff --git a/src/lib/corelib/language/tst_language.cpp b/src/lib/corelib/language/tst_language.cpp
index ff19b8afe..6873391e9 100644
--- a/src/lib/corelib/language/tst_language.cpp
+++ b/src/lib/corelib/language/tst_language.cpp
@@ -370,6 +370,8 @@ void TestLanguage::erroneousFiles_data()
<< "Unexpected item type 'Narf'";
QTest::newRow("invalid_child_item_type")
<< "Items of type 'Project' cannot contain items of type 'Depends'.";
+ QTest::newRow("conflicting_fileTagsFilter")
+ << "Conflicting fileTagsFilter in Group items";
}
void TestLanguage::erroneousFiles()
@@ -1113,7 +1115,7 @@ void TestLanguage::productDirectories()
QVERIFY(product);
const QVariantMap config = product->productProperties;
QCOMPARE(config.value(QLatin1String("buildDirectory")).toString(),
- buildDir(defaultParameters));
+ buildDir(defaultParameters) + QLatin1Char('/') + product->name);
QCOMPARE(config.value(QLatin1String("sourceDirectory")).toString(), testDataDir());
}
catch (const ErrorInfo &e) {
diff --git a/src/lib/corelib/tools/settings.cpp b/src/lib/corelib/tools/settings.cpp
index f59e83e2c..617fa2137 100644
--- a/src/lib/corelib/tools/settings.cpp
+++ b/src/lib/corelib/tools/settings.cpp
@@ -45,26 +45,6 @@ static QSettings::Format format()
return HostOsInfo::isWindowsHost() ? QSettings::IniFormat : QSettings::NativeFormat;
}
-static void migrateValue(QSettings *settings, const QString &key)
-{
- const QVariant v = settings->value(key);
- if (!v.isValid())
- return;
- settings->setValue(QLatin1String("org/qt-project/qbs/") + key, v);
- settings->remove(key);
-}
-
-static void migrateGroup(QSettings *settings, const QString &group)
-{
- QStringList fullKeys;
- settings->beginGroup(group);
- foreach (const QString &key, settings->allKeys())
- fullKeys += group + QLatin1Char('/') + key;
- settings->endGroup();
- foreach (const QString &key, fullKeys)
- migrateValue(settings, key);
-}
-
static QSettings *createQSettings(const QString &baseDir)
{
return baseDir.isEmpty()
@@ -75,13 +55,6 @@ static QSettings *createQSettings(const QString &baseDir)
Settings::Settings(const QString &baseDir) : m_settings(createQSettings(baseDir))
{
- // Migrate settings to internal group.
- // ### remove in qbs 1.3
- if (!m_settings->childGroups().contains(QLatin1String("org/qt-project/qbs"))) {
- migrateValue(m_settings, QLatin1String("defaultProfile"));
- migrateGroup(m_settings, QLatin1String("profiles"));
- migrateGroup(m_settings, QLatin1String("preferences"));
- }
// Actual qbs settings are stored transparently within a group, because QSettings
// can see non-qbs fallback settings e.g. from QtProject that we're not interested in.
m_settings->beginGroup(QLatin1String("org/qt-project/qbs"));
diff --git a/src/lib/qtprofilesetup/qtprofilesetup.cpp b/src/lib/qtprofilesetup/qtprofilesetup.cpp
index 31b6950a1..ddaec6c8e 100644
--- a/src/lib/qtprofilesetup/qtprofilesetup.cpp
+++ b/src/lib/qtprofilesetup/qtprofilesetup.cpp
@@ -119,6 +119,13 @@ static void addDesignerComponentsModule(QList<QtModuleInfo> &modules)
modules << module;
}
+static QString quotedPath(const QString &str)
+{
+ return QLatin1Char('"')
+ + QDir::fromNativeSeparators(str).replace(QLatin1Char('"'), QLatin1String("\\\""))
+ + QLatin1Char('"');
+}
+
static void createModules(Profile &profile, Settings *settings,
const QtEnvironment &qtEnvironment)
{
@@ -314,9 +321,21 @@ static void createModules(Profile &profile, Settings *settings,
const QByteArray debugMacro = module.qbsName == QLatin1String("declarative")
|| qtEnvironment.qtMajorVersion < 5
? "QT_DECLARATIVE_DEBUG" : "QT_QML_DEBUG";
- propertiesString = "property bool qmlDebugging: false\n"
- " cpp.defines: "
- "qmlDebugging ? base.concat('" + debugMacro + "') : base";
+
+ const QString indent = QLatin1String(" ");
+ QTextStream s(&propertiesString);
+ s << "property bool qmlDebugging: false" << endl
+ << indent << "cpp.defines: "
+ << "qmlDebugging ? base.concat('" + debugMacro + "') : base" << endl;
+
+ s << indent << "property string qmlPath";
+ if (qtEnvironment.qmlPath.isEmpty())
+ s << endl;
+ else
+ s << ": " << quotedPath(qtEnvironment.qmlPath) << endl;
+
+ s << indent << "property string qmlImportsPath: "
+ << quotedPath(qtEnvironment.qmlImportPath);
}
content.replace("### special properties", propertiesString);
moduleFile.resize(0);
diff --git a/src/lib/qtprofilesetup/qtprofilesetup.h b/src/lib/qtprofilesetup/qtprofilesetup.h
index a2283202a..88e56d2c9 100644
--- a/src/lib/qtprofilesetup/qtprofilesetup.h
+++ b/src/lib/qtprofilesetup/qtprofilesetup.h
@@ -43,6 +43,7 @@ public:
QString libraryPath;
QString includePath;
QString binaryPath;
+ QString qmlPath;
QString qmlImportPath;
QString documentationPath;
QString dataPath;
diff --git a/src/lib/qtprofilesetup/templates/core.qbs b/src/lib/qtprofilesetup/templates/core.qbs
index 0d706b255..35ca1b8fd 100644
--- a/src/lib/qtprofilesetup/templates/core.qbs
+++ b/src/lib/qtprofilesetup/templates/core.qbs
@@ -37,7 +37,7 @@ Module {
// These are deliberately not path types
// We don't want to resolve them against the source directory
- property string generatedFilesDir: "GeneratedFiles/" + product.name
+ property string generatedFilesDir: product.buildDirectory + "/GeneratedFiles"
property string qmFilesDir: product.destinationDirectory
// private properties
@@ -62,7 +62,7 @@ Module {
paths.push(libPath + '/QtCore' + libInfix + '.framework/Versions/' + versionMajor + '/Headers');
paths.push(incPath + '/QtCore');
paths.push(incPath);
- paths.push(product.buildDirectory + '/' + generatedFilesDir);
+ paths.push(generatedFilesDir);
return paths;
}
cpp.libraryPaths: {
@@ -117,54 +117,29 @@ Module {
additionalProductTypes: ["qm"]
validate: {
- var requiredProperties = {
- "binPath": binPath,
- "incPath": incPath,
- "libPath": libPath,
- "mkspecPath": mkspecPath,
- "version": version,
- "config": config,
- "qtConfig": qtConfig,
- // Validate these in case 'version' is in some non-standard format
- "versionMajor": versionMajor,
- "versionMinor": versionMinor,
- "versionPatch": versionPatch
- };
-
- if (!staticBuild) {
- requiredProperties["pluginPath"] = pluginPath;
- }
-
- var missingProperties = [];
- for (var i in requiredProperties) {
- if (requiredProperties[i] === undefined) {
- missingProperties.push("Qt.core." + i);
- }
- }
-
- var invalidProperties = {};
- if (versionMajor <= 0)
- invalidProperties["versionMajor"] = "must be > 0";
- if (versionMinor < 0)
- invalidProperties["versionMinor"] = "must be >= 0";
- if (versionPatch < 0)
- invalidProperties["versionPatch"] = "must be >= 0";
-
- var errorMessage = "";
- if (missingProperties.length > 0) {
- errorMessage += "The following Qt module properties are not set. " +
- "Set them in your profile:\n" +
- missingProperties.sort().join("\n");
- }
-
- if (Object.keys(invalidProperties).length > 0) {
- errorMessage += "The following Qt module properties have invalid values:\n" +
- Object.map(invalidProperties,
- function(msg, prop) { return prop + ": " + msg; }).join("\n");
- }
-
- if (errorMessage.length > 0)
- throw errorMessage;
+ var validator = new ModUtils.PropertyValidator("Qt.core");
+ validator.setRequiredProperty("binPath", binPath);
+ validator.setRequiredProperty("incPath", incPath);
+ validator.setRequiredProperty("libPath", libPath);
+ validator.setRequiredProperty("mkspecPath", mkspecPath);
+ validator.setRequiredProperty("version", version);
+ validator.setRequiredProperty("config", config);
+ validator.setRequiredProperty("qtConfig", qtConfig);
+ validator.setRequiredProperty("versionMajor", versionMajor);
+ validator.setRequiredProperty("versionMinor", versionMinor);
+ validator.setRequiredProperty("versionPatch", versionPatch);
+
+ if (!staticBuild)
+ validator.setRequiredProperty("pluginPath", pluginPath);
+
+ // Allow custom version suffix since some distributions might want to do this,
+ // but otherwise the version must start with a valid 3-component string
+ validator.addVersionValidator("version", version, 3, 3, true);
+ validator.addRangeValidator("versionMajor", versionMajor, 1);
+ validator.addRangeValidator("versionMinor", versionMinor, 0);
+ validator.addRangeValidator("versionPatch", versionPatch, 0);
+
+ validator.validate();
}
setupRunEnvironment: {
@@ -228,7 +203,7 @@ Module {
prepare: {
var cmd = new Command(Moc.fullPath(product),
Moc.args(product, input, output.filePath));
- cmd.description = 'moc ' + FileInfo.fileName(input.filePath);
+ cmd.description = 'moc ' + input.fileName;
cmd.highlight = 'codegen';
return cmd;
}
@@ -247,7 +222,7 @@ Module {
[input.filePath, '-name',
FileInfo.completeBaseName(input.filePath),
'-o', output.filePath]);
- cmd.description = 'rcc ' + FileInfo.fileName(input.filePath);
+ cmd.description = 'rcc ' + input.fileName;
cmd.highlight = 'codegen';
return cmd;
}
@@ -266,7 +241,7 @@ Module {
var cmd = new Command(ModUtils.moduleProperty(product, "binPath") + '/'
+ ModUtils.moduleProperty(product, "lreleaseName"),
['-silent', input.filePath, '-qm', output.filePath]);
- cmd.description = 'lrelease ' + FileInfo.fileName(input.filePath);
+ cmd.description = 'lrelease ' + input.fileName;
cmd.highlight = 'filegen';
return cmd;
}
@@ -297,7 +272,7 @@ Module {
}
var cmd = new Command(ModUtils.moduleProperty(product, "binPath") + '/'
+ ModUtils.moduleProperty(product, "qdocName"), args);
- cmd.description = 'qdoc ' + FileInfo.fileName(input.filePath);
+ cmd.description = 'qdoc ' + input.fileName;
cmd.highlight = 'filegen';
cmd.environment = ModUtils.moduleProperty(product, "qdocEnvironment");
cmd.environment.push("OUTDIR=" + outputDir); // Qt 4 replacement for -outputdir
@@ -321,7 +296,7 @@ Module {
args.push(output.filePath);
var cmd = new Command(ModUtils.moduleProperty(product, "binPath") + "/qhelpgenerator",
args);
- cmd.description = 'qhelpgenerator ' + FileInfo.fileName(input.filePath);
+ cmd.description = 'qhelpgenerator ' + input.fileName;
cmd.highlight = 'filegen';
return cmd;
}
diff --git a/src/lib/qtprofilesetup/templates/gui.qbs b/src/lib/qtprofilesetup/templates/gui.qbs
index 02b90f40c..5e4f43e15 100644
--- a/src/lib/qtprofilesetup/templates/gui.qbs
+++ b/src/lib/qtprofilesetup/templates/gui.qbs
@@ -18,7 +18,7 @@ QtModule {
Artifact {
// ### TODO we want to access the module's property "Qt.core.generatedFilesDir" here. But without evaluating all available properties a priori.
- fileName: 'GeneratedFiles/' + product.name + '/ui_' + input.completeBaseName + '.h'
+ fileName: 'GeneratedFiles/ui_' + input.completeBaseName + '.h'
fileTags: ["hpp"]
}
@@ -26,7 +26,7 @@ QtModule {
var cmd = new Command(ModUtils.moduleProperty(product, "binPath") + '/'
+ ModUtils.moduleProperty(product, "uicName"),
[input.filePath, '-o', output.filePath])
- cmd.description = 'uic ' + FileInfo.fileName(input.filePath);
+ cmd.description = 'uic ' + input.fileName;
cmd.highlight = 'codegen';
return cmd;
}
diff --git a/tests/auto/api/testdata/is-runnable/project.qbs b/tests/auto/api/testdata/is-runnable/project.qbs
new file mode 100644
index 000000000..02200fff2
--- /dev/null
+++ b/tests/auto/api/testdata/is-runnable/project.qbs
@@ -0,0 +1,10 @@
+import qbs
+
+Project {
+ CppApplication {
+ name: "app"
+ }
+ DynamicLibrary {
+ name: "lib"
+ }
+}
diff --git a/tests/auto/api/tst_api.cpp b/tests/auto/api/tst_api.cpp
index 3b0fb1828..ab52b7040 100644
--- a/tests/auto/api/tst_api.cpp
+++ b/tests/auto/api/tst_api.cpp
@@ -425,7 +425,6 @@ void TestApi::fileTagsFilterOverride()
QList<qbs::InstallableFile> installableFiles
= project.installableFilesForProduct(product, qbs::InstallOptions());
QCOMPARE(installableFiles.count(), 1);
- QEXPECT_FAIL(0, "QBS-424", Continue);
QVERIFY(installableFiles.first().targetDirectory().contains("habicht"));
}
@@ -517,6 +516,27 @@ void TestApi::installableFiles()
QCOMPARE(installableFiles.last().targetFilePath(), QLatin1String("/tmp/dir/file2.txt"));
}
+void TestApi::isRunnable()
+{
+ qbs::SetupProjectParameters setupParams = defaultSetupParameters();
+ setupParams.setProjectFilePath(QDir::cleanPath(QLatin1String(SRCDIR "/testdata"
+ "/is-runnable/project.qbs")));
+ QScopedPointer<qbs::SetupProjectJob> job(qbs::Project::setupProject(setupParams,
+ m_logSink, 0));
+ waitForFinished(job.data());
+ QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString()));
+ qbs::Project project = job->project();
+ const QList<qbs::ProductData> products = project.projectData().products();
+ QCOMPARE(products.count(), 2);
+ foreach (const qbs::ProductData &p, products) {
+ QVERIFY2(p.name() == "app" || p.name() == "lib", qPrintable(p.name()));
+ if (p.name() == "app")
+ QVERIFY(p.isRunnable());
+ else
+ QVERIFY(!p.isRunnable());
+ }
+}
+
void TestApi::listBuildSystemFiles()
{
qbs::SetupProjectParameters setupParams = defaultSetupParameters();
diff --git a/tests/auto/api/tst_api.h b/tests/auto/api/tst_api.h
index 9aab60651..0ad65c5e5 100644
--- a/tests/auto/api/tst_api.h
+++ b/tests/auto/api/tst_api.h
@@ -55,6 +55,7 @@ private slots:
void infiniteLoopBuilding_data();
void infiniteLoopResolving();
void installableFiles();
+ void isRunnable();
void listBuildSystemFiles();
void nonexistingProjectPropertyFromProduct();
void nonexistingProjectPropertyFromCommandLine();
diff --git a/tests/auto/blackbox/testdata/build-directories/input.txt b/tests/auto/blackbox/testdata/build-directories/input.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/auto/blackbox/testdata/build-directories/input.txt
diff --git a/tests/auto/blackbox/testdata/build-directories/project.qbs b/tests/auto/blackbox/testdata/build-directories/project.qbs
new file mode 100644
index 000000000..025340a0a
--- /dev/null
+++ b/tests/auto/blackbox/testdata/build-directories/project.qbs
@@ -0,0 +1,49 @@
+import qbs
+
+Project {
+ Product {
+ name: "p1"
+ type: "blubb1"
+ Transformer {
+ Artifact {
+ fileName: "dummy1.txt"
+ fileTags: product.type
+ }
+ prepare: {
+ var cmd = new JavaScriptCommand();
+ cmd.silent = true;
+ cmd.sourceCode = function() {
+ print(product.buildDirectory);
+ }
+ return cmd;
+ }
+ }
+ }
+ Product {
+ name: "p2"
+ type: "blubb2"
+ Depends { name: "p1" }
+ Group {
+ files: "input.txt"
+ fileTags: "input"
+ }
+ Rule {
+ inputs: "input"
+ usings: "blubb1"
+ Artifact {
+ fileName: "dummy2.txt"
+ fileTags: product.type
+ }
+ prepare: {
+ var cmd = new JavaScriptCommand();
+ cmd.silent = true;
+ cmd.sourceCode = function() {
+ print(product.buildDirectory);
+ print(project.buildDirectory);
+ print(project.sourceDirectory);
+ }
+ return cmd;
+ }
+ }
+ }
+}
diff --git a/tests/auto/blackbox/testdata/dynamicRuleOutputs/before/genlexer.qbs b/tests/auto/blackbox/testdata/dynamicRuleOutputs/before/genlexer.qbs
index 61504573f..2c145682a 100644
--- a/tests/auto/blackbox/testdata/dynamicRuleOutputs/before/genlexer.qbs
+++ b/tests/auto/blackbox/testdata/dynamicRuleOutputs/before/genlexer.qbs
@@ -64,12 +64,12 @@ Project {
var sourceFileName = options["outfile"] || "lex.yy.c";
var headerFileName = options["header-file"];
var result = [{
- filePath: "GeneratedFiles/" + product.name + "/" + sourceFileName,
+ filePath: "GeneratedFiles/" + sourceFileName,
fileTags: ["c"]
}];
if (headerFileName) {
result.push({
- filePath: "GeneratedFiles/" + product.name + "/" + headerFileName,
+ filePath: "GeneratedFiles/" + headerFileName,
fileTags: ["hpp"]
});
}
@@ -80,7 +80,7 @@ Project {
if (product.isFlexAvailable) {
// flex is available. Let's call it.
cmd = new Command("flex", [input.filePath]);
- cmd.workingDirectory = product.buildDirectory + "/GeneratedFiles/" + product.name;
+ cmd.workingDirectory = product.buildDirectory + "/GeneratedFiles";
} else {
// No flex available here, generate some C source and header.
cmd = new JavaScriptCommand();
diff --git a/tests/auto/blackbox/testdata/nodejs/hello.js b/tests/auto/blackbox/testdata/nodejs/hello.js
new file mode 100644
index 000000000..43f1e2ffd
--- /dev/null
+++ b/tests/auto/blackbox/testdata/nodejs/hello.js
@@ -0,0 +1,3 @@
+if (console) {
+ console.log("hello world");
+}
diff --git a/tests/auto/blackbox/testdata/nodejs/hello.qbs b/tests/auto/blackbox/testdata/nodejs/hello.qbs
new file mode 100644
index 000000000..e11b1a599
--- /dev/null
+++ b/tests/auto/blackbox/testdata/nodejs/hello.qbs
@@ -0,0 +1,7 @@
+import qbs
+
+NodeJSApplication {
+ nodejs.applicationFile: "hello.js"
+ name: "hello"
+ files: "hello.js"
+}
diff --git a/tests/auto/blackbox/testdata/simpleProbe/main.cpp b/tests/auto/blackbox/testdata/simpleProbe/main.cpp
new file mode 100644
index 000000000..76e819701
--- /dev/null
+++ b/tests/auto/blackbox/testdata/simpleProbe/main.cpp
@@ -0,0 +1 @@
+int main() { return 0; }
diff --git a/tests/auto/blackbox/testdata/simpleProbe/simpleProbe.qbs b/tests/auto/blackbox/testdata/simpleProbe/simpleProbe.qbs
new file mode 100644
index 000000000..b409c7cc6
--- /dev/null
+++ b/tests/auto/blackbox/testdata/simpleProbe/simpleProbe.qbs
@@ -0,0 +1,31 @@
+import qbs 1.0
+import qbs.Probes
+
+CppApplication {
+ Probe {
+ id: probe1
+ property string someString
+ configure: {
+ someString = "one";
+ found = true;
+ }
+ }
+ Probe {
+ id: probe2
+ configure: {
+ found = false;
+ }
+ }
+ name: "MyApp"
+ type: {
+ if (!probe1.found)
+ throw "probe1 not found";
+ if (probe2.found)
+ throw "probe2 unexpectedly found";
+ if (probe1.someString !== "one")
+ throw "probe1.someString expected to be \"one\"."
+ return "application"
+ }
+ files: ["main.cpp"]
+}
+
diff --git a/tests/auto/blackbox/testdata/typescript/animals.ts b/tests/auto/blackbox/testdata/typescript/animals.ts
new file mode 100644
index 000000000..a33ae5c11
--- /dev/null
+++ b/tests/auto/blackbox/testdata/typescript/animals.ts
@@ -0,0 +1,21 @@
+export interface Mammal {
+ speak(): string;
+}
+
+export class Cat implements Mammal {
+ public speak() {
+ return "Meow"; // a cat says meow
+ }
+}
+
+export class Dog implements Mammal {
+ public speak() {
+ return "Woof"; // a dog says woof
+ }
+}
+
+export class Human implements Mammal {
+ public speak() {
+ return "Hello";
+ }
+}
diff --git a/tests/auto/blackbox/testdata/typescript/extra.js b/tests/auto/blackbox/testdata/typescript/extra.js
new file mode 100644
index 000000000..5500e4688
--- /dev/null
+++ b/tests/auto/blackbox/testdata/typescript/extra.js
@@ -0,0 +1,3 @@
+if (console) {
+ console.log("This doesn't do anything useful!");
+}
diff --git a/tests/auto/blackbox/testdata/typescript/foo.ts b/tests/auto/blackbox/testdata/typescript/foo.ts
new file mode 100644
index 000000000..3554d317a
--- /dev/null
+++ b/tests/auto/blackbox/testdata/typescript/foo.ts
@@ -0,0 +1,5 @@
+export class Greeter {
+ public getGreeting(): string {
+ return "guten Tag!";
+ }
+}
diff --git a/tests/auto/blackbox/testdata/typescript/main.ts b/tests/auto/blackbox/testdata/typescript/main.ts
new file mode 100644
index 000000000..c41eebea5
--- /dev/null
+++ b/tests/auto/blackbox/testdata/typescript/main.ts
@@ -0,0 +1,19 @@
+import Animals = require("animals");
+import Foo = require("foo");
+
+function main() {
+ var mammals: Animals.Mammal[] = [];
+ mammals.push(new Animals.Human());
+ mammals.push(new Animals.Dog());
+ mammals.push(new Animals.Cat());
+
+ // Make everyone speak
+ for (var i = 0; i < mammals.length; ++i) {
+ console.log(mammals[i].speak());
+ }
+
+ var greeting: string = (new Foo.Greeter()).getGreeting();
+ console.log(greeting);
+}
+
+main();
diff --git a/tests/auto/blackbox/testdata/typescript/typescript.qbs b/tests/auto/blackbox/testdata/typescript/typescript.qbs
new file mode 100644
index 000000000..8407d1203
--- /dev/null
+++ b/tests/auto/blackbox/testdata/typescript/typescript.qbs
@@ -0,0 +1,34 @@
+import qbs
+
+Project {
+ NodeJSApplication {
+ Depends { name: "typescript" }
+ Depends { name: "lib" }
+
+ typescript.warningLevel: ["pedantic"]
+ typescript.generateDeclarations: true
+ typescript.moduleLoader: "commonjs"
+ nodejs.applicationFile: "main.ts"
+
+ name: "animals"
+
+ files: [
+ "animals.ts",
+ "extra.js",
+ "main.ts"
+ ]
+ }
+
+ Product {
+ Depends { name: "typescript" }
+
+ typescript.generateDeclarations: true
+ typescript.moduleLoader: "commonjs"
+
+ name: "lib"
+
+ files: [
+ "foo.ts"
+ ]
+ }
+}
diff --git a/tests/auto/blackbox/tst_blackbox.cpp b/tests/auto/blackbox/tst_blackbox.cpp
index cc68a57d4..c48eb0611 100644
--- a/tests/auto/blackbox/tst_blackbox.cpp
+++ b/tests/auto/blackbox/tst_blackbox.cpp
@@ -47,6 +47,12 @@ using qbs::Internal::HostOsInfo;
using qbs::Internal::removeDirectoryWithContents;
using qbs::Profile;
+static bool regularFileExists(const QString &filePath)
+{
+ const QFileInfo fi(filePath);
+ return fi.exists() && fi.isFile();
+}
+
static QString initQbsExecutableFilePath()
{
QString filePath = QCoreApplication::applicationDirPath() + QLatin1String("/qbs");
@@ -173,7 +179,7 @@ QByteArray TestBlackbox::unifiedLineEndings(const QByteArray &ba)
void TestBlackbox::initTestCase()
{
- QVERIFY(QFile::exists(qbsExecutableFilePath));
+ QVERIFY(regularFileExists(qbsExecutableFilePath));
SettingsPtr settings = qbsSettings(QString());
if (!settings->profiles().contains(buildProfileName))
@@ -259,49 +265,71 @@ void TestBlackbox::baseProperties()
QCOMPARE(runQbs(), 0);
}
+void TestBlackbox::buildDirectories()
+{
+ const QString projectDir
+ = QDir::cleanPath(testDataDir + QLatin1String("/build-directories"));
+ const QString projectBuildDir = projectDir + '/' + buildDir;
+ QDir::setCurrent(projectDir);
+ QCOMPARE(runQbs(QStringList("-qq")), 0);
+ const QStringList outputLines
+ = QString::fromLocal8Bit(m_qbsStderr.trimmed()).split('\n', QString::SkipEmptyParts);
+ QCOMPARE(outputLines.count(), 4);
+ QCOMPARE(outputLines.at(0).trimmed(), projectBuildDir + "/p1");
+ QCOMPARE(outputLines.at(1).trimmed(), projectBuildDir + "/p2");
+ QCOMPARE(outputLines.at(2).trimmed(), projectBuildDir);
+ QCOMPARE(outputLines.at(3).trimmed(), projectDir);
+}
+
void TestBlackbox::build_project_data()
{
QTest::addColumn<QString>("projectSubDir");
QTest::addColumn<QString>("productFileName");
QTest::newRow("BPs in Sources")
<< QString("buildproperties_source")
- << QString(HostOsInfo::appendExecutableSuffix(buildDir + "/HelloWorld"));
+ << QString(HostOsInfo::appendExecutableSuffix(buildDir + "/HelloWorld/HelloWorld"));
QTest::newRow("code generator")
<< QString("codegen")
- << QString(HostOsInfo::appendExecutableSuffix(buildDir + "/codegen"));
+ << QString(HostOsInfo::appendExecutableSuffix(buildDir + "/codegen/codegen"));
QTest::newRow("link static libs")
<< QString("link_staticlib")
<< QString(buildDir + QLatin1String("/")
- + HostOsInfo::appendExecutableSuffix("HelloWorld"));
+ + HostOsInfo::appendExecutableSuffix("HelloWorld/HelloWorld"));
QTest::newRow("precompiled header")
<< QString("precompiledHeader")
<< QString(buildDir + QLatin1String("/")
- + HostOsInfo::appendExecutableSuffix("MyApp"));
+ + HostOsInfo::appendExecutableSuffix("MyApp/MyApp"));
QTest::newRow("lots of dots")
<< QString("lotsofdots")
<< QString(buildDir + QLatin1String("/")
- + HostOsInfo::appendExecutableSuffix("lots.of.dots"));
+ + HostOsInfo::appendExecutableSuffix("lots.of.dots/lots.of.dots"));
QTest::newRow("Qt5 plugin")
<< QString("qt5plugin")
- << QString(buildDir + QLatin1String("/") + HostOsInfo::dynamicLibraryName("echoplugin"));
+ << QString(buildDir + QLatin1String("/echoplugin/")
+ + HostOsInfo::dynamicLibraryName("echoplugin"));
QTest::newRow("Q_OBJECT in source")
<< QString("moc_cpp")
- << QString(HostOsInfo::appendExecutableSuffix(buildDir + "/moc_cpp"));
+ << QString(HostOsInfo::appendExecutableSuffix(buildDir + "/moc_cpp/moc_cpp"));
QTest::newRow("Q_OBJECT in header")
<< QString("moc_hpp")
- << QString(HostOsInfo::appendExecutableSuffix(buildDir + "/moc_hpp"));
+ << QString(HostOsInfo::appendExecutableSuffix(buildDir + "/moc_hpp/moc_hpp"));
QTest::newRow("Q_OBJECT in header, moc_XXX.cpp included")
<< QString("moc_hpp_included")
- << QString(HostOsInfo::appendExecutableSuffix(buildDir + "/moc_hpp_included"));
+ << QString(HostOsInfo::appendExecutableSuffix(buildDir
+ + "/moc_hpp_included/moc_hpp_included"));
QTest::newRow("app and lib with same source file")
<< QString("lib_samesource")
- << QString(HostOsInfo::appendExecutableSuffix(buildDir + "/HelloWorldApp"));
+ << QString(HostOsInfo::appendExecutableSuffix(buildDir
+ + "/HelloWorldApp/HelloWorldApp"));
QTest::newRow("source files with the same base name but different extensions")
<< QString("sameBaseName")
- << QString(HostOsInfo::appendExecutableSuffix(buildDir + "/basename"));
+ << QString(HostOsInfo::appendExecutableSuffix(buildDir + "/basename/basename"));
QTest::newRow("static library dependencies")
<< QString("staticLibDeps")
- << QString(HostOsInfo::appendExecutableSuffix(buildDir + "/staticLibDeps"));
+ << QString(HostOsInfo::appendExecutableSuffix(buildDir + "/staticLibDeps/staticLibDeps"));
+ QTest::newRow("simple probes")
+ << QString("simpleProbe")
+ << QString(HostOsInfo::appendExecutableSuffix(buildDir + "/MyApp/MyApp"));
}
void TestBlackbox::build_project()
@@ -315,13 +343,13 @@ void TestBlackbox::build_project()
rmDirR(buildDir);
QCOMPARE(runQbs(), 0);
- QVERIFY2(QFile::exists(productFileName), qPrintable(productFileName));
- QVERIFY(QFile::exists(buildGraphPath));
+ QVERIFY2(regularFileExists(productFileName), qPrintable(productFileName));
+ QVERIFY(regularFileExists(buildGraphPath));
QVERIFY2(QFile::remove(productFileName), qPrintable(productFileName));
waitForNewTimestamp();
QCOMPARE(runQbs(QbsRunParameters(QStringList("--check-timestamps"))), 0);
- QVERIFY2(QFile::exists(productFileName), qPrintable(productFileName));
- QVERIFY(QFile::exists(buildGraphPath));
+ QVERIFY2(regularFileExists(productFileName), qPrintable(productFileName));
+ QVERIFY(regularFileExists(buildGraphPath));
}
void TestBlackbox::build_project_dry_run_data()
@@ -387,7 +415,7 @@ void TestBlackbox::dependenciesProperty()
{
QDir::setCurrent(testDataDir + QLatin1String("/dependenciesProperty"));
QCOMPARE(runQbs(), 0);
- QFile depsFile(buildDir + QLatin1String("/product1.deps"));
+ QFile depsFile(buildDir + QLatin1String("/product1/product1.deps"));
QVERIFY(depsFile.open(QFile::ReadOnly));
QString deps = QString::fromLatin1(depsFile.readAll());
QVERIFY(!deps.isEmpty());
@@ -445,7 +473,7 @@ void TestBlackbox::resolve_project()
QCOMPARE(runQbs(QbsRunParameters("resolve")), 0);
QVERIFY2(!QFile::exists(productFileName), qPrintable(productFileName));
- QVERIFY(QFile::exists(buildGraphPath));
+ QVERIFY(regularFileExists(buildGraphPath));
}
void TestBlackbox::resolve_project_dry_run_data()
@@ -475,10 +503,10 @@ static bool symlinkExists(const QString &linkFilePath)
void TestBlackbox::clean()
{
- const QString appObjectFilePath = buildDir + "/.obj/app/main.cpp" + QTC_HOST_OBJECT_SUFFIX;
- const QString appExeFilePath = buildDir + "/app" + QTC_HOST_EXE_SUFFIX;
- const QString depObjectFilePath = buildDir + "/.obj/dep/dep.cpp" + QTC_HOST_OBJECT_SUFFIX;
- const QString depLibBase = buildDir + '/' + QTC_HOST_DYNAMICLIB_PREFIX + "dep";
+ const QString appObjectFilePath = buildDir + "/app/.obj/main.cpp" + QTC_HOST_OBJECT_SUFFIX;
+ const QString appExeFilePath = buildDir + "/app/app" + QTC_HOST_EXE_SUFFIX;
+ const QString depObjectFilePath = buildDir + "/dep/.obj/dep.cpp" + QTC_HOST_OBJECT_SUFFIX;
+ const QString depLibBase = buildDir + "/dep/" + QTC_HOST_DYNAMICLIB_PREFIX + "dep";
QString depLibFilePath;
QStringList symlinks;
if (qbs::Internal::HostOsInfo::isOsxHost()) {
@@ -499,24 +527,24 @@ void TestBlackbox::clean()
// Default behavior: Remove only temporaries.
QCOMPARE(runQbs(), 0);
- QVERIFY(QFile(appObjectFilePath).exists());
- QVERIFY(QFile(appExeFilePath).exists());
- QVERIFY(QFile(depObjectFilePath).exists());
- QVERIFY(QFile(depLibFilePath).exists());
+ QVERIFY(regularFileExists(appObjectFilePath));
+ QVERIFY(regularFileExists(appExeFilePath));
+ QVERIFY(regularFileExists(depObjectFilePath));
+ QVERIFY(regularFileExists(depLibFilePath));
foreach (const QString &symLink, symlinks)
- QVERIFY2(QFile(symLink).exists(), qPrintable(symLink));
+ QVERIFY2(regularFileExists(symLink), qPrintable(symLink));
QCOMPARE(runQbs(QbsRunParameters("clean")), 0);
QVERIFY(!QFile(appObjectFilePath).exists());
- QVERIFY(QFile(appExeFilePath).exists());
+ QVERIFY(regularFileExists(appExeFilePath));
QVERIFY(!QFile(depObjectFilePath).exists());
- QVERIFY(QFile(depLibFilePath).exists());
+ QVERIFY(regularFileExists(depLibFilePath));
foreach (const QString &symLink, symlinks)
QVERIFY2(symlinkExists(symLink), qPrintable(symLink));
// Remove all.
QCOMPARE(runQbs(), 0);
- QVERIFY(QFile(appObjectFilePath).exists());
- QVERIFY(QFile(appExeFilePath).exists());
+ QVERIFY(regularFileExists(appObjectFilePath));
+ QVERIFY(regularFileExists(appExeFilePath));
QCOMPARE(runQbs(QbsRunParameters(QLatin1String("clean"), QStringList("--all-artifacts"))), 0);
QVERIFY(!QFile(appObjectFilePath).exists());
QVERIFY(!QFile(appExeFilePath).exists());
@@ -527,28 +555,28 @@ void TestBlackbox::clean()
// Dry run.
QCOMPARE(runQbs(), 0);
- QVERIFY(QFile(appObjectFilePath).exists());
- QVERIFY(QFile(appExeFilePath).exists());
+ QVERIFY(regularFileExists(appObjectFilePath));
+ QVERIFY(regularFileExists(appExeFilePath));
QCOMPARE(runQbs(QbsRunParameters(QLatin1String("clean"),
QStringList("--all-artifacts") << "-n")), 0);
- QVERIFY(QFile(appObjectFilePath).exists());
- QVERIFY(QFile(appExeFilePath).exists());
- QVERIFY(QFile(depObjectFilePath).exists());
- QVERIFY(QFile(depLibFilePath).exists());
+ QVERIFY(regularFileExists(appObjectFilePath));
+ QVERIFY(regularFileExists(appExeFilePath));
+ QVERIFY(regularFileExists(depObjectFilePath));
+ QVERIFY(regularFileExists(depLibFilePath));
foreach (const QString &symLink, symlinks)
QVERIFY2(symlinkExists(symLink), qPrintable(symLink));
// Product-wise, dependency only.
QCOMPARE(runQbs(), 0);
- QVERIFY(QFile(appObjectFilePath).exists());
- QVERIFY(QFile(appExeFilePath).exists());
- QVERIFY(QFile(depObjectFilePath).exists());
- QVERIFY(QFile(depLibFilePath).exists());
+ QVERIFY(regularFileExists(appObjectFilePath));
+ QVERIFY(regularFileExists(appExeFilePath));
+ QVERIFY(regularFileExists(depObjectFilePath));
+ QVERIFY(regularFileExists(depLibFilePath));
QCOMPARE(runQbs(QbsRunParameters(QLatin1String("clean"),
QStringList("--all-artifacts") << "-p" << "dep")),
0);
- QVERIFY(QFile(appObjectFilePath).exists());
- QVERIFY(QFile(appExeFilePath).exists());
+ QVERIFY(regularFileExists(appObjectFilePath));
+ QVERIFY(regularFileExists(appExeFilePath));
QVERIFY(!QFile(depObjectFilePath).exists());
QVERIFY(!QFile(depLibFilePath).exists());
foreach (const QString &symLink, symlinks)
@@ -556,17 +584,17 @@ void TestBlackbox::clean()
// Product-wise, dependent product only.
QCOMPARE(runQbs(), 0);
- QVERIFY(QFile(appObjectFilePath).exists());
- QVERIFY(QFile(appExeFilePath).exists());
- QVERIFY(QFile(depObjectFilePath).exists());
- QVERIFY(QFile(depLibFilePath).exists());
+ QVERIFY(regularFileExists(appObjectFilePath));
+ QVERIFY(regularFileExists(appExeFilePath));
+ QVERIFY(regularFileExists(depObjectFilePath));
+ QVERIFY(regularFileExists(depLibFilePath));
QCOMPARE(runQbs(QbsRunParameters(QLatin1String("clean"),
QStringList("--all-artifacts") << "-p" << "app")),
0);
QVERIFY(!QFile(appObjectFilePath).exists());
QVERIFY(!QFile(appExeFilePath).exists());
- QVERIFY(QFile(depObjectFilePath).exists());
- QVERIFY(QFile(depLibFilePath).exists());
+ QVERIFY(regularFileExists(depObjectFilePath));
+ QVERIFY(regularFileExists(depLibFilePath));
foreach (const QString &symLink, symlinks)
QVERIFY2(symlinkExists(symLink), qPrintable(symLink));
}
@@ -708,8 +736,8 @@ void TestBlackbox::track_qrc()
{
QDir::setCurrent(testDataDir + "/qrc");
QCOMPARE(runQbs(), 0);
- const QString fileName = HostOsInfo::appendExecutableSuffix(buildDir + "/i");
- QVERIFY2(QFile(fileName).exists(), qPrintable(fileName));
+ const QString fileName = buildDir + "/i/" + HostOsInfo::appendExecutableSuffix("i");
+ QVERIFY2(regularFileExists(fileName), qPrintable(fileName));
QDateTime dt = QFileInfo(fileName).lastModified();
QTest::qSleep(2020);
{
@@ -720,7 +748,7 @@ void TestBlackbox::track_qrc()
f.close();
}
QCOMPARE(runQbs(), 0);
- QVERIFY(QFile(fileName).exists());
+ QVERIFY(regularFileExists(fileName));
QVERIFY(dt < QFileInfo(fileName).lastModified());
}
@@ -731,18 +759,18 @@ void TestBlackbox::track_qobject_change()
QVERIFY(QFile("bla_qobject.h").copy("bla.h"));
touch("bla.h");
QCOMPARE(runQbs(), 0);
- const QString productFilePath = HostOsInfo::appendExecutableSuffix(buildDir + "/i");
- QVERIFY2(QFile(productFilePath).exists(), qPrintable(productFilePath));
+ const QString productFilePath = buildDir + "/i/" + HostOsInfo::appendExecutableSuffix("i");
+ QVERIFY2(regularFileExists(productFilePath), qPrintable(productFilePath));
QString moc_bla_objectFileName
- = buildDir + "/.obj/i/GeneratedFiles/i/moc_bla.cpp" QTC_HOST_OBJECT_SUFFIX;
- QVERIFY(QFile(moc_bla_objectFileName).exists());
+ = buildDir + "/i/.obj/i/GeneratedFiles/moc_bla.cpp" QTC_HOST_OBJECT_SUFFIX;
+ QVERIFY2(regularFileExists(moc_bla_objectFileName), qPrintable(moc_bla_objectFileName));
QTest::qSleep(1000);
QFile("bla.h").remove();
QVERIFY(QFile("bla_noqobject.h").copy("bla.h"));
touch("bla.h");
QCOMPARE(runQbs(), 0);
- QVERIFY(QFile(productFilePath).exists());
+ QVERIFY(regularFileExists(productFilePath));
QVERIFY(!QFile(moc_bla_objectFileName).exists());
}
@@ -758,7 +786,7 @@ void TestBlackbox::trackAddFile()
QDir::setCurrent(testDataDir + "/trackAddFile/work");
QCOMPARE(runQbs(), 0);
- process.start(buildDir + "/someapp");
+ process.start(buildDir + "/someapp/someapp");
QVERIFY2(process.waitForStarted(), qPrintable(process.errorString()));
QVERIFY2(process.waitForFinished(), qPrintable(process.errorString()));
QCOMPARE(process.exitCode(), 0);
@@ -774,7 +802,7 @@ void TestBlackbox::trackAddFile()
touch("main.cpp");
QCOMPARE(runQbs(), 0);
- process.start(buildDir + "/someapp");
+ process.start(buildDir + "/someapp/someapp");
QVERIFY(process.waitForStarted());
QVERIFY(process.waitForFinished());
QCOMPARE(process.exitCode(), 0);
@@ -863,7 +891,7 @@ void TestBlackbox::trackRemoveFile()
QDir::setCurrent(testDataDir + "/trackAddFile/work");
QCOMPARE(runQbs(), 0);
- process.start(buildDir + "/someapp");
+ process.start(buildDir + "/someapp/someapp");
QVERIFY2(process.waitForStarted(), qPrintable(process.errorString()));
QVERIFY2(process.waitForFinished(), qPrintable(process.errorString()));
QCOMPARE(process.exitCode(), 0);
@@ -887,7 +915,7 @@ void TestBlackbox::trackRemoveFile()
touch("project.qbs");
QCOMPARE(runQbs(), 0);
- process.start(buildDir + "/someapp");
+ process.start(buildDir + "/someapp/someapp");
QVERIFY(process.waitForStarted());
QVERIFY(process.waitForFinished());
QCOMPARE(process.exitCode(), 0);
@@ -900,7 +928,7 @@ void TestBlackbox::trackRemoveFile()
QCOMPARE(unchangedObjectFileTime1, unchangedObjectFileTime2);
// the object file for the removed cpp file should have vanished too
- QCOMPARE(QFile::exists(buildDir + "/someapp/zort.cpp" QTC_HOST_OBJECT_SUFFIX), false);
+ QCOMPARE(regularFileExists(buildDir + "/someapp/zort.cpp" QTC_HOST_OBJECT_SUFFIX), false);
}
void TestBlackbox::trackAddFileTag()
@@ -915,7 +943,7 @@ void TestBlackbox::trackAddFileTag()
QDir::setCurrent(testDataDir + "/trackFileTags/work");
QCOMPARE(runQbs(), 0);
- process.start(buildDir + "/someapp");
+ process.start(buildDir + "/someapp/someapp");
QVERIFY2(process.waitForStarted(), qPrintable(process.errorString()));
QVERIFY2(process.waitForFinished(), qPrintable(process.errorString()));
QCOMPARE(process.exitCode(), 0);
@@ -928,7 +956,7 @@ void TestBlackbox::trackAddFileTag()
touch("project.qbs");
QCOMPARE(runQbs(), 0);
- process.start(buildDir + "/someapp");
+ process.start(buildDir + "/someapp/someapp");
QVERIFY(process.waitForStarted());
QVERIFY(process.waitForFinished());
QCOMPARE(process.exitCode(), 0);
@@ -949,12 +977,11 @@ void TestBlackbox::trackRemoveFileTag()
QCOMPARE(runQbs(), 0);
// check if the artifacts are here that will become stale in the 2nd step
- QVERIFY2(QFile::exists(buildDir + "/.obj/someapp/main_foo.cpp" QTC_HOST_OBJECT_SUFFIX),
- qPrintable(buildDir + "/.obj/someapp/main_foo.cpp" QTC_HOST_OBJECT_SUFFIX));
- QVERIFY2(QFile::exists(buildDir + "/main_foo.cpp"), qPrintable(buildDir + "/main_foo.cpp"));
- QVERIFY2(QFile::exists(buildDir + "/main.foo"), qPrintable(buildDir + "/main.foo"));
+ QVERIFY(regularFileExists(buildDir + "/someapp/.obj/someapp/main_foo.cpp" QTC_HOST_OBJECT_SUFFIX));
+ QVERIFY(regularFileExists(buildDir + "/someapp/main_foo.cpp"));
+ QVERIFY(regularFileExists(buildDir + "/someapp/main.foo"));
- process.start(buildDir + "/someapp");
+ process.start(buildDir + "/someapp/someapp");
QVERIFY(process.waitForStarted());
QVERIFY(process.waitForFinished());
QCOMPARE(process.exitCode(), 0);
@@ -967,7 +994,7 @@ void TestBlackbox::trackRemoveFileTag()
touch("project.qbs");
QCOMPARE(runQbs(), 0);
- process.start(buildDir + "/someapp");
+ process.start(buildDir + "/someapp/someapp");
QVERIFY(process.waitForStarted());
QVERIFY(process.waitForFinished());
QCOMPARE(process.exitCode(), 0);
@@ -975,9 +1002,9 @@ void TestBlackbox::trackRemoveFileTag()
QCOMPARE(output.takeFirst().trimmed().constData(), "there's no foo here");
// check if stale artifacts have been removed
- QCOMPARE(QFile::exists(buildDir + "/someapp/main_foo.cpp" QTC_HOST_OBJECT_SUFFIX), false);
- QCOMPARE(QFile::exists(buildDir + "/someapp/main_foo.cpp"), false);
- QCOMPARE(QFile::exists(buildDir + "/someapp/main.foo"), false);
+ QCOMPARE(regularFileExists(buildDir + "/someapp/.obj/main_foo.cpp" QTC_HOST_OBJECT_SUFFIX), false);
+ QCOMPARE(regularFileExists(buildDir + "/someapp/main_foo.cpp"), false);
+ QCOMPARE(regularFileExists(buildDir + "/someapp/main.foo"), false);
}
void TestBlackbox::trackAddMocInclude()
@@ -1108,10 +1135,12 @@ void TestBlackbox::ruleConditions()
{
QDir::setCurrent(testDataDir + "/ruleConditions");
QCOMPARE(runQbs(), 0);
- QVERIFY(QFileInfo(buildDir + HostOsInfo::appendExecutableSuffix("/zorted")).exists());
- QVERIFY(QFileInfo(buildDir + HostOsInfo::appendExecutableSuffix("/unzorted")).exists());
- QVERIFY(QFileInfo(buildDir + "/zorted.foo.narf.zort").exists());
- QVERIFY(!QFileInfo(buildDir + "/unzorted.foo.narf.zort").exists());
+ QVERIFY(QFileInfo(buildDir + "/zorted/"
+ + HostOsInfo::appendExecutableSuffix("zorted")).exists());
+ QVERIFY(QFileInfo(buildDir + "/unzorted/"
+ + HostOsInfo::appendExecutableSuffix("unzorted")).exists());
+ QVERIFY(QFileInfo(buildDir + "/zorted/zorted.foo.narf.zort").exists());
+ QVERIFY(!QFileInfo(buildDir + "/unzorted/unzorted.foo.narf.zort").exists());
}
void TestBlackbox::ruleCycle()
@@ -1175,7 +1204,8 @@ void TestBlackbox::overrideProjectProperties()
<< QLatin1String("project.someInt:156")
<< QLatin1String("project.someStringList:one")
<< QLatin1String("MyAppForYou.mainFile:main.cpp"))), 0);
- QVERIFY(QFile::exists(buildDir + HostOsInfo::appendExecutableSuffix("/MyAppForYou")));
+ QVERIFY(regularFileExists(buildDir + "/MyAppForYou/"
+ + HostOsInfo::appendExecutableSuffix("MyAppForYou")));
QVERIFY(QFile::remove(buildGraphPath));
QbsRunParameters params;
@@ -1196,7 +1226,8 @@ void TestBlackbox::productProperties()
QDir::setCurrent(testDataDir + "/productproperties");
QCOMPARE(runQbs(QbsRunParameters(QStringList() << QLatin1String("-f")
<< QLatin1String("project.qbs"))), 0);
- QVERIFY(QFile::exists(buildDir + HostOsInfo::appendExecutableSuffix("/blubb_user")));
+ QVERIFY(regularFileExists(buildDir + "/blubb_user/"
+ + HostOsInfo::appendExecutableSuffix("blubb_user")));
}
void TestBlackbox::propertyChanges()
@@ -1214,7 +1245,7 @@ void TestBlackbox::propertyChanges()
QVERIFY(m_qbsStdout.contains("linking product 1.debug"));
QVERIFY(m_qbsStdout.contains("generated.txt"));
QVERIFY(m_qbsStdout.contains("Making output from input"));
- QFile generatedFile(buildDir + QLatin1String("/generated.txt"));
+ QFile generatedFile(buildDir + QLatin1String("/generated text file/generated.txt"));
QVERIFY(generatedFile.open(QIODevice::ReadOnly));
QCOMPARE(generatedFile.readAll(), QByteArray("prefix 1contents 1suffix 1"));
generatedFile.close();
@@ -1480,15 +1511,15 @@ void TestBlackbox::dynamicRuleOutputs()
QDir::setCurrent(testDir + "/work");
QCOMPARE(runQbs(), 0);
- const QString appFile = buildDir + "/genlexer" + QTC_HOST_EXE_SUFFIX;
- const QString headerFile1 = buildDir + "/GeneratedFiles/genlexer/numberscanner.h";
- const QString sourceFile1 = buildDir + "/GeneratedFiles/genlexer/numberscanner.c";
- const QString sourceFile2 = buildDir + "/GeneratedFiles/genlexer/lex.yy.c";
+ const QString appFile = buildDir + "/genlexer/genlexer" + QTC_HOST_EXE_SUFFIX;
+ const QString headerFile1 = buildDir + "/genlexer/GeneratedFiles/numberscanner.h";
+ const QString sourceFile1 = buildDir + "/genlexer/GeneratedFiles/numberscanner.c";
+ const QString sourceFile2 = buildDir + "/genlexer/GeneratedFiles/lex.yy.c";
// Check build #1: source and header file name are specified in numbers.l
- QVERIFY(QFile::exists(appFile));
- QVERIFY(QFile::exists(headerFile1));
- QVERIFY(QFile::exists(sourceFile1));
+ QVERIFY(regularFileExists(appFile));
+ QVERIFY(regularFileExists(headerFile1));
+ QVERIFY(regularFileExists(sourceFile1));
QVERIFY(!QFile::exists(sourceFile2));
QDateTime appFileTimeStamp1 = QFileInfo(appFile).lastModified();
@@ -1504,7 +1535,7 @@ void TestBlackbox::dynamicRuleOutputs()
QVERIFY(appFileTimeStamp1 < appFileTimeStamp2);
QVERIFY(!QFile::exists(headerFile1));
QVERIFY(!QFile::exists(sourceFile1));
- QVERIFY(QFile::exists(sourceFile2));
+ QVERIFY(regularFileExists(sourceFile2));
waitForNewTimestamp();
QFile::remove("numbers.l");
@@ -1515,9 +1546,9 @@ void TestBlackbox::dynamicRuleOutputs()
// Check build #3: source and header file name are specified in numbers.l
QDateTime appFileTimeStamp3 = QFileInfo(appFile).lastModified();
QVERIFY(appFileTimeStamp2 < appFileTimeStamp3);
- QVERIFY(QFile::exists(appFile));
- QVERIFY(QFile::exists(headerFile1));
- QVERIFY(QFile::exists(sourceFile1));
+ QVERIFY(regularFileExists(appFile));
+ QVERIFY(regularFileExists(headerFile1));
+ QVERIFY(regularFileExists(sourceFile1));
QVERIFY(!QFile::exists(sourceFile2));
}
@@ -1541,8 +1572,9 @@ void TestBlackbox::fileDependencies()
QCOMPARE(runQbs(), 0);
QVERIFY(m_qbsStdout.contains("compiling narf.cpp"));
QVERIFY(m_qbsStdout.contains("compiling zort.cpp"));
- const QString productFileName = HostOsInfo::appendExecutableSuffix(buildDir + "/myapp");
- QVERIFY2(QFile::exists(productFileName), qPrintable(productFileName));
+ const QString productFileName = buildDir + "/myapp/"
+ + HostOsInfo::appendExecutableSuffix("myapp");
+ QVERIFY2(regularFileExists(productFileName), qPrintable(productFileName));
// Incremental build without changes.
QCOMPARE(runQbs(), 0);
@@ -1720,7 +1752,8 @@ void TestBlackbox::qmlDebugging()
QDir::setCurrent(testDataDir + "/qml-debugging");
QCOMPARE(runQbs(), 0);
QProcess nm;
- nm.start("nm", QStringList(HostOsInfo::appendExecutableSuffix(buildDir + "/debuggable-app")));
+ nm.start("nm", QStringList(HostOsInfo::appendExecutableSuffix(buildDir
+ + "/debuggable-app/debuggable-app")));
if (nm.waitForStarted()) { // Let's ignore hosts without nm.
QVERIFY2(nm.waitForFinished(), qPrintable(nm.errorString()));
QVERIFY2(nm.exitCode() == 0, nm.readAllStandardError().constData());
@@ -1758,12 +1791,12 @@ void TestBlackbox::installedApp()
QDir::setCurrent(testDataDir + "/installed_artifact");
QCOMPARE(runQbs(QbsRunParameters("install")), 0);
- QVERIFY(QFile::exists(defaultInstallRoot
+ QVERIFY(regularFileExists(defaultInstallRoot
+ HostOsInfo::appendExecutableSuffix(QLatin1String("/usr/bin/installedApp"))));
QCOMPARE(runQbs(QbsRunParameters(QLatin1String("install"), QStringList("--install-root")
<< (testDataDir + "/installed-app"))), 0);
- QVERIFY(QFile::exists(testDataDir
+ QVERIFY(regularFileExists(testDataDir
+ HostOsInfo::appendExecutableSuffix("/installed-app/usr/bin/installedApp")));
QFile addedFile(defaultInstallRoot + QLatin1String("/blubb.txt"));
@@ -1771,9 +1804,9 @@ void TestBlackbox::installedApp()
addedFile.close();
QVERIFY(addedFile.exists());
QCOMPARE(runQbs(QbsRunParameters(QLatin1String("install"), QStringList("--remove-first"))), 0);
- QVERIFY(QFile::exists(defaultInstallRoot
+ QVERIFY(regularFileExists(defaultInstallRoot
+ HostOsInfo::appendExecutableSuffix(QLatin1String("/usr/bin/installedApp"))));
- QVERIFY(QFile::exists(defaultInstallRoot + QLatin1String("/usr/src/main.cpp")));
+ QVERIFY(regularFileExists(defaultInstallRoot + QLatin1String("/usr/src/main.cpp")));
QVERIFY(!addedFile.exists());
// Check whether changing install parameters on the product causes re-installation.
@@ -1786,9 +1819,9 @@ void TestBlackbox::installedApp()
projectFile.write(content);
QVERIFY(projectFile.flush());
QCOMPARE(runQbs(QbsRunParameters(QLatin1String("install"))), 0);
- QVERIFY(QFile::exists(defaultInstallRoot
+ QVERIFY(regularFileExists(defaultInstallRoot
+ HostOsInfo::appendExecutableSuffix(QLatin1String("/usr/local/bin/installedApp"))));
- QVERIFY(QFile::exists(defaultInstallRoot + QLatin1String("/usr/local/src/main.cpp")));
+ QVERIFY(regularFileExists(defaultInstallRoot + QLatin1String("/usr/local/src/main.cpp")));
// Check whether changing install parameters on the artifact causes re-installation.
content.replace("qbs.installDir: \"bin\"", "qbs.installDir: 'custom'");
@@ -1797,7 +1830,7 @@ void TestBlackbox::installedApp()
projectFile.write(content);
QVERIFY(projectFile.flush());
QCOMPARE(runQbs(QbsRunParameters(QLatin1String("install"))), 0);
- QVERIFY(QFile::exists(defaultInstallRoot
+ QVERIFY(regularFileExists(defaultInstallRoot
+ HostOsInfo::appendExecutableSuffix(QLatin1String("/usr/local/custom/installedApp"))));
// Check whether changing install parameters on a source file causes re-installation.
@@ -1807,7 +1840,7 @@ void TestBlackbox::installedApp()
projectFile.write(content);
projectFile.close();
QCOMPARE(runQbs(QbsRunParameters(QLatin1String("install"))), 0);
- QVERIFY(QFile::exists(defaultInstallRoot + QLatin1String("/usr/local/source/main.cpp")));
+ QVERIFY(regularFileExists(defaultInstallRoot + QLatin1String("/usr/local/source/main.cpp")));
rmDirR(buildDir);
QbsRunParameters params;
@@ -1910,7 +1943,7 @@ void TestBlackbox::testNsis()
bool haveMakeNsis = false;
foreach (const QString &path, paths) {
- if (QFile::exists(QDir::fromNativeSeparators(path) +
+ if (regularFileExists(QDir::fromNativeSeparators(path) +
HostOsInfo::appendExecutableSuffix(QLatin1String("/makensis")))) {
haveMakeNsis = true;
break;
@@ -1970,9 +2003,9 @@ static bool haveWiX()
}
foreach (const QString &path, paths) {
- if (QFile::exists(QDir::fromNativeSeparators(path) +
+ if (regularFileExists(QDir::fromNativeSeparators(path) +
HostOsInfo::appendExecutableSuffix(QLatin1String("/candle"))) &&
- QFile::exists(QDir::fromNativeSeparators(path) +
+ regularFileExists(QDir::fromNativeSeparators(path) +
HostOsInfo::appendExecutableSuffix(QLatin1String("/light")))) {
return true;
}
@@ -2003,8 +2036,67 @@ void TestBlackbox::testWiX()
QVERIFY(m_qbsStdout.contains("compiling QbsBootstrapper.wxs"));
QVERIFY(m_qbsStdout.contains("linking qbs-" + arch + ".msi"));
QVERIFY(m_qbsStdout.contains("linking qbs-setup-" + arch + ".exe"));
- QVERIFY(QFile::exists(buildDir + "/qbs-" + arch + ".msi"));
- QVERIFY(QFile::exists(buildDir + "/qbs-setup-" + arch + ".exe"));
+ QVERIFY(regularFileExists(buildDir + "/qbs-" + arch + ".msi"));
+ QVERIFY(regularFileExists(buildDir + "/qbs-setup-" + arch + ".exe"));
+}
+
+static QString findExecutable(const QStringList &fileNames)
+{
+ const QStringList path = QString::fromLocal8Bit(qgetenv("PATH"))
+ .split(HostOsInfo::pathListSeparator(), QString::SkipEmptyParts);
+
+ foreach (const QString &fileName, fileNames) {
+ foreach (const QString &ppath, path) {
+ const QString fullPath = ppath + QLatin1Char('/') + fileName;
+ if (QFileInfo(fullPath).exists())
+ return QDir::cleanPath(fullPath);
+ }
+ }
+ return QString();
+}
+
+static bool haveNodeJs()
+{
+ // The Node.js binary is called nodejs on Debian/Ubuntu-family operating systems due to a
+ // conflict with another package containing a binary named node
+ return !findExecutable(QStringList()
+ << QLatin1String("nodejs")
+ << QLatin1String("node")).isEmpty();
+}
+
+void TestBlackbox::testNodeJs()
+{
+ if (!haveNodeJs()) {
+ SKIP_TEST("Node.js is not installed");
+ return;
+ }
+
+ QDir::setCurrent(testDataDir + QLatin1String("/nodejs"));
+
+ QbsRunParameters params;
+ params.command = QLatin1String("run");
+ QCOMPARE(runQbs(params), 0);
+ QVERIFY((bool)m_qbsStdout.contains("hello world"));
+ QVERIFY(regularFileExists(buildDir + "/hello/hello.js"));
+}
+
+void TestBlackbox::testTypeScript()
+{
+ if (!haveNodeJs()) {
+ SKIP_TEST("node.js is not installed");
+ return;
+ }
+
+ QDir::setCurrent(testDataDir + QLatin1String("/typescript"));
+
+ QbsRunParameters params;
+ params.command = QLatin1String("run");
+ params.arguments = QStringList() << "-p" << "animals";
+ QCOMPARE(runQbs(params), 0);
+
+ QVERIFY(regularFileExists(buildDir + "/animals/animals.js"));
+ QVERIFY(regularFileExists(buildDir + "/animals/extra.js"));
+ QVERIFY(regularFileExists(buildDir + "/animals/main.js"));
}
QTEST_MAIN(TestBlackbox)
diff --git a/tests/auto/blackbox/tst_blackbox.h b/tests/auto/blackbox/tst_blackbox.h
index 87008e266..fa0128022 100644
--- a/tests/auto/blackbox/tst_blackbox.h
+++ b/tests/auto/blackbox/tst_blackbox.h
@@ -99,6 +99,7 @@ private slots:
void addedFilePersistent();
void addQObjectMacroToCppFile();
void baseProperties();
+ void buildDirectories();
void build_project_data();
void build_project();
void build_project_dry_run_data();
@@ -170,6 +171,8 @@ private slots:
void testNsis();
void testEmbedInfoPlist();
void testWiX();
+ void testNodeJs();
+ void testTypeScript();
private:
QByteArray m_qbsStderr;
diff --git a/version.js b/version.js
index 0c2cf389a..3a7d7de44 100644
--- a/version.js
+++ b/version.js
@@ -1 +1 @@
-function qbsVersion() { return "1.2.1"; }
+function qbsVersion() { return "1.3.0"; }