1################################################################################################ 2# Exclude and prepend functionalities 3function(exclude OUTPUT INPUT) 4set(EXCLUDES ${ARGN}) 5foreach(EXCLUDE ${EXCLUDES}) 6 list(REMOVE_ITEM INPUT "${EXCLUDE}") 7endforeach() 8set(${OUTPUT} ${INPUT} PARENT_SCOPE) 9endfunction(exclude) 10 11function(prepend OUTPUT PREPEND) 12set(OUT "") 13foreach(ITEM ${ARGN}) 14 list(APPEND OUT "${PREPEND}${ITEM}") 15endforeach() 16set(${OUTPUT} ${OUT} PARENT_SCOPE) 17endfunction(prepend) 18 19################################################################################################ 20# Parses a version string that might have values beyond major, minor, and patch 21# and set version variables for the library. 22# Usage: 23# caffe2_parse_version_str(<library_name> <version_string>) 24function(caffe2_parse_version_str LIBNAME VERSIONSTR) 25 string(REGEX REPLACE "^([0-9]+).*$" "\\1" ${LIBNAME}_VERSION_MAJOR "${VERSIONSTR}") 26 string(REGEX REPLACE "^[0-9]+\\.([0-9]+).*$" "\\1" ${LIBNAME}_VERSION_MINOR "${VERSIONSTR}") 27 string(REGEX REPLACE "[0-9]+\\.[0-9]+\\.([0-9]+).*$" "\\1" ${LIBNAME}_VERSION_PATCH "${VERSIONSTR}") 28 set(${LIBNAME}_VERSION_MAJOR ${${LIBNAME}_VERSION_MAJOR} ${ARGN} PARENT_SCOPE) 29 set(${LIBNAME}_VERSION_MINOR ${${LIBNAME}_VERSION_MINOR} ${ARGN} PARENT_SCOPE) 30 set(${LIBNAME}_VERSION_PATCH ${${LIBNAME}_VERSION_PATCH} ${ARGN} PARENT_SCOPE) 31 set(${LIBNAME}_VERSION "${${LIBNAME}_VERSION_MAJOR}.${${LIBNAME}_VERSION_MINOR}.${${LIBNAME}_VERSION_PATCH}" PARENT_SCOPE) 32endfunction() 33 34### 35# Removes common indentation from a block of text to produce code suitable for 36# setting to `python -c`, or using with pycmd. This allows multiline code to be 37# nested nicely in the surrounding code structure. 38# 39# This function respsects Python_EXECUTABLE if it defined, otherwise it uses 40# `python` and hopes for the best. An error will be thrown if it is not found. 41# 42# Args: 43# outvar : variable that will hold the stdout of the python command 44# text : text to remove indentation from 45# 46function(dedent outvar text) 47 # Use Python_EXECUTABLE if it is defined, otherwise default to python 48 if("${Python_EXECUTABLE}" STREQUAL "") 49 set(_python_exe "python3") 50 else() 51 set(_python_exe "${Python_EXECUTABLE}") 52 endif() 53 set(_fixup_cmd "import sys; from textwrap import dedent; print(dedent(sys.stdin.read()))") 54 file(WRITE "${CMAKE_BINARY_DIR}/indented.txt" "${text}") 55 execute_process( 56 COMMAND "${_python_exe}" -c "${_fixup_cmd}" 57 INPUT_FILE "${CMAKE_BINARY_DIR}/indented.txt" 58 RESULT_VARIABLE _dedent_exitcode 59 OUTPUT_VARIABLE _dedent_text) 60 if(NOT _dedent_exitcode EQUAL 0) 61 message(ERROR " Failed to remove indentation from: \n\"\"\"\n${text}\n\"\"\" 62 Python dedent failed with error code: ${_dedent_exitcode}") 63 message(FATAL_ERROR " Python dedent failed with error code: ${_dedent_exitcode}") 64 endif() 65 # Remove supurflous newlines (artifacts of print) 66 string(STRIP "${_dedent_text}" _dedent_text) 67 set(${outvar} "${_dedent_text}" PARENT_SCOPE) 68endfunction() 69 70 71function(pycmd_no_exit outvar exitcode cmd) 72 # Use Python_EXECUTABLE if it is defined, otherwise default to python 73 if("${Python_EXECUTABLE}" STREQUAL "") 74 set(_python_exe "python") 75 else() 76 set(_python_exe "${Python_EXECUTABLE}") 77 endif() 78 # run the actual command 79 execute_process( 80 COMMAND "${_python_exe}" -c "${cmd}" 81 RESULT_VARIABLE _exitcode 82 OUTPUT_VARIABLE _output) 83 # Remove supurflous newlines (artifacts of print) 84 string(STRIP "${_output}" _output) 85 set(${outvar} "${_output}" PARENT_SCOPE) 86 set(${exitcode} "${_exitcode}" PARENT_SCOPE) 87endfunction() 88 89 90### 91# Helper function to run `python -c "<cmd>"` and capture the results of stdout 92# 93# Runs a python command and populates an outvar with the result of stdout. 94# Common indentation in the text of `cmd` is removed before the command is 95# executed, so the caller does not need to worry about indentation issues. 96# 97# This function respsects Python_EXECUTABLE if it defined, otherwise it uses 98# `python` and hopes for the best. An error will be thrown if it is not found. 99# 100# Args: 101# outvar : variable that will hold the stdout of the python command 102# cmd : text representing a (possibly multiline) block of python code 103# 104function(pycmd outvar cmd) 105 dedent(_dedent_cmd "${cmd}") 106 pycmd_no_exit(_output _exitcode "${_dedent_cmd}") 107 108 if(NOT _exitcode EQUAL 0) 109 message(ERROR " Failed when running python code: \"\"\"\n${_dedent_cmd}\n\"\"\"") 110 message(FATAL_ERROR " Python command failed with error code: ${_exitcode}") 111 endif() 112 # Remove supurflous newlines (artifacts of print) 113 string(STRIP "${_output}" _output) 114 set(${outvar} "${_output}" PARENT_SCOPE) 115endfunction() 116 117 118############################################################################## 119# Macro to update cached options. 120macro(caffe2_update_option variable value) 121 if(CAFFE2_CMAKE_BUILDING_WITH_MAIN_REPO) 122 get_property(__help_string CACHE ${variable} PROPERTY HELPSTRING) 123 set(${variable} ${value} CACHE BOOL ${__help_string} FORCE) 124 else() 125 set(${variable} ${value}) 126 endif() 127endmacro() 128 129 130############################################################################## 131# Add an interface library definition that is dependent on the source. 132# 133# It's probably easiest to explain why this macro exists, by describing 134# what things would look like if we didn't have this macro. 135# 136# Let's suppose we want to statically link against torch. We've defined 137# a library in cmake called torch, and we might think that we just 138# target_link_libraries(my-app PUBLIC torch). This will result in a 139# linker argument 'libtorch.a' getting passed to the linker. 140# 141# Unfortunately, this link command is wrong! We have static 142# initializers in libtorch.a that would get improperly pruned by 143# the default link settings. What we actually need is for you 144# to do -Wl,--whole-archive,libtorch.a -Wl,--no-whole-archive to ensure 145# that we keep all symbols, even if they are (seemingly) not used. 146# 147# What caffe2_interface_library does is create an interface library 148# that indirectly depends on the real library, but sets up the link 149# arguments so that you get all of the extra link settings you need. 150# The result is not a "real" library, and so we have to manually 151# copy over necessary properties from the original target. 152# 153# (The discussion above is about static libraries, but a similar 154# situation occurs for dynamic libraries: if no symbols are used from 155# a dynamic library, it will be pruned unless you are --no-as-needed) 156macro(caffe2_interface_library SRC DST) 157 add_library(${DST} INTERFACE) 158 add_dependencies(${DST} ${SRC}) 159 # Depending on the nature of the source library as well as the compiler, 160 # determine the needed compilation flags. 161 get_target_property(__src_target_type ${SRC} TYPE) 162 # Depending on the type of the source library, we will set up the 163 # link command for the specific SRC library. 164 if(${__src_target_type} STREQUAL "STATIC_LIBRARY") 165 # In the case of static library, we will need to add whole-static flags. 166 if(APPLE) 167 target_link_libraries( 168 ${DST} INTERFACE -Wl,-force_load,\"$<TARGET_FILE:${SRC}>\") 169 elseif(MSVC) 170 # In MSVC, we will add whole archive in default. 171 target_link_libraries( 172 ${DST} INTERFACE "$<TARGET_FILE:${SRC}>") 173 target_link_options( 174 ${DST} INTERFACE "-WHOLEARCHIVE:$<TARGET_FILE:${SRC}>") 175 else() 176 # Assume everything else is like gcc 177 target_link_libraries(${DST} INTERFACE 178 "-Wl,--whole-archive,\"$<TARGET_FILE:${SRC}>\" -Wl,--no-whole-archive") 179 endif() 180 # Link all interface link libraries of the src target as well. 181 # For static library, we need to explicitly depend on all the libraries 182 # that are the dependent library of the source library. Note that we cannot 183 # use the populated INTERFACE_LINK_LIBRARIES property, because if one of the 184 # dependent library is not a target, cmake creates a $<LINK_ONLY:src> wrapper 185 # and then one is not able to find target "src". For more discussions, check 186 # https://gitlab.kitware.com/cmake/cmake/issues/15415 187 # https://cmake.org/pipermail/cmake-developers/2013-May/019019.html 188 # Specifically the following quote 189 # 190 # """ 191 # For STATIC libraries we can define that the PUBLIC/PRIVATE/INTERFACE keys 192 # are ignored for linking and that it always populates both LINK_LIBRARIES 193 # LINK_INTERFACE_LIBRARIES. Note that for STATIC libraries the 194 # LINK_LIBRARIES property will not be used for anything except build-order 195 # dependencies. 196 # """ 197 target_link_libraries(${DST} INTERFACE 198 $<TARGET_PROPERTY:${SRC},LINK_LIBRARIES>) 199 elseif(${__src_target_type} STREQUAL "SHARED_LIBRARY") 200 if("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") 201 target_link_libraries(${DST} INTERFACE 202 "-Wl,--no-as-needed,\"$<TARGET_FILE:${SRC}>\" -Wl,--as-needed") 203 else() 204 target_link_libraries(${DST} INTERFACE ${SRC}) 205 endif() 206 # Link all interface link libraries of the src target as well. 207 # For shared libraries, we can simply depend on the INTERFACE_LINK_LIBRARIES 208 # property of the target. 209 target_link_libraries(${DST} INTERFACE 210 $<TARGET_PROPERTY:${SRC},INTERFACE_LINK_LIBRARIES>) 211 else() 212 message(FATAL_ERROR 213 "You made a CMake build file error: target " ${SRC} 214 " must be of type either STATIC_LIBRARY or SHARED_LIBRARY. However, " 215 "I got " ${__src_target_type} ".") 216 endif() 217 # For all other interface properties, manually inherit from the source target. 218 set_target_properties(${DST} PROPERTIES 219 INTERFACE_COMPILE_DEFINITIONS 220 $<TARGET_PROPERTY:${SRC},INTERFACE_COMPILE_DEFINITIONS> 221 INTERFACE_COMPILE_OPTIONS 222 $<TARGET_PROPERTY:${SRC},INTERFACE_COMPILE_OPTIONS> 223 INTERFACE_INCLUDE_DIRECTORIES 224 $<TARGET_PROPERTY:${SRC},INTERFACE_INCLUDE_DIRECTORIES> 225 INTERFACE_SYSTEM_INCLUDE_DIRECTORIES 226 $<TARGET_PROPERTY:${SRC},INTERFACE_SYSTEM_INCLUDE_DIRECTORIES>) 227endmacro() 228 229 230############################################################################## 231# Creating a Caffe2 binary target with sources specified with relative path. 232# Usage: 233# caffe2_binary_target(target_name_or_src <src1> [<src2>] [<src3>] ...) 234# If only target_name_or_src is specified, this target is build with one single 235# source file and the target name is autogen from the filename. Otherwise, the 236# target name is given by the first argument and the rest are the source files 237# to build the target. 238function(caffe2_binary_target target_name_or_src) 239 # https://cmake.org/cmake/help/latest/command/function.html 240 # Checking that ARGC is greater than # is the only way to ensure 241 # that ARGV# was passed to the function as an extra argument. 242 if(ARGC GREATER 1) 243 set(__target ${target_name_or_src}) 244 prepend(__srcs "${CMAKE_CURRENT_SOURCE_DIR}/" "${ARGN}") 245 else() 246 get_filename_component(__target ${target_name_or_src} NAME_WE) 247 prepend(__srcs "${CMAKE_CURRENT_SOURCE_DIR}/" "${target_name_or_src}") 248 endif() 249 add_executable(${__target} ${__srcs}) 250 target_link_libraries(${__target} torch_library) 251 # If we have Caffe2_MODULES defined, we will also link with the modules. 252 if(DEFINED Caffe2_MODULES) 253 target_link_libraries(${__target} ${Caffe2_MODULES}) 254 endif() 255 install(TARGETS ${__target} DESTINATION bin) 256endfunction() 257 258function(caffe2_hip_binary_target target_name_or_src) 259 if(ARGC GREATER 1) 260 set(__target ${target_name_or_src}) 261 prepend(__srcs "${CMAKE_CURRENT_SOURCE_DIR}/" "${ARGN}") 262 else() 263 get_filename_component(__target ${target_name_or_src} NAME_WE) 264 prepend(__srcs "${CMAKE_CURRENT_SOURCE_DIR}/" "${target_name_or_src}") 265 endif() 266 267 caffe2_binary_target(${target_name_or_src}) 268 269 target_compile_options(${__target} PRIVATE ${HIP_CXX_FLAGS}) 270 target_include_directories(${__target} PRIVATE ${Caffe2_HIP_INCLUDE}) 271endfunction() 272 273 274############################################################################## 275# Multiplex between adding libraries for CUDA versus HIP (AMD Software Stack). 276# Usage: 277# torch_cuda_based_add_library(cuda_target) 278# 279macro(torch_cuda_based_add_library cuda_target) 280 if(USE_ROCM) 281 hip_add_library(${cuda_target} ${ARGN}) 282 elseif(USE_CUDA) 283 add_library(${cuda_target} ${ARGN}) 284 else() 285 endif() 286endmacro() 287 288############################################################################## 289# Get the HIP arch flags specified by PYTORCH_ROCM_ARCH. 290# Usage: 291# torch_hip_get_arch_list(variable_to_store_flags) 292# 293macro(torch_hip_get_arch_list store_var) 294 if(DEFINED ENV{PYTORCH_ROCM_ARCH}) 295 set(_TMP $ENV{PYTORCH_ROCM_ARCH}) 296 else() 297 # Use arch of installed GPUs as default 298 execute_process(COMMAND "rocm_agent_enumerator" COMMAND bash "-c" "grep -v gfx000 | sort -u | xargs | tr -d '\n'" 299 RESULT_VARIABLE ROCM_AGENT_ENUMERATOR_RESULT 300 OUTPUT_VARIABLE ROCM_ARCH_INSTALLED) 301 if(NOT ROCM_AGENT_ENUMERATOR_RESULT EQUAL 0) 302 message(FATAL_ERROR " Could not detect ROCm arch for GPUs on machine. Result: '${ROCM_AGENT_ENUMERATOR_RESULT}'") 303 endif() 304 set(_TMP ${ROCM_ARCH_INSTALLED}) 305 endif() 306 string(REPLACE " " ";" ${store_var} "${_TMP}") 307endmacro() 308 309############################################################################## 310# Get the NVCC arch flags specified by TORCH_CUDA_ARCH_LIST and CUDA_ARCH_NAME. 311# Usage: 312# torch_cuda_get_nvcc_gencode_flag(variable_to_store_flags) 313# 314macro(torch_cuda_get_nvcc_gencode_flag store_var) 315 # setting nvcc arch flags 316 if((NOT DEFINED TORCH_CUDA_ARCH_LIST) AND (DEFINED ENV{TORCH_CUDA_ARCH_LIST})) 317 message(WARNING 318 "In the future we will require one to explicitly pass " 319 "TORCH_CUDA_ARCH_LIST to cmake instead of implicitly setting it as an " 320 "env variable. This will become a FATAL_ERROR in future version of " 321 "pytorch.") 322 set(TORCH_CUDA_ARCH_LIST $ENV{TORCH_CUDA_ARCH_LIST}) 323 endif() 324 if(DEFINED CUDA_ARCH_NAME) 325 message(WARNING 326 "CUDA_ARCH_NAME is no longer used. Use TORCH_CUDA_ARCH_LIST instead. " 327 "Right now, CUDA_ARCH_NAME is ${CUDA_ARCH_NAME} and " 328 "TORCH_CUDA_ARCH_LIST is ${TORCH_CUDA_ARCH_LIST}.") 329 set(TORCH_CUDA_ARCH_LIST TORCH_CUDA_ARCH_LIST ${CUDA_ARCH_NAME}) 330 endif() 331 332 # Invoke cuda_select_nvcc_arch_flags from proper cmake FindCUDA. 333 cuda_select_nvcc_arch_flags(${store_var} ${TORCH_CUDA_ARCH_LIST}) 334endmacro() 335 336 337############################################################################## 338# Add standard compile options. 339# Usage: 340# torch_compile_options(lib_name) 341function(torch_compile_options libname) 342 set_property(TARGET ${libname} PROPERTY CXX_STANDARD 17) 343 344 # until they can be unified, keep these lists synced with setup.py 345 if(MSVC) 346 347 if(MSVC_Z7_OVERRIDE) 348 set(MSVC_DEBINFO_OPTION "/Z7") 349 else() 350 set(MSVC_DEBINFO_OPTION "/Zi") 351 endif() 352 353 target_compile_options(${libname} PUBLIC 354 $<$<COMPILE_LANGUAGE:CXX>: 355 ${MSVC_RUNTIME_LIBRARY_OPTION} 356 $<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:${MSVC_DEBINFO_OPTION}> 357 /EHsc 358 /bigobj> 359 ) 360 else() 361 set(private_compile_options 362 -Wall 363 -Wextra 364 -Wdeprecated 365 -Wno-unused-parameter 366 -Wno-missing-field-initializers 367 -Wno-type-limits 368 -Wno-array-bounds 369 -Wno-unknown-pragmas 370 -Wno-strict-overflow 371 -Wno-strict-aliasing 372 ) 373 list(APPEND private_compile_options -Wunused-function) 374 list(APPEND private_compile_options -Wunused-variable) 375 if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 376 list(APPEND private_compile_options -Wunused-but-set-variable) 377 endif() 378 if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") 379 list(APPEND private_compile_options -Wunused-private-field) 380 endif() 381 if(NOT "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") 382 list(APPEND private_compile_options 383 # Considered to be flaky. See the discussion at 384 # https://github.com/pytorch/pytorch/pull/9608 385 -Wno-maybe-uninitialized) 386 endif() 387 388 if(WERROR) 389 list(APPEND private_compile_options 390 -Werror 391 -Werror=inconsistent-missing-override 392 -Werror=inconsistent-missing-destructor-override 393 -Werror=unused-function 394 -Werror=unused-variable 395 -Werror=pedantic 396 ) 397 if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 398 list(APPEND private_compile_options -Werror=unused-but-set-variable) 399 endif() 400 endif() 401 endif() 402 403 404 target_compile_options(${libname} PRIVATE 405 $<$<COMPILE_LANGUAGE:CXX>:${private_compile_options}>) 406 if(USE_CUDA) 407 foreach(option IN LISTS private_compile_options) 408 target_compile_options(${libname} PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:-Xcompiler ${option}>) 409 endforeach() 410 endif() 411 412 if(NOT WIN32 AND NOT USE_ASAN) 413 # Enable hidden visibility by default to make it easier to debug issues with 414 # TORCH_API annotations. Hidden visibility with selective default visibility 415 # behaves close enough to Windows' dllimport/dllexport. 416 # 417 # Unfortunately, hidden visibility messes up some ubsan warnings because 418 # templated classes crossing library boundary get duplicated (but identical) 419 # definitions. It's easier to just disable it. 420 target_compile_options(${libname} PRIVATE 421 $<$<COMPILE_LANGUAGE:CXX>: -fvisibility=hidden>) 422 endif() 423 424 # Use -O2 for release builds (-O3 doesn't improve perf, and -Os results in perf regression) 425 target_compile_options(${libname} PRIVATE 426 $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<OR:$<CONFIG:Release>,$<CONFIG:RelWithDebInfo>>>:-O2>) 427 428endfunction() 429 430############################################################################## 431# Set old-style FindCuda.cmake compile flags from modern CMake cuda flags. 432# Usage: 433# torch_update_find_cuda_flags() 434function(torch_update_find_cuda_flags) 435 # Convert -O2 -Xcompiler="-O2 -Wall" to "-O2;-Xcompiler=-O2,-Wall" 436 if(USE_CUDA) 437 separate_arguments(FLAGS UNIX_COMMAND "${CMAKE_CUDA_FLAGS}") 438 string(REPLACE " " "," FLAGS "${FLAGS}") 439 set(CUDA_NVCC_FLAGS ${FLAGS} PARENT_SCOPE) 440 441 separate_arguments(FLAGS_DEBUG UNIX_COMMAND "${CMAKE_CUDA_FLAGS_DEBUG}") 442 string(REPLACE " " "," FLAGS_DEBUG "${FLAGS_DEBUG}") 443 set(CUDA_NVCC_FLAGS_DEBUG "${FLAGS_DEBUG}" PARENT_SCOPE) 444 445 separate_arguments(FLAGS_RELEASE UNIX_COMMAND "${CMAKE_CUDA_FLAGS_RELEASE}") 446 string(REPLACE " " "," FLAGS_RELEASE "${FLAGS_RELEASE}") 447 set(CUDA_NVCC_FLAGS_RELEASE "${FLAGS_RELEASE}" PARENT_SCOPE) 448 449 separate_arguments(FLAGS_MINSIZEREL UNIX_COMMAND "${CMAKE_CUDA_FLAGS_MINSIZEREL}") 450 string(REPLACE " " "," FLAGS_MINSIZEREL "${FLAGS_MINSIZEREL}") 451 set(CUDA_NVCC_FLAGS_MINSIZEREL "${FLAGS_MINSIZEREL}" PARENT_SCOPE) 452 453 separate_arguments(FLAGS_RELWITHDEBINFO UNIX_COMMAND "${CMAKE_CUDA_FLAGS_RELWITHDEBINFO}") 454 string(REPLACE " " "," FLAGS_RELWITHDEBINFO "${FLAGS_RELWITHDEBINFO}") 455 set(CUDA_NVCC_FLAGS_RELWITHDEBINFO "${FLAGS_RELWITHDEBINFO}" PARENT_SCOPE) 456 457 message(STATUS "Converting CMAKE_CUDA_FLAGS to CUDA_NVCC_FLAGS:\n" 458 " CUDA_NVCC_FLAGS = ${FLAGS}\n" 459 " CUDA_NVCC_FLAGS_DEBUG = ${FLAGS_DEBUG}\n" 460 " CUDA_NVCC_FLAGS_RELEASE = ${FLAGS_RELEASE}\n" 461 " CUDA_NVCC_FLAGS_RELWITHDEBINFO = ${FLAGS_RELWITHDEBINFO}\n" 462 " CUDA_NVCC_FLAGS_MINSIZEREL = ${FLAGS_MINSIZEREL}") 463 endif() 464endfunction() 465 466include(CheckCXXCompilerFlag) 467 468############################################################################## 469# CHeck if given flag is supported and append it to provided outputvar 470# Also define HAS_UPPER_CASE_FLAG_NAME variable 471# Usage: 472# append_cxx_flag_if_supported("-Werror" CMAKE_CXX_FLAGS) 473function(append_cxx_flag_if_supported flag outputvar) 474 string(TOUPPER "HAS${flag}" _FLAG_NAME) 475 string(REGEX REPLACE "[=-]" "_" _FLAG_NAME "${_FLAG_NAME}") 476 # GCC silents unknown -Wno-XXX flags, so we detect the corresponding -WXXX. 477 if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 478 string(REGEX REPLACE "Wno-" "W" new_flag "${flag}") 479 else() 480 set(new_flag ${flag}) 481 endif() 482 check_cxx_compiler_flag("${new_flag}" ${_FLAG_NAME}) 483 if(${_FLAG_NAME}) 484 string(APPEND ${outputvar} " ${flag}") 485 set(${outputvar} "${${outputvar}}" PARENT_SCOPE) 486 endif() 487endfunction() 488 489function(target_compile_options_if_supported target flag) 490 set(_compile_options "") 491 append_cxx_flag_if_supported("${flag}" _compile_options) 492 if(NOT "${_compile_options}" STREQUAL "") 493 target_compile_options(${target} PRIVATE ${flag}) 494 endif() 495endfunction() 496