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
Changes from 1 commit
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
Next Next commit
Add cmake build
Signed-off-by: Dmitriy Khaustov aka xDimon <khaustov.dm@gmail.com>
xDimon committed Jun 22, 2020
commit 3ba2861dba1896084c566be2e5fadca8f4f3bc9d
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -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
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" ON)
#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)
19 changes: 19 additions & 0 deletions cmake/modules/SanitizeHelper.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Sanitize a variable according to cmake rules
# https://cmake.org/cmake/help/v3.10/manual/cmake-language.7.html#variable-references
# The NUL and ';' characters cannot be escaped in this context (see CMP0053)
macro(sanitize_variable PREFIX RAW_VAR SANITIZED_VAR)
# Escaping characters not in the supported list (see documentation) will
# work as long as the variable is not cached.

# Variable caching is achieved by writing the variable to a CMakeCache.txt
# file, where the escaped chars get interpreted. The issue occurs when the
# cache is read, as the chars are not getting escaped again and cause the
# read to fail.

# The safe way to sanitize a variable is not to escape these chars, but
# rather to replace them with a known supported one, here '_' is chosen.
# Not: this could lead to name collision in some rare case. These case can
# be handled manually by using a different prefix.
string(REGEX REPLACE
"([^a-zA-Z0-9/_.+-])" "_" ${SANITIZED_VAR} "${PREFIX}${RAW_VAR}")
endmacro()
18 changes: 18 additions & 0 deletions cmake/modules/TestSuite.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Allow to easily build test suites

macro(create_test_suite NAME)
enable_testing()
set(TARGET "check-${NAME}")
add_custom_target(${TARGET} COMMAND ${CMAKE_CTEST_COMMAND})

# If the magic target check-all exists, attach to it.
if(TARGET check-all)
add_dependencies(check-all ${TARGET})
endif()
endmacro(create_test_suite)

function(add_test_to_suite SUITE NAME)
add_executable(${NAME} EXCLUDE_FROM_ALL ${ARGN})
add_test(${NAME} ${NAME})
add_dependencies("check-${SUITE}" ${NAME})
endfunction(add_test_to_suite)
42 changes: 42 additions & 0 deletions src/libsecp256k1-config.h.cmake.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/* Copyright (c) 2017 The Bitcoin developers */

#pragma once

#cmakedefine HAVE___INT128

#cmakedefine USE_NUM_GMP
#cmakedefine USE_FIELD_INV_NUM
#cmakedefine USE_SCALAR_INV_NUM

#cmakedefine USE_NUM_NONE
#cmakedefine USE_FIELD_INV_BUILTIN
#cmakedefine USE_SCALAR_INV_BUILTIN

#cmakedefine USE_SCALAR_4X64
#cmakedefine USE_FIELD_5X52

#cmakedefine USE_SCALAR_8X32
#cmakedefine USE_FIELD_10X26

#cmakedefine USE_ASM_X86_64

#cmakedefine USE_ECMULT_STATIC_PRECOMPUTATION

#cmakedefine ECMULT_GEN_PREC_BITS
#if defined(ECMULT_GEN_PREC_BITS)
#define ECMULT_GEN_PREC_BITS ${CLIENT_BUILD_SUFFIX}
#else
#define ECMULT_GEN_PREC_BITS 4
#endif

#cmakedefine ECMULT_WINDOW_SIZE
#if defined(ECMULT_WINDOW_SIZE)
#define ECMULT_WINDOW_SIZE ${ECMULT_WINDOW_SIZE}
#else
#define ECMULT_WINDOW_SIZE 15
#endif

#cmakedefine ENABLE_MODULE_ECDH
#cmakedefine ENABLE_MODULE_MULTISET
#cmakedefine ENABLE_MODULE_RECOVERY
#cmakedefine ENABLE_MODULE_SCHNORR