summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/app/qbs/parser/command.cpp1
-rw-r--r--src/app/qbs/parser/commandlineoption.cpp12
-rw-r--r--src/app/qbs/parser/commandlineoption.h8
-rw-r--r--src/app/qbs/parser/commandlineoptionpool.cpp9
-rw-r--r--src/app/qbs/parser/commandlineoptionpool.h1
-rw-r--r--src/app/qbs/parser/commandlineparser.cpp6
-rw-r--r--src/app/qbs/parser/commandlineparser.h1
-rw-r--r--src/lib/corelib/buildgraph/executor.cpp20
-rw-r--r--src/lib/corelib/tools/buildoptions.cpp21
-rw-r--r--src/lib/corelib/tools/buildoptions.h3
-rw-r--r--tests/auto/api/testdata/check-outputs/foo.txt1
-rw-r--r--tests/auto/api/testdata/check-outputs/project.qbs37
-rw-r--r--tests/auto/api/tst_api.cpp21
-rw-r--r--tests/auto/api/tst_api.h2
-rw-r--r--tests/auto/cmdlineparser/tst_cmdlineparser.cpp2
15 files changed, 143 insertions, 2 deletions
diff --git a/src/app/qbs/parser/command.cpp b/src/app/qbs/parser/command.cpp
index 73c40867f..ed8352788 100644
--- a/src/app/qbs/parser/command.cpp
+++ b/src/app/qbs/parser/command.cpp
@@ -247,6 +247,7 @@ static QList<CommandLineOption::Type> buildOptions()
<< CommandLineOption::ProductsOptionType
<< CommandLineOption::ChangedFilesOptionType
<< CommandLineOption::ForceTimestampCheckOptionType
+ << CommandLineOption::ForceOutputCheckOptionType
<< CommandLineOption::BuildNonDefaultOptionType
<< CommandLineOption::VersionOptionType
<< CommandLineOption::CommandEchoModeOptionType
diff --git a/src/app/qbs/parser/commandlineoption.cpp b/src/app/qbs/parser/commandlineoption.cpp
index 42c167447..8a4adc63f 100644
--- a/src/app/qbs/parser/commandlineoption.cpp
+++ b/src/app/qbs/parser/commandlineoption.cpp
@@ -426,6 +426,18 @@ QString ForceTimeStampCheckOption::longRepresentation() const
return QLatin1String("--check-timestamps");
}
+QString ForceOutputCheckOption::description(CommandType command) const
+{
+ Q_UNUSED(command);
+ return Tr::tr("%1\n\tForce transformer output artifact checks.\n"
+ "\tVerify that the output artifacts declared by rules and transformers in the\n"
+ "\tproject are actually created.\n").arg(longRepresentation());
+}
+
+QString ForceOutputCheckOption::longRepresentation() const
+{
+ return QLatin1String("--check-outputs");
+}
QString BuildNonDefaultOption::description(CommandType command) const
{
diff --git a/src/app/qbs/parser/commandlineoption.h b/src/app/qbs/parser/commandlineoption.h
index b096ea2ba..244332574 100644
--- a/src/app/qbs/parser/commandlineoption.h
+++ b/src/app/qbs/parser/commandlineoption.h
@@ -55,6 +55,7 @@ public:
InstallRootOptionType, RemoveFirstOptionType, NoBuildOptionType,
ForceOptionType,
ForceTimestampCheckOptionType,
+ ForceOutputCheckOptionType,
BuildNonDefaultOptionType,
VersionOptionType,
LogTimeOptionType,
@@ -230,6 +231,13 @@ class ForceTimeStampCheckOption : public OnOffOption
QString longRepresentation() const;
};
+class ForceOutputCheckOption : public OnOffOption
+{
+ QString description(CommandType command) const;
+ QString shortRepresentation() const { return QString(); }
+ QString longRepresentation() const;
+};
+
class BuildNonDefaultOption : public OnOffOption
{
QString description(CommandType command) const;
diff --git a/src/app/qbs/parser/commandlineoptionpool.cpp b/src/app/qbs/parser/commandlineoptionpool.cpp
index 212ea3d06..e5d8854a6 100644
--- a/src/app/qbs/parser/commandlineoptionpool.cpp
+++ b/src/app/qbs/parser/commandlineoptionpool.cpp
@@ -92,6 +92,9 @@ CommandLineOption *CommandLineOptionPool::getOption(CommandLineOption::Type type
case CommandLineOption::ForceTimestampCheckOptionType:
option = new ForceTimeStampCheckOption;
break;
+ case CommandLineOption::ForceOutputCheckOptionType:
+ option = new ForceOutputCheckOption;
+ break;
case CommandLineOption::BuildNonDefaultOptionType:
option = new BuildNonDefaultOption;
break;
@@ -203,6 +206,12 @@ ForceTimeStampCheckOption *CommandLineOptionPool::forceTimestampCheckOption() co
getOption(CommandLineOption::ForceTimestampCheckOptionType));
}
+ForceOutputCheckOption *CommandLineOptionPool::forceOutputCheckOption() const
+{
+ return static_cast<ForceOutputCheckOption *>(
+ getOption(CommandLineOption::ForceOutputCheckOptionType));
+}
+
BuildNonDefaultOption *CommandLineOptionPool::buildNonDefaultOption() const
{
return static_cast<BuildNonDefaultOption *>(
diff --git a/src/app/qbs/parser/commandlineoptionpool.h b/src/app/qbs/parser/commandlineoptionpool.h
index cbea5023d..907fda0a0 100644
--- a/src/app/qbs/parser/commandlineoptionpool.h
+++ b/src/app/qbs/parser/commandlineoptionpool.h
@@ -59,6 +59,7 @@ public:
NoBuildOption *noBuildOption() const;
ForceOption *forceOption() const;
ForceTimeStampCheckOption *forceTimestampCheckOption() const;
+ ForceOutputCheckOption *forceOutputCheckOption() const;
BuildNonDefaultOption *buildNonDefaultOption() const;
VersionOption *versionOption() const;
LogTimeOption *logTimeOption() const;
diff --git a/src/app/qbs/parser/commandlineparser.cpp b/src/app/qbs/parser/commandlineparser.cpp
index 62e15f3e4..73f445947 100644
--- a/src/app/qbs/parser/commandlineparser.cpp
+++ b/src/app/qbs/parser/commandlineparser.cpp
@@ -206,6 +206,11 @@ bool CommandLineParser::forceTimestampCheck() const
return d->optionPool.forceTimestampCheckOption()->enabled();
}
+bool CommandLineParser::forceOutputCheck() const
+{
+ return d->optionPool.forceOutputCheckOption()->enabled();
+}
+
bool CommandLineParser::dryRun() const
{
return d->dryRun();
@@ -506,6 +511,7 @@ void CommandLineParser::CommandLineParserPrivate::setupBuildOptions()
buildOptions.setChangedFiles(changedFiles);
buildOptions.setKeepGoing(optionPool.keepGoingOption()->enabled());
buildOptions.setForceTimestampCheck(optionPool.forceTimestampCheckOption()->enabled());
+ buildOptions.setForceOutputCheck(optionPool.forceOutputCheckOption()->enabled());
const JobsOption * jobsOption = optionPool.jobsOption();
buildOptions.setMaxJobCount(jobsOption->jobCount());
buildOptions.setLogElapsedTime(logTime);
diff --git a/src/app/qbs/parser/commandlineparser.h b/src/app/qbs/parser/commandlineparser.h
index 27df3c0db..41cdbd927 100644
--- a/src/app/qbs/parser/commandlineparser.h
+++ b/src/app/qbs/parser/commandlineparser.h
@@ -63,6 +63,7 @@ public:
InstallOptions installOptions(const QString &profile) const;
bool force() const;
bool forceTimestampCheck() const;
+ bool forceOutputCheck() const;
bool dryRun() const;
bool logTime() const;
bool withNonDefaultProducts() const;
diff --git a/src/lib/corelib/buildgraph/executor.cpp b/src/lib/corelib/buildgraph/executor.cpp
index 447cdd287..150133c04 100644
--- a/src/lib/corelib/buildgraph/executor.cpp
+++ b/src/lib/corelib/buildgraph/executor.cpp
@@ -510,10 +510,26 @@ void Executor::finishJob(ExecutorJob *job, bool success)
if (success) {
m_project->buildData->isDirty = true;
foreach (Artifact *artifact, transformer->outputs) {
- if (artifact->alwaysUpdated)
+ if (artifact->alwaysUpdated) {
artifact->setTimestamp(FileTime::currentTime());
- else
+ if (m_buildOptions.forceOutputCheck() && !FileInfo(artifact->filePath()).exists()) {
+ if (transformer->rule) {
+ if (!transformer->rule->name.isEmpty()) {
+ throw ErrorInfo(tr("Rule '%1' declares artifact '%2', "
+ "but the artifact was not produced.")
+ .arg(transformer->rule->name, artifact->filePath()));
+ }
+ throw ErrorInfo(tr("Rule declares artifact '%1', "
+ "but the artifact was not produced.")
+ .arg(artifact->filePath()));
+ }
+ throw ErrorInfo(tr("Transformer declares artifact '%1', "
+ "but the artifact was not produced.")
+ .arg(artifact->filePath()));
+ }
+ } else {
artifact->setTimestamp(FileInfo(artifact->filePath()).lastModified());
+ }
}
finishTransformer(transformer);
}
diff --git a/src/lib/corelib/tools/buildoptions.cpp b/src/lib/corelib/tools/buildoptions.cpp
index fafb86404..a2d5c2db2 100644
--- a/src/lib/corelib/tools/buildoptions.cpp
+++ b/src/lib/corelib/tools/buildoptions.cpp
@@ -40,6 +40,7 @@ class BuildOptionsPrivate : public QSharedData
public:
BuildOptionsPrivate()
: maxJobCount(0), dryRun(false), keepGoing(false), forceTimestampCheck(false),
+ forceOutputCheck(false),
logElapsedTime(false), echoMode(defaultCommandEchoMode()), install(true),
removeExistingInstallation(false), onlyExecuteRules(false)
{
@@ -52,6 +53,7 @@ public:
bool dryRun;
bool keepGoing;
bool forceTimestampCheck;
+ bool forceOutputCheck;
bool logElapsedTime;
CommandEchoMode echoMode;
bool install;
@@ -240,6 +242,25 @@ void BuildOptions::setForceTimestampCheck(bool enabled)
}
/*!
+ * \brief Returns true if qbs will test whether rules and transformers actually create their
+ * declared output artifacts.
+ * The default is \c false.
+ */
+bool BuildOptions::forceOutputCheck() const
+{
+ return d->forceOutputCheck;
+}
+
+/*!
+ * \brief Controls whether qbs should test whether rules and transformers actually create their
+ * declared output artifacts. Enabling this may introduce some small I/O overhead during the build.
+ */
+void BuildOptions::setForceOutputCheck(bool enabled)
+{
+ d->forceOutputCheck = enabled;
+}
+
+/*!
* \brief Returns true iff the time the operation takes will be logged.
* The default is \c false.
*/
diff --git a/src/lib/corelib/tools/buildoptions.h b/src/lib/corelib/tools/buildoptions.h
index df582bc4b..03187bbfe 100644
--- a/src/lib/corelib/tools/buildoptions.h
+++ b/src/lib/corelib/tools/buildoptions.h
@@ -70,6 +70,9 @@ public:
bool forceTimestampCheck() const;
void setForceTimestampCheck(bool enabled);
+ bool forceOutputCheck() const;
+ void setForceOutputCheck(bool enabled);
+
bool logElapsedTime() const;
void setLogElapsedTime(bool log);
diff --git a/tests/auto/api/testdata/check-outputs/foo.txt b/tests/auto/api/testdata/check-outputs/foo.txt
new file mode 100644
index 000000000..76e819701
--- /dev/null
+++ b/tests/auto/api/testdata/check-outputs/foo.txt
@@ -0,0 +1 @@
+int main() { return 0; }
diff --git a/tests/auto/api/testdata/check-outputs/project.qbs b/tests/auto/api/testdata/check-outputs/project.qbs
new file mode 100644
index 000000000..27eada48f
--- /dev/null
+++ b/tests/auto/api/testdata/check-outputs/project.qbs
@@ -0,0 +1,37 @@
+import qbs
+import qbs.File
+
+Project {
+ Product {
+ type: 'application'
+ consoleApplication: true
+ Group {
+ files: 'foo.txt'
+ fileTags: ['text']
+ }
+ Depends { name: 'cpp' }
+ }
+
+ Rule {
+ inputs: ['text']
+ Artifact {
+ fileTags: ['cpp']
+ filePath: input.baseName + '.cpp'
+ }
+ Artifact {
+ fileTags: ['ghost']
+ filePath: input.baseName + '.ghost'
+ }
+
+ prepare: {
+ var cmd = new JavaScriptCommand();
+ cmd.inp = inputs["text"][0].filePath;
+ cmd.out = outputs["cpp"][0].filePath;
+ cmd.description = "generating " + outputs["cpp"][0].fileName;
+ cmd.sourceCode = function() {
+ File.copy(inp, out);
+ };
+ return cmd;
+ }
+ }
+}
diff --git a/tests/auto/api/tst_api.cpp b/tests/auto/api/tst_api.cpp
index a33330d6f..dcef6b37b 100644
--- a/tests/auto/api/tst_api.cpp
+++ b/tests/auto/api/tst_api.cpp
@@ -389,6 +389,27 @@ void TestApi::buildSingleFile()
qPrintable(receiver.descriptions));
}
+void TestApi::checkOutputs()
+{
+ QFETCH(bool, check);
+ qbs::SetupProjectParameters params = defaultSetupParameters("/check-outputs/project.qbs");
+ qbs::BuildOptions options;
+ options.setForceOutputCheck(check);
+ removeBuildDir(params);
+ qbs::ErrorInfo errorInfo = doBuildProject("/check-outputs/project.qbs", 0, 0, 0, options);
+ if (check)
+ QVERIFY(errorInfo.hasError());
+ else
+ VERIFY_NO_ERROR(errorInfo);
+}
+
+void TestApi::checkOutputs_data()
+{
+ QTest::addColumn<bool>("check");
+ QTest::newRow("checked outputs") << true;
+ QTest::newRow("unchecked outputs") << false;
+}
+
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 6a3f50e1c..a4e83b717 100644
--- a/tests/auto/api/tst_api.h
+++ b/tests/auto/api/tst_api.h
@@ -67,6 +67,8 @@ private slots:
void changeContent();
#endif
void changeDependentLib();
+ void checkOutputs();
+ void checkOutputs_data();
void commandExtraction();
void disabledInstallGroup();
void disabledProduct();
diff --git a/tests/auto/cmdlineparser/tst_cmdlineparser.cpp b/tests/auto/cmdlineparser/tst_cmdlineparser.cpp
index 60b7bfd73..f1f27d1e8 100644
--- a/tests/auto/cmdlineparser/tst_cmdlineparser.cpp
+++ b/tests/auto/cmdlineparser/tst_cmdlineparser.cpp
@@ -63,6 +63,7 @@ private slots:
args << "--changed-files" << "foo,bar" << fileArgs;
args << "--force";
args << "--check-timestamps";
+ args << "--check-outputs";
CommandLineParser parser;
QVERIFY(parser.parseCommandLine(args));
@@ -73,6 +74,7 @@ private slots:
QVERIFY(parser.buildOptions(QString()).keepGoing());
QVERIFY(parser.force());
QVERIFY(parser.forceTimestampCheck());
+ QVERIFY(parser.forceOutputCheck());
QVERIFY(!parser.logTime());
QCOMPARE(parser.buildConfigurations().count(), 1);