Is prepend() a built-in function or macro of CMake? - cmake

I was trying to read the source code of Pytorch, and got stuck with its CMake files.
As you can see in the file:utils.cmake:
##############################################################################
# Macro to update cached options.
macro (caffe2_update_option variable value)
if(CAFFE2_CMAKE_BUILDING_WITH_MAIN_REPO)
get_property(__help_string CACHE ${variable} PROPERTY HELPSTRING)
set(${variable} ${value} CACHE BOOL ${__help_string} FORCE)
else()
set(${variable} ${value})
endif()
endmacro()
##############################################################################
# Add an interface library definition that is dependent on the source.
#
# It's probably easiest to explain why this macro exists, by describing
# what things would look like if we didn't have this macro.
#
# Let's suppose we want to statically link against torch. We've defined
# a library in cmake called torch, and we might think that we just
# target_link_libraries(my-app PUBLIC torch). This will result in a
# linker argument 'libtorch.a' getting passed to the linker.
#
# Unfortunately, this link command is wrong! We have static
# initializers in libtorch.a that would get improperly pruned by
# the default link settings. What we actually need is for you
# to do -Wl,--whole-archive,libtorch.a -Wl,--no-whole-archive to ensure
# that we keep all symbols, even if they are (seemingly) not used.
#
# What caffe2_interface_library does is create an interface library
# that indirectly depends on the real library, but sets up the link
# arguments so that you get all of the extra link settings you need.
# The result is not a "real" library, and so we have to manually
# copy over necessary properties from the original target.
#
# (The discussion above is about static libraries, but a similar
# situation occurs for dynamic libraries: if no symbols are used from
# a dynamic library, it will be pruned unless you are --no-as-needed)
macro(caffe2_interface_library SRC DST)
add_library(${DST} INTERFACE)
add_dependencies(${DST} ${SRC})
# Depending on the nature of the source library as well as the compiler,
# determine the needed compilation flags.
get_target_property(__src_target_type ${SRC} TYPE)
# Depending on the type of the source library, we will set up the
# link command for the specific SRC library.
if (${__src_target_type} STREQUAL "STATIC_LIBRARY")
# In the case of static library, we will need to add whole-static flags.
if(APPLE)
target_link_libraries(
${DST} INTERFACE -Wl,-force_load,$<TARGET_FILE:${SRC}>)
elseif(MSVC)
# In MSVC, we will add whole archive in default.
target_link_libraries(
${DST} INTERFACE -WHOLEARCHIVE:$<TARGET_FILE:${SRC}>)
else()
# Assume everything else is like gcc
target_link_libraries(${DST} INTERFACE
"-Wl,--whole-archive,$<TARGET_FILE:${SRC}> -Wl,--no-whole-archive")
endif()
# Link all interface link libraries of the src target as well.
# For static library, we need to explicitly depend on all the libraries
# that are the dependent library of the source library. Note that we cannot
# use the populated INTERFACE_LINK_LIBRARIES property, because if one of the
# dependent library is not a target, cmake creates a $<LINK_ONLY:src> wrapper
# and then one is not able to find target "src". For more discussions, check
# https://gitlab.kitware.com/cmake/cmake/issues/15415
# https://cmake.org/pipermail/cmake-developers/2013-May/019019.html
# Specifically the following quote
#
# """
# For STATIC libraries we can define that the PUBLIC/PRIVATE/INTERFACE keys
# are ignored for linking and that it always populates both LINK_LIBRARIES
# LINK_INTERFACE_LIBRARIES. Note that for STATIC libraries the
# LINK_LIBRARIES property will not be used for anything except build-order
# dependencies.
# """
target_link_libraries(${DST} INTERFACE
$<TARGET_PROPERTY:${SRC},LINK_LIBRARIES>)
elseif(${__src_target_type} STREQUAL "SHARED_LIBRARY")
if("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")
target_link_libraries(${DST} INTERFACE
"-Wl,--no-as-needed,$<TARGET_FILE:${SRC}> -Wl,--as-needed")
else()
target_link_libraries(${DST} INTERFACE ${SRC})
endif()
# Link all interface link libraries of the src target as well.
# For shared libraries, we can simply depend on the INTERFACE_LINK_LIBRARIES
# property of the target.
target_link_libraries(${DST} INTERFACE
$<TARGET_PROPERTY:${SRC},INTERFACE_LINK_LIBRARIES>)
else()
message(FATAL_ERROR
"You made a CMake build file error: target " ${SRC}
" must be of type either STATIC_LIBRARY or SHARED_LIBRARY. However, "
"I got " ${__src_target_type} ".")
endif()
# For all other interface properties, manually inherit from the source target.
set_target_properties(${DST} PROPERTIES
INTERFACE_COMPILE_DEFINITIONS
$<TARGET_PROPERTY:${SRC},INTERFACE_COMPILE_DEFINITIONS>
INTERFACE_COMPILE_OPTIONS
$<TARGET_PROPERTY:${SRC},INTERFACE_COMPILE_OPTIONS>
INTERFACE_INCLUDE_DIRECTORIES
$<TARGET_PROPERTY:${SRC},INTERFACE_INCLUDE_DIRECTORIES>
INTERFACE_SYSTEM_INCLUDE_DIRECTORIES
$<TARGET_PROPERTY:${SRC},INTERFACE_SYSTEM_INCLUDE_DIRECTORIES>)
endmacro()
##############################################################################
# Creating a Caffe2 binary target with sources specified with relative path.
# Usage:
# caffe2_binary_target(target_name_or_src <src1> [<src2>] [<src3>] ...)
# If only target_name_or_src is specified, this target is build with one single
# source file and the target name is autogen from the filename. Otherwise, the
# target name is given by the first argument and the rest are the source files
# to build the target.
function(caffe2_binary_target target_name_or_src)
# https://cmake.org/cmake/help/latest/command/function.html
# Checking that ARGC is greater than # is the only way to ensure
# that ARGV# was passed to the function as an extra argument.
if (ARGC GREATER 1)
set(__target ${target_name_or_src})
prepend(__srcs "${CMAKE_CURRENT_SOURCE_DIR}/" "${ARGN}")
else()
get_filename_component(__target ${target_name_or_src} NAME_WE)
prepend(__srcs "${CMAKE_CURRENT_SOURCE_DIR}/" "${target_name_or_src}")
endif()
add_executable(${__target} ${__srcs})
target_link_libraries(${__target} ${Caffe2_MAIN_LIBS})
# If we have Caffe2_MODULES defined, we will also link with the modules.
if (DEFINED Caffe2_MODULES)
target_link_libraries(${__target} ${Caffe2_MODULES})
endif()
if (USE_TBB)
target_include_directories(${__target} PUBLIC ${TBB_ROOT_DIR}/include)
endif()
install(TARGETS ${__target} DESTINATION bin)
endfunction()
function(caffe2_hip_binary_target target_name_or_src)
if (ARGC GREATER 1)
set(__target ${target_name_or_src})
prepend(__srcs "${CMAKE_CURRENT_SOURCE_DIR}/" "${ARGN}")
else()
get_filename_component(__target ${target_name_or_src} NAME_WE)
prepend(__srcs "${CMAKE_CURRENT_SOURCE_DIR}/" "${target_name_or_src}")
endif()
caffe2_binary_target(${target_name_or_src})
target_compile_options(${__target} PRIVATE ${HIP_CXX_FLAGS})
target_include_directories(${__target} PRIVATE ${Caffe2_HIP_INCLUDE})
endfunction()
##############################################################################
# Multiplex between loading executables for CUDA versus HIP (AMD Software Stack).
# Usage:
# torch_cuda_based_add_executable(cuda_target)
#
macro(torch_cuda_based_add_executable cuda_target)
IF (USE_ROCM)
hip_add_executable(${cuda_target} ${ARGN})
ELSEIF(USE_CUDA)
cuda_add_executable(${cuda_target} ${ARGN})
ELSE()
ENDIF()
endmacro()
##############################################################################
# Multiplex between adding libraries for CUDA versus HIP (AMD Software Stack).
# Usage:
# torch_cuda_based_add_library(cuda_target)
#
macro(torch_cuda_based_add_library cuda_target)
IF (USE_ROCM)
hip_add_library(${cuda_target} ${ARGN})
ELSEIF(USE_CUDA)
cuda_add_library(${cuda_target} ${ARGN})
ELSE()
ENDIF()
endmacro()
##############################################################################
# Get the NVCC arch flags specified by TORCH_CUDA_ARCH_LIST and CUDA_ARCH_NAME.
# Usage:
# torch_cuda_get_nvcc_gencode_flag(variable_to_store_flags)
#
macro(torch_cuda_get_nvcc_gencode_flag store_var)
# setting nvcc arch flags
if ((NOT EXISTS ${TORCH_CUDA_ARCH_LIST}) AND (DEFINED ENV{TORCH_CUDA_ARCH_LIST}))
message(WARNING
"In the future we will require one to explicitly pass "
"TORCH_CUDA_ARCH_LIST to cmake instead of implicitly setting it as an "
"env variable. This will become a FATAL_ERROR in future version of "
"pytorch.")
set(TORCH_CUDA_ARCH_LIST $ENV{TORCH_CUDA_ARCH_LIST})
endif()
if (EXISTS ${CUDA_ARCH_NAME})
message(WARNING
"CUDA_ARCH_NAME is no longer used. Use TORCH_CUDA_ARCH_LIST instead. "
"Right now, CUDA_ARCH_NAME is ${CUDA_ARCH_NAME} and "
"TORCH_CUDA_ARCH_LIST is ${TORCH_CUDA_ARCH_LIST}.")
set(TORCH_CUDA_ARCH_LIST TORCH_CUDA_ARCH_LIST ${CUDA_ARCH_NAME})
endif()
# Invoke cuda_select_nvcc_arch_flags from proper cmake FindCUDA.
cuda_select_nvcc_arch_flags(${store_var} ${TORCH_CUDA_ARCH_LIST})
endmacro()
##############################################################################
# Add standard compile options.
# Usage:
# torch_compile_options(lib_name)
function(torch_compile_options libname)
set_property(TARGET ${libname} PROPERTY CXX_STANDARD 14)
if (NOT INTERN_BUILD_MOBILE OR NOT BUILD_CAFFE2_MOBILE)
# until they can be unified, keep these lists synced with setup.py
if(MSVC)
if (MSVC_Z7_OVERRIDE)
set(MSVC_DEBINFO_OPTION "/Z7")
else()
set(MSVC_DEBINFO_OPTION "/Zi")
endif()
target_compile_options(${libname} PUBLIC
${MSVC_RUNTIME_LIBRARY_OPTION}
${MSVC_DEBINFO_OPTION}
/EHa
/DNOMINMAX
/wd4267
/wd4251
/wd4522
/wd4522
/wd4838
/wd4305
/wd4244
/wd4190
/wd4101
/wd4996
/wd4275
/bigobj
)
else()
target_compile_options(${libname} PUBLIC
# -std=c++14
-Wall
-Wextra
-Wno-unused-parameter
-Wno-missing-field-initializers
-Wno-write-strings
-Wno-unknown-pragmas
# Clang has an unfixed bug leading to spurious missing braces
# warnings, see https://bugs.llvm.org/show_bug.cgi?id=21629
-Wno-missing-braces
)
if(NOT APPLE)
target_compile_options(${libname} PRIVATE
# Considered to be flaky. See the discussion at
# https://github.com/pytorch/pytorch/pull/9608
-Wno-maybe-uninitialized)
endif()
endif()
if (MSVC)
elseif (WERROR)
target_compile_options(${libname} PRIVATE -Werror -Wno-strict-overflow)
endif()
endif()
if (NOT WIN32 AND NOT USE_ASAN)
# Enable hidden visibility by default to make it easier to debug issues with
# TORCH_API annotations. Hidden visibility with selective default visibility
# behaves close enough to Windows' dllimport/dllexport.
#
# Unfortunately, hidden visibility messes up some ubsan warnings because
# templated classes crossing library boundary get duplicated (but identical)
# definitions. It's easier to just disable it.
target_compile_options(${libname} PRIVATE "-fvisibility=hidden")
endif()
# Use -O2 for release builds (-O3 doesn't improve perf, and -Os results in perf regression)
target_compile_options(${libname} PRIVATE "$<$<OR:$<CONFIG:Release>,$<CONFIG:RelWithDebInfo>>:-O2>")
# ---[ Check if warnings should be errors.
# TODO: Dedupe with WERROR check above
if (WERROR)
target_compile_options(${libname} PRIVATE -Werror)
endif()
endfunction()
##############################################################################
# Set standard target properties.
# Usage:
# torch_set_target_props(lib_name)
function(torch_set_target_props libname)
if(MSVC AND AT_MKL_MT)
set_target_properties(${libname} PROPERTIES LINK_FLAGS_RELEASE "/NODEFAULTLIB:${VCOMP_LIB}")
set_target_properties(${libname} PROPERTIES LINK_FLAGS_DEBUG "/NODEFAULTLIB:${VCOMP_LIB}")
set_target_properties(${libname} PROPERTIES STATIC_LIBRARY_FLAGS "/NODEFAULTLIB:${VCOMP_LIB}")
endif()
endfunction()
In the line 125, it uses a function or macro named
prepend(__srcs "${CMAKE_CURRENT_SOURCE_DIR}/" "${ARGN}").
As you may have noticed that such file is just a CMake module file and without any external import at a glance. Therefore, I guess it might be a built-in function or macro of CMake.
However, I also looked up to the CMake Manual and search for details of prepend() and the search result shows that there is no keyword matching with prepend as built-in functions or macros.
Here is my question: is prepend a legal built-in function or macro in CMake? Even though I can understand what kind of operation this piece of code is trying to do (it tries to prepend ${CMAKE_CURRENT_SOURCE_DIR}/ to every item in ${ARGN}), I just want to further confirm whether such usage is still effective (or recommended) in current latest CMake system.
BTW, I am not an adept in CMake scripting so I am not whether my guess is correct. Longing for your replies and generous help.

