Files
asmjit/CxxProject.cmake
2017-11-27 14:15:08 +01:00

336 lines
13 KiB
CMake

# 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)
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
cxx_detect_cflags(out_array "/std:c++latest" "/std:c++14")
else()
cxx_detect_cflags(out_array "-std=c++17" "-std=c++14" "-std=c++11" "-std=c++0x")
endif()
# 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")
# 64-bit MSVC compiler doesn't like /arch:SSE[2] as it's implicit.
if(NOT CMAKE_CL_64)
list(APPEND CXX_CFLAGS_SSE "/arch:SSE")
list(APPEND CXX_CFLAGS_SSE2 "/arch:SSE2")
list(APPEND CXX_CFLAGS_SSE3 "/arch:SSE2")
list(APPEND CXX_CFLAGS_SSSE3 "/arch:SSE2")
list(APPEND CXX_CFLAGS_SSE4_1 "/arch:SSE2")
list(APPEND CXX_CFLAGS_SSE4_2 "/arch:SSE2")
endif()
# MSVC doesn't provide any preprocessor definitions to detect SSE3+,
# these unify MSVC with definitions defined by Intel|Clang|GCC.
list(APPEND CXX_CFLAGS_SSE "${CXX_DEFINE}__SSE__")
list(APPEND CXX_CFLAGS_SSE2 "${CXX_DEFINE}__SSE2__")
list(APPEND CXX_CFLAGS_SSE3 "${CXX_DEFINE}__SSE3__")
list(APPEND CXX_CFLAGS_SSSE3 "${CXX_DEFINE}__SSSE3__")
list(APPEND CXX_CFLAGS_SSE4_1 "${CXX_DEFINE}__SSE4_1__")
list(APPEND CXX_CFLAGS_SSE4_2 "${CXX_DEFINE}__SSE4_2__")
# AVX/AVX2 doesn't need custom defs as MSVC does define __AVX[2]__ by itself.
cxx_detect_cflags(CXX_CFLAGS_AVX "/arch:AVX")
cxx_detect_cflags(CXX_CFLAGS_AVX2 "/arch:AVX2")
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()
# HACK: Setting `COMPILE_FLAGS` property cannot be used when your input
# is LIST, even when you use `VALUE1 VALUE2 ...` as cmake would insert
# escaped semicolons instead of spaces. So let's make it the cmake way:
# - nonituitive, verbose, and idiotic.
if(NOT "${src_cflags}" STREQUAL "")
foreach(src_cflag ${src_cflags})
set_property(SOURCE "${src_file}" APPEND_STRING PROPERTY COMPILE_FLAGS " ${src_cflag}")
endforeach()
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} $<$<CONFIG:Debug>:${cflags_dbg}> $<$<NOT:$<CONFIG:Debug>>:${cflags_rel}>)
endif()
if(NOT ${PRODUCT}_EMBED)
install(TARGETS ${target} RUNTIME DESTINATION "bin"
LIBRARY DESTINATION "lib${LIB_SUFFIX}"
ARCHIVE 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} $<$<CONFIG:Debug>:${cflags_dbg}> $<$<NOT:$<CONFIG:Debug>>:${cflags_rel}>)
endif()
if(NOT ${PRODUCT}_STATIC)
install(TARGETS ${target} DESTINATION "lib${LIB_SUFFIX}")
endif()
endfunction()
endif()