1

I have the following folder structure:

bin/ <-binary-file is in here
include/
src/
data/
Makefile

In my code, I use relative paths to my data. So "../data/xml/xmlFile.xml". This is fine if I were executing the binary file from the bin/ folder:

brandonto@computer:~/PATH-TO-PROJECT/bin$ ./binary-file 
argv[0] = ./binary-file
dirname(argv[0]) = .

But if I were executing the binary from the main folder (or any other folder that is not the bin/ folder):

brandonto@computer:~/PATH-TO-PROJECT$ bin/binary-file 
argv[0] = bin/binary-file
dirname(argv[0]) = bin

The xml files would not be found because "../data" would now go up one directory from the main folder (or whatever folder you are in when executing the program).

How could I make it so that the binary file could be executed from any directory on my system?

To make the question a little more clear:

brandonto@brandonto-Aspire-S3-391:~/cpp-workspace/sdl-projects/sdl-space-shooter/bin$ ~/cpp-workspace/sdl-projects/sdl-space-shooter/bin/SpaceShooter 
argv[0] = /home/brandonto/cpp-workspace/sdl-projects/sdl-space-shooter/bin/SpaceShooter
dirname(argv[0]) = /home/brandonto/cpp-workspace/sdl-projects/sdl-space-shooter/bin

brandonto@brandonto-Aspire-S3-391:~/cpp-workspace/sdl-projects/sdl-space-shooter/bin$ cd ..
brandonto@brandonto-Aspire-S3-391:~/cpp-workspace/sdl-projects/sdl-space-shooter$ ~/cpp-workspace/sdl-projects/sdl-space-shooter/bin/SpaceShooter 
argv[0] = /home/brandonto/cpp-workspace/sdl-projects/sdl-space-shooter/bin/SpaceShooter
dirname(argv[0]) = /home/brandonto/cpp-workspace/sdl-projects/sdl-space-shooter/bin
Unable to load image ../data/graphics/background/darkPurple.png! SDL_image Error: Couldn't open ../data/graphics/background/darkPurple.png
Unable to load image ../data/graphics/sprites/meteorBrown_big1.png! SDL_image Error: Couldn't open ../data/graphics/sprites/meteorBrown_big1.png

Here, I executed the binary file once from inside the bin/ folder, then once from inside the main folder. The binary ran fine from inside the bin/ folder, but could not find the relative paths to the .png files from inside the main folder.

brandonto
  • 125
  • 2
  • 10
  • You need to add a sniffer for the executable to figure out where it is. Usually that means reading arg[0] (the original call) and perhaps checking some search paths. – Jiminion Feb 16 '15 at 20:30
  • You need to be able to get to (find) the bin folder regardless of where the executable is called. – Jiminion Feb 16 '15 at 20:42
  • Did you write `SpaceShooter` yourself? If not, how about writing a shell script which changes to the `bin` folder and runs `SpaceShooter` there? – lurker Feb 16 '15 at 20:57
  • @lurker Yes, I wrote SpaceShooter. The the program is, the program will only run correctly if I am executing it from the bin/ folder since the paths are "../data/". But normally, when you execute a program, it shouldn't matter where you are executing it from. – brandonto Feb 16 '15 at 21:07
  • @Jiminion How would I do that? I thought my relative paths "../data" would be from where the executable is located, not where it is called. – brandonto Feb 16 '15 at 21:09
  • I don't think so. To complete the path, the program uses the search path that usually (but not always) contains . for the pwd. If you don't have the executable location in the search path, then it has no way of finding it. – Jiminion Feb 16 '15 at 21:12

5 Answers5

3

Probably you are asking a wrong question: the build system has nothing to do with program execution.

However, if you look for an answer, how to make my program to correctly use data, that is located relative to program installation, than here is an answer.

When you program main gets executed, it gets the binary path as the first parameter (index 0). That path can be relative or absolute, but in any case it allows you to find the base directory.

These are also useful links:

Here how you can use first argument:

#include <linux/limits.h>
#include <stdio.h>
#include <string.h>
#include <libgen.h>

int main(int argc, char *argv[])
{
  char datadir[PATH_MAX];
  strncpy(datadir, argv[0], sizeof(datadir));
  dirname(datadir);
  strncat(datadir, "/../data", sizeof(datadir));

  printf("Data dir: %s\n", datadir);

  return 0;
}
Community
  • 1
  • 1
Valeri Atamaniouk
  • 5,125
  • 2
  • 16
  • 18
  • Thanks! This worked. I'd like to add that if you are executing in the /bin/ directory itself, then argv[0]='.' which is an edge case that you must take care of if you are using this method. – brandonto Feb 18 '15 at 21:17
0

I believe that you can find your process id (pid) using the getpid command and perform functions to extract the directory in a manner similar to this question on Ask Ubuntu.

Community
  • 1
  • 1
ojblass
  • 21,146
  • 22
  • 83
  • 132
0

I would have the data associated in some way (organizationally) with the bin directory where the executable resides.

Then, when running the routine, if a complete path is provided (noted by checking arg[0]), then you can find the data directory. If a relative path is provided, then search the search path sequentially until you find the executable, and then you can therefore find the data directory.

No pids needed. (I think this is how Python finds its way, or at least how it used to do so.)

Jiminion
  • 5,080
  • 1
  • 31
  • 54
0

I usually solve this with a program setting. In the good old days I would have these settings in a .ini file which would accompany the executable. Some settings would be configurable from within the program, and all could be edited with a text editor. If the file was missing, or any setting missing, they would be created by default.

For the location of the program's data I would use its full absolute path name. For example it might be

Datapath = D:\os50k

and the program then appends individual file names to the path as necessary.

These days in Windows the System Registry is used for this purpose. However your question is tagged Linux which stores settings in various places, including the program directory.

This question, and this question describe the process more fully.

Community
  • 1
  • 1
Weather Vane
  • 33,872
  • 7
  • 36
  • 56
0

If your paths can be determined at build time, (i.e. your project will never need to be installed to another directory,) you can inject the path through the build system as a preprocessor definition. Here's an example with CMake:

file(TO_CMAKE_PATH "${PROJECT_BINARY_DIR}/resources" RESOURCE_DIR) # Normalize Windows/Linux paths
add_custom_command(
    TARGET my_target POST_BUILD
    COMMAND ${CMAKE_COMMAND} ARGS -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/resources ${RESOURCE_DIR}
)
target_compile_definitions(my_target PUBLIC RESOURCE_DIR=${RESOURCE_DIR})

.

#define VAL(x) #x
#define STR(x) VAL(x)
const char* my_resource = STR(RESOURCE_DIR) "/my_resource.abc";
0x5453
  • 12,753
  • 1
  • 32
  • 61