diff options
Diffstat (limited to 'qmake')
-rw-r--r-- | qmake/Makefile.unix | 7 | ||||
-rw-r--r-- | qmake/Makefile.win32 | 2 | ||||
-rw-r--r-- | qmake/Makefile.win32-g++ | 3 | ||||
-rw-r--r-- | qmake/generators/makefile.cpp | 188 | ||||
-rw-r--r-- | qmake/generators/makefile.h | 8 | ||||
-rw-r--r-- | qmake/generators/metamakefile.cpp | 4 | ||||
-rw-r--r-- | qmake/generators/unix/unixmake.cpp | 2 | ||||
-rw-r--r-- | qmake/generators/win32/cesdkhandler.cpp | 127 | ||||
-rw-r--r-- | qmake/generators/win32/cesdkhandler.h | 94 | ||||
-rw-r--r-- | qmake/generators/win32/msvc_nmake.cpp | 25 | ||||
-rw-r--r-- | qmake/generators/win32/msvc_vcproj.cpp | 36 | ||||
-rw-r--r-- | qmake/generators/win32/msvc_vcproj.h | 1 | ||||
-rw-r--r-- | qmake/main.cpp | 5 | ||||
-rw-r--r-- | qmake/option.cpp | 134 | ||||
-rw-r--r-- | qmake/option.h | 14 | ||||
-rw-r--r-- | qmake/project.cpp | 1020 | ||||
-rw-r--r-- | qmake/project.h | 9 | ||||
-rw-r--r-- | qmake/property.cpp | 10 | ||||
-rw-r--r-- | qmake/qmake.pri | 6 | ||||
-rw-r--r-- | qmake/qmake.pro | 1 |
20 files changed, 1347 insertions, 349 deletions
diff --git a/qmake/Makefile.unix b/qmake/Makefile.unix index ad430e2930..b1df49aab3 100644 --- a/qmake/Makefile.unix +++ b/qmake/Makefile.unix @@ -13,7 +13,7 @@ OBJS=project.o property.o main.o makefile.o unixmake2.o unixmake.o \ mingw_make.o option.o winmakefile.o projectgenerator.o \ meta.o makefiledeps.o metamakefile.o xmloutput.o pbuilder_pbx.o \ borland_bmake.o msvc_vcproj.o msvc_vcxproj.o msvc_nmake.o msvc_objectmodel.o msbuild_objectmodel.o \ - gbuild.o + gbuild.o cesdkhandler.o #qt code QOBJS=qtextcodec.o qutfcodec.o qstring.o qstringbuilder.o qtextstream.o qiodevice.o qmalloc.o qglobal.o \ @@ -36,7 +36,7 @@ DEPEND_SRC=project.cpp property.cpp meta.cpp main.cpp generators/makefile.cpp ge generators/mac/pbuilder_pbx.cpp generators/mac/xmloutput.cpp generators/metamakefile.cpp \ generators/makefiledeps.cpp option.cpp generators/win32/mingw_make.cpp generators/makefile.cpp \ generators/win32/msvc_vcproj.cpp generators/win32/msvc_vcxproj.cpp generators/win32/msvc_objectmodel.cpp generators/win32/msbuild_objectmodel.cpp generators/win32/msbuild_objectmodel.cpp generators/win32/msvc_nmake.cpp generators/win32/borland_bmake.cpp \ - generators/integrity/gbuild.cpp \ + generators/integrity/gbuild.cpp generators/win32/cesdkhandler.cpp \ $(SOURCE_PATH)/src/corelib/codecs/qtextcodec.cpp $(SOURCE_PATH)/src/corelib/codecs/qutfcodec.cpp \ $(SOURCE_PATH)/src/corelib/tools/qstring.cpp $(SOURCE_PATH)/src/corelib/io/qfile.cpp \ $(SOURCE_PATH)/src/corelib/io/qfiledevice.cpp \ @@ -160,6 +160,9 @@ msvc_vcxproj.o: $(QMKSRC)/generators/win32/msvc_vcxproj.cpp msvc_nmake.o: $(QMKSRC)/generators/win32/msvc_nmake.cpp $(CXX) -c -o $@ $(CXXFLAGS) $< +cesdkhandler.o: $(QMKSRC)/generators/win32/cesdkhandler.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $< + pbuilder_pbx.o: $(QMKSRC)/generators/mac/pbuilder_pbx.cpp $(CXX) -c -o $@ $(CXXFLAGS) $< diff --git a/qmake/Makefile.win32 b/qmake/Makefile.win32 index 029546f69d..ad6d4448d7 100644 --- a/qmake/Makefile.win32 +++ b/qmake/Makefile.win32 @@ -61,7 +61,7 @@ OBJS = project.obj main.obj makefile.obj unixmake.obj unixmake2.obj mingw makefiledeps.obj metamakefile.obj xmloutput.obj pbuilder_pbx.obj \ borland_bmake.obj msvc_nmake.obj msvc_vcproj.obj msvc_vcxproj.obj \ msvc_objectmodel.obj msbuild_objectmodel.obj registry.obj \ - gbuild.obj + gbuild.obj cesdkhandler.obj !IFDEF QMAKE_OPENSOURCE_EDITION CFLAGS = $(CFLAGS) -DQMAKE_OPENSOURCE_EDITION diff --git a/qmake/Makefile.win32-g++ b/qmake/Makefile.win32-g++ index 1966a8bbbc..d06d958ca3 100644 --- a/qmake/Makefile.win32-g++ +++ b/qmake/Makefile.win32-g++ @@ -64,7 +64,8 @@ OBJS = project.o main.o makefile.o unixmake.o unixmake2.o mingw_make.o \ option.o winmakefile.o projectgenerator.o property.o meta.o \ makefiledeps.o metamakefile.o xmloutput.o pbuilder_pbx.o \ borland_bmake.o msvc_nmake.o msvc_vcproj.o msvc_vcxproj.o \ - msvc_objectmodel.o msbuild_objectmodel.o registry.o gbuild.o + msvc_objectmodel.o msbuild_objectmodel.o registry.o gbuild.o \ + cesdkhandler.o ifdef QMAKE_OPENSOURCE_EDITION CFLAGS += -DQMAKE_OPENSOURCE_EDITION diff --git a/qmake/generators/makefile.cpp b/qmake/generators/makefile.cpp index d37089ee35..83354b9114 100644 --- a/qmake/generators/makefile.cpp +++ b/qmake/generators/makefile.cpp @@ -93,17 +93,12 @@ bool MakefileGenerator::canExecute(const QStringList &cmdline, int *a) const QString MakefileGenerator::mkdir_p_asstring(const QString &dir, bool escape) const { - QString ret = "@$(CHK_DIR_EXISTS) "; + QString ret = "@" + chkdir + " "; if(escape) ret += escapeFilePath(dir); else ret += dir; - ret += " "; - if(isWindowsShell()) - ret += "$(MKDIR)"; - else - ret += "|| $(MKDIR)"; - ret += " "; + ret += " " + chkglue + "$(MKDIR) "; if(escape) ret += escapeFilePath(dir); else @@ -198,12 +193,12 @@ MakefileGenerator::initOutPaths() QHash<QString, QStringList> &v = project->variables(); //for shadow builds if(!v.contains("QMAKE_ABSOLUTE_SOURCE_PATH")) { - if(Option::mkfile::do_cache && !Option::mkfile::cachefile.isEmpty() && + if (Option::mkfile::do_cache && !project->cacheFile().isEmpty() && v.contains("QMAKE_ABSOLUTE_SOURCE_ROOT")) { QString root = v["QMAKE_ABSOLUTE_SOURCE_ROOT"].first(); root = QDir::fromNativeSeparators(root); if(!root.isEmpty()) { - QFileInfo fi = fileInfo(Option::mkfile::cachefile); + QFileInfo fi = fileInfo(project->cacheFile()); if(!fi.makeAbsolute()) { QString cache_r = fi.path(), pwd = Option::output_dir; if(pwd.startsWith(cache_r) && !pwd.startsWith(root)) { @@ -431,6 +426,15 @@ MakefileGenerator::init() init_already = true; QHash<QString, QStringList> &v = project->variables(); + + chkdir = v["QMAKE_CHK_DIR_EXISTS"].join(" "); + chkfile = v["QMAKE_CHK_FILE_EXISTS"].join(" "); + if (chkfile.isEmpty()) // Backwards compat with Qt4 specs + chkfile = isWindowsShell() ? "if not exist" : "test -f"; + chkglue = v["QMAKE_CHK_EXISTS_GLUE"].join(" "); + if (chkglue.isEmpty()) // Backwards compat with Qt4 specs + chkglue = isWindowsShell() ? "" : "|| "; + QStringList &quc = v["QMAKE_EXTRA_COMPILERS"]; //make sure the COMPILERS are in the correct input/output chain order @@ -853,8 +857,8 @@ MakefileGenerator::init() } // escape qmake command - QStringList &qmk = project->values("QMAKE_QMAKE"); - qmk = escapeFilePaths(qmk); + project->values("QMAKE_QMAKE") = + escapeFilePaths(QStringList(Option::fixPathToTargetOS(Option::qmake_abslocation, false))); } bool @@ -1771,10 +1775,6 @@ MakefileGenerator::writeExtraTargets(QTextStream &t) if(!cmd.isEmpty()) t << "\n\t" << cmd; t << endl << endl; - - project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_TARGETS.") + (*it)) << escapeDependencyPath(targ); - project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_DEPS.") + (*it) + escapeDependencyPath(targ)) << deps.split(" ", QString::SkipEmptyParts); - project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_CMD.") + (*it) + escapeDependencyPath(targ)) << cmd; } } @@ -1973,17 +1973,13 @@ MakefileGenerator::writeExtraCompilerTargets(QTextStream &t) QString cmd = replaceExtraCompilerVariables(tmp_cmd, escapeFilePaths(inputs), QStringList(tmp_out)); t << escapeDependencyPath(tmp_out) << ":"; - project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_TARGETS.") + (*it)) << escapeDependencyPath(tmp_out); // compiler.CONFIG+=explicit_dependencies means that ONLY compiler.depends gets to cause Makefile dependencies if(project->values((*it) + ".CONFIG").indexOf("explicit_dependencies") != -1) { t << " " << valList(escapeDependencyPaths(fileFixify(tmp_dep, Option::output_dir, Option::output_dir))); - project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_DEPS.") + (*it) + escapeDependencyPath(tmp_out)) << tmp_dep; } else { t << " " << valList(escapeDependencyPaths(inputs)) << " " << valList(escapeDependencyPaths(deps)); - project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_DEPS.") + (*it) + escapeDependencyPath(tmp_out)) << inputs << deps; } t << "\n\t" << cmd << endl << endl; - project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_CMD.") + (*it) + escapeDependencyPath(tmp_out)) << cmd; continue; } for(QStringList::ConstIterator input = tmp_inputs.begin(); input != tmp_inputs.end(); ++input) { @@ -2087,9 +2083,6 @@ MakefileGenerator::writeExtraCompilerTargets(QTextStream &t) } t << escapeDependencyPath(out) << ": " << valList(escapeDependencyPaths(deps)) << "\n\t" << cmd << endl << endl; - project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_TARGETS.") + (*it)) << escapeDependencyPath(out); - project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_DEPS.") + (*it) + escapeDependencyPath(out)) << deps; - project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_CMD.") + (*it) + escapeDependencyPath(out)) << cmd; } } t << "compiler_clean: " << clean_targets << endl << endl; @@ -2193,7 +2186,9 @@ QString MakefileGenerator::buildArgs(const QString &outdir) if(!Option::mkfile::do_dep_heuristics) ret += " -nodependheuristics"; if(!Option::mkfile::qmakespec_commandline.isEmpty()) - ret += " -spec " + specdir(outdir); + ret += " -spec " + specdir(outdir, 1); + if (!Option::mkfile::xqmakespec_commandline.isEmpty()) + ret += " -xspec " + specdir(outdir, 0); if (Option::target_mode_overridden) { if (Option::target_mode == Option::TARG_MACX_MODE) ret += " -macx"; @@ -2307,7 +2302,7 @@ MakefileGenerator::findSubDirsSubTargets() const if(!project->isEmpty(fixedSubdir + ".makefile")) { st->makefile = project->first(fixedSubdir + ".makefile"); } else { - st->makefile = "$(MAKEFILE)"; + st->makefile = "Makefile"; if(!st->profile.isEmpty()) { QString basename = st->in_directory; int new_slsh = basename.lastIndexOf(Option::dir_sep); @@ -2360,7 +2355,7 @@ void MakefileGenerator::writeSubDirs(QTextStream &t) { QList<SubTarget*> targets = findSubDirsSubTargets(); - t << "first: make_default" << endl; + t << "first: make_first" << endl; int flags = SubTargetInstalls; if(project->isActiveConfig("ordered")) flags |= SubTargetOrdered; @@ -2377,6 +2372,22 @@ void MakefileGenerator::writeSubMakeCall(QTextStream &t, const QString &callPref } void +MakefileGenerator::writeSubTargetCall(QTextStream &t, + const QString &in_directory, const QString &in, const QString &out_directory, const QString &out, + const QString &out_directory_cdin, const QString &makefilein, const QString &out_directory_cdout) +{ + QString pfx; + if (!in.isEmpty()) { + if (!in_directory.isEmpty()) + t << "\n\t" << mkdir_p_asstring(out_directory); + pfx = "( " + chkfile + " " + out + " " + chkglue + + "$(QMAKE) " + in + buildArgs(in_directory) + " -o " + out + + " ) && "; + } + writeSubMakeCall(t, out_directory_cdin + pfx, makefilein, out_directory_cdout); +} + +void MakefileGenerator::writeSubTargets(QTextStream &t, QList<MakefileGenerator::SubTarget*> targets, int flags) { // blasted includes @@ -2405,8 +2416,6 @@ MakefileGenerator::writeSubTargets(QTextStream &t, QList<MakefileGenerator::SubT t << "SYMLINK = " << var("QMAKE_SYMBOLIC_LINK") << endl; t << "DEL_DIR = " << var("QMAKE_DEL_DIR") << endl; t << "MOVE = " << var("QMAKE_MOVE") << endl; - t << "CHK_DIR_EXISTS= " << var("QMAKE_CHK_DIR_EXISTS") << endl; - t << "MKDIR = " << var("QMAKE_MKDIR") << endl; t << "SUBTARGETS = "; // subtargets are sub-directory for(int target = 0; target < targets.size(); ++target) t << " \\\n\t\t" << targets.at(target)->target; @@ -2417,11 +2426,13 @@ MakefileGenerator::writeSubTargets(QTextStream &t, QList<MakefileGenerator::SubT QStringList targetSuffixes; const QString abs_source_path = project->first("QMAKE_ABSOLUTE_SOURCE_PATH"); if (!(flags & SubTargetSkipDefaultTargets)) { - targetSuffixes << "make_default" << "make_first" << "all" << "clean" << "distclean" + targetSuffixes << "make_first" << "all" << "clean" << "distclean" << QString((flags & SubTargetInstalls) ? "install_subtargets" : "install") << QString((flags & SubTargetInstalls) ? "uninstall_subtargets" : "uninstall"); } + bool dont_recurse = project->isActiveConfig("dont_recurse"); + // generate target rules for(int target = 0; target < targets.size(); ++target) { SubTarget *subtarget = targets.at(target); @@ -2434,11 +2445,6 @@ MakefileGenerator::writeSubTargets(QTextStream &t, QList<MakefileGenerator::SubT if(!abs_source_path.isEmpty() && out_directory.startsWith(abs_source_path)) out_directory = Option::output_dir + out_directory.mid(abs_source_path.length()); - QString mkfile = subtarget->makefile; - if(!in_directory.isEmpty()) - mkfile.prepend(out_directory); - - QString in_directory_cdin, in_directory_cdout, out_directory_cdin, out_directory_cdout; #define MAKE_CD_IN_AND_OUT(directory) \ if(!directory.isEmpty()) { \ if(project->isActiveConfig("cd_change_global")) { \ @@ -2459,25 +2465,28 @@ MakefileGenerator::writeSubTargets(QTextStream &t, QList<MakefileGenerator::SubT } else { \ directory ## _cdin = "\n\t"; \ } - MAKE_CD_IN_AND_OUT(in_directory); + + QString out_directory_cdin, out_directory_cdout; MAKE_CD_IN_AND_OUT(out_directory); + QString makefilein = " -f " + subtarget->makefile; + //qmake it + QString out; + QString in; if(!subtarget->profile.isEmpty()) { - QString out = subtarget->makefile; - QString in = escapeFilePath(fileFixify(in_directory + subtarget->profile, FileFixifyAbsolute)); + out = subtarget->makefile; + in = escapeFilePath(fileFixify(in_directory + subtarget->profile, FileFixifyAbsolute)); if(out.startsWith(in_directory)) out = out.mid(in_directory.length()); - t << mkfile << ": " << "\n\t"; - if(!in_directory.isEmpty()) { - t << mkdir_p_asstring(out_directory) - << out_directory_cdin - << "$(QMAKE) " << in << buildArgs(in_directory) << " -o " << out - << in_directory_cdout << endl; + t << subtarget->target << "-qmake_all: "; + if (flags & SubTargetOrdered) { + if (target) + t << targets.at(target - 1)->target << "-qmake_all"; } else { - t << "$(QMAKE) " << in << buildArgs(in_directory) << " -o " << out << endl; + if (!subtarget->depends.isEmpty()) + t << valGlue(subtarget->depends, QString(), "-qmake_all ", "-qmake_all"); } - t << subtarget->target << "-qmake_all: "; if(project->isEmpty("QMAKE_NOFORCE")) t << " FORCE"; t << "\n\t"; @@ -2485,21 +2494,24 @@ MakefileGenerator::writeSubTargets(QTextStream &t, QList<MakefileGenerator::SubT t << mkdir_p_asstring(out_directory) << out_directory_cdin << "$(QMAKE) " << in << buildArgs(in_directory) << " -o " << out - << in_directory_cdout << endl; + << out_directory_cdout; } else { - t << "$(QMAKE) " << in << buildArgs(in_directory) << " -o " << out << endl; + t << "$(QMAKE) " << in << buildArgs(in_directory) << " -o " << out; } + if (!dont_recurse) + writeSubMakeCall(t, out_directory_cdin, makefilein + " qmake_all", out_directory_cdout); + else + t << endl; } - QString makefilein = " -f " + subtarget->makefile; - { //actually compile - t << subtarget->target << ": " << mkfile; + t << subtarget->target << ":"; if(!subtarget->depends.isEmpty()) t << " " << valList(subtarget->depends); if(project->isEmpty("QMAKE_NOFORCE")) t << " FORCE"; - writeSubMakeCall(t, out_directory_cdin, makefilein, out_directory_cdout); + writeSubTargetCall(t, in_directory, in, out_directory, out, + out_directory_cdin, makefilein, out_directory_cdout); } for(int suffix = 0; suffix < targetSuffixes.size(); ++suffix) { @@ -2509,34 +2521,31 @@ MakefileGenerator::writeSubTargets(QTextStream &t, QList<MakefileGenerator::SubT else if(s == "uninstall_subtargets") s = "uninstall"; else if(s == "make_first") - s = "first"; - else if(s == "make_default") s = QString(); if(flags & SubTargetOrdered) { - t << subtarget->target << "-" << targetSuffixes.at(suffix) << "-ordered: " << mkfile; + t << subtarget->target << "-" << targetSuffixes.at(suffix) << "-ordered:"; if(target) t << " " << targets.at(target-1)->target << "-" << targetSuffixes.at(suffix) << "-ordered "; if(project->isEmpty("QMAKE_NOFORCE")) t << " FORCE"; - writeSubMakeCall(t, out_directory_cdin, makefilein + " " + s, out_directory_cdout); + writeSubTargetCall(t, in_directory, in, out_directory, out, + out_directory_cdin, makefilein + " " + s, out_directory_cdout); } - t << subtarget->target << "-" << targetSuffixes.at(suffix) << ": " << mkfile; + t << subtarget->target << "-" << targetSuffixes.at(suffix) << ":"; if(!subtarget->depends.isEmpty()) t << " " << valGlue(subtarget->depends, QString(), "-" + targetSuffixes.at(suffix) + " ", "-"+targetSuffixes.at(suffix)); if(project->isEmpty("QMAKE_NOFORCE")) t << " FORCE"; - writeSubMakeCall(t, out_directory_cdin, makefilein + " " + s, out_directory_cdout); + writeSubTargetCall(t, in_directory, in, out_directory, out, + out_directory_cdin, makefilein + " " + s, out_directory_cdout); } } t << endl; if (!(flags & SubTargetSkipDefaultTargets)) { - if(project->values("QMAKE_INTERNAL_QMAKE_DEPS").indexOf("qmake_all") == -1) - project->values("QMAKE_INTERNAL_QMAKE_DEPS").append("qmake_all"); - - writeMakeQmake(t); + writeMakeQmake(t, true); t << "qmake_all:"; if(!targets.isEmpty()) { @@ -2560,7 +2569,7 @@ MakefileGenerator::writeSubTargets(QTextStream &t, QList<MakefileGenerator::SubT t << suffix << ":"; for(int target = 0; target < targets.size(); ++target) { SubTarget *subTarget = targets.at(target); - if((suffix == "make_first" || suffix == "make_default") + if (suffix == "make_first" && project->values(subTarget->name + ".CONFIG").indexOf("no_default_target") != -1) { continue; } @@ -2629,28 +2638,31 @@ MakefileGenerator::writeSubTargets(QTextStream &t, QList<MakefileGenerator::SubT if(!recurse.contains(subtarget->name)) continue; - QString mkfile = subtarget->makefile; - if(!in_directory.isEmpty()) { - if(!out_directory.endsWith(Option::dir_sep)) - mkfile.prepend(out_directory + Option::dir_sep); - else - mkfile.prepend(out_directory); - } + QString out_directory_cdin, out_directory_cdout; MAKE_CD_IN_AND_OUT(out_directory); QString makefilein = " -f " + subtarget->makefile; + QString out; + QString in; + if (!subtarget->profile.isEmpty()) { + out = subtarget->makefile; + in = escapeFilePath(fileFixify(in_directory + subtarget->profile, FileFixifyAbsolute)); + if (out.startsWith(in_directory)) + out = out.mid(in_directory.length()); + } + //write the rule/depends if(flags & SubTargetOrdered) { const QString dep = subtarget->target + "-" + (*qut_it) + "_ordered"; - t << dep << ": " << mkfile; + t << dep << ":"; if(target) t << " " << targets.at(target-1)->target << "-" << (*qut_it) << "_ordered "; deps += " " + dep; } else { const QString dep = subtarget->target + "-" + (*qut_it); - t << dep << ": " << mkfile; + t << dep << ":"; if(!subtarget->depends.isEmpty()) t << " " << valGlue(subtarget->depends, QString(), "-" + (*qut_it) + " ", "-" + (*qut_it)); deps += " " + dep; @@ -2661,12 +2673,8 @@ MakefileGenerator::writeSubTargets(QTextStream &t, QList<MakefileGenerator::SubT sub_targ = project->first((*qut_it) + ".recurse_target"); //write the commands - if(!out_directory.isEmpty()) { - writeSubMakeCall(t, out_directory_cdin, makefilein + " " + sub_targ, - out_directory_cdout); - } else { - writeSubMakeCall(t, "\n\t", makefilein + " " + sub_targ, QString()); - } + writeSubTargetCall(t, in_directory, in, out_directory, out, + out_directory_cdin, makefilein + " " + sub_targ, out_directory_cdout); } } if(project->isEmpty("QMAKE_NOFORCE") && @@ -2688,7 +2696,7 @@ MakefileGenerator::writeSubTargets(QTextStream &t, QList<MakefileGenerator::SubT } void -MakefileGenerator::writeMakeQmake(QTextStream &t) +MakefileGenerator::writeMakeQmake(QTextStream &t, bool noDummyQmakeAll) { QString ofile = Option::fixPathToTargetOS(fileFixify(Option::output.fileName())); if(project->isEmpty("QMAKE_FAILED_REQUIREMENTS") && !project->isEmpty("QMAKE_INTERNAL_PRL_FILE")) { @@ -2702,8 +2710,12 @@ MakefileGenerator::writeMakeQmake(QTextStream &t) QString qmake = build_args(); if(!ofile.isEmpty() && !project->isActiveConfig("no_autoqmake")) { t << escapeFilePath(ofile) << ": " << escapeDependencyPath(fileFixify(pfile)) << " "; - if(Option::mkfile::do_cache) - t << escapeDependencyPath(fileFixify(Option::mkfile::cachefile)) << " "; + if (Option::mkfile::do_cache) { + if (!project->confFile().isEmpty()) + t << escapeDependencyPath(fileFixify(project->confFile())) << " "; + if (!project->cacheFile().isEmpty()) + t << escapeDependencyPath(fileFixify(project->cacheFile())) << " "; + } if(!specdir().isEmpty()) { if(exists(Option::fixPathToLocalOS(specdir()+QDir::separator()+"qmake.conf"))) t << escapeDependencyPath(specdir() + Option::dir_sep + "qmake.conf") << " "; @@ -2718,11 +2730,18 @@ MakefileGenerator::writeMakeQmake(QTextStream &t) } } if(project->first("QMAKE_ORIG_TARGET") != "qmake") { - t << "qmake: " << - project->values("QMAKE_INTERNAL_QMAKE_DEPS").join(" \\\n\t\t"); + t << "qmake:"; if(project->isEmpty("QMAKE_NOFORCE")) t << " FORCE"; t << "\n\t" << "@" << qmake << endl << endl; + if (!noDummyQmakeAll) { + t << "qmake_all:"; + if (project->isEmpty("QMAKE_NOFORCE")) + t << " FORCE"; + if (project->isActiveConfig("no_empty_targets")) + t << "\n\t" << "@cd ."; + t << endl << endl; + } } } } @@ -3046,13 +3065,14 @@ QStringList } QString -MakefileGenerator::specdir(const QString &outdir) +MakefileGenerator::specdir(const QString &outdir, int host_build) { #if 0 if(!spec.isEmpty()) return spec; #endif - spec = fileFixify(Option::mkfile::qmakespec, outdir); + spec = fileFixify((host_build >= 0 ? bool(host_build) : project->isHostBuild()) + ? Option::mkfile::qmakespec : Option::mkfile::xqmakespec, outdir); return spec; } @@ -3142,7 +3162,7 @@ MakefileGenerator::pkgConfigPrefix() const { if(!project->isEmpty("QMAKE_PKGCONFIG_PREFIX")) return project->first("QMAKE_PKGCONFIG_PREFIX"); - return QLibraryInfo::rawLocation(QLibraryInfo::PrefixPath); + return QLibraryInfo::rawLocation(QLibraryInfo::PrefixPath, QLibraryInfo::FinalPaths); } QString diff --git a/qmake/generators/makefile.h b/qmake/generators/makefile.h index 5b64ea68fb..33602dcf80 100644 --- a/qmake/generators/makefile.h +++ b/qmake/generators/makefile.h @@ -81,6 +81,7 @@ class MakefileGenerator : protected QMakeSourceFileInfo QString spec; bool init_opath_already, init_already, no_io; QHash<QString, bool> init_compiler_already; + QString chkdir, chkfile, chkglue; QString build_args(const QString &outdir=QString()); void checkMultipleDefinition(const QString &, const QString &); @@ -97,7 +98,7 @@ protected: void writeInstalls(QTextStream &t, const QString &installs, bool noBuild=false); void writeHeader(QTextStream &t); void writeSubDirs(QTextStream &t); - void writeMakeQmake(QTextStream &t); + void writeMakeQmake(QTextStream &t, bool noDummyQmakeAll = false); void writeExtraVariables(QTextStream &t); void writeExtraTargets(QTextStream &t); void writeExtraCompilerTargets(QTextStream &t); @@ -127,6 +128,9 @@ protected: SubTargetsNoFlags=0x00 }; QList<MakefileGenerator::SubTarget*> findSubDirsSubTargets() const; + void writeSubTargetCall(QTextStream &t, + const QString &in_directory, const QString &in, const QString &out_directory, const QString &out, + const QString &out_directory_cdin, const QString &makefilein, const QString &out_directory_cdout); virtual void writeSubMakeCall(QTextStream &t, const QString &outDirectory_cdin, const QString &makeFileIn, const QString &outDirectory_cdout); void writeSubTargets(QTextStream &t, QList<SubTarget*> subtargets, int flags); @@ -187,7 +191,7 @@ protected: //subclasses can use these to query information about how the generator was "run" QString buildArgs(const QString &outdir=QString()); - QString specdir(const QString &outdir=QString()); + QString specdir(const QString &outdir = QString(), int host_build = -1); virtual QStringList &findDependencies(const QString &file); virtual bool doDepends() const { return Option::mkfile::do_deps; } diff --git a/qmake/generators/metamakefile.cpp b/qmake/generators/metamakefile.cpp index c69d5a5812..a1ab4ada75 100644 --- a/qmake/generators/metamakefile.cpp +++ b/qmake/generators/metamakefile.cpp @@ -300,6 +300,8 @@ SubdirsMetaMakefileGenerator::init() bool recurse = Option::recursive == Option::QMAKE_RECURSIVE_YES || (Option::recursive == Option::QMAKE_RECURSIVE_DEFAULT && project->isRecursive()); + if (recurse && project->isActiveConfig("dont_recurse")) + recurse = false; if(recurse) { QString old_output_dir = Option::output_dir; QString old_output = Option::output.fileName(); @@ -336,7 +338,7 @@ SubdirsMetaMakefileGenerator::init() printf(" "); sub->input_dir = subdir.absolutePath(); if(subdir.isRelative() && old_output_dir != oldpwd) { - sub->output_dir = old_output_dir + "/" + subdir.path(); + sub->output_dir = old_output_dir + (subdir.path() != "." ? "/" + subdir.path() : QString()); 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; diff --git a/qmake/generators/unix/unixmake.cpp b/qmake/generators/unix/unixmake.cpp index 856bf371a7..b8b4f1bbc5 100644 --- a/qmake/generators/unix/unixmake.cpp +++ b/qmake/generators/unix/unixmake.cpp @@ -103,8 +103,6 @@ UnixMakefileGenerator::init() MakefileGenerator::init(); if(project->isEmpty("MAKEFILE")) project->values("MAKEFILE").append("Makefile"); - if(project->values("QMAKE_INTERNAL_QMAKE_DEPS").indexOf("qmake_all") == -1) - project->values("QMAKE_INTERNAL_QMAKE_DEPS").append("qmake_all"); return; /* subdirs is done */ } diff --git a/qmake/generators/win32/cesdkhandler.cpp b/qmake/generators/win32/cesdkhandler.cpp new file mode 100644 index 0000000000..de6a55112b --- /dev/null +++ b/qmake/generators/win32/cesdkhandler.cpp @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the tools applications 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 "cesdkhandler.h" + +#include <QtCore/QFile> +#include <QtCore/QDebug> +#include <QtCore/QXmlStreamReader> + +QT_BEGIN_NAMESPACE + +CeSdkInfo::CeSdkInfo() : m_major(0) , m_minor(0) +{ +} + +CeSdkHandler::CeSdkHandler() +{ +} + +bool CeSdkHandler::parse() +{ + // look at the file at %VCInstallDir%/vcpackages/WCE.VCPlatform.config + // and scan through all installed sdks... + m_list.clear(); + m_vcInstallDir = QString::fromLatin1(qgetenv("VCInstallDir")); + if (m_vcInstallDir.isEmpty()) + return false; + + QDir vStudioDir(m_vcInstallDir); + if (!vStudioDir.cd(QLatin1String("vcpackages"))) + return false; + + QFile configFile(vStudioDir.absoluteFilePath(QLatin1String("WCE.VCPlatform.config"))); + if (!configFile.open(QIODevice::ReadOnly)) + return false; + + QString currentElement; + CeSdkInfo currentItem; + QXmlStreamReader xml(&configFile); + while (!xml.atEnd()) { + xml.readNext(); + if (xml.isStartElement()) { + currentElement = xml.name().toString(); + if (currentElement == QLatin1String("Platform")) { + currentItem = CeSdkInfo(); + } else if (currentElement == QLatin1String("Directories")) { + QXmlStreamAttributes attr = xml.attributes(); + currentItem.m_include = fixPaths(attr.value(QLatin1String("Include")).toString()); + currentItem.m_lib = fixPaths(attr.value(QLatin1String("Library")).toString()); + currentItem.m_bin = fixPaths(attr.value(QLatin1String("Path")).toString()); + } + } else if (xml.isEndElement()) { + if (xml.name().toString() == QLatin1String("Platform")) + m_list.append(currentItem); + } else if (xml.isCharacters() && !xml.isWhitespace()) { + if (currentElement == QLatin1String("PlatformName")) + currentItem.m_name = xml.text().toString(); + else if (currentElement == QLatin1String("OSMajorVersion")) + currentItem.m_major = xml.text().toString().toInt(); + else if (currentElement == QLatin1String("OSMinorVersion")) + currentItem.m_minor = xml.text().toString().toInt(); + } + } + + if (xml.error() && xml.error() != QXmlStreamReader::PrematureEndOfDocumentError) { + qWarning() << "XML ERROR:" << xml.lineNumber() << ": " << xml.errorString(); + return false; + } + + return m_list.size() > 0 ? true : false; +} + +QString CeSdkHandler::fixPaths(QString path) const +{ + QRegExp searchStr(QLatin1String("(\\$\\(\\w+\\))")); + QString fixedString = path; + for (int index = fixedString.indexOf(searchStr, 0); + index >= 0; + index = fixedString.indexOf(searchStr, index)) { + const QString capture = searchStr.cap(0); + fixedString.replace(index, capture.length(), capture.toUpper()); + index += capture.length(); // don't count the zero terminator + fixedString.insert(index, '\\'); // the configuration file lacks a directory separator for env vars + ++index; + } + return fixedString; +} + +QT_END_NAMESPACE diff --git a/qmake/generators/win32/cesdkhandler.h b/qmake/generators/win32/cesdkhandler.h new file mode 100644 index 0000000000..8d35694e5d --- /dev/null +++ b/qmake/generators/win32/cesdkhandler.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the tools applications 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$ +** +****************************************************************************/ + +#ifndef CE_SDK_HANDLER_INCL +#define CE_SDK_HANDLER_INCL + +#include <QStringList> +#include <QDir> + +QT_BEGIN_NAMESPACE + +class CeSdkInfo +{ +public: + CeSdkInfo(); + inline QString name() const { return m_name; } + inline QString binPath() const { return m_bin; } + inline QString includePath() const { return m_include; } + inline QString libPath() const { return m_lib; } + inline bool isValid() const; + inline int majorVersion() const { return m_major; } + inline int minorVersion() const { return m_minor; } + inline bool isSupported() const { return m_major >= 5; } +private: + friend class CeSdkHandler; + QString m_name; + QString m_bin; + QString m_include; + QString m_lib; + int m_major; + int m_minor; +}; + +bool CeSdkInfo::isValid() const +{ + return !m_name.isEmpty() && + !m_bin.isEmpty() && + !m_include.isEmpty() && + !m_lib.isEmpty(); +} + +class CeSdkHandler +{ +public: + CeSdkHandler(); + bool parse(); + inline QList<CeSdkInfo> listAll() const { return m_list; } +private: + inline QString fixPaths(QString path) const; + QList<CeSdkInfo> m_list; + QString m_vcInstallDir; +}; + +QT_END_NAMESPACE + +#endif diff --git a/qmake/generators/win32/msvc_nmake.cpp b/qmake/generators/win32/msvc_nmake.cpp index db6651cb88..adf8883b01 100644 --- a/qmake/generators/win32/msvc_nmake.cpp +++ b/qmake/generators/win32/msvc_nmake.cpp @@ -41,6 +41,7 @@ #include "msvc_nmake.h" #include "option.h" +#include "cesdkhandler.h" #include <qregexp.h> #include <qhash.h> #include <qdir.h> @@ -76,6 +77,30 @@ NmakeMakefileGenerator::writeMakefile(QTextStream &t) if(Option::mkfile::do_stub_makefile) return MakefileGenerator::writeStubMakefile(t); #endif + if (!project->isHostBuild()) { + const QHash<QString, QStringList> &variables = project->variables(); + if (variables.contains("XQMAKESPEC") + && !variables["XQMAKESPEC"].isEmpty() + && variables["XQMAKESPEC"].first().contains("wince", Qt::CaseInsensitive)) { + CeSdkHandler sdkhandler; + sdkhandler.parse(); + const QString sdkName = variables["CE_SDK"].join(" ") + + " (" + variables["CE_ARCH"].join(" ") + ")"; + const QList<CeSdkInfo> sdkList = sdkhandler.listAll(); + CeSdkInfo sdk; + foreach (const CeSdkInfo &info, sdkList) { + if (info.name().compare(sdkName, Qt::CaseInsensitive ) == 0) { + sdk = info; + break; + } + } + if (sdk.isValid()) { + t << "\nINCLUDE = " << sdk.includePath(); + t << "\nLIB = " << sdk.libPath(); + t << "\nPATH = " << sdk.binPath() << "\n"; + } + } + } writeNmakeParts(t); return MakefileGenerator::writeMakefile(t); } diff --git a/qmake/generators/win32/msvc_vcproj.cpp b/qmake/generators/win32/msvc_vcproj.cpp index 951784ec28..b228917692 100644 --- a/qmake/generators/win32/msvc_vcproj.cpp +++ b/qmake/generators/win32/msvc_vcproj.cpp @@ -649,12 +649,15 @@ nextfile: t << _slnProjConfBeg; for(QList<VcsolutionDepend*>::Iterator it = solution_cleanup.begin(); it != solution_cleanup.end(); ++it) { QString platform = is64Bit ? "x64" : "Win32"; + QString xplatform = platform; if (!project->isEmpty("CE_SDK") && !project->isEmpty("CE_ARCH")) - platform = project->values("CE_SDK").join(" ") + " (" + project->first("CE_ARCH") + ")"; - t << "\n\t\t" << (*it)->uuid << QString(_slnProjDbgConfTag1).arg(platform) << platform; - t << "\n\t\t" << (*it)->uuid << QString(_slnProjDbgConfTag2).arg(platform) << platform; - t << "\n\t\t" << (*it)->uuid << QString(_slnProjRelConfTag1).arg(platform) << platform; - t << "\n\t\t" << (*it)->uuid << QString(_slnProjRelConfTag2).arg(platform) << platform; + xplatform = project->values("CE_SDK").join(" ") + " (" + project->first("CE_ARCH") + ")"; + if (!project->isHostBuild()) + platform = xplatform; + t << "\n\t\t" << (*it)->uuid << QString(_slnProjDbgConfTag1).arg(xplatform) << platform; + t << "\n\t\t" << (*it)->uuid << QString(_slnProjDbgConfTag2).arg(xplatform) << platform; + t << "\n\t\t" << (*it)->uuid << QString(_slnProjRelConfTag1).arg(xplatform) << platform; + t << "\n\t\t" << (*it)->uuid << QString(_slnProjRelConfTag2).arg(xplatform) << platform; } t << _slnProjConfEnd; t << _slnExtSections; @@ -858,7 +861,7 @@ void VcprojGenerator::initProject() } vcProject.Keyword = project->first("VCPROJ_KEYWORD"); - if (project->isEmpty("CE_SDK") || project->isEmpty("CE_ARCH")) { + if (project->isHostBuild() || project->isEmpty("CE_SDK") || project->isEmpty("CE_ARCH")) { vcProject.PlatformName = (is64Bit ? "x64" : "Win32"); } else { vcProject.PlatformName = project->values("CE_SDK").join(" ") + " (" + project->first("CE_ARCH") + ")"; @@ -922,7 +925,7 @@ void VcprojGenerator::initConfiguration() if (conf.Name.isEmpty()) conf.Name = isDebug ? "Debug" : "Release"; conf.ConfigurationName = conf.Name; - if (project->isEmpty("CE_SDK") || project->isEmpty("CE_ARCH")) { + if (project->isHostBuild() || project->isEmpty("CE_SDK") || project->isEmpty("CE_ARCH")) { conf.Name += (is64Bit ? "|x64" : "|Win32"); } else { conf.Name += "|" + project->values("CE_SDK").join(" ") + " (" + project->first("CE_ARCH") + ")"; @@ -948,7 +951,7 @@ void VcprojGenerator::initConfiguration() initPreBuildEventTools(); initPostBuildEventTools(); // Only deploy for CE projects - if (!project->isEmpty("CE_SDK") && !project->isEmpty("CE_ARCH")) + if (!project->isHostBuild() && !project->isEmpty("CE_SDK") && !project->isEmpty("CE_ARCH")) initDeploymentTool(); initPreLinkEventTools(); @@ -1084,7 +1087,7 @@ void VcprojGenerator::initPostBuildEventTools() QString signature = !project->isEmpty("SIGNATURE_FILE") ? var("SIGNATURE_FILE") : var("DEFAULT_SIGNATURE"); bool useSignature = !signature.isEmpty() && !project->isActiveConfig("staticlib") && - !project->isEmpty("CE_SDK") && !project->isEmpty("CE_ARCH"); + !project->isHostBuild() && !project->isEmpty("CE_SDK") && !project->isEmpty("CE_ARCH"); if (useSignature) { conf.postBuild.CommandLine.prepend( QLatin1String("signtool sign /F ") + signature + QLatin1String(" \"$(TargetPath)\"")); @@ -1574,21 +1577,6 @@ QString VcprojGenerator::fixFilename(QString ofile) const return ofile; } -QString VcprojGenerator::findTemplate(QString file) -{ - QString ret; - if(!exists((ret = file)) && - !exists((ret = QString(Option::mkfile::qmakespec + "/" + file))) && - !exists((ret = QString(QLibraryInfo::location(QLibraryInfo::HostDataPath) + "/win32-msvc.net/" + file))) && - !exists((ret = QString(QLibraryInfo::location(QLibraryInfo::HostDataPath) + "/win32-msvc2002/" + file))) && - !exists((ret = QString(QLibraryInfo::location(QLibraryInfo::HostDataPath) + "/win32-msvc2003/" + file))) && - !exists((ret = QString(QLibraryInfo::location(QLibraryInfo::HostDataPath) + "/win32-msvc2005/" + file))) && - !exists((ret = QString(QLibraryInfo::location(QLibraryInfo::HostDataPath) + "/win32-msvc2008/" + file)))) - return ""; - debug_msg(1, "Generator: MSVC.NET: Found template \'%s\'", ret.toLatin1().constData()); - return ret; -} - void VcprojGenerator::outputVariables() { #if 0 diff --git a/qmake/generators/win32/msvc_vcproj.h b/qmake/generators/win32/msvc_vcproj.h index 30b149f8f5..416e233614 100644 --- a/qmake/generators/win32/msvc_vcproj.h +++ b/qmake/generators/win32/msvc_vcproj.h @@ -63,7 +63,6 @@ class VcprojGenerator : public Win32MakefileGenerator bool writeMakefile(QTextStream &); bool writeProjectMakefile(); - QString findTemplate(QString file); void init(); public: diff --git a/qmake/main.cpp b/qmake/main.cpp index 985afaa8e8..45672c67ee 100644 --- a/qmake/main.cpp +++ b/qmake/main.cpp @@ -163,10 +163,7 @@ int runQMake(int argc, char **argv) fn = fn.right(fn.length() - di - 1); } - if (!Option::prepareProject(fn)) { - exit_val = 3; - break; - } + Option::prepareProject(fn); // read project.. if(!project.read(fn)) { diff --git a/qmake/option.cpp b/qmake/option.cpp index b2a1e6982f..6eeddfd154 100644 --- a/qmake/option.cpp +++ b/qmake/option.cpp @@ -54,7 +54,6 @@ QT_BEGIN_NAMESPACE //convenience const char *Option::application_argv0 = 0; QString Option::prf_ext; -QString Option::js_ext; QString Option::prl_ext; QString Option::libtool_ext; QString Option::pkgcfg_ext; @@ -68,7 +67,6 @@ QString Option::obj_ext; QString Option::lex_ext; QString Option::yacc_ext; QString Option::pro_ext; -QString Option::mmp_ext; QString Option::dir_sep; QString Option::dirlist_sep; QString Option::h_moc_mod; @@ -107,6 +105,7 @@ QStringList Option::projfile::project_dirs; //QMAKE_GENERATE_MAKEFILE stuff QString Option::mkfile::qmakespec; +QString Option::mkfile::xqmakespec; int Option::mkfile::cachefile_depth = -1; bool Option::mkfile::do_deps = true; bool Option::mkfile::do_mocs = true; @@ -114,11 +113,12 @@ bool Option::mkfile::do_dep_heuristics = true; bool Option::mkfile::do_preprocess = false; bool Option::mkfile::do_stub_makefile = false; bool Option::mkfile::do_cache = true; -QString Option::mkfile::project_root; -QString Option::mkfile::project_build_root; +QString Option::mkfile::source_root; +QString Option::mkfile::build_root; QString Option::mkfile::cachefile; QStringList Option::mkfile::project_files; QString Option::mkfile::qmakespec_commandline; +QString Option::mkfile::xqmakespec_commandline; static Option::QMAKE_MODE default_mode(QString progname) { @@ -329,6 +329,9 @@ Option::parseCommandLine(int argc, char **argv, int skip) } else if(opt == "platform" || opt == "spec") { Option::mkfile::qmakespec = cleanSpec(argv[++x]); Option::mkfile::qmakespec_commandline = argv[x]; + } else if (opt == "xplatform" || opt == "xspec") { + Option::mkfile::xqmakespec = cleanSpec(argv[++x]); + Option::mkfile::xqmakespec_commandline = argv[x]; } else { fprintf(stderr, "***Unknown option -%s\n", opt.toLatin1().constData()); return Option::QMAKE_CMDLINE_SHOW_USAGE | Option::QMAKE_CMDLINE_ERROR; @@ -383,6 +386,9 @@ Option::parseCommandLine(int argc, char **argv, int skip) if (!user_configs.isEmpty()) Option::before_user_vars += "CONFIG += " + user_configs.join(" "); + if (Option::mkfile::xqmakespec.isEmpty()) + Option::mkfile::xqmakespec = Option::mkfile::qmakespec; + return Option::QMAKE_CMDLINE_SUCCESS; } @@ -414,7 +420,6 @@ Option::init(int argc, char **argv) Option::libtool_ext = ".la"; Option::pkgcfg_ext = ".pc"; Option::prf_ext = ".prf"; - Option::js_ext = ".js"; Option::ui_ext = ".ui"; Option::h_ext << ".h" << ".hpp" << ".hh" << ".hxx"; Option::c_ext << ".c"; @@ -430,7 +435,6 @@ Option::init(int argc, char **argv) Option::lex_ext = ".l"; Option::yacc_ext = ".y"; Option::pro_ext = ".pro"; - Option::mmp_ext = ".mmp"; #ifdef Q_OS_WIN Option::dirlist_sep = ";"; Option::shellPath = detectShellPath(); @@ -538,8 +542,13 @@ Option::init(int argc, char **argv) //last chance for defaults if(Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE || Option::qmake_mode == Option::QMAKE_GENERATE_PRL) { - if(Option::mkfile::qmakespec.isNull() || Option::mkfile::qmakespec.isEmpty()) + if (Option::mkfile::xqmakespec.isEmpty()) + Option::mkfile::xqmakespec = QString::fromLocal8Bit(qgetenv("XQMAKESPEC").constData()); + if (Option::mkfile::qmakespec.isEmpty()) { Option::mkfile::qmakespec = QString::fromLocal8Bit(qgetenv("QMAKESPEC").constData()); + if (Option::mkfile::xqmakespec.isEmpty()) + Option::mkfile::xqmakespec = Option::mkfile::qmakespec; + } //try REALLY hard to do it for them, lazy.. if(Option::mkfile::project_files.isEmpty()) { @@ -583,106 +592,27 @@ void Option::applyHostMode() } } -QStringList Option::mkspecPaths() -{ - QStringList ret; - const QString concat = QLatin1String("/mkspecs"); - - QByteArray qmakepath = qgetenv("QMAKEPATH"); - if (!qmakepath.isEmpty()) { - const QStringList lst = splitPathList(QString::fromLocal8Bit(qmakepath)); - for (QStringList::ConstIterator it = lst.begin(); it != lst.end(); ++it) - ret << ((*it) + concat); - } - if (!Option::mkfile::project_build_root.isEmpty()) - ret << Option::mkfile::project_build_root + concat; - if (!Option::mkfile::project_root.isEmpty()) - ret << Option::mkfile::project_root + concat; - ret << QLibraryInfo::location(QLibraryInfo::HostDataPath) + concat; - ret.removeDuplicates(); - return ret; -} - -bool Option::resolveSpec(QString *spec) +void Option::prepareProject(const QString &pfile) { - QString qmakespec = fixEnvVariables(*spec); - if (qmakespec.isEmpty()) - qmakespec = "default"; - if (QDir::isRelativePath(qmakespec)) { - QStringList mkspec_roots = mkspecPaths(); - debug_msg(2, "Looking for mkspec %s in (%s)", qmakespec.toLatin1().constData(), - mkspec_roots.join("::").toLatin1().constData()); - for (QStringList::ConstIterator it = mkspec_roots.begin(); it != mkspec_roots.end(); ++it) { - QString mkspec = (*it) + QLatin1Char('/') + qmakespec; - if (QFile::exists(mkspec)) { - *spec = mkspec; - return true; - } - } - 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; - } - return true; -} - -bool Option::prepareProject(const QString &pfile) -{ - mkfile::project_build_root.clear(); - if (mkfile::do_cache) { - if (mkfile::cachefile.isEmpty()) { //find it as it has not been specified - QDir dir(output_dir); - while (!dir.exists(QLatin1String(".qmake.cache"))) - if (dir.isRoot() || !dir.cdUp()) - goto no_cache; - mkfile::cachefile = dir.filePath(QLatin1String(".qmake.cache")); - mkfile::project_build_root = dir.path(); - } else { - QFileInfo fi(mkfile::cachefile); - mkfile::cachefile = QDir::cleanPath(fi.absoluteFilePath()); - mkfile::project_build_root = QDir::cleanPath(fi.absolutePath()); - } - - if (mkfile::qmakespec.isEmpty()) { - QMakeProject cproj; - if (!cproj.read(mkfile::cachefile, QMakeProject::ReadProFile)) - return false; - mkfile::qmakespec = cproj.first(QLatin1String("QMAKESPEC")); - } - } - no_cache: - QString srcpath = (pfile != "-") ? QDir::cleanPath(QFileInfo(pfile).absolutePath()) : qmake_getpwd(); - if (srcpath != output_dir || mkfile::project_build_root.isEmpty()) { - QDir srcdir(srcpath); - QDir dstdir(output_dir); - do { - if (!mkfile::project_build_root.isEmpty()) { - // If we already know the build root, just match up the source root with it. - if (dstdir.path() == mkfile::project_build_root) { - mkfile::project_root = srcdir.path(); - break; - } - } else { - // Look for mkspecs/ in source and build. First to win determines the root. - if (dstdir.exists("mkspecs") || srcdir.exists("mkspecs")) { - mkfile::project_build_root = dstdir.path(); - mkfile::project_root = srcdir.path(); - if (mkfile::project_root == mkfile::project_build_root) - mkfile::project_root.clear(); - break; - } - } - } while (!srcdir.isRoot() && srcdir.cdUp() && !dstdir.isRoot() && dstdir.cdUp()); + if (srcpath != output_dir) { + if (!srcpath.endsWith(QLatin1Char('/'))) + srcpath += QLatin1Char('/'); + QString dstpath = output_dir; + if (!dstpath.endsWith(QLatin1Char('/'))) + dstpath += QLatin1Char('/'); + int srcLen = srcpath.length(); + int dstLen = dstpath.length(); + int lastSl = 0; + while (++lastSl, srcpath.at(--srcLen) == dstpath.at(--dstLen)) + if (srcpath.at(srcLen) == QLatin1Char('/')) + lastSl = 1; + mkfile::source_root = srcpath.left(srcLen + lastSl); + mkfile::build_root = dstpath.left(dstLen + lastSl); } else { - mkfile::project_root.clear(); + mkfile::source_root.clear(); } - - if (!resolveSpec(&Option::mkfile::qmakespec)) - return false; - - return true; } bool Option::postProcessProject(QMakeProject *project) diff --git a/qmake/option.h b/qmake/option.h index 23384877fd..7b86d56e89 100644 --- a/qmake/option.h +++ b/qmake/option.h @@ -72,7 +72,6 @@ void warn_msg(QMakeWarn t, const char *fmt, ...); struct Option { //simply global convenience - static QString js_ext; static QString libtool_ext; static QString pkgcfg_ext; static QString prf_ext; @@ -94,7 +93,6 @@ struct Option static QString dirlist_sep; static QString sysenv_mod; static QString pro_ext; - static QString mmp_ext; static QString res_ext; static char field_sep; static const char *application_argv0; @@ -109,8 +107,7 @@ struct Option //both of these must be called.. static int init(int argc=0, char **argv=0); //parse cmdline static void applyHostMode(); - static QStringList mkspecPaths(); - static bool prepareProject(const QString &pfile); + static void prepareProject(const QString &pfile); static bool postProcessProject(QMakeProject *); enum StringFixFlags { @@ -198,27 +195,28 @@ struct Option //QMAKE_GENERATE_MAKEFILE options struct mkfile { static QString qmakespec; + static QString xqmakespec; static bool do_cache; static bool do_deps; static bool do_mocs; static bool do_dep_heuristics; static bool do_preprocess; static bool do_stub_makefile; - static QString project_root; - static QString project_build_root; + static QString source_root; + static QString build_root; static QString cachefile; static int cachefile_depth; static QStringList project_files; static QString qmakespec_commandline; + static QString xqmakespec_commandline; }; private: static int parseCommandLine(int, char **, int=0); - static bool resolveSpec(QString *spec); }; inline QString fixEnvVariables(const QString &x) { return Option::fixString(x, Option::FixEnvVars); } -inline QStringList splitPathList(const QString &paths) { return paths.split(Option::dirlist_sep); } +inline QStringList splitPathList(const QString &paths) { return paths.isEmpty() ? QStringList() : paths.split(Option::dirlist_sep); } QT_END_NAMESPACE 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 ¶ms, 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 ¶ms, 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."))) { diff --git a/qmake/project.h b/qmake/project.h index ccdc1b63f1..e733f6097c 100644 --- a/qmake/project.h +++ b/qmake/project.h @@ -79,8 +79,13 @@ class QMakeProject QHash<QString, FunctionBlock*> testFunctions, replaceFunctions; bool recursive; + bool host_build; + bool need_restart; bool own_prop; bool backslashWarned; + QString conffile; + QString superfile; + QString cachefile; QString pfile; QMakeProperty *prop; void reset(); @@ -107,6 +112,7 @@ class QMakeProject bool doVariableReplace(QString &str, QHash<QString, QStringList> &place); QStringList doVariableReplaceExpand(const QString &str, QHash<QString, QStringList> &place, bool *ok=0); void init(QMakeProperty *); + void cleanup(); QStringList &values(const QString &v, QHash<QString, QStringList> &place); void validateModes(); void resolveSpec(QString *spec, const QString &qmakespec); @@ -128,6 +134,8 @@ public: QStringList userTestFunctions() { return testFunctions.keys(); } QString projectFile(); + QString confFile() const { return conffile; } + QString cacheFile() const { return cachefile; } inline QMakeProperty *properties() { return prop; } bool doProjectTest(QString str, QHash<QString, QStringList> &place); @@ -160,6 +168,7 @@ public: QHash<QString, QStringList> &variables(); // No compat mapping and magic, obviously bool isRecursive() const { return recursive; } + bool isHostBuild() const { return host_build; } protected: friend class MakefileGenerator; diff --git a/qmake/property.cpp b/qmake/property.cpp index 1952b5069b..8d2e14ca11 100644 --- a/qmake/property.cpp +++ b/qmake/property.cpp @@ -49,6 +49,8 @@ QT_BEGIN_NAMESPACE +QStringList qmake_mkspec_paths(); //project.cpp + static const struct { const char *name; QLibraryInfo::LibraryLocation loc; @@ -77,7 +79,8 @@ QMakeProperty::QMakeProperty() : settings(0) { for (int i = 0; i < sizeof(propList)/sizeof(propList[0]); i++) { QString name = QString::fromLatin1(propList[i].name); - QString val = QLibraryInfo::rawLocation(propList[i].loc); + m_values[name + "/get"] = QLibraryInfo::rawLocation(propList[i].loc, QLibraryInfo::EffectivePaths); + QString val = QLibraryInfo::rawLocation(propList[i].loc, QLibraryInfo::FinalPaths); if (!propList[i].raw) { m_values[name] = QLibraryInfo::location(propList[i].loc); name += "/raw"; @@ -115,7 +118,7 @@ QMakeProperty::value(QString v, bool just_check) if (!val.isNull()) return val; else if(v == "QMAKE_MKSPECS") - return Option::mkspecPaths().join(Option::dirlist_sep); + return qmake_mkspec_paths().join(Option::dirlist_sep); else if(v == "QMAKE_VERSION") return qmake_version(); #ifdef QT_VERSION_STR @@ -212,9 +215,12 @@ QMakeProperty::exec() foreach (QString prop, specialProps) { QString val = value(prop); QString pval = value(prop + "/raw"); + QString gval = value(prop + "/get"); fprintf(stdout, "%s:%s\n", prop.toLatin1().constData(), val.toLatin1().constData()); if (!pval.isEmpty() && pval != val) fprintf(stdout, "%s/raw:%s\n", prop.toLatin1().constData(), pval.toLatin1().constData()); + if (!gval.isEmpty() && gval != (pval.isEmpty() ? val : pval)) + fprintf(stdout, "%s/get:%s\n", prop.toLatin1().constData(), gval.toLatin1().constData()); } return true; } diff --git a/qmake/qmake.pri b/qmake/qmake.pri index cfa0c1359d..68b78f8d21 100644 --- a/qmake/qmake.pri +++ b/qmake/qmake.pri @@ -14,7 +14,8 @@ SOURCES += project.cpp property.cpp main.cpp generators/makefile.cpp \ generators/win32/msvc_vcproj.cpp \ generators/win32/msvc_vcxproj.cpp \ generators/win32/msvc_objectmodel.cpp generators/win32/msbuild_objectmodel.cpp \ - generators/integrity/gbuild.cpp + generators/integrity/gbuild.cpp \ + generators/win32/cesdkhandler.cpp HEADERS += project.h property.h generators/makefile.h \ generators/unix/unixmake.h meta.h option.h cachekeys.h \ @@ -24,7 +25,8 @@ HEADERS += project.h property.h generators/makefile.h \ generators/win32/msvc_vcproj.h \ generators/win32/msvc_vcxproj.h \ generators/win32/msvc_objectmodel.h generators/win32/msbuild_objectmodel.h \ - generators/integrity/gbuild.h + generators/integrity/gbuild.h \ + generators/win32/cesdkhandler.h contains(QT_EDITION, OpenSource) { DEFINES += QMAKE_OPENSOURCE_EDITION diff --git a/qmake/qmake.pro b/qmake/qmake.pro index 5a5010ff6a..0693d9a4ac 100644 --- a/qmake/qmake.pro +++ b/qmake/qmake.pro @@ -3,6 +3,7 @@ #once you are boot strapped though, the qmake.pro will offer better coverage of a #platform than either of the generic makefiles +option(host_build) CONFIG += console bootstrap CONFIG -= qt shared app_bundle uic DEFINES += QT_BUILD_QMAKE QT_BOOTSTRAPPED |