This function is not native to CMake. PyTorch defines this custom prepend() function to handle cases as you've stated, such as pre-pending ${CMAKE_CURRENT_SOURCE_DIR}/ to each item in ${ARGN}, and placing the results in a list. This cmake/Utils.cmake file was likely included earlier in the CMake execution to make this function available to use.
Modern CMake does provide a list PREPEND feature with the following signature:
list(PREPEND <list> [<element> ...])
which prepends elements to an existing list. However, this is not equivalent to the PyTorch prepend() function.

Related

AC_SYS_LARGEFILE with CMake

I declare that I have never worked with the autoconf tool.
Now I'm migrating a small project from autoconf to cmake, and I ran into the AC_SYS_LARGEFILE. I have not understood much about AC_SYS_LARGEFILE except that it is a kind of check to understand if for example off_t is 4 or 8 bytes.
Out of curiosity, I would like to understand which are the typical use cases of AC_SYS_LARGEFILE.
Coming to the real question, do I still need AC_SYS_LARGEFILE for CMake?
If yes, does a mechanism already exist in CMake or should I implement it myself?
You are almost correct. The AC_SYS_LARGEFILE autoconf macro enables large-file support (8-byte file offsets) for the program. With some compilers, additional compiler definitions must also be defined to work with large files.
As far as I know, there is not currently any support for this native to CMake. However, there are some CMake projects that have created their own solutions; see these files in the WireShark and the OpenJPEG repositories. The code in each of these appears to be adapted from the Gromacs Project.
For completeness, here is the WireShark CMake file, showing how to test the need for large file support in your program:
# This code was copied from http://www.gromacs.org/
# and its toplevel COPYING file starts with:
#
# GROMACS is free software, distributed under the GNU General Public License
# (GPL) Version 2.
# - Define macro to check large file support
#
# GMX_TEST_LARGE_FILES(VARIABLE)
#
# VARIABLE will be set to true if 64-bit file support is available.
# This macro will also set defines as necessary to enable large file
# support, for instance:
# _LARGE_FILES
# _LARGEFILE_SOURCE
# _FILE_OFFSET_BITS=64
#
# However, it is YOUR job to make sure these defines are set in a cmakedefine so they
# end up in a config.h file that is included in your source if necessary!
MACRO(GMX_TEST_LARGE_FILES VARIABLE)
IF(NOT DEFINED ${VARIABLE})
if(CMAKE_C_COMPILER_ID MATCHES "MSVC")
#
# This is Visual Studio; Visual Studio has supported
# _fseeki64 and _ftelli64 since Visual Studio 2005 / MSVCR80,
# and we require newer versions, so we know we have them.
#
MESSAGE(STATUS "_fseeki64 and _ftelli64 are present")
else(CMAKE_C_COMPILER_ID MATCHES "MSVC")
#
# This is UN*X, or some other Windows compiler.
#
# For UN*X, we do the Large File Support tests, to see
# whether it's present and, if so what we need to define
# to enable it.
#
# XXX - what's the right thing to do on Windows with,
# for example, MinGW?
#
# On most platforms it is probably overkill to first test
# the flags for 64-bit off_t, and then separately fseeko.
# However, in the future we might have 128-bit seek offsets
# to support 128-bit filesystems that allow 128-bit offsets
# (ZFS), so it might be dangerous to indiscriminately set
# e.g. _FILE_OFFSET_BITS=64.
#
MESSAGE(STATUS "Checking for 64-bit off_t")
# First check without any special flags
TRY_COMPILE(FILE64_OK "${CMAKE_BINARY_DIR}"
"${CMAKE_SOURCE_DIR}/cmake/TestFileOffsetBits.c")
if(FILE64_OK)
MESSAGE(STATUS "64-bit off_t is present with no special flags")
endif(FILE64_OK)
if(NOT FILE64_OK)
# Test with _FILE_OFFSET_BITS=64
TRY_COMPILE(FILE64_OK "${CMAKE_BINARY_DIR}"
"${CMAKE_SOURCE_DIR}/cmake/TestFileOffsetBits.c"
COMPILE_DEFINITIONS "-D_FILE_OFFSET_BITS=64" )
if(FILE64_OK)
MESSAGE(STATUS "64-bit off_t is present with _FILE_OFFSET_BITS=64")
set(_FILE_OFFSET_BITS 64 CACHE INTERNAL "64-bit off_t requires _FILE_OFFSET_BITS=64")
endif(FILE64_OK)
endif(NOT FILE64_OK)
if(NOT FILE64_OK)
# Test with _LARGE_FILES
TRY_COMPILE(FILE64_OK "${CMAKE_BINARY_DIR}"
"${CMAKE_SOURCE_DIR}/cmake/TestFileOffsetBits.c"
COMPILE_DEFINITIONS "-D_LARGE_FILES" )
if(FILE64_OK)
MESSAGE(STATUS "64-bit off_t is present with _LARGE_FILES")
set(_LARGE_FILES 1 CACHE INTERNAL "64-bit off_t requires _LARGE_FILES")
endif(FILE64_OK)
endif(NOT FILE64_OK)
if(NOT FILE64_OK)
# Test with _LARGEFILE_SOURCE
TRY_COMPILE(FILE64_OK "${CMAKE_BINARY_DIR}"
"${CMAKE_SOURCE_DIR}/cmake/TestFileOffsetBits.c"
COMPILE_DEFINITIONS "-D_LARGEFILE_SOURCE" )
if(FILE64_OK)
MESSAGE(STATUS "64-bit off_t is present with _LARGEFILE_SOURCE")
set(_LARGEFILE_SOURCE 1 CACHE INTERNAL "64-bit off_t requires _LARGEFILE_SOURCE")
endif(FILE64_OK)
endif(NOT FILE64_OK)
if(NOT FILE64_OK)
MESSAGE(STATUS "64-bit file offset support not available")
else(NOT FILE64_OK)
# Set the flags we might have determined to be required above
configure_file("${CMAKE_SOURCE_DIR}/cmake/TestLargeFiles.c.cmakein"
"${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestLargeFiles.c")
#
# If this is Windows, assume we have _fseeki64/_ftelli64
# available. If this is UN*X, check for fseeko/ftello.
#
if(NOT WIN32)
MESSAGE(STATUS "Checking for fseeko/ftello")
# Test if ftello/fseeko are available
TRY_COMPILE(FSEEKO_COMPILE_OK "${CMAKE_BINARY_DIR}"
"${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestLargeFiles.c")
if(FSEEKO_COMPILE_OK)
MESSAGE(STATUS "Checking for fseeko/ftello - present")
endif(FSEEKO_COMPILE_OK)
if(NOT FSEEKO_COMPILE_OK)
# glibc 2.2 neds _LARGEFILE_SOURCE for fseeko (but not 64-bit off_t...)
TRY_COMPILE(FSEEKO_COMPILE_OK "${CMAKE_BINARY_DIR}"
"${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestLargeFiles.c"
COMPILE_DEFINITIONS "-D_LARGEFILE_SOURCE" )
if(FSEEKO_COMPILE_OK)
MESSAGE(STATUS "Checking for fseeko/ftello - present with _LARGEFILE_SOURCE")
set(_LARGEFILE_SOURCE 1 CACHE INTERNAL "64-bit fseeko requires _LARGEFILE_SOURCE")
endif(FSEEKO_COMPILE_OK)
endif(NOT FSEEKO_COMPILE_OK)
if(FSEEKO_COMPILE_OK)
SET(${VARIABLE} 1 CACHE INTERNAL "Result of test for large file support" FORCE)
set(HAVE_FSEEKO 1 CACHE INTERNAL "64-bit fseeko is available" FORCE)
else(FSEEKO_COMPILE_OK)
MESSAGE(STATUS "Checking for fseeko/ftello - not found")
SET(${VARIABLE} 0 CACHE INTERNAL "Result of test for large file support" FORCE)
endif(FSEEKO_COMPILE_OK)
endif(NOT WIN32)
endif(NOT FILE64_OK)
endif(CMAKE_C_COMPILER_ID MATCHES "MSVC")
ENDIF(NOT DEFINED ${VARIABLE})
ENDMACRO(GMX_TEST_LARGE_FILES VARIABLE)

