qmake: Harden logic for handling the -o option

We now treat -o foo/bar/baz as a request to generate the output in the
foo/bar directory with baz as the output name, or if foo/bar/baz is already
a directory, in the foo/bar/baz directory with the default output name.

We take care to handle generator specific directory structures, so
that the project directory does not get merged into OUT_PWD. This is
done in runQmake(), before parsing the project file, so that OUT_PWD
will be correct during project parsing. The individual generators are
then passed the filename relative to the final output directory.

Each generator now also makes sure to add the right project suffix
to the output file, so -o foo will result in foo.pro or foo.vcproj,
instead of just foo.

Task-number: QTBUG-44408
Change-Id: I26990cec0c0458bee2b88dbb86322617a85f54b5
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@qt.io>
This commit is contained in:
Tor Arne Vestbø 2018-07-17 15:11:07 +02:00
parent 3ed22c5efe
commit 8cea3ec8ce
6 changed files with 58 additions and 104 deletions

View File

@ -1882,33 +1882,28 @@ ProjectBuilderMakefileGenerator::keyFor(const QString &block)
bool bool
ProjectBuilderMakefileGenerator::openOutput(QFile &file, const QString &build) const ProjectBuilderMakefileGenerator::openOutput(QFile &file, const QString &build) const
{ {
if(QDir::isRelativePath(file.fileName())) Q_ASSERT_X(QDir::isRelativePath(file.fileName()), "ProjectBuilderMakefileGenerator",
file.setFileName(Option::output_dir + "/" + file.fileName()); //pwd when qmake was run "runQMake() should have normalized the filename and made it relative");
QFileInfo fi(fileInfo(file.fileName())); QFileInfo fi(fileInfo(file.fileName()));
if(fi.suffix() != "pbxproj" || file.fileName().isEmpty()) { if (fi.suffix() != "pbxproj") {
QString output = file.fileName(); QString output = file.fileName();
if(fi.isDir()) if (!output.endsWith(projectSuffix())) {
output += QDir::separator(); if (fi.fileName().isEmpty()) {
if(!output.endsWith(projectSuffix())) { if (project->first("TEMPLATE") == "subdirs" || project->isEmpty("QMAKE_ORIG_TARGET"))
if(file.fileName().isEmpty() || fi.isDir()) {
if(project->first("TEMPLATE") == "subdirs" || project->isEmpty("QMAKE_ORIG_TARGET"))
output += fileInfo(project->projectFile()).baseName(); output += fileInfo(project->projectFile()).baseName();
else else
output += project->first("QMAKE_ORIG_TARGET").toQString(); output += project->first("QMAKE_ORIG_TARGET").toQString();
} }
output += projectSuffix() + QDir::separator(); output += projectSuffix() + QDir::separator();
} else if(output[(int)output.length() - 1] != QDir::separator()) { } else {
output += QDir::separator(); output += QDir::separator();
} }
output += QString("project.pbxproj"); output += QString("project.pbxproj");
file.setFileName(output); file.setFileName(output);
bool ret = UnixMakefileGenerator::openOutput(file, build);
((ProjectBuilderMakefileGenerator*)this)->pbx_dir = Option::output_dir.section(Option::dir_sep, 0, -1);
Option::output_dir = pbx_dir.section(Option::dir_sep, 0, -2);
return ret;
} }
((ProjectBuilderMakefileGenerator*)this)->pbx_dir = Option::output_dir; pbx_dir = Option::output_dir + Option::dir_sep + file.fileName().section(Option::dir_sep, 0, 0);
return UnixMakefileGenerator::openOutput(file, build); return UnixMakefileGenerator::openOutput(file, build);
} }

View File

@ -36,7 +36,7 @@ QT_BEGIN_NAMESPACE
class ProjectBuilderMakefileGenerator : public UnixMakefileGenerator class ProjectBuilderMakefileGenerator : public UnixMakefileGenerator
{ {
bool writingUnixMakefileGenerator; bool writingUnixMakefileGenerator;
QString pbx_dir; mutable QString pbx_dir;
int pbuilderVersion() const; int pbuilderVersion() const;
bool writeSubDirs(QTextStream &); bool writeSubDirs(QTextStream &);
bool writeMakeParts(QTextStream &); bool writeMakeParts(QTextStream &);

View File

@ -3167,54 +3167,31 @@ MakefileGenerator::specdir()
bool bool
MakefileGenerator::openOutput(QFile &file, const QString &build) const MakefileGenerator::openOutput(QFile &file, const QString &build) const
{ {
{ debug_msg(3, "asked to open output file '%s' in %s",
QString outdir; qPrintable(file.fileName()), qPrintable(Option::output_dir));
if(!file.fileName().isEmpty()) {
if(QDir::isRelativePath(file.fileName())) if (file.fileName().isEmpty()) {
file.setFileName(Option::output_dir + "/" + file.fileName()); //pwd when qmake was run file.setFileName(!project->isEmpty("MAKEFILE")
QFileInfo fi(fileInfo(file.fileName())); ? project->first("MAKEFILE").toQString() : "Makefile");
if(fi.isDir())
outdir = file.fileName() + '/';
}
if(!outdir.isEmpty() || file.fileName().isEmpty()) {
QString fname = "Makefile";
if(!project->isEmpty("MAKEFILE"))
fname = project->first("MAKEFILE").toQString();
file.setFileName(outdir + fname);
}
} }
if(QDir::isRelativePath(file.fileName())) {
QString fname = Option::output_dir; //pwd when qmake was run file.setFileName(QDir(Option::output_dir).absoluteFilePath(file.fileName()));
if(!fname.endsWith("/"))
fname += "/"; if (!build.isEmpty())
fname += file.fileName();
file.setFileName(fname);
}
if(!build.isEmpty())
file.setFileName(file.fileName() + "." + build); file.setFileName(file.fileName() + "." + build);
if(project->isEmpty("QMAKE_MAKEFILE"))
if (project->isEmpty("QMAKE_MAKEFILE"))
project->values("QMAKE_MAKEFILE").append(file.fileName()); project->values("QMAKE_MAKEFILE").append(file.fileName());
// Make required directories. Note that we do this based on the
// filename, not Option::output_dir, as the filename may include
// generator specific directories not included in output_dir.
int slsh = file.fileName().lastIndexOf('/'); int slsh = file.fileName().lastIndexOf('/');
if(slsh != -1) if (slsh != -1)
mkdir(file.fileName().left(slsh)); mkdir(file.fileName().left(slsh));
if(file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
QFileInfo fi(fileInfo(Option::output.fileName())); debug_msg(3, "opening output file %s", qPrintable(file.fileName()));
QString od; return file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate);
if(fi.isSymLink())
od = fileInfo(fi.readLink()).absolutePath();
else
od = fi.path();
od = QDir::fromNativeSeparators(od);
if(QDir::isRelativePath(od)) {
QString dir = Option::output_dir;
if (!dir.endsWith('/') && !od.isEmpty())
dir += '/';
od.prepend(dir);
}
Option::output_dir = od;
return true;
}
return false;
} }
QString QString

View File

@ -473,18 +473,11 @@ ProjectGenerator::getWritableVar(const char *vk, bool)
bool bool
ProjectGenerator::openOutput(QFile &file, const QString &build) const ProjectGenerator::openOutput(QFile &file, const QString &build) const
{ {
QString outdir; ProString fileName = file.fileName();
if(!file.fileName().isEmpty()) { if (!fileName.endsWith(Option::pro_ext)) {
QFileInfo fi(fileInfo(file.fileName())); if (fileName.isEmpty())
if(fi.isDir()) fileName = fileInfo(Option::output_dir).fileName();
outdir = fi.path() + QDir::separator(); file.setFileName(fileName + Option::pro_ext);
}
if(!outdir.isEmpty() || file.fileName().isEmpty()) {
QString dir = qmake_getpwd();
int s = dir.lastIndexOf('/');
if(s != -1)
dir = dir.right(dir.length() - (s + 1));
file.setFileName(outdir + dir + Option::pro_ext);
} }
return MakefileGenerator::openOutput(file, build); return MakefileGenerator::openOutput(file, build);
} }

View File

@ -1617,20 +1617,15 @@ QString VcprojGenerator::replaceExtraCompilerVariables(
bool VcprojGenerator::openOutput(QFile &file, const QString &/*build*/) const bool VcprojGenerator::openOutput(QFile &file, const QString &/*build*/) const
{ {
QString outdir; ProString fileName = file.fileName();
if(!file.fileName().isEmpty()) { ProString extension = project->first("TEMPLATE") == "vcsubdirs"
QFileInfo fi(fileInfo(file.fileName())); ? project->first("VCSOLUTION_EXTENSION") : project->first("VCPROJ_EXTENSION");
if(fi.isDir()) if (!fileName.endsWith(extension)) {
outdir = file.fileName() + QDir::separator(); if (fileName.isEmpty()) {
} fileName = !project->first("MAKEFILE").isEmpty()
if(!outdir.isEmpty() || file.fileName().isEmpty()) { ? project->first("MAKEFILE") : project->first("TARGET");
ProString ext = project->first("VCPROJ_EXTENSION"); }
if(project->first("TEMPLATE") == "vcsubdirs") file.setFileName(fileName + extension);
ext = project->first("VCSOLUTION_EXTENSION");
ProString outputName = project->first("TARGET");
if (!project->first("MAKEFILE").isEmpty())
outputName = project->first("MAKEFILE");
file.setFileName(outdir + outputName + ext);
} }
return Win32MakefileGenerator::openOutput(file, QString()); return Win32MakefileGenerator::openOutput(file, QString());
} }

View File

@ -455,28 +455,22 @@ int runQMake(int argc, char **argv)
QString oldpwd = qmake_getpwd(); QString oldpwd = qmake_getpwd();
Option::output_dir = oldpwd; //for now this is the output dir Option::output_dir = oldpwd; //for now this is the output dir
if(Option::output.fileName() != "-") { if (!Option::output.fileName().isEmpty() && Option::output.fileName() != "-") {
// The output 'filename', as given by the -o option, might include one
// or more directories, so we may need to rebase the output directory.
QFileInfo fi(Option::output); QFileInfo fi(Option::output);
QString dir;
if(fi.isDir()) { QDir dir(QDir::cleanPath(fi.isDir() ? fi.absoluteFilePath() : fi.absolutePath()));
dir = fi.filePath();
} else { // Don't treat Xcode project directory as part of OUT_PWD
QString tmp_dir = fi.path(); if (dir.dirName().endsWith(QLatin1String(".xcodeproj"))) {
if(!tmp_dir.isEmpty() && QFile::exists(tmp_dir)) // Note: we're intentionally not using cdUp(), as the dir may not exist
dir = tmp_dir; dir.setPath(QDir::cleanPath(dir.filePath("..")));
} }
#ifdef Q_OS_MAC
if (fi.fileName().endsWith(QLatin1String(".pbxproj")) Option::output_dir = dir.path();
&& dir.endsWith(QLatin1String(".xcodeproj"))) QString absoluteFilePath = QDir::cleanPath(fi.absoluteFilePath());
dir += QStringLiteral("/.."); Option::output.setFileName(absoluteFilePath.mid(Option::output_dir.length() + 1));
#endif
if(!dir.isNull() && dir != ".")
Option::output_dir = dir;
if (QDir::isRelativePath(Option::output_dir)) {
Option::output.setFileName(fi.fileName());
Option::output_dir.prepend(oldpwd + QLatin1Char('/'));
}
Option::output_dir = QDir::cleanPath(Option::output_dir);
} }
QMakeProperty prop; QMakeProperty prop;