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

Add cmake support #761

Closed
wants to merge 2 commits into from
Closed
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
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ libtool
*.lo
*.o
*~

build/
cmake-build-*
.idea/

src/libsecp256k1-config.h
src/libsecp256k1-config.h.in
src/ecmult_static_context.h
Expand Down
183 changes: 183 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
cmake_minimum_required(VERSION 3.5)
project(secp256k1)

enable_language(CXX)

# Add path for custom modules
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")

include(AddCompilerFlags)

# libsecp256k1 use a different set of flags.
add_compiler_flag(
-pedantic
-Wshadow
-Wno-unused-function
-Wno-nonnull
-Wno-overlength-strings
)

add_c_compiler_flag(
-std=c99
-Wno-long-long
)

# Default visibility is hidden on all targets.
set(CMAKE_C_VISIBILITY_PRESET hidden)

include_directories(
.
src
# For the config
${CMAKE_CURRENT_BINARY_DIR}/src
)

# The library
add_library(secp256k1 src/secp256k1.c)
target_include_directories(secp256k1 PUBLIC include)

# We need to link in GMP
find_package(GMP)
if(GMP_FOUND)
target_include_directories(secp256k1 PUBLIC ${GMP_INCLUDE_DIR})
target_link_libraries(secp256k1 ${GMP_LIBRARY})
set(USE_NUM_GMP 1)
set(USE_FIELD_INV_NUM 1)
set(USE_SCALAR_INV_NUM 1)
else()
set(USE_NUM_NONE 1)
set(USE_FIELD_INV_BUILTIN 1)
set(USE_SCALAR_INV_BUILTIN 1)
endif()

