Skip to content

build: Introduce CMake-based build system #75

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 1, 2025
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
69 changes: 69 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: CI
on:
pull_request:
push:
branches:
- '**'
tags-ignore:
- '**'

concurrency:
group: ${{ github.event_name != 'pull_request' && github.run_id || github.ref }}
cancel-in-progress: true

jobs:
windows-native:
name: ${{ matrix.configuration.job_name }}
# See: https://github.com/actions/runner-images#available-images.
runs-on: windows-2025

strategy:
fail-fast: false
matrix:
configuration:
- job_name: 'x64 (MSVC): Windows (VS 2022)'
build_configuration: 'Release'
- job_name: 'x64 (MSVC): Windows (VS 2022, fields=32)'
cmake_options: '-DMINISKETCH_FIELDS=32'
build_configuration: 'Release'
- job_name: 'x64 (MSVC): Windows (VS 2022, debug)'
build_configuration: 'Debug'
# TODO: Resolve the issue and re-enable benchmark.
# See: https://github.com/bitcoin-core/minisketch/pull/96.
skip_benchmark: true
- job_name: 'x64 (MSVC): Windows (VS 2022, shared)'
cmake_options: '-DBUILD_SHARED_LIBS=ON'
build_configuration: 'Release'
- job_name: 'x64 (clang-cl): Windows (VS 2022)'
cmake_options: '-T ClangCL'
build_configuration: 'Release'
# TODO: Resolve the issue and re-enable benchmark.
# See: https://github.com/bitcoin-core/minisketch/pull/96.
skip_benchmark: true

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Generate buildsystem
env:
CXXFLAGS: '/WX'
run: cmake -B build -DMINISKETCH_BUILD_BENCHMARK=ON ${{ matrix.configuration.cmake_options }}

- name: Build
run: cmake --build build --config ${{ matrix.configuration.build_configuration }}

- name: Binaries info
shell: bash
run: |
cd build/bin/${{ matrix.configuration.build_configuration }}
file * | grep "\.exe\|\.dll"

- name: Check
working-directory: build
run: ctest --output-on-failure -j $env:NUMBER_OF_PROCESSORS -C ${{ matrix.configuration.build_configuration }}

- name: Benchmark
if: ${{ ! matrix.configuration.skip_benchmark }}
working-directory: build
run: bin\${{ matrix.configuration.build_configuration }}\bench.exe
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,6 @@ stamp-h1

test*
bench