CMake: target_link_libraries include as SYSTEM to suppress compiler warnings

To suppress compiler warnings that originate from libraries I use in my application, I manually include their directories with target_include_directories(myapp SYSTEM ...) as system libraries before adding them with target_link_libraries like so:
add_executable(myapp myapp.cpp)
target_include_directories(myapp SYSTEM
PRIVATE "extern/lib/include"
)
target_link_libraries(myapp lib::lib)
However, that kind of feels hacky and will also break if the developers of lib decide to change the include path. This wouldn't be a problem if using only target_link_library but then, of course, they are included via -I and again I would get compiler warnings coming from this include.
Is there any more elegant and fail-safe way of doing this? It would be great if target_link_libraries had a SYSTEM option to tell cmake to include it as a system library.
I defined a function to handle this for me:
function(target_link_libraries_system target)
set(libs ${ARGN})
foreach(lib ${libs})
get_target_property(lib_include_dirs ${lib} INTERFACE_INCLUDE_DIRECTORIES)
target_include_directories(${target} SYSTEM PRIVATE ${lib_include_dirs})
target_link_libraries(${target} ${lib})
endforeach(lib)
endfunction(target_link_libraries_system)
I can now call target_link_libraries_system(myapp lib::lib) and the include directories are read from the target's properties.
This can be extended to optionally specify the PUBLIC|PRIVATE|INTERFACE scope:
function(target_link_libraries_system target)
set(options PRIVATE PUBLIC INTERFACE)
cmake_parse_arguments(TLLS "${options}" "" "" ${ARGN})
foreach(op ${options})
if(TLLS_${op})
set(scope ${op})
endif()
endforeach(op)
set(libs ${TLLS_UNPARSED_ARGUMENTS})
foreach(lib ${libs})
get_target_property(lib_include_dirs ${lib} INTERFACE_INCLUDE_DIRECTORIES)
if(lib_include_dirs)
if(scope)
target_include_directories(${target} SYSTEM ${scope} ${lib_include_dirs})
else()
target_include_directories(${target} SYSTEM PRIVATE ${lib_include_dirs})
endif()
else()
message("Warning: ${lib} doesn't set INTERFACE_INCLUDE_DIRECTORIES. No include_directories set.")
endif()
if(scope)
target_link_libraries(${target} ${scope} ${lib})
else()
target_link_libraries(${target} ${lib})
endif()
endforeach()
endfunction(target_link_libraries_system)
This extended version will also print a warning if a library didn't set its INTERFACE_INCLUDE_DIRECTORIES property.
I modified Sebastian's solution to include the scope.
function(target_link_libraries_system target scope)
set(libs ${ARGN})
foreach(lib ${libs})
get_target_property(lib_include_dirs ${lib} INTERFACE_INCLUDE_DIRECTORIES)
target_include_directories(${target} SYSTEM ${scope} ${lib_include_dirs})
target_link_libraries(${target} ${scope} ${lib})
endforeach(lib)
endfunction(target_link_libraries_system)
This was asked in the CMake discourse and #ben.boeckel (CMake developer) answered:
IMPORTED targets should already have their include directories
treated as SYSTEM though. There is the NO_SYSTEM_FROM_IMPORTED target
property to disable it.
To start with, you haven't specified whether your target that you're linking to is IMPORTED or not. I'm assuming that it is IMPORTED because include directories for IMPORTED targets are SYSTEM by default. Note: This behaviour, can be disabled. Pre-CMake v3.25, one would use one of NO_SYSTEM_FROM_IMPORTED or IMPORTED_NO_SYSTEM. For CMake 3.25 and later, one would modify the SYSTEM property of the target.
That typically just leaves the cases of targets added via add_subdirectory, which includes those added by FetchContent in its non-find_package mode. In that case, you can see the FetchContent Q&A here, and the add_subdirectory Q&A here. In summary, for pre-CMake v3.25, use a workaround in which you copy/move the INTERFACE_INCLUDE_DIRECTORIES target property to the INTERFACE_SYSTEM_INCLUDE_DIRECTORIES target property, and for CMake v3.25 or later, you can modify the SYSTEM target property, or the SYSTEM directory property, or use the SYSTEM argument of add_subdirectory/FetchContent_Declare.
Possible gotcha: If you are like me, and start every project by enabling all warnings in the toplevel directory…
# Warning level
add_compile_options(
-Wall -Wextra -Wpedantic
-Werror=switch
-Werror=return-type
-Werror=uninitialized
-Werror=format-security
-Werror=reorder
-Werror=delete-non-virtual-dtor
$<$<CONFIG:Debug>:-Werror>
)
… then, this obviously gets inherited by all subprojects (like git submodules, or what have you).
If this is the case, the solution is simple – be specific: Do it in a subdirectory, and/or use target_compile_options, for good measure:
target_compile_options(myTarget PRIVATE
...
)

