summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/jomlib/macrotable.cpp48
-rw-r--r--src/jomlib/macrotable.h28
-rw-r--r--src/jomlib/makefilefactory.cpp40
-rw-r--r--src/jomlib/options.cpp15
-rw-r--r--tests/makefiles/blackbox/macrosOnCommandLine/test.mk3
-rw-r--r--tests/tests.cpp77
-rw-r--r--tests/tests.h2
7 files changed, 177 insertions, 36 deletions
diff --git a/src/jomlib/macrotable.cpp b/src/jomlib/macrotable.cpp
index 90a35c5..7026625 100644
--- a/src/jomlib/macrotable.cpp
+++ b/src/jomlib/macrotable.cpp
@@ -56,8 +56,14 @@ QString MacroTable::macroValue(const QString& macroName) const
void MacroTable::defineEnvironmentMacroValue(const QString& name, const QString& value, bool readOnly)
{
const QString upperName = name.toUpper();
- if (m_macros.contains(upperName))
+ if (m_macros.contains(upperName)) {
+ MacroData &md = m_macros[upperName];
+ if (md.source == MacroSource::CommandLine) {
+ md.source = MacroSource::Environment;
+ setEnvironmentVariable(upperName, expandMacros(md.value));
+ }
return;
+ }
QString expandedValue;
try {
// The make variable gets the unexpanded value.
@@ -72,11 +78,31 @@ void MacroTable::defineEnvironmentMacroValue(const QString& name, const QString&
MacroData* macroData = internalSetMacroValue(upperName, value);
if (!macroData)
return;
- macroData->isEnvironmentVariable = true;
+ macroData->source = MacroSource::Environment;
macroData->isReadOnly = readOnly;
setEnvironmentVariable(upperName, expandedValue);
}
+void MacroTable::defineCommandLineMacroValue(const QString &name, const QString &value)
+{
+ defineCommandLineMacroValueImpl(name, value, MacroSource::CommandLine);
+}
+
+void MacroTable::defineImplicitCommandLineMacroValue(const QString &name, const QString &value)
+{
+ defineCommandLineMacroValueImpl(name, value, MacroSource::CommandLineImplicit);
+}
+
+void MacroTable::defineCommandLineMacroValueImpl(const QString &name, const QString &value,
+ MacroSource source)
+{
+ MacroData* macroData = internalSetMacroValue(name, value, true);
+ if (!macroData)
+ return;
+ macroData->source = source;
+ macroData->isReadOnly = true;
+}
+
bool MacroTable::isMacroNameValid(const QString& name) const
{
static QRegExp rexMacroIdentifier;
@@ -97,16 +123,27 @@ bool MacroTable::isMacroNameValid(const QString& name) const
*/
void MacroTable::setMacroValue(const QString& name, const QString& value)
{
+ setMacroValueImpl(name, value, MacroSource::MakeFile);
+}
+
+void MacroTable::setMacroValueImpl(const QString &name, const QString &value, MacroSource source)
+{
MacroData* macroData = internalSetMacroValue(name, value);
if (!macroData) {
QString msg = QLatin1String("macro name %1 is invalid");
throw Exception(msg.arg(name));
}
- if (macroData->isEnvironmentVariable)
+ macroData->source = source;
+ if (macroData->source == MacroSource::Environment)
setEnvironmentVariable(name, expandMacros(macroData->value));
}
+void MacroTable::predefineValue(const QString &name, const QString &value)
+{
+ setMacroValueImpl(name, value, MacroSource::Predefinition);
+}
+
/**
* Sets the value of an environment variable.
* The environment will be passed to the QProcess instances.
@@ -150,7 +187,8 @@ private:
const QString &str;
};
-MacroTable::MacroData* MacroTable::internalSetMacroValue(const QString& name, const QString& value)
+MacroTable::MacroData* MacroTable::internalSetMacroValue(const QString &name, const QString &value,
+ bool ignoreReadOnly)
{
QString expandedName = expandMacros(name);
if (!isMacroNameValid(expandedName))
@@ -162,7 +200,7 @@ MacroTable::MacroData* MacroTable::internalSetMacroValue(const QString& name, co
replaceStringWithLazyValue(newValue, instantiatedName, MacroValueOp(this, expandedName));
result = &m_macros[expandedName];
- if (!result->isReadOnly)
+ if (ignoreReadOnly || !result->isReadOnly)
result->value = newValue;
return result;
diff --git a/src/jomlib/macrotable.h b/src/jomlib/macrotable.h
index 27728fd..7a93043 100644
--- a/src/jomlib/macrotable.h
+++ b/src/jomlib/macrotable.h
@@ -48,9 +48,14 @@ public:
bool isMacroNameValid(const QString& name) const;
QString macroValue(const QString& macroName) const;
void defineEnvironmentMacroValue(const QString& name, const QString& value, bool readOnly = false);
+ void defineCommandLineMacroValue(const QString &name, const QString &value);
+ void defineImplicitCommandLineMacroValue(const QString &name, const QString &value);
void setMacroValue(const QString& name, const QString& value);
void setMacroValue(const char *szStr, const QString& value) { setMacroValue(QString::fromLatin1(szStr), value); }
void setMacroValue(const char *szStr, const char *szValue) { setMacroValue(QString::fromLatin1(szStr), QString::fromLatin1(szValue)); }
+ void predefineValue(const QString &name, const QString &value);
+ void predefineValue(const char *szStr, const QString &value) { predefineValue(QString::fromLatin1(szStr), value); }
+ void predefineValue(const char *szStr, const char *szValue) { predefineValue(QString::fromLatin1(szStr), QString::fromLatin1(szValue)); }
void undefineMacro(const QString& name);
QString expandMacros(const QString& str, bool inDependentsLine = false) const;
void dump() const;
@@ -65,18 +70,27 @@ public:
static void applySubstitution(const Substitution &substitution, QString &value);
private:
- struct MacroData
+ enum class MacroSource
{
- MacroData()
- : isEnvironmentVariable(false), isReadOnly(false)
- {}
+ CommandLine,
+ CommandLineImplicit,
+ MakeFile,
+ Environment,
+ Predefinition
+ };
- bool isEnvironmentVariable;
- bool isReadOnly;
+ struct MacroData
+ {
+ MacroSource source = MacroSource::MakeFile;
+ bool isReadOnly = false;
QString value;
};
- MacroData* internalSetMacroValue(const QString& name, const QString& value);
+ void setMacroValueImpl(const QString &name, const QString &value, MacroSource source);
+ void defineCommandLineMacroValueImpl(const QString &name, const QString &value,
+ MacroSource source);
+ MacroData* internalSetMacroValue(const QString &name, const QString &value,
+ bool ignoreReadOnly = false);
void setEnvironmentVariable(const QString& name, const QString& value);
QString expandMacros(const QString& str, bool inDependentsLine, QSet<QString>& usedMacros) const;
QString cycleCheckedMacroValue(const QString& macroName, QSet<QString>& usedMacros) const;
diff --git a/src/jomlib/makefilefactory.cpp b/src/jomlib/makefilefactory.cpp
index c97243b..1fb0a8a 100644
--- a/src/jomlib/makefilefactory.cpp
+++ b/src/jomlib/makefilefactory.cpp
@@ -133,26 +133,26 @@ bool MakefileFactory::apply(const QStringList& commandLineArguments, Options **o
readEnvironment(m_environment, macroTable, options->overrideEnvVarMacros);
if (!options->ignorePredefinedRulesAndMacros) {
- macroTable->setMacroValue("MAKE", encloseInDoubleQuotesIfNeeded(options->fullAppPath));
- macroTable->setMacroValue("MAKEDIR", encloseInDoubleQuotesIfNeeded(QDir::currentPath()));
- macroTable->setMacroValue("AS", "ml"); // Macro Assembler
- macroTable->setMacroValue("ASFLAGS", QString());
- macroTable->setMacroValue("BC", "bc"); // Basic Compiler
- macroTable->setMacroValue("BCFLAGS", QString());
- macroTable->setMacroValue("CC", "cl"); // C Compiler
- macroTable->setMacroValue("CCFLAGS", QString());
- macroTable->setMacroValue("COBOL", "cobol"); // COBOL Compiler
- macroTable->setMacroValue("COBOLFLAGS", QString());
- macroTable->setMacroValue("CPP", "cl"); // C++ Compiler
- macroTable->setMacroValue("CPPFLAGS", QString());
- macroTable->setMacroValue("CXX", "cl"); // C++ Compiler
- macroTable->setMacroValue("CXXFLAGS", QString());
- macroTable->setMacroValue("FOR", "fl"); // FORTRAN Compiler
- macroTable->setMacroValue("FORFLAGS", QString());
- macroTable->setMacroValue("PASCAL", "pl"); // Pascal Compiler
- macroTable->setMacroValue("PASCALFLAGS", QString());
- macroTable->setMacroValue("RC", "rc"); // Resource Compiler
- macroTable->setMacroValue("RCFLAGS", QString());
+ macroTable->predefineValue("MAKE", encloseInDoubleQuotesIfNeeded(options->fullAppPath));
+ macroTable->predefineValue("MAKEDIR", encloseInDoubleQuotesIfNeeded(QDir::currentPath()));
+ macroTable->predefineValue("AS", "ml"); // Macro Assembler
+ macroTable->predefineValue("ASFLAGS", QString());
+ macroTable->predefineValue("BC", "bc"); // Basic Compiler
+ macroTable->predefineValue("BCFLAGS", QString());
+ macroTable->predefineValue("CC", "cl"); // C Compiler
+ macroTable->predefineValue("CCFLAGS", QString());
+ macroTable->predefineValue("COBOL", "cobol"); // COBOL Compiler
+ macroTable->predefineValue("COBOLFLAGS", QString());
+ macroTable->predefineValue("CPP", "cl"); // C++ Compiler
+ macroTable->predefineValue("CPPFLAGS", QString());
+ macroTable->predefineValue("CXX", "cl"); // C++ Compiler
+ macroTable->predefineValue("CXXFLAGS", QString());
+ macroTable->predefineValue("FOR", "fl"); // FORTRAN Compiler
+ macroTable->predefineValue("FORFLAGS", QString());
+ macroTable->predefineValue("PASCAL", "pl"); // Pascal Compiler
+ macroTable->predefineValue("PASCALFLAGS", QString());
+ macroTable->predefineValue("RC", "rc"); // Resource Compiler
+ macroTable->predefineValue("RCFLAGS", QString());
}
try {
diff --git a/src/jomlib/options.cpp b/src/jomlib/options.cpp
index b026cf6..01ce796 100644
--- a/src/jomlib/options.cpp
+++ b/src/jomlib/options.cpp
@@ -30,8 +30,9 @@
#include <cstdlib>
-#include <QThread>
#include <QFile>
+#include <QSet>
+#include <QThread>
namespace NMakeFile {
@@ -85,6 +86,7 @@ bool Options::readCommandLineArguments(QStringList arguments, QString& makefile,
QString makeflags;
if (!expandCommandFiles(arguments))
return false;
+ QSet<QString> explicitlyDefinedMacros;
const QStringList originalArguments = arguments;
while (!arguments.isEmpty()) {
@@ -105,7 +107,16 @@ bool Options::readCommandLineArguments(QStringList arguments, QString& makefile,
fprintf(stderr, "Error: The macro name %s is invalid.", qPrintable(name));
exit(128);
}
- macroTable.defineEnvironmentMacroValue(name, trimLeft(arg.mid(idx+1)), true);
+ const QString value = trimLeft(arg.mid(idx+1));
+ if (!explicitlyDefinedMacros.contains(name)) {
+ explicitlyDefinedMacros.insert(name);
+ macroTable.defineCommandLineMacroValue(name, value);
+ }
+ const QString upperName = name.toUpper();
+ if (upperName != name && !explicitlyDefinedMacros.contains(upperName)) {
+ explicitlyDefinedMacros.remove(upperName);
+ macroTable.defineImplicitCommandLineMacroValue(upperName, value);
+ }
} else {
// handle target
arg = arg.trimmed();
diff --git a/tests/makefiles/blackbox/macrosOnCommandLine/test.mk b/tests/makefiles/blackbox/macrosOnCommandLine/test.mk
new file mode 100644
index 0000000..efd7090
--- /dev/null
+++ b/tests/makefiles/blackbox/macrosOnCommandLine/test.mk
@@ -0,0 +1,3 @@
+FooBar = 1
+!message FooBar:$(FooBar)
+!message FOOBAR:$(FOOBAR)
diff --git a/tests/tests.cpp b/tests/tests.cpp
index d939cb0..21184de 100644
--- a/tests/tests.cpp
+++ b/tests/tests.cpp
@@ -25,11 +25,12 @@
#include "tests.h"
-#include <QTest>
+#include <QDebug>
#include <QDir>
+#include <QHash>
#include <QScopedPointer>
-#include <QDebug>
#include <QStringBuilder>
+#include <QTest>
#include <ppexprparser.h>
#include <makefilefactory.h>
@@ -39,6 +40,7 @@
#include <exception.h>
#include <algorithm>
+#include <functional>
#include <limits>
using namespace NMakeFile;
@@ -1208,6 +1210,77 @@ void Tests::suffixes()
QCOMPARE(output.takeFirst(), QByteArray("c -> x"));
}
+using ByteArrayDict = QHash<QByteArray, QByteArray>;
+
+void Tests::macrosOnCommandLine_data()
+{
+ QTest::addColumn<QStringList>("arguments");
+ QTest::addColumn<ByteArrayDict>("expectedMacros");
+ QTest::newRow("no_arguments")
+ << QStringList()
+ << ByteArrayDict{ { "FooBar", "1" }, { "FOOBAR", "" } };
+ QTest::newRow("FooBar")
+ << QStringList{ "FooBar=2" }
+ << ByteArrayDict{ { "FooBar", "2" }, { "FOOBAR", "2" } };
+ QTest::newRow("FOOBAR")
+ << QStringList{ "FOOBAR=2" }
+ << ByteArrayDict{ { "FooBar", "1" }, { "FOOBAR", "2" } };
+ QTest::newRow("foobar")
+ << QStringList{ "foobar=2" }
+ << ByteArrayDict{ { "FooBar", "1" }, { "FOOBAR", "2" } };
+ QTest::newRow("FooBar_FOOBAR")
+ << QStringList{ "FooBar=2", "FOOBAR=3" }
+ << ByteArrayDict{ { "FooBar", "2" }, { "FOOBAR", "3" } };
+ QTest::newRow("FOOBAR_FooBar")
+ << QStringList{ "FOOBAR=2", "FooBar=3" }
+ << ByteArrayDict{ { "FooBar", "3" }, { "FOOBAR", "2" } };
+ QTest::newRow("FooBar_FOOBAR_foobar")
+ << QStringList{ "FooBar=2", "FOOBAR=3", "foobar=4" }
+ << ByteArrayDict{ { "FooBar", "2" }, { "FOOBAR", "3" } };
+ QTest::newRow("foobar_FooBar_FOOBAR")
+ << QStringList{ "foobar=2", "FooBar=3", "FOOBAR=4" }
+ << ByteArrayDict{ { "FooBar", "3" }, { "FOOBAR", "4" } };
+ QTest::newRow("FooBar_FooBar")
+ << QStringList{ "FooBar=2", "FooBar=3" }
+ << ByteArrayDict{ { "FooBar", "2" }, { "FOOBAR", "3" } };
+ QTest::newRow("FooBar_FooBar_FooBar")
+ << QStringList{ "FooBar=2", "FooBar=3", "FooBar=4" }
+ << ByteArrayDict{ { "FooBar", "2" }, { "FOOBAR", "4" } };
+ QTest::newRow("FOOBAR_FOOBAR")
+ << QStringList{ "FOOBAR=2", "FOOBAR=3" }
+ << ByteArrayDict{ { "FooBar", "1" }, { "FOOBAR", "2" } };
+ QTest::newRow("FOOBAR_FOOBAR_FOOBAR")
+ << QStringList{ "FOOBAR=2", "FOOBAR=3", "FOOBAR=4" }
+ << ByteArrayDict{ { "FooBar", "1" }, { "FOOBAR", "2" } };
+ QTest::newRow("FooBar_FooBar_FOOBAR_FOOBAR")
+ << QStringList{ "FooBar=2", "FooBar=3", "FOOBAR=4", "FOOBAR=5" }
+ << ByteArrayDict{ { "FooBar", "2" }, { "FOOBAR", "4" } };
+}
+
+void Tests::macrosOnCommandLine()
+{
+ QFETCH(QStringList, arguments);
+ QFETCH(ByteArrayDict, expectedMacros);
+ QVERIFY(runJom(arguments << "/nologo" << "/f" << "test.mk", "blackbox/macrosOnCommandLine"));
+ QCOMPARE(m_jomProcess->exitCode(), 0);
+ QList<QByteArray> output = m_jomProcess->readAllStandardOutput().split('\n');
+ std::transform(output.cbegin(), output.cend(), output.begin(),
+ [](const QByteArray &line) { return line.trimmed(); });
+ auto it = std::remove_if(output.begin(), output.end(), std::mem_fn(&QByteArray::isEmpty));
+ if (it != output.end())
+ output.erase(it, output.end());
+ ByteArrayDict macros;
+ for (const QByteArray &line : qAsConst(output)) {
+ auto x = line.split(':');
+ macros[x.at(0)] = x.at(1);
+ }
+ if (macros != expectedMacros) {
+ qDebug() << " actual:" << macros;
+ qDebug() << "expected:" << expectedMacros;
+ QFAIL("Unexpected macro values");
+ }
+}
+
void Tests::nonexistentDependent()
{
QVERIFY(runJom(QStringList() << "/nologo" << "/f" << "test.mk", "blackbox/nonexistentdependent"));
diff --git a/tests/tests.h b/tests/tests.h
index b98758c..820a078 100644
--- a/tests/tests.h
+++ b/tests/tests.h
@@ -80,6 +80,8 @@ private slots:
void builtin_cd_data();
void builtin_cd();
void suffixes();
+ void macrosOnCommandLine_data();
+ void macrosOnCommandLine();
void nonexistentDependent();
void noTargets();
void outOfDateCheck();