My project is quite simple but I like to keep files in different folders for clarity. For instance I have three folders.
An Output folder that contains all the classes used for the output. (for now only Output.cc and Output.h).
A Math folder, containing all of the classes related to math. (vector.cc, vector.h, randomgen.h, randomgen.cc, etc)
A Tests folder where there are different cpp files each containing a main function. (Output_test.cc, vector_test.cc, etc)
How can I create a CMake script that complies all of the different main function of the different test programs using the classes that are in different folders?
In addition, I didn't see where the compiler, and its options, are specified in a CMake file.
How to specify the compiler?
There are a few ways to specify the compiler you want to use. Settings environment variables, defining compiler variables, or designating a generator.
Settings Environment Variables
There are two ways to use environment variables to help CMake determine which compiler to use during a CMake configuration. Using the PATH variable or the CC and CXX variables.
PATH
Make sure the path to your desired compiler is first in the list. If you don't want to modify your path, then use the 2nd option.
CC & CXX
CMake reads the variables CC and CXX to determine the path for the C compiler and C++ compiler respectively. Note that the first time CMake is configured for a project it will cache these paths, and look to the cache first for all future configurations. So if you wish to change the compiler path, be sure to delete the cache file CMakeCache.txt. As HughB pointed out, there is a good example of this given by Guillaume
Defining Compiler Variables
Similar to using CC and CXX, there are CMake variables that can be defined at the commandline to choose the desired compiler. They are CMAKE_C_COMPILER and CMAKE_CXX_COMPILER. You can set them using the -D option and they use the same values as CC and CXX. Note, just like CC and CXX these are cached after the first CMake configuration.
Example
cmake -DCMAKE_CXX_COMPILER=/usr/bin/g++4.6/g++ -DCMAKE_C_COMPILER=/usr/bin/gcc4.6/gcc <path to src>
Designating a Generator
The last way to choose the compiler is by using the -G option to select a generator. There are numerous generators to choose and I recently answered a question about them. I'm not going to go into too much detail about them in this answer, but you can read my other answer for more details.
Example
cmake -G "Unix Makefile" <path to src>
Don't Hardcode the Compiler
I recommend resisting the urge to "hardcode" the compiler in the CMakeLists.txt files. CMake is meant to be compiler independent and you should be setting the compiler information external of the CMake files. Use one of the methods mentioned above to keep your CMake files portable and to avoid confusion if yourself or someone else wants to build the project with a different compiler.
Related references. (Disclaimer: Written by me)
What is a CMake generator
Understanding the Purpose Behind CMake
How to Compile Multiple Executables?
As HughB mentioned use add_executables. You could also create separate libraries for each folder group, there are many ways to organize your project. I'm going to keep it simple and give an example that builds two executables in the same project.
For this example I created 5 files:
include/Helloworld.h
#include <stdio.h>
src/HelloWorld.cpp
#include "HelloWorld.h"
int main()
{
printf("Hello World!\n");
return 0;
}
include/HelloWorld2.h
#include <stdio.h>
src/HelloWorld2.cpp
#include "HelloWorld2.h"
int main()
{
printf("Hello World2!\n");
return 0;
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
# This is required to compile and must contain the paths to
# all of the header files
include_directories(include)
# These are optional, but are added to be passed to the
# add_executable command so that the headers are accessible
# from within the project
set(HW_INCLUDES include/HelloWorld.h)
set(HW_INCLUDES2 include/HelloWorld2.h)
project(HelloWorlds)
# Required sources
set(HW_SOURCES src/HelloWorld.cpp)
set(HW_SOURCES2 src/HelloWorld2.cpp)
add_executable(HelloWorld ${HW_SOURCES} ${HW_INCLUDES})
add_executable(HelloWorld2 ${HW_SOURCES2} ${HW_INCLUDES2})
When this project is built there will be two executables: HelloWorld.exe and HelloWorld2.exe.
Consider putting the code that will be used in different program mains in a library. Use the add_library command to do make a library. You could have a directory hierarchy like this:
TopDir
CMakeLists.txt
MyLib
CMakeLists.txt
vector.cc
vector.h
....
MyExe1
CMakeLists.txt
main1.cc
MyExe2
CMakeLists.txt
main2.cc
Use add_subdirectory in your top level cmakelists.txt to traverse the directories. In the dirs that build executables, use add_executable and target_link_libraries. If you named the library MyLib then your target_link_libraries command would look like
target_link_libraries( exe1 MyLib )
In regard to overriding the compiler, see how to specify new gcc path for cmake
Related
So from what I understand, a static compiled mylib.a file should just be usable as plug-and-play. But I can't use it without the source code, I'm trying to avoid that.
I adapted this tutorial to compile using CMake with the following directory structure:
/
lib
libmy_math.a
main.cpp
my_math.h
my_math.cpp
CMakeLists.txt
The contents of the files are exactly as in the tutorial. The only added file is CMakeLists.txt which basically just runs the library compiling part before building:
CMakeLists.txt
cmake_minimum_required(VERSION 3.21)
project(Test)
### Uncomment this ONCE to compile a libmy_math.a file
# add_library(my_math STATIC my_math.cpp)
add_executable(Test main.cpp)
find_library(MY_MATH_LIB my_math HINTS ./lib)
target_link_libraries(Test PUBLIC ${MY_MATH_LIB})
As the CMake file says, uncommenting Line 5 compiles the library, which is then linked to my code when compiling.
I'm trying to package my code so that I don't show my client the source code for the compiled library (my_math), but if I delete the my_math.h and my_math.cpp files, I get a "file not found" error on import:
/Users/Joe/Desktop/Test/main.cpp:1:10: fatal error: 'my_math.h' file not found
#include "my_math.h"
^~~~~~~~~~~
I thought you could compile libraries without needing the source code. What am I missing here?
A static library does not contain each and every definition a header can contain - think of macros, etc. Thus you still need the header. However, you don't need the .cpp anymore to link the library.
Besides the problem stated in Iorro's answer the find_library call here will not work as expected.
find_library(MY_MATH_LIB my_math HINTS ./lib)
target_link_libraries(Test PUBLIC ${MY_MATH_LIB})
What this does is, while running CMake, look for the my_math.a file. However, at that point of the build process, my_math.a has not been built yet. You're just running CMake for the first time, so all you have is source files. Nothing has been compiled yet.
What you want to do instead is link to the library target directly, as CMake already knows about that and can resolve the dependency on its own. So you need to replace the two lines above with:
target_link_libraries(Test PUBLIC my_math)
This now introduces a curious asymmetry in how your test consumes the library (as it is part of the same build) vs. how your clients consume the same library (which are not part of the same build and thus need to do a find_package call before they can use it). Look at CMake's config-file packages for a mechanism that allows you to restore symmetry here, at the cost of a significant amount of boilerplate required in your CMake script.
I am attempting to use the much stricter -Weffc++ compiler flag for a project. The issue that I'm having is that the project uses a bunch of open-source libraries. These libraries in no way adhere to the stricter requirements, and I have no intention of modifying them to comply.
Is there a way to target a specific set of files with a compilation option using CMake?
Furthermore, the compilation is conditional on what is compiling the program. The program can either be compiled as an executable, or it can be compiled as a library so that it can be used by the automated unit test fixtures employed.
Is there a way to target a specific subdirectory added with the add_subdirectory CMake command, with specific compile flags?
Thank you!
If you can modify all relevant targets, I recommend using target_compile_options, target_compile_definitions and or/ target_link_options to achieve your goals. You could introduce a cmake function, if the same compiler/linker options are required for multiple targets.
target_compile_options(my_target PRIVATE -Weffc++)
If you want to target a subdirectory, you should be able to use add_compile_options/add_compile_definitions/add_link_options in a new scope using a function to restruct the scope for the settings. Note that you only need a function, if the options must not apply to every target added in the same CMakeLists.txt file after adding the subdirectory and to targets created in subdirectories added from the CMakeLists.txt file after adding the subdirectory. Using a function anyways doesn't "hurt" though and may even reduce the number of things you need to consider for future edits.
function(add_subdir_with_flags DIR)
add_compile_options(-Weffc++)
add_subdirectory(${DIR} ${ARGN}) # ARGN used to allow specifying the build dir for the subdir
endfunction()
...
add_subdir_with_flags(subdir1)
add_subdir_with_flags(subdir2 builddir2)
Note: If you want to go down to the source file level, you could do so by adding compile options on a per-file basis, but you should only use this option, if not all source files of a target should be compiled with these options.
# apply options only to foo.cpp and bar.cpp
set_source_files_properties(foo.cpp bar.cpp PROPERTIES COMPILE_OPTIONS -Weffc++)
I have worked on a project where I was using g++ to compile C code in files that end in .c. The reason is that I'm told that g++ has better warning messages.
I am switching the build process for this project to use CMake. I found that initially CMake wanted to use gcc to compile C files. This failed because of things like declaring variables at use time. So I tried to use g++ to compile C files by using the setting
set(CMAKE_C_COMPILER_INIT g++)
in the CMakeLists.txt file. But this results in the error message:
#error "The CMAKE_C_COMPILER is set to a C++ compiler"
I have been renaming my .c files to .cpp to fix this problem as that seems to be the easiest way for me to make things work, and perhaps the best way too. But I was wondering if it is possible to force CMake to use g++ to compile C files.
You should not override the compiler for this purpose. If you really need to compile your C files as C++ then you should teach cmake that your files belong to C++ language:
set_source_files_properties(filename.c PROPERTIES LANGUAGE CXX )
To have cmake treat all C files as C++ files use:
file(GLOB_RECURSE CFILES "${CMAKE_SOURCE_DIR}/*.c")
SET_SOURCE_FILES_PROPERTIES(${CFILES} PROPERTIES LANGUAGE CXX )
If you need to switch the whole project, set it in the project directive:
project(derproject LANGUAGES CXX)
set_source_files_properties
The CMake setting of (my) choice here would be the set_source_files_properties command. https://cmake.org/cmake/help/latest/command/set_source_files_properties.html
set(qpid_dispatch_SOURCES
alloc.c
alloc_pool.c
aprintf.c
amqp.c
atomic.c
# [...]
)
set_source_files_properties(${qpid_dispatch_SOURCES} PROPERTIES LANGUAGE CXX)
add_library(qpid-dispatch OBJECT ${qpid_dispatch_SOURCES})
As described in the linked docs, CMake 3.18 changed the scoped effect of set_source_files_properties. See the DIRECTORY and TARGET_DIRECTORY options. Therefore, to apply source file property recursively to all files in your project, your CMakeLists.txt should look something like this
cmake_minimum_required(VERSION 3.20)
project(qpid-dispatch LANGUAGES C CXX)
# [...]
add_subdirectory(src)
add_subdirectory(tests)
add_subdirectory(router)
# [...]
file(GLOB_RECURSE CFILES "*.c")
set_source_files_properties(${CFILES}
DIRECTORY src tests router
PROPERTIES LANGUAGE CXX)
I'm (cross-)compiling a shared C library with support for many different platforms which is handled by an hierarchy of CMakeLists files. In those files, several platform specific compiler flags are conditionally produced (with add_definitions()). I can successfully compile and link the source code leading to an appropriate .so file.
But to use the library in any project, I need to provide the right header files, too. The following install command of CMake selects the right header files to copy but does not apply the replacement of preprocessor defines/includes:
install(FILES ${headers} DESTINATION include/mylibrary)
So how can I generate/install the "post-compiled" header files?
What I thought of so far:
As add_definitions() should stack my -D's in the COMPILE_DEFINITIONS variable, maybe running a foreach loop on the copied raw headers and replace the define/include placeholders?
Using add_custom_command() to apply some logic before copying?
Edit: As pointed out by Tsyvarev, there is an answer quite near to my needs here, but unfortunately not quite it. In summary, the answer gives 2 options:
Include a special 'config' header in all of the library's headers and leverage the cmakedefine command to call configure_file() on this header. I can't use this approach because I don't want to alter the library headers.
Create a target-specific .cmake file which helps external projects in including the right headers together with all necessary -D defines. I can't use this approach either, because my external projects do not use cmake for building. Plus, I wish to create a library that is as easy to include as possible.
Any other thoughts?
Edit 2: I may have to elaborate on my statement, that the install command of CMake is not replacing defines. Take the following example:
//sampleheader.hpp
#ifndef SAMPLEHEADER_HPP_
#define SAMPLEHEADER_HPP_
#include OS_SPECIFIC_HEADER
//...
Now I have a CMakeLists.txt file that does something like this:
# ...
if (${OS} MATCHES "arm-emblinux")
add_definitions(-DOS_SPECIFIC_HEADER="emblinuxHeader.hpp")
elseif (${OS} MATCHES "linux")
add_definitions(-DOS_SPECIFIC_HEADER="linuxHeader.hpp")
endif()
# ...
Everything compiles fine, but when the install command above gets called, I have a header file in my ../include/ directory still with OS_SPECIFIC_HEADER placeholder in it. And of course, this cannot be properly included in any development project.
I would like to build an executable from static libraries (i. e. .a-files) only. This is possible, because the main() function is contained in one of these libraries.
The add_executable() function requires me to provide at least one source file. But this is not what I want to do.
There is no way to do it without a hack. You need at least one *.c or *.cpp file.
What I do is make a dummy null.cpp file (zero bytes) and use that. You can also use /dev/null but that only works on Linux.
file(WRITE null.cpp "")
add_executable(tester
null.cpp
)
target_link_libraries(tester
-Wl,--whole-archive
libtest1
libtest2
libtest3
libtest4
-Wl,--no-whole-archive
gtest_main
)
There are mainly two reasons why a source file is enforced by CMake:
To determine the LINKER_LANGUAGE from the file ending(s)
Not all compilers do support an object/library only link step (for details see below)
And if you move the main() function to library please keep the following in mind: Why does the order in which libraries are linked sometimes cause errors in GCC?
So if you build the libraries with CMake in the same project, I would recommend to change your libraries (at least the one containing your main() function) to an object library:
cmake_minimum_required(VERSION 2.8.8)
project(NoSourceForExe)
file(WRITE main.cc "int main() { return 0; }")
add_library(MyLibrary OBJECT main.cc)
add_executable(MyExecutable $<TARGET_OBJECTS:MyLibrary>)
The add_library() documentation lists a warning here:
Some native build systems may not like targets that have only object files, so consider adding at least one real source file to any target that references $<TARGET_OBJECTS:objlib>.
But those are rare and listed in Tests/ObjectLibrary/CMakeLists.txt:
# VS 6 and 7 generators do not add objects as sources so we need a
# dummy object to convince the IDE to build the targets below.
...
# Xcode does not seem to support targets without sources.
Not knowing which host OS(s) you are targeting, you may just give it a try.
References
CMake Object Lib containing main
CMake/Tutorials/Object Library