CMake with Mingw "No rule to make target 'C:/Windows/System32/SDL2.dll'"

I'm getting this strange error while trying to build a SDL demo project in CLion.
"C:\Program Files (x86)\JetBrains\CLion 2016.1.3\bin\cmake\bin\cmake.exe" --build C:\Users\****\.CLion2016.1\system\cmake\generated\teste-62957c9d\62957c9d\Debug --target teste -- -j 4
mingw32-make.exe[3]: *** No rule to make target 'C:/Windows/System32/SDL2.dll', needed by 'teste.exe'. Stop.
CMakeFiles\Makefile2:66: recipe for target 'CMakeFiles/teste.dir/all' failed
mingw32-make.exe[2]: *** [CMakeFiles/teste.dir/all] Error 2
mingw32-make.exe[1]: *** [CMakeFiles/teste.dir/rule] Error 2
mingw32-make.exe: *** [teste] Error 2
CMakeFiles\Makefile2:78: recipe for target 'CMakeFiles/teste.dir/rule' failed
Makefile:117: recipe for target 'teste' failed
I've tried googling but I didn't find any answer or similar errors relating SDL and mingw. What could be the problem?
I don't know if its of any help, but here is my CMakeLists file:
cmake_minimum_required(VERSION 3.5)
project(teste)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
find_package(SDL2 REQUIRED)
include_directories(${SDL2_INCLUDE_DIR})
set(SOURCE_FILES main.cpp)
add_executable(teste ${SOURCE_FILES})
target_link_libraries(teste ${SDL2_LIBRARY})
and an auxiliary .cmake file I use to find SDL2 in my system:
# Locate SDL2 library
# This module defines
# SDL2_LIBRARY, the name of the library to link against
# SDL2_FOUND, if false, do not try to link to SDL2
# SDL2_INCLUDE_DIR, where to find SDL.h
#
# This module responds to the the flag:
# SDL2_BUILDING_LIBRARY
# If this is defined, then no SDL2_main will be linked in because
# only applications need main().
# Otherwise, it is assumed you are building an application and this
# module will attempt to locate and set the the proper link flags
# as part of the returned SDL2_LIBRARY variable.
#
# Don't forget to include SDL2main.h and SDL2main.m your project for the
# OS X framework based version. (Other versions link to -lSDL2main which
# this module will try to find on your behalf.) Also for OS X, this
# module will automatically add the -framework Cocoa on your behalf.
#
#
# Additional Note: If you see an empty SDL2_LIBRARY_TEMP in your configuration
# and no SDL2_LIBRARY, it means CMake did not find your SDL2 library
# (SDL2.dll, libsdl2.so, SDL2.framework, etc).
# Set SDL2_LIBRARY_TEMP to point to your SDL2 library, and configure again.
# Similarly, if you see an empty SDL2MAIN_LIBRARY, you should set this value
# as appropriate. These values are used to generate the final SDL2_LIBRARY
# variable, but when these values are unset, SDL2_LIBRARY does not get created.
#
#
# $SDL2 is an environment variable that would
# correspond to the ./configure --prefix=$SDL2
# used in building SDL2.
# l.e.galup 9-20-02
#
# Modified by Eric Wing.
# Added code to assist with automated building by using environmental variables
# and providing a more controlled/consistent search behavior.
# Added new modifications to recognize OS X frameworks and
# additional Unix paths (FreeBSD, etc).
# Also corrected the header search path to follow "proper" SDL2 guidelines.
# Added a search for SDL2main which is needed by some platforms.
# Added a search for threads which is needed by some platforms.
# Added needed compile switches for MinGW.
#
# On OSX, this will prefer the Framework version (if found) over others.
# People will have to manually change the cache values of
# SDL2_LIBRARY to override this selection or set the CMake environment
# CMAKE_INCLUDE_PATH to modify the search paths.
#
# Note that the header path has changed from SDL2/SDL.h to just SDL.h
# This needed to change because "proper" SDL2 convention
# is #include "SDL.h", not <SDL2/SDL.h>. This is done for portability
# reasons because not all systems place things in SDL2/ (see FreeBSD).
#
# Ported by Johnny Patterson. This is a literal port for SDL2 of the FindSDL.cmake
# module with the minor edit of changing "SDL" to "SDL2" where necessary. This
# was not created for redistribution, and exists temporarily pending official
# SDL2 CMake modules.
#
# Note that on windows this will only search for the 32bit libraries, to search
# for 64bit change x86/i686-w64 to x64/x86_64-w64
#=============================================================================
# Copyright 2003-2009 Kitware, Inc.
#
# CMake - Cross Platform Makefile Generator
# Copyright 2000-2014 Kitware, Inc.
# Copyright 2000-2011 Insight Software Consortium
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# * Neither the names of Kitware, Inc., the Insight Software Consortium,
# nor the names of their contributors may be used to endorse or promote
# products derived from this software without specific prior written
# permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distribute this file outside of CMake, substitute the full
# License text for the above reference.)
FIND_PATH(SDL2_INCLUDE_DIR SDL.h
HINTS
${SDL2}
$ENV{SDL2}
PATH_SUFFIXES include/SDL2 include SDL2
i686-w64-mingw32/include/SDL2
x86_64-w64-mingw32/include/SDL2
PATHS
~/Library/Frameworks
/Library/Frameworks
/usr/local/include/SDL2
/usr/include/SDL2
/sw # Fink
/opt/local # DarwinPorts
/opt/csw # Blastwave
/opt
)
# Lookup the 64 bit libs on x64
IF(CMAKE_SIZEOF_VOID_P EQUAL 8)
FIND_LIBRARY(SDL2_LIBRARY_TEMP SDL2
HINTS
${SDL2}
$ENV{SDL2}
PATH_SUFFIXES lib64 lib
lib/x64
x86_64-w64-mingw32/lib
PATHS
/sw
/opt/local
/opt/csw
/opt
)
# On 32bit build find the 32bit libs
ELSE(CMAKE_SIZEOF_VOID_P EQUAL 8)
FIND_LIBRARY(SDL2_LIBRARY_TEMP SDL2
HINTS
${SDL2}
$ENV{SDL2}
PATH_SUFFIXES lib
lib/x86
i686-w64-mingw32/lib
PATHS
/sw
/opt/local
/opt/csw
/opt
)
ENDIF(CMAKE_SIZEOF_VOID_P EQUAL 8)
IF(NOT SDL2_BUILDING_LIBRARY)
IF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework")
# Non-OS X framework versions expect you to also dynamically link to
# SDL2main. This is mainly for Windows and OS X. Other (Unix) platforms
# seem to provide SDL2main for compatibility even though they don't
# necessarily need it.
# Lookup the 64 bit libs on x64
IF(CMAKE_SIZEOF_VOID_P EQUAL 8)
FIND_LIBRARY(SDL2MAIN_LIBRARY
NAMES SDL2main
HINTS
${SDL2}
$ENV{SDL2}
PATH_SUFFIXES lib64 lib
lib/x64
x86_64-w64-mingw32/lib
PATHS
/sw
/opt/local
/opt/csw
/opt
)
# On 32bit build find the 32bit libs
ELSE(CMAKE_SIZEOF_VOID_P EQUAL 8)
FIND_LIBRARY(SDL2MAIN_LIBRARY
NAMES SDL2main
HINTS
${SDL2}
$ENV{SDL2}
PATH_SUFFIXES lib
lib/x86
i686-w64-mingw32/lib
PATHS
/sw
/opt/local
/opt/csw
/opt
)
ENDIF(CMAKE_SIZEOF_VOID_P EQUAL 8)
ENDIF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework")
ENDIF(NOT SDL2_BUILDING_LIBRARY)
# SDL2 may require threads on your system.
# The Apple build may not need an explicit flag because one of the
# frameworks may already provide it.
# But for non-OSX systems, I will use the CMake Threads package.
IF(NOT APPLE)
FIND_PACKAGE(Threads)
ENDIF(NOT APPLE)
# MinGW needs an additional library, mwindows
# It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -lmwindows
# (Actually on second look, I think it only needs one of the m* libraries.)
IF(MINGW)
SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW")
ENDIF(MINGW)
SET(SDL2_FOUND "NO")
IF(SDL2_LIBRARY_TEMP)
# For SDL2main
IF(NOT SDL2_BUILDING_LIBRARY)
IF(SDL2MAIN_LIBRARY)
SET(SDL2_LIBRARY_TEMP ${SDL2MAIN_LIBRARY} ${SDL2_LIBRARY_TEMP})
ENDIF(SDL2MAIN_LIBRARY)
ENDIF(NOT SDL2_BUILDING_LIBRARY)
# For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa.
# CMake doesn't display the -framework Cocoa string in the UI even
# though it actually is there if I modify a pre-used variable.
# I think it has something to do with the CACHE STRING.
# So I use a temporary variable until the end so I can set the
# "real" variable in one-shot.
IF(APPLE)
SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} "-framework Cocoa")
ENDIF(APPLE)
# For threads, as mentioned Apple doesn't need this.
# In fact, there seems to be a problem if I used the Threads package
# and try using this line, so I'm just skipping it entirely for OS X.
IF(NOT APPLE)
SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT})
ENDIF(NOT APPLE)
# For MinGW library
IF(MINGW)
SET(SDL2_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL2_LIBRARY_TEMP})
ENDIF(MINGW)
# Set the final string here so the GUI reflects the final state.
SET(SDL2_LIBRARY ${SDL2_LIBRARY_TEMP} CACHE STRING "Where the SDL2 Library can be found")
# Set the temp variable to INTERNAL so it is not seen in the CMake GUI
SET(SDL2_LIBRARY_TEMP "${SDL2_LIBRARY_TEMP}" CACHE INTERNAL "")
SET(SDL2_FOUND "YES")
ENDIF(SDL2_LIBRARY_TEMP)
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR)
Update
After a reconfiguration of the project, as #Tsyvarev pointed out, the error was gone and now I have this undefined reference to SDL_main errors:
C:/SDL2/i686-w64-mingw32/lib/libSDL2main.a(SDL_windows_main.o): In function `main_utf8':
/Users/slouken/release/SDL/SDL2-2.0.4-source/foo-x86/../src/main/windows/SDL_windows_main.c:126: undefined reference to `SDL_main'
/Users/slouken/release/SDL/SDL2-2.0.4-source/foo-x86/../src/main/windows/SDL_windows_main.c:126: undefined reference to `SDL_main'
/Users/slouken/release/SDL/SDL2-2.0.4-source/foo-x86/../src/main/windows/SDL_windows_main.c:126: undefined reference to `SDL_main'
collect2.exe: error: ld returned 1 exit status
mingw32-make.exe[3]: *** [SDLDemo.exe] Error 1
mingw32-make.exe[2]: *** [CMakeFiles/SDLDemo.dir/all] Error 2
CMakeFiles\SDLDemo.dir\build.make:98: recipe for target 'SDLDemo.exe' failed
CMakeFiles\Makefile2:66: recipe for target 'CMakeFiles/SDLDemo.dir/all' failed
mingw32-make.exe[1]: *** [CMakeFiles/SDLDemo.dir/rule] Error 2
mingw32-make.exe: *** [SDLDemo] Error 2
CMakeFiles\Makefile2:78: recipe for target 'CMakeFiles/SDLDemo.dir/rule' failed
Makefile:117: recipe for target 'SDLDemo' failed
Update 2:
A simple change from int main(){...} to int main(int argc, char *argv[]){...} in my main source code file solved the problem..

