Skip to content

Commit 35f8a23

Browse files
authored
build: add Meson build support and export a C++20 module interface (#240)
1 parent 662d0c1 commit 35f8a23

5 files changed

Lines changed: 339 additions & 8 deletions

File tree

‎.github/workflows/ci-meson.yml‎

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
name: MinIO C++ Meson
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
workflow_dispatch:
9+
10+
concurrency:
11+
group: ${{ github.workflow }}-${{ github.head_ref }}
12+
cancel-in-progress: true
13+
14+
permissions:
15+
contents: read
16+
17+
jobs:
18+
build-meson:
19+
strategy:
20+
fail-fast: false
21+
matrix:
22+
config:
23+
- { name: "Ubuntu_Latest_GCC_Meson", os: ubuntu-latest, triplet: "x64-linux" }
24+
- { name: "Ubuntu_Latest_GCC_arm64_Meson", os: ubuntu-24.04-arm, triplet: "arm64-linux" }
25+
- { name: "Ubuntu_Latest_Clang_Meson", os: ubuntu-latest, triplet: "x64-linux" }
26+
- { name: "macOS_Latest_Clang_Meson", os: macos-latest, triplet: "arm64-osx" }
27+
- { name: "Windows_Latest_MSVC_Meson", os: windows-latest, triplet: "x64-windows" }
28+
29+
name: ${{ matrix.config.name }}
30+
runs-on: ${{ matrix.config.os }}
31+
32+
steps:
33+
- name: Checkout minio-cpp
34+
uses: actions/checkout@v4
35+
with:
36+
path: minio-cpp
37+
38+
- name: Checkout vcpkg
39+
uses: actions/checkout@v4
40+
with:
41+
repository: microsoft/vcpkg
42+
ref: "2026.07.29"
43+
path: vcpkg
44+
45+
- name: Install Meson and Ninja
46+
if: startsWith(matrix.config.os, 'ubuntu')
47+
run: pip install --user meson ninja
48+
49+
- name: Install Meson and Ninja if Windows
50+
if: startsWith(matrix.config.os, 'windows')
51+
run: pip install meson ninja
52+
53+
# arm64 Linux has no prebuilt vcpkg CMake, so vcpkg falls back to the system
54+
# cmake; the apt version (3.28) is too old for vcpkg's SPDX generation
55+
# (string(JSON ... STRING_ENCODE)). Provide a recent cmake on PATH.
56+
- name: Install recent CMake
57+
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
58+
59+
- name: Install GCC 14 if Ubuntu
60+
if: startsWith(matrix.config.name, 'Ubuntu_Latest_GCC')
61+
run: |
62+
sudo apt-get update
63+
sudo apt-get install -y gcc-14 g++-14
64+
echo "CC=gcc-14" >> "$GITHUB_ENV"
65+
echo "CXX=g++-14" >> "$GITHUB_ENV"
66+
67+
# clang-tools-18 надає clang-scan-deps - Meson шукає бінарник тієї ж
68+
# версії, що й сам компілятор, щоб просканувати modules/miniocpp.cc на
69+
# залежності для Ninja dyndep.
70+
- name: Install Clang 18 if Ubuntu Clang
71+
if: matrix.config.name == 'Ubuntu_Latest_Clang_Meson'
72+
run: |
73+
sudo apt-get update
74+
sudo apt-get install -y clang-18 clang-tools-18
75+
echo "CC=clang-18" >> "$GITHUB_ENV"
76+
echo "CXX=clang++-18" >> "$GITHUB_ENV"
77+
78+
- name: Install dependencies if macOS
79+
if: startsWith(matrix.config.os, 'macos')
80+
run: brew install meson ninja pkg-config
81+
82+
- name: Set up MSVC dev environment if Windows
83+
if: startsWith(matrix.config.os, 'windows')
84+
uses: ilammy/msvc-dev-cmd@v1
85+
86+
- name: Remove Git's link.exe if Windows
87+
if: startsWith(matrix.config.os, 'windows')
88+
shell: bash
89+
run: rm -f "/c/Program Files/Git/usr/bin/link.EXE"
90+
91+
- name: Bootstrap vcpkg
92+
shell: bash
93+
run: |
94+
if [ "$RUNNER_OS" = "Windows" ]; then
95+
./vcpkg/bootstrap-vcpkg.bat -disableMetrics
96+
else
97+
./vcpkg/bootstrap-vcpkg.sh -disableMetrics
98+
fi
99+
100+
- name: Install dependencies via vcpkg
101+
shell: bash
102+
run: |
103+
./vcpkg/vcpkg install \
104+
"cpp-httplib[core,openssl]:${{ matrix.config.triplet }}" \
105+
"inih[core,cpp]:${{ matrix.config.triplet }}" \
106+
"nlohmann-json:${{ matrix.config.triplet }}" \
107+
"openssl:${{ matrix.config.triplet }}" \
108+
"pugixml:${{ matrix.config.triplet }}" \
109+
"zlib:${{ matrix.config.triplet }}"
110+
111+
- name: Configure and build (Meson)
112+
if: ${{ !startsWith(matrix.config.os, 'windows') }}
113+
shell: bash
114+
run: |
115+
cd minio-cpp
116+
meson setup build \
117+
-Dpkg_config_path="${{ github.workspace }}/vcpkg/installed/${{ matrix.config.triplet }}/lib/pkgconfig" \
118+
-Dcmake_prefix_path="${{ github.workspace }}/vcpkg/installed/${{ matrix.config.triplet }}"
119+
meson compile -C build
120+
121+
- name: Configure and build (Meson) if Windows
122+
if: startsWith(matrix.config.os, 'windows')
123+
shell: cmd
124+
run: |
125+
cd minio-cpp
126+
meson setup build -Dpkg_config_path="${{ github.workspace }}/vcpkg/installed/${{ matrix.config.triplet }}/lib/pkgconfig" -Dcmake_prefix_path="${{ github.workspace }}/vcpkg/installed/${{ matrix.config.triplet }}"
127+
meson compile -C build

‎CMakeLists.txt‎

Lines changed: 103 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,10 @@ list(APPEND MINIO_CPP_LIBS ${MINIO_CPP_DEPS_LINK_LIBS})
102102
if (WIN32)
103103
list(APPEND MINIO_CPP_LIBS wsock32)
104104
list(APPEND MINIO_CPP_LIBS ws2_32)
105+
# miniocpp.pc.in has no visibility into target_link_libraries() - without this,
106+
# pkg-config consumers (non-CMake) are missing wsock32/ws2_32 at link time
107+
# (getaddrinfo, select, ...).
108+
set(MINIO_CPP_PC_EXTRA_LIBS " -lwsock32 -lws2_32")
105109
endif()
106110
if (APPLE)
107111
# httplib loads macOS system certificates from the Keychain
@@ -179,6 +183,78 @@ target_include_directories(miniocpp PUBLIC
179183
$<BUILD_INTERFACE:${MINIO_CPP_INCLUDES}>
180184
)
181185
target_link_libraries(miniocpp PUBLIC ${MINIO_CPP_LIBS})
186+
187+
# Optional C++20 module interface for the public API (modules/miniocpp.cc).
188+
# FILE_SET CXX_MODULES (CMake 3.28+) attaches it as a real part of the miniocpp
189+
# target's build - the compiler gets whatever flags it needs for a module
190+
# interface unit (e.g. /interface for MSVC) based on this declaration, not on
191+
# the file's extension, so any CMake/MSBuild/Meson (via cmake.subproject())
192+
# consumer of this target picks up `import miniocpp;` automatically. Requires
193+
# C++20 (MINIO_CPP_STD=20); silently skipped otherwise for older setups.
194+
#
195+
# CMake's module dependency scanning is only implemented for the Ninja,
196+
# Ninja Multi-Config, and Visual Studio (17.4+) generators - with any other
197+
# generator (e.g. the Linux/macOS default "Unix Makefiles") CMake hard-errors
198+
# at the Generate step as soon as it sees FILE_SET CXX_MODULES sources, even
199+
# if MINIO_CPP_STD=20 was requested intentionally. Generator *name* alone is
200+
# not enough either: CMake 3.28 requires Ninja 1.11+ for the dyndep-based
201+
# scanning, and each compiler has its own minimum version for module support
202+
# (MSVC 14.34 / VS 17.4, reported as compiler version 19.34; Clang 16;
203+
# GCC 14) - see https://cmake.org/cmake/help/v3.28/manual/cmake-cxxmodules.7.html.
204+
# An older or unrecognized compiler can still match the generator check
205+
# above and then break module dependency scanning instead of falling back
206+
# cleanly, so reject anything not explicitly known-good rather than
207+
# defaulting to allowed.
208+
set(MINIO_CPP_HAS_CXX_MODULE OFF)
209+
if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.28" AND MINIO_CPP_STD STREQUAL "20")
210+
set(MINIO_CPP_MODULE_TOOLCHAIN_OK OFF)
211+
if (CMAKE_GENERATOR STREQUAL "Ninja" OR CMAKE_GENERATOR STREQUAL "Ninja Multi-Config")
212+
execute_process(
213+
COMMAND "${CMAKE_MAKE_PROGRAM}" --version
214+
OUTPUT_VARIABLE MINIO_CPP_NINJA_VERSION
215+
OUTPUT_STRIP_TRAILING_WHITESPACE
216+
ERROR_QUIET
217+
)
218+
if (MINIO_CPP_NINJA_VERSION VERSION_GREATER_EQUAL "1.11")
219+
set(MINIO_CPP_MODULE_TOOLCHAIN_OK ON)
220+
endif()
221+
elseif (CMAKE_GENERATOR MATCHES "^Visual Studio ")
222+
set(MINIO_CPP_MODULE_TOOLCHAIN_OK ON)
223+
endif()
224+
225+
# Applies regardless of which of the generators above is in use - Ninja can
226+
# also drive an MSVC/Clang/GCC toolchain, and each compiler's own module
227+
# support is gated on its own version, not the generator. Reject any
228+
# compiler not explicitly listed here, rather than silently allowing it.
229+
if (MINIO_CPP_MODULE_TOOLCHAIN_OK)
230+
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
231+
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "19.34")
232+
set(MINIO_CPP_MODULE_TOOLCHAIN_OK OFF)
233+
endif()
234+
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
235+
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "16.0")
236+
set(MINIO_CPP_MODULE_TOOLCHAIN_OK OFF)
237+
endif()
238+
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
239+
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "14")
240+
set(MINIO_CPP_MODULE_TOOLCHAIN_OK OFF)
241+
endif()
242+
else()
243+
set(MINIO_CPP_MODULE_TOOLCHAIN_OK OFF)
244+
endif()
245+
endif()
246+
247+
if (MINIO_CPP_MODULE_TOOLCHAIN_OK)
248+
set(MINIO_CPP_HAS_CXX_MODULE ON)
249+
target_sources(miniocpp
250+
PUBLIC
251+
FILE_SET CXX_MODULES
252+
BASE_DIRS modules
253+
FILES modules/miniocpp.cc
254+
)
255+
endif()
256+
endif()
257+
182258
# httplib's class layout depends on CPPHTTPLIB_OPENSSL_SUPPORT; define it once
183259
# target-wide so every translation unit compiles httplib.h identically.
184260
target_compile_definitions(miniocpp PRIVATE CPPHTTPLIB_OPENSSL_SUPPORT)
@@ -317,12 +393,22 @@ if (MINIO_CPP_ENABLE_RDMA)
317393
INSTALL_RPATH "$ORIGIN")
318394
endif()
319395

