cmake. define project languages according to architecture (CMAKE_SYSTEM_NAME) - cmake

My project definition expect different set of languages according to architecture (macOS or Windows).
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
project(myProj CXX OBJC OBJCXX)
elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
project(myProj)
endif()
However, it looks like the CMAKE_SYSTEM_NAME is only defined after the project command.
like in this sample code, only the second message will show valid arch.
message("CMAKE_SYSTEM_NAME: ${CMAKE_SYSTEM_NAME}")
project(myProj)
message("CMAKE_SYSTEM_NAME: ${CMAKE_SYSTEM_NAME}")
Any idea how to fetch the running arch before the project is defined ?

The following is what you intend:
project(myProj LANGUAGES NONE)
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
enable_language(CXX)
enable_language(OBJC)
enable_language(OBJCXX)
elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
enable_language(C)
enable_language(CXX)
endif()
Of course, this leaves no languages configured on other systems, which is probably not what you want...

Related

How can I get a target's output-file's name during CMake's configuration phase (not the generation phase)?

I know we can use $<TARGET_FILE_NAME:Foo> to get the filename for add_custom_command and add_custom_target during build time, but I can't seem to find the answer on out how to get the filename during config time. For example,
add_library(Foo SHARED foo.cpp foo.h)
The best I get is get_target_property(FOO_NAME Foo NAME), but ${FOO_NAME} is Foo, what I want is something like libFoo.so or libFoo.dylib depends on the platform. How can we get the target file name during cmake config time?
For context on why I thought I initially thought I needed to be able to do this, see this other question: In CMake how can I copy a target file to a custom location when target based generator expression for OUTPUT is not supported?.
Through a combination of the following:
variable/CMAKE_BUILD_TYPE
prop_tgt/TYPE
prop_tgt/FRAMEWORK
variable/CMAKE_STATIC_LIBRARY_PREFIX and variable/CMAKE_STATIC_LIBRARY_SUFFIX
variable/CMAKE_SHARED_LIBRARY_PREFIX and variable/CMAKE_SHARED_LIBRARY_SUFFIX
variable/CMAKE_SHARED_MODULE_PREFIX and variable/CMAKE_SHARED_MODULE_SUFFIX
variable/CMAKE_EXECUTABLE_SUFFIX
prop_tgt/OUTPUT_NAME and prop_tgt/OUTPUT_NAME_<CONFIG>
prop_tgt/ARCHIVE_OUTPUT_NAME and prop_tgt/ARCHIVE_OUTPUT_NAME_<CONFIG>
prop_tgt/LIBRARY_OUTPUT_NAME and prop_tgt/LIBRARY_OUTPUT_NAME_<CONFIG>
prop_tgt/RUNTIME_OUTPUT_NAME and prop_tgt/RUNTIME_OUTPUT_NAME_<CONFIG>
I believe you could do something like this:
# cmake_minimum_required(VERSION 3.x)
project(hello)
function(get_target_filename target outvar)
get_target_property(prop_type "${target}" TYPE)
get_target_property(prop_is_framework "${target}" FRAMEWORK)
get_target_property(prop_outname "${target}" OUTPUT_NAME)
get_target_property(prop_archive_outname "${target}" ARCHIVE_OUTPUT_NAME)
get_target_property(prop_library_outname "${target}" LIBRARY_OUTPUT_NAME)
get_target_property(prop_runtime_outname "${target}" RUNTIME_OUTPUT_NAME)
# message("prop_archive_outname: ${prop_archive_outname}")
# message("prop_library_outname: ${prop_library_outname}")
# message("prop_runtime_outname: ${prop_runtime_outname}")
if(DEFINED CMAKE_BUILD_TYPE)
get_target_property(prop_cfg_outname "${target}" "${OUTPUT_NAME}_${CMAKE_BUILD_TYPE}")
get_target_property(prop_archive_cfg_outname "${target}" "${ARCHIVE_OUTPUT_NAME}_${CMAKE_BUILD_TYPE}")
get_target_property(prop_library_cfg_outname "${target}" "${LIBRARY_OUTPUT_NAME}_${CMAKE_BUILD_TYPE}")
get_target_property(prop_runtime_cfg_outname "${target}" "${RUNTIME_OUTPUT_NAME}_${CMAKE_BUILD_TYPE}")
# message("prop_archive_cfg_outname: ${prop_archive_cfg_outname}")
# message("prop_library_cfg_outname: ${prop_library_cfg_outname}")
# message("prop_runtime_cfg_outname: ${prop_runtime_cfg_outname}")
if(NOT ("${prop_cfg_outname}" STREQUAL "prop_cfg_outname-NOTFOUND"))
set(prop_outname "${prop_cfg_outname}")
endif()
if(NOT ("${prop_archive_cfg_outname}" STREQUAL "prop_archive_cfg_outname-NOTFOUND"))
set(prop_archive_outname "${prop_archive_cfg_outname}")
endif()
if(NOT ("${prop_library_cfg_outname}" STREQUAL "prop_library_cfg_outname-NOTFOUND"))
set(prop_library_outname "${prop_library_cfg_outname}")
endif()
if(NOT ("${prop_runtime_cfg_outname}" STREQUAL "prop_runtime_cfg_outname-NOTFOUND"))
set(prop_runtime_outname "${prop_runtime_cfg_outname}")
endif()
endif()
set(outname "${target}")
if(NOT ("${prop_outname}" STREQUAL "prop_outname-NOTFOUND"))
set(outname "${prop_outname}")
endif()
if("${prop_is_framework}")
set(filename "${outname}")
elseif(prop_type STREQUAL "STATIC_LIBRARY")
if(NOT ("${prop_archive_outname}" STREQUAL "prop_archive_outname-NOTFOUND"))
set(outname "${prop_archive_outname}")
endif()
set(filename "${CMAKE_STATIC_LIBRARY_PREFIX}${outname}${CMAKE_STATIC_LIBRARY_SUFFIX}")
elseif(prop_type STREQUAL "MODULE_LIBRARY")
if(NOT ("${prop_library_outname}" STREQUAL "prop_library_outname-NOTFOUND"))
set(outname "${prop_library_outname}")
endif()
set(filename "${CMAKE_SHARED_MODULE_LIBRARY_PREFIX}${outname}${CMAKE_SHARED_MODULE_LIBRARY_SUFFIX}")
elseif(prop_type STREQUAL "SHARED_LIBRARY")
if(WIN32)
if(NOT ("${prop_runtime_outname}" STREQUAL "prop_runtime_outname-NOTFOUND"))
set(outname "${prop_runtime_outname}")
endif()
else()
if(NOT ("${prop_library_outname}" STREQUAL "prop_library_outname-NOTFOUND"))
set(outname "${prop_library_outname}")
endif()
endif()
set(filename "${CMAKE_SHARED_LIBRARY_PREFIX}${outname}${CMAKE_SHARED_LIBRARY_SUFFIX}")
elseif(prop_type STREQUAL "EXECUTABLE")
if(NOT ("${prop_runtime_outname}" STREQUAL "prop_runtime_outname-NOTFOUND"))
set(outname "${prop_runtime_outname}")
endif()
set(filename "${CMAKE_EXECUTABLE_PREFIX}${outname}${CMAKE_EXECUTABLE_SUFFIX}")
else()
message(FATAL_ERROR "target \"${target}\" is not of type STATIC_LIBRARY, MODULE_LIBRARY, SHARED_LIBRARY, or EXECUTABLE.")
endif()
set("${outvar}" "${filename}" PARENT_SCOPE)
endfunction()
add_library(static_lib STATIC test.cpp)
add_library(shared_lib SHARED test.cpp)
add_executable(executable test.cpp)
get_target_filename(static_lib static_lib_filename)
get_target_filename(shared_lib shared_lib_filename)
get_target_filename(executable executable_filename)
message(STATUS "static_lib_filename: ${static_lib_filename}")
message(STATUS "shared_lib_filename: ${shared_lib_filename}")
message(STATUS "executable_filename: ${executable_filename}")
The above is a basic implementation. It doesn't handle some (perhaps important) nuances like:
The fact that most of those target properties can themselves have generator expressions in them (see their docs), which, if it happens to you, I think you're out of luck.
The fact that CMAKE_BUILD_TYPE is only relevant for single-config generators- not multi-config generators.
https://cmake.org/cmake/help/latest/variable/CMAKE_EXECUTABLE_SUFFIX_LANG.html
Other language-specific overrides like CMAKE_SHARED_LIBRARY_PREFIX_<LANG>
You'd need to check if those exist and handle them if they do... except in honesty I'm not quite sure how, given that it doesn't seem like targets have a LANGUAGE property. Source files do, but that's not what we need here. One might need to go to the CMake Discourse to ask about this.
Note: If you want the full path to the target output file... oh boy...
prop_tgt/ARCHIVE_OUTPUT_DIRECTORY and prop_tgt/ARCHIVE_OUTPUT_DIRECTORY_<CONFIG>
prop_tgt/LIBRARY_OUTPUT_DIRECTORY and prop_tgt/LIBRARY_OUTPUT_DIRECTORY_<CONFIG>
prop_tgt/RUNTIME_OUTPUT_DIRECTORY and prop_tgt/RUNTIME_OUTPUT_DIRECTORY_<CONFIG>
More fun notes: If you want to evaluate generator expressions recursively at generation time (for generator expressions that themselves evaluate to generator expressions), you can use the $<GENEX_EVAL:...> generator expression, but of course- that doesn't apply to this question, which is about configure time.
A little bit clumsy but the file name can be constructed in the following way:
set(FOO_NAME "${CMAKE_SHARED_LIBRARY_PREFIX}Foo${CMAKE_SHARED_LIBRARY_SUFFIX}")
See also cmake doc.
Note that the two variables will be overridden by the respective CMAKE_SHARED_LIBRARY_??FIX_<LANG> variables. So if that's a possibility, you need make sure you catch the right variable.
Let me add the final remark that the rationale behind CMake is you don't need to know. CMake operates on targets, not files. So whatever you're trying to achieve might be possible without getting the filename.

