diff --git a/cmake/ecbuild_compiler_flags.cmake b/cmake/ecbuild_compiler_flags.cmake index 594a49cf..77c62f33 100644 --- a/cmake/ecbuild_compiler_flags.cmake +++ b/cmake/ecbuild_compiler_flags.cmake @@ -72,6 +72,142 @@ macro( ecbuild_compiler_flags _lang ) endmacro() +############################################################################## +#.rst: +# +# ecbuild_purge_compiler_flags +# ====================== +# +# Purge compiler flags for a given language :: +# +# ecbuild_purge_compiler_flags( ) +# +############################################################################## + +macro( ecbuild_purge_compiler_flags _lang ) + + set( options WARN ) + set( oneValueArgs "" ) + set( multiValueArgs "" ) + + cmake_parse_arguments( _PAR "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) + + if( CMAKE_${_lang}_COMPILER_LOADED ) + + # Clear default compilation flags potentially inherited from parent scope + # when using custom compilation flags + if( ECBUILD_SOURCE_FLAGS OR ECBUILD_COMPILE_FLAGS ) + set(CMAKE_${_lang}_FLAGS "") + foreach(_btype ALL RELEASE RELWITHDEBINFO PRODUCTION BIT DEBUG) + set(CMAKE_${_lang}_FLAGS_${_btype} "") + endforeach() + endif() + + endif() + + if( ${_PAR_WARN} ) + ecbuild_warn( "Purging compiler flags set for ${_lang}" ) + endif() + +endmacro() + +############################################################################## +#.rst: +# +# ecbuild_linker_flags +# ==================== +# +# Apply user or toolchain specified linker flag overrides per object type (NOT written to cache) +# +# ecbuild_linker_flags() +# +############################################################################## + +macro( ecbuild_linker_flags ) + foreach( _obj EXE SHARED MODULE ) + if( ECBUILD_${_obj}_LINKER_FLAGS ) + set( CMAKE_${_obj}_LINKER_FLAGS ${ECBUILD_${_obj}_LINKER_FLAGS} ) + endif() + + if( NOT "$ENV{LD_RUN_PATH}" EQUAL "" ) + set( LD_RUN_PATH "$ENV{LD_RUN_PATH}" ) + string( REPLACE ":" ";" LD_RUN_PATH "$ENV{LD_RUN_PATH}" ) + foreach( rpath ${LD_RUN_PATH} ) + ecbuild_regex_escape( "${rpath}" rpath_escaped ) + if( NOT CMAKE_${_obj}_LINKER_FLAGS MATCHES ".*-Wl,-rpath,${rpath_escaped}.*") + set( CMAKE_${_obj}_LINKER_FLAGS "${CMAKE_${_obj}_LINKER_FLAGS} -Wl,-rpath,${rpath}" ) + endif() + endforeach() + endif() + endforeach() + + foreach( _btype NONE DEBUG BIT PRODUCTION RELEASE RELWITHDEBINFO ) + + foreach( _obj EXE SHARED MODULE ) + if( ECBUILD_${_obj}_LINKER_FLAGS_${_btype} ) + set( CMAKE_${_obj}_LINKER_FLAGS_${_btype} ${ECBUILD_${_obj}_LINKER_FLAGS_${_btype}} ) + endif() + endforeach() + + endforeach() +endmacro() + +############################################################################## +#.rst: +# +# ecbuild_override_compiler_flags +# ====================== +# +# Purge existing CMAKE__FLAGS flags and trigger the use of per source +# file overrideable flags (see ``Using custom compilation flags`` for an +# explanation). +# +# ecbuild_override_compiler_flags() +# +############################################################################## + +macro( ecbuild_override_compiler_flags ) + + set( options "" ) + set( oneValueArgs SOURCE_FLAGS COMPILE_FLAGS ) + set( multiValueArgs "" ) + + cmake_parse_arguments( _PAR "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) + + # Ensure COMPILE/SOURCE_FLAGS is a valid file path + if( DEFINED _PAR_COMPILE_FLAGS AND NOT EXISTS ${_PAR_COMPILE_FLAGS} ) + ecbuild_warn( "COMPILE_FLAGS points to non-existent file ${_PAR_COMPILE_FLAGS} and will be ignored" ) + unset( ECBUILD_COMPILE_FLAGS ) + unset( ECBUILD_COMPILE_FLAGS CACHE ) + elseif( DEFINED _PAR_SOURCE_FLAGS AND NOT EXISTS ${_PAR_SOURCE_FLAGS} ) + ecbuild_warn( "SOURCE_FLAGS points to non-existent file ${_PAR_SOURCE_FLAGS} and will be ignored" ) + unset( ECBUILD_SOURCE_FLAGS ) + unset( ECBUILD_SOURCE_FLAGS CACHE ) + elseif( DEFINED _PAR_SOURCE_FLAGS OR DEFINED _PAR_COMPILE_FLAGS ) + foreach( _lang C CXX Fortran ) + if( CMAKE_${_lang}_COMPILER_LOADED ) + ecbuild_purge_compiler_flags( ${_lang} WARN ) + endif() + endforeach() + + if( DEFINED _PAR_COMPILE_FLAGS ) + if( DEFINED ECBUILD_COMPILE_FLAGS) + ecbuild_debug( "Override ECBUILD_COMPILE_FLAGS (${ECBUILD_COMPILE_FLAGS}) with ${_PAR_COMPILE_FLAGS}" ) + endif() + set( ECBUILD_COMPILE_FLAGS ${_PAR_COMPILE_FLAGS} ) + include( ${ECBUILD_COMPILE_FLAGS} ) + elseif( DEFINED _PAR_SOURCE_FLAGS ) + if( DEFINED ECBUILD_SOURCE_FLAGS) + ecbuild_debug( "Override ECBUILD_SOURCE_FLAGS (${ECBUILD_SOURCE_FLAGS}) with ${_PAR_SOURCE_FLAGS}" ) + endif() + set( ECBUILD_SOURCE_FLAGS ${_PAR_SOURCE_FLAGS} ) + endif() + + ecbuild_linker_flags() + endif() + +endmacro() + ############################################################################## #.rst: # @@ -172,59 +308,25 @@ foreach( _flags COMPILE SOURCE ) endif() set( ECBUILD_${_flags}_FLAGS ${${PROJECT_NAME_CAPS}_ECBUILD_${_flags}_FLAGS} ) endif() - # Ensure ECBUILD_${_flags}_FLAGS is a valid file path - if( DEFINED ECBUILD_${_flags}_FLAGS AND NOT EXISTS ${ECBUILD_${_flags}_FLAGS} ) - ecbuild_warn( "ECBUILD_${_flags}_FLAGS points to non-existent file ${ECBUILD_${_flags}_FLAGS} and will be ignored" ) - unset( ECBUILD_${_flags}_FLAGS ) - unset( ECBUILD_${_flags}_FLAGS CACHE ) - endif() endforeach() -if( ECBUILD_COMPILE_FLAGS ) - include( "${ECBUILD_COMPILE_FLAGS}" ) +if( DEFINED ECBUILD_COMPILE_FLAGS ) + ecbuild_override_compiler_flags( COMPILE_FLAGS ${ECBUILD_COMPILE_FLAGS} ) +elseif( DEFINED ECBUILD_SOURCE_FLAGS ) + ecbuild_override_compiler_flags( SOURCE_FLAGS ${ECBUILD_SOURCE_FLAGS} ) endif() foreach( _lang C CXX Fortran ) if( CMAKE_${_lang}_COMPILER_LOADED ) - # Clear default compilation flags potentially inherited from parent scope - # when using custom compilation flags - if( ECBUILD_SOURCE_FLAGS OR ECBUILD_COMPILE_FLAGS ) - set(CMAKE_${_lang}_FLAGS "") - foreach(_btype ALL RELEASE RELWITHDEBINFO PRODUCTION BIT DEBUG) - set(CMAKE_${_lang}_FLAGS_${_btype} "") - endforeach() # Load default compilation flags only if custom compilation flags not enabled - else() + if( NOT (DEFINED ECBUILD_SOURCE_FLAGS OR DEFINED ECBUILD_COMPILE_FLAGS) ) ecbuild_compiler_flags( ${_lang} ) endif() endif() endforeach() -# Apply user or toolchain specified linker flag overrides per object type (NOT written to cache) -foreach( _obj EXE SHARED MODULE ) - if( ECBUILD_${_obj}_LINKER_FLAGS ) - set( CMAKE_${_obj}_LINKER_FLAGS ${ECBUILD_${_obj}_LINKER_FLAGS} ) - endif() - - if( NOT "$ENV{LD_RUN_PATH}" EQUAL "" ) - set( LD_RUN_PATH "$ENV{LD_RUN_PATH}" ) - string( REPLACE ":" ";" LD_RUN_PATH "$ENV{LD_RUN_PATH}" ) - foreach( rpath ${LD_RUN_PATH} ) - ecbuild_regex_escape( "${rpath}" rpath_escaped ) - if( NOT CMAKE_${_obj}_LINKER_FLAGS MATCHES ".*-Wl,-rpath,${rpath_escaped}.*") - set( CMAKE_${_obj}_LINKER_FLAGS "${CMAKE_${_obj}_LINKER_FLAGS} -Wl,-rpath,${rpath}" ) - endif() - endforeach() - endif() -endforeach() - -foreach( _btype NONE DEBUG BIT PRODUCTION RELEASE RELWITHDEBINFO ) - - foreach( _obj EXE SHARED MODULE ) - if( ECBUILD_${_obj}_LINKER_FLAGS_${_btype} ) - set( CMAKE_${_obj}_LINKER_FLAGS_${_btype} ${ECBUILD_${_obj}_LINKER_FLAGS_${_btype}} ) - endif() - endforeach() +if( NOT DEFINED ECBUILD_COMPILE_FLAGS AND NOT DEFINED ECBUILD_SOURCE_FLAGS ) + ecbuild_linker_flags() +endif() -endforeach() diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index fa0c3d4b..b68b2781 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -11,3 +11,4 @@ add_subdirectory( ecbuild_shared_libs ) add_subdirectory( interface_library ) add_subdirectory( project_import ) add_subdirectory( project_summary ) +add_subdirectory( ecbuild_override_compiler_flags ) diff --git a/tests/ecbuild_override_compiler_flags/CMakeLists.txt b/tests/ecbuild_override_compiler_flags/CMakeLists.txt new file mode 100644 index 00000000..676fe2e7 --- /dev/null +++ b/tests/ecbuild_override_compiler_flags/CMakeLists.txt @@ -0,0 +1,13 @@ +set(_dir ${CMAKE_CURRENT_BINARY_DIR}) +configure_file(run-test.sh.in ${_dir}/run-test.sh @ONLY) +configure_file(test_ecbuild_override_compiler_flags.cmake ${_dir}/CMakeLists.txt COPYONLY) +configure_file(emptyfile.c ${_dir}/emptyfile.c COPYONLY) +configure_file(emptyfile.cxx ${_dir}/emptyfile.cxx COPYONLY) +configure_file(emptyfile.F90 ${_dir}/emptyfile.F90 COPYONLY) +configure_file(compiler_flags.cmake ${_dir}/compiler_flags.cmake COPYONLY) + +ecbuild_add_test( + TARGET test_ecbuild_override_compiler_flags + TYPE SCRIPT + COMMAND run-test.sh +) diff --git a/tests/ecbuild_override_compiler_flags/compiler_flags.cmake b/tests/ecbuild_override_compiler_flags/compiler_flags.cmake new file mode 100644 index 00000000..5b3834b5 --- /dev/null +++ b/tests/ecbuild_override_compiler_flags/compiler_flags.cmake @@ -0,0 +1,9 @@ +set( OVERRIDECOMPILERFLAGS_C_FLAGS "-g -fPIC" ) +set( OVERRIDECOMPILERFLAGS_CXX_FLAGS "-g -fPIC" ) +set( OVERRIDECOMPILERFLAGS_Fortran_FLAGS "-g -fortran_only_flag" ) + +set( OVERRIDECOMPILERFLAGS_C_FLAGS_DEBUG "-O0" ) +set( OVERRIDECOMPILERFLAGS_CXX_FLAGS_DEBUG "-O0" ) + +set( OVERRIDECOMPILERFLAGS_C_FLAGS_BIT "-O2" ) +set( OVERRIDECOMPILERFLAGS_CXX_FLAGS_BIT "-O2" ) diff --git a/tests/ecbuild_override_compiler_flags/emptyfile.F90 b/tests/ecbuild_override_compiler_flags/emptyfile.F90 new file mode 100644 index 00000000..199bc971 --- /dev/null +++ b/tests/ecbuild_override_compiler_flags/emptyfile.F90 @@ -0,0 +1 @@ +! An empty file diff --git a/tests/ecbuild_override_compiler_flags/emptyfile.c b/tests/ecbuild_override_compiler_flags/emptyfile.c new file mode 100644 index 00000000..4d4d8bb5 --- /dev/null +++ b/tests/ecbuild_override_compiler_flags/emptyfile.c @@ -0,0 +1 @@ +// An empty file diff --git a/tests/ecbuild_override_compiler_flags/emptyfile.cxx b/tests/ecbuild_override_compiler_flags/emptyfile.cxx new file mode 100644 index 00000000..4d4d8bb5 --- /dev/null +++ b/tests/ecbuild_override_compiler_flags/emptyfile.cxx @@ -0,0 +1 @@ +// An empty file diff --git a/tests/ecbuild_override_compiler_flags/run-test.sh.in b/tests/ecbuild_override_compiler_flags/run-test.sh.in new file mode 100755 index 00000000..ee9c4197 --- /dev/null +++ b/tests/ecbuild_override_compiler_flags/run-test.sh.in @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +set -e + +HERE="$( cd $( dirname "${BASH_SOURCE[0]}" ) && pwd -P )" + +cd $HERE + +rm -rf build # cleanup +mkdir build +cd build + +cmake -DCMAKE_MODULE_PATH=@ECBUILD_MACROS_DIR@ -DECBUILD_LOG_LEVEL=DEBUG -DCMAKE_BUILD_TYPE=BIT -DECBUILD_SOURCE_FLAGS=@CMAKE_CURRENT_SOURCE_DIR@/flags-sourceflags.json .. + +cd .. + +rm -rf build # cleanup +mkdir build +cd build + +cmake -DCMAKE_MODULE_PATH=@ECBUILD_MACROS_DIR@ -DECBUILD_LOG_LEVEL=DEBUG -DCMAKE_BUILD_TYPE=DEBUG -DECBUILD_SOURCE_FLAGS=@CMAKE_CURRENT_SOURCE_DIR@/flags-sourceflags.json .. + diff --git a/tests/ecbuild_override_compiler_flags/test_ecbuild_override_compiler_flags.cmake b/tests/ecbuild_override_compiler_flags/test_ecbuild_override_compiler_flags.cmake new file mode 100644 index 00000000..d7d6fb20 --- /dev/null +++ b/tests/ecbuild_override_compiler_flags/test_ecbuild_override_compiler_flags.cmake @@ -0,0 +1,45 @@ +cmake_minimum_required( VERSION 3.12 FATAL_ERROR ) + +find_package( ecbuild 3.6 REQUIRED ) + +project(OverrideCompilerFlags VERSION 1.0 LANGUAGES C CXX Fortran) + +ecbuild_override_compiler_flags( COMPILE_FLAGS compiler_flags.cmake ) + +ecbuild_add_library( + TARGET overrideflags + SOURCES emptyfile.c emptyfile.cxx emptyfile.F90 +) + +get_property( _flags SOURCE emptyfile.c PROPERTY COMPILE_FLAGS ) +if( CMAKE_BUILD_TYPE MATCHES BIT ) + if( NOT ${_flags} MATCHES "-g -fPIC -O2" ) + message(${_flags}) + message(FATAL_ERROR "Incorrect BIT flags for emptyfile.c") + endif() +elseif( CMAKE_BUILD_TYPE MATCHES DEBUG ) + if( NOT ${_flags} MATCHES "-g -fPIC -O0" ) + message(${_flags}) + message(FATAL_ERROR "Incorrect DEBUG flags for emptyfile.c") + endif() +endif() + +get_property( _flags SOURCE emptyfile.cxx PROPERTY COMPILE_FLAGS ) +if( CMAKE_BUILD_TYPE MATCHES BIT ) + if( NOT ${_flags} MATCHES "-g -fPIC -O2" ) + message(${_flags}) + message(FATAL_ERROR "Incorrect BIT flags for emptyfile.cxx") + endif() +elseif( CMAKE_BUILD_TYPE MATCHES DEBUG ) + if( NOT ${_flags} MATCHES "-g -fPIC -O0" ) + message(${_flags}) + message(FATAL_ERROR "Incorrect DEBUG flags for emptyfile.cxx") + endif() +endif() + +get_property( _flags SOURCE emptyfile.F90 PROPERTY COMPILE_FLAGS ) +if( NOT ${_flags} MATCHES "-g -fortran_only_flag" ) + message(${_flags}) + message(FATAL_ERROR "Incorrect flags for emptyfile.F90") +endif() +