122 static const QMakeBuiltinInit expandInits[] = {
123 {
"member", E_MEMBER, 1, 3,
"var, [start, [end]]" },
124 {
"str_member", E_STR_MEMBER, -1, 3,
"str, [start, [end]]" },
125 {
"first", E_FIRST, 1, 1,
"var" },
126 {
"take_first", E_TAKE_FIRST, 1, 1,
"var" },
127 {
"last", E_LAST, 1, 1,
"var" },
128 {
"take_last", E_TAKE_LAST, 1, 1,
"var" },
129 {
"size", E_SIZE, 1, 1,
"var" },
130 {
"str_size", E_STR_SIZE, -1, 1,
"str" },
131 {
"cat", E_CAT, 1, 2,
"file, [mode=true|blob|lines]" },
132 {
"fromfile", E_FROMFILE, 2, 2,
"file, var" },
133 {
"eval", E_EVAL, 1, 1,
"var" },
134 {
"list", E_LIST, 0, QMakeBuiltinInit::VarArgs,
nullptr },
135 {
"sprintf", E_SPRINTF, 1, QMakeBuiltinInit::VarArgs,
"format, ..." },
136 {
"format_number", E_FORMAT_NUMBER, 1, 2,
"number, [options...]" },
137 {
"num_add", E_NUM_ADD, 1, QMakeBuiltinInit::VarArgs,
"num, ..." },
138 {
"join", E_JOIN, 1, 4,
"var, [glue, [before, [after]]]" },
139 {
"split", E_SPLIT, 1, 2,
"var, sep" },
140 {
"basename", E_BASENAME, 1, 1,
"var" },
141 {
"dirname", E_DIRNAME, 1, 1,
"var" },
142 {
"section", E_SECTION, 3, 4,
"var, sep, begin, [end]" },
143 {
"find", E_FIND, 2, 2,
"var, str" },
144 {
"system", E_SYSTEM, 1, 3,
"command, [mode], [stsvar]" },
145 {
"unique", E_UNIQUE, 1, 1,
"var" },
146 {
"sorted", E_SORTED, 1, 1,
"var" },
147 {
"reverse", E_REVERSE, 1, 1,
"var" },
148 {
"quote", E_QUOTE, 0, QMakeBuiltinInit::VarArgs,
nullptr },
149 {
"escape_expand", E_ESCAPE_EXPAND, 0, QMakeBuiltinInit::VarArgs,
nullptr },
150 {
"upper", E_UPPER, 0, QMakeBuiltinInit::VarArgs,
nullptr },
151 {
"lower", E_LOWER, 0, QMakeBuiltinInit::VarArgs,
nullptr },
152 {
"title", E_TITLE, 0, QMakeBuiltinInit::VarArgs,
nullptr },
153 {
"re_escape", E_RE_ESCAPE, 0, QMakeBuiltinInit::VarArgs,
nullptr },
154 {
"val_escape", E_VAL_ESCAPE, 1, 1,
"var" },
155 {
"files", E_FILES, 1, 2,
"pattern, [recursive=false]" },
156 {
"prompt", E_PROMPT, 1, 2,
"question, [decorate=true]" },
157 {
"replace", E_REPLACE, 3, 3,
"var, before, after" },
158 {
"sort_depends", E_SORT_DEPENDS, 1, 4,
"var, [prefix, [suffixes, [prio-suffix]]]" },
159 {
"resolve_depends", E_RESOLVE_DEPENDS, 1, 4,
"var, [prefix, [suffixes, [prio-suffix]]]" },
160 {
"enumerate_vars", E_ENUMERATE_VARS, 0, 0,
"" },
161 {
"shadowed", E_SHADOWED, 1, 1,
"path" },
162 {
"absolute_path", E_ABSOLUTE_PATH, -1, 2,
"path, [base]" },
163 {
"relative_path", E_RELATIVE_PATH, -1, 2,
"path, [base]" },
164 {
"clean_path", E_CLEAN_PATH, -1, 1,
"path" },
165 {
"system_path", E_SYSTEM_PATH, -1, 1,
"path" },
166 {
"shell_path", E_SHELL_PATH, -1, 1,
"path" },
167 {
"system_quote", E_SYSTEM_QUOTE, -1, 1,
"arg" },
168 {
"shell_quote", E_SHELL_QUOTE, -1, 1,
"arg" },
169 {
"getenv", E_GETENV, 1, 1,
"arg" },
170 {
"read_registry", E_READ_REGISTRY, 2, 3,
"tree, key, [wow64]" },
172 statics.expands.reserve((
int)(
sizeof(expandInits)/
sizeof(expandInits[0])));
173 for (
unsigned i = 0; i <
sizeof(expandInits)/
sizeof(expandInits[0]); ++i)
174 statics.expands.insert(ProKey(expandInits[i].name), QMakeBuiltin(expandInits[i]));
176 static const QMakeBuiltinInit testInits[] = {
177 {
"requires", T_REQUIRES, 0, QMakeBuiltinInit::VarArgs,
nullptr },
178 {
"greaterThan", T_GREATERTHAN, 2, 2,
"var, val" },
179 {
"lessThan", T_LESSTHAN, 2, 2,
"var, val" },
180 {
"equals", T_EQUALS, 2, 2,
"var, val" },
181 {
"isEqual", T_EQUALS, 2, 2,
"var, val" },
182 {
"versionAtLeast", T_VERSION_AT_LEAST, 2, 2,
"var, version" },
183 {
"versionAtMost", T_VERSION_AT_MOST, 2, 2,
"var, version" },
184 {
"exists", T_EXISTS, 1, 1,
"file" },
185 {
"export", T_EXPORT, 1, 1,
"var" },
186 {
"clear", T_CLEAR, 1, 1,
"var" },
187 {
"unset", T_UNSET, 1, 1,
"var" },
188 {
"eval", T_EVAL, 0, QMakeBuiltinInit::VarArgs,
nullptr },
189 {
"CONFIG", T_CONFIG, 1, 2,
"config, [mutuals]" },
190 {
"if", T_IF, 1, 1,
"condition" },
191 {
"isActiveConfig", T_CONFIG, 1, 2,
"config, [mutuals]" },
192 {
"system", T_SYSTEM, 1, 1,
"exec" },
193 {
"discard_from", T_DISCARD_FROM, 1, 1,
"file" },
194 {
"defined", T_DEFINED, 1, 2,
"object, [\"test\"|\"replace\"|\"var\"]" },
195 {
"contains", T_CONTAINS, 2, 3,
"var, val, [mutuals]" },
196 {
"infile", T_INFILE, 2, 3,
"file, var, [values]" },
197 {
"count", T_COUNT, 2, 3,
"var, count, [op=operator]" },
198 {
"isEmpty", T_ISEMPTY, 1, 1,
"var" },
199 {
"parseJson", T_PARSE_JSON, 2, 2,
"var, into" },
200 {
"load", T_LOAD, 1, 2,
"feature, [ignore_errors=false]" },
201 {
"include", T_INCLUDE, 1, 3,
"file, [into, [silent]]" },
202 {
"debug", T_DEBUG, 2, 2,
"level, message" },
203 {
"log", T_LOG, 1, 1,
"message" },
204 {
"message", T_MESSAGE, 1, 1,
"message" },
205 {
"warning", T_WARNING, 1, 1,
"message" },
206 {
"error", T_ERROR, 0, 1,
"message" },
207 {
"mkpath", T_MKPATH, 1, 1,
"path" },
208 {
"write_file", T_WRITE_FILE, 1, 3,
"name, [content var, [append] [exe]]" },
209 {
"touch", T_TOUCH, 2, 2,
"file, reffile" },
210 {
"cache", T_CACHE, 0, 3,
"[var], [set|add|sub] [transient] [super|stash], [srcvar]" },
211 {
"reload_properties", T_RELOAD_PROPERTIES, 0, 0,
"" },
213 statics.functions.reserve((
int)(
sizeof(testInits)/
sizeof(testInits[0])));
214 for (
unsigned i = 0; i <
sizeof(testInits)/
sizeof(testInits[0]); ++i)
215 statics.functions.insert(ProKey(testInits[i].name), QMakeBuiltin(testInits[i]));
578 int asz = args.size() > 1 ? args.size() : args.at(0).isEmpty() ? 0 : 1;
579 if (asz < adef.minArgs || asz > adef.maxArgs) {
584 int func_t = adef.index;
594 if (func_t == E_SECTION) {
596 sep = args.at(1).toQString();
597 beg = args.at(2).toInt();
598 if (args.size() == 4)
599 end = args.at(3).toInt();
603 sep = QLatin1String(
"[\\\\/]");
604 if (func_t == E_DIRNAME)
612 QRegularExpression sepRx(sep, QRegularExpression::DotMatchesEverythingOption);
613 if (!sepRx.isValid()) {
614 evalError(
fL1S(
"section(): Encountered invalid regular expression '%1'.").arg(sep));
617 for (
const ProString &str : strings) {
618 ProStringRwUser u1(str, m_tmp[m_toggle ^= 1]);
619 ret << u1.extract(u1.str().section(sepRx, beg, end));
622 for (
const ProString &str : strings) {
623 ProStringRwUser u1(str, m_tmp1);
624 ret << u1.extract(u1.str().section(sep, beg, end));
632 QString tmp = u1.str();
633 for (
int i = 1; i < args.size(); ++i)
634 tmp = tmp.arg(args.at(i).toQStringView());
635 ret << u1.extract(tmp);
638 case E_FORMAT_NUMBER: {
642 bool zeropad =
false;
643 bool leftalign =
false;
644 enum { DefaultSign, PadSign, AlwaysSign } sign = DefaultSign;
645 if (args.size() >= 2) {
646 const auto opts = split_value_list(args.at(1).toQStringView());
647 for (
const ProString &opt : opts) {
648 if (opt.startsWith(QLatin1String(
"ibase="))) {
649 ibase = opt.mid(6).toInt();
650 }
else if (opt.startsWith(QLatin1String(
"obase="))) {
651 obase = opt.mid(6).toInt();
652 }
else if (opt.startsWith(QLatin1String(
"width="))) {
653 width = opt.mid(6).toInt();
654 }
else if (opt == QLatin1String(
"zeropad")) {
656 }
else if (opt == QLatin1String(
"padsign")) {
658 }
else if (opt == QLatin1String(
"alwayssign")) {
660 }
else if (opt == QLatin1String(
"leftalign")) {
663 evalError(
fL1S(
"format_number(): invalid format option %1.")
664 .arg(opt.toQStringView()));
669 if (args.at(0).contains(QLatin1Char(
'.'))) {
670 evalError(
fL1S(
"format_number(): floats are currently not supported."));
674 qlonglong num = args.at(0).toLongLong(&ok, ibase);
676 evalError(
fL1S(
"format_number(): malformed number %2 for base %1.")
677 .arg(ibase).arg(args.at(0).toQStringView()));
683 outstr = QLatin1Char(
'-');
684 }
else if (sign == AlwaysSign) {
685 outstr = QLatin1Char(
'+');
686 }
else if (sign == PadSign) {
687 outstr = QLatin1Char(
' ');
689 QString numstr = QString::number(num, obase);
690 int space = width - outstr.size() - numstr.size();
693 }
else if (leftalign) {
694 outstr += numstr + QString(space, QLatin1Char(
' '));
695 }
else if (zeropad) {
696 outstr += QString(space, QLatin1Char(
'0')) + numstr;
698 outstr.prepend(QString(space, QLatin1Char(
' ')));
706 for (
const ProString &arg : std::as_const(args)) {
707 if (arg.contains(QLatin1Char(
'.'))) {
708 evalError(
fL1S(
"num_add(): floats are currently not supported."));
712 qlonglong num = arg.toLongLong(&ok);
714 evalError(
fL1S(
"num_add(): malformed number %1.")
715 .arg(arg.toQStringView()));
720 ret += ProString(QString::number(sum));
725 if (args.size() >= 2)
727 if (args.size() >= 3)
729 if (args.size() == 4)
732 if (!var.isEmpty()) {
734 for (
const ProString &v : var)
735 if (
int s = v.sourceFile()) {
745 const QString &sep = (args.size() == 2) ? u1.set(args.at(1)) : statics.field_sep;
746 const auto vars =
values(map(args.at(0))
);
747 for (
const ProString &var : vars) {
749 const auto splits = var.toQStringView().split(sep, Qt::KeepEmptyParts);
750 for (
const auto &splt : splits)
751 ret << ProString(splt).setSource(var);
759 ret.reserve(qAbs(end - start) + 1);
761 for (
int i = start; i <= end && src.size() >= i; i++)
764 for (
int i = start; i >= end && src.size() >= i && i >= 0; i--)
775 res.reserve(qAbs(end - start) + 1);
777 for (
int i = start; i <= end && src
.size() >= i; i++)
780 for (
int i = start; i >= end && src
.size() >= i && i >= 0; i--)
790 if (!var.isEmpty()) {
791 if (func_t == E_FIRST)
794 ret.append(var.last());
801 if (!var.isEmpty()) {
802 if (func_t == E_TAKE_FIRST)
803 ret.append(var.takeFirst());
805 ret.append(var.takeLast());
810 ret.append(ProString(QString::number(values(map(args.at(0))).size())));
813 ret.append(ProString(QString::number(args.at(0).size())));
818 bool singleLine =
true;
819 if (args.size() > 1) {
820 if (!args.at(1).compare(QLatin1String(
"false"), Qt::CaseInsensitive))
822 else if (!args.at(1).compare(QLatin1String(
"blob"), Qt::CaseInsensitive))
824 else if (!args.at(1).compare(QLatin1String(
"lines"), Qt::CaseInsensitive))
827 QString fn = filePathEnvArg0(args);
829 if (qfile.open(QIODevice::ReadOnly)) {
830 QTextStream stream(&qfile);
834 while (!stream.atEnd()) {
838 const QString &line = stream.readLine();
839 ret += split_value_list(QStringView(line).trimmed());
850 QString fn = filePathEnvArg0(args);
851 if (evaluateFileInto(fn, &vars, LoadProOnly) == ReturnTrue)
852 ret = vars.value(map(args.at(1)));
859 QString tmp(QString::asprintf(
".QMAKE_INTERNAL_TMP_variableName_%d", m_listCount++));
862 for (
const ProString &arg : args)
863 lst += split_value_list(arg.toQStringView(), arg.sourceFile());
867 QRegularExpression regx(args.at(1).toQString(), QRegularExpression::DotMatchesEverythingOption);
868 if (!regx.isValid()) {
869 evalError(
fL1S(
"find(): Encountered invalid regular expression '%1'.").arg(regx.pattern()));
872 const auto vals =
values(map(args.at(0))
);
873 for (
const ProString &val : vals) {
874 ProStringRoUser u1(val, m_tmp[m_toggle ^= 1]);
875 if (u1.str().contains(regx))
885 bool singleLine =
true;
886 if (args.size() > 1) {
887 if (!args.at(1).compare(QLatin1String(
"false"), Qt::CaseInsensitive))
889 else if (!args.at(1).compare(QLatin1String(
"blob"), Qt::CaseInsensitive))
891 else if (!args.at(1).compare(QLatin1String(
"lines"), Qt::CaseInsensitive))
895 QByteArray bytes = getCommandOutput(args.at(0).toQString(), &exitCode);
896 if (args.size() > 2 && !args.at(2).isEmpty()) {
897 m_valuemapStack.top()[args.at(2).toKey()] =
898 ProStringList(ProString(QString::number(exitCode)));
901 QTextStream stream(bytes);
902 while (!stream.atEnd())
905 QString output = QString::fromLocal8Bit(bytes);
909 output.replace(QLatin1Char(
'\t'), QLatin1Char(
' '));
911 output.replace(QLatin1Char(
'\n'), QLatin1Char(
' '));
912 ret += split_value_list(QStringView(output));
923 std::sort(ret.begin(), ret.end());
927 for (
int i = 0; i < var.size() / 2; i++)
928 qSwap(var[i], var[var.size() - i - 1]);
935 case E_ESCAPE_EXPAND:
936 for (
int i = 0; i < args.size(); ++i) {
937 QString str = args.at(i).toQString();
938 QChar *i_data = str.data();
939 int i_len = str.size();
940 for (
int x = 0; x < i_len; ++x) {
941 if (*(i_data+x) == QLatin1Char(
'\\') && x < i_len-1) {
942 if (*(i_data+x+1) == QLatin1Char(
'\\')) {
947 } mapped_quotes[] = {
953 for (
int i = 0; mapped_quotes[i].in; ++i) {
954 if (*(i_data+x+1) == QLatin1Char(mapped_quotes[i].in)) {
955 *(i_data+x) = QLatin1Char(mapped_quotes[i].out);
957 memmove(i_data+x+1, i_data+x+2, (i_len-x-2)*
sizeof(QChar));
965 ret.append(
ProString(QString(i_data, i_len)).setSource(args.at(i)));
969 for (
int i = 0; i < args.size(); ++i) {
971 ret << u1.extract(QRegularExpression::escape(u1.str()));
976 ret.reserve(vals.size());
977 for (
const ProString &str : vals)
978 ret += ProString(quoteValue(str));
984 for (
int i = 0; i < args.size(); ++i) {
986 QString rstr = u1.str();
987 if (func_t == E_UPPER) {
988 rstr = rstr.toUpper();
990 rstr = rstr.toLower();
991 if (func_t == E_TITLE && rstr.size() > 0)
992 rstr[0] = rstr.at(0).toTitleCase();
994 ret << u1.extract(rstr);
998 bool recursive =
false;
999 if (args.size() == 2)
1000 recursive = isTrue(args.at(1));
1003 QString r = m_option->expandEnvVars(u1.str())
1004 .replace(QLatin1Char(
'\\'), QLatin1Char(
'/'));
1006 if (IoUtils::isRelativePath(r)) {
1008 if (!pfx.endsWith(QLatin1Char(
'/')))
1009 pfx += QLatin1Char(
'/');
1011 int slash = r.lastIndexOf(QLatin1Char(
'/'));
1013 dirs.append(r.left(slash+1));
1016 dirs.append(QString());
1019 QString pattern = QRegularExpression::wildcardToRegularExpression(r);
1020 QRegularExpression regex(pattern, QRegularExpression::DotMatchesEverythingOption);
1021 if (!regex.isValid()) {
1022 evalError(
fL1S(
"section(): Encountered invalid wildcard expression '%1'.").arg(pattern));
1025 for (
int d = 0; d < dirs.size(); d++) {
1026 QString dir = dirs[d];
1027 QDir qdir(pfx + dir);
1028 for (
int i = 0, count =
int(qdir.count()); i < count; ++i) {
1029 if (qdir[i] == statics.strDot || qdir[i] == statics.strDotDot)
1031 QString fname = dir + qdir[i];
1032 if (IoUtils::fileType(pfx + fname) == IoUtils::FileIsDir) {
1034 dirs.append(fname + QLatin1Char(
'/'));
1036 if (regex.match(qdir[i]).hasMatch())
1042#ifdef PROEVALUATOR_FULL
1044 ProStringRoUser u1(args.at(0), m_tmp1);
1045 QString msg = m_option->expandEnvVars(u1.str());
1046 bool decorate =
true;
1047 if (args.count() == 2)
1048 decorate = isTrue(args.at(1));
1050 if (!msg.endsWith(QLatin1Char(
'?')))
1051 msg += QLatin1Char(
'?');
1052 fprintf(stderr,
"Project PROMPT: %s ", qPrintable(msg));
1054 fputs(qPrintable(msg), stderr);
1057 if (qfile.open(stdin, QIODevice::ReadOnly)) {
1058 QTextStream t(&qfile);
1059 const QString &line = t.readLine();
1061 fputs(
"\n", stderr);
1062 evalError(fL1S(
"Unexpected EOF."));
1065 ret = split_value_list(QStringView(line));
1071 const QRegularExpression before(args.at(1).toQString(), QRegularExpression::DotMatchesEverythingOption);
1072 if (!before.isValid()) {
1073 evalError(
fL1S(
"replace(): Encountered invalid regular expression '%1'.").arg(before.pattern()));
1077 const QString &after = u2
.str();
1078 const auto vals =
values(map(args.at(0))
);
1079 for (
const ProString &val : vals) {
1080 ProStringRwUser u1(val, m_tmp1);
1081 QString rstr = u1.str();
1082 QString copy = rstr;
1083 rstr.replace(before, after);
1084 ret << u1.extract(rstr, u2);
1088 case E_SORT_DEPENDS:
1089 case E_RESOLVE_DEPENDS: {
1090 QHash<ProKey, QSet<ProKey> > dependencies;
1092 QMultiMap<
int, ProString> rootSet;
1096 populateDeps(orgList, prefix,
1097 args.size() < 3 ? ProStringList(ProString(
".depends"))
1098 : split_value_list(args.at(2).toQStringView()),
1099 priosfx, dependencies, dependees, rootSet);
1100 while (!rootSet.isEmpty()) {
1101 QMultiMap<
int, ProString>::iterator it = rootSet.begin();
1104 if ((func_t == E_RESOLVE_DEPENDS) || orgList.contains(item))
1106 for (
const ProString &dep : std::as_const(dependees[item.toKey()])) {
1107 QSet<ProKey> &dset = dependencies[dep.toKey()];
1108 dset.remove(item.toKey());
1110 rootSet.insert(first(ProKey(prefix + dep + priosfx)).toInt(), dep);
1115 case E_ENUMERATE_VARS: {
1116 QSet<ProString> keys;
1117 for (
const ProValueMap &vmap : std::as_const(m_valuemapStack))
1118 for (ProValueMap::ConstIterator it = vmap.constBegin(); it != vmap.constEnd(); ++it)
1119 keys.insert(it.key());
1120 ret.reserve(keys.size());
1121 for (
const ProString &key : std::as_const(keys))
1126 QString rstr = m_option->shadowedPath(resolvePath(u1.str()));
1127 if (!rstr.isEmpty())
1128 ret << u1.extract(rstr);
1131 case E_ABSOLUTE_PATH: {
1134 QString baseDir = args.size() > 1
1135 ? IoUtils::resolvePath(currentDirectory(), u2.set(args.at(1)))
1136 : currentDirectory();
1137 QString rstr = u1.str().isEmpty() ? baseDir : IoUtils::resolvePath(baseDir, u1.str());
1138 ret << u1.extract(rstr, u2);
1141 case E_RELATIVE_PATH: {
1144 QString baseDir = args.size() > 1
1145 ? IoUtils::resolvePath(currentDirectory(), u2.set(args.at(1)))
1146 : currentDirectory();
1147 QString absArg = u1.str().isEmpty() ? baseDir : IoUtils::resolvePath(baseDir, u1.str());
1148 QString rstr = QDir(baseDir).relativeFilePath(absArg);
1149 ret << u1.extract(rstr);
1152 case E_CLEAN_PATH: {
1154 ret << u1.extract(QDir::cleanPath(u1.str()));
1157 case E_SYSTEM_PATH: {
1159 QString rstr = u1.str();
1161 rstr.replace(QLatin1Char(
'/'), QLatin1Char(
'\\'));
1163 rstr.replace(QLatin1Char(
'\\'), QLatin1Char(
'/'));
1165 ret << u1.extract(rstr);
1168 case E_SHELL_PATH: {
1170 QString rstr = u1.str();
1171 if (m_dirSep.startsWith(QLatin1Char(
'\\'))) {
1172 rstr.replace(QLatin1Char(
'/'), QLatin1Char(
'\\'));
1174 rstr.replace(QLatin1Char(
'\\'), QLatin1Char(
'/'));
1177 if (rstr.length() > 2 && rstr.at(1) == QLatin1Char(
':') && rstr.at(2) == QLatin1Char(
'/')) {
1178 rstr[1] = rstr.at(0);
1179 rstr[0] = QLatin1Char(
'/');
1183 ret << u1.extract(rstr);
1186 case E_SYSTEM_QUOTE: {
1188 ret << u1.extract(IoUtils::shellQuote(u1.str()));
1191 case E_SHELL_QUOTE: {
1193 QString rstr = u1.str();
1194 if (m_dirSep.startsWith(QLatin1Char(
'\\')))
1195 rstr = IoUtils::shellQuoteWin(rstr);
1197 rstr = IoUtils::shellQuoteUnix(rstr);
1198 ret << u1.extract(rstr);
1207 case E_READ_REGISTRY: {
1209 const auto par = args.at(0);
1210 if (!par.compare(QLatin1String(
"HKCU"), Qt::CaseInsensitive)
1211 || !par.compare(QLatin1String(
"HKEY_CURRENT_USER"), Qt::CaseInsensitive)) {
1212 tree = HKEY_CURRENT_USER;
1213 }
else if (!par.compare(QLatin1String(
"HKLM"), Qt::CaseInsensitive)
1214 || !par.compare(QLatin1String(
"HKEY_LOCAL_MACHINE"), Qt::CaseInsensitive)) {
1215 tree = HKEY_LOCAL_MACHINE;
1217 evalError(fL1S(
"read_registry(): invalid or unsupported registry tree %1.")
1218 .arg(par.toQStringView()));
1222 if (args.count() > 2) {
1223 const auto opt = args.at(2);
1225 || !opt.compare(QLatin1String(
"wow64_32key"), Qt::CaseInsensitive)) {
1226 flags = KEY_WOW64_32KEY;
1227 }
else if (opt ==
"64"
1228 || !opt.compare(QLatin1String(
"wow64_64key"), Qt::CaseInsensitive)) {
1229 flags = KEY_WOW64_64KEY;
1231 evalError(fL1S(
"read_registry(): invalid option %1.")
1232 .arg(opt.toQStringView()));
1236 ret << ProString(qt_readRegistryKey(tree, args.at(1).toQString(m_tmp1), flags));
1241 evalError(
fL1S(
"Function '%1' is not implemented.").arg(func.toQStringView()));
1418 int asz = args.size() > 1 ? args.size() : args.at(0).isEmpty() ? 0 : 1;
1427 const ProKey &var = args.at(0).toKey();
1428 if (args.size() > 1) {
1429 if (args[1] == QLatin1String(
"test")) {
1430 return returnBool(m_functionDefs.testFunctions.contains(var));
1431 }
else if (args[1] == QLatin1String(
"replace")) {
1432 return returnBool(m_functionDefs.replaceFunctions.contains(var));
1433 }
else if (args[1] == QLatin1String(
"var")) {
1434 ProValueMap::Iterator it;
1435 return returnBool(findValues(var, &it));
1437 evalError(
fL1S(
"defined(function, type): unexpected type [%1].")
1438 .arg(args.at(1).toQStringView()));
1441 return returnBool(m_functionDefs.replaceFunctions.contains(var)
1442 || m_functionDefs.testFunctions.contains(var));
1445 const ProKey &var = map(args.at(0));
1446 for (ProValueMapStack::iterator vmi = m_valuemapStack.end();
1448 ProValueMap::Iterator it = (*vmi).find(var);
1449 if (it != (*vmi).end()) {
1450 if (it->constBegin() == statics.fakeValue.constBegin()) {
1466 evalError(
fL1S(
"discard_from() cannot be called from functions."));
1470 QString fn = resolvePath(u1.str());
1471 QMakeVfs::VfsFlags flags = (m_cumulative ? QMakeVfs::VfsCumulative : QMakeVfs::VfsExact);
1472 int pro = m_vfs->idForFileName(fn, flags | QMakeVfs::VfsAccessedOnly);
1476 for (
auto vit = vmap.begin(); vit != vmap.end(); ) {
1477 if (!vit->isEmpty()) {
1478 auto isFrom = [pro](
const ProString &s) {
1481 vit->removeIf(isFrom);
1482 if (vit->isEmpty()) {
1485 vit = vmap.erase(vit);
1491 for (
auto fit = m_functionDefs.testFunctions.begin(); fit != m_functionDefs.testFunctions.end(); ) {
1492 if (fit->pro()->id() == pro)
1493 fit = m_functionDefs.testFunctions.erase(fit);
1497 for (
auto fit = m_functionDefs.replaceFunctions.begin(); fit != m_functionDefs.replaceFunctions.end(); ) {
1498 if (fit->pro()->id() == pro)
1499 fit = m_functionDefs.replaceFunctions.erase(fit);
1511 QString fn = filePathEnvArg0(args);
1512 VisitReturn ok = evaluateFileInto(fn, &vars, LoadProOnly);
1515 if (args.size() == 2)
1517 QRegularExpression regx;
1518 regx.setPatternOptions(QRegularExpression::DotMatchesEverythingOption);
1520 const QString &qry = u1
.str();
1521 if (qry != QRegularExpression::escape(qry)) {
1522 regx.setPattern(QRegularExpression::anchoredPattern(qry));
1523 if (!regx.isValid()) {
1524 evalError(
fL1S(
"infile(): Encountered invalid regular expression '%1'.").arg(qry));
1528 const auto strings = vars.value(map(args.at(1)));
1529 for (
const ProString &s : strings) {
1532 if (!regx.pattern().isEmpty()) {
1533 ProStringRoUser u2(s, m_tmp[m_toggle ^= 1]);
1534 if (regx.match(u2.str()).hasMatch())
1541#ifdef PROEVALUATOR_FULL
1542 if (checkRequirements(args) == ReturnError)
1548 QString contents = args.join(statics.field_sep);
1552 m_locationStack.push(m_current);
1555 m_current = m_locationStack.pop();
1561 return evaluateConditional(args.at(0).toQStringView(),
1562 m_current.pro->fileName(), m_current.line);
1565 if (args.size() == 1)
1566 return returnBool(isActiveConfig(args.at(0).toQStringView()));
1567 const auto mutuals = args.at(1).toQStringView().split(QLatin1Char(
'|'),
1568 Qt::SkipEmptyParts);
1571 for (
int i = configs.size() - 1; i >= 0; i--) {
1572 for (
int mut = 0; mut < mutuals.size(); mut++) {
1573 if (configs[i].toQStringView() == mutuals[mut].trimmed())
1574 return returnBool(configs[i] == args[0]);
1581 const QString &qry = u1
.str();
1582 QRegularExpression regx;
1583 regx.setPatternOptions(QRegularExpression::DotMatchesEverythingOption);
1584 if (qry != QRegularExpression::escape(qry)) {
1585 regx.setPattern(QRegularExpression::anchoredPattern(qry));
1586 if (!regx.isValid()) {
1587 evalError(
fL1S(
"contains(): Encountered invalid regular expression '%1'.").arg(qry));
1592 if (args.size() == 2) {
1593 for (
int i = 0; i < l.size(); ++i) {
1597 if (!regx.pattern().isEmpty()) {
1599 if (regx.match(u2
.str()).hasMatch())
1604 const auto mutuals = args.at(2).toQStringView().split(QLatin1Char(
'|'),
1605 Qt::SkipEmptyParts);
1606 for (
int i = l.size() - 1; i >= 0; i--) {
1608 for (
int mut = 0; mut < mutuals.size(); mut++) {
1609 if (val.toQStringView() == mutuals[mut].trimmed()) {
1612 if (!regx.pattern().isEmpty()) {
1614 if (regx.match(u2
.str()).hasMatch())
1625 int cnt =
values(map(args.at(0))
).size();
1626 int val = args.at(1).toInt();
1627 if (args.size() == 3) {
1629 if (comp == QLatin1String(
">") || comp == QLatin1String(
"greaterThan")) {
1631 }
else if (comp == QLatin1String(
">=")) {
1633 }
else if (comp == QLatin1String(
"<") || comp == QLatin1String(
"lessThan")) {
1635 }
else if (comp == QLatin1String(
"<=")) {
1637 }
else if (comp == QLatin1String(
"equals") || comp == QLatin1String(
"isEqual")
1638 || comp == QLatin1String(
"=") || comp == QLatin1String(
"==")) {
1641 evalError(
fL1S(
"Unexpected modifier to count(%2).").arg(comp.toQStringView()));
1650 const QString &lhs = values(map(args.at(0))).join(statics.field_sep);
1654 int lhs_int = lhs.toInt(&ok);
1666 return returnBool(values(map(args.at(0))).join(statics.field_sep)
1667 == args.at(1).toQStringView());
1670 const QVersionNumber lvn = QVersionNumber::fromString(values(args.at(0).toKey()).join(QLatin1Char(
'.')));
1671 const QVersionNumber rvn = QVersionNumber::fromString(args.at(1).toQStringView());
1678 ProValueMap::Iterator it;
1679 const ProKey &var = map(args.at(0));
1680 if (!(hsh = findValues(var, &it)))
1685 m_valuemapStack.top()[var].clear();
1690 ProValueMap::Iterator it;
1691 const ProKey &var = map(args.at(0));
1692 if (!(hsh = findValues(var, &it)))
1696 else if (hsh == &m_valuemapStack.top())
1697 *it = statics.fakeValue;
1699 m_valuemapStack.top()[var] = statics.fakeValue;
1703 QByteArray json = values(args.at(0).toKey()).join(QLatin1Char(
' ')).toUtf8();
1705 QString parseInto = u1.str();
1713 if (args.size() >= 2) {
1714 if (!args.at(1).isEmpty())
1715 parseInto = args.at(1) + QLatin1Char(
'.');
1716 if (args.size() >= 3 && isTrue(args.at(2)))
1719 QString fn = filePathEnvArg0(args);
1721 if (parseInto.isEmpty()) {
1722 ok = evaluateFileChecked(fn, QMakeHandler::EvalIncludeFile, LoadProOnly | flags);
1725 if ((ok = evaluateFileInto(fn, &symbols, LoadAll | flags)) == ReturnTrue) {
1727 for (ProValueMap::ConstIterator
1728 it = m_valuemapStack.top().constBegin(),
1729 end = m_valuemapStack.top().constEnd();
1731 const ProString &ky = it.key();
1732 if (!ky.startsWith(parseInto))
1733 newMap[it.key()] = it.value();
1735 for (ProValueMap::ConstIterator it = symbols.constBegin();
1736 it != symbols.constEnd(); ++it) {
1737 if (!it.key().startsWith(QLatin1Char(
'.')))
1738 newMap.insert(ProKey(parseInto + it.key()), it.value());
1740 m_valuemapStack.top() = newMap;
1743 if (ok == ReturnFalse && (flags & LoadSilent))
1748 bool ignore_error = (args.size() == 2 && isTrue(args.at(1)));
1756#ifdef PROEVALUATOR_DEBUG
1757 int level = args.at(0).toInt();
1758 if (level <= m_debugLevel) {
1759 ProStringRoUser u1(args.at(1), m_tmp1);
1760 const QString &msg = m_option->expandEnvVars(u1.str());
1761 debugMsg(level,
"Project DEBUG: %s", qPrintable(msg));
1773 if (func_t ==
T_LOG) {
1774#ifdef PROEVALUATOR_FULL
1775 fputs(msg.toLatin1().constData(), stderr);
1777 }
else if (!msg.isEmpty() || func_t !=
T_ERROR) {
1779 m_handler->fileMessage(
1780 (func_t == T_ERROR ? QMakeHandler::ErrorMessage :
1781 func_t == T_WARNING ? QMakeHandler::WarningMessage :
1782 QMakeHandler::InfoMessage)
1783 | (m_cumulative ? QMakeHandler::CumulativeEvalMessage : 0),
1784 fL1S(
"Project %1: %2").arg(u2.str().toUpper(), msg));
1790#ifdef PROEVALUATOR_FULL
1793#if QT_CONFIG(process)
1795 proc.setProcessChannelMode(QProcess::ForwardedChannels);
1796 runProcess(&proc, args.at(0).toQString());
1797 return returnBool(proc.exitStatus() == QProcess::NormalExit && proc.exitCode() == 0);
1799 int ec = system((QLatin1String(
"cd ")
1800 + IoUtils::shellQuote(QDir::toNativeSeparators(currentDirectory()))
1801 + QLatin1String(
" && ") + args.at(0)).toLocal8Bit().constData());
1803 if (ec != -1 && WIFSIGNALED(ec) && (WTERMSIG(ec) == SIGQUIT || WTERMSIG(ec) == SIGINT))
1804 raise(WTERMSIG(ec));
1806 return returnBool(ec == 0);
1816 QString file = filePathEnvArg0(args);
1820 if (IoUtils::exists(file))
1822 int slsh = file.lastIndexOf(QLatin1Char(
'/'));
1823 QString fn = file.mid(slsh+1);
1824 if (fn.contains(QLatin1Char(
'*')) || fn.contains(QLatin1Char(
'?'))) {
1825 QString dirstr = file.left(slsh+1);
1827 if (!QDir(dirstr).entryList(QStringList(fn)).isEmpty())
1834#ifdef PROEVALUATOR_FULL
1835 QString fn = filePathArg0(args);
1836 if (!QDir::current().mkpath(fn)) {
1837 evalError(fL1S(
"Cannot create directory %1.").arg(QDir::toNativeSeparators(fn)));
1844 QIODevice::OpenMode mode = QIODevice::Truncate;
1845 QMakeVfs::VfsFlags flags = (m_cumulative ? QMakeVfs::VfsCumulative : QMakeVfs::VfsExact);
1847 if (args.size() >= 2) {
1849 if (!vals.isEmpty())
1850 contents = vals.join(QLatin1Char(
'\n')) + QLatin1Char(
'\n');
1851 if (args.size() >= 3) {
1852 const auto opts = split_value_list(args.at(2).toQStringView());
1853 for (
const ProString &opt : opts) {
1854 if (opt == QLatin1String(
"append")) {
1855 mode = QIODevice::Append;
1856 }
else if (opt == QLatin1String(
"exe")) {
1857 flags |= QMakeVfs::VfsExecutable;
1859 evalError(
fL1S(
"write_file(): invalid flag %1.").arg(opt.toQStringView()));
1865 QString path = filePathArg0(args);
1866 return writeFile(QString(), path, mode, flags, contents);
1869#ifdef PROEVALUATOR_FULL
1870 ProStringRoUser u1(args.at(0), m_tmp1);
1871 ProStringRoUser u2(args.at(1), m_tmp2);
1872 const QString &tfn = resolvePath(u1.str());
1873 const QString &rfn = resolvePath(u2.str());
1875 if (!IoUtils::touchFile(tfn, rfn, &error)) {
1883 return testFunc_cache(args);
1885#ifdef QT_BUILD_QMAKE
1886 m_option->reloadProperties();
1890 evalError(
fL1S(
"Function '%1' is not implemented.").arg(function.toQStringView()));