blt icon indicating copy to clipboard operation
blt copied to clipboard

Link options from MPI compiler wrapper collapsed incorrectly (CMake 3.13+)

Open klevzoff opened this issue 5 years ago • 1 comments

CMake: 3.14.3 BLT: https://github.com/LLNL/blt/commit/938345715810c6457d3e57a2c20d4ee0fa253632

When using e.g. Intel MPI, link flags obtained from mpicc may look like the following:

-Xlinker -rpath -Xlinker /path/to/A -Xlinker -rpath -Xlinker /path/to/B -Xlinker -rpath -Xlinker /path/to/C ...

After the link options are set on the target in blt_add_target_link_flags via LINK_OPTIONS, the resulting command line looks like:

-Xlinker -rpath /path/to/A /path/to/B /path/to/C ...

which is incorrect. This is because CMake removes duplicates, as per documentation.

Unless there is a way to pass the link flags verbatim (as opposed to splitting and adding one by one), perhaps the correct treatment would be:

  1. Collect and concatenate all consecutive options that have a preceding -Xlinker into a comma-separated string
  2. Add the resulting string with a LINKER: prefix.

So we end up adding

LINKER:-rpath,/path/to/A,-rpath,/path/to/B,-rpath,/path/to/C

as a single link option, which CMake translates back into the correct string (using -Xlinker or -Wl syntax, as appropriate).

klevzoff avatar Jul 25 '19 04:07 klevzoff

Solution that works for me (but is kinda ugly and very specific to just handling -Xlinker flag): in blt_add_target_link_flags

if( ${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.13.0" )
    # In CMake 3.13+, LINK_FLAGS was converted to LINK_OPTIONS.
    # This now supports generator expressions but expects a list
    # not a string
    separate_arguments(_flag_list NATIVE_COMMAND "${arg_FLAGS}" )
    set(_prev_xlinker 0)
    set(_xlinker_flags "")
    foreach( _flag  ${_flag_list} )
        # If previous flag was -Xlinker, accumulate current flag unto a list, reset the indicator
        if(_prev_xlinker)
            list(APPEND _xlinker_flags "${_flag}")
            set(_prev_xlinker 0)
        # Else, if current flag is -Xlinker, set the indicator
        elseif("${_flag}" STREQUAL "-Xlinker")
            set(_prev_xlinker 1)
        # Else (a free-standing flag, i.e. a series of -Xlinker guarded flags is over),
        # dump the accumulated list with a prefix, then add the current flag as well
        else()
            if(_xlinker_flags)
                list(JOIN _xlinker_flags "," _xlinker_flags_str)
                set_property(TARGET ${arg_TO} APPEND PROPERTY LINK_OPTIONS "LINKER:${_xlinker_flags_str}")
                set(_xlinker_flags "")
            endif()
            set_property(TARGET ${arg_TO} APPEND PROPERTY LINK_OPTIONS "${_flag}")
        endif()
    endforeach()
    # Once the list is done, also dump any accumulated -Xlinker guarded flags with a prefix
    if(_xlinker_flags)
        list(JOIN _xlinker_flags "," _xlinker_flags_str)
        set_property(TARGET ${arg_TO} APPEND PROPERTY LINK_OPTIONS "LINKER:${_xlinker_flags_str}" )
        set(_xlinker_flags "")
    endif()

else()
    ...
endif()

klevzoff avatar Jul 25 '19 04:07 klevzoff