cmake_minimum_required(VERSION 3.20)
# C is enabled (not just CXX) because LLVM's `find_package(LLVM)` invokes
# `check_c_source_compiles` via FindFFI/FindLibEdit/FindTerminfo. Without
# C, the LLVM config aborts the configure step.
project(stig_code_analyzer LANGUAGES C CXX)

# ─────────────────────────────────────────────────────────────────────────
# Toolchain
#
# C++17 to match PhASAR's surface. RTTI must be ON because PhASAR + SVF
# both use dynamic_cast extensively across plugin boundaries.
# ─────────────────────────────────────────────────────────────────────────
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)

if (NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
endif ()

# Export compile_commands.json for clangd / IDE integration.
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# ─────────────────────────────────────────────────────────────────────────
# Version string.
#
# When Gradle drives the build, it passes -DSTIG_CODE_ANALYZER_VERSION=...
# resolved from `project.version` (axion-release). When invoked via plain
# `cmake -S ... -B ...` (developer working directly in src/main/cpp/),
# the variable is unset and we fall back to "dev". That string is
# baked into the binary via target_compile_definitions below and
# surfaced through `stig-code-analyzer --version`.
# ─────────────────────────────────────────────────────────────────────────
if (NOT DEFINED STIG_CODE_ANALYZER_VERSION OR STIG_CODE_ANALYZER_VERSION STREQUAL "")
    set(STIG_CODE_ANALYZER_VERSION "dev")
endif ()
message(STATUS "stig-code-analyzer: version = ${STIG_CODE_ANALYZER_VERSION}")

# ─────────────────────────────────────────────────────────────────────────
# Dependencies
#
# LLVM 18 + PhASAR + SVF come from the host (installed by scripts/setup.sh
# into known prefixes — see scripts/pins.env). Header-only deps (CLI11,
# nlohmann/json, GoogleTest later) are vendored via FetchContent at pinned
# tags so the same versions land on every machine.
# ─────────────────────────────────────────────────────────────────────────
include(cmake/dependencies.cmake)

# Phase 1: link LLVM (proves the toolchain is wired correctly). PhASAR +
# SVF wiring lands in Phase 2 once main.cpp actually instantiates a
# HelperAnalyses + IDESolver.
#
# LLVM major pinned in scripts/pins.env (`LLVM_MAJOR=18`). We do NOT pass
# a version number to `find_package` here because LLVM's installed
# `LLVMConfigVersion.cmake` requires callers to specify major.minor with
# patch ≥ installed-patch — too brittle (any Fedora/Ubuntu point-release
# bump would break it). Instead we point CMake at the right install via
# -DLLVM_DIR=... (set by build.gradle.kts's autodetect or by the
# CMakePresets dev preset) and validate the major version ourselves.
# Keep this STIG_REQUIRED_LLVM_MAJOR in sync with pins.env.
set(STIG_REQUIRED_LLVM_MAJOR 18)
find_package(LLVM REQUIRED CONFIG)
if (NOT LLVM_VERSION_MAJOR STREQUAL "${STIG_REQUIRED_LLVM_MAJOR}")
    message(FATAL_ERROR
            "stig-code-analyzer requires LLVM ${STIG_REQUIRED_LLVM_MAJOR}.x; "
            "found LLVM ${LLVM_PACKAGE_VERSION} at ${LLVM_DIR}. "
            "Point CMake at the right install via -DLLVM_DIR=/path/to/llvm${STIG_REQUIRED_LLVM_MAJOR}/lib/cmake/llvm "
            "or update scripts/pins.env if the pin should move."
    )
endif ()
message(STATUS "stig-code-analyzer: found LLVM ${LLVM_PACKAGE_VERSION} at ${LLVM_DIR}")

# ─────────────────────────────────────────────────────────────────────────
# Targets
# ─────────────────────────────────────────────────────────────────────────
add_executable(stig-code-analyzer
        src/main.cpp
)

target_include_directories(stig-code-analyzer PRIVATE
        ${LLVM_INCLUDE_DIRS}
)

target_compile_definitions(stig-code-analyzer PRIVATE
        ${LLVM_DEFINITIONS}
        STIG_CODE_ANALYZER_VERSION="${STIG_CODE_ANALYZER_VERSION}"
)

# LLVM's Support library carries demangle, raw_ostream, etc. — useful from
# the very first --help build so we have a known-good link line.
llvm_map_components_to_libnames(LLVM_LIBS Support)

target_link_libraries(stig-code-analyzer PRIVATE
        CLI11::CLI11
        ${LLVM_LIBS}
)

# Hand the binary the same RPATH the PhASAR/SVF install uses so it can
# find libphasar.so / libSvfLLVM.so at runtime without LD_LIBRARY_PATH
# gymnastics. The actual install prefixes come from scripts/setup.sh.
set_target_properties(stig-code-analyzer PROPERTIES
        INSTALL_RPATH_USE_LINK_PATH ON
        BUILD_RPATH_USE_ORIGIN ON
)

install(TARGETS stig-code-analyzer RUNTIME DESTINATION bin)
