├── .clang-format ├── .clang-format-include ├── .clang-tidy ├── .clang-uml ├── .github └── workflows │ ├── build.yml │ └── macos.yml ├── .gitignore ├── AUTHORS.md ├── CHANGELOG.md ├── CMakeLists.txt ├── CODE_OF_CONDUCT.md ├── INSTALLATION.md ├── LICENSE.md ├── Makefile ├── README.md ├── build.ps1 ├── cmake ├── FindLibClang.cmake └── GitVersion.cmake ├── packaging ├── Makefile ├── README.md ├── conda │ ├── build.sh │ └── meta.yaml.in ├── debian │ ├── changelog │ ├── changelog.bookworm │ ├── compat │ ├── control.bookworm │ ├── control.focal │ ├── control.jammy │ ├── control.noble │ ├── control.oracular │ ├── control.plucky │ ├── copyright │ ├── rules │ ├── source │ │ └── format │ └── watch ├── fedora │ └── clang-include-graph.spec └── make_installer.ps1 ├── src ├── compilation_database.cc ├── compilation_database.h ├── config.cc ├── config.h ├── include_graph.cc ├── include_graph.h ├── include_graph_cycles_printer.cc ├── include_graph_cycles_printer.h ├── include_graph_dependants_printer.cc ├── include_graph_dependants_printer.h ├── include_graph_graphml_printer.cc ├── include_graph_graphml_printer.h ├── include_graph_graphviz_printer.cc ├── include_graph_graphviz_printer.h ├── include_graph_json_printer.cc ├── include_graph_json_printer.h ├── include_graph_parser.cc ├── include_graph_parser.h ├── include_graph_plantuml_printer.cc ├── include_graph_plantuml_printer.h ├── include_graph_printer.cc ├── include_graph_printer.h ├── include_graph_topological_sort_printer.cc ├── include_graph_topological_sort_printer.h ├── include_graph_tree_printer.cc ├── include_graph_tree_printer.h ├── main.cc ├── path_printer.cc ├── path_printer.h ├── util.cc └── util.h ├── tests ├── CMakeLists.txt ├── test_cycles_printer.cc ├── test_dependants_printer.cc ├── test_graphml_printer.cc ├── test_graphviz_printer.cc ├── test_json_printer.cc ├── test_plantuml_printer.cc ├── test_topological_sort_printer.cc ├── test_tree_printer.cc ├── test_util.cc └── test_utils.h ├── thirdparty └── glob │ ├── LICENSE │ └── glob.hpp └── util ├── check_formatting.sh ├── msbuild_compile_commands_logger ├── CompileCommandsLogger.cs └── CompileCommandsLogger.csproj └── test_llvm_versions.sh /.clang-format: -------------------------------------------------------------------------------- 1 | AlignTrailingComments: true 2 | BasedOnStyle: WebKit 3 | BreakConstructorInitializersBeforeComma: true 4 | ColumnLimit: 80 5 | Cpp11BracedListStyle: true 6 | NamespaceIndentation: None 7 | PointerBindsToType: false 8 | Standard: Cpp11 9 | BreakBeforeBinaryOperators: false 10 | BreakBeforeBraces: Stroustrup 11 | IndentCaseLabels: false 12 | SpaceBeforeCpp11BracedList: false 13 | -------------------------------------------------------------------------------- /.clang-format-include: -------------------------------------------------------------------------------- 1 | # 2 | # List of glob patterns for files to check 3 | # 4 | # 5 | + src/**/*.cc 6 | + src/**/*.h 7 | + tests/**/*.cc 8 | + tests/**/*.h -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | Checks: >- 2 | *, 3 | -abseil-*, 4 | -altera-*, 5 | -android-*, 6 | -bugprone-easily-swappable-parameters, 7 | -clang-analyzer-cplusplus.NewDelete, 8 | -clang-analyzer-optin.core.EnumCastOutOfRange, 9 | -concurrency-mt-unsafe, 10 | -cppcoreguidelines-pro-bounds-array-to-pointer-decay, 11 | -cppcoreguidelines-pro-bounds-pointer-arithmetic, 12 | -cppcoreguidelines-avoid-const-or-ref-data-members, 13 | -google-runtime-int, 14 | -google-readability-braces-around-statements, 15 | -google-readability-todo, 16 | -hicpp-no-array-decay, 17 | -hicpp-braces-around-statements, 18 | -fuchsia-*, 19 | -llvm-*, 20 | -llvmlibc-*, 21 | -misc-no-recursion*, 22 | -misc-include-cleaner, 23 | -modernize-use-trailing-return-type, 24 | -mpi-*, 25 | -objc-*, 26 | -openmp-*, 27 | -readability-identifier-length, 28 | -readability-function-cognitive-complexity, 29 | -readability-braces-around-statements, 30 | -readability-suspicious-call-argument, 31 | -zircon-* 32 | WarningsAsErrors: '*' 33 | HeaderFilterRegex: 'src' 34 | CheckOptions: 35 | - key: readability-identifier-naming.ClassMemberSuffix 36 | value: '_' -------------------------------------------------------------------------------- /.clang-uml: -------------------------------------------------------------------------------- 1 | compilation_database_dir: debug 2 | output_directory: docs/diagrams 3 | diagrams: 4 | printer_class_diagram: 5 | type: class 6 | glob: 7 | - src/*.cc 8 | using_namespace: 9 | - clang_include_graph 10 | include: 11 | namespaces: 12 | - clang_include_graph 13 | paths: 14 | - src 15 | context: 16 | - clang_include_graph::include_graph_printer 17 | exclude: 18 | relationships: 19 | - dependency 20 | method_types: 21 | - constructor 22 | - operator 23 | plantuml: 24 | before: 25 | - left to right direction -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build-ubuntu: 7 | runs-on: ubuntu-24.04 8 | steps: 9 | - name: Checkout repository 10 | uses: actions/checkout@v4 11 | with: 12 | submodules: recursive 13 | - name: Update package database 14 | run: sudo apt -y update 15 | - name: ccache 16 | uses: hendrikmuhs/ccache-action@v1.2 17 | with: 18 | max-size: "2000M" 19 | - name: Install deps 20 | run: sudo apt -y install git make gcc g++ cmake clang-18 libclang-18-dev libclang-cpp18-dev libboost-graph1.83-dev libboost-filesystem1.83-dev libboost-test1.83-dev libboost-program-options1.83-dev libboost-json1.83-dev libboost-log1.83-dev 21 | - name: Check code formatting 22 | run: | 23 | make check-formatting 24 | - name: Build and unit test 25 | run: | 26 | NUMPROC=2 LLVM_CONFIG_PATH=/usr/bin/llvm-config-18 make test -------------------------------------------------------------------------------- /.github/workflows/macos.yml: -------------------------------------------------------------------------------- 1 | name: macOS 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build-macos: 7 | name: Build and test on macos 8 | runs-on: macos-15 9 | 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v4 13 | 14 | - name: ccache 15 | uses: hendrikmuhs/ccache-action@v1.2 16 | 17 | - name: Install dependencies using homebrew 18 | run: brew update && brew install llvm@19 ninja boost cmake ccache 19 | 20 | - name: Build and test 21 | run: CC=/opt/homebrew/opt/llvm@19/bin/clang CXX=/opt/homebrew/opt/llvm@19/bin/clang++ CMAKE_PREFIX=/opt/homebrew/opt/llvm@19/lib/cmake/llvm LLVM_CONFIG_PATH=/opt/homebrew/opt/llvm@19/bin/llvm-config CMAKE_EXE_LINKER_FLAGS="-L/opt/homebrew/opt/llvm@19/lib/c++ -Wl,-rpath,/opt/homebrew/opt/llvm@19/lib/c++" make test 22 | 23 | - name: Print build version 24 | run: debug/clang-include-graph --version 25 | 26 | - name: Print help 27 | run: debug/clang-include-graph --help 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gdb_history 2 | CMakeLists.txt.user 3 | CMakeCache.txt 4 | CMakeFiles 5 | CMakeScripts 6 | Testing 7 | cmake_install.cmake 8 | install_manifest.txt 9 | compile_commands.json 10 | CTestTestfile.cmake 11 | Session.vim 12 | _deps 13 | /build/ 14 | lib/ 15 | bin/ 16 | *.swp 17 | /puml/ 18 | /debug/ 19 | /release/ 20 | /.cache 21 | docs/diagrams 22 | 23 | coverage*.info 24 | 25 | packaging/_BUILD 26 | packaging/conda/meta.yaml 27 | 28 | compile_flags.txt 29 | 30 | util/msbuild_compile_commands_logger/CompileCommandsLogger.dll 31 | util/msbuild_compile_commands_logger/obj/ 32 | 33 | # CLion 34 | 35 | .idea/ 36 | cmake-build- 37 | cmake-build-* 38 | -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | # Authors 2 | 3 | Bartek Kryza -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ### 0.2.0 4 | * Added GraphML serialization (#11) 5 | * Added JSON serialization (#4) 6 | * Added option to write output to file (#14) 7 | * Added reverse include graph print option (#9) 8 | * Add thread pool to parse translation units in parallel (#13) 9 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(clang-include-graph) 3 | 4 | set(CMAKE_CXX_STANDARD 14) 5 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 6 | set(CMAKE_CXX_EXTENSIONS OFF) 7 | 8 | set(CMAKE_DISABLE_IN_SOURCE_BUILD ON) 9 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 10 | 11 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/") 12 | 13 | set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}) 14 | 15 | option(LLVM_CONFIG_PATH "Path to custom llvm-config executable") 16 | 17 | option(BOOST_STATIC "Enable linking static Boost libraries" OFF) 18 | 19 | option(WITH_JSON "Add JSON output support" ON) 20 | 21 | # 22 | # Setup version string 23 | # 24 | include(GitVersion) 25 | setup_git_version() 26 | message(STATUS "clang-include-graph version: " 27 | "${GIT_VERSION_MAJOR}.${GIT_VERSION_MINOR}.${GIT_VERSION_PATCH}") 28 | if("${GIT_VERSION}" STREQUAL "") 29 | set(GIT_VERSION "${GIT_VERSION_MAJOR}.${GIT_VERSION_MINOR}.${GIT_VERSION_PATCH}") 30 | endif() 31 | message(STATUS "Effective clang-include-version is: ${GIT_VERSION}") 32 | 33 | if (LLVM_CONFIG_PATH) 34 | message(STATUS "Using llvm-config from ${LLVM_CONFIG_PATH}") 35 | set(LIBCLANG_LLVM_CONFIG_EXECUTABLE ${LLVM_CONFIG_PATH}) 36 | set(LLVM_CONFIG_BINARY ${LLVM_CONFIG_PATH}) 37 | endif (LLVM_CONFIG_PATH) 38 | 39 | message(STATUS "Checking for libclang...") 40 | set(LLVM_PREFERRED_VERSION 16.0.0) 41 | 42 | find_package(LibClang REQUIRED) 43 | message(STATUS "Linking libClang from ${LIBCLANG_LIBRARIES}") 44 | 45 | set(Boost_USE_STATIC_LIBS ${BOOST_STATIC}) 46 | set(Boost_USE_MULTITHREADED ON) 47 | set(Boost_USE_DEBUG_RUNTIME OFF) 48 | set(Boost_USE_STATIC_RUNTIME OFF) 49 | set(BOOST_LOG_NO_LIB ON) 50 | 51 | set(BOOST_COMPONENTS graph program_options filesystem log_setup log unit_test_framework) 52 | if(WITH_JSON) 53 | list(APPEND BOOST_COMPONENTS json) 54 | add_compile_definitions(WITH_JSON_OUTPUT) 55 | endif(WITH_JSON) 56 | find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS}) 57 | include_directories(${Boost_INCLUDE_DIRS}) 58 | 59 | set(THIRDPARTY_HEADERS_DIR ${PROJECT_SOURCE_DIR}/thirdparty/) 60 | include_directories(${THIRDPARTY_HEADERS_DIR}) 61 | 62 | if(MSVC) 63 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -DCINDEX_NO_EXPORTS -DLIBCLANG_VERSION_STRING=\\\"${LIBCLANG_VERSION_STRING}\\\" -DGIT_VERSION=\\\"${GIT_VERSION}\\\" ${LIBCLANG_CXXFLAGS}") 64 | else(MSVC) 65 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-deprecated-copy-with-user-provided-copy -DLIBCLANG_VERSION_STRING=\\\"${LIBCLANG_VERSION_STRING}\\\" -DGIT_VERSION=\\\"${GIT_VERSION}\\\" -DBOOST_LOG_DYN_LINK ${LIBCLANG_CXXFLAGS}") 66 | endif(MSVC) 67 | 68 | file(GLOB_RECURSE SOURCES src/*.cc src/*.h) 69 | if(NOT WITH_JSON) 70 | list(REMOVE_ITEM SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/include_graph_json_printer.cc) 71 | list(REMOVE_ITEM SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/include_graph_json_printer.h) 72 | endif(NOT WITH_JSON) 73 | set(MAIN_SOURCE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/src/main.cc) 74 | list(REMOVE_ITEM SOURCES ${MAIN_SOURCE_FILE}) 75 | 76 | add_library(lib-clang-include-graph OBJECT ${SOURCES}) 77 | 78 | add_executable(clang-include-graph $ src/main.cc) 79 | 80 | target_link_libraries(clang-include-graph ${LIBCLANG_LIBRARIES} 81 | Boost::filesystem Boost::graph Boost::program_options Boost::log) 82 | if(WITH_JSON) 83 | target_link_libraries(clang-include-graph Boost::json) 84 | endif(WITH_JSON) 85 | 86 | find_program( 87 | CLANG_TIDY 88 | NAMES "run-clang-tidy-18" 89 | DOC "Path to run-clang-tidy script") 90 | 91 | if(CLANG_TIDY) 92 | include(ProcessorCount) 93 | ProcessorCount(CLANG_TIDY_PARALLEL_JOBS) 94 | message(STATUS 95 | "run-clang-tidy script found: ${CLANG_TIDY} - adding target clang-tidy") 96 | set(CLANG_TIDY_SOURCE_FILTER "src/*.cc") 97 | file(GLOB_RECURSE CLANG_TIDY_SOURCES 98 | "${CMAKE_CURRENT_SOURCE_DIR}/${CLANG_TIDY_SOURCE_FILTER}") 99 | add_custom_target(clang-tidy COMMAND ${CLANG_TIDY} 100 | -export-fixes clang-tidy-suggested-fixes.yaml 101 | -j ${CLANG_TIDY_PARALLEL_JOBS} 102 | ${CLANG_TIDY_SOURCES}) 103 | else(CLANG_TIDY) 104 | message(STATUS "run-clang-tidy script not found - target clang-tidy not available") 105 | endif(CLANG_TIDY) 106 | 107 | include(GNUInstallDirs) 108 | 109 | install(TARGETS clang-include-graph DESTINATION ${CMAKE_INSTALL_BINDIR}) 110 | install(FILES LICENSE.md DESTINATION ${CMAKE_INSTALL_DOCDIR}) 111 | install(FILES README.md DESTINATION ${CMAKE_INSTALL_DOCDIR}) 112 | install(FILES AUTHORS.md DESTINATION ${CMAKE_INSTALL_DOCDIR}) 113 | 114 | # 115 | # Setup installer 116 | # 117 | set(CPACK_PACKAGE_NAME "clang-include-graph") 118 | set(CPACK_PACKAGE_VENDOR "Bartek Kryza ") 119 | set(CPACK_PACKAGE_DESCRIPTION_SUMMARY 120 | "clang-include-graph - Simple tool for analyzing C++ project include graph") 121 | set(CPACK_PACKAGE_VERSION "${GIT_VERSION}") 122 | set(CPACK_PACKAGE_VERSION_MAJOR "${GIT_VERSION_MAJOR}") 123 | set(CPACK_PACKAGE_VERSION_MINOR "${GIT_VERSION_MINOR}") 124 | set(CPACK_PACKAGE_VERSION_PATCH "${GIT_VERSION_PATCH}") 125 | set(CPACK_PACKAGE_INSTALL_DIRECTORY "clang-include-graph") 126 | set(CPACK_RESOURCE_FILE_LICENSE ${PROJECT_SOURCE_DIR}/LICENSE.md) 127 | 128 | if(MSVC) 129 | set(CPACK_GENERATOR "NSIS") 130 | set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON) 131 | set(CPACK_NSIS_DISPLAY_NAME "clang-include-graph") 132 | set(CPACK_NSIS_HELP_LINK "https://github.com/bkryza/clang-include-graph") 133 | set(CPACK_NSIS_URL_INFO_ABOUT "https://github.com/bkryza/clang-include-graph") 134 | set(CPACK_NSIS_CONTACT "Bartek Kryza ") 135 | set(CPACK_NSIS_MODIFY_PATH ON) 136 | set(CPACK_SOURCE_GENERATOR "ZIP") 137 | endif(MSVC) 138 | 139 | include(CPack) 140 | 141 | # Enable testing via CTest 142 | enable_testing() 143 | add_subdirectory(tests) 144 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | Just be nice 2 | -------------------------------------------------------------------------------- /INSTALLATION.md: -------------------------------------------------------------------------------- 1 | ## Installation 2 | ### Distribution packages 3 | #### Ubuntu 4 | ```bash 5 | sudo add-apt-repository ppa:bkryza/clang-include-graph 6 | sudo apt update 7 | sudo apt install clang-include-graph 8 | ``` 9 | 10 | #### Debian 11 | 12 | ```bash 13 | # Bookworm 14 | wget https://github.com/bkryza/clang-include-graph/releases/download/0.2.0/clang-include-graph_0.2.0-1_amd64.deb 15 | sudo apt install ./clang-include-graph_0.2.0-1_amd64.deb 16 | ``` 17 | 18 | #### Fedora 19 | 20 | ```bash 21 | # Fedora 39 22 | wget https://github.com/bkryza/clang-include-graph/releases/download/0.2.0/clang-include-graph-0.2.0-1.fc39.x86_64.rpm 23 | sudo dnf install ./clang-include-graph-0.2.0-1.fc39.x86_64.rpm 24 | 25 | # Fedora 40 26 | wget https://github.com/bkryza/clang-include-graph/releases/download/0.2.0/clang-include-graph-0.2.0-1.fc40.x86_64.rpm 27 | sudo dnf install ./clang-include-graph-0.2.0-1.fc40.x86_64.rpm 28 | 29 | # Fedora 41 30 | wget https://github.com/bkryza/clang-include-graph/releases/download/0.2.0/clang-include-graph-0.2.0-1.fc41.x86_64.rpm 31 | sudo dnf install ./clang-include-graph-0.2.0-1.fc41.x86_64.rpm 32 | ``` 33 | 34 | #### Conda 35 | 36 | ```bash 37 | conda config --add channels conda-forge 38 | conda config --set channel_priority strict 39 | conda install -c bkryza/label/clang-include-graph clang-include-graph 40 | ``` 41 | 42 | #### macOS 43 | 44 | Using Homebrew: 45 | 46 | ```console 47 | brew install clang-include-graph 48 | ``` 49 | 50 | #### Windows 51 | 52 | Download and run the latest Windows installer from 53 | [Releases page](https://github.com/bkryza/clang-include-graph/releases). 54 | 55 | 56 | ### Building from source 57 | 58 | #### Linux 59 | First make sure that you have the following dependencies installed: 60 | 61 | ```bash 62 | # Ubuntu (clang version will vary depending on Ubuntu version) 63 | apt install git make gcc g++ cmake clang-19 libclang-19-dev libclang-cpp19-dev libboost-log1.83-dev libboost-graph1.83-dev libboost-filesystem1.83-dev libboost-test1.83-dev libboost-json1.83-dev libboost-program-options1.83-dev 64 | ``` 65 | 66 | Then proceed with building the sources: 67 | 68 | ```bash 69 | git clone https://github.com/bkryza/clang-include-graph 70 | cd clang-include-graph 71 | # Please note that top level Makefile is just a convenience wrapper for CMake 72 | make release 73 | release/clang-include-graph --help 74 | 75 | # To build using a specific installed version of LLVM use: 76 | LLVM_CONFIG_PATH=/usr/bin/llvm-config-18 make release 77 | 78 | export PATH=$PATH:$PWD/release 79 | ``` 80 | 81 | #### Windows 82 | ##### Visual Studio native build 83 | 84 | These steps present how to build and use `clang-include-graph` natively using Microsoft Visual Studio only. 85 | 86 | First, install the following dependencies manually: 87 | 88 | * [Python 3](https://www.python.org/downloads/windows/) 89 | * [Git](https://git-scm.com/download/win) 90 | * [CMake](https://cmake.org/download/) 91 | * [Visual Studio](https://visualstudio.microsoft.com/vs/community/) 92 | 93 | > All the following steps should be invoked in `Developer PowerShell for VS`. 94 | 95 | Create installation directory for `clang-uml` and its dependencies: 96 | 97 | ```bash 98 | # This is where clang-uml binary and its dependencies will be installed after build 99 | # If you change this path, adapt all consecutive steps 100 | mkdir C:\clang-include-graph-llvm20 101 | # This directory will be removed after build 102 | mkdir C:\clang-include-graph-llvm20-tmp 103 | cd C:\clang-include-graph-llvm20-tmp 104 | ``` 105 | 106 | Build and install `LLVM`: 107 | 108 | ```bash 109 | pip install psutil 110 | # Update the LLVM branch if necessary 111 | git clone --branch llvmorg-20.1.5 --depth 1 https://github.com/llvm/llvm-project.git llvm 112 | cmake -S .\llvm\llvm -B llvm-build -DLLVM_ENABLE_PROJECTS=clang -DCMAKE_INSTALL_PREFIX="C:\clang-include-graph-llvm20" -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_PIC=OFF -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Release -DLLVM_TARGETS_TO_BUILD=X86 -DCLANG_LINK_CLANG_DYLIB=OFF -Thost=x64 113 | cd llvm-build 114 | # Make sure you are running this in a developer console 115 | msbuild .\INSTALL.vcxproj -maxcpucount /p:Configuration=Release 116 | ``` 117 | 118 | Build and install `Boost`: 119 | 120 | ```bash 121 | # Download and unpack Boost, then 122 | cd boost_1_88_0 123 | cd .\tools\build\ 124 | .\bootstrap.bat 125 | cd .. 126 | cd .. 127 | .\bootstrap.bat msvc 128 | .\b2.exe toolset=msvc variant=release threading=multi link=static address-model=64 --with-atomic --with-container --with-filesystem --with-thread --with-graph --with-log --with-test --with-json --with-program_options --with-test --prefix="C:\clang-include-graph-llvm20-tmp" -j6 install 129 | ``` 130 | 131 | Build and install `clang-include-graph`: 132 | 133 | ```bash 134 | git clone https://github.com/bkryza/clang-include-graph 135 | cd clang-include-graph 136 | # Edit build.ps1 and adjust $Prefix to C:\clang-include-graph-llvm20-tmp 137 | .\build.ps1 138 | ``` 139 | 140 | Check if `clang-include-graph` works: 141 | 142 | ```bash 143 | .\Release\Release\clang-include-graph --version 144 | ``` 145 | 146 | It should produce something like: 147 | ```bash 148 | clang-include-graph 0.2.0 149 | Copyright (C) 2022-present Bartek Kryza 150 | Built with libclang: 20.1.5 151 | ``` 152 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Apache License 2 | ============== 3 | 4 | _Version 2.0, January 2004_ 5 | _<>_ 6 | 7 | ### Terms and Conditions for use, reproduction, and distribution 8 | 9 | #### 1. Definitions 10 | 11 | “License” shall mean the terms and conditions for use, reproduction, and 12 | distribution as defined by Sections 1 through 9 of this document. 13 | 14 | “Licensor” shall mean the copyright owner or entity authorized by the copyright 15 | owner that is granting the License. 16 | 17 | “Legal Entity” shall mean the union of the acting entity and all other entities 18 | that control, are controlled by, or are under common control with that entity. 19 | For the purposes of this definition, “control” means **(i)** the power, direct or 20 | indirect, to cause the direction or management of such entity, whether by 21 | contract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the 22 | outstanding shares, or **(iii)** beneficial ownership of such entity. 23 | 24 | “You” (or “Your”) shall mean an individual or Legal Entity exercising 25 | permissions granted by this License. 26 | 27 | “Source” form shall mean the preferred form for making modifications, including 28 | but not limited to software source code, documentation source, and configuration 29 | files. 30 | 31 | “Object” form shall mean any form resulting from mechanical transformation or 32 | translation of a Source form, including but not limited to compiled object code, 33 | generated documentation, and conversions to other media types. 34 | 35 | “Work” shall mean the work of authorship, whether in Source or Object form, made 36 | available under the License, as indicated by a copyright notice that is included 37 | in or attached to the work (an example is provided in the Appendix below). 38 | 39 | “Derivative Works” shall mean any work, whether in Source or Object form, that 40 | is based on (or derived from) the Work and for which the editorial revisions, 41 | annotations, elaborations, or other modifications represent, as a whole, an 42 | original work of authorship. For the purposes of this License, Derivative Works 43 | shall not include works that remain separable from, or merely link (or bind by 44 | name) to the interfaces of, the Work and Derivative Works thereof. 45 | 46 | “Contribution” shall mean any work of authorship, including the original version 47 | of the Work and any modifications or additions to that Work or Derivative Works 48 | thereof, that is intentionally submitted to Licensor for inclusion in the Work 49 | by the copyright owner or by an individual or Legal Entity authorized to submit 50 | on behalf of the copyright owner. For the purposes of this definition, 51 | “submitted” means any form of electronic, verbal, or written communication sent 52 | to the Licensor or its representatives, including but not limited to 53 | communication on electronic mailing lists, source code control systems, and 54 | issue tracking systems that are managed by, or on behalf of, the Licensor for 55 | the purpose of discussing and improving the Work, but excluding communication 56 | that is conspicuously marked or otherwise designated in writing by the copyright 57 | owner as “Not a Contribution.” 58 | 59 | “Contributor” shall mean Licensor and any individual or Legal Entity on behalf 60 | of whom a Contribution has been received by Licensor and subsequently 61 | incorporated within the Work. 62 | 63 | #### 2. Grant of Copyright License 64 | 65 | Subject to the terms and conditions of this License, each Contributor hereby 66 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 67 | irrevocable copyright license to reproduce, prepare Derivative Works of, 68 | publicly display, publicly perform, sublicense, and distribute the Work and such 69 | Derivative Works in Source or Object form. 70 | 71 | #### 3. Grant of Patent License 72 | 73 | Subject to the terms and conditions of this License, each Contributor hereby 74 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 75 | irrevocable (except as stated in this section) patent license to make, have 76 | made, use, offer to sell, sell, import, and otherwise transfer the Work, where 77 | such license applies only to those patent claims licensable by such Contributor 78 | that are necessarily infringed by their Contribution(s) alone or by combination 79 | of their Contribution(s) with the Work to which such Contribution(s) was 80 | submitted. If You institute patent litigation against any entity (including a 81 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a 82 | Contribution incorporated within the Work constitutes direct or contributory 83 | patent infringement, then any patent licenses granted to You under this License 84 | for that Work shall terminate as of the date such litigation is filed. 85 | 86 | #### 4. Redistribution 87 | 88 | You may reproduce and distribute copies of the Work or Derivative Works thereof 89 | in any medium, with or without modifications, and in Source or Object form, 90 | provided that You meet the following conditions: 91 | 92 | * **(a)** You must give any other recipients of the Work or Derivative Works a copy of 93 | this License; and 94 | * **(b)** You must cause any modified files to carry prominent notices stating that You 95 | changed the files; and 96 | * **(c)** You must retain, in the Source form of any Derivative Works that You distribute, 97 | all copyright, patent, trademark, and attribution notices from the Source form 98 | of the Work, excluding those notices that do not pertain to any part of the 99 | Derivative Works; and 100 | * **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any 101 | Derivative Works that You distribute must include a readable copy of the 102 | attribution notices contained within such NOTICE file, excluding those notices 103 | that do not pertain to any part of the Derivative Works, in at least one of the 104 | following places: within a NOTICE text file distributed as part of the 105 | Derivative Works; within the Source form or documentation, if provided along 106 | with the Derivative Works; or, within a display generated by the Derivative 107 | Works, if and wherever such third-party notices normally appear. The contents of 108 | the NOTICE file are for informational purposes only and do not modify the 109 | License. You may add Your own attribution notices within Derivative Works that 110 | You distribute, alongside or as an addendum to the NOTICE text from the Work, 111 | provided that such additional attribution notices cannot be construed as 112 | modifying the License. 113 | 114 | You may add Your own copyright statement to Your modifications and may provide 115 | additional or different license terms and conditions for use, reproduction, or 116 | distribution of Your modifications, or for any such Derivative Works as a whole, 117 | provided Your use, reproduction, and distribution of the Work otherwise complies 118 | with the conditions stated in this License. 119 | 120 | #### 5. Submission of Contributions 121 | 122 | Unless You explicitly state otherwise, any Contribution intentionally submitted 123 | for inclusion in the Work by You to the Licensor shall be under the terms and 124 | conditions of this License, without any additional terms or conditions. 125 | Notwithstanding the above, nothing herein shall supersede or modify the terms of 126 | any separate license agreement you may have executed with Licensor regarding 127 | such Contributions. 128 | 129 | #### 6. Trademarks 130 | 131 | This License does not grant permission to use the trade names, trademarks, 132 | service marks, or product names of the Licensor, except as required for 133 | reasonable and customary use in describing the origin of the Work and 134 | reproducing the content of the NOTICE file. 135 | 136 | #### 7. Disclaimer of Warranty 137 | 138 | Unless required by applicable law or agreed to in writing, Licensor provides the 139 | Work (and each Contributor provides its Contributions) on an “AS IS” BASIS, 140 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, 141 | including, without limitation, any warranties or conditions of TITLE, 142 | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are 143 | solely responsible for determining the appropriateness of using or 144 | redistributing the Work and assume any risks associated with Your exercise of 145 | permissions under this License. 146 | 147 | #### 8. Limitation of Liability 148 | 149 | In no event and under no legal theory, whether in tort (including negligence), 150 | contract, or otherwise, unless required by applicable law (such as deliberate 151 | and grossly negligent acts) or agreed to in writing, shall any Contributor be 152 | liable to You for damages, including any direct, indirect, special, incidental, 153 | or consequential damages of any character arising as a result of this License or 154 | out of the use or inability to use the Work (including but not limited to 155 | damages for loss of goodwill, work stoppage, computer failure or malfunction, or 156 | any and all other commercial damages or losses), even if such Contributor has 157 | been advised of the possibility of such damages. 158 | 159 | #### 9. Accepting Warranty or Additional Liability 160 | 161 | While redistributing the Work or Derivative Works thereof, You may choose to 162 | offer, and charge a fee for, acceptance of support, warranty, indemnity, or 163 | other liability obligations and/or rights consistent with this License. However, 164 | in accepting such obligations, You may act only on Your own behalf and on Your 165 | sole responsibility, not on behalf of any other Contributor, and only if You 166 | agree to indemnify, defend, and hold each Contributor harmless for any liability 167 | incurred by, or claims asserted against, such Contributor by reason of your 168 | accepting any such warranty or additional liability. 169 | 170 | _END OF TERMS AND CONDITIONS_ 171 | 172 | ### APPENDIX: How to apply the Apache License to your work 173 | 174 | To apply the Apache License to your work, attach the following boilerplate 175 | notice, with the fields enclosed by brackets `[]` replaced with your own 176 | identifying information. (Don't include the brackets!) The text should be 177 | enclosed in the appropriate comment syntax for the file format. We also 178 | recommend that a file or class name and description of purpose be included on 179 | the same “printed page” as the copyright notice for easier identification within 180 | third-party archives. 181 | 182 | Copyright [yyyy] [name of copyright owner] 183 | 184 | Licensed under the Apache License, Version 2.0 (the "License"); 185 | you may not use this file except in compliance with the License. 186 | You may obtain a copy of the License at 187 | 188 | http://www.apache.org/licenses/LICENSE-2.0 189 | 190 | Unless required by applicable law or agreed to in writing, software 191 | distributed under the License is distributed on an "AS IS" BASIS, 192 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 193 | See the License for the specific language governing permissions and 194 | limitations under the License. 195 | 196 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile 3 | # 4 | # Copyright (c) 2022-present Bartek Kryza 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | # 19 | # This Makefile is just a handy wrapper around cmake 20 | # 21 | 22 | .DEFAULT_GOAL := debug 23 | 24 | NUMPROC ?= $(shell nproc) 25 | 26 | LLVM_CONFIG_PATH ?= 27 | CMAKE_CXX_FLAGS ?= 28 | CMAKE_EXE_LINKER_FLAGS ?= 29 | 30 | GIT_VERSION ?= $(shell git describe --tags --always --abbrev=7) 31 | PKG_VERSION ?= $(shell git describe --tags --always --abbrev=7 | tr - .) 32 | GIT_COMMIT ?= $(shell git rev-parse HEAD) 33 | GIT_BRANCH ?= $(shell git rev-parse --abbrev-ref HEAD) 34 | 35 | WITH_JSON ?= ON 36 | 37 | .PHONY: clean 38 | clean: 39 | rm -rf debug release 40 | 41 | debug/CMakeLists.txt: 42 | cmake -S . -B debug \ 43 | -DGIT_VERSION=$(GIT_VERSION) \ 44 | -DCMAKE_BUILD_TYPE=Debug \ 45 | -DCMAKE_CXX_FLAGS="$(CMAKE_CXX_FLAGS)" \ 46 | -DCMAKE_EXE_LINKER_FLAGS="$(CMAKE_EXE_LINKER_FLAGS)" \ 47 | -DLLVM_CONFIG_PATH=$(LLVM_CONFIG_PATH) \ 48 | -DWITH_JSON=$(WITH_JSON) 49 | 50 | release/CMakeLists.txt: 51 | cmake -S . -B release \ 52 | -DGIT_VERSION=$(GIT_VERSION) \ 53 | -DCMAKE_BUILD_TYPE=Release \ 54 | -DCMAKE_CXX_FLAGS="$(CMAKE_CXX_FLAGS)" \ 55 | -DCMAKE_EXE_LINKER_FLAGS="$(CMAKE_EXE_LINKER_FLAGS)" \ 56 | -DLLVM_CONFIG_PATH=$(LLVM_CONFIG_PATH) \ 57 | -DWITH_JSON=$(WITH_JSON) 58 | 59 | debug: debug/CMakeLists.txt 60 | echo "Using ${NUMPROC} cores" 61 | make -C debug -j$(NUMPROC) 62 | 63 | release: release/CMakeLists.txt 64 | echo "Using ${NUMPROC} cores" 65 | make -C release -j$(NUMPROC) 66 | 67 | test: debug 68 | CTEST_OUTPUT_ON_FAILURE=1 make -C debug test 69 | 70 | test_release: release 71 | CTEST_OUTPUT_ON_FAILURE=1 make -C release test 72 | 73 | .PHONY: check-formatting 74 | check-formatting: 75 | ./util/check_formatting.sh 76 | 77 | .PHONY: format 78 | format: 79 | docker run --rm -v $(CURDIR):/root/sources bkryza/clang-format-check:1.5 80 | 81 | .PHONY: tidy 82 | tidy: 83 | cmake --build debug --target clang-tidy 84 | 85 | .PHONY: fedora/% 86 | fedora/%: 87 | mkdir -p packaging/_BUILD/fedora/$* 88 | git archive --format=tar.gz --prefix=clang-include-graph-$(PKG_VERSION)/ v$(GIT_VERSION) >packaging/_BUILD/fedora/$*/clang-include-graph-$(PKG_VERSION).tar.gz 89 | docker run --cpuset-cpus=0-7 -v $(PWD):$(PWD) fedora:$* sh -c "dnf install -y make git && cd ${PWD} && make OS=fedora DIST=$* VERSION=${PKG_VERSION} COMMIT=${GIT_COMMIT} BRANCH=${GIT_BRANCH} -C packaging rpm" 90 | 91 | -------------------------------------------------------------------------------- /build.ps1: -------------------------------------------------------------------------------- 1 | param ( 2 | $Prefix="C:\llvm20", 3 | $BuildType="Release" 4 | ) 5 | 6 | # Save the original directory 7 | $originalDirectory = Get-Location 8 | 9 | $ErrorActionPreference = "Stop" 10 | $PSNativeCommandUseErrorActionPreference = $true 11 | 12 | try { 13 | Set-Location util/msbuild_compile_commands_logger 14 | dotnet build 15 | Copy-Item bin/Debug/netstandard2.0/CompileCommandsLogger.dll CompileCommandsLogger.dll 16 | Set-Location $originalDirectory 17 | 18 | cmake -G "Visual Studio 17 2022" -S . -B $BuildType -DBOOST_STATIC=ON -DCMAKE_PREFIX_PATH="$Prefix" -Thost=x64 19 | 20 | cmake --build $BuildType --config $BuildType -- "-logger:$PWD/util/msbuild_compile_commands_logger/CompileCommandsLogger.dll" 21 | 22 | Set-Location $BuildType 23 | 24 | ctest -C $BuildType --output-on-failure 25 | } 26 | catch { 27 | Write-Host $Error[0] 28 | Write-Host "Exiting script." 29 | return 30 | } 31 | finally { 32 | # Always return to the original directory 33 | Set-Location $originalDirectory 34 | } -------------------------------------------------------------------------------- /cmake/FindLibClang.cmake: -------------------------------------------------------------------------------- 1 | # FindLibClang 2 | # 3 | # This module searches libclang and llvm-config, the llvm-config tool is used to 4 | # get information about the installed llvm/clang package to compile LLVM based 5 | # programs. 6 | # 7 | # It defines the following variables 8 | # 9 | # ``LIBCLANG_LLVM_CONFIG_EXECUTABLE`` 10 | # the llvm-config tool to get various information. 11 | # ``LIBCLANG_LIBRARIES`` 12 | # the clang libraries to link against to use Clang/LLVM. 13 | # ``LIBCLANG_LIBDIR`` 14 | # the directory where the clang libraries are located. 15 | # ``LIBCLANG_FOUND`` 16 | # true if libclang was found 17 | # ``LIBCLANG_VERSION_STRING`` 18 | # version number as a string 19 | # ``LIBCLANG_CXXFLAGS`` 20 | # the compiler flags for files that include LLVM headers 21 | # 22 | #============================================================================= 23 | # Copyright (C) 2011, 2012, 2013 Jan Erik Hanssen and Anders Bakken 24 | # Copyright (C) 2015 Christian Schwarzgruber 25 | # 26 | # This file is part of RTags (https://github.com/Andersbakken/rtags). 27 | # 28 | # RTags is free software: you can redistribute it and/or modify 29 | # it under the terms of the GNU General Public License as published by 30 | # the Free Software Foundation, either version 3 of the License, or 31 | # (at your option) any later version. 32 | # 33 | # RTags is distributed in the hope that it will be useful, 34 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 35 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 36 | # GNU General Public License for more details. 37 | # 38 | # You should have received a copy of the GNU General Public License 39 | # along with RTags. If not, see . 40 | 41 | if (NOT LIBCLANG_ROOT_DIR) 42 | set(LIBCLANG_ROOT_DIR $ENV{LIBCLANG_ROOT_DIR}) 43 | endif () 44 | 45 | if (NOT LIBCLANG_LLVM_CONFIG_EXECUTABLE) 46 | set(LIBCLANG_LLVM_CONFIG_EXECUTABLE $ENV{LIBCLANG_LLVM_CONFIG_EXECUTABLE}) 47 | if (NOT LIBCLANG_LLVM_CONFIG_EXECUTABLE) 48 | find_program(LIBCLANG_LLVM_CONFIG_EXECUTABLE "llvm-config") 49 | endif () 50 | if (NOT LIBCLANG_LLVM_CONFIG_EXECUTABLE) 51 | if (APPLE) 52 | execute_process(COMMAND brew --prefix llvm OUTPUT_VARIABLE BREW_LLVM_PATH RESULT_VARIABLE BREW_LLVM_RESULT) 53 | if (NOT ${BREW_LLVM_RESULT} EQUAL 0) 54 | set(BREW_LLVM_PATH "/usr/local/opt/llvm") 55 | endif () 56 | string(STRIP ${BREW_LLVM_PATH} BREW_LLVM_PATH) 57 | find_program(LIBCLANG_LLVM_CONFIG_EXECUTABLE NAMES llvm-config PATHS "${BREW_LLVM_PATH}/bin") 58 | else () 59 | set(llvm_config_names llvm-config) 60 | foreach(major RANGE 20 3) 61 | list(APPEND llvm_config_names "llvm-config${major}" "llvm-config-${major}") 62 | foreach(minor RANGE 9 0) 63 | list(APPEND llvm_config_names "llvm-config${major}${minor}" "llvm-config-${major}.${minor}" "llvm-config-mp-${major}.${minor}") 64 | endforeach () 65 | endforeach () 66 | find_program(LIBCLANG_LLVM_CONFIG_EXECUTABLE NAMES ${llvm_config_names} PATHS /usr/bin) 67 | endif () 68 | endif () 69 | if (LIBCLANG_LLVM_CONFIG_EXECUTABLE) 70 | message(STATUS "llvm-config executable found: ${LIBCLANG_LLVM_CONFIG_EXECUTABLE}") 71 | endif () 72 | endif () 73 | 74 | if (NOT LIBCLANG_CXXFLAGS) 75 | if (NOT LIBCLANG_LLVM_CONFIG_EXECUTABLE) 76 | message(FATAL_ERROR "Could NOT find llvm-config executable and LIBCLANG_CXXFLAGS is not set ") 77 | endif () 78 | execute_process(COMMAND ${LIBCLANG_LLVM_CONFIG_EXECUTABLE} --cxxflags OUTPUT_VARIABLE LIBCLANG_CXXFLAGS OUTPUT_STRIP_TRAILING_WHITESPACE) 79 | if (NOT LIBCLANG_CXXFLAGS) 80 | find_path(LIBCLANG_CXXFLAGS_HACK_CMAKECACHE_DOT_TEXT_BULLSHIT clang-c/Index.h HINTS ${LIBCLANG_ROOT_DIR}/include NO_DEFAULT_PATH) 81 | if (NOT EXISTS ${LIBCLANG_CXXFLAGS_HACK_CMAKECACHE_DOT_TEXT_BULLSHIT}) 82 | find_path(LIBCLANG_CXXFLAGS clang-c/Index.h) 83 | if (NOT EXISTS ${LIBCLANG_CXXFLAGS}) 84 | message(FATAL_ERROR "Could NOT find clang include path. You can fix this by setting LIBCLANG_CXXFLAGS in your shell or as a cmake variable.") 85 | endif () 86 | else () 87 | set(LIBCLANG_CXXFLAGS ${LIBCLANG_CXXFLAGS_HACK_CMAKECACHE_DOT_TEXT_BULLSHIT}) 88 | endif () 89 | set(LIBCLANG_CXXFLAGS "-I${LIBCLANG_CXXFLAGS}") 90 | endif () 91 | string(REGEX MATCHALL "-(D__?[a-zA-Z_]*|I([^\" ]+|\"[^\"]+\"))" LIBCLANG_CXXFLAGS "${LIBCLANG_CXXFLAGS}") 92 | string(REGEX REPLACE ";" " " LIBCLANG_CXXFLAGS "${LIBCLANG_CXXFLAGS}") 93 | set(LIBCLANG_CXXFLAGS ${LIBCLANG_CXXFLAGS} CACHE STRING "The LLVM C++ compiler flags needed to compile LLVM based applications.") 94 | unset(LIBCLANG_CXXFLAGS_HACK_CMAKECACHE_DOT_TEXT_BULLSHIT CACHE) 95 | endif () 96 | 97 | if (NOT EXISTS ${LIBCLANG_LIBDIR}) 98 | if (NOT LIBCLANG_LLVM_CONFIG_EXECUTABLE) 99 | message(FATAL_ERROR "Could NOT find llvm-config executable and LIBCLANG_LIBDIR is not set ") 100 | endif () 101 | execute_process(COMMAND ${LIBCLANG_LLVM_CONFIG_EXECUTABLE} --libdir OUTPUT_VARIABLE LIBCLANG_LIBDIR OUTPUT_STRIP_TRAILING_WHITESPACE) 102 | if (NOT EXISTS ${LIBCLANG_LIBDIR}) 103 | message(FATAL_ERROR "Could NOT find clang libdir. You can fix this by setting LIBCLANG_LIBDIR in your shell or as a cmake variable.") 104 | endif () 105 | set(LIBCLANG_LIBDIR ${LIBCLANG_LIBDIR} CACHE STRING "Path to the clang library.") 106 | endif () 107 | 108 | if (NOT LIBCLANG_LIBRARIES) 109 | find_library(LIBCLANG_LIB_HACK_CMAKECACHE_DOT_TEXT_BULLSHIT NAMES clang libclang HINTS ${LIBCLANG_LIBDIR} ${LIBCLANG_ROOT_DIR}/lib NO_DEFAULT_PATH) 110 | if (LIBCLANG_LIB_HACK_CMAKECACHE_DOT_TEXT_BULLSHIT) 111 | set(LIBCLANG_LIBRARIES "${LIBCLANG_LIB_HACK_CMAKECACHE_DOT_TEXT_BULLSHIT}") 112 | else () 113 | find_library(LIBCLANG_LIBRARIES NAMES clang libclang) 114 | if (NOT EXISTS ${LIBCLANG_LIBRARIES}) 115 | set (LIBCLANG_LIBRARIES "-L${LIBCLANG_LIBDIR}" "-lclang" "-Wl,-rpath,${LIBCLANG_LIBDIR}") 116 | endif () 117 | endif () 118 | unset(LIBCLANG_LIB_HACK_CMAKECACHE_DOT_TEXT_BULLSHIT CACHE) 119 | endif () 120 | set(LIBCLANG_LIBRARY ${LIBCLANG_LIBRARIES} CACHE FILEPATH "Path to the libclang library") 121 | 122 | if(NOT MSVC) 123 | if (NOT LIBCLANG_SYSTEM_LIBS) 124 | execute_process(COMMAND ${LIBCLANG_LLVM_CONFIG_EXECUTABLE} --system-libs OUTPUT_VARIABLE LIBCLANG_SYSTEM_LIBS OUTPUT_STRIP_TRAILING_WHITESPACE) 125 | if (LIBCLANG_SYSTEM_LIBS) 126 | set (LIBCLANG_LIBRARIES ${LIBCLANG_LIBRARIES} ${LIBCLANG_SYSTEM_LIBS}) 127 | endif () 128 | endif () 129 | endif(NOT MSVC) 130 | 131 | # Currently only static clang linking is supported on Windows 132 | if(MSVC) 133 | # Add clang static libs 134 | set(CLANG_STATIC_LIBS 135 | clangAnalysis 136 | clangAnalysisFlowSensitive 137 | clangAnalysisFlowSensitiveModels 138 | clangAPINotes 139 | clangARCMigrate 140 | clangAST 141 | clangASTMatchers 142 | clangBasic 143 | clangCodeGen 144 | clangCrossTU 145 | clangDependencyScanning 146 | clangDirectoryWatcher 147 | clangDriver 148 | clangDynamicASTMatchers 149 | clangEdit 150 | clangExtractAPI 151 | clangFormat 152 | clangFrontend 153 | clangFrontendTool 154 | clangHandleCXX 155 | clangHandleLLVM 156 | clangIndex 157 | clangIndexSerialization 158 | clangInstallAPI 159 | clangInterpreter 160 | clangLex 161 | clangParse 162 | clangRewrite 163 | clangRewriteFrontend 164 | clangSema 165 | clangSerialization 166 | clangStaticAnalyzerCheckers 167 | clangStaticAnalyzerCore 168 | clangStaticAnalyzerFrontend 169 | clangSupport 170 | clangTooling 171 | clangToolingASTDiff 172 | clangToolingCore 173 | clangToolingInclusions 174 | clangToolingInclusionsStdlib 175 | clangToolingRefactoring 176 | clangToolingSyntax 177 | clangTransformer 178 | LLVMAggressiveInstCombine 179 | LLVMAnalysis 180 | LLVMAsmParser 181 | LLVMAsmPrinter 182 | LLVMBinaryFormat 183 | LLVMBitReader 184 | LLVMBitstreamReader 185 | LLVMBitWriter 186 | LLVMCFGuard 187 | LLVMCFIVerify 188 | LLVMCGData 189 | LLVMCodeGen 190 | LLVMCodeGenTypes 191 | LLVMCore 192 | LLVMCoroutines 193 | LLVMCoverage 194 | LLVMDebugInfoBTF 195 | LLVMDebugInfoCodeView 196 | LLVMDebuginfod 197 | LLVMDebugInfoDWARF 198 | LLVMDebugInfoGSYM 199 | LLVMDebugInfoLogicalView 200 | LLVMDebugInfoMSF 201 | LLVMDebugInfoPDB 202 | LLVMDemangle 203 | LLVMDiff 204 | LLVMDlltoolDriver 205 | LLVMDWARFLinker 206 | LLVMDWARFLinkerClassic 207 | LLVMDWARFLinkerParallel 208 | LLVMDWP 209 | LLVMExecutionEngine 210 | LLVMExegesis 211 | LLVMExegesisX86 212 | LLVMExtensions 213 | LLVMFileCheck 214 | LLVMFrontendAtomic 215 | LLVMFrontendDriver 216 | LLVMFrontendHLSL 217 | LLVMFrontendOffloading 218 | LLVMFrontendOpenACC 219 | LLVMFrontendOpenMP 220 | LLVMFuzzerCLI 221 | LLVMFuzzMutate 222 | LLVMGlobalISel 223 | LLVMHipStdPar 224 | LLVMInstCombine 225 | LLVMInstrumentation 226 | LLVMInterfaceStub 227 | LLVMInterpreter 228 | LLVMipo 229 | LLVMIRPrinter 230 | LLVMIRReader 231 | LLVMJITLink 232 | LLVMLibDriver 233 | LLVMLineEditor 234 | LLVMLinker 235 | LLVMLTO 236 | LLVMMC 237 | LLVMMCA 238 | LLVMMCDisassembler 239 | LLVMMCJIT 240 | LLVMMCParser 241 | LLVMMIRParser 242 | LLVMObjCARCOpts 243 | LLVMObjCopy 244 | LLVMObject 245 | LLVMObjectYAML 246 | LLVMOptDriver 247 | LLVMOption 248 | LLVMOrcDebugging 249 | LLVMOrcJIT 250 | LLVMOrcShared 251 | LLVMOrcTargetProcess 252 | LLVMPasses 253 | LLVMProfileData 254 | LLVMRemarks 255 | LLVMRuntimeDyld 256 | LLVMSandboxIR 257 | LLVMScalarOpts 258 | LLVMSelectionDAG 259 | LLVMSupport 260 | LLVMSymbolize 261 | LLVMTableGen 262 | LLVMTableGenBasic 263 | LLVMTableGenCommon 264 | LLVMTarget 265 | LLVMTargetParser 266 | LLVMTelemetry 267 | LLVMTextAPI 268 | LLVMTextAPIBinaryReader 269 | LLVMTransformUtils 270 | LLVMVectorize 271 | LLVMWindowsDriver 272 | LLVMWindowsManifest 273 | LLVMX86AsmParser 274 | LLVMX86CodeGen 275 | LLVMX86Desc 276 | LLVMX86Disassembler 277 | LLVMX86Info 278 | LLVMX86TargetMCA 279 | LLVMXRay) 280 | list(TRANSFORM CLANG_STATIC_LIBS PREPEND ${LIBCLANG_LIBDIR}\\ OUTPUT_VARIABLE CLANG_STATIC_LIBS_RES) 281 | list(TRANSFORM CLANG_STATIC_LIBS_RES APPEND .lib OUTPUT_VARIABLE CLANG_STATIC_LIBS_RES) 282 | set (LIBCLANG_LIBRARIES ${CLANG_STATIC_LIBS_RES} ${LIBCLANG_LIBRARIES} version ntdll) 283 | endif(MSVC) 284 | 285 | if (LIBCLANG_LLVM_CONFIG_EXECUTABLE) 286 | execute_process(COMMAND ${LIBCLANG_LLVM_CONFIG_EXECUTABLE} --version OUTPUT_VARIABLE LIBCLANG_VERSION_STRING OUTPUT_STRIP_TRAILING_WHITESPACE) 287 | else () 288 | set(LIBCLANG_VERSION_STRING "Unknown") 289 | endif () 290 | message("-- Using Clang version ${LIBCLANG_VERSION_STRING} from ${LIBCLANG_LIBDIR} with CXXFLAGS ${LIBCLANG_CXXFLAGS}") 291 | 292 | # Handle the QUIETLY and REQUIRED arguments and set LIBCLANG_FOUND to TRUE if all listed variables are TRUE 293 | include(FindPackageHandleStandardArgs) 294 | find_package_handle_standard_args(LibClang DEFAULT_MSG LIBCLANG_LIBRARY LIBCLANG_CXXFLAGS LIBCLANG_LIBDIR) 295 | mark_as_advanced(LIBCLANG_CXXFLAGS LIBCLANG_LIBRARY LIBCLANG_LLVM_CONFIG_EXECUTABLE LIBCLANG_LIBDIR) 296 | -------------------------------------------------------------------------------- /cmake/GitVersion.cmake: -------------------------------------------------------------------------------- 1 | find_package(Git) 2 | 3 | function(setup_git_version) 4 | if(NOT DEFINED GIT_VERSION) 5 | if(GIT_EXECUTABLE) 6 | execute_process( 7 | COMMAND ${GIT_EXECUTABLE} describe --tags --always --abbrev=7 8 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 9 | OUTPUT_VARIABLE GIT_VERSION 10 | RESULT_VARIABLE GIT_ERROR_CODE 11 | OUTPUT_STRIP_TRAILING_WHITESPACE 12 | ) 13 | else(GIT_EXECUTABLE) 14 | message(STATUS "Git executable not found") 15 | endif(GIT_EXECUTABLE) 16 | endif(NOT DEFINED GIT_VERSION) 17 | 18 | if(NOT DEFINED GIT_VERSION OR "${GIT_VERSION}" STREQUAL "") 19 | set(GIT_VERSION "0.0.0-unknown") 20 | endif(NOT DEFINED GIT_VERSION OR "${GIT_VERSION}" STREQUAL "") 21 | 22 | string(REGEX MATCH "^([0-9]+)\\.([0-9]+)\\.(.+)" 23 | GIT_VERSION_MATCH ${GIT_VERSION}) 24 | set(GIT_VERSION_MAJOR ${CMAKE_MATCH_1} PARENT_SCOPE) 25 | set(GIT_VERSION_MINOR ${CMAKE_MATCH_2} PARENT_SCOPE) 26 | set(GIT_VERSION_PATCH ${CMAKE_MATCH_3} PARENT_SCOPE) 27 | set(GIT_VERSION ${GIT_VERSION} PARENT_SCOPE) 28 | 29 | endfunction() -------------------------------------------------------------------------------- /packaging/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile 2 | # 3 | # Copyright (c) 2022-present Bartek Kryza 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | SHELL := /bin/bash 18 | .ONESHELL: 19 | 20 | .PHONY: download deb debian clean conda 21 | 22 | NAME ?= clang-include-graph 23 | REBUILD ?= 1 24 | MAINTAINER_NAME ?= Bartek Kryza 25 | MAINTAINER_EMAIL ?= bkryza@gmail.com 26 | GPG_KEY ?= 702014E322FE5CA9B5D920F66CDA4566635E93B1 27 | OS ?= ubuntu 28 | DIST ?= focal 29 | TAR_EXT ?= gz 30 | 31 | build_dir = _BUILD/$(OS)/$(DIST) 32 | 33 | VERSION ?= $(shell git describe --tags --always --abbrev=7) 34 | COMMIT ?= $(shell git rev-parse HEAD) 35 | BRANCH ?= $(shell git rev-parse --abbrev-ref HEAD) 36 | SOURCE_ARCHIVE ?= $(NAME)-$(VERSION).tar.$(TAR_EXT) 37 | SOURCE_ARCHIVE_DEB ?= $(NAME)-$(VERSION)-$(REBUILD).tar.$(TAR_EXT) 38 | SOURCE_ARCHIVE_RPM ?= $(NAME)-$(VERSION).tar.$(TAR_EXT) 39 | CONDA_TOKEN ?= 40 | WITH_JSON ?= ON 41 | 42 | # 43 | # Replace mustache template variable in all files in directory recursively, 44 | # e.g.: 45 | # $(call subst_template,VERSION,${VERSION},debian) 46 | # 47 | define subst_template_dir 48 | find $(3) -type f -exec sed -i "s/{{$(1)}}/$(2)/g" {} \; 49 | endef 50 | 51 | define subst_conda_meta_yaml 52 | find $(3) -name meta.yaml -exec sed -i "s/{{$(1)}}/$(2)/g" {} \; 53 | endef 54 | 55 | 56 | 57 | _BUILD/$(SOURCE_ARCHIVE): 58 | echo "############################" 59 | echo "Creating source archive from latest commit $(COMMIT) - $(SOURCE_ARCHIVE) - $(VERSION)" 60 | echo "############################" 61 | mkdir -p $(build_dir) 62 | git -C .. archive --format=tar.gz --prefix=$(NAME)-$(VERSION)/ $(VERSION) > _BUILD/$(SOURCE_ARCHIVE) 63 | 64 | _BUILD/$(SOURCE_ARCHIVE_DEB): 65 | echo "############################" 66 | echo "Creating source archive for DEB from latest commit $(COMMIT) - $(SOURCE_ARCHIVE) - $(VERSION)" 67 | echo "############################" 68 | mkdir -p $(build_dir) 69 | git -C .. archive --format=tar.gz --prefix=$(NAME)-$(VERSION)/ $(VERSION) > _BUILD/$(SOURCE_ARCHIVE_DEB) 70 | 71 | rpm: 72 | echo "############################" 73 | echo "Creating rpm package for $(OS) $(DIST)" 74 | echo "Creating directory: ", $(build_dir)/$(NAME)-$(VERSION) 75 | echo "Extracting source archive..." 76 | echo "############################" 77 | #rm -rf $(build_dir) 78 | mkdir -p $(build_dir) 79 | dnf install -y fedora-packager rpmdevtools gcc gdb cmake git boost-devel clang-devel clang-tools-extra llvm-devel ccache wget 80 | rpmdev-setuptree 81 | cp $(build_dir)/$(SOURCE_ARCHIVE) /root/rpmbuild/SOURCES/ 82 | cp fedora/clang-include-graph.spec /root/rpmbuild/SPECS/ 83 | rpmbuild -ba --define 'git_version ${VERSION}' /root/rpmbuild/SPECS/clang-include-graph.spec 84 | cp /root/rpmbuild/RPMS/x86_64/* $(build_dir) 85 | cp /root/rpmbuild/SRPMS/* $(build_dir) 86 | 87 | # 88 | # Debian 89 | # 90 | debian: _BUILD/$(SOURCE_ARCHIVE_DEB) 91 | echo "############################" 92 | echo "Creating debian source package for $(OS) $(DIST)" 93 | echo "Creating directory: ", $(build_dir)/$(NAME)-$(VERSION)-$(REBUILD) 94 | echo "Extracting source archive..." 95 | echo "############################" 96 | rm -rf $(build_dir) 97 | mkdir -p $(build_dir) 98 | cp _BUILD/$(SOURCE_ARCHIVE_DEB) $(build_dir) 99 | cd $(build_dir) 100 | mkdir -p $(NAME)-$(VERSION)-$(REBUILD) 101 | tar xf $(SOURCE_ARCHIVE_DEB) -C $(NAME)-$(VERSION)-$(REBUILD) --strip-components 1 102 | cp -R ../../../debian $(NAME)-$(VERSION)-$(REBUILD)/debian 103 | cd $(NAME)-$(VERSION)-$(REBUILD) 104 | $(call subst_template_dir,DATETIME,$(shell date -R),debian) 105 | $(call subst_template_dir,OS,${OS},debian) 106 | $(call subst_template_dir,NAME,${NAME},debian) 107 | $(call subst_template_dir,VERSION,${VERSION},debian) 108 | $(call subst_template_dir,REBUILD,${REBUILD},debian) 109 | $(call subst_template_dir,DISTRIBUTION,${DIST},debian) 110 | $(call subst_template_dir,MAINTAINER_NAME,${MAINTAINER_NAME},debian) 111 | $(call subst_template_dir,MAINTAINER_EMAIL,${MAINTAINER_EMAIL},debian) 112 | $(call subst_template_dir,GIT_COMMIT,${COMMIT},debian) 113 | $(call subst_template_dir,GIT_BRANCH,${BRANCH},debian) 114 | $(call subst_template_dir,DIST,${DIST},debian) 115 | $(call subst_template_dir,WITH_JSON,${WITH_JSON},debian) 116 | cp debian/control.$(DIST) debian/control 117 | cp debian/changelog.$(DIST) debian/changelog 118 | DEB_BUILD_OPTIONS='parallel=16' dpkg-buildpackage -b -us -uc 119 | 120 | # 121 | # Ubuntu 122 | # 123 | deb: _BUILD/$(SOURCE_ARCHIVE) 124 | echo "############################" 125 | echo "Creating deb source package for $(OS) $(DIST)" 126 | echo "Creating directory: ", $(build_dir)/$(NAME)-$(VERSION) 127 | echo "Extracting source archive..." 128 | echo "############################" 129 | rm -rf $(build_dir) 130 | mkdir -p $(build_dir) 131 | cp _BUILD/$(SOURCE_ARCHIVE) $(build_dir) 132 | cd $(build_dir) 133 | mkdir -p $(NAME)-$(VERSION) 134 | tar xf $(SOURCE_ARCHIVE) -C $(NAME)-$(VERSION) --strip-components 1 135 | cp -R ../../../debian $(NAME)-$(VERSION)/debian 136 | cd $(NAME)-$(VERSION) 137 | $(call subst_template_dir,DATETIME,$(shell date -R),debian) 138 | $(call subst_template_dir,OS,${OS},debian) 139 | $(call subst_template_dir,NAME,${NAME},debian) 140 | $(call subst_template_dir,VERSION,${VERSION},debian) 141 | $(call subst_template_dir,REBUILD,${REBUILD},debian) 142 | $(call subst_template_dir,DISTRIBUTION,${DIST},debian) 143 | $(call subst_template_dir,MAINTAINER_NAME,${MAINTAINER_NAME},debian) 144 | $(call subst_template_dir,MAINTAINER_EMAIL,${MAINTAINER_EMAIL},debian) 145 | $(call subst_template_dir,GIT_COMMIT,${COMMIT},debian) 146 | $(call subst_template_dir,GIT_BRANCH,${BRANCH},debian) 147 | $(call subst_template_dir,DIST,${DIST},debian) 148 | $(call subst_template_dir,WITH_JSON,${WITH_JSON},debian) 149 | mk-origtargz ../$(NAME)-$(VERSION).tar.$(TAR_EXT) 150 | cp debian/control.$(DIST) debian/control 151 | # BUILD SOURCE PACKAGE FOR LAUNCHPAD 152 | debuild -S -sa -us -d -k$(GPG_KEY) 153 | # BUILD LOCALLY BINARY PACKAGE 154 | # debuild -us -uc 155 | 156 | conda: _BUILD/$(SOURCE_ARCHIVE) 157 | echo "############################" 158 | echo "Creating conda archive from source file $(SOURCE_ARCHIVE)" 159 | echo "############################" 160 | conda config --add channels conda-forge 161 | conda config --set channel_priority strict 162 | mkdir -p _BUILD/conda 163 | cp _BUILD/$(SOURCE_ARCHIVE) _BUILD/conda/ 164 | cp conda/meta.yaml.in conda/meta.yaml 165 | $(call subst_conda_meta_yaml,PKG_VERSION,${VERSION},conda) 166 | $(call subst_conda_meta_yaml,PKG_SOURCE,..\/_BUILD\/clang-include-graph-$(VERSION).tar.$(TAR_EXT),conda) 167 | $(call subst_conda_meta_yaml,GIT_COMMIT,${COMMIT},conda) 168 | $(call subst_conda_meta_yaml,GIT_BRANCH,${BRANCH},conda) 169 | conda build --user bkryza --token $(CONDA_TOKEN) conda 170 | -------------------------------------------------------------------------------- /packaging/README.md: -------------------------------------------------------------------------------- 1 | # Building releases 2 | 3 | * Update CHANGELOG.md 4 | * Tag the release commit, e.g. ```git tag 0.1.0``` 5 | 6 | ## Ubuntu 7 | 8 | ```bash 9 | cd packaging 10 | make DIST=plucky deb 11 | make DIST=focal deb 12 | make DIST=jammy deb 13 | make DIST=noble deb 14 | make DIST=oracular deb 15 | 16 | # Repeat for each distro 17 | cd _BUILD/ubuntu/focal 18 | dput ppa:bkryza/clang-include-graph *.changes 19 | 20 | ``` 21 | 22 | ## Debian 23 | 24 | ```bash 25 | docker run --rm -v $PWD:$PWD -it debian:bookworm bash 26 | apt update 27 | apt install debhelper python3 python3-pip git make ccache pkg-config gcc g++ gdb cmake libyaml-cpp-dev llvm-19 llvm-19-dev clang-19 clang-tools-19 libclang-19-dev libclang-cpp19-dev libmlir-19-dev mlir-19-tools libboost-graph1.81-dev libboost-filesystem1.81-dev libboost-test1.81-dev libboost-program-options1.81-dev libboost-json1.81-dev libboost-log1.81-dev libdw-dev libunwind-dev 28 | pip3 install --break-system-packages git-archive-all 29 | git config --global --add safe.directory /home/bartek/devel/clang-include-graph 30 | cd packaging 31 | make OS=debian DIST=bookworm debian 32 | ``` 33 | 34 | ## Fedora 35 | 36 | ```bash 37 | cd clang-include-graph 38 | make fedora/39 39 | make fedora/40 40 | make fedora/41 41 | find packaging/_BUILD/fedora 42 | ``` 43 | 44 | ## Anaconda 45 | 46 | ```bash 47 | docker run --rm -v $PWD:$PWD continuumio/miniconda3 bash 48 | conda install conda-build make anaconda-client 49 | cd packaging 50 | git config --global --add safe.directory $PWD/.. 51 | make CONDA_TOKEN= conda 52 | ``` -------------------------------------------------------------------------------- /packaging/conda/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mkdir build && cd build 4 | 5 | export PKG_CONFIG_PATH="$BUILD_PREFIX/lib/pkgconfig:$PKG_CONFIG_PATH" 6 | 7 | export CLANGINCLUDE_GRAPH_GIT_TOPLEVEL_DIR=${SRC_DIR} 8 | 9 | cmake -DCMAKE_BUILD_TYPE=Release \ 10 | -DGIT_VERSION=${GIT_VERSION} \ 11 | -DCODE_COVERAGE=OFF \ 12 | -DWITH_TESTS=ON \ 13 | -DLLVM_CONFIG_PATH=${BUILD_PREFIX}/bin/llvm-config \ 14 | -DCONDA_BUILD_PREFIX=${BUILD_PREFIX} \ 15 | -DCMAKE_INSTALL_PREFIX=${PREFIX} \ 16 | -DBOOST_STATIC=OFF \ 17 | -DCMAKE_VERBOSE_MAKEFILE=ON \ 18 | .. 19 | 20 | CTEST_OUTPUT_ON_FAILURE=1 make -j${CPU_COUNT} 21 | 22 | CTEST_OUTPUT_ON_FAILURE=1 ctest -j${CPU_COUNT} 23 | 24 | make install -------------------------------------------------------------------------------- /packaging/conda/meta.yaml.in: -------------------------------------------------------------------------------- 1 | {% set name = "clang-include-graph" %} 2 | {% set version = "{{PKG_VERSION}}" %} 3 | 4 | package: 5 | name: {{ name|lower }} 6 | version: {{ version|replace('-', '.') }} 7 | 8 | source: 9 | url: "{{PKG_SOURCE}}" 10 | 11 | build: 12 | binary_relocation: true 13 | script_env: 14 | - PKG_VERSION 15 | - GIT_VERSION={{PKG_VERSION}} 16 | - CLANGINCLUDEGRAPH_GIT_REVISION={{PKG_VERSION}} 17 | - CLANGINCLUDEGRAPH_GIT_BRANCH={{GIT_BRANCH}} 18 | - CLANGINCLUDEGRAPH_GIT_COMMIT={{GIT_COMMIT}} 19 | 20 | requirements: 21 | build: 22 | - {{ compiler('c') }} 23 | - {{ compiler('cxx') }} 24 | - conda-forge::pkg-config 25 | - conda-forge::boost 1.85.0 26 | - conda-forge::clangdev 20.1.4 27 | - conda-forge::libclang 20.1.4 28 | - conda-forge::cmake 29 | - conda-forge::git 30 | - conda-forge::make # [unix] 31 | run: 32 | - conda-forge::libclang 20.1.4 33 | - conda-forge::boost 1.85.0 34 | 35 | test: 36 | commands: 37 | - $PREFIX/bin/clang-include-graph --version 38 | 39 | about: 40 | home: https://github.com/bkryza/clang-include-graph 41 | license: Apache 2.0 42 | summary: clang-include-graph is an automatic C++ UML diagram generator based on Clang. 43 | 44 | extra: 45 | recipe-maintainers: 46 | - bkryza -------------------------------------------------------------------------------- /packaging/debian/changelog: -------------------------------------------------------------------------------- 1 | clang-include-graph ({{VERSION}}-0{{OS}}{{REBUILD}}ppa1~{{DISTRIBUTION}}) {{DISTRIBUTION}}; urgency=low 2 | 3 | * Initial release 4 | 5 | -- Bartek Kryza {{DATETIME}} 6 | -------------------------------------------------------------------------------- /packaging/debian/changelog.bookworm: -------------------------------------------------------------------------------- 1 | clang-include-graph ({{VERSION}}-1) {{DISTRIBUTION}}; urgency=low 2 | 3 | * Packages for release {{VERSION}} 4 | 5 | -- Bartek Kryza {{DATETIME}} -------------------------------------------------------------------------------- /packaging/debian/compat: -------------------------------------------------------------------------------- 1 | 10 2 | -------------------------------------------------------------------------------- /packaging/debian/control.bookworm: -------------------------------------------------------------------------------- 1 | Source: clang-include-graph 2 | Maintainer: Bartek Kryza 3 | Section: devel 4 | Priority: optional 5 | Build-Depends: 6 | debhelper, 7 | make, 8 | gcc, 9 | g++, 10 | cmake (>= 3.10), 11 | llvm-19, 12 | llvm-19-dev, 13 | clang-19, 14 | libclang-19-dev, 15 | libclang-cpp19-dev, 16 | libboost-graph1.81-dev, 17 | libboost-filesystem1.81-dev, 18 | libboost-test1.81-dev, 19 | libboost-program-options1.81-dev, 20 | libboost-json1.81-dev, 21 | libboost-log1.81-dev 22 | Standards-Version: 4.3.0 23 | Vcs-Browser: https://github.com/bkryza/clang-include-graph 24 | Vcs-Git: https://github.com/bkryza/clang-include-graph.git 25 | Homepage: https://github.com/bkryza/clang-include-graph 26 | 27 | Package: clang-include-graph 28 | Architecture: any 29 | Section: utils 30 | Depends: ${misc:Depends}, ${shlibs:Depends} 31 | Pre-Depends: ${misc:Pre-Depends} 32 | Description: Simple tool for analyzing C++ project include graph. 33 | . 34 | This package provides the clang-include-graph binary. 35 | -------------------------------------------------------------------------------- /packaging/debian/control.focal: -------------------------------------------------------------------------------- 1 | Source: clang-include-graph 2 | Maintainer: Bartek Kryza 3 | Section: devel 4 | Priority: optional 5 | Build-Depends: 6 | debhelper, 7 | make, 8 | gcc, 9 | g++, 10 | cmake (>= 3.10), 11 | llvm-12, 12 | llvm-12-dev, 13 | clang-12, 14 | libclang-12-dev, 15 | libclang-cpp12-dev, 16 | libboost-graph1.71-dev, 17 | libboost-filesystem1.71-dev, 18 | libboost-test1.71-dev, 19 | libboost-program-options1.71-dev, 20 | libboost-log1.71-dev 21 | Standards-Version: 4.3.0 22 | Vcs-Browser: https://github.com/bkryza/clang-include-graph 23 | Vcs-Git: https://github.com/bkryza/clang-include-graph.git 24 | Homepage: https://github.com/bkryza/clang-include-graph 25 | 26 | Package: clang-include-graph 27 | Architecture: any 28 | Section: utils 29 | Depends: ${misc:Depends}, ${shlibs:Depends} 30 | Pre-Depends: ${misc:Pre-Depends} 31 | Description: Simple tool for analyzing C++ project include graph. 32 | . 33 | This package provides the clang-include-graph binary. 34 | -------------------------------------------------------------------------------- /packaging/debian/control.jammy: -------------------------------------------------------------------------------- 1 | Source: clang-include-graph 2 | Maintainer: Bartek Kryza 3 | Section: devel 4 | Priority: optional 5 | Build-Depends: 6 | debhelper, 7 | make, 8 | gcc, 9 | g++, 10 | cmake (>= 3.10), 11 | llvm-15, 12 | llvm-15-dev, 13 | clang-15, 14 | libclang-15-dev, 15 | libclang-cpp15-dev, 16 | libboost-graph1.74-dev, 17 | libboost-filesystem1.74-dev, 18 | libboost-test1.74-dev, 19 | libboost-program-options1.74-dev, 20 | libboost-log1.74-dev 21 | Standards-Version: 4.3.0 22 | Vcs-Browser: https://github.com/bkryza/clang-include-graph 23 | Vcs-Git: https://github.com/bkryza/clang-include-graph.git 24 | Homepage: https://github.com/bkryza/clang-include-graph 25 | 26 | Package: clang-include-graph 27 | Architecture: any 28 | Section: utils 29 | Depends: ${misc:Depends}, ${shlibs:Depends} 30 | Pre-Depends: ${misc:Pre-Depends} 31 | Description: Simple tool for analyzing C++ project include graph. 32 | . 33 | This package provides the clang-include-graph binary. 34 | -------------------------------------------------------------------------------- /packaging/debian/control.noble: -------------------------------------------------------------------------------- 1 | Source: clang-include-graph 2 | Maintainer: Bartek Kryza 3 | Section: devel 4 | Priority: optional 5 | Build-Depends: 6 | debhelper, 7 | make, 8 | gcc, 9 | g++, 10 | cmake (>= 3.10), 11 | llvm-19, 12 | llvm-19-dev, 13 | clang-19, 14 | libclang-19-dev, 15 | libclang-cpp19-dev, 16 | libboost-graph1.83-dev, 17 | libboost-filesystem1.83-dev, 18 | libboost-test1.83-dev, 19 | libboost-program-options1.83-dev, 20 | libboost-json1.83-dev, 21 | libboost-log1.83-dev 22 | Standards-Version: 4.3.0 23 | Vcs-Browser: https://github.com/bkryza/clang-include-graph 24 | Vcs-Git: https://github.com/bkryza/clang-include-graph.git 25 | Homepage: https://github.com/bkryza/clang-include-graph 26 | 27 | Package: clang-include-graph 28 | Architecture: any 29 | Section: utils 30 | Depends: ${misc:Depends}, ${shlibs:Depends} 31 | Pre-Depends: ${misc:Pre-Depends} 32 | Description: Simple tool for analyzing C++ project include graph. 33 | . 34 | This package provides the clang-include-graph binary. 35 | -------------------------------------------------------------------------------- /packaging/debian/control.oracular: -------------------------------------------------------------------------------- 1 | Source: clang-include-graph 2 | Maintainer: Bartek Kryza 3 | Section: devel 4 | Priority: optional 5 | Build-Depends: 6 | debhelper, 7 | make, 8 | gcc, 9 | g++, 10 | cmake (>= 3.10), 11 | llvm-19, 12 | llvm-19-dev, 13 | clang-19, 14 | libclang-19-dev, 15 | libclang-cpp19-dev, 16 | libboost-graph1.83-dev, 17 | libboost-filesystem1.83-dev, 18 | libboost-test1.83-dev, 19 | libboost-program-options1.83-dev, 20 | libboost-json1.83-dev, 21 | libboost-log1.83-dev 22 | Standards-Version: 4.3.0 23 | Vcs-Browser: https://github.com/bkryza/clang-include-graph 24 | Vcs-Git: https://github.com/bkryza/clang-include-graph.git 25 | Homepage: https://github.com/bkryza/clang-include-graph 26 | 27 | Package: clang-include-graph 28 | Architecture: any 29 | Section: utils 30 | Depends: ${misc:Depends}, ${shlibs:Depends} 31 | Pre-Depends: ${misc:Pre-Depends} 32 | Description: Simple tool for analyzing C++ project include graph. 33 | . 34 | This package provides the clang-include-graph binary. 35 | -------------------------------------------------------------------------------- /packaging/debian/control.plucky: -------------------------------------------------------------------------------- 1 | Source: clang-include-graph 2 | Maintainer: Bartek Kryza 3 | Section: devel 4 | Priority: optional 5 | Build-Depends: 6 | debhelper, 7 | make, 8 | gcc, 9 | g++, 10 | cmake (>= 3.10), 11 | llvm-20, 12 | llvm-20-dev, 13 | clang-20, 14 | libclang-20-dev, 15 | libclang-cpp20-dev, 16 | libboost-graph1.83-dev, 17 | libboost-filesystem1.83-dev, 18 | libboost-test1.83-dev, 19 | libboost-program-options1.83-dev, 20 | libboost-json1.83-dev, 21 | libboost-log1.83-dev 22 | Standards-Version: 4.3.0 23 | Vcs-Browser: https://github.com/bkryza/clang-include-graph 24 | Vcs-Git: https://github.com/bkryza/clang-include-graph.git 25 | Homepage: https://github.com/bkryza/clang-include-graph 26 | 27 | Package: clang-include-graph 28 | Architecture: any 29 | Section: utils 30 | Depends: ${misc:Depends}, ${shlibs:Depends} 31 | Pre-Depends: ${misc:Pre-Depends} 32 | Description: Simple tool for analyzing C++ project include graph. 33 | . 34 | This package provides the clang-include-graph binary. 35 | -------------------------------------------------------------------------------- /packaging/debian/copyright: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: clang-include-graph 3 | Source: https://github.com/bkryza/clang-include-graph 4 | 5 | Files: * 6 | Copyright: 2022-present Bartek Kryza 7 | License: apache 8 | Licensed under the Apache License, Version 2.0 (the "License"); 9 | you may not use this file except in compliance with the License. 10 | You may obtain a copy of the License at 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | -------------------------------------------------------------------------------- /packaging/debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | export DH_VERBOSE=1 4 | export VERSION_CODENAME={{DIST}} 5 | export CLANGINCLUDEGRAPH_GIT_TOPLEVEL_DIR=$(CURDIR) 6 | export CLANGINCLUDEGRAPH_GIT_REVISION={{VERSION}} 7 | export CLANGINCLUDEGRAPH_GIT_BRANCH={{GIT_BRANCH}} 8 | export CLANGINCLUDEGRAPH_GIT_COMMIT={{GIT_COMMIT}} 9 | 10 | override_dh_auto_configure: 11 | dh_auto_configure --buildsystem=cmake -- -DCMAKE_BUILD_TYPE=release -DCMAKE_INSTALL_PREFIX=/usr -DWITH_JSON={{WITH_JSON}} -DGIT_VERSION={{VERSION}} 12 | 13 | %: 14 | dh $@ 15 | -------------------------------------------------------------------------------- /packaging/debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (quilt) 2 | -------------------------------------------------------------------------------- /packaging/debian/watch: -------------------------------------------------------------------------------- 1 | version=4 2 | 3 | https://github.com/bkryza/clang-include-graph/releases .*/[relasymcp.-]*(\d\S*)\.tar\.gz 4 | -------------------------------------------------------------------------------- /packaging/fedora/clang-include-graph.spec: -------------------------------------------------------------------------------- 1 | %define _unpackaged_files_terminate_build 0 2 | 3 | Name: clang-include-graph 4 | Version: %{?git_version} 5 | Release: 1%{?dist} 6 | Summary: Simple tool for analyzing C++ project include graph. 7 | License: ASL 2.0 8 | URL: https://github.com/bkryza/clang-include-graph 9 | Source0: clang-include-graph-%{version}.tar.gz 10 | 11 | BuildRequires: cmake 12 | BuildRequires: git 13 | BuildRequires: clang-devel 14 | BuildRequires: clang-tools-extra 15 | BuildRequires: ccache 16 | BuildRequires: boost-devel 17 | BuildRequires: gdb 18 | 19 | Requires: clang 20 | Requires: boost 21 | Requires: boost-graph 22 | Requires: boost-program_options 23 | Requires: boost-json 24 | Requires: boost-log 25 | Requires: boost-container 26 | Requires: boost-filesystem 27 | Requires: boost-regex 28 | Requires: boost-chrono 29 | Requires: boost-thread 30 | Requires: boost-atomic 31 | 32 | Requires(post): info 33 | Requires(preun): info 34 | 35 | %description 36 | This package provides the clang-include-graph binary. 37 | 38 | 39 | %prep 40 | %setup -q -n clang-include-graph-%{version} 41 | 42 | %build 43 | %cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo \ 44 | -DCMAKE_CXX_FLAGS="-Wno-nonnull -Wno-stringop-overflow -Wno-dangling-reference -Wno-array-bounds" \ 45 | -DCMAKE_NO_SYSTEM_FROM_IMPORTED=ON \ 46 | -DCMAKE_INSTALL_PREFIX=%{_exec_prefix} \ 47 | -DCMAKE_POSITION_INDEPENDENT_CODE=ON \ 48 | -DGIT_VERSION=%{version} \ 49 | -DBUILD_TESTS=OFF 50 | 51 | %cmake_build 52 | 53 | %install 54 | %cmake_install 55 | rm -f %{buildroot}/%{_infodir}/dir 56 | 57 | %post 58 | /sbin/install-info %{_infodir}/%{name}.info %{_infodir}/dir || : 59 | 60 | %preun 61 | if [ $1 = 0 ] ; then 62 | /sbin/install-info --delete %{_infodir}/%{name}.info %{_infodir}/dir || : 63 | fi 64 | 65 | %files 66 | %{_bindir}/clang-include-graph 67 | %doc CHANGELOG.md README.md AUTHORS.md LICENSE.md 68 | %license LICENSE.md 69 | 70 | %changelog 71 | * Wed May 14 2025 Bartek Kryza 72 | - Initial version of the package for Fedora 73 | -------------------------------------------------------------------------------- /packaging/make_installer.ps1: -------------------------------------------------------------------------------- 1 | # This script assumes that all clang-uml dependencies are instaled in C:\clang-uml 2 | 3 | param ($Prefix="C:\llvm20", $BuildType="Release") 4 | 5 | mkdir _BUILD 6 | 7 | cmake -S .. -B .\_BUILD\windows\ -DBOOST_STATIC=ON -DCMAKE_PREFIX_PATH="$Prefix" -Thost=x64 8 | 9 | cd .\_BUILD\windows 10 | 11 | msbuild .\clang-include-graph.vcxproj -maxcpucount /p:Configuration=Release 12 | 13 | cpack -C "Release" -G NSIS64 14 | 15 | cd .. 16 | cd .. -------------------------------------------------------------------------------- /src/compilation_database.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * src/compilation_database.cc 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #include "compilation_database.h" 20 | #include "glob/glob.hpp" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | namespace clang_include_graph { 28 | 29 | std::set get_all_files(CXCompilationDatabase database) 30 | { 31 | std::set result; 32 | 33 | auto *compile_commands = 34 | clang_CompilationDatabase_getAllCompileCommands(database); 35 | 36 | auto compile_commands_size = 37 | clang_CompileCommands_getSize(compile_commands); 38 | 39 | for (auto i = 0U; i < compile_commands_size; i++) { 40 | auto *command = clang_CompileCommands_getCommand(compile_commands, i); 41 | 42 | auto canonical_file = get_canonical_file(command); 43 | 44 | LOG(trace) << "Found file " << canonical_file.string() 45 | << " in compilation database\n"; 46 | 47 | result.emplace(std::move(canonical_file)); 48 | } 49 | 50 | return result; 51 | } 52 | 53 | void intersect_glob_matches_with_compilation_database(void *database, 54 | const bool is_fixed, 55 | const std::set 56 | &compilation_database_files_absolute, 57 | std::vector &matching_compile_commands, 58 | std::set &glob_files_absolute) 59 | { 60 | std::vector matching_files; 61 | 62 | for (const auto &gm : glob_files_absolute) { 63 | assert(gm.is_absolute()); 64 | 65 | auto preferred_path = gm; 66 | preferred_path.make_preferred(); 67 | 68 | if (is_fixed || 69 | boost::algorithm::any_of_equal( 70 | compilation_database_files_absolute, gm) || 71 | boost::algorithm::any_of_equal( 72 | compilation_database_files_absolute, preferred_path)) { 73 | matching_files.emplace_back(gm.string()); 74 | 75 | LOG(trace) << "Found matching compilation database file: " 76 | << gm.string(); 77 | } 78 | } 79 | 80 | for (const auto &file : matching_files) { 81 | matching_compile_commands.emplace_back( 82 | clang_CompilationDatabase_getCompileCommands( 83 | database, file.c_str())); 84 | } 85 | } 86 | 87 | void filter_blacklist_glob_patterns( 88 | const std::vector &translation_unit_patterns, 89 | std::set &glob_files_absolute) 90 | { 91 | for (const auto &glob : translation_unit_patterns) { 92 | if (glob.string().size() < 2 || glob.string()[0] != '!') 93 | continue; 94 | 95 | const boost::filesystem::path absolute_glob_path{ 96 | glob.string().substr(1)}; 97 | auto matches = glob::glob(absolute_glob_path.string(), true, false); 98 | 99 | for (const auto &match : matches) { 100 | const auto path = boost::filesystem::weakly_canonical(match); 101 | 102 | assert(path.is_absolute()); 103 | 104 | LOG(trace) << "Removing match " << path.string() 105 | << " based on glob " << glob.string(); 106 | 107 | glob_files_absolute.erase(path); 108 | } 109 | } 110 | } 111 | 112 | void resolve_whitelist_glob_patterns( 113 | const std::vector &translation_unit_patterns, 114 | std::set &glob_files_absolute) 115 | { 116 | for (const auto &glob : translation_unit_patterns) { 117 | if (glob.string().empty() || glob.string()[0] == '!') 118 | continue; 119 | 120 | boost::filesystem::path absolute_glob_path{glob.string()}; 121 | 122 | if (!absolute_glob_path.is_absolute()) 123 | absolute_glob_path = 124 | boost::filesystem::current_path() / absolute_glob_path; 125 | 126 | LOG(debug) << "Searching glob path " << absolute_glob_path.string() 127 | << " [" << glob.string() << "]"; 128 | 129 | auto matches = glob::glob(absolute_glob_path.string(), true, false); 130 | 131 | LOG(debug) << "Found " << matches.size() 132 | << " files matching glob: " << absolute_glob_path.string(); 133 | 134 | for (const auto &match : matches) { 135 | const auto path = boost::filesystem::weakly_canonical(match); 136 | 137 | assert(path.is_absolute()); 138 | 139 | glob_files_absolute.emplace(path); 140 | } 141 | } 142 | } 143 | 144 | boost::filesystem::path get_canonical_file(CXCompileCommand command) 145 | { 146 | boost::filesystem::path file{ 147 | clang_getCString(clang_CompileCommand_getFilename(command))}; 148 | 149 | if (file.is_absolute()) 150 | return file; 151 | 152 | const boost::filesystem::path directory{ 153 | clang_getCString(clang_CompileCommand_getDirectory(command))}; 154 | 155 | return weakly_canonical(directory / file); 156 | } 157 | } // namespace clang_include_graph -------------------------------------------------------------------------------- /src/compilation_database.h: -------------------------------------------------------------------------------- 1 | /** 2 | * src/compilation_database.h 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #ifndef CLANG_INCLUDE_GRAPH_COMPILATION_DATABASE_H 20 | #define CLANG_INCLUDE_GRAPH_COMPILATION_DATABASE_H 21 | 22 | #include "util.h" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | 31 | namespace clang_include_graph { 32 | 33 | std::set get_all_files(CXCompilationDatabase database); 34 | 35 | void intersect_glob_matches_with_compilation_database(void *database, 36 | bool is_fixed, 37 | const std::set 38 | &compilation_database_files_absolute, 39 | std::vector &matching_compile_commands, 40 | std::set &glob_files_absolute); 41 | 42 | void resolve_whitelist_glob_patterns( 43 | const std::vector &translation_unit_patterns, 44 | std::set &glob_files_absolute); 45 | 46 | void filter_blacklist_glob_patterns( 47 | const std::vector &translation_unit_patterns, 48 | std::set &glob_files_absolute); 49 | 50 | boost::filesystem::path get_canonical_file(CXCompileCommand command); 51 | 52 | } // namespace clang_include_graph 53 | 54 | #endif // CLANG_INCLUDE_GRAPH_COMPILATION_DATABASE_H -------------------------------------------------------------------------------- /src/config.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * src/config.cc 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #include "config.h" 20 | #include "util.h" 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | 29 | namespace clang_include_graph { 30 | 31 | void config_t::init( 32 | boost::program_options::variables_map &vm, const std::string &cli_arguments) 33 | { 34 | cli_arguments_ = cli_arguments; 35 | 36 | if (vm.count("verbose") == 1) { 37 | verbosity_ = vm["verbose"].as(); 38 | } 39 | 40 | if (vm.count("log-file") == 1) { 41 | log_file_ = vm["log-file"].as(); 42 | } 43 | 44 | if (vm.count("jobs") == 1) { 45 | auto jobs_arg = vm["jobs"].as(); 46 | if (jobs_arg != 0) { 47 | jobs_ = jobs_arg; 48 | } 49 | } 50 | 51 | if (vm.count("compilation-database-dir") == 1) { 52 | compilation_database_directory_ = util::to_absolute_path( 53 | vm["compilation-database-dir"].as()); 54 | } 55 | else { 56 | compilation_database_directory_ = util::to_absolute_path("."); 57 | } 58 | 59 | if (vm.count("output") == 1) { 60 | output_file_ = vm["output"].as(); 61 | } 62 | 63 | if (!compilation_database_directory_) { 64 | std::cerr << "ERROR: Cannot find compilation database - aborting..." 65 | << '\n'; 66 | exit(-1); 67 | } 68 | 69 | if (vm.count("relative-to") == 1) { 70 | relative_to_ = 71 | util::to_absolute_path(vm["relative-to"].as()); 72 | } 73 | 74 | if (vm.count("names-only") == 1) { 75 | filenames_only_ = true; 76 | } 77 | 78 | if (vm.count("relative-only") == 1) { 79 | relative_only_ = true; 80 | } 81 | 82 | if (relative_only_ && !relative_to_) { 83 | relative_to_ = util::to_absolute_path("."); 84 | } 85 | 86 | if (relative_to_ && filenames_only_) { 87 | std::cerr << "ERROR: --relative-to and --names-only cannot be enabled " 88 | "at the same time" 89 | << " - aborting..." << '\n'; 90 | exit(-1); 91 | } 92 | 93 | if (vm.count("translation-unit") == 1) { 94 | translation_unit_ = 95 | vm["translation-unit"].as>(); 96 | } 97 | 98 | if (vm.count("add-compile-flag") == 1) { 99 | add_compile_flag_ = 100 | vm["add-compile-flag"].as>(); 101 | } 102 | 103 | if (vm.count("remove-compile-flag") == 1) { 104 | remove_compile_flag_ = 105 | vm["remove-compile-flag"].as>(); 106 | } 107 | 108 | if (vm.count("tree") + vm.count("reverse-tree") + vm.count("cycles") + 109 | vm.count("graphviz") + vm.count("topological-sort") + 110 | vm.count("plantuml") > 111 | 1) { 112 | std::cerr << "ERROR: Only one output method can be selected at a time" 113 | << " - aborting..." << '\n'; 114 | exit(-1); 115 | } 116 | 117 | if (vm.count("dependants-of") > 0) { 118 | printer_ = printer_t::dependants; 119 | dependants_of_ = resolve_path(vm["dependants-of"].as()); 120 | } 121 | 122 | if (vm.count("translation-units-only") == 1) { 123 | translation_units_only_ = true; 124 | } 125 | 126 | if (vm.count("exclude-system-headers") == 1) { 127 | exclude_system_headers_ = true; 128 | } 129 | 130 | if (vm.count("title") == 1) { 131 | title_ = vm["title"].as(); 132 | } 133 | 134 | if (vm.count("json-numeric-ids") == 1) { 135 | json_printer_opts_.numeric_ids = true; 136 | } 137 | 138 | if (vm.count("tree") > 0U) { 139 | printer_ = printer_t::tree; 140 | } 141 | else if (vm.count("reverse-tree") > 0U) { 142 | printer_ = printer_t::reverse_tree; 143 | } 144 | else if (vm.count("graphviz") > 0U) { 145 | printer_ = printer_t::graphviz; 146 | } 147 | else if (vm.count("graphml") > 0U) { 148 | printer_ = printer_t::graphml; 149 | } 150 | else if (vm.count("topological-sort") > 0U) { 151 | printer_ = printer_t::topological_sort; 152 | } 153 | else if (vm.count("cycles") > 0U) { 154 | printer_ = printer_t::cycles; 155 | } 156 | else if (vm.count("plantuml") > 0U) { 157 | printer_ = printer_t::plantuml; 158 | } 159 | else if (vm.count("json") > 0U) { 160 | printer_ = printer_t::json; 161 | } 162 | } 163 | 164 | int config_t::verbosity() const noexcept { return verbosity_; } 165 | 166 | const boost::optional & 167 | config_t::log_file() const noexcept 168 | { 169 | return log_file_; 170 | } 171 | 172 | const boost::optional & 173 | config_t::compilation_database_directory() const noexcept 174 | { 175 | return compilation_database_directory_; 176 | } 177 | 178 | const boost::optional & 179 | config_t::output_file() const noexcept 180 | { 181 | return output_file_; 182 | } 183 | 184 | const std::vector & 185 | config_t::translation_unit() const noexcept 186 | { 187 | return translation_unit_; 188 | } 189 | 190 | const boost::optional & 191 | config_t::relative_to() const noexcept 192 | { 193 | return relative_to_; 194 | } 195 | 196 | void config_t::relative_to(std::string rt) { relative_to_ = std::move(rt); }; 197 | 198 | const boost::optional & 199 | config_t::dependants_of() const noexcept 200 | { 201 | return dependants_of_; 202 | } 203 | 204 | void config_t::dependants_of(std::string file) 205 | { 206 | dependants_of_ = std::move(file); 207 | } 208 | 209 | bool config_t::filenames_only() const noexcept { return filenames_only_; }; 210 | 211 | bool config_t::relative_only() const noexcept { return relative_only_; } 212 | 213 | void config_t::relative_only(bool ro) noexcept { relative_only_ = ro; } 214 | 215 | bool config_t::translation_units_only() const noexcept 216 | { 217 | return translation_units_only_; 218 | } 219 | 220 | void config_t::translation_units_only(bool tuo) noexcept 221 | { 222 | translation_units_only_ = tuo; 223 | } 224 | 225 | bool config_t::exclude_system_headers() const noexcept 226 | { 227 | return exclude_system_headers_; 228 | } 229 | 230 | void config_t::exclude_system_headers(bool esh) noexcept 231 | { 232 | exclude_system_headers_ = esh; 233 | } 234 | 235 | const boost::optional &config_t::title() const noexcept 236 | { 237 | return title_; 238 | } 239 | 240 | printer_t config_t::printer() const noexcept { return printer_; } 241 | 242 | void config_t::printer(printer_t printer) noexcept { printer_ = printer; } 243 | 244 | unsigned config_t::jobs() const noexcept { return jobs_; } 245 | 246 | void config_t::jobs(unsigned j) noexcept { jobs_ = j; } 247 | 248 | const std::vector &config_t::add_compile_flag() const noexcept 249 | { 250 | return add_compile_flag_; 251 | } 252 | 253 | const std::vector &config_t::remove_compile_flag() const noexcept 254 | { 255 | return remove_compile_flag_; 256 | } 257 | 258 | const std::string &config_t::cli_arguments() const noexcept 259 | { 260 | return cli_arguments_; 261 | } 262 | 263 | const json_printer_opts_t &config_t::json_printer_opts() const noexcept 264 | { 265 | return json_printer_opts_; 266 | } 267 | 268 | json_printer_opts_t &config_t::json_printer_opts() noexcept 269 | { 270 | return json_printer_opts_; 271 | } 272 | 273 | boost::filesystem::path config_t::resolve_path( 274 | const boost::filesystem::path &p) const 275 | { 276 | auto result{p}; 277 | if (p.is_absolute()) 278 | return p; 279 | 280 | if (relative_to_) 281 | result = boost::filesystem::path{*relative_to_} / p; 282 | 283 | if (result.is_relative()) 284 | result = util::to_absolute_path(result); 285 | 286 | return result; 287 | } 288 | } // namespace clang_include_graph 289 | -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | /** 2 | * src/config.h 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #ifndef CLANG_INCLUDE_GRAPH_CONFIG_H 20 | #define CLANG_INCLUDE_GRAPH_CONFIG_H 21 | 22 | #include "util.h" 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | 30 | namespace clang_include_graph { 31 | 32 | enum class printer_t : std::uint8_t { 33 | topological_sort, 34 | tree, 35 | reverse_tree, 36 | cycles, 37 | dependants, 38 | graphviz, 39 | graphml, 40 | plantuml, 41 | json, 42 | unknown 43 | }; 44 | 45 | struct json_printer_opts_t { 46 | bool numeric_ids{false}; 47 | }; 48 | 49 | class config_t { 50 | public: 51 | void init(boost::program_options::variables_map &vm, 52 | const std::string &cli_arguments); 53 | 54 | int verbosity() const noexcept; 55 | 56 | const boost::optional &log_file() const noexcept; 57 | 58 | const boost::optional & 59 | compilation_database_directory() const noexcept; 60 | 61 | const boost::optional & 62 | output_file() const noexcept; 63 | 64 | const std::vector & 65 | translation_unit() const noexcept; 66 | 67 | const boost::optional & 68 | relative_to() const noexcept; 69 | void relative_to(std::string rt); 70 | 71 | const boost::optional & 72 | dependants_of() const noexcept; 73 | void dependants_of(std::string file); 74 | 75 | bool filenames_only() const noexcept; 76 | 77 | bool relative_only() const noexcept; 78 | void relative_only(bool ro) noexcept; 79 | 80 | bool translation_units_only() const noexcept; 81 | void translation_units_only(bool tuo) noexcept; 82 | 83 | bool exclude_system_headers() const noexcept; 84 | void exclude_system_headers(bool esh) noexcept; 85 | 86 | const boost::optional &title() const noexcept; 87 | 88 | printer_t printer() const noexcept; 89 | void printer(printer_t printer) noexcept; 90 | 91 | unsigned jobs() const noexcept; 92 | void jobs(unsigned j) noexcept; 93 | 94 | const std::vector &add_compile_flag() const noexcept; 95 | 96 | const std::vector &remove_compile_flag() const noexcept; 97 | 98 | const std::string &cli_arguments() const noexcept; 99 | 100 | boost::filesystem::path resolve_path( 101 | const boost::filesystem::path &p) const; 102 | 103 | const json_printer_opts_t &json_printer_opts() const noexcept; 104 | 105 | json_printer_opts_t &json_printer_opts() noexcept; 106 | 107 | private: 108 | int verbosity_{0}; 109 | boost::optional log_file_; 110 | boost::optional compilation_database_directory_; 111 | boost::optional output_file_; 112 | std::vector translation_unit_; 113 | boost::optional relative_to_; 114 | boost::optional dependants_of_; 115 | bool filenames_only_{false}; 116 | bool relative_only_{false}; 117 | bool translation_units_only_{false}; 118 | bool exclude_system_headers_{false}; 119 | json_printer_opts_t json_printer_opts_; 120 | printer_t printer_{printer_t::topological_sort}; 121 | unsigned jobs_{std::thread::hardware_concurrency()}; 122 | std::vector add_compile_flag_; 123 | std::vector remove_compile_flag_; 124 | std::string cli_arguments_; 125 | boost::optional title_; 126 | }; 127 | 128 | } // namespace clang_include_graph 129 | 130 | #endif // CLANG_INCLUDE_GRAPH_CONFIG_H 131 | -------------------------------------------------------------------------------- /src/include_graph.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * src/include_graph.cc 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #include "include_graph.h" 20 | 21 | #include "config.h" 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | 30 | namespace clang_include_graph { 31 | 32 | void include_graph_t::add_edge(const std::string &to, const std::string &from, 33 | bool from_translation_unit, bool is_system) 34 | { 35 | add_edge(to, from, to, from_translation_unit, is_system); 36 | } 37 | 38 | void include_graph_t::add_edge(const std::string &to, const std::string &from, 39 | const std::string &include_spelling, bool from_translation_unit, 40 | bool is_system) 41 | { 42 | const std::lock_guard guard{mutex_}; 43 | 44 | if (graph_.vertex(to) == graph_t::null_vertex()) { 45 | LOG(trace) << "Adding target vertex " << to 46 | << " [is_system_header=" << is_system 47 | << ", include_spelling=" << include_spelling << "]"; 48 | 49 | const auto &to_v = boost::add_vertex(to, graph_); 50 | graph_.graph()[to_v].file = to; 51 | graph_.graph()[to_v].is_system_header = is_system; 52 | graph_.graph()[to_v].include_spelling = include_spelling; 53 | } 54 | 55 | if (graph_.vertex(from) == graph_t::null_vertex()) { 56 | LOG(trace) << "Adding source vertex " << from 57 | << " [is_system_header=" << is_system 58 | << ", include_spelling=" << include_spelling << "]"; 59 | 60 | const auto &from_v = boost::add_vertex(from, graph_); 61 | graph_.graph()[from_v].file = from; 62 | graph_.graph()[from_v].is_translation_unit = from_translation_unit; 63 | } 64 | 65 | std::pair edge_pair; 66 | if (printer_ == printer_t::reverse_tree || 67 | printer_ == printer_t::dependants) { 68 | edge_pair = boost::add_edge_by_label(to, from, graph_); 69 | } 70 | else { 71 | edge_pair = boost::add_edge_by_label(from, to, graph_); 72 | } 73 | 74 | if (edge_pair.second) { 75 | graph_.graph()[edge_pair.first].is_system = is_system; 76 | } 77 | } 78 | 79 | void include_graph_t::init(const config_t &config) 80 | { 81 | relative_to_ = config.relative_to(); 82 | relative_only_ = config.relative_only(); 83 | dependants_of_ = config.dependants_of(); 84 | translation_units_only_ = config.translation_units_only(); 85 | printer_ = config.printer(); 86 | cli_arguments_ = config.cli_arguments(); 87 | numeric_ids_ = config.json_printer_opts().numeric_ids; 88 | title_ = config.title(); 89 | exclude_system_headers_ = config.exclude_system_headers(); 90 | } 91 | 92 | void include_graph_t::build_dag() 93 | { 94 | const std::lock_guard guard{mutex_}; 95 | 96 | if (dag_) { 97 | return; 98 | } 99 | 100 | dag_ = include_graph_t::graph_t{}; 101 | const detail::dag_include_graph_visitor_t visitor{*this}; 102 | 103 | boost::depth_first_search(graph_, boost::visitor(visitor)); 104 | } 105 | 106 | const boost::optional & 107 | include_graph_t::dag() const noexcept 108 | { 109 | return dag_; 110 | } 111 | 112 | boost::optional &include_graph_t::dag() noexcept 113 | { 114 | return dag_; 115 | } 116 | 117 | const include_graph_t::graph_t &include_graph_t::graph() const noexcept 118 | { 119 | return graph_; 120 | } 121 | 122 | bool include_graph_t::relative_only() const noexcept { return relative_only_; } 123 | 124 | bool include_graph_t::translation_units_only() const noexcept 125 | { 126 | return translation_units_only_; 127 | } 128 | 129 | bool include_graph_t::exclude_system_headers() const noexcept 130 | { 131 | return exclude_system_headers_; 132 | } 133 | 134 | printer_t include_graph_t::printer() const noexcept { return printer_; } 135 | 136 | const boost::optional & 137 | include_graph_t::relative_to() const noexcept 138 | { 139 | return relative_to_; 140 | } 141 | 142 | const boost::optional & 143 | include_graph_t::dependants_of() const noexcept 144 | { 145 | return dependants_of_; 146 | } 147 | 148 | const std::string &include_graph_t::cli_arguments() const noexcept 149 | { 150 | return cli_arguments_; 151 | } 152 | 153 | bool include_graph_t::numeric_ids() const noexcept { return numeric_ids_; } 154 | 155 | const boost::optional &include_graph_t::title() const noexcept 156 | { 157 | return title_; 158 | } 159 | } // namespace clang_include_graph 160 | -------------------------------------------------------------------------------- /src/include_graph.h: -------------------------------------------------------------------------------- 1 | /** 2 | * src/include_graph.h 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #ifndef CLANG_INCLUDE_GRAPH_INCLUDE_GRAPH_H 20 | #define CLANG_INCLUDE_GRAPH_INCLUDE_GRAPH_H 21 | 22 | #include "config.h" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | #include 32 | 33 | namespace clang_include_graph { 34 | 35 | class include_graph_t { 36 | public: 37 | struct vertex_t { 38 | std::string file; 39 | std::string include_spelling; 40 | bool is_system_header{false}; 41 | bool is_translation_unit{false}; 42 | }; 43 | 44 | struct edge_t { 45 | bool is_system{false}; 46 | }; 47 | 48 | using graph_adjlist_t = boost::adjacency_list; 50 | using graph_t = 51 | boost::labeled_graph; 52 | 53 | void add_edge(const std::string &to, const std::string &from, 54 | bool from_translation_unit = false, bool is_system = false); 55 | 56 | void add_edge(const std::string &to, const std::string &from, 57 | const std::string &include_spelling, bool from_translation_unit = false, 58 | bool is_system = false); 59 | 60 | void init(const config_t &config); 61 | 62 | void build_dag(); 63 | 64 | const boost::optional &dag() const noexcept; 65 | 66 | boost::optional &dag() noexcept; 67 | 68 | const graph_t &graph() const noexcept; 69 | 70 | bool relative_only() const noexcept; 71 | 72 | bool translation_units_only() const noexcept; 73 | 74 | bool exclude_system_headers() const noexcept; 75 | 76 | printer_t printer() const noexcept; 77 | 78 | const boost::optional & 79 | relative_to() const noexcept; 80 | 81 | const boost::optional & 82 | dependants_of() const noexcept; 83 | 84 | const std::string &cli_arguments() const noexcept; 85 | 86 | bool numeric_ids() const noexcept; 87 | 88 | const boost::optional &title() const noexcept; 89 | 90 | private: 91 | graph_t graph_; 92 | boost::optional dag_; 93 | boost::optional relative_to_; 94 | bool relative_only_{false}; 95 | boost::optional dependants_of_; 96 | bool translation_units_only_{false}; 97 | bool exclude_system_headers_{false}; 98 | std::string cli_arguments_; 99 | bool numeric_ids_{false}; 100 | boost::optional title_; 101 | 102 | printer_t printer_{printer_t::unknown}; 103 | 104 | std::mutex mutex_; 105 | }; 106 | 107 | namespace detail { 108 | class dag_include_graph_visitor_t : public boost::default_dfs_visitor { 109 | public: 110 | explicit dag_include_graph_visitor_t(include_graph_t &graph) 111 | : graph_{graph} 112 | , dag_{graph_.dag().value()} 113 | { 114 | } 115 | 116 | template 117 | void tree_edge(E edge, const G &graph) const 118 | { 119 | boost::add_edge( 120 | boost::source(edge, graph), boost::target(edge, graph), dag_); 121 | } 122 | 123 | template 124 | void forward_or_cross_edge(E edge, const G &graph) const 125 | { 126 | boost::add_edge( 127 | boost::source(edge, graph), boost::target(edge, graph), dag_); 128 | } 129 | 130 | template 131 | void back_edge(E /*edge*/, const G & /*graph*/) const 132 | { 133 | // skip 134 | } 135 | 136 | private: 137 | include_graph_t &graph_; 138 | include_graph_t::graph_t &dag_; 139 | }; 140 | 141 | } // namespace detail 142 | } // namespace clang_include_graph 143 | 144 | #endif // CLANG_INCLUDE_GRAPH_INCLUDE_GRAPH_H 145 | -------------------------------------------------------------------------------- /src/include_graph_cycles_printer.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * src/include_graph_cycles_printer.cc 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #include "include_graph_cycles_printer.h" 20 | #include "include_graph.h" 21 | #include "path_printer.h" 22 | 23 | #include 24 | 25 | #include 26 | 27 | namespace boost { 28 | void renumber_vertex_indices( 29 | clang_include_graph::include_graph_t::graph_adjlist_t const & /*unused*/) 30 | { 31 | } 32 | } // namespace boost 33 | 34 | namespace clang_include_graph { 35 | namespace detail { 36 | 37 | cycle_printer_t::cycle_printer_t(const include_graph_t::graph_t &graph, 38 | const path_printer_t &pp, std::ostream &stream) 39 | : graph_{graph} 40 | , path_printer_{pp} 41 | , os{stream} 42 | { 43 | } 44 | 45 | template 46 | void cycle_printer_t::cycle(const Path &p, const Graph & /*g*/) 47 | { 48 | os << "[\n"; 49 | for (auto it = p.begin(); it != p.end(); ++it) { 50 | os << " " << path_printer_.print(graph_.graph()[*it]) << "\n"; 51 | } 52 | os << "]\n"; 53 | } 54 | 55 | } // namespace detail 56 | 57 | void include_graph_cycles_printer_t::operator()(std::ostream &os) const 58 | { 59 | const detail::cycle_printer_t cycle_printer_visitor{ 60 | include_graph().graph(), path_printer(), os}; 61 | 62 | boost::tiernan_all_cycles( 63 | include_graph().graph().graph(), cycle_printer_visitor); 64 | } 65 | 66 | } // namespace clang_include_graph 67 | -------------------------------------------------------------------------------- /src/include_graph_cycles_printer.h: -------------------------------------------------------------------------------- 1 | /** 2 | * src/include_graph_cycles_printer.h 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #ifndef CLANG_INCLUDE_GRAPH_INCLUDE_GRAPH_CYCLES_PRINTER_H 20 | #define CLANG_INCLUDE_GRAPH_INCLUDE_GRAPH_CYCLES_PRINTER_H 21 | 22 | #include "include_graph_printer.h" 23 | 24 | #include 25 | 26 | #include 27 | 28 | namespace clang_include_graph { 29 | 30 | namespace detail { 31 | 32 | struct cycle_printer_t { 33 | cycle_printer_t(const include_graph_t::graph_t &graph, 34 | const path_printer_t &pp, std::ostream &stream); 35 | 36 | template 37 | void cycle(const Path &p, const Graph & /*g*/); 38 | 39 | private: 40 | const include_graph_t::graph_t &graph_; 41 | const path_printer_t &path_printer_; 42 | std::ostream &os; 43 | }; 44 | 45 | } // namespace detail 46 | 47 | class include_graph_cycles_printer_t : public include_graph_printer_t { 48 | public: 49 | using include_graph_printer_t::include_graph_printer_t; 50 | 51 | ~include_graph_cycles_printer_t() override = default; 52 | 53 | include_graph_cycles_printer_t( 54 | const include_graph_cycles_printer_t &) = default; 55 | include_graph_cycles_printer_t(include_graph_cycles_printer_t &&) = default; 56 | include_graph_cycles_printer_t &operator=( 57 | const include_graph_cycles_printer_t &) = delete; 58 | include_graph_cycles_printer_t &operator=( 59 | include_graph_cycles_printer_t &&) = delete; 60 | 61 | void operator()(std::ostream &os) const override; 62 | }; 63 | 64 | } // namespace clang_include_graph 65 | 66 | #endif // CLANG_INCLUDE_GRAPH_INCLUDE_GRAPH_CYCLES_PRINTER_H -------------------------------------------------------------------------------- /src/include_graph_dependants_printer.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * src/include_graph_dependants_printer.cc 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #include "include_graph_dependants_printer.h" 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | namespace clang_include_graph { 31 | 32 | include_graph_dependants_printer_t::include_graph_dependants_printer_t( 33 | const include_graph_t &graph, const path_printer_t &pp) 34 | : include_graph_printer_t{graph, pp} 35 | { 36 | } 37 | 38 | void include_graph_dependants_printer_t::operator()(std::ostream &os) const 39 | { 40 | assert(include_graph().dag()); 41 | 42 | const auto &dag = include_graph().dag().value().graph(); 43 | const auto &graph = include_graph().graph().graph(); 44 | 45 | const auto &dependants_root = include_graph().dependants_of().value(); 46 | 47 | auto vertices_range = boost::vertices(dag); 48 | auto it = std::find_if(vertices_range.first, vertices_range.second, 49 | [&](include_graph_t::graph_t::vertex_descriptor v) { 50 | return graph[v].file == dependants_root; 51 | }); 52 | 53 | if (it == vertices_range.second) { 54 | LOG(error) << "ERROR: Dependants root '" << dependants_root 55 | << "' not found.\n"; 56 | return; 57 | } 58 | 59 | auto start = *it; 60 | 61 | std::set dependants; 62 | 63 | std::set current_out_set; 64 | current_out_set.emplace(start); 65 | 66 | // Perform breadth first search over all outgoing edges starting from 67 | // vertex representing root of dependants tree 68 | while (!current_out_set.empty()) { 69 | std::set next_out_set; 70 | 71 | for (const auto &itt : current_out_set) { 72 | for (const auto &it : graph.out_edge_list(itt)) { 73 | if (dependants.count(it.get_target()) == 0) { 74 | next_out_set.emplace(it.get_target()); 75 | } 76 | } 77 | } 78 | 79 | dependants.insert(current_out_set.begin(), current_out_set.end()); 80 | 81 | std::swap(next_out_set, current_out_set); 82 | } 83 | 84 | for (const auto &v : dependants) { 85 | if (v == start) { 86 | continue; 87 | } 88 | 89 | if (!include_graph().translation_units_only() || 90 | graph[v].is_translation_unit) { 91 | auto dependant_path = path_printer().print(graph[v]); 92 | os << dependant_path << '\n'; 93 | } 94 | } 95 | } 96 | 97 | } // namespace clang_include_graph 98 | -------------------------------------------------------------------------------- /src/include_graph_dependants_printer.h: -------------------------------------------------------------------------------- 1 | /** 2 | * src/include_graph_dependants_printer.h 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #ifndef CLANG_INCLUDE_GRAPH_INCLUDE_GRAPH_DEPENDANTS_PRINTER_H 20 | #define CLANG_INCLUDE_GRAPH_INCLUDE_GRAPH_DEPENDANTS_PRINTER_H 21 | 22 | #include "include_graph_printer.h" 23 | 24 | #include 25 | 26 | namespace clang_include_graph { 27 | 28 | class include_graph_dependants_printer_t : public include_graph_printer_t { 29 | public: 30 | include_graph_dependants_printer_t( 31 | const include_graph_t &graph, const path_printer_t &pp); 32 | 33 | ~include_graph_dependants_printer_t() override = default; 34 | 35 | include_graph_dependants_printer_t( 36 | const include_graph_dependants_printer_t &) = default; 37 | include_graph_dependants_printer_t( 38 | include_graph_dependants_printer_t &&) = default; 39 | include_graph_dependants_printer_t &operator=( 40 | const include_graph_dependants_printer_t &) = delete; 41 | include_graph_dependants_printer_t &operator=( 42 | include_graph_dependants_printer_t &&) = delete; 43 | 44 | void operator()(std::ostream &os) const override; 45 | }; 46 | 47 | } // namespace clang_include_graph 48 | 49 | #endif // CLANG_INCLUDE_GRAPH_INCLUDE_GRAPH_DEPENDANTS_PRINTER_H 50 | -------------------------------------------------------------------------------- /src/include_graph_graphml_printer.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * src/include_graph_graphml_printer.cc 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #include "include_graph_graphml_printer.h" 20 | #include "include_graph.h" 21 | #include "path_printer.h" 22 | 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | namespace clang_include_graph { 29 | 30 | void include_graph_graphml_printer_t::operator()(std::ostream &os) const 31 | { 32 | const auto &printer = path_printer(); 33 | 34 | boost::dynamic_properties dp; 35 | 36 | #if !defined(_MSC_VER) 37 | // TODO: figure out how to compile this on MSVC 38 | if (include_graph().title()) { 39 | auto title_map = 40 | boost::make_constant_property(*include_graph().title()); 42 | dp.property("title", title_map); 43 | } 44 | #endif 45 | 46 | auto file_map = boost::make_transform_value_property_map( 47 | [&printer]( 48 | const include_graph_t::vertex_t &v) { return printer.print(v); }, 49 | get(boost::vertex_bundle, include_graph().graph())); 50 | 51 | dp.property("file", file_map); 52 | 53 | auto is_system_map = boost::make_transform_value_property_map( 54 | [](const bool is_system) { return is_system; }, 55 | get(&include_graph_t::edge_t::is_system, include_graph().graph())); 56 | 57 | dp.property("is_system", is_system_map); 58 | 59 | boost::write_graphml(os, include_graph().graph(), dp, true); 60 | } 61 | 62 | } // namespace clang_include_graph 63 | -------------------------------------------------------------------------------- /src/include_graph_graphml_printer.h: -------------------------------------------------------------------------------- 1 | /** 2 | * src/include_graph_graphml_printer.h 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #ifndef CLANG_INCLUDE_GRAPH_INCLUDE_GRAPH_GRAPHML_PRINTER_H 20 | #define CLANG_INCLUDE_GRAPH_INCLUDE_GRAPH_GRAPHML_PRINTER_H 21 | 22 | #include "include_graph_printer.h" 23 | 24 | #include 25 | 26 | namespace clang_include_graph { 27 | 28 | class include_graph_graphml_printer_t : public include_graph_printer_t { 29 | public: 30 | using include_graph_printer_t::include_graph_printer_t; 31 | 32 | ~include_graph_graphml_printer_t() override = default; 33 | 34 | include_graph_graphml_printer_t( 35 | const include_graph_graphml_printer_t &) = default; 36 | include_graph_graphml_printer_t( 37 | include_graph_graphml_printer_t &&) = default; 38 | include_graph_graphml_printer_t &operator=( 39 | const include_graph_graphml_printer_t &) = delete; 40 | include_graph_graphml_printer_t &operator=( 41 | include_graph_graphml_printer_t &&) = delete; 42 | 43 | void operator()(std::ostream &os) const override; 44 | }; 45 | 46 | } // namespace clang_include_graph 47 | 48 | #endif // CLANG_INCLUDE_GRAPH_INCLUDE_GRAPH_GRAPHML_PRINTER_H -------------------------------------------------------------------------------- /src/include_graph_graphviz_printer.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * src/include_graph_graphviz_printer.cc 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #include "include_graph_graphviz_printer.h" 20 | #include "include_graph.h" 21 | #include "path_printer.h" 22 | 23 | #include 24 | 25 | #include 26 | 27 | namespace clang_include_graph { 28 | 29 | namespace detail { 30 | 31 | label_writer::label_writer( 32 | const include_graph_t::graph_t &graph, const path_printer_t &pp) 33 | : graph_{graph} 34 | , path_printer_{pp} 35 | { 36 | } 37 | template 38 | void label_writer::operator()(std::ostream &out, const Vertex &v) const 39 | { 40 | out << "[label=\"" << path_printer_.print(graph_.graph()[v]) << "\"]"; 41 | } 42 | 43 | } // namespace detail 44 | 45 | void include_graph_graphviz_printer_t::operator()(std::ostream &os) const 46 | { 47 | const detail::label_writer writer{include_graph().graph(), path_printer()}; 48 | 49 | boost::write_graphviz(os, include_graph().graph(), writer); 50 | } 51 | 52 | } // namespace clang_include_graph 53 | -------------------------------------------------------------------------------- /src/include_graph_graphviz_printer.h: -------------------------------------------------------------------------------- 1 | /** 2 | * src/include_graph_graphviz_printer.h 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #ifndef CLANG_INCLUDE_GRAPH_INCLUDE_GRAPH_GRAPHVIZ_PRINTER_H 20 | #define CLANG_INCLUDE_GRAPH_INCLUDE_GRAPH_GRAPHVIZ_PRINTER_H 21 | 22 | #include "include_graph_printer.h" 23 | 24 | #include 25 | 26 | #include 27 | 28 | namespace clang_include_graph { 29 | 30 | namespace detail { 31 | 32 | class label_writer { 33 | public: 34 | label_writer( 35 | const include_graph_t::graph_t &graph, const path_printer_t &pp); 36 | 37 | template 38 | void operator()(std::ostream &out, const Vertex &v) const; 39 | 40 | private: 41 | const include_graph_t::graph_t &graph_; 42 | const path_printer_t &path_printer_; 43 | }; 44 | 45 | } // namespace detail 46 | 47 | class include_graph_graphviz_printer_t : public include_graph_printer_t { 48 | public: 49 | using include_graph_printer_t::include_graph_printer_t; 50 | 51 | ~include_graph_graphviz_printer_t() override = default; 52 | 53 | include_graph_graphviz_printer_t( 54 | const include_graph_graphviz_printer_t &) = default; 55 | include_graph_graphviz_printer_t( 56 | include_graph_graphviz_printer_t &&) = default; 57 | include_graph_graphviz_printer_t &operator=( 58 | const include_graph_graphviz_printer_t &) = delete; 59 | include_graph_graphviz_printer_t &operator=( 60 | include_graph_graphviz_printer_t &&) = delete; 61 | 62 | void operator()(std::ostream &os) const override; 63 | }; 64 | 65 | } // namespace clang_include_graph 66 | 67 | #endif // CLANG_INCLUDE_GRAPH_INCLUDE_GRAPH_GRAPHVIZ_PRINTER_H -------------------------------------------------------------------------------- /src/include_graph_json_printer.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * src/include_graph_json_printer.cc 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #include "include_graph_json_printer.h" 20 | 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | namespace clang_include_graph { 31 | 32 | include_graph_json_printer_t::include_graph_json_printer_t( 33 | const include_graph_t &graph, const path_printer_t &pp) 34 | : include_graph_printer_t{graph, pp} 35 | { 36 | } 37 | 38 | void include_graph_json_printer_t::operator()(std::ostream &os) const 39 | { 40 | assert(include_graph().dag()); 41 | 42 | auto begin = boost::vertices(include_graph().dag().value().graph()).first; 43 | auto end = boost::vertices(include_graph().dag().value().graph()).second; 44 | 45 | boost::json::object g; 46 | g["directed"] = true; 47 | g["type"] = "include_graph"; 48 | 49 | if (include_graph().title()) 50 | g["label"] = *include_graph().title(); 51 | 52 | boost::json::object meta; 53 | meta["cli_arguments"] = include_graph().cli_arguments(); 54 | g["metadata"] = std::move(meta); 55 | 56 | boost::json::object nodes; 57 | 58 | std::for_each( 59 | begin, end, [&](const include_graph_t::graph_t::vertex_descriptor &v) { 60 | const auto &graph = include_graph().graph().graph(); 61 | const auto &vertex = graph[v]; 62 | 63 | const auto file_path = path_printer().print(vertex); 64 | boost::json::object node; 65 | node["label"] = file_path; 66 | boost::json::object metadata; 67 | metadata["is_system_header"] = vertex.is_system_header; 68 | metadata["is_translation_unit"] = vertex.is_translation_unit; 69 | node["metadata"] = std::move(metadata); 70 | 71 | if (include_graph().numeric_ids()) 72 | nodes[std::to_string(v)] = std::move(node); 73 | else 74 | nodes[file_path] = std::move(node); 75 | }); 76 | 77 | g["nodes"] = std::move(nodes); 78 | 79 | boost::json::array edges; 80 | 81 | auto edges_begin = boost::edges(include_graph().graph()).first; 82 | auto edges_end = boost::edges(include_graph().graph()).second; 83 | 84 | std::for_each(edges_begin, edges_end, 85 | [&](const include_graph_t::graph_t::edge_descriptor &e) { 86 | const auto &graph = include_graph().graph().graph(); 87 | 88 | const include_graph_t::graph_t::vertex_descriptor &from = 89 | boost::source(e, graph); 90 | const include_graph_t::graph_t::vertex_descriptor &to = 91 | boost::target(e, graph); 92 | 93 | boost::json::object edge; 94 | if (include_graph().numeric_ids()) { 95 | edge["target"] = std::to_string(to); 96 | edge["source"] = std::to_string(from); 97 | } 98 | else { 99 | const auto &from_vertex = graph[from]; 100 | const auto from_file_path = path_printer().print(from_vertex); 101 | 102 | const auto &to_vertex = graph[to]; 103 | const auto to_file_path = path_printer().print(to_vertex); 104 | 105 | edge["target"] = to_file_path; 106 | edge["source"] = from_file_path; 107 | } 108 | 109 | edge["is_system"] = include_graph().graph()[e].is_system; 110 | 111 | edges.emplace_back(std::move(edge)); 112 | }); 113 | 114 | g["edges"] = std::move(edges); 115 | 116 | os << g; 117 | } 118 | 119 | } // namespace clang_include_graph 120 | -------------------------------------------------------------------------------- /src/include_graph_json_printer.h: -------------------------------------------------------------------------------- 1 | /** 2 | * src/include_graph_json_printer.h 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #ifndef CLANG_INCLUDE_GRAPH_INCLUDE_GRAPH_JSON_PRINTER_H 20 | #define CLANG_INCLUDE_GRAPH_INCLUDE_GRAPH_JSON_PRINTER_H 21 | 22 | #include "include_graph_printer.h" 23 | 24 | #include 25 | 26 | namespace clang_include_graph { 27 | 28 | class include_graph_json_printer_t : public include_graph_printer_t { 29 | public: 30 | include_graph_json_printer_t( 31 | const include_graph_t &graph, const path_printer_t &pp); 32 | 33 | ~include_graph_json_printer_t() override = default; 34 | 35 | include_graph_json_printer_t( 36 | const include_graph_json_printer_t &) = default; 37 | include_graph_json_printer_t(include_graph_json_printer_t &&) = default; 38 | include_graph_json_printer_t &operator=( 39 | const include_graph_json_printer_t &) = delete; 40 | include_graph_json_printer_t &operator=( 41 | include_graph_json_printer_t &&) = delete; 42 | 43 | void operator()(std::ostream &os) const override; 44 | }; 45 | 46 | } // namespace clang_include_graph 47 | 48 | #endif // CLANG_INCLUDE_GRAPH_INCLUDE_GRAPH_JSON_PRINTER_H 49 | -------------------------------------------------------------------------------- /src/include_graph_parser.h: -------------------------------------------------------------------------------- 1 | /** 2 | * src/include_graph_parser.h 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #ifndef CLANG_INCLUDE_GRAPH_INCLUDE_GRAPH_PARSER_H 20 | #define CLANG_INCLUDE_GRAPH_INCLUDE_GRAPH_PARSER_H 21 | 22 | #include "config.h" 23 | #include "include_graph.h" 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | namespace clang_include_graph { 35 | 36 | void process_translation_unit(include_graph_t &include_graph, 37 | CXCompileCommand command, const boost::filesystem::path &tu_path, 38 | std::string &include_path_str, CXIndex &index); 39 | 40 | bool is_system_header(CXTranslationUnit tu, CXCursor cursor); 41 | 42 | class include_graph_parser_t { 43 | public: 44 | explicit include_graph_parser_t(const config_t &config); 45 | 46 | ~include_graph_parser_t(); 47 | include_graph_parser_t(const include_graph_parser_t &) = delete; 48 | include_graph_parser_t(include_graph_parser_t &&) = delete; 49 | include_graph_parser_t &operator=(const include_graph_parser_t &) = delete; 50 | include_graph_parser_t &operator=(include_graph_parser_t &&) = delete; 51 | 52 | void parse(include_graph_t &include_graph); 53 | 54 | const std::set &translation_units() const; 55 | 56 | private: 57 | CXIndex index_; 58 | const config_t &config_; 59 | std::set translation_units_; 60 | }; 61 | 62 | } // namespace clang_include_graph 63 | 64 | #endif // CLANG_INCLUDE_GRAPH_INCLUDE_GRAPH_PARSER_H 65 | -------------------------------------------------------------------------------- /src/include_graph_plantuml_printer.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * src/include_graph_plantuml_printer.cc 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #include "include_graph_plantuml_printer.h" 20 | 21 | namespace clang_include_graph { 22 | 23 | include_graph_plantuml_printer_t::include_graph_plantuml_printer_t( 24 | const include_graph_t &graph, const path_printer_t &pp) 25 | : include_graph_printer_t{graph, pp} 26 | { 27 | } 28 | 29 | void include_graph_plantuml_printer_t::operator()(std::ostream &os) const 30 | { 31 | os << "@startuml" << '\n'; 32 | 33 | if (include_graph().title()) { 34 | os << "title " << *include_graph().title() << '\n'; 35 | } 36 | 37 | // First generate vertices with PlantUML aliases 38 | auto vertex_begin = boost::vertices(include_graph().graph()).first; 39 | const auto vertex_end = boost::vertices(include_graph().graph()).second; 40 | 41 | std::for_each(vertex_begin, vertex_end, 42 | [&](const include_graph_t::graph_t::vertex_descriptor &v) { 43 | const auto &vertex = include_graph().graph().graph()[v]; 44 | os << "file \"" << path_printer().print(vertex) << "\" as F_" << v 45 | << '\n'; 46 | }); 47 | 48 | // Now generate include relationships based on edge list 49 | auto edges_begin = boost::edges(include_graph().graph()).first; 50 | auto edges_end = boost::edges(include_graph().graph()).second; 51 | 52 | std::for_each(edges_begin, edges_end, 53 | [&](const include_graph_t::graph_t::edge_descriptor &e) { 54 | const include_graph_t::graph_t::vertex_descriptor &from = 55 | boost::source(e, include_graph().graph()); 56 | const include_graph_t::graph_t::vertex_descriptor &to = 57 | boost::target(e, include_graph().graph()); 58 | 59 | os << "F_" << to; 60 | 61 | if (include_graph().graph()[e].is_system) 62 | os << " <.. "; 63 | else 64 | os << " <-- "; 65 | 66 | os << " F_" << from << '\n'; 67 | }); 68 | 69 | os << "@enduml" << '\n'; 70 | } 71 | 72 | } // namespace clang_include_graph 73 | -------------------------------------------------------------------------------- /src/include_graph_plantuml_printer.h: -------------------------------------------------------------------------------- 1 | /** 2 | * src/include_graph_plantuml_printer.h 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #ifndef CLANG_INCLUDE_GRAPH_INCLUDE_GRAPH_PLANTUML_PRINTER_H 20 | #define CLANG_INCLUDE_GRAPH_INCLUDE_GRAPH_PLANTUML_PRINTER_H 21 | 22 | #include "include_graph_printer.h" 23 | 24 | #include 25 | 26 | namespace clang_include_graph { 27 | 28 | class include_graph_plantuml_printer_t : public include_graph_printer_t { 29 | public: 30 | include_graph_plantuml_printer_t( 31 | const include_graph_t &graph, const path_printer_t &pp); 32 | 33 | ~include_graph_plantuml_printer_t() override = default; 34 | 35 | include_graph_plantuml_printer_t( 36 | const include_graph_plantuml_printer_t &) = default; 37 | include_graph_plantuml_printer_t( 38 | include_graph_plantuml_printer_t &&) = default; 39 | include_graph_plantuml_printer_t &operator=( 40 | const include_graph_plantuml_printer_t &) = delete; 41 | include_graph_plantuml_printer_t &operator=( 42 | include_graph_plantuml_printer_t &&) = delete; 43 | 44 | void operator()(std::ostream &os) const override; 45 | }; 46 | 47 | } // namespace clang_include_graph 48 | 49 | #endif // CLANG_INCLUDE_GRAPH_INCLUDE_GRAPH_PLANTUML_PRINTER_H 50 | -------------------------------------------------------------------------------- /src/include_graph_printer.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * src/include_graph_printer.cc 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #include "include_graph_printer.h" 20 | 21 | #include 22 | 23 | namespace clang_include_graph { 24 | 25 | include_graph_printer_t::include_graph_printer_t( 26 | const include_graph_t &graph, const path_printer_t &pp) 27 | : graph_{graph} 28 | , path_printer_{pp} 29 | { 30 | } 31 | 32 | const path_printer_t &include_graph_printer_t::path_printer() const 33 | { 34 | return path_printer_; 35 | }; 36 | 37 | const include_graph_t &include_graph_printer_t::include_graph() const 38 | { 39 | return graph_; 40 | }; 41 | 42 | std::ostream &operator<<(std::ostream &os, include_graph_printer_t &a) 43 | { 44 | a(os); 45 | return os; 46 | } 47 | 48 | } // namespace clang_include_graph 49 | -------------------------------------------------------------------------------- /src/include_graph_printer.h: -------------------------------------------------------------------------------- 1 | /** 2 | * src/include_graph_printer.h 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #ifndef CLANG_INCLUDE_GRAPH_INCLUDE_GRAPH_PRINTER_H 20 | #define CLANG_INCLUDE_GRAPH_INCLUDE_GRAPH_PRINTER_H 21 | 22 | #include "include_graph.h" 23 | #include "path_printer.h" 24 | 25 | namespace clang_include_graph { 26 | 27 | class include_graph_printer_t { 28 | public: 29 | include_graph_printer_t( 30 | const include_graph_t &graph, const path_printer_t &pp); 31 | 32 | virtual ~include_graph_printer_t() = default; 33 | 34 | include_graph_printer_t(const include_graph_printer_t &) = default; 35 | include_graph_printer_t(include_graph_printer_t &&) = default; 36 | include_graph_printer_t &operator=( 37 | const include_graph_printer_t &) = delete; 38 | include_graph_printer_t &operator=(include_graph_printer_t &&) = delete; 39 | 40 | virtual void operator()(std::ostream &) const = 0; 41 | 42 | const path_printer_t &path_printer() const; 43 | 44 | const include_graph_t &include_graph() const; 45 | 46 | private: 47 | const include_graph_t &graph_; 48 | const path_printer_t &path_printer_; 49 | }; 50 | 51 | std::ostream &operator<<(std::ostream &os, include_graph_printer_t &a); 52 | 53 | } // namespace clang_include_graph 54 | 55 | #endif // CLANG_INCLUDE_GRAPH_INCLUDE_GRAPH_PRINTER_H -------------------------------------------------------------------------------- /src/include_graph_topological_sort_printer.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * src/include_graph_topological_sort_printer.cc 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #include "include_graph_topological_sort_printer.h" 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | namespace clang_include_graph { 29 | 30 | void include_graph_topological_sort_printer_t::operator()( 31 | std::ostream &os) const 32 | { 33 | assert(include_graph().dag()); 34 | 35 | std::vector include_order; 36 | boost::topological_sort( 37 | include_graph().dag().value(), std::back_inserter(include_order)); 38 | 39 | for (const auto id : include_order) { 40 | os << path_printer().print(include_graph().graph().graph()[id]) << '\n'; 41 | } 42 | } 43 | 44 | } // namespace clang_include_graph 45 | -------------------------------------------------------------------------------- /src/include_graph_topological_sort_printer.h: -------------------------------------------------------------------------------- 1 | /** 2 | * src/include_graph_topological_sort_printer.h 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #ifndef CLANG_INCLUDE_GRAPH_INCLUDE_GRAPH_TOPOLOGICAL_SORT_PRINTER_H 20 | #define CLANG_INCLUDE_GRAPH_INCLUDE_GRAPH_TOPOLOGICAL_SORT_PRINTER_H 21 | 22 | #include "include_graph_printer.h" 23 | 24 | #include 25 | 26 | namespace clang_include_graph { 27 | 28 | class include_graph_topological_sort_printer_t 29 | : public include_graph_printer_t { 30 | public: 31 | using include_graph_printer_t::include_graph_printer_t; 32 | 33 | ~include_graph_topological_sort_printer_t() override = default; 34 | 35 | include_graph_topological_sort_printer_t( 36 | const include_graph_topological_sort_printer_t &) = default; 37 | include_graph_topological_sort_printer_t( 38 | include_graph_topological_sort_printer_t &&) = default; 39 | include_graph_topological_sort_printer_t &operator=( 40 | const include_graph_topological_sort_printer_t &) = delete; 41 | include_graph_topological_sort_printer_t &operator=( 42 | include_graph_topological_sort_printer_t &&) = delete; 43 | 44 | void operator()(std::ostream &os) const override; 45 | }; 46 | 47 | } // namespace clang_include_graph 48 | 49 | #endif // CLANG_INCLUDE_GRAPH_INCLUDE_GRAPH_TOPOLOGICAL_SORT_PRINTER_H -------------------------------------------------------------------------------- /src/include_graph_tree_printer.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * src/include_graph_tree_printer.cc 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #include "include_graph_tree_printer.h" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | namespace clang_include_graph { 33 | 34 | include_graph_tree_printer_t::include_graph_tree_printer_t( 35 | const include_graph_t &graph, const path_printer_t &pp) 36 | : include_graph_printer_t{graph, pp} 37 | { 38 | } 39 | 40 | void include_graph_tree_printer_t::operator()(std::ostream &os) const 41 | { 42 | assert(include_graph().dag()); 43 | 44 | auto begin = boost::vertices(include_graph().dag().value().graph()).first; 45 | auto end = boost::vertices(include_graph().dag().value().graph()).second; 46 | 47 | std::for_each( 48 | begin, end, [&](const include_graph_t::graph_t::vertex_descriptor &v) { 49 | const auto &graph = include_graph().graph().graph(); 50 | const auto &vertex = graph[v]; 51 | 52 | bool is_tree_root{false}; 53 | 54 | if (include_graph().printer() == printer_t::reverse_tree) { 55 | is_tree_root = 56 | (boost::in_degree(v, include_graph().graph()) == 0); 57 | } 58 | else { 59 | is_tree_root = vertex.is_translation_unit; 60 | } 61 | 62 | if (is_tree_root) { 63 | os << path_printer().print(vertex) << '\n'; 64 | print_tu_subtree(os, v, 0, include_graph(), {}); 65 | } 66 | }); 67 | } 68 | 69 | void include_graph_tree_printer_t::print_tu_subtree(std::ostream &os, 70 | const include_graph_t::graph_t::vertex_descriptor tu_id, 71 | const unsigned int level, const include_graph_t &include_graph, 72 | std::vector continuation_line) const 73 | { 74 | const auto kIndentWidth = 4U; 75 | 76 | boost::graph_traits::adjacency_iterator it; 77 | boost::graph_traits::adjacency_iterator it_end; 78 | 79 | boost::tie(it, it_end) = 80 | boost::adjacent_vertices(tu_id, include_graph.dag().value().graph()); 81 | for (; it != it_end; ++it) { 82 | auto continuation_line_tmp = continuation_line; 83 | if (level > 0) { 84 | for (auto i = 0U; i < level; i++) { 85 | if (i % kIndentWidth == 0 && 86 | continuation_line[i / kIndentWidth]) { 87 | #ifdef _MSC_VER 88 | os << "I"; 89 | #else 90 | os << "│"; 91 | #endif 92 | } 93 | else { 94 | os << " "; 95 | } 96 | } 97 | } 98 | 99 | if (std::next(it) == it_end) { 100 | #ifdef _MSC_VER 101 | os << "\\-- "; 102 | #else 103 | os << "└── "; 104 | #endif 105 | continuation_line_tmp.push_back(false); 106 | } 107 | else { 108 | #ifdef _MSC_VER 109 | os << "+-- "; 110 | #else 111 | os << "├── "; 112 | #endif 113 | continuation_line_tmp.push_back(true); 114 | } 115 | 116 | os << path_printer().print(include_graph.graph().graph()[*it]) << '\n'; 117 | print_tu_subtree(os, *it, level + kIndentWidth, include_graph, 118 | continuation_line_tmp); 119 | } 120 | } 121 | 122 | } // namespace clang_include_graph 123 | -------------------------------------------------------------------------------- /src/include_graph_tree_printer.h: -------------------------------------------------------------------------------- 1 | /** 2 | * src/include_graph_tree_printer.h 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #ifndef CLANG_INCLUDE_GRAPH_INCLUDE_GRAPH_TREE_PRINTER_H 20 | #define CLANG_INCLUDE_GRAPH_INCLUDE_GRAPH_TREE_PRINTER_H 21 | 22 | #include "include_graph_printer.h" 23 | 24 | #include 25 | 26 | namespace clang_include_graph { 27 | 28 | class include_graph_tree_printer_t : public include_graph_printer_t { 29 | public: 30 | include_graph_tree_printer_t( 31 | const include_graph_t &graph, const path_printer_t &pp); 32 | 33 | ~include_graph_tree_printer_t() override = default; 34 | 35 | include_graph_tree_printer_t( 36 | const include_graph_tree_printer_t &) = default; 37 | include_graph_tree_printer_t(include_graph_tree_printer_t &&) = default; 38 | include_graph_tree_printer_t &operator=( 39 | const include_graph_tree_printer_t &) = delete; 40 | include_graph_tree_printer_t &operator=( 41 | include_graph_tree_printer_t &&) = delete; 42 | 43 | void operator()(std::ostream &os) const override; 44 | 45 | private: 46 | void print_tu_subtree(std::ostream &os, 47 | include_graph_t::graph_t::vertex_descriptor tu_id, unsigned int level, 48 | const include_graph_t &include_graph, 49 | std::vector continuation_line) const; 50 | }; 51 | 52 | } // namespace clang_include_graph 53 | 54 | #endif // CLANG_INCLUDE_GRAPH_INCLUDE_GRAPH_TREE_PRINTER_H 55 | -------------------------------------------------------------------------------- /src/main.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * src/main.cc 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #include "config.h" 20 | #include "include_graph.h" 21 | #include "include_graph_cycles_printer.h" 22 | #include "include_graph_dependants_printer.h" 23 | #include "include_graph_graphml_printer.h" 24 | #include "include_graph_graphviz_printer.h" 25 | #if defined(WITH_JSON_OUTPUT) 26 | #include "include_graph_json_printer.h" 27 | #endif 28 | #include "include_graph_parser.h" 29 | #include "include_graph_plantuml_printer.h" 30 | #include "include_graph_topological_sort_printer.h" 31 | #include "include_graph_tree_printer.h" 32 | #include "util.h" 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #include 41 | #include 42 | #include 43 | 44 | namespace po = boost::program_options; 45 | 46 | #ifndef LIBCLANG_VERSION_STRING 47 | #define LIBCLANG_VERSION_STRING "0.0.0" 48 | #endif 49 | 50 | #ifndef GIT_VERSION 51 | #define GIT_VERSION "0.2.0" 52 | #endif 53 | 54 | void print_version(); 55 | 56 | void process_command_line_options(int argc, char **argv, po::variables_map &vm, 57 | clang_include_graph::config_t &config); 58 | 59 | std::ostream &get_output_stream( 60 | const boost::optional &output_path); 61 | 62 | int main(int argc, char **argv) 63 | { 64 | using clang_include_graph::config_t; 65 | using clang_include_graph::include_graph_cycles_printer_t; 66 | using clang_include_graph::include_graph_dependants_printer_t; 67 | using clang_include_graph::include_graph_graphml_printer_t; 68 | using clang_include_graph::include_graph_graphviz_printer_t; 69 | #if defined(WITH_JSON_OUTPUT) 70 | using clang_include_graph::include_graph_json_printer_t; 71 | #endif 72 | using clang_include_graph::include_graph_parser_t; 73 | using clang_include_graph::include_graph_plantuml_printer_t; 74 | using clang_include_graph::include_graph_t; 75 | using clang_include_graph::include_graph_topological_sort_printer_t; 76 | using clang_include_graph::include_graph_tree_printer_t; 77 | using clang_include_graph::path_printer_t; 78 | using clang_include_graph::printer_t; 79 | 80 | include_graph_t include_graph; 81 | config_t config; 82 | po::variables_map vm; 83 | 84 | process_command_line_options(argc, argv, vm, config); 85 | 86 | LOG(info) << "Loading compilation database from " 87 | << config.compilation_database_directory().value() << '\n'; 88 | 89 | // Parse translation units and build the include graph 90 | include_graph_parser_t include_graph_parser{config}; 91 | include_graph_parser.parse(include_graph); 92 | 93 | // Select file path printer based on config 94 | std::unique_ptr const path_printer = 95 | path_printer_t::from_config(config); 96 | 97 | auto &output = get_output_stream(config.output_file()); 98 | 99 | // Generate output using selected printer 100 | if (config.printer() == printer_t::tree) { 101 | LOG(info) << "Printing include graph tree\n"; 102 | 103 | include_graph.build_dag(); 104 | 105 | include_graph_tree_printer_t printer{include_graph, *path_printer}; 106 | 107 | output << printer; 108 | } 109 | else if (config.printer() == printer_t::reverse_tree) { 110 | LOG(info) << "Printing reverse include graph tree\n"; 111 | 112 | include_graph.build_dag(); 113 | 114 | include_graph_tree_printer_t printer{include_graph, *path_printer}; 115 | 116 | output << printer; 117 | } 118 | #if defined(WITH_JSON_OUTPUT) 119 | else if (config.printer() == printer_t::json) { 120 | LOG(info) << "Printing include graph in JSON Graph Format\n"; 121 | 122 | include_graph.build_dag(); 123 | 124 | include_graph_json_printer_t printer{include_graph, *path_printer}; 125 | 126 | output << printer; 127 | } 128 | #endif 129 | else if (config.printer() == printer_t::dependants) { 130 | LOG(info) << "Printing dependants of " << *config.dependants_of() 131 | << '\n'; 132 | 133 | include_graph.build_dag(); 134 | 135 | include_graph_dependants_printer_t printer{ 136 | include_graph, *path_printer}; 137 | 138 | output << printer; 139 | } 140 | else if (config.printer() == printer_t::topological_sort) { 141 | LOG(info) << "Printing include graph sorted in topological order\n"; 142 | 143 | include_graph.build_dag(); 144 | 145 | include_graph_topological_sort_printer_t printer{ 146 | include_graph, *path_printer}; 147 | 148 | output << printer; 149 | } 150 | else if (config.printer() == printer_t::cycles) { 151 | LOG(info) << "Printing include graph cycles\n"; 152 | 153 | include_graph_cycles_printer_t printer{include_graph, *path_printer}; 154 | 155 | output << printer; 156 | } 157 | else if (config.printer() == printer_t::graphviz) { 158 | LOG(info) << "Printing include graph in GraphViz format\n"; 159 | 160 | include_graph_graphviz_printer_t printer{include_graph, *path_printer}; 161 | 162 | output << printer; 163 | } 164 | else if (config.printer() == printer_t::graphml) { 165 | LOG(info) << "Printing include graph in GraphML format\n"; 166 | 167 | include_graph_graphml_printer_t printer{include_graph, *path_printer}; 168 | 169 | output << printer; 170 | } 171 | else if (config.printer() == printer_t::plantuml) { 172 | LOG(info) << "Printing include graph in PlantUML format\n"; 173 | 174 | include_graph_plantuml_printer_t printer{include_graph, *path_printer}; 175 | 176 | output << printer; 177 | } 178 | else { 179 | LOG(error) << "ERROR: Invalid output printer - aborting..." << '\n'; 180 | exit(-1); 181 | } 182 | 183 | if (config.output_file()) { 184 | LOG(info) << "Output written to file: " 185 | << config.output_file()->string() << '\n'; 186 | } 187 | } 188 | 189 | void process_command_line_options(int argc, char **argv, po::variables_map &vm, 190 | clang_include_graph::config_t &config) 191 | { 192 | po::options_description options("clang-include-graph options"); 193 | 194 | // clang-format off 195 | options.add_options() 196 | ("help,h", "Print help message and exit") 197 | ("version,V", "Print program version and exit") 198 | ("verbose,v", po::value(), 199 | "Set log verbosity level") 200 | ("log-file", po::value(), 201 | "Log to specified file instead of console") 202 | ("jobs,J", po::value(), 203 | "Number of threads used to parse translation units") 204 | ("compilation-database-dir,d", po::value(), 205 | "Path to compilation database directory (default: $PWD)") 206 | ("add-compile-flag", po::value>(), 207 | "Add a compile flag to the compilation database") 208 | ("remove-compile-flag", po::value>(), 209 | "Remove a compile flag from the compilation database. " 210 | "Can contain a '*' to match multiple flags") 211 | ("translation-unit,u", po::value>(), 212 | "Path or glob patterns to match translation units for processing " 213 | "(relative to $PWD). ") 214 | ("relative-to,r", po::value(), 215 | "Print paths relative to path (except for system headers)") 216 | ("names-only,n", "Print only file names") 217 | ("relative-only,l", 218 | "Include only files relative to 'relative-to' directory") 219 | ("output,o", po::value(), 220 | "Write the output to a specified file instead of stdout") 221 | #if defined(WITH_JSON_OUTPUT) 222 | ("json-numeric-ids", "Use numeric ids for nodes instead of paths") 223 | #endif 224 | ("dependants-of,e", po::value(), 225 | "Print all files that depend on a specific header") 226 | ("translation-units-only", "Print only translation units") 227 | ("exclude-system-headers", "Exclude system headers from include graph") 228 | ("title", po::value(), 229 | "Graph title that can be added ") 230 | ("topological-sort,s", 231 | "Print includes and translation units in topological" 232 | "sort order (default)") 233 | ("tree,t", "Print include graph in tree form") 234 | ("reverse-tree,T", "Print reverse include graph in tree form") 235 | #if defined(WITH_JSON_OUTPUT) 236 | ("json,j", "Print include graph in Json Graph format") 237 | #endif 238 | ("cycles,c", "Print include graph cycles, if any") 239 | ("graphviz,g", "Print include graph in GraphViz format") 240 | ("graphml,G", "Print include graph in GraphML format") 241 | ("plantuml,p", "Print include graph in PlantUML format"); 242 | // clang-format on 243 | 244 | try { 245 | po::store(po::parse_command_line(argc, argv, options), vm); 246 | po::notify(vm); 247 | } 248 | catch (const po::error &e) { 249 | std::cerr << "ERROR: Invalid options - " << e.what() << '\n'; 250 | exit(-1); 251 | } 252 | 253 | if (vm.count("help") > 0U) { 254 | std::cout << options << "\n"; 255 | exit(0); 256 | } 257 | 258 | if (vm.count("version") > 0U) { 259 | print_version(); 260 | exit(0); 261 | } 262 | 263 | config.init(vm, clang_include_graph::util::join_cli_args(argc, argv)); 264 | 265 | clang_include_graph::util::setup_logging( 266 | config.verbosity(), config.log_file()); 267 | } 268 | 269 | std::ostream &get_output_stream( 270 | const boost::optional &output_path) 271 | { 272 | if (!output_path) { 273 | return std::cout; 274 | } 275 | 276 | try { 277 | boost::filesystem::create_directories( 278 | output_path.value().parent_path()); 279 | } 280 | catch (const boost::filesystem::filesystem_error &e) { 281 | std::cerr << "ERROR: Cannot create output directory " 282 | << output_path.value().parent_path() << ": '" << e.what() 283 | << "'" << '\n'; 284 | exit(-1); 285 | } 286 | 287 | static std::ofstream output_file; 288 | 289 | if (output_file.is_open()) { 290 | output_file.close(); 291 | } 292 | output_file.clear(); 293 | 294 | output_file.open(output_path->string()); 295 | if (!output_file) { 296 | LOG(error) << "Failed to open output file: " << output_path->string(); 297 | exit(-1); 298 | } 299 | 300 | return output_file; 301 | } 302 | 303 | void print_version() 304 | { 305 | std::cout << "clang-include-graph " << GIT_VERSION << '\n'; 306 | std::cout << "Copyright (C) 2022-present Bartek Kryza " 307 | << '\n'; 308 | std::cout << "Built with libclang: " << LIBCLANG_VERSION_STRING << '\n'; 309 | } 310 | -------------------------------------------------------------------------------- /src/path_printer.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * src/path_printer.cc 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #include "path_printer.h" 20 | #include "config.h" 21 | 22 | #include 23 | 24 | namespace clang_include_graph { 25 | 26 | namespace detail { 27 | template 28 | std::unique_ptr make_unique(Args &&...args) 29 | { 30 | return std::unique_ptr(new T(std::forward(args)...)); 31 | } 32 | } // namespace detail 33 | 34 | std::unique_ptr path_printer_t::from_config( 35 | const config_t &config) 36 | { 37 | if (config.relative_to()) { 38 | return detail::make_unique(config); 39 | } 40 | 41 | if (config.filenames_only()) { 42 | return detail::make_unique(config); 43 | } 44 | 45 | return detail::make_unique(config); 46 | } 47 | 48 | } // namespace clang_include_graph -------------------------------------------------------------------------------- /src/path_printer.h: -------------------------------------------------------------------------------- 1 | /** 2 | * src/path_printer.h 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #ifndef CLANG_INCLUDE_GRAPH_PATH_PRINTER_H 20 | #define CLANG_INCLUDE_GRAPH_PATH_PRINTER_H 21 | 22 | #include "config.h" 23 | #include "include_graph.h" 24 | 25 | #include 26 | #include 27 | 28 | namespace clang_include_graph { 29 | 30 | class path_printer_t { 31 | public: 32 | path_printer_t() = default; 33 | 34 | explicit path_printer_t(config_t config) 35 | : config_{std::move(config)} 36 | { 37 | } 38 | 39 | virtual ~path_printer_t() = default; 40 | path_printer_t(const path_printer_t &) = default; 41 | path_printer_t(path_printer_t &&) = default; 42 | path_printer_t &operator=(const path_printer_t &) = default; 43 | path_printer_t &operator=(path_printer_t &&) = default; 44 | 45 | virtual std::string print(const include_graph_t::vertex_t &v) const 46 | { 47 | return v.file; 48 | } 49 | 50 | static std::unique_ptr from_config(const config_t &config); 51 | 52 | const config_t &config() { return config_; } 53 | 54 | private: 55 | config_t config_; 56 | }; 57 | 58 | class path_relative_printer_t final : public path_printer_t { 59 | public: 60 | explicit path_relative_printer_t(config_t config) 61 | : path_printer_t{std::move(config)} 62 | , relative_to_{*this->config().relative_to()} 63 | { 64 | } 65 | 66 | std::string print(const include_graph_t::vertex_t &v) const override 67 | { 68 | // Only return relative path, if path is in relative_to_ directory 69 | if (boost::starts_with(v.file, relative_to_.string())) { 70 | return boost::filesystem::relative(v.file, relative_to_).string(); 71 | } 72 | 73 | return v.file; 74 | } 75 | 76 | private: 77 | boost::filesystem::path relative_to_; 78 | }; 79 | 80 | class path_name_printer_t final : public path_printer_t { 81 | public: 82 | using path_printer_t::path_printer_t; 83 | 84 | std::string print(const include_graph_t::vertex_t &v) const override 85 | { 86 | return boost::filesystem::path(v.file).filename().string(); 87 | } 88 | }; 89 | 90 | } // namespace clang_include_graph 91 | 92 | #endif // CLANG_INCLUDE_GRAPH_PATH_PRINTER_H 93 | -------------------------------------------------------------------------------- /src/util.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * src/util.cc 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #include "util.h" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | 30 | namespace clang_include_graph { 31 | namespace util { 32 | 33 | std::string join_cli_args(int argc, char **argv) 34 | { 35 | std::string result; 36 | 37 | for (auto i = 1; i < argc; ++i) { 38 | if (i > 1) 39 | result += ' '; 40 | result += argv[i]; // NOLINT 41 | } 42 | 43 | return result; 44 | } 45 | 46 | boost::filesystem::path to_absolute_path( 47 | const boost::filesystem::path &relative_path) 48 | { 49 | try { 50 | return boost::filesystem::weakly_canonical(relative_path).string(); 51 | } 52 | catch (const boost::filesystem::filesystem_error &e) { 53 | LOG(error) << "ERROR: Failed to resolve absolute path from '" 54 | << relative_path << "': " << e.what() << 'n'; 55 | throw e; // NOLINT 56 | } 57 | } 58 | 59 | void setup_logging( 60 | int log_level, boost::optional log_file) 61 | { 62 | const boost::shared_ptr< 63 | sinks::synchronous_sink> 64 | sink(new sinks::synchronous_sink); 65 | 66 | boost::optional< 67 | boost::shared_ptr>> 68 | file_sink; 69 | 70 | sink->locked_backend()->add_stream( 71 | boost::shared_ptr(&std::clog, boost::null_deleter())); 72 | 73 | sink->set_formatter(expr::format("=== %1%") % expr::smessage); 74 | 75 | logging::core::get()->add_sink(sink); 76 | 77 | if (log_file) { 78 | // Make sure the log directory exists 79 | try { 80 | boost::filesystem::create_directories(log_file->parent_path()); 81 | } 82 | catch (const boost::filesystem::filesystem_error &e) { 83 | std::cerr << "ERROR: Cannot create log directory " 84 | << log_file->parent_path() << ": '" << e.what() << "'" 85 | << '\n'; 86 | exit(-1); 87 | } 88 | 89 | // Try to create the log file if it doesn't exist yet 90 | try { 91 | if (!boost::filesystem::exists(*log_file)) { 92 | const std::ofstream ofs(log_file->string()); 93 | if (!ofs) { 94 | std::cerr << "ERROR: Cannot create log file " 95 | << log_file->string() << '\n'; 96 | exit(-1); 97 | } 98 | } 99 | } 100 | catch (const boost::filesystem::filesystem_error &e) { 101 | std::cerr << "ERROR: Cannot create log file " 102 | << log_file->parent_path() << ": '" << e.what() << "'" 103 | << '\n'; 104 | exit(-1); 105 | } 106 | 107 | file_sink = boost::shared_ptr< 108 | sinks::synchronous_sink>( 109 | new sinks::synchronous_sink); 110 | 111 | file_sink.value()->locked_backend()->add_stream( 112 | boost::shared_ptr( 113 | new std::ofstream(log_file->string().c_str()))); 114 | 115 | // Set up a formatter for all sinks in the core 116 | file_sink.value()->set_formatter(expr::format("[%1%] [%2%] - %3%") % 117 | expr::attr("TimeStamp") % 118 | expr::attr("ThreadID") % 119 | expr::smessage); 120 | 121 | logging::core::get()->add_global_attribute( 122 | "TimeStamp", attrs::local_clock()); 123 | logging::core::get()->add_global_attribute( 124 | "ThreadID", attrs::current_thread_id()); 125 | 126 | logging::core::get()->add_sink(*file_sink); 127 | } 128 | 129 | auto log_severity = boost::log::trivial::info; 130 | 131 | switch (log_level) { 132 | case 0: 133 | log_severity = boost::log::trivial::warning; 134 | break; 135 | case 1: 136 | log_severity = boost::log::trivial::info; 137 | break; 138 | case 2: 139 | log_severity = boost::log::trivial::debug; 140 | break; 141 | default: 142 | log_severity = boost::log::trivial::trace; 143 | } 144 | 145 | sink->set_filter(logging::trivial::severity >= log_severity); 146 | 147 | LOG(info) << "Set console logging level to: " << to_string(log_severity) 148 | << '\n'; 149 | } 150 | 151 | std::regex glob_to_regex(const std::string &glob_pattern) 152 | { 153 | std::string regex_pattern; 154 | regex_pattern.reserve(glob_pattern.size() * 2); 155 | 156 | regex_pattern += '^'; // Match start of string 157 | 158 | for (const auto c : glob_pattern) { 159 | switch (c) { 160 | case '*': 161 | regex_pattern += ".*"; 162 | break; 163 | case '?': 164 | regex_pattern += "."; 165 | break; 166 | case '.': 167 | case '+': 168 | case '(': 169 | case ')': 170 | case '{': 171 | case '}': 172 | case '[': 173 | case ']': 174 | case '^': 175 | case '$': 176 | case '|': 177 | case '\\': 178 | // Escape special regex characters 179 | regex_pattern += '\\'; 180 | regex_pattern += c; 181 | break; 182 | default: 183 | regex_pattern += c; 184 | } 185 | } 186 | 187 | regex_pattern += '$'; 188 | 189 | return std::regex(regex_pattern); 190 | } 191 | 192 | bool match_flag_glob(const std::string &flag, const std::string &glob) 193 | { 194 | std::cmatch m; 195 | std::regex_match(flag.c_str(), m, util::glob_to_regex(glob)); 196 | 197 | return m.size() == 1; 198 | } 199 | 200 | } // namespace util 201 | } // namespace clang_include_graph 202 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | /** 2 | * src/util.h 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #ifndef CLANG_INCLUDE_GRAPH_UTIL_H 20 | #define CLANG_INCLUDE_GRAPH_UTIL_H 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | #include 33 | 34 | BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(global_logger, 35 | boost::log::sources::severity_logger_mt< 36 | boost::log::trivial::severity_level>) 37 | 38 | // NOLINTNEXTLINE 39 | #define LOG(lvl) \ 40 | BOOST_LOG_SEV( \ 41 | global_logger::get(), boost::log::trivial::severity_level::lvl) 42 | 43 | namespace clang_include_graph { 44 | namespace util { 45 | 46 | namespace logging = boost::log; 47 | namespace attrs = boost::log::attributes; 48 | namespace src = boost::log::sources; 49 | namespace sinks = boost::log::sinks; 50 | namespace expr = boost::log::expressions; 51 | namespace keywords = boost::log::keywords; 52 | 53 | std::string join_cli_args(int argc, char **argv); 54 | 55 | boost::filesystem::path to_absolute_path( 56 | const boost::filesystem::path &relative_path); 57 | 58 | void setup_logging( 59 | int log_level, boost::optional log_file); 60 | 61 | std::regex glob_to_regex(const std::string &glob_pattern); 62 | 63 | bool match_flag_glob(const std::string &flag, const std::string &glob); 64 | } // namespace util 65 | } // namespace clang_include_graph 66 | 67 | #endif // CLANG_INCLUDE_GRAPH_UTIL_H 68 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | list(APPEND TESTCASES 2 | test_topological_sort_printer 3 | test_cycles_printer 4 | test_tree_printer 5 | test_dependants_printer 6 | test_graphviz_printer 7 | test_graphml_printer 8 | test_plantuml_printer 9 | test_util) 10 | 11 | if(WITH_JSON) 12 | list(APPEND TESTCASES test_json_printer) 13 | endif(WITH_JSON) 14 | 15 | foreach(TESTCASE IN LISTS TESTCASES) 16 | add_executable( 17 | ${TESTCASE} 18 | $ 19 | ${TESTCASE}.cc) 20 | 21 | target_include_directories(${TESTCASE} PUBLIC ${Boost_INCLUDE_DIRS}) 22 | 23 | target_compile_options( 24 | ${TESTCASE} 25 | PRIVATE $<$: 26 | $<$,$>: 27 | -Wno-unused-parameter 28 | -Wno-unused-variable 29 | -Wno-attributes 30 | -Wno-nonnull 31 | -Wno-deprecated-enum-enum-conversion 32 | ${CUSTOM_COMPILE_OPTIONS}> 33 | $<$:/MP 34 | /MD 35 | /W1 36 | /bigobj 37 | /wd4624>>) 38 | 39 | target_link_libraries(${TESTCASE} 40 | ${LIBCLANG_LIBRARIES} 41 | Boost::filesystem 42 | Boost::graph 43 | Boost::program_options 44 | Boost::unit_test_framework 45 | Boost::log) 46 | 47 | if(WITH_JSON) 48 | target_link_libraries(${TESTCASE} Boost::json) 49 | endif(WITH_JSON) 50 | 51 | add_test(${TESTCASE} ${TESTCASE}) 52 | endforeach() -------------------------------------------------------------------------------- /tests/test_cycles_printer.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * tests/test_cycles_printer.cc 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #define BOOST_TEST_MODULE Unit test of include_graph_cycles_printer_t 20 | 21 | #include 22 | 23 | #include "../src/include_graph_cycles_printer.h" 24 | #include "test_utils.h" 25 | 26 | #include 27 | 28 | using namespace clang_include_graph; 29 | 30 | BOOST_AUTO_TEST_CASE(test_dag_prints_no_cycles) 31 | { 32 | include_graph_t graph; 33 | graph.add_edge("include1.h", "main.cc", true); 34 | graph.add_edge("include2.h", "main.cc", true); 35 | graph.add_edge("include3.h", "include1.h"); 36 | 37 | graph.build_dag(); 38 | 39 | path_printer_t pp; 40 | 41 | include_graph_cycles_printer_t p{graph, pp}; 42 | 43 | std::stringstream ss; 44 | ss << p; 45 | 46 | std::vector includes; 47 | read_lines(ss, includes); 48 | 49 | BOOST_TEST(includes.size() == 0); 50 | } 51 | 52 | BOOST_AUTO_TEST_CASE(test_dag_prints_simple_cycles) 53 | { 54 | include_graph_t graph; 55 | graph.add_edge("include1.h", "main.cc", true); 56 | graph.add_edge("include2.h", "main.cc", true); 57 | graph.add_edge("include2.h", "include1.h"); 58 | graph.add_edge("include1.h", "include2.h"); 59 | 60 | graph.build_dag(); 61 | 62 | path_printer_t pp; 63 | 64 | include_graph_cycles_printer_t p{graph, pp}; 65 | 66 | std::stringstream ss; 67 | ss << p; 68 | 69 | std::vector includes; 70 | read_lines(ss, includes); 71 | 72 | BOOST_TEST(includes.size() == 4); 73 | BOOST_TEST(includes[0] == "["); 74 | BOOST_TEST(includes[1] == " include1.h"); 75 | BOOST_TEST(includes[2] == " include2.h"); 76 | BOOST_TEST(includes[3] == "]"); 77 | } 78 | 79 | BOOST_AUTO_TEST_CASE(test_dag_prints_long_nested_cycles) 80 | { 81 | include_graph_t graph; 82 | graph.add_edge("include1.h", "main.cc", true); 83 | graph.add_edge("include2.h", "main.cc", true); 84 | graph.add_edge("include2.h", "include1.h"); 85 | graph.add_edge("include1.h", "include2.h"); 86 | graph.add_edge("include3.h", "include1.h"); 87 | graph.add_edge("include4.h", "include3.h"); 88 | graph.add_edge("include5.h", "include4.h"); 89 | graph.add_edge("include1.h", "include5.h"); 90 | 91 | path_printer_t pp; 92 | 93 | include_graph_cycles_printer_t p{graph, pp}; 94 | 95 | std::stringstream ss; 96 | ss << p; 97 | 98 | std::vector includes; 99 | read_lines(ss, includes); 100 | 101 | BOOST_TEST(includes.size() == 10); 102 | BOOST_TEST(includes[0] == "["); 103 | BOOST_TEST(includes[1] == " include1.h"); 104 | BOOST_TEST(includes[2] == " include2.h"); 105 | BOOST_TEST(includes[3] == "]"); 106 | BOOST_TEST(includes[4] == "["); 107 | BOOST_TEST(includes[5] == " include1.h"); 108 | BOOST_TEST(includes[6] == " include3.h"); 109 | BOOST_TEST(includes[7] == " include4.h"); 110 | BOOST_TEST(includes[8] == " include5.h"); 111 | BOOST_TEST(includes[9] == "]"); 112 | } -------------------------------------------------------------------------------- /tests/test_dependants_printer.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * tests/test_dependants_printer.cc 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #define BOOST_TEST_MODULE Unit test of include_graph_dependants_printer_t 20 | 21 | #include 22 | 23 | #include "../src/include_graph_dependants_printer.h" 24 | #include "test_utils.h" 25 | 26 | #include 27 | 28 | using namespace clang_include_graph; 29 | 30 | BOOST_AUTO_TEST_CASE(test_dependants_printer_prints_only_dependants_of_header) 31 | { 32 | config_t config; 33 | config.dependants_of("include1.h"); 34 | config.relative_to("."); 35 | config.relative_only(true); 36 | config.translation_units_only(false); 37 | config.printer(printer_t::dependants); 38 | 39 | include_graph_t graph; 40 | graph.init(config); 41 | 42 | graph.add_edge("include1.h", "main.cc", true); 43 | graph.add_edge("include2.h", "util.cc", true); 44 | graph.add_edge("include3.h", "other.cc", true); 45 | graph.add_edge("include1.h", "include2.h"); 46 | graph.add_edge("include3.h", "include1.h"); 47 | 48 | graph.build_dag(); 49 | 50 | path_printer_t pp; 51 | 52 | include_graph_dependants_printer_t p{graph, pp}; 53 | 54 | std::stringstream ss; 55 | ss << p; 56 | 57 | std::vector includes; 58 | read_lines(ss, includes); 59 | 60 | BOOST_TEST(includes.size() == 3); 61 | BOOST_TEST(includes[0] == "main.cc"); 62 | BOOST_TEST(includes[1] == "include2.h"); 63 | BOOST_TEST(includes[2] == "util.cc"); 64 | } 65 | 66 | BOOST_AUTO_TEST_CASE( 67 | test_dependants_printer_prints_only_tu_dependants_of_header) 68 | { 69 | config_t config; 70 | config.dependants_of("include1.h"); 71 | config.relative_to("."); 72 | config.relative_only(true); 73 | config.translation_units_only(true); 74 | config.printer(printer_t::dependants); 75 | 76 | include_graph_t graph; 77 | graph.init(config); 78 | 79 | graph.add_edge("include1.h", "main.cc", true); 80 | graph.add_edge("include2.h", "util.cc", true); 81 | graph.add_edge("include3.h", "other.cc", true); 82 | graph.add_edge("include1.h", "include2.h"); 83 | graph.add_edge("include3.h", "include1.h"); 84 | 85 | graph.build_dag(); 86 | 87 | path_printer_t pp; 88 | 89 | include_graph_dependants_printer_t p{graph, pp}; 90 | 91 | std::stringstream ss; 92 | ss << p; 93 | 94 | std::vector includes; 95 | read_lines(ss, includes); 96 | 97 | BOOST_TEST(includes.size() == 2); 98 | BOOST_TEST(includes[0] == "main.cc"); 99 | BOOST_TEST(includes[1] == "util.cc"); 100 | } -------------------------------------------------------------------------------- /tests/test_graphml_printer.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * tests/test_graphml_printer.cc 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #define BOOST_TEST_MODULE Unit test of include_graph_graphml_printer_t 20 | 21 | #include 22 | 23 | #include "../src/include_graph_graphml_printer.h" 24 | 25 | #include 26 | 27 | using namespace clang_include_graph; 28 | 29 | BOOST_AUTO_TEST_CASE(test_simple_graphviz) 30 | { 31 | include_graph_t graph; 32 | graph.add_edge("include1.h", "main.cc", true); 33 | graph.add_edge("include2.h", "main.cc", true); 34 | graph.add_edge("string", "main.cc", true, true); 35 | graph.add_edge("include3.h", "include1.h"); 36 | 37 | path_printer_t pp; 38 | 39 | include_graph_graphml_printer_t p{graph, pp}; 40 | 41 | std::stringstream ss; 42 | ss << p; 43 | 44 | std::string expected = R"( 45 | 46 | 47 | 48 | 49 | 50 | include1.h 51 | 52 | 53 | main.cc 54 | 55 | 56 | include2.h 57 | 58 | 59 | string 60 | 61 | 62 | include3.h 63 | 64 | 65 | 0 66 | 67 | 68 | 0 69 | 70 | 71 | 1 72 | 73 | 74 | 0 75 | 76 | 77 | 78 | )"; 79 | 80 | BOOST_TEST(ss.str() == expected); 81 | } 82 | -------------------------------------------------------------------------------- /tests/test_graphviz_printer.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * tests/test_graphviz_printer.cc 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #define BOOST_TEST_MODULE Unit test of include_graph_graphviz_printer_t 20 | 21 | #include 22 | 23 | #include "../src/include_graph_graphviz_printer.h" 24 | 25 | #include 26 | 27 | using namespace clang_include_graph; 28 | 29 | BOOST_AUTO_TEST_CASE(test_simple_graphviz) 30 | { 31 | include_graph_t graph; 32 | graph.add_edge("include1.h", "main.cc", true); 33 | graph.add_edge("include2.h", "main.cc", true); 34 | graph.add_edge("include3.h", "include1.h"); 35 | 36 | path_printer_t pp; 37 | 38 | include_graph_graphviz_printer_t p{graph, pp}; 39 | 40 | std::stringstream ss; 41 | ss << p; 42 | 43 | std::string expected = R"(digraph G { 44 | 0[label="include1.h"]; 45 | 1[label="main.cc"]; 46 | 2[label="include2.h"]; 47 | 3[label="include3.h"]; 48 | 1->0 ; 49 | 1->2 ; 50 | 0->3 ; 51 | } 52 | )"; 53 | 54 | BOOST_TEST(ss.str() == expected); 55 | } 56 | -------------------------------------------------------------------------------- /tests/test_json_printer.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * tests/test_json_printer.cc 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #define BOOST_TEST_MODULE Unit test of include_graph_json_printer_t 20 | 21 | #include 22 | #include 23 | 24 | #include "../src/include_graph_json_printer.h" 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | using namespace clang_include_graph; 31 | 32 | BOOST_AUTO_TEST_CASE(test_json_printer) 33 | { 34 | include_graph_t graph; 35 | graph.add_edge("include1.h", "main.cc", true); 36 | graph.add_edge("include2.h", "main.cc", true); 37 | graph.add_edge("include3.h", "util.cc", true); 38 | graph.add_edge("include4.h", "util.cc", true); 39 | graph.add_edge("string", "main.cc", true, true); 40 | graph.add_edge("include5.h", "include4.h"); 41 | 42 | graph.build_dag(); 43 | 44 | path_printer_t pp; 45 | 46 | include_graph_json_printer_t p{graph, pp}; 47 | 48 | std::stringstream ss; 49 | ss << p; 50 | 51 | BOOST_CHECK_NO_THROW({ 52 | boost::json::value root = boost::json::parse(ss.str()); 53 | 54 | BOOST_TEST(root.is_object()); 55 | auto &obj = root.as_object(); 56 | 57 | BOOST_CHECK(obj.contains("nodes")); 58 | BOOST_TEST(obj["nodes"].is_object()); 59 | 60 | auto &nodes = obj["nodes"].as_object(); 61 | BOOST_CHECK(nodes.count("include1.h")); 62 | BOOST_CHECK(nodes.count("include2.h")); 63 | BOOST_CHECK(nodes.count("include3.h")); 64 | BOOST_CHECK(nodes.count("include4.h")); 65 | BOOST_CHECK(nodes.count("util.cc")); 66 | BOOST_CHECK(nodes.count("main.cc")); 67 | 68 | BOOST_CHECK(obj.contains("edges")); 69 | BOOST_TEST(obj["edges"].is_array()); 70 | 71 | auto &edges = obj["edges"].as_array(); 72 | 73 | BOOST_CHECK(edges.size() == 6); 74 | 75 | std::string expected = 76 | R"({"directed":true,"type":"include_graph","metadata":{"cli_arguments":""},"nodes":{"include1.h":{"label":"include1.h","metadata":{"is_system_header":false,"is_translation_unit":false}},"main.cc":{"label":"main.cc","metadata":{"is_system_header":false,"is_translation_unit":true}},"include2.h":{"label":"include2.h","metadata":{"is_system_header":false,"is_translation_unit":false}},"include3.h":{"label":"include3.h","metadata":{"is_system_header":false,"is_translation_unit":false}},"util.cc":{"label":"util.cc","metadata":{"is_system_header":false,"is_translation_unit":true}},"include4.h":{"label":"include4.h","metadata":{"is_system_header":false,"is_translation_unit":false}},"string":{"label":"string","metadata":{"is_system_header":true,"is_translation_unit":false}},"include5.h":{"label":"include5.h","metadata":{"is_system_header":false,"is_translation_unit":false}}},"edges":[{"target":"include1.h","source":"main.cc","is_system":false},{"target":"include2.h","source":"main.cc","is_system":false},{"target":"include3.h","source":"util.cc","is_system":false},{"target":"include4.h","source":"util.cc","is_system":false},{"target":"string","source":"main.cc","is_system":true},{"target":"include5.h","source":"include4.h","is_system":false}]})"; 77 | 78 | BOOST_TEST(ss.str() == expected); 79 | }); 80 | } 81 | 82 | BOOST_AUTO_TEST_CASE(test_json_printer_numeric_ids) 83 | { 84 | include_graph_t graph; 85 | config_t config; 86 | config.json_printer_opts().numeric_ids = true; 87 | 88 | graph.init(config); 89 | graph.add_edge("include1.h", "main.cc", true); 90 | graph.add_edge("include2.h", "main.cc", true); 91 | 92 | graph.build_dag(); 93 | 94 | path_printer_t pp; 95 | 96 | include_graph_json_printer_t p{graph, pp}; 97 | 98 | std::stringstream ss; 99 | ss << p; 100 | 101 | boost::json::value root = boost::json::parse(ss.str()); 102 | 103 | BOOST_TEST(root.is_object()); 104 | auto &obj = root.as_object(); 105 | 106 | BOOST_CHECK(obj.contains("nodes")); 107 | BOOST_TEST(obj["nodes"].is_object()); 108 | 109 | auto &nodes = obj["nodes"].as_object(); 110 | 111 | std::map node_ids; 112 | for (const auto &node_it : nodes) { 113 | node_ids.emplace( 114 | node_it.value().as_object().at("label").as_string().c_str(), 115 | node_it.key()); 116 | } 117 | 118 | BOOST_CHECK(nodes.count(node_ids.at("include1.h"))); 119 | BOOST_CHECK(nodes.count(node_ids.at("include2.h"))); 120 | BOOST_CHECK(nodes.count(node_ids.at("main.cc"))); 121 | 122 | BOOST_CHECK(obj.contains("edges")); 123 | BOOST_TEST(obj["edges"].is_array()); 124 | 125 | auto &edges = obj["edges"].as_array(); 126 | 127 | BOOST_CHECK(edges.size() == 2); 128 | } 129 | -------------------------------------------------------------------------------- /tests/test_plantuml_printer.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * tests/test_plantuml_printer.cc 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #define BOOST_TEST_MODULE Unit test of include_graph_plantuml_printer_t 20 | 21 | #include 22 | 23 | #include "../src/include_graph_plantuml_printer.h" 24 | 25 | #include 26 | 27 | using namespace clang_include_graph; 28 | 29 | BOOST_AUTO_TEST_CASE(test_simple_plantuml) 30 | { 31 | include_graph_t graph; 32 | graph.add_edge("include1.h", "main.cc", true); 33 | graph.add_edge("include2.h", "main.cc", true); 34 | graph.add_edge("string", "main.cc", true, true); 35 | graph.add_edge("include3.h", "include1.h"); 36 | 37 | path_printer_t pp; 38 | 39 | include_graph_plantuml_printer_t p{graph, pp}; 40 | 41 | std::stringstream ss; 42 | ss << p; 43 | 44 | std::string expected = R"(@startuml 45 | file "include1.h" as F_0 46 | file "main.cc" as F_1 47 | file "include2.h" as F_2 48 | file "string" as F_3 49 | file "include3.h" as F_4 50 | F_0 <-- F_1 51 | F_2 <-- F_1 52 | F_3 <.. F_1 53 | F_4 <-- F_0 54 | @enduml 55 | )"; 56 | 57 | BOOST_TEST(ss.str() == expected); 58 | } 59 | -------------------------------------------------------------------------------- /tests/test_topological_sort_printer.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * tests/test_topological_sort_printer.cc 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #define BOOST_TEST_MODULE Unit test of include_graph_topological_sort_printer_t 20 | 21 | #include 22 | 23 | #include "../src/include_graph_topological_sort_printer.h" 24 | #include "test_utils.h" 25 | 26 | #include 27 | 28 | using namespace clang_include_graph; 29 | 30 | BOOST_AUTO_TEST_CASE(test_basic_graph_is_properly_sorted) 31 | { 32 | include_graph_t graph; 33 | graph.add_edge("include1.h", "main.cc", true); 34 | graph.add_edge("include2.h", "main.cc", true); 35 | graph.add_edge("include3.h", "include1.h"); 36 | 37 | graph.build_dag(); 38 | 39 | path_printer_t pp; 40 | 41 | include_graph_topological_sort_printer_t p{graph, pp}; 42 | 43 | std::stringstream ss; 44 | ss << p; 45 | 46 | std::vector includes; 47 | read_lines(ss, includes); 48 | 49 | BOOST_TEST(includes.size() == 4); 50 | BOOST_TEST(includes[0] == "include3.h"); 51 | BOOST_TEST(includes[1] == "include1.h"); 52 | BOOST_TEST(includes[2] == "include2.h"); 53 | BOOST_TEST(includes[3] == "main.cc"); 54 | } 55 | 56 | BOOST_AUTO_TEST_CASE(test_cyclic_graph_is_properly_sorted) 57 | { 58 | include_graph_t graph; 59 | graph.add_edge("include1.h", "main.cc", true); 60 | graph.add_edge("include2.h", "main.cc", true); 61 | graph.add_edge("include1.h", "include2.h"); 62 | graph.add_edge("include2.h", "include1.h"); 63 | 64 | graph.build_dag(); 65 | 66 | path_printer_t pp; 67 | 68 | include_graph_topological_sort_printer_t p{graph, pp}; 69 | 70 | std::stringstream ss; 71 | ss << p; 72 | 73 | std::vector includes; 74 | read_lines(ss, includes); 75 | 76 | BOOST_TEST(includes.size() == 3); 77 | BOOST_TEST((includes[0] == "include1.h" || includes[0] == "include2.h")); 78 | BOOST_TEST((includes[1] == "include2.h" || includes[1] == "include1.h")); 79 | BOOST_TEST(includes[2] == "main.cc"); 80 | } 81 | 82 | BOOST_AUTO_TEST_CASE(test_long_cyclic_graph_is_properly_sorted) 83 | { 84 | include_graph_t graph; 85 | graph.add_edge("include1.h", "main.cc", true); 86 | graph.add_edge("include1.h", "include2.h"); 87 | graph.add_edge("include2.h", "include3.h"); 88 | graph.add_edge("include3.h", "include4.h"); 89 | graph.add_edge("include4.h", "include1.h"); 90 | 91 | graph.build_dag(); 92 | 93 | path_printer_t pp; 94 | 95 | include_graph_topological_sort_printer_t p{graph, pp}; 96 | 97 | std::stringstream ss; 98 | ss << p; 99 | 100 | std::vector includes; 101 | read_lines(ss, includes); 102 | 103 | BOOST_TEST(includes.size() == 5); 104 | BOOST_TEST(includes[0] == "include2.h"); 105 | BOOST_TEST(includes[1] == "include3.h"); 106 | BOOST_TEST(includes[2] == "include4.h"); 107 | BOOST_TEST(includes[3] == "include1.h"); 108 | BOOST_TEST(includes[4] == "main.cc"); 109 | } -------------------------------------------------------------------------------- /tests/test_tree_printer.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * tests/test_tree_printer.cc 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #define BOOST_TEST_MODULE Unit test of include_graph_tree_printer_t 20 | 21 | #include 22 | 23 | #include "../src/include_graph_tree_printer.h" 24 | #include "test_utils.h" 25 | 26 | #include 27 | 28 | using namespace clang_include_graph; 29 | 30 | BOOST_AUTO_TEST_CASE(test_dag_prints_no_cycles) 31 | { 32 | include_graph_t graph; 33 | graph.add_edge("include1.h", "main.cc", true); 34 | graph.add_edge("include2.h", "main.cc", true); 35 | graph.add_edge("include3.h", "util.cc", true); 36 | graph.add_edge("include4.h", "util.cc", true); 37 | graph.add_edge("include5.h", "include4.h"); 38 | 39 | graph.build_dag(); 40 | 41 | path_printer_t pp; 42 | 43 | include_graph_tree_printer_t p{graph, pp}; 44 | 45 | std::stringstream ss; 46 | ss << p; 47 | 48 | std::vector includes; 49 | read_lines(ss, includes); 50 | 51 | BOOST_TEST(includes.size() == 7); 52 | 53 | #if !defined(_MSC_VER) 54 | BOOST_TEST(includes[0] == "main.cc"); 55 | BOOST_TEST(includes[1] == "├── include1.h"); 56 | BOOST_TEST(includes[2] == "└── include2.h"); 57 | BOOST_TEST(includes[3] == "util.cc"); 58 | BOOST_TEST(includes[4] == "├── include3.h"); 59 | BOOST_TEST(includes[5] == "└── include4.h"); 60 | BOOST_TEST(includes[6] == " └── include5.h"); 61 | #else 62 | BOOST_TEST(includes[0] == "main.cc"); 63 | BOOST_TEST(includes[1] == "+-- include1.h"); 64 | BOOST_TEST(includes[2] == "\\-- include2.h"); 65 | BOOST_TEST(includes[3] == "util.cc"); 66 | BOOST_TEST(includes[4] == "+-- include3.h"); 67 | BOOST_TEST(includes[5] == "\\-- include4.h"); 68 | BOOST_TEST(includes[6] == " \\-- include5.h"); 69 | #endif 70 | } 71 | 72 | BOOST_AUTO_TEST_CASE(test_dag_prints_with_cycles) 73 | { 74 | include_graph_t graph; 75 | graph.add_edge("include1.h", "main.cc", true); 76 | graph.add_edge("include2.h", "main.cc", true); 77 | graph.add_edge("include3.h", "util.cc", true); 78 | graph.add_edge("include4.h", "util.cc", true); 79 | graph.add_edge("include2.h", "include1.h"); 80 | graph.add_edge("include1.h", "include2.h"); 81 | 82 | graph.build_dag(); 83 | 84 | path_printer_t pp; 85 | 86 | include_graph_tree_printer_t p{graph, pp}; 87 | 88 | std::stringstream ss; 89 | ss << p; 90 | 91 | std::vector includes; 92 | read_lines(ss, includes); 93 | 94 | BOOST_TEST(includes.size() == 7); 95 | #if !defined(_MSC_VER) 96 | BOOST_TEST(includes[0] == "main.cc"); 97 | BOOST_TEST(includes[1] == "├── include1.h"); 98 | BOOST_TEST(includes[2] == "│ └── include2.h"); 99 | BOOST_TEST(includes[3] == "└── include2.h"); 100 | BOOST_TEST(includes[4] == "util.cc"); 101 | BOOST_TEST(includes[5] == "├── include3.h"); 102 | BOOST_TEST(includes[6] == "└── include4.h"); 103 | #else 104 | BOOST_TEST(includes[0] == "main.cc"); 105 | BOOST_TEST(includes[1] == "+-- include1.h"); 106 | BOOST_TEST(includes[2] == "I \\-- include2.h"); 107 | BOOST_TEST(includes[3] == "\\-- include2.h"); 108 | BOOST_TEST(includes[4] == "util.cc"); 109 | BOOST_TEST(includes[5] == "+-- include3.h"); 110 | BOOST_TEST(includes[6] == "\\-- include4.h"); 111 | #endif 112 | } 113 | 114 | BOOST_AUTO_TEST_CASE(test_dag_prints_reverse_tree) 115 | { 116 | config_t config; 117 | config.printer(printer_t::reverse_tree); 118 | 119 | include_graph_t graph; 120 | graph.init(config); 121 | graph.add_edge("include1.h", "main.cc", true); 122 | graph.add_edge("include2.h", "main.cc", true); 123 | graph.add_edge("include3.h", "main.cc", true); 124 | graph.add_edge("include5.h", "main.cc", true); 125 | graph.add_edge("include4.h", "include5.h"); 126 | 127 | graph.build_dag(); 128 | 129 | path_printer_t pp; 130 | 131 | include_graph_tree_printer_t p{graph, pp}; 132 | 133 | std::stringstream ss; 134 | ss << p; 135 | 136 | std::vector includes; 137 | read_lines(ss, includes); 138 | 139 | BOOST_TEST(includes.size() == 9); 140 | #if !defined(_MSC_VER) 141 | BOOST_TEST(includes[0] == "include1.h"); 142 | BOOST_TEST(includes[1] == "└── main.cc"); 143 | BOOST_TEST(includes[2] == "include2.h"); 144 | BOOST_TEST(includes[3] == "└── main.cc"); 145 | BOOST_TEST(includes[4] == "include3.h"); 146 | BOOST_TEST(includes[5] == "└── main.cc"); 147 | BOOST_TEST(includes[6] == "include4.h"); 148 | BOOST_TEST(includes[7] == "└── include5.h"); 149 | BOOST_TEST(includes[8] == " └── main.cc"); 150 | #else 151 | BOOST_TEST(includes[0] == "include1.h"); 152 | BOOST_TEST(includes[1] == "\\-- main.cc"); 153 | BOOST_TEST(includes[2] == "include2.h"); 154 | BOOST_TEST(includes[3] == "\\-- main.cc"); 155 | BOOST_TEST(includes[4] == "include3.h"); 156 | BOOST_TEST(includes[5] == "\\-- main.cc"); 157 | BOOST_TEST(includes[6] == "include4.h"); 158 | BOOST_TEST(includes[7] == "\\-- include5.h"); 159 | BOOST_TEST(includes[8] == " \\-- main.cc"); 160 | #endif 161 | } -------------------------------------------------------------------------------- /tests/test_util.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * tests/test_cycles_printer.cc 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #define BOOST_TEST_MODULE Unit test of utility functions 20 | 21 | #include 22 | 23 | #include "../src/include_graph_cycles_printer.h" 24 | 25 | using namespace clang_include_graph::util; 26 | 27 | BOOST_AUTO_TEST_CASE(test_glob_to_regex) 28 | { 29 | BOOST_TEST(match_flag_glob("-Wall", "-Wall")); 30 | BOOST_TEST(match_flag_glob("-Wall", "-W*")); 31 | BOOST_TEST(match_flag_glob("-Wall", "-Wa*")); 32 | BOOST_TEST(!match_flag_glob("-Wall", "-Wno-*")); 33 | } -------------------------------------------------------------------------------- /tests/test_utils.h: -------------------------------------------------------------------------------- 1 | /** 2 | * tests/test_utils.h 3 | * 4 | * Copyright (c) 2022-present Bartek Kryza 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #ifndef CLANG_INCLUDE_GRAPH_TEST_UTILS_H 20 | #define CLANG_INCLUDE_GRAPH_TEST_UTILS_H 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | namespace clang_include_graph { 27 | 28 | void read_lines(std::istream &s, std::vector &o) 29 | { 30 | while (!s.fail()) { 31 | std::string l; 32 | std::getline(s, l); 33 | if (!l.empty()) 34 | o.push_back(l); 35 | } 36 | } 37 | 38 | } 39 | #endif // CLANG_INCLUDE_GRAPH_TEST_UTILS_H 40 | -------------------------------------------------------------------------------- /thirdparty/glob/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Pranav 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /util/check_formatting.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ## 4 | ## util/check_formatting.sh 5 | ## 6 | ## Copyright (c) 2021-2025 Bartek Kryza 7 | ## 8 | ## Licensed under the Apache License, Version 2.0 (the "License"); 9 | ## you may not use this file except in compliance with the License. 10 | ## You may obtain a copy of the License at 11 | ## 12 | ## http://www.apache.org/licenses/LICENSE-2.0 13 | ## 14 | ## Unless required by applicable law or agreed to in writing, software 15 | ## distributed under the License is distributed on an "AS IS" BASIS, 16 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | ## See the License for the specific language governing permissions and 18 | ## limitations under the License. 19 | ## 20 | 21 | shopt -s globstar 22 | 23 | include_file_list() { 24 | cat .clang-format-include | grep "^\+" | awk '{print $2}' \ 25 | | tr [:space:] '\n' | sort | uniq 26 | } 27 | 28 | ignore_file_list() { 29 | echo $(cat .clang-format-include | grep "^\-" | awk '{print $2}') \ 30 | $(git ls-files --others --exclude-standard --ignored) \ 31 | | tr [:space:] '\n' | sort | uniq 32 | } 33 | 34 | valid_ignore_file_list() { 35 | echo $(include_file_list) $(ignore_file_list) \ 36 | | tr [:space:] '\n' | sort | uniq -d 37 | } 38 | 39 | effective_file_list() { 40 | echo $(include_file_list) $(valid_ignore_file_list) \ 41 | | tr [:space:] '\n' | sort | uniq -u 42 | } 43 | 44 | GIT_STATUS=$(git diff-index --quiet HEAD --) 45 | 46 | EFFECTIVE_FILE_LIST=$(effective_file_list) 47 | 48 | if [[ ${#EFFECTIVE_FILE_LIST[@]} -eq 0 ]]; then 49 | echo ".clang-format-include patterns did not match any files." 50 | exit 0 51 | else 52 | clang-format-18 --dry-run --Werror ${EFFECTIVE_FILE_LIST} 53 | fi -------------------------------------------------------------------------------- /util/msbuild_compile_commands_logger/CompileCommandsLogger.cs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2024-present Bartek Kryza 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | using System; 24 | using System.Collections.Generic; 25 | using System.IO; 26 | using System.Runtime.InteropServices; 27 | using System.Security; 28 | using System.Text; 29 | using System.Web; 30 | using Microsoft.Build.Framework; 31 | using Microsoft.Build.Utilities; 32 | 33 | /// 34 | /// MSBuild logger plugin which generates compile_commands.json file. 35 | /// 36 | /// 37 | /// Based on https://github.com/0xabu/MsBuildCompileCommandsJson, but significantly simplified and customized 38 | /// for clang-uml. 39 | /// 40 | public class CompileCommandsLogger : Logger 41 | { 42 | private static readonly string CCompilerFrontend = "clang.exe"; 43 | private static readonly string CPPCompilerFrontend = "clang++.exe"; 44 | 45 | private static readonly string[] SourceFileTypeQualifierFlags = {"/Tc", "/Tp"}; 46 | 47 | private static readonly string[] CppExtensions = { ".cpp", ".cxx", ".cc", ".c" }; 48 | 49 | public override void Initialize(IEventSource eventSource) 50 | { 51 | string outputFilePath = "compile_commands.json.tmp"; 52 | 53 | try 54 | { 55 | const bool append = false; 56 | Encoding utf8WithoutBom = new UTF8Encoding(false); 57 | this.streamWriter = new StreamWriter(outputFilePath, append, utf8WithoutBom); 58 | this.firstLine = true; 59 | streamWriter.WriteLine("["); 60 | } 61 | catch (Exception ex) 62 | { 63 | if (ex is UnauthorizedAccessException 64 | || ex is ArgumentNullException 65 | || ex is PathTooLongException 66 | || ex is DirectoryNotFoundException 67 | || ex is NotSupportedException 68 | || ex is ArgumentException 69 | || ex is SecurityException 70 | || ex is IOException) 71 | { 72 | throw new LoggerException("Failed to create " + outputFilePath + ": " + ex.Message); 73 | } 74 | else 75 | { 76 | // Unexpected failure 77 | throw; 78 | } 79 | } 80 | 81 | eventSource.AnyEventRaised += EventSource_AnyEventRaised; 82 | } 83 | 84 | private void EventSource_AnyEventRaised(object sender, BuildEventArgs args) 85 | { 86 | if (args is TaskCommandLineEventArgs taskArgs && taskArgs.TaskName == "CL") 87 | { 88 | const string clExe = "cl.exe "; 89 | int clExeIndex = taskArgs.CommandLine.ToLower().IndexOf(clExe); 90 | if (clExeIndex == -1) 91 | { 92 | throw new LoggerException("Unexpected lack of CL.exe in " + taskArgs.CommandLine); 93 | } 94 | 95 | string compilerPath = taskArgs.CommandLine.Substring(0, clExeIndex + clExe.Length - 1).Replace("\\", "/"); 96 | string argsString = taskArgs.CommandLine.Substring(clExeIndex + clExe.Length).TrimStart(); 97 | string[] cmdArgs = CommandLineToArgs(argsString); 98 | 99 | List filenames = new List(); 100 | List compileFlags = new List(); 101 | 102 | // Here we assume that source files are at the end of the command and are absolute 103 | int argIndex = cmdArgs.Length - 1; 104 | for(;argIndex >= 0; argIndex--) { 105 | if(Array.IndexOf(SourceFileTypeQualifierFlags, cmdArgs[argIndex]) > -1) { 106 | continue; 107 | } 108 | else if(IsCPPSourcePath(cmdArgs[argIndex])) { 109 | filenames.Add(cmdArgs[argIndex]); 110 | } 111 | else { 112 | break; 113 | } 114 | } 115 | 116 | for(int i = 0; i < argIndex; i++) { 117 | if(cmdArgs[i] == "/I") { 118 | compileFlags.Add("-I"); 119 | compileFlags.Add(NormalizeCommandArgument(cmdArgs[i+1])); 120 | i++; 121 | 122 | continue; 123 | } 124 | 125 | if(cmdArgs[i].StartsWith("/I")) { 126 | compileFlags.Add("-I"); 127 | compileFlags.Add(NormalizeCommandArgument(cmdArgs[i].Substring(2))); 128 | 129 | continue; 130 | } 131 | 132 | if(cmdArgs[i] == "/external:I") { 133 | compileFlags.Add("-isystem"); 134 | compileFlags.Add(NormalizeCommandArgument(cmdArgs[i+1])); 135 | i++; 136 | 137 | continue; 138 | } 139 | 140 | if(cmdArgs[i].StartsWith("/external:I")) { 141 | compileFlags.Add("-isystem"); 142 | compileFlags.Add(NormalizeCommandArgument(cmdArgs[i].Substring(2))); 143 | 144 | continue; 145 | } 146 | 147 | if(cmdArgs[i] == "/D") { 148 | compileFlags.Add("-D" + cmdArgs[i+1]); 149 | i++; 150 | 151 | continue; 152 | } 153 | 154 | if(cmdArgs[i].StartsWith("/D")) { 155 | compileFlags.Add("-D" + cmdArgs[i].Substring(2)); 156 | 157 | continue; 158 | } 159 | 160 | if(cmdArgs[i] == "/std:c++14") { 161 | compileFlags.Add("-std=c++14"); 162 | continue; 163 | } 164 | 165 | if(cmdArgs[i] == "/std:c++17") { 166 | compileFlags.Add("-std=c++17"); 167 | continue; 168 | } 169 | 170 | if(cmdArgs[i] == "/std:c++20") { 171 | compileFlags.Add("-std=c++20"); 172 | continue; 173 | } 174 | 175 | if(cmdArgs[i] == "/std:c++23") { 176 | compileFlags.Add("-std=c++23"); 177 | continue; 178 | } 179 | } 180 | 181 | filenames.Reverse(); 182 | 183 | // simplify the compile command to avoid .. etc. 184 | string dirname = Path.GetDirectoryName(taskArgs.ProjectFile); 185 | 186 | // For each source file, emit a JSON entry 187 | foreach (string filename in filenames) 188 | { 189 | // Terminate the preceding entry 190 | if (firstLine) 191 | { 192 | firstLine = false; 193 | } 194 | else 195 | { 196 | streamWriter.WriteLine(" ,"); 197 | } 198 | 199 | string directoryPath = HttpUtility.JavaScriptStringEncode(dirname.Replace("\\", "/")); 200 | 201 | string filePath = HttpUtility.JavaScriptStringEncode(NormalizePath(filename)); 202 | 203 | string compileCommand = HttpUtility.JavaScriptStringEncode(GetCompilerFrontend(filename) + " " + String.Join(" ", compileFlags)); 204 | 205 | string compileCommandWithFilename = compileCommand + " " + filePath; 206 | 207 | // Write one entry 208 | streamWriter.WriteLine(" {"); 209 | 210 | streamWriter.WriteLine(String.Format( " \"directory\": \"{0}\",", directoryPath)); 211 | 212 | streamWriter.Write(" \"arguments\": ["); 213 | 214 | streamWriter.Write(string.Format("\"{0}\"", GetCompilerFrontend(filename))); 215 | foreach (string arg in compileFlags) { 216 | streamWriter.Write(string.Format(", \"{0}\"", HttpUtility.JavaScriptStringEncode(arg))); 217 | } 218 | streamWriter.Write(string.Format(", \"{0}\"", filename.Replace("\\", "/"))); 219 | 220 | streamWriter.WriteLine("],"); 221 | 222 | streamWriter.Write(String.Format(" \"file\": \"{0}\"", filename.Replace("\\", "/"))); 223 | 224 | streamWriter.WriteLine(""); 225 | streamWriter.WriteLine(" }"); 226 | } 227 | } 228 | } 229 | 230 | [DllImport("shell32.dll", SetLastError = true)] 231 | static extern IntPtr CommandLineToArgvW( 232 | [MarshalAs(UnmanagedType.LPWStr)] string lpCmdLine, out int pNumArgs); 233 | 234 | static string[] CommandLineToArgs(string commandLine) 235 | { 236 | int argc; 237 | var argv = CommandLineToArgvW(commandLine, out argc); 238 | if (argv == IntPtr.Zero) 239 | throw new System.ComponentModel.Win32Exception(); 240 | try 241 | { 242 | var args = new string[argc]; 243 | for (var i = 0; i < args.Length; i++) 244 | { 245 | var p = Marshal.ReadIntPtr(argv, i * IntPtr.Size); 246 | args[i] = Marshal.PtrToStringUni(p); 247 | } 248 | 249 | return args; 250 | } 251 | finally 252 | { 253 | Marshal.FreeHGlobal(argv); 254 | } 255 | } 256 | 257 | public static string GetCompilerFrontend(string filePath) { 258 | string extension = Path.GetExtension(filePath).ToLowerInvariant(); 259 | if(extension == ".c") { 260 | return CCompilerFrontend; 261 | } 262 | 263 | return CPPCompilerFrontend; 264 | } 265 | 266 | public static bool IsCPPSourcePath(string filePath) 267 | { 268 | try { 269 | if (string.IsNullOrWhiteSpace(filePath)) 270 | { 271 | return false; 272 | } 273 | 274 | if(!Path.IsPathRooted(filePath)) { 275 | return false; 276 | } 277 | 278 | if(!File.Exists(filePath)) { 279 | return false; 280 | } 281 | 282 | string extension = Path.GetExtension(filePath).ToLowerInvariant(); 283 | foreach (string cppExt in CppExtensions) 284 | { 285 | if (extension == cppExt) 286 | { 287 | return true; 288 | } 289 | } 290 | } 291 | catch(Exception) { 292 | // Ignore non-paths 293 | } 294 | 295 | return false; 296 | } 297 | 298 | public static string NormalizeCommandArgument(string input) { 299 | string result = input.Replace("\\", "/"); 300 | 301 | if (string.IsNullOrEmpty(result)) 302 | { 303 | return result; 304 | } 305 | 306 | return result; 307 | } 308 | 309 | public static string NormalizePath(string input) 310 | { 311 | string result = input.Replace("\\", "/"); 312 | 313 | if (string.IsNullOrEmpty(result)) 314 | { 315 | return result; 316 | } 317 | 318 | if (!result.StartsWith("\"")) 319 | { 320 | result = "\"" + result; 321 | } 322 | 323 | if (!result.EndsWith("\"")) 324 | { 325 | result = result + "\""; 326 | } 327 | 328 | return result; 329 | } 330 | 331 | public override void Shutdown() 332 | { 333 | streamWriter.WriteLine("]"); 334 | streamWriter.Close(); 335 | 336 | // Do not overwrite existing compile_commands.json 337 | if (!File.Exists("compile_commands.json")) 338 | { 339 | File.Move("compile_commands.json.tmp", "compile_commands.json"); 340 | } 341 | 342 | base.Shutdown(); 343 | } 344 | 345 | private StreamWriter streamWriter; 346 | private bool firstLine; 347 | } 348 | -------------------------------------------------------------------------------- /util/msbuild_compile_commands_logger/CompileCommandsLogger.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | c 6 | 7 | MSBuild logger plugin which generates compile_commands.json file. 8 | MIT 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /util/test_llvm_versions.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | trap 'echo "Build failed!"' ERR 5 | 6 | declare -a llvm_configs=("llvm-config-12" 7 | "llvm-config-13" 8 | "llvm-config-14" 9 | "llvm-config-15" 10 | "llvm-config-16" 11 | "llvm-config-17" 12 | "llvm-config-18" 13 | "llvm-config-19" 14 | "llvm-config-20" ) 15 | 16 | # Test with GCC and different LLVM versions 17 | for config_path in ${llvm_configs[@]}; do 18 | echo "---------------------------------------------------------" 19 | echo " Running clang-include-graph tests against LLVM $(${config_path} --version)" 20 | echo "---------------------------------------------------------" 21 | make clean 22 | CC=/usr/bin/gcc-13 CXX=/usr/bin/g++-13 LLVM_CONFIG_PATH=$config_path NUMPROC=16 make test 23 | done 24 | 25 | # Also check compilation with Clang 26 | make clean 27 | CC=/usr/bin/clang-17 CXX=/usr/bin/clang++-17 LLVM_VERSION=17 NUMPROC=16 make test 28 | make clean 29 | CC=/usr/bin/clang-18 CXX=/usr/bin/clang++-18 LLVM_VERSION=18 NUMPROC=16 make test 30 | make clean 31 | CC=/usr/bin/clang-19 CXX=/usr/bin/clang++-19 LLVM_VERSION=19 NUMPROC=16 make test 32 | make clean 33 | CC=/usr/bin/clang-20 CXX=/usr/bin/clang++-20 LLVM_VERSION=20 NUMPROC=16 make test 34 | make clean --------------------------------------------------------------------------------