Skip to content
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

build: Add CMake-based build system (8 of N) #18

Merged
merged 7 commits into from
Sep 13, 2023
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
164 changes: 128 additions & 36 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,13 @@ set(CMAKE_CXX_EXTENSIONS OFF)

set(configure_warnings)

# The core interface library aims to encapsulate all common build flags.
# It is intended to be a usage requirement for all other targets.
add_library(core INTERFACE)

include(TryAppendCXXFlags)
include(TryAppendLinkerFlag)

if(WIN32)
#[=[
This build system supports two ways to build binaries for Windows.
Expand All @@ -97,29 +104,63 @@ if(WIN32)
- A run-time library must be specified explicitly using _MT definition.
]=]

add_compile_definitions(_WIN32_WINNT=0x0601 _WIN32_IE=0x0501 WIN32_LEAN_AND_MEAN NOMINMAX)
target_compile_definitions(core INTERFACE
_WIN32_WINNT=0x0601
_WIN32_IE=0x0501
WIN32_LEAN_AND_MEAN
NOMINMAX
)

if(MSVC)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
add_compile_options(/utf-8 /Zc:__cplusplus)
target_compile_options(core INTERFACE
/utf-8
/Zc:__cplusplus
)
# Improve parallelism in MSBuild.
# See: https://devblogs.microsoft.com/cppblog/improved-parallelism-in-msbuild/.
list(APPEND CMAKE_VS_GLOBALS "UseMultiToolTask=true")
endif()

if(MINGW)
add_compile_definitions(WIN32 _WINDOWS _MT)
target_compile_definitions(core INTERFACE
WIN32
_WINDOWS
_MT
)
# We require Windows 7 (NT 6.1) or later.
add_link_options(-Wl,--major-subsystem-version,6 -Wl,--minor-subsystem-version,1)
try_append_linker_flag(core "-Wl,--major-subsystem-version,6")
try_append_linker_flag(core "-Wl,--minor-subsystem-version,1")
endif()
endif()

# Use 64-bit off_t on 32-bit Linux.
if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SIZEOF_VOID_P EQUAL 4)
# Ensure 64-bit offsets are used for filesystem accesses for 32-bit compilation.
target_compile_definitions(core INTERFACE
_FILE_OFFSET_BITS=64
)
endif()

if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
add_compile_definitions(MAC_OSX)
target_compile_definitions(core INTERFACE
MAC_OSX
)
# These flags are specific to ld64, and may cause issues with other linkers.
# For example: GNU ld will interpret -dead_strip as -de and then try and use
# "ad_strip" as the symbol for the entry point.
try_append_linker_flag(core "-Wl,-dead_strip")
try_append_linker_flag(core "-Wl,-dead_strip_dylibs")
try_append_linker_flag(core "-Wl,-headerpad_max_install_names")
endif()

if(CMAKE_CROSSCOMPILING AND DEPENDS_ALLOW_HOST_PACKAGES)
list(APPEND CMAKE_FIND_ROOT_PATH "${CMAKE_SYSTEM_PREFIX_PATH}")
if(CMAKE_CROSSCOMPILING)
target_compile_definitions(core INTERFACE ${DEPENDS_COMPILE_DEFINITIONS})
target_compile_options(core INTERFACE "$<$<COMPILE_LANGUAGE:C>:${DEPENDS_C_COMPILER_FLAGS}>")
target_compile_options(core INTERFACE "$<$<COMPILE_LANGUAGE:CXX>:${DEPENDS_CXX_COMPILER_FLAGS}>")
if(DEPENDS_ALLOW_HOST_PACKAGES)
list(APPEND CMAKE_FIND_ROOT_PATH "${CMAKE_SYSTEM_PREFIX_PATH}")
endif()
endif()

include(AddThreadsIfNeeded)
Expand All @@ -140,6 +181,47 @@ include(cmake/leveldb.cmake)
include(cmake/minisketch.cmake)
include(cmake/secp256k1.cmake)

