# CxxProject 1.0.0 # ---------------- if (NOT __CXX_INCLUDED) set(__CXX_INCLUDED TRUE) include(CheckCXXCompilerFlag) # --------------------------------------------------------------------------- # C++ COMPILER SUPPORT: # # * cxx_detect_cflags(out, ...) # * cxx_detect_standard(out) # --------------------------------------------------------------------------- function(cxx_detect_cflags out) set(out_array ${${out}}) foreach(flag ${ARGN}) string(REGEX REPLACE "[-=:;/.]" "_" flag_signature "${flag}") check_cxx_compiler_flag(${flag} "__CxxFlag_${flag_signature}") if(${__CxxFlag_${flag_signature}}) list(APPEND out_array "${flag}") endif() endforeach() set(${out} "${out_array}" PARENT_SCOPE) endfunction() function(cxx_detect_standard out) set(out_array) cxx_detect_cflags(out_array "-std=c++14" "-std=c++11" "-std=c++0x") # Keep only the first flag detected, which keeps the highest version supported. if(out_array) list(GET out_array 0 out_array) endif() set(out_array ${${out}} ${out_array}) set(${out} "${out_array}" PARENT_SCOPE) endfunction() function(cxx_print_cflags cflags_any cflags_dbg cflags_rel) foreach(flag ${cflags_any}) message(" ${flag}") endforeach() foreach(flag ${cflags_dbg}) message(" ${flag} [DEBUG]") endforeach() foreach(flag ${cflags_rel}) message(" ${flag} [RELEASE]") endforeach() endfunction() # ----------------------------------------------------------------------------- # This part detects the c++ compiler and fills basic CXX_... variables to make # integration with that compiler easier. It provides the most common flags in # a cross-platform way. # ----------------------------------------------------------------------------- set(CXX_DEFINE "-D") # Define a preprocessor macro: "${CXX_DEFINE}VAR=1" set(CXX_INCLUDE "-I") # Define an include directory: "${CXX_INCLUDE}PATH" set(CXX_CFLAGS_SSE "") # Compiler flags to build a file that uses SSE intrinsics. set(CXX_CFLAGS_SSE2 "") # Compiler flags to build a file that uses SSE2 intrinsics. set(CXX_CFLAGS_SSE3 "") # Compiler flags to build a file that uses SSE3 intrinsics. set(CXX_CFLAGS_SSSE3 "") # Compiler flags to build a file that uses SSSE3 intrinsics. set(CXX_CFLAGS_SSE4_1 "") # Compiler flags to build a file that uses SSE4.1 intrinsics. set(CXX_CFLAGS_SSE4_2 "") # Compiler flags to build a file that uses SSE4.2 intrinsics. set(CXX_CFLAGS_AVX "") # Compiler flags to build a file that uses AVX intrinsics. set(CXX_CFLAGS_AVX2 "") # Compiler flags to build a file that uses AVX2 intrinsics. if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") set(CXX_DEFINE "/D") set(CXX_INCLUDE "/I") if(CMAKE_CL_64) # 64-bit MSVC compiler doesn't like /arch:SSE[2] as it's implicit. list(APPEND CXX_CFLAGS_SSE "${CXX_DEFINE}__SSE__=1") list(APPEND CXX_CFLAGS_SSE2 "${CXX_DEFINE}__SSE__=1;${CXX_DEFINE}__SSE2__=1") else() cxx_detect_cflags(CXX_CFLAGS_SSE "/arch:SSE") if(CXX_CFLAGS_SSE) list(APPEND CXX_CFLAGS_SSE "${CXX_DEFINE}__SSE__=1") endif() cxx_detect_cflags(CXX_CFLAGS_SSE2 "/arch:SSE2") if(CXX_CFLAGS_SSE2) list(APPEND CXX_CFLAGS_SSE2 "${CXX_DEFINE}__SSE__=1;${CXX_DEFINE}__SSE2__=1") endif() endif() # MSVC doesn't provide any preprocessor definitions to detect SSE3+, # these unify MSVC with definitions defined by Clang|GCC|Intel. if(CXX_CFLAGS_SSE2) list(APPEND CXX_CFLAGS_SSE3 "${CXX_CFLAGS_SSE2};${CXX_DEFINE}__SSE3__=1") list(APPEND CXX_CFLAGS_SSSE3 "${CXX_CFLAGS_SSE3};${CXX_DEFINE}__SSSE3__=1") list(APPEND CXX_CFLAGS_SSE4_1 "${CXX_CFLAGS_SSSE3};${CXX_DEFINE}__SSE4_1__=1") list(APPEND CXX_CFLAGS_SSE4_2 "${CXX_CFLAGS_SSE4_1};${CXX_DEFINE}__SSE4_2__=1") endif() # When using AVX and AVX2 MSVC does define '__AVX__' and '__AVX2__', respectively. cxx_detect_cflags(CXX_CFLAGS_AVX "/arch:AVX") cxx_detect_cflags(CXX_CFLAGS_AVX2 "/arch:AVX2") if(CXX_CFLAGS_AVX) list(APPEND CXX_CFLAGS_AVX "${CXX_DEFINE}__SSE__=1;${CXX_DEFINE}__SSE2__=1;${CXX_DEFINE}__SSE3__=1;${CXX_DEFINE}__SSSE3__=1;${CXX_DEFINE}__SSE4_1__=1;${CXX_DEFINE}__SSE4_2__=1") endif() if(CXX_CFLAGS_AVX2) list(APPEND CXX_CFLAGS_AVX2 "${CXX_DEFINE}__SSE__=1;${CXX_DEFINE}__SSE2__=1;${CXX_DEFINE}__SSE3__=1;${CXX_DEFINE}__SSSE3__=1;${CXX_DEFINE}__SSE4_1__=1;${CXX_DEFINE}__SSE4_2__=1") endif() elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel" AND WIN32) # Intel on Windows uses CL syntax. set(CXX_DEFINE "/D") set(CXX_INCLUDE "/I") # Intel deprecated /arch:SSE, so it's implicit. In contrast to MSVC, Intel # also provides /arch:SSE3+ options and uses the same definitions as GCC # and Clang, so no magic needed here. cxx_detect_cflags(CXX_CFLAGS_SSE2 "/arch:SSE2") cxx_detect_cflags(CXX_CFLAGS_SSE3 "/arch:SSE3") cxx_detect_cflags(CXX_CFLAGS_SSSE3 "/arch:SSSE3") cxx_detect_cflags(CXX_CFLAGS_SSE4_1 "/arch:SSE4.1") cxx_detect_cflags(CXX_CFLAGS_SSE4_2 "/arch:SSE4.2") cxx_detect_cflags(CXX_CFLAGS_AVX "/arch:AVX") cxx_detect_cflags(CXX_CFLAGS_AVX2 "/arch:AVX2") else() cxx_detect_cflags(CXX_CFLAGS_SSE "-msse") cxx_detect_cflags(CXX_CFLAGS_SSE2 "-msse2") cxx_detect_cflags(CXX_CFLAGS_SSE3 "-msse3") cxx_detect_cflags(CXX_CFLAGS_SSSE3 "-mssse3") cxx_detect_cflags(CXX_CFLAGS_SSE4_1 "-msse4.1") cxx_detect_cflags(CXX_CFLAGS_SSE4_2 "-msse4.2") cxx_detect_cflags(CXX_CFLAGS_AVX "-mavx") cxx_detect_cflags(CXX_CFLAGS_AVX2 "-mavx2") endif() # --------------------------------------------------------------------------- # Function # cxx_project(product) # # Create a master project or embed other project in a master project. # --------------------------------------------------------------------------- function(cxx_project product) string(TOUPPER "${product}" PRODUCT) set(MODE_EMBED ${${PRODUCT}_EMBED}) set(MODE_STATIC ${${PRODUCT}_STATIC}) # EMBED implies STATIC. if(MODE_EMBED) set(MODE_STATIC TRUE) set(${PRODUCT}_STATIC TRUE PARENT_SCOPE) endif() # Deduce source and include directories. By default CxxProject assumes that # both source and include files are located at './src'. set(SOURCE_DIR "${${PRODUCT}_SOURCE_DIR}") set(INCLUDE_DIR "${${PRODUCT}_INCLUDE_DIR}") if(NOT SOURCE_DIR) set(SOURCE_DIR "${${PRODUCT}_DIR}/src") set(${PRODUCT}_SOURCE_DIR "${SOURCE_DIR}" PARENT_SCOPE) endif() if(NOT INCLUDE_DIR) set(INCLUDE_DIR "${SOURCE_DIR}") set(${PRODUCT}_INCLUDE_DIR "${INCLUDE_DIR}" PARENT_SCOPE) endif() set(DEPS "") # Dependencies (list of libraries) for the linker. set(LIBS "") # Dependencies with project included, for consumers. set(CFLAGS "") # Public compiler flags. set(PRIVATE_CFLAGS "") # Private compiler flags independent of build type. set(PRIVATE_CFLAGS_DBG "") # Private compiler flags used by debug builds. set(PRIVATE_CFLAGS_REL "") # Private compiler flags used by release builds. set(PRIVATE_LFLAGS "") # Private linker flags. if(MODE_EMBED) list(APPEND CFLAGS "${CXX_DEFINE}${PRODUCT}_EMBED") list(APPEND PRIVATE_CFLAGS "${CXX_DEFINE}${PRODUCT}_EMBED") endif() if(MODE_STATIC) list(APPEND CFLAGS "${CXX_DEFINE}${PRODUCT}_STATIC") list(APPEND PRIVATE_CFLAGS "${CXX_DEFINE}${PRODUCT}_STATIC") endif() # PUBLIC properties - usable by third parties. set(${PRODUCT}_DEPS "${DEPS}" PARENT_SCOPE) set(${PRODUCT}_LIBS "${LIBS}" PARENT_SCOPE) set(${PRODUCT}_CFLAGS "${CFLAGS}" PARENT_SCOPE) # PRIVATE properties - only used during build. set(${PRODUCT}_PRIVATE_CFLAGS "${PRIVATE_CFLAGS}" PARENT_SCOPE) set(${PRODUCT}_PRIVATE_CFLAGS_DBG "${PRIVATE_CFLAGS_DBG}" PARENT_SCOPE) set(${PRODUCT}_PRIVATE_CFLAGS_REL "${PRIVATE_CFLAGS_REL}" PARENT_SCOPE) set(${PRODUCT}_PRIVATE_LFLAGS "${PRIVATE_LFLAGS}" PARENT_SCOPE) endfunction() function(cxx_project_info product) string(TOUPPER "${product}" PRODUCT) set(BUILD_MODE "") set(BUILD_TEST "") if(${PRODUCT}_EMBED) set(BUILD_MODE "Embed") elseif(${PRODUCT}_STATIC) set(BUILD_MODE "Static") else() set(BUILD_MODE "Shared") endif() if(${PRODUCT}_BUILD_TEST) set(BUILD_TEST "On") else() set(BUILD_TEST "Off") endif() message("-- [${product}]") message(" BuildMode=${BUILD_MODE}") message(" BuildTest=${BUILD_TEST}") message(" ${PRODUCT}_DIR=${${PRODUCT}_DIR}") message(" ${PRODUCT}_DEPS=${${PRODUCT}_DEPS}") message(" ${PRODUCT}_LIBS=${${PRODUCT}_LIBS}") message(" ${PRODUCT}_CFLAGS=${${PRODUCT}_CFLAGS}") message(" ${PRODUCT}_SOURCE_DIR=${${PRODUCT}_SOURCE_DIR}") message(" ${PRODUCT}_INCLUDE_DIR=${${PRODUCT}_INCLUDE_DIR}") message(" ${PRODUCT}_PRIVATE_CFLAGS=") cxx_print_cflags( "${${PRODUCT}_PRIVATE_CFLAGS}" "${${PRODUCT}_PRIVATE_CFLAGS_DBG}" "${${PRODUCT}_PRIVATE_CFLAGS_REL}") endfunction() function(cxx_add_source product out src_dir) string(TOUPPER "${product}" PRODUCT) set(src_path "${${PRODUCT}_SOURCE_DIR}/${src_dir}") set(src_array) foreach(file ${ARGN}) set(src_file "${src_path}/${file}") set(src_cflags "") if(file MATCHES "\\.c|\\.cc|\\.cxx|\\.cpp|\\.m|\\.mm") if(file MATCHES "_sse\\." AND NOT "${CXX_CFLAGS_SSE}" STREQUAL "") list(APPEND src_cflags ${CXX_CFLAGS_SSE}) endif() if(file MATCHES "_sse2\\." AND NOT "${CXX_CFLAGS_SSE2}" STREQUAL "") list(APPEND src_cflags ${CXX_CFLAGS_SSE2}) endif() if(file MATCHES "_sse3\\." AND NOT "${CXX_CFLAGS_SSE3}" STREQUAL "") list(APPEND src_cflags ${CXX_CFLAGS_SSE3}) endif() if(file MATCHES "_ssse3\\." AND NOT "${CXX_CFLAGS_SSSE3}" STREQUAL "") list(APPEND src_cflags ${CXX_CFLAGS_SSSE3}) endif() if(file MATCHES "_sse4_1\\." AND NOT "${CXX_CFLAGS_SSE4_1}" STREQUAL "") list(APPEND src_cflags ${CXX_CFLAGS_SSE4_1}) endif() if(file MATCHES "_sse4_2\\." AND NOT "${CXX_CFLAGS_SSE4_2}" STREQUAL "") list(APPEND src_cflags ${CXX_CFLAGS_SSE4_2}) endif() if(file MATCHES "_avx\\." AND NOT "${CXX_CFLAGS_AVX}" STREQUAL "") list(APPEND src_cflags ${CXX_CFLAGS_AVX}) endif() if(file MATCHES "_avx2\\." AND NOT "${CXX_CFLAGS_AVX2}" STREQUAL "") list(APPEND src_cflags ${CXX_CFLAGS_AVX2}) endif() if(NOT "${src_cflags}" STREQUAL "") set_source_files_properties(${src_file} PROPERTIES COMPILE_FLAGS ${src_cflags}) endif() endif() list(APPEND src_array ${src_file}) endforeach() source_group(${src_dir} FILES ${src_array}) set(out_tmp ${${out}}) list(APPEND out_tmp ${src_array}) set("${out}" "${out_tmp}" PARENT_SCOPE) endfunction() function(cxx_add_library product target src deps cflags cflags_dbg cflags_rel) string(TOUPPER "${product}" PRODUCT) if(NOT ${PRODUCT}_STATIC) add_library(${target} SHARED ${src}) else() add_library(${target} STATIC ${src}) endif() target_link_libraries(${target} ${deps}) if (NOT "${${PRODUCT}_PRIVATE_LFLAGS}" STREQUAL "") set_target_properties(${target} PROPERTIES LINK_FLAGS "${${PRODUCT}_PRIVATE_LFLAGS}") endif() if(CMAKE_BUILD_TYPE) if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") target_compile_options(${target} PRIVATE ${cflags} ${cflags_dbg}) else() target_compile_options(${target} PRIVATE ${cflags} ${cflags_rel}) endif() else() target_compile_options(${target} PRIVATE ${cflags} $<$:${cflags_dbg}> $<$>:${cflags_rel}>) endif() if(NOT ${PRODUCT}_STATIC) install(TARGETS ${target} DESTINATION "lib${LIB_SUFFIX}") endif() endfunction() function(cxx_add_executable product target src deps cflags cflags_dbg cflags_rel) string(TOUPPER "${product}" PRODUCT) add_executable(${target} ${src}) target_link_libraries(${target} ${deps}) if (NOT "${${PRODUCT}_PRIVATE_LFLAGS}" STREQUAL "") set_target_properties(${target} PROPERTIES LINK_FLAGS "${${PRODUCT}_PRIVATE_LFLAGS}") endif() if(CMAKE_BUILD_TYPE) if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") target_compile_options(${target} PRIVATE ${cflags} ${cflags_dbg}) else() target_compile_options(${target} PRIVATE ${cflags} ${cflags_rel}) endif() else() target_compile_options(${target} PRIVATE ${cflags} $<$:${cflags_dbg}> $<$>:${cflags_rel}>) endif() if(NOT ${PRODUCT}_STATIC) install(TARGETS ${target} DESTINATION "lib${LIB_SUFFIX}") endif() endfunction() endif()