# CMake build directories.
/*build*
122 changes: 122 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
cmake_minimum_required(VERSION 3.22)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cmake_minimum_required() command does two things:

  1. Sets the minimum required version of CMake for the project.
  2. Sets the policy settings.

Rationale for point 1:

  • Minimum required version 3.22 allows to build on Ubuntu 22.04 LTS.
  • Same as in Bitcoin Core.

Rationale for point 2:

  • From Professional CMake: A Practical Guide, 21st Edition, Section 13.1:

    This gives projects confidence that developers should be able to update to any newer version of CMake at their convenience, and the project will still build as it did before.

Some other developers, namely @ryanofsky and @purpleKarrot, disagree.
See: bitcoin-core/libmultiprocess#163.


#=============================
# Project / Package Metadata
#=============================
project(minisketch
VERSION 0.0.1
DESCRIPTION "A library for BCH-based set reconciliation"
HOMEPAGE_URL "https://github.com/bitcoin-core/minisketch"
LANGUAGES CXX
)

# ============================================================
# Project Initialization
# ============================================================
enable_testing()
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is superior to include(CTest), as the latter has a few drawbacks.

Some other developers, namely @purpleKarrot, disagree.
See: bitcoin-core/libmultiprocess#145

include(CTestUseLaunchers)

list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)

if(NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
endif()
if(NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
endif()
if(NOT CMAKE_ARCHIVE_OUTPUT_DIRECTORY)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
endif()

# Prevent include directories from parent project from leaking into this one.
set_property(DIRECTORY PROPERTY INCLUDE_DIRECTORIES "")
Comment on lines +31 to +32
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See similar code in the libmultiprocess project:

# Prevent include directories from parent project from leaking into this one.
set_property(DIRECTORY PROPERTY INCLUDE_DIRECTORIES "")


#=============================
# Language Setup
#=============================
if(DEFINED CMAKE_CXX_STANDARD)
if(CMAKE_CXX_STANDARD EQUAL 98 OR CMAKE_CXX_STANDARD LESS 11)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hah! Stupid CMake :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Y2K problem :)

message(FATAL_ERROR "This project requires at least C++11")
endif()
else()
set(CMAKE_CXX_STANDARD 11)
endif()
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(NOT DEFINED CMAKE_CXX_EXTENSIONS)
set(CMAKE_CXX_EXTENSIONS OFF)
endif()

#=============================
# Configurable Options
#=============================
option(MINISKETCH_INSTALL "Enable installation." ${PROJECT_IS_TOP_LEVEL})
if(NOT PROJECT_IS_TOP_LEVEL)
mark_as_advanced(MINISKETCH_INSTALL)
endif()

option(MINISKETCH_BUILD_TESTS "Build tests." ON)
option(MINISKETCH_BUILD_BENCHMARK "Build benchmark." OFF)

set(supported_fields "")
set(have_enabled_fields NO)
set(have_disabled_fields NO)
foreach(i RANGE 2 64)
list(APPEND supported_fields ${i})
endforeach()
if(NOT DEFINED MINISKETCH_FIELDS)
set(MINISKETCH_FIELDS ${supported_fields} CACHE STRING "Semicolon-separated list of field sizes to build. Default=all. Available sizes: ${supported_fields}.")
endif()
foreach(field IN LISTS supported_fields)
if(field IN_LIST MINISKETCH_FIELDS)
set(have_enabled_fields YES)
else()
set(have_disabled_fields YES)
add_compile_definitions(DISABLE_FIELD_${field})
endif()
endforeach()
if(NOT have_enabled_fields)
message(FATAL_ERROR "No field sizes are enabled.")
endif()
unset(have_enabled_fields)
unset(supported_fields)

#=============================
# Build Options
#=============================
set(CMAKE_CXX_VISIBILITY_PRESET hidden)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feature actually requires a bit more work to get right. It is currently not working correctly in the autotools build either, so it can be done in a separate PR.


if(MSVC)
add_compile_options(/Zc:__cplusplus)
endif()

if(MINGW)
add_link_options(-static)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should port this functionality. Instead we should use the CMake shared/static selection machinery.

Copy link
Member Author

@hebasto hebasto Apr 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This affects only benchmark/test binaries and allows to run them on the build system via Wine when building either a static library or a shared one.

UPD. It also links libstdc++-6 to libminisketch.dll statically when cross-compiling for Windows and -DBUILD_SHARED_LIBS=YES is provided.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider

cmake -B build \
  -DCMAKE_BUILD_TYPE=Release \
  -DCMAKE_SYSTEM_NAME=Windows \
  -DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-g++-posix
cmake --build build --target test
wine ./build/src/test.exe

or

cmake -B build \
  -DCMAKE_BUILD_TYPE=Release \
  -DCMAKE_SYSTEM_NAME=Windows \
  -DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-g++-posix \
  -DBUILD_SHARED_LIBS=YES
cmake --build build --target test
wine ./build/src/test.exe

endif()

#=============================
# Diagnostics Options
#=============================
if(MSVC)
# For both MSVC's cl.exe and clang-cl compilers.
add_compile_options(/W3) # Production quality warning level. Enables -Wall Clang's core option.
else()
add_compile_options(-Wall)
endif()

if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
add_compile_options(/wd4060) # Disable warning C4060 "switch statement contains no 'case' or 'default' labels".
add_compile_options(/wd4065) # Disable warning C4065 "switch statement contains 'default' but no 'case' labels".
add_compile_options(/wd4146) # Disable warning C4146 "unary minus operator applied to unsigned type, result still unsigned".
add_compile_options(/wd4244) # Disable warning C4244 "conversion from 'type1' to 'type2', possible loss of data".
else()
add_compile_options(-Wundef)
endif()

#=============================
# Main Processing
#=============================
include(SystemIntrospection)
add_subdirectory(src)

include(PrintConfigureSummary)
print_configure_summary()
40 changes: 39 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ Below we compare the PinSketch algorithm (which `libminisketch` is an implementa
* **Difference type:** PinSketch can only compute the symmetric difference from a merged sketch, while CPISync and IBLT can distinguish which side certain elements were missing on. When the decoder has access to one of the sets, this generally doesn't matter, as he can look up each of the elements in the symmetric difference with one of the sets.
* **Secure sketch:** Whether the sketch satisfies the definition of a secure sketch<sup>[[1]](#myfootnote1)</sup>, which implies a minimal amount about a set can be extracted from a sketch by anyone who does not know most of the elements already. This makes the algorithm appropriate for applications like fingerprint authentication.

## Building
## Building with Autotools

The build system is very rudimentary for now, and [improvements](https://github.com/bitcoin-core/minisketch/pulls) are welcome.

Expand All @@ -78,6 +78,44 @@ cd minisketch
./autogen.sh && ./configure && make
```

## Building with CMake

To maintain a pristine source tree, CMake encourages performing an out-of-source build by using a separate dedicated build directory.

### Building on POSIX systems

The following commands will produce the same `libminisketch.a` file as in the example [above](#building-with-autotools):

```bash
cmake -B build -DCMAKE_CXX_FLAGS="-g -O2" # Generate a build system in subdirectory "build"
cmake --build build # Run the actual build process
ctest --test-dir build # Run the test suite
sudo cmake --install build # Install the library into the system (optional)
```

Run `cmake -B build -LH` or `ccmake -B build` to see the full list of configurable build options.

### Cross compiling

The following example works on modern Ubuntu/Debian systems:

```bash
sudo apt install g++-mingw-w64-x86-64-posix
cmake -B build -DCMAKE_SYSTEM_NAME=Windows -DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-g++-posix
cmake --build build
```

### Building on Windows

The following example assumes the use of Visual Studio 2022 and CMake v3.21 or newer.

In "Developer Command Prompt for VS 2022":

```cmd
cmake -B build
cmake --build build --config Release
```

## Usage

In this section Alice and Bob are trying to find the difference between their sets.
Expand Down
112 changes: 112 additions & 0 deletions cmake/PrintConfigureSummary.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
include_guard(GLOBAL)

function(indent_message header content indent_num)
if(indent_num GREATER 0)
string(REPEAT " " ${indent_num} indentation)
string(REPEAT "." ${indent_num} tail)
string(REGEX REPLACE "${tail}$" "" header "${header}")
endif()
message("${indentation}${header} ${content}")
endfunction()

# Print compiler's flags on best-effort. Include the abstracted
# CMake flags that we touch ourselves.
function(print_flags_per_config config indent_num)
string(STRIP "${CMAKE_CXX_COMPILER_ARG1} ${CMAKE_CXX_FLAGS}" combined_cxx_flags)
string(TOUPPER "${config}" config_uppercase)
string(STRIP "${combined_cxx_flags} ${CMAKE_CXX_FLAGS_${config_uppercase}}" combined_cxx_flags)
string(STRIP "${combined_cxx_flags} ${CMAKE_CXX${CMAKE_CXX_STANDARD}_STANDARD_COMPILE_OPTION}" combined_cxx_flags)
if(CMAKE_POSITION_INDEPENDENT_CODE)
string(JOIN " " combined_cxx_flags ${combined_cxx_flags} ${CMAKE_CXX_COMPILE_OPTIONS_PIC})
endif()
if(CMAKE_CXX_COMPILE_OPTIONS_VISIBILITY AND CMAKE_CXX_VISIBILITY_PRESET)
string(JOIN " " combined_cxx_flags ${combined_cxx_flags} ${CMAKE_CXX_COMPILE_OPTIONS_VISIBILITY}${CMAKE_CXX_VISIBILITY_PRESET})
endif()
get_directory_property(compile_options COMPILE_OPTIONS)
string(JOIN " " combined_cxx_flags ${combined_cxx_flags} ${compile_options})
indent_message("CXXFLAGS .............................." "${combined_cxx_flags}" ${indent_num})
endfunction()

function(print_configure_summary)
message("")
if(PROJECT_IS_TOP_LEVEL)
message("Configure summary")
message("=================")
else()
message("minisketch configure summary")
message("============================")
endif()
if(BUILD_SHARED_LIBS)
set(library_type "Shared")
else()
set(library_type "Static")
endif()
message("Library type .......................... ${library_type}")
message("Build options:")
if(have_disabled_fields)
set(filed_sizes "${MINISKETCH_FIELDS}")
else()
set(filed_sizes "All")
endif()
message(" field sizes ........................ ${filed_sizes}")
if(HAVE_CLMUL)
set(clmul_status "Enabled")
else()
set(clmul_status "Disabled")
endif()
message(" clmul fields ........................ ${clmul_status}")
if(CMAKE_CXX_STANDARD GREATER_EQUAL 20)
set(clz_status "C++20")
elseif(HAVE_CLZ)
set(clz_status "Compiler builtin")
else()
set(clz_status "Default")
endif()
message(" clz implementation .................. ${clz_status}")
message("Optional binaries:")
message(" benchmark ........................... ${MINISKETCH_BUILD_BENCHMARK}")
message(" tests ............................... ${MINISKETCH_BUILD_TESTS}")
message("")
if(CMAKE_CROSSCOMPILING)
set(cross_status "TRUE, for ${CMAKE_SYSTEM_NAME}, ${CMAKE_SYSTEM_PROCESSOR}")
else()
set(cross_status "FALSE")
endif()
message("Cross compiling ....................... ${cross_status}")
message("C++ compiler .......................... ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}, ${CMAKE_CXX_COMPILER}")
get_property(_is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(_is_multi_config)
list(JOIN CMAKE_CONFIGURATION_TYPES ", " configs)
message("Available build configurations ........ ${configs}")
if(CMAKE_GENERATOR MATCHES "Visual Studio")
set(default_config "Debug")
else()
list(GET CMAKE_CONFIGURATION_TYPES 0 default_config)
endif()
message("Default build configuration ........... ${default_config}")
foreach(config IN LISTS CMAKE_CONFIGURATION_TYPES)
message("'${config}' build configuration:")
print_flags_per_config("${config}" 2)
endforeach()
else()
message("CMAKE_BUILD_TYPE ...................... ${CMAKE_BUILD_TYPE}")
print_flags_per_config("${CMAKE_BUILD_TYPE}" 0)
endif()
unset(_is_multi_config)

message([=[

NOTE: The summary above may not exactly match the final applied build flags
if any additional CMAKE_* or environment variables have been modified.
To see the exact flags applied, build with the --verbose option.]=]
)

if(have_disabled_fields AND PROJECT_IS_TOP_LEVEL)
message("")
message(WARNING
"Only compiling in support for field sizes: ${MINISKETCH_FIELDS}\n"
"This means the library will lack support for other field sizes entirely.\n"
)
endif()
message("")
endfunction()
Loading