diff --git a/CMakeLists.txt b/CMakeLists.txt index bfa058f905..6cf64fcaa8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,159 +1,158 @@ cmake_minimum_required(VERSION 3.0) project(KDevelop) # KDevelop version set(KDEVELOP_VERSION_MAJOR 5) set(KDEVELOP_VERSION_MINOR 1) set(KDEVELOP_VERSION_PATCH 40) set( KDEVELOP_VERSION "${KDEVELOP_VERSION_MAJOR}.${KDEVELOP_VERSION_MINOR}.${KDEVELOP_VERSION_PATCH}" ) # we need some parts of the ECM CMake helpers find_package (ECM 5.14.0 REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${KDevelop_SOURCE_DIR}/cmake/modules ${ECM_MODULE_PATH}) include(KDECompilerSettings NO_POLICY_SCOPE) # needs to be first, as set policies influence following macros include(ECMOptionalAddSubdirectory) include(ECMInstallIcons) include(ECMAddAppIcon) include(ECMSetupVersion) include(ECMAddTests) include(ECMMarkNonGuiExecutable) include(ECMGenerateHeaders) include(ECMQtDeclareLoggingCategory) -include(CTest) include(GenerateExportHeader) include(CMakePackageConfigHelpers) include(FeatureSummary) include(WriteBasicConfigVersionFile) include(CheckFunctionExists) include(KDEInstallDirs) include(KDECMakeSettings) set(QT_MIN_VERSION "5.5.0") find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED Widgets Concurrent Quick QuickWidgets Test) set(KF5_DEP_VERSION "5.15.0") # we need KCrash::initialize find_package(KF5 ${KF5_DEP_VERSION} REQUIRED COMPONENTS Config Declarative DocTools IconThemes I18n ItemModels ItemViews JobWidgets KCMUtils KIO NewStuff NotifyConfig Parts Service TextEditor ThreadWeaver XmlGui WindowSystem Crash ) find_package(KF5SysGuard CONFIG) set_package_properties(KF5SysGuard PROPERTIES PURPOSE "Framework for process listing. Required for the 'Attach to Process' feature" ) find_package(KDevelop-PG-Qt 1.90.90 CONFIG) set_package_properties(KDevelop-PG-Qt PROPERTIES PURPOSE "KDevelop parser generator library. Required for the QMake Builder/Manager plugin." ) find_package(KDevPlatform ${KDEVELOP_VERSION} REQUIRED) find_package(SharedMimeInfo REQUIRED) add_definitions( -DQT_DEPRECATED_WARNINGS -DQT_DISABLE_DEPRECATED_BEFORE=0x050400 -DQT_NO_SIGNALS_SLOTS_KEYWORDS -DQT_NO_URL_CAST_FROM_STRING -DQT_STRICT_ITERATORS -DQT_USE_FAST_CONCATENATION -DQT_USE_FAST_OPERATOR_PLUS ) # Turn off missing-field-initializers warning to avoid noise from false positives with empty {} # See discussion: http://mail.kde.org/pipermail/kdevelop-devel/2014-February/046910.html check_cxx_compiler_flag(-Wno-missing-field-initializers HAVE_MFI_FLAG) check_cxx_compiler_flag(-Werror=undefined-bool-conversion HAVE_UBC_FLAG) check_cxx_compiler_flag(-Werror=tautological-undefined-compare HAVE_TUC_FLAG) if (HAVE_MFI_FLAG) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-missing-field-initializers") endif() if (HAVE_UBC_FLAG) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror=undefined-bool-conversion") endif() if (HAVE_TUC_FLAG) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror=tautological-undefined-compare") endif() if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wdocumentation") endif() include_directories(${KDevelop_SOURCE_DIR} ${KDevelop_BINARY_DIR} ) string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_TOLOWER) if(CMAKE_BUILD_TYPE_TOLOWER MATCHES "debug" OR CMAKE_BUILD_TYPE_TOLOWER STREQUAL "") set(COMPILER_OPTIMIZATIONS_DISABLED TRUE) else() set(COMPILER_OPTIMIZATIONS_DISABLED FALSE) endif() # create config.h configure_file (config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h ) add_subdirectory(pics) add_subdirectory(app) add_subdirectory(analyzers) add_subdirectory(formatters) add_subdirectory(languages) add_subdirectory(projectbuilders) add_subdirectory(projectmanagers) add_subdirectory(debuggers) add_subdirectory(app_templates) add_subdirectory(documentation) add_subdirectory(kdeintegration) add_subdirectory(utils) add_subdirectory(file_templates) add_subdirectory(providers) add_subdirectory(runtimes) add_subdirectory(shortcuts) add_subdirectory(doc) set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KDevelop") configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/KDevelopConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/KDevelopConfig.cmake" INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} ) ecm_setup_version(${KDEVELOP_VERSION_MAJOR}.${KDEVELOP_VERSION_MINOR}.${KDEVELOP_VERSION_PATCH} VARIABLE_PREFIX KDEVELOP VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/kdevelop_version.h" PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KDevelopConfigVersion.cmake" SOVERSION ${KDEVELOP_LIB_SOVERSION} ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/kdevelop_version.h" DESTINATION "${KDE_INSTALL_INCLUDEDIR}/kdevelop") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/KDevelopConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/KDevelopConfigVersion.cmake" DESTINATION "${CMAKECONFIG_INSTALL_DIR}" ) install(EXPORT KDevelopTargets DESTINATION "${CMAKECONFIG_INSTALL_DIR}" NAMESPACE KDev:: FILE KDevelopTargets.cmake) # kdebugsettings file install(FILES kdevelop.categories DESTINATION ${KDE_INSTALL_CONFDIR}) # CTestCustom.cmake has to be in the CTEST_BINARY_DIR. # in the KDE build system, this is the same as CMAKE_BINARY_DIR. configure_file(${CMAKE_SOURCE_DIR}/CTestCustom.cmake ${CMAKE_BINARY_DIR}/CTestCustom.cmake) install(FILES org.kde.kdevelop.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR}) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/projectmanagers/cmake/CMakeLists.txt b/projectmanagers/cmake/CMakeLists.txt index 7d09648642..4ee72930d6 100644 --- a/projectmanagers/cmake/CMakeLists.txt +++ b/projectmanagers/cmake/CMakeLists.txt @@ -1,113 +1,114 @@ project(cmakemanager) add_definitions(-DTRANSLATION_DOMAIN=\"kdevcmake\") include_directories(${CMAKE_CURRENT_SOURCE_DIR}/parser) add_subdirectory(tests) add_subdirectory(icons) # enable this if you want to have the cmake debug visitor run on each CMakeLists.txt # the debug visitor prints out the Ast for the CMakeLists.txt file. #add_definitions( -DCMAKEDEBUGVISITOR ) ecm_qt_declare_logging_category(cmake_LOG_SRCS HEADER debug.h IDENTIFIER CMAKE CATEGORY_NAME "kdevelop.projectmanagers.cmake" ) set( cmakecommon_SRCS parser/cmListFileLexer.c parser/cmakecachereader.cpp parser/cmakelistsparser.cpp parser/cmakeduchaintypes.cpp cmakeutils.cpp cmakeextraargumentshistory.cpp cmakebuilddirchooser.cpp cmakeserver.cpp ${cmake_LOG_SRCS} ) +set_source_files_properties(parser/cmListFileLexer.c PROPERTIES COMPILE_FLAGS "-DYY_NO_INPUT -DYY_NO_UNPUT") set( cmakecommon_UI cmakebuilddirchooser.ui ) set( cmakemanager_SRCS testing/ctestutils.cpp testing/ctestfindjob.cpp testing/ctestrunjob.cpp testing/ctestsuite.cpp testing/qttestdelegate.cpp cmakeimportjsonjob.cpp cmakeserverimportjob.cpp cmakenavigationwidget.cpp cmakemanager.cpp cmakeprojectdata.cpp cmakemodelitems.cpp duchain/cmakeparsejob.cpp duchain/usebuilder.cpp duchain/declarationbuilder.cpp duchain/contextbuilder.cpp cmakecodecompletionmodel.cpp # cmakecommitchangesjob.cpp # cmakeedit.cpp ${cmake_LOG_SRCS} ) set( cmakemanager_UI cmakepossibleroots.ui ) set( cmakesettings_SRCS settings/cmakepreferences.cpp settings/cmakecachemodel.cpp settings/cmakecachedelegate.cpp settings/cmakecachemodel.cpp ) ki18n_wrap_ui(cmakesettings_SRCS settings/cmakebuildsettings.ui) set( cmakedoc_SRCS cmakedocumentation.cpp cmakehelpdocumentation.cpp cmakecommandscontents.cpp ) if(MSVC) add_definitions(-DYY_NO_UNISTD_H) endif() # Note: This library doesn't follow API/ABI/BC rules and shouldn't have a SOVERSION # Its only purpose is to support the plugin without needing to add all source files # to the plugin target kconfig_add_kcfg_files( cmakecommon_SRCS cmakebuilderconfig.kcfgc ) ki18n_wrap_ui( cmakecommon_SRCS ${cmakecommon_UI} ) add_library( kdevcmakecommon SHARED ${cmakecommon_SRCS} ) target_link_libraries( kdevcmakecommon KF5::TextEditor KDev::Interfaces KDev::Project KDev::Util KDev::Language ) generate_export_header(kdevcmakecommon EXPORT_FILE_NAME cmakecommonexport.h) ki18n_wrap_ui( cmakemanager_SRCS ${cmakemanager_UI} ) add_library( kdevcmakemanagernosettings STATIC ${cmakemanager_SRCS}) target_link_libraries( kdevcmakemanagernosettings KF5::KIOWidgets KDev::Util KDev::Interfaces kdevcmakecommon kdevmakefileresolver KDev::Project KDev::Language KDev::OutputView KF5::TextEditor Qt5::Concurrent) kdevplatform_add_plugin(kdevcmakemanager JSON kdevcmakemanager.json SOURCES ${cmakemanager_SRCS} ${cmakesettings_SRCS}) target_link_libraries( kdevcmakemanager KF5::KIOWidgets KDev::Util KDev::Interfaces kdevcmakecommon kdevmakefileresolver KDev::Project KDev::Language KDev::OutputView KF5::TextEditor Qt5::Concurrent) kdevplatform_add_plugin(kdevcmakedocumentation JSON kdevcmakedocumentation.json SOURCES ${cmakedoc_SRCS}) target_link_libraries( kdevcmakedocumentation KDev::Interfaces kdevcmakecommon KDev::Project KDev::Language KDev::Documentation KDev::Tests KF5::ItemModels KF5::TextEditor) install(TARGETS kdevcmakecommon ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/projectmanagers/qmake/tests/test_qmakefile.cpp b/projectmanagers/qmake/tests/test_qmakefile.cpp index 7ab0d14792..4bdf20d02b 100644 --- a/projectmanagers/qmake/tests/test_qmakefile.cpp +++ b/projectmanagers/qmake/tests/test_qmakefile.cpp @@ -1,675 +1,678 @@ /* KDevelop QMake Support * * Copyright 2010 Milian Wolff * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #include "test_qmakefile.h" #include "qmakefile.h" #include "variablereferenceparser.h" #include "qmakeprojectfile.h" #include "qmakemkspecs.h" #include #include #include #include #include #include #include #include #include #include QTEST_MAIN(TestQMakeFile); typedef QHash DefineHash; Q_DECLARE_METATYPE(QMakeFile::VariableMap) Q_DECLARE_METATYPE(DefineHash) namespace QTest { template <> char* toString(const QStringList& list) { QByteArray ba; if (list.isEmpty()) { ba = "()"; } else { ba = "(\"" + list.join(QStringLiteral("\", \"")).toLocal8Bit() + "\")"; } return qstrdup(ba.data()); } template <> char* toString(const QMakeFile::VariableMap& variables) { QByteArray ba = "VariableMap("; QMakeFile::VariableMap::const_iterator it = variables.constBegin(); while (it != variables.constEnd()) { ba += "["; ba += it.key().toLocal8Bit(); ba += "] = "; ba += toString(it.value()); ++it; if (it != variables.constEnd()) { ba += ", "; } } ba += ")"; return qstrdup(ba.data()); } } QHash setDefaultMKSpec(QMakeProjectFile& file) { static const QHash qmvars = QMakeConfig::queryQMake(QMakeConfig::qmakeExecutable(nullptr)); static const QString specFile = QMakeConfig::findBasicMkSpec(qmvars); if (!QFile::exists(specFile)) { qDebug() << "mkspec file does not exist:" << specFile; return {}; } QMakeMkSpecs* mkspecs = new QMakeMkSpecs(specFile, qmvars); mkspecs->read(); file.setMkSpecs(mkspecs); return qmvars; } void TestQMakeFile::varResolution() { QFETCH(QString, fileContents); QFETCH(QMakeFile::VariableMap, variables); QTemporaryFile tmpfile; tmpfile.open(); QTextStream stream(&tmpfile); stream << fileContents; stream << flush; tmpfile.close(); QMakeFile file(tmpfile.fileName()); QVERIFY(file.read()); QCOMPARE(file.variableMap(), variables); } void TestQMakeFile::varResolution_data() { QTest::addColumn("fileContents"); QTest::addColumn("variables"); { QMakeFile::VariableMap variables; variables[QStringLiteral("VAR1")] = QStringList() << QStringLiteral("1"); QTest::newRow("simple") << "VAR1 = 1\n" << variables; } { QMakeFile::VariableMap variables; variables[QStringLiteral("VAR1")] = QStringList() << QStringLiteral("1"); variables[QStringLiteral("VAR2")] = QStringList() << QStringLiteral("1"); QTest::newRow("var-in-var") << "VAR1 = 1\nVAR2 = $$VAR1\n" << variables; } { QMakeFile::VariableMap variables; variables[QStringLiteral("VAR1")] = QStringList() << QStringLiteral("foo"); variables[QStringLiteral("VAR2")] = QStringList() << QStringLiteral("foo"); QTest::newRow("curlyvar") << "VAR1 = foo\nVAR2 = $${VAR1}\n" << variables; } { QMakeFile::VariableMap variables; variables[QStringLiteral("VAR1")] = QStringList() << QProcessEnvironment::systemEnvironment().value(QStringLiteral("USER")); QTest::newRow("qmakeshell") << "VAR1 = $$(USER)\n" << variables; } { QMakeFile::VariableMap variables; variables[QStringLiteral("VAR1")] = QStringList() << QStringLiteral("foo"); variables[QStringLiteral("VAR2")] = QStringList() << QStringLiteral("foo/bar"); QTest::newRow("path") << "VAR1 = foo\nVAR2 = $$VAR1/bar\n" << variables; } { QMakeFile::VariableMap variables; variables[QStringLiteral("VAR_1")] = QStringList() << QStringLiteral("foo"); variables[QStringLiteral("VAR_2")] = QStringList() << QStringLiteral("foo/bar"); QTest::newRow("var-underscore") << "VAR_1 = foo\nVAR_2 = $$VAR_1/bar" << variables; } } void TestQMakeFile::referenceParser() { QFETCH(QString, var); VariableReferenceParser parser; parser.setContent(var); QVERIFY(parser.parse()); } void TestQMakeFile::referenceParser_data() { QTest::addColumn("var"); QTest::newRow("dot") << "."; QTest::newRow("dotdot") << ".."; } void TestQMakeFile::libTarget() { QFETCH(QString, target); QFETCH(QString, resolved); QTemporaryFile tmpfile; tmpfile.open(); QTextStream stream(&tmpfile); stream << "TARGET = " << target << "\nTEMPLATE = lib\n"; stream << flush; tmpfile.close(); QMakeProjectFile file(tmpfile.fileName()); const auto qmvars = setDefaultMKSpec(file); if (qmvars.isEmpty()) { QSKIP("Problem querying QMake, skipping test function"); } QVERIFY(file.read()); QCOMPARE(file.targets(), QStringList() << resolved); } void TestQMakeFile::libTarget_data() { QTest::addColumn("target"); QTest::addColumn("resolved"); QTest::newRow("simple") << "MyLib" << "MyLib"; QTest::newRow("qtLibraryTarget") << "$$qtLibraryTarget(MyLib)" << "MyLib"; QTest::newRow("qtLibraryTarget-Var") << "MyLib\nTARGET = $$qtLibraryTarget($$TARGET)" << "MyLib"; } void TestQMakeFile::defines() { QFETCH(QString, fileContents); QFETCH(DefineHash, expectedDefines); QTemporaryFile tmpfile; tmpfile.open(); QTextStream stream(&tmpfile); stream << fileContents; stream << flush; tmpfile.close(); QMakeProjectFile file(tmpfile.fileName()); - setDefaultMKSpec(file); + const auto qmvars = setDefaultMKSpec(file); + if (qmvars.isEmpty()) { + QSKIP("Problem querying QMake, skipping test function"); + } QVERIFY(file.read()); QList list = file.defines(); QCOMPARE(list.size(), expectedDefines.size()); foreach (QMakeProjectFile::DefinePair define, list) { QVERIFY(expectedDefines.find(define.first) != expectedDefines.end()); QCOMPARE(define.second, expectedDefines[define.first]); } } void TestQMakeFile::defines_data() { QTest::addColumn("fileContents"); QTest::addColumn("expectedDefines"); { DefineHash list; list.insert(QStringLiteral("VAR1"), QLatin1String("")); QTest::newRow("Simple define") << "DEFINES += VAR1" << list; } { DefineHash list; list.insert(QStringLiteral("ANSWER"), QStringLiteral("42")); QTest::newRow("Define with value") << "DEFINES += ANSWER=42" << list; } { DefineHash list; list.insert(QStringLiteral("ANSWER"), QStringLiteral("42")); list.insert(QStringLiteral("ANOTHER_DEFINE"), QLatin1String("")); QTest::newRow("Multiple defines") << "DEFINES += ANSWER=42 ANOTHER_DEFINE" << list; } } void TestQMakeFile::replaceFunctions_data() { QTest::addColumn("fileContents"); QTest::addColumn("definedVariables"); QTest::addColumn("undefinedVariables"); { QString contents = "defineReplace(test) {\n" " FOO = $$1\n" " return($$FOO)\n" "}\n" "BAR = $$test(asdf)\n"; QMakeFile::VariableMap vars; vars[QStringLiteral("BAR")] = QStringList() << QStringLiteral("asdf"); QStringList undefined; undefined << QStringLiteral("FOO") << QStringLiteral("1"); QTest::newRow("defineReplace-1") << contents << vars << undefined; } } void TestQMakeFile::replaceFunctions() { QFETCH(QString, fileContents); QFETCH(QMakeFile::VariableMap, definedVariables); QFETCH(QStringList, undefinedVariables); QTemporaryFile tmpFile; tmpFile.open(); tmpFile.write(fileContents.toUtf8()); tmpFile.close(); QMakeProjectFile file(tmpFile.fileName()); setDefaultMKSpec(file); QVERIFY(file.read()); QMakeFile::VariableMap::const_iterator it = definedVariables.constBegin(); while (it != definedVariables.constEnd()) { QCOMPARE(file.variableValues(it.key()), it.value()); ++it; } foreach (const QString& var, undefinedVariables) { QVERIFY(!file.containsVariable(var)); } } void TestQMakeFile::qtIncludeDirs_data() { QTest::addColumn("fileContents"); QTest::addColumn("modules"); QTest::addColumn("missingModules"); { QStringList list; list << QStringLiteral("core") << QStringLiteral("gui"); QTest::newRow("defaults") << "" << list; } { QStringList list; list << QStringLiteral("core"); QTest::newRow("minimal") << "QT -= gui" << list; } { QStringList modules; modules << QStringLiteral("core") << QStringLiteral("gui") << QStringLiteral("network") << QStringLiteral("opengl") << QStringLiteral("phonon") << QStringLiteral("script") << QStringLiteral("scripttools") << QStringLiteral("sql") << QStringLiteral("svg") << QStringLiteral("webkit") << QStringLiteral("xml") << QStringLiteral("xmlpatterns") << QStringLiteral("qt3support") << QStringLiteral("designer") << QStringLiteral("uitools") << QStringLiteral("help") << QStringLiteral("assistant") << QStringLiteral("qtestlib") << QStringLiteral("testlib") << QStringLiteral("qaxcontainer") << QStringLiteral("qaxserver") << QStringLiteral("dbus") << QStringLiteral("declarative"); foreach (const QString& module, modules) { QStringList expected; expected << module; if (module != QLatin1String("core")) { expected << QStringLiteral("core"); } QTest::newRow(qPrintable(module)) << QStringLiteral("QT = %1").arg(module) << expected; } } } void TestQMakeFile::qtIncludeDirs() { QFETCH(QString, fileContents); QFETCH(QStringList, modules); QMap moduleMap; moduleMap[QStringLiteral("core")] = QStringLiteral("QtCore"); moduleMap[QStringLiteral("gui")] = QStringLiteral("QtGui"); moduleMap[QStringLiteral("network")] = QStringLiteral("QtNetwork"); moduleMap[QStringLiteral("opengl")] = QStringLiteral("QtOpenGL"); moduleMap[QStringLiteral("phonon")] = QStringLiteral("Phonon"); moduleMap[QStringLiteral("script")] = QStringLiteral("QtScript"); moduleMap[QStringLiteral("scripttools")] = QStringLiteral("QtScriptTools"); moduleMap[QStringLiteral("sql")] = QStringLiteral("QtSql"); moduleMap[QStringLiteral("svg")] = QStringLiteral("QtSvg"); moduleMap[QStringLiteral("webkit")] = QStringLiteral("QtWebKit"); moduleMap[QStringLiteral("xml")] = QStringLiteral("QtXml"); moduleMap[QStringLiteral("xmlpatterns")] = QStringLiteral("QtXmlPatterns"); moduleMap[QStringLiteral("qt3support")] = QStringLiteral("Qt3Support"); moduleMap[QStringLiteral("designer")] = QStringLiteral("QtDesigner"); moduleMap[QStringLiteral("uitools")] = QStringLiteral("QtUiTools"); moduleMap[QStringLiteral("help")] = QStringLiteral("QtHelp"); moduleMap[QStringLiteral("assistant")] = QStringLiteral("QtAssistant"); moduleMap[QStringLiteral("qtestlib")] = QStringLiteral("QtTest"); moduleMap[QStringLiteral("testlib")] = QStringLiteral("QtTest"); moduleMap[QStringLiteral("qaxcontainer")] = QStringLiteral("ActiveQt"); moduleMap[QStringLiteral("qaxserver")] = QStringLiteral("ActiveQt"); moduleMap[QStringLiteral("dbus")] = QStringLiteral("QtDBus"); moduleMap[QStringLiteral("declarative")] = QStringLiteral("QtDeclarative"); QTemporaryFile tmpFile; tmpFile.open(); tmpFile.write(fileContents.toUtf8()); tmpFile.close(); QMakeProjectFile file(tmpFile.fileName()); const auto qmvars = setDefaultMKSpec(file); if (qmvars.isEmpty()) { QSKIP("Problem querying QMake, skipping test function"); } QVERIFY(file.read()); const QStringList includes = file.includeDirectories(); // should always be there QVERIFY(includes.contains(qmvars["QT_INSTALL_HEADERS"])); for (QMap::const_iterator it = moduleMap.constBegin(); it != moduleMap.constEnd(); ++it) { QFileInfo include(qmvars[QStringLiteral("QT_INSTALL_HEADERS")] + "/" + it.value()); bool shouldBeIncluded = include.exists(); if (shouldBeIncluded) { shouldBeIncluded = modules.contains(it.key()); if (!shouldBeIncluded) { foreach (const QString& module, modules) { if (module != it.key() && moduleMap.value(module) == it.value()) { shouldBeIncluded = true; break; } } } } QCOMPARE((bool)includes.contains(include.filePath()), shouldBeIncluded); } } void TestQMakeFile::testInclude() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); QTemporaryFile includeFile(tempDir.path() + "/qmake-include"); QVERIFY(includeFile.open()); includeFile.write("DEFINES += SOME_INCLUDE_DEF\n" "SOURCES += includedFile.cpp\n" "INCLUDEPATH += $$PWD\n" "QT += webkit\n"); includeFile.close(); QTemporaryFile baseFile; baseFile.open(); baseFile.write("TEMPLATE = app\n" "TARGET = includeTest\n" "QT += network\n" "DEFINES += SOME_DEF\n" "SOURCES += file.cpp\n" /* "CONFIG += console" "# Comment to enable Debug Messages" "DEFINES += QT_NO_DEBUG_OUTPUT" "DESTDIR = ../bin" "RESOURCES = phantomjs.qrc" "HEADERS += csconverter.h \\" " phantom.h \\" " webpage.h \\" " consts.h \\" " utils.h \\" " networkaccessmanager.h \\" " cookiejar.h \\" " filesystem.h \\" " terminal.h \\" " encoding.h \\" " config.h \\" " mimesniffer.cpp \\" " third_party/mongoose/mongoose.h \\" " webserver.h" "SOURCES += phantom.cpp \\" " webpage.cpp \\" " main.cpp \\" " csconverter.cpp \\" " utils.cpp \\" " networkaccessmanager.cpp \\" " cookiejar.cpp \\" " filesystem.cpp \\" " terminal.cpp \\" " encoding.cpp \\" " config.cpp \\" " mimesniffer.cpp \\" " third_party/mongoose/mongoose.c \\" " webserver.cpp" "" "OTHER_FILES += usage.txt \\" " bootstrap.js \\" " configurator.js \\" " modules/fs.js \\" " modules/webpage.js \\" " modules/webserver.js" "" */ "include(" + includeFile.fileName().toLocal8Bit() + ")\n"); baseFile.close(); QMakeProjectFile file(baseFile.fileName()); const auto qmvars = setDefaultMKSpec(file); if (qmvars.isEmpty()) { QSKIP("Problem querying QMake, skipping test function"); } QVERIFY(file.read()); QCOMPARE(file.variableValues("DEFINES"), QStringList() << "SOME_DEF" << "SOME_INCLUDE_DEF"); QCOMPARE(file.variableValues("SOURCES"), QStringList() << "file.cpp" << "includedFile.cpp"); QCOMPARE(file.variableValues("QT"), QStringList() << "core" << "gui" << "network" << "webkit"); // verify that include path was properly propagated QVERIFY(file.includeDirectories().contains(tempDir.path())); } void TestQMakeFile::globbing_data() { QTest::addColumn("files"); QTest::addColumn("pattern"); QTest::addColumn("matches"); QTest::newRow("wildcard-simple") << (QStringList() << QStringLiteral("foo.cpp")) << "*.cpp" << (QStringList() << QStringLiteral("foo.cpp")); QTest::newRow("wildcard-extended") << (QStringList() << QStringLiteral("foo.cpp") << QStringLiteral("bar.h") << QStringLiteral("asdf.cpp")) << "*.cpp" << (QStringList() << QStringLiteral("foo.cpp") << QStringLiteral("asdf.cpp")); QTest::newRow("wildcard-multiple") << (QStringList() << QStringLiteral("foo.cpp") << QStringLiteral("bar.h") << QStringLiteral("asdf.cpp")) << "*.cpp *.h" << (QStringList() << QStringLiteral("foo.cpp") << QStringLiteral("bar.h") << QStringLiteral("asdf.cpp")); QTest::newRow("wildcard-subdir") << (QStringList() << QStringLiteral("foo/bar.cpp") << QStringLiteral("fooasdf/bar.cpp") << QStringLiteral("asdf/asdf.cpp")) << "foo*/*.cpp" << (QStringList() << QStringLiteral("foo/bar.cpp") << QStringLiteral("fooasdf/bar.cpp")); QTest::newRow("bracket") << (QStringList() << QStringLiteral("foo1.cpp") << QStringLiteral("foo2.cpp") << QStringLiteral("fooX.cpp")) << "foo[0-9].cpp" << (QStringList() << QStringLiteral("foo1.cpp") << QStringLiteral("foo2.cpp")); QTest::newRow("questionmark") << (QStringList() << QStringLiteral("foo1.cpp") << QStringLiteral("fooX.cpp") << QStringLiteral("foo.cpp") << QStringLiteral("fooXY.cpp")) << "foo?.cpp" << (QStringList() << QStringLiteral("foo1.cpp") << QStringLiteral("fooX.cpp")); QTest::newRow("mixed") << (QStringList() << QStringLiteral("foo/asdf/test.cpp") << QStringLiteral("fooX/asdf1/test.cpp")) << "foo?/asdf[0-9]/*.cpp" << (QStringList() << QStringLiteral("fooX/asdf1/test.cpp")); } void TestQMakeFile::globbing() { QFETCH(QStringList, files); QFETCH(QString, pattern); QFETCH(QStringList, matches); QTemporaryDir tempDir; QDir tempDirDir(tempDir.path()); QVERIFY(tempDir.isValid()); foreach (const QString& file, files) { QVERIFY(tempDirDir.mkpath(QFileInfo(file).path())); QFile f(tempDir.path() + '/' + file); QVERIFY(f.open(QIODevice::WriteOnly)); } QTemporaryFile testFile(tempDir.path() + "/XXXXXX.pro"); QVERIFY(testFile.open()); testFile.write(("SOURCES = " + pattern + "\n").toUtf8()); testFile.close(); QMakeProjectFile pro(testFile.fileName()); const auto qmvars = setDefaultMKSpec(pro); if (qmvars.isEmpty()) { QSKIP("Problem querying QMake, skipping test function"); } QVERIFY(pro.read()); QStringList actual; foreach (QString path, pro.files()) { actual << path.remove(tempDir.path() + '/'); } std::sort(actual.begin(), actual.end()); std::sort(matches.begin(), matches.end()); QCOMPARE(actual, matches); } void TestQMakeFile::benchGlobbing() { QTemporaryDir tempDir; QDir dir(tempDir.path()); const int folders = 10; const int files = 100; for (int i = 0; i < folders; ++i) { QString folder = QStringLiteral("folder%1").arg(i); dir.mkdir(folder); for (int j = 0; j < files; ++j) { QFile f1(dir.filePath(folder + QStringLiteral("/file%1.cpp").arg(j))); QVERIFY(f1.open(QIODevice::WriteOnly)); QFile f2(dir.filePath(folder + QStringLiteral("/file%1.h").arg(j))); QVERIFY(f2.open(QIODevice::WriteOnly)); } } QTemporaryFile testFile(tempDir.path() + "/XXXXXX.pro"); QVERIFY(testFile.open()); testFile.write("SOURCES = fo?der[0-9]/*.cpp\n"); testFile.close(); QMakeProjectFile pro(testFile.fileName()); const auto qmvars = setDefaultMKSpec(pro); if (qmvars.isEmpty()) { QSKIP("Problem querying QMake, skipping test function"); } QVERIFY(pro.read()); int found = 0; QBENCHMARK { found = pro.files().size(); } QCOMPARE(found, files * folders); } void TestQMakeFile::benchGlobbingNoPattern() { QTemporaryDir tempDir; QDir dir(tempDir.path()); const int folders = 10; const int files = 100; for (int i = 0; i < folders; ++i) { QString folder = QStringLiteral("folder%1").arg(i); dir.mkdir(folder); for (int j = 0; j < files; ++j) { QFile f1(dir.filePath(folder + QStringLiteral("/file%1.cpp").arg(j))); QVERIFY(f1.open(QIODevice::WriteOnly)); QFile f2(dir.filePath(folder + QStringLiteral("/file%1.h").arg(j))); QVERIFY(f2.open(QIODevice::WriteOnly)); } } QTemporaryFile testFile(tempDir.path() + "/XXXXXX.pro"); QVERIFY(testFile.open()); testFile.write("SOURCES = folder0/file1.cpp\n"); testFile.close(); QMakeProjectFile pro(testFile.fileName()); const auto qmvars = setDefaultMKSpec(pro); if (qmvars.isEmpty()) { QSKIP("Problem querying QMake, skipping test function"); } QVERIFY(pro.read()); int found = 0; QBENCHMARK { found = pro.files().size(); } QCOMPARE(found, 1); }