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

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

Related

Missing CMAKE_ASM_NASM_COMPILER when compiling gRPC with MS visual studio

I am trying to build gRPC C++ (1.48.0) with Visual Studio 2022 on Windows 10. It's a CMake build (cmake 3.22.22011901-MSVC_2)
I was able to build everything else but am stuck at BoringSSL. The relevant CMakeList is trying to enable_language(ASM_NASM). Context below:
if(NOT OPENSSL_NO_ASM)
if(UNIX)
enable_language(ASM)
# Clang's integerated assembler does not support debug symbols.
if(NOT CMAKE_ASM_COMPILER_ID MATCHES "Clang")
set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -Wa,-g")
endif()
# CMake does not add -isysroot and -arch flags to assembly.
if(APPLE)
if(CMAKE_OSX_SYSROOT)
set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -isysroot \"${CMAKE_OSX_SYSROOT}\"")
endif()
foreach(arch ${CMAKE_OSX_ARCHITECTURES})
set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -arch ${arch}")
endforeach()
endif()
else()
set(CMAKE_ASM_NASM_FLAGS "${CMAKE_ASM_NASM_FLAGS} -gcv8")
enable_language(ASM_NASM)
endif()
endif()
It gives me CMake Error: "No CMAKE_ASM_NASM_COMPILER could be found."
I don't know enough about compilers / assemblers, and why boringSSL would need a specific one (which none of the other modules needed including gRPC).
What is the recommended way to fix this?
To answer at least some of my questions for my future self, and others who are at a similar point in the journey.
NASM is an assembly compiler (assembler). BoringSSL has some assembly language code, which is why it needs an assembly compiler (and gRPC or other modules don't). I'll let someone else opine on why NASM and not some other assembler.
To fix the issue, you have to download/install the relevant NASM executable from here. I found it easier to download the exe, place it in a folder, add that folder to the PATH and set another env variable ASM_NASM with the name of nasm.exe.
Once I did that, boringSSL and the whole gRPC compiled quite smoothly.

Compiling library with different flags than main code

My project includes an external library (HPTT) that needs to be compiled and linked with the main part of the code. At the moment, I compile both HPTT and my own source code together with the same compiler flags, using the following CMake file:
cmake_minimum_required(VERSION 2.6)
project(custom_tthresh)
# Default settings
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
set(CMAKE_CXX_FLAGS "-Wall -Wextra -Wno-unknown-pragmas") # -Wno-unknown-pragmas ignores unknown OpenMP pragma's without warnings.
set(CMAKE_CXX_FLAGS_DEBUG "-g")
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
file(GLOB SRC "src/*.cpp")
file(GLOB HPTT "src/hptt/*.cpp")
add_executable(experiments ${SRC} ${HPTT})
target_include_directories(experiments PRIVATE /usr/include/eigen3/ src/include/)
add_definitions(-std=c++14)
However, I have two issues with this setup:
HPTT takes a long time to compile, so whenever I change any flags for my own code, I have to wait a lot for it to recompile.
HPTT gives me a bunch of warnings, especially with the -Wall -Wextra -Wno-unknown-pragmas flags, which I'd like to keep for my own code.
How can I set up my CMake file so that it compiles both the library and my own code separately, using different compiler flags, and then links them together? I'd like to stick to some static settings for HPTT (always in release mode, less/no warnings, ...). For full information, these are the current locations of the relevant files:
My own header and source files are in src/
HPTT headers are in src/include/ (this directory needs to be included for the HPTT source files to compile)
HPTT source files are in src/hptt/
Update: Thanks for all the advice. I updated my CMake file now:
cmake_minimum_required(VERSION 3.7)
project(custom_tthresh)
# Always compile external dependencies in Release mode
# We use the custom flag CUSTOM_TTHRESH_BUILD_TYPE to determine the build type for our own library and its related executables
set(CUSTOM_TTHRESH_BUILD_TYPE Release FORCE)
# HPTT
set(HPTT_SRCS src/hptt/hptt.cpp src/hptt/plan.cpp src/hptt/transpose.cpp src/hptt/utils.cpp)
add_library(hptt STATIC ${HPTT_SRCS})
target_include_directories(hptt PRIVATE src/include)
target_compile_options(hptt PRIVATE -w)
# Custom TTHRESH
set(CUSTOM_TTHRESH_SRCS
src/compress.cpp
src/CompressedIO.cpp
src/Compressor.cpp
src/DataBuffer.cpp
src/decompress.cpp
src/quantize.cpp
src/Sizes.cpp
src/Slice.cpp
src/st_hosvd.cpp
)
add_library(custom_tthresh STATIC ${CUSTOM_TTHRESH_SRCS})
target_include_directories(custom_tthresh PRIVATE /usr/include/eigen3/)
target_link_libraries(custom_tthresh hptt)
target_compile_options(custom_tthresh PRIVATE -Wall -Wextra -Wno-unknown-pragmas)
if(CUSTOM_TTHRESH_BUILD_TYPE EQUAL Release)
target_compile_options(custom_tthresh PRIVATE -O3 -DNDEBUG)
else()
target_compile_options(custom_tthresh PRIVATE -g)
endif()
set_target_properties(custom_tthresh PROPERTIES
CXX_STANDARD 14
CXX_STANDARD_REQUIRED ON
)
# Experiments
add_executable(experiments src/experiments.cpp)
target_link_libraries(experiments custom_tthresh)
target_compile_options(experiments PRIVATE -Wall -Wextra -Wno-unknown-pragmas)
if(CUSTOM_TTHRESH_BUILD_TYPE EQUAL Release)
target_compile_options(custom_tthresh PRIVATE -O3 -DNDEBUG)
else()
target_compile_options(custom_tthresh PRIVATE -g)
endif()
This seems to address my problems, avoids some of the bad practices pointed out below and actually reflects the structure of the project. I'm still not proud of the use of CUSTOM_TTHRESH_BUILD_TYPE (based on this question), however I couldn't find a better solution.
Use target_compile_options() to set flags per target:
target_compile_options(experiments PRIVATE "-Wall -Wextra -Wno-unknown-pragmas")
Additionally, don't set flags globally because it sets the flag for everything in the source tree. Don't do this:
set(CMAKE_CXX_FLAGS "-Wall -Wextra -Wno-unknown-pragmas") # don' do this
set(CMAKE_CXX_FLAGS_DEBUG "-g") # especially this
set(CMAKE_CXX_FLAGS_RELEASE "-O3") # and this
Another bad practice is using file globbing. Read Why is cmake file GLOB evil?
file(GLOB SRC "src/*.cpp")
file(GLOB HPTT "src/hptt/*.cpp") #avoid this
And from the cmake docs:
Note: We do not recommend using GLOB to collect a list of source files from your source tree. If no CMakeLists.txt file changes when a source is added or removed then the generated build system cannot know when to ask CMake to regenerate. The CONFIGURE_DEPENDS flag may not work reliably on all generators, or if a new generator is added in the future that cannot support it, projects using it will be stuck. Even if CONFIGURE_DEPENDS works reliably, there is still a cost to perform the check on every rebuild.
This doesn't do what you think it does. It's certainly not setting the C++ standard:
add_definitions(-std=c++14)
To set the C++ standard, use set_target_properties:
set_target_properties(experiments PROPERTIES
CXX_STANDARD 14 # standard version
CXX_STANDARD_REQUIRED ON # required yes
)
You can set the standard globally using set(CMAKE_CXX_STANDARD 14) if you want to, but it may not work with MSVC.
How can I set up my CMake file so that it compiles both the library and my own code separately, using different compiler flags, and then links them together?
Use target_compile_options and target_link_options separately on targets to specific flags for a specific target.
Your add_definitions(-std=c++14) is doing nothing (because there are no targets after it) and prefer using set_target_properties(target PROPERTIES CXX_STANDARD 14 CXX_STANDARD_REQUIRED YES CXX_EXTENSIONS NO) to portably set C++14.

linking librarys as a part of flags in toolchain file in cmake

i've tried to add PROFILE to the CMAKE_BUILD_TYPES. For this I need to add -finstrument-functionsto the CMAKE_CXX_FLAGS in the toolchain file and link the belonging library with the absolut path in the CMakeLists.txt via target_link_libraries. So far so good and unproblematic.
But when refreshing the buildinformations cmake checks if the compiler stil does it's job. And now "is not able to compile a simple test program", due to the lack of the needed library, which is linked later in the CMakeLists.
So I commented the -finstrument-functions flag and the testprogramm could be compiled.
I've tried to:
- Set librarie and path as a part of the CXX flags with -L -l:
Use the link_directories(<dir>) command as well as SET(CMAKE_LINK_DIRECTORIES_BEFORE <Path> ) and SET(CMAKE_LIBRARY_PATH <Path>)
Set variables in the toolchainfile and call them later in the cmakelists:
SET(ADDITIONAL_PROFILE_LIBRARY "$ENV{QNX_BASE}/target/qnx7/armle-v7/usr/liblibprofilingS.a")
SET(ADDITIONAL_PROFILE_FLAGS "-finstrument-functions")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}" "${ADDITIONAL_PROFILE_FLAGS}")
add_executable(toolchainTester main.cpp)
target_link_libraries(toolchainTester ${ADDITIONAL_PROFILE_LIBRARY})
But then getting the error: mingw32-make.exe[3]: *** No rule to make target 'C:/qnx700/target/qnx7/armle-v7/usr/liblibprofilingS.a', needed by '../out/profileout'. Stop.
Got anyone any more ideas? Thanks in advance.
If there anyone out there, looking for the same workaround how to manage different flags and libraries for different profiles, this is one working way:
First set variables in the toolchainfile
SET(ADDITIONAL_PROFILE_LIBRARY "$ENV{QNX_BASE}/target/qnx7/armle-v7/usr/lib/libprofilingS.a")
SET(ADDITIONAL_CXX_FLAGS "-finstrument-functions")
The call/overright them in the CMakeLists.txt
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}" "${ADDITIONAL_CXX_FLAGS}")
add_executable(toolchainTester main.cpp)
target_link_libraries(toolchainTester ${ADDITIONAL_PROFILE_LIBRARY})

