1# Copyright (c) 2017-2021, Lawrence Livermore National Security, LLC and
2# other BLT Project Developers. See the top-level LICENSE file for details
3#
4# SPDX-License-Identifier: (BSD-3-Clause)
5
6include(CMakeParseArguments)
7
8## Internal BLT CMake Macros
9
10
11##-----------------------------------------------------------------------------
12## blt_determine_scope(TARGET <target>
13##                     SCOPE  <PUBLIC (Default)| INTERFACE | PRIVATE>
14##                     OUT    <out variable name>)
15##
16## Returns the normalized scope string for a given SCOPE and TARGET to be used
17## in BLT macros.
18##
19## TARGET - Name of CMake Target that the property is being added to
20##          Note: the only real purpose of this parameter is to make sure we aren't
21##                adding returning other than INTERFACE for Interface Libraries
22## SCOPE  - case-insensitive scope string, defaults to PUBLIC
23## OUT    - variable that is filled with the uppercased scope
24##
25##-----------------------------------------------------------------------------
26macro(blt_determine_scope)
27
28    set(options)
29    set(singleValueArgs TARGET SCOPE OUT)
30    set(multiValueArgs )
31
32    # Parse the arguments
33    cmake_parse_arguments(arg "${options}" "${singleValueArgs}"
34                        "${multiValueArgs}" ${ARGN} )
35
36    # Convert to upper case and strip white space
37    string(TOUPPER "${arg_SCOPE}" _uppercaseScope)
38    string(STRIP "${_uppercaseScope}" _uppercaseScope )
39
40    if("${_uppercaseScope}" STREQUAL "")
41        # Default to public
42        set(_uppercaseScope PUBLIC)
43    elseif(NOT ("${_uppercaseScope}" STREQUAL "PUBLIC" OR
44                "${_uppercaseScope}" STREQUAL "INTERFACE" OR
45                "${_uppercaseScope}" STREQUAL "PRIVATE"))
46        message(FATAL_ERROR "Given SCOPE (${arg_SCOPE}) is not valid, valid options are:"
47                            "PUBLIC, INTERFACE, or PRIVATE")
48    endif()
49
50    if(TARGET ${arg_TARGET})
51        get_property(_targetType TARGET ${arg_TARGET} PROPERTY TYPE)
52        if(${_targetType} STREQUAL "INTERFACE_LIBRARY")
53            # Interface targets can only set INTERFACE
54            if("${_uppercaseScope}" STREQUAL "PUBLIC" OR
55               "${_uppercaseScope}" STREQUAL "INTERFACE")
56                set(${arg_OUT} INTERFACE)
57            else()
58                message(FATAL_ERROR "Cannot set PRIVATE scope to Interface Library."
59                                    "Change to Scope to INTERFACE.")
60            endif()
61        else()
62            set(${arg_OUT} ${_uppercaseScope})
63        endif()
64    else()
65        set(${arg_OUT} ${_uppercaseScope})
66    endif()
67
68    unset(_targetType)
69    unset(_uppercaseScope)
70
71endmacro(blt_determine_scope)
72
73
74##-----------------------------------------------------------------------------
75## blt_error_if_target_exists()
76##
77## Checks if target already exists in CMake project and errors out with given
78## error_msg.
79##-----------------------------------------------------------------------------
80function(blt_error_if_target_exists target_name error_msg)
81    if (TARGET ${target_name})
82        message(FATAL_ERROR "${error_msg}Duplicate target name: ${target_name}")
83    endif()
84endfunction()
85
86##-----------------------------------------------------------------------------
87## blt_fix_fortran_openmp_flags(<target name>)
88##
89## Fixes the openmp flags for a Fortran target if they are different from the
90## corresponding C/C++ OpenMP flags.
91##-----------------------------------------------------------------------------
92function(blt_fix_fortran_openmp_flags target_name)
93
94    if (ENABLE_FORTRAN AND ENABLE_OPENMP AND BLT_OPENMP_FLAGS_DIFFER)
95
96        # The OpenMP interface library will have been added as a direct
97        # link dependency instead of via flags
98        get_target_property(target_link_libs ${target_name} LINK_LIBRARIES)
99        if ( target_link_libs )
100            # Since this is only called on executable targets we can safely convert
101            # from a "real" target back to a "fake" one as this is a sink vertex in
102            # the DAG.  Only the link flags need to be modified.
103            list(FIND target_link_libs "openmp" _omp_index)
104            if(${_omp_index} GREATER -1)
105                message(STATUS "Fixing OpenMP Flags for target[${target_name}]")
106
107                # Remove openmp from libraries
108                list(REMOVE_ITEM target_link_libs "openmp")
109                set_target_properties( ${target_name} PROPERTIES
110                                       LINK_LIBRARIES "${target_link_libs}" )
111
112                # Add openmp compile flags verbatim w/ generator expression
113                get_target_property(omp_compile_flags openmp INTERFACE_COMPILE_OPTIONS)
114                target_compile_options(${target_name} PUBLIC ${omp_compile_flags})
115
116                # Change CXX flags to Fortran flags
117
118                # These are set through blt_add_target_link_flags which needs to use
119                # the link_libraries for interface libraries in CMake < 3.13
120                if( ${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.13.0" )
121                    get_target_property(omp_link_flags openmp INTERFACE_LINK_OPTIONS)
122                else()
123                    get_target_property(omp_link_flags openmp INTERFACE_LINK_LIBRARIES)
124                endif()
125
126                string( REPLACE "${OpenMP_CXX_FLAGS}" "${OpenMP_Fortran_FLAGS}"
127                        correct_omp_link_flags
128                        "${omp_link_flags}"
129                        )
130                if( ${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.13.0" )
131                    target_link_options(${target_name} PRIVATE "${correct_omp_link_flags}")
132                else()
133                    set_property(TARGET ${target_name} APPEND PROPERTY LINK_FLAGS "${correct_omp_link_flags}")
134                endif()
135            endif()
136
137            # Handle registered library general case
138
139            # OpenMP is an interface library which doesn't have a LINK_FLAGS property
140            # in versions < 3.13
141            set(_property_name LINK_FLAGS)
142            if( ${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.13.0" )
143                # In CMake 3.13+, LINK_FLAGS was converted to LINK_OPTIONS.
144                set(_property_name LINK_OPTIONS)
145            endif()
146            get_target_property(target_link_flags ${target_name} ${_property_name})
147            if ( target_link_flags )
148
149                string( REPLACE "${OpenMP_CXX_FLAGS}" "${OpenMP_Fortran_FLAGS}"
150                        correct_link_flags
151                        "${target_link_flags}"
152                        )
153                set_target_properties( ${target_name} PROPERTIES ${_property_name}
154                                    "${correct_link_flags}" )
155            endif()
156        endif()
157
158    endif()
159
160endfunction()
161
162##-----------------------------------------------------------------------------
163## blt_find_executable(NAME         <name of program to find>
164##                     EXECUTABLES  [exe1 [exe2 ...]])
165##
166## This macro attempts to find the given executable via either a previously defined
167## <UPPERCASE_NAME>_EXECUTABLE or using find_program with the given EXECUTABLES.
168## if EXECUTABLES is left empty, then NAME is used.  This macro will only attempt
169## to locate the executable if <UPPERCASE_NAME>_ENABLED is TRUE.
170##
171## If successful the following variables will be defined:
172## <UPPERCASE_NAME>_FOUND
173## <UPPERCASE_NAME>_EXECUTABLE
174##-----------------------------------------------------------------------------
175macro(blt_find_executable)
176
177    set(options)
178    set(singleValueArgs NAME)
179    set(multiValueArgs  EXECUTABLES)
180
181    # Parse the arguments
182    cmake_parse_arguments(arg "${options}" "${singleValueArgs}"
183                        "${multiValueArgs}" ${ARGN} )
184
185    # Check arguments
186    if ( NOT DEFINED arg_NAME )
187        message( FATAL_ERROR "Must provide a NAME argument to the 'blt_find_executable' macro" )
188    endif()
189
190    string(TOUPPER ${arg_NAME} _ucname)
191
192    message(STATUS "${arg_NAME} support is ${ENABLE_${_ucname}}")
193    if (${ENABLE_${_ucname}})
194        set(_exes ${arg_NAME})
195        if (DEFINED arg_EXECUTABLES)
196            set(_exes ${arg_EXECUTABLES})
197        endif()
198
199        if (${_ucname}_EXECUTABLE)
200            if (NOT EXISTS ${${_ucname}_EXECUTABLE})
201                message(FATAL_ERROR "User defined ${_ucname}_EXECUTABLE does not exist. Fix/unset variable or set ENABLE_${_ucname} to OFF.")
202            endif()
203        else()
204            find_program(${_ucname}_EXECUTABLE
205                         NAMES ${_exes}
206                         DOC "Path to ${arg_NAME} executable")
207        endif()
208
209        # Handle REQUIRED and QUIET arguments
210        # this will also set ${_ucname}_FOUND to true if ${_ucname}_EXECUTABLE exists
211        include(FindPackageHandleStandardArgs)
212        find_package_handle_standard_args(${arg_NAME}
213                                          "Failed to locate ${arg_NAME} executable"
214                                          ${_ucname}_EXECUTABLE)
215    endif()
216endmacro(blt_find_executable)
217
218
219##------------------------------------------------------------------------------
220## blt_inherit_target_info( TO       <target>
221##                          FROM     <target>
222##                          OBJECT   [TRUE|FALSE])
223##
224##  The purpose of this macro is if you want to grab all the inheritable info
225##  from the FROM target but don't want to make the TO target depend on it.
226##  Which is useful if you don't want to export the FROM target.
227##
228##  The OBJECT parameter is because object libraries can only inherit certain
229##  properties.
230##
231##  This inherits the following properties:
232##    INTERFACE_COMPILE_DEFINITIONS
233##    INTERFACE_INCLUDE_DIRECTORIES
234##    INTERFACE_LINK_DIRECTORIES
235##    INTERFACE_LINK_LIBRARIES
236##    INTERFACE_SYSTEM_INCLUDE_DIRECTORIES
237##------------------------------------------------------------------------------
238macro(blt_inherit_target_info)
239    set(options)
240    set(singleValueArgs TO FROM OBJECT)
241    set(multiValueArgs)
242
243    # Parse the arguments
244    cmake_parse_arguments(arg "${options}" "${singleValueArgs}"
245                        "${multiValueArgs}" ${ARGN} )
246
247    # Check arguments
248    if ( NOT DEFINED arg_TO )
249        message( FATAL_ERROR "Must provide a TO argument to the 'blt_inherit_target' macro" )
250    endif()
251
252    if ( NOT DEFINED arg_FROM )
253        message( FATAL_ERROR "Must provide a FROM argument to the 'blt_inherit_target' macro" )
254    endif()
255
256    blt_determine_scope(TARGET ${arg_TO} OUT _scope)
257
258    get_target_property(_interface_system_includes
259                        ${arg_FROM} INTERFACE_SYSTEM_INCLUDE_DIRECTORIES)
260    if ( _interface_system_includes )
261        target_include_directories(${arg_TO} SYSTEM ${_scope} ${_interface_system_includes})
262    endif()
263
264    get_target_property(_interface_includes
265                        ${arg_FROM} INTERFACE_INCLUDE_DIRECTORIES)
266    if ( _interface_includes )
267        target_include_directories(${arg_TO} ${_scope} ${_interface_includes})
268    endif()
269
270    get_target_property(_interface_defines
271                        ${arg_FROM} INTERFACE_COMPILE_DEFINITIONS)
272    if ( _interface_defines )
273        target_compile_definitions( ${arg_TO} ${_scope} ${_interface_defines})
274    endif()
275
276    if( ${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.13.0" )
277        get_target_property(_interface_link_options
278                            ${arg_FROM} INTERFACE_LINK_OPTIONS)
279        if ( _interface_link_options )
280            target_link_options( ${arg_TO} ${_scope} ${_interface_link_options})
281        endif()
282    endif()
283
284    get_target_property(_interface_compile_options
285                        ${arg_FROM} INTERFACE_COMPILE_OPTIONS)
286    if ( _interface_compile_options )
287        target_compile_options( ${arg_TO} ${_scope} ${_interface_compile_options})
288    endif()
289
290    if ( NOT arg_OBJECT )
291        get_target_property(_interface_link_directories
292                            ${arg_FROM} INTERFACE_LINK_DIRECTORIES)
293        if ( _interface_link_directories )
294            target_link_directories( ${arg_TO} ${_scope} ${_interface_link_directories})
295        endif()
296
297        get_target_property(_interface_link_libraries
298                            ${arg_FROM} INTERFACE_LINK_LIBRARIES)
299        if ( _interface_link_libraries )
300            target_link_libraries( ${arg_TO} ${_scope} ${_interface_link_libraries})
301        endif()
302    endif()
303
304endmacro(blt_inherit_target_info)
305
306##------------------------------------------------------------------------------
307## blt_expand_depends( DEPENDS_ON [dep1 ...]
308##                     RESULT [variable] )
309##------------------------------------------------------------------------------
310macro(blt_expand_depends)
311    set(options)
312    set(singleValueArgs RESULT)
313    set(multiValueArgs DEPENDS_ON)
314
315    # Parse the arguments
316    cmake_parse_arguments(arg "${options}" "${singleValueArgs}"
317                        "${multiValueArgs}" ${ARGN} )
318
319    # Expand dependency list
320    set(_deps_to_process ${arg_DEPENDS_ON})
321    set(_expanded_DEPENDS_ON)
322    while(_deps_to_process)
323        # Copy the current set of dependencies to process
324        set(_current_deps_to_process ${_deps_to_process})
325        # and add them to the full expanded list
326        list(APPEND _expanded_DEPENDS_ON ${_deps_to_process})
327        # Then clear it so we can check if new ones were added
328        set(_deps_to_process)
329        foreach( dependency ${_current_deps_to_process} )
330            string(TOUPPER ${dependency} uppercase_dependency )
331            if ( DEFINED _BLT_${uppercase_dependency}_DEPENDS_ON )
332                foreach(new_dependency ${_BLT_${uppercase_dependency}_DEPENDS_ON})
333                    # Don't add duplicates
334                    if (NOT ${new_dependency} IN_LIST _expanded_DEPENDS_ON)
335                        list(APPEND _deps_to_process ${new_dependency})
336                    endif()
337                endforeach()
338            endif()
339        endforeach()
340    endwhile()
341
342    # Write the output to the requested variable
343    set(${arg_RESULT} ${_expanded_DEPENDS_ON})
344endmacro()
345
346
347##------------------------------------------------------------------------------
348## blt_setup_target( NAME       [name]
349##                   DEPENDS_ON [dep1 ...]
350##                   OBJECT     [TRUE | FALSE])
351##------------------------------------------------------------------------------
352macro(blt_setup_target)
353
354    set(options)
355    set(singleValueArgs NAME OBJECT)
356    set(multiValueArgs DEPENDS_ON)
357
358    # Parse the arguments
359    cmake_parse_arguments(arg "${options}" "${singleValueArgs}"
360                        "${multiValueArgs}" ${ARGN} )
361
362    # Check arguments
363    if ( NOT DEFINED arg_NAME )
364        message( FATAL_ERROR "Must provide a NAME argument to the 'blt_setup_target' macro" )
365    endif()
366
367    # Default to "real" scope, unless it's an interface library
368    set(_private_scope PRIVATE)
369    set(_public_scope  PUBLIC)
370    get_target_property(_target_type ${arg_NAME} TYPE)
371    if("${_target_type}" STREQUAL "INTERFACE_LIBRARY")
372        set(_private_scope INTERFACE)
373        set(_public_scope  INTERFACE)
374    endif()
375
376    # Expand dependency list - avoid "recalculating" if the information already exists
377    set(_expanded_DEPENDS_ON)
378    if(NOT "${_target_type}" STREQUAL "INTERFACE_LIBRARY")
379        get_target_property(_expanded_DEPENDS_ON ${arg_NAME} BLT_EXPANDED_DEPENDENCIES)
380    endif()
381    if(NOT _expanded_DEPENDS_ON)
382        blt_expand_depends(DEPENDS_ON ${arg_DEPENDS_ON} RESULT _expanded_DEPENDS_ON)
383    endif()
384
385    # Add dependency's information
386    foreach( dependency ${_expanded_DEPENDS_ON} )
387        string(TOUPPER ${dependency} uppercase_dependency )
388
389        if ( NOT arg_OBJECT AND _BLT_${uppercase_dependency}_IS_OBJECT_LIBRARY )
390            target_sources(${arg_NAME} ${_private_scope} $<TARGET_OBJECTS:${dependency}>)
391        endif()
392
393        if ( DEFINED _BLT_${uppercase_dependency}_INCLUDES )
394            if ( _BLT_${uppercase_dependency}_TREAT_INCLUDES_AS_SYSTEM )
395                target_include_directories( ${arg_NAME} SYSTEM ${_public_scope}
396                    ${_BLT_${uppercase_dependency}_INCLUDES} )
397            else()
398                target_include_directories( ${arg_NAME} ${_public_scope}
399                    ${_BLT_${uppercase_dependency}_INCLUDES} )
400            endif()
401        endif()
402
403        if ( DEFINED _BLT_${uppercase_dependency}_FORTRAN_MODULES )
404            target_include_directories( ${arg_NAME} ${_public_scope}
405                ${_BLT_${uppercase_dependency}_FORTRAN_MODULES} )
406        endif()
407
408        if ( arg_OBJECT )
409            # Object libraries need to inherit info from their CMake targets listed
410            # in their LIBRARIES
411            foreach( _library ${_BLT_${uppercase_dependency}_LIBRARIES} )
412                if(TARGET ${_library})
413                    blt_inherit_target_info(TO     ${arg_NAME}
414                                            FROM   ${_library}
415                                            OBJECT ${arg_OBJECT})
416                endif()
417            endforeach()
418        endif()
419
420        if ( arg_OBJECT OR _BLT_${uppercase_dependency}_IS_OBJECT_LIBRARY )
421            # We want object libraries to inherit the vital info but not call
422            # target_link_libraries() otherwise you have to install the object
423            # files associated with the object library which noone wants.
424            if ( TARGET ${dependency} )
425                blt_inherit_target_info(TO     ${arg_NAME}
426                                        FROM   ${dependency}
427                                        OBJECT ${arg_OBJECT})
428            endif()
429        elseif (DEFINED _BLT_${uppercase_dependency}_LIBRARIES)
430            # This prevents cmake from adding -l<library name> to the
431            # command line for BLT registered libraries which are not
432            # actual CMake targets
433            if(NOT "${_BLT_${uppercase_dependency}_LIBRARIES}"
434                    STREQUAL "BLT_NO_LIBRARIES" )
435                target_link_libraries( ${arg_NAME} ${_public_scope}
436                    ${_BLT_${uppercase_dependency}_LIBRARIES} )
437            endif()
438        else()
439            target_link_libraries( ${arg_NAME} ${_public_scope} ${dependency} )
440        endif()
441
442        if ( DEFINED _BLT_${uppercase_dependency}_DEFINES )
443            target_compile_definitions( ${arg_NAME} ${_public_scope}
444                ${_BLT_${uppercase_dependency}_DEFINES} )
445        endif()
446
447        if ( DEFINED _BLT_${uppercase_dependency}_COMPILE_FLAGS )
448            blt_add_target_compile_flags(TO ${arg_NAME}
449                                         FLAGS ${_BLT_${uppercase_dependency}_COMPILE_FLAGS} )
450        endif()
451
452        if ( NOT arg_OBJECT AND DEFINED _BLT_${uppercase_dependency}_LINK_FLAGS )
453            blt_add_target_link_flags(TO ${arg_NAME}
454                                      FLAGS ${_BLT_${uppercase_dependency}_LINK_FLAGS} )
455        endif()
456
457        if(TARGET ${dependency})
458            # If it's an interface library CMake doesn't even allow us to query the property
459            get_target_property(_dep_type ${dependency} TYPE)
460            if(NOT "${_dep_type}" STREQUAL "INTERFACE_LIBRARY")
461                # Propagate the overridden linker language, if applicable
462                get_target_property(_blt_link_lang ${dependency} INTERFACE_BLT_LINKER_LANGUAGE_OVERRIDE)
463                # TODO: Do we need to worry about overwriting?  Should only ever be HIP or CUDA
464                if(_blt_link_lang)
465                    set_target_properties(${arg_NAME} PROPERTIES INTERFACE_BLT_LINKER_LANGUAGE_OVERRIDE ${_blt_link_lang})
466                endif()
467            endif()
468
469            # Check if a separate device link is needed
470            if(ENABLE_CUDA AND "${_dep_type}" STREQUAL "OBJECT_LIBRARY")
471                get_target_property(_device_link ${dependency} CUDA_RESOLVE_DEVICE_SYMBOLS)
472                if(_device_link AND CUDA_LINK_WITH_NVCC)
473                    set(_dlink_obj "${dependency}_device_link${CMAKE_CUDA_OUTPUT_EXTENSION}")
474                    # Make sure a target wasn't already added
475                    get_source_file_property(_generated ${_dlink_obj} GENERATED)
476                    if(NOT _generated)
477                        # Convert string to list as it will be expanded
478                        string(REPLACE " " ";" _cuda_flags ${CMAKE_CUDA_FLAGS})
479                        add_custom_command(
480                            OUTPUT ${_dlink_obj}
481                            COMMAND ${CMAKE_CUDA_COMPILER} --device-link ${_cuda_flags} $<TARGET_OBJECTS:${dependency}> -o ${_dlink_obj}
482                            DEPENDS $<TARGET_OBJECTS:${dependency}>
483                            COMMAND_EXPAND_LISTS
484                        )
485                    endif()
486                    target_sources(${arg_NAME} PRIVATE ${_dlink_obj})
487                endif()
488            endif()
489        endif()
490    endforeach()
491
492endmacro(blt_setup_target)
493
494
495##------------------------------------------------------------------------------
496## blt_setup_cuda_target(NAME <name of target>
497##                       SOURCES <list of sources>
498##                       DEPENDS_ON <list of dependencies>
499##                       LIBRARY_TYPE <STATIC, SHARED, OBJECT, or blank for executables>)
500##------------------------------------------------------------------------------
501macro(blt_setup_cuda_target)
502
503    set(options)
504    set(singleValueArgs NAME LIBRARY_TYPE)
505    set(multiValueArgs SOURCES DEPENDS_ON)
506
507    # Parse the arguments
508    cmake_parse_arguments(arg "${options}" "${singleValueArgs}"
509                            "${multiValueArgs}" ${ARGN} )
510
511    # Check arguments
512    if ( NOT DEFINED arg_NAME )
513        message( FATAL_ERROR "Must provide a NAME argument to the 'blt_setup_cuda_target' macro")
514    endif()
515
516    if ( NOT DEFINED arg_SOURCES )
517        message( FATAL_ERROR "Must provide SOURCES to the 'blt_setup_cuda_target' macro")
518    endif()
519
520    # Determine if cuda or cuda_runtime are in DEPENDS_ON
521    list(FIND arg_DEPENDS_ON "cuda" _cuda_index)
522    set(_depends_on_cuda FALSE)
523    if(${_cuda_index} GREATER -1)
524        set(_depends_on_cuda TRUE)
525    endif()
526    list(FIND arg_DEPENDS_ON "cuda_runtime" _cuda_runtime_index)
527    set(_depends_on_cuda_runtime FALSE)
528    if(${_cuda_runtime_index} GREATER -1)
529        set(_depends_on_cuda_runtime TRUE)
530    endif()
531
532    if (${_depends_on_cuda_runtime} OR ${_depends_on_cuda})
533        if (CUDA_LINK_WITH_NVCC)
534            set_target_properties( ${arg_NAME} PROPERTIES LINKER_LANGUAGE CUDA)
535            # This will be propagated up to executable targets that depend on this
536            # library, which will need the HIP linker
537            set_target_properties( ${arg_NAME} PROPERTIES INTERFACE_BLT_LINKER_LANGUAGE_OVERRIDE CUDA)
538        endif()
539    endif()
540
541    if (${_depends_on_cuda})
542        # if cuda is in depends_on, flag each file's language as CUDA
543        # instead of leaving it up to CMake to decide
544        # Note: we don't do this when depending on just 'cuda_runtime'
545        set(_cuda_sources)
546        set(_non_cuda_sources)
547        blt_split_source_list_by_language(SOURCES      ${arg_SOURCES}
548                                          C_LIST       _cuda_sources
549                                          Fortran_LIST _non_cuda_sources)
550
551        set_source_files_properties( ${_cuda_sources} PROPERTIES
552                                     LANGUAGE CUDA)
553
554        if (CUDA_SEPARABLE_COMPILATION)
555            set_source_files_properties( ${_cuda_sources} PROPERTIES
556                                         CUDA_SEPARABLE_COMPILATION ON)
557            set_target_properties( ${arg_NAME} PROPERTIES
558                                   CUDA_SEPARABLE_COMPILATION ON)
559        endif()
560
561        if (DEFINED arg_LIBRARY_TYPE)
562            if (${arg_LIBRARY_TYPE} STREQUAL "static")
563                set_target_properties( ${arg_NAME} PROPERTIES
564                                       CMAKE_CUDA_CREATE_STATIC_LIBRARY ON)
565            else()
566                set_target_properties( ${arg_NAME} PROPERTIES
567                                       CMAKE_CUDA_CREATE_STATIC_LIBRARY OFF)
568            endif()
569        endif()
570
571        # Replicate the behavior of CMAKE_CUDA_RESOLVE_DEVICE_SYMBOLS
572        if(${CMAKE_VERSION} VERSION_LESS "3.16.0" AND CMAKE_CUDA_RESOLVE_DEVICE_SYMBOLS)
573            set_target_properties( ${arg_NAME} PROPERTIES
574                                   CUDA_RESOLVE_DEVICE_SYMBOLS ON)
575        endif()
576    endif()
577endmacro(blt_setup_cuda_target)
578
579##------------------------------------------------------------------------------
580## blt_cleanup_hip_globals(FROM_TARGET <target>)
581##
582## Needed as the SetupHIP macros (specifically, HIP_PREPARE_TARGET_COMMANDS)
583## "pollutes" the global HIP_HIPCC_FLAGS with target-specific options.  This
584## macro removes the target-specific generator expressions from the global flags
585## which have already been copied to source-file-specific instances of the
586## run_hipcc script.  Other global flags in HIP_HIPCC_FLAGS, e.g., those set by
587## the user, are left untouched.
588##------------------------------------------------------------------------------
589macro(blt_cleanup_hip_globals)
590    set(options)
591    set(singleValueArgs FROM_TARGET)
592    set(multiValueArgs)
593
594    # Parse the arguments
595    cmake_parse_arguments(arg "${options}" "${singleValueArgs}"
596                            "${multiValueArgs}" ${ARGN} )
597
598    # Check arguments
599    if ( NOT DEFINED arg_FROM_TARGET )
600        message( FATAL_ERROR "Must provide a FROM_TARGET argument to the 'blt_cleanup_hip_globals' macro")
601    endif()
602
603    # Remove the compile definitions generator expression
604    # This must be copied verbatim from HIP_PREPARE_TARGET_COMMANDS,
605    # which would have just added it to HIP_HIPCC_FLAGS
606    set(_defines_genexpr "$<TARGET_PROPERTY:${arg_FROM_TARGET},COMPILE_DEFINITIONS>")
607    set(_defines_flags_genexpr "$<$<BOOL:${_defines_genexpr}>:-D$<JOIN:${_defines_genexpr}, -D>>")
608    list(REMOVE_ITEM HIP_HIPCC_FLAGS ${_defines_flags_genexpr})
609endmacro(blt_cleanup_hip_globals)
610
611##------------------------------------------------------------------------------
612## blt_add_hip_library(NAME         <libname>
613##                     SOURCES      [source1 [source2 ...]]
614##                     HEADERS      [header1 [header2 ...]]
615##                     DEPENDS_ON   [dep1 ...]
616##                     LIBRARY_TYPE <STATIC, SHARED, or OBJECT>
617##------------------------------------------------------------------------------
618macro(blt_add_hip_library)
619
620    set(options)
621    set(singleValueArgs NAME LIBRARY_TYPE)
622    set(multiValueArgs SOURCES HEADERS DEPENDS_ON)
623
624    # Parse the arguments
625    cmake_parse_arguments(arg "${options}" "${singleValueArgs}"
626                            "${multiValueArgs}" ${ARGN} )
627
628    # Check arguments
629    if ( NOT DEFINED arg_NAME )
630        message( FATAL_ERROR "Must provide a NAME argument to the 'blt_add_hip_library' macro")
631    endif()
632
633    if ( NOT DEFINED arg_SOURCES )
634        message( FATAL_ERROR "Must provide SOURCES to the 'blt_add_hip_library' macro")
635    endif()
636
637    # Determine if hip or hip_runtime are in DEPENDS_ON
638    list(FIND arg_DEPENDS_ON "hip" _hip_index)
639    set(_depends_on_hip FALSE)
640    if(${_hip_index} GREATER -1)
641        set(_depends_on_hip TRUE)
642    endif()
643    list(FIND arg_DEPENDS_ON "hip_runtime" _hip_runtime_index)
644    set(_depends_on_hip_runtime FALSE)
645    if(${_hip_runtime_index} GREATER -1)
646        set(_depends_on_hip_runtime TRUE)
647    endif()
648
649    if (${_depends_on_hip})
650        # if hip is in depends_on, flag each file's language as HIP
651        # instead of leaving it up to CMake to decide
652        # Note: we don't do this when depending on just 'hip_runtime'
653        set(_hip_sources)
654        set(_non_hip_sources)
655        blt_split_source_list_by_language(SOURCES      ${arg_SOURCES}
656                                          C_LIST       _hip_sources
657                                          Fortran_LIST _non_hip_sources)
658
659        set_source_files_properties( ${_hip_sources}
660                                     PROPERTIES
661                                     HIP_SOURCE_PROPERTY_FORMAT TRUE)
662
663        hip_add_library( ${arg_NAME} ${arg_SOURCES} ${arg_LIBRARY_TYPE} )
664        blt_cleanup_hip_globals(FROM_TARGET ${arg_NAME})
665        # Link to the hip_runtime target so it gets pulled in by targets
666        # depending on this target
667        target_link_libraries(${arg_NAME} PUBLIC hip_runtime)
668    else()
669        add_library( ${arg_NAME} ${arg_LIBRARY_TYPE} ${arg_SOURCES} ${arg_HEADERS} )
670    endif()
671
672    if (${_depends_on_hip_runtime} OR ${_depends_on_hip})
673        # This will be propagated up to executable targets that depend on this
674        # library, which will need the HIP linker
675        set_target_properties( ${arg_NAME} PROPERTIES INTERFACE_BLT_LINKER_LANGUAGE_OVERRIDE HIP)
676    endif()
677
678endmacro(blt_add_hip_library)
679
680##------------------------------------------------------------------------------
681## blt_add_hip_executable(NAME         <libname>
682##                        SOURCES      [source1 [source2 ...]]
683##                        HEADERS      [header1 [header2 ...]]
684##                        DEPENDS_ON   [dep1 ...]
685##------------------------------------------------------------------------------
686macro(blt_add_hip_executable)
687
688    set(options)
689    set(singleValueArgs NAME)
690    set(multiValueArgs HEADERS SOURCES DEPENDS_ON)
691
692    # Parse the arguments
693    cmake_parse_arguments(arg "${options}" "${singleValueArgs}"
694                            "${multiValueArgs}" ${ARGN} )
695
696    # Check arguments
697    if ( NOT DEFINED arg_NAME )
698        message( FATAL_ERROR "Must provide a NAME argument to the 'blt_add_hip_executable' macro")
699    endif()
700
701    if ( NOT DEFINED arg_SOURCES )
702        message( FATAL_ERROR "Must provide SOURCES to the 'blt_add_hip_executable' macro")
703    endif()
704
705    # Determine if hip or hip_runtime are in DEPENDS_ON
706    list(FIND arg_DEPENDS_ON "hip" _hip_index)
707    set(_depends_on_hip FALSE)
708    if(${_hip_index} GREATER -1)
709        set(_depends_on_hip TRUE)
710    endif()
711    list(FIND arg_DEPENDS_ON "hip_runtime" _hip_runtime_index)
712    set(_depends_on_hip_runtime FALSE)
713    if(${_hip_runtime_index} GREATER -1)
714        set(_depends_on_hip_runtime TRUE)
715    endif()
716
717    blt_expand_depends(DEPENDS_ON ${arg_DEPENDS_ON} RESULT _expanded_DEPENDS_ON)
718    foreach( dependency ${_expanded_DEPENDS_ON} )
719        if(TARGET ${dependency})
720            get_target_property(_dep_type ${dependency} TYPE)
721            if(NOT "${_dep_type}" STREQUAL "INTERFACE_LIBRARY")
722                # Propagate the overridden linker language, if applicable
723                get_target_property(_blt_link_lang ${dependency} INTERFACE_BLT_LINKER_LANGUAGE_OVERRIDE)
724                if(_blt_link_lang STREQUAL "HIP")
725                    set(_depends_on_hip_runtime TRUE)
726                endif()
727            endif()
728        endif()
729    endforeach()
730
731    if (${_depends_on_hip} OR ${_depends_on_hip_runtime})
732        # if hip is in depends_on, flag each file's language as HIP
733        # instead of leaving it up to CMake to decide
734        # Note: we don't do this when depending on just 'hip_runtime'
735        set(_hip_sources)
736        set(_non_hip_sources)
737        blt_split_source_list_by_language(SOURCES      ${arg_SOURCES}
738                                          C_LIST       _hip_sources
739                                          Fortran_LIST _non_hip_sources)
740
741        set_source_files_properties( ${_hip_sources}
742                                     PROPERTIES
743                                     HIP_SOURCE_PROPERTY_FORMAT TRUE)
744
745        hip_add_executable( ${arg_NAME} ${arg_SOURCES} )
746        blt_cleanup_hip_globals(FROM_TARGET ${arg_NAME})
747    else()
748        add_executable( ${arg_NAME} ${arg_SOURCES} ${arg_HEADERS})
749    endif()
750
751    # Save the expanded dependencies to avoid recalculating later
752    set_target_properties(${arg_NAME} PROPERTIES
753                          BLT_EXPANDED_DEPENDENCIES "${_expanded_DEPENDS_ON}")
754
755endmacro(blt_add_hip_executable)
756
757##------------------------------------------------------------------------------
758## blt_split_source_list_by_language( SOURCES <sources>
759##                                    C_LIST <list name>
760##                                    Fortran_LIST <list name>
761##                                    Python_LIST <list name>)
762##                                    CMAKE_LIST <list name>)
763##
764## Filters source list by file extension into C/C++, Fortran, Python, and
765## CMake source lists based on BLT_C_FILE_EXTS, BLT_Fortran_FILE_EXTS,
766## and BLT_CMAKE_FILE_EXTS (global BLT variables). Files named
767## "CMakeLists.txt" are also filtered here. Files with no extension
768## or generator expressions that are not object libraries (of the form
769## "$<TARGET_OBJECTS:nameofobjectlibrary>") will throw fatal errors.
770## ------------------------------------------------------------------------------
771macro(blt_split_source_list_by_language)
772
773    set(options)
774    set(singleValueArgs C_LIST Fortran_LIST Python_LIST CMAKE_LIST)
775    set(multiValueArgs SOURCES)
776
777    # Parse the arguments
778    cmake_parse_arguments(arg "${options}" "${singleValueArgs}"
779                            "${multiValueArgs}" ${ARGN} )
780
781    # Check arguments
782    if ( NOT DEFINED arg_SOURCES )
783        message( FATAL_ERROR "Must provide a SOURCES argument to the 'blt_split_source_list_by_language' macro" )
784    endif()
785
786    # Generate source lists based on language
787    foreach(_file ${arg_SOURCES})
788        # Allow CMake object libraries but disallow generator expressions
789        # in source lists due to this causing all sorts of bad side effects
790        if("${_file}" MATCHES "^\\$<TARGET_OBJECTS:")
791            continue()
792        elseif("${_file}" MATCHES "^\\$<")
793            message(FATAL_ERROR "blt_split_source_list_by_language macro does not support generator expressions because CMake does not provide a way to evaluate them. Given generator expression: ${_file}")
794        endif()
795
796        get_filename_component(_ext "${_file}" EXT)
797        if("${_ext}" STREQUAL "")
798            message(FATAL_ERROR "blt_split_source_list_by_language given source file with no extension: ${_file}")
799        endif()
800
801        get_filename_component(_name "${_file}" NAME)
802
803        string(TOLOWER "${_ext}" _ext_lower)
804
805        if("${_ext_lower}" IN_LIST BLT_C_FILE_EXTS)
806            if (DEFINED arg_C_LIST)
807                list(APPEND ${arg_C_LIST} "${_file}")
808            endif()
809        elseif("${_ext_lower}" IN_LIST BLT_Fortran_FILE_EXTS)
810            if (DEFINED arg_Fortran_LIST)
811                list(APPEND ${arg_Fortran_LIST} "${_file}")
812            endif()
813        elseif("${_ext_lower}" IN_LIST BLT_Python_FILE_EXTS)
814            if (DEFINED arg_Python_LIST)
815                list(APPEND ${arg_Python_LIST} "${_file}")
816            endif()
817        elseif("${_ext_lower}" IN_LIST BLT_CMAKE_FILE_EXTS OR "${_name}" STREQUAL "CMakeLists.txt")
818            if (DEFINED arg_CMAKE_LIST)
819                list(APPEND ${arg_CMAKE_LIST} "${_file}")
820            endif()
821        else()
822            message(FATAL_ERROR "blt_split_source_list_by_language given source file with unknown file extension. Add the missing extension to the corresponding list (BLT_C_FILE_EXTS, BLT_Fortran_FILE_EXTS, BLT_Python_FILE_EXTS, or BLT_CMAKE_FILE_EXTS).\n Unknown file: ${_file}")
823        endif()
824    endforeach()
825
826endmacro(blt_split_source_list_by_language)
827
828
829##------------------------------------------------------------------------------
830## blt_update_project_sources( TARGET_SOURCES <sources> )
831##------------------------------------------------------------------------------
832macro(blt_update_project_sources)
833
834    set(options)
835    set(singleValueArgs)
836    set(multiValueArgs TARGET_SOURCES)
837
838    # Parse the arguments
839    cmake_parse_arguments(arg "${options}" "${singleValueArgs}"
840                            "${multiValueArgs}" ${ARGN} )
841
842    # Check arguments
843    if ( NOT DEFINED arg_TARGET_SOURCES )
844        message( FATAL_ERROR "Must provide target sources" )
845    endif()
846
847    ## append the target source to the all project sources
848    foreach( src ${arg_TARGET_SOURCES} )
849        if(IS_ABSOLUTE ${src})
850            list(APPEND "${PROJECT_NAME}_ALL_SOURCES" "${src}")
851        else()
852            list(APPEND "${PROJECT_NAME}_ALL_SOURCES"
853                "${CMAKE_CURRENT_SOURCE_DIR}/${src}")
854        endif()
855    endforeach()
856
857    set( "${PROJECT_NAME}_ALL_SOURCES" "${${PROJECT_NAME}_ALL_SOURCES}"
858        CACHE STRING "" FORCE )
859    mark_as_advanced("${PROJECT_NAME}_ALL_SOURCES")
860
861endmacro(blt_update_project_sources)
862
863
864##------------------------------------------------------------------------------
865## blt_filter_list( TO <list_var> REGEX <string> OPERATION <string> )
866##
867## This macro provides the same functionality as cmake's list(FILTER )
868## which is only available in cmake-3.6+.
869##
870## The TO argument (required) is the name of a list variable.
871## The REGEX argument (required) is a string containing a regex.
872## The OPERATION argument (required) is a string that defines the macro's operation.
873## Supported values are "include" and "exclude"
874##
875## The filter is applied to the input list, which is modified in place.
876##------------------------------------------------------------------------------
877macro(blt_filter_list)
878
879    set(options )
880    set(singleValueArgs TO REGEX OPERATION)
881    set(multiValueArgs )
882
883    # Parse arguments
884    cmake_parse_arguments(arg "${options}" "${singleValueArgs}"
885                            "${multiValueArgs}" ${ARGN} )
886
887    # Check arguments
888    if( NOT DEFINED arg_TO )
889        message(FATAL_ERROR "blt_filter_list macro requires a TO <list> argument")
890    endif()
891
892    if( NOT DEFINED arg_REGEX )
893        message(FATAL_ERROR "blt_filter_list macro requires a REGEX <string> argument")
894    endif()
895
896    # Ensure OPERATION argument is provided with value "include" or "exclude"
897    set(_exclude)
898    if( NOT DEFINED arg_OPERATION )
899        message(FATAL_ERROR "blt_filter_list macro requires a OPERATION <string> argument")
900    elseif(NOT arg_OPERATION MATCHES "^(include|exclude)$")
901        message(FATAL_ERROR "blt_filter_list macro's OPERATION argument must be either 'include' or 'exclude'")
902    else()
903        if(${arg_OPERATION} MATCHES "exclude")
904            set(_exclude TRUE)
905        else()
906            set(_exclude FALSE)
907        endif()
908    endif()
909
910    # Filter the list
911    set(_resultList)
912    foreach(elem ${${arg_TO}})
913        if(elem MATCHES ${arg_REGEX})
914            if(NOT ${_exclude})
915                list(APPEND _resultList ${elem})
916            endif()
917        else()
918            if(${_exclude})
919                list(APPEND _resultList ${elem})
920            endif()
921        endif()
922    endforeach()
923
924    # Copy result back to input list variable
925    set(${arg_TO} ${_resultList})
926
927    unset(_exclude)
928    unset(_resultList)
929endmacro(blt_filter_list)
930
931
932##------------------------------------------------------------------------------
933## blt_clean_target( TARGET <target name> )
934##
935## This macro removes duplicates in a small subset of target properties that are
936## safe to do so.
937##------------------------------------------------------------------------------
938macro(blt_clean_target)
939
940    set(options )
941    set(singleValueArgs TARGET)
942    set(multiValueArgs )
943
944    # Parse arguments
945    cmake_parse_arguments(arg "${options}" "${singleValueArgs}"
946                            "${multiValueArgs}" ${ARGN} )
947
948    # Properties to remove duplicates from
949    set(_dup_properties
950        INCLUDE_DIRECTORIES
951        INTERFACE_COMPILE_DEFINITIONS
952        INTERFACE_INCLUDE_DIRECTORIES
953        INTERFACE_SYSTEM_INCLUDE_DIRECTORIES)
954
955    foreach(_prop ${_dup_properties})
956        get_target_property(_values ${arg_TARGET} ${_prop})
957        if ( _values )
958            list(REMOVE_DUPLICATES _values)
959            set_property(TARGET ${arg_TARGET} PROPERTY ${_prop} ${_values})
960        endif()
961    endforeach()
962
963endmacro(blt_clean_target)
964