diff --git a/CMake/OpenAccHelper.cmake b/CMake/OpenAccHelper.cmake index 5838742f8..bb09f8966 100644 --- a/CMake/OpenAccHelper.cmake +++ b/CMake/OpenAccHelper.cmake @@ -4,10 +4,35 @@ # See top-level LICENSE file for details. # ============================================================================= +# Helper to parse X.Y[.{anything] into X.Y +function(cnrn_parse_version FULL_VERSION) + cmake_parse_arguments(PARSE_ARGV 1 CNRN_PARSE_VERSION "" "OUTPUT_MAJOR_MINOR" "") + if(NOT "${CNRN_PARSE_VERSION_UNPARSED_ARGUMENTS}" STREQUAL "") + message( + FATAL_ERROR + "cnrn_parse_version got unexpected arguments: ${CNRN_PARSE_VERSION_UNPARSED_ARGUMENTS}") + endif() + string(FIND ${FULL_VERSION} . first_dot) + math(EXPR first_dot_plus_one "${first_dot}+1") + string(SUBSTRING ${FULL_VERSION} ${first_dot_plus_one} -1 minor_and_later) + string(FIND ${minor_and_later} . second_dot_relative) + if(${first_dot} EQUAL -1 OR ${second_dot_relative} EQUAL -1) + message(FATAL_ERROR "Failed to parse major.minor from ${FULL_VERSION}") + endif() + math(EXPR second_dot_plus_one "${first_dot}+${second_dot_relative}+1") + string(SUBSTRING ${FULL_VERSION} 0 ${second_dot_plus_one} major_minor) + set(${CNRN_PARSE_VERSION_OUTPUT_MAJOR_MINOR} + ${major_minor} + PARENT_SCOPE) +endfunction() + # ============================================================================= # Prepare compiler flags for GPU target # ============================================================================= if(CORENRN_ENABLE_GPU) + # Get the NVC++ version number for use in nrnivmodl_core_makefile.in + cnrn_parse_version(${CMAKE_CXX_COMPILER_VERSION} OUTPUT_MAJOR_MINOR + CORENRN_NVHPC_MAJOR_MINOR_VERSION) # Enable cudaProfiler{Start,Stop}() behind the Instrumentor::phase... APIs add_compile_definitions(CORENEURON_CUDA_PROFILING CORENEURON_ENABLE_GPU) # Plain C++ code in CoreNEURON may need to use CUDA runtime APIs for, for example, starting and @@ -23,20 +48,7 @@ if(CORENRN_ENABLE_GPU) if(NOT ${CMAKE_CUDA_COMPILER_ID} STREQUAL "NVIDIA") message(FATAL_ERROR "Unsupported CUDA compiler ${CMAKE_CUDA_COMPILER_ID}") endif() - # Parse CMAKE_CUDA_COMPILER_VERSION=x.y.z into CUDA_VERSION=x.y - string(FIND ${CMAKE_CUDA_COMPILER_VERSION} . first_dot) - math(EXPR first_dot_plus_one "${first_dot}+1") - string(SUBSTRING ${CMAKE_CUDA_COMPILER_VERSION} ${first_dot_plus_one} -1 minor_and_later) - string(FIND ${minor_and_later} . second_dot_relative) - if(${first_dot} EQUAL -1 OR ${second_dot_relative} EQUAL -1) - message( - FATAL_ERROR - "Failed to parse a CUDA_VERSION from CMAKE_CUDA_COMPILER_VERSION=${CMAKE_CUDA_COMPILER_VERSION}" - ) - endif() - math(EXPR second_dot_plus_one "${first_dot}+${second_dot_relative}+1") - string(SUBSTRING ${CMAKE_CUDA_COMPILER_VERSION} 0 ${second_dot_plus_one} - CORENRN_CUDA_VERSION_SHORT) + cnrn_parse_version(${CMAKE_CUDA_COMPILER_VERSION} OUTPUT_MAJOR_MINOR CORENRN_CUDA_VERSION_SHORT) else() # This is a lazy way of getting the major/minor versions separately without parsing # ${CMAKE_CUDA_COMPILER_VERSION} @@ -91,7 +103,7 @@ if(CORENRN_ENABLE_GPU) GLOBAL PROPERTY CORENEURON_LIB_LINK_FLAGS - "${NVHPC_ACC_COMP_FLAGS} -rdynamic -lrt -Wl,--whole-archive -L${CMAKE_HOST_SYSTEM_PROCESSOR} -lcorenrnmech -L${CMAKE_INSTALL_PREFIX}/lib -lcoreneuron -Wl,--no-whole-archive" + "${NVHPC_ACC_COMP_FLAGS} -rdynamic -lrt -Wl,--whole-archive -L${CMAKE_HOST_SYSTEM_PROCESSOR} -lcorenrnmech -L$(libdir) -lcoreneuron -Wl,--no-whole-archive" ) else() set_property(GLOBAL PROPERTY CORENEURON_LIB_LINK_FLAGS diff --git a/CMakeLists.txt b/CMakeLists.txt index 4cd0773d3..22f9b2b94 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -426,7 +426,12 @@ endif() # ============================================================================= # Common CXX flags : ignore unknown pragma warnings # ============================================================================= -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${IGNORE_UNKNOWN_PRAGMA_FLAGS}") +# Do not set this when building wheels. The nrnivmodl workflow means that we do not know what +# compiler will be invoked with these flags, so we have to use flags that are as generic as +# possible. +if(NOT DEFINED NRN_WHEEL_BUILD OR NOT NRN_WHEEL_BUILD) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${IGNORE_UNKNOWN_PRAGMA_FLAGS}") +endif() # ============================================================================= # Add main directories @@ -436,7 +441,21 @@ add_subdirectory(coreneuron) if(CORENRN_ENABLE_GPU) get_target_property(CORENRN_LINK_LIBRARIES coreneuron INTERFACE_LINK_LIBRARIES) foreach(LIB ${CORENRN_LINK_LIBRARIES}) - set_property(GLOBAL APPEND_STRING PROPERTY CORENEURON_LIB_LINK_FLAGS " ${LIB}") + get_filename_component(dir_path ${LIB} DIRECTORY) + if(TARGET ${LIB}) + # See, for example, caliper where the coreneuron target depends on the caliper target (so we + # get LIB=caliper in this loop), but -l and -L are already added manually here: + # https://github.com/BlueBrain/CoreNeuron/blob/856cea4aa647c8f2b0d5bda6d0fc32144c5942e3/CMakeLists.txt#L411-L412 + message( + NOTICE + "Ignoring dependency '${LIB}' of 'coreneuron' and assuming relevant flags have already been added to CORENEURON_LIB_LINK_FLAGS." + ) + elseif(NOT dir_path) + # In case LIB is not a target but is just the name of a library, e.g. "dl" + set_property(GLOBAL APPEND_STRING PROPERTY CORENEURON_LIB_LINK_FLAGS " -l${LIB}") + else() + set_property(GLOBAL APPEND_STRING PROPERTY CORENEURON_LIB_LINK_FLAGS " ${LIB}") + endif() endforeach() endif() @@ -528,7 +547,6 @@ message(STATUS "COMPILE FLAGS | ${CORENRN_CXX_FLAGS}") message(STATUS "Build Type | ${COMPILE_LIBRARY_TYPE}") message(STATUS "MPI | ${CORENRN_ENABLE_MPI}") if(CORENRN_ENABLE_MPI) - message(STATUS " INC | ${MPI_CXX_INCLUDE_PATH}") message(STATUS " DYNAMIC | ${CORENRN_ENABLE_MPI_DYNAMIC}") if(CORENRN_ENABLE_MPI_DYNAMIC AND NRN_MPI_LIBNAME_LIST) # ~~~ @@ -543,6 +561,8 @@ if(CORENRN_ENABLE_MPI) message(STATUS " LIBNAME | core${libname}") message(STATUS " INC | ${include}") endforeach(val) + else() + message(STATUS " INC | ${MPI_CXX_INCLUDE_PATH}") endif() endif() message(STATUS "OpenMP | ${CORENRN_ENABLE_OPENMP}") diff --git a/extra/nrnivmodl_core_makefile.in b/extra/nrnivmodl_core_makefile.in index 47dc02f0d..f50e99de2 100644 --- a/extra/nrnivmodl_core_makefile.in +++ b/extra/nrnivmodl_core_makefile.in @@ -52,7 +52,9 @@ CORENRNLIB_FLAGS += $(if @caliper_LIB_DIR@,-L@caliper_LIB_DIR@,) # coreneuron/utils/randoms goes first because it needs to override the NEURON # directory in INCFLAGS INCLUDES = -I$(CORENRN_INC_DIR)/coreneuron/utils/randoms $(INCFLAGS) -I$(CORENRN_INC_DIR) -INCLUDES += $(if @MPI_CXX_INCLUDE_PATH@, -I$(subst ;, -I,@MPI_CXX_INCLUDE_PATH@),) +ifeq (@CORENRN_ENABLE_MPI_DYNAMIC@, OFF) + INCLUDES += $(if @MPI_CXX_INCLUDE_PATH@, -I$(subst ;, -I,@MPI_CXX_INCLUDE_PATH@),) +endif INCLUDES += $(if @reportinglib_INCLUDE_DIR@, -I$(subst ;, -I,@reportinglib_INCLUDE_DIR@),) # CXX is always defined. If the definition comes from default change it @@ -60,6 +62,16 @@ ifeq ($(origin CXX), default) CXX = @CMAKE_CXX_COMPILER@ endif +ifeq (@CORENRN_ENABLE_GPU@, ON) + ifneq ($(shell $(CXX) --version | grep -o nvc++), nvc++) + $(error GPU wheels are only compatible with the NVIDIA C++ compiler nvc++, but CXX=$(CXX) and --version gives $(shell $(CXX) --version)) + endif + # nvc++ -dumpversion is simpler, but only available from 22.2 + ifeq ($(findstring nvc++ @CORENRN_NVHPC_MAJOR_MINOR_VERSION@, $(shell $(CXX) --version)),) + $(error GPU wheels are currently not compatible across NVIDIA HPC SDK versions. You have $(shell $(CXX) -V | grep nvc++) but this wheel was built with @CORENRN_NVHPC_MAJOR_MINOR_VERSION@.) + endif +endif + # In case of wheel, python and perl exe paths are from the build machine. # First prefer env variables set by neuron's nrnivmodl wrapper then check # binary used during build. If they don't exist then simply use python and