How to set compile flags for external INTERFACE_SOURCES in CMake?

I use an external package in cmake, that uses INTERFACE_SOURCES. This means when I link the imported library to my target, the interface source files are automatically added to my target. When I compile my target, those external files are compiled too.
This causes a problem for me, because the external files cause compile warnings. I want to remove the warnings by setting a lower warning level when compiling the external files. But I do not know how to do this.
This is what I got so far.
# reduce the warning level for some files over which we have no control.
macro( remove_warning_flags_for_some_external_files myTarget )
# blacklist of files that throw warnings
set( blackListedExternalFiles
static_qt_plugins.cpp
)
get_target_property( linkedLibraries ${myTarget} LINK_LIBRARIES )
foreach(library ${linkedLibraries})
get_property( sources TARGET ${library} PROPERTY INTERFACE_SOURCES )
foreach(source ${sources})
get_filename_component(shortName ${source} NAME)
if( ${shortName} IN_LIST blackListedExternalFiles)
# everything works until here
# does not work
get_source_file_property( flags1 ${source} COMPILE_FLAGS)
# does not work
get_property(flags2 SOURCE ${source} PROPERTY COMPILE_FLAGS)
# exchange flags in list, this I can do
# set flags to source file, do not know how to
endif()
endforeach()
endforeach()
endmacro()
This is what this should do
Go through all linked libraries and get the external INTERFACE_SOURCES source files.
Check for each external source file if it appears in the black-list. If so, change its compile flags to a lower level.
The problem I have is with getting and setting the compile flags for those INTERFACE_SOURCES. The get_source_file_property() and get_property() calls return nothing.
How can I get and set the flags for these files that seem to not belong to my target, but are compiled at the same time?
The get_source_file_property() and get_property() calls return nothing
COMPILE_FLAGS property of source file is not modified by "target" commands like target_compile_options. Final set of flags is a mix of global + target specific + source specific. As far as I understand there is no way to turn off "inheriting" of global or target flags.
As a workaround you can disable warning by adding -w option:
get_source_file_property(flags "${external_source}" COMPILE_FLAGS)
if(NOT flags)
set(flags "") # convert "NOTFOUND" to "" if needed
endif()
set_source_files_properties(
"${external_source}"
PROPERTIES
COMPILE_FLAGS "-w ${flags}"
)
Just for your information: How to set warning level in CMake?.
For the record: originally from issue #408.

