diff options
author | Ivan Donchevskii <ivan.donchevskii@qt.io> | 2018-08-01 10:49:29 +0200 |
---|---|---|
committer | Ivan Donchevskii <ivan.donchevskii@qt.io> | 2018-08-01 12:39:28 +0000 |
commit | 61f9ff7bb83fe0e09b576b30c85ce88ca83e7b77 (patch) | |
tree | 5ab31c663f803a6f282c00727a80815e8ad69ba6 /tools | |
parent | 5093ff0b282ace605d44a1553a61a82afc921055 (diff) |
Index whild build. Part 3
https://reviews.llvm.org/D41407
Change-Id: I73563f2318d48e70f96b1082337f3b5a7a6c62b2
Reviewed-by: Ivan Donchevskii <ivan.donchevskii@qt.io>
Diffstat (limited to 'tools')
-rw-r--r-- | tools/CMakeLists.txt | 1 | ||||
-rw-r--r-- | tools/IndexStore/CMakeLists.txt | 94 | ||||
-rw-r--r-- | tools/IndexStore/IndexStore.cpp | 636 | ||||
-rw-r--r-- | tools/IndexStore/IndexStore.exports | 69 | ||||
-rw-r--r-- | tools/c-index-test/CMakeLists.txt | 20 | ||||
-rw-r--r-- | tools/c-index-test/JSONAggregation.cpp | 421 | ||||
-rw-r--r-- | tools/c-index-test/JSONAggregation.h | 24 | ||||
-rw-r--r-- | tools/c-index-test/core_main.cpp | 639 |
8 files changed, 1900 insertions, 4 deletions
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 9f76d36dba..f50305c2f1 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -10,6 +10,7 @@ add_clang_subdirectory(clang-import-test) add_clang_subdirectory(clang-offload-bundler) add_clang_subdirectory(c-index-test) +add_clang_subdirectory(IndexStore) add_clang_subdirectory(clang-rename) add_clang_subdirectory(clang-refactor) diff --git a/tools/IndexStore/CMakeLists.txt b/tools/IndexStore/CMakeLists.txt new file mode 100644 index 0000000000..8ad6499117 --- /dev/null +++ b/tools/IndexStore/CMakeLists.txt @@ -0,0 +1,94 @@ +include(CheckIncludeFiles) + +set(SOURCES + IndexStore.cpp + + ADDITIONAL_HEADERS + ../../include/indexstore/indexstore.h + ../../include/indexstore/IndexStoreCXX.h + ) + +set(LIBS + clangDirectoryWatcher + clangIndex +) + +set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/IndexStore.exports) + +set(ENABLE_SHARED SHARED) + +if(WIN32) + set(output_name "libIndexStore") +else() + set(output_name "IndexStore") +endif() + +# FIXME: needs to be ported to non-Apple platforms. +if(APPLE) + +add_clang_library(IndexStore ${ENABLE_SHARED} ${ENABLE_STATIC} + OUTPUT_NAME ${output_name} + ${SOURCES} + + LINK_LIBS + ${LIBS} + + LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} + Core + Support + ) + +set(INDEXSTORE_LIBRARY_VERSION "${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}") + +if(ENABLE_SHARED) + if(WIN32) + set_target_properties(IndexStore + PROPERTIES + VERSION ${INDEXSTORE_LIBRARY_VERSION} + DEFINE_SYMBOL _CINDEX_LIB_) + elseif(APPLE) + set(INDEXSTORE_LINK_FLAGS " -Wl,-compatibility_version -Wl,1") + set(INDEXSTORE_LINK_FLAGS "${INDEXSTORE_LINK_FLAGS} -Wl,-current_version -Wl,${INDEXSTORE_LIBRARY_VERSION}") + + check_include_files("CoreServices/CoreServices.h" HAVE_CORESERVICES_H) + if(HAVE_CORESERVICES_H) + set(INDEXSTORE_LINK_FLAGS "${INDEXSTORE_LINK_FLAGS} -framework CoreServices") + endif() + + set_property(TARGET IndexStore APPEND_STRING PROPERTY + LINK_FLAGS ${INDEXSTORE_LINK_FLAGS}) + else() + set_target_properties(IndexStore + PROPERTIES + VERSION ${INDEXSTORE_LIBRARY_VERSION} + DEFINE_SYMBOL _CINDEX_LIB_) + endif() +endif() + +if (LLVM_INSTALL_TOOLCHAIN_ONLY) + install(TARGETS IndexStore + COMPONENT IndexStore + LIBRARY DESTINATION lib${LLVM_LIBDIR_SUFFIX} + ARCHIVE DESTINATION lib${LLVM_LIBDIR_SUFFIX} + RUNTIME DESTINATION bin) + + if (NOT CMAKE_CONFIGURATION_TYPES) + add_custom_target(install-IndexStore + DEPENDS IndexStore + COMMAND "${CMAKE_COMMAND}" + -DCMAKE_INSTALL_COMPONENT=IndexStore + -P "${CMAKE_BINARY_DIR}/cmake_install.cmake") + endif() +endif() + +set(INDEXSTORE_HEADERS_INSTALL_DESTINATION "local/include") + +install(DIRECTORY ../../include/indexstore + COMPONENT IndexStore + DESTINATION "${INDEXSTORE_HEADERS_INSTALL_DESTINATION}" + FILES_MATCHING + PATTERN "*.h" + PATTERN ".svn" EXCLUDE + ) +endif() diff --git a/tools/IndexStore/IndexStore.cpp b/tools/IndexStore/IndexStore.cpp new file mode 100644 index 0000000000..c73b9a9aa7 --- /dev/null +++ b/tools/IndexStore/IndexStore.cpp @@ -0,0 +1,636 @@ +//===- IndexStore.cpp - Index store API -----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the API for the index store. +// +//===----------------------------------------------------------------------===// + +#include "indexstore/indexstore.h" +#include "clang/DirectoryWatcher/DirectoryWatcher.h" +#include "clang/Index/IndexDataStore.h" +#include "clang/Index/IndexDataStoreSymbolUtils.h" +#include "clang/Index/IndexRecordReader.h" +#include "clang/Index/IndexUnitReader.h" +#include "clang/Index/IndexUnitWriter.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/Chrono.h" +#include <Block.h> + +using namespace clang; +using namespace clang::index; +using namespace llvm; + +static indexstore_string_ref_t toIndexStoreString(StringRef str) { + return indexstore_string_ref_t{str.data(), str.size()}; +} + +static timespec toTimeSpec(sys::TimePoint<> tp) { + std::chrono::seconds sec = + std::chrono::time_point_cast<std::chrono::seconds>(tp).time_since_epoch(); + std::chrono::nanoseconds nsec = + std::chrono::time_point_cast<std::chrono::nanoseconds>(tp - sec) + .time_since_epoch(); + timespec ts; + ts.tv_sec = sec.count(); + ts.tv_nsec = nsec.count(); + return ts; +} + +namespace { + +struct IndexStoreError { + std::string Error; +}; + +} // anonymous namespace + +const char *indexstore_error_get_description(indexstore_error_t err) { + return static_cast<IndexStoreError *>(err)->Error.c_str(); +} + +void indexstore_error_dispose(indexstore_error_t err) { + delete static_cast<IndexStoreError *>(err); +} + +unsigned indexstore_format_version(void) { + return IndexDataStore::getFormatVersion(); +} + +indexstore_t indexstore_store_create(const char *store_path, + indexstore_error_t *c_error) { + std::unique_ptr<IndexDataStore> store; + std::string error; + store = IndexDataStore::create(store_path, error); + if (!store) { + if (c_error) + *c_error = new IndexStoreError{error}; + return nullptr; + } + return store.release(); +} + +void indexstore_store_dispose(indexstore_t store) { + delete static_cast<IndexDataStore *>(store); +} + +#if INDEXSTORE_HAS_BLOCKS +bool indexstore_store_units_apply( + indexstore_t c_store, unsigned sorted, + bool (^applier)(indexstore_string_ref_t unit_name)) { + IndexDataStore *store = static_cast<IndexDataStore *>(c_store); + return store->foreachUnitName(sorted, [&](StringRef unitName) -> bool { + return applier(toIndexStoreString(unitName)); + }); +} + +size_t indexstore_unit_event_notification_get_events_count( + indexstore_unit_event_notification_t c_evtnote) { + auto *evtnote = + static_cast<IndexDataStore::UnitEventNotification *>(c_evtnote); + return evtnote->Events.size(); +} + +indexstore_unit_event_t indexstore_unit_event_notification_get_event( + indexstore_unit_event_notification_t c_evtnote, size_t index) { + auto *evtnote = + static_cast<IndexDataStore::UnitEventNotification *>(c_evtnote); + return (indexstore_unit_event_t)&evtnote->Events[index]; +} + +bool indexstore_unit_event_notification_is_initial( + indexstore_unit_event_notification_t c_evtnote) { + auto *evtnote = + static_cast<IndexDataStore::UnitEventNotification *>(c_evtnote); + return evtnote->IsInitial; +} + +indexstore_unit_event_kind_t +indexstore_unit_event_get_kind(indexstore_unit_event_t c_evt) { + auto *evt = static_cast<IndexDataStore::UnitEvent *>(c_evt); + indexstore_unit_event_kind_t k; + switch (evt->Kind) { + case IndexDataStore::UnitEventKind::Added: + k = INDEXSTORE_UNIT_EVENT_ADDED; + break; + case IndexDataStore::UnitEventKind::Removed: + k = INDEXSTORE_UNIT_EVENT_REMOVED; + break; + case IndexDataStore::UnitEventKind::Modified: + k = INDEXSTORE_UNIT_EVENT_MODIFIED; + break; + case IndexDataStore::UnitEventKind::DirectoryDeleted: + k = INDEXSTORE_UNIT_EVENT_DIRECTORY_DELETED; + break; + } + return k; +} + +indexstore_string_ref_t +indexstore_unit_event_get_unit_name(indexstore_unit_event_t c_evt) { + auto *evt = static_cast<IndexDataStore::UnitEvent *>(c_evt); + return toIndexStoreString(evt->UnitName); +} + +timespec +indexstore_unit_event_get_modification_time(indexstore_unit_event_t c_evt) { + auto *evt = static_cast<IndexDataStore::UnitEvent *>(c_evt); + return evt->ModTime; +} + +void indexstore_store_set_unit_event_handler( + indexstore_t c_store, indexstore_unit_event_handler_t blk_handler) { + IndexDataStore *store = static_cast<IndexDataStore *>(c_store); + if (!blk_handler) { + store->setUnitEventHandler(nullptr); + return; + } + + class BlockWrapper { + indexstore_unit_event_handler_t blk_handler; + + public: + BlockWrapper(indexstore_unit_event_handler_t handler) { + blk_handler = Block_copy(handler); + } + BlockWrapper(const BlockWrapper &other) { + blk_handler = Block_copy(other.blk_handler); + } + ~BlockWrapper() { Block_release(blk_handler); } + + void operator()(indexstore_unit_event_notification_t evt_note) const { + blk_handler(evt_note); + } + }; + + BlockWrapper handler(blk_handler); + + store->setUnitEventHandler( + [handler](IndexDataStore::UnitEventNotification evtNote) { + handler(&evtNote); + }); +} +#endif + +bool indexstore_store_start_unit_event_listening( + indexstore_t c_store, indexstore_unit_event_listen_options_t *client_opts, + size_t listen_options_struct_size, indexstore_error_t *c_error) { + IndexDataStore *store = static_cast<IndexDataStore *>(c_store); + indexstore_unit_event_listen_options_t listen_opts; + memset(&listen_opts, 0, sizeof(listen_opts)); + unsigned clientOptSize = listen_options_struct_size < sizeof(listen_opts) + ? listen_options_struct_size + : sizeof(listen_opts); + memcpy(&listen_opts, client_opts, clientOptSize); + + std::string error; + auto createFn = + [](StringRef Path, AbstractDirectoryWatcher::EventReceiver Receiver, + bool waitInitialSync, + std::string &Error) -> std::unique_ptr<AbstractDirectoryWatcher> { + return DirectoryWatcher::create(Path, std::move(Receiver), waitInitialSync, + Error); + }; + bool err = store->startEventListening(createFn, listen_opts.wait_initial_sync, + error); + if (err && c_error) + *c_error = new IndexStoreError{error}; + return err; +} + +void indexstore_store_stop_unit_event_listening(indexstore_t c_store) { + IndexDataStore *store = static_cast<IndexDataStore *>(c_store); + store->stopEventListening(); +} + +void indexstore_store_discard_unit(indexstore_t c_store, + const char *unit_name) { + IndexDataStore *store = static_cast<IndexDataStore *>(c_store); + store->discardUnit(unit_name); +} + +void indexstore_store_discard_record(indexstore_t c_store, + const char *record_name) { + IndexDataStore *store = static_cast<IndexDataStore *>(c_store); + store->discardRecord(record_name); +} + +void indexstore_store_purge_stale_data(indexstore_t c_store) { + IndexDataStore *store = static_cast<IndexDataStore *>(c_store); + store->purgeStaleData(); +} + +indexstore_symbol_kind_t indexstore_symbol_get_kind(indexstore_symbol_t sym) { + return getIndexStoreKind(static_cast<IndexRecordDecl *>(sym)->SymInfo.Kind); +} + +indexstore_symbol_subkind_t +indexstore_symbol_get_subkind(indexstore_symbol_t sym) { + return getIndexStoreSubKind( + static_cast<IndexRecordDecl *>(sym)->SymInfo.SubKind); +} + +indexstore_symbol_language_t +indexstore_symbol_get_language(indexstore_symbol_t sym) { + return getIndexStoreLang(static_cast<IndexRecordDecl *>(sym)->SymInfo.Lang); +} + +uint64_t indexstore_symbol_get_properties(indexstore_symbol_t sym) { + return getIndexStoreProperties( + static_cast<IndexRecordDecl *>(sym)->SymInfo.Properties); +} + +uint64_t indexstore_symbol_get_roles(indexstore_symbol_t sym) { + return getIndexStoreRoles(static_cast<IndexRecordDecl *>(sym)->Roles); +} + +uint64_t indexstore_symbol_get_related_roles(indexstore_symbol_t sym) { + return getIndexStoreRoles(static_cast<IndexRecordDecl *>(sym)->RelatedRoles); +} + +indexstore_string_ref_t indexstore_symbol_get_name(indexstore_symbol_t sym) { + auto *D = static_cast<IndexRecordDecl *>(sym); + return toIndexStoreString(D->Name); +} + +indexstore_string_ref_t indexstore_symbol_get_usr(indexstore_symbol_t sym) { + auto *D = static_cast<IndexRecordDecl *>(sym); + return toIndexStoreString(D->USR); +} + +indexstore_string_ref_t +indexstore_symbol_get_codegen_name(indexstore_symbol_t sym) { + auto *D = static_cast<IndexRecordDecl *>(sym); + return toIndexStoreString(D->CodeGenName); +} + +uint64_t +indexstore_symbol_relation_get_roles(indexstore_symbol_relation_t sym_rel) { + return getIndexStoreRoles(static_cast<IndexRecordRelation *>(sym_rel)->Roles); +} + +indexstore_symbol_t +indexstore_symbol_relation_get_symbol(indexstore_symbol_relation_t sym_rel) { + return (indexstore_symbol_t) static_cast<IndexRecordRelation *>(sym_rel)->Dcl; +} + +indexstore_symbol_t +indexstore_occurrence_get_symbol(indexstore_occurrence_t occur) { + return (indexstore_symbol_t) static_cast<IndexRecordOccurrence *>(occur)->Dcl; +} + +#if INDEXSTORE_HAS_BLOCKS +bool indexstore_occurrence_relations_apply( + indexstore_occurrence_t occur, + bool (^applier)(indexstore_symbol_relation_t symbol_rel)) { + auto *recOccur = static_cast<IndexRecordOccurrence *>(occur); + for (auto &rel : recOccur->Relations) { + if (!applier(&rel)) + return false; + } + return true; +} +#endif + +uint64_t indexstore_occurrence_get_roles(indexstore_occurrence_t occur) { + return static_cast<IndexRecordOccurrence *>(occur)->Roles; +} + +void indexstore_occurrence_get_line_col(indexstore_occurrence_t occur, + unsigned *line, unsigned *column) { + auto *recOccur = static_cast<IndexRecordOccurrence *>(occur); + if (line) + *line = recOccur->Line; + if (column) + *column = recOccur->Column; +} + +typedef void *indexstore_record_reader_t; + +indexstore_record_reader_t +indexstore_record_reader_create(indexstore_t c_store, const char *record_name, + indexstore_error_t *c_error) { + IndexDataStore *store = static_cast<IndexDataStore *>(c_store); + std::unique_ptr<IndexRecordReader> reader; + std::string error; + reader = IndexRecordReader::createWithRecordFilename( + record_name, store->getFilePath(), error); + if (!reader) { + if (c_error) + *c_error = new IndexStoreError{error}; + return nullptr; + } + return reader.release(); +} + +void indexstore_record_reader_dispose(indexstore_record_reader_t rdr) { + auto *reader = static_cast<IndexRecordReader *>(rdr); + delete reader; +} + +#if INDEXSTORE_HAS_BLOCKS +/// Goes through the symbol data and passes symbols to \c receiver, for the +/// symbol data that \c filter returns true on. +/// +/// This allows allocating memory only for the record symbols that the caller is +/// interested in. +bool indexstore_record_reader_search_symbols( + indexstore_record_reader_t rdr, + bool (^filter)(indexstore_symbol_t symbol, bool *stop), + void (^receiver)(indexstore_symbol_t symbol)) { + auto *reader = static_cast<IndexRecordReader *>(rdr); + + auto filterFn = + [&](const IndexRecordDecl &D) -> IndexRecordReader::DeclSearchReturn { + bool stop = false; + bool accept = filter((indexstore_symbol_t)&D, &stop); + return {accept, !stop}; + }; + auto receiverFn = [&](const IndexRecordDecl *D) { + receiver((indexstore_symbol_t)D); + }; + + return reader->searchDecls(filterFn, receiverFn); +} + +bool indexstore_record_reader_symbols_apply( + indexstore_record_reader_t rdr, bool nocache, + bool (^applier)(indexstore_symbol_t symbol)) { + auto *reader = static_cast<IndexRecordReader *>(rdr); + auto receiverFn = [&](const IndexRecordDecl *D) -> bool { + return applier((indexstore_symbol_t)D); + }; + return reader->foreachDecl(nocache, receiverFn); +} + +bool indexstore_record_reader_occurrences_apply( + indexstore_record_reader_t rdr, + bool (^applier)(indexstore_occurrence_t occur)) { + auto *reader = static_cast<IndexRecordReader *>(rdr); + auto receiverFn = [&](const IndexRecordOccurrence &RO) -> bool { + return applier((indexstore_occurrence_t)&RO); + }; + return reader->foreachOccurrence(receiverFn); +} + +bool indexstore_record_reader_occurrences_in_line_range_apply( + indexstore_record_reader_t rdr, unsigned line_start, unsigned line_count, + bool (^applier)(indexstore_occurrence_t occur)) { + auto *reader = static_cast<IndexRecordReader *>(rdr); + auto receiverFn = [&](const IndexRecordOccurrence &RO) -> bool { + return applier((indexstore_occurrence_t)&RO); + }; + return reader->foreachOccurrenceInLineRange(line_start, line_count, + receiverFn); +} + +/// \param symbols if non-zero \c symbols_count, indicates the list of symbols +/// that we want to get occurrences for. An empty array indicates that we want +/// occurrences for all symbols. +/// \param related_symbols Same as \c symbols but for related symbols. +bool indexstore_record_reader_occurrences_of_symbols_apply( + indexstore_record_reader_t rdr, indexstore_symbol_t *symbols, + size_t symbols_count, indexstore_symbol_t *related_symbols, + size_t related_symbols_count, + bool (^applier)(indexstore_occurrence_t occur)) { + auto *reader = static_cast<IndexRecordReader *>(rdr); + auto receiverFn = [&](const IndexRecordOccurrence &RO) -> bool { + return applier((indexstore_occurrence_t)&RO); + }; + return reader->foreachOccurrence( + {(IndexRecordDecl **)symbols, symbols_count}, + {(IndexRecordDecl **)related_symbols, related_symbols_count}, receiverFn); +} +#endif + +size_t indexstore_store_get_unit_name_from_output_path(indexstore_t store, + const char *output_path, + char *name_buf, + size_t buf_size) { + SmallString<256> unitName; + IndexUnitWriter::getUnitNameForAbsoluteOutputFile(output_path, unitName); + size_t nameLen = unitName.size(); + strlcpy(name_buf, unitName.c_str(), buf_size); + return nameLen; +} + +bool indexstore_store_get_unit_modification_time(indexstore_t c_store, + const char *unit_name, + int64_t *seconds, + int64_t *nanoseconds, + indexstore_error_t *c_error) { + IndexDataStore *store = static_cast<IndexDataStore *>(c_store); + std::string error; + // FIXME: This provides mod time with second-only accuracy. + auto optModTime = IndexUnitReader::getModificationTimeForUnit( + unit_name, store->getFilePath(), error); + if (!optModTime) { + if (c_error) + *c_error = new IndexStoreError{error}; + return true; + } + + timespec ts = toTimeSpec(*optModTime); + if (seconds) + *seconds = ts.tv_sec; + if (nanoseconds) + *nanoseconds = ts.tv_nsec; + + return false; +} + +indexstore_unit_reader_t +indexstore_unit_reader_create(indexstore_t c_store, const char *unit_name, + indexstore_error_t *c_error) { + IndexDataStore *store = static_cast<IndexDataStore *>(c_store); + std::unique_ptr<IndexUnitReader> reader; + std::string error; + reader = IndexUnitReader::createWithUnitFilename(unit_name, + store->getFilePath(), error); + if (!reader) { + if (c_error) + *c_error = new IndexStoreError{error}; + return nullptr; + } + return reader.release(); +} + +void indexstore_unit_reader_dispose(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader *>(rdr); + delete reader; +} + +indexstore_string_ref_t +indexstore_unit_reader_get_provider_identifier(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader *>(rdr); + return toIndexStoreString(reader->getProviderIdentifier()); +} + +indexstore_string_ref_t +indexstore_unit_reader_get_provider_version(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader *>(rdr); + return toIndexStoreString(reader->getProviderVersion()); +} + +void indexstore_unit_reader_get_modification_time(indexstore_unit_reader_t rdr, + int64_t *seconds, + int64_t *nanoseconds) { + auto reader = static_cast<IndexUnitReader *>(rdr); + // FIXME: This provides mod time with second-only accuracy. + sys::TimePoint<> timeVal = reader->getModificationTime(); + timespec ts = toTimeSpec(timeVal); + if (seconds) + *seconds = ts.tv_sec; + if (nanoseconds) + *nanoseconds = ts.tv_nsec; +} + +bool indexstore_unit_reader_is_system_unit(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader *>(rdr); + return reader->isSystemUnit(); +} + +bool indexstore_unit_reader_is_module_unit(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader *>(rdr); + return reader->isModuleUnit(); +} + +bool indexstore_unit_reader_is_debug_compilation(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader *>(rdr); + return reader->isDebugCompilation(); +} + +bool indexstore_unit_reader_has_main_file(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader *>(rdr); + return reader->hasMainFile(); +} + +indexstore_string_ref_t +indexstore_unit_reader_get_main_file(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader *>(rdr); + return toIndexStoreString(reader->getMainFilePath()); +} + +indexstore_string_ref_t +indexstore_unit_reader_get_module_name(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader *>(rdr); + return toIndexStoreString(reader->getModuleName()); +} + +indexstore_string_ref_t +indexstore_unit_reader_get_working_dir(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader *>(rdr); + return toIndexStoreString(reader->getWorkingDirectory()); +} + +indexstore_string_ref_t +indexstore_unit_reader_get_output_file(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader *>(rdr); + return toIndexStoreString(reader->getOutputFile()); +} + +indexstore_string_ref_t +indexstore_unit_reader_get_sysroot_path(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader *>(rdr); + return toIndexStoreString(reader->getSysrootPath()); +} + +indexstore_string_ref_t +indexstore_unit_reader_get_target(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader *>(rdr); + return toIndexStoreString(reader->getTarget()); +} + +indexstore_unit_dependency_kind_t +indexstore_unit_dependency_get_kind(indexstore_unit_dependency_t c_dep) { + auto dep = static_cast<const IndexUnitReader::DependencyInfo *>(c_dep); + switch (dep->Kind) { + case IndexUnitReader::DependencyKind::Unit: + return INDEXSTORE_UNIT_DEPENDENCY_UNIT; + case IndexUnitReader::DependencyKind::Record: + return INDEXSTORE_UNIT_DEPENDENCY_RECORD; + case IndexUnitReader::DependencyKind::File: + return INDEXSTORE_UNIT_DEPENDENCY_FILE; + } +} + +bool indexstore_unit_dependency_is_system(indexstore_unit_dependency_t c_dep) { + auto dep = static_cast<const IndexUnitReader::DependencyInfo *>(c_dep); + return dep->IsSystem; +} + +indexstore_string_ref_t +indexstore_unit_dependency_get_filepath(indexstore_unit_dependency_t c_dep) { + auto dep = static_cast<const IndexUnitReader::DependencyInfo *>(c_dep); + return toIndexStoreString(dep->FilePath); +} + +indexstore_string_ref_t +indexstore_unit_dependency_get_modulename(indexstore_unit_dependency_t c_dep) { + auto dep = static_cast<const IndexUnitReader::DependencyInfo *>(c_dep); + return toIndexStoreString(dep->ModuleName); +} + +indexstore_string_ref_t +indexstore_unit_dependency_get_name(indexstore_unit_dependency_t c_dep) { + auto dep = static_cast<const IndexUnitReader::DependencyInfo *>(c_dep); + return toIndexStoreString(dep->UnitOrRecordName); +} + +time_t indexstore_unit_dependency_get_modification_time( + indexstore_unit_dependency_t c_dep) { + auto dep = static_cast<const IndexUnitReader::DependencyInfo *>(c_dep); + return dep->ModTime; +} + +size_t +indexstore_unit_dependency_get_file_size(indexstore_unit_dependency_t c_dep) { + auto dep = static_cast<const IndexUnitReader::DependencyInfo *>(c_dep); + return dep->FileSize; +} + +indexstore_string_ref_t +indexstore_unit_include_get_source_path(indexstore_unit_include_t c_inc) { + auto inc = static_cast<const IndexUnitReader::IncludeInfo *>(c_inc); + return toIndexStoreString(inc->SourcePath); +} + +indexstore_string_ref_t +indexstore_unit_include_get_target_path(indexstore_unit_include_t c_inc) { + auto inc = static_cast<const IndexUnitReader::IncludeInfo *>(c_inc); + return toIndexStoreString(inc->TargetPath); +} + +unsigned +indexstore_unit_include_get_source_line(indexstore_unit_include_t c_inc) { + auto inc = static_cast<const IndexUnitReader::IncludeInfo *>(c_inc); + return inc->SourceLine; +} + +#if INDEXSTORE_HAS_BLOCKS +bool indexstore_unit_reader_dependencies_apply( + indexstore_unit_reader_t rdr, + bool (^applier)(indexstore_unit_dependency_t)) { + auto reader = static_cast<IndexUnitReader *>(rdr); + return reader->foreachDependency( + [&](const IndexUnitReader::DependencyInfo &depInfo) -> bool { + return applier((void *)&depInfo); + }); +} + +bool indexstore_unit_reader_includes_apply( + indexstore_unit_reader_t rdr, bool (^applier)(indexstore_unit_include_t)) { + auto reader = static_cast<IndexUnitReader *>(rdr); + return reader->foreachInclude( + [&](const IndexUnitReader::IncludeInfo &incInfo) -> bool { + return applier((void *)&incInfo); + }); +} +#endif diff --git a/tools/IndexStore/IndexStore.exports b/tools/IndexStore/IndexStore.exports new file mode 100644 index 0000000000..70c174f360 --- /dev/null +++ b/tools/IndexStore/IndexStore.exports @@ -0,0 +1,69 @@ +indexstore_error_get_description +indexstore_error_dispose +indexstore_format_version +indexstore_store_create +indexstore_store_dispose +indexstore_store_get_unit_modification_time +indexstore_store_get_unit_name_from_output_path +indexstore_store_units_apply +indexstore_store_set_unit_event_handler +indexstore_store_start_unit_event_listening +indexstore_store_stop_unit_event_listening +indexstore_store_discard_unit +indexstore_store_discard_record +indexstore_store_purge_stale_data +indexstore_symbol_get_kind +indexstore_symbol_get_language +indexstore_symbol_get_properties +indexstore_symbol_get_roles +indexstore_symbol_get_related_roles +indexstore_symbol_get_subkind +indexstore_symbol_get_name +indexstore_symbol_get_usr +indexstore_symbol_get_codegen_name +indexstore_symbol_relation_get_roles +indexstore_symbol_relation_get_symbol +indexstore_occurrence_get_symbol +indexstore_occurrence_get_roles +indexstore_occurrence_get_line_col +indexstore_occurrence_relations_apply +indexstore_record_reader_create +indexstore_record_reader_dispose +indexstore_record_reader_search_symbols +indexstore_record_reader_symbols_apply +indexstore_record_reader_occurrences_apply +indexstore_record_reader_occurrences_in_line_range_apply +indexstore_record_reader_occurrences_of_symbols_apply +indexstore_unit_dependency_get_kind +indexstore_unit_dependency_get_filepath +indexstore_unit_dependency_get_file_size +indexstore_unit_dependency_get_modification_time +indexstore_unit_dependency_get_modulename +indexstore_unit_dependency_get_name +indexstore_unit_dependency_is_system +indexstore_unit_event_get_kind +indexstore_unit_event_get_modification_time +indexstore_unit_event_get_unit_name +indexstore_unit_event_notification_get_event +indexstore_unit_event_notification_get_events_count +indexstore_unit_event_notification_is_initial +indexstore_unit_reader_create +indexstore_unit_reader_dispose +indexstore_unit_reader_get_main_file +indexstore_unit_reader_get_modification_time +indexstore_unit_reader_get_module_name +indexstore_unit_reader_get_provider_identifier +indexstore_unit_reader_get_provider_version +indexstore_unit_reader_get_working_dir +indexstore_unit_reader_get_output_file +indexstore_unit_reader_get_sysroot_path +indexstore_unit_reader_get_target +indexstore_unit_reader_dependencies_apply +indexstore_unit_reader_includes_apply +indexstore_unit_reader_has_main_file +indexstore_unit_reader_is_debug_compilation +indexstore_unit_reader_is_module_unit +indexstore_unit_reader_is_system_unit +indexstore_unit_include_get_source_path +indexstore_unit_include_get_target_path +indexstore_unit_include_get_source_line diff --git a/tools/c-index-test/CMakeLists.txt b/tools/c-index-test/CMakeLists.txt index d38c7bb287..82245dc08a 100644 --- a/tools/c-index-test/CMakeLists.txt +++ b/tools/c-index-test/CMakeLists.txt @@ -1,3 +1,5 @@ +include(CheckIncludeFiles) + set(LLVM_LINK_COMPONENTS support ) @@ -5,8 +7,15 @@ set(LLVM_LINK_COMPONENTS add_clang_executable(c-index-test c-index-test.c core_main.cpp + JSONAggregation.cpp ) +set(INDEXSTORE_LIB) +set(CINDEXTEST_LIBS) +if(APPLE) + set(INDEXSTORE_LIB IndexStore) +endif() + if(NOT MSVC) set_property( SOURCE c-index-test.c @@ -20,17 +29,21 @@ if (LLVM_BUILD_STATIC) libclang_static clangCodeGen clangIndex + ${CINDEXTEST_LIBS} ) else() target_link_libraries(c-index-test PRIVATE libclang + ${INDEXSTORE_LIB} clangAST clangBasic clangCodeGen + clangDirectoryWatcher clangFrontend clangIndex clangSerialization + ${CINDEXTEST_LIBS} ) endif() @@ -44,6 +57,13 @@ if (CLANG_HAVE_LIBXML) target_link_libraries(c-index-test PRIVATE ${LIBXML2_LIBRARIES}) endif() +if(APPLE) + check_include_files("CoreServices/CoreServices.h" HAVE_CORESERVICES_H) + if(HAVE_CORESERVICES_H) + target_link_libraries(c-index-test PRIVATE "-framework CoreServices") + endif() +endif() + if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY) if(INTERNAL_INSTALL_PREFIX) set(INSTALL_DESTINATION "${INTERNAL_INSTALL_PREFIX}/bin") diff --git a/tools/c-index-test/JSONAggregation.cpp b/tools/c-index-test/JSONAggregation.cpp new file mode 100644 index 0000000000..4c761b6f8f --- /dev/null +++ b/tools/c-index-test/JSONAggregation.cpp @@ -0,0 +1,421 @@ +//===--- JSONAggregation.cpp - Index data aggregation in JSON format ------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "JSONAggregation.h" +#include "indexstore/IndexStoreCXX.h" +#include "clang/Index/IndexDataStoreSymbolUtils.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace clang::index; +using namespace indexstore; +using namespace llvm; + +#if INDEXSTORE_HAS_BLOCKS + +namespace { + +typedef size_t FilePathIndex; +typedef size_t RecordIndex; +typedef size_t SymbolIndex; + +struct UnitSourceInfo { + FilePathIndex FilePath; + SmallVector<RecordIndex, 2> AssociatedRecords; +}; + +struct UnitInfo { + std::string Name; + SmallVector<UnitSourceInfo, 8> Sources; + SmallVector<std::string, 3> UnitDepends; + FilePathIndex OutFile; + StringRef Triple; +}; + +struct SymbolInfo { + SymbolKind Kind; + SymbolLanguage Lang; + StringRef USR; + StringRef Name; + StringRef CodegenName; + SymbolRoleSet Roles = 0; + SymbolRoleSet RelatedRoles = 0; +}; + +struct SymbolRelationInfo { + SymbolIndex RelatedSymbol; + SymbolRoleSet Roles; + SymbolRelationInfo(SymbolIndex relSymbol, SymbolRoleSet roles) + : RelatedSymbol(relSymbol), Roles(roles) {} +}; + +struct SymbolOccurrenceInfo { + SymbolIndex Symbol; + SymbolRoleSet Roles = 0; + std::vector<SymbolRelationInfo> Relations; + unsigned Line; + unsigned Column; +}; + +struct RecordInfo { + SmallVector<SymbolOccurrenceInfo, 8> Occurrences; +}; + +class Aggregator { + IndexStore Store; + + BumpPtrAllocator Allocator; + + StringMap<FilePathIndex, BumpPtrAllocator &> FilePathIndices; + std::vector<StringRef> FilePaths; + StringMap<char, BumpPtrAllocator &> Triples; + + std::vector<std::unique_ptr<UnitInfo>> Units; + + StringMap<RecordIndex, BumpPtrAllocator &> RecordIndices; + std::vector<std::unique_ptr<RecordInfo>> Records; + + StringMap<SymbolIndex, BumpPtrAllocator &> SymbolIndices; + std::vector<SymbolInfo> Symbols; + +public: + explicit Aggregator(IndexStore store) + : Store(std::move(store)), FilePathIndices(Allocator), Triples(Allocator), + RecordIndices(Allocator), SymbolIndices(Allocator) {} + + bool process(); + void processUnit(StringRef name, IndexUnitReader &UnitReader); + void dumpJSON(raw_ostream &OS); + +private: + StringRef copyStr(StringRef str) { + if (str.empty()) + return StringRef(); + char *buf = Allocator.Allocate<char>(str.size()); + std::copy(str.begin(), str.end(), buf); + return StringRef(buf, str.size()); + } + + StringRef getTripleString(StringRef inputTriple) { + return Triples.insert(std::make_pair(inputTriple, 0)).first->first(); + } + + FilePathIndex getFilePathIndex(StringRef path, StringRef workingDir); + RecordIndex getRecordIndex(StringRef recordFile); + SymbolIndex getSymbolIndex(IndexRecordSymbol sym); + std::unique_ptr<RecordInfo> processRecord(StringRef recordFile); +}; + +} // anonymous namespace + +bool Aggregator::process() { + bool succ = + Store.foreachUnit(/*sorted=*/true, [&](StringRef unitName) -> bool { + std::string error; + auto unitReader = IndexUnitReader(Store, unitName, error); + if (!unitReader) { + errs() << "error opening unit file '" << unitName << "': " << error + << '\n'; + return false; + } + + processUnit(unitName, unitReader); + return true; + }); + + return !succ; +} + +void Aggregator::processUnit(StringRef name, IndexUnitReader &UnitReader) { + auto workDir = UnitReader.getWorkingDirectory(); + auto unit = llvm::make_unique<UnitInfo>(); + unit->Name = name; + unit->Triple = getTripleString(UnitReader.getTarget()); + unit->OutFile = getFilePathIndex(UnitReader.getOutputFile(), workDir); + + struct DepInfo { + UnitSourceInfo source; + std::string unitName; + }; + SmallVector<DepInfo, 32> Deps; + UnitReader.foreachDependency([&](IndexUnitDependency dep) -> bool { + Deps.resize(Deps.size() + 1); + auto &depInfo = Deps.back(); + switch (dep.getKind()) { + case IndexUnitDependency::DependencyKind::Unit: { + depInfo.unitName = dep.getName(); + StringRef filePath = dep.getFilePath(); + if (!filePath.empty()) + depInfo.source.FilePath = getFilePathIndex(filePath, workDir); + break; + } + case IndexUnitDependency::DependencyKind::Record: { + depInfo.source.FilePath = getFilePathIndex(dep.getFilePath(), workDir); + RecordIndex recIndex = getRecordIndex(dep.getName()); + depInfo.source.AssociatedRecords.push_back(recIndex); + break; + } + case IndexUnitDependency::DependencyKind::File: + depInfo.source.FilePath = getFilePathIndex(dep.getFilePath(), workDir); + } + return true; + }); + + unit->Sources.reserve(Deps.size()); + for (auto &dep : Deps) { + if (!dep.unitName.empty()) { + unit->UnitDepends.emplace_back(std::move(dep.unitName)); + } else { + unit->Sources.push_back(std::move(dep.source)); + } + } + + Units.push_back(std::move(unit)); +} + +FilePathIndex Aggregator::getFilePathIndex(StringRef path, + StringRef workingDir) { + StringRef absPath; + SmallString<128> absPathBuf; + if (sys::path::is_absolute(path) || workingDir.empty()) { + absPath = path; + } else { + absPathBuf = workingDir; + sys::path::append(absPathBuf, path); + absPath = absPathBuf.str(); + } + + auto pair = FilePathIndices.insert(std::make_pair(absPath, FilePaths.size())); + bool wasInserted = pair.second; + if (wasInserted) { + FilePaths.push_back(pair.first->first()); + } + return pair.first->second; +} + +RecordIndex Aggregator::getRecordIndex(StringRef recordFile) { + auto pair = RecordIndices.insert(std::make_pair(recordFile, Records.size())); + bool wasInserted = pair.second; + if (wasInserted) { + Records.push_back(processRecord(recordFile)); + } + return pair.first->second; +} + +std::unique_ptr<RecordInfo> Aggregator::processRecord(StringRef recordFile) { + std::string error; + auto recordReader = IndexRecordReader(Store, recordFile, error); + if (!recordReader) { + errs() << "failed reading record file: " << recordFile << '\n'; + ::exit(1); + } + auto record = llvm::make_unique<RecordInfo>(); + recordReader.foreachOccurrence([&](IndexRecordOccurrence idxOccur) -> bool { + SymbolIndex symIdx = getSymbolIndex(idxOccur.getSymbol()); + SymbolInfo &symInfo = Symbols[symIdx]; + symInfo.Roles |= idxOccur.getRoles(); + SymbolOccurrenceInfo occurInfo; + occurInfo.Symbol = symIdx; + idxOccur.foreachRelation([&](IndexSymbolRelation rel) -> bool { + SymbolIndex relsymIdx = getSymbolIndex(rel.getSymbol()); + SymbolInfo &relsymInfo = Symbols[relsymIdx]; + relsymInfo.RelatedRoles |= rel.getRoles(); + occurInfo.Relations.emplace_back(relsymIdx, rel.getRoles()); + return true; + }); + occurInfo.Roles = idxOccur.getRoles(); + std::tie(occurInfo.Line, occurInfo.Column) = idxOccur.getLineCol(); + record->Occurrences.push_back(std::move(occurInfo)); + return true; + }); + return record; +} + +SymbolIndex Aggregator::getSymbolIndex(IndexRecordSymbol sym) { + auto pair = + SymbolIndices.insert(std::make_pair(sym.getUSR(), Symbols.size())); + bool wasInserted = pair.second; + if (wasInserted) { + SymbolInfo symInfo; + symInfo.Kind = getSymbolKind(sym.getKind()); + symInfo.Lang = getSymbolLanguage(sym.getLanguage()); + symInfo.USR = pair.first->first(); + symInfo.Name = copyStr(sym.getName()); + symInfo.CodegenName = copyStr(sym.getCodegenName()); + Symbols.push_back(std::move(symInfo)); + } + return pair.first->second; +} + +void Aggregator::dumpJSON(raw_ostream &OS) { + OS << "{\n"; + OS.indent(2) << "\"files\": [\n"; + for (unsigned i = 0, e = FilePaths.size(); i != e; ++i) { + OS.indent(4) << '\"' << FilePaths[i] << '\"'; + if (i < e - 1) + OS << ','; + OS << '\n'; + } + OS.indent(2) << "],\n"; + + OS.indent(2) << "\"symbols\": [\n"; + for (unsigned i = 0, e = Symbols.size(); i != e; ++i) { + OS.indent(4) << "{\n"; + SymbolInfo &symInfo = Symbols[i]; + OS.indent(6) << "\"kind\": \"" << getSymbolKindString(symInfo.Kind) + << "\",\n"; + OS.indent(6) << "\"lang\": \"" << getSymbolLanguageString(symInfo.Lang) + << "\",\n"; + OS.indent(6) << "\"usr\": \"" << symInfo.USR << "\",\n"; + OS.indent(6) << "\"name\": \"" << symInfo.Name << "\",\n"; + if (!symInfo.CodegenName.empty()) + OS.indent(6) << "\"codegen\": \"" << symInfo.CodegenName << "\",\n"; + OS.indent(6) << "\"roles\": \""; + printSymbolRoles(symInfo.Roles, OS); + OS << '\"'; + if (symInfo.RelatedRoles != 0) { + OS << ",\n"; + OS.indent(6) << "\"rel-roles\": \""; + printSymbolRoles(symInfo.RelatedRoles, OS); + OS << '\"'; + } + OS << '\n'; + OS.indent(4) << "}"; + if (i < e - 1) + OS << ','; + OS << '\n'; + } + OS.indent(2) << "],\n"; + + OS.indent(2) << "\"records\": [\n"; + for (unsigned i = 0, e = Records.size(); i != e; ++i) { + OS.indent(4) << "{\n"; + RecordInfo &recInfo = *Records[i]; + OS.indent(6) << "\"occurrences\": [\n"; + for (unsigned oi = 0, oe = recInfo.Occurrences.size(); oi != oe; ++oi) { + OS.indent(8) << "{\n"; + SymbolOccurrenceInfo &occurInfo = recInfo.Occurrences[oi]; + OS.indent(10) << "\"symbol\": " << occurInfo.Symbol << ",\n"; + OS.indent(10) << "\"line\": " << occurInfo.Line << ",\n"; + OS.indent(10) << "\"col\": " << occurInfo.Column << ",\n"; + OS.indent(10) << "\"roles\": \""; + printSymbolRoles(occurInfo.Roles, OS); + OS << '\"'; + if (!occurInfo.Relations.empty()) { + OS << ",\n"; + OS.indent(10) << "\"relations\": [\n"; + for (unsigned ri = 0, re = occurInfo.Relations.size(); ri != re; ++ri) { + OS.indent(12) << "{\n"; + SymbolRelationInfo &relInfo = occurInfo.Relations[ri]; + OS.indent(14) << "\"symbol\": " << relInfo.RelatedSymbol << ",\n"; + OS.indent(14) << "\"rel-roles\": \""; + printSymbolRoles(relInfo.Roles, OS); + OS << "\"\n"; + OS.indent(12) << "}"; + if (ri < re - 1) + OS << ','; + OS << '\n'; + } + OS.indent(10) << "]\n"; + } + OS << '\n'; + OS.indent(8) << "}"; + if (oi < oe - 1) + OS << ','; + OS << '\n'; + } + OS.indent(6) << "]\n"; + OS.indent(4) << "}"; + if (i < e - 1) + OS << ','; + OS << '\n'; + } + OS.indent(2) << "],\n"; + + StringMap<size_t> UnitIndicesByName; + for (unsigned i = 0, e = Units.size(); i != e; ++i) { + UnitInfo &unit = *Units[i]; + UnitIndicesByName[unit.Name] = i; + } + + OS.indent(2) << "\"units\": [\n"; + for (unsigned i = 0, e = Units.size(); i != e; ++i) { + OS.indent(4) << "{\n"; + UnitInfo &unit = *Units[i]; + OS.indent(6) << "\"triple\": \"" << unit.Triple << "\",\n"; + OS.indent(6) << "\"out-file\": " << unit.OutFile << ",\n"; + if (!unit.UnitDepends.empty()) { + OS.indent(6) << "\"unit-dependencies\": ["; + for (unsigned ui = 0, ue = unit.UnitDepends.size(); ui != ue; ++ui) { + OS << UnitIndicesByName[unit.UnitDepends[ui]]; + if (ui < ue - 1) + OS << ", "; + } + OS << "],\n"; + } + OS.indent(6) << "\"sources\": [\n"; + for (unsigned si = 0, se = unit.Sources.size(); si != se; ++si) { + OS.indent(8) << "{\n"; + UnitSourceInfo &source = unit.Sources[si]; + OS.indent(10) << "\"file\": " << source.FilePath; + if (!source.AssociatedRecords.empty()) { + OS << ",\n"; + OS.indent(10) << "\"records\": ["; + for (unsigned ri = 0, re = source.AssociatedRecords.size(); ri != re; + ++ri) { + OS << source.AssociatedRecords[ri]; + if (ri < re - 1) + OS << ", "; + } + OS << ']'; + } + OS << '\n'; + OS.indent(8) << "}"; + if (si < se - 1) + OS << ','; + OS << '\n'; + } + OS.indent(6) << "]\n"; + OS.indent(4) << "}"; + if (i < e - 1) + OS << ','; + OS << '\n'; + } + OS.indent(2) << "]\n"; + OS << "}\n"; +} + +bool index::aggregateDataAsJSON(StringRef StorePath, raw_ostream &OS) { + std::string error; + auto dataStore = IndexStore(StorePath, error); + if (!dataStore) { + errs() << "error opening store path '" << StorePath << "': " << error + << '\n'; + return true; + } + + // Explicitely avoid doing any memory cleanup for aggregator since the process + // is going to exit when we are done. + Aggregator *aggregator = new Aggregator(std::move(dataStore)); + bool err = aggregator->process(); + if (err) + return true; + aggregator->dumpJSON(OS); + return false; +} + +#else + +bool index::aggregateDataAsJSON(StringRef StorePath, raw_ostream &OS) { + return true; +} +#endif diff --git a/tools/c-index-test/JSONAggregation.h b/tools/c-index-test/JSONAggregation.h new file mode 100644 index 0000000000..5224ce8e87 --- /dev/null +++ b/tools/c-index-test/JSONAggregation.h @@ -0,0 +1,24 @@ +//===--- JSONAggregation.h - Index data aggregation in JSON format --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_CINDEXTEST_JSONAGGREGATION_H +#define LLVM_CLANG_TOOLS_CINDEXTEST_JSONAGGREGATION_H + +#include "clang/Basic/LLVM.h" + +namespace clang { +namespace index { + +/// Returns true if an error occurred, false otherwise. +bool aggregateDataAsJSON(StringRef StorePath, raw_ostream &OS); + +} // end namespace index +} // end namespace clang + +#endif diff --git a/tools/c-index-test/core_main.cpp b/tools/c-index-test/core_main.cpp index 805f21cc77..d89b2116d5 100644 --- a/tools/c-index-test/core_main.cpp +++ b/tools/c-index-test/core_main.cpp @@ -7,23 +7,42 @@ // //===----------------------------------------------------------------------===// +#include "JSONAggregation.h" +#include "indexstore/IndexStoreCXX.h" #include "clang/CodeGen/ObjectFilePCHContainerOperations.h" +#include "clang/DirectoryWatcher/DirectoryWatcher.h" #include "clang/Frontend/ASTUnit.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/FrontendAction.h" #include "clang/Index/CodegenNameGenerator.h" #include "clang/Index/IndexDataConsumer.h" +#include "clang/Index/IndexDataStoreSymbolUtils.h" +#include "clang/Index/IndexRecordReader.h" +#include "clang/Index/IndexUnitReader.h" #include "clang/Index/USRGeneration.h" #include "clang/Index/UnitIndexDataConsumer.h" #include "clang/Index/UnitIndexingAction.h" #include "clang/Serialization/ASTReader.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Signals.h" #include "llvm/Support/raw_ostream.h" +#define HAVE_CORESERVICES 0 + +#if defined(__has_include) +#if __has_include(<CoreServices/CoreServices.h>) + +#include <CoreServices/CoreServices.h> +#undef HAVE_CORESERVICES +#define HAVE_CORESERVICES 1 + +#endif +#endif + using namespace clang; using namespace clang::index; using namespace llvm; @@ -36,6 +55,11 @@ enum class ActionType { None, PrintSourceSymbols, PrintSourceUnit, + PrintRecord, + PrintUnit, + PrintStoreFormatVersion, + AggregateAsJSON, + WatchDir, }; namespace options { @@ -44,12 +68,28 @@ static cl::OptionCategory IndexTestCoreCategory("index-test-core options"); static cl::opt<ActionType> Action( cl::desc("Action:"), cl::init(ActionType::None), - cl::values(clEnumValN(ActionType::PrintSourceSymbols, - "print-source-symbols", "Print symbols from source"), - clEnumValN(ActionType::PrintSourceUnit, "print-source-unit", - "Print unit info from source")), + cl::values( + clEnumValN(ActionType::PrintSourceSymbols, "print-source-symbols", + "Print symbols from source"), + clEnumValN(ActionType::PrintSourceUnit, "print-source-unit", + "Print unit info from source"), + clEnumValN(ActionType::PrintRecord, "print-record", + "Print record file info"), + clEnumValN(ActionType::PrintUnit, "print-unit", "Print unit file info"), + clEnumValN(ActionType::PrintStoreFormatVersion, + "print-store-format-version", "Print store format version"), + clEnumValN(ActionType::AggregateAsJSON, "aggregate-json", + "Aggregate index data in JSON format"), + clEnumValN(ActionType::WatchDir, "watch-dir", + "Watch directory for file events")), cl::cat(IndexTestCoreCategory)); +static cl::opt<std::string> OutputFile("o", cl::desc("output file"), + cl::cat(IndexTestCoreCategory)); + +static cl::list<std::string> InputFiles(cl::Positional, + cl::desc("<filename>...")); + static cl::extrahelp MoreHelp( "\nAdd \"-- <compiler arguments>\" at the end to setup the compiler " "invocation\n" @@ -69,6 +109,8 @@ static cl::opt<std::string> ModuleFormat("fmodule-format", cl::init("raw"), cl::desc("Container format for clang modules and PCH, 'raw' or 'obj'")); +static cl::opt<std::string> FilePathAndRange( + "filepath", cl::desc("File path that can optionally include a line range")); } } // anonymous namespace @@ -400,6 +442,324 @@ static bool printSourceUnit(ArrayRef<const char *> Args, bool IndexLocals, return !Unit; } +#if INDEXSTORE_HAS_BLOCKS + +//===----------------------------------------------------------------------===// +// Print Record +//===----------------------------------------------------------------------===// + +static void printSymbol(const IndexRecordDecl &Rec, raw_ostream &OS); +static void printSymbol(const IndexRecordOccurrence &Rec, raw_ostream &OS); + +static int printRecord(StringRef Filename, raw_ostream &OS) { + std::string Error; + auto Reader = IndexRecordReader::createWithFilePath(Filename, Error); + if (!Reader) { + errs() << Error << '\n'; + return true; + } + + Reader->foreachDecl(/*noCache=*/true, + [&](const IndexRecordDecl *Rec) -> bool { + printSymbol(*Rec, OS); + return true; + }); + OS << "------------\n"; + Reader->foreachOccurrence([&](const IndexRecordOccurrence &Rec) -> bool { + printSymbol(Rec, OS); + return true; + }); + + return false; +}; + +//===----------------------------------------------------------------------===// +// Print Store Records +//===----------------------------------------------------------------------===// + +static void printSymbol(indexstore::IndexRecordSymbol Sym, raw_ostream &OS); +static void printSymbol(indexstore::IndexRecordOccurrence Occur, + raw_ostream &OS); + +static bool printStoreRecord(indexstore::IndexStore &Store, StringRef RecName, + StringRef FilePath, raw_ostream &OS) { + std::string Error; + indexstore::IndexRecordReader Reader(Store, RecName, Error); + if (!Reader) { + errs() << "error loading record: " << Error << "\n"; + return true; + } + + StringRef Filename = sys::path::filename(FilePath); + OS << Filename << '\n'; + OS << "------------\n"; + Reader.foreachSymbol(/*noCache=*/true, + [&](indexstore::IndexRecordSymbol Sym) -> bool { + printSymbol(Sym, OS); + return true; + }); + OS << "------------\n"; + Reader.foreachOccurrence( + [&](indexstore::IndexRecordOccurrence Occur) -> bool { + printSymbol(Occur, OS); + return true; + }); + + return false; +} + +static int printStoreRecords(StringRef StorePath, raw_ostream &OS) { + std::string Error; + indexstore::IndexStore Store(StorePath, Error); + if (!Store) { + errs() << "error loading store: " << Error << "\n"; + return 1; + } + + bool Success = + Store.foreachUnit(/*sorted=*/true, [&](StringRef UnitName) -> bool { + indexstore::IndexUnitReader Reader(Store, UnitName, Error); + if (!Reader) { + errs() << "error loading unit: " << Error << "\n"; + return false; + } + return Reader.foreachDependency( + [&](indexstore::IndexUnitDependency Dep) -> bool { + if (Dep.getKind() == + indexstore::IndexUnitDependency::DependencyKind::Record) { + bool Err = printStoreRecord(Store, Dep.getName(), + Dep.getFilePath(), OS); + OS << '\n'; + return !Err; + } + return true; + }); + }); + + return !Success; +} + +static std::string findRecordNameForFile(indexstore::IndexStore &store, + StringRef filePath) { + std::string recName; + store.foreachUnit(/*sorted=*/false, [&](StringRef unitName) -> bool { + std::string error; + indexstore::IndexUnitReader Reader(store, unitName, error); + if (!Reader) { + errs() << "error loading unit: " << error << "\n"; + return false; + } + Reader.foreachDependency([&](indexstore::IndexUnitDependency Dep) -> bool { + if (Dep.getKind() == + indexstore::IndexUnitDependency::DependencyKind::Record) { + if (Dep.getFilePath() == filePath) { + recName = Dep.getName(); + return false; + } + return true; + } + return true; + }); + return true; + }); + return recName; +} + +static int printStoreFileRecord(StringRef storePath, StringRef filePath, + Optional<unsigned> lineStart, + unsigned lineCount, raw_ostream &OS) { + std::string error; + indexstore::IndexStore store(storePath, error); + if (!store) { + errs() << "error loading store: " << error << "\n"; + return 1; + } + + std::string recName = findRecordNameForFile(store, filePath); + if (recName.empty()) { + errs() << "could not find record for '" << filePath << "'\n"; + return 1; + } + + if (!lineStart.hasValue()) + return printStoreRecord(store, recName, filePath, OS); + + indexstore::IndexRecordReader Reader(store, recName, error); + if (!Reader) { + errs() << "error loading record: " << error << "\n"; + return 1; + } + + Reader.foreachOccurrenceInLineRange( + *lineStart, lineCount, + [&](indexstore::IndexRecordOccurrence Occur) -> bool { + printSymbol(Occur, OS); + return true; + }); + + return 0; +} + +//===----------------------------------------------------------------------===// +// Print Unit +//===----------------------------------------------------------------------===// + +static int printUnit(StringRef Filename, raw_ostream &OS) { + std::string Error; + auto Reader = IndexUnitReader::createWithFilePath(Filename, Error); + if (!Reader) { + errs() << Error << '\n'; + return true; + } + + OS << "provider: " << Reader->getProviderIdentifier() << '-' + << Reader->getProviderVersion() << '\n'; + OS << "is-system: " << Reader->isSystemUnit() << '\n'; + OS << "is-module: " << Reader->isModuleUnit() << '\n'; + OS << "module-name: " + << (Reader->getModuleName().empty() ? "<none>" : Reader->getModuleName()) + << '\n'; + OS << "has-main: " << Reader->hasMainFile() << '\n'; + OS << "main-path: " << Reader->getMainFilePath() << '\n'; + OS << "work-dir: " << Reader->getWorkingDirectory() << '\n'; + OS << "out-file: " << Reader->getOutputFile() << '\n'; + OS << "target: " << Reader->getTarget() << '\n'; + OS << "is-debug: " << Reader->isDebugCompilation() << '\n'; + OS << "DEPEND START\n"; + unsigned NumDepends = 0; + Reader->foreachDependency( + [&](const IndexUnitReader::DependencyInfo &Dep) -> bool { + switch (Dep.Kind) { + case IndexUnitReader::DependencyKind::Unit: + OS << "Unit | "; + break; + case IndexUnitReader::DependencyKind::Record: + OS << "Record | "; + break; + case IndexUnitReader::DependencyKind::File: + OS << "File | "; + break; + } + OS << (Dep.IsSystem ? "system" : "user"); + OS << " | "; + if (!Dep.ModuleName.empty()) + OS << Dep.ModuleName << " | "; + OS << Dep.FilePath << " | " << Dep.UnitOrRecordName << " | "; + OS << Dep.ModTime << " | " << Dep.FileSize << '\n'; + ++NumDepends; + return true; + }); + OS << "DEPEND END (" << NumDepends << ")\n"; + OS << "INCLUDE START\n"; + unsigned NumIncludes = 0; + Reader->foreachInclude([&](const IndexUnitReader::IncludeInfo &Inc) -> bool { + OS << Inc.SourcePath << ":" << Inc.SourceLine << " | "; + OS << Inc.TargetPath << '\n'; + ++NumIncludes; + return true; + }); + OS << "INCLUDE END (" << NumIncludes << ")\n"; + + return false; +}; + +//===----------------------------------------------------------------------===// +// Print Store Units +//===----------------------------------------------------------------------===// + +static bool printStoreUnit(indexstore::IndexStore &Store, StringRef UnitName, + raw_ostream &OS) { + std::string Error; + indexstore::IndexUnitReader Reader(Store, UnitName, Error); + if (!Reader) { + errs() << "error loading unit: " << Error << "\n"; + return true; + } + + OS << "provider: " << Reader.getProviderIdentifier() << '-' + << Reader.getProviderVersion() << '\n'; + OS << "is-system: " << Reader.isSystemUnit() << '\n'; + OS << "is-module: " << Reader.isModuleUnit() << '\n'; + OS << "module-name: " + << (Reader.getModuleName().empty() ? "<none>" : Reader.getModuleName()) + << '\n'; + OS << "has-main: " << Reader.hasMainFile() << '\n'; + OS << "main-path: " << Reader.getMainFilePath() << '\n'; + OS << "work-dir: " << Reader.getWorkingDirectory() << '\n'; + OS << "out-file: " << Reader.getOutputFile() << '\n'; + OS << "target: " << Reader.getTarget() << '\n'; + OS << "is-debug: " << Reader.isDebugCompilation() << '\n'; + OS << "DEPEND START\n"; + unsigned NumDepends = 0; + Reader.foreachDependency([&](indexstore::IndexUnitDependency Dep) -> bool { + switch (Dep.getKind()) { + case indexstore::IndexUnitDependency::DependencyKind::Unit: + OS << "Unit | "; + break; + case indexstore::IndexUnitDependency::DependencyKind::Record: + OS << "Record | "; + break; + case indexstore::IndexUnitDependency::DependencyKind::File: + OS << "File | "; + break; + } + OS << (Dep.isSystem() ? "system" : "user"); + OS << " | "; + if (!Dep.getModuleName().empty()) + OS << Dep.getModuleName() << " | "; + OS << Dep.getFilePath() << " | " << Dep.getName() << " | "; + OS << Dep.getModificationTime() << '\n'; + ++NumDepends; + return true; + }); + OS << "DEPEND END (" << NumDepends << ")\n"; + OS << "INCLUDE START\n"; + unsigned NumIncludes = 0; + Reader.foreachInclude([&](indexstore::IndexUnitInclude Inc) -> bool { + OS << Inc.getSourcePath() << ":" << Inc.getSourceLine() << " | "; + OS << Inc.getTargetPath() << '\n'; + ++NumIncludes; + return true; + }); + OS << "INCLUDE END (" << NumIncludes << ")\n"; + + return false; +} + +static int printStoreUnits(StringRef StorePath, raw_ostream &OS) { + std::string Error; + indexstore::IndexStore Store(StorePath, Error); + if (!Store) { + errs() << "error loading store: " << Error << "\n"; + return 1; + } + + bool Success = + Store.foreachUnit(/*sorted=*/true, [&](StringRef UnitName) -> bool { + OS << UnitName << '\n'; + OS << "--------\n"; + bool err = printStoreUnit(Store, UnitName, OS); + OS << '\n'; + return !err; + }); + + return !Success; +} + +#else + +static int printUnit(StringRef Filename, raw_ostream &OS) { return 1; } + +static int printStoreUnits(StringRef StorePath, raw_ostream &OS) { return 1; } + +static int printStoreFileRecord(StringRef storePath, StringRef filePath, + Optional<unsigned> lineStart, + unsigned lineCount, raw_ostream &OS) { + return 1; +} + +#endif + //===----------------------------------------------------------------------===// // Helper Utils //===----------------------------------------------------------------------===// @@ -431,10 +791,210 @@ static void printSymbolNameAndUSR(const Decl *D, ASTContext &Ctx, } } +#if INDEXSTORE_HAS_BLOCKS + +static void printSymbol(const IndexRecordDecl &Rec, raw_ostream &OS) { + printSymbolInfo(Rec.SymInfo, OS); + OS << " | "; + + if (Rec.Name.empty()) + OS << "<no-name>"; + else + OS << Rec.Name; + OS << " | "; + + if (Rec.USR.empty()) + OS << "<no-usr>"; + else + OS << Rec.USR; + OS << " | "; + + if (Rec.CodeGenName.empty()) + OS << "<no-cgname>"; + else + OS << Rec.CodeGenName; + OS << " | "; + + printSymbolRoles(Rec.Roles, OS); + OS << " - "; + printSymbolRoles(Rec.RelatedRoles, OS); + OS << '\n'; +} + +static void printSymbol(const IndexRecordOccurrence &Rec, raw_ostream &OS) { + OS << Rec.Line << ':' << Rec.Column << " | "; + printSymbolInfo(Rec.Dcl->SymInfo, OS); + OS << " | "; + + if (Rec.Dcl->USR.empty()) + OS << "<no-usr>"; + else + OS << Rec.Dcl->USR; + OS << " | "; + + printSymbolRoles(Rec.Roles, OS); + OS << " | "; + OS << "rel: " << Rec.Relations.size() << '\n'; + for (auto &Rel : Rec.Relations) { + OS << '\t'; + printSymbolRoles(Rel.Roles, OS); + OS << " | "; + if (Rel.Dcl->USR.empty()) + OS << "<no-usr>"; + else + OS << Rel.Dcl->USR; + OS << '\n'; + } +} + +static void printSymbol(indexstore::IndexRecordSymbol Sym, raw_ostream &OS) { + SymbolInfo SymInfo{getSymbolKind(Sym.getKind()), + getSymbolSubKind(Sym.getSubKind()), + getSymbolLanguage(Sym.getLanguage()), + SymbolPropertySet(Sym.getProperties())}; + + printSymbolInfo(SymInfo, OS); + OS << " | "; + + if (Sym.getName().empty()) + OS << "<no-name>"; + else + OS << Sym.getName(); + OS << " | "; + + if (Sym.getUSR().empty()) + OS << "<no-usr>"; + else + OS << Sym.getUSR(); + OS << " | "; + + if (Sym.getCodegenName().empty()) + OS << "<no-cgname>"; + else + OS << Sym.getCodegenName(); + OS << " | "; + + printSymbolRoles(Sym.getRoles(), OS); + OS << " - "; + printSymbolRoles(Sym.getRelatedRoles(), OS); + OS << '\n'; +} + +static void printSymbol(indexstore::IndexRecordOccurrence Occur, + raw_ostream &OS) { + OS << Occur.getLineCol().first << ':' << Occur.getLineCol().second << " | "; + auto Sym = Occur.getSymbol(); + SymbolInfo SymInfo{getSymbolKind(Sym.getKind()), + getSymbolSubKind(Sym.getSubKind()), + getSymbolLanguage(Sym.getLanguage()), + SymbolPropertySet(Sym.getProperties())}; + + printSymbolInfo(SymInfo, OS); + OS << " | "; + + if (Sym.getUSR().empty()) + OS << "<no-usr>"; + else + OS << Sym.getUSR(); + OS << " | "; + + unsigned NumRelations = 0; + Occur.foreachRelation([&](indexstore::IndexSymbolRelation) { + ++NumRelations; + return true; + }); + + printSymbolRoles(Occur.getRoles(), OS); + OS << " | "; + OS << "rel: " << NumRelations << '\n'; + Occur.foreachRelation([&](indexstore::IndexSymbolRelation Rel) { + OS << '\t'; + printSymbolRoles(Rel.getRoles(), OS); + OS << " | "; + auto Sym = Rel.getSymbol(); + if (Sym.getUSR().empty()) + OS << "<no-usr>"; + else + OS << Sym.getUSR(); + OS << '\n'; + return true; + }); +} + +#else + +static int printRecord(StringRef Filename, raw_ostream &OS) { return 1; } +static int printStoreRecords(StringRef StorePath, raw_ostream &OS) { return 1; } + +#endif + +static int watchDirectory(StringRef dirPath) { + raw_ostream &OS = outs(); + auto receiver = [&](ArrayRef<DirectoryWatcher::Event> Events, + bool isInitial) { + for (auto evt : Events) { + switch (evt.Kind) { + case DirectoryWatcher::EventKind::Added: + OS << "added: "; + break; + case DirectoryWatcher::EventKind::Modified: + OS << "modified: "; + break; + case DirectoryWatcher::EventKind::Removed: + OS << "removed: "; + break; + case DirectoryWatcher::EventKind::DirectoryDeleted: + OS << "dir deleted: "; + break; + } + OS << evt.Filename << '\n'; + } + }; + std::string Error; + auto watcher = DirectoryWatcher::create(dirPath, receiver, + /*waitInitialSync=*/true, Error); + if (!watcher) { + errs() << "failed creating directory watcher: " << Error << '\n'; + return 1; + } +#if HAVE_CORESERVICES + dispatch_main(); +#else + return 1; +#endif +} + //===----------------------------------------------------------------------===// // Command line processing. //===----------------------------------------------------------------------===// +bool deconstructPathAndRange(StringRef input, std::string &filepath, + Optional<unsigned> &lineStart, + unsigned &lineCount) { + StringRef path, range; + std::tie(path, range) = input.split(':'); + StringRef start, end; + std::tie(start, end) = range.split(':'); + filepath = path; + lineCount = 0; + if (start.empty()) + return false; + unsigned num; + if (start.getAsInteger(10, num)) { + errs() << "couldn't convert to integer: " << start << '\n'; + return true; + } + lineStart = num; + if (end.empty()) + return false; + if (end.getAsInteger(10, num)) { + errs() << "couldn't convert to integer: " << end << '\n'; + return true; + } + lineCount = num - lineStart.getValue(); + return false; +} + int indextest_core_main(int argc, const char **argv) { sys::PrintStackTraceOnErrorSignal(argv[0]); PrettyStackTraceProgram X(argc, argv); @@ -480,5 +1040,76 @@ int indextest_core_main(int argc, const char **argv) { /*IndexModDepedencies=*/true); } + if (options::Action == ActionType::PrintRecord) { + if (!options::FilePathAndRange.empty()) { + std::string filepath; + Optional<unsigned> lineStart; + unsigned lineCount; + if (deconstructPathAndRange(options::FilePathAndRange, filepath, + lineStart, lineCount)) + return 1; + + if (options::InputFiles.empty()) { + errs() << "error: missing index store path\n"; + return 1; + } + return printStoreFileRecord(options::InputFiles[0], filepath, lineStart, + lineCount, outs()); + } + + if (options::InputFiles.empty()) { + errs() << "error: missing input file or directory\n"; + return 1; + } + + if (sys::fs::is_directory(options::InputFiles[0])) + return printStoreRecords(options::InputFiles[0], outs()); + else + return printRecord(options::InputFiles[0], outs()); + } + + if (options::Action == ActionType::PrintUnit) { + if (options::InputFiles.empty()) { + errs() << "error: missing input file or directory\n"; + return 1; + } + + if (sys::fs::is_directory(options::InputFiles[0])) + return printStoreUnits(options::InputFiles[0], outs()); + else + return printUnit(options::InputFiles[0], outs()); + } + +#if INDEXSTORE_HAS_BLOCKS + if (options::Action == ActionType::PrintStoreFormatVersion) { + outs() << indexstore::IndexStore::formatVersion() << '\n'; + } +#endif + + if (options::Action == ActionType::AggregateAsJSON) { + if (options::InputFiles.empty()) { + errs() << "error: missing input data store directory\n"; + return 1; + } + StringRef storePath = options::InputFiles[0]; + if (options::OutputFile.empty()) + return aggregateDataAsJSON(storePath, outs()); + std::error_code EC; + raw_fd_ostream OS(options::OutputFile, EC, llvm::sys::fs::F_None); + if (EC) { + errs() << "failed to open output file: " << EC.message() << '\n'; + return 1; + } + return aggregateDataAsJSON(storePath, OS); + } + + if (options::Action == ActionType::WatchDir) { + if (options::InputFiles.empty()) { + errs() << "error: missing directory path\n"; + return 1; + } + return watchDirectory(options::InputFiles[0]); + } + return 0; } |