320-
install(TARGETS miniocpp
321-
EXPORT miniocpp-targets
322-
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
323-
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
324-
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
325-
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
396+
if (MINIO_CPP_HAS_CXX_MODULE)
397+
install(TARGETS miniocpp
398+
EXPORT miniocpp-targets
399+
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
400+
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
401+
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
402+
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
403+
FILE_SET CXX_MODULES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/miniocpp/modules")
404+
else()
405+
install(TARGETS miniocpp
406+
EXPORT miniocpp-targets
407+
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
408+
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
409+
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
410+
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
411+
endif()
326412

327413
# Source-built deps must be in an export set for install(EXPORT) to resolve
328414
# them (miniocpp_inih does not export itself).
@@ -334,6 +420,17 @@ if (MINIO_CPP_DEPS_EXPORT_TARGETS)
334420
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}")
335421
endif()
336422

423+
# Known limitation: this export does not pass CXX_MODULES_DIRECTORY, so an
424+
# installed package (via find_package(miniocpp) + CMake, as opposed to
425+
# consuming subprojects/miniocpp source directly, or via pkg-config/vcpkg)
426+
# is missing the generated module metadata CMake needs to let a downstream
427+
# CMake consumer `import miniocpp;` - see
428+
# https://cmake.org/cmake/help/latest/command/install.html#exporting-c-modules.
429+
# Separately, installing module interfaces is not supported at all with
430+
# the Visual Studio generator (independent of the MINIO_CPP_HAS_CXX_MODULE
431+
# toolchain gate above, which only covers the build step). Neither gap is
432+
# covered by an automated test yet.
433+
337434
# The link interface is not exported; miniocpp-config.cmake re-applies it.
338435
install(EXPORT miniocpp-targets
339436
NAMESPACE miniocpp::

‎meson.build‎

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
project('miniocpp', 'cpp', version : '0.6.0', default_options : ['cpp_std=c++17'])
2+
3+
cxx = meson.get_compiler('cpp')
4+
5+
# method: 'cmake' скрізь нижче (крім openssl - у Meson є вбудований
6+
# спеціальний резолвер під нього) - на Windows-раннерах pkg-config може
7+
# бути затінений нефункціональною заглушкою (напр. Strawberry Perl's
8+
# pkg-config.bat), що ламає дефолтний auto-метод. Назви пакетів/таргетів
9+
# узяті з того, що реально шукає CMake-версія цього ж репозиторію
10+
# (cmake/miniocpp-deps.cmake).
11+
openssl_dep = dependency('openssl')
12+
httplib_dep = dependency('httplib', method : 'cmake', modules : ['httplib::httplib'])
13+
inih_dep = dependency('unofficial-inih', method : 'cmake', modules : ['unofficial::inih::inireader'])
14+
nlohmann_json_dep = dependency('nlohmann_json', method : 'cmake', modules : ['nlohmann_json::nlohmann_json'])
15+
pugixml_dep = dependency('pugixml', method : 'cmake', modules : ['pugixml'])
16+
zlib_dep = dependency('zlib', method : 'cmake', modules : ['ZLIB::ZLIB'])
17+
18+
extra_libs = []
19+
if host_machine.system() == 'windows'
20+
extra_libs = [cxx.find_library('ws2_32'), cxx.find_library('wsock32')]
21+
endif
22+
23+
all_deps = [openssl_dep, httplib_dep, inih_dep, nlohmann_json_dep, pugixml_dep, zlib_dep] + extra_libs
24+
miniocpp_include = include_directories('include')
25+
26+
miniocpp_sources = files(
27+
'src/args.cc',
28+
'src/baseclient.cc',
29+
'src/client.cc',
30+
'src/credentials.cc',
31+
'src/error.cc',
32+
'src/http.cc',
33+
'src/providers.cc',
34+
'src/request.cc',
35+
'src/response.cc',
36+
'src/select.cc',
37+
'src/signer.cc',
38+
'src/sse.cc',
39+
'src/types.cc',
40+
'src/utils.cc',
41+
)
42+
43+
# The real library - all .cc files from this repo, not the official minio-cpp
44+
# vcpkg package.
45+
miniocpp_lib = static_library('miniocpp', miniocpp_sources,
46+
include_directories : miniocpp_include,
47+
dependencies : all_deps,
48+
)
49+
50+
build_module = (
51+
(cxx.get_id() == 'msvc' and cxx.version().version_compare('>=19.34'))
52+
or (cxx.get_id() == 'clang' and host_machine.system() != 'darwin'
53+
and cxx.version().version_compare('>=16.0'))
54+
)
55+
56+
if build_module
57+
if cxx.get_id() == 'msvc'
58+
module_cpp_args = ['/interface']
59+
elif cxx.get_id() == 'clang'
60+
module_cpp_args = ['-x', 'c++-module']
61+
else
62+
module_cpp_args = []
63+
endif
64+
miniocpp_module_lib = static_library('miniocpp_module', 'modules/miniocpp.cc',
65+
include_directories : miniocpp_include,
66+
dependencies : all_deps,
67+
cpp_args : module_cpp_args,
68+
override_options : ['cpp_std=c++20'],
69+
)
70+
miniocpp_dep = declare_dependency(
71+
link_with : [miniocpp_lib, miniocpp_module_lib],
72+
include_directories : miniocpp_include,
73+
dependencies : all_deps,
74+
)
75+
else
76+
miniocpp_dep = declare_dependency(
77+
link_with : [miniocpp_lib],
78+
include_directories : miniocpp_include,
79+
dependencies : all_deps,
80+
)
81+
endif

‎miniocpp.pc.in‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@ Name: @PROJECT_NAME@
77
Description: @PROJECT_DESCRIPTION@
88
Version: @PROJECT_VERSION@
99
10-
Requires:
11-
Libs: -L${libdir} -lminiocpp
10+
Requires: libcrypto libssl pugixml zlib
11+
Libs: -L${libdir} -lminiocpp@MINIO_CPP_PC_EXTRA_LIBS@
1212
Cflags: -I${includedir}

‎modules/miniocpp.cc‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
module;
2+
3+
#include <miniocpp/client.h>
4+
5+
export module miniocpp;
6+
7+
// Re-exporting the public API through the module requires an EXPLICIT using-
8+
// declaration per symbol (using ns::Symbol;) - not a using-directive (using
9+
// namespace ns;), because declarations from the global module fragment (the
10+
// #include above) only become "reachable", not automatic members of an export
11+
// namespace. The list below covers what a typical consumer needs today -
12+
// extend it by adding more using-declarations as needed.
13+
export namespace minio::s3 {
14+
using s3::BaseUrl;
15+
using s3::BucketExistsArgs;
16+
using s3::BucketExistsResponse;
17+
using s3::Client;
18+
} // namespace minio::s3
19+
20+
export namespace minio::creds {
21+
using minio::creds::StaticProvider;
22+
}
23+
24+
export namespace minio {
25+
using minio::Result;
26+
}

0 commit comments

Comments
 (0)