12#include <qbytearray.h>
19#include <qregularexpression.h>
23#include <qstringlist.h>
24#ifdef PROEVALUATOR_THREAD_SAFE
25# include <qthreadpool.h>
30#include <sys/utsname.h>
32# include <sys/sysctl.h>
35#include <qt_windows.h>
40using namespace QMakeInternal;
44#define fL1S(s) QString::fromLatin1(s)
50#ifdef PROEVALUATOR_THREAD_SAFE
51 return QThread::idealThreadCount();
52#elif defined(Q_OS_WIN)
54 GetSystemInfo(&sysinfo);
55 return sysinfo.dwNumberOfProcessors;
60# if defined(Q_OS_BSD4)
62 size_t len =
sizeof(cores);
66 if (sysctl(mib, 2, &cores, &len, NULL, 0) != 0) {
69# elif defined(_SC_NPROCESSORS_ONLN)
71 cores = (
int)sysconf(_SC_NPROCESSORS_ONLN);
87 return qHash(key.root) ^ qHash(key.stash) ^ (uint)key.hostBuild;
98#ifdef PROEVALUATOR_THREAD_SAFE
114 if (!statics.field_sep.isNull())
117 statics.field_sep = QLatin1String(
" ");
118 statics.strtrue = QLatin1String(
"true");
119 statics.strfalse = QLatin1String(
"false");
120 statics.strCONFIG = ProKey(
"CONFIG");
121 statics.strARGS = ProKey(
"ARGS");
122 statics.strARGC = ProKey(
"ARGC");
123 statics.strDot = QLatin1String(
".");
124 statics.strDotDot = QLatin1String(
"..");
125 statics.strever = QLatin1String(
"ever");
126 statics.strforever = QLatin1String(
"forever");
127 statics.strhost_build = QLatin1String(
"host_build");
128 statics.strTEMPLATE = ProKey(
"TEMPLATE");
129 statics.strQMAKE_PLATFORM = ProKey(
"QMAKE_PLATFORM");
130 statics.strQMAKE_DIR_SEP = ProKey(
"QMAKE_DIR_SEP");
131 statics.strQMAKESPEC = ProKey(
"QMAKESPEC");
132#ifdef PROEVALUATOR_FULL
133 statics.strREQUIRES = ProKey(
"REQUIRES");
136 statics.fakeValue = ProStringList(ProString(
"_FAKE_"));
140 static const struct {
141 const char *
const oldname, *
const newname;
143 {
"INTERFACES",
"FORMS" },
144 {
"QMAKE_POST_BUILD",
"QMAKE_POST_LINK" },
145 {
"TARGETDEPS",
"POST_TARGETDEPS" },
146 {
"LIBPATH",
"QMAKE_LIBDIR" },
147 {
"QMAKE_EXT_MOC",
"QMAKE_EXT_CPP_MOC" },
148 {
"QMAKE_MOD_MOC",
"QMAKE_H_MOD_MOC" },
149 {
"QMAKE_LFLAGS_SHAPP",
"QMAKE_LFLAGS_APP" },
150 {
"PRECOMPH",
"PRECOMPILED_HEADER" },
151 {
"PRECOMPCPP",
"PRECOMPILED_SOURCE" },
152 {
"INCPATH",
"INCLUDEPATH" },
153 {
"QMAKE_EXTRA_WIN_COMPILERS",
"QMAKE_EXTRA_COMPILERS" },
154 {
"QMAKE_EXTRA_UNIX_COMPILERS",
"QMAKE_EXTRA_COMPILERS" },
155 {
"QMAKE_EXTRA_WIN_TARGETS",
"QMAKE_EXTRA_TARGETS" },
156 {
"QMAKE_EXTRA_UNIX_TARGETS",
"QMAKE_EXTRA_TARGETS" },
157 {
"QMAKE_EXTRA_UNIX_INCLUDES",
"QMAKE_EXTRA_INCLUDES" },
158 {
"QMAKE_EXTRA_UNIX_VARIABLES",
"QMAKE_EXTRA_VARIABLES" },
159 {
"QMAKE_RPATH",
"QMAKE_LFLAGS_RPATH" },
160 {
"QMAKE_FRAMEWORKDIR",
"QMAKE_FRAMEWORKPATH" },
161 {
"QMAKE_FRAMEWORKDIR_FLAGS",
"QMAKE_FRAMEWORKPATH_FLAGS" },
163 {
"DEPLOYMENT",
"INSTALLS" }
165 statics.varMap.reserve((
int)(
sizeof(mapInits)/
sizeof(mapInits[0])));
166 for (
unsigned i = 0; i <
sizeof(mapInits)/
sizeof(mapInits[0]); ++i)
167 statics.varMap.insert(ProKey(mapInits[i].oldname), ProKey(mapInits[i].newname));
172 QHash<ProKey, ProKey>::ConstIterator it = statics.varMap.constFind(var);
173 if (it == statics.varMap.constEnd())
175 deprecationWarning(
fL1S(
"Variable %1 is deprecated; use %2 instead.")
176 .arg(var.toQString(), it.value().toQString()));
184#ifdef PROEVALUATOR_DEBUG
194#ifdef PROEVALUATOR_CUMULATIVE
195 m_cumulative =
false;
200#ifdef PROEVALUATOR_CUMULATIVE
215 Q_ASSERT_X(other,
"QMakeEvaluator::visitProFile",
"Project not prepared");
216 m_functionDefs = other->m_functionDefs;
219 m_qmakespec = other->m_qmakespec;
220 m_qmakespecName = other->m_qmakespecName;
221 m_mkspecPaths = other->m_mkspecPaths;
222 m_featureRoots = other->m_featureRoots;
223 m_dirSep = other->m_dirSep;
230 uint len = *tokPtr++;
231 len |= (uint)*tokPtr++ << 16;
237 uint len = *tokPtr++;
244 uint len = *tokPtr++;
258 const QChar *vals_data = vals.data();
259 const int vals_len = vals.size();
261 bool hadWord =
false;
262 for (
int x = 0; x < vals_len; x++) {
263 char16_t unicode = vals_data[x].unicode();
264 if (unicode == quote) {
267 build += QChar(unicode);
290 if (x + 1 != vals_len) {
291 char16_t next = vals_data[++x].unicode();
292 if (next ==
'\'' || next ==
'"' || next ==
'\\') {
293 build += QChar(unicode);
304 build += QChar(unicode);
312 const QRegularExpression ®exp,
const QString &replace,
bool global, QString &tmp)
314 for (ProStringList::Iterator varit = varlist->begin(); varit != varlist->end(); ) {
316 QString val = u1.str();
318 val.replace(regexp, replace);
319 if (!val.isSharedWith(copy) && val != copy) {
321 varit = varlist->erase(varit);
323 (*varit).setValue(val);
340 ret->last().append(str, &pending);
346 ret->last().append(str);
354 if (!list.isEmpty()) {
356 ret->last().append(list, &pending, !(tok &
TokQuoted));
363 ret->last().append(list);
368 if (!list.at(0).isEmpty()) {
375 ret->last().append(list.at(0));
378 for (
int j = 1; j < list.size(); ++j) {
390 debugMsg(2
, joined ?
"evaluating joined expression" :
"evaluating expression");
394 bool pending =
false;
396 ushort tok = *tokPtr++;
397 if (tok & TokNewStr) {
401 ushort maskedTok = tok & TokMask;
404 m_current.line = *tokPtr++;
407 const ProString &val = pro->getStr(tokPtr);
409 addStr(val, ret, pending, joined);
411 case TokHashLiteral: {
412 const ProKey &val = pro->getHashStr(tokPtr);
414 addStr(val, ret, pending, joined);
417 const ProKey &var = pro->getHashStr(tokPtr);
418 const ProStringList &val = values(map(var));
420 addStrList(val, tok, ret, pending, joined);
423 const ProKey &var = pro->getHashStr(tokPtr);
424 const ProString &val = propertyValue(var);
426 addStr(val, ret, pending, joined);
429 const ProString &var = pro->getStr(tokPtr);
430 const ProString &val = ProString(m_option->getEnv(var.toQString()));
432 addStr(val, ret, pending, joined);
435 const ProKey &func = pro->getHashStr(tokPtr);
438 if (evaluateExpandFunction(func, tokPtr, &val) == ReturnError)
440 addStrList(val, tok, ret, pending, joined);
452 const ushort *tokPtr = pTokPtr;
454 ushort tok = *tokPtr++;
457 m_current.line = *tokPtr++;
459 case TokValueTerminator:
460 case TokFuncTerminator:
463 case TokArgSeparator:
466 switch (tok & TokMask) {
479 skipExpression(pTokPtr);
483 Q_ASSERT_X(
false,
"skipExpression",
"Unrecognized token");
491 ProFile *pro,
const ushort *tokPtr)
499 const ushort *tokPtr)
504 bool okey =
true, or_op =
false, invert =
false;
506 while (ushort tok = *tokPtr++) {
525#ifdef PROEVALUATOR_CUMULATIVE
528 ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue;
530 blockLen = getBlockLen(tokPtr);
535 if ((ret == ReturnTrue || ret == ReturnFalse) && blockLen)
536 ret = visitProBlock(tokPtr);
555 okey =
true, or_op =
false;
561 const ushort *exprPtr = tokPtr;
574 okey =
true, or_op =
false;
582 savedValuemapStack, savedValuemapStack.begin());
586 savedValuemapStack.splice(savedValuemapStack.begin(),
594 okey =
true, or_op =
false;
610 okey =
true, or_op =
false;
626 if (curr.size() != 1) {
627 if (!m_cumulative || !curr.isEmpty())
628 evalError(
fL1S(
"Conditional must expand to exactly one word."));
631 okey = isActiveConfig(curr.at(0).toQStringView(),
true);
636 traceMsg("skipped condition %s", curr.size() == 1 ?
dbgStr(curr.at(0)) :
"<invalid>");
644 if (curr.size() != 1) {
645 if (!m_cumulative || !curr.isEmpty())
646 evalError(
fL1S(
"Test name must expand to exactly one word."));
663#ifdef PROEVALUATOR_CUMULATIVE
665 if (curr.size() != 1)
666 skipExpression(tokPtr);
668 evaluateConditionalFunction(curr.at(0).toKey(), tokPtr);
673 traceMsg("skipped test function %s", curr.size() == 1 ?
dbgStr(curr.at(0)) :
"<invalid>");
680 m_returnValue = curr;
695 okey =
false, or_op =
true;
698 const ushort *oTokPtr = --tokPtr;
703 Q_ASSERT_X(
false,
"visitProBlock",
"unexpected item type");
717 ushort tok,
const ProKey &name,
const ushort *tokPtr)
721 ? &m_functionDefs.testFunctions
722 : &m_functionDefs.replaceFunctions);
727 const ProKey &_variable,
const ushort *exprPtr,
const ushort *tokPtr)
730 bool infinite =
false;
739 if (it_list != statics.strever) {
740 evalError(
fL1S(
"Invalid loop expression."));
743 it_list = ProString(statics.strforever);
745 variable =
map(_variable
);
749 if (list.isEmpty()) {
750 if (it_list == statics.strforever) {
753 traceMsg("skipping forever loop in cumulative mode");
758 QStringView itl = it_list.toQStringView();
759 int dotdot = itl.indexOf(statics.strDotDot);
762 int start = itl.left(dotdot).toInt(&ok);
764 int end = itl.mid(dotdot+2).toInt(&ok);
766 const int absDiff = qAbs(end - start);
770 traceMsg("skipping excessive loop in cumulative mode");
773 list.reserve(absDiff + 1);
775 for (
int i = start; i <= end; i++)
776 list << ProString(QString::number(i));
778 for (
int i = start; i >= end; i--)
779 list << ProString(QString::number(i));
794 if (!variable.isEmpty())
795 m_valuemapStack.top()[variable] = ProStringList(ProString(QString::number(index)));
796 if (++index > 1000) {
797 evalError(
fL1S(
"Ran into infinite loop (> 1000 iterations)."));
800 traceMsg(
"loop iteration %d", index);
804 if (index >= list.size())
806 val = list.at(index++);
807 }
while (val.isEmpty());
809 m_valuemapStack.top()[variable] = ProStringList(val);
812 ret = visitProBlock(tokPtr);
831 if (!variable.isEmpty())
832 m_valuemapStack.top()[variable] = oldVarVal;
837 ushort tok,
const ProStringList &curr,
const ushort *&tokPtr)
839 int sizeHint = *tokPtr++;
841 if (curr.size() != 1) {
843 if (!m_cumulative || !curr.isEmpty())
844 evalError(
fL1S(
"Left hand side of assignment must expand to exactly one word."));
847 const ProKey &varName = map(curr.first());
855 QStringView val = varVal.at(0).toQStringView();
856 if (val.size() < 4 || val.at(0) != QLatin1Char(
's')) {
857 evalError(
fL1S(
"The ~= operator can handle only the s/// function."));
860 QChar sep = val.at(1);
861 auto func = val.split(sep, Qt::KeepEmptyParts);
862 if (func.size() < 3 || func.size() > 4) {
863 evalError(
fL1S(
"The s/// function expects 3 or 4 arguments."));
867 bool global =
false, quote =
false, case_sense =
false;
868 if (func.size() == 4) {
869 global = func[3].indexOf(QLatin1Char(
'g')) != -1;
870 case_sense = func[3].indexOf(QLatin1Char(
'i')) == -1;
871 quote = func[3].indexOf(QLatin1Char(
'q')) != -1;
873 QString pattern = func[1].toString();
874 QString replace = func[2].toString();
876 pattern = QRegularExpression::escape(pattern);
878 QRegularExpression regexp(pattern, case_sense ? QRegularExpression::NoPatternOption :
879 QRegularExpression::CaseInsensitiveOption);
883 replaceInList(&valuesRef(varName), regexp, replace, global, m_tmp2);
895 m_valuemapStack.top()[varName] = varVal;
919 if (varName == statics.strTEMPLATE)
921 else if (varName == statics.strQMAKE_PLATFORM)
922 m_featureRoots =
nullptr;
923 else if (varName == statics.strQMAKE_DIR_SEP)
924 m_dirSep = first(varName);
925 else if (varName == statics.strQMAKESPEC) {
927 QString spec = values(varName).first().toQString();
928 if (IoUtils::isAbsolutePath(spec)) {
930 m_qmakespecName = IoUtils::fileName(m_qmakespec).toString();
931 m_featureRoots =
nullptr;
935#ifdef PROEVALUATOR_FULL
936 else if (varName == statics.strREQUIRES)
937 return checkRequirements(values(varName));
946 if (!
m_option->user_template.isEmpty()) {
950 if (values.isEmpty())
953 values.erase(values.begin() + 1, values.end());
955 if (!
m_option->user_template_prefix.isEmpty()) {
957 if (!val.startsWith(
m_option->user_template_prefix))
962#if defined(Q_CC_MSVC)
963static ProString msvcBinDirToQMakeArch(QString subdir)
965 int idx = subdir.indexOf(QLatin1Char(
'\\'));
967 return ProString(
"x86");
968 subdir.remove(0, idx + 1);
969 idx = subdir.indexOf(QLatin1Char(
'_'));
971 subdir.remove(0, idx + 1);
972 subdir = subdir.toLower();
973 if (subdir == QLatin1String(
"amd64"))
974 return ProString(
"x86_64");
976 idx = subdir.indexOf(QLatin1Char(
'\\'));
978 return ProString(
"x86");
979 subdir.remove(0, idx + 1);
980 if (subdir == QLatin1String(
"x64"))
981 return ProString(
"x86_64");
982 return ProString(subdir);
985static ProString defaultMsvcArchitecture()
987#if defined(Q_OS_WIN64)
988 return ProString(
"x86_64");
990 return ProString(
"x86");
994static ProString msvcArchitecture(
const QString &vcInstallDir,
const QString &pathVar)
996 if (vcInstallDir.isEmpty())
997 return defaultMsvcArchitecture();
998 QString vcBinDir = vcInstallDir;
999 if (vcBinDir.endsWith(QLatin1Char(
'\\')))
1001 const auto dirs = pathVar.split(QLatin1Char(
';'), Qt::SkipEmptyParts);
1002 for (
const QString &dir : dirs) {
1003 if (!dir.startsWith(vcBinDir, Qt::CaseInsensitive))
1005 const ProString arch = msvcBinDirToQMakeArch(dir.mid(vcBinDir.length() + 1));
1006 if (!arch.isEmpty())
1009 return defaultMsvcArchitecture();
1017 vars[ProKey(
"DIR_SEPARATOR")] << ProString(m_option->dir_sep);
1018 vars[ProKey(
"DIRLIST_SEPARATOR")] << ProString(m_option->dirlist_sep);
1019 vars[ProKey(
"_DATE_")] << ProString(QDateTime::currentDateTime().toString());
1020 if (!m_option->qmake_abslocation.isEmpty())
1021 vars[ProKey(
"QMAKE_QMAKE")] << ProString(m_option->qmake_abslocation);
1022 if (!m_option->qmake_args.isEmpty())
1023 vars[ProKey(
"QMAKE_ARGS")] = ProStringList(m_option->qmake_args);
1024 if (!m_option->qtconf.isEmpty())
1025 vars[ProKey(
"QMAKE_QTCONF")] = ProString(m_option->qtconf);
1026 vars[ProKey(
"QMAKE_HOST.cpu_count")] = ProString(QString::number(idealThreadCount()));
1027#if defined(Q_OS_WIN32)
1028 vars[ProKey(
"QMAKE_HOST.os")] << ProString(
"Windows");
1030 DWORD name_length = 1024;
1032 if (GetComputerName(name, &name_length))
1033 vars[ProKey(
"QMAKE_HOST.name")] << ProString(QString::fromWCharArray(name));
1035 vars[ProKey(
"QMAKE_HOST.version")] << ProString(QSysInfo::kernelVersion());
1036 vars[ProKey(
"QMAKE_HOST.version_string")] << ProString(QSysInfo::productVersion());
1039 GetSystemInfo(&info);
1041 switch (info.wProcessorArchitecture) {
1042# ifdef PROCESSOR_ARCHITECTURE_AMD64
1043 case PROCESSOR_ARCHITECTURE_AMD64:
1044 archStr = ProString(
"x86_64");
1047 case PROCESSOR_ARCHITECTURE_INTEL:
1048 archStr = ProString(
"x86");
1050 case PROCESSOR_ARCHITECTURE_IA64:
1051# ifdef PROCESSOR_ARCHITECTURE_IA32_ON_WIN64
1052 case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64:
1054 archStr = ProString(
"IA64");
1057 archStr = ProString(
"Unknown");
1060 vars[ProKey(
"QMAKE_HOST.arch")] << archStr;
1062# if defined(Q_CC_MSVC)
1064 QString vcInstallDir = m_option->getEnv(QLatin1String(
"VCToolsInstallDir"));
1065 if (vcInstallDir.isEmpty())
1066 vcInstallDir = m_option->getEnv(QLatin1String(
"VCINSTALLDIR"));
1067 vars[ProKey(
"QMAKE_TARGET.arch")] = msvcArchitecture(
1069 m_option->getEnv(QLatin1String(
"PATH")));
1071#elif defined(Q_OS_UNIX)
1072 struct utsname name;
1073 if (uname(&name) != -1) {
1074 vars[ProKey(
"QMAKE_HOST.os")] << ProString(name.sysname);
1075 vars[ProKey(
"QMAKE_HOST.name")] << ProString(QString::fromLocal8Bit(name.nodename));
1076 vars[ProKey(
"QMAKE_HOST.version")] << ProString(name.release);
1077 vars[ProKey(
"QMAKE_HOST.version_string")] << ProString(name.version);
1078 vars[ProKey(
"QMAKE_HOST.arch")] << ProString(name.machine);
1087 QMakeVfs::VfsFlags flags = (m_cumulative ? QMakeVfs::VfsCumulative : QMakeVfs::VfsExact);
1091 QString cachefile = m_option->cachefile;
1092 if (cachefile.isEmpty()) {
1093 if (m_outputDir.isEmpty())
1095 superdir = m_outputDir;
1097 QString superfile = superdir + QLatin1String(
"/.qmake.super");
1098 if (m_vfs->exists(superfile, flags)) {
1099 m_superfile = QDir::cleanPath(superfile);
1102 QFileInfo qdfi(superdir);
1103 if (qdfi.isRoot()) {
1107 superdir = qdfi.path();
1109 QString sdir = inDir;
1110 QString dir = m_outputDir;
1112 conffile = sdir + QLatin1String(
"/.qmake.conf");
1113 if (!m_vfs->exists(conffile, flags))
1115 cachefile = dir + QLatin1String(
"/.qmake.cache");
1116 if (!m_vfs->exists(cachefile, flags))
1118 if (!conffile.isEmpty() || !cachefile.isEmpty()) {
1120 m_sourceRoot = sdir;
1124 if (dir == superdir)
1126 QFileInfo qsdfi(sdir);
1127 QFileInfo qdfi(dir);
1128 if (qsdfi.isRoot() || qdfi.isRoot())
1130 sdir = qsdfi.path();
1134 m_buildRoot = QFileInfo(cachefile).path();
1136 m_conffile = QDir::cleanPath(conffile);
1137 m_cachefile = QDir::cleanPath(cachefile);
1141 QString dir = m_outputDir;
1143 QString stashfile = dir + QLatin1String(
"/.qmake.stash");
1144 if (dir == (!superdir.isEmpty() ? superdir : m_buildRoot) || m_vfs->exists(stashfile, flags)) {
1145 m_stashfile = QDir::cleanPath(stashfile);
1148 QFileInfo qdfi(dir);
1161 QString spec = m_qmakespec + QLatin1String(
"/qmake.conf");
1162 if (evaluateFile(spec, QMakeHandler::EvalConfigFile, LoadProOnly) != ReturnTrue) {
1163 evalError(
fL1S(
"Could not read qmake configuration file %1.").arg(spec));
1166#ifndef QT_BUILD_QMAKE
1169 if (m_qmakespec.endsWith(QLatin1String(
"/default-host"))
1170 || m_qmakespec.endsWith(QLatin1String(
"/default"))) {
1171 QString rspec = QFileInfo(m_qmakespec).symLinkTarget();
1172 if (!rspec.isEmpty())
1173 m_qmakespec = QDir::cleanPath(QDir(m_qmakespec).absoluteFilePath(rspec));
1181 QString spec = orig_spec.toQString();
1182 if (IoUtils::isAbsolutePath(spec))
1187 valuesRef(ProKey(
"QMAKESPEC")) = ProString(m_qmakespec);
1188 m_qmakespecName = IoUtils::fileName(m_qmakespec).toString();
1197 QString qmakespec = m_option->expandEnvVars(
1198 m_hostBuild ? m_option->qmakespec : m_option->xqmakespec);
1202 evaluator.m_sourceRoot = m_sourceRoot;
1203 evaluator.m_buildRoot = m_buildRoot;
1205 if (!m_superfile.isEmpty() && evaluator.evaluateFile(
1206 m_superfile, QMakeHandler::EvalConfigFile, LoadProOnly|LoadHidden) != ReturnTrue) {
1209 if (!m_conffile.isEmpty() && evaluator.evaluateFile(
1210 m_conffile, QMakeHandler::EvalConfigFile, LoadProOnly|LoadHidden) != ReturnTrue) {
1213 if (!m_cachefile.isEmpty() && evaluator.evaluateFile(
1214 m_cachefile, QMakeHandler::EvalConfigFile, LoadProOnly|LoadHidden) != ReturnTrue) {
1217 if (qmakespec.isEmpty()) {
1220 if (qmakespec.isEmpty())
1223 m_qmakepath = evaluator.values(ProKey(
"QMAKEPATH")).toQStringList();
1224 m_qmakefeatures = evaluator.values(ProKey(
"QMAKEFEATURES")).toQStringList();
1228 if (qmakespec.isEmpty())
1230#ifndef QT_BUILD_QMAKE
1232 if (qmakespec.isEmpty())
1233 qmakespec =
m_hostBuild ? QLatin1String(
"default-host") : QLatin1String(
"default");
1235 if (IoUtils::isRelativePath(qmakespec)) {
1236 for (
const QString &root : std::as_const(m_mkspecPaths)) {
1237 QString mkspec = root + QLatin1Char(
'/') + qmakespec;
1238 if (IoUtils::exists(mkspec)) {
1243 evalError(
fL1S(
"Could not find qmake spec '%1'.").arg(qmakespec));
1247 m_qmakespec = QDir::cleanPath(qmakespec);
1249 if (!m_superfile.isEmpty()) {
1250 valuesRef(ProKey(
"_QMAKE_SUPER_CACHE_")) << ProString(m_superfile);
1252 m_superfile, QMakeHandler::EvalConfigFile, LoadProOnly|LoadHidden) != ReturnTrue)
1257 if (!m_conffile.isEmpty()) {
1258 valuesRef(ProKey(
"_QMAKE_CONF_")) << ProString(m_conffile);
1260 m_conffile, QMakeHandler::EvalConfigFile, LoadProOnly) != ReturnTrue)
1263 if (!m_cachefile.isEmpty()) {
1264 valuesRef(ProKey(
"_QMAKE_CACHE_")) << ProString(m_cachefile);
1266 m_cachefile, QMakeHandler::EvalConfigFile, LoadProOnly) != ReturnTrue)
1269 QMakeVfs::VfsFlags flags = (m_cumulative ? QMakeVfs::VfsCumulative : QMakeVfs::VfsExact);
1270 if (!m_stashfile.isEmpty() && m_vfs->exists(m_stashfile, flags)) {
1271 valuesRef(ProKey(
"_QMAKE_STASH_")) << ProString(m_stashfile);
1273 m_stashfile, QMakeHandler::EvalConfigFile, LoadProOnly) != ReturnTrue)
1284 vars[ProKey(
"TARGET")] << ProString(QFileInfo(currentFileName()).baseName()).setSource(proFile);
1285 vars[ProKey(
"_PRO_FILE_")] << ProString(currentFileName()).setSource(proFile);
1286 vars[ProKey(
"_PRO_FILE_PWD_")] << ProString(currentDirectory()).setSource(proFile);
1287 vars[ProKey(
"OUT_PWD")] << ProString(m_outputDir).setSource(proFile);
1292 if (!cmds.isEmpty()) {
1293 ProFile *pro =
m_parser->parsedProBlock(QStringView(cmds), 0, where, -1);
1295 m_locationStack.push(m_current);
1297 m_current = m_locationStack.pop();
1305 if (m_extraConfigs.isEmpty())
1308 evaluateCommand(
fL1S(
"CONFIG += ") + m_extraConfigs.join(QLatin1Char(
' ')),
fL1S(
"(extra configs)"));
1313 QSet<QString> processed;
1315 bool finished =
true;
1316 ProStringList configs = values(statics.strCONFIG);
1317 for (
int i = configs.size() - 1; i >= 0; --i) {
1318 ProStringRoUser u1(configs.at(i), m_tmp1);
1319 QString config = u1.str().toLower();
1320 if (!processed.contains(config)) {
1322 processed.insert(config);
1323 VisitReturn vr = evaluateFeatureFile(config,
true);
1324 if (vr == ReturnError && !m_cumulative)
1326 if (vr == ReturnTrue) {
1350#ifdef PROEVALUATOR_THREAD_SAFE
1351 m_option->mutex.lock();
1353 QMakeBaseEnv **baseEnvPtr = &m_option->baseEnvs[QMakeBaseKey(m_buildRoot, m_stashfile, m_hostBuild)];
1358#ifdef PROEVALUATOR_THREAD_SAFE
1359 QMutexLocker locker(&baseEnv->mutex);
1360 m_option->mutex.unlock();
1361 if (baseEnv->inProgress) {
1362 QThreadPool::globalInstance()->releaseThread();
1363 baseEnv->cond.wait(&baseEnv->mutex);
1364 QThreadPool::globalInstance()->reserveThread();
1370#ifdef PROEVALUATOR_THREAD_SAFE
1371 baseEnv->inProgress =
true;
1377 baseEval->m_superfile = m_superfile;
1378 baseEval->m_conffile = m_conffile;
1379 baseEval->m_cachefile = m_cachefile;
1380 baseEval->m_stashfile = m_stashfile;
1381 baseEval->m_sourceRoot = m_sourceRoot;
1382 baseEval->m_buildRoot = m_buildRoot;
1386#ifdef PROEVALUATOR_THREAD_SAFE
1389 baseEnv->inProgress =
false;
1390 baseEnv->cond.wakeAll();
1396#ifdef PROEVALUATOR_THREAD_SAFE
1397 else if (!baseEnv->isOk)
1410 m_profileStack.push(pro);
1415 if (!m_option->extra_cmds[QMakeEvalEarly].isEmpty())
1416 evaluateCommand(m_option->extra_cmds[QMakeEvalEarly],
fL1S(
"(command line -early)"));
1418 for (ProValueMap::ConstIterator it = m_extraVars.constBegin();
1419 it != m_extraVars.constEnd(); ++it)
1430 evaluateCommand(m_option->extra_cmds[QMakeEvalBefore],
fL1S(
"(command line)"));
1443 evaluateCommand(m_option->extra_cmds[QMakeEvalAfter],
fL1S(
"(command line -after)"));
1453 if (!m_option->extra_cmds[QMakeEvalLate].isEmpty())
1454 evaluateCommand(m_option->extra_cmds[QMakeEvalLate],
fL1S(
"(command line -late)"));
1461 m_profileStack.pop();
1472 const QString concat = QLatin1String(
"/mkspecs");
1474 const auto paths =
m_option->getPathListEnv(QLatin1String(
"QMAKEPATH"));
1475 for (
const QString &it : paths)
1478 for (
const QString &it : std::as_const(m_qmakepath))
1481 if (!m_buildRoot.isEmpty())
1482 ret << m_buildRoot + concat;
1483 if (!m_sourceRoot.isEmpty())
1484 ret << m_sourceRoot + concat;
1489 ret.removeDuplicates();
1490 m_mkspecPaths = ret;
1495 QString mkspecs_concat = QLatin1String(
"/mkspecs");
1496 QString features_concat = QLatin1String(
"/features/");
1498 QStringList feature_roots;
1500 feature_roots +=
m_option->getPathListEnv(QLatin1String(
"QMAKEFEATURES"));
1501 feature_roots += m_qmakefeatures;
1502 feature_roots +=
m_option->splitPathList(
1505 QStringList feature_bases;
1506 if (!m_buildRoot.isEmpty()) {
1507 feature_bases << m_buildRoot + mkspecs_concat;
1508 feature_bases << m_buildRoot;
1510 if (!m_sourceRoot.isEmpty()) {
1511 feature_bases << m_sourceRoot + mkspecs_concat;
1512 feature_bases << m_sourceRoot;
1515 const auto items =
m_option->getPathListEnv(QLatin1String(
"QMAKEPATH"));
1516 for (
const QString &item : items)
1517 feature_bases << (item + mkspecs_concat);
1519 for (
const QString &item : std::as_const(m_qmakepath))
1520 feature_bases << (item + mkspecs_concat);
1522 if (!m_qmakespec.isEmpty()) {
1524 feature_roots << (m_qmakespec + features_concat);
1527 QDir specdir(m_qmakespec);
1528 while (!specdir.isRoot() && specdir.cdUp()) {
1529 const QString specpath = specdir.path();
1530 if (specpath.endsWith(mkspecs_concat)) {
1531 if (IoUtils::exists(specpath + features_concat))
1532 feature_bases << specpath;
1541 for (
const QString &fb : std::as_const(feature_bases)) {
1542 const auto sfxs = values(ProKey(
"QMAKE_PLATFORM"));
1543 for (
const ProString &sfx : sfxs)
1544 feature_roots << (fb + features_concat + sfx + QLatin1Char(
'/'));
1545 feature_roots << (fb + features_concat);
1548 for (
int i = 0; i < feature_roots.size(); ++i)
1549 if (!feature_roots.at(i).endsWith(QLatin1Char(
'/')))
1550 feature_roots[i].append(QLatin1Char(
'/'));
1552 feature_roots.removeDuplicates();
1555 for (
const QString &root : std::as_const(feature_roots))
1556 if (IoUtils::exists(root))
1558 m_featureRoots =
new QMakeFeatureRoots(ret);
1563 if (name == QLatin1String(
"QMAKE_MKSPECS"))
1564 return ProString(m_mkspecPaths.join(m_option->dirlist_sep));
1573 if (m_profileStack.size() > 0)
1574 return m_profileStack.top();
1590 return pro->fileName();
1598 return pro->directoryName();
1605 if (config == statics.strtrue)
1607 if (config == statics.strfalse)
1610 if (config == statics.strhost_build)
1613 if (regex && (config.contains(QLatin1Char(
'*')) || config.contains(QLatin1Char(
'?')))) {
1614 auto re = QRegularExpression::fromWildcard(config.toString());
1617 if (re.match(m_qmakespecName).hasMatch())
1621 const auto configValues = values(statics.strCONFIG);
1622 for (
const ProString &configValue : configValues) {
1623 ProStringRoUser u1(configValue, m_tmp[m_toggle ^= 1]);
1624 if (re.match(u1.str()).hasMatch())
1629 if (m_qmakespecName == config)
1633 if (values(statics.strCONFIG).contains(config))
1641 const ushort *&tokPtr,
int sizeHint,
ProStringList *ret,
bool joined)
1643 ret->reserve(sizeHint);
1645 if (evaluateExpression(tokPtr, ret, joined) == ReturnError)
1648 case TokValueTerminator:
1649 case TokFuncTerminator:
1652 case TokArgSeparator:
1659 Q_ASSERT_X(
false,
"expandVariableReferences",
"Unrecognized token");
1689 evalError(
fL1S(
"Ran into infinite recursion (depth > 100)."));
1693 m_locationStack.push(m_current);
1696 for (
int i = 0; i < argumentsList.size(); ++i) {
1697 args += argumentsList[i];
1698 m_valuemapStack.top()[ProKey(QString::number(i+1))] = argumentsList[i];
1700 m_valuemapStack.top()[statics.strARGS] = args;
1701 m_valuemapStack.top()[statics.strARGC] = ProStringList(ProString(QString::number(argumentsList.size())));
1705 if (vr == ReturnTrue)
1706 *ret = m_returnValue;
1707 m_returnValue.clear();
1709 m_current = m_locationStack.pop();
1724 if (ret.at(0) != statics.strfalse) {
1725 if (ret.at(0) == statics.strtrue)
1728 int val = ret.at(0).toInt(&ok);
1734 evalError(
fL1S(
"Unexpected return value from test '%1': %2.")
1735 .arg(u1.str(), ret.join(QLatin1String(
" :: "))));
1744 const ProKey &func,
const ushort *&tokPtr)
1746 auto adef = statics.functions.constFind(func);
1747 if (adef != statics.functions.constEnd()) {
1755 QHash<ProKey, ProFunctionDef>::ConstIterator it =
1756 m_functionDefs.testFunctions.constFind(func);
1757 if (it != m_functionDefs.testFunctions.constEnd()) {
1762 return evaluateBoolFunction(*it, args, func);
1766 evalError(
fL1S(
"'%1' is not a recognized test function.").arg(func.toQStringView()));
1773 auto adef = statics.expands.constFind(func);
1774 if (adef != statics.expands.constEnd()) {
1782 QHash<ProKey, ProFunctionDef>::ConstIterator it =
1783 m_functionDefs.replaceFunctions.constFind(func);
1784 if (it != m_functionDefs.replaceFunctions.constEnd()) {
1789 return evaluateFunction(*it, args, ret);
1793 evalError(
fL1S(
"'%1' is not a recognized replace function.").arg(func.toQStringView()));
1798 QStringView cond,
const QString &where,
int line)
1803 m_locationStack.push(m_current);
1805 m_current = m_locationStack.pop();
1811#ifdef PROEVALUATOR_FULL
1812QMakeEvaluator::VisitReturn QMakeEvaluator::checkRequirements(
const ProStringList &deps)
1814 ProStringList &failed = valuesRef(ProKey(
"QMAKE_FAILED_REQUIREMENTS"));
1815 for (
const ProString &dep : deps) {
1816 VisitReturn vr = evaluateConditional(dep.toQStringView(), m_current.pro->fileName(), m_current.line);
1817 if (vr == ReturnError)
1819 if (vr != ReturnTrue)
1828 const int len = variableName
.size();
1829 const QChar *data = variableName.constData();
1830 for (
int i = 0; i < len; i++) {
1831 ushort c = data[i].unicode();
1832 if (c <
'0' || c >
'9')
1840 ProValueMapStack::iterator vmi = m_valuemapStack.end();
1841 for (
bool first =
true; ; first =
false) {
1843 ProValueMap::Iterator it = (*vmi).find(variableName);
1844 if (it != (*vmi).end()) {
1845 if (it->constBegin() == statics.fakeValue.constBegin())
1860 ProValueMap::Iterator it = m_valuemapStack.top().find(variableName);
1862 if (it->constBegin() == statics.fakeValue.constBegin())
1867 ProValueMapStack::iterator vmi = m_valuemapStack.end();
1871 ProValueMap::ConstIterator it = (*vmi).constFind(variableName);
1872 if (it != (*vmi).constEnd()) {
1874 if (it->constBegin() != statics.fakeValue.constBegin())
1881 return m_valuemapStack.top()[variableName];
1886 ProValueMapStack::const_iterator vmi = m_valuemapStack.cend();
1887 for (
bool first =
true; ; first =
false) {
1889 ProValueMap::ConstIterator it = (*vmi).constFind(variableName);
1890 if (it != (*vmi).constEnd()) {
1891 if (it->constBegin() == statics.fakeValue.constBegin())
1906 if (!vals.isEmpty())
1907 return vals.first();
1914 QMakeParser::ParseFlags pflags = QMakeParser::ParseUseCache;
1915 if (!(flags & LoadSilent))
1916 pflags |= QMakeParser::ParseReportMissing;
1917 if (
ProFile *pro = m_parser->parsedProFile(fileName, pflags)) {
1918 m_locationStack.push(m_current);
1920 m_current = m_locationStack.pop();
1925 if (!iif.contains(ifn))
1937 if (fileName.isEmpty())
1941 for (
const ProFile *pf : ref->m_profileStack)
1942 if (pf->fileName() == fileName) {
1943 evalError(
fL1S(
"Circular inclusion of %1.").arg(fileName));
1947 return evaluateFile(fileName, type, flags);
1951 const QString &fileName,
bool silent)
1953 QString fn = fileName;
1954 if (!fn.endsWith(QLatin1String(
".prf")))
1955 fn += QLatin1String(
".prf");
1957 if (!m_featureRoots)
1959#ifdef PROEVALUATOR_THREAD_SAFE
1960 m_featureRoots->mutex.lock();
1962 QString currFn = currentFileName();
1963 if (IoUtils::fileName(currFn) != IoUtils::fileName(fn))
1967 QString *fnp = &m_featureRoots->cache[qMakePair(fn, currFn)];
1968 if (fnp->isNull()) {
1969#ifdef QMAKE_OVERRIDE_PRFS
1971 QString ovrfn(QLatin1String(
":/qmake/override_features/") + fn);
1972 if (QFileInfo::exists(ovrfn)) {
1980 const QStringList &paths = m_featureRoots->paths;
1981 if (!currFn.isEmpty()) {
1982 QStringView currPath = IoUtils::pathName(currFn);
1983 for (
int root = 0; root < paths.size(); ++root)
1984 if (currPath == paths.at(root)) {
1985 start_root = root + 1;
1989 for (
int root = start_root; root < paths.size(); ++root) {
1990 QString fname = paths.at(root) + fn;
1991 if (IoUtils::exists(fname)) {
1997#ifdef QMAKE_BUILTIN_PRFS
1998 fn.prepend(QLatin1String(
":/qmake/features/"));
1999 if (QFileInfo::exists(fn))
2002 fn = QLatin1String(
"");
2009#ifdef PROEVALUATOR_THREAD_SAFE
2010 m_featureRoots->mutex.unlock();
2014 evalError(
fL1S(
"Cannot find feature %1").arg(fileName));
2019 if (already.contains(afn)) {
2021 languageWarning(
fL1S(
"Feature %1 already included").arg(fileName));
2024 already.append(afn);
2026#ifdef PROEVALUATOR_CUMULATIVE
2027 bool cumulative = m_cumulative;
2032 m_cumulative =
false;
2036 VisitReturn ok = evaluateFile(fn, QMakeHandler::EvalFeatureFile, LoadProOnly);
2038#ifdef PROEVALUATOR_CUMULATIVE
2039 m_cumulative = cumulative;
2052 const QString &fileName,
ProValueMap *values, LoadFlags flags)
2056 visitor.m_outputDir = m_outputDir;
2057 visitor.m_featureRoots = m_featureRoots;
2062 ProKey qiif
("QMAKE_INTERNAL_INCLUDED_FILES");
2064 const auto ifns = values->value(qiif);
2065 for (
const ProString &ifn : ifns)
2066 if (!iif.contains(ifn))
2079#ifdef PROEVALUATOR_DEBUG
2080void QMakeEvaluator::debugMsgInternal(
int level,
const char *fmt, ...)
const
2084 if (level <= m_debugLevel) {
2085 fprintf(stderr,
"DEBUG %d: ", level);
2087 vfprintf(stderr, fmt, ap);
2089 fputc(
'\n', stderr);
2093void QMakeEvaluator::traceMsgInternal(
const char *fmt, ...)
const
2098 fprintf(stderr,
"DEBUG 1: ");
2099 else if (m_current.line <= 0)
2100 fprintf(stderr,
"DEBUG 1: %s: ", qPrintable(m_current.pro->fileName()));
2102 fprintf(stderr,
"DEBUG 1: %s:%d: ", qPrintable(m_current.pro->fileName()), m_current.line);
2104 vfprintf(stderr, fmt, ap);
2106 fputc(
'\n', stderr);
2109QString QMakeEvaluator::formatValue(
const ProString &val,
bool forceQuote)
2112 ret.reserve(val.size() + 2);
2113 const QChar *chars = val.constData();
2114 bool quote = forceQuote || val.isEmpty();
2115 for (
int i = 0, l = val.size(); i < l; i++) {
2117 ushort uc = c.unicode();
2121 ret += QLatin1String(
"\\r");
2124 ret += QLatin1String(
"\\n");
2127 ret += QLatin1String(
"\\t");
2130 ret += QString::fromLatin1(
"\\x%1").arg(uc, 2, 16, QLatin1Char(
'0'));
2136 ret += QLatin1String(
"\\\\");
2139 ret += QLatin1String(
"\\\"");
2142 ret += QLatin1String(
"\\'");
2154 ret.prepend(QLatin1Char(
'"'));
2155 ret.append(QLatin1Char(
'"'));
2160QString QMakeEvaluator::formatValueList(
const ProStringList &vals,
bool commas)
2164 for (
const ProString &str : vals) {
2165 if (!ret.isEmpty()) {
2167 ret += QLatin1Char(
',');
2168 ret += QLatin1Char(
' ');
2170 ret += formatValue(str);
2175QString QMakeEvaluator::formatValueListList(
const QList<ProStringList> &lists)
2179 for (
const ProStringList &list : lists) {
2181 ret += QLatin1String(
", ");
2182 ret += formatValueList(list);
const ushort * tokPtr() const
ProKey getHashStr(const ushort *&tPtr)
const ushort * tokPtr() const
ProFunctionDef(ProFile *pro, int offset)
PROITEM_EXPLICIT ProKey(const char *str)
void insertUnique(const ProStringList &value)
void removeEach(const ProStringList &value)
QString toQString() const
ProString(const QString &str)
ALWAYS_INLINE ProKey & toKey()
PROITEM_EXPLICIT ProString(const char *str)
ProString & setSource(int id)
void push(const ProValueMap &t)
QMakeEvaluator * evaluator
QMakeBaseKey(const QString &_root, const QString &_stash, bool _hostBuild)
VisitReturn evaluateFileInto(const QString &fileName, ProValueMap *values, LoadFlags flags)
static ALWAYS_INLINE VisitReturn returnBool(bool b)
VisitReturn evaluateBuiltinConditional(const QMakeInternal::QMakeBuiltin &adef, const ProKey &function, const ProStringList &args)
ProStringList split_value_list(QStringView vals, int source=0)
VisitReturn evaluateBuiltinExpand(const QMakeInternal::QMakeBuiltin &adef, const ProKey &function, const ProStringList &args, ProStringList &ret)
VisitReturn prepareFunctionArgs(const ushort *&tokPtr, QList< ProStringList > *ret)
VisitReturn evaluateConfigFeatures()
VisitReturn visitProVariable(ushort tok, const ProStringList &curr, const ushort *&tokPtr)
int currentFileId() const
VisitReturn visitProBlock(const ushort *tokPtr)
VisitReturn evaluateFunction(const ProFunctionDef &func, const QList< ProStringList > &argumentsList, ProStringList *ret)
QMakeEvaluator(QMakeGlobals *option, QMakeParser *parser, QMakeVfs *vfs, QMakeHandler *handler)
static void initFunctionStatics()
VisitReturn visitProBlock(ProFile *pro, const ushort *tokPtr)
bool prepareProject(const QString &inDir)
VisitReturn evaluateBoolFunction(const ProFunctionDef &func, const QList< ProStringList > &argumentsList, const ProString &function)
QString currentDirectory() const
VisitReturn evaluateFile(const QString &fileName, QMakeHandler::EvalFileType type, LoadFlags flags)
VisitReturn expandVariableReferences(const ushort *&tokPtr, int sizeHint, ProStringList *ret, bool joined)
ProString first(const ProKey &variableName) const
static ALWAYS_INLINE void skipStr(const ushort *&tokPtr)
ProString propertyValue(const ProKey &val) const
static ALWAYS_INLINE void skipHashStr(const ushort *&tokPtr)
VisitReturn evaluateConditional(QStringView cond, const QString &where, int line=-1)
void updateFeaturePaths()
ALWAYS_INLINE void traceMsgInternal(const char *,...) const
ProStringList & valuesRef(const ProKey &variableName)
VisitReturn evaluateFeatureFile(const QString &fileName, bool silent=false)
VisitReturn evaluateFileChecked(const QString &fileName, QMakeHandler::EvalFileType type, LoadFlags flags)
VisitReturn visitProFile(ProFile *pro, QMakeHandler::EvalFileType type, LoadFlags flags)
void message(int type, const QString &msg) const
ALWAYS_INLINE void debugMsgInternal(int, const char *,...) const
const ProKey & map(const ProKey &var)
ProValueMap * findValues(const ProKey &variableName, ProValueMap::Iterator *it)
void initFrom(const QMakeEvaluator *other)
ProStringList values(const ProKey &variableName) const
static ALWAYS_INLINE uint getBlockLen(const ushort *&tokPtr)
void evaluateCommand(const QString &cmds, const QString &where)
ProFile * currentProFile() const
VisitReturn evaluateExpandFunction(const ProKey &function, const ushort *&tokPtr, ProStringList *ret)
void skipExpression(const ushort *&tokPtr)
void visitProFunctionDef(ushort tok, const ProKey &name, const ushort *tokPtr)
VisitReturn evaluateExpression(const ushort *&tokPtr, ProStringList *ret, bool joined)
ProValueMapStack m_valuemapStack
VisitReturn visitProLoop(const ProKey &variable, const ushort *exprPtr, const ushort *tokPtr)
QMakeEvaluator * m_caller
bool isActiveConfig(QStringView config, bool regex=false)
VisitReturn evaluateConditionalFunction(const ProKey &function, const ushort *&tokPtr)
QString currentFileName() const
ProString propertyValue(const ProKey &name) const
virtual void aboutToEval(ProFile *parent, ProFile *proFile, EvalFileType type)=0
virtual void doneWithEval(ProFile *parent)=0
virtual void message(int type, const QString &msg, const QString &fileName=QString(), int lineNo=0)=0
QMap< ProKey, ProStringList > ProValueMap
static int idealThreadCount()
static void replaceInList(ProStringList *varlist, const QRegularExpression ®exp, const QString &replace, bool global, QString &tmp)
static bool isFunctParam(const ProKey &variableName)
static ALWAYS_INLINE void addStrList(const ProStringList &list, ushort tok, ProStringList *ret, bool &pending, bool joined)
static ALWAYS_INLINE void addStr(const ProString &str, ProStringList *ret, bool &pending, bool joined)
#define dbgStrListList(s)
bool operator==(const QMakeBaseKey &one, const QMakeBaseKey &two)
size_t qHash(const QMakeBaseKey &key)
#define qPrintable(string)