diff options
Diffstat (limited to 'qmake')
-rw-r--r-- | qmake/Makefile.unix | 6 | ||||
-rw-r--r-- | qmake/Makefile.win32 | 1 | ||||
-rw-r--r-- | qmake/doc/src/qmake-manual.qdoc | 85 | ||||
-rw-r--r-- | qmake/generators/mac/pbuilder_pbx.cpp | 4 | ||||
-rw-r--r-- | qmake/generators/makefile.cpp | 129 | ||||
-rw-r--r-- | qmake/generators/makefile.h | 7 | ||||
-rw-r--r-- | qmake/generators/unix/unixmake.cpp | 12 | ||||
-rw-r--r-- | qmake/generators/unix/unixmake2.cpp | 26 | ||||
-rw-r--r-- | qmake/generators/win32/mingw_make.cpp | 18 | ||||
-rw-r--r-- | qmake/generators/win32/mingw_make.h | 2 | ||||
-rw-r--r-- | qmake/generators/win32/msvc_nmake.cpp | 5 | ||||
-rw-r--r-- | qmake/generators/win32/winmakefile.cpp | 25 | ||||
-rw-r--r-- | qmake/generators/win32/winmakefile.h | 2 | ||||
-rw-r--r-- | qmake/library/proitems.h | 49 | ||||
-rw-r--r-- | qmake/library/qmakebuiltins.cpp | 1468 | ||||
-rw-r--r-- | qmake/library/qmakeevaluator.cpp | 23 | ||||
-rw-r--r-- | qmake/library/qmakeevaluator.h | 10 | ||||
-rw-r--r-- | qmake/library/qmakeevaluator_p.h | 22 | ||||
-rw-r--r-- | qmake/meta.cpp | 121 | ||||
-rw-r--r-- | qmake/meta.h | 9 | ||||
-rw-r--r-- | qmake/project.cpp | 10 |
21 files changed, 1009 insertions, 1025 deletions
diff --git a/qmake/Makefile.unix b/qmake/Makefile.unix index 426387f0c2..29461be5c5 100644 --- a/qmake/Makefile.unix +++ b/qmake/Makefile.unix @@ -18,7 +18,7 @@ OBJS = \ #qt code (please keep in order matching DEPEND_SRC) QOBJS = \ qtextcodec.o qutfcodec.o \ - qglobal.o qlogging.o qmalloc.o qnumeric.o qoperatingsystemversion.o qrandom.o \ + qendian.o qglobal.o qlogging.o qmalloc.o qnumeric.o qoperatingsystemversion.o qrandom.o \ qabstractfileengine.o qbuffer.o qdatastream.o qdebug.o \ qdir.o qdiriterator.o \ qfile.o qfiledevice.o qfileinfo.o qfilesystemengine.o \ @@ -68,6 +68,7 @@ DEPEND_SRC = \ $(QMKGENSRC)/xmloutput.cpp \ $(SOURCE_PATH)/src/corelib/codecs/qtextcodec.cpp \ $(SOURCE_PATH)/src/corelib/codecs/qutfcodec.cpp \ + $(SOURCE_PATH)/src/corelib/global/qendian.cpp \ $(SOURCE_PATH)/src/corelib/global/qglobal.cpp \ $(SOURCE_PATH)/src/corelib/global/qlibraryinfo.cpp \ $(SOURCE_PATH)/src/corelib/global/qlogging.cpp \ @@ -295,6 +296,9 @@ qdebug.o: $(SOURCE_PATH)/src/corelib/io/qdebug.cpp qmalloc.o: $(SOURCE_PATH)/src/corelib/global/qmalloc.cpp $(CXX) -c -o $@ $(CXXFLAGS) $< +qendian.o: $(SOURCE_PATH)/src/corelib/global/qendian.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $< + qglobal.o: $(SOURCE_PATH)/src/corelib/global/qglobal.cpp $(CXX) -c -o $@ $(CXXFLAGS) $< diff --git a/qmake/Makefile.win32 b/qmake/Makefile.win32 index 5292332187..a1699bd6f8 100644 --- a/qmake/Makefile.win32 +++ b/qmake/Makefile.win32 @@ -83,6 +83,7 @@ QTOBJS= \ qfsfileengine_win.obj \ qsystemlibrary.obj \ qfileinfo.obj \ + qendian.obj \ qglobal.obj \ qhash.obj \ qiodevice.obj \ diff --git a/qmake/doc/src/qmake-manual.qdoc b/qmake/doc/src/qmake-manual.qdoc index b07ded3888..409062cf49 100644 --- a/qmake/doc/src/qmake-manual.qdoc +++ b/qmake/doc/src/qmake-manual.qdoc @@ -956,14 +956,35 @@ default is used. \row \li thread \li Thread support is enabled. This is enabled when CONFIG includes \c qt, which is the default. + \row \li c99 \li C99 support is enabled. This option has no effect if + the compiler does not support C99, or can't select the C standard. + By default, the compiler default is used. + \row \li c11 \li C11 support is enabled. This option has no effect if + the compiler does not support C11, or can't select the C standard. + By default, the compiler default is used. + \row \li strict_c \li Disables support for C compiler extensions. + By default, they are enabled. \row \li c++11 \li C++11 support is enabled. This option has no effect if - the compiler does not support C++11. - By default, support is disabled. + the compiler does not support C++11, or can't select the C++ standard. + By default, support is enabled. \row \li c++14 \li C++14 support is enabled. This option has no effect if - the compiler does not support C++14. + the compiler does not support C++14, or can't select the C++ standard. + By default, the compiler default is used. + \row \li c++1z \li C++17 support is enabled. This option has no effect if + the compiler does not support C++17, or can't select the C++ standard. By default, support is disabled. + \row \li strict_c++ \li Disables support for C++ compiler extensions. + By default, they are enabled. \row \li depend_includepath \li Appending the value of INCLUDEPATH to DEPENDPATH is enabled. Set by default. + \row \li lrelease \li Run \c lrelease for all files listed in + \l TRANSLATIONS and \l EXTRA_TRANSLATIONS. If \c embed_translations + is not set, install the generated .qm files into + QM_FILES_INSTALL_PATH. Use QMAKE_LRELEASE_FLAGS to add options to + the lrelease call. Not set by default. + \row \li embed_translations \li Embed the generated translations from + \c lrelease in the executable, under \l{QM_FILES_RESOURCE_PREFIX}. + Requires \c lrelease to be set, too. Not set by default. \endtable When you use the \c debug_and_release option (which is the default under @@ -1149,6 +1170,24 @@ Specifies where to copy the \l{#TARGET}{target} dll. + \target EXTRA_TRANSLATIONS + \section1 EXTRA_TRANSLATIONS + + Specifies a list of translation (.ts) files that contain + translations of the user interface text into non-native languages. + + In contrast to \l TRANSLATIONS, translation files in \c EXTRA_TRANSLATIONS + will be processed only by \l{Using lrelease}{lrelease}, not + \l{Using lupdate}{lupdate}. + + You can use \l{CONFIG}{CONFIG += lrelease} to automatically compile the + files during the build, and + \l{CONFIG}{CONFIG += lrelease embed_translations} to make them available in + \l{The Qt Resource System}. + + See the \l{Qt Linguist Manual} for more information about + internationalization (i18n) and localization (l10n) with Qt. + \target FORMS \section1 FORMS @@ -1419,6 +1458,21 @@ \note Do not attempt to overwrite the value of this variable. + \target QM_FILES_RESOURCE_PREFIX + \section1 QM_FILES_RESOURCE_PREFIX + + Specifies the directory in the resource system where \c .qm files will + be made available by \l{CONFIG}{CONFIG += embed_translations}. + + The default is \c{:/i18n/}. + + \target QM_FILES_INSTALL_PATH + \section1 QM_FILES_INSTALL_PATH + + Specifies the target directory \c .qm files generated by + \l{CONFIG}{CONFIG += lrelease} will be installed to. Does not have any + effect if \l{CONFIG}{CONFIG += embed_translations} is set. + \target QMAKE_systemvariable \section1 QMAKE @@ -2134,6 +2188,12 @@ value of this variable is typically handled by qmake or \l{#QMAKESPEC}{qmake.conf} and rarely needs to be modified. + \target QMAKE_LRELEASE_FLAGS + \section1 QMAKE_LRELEASE_FLAGS + + List of additional options passed to \l{Using lrelease}{lrelease} when + enabled through \l{CONFIG}{CONFIG += lrelease}. + \section1 QMAKE_OBJECTIVE_CFLAGS Specifies the Objective C/C++ compiler flags for building @@ -2342,9 +2402,7 @@ This variable is used to customize the list of options passed to the \l{uic}{User Interface Compiler} in each of the build rules where it is - used. For example, \c{-no-stringliteral} can be passed to use QLatin1String - instead of QStringLiteral in generated code (which is the default for - dynamic libraries). + used. \section1 QMAKE_WATCHOS_DEPLOYMENT_TARGET @@ -2613,11 +2671,21 @@ determine how the project is built, it is necessary to declare TEMPLATE on the command line rather than use the \c -t option. + \target TRANSLATIONS \section1 TRANSLATIONS Specifies a list of translation (.ts) files that contain translations of the user interface text into non-native languages. + Translation files in \c TRANSLATIONS will be processed by both + \l{Using lrelease}{lrelease} and \l{Using lupdate} tools. Use + \l EXTRA_TRANSLATIONS if you want only \c lrelease to process a file. + + You can use \l{CONFIG}{CONFIG += lrelease} to automatically compile the + files during the build, and + \l{CONFIG}{CONFIG += lrelease embed_translations} to make them available in + \l{The Qt Resource System}. + See the \l{Qt Linguist Manual} for more information about internationalization (i18n) and localization (l10n) with Qt. @@ -4511,6 +4579,11 @@ \li The dependencies for the output only get generated from the depends member and from nowhere else. \row + \li dep_lines + \li The output from the .depend_command is interpreted to be one file + per line. The default is to split on whitespace and is maintained + only for backwards compatibility reasons. + \row \li no_link \li Indicates that the output should not be added to the list of objects to be linked in. diff --git a/qmake/generators/mac/pbuilder_pbx.cpp b/qmake/generators/mac/pbuilder_pbx.cpp index a0aea70dc5..57a1bff3a0 100644 --- a/qmake/generators/mac/pbuilder_pbx.cpp +++ b/qmake/generators/mac/pbuilder_pbx.cpp @@ -848,7 +848,8 @@ ProjectBuilderMakefileGenerator::writeMakeParts(QTextStream &t) encode the version number in the Project file which might be a bad things in days to come? --Sam */ - QString lib_file = QMakeMetaInfo::findLib(Option::normalizePath((*lit) + Option::dir_sep + lib)); + QString lib_file = QMakeMetaInfo::checkLib(Option::normalizePath( + (*lit) + Option::dir_sep + lib + Option::prl_ext)); if (!lib_file.isEmpty()) { QMakeMetaInfo libinfo(project); if(libinfo.readLib(lib_file)) { @@ -1782,6 +1783,7 @@ ProjectBuilderMakefileGenerator::writeMakeParts(QTextStream &t) schemeData.replace(QLatin1String("@QMAKE_ORIG_TARGET@"), target); schemeData.replace(QLatin1String("@TARGET_PBX_KEY@"), keyFor(pbx_dir + "QMAKE_PBX_TARGET")); schemeData.replace(QLatin1String("@TEST_BUNDLE_PBX_KEY@"), keyFor("QMAKE_TEST_BUNDLE_REFERENCE")); + schemeData.replace(QLatin1String("@QMAKE_RELATIVE_PBX_DIR@"), fileFixify(pbx_dir)); QTextStream outputSchemeStream(&outputSchemeFile); outputSchemeStream << schemeData; diff --git a/qmake/generators/makefile.cpp b/qmake/generators/makefile.cpp index 580df85c1e..cec4892e1f 100644 --- a/qmake/generators/makefile.cpp +++ b/qmake/generators/makefile.cpp @@ -876,20 +876,37 @@ MakefileGenerator::init() } bool -MakefileGenerator::processPrlFile(QString &file) +MakefileGenerator::processPrlFile(QString &file, bool baseOnly) { - bool try_replace_file = false; QString f = fileFixify(file, FileFixifyBackwards); - QString meta_file = QMakeMetaInfo::findLib(f); - if (!meta_file.isEmpty()) { - try_replace_file = true; - } else { - QString tmp = f; - int ext = tmp.lastIndexOf('.'); - if(ext != -1) - tmp = tmp.left(ext); - meta_file = QMakeMetaInfo::findLib(tmp); + // Explicitly given full .prl name + if (!baseOnly && f.endsWith(Option::prl_ext)) + return processPrlFileCore(file, QStringRef(), f); + // Explicitly given or derived (from -l) base name + if (processPrlFileCore(file, QStringRef(), f + Option::prl_ext)) + return true; + if (!baseOnly) { + // Explicitly given full library name + int off = qMax(f.lastIndexOf('/'), f.lastIndexOf('\\')) + 1; + int ext = f.midRef(off).lastIndexOf('.'); + if (ext != -1) + return processPrlFileBase(file, f.midRef(off), f.leftRef(off + ext), off); } + return false; +} + +bool +MakefileGenerator::processPrlFileBase(QString &origFile, const QStringRef &origName, + const QStringRef &fixedBase, int slashOff) +{ + return processPrlFileCore(origFile, origName, fixedBase + Option::prl_ext); +} + +bool +MakefileGenerator::processPrlFileCore(QString &origFile, const QStringRef &origName, + const QString &fixedFile) +{ + const QString meta_file = QMakeMetaInfo::checkLib(fixedFile); if (meta_file.isEmpty()) return false; QMakeMetaInfo libinfo(project); @@ -898,9 +915,33 @@ MakefileGenerator::processPrlFile(QString &file) fprintf(stderr, "Error processing meta file %s\n", meta_file.toLatin1().constData()); return false; } - if (project->isActiveConfig("no_read_prl_" + libinfo.type().toLower())) { - debug_msg(2, "Ignored meta file %s [%s]", - meta_file.toLatin1().constData(), libinfo.type().toLatin1().constData()); + if (project->isActiveConfig("no_read_prl_qmake")) { + debug_msg(2, "Ignored meta file %s", meta_file.toLatin1().constData()); + return false; + } + ProString tgt = libinfo.first("QMAKE_PRL_TARGET"); + if (tgt.isEmpty()) { + fprintf(stderr, "Error: %s does not define QMAKE_PRL_TARGET\n", + meta_file.toLatin1().constData()); + return false; + } + if (!tgt.contains('.') && !libinfo.values("QMAKE_PRL_CONFIG").contains("lib_bundle")) { + fprintf(stderr, "Error: %s defines QMAKE_PRL_TARGET without extension\n", + meta_file.toLatin1().constData()); + return false; + } + if (origName.isEmpty()) { + // We got a .prl file as input, replace it with an actual library. + int off = qMax(origFile.lastIndexOf('/'), origFile.lastIndexOf('\\')) + 1; + debug_msg(1, " Replacing library reference %s with %s", + origFile.mid(off).toLatin1().constData(), + tgt.toQString().toLatin1().constData()); + origFile.replace(off, 1000, tgt.toQString()); + } else if (tgt != origName) { + // We got an actual library as input, and found the wrong .prl for it. + debug_msg(2, "Mismatched meta file %s (want %s, got %s)", + meta_file.toLatin1().constData(), + origName.toLatin1().constData(), tgt.toLatin1().constData()); return false; } project->values("QMAKE_CURRENT_PRL_LIBS") = libinfo.values("QMAKE_PRL_LIBS"); @@ -909,23 +950,6 @@ MakefileGenerator::processPrlFile(QString &file) for (const ProString &def : libinfo.values("QMAKE_PRL_DEFINES")) if (!defs.contains(def) && prl_defs.contains(def)) defs.append(def); - if (try_replace_file) { - ProString tgt = libinfo.first("QMAKE_PRL_TARGET"); - if (tgt.isEmpty()) { - fprintf(stderr, "Error: %s does not define QMAKE_PRL_TARGET\n", - meta_file.toLatin1().constData()); - } else if (!tgt.contains('.') - && !libinfo.values("QMAKE_PRL_CONFIG").contains("lib_bundle")) { - fprintf(stderr, "Error: %s defines QMAKE_PRL_TARGET without extension\n", - meta_file.toLatin1().constData()); - } else { - int off = qMax(file.lastIndexOf('/'), file.lastIndexOf('\\')) + 1; - debug_msg(1, " Replacing library reference %s with %s", - file.mid(off).toLatin1().constData(), - tgt.toQString().toLatin1().constData()); - file.replace(off, 1000, tgt.toQString()); - } - } QString mf = fileFixify(meta_file); if (!project->values("QMAKE_PRL_INTERNAL_FILES").contains(mf)) project->values("QMAKE_PRL_INTERNAL_FILES").append(mf); @@ -1815,12 +1839,27 @@ MakefileGenerator::writeExtraTargets(QTextStream &t) } } +static QStringList splitDeps(const QString &indeps, bool lineMode) +{ + if (!lineMode) + return indeps.simplified().split(' '); + QStringList deps = indeps.split('\n', QString::SkipEmptyParts); +#ifdef Q_OS_WIN + for (auto &dep : deps) { + if (dep.endsWith(QLatin1Char('\r'))) + dep.chop(1); + } +#endif + return deps; +} + void MakefileGenerator::writeExtraCompilerTargets(QTextStream &t) { QString clean_targets; const ProStringList &quc = project->values("QMAKE_EXTRA_COMPILERS"); for (ProStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) { + const ProStringList &config = project->values(ProKey(*it + ".CONFIG")); QString tmp_out = fileFixify(project->first(ProKey(*it + ".output")).toQString(), FileFixifyFromOutdir); const QString tmp_cmd = project->values(ProKey(*it + ".commands")).join(' '); @@ -1831,6 +1870,7 @@ MakefileGenerator::writeExtraCompilerTargets(QTextStream &t) + IoUtils::shellQuote(Option::fixPathToLocalOS(Option::output_dir, false)) + QLatin1String(" && "); } + const bool dep_lines = (config.indexOf("dep_lines") != -1); const ProStringList &vars = project->values(ProKey(*it + ".variables")); if(tmp_out.isEmpty() || tmp_cmd.isEmpty()) continue; @@ -1847,7 +1887,6 @@ MakefileGenerator::writeExtraCompilerTargets(QTextStream &t) } t << "compiler_" << (*it) << "_make_all:"; - const ProStringList &config = project->values(ProKey(*it + ".CONFIG")); if (config.indexOf("combine") != -1) { // compilers with a combined input only have one output QString input = project->first(ProKey(*it + ".output")).toQString(); @@ -1954,12 +1993,13 @@ MakefileGenerator::writeExtraCompilerTargets(QTextStream &t) QT_PCLOSE(proc); if(!indeps.isEmpty()) { QDir outDir(Option::output_dir); - // ### This is basically fubar. Add 'lines' flag to CONFIG? - QStringList dep_cmd_deps = indeps.replace('\n', ' ').simplified().split(' '); + QStringList dep_cmd_deps = splitDeps(indeps, dep_lines); for(int i = 0; i < dep_cmd_deps.count(); ++i) { QString &file = dep_cmd_deps[i]; QString absFile = outDir.absoluteFilePath(file); - if (exists(absFile)) { + if (absFile == file) { + // already absolute; don't do any checks. + } else if (exists(absFile)) { file = absFile; } else { QString localFile; @@ -1978,7 +2018,7 @@ MakefileGenerator::writeExtraCompilerTargets(QTextStream &t) " prints paths relative to source directory", (*it).toLatin1().constData()); else - file.clear(); + file = absFile; // fallback for generated resources } else { file = localFile; } @@ -2048,12 +2088,13 @@ MakefileGenerator::writeExtraCompilerTargets(QTextStream &t) QT_PCLOSE(proc); if(!indeps.isEmpty()) { QDir outDir(Option::output_dir); - // ### This is basically fubar. Add 'lines' flag to CONFIG? - QStringList dep_cmd_deps = indeps.replace('\n', ' ').simplified().split(' '); + QStringList dep_cmd_deps = splitDeps(indeps, dep_lines); for(int i = 0; i < dep_cmd_deps.count(); ++i) { QString &file = dep_cmd_deps[i]; QString absFile = outDir.absoluteFilePath(file); - if (exists(absFile)) { + if (absFile == file) { + // already absolute; don't do any checks. + } else if (exists(absFile)) { file = absFile; } else { QString localFile; @@ -2072,7 +2113,7 @@ MakefileGenerator::writeExtraCompilerTargets(QTextStream &t) " prints paths relative to source directory", (*it).toLatin1().constData()); else - file.clear(); + file = absFile; // fallback for generated resources } else { file = localFile; } @@ -3306,10 +3347,8 @@ MakefileGenerator::writePkgConfigFile() t << "Libs: "; QString pkgConfiglibName; if (target_mode == TARG_MAC_MODE && project->isActiveConfig("lib_bundle")) { - if (libDir != QLatin1String("/System/Library/Frameworks") - && libDir != QLatin1String("/Library/Frameworks")) { + if (libDir != QLatin1String("/Library/Frameworks")) t << "-F${libdir} "; - } ProString bundle; if (!project->isEmpty("QMAKE_FRAMEWORK_BUNDLE_NAME")) bundle = project->first("QMAKE_FRAMEWORK_BUNDLE_NAME"); @@ -3354,6 +3393,10 @@ MakefileGenerator::writePkgConfigFile() ; if (!project->values("QMAKE_DEFAULT_INCDIRS").contains(includeDir)) t << "-I${includedir}"; + if (target_mode == TARG_MAC_MODE && project->isActiveConfig("lib_bundle") + && libDir != QLatin1String("/Library/Frameworks")) { + t << " -F${libdir}"; + } t << endl; // requires diff --git a/qmake/generators/makefile.h b/qmake/generators/makefile.h index f32bec650e..6b9bd43cae 100644 --- a/qmake/generators/makefile.h +++ b/qmake/generators/makefile.h @@ -198,7 +198,7 @@ protected: //for prl QString prlFileName(bool fixify=true); void writePrlFile(); - bool processPrlFile(QString &); + bool processPrlFile(QString &, bool baseOnly); virtual void writePrlFile(QTextStream &); //make sure libraries are found @@ -246,6 +246,11 @@ protected: QString installMetaFile(const ProKey &replace_rule, const QString &src, const QString &dst); + virtual bool processPrlFileBase(QString &origFile, const QStringRef &origName, + const QStringRef &fixedBase, int slashOff); + bool processPrlFileCore(QString &origFile, const QStringRef &origName, + const QString &fixedFile); + public: MakefileGenerator(); virtual ~MakefileGenerator(); diff --git a/qmake/generators/unix/unixmake.cpp b/qmake/generators/unix/unixmake.cpp index 894020d2bd..52d34ef513 100644 --- a/qmake/generators/unix/unixmake.cpp +++ b/qmake/generators/unix/unixmake.cpp @@ -443,7 +443,7 @@ UnixMakefileGenerator::findLibraries(bool linkPrl, bool mergeLflags) dep_it != libdirs.end(); ++dep_it) { QString libBase = (*dep_it).local() + '/' + project->first("QMAKE_PREFIX_SHLIB") + lib; - if (linkPrl && processPrlFile(libBase)) + if (linkPrl && processPrlFile(libBase, true)) goto found; for (ProStringList::Iterator extit = extens.begin(); extit != extens.end(); ++extit) { if (exists(libBase + '.' + (*extit))) @@ -471,12 +471,12 @@ UnixMakefileGenerator::findLibraries(bool linkPrl, bool mergeLflags) } for (const QMakeLocalFileName &dir : qAsConst(frameworkdirs)) { QString frameworkDirectory = dir.local() + "/" + frameworkName + + ".framework/"; - QString suffixedPrl = frameworkDirectory + opt + Option::prl_ext; - if (processPrlFile(suffixedPrl)) + QString suffixedPrl = frameworkDirectory + opt; + if (processPrlFile(suffixedPrl, true)) break; if (hasSuffix) { - QString unsuffixedPrl = frameworkDirectory + frameworkName + Option::prl_ext; - if (processPrlFile(unsuffixedPrl)) + QString unsuffixedPrl = frameworkDirectory + frameworkName; + if (processPrlFile(unsuffixedPrl, true)) break; } } @@ -487,7 +487,7 @@ UnixMakefileGenerator::findLibraries(bool linkPrl, bool mergeLflags) } } } else if (linkPrl) { - processPrlFile(opt); + processPrlFile(opt, false); } ProStringList &prl_libs = project->values("QMAKE_CURRENT_PRL_LIBS"); diff --git a/qmake/generators/unix/unixmake2.cpp b/qmake/generators/unix/unixmake2.cpp index ecb0de3b52..bda44eaa67 100644 --- a/qmake/generators/unix/unixmake2.cpp +++ b/qmake/generators/unix/unixmake2.cpp @@ -280,11 +280,11 @@ UnixMakefileGenerator::writeMakeParts(QTextStream &t) if (destd.endsWith('\\')) destd += '\\'; t << "DESTDIR = " << destd << endl; - t << "TARGET = " << fileVar("TARGET") << endl; // ### mixed use! + t << "TARGET = " << fileVar("TARGET") << endl; if(project->isActiveConfig("plugin")) { t << "TARGETD = " << fileVar("TARGET") << endl; } else if(!project->isActiveConfig("staticlib") && project->values("QMAKE_APP_FLAG").isEmpty()) { - t << "TARGETA = " << fileVar("TARGETA") << endl; // ### mixed use! + t << "TARGETA = " << fileVar("TARGETA") << endl; if(!project->isEmpty("QMAKE_BUNDLE")) { t << "TARGETD = " << fileVar("TARGET_x.y") << endl; t << "TARGET0 = " << fileVar("TARGET_") << endl; @@ -502,7 +502,7 @@ UnixMakefileGenerator::writeMakeParts(QTextStream &t) t << "\n\t" << var("QMAKE_POST_LINK"); t << endl << endl; } else { - t << "$(TARGET): " << depVar("PRE_TARGETDEPS") << " $(OBJECTS) " + t << depVar("TARGET") << ": " << depVar("PRE_TARGETDEPS") << " $(OBJECTS) " << target_deps << ' ' << depVar("POST_TARGETDEPS") << "\n\t"; if (project->first("TEMPLATE") != "aux") { if (!destdir.isEmpty()) @@ -515,7 +515,7 @@ UnixMakefileGenerator::writeMakeParts(QTextStream &t) } t << endl << endl; } - allDeps = " $(TARGET)"; + allDeps = ' ' + depVar("TARGET"); } else if(!project->isActiveConfig("staticlib")) { QString destdir_r = project->first("DESTDIR").toQString(), incr_deps; if(!project->isEmpty("QMAKE_BUNDLE")) { @@ -580,14 +580,14 @@ UnixMakefileGenerator::writeMakeParts(QTextStream &t) } //real target - t << destdir_d << "$(TARGET): " << depVar("PRE_TARGETDEPS") << ' ' + t << destdir_d << depVar("TARGET") << ": " << depVar("PRE_TARGETDEPS") << ' ' << incr_deps << " $(SUBLIBS) " << target_deps << ' ' << depVar("POST_TARGETDEPS"); } else { - t << destdir_d << "$(TARGET): " << depVar("PRE_TARGETDEPS") + t << destdir_d << depVar("TARGET") << ": " << depVar("PRE_TARGETDEPS") << " $(OBJECTS) $(SUBLIBS) $(OBJCOMP) " << target_deps << ' ' << depVar("POST_TARGETDEPS"); } - allDeps = ' ' + destdir_d + "$(TARGET)"; + allDeps = ' ' + destdir_d + depVar("TARGET"); if(!destdir.isEmpty()) t << "\n\t" << mkdir_p_asstring(destdir, false); if(!project->isEmpty("QMAKE_PRE_LINK")) @@ -604,7 +604,7 @@ UnixMakefileGenerator::writeMakeParts(QTextStream &t) t << "\n\t" << var("QMAKE_POST_LINK"); t << endl << endl; } else if(!project->isEmpty("QMAKE_BUNDLE")) { - bundledFiles << destdir_r + "$(TARGET)"; + bundledFiles << destdir_r + var("TARGET"); t << "\n\t" << "-$(DEL_FILE) $(TARGET) $(TARGET0) $(DESTDIR)$(TARGET0)\n\t" << var("QMAKE_LINK_SHLIB_CMD") << "\n\t" @@ -669,8 +669,8 @@ UnixMakefileGenerator::writeMakeParts(QTextStream &t) t << endl << endl; if (! project->isActiveConfig("plugin")) { - t << "staticlib: $(TARGETA)\n\n"; - t << "$(TARGETA): " << depVar("PRE_TARGETDEPS") << " $(OBJECTS) $(OBJCOMP)"; + t << "staticlib: " << depVar("TARGETA") << "\n\n"; + t << depVar("TARGETA") << ": " << depVar("PRE_TARGETDEPS") << " $(OBJECTS) $(OBJCOMP)"; if(do_incremental) t << " $(INCREMENTAL_OBJECTS)"; t << ' ' << depVar("POST_TARGETDEPS") << "\n\t"; @@ -690,11 +690,11 @@ UnixMakefileGenerator::writeMakeParts(QTextStream &t) QString destdir_r = project->first("DESTDIR").toQString(); QString destdir_d = escapeDependencyPath(destdir_r); QString destdir = escapeFilePath(destdir_r); - allDeps = ' ' + destdir_d + "$(TARGET)" + allDeps = ' ' + destdir_d + depVar("TARGET") + varGlue("QMAKE_AR_SUBLIBS", ' ' + destdir_d, ' ' + destdir_d, ""); t << "staticlib: " << destdir_d << "$(TARGET)\n\n"; if(project->isEmpty("QMAKE_AR_SUBLIBS")) { - t << destdir_d << "$(TARGET): " << depVar("PRE_TARGETDEPS") + t << destdir_d << depVar("TARGET") << ": " << depVar("PRE_TARGETDEPS") << " $(OBJECTS) $(OBJCOMP) " << depVar("POST_TARGETDEPS") << "\n\t"; if(!destdir.isEmpty()) t << mkdir_p_asstring(destdir, false) << "\n\t"; @@ -719,7 +719,7 @@ UnixMakefileGenerator::writeMakeParts(QTextStream &t) QString ar; ProString lib = destdir + escapeFilePath(*libit); if((*libit) == "$(TARGET)") { - t << destdir_d << "$(TARGET): " << depVar("PRE_TARGETDEPS") + t << destdir_d << depVar("TARGET") << ": " << depVar("PRE_TARGETDEPS") << ' ' << depVar("POST_TARGETDEPS") << valList(escapeDependencyPaths(build)) << "\n\t"; ar = project->first("QMAKE_AR_CMD").toQString(); ar.replace(QLatin1String("$(OBJECTS)"), escapeFilePaths(build).join(' ')); diff --git a/qmake/generators/win32/mingw_make.cpp b/qmake/generators/win32/mingw_make.cpp index 6fcfe96380..95f69dbbc3 100644 --- a/qmake/generators/win32/mingw_make.cpp +++ b/qmake/generators/win32/mingw_make.cpp @@ -73,6 +73,19 @@ MingwMakefileGenerator::parseLibFlag(const ProString &flag, ProString *arg) return MakefileGenerator::parseLibFlag(flag, arg); } +bool MingwMakefileGenerator::processPrlFileBase(QString &origFile, const QStringRef &origName, + const QStringRef &fixedBase, int slashOff) +{ + if (origName.startsWith("lib")) { + QString newFixedBase = fixedBase.left(slashOff) + fixedBase.mid(slashOff + 3); + if (Win32MakefileGenerator::processPrlFileBase(origFile, origName, + QStringRef(&newFixedBase), slashOff)) { + return true; + } + } + return Win32MakefileGenerator::processPrlFileBase(origFile, origName, fixedBase, slashOff); +} + bool MingwMakefileGenerator::writeMakefile(QTextStream &t) { writeHeader(t); @@ -312,8 +325,9 @@ void MingwMakefileGenerator::writeBuildRulesPart(QTextStream &t) { t << "first: all\n"; t << "all: " << escapeDependencyPath(fileFixify(Option::output.fileName())) - << ' ' << depVar("ALL_DEPS") << " $(DESTDIR_TARGET)\n\n"; - t << "$(DESTDIR_TARGET): " << depVar("PRE_TARGETDEPS") << " $(OBJECTS) " << depVar("POST_TARGETDEPS"); + << ' ' << depVar("ALL_DEPS") << ' ' << depVar("DEST_TARGET") << "\n\n"; + t << depVar("DEST_TARGET") << ": " + << depVar("PRE_TARGETDEPS") << " $(OBJECTS) " << depVar("POST_TARGETDEPS"); if (project->first("TEMPLATE") == "aux") { t << "\n\n"; return; diff --git a/qmake/generators/win32/mingw_make.h b/qmake/generators/win32/mingw_make.h index 6f041cfd4a..934d28456d 100644 --- a/qmake/generators/win32/mingw_make.h +++ b/qmake/generators/win32/mingw_make.h @@ -42,6 +42,8 @@ protected: using MakefileGenerator::escapeDependencyPath; virtual QString escapeDependencyPath(const QString &path) const; virtual ProString fixLibFlag(const ProString &lib); + virtual bool processPrlFileBase(QString &origFile, const QStringRef &origName, + const QStringRef &fixedBase, int slashOff); virtual QString getManifestFileForRcFile() const; bool writeMakefile(QTextStream &); void init(); diff --git a/qmake/generators/win32/msvc_nmake.cpp b/qmake/generators/win32/msvc_nmake.cpp index 92b4eb5054..a1e3c08a7e 100644 --- a/qmake/generators/win32/msvc_nmake.cpp +++ b/qmake/generators/win32/msvc_nmake.cpp @@ -584,8 +584,9 @@ void NmakeMakefileGenerator::writeBuildRulesPart(QTextStream &t) t << "first: all\n"; t << "all: " << escapeDependencyPath(fileFixify(Option::output.fileName())) - << ' ' << depVar("ALL_DEPS") << " $(DESTDIR_TARGET)\n\n"; - t << "$(DESTDIR_TARGET): " << depVar("PRE_TARGETDEPS") << " $(OBJECTS) " << depVar("POST_TARGETDEPS"); + << ' ' << depVar("ALL_DEPS") << ' ' << depVar("DEST_TARGET") << "\n\n"; + t << depVar("DEST_TARGET") << ": " + << depVar("PRE_TARGETDEPS") << " $(OBJECTS) " << depVar("POST_TARGETDEPS"); if (templateName == "aux") { t << "\n\n"; return; diff --git a/qmake/generators/win32/winmakefile.cpp b/qmake/generators/win32/winmakefile.cpp index bca27b7044..318ce50661 100644 --- a/qmake/generators/win32/winmakefile.cpp +++ b/qmake/generators/win32/winmakefile.cpp @@ -106,7 +106,7 @@ Win32MakefileGenerator::findLibraries(bool linkPrl, bool mergeLflags) for (QList<QMakeLocalFileName>::Iterator dir_it = dirs.begin(); dir_it != dirs.end(); ++dir_it) { QString cand = (*dir_it).real() + Option::dir_sep + lib; - if (linkPrl && processPrlFile(cand)) { + if (linkPrl && processPrlFile(cand, true)) { (*it) = cand; goto found; } @@ -124,13 +124,13 @@ Win32MakefileGenerator::findLibraries(bool linkPrl, bool mergeLflags) } else if (linkPrl && type == LibFlagFile) { QString lib = opt.toQString(); if (fileInfo(lib).isAbsolute()) { - if (processPrlFile(lib)) + if (processPrlFile(lib, false)) (*it) = lib; } else { for (QList<QMakeLocalFileName>::Iterator dir_it = dirs.begin(); dir_it != dirs.end(); ++dir_it) { QString cand = (*dir_it).real() + Option::dir_sep + lib; - if (processPrlFile(cand)) { + if (processPrlFile(cand, false)) { (*it) = cand; break; } @@ -163,6 +163,23 @@ Win32MakefileGenerator::findLibraries(bool linkPrl, bool mergeLflags) return true; } +bool Win32MakefileGenerator::processPrlFileBase(QString &origFile, const QStringRef &origName, + const QStringRef &fixedBase, int slashOff) +{ + if (MakefileGenerator::processPrlFileBase(origFile, origName, fixedBase, slashOff)) + return true; + for (int off = fixedBase.length(); off > slashOff; off--) { + if (!fixedBase.at(off - 1).isDigit()) { + if (off != fixedBase.length()) { + return MakefileGenerator::processPrlFileBase( + origFile, origName, fixedBase.left(off), slashOff); + } + break; + } + } + return false; +} + void Win32MakefileGenerator::processVars() { if (project->first("TEMPLATE").endsWith("aux")) @@ -563,7 +580,7 @@ void Win32MakefileGenerator::writeStandardParts(QTextStream &t) t << "DIST = " << fileVarList("DISTFILES") << ' ' << fileVarList("HEADERS") << ' ' << fileVarList("SOURCES") << endl; - t << "QMAKE_TARGET = " << fileVar("QMAKE_ORIG_TARGET") << endl; + t << "QMAKE_TARGET = " << fileVar("QMAKE_ORIG_TARGET") << endl; // unused // The comment is important to maintain variable compatibility with Unix // Makefiles, while not interpreting a trailing-slash as a linebreak t << "DESTDIR = " << escapeFilePath(destDir) << " #avoid trailing-slash linebreak\n"; diff --git a/qmake/generators/win32/winmakefile.h b/qmake/generators/win32/winmakefile.h index b85a6b67df..a2a0c9a63c 100644 --- a/qmake/generators/win32/winmakefile.h +++ b/qmake/generators/win32/winmakefile.h @@ -58,6 +58,8 @@ protected: virtual LibFlagType parseLibFlag(const ProString &flag, ProString *arg); virtual ProString fixLibFlag(const ProString &lib); + virtual bool processPrlFileBase(QString &origFile, const QStringRef &origName, + const QStringRef &fixedBase, int slashOff); void processVars(); void fixTargetExt(); diff --git a/qmake/library/proitems.h b/qmake/library/proitems.h index 1d7ebed3aa..fcb6869780 100644 --- a/qmake/library/proitems.h +++ b/qmake/library/proitems.h @@ -229,6 +229,55 @@ inline bool operator!=(const QString &that, const ProString &other) QTextStream &operator<<(QTextStream &t, const ProString &str); +// This class manages read-only access to a ProString via a raw data QString +// temporary, ensuring that the latter is accessed exclusively. +class ProStringRoUser +{ +public: + ProStringRoUser(QString &rs) + { + Q_ASSERT(rs.isDetached() || rs.isEmpty()); + m_rs = &rs; + } + ProStringRoUser(const ProString &ps, QString &rs) + : ProStringRoUser(rs) + { + ps.toQString(rs); + } + // No destructor, as a RAII pattern cannot be used: references to the + // temporary string can legitimately outlive instances of this class + // (if they are held by Qt, e.g. in QRegExp). + QString &set(const ProString &ps) { return ps.toQString(*m_rs); } + QString &str() { return *m_rs; } + +protected: + QString *m_rs; +}; + +// This class manages read-write access to a ProString via a raw data QString +// temporary, ensuring that the latter is accessed exclusively, and that raw +// data does not leak outside its source's refcounting. +class ProStringRwUser : public ProStringRoUser +{ +public: + ProStringRwUser(QString &rs) + : ProStringRoUser(rs), m_ps(0) {} + ProStringRwUser(const ProString &ps, QString &rs) + : ProStringRoUser(ps, rs), m_ps(&ps) {} + QString &set(const ProString &ps) { m_ps = &ps; return ProStringRoUser::set(ps); } + ProString extract(const QString &s) const + { return s.isSharedWith(*m_rs) ? *m_ps : ProString(s).setSource(*m_ps); } + ProString extract(const QString &s, const ProStringRwUser &other) const + { + if (other.m_ps && s.isSharedWith(*other.m_rs)) + return *other.m_ps; + return extract(s); + } + +private: + const ProString *m_ps; +}; + class ProStringList : public QVector<ProString> { public: ProStringList() {} diff --git a/qmake/library/qmakebuiltins.cpp b/qmake/library/qmakebuiltins.cpp index 6907dfc01d..fda1e1c593 100644 --- a/qmake/library/qmakebuiltins.cpp +++ b/qmake/library/qmakebuiltins.cpp @@ -105,109 +105,138 @@ enum TestFunc { T_MKPATH, T_WRITE_FILE, T_TOUCH, T_CACHE, T_RELOAD_PROPERTIES }; +QMakeBuiltin::QMakeBuiltin(const QMakeBuiltinInit &d) + : index(d.func), minArgs(qMax(0, d.min_args)), maxArgs(d.max_args) +{ + static const char * const nstr[6] = { "no", "one", "two", "three", "four", "five" }; + // For legacy reasons, there is actually no such thing as "no arguments" + // - there is only "empty first argument", which needs to be mapped back. + // -1 means "one, which may be empty", which is effectively zero, except + // for the error message if there are too many arguments. + int dmin = qAbs(d.min_args); + int dmax = d.max_args; + if (dmax == QMakeBuiltinInit::VarArgs) { + Q_ASSERT_X(dmin < 2, "init", d.name); + if (dmin == 1) { + Q_ASSERT_X(d.args != nullptr, "init", d.name); + usage = fL1S("%1(%2) requires at least one argument.") + .arg(fL1S(d.name), fL1S(d.args)); + } + return; + } + int arange = dmax - dmin; + Q_ASSERT_X(arange >= 0, "init", d.name); + Q_ASSERT_X(d.args != nullptr, "init", d.name); + usage = arange > 1 + ? fL1S("%1(%2) requires %3 to %4 arguments.") + .arg(fL1S(d.name), fL1S(d.args), fL1S(nstr[dmin]), fL1S(nstr[dmax])) + : arange > 0 + ? fL1S("%1(%2) requires %3 or %4 arguments.") + .arg(fL1S(d.name), fL1S(d.args), fL1S(nstr[dmin]), fL1S(nstr[dmax])) + : dmax != 1 + ? fL1S("%1(%2) requires %3 arguments.") + .arg(fL1S(d.name), fL1S(d.args), fL1S(nstr[dmax])) + : fL1S("%1(%2) requires one argument.") + .arg(fL1S(d.name), fL1S(d.args)); +} + void QMakeEvaluator::initFunctionStatics() { - static const struct { - const char * const name; - const ExpandFunc func; - } expandInits[] = { - { "member", E_MEMBER }, - { "str_member", E_STR_MEMBER }, - { "first", E_FIRST }, - { "take_first", E_TAKE_FIRST }, - { "last", E_LAST }, - { "take_last", E_TAKE_LAST }, - { "size", E_SIZE }, - { "str_size", E_STR_SIZE }, - { "cat", E_CAT }, - { "fromfile", E_FROMFILE }, - { "eval", E_EVAL }, - { "list", E_LIST }, - { "sprintf", E_SPRINTF }, - { "format_number", E_FORMAT_NUMBER }, - { "num_add", E_NUM_ADD }, - { "join", E_JOIN }, - { "split", E_SPLIT }, - { "basename", E_BASENAME }, - { "dirname", E_DIRNAME }, - { "section", E_SECTION }, - { "find", E_FIND }, - { "system", E_SYSTEM }, - { "unique", E_UNIQUE }, - { "sorted", E_SORTED }, - { "reverse", E_REVERSE }, - { "quote", E_QUOTE }, - { "escape_expand", E_ESCAPE_EXPAND }, - { "upper", E_UPPER }, - { "lower", E_LOWER }, - { "title", E_TITLE }, - { "re_escape", E_RE_ESCAPE }, - { "val_escape", E_VAL_ESCAPE }, - { "files", E_FILES }, - { "prompt", E_PROMPT }, - { "replace", E_REPLACE }, - { "sort_depends", E_SORT_DEPENDS }, - { "resolve_depends", E_RESOLVE_DEPENDS }, - { "enumerate_vars", E_ENUMERATE_VARS }, - { "shadowed", E_SHADOWED }, - { "absolute_path", E_ABSOLUTE_PATH }, - { "relative_path", E_RELATIVE_PATH }, - { "clean_path", E_CLEAN_PATH }, - { "system_path", E_SYSTEM_PATH }, - { "shell_path", E_SHELL_PATH }, - { "system_quote", E_SYSTEM_QUOTE }, - { "shell_quote", E_SHELL_QUOTE }, - { "getenv", E_GETENV }, + static const QMakeBuiltinInit expandInits[] = { + { "member", E_MEMBER, 1, 3, "var, [start, [end]]" }, + { "str_member", E_STR_MEMBER, -1, 3, "str, [start, [end]]" }, + { "first", E_FIRST, 1, 1, "var" }, + { "take_first", E_TAKE_FIRST, 1, 1, "var" }, + { "last", E_LAST, 1, 1, "var" }, + { "take_last", E_TAKE_LAST, 1, 1, "var" }, + { "size", E_SIZE, 1, 1, "var" }, + { "str_size", E_STR_SIZE, -1, 1, "str" }, + { "cat", E_CAT, 1, 2, "file, [mode=true|blob|lines]" }, + { "fromfile", E_FROMFILE, 2, 2, "file, var" }, + { "eval", E_EVAL, 1, 1, "var" }, + { "list", E_LIST, 0, QMakeBuiltinInit::VarArgs, nullptr }, + { "sprintf", E_SPRINTF, 1, QMakeBuiltinInit::VarArgs, "format, ..." }, + { "format_number", E_FORMAT_NUMBER, 1, 2, "number, [options...]" }, + { "num_add", E_NUM_ADD, 1, QMakeBuiltinInit::VarArgs, "num, ..." }, + { "join", E_JOIN, 1, 4, "var, [glue, [before, [after]]]" }, + { "split", E_SPLIT, 1, 2, "var, sep" }, + { "basename", E_BASENAME, 1, 1, "var" }, + { "dirname", E_DIRNAME, 1, 1, "var" }, + { "section", E_SECTION, 3, 4, "var, sep, begin, [end]" }, + { "find", E_FIND, 2, 2, "var, str" }, + { "system", E_SYSTEM, 1, 3, "command, [mode], [stsvar]" }, + { "unique", E_UNIQUE, 1, 1, "var" }, + { "sorted", E_SORTED, 1, 1, "var" }, + { "reverse", E_REVERSE, 1, 1, "var" }, + { "quote", E_QUOTE, 0, QMakeBuiltinInit::VarArgs, nullptr }, + { "escape_expand", E_ESCAPE_EXPAND, 0, QMakeBuiltinInit::VarArgs, nullptr }, + { "upper", E_UPPER, 0, QMakeBuiltinInit::VarArgs, nullptr }, + { "lower", E_LOWER, 0, QMakeBuiltinInit::VarArgs, nullptr }, + { "title", E_TITLE, 0, QMakeBuiltinInit::VarArgs, nullptr }, + { "re_escape", E_RE_ESCAPE, 0, QMakeBuiltinInit::VarArgs, nullptr }, + { "val_escape", E_VAL_ESCAPE, 1, 1, "var" }, + { "files", E_FILES, 1, 2, "pattern, [recursive=false]" }, + { "prompt", E_PROMPT, 1, 2, "question, [decorate=true]" }, + { "replace", E_REPLACE, 3, 3, "var, before, after" }, + { "sort_depends", E_SORT_DEPENDS, 1, 4, "var, [prefix, [suffixes, [prio-suffix]]]" }, + { "resolve_depends", E_RESOLVE_DEPENDS, 1, 4, "var, [prefix, [suffixes, [prio-suffix]]]" }, + { "enumerate_vars", E_ENUMERATE_VARS, 0, 0, "" }, + { "shadowed", E_SHADOWED, 1, 1, "path" }, + { "absolute_path", E_ABSOLUTE_PATH, -1, 2, "path, [base]" }, + { "relative_path", E_RELATIVE_PATH, -1, 2, "path, [base]" }, + { "clean_path", E_CLEAN_PATH, -1, 1, "path" }, + { "system_path", E_SYSTEM_PATH, -1, 1, "path" }, + { "shell_path", E_SHELL_PATH, -1, 1, "path" }, + { "system_quote", E_SYSTEM_QUOTE, -1, 1, "arg" }, + { "shell_quote", E_SHELL_QUOTE, -1, 1, "arg" }, + { "getenv", E_GETENV, 1, 1, "arg" }, }; statics.expands.reserve((int)(sizeof(expandInits)/sizeof(expandInits[0]))); for (unsigned i = 0; i < sizeof(expandInits)/sizeof(expandInits[0]); ++i) - statics.expands.insert(ProKey(expandInits[i].name), expandInits[i].func); + statics.expands.insert(ProKey(expandInits[i].name), QMakeBuiltin(expandInits[i])); - static const struct { - const char * const name; - const TestFunc func; - } testInits[] = { - { "requires", T_REQUIRES }, - { "greaterThan", T_GREATERTHAN }, - { "lessThan", T_LESSTHAN }, - { "equals", T_EQUALS }, - { "isEqual", T_EQUALS }, - { "versionAtLeast", T_VERSION_AT_LEAST }, - { "versionAtMost", T_VERSION_AT_MOST }, - { "exists", T_EXISTS }, - { "export", T_EXPORT }, - { "clear", T_CLEAR }, - { "unset", T_UNSET }, - { "eval", T_EVAL }, - { "CONFIG", T_CONFIG }, - { "if", T_IF }, - { "isActiveConfig", T_CONFIG }, - { "system", T_SYSTEM }, - { "discard_from", T_DISCARD_FROM }, - { "defined", T_DEFINED }, - { "contains", T_CONTAINS }, - { "infile", T_INFILE }, - { "count", T_COUNT }, - { "isEmpty", T_ISEMPTY }, + static const QMakeBuiltinInit testInits[] = { + { "requires", T_REQUIRES, 0, QMakeBuiltinInit::VarArgs, nullptr }, + { "greaterThan", T_GREATERTHAN, 2, 2, "var, val" }, + { "lessThan", T_LESSTHAN, 2, 2, "var, val" }, + { "equals", T_EQUALS, 2, 2, "var, val" }, + { "isEqual", T_EQUALS, 2, 2, "var, val" }, + { "versionAtLeast", T_VERSION_AT_LEAST, 2, 2, "var, version" }, + { "versionAtMost", T_VERSION_AT_MOST, 2, 2, "var, version" }, + { "exists", T_EXISTS, 1, 1, "file" }, + { "export", T_EXPORT, 1, 1, "var" }, + { "clear", T_CLEAR, 1, 1, "var" }, + { "unset", T_UNSET, 1, 1, "var" }, + { "eval", T_EVAL, 0, QMakeBuiltinInit::VarArgs, nullptr }, + { "CONFIG", T_CONFIG, 1, 2, "config, [mutuals]" }, + { "if", T_IF, 1, 1, "condition" }, + { "isActiveConfig", T_CONFIG, 1, 2, "config, [mutuals]" }, + { "system", T_SYSTEM, 1, 1, "exec" }, + { "discard_from", T_DISCARD_FROM, 1, 1, "file" }, + { "defined", T_DEFINED, 1, 2, "object, [\"test\"|\"replace\"|\"var\"]" }, + { "contains", T_CONTAINS, 2, 3, "var, val, [mutuals]" }, + { "infile", T_INFILE, 2, 3, "file, var, [values]" }, + { "count", T_COUNT, 2, 3, "var, count, [op=operator]" }, + { "isEmpty", T_ISEMPTY, 1, 1, "var" }, #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - { "parseJson", T_PARSE_JSON }, + { "parseJson", T_PARSE_JSON, 2, 2, "var, into" }, #endif - { "load", T_LOAD }, - { "include", T_INCLUDE }, - { "debug", T_DEBUG }, - { "log", T_LOG }, - { "message", T_MESSAGE }, - { "warning", T_WARNING }, - { "error", T_ERROR }, - { "mkpath", T_MKPATH }, - { "write_file", T_WRITE_FILE }, - { "touch", T_TOUCH }, - { "cache", T_CACHE }, - { "reload_properties", T_RELOAD_PROPERTIES }, + { "load", T_LOAD, 1, 2, "feature, [ignore_errors=false]" }, + { "include", T_INCLUDE, 1, 3, "file, [into, [silent]]" }, + { "debug", T_DEBUG, 2, 2, "level, message" }, + { "log", T_LOG, 1, 1, "message" }, + { "message", T_MESSAGE, 1, 1, "message" }, + { "warning", T_WARNING, 1, 1, "message" }, + { "error", T_ERROR, 0, 1, "message" }, + { "mkpath", T_MKPATH, 1, 1, "path" }, + { "write_file", T_WRITE_FILE, 1, 3, "name, [content var, [append] [exe]]" }, + { "touch", T_TOUCH, 2, 2, "file, reffile" }, + { "cache", T_CACHE, 0, 3, "[var], [set|add|sub] [transient] [super|stash], [srcvar]" }, + { "reload_properties", T_RELOAD_PROPERTIES, 0, 0, "" }, }; statics.functions.reserve((int)(sizeof(testInits)/sizeof(testInits[0]))); for (unsigned i = 0; i < sizeof(testInits)/sizeof(testInits[0]); ++i) - statics.functions.insert(ProKey(testInits[i].name), testInits[i].func); + statics.functions.insert(ProKey(testInits[i].name), QMakeBuiltin(testInits[i])); } static bool isTrue(const ProString &str) @@ -234,8 +263,9 @@ QMakeEvaluator::getMemberArgs(const ProKey &func, int srclen, const ProStringLis } } if (!ok) { - evalError(fL1S("%1() argument 2 (start) '%2' invalid.") - .arg(func.toQString(m_tmp1), start_str.toQString(m_tmp2))); + ProStringRoUser u1(func, m_tmp1); + ProStringRoUser u2(start_str, m_tmp2); + evalError(fL1S("%1() argument 2 (start) '%2' invalid.").arg(u1.str(), u2.str())); return false; } } else { @@ -243,8 +273,9 @@ QMakeEvaluator::getMemberArgs(const ProKey &func, int srclen, const ProStringLis if (args.count() == 3) *end = args.at(2).toInt(&ok); if (!ok) { - evalError(fL1S("%1() argument 3 (end) '%2' invalid.") - .arg(func.toQString(m_tmp1), args.at(2).toQString(m_tmp2))); + ProStringRoUser u1(func, m_tmp1); + ProStringRoUser u2(args.at(2), m_tmp2); + evalError(fL1S("%1() argument 3 (end) '%2' invalid.").arg(u1.str(), u2.str())); return false; } } @@ -550,11 +581,33 @@ void QMakeEvaluator::populateDeps( } } +QString QMakeEvaluator::filePathArg0(const ProStringList &args) +{ + ProStringRoUser u1(args.at(0), m_tmp1); + QString fn = resolvePath(u1.str()); + fn.detach(); + return fn; +} + +QString QMakeEvaluator::filePathEnvArg0(const ProStringList &args) +{ + ProStringRoUser u1(args.at(0), m_tmp1); + QString fn = resolvePath(m_option->expandEnvVars(u1.str())); + fn.detach(); + return fn; +} + QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinExpand( - int func_t, const ProKey &func, const ProStringList &args, ProStringList &ret) + const QMakeBuiltin &adef, const ProKey &func, const ProStringList &args, ProStringList &ret) { traceMsg("calling built-in $$%s(%s)", dbgKey(func), dbgSepStrList(args)); + int asz = args.size() > 1 ? args.size() : args.at(0).isEmpty() ? 0 : 1; + if (asz < adef.minArgs || asz > adef.maxArgs) { + evalError(adef.usage); + return ReturnTrue; + } + int func_t = adef.index; switch (func_t) { case E_BASENAME: case E_DIRNAME: @@ -565,319 +618,264 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinExpand( int beg = 0; int end = -1; if (func_t == E_SECTION) { - if (args.count() != 3 && args.count() != 4) { - evalError(fL1S("section(var, sep, begin, end) requires three or four arguments.")); - } else { - var = args[0]; - sep = args.at(1).toQString(); - beg = args.at(2).toInt(); - if (args.count() == 4) - end = args.at(3).toInt(); - } + var = args[0]; + sep = args.at(1).toQString(); + beg = args.at(2).toInt(); + if (args.count() == 4) + end = args.at(3).toInt(); } else { - if (args.count() != 1) { - evalError(fL1S("%1(var) requires one argument.").arg(func.toQStringView())); - } else { - var = args[0]; - regexp = true; - sep = QLatin1String("[\\\\/]"); - if (func_t == E_DIRNAME) - end = -2; - else - beg = -1; - } + var = args[0]; + regexp = true; + sep = QLatin1String("[\\\\/]"); + if (func_t == E_DIRNAME) + end = -2; + else + beg = -1; } if (!var.isEmpty()) { const auto strings = values(map(var)); if (regexp) { QRegExp sepRx(sep); for (const ProString &str : strings) { - const QString &rstr = str.toQString(m_tmp[m_toggle ^= 1]).section(sepRx, beg, end); - ret << (rstr.isSharedWith(m_tmp[m_toggle]) ? str : ProString(rstr).setSource(str)); + ProStringRwUser u1(str, m_tmp[m_toggle ^= 1]); + ret << u1.extract(u1.str().section(sepRx, beg, end)); } } else { for (const ProString &str : strings) { - const QString &rstr = str.toQString(m_tmp1).section(sep, beg, end); - ret << (rstr.isSharedWith(m_tmp1) ? str : ProString(rstr).setSource(str)); + ProStringRwUser u1(str, m_tmp1); + ret << u1.extract(u1.str().section(sep, beg, end)); } } } break; } - case E_SPRINTF: - if (args.count() < 1) { - evalError(fL1S("sprintf(format, ...) requires at least one argument.")); - } else { - QString tmp = args.at(0).toQString(m_tmp1); - for (int i = 1; i < args.count(); ++i) - tmp = tmp.arg(args.at(i).toQStringView()); - ret << (tmp.isSharedWith(m_tmp1) ? args.at(0) : ProString(tmp).setSource(args.at(0))); - } + case E_SPRINTF: { + ProStringRwUser u1(args.at(0), m_tmp1); + QString tmp = u1.str(); + for (int i = 1; i < args.count(); ++i) + tmp = tmp.arg(args.at(i).toQStringView()); + ret << u1.extract(tmp); break; - case E_FORMAT_NUMBER: - if (args.count() > 2) { - evalError(fL1S("format_number(number[, options...]) requires one or two arguments.")); - } 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) { - const auto opts = split_value_list(args.at(1).toQStringRef()); - for (const ProString &opt : opts) { - 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 { - evalError(fL1S("format_number(): invalid format option %1.") - .arg(opt.toQStringView())); - goto formfail; - } + } + case E_FORMAT_NUMBER: { + 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) { + const auto opts = split_value_list(args.at(1).toQStringRef()); + for (const ProString &opt : opts) { + 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 { + evalError(fL1S("format_number(): invalid format option %1.") + .arg(opt.toQStringView())); + goto allfail; } } - if (args.at(0).contains(QLatin1Char('.'))) { - evalError(fL1S("format_number(): floats are currently not supported.")); - break; + } + if (args.at(0).contains(QLatin1Char('.'))) { + evalError(fL1S("format_number(): floats are currently not supported.")); + break; + } + bool ok; + qlonglong num = args.at(0).toLongLong(&ok, ibase); + if (!ok) { + evalError(fL1S("format_number(): malformed number %2 for base %1.") + .arg(ibase).arg(args.at(0).toQStringView())); + 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 += ProString(outstr); + break; + } + case E_NUM_ADD: { + qlonglong sum = 0; + for (const ProString &arg : qAsConst(args)) { + if (arg.contains(QLatin1Char('.'))) { + evalError(fL1S("num_add(): floats are currently not supported.")); + goto allfail; } bool ok; - qlonglong num = args.at(0).toLongLong(&ok, ibase); + qlonglong num = arg.toLongLong(&ok); if (!ok) { - evalError(fL1S("format_number(): malformed number %2 for base %1.") - .arg(ibase).arg(args.at(0).toQStringView())); - 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; + evalError(fL1S("num_add(): malformed number %1.") + .arg(arg.toQStringView())); + goto allfail; } - ret += ProString(outstr); + sum += num; } - formfail: - break; - case E_NUM_ADD: - if (args.count() < 1 || args.at(0).isEmpty()) { - evalError(fL1S("num_add(num, ...) requires at least one argument.")); - } else { - qlonglong sum = 0; - for (const ProString &arg : qAsConst(args)) { - if (arg.contains(QLatin1Char('.'))) { - evalError(fL1S("num_add(): floats are currently not supported.")); - goto nafail; - } - bool ok; - qlonglong num = arg.toLongLong(&ok); - if (!ok) { - evalError(fL1S("num_add(): malformed number %1.") - .arg(arg.toQStringView())); - goto nafail; - } - sum += num; - } - ret += ProString(QString::number(sum)); - } - nafail: + ret += ProString(QString::number(sum)); break; + } case E_JOIN: { - if (args.count() < 1 || args.count() > 4) { - evalError(fL1S("join(var, glue, before, after) requires one to four arguments.")); - } else { - ProString glue, before, after; - if (args.count() >= 2) - glue = args.at(1); - if (args.count() >= 3) - before = args[2]; - if (args.count() == 4) - after = args[3]; - const ProStringList &var = values(map(args.at(0))); - if (!var.isEmpty()) { - int src = currentFileId(); - for (const ProString &v : var) - if (int s = v.sourceFile()) { - src = s; - break; - } - ret << ProString(before + var.join(glue) + after).setSource(src); - } + ProString glue, before, after; + if (args.count() >= 2) + glue = args.at(1); + if (args.count() >= 3) + before = args[2]; + if (args.count() == 4) + after = args[3]; + const ProStringList &var = values(map(args.at(0))); + if (!var.isEmpty()) { + int src = currentFileId(); + for (const ProString &v : var) + if (int s = v.sourceFile()) { + src = s; + break; + } + ret << ProString(before + var.join(glue) + after).setSource(src); } break; } - case E_SPLIT: - if (args.count() < 1 || args.count() > 2) { - evalError(fL1S("split(var, sep) requires one or two arguments.")); - } else { - const QString &sep = (args.count() == 2) ? args.at(1).toQString(m_tmp1) : statics.field_sep; - const auto vars = values(map(args.at(0))); - for (const ProString &var : vars) { - // FIXME: this is inconsistent with the "there are no empty strings" dogma. - const auto splits = var.toQStringRef().split(sep, QString::KeepEmptyParts); - for (const auto &splt : splits) - ret << ProString(splt).setSource(var); - } + case E_SPLIT: { + ProStringRoUser u1(m_tmp1); + const QString &sep = (args.count() == 2) ? u1.set(args.at(1)) : statics.field_sep; + const auto vars = values(map(args.at(0))); + for (const ProString &var : vars) { + // FIXME: this is inconsistent with the "there are no empty strings" dogma. + const auto splits = var.toQStringRef().split(sep, QString::KeepEmptyParts); + for (const auto &splt : splits) + ret << ProString(splt).setSource(var); } break; - case E_MEMBER: - if (args.count() < 1 || args.count() > 3) { - evalError(fL1S("member(var, start, end) requires one to three arguments.")); - } else { - const ProStringList &src = values(map(args.at(0))); - int start, end; - if (getMemberArgs(func, src.size(), args, &start, &end)) { - ret.reserve(qAbs(end - start) + 1); - if (start < end) { - for (int i = start; i <= end && src.size() >= i; i++) - ret += src.at(i); - } else { - for (int i = start; i >= end && src.size() >= i && i >= 0; i--) - ret += src.at(i); - } + } + case E_MEMBER: { + const ProStringList &src = values(map(args.at(0))); + int start, end; + if (getMemberArgs(func, src.size(), args, &start, &end)) { + ret.reserve(qAbs(end - start) + 1); + if (start < end) { + for (int i = start; i <= end && src.size() >= i; i++) + ret += src.at(i); + } else { + for (int i = start; i >= end && src.size() >= i && i >= 0; i--) + ret += src.at(i); } } break; - case E_STR_MEMBER: - if (args.count() < 1 || args.count() > 3) { - evalError(fL1S("str_member(str, start, end) requires one to three arguments.")); - } else { - const ProString &src = args.at(0); - int start, end; - if (getMemberArgs(func, src.size(), args, &start, &end)) { - QString res; - res.reserve(qAbs(end - start) + 1); - if (start < end) { - for (int i = start; i <= end && src.size() >= i; i++) - res += src.at(i); - } else { - for (int i = start; i >= end && src.size() >= i && i >= 0; i--) - res += src.at(i); - } - ret += ProString(res); + } + case E_STR_MEMBER: { + const ProString &src = args.at(0); + int start, end; + if (getMemberArgs(func, src.size(), args, &start, &end)) { + QString res; + res.reserve(qAbs(end - start) + 1); + if (start < end) { + for (int i = start; i <= end && src.size() >= i; i++) + res += src.at(i); + } else { + for (int i = start; i >= end && src.size() >= i && i >= 0; i--) + res += src.at(i); } + ret += ProString(res); } break; + } case E_FIRST: - case E_LAST: - if (args.count() != 1) { - evalError(fL1S("%1(var) requires one argument.").arg(func.toQStringView())); - } else { - const ProStringList &var = values(map(args.at(0))); - if (!var.isEmpty()) { - if (func_t == E_FIRST) - ret.append(var[0]); - else - ret.append(var.last()); - } + case E_LAST: { + const ProStringList &var = values(map(args.at(0))); + if (!var.isEmpty()) { + if (func_t == E_FIRST) + ret.append(var[0]); + else + ret.append(var.last()); } break; + } case E_TAKE_FIRST: - case E_TAKE_LAST: - if (args.count() != 1) { - evalError(fL1S("%1(var) requires one argument.").arg(func.toQStringView())); - } else { - ProStringList &var = valuesRef(map(args.at(0))); - if (!var.isEmpty()) { - if (func_t == E_TAKE_FIRST) - ret.append(var.takeFirst()); - else - ret.append(var.takeLast()); - } + case E_TAKE_LAST: { + ProStringList &var = valuesRef(map(args.at(0))); + if (!var.isEmpty()) { + if (func_t == E_TAKE_FIRST) + ret.append(var.takeFirst()); + else + ret.append(var.takeLast()); } break; + } case E_SIZE: - if (args.count() != 1) - evalError(fL1S("size(var) requires one argument.")); - else - ret.append(ProString(QString::number(values(map(args.at(0))).size()))); + ret.append(ProString(QString::number(values(map(args.at(0))).size()))); break; case E_STR_SIZE: - if (args.count() != 1) - evalError(fL1S("str_size(str) requires one argument.")); - else - ret.append(ProString(QString::number(args.at(0).size()))); + ret.append(ProString(QString::number(args.at(0).size()))); break; - case E_CAT: - if (args.count() < 1 || args.count() > 2) { - evalError(fL1S("cat(file, singleline=true) requires one or two arguments.")); - } else { - QString fn = resolvePath(m_option->expandEnvVars(args.at(0).toQString(m_tmp1))); - fn.detach(); - - bool blob = false; - bool lines = false; - bool singleLine = 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(fn); - if (qfile.open(QIODevice::ReadOnly)) { - QTextStream stream(&qfile); - if (blob) { - ret += ProString(stream.readAll()); - } else { - while (!stream.atEnd()) { - if (lines) { - ret += ProString(stream.readLine()); - } else { - const QString &line = stream.readLine(); - ret += split_value_list(QStringRef(&line).trimmed()); - if (!singleLine) - ret += ProString("\n"); - } + case E_CAT: { + bool blob = false; + bool lines = false; + bool singleLine = 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; + } + QString fn = filePathEnvArg0(args); + QFile qfile(fn); + if (qfile.open(QIODevice::ReadOnly)) { + QTextStream stream(&qfile); + if (blob) { + ret += ProString(stream.readAll()); + } else { + while (!stream.atEnd()) { + if (lines) { + ret += ProString(stream.readLine()); + } else { + const QString &line = stream.readLine(); + ret += split_value_list(QStringRef(&line).trimmed()); + if (!singleLine) + ret += ProString("\n"); } } } } break; - case E_FROMFILE: - if (args.count() != 2) { - evalError(fL1S("fromfile(file, variable) requires two arguments.")); - } else { - ProValueMap vars; - QString fn = resolvePath(m_option->expandEnvVars(args.at(0).toQString(m_tmp1))); - fn.detach(); - if (evaluateFileInto(fn, &vars, LoadProOnly) == ReturnTrue) - ret = vars.value(map(args.at(1))); - } + } + case E_FROMFILE: { + ProValueMap vars; + QString fn = filePathEnvArg0(args); + if (evaluateFileInto(fn, &vars, LoadProOnly) == ReturnTrue) + ret = vars.value(map(args.at(1))); break; + } case E_EVAL: - if (args.count() != 1) - evalError(fL1S("eval(variable) requires one argument.")); - else - ret += values(map(args.at(0))); + ret += values(map(args.at(0))); break; case E_LIST: { QString tmp; @@ -888,84 +886,68 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinExpand( lst += split_value_list(arg.toQStringRef(), arg.sourceFile()); // Relies on deep copy m_valuemapStack.top()[ret.at(0).toKey()] = lst; break; } - case E_FIND: - if (args.count() != 2) { - evalError(fL1S("find(var, str) requires two arguments.")); - } else { - QRegExp regx(args.at(1).toQString()); - const auto vals = values(map(args.at(0))); - for (const ProString &val : vals) { - if (regx.indexIn(val.toQString(m_tmp[m_toggle ^= 1])) != -1) - ret += val; - } + case E_FIND: { + QRegExp regx(args.at(1).toQString()); + const auto vals = values(map(args.at(0))); + for (const ProString &val : vals) { + ProStringRoUser u1(val, m_tmp[m_toggle ^= 1]); + if (regx.indexIn(u1.str()) != -1) + ret += val; } break; - case E_SYSTEM: - if (!m_skipLevel) { - if (args.count() < 1 || args.count() > 3) { - evalError(fL1S("system(command, [mode], [stsvar]) requires one to three arguments.")); + } + case E_SYSTEM: { + if (m_skipLevel) + break; + bool blob = false; + bool lines = false; + bool singleLine = 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; + } + int exitCode; + QByteArray bytes = getCommandOutput(args.at(0).toQString(), &exitCode); + if (args.count() > 2 && !args.at(2).isEmpty()) { + m_valuemapStack.top()[args.at(2).toKey()] = + ProStringList(ProString(QString::number(exitCode))); + } + if (lines) { + QTextStream stream(bytes); + while (!stream.atEnd()) + ret += ProString(stream.readLine()); + } else { + QString output = QString::fromLocal8Bit(bytes); + if (blob) { + ret += ProString(output); } else { - bool blob = false; - bool lines = false; - bool singleLine = 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; - } - int exitCode; - QByteArray bytes = getCommandOutput(args.at(0).toQString(), &exitCode); - if (args.count() > 2 && !args.at(2).isEmpty()) { - m_valuemapStack.top()[args.at(2).toKey()] = - ProStringList(ProString(QString::number(exitCode))); - } - if (lines) { - QTextStream stream(bytes); - while (!stream.atEnd()) - ret += ProString(stream.readLine()); - } else { - QString output = QString::fromLocal8Bit(bytes); - if (blob) { - ret += ProString(output); - } else { - output.replace(QLatin1Char('\t'), QLatin1Char(' ')); - if (singleLine) - output.replace(QLatin1Char('\n'), QLatin1Char(' ')); - ret += split_value_list(QStringRef(&output)); - } - } + output.replace(QLatin1Char('\t'), QLatin1Char(' ')); + if (singleLine) + output.replace(QLatin1Char('\n'), QLatin1Char(' ')); + ret += split_value_list(QStringRef(&output)); } } break; + } case E_UNIQUE: - if (args.count() != 1) { - evalError(fL1S("unique(var) requires one argument.")); - } else { - ret = values(map(args.at(0))); - ret.removeDuplicates(); - } + ret = values(map(args.at(0))); + ret.removeDuplicates(); break; case E_SORTED: - if (args.count() != 1) { - evalError(fL1S("sorted(var) requires one argument.")); - } else { - ret = values(map(args.at(0))); - std::sort(ret.begin(), ret.end()); - } + ret = values(map(args.at(0))); + std::sort(ret.begin(), ret.end()); break; - case E_REVERSE: - if (args.count() != 1) { - evalError(fL1S("reverse(var) requires one argument.")); - } else { - ProStringList var = values(args.at(0).toKey()); - for (int i = 0; i < var.size() / 2; i++) - qSwap(var[i], var[var.size() - i - 1]); - ret += var; - } + case E_REVERSE: { + ProStringList var = values(args.at(0).toKey()); + for (int i = 0; i < var.size() / 2; i++) + qSwap(var[i], var[var.size() - i - 1]); + ret += var; break; + } case E_QUOTE: ret += args; break; @@ -1004,25 +986,23 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinExpand( break; case E_RE_ESCAPE: for (int i = 0; i < args.size(); ++i) { - const QString &rstr = QRegExp::escape(args.at(i).toQString(m_tmp1)); - ret << (rstr.isSharedWith(m_tmp1) ? args.at(i) : ProString(rstr).setSource(args.at(i))); + ProStringRwUser u1(args.at(i), m_tmp1); + ret << u1.extract(QRegExp::escape(u1.str())); } break; - case E_VAL_ESCAPE: - if (args.count() != 1) { - evalError(fL1S("val_escape(var) requires one argument.")); - } else { - const ProStringList &vals = values(args.at(0).toKey()); - ret.reserve(vals.size()); - for (const ProString &str : vals) - ret += ProString(quoteValue(str)); - } + case E_VAL_ESCAPE: { + const ProStringList &vals = values(args.at(0).toKey()); + ret.reserve(vals.size()); + for (const ProString &str : vals) + ret += ProString(quoteValue(str)); break; + } case E_UPPER: case E_LOWER: case E_TITLE: for (int i = 0; i < args.count(); ++i) { - QString rstr = args.at(i).toQString(m_tmp1); + ProStringRwUser u1(args.at(i), m_tmp1); + QString rstr = u1.str(); if (func_t == E_UPPER) { rstr = rstr.toUpper(); } else { @@ -1030,134 +1010,119 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinExpand( if (func_t == E_TITLE && rstr.length() > 0) rstr[0] = rstr.at(0).toTitleCase(); } - ret << (rstr.isSharedWith(m_tmp1) ? args.at(i) : ProString(rstr).setSource(args.at(i))); + ret << u1.extract(rstr); } break; - case E_FILES: - if (args.count() != 1 && args.count() != 2) { - evalError(fL1S("files(pattern, recursive=false) requires one or two arguments.")); + case E_FILES: { + bool recursive = false; + if (args.count() == 2) + recursive = isTrue(args.at(1)); + QStringList dirs; + ProStringRoUser u1(args.at(0), m_tmp1); + QString r = m_option->expandEnvVars(u1.str()) + .replace(QLatin1Char('\\'), QLatin1Char('/')); + QString pfx; + if (IoUtils::isRelativePath(r)) { + pfx = currentDirectory(); + if (!pfx.endsWith(QLatin1Char('/'))) + pfx += QLatin1Char('/'); + } + int slash = r.lastIndexOf(QLatin1Char('/')); + if (slash != -1) { + dirs.append(r.left(slash+1)); + r = r.mid(slash+1); } else { - bool recursive = false; - if (args.count() == 2) - recursive = isTrue(args.at(1)); - QStringList dirs; - QString r = m_option->expandEnvVars(args.at(0).toQString(m_tmp1)) - .replace(QLatin1Char('\\'), QLatin1Char('/')); - QString pfx; - if (IoUtils::isRelativePath(r)) { - pfx = currentDirectory(); - if (!pfx.endsWith(QLatin1Char('/'))) - pfx += QLatin1Char('/'); - } - int slash = r.lastIndexOf(QLatin1Char('/')); - if (slash != -1) { - dirs.append(r.left(slash+1)); - r = r.mid(slash+1); - } else { - dirs.append(QString()); - } + dirs.append(QString()); + } - r.detach(); // Keep m_tmp out of QRegExp's cache - QRegExp regex(r, Qt::CaseSensitive, QRegExp::Wildcard); - for (int d = 0; d < dirs.count(); d++) { - QString dir = dirs[d]; - QDir qdir(pfx + dir); - for (int i = 0; i < (int)qdir.count(); ++i) { - if (qdir[i] == statics.strDot || qdir[i] == statics.strDotDot) - continue; - QString fname = dir + qdir[i]; - if (IoUtils::fileType(pfx + fname) == IoUtils::FileIsDir) { - if (recursive) - dirs.append(fname + QLatin1Char('/')); - } - if (regex.exactMatch(qdir[i])) - ret += ProString(fname).setSource(currentFileId()); + r.detach(); // Keep m_tmp out of QRegExp's cache + QRegExp regex(r, Qt::CaseSensitive, QRegExp::Wildcard); + for (int d = 0; d < dirs.count(); d++) { + QString dir = dirs[d]; + QDir qdir(pfx + dir); + for (int i = 0; i < (int)qdir.count(); ++i) { + if (qdir[i] == statics.strDot || qdir[i] == statics.strDotDot) + continue; + QString fname = dir + qdir[i]; + if (IoUtils::fileType(pfx + fname) == IoUtils::FileIsDir) { + if (recursive) + dirs.append(fname + QLatin1Char('/')); } + if (regex.exactMatch(qdir[i])) + ret += ProString(fname).setSource(currentFileId()); } } break; + } #ifdef PROEVALUATOR_FULL case E_PROMPT: { - if (args.count() != 1 && args.count() != 2) { - evalError(fL1S("prompt(question, [decorate=true]) requires one or two arguments.")); -// } else if (currentFileName() == QLatin1String("-")) { -// evalError(fL1S("prompt(question) cannot be used when '-o -' is used")); + ProStringRoUser u1(args.at(0), m_tmp1); + QString msg = m_option->expandEnvVars(u1.str()); + bool decorate = true; + if (args.count() == 2) + decorate = isTrue(args.at(1)); + if (decorate) { + if (!msg.endsWith(QLatin1Char('?'))) + msg += QLatin1Char('?'); + fprintf(stderr, "Project PROMPT: %s ", qPrintable(msg)); } else { - QString msg = m_option->expandEnvVars(args.at(0).toQString(m_tmp1)); - bool decorate = true; - if (args.count() == 2) - decorate = isTrue(args.at(1)); - if (decorate) { - if (!msg.endsWith(QLatin1Char('?'))) - msg += QLatin1Char('?'); - fprintf(stderr, "Project PROMPT: %s ", qPrintable(msg)); - } else { - fputs(qPrintable(msg), stderr); - } - QFile qfile; - if (qfile.open(stdin, QIODevice::ReadOnly)) { - QTextStream t(&qfile); - const QString &line = t.readLine(); - if (t.atEnd()) { - fputs("\n", stderr); - evalError(fL1S("Unexpected EOF.")); - return ReturnError; - } - ret = split_value_list(QStringRef(&line)); + fputs(qPrintable(msg), stderr); + } + QFile qfile; + if (qfile.open(stdin, QIODevice::ReadOnly)) { + QTextStream t(&qfile); + const QString &line = t.readLine(); + if (t.atEnd()) { + fputs("\n", stderr); + evalError(fL1S("Unexpected EOF.")); + return ReturnError; } + ret = split_value_list(QStringRef(&line)); } - break; } + break; + } #endif - case E_REPLACE: - if (args.count() != 3 ) { - evalError(fL1S("replace(var, before, after) requires three arguments.")); - } else { - const QRegExp before(args.at(1).toQString()); - const QString &after(args.at(2).toQString(m_tmp2)); - const auto vals = values(map(args.at(0))); - for (const ProString &val : vals) { - QString rstr = val.toQString(m_tmp1); - QString copy = rstr; // Force a detach on modify - rstr.replace(before, after); - ret << (rstr.isSharedWith(m_tmp1) - ? val - : rstr.isSharedWith(m_tmp2) - ? args.at(2) - : ProString(rstr).setSource(val)); - } + case E_REPLACE: { + const QRegExp before(args.at(1).toQString()); + ProStringRwUser u2(args.at(2), m_tmp2); + const QString &after = u2.str(); + const auto vals = values(map(args.at(0))); + for (const ProString &val : vals) { + ProStringRwUser u1(val, m_tmp1); + QString rstr = u1.str(); + QString copy = rstr; // Force a detach on modify + rstr.replace(before, after); + ret << u1.extract(rstr, u2); } break; + } case E_SORT_DEPENDS: - case E_RESOLVE_DEPENDS: - if (args.count() < 1 || args.count() > 4) { - evalError(fL1S("%1(var, [prefix, [suffixes, [prio-suffix]]]) requires one to four arguments.") - .arg(func.toQStringView())); - } else { - QHash<ProKey, QSet<ProKey> > dependencies; - ProValueMap dependees; - QMultiMap<int, ProString> rootSet; - ProStringList orgList = values(args.at(0).toKey()); - ProString prefix = args.count() < 2 ? ProString() : args.at(1); - ProString priosfx = args.count() < 4 ? ProString(".priority") : args.at(3); - populateDeps(orgList, prefix, - args.count() < 3 ? ProStringList(ProString(".depends")) - : split_value_list(args.at(2).toQStringRef()), - priosfx, dependencies, dependees, rootSet); - while (!rootSet.isEmpty()) { - QMultiMap<int, ProString>::iterator it = rootSet.begin(); - const ProString item = *it; - rootSet.erase(it); - if ((func_t == E_RESOLVE_DEPENDS) || orgList.contains(item)) - ret.prepend(item); - for (const ProString &dep : qAsConst(dependees[item.toKey()])) { - QSet<ProKey> &dset = dependencies[dep.toKey()]; - dset.remove(item.toKey()); - if (dset.isEmpty()) - rootSet.insert(first(ProKey(prefix + dep + priosfx)).toInt(), dep); - } + case E_RESOLVE_DEPENDS: { + QHash<ProKey, QSet<ProKey> > dependencies; + ProValueMap dependees; + QMultiMap<int, ProString> rootSet; + ProStringList orgList = values(args.at(0).toKey()); + ProString prefix = args.count() < 2 ? ProString() : args.at(1); + ProString priosfx = args.count() < 4 ? ProString(".priority") : args.at(3); + populateDeps(orgList, prefix, + args.count() < 3 ? ProStringList(ProString(".depends")) + : split_value_list(args.at(2).toQStringRef()), + priosfx, dependencies, dependees, rootSet); + while (!rootSet.isEmpty()) { + QMultiMap<int, ProString>::iterator it = rootSet.begin(); + const ProString item = *it; + rootSet.erase(it); + if ((func_t == E_RESOLVE_DEPENDS) || orgList.contains(item)) + ret.prepend(item); + for (const ProString &dep : qAsConst(dependees[item.toKey()])) { + QSet<ProKey> &dset = dependencies[dep.toKey()]; + dset.remove(item.toKey()); + if (dset.isEmpty()) + rootSet.insert(first(ProKey(prefix + dep + priosfx)).toInt(), dep); } } break; + } case E_ENUMERATE_VARS: { QSet<ProString> keys; for (const ProValueMap &vmap : qAsConst(m_valuemapStack)) @@ -1167,120 +1132,94 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinExpand( for (const ProString &key : qAsConst(keys)) ret << key; break; } - case E_SHADOWED: - if (args.count() != 1) { - evalError(fL1S("shadowed(path) requires one argument.")); - } else { - QString rstr = m_option->shadowedPath(resolvePath(args.at(0).toQString(m_tmp1))); - if (rstr.isEmpty()) - break; - ret << (rstr.isSharedWith(m_tmp1) ? args.at(0) : ProString(rstr).setSource(args.at(0))); - } + case E_SHADOWED: { + ProStringRwUser u1(args.at(0), m_tmp1); + QString rstr = m_option->shadowedPath(resolvePath(u1.str())); + if (!rstr.isEmpty()) + ret << u1.extract(rstr); break; - case E_ABSOLUTE_PATH: - if (args.count() > 2) { - evalError(fL1S("absolute_path(path[, base]) requires one or two arguments.")); - } else { - QString arg = args.at(0).toQString(m_tmp1); - QString baseDir = args.count() > 1 - ? IoUtils::resolvePath(currentDirectory(), args.at(1).toQString(m_tmp2)) - : currentDirectory(); - QString rstr = arg.isEmpty() ? baseDir : IoUtils::resolvePath(baseDir, arg); - ret << (rstr.isSharedWith(m_tmp1) - ? args.at(0) - : args.count() > 1 && rstr.isSharedWith(m_tmp2) - ? args.at(1) - : ProString(rstr).setSource(args.at(0))); - } + } + case E_ABSOLUTE_PATH: { + ProStringRwUser u1(args.at(0), m_tmp1); + ProStringRwUser u2(m_tmp2); + QString baseDir = args.count() > 1 + ? IoUtils::resolvePath(currentDirectory(), u2.set(args.at(1))) + : currentDirectory(); + QString rstr = u1.str().isEmpty() ? baseDir : IoUtils::resolvePath(baseDir, u1.str()); + ret << u1.extract(rstr, u2); break; - case E_RELATIVE_PATH: - if (args.count() > 2) { - evalError(fL1S("relative_path(path[, base]) requires one or two arguments.")); - } else { - QString arg = args.at(0).toQString(m_tmp1); - QString baseDir = args.count() > 1 - ? IoUtils::resolvePath(currentDirectory(), args.at(1).toQString(m_tmp2)) - : currentDirectory(); - QString absArg = arg.isEmpty() ? baseDir : IoUtils::resolvePath(baseDir, arg); - QString rstr = QDir(baseDir).relativeFilePath(absArg); - ret << (rstr.isSharedWith(m_tmp1) ? args.at(0) : ProString(rstr).setSource(args.at(0))); - } + } + case E_RELATIVE_PATH: { + ProStringRwUser u1(args.at(0), m_tmp1); + ProStringRoUser u2(m_tmp2); + QString baseDir = args.count() > 1 + ? IoUtils::resolvePath(currentDirectory(), u2.set(args.at(1))) + : currentDirectory(); + QString absArg = u1.str().isEmpty() ? baseDir : IoUtils::resolvePath(baseDir, u1.str()); + QString rstr = QDir(baseDir).relativeFilePath(absArg); + ret << u1.extract(rstr); break; - case E_CLEAN_PATH: - if (args.count() != 1) { - evalError(fL1S("clean_path(path) requires one argument.")); - } else { - QString rstr = QDir::cleanPath(args.at(0).toQString(m_tmp1)); - ret << (rstr.isSharedWith(m_tmp1) ? args.at(0) : ProString(rstr).setSource(args.at(0))); - } + } + case E_CLEAN_PATH: { + ProStringRwUser u1(args.at(0), m_tmp1); + ret << u1.extract(QDir::cleanPath(u1.str())); break; - case E_SYSTEM_PATH: - if (args.count() != 1) { - evalError(fL1S("system_path(path) requires one argument.")); - } else { - QString rstr = args.at(0).toQString(m_tmp1); + } + case E_SYSTEM_PATH: { + ProStringRwUser u1(args.at(0), m_tmp1); + QString rstr = u1.str(); #ifdef Q_OS_WIN - rstr.replace(QLatin1Char('/'), QLatin1Char('\\')); + rstr.replace(QLatin1Char('/'), QLatin1Char('\\')); #else - rstr.replace(QLatin1Char('\\'), QLatin1Char('/')); + rstr.replace(QLatin1Char('\\'), QLatin1Char('/')); #endif - ret << (rstr.isSharedWith(m_tmp1) ? args.at(0) : ProString(rstr).setSource(args.at(0))); - } + ret << u1.extract(rstr); break; - case E_SHELL_PATH: - if (args.count() != 1) { - evalError(fL1S("shell_path(path) requires one argument.")); + } + case E_SHELL_PATH: { + ProStringRwUser u1(args.at(0), m_tmp1); + QString rstr = u1.str(); + if (m_dirSep.startsWith(QLatin1Char('\\'))) { + rstr.replace(QLatin1Char('/'), QLatin1Char('\\')); } else { - QString rstr = args.at(0).toQString(m_tmp1); - if (m_dirSep.startsWith(QLatin1Char('\\'))) { - rstr.replace(QLatin1Char('/'), QLatin1Char('\\')); - } else { - rstr.replace(QLatin1Char('\\'), QLatin1Char('/')); + rstr.replace(QLatin1Char('\\'), QLatin1Char('/')); #ifdef Q_OS_WIN - // Convert d:/foo/bar to msys-style /d/foo/bar. - if (rstr.length() > 2 && rstr.at(1) == QLatin1Char(':') && rstr.at(2) == QLatin1Char('/')) { - rstr[1] = rstr.at(0); - rstr[0] = QLatin1Char('/'); - } -#endif + // Convert d:/foo/bar to msys-style /d/foo/bar. + if (rstr.length() > 2 && rstr.at(1) == QLatin1Char(':') && rstr.at(2) == QLatin1Char('/')) { + rstr[1] = rstr.at(0); + rstr[0] = QLatin1Char('/'); } - ret << (rstr.isSharedWith(m_tmp1) ? args.at(0) : ProString(rstr).setSource(args.at(0))); +#endif } + ret << u1.extract(rstr); break; - case E_SYSTEM_QUOTE: - if (args.count() != 1) { - evalError(fL1S("system_quote(arg) requires one argument.")); - } else { - QString rstr = IoUtils::shellQuote(args.at(0).toQString(m_tmp1)); - ret << (rstr.isSharedWith(m_tmp1) ? args.at(0) : ProString(rstr).setSource(args.at(0))); - } + } + case E_SYSTEM_QUOTE: { + ProStringRwUser u1(args.at(0), m_tmp1); + ret << u1.extract(IoUtils::shellQuote(u1.str())); break; - case E_SHELL_QUOTE: - if (args.count() != 1) { - evalError(fL1S("shell_quote(arg) requires one argument.")); - } else { - QString rstr = args.at(0).toQString(m_tmp1); - if (m_dirSep.startsWith(QLatin1Char('\\'))) - rstr = IoUtils::shellQuoteWin(rstr); - else - rstr = IoUtils::shellQuoteUnix(rstr); - ret << (rstr.isSharedWith(m_tmp1) ? args.at(0) : ProString(rstr).setSource(args.at(0))); - } + } + case E_SHELL_QUOTE: { + ProStringRwUser u1(args.at(0), m_tmp1); + QString rstr = u1.str(); + if (m_dirSep.startsWith(QLatin1Char('\\'))) + rstr = IoUtils::shellQuoteWin(rstr); + else + rstr = IoUtils::shellQuoteUnix(rstr); + ret << u1.extract(rstr); break; - case E_GETENV: - if (args.count() != 1) { - evalError(fL1S("getenv(arg) requires one argument.")); - } else { - const ProString &var = args.at(0); - const ProString &val = ProString(m_option->getEnv(var.toQString(m_tmp1))); - ret << val; - } + } + case E_GETENV: { + ProStringRoUser u1(args.at(0), m_tmp1); + ret << ProString(m_option->getEnv(u1.str())); break; + } default: evalError(fL1S("Function '%1' is not implemented.").arg(func.toQStringView())); break; } + allfail: return ReturnTrue; } @@ -1306,7 +1245,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::testFunc_cache(const ProStringList & } else if (opt == QLatin1String("sub")) { mode = CacheSub; } else { - evalError(fL1S("cache(): invalid flag %1.").arg(opt.toQString(m_tmp3))); + evalError(fL1S("cache(): invalid flag %1.").arg(opt.toQStringView())); return ReturnFalse; } } @@ -1450,17 +1389,18 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::testFunc_cache(const ProStringList & } QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( - int func_t, const ProKey &function, const ProStringList &args) + const QMakeInternal::QMakeBuiltin &adef, const ProKey &function, const ProStringList &args) { traceMsg("calling built-in %s(%s)", dbgKey(function), dbgSepStrList(args)); + int asz = args.size() > 1 ? args.size() : args.at(0).isEmpty() ? 0 : 1; + if (asz < adef.minArgs || asz > adef.maxArgs) { + evalError(adef.usage); + return ReturnFalse; + } + int func_t = adef.index; switch (func_t) { case T_DEFINED: { - if (args.count() < 1 || args.count() > 2) { - evalError(fL1S("defined(function, [\"test\"|\"replace\"|\"var\"])" - " requires one or two arguments.")); - return ReturnFalse; - } const ProKey &var = args.at(0).toKey(); if (args.count() > 1) { if (args[1] == QLatin1String("test")) { @@ -1479,10 +1419,6 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( || m_functionDefs.testFunctions.contains(var)); } case T_EXPORT: { - if (args.count() != 1) { - evalError(fL1S("export(variable) requires one argument.")); - return ReturnFalse; - } const ProKey &var = map(args.at(0)); for (ProValueMapStack::Iterator vmi = m_valuemapStack.end(); --vmi != m_valuemapStack.begin(); ) { @@ -1503,15 +1439,12 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( return ReturnTrue; } case T_DISCARD_FROM: { - if (args.count() != 1 || args.at(0).isEmpty()) { - evalError(fL1S("discard_from(file) requires one argument.")); - return ReturnFalse; - } if (m_valuemapStack.count() != 1) { evalError(fL1S("discard_from() cannot be called from functions.")); return ReturnFalse; } - QString fn = resolvePath(args.at(0).toQString(m_tmp1)); + ProStringRoUser u1(args.at(0), m_tmp1); + QString fn = resolvePath(u1.str()); QMakeVfs::VfsFlags flags = (m_cumulative ? QMakeVfs::VfsCumulative : QMakeVfs::VfsExact); int pro = m_vfs->idForFileName(fn, flags | QMakeVfs::VfsAccessedOnly); if (!pro) @@ -1550,32 +1483,34 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( iif.removeAt(idx); return ReturnTrue; } - case T_INFILE: - if (args.count() < 2 || args.count() > 3) { - evalError(fL1S("infile(file, var, [values]) requires two or three arguments.")); - } else { - ProValueMap vars; - QString fn = resolvePath(m_option->expandEnvVars(args.at(0).toQString(m_tmp1))); - fn.detach(); - VisitReturn ok = evaluateFileInto(fn, &vars, LoadProOnly); - if (ok != ReturnTrue) - return ok; - if (args.count() == 2) - return returnBool(vars.contains(map(args.at(1)))); - QRegExp regx; - const QString &qry = args.at(2).toQString(m_tmp1); - if (qry != QRegExp::escape(qry)) { - QString copy = qry; - copy.detach(); - regx.setPattern(copy); - } - const auto strings = vars.value(map(args.at(1))); - for (const ProString &s : strings) { - if ((!regx.isEmpty() && regx.exactMatch(s.toQString(m_tmp[m_toggle ^= 1]))) || s == qry) + case T_INFILE: { + ProValueMap vars; + QString fn = filePathEnvArg0(args); + VisitReturn ok = evaluateFileInto(fn, &vars, LoadProOnly); + if (ok != ReturnTrue) + return ok; + if (args.count() == 2) + return returnBool(vars.contains(map(args.at(1)))); + QRegExp regx; + ProStringRoUser u1(args.at(2), m_tmp1); + const QString &qry = u1.str(); + if (qry != QRegExp::escape(qry)) { + QString copy = qry; + copy.detach(); + regx.setPattern(copy); + } + const auto strings = vars.value(map(args.at(1))); + for (const ProString &s : strings) { + if (s == qry) + return ReturnTrue; + if (!regx.isEmpty()) { + ProStringRoUser u2(s, m_tmp[m_toggle ^= 1]); + if (regx.exactMatch(u2.str())) return ReturnTrue; } } return ReturnFalse; + } case T_REQUIRES: #ifdef PROEVALUATOR_FULL if (checkRequirements(args) == ReturnError) @@ -1583,32 +1518,24 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( #endif return ReturnFalse; // Another qmake breakage case T_EVAL: { - VisitReturn ret = ReturnFalse; - QString contents = args.join(statics.field_sep); - ProFile *pro = m_parser->parsedProBlock(QStringRef(&contents), - 0, m_current.pro->fileName(), m_current.line); - if (m_cumulative || pro->isOk()) { - m_locationStack.push(m_current); - visitProBlock(pro, pro->tokPtr()); - ret = ReturnTrue; // This return value is not too useful, but that's qmake - m_current = m_locationStack.pop(); - } - pro->deref(); - return ret; - } + VisitReturn ret = ReturnFalse; + QString contents = args.join(statics.field_sep); + ProFile *pro = m_parser->parsedProBlock(QStringRef(&contents), + 0, m_current.pro->fileName(), m_current.line); + if (m_cumulative || pro->isOk()) { + m_locationStack.push(m_current); + visitProBlock(pro, pro->tokPtr()); + ret = ReturnTrue; // This return value is not too useful, but that's qmake + m_current = m_locationStack.pop(); + } + pro->deref(); + return ret; + } case T_IF: { - if (args.count() != 1) { - evalError(fL1S("if(condition) requires one argument.")); - return ReturnFalse; - } return evaluateConditional(args.at(0).toQStringRef(), m_current.pro->fileName(), m_current.line); } case T_CONFIG: { - if (args.count() < 1 || args.count() > 2) { - evalError(fL1S("CONFIG(config) requires one or two arguments.")); - return ReturnFalse; - } if (args.count() == 1) return returnBool(isActiveConfig(args.at(0).toQStringRef())); const auto mutuals = args.at(1).toQStringRef().split(QLatin1Char('|'), @@ -1624,12 +1551,8 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( return ReturnFalse; } case T_CONTAINS: { - if (args.count() < 2 || args.count() > 3) { - evalError(fL1S("contains(var, val) requires two or three arguments.")); - return ReturnFalse; - } - - const QString &qry = args.at(1).toQString(m_tmp1); + ProStringRoUser u1(args.at(1), m_tmp1); + const QString &qry = u1.str(); QRegExp regx; if (qry != QRegExp::escape(qry)) { QString copy = qry; @@ -1640,19 +1563,29 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( if (args.count() == 2) { for (int i = 0; i < l.size(); ++i) { const ProString &val = l[i]; - if ((!regx.isEmpty() && regx.exactMatch(val.toQString(m_tmp[m_toggle ^= 1]))) || val == qry) + if (val == qry) return ReturnTrue; + if (!regx.isEmpty()) { + ProStringRoUser u2(val, m_tmp[m_toggle ^= 1]); + if (regx.exactMatch(u2.str())) + return ReturnTrue; + } } } else { const auto mutuals = args.at(2).toQStringRef().split(QLatin1Char('|'), QString::SkipEmptyParts); for (int i = l.size() - 1; i >= 0; i--) { - const ProString val = l[i]; + const ProString &val = l[i]; for (int mut = 0; mut < mutuals.count(); mut++) { if (val.toQStringRef() == mutuals[mut].trimmed()) { - return returnBool((!regx.isEmpty() - && regx.exactMatch(val.toQString(m_tmp[m_toggle ^= 1]))) - || val == qry); + if (val == qry) + return ReturnTrue; + if (!regx.isEmpty()) { + ProStringRoUser u2(val, m_tmp[m_toggle ^= 1]); + if (regx.exactMatch(u2.str())) + return ReturnTrue; + } + return ReturnFalse; } } } @@ -1660,10 +1593,6 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( return ReturnFalse; } case T_COUNT: { - if (args.count() != 2 && args.count() != 3) { - evalError(fL1S("count(var, count, op=\"equals\") requires two or three arguments.")); - return ReturnFalse; - } int cnt = values(map(args.at(0))).count(); int val = args.at(1).toInt(); if (args.count() == 3) { @@ -1688,11 +1617,6 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( } case T_GREATERTHAN: case T_LESSTHAN: { - if (args.count() != 2) { - evalError(fL1S("%1(variable, value) requires two arguments.") - .arg(function.toQStringView())); - return ReturnFalse; - } const ProString &rhs = args.at(1); const QString &lhs = values(map(args.at(0))).join(statics.field_sep); bool ok; @@ -1710,20 +1634,10 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( return returnBool(lhs < rhs.toQStringRef()); } case T_EQUALS: - if (args.count() != 2) { - evalError(fL1S("%1(variable, value) requires two arguments.") - .arg(function.toQStringView())); - return ReturnFalse; - } return returnBool(values(map(args.at(0))).join(statics.field_sep) == args.at(1).toQStringView()); case T_VERSION_AT_LEAST: case T_VERSION_AT_MOST: { - if (args.count() != 2) { - evalError(fL1S("%1(variable, versionNumber) requires two arguments.") - .arg(function.toQStringView())); - return ReturnFalse; - } const QVersionNumber lvn = QVersionNumber::fromString(values(args.at(0).toKey()).join('.')); const QVersionNumber rvn = QVersionNumber::fromString(args.at(1).toQStringView()); if (func_t == T_VERSION_AT_LEAST) @@ -1731,11 +1645,6 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( return returnBool(lvn <= rvn); } case T_CLEAR: { - if (args.count() != 1) { - evalError(fL1S("%1(variable) requires one argument.") - .arg(function.toQStringView())); - return ReturnFalse; - } ProValueMap *hsh; ProValueMap::Iterator it; const ProKey &var = map(args.at(0)); @@ -1748,11 +1657,6 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( return ReturnTrue; } case T_UNSET: { - if (args.count() != 1) { - evalError(fL1S("%1(variable) requires one argument.") - .arg(function.toQStringView())); - return ReturnFalse; - } ProValueMap *hsh; ProValueMap::Iterator it; const ProKey &var = map(args.at(0)); @@ -1768,21 +1672,13 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( } #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) case T_PARSE_JSON: { - if (args.count() != 2) { - evalError(fL1S("parseJson(variable, into) requires two arguments.")); - return ReturnFalse; - } - QByteArray json = values(args.at(0).toKey()).join(QLatin1Char(' ')).toUtf8(); - QString parseInto = args.at(1).toQString(m_tmp2); + ProStringRoUser u1(args.at(1), m_tmp2); + QString parseInto = u1.str(); return parseJsonInto(json, parseInto, &m_valuemapStack.top()); } #endif case T_INCLUDE: { - if (args.count() < 1 || args.count() > 3) { - evalError(fL1S("include(file, [into, [silent]]) requires one, two or three arguments.")); - return ReturnFalse; - } QString parseInto; LoadFlags flags = 0; if (m_cumulative) @@ -1793,8 +1689,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( if (args.count() >= 3 && isTrue(args.at(2))) flags = LoadSilent; } - QString fn = resolvePath(m_option->expandEnvVars(args.at(0).toQString(m_tmp1))); - fn.detach(); + QString fn = filePathEnvArg0(args); VisitReturn ok; if (parseInto.isEmpty()) { ok = evaluateFileChecked(fn, QMakeHandler::EvalIncludeFile, LoadProOnly | flags); @@ -1812,9 +1707,8 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( } for (ProValueMap::ConstIterator it = symbols.constBegin(); it != symbols.constEnd(); ++it) { - const QString &ky = it.key().toQString(m_tmp1); - if (!ky.startsWith(QLatin1Char('.'))) - newMap.insert(ProKey(parseInto + ky), it.value()); + if (!it.key().startsWith(QLatin1Char('.'))) + newMap.insert(ProKey(parseInto + it.key()), it.value()); } m_valuemapStack.top() = newMap; } @@ -1824,13 +1718,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( return ok; } case T_LOAD: { - bool ignore_error = false; - if (args.count() == 2) { - ignore_error = isTrue(args.at(1)); - } else if (args.count() != 1) { - evalError(fL1S("load(feature) requires one or two arguments.")); - return ReturnFalse; - } + bool ignore_error = (args.count() == 2 && isTrue(args.at(1))); VisitReturn ok = evaluateFeatureFile(m_option->expandEnvVars(args.at(0).toQString()), ignore_error); if (ok == ReturnFalse && ignore_error) @@ -1839,13 +1727,10 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( } case T_DEBUG: { #ifdef PROEVALUATOR_DEBUG - if (args.count() != 2) { - evalError(fL1S("debug(level, message) requires two arguments.")); - return ReturnFalse; - } int level = args.at(0).toInt(); if (level <= m_debugLevel) { - const QString &msg = m_option->expandEnvVars(args.at(1).toQString(m_tmp2)); + ProStringRoUser u1(args.at(1), m_tmp1); + const QString &msg = m_option->expandEnvVars(u1.str()); debugMsg(level, "Project DEBUG: %s", qPrintable(msg)); } #endif @@ -1855,33 +1740,26 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( case T_ERROR: case T_WARNING: case T_MESSAGE: { - if (args.count() != 1) { - evalError(fL1S("%1(message) requires one argument.") - .arg(function.toQStringView())); - return ReturnFalse; - } - const QString &msg = m_option->expandEnvVars(args.at(0).toQString(m_tmp2)); + ProStringRoUser u1(args.at(0), m_tmp1); + const QString &msg = m_option->expandEnvVars(u1.str()); if (!m_skipLevel) { if (func_t == T_LOG) { #ifdef PROEVALUATOR_FULL fputs(msg.toLatin1().constData(), stderr); #endif } else if (!msg.isEmpty() || func_t != T_ERROR) { + ProStringRoUser u2(function, m_tmp2); m_handler->fileMessage( (func_t == T_ERROR ? QMakeHandler::ErrorMessage : func_t == T_WARNING ? QMakeHandler::WarningMessage : QMakeHandler::InfoMessage) | (m_cumulative ? QMakeHandler::CumulativeEvalMessage : 0), - fL1S("Project %1: %2").arg(function.toQString(m_tmp1).toUpper(), msg)); + fL1S("Project %1: %2").arg(u2.str().toUpper(), msg)); } } return (func_t == T_ERROR && !m_cumulative) ? ReturnError : ReturnTrue; } case T_SYSTEM: { - if (args.count() != 1) { - evalError(fL1S("system(exec) requires one argument.")); - return ReturnFalse; - } #ifdef PROEVALUATOR_FULL if (m_cumulative) // Anything else would be insanity return ReturnFalse; @@ -1905,19 +1783,10 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( #endif } case T_ISEMPTY: { - if (args.count() != 1) { - evalError(fL1S("isEmpty(var) requires one argument.")); - return ReturnFalse; - } return returnBool(values(map(args.at(0))).isEmpty()); } case T_EXISTS: { - if (args.count() != 1) { - evalError(fL1S("exists(file) requires one argument.")); - return ReturnFalse; - } - const QString &file = resolvePath(m_option->expandEnvVars(args.at(0).toQString(m_tmp1))); - + QString file = filePathEnvArg0(args); // Don't use VFS here: // - it supports neither listing nor even directories // - it's unlikely that somebody would test for files they created themselves @@ -1925,7 +1794,6 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( return ReturnTrue; int slsh = file.lastIndexOf(QLatin1Char('/')); QString fn = file.mid(slsh+1); - fn.detach(); if (fn.contains(QLatin1Char('*')) || fn.contains(QLatin1Char('?'))) { QString dirstr = file.left(slsh+1); dirstr.detach(); @@ -1936,13 +1804,8 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( return ReturnFalse; } case T_MKPATH: { - if (args.count() != 1) { - evalError(fL1S("mkpath(file) requires one argument.")); - return ReturnFalse; - } #ifdef PROEVALUATOR_FULL - QString fn = resolvePath(args.at(0).toQString(m_tmp1)); - fn.detach(); + QString fn = filePathArg0(args); if (!QDir::current().mkpath(fn)) { evalError(fL1S("Cannot create directory %1.").arg(QDir::toNativeSeparators(fn))); return ReturnFalse; @@ -1951,10 +1814,6 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( return ReturnTrue; } case T_WRITE_FILE: { - if (args.count() > 3) { - evalError(fL1S("write_file(name, [content var, [append] [exe]]) requires one to three arguments.")); - return ReturnFalse; - } QIODevice::OpenMode mode = QIODevice::Truncate; QMakeVfs::VfsFlags flags = (m_cumulative ? QMakeVfs::VfsCumulative : QMakeVfs::VfsExact); QString contents; @@ -1970,24 +1829,21 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( } else if (opt == QLatin1String("exe")) { flags |= QMakeVfs::VfsExecutable; } else { - evalError(fL1S("write_file(): invalid flag %1.").arg(opt.toQString(m_tmp3))); + evalError(fL1S("write_file(): invalid flag %1.").arg(opt.toQStringView())); return ReturnFalse; } } } } - QString path = resolvePath(args.at(0).toQString(m_tmp1)); - path.detach(); // make sure to not leak m_tmp1 into the map of written files. + QString path = filePathArg0(args); return writeFile(QString(), path, mode, flags, contents); } case T_TOUCH: { - if (args.count() != 2) { - evalError(fL1S("touch(file, reffile) requires two arguments.")); - return ReturnFalse; - } #ifdef PROEVALUATOR_FULL - const QString &tfn = resolvePath(args.at(0).toQString(m_tmp1)); - const QString &rfn = resolvePath(args.at(1).toQString(m_tmp2)); + ProStringRoUser u1(args.at(0), m_tmp1); + ProStringRoUser u2(args.at(1), m_tmp2); + const QString &tfn = resolvePath(u1.str()); + const QString &rfn = resolvePath(u2.str()); QString error; if (!IoUtils::touchFile(tfn, rfn, &error)) { evalError(error); @@ -1997,10 +1853,6 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( return ReturnTrue; } case T_CACHE: - if (args.count() > 3) { - evalError(fL1S("cache(var, [set|add|sub] [transient] [super|stash], [srcvar]) requires one to three arguments.")); - return ReturnFalse; - } return testFunc_cache(args); case T_RELOAD_PROPERTIES: #ifdef QT_BUILD_QMAKE diff --git a/qmake/library/qmakeevaluator.cpp b/qmake/library/qmakeevaluator.cpp index 6b23412327..ea2233f201 100644 --- a/qmake/library/qmakeevaluator.cpp +++ b/qmake/library/qmakeevaluator.cpp @@ -337,7 +337,8 @@ static void replaceInList(ProStringList *varlist, const QRegExp ®exp, const QString &replace, bool global, QString &tmp) { for (ProStringList::Iterator varit = varlist->begin(); varit != varlist->end(); ) { - QString val = varit->toQString(tmp); + ProStringRoUser u1(*varit, tmp); + QString val = u1.str(); QString copy = val; // Force detach and have a reference value val.replace(regexp, replace); if (!val.isSharedWith(copy) && val != copy) { @@ -1336,7 +1337,8 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateConfigFeatures() bool finished = true; ProStringList configs = values(statics.strCONFIG); for (int i = configs.size() - 1; i >= 0; --i) { - QString config = configs.at(i).toQString(m_tmp1).toLower(); + ProStringRoUser u1(configs.at(i), m_tmp1); + QString config = u1.str().toLower(); if (!processed.contains(config)) { config.detach(); processed.insert(config); @@ -1640,7 +1642,8 @@ bool QMakeEvaluator::isActiveConfig(const QStringRef &config, bool regex) // CONFIG variable const auto configValues = values(statics.strCONFIG); for (const ProString &configValue : configValues) { - if (re.exactMatch(configValue.toQString(m_tmp[m_toggle ^= 1]))) + ProStringRoUser u1(configValue, m_tmp[m_toggle ^= 1]); + if (re.exactMatch(u1.str())) return true; } } else { @@ -1749,9 +1752,9 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBoolFunction( if (val) return ReturnTrue; } else { + ProStringRoUser u1(function, m_tmp1); evalError(fL1S("Unexpected return value from test '%1': %2.") - .arg(function.toQString(m_tmp1)) - .arg(ret.join(QLatin1String(" :: ")))); + .arg(u1.str(), ret.join(QLatin1String(" :: ")))); } } return ReturnFalse; @@ -1762,12 +1765,13 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBoolFunction( QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateConditionalFunction( const ProKey &func, const ushort *&tokPtr) { - if (int func_t = statics.functions.value(func)) { + auto adef = statics.functions.constFind(func); + if (adef != statics.functions.constEnd()) { //why don't the builtin functions just use args_list? --Sam ProStringList args; if (expandVariableReferences(tokPtr, 5, &args, true) == ReturnError) return ReturnError; - return evaluateBuiltinConditional(func_t, func, args); + return evaluateBuiltinConditional(*adef, func, args); } QHash<ProKey, ProFunctionDef>::ConstIterator it = @@ -1788,12 +1792,13 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateConditionalFunction( QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateExpandFunction( const ProKey &func, const ushort *&tokPtr, ProStringList *ret) { - if (int func_t = statics.expands.value(func)) { + auto adef = statics.expands.constFind(func); + if (adef != statics.expands.constEnd()) { //why don't the builtin functions just use args_list? --Sam ProStringList args; if (expandVariableReferences(tokPtr, 5, &args, true) == ReturnError) return ReturnError; - return evaluateBuiltinExpand(func_t, func, args, *ret); + return evaluateBuiltinExpand(*adef, func, args, *ret); } QHash<ProKey, ProFunctionDef>::ConstIterator it = diff --git a/qmake/library/qmakeevaluator.h b/qmake/library/qmakeevaluator.h index dc35e1ddab..b87aaa0eec 100644 --- a/qmake/library/qmakeevaluator.h +++ b/qmake/library/qmakeevaluator.h @@ -105,6 +105,8 @@ public: const ProValueMap &top() const { return last(); } }; +namespace QMakeInternal { struct QMakeBuiltin; } + class QMAKE_EXPORT QMakeEvaluator { public: @@ -186,6 +188,8 @@ public: int currentFileId() const; QString resolvePath(const QString &fileName) const { return QMakeInternal::IoUtils::resolvePath(currentDirectory(), fileName); } + QString filePathArg0(const ProStringList &args); + QString filePathEnvArg0(const ProStringList &args); VisitReturn evaluateFile(const QString &fileName, QMakeHandler::EvalFileType type, LoadFlags flags); @@ -214,8 +218,10 @@ public: VisitReturn evaluateExpandFunction(const ProKey &function, const ushort *&tokPtr, ProStringList *ret); VisitReturn evaluateConditionalFunction(const ProKey &function, const ushort *&tokPtr); - VisitReturn evaluateBuiltinExpand(int func_t, const ProKey &function, const ProStringList &args, ProStringList &ret); - VisitReturn evaluateBuiltinConditional(int func_t, const ProKey &function, const ProStringList &args); + VisitReturn evaluateBuiltinExpand(const QMakeInternal::QMakeBuiltin &adef, + const ProKey &function, const ProStringList &args, ProStringList &ret); + VisitReturn evaluateBuiltinConditional(const QMakeInternal::QMakeBuiltin &adef, + const ProKey &function, const ProStringList &args); VisitReturn evaluateConditional(const QStringRef &cond, const QString &where, int line = -1); #ifdef PROEVALUATOR_FULL diff --git a/qmake/library/qmakeevaluator_p.h b/qmake/library/qmakeevaluator_p.h index f386a49108..f888bc5765 100644 --- a/qmake/library/qmakeevaluator_p.h +++ b/qmake/library/qmakeevaluator_p.h @@ -64,6 +64,22 @@ QT_BEGIN_NAMESPACE namespace QMakeInternal { +struct QMakeBuiltinInit +{ + const char *name; + int func; + enum { VarArgs = 1000 }; + int min_args, max_args; + const char *args; +}; + +struct QMakeBuiltin +{ + QMakeBuiltin(const QMakeBuiltinInit &data); + QString usage; + int index, minArgs, maxArgs; +}; + struct QMakeStatics { QString field_sep; QString strtrue; @@ -83,8 +99,8 @@ struct QMakeStatics { #ifdef PROEVALUATOR_FULL ProKey strREQUIRES; #endif - QHash<ProKey, int> expands; - QHash<ProKey, int> functions; + QHash<ProKey, QMakeBuiltin> expands; + QHash<ProKey, QMakeBuiltin> functions; QHash<ProKey, ProKey> varMap; ProStringList fakeValue; }; @@ -93,6 +109,8 @@ extern QMakeStatics statics; } +Q_DECLARE_TYPEINFO(QMakeInternal::QMakeBuiltin, Q_MOVABLE_TYPE); + QT_END_NAMESPACE #endif // QMAKEEVALUATOR_P_H diff --git a/qmake/meta.cpp b/qmake/meta.cpp index b47645b07f..2e8759b8ba 100644 --- a/qmake/meta.cpp +++ b/qmake/meta.cpp @@ -50,49 +50,19 @@ QMakeMetaInfo::readLib(const QString &meta_file) return true; } - bool ret = false; - if(!meta_file.isNull()) { - if(meta_file.endsWith(Option::pkgcfg_ext)) { - if((ret=readPkgCfgFile(meta_file))) - meta_type = "pkgcfg"; - } else if(meta_file.endsWith(Option::libtool_ext)) { - if((ret=readLibtoolFile(meta_file))) - meta_type = "libtool"; - } else if(meta_file.endsWith(Option::prl_ext)) { - QMakeProject proj; - if (!proj.read(Option::normalizePath(meta_file), QMakeEvaluator::LoadProOnly)) - return false; - meta_type = "qmake"; - vars = proj.variables(); - ret = true; - } else { - warn_msg(WarnLogic, "QMakeMetaInfo: unknown file format for %s", - QDir::toNativeSeparators(meta_file).toLatin1().constData()); - } - } - if(ret) - cache_vars.insert(meta_file, vars); - return ret; + QMakeProject proj; + if (!proj.read(Option::normalizePath(meta_file), QMakeEvaluator::LoadProOnly)) + return false; + vars = proj.variables(); + cache_vars.insert(meta_file, vars); + return true; } QString -QMakeMetaInfo::findLib(const QString &lib) +QMakeMetaInfo::checkLib(const QString &lib) { - QString ret; - QString extns[] = { Option::prl_ext, /*Option::pkgcfg_ext, Option::libtool_ext,*/ QString() }; - for(int extn = 0; !extns[extn].isNull(); extn++) { - if(lib.endsWith(extns[extn])) - ret = QFile::exists(lib) ? lib : QString(); - } - if(ret.isNull()) { - for(int extn = 0; !extns[extn].isNull(); extn++) { - if(QFile::exists(lib + extns[extn])) { - ret = lib + extns[extn]; - break; - } - } - } + QString ret = QFile::exists(lib) ? lib : QString(); if(ret.isNull()) { debug_msg(2, "QMakeMetaInfo: Cannot find info file for %s", lib.toLatin1().constData()); } else { @@ -101,79 +71,4 @@ QMakeMetaInfo::findLib(const QString &lib) return ret; } - -bool -QMakeMetaInfo::readLibtoolFile(const QString &f) -{ - /* I can just run the .la through the .pro parser since they are compatible.. */ - QMakeProject proj; - QString nf = Option::normalizePath(f); - if (!proj.read(nf, QMakeEvaluator::LoadProOnly)) - return false; - QString dirf = nf.section(QLatin1Char('/'), 0, -2); - if(dirf == nf) - dirf = ""; - else if(!dirf.isEmpty() && !dirf.endsWith(Option::output_dir)) - dirf += QLatin1Char('/'); - const ProValueMap &v = proj.variables(); - for (ProValueMap::ConstIterator it = v.begin(); it != v.end(); ++it) { - ProStringList lst = it.value(); - if(lst.count() == 1 && (lst.first().startsWith("'") || lst.first().startsWith("\"")) && - lst.first().endsWith(QString(lst.first().at(0)))) - lst = ProStringList(lst.first().mid(1, lst.first().length() - 2)); - if(!vars.contains("QMAKE_PRL_TARGET") && - (it.key() == "dlname" || it.key() == "library_names" || it.key() == "old_library")) { - ProString dir = v["libdir"].first(); - if ((dir.startsWith('\'') || dir.startsWith('"')) && dir.endsWith(dir.at(0))) - dir = dir.mid(1, dir.length() - 2); - dir = dir.trimmed(); - if(!dir.isEmpty() && !dir.endsWith(QLatin1Char('/'))) - dir += QLatin1Char('/'); - if(lst.count() == 1) - lst = ProStringList(lst.first().toQString().split(" ")); - for (ProStringList::Iterator lst_it = lst.begin(); lst_it != lst.end(); ++lst_it) { - bool found = false; - QString dirs[] = { "", dir.toQString(), dirf, dirf + ".libs/", "(term)" }; - for(int i = 0; !found && dirs[i] != "(term)"; i++) { - if(QFile::exists(dirs[i] + (*lst_it))) { - QString targ = dirs[i] + (*lst_it); - if(QDir::isRelativePath(targ)) - targ.prepend(qmake_getpwd() + QLatin1Char('/')); - vars["QMAKE_PRL_TARGET"] << targ; - found = true; - } - } - if(found) - break; - } - } else if(it.key() == "dependency_libs") { - if(lst.count() == 1) { - ProString dep = lst.first(); - if ((dep.startsWith('\'') || dep.startsWith('"')) && dep.endsWith(dep.at(0))) - dep = dep.mid(1, dep.length() - 2); - lst = ProStringList(dep.trimmed().toQString().split(" ")); - } - for (ProStringList::Iterator lit = lst.begin(); lit != lst.end(); ++lit) { - if((*lit).startsWith("-R")) { - if(!conf->isEmpty("QMAKE_LFLAGS_RPATH")) - (*lit) = conf->first("QMAKE_LFLAGS_RPATH") + (*lit).mid(2); - } - } - ProStringList &prlLibs = vars["QMAKE_PRL_LIBS"]; - for (const ProString &s : qAsConst(lst)) { - prlLibs.removeAll(s); - prlLibs.append(s); - } - } - } - return true; -} - -bool -QMakeMetaInfo::readPkgCfgFile(const QString &f) -{ - fprintf(stderr, "Must implement reading in pkg-config files (%s)!!!\n", f.toLatin1().constData()); - return false; -} - QT_END_NAMESPACE diff --git a/qmake/meta.h b/qmake/meta.h index dd53cdc7a3..4721085fd2 100644 --- a/qmake/meta.h +++ b/qmake/meta.h @@ -41,20 +41,16 @@ class QMakeProject; class QMakeMetaInfo { - bool readLibtoolFile(const QString &f); - bool readPkgCfgFile(const QString &f); QMakeProject *conf; ProValueMap vars; - QString meta_type; static QHash<QString, ProValueMap> cache_vars; public: QMakeMetaInfo(QMakeProject *_conf); // These functions expect the path to be normalized - static QString findLib(const QString &lib); + static QString checkLib(const QString &lib); bool readLib(const QString &meta_file); - QString type() const; bool isEmpty(const ProKey &v); ProStringList &values(const ProKey &v); ProString first(const ProKey &v); @@ -64,9 +60,6 @@ public: inline bool QMakeMetaInfo::isEmpty(const ProKey &v) { return !vars.contains(v) || vars[v].isEmpty(); } -inline QString QMakeMetaInfo::type() const -{ return meta_type; } - inline ProStringList &QMakeMetaInfo::values(const ProKey &v) { return vars[v]; } diff --git a/qmake/project.cpp b/qmake/project.cpp index e6bdb04bfb..1bc9b352b5 100644 --- a/qmake/project.cpp +++ b/qmake/project.cpp @@ -82,8 +82,9 @@ bool QMakeProject::test(const ProKey &func, const QList<ProStringList> &args) { m_current.clear(); - if (int func_t = statics.functions.value(func)) - return boolRet(evaluateBuiltinConditional(func_t, func, prepareBuiltinArgs(args))); + auto adef = statics.functions.constFind(func); + if (adef != statics.functions.constEnd()) + return boolRet(evaluateBuiltinConditional(*adef, func, prepareBuiltinArgs(args))); QHash<ProKey, ProFunctionDef>::ConstIterator it = m_functionDefs.testFunctions.constFind(func); @@ -99,9 +100,10 @@ QStringList QMakeProject::expand(const ProKey &func, const QList<ProStringList> { m_current.clear(); - if (int func_t = statics.expands.value(func)) { + auto adef = statics.expands.constFind(func); + if (adef != statics.expands.constEnd()) { ProStringList ret; - if (evaluateBuiltinExpand(func_t, func, prepareBuiltinArgs(args), ret) == ReturnError) + if (evaluateBuiltinExpand(*adef, func, prepareBuiltinArgs(args), ret) == ReturnError) exit(3); return ret.toQStringList(); } |