I'm trying to cross-build a piece of SW over Android and iOS using CMake. Android project uses GNU ld, while iOS uses lld. I need to add linker options for stripping dead code from linked libraries to both toolchains. I identified the way to achieve it adding on GNU ld, the --gc-sections linker options, while on lld adding the -dead_strip linker options. So, my question is:
Is there an alternative way to check cross-compiling platform, like below?
if(CMAKE_SYSTEM_NAME STREQUAL Android)
target_link_options(GarminAis PRIVATE LINKER:--gc-sections)
elseif(CMAKE_SYSTEM_NAME STREQUAL APPLE)
target_link_options(GarminAis PRIVATE LINKER:-dead_strip)
endif()
I would strongly prefer an unified approach.
Thanks in advance for any help
Related
I have some .metal files and I want to compile it to .metallib. Whole build system is using CMake and I am going to use it to compile .metal too.
To build .metallib I must use the next two commands:
xcrun -sdk macosx metal -c MyLibrary.metal -o MyLibrary.air
xcrun -sdk macosx metallib MyLibrary.air -o MyLibrary.metallib
Is there any convenient way to do it with CMake where I can set compiler and CMake will do everything automatically? Or I should use add_custom_command and manually call xcrun?
TL;DR Both approaches, using custom commands/targets and adding a new language, are valid. The choice seems to depend on trade-offs between reusability, distribution range, target users, etc. Personally, I'd initially opt for the former.
Having said that, if you opt for the add_custom_command/add_custom_target method, it can get you pretty far. Just make sure that you provide actual targets and link them with the OUTPUT of custom commands, in order to be able to set up proper dependencies and avoiding unnecessary re-execution of targets when dependencies are still up to date. For example,
set(FOO_FILE "foo.txt")
add_custom_command(OUTPUT ${FOO_FILE}
COMMAND ${CMAKE_COMMAND} -E touch ${FOO_FILE}
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
COMMENT "Creating ${FOO_FILE}"
VERBATIM)
add_custom_target(foo DEPENDS ${FOO_FILE})
The approach of adding a new language is a bit more involved, as it's targeted to be more generic and provide language support as a CMake feature (i.e. not tied to a particular project). For this, you are required to define various aspects of the language, such as the compiler and the rules to use to process the source files. Apart, from these, feature detection and testing usually fall here too. For more detailed documentation have a look here.
However, investigating an actual language as is supported by CMake could be helpful. I'd suggest having a look under CMake's Modules directory for CUDA (this will lead you to have a look into the Compiler and Platform subdirs too).
NOTE: Digging through the mailing list, I bumped into this thread, that mentions that the second approach might not work so well with non Makefile-based generators. It is not clear from the thread (or the existing documentation) and I cannot try it since I'm not using CMake via such a tool (e.g. MSVC or Xcode).
# metal kernel
set(SRC_METAL_KERNEL
"src/rgb2bgr.metal"
"src/yuv2rgb.metal"
)
set_source_files_properties(${SRC_METAL_KERNEL} PROPERTIES LANGUAGE METAL)
# set metal version
if(TARGETOS STREQUAL "mac")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=macos-metal2.0")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=macos-metal2.0")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=ios-metal2.0")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=ios-metal2.0")
endif()
add_library(MetalLibary STATIC ${SRC_METAL_KERNEL})
set(ADDITIONAL_LINKER_FLAGS "-framework Metal -framework MetalPerformanceShaders")
target_link_libraries(MetalLibary ${ADDITIONAL_LINKER_FLAGS})
Id like to use GLib in my C application which uses CMake as the build system.
Now, I'm somehow confused how I should enable GLib in my CMakeLists.txt. Basically, you add libraries in cmake using the find_package command, so I tried, according to this bugreport
find_package(GLib2)
But nothing is found. In the GLib documentation it is suggested to use pkg-config, on the other hand.
What is the recommended way of enabling glib in a cmake-based project?
Since CMake 3.6 (released in July 2016), pkg_check_modules supports IMPORTED_TARGET argument, reducing the dependency configuration to a single target_link_libraries statement, which will take care of all required compiler and linker options:
find_package(PkgConfig REQUIRED)
pkg_check_modules(deps REQUIRED IMPORTED_TARGET glib-2.0)
target_link_libraries(target PkgConfig::deps)
(above I used the name deps because one can list multiple dependencies with a single pkg_check_modules statement)
In your CMakeLists.txt:
find_package(PkgConfig REQUIRED)
pkg_search_module(GLIB REQUIRED glib-2.0)
target_include_directories(mytarget PRIVATE ${GLIB_INCLUDE_DIRS})
target_link_libraries(mytarget INTERFACE ${GLIB_LDFLAGS})
Give a look at my answer on using CMake with GTK
It's pretty much the same with GLib.
GLib (and various other C libraries using autotools) provide a pkg-config file for declaring:
compiler flags
linker flags
build-time variables
dependencies
The appropriate way to discover where these libraries are with CMake is to use the FindPkgConfig CMake module:
https://cmake.org/cmake/help/v3.0/module/FindPkgConfig.html
yet another version, combination of multiple answers and what actually worked for me (on Linux)!
cmake_minimum_required(VERSION 2.6.4)
project(my_proj)
find_package(PkgConfig REQUIRED)
pkg_search_module(GLIB REQUIRED glib-2.0)
include_directories(${GLIB_INCLUDE_DIRS})
link_directories(${GLIB_LIBRARY_DIRS})
add_executable(my_proj main.c)
add_definitions(${GLIB_CFLAGS_OTHER})
target_link_libraries(my_proj ${GLIB_LIBRARIES})
I've been working on some CMake modules for GNOME (including one for GLib) which you might want to try. Basically, just find_package(GLib), then you can use the glib-2.0 imported target to link to it.
For Macos, I'd like to link to some framework. In windows, I would like to link to some library.
For example, OpenGL Framework, how to express this requirement using cmake?
You could try the following code:
target_link_libraries(<target name>
"-framework AVFoundation"
"-framework CoreGraphics"
"-framework CoreMotion"
"-framework Foundation"
"-framework MediaPlayer"
"-framework OpenGLES"
"-framework QuartzCore"
"-framework UIKit"
)
To tell CMake that you want to link to OpenGL, add the following to your CMakeLists.txt:
find_package(OpenGL REQUIRED)
include_directories(${OPENGL_INCLUDE_DIR})
target_link_libraries(<your program name> ${OPENGL_LIBRARIES})
find_package will look for OpenGL and tell the rest of the script where OpenGL is by setting some OPENGL* variables. include_directories tells your compiler where to find OpenGL headers. target_link_libraries instructs CMake to link in OpenGL.
The following code will do different actions based on the operating system:
if(WIN32)
#Windows specific code
elseif(APPLE)
#OSX specific code
endif()
You could try the following macro code:
macro(ADD_OSX_FRAMEWORK fwname target)
find_library(FRAMEWORK_${fwname}
NAMES ${fwname}
PATHS ${CMAKE_OSX_SYSROOT}/System/Library
PATH_SUFFIXES Frameworks
NO_DEFAULT_PATH)
if( ${FRAMEWORK_${fwname}} STREQUAL FRAMEWORK_${fwname}-NOTFOUND)
MESSAGE(ERROR ": Framework ${fwname} not found")
else()
TARGET_LINK_LIBRARIES(${target} PUBLIC "${FRAMEWORK_${fwname}}/${fwname}")
MESSAGE(STATUS "Framework ${fwname} found at ${FRAMEWORK_${fwname}}")
endif()
endmacro(ADD_OSX_FRAMEWORK)
Example
ADD_OSX_FRAMEWORK(Foundation ${YOUR_TARGET}) # Add the foundation OSX Framework
You can find this example code here
For custom framework
cmake version 3.20.1
https://github.com/Sunbreak/cli-breakpad.trial/blob/master/CMakeLists.txt#L10-L12
if(APPLE)
find_library(BREAKPAD_CLIENT Breakpad "${CMAKE_CURRENT_SOURCE_DIR}/breakpad/mac/")
target_link_libraries(cli-breakpad PRIVATE ${BREAKPAD_CLIENT})
I've been developing a program with Qt5 on Linux without a problem, now I'm trying to compile it on Windows using CMake (with Qt5CoreConfig.cmake, etc.) and mingw32.
Everything works OK, except that I don't know how to static link my project.
This is how I add Qt5 to my project:
foreach(lib Qt5Core Qt5Widgets Qt5Gui Qt5Xml)
find_package(${lib} REQUIRED)
include_directories(${${lib}_INCLUDE_DIRS})
list(APPEND LIBS ${${lib}_LIBRARIES})
add_definitions(${${lib}_DEFINITIONS})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${${lib}_EXECUTABLE_COMPILE_FLAGS}")
endforeach(lib)
What should I do to make it static?
You cannot build a static application with the windows version installed from the QT web site. You need to compile a windows binary from source with -static option enabled.
How to set the warning level for a project (not the whole solution) using CMake? Should work on Visual Studio and GCC.
I found various options but most seem either not to work or are not consistent with the documentation.
In modern CMake, the following works well:
if(MSVC)
target_compile_options(${TARGET_NAME} PRIVATE /W4 /WX)
else()
target_compile_options(${TARGET_NAME} PRIVATE -Wall -Wextra -Wpedantic -Werror)
endif()
My colleague suggested an alternative version:
target_compile_options(${TARGET_NAME} PRIVATE
$<$<CXX_COMPILER_ID:MSVC>:/W4 /WX>
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Wextra -Wpedantic -Werror>
)
Replace ${TARGET_NAME} with the actual target name. -Werror is optional, it turns all warnings into errors.
Or use add_compile_options(...) if you want to apply it to all targets as suggested by #aldo in the comments.
Also, be sure to understand the difference between PRIVATE and PUBLIC (public options will be inherited by targets that depend on the given target).
As #davidfong notes in the comments, since CMake v3.24, there is the CMAKE_COMPILE_WARNING_AS_ERROR variable that switches on treating compile warings as errors. In case it is set inside CMakeLists.txt, the user can still turn it off with the --compile-no-warning-as-error cmake flag. In case you want to add warning-as-error manually, add /WX in Windows and -Werror elsewhere to target_compile_options.
UPDATE: This answer predates the Modern CMake era. Every sane CMake user should refrain from fiddling with CMAKE_CXX_FLAGS directly and call the target_compile_options command instead. Check the mrts' answer which presents the recommended best practice.
You can do something similar to this:
if(MSVC)
# Force to always compile with W4
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
endif()
elseif(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
# Update if necessary
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long -pedantic")
endif()
Some CMake modules I've written include experimental cross-platfrom warning suppression:
sugar_generate_warning_flags(
target_compile_options
target_properties
ENABLE conversion
TREAT_AS_ERRORS ALL
)
set_target_properties(
foo
PROPERTIES
${target_properties}
COMPILE_OPTIONS
"${target_compile_options}"
)
Result for Xcode:
Set CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION Xcode attribute
(aka build settings -> warnings -> suspicious implicit conversions -> YES)
Add compiler flag: -Werror
Makefile gcc and clang:
Add compiler flags: -Wconversion, -Werror
Visual studio:
Add compiler flags: /WX, /w14244
Links
List of available warnings
Usage and more options
As per Cmake 3.24.2 documentation:
if (MSVC)
# warning level 4 and all warnings as errors
add_compile_options(/W4 /WX)
else()
# lots of warnings and all warnings as errors
add_compile_options(-Wall -Wextra -pedantic -Werror)
endif()
GCC and Clang share these flags, so this should cover all 3.
Here is the best solution I found so far (including a compiler check):
if(CMAKE_BUILD_TOOL MATCHES "(msdev|devenv|nmake)")
add_definitions(/W2)
endif()
The GCC equivalent is -Wall (untested).
if(MSVC)
string(REGEX REPLACE "/W[1-3]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
endif()
If you use target_compile_options - cmake will try to use double /W* flag, which will give warning by compiler.
How to set the warning level for a project (not the whole solution) using CMake?
(I assume this to mean a CMake target, and not a CMake project.)
I found various options but most seem either not to work or are not consistent with the documentation.
Kitware's APIs may be trying to deter you from making your build system brittle and error-prone. The special-casing encouraged by other answers to this question violate at least two important principles of modern CMake build systems...
Firstly, prefer not to specify toolchain-specific details in CMakeLists.txt files. It makes the build system brittle. For example, if a new warning appears in a future version of the toolchain, the compiler will emit an error and your user may need to hack your project in order to build the target.
Instead, write toolchain-agnostic CMakeLists.txt files and preserve the user's ability to customise as they see fit. Ideally, your project should build everywhere with vanilla toolchain configuration - even if that doesn't enable your preferred warnings by default.
Secondly, if you intend to link binaries together, flags should be consistent. This reduces the risk of incompatibility which could result in an ill-formed program. However, warning flags are unlikely to affect code generation, so it may be safe to vary these between the targets you link together.
So... if you wish to specify flags per toolchain and if you absolutely must have different flags for different targets, use custom variables:
# CMakeLists.txt
project(my_project)
add_executable(my_target source_file.cpp)
target_compile_options(my_target PRIVATE "${MY_PROJECT_ELEVATED_WARNING_FLAGS}")
There are many ways to set these variables, such as CMakeCache.txt, a toolchain file, and via CMAKE_PROJECT_INCLUDE_BEFORE. But the simplest way is on the command line during configuration, for GCC
cmake -DMY_PROJECT_ELEVATED_WARNING_FLAGS:STRING="-Wall;-Wextra;-Wpedantic;-Werror" <path-to-project>
for MSVC
cmake -DMY_PROJECT_ELEVATED_WARNING_FLAGS:STRING="/W4;/WX" <path-to-project>