Simple CMakeLists.txt that reflects typical directory structure (/src, /inc, /bin subdirectories)

I am struggling to make a CMakeList.txt file to reflect a simple, typical makefile. The original is here http://pastebin.com/S9Czr1pt .
I tried many things (like SET(SOURCE ... and SET(HEADERS... ) to add /src, /lib and /third-party//include ), but I have no luck.
Can anyone either help me out or point to a tutorial that does this thing?
This is just an out of the blue skeleton - please see the CMake documentation for details of each function:
cmake_minimum_required(VERSION 2.6)
# Project name, can be used as target name too
project(Example)
# Search all .cpp files within src - recursive!
# You can add all source files by hand here too
file(GLOB_RECURSE SRCS "src/*.cpp")
# Add include path (you can also add it's sub directories
include_directories("include")
# Search for packages -- PLEASE NOTE: many libraries provide alternative's to this
# which often provide more functionality
find_package(PkgConfig REQUIRED)
# TODO: add proper pkg modul search info's and change the variable's name
pkg_search_module(PACKAGE_NO1 ...)
# Show some messages (optional)
if( (PACKAGE_NO1 )
include_directories(${(PACKAGE_NO1_INCLUDE_DIRS})
message(STATUS "Using OpenSSL ${(PACKAGE_NO1_VERSION}")
else()
# do error handling
endif()
# Add compiler flags
add_definitions(-std=c++11 -Wall) # -g O3 etc are added according to Release / Debug compilation
# Build a executable target (1st param: Target name; 2nd: source files)
add_executable(${PROJECT_NAME} ${SRCS})