forked from qt-creator/qt-creator
provide upgrade path for various user file changes
- command argument quoting changes
- environment expansion changes
- substitution of ${SOURCE,BUILD}DIR with %{{source,build}Dir}
- VariableManger syntax change ${} => %{}
This commit is contained in:
@@ -41,6 +41,7 @@
|
|||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
#include <coreplugin/ifile.h>
|
#include <coreplugin/ifile.h>
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
|
#include <utils/qtcprocess.h>
|
||||||
|
|
||||||
#include <QtGui/QApplication>
|
#include <QtGui/QApplication>
|
||||||
#include <QtGui/QMainWindow>
|
#include <QtGui/QMainWindow>
|
||||||
@@ -218,6 +219,28 @@ public:
|
|||||||
QVariantMap update(Project *project, const QVariantMap &map);
|
QVariantMap update(Project *project, const QVariantMap &map);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Version 8 reflects the change of environment variable expansion rules,
|
||||||
|
// turning some env variables into expandos, the change of argument quoting rules,
|
||||||
|
// and the change of VariableManager's expansion syntax.
|
||||||
|
class Version8Handler : public UserFileVersionHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
int userFileVersion() const
|
||||||
|
{
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString displayUserFileVersion() const
|
||||||
|
{
|
||||||
|
// pre5 because we renamed 2.2 to 2.1 later, so people already have 2.2pre4 files
|
||||||
|
return QLatin1String("2.2pre5");
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantMap update(Project *project, const QVariantMap &map);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
//
|
//
|
||||||
// Helper functions:
|
// Helper functions:
|
||||||
//
|
//
|
||||||
@@ -237,7 +260,57 @@ static QString fileNameFor(const QString &name)
|
|||||||
return baseName + QLatin1String(PROJECT_FILE_POSTFIX);
|
return baseName + QLatin1String(PROJECT_FILE_POSTFIX);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
class HandlerNode {
|
||||||
|
public:
|
||||||
|
QSet<QString> strings;
|
||||||
|
QHash<QString, HandlerNode> children;
|
||||||
|
};
|
||||||
|
Q_DECLARE_TYPEINFO(HandlerNode, Q_MOVABLE_TYPE);
|
||||||
|
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
static HandlerNode buildHandlerNodes(const char * const **keys)
|
||||||
|
{
|
||||||
|
HandlerNode ret;
|
||||||
|
while (const char *rname = *(*keys)++) {
|
||||||
|
QString name = QLatin1String(rname);
|
||||||
|
if (name.endsWith(QLatin1Char('.'))) {
|
||||||
|
HandlerNode sub = buildHandlerNodes(keys);
|
||||||
|
foreach (const QString &key, name.split(QLatin1Char('|')))
|
||||||
|
ret.children.insert(key, sub);
|
||||||
|
} else {
|
||||||
|
ret.strings.insert(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QVariantMap processHandlerNodes(const HandlerNode &node, const QVariantMap &map,
|
||||||
|
QVariant (*handler)(const QVariant &var))
|
||||||
|
{
|
||||||
|
QVariantMap result;
|
||||||
|
QMapIterator<QString, QVariant> it(map);
|
||||||
|
while (it.hasNext()) {
|
||||||
|
it.next();
|
||||||
|
const QString &key = it.key();
|
||||||
|
if (node.strings.contains(key)) {
|
||||||
|
result.insert(key, handler(it.value()));
|
||||||
|
goto handled;
|
||||||
|
}
|
||||||
|
if (it.value().type() == QVariant::Map)
|
||||||
|
for (QHash<QString, HandlerNode>::ConstIterator subit = node.children.constBegin();
|
||||||
|
subit != node.children.constEnd(); ++subit)
|
||||||
|
if (key.startsWith(subit.key())) {
|
||||||
|
result.insert(key, processHandlerNodes(subit.value(), it.value().toMap(), handler));
|
||||||
|
goto handled;
|
||||||
|
}
|
||||||
|
result.insert(key, it.value());
|
||||||
|
handled: ;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// UserFileVersionHandler
|
// UserFileVersionHandler
|
||||||
@@ -292,6 +365,7 @@ UserFileAccessor::UserFileAccessor() :
|
|||||||
addVersionHandler(new Version5Handler);
|
addVersionHandler(new Version5Handler);
|
||||||
addVersionHandler(new Version6Handler);
|
addVersionHandler(new Version6Handler);
|
||||||
addVersionHandler(new Version7Handler);
|
addVersionHandler(new Version7Handler);
|
||||||
|
addVersionHandler(new Version8Handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
UserFileAccessor::~UserFileAccessor()
|
UserFileAccessor::~UserFileAccessor()
|
||||||
@@ -1419,3 +1493,306 @@ QVariantMap Version7Handler::update(Project *, const QVariantMap &map)
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Version8Handler
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Argument list reinterpretation
|
||||||
|
|
||||||
|
static const char * const argListKeys[] = {
|
||||||
|
"ProjectExplorer.Project.Target.",
|
||||||
|
"ProjectExplorer.Target.BuildConfiguration."
|
||||||
|
"|ProjectExplorer.Target.DeployConfiguration.",
|
||||||
|
"ProjectExplorer.BuildConfiguration.BuildStepList.",
|
||||||
|
"ProjectExplorer.BuildStepList.Step.",
|
||||||
|
"GenericProjectManager.GenericMakeStep.MakeArguments",
|
||||||
|
"QtProjectManager.QMakeBuildStep.QMakeArguments",
|
||||||
|
"Qt4ProjectManager.MakeStep.MakeArguments",
|
||||||
|
"CMakeProjectManager.MakeStep.AdditionalArguments",
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
"ProjectExplorer.Target.RunConfiguration.",
|
||||||
|
"ProjectExplorer.CustomExecutableRunConfiguration.Arguments",
|
||||||
|
"Qt4ProjectManager.Qt4RunConfiguration.CommandLineArguments",
|
||||||
|
"CMakeProjectManager.CMakeRunConfiguration.Arguments",
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char * const lameArgListKeys[] = {
|
||||||
|
"ProjectExplorer.Project.Target.",
|
||||||
|
"ProjectExplorer.Target.BuildConfiguration."
|
||||||
|
"|ProjectExplorer.Target.DeployConfiguration.",
|
||||||
|
"ProjectExplorer.BuildConfiguration.BuildStepList.",
|
||||||
|
"ProjectExplorer.BuildStepList.Step.",
|
||||||
|
"ProjectExplorer.ProcessStep.Arguments",
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
"ProjectExplorer.Target.RunConfiguration.",
|
||||||
|
"Qt4ProjectManager.MaemoRunConfiguration.Arguments",
|
||||||
|
"Qt4ProjectManager.S60DeviceRunConfiguration.CommandLineArguments",
|
||||||
|
"QmlProjectManager.QmlRunConfiguration.QDeclarativeViewerArguments",
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef Q_OS_UNIX
|
||||||
|
inline static bool isSpecialChar(ushort c)
|
||||||
|
{
|
||||||
|
// Chars that should be quoted (TM). This includes:
|
||||||
|
static const uchar iqm[] = {
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xdf, 0x07, 0x00, 0xd8,
|
||||||
|
0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x78
|
||||||
|
}; // 0-32 \'"$`<>|;&(){}*?#!~[]
|
||||||
|
|
||||||
|
return (c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7)));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static bool hasSpecialChars(const QString &arg)
|
||||||
|
{
|
||||||
|
for (int x = arg.length() - 1; x >= 0; --x)
|
||||||
|
if (isSpecialChar(arg.unicode()[x].unicode()))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// These were split according to sane (even if a bit arcane) rules
|
||||||
|
static QVariant version8ArgNodeHandler(const QVariant &var)
|
||||||
|
{
|
||||||
|
QString ret;
|
||||||
|
foreach (const QVariant &svar, var.toList()) {
|
||||||
|
#ifdef Q_OS_UNIX
|
||||||
|
// We don't just addArg, so we don't disarm existing env expansions.
|
||||||
|
// This is a bit fuzzy logic ...
|
||||||
|
QString s = svar.toString();
|
||||||
|
s.replace(QLatin1Char('\\'), QLatin1String("\\\\"));
|
||||||
|
s.replace(QLatin1Char('"'), QLatin1String("\\\""));
|
||||||
|
s.replace(QLatin1Char('`'), QLatin1String("\\`"));
|
||||||
|
if (s != svar.toString() || hasSpecialChars(s))
|
||||||
|
s.prepend(QLatin1Char('"')).append(QLatin1Char('"'));
|
||||||
|
Utils::QtcProcess::addArgs(&ret, s);
|
||||||
|
#else
|
||||||
|
// Under windows, env expansions cannot be quoted anyway.
|
||||||
|
Utils::QtcProcess::addArg(&ret, svar.toString());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return QVariant(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
// These were just split on whitespace
|
||||||
|
static QVariant version8LameArgNodeHandler(const QVariant &var)
|
||||||
|
{
|
||||||
|
QString ret;
|
||||||
|
foreach (const QVariant &svar, var.toList())
|
||||||
|
Utils::QtcProcess::addArgs(&ret, svar.toString());
|
||||||
|
return QVariant(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Environment variable reinterpretation
|
||||||
|
|
||||||
|
static const char * const envExpandedKeys[] = {
|
||||||
|
"ProjectExplorer.Project.Target.",
|
||||||
|
"ProjectExplorer.Target.BuildConfiguration."
|
||||||
|
"|ProjectExplorer.Target.DeployConfiguration.",
|
||||||
|
"ProjectExplorer.BuildConfiguration.BuildStepList.",
|
||||||
|
"ProjectExplorer.BuildStepList.Step.",
|
||||||
|
"ProjectExplorer.ProcessStep.WorkingDirectory",
|
||||||
|
"ProjectExplorer.ProcessStep.Command",
|
||||||
|
"ProjectExplorer.ProcessStep.Arguments",
|
||||||
|
"GenericProjectManager.GenericMakeStep.MakeCommand",
|
||||||
|
"GenericProjectManager.GenericMakeStep.MakeArguments",
|
||||||
|
"GenericProjectManager.GenericMakeStep.BuildTargets",
|
||||||
|
"QtProjectManager.QMakeBuildStep.QMakeArguments",
|
||||||
|
"Qt4ProjectManager.MakeStep.MakeCommand",
|
||||||
|
"Qt4ProjectManager.MakeStep.MakeArguments",
|
||||||
|
"CMakeProjectManager.MakeStep.AdditionalArguments",
|
||||||
|
"CMakeProjectManager.MakeStep.BuildTargets",
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
"Qt4ProjectManager.Qt4BuildConfiguration.BuildDirectory",
|
||||||
|
0,
|
||||||
|
"ProjectExplorer.Target.RunConfiguration.",
|
||||||
|
"ProjectExplorer.CustomExecutableRunConfiguration.WorkingDirectory",
|
||||||
|
"ProjectExplorer.CustomExecutableRunConfiguration.Executable",
|
||||||
|
"ProjectExplorer.CustomExecutableRunConfiguration.Arguments",
|
||||||
|
"Qt4ProjectManager.Qt4RunConfiguration.UserWorkingDirectory",
|
||||||
|
"Qt4ProjectManager.Qt4RunConfiguration.CommandLineArguments",
|
||||||
|
"Qt4ProjectManager.MaemoRunConfiguration.Arguments",
|
||||||
|
"Qt4ProjectManager.S60DeviceRunConfiguration.CommandLineArguments",
|
||||||
|
"CMakeProjectManager.CMakeRunConfiguration.UserWorkingDirectory",
|
||||||
|
"CMakeProjectManager.CMakeRunConfiguration.Arguments",
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
static QString version8NewVar(const QString &old)
|
||||||
|
{
|
||||||
|
QString ret = old;
|
||||||
|
#ifdef Q_OS_UNIX
|
||||||
|
ret.prepend(QLatin1String("${"));
|
||||||
|
ret.append(QLatin1Char('}'));
|
||||||
|
#else
|
||||||
|
ret.prepend(QLatin1Char('%'));
|
||||||
|
ret.append(QLatin1Char('%'));
|
||||||
|
#endif
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translate DOS-like env var expansions into Unix-like ones and vice versa.
|
||||||
|
// On the way, change {SOURCE,BUILD}DIR env expansions to %{}-expandos
|
||||||
|
static QVariant version8EnvNodeTransform(const QVariant &var)
|
||||||
|
{
|
||||||
|
QString result = var.toString();
|
||||||
|
|
||||||
|
result.replace(QRegExp(QLatin1String("%SOURCEDIR%|\\$(SOURCEDIR\\b|{SOURCEDIR})")),
|
||||||
|
QLatin1String("%{sourceDir}"));
|
||||||
|
result.replace(QRegExp(QLatin1String("%BUILDDIR%|\\$(BUILDDIR\\b|{BUILDDIR})")),
|
||||||
|
QLatin1String("%{buildDir}"));
|
||||||
|
#ifdef Q_OS_UNIX
|
||||||
|
for (int vStart = -1, i = 0; i < result.length(); ) {
|
||||||
|
QChar c = result.at(i++);
|
||||||
|
if (c == QLatin1Char('%')) {
|
||||||
|
if (vStart > 0 && vStart < i - 1) {
|
||||||
|
QString nv = version8NewVar(result.mid(vStart, i - 1 - vStart));
|
||||||
|
result.replace(vStart - 1, i - vStart + 1, nv);
|
||||||
|
i = vStart - 1 + nv.length();
|
||||||
|
vStart = -1;
|
||||||
|
} else {
|
||||||
|
vStart = i;
|
||||||
|
}
|
||||||
|
} else if (vStart > 0) {
|
||||||
|
// Sanity check so we don't catch too much garbage
|
||||||
|
if (!c.isLetterOrNumber() && c != QLatin1Char('_'))
|
||||||
|
vStart = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
enum { BASE, OPTIONALVARIABLEBRACE, VARIABLE, BRACEDVARIABLE } state = BASE;
|
||||||
|
int vStart = -1;
|
||||||
|
|
||||||
|
for (int i = 0; i < result.length();) {
|
||||||
|
QChar c = result.at(i++);
|
||||||
|
if (state == BASE) {
|
||||||
|
if (c == QLatin1Char('$'))
|
||||||
|
state = OPTIONALVARIABLEBRACE;
|
||||||
|
} else if (state == OPTIONALVARIABLEBRACE) {
|
||||||
|
if (c == QLatin1Char('{')) {
|
||||||
|
state = BRACEDVARIABLE;
|
||||||
|
vStart = i;
|
||||||
|
} else if (c.isLetterOrNumber() || c == QLatin1Char('_')) {
|
||||||
|
state = VARIABLE;
|
||||||
|
vStart = i - 1;
|
||||||
|
} else {
|
||||||
|
state = BASE;
|
||||||
|
}
|
||||||
|
} else if (state == BRACEDVARIABLE) {
|
||||||
|
if (c == QLatin1Char('}')) {
|
||||||
|
QString nv = version8NewVar(result.mid(vStart, i - 1 - vStart));
|
||||||
|
result.replace(vStart - 2, i - vStart + 2, nv);
|
||||||
|
i = vStart + nv.length();
|
||||||
|
state = BASE;
|
||||||
|
}
|
||||||
|
} else if (state == VARIABLE) {
|
||||||
|
if (!c.isLetterOrNumber() && c != QLatin1Char('_')) {
|
||||||
|
QString nv = version8NewVar(result.mid(vStart, i - 1 - vStart));
|
||||||
|
result.replace(vStart - 1, i - vStart, nv);
|
||||||
|
i = vStart - 1 + nv.length(); // On the same char - could be next expansion.
|
||||||
|
state = BASE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (state == VARIABLE) {
|
||||||
|
QString nv = version8NewVar(result.mid(vStart));
|
||||||
|
result.truncate(vStart - 1);
|
||||||
|
result += nv;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return QVariant(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static QVariant version8EnvNodeHandler(const QVariant &var)
|
||||||
|
{
|
||||||
|
if (var.type() != QVariant::List)
|
||||||
|
return version8EnvNodeTransform(var);
|
||||||
|
|
||||||
|
QVariantList vl;
|
||||||
|
foreach (const QVariant &svar, var.toList())
|
||||||
|
vl << version8EnvNodeTransform(svar);
|
||||||
|
return vl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// VariableManager expando reinterpretation
|
||||||
|
|
||||||
|
static const char * const varExpandedKeys[] = {
|
||||||
|
"ProjectExplorer.Project.Target.",
|
||||||
|
"ProjectExplorer.Target.BuildConfiguration."
|
||||||
|
"|ProjectExplorer.Target.DeployConfiguration.",
|
||||||
|
"ProjectExplorer.BuildConfiguration.BuildStepList.",
|
||||||
|
"ProjectExplorer.BuildStepList.Step.",
|
||||||
|
"GenericProjectManager.GenericMakeStep.MakeCommand",
|
||||||
|
"GenericProjectManager.GenericMakeStep.MakeArguments",
|
||||||
|
"GenericProjectManager.GenericMakeStep.BuildTargets",
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
// Translate old-style ${} var expansions into new-style %{} ones
|
||||||
|
static QVariant version8VarNodeHandler(const QVariant &var)
|
||||||
|
{
|
||||||
|
static const char * const vars[] = {
|
||||||
|
"absoluteFilePath",
|
||||||
|
"absolutePath",
|
||||||
|
"baseName",
|
||||||
|
"canonicalPath",
|
||||||
|
"canonicalFilePath",
|
||||||
|
"completeBaseName",
|
||||||
|
"completeSuffix",
|
||||||
|
"fileName",
|
||||||
|
"filePath",
|
||||||
|
"path",
|
||||||
|
"suffix"
|
||||||
|
};
|
||||||
|
static QSet<QString> map;
|
||||||
|
if (map.isEmpty())
|
||||||
|
for (unsigned i = 0; i < sizeof(vars)/sizeof(vars[0]); i++)
|
||||||
|
map.insert(QLatin1String("CURRENT_DOCUMENT:") + QLatin1String(vars[i]));
|
||||||
|
|
||||||
|
QString str = var.toString();
|
||||||
|
int pos = 0;
|
||||||
|
forever {
|
||||||
|
int openPos = str.indexOf(QLatin1String("${"), pos);
|
||||||
|
if (openPos < 0)
|
||||||
|
break;
|
||||||
|
int varPos = openPos + 2;
|
||||||
|
int closePos = str.indexOf(QLatin1Char('}'), varPos);
|
||||||
|
if (closePos < 0)
|
||||||
|
break;
|
||||||
|
if (map.contains(str.mid(varPos, closePos - varPos)))
|
||||||
|
str[openPos] = QLatin1Char('%');
|
||||||
|
pos = closePos + 1;
|
||||||
|
}
|
||||||
|
return QVariant(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantMap Version8Handler::update(Project *, const QVariantMap &map)
|
||||||
|
{
|
||||||
|
const char * const *p1 = argListKeys;
|
||||||
|
QVariantMap rmap1 = processHandlerNodes(buildHandlerNodes(&p1), map, version8ArgNodeHandler);
|
||||||
|
const char * const *p2 = lameArgListKeys;
|
||||||
|
QVariantMap rmap2 = processHandlerNodes(buildHandlerNodes(&p2), rmap1, version8LameArgNodeHandler);
|
||||||
|
const char * const *p3 = envExpandedKeys;
|
||||||
|
QVariantMap rmap3 = processHandlerNodes(buildHandlerNodes(&p3), rmap2, version8EnvNodeHandler);
|
||||||
|
const char * const *p4 = varExpandedKeys;
|
||||||
|
return processHandlerNodes(buildHandlerNodes(&p4), rmap3, version8VarNodeHandler);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user