This has been on my mind for years. It is about using resource files with CMake in code such that they can be accessed when running in the build directory and when installed.
Say you have files necessary to run your program in a top-level directory called res. It contains res/file1.txt and res/file2.txt. You build this program and the resources are copied to the build directory from which the program can be run during development. Then, the program is installed with the resources into /usr/share/my_program or something and the program runs by accessing those files. The question is what to do in the source code so that the same code works when running from the build directory and when installed. I've seen several different answers for how to handle resources but each seems to have a flaw:
The program searches in some preconfigured absolute directory such as CMAKE_INSTALL_PREFIX/share/my_program and must be installed first to run, not ideal for easy use during development.
Finding some way to use relative paths to the binary but I don't see how this would work since the build tree will not mirror the installed file paths, being installed to the bin and share directories.
Differentiating between the two with a CMake variable so that it searches for a relative path in one scenario or the installed location with another. This could possibly just be the debug/release variable. It would also require rerunning CMake before installing to rebuild with the new resource paths.
Baking the files into the executable. Seems uneccessary when dealing with most resources since they could just be opened instead and may be inconvenient with large directories of files.
Is one of these the best solution? Is there something I'm not misunderstanding? I've always been under the impression programs should be able to be run from the build directory before installing to see if they work. If possible, I would like to know what both the CMake and C/C++ code would look like, such as open("my_resource_location"); and my_cmake_command(). Most answers I've seen relating to resources don't include both.
It seems to me that what you are looking for is a relocatable build.
One way to achieve this in CMake is to use configuration files to incorporate resource paths in your code. I have prepared a minimal and reproducible example to give you an idea of how it works.
First, imagine you have the following program structure on disk:
main
CMakeLists.txt
main.cpp
data
CMakeLists.txt
Resources.h.in (Thats the configuration file)
resource.txt
The resource.txt file only contains a string. The program simply opens that file, reads its content and displays it to the terminal. So you have something like:
#include <fstream>
#include <iostream>
#include <string>
#include <Resources.h>
const std::string MY_FILE = "/resource.txt";
int main(int argc, char *argv[])
{
std::string line;
std::ifstream myfile (RESOURCE_PATH + MY_FILE);
if(myfile.is_open())
{
while(std::getline(myfile,line))
{
std::cout << line << '\n';
}
myfile.close();
}
else
{
std::cout << "Unable to open file";
}
return 0;
}
Note that this file #includes a Resources.h file, but that this file does not exist in our project. However, we have a Resources.h.in configuration file that CMake will turn into a Resources.h file on generation. This generated file will then contain the resource path we need inside the RESOURCE_PATH variable. Here is what the configuration file contains:
#define RESSOURCE_PATH "#CMAKE_INSTALL_PREFIX#/#CMAKE_INSTALL_DATADIR#"
A simple #define with this strange #something# notation. On generation, CMake will make the appropriate substitution and write the result to Resource.h, which we can then consume. The main CMakeLists.txt file is pretty straight forward:
cmake_minimum_required(VERSION 3.10)
project(example)
# Sets up classic installation directories. However,
# you can override them:
include(GNUInstallDirs)
add_subdirectory(data)
add_subdirectory(main)
I use the GNUInstallDirs, which populates variables such as CMAKE_INSTALL_DATADIR, for convinience. The main/CMakeLists.txt file:
add_executable(example main.cpp)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/Ressources.h.in" "Ressources.h")
target_include_directories(example
PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
)
install(
TARGETS example
DESTINATION ${CMAKE_INSTALL_BINDIR}
)
This is where the configuration file is set up. The target_include_directories just below is added for CMake to be able to find the generated Resource.h file, which will reside in the build directory. Finally, the data/CMakeLists.txt file:
install(
FILES resource.txt
DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}
)
only has a simple installation instruction. From there, you can install as usual. Simply create a build directory and use CMake as usual. For example, if the build directory is located at the same level as the project:
mkdir ../build
cd ../build
cmake ../project
make
sudo make install
Since by default (on Linux) we have:
CMAKE_INSTALL_PREFIX=/usr/local
CMAKE_INSTALL_BINDIR=bin
CMAKE_INSTALL_DATADIR=share
The program will be installed to /usr/local/bin and the resource to /usr/local/share. Note that the Resource.h file will give you the correct path once generated by CMake. Whats is nice now, however, is that you can modify the value of CMAKE_INSTALL_PREFIX to point to somewhere else by calling CMake like so (for example, this could be a dev build directory):
cmake -DCMAKE_INSTALL_PREFIX=/home/someone/example ../project
and you will have these values instead:
CMAKE_INSTALL_PREFIX=/home/someone/example
CMAKE_INSTALL_BINDIR=bin
CMAKE_INSTALL_DATADIR=share
The Resources.h file will also be updated to point to the right location, and your resource will still be found.
Related
I try to build large project with many directories and sub-directories, some of them are being used to create different libs.
using GNU make using same compilation flags. most of the folder are successfully built, but in a specific folder the build failed, and it gets many errors that some definitions is missing.
For instance, the first error is:
In file included from /usr/include/c++/4.8.2/cwchar:44:0,
from /usr/include/c++/4.8.2/bits/postypes.h:40,
from /usr/include/c++/4.8.2/iosfwd:40,
from /usr/include/c++/4.8.2/memory:72,
...
/usr/include/wchar.h:614:9: error: ‘__gnuc_va_list’ has not been
declared
__gnuc_va_list __arg
)
this error comes from simple #include at of the files in this lib, but same sort of error happens for any file and for different standard library headers.
The strength thing is that this project was completely successfully built before i pulled some updates from remote repository. at this merge no changes were done to this file.
Tried to use
g++ -E /usr/include/wchar.h | grep __gnuc_va_list | head -1
result is:
typedef __builtin_va_list __gnuc_va_list;
As i see at this answer, __builtin_va_list shuould be created by gcc, and it probably did create it- otherwise many other files were failed to compile
I can't understand why it happens and why only at this folder/lib.
I had same problem for libhydrogen on windows and mingw 5.3.0, you can just define it to:
typedef void* __gnuc_va_list;
... then it compiled and the delivered tests.c worked.
i think for unix systems its (unsure):
typedef char* __gnuc_va_list;
I have a CMake project that looks like this:
project/
CMakeLists.txt
subprojectA/
CMakeLists.txt
include/
headerA.hpp
src/
libraryA.cpp
subprojectB/
CMakeLists.txt
src/
mainB.cpp
The "library" subproject, A, is compiled as a static library, becoming libsubprojectA.a. The "main" project, B, is compiled as a binary and depends on the library. mainB.cpp includes a reference to headerA.hpp.
Here is subprojectA/CMakeLists.txt:
project(SubProjectA)
include_directories(include)
add_library(subprojectA STATIC src/libraryA.cpp)
set(${PROJECT_NAME}_INCLUDE_DIRS
${PROJECT_SOURCE_DIR}/include
CACHE INTERNAL "${PROJECT_NAME}: Include Directories" FORCE)
And here is subprojectB/CMakeLists.txt:
project(SubProjectB)
include_directories(${SubProjectA_INCLUDE_DIRS})
add_executable(mainBinary src/mainB.cpp)
target_link_libraries(mainBinary subprojectA)
The main Project CMakeLists.txt looks like:
project(Project)
add_subdirectory(subprojectB)
add_subdirectory(subprojectA)
Note that subprojectB, the main project, is listed before subprojectA.
Here's the problem. When I first run "cmake" on this project, ${SubProjectA_INCLUDE_DIRS} is not set within SubProjectB.
What I think is happening is that the CMakeLists for SubProjectB loads first, when ${SubProjectA_INCLUDE_DIRS} has not yet been set. It sets its own include path to an empty string as a result. However, even though libsubprojectA.a gets built successfully before mainBinary, the include path was already set empty beforehand. As a result, I get this error when trying to make mainBinary:
subprojectB/src/mainB.cpp:1:23: fatal error: headerA.hpp: No such file or directory
#include "headerA.hpp"
^
It's a workaround to put subprojectA before subprojectB in the main Project CMakeLists in the declarative world of CMake. What I really want is to know the proper way to indicate to CMake that the include_directories(${SubProjectA_INCLUDE_DIRS}) line depends on the definitions that exist inside SubProjectA's CMakeLists. Is there a better way to do this?
If you want to express that include directory subprojectA/include is an interface of the library subprojectA, attach this property to the target with target_include_directories command:
subprojectA/CMakeLists.txt:
project(SubProjectA)
add_library(subprojectA STATIC src/libraryA.cpp)
# PUBLIC adds both:
# 1) include directories for compile library and
# 2) include directories for library's interface
target_include_directories(subprojectA PUBLIC include)
So any executable(or other library) which linked with subprojectA will have this include directory automatically:
subprojectB/CMakeLists.txt:
project(SubProjectB)
add_executable(mainBinary src/mainB.cpp)
target_link_libraries(mainBinary subprojectA)
Of course, for use last command properly you need to process directory with library before one with executable:
CMakeLists.txt:
project(Project)
add_subdirectory(subprojectA)
add_subdirectory(subprojectB)
Using CMake I'm able to cross-compile and create a static library which depends on other static libraries. However, the resulting static library doesn't include the "content" of the other static libraries. Therefore, I want to create an archive, i.e. a static library, that actually includes everything (all the object files).
To this aim, I've tried to use the following directive in CMakeLists.txt:
add_custom_target(combined ALL COMMAND ${CMAKE_AR} rc libcombined.a $<TARGET_FILE:target1> $<TARGET_FILE:target2> $<TARGET_FILE:target3>)
CMAKE_AR is set to an absolute path where my cross-compiler(archiver?) is; its values is something like: c:/arm/arm-none-eabi-ar.exe
However, when I try to build the project (make && make install), I receive the following error:
'..' is not recognized as an internal or external command, operable program or batch file.
make[2]: *** [src/CMakeFiles/combined] Error 1
If I open the build.make file where the src/CMakeFiles/combined is, I have something similar to this:
src/CMakeFiles/combined:
cd absolute-path-to-the-build-folder/src && ../../relative-path-to-the-archiver-executable/arm-none-eabi-ar.exe rc libcombined.a absolute-path-to-target1-archive absolute-path-to-target2-archive absolute-path-to-target3-archive
If I manually replace the relative path to the archiver executable with the absolute path, the new archive is created correctly. The same happens if I just add quotes in the specified archiver path.
At this point, I think it might be something related to Windows and/or to the make utility which doesn't interpret correctly the command/path.
Therefore, I have the following question:
Why is the CMAKE_AR value converted to a relative path from an absolute path?
How can I solve this without involving external scripts?
About last point, I tried to escape the CMAKE_AR path with quotes, but then I get the error: "COMMAND may not contain literal quotes".
What you want to do is create an object library that depends on your various static libraries in order to get a single library that contains all the dependencies.
I would like to use wxWidgets 3.0.2 library in my project. However I am unable to run even the hello world program. I have downloaded the headers and the appropriate binaries (TDM GCC x64 4.8.1). I've extracted them without any changes. So there are include and libs folders present in my wxWidgets folder. I am using TDM-GCC 5.1 which is setup properly.
When I create a simple console application and only include the main file
#include "C:\wxWidgets\include\wx\wx.h"
I get an error
C:\wxWidgets\include\wx\wx.h|14|fatal error: wx/defs.h: No such file or directory|
Which is quite reasonable, as defs.h is in the same folder as wx.h and there is no wx folder inside. Do I need to rearrange the file structure?
Is the compiler problem here (5.1 used instead of 4.8.1)?
For three days I am running trough different tutorials, and all the time I get this or similar errors. How to set it up properly?
The whole code is just:
#include <iostream>
using namespace std;
#include "C:\wxWidgets\include\wx\wx.h"
int main()
{
cout << "hello" << endl;
return 0;
}
You should never include wxWidgets (or any other library) header files using full paths. Instead you should have just
#include <wx/wx.h>
in your code and set up your compiler headers search path to include c:\wxWidgets\include directory. Notice that you will also need to add c:\wxWidgets\lib\gcc481_lib\mswu or similar to the includes path, depending on the exactl configuration you're using (e.g. it could be gcc481_dll if you're using the DLL build).
I have 2 libraries that share same source files:
# src/lib_mt/Makefile.am:
libppb_la_SOURCES = rphs_mt.c timer_mt.c
# src/sipplib/Makefile.am:
libsipp_a_SOURCES = ../lib_mt/rphs_mt.c ../lib_mt/timer_mt.c
Each source file compiled twice. First for lib_mt with -fPIC, second for sipplib without -fPIC.
Object files for each library created in corresponding directory.
Eventually subdir-objects becomes default. How to keep current behavior for these 2 source files? Some explicit rule maybe?
There is no way to disable that the moment it becomes the default. What you can do instead is migrate this to a non-recursive Automake buildsystem. At that point, it will know that there are different targets compiling the same source files with different flags (it requires AC_PROG_CC_C_O to be called in configure.ac.)
Alternatively, the hacky version is to create a src/sipplib/rphs_mt.c file that only contains
#include "../libmt/rphs_mt.c"
so that it is actually a separate build target.