qmake: require a drive in a DOS path for it to be absolute

For Q_OS_WIN, a path is only truly absolute if it includes a drive
letter; merely starting with a slash is not enough.  (We can't support
UNC paths, so don't even try: qmake runs various commands in the
source directory using CMD.exe, which doesn't support UNC as PWD.)
This requires, when resolving a path relative to a root, transcribing
the root's drive to such not-quite-absolute paths.

Changed QMakeGlobals, $$absolute_path() and $$relative_path() to now
use IoUtils::resolvePath() rather than delegating to QDir's absolute
path method, since that doesn't correctly recognize the need for a
drive letter (and qmake did run into problems with some paths, from
splitPathList and a failing test, as a result).

Change-Id: I2bfc13c1bfbe1ae09997274622ea55cb3de31b43
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@qt.io>
(cherry picked from qtbase/e86f3c018833141776db2d15772ba53995656eac)
Reviewed-by: Joerg Bornemann <joerg.bornemann@qt.io>
Reviewed-by: Tobias Hunger <tobias.hunger@qt.io>
This commit is contained in:
Edward Welbourne
2017-07-28 13:25:27 +02:00
committed by Oswald Buddenhagen
parent a13776d848
commit 912dd8df64
3 changed files with 30 additions and 21 deletions

View File

@@ -64,21 +64,22 @@ IoUtils::FileType IoUtils::fileType(const QString &fileName)
bool IoUtils::isRelativePath(const QString &path) bool IoUtils::isRelativePath(const QString &path)
{ {
if (path.startsWith(QLatin1Char('/')))
return false;
#ifdef QMAKE_BUILTIN_PRFS #ifdef QMAKE_BUILTIN_PRFS
if (path.startsWith(QLatin1String(":/"))) if (path.startsWith(QLatin1String(":/")))
return false; return false;
#endif #endif
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
if (path.startsWith(QLatin1Char('\\'))) // Unlike QFileInfo, this considers only paths with both a drive prefix and
return false; // a subsequent (back-)slash absolute:
// Unlike QFileInfo, this won't accept a relative path with a drive letter.
// Such paths result in a royal mess anyway ...
if (path.length() >= 3 && path.at(1) == QLatin1Char(':') && path.at(0).isLetter() if (path.length() >= 3 && path.at(1) == QLatin1Char(':') && path.at(0).isLetter()
&& (path.at(2) == QLatin1Char('/') || path.at(2) == QLatin1Char('\\'))) && (path.at(2) == QLatin1Char('/') || path.at(2) == QLatin1Char('\\'))) {
return false; return false;
#endif }
// (... unless, of course, they're UNC, which qmake fails on anyway)
#else
if (path.startsWith(QLatin1Char('/')))
return false;
#endif // Q_OS_WIN
return true; return true;
} }
@@ -98,6 +99,12 @@ QString IoUtils::resolvePath(const QString &baseDir, const QString &fileName)
return QString(); return QString();
if (isAbsolutePath(fileName)) if (isAbsolutePath(fileName))
return QDir::cleanPath(fileName); return QDir::cleanPath(fileName);
#ifdef Q_OS_WIN // Add drive to otherwise-absolute path:
if (fileName.at(0).unicode() == '/' || fileName.at(0).unicode() == '\\') {
Q_ASSERT(isAbsolutePath(baseDir));
return QDir::cleanPath(baseDir.left(2) + fileName);
}
#endif // Q_OS_WIN
return QDir::cleanPath(baseDir + QLatin1Char('/') + fileName); return QDir::cleanPath(baseDir + QLatin1Char('/') + fileName);
} }

View File

