From 3ba2861dba1896084c566be2e5fadca8f4f3bc9d Mon Sep 17 00:00:00 2001 From: Dmitriy Khaustov aka xDimon Date: Mon, 22 Jun 2020 15:32:25 +0300 Subject: [PATCH 1/2] Add cmake build Signed-off-by: Dmitriy Khaustov aka xDimon --- .gitignore | 5 + CMakeLists.txt | 183 +++++++++++++++++++++++++++ cmake/modules/AddCompilerFlags.cmake | 117 +++++++++++++++++ cmake/modules/FindGMP.cmake | 24 ++++ cmake/modules/NativeExecutable.cmake | 50 ++++++++ cmake/modules/SanitizeHelper.cmake | 19 +++ cmake/modules/TestSuite.cmake | 18 +++ src/libsecp256k1-config.h.cmake.in | 42 ++++++ 8 files changed, 458 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 cmake/modules/AddCompilerFlags.cmake create mode 100644 cmake/modules/FindGMP.cmake create mode 100644 cmake/modules/NativeExecutable.cmake create mode 100644 cmake/modules/SanitizeHelper.cmake create mode 100644 cmake/modules/TestSuite.cmake create mode 100644 src/libsecp256k1-config.h.cmake.in diff --git a/.gitignore b/.gitignore index cb4331aa90..2584b73bec 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000000..018e43879e --- /dev/null +++ b/CMakeLists.txt @@ -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 + 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) diff --git a/cmake/modules/AddCompilerFlags.cmake b/cmake/modules/AddCompilerFlags.cmake new file mode 100644 index 0000000000..d0109215ff --- /dev/null +++ b/cmake/modules/AddCompilerFlags.cmake @@ -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() diff --git a/cmake/modules/FindGMP.cmake b/cmake/modules/FindGMP.cmake new file mode 100644 index 0000000000..d8db762bb6 --- /dev/null +++ b/cmake/modules/FindGMP.cmake @@ -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}) diff --git a/cmake/modules/NativeExecutable.cmake b/cmake/modules/NativeExecutable.cmake new file mode 100644 index 0000000000..31fdce14ec --- /dev/null +++ b/cmake/modules/NativeExecutable.cmake @@ -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) diff --git a/cmake/modules/SanitizeHelper.cmake b/cmake/modules/SanitizeHelper.cmake new file mode 100644 index 0000000000..6677cc41e6 --- /dev/null +++ b/cmake/modules/SanitizeHelper.cmake @@ -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() diff --git a/cmake/modules/TestSuite.cmake b/cmake/modules/TestSuite.cmake new file mode 100644 index 0000000000..f9b2cf08b4 --- /dev/null +++ b/cmake/modules/TestSuite.cmake @@ -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) diff --git a/src/libsecp256k1-config.h.cmake.in b/src/libsecp256k1-config.h.cmake.in new file mode 100644 index 0000000000..791dd34fe8 --- /dev/null +++ b/src/libsecp256k1-config.h.cmake.in @@ -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 From a9830be0340dfbdff33c83cc86a1bf8c4a6fcdde Mon Sep 17 00:00:00 2001 From: Dmitriy Khaustov Date: Mon, 22 Jun 2020 16:38:25 +0300 Subject: [PATCH 2/2] Update CMakeLists.txt --- CMakeLists.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 018e43879e..d2d09deb56 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -109,11 +109,11 @@ if(SECP256K1_ENABLE_MODULE_ECDH) 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() +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)