c0791b86ab
this is a complete bastardization of the concept and breaks the (unused) debugging feature to create proper unix makefiles under windows, but apparently it permits cross-compiling unix targets under windows. Change-Id: I4ee95a20e7d6a6b2063ec7aa22f52c1a97d78a77 Reviewed-by: Oliver Wolff <oliver.wolff@nokia.com> Reviewed-by: Joerg Bornemann <joerg.bornemann@nokia.com>
556 lines
20 KiB
C++
556 lines
20 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
|
** Contact: http://www.qt-project.org/
|
|
**
|
|
** This file is part of the qmake application of the Qt Toolkit.
|
|
**
|
|
** $QT_BEGIN_LICENSE:LGPL$
|
|
** GNU Lesser General Public License Usage
|
|
** This file may be used under the terms of the GNU Lesser General Public
|
|
** License version 2.1 as published by the Free Software Foundation and
|
|
** appearing in the file LICENSE.LGPL included in the packaging of this
|
|
** file. Please review the following information to ensure the GNU Lesser
|
|
** General Public License version 2.1 requirements will be met:
|
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
**
|
|
** In addition, as a special exception, Nokia gives you certain additional
|
|
** rights. These rights are described in the Nokia Qt LGPL Exception
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
**
|
|
** GNU General Public License Usage
|
|
** Alternatively, this file may be used under the terms of the GNU General
|
|
** Public License version 3.0 as published by the Free Software Foundation
|
|
** and appearing in the file LICENSE.GPL included in the packaging of this
|
|
** file. Please review the following information to ensure the GNU General
|
|
** Public License version 3.0 requirements will be met:
|
|
** http://www.gnu.org/copyleft/gpl.html.
|
|
**
|
|
** Other Usage
|
|
** Alternatively, this file may be used in accordance with the terms and
|
|
** conditions contained in a signed written agreement between you and Nokia.
|
|
**
|
|
**
|
|
**
|
|
**
|
|
**
|
|
**
|
|
** $QT_END_LICENSE$
|
|
**
|
|
****************************************************************************/
|
|
|
|
#include "metamakefile.h"
|
|
#include "qregexp.h"
|
|
#include "qdir.h"
|
|
#include "qdebug.h"
|
|
#include "makefile.h"
|
|
#include "project.h"
|
|
#include "cachekeys.h"
|
|
|
|
#define BUILDSMETATYPE 1
|
|
#define SUBDIRSMETATYPE 2
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
MetaMakefileGenerator::~MetaMakefileGenerator()
|
|
{
|
|
if(own_project)
|
|
delete project;
|
|
}
|
|
|
|
#ifndef QT_QMAKE_PARSER_ONLY
|
|
|
|
class BuildsMetaMakefileGenerator : public MetaMakefileGenerator
|
|
{
|
|
private:
|
|
bool init_flag;
|
|
struct Build {
|
|
QString name, build;
|
|
MakefileGenerator *makefile;
|
|
};
|
|
QList<Build *> makefiles;
|
|
void clearBuilds();
|
|
MakefileGenerator *processBuild(const QString &);
|
|
|
|
public:
|
|
|
|
BuildsMetaMakefileGenerator(QMakeProject *p, const QString &n, bool op) : MetaMakefileGenerator(p, n, op), init_flag(false) { }
|
|
virtual ~BuildsMetaMakefileGenerator() { clearBuilds(); }
|
|
|
|
virtual bool init();
|
|
virtual int type() const { return BUILDSMETATYPE; }
|
|
virtual bool write(const QString &);
|
|
};
|
|
|
|
void
|
|
BuildsMetaMakefileGenerator::clearBuilds()
|
|
{
|
|
for(int i = 0; i < makefiles.count(); i++) {
|
|
Build *build = makefiles[i];
|
|
if(QMakeProject *p = build->makefile->projectFile()) {
|
|
if(p != project)
|
|
delete p;
|
|
}
|
|
delete build->makefile;
|
|
delete build;
|
|
}
|
|
makefiles.clear();
|
|
}
|
|
|
|
bool
|
|
BuildsMetaMakefileGenerator::init()
|
|
{
|
|
if(init_flag)
|
|
return false;
|
|
init_flag = true;
|
|
|
|
const QStringList &builds = project->variables()["BUILDS"];
|
|
bool use_single_build = builds.isEmpty();
|
|
if(builds.count() > 1 && Option::output.fileName() == "-") {
|
|
use_single_build = true;
|
|
warn_msg(WarnLogic, "Cannot direct to stdout when using multiple BUILDS.");
|
|
} else if(0 && !use_single_build && project->first("TEMPLATE") == "subdirs") {
|
|
use_single_build = true;
|
|
warn_msg(WarnLogic, "Cannot specify multiple builds with TEMPLATE subdirs.");
|
|
}
|
|
if(!use_single_build) {
|
|
for(int i = 0; i < builds.count(); i++) {
|
|
QString build = builds[i];
|
|
MakefileGenerator *makefile = processBuild(build);
|
|
if(!makefile)
|
|
return false;
|
|
if(!makefile->supportsMetaBuild()) {
|
|
warn_msg(WarnLogic, "QMAKESPEC does not support multiple BUILDS.");
|
|
clearBuilds();
|
|
use_single_build = true;
|
|
break;
|
|
} else {
|
|
Build *b = new Build;
|
|
b->name = name;
|
|
if(builds.count() != 1)
|
|
b->build += build;
|
|
b->makefile = makefile;
|
|
makefiles += b;
|
|
}
|
|
}
|
|
}
|
|
if(use_single_build) {
|
|
Build *build = new Build;
|
|
build->name = name;
|
|
build->makefile = createMakefileGenerator(project, false);
|
|
if (build->makefile){
|
|
makefiles += build;
|
|
}else {
|
|
delete build;
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
BuildsMetaMakefileGenerator::write(const QString &oldpwd)
|
|
{
|
|
Build *glue = 0;
|
|
if(!makefiles.isEmpty() && !makefiles.first()->build.isNull()) {
|
|
glue = new Build;
|
|
glue->name = name;
|
|
glue->makefile = createMakefileGenerator(project, true);
|
|
makefiles += glue;
|
|
}
|
|
|
|
bool ret = true;
|
|
const QString &output_name = Option::output.fileName();
|
|
for(int i = 0; ret && i < makefiles.count(); i++) {
|
|
Option::output.setFileName(output_name);
|
|
Build *build = makefiles[i];
|
|
|
|
bool using_stdout = false;
|
|
if(build->makefile && (Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE ||
|
|
Option::qmake_mode == Option::QMAKE_GENERATE_PROJECT)
|
|
&& (!build->makefile->supportsMergedBuilds()
|
|
|| (build->makefile->supportsMergedBuilds() && (!glue || build == glue)))) {
|
|
//open output
|
|
if(!(Option::output.isOpen())) {
|
|
if(Option::output.fileName() == "-") {
|
|
Option::output.setFileName("");
|
|
Option::output_dir = qmake_getpwd();
|
|
Option::output.open(stdout, QIODevice::WriteOnly | QIODevice::Text);
|
|
using_stdout = true;
|
|
} else {
|
|
if(Option::output.fileName().isEmpty() &&
|
|
Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE)
|
|
Option::output.setFileName(project->first("QMAKE_MAKEFILE"));
|
|
Option::output_dir = oldpwd;
|
|
QString build_name = build->name;
|
|
if(!build->build.isEmpty()) {
|
|
if(!build_name.isEmpty())
|
|
build_name += ".";
|
|
build_name += build->build;
|
|
}
|
|
if(!build->makefile->openOutput(Option::output, build_name)) {
|
|
fprintf(stderr, "Failure to open file: %s\n",
|
|
Option::output.fileName().isEmpty() ? "(stdout)" :
|
|
Option::output.fileName().toLatin1().constData());
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
using_stdout = true; //kind of..
|
|
}
|
|
|
|
if(!build->makefile) {
|
|
ret = false;
|
|
} else if(build == glue) {
|
|
ret = build->makefile->writeProjectMakefile();
|
|
} else {
|
|
ret = build->makefile->write();
|
|
if (glue && glue->makefile->supportsMergedBuilds())
|
|
ret = glue->makefile->mergeBuildProject(build->makefile);
|
|
}
|
|
if(!using_stdout) {
|
|
Option::output.close();
|
|
if(!ret)
|
|
Option::output.remove();
|
|
}
|
|
|
|
// debugging
|
|
if(Option::debug_level) {
|
|
debug_msg(1, "Dumping all variables:");
|
|
QHash<QString, QStringList> &vars = project->variables();
|
|
for(QHash<QString, QStringList>::Iterator it = vars.begin(); it != vars.end(); ++it) {
|
|
if(!it.key().startsWith(".") && !it.value().isEmpty())
|
|
debug_msg(1, "%s === %s", it.key().toLatin1().constData(),
|
|
it.value().join(" :: ").toLatin1().constData());
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
MakefileGenerator
|
|
*BuildsMetaMakefileGenerator::processBuild(const QString &build)
|
|
{
|
|
if(project) {
|
|
debug_msg(1, "Meta Generator: Parsing '%s' for build [%s].",
|
|
project->projectFile().toLatin1().constData(),build.toLatin1().constData());
|
|
|
|
//initialize the base
|
|
QHash<QString, QStringList> basevars;
|
|
QStringList basecfgs;
|
|
if(!project->isEmpty(build + ".CONFIG"))
|
|
basecfgs = project->values(build + ".CONFIG");
|
|
basecfgs += build;
|
|
basecfgs += "build_pass";
|
|
basevars["BUILD_PASS"] = QStringList(build);
|
|
QStringList buildname = project->values(build + ".name");
|
|
basevars["BUILD_NAME"] = (buildname.isEmpty() ? QStringList(build) : buildname);
|
|
|
|
//create project
|
|
QMakeProject *build_proj = new QMakeProject(project->properties());
|
|
build_proj->setExtraVars(basevars);
|
|
build_proj->setExtraConfigs(basecfgs);
|
|
|
|
build_proj->read(project->projectFile());
|
|
|
|
//done
|
|
return createMakefileGenerator(build_proj);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
class SubdirsMetaMakefileGenerator : public MetaMakefileGenerator
|
|
{
|
|
protected:
|
|
bool init_flag;
|
|
struct Subdir {
|
|
Subdir() : makefile(0), indent(0) { }
|
|
~Subdir() { delete makefile; }
|
|
QString input_dir;
|
|
QString output_dir, output_file;
|
|
MetaMakefileGenerator *makefile;
|
|
int indent;
|
|
};
|
|
QList<Subdir *> subs;
|
|
MakefileGenerator *processBuild(const QString &);
|
|
|
|
public:
|
|
SubdirsMetaMakefileGenerator(QMakeProject *p, const QString &n, bool op) : MetaMakefileGenerator(p, n, op), init_flag(false) { }
|
|
virtual ~SubdirsMetaMakefileGenerator();
|
|
|
|
virtual bool init();
|
|
virtual int type() const { return SUBDIRSMETATYPE; }
|
|
virtual bool write(const QString &);
|
|
};
|
|
|
|
bool
|
|
SubdirsMetaMakefileGenerator::init()
|
|
{
|
|
if(init_flag)
|
|
return false;
|
|
init_flag = true;
|
|
bool hasError = false;
|
|
|
|
// It might make sense to bequeath the CONFIG option to the recursed
|
|
// projects. OTOH, one would most likely have it in all projects anyway -
|
|
// either through a qmakespec, a .qmake.cache or explicitly - as otherwise
|
|
// running qmake in a subdirectory would have a different auto-recurse
|
|
// setting than in parent directories.
|
|
bool recurse = Option::recursive == Option::QMAKE_RECURSIVE_YES
|
|
|| (Option::recursive == Option::QMAKE_RECURSIVE_DEFAULT
|
|
&& project->isRecursive());
|
|
if(recurse) {
|
|
QString old_output_dir = Option::output_dir;
|
|
QString old_output = Option::output.fileName();
|
|
QString oldpwd = qmake_getpwd();
|
|
QString thispwd = oldpwd;
|
|
if(!thispwd.endsWith('/'))
|
|
thispwd += '/';
|
|
const QStringList &subdirs = project->values("SUBDIRS");
|
|
static int recurseDepth = -1;
|
|
++recurseDepth;
|
|
for(int i = 0; i < subdirs.size(); ++i) {
|
|
Subdir *sub = new Subdir;
|
|
sub->indent = recurseDepth;
|
|
|
|
QFileInfo subdir(subdirs.at(i));
|
|
if(!project->isEmpty(subdirs.at(i) + ".file"))
|
|
subdir = project->first(subdirs.at(i) + ".file");
|
|
else if(!project->isEmpty(subdirs.at(i) + ".subdir"))
|
|
subdir = project->first(subdirs.at(i) + ".subdir");
|
|
QString sub_name;
|
|
if(subdir.isDir())
|
|
subdir = QFileInfo(subdir.filePath() + "/" + subdir.fileName() + Option::pro_ext);
|
|
else
|
|
sub_name = subdir.baseName();
|
|
if(!subdir.isRelative()) { //we can try to make it relative
|
|
QString subdir_path = subdir.filePath();
|
|
if(subdir_path.startsWith(thispwd))
|
|
subdir = QFileInfo(subdir_path.mid(thispwd.length()));
|
|
}
|
|
|
|
//handle sub project
|
|
QMakeProject *sub_proj = new QMakeProject(project->properties());
|
|
for (int ind = 0; ind < sub->indent; ++ind)
|
|
printf(" ");
|
|
sub->input_dir = subdir.absolutePath();
|
|
if(subdir.isRelative() && old_output_dir != oldpwd) {
|
|
sub->output_dir = old_output_dir + "/" + subdir.path();
|
|
printf("Reading %s [%s]\n", subdir.absoluteFilePath().toLatin1().constData(), sub->output_dir.toLatin1().constData());
|
|
} else { //what about shadow builds?
|
|
sub->output_dir = sub->input_dir;
|
|
printf("Reading %s\n", subdir.absoluteFilePath().toLatin1().constData());
|
|
}
|
|
qmake_setpwd(sub->input_dir);
|
|
Option::output_dir = sub->output_dir;
|
|
bool tmpError = !sub_proj->read(subdir.fileName());
|
|
if(!sub_proj->variables()["QMAKE_FAILED_REQUIREMENTS"].isEmpty()) {
|
|
fprintf(stderr, "Project file(%s) not recursed because all requirements not met:\n\t%s\n",
|
|
subdir.fileName().toLatin1().constData(),
|
|
sub_proj->values("QMAKE_FAILED_REQUIREMENTS").join(" ").toLatin1().constData());
|
|
delete sub;
|
|
delete sub_proj;
|
|
Option::output_dir = old_output_dir;
|
|
qmake_setpwd(oldpwd);
|
|
continue;
|
|
} else {
|
|
hasError |= tmpError;
|
|
}
|
|
sub->makefile = MetaMakefileGenerator::createMetaGenerator(sub_proj, sub_name);
|
|
if(0 && sub->makefile->type() == SUBDIRSMETATYPE) {
|
|
subs.append(sub);
|
|
} else {
|
|
const QString output_name = Option::output.fileName();
|
|
Option::output.setFileName(sub->output_file);
|
|
hasError |= !sub->makefile->write(sub->output_dir);
|
|
delete sub;
|
|
qmakeClearCaches();
|
|
sub = 0;
|
|
Option::output.setFileName(output_name);
|
|
}
|
|
Option::output_dir = old_output_dir;
|
|
qmake_setpwd(oldpwd);
|
|
|
|
}
|
|
--recurseDepth;
|
|
Option::output.setFileName(old_output);
|
|
Option::output_dir = old_output_dir;
|
|
qmake_setpwd(oldpwd);
|
|
}
|
|
|
|
Subdir *self = new Subdir;
|
|
self->input_dir = qmake_getpwd();
|
|
self->output_dir = Option::output_dir;
|
|
if(!recurse || (!Option::output.fileName().endsWith(Option::dir_sep) && !QFileInfo(Option::output).isDir()))
|
|
self->output_file = Option::output.fileName();
|
|
self->makefile = new BuildsMetaMakefileGenerator(project, name, false);
|
|
self->makefile->init();
|
|
subs.append(self);
|
|
|
|
return !hasError;
|
|
}
|
|
|
|
bool
|
|
SubdirsMetaMakefileGenerator::write(const QString &oldpwd)
|
|
{
|
|
bool ret = true;
|
|
const QString &pwd = qmake_getpwd();
|
|
const QString &output_dir = Option::output_dir;
|
|
const QString &output_name = Option::output.fileName();
|
|
for(int i = 0; ret && i < subs.count(); i++) {
|
|
const Subdir *sub = subs.at(i);
|
|
qmake_setpwd(subs.at(i)->input_dir);
|
|
Option::output_dir = QFileInfo(subs.at(i)->output_dir).absoluteFilePath();
|
|
if(Option::output_dir.at(Option::output_dir.length()-1) != QLatin1Char('/'))
|
|
Option::output_dir += QLatin1Char('/');
|
|
Option::output.setFileName(subs.at(i)->output_file);
|
|
if(i != subs.count()-1) {
|
|
for (int ind = 0; ind < sub->indent; ++ind)
|
|
printf(" ");
|
|
printf("Writing %s\n", QDir::cleanPath(Option::output_dir+"/"+
|
|
Option::output.fileName()).toLatin1().constData());
|
|
}
|
|
QString writepwd = Option::fixPathToLocalOS(qmake_getpwd());
|
|
if(!writepwd.startsWith(Option::fixPathToLocalOS(oldpwd)))
|
|
writepwd = oldpwd;
|
|
if(!(ret = subs.at(i)->makefile->write(writepwd)))
|
|
break;
|
|
//restore because I'm paranoid
|
|
qmake_setpwd(pwd);
|
|
Option::output.setFileName(output_name);
|
|
Option::output_dir = output_dir;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
SubdirsMetaMakefileGenerator::~SubdirsMetaMakefileGenerator()
|
|
{
|
|
for(int i = 0; i < subs.count(); i++)
|
|
delete subs[i];
|
|
subs.clear();
|
|
}
|
|
|
|
//Factory things
|
|
QT_BEGIN_INCLUDE_NAMESPACE
|
|
#include "unixmake.h"
|
|
#include "mingw_make.h"
|
|
#include "projectgenerator.h"
|
|
#include "pbuilder_pbx.h"
|
|
#include "msvc_nmake.h"
|
|
#include "borland_bmake.h"
|
|
#include "msvc_vcproj.h"
|
|
#include "msvc_vcxproj.h"
|
|
#include "gbuild.h"
|
|
QT_END_INCLUDE_NAMESPACE
|
|
|
|
MakefileGenerator *
|
|
MetaMakefileGenerator::createMakefileGenerator(QMakeProject *proj, bool noIO)
|
|
{
|
|
Option::postProcessProject(proj);
|
|
|
|
MakefileGenerator *mkfile = NULL;
|
|
if(Option::qmake_mode == Option::QMAKE_GENERATE_PROJECT) {
|
|
mkfile = new ProjectGenerator;
|
|
mkfile->setProjectFile(proj);
|
|
return mkfile;
|
|
}
|
|
|
|
QString gen = proj->first("MAKEFILE_GENERATOR");
|
|
if(gen.isEmpty()) {
|
|
fprintf(stderr, "MAKEFILE_GENERATOR variable not set as a result of parsing : %s. Possibly qmake was not able to find files included using \"include(..)\" - enable qmake debugging to investigate more.\n",
|
|
proj->projectFile().toLatin1().constData());
|
|
} else if(gen == "UNIX") {
|
|
mkfile = new UnixMakefileGenerator;
|
|
} else if(gen == "MINGW") {
|
|
mkfile = new MingwMakefileGenerator;
|
|
} else if(gen == "PROJECTBUILDER" || gen == "XCODE") {
|
|
mkfile = new ProjectBuilderMakefileGenerator;
|
|
} else if(gen == "MSVC.NET") {
|
|
if (proj->first("TEMPLATE").startsWith("vc"))
|
|
mkfile = new VcprojGenerator;
|
|
else
|
|
mkfile = new NmakeMakefileGenerator;
|
|
} else if(gen == "MSBUILD") {
|
|
// Visual Studio >= v11.0
|
|
if (proj->first("TEMPLATE").startsWith("vc"))
|
|
mkfile = new VcxprojGenerator;
|
|
else
|
|
mkfile = new NmakeMakefileGenerator;
|
|
} else if(gen == "BMAKE") {
|
|
mkfile = new BorlandMakefileGenerator;
|
|
} else if(gen == "GBUILD") {
|
|
mkfile = new GBuildMakefileGenerator;
|
|
} else {
|
|
fprintf(stderr, "Unknown generator specified: %s\n", gen.toLatin1().constData());
|
|
}
|
|
if (mkfile) {
|
|
mkfile->setNoIO(noIO);
|
|
mkfile->setProjectFile(proj);
|
|
}
|
|
return mkfile;
|
|
}
|
|
|
|
MetaMakefileGenerator *
|
|
MetaMakefileGenerator::createMetaGenerator(QMakeProject *proj, const QString &name, bool op, bool *success)
|
|
{
|
|
Option::postProcessProject(proj);
|
|
|
|
MetaMakefileGenerator *ret = 0;
|
|
if ((Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE ||
|
|
Option::qmake_mode == Option::QMAKE_GENERATE_PRL)) {
|
|
if (proj->first("TEMPLATE").endsWith("subdirs"))
|
|
ret = new SubdirsMetaMakefileGenerator(proj, name, op);
|
|
}
|
|
if (!ret)
|
|
ret = new BuildsMetaMakefileGenerator(proj, name, op);
|
|
bool res = ret->init();
|
|
if (success)
|
|
*success = res;
|
|
return ret;
|
|
}
|
|
|
|
#endif // QT_QMAKE_PARSER_ONLY
|
|
|
|
bool
|
|
MetaMakefileGenerator::modesForGenerator(const QString &gen,
|
|
Option::HOST_MODE *host_mode, Option::TARG_MODE *target_mode)
|
|
{
|
|
if (gen == "UNIX") {
|
|
#ifdef Q_OS_MAC
|
|
*host_mode = Option::HOST_MACX_MODE;
|
|
*target_mode = Option::TARG_MACX_MODE;
|
|
#elif defined(Q_OS_WIN)
|
|
*host_mode = Option::HOST_WIN_MODE;
|
|
*target_mode = Option::TARG_UNIX_MODE;
|
|
#else
|
|
*host_mode = Option::HOST_UNIX_MODE;
|
|
*target_mode = Option::TARG_UNIX_MODE;
|
|
#endif
|
|
} else if (gen == "MSVC.NET" || gen == "BMAKE" || gen == "MSBUILD") {
|
|
*host_mode = Option::HOST_WIN_MODE;
|
|
*target_mode = Option::TARG_WIN_MODE;
|
|
} else if (gen == "MINGW") {
|
|
#if defined(Q_OS_MAC)
|
|
*host_mode = Option::HOST_MACX_MODE;
|
|
#elif defined(Q_OS_UNIX)
|
|
*host_mode = Option::HOST_UNIX_MODE;
|
|
#else
|
|
*host_mode = Option::HOST_WIN_MODE;
|
|
#endif
|
|
*target_mode = Option::TARG_WIN_MODE;
|
|
} else if (gen == "PROJECTBUILDER" || gen == "XCODE") {
|
|
*host_mode = Option::HOST_MACX_MODE;
|
|
*target_mode = Option::TARG_MACX_MODE;
|
|
} else if (gen == "GBUILD") {
|
|
*host_mode = Option::HOST_UNIX_MODE;
|
|
*target_mode = Option::TARG_INTEGRITY_MODE;
|
|
} else {
|
|
fprintf(stderr, "Unknown generator specified: %s\n", gen.toLatin1().constData());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
QT_END_NAMESPACE
|