2

I am using CMake to compile and test a C++ project. Let's say I have a structure like this:

inc/
    ..
src/
    ..
data/
    dataFile

File dataFile is accessed from code, and will be installed under <prefix>/share or similar. In my code, I do something like:

std::ifstream dataFile(DATAFILE_PATH);

I get that I can configure DATAFILE_PATH with CMake (with header template, or compiling options, or anything) so I don't have to hardcode the file path in my code directly. Also, I know how to install files to the install tree, and that it is possible to copy files to copy files to build tree, for example for running tests o simply running the executable from build tree.

My question is, how should I configure CMake, or maybe my C++ code, so I can compile my program and run and test it from build tree (accessing a dataFile copy within build tree) and from install tree (accessing the dataFile installed copy under <prefix>/share)? Is there any pattern or technique implemented in CMake that I am missing?

Thanks.

Marc
  • 16,170
  • 20
  • 76
  • 119
jdehesa
  • 58,456
  • 7
  • 77
  • 121

1 Answers1

1

I do not know about the CMake particulars about this, so perhaps I'm not giving you the simplest solution. But I can think of two ways of handling this that do not require recompiling the program between the test and the install.

The first is to not use a single path, but a list of them, and use the first one that can be successfully opened.

const char* datafile_paths[] = { DATAFILE_PATH };
...
std::ifstream dataFile;
for(int i = 0; i < sizeof(datafile_paths)/sizeof(char*); i++)
{
    data.open(datafile_paths[i]);
    if(data.is_open()) break;
}
if(!data.is_open()) handle error;

Here DATAFILE_PATH could look like this, for example "/foo/bar/data.dat", "/foo/data.dat", i.e. a comma-separated list of paths.

The other is to use an environment variable instead. For some projects, this might be preferrable as it is more flexible, letting the user choose where to keep the resource files. But it typically means .bashrc or similar needs to be modified, which is a bit icky.

char * datapath = getenv("MYPROJECT_DATAPATH");
if(!datapath) handle error;
std::ifstream data(datapath);

Typically one would not use this technique alone, but combine it with the one above, first checking for the environment variable, and falling back on the compiled-in path list if that fails.

amaurea
  • 4,950
  • 26
  • 35
  • I guess there is no better way to do this. I have implemented this with a couple of functions, `bool runningInBuildTree()` and `std::string executableFilePath()`, using information in http://stackoverflow.com/questions/1023306/finding-current-executables-path-without-proc-self-exe/1024937#1024937 – jdehesa Nov 05 '12 at 09:49