include(ProcessConfigurations)
set_default_config(RelWithDebInfo)

# Redefine configuration flags.
target_compile_definitions(core INTERFACE
$<$<CONFIG:Debug>:DEBUG>
$<$<CONFIG:Debug>:DEBUG_LOCKORDER>
$<$<CONFIG:Debug>:DEBUG_LOCKCONTENTION>
$<$<CONFIG:Debug>:RPC_DOC_CHECK>
$<$<CONFIG:Debug>:ABORT_ON_FAILED_ASSUME>
)
# We leave assertions on.
if(MSVC)
remove_c_flag_from_all_configs(/DNDEBUG)
Copy link

Choose a reason for hiding this comment

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

This is a CPPFLAG (preprocessor) flag, not a cflag/cxxflag and not c/cxx specific. Surely it's possible to remove it from the list of definitions instead?

Copy link
Owner Author

Choose a reason for hiding this comment

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

This is a CPPFLAG (preprocessor) flag, not a cflag/cxxflag and not c/cxx specific.

Agree :)

Surely it's possible to remove it from the list of definitions instead?

This is all about CMake's default platform-specific CMAKE_<LANG>_FLAGS_<CONFIG>_INIT variables. There are no preprocessor variable among them. And CMake indeed sets initial preprocessor definitions in the mentioned above language-specific variables.

Copy link
Owner Author

Choose a reason for hiding this comment

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

remove_cxx_flag_from_all_configs(/DNDEBUG)
else()
remove_c_flag_from_all_configs(-DNDEBUG)
remove_cxx_flag_from_all_configs(-DNDEBUG)

# Prefer -O2 optimization level. (-O3 is CMake's default for Release for many compilers.)
replace_c_flag_in_config(Release -O3 -O2)
replace_cxx_flag_in_config(Release -O3 -O2)
Copy link

Choose a reason for hiding this comment

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

I believe this means the user has no way to override and is stuck with "-O2" builds ?

Copy link
Owner Author

Choose a reason for hiding this comment

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

FWIW, an issue with the user's opportunities to override build system options described in details in bitcoin-core/secp256k1#1249.


set(debug_c_flags "")
set(debug_cxx_flags "")
try_append_cxx_flags(debug_cxx_flags "-O0" RESULT_VAR compiler_supports_O0)
if(compiler_supports_O0)
string(STRIP "${debug_c_flags} -O0" debug_c_flags)
endif()
try_append_cxx_flags(debug_cxx_flags "-g3" RESULT_VAR compiler_supports_g3)
Copy link

Choose a reason for hiding this comment

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

The "-g" flags should be on for Debug + RelWithDebInfo, off for MinSizeRel/Release. But it appears they're applied here unconditionally?

Copy link

Choose a reason for hiding this comment

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

Hmm, though your multi-config output looks as I'd expect. I must be misunderstanding something. Ignore the above comment for now.

if(compiler_supports_g3)
string(STRIP "${debug_c_flags} -g3" debug_c_flags)
else()
try_append_cxx_flags(debug_cxx_flags "-g")
string(STRIP "${debug_c_flags} -g" debug_c_flags)
endif()
try_append_cxx_flags(debug_cxx_flags "-ftrapv")
set(CMAKE_C_FLAGS_DEBUG "${debug_c_flags}")
set(CMAKE_CXX_FLAGS_DEBUG "${debug_cxx_flags}")
endif()

include(cmake/optional.cmake)

if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.14)
Expand All @@ -163,6 +245,9 @@ add_subdirectory(test)

include(cmake/tests.cmake)

get_target_property(definitions core INTERFACE_COMPILE_DEFINITIONS)
separate_by_configs(definitions)