@@ -1174,9 +1174,9 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinExpand(
if (args.count() > 2) { if (args.count() > 2) {
evalError(fL1S("absolute_path(path[, base]) requires one or two arguments.")); evalError(fL1S("absolute_path(path[, base]) requires one or two arguments."));
} else { } else {
QString rstr = QDir::cleanPath( QString arg = args.at(0).toQString(m_tmp1);
QDir(args.count() > 1 ? args.at(1).toQString(m_tmp2) : currentDirectory()) QString baseDir = args.count() > 1 ? args.at(1).toQString(m_tmp2) : currentDirectory();
.absoluteFilePath(args.at(0).toQString(m_tmp1))); QString rstr = arg.isEmpty() ? baseDir : IoUtils::resolvePath(baseDir, arg);
ret << (rstr.isSharedWith(m_tmp1) ret << (rstr.isSharedWith(m_tmp1)
? args.at(0) ? args.at(0)
: args.count() > 1 && rstr.isSharedWith(m_tmp2) : args.count() > 1 && rstr.isSharedWith(m_tmp2)
@@ -1188,9 +1188,10 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinExpand(
if (args.count() > 2) { if (args.count() > 2) {
evalError(fL1S("relative_path(path[, base]) requires one or two arguments.")); evalError(fL1S("relative_path(path[, base]) requires one or two arguments."));
} else { } else {
QDir baseDir(args.count() > 1 ? args.at(1).toQString(m_tmp2) : currentDirectory()); QString arg = args.at(0).toQString(m_tmp1);
QString rstr = baseDir.relativeFilePath(baseDir.absoluteFilePath( QString baseDir = args.count() > 1 ? args.at(1).toQString(m_tmp2) : currentDirectory();
args.at(0).toQString(m_tmp1))); QString absArg = arg.isEmpty() ? baseDir : IoUtils::resolvePath(baseDir, arg);
QString rstr = QDir(baseDir).relativeFilePath(absArg);
ret << (rstr.isSharedWith(m_tmp1) ? args.at(0) : ProString(rstr).setSource(args.at(0))); ret << (rstr.isSharedWith(m_tmp1) ? args.at(0) : ProString(rstr).setSource(args.at(0)));
} }
break; break;

View File

@@ -65,6 +65,7 @@
#endif #endif
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
using namespace QMakeInternal; // for IoUtils
#define fL1S(s) QString::fromLatin1(s) #define fL1S(s) QString::fromLatin1(s)
@@ -93,9 +94,9 @@ QString QMakeGlobals::cleanSpec(QMakeCmdLineParserState &state, const QString &s
{ {
QString ret = QDir::cleanPath(spec); QString ret = QDir::cleanPath(spec);
if (ret.contains(QLatin1Char('/'))) { if (ret.contains(QLatin1Char('/'))) {
QString absRet = QDir(state.pwd).absoluteFilePath(ret); QString absRet = IoUtils::resolvePath(state.pwd, ret);
if (QFile::exists(absRet)) if (QFile::exists(absRet))
ret = QDir::cleanPath(absRet); ret = absRet;
} }
return ret; return ret;
} }
@@ -123,10 +124,10 @@ QMakeGlobals::ArgumentReturn QMakeGlobals::addCommandLineArguments(
user_template_prefix = arg; user_template_prefix = arg;
break; break;
case ArgCache: case ArgCache:
cachefile = args[*pos] = QDir::cleanPath(QDir(state.pwd).absoluteFilePath(arg)); cachefile = args[*pos] = IoUtils::resolvePath(state.pwd, arg);
break; break;
case ArgQtConf: case ArgQtConf:
qtconf = args[*pos] = QDir::cleanPath(QDir(state.pwd).absoluteFilePath(arg)); qtconf = args[*pos] = IoUtils::resolvePath(state.pwd, arg);
break; break;
default: default:
if (arg.startsWith(QLatin1Char('-'))) { if (arg.startsWith(QLatin1Char('-'))) {
@@ -256,11 +257,11 @@ QStringList QMakeGlobals::splitPathList(const QString &val) const
{ {
QStringList ret; QStringList ret;
if (!val.isEmpty()) { if (!val.isEmpty()) {
QDir bdir; QString cwd(QDir::currentPath());
const QStringList vals = val.split(dirlist_sep); const QStringList vals = val.split(dirlist_sep);
ret.reserve(vals.length()); ret.reserve(vals.length());
for (const QString &it : vals) for (const QString &it : vals)
ret << QDir::cleanPath(bdir.absoluteFilePath(it)); ret << IoUtils::resolvePath(cwd, it);
} }
return ret; return ret;
} }
@@ -315,7 +316,7 @@ bool QMakeGlobals::initProperties()
return false; return false;
data = proc.readAll(); data = proc.readAll();
#else #else
if (FILE *proc = QT_POPEN(QString(QMakeInternal::IoUtils::shellQuote(qmake_abslocation) if (FILE *proc = QT_POPEN(QString(IoUtils::shellQuote(qmake_abslocation)
+ QLatin1String(" -query")).toLocal8Bit(), QT_POPEN_READ)) { + QLatin1String(" -query")).toLocal8Bit(), QT_POPEN_READ)) {
char buff[1024]; char buff[1024];
while (!feof(proc)) while (!feof(proc))