How to Make Cmake Use Specific Compiler and Flags When Final Compilation Stage Instead of Detection

How can I make Cmake use specific compiler and flags when final compilation stage instead of detection?

I solved this and built Bullet Physics for iOS.

Solution

Here's toolchain configuration that I used.

INCLUDE(CMakeForceCompiler)

SET (CMAKE_CROSSCOMPILING TRUE)
SET (CMAKE_SYSTEM_NAME "Darwin")
SET (CMAKE_SYSTEM_PROCESSOR "arm")

SET (SDKVER "4.3")
SET (DEVROOT "/Developer/Platforms/iPhoneOS.platform/Developer")
SET (SDKROOT "/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS${SDKVER}.sdk")
SET (CC "${DEVROOT}/usr/bin/clang")
SET (CXX "${DEVROOT}/usr/bin/clang++")

CMAKE_FORCE_C_COMPILER (${CC} CLang)
CMAKE_FORCE_CXX_COMPILER (${CXX} CLang)

SET (CMAKE_FIND_ROOT_PATH "${SDKROOT}" "${DEVROOT}")
SET (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
SET (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
SET (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

And the build script. This is important!

PKG_NAME=bullet-2.78
BUILD_DIR=build

rm -rf ${PKG_NAME} ${BUILD_DIR}
tar -x -f ${PKG_NAME}-r2387.tar

mkdir build
cd build

DEVROOT=/Developer/Platforms/iPhoneOS.platform/Developer
SYSROOT=$DEVROOT/SDKs/iPhoneOS4.3.sdk
CC=$DEVROOT/usr/bin/clang
CXX=$DEVROOT/usr/bin/clang++
CFLAGS="-arch armv6 -arch armv7 -isysroot $SYSROOT -miphoneos-version-min=4.0"
CXXFLAGS=$CFLAGS
LDFLAGS=$CFLAGS

export CC=$CC
export CXX=$CXX
export CFLAGS=$CFLAGS
export CXXFLAGS=$CXXFLAGS
export LDFLAGS=$LDFLAGS

cmake ../$PKG_NAME -DCMAKE_TOOLCHAIN_FILE=../CMAKE_IPHONEOS_TOOLCHAIN.cmake
make
lipo -info src/LinearMath/libLinearMath.a

This is very minimal configuration. However you got the idea.

Description

First, the toolchain configuration is just a stage figuring out features available on target machine. But cross compilation to iOS require some special compiler flags, and this is exception situation described on Cmake wiki.

So I just forced specific compiler, and Cmake will skip compiler verification stage.

Second, all compiler flags needed for cross compilation offered via shell variable export in build script. This is very rough options, however important is we have to pass via shell variables. Not with toolchain configuration.

However some kind of toolchain configuration affects on generated Makefile. We have to specify correct CMAKE_SYSTEM_NAME (Darwin) and CMAKE_SYSTEM_PROCESSOR (arm).



Update

There is another trials. Anyway these don't work for me anymore.

  • http://code.google.com/p/ios-cmake/
  • http://www.ltengsoft.com/node/20

Here's one more. This looks promising.

  • http://immersedcode.org/2011/4/25/sdl-on-ios/

Update 2

Now Bullet includes a build script for iOS platforms. Anyway it's inconvenient because it does not handle special needs for simulator stuffs, and I wrote another build script which makes far library for debug/release mode.

https://github.com/Eonil/Bullet-PhysicsEngine-BuildScript

CMAKE: how to set the compiler flags that contains a given destination path and file name

The simple method is to set the property COMPILE_OPTIONS for each needed source file.

Here is the test CMakeLists.txt

project(TestSourceProp)

set(SOURCE_FILES
main.c
file.c
)

add_executable(${PROJECT_NAME} ${SOURCE_FILES})

foreach(SRC_ IN LISTS SOURCE_FILES)
get_filename_component(SRC_BASENAME_ ${SRC_} NAME_WE)
set_source_files_properties(${SRC_} PROPERTIES COMPILE_OPTIONS "--omf_browse=${SRC_BASENAME_}.crf;--depend=${SRC_BASENAME_}.d")
get_source_file_property(SRC_PROP_ ${SRC_} COMPILE_OPTIONS)
message(STATUS "${SRC_}: ${SRC_PROP_}")
endforeach()

The output:

...
-- main.c: --omf_browse=main.crf;--depend=main.d
-- file.c: --omf_browse=file.crf;--depend=file.d
-- Configuring done

Makefile:

CMakeFiles/TestSourceProp.dir/main.c.obj: ../main.c
@$(CMAKE_COMMAND) -E cmake_echo_color ...
gcc $(C_DEFINES) $(C_INCLUDES) $(C_FLAGS) --omf_browse=main.crf --depend=main.d -o CMakeFiles/TestSourceProp.dir/main.c.obj -c ...

Another way, I think, is to create a custom CMake-toolchain file.

CMake: add compiler flags to a target if another specific target depends on it

In CMake you cannot set compiler flags depending on what is being built.

All flags are determined on *configuration stage (cmake invocation), but selecting targets for build is performed only on build stage (make invocation).


While build-dependent flags are supported by make, they are not supported by many of other build tools. And CMake tends to be build tool-independent.

How to specify a compiler in CMake?

To select a specific compiler, you have several solutions, as exaplained in CMake wiki:

Method 1: use environment variables

For C and C++, set the CC and CXX environment variables. This method is not guaranteed to work for all generators. (Specifically, if you are trying to set Xcode's GCC_VERSION, this method confuses Xcode.)
For example:

CC=gcc-4.2 CXX=/usr/bin/g++-4.2 cmake -G "Your Generator" path/to/your/source

Method 2: use cmake -D

Set the appropriate CMAKE_FOO_COMPILER variable(s) to a valid compiler name or full path on the command-line using cmake -D.
For example:

cmake -G "Your Generator" -D CMAKE_C_COMPILER=gcc-4.2 -D CMAKE_CXX_COMPILER=g++-4.2 path/to/your/source

Method 3 (avoid): use set()

Set the appropriate CMAKE_FOO_COMPILER variable(s) to a valid compiler name or full path in a list file using set(). This must be done before any language is set (ie: before any project() or enable_language() command).
For example:

set(CMAKE_C_COMPILER "gcc-4.2")
set(CMAKE_CXX_COMPILER "/usr/bin/g++-4.2")

project("YourProjectName")

The wiki doesn't provide reason why 3rd method should be avoided...

How can you add warning flags using cmake cross platform?

It relies on you having, for every project, to add specific support for each >compiler that you support, one at a time.

At now you can only have something like compiler.cmake, where you configure suitable flags for each compiler, and share compiler.cmake among projects.

Is there no equivalent solution for adding the equivalent of -Wall / /W3 to the >compile simply via a cmake setting?

No, now there is only disscussion about similar feature and it's possible implementation, see

https://cmake.org/pipermail/cmake-developers/2016-March/028107.html

How do I detect that I am cross-compiling in CMakeLists.txt?

The test for CMAKE_CROSSCOMPILING must come after the "project" instruction in CMakeLists.txt.

How to instruct CMake to use the build architecture compiler

CMake can only handle one compiler at a time. So - if you don't go the long way to set up the other compiler as a new language - you will end up with two configuration cycles.

I see the following approaches to automate this process:

  1. Taking the example "CMake Cross Compiling - Using executables in the build created during the build?" from the CMake pages as a starting point I'll get:

    CMakeLists.txt

    cmake_minimum_required(VERSION 3.0)
    project(FooBarTest)

    # When crosscompiling import the executable targets
    if (CMAKE_CROSSCOMPILING)
    set(IMPORT_PATH "IMPORTFILE-NOTFOUND" CACHE FILEPATH "Point it to the export file path from a native build")
    file(TO_CMAKE_PATH "${IMPORT_PATH}" IMPORT_PATH_CMAKE)
    include(${IMPORT_PATH_CMAKE}/genfooTargets.cmake)

    # Then use the target name as COMMAND, CMake >= 2.6 knows how to handle this
    add_custom_command(
    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/foo.h
    COMMAND genfoo
    )

    add_executable(bar bar.cpp ${CMAKE_CURRENT_BINARY_DIR}/foo.h)
    target_include_directories(bar PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
    endif()

    # Only build the generator if not crosscompiling
    if (NOT CMAKE_CROSSCOMPILING)
    add_executable(genfoo genfoo.cpp)
    export(TARGETS genfoo FILE "${CMAKE_CURRENT_BINARY_DIR}/genfooTargets.cmake")
    endif()

    Then using a script like:

    build.sh

    #!/bin/bash

    if [ ! -d hostBuild ]; then
    cmake -E make_directory hostBuild
    cmake -E chdir hostBuild cmake ..
    fi
    cmake --build hostBuild

    if [ ! -d crossBuild ]; then
    cmake -E make_directory crossBuild
    cmake -E chdir crossBuild cmake .. -DIMPORT_PATH=${PWD}/hostBuild -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake
    fi
    cmake --build crossBuild

    I'll get the desired results by calling ./build.sh.

  2. Splitting the CMakeLists.txt and maybe even replace the export()/include() with something where I know the output path of my build tools e.g. by using CMAKE_RUNTIME_OUTPUT_DIRECTORY would simplify things:

    CMakeLists.txt

    cmake_minimum_required(VERSION 3.0)
    project(FooBarTest)

    # Then use the target name as COMMAND. CMake >= 2.6 knows how to handle this
    add_custom_command(
    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/foo.h
    COMMAND genfoo
    )

    add_executable(bar bar.cpp ${CMAKE_CURRENT_BINARY_DIR}/foo.h)
    target_include_directories(bar PRIVATE ${CMAKE_CURRENT_BINARY_DIR})

    buildTools/CMakeLists.txt

    cmake_minimum_required(VERSION 3.0)
    project(BuildTools)

    add_executable(genfoo genfoo.cpp)

    build.sh

    #!/bin/bash

    if [ ! -d crossBuild ]; then
    cmake -E make_directory crossBuild
    cmake -E chdir crossBuild cmake .. -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake
    fi
    if [ ! -d hostBuild ]; then
    cmake -E make_directory hostBuild
    cmake -E chdir hostBuild cmake ../buildTools -DCMAKE_RUNTIME_OUTPUT_DIRECTORY:PATH=${PWD}/crossBuild
    fi
    cmake --build hostBuild
    cmake --build crossBuild

References

  • Making a CMake library accessible by other CMake packages automatically
  • CMake build multiple targets in different build directories
  • How do I make CMake output into a 'bin' dir?

Override compile flags for single files

Your attempts above are adding further flags to your file/target rather than overwriting as you seem to expect. For example, from the docs for Properties on Source Files - COMPILE_FLAGS:

These flags will be added to the list of compile flags when this source file builds.

You should be able to countermand the -Weffc++ flag for foo.cpp by doing

set_source_files_properties(foo.cpp PROPERTIES COMPILE_FLAGS -Wno-effc++)

This should have the effect of adding -Wno-effc++ after -Weffc++ in the compiler command, and the latter setting wins. To see the full command and check that this is indeed the case, you can do

make VERBOSE=1

As an aside, one of the maintainers of the GNU C++ Standard Library presents a pretty negative opinion on -Weffc++ in this answer.

Another point is that you're misusing add_definitions in the sense that you're using this for compiler flags rather than the intended preprocessor definitions.

It would be preferable to use add_compile_options

add_compile_options(-Wall -Weffc++ -pedantic -std=c++0x)

or for CMake versions < 3.0 to do something more like:

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Weffc++ -pedantic -std=c++0x")

In response to further questions in the comments below, I believe it's impossible to reliably remove a flag on a single file. The reason is that for any given source file, it has the COMPILE_OPTIONS and COMPILE_FLAGS1 of its target applied, but these don't show up in any of the properties for that source file.

You could look at stripping the problem flag from the target's COMPILE_OPTIONS, then applying it to each of the target's sources individually, omitting it from the specific source file as required.

However, while this could work in many scenarios, it has a couple of problems.

First - source files' properties don't include COMPILE_OPTIONS, only COMPILE_FLAGS. This is a problem because the COMPILE_OPTIONS of a target can include generator expressions, but COMPILE_FLAGS doesn't support them. So you'd have to accommodate generator expressions while searching for your flag, and indeed you'd maybe even have to "parse" generator expressions if your flag was contained in one or more to see whether it should be re-applied to the remaining source files.

Second - since CMake v3.0, targets can specify INTERFACE_COMPILE_OPTIONS. This means that a dependency of your target can add or override your target's COMPILE_OPTIONS via its INTERFACE_COMPILE_OPTIONS. So you'd further have to recursively iterate through all your target's dependencies (not a particularly easy task since the list of LINK_LIBRARIES for the target can also contain generator expressions) to find any which are applying the problem flag, and try and remove it from those targets' INTERFACE_COMPILE_OPTIONS too.

At this stage of complexity, I'd be looking to submit a patch to CMake to provide the functionality to remove a specific flag unconditionally from a source file.


1: Note that unlike the COMPILE_FLAGS property on source files, the COMPILE_FLAGS property on targets is deprecated.



Related Topics



Leave a reply



Submit