# We check if amd64 asm is supported.
check_c_source_compiles("
#include <stdint.h>
int main() {
uint64_t a = 11, tmp;
__asm__ __volatile__(\"movq \$0x100000000,%1; mulq %%rsi\" : \"+a\"(a) : \"S\"(tmp) : \"cc\", \"%rdx\");
return 0;
}
" USE_ASM_X86_64)

# We make sure __int128 is defined
include(CheckTypeSize)
check_type_size(__int128 SIZEOF___INT128)
if(SIZEOF___INT128 EQUAL 16)
set(HAVE___INT128 1)
else()
# If we do not support __int128, we should be falling back
# on 32bits implementations for field and scalar.
endif()

# Detect if we are on a 32 or 64 bits plateform and chose
# scalar and filed implementation accordingly
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
# 64 bits implementationr require either __int128 or asm support.
if (HAVE___INT128 OR USE_ASM_X86_64)
set(USE_SCALAR_4X64 1)
set(USE_FIELD_5X52 1)
else()
message(SEND_ERROR "Compiler does not support __int128 or insline assembly")
endif()
else()
set(USE_SCALAR_8X32 1)
set(USE_FIELD_10X26 1)
endif()

# Executable internal to secp256k1 need to have the HAVE_CONFIG_H define set.
# For convenience, we wrap this into a function.
function(link_secp256k1_internal NAME)
target_link_libraries(${NAME} secp256k1)
target_compile_definitions(${NAME} PRIVATE HAVE_CONFIG_H SECP256K1_BUILD)
endfunction(link_secp256k1_internal)

# Phony target to build benchmarks
add_custom_target(bench-secp256k1)

function(add_secp256k1_bench NAME)
add_executable(${NAME} EXCLUDE_FROM_ALL ${ARGN})
link_secp256k1_internal(${NAME})
add_dependencies(bench-secp256k1 ${NAME})
endfunction(add_secp256k1_bench)

# ECDH module
option(SECP256K1_ENABLE_MODULE_ECDH "Build libsecp256k1's ECDH module" OFF)
if(SECP256K1_ENABLE_MODULE_ECDH)
set(ENABLE_MODULE_ECDH 1)
add_secp256k1_bench(bench_ecdh src/bench_ecdh.c)
endif()

## MultiSet module
option(SECP256K1_ENABLE_MODULE_MULTISET "Build libsecp256k1's MULTISET module" OFF)
if(SECP256K1_ENABLE_MODULE_MULTISET)
set(ENABLE_MODULE_MULTISET 1)
add_secp256k1_bench(bench_multiset src/bench_multiset.c)
endif()

# Recovery module
option(SECP256K1_ENABLE_MODULE_RECOVERY "Build libsecp256k1's recovery module" ON)
if(SECP256K1_ENABLE_MODULE_RECOVERY)
set(ENABLE_MODULE_RECOVERY 1)
add_secp256k1_bench(bench_recover src/bench_recover.c)
endif()

# Schnorr module
option(SECP256K1_ENABLE_MODULE_SCHNORR "Build libsecp256k1's Schnorr module" ON)
if(SECP256K1_ENABLE_MODULE_SCHNORR)
set(ENABLE_MODULE_SCHNORR 1)
endif()

# Static precomputation for eliptic curve mutliplication
option(SECP256K1_ECMULT_STATIC_PRECOMPUTATION "Precompute libsecp256k1's eliptic curve mutliplication tables" ON)
if(SECP256K1_ECMULT_STATIC_PRECOMPUTATION)
set(USE_ECMULT_STATIC_PRECOMPUTATION 1)

include(NativeExecutable)
add_native_executable(gen_context src/gen_context.c)
target_compile_definitions(gen_context PRIVATE HAVE_CONFIG_H)

add_custom_command(
OUTPUT ecmult_static_context.h.stamp
COMMAND gen_context
COMMAND ${CMAKE_COMMAND} -E touch ecmult_static_context.h.stamp
VERBATIM
)

add_custom_target(ecmult_static_context
DEPENDS ecmult_static_context.h.stamp
)

add_dependencies(secp256k1 ecmult_static_context)

target_include_directories(secp256k1 PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src)
endif()

# Generate the config
configure_file(src/libsecp256k1-config.h.cmake.in src/libsecp256k1-config.h ESCAPE_QUOTES)
target_compile_definitions(secp256k1 PRIVATE HAVE_CONFIG_H SECP256K1_BUILD)

# Tests
option(SECP256K1_BUILD_TEST "Build secp256k1's unit tests" ON)
if(SECP256K1_BUILD_TEST)
include(TestSuite)
create_test_suite(secp256k1)

function(create_secp256k1_test NAME FILES)
add_test_to_suite(secp256k1 ${NAME} EXCLUDE_FROM_ALL ${FILES})
link_secp256k1_internal(${NAME})
endfunction()

create_secp256k1_test(secp256k1_tests src/tests.c)
target_compile_definitions(secp256k1_tests PRIVATE VERIFY)

create_secp256k1_test(exhaustive_tests src/tests_exhaustive.c)
# This should not be enabled at the same time as coverage is.
# TODO: support coverage.
target_compile_definitions(exhaustive_tests PRIVATE VERIFY)
endif(SECP256K1_BUILD_TEST)

# Benchmarks
add_secp256k1_bench(bench_verify src/bench_verify.c)
add_secp256k1_bench(bench_sign src/bench_sign.c)
add_secp256k1_bench(bench_internal src/bench_internal.c)
117 changes: 117 additions & 0 deletions cmake/modules/AddCompilerFlags.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# Allow to easily add flags for C and C++
include(CheckCXXCompilerFlag)
include(CheckCCompilerFlag)
include(SanitizeHelper)

function(check_compiler_flag RESULT LANGUAGE FLAG)
sanitize_variable("have_${LANGUAGE}_" ${FLAG} TEST_NAME)

if("${LANGUAGE}" STREQUAL "C")
CHECK_C_COMPILER_FLAG(${FLAG} ${TEST_NAME})
elseif("${LANGUAGE}" STREQUAL "CXX")
CHECK_CXX_COMPILER_FLAG(${FLAG} ${TEST_NAME})
else()
message(FATAL_ERROR "check_compiler_flag LANGUAGE should be C or CXX")
endif()
set(${RESULT} ${${TEST_NAME}} PARENT_SCOPE)
endfunction()

function(add_c_compiler_flag)
foreach(f ${ARGN})
check_compiler_flag(FLAG_IS_SUPPORTED C ${f})
if(${FLAG_IS_SUPPORTED})
string(APPEND CMAKE_C_FLAGS " ${f}")
endif()
endforeach()
set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} PARENT_SCOPE)
endfunction()

