Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
build:
name: build (${{ matrix.os }} / ${{ matrix.compiler }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-24.04
compiler: clang-18
cc: clang-18
cxx: clang++-18
extra_apt: clang-18 libc++-18-dev libc++abi-18-dev
cxxflags: "-stdlib=libc++"
ldflags: "-stdlib=libc++"
- os: ubuntu-24.04
compiler: gcc-14
cc: gcc-14
cxx: g++-14
extra_apt: gcc-14 g++-14
cxxflags: ""
ldflags: ""
- os: macos-14
compiler: brew-llvm
cc: brew-clang
cxx: brew-clang++
extra_apt: ""
cxxflags: ""
ldflags: ""
steps:
- uses: actions/checkout@v4

- name: Install Linux toolchain
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends ${{ matrix.extra_apt }} cmake ninja-build
echo "CC=${{ matrix.cc }}" >> "$GITHUB_ENV"
echo "CXX=${{ matrix.cxx }}" >> "$GITHUB_ENV"

- name: Install macOS toolchain
if: runner.os == 'macOS'
run: |
brew update
brew install llvm cmake ninja
LLVM_PREFIX="$(brew --prefix llvm)"
echo "CC=$LLVM_PREFIX/bin/clang" >> "$GITHUB_ENV"
echo "CXX=$LLVM_PREFIX/bin/clang++" >> "$GITHUB_ENV"

- name: Configure
env:
CXXFLAGS: ${{ matrix.cxxflags }}
LDFLAGS: ${{ matrix.ldflags }}
run: |
cmake -S . -B build -G Ninja \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DMNE_BUILD_TESTS=ON

- name: Build
run: cmake --build build -j

- name: Run smoke binary
run: ./build/merlion-node-exporter --version

- name: Run tests
run: ctest --test-dir build --output-on-failure

clang-format:
name: clang-format
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Install clang-format
run: sudo apt-get update && sudo apt-get install -y clang-format-18
# clang-format-18 supports c++20 mode used by our .clang-format
- name: Check formatting
run: |
files=$(git ls-files '*.cpp' '*.hpp' '*.hpp.in' || true)
if [ -n "$files" ]; then
clang-format-18 --dry-run --Werror $files
fi
186 changes: 186 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
cmake_minimum_required(VERSION 3.28)

project(merlion-node-exporter-cpp
VERSION 0.1.0
DESCRIPTION "Modern C++23 reimplementation of Prometheus node_exporter."
HOMEPAGE_URL "https://github.com/MerlionOS/merlion-node-exporter-cpp"
LANGUAGES CXX
)

# ---------------------------------------------------------------------------
# Project options
# ---------------------------------------------------------------------------
option(MNE_BUILD_TESTS "Build Catch2 unit tests" ON)
option(MNE_ENABLE_LTO "Enable link-time optimisation in release builds" ON)
option(MNE_WARNINGS_AS_ERRORS "Treat compiler warnings as errors" ON)

set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE Release CACHE STRING "" FORCE)
endif()

# ---------------------------------------------------------------------------
# Compiler sanity check
# ---------------------------------------------------------------------------
# Apple Clang's libc++ still lags on std::expected / std::format. Fail fast
# instead of silently producing a broken build.
if(APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
message(FATAL_ERROR
"Apple Clang is not supported. Install Homebrew LLVM and re-configure:\n"
" brew install llvm\n"
" export CC=\"$(brew --prefix llvm)/bin/clang\"\n"
" export CXX=\"$(brew --prefix llvm)/bin/clang++\"\n"
"Or pass -DCMAKE_TOOLCHAIN_FILE=cmake/ToolchainLLVM.cmake."
)
endif()

# ---------------------------------------------------------------------------
# Warnings
# ---------------------------------------------------------------------------
add_library(mne_compile_flags INTERFACE)
target_compile_features(mne_compile_flags INTERFACE cxx_std_23)
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
target_compile_options(mne_compile_flags INTERFACE
-Wall -Wextra -Wpedantic
-Wshadow -Wconversion -Wsign-conversion
-Wno-unused-parameter
)
if(MNE_WARNINGS_AS_ERRORS)
target_compile_options(mne_compile_flags INTERFACE -Werror)
endif()
endif()

# Release-only flags.
add_library(mne_release_flags INTERFACE)
target_compile_options(mne_release_flags INTERFACE
$<$<CONFIG:Release,RelWithDebInfo>:-O3>
$<$<CONFIG:Release,RelWithDebInfo>:-ffunction-sections>
$<$<CONFIG:Release,RelWithDebInfo>:-fdata-sections>
)
if(UNIX AND NOT APPLE)
target_link_options(mne_release_flags INTERFACE
$<$<CONFIG:Release,RelWithDebInfo>:LINKER:--gc-sections>
)
elseif(APPLE)
target_link_options(mne_release_flags INTERFACE
$<$<CONFIG:Release,RelWithDebInfo>:LINKER:-dead_strip>
)
endif()

# LTO when supported.
if(MNE_ENABLE_LTO)
include(CheckIPOSupported)
check_ipo_supported(RESULT _ipo_ok OUTPUT _ipo_msg)
if(_ipo_ok)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE)
else()
message(STATUS "IPO/LTO not supported: ${_ipo_msg}")
endif()
endif()

# ---------------------------------------------------------------------------
# Dependencies (FetchContent)
# ---------------------------------------------------------------------------
include(FetchContent)
set(FETCHCONTENT_QUIET FALSE)

FetchContent_Declare(
cpp-httplib
GIT_REPOSITORY https://github.com/yhirose/cpp-httplib.git
GIT_TAG v0.18.5
GIT_SHALLOW TRUE
)
FetchContent_Declare(
CLI11
GIT_REPOSITORY https://github.com/CLIUtils/CLI11.git
GIT_TAG v2.4.2
GIT_SHALLOW TRUE
)
FetchContent_Declare(
spdlog
GIT_REPOSITORY https://github.com/gabime/spdlog.git
GIT_TAG v1.14.1
GIT_SHALLOW TRUE
)

set(HTTPLIB_COMPILE OFF CACHE BOOL "" FORCE) # header-only
set(CLI11_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(CLI11_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
set(SPDLOG_BUILD_EXAMPLE OFF CACHE BOOL "" FORCE)
set(SPDLOG_BUILD_TESTS OFF CACHE BOOL "" FORCE)

FetchContent_MakeAvailable(cpp-httplib CLI11 spdlog)

# ---------------------------------------------------------------------------
# Generated version header
# ---------------------------------------------------------------------------
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/include/merlion_node_exporter/version.hpp.in
${CMAKE_CURRENT_BINARY_DIR}/generated/include/merlion_node_exporter/version.hpp
@ONLY
)

# ---------------------------------------------------------------------------
# Library target — everything except main.cpp
# ---------------------------------------------------------------------------
# Populated as files land. For now, define the target so dependencies wire up.
add_library(merlion_node_exporter_lib STATIC)
target_include_directories(merlion_node_exporter_lib PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_BINARY_DIR}/generated/include
)
target_link_libraries(merlion_node_exporter_lib
PUBLIC mne_compile_flags
PRIVATE httplib::httplib CLI11::CLI11 spdlog::spdlog mne_release_flags
)
# Empty source list — placeholder until collectors land.
target_sources(merlion_node_exporter_lib PRIVATE
src/placeholder.cpp
)

# ---------------------------------------------------------------------------
# Executable
# ---------------------------------------------------------------------------
add_executable(merlion-node-exporter src/main.cpp)
target_link_libraries(merlion-node-exporter
PRIVATE merlion_node_exporter_lib mne_release_flags
)
set_target_properties(merlion-node-exporter PROPERTIES
OUTPUT_NAME merlion-node-exporter
)

# ---------------------------------------------------------------------------
# Tests
# ---------------------------------------------------------------------------
if(MNE_BUILD_TESTS)
enable_testing()
FetchContent_Declare(
Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v3.7.1
GIT_SHALLOW TRUE
)
FetchContent_MakeAvailable(Catch2)
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)

