aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Kandeler <christian.kandeler@digia.com>2014-10-28 15:39:15 +0100
committerChristian Kandeler <christian.kandeler@digia.com>2014-10-31 11:08:17 +0100
commit0d1b18c7ad5bbfe4507f33d5f726b46f39d08677 (patch)
tree270034545db1c5682dd50719402125ca1035090b
parent29a977873bc2d8d46926de5f60d1e78da82fde74 (diff)
Make information about commands available via the API.
Users can supply a file path and a tag, and they will get the list of commands that are run to create an output file with the given tag from the given input file. This information is needed by Qt Creator. Change-Id: I36f92c200e78831733fccc4f7b5f54b8218d6311 Reviewed-by: Leena Miettinen <riitta-leena.miettinen@digia.com> Reviewed-by: Joerg Bornemann <joerg.bornemann@theqtcompany.com>
-rw-r--r--src/lib/corelib/api/api.pri8
-rw-r--r--src/lib/corelib/api/project.cpp70
-rw-r--r--src/lib/corelib/api/project.h4
-rw-r--r--src/lib/corelib/api/project_p.h4
-rw-r--r--src/lib/corelib/api/rulecommand.cpp142
-rw-r--r--src/lib/corelib/api/rulecommand.h78
-rw-r--r--src/lib/corelib/api/rulecommand_p.h59
-rw-r--r--src/lib/corelib/corelib.qbs3
-rw-r--r--src/lib/corelib/qbs.h1
-rw-r--r--tests/auto/api/testdata/command-extraction/main.cpp1
-rw-r--r--tests/auto/api/testdata/command-extraction/project.qbs5
-rw-r--r--tests/auto/api/tst_api.cpp43
-rw-r--r--tests/auto/api/tst_api.h1
13 files changed, 417 insertions, 2 deletions
diff --git a/src/lib/corelib/api/api.pri b/src/lib/corelib/api/api.pri
index 2bd59b5b6..18b3b4716 100644
--- a/src/lib/corelib/api/api.pri
+++ b/src/lib/corelib/api/api.pri
@@ -7,7 +7,9 @@ HEADERS += \
$$PWD/project.h \
$$PWD/project_p.h \
$$PWD/propertymap_p.h \
- $$PWD/projectdata_p.h
+ $$PWD/projectdata_p.h \
+ $$PWD/rulecommand.h \
+ $$PWD/rulecommand_p.h
SOURCES += \
$$PWD/internaljobs.cpp \
@@ -15,7 +17,8 @@ SOURCES += \
$$PWD/projectdata.cpp \
$$PWD/jobs.cpp \
$$PWD/languageinfo.cpp \
- $$PWD/project.cpp
+ $$PWD/project.cpp \
+ $$PWD/rulecommand.cpp
!qbs_no_dev_install {
api_headers.files = \
@@ -23,6 +26,7 @@ SOURCES += \
$$PWD/languageinfo.h \
$$PWD/project.h \
$$PWD/projectdata.h \
+ $$PWD/rulecommand.h \
$$PWD/runenvironment.h
api_headers.path = $${QBS_INSTALL_PREFIX}/include/qbs/api
INSTALLS += api_headers
diff --git a/src/lib/corelib/api/project.cpp b/src/lib/corelib/api/project.cpp
index a39e4eaba..270398bb2 100644
--- a/src/lib/corelib/api/project.cpp
+++ b/src/lib/corelib/api/project.cpp
@@ -38,9 +38,11 @@
#include "jobs.h"
#include "projectdata_p.h"
#include "propertymap_p.h"
+#include "rulecommand_p.h"
#include "runenvironment.h"
#include <buildgraph/artifact.h>
#include <buildgraph/buildgraph.h>
+#include <buildgraph/command.h>
#include <buildgraph/emptydirectoriesremover.h>
#include <buildgraph/productbuilddata.h>
#include <buildgraph/productinstaller.h>
@@ -613,6 +615,59 @@ void ProjectPrivate::prepareChangeToProject()
retrieveProjectData(m_projectData, internalProject);
}
+RuleCommandList ProjectPrivate::ruleCommands(const ProductData &product,
+ const QString &inputFilePath, const QString &outputFileTag) const
+{
+ if (internalProject->locked)
+ throw ErrorInfo(Tr::tr("A job is currently in process."));
+ const ResolvedProductConstPtr resolvedProduct = internalProduct(product);
+ if (!resolvedProduct)
+ throw ErrorInfo(Tr::tr("No such product '%1'.").arg(product.name()));
+ if (!resolvedProduct->enabled)
+ throw ErrorInfo(Tr::tr("Product '%1' is disabled.").arg(product.name()));
+ QBS_CHECK(resolvedProduct->buildData);
+ const ArtifactSet &outputArtifacts = resolvedProduct->buildData->artifactsByFileTag
+ .value(FileTag(outputFileTag.toLocal8Bit()));
+ foreach (const Artifact * const outputArtifact, outputArtifacts) {
+ const TransformerConstPtr transformer = outputArtifact->transformer;
+ if (!transformer)
+ continue;
+ foreach (const Artifact * const inputArtifact, transformer->inputs) {
+ if (inputArtifact->filePath() == inputFilePath) {
+ RuleCommandList list;
+ foreach (const AbstractCommandPtr &internalCommand, transformer->commands) {
+ RuleCommand externalCommand;
+ externalCommand.d->description = internalCommand->description();
+ switch (internalCommand->type()) {
+ case AbstractCommand::JavaScriptCommandType: {
+ externalCommand.d->type = RuleCommand::JavaScriptCommandType;
+ const JavaScriptCommandPtr &jsCmd
+ = internalCommand.staticCast<JavaScriptCommand>();
+ externalCommand.d->sourceCode = jsCmd->sourceCode();
+ break;
+ }
+ case AbstractCommand::ProcessCommandType: {
+ externalCommand.d->type = RuleCommand::ProcessCommandType;
+ const ProcessCommandPtr &procCmd
+ = internalCommand.staticCast<ProcessCommand>();
+ externalCommand.d->executable = procCmd->program();
+ externalCommand.d->arguments = procCmd->arguments();
+ externalCommand.d->workingDir = procCmd->workingDir();
+ externalCommand.d->environment = procCmd->environment();
+ break;
+ }
+ }
+ list << externalCommand;
+ }
+ return list;
+ }
+ }
+ }
+
+ throw ErrorInfo(Tr::tr("No rule was found that produces an artifact tagged '%1' "
+ "from input file '%2'.").arg(outputFileTag, inputFilePath));
+}
+
static bool productIsRunnable(const ResolvedProductConstPtr &product)
{
return product->fileTags.contains("application")
@@ -1048,6 +1103,21 @@ QSet<QString> Project::buildSystemFiles() const
return d->internalProject->buildSystemFiles;
}
+RuleCommandList Project::ruleCommands(const ProductData &product,
+ const QString &inputFilePath, const QString &outputFileTag, ErrorInfo *error) const
+{
+ QBS_ASSERT(isValid(), return RuleCommandList());
+ QBS_ASSERT(product.isValid(), return RuleCommandList());
+
+ try {
+ return d->ruleCommands(product, inputFilePath, outputFileTag);
+ } catch (const ErrorInfo &e) {
+ if (error)
+ *error = e;
+ return RuleCommandList();
+ }
+}
+
#ifdef QBS_ENABLE_PROJECT_FILE_UPDATES
/*!
* \brief Adds a new empty group to the given product.
diff --git a/src/lib/corelib/api/project.h b/src/lib/corelib/api/project.h
index 50abb3614..d943a86d8 100644
--- a/src/lib/corelib/api/project.h
+++ b/src/lib/corelib/api/project.h
@@ -30,6 +30,7 @@
#ifndef QBS_PROJECT_H
#define QBS_PROJECT_H
+#include "rulecommand.h"
#include "../language/forward_decls.h"
#include "../tools/qbs_export.h"
@@ -125,6 +126,9 @@ public:
QSet<QString> buildSystemFiles() const;
+ RuleCommandList ruleCommands(const ProductData &product, const QString &inputFilePath,
+ const QString &outputFileTag, ErrorInfo *error = 0) const;
+
#ifdef QBS_ENABLE_PROJECT_FILE_UPDATES
ErrorInfo addGroup(const ProductData &product, const QString &groupName);
ErrorInfo addFiles(const ProductData &product, const GroupData &group,
diff --git a/src/lib/corelib/api/project_p.h b/src/lib/corelib/api/project_p.h
index 73f6e19ae..273f637e0 100644
--- a/src/lib/corelib/api/project_p.h
+++ b/src/lib/corelib/api/project_p.h
@@ -31,6 +31,7 @@
#define QBS_PROJECT_P_H
#include "projectdata.h"
+#include "rulecommand.h"
#include <language/language.h>
#include <logging/logger.h>
@@ -105,6 +106,9 @@ public:
const CodeLocation &changeLocation, int lineOffset);
void prepareChangeToProject();
+ RuleCommandList ruleCommands(const ProductData &product,
+ const QString &inputFilePath, const QString &outputFileTag) const;
+
TopLevelProjectPtr internalProject;
Logger logger;
diff --git a/src/lib/corelib/api/rulecommand.cpp b/src/lib/corelib/api/rulecommand.cpp
new file mode 100644
index 000000000..844b9e975
--- /dev/null
+++ b/src/lib/corelib/api/rulecommand.cpp
@@ -0,0 +1,142 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** 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://www.qt.io/licensing. For further information
+** use the contact form at http://www.qt.io/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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** 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.
+**
+****************************************************************************/
+
+#include "rulecommand.h"
+#include "rulecommand_p.h"
+
+#include <tools/qbsassert.h>
+
+namespace qbs {
+
+/*!
+ * \class RuleCommand
+ * \brief The \c RuleCommand class corresponds to a \c ProcessCommand or \c JavaScriptCommand
+ * in \QBS.
+ */
+
+/*!
+ * \enum RuleCommand::Type
+ * This enum type represents the different kinds of commands.
+ * \value ProcessCommandType For the \QBS type \c ProcessCommand, which represents a command
+ * whose execution involves calling an executable.
+ * \value JavaScriptCommandType For the \QBS type \c JavaScriptCommand, which represents a command
+ * whose execution involves running a piece of JavaScript code inside \QBS.
+ * \value InvalidType Used to mark \c RuleCommand objects as invalid.
+ */
+
+
+RuleCommand::RuleCommand() : d(new Internal::RuleCommandPrivate)
+{
+}
+
+RuleCommand::RuleCommand(const RuleCommand &other) : d(other.d) {}
+
+RuleCommand::~RuleCommand()
+{
+}
+
+RuleCommand& RuleCommand::operator=(const RuleCommand &other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ * Returns the type of this object. If the value is \c InvalidType, the object is invalid.
+ */
+RuleCommand::Type RuleCommand::type() const
+{
+ return d->type;
+}
+
+/*!
+ * Returns the human-readable description of this command that \QBS will print when
+ * the command executed.
+ */
+QString RuleCommand::description() const
+{
+ return d->description;
+}
+
+/*!
+ * Returns the source of the command if \c type() is \c JavaScriptCommandType.
+ * If \c type() is anything else, the behavior of this function is undefined.
+ */
+QString RuleCommand::sourceCode() const
+{
+ QBS_ASSERT(type() == JavaScriptCommandType, return QString());
+ return d->sourceCode;
+}
+
+/*!
+ * Returns the executable that will be called when the corresponding \c ProcessCommand
+ * is executed.
+ * If \c type() is not \c ProcessCommandType, the behavior of this function is undefined.
+ */
+QString RuleCommand::executable() const
+{
+ QBS_ASSERT(type() == ProcessCommandType, return QString());
+ return d->executable;
+}
+
+/*!
+ * Returns the command-line arguments of the executable that will be called when the
+ * corresponding \c ProcessCommand is executed.
+ * If \c type() is not \c ProcessCommandType, the behavior of this function is undefined.
+ */
+QStringList RuleCommand::arguments() const
+{
+ QBS_ASSERT(type() == ProcessCommandType, return QStringList());
+ return d->arguments;
+}
+
+/*!
+ * Returns the working directory of the executable that will be called when the
+ * corresponding \c ProcessCommand is executed.
+ * If \c type() is not \c ProcessCommandType, the behavior of this function is undefined.
+ */
+QString RuleCommand::workingDirectory() const
+{
+ QBS_ASSERT(type() == ProcessCommandType, return QString());
+ return d->workingDir;
+}
+
+/*!
+ * Returns the environment of the executable that will be called when the
+ * corresponding \c ProcessCommand is executed.
+ * If \c type() is not \c ProcessCommandType, the behavior of this function is undefined.
+ */
+QProcessEnvironment RuleCommand::environment() const
+{
+ QBS_ASSERT(type() == ProcessCommandType, return QProcessEnvironment());
+ return d->environment;
+}
+
+} // namespace qbs
diff --git a/src/lib/corelib/api/rulecommand.h b/src/lib/corelib/api/rulecommand.h
new file mode 100644
index 000000000..04c89d190
--- /dev/null
+++ b/src/lib/corelib/api/rulecommand.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** 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://www.qt.io/licensing. For further information
+** use the contact form at http://www.qt.io/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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_RULECOMMAND_H
+#define QBS_RULECOMMAND_H
+
+#include <tools/qbs_export.h>
+
+#include <QExplicitlySharedDataPointer>
+#include <QStringList>
+
+QT_BEGIN_NAMESPACE
+class QProcessEnvironment;
+QT_END_NAMESPACE
+
+namespace qbs {
+namespace Internal {
+class ProjectPrivate;
+class RuleCommandPrivate;
+}
+
+class QBS_EXPORT RuleCommand
+{
+ friend class Internal::ProjectPrivate;
+public:
+ RuleCommand();
+ RuleCommand(const RuleCommand &other);
+ ~RuleCommand();
+ RuleCommand &operator=(const RuleCommand &other);
+
+ enum Type { ProcessCommandType, JavaScriptCommandType, InvalidType };
+
+
+ Type type() const;
+ QString description() const;
+ QString sourceCode() const;
+ QString executable() const;
+ QStringList arguments() const;
+ QString workingDirectory() const;
+ QProcessEnvironment environment() const;
+
+private:
+ QExplicitlySharedDataPointer<Internal::RuleCommandPrivate> d;
+};
+
+
+typedef QList<RuleCommand> RuleCommandList;
+
+} // namespace qbs
+
+#endif // Include guard.
diff --git a/src/lib/corelib/api/rulecommand_p.h b/src/lib/corelib/api/rulecommand_p.h
new file mode 100644
index 000000000..529c18636
--- /dev/null
+++ b/src/lib/corelib/api/rulecommand_p.h
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** 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://www.qt.io/licensing. For further information
+** use the contact form at http://www.qt.io/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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_RULECOMMAND_P_H
+#define QBS_RULECOMMAND_P_H
+
+#include "rulecommand.h"
+
+#include <QProcessEnvironment>
+#include <QSharedData>
+
+namespace qbs {
+namespace Internal {
+
+class RuleCommandPrivate : public QSharedData
+{
+public:
+ RuleCommandPrivate(): type(RuleCommand::InvalidType) {}
+
+ RuleCommand::Type type;
+ QString description;
+ QString sourceCode;
+ QString executable;
+ QStringList arguments;
+ QString workingDir;
+ QProcessEnvironment environment;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // Include guard.
diff --git a/src/lib/corelib/corelib.qbs b/src/lib/corelib/corelib.qbs
index 406ab264e..0d8e31fba 100644
--- a/src/lib/corelib/corelib.qbs
+++ b/src/lib/corelib/corelib.qbs
@@ -57,6 +57,8 @@ QbsLibrary {
"projectdata.cpp",
"projectdata_p.h",
"propertymap_p.h",
+ "rulecommand.cpp",
+ "rulecommand_p.h",
"runenvironment.cpp",
]
}
@@ -70,6 +72,7 @@ QbsLibrary {
"languageinfo.h",
"project.h",
"projectdata.h",
+ "rulecommand.h",
"runenvironment.h"
]
}
diff --git a/src/lib/corelib/qbs.h b/src/lib/corelib/qbs.h
index 2bd3e7529..4c39c3dc1 100644
--- a/src/lib/corelib/qbs.h
+++ b/src/lib/corelib/qbs.h
@@ -34,6 +34,7 @@
#include "api/languageinfo.h"
#include "api/project.h"
#include "api/projectdata.h"
+#include "api/rulecommand.h"
#include "logging/ilogsink.h"
#include "tools/architectures.h"
#include "tools/buildoptions.h"
diff --git a/tests/auto/api/testdata/command-extraction/main.cpp b/tests/auto/api/testdata/command-extraction/main.cpp
new file mode 100644
index 000000000..237c8ce18
--- /dev/null
+++ b/tests/auto/api/testdata/command-extraction/main.cpp
@@ -0,0 +1 @@
+int main() {}
diff --git a/tests/auto/api/testdata/command-extraction/project.qbs b/tests/auto/api/testdata/command-extraction/project.qbs
new file mode 100644
index 000000000..73b66aaa0
--- /dev/null
+++ b/tests/auto/api/testdata/command-extraction/project.qbs
@@ -0,0 +1,5 @@
+import qbs
+
+CppApplication {
+ files: "main.cpp"
+}
diff --git a/tests/auto/api/tst_api.cpp b/tests/auto/api/tst_api.cpp
index 17fa0127f..c890d6261 100644
--- a/tests/auto/api/tst_api.cpp
+++ b/tests/auto/api/tst_api.cpp
@@ -173,6 +173,49 @@ void TestApi::buildSingleFile()
qPrintable(receiver.descriptions));
}
+void TestApi::commandExtraction()
+{
+ qbs::SetupProjectParameters setupParams = defaultSetupParameters();
+ const QString projectDirPath = QDir::cleanPath(m_workingDataDir + "/command-extraction");
+ setupParams.setProjectFilePath(projectDirPath + "/project.qbs");
+ QScopedPointer<qbs::SetupProjectJob> setupJob(qbs::Project().setupProject(setupParams,
+ m_logSink, 0));
+ waitForFinished(setupJob.data());
+ QVERIFY2(!setupJob->error().hasError(), qPrintable(setupJob->error().toString()));
+ qbs::Project project = setupJob->project();
+ qbs::ProjectData projectData = project.projectData();
+ QCOMPARE(projectData.allProducts().count(), 1);
+ qbs::ProductData productData = projectData.allProducts().first();
+ qbs::ErrorInfo errorInfo;
+ const QString sourceFilePath = projectDirPath + "/main.cpp";
+
+ // Before the first build, no rules exist.
+ qbs::RuleCommandList commands
+ = project.ruleCommands(productData, sourceFilePath, "obj", &errorInfo);
+ QCOMPARE(commands.count(), 0);
+ QVERIFY(errorInfo.hasError());
+ QVERIFY2(errorInfo.toString().contains("No rule"), qPrintable(errorInfo.toString()));
+
+ qbs::BuildOptions options;
+ options.setDryRun(true);
+ QScopedPointer<qbs::BuildJob> buildJob(project.buildAllProducts(options));
+ waitForFinished(buildJob.data());
+ QVERIFY2(!buildJob->error().hasError(), qPrintable(buildJob->error().toString()));
+ projectData = project.projectData();
+ QCOMPARE(projectData.allProducts().count(), 1);
+ productData = projectData.allProducts().first();
+ errorInfo = qbs::ErrorInfo();
+
+ // After the build, the compile command must be found.
+ commands = project.ruleCommands(productData, sourceFilePath, "obj", &errorInfo);
+ QCOMPARE(commands.count(), 1);
+ QVERIFY2(!errorInfo.hasError(), qPrintable(errorInfo.toString()));
+ const qbs::RuleCommand command = commands.first();
+ QCOMPARE(command.type(), qbs::RuleCommand::ProcessCommandType);
+ QVERIFY(!command.executable().isEmpty());
+ QVERIFY(!command.arguments().isEmpty());
+}
+
qbs::GroupData findGroup(const qbs::ProductData &product, const QString &name)
{
foreach (const qbs::GroupData &g, product.groups()) {
diff --git a/tests/auto/api/tst_api.h b/tests/auto/api/tst_api.h
index 08740a479..512e0cdc0 100644
--- a/tests/auto/api/tst_api.h
+++ b/tests/auto/api/tst_api.h
@@ -53,6 +53,7 @@ private slots:
#ifdef QBS_ENABLE_PROJECT_FILE_UPDATES
void changeContent();
#endif
+ void commandExtraction();
void disabledInstallGroup();
void fileTagsFilterOverride();
void infiniteLoopBuilding();