Compiler Flags from CMakeLists.txt don't appear in CMake-Gui or CMakeCache.txt

I just started to learn CMake and thought I would have understood the basic process of first writing the CMakeLists.txt, then configuring to generate the CMakeCache.txtand at the end generating the Makefiles.
However, when I try to apply it to the following CMakeLists.txt, I'm not getting the expected results and I'm not sure what is going wrong. Part of the CMakeLists.txt looks like this:
# compiler flags
if (CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fpermissive -Wall -Wformat-security")
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.8)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-local-typedefs")
endif()
endif()
Since I'm using gcc/g++ 4.7.3, the compiler flags from the first if-statement should be set. But if I configure this with CMake-Gui, there are no compiler flags pre-defined whatsoever. The same happens when I out-comment the if-statements and just keep the set(CMAKE_CXX_FLAGS ...).
When searching the CMakeCache.txt for any -std=c++11 flags, I don't get any results, too.
Why does this happen? What's the point of specifying compiler flags inside the CMakeLists.txt when they aren't used? Or am I getting something completely wrong and they are used, but then I don't know why and how I could check.
When generating the actual (Eclipse CDT) project with make and importing it to Eclipse, I'm getting error messages that C++11 features can't be resolved, the __cplusplus macro contains the value 199711 so the -std=c++11 flag is obviously not used.
The flags you specified in the CMakeLists.txt file are probably correctly used by the compiler. You can't see them directly in CMakeCache.txt but:
You can see command lines by running make VERBOSE=1 instead of standard make
Also, you can set CMAKE_VERBOSE_MAKEFILE to 1 to enable printing of commands (this can be found by checking "Advanced" in CMake GUI)
As #Angew said, if you really want to see the updated flags in the CMake GUI, set your variables with CACHE FORCE
As an example, i use this kind of configuration in a project for some month, and never had problem:
if(MSVC) # MSVC compiler (Win32 only)
# Display more warnings
set(CMAKE_CXX_FLAGS "/W3")
elseif(UNIX OR CMAKE_COMPILER_IS_GNUCXX) # Clang OR Gcc (Linux, Mac OS or Win32 with MingW)
# Enable C++11 and displays all warnings
set(CMAKE_CXX_FLAGS "-Wall -std=c++11")
if(APPLE) # Clang / Mac OS only
# Required on OSX to compile c++11
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -mmacosx-version-min=10.7")
endif(APPLE)
endif()
Update:
Starting with CMake 3.0, you can replace set(CMAKE_CXX_FLAGS "...") by add_compile_options(-std=c++11)
CMake 3.1 introduced a new syntax to configure the compiler with specific C++ version:
set(CMAKE_CXX_STANDARD 11)
You can first, set the variable to a value only if it is not in cache already. The last parameter is the description which we don't need since we'll override it anyway.
set(VARIABLE "Hello World!" CACHE STRING "")
Then force the value into cache using its existing value from the line above. Since that is cached, users can still change the variable and it won't be set back every time.
set(VARIABLE ${VARIABLE} CACHE STRING "Description." FORCE)
This is a bit hacky in CMake as you can see, but it works reliably.

