diff --git a/CMakeLists.txt b/CMakeLists.txt index 013300a..517b2c3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,12 @@ project( LANGUAGES CXX ) +# See notes in GNUmakefile about using abi-compliance-checker. +# soversion is major ABI version. +set( abi_version 1.0.0 ) +string( REPLACE "." ";" abi_list "${abi_version}" ) +list( GET abi_list 0 soversion ) + include( CheckCXXCompilerFlag ) # When built as a sub-project, add a namespace to make targets unique, @@ -32,6 +38,16 @@ endif() #------------------------------------------------------------------------------- # Options +if (testsweeper_is_project) + set( log "" CACHE STRING "Shorthand for CMAKE_MESSAGE_LOG_LEVEL" ) + set_property( CACHE log PROPERTY STRINGS + FATAL_ERROR SEND_ERROR WARNING AUTHOR_WARNING DEPRECATION + NOTICE STATUS VERBOSE DEBUG TRACE ) + if (log) + set( CMAKE_MESSAGE_LOG_LEVEL "${log}" ) + endif() +endif() + option( BUILD_SHARED_LIBS "Build shared libraries" true ) option( build_tests "Build test suite" "${testsweeper_is_project}" ) option( color "Use ANSI color output" true ) @@ -68,6 +84,8 @@ use_openmp = ${use_openmp} testsweeper_install = ${testsweeper_install} testsweeper_is_project = ${testsweeper_is_project} testsweeper_ = ${testsweeper_} +abi_version = ${abi_version} +soversion = ${soversion} " ) #------------------------------------------------------------------------------- @@ -130,6 +148,8 @@ set_target_properties( CXX_STANDARD_REQUIRED true # prohibit < c++17 CXX_EXTENSIONS false # prohibit gnu++17 WINDOWS_EXPORT_ALL_SYMBOLS ON + VERSION "${abi_version}" + SOVERSION "${soversion}" ) if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.15) diff --git a/GNUmakefile b/GNUmakefile index 5b7bc5d..4f55823 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -44,12 +44,41 @@ ifeq ($(origin LD),default) LD = $(CXX) endif +# Use abi-compliance-checker to compare the ABI (application binary +# interface) of 2 releases. Changing the ABI does not necessarily change +# the API (application programming interface). Rearranging a struct or +# changing a by-value argument from int64 to int doesn't change the +# API--no source code changes are required, just a recompile. +# +# if structs or routines are changed or removed: +# bump major version and reset minor, revision = 0; +# else if structs or routines are added: +# bump minor version and reset revision = 0; +# else (e.g., bug fixes): +# bump revision +# +# soversion is major ABI version. +abi_version = 1.0.0 +soversion = ${word 1, ${subst ., ,${abi_version}}} + +#------------------------------------------------------------------------------- # auto-detect OS # $OSTYPE may not be exported from the shell, so echo it ostype := $(shell echo $${OSTYPE}) ifneq ($(findstring darwin, $(ostype)),) # MacOS is darwin macos = 1 + # MacOS needs shared library's path set, and shared library version. + ldflags_shared = -install_name @rpath/${notdir $@} \ + -current_version ${abi_version} \ + -compatibility_version ${soversion} + so = dylib + so2 = .dylib + # on macOS, .dylib comes after version: libfoo.4.dylib +else + so = so + so1 = .so + # on Linux, .so comes before version: libfoo.so.4 endif #------------------------------------------------------------------------------- @@ -57,19 +86,11 @@ endif ifneq ($(static),1) CXXFLAGS += -fPIC LDFLAGS += -fPIC - lib_ext = so + lib_ext = ${so} else lib_ext = a endif -#------------------------------------------------------------------------------- -# MacOS needs shared library's path set -ifeq ($(macos),1) - install_name = -install_name @rpath/$(notdir $@) -else - install_name = -endif - #------------------------------------------------------------------------------- # Files @@ -124,7 +145,7 @@ install: lib mkdir -p $(DESTDIR)$(prefix)/include mkdir -p $(DESTDIR)$(prefix)/lib$(LIB_SUFFIX) cp $(headers) $(DESTDIR)$(prefix)/include - cp $(lib) $(DESTDIR)$(prefix)/lib$(LIB_SUFFIX) + cp -av libtestsweeper.* $(DESTDIR)$(prefix)/lib$(LIB_SUFFIX) uninstall: $(RM) $(addprefix $(DESTDIR)$(prefix)/include/, $(headers)) @@ -136,12 +157,21 @@ $(lib_obj) $(tester_obj): make.inc #------------------------------------------------------------------------------- # TestSweeper library -lib_a = libtestsweeper.a -lib_so = libtestsweeper.so -lib = libtestsweeper.$(lib_ext) +lib_name = libtestsweeper +lib_a = ${lib_name}.a +lib_so = ${lib_name}.${so} +lib = ${lib_name}.${lib_ext} + +# libfoo.so.3 or libfoo.3.dylib +lib_so_abi_version = ${lib_name}${so1}.${abi_version}${so2} +lib_so_soversion = ${lib_name}${so1}.${soversion}${so2} + +${lib_so_abi_version}: ${lib_obj} + ${LD} ${LDFLAGS} -shared ${ldflags_shared} ${lib_obj} ${LIBS} -o $@ -$(lib_so): $(lib_obj) - $(LD) $(LDFLAGS) -shared $(install_name) $(lib_obj) $(LIBS) -o $@ +${lib_so}: | ${lib_so_abi_version} + ln -fs ${lib_so_abi_version} ${lib_so} + ln -fs ${lib_so_abi_version} ${lib_so_soversion} $(lib_a): $(lib_obj) $(RM) $@ @@ -217,10 +247,15 @@ echo: @echo "static = '$(static)'" @echo "id = '$(id)'" @echo "last_id = '$(last_id)'" + @echo "abi_version = '${abi_version}'" + @echo "soversion = '${soversion}'" @echo + @echo "lib_name = ${lib_name}" @echo "lib_a = $(lib_a)" @echo "lib_so = $(lib_so)" @echo "lib = $(lib)" + @echo "lib_so_abi_version = ${lib_so_abi_version}" + @echo "lib_so_soversion = ${lib_so_soversion}" @echo @echo "lib_src = $(lib_src)" @echo @@ -240,6 +275,7 @@ echo: @echo "LD = $(LD)" @echo "LDFLAGS = $(LDFLAGS)" @echo "LIBS = $(LIBS)" + @echo "ldflags_shared = ${ldflags_shared}" @echo @echo "TEST_LDFLAGS = $(TEST_LDFLAGS)" @echo "TEST_LIBS = $(TEST_LIBS)" diff --git a/GNUmakefile.subdir b/GNUmakefile.subdir new file mode 100644 index 0000000..6f00966 --- /dev/null +++ b/GNUmakefile.subdir @@ -0,0 +1,50 @@ +# Copyright (c) 2017-2023, University of Tennessee. All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# This program is free software: you can redistribute it and/or modify it under +# the terms of the BSD 3-Clause license. See the accompanying LICENSE file. +# +# Subdirectories include this makefile to forward rules to the top level makefile. +# Define ${top} for where the top level is. +# Example: src/GNUmakefile: +# top = .. +# include ${top}/GNUmakefile.subdir + +.SUFFIXES: + +pwd := ${shell pwd} +abs_top := ${abspath ${top}} +cdir := ${subst ${abs_top}/,,${pwd}} + +.DEFAULT_GOAL: ${cdir} +.PHONY: ${cdir} + +${cdir}: + cd ${top} && ${MAKE} $@ + +DONT_FORWARD += echo_sub +echo_sub: + @echo "pwd ${pwd}" + @echo "abs_top ${abs_top}" + @echo "cdir ${cdir}" + +# ------------------------------------------------------------------------------ +ifneq (${MAKECMDGOALS},) + +# If arguments are given, presumably files like test.o, forward them to top +# with cdir prefix. +# All files are forwarded as one rule, based on first; rest are quietly ignored. +goals := ${filter-out echo ${DONT_FORWARD}, ${MAKECMDGOALS}} +forward := ${addprefix ${cdir}/, ${goals}} +first := ${firstword ${goals}} +rest := ${wordlist 2, ${words ${goals}}, ${goals}} + +${first}: force + cd ${top} && ${MAKE} ${forward} + +${rest}: force + @echo > /dev/null + +endif +# ------------------------------------------------------------------------------ + +force: ; diff --git a/test/GNUmakefile b/test/GNUmakefile new file mode 100644 index 0000000..042d0c6 --- /dev/null +++ b/test/GNUmakefile @@ -0,0 +1,2 @@ +top = .. +include ${top}/GNUmakefile.subdir diff --git a/testsweeper.hh b/testsweeper.hh index 081ecd5..d073fb3 100644 --- a/testsweeper.hh +++ b/testsweeper.hh @@ -421,13 +421,6 @@ public: values_[ index_ ] = value; } - [[deprecated( "use param() instead of param.value(). To be removed 2024-03." )]] - T& value() - { - used_ = true; - return values_[ index_ ]; - } - void set_default( const T& default_value ) { default_value_ = default_value;