add_executable(merlion_node_exporter_tests
tests/placeholder_test.cpp
)
target_link_libraries(merlion_node_exporter_tests
PRIVATE merlion_node_exporter_lib Catch2::Catch2WithMain
)
include(Catch)
catch_discover_tests(merlion_node_exporter_tests)
endif()

# ---------------------------------------------------------------------------
# Install
# ---------------------------------------------------------------------------
include(GNUInstallDirs)
install(TARGETS merlion-node-exporter
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
29 changes: 29 additions & 0 deletions cmake/ToolchainLLVM.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Optional toolchain file that forces the build to use Homebrew LLVM clang
# on macOS instead of Apple Clang. Apple Clang ships with a libc++ that
# trails mainline by several C++23 features we use (std::expected,
# std::format), so we fail fast rather than degrade silently.
#
# Usage:
# cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=cmake/ToolchainLLVM.cmake
#
# Or, equivalently, set $CC / $CXX in the environment before configuring.

if(APPLE)
execute_process(
COMMAND brew --prefix llvm
OUTPUT_VARIABLE _LLVM_PREFIX
OUTPUT_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE _BREW_RESULT
)
if(NOT _BREW_RESULT EQUAL 0)
message(FATAL_ERROR
"ToolchainLLVM.cmake: `brew --prefix llvm` failed. "
"Install with `brew install llvm` or unset the toolchain file."
)
endif()
set(CMAKE_C_COMPILER "${_LLVM_PREFIX}/bin/clang" CACHE FILEPATH "" FORCE)
set(CMAKE_CXX_COMPILER "${_LLVM_PREFIX}/bin/clang++" CACHE FILEPATH "" FORCE)
# Make sure the rpath finds Homebrew's libc++ at runtime.
set(CMAKE_INSTALL_RPATH "${_LLVM_PREFIX}/lib/c++")
set(CMAKE_BUILD_RPATH "${_LLVM_PREFIX}/lib/c++")
endif()
Loading
Loading