Skip to content

Instantly share code, notes, and snippets.

@shlomif
Created October 30, 2018 15:22
Show Gist options
  • Save shlomif/34da786a40386fe3b7e381beb2835dd7 to your computer and use it in GitHub Desktop.
Save shlomif/34da786a40386fe3b7e381beb2835dd7 to your computer and use it in GitHub Desktop.
#.rst:
# ECMExportSymbols
# ----------------
#
# This module provides the ``ecm_export_symbols`` function for providing
# versioned symbols within libraries.
#
# ::
#
# ecm_export_symbols(<target_name>
# [UNSTABLE_ABI]
# [FIRST_STABLE_VERSION <version_number>]
# )
#
# If GNU LD is detected, this function creates a file called <target_name>.map
# which will be used when linking shared libraries.
#
# ``<target_name>`` may be a library name or an alias.
#
# ``UNSTABLE_ABI`` may be used to signal this library ABI is not stable. The
# function will not append additional version to the mapping file.
#
# ``FIRST_STABLE_VERSION`` specifies the first version where the ABI is
# considered stable.
#
# <target_name>.map will contain at least an entry for the current library
# version.
# Unless ``UNSTABLE_ABI`` or ``FIRST_STABLE_VERSION`` are used, it will then
# create entries for each minor version, starting with 0.
#
# This function has no effect if ``target_name`` is not a SHARED library.
#
# Example usage:
#
# .. code-block:: cmake
#
# include(ECMExportSymbols)
#
# add_library (KF5Foo ${_SRCS})
# set_target_properties(KF5Foo PROPERTIES
# VERSION ${KF5FOO_VERSION_STRING}
# SOVERSION ${KF5FOO_SOVERSION}
# EXPORT_NAME "Foo"
# )
# ecm_export_symbols(KF5Foo)
#
# Since 5.49.0.
#=============================================================================
# Copyright 2018 Christophe Giboudeaux <christophe@krop.fr>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. The name of the author may not be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
include(CMakeParseArguments)
function(ecm_export_symbols target_name)
set(options UNSTABLE_ABI)
set(oneValueArgs)
set(multiValueArgs FIRST_STABLE_VERSION)
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(NOT TARGET ${target_name})
message(FATAL_ERROR "ecm_export_symbols was invoked with a non-existent target name. Check your syntax.")
endif()
# -Wl,--version-script only works with GNU LD
if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
return()
endif()
# Don't do anything if the target is not a shared library
get_target_property(_target_type ${target_name} TYPE)
if(NOT "${_target_type}" STREQUAL "SHARED_LIBRARY")
return()
endif()
# If ``target_name`` is an alias, get the aliased target name
get_target_property(_real_target_name ${target_name} ALIASED_TARGET)
if(_real_target_name)
set(target_name "${_real_target_name}")
endif()
# The target exists and is valid, set the map file name
set(_target_map_file "${CMAKE_CURRENT_BINARY_DIR}/${target_name}.map")
# Split _target_version to get the major and minor version.
get_target_property(_target_version ${target_name} VERSION)
if(NOT _target_version)
message(FATAL_ERROR "ecm_export_symbols was called before setting the ${_real_target_name} version.")
endif()
string(REPLACE "." ";" _target_version_list ${_target_version})
list(GET _target_version_list 0 _target_version_major)
list(GET _target_version_list 1 _target_version_minor)
# i.e: every symbol is 'global'
file(WRITE "${_target_map_file}" "${target_name}_${_target_version_major} { global: *; };")
# If the ABI is declared unstable, we don't have to call _ecm_generate_compatibility_list()
if(NOT ARG_UNSTABLE_ABI)
_ecm_generate_compatibility_list()
file(APPEND "${_target_map_file}" "${compatibility_list}\n")
endif()
set_target_properties(${target_name} PROPERTIES LINK_FLAGS "-Wl,--version-script=${CMAKE_CURRENT_BINARY_DIR}/${target_name}.map")
endfunction()
macro(_ecm_generate_compatibility_list)
if(DEFINED ARG_FIRST_STABLE_VERSION)
string(REPLACE "." ";" _first_stable_version ${ARG_FIRST_STABLE_VERSION})
list(GET _first_stable_version 0 _first_stable_version_major)
list(GET _first_stable_version 1 _first_stable_version_minor)
if(NOT ${_target_version_major} EQUAL ${_first_stable_version_major})
message(FATAL_ERROR "The ${target_name} major version and FIRST_STABLE_VERSION major version don't match.")
endif()
endif()
set(compatibility_list)
set(_index)
if(DEFINED _first_stable_version_minor)
set(_index "${_first_stable_version_minor}")
else()
set(_index "0")
endif()
set(compatibility_list "${compatibility_list}\n${target_name}_${_target_version_major}.${_index} {} ${target_name}_${_target_version_major};")
# The other items shall be backward compatible with the previous version
while(_index LESS _target_version_minor)
math(EXPR _next_index ${_index}+1)
set(compatibility_list "${compatibility_list}\n${target_name}_${_target_version_major}.${_next_index} {} ${target_name}_${_target_version_major}.${_index};")
math(EXPR _index "${_index}+1")
endwhile()
endmacro()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment