diff --git a/src/tools/clangpchmanagerbackend/clangpchmanagerbackendmain.cpp b/src/tools/clangpchmanagerbackend/clangpchmanagerbackendmain.cpp index 929393116ed..45ddec5bd67 100644 --- a/src/tools/clangpchmanagerbackend/clangpchmanagerbackendmain.cpp +++ b/src/tools/clangpchmanagerbackend/clangpchmanagerbackendmain.cpp @@ -37,6 +37,7 @@ #include #include #include +#include using ClangBackEnd::ClangPathWatcher; using ClangBackEnd::ConnectionServer; @@ -93,11 +94,15 @@ int main(int argc, char *argv[]) const QString connection = processArguments(application); StringCache filePathCache; - ClangPathWatcher includeWatcher(filePathCache); + ClangPathWatcher includeWatcher(filePathCache); ApplicationEnvironment environment; PchCreator pchCreator(environment, filePathCache); ProjectParts projectParts; - PchManagerServer clangPchManagerServer(filePathCache, includeWatcher, pchCreator, projectParts); + PchManagerServer clangPchManagerServer(filePathCache, + includeWatcher, + pchCreator, + projectParts); + includeWatcher.setNotifier(&clangPchManagerServer); ConnectionServer connectionServer(connection); connectionServer.start(); connectionServer.setServer(&clangPchManagerServer); diff --git a/src/tools/clangpchmanagerbackend/source/changedfilepathcompressor.h b/src/tools/clangpchmanagerbackend/source/changedfilepathcompressor.h new file mode 100644 index 00000000000..32fa539379d --- /dev/null +++ b/src/tools/clangpchmanagerbackend/source/changedfilepathcompressor.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include + +#include + +#include + +#include + +namespace ClangBackEnd { + +template +class ChangedFilePathCompressor non_unittest_final +{ +public: + ChangedFilePathCompressor() + { + m_timer.setSingleShot(true); + } + + virtual ~ChangedFilePathCompressor() + { + } + + void addFilePath(const QString &filePath) + { + m_filePaths.push_back(filePath); + + restartTimer(); + } + + Utils::SmallStringVector takeFilePaths() + { + return std::move(m_filePaths); + } + + virtual void setCallback(std::function &&callback) + { + QObject::connect(&m_timer, + &Timer::timeout, + [this, callback=std::move(callback)] { callback(takeFilePaths()); }); + } + +unitttest_public: + virtual void restartTimer() + { + m_timer.start(20); + } + + Timer &timer() + { + return m_timer; + } + +private: + Utils::SmallStringVector m_filePaths; + Timer m_timer; +}; + +} // namespace ClangBackEnd diff --git a/src/tools/clangpchmanagerbackend/source/clangpathwatcher.h b/src/tools/clangpchmanagerbackend/source/clangpathwatcher.h index b3756ed2e56..fc7187bb9e7 100644 --- a/src/tools/clangpchmanagerbackend/source/clangpathwatcher.h +++ b/src/tools/clangpchmanagerbackend/source/clangpathwatcher.h @@ -27,9 +27,10 @@ #include "clangpathwatcherinterface.h" #include "clangpathwatchernotifier.h" +#include "changedfilepathcompressor.h" #include "stringcache.h" -#include +#include namespace ClangBackEnd { @@ -60,7 +61,10 @@ public: } }; -template +using WatcherEntries = std::vector; + +template class ClangPathWatcher : public ClangPathWatcherInterface { public: @@ -71,7 +75,16 @@ public: { QObject::connect(&m_fileSystemWatcher, &FileSystemWatcher::fileChanged, - [&] (const QString &filePath) { addChangedPathForFilePath(filePath); }); + [&] (const QString &filePath) { compressChangedFilePath(filePath); }); + + m_changedFilePathCompressor.setCallback([&] (Utils::SmallStringVector &&filePaths) { + addChangedPathForFilePath(std::move(filePaths)); + }); + } + + ~ClangPathWatcher() + { + m_changedFilePathCompressor.setCallback([&] (Utils::SmallStringVector &&) {}); } void updateIdPaths(const std::vector &idPaths) override @@ -136,10 +149,10 @@ unitttest_public: } - std::pair,std::vector> + std::pair> convertIdPathsToWatcherEntriesAndIds(const std::vector &idPaths) { - std::vector entries; + WatcherEntries entries; entries.reserve(sizeOfIdPaths(idPaths)); std::vector ids; ids.reserve(ids.size()); @@ -164,7 +177,7 @@ unitttest_public: return {entries, ids}; } - void addEntries(const std::vector &entries) + void addEntries(const WatcherEntries &entries) { auto newEntries = notWatchedEntries(entries); @@ -176,7 +189,7 @@ unitttest_public: m_fileSystemWatcher.addPaths(convertWatcherEntriesToQStringList(filteredPaths)); } - void removeUnusedEntries(const std::vector &entries, + void removeUnusedEntries(const WatcherEntries &entries, const std::vector &ids) { auto oldEntries = notAnymoreWatchedEntriesWithIds(entries, ids); @@ -195,7 +208,7 @@ unitttest_public: } QStringList convertWatcherEntriesToQStringList( - const std::vector &watcherEntries) + const WatcherEntries &watcherEntries) { QStringList paths; paths.reserve(int(watcherEntries.size())); @@ -209,10 +222,10 @@ unitttest_public: } template - std::vector notWatchedEntries(const std::vector &entries, + WatcherEntries notWatchedEntries(const WatcherEntries &entries, Compare compare) const { - std::vector notWatchedEntries; + WatcherEntries notWatchedEntries; notWatchedEntries.reserve(entries.size()); std::set_difference(entries.begin(), @@ -225,12 +238,12 @@ unitttest_public: return notWatchedEntries; } - std::vector notWatchedEntries(const std::vector &entries) const + WatcherEntries notWatchedEntries(const WatcherEntries &entries) const { return notWatchedEntries(entries, std::less()); } - std::vector notWatchedPaths(const std::vector &entries) const + WatcherEntries notWatchedPaths(const WatcherEntries &entries) const { auto compare = [] (const WatcherEntry &first, const WatcherEntry &second) { return first.path < second.path; @@ -240,11 +253,11 @@ unitttest_public: } template - std::vector notAnymoreWatchedEntries( - const std::vector &newEntries, + WatcherEntries notAnymoreWatchedEntries( + const WatcherEntries &newEntries, Compare compare) const { - std::vector notAnymoreWatchedEntries; + WatcherEntries notAnymoreWatchedEntries; notAnymoreWatchedEntries.reserve(m_watchedEntries.size()); std::set_difference(m_watchedEntries.cbegin(), @@ -257,8 +270,8 @@ unitttest_public: return notAnymoreWatchedEntries; } - std::vector notAnymoreWatchedEntriesWithIds( - const std::vector &newEntries, + WatcherEntries notAnymoreWatchedEntriesWithIds( + const WatcherEntries &newEntries, const std::vector &ids) const { auto oldEntries = notAnymoreWatchedEntries(newEntries, std::less()); @@ -274,9 +287,9 @@ unitttest_public: return oldEntries; } - void mergeToWatchedEntries(const std::vector &newEntries) + void mergeToWatchedEntries(const WatcherEntries &newEntries) { - std::vector newWatchedEntries; + WatcherEntries newWatchedEntries; newWatchedEntries.reserve(m_watchedEntries.size() + newEntries.size()); std::merge(m_watchedEntries.cbegin(), @@ -289,9 +302,9 @@ unitttest_public: } static - std::vector uniquePaths(const std::vector &pathEntries) + WatcherEntries uniquePaths(const WatcherEntries &pathEntries) { - std::vector uniqueEntries; + WatcherEntries uniqueEntries; auto compare = [] (const WatcherEntry &first, const WatcherEntry &second) { return first.path == second.path; @@ -305,17 +318,17 @@ unitttest_public: return uniqueEntries; } - std::vector filterNotWatchedPaths(const std::vector &entries) + WatcherEntries filterNotWatchedPaths(const WatcherEntries &entries) { return notWatchedPaths(uniquePaths(entries)); } - const std::vector &watchedEntries() const + const WatcherEntries &watchedEntries() const { return m_watchedEntries; } - std::vector removeIdsFromWatchedEntries(const std::vector &ids) + WatcherEntries removeIdsFromWatchedEntries(const std::vector &ids) { auto keep = [&] (const WatcherEntry &entry) { @@ -326,16 +339,16 @@ unitttest_public: m_watchedEntries.end(), keep); - std::vector removedEntries(found, m_watchedEntries.end()); + WatcherEntries removedEntries(found, m_watchedEntries.end()); m_watchedEntries.erase(found, m_watchedEntries.end()); return removedEntries; } - void removeFromWatchedEntries(const std::vector &oldEntries) + void removeFromWatchedEntries(const WatcherEntries &oldEntries) { - std::vector newWatchedEntries; + WatcherEntries newWatchedEntries; newWatchedEntries.reserve(m_watchedEntries.size() - oldEntries.size()); std::set_difference(m_watchedEntries.cbegin(), @@ -348,25 +361,57 @@ unitttest_public: m_watchedEntries = newWatchedEntries; } - void addChangedPathForFilePath(const QString &filePath) + void compressChangedFilePath(const QString &filePath) { - uint pathId = m_pathCache.stringId(Utils::SmallString(filePath)); + m_changedFilePathCompressor.addFilePath(filePath); + } - auto range = std::equal_range(m_watchedEntries.begin(), m_watchedEntries.end(), pathId); + WatcherEntries watchedEntriesForPaths(Utils::SmallStringVector &&filePaths) + { + std::vector pathIds = m_pathCache.stringIds(filePaths); - Utils::SmallStringVector changedIds; + WatcherEntries foundEntries; - std::transform(range.first, - range.second, - std::back_inserter(changedIds), + for (uint pathId : pathIds) { + auto range = std::equal_range(m_watchedEntries.begin(), m_watchedEntries.end(), pathId); + foundEntries.insert(foundEntries.end(), range.first, range.second); + } + + return foundEntries; + } + + Utils::SmallStringVector idsForWatcherEntries(const WatcherEntries &foundEntries) + { + Utils::SmallStringVector ids; + + std::transform(foundEntries.begin(), + foundEntries.end(), + std::back_inserter(ids), [&] (const WatcherEntry &entry) { return m_idCache.string(entry.id); }); - std::sort(changedIds.begin(), changedIds.end()); + return ids; + } - if (m_notifier) - m_notifier->pathsWithIdsChanged(changedIds); + Utils::SmallStringVector uniqueIds(Utils::SmallStringVector &&ids) + { + std::sort(ids.begin(), ids.end()); + auto newEnd = std::unique(ids.begin(), ids.end()); + ids.erase(newEnd, ids.end()); + + return std::move(ids); + } + + void addChangedPathForFilePath(Utils::SmallStringVector &&filePaths) + { + if (m_notifier) { + WatcherEntries foundEntries = watchedEntriesForPaths(std::move(filePaths)); + + Utils::SmallStringVector changedIds = idsForWatcherEntries(foundEntries); + + m_notifier->pathsWithIdsChanged(uniqueIds(std::move(changedIds))); + } } StringCache &pathCache() @@ -381,7 +426,8 @@ unitttest_public: private: StringCache m_idCache; - std::vector m_watchedEntries; + WatcherEntries m_watchedEntries; + ChangedFilePathCompressor m_changedFilePathCompressor; FileSystemWatcher m_fileSystemWatcher; StringCache &m_pathCache; ClangPathWatcherNotifier *m_notifier; diff --git a/src/tools/clangpchmanagerbackend/source/clangpchmanagerbackend-source.pri b/src/tools/clangpchmanagerbackend/source/clangpchmanagerbackend-source.pri index 696159245b6..b6c36b3945d 100644 --- a/src/tools/clangpchmanagerbackend/source/clangpchmanagerbackend-source.pri +++ b/src/tools/clangpchmanagerbackend/source/clangpchmanagerbackend-source.pri @@ -29,4 +29,5 @@ HEADERS += \ $$PWD/pchcreatorinterface.h \ $$PWD/clangpathwatcherinterface.h \ $$PWD/projectpartsinterface.h \ - $$PWD/clangpathwatchernotifier.h + $$PWD/clangpathwatchernotifier.h \ + $$PWD/changedfilepathcompressor.h diff --git a/src/tools/clangpchmanagerbackend/source/clangpchmanagerbackend_global.h b/src/tools/clangpchmanagerbackend/source/clangpchmanagerbackend_global.h index 57fa8c64f8f..b44fba700f0 100644 --- a/src/tools/clangpchmanagerbackend/source/clangpchmanagerbackend_global.h +++ b/src/tools/clangpchmanagerbackend/source/clangpchmanagerbackend_global.h @@ -27,8 +27,10 @@ #ifdef UNIT_TESTS #define unitttest_public public +#define non_unittest_final #else #define unitttest_public private +#define non_unittest_final final #endif namespace llvm { diff --git a/src/tools/clangpchmanagerbackend/source/includecollector.cpp b/src/tools/clangpchmanagerbackend/source/includecollector.cpp index f383af96090..c90f345cc2a 100644 --- a/src/tools/clangpchmanagerbackend/source/includecollector.cpp +++ b/src/tools/clangpchmanagerbackend/source/includecollector.cpp @@ -65,14 +65,12 @@ std::vector IncludeCollector::generateExcludedIncludeFileUIDs(clang::FileM std::vector fileUIDs; fileUIDs.reserve(m_excludedIncludes.size()); - auto generateUID = [&] (const Utils::SmallString &filePath) { - return fileManager.getFile({filePath.data(), filePath.size()})->getUID(); - }; + for (const Utils::SmallString &filePath : m_excludedIncludes) { + const clang::FileEntry *file = fileManager.getFile({filePath.data(), filePath.size()}); - std::transform(m_excludedIncludes.begin(), - m_excludedIncludes.end(), - std::back_inserter(fileUIDs), - generateUID); + if (file) + fileUIDs.push_back(file->getUID()); + } std::sort(fileUIDs.begin(), fileUIDs.end()); diff --git a/tests/unit/unittest/changedfilepathcompressor-test.cpp b/tests/unit/unittest/changedfilepathcompressor-test.cpp new file mode 100644 index 00000000000..2aa28b7cc05 --- /dev/null +++ b/tests/unit/unittest/changedfilepathcompressor-test.cpp @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "googletest.h" + +#include + +namespace { + +using testing::ElementsAre; +using testing::Invoke; +using testing::IsEmpty; +using testing::NiceMock; + +class ChangedFilePathCompressor : public testing::Test +{ +protected: + void SetUp(); + +protected: + NiceMock mockCompressor; + ClangBackEnd::ChangedFilePathCompressor compressor; + QString filePath1{"filePath1"}; + QString filePath2{"filePath2"}; +}; + +TEST_F(ChangedFilePathCompressor, AddFilePath) +{ + mockCompressor.addFilePath(filePath1); + + ASSERT_THAT(mockCompressor.takeFilePaths(), ElementsAre(filePath1)); +} + +TEST_F(ChangedFilePathCompressor, NoFilePathsAferTakenThem) +{ + mockCompressor.addFilePath(filePath1); + + mockCompressor.takeFilePaths(); + + ASSERT_THAT(mockCompressor.takeFilePaths(), IsEmpty()); +} + +TEST_F(ChangedFilePathCompressor, CallRestartTimerAfterAddingPath) +{ + EXPECT_CALL(mockCompressor, restartTimer()); + + mockCompressor.addFilePath(filePath1); +} + +TEST_F(ChangedFilePathCompressor, CallTimeOutAfterAddingPath) +{ + EXPECT_CALL(mockCompressor, callbackCalled(ElementsAre(filePath1, filePath2))); + + compressor.addFilePath(filePath1); + compressor.addFilePath(filePath2); +} + +void ChangedFilePathCompressor::SetUp() +{ + compressor.setCallback([&] (Utils::SmallStringVector &&filePaths) { + mockCompressor.callbackCalled(filePaths); + }); +} + +} diff --git a/tests/unit/unittest/clangpathwatcher-test.cpp b/tests/unit/unittest/clangpathwatcher-test.cpp index 04fd2bf9129..62baf6befa1 100644 --- a/tests/unit/unittest/clangpathwatcher-test.cpp +++ b/tests/unit/unittest/clangpathwatcher-test.cpp @@ -25,6 +25,7 @@ #include "googletest.h" +#include "faketimer.h" #include "mockqfilesystemwatcher.h" #include "mockclangpathwatchernotifier.h" @@ -39,7 +40,7 @@ using testing::IsEmpty; using testing::SizeIs; using testing::NiceMock; -using Watcher = ClangBackEnd::ClangPathWatcher>; +using Watcher = ClangBackEnd::ClangPathWatcher, FakeTimer>; using ClangBackEnd::WatcherEntry; class ClangPathWatcher : public testing::Test @@ -310,4 +311,14 @@ TEST_F(ClangPathWatcher, NotifyFileChange) mockQFileSytemWatcher.fileChanged(path1.toQString()); } +TEST_F(ClangPathWatcher, TwoNotifyFileChanges) +{ + watcher.addEntries({watcherEntry1, watcherEntry2, watcherEntry3, watcherEntry4, watcherEntry5}); + + EXPECT_CALL(notifier, pathsWithIdsChanged(ElementsAre(id2, id3, id1))); + + mockQFileSytemWatcher.fileChanged(path2.toQString()); + mockQFileSytemWatcher.fileChanged(path1.toQString()); +} + } diff --git a/tests/unit/unittest/faketimer.cpp b/tests/unit/unittest/faketimer.cpp new file mode 100644 index 00000000000..61e26893734 --- /dev/null +++ b/tests/unit/unittest/faketimer.cpp @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "faketimer.h" + +FakeTimer::~FakeTimer() +{ + emitTimoutIfStarted(); +} + +void FakeTimer::start(int) +{ + m_isStarted = true; +} + +void FakeTimer::setSingleShot(bool) +{ +} + +void FakeTimer::emitTimoutIfStarted() +{ + if (m_isStarted) + emit timeout(); +} diff --git a/tests/unit/unittest/faketimer.h b/tests/unit/unittest/faketimer.h new file mode 100644 index 00000000000..ef6945205c7 --- /dev/null +++ b/tests/unit/unittest/faketimer.h @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ +#pragma once + +#include + +class FakeTimer : public QObject +{ + Q_OBJECT + +public: + ~FakeTimer(); + + void start(int interval); + void setSingleShot(bool); + + void emitTimoutIfStarted(); + +signals: + void timeout(); + +private: + bool m_isStarted = false; +}; diff --git a/tests/unit/unittest/mockchangedfilepathcompressor.h b/tests/unit/unittest/mockchangedfilepathcompressor.h new file mode 100644 index 00000000000..69ce485eeb9 --- /dev/null +++ b/tests/unit/unittest/mockchangedfilepathcompressor.h @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "googletest.h" + +#include "faketimer.h" + +#include + +class MockChangedFilePathCompressor : public ClangBackEnd::ChangedFilePathCompressor +{ +public: + MOCK_METHOD0(restartTimer, + void ()); + + MOCK_METHOD1(callbackCalled, + void (const Utils::SmallStringVector &filePaths)); +}; + diff --git a/tests/unit/unittest/unittest.pro b/tests/unit/unittest/unittest.pro index 9a902c53ba0..fe8b68c68b9 100644 --- a/tests/unit/unittest/unittest.pro +++ b/tests/unit/unittest/unittest.pro @@ -47,7 +47,9 @@ SOURCES += \ clientserveroutsideprocess-test.cpp \ clangpathwatcher-test.cpp \ projectparts-test.cpp \ - stringcache-test.cpp + stringcache-test.cpp \ + changedfilepathcompressor-test.cpp \ + faketimer.cpp !isEmpty(LIBCLANG_LIBS) { SOURCES += \ @@ -144,7 +146,9 @@ HEADERS += \ mockqfilesystemwatcher.h \ mockclangpathwatcher.h \ mockprojectparts.h \ - mockclangpathwatchernotifier.h + mockclangpathwatchernotifier.h \ + mockchangedfilepathcompressor.h \ + faketimer.h !isEmpty(LIBCLANG_LIBS) { HEADERS += \