function(add_cxx_compiler_flag)
foreach(f ${ARGN})
check_compiler_flag(FLAG_IS_SUPPORTED CXX ${f})
if(${FLAG_IS_SUPPORTED})
string(APPEND CMAKE_CXX_FLAGS " ${f}")
endif()
endforeach()
set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} PARENT_SCOPE)
endfunction()

macro(add_compiler_flag)
add_c_compiler_flag(${ARGN})
add_cxx_compiler_flag(${ARGN})
endmacro()

macro(remove_c_compiler_flags)
if(NOT "${CMAKE_BUILD_TYPE}" STREQUAL "")
string(TOUPPER ${CMAKE_BUILD_TYPE} BUILD_TYPE)
set(BUILD_TYPE_C_FLAGS "CMAKE_C_FLAGS_${BUILD_TYPE}")
endif()

foreach(f ${ARGN})
string(REGEX REPLACE "${f}( |$)" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
if(NOT "${CMAKE_BUILD_TYPE}" STREQUAL "")
string(REGEX REPLACE "${f}( |$)" "" ${BUILD_TYPE_C_FLAGS} "${${BUILD_TYPE_C_FLAGS}}")
endif()
endforeach()
endmacro()

macro(remove_cxx_compiler_flags)
if(NOT "${CMAKE_BUILD_TYPE}" STREQUAL "")
string(TOUPPER ${CMAKE_BUILD_TYPE} BUILD_TYPE)
set(BUILD_TYPE_CXX_FLAGS "CMAKE_CXX_FLAGS_${BUILD_TYPE}")
endif()

foreach(f ${ARGN})
string(REGEX REPLACE "${f}( |$)" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
if(NOT "${CMAKE_BUILD_TYPE}" STREQUAL "")
string(REGEX REPLACE "${f}( |$)" "" ${BUILD_TYPE_CXX_FLAGS} "${${BUILD_TYPE_CXX_FLAGS}}")
endif()
endforeach()
endmacro()

macro(remove_compiler_flags)
remove_c_compiler_flags(${ARGN})
remove_cxx_compiler_flags(${ARGN})
endmacro()

function(add_cxx_compiler_flag_with_fallback TARGET_VAR FLAG FALLBACK)
# Remove the fallback flag if it exists, so that the main flag will override
# it if it was previously added.
remove_cxx_compiler_flags(${FALLBACK})

set(FLAG_CANDIDATE ${FLAG})
check_compiler_flag(FLAG_IS_SUPPORTED CXX ${FLAG_CANDIDATE})
if(NOT ${FLAG_IS_SUPPORTED})
set(FLAG_CANDIDATE ${FALLBACK})
check_compiler_flag(FLAG_IS_SUPPORTED CXX ${FLAG_CANDIDATE})
endif()

if(${FLAG_IS_SUPPORTED})
string(APPEND ${TARGET_VAR} " ${FLAG_CANDIDATE}")
set(${TARGET_VAR} ${${TARGET_VAR}} PARENT_SCOPE)
endif()
endfunction()

# Note that CMake does not provide any facility to check that a linker flag is
# supported by the compiler.
# However since CMake 3.2 introduced the CMP0056 policy, the
# CMAKE_EXE_LINKER_FLAGS variable is used by the try_compile function, so there
# is a workaround that allow for testing the linker flags.
function(add_linker_flag)
foreach(f ${ARGN})
sanitize_variable("have_linker_" ${f} FLAG_IS_SUPPORTED)

# Save the current linker flags
set(SAVE_CMAKE_EXE_LINKERFLAGS ${CMAKE_EXE_LINKER_FLAGS})
string(APPEND CMAKE_EXE_LINKER_FLAGS " ${f}")
# CHECK_CXX_COMPILER_FLAG calls CHECK_CXX_SOURCE_COMPILES which in turn
# calls try_compile, so it will check our flag
CHECK_CXX_COMPILER_FLAG("" ${FLAG_IS_SUPPORTED})

# If the flag is not supported restore CMAKE_EXE_LINKER_FLAGS
if(NOT ${FLAG_IS_SUPPORTED})
set(CMAKE_EXE_LINKER_FLAGS ${SAVE_CMAKE_EXE_LINKERFLAGS})
endif()
endforeach()
set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} PARENT_SCOPE)
endfunction()
24 changes: 24 additions & 0 deletions cmake/modules/FindGMP.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Try to find the GMP libraries
# GMP_FOUND - system has GMP lib
# GMP_INCLUDE_DIR - the GMP include directory
# GMP_LIBRARY - Library needed to use GMP
# GMPXX_LIBRARY - Library needed to use GMP C++ API