message("\n")
message("Configure summary")
message("=================")
Expand Down Expand Up @@ -190,40 +275,33 @@ else()
set(cross_status "FALSE")
endif()
message("Cross compiling ....................... ${cross_status}")
get_directory_property(definitions COMPILE_DEFINITIONS)
string(REPLACE ";" " " definitions "${definitions}")
message("Preprocessor defined macros ........... ${definitions}")
message("Preprocessor defined macros ........... ${definitions_ALL}")
message("C compiler ............................ ${CMAKE_C_COMPILER}")
message("CFLAGS ................................ ${CMAKE_C_FLAGS}")
list(JOIN DEPENDS_C_COMPILER_FLAGS " " depends_c_flags)
string(STRIP "${CMAKE_C_FLAGS} ${depends_c_flags}" combined_c_flags)
message("CFLAGS ................................ ${combined_c_flags}")
message("C++ compiler .......................... ${CMAKE_CXX_COMPILER}")
message("CXXFLAGS .............................. ${CMAKE_CXX_FLAGS}")
get_directory_property(common_compile_options COMPILE_OPTIONS)
string(REPLACE ";" " " common_compile_options "${common_compile_options}")
list(JOIN DEPENDS_CXX_COMPILER_FLAGS " " depends_cxx_flags)
string(STRIP "${CMAKE_CXX_FLAGS} ${depends_cxx_flags}" combined_cxx_flags)
message("CXXFLAGS .............................. ${combined_cxx_flags}")
get_target_property(common_compile_options core INTERFACE_COMPILE_OPTIONS)
if(common_compile_options)
list(JOIN common_compile_options " " common_compile_options)
else()
set(common_compile_options)
endif()
string(GENEX_STRIP "${common_compile_options}" common_compile_options)
message("Common compile options ................ ${common_compile_options}")
get_directory_property(common_link_options LINK_OPTIONS)
string(REPLACE ";" " " common_link_options "${common_link_options}")
message("Common link options ................... ${common_link_options}")
if(DEFINED CMAKE_BUILD_TYPE)
message("Build type:")
message(" - CMAKE_BUILD_TYPE ................... ${CMAKE_BUILD_TYPE}")
string(TOUPPER "${CMAKE_BUILD_TYPE}" build_type)
message(" - CFLAGS ............................. ${CMAKE_C_FLAGS_${build_type}}")
message(" - CXXFLAGS ........................... ${CMAKE_CXX_FLAGS_${build_type}}")
message(" - LDFLAGS for executables ............ ${CMAKE_EXE_LINKER_FLAGS_${build_type}}")
message(" - LDFLAGS for shared libraries ....... ${CMAKE_SHARED_LINKER_FLAGS_${build_type}}")
get_target_property(common_link_options core INTERFACE_LINK_OPTIONS)
if(common_link_options)
list(JOIN common_link_options " " common_link_options)
else()
message("Available configurations .............. ${CMAKE_CONFIGURATION_TYPES}")
message("Debug configuration:")
message(" - CFLAGS ............................. ${CMAKE_C_FLAGS_DEBUG}")
message(" - CXXFLAGS ........................... ${CMAKE_CXX_FLAGS_DEBUG}")
message(" - LDFLAGS for executables ............ ${CMAKE_EXE_LINKER_FLAGS_DEBUG}")
message(" - LDFLAGS for shared libraries ....... ${CMAKE_SHARED_LINKER_FLAGS_DEBUG}")
message("Release configuration:")
message(" - CFLAGS ............................. ${CMAKE_C_FLAGS_RELEASE}")
message(" - CXXFLAGS ........................... ${CMAKE_CXX_FLAGS_RELEASE}")
message(" - LDFLAGS for executables ............ ${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
message(" - LDFLAGS for shared libraries ....... ${CMAKE_SHARED_LINKER_FLAGS_RELEASE}")
set(common_link_options)
endif()
message("Common link options ................... ${common_link_options}")
message("Linker flags for executables .......... ${CMAKE_EXE_LINKER_FLAGS}")
message("Linker flags for shared libraries ..... ${CMAKE_SHARED_LINKER_FLAGS}")
print_config_flags()
message("Use assembly routines ................. ${ASM}")
message("Use ccache for compiling .............. ${CCACHE}")
message("\n")
Expand All @@ -234,3 +312,17 @@ if(configure_warnings)
endforeach()
message(" ******\n")
endif()

# We want all build properties to be encapsulated properly.
get_directory_property(global_compile_definitions COMPILE_DEFINITIONS)
Copy link

Choose a reason for hiding this comment

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

Sanity checking -- Love it!

if(global_compile_definitions)
message(AUTHOR_WARNING "The directory's COMPILE_DEFINITIONS property is not empty: ${global_compile_definitions}")
endif()
get_directory_property(global_compile_options COMPILE_OPTIONS)
if(global_compile_options)
message(AUTHOR_WARNING "The directory's COMPILE_OPTIONS property is not empty: ${global_compile_options}")
endif()
get_directory_property(global_link_options LINK_OPTIONS)
if(global_link_options)
message(AUTHOR_WARNING "The directory's LINK_OPTIONS property is not empty: ${global_link_options}")
endif()
2 changes: 2 additions & 0 deletions cmake/crc32c.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,5 @@ if(HAVE_ARM64_CRC32C)
APPEND PROPERTY COMPILE_OPTIONS ${ARM_CRC_CXXFLAGS}
)
endif()