how to include NTL using CMake

I use this line to compile a simple program:
g++ main.cc -lntl -lm -lgmp
How do you include this into CMake?
find_package(NTL REQUIRED)
find_package(GMP REQUIRED)
Doesn't work. And gives the following error:
CMake Error at CMakeLists.txt:30 (find_package):
Could not find module FindNTL.cmake or a configuration file for package
NTL.
...
.
and
SET(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -std=c++0x -lntl -lm -lgmp)
Doesn't work either (but I think it's just wrong in general).
Thank you!
If ntl, m, and gmp libraries are usually installed to somewhere in the default path (e.g. /usr/ or /usr/local/), you could simply do something like:
cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
project(Test)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
add_executable(test main.cc)
target_link_libraries(test ntl m gmp)
This is probably closest to your original g++ command, but it isn't very robust however; if any of the libraries aren't found, you won't know about it until you try linking. If you want to fail at configure time (i.e. while running CMake), you could add find_library calls for each of the required libs, e.g.
find_library(NTL_LIB ntl)
if(NOT NTL_LIB)
message(FATAL_ERROR "ntl library not found. Rerun cmake with -DCMAKE_PREFIX_PATH=\"<path to lib1>;<path to lib2>\"")
endif()
You'd then have to change your target_link_libraries command to
target_link_libraries(test ${NTL_LIB} ${M_LIB} ${GMP_LIB})
You'd probably also then have to do a find_file for one of each lib's header files to find out the appropriate path to add via the include_directories command (which translates to -I for g++).
Note, it's important to put quotes around the extra CXX_FLAGS arguments, or CMake treats them like separate values in a list and inserts a semi-colon between the flags.
For further information about find_library, find_file, etc. run:
cmake --help-command find_library
cmake --help-command find_file
Regarding your error:
It doesn't look like there's a FindNTL.cmake module included with CMake. That means you'll have to either:
Write your own FindNTL.cmake,
Find another that somebody else has written,
Hack together a solution that:
Checks if NTL is installed
Provides link targets, relevant flags, etc.
From a (rather quick) Google search, it appears somebody has an NTL module for CMake. Since NTL use GMP, you will probably need the associated GMP module for CMake. It doesn't look like a fully-featured CMake module, but it also appears to be the best thing out there (again, it was a quick Google search, and I don't use NTL).
To use, you'll want to add some things to your CMakeLists.txt:
# Let CMake know where you've put the FindNTL.cmake module.
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/build/CMake/Modules")
# Call the FindNTL module:
find_package(NTL REQUIRED)
SET(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -std=c++0x -lntl -lm -lgmp)
Yes, this is wrong. You don't want to be setting your CXX_FLAGS with linking directives. I would use:
SET ( CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -std=cxx0x )
to set the Cxx standard you want to use. To actually link to libraries, you'll want to:
Ensure that you've found the libraries (with the relevant find_package ( FOO ) lines)
Link those against your target, like this:
# Build the Foo executable. (Or library, or whatever)
add_executable (FooEXE ${Foo_SOURCES} )
target_link_libraries (FooEXE
${bar_LIBRARIES}
${baz_LIBRARY}
)
Please note! ${bar_LIBRARIES} and ${baz_LIBRARY} is not a typo; there's no standard way of setting the relevant libraries in the FindFOO.cmake modules, which is, in my opinion, an annoyance. If one doesn't work, try the other, or, worst case, have a look in the relevant FindFOO.cmake file (there's a bunch installed with CMake by default) to see what each one uses. With the link i provided, you can use ${NTL_LIB} and ${GMP_LIB}.