diff --git a/CMakeLists.txt b/CMakeLists.txt index e2a04c26..66c7fa64 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -81,6 +81,13 @@ if (BSD OR SOLARIS OR OSX) find_package(Argp) endif (BSD OR SOLARIS OR OSX) +# Disable symbol versioning in non UNIX platforms +if (UNIX) + find_package(ABIMap) +else (UNIX) + set(WITH_SYMBOL_VERSIONING OFF) +endif (UNIX) + # config.h checks include(ConfigureChecks.cmake) configure_file(config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h) @@ -132,6 +139,60 @@ if (UNIT_TESTING) add_subdirectory(tests) endif (UNIT_TESTING) +### SOURCE PACKAGE +if (WITH_SYMBOL_VERSIONING AND ABIMAP_FOUND) + # Get the current ABI version from source + get_filename_component(current_abi_path + "${CMAKE_SOURCE_DIR}/src/ABI/current" + ABSOLUTE) + + # Check if the ABI version should be updated + file(READ ${current_abi_path} CURRENT_ABI_CONTENT) + string(STRIP "${CURRENT_ABI_CONTENT}" CURRENT_ABI_VERSION) + + if (LIBRARY_VERSION VERSION_GREATER CURRENT_ABI_VERSION) + set(UPDATE_ABI TRUE) + endif () + + if (UPDATE_ABI) + message(STATUS "Library version bumped to ${LIBRARY_VERSION}: Updating ABI") + + # Get the list of header files + get_file_list("${PROJECT_NAME}_header_list" + DIRECTORIES "${CMAKE_SOURCE_DIR}/include/libssh" + FILES_PATTERNS "*.h") + + # Extract the symbols marked as "LIBSSH_API" from the header files + extract_symbols(${PROJECT_NAME}.symbols + HEADERS_LIST_FILE "${PROJECT_NAME}_header_list" + FILTER_PATTERN "LIBSSH_API" + COPY_TO "${CMAKE_SOURCE_DIR}/src/ABI/${PROJECT_NAME}-${LIBRARY_VERSION}.symbols") + + if (WITH_ABI_BREAK) + set(ALLOW_ABI_BREAK "BREAK_ABI") + endif() + + # Target we can depend on in 'make dist' + set(_SYMBOL_TARGET "${PROJECT_NAME}.map") + + # Set the path to the current map file + set(MAP_PATH "${CMAKE_SOURCE_DIR}/src/${_SYMBOL_TARGET}") + + # Generate the symbol version map file + generate_map_file(${_SYMBOL_TARGET} + SYMBOLS "${PROJECT_NAME}.symbols" + RELEASE_NAME_VERSION ${PROJECT_NAME}_${LIBRARY_VERSION} + CURRENT_MAP ${MAP_PATH} + COPY_TO ${MAP_PATH} + FINAL + ${ALLOW_ABI_BREAK}) + + # Write the current version to the source + file(WRITE ${current_abi_path} ${LIBRARY_VERSION}) + endif(UPDATE_ABI) +endif (WITH_SYMBOL_VERSIONING AND ABIMAP_FOUND) + +add_custom_target(dist COMMAND ${CMAKE_MAKE_PROGRAM} package_source DEPENDS ${_SYMBOL_TARGET}) message(STATUS "********************************************") message(STATUS "********** ${PROJECT_NAME} build options : **********") @@ -158,5 +219,8 @@ else (WITH_INTERNAL_DOC) message(STATUS "Public API documentation generation") endif (WITH_INTERNAL_DOC) message(STATUS "Benchmarks: ${WITH_BENCHMARKS}") +message(STATUS "Symbol versioning: ${WITH_SYMBOL_VERSIONING}") +message(STATUS "Allow ABI break: ${WITH_ABI_BREAK}") +message(STATUS "Release is final: ${WITH_FINAL}") message(STATUS "********************************************") diff --git a/DefineOptions.cmake b/DefineOptions.cmake index 77808276..eb60b097 100644 --- a/DefineOptions.cmake +++ b/DefineOptions.cmake @@ -16,6 +16,8 @@ option(SERVER_TESTING "Build with server tests; requires openssh and dropbear" O option(WITH_BENCHMARKS "Build benchmarks tools" OFF) option(WITH_EXAMPLES "Build examples" ON) option(WITH_NACL "Build with libnacl (curve25519)" ON) +option(WITH_SYMBOL_VERSIONING "Build with symbol versioning" ON) +option(WITH_ABI_BREAK "Allow ABI break" OFF) option(FUZZ_TESTING "Build with fuzzer for the server" OFF) if (WITH_ZLIB) set(WITH_LIBZ ON) @@ -34,3 +36,7 @@ endif (UNIT_TESTING) if (WITH_NACL) set(WITH_NACL ON) endif (WITH_NACL) + +if (WITH_ABI_BREAK) + set(WITH_SYMBOL_VERSIONING ON) +endif (WITH_ABI_BREAK) diff --git a/cmake/Modules/ExtractSymbols.cmake b/cmake/Modules/ExtractSymbols.cmake new file mode 100644 index 00000000..d37778e1 --- /dev/null +++ b/cmake/Modules/ExtractSymbols.cmake @@ -0,0 +1,88 @@ +# +# Copyright (c) 2018 Anderson Toshiyuki Sasaki +# +# Redistribution and use is allowed according to the terms of the New +# BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. +# + +#.rst: +# ExtractSymbols +# -------------- +# +# This is a helper script for FindABImap.cmake. +# +# Extract symbols from header files and output a list to a file. +# This script is run in build time to extract symbols from the provided header +# files. This way, symbols added or removed can be checked and used to update +# the symbol version script. +# +# All symbols followed by the character ``'('`` are extracted. If a +# ``FILTER_PATTERN`` is provided, only the lines containing the given string are +# considered. +# +# Expected defined variables +# -------------------------- +# +# ``HEADERS_LIST_FILE``: +# Required, expects a file containing the list of header files to be parsed. +# +# ``OUTPUT_PATH``: +# Required, expects the output file path. +# +# Optionally defined variables +# ---------------------------- +# +# ``FILTER_PATTERN``: +# Expects a string. Only lines containing the given string will be considered +# when extracting symbols. +# + +if (NOT DEFINED OUTPUT_PATH) + message(SEND_ERROR "OUTPUT_PATH not defined") +endif() + +if (NOT DEFINED HEADERS_LIST_FILE) + message(SEND_ERROR "HEADERS not defined") +endif() + +file(READ ${HEADERS_LIST_FILE} HEADERS_LIST) + +set(symbols) +foreach(header ${HEADERS_LIST}) + + # Filter only lines containing the FILTER_PATTERN + file(STRINGS ${header} contain_filter + REGEX "^.*${FILTER_PATTERN}.*[(]" + ) + + # Remove function-like macros + foreach(line ${contain_filter}) + if (NOT ${line} MATCHES ".*#[ ]*define") + list(APPEND not_macro ${line}) + endif() + endforeach() + + set(functions) + + # Get only the function names followed by '(' + foreach(line ${not_macro}) + string(REGEX MATCHALL "[a-zA-Z0-9_]+[ ]*[(]" func ${line}) + list(APPEND functions ${func}) + endforeach() + + set(extracted_symbols) + + # Remove '(' + foreach(line ${functions}) + string(REGEX REPLACE "[(]" "" symbol ${line}) + string(STRIP "${symbol}" symbol) + list(APPEND extracted_symbols ${symbol}) + endforeach() + + list(APPEND symbols ${extracted_symbols}) +endforeach() + +list(REMOVE_DUPLICATES symbols) + +file(WRITE ${OUTPUT_PATH} "${symbols}") diff --git a/cmake/Modules/FindABIMap.cmake b/cmake/Modules/FindABIMap.cmake new file mode 100644 index 00000000..78bfa36a --- /dev/null +++ b/cmake/Modules/FindABIMap.cmake @@ -0,0 +1,394 @@ +# +# Copyright (c) 2018 Anderson Toshiyuki Sasaki +# +# Redistribution and use is allowed according to the terms of the New +# BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. +# + +#.rst: +# FindABIMap +# ---------- +# +# This file provides functions to generate the symbol version script. It uses +# the ``abimap`` tool to generate and update the linker script file. It can be +# installed by calling:: +# +# $ pip install abimap +# +# The ``function generate_map_file`` generates a symbol version script +# containing the provided symbols. It defines a custom command which sets +# ``target_name`` as its ``OUTPUT``. +# +# The experimental function ``extract_symbols()`` is provided as a simple +# parser to extract the symbols from C header files. It simply extracts symbols +# followed by an opening '``(``'. It is recommended to use a filter pattern to +# select the lines to be considered. It defines a custom command which sets +# ``target_name`` as its output. +# +# The helper function ``get_files_list()`` is provided to find files given a +# name pattern. It defines a custom command which sets ``target_name`` as its +# output. +# +# Functions provided +# ------------------ +# +# :: +# +# generate_map_file(target_name +# RELEASE_NAME_VERSION release_name +# SYMBOLS symbols_file +# [CURRENT_MAP cur_map] +# [FINAL] +# [BREAK_ABI] +# [COPY_TO output] +# ) +# +# ``target_name``: +# Required, expects the name of the file to receive the generated symbol +# version script. It should be added as a dependency for the library. Use the +# linker option ``--version-script filename`` to add the version information +# to the symbols when building the library. +# +# ``RELEASE_NAME_VERSION``: +# Required, expects a string containing the name and version information to be +# added to the symbols in the format ``lib_name_1_2_3``. +# +# ``SYMBOLS``: +# Required, expects a file containing the list of symbols to be added to the +# symbol version script. +# +# ``CURRENT_MAP``: +# Optional. If given, the new set of symbols will be checked against the +# ones contained in the ``cur_map`` file and updated properly. If an +# incompatible change is detected and ``BREAK_ABI`` is not defined, the build +# will fail. +# +# ``FINAL``: +# Optional. If given, will provide the ``--final`` option to ``abimap`` tool, +# which will mark the modified release in the symbol version script with a +# special comment, preventing later changes. This option should be set when +# creating a library release and the resulting map file should be stored with +# the source code. +# +# ``BREAK_ABI``: +# Optional. If provided, will use ``abimap`` ``--allow-abi-break`` option, which +# accepts incompatible changes to the set of symbols. This is necessary if any +# previously existing symbol were removed. +# +# ``COPY_TO``: +# Optional, expects a string containing the path to where the generated +# map file will be copied. +# +# Example: +# +# .. code-block:: cmake +# +# find_package(ABIMap) +# generate_map_file("lib.map" +# RELEASE_NAME_VERSION "lib_1_0_0" +# SYMBOLS "symbol1;symbol2" +# ) +# +# This example would result in the symbol version script to be created in +# ``${CMAKE_CURRENT_BINARY_DIR}/lib.map`` containing the provided symbols. +# +# :: +# +# get_files_list(target_name +# DIRECTORIES dir1 [dir2 ...] +# FILES_PATTERNS exp1 [exp2 ...] +# [COPY_TO output] +# ) +# +# ``target_name``: +# Required, expects the name of the target to be created. A file named after +# the string given in ``target_name`` will be created in +# ``${CMAKE_CURRENT_BINARY_DIR}`` to receive the list of files found. +# +# ``DIRECTORIES``: +# Required, expects a list of directories paths. Only absolute paths are +# supported. +# +# ``FILES_PATTERN``: +# Required, expects a list of matching expressions to find the files to be +# considered. +# +# ``COPY_TO``: +# Optional, expects a string containing the path to where the file containing +# the list of files will be copied. +# +# This command searches the directories provided in ``DIRECTORIES`` for files +# matching any of the patterns provided in ``FILES_PATTERNS``. The obtained list +# is written to the path specified by ``output``. +# +# Example: +# +# .. code-block:: cmake +# +# find_package(ABIMap) +# get_files_list(target +# DIRECTORIES "/include/mylib" +# FILES_PATTERNS "*.h" +# COPY_TO "my_list.txt" +# ) +# +# Consider that ``/include/mylib`` contains 3 files, ``h1.h``, ``h2.h``, and +# ``h3.hpp`` +# +# Will result in a file ``my_list.txt`` containing:: +# +# ``h1.h;h2.h`` +# +# :: +# +# extract_symbols(target_name +# HEADERS_LIST_FILE headers_list +# [FILTER_PATTERN pattern] +# [COPY_TO output] +# ) +# +# ``target_name``: +# Required, expects the name of the target to be created. A file named after +# the string given in ``target_name`` will be created in +# ``${CMAKE_CURRENT_BINARY_DIR}`` to receive the list of symbols. +# +# ``HEADERS_LIST_FILE``: +# Required, expects a path to a file containing the list of header files to be +# parsed. +# +# ``FILTER_PATTERN``: +# Optional, expects a string. Only the lines containing the filter pattern +# will be considered. +# +# ``COPY_TO``: +# Optional, expects a string containing the path to where the file containing +# the found symbols will be copied. +# +# This command extracts the symbols from the files listed in +# ``headers_list`` and write them on the ``output`` file. If ``pattern`` +# is provided, then only the lines containing the string given in ``pattern`` +# will be considered. It is recommended to provide a ``FILTER_PATTERN`` to mark +# the lines containing exported function declaration, since this function is +# experimental and can return wrong symbols when parsing the header files. +# +# Example: +# +# .. code-block:: cmake +# +# find_package(ABIMap) +# extract_symbols("lib.symbols" +# HEADERS_LIST_FILE "headers_list" +# FILTER_PATTERN "API_FUNCTION" +# ) +# +# Where headers_list contains:: +# +# header1.h;header2.h +# +# Where ``header1.h`` contains:: +# +# API_FUNCTION int exported_func1(int a, int b); +# +# ``header2.h`` contains:: +# +# API_FUNCTION int exported_func2(int a); +# +# int private_func2(int b); +# +# Will result in a file ``lib.symbols`` in ``${CMAKE_CURRENT_BINARY_DIR}`` containing:: +# +# ``exported_func1;exported_func2`` +# + +# Search for python which is required +find_package(PythonInterp REQUIRED) + +# Search for abimap tool used to generate the map files +find_program(ABIMAP_EXECUTABLE NAMES abimap DOC "path to the abimap executable") +mark_as_advanced(ABIMAP_EXECUTABLE) + +if (NOT ABIMAP_EXECUTABLE AND UNIX) + message(STATUS "Could not find `abimap` in PATH." + " It can be found in PyPI as `abimap`" + " (try `pip install abimap`)") +else () + set(ABIMAP_FOUND TRUE) +endif () + +# Define helper scripts +set(_EXTRACT_SYMBOLS_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/ExtractSymbols.cmake) +set(_GENERATE_MAP_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/GenerateMap.cmake) +set(_GET_FILES_LIST_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/GetFilesList.cmake) + +function(get_file_list _TARGET_NAME) + + set(one_value_arguments + COPY_TO + ) + + set(multi_value_arguments + DIRECTORIES + FILES_PATTERNS + ) + + cmake_parse_arguments(_get_files_list + "" + "${one_value_arguments}" + "${multi_value_arguments}" + ${ARGN} + ) + + # The DIRS argument is required + if (NOT DEFINED _get_files_list_DIRECTORIES) + message(FATAL_ERROR "No directories paths provided. Provide a list of" + " directories paths containing header files." + ) + endif() + + # The FILES_PATTERNS argument is required + if (NOT DEFINED _get_files_list_FILES_PATTERNS) + message(FATAL_ERROR "No matching expressions provided. Provide a list" + " of matching patterns for the header files." + ) + endif() + + get_filename_component(_get_files_list_OUTPUT_PATH + "${CMAKE_CURRENT_BINARY_DIR}/${_TARGET_NAME}" + ABSOLUTE + ) + + add_custom_command( + OUTPUT ${_TARGET_NAME} + COMMAND ${CMAKE_COMMAND} + -DOUTPUT_PATH="${_get_files_list_OUTPUT_PATH}" + -DDIRECTORIES="${_get_files_list_DIRECTORIES}" + -DFILES_PATTERNS="${_get_files_list_FILES_PATTERNS}" + -P ${_GET_FILES_LIST_SCRIPT} + COMMENT + "Searching for files" + ) + + if (DEFINED _get_files_list_COPY_TO) + # Copy the generated file back to the COPY_TO + add_custom_target(copy_headers_list_${TARGET_NAME} ALL + COMMAND + ${CMAKE_COMMAND} -E copy_if_different ${_TARGET_NAME} ${_get_files_list_COPY_TO} + DEPENDS "${_TARGET_NAME}" + COMMENT "Copying ${_TARGET_NAME} to ${_get_files_list_COPY_TO}" + ) + endif() +endfunction() + +function(extract_symbols _TARGET_NAME) + + set(one_value_arguments + FILTER_PATTERN + HEADERS_LIST_FILE + COPY_TO + ) + + set(multi_value_arguments + ) + + cmake_parse_arguments(_extract_symbols + "" + "${one_value_arguments}" + "${multi_value_arguments}" + ${ARGN} + ) + + # The HEADERS_LIST_FILE argument is required + if (NOT DEFINED _extract_symbols_HEADERS_LIST_FILE) + message(FATAL_ERROR "No header files given. Provide a list of header" + " files containing exported symbols." + ) + endif() + + get_filename_component(_extract_symbols_OUTPUT_PATH + "${CMAKE_CURRENT_BINARY_DIR}/${_TARGET_NAME}" + ABSOLUTE + ) + + add_custom_target(${_TARGET_NAME} + COMMAND ${CMAKE_COMMAND} + -DOUTPUT_PATH="${_extract_symbols_OUTPUT_PATH}" + -DHEADERS_LIST_FILE="${_extract_symbols_HEADERS_LIST_FILE}" + -DFILTER_PATTERN=${_extract_symbols_FILTER_PATTERN} + -P ${_EXTRACT_SYMBOLS_SCRIPT} + DEPENDS ${_extract_symbols_HEADERS_LIST_FILE} + COMMENT "Extracting symbols from headers") + + if (DEFINED _extract_symbols_COPY_TO) + file(READ "${CMAKE_CURRENT_BINARY_DIR}/${_TARGET_NAME}" SYMBOL_CONTENT) + string(REPLACE ";" "\n" SYMBOL_CONTENT_NEW "${SYMBOL_CONTENT}") + file(WRITE "${_extract_symbols_COPY_TO}" "${SYMBOL_CONTENT_NEW}") + endif() +endfunction() + +function(generate_map_file _TARGET_NAME) + + set(options + FINAL + BREAK_ABI + ) + + set(one_value_arguments + RELEASE_NAME_VERSION + SYMBOLS + CURRENT_MAP + COPY_TO + ) + + set(multi_value_arguments + ) + + cmake_parse_arguments(_generate_map_file + "${options}" + "${one_value_arguments}" + "${multi_value_arguments}" + ${ARGN} + ) + + if (NOT DEFINED _generate_map_file_SYMBOLS) + message(FATAL_ERROR "No symbols file provided." + ) + endif() + + if (NOT DEFINED _generate_map_file_RELEASE_NAME_VERSION) + message(FATAL_ERROR "Release name and version not provided." + " (e.g. libname_1_0_0" + ) + endif() + + # Set generated map file path + get_filename_component(_generate_map_file_OUTPUT_PATH + "${CMAKE_CURRENT_BINARY_DIR}/${_TARGET_NAME}" + ABSOLUTE + ) + + add_custom_command( + OUTPUT ${_TARGET_NAME} + COMMAND ${CMAKE_COMMAND} + -DABIMAP_EXECUTABLE=${ABIMAP_EXECUTABLE} + -DSYMBOLS="${_generate_map_file_SYMBOLS}" + -DCURRENT_MAP=${_generate_map_file_CURRENT_MAP} + -DOUTPUT_PATH="${_generate_map_file_OUTPUT_PATH}" + -DFINAL=${_generate_map_file_FINAL} + -DBREAK_ABI=${_generate_map_file_BREAK_ABI} + -DRELEASE_NAME_VERSION=${_generate_map_file_RELEASE_NAME_VERSION} + -P ${_GENERATE_MAP_SCRIPT} + DEPENDS ${_generate_map_file_SYMBOLS} + COMMENT "Generating the map ${_TARGET_NAME}" + ) + + if (DEFINED _generate_map_file_COPY_TO) + # Copy the generated map back to the COPY_TO + add_custom_target(copy_map_${_TARGET_NAME} ALL + COMMAND + ${CMAKE_COMMAND} -E copy_if_different ${_TARGET_NAME} ${_generate_map_file_COPY_TO} + DEPENDS "${_TARGET_NAME}" + COMMENT "Copying ${_TARGET_NAME} to ${_generate_map_file_COPY_TO}" + ) + endif() +endfunction() diff --git a/cmake/Modules/GenerateMap.cmake b/cmake/Modules/GenerateMap.cmake new file mode 100644 index 00000000..c22dfbc3 --- /dev/null +++ b/cmake/Modules/GenerateMap.cmake @@ -0,0 +1,118 @@ +# +# Copyright (c) 2018 Anderson Toshiyuki Sasaki +# +# Redistribution and use is allowed according to the terms of the New +# BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. +# + +#.rst: +# GenerateMap +# ----------- +# +# This is a helper script for FindABImap.cmake. +# +# Generates a symbols version script using the abimap tool. +# This script is run in build time to use the correct command depending on the +# existence of the file provided ``CURRENT_MAP``. +# +# If the file exists, the ``abimap update`` subcommand is used to update the +# existing map. Otherwise, the ``abimap new`` subcommand is used to create a new +# map file. +# +# If the file provided in ``CURRENT_MAP`` exists, it is copied to the +# ``OUTPUT_PATH`` before updating. +# This is required because ``abimap`` do not generate output if no symbols were +# changed when updating an existing file. +# +# Expected defined variables +# -------------------------- +# +# ``SYMBOLS``: +# Required file containing the symbols to be used as input. Usually this is +# the ``OUTPUT`` generated by ``extract_symbols()`` function provided in +# FindABImap.cmake +# +# ``RELEASE_NAME_VERSION``: +# Required, expects the library name and version information to be added to +# the symbols in the format ``library_name_1_2_3`` +# +# ``CURRENT_MAP``: +# Required, expects the path to the current map file (or the path were it +# should be) +# +# ``OUTPUT_PATH``: +# Required, expects the output file path. +# +# ``ABIMAP_EXECUTABLE``: +# Required, expects the path to the ``abimap`` tool. +# +# Optionally defined variables +# ---------------------------- +# +# ``FINAL``: +# If defined, will mark the modified set of symbols in the symbol version +# script as final, preventing later changes using ``abimap``. +# +# ``BREAK_ABI``: +# If defined, the build will not fail if symbols were removed. +# If defined and a symbol is removed, a new release is created containing +# all symbols from all released versions. This makes an incompatible release. +# + +if (NOT DEFINED RELEASE_NAME_VERSION) + message(SEND_ERROR "RELEASE_NAME_VERSION not defined") +endif() + +if (NOT DEFINED SYMBOLS) + message(SEND_ERROR "SYMBOLS not defined") +endif() + +if (NOT DEFINED CURRENT_MAP) + message(SEND_ERROR "CURRENT_MAP not defined") +endif() + +if (NOT DEFINED OUTPUT_PATH) + message(SEND_ERROR "OUTPUT_PATH not defined") +endif() + +if (NOT ABIMAP_EXECUTABLE) + message(SEND_ERROR "ABIMAP_EXECUTABLE not defined") +endif() + +set(ARGS_LIST) + +if (FINAL) + list(APPEND ARGS_LIST "--final") +endif() + +if (EXISTS ${CURRENT_MAP}) + if (BREAK_ABI) + list(APPEND ARGS_LIST "--allow-abi-break") + endif() + + execute_process( + COMMAND + ${CMAKE_COMMAND} -E copy_if_different ${CURRENT_MAP} ${OUTPUT_PATH} + COMMAND + ${ABIMAP_EXECUTABLE} update ${ARGS_LIST} + -r ${RELEASE_NAME_VERSION} + -i ${SYMBOLS} + -o ${OUTPUT_PATH} + ${CURRENT_MAP} + RESULT_VARIABLE result + ) +else () + execute_process( + COMMAND + ${ABIMAP_EXECUTABLE} new ${ARGS_LIST} + -r ${RELEASE_NAME_VERSION} + -i ${SYMBOLS} + -o ${OUTPUT_PATH} + RESULT_VARIABLE result + ) +endif() + +if (NOT "${result}" STREQUAL "0") + message(SEND_ERROR "Map generation failed") +endif() diff --git a/cmake/Modules/GetFilesList.cmake b/cmake/Modules/GetFilesList.cmake new file mode 100644 index 00000000..e3e8a2a8 --- /dev/null +++ b/cmake/Modules/GetFilesList.cmake @@ -0,0 +1,59 @@ +# +# Copyright (c) 2018 Anderson Toshiyuki Sasaki +# +# Redistribution and use is allowed according to the terms of the New +# BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. +# + +#.rst: +# GetFilesList +# ------------ +# +# This is a helper script for FindABImap.cmake. +# +# Search in the provided directories for files matching the provided pattern. +# The list of files is then written to the output file. +# +# Expected defined variables +# -------------------------- +# +# ``DIRECTORIES``: +# Required, expects a list of directories paths. +# +# ``FILES_PATTERNS``: +# Required, expects a list of patterns to be used to search files +# +# ``OUTPUT_PATH``: +# Required, expects the output file path. + +if (NOT DEFINED DIRECTORIES) + message(SEND_ERROR "DIRECTORIES not defined") +endif() + +if (NOT DEFINED FILES_PATTERNS) + message(SEND_ERROR "FILES_PATTERNS not defined") +endif() + +if (NOT DEFINED OUTPUT_PATH) + message(SEND_ERROR "OUTPUT_PATH not defined") +endif() + +string(REPLACE " " ";" DIRECTORIES_LIST "${DIRECTORIES}") +string(REPLACE " " ";" FILES_PATTERNS_LIST "${FILES_PATTERNS}") + +# Create the list of expressions for the files +set(glob_expressions) +foreach(dir ${DIRECTORIES_LIST}) + foreach(exp ${FILES_PATTERNS_LIST}) + list(APPEND glob_expressions + "${dir}/${exp}" + ) + endforeach() +endforeach() + +# Create the list of files +file(GLOB files ${glob_expressions}) + +# Write to the output +file(WRITE ${OUTPUT_PATH} "${files}") diff --git a/obj/build_make.sh b/obj/build_make.sh index a030ed71..7c559141 100755 --- a/obj/build_make.sh +++ b/obj/build_make.sh @@ -63,7 +63,7 @@ function clean_build_dir() { function usage () { echo "Usage: `basename $0` [--prefix /install_prefix|--build [debug|final]|--clean|--verbose|--libsuffix (32|64)|--help|--clang|--cmakedir /directory|--make -(gmake|make)|--ccompiler (gcc|cc)|--withstaticlib|--unittesting|--clientunittesting|--withserver]" +(gmake|make)|--ccompiler(gcc|cc)|--withstaticlib|--unittesting|--clientunittesting|--withserver|--withoutsymbolversioning]" cleanup_and_exit } @@ -145,6 +145,12 @@ while test -n "$1"; do *-withserver) OPTIONS="${OPTIONS} -DWITH_SERVER=ON" ;; + *-withoutsymbolversioning) + OPTIONS="${OPTIONS} -DWITH_SYMBOL_VERSIONING=OFF" + ;; + *-finalrelease) + OPTIONS="${OPTIONS} -DWITH_FINAL=ON" + ;; ----noarg) echo "$ARG does not take an argument" cleanup_and_exit diff --git a/src/ABI/current b/src/ABI/current index ae153944..a84947d6 100644 --- a/src/ABI/current +++ b/src/ABI/current @@ -1 +1 @@ -4.5.0 \ No newline at end of file +4.5.0 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6cf44158..e5746b15 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,5 +1,3 @@ -project(libssh-library C) - set(LIBSSH_PUBLIC_INCLUDE_DIRS ${libssh_SOURCE_DIR}/include CACHE INTERNAL "libssh public include directories" @@ -265,10 +263,52 @@ include_directories( ${LIBSSH_PRIVATE_INCLUDE_DIRS} ) +# Set the path to the default map file +set(MAP_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}.map") + +if (WITH_SYMBOL_VERSIONING AND ABIMAP_FOUND) + # Get the list of header files + get_file_list("dev_header_list" + DIRECTORIES "${LIBSSH_PUBLIC_INCLUDE_DIRS}/libssh" + FILES_PATTERNS "*.h") + + # Extract the symbols marked as "LIBSSH_API" from the header files + extract_symbols("${PROJECT_NAME}_dev.symbols" + HEADERS_LIST_FILE "dev_header_list" + FILTER_PATTERN "LIBSSH_API") + + if (WITH_ABI_BREAK) + set(ALLOW_ABI_BREAK "BREAK_ABI") + endif() + + # Generate the symbol version map file + generate_map_file("${PROJECT_NAME}_dev.map" + SYMBOLS "${PROJECT_NAME}_dev.symbols" + RELEASE_NAME_VERSION ${PROJECT_NAME}_AFTER_${LIBRARY_VERSION} + CURRENT_MAP ${MAP_PATH} + ${ALLOW_ABI_BREAK}) + + set(libssh_SRCS + ${libssh_SRCS} + ${PROJECT_NAME}_dev.map + ) +endif (WITH_SYMBOL_VERSIONING AND ABIMAP_FOUND) + add_library(${LIBSSH_SHARED_LIBRARY} SHARED ${libssh_SRCS}) target_link_libraries(${LIBSSH_SHARED_LIBRARY} ${LIBSSH_LINK_LIBRARIES}) +if (WITH_SYMBOL_VERSIONING) + if (ABIMAP_FOUND) + # Change path to devel map file + set(MAP_PATH "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_dev.map") + endif (ABIMAP_FOUND) + + set_target_properties(${LIBSSH_SHARED_LIBRARY} + PROPERTIES LINK_FLAGS + "-Wl,--version-script,\"${MAP_PATH}\"") +endif (WITH_SYMBOL_VERSIONING) + set_target_properties( ${LIBSSH_SHARED_LIBRARY} PROPERTIES