CMake: custom add_executable failing when adding link libraries

I'm making a custom cross-platform library that needs to be in native gui mode, so, the add_executable need to have the parameter of WIN32 or MACOSX_BUNDLE depending on the platform. To facilitate this process I created a cmake function:
function(add_windowed_executable targetProject targetSources)
if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
add_executable(${targetProject} WIN32 ${targetSources})
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") # MacOS
add_executable(${targetProject} MACOSX_BUNDLE ${targetSources})
# ... link with native libraries
elseif(CMAKE_SYSTEM_NAME STREQUAL Linux)
add_executable(${targetProject} ${targetSources})
# ... link with native libraries
endif()
endfunction()
Now when I try to create an executable with target_link_library it gives me the error "Cannot specify link libraries for target "test" which is not built by this project.". It comes down to the fact that the add_windowed_executable is the one that calls the add_executable and not the main file. Is there any way to use this function and allow to link with other libraries, basically replicating the add_executable command?
Main cmake
# ...
add_subdirectory("functions")
# ...
add_windowed_executable("test" "src/main.c")
target_link_libraries("test" "otherLibrary") # It's here where the error appears
EDIT: tryed with macro instead of function and the error still remains
add_executable(targetProject
You added targetProject. Not test. You want to use the variable:
add_executable(${targetProject} ${targetSources})
and the same with targetSources.
It needs to be done the oposite way. First check the os then create the function or macro:
if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
function(add_windowed_executable targetProject)
add_executable(${targetProject} WIN32 ${ARGN})
endfunction()
# ...
endif ()

CMake: Not Connecting -lrt to project

I have a project, dependent on the rt library, that shows undeclared errors for functions ddefined in LIBRT when using make following running the "cmake .". I've looked at the plethora of suggestions for questions similar to this but none worked for me. Using the functions check_include_files() and check_library_exists() return TRUE, and it appears I succeed in finding the library using find_library(). I even get "-lrt" appended at the end of the link.txt file.
Anyways, I am a bit stumped and looking for some guidance. Below is my CMakeLists.txt file:
cmake_minimum_required (VERSION 3.5)
project (proj_name C)
enable_language(C)
set (SOURCES
/source/file/path
)
add_definitions (-std=c99)
add_executable (proj_name ${SOURCES})
find_library(LIBRT rt)
if(LIBRT)
target_include_directories (proj_name PUBLIC ${PROJECT_SOURCE_DIR}
/proj/includes/path #not including rt header path
${RT_INCLUDE_DIRS}
)
target_link_libraries(proj_name PUBLIC ${LIBRT})
endif(LIBRT)
set(CMAKE_C_COMPILER /usr/bin/gcc)
include(CheckIncludeFiles)
check_include_files(mqueue.h HAVE_MQUEUE_H)
if(NOT HAVE_MQUEUE_H)
message(FATAL_ERROR "mqueue.h not found")
endif(NOT HAVE_MQUEUE_H)
include(CheckLibraryExists)
check_library_exists(rt timer_gettime "" HAVE_RT)
if(NOT HAVE_RT)
message(FATAL_ERROR "timer_gettime not found")
endif(NOT HAVE_RT)
Running on WSL Ubuntu.
Thanks for any insight possible!

Project build configuration in CMake

My question is very similar to CMake : Changing name of Visual Studio and Xcode exectuables depending on configuration in a project generated by CMake. In that post the output file name will change according to the project configuration (Debug, Release and so on). I want to go further. When I know the configuration of the project, I want to tell the executable program to link different library names depending on project configurations. I was wondering whether there is a variable in CMake that can tell the project configuration. If there exists such a variable, my task will become easier:
if (Project_Configure_Name STREQUAL "Debug")
#do some thing
elseif (Project_Configure_Name STREQUAL "Release")
#do some thing
endif()
According to http://cmake.org/cmake/help/v2.8.8/cmake.html#command:target_link_libraries, you can specify libraries according to the configurations, for example:
target_link_libraries(mytarget
debug mydebuglibrary
optimized myreleaselibrary
)
Be careful that the optimized mode means every configuration that is not debug.
Following is a more complicated but more controllable solution:
Assuming you are linking to an imported library (not compiled in your cmake project), you can add it using:
add_library(foo STATIC IMPORTED)
set_property(TARGET foo PROPERTY IMPORTED_LOCATION_RELEASE c:/path/to/foo.lib)
set_property(TARGET foo PROPERTY IMPORTED_LOCATION_DEBUG c:/path/to/foo_d.lib)
add_executable(myexe src1.c src2.c)
target_link_libraries(myexe foo)
See http://www.cmake.org/Wiki/CMake/Tutorials/Exporting_and_Importing_Targets for more details.
There is always another way:
if(CMAKE_BUILD_TYPE MATCHES "release")
SET(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE})
else(CMAKE_BUILD_TYPE MATCHES "debug")
SET(CMAKE_BUILD_TYPE "debug")
endif(CMAKE_BUILD_TYPE MATCHES "release")
We can use the variable CMAKE_BUILD_TYPE. We can also change this variable at the beginning of invoking CMAKE:
cmake .. -DCMAKE_BUILD_TYPE:STRING=debug
Then we can use this variable as an indicator of build configuration.