if(GMP_INCLUDE_DIR AND GMP_LIBRARY)
# Already in cache, be silent
set(GMP_FIND_QUIETLY TRUE)
endif()

find_path(GMP_INCLUDE_DIR NAMES gmp.h)
find_library(GMP_LIBRARY NAMES gmp libgmp)
find_library(GMPXX_LIBRARY NAMES gmpxx libgmpxx)

message(STATUS "GMP libs: " ${GMP_LIBRARY} " " ${GMPXX_LIBRARY})

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GMP DEFAULT_MSG GMP_INCLUDE_DIR GMP_LIBRARY)

mark_as_advanced(GMP_INCLUDE_DIR GMP_LIBRARY GMPXX_LIBRARY)

set(GMP_LIBRARIES ${GMP_LIBRARY} ${GMPXX_LIBRARY})
set(GMP_INCLUDE_DIRS ${GMP_INCLUDE_DIR})
50 changes: 50 additions & 0 deletions cmake/modules/NativeExecutable.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Allow to easily build native executable.
# Useful for cross compilation.

# If we are cross compiling, create a directory for native build.
set(NATIVE_BUILD_DIR "${CMAKE_BINARY_DIR}/native" CACHE PATH "Path to the native build directory")
set(NATIVE_BINARY_DIR "${NATIVE_BUILD_DIR}/bin" CACHE PATH "Path to the native binary directory")
set(NATIVE_BUILD_TARGET "${NATIVE_BUILD_DIR}/CMakeCache.txt")

if(CMAKE_CROSSCOMPILING AND NOT TARGET native-cmake-build)
file(MAKE_DIRECTORY ${NATIVE_BUILD_DIR})
add_custom_command(
OUTPUT ${NATIVE_BUILD_TARGET}
COMMAND ${CMAKE_COMMAND}
-G "${CMAKE_GENERATOR}"
"${CMAKE_SOURCE_DIR}"
"-DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}"
"-DCMAKE_RUNTIME_OUTPUT_DIRECTORY:PATH=${NATIVE_BINARY_DIR}"
WORKING_DIRECTORY ${NATIVE_BUILD_DIR}
VERBATIM USES_TERMINAL
)

add_custom_target(native-cmake-build DEPENDS ${NATIVE_BUILD_TARGET})
endif()

macro(add_native_executable NAME)
if(CMAKE_CROSSCOMPILING)
set(NATIVE_BINARY "${NATIVE_BINARY_DIR}/${NAME}")
add_custom_target("build-native-${NAME}"
COMMAND ${CMAKE_COMMAND}
--build "${NATIVE_BUILD_DIR}"
--target "${NAME}"
DEPENDS ${NATIVE_BUILD_TARGET}
BYPRODUCTS ${NATIVE_BINARY}
WORKING_DIRECTORY ${NATIVE_BUILD_DIR}
VERBATIM USES_TERMINAL
)

add_executable(${NAME} IMPORTED)
add_dependencies(${NAME} "build-native-${NAME}")
set_property(TARGET ${NAME} PROPERTY IMPORTED_LOCATION ${NATIVE_BINARY})
else()
add_executable(${NAME} EXCLUDE_FROM_ALL ${ARGN})
endif(CMAKE_CROSSCOMPILING)
endmacro(add_native_executable)

function(native_target_include_directories)
if(NOT CMAKE_CROSSCOMPILING)
target_include_directories(${ARGN})
endif(NOT CMAKE_CROSSCOMPILING)
endfunction(native_target_include_directories)
Loading