-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCMakeLists.txt
More file actions
193 lines (169 loc) · 9.31 KB
/
CMakeLists.txt
File metadata and controls
193 lines (169 loc) · 9.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
cmake_minimum_required(VERSION 3.16)
project(PagurusClangPlugin C CXX)
# ── Locate LLVM / Clang ───────────────────────────────────────────────────────
# Supports Clang/LLVM 11 through 18.
# Tested with: llvm-11-dev through llvm-18-dev
# Install on Debian/Ubuntu:
# sudo apt install libclang-18-dev llvm-18-dev
find_package(LLVM REQUIRED CONFIG)
find_package(Clang REQUIRED CONFIG)
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
message(STATUS " LLVM_INCLUDE_DIRS : ${LLVM_INCLUDE_DIRS}")
message(STATUS " LLVM_LIBRARY_DIRS : ${LLVM_LIBRARY_DIRS}")
message(STATUS " Clang includes : ${CLANG_INCLUDE_DIRS}")
# Require C++17 (needed for structured bindings, std::optional, inline variables).
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# ── Optional: LLVM IR pass ────────────────────────────────────────────────────
# The IR pass (-fpass-plugin=) provides deeper alias/MemorySSA analysis but
# requires llvm/IR and llvm/Analysis headers (included in llvm-18-dev).
# Disable to build the AST-only plugin with reduced header requirements.
#
# To disable: cmake -DPAGURUS_WITH_IR_PASS=OFF ..
option(PAGURUS_WITH_IR_PASS "Build the LLVM IR borrow-check pass (-fpass-plugin=)" ON)
if(NOT PAGURUS_WITH_IR_PASS)
message(STATUS "LLVM IR pass disabled (PAGURUS_WITH_IR_PASS=OFF) — AST-only build")
endif()
# ── Optional: MLIR ────────────────────────────────────────────────────────────
# Requires mlir-dev (not part of standard llvm-dev on most distros).
# When MLIR is found, enables the memref-dialect borrow analysis pass.
#
# Install (Ubuntu 22.04 with LLVM PPA):
# sudo apt install libmlir-18-dev mlir-18-tools
#
# To enable: cmake -DPAGURUS_WITH_MLIR=ON -DMLIR_DIR=<mlir-cmake-dir> ..
option(PAGURUS_WITH_MLIR "Build with MLIR memref dialect analysis" OFF)
if(PAGURUS_WITH_MLIR)
find_package(MLIR CONFIG REQUIRED)
message(STATUS "Found MLIR ${MLIR_PACKAGE_VERSION}")
message(STATUS " MLIR_INCLUDE_DIRS: ${MLIR_INCLUDE_DIRS}")
add_compile_definitions(PAGURUS_WITH_MLIR=1)
endif()
# ── LLVM analysis libraries required for the IR pass ─────────────────────────
# The -fpass-plugin= entry point (PagurusIRPass) requires:
# LLVMPasses : PassBuilder, PassPlugin, FunctionPassManager
# LLVMAnalysis : AliasAnalysis, BasicAA, MemorySSA, DominatorTree, CallGraph
# LLVMCore : IR types (Function, BasicBlock, Instruction, …)
# LLVMSupport : raw_ostream, StringRef, SmallVector, …
# LLVMTransformUtils : createModuleToFunctionPassAdaptor
# These are resolved against the SHARED LLVM DSO (libLLVM-<ver>.so) to avoid
# symbol conflicts when the same LLVM is linked into both clang and the plugin.
if(PAGURUS_WITH_IR_PASS)
llvm_map_components_to_libnames(LLVM_IR_LIBS
Passes Analysis Core Support TransformUtils
)
endif()
# ── Clang libraries required for the AST plugin ───────────────────────────────
# clangAST / clangFrontend / clangAnalysis are NOT linked directly because
# a Clang frontend plugin is loaded into an already-running clang process and
# must not double-link Clang symbols. The symbols are resolved at dlopen-time
# from the host clang executable. Listing them here as INTERFACE dependencies
# ensures the compiler finds the headers but does not emit link entries.
# (clangBasic is the only one that may need static linking for diagnostics.)
# ── The pagurus_plugin shared library ────────────────────────────────────────
add_library(pagurus_plugin MODULE src/pagurus_plugin.cpp)
target_include_directories(pagurus_plugin PRIVATE
${LLVM_INCLUDE_DIRS}
${CLANG_INCLUDE_DIRS}
)
if(PAGURUS_WITH_MLIR)
target_include_directories(pagurus_plugin PRIVATE ${MLIR_INCLUDE_DIRS})
# Link the MLIR umbrella shared library (libMLIR.so). Unlike LLVM and
# Clang, MLIR is NOT embedded in the clang binary, so the plugin must carry
# the symbols itself. Using the shared library avoids the symbol-bloat and
# double-registration issues that come with linking many static MLIR .a
# archives. The `MLIR` cmake target is the SHARED IMPORTED target set up
# by find_package(MLIR).
target_link_libraries(pagurus_plugin PRIVATE MLIR)
endif()
separate_arguments(LLVM_DEFINITIONS_LIST UNIX_COMMAND "${LLVM_DEFINITIONS}")
target_compile_definitions(pagurus_plugin PRIVATE ${LLVM_DEFINITIONS_LIST})
if(NOT PAGURUS_WITH_IR_PASS)
target_compile_definitions(pagurus_plugin PRIVATE PAGURUS_NO_IR_PASS=1)
endif()
# IMPORTANT: Do NOT link LLVM or Clang libraries statically into the plugin.
# A Clang frontend plugin (-fplugin=) is loaded into an already-running clang
# process that already has all LLVM/Clang symbols. Linking them again causes
# double-registration of CommandLine options (fatal error at load time).
# Instead we use `--allow-shlib-undefined` so the linker accepts unresolved
# symbols that will be provided by the host clang at dlopen-time.
#
# For the IR pass (-fpass-plugin=), LLVM symbols are also resolved from the
# host clang/opt process, so no special linking is needed there either.
target_link_options(pagurus_plugin PRIVATE
"-Wl,--allow-shlib-undefined"
# Keep the plugin in memory after it's loaded (needed for static initializers).
"-Wl,-z,nodelete"
)
# Plugin ABI flags: no SONAME prefix, hidden visibility for private symbols.
set_target_properties(pagurus_plugin PROPERTIES
PREFIX ""
SUFFIX ".so"
CXX_VISIBILITY_PRESET hidden
VISIBILITY_INLINES_HIDDEN ON
)
# Suppress -Wno-deprecated-declarations for FileEntry::getName() on Clang < 16.
target_compile_options(pagurus_plugin PRIVATE -Wno-deprecated-declarations)
# ── Install ───────────────────────────────────────────────────────────────────
install(TARGETS pagurus_plugin LIBRARY DESTINATION lib)
# ── CTest integration ─────────────────────────────────────────────────────────
# Enable CTest. Each .c file in tests/ is registered as an individual
# test so that `ctest` (or `ctest -R pagurus_plugin`) gives per-test visibility.
#
# Build and test (replace 18 with 11–17 as needed):
# cmake -DLLVM_DIR=$(llvm-config-18 --cmakedir) \
# -DClang_DIR=/usr/lib/llvm-18/lib/cmake/clang ..
# cmake --build .
# ctest --output-on-failure
include(CTest)
if(BUILD_TESTING)
# Locate the clang executable that matches the LLVM version we compiled with.
find_program(CLANG_EXECUTABLE
NAMES clang-${LLVM_VERSION_MAJOR} clang
HINTS "${LLVM_TOOLS_BINARY_DIR}"
DOC "Clang executable used to run plugin tests"
)
if(CLANG_EXECUTABLE)
set(PAGURUS_TEST_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tests")
set(PAGURUS_TEST_RUNNER "${PAGURUS_TEST_DIR}/run_tests.sh")
# Glob all .c test files so new ones are picked up automatically.
# Exclude *.pagurus.c files — those are compile-mode output artefacts
# produced by the plugin itself and are not test inputs.
file(GLOB PAGURUS_TEST_SOURCES "${PAGURUS_TEST_DIR}/*.c")
list(FILTER PAGURUS_TEST_SOURCES EXCLUDE REGEX "\\.pagurus\\.c$")
foreach(test_src IN LISTS PAGURUS_TEST_SOURCES)
get_filename_component(test_name "${test_src}" NAME_WE)
add_test(
NAME "pagurus_plugin_${test_name}"
COMMAND bash "${PAGURUS_TEST_RUNNER}"
"$<TARGET_FILE:pagurus_plugin>"
"${test_src}"
)
set_tests_properties("pagurus_plugin_${test_name}" PROPERTIES
ENVIRONMENT "CLANG=${CLANG_EXECUTABLE}"
)
endforeach()
# ── Multi-file / whole-project integration tests ──────────────────────
# Tests that verify pagurus-check and pagurus.mk work correctly across
# a small multi-translation-unit project (tests/multifile/).
set(PAGURUS_MULTIFILE_RUNNER "${PAGURUS_TEST_DIR}/run_multifile_tests.sh")
set(PAGURUS_MULTIFILE_DIR "${PAGURUS_TEST_DIR}/multifile")
add_test(
NAME "pagurus_multifile"
COMMAND bash "${PAGURUS_MULTIFILE_RUNNER}"
"$<TARGET_FILE:pagurus_plugin>"
"${PAGURUS_MULTIFILE_DIR}"
)
set_tests_properties("pagurus_multifile" PROPERTIES
ENVIRONMENT "CLANG=${CLANG_EXECUTABLE}"
)
message(STATUS
"Plugin tests registered: ${CMAKE_CURRENT_BINARY_DIR} "
"(run with: ctest --output-on-failure)")
else()
message(STATUS
"Clang executable not found — plugin tests will not be registered. "
"Set CLANG_EXECUTABLE or ensure clang is on PATH.")
endif()
endif()