fix host vs. makefile directory separator mess

the system path separator and shell are bound to the host system
(system() will use cmd even on mingw with sh.exe in path).
the makefiles otoh may depend on what the qmakespec defines.

consequently, add $$system_path() and $$system_quote() (for use with
system() & $$system()). $$native_path() is renamed to $$shell_path() and
should be used with $$shell_quote() to produce command lines in
makefiles.
$$QMAKE_DIR_SEP needs to be applied to Option::dir_sep right after
parsing the spec, so it is available to $$shell_{path,quote}().

Change-Id: If3db4849e7f96068cf03a32348a24f3a72d6292c
Reviewed-by: Rafael Roquetto <rafael.roquetto@kdab.com>
Reviewed-by: Sean Harmer <sean.harmer@kdab.com>
This commit is contained in:
Oswald Buddenhagen 2012-07-25 10:51:44 +02:00 committed by Qt by Nokia
parent d9048bef20
commit e07372ff50
10 changed files with 86 additions and 51 deletions

View File

@ -44,7 +44,7 @@ defineTest(qtCompileTest) {
test_dir = $$QMAKE_CONFIG_TESTS_DIR/$$1
test_out_dir = $$shadowed($$test_dir)
test_cmd_base = "cd $$shell_quote($$native_path($$test_out_dir)) &&"
test_cmd_base = "cd $$system_quote($$system_path($$test_out_dir)) &&"
# Disable qmake features which are typically counterproductive for tests
qmake_configs = "\"CONFIG -= qt debug_and_release app_bundle lib_bundle\""
@ -54,7 +54,7 @@ defineTest(qtCompileTest) {
mkpath($$test_out_dir)|error("Aborting.")
qtRunLoggedCommand("$$test_cmd_base $$shell_quote($$native_path($$QMAKE_QMAKE)) $$qmake_configs $$shell_quote($$test_dir)") {
qtRunLoggedCommand("$$test_cmd_base $$system_quote($$system_path($$QMAKE_QMAKE)) $$qmake_configs $$shell_quote($$test_dir)") {
qtRunLoggedCommand("$$test_cmd_base $$QMAKE_MAKE") {
log("yes$$escape_expand(\\n)")
msg = "test $$1 succeeded"

View File

@ -31,8 +31,8 @@ breakpad {
load(resolve_target)
win32: QMAKE_CLEAN += $$replace(QMAKE_RESOLVED_TARGET, ...$, pdb) # for the debug case it is hardcoded in qmake
DEBUGFILENAME = $$shell_quote($$native_path($$QMAKE_RESOLVED_TARGET))
PROJECTPATH = $$shell_quote($$native_path($$OUT_PWD))
DEBUGFILENAME = $$shell_quote($$shell_path($$QMAKE_RESOLVED_TARGET))
PROJECTPATH = $$shell_quote($$shell_path($$OUT_PWD))
!isEmpty(QMAKE_POST_LINK):QMAKE_POST_LINK = $$QMAKE_POST_LINK$$escape_expand(\\n\\t)
QMAKE_POST_LINK = $$QMAKE_POST_LINK$$quote($${QT_BREAKPAD_ROOT_PATH}$${QMAKE_DIR_SEP}qtbreakpadsymbols $$DEBUGFILENAME $$PROJECTPATH)

View File

@ -6,6 +6,6 @@ contains(TEMPLATE, "vc.*") {
win32-msvc2*|wince*msvc*: EOC = $$escape_expand(\\r\\h)
for(xge, INCREDIBUILD_XGE) {
$${xge}.commands = Rem IncrediBuild_AllowRemote $$EOC Rem IncrediBuild_OutputFile $$native_path($${xge}.output) $$EOC $$eval($${xge}.commands)
$${xge}.commands = Rem IncrediBuild_AllowRemote $$EOC Rem IncrediBuild_OutputFile $$shell_path($${xge}.output) $$EOC $$eval($${xge}.commands)
}
}

View File

@ -137,12 +137,12 @@ defineTest(qtPrepareTool) {
}
}
}
$$1 = $$native_path($$eval($$1))
$$1 = $$shell_path($$eval($$1))
deps = $$resolve_depends(QT_TOOL.$${2}.depends, "QT.")
!isEmpty(deps) {
for(dep, deps): \
deppath += $$native_path($$eval(QT.$${dep}.libs))
deppath += $$shell_path($$eval(QT.$${dep}.libs))
deppath = $$unique(deppath)
equals(QMAKE_DIR_SEP, /) {
equals(QMAKE_HOST.os, Windows): \

View File

@ -25,7 +25,7 @@ TESTCOCOON_COVERAGE_OPTIONS = \
# is built directly in target.path and there is no need to move the csmes.
!isEmpty(DESTDIR):contains(TEMPLATE, lib) {
!isEmpty(QMAKE_POST_LINK):QMAKE_POST_LINK = $$escape_expand(\\n\\t)$$QMAKE_POST_LINK
QMAKE_POST_LINK = -$(MOVE) $${TARGET_BASENAME}.csmes $$native_path($${QMAKE_RESOLVED_TARGET}.csmes)$$QMAKE_POST_LINK
QMAKE_POST_LINK = -$(MOVE) $${TARGET_BASENAME}.csmes $$shell_path($${QMAKE_RESOLVED_TARGET}.csmes)$$QMAKE_POST_LINK
}
QMAKE_CLEAN += *.csexe *.csmes

View File

@ -7,6 +7,6 @@
NOPATH_TARGET ~= s,^(.*/)+,, # Remove all paths
QMAKE_LFLAGS += /MANIFEST $$quote(/MANIFESTFILE:\"$${MANIFEST_DIR}\\$${NOPATH_TARGET}.intermediate.manifest\")
!isEmpty(QMAKE_POST_LINK):QMAKE_POST_LINK = $$escape_expand(\\n\\t)$$QMAKE_POST_LINK
QMAKE_POST_LINK = $$quote(mt.exe -nologo -manifest $$shell_quote($$native_path($$MANIFEST_DIR/$${NOPATH_TARGET}.intermediate.manifest)) -outputresource:$(DESTDIR_TARGET);2)$$QMAKE_POST_LINK
QMAKE_POST_LINK = $$quote(mt.exe -nologo -manifest $$shell_quote($$shell_path($$MANIFEST_DIR/$${NOPATH_TARGET}.intermediate.manifest)) -outputresource:$(DESTDIR_TARGET);2)$$QMAKE_POST_LINK
QMAKE_CLEAN += $$MANIFEST_DIR/$${NOPATH_TARGET}.intermediate.manifest
}

View File

@ -7,6 +7,6 @@ if(win32-msvc2005*|win32-msvc2008*|win32-msvc2010*):equals(TEMPLATE, "app") {
NOPATH_TARGET ~= s,^(.*/)+,, # Remove all paths
QMAKE_LFLAGS += /MANIFEST $$quote(/MANIFESTFILE:\"$${MANIFEST_DIR}\\$${NOPATH_TARGET}.intermediate.manifest\")
!isEmpty(QMAKE_POST_LINK):QMAKE_POST_LINK = $$escape_expand(\\n\\t)$$QMAKE_POST_LINK
QMAKE_POST_LINK = $$quote(mt.exe -nologo -manifest $$shell_quote($$native_path($$MANIFEST_DIR/$${NOPATH_TARGET}.intermediate.manifest)) -outputresource:$(DESTDIR_TARGET);1)$$QMAKE_POST_LINK
QMAKE_POST_LINK = $$quote(mt.exe -nologo -manifest $$shell_quote($$shell_path($$MANIFEST_DIR/$${NOPATH_TARGET}.intermediate.manifest)) -outputresource:$(DESTDIR_TARGET);1)$$QMAKE_POST_LINK
QMAKE_CLEAN += $$MANIFEST_DIR/$${NOPATH_TARGET}.intermediate.manifest
}

View File

@ -555,7 +555,6 @@ bool Option::postProcessProject(QMakeProject *project)
Option::h_moc_mod = project->first("QMAKE_H_MOD_MOC");
Option::lex_mod = project->first("QMAKE_MOD_LEX");
Option::yacc_mod = project->first("QMAKE_MOD_YACC");
Option::dir_sep = project->first("QMAKE_DIR_SEP");
if (Option::output_dir.startsWith(project->buildRoot()))
Option::mkfile::cachefile_depth =

View File

@ -82,8 +82,8 @@ enum ExpandFunc { E_MEMBER=1, E_FIRST, E_LAST, E_CAT, E_FROMFILE, E_EVAL, E_LIST
E_FIND, E_SYSTEM, E_UNIQUE, E_REVERSE, E_QUOTE, E_ESCAPE_EXPAND,
E_UPPER, E_LOWER, E_FILES, E_PROMPT, E_RE_ESCAPE, E_VAL_ESCAPE, E_REPLACE,
E_SIZE, E_SORT_DEPENDS, E_RESOLVE_DEPENDS, E_ENUMERATE_VARS,
E_SHADOWED, E_ABSOLUTE_PATH, E_RELATIVE_PATH, E_CLEAN_PATH, E_NATIVE_PATH,
E_SHELL_QUOTE };
E_SHADOWED, E_ABSOLUTE_PATH, E_RELATIVE_PATH, E_CLEAN_PATH,
E_SYSTEM_PATH, E_SHELL_PATH, E_SYSTEM_QUOTE, E_SHELL_QUOTE };
QHash<QString, ExpandFunc> qmake_expandFunctions()
{
static QHash<QString, ExpandFunc> *qmake_expand_functions = 0;
@ -125,7 +125,9 @@ QHash<QString, ExpandFunc> qmake_expandFunctions()
qmake_expand_functions->insert("absolute_path", E_ABSOLUTE_PATH);
qmake_expand_functions->insert("relative_path", E_RELATIVE_PATH);
qmake_expand_functions->insert("clean_path", E_CLEAN_PATH);
qmake_expand_functions->insert("native_path", E_NATIVE_PATH);
qmake_expand_functions->insert("system_path", E_SYSTEM_PATH);
qmake_expand_functions->insert("shell_path", E_SHELL_PATH);
qmake_expand_functions->insert("system_quote", E_SYSTEM_QUOTE);
qmake_expand_functions->insert("shell_quote", E_SHELL_QUOTE);
}
return *qmake_expand_functions;
@ -1498,6 +1500,8 @@ QMakeProject::read(uchar cmd)
doProjectInclude("spec_post", IncludeFlagFeature, vars);
// The spec extends the feature search path, so invalidate the cache.
invalidateFeatureRoots();
// The MinGW and x-build specs may change the separator; $$shell_{path,quote}() need it
Option::dir_sep = first(QLatin1String("QMAKE_DIR_SEP"));
if (!conffile.isEmpty()) {
debug_msg(1, "Project config file: reading %s", conffile.toLatin1().constData());
@ -1895,10 +1899,41 @@ subAll(QStringList *val, const QStringList &diffval)
}
inline static
bool isSpecialChar(ushort c)
bool hasSpecialChars(const QString &arg, const uchar (&iqm)[16])
{
for (int x = arg.length() - 1; x >= 0; --x) {
ushort c = arg.unicode()[x].unicode();
if ((c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7))))
return true;
}
return false;
}
static QString
shellQuoteUnix(const QString &arg)
{
// 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 \'"$`<>|;&(){}*?#!~[]
if (!arg.length())
return QString::fromLatin1("\"\"");
QString ret(arg);
if (hasSpecialChars(ret, iqm)) {
ret.replace(QLatin1Char('\''), QLatin1String("'\\''"));
ret.prepend(QLatin1Char('\''));
ret.append(QLatin1Char('\''));
}
return ret;
}
static QString
shellQuoteWin(const QString &arg)
{
// Chars that should be quoted (TM). This includes:
#ifdef Q_OS_WIN
// - control chars & space
// - the shell meta chars "&()<>^|
// - the potential separators ,;=
@ -1906,34 +1941,12 @@ bool isSpecialChar(ushort c)
0xff, 0xff, 0xff, 0xff, 0x45, 0x13, 0x00, 0x78,
0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x10
};
#else
static const uchar iqm[] = {
0xff, 0xff, 0xff, 0xff, 0xdf, 0x07, 0x00, 0xd8,
0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x78
}; // 0-32 \'"$`<>|;&(){}*?#!~[]
#endif
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;
}
static QString
shellQuote(const QString &arg)
{
if (!arg.length())
return QString::fromLatin1("\"\"");
QString ret(arg);
if (hasSpecialChars(ret)) {
#ifdef Q_OS_WIN
if (hasSpecialChars(ret, iqm)) {
// Quotes are escaped and their preceding backslashes are doubled.
// It's impossible to escape anything inside a quoted string on cmd
// level, so the outer quoting must be "suspended".
@ -1946,11 +1959,6 @@ shellQuote(const QString &arg)
--i;
ret.insert(i, QLatin1Char('"'));
ret.prepend(QLatin1Char('"'));
#else // Q_OS_WIN
ret.replace(QLatin1Char('\''), QLatin1String("'\\''"));
ret.prepend(QLatin1Char('\''));
ret.append(QLatin1Char('\''));
#endif // Q_OS_WIN
}
return ret;
}
@ -2738,19 +2746,39 @@ QMakeProject::doProjectExpand(QString func, QList<QStringList> args_list,
else
ret += QDir::cleanPath(args.at(0));
break;
case E_NATIVE_PATH:
case E_SYSTEM_PATH:
if (args.count() != 1)
fprintf(stderr, "%s:%d native_path(path) requires one argument.\n",
fprintf(stderr, "%s:%d system_path(path) requires one argument.\n",
parser.file.toLatin1().constData(), parser.line_no);
else
ret += Option::fixPathToLocalOS(args.at(0), false);
break;
case E_SHELL_PATH:
if (args.count() != 1)
fprintf(stderr, "%s:%d shell_path(path) requires one argument.\n",
parser.file.toLatin1().constData(), parser.line_no);
else
ret += Option::fixPathToTargetOS(args.at(0), false);
break;
case E_SYSTEM_QUOTE:
if (args.count() != 1)
fprintf(stderr, "%s:%d system_quote(args) requires one argument.\n",
parser.file.toLatin1().constData(), parser.line_no);
else
#ifdef Q_OS_WIN
ret += shellQuoteWin(args.at(0));
#else
ret += shellQuoteUnix(args.at(0));
#endif
break;
case E_SHELL_QUOTE:
if (args.count() != 1)
fprintf(stderr, "%s:%d shell_quote(args) requires one argument.\n",
parser.file.toLatin1().constData(), parser.line_no);
else if (Option::dir_sep.at(0) != QLatin1Char('/'))
ret += shellQuoteWin(args.at(0));
else
ret += shellQuote(args.at(0));
ret += shellQuoteUnix(args.at(0));
break;
default: {
fprintf(stderr, "%s:%d: Unknown replace function: %s\n",

View File

@ -131,7 +131,8 @@ testReplace($$format_number(13, width=5 padsign zeropad), " 0013", "zero-padded
testReplace($$clean_path("c:$${DIR_SEPARATOR}crazy//path/../trolls"), "c:/crazy/trolls", "clean_path")
testReplace($$native_path("/crazy/trolls"), "$${DIR_SEPARATOR}crazy$${DIR_SEPARATOR}trolls", "native_path")
testReplace($$shell_path("/crazy/trolls"), "$${QMAKE_DIR_SEP}crazy$${QMAKE_DIR_SEP}trolls", "shell_path")
testReplace($$system_path("/crazy/trolls"), "$${DIR_SEPARATOR}crazy$${DIR_SEPARATOR}trolls", "system_path")
testReplace($$absolute_path("crazy/trolls"), "$$PWD/crazy/trolls", "absolute_path")
testReplace($$absolute_path("crazy/trolls", "/fake/path"), "/fake/path/crazy/trolls", "absolute_path with base")
@ -142,10 +143,17 @@ testReplace($$relative_path(""), "", "relative_path of empty")
#this test is very rudimentary. the backend function is thoroughly tested in qt creator
in = "some nasty\" path\\"
win32: \
out = "\"some nasty\"\\^\"\" path\"\\"
out_cmd = "\"some nasty\"\\^\"\" path\"\\"
out_sh = "'some nasty\" path\\'"
equals(QMAKE_HOST.os, Windows): \
out = $$out_cmd
else: \
out = "'some nasty\" path\\'"
out = $$out_sh
testReplace($$system_quote($$in), $$out, "system_quote")
!equals(QMAKE_DIR_SEP, /): \
out = $$out_cmd
else: \
out = $$out_sh
testReplace($$shell_quote($$in), $$out, "shell_quote")
testReplace($$reverse($$list(one two three)), three two one, "reverse")