Compile without linking - c++

I have a project set up with CMake, and I would like to build one object file in the project and output its llvm IR representation, in order to systematically inspect the generated output.
What I have so far is this:
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
separate_arguments(COMPILE_FLAGS UNIX_COMMAND ${CMAKE_CXX_FLAGS})
list(APPEND COMPILE_FLAGS "-S" "-emit-llvm" "-o" "example1.llvm")
add_custom_command(
TARGET example1 #example1 is the target that builds the actual executable
POST_BUILD
COMMAND ${CMAKE_CXX_COMPILER}
ARGS ${COMPILE_FLAGS} ${CMAKE_CURRENT_SOURCE_DIR}/calculator1.cpp
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Output the llvm IR representation"
)
endif()
Unfortunately ${CMAKE_CXX_FLAGS} does not contain all the arguments passed to clang in the actual compilation. I miss at least the extra include directories, and probably something else.
I tried
get_target_property(MYVAR example1 COMPILE_FLAGS)
and
get_property(
MYVAR
TARGET example1
PROPERTY COMPILE_FLAGS )
But they both fail.
How do I get all the options passed to the compiler (and not the linker)?
Should I take an entirely different approach?

Do you generate Makefile:s out of CMake? In that case you can use the property RULE_LAUNCH_COMPILE:
RULE_LAUNCH_COMPILE Specify a launcher for compile rules.
Makefile generators prefix compiler commands with the given launcher
command line. This is intended to allow launchers to intercept build
problems with high granularity. Non-Makefile generators currently
ignore this property.

Related

How do I make CMake play nice with a proprietary C++ compiler?

