summaryrefslogtreecommitdiffstats
path: root/qmake/project.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'qmake/project.cpp')
-rw-r--r--qmake/project.cpp1020
1 files changed, 907 insertions, 113 deletions
diff --git a/qmake/project.cpp b/qmake/project.cpp
index 3df80bc00d..3a34aef1a9 100644
--- a/qmake/project.cpp
+++ b/qmake/project.cpp
@@ -54,7 +54,11 @@
#include <qstack.h>
#include <qdebug.h>
#ifdef Q_OS_UNIX
+#include <time.h>
+#include <utime.h>
+#include <errno.h>
#include <unistd.h>
+#include <sys/stat.h>
#include <sys/utsname.h>
#elif defined(Q_OS_WIN32)
#include <windows.h>
@@ -74,10 +78,12 @@ QT_BEGIN_NAMESPACE
//expand functions
enum ExpandFunc { E_MEMBER=1, E_FIRST, E_LAST, E_CAT, E_FROMFILE, E_EVAL, E_LIST,
- E_SPRINTF, E_JOIN, E_SPLIT, E_BASENAME, E_DIRNAME, E_SECTION,
- E_FIND, E_SYSTEM, E_UNIQUE, E_QUOTE, E_ESCAPE_EXPAND,
- E_UPPER, E_LOWER, E_FILES, E_PROMPT, E_RE_ESCAPE, E_REPLACE,
- E_SIZE, E_SORT_DEPENDS, E_RESOLVE_DEPENDS };
+ E_SPRINTF, E_FORMAT_NUMBER, E_JOIN, E_SPLIT, E_BASENAME, E_DIRNAME, E_SECTION,
+ 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 };
QHash<QString, ExpandFunc> qmake_expandFunctions()
{
static QHash<QString, ExpandFunc> *qmake_expand_functions = 0;
@@ -92,6 +98,7 @@ QHash<QString, ExpandFunc> qmake_expandFunctions()
qmake_expand_functions->insert("eval", E_EVAL);
qmake_expand_functions->insert("list", E_LIST);
qmake_expand_functions->insert("sprintf", E_SPRINTF);
+ qmake_expand_functions->insert("format_number", E_FORMAT_NUMBER);
qmake_expand_functions->insert("join", E_JOIN);
qmake_expand_functions->insert("split", E_SPLIT);
qmake_expand_functions->insert("basename", E_BASENAME);
@@ -100,17 +107,26 @@ QHash<QString, ExpandFunc> qmake_expandFunctions()
qmake_expand_functions->insert("find", E_FIND);
qmake_expand_functions->insert("system", E_SYSTEM);
qmake_expand_functions->insert("unique", E_UNIQUE);
+ qmake_expand_functions->insert("reverse", E_REVERSE);
qmake_expand_functions->insert("quote", E_QUOTE);
qmake_expand_functions->insert("escape_expand", E_ESCAPE_EXPAND);
qmake_expand_functions->insert("upper", E_UPPER);
qmake_expand_functions->insert("lower", E_LOWER);
qmake_expand_functions->insert("re_escape", E_RE_ESCAPE);
+ qmake_expand_functions->insert("val_escape", E_VAL_ESCAPE);
qmake_expand_functions->insert("files", E_FILES);
qmake_expand_functions->insert("prompt", E_PROMPT);
qmake_expand_functions->insert("replace", E_REPLACE);
qmake_expand_functions->insert("size", E_SIZE);
qmake_expand_functions->insert("sort_depends", E_SORT_DEPENDS);
qmake_expand_functions->insert("resolve_depends", E_RESOLVE_DEPENDS);
+ qmake_expand_functions->insert("enumerate_vars", E_ENUMERATE_VARS);
+ qmake_expand_functions->insert("shadowed", E_SHADOWED);
+ 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("shell_quote", E_SHELL_QUOTE);
}
return *qmake_expand_functions;
}
@@ -118,8 +134,9 @@ QHash<QString, ExpandFunc> qmake_expandFunctions()
enum TestFunc { T_REQUIRES=1, T_GREATERTHAN, T_LESSTHAN, T_EQUALS,
T_EXISTS, T_EXPORT, T_CLEAR, T_UNSET, T_EVAL, T_CONFIG, T_SYSTEM,
T_RETURN, T_BREAK, T_NEXT, T_DEFINED, T_CONTAINS, T_INFILE,
- T_COUNT, T_ISEMPTY, T_INCLUDE, T_LOAD, T_DEBUG, T_ERROR,
- T_MESSAGE, T_WARNING, T_IF, T_OPTION };
+ T_COUNT, T_ISEMPTY, T_INCLUDE, T_LOAD,
+ T_DEBUG, T_ERROR, T_MESSAGE, T_WARNING, T_LOG,
+ T_IF, T_OPTION, T_CACHE, T_MKPATH, T_WRITE_FILE, T_TOUCH };
QHash<QString, TestFunc> qmake_testFunctions()
{
static QHash<QString, TestFunc> *qmake_test_functions = 0;
@@ -153,7 +170,12 @@ QHash<QString, TestFunc> qmake_testFunctions()
qmake_test_functions->insert("error", T_ERROR);
qmake_test_functions->insert("message", T_MESSAGE);
qmake_test_functions->insert("warning", T_WARNING);
+ qmake_test_functions->insert("log", T_LOG);
qmake_test_functions->insert("option", T_OPTION);
+ qmake_test_functions->insert("cache", T_CACHE);
+ qmake_test_functions->insert("mkpath", T_MKPATH);
+ qmake_test_functions->insert("write_file", T_WRITE_FILE);
+ qmake_test_functions->insert("touch", T_TOUCH);
}
return *qmake_test_functions;
}
@@ -164,6 +186,21 @@ struct parser_info {
bool from_file;
} parser;
+static QString cached_source_root;
+static QString cached_build_root;
+static QStringList cached_qmakepath;
+static QStringList cached_qmakefeatures;
+
+static QStringList *all_feature_roots[2] = { 0, 0 };
+
+static void
+invalidateFeatureRoots()
+{
+ for (int i = 0; i < 2; i++)
+ if (all_feature_roots[i])
+ all_feature_roots[i]->clear();
+}
+
static QString remove_quotes(const QString &arg)
{
const ushort SINGLEQUOTE = '\'';
@@ -528,7 +565,7 @@ static void qmake_error_msg(const QString &msg)
1) features/(unix|win32|macx)/
2) features/
*/
-QStringList qmake_feature_paths(QMakeProperty *prop=0)
+QStringList qmake_feature_paths(QMakeProperty *prop, bool host_build)
{
const QString mkspecs_concat = QLatin1String("/mkspecs");
const QString base_concat = QLatin1String("/features");
@@ -551,36 +588,26 @@ QStringList qmake_feature_paths(QMakeProperty *prop=0)
concat << base_concat;
}
- QStringList feature_roots;
- QByteArray mkspec_path = qgetenv("QMAKEFEATURES");
- if(!mkspec_path.isNull())
- feature_roots += splitPathList(QString::fromLocal8Bit(mkspec_path));
+ QStringList feature_roots = splitPathList(QString::fromLocal8Bit(qgetenv("QMAKEFEATURES")));
+ feature_roots += cached_qmakefeatures;
if(prop)
feature_roots += splitPathList(prop->value("QMAKEFEATURES"));
- if(!Option::mkfile::cachefile.isEmpty()) {
- QString path;
- int last_slash = Option::mkfile::cachefile.lastIndexOf(QLatin1Char('/'));
- if(last_slash != -1)
- path = Option::normalizePath(Option::mkfile::cachefile.left(last_slash), false);
+ if (!cached_build_root.isEmpty())
for(QStringList::Iterator concat_it = concat.begin();
concat_it != concat.end(); ++concat_it)
- feature_roots << (path + (*concat_it));
- }
- QByteArray qmakepath = qgetenv("QMAKEPATH");
- if (!qmakepath.isNull()) {
- const QStringList lst = splitPathList(QString::fromLocal8Bit(qmakepath));
- for(QStringList::ConstIterator it = lst.begin(); it != lst.end(); ++it) {
- for(QStringList::Iterator concat_it = concat.begin();
- concat_it != concat.end(); ++concat_it)
- feature_roots << ((*it) + mkspecs_concat + (*concat_it));
- }
- }
- if(!Option::mkfile::qmakespec.isEmpty()) {
+ feature_roots << (cached_build_root + (*concat_it));
+ QStringList qmakepath = splitPathList(QString::fromLocal8Bit(qgetenv("QMAKEPATH")));
+ qmakepath += cached_qmakepath;
+ foreach (const QString &path, qmakepath)
+ foreach (const QString &cat, concat)
+ feature_roots << (path + mkspecs_concat + cat);
+ QString *specp = host_build ? &Option::mkfile::qmakespec : &Option::mkfile::xqmakespec;
+ if (!specp->isEmpty()) {
// The spec is already platform-dependent, so no subdirs here.
- feature_roots << Option::mkfile::qmakespec + base_concat;
+ feature_roots << *specp + base_concat;
// Also check directly under the root directory of the mkspecs collection
- QFileInfo specfi(Option::mkfile::qmakespec);
+ QFileInfo specfi(*specp);
QDir specrootdir(specfi.absolutePath());
while (!specrootdir.isRoot()) {
const QString specrootpath = specrootdir.path();
@@ -596,26 +623,37 @@ QStringList qmake_feature_paths(QMakeProperty *prop=0)
}
for(QStringList::Iterator concat_it = concat.begin();
concat_it != concat.end(); ++concat_it)
- feature_roots << (QLibraryInfo::location(QLibraryInfo::HostDataPath) +
+ feature_roots << (QLibraryInfo::rawLocation(QLibraryInfo::HostDataPath,
+ QLibraryInfo::EffectivePaths) +
mkspecs_concat + (*concat_it));
feature_roots.removeDuplicates();
return feature_roots;
}
+QStringList qmake_mkspec_paths()
+{
+ QStringList ret;
+ const QString concat = QLatin1String("/mkspecs");
+
+ QStringList qmakepath = splitPathList(QString::fromLocal8Bit(qgetenv("QMAKEPATH")));
+ qmakepath += cached_qmakepath;
+ foreach (const QString &path, qmakepath)
+ ret << (path + concat);
+ if (!cached_build_root.isEmpty())
+ ret << cached_build_root + concat;
+ if (!cached_source_root.isEmpty())
+ ret << cached_source_root + concat;
+ ret << QLibraryInfo::rawLocation(QLibraryInfo::HostDataPath, QLibraryInfo::EffectivePaths) + concat;
+ ret.removeDuplicates();
+
+ return ret;
+}
+
QMakeProject::~QMakeProject()
{
if(own_prop)
delete prop;
- for(QHash<QString, FunctionBlock*>::iterator it = replaceFunctions.begin(); it != replaceFunctions.end(); ++it) {
- if(!it.value()->deref())
- delete it.value();
- }
- replaceFunctions.clear();
- for(QHash<QString, FunctionBlock*>::iterator it = testFunctions.begin(); it != testFunctions.end(); ++it) {
- if(!it.value()->deref())
- delete it.value();
- }
- testFunctions.clear();
+ cleanup();
}
@@ -630,14 +668,29 @@ QMakeProject::init(QMakeProperty *p)
own_prop = false;
}
recursive = false;
+ host_build = false;
reset();
}
+void
+QMakeProject::cleanup()
+{
+ for (QHash<QString, FunctionBlock*>::iterator it = replaceFunctions.begin(); it != replaceFunctions.end(); ++it)
+ if (!it.value()->deref())
+ delete it.value();
+ replaceFunctions.clear();
+ for (QHash<QString, FunctionBlock*>::iterator it = testFunctions.begin(); it != testFunctions.end(); ++it)
+ if (!it.value()->deref())
+ delete it.value();
+ testFunctions.clear();
+}
+
// Duplicate project. It is *not* allowed to call the complex read() functions on the copy.
QMakeProject::QMakeProject(QMakeProject *p, const QHash<QString, QStringList> *_vars)
{
init(p->properties());
vars = _vars ? *_vars : p->variables();
+ host_build = p->host_build;
for(QHash<QString, FunctionBlock*>::iterator it = p->replaceFunctions.begin(); it != p->replaceFunctions.end(); ++it) {
it.value()->ref();
replaceFunctions.insert(it.key(), it.value());
@@ -657,6 +710,7 @@ QMakeProject::reset()
iterator = 0;
function = 0;
backslashWarned = false;
+ need_restart = false;
}
bool
@@ -1164,8 +1218,6 @@ QMakeProject::parse(const QString &t, QHash<QString, QStringList> &place, int nu
}
if(var == "REQUIRES") // special case to get communicated to backends!
doProjectCheckReqs(vallist, place);
- else if (var == "_QMAKE_CACHE_")
- Option::mkfile::cachefile = varlist.isEmpty() ? QString() : varlist.at(0);
}
return true;
}
@@ -1205,6 +1257,8 @@ QMakeProject::read(QTextStream &file, QHash<QString, QStringList> &place)
}
s = "";
numLines = 0;
+ if (need_restart)
+ break;
}
}
}
@@ -1250,7 +1304,7 @@ QMakeProject::read(const QString &file, QHash<QString, QStringList> &place)
if(!using_stdin)
qfile.close();
}
- if(scope_blocks.count() != 1) {
+ if (!need_restart && scope_blocks.count() != 1) {
qmake_error_msg("Unterminated conditional block at end of file");
ret = false;
}
@@ -1269,6 +1323,7 @@ QMakeProject::read(const QString &project, uchar cmd)
bool
QMakeProject::read(uchar cmd)
{
+ again:
if ((cmd & ReadSetup) && base_vars.isEmpty()) {
// hack to get the Option stuff in there
base_vars["QMAKE_EXT_CPP"] = Option::cpp_ext;
@@ -1278,13 +1333,147 @@ QMakeProject::read(uchar cmd)
if(!Option::user_template_prefix.isEmpty())
base_vars["TEMPLATE_PREFIX"] = QStringList(Option::user_template_prefix);
+ QString superdir;
+ QString project_root;
+ QString project_build_root;
+ QStringList qmakepath;
+ QStringList qmakefeatures;
if (Option::mkfile::do_cache) { // parse the cache
- if (Option::output_dir.startsWith(Option::mkfile::project_build_root))
+ QHash<QString, QStringList> cache;
+ QString rdir = Option::output_dir;
+ forever {
+ QFileInfo qfi(rdir, QLatin1String(".qmake.super"));
+ if (qfi.exists()) {
+ superfile = qfi.filePath();
+ if (!read(superfile, cache))
+ return false;
+ superdir = rdir;
+ break;
+ }
+ QFileInfo qdfi(rdir);
+ if (qdfi.isRoot())
+ break;
+ rdir = qdfi.path();
+ }
+ if (Option::mkfile::cachefile.isEmpty()) { //find it as it has not been specified
+ QString sdir = qmake_getpwd();
+ QString dir = Option::output_dir;
+ forever {
+ QFileInfo qsfi(sdir, QLatin1String(".qmake.conf"));
+ if (qsfi.exists()) {
+ conffile = qsfi.filePath();
+ if (!read(conffile, cache))
+ return false;
+ }
+ QFileInfo qfi(dir, QLatin1String(".qmake.cache"));
+ if (qfi.exists()) {
+ cachefile = qfi.filePath();
+ if (!read(cachefile, cache))
+ return false;
+ }
+ if (!conffile.isEmpty() || !cachefile.isEmpty()) {
+ project_root = sdir;
+ project_build_root = dir;
+ break;
+ }
+ if (dir == superdir)
+ goto no_cache;
+ QFileInfo qsdfi(sdir);
+ QFileInfo qdfi(dir);
+ if (qsdfi.isRoot() || qdfi.isRoot())
+ goto no_cache;
+ sdir = qsdfi.path();
+ dir = qdfi.path();
+ }
+ } else {
+ QFileInfo fi(Option::mkfile::cachefile);
+ cachefile = QDir::cleanPath(fi.absoluteFilePath());
+ if (!read(cachefile, cache))
+ return false;
+ project_build_root = QDir::cleanPath(fi.absolutePath());
+ // This intentionally bypasses finding a source root,
+ // as the result would be more or less arbitrary.
+ }
+
+ if (Option::mkfile::xqmakespec.isEmpty() && !cache["XQMAKESPEC"].isEmpty())
+ Option::mkfile::xqmakespec = cache["XQMAKESPEC"].first();
+ if (Option::mkfile::qmakespec.isEmpty() && !cache["QMAKESPEC"].isEmpty()) {
+ Option::mkfile::qmakespec = cache["QMAKESPEC"].first();
+ if (Option::mkfile::xqmakespec.isEmpty())
+ Option::mkfile::xqmakespec = Option::mkfile::qmakespec;
+ }
+ qmakepath = cache.value(QLatin1String("QMAKEPATH"));
+ qmakefeatures = cache.value(QLatin1String("QMAKEFEATURES"));
+
+ if (Option::output_dir.startsWith(project_build_root))
Option::mkfile::cachefile_depth =
- Option::output_dir.mid(Option::mkfile::project_build_root.length()).count('/');
+ Option::output_dir.mid(project_build_root.length()).count('/');
+ }
+ no_cache:
+
+ // Look for mkspecs/ in source and build. First to win determines the root.
+ QString sdir = qmake_getpwd();
+ QString dir = Option::output_dir;
+ while (dir != project_build_root) {
+ if ((dir != sdir && QFileInfo(sdir, QLatin1String("mkspecs")).isDir())
+ || QFileInfo(dir, QLatin1String("mkspecs")).isDir()) {
+ if (dir != sdir)
+ project_root = sdir;
+ project_build_root = dir;
+ break;
+ }
+ if (dir == superdir)
+ break;
+ QFileInfo qsdfi(sdir);
+ QFileInfo qdfi(dir);
+ if (qsdfi.isRoot() || qdfi.isRoot())
+ break;
+ sdir = qsdfi.path();
+ dir = qdfi.path();
+ }
+
+ if (qmakepath != cached_qmakepath || qmakefeatures != cached_qmakefeatures
+ || project_build_root != cached_build_root) { // No need to check source dir, as it goes in sync
+ cached_source_root = project_root;
+ cached_build_root = project_build_root;
+ cached_qmakepath = qmakepath;
+ cached_qmakefeatures = qmakefeatures;
+ invalidateFeatureRoots();
}
+
{ // parse mkspec
- QString qmakespec = Option::mkfile::qmakespec;
+ QString *specp = host_build ? &Option::mkfile::qmakespec : &Option::mkfile::xqmakespec;
+ QString qmakespec = *specp;
+ if (qmakespec.isEmpty())
+ qmakespec = host_build ? "default-host" : "default";
+ if (QDir::isRelativePath(qmakespec)) {
+ QStringList mkspec_roots = qmake_mkspec_paths();
+ debug_msg(2, "Looking for mkspec %s in (%s)", qmakespec.toLatin1().constData(),
+ mkspec_roots.join("::").toLatin1().constData());
+ bool found_mkspec = false;
+ for (QStringList::ConstIterator it = mkspec_roots.begin(); it != mkspec_roots.end(); ++it) {
+ QString mkspec = (*it) + QLatin1Char('/') + qmakespec;
+ if (QFile::exists(mkspec)) {
+ found_mkspec = true;
+ *specp = qmakespec = mkspec;
+ break;
+ }
+ }
+ if (!found_mkspec) {
+ fprintf(stderr, "Could not find mkspecs for your QMAKESPEC(%s) after trying:\n\t%s\n",
+ qmakespec.toLatin1().constData(), mkspec_roots.join("\n\t").toLatin1().constData());
+ return false;
+ }
+ }
+
+ // We do this before reading the spec, so it can use module and feature paths from
+ // here without resorting to tricks. This is the only planned use case anyway.
+ if (!superfile.isEmpty()) {
+ debug_msg(1, "Project super cache file: reading %s", superfile.toLatin1().constData());
+ read(superfile, base_vars);
+ }
+
+ // parse qmake configuration
while(qmakespec.endsWith(QLatin1Char('/')))
qmakespec.truncate(qmakespec.length()-1);
QString spec = qmakespec + QLatin1String("/qmake.conf");
@@ -1295,9 +1484,13 @@ QMakeProject::read(uchar cmd)
}
validateModes();
- if(Option::mkfile::do_cache && !Option::mkfile::cachefile.isEmpty()) {
- debug_msg(1, "QMAKECACHE file: reading %s", Option::mkfile::cachefile.toLatin1().constData());
- read(Option::mkfile::cachefile, base_vars);
+ if (!conffile.isEmpty()) {
+ debug_msg(1, "Project config file: reading %s", conffile.toLatin1().constData());
+ read(conffile, base_vars);
+ }
+ if (!cachefile.isEmpty()) {
+ debug_msg(1, "QMAKECACHE file: reading %s", cachefile.toLatin1().constData());
+ read(cachefile, base_vars);
}
}
}
@@ -1347,6 +1540,11 @@ QMakeProject::read(uchar cmd)
pfile += Option::pro_ext;
if(!read(pfile, vars))
return false;
+ if (need_restart) {
+ base_vars.clear();
+ cleanup();
+ goto again;
+ }
}
if (cmd & ReadSetup) {
@@ -1443,7 +1641,7 @@ QMakeProject::resolveSpec(QString *spec, const QString &qmakespec)
{
if (spec->isEmpty()) {
*spec = QFileInfo(qmakespec).fileName();
- if (*spec == "default") {
+ if (*spec == "default" || *spec == "default-host") {
#ifdef Q_OS_UNIX
char buffer[1024];
int l = readlink(qmakespec.toLatin1().constData(), buffer, 1023);
@@ -1492,9 +1690,14 @@ QMakeProject::isActiveConfig(const QString &x, bool regex, QHash<QString, QStrin
return Option::target_mode == Option::TARG_WIN_MODE;
}
+ if (x == "host_build")
+ return host_build ? "true" : "false";
+
//mkspecs
- static QString spec;
- resolveSpec(&spec, Option::mkfile::qmakespec);
+ static QString hspec, xspec;
+ resolveSpec(&hspec, Option::mkfile::qmakespec);
+ resolveSpec(&xspec, Option::mkfile::xqmakespec);
+ const QString &spec = host_build ? hspec : xspec;
QRegExp re(x, Qt::CaseSensitive, QRegExp::Wildcard);
if((regex && re.exactMatch(spec)) || (!regex && spec == x))
return true;
@@ -1546,17 +1749,18 @@ QMakeProject::doProjectTest(QString func, const QString &params,
QMakeProject::IncludeStatus
QMakeProject::doProjectInclude(QString file, uchar flags, QHash<QString, QStringList> &place)
{
- enum { UnknownFormat, ProFormat, JSFormat } format = UnknownFormat;
if(flags & IncludeFlagFeature) {
if(!file.endsWith(Option::prf_ext))
file += Option::prf_ext;
validateModes(); // init dir_sep
if(file.indexOf(QLatin1Char('/')) == -1 || !QFile::exists(file)) {
- static QStringList *feature_roots = 0;
+ QStringList *&feature_roots = all_feature_roots[host_build];
if(!feature_roots) {
- feature_roots = new QStringList(qmake_feature_paths(prop));
+ feature_roots = new QStringList;
qmakeAddCacheClear(qmakeDeleteCacheClear<QStringList>, (void**)&feature_roots);
}
+ if (feature_roots->isEmpty())
+ *feature_roots = qmake_feature_paths(prop, host_build);
debug_msg(2, "Looking for feature '%s' in (%s)", file.toLatin1().constData(),
feature_roots->join("::").toLatin1().constData());
int start_root = 0;
@@ -1576,18 +1780,13 @@ QMakeProject::doProjectInclude(QString file, uchar flags, QHash<QString, QString
}
for(int root = start_root; root < feature_roots->size(); ++root) {
QString prf(feature_roots->at(root) + QLatin1Char('/') + file);
- if(QFile::exists(prf + Option::js_ext)) {
- format = JSFormat;
- file = prf + Option::js_ext;
- break;
- } else if(QFile::exists(prf)) {
- format = ProFormat;
+ if (QFile::exists(prf)) {
file = prf;
- break;
+ goto foundf;
}
}
- if(format == UnknownFormat)
- return IncludeNoExist;
+ return IncludeNoExist;
+ foundf: ;
}
if(place["QMAKE_INTERNAL_INCLUDED_FEATURES"].indexOf(file) != -1)
return IncludeFeatureAlreadyLoaded;
@@ -1605,19 +1804,13 @@ QMakeProject::doProjectInclude(QString file, uchar flags, QHash<QString, QString
testName += file;
if(QFile::exists(testName)) {
file = testName;
- break;
+ goto foundi;
}
}
- }
- if(format == UnknownFormat) {
- if(QFile::exists(file)) {
- if(file.endsWith(Option::js_ext))
- format = JSFormat;
- else
- format = ProFormat;
- } else {
- return IncludeNoExist;
- }
+ return IncludeNoExist;
+ foundi: ;
+ } else if (!QFile::exists(file)) {
+ return IncludeNoExist;
}
if(Option::mkfile::do_preprocess) //nice to see this first..
fprintf(stderr, "#switching file %s(%s) - %s:%d\n", (flags & IncludeFlagFeature) ? "load" : "include",
@@ -1637,10 +1830,7 @@ QMakeProject::doProjectInclude(QString file, uchar flags, QHash<QString, QString
}
bool parsed = false;
parser_info pi = parser;
- if(format == JSFormat) {
- warn_msg(WarnParser, "%s:%d: QtScript support disabled for %s.",
- pi.file.toLatin1().constData(), pi.line_no, orig_file.toLatin1().constData());
- } else {
+ {
if(flags & (IncludeFlagNewProject|IncludeFlagNewParser)) {
// The "project's variables" are used in other places (eg. export()) so it's not
// possible to use "place" everywhere. Instead just set variables and grab them later
@@ -1675,6 +1865,199 @@ QMakeProject::doProjectInclude(QString file, uchar flags, QHash<QString, QString
return IncludeSuccess;
}
+static void
+subAll(QStringList *val, const QStringList &diffval)
+{
+ foreach (const QString &dv, diffval)
+ val->removeAll(dv);
+}
+
+inline static
+bool isSpecialChar(ushort c)
+{
+ // Chars that should be quoted (TM). This includes:
+#ifdef Q_OS_WIN
+ // - control chars & space
+ // - the shell meta chars "&()<>^|
+ // - the potential separators ,;=
+ static const uchar iqm[] = {
+ 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
+ // 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".
+ ret.replace(QRegExp(QLatin1String("(\\\\*)\"")), QLatin1String("\"\\1\\1\\^\"\""));
+ // The argument must not end with a \ since this would be interpreted
+ // as escaping the quote -- rather put the \ behind the quote: e.g.
+ // rather use "foo"\ than "foo\"
+ int i = ret.length();
+ while (i > 0 && ret.at(i - 1) == QLatin1Char('\\'))
+ --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;
+}
+
+static QString
+quoteValue(const QString &val)
+{
+ QString ret;
+ ret.reserve(val.length());
+ bool quote = val.isEmpty();
+ bool escaping = false;
+ for (int i = 0, l = val.length(); i < l; i++) {
+ QChar c = val.unicode()[i];
+ ushort uc = c.unicode();
+ if (uc < 32) {
+ if (!escaping) {
+ escaping = true;
+ ret += QLatin1String("$$escape_expand(");
+ }
+ switch (uc) {
+ case '\r':
+ ret += QLatin1String("\\\\r");
+ break;
+ case '\n':
+ ret += QLatin1String("\\\\n");
+ break;
+ case '\t':
+ ret += QLatin1String("\\\\t");
+ break;
+ default:
+ ret += QString::fromLatin1("\\\\x%1").arg(uc, 2, 16, QLatin1Char('0'));
+ break;
+ }
+ } else {
+ if (escaping) {
+ escaping = false;
+ ret += QLatin1Char(')');
+ }
+ switch (uc) {
+ case '\\':
+ ret += QLatin1String("\\\\");
+ break;
+ case '"':
+ ret += QLatin1String("\\\"");
+ break;
+ case '\'':
+ ret += QLatin1String("\\'");
+ break;
+ case '$':
+ ret += QLatin1String("\\$");
+ break;
+ case '#':
+ ret += QLatin1String("$${LITERAL_HASH}");
+ break;
+ case 32:
+ quote = true;
+ // fallthrough
+ default:
+ ret += c;
+ break;
+ }
+ }
+ }
+ if (escaping)
+ ret += QLatin1Char(')');
+ if (quote) {
+ ret.prepend(QLatin1Char('"'));
+ ret.append(QLatin1Char('"'));
+ }
+ return ret;
+}
+
+static bool
+writeFile(const QString &name, QIODevice::OpenMode mode, const QString &contents, QString *errStr)
+{
+ QByteArray bytes = contents.toLocal8Bit();
+ QFile cfile(name);
+ if (!(mode & QIODevice::Append) && cfile.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ if (cfile.readAll() == bytes)
+ return true;
+ cfile.close();
+ }
+ if (!cfile.open(mode | QIODevice::WriteOnly | QIODevice::Text)) {
+ *errStr = cfile.errorString();
+ return false;
+ }
+ cfile.write(bytes);
+ cfile.close();
+ if (cfile.error() != QFile::NoError) {
+ *errStr = cfile.errorString();
+ return false;
+ }
+ return true;
+}
+
+static QByteArray
+getCommandOutput(const QString &args)
+{
+ QByteArray out;
+ if (FILE *proc = QT_POPEN(args.toLatin1().constData(), "r")) {
+ while (!feof(proc)) {
+ char buff[10 * 1024];
+ int read_in = int(fread(buff, 1, sizeof(buff), proc));
+ if (!read_in)
+ break;
+ out += QByteArray(buff, read_in);
+ }
+ QT_PCLOSE(proc);
+ }
+ return out;
+}
+
+#ifdef Q_OS_WIN
+static QString windowsErrorCode()
+{
+ wchar_t *string = 0;
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPWSTR)&string,
+ 0,
+ NULL);
+ QString ret = QString::fromWCharArray(string);
+ LocalFree((HLOCAL)string);
+ return ret;
+}
+#endif
+
QStringList
QMakeProject::doProjectExpand(QString func, const QString &params,
QHash<QString, QStringList> &place)
@@ -1818,19 +2201,33 @@ QMakeProject::doProjectExpand(QString func, QList<QStringList> args_list,
} else {
QString file = Option::normalizePath(args[0]);
+ bool blob = false;
+ bool lines = false;
bool singleLine = true;
- if(args.count() > 1)
- singleLine = (args[1].toLower() == "true");
-
+ if (args.count() > 1) {
+ if (!args.at(1).compare(QLatin1String("false"), Qt::CaseInsensitive))
+ singleLine = false;
+ else if (!args.at(1).compare(QLatin1String("blob"), Qt::CaseInsensitive))
+ blob = true;
+ else if (!args.at(1).compare(QLatin1String("lines"), Qt::CaseInsensitive))
+ lines = true;
+ }
QFile qfile(file);
if(qfile.open(QIODevice::ReadOnly)) {
QTextStream stream(&qfile);
- while(!stream.atEnd()) {
- ret += split_value_list(stream.readLine().trimmed());
- if(!singleLine)
- ret += "\n";
+ if (blob) {
+ ret += stream.readAll();
+ } else {
+ while (!stream.atEnd()) {
+ if (lines) {
+ ret += stream.readLine();
+ } else {
+ ret += split_value_list(stream.readLine().trimmed());
+ if (!singleLine)
+ ret += "\n";
+ }
+ }
}
- qfile.close();
}
}
break; }
@@ -1897,6 +2294,79 @@ QMakeProject::doProjectExpand(QString func, QList<QStringList> args_list,
ret = split_value_list(tmp);
}
break; }
+ case E_FORMAT_NUMBER:
+ if (args.count() > 2) {
+ fprintf(stderr, "%s:%d: format_number(number[, options...]) requires one or two arguments.\n",
+ parser.file.toLatin1().constData(), parser.line_no);
+ } else {
+ int ibase = 10;
+ int obase = 10;
+ int width = 0;
+ bool zeropad = false;
+ bool leftalign = false;
+ enum { DefaultSign, PadSign, AlwaysSign } sign = DefaultSign;
+ if (args.count() >= 2) {
+ foreach (const QString &opt, split_value_list(args.at(1))) {
+ if (opt.startsWith(QLatin1String("ibase="))) {
+ ibase = opt.mid(6).toInt();
+ } else if (opt.startsWith(QLatin1String("obase="))) {
+ obase = opt.mid(6).toInt();
+ } else if (opt.startsWith(QLatin1String("width="))) {
+ width = opt.mid(6).toInt();
+ } else if (opt == QLatin1String("zeropad")) {
+ zeropad = true;
+ } else if (opt == QLatin1String("padsign")) {
+ sign = PadSign;
+ } else if (opt == QLatin1String("alwayssign")) {
+ sign = AlwaysSign;
+ } else if (opt == QLatin1String("leftalign")) {
+ leftalign = true;
+ } else {
+ fprintf(stderr, "%s:%d: format_number(): invalid format option %s.\n",
+ parser.file.toLatin1().constData(), parser.line_no,
+ opt.toLatin1().constData());
+ goto formfail;
+ }
+ }
+ }
+ if (args.at(0).contains(QLatin1Char('.'))) {
+ fprintf(stderr, "%s:%d: format_number(): floats are currently not supported.\n",
+ parser.file.toLatin1().constData(), parser.line_no);
+ break;
+ }
+ bool ok;
+ qlonglong num = args.at(0).toLongLong(&ok, ibase);
+ if (!ok) {
+ fprintf(stderr, "%s:%d: format_number(): malformed number %s for base %d.\n",
+ parser.file.toLatin1().constData(), parser.line_no,
+ args.at(0).toLatin1().constData(), ibase);
+ break;
+ }
+ QString outstr;
+ if (num < 0) {
+ num = -num;
+ outstr = QLatin1Char('-');
+ } else if (sign == AlwaysSign) {
+ outstr = QLatin1Char('+');
+ } else if (sign == PadSign) {
+ outstr = QLatin1Char(' ');
+ }
+ QString numstr = QString::number(num, obase);
+ int space = width - outstr.length() - numstr.length();
+ if (space <= 0) {
+ outstr += numstr;
+ } else if (leftalign) {
+ outstr += numstr + QString(space, QLatin1Char(' '));
+ } else if (zeropad) {
+ outstr += QString(space, QLatin1Char('0')) + numstr;
+ } else {
+ outstr.prepend(QString(space, QLatin1Char(' ')));
+ outstr += numstr;
+ }
+ ret += outstr;
+ }
+ formfail:
+ break;
case E_JOIN: {
if(args.count() < 1 || args.count() > 4) {
fprintf(stderr, "%s:%d: join(var, glue, before, after) requires four"
@@ -1991,26 +2461,33 @@ QMakeProject::doProjectExpand(QString func, QList<QStringList> args_list,
fprintf(stderr, "%s:%d system(execut) requires one argument.\n",
parser.file.toLatin1().constData(), parser.line_no);
} else {
- char buff[256];
+ bool blob = false;
+ bool lines = false;
bool singleLine = true;
- if(args.count() > 1)
- singleLine = (args[1].toLower() == "true");
- QString output;
- FILE *proc = QT_POPEN(args[0].toLatin1().constData(), "r");
- while(proc && !feof(proc)) {
- int read_in = int(fread(buff, 1, 255, proc));
- if(!read_in)
- break;
- for(int i = 0; i < read_in; i++) {
- if((singleLine && buff[i] == '\n') || buff[i] == '\t')
- buff[i] = ' ';
+ if (args.count() > 1) {
+ if (!args.at(1).compare(QLatin1String("false"), Qt::CaseInsensitive))
+ singleLine = false;
+ else if (!args.at(1).compare(QLatin1String("blob"), Qt::CaseInsensitive))
+ blob = true;
+ else if (!args.at(1).compare(QLatin1String("lines"), Qt::CaseInsensitive))
+ lines = true;
+ }
+ QByteArray bytes = getCommandOutput(args.at(0));
+ if (lines) {
+ QTextStream stream(bytes);
+ while (!stream.atEnd())
+ ret += stream.readLine();
+ } else {
+ QString output = QString::fromLocal8Bit(bytes);
+ if (blob) {
+ ret += output;
+ } else {
+ output.replace(QLatin1Char('\t'), QLatin1Char(' '));
+ if (singleLine)
+ output.replace(QLatin1Char('\n'), QLatin1Char(' '));
+ ret += split_value_list(output);
}
- buff[read_in] = '\0';
- output += buff;
}
- ret += split_value_list(output);
- if(proc)
- QT_PCLOSE(proc);
}
break; }
case E_UNIQUE: {
@@ -2025,6 +2502,17 @@ QMakeProject::doProjectExpand(QString func, QList<QStringList> args_list,
}
}
break; }
+ case E_REVERSE:
+ if (args.count() != 1) {
+ fprintf(stderr, "%s:%d reverse(var) requires one argument.\n",
+ parser.file.toLatin1().constData(), parser.line_no);
+ } else {
+ QStringList var = values(args.first(), place);
+ for (int i = 0; i < var.size() / 2; i++)
+ var.swap(i, var.size() - i - 1);
+ ret += var;
+ }
+ break;
case E_QUOTE:
ret = args;
break;
@@ -2064,6 +2552,17 @@ QMakeProject::doProjectExpand(QString func, QList<QStringList> args_list,
for(int i = 0; i < args.size(); ++i)
ret += QRegExp::escape(args[i]);
break; }
+ case E_VAL_ESCAPE:
+ if (args.count() != 1) {
+ fprintf(stderr, "%s:%d val_escape(var) requires one argument.\n",
+ parser.file.toLatin1().constData(), parser.line_no);
+ } else {
+ QStringList vals = values(args.at(0), place);
+ ret.reserve(vals.length());
+ foreach (const QString &str, vals)
+ ret += quoteValue(str);
+ }
+ break;
case E_UPPER:
case E_LOWER: {
for(int i = 0; i < args.size(); ++i) {
@@ -2181,6 +2680,53 @@ QMakeProject::doProjectExpand(QString func, QList<QStringList> args_list,
}
}
break; }
+ case E_ENUMERATE_VARS:
+ ret += place.keys();
+ break;
+ case E_SHADOWED: {
+ QString val = QDir::cleanPath(QFileInfo(args.at(0)).absoluteFilePath());
+ if (Option::mkfile::source_root.isEmpty())
+ ret += val;
+ else if (val.startsWith(Option::mkfile::source_root))
+ ret += Option::mkfile::build_root + val.mid(Option::mkfile::source_root.length());
+ break; }
+ case E_ABSOLUTE_PATH:
+ if (args.count() > 2)
+ fprintf(stderr, "%s:%d absolute_path(path[, base]) requires one or two arguments.\n",
+ parser.file.toLatin1().constData(), parser.line_no);
+ else
+ ret += QDir::cleanPath(QDir(args.count() > 1 ? args.at(1) : QString())
+ .absoluteFilePath(args.at(0)));
+ break;
+ case E_RELATIVE_PATH:
+ if (args.count() > 2)
+ fprintf(stderr, "%s:%d relative_path(path[, base]) requires one or two arguments.\n",
+ parser.file.toLatin1().constData(), parser.line_no);
+ else
+ ret += QDir::cleanPath(QDir(args.count() > 1 ? args.at(1) : QString())
+ .relativeFilePath(args.at(0)));
+ break;
+ case E_CLEAN_PATH:
+ if (args.count() != 1)
+ fprintf(stderr, "%s:%d clean_path(path) requires one argument.\n",
+ parser.file.toLatin1().constData(), parser.line_no);
+ else
+ ret += QDir::cleanPath(args.at(0));
+ break;
+ case E_NATIVE_PATH:
+ if (args.count() != 1)
+ fprintf(stderr, "%s:%d native_path(path) requires one argument.\n",
+ parser.file.toLatin1().constData(), parser.line_no);
+ else
+ ret += Option::fixPathToTargetOS(args.at(0), false);
+ 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
+ ret += shellQuote(args.at(0));
+ break;
default: {
fprintf(stderr, "%s:%d: Unknown replace function: %s\n",
parser.file.toLatin1().constData(), parser.line_no,
@@ -2448,6 +2994,8 @@ QMakeProject::doProjectTest(QString func, QList<QStringList> args_list, QHash<QS
return testFunctions.contains(args[0]);
else if(args[1] == "replace")
return replaceFunctions.contains(args[0]);
+ else if(args[1] == "var")
+ return place.contains(args[0]);
fprintf(stderr, "%s:%d: defined(function, type): unexpected type [%s].\n",
parser.file.toLatin1().constData(), parser.line_no,
args[1].toLatin1().constData());
@@ -2623,6 +3171,7 @@ QMakeProject::doProjectTest(QString func, QList<QStringList> args_list, QHash<QS
QString msg = fixEnvVariables(args[1]);
debug_msg(args[0].toInt(), "Project DEBUG: %s", msg.toLatin1().constData());
return true; }
+ case T_LOG:
case T_ERROR:
case T_MESSAGE:
case T_WARNING: {
@@ -2632,13 +3181,17 @@ QMakeProject::doProjectTest(QString func, QList<QStringList> args_list, QHash<QS
return false;
}
QString msg = fixEnvVariables(args.first());
- fprintf(stderr, "Project %s: %s\n", func.toUpper().toLatin1().constData(), msg.toLatin1().constData());
- if(func == "error")
+ if (func_t == T_LOG) {
+ fputs(msg.toLatin1().constData(), stderr);
+ } else {
+ fprintf(stderr, "Project %s: %s\n", func.toUpper().toLatin1().constData(), msg.toLatin1().constData());
+ if (func == "error")
#if defined(QT_BUILD_QMAKE_LIBRARY)
- return false;
+ return false;
#else
- exit(2);
+ exit(2);
#endif
+ }
return true; }
case T_OPTION:
if (args.count() != 1) {
@@ -2648,6 +3201,11 @@ QMakeProject::doProjectTest(QString func, QList<QStringList> args_list, QHash<QS
}
if (args.first() == "recursive") {
recursive = true;
+ } else if (args.first() == "host_build") {
+ if (!host_build && isActiveConfig("cross_compile")) {
+ host_build = true;
+ need_restart = true;
+ }
} else {
fprintf(stderr, "%s:%d: unrecognized option() argument '%s'.\n",
parser.file.toLatin1().constData(), parser.line_no,
@@ -2655,6 +3213,238 @@ QMakeProject::doProjectTest(QString func, QList<QStringList> args_list, QHash<QS
return false;
}
return true;
+ case T_CACHE: {
+ if (args.count() > 3) {
+ fprintf(stderr, "%s:%d: cache(var, [set|add|sub] [transient] [super], [srcvar]) requires one to three arguments.\n",
+ parser.file.toLatin1().constData(), parser.line_no);
+ return false;
+ }
+ bool persist = true;
+ bool super = false;
+ enum { CacheSet, CacheAdd, CacheSub } mode = CacheSet;
+ QString srcvar;
+ if (args.count() >= 2) {
+ foreach (const QString &opt, split_value_list(args.at(1))) {
+ if (opt == QLatin1String("transient")) {
+ persist = false;
+ } else if (opt == QLatin1String("super")) {
+ super = true;
+ } else if (opt == QLatin1String("set")) {
+ mode = CacheSet;
+ } else if (opt == QLatin1String("add")) {
+ mode = CacheAdd;
+ } else if (opt == QLatin1String("sub")) {
+ mode = CacheSub;
+ } else {
+ fprintf(stderr, "%s:%d: cache(): invalid flag %s.\n",
+ parser.file.toLatin1().constData(), parser.line_no,
+ opt.toLatin1().constData());
+ return false;
+ }
+ }
+ if (args.count() >= 3) {
+ srcvar = args.at(2);
+ } else if (mode != CacheSet) {
+ fprintf(stderr, "%s:%d: cache(): modes other than 'set' require a source variable.\n",
+ parser.file.toLatin1().constData(), parser.line_no);
+ return false;
+ }
+ }
+ QString varstr;
+ QString dstvar = args.at(0);
+ if (!dstvar.isEmpty()) {
+ if (srcvar.isEmpty())
+ srcvar = dstvar;
+ if (!place.contains(srcvar)) {
+ fprintf(stderr, "%s:%d: variable %s is not defined.\n",
+ parser.file.toLatin1().constData(), parser.line_no,
+ srcvar.toLatin1().constData());
+ return false;
+ }
+ // The current ("native") value can differ from the cached value, e.g., the current
+ // CONFIG will typically have more values than the cached one. Therefore we deal with
+ // them separately.
+ const QStringList diffval = values(srcvar, place);
+ const QStringList oldval = base_vars.value(dstvar);
+ QStringList newval;
+ if (mode == CacheSet) {
+ newval = diffval;
+ } else {
+ newval = oldval;
+ if (mode == CacheAdd)
+ newval += diffval;
+ else
+ subAll(&newval, diffval);
+ }
+ // We assume that whatever got the cached value to be what it is now will do so
+ // the next time as well, so it is OK that the early exit here will skip the
+ // persisting as well.
+ if (oldval == newval)
+ return true;
+ base_vars[dstvar] = newval;
+ do {
+ if (dstvar == "QMAKEPATH")
+ cached_qmakepath = newval;
+ else if (dstvar == "QMAKEFEATURES")
+ cached_qmakefeatures = newval;
+ else
+ break;
+ invalidateFeatureRoots();
+ } while (false);
+ if (!persist)
+ return true;
+ varstr = dstvar;
+ if (mode == CacheAdd)
+ varstr += QLatin1String(" +=");
+ else if (mode == CacheSub)
+ varstr += QLatin1String(" -=");
+ else
+ varstr += QLatin1String(" =");
+ if (diffval.count() == 1) {
+ varstr += QLatin1Char(' ');
+ varstr += quoteValue(diffval.at(0));
+ } else if (!diffval.isEmpty()) {
+ foreach (const QString &vval, diffval) {
+ varstr += QLatin1String(" \\\n ");
+ varstr += quoteValue(vval);
+ }
+ }
+ varstr += QLatin1Char('\n');
+ }
+ QString fn;
+ if (super) {
+ if (superfile.isEmpty()) {
+ superfile = Option::output_dir + QLatin1String("/.qmake.super");
+ printf("Info: creating super cache file %s\n", superfile.toLatin1().constData());
+ }
+ fn = superfile;
+ } else {
+ if (cachefile.isEmpty()) {
+ cachefile = Option::output_dir + QLatin1String("/.qmake.cache");
+ printf("Info: creating cache file %s\n", cachefile.toLatin1().constData());
+ if (cached_build_root.isEmpty()) {
+ cached_build_root = Option::output_dir;
+ cached_source_root = values("_PRO_FILE_PWD_", place).first();
+ if (cached_source_root == cached_build_root)
+ cached_source_root.clear();
+ invalidateFeatureRoots();
+ }
+ }
+ fn = cachefile;
+ }
+ QFileInfo qfi(fn);
+ if (!QDir::current().mkpath(qfi.path())) {
+ fprintf(stderr, "%s:%d: ERROR creating cache directory %s\n",
+ parser.file.toLatin1().constData(), parser.line_no,
+ qfi.path().toLatin1().constData());
+ return false;
+ }
+ QString errStr;
+ if (!writeFile(fn, QIODevice::Append, varstr, &errStr)) {
+ fprintf(stderr, "ERROR writing cache file %s: %s\n",
+ fn.toLatin1().constData(), errStr.toLatin1().constData());
+#if defined(QT_BUILD_QMAKE_LIBRARY)
+ return false;
+#else
+ exit(2);
+#endif
+ }
+ return true; }
+ case T_MKPATH:
+ if (args.count() != 1) {
+ fprintf(stderr, "%s:%d: mkpath(name) requires one argument.\n",
+ parser.file.toLatin1().constData(), parser.line_no);
+ return false;
+ }
+ if (!QDir::current().mkpath(args.at(0))) {
+ fprintf(stderr, "%s:%d: ERROR creating directory %s\n",
+ parser.file.toLatin1().constData(), parser.line_no,
+ QDir::toNativeSeparators(args.at(0)).toLatin1().constData());
+ return false;
+ }
+ return true;
+ case T_WRITE_FILE: {
+ if (args.count() > 3) {
+ fprintf(stderr, "%s:%d: write_file(name, [content var, [append]]) requires one to three arguments.\n",
+ parser.file.toLatin1().constData(), parser.line_no);
+ return false;
+ }
+ QIODevice::OpenMode mode = QIODevice::Truncate;
+ QString contents;
+ if (args.count() >= 2) {
+ QStringList vals = values(args.at(1), place);
+ if (!vals.isEmpty())
+ contents = vals.join(QLatin1String("\n")) + QLatin1Char('\n');
+ if (args.count() >= 3)
+ if (!args.at(2).compare(QLatin1String("append"), Qt::CaseInsensitive))
+ mode = QIODevice::Append;
+ }
+ QFileInfo qfi(args.at(0));
+ if (!QDir::current().mkpath(qfi.path())) {
+ fprintf(stderr, "%s:%d: ERROR creating directory %s\n",
+ parser.file.toLatin1().constData(), parser.line_no,
+ qfi.path().toLatin1().constData());
+ return false;
+ }
+ QString errStr;
+ if (!writeFile(args.at(0), mode, contents, &errStr)) {
+ fprintf(stderr, "%s:%d ERROR writing %s: %s\n",
+ parser.file.toLatin1().constData(), parser.line_no,
+ args.at(0).toLatin1().constData(), errStr.toLatin1().constData());
+ return false;
+ }
+ return true; }
+ case T_TOUCH: {
+ if (args.count() != 2) {
+ fprintf(stderr, "%s:%d: touch(file, reffile) requires two arguments.\n",
+ parser.file.toLatin1().constData(), parser.line_no);
+ return false;
+ }
+#ifdef Q_OS_UNIX
+ struct stat st;
+ if (stat(args.at(1).toLocal8Bit().constData(), &st)) {
+ fprintf(stderr, "%s:%d: ERROR: cannot stat() reference file %s: %s.\n",
+ parser.file.toLatin1().constData(), parser.line_no,
+ args.at(1).toLatin1().constData(), strerror(errno));
+ return false;
+ }
+ struct utimbuf utb;
+ utb.actime = time(0);
+ utb.modtime = st.st_mtime;
+ if (utime(args.at(0).toLocal8Bit().constData(), &utb)) {
+ fprintf(stderr, "%s:%d: ERROR: cannot touch %s: %s.\n",
+ parser.file.toLatin1().constData(), parser.line_no,
+ args.at(0).toLatin1().constData(), strerror(errno));
+ return false;
+ }
+#else
+ HANDLE rHand = CreateFile((wchar_t*)args.at(1).utf16(),
+ GENERIC_READ, FILE_SHARE_READ,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (rHand == INVALID_HANDLE_VALUE) {
+ fprintf(stderr, "%s:%d: ERROR: cannot open() reference file %s: %s.\n",
+ parser.file.toLatin1().constData(), parser.line_no,
+ args.at(1).toLatin1().constData(),
+ windowsErrorCode().toLatin1().constData());
+ return false;
+ }
+ FILETIME ft;
+ GetFileTime(rHand, 0, 0, &ft);
+ CloseHandle(rHand);
+ HANDLE wHand = CreateFile((wchar_t*)args.at(0).utf16(),
+ GENERIC_WRITE, FILE_SHARE_READ,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (wHand == INVALID_HANDLE_VALUE) {
+ fprintf(stderr, "%s:%d: ERROR: cannot open %s: %s.\n",
+ parser.file.toLatin1().constData(), parser.line_no,
+ args.at(0).toLatin1().constData(),
+ windowsErrorCode().toLatin1().constData());
+ return false;
+ }
+ SetFileTime(wHand, 0, 0, &ft);
+ CloseHandle(wHand);
+#endif
+ break; }
default:
fprintf(stderr, "%s:%d: Unknown test function: %s\n", parser.file.toLatin1().constData(), parser.line_no,
func.toLatin1().constData());
@@ -2968,7 +3758,11 @@ QStringList &QMakeProject::values(const QString &_var, QHash<QString, QStringLis
} else if(var == QLatin1String("_QMAKE_CACHE_")) {
var = ".BUILTIN." + var;
if(Option::mkfile::do_cache)
- place[var] = QStringList(Option::mkfile::cachefile);
+ place[var] = QStringList(cachefile);
+ } else if(var == QLatin1String("_QMAKE_SUPER_CACHE_")) {
+ var = ".BUILTIN." + var;
+ if(Option::mkfile::do_cache && !superfile.isEmpty())
+ place[var] = QStringList(superfile);
} else if(var == QLatin1String("TEMPLATE")) {
if(!Option::user_template.isEmpty()) {
var = ".BUILTIN.USER." + var;
@@ -3067,11 +3861,11 @@ QStringList &QMakeProject::values(const QString &_var, QHash<QString, QStringLis
}
} else if (var == QLatin1String("QMAKE_QMAKE")) {
if (place[var].isEmpty())
- place[var] = QStringList(Option::fixPathToTargetOS(
+ place[var] = QStringList(
!Option::qmake_abslocation.isEmpty()
? Option::qmake_abslocation
- : QLibraryInfo::location(QLibraryInfo::HostBinariesPath) + "/qmake",
- false));
+ : QLibraryInfo::rawLocation(QLibraryInfo::HostBinariesPath,
+ QLibraryInfo::EffectivePaths) + "/qmake");
}
#if defined(Q_OS_WIN32) && defined(Q_CC_MSVC)
else if(var.startsWith(QLatin1String("QMAKE_TARGET."))) {