Is it possible to replace certain symbol to different symbol with CMake? - c++

I'm trying to make an executable with CMake.
This executable requires full path to file which is located in the project.
int main()
{
std::ifstream("fullpath_to_file");
//....
}
I think if CMake can replace certain symbols in source code with user specified string, there will be no need of hard-coding fullpath.
For example, if CMake can replace ${CMAKE_PROJECT_DIR} in source code(cpp) into a cmake's variable like ${PROJECT_SOURCE_DIR}, then I would be able to write source like this.
int main()
{
std::ifstream("${CMAKE_PROJECT_DIR}/input/my_input.txt");
//....
}
Is there any way to do this?
Thanks.

A good way to expose as many CMake variables to you source files as you want is creating a configuration header to your project. First, create a template to the header, call it say config.h.in and define the symbols you want CMake to set at build time, for exemple the contents of config.h.in your case can be
#ifndef __CONFIG_H__
#define __CONFIG_H__
#define PROJECT_DIR #CMAKE_PROJECT_DIR#
#endif
then add to your CMakeLists.txt an instruction to change this configuration header and replace the value between the #'s with the value of the corresponding cmake variable. For instance, put the following in CMakeLists.txt:
include_directories(${CMAKE_BINARY_DIR})
config_file(
${CMAKE_SOURCE_DIR}/config.h.in
${CMAKE_BINARY_DIR}/config.h
)
Then CMake will create config.h with the definitions for the values you want to acces from your sources. Now all you have to do is: include config.h in your c++ sources and use the PROJECT_DIR macro as you expect.

No, CMake cannot do this directly. And you shouldn't do it, because you would have to place your source code within your build directory instead of your source directory.
The usual way - with respect to C++ and CMake - is to use a macro FOO_PATH in your C++ code and
pass -DFOO_PATH=<path_to_foo> via CMake.
add a define #cmakedefine FOO_PATH FOO_PATH to your configure file config.h, define FOO_PATH within your CMake code, and regenerate your config.

How do you do this when using ctest. In this instance there is no .h file to include, but needs to modify .cc files that are compiled? Such that it does not modify the original ${CMAKE_SOURCE_DIR} files
add_compile_definitions(TEST_DATA_DIR="${CMAKE_SOURCE_DIR}/data")
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test_file.cc
${CMAKE_CURRENT_BINARY_DIR}/test_file.cc)
and in C++:
std::string filename = "TEST_DATA_DIR/data.txt";
std::cout << filename << std::endl;
...
outputs "TEST_DATA_DIR/data.txt"

Related

Remove a source file after adding it with target_sources() in CMake

I am using CMake 3.16.
I add my source files to my target with target_sources(). I would like later in my CMakeLists.txt to remove a source file I previously added with target_sources().
For example:
target_sources(my_target PRIVATE main.c abc.c def.c ghi.c)
# Later...
# Remove def.c from the previously added source files.
Is there a way to do this ideally without setting a custom "sources" variable and removing it from this list with list(REMOVE_ITEM ...)?
EDIT:
The context of this question is unit testing static functions in C.
My program is made of a lot of static functions that I want to unit test. To test them, I decided to include the .c file in the test files instead of the .h.
For example:
abc.c:
#include "abc.h"
// several static functions defined here
abc.h:
// some stuff
test_abc.c:
#include "unity.h" // a unit test framework
#include "abc.c" // notice the .c instead of the .h to be able to test the static functions
// the test functions
By doing this, I need to remove in CMake the xxx.c file if I add the test_xxx.c file because otherwise the content of the xxx.c file will be defined 2 times and the linker will not be happy with it.
You can set the source file property HEADER_FILE_ONLY to ON on the source file in question. See also set_source_files_properties().
Since CMake version 3.18, you can also do this if the source file belongs to a target defined in a different directory:
set_source_files_properties(subdir/source.c TARGET_DIRECTORY target-from-subdir PROPERTIES HEADER_FILE_ONLY ON)

CMake/make apply -D flags on header files

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.

How to include a folder path using cmake into a C/C++ program

My c++ program needs a folder path and I like to input from cmake configuration.
For example, my c++ program is
int main(){
std::string pretrained_binary_proto("/home/Softwares/Libraries/caffe-master/models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel");
}
I like to set this folder path using cmake.
/home/Softwares/Libraries/caffe-master/models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel
In my CMakeLists.txt, I have
set(CAFFE_MODEL_PATH "/home/nyan/Softwares/Libraries/caffe-master/models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel")
But I don't see that CAFFE_MODEL_PATH in my ccmake.. configuration.
Then how can I include that path to my program?
The "easy" way:
add_definitions(-DCAFFE_MODEL_PATH=\"${CAFFE_MODEL_PATH}\")
and then use CAFFE_MODEL_PATH constant in the code.
More preferred way if you have many such defines:
Create yourproject-config.h.cmake with contents like #cmakedefine CAFFE_MODEL_PATH.
Use configure_file(yourproject-config.h.cmake yourproject-config.h)
Do not forget to include_directories(${CMAKE_CURRENT_BINARY_DIR})
#include "yourproject-config.h" whenever and wherever you need to access your constants.

Adding header and .cpp files in a project built with cmake

I have built a project using cmake and some libraries.I want however to add some header and .cpp files in the project which I am going to code.What is the easiest way to do it?Can I just create a .cpp and header files and then build again project in Visual Studio? Or due to the fact that project was built using cmake I can't?
You can put all header/source files in the same folder and use something like
file(GLOB SOURCES
header-folder/*.h
source-folder/*.cpp
)
add_executable(yourProj ${SOURCES})
In this way, you can do either of the following two methods to add new added header/source into VS:
need to generate in CMake again.
fake to edit the CMakeLists.txt a little bit, e.g. simply add a space. And then build your solution in VS, it will automatically add new header/source files.
you need to add every .h and .cpp file to CMakeList.txt like this:
# Local header files here ONLY
SET(TARGET_H
Header.h
Plugin.h
messagelog.h
win32application.h
timer.h
)
# Local source files here
SET(TARGET_SRC
Plugin.cpp
messagelog.cpp
win32application.cpp
timer.cpp
)
then configure and build the solution again and reload it in VS.
Although it's a late Response and I just saw it. I am using CLion IDE from JetBrains, which adds these header and .cpp files automatically when you create them. Although it may not be your need, it may be useful for other peoples who see it.

How to Specify the Compiler and To Compile Different Main Functions

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