I'm building an application for which I'd like to support a certain proprietary platform. It uses a modified version of ARMCC which CMake doesn't seem to like - no matter what I do, it keeps trying to provide strange flags to armlink where it should not, ignoring attempts to override its behavior.
Is it possible to essentially provide an all-new definition of how CMake should deal with a particular compiler? i.e. specify the binary and flags used for compilation, linking, etc., the expected file extensions, etc. throughout the whole process such that CMake doesn't do anything nasty? CMake seems to make custom compiler definitions an incredibly obscure thing to accomplish.
Edit: I've made it provide most of the right flags, but now I'm realizing I can't change the CMake test program - programs for this platform will fail to build if a certain atypical set of symbols (alternative entry point, not main) don't exist.
Edit 2: I've skipped the test program, but now it's failing while trying to get compiler information (why bother? I've given it everything it needs to know for the situation...) with this error:
Error: C9912E: No --cpu selected
To make it abundantly clear, the command it presents on the line above this clearly has the --cpu flag provided to a valid value:
armcc.exe -xc++ --cpu=MPCore -fpch-preprocess -v -dD -E
Though I'm entirely unsure as to what it's trying to do with the rest of it - I can't seem to override this part.
First, you need a toolchain.cmake file. Here, you provide the paths to your armcc compiler, linker, etc.
SET(CMAKE_SYSTEM_NAME Generic)
SET(CMAKE_SYSTEM_VERSION 1)
# this one is important
SET( CMAKE_SYSTEM_PROCESSOR arm )
SET(MCU_ARCH Cortex-M4) #MCU architecture
SET(TOOLCHAIN_BIN_DIR "C:/Keil_v5/ARM/ARMCC/bin/")
set(LINKER_SCATTER_SCRIPT ${PROJECT_SOURCE_DIR}/Scatter_file.sct)
SET(CMAKE_C_COMPILER ${TOOLCHAIN_BIN_DIR}/armcc.exe CACHE FILEPATH "C compiler")
SET(CMAKE_CXX_COMPILER ${TOOLCHAIN_BIN_DIR}/armcc.exe CACHE FILEPATH "C++ compiler")
SET(CMAKE_LINKER ${TOOLCHAIN_BIN_DIR}/armlink.exe CACHE FILEPATH "linker")
SET(CMAKE_AR ${TOOLCHAIN_BIN_DIR}/armar.exe CACHE FILEPATH "Archiver")
SET(CMAKE_ASM_COMPILER ${TOOLCHAIN_BIN_DIR}/armasm.exe CACHE FILEPATH "Assembler")
SET(CMAKE_FROMELF ${TOOLCHAIN_BIN_DIR}/fromelf.exe CACHE FILEPATH "From ELF tool")
SET(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
SET(CMAKE_EXE_LINKER_FLAGS_INIT "--cpu ${MCU_ARCH} --myFlag1 --myFlag2 ${LINKER_SCATTER_SCRIPT}")
Next, within the CMakeLists.txt file, you can pass the compiler and linker flags:
cmake_minimum_required(VERSION 3.10)
SET(CMAKE_VERBOSE_MAKEFILE ON)
project (MyProject C CXX ASM)
#Source Files -> Let CMake know your source files
SET(SRC_FILES ${CMAKE_SOURCE_DIR}/Dir/main.c)
add_executable(
${PROJECT_NAME}
${SRC_FILES})
#Defining compiler preprocessor directives
target_compile_definitions(${PROJECT_NAME} PRIVATE
$<$<COMPILE_LANGUAGE:C>: DEFINE_1;__DEBUG;>
$<$<COMPILE_LANGUAGE:CXX>:DEFINE_2;__DEBUG;>)
#Defining compiler flags
target_compile_options(${PROJECT_NAME} PRIVATE
$<$<COMPILE_LANGUAGE:C>: --c99 -c -O0 --cpu ${MCU_ARCH};>
$<$<COMPILE_LANGUAGE:CXX>:--cpp11 -c -O0 --cpu ${MCU_ARCH};>
$<$<COMPILE_LANGUAGE:ASM>:--cpu ${MCU_ARCH} -g>)
SET(CMAKE_ASM_FLAGS "--pd \"DEFINE_3"")
target_link_libraries(${PROJECT_NAME} ${CMAKE_SOURCE_DIR}/SomeLibFile.lib --flagForLibFile)
target_link_options(${PROJECT_NAME} PRIVATE --linkerFlags)
target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_SOURCE_DIR})
For the necessary flags, have a look at what Keil is using.
Then you can build your application by passing the toolchain.cmake file as -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake
EDIT: Updated based on the feedback from KamilCuk

How to properly set CMake flags in CMakeLists.txt

I'm having trouble setting up options and CMake variables and getting them propagated to the CMake GUI.
For example, the following lines are in my CMakeLists.txt file:
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -tR -DPOCO_DLL")
message("CMake flags: " ${CMAKE_CXX_FLAGS})
When running configure in the CMake GUI it prints "CMake flags: -tM -tR -DPOCO_DLL" indicating that setting the CMAKE_CXX_FLAGS "works".
The GUI however don't get updated and only shows "-tM" on the CMAKE_CXX_FLAGS line.
What is the proper way of setting up these CMAKE options in the CMakeLists file so they get propagated to the CMake GUI?
The trick is to set CMAKE_CXX_FLAGS before project(...) statement. But this will not be enough; you will also need to put it into the cache.
Also, if you plan to support setting it initially from the command line interface, and/or -C cmake option (locad initial cache), you will need to force put it into the cache, as shown:
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -tR -DPOCO_DLL" CACHE STRING "Default CXX options" FORCE)
...
...
project(MyProject)
....
On the other hand, be very careful, because you are setting command line options at the moment you know nothing about the compiler, that is by default determined during execution of project statement.
Implicitly, this renders your CMakeLists.txt compiler dependant.
At the end, here is documentation about set cmake command

Optimize in CMake by default

I have a C++ project which uses CMake as its build system. I'd like the following behavior:
If cmake is invoked as cmake .., then CMAKE_CXX_FLAGS is -O3 -Wall -Wextra
If cmake is invoked as cmake .. -DCMAKE_BUILD_TYPE=Debug, then CMAKE_CXX_FLAGS is -g -Wall -Wextra
I tried the following
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
set(CMAKE_CXX_FLAGS "-O3 -Wall -Wextra")
set(CMAKE_CXX_FLAGS_DEBUG "-g -Wall -Wextra")
But this has a big problem. First of all, if the second invocation is used, then both -O3 and -g flags are passed to the compiler. Besides, if I use the second invocation and the first thereafter, CMAKE_BUILD_TYPE stays Debug although not explicitly ordered so - so I get a Debug build although I want an optimized build.
Why? What can I do to get the desired behavior?
First off: recommended usage of CMake is to always specify CMAKE_BUILD_TYPE explicitly on the command line (if and only if using a single-configuration generator). Your use case deviates from this best practice, so treat this answer as "how you can do it," not necessarily as "how you should do it."
To address the first issue, you should be able to do this early in your CMakeList:
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
set(CMAKE_CXX_FLAGS "-Wall -Wextra")
set(CMAKE_CXX_FLAGS_DEBUG "-g")
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
This will make sure that if you do not specify a build type at all, it will default to "Release" and thus CMAKE_CXX_FLAGS_RELEASE will be used.
The second one is harder to tackle. Variables passed from the command line (such as CMAKE_BUILD_TYPE=Debug) are cached by CMake and thus re-used in subsequent invocations (that is necessary, since CMake can re-trigger itself if you modify its inputs between builds).
The only solution is to make the user switch the build type explicitly again, using cmake .. -DCMAKE_BUILD_TYPE=Release.
Consider why this is necessary: as I said, CMake can re-trigger itself as part of a build if CMake's input (CMakeLists.txt files or their dependencies) has changed since last CMake ran. In such case, it will also be run without command-line arguments such as -DCMAKE_BUILD_TYPE=whatever, and will rely on the cache to supply the same value as last time. This scenario is indistinguishable from you manually running cmake .. without additional arguments.
I could provide a hacky solution to always reset CMAKE_BUILD_TYPE to Release if not specified explicitly on the command line. However, it would also mean that a buildsystem generated as Debug would get re-generated as Release if automatic re-generation happened. I am pretty sure that's not what you want.
For CXX flags specific for Release target, you should set
CMAKE_CXX_FLAGS_RELEASE
instead of
CMAKE_CXX_FLAGS
In your case you can use:
set(CMAKE_CXX_FLAGS "-Wall -Wextra")
set(CMAKE_CXX_FLAGS_DEBUG "-g")
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
A more modern CMake approach (which I suggest, if you are using CMake version 2.8.12 or newer), is well described in this StackOverflow answer and involves the use of target_compile_options.
The default optimization level for various release modes is O3, which often isn't the best choice. Within CMakeLists.txt file, these can be modified to O2:
# Modify compile flags to change optimization level from O3 to O2
string(REGEX REPLACE "([\\/\\-]O)3" "\\12"
CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
string(REGEX REPLACE "([\\/\\-]O)3" "\\12"
CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
string(REGEX REPLACE "([\\/\\-]O)3" "\\12"
CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL}")
string(REGEX REPLACE "([\\/\\-]O)3" "\\12"
CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
These regular expressions will be modified (e.g.):
-O3 to -O2 usually for Linux-based compilers
/O3 to /O2 usually for Windows-based compilers

code builds cleanly with cmake, fails when added as an external project

Question summary.
I have a project A that I build using CMake, which compiles cleanly.
However, when I pull A into another project B using CMake's ExternalProject_Add command, compilation fails when it reaches the point of building A.
The kind of errors I get.
Compiling B gives errors like this one
warning: rvalue references are a C++11 extension [-Wc++11-extensions]
when it starts compiling A (which again, is brought in by ExternalProject_Add).
Note that -std=c++11 is set in all involved CMakeList.txt files.
Note that I am also pulling a google project using ExternalProject_Add, but it does not cause any problems.
Some specifics from the CMakeLists.txt files involved.
The following excerpt is from A's CMakeLists.txt:
# Use the C++11 standard.
set (CC_FLAGS "-std=c++11")
# Figure out the warning flags to use.
CHECK_CXX_COMPILER_FLAG("-pedantic-errors" SUPPORTS_PEDANTIC_ERRORS)
CHECK_CXX_COMPILER_FLAG("-Wall" SUPPORTS_WALL)
CHECK_CXX_COMPILER_FLAG("-Wextra" SUPPORTS_WEXTRA)
if (SUPPORTS_PEDANTIC)
set (CC_FLAGS "${CC_FLAGS} -pedantic")
endif()
# [omitted]... similarly for the rest of the flags.
set (CMAKE_CXX_FLAGS_RELEASE "-O3 ${CC_FLAGS}")
set (CMAKE_CXX_FLAGS_DEBUG "-O0 -g ${CC_FLAGS}")
The following is from B's CMakeLists.txt. The part that differs comes after the asterisks (*).
# Use the C++11 standard.
set (CC_FLAGS "-std=c++11")
# Figure out the warning flags to use.
CHECK_CXX_COMPILER_FLAG("-pedantic-errors" SUPPORTS_PEDANTIC_ERRORS)
CHECK_CXX_COMPILER_FLAG("-Wall" SUPPORTS_WALL)
CHECK_CXX_COMPILER_FLAG("-Wextra" SUPPORTS_WEXTRA)
if (SUPPORTS_PEDANTIC)
set (CC_FLAGS "${CC_FLAGS} -pedantic")
endif()
# [omitted]... similarly for the rest of the flags.
set (CMAKE_CXX_FLAGS_RELEASE "-O3 ${CC_FLAGS}")
set (CMAKE_CXX_FLAGS_DEBUG "-O0 -g ${CC_FLAGS}")
# ************* DIFFERS HERE ************
ExternalProject_Add (
projectA
PREFIX "${projectA_prefix}"
GIT_REPOSITORY "[omitted]"
INSTALL_COMMAND ""
)
ExternalProject_Add (
google_benchmark
PREFIX "${GoogleBenchmarkPrefix}"
GIT_REPOSITORY "https://github.com/google/benchmark.git"
UPDATE_COMMAND ""
BUILD_COMMAND make benchmark
INSTALL_COMMAND ""
)
By default, CMAKE_BUILD_TYPE is empty, so all configuration-specific settings are omitted.
That is why none of your CMAKE_CXX_FLAGS_* variable is used, so the project is built without c++11.
Exception is optimized keyword for target_link_libraries: it works as corresponded to any non-debug config.
Good practice is to provide default build type for the project. This post suggests nice template for that purpose:
# Set a default build type if none was specified
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
message(STATUS "Setting build type to 'Debug' as none was specified.")
set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE)
# Set the possible values of build type for cmake-gui
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release"
"MinSizeRel" "RelWithDebInfo")
endif()

CMake compile C++ file in custom command

I'm trying to precompile a header file in GCC with the following command:
ADD_CUSTOM_COMMAND(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/all.hpp.gch
COMMAND ${CMAKE_CXX_COMPILER} ${CMAKE_CXX_FLAGS} -o ${CMAKE_BINARY_DIR}/all.hpp.gch ${CMAKE_CURRENT_SOURCE_DIR}/all.hpp
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/all.hpp
COMMENT "Generating precompiled headers"
)
However, I don't get CMAKE_CXX_FLAGS to expand into flags I've set using CMake's add_definitions(). What is the correct way of compiling in add_custom_command()?
I don't believe that add_definitions() adds its arguments to CMAKE_CXX_FLAGS. In fact, as far as I can tell they aren't saved anywhere (apart from arguments beginning with -D or /D which get added to COMPILE_DEFINITIONS).
The simplest way to solve that would be to, whenever calling add_definitions(), to also manually add those flags to CMAKE_CXX_FLAGS.
To see what is in CMAKE_CXX_FLAGS at any point, you can do
message(STATUS ${CMAKE_CXX_FLAGS})
or check the CMakeCache.txt in the build directory (or via ccmake or cmake-gui).