target_link_libraries(crc32c PRIVATE core)
2 changes: 1 addition & 1 deletion cmake/leveldb.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,4 @@ target_include_directories(leveldb
#TODO: figure out how to filter out:
# -Wconditional-uninitialized -Werror=conditional-uninitialized -Wsuggest-override -Werror=suggest-override

target_link_libraries(leveldb PRIVATE crc32c)
target_link_libraries(leveldb PRIVATE core crc32c)
3 changes: 3 additions & 0 deletions cmake/minisketch.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,6 @@ target_link_libraries(minisketch
minisketch_defs
$<TARGET_NAME_IF_EXISTS:minisketch_clmul>
)

target_link_libraries(minisketch_clmul PRIVATE core)
target_link_libraries(minisketch PRIVATE core)
136 changes: 136 additions & 0 deletions cmake/module/ProcessConfigurations.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# Copyright (c) 2023-present The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or https://opensource.org/license/mit/.

function(get_all_configs output)
get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(is_multi_config)
set(all_configs ${CMAKE_CONFIGURATION_TYPES})
else()
get_property(all_configs CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS)
if(NOT all_configs)
# See https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#default-and-custom-configurations
set(all_configs Debug Release RelWithDebInfo MinSizeRel)
endif()
endif()
set(${output} "${all_configs}" PARENT_SCOPE)
endfunction()


#[=[
Set the default build configuration.

See: https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#build-configurations.

On single-configuration generators, this function sets the CMAKE_BUILD_TYPE variable to
the default build configuration, which can be overridden by the user at configure time if needed.

On multi-configuration generators, this function rearranges the CMAKE_CONFIGURATION_TYPES list,
ensuring that the default build configuration appears first while maintaining the order of the
remaining configurations. The user can choose a build configuration at build time.
]=]
function(set_default_config config)
get_all_configs(all_configs)
if(NOT ${config} IN_LIST all_configs)
message(FATAL_ERROR "The default config is \"${config}\", but must be one of ${all_configs}.")
endif()

list(REMOVE_ITEM all_configs ${config})
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.15)
list(PREPEND all_configs ${config})
else()
set(all_configs ${config} ${all_configs})
endif()

get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(is_multi_config)
get_property(help_string CACHE CMAKE_CONFIGURATION_TYPES PROPERTY HELPSTRING)
set(CMAKE_CONFIGURATION_TYPES "${all_configs}" CACHE STRING "${help_string}" FORCE)
else()
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY
STRINGS "${all_configs}"
)
if(NOT CMAKE_BUILD_TYPE)
message(STATUS "Setting build type to \"${config}\" as none was specified")
get_property(help_string CACHE CMAKE_BUILD_TYPE PROPERTY HELPSTRING)
set(CMAKE_BUILD_TYPE "${config}" CACHE STRING "${help_string}" FORCE)
endif()
endif()
endfunction()