How can I add a minimum compiler version requisite?

I want to create a project in C++11 and I use CMake as my build system.
How can I add a minimum compiler version requisite in the CMake config files?
AFAIK, there is no built-in support for something like this, but you could certainly write it yourself:
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "your.required.gcc.version")
message(FATAL_ERROR "Insufficient gcc version")
endif()
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "your.required.msvc.version")
message(FATAL_ERROR "Insufficient msvc version")
endif()
elseif(...)
# etc.
endif()
However, I suggest you actually consider a feature-detection approach instead. That is, use try_compile() to verify that the compiler supports the features you need, and FATAL_ERROR if it doesn't. It's more idiomatic in CMake, and has the added benefit you don't have to discover the appropriate minimal version for all compilers out there.
Starting with CMake 2.8.10 the CMAKE_<LANG>_COMPILER_VERSION variables can be accessed by users to get the compiler version. In previous versions those were only reserved for internal purposes and should not be read by user code. They are also not guaranteed to be set for all languages but C and CXX should definitely be available.
CMake also contains operators for version comparison (VERSION_LESS, VERSION_EQUAL, VERSION_GREATER) that you can use to write your version validation code.
Remember that you will need to find out which compiler you have first and then check for the correct version.
Here is a short sample from one of my projects:
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
# require at least gcc 4.8
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.8)
message(FATAL_ERROR "GCC version must be at least 4.8!")
endif()
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
# require at least clang 3.2
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.2)
message(FATAL_ERROR "Clang version must be at least 3.2!")
endif()
else()
message(WARNING "You are using an unsupported compiler! Compilation has only been tested with Clang and GCC.")
endif()
You can check the specific gcc version as follows:
if (CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.1)
message(FATAL_ERROR "Require at least gcc-5.1")
endif()
There is built-in support nowadays. From the Documentation:
target_compile_features(mylib PUBLIC cxx_std_11)
"In this example, CMake will ensure the compiler is invoked in a mode of at-least C++ 11 (or C++ 14, C++ 17, ...), adding flags such as -std=gnu++11 if necessary. This applies to sources within mylib as well as any dependents (that may include headers from mylib)."