function(remove_c_flag_from_all_configs flag)
get_all_configs(all_configs)
foreach(config IN LISTS all_configs)
string(TOUPPER "${config}" config_uppercase)
set(flags "${CMAKE_C_FLAGS_${config_uppercase}}")
separate_arguments(flags)
list(FILTER flags EXCLUDE REGEX "${flag}")
list(JOIN flags " " new_flags)
set(CMAKE_C_FLAGS_${config_uppercase} "${new_flags}" PARENT_SCOPE)
endforeach()
endfunction()

function(remove_cxx_flag_from_all_configs flag)
get_all_configs(all_configs)
foreach(config IN LISTS all_configs)
string(TOUPPER "${config}" config_uppercase)
set(flags "${CMAKE_CXX_FLAGS_${config_uppercase}}")
separate_arguments(flags)
list(FILTER flags EXCLUDE REGEX "${flag}")
list(JOIN flags " " new_flags)
set(CMAKE_CXX_FLAGS_${config_uppercase} "${new_flags}" PARENT_SCOPE)
endforeach()
endfunction()

function(replace_c_flag_in_config config old_flag new_flag)
string(TOUPPER "${config}" config_uppercase)
string(REGEX REPLACE "(^| )${old_flag}( |$)" "\\1${new_flag}\\2" new_flags "${CMAKE_C_FLAGS_${config_uppercase}}")
set(CMAKE_C_FLAGS_${config_uppercase} "${new_flags}" PARENT_SCOPE)
endfunction()

function(replace_cxx_flag_in_config config old_flag new_flag)
string(TOUPPER "${config}" config_uppercase)
string(REGEX REPLACE "(^| )${old_flag}( |$)" "\\1${new_flag}\\2" new_flags "${CMAKE_CXX_FLAGS_${config_uppercase}}")
set(CMAKE_CXX_FLAGS_${config_uppercase} "${new_flags}" PARENT_SCOPE)
endfunction()

function(separate_by_configs options)
list(JOIN ${options} " " ${options}_ALL)
string(GENEX_STRIP "${${options}_ALL}" ${options}_ALL)
set(${options}_ALL "${${options}_ALL}" PARENT_SCOPE)

get_all_configs(all_configs)
foreach(config IN LISTS all_configs)
string(REGEX MATCHALL "\\$<\\$<CONFIG:${config}>[^\n]*>" match "${${options}}")
list(JOIN match " " match)
string(REPLACE "\$<\$<CONFIG:${config}>:" "" match "${match}")
string(REPLACE ">" "" match "${match}")
string(TOUPPER "${config}" conf_upper)
set(${options}_${conf_upper} "${match}" PARENT_SCOPE)
endforeach()
endfunction()

function(print_config_flags)
macro(print_flags config)
string(TOUPPER "${config}" config_uppercase)
message(" - Preprocessor defined macros ........ ${definitions_${config_uppercase}}")
message(" - CFLAGS ............................. ${CMAKE_C_FLAGS_${config_uppercase}}")
message(" - CXXFLAGS ........................... ${CMAKE_CXX_FLAGS_${config_uppercase}}")
message(" - LDFLAGS for executables ............ ${CMAKE_EXE_LINKER_FLAGS_${config_uppercase}}")
message(" - LDFLAGS for shared libraries ....... ${CMAKE_SHARED_LINKER_FLAGS_${config_uppercase}}")
endmacro()

get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(is_multi_config)
list(JOIN CMAKE_CONFIGURATION_TYPES " " configs)
message("Available build types (configurations) ${configs}")
foreach(config IN LISTS CMAKE_CONFIGURATION_TYPES)
message("'${config}' build type (configuration):")
print_flags(${config})
endforeach()
else()
message("Build type (configuration):")
message(" - CMAKE_BUILD_TYPE ................... ${CMAKE_BUILD_TYPE}")
print_flags(${CMAKE_BUILD_TYPE})
endif()
endfunction()
Loading