CMake CPack NSIS How to pass install location to project? - c

I have a project written in C. Currently it's using UNIX makefiles to compile itself for Linux, but recently I've been looking into CMake, to be more portable.
The executable, when running, needs to access some asset files that are part of the project. When using makefiles, I would just compile the project with:
make prefix=/usr
make prefix=/usr install
So while the project is compiled, it knows that it will end up in /usr, and when running, it searching for its own project files there (in something like /usr/share/my-project/).
I created a very basic CMakeLists.txt, that compiles the .exe file, and installs it together with one other asset file in the install directory. I then run the following commands to create an NSIS installer for Windows:
cmake.exe --build --config Release .
cpack.exe
Which succesfully gives me the NSIS installer. When run, it shows the user a few steps, one of them is to decide where the project will be installed, which the user can modify.
So my question is, at that point the project has already been compiled, so how can I pass to my project its own install location, so it can access files included in the project? How do other projects do this? I couldn't find much information online about it, which makes me think I might be taking the wrong approach.

For anyone stuck in a similar problem, I found one solution.
Upon looking online, this seems to be something not recommended for Unix systems, and setting the install location during compilation is pretty standard.
For windows, however, I found the function GetModuleFileNameW (GetModuleFileNameW function (libloaderapi.h)).
It returns the path to the current executable (something like C:\Program Files\<my-app>\bin\my-app.exe). I've confirmed it returns the right path, even when I install the project on different directories. It returns the result using wchar_t, so unicode directories are also supported.
Here is a small example of how it can be used:
// to keep the example simple, this is assuming maximum 1000 characters in the path
wchar_t dynamicProjectLocationW[1000];
GetModuleFileNameW(NULL, dynamicProjectLocationW, 999);
dynamicProjectLocationW[999] = L'\0';
// given a path like "C:\X\Y\bin\myapp.exe" find the second to last slash
// so we can get the path "C:\X\Y\"
wchar_t *pointer = dynamicProjectLocationW;
wchar_t *secondToLastSlash = 0;
wchar_t *lastSlash = 0;
while (pointer[0] != L'\0') {
if (pointer[0] == L'\\') {
secondToLastSlash = lastSlash;
lastSlash = pointer;
}
pointer++;
}
// cut the path short, so we can use the project path to find other files
if (secondToLastSlash) {
secondToLastSlash++;
secondToLastSlash[0] = L'\0';
}
This is solving my problem for now, so I'll be using this until a better solution is found.

Related

Current directory in CLion

I use Clion 2016.1. For example, I run such code in the directory ~/CLionProjects/Tutorial:
#include <stdio.h>
int main() {
char * string;
string = "Hello, everyone";
printf(string);
}
Why does Clion go for this code to this directory?:
/home/ken/.CLion2016.1/system/cmake/generated/Tutorial-9a39f70/9a39f70/Debug/Tutorial
Hello, everyone
Process finished with exit code 15
How to make the programs running in "normal" directory ~/CLionProjects/Tutorial?
UPD
I want to read a "data.csv" file locates in the current directory (where main.c is). But CLion looks for it in /home/ken/.CLion2016.1/system/cmake/generated/Tutorial-9a39f70/9a39f70/Debug/Tutorial. How to make that CLion looks for data.csv in ~/CLionProjects/Tutorial?
If you want to appear your binaries in a folder you've specified, you need to tell it CLion by adjusting your CMakeLists.txt like this:
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/lib)
Edit:
Somehow it seems that these options are not respected in the current version of CLion (2016.2). Therefore one might has to change the desired output directory via: Build, Execution, Deployment | CMake settings and set it there.
For CLion v2016.1 and earlier
To change build output path, open CLion Settings and go to Build, Execution, Deployment | CMake settings and set it there.
This is more an answer for the UPD question than the original. But the answers are related. .EXE is built into a folder that depends on the build options (debug, release, etc). An exe's default working directory is the folder where the exe is found. That is the folder where the program will look for data files, etc, unless you tell it to look elsewhere.
You can change the working directory for the exe with a runtime option. I made a YouTube tutorial for my COSC1030 (Beginning C++) students but the solution is the same for everyone: https://youtu.be/dTtZEAfh_LM

Linking C and R in Windows

I am trying to Link R and C in windows while following the instructions on this web page
http://mcglinn.web.unc.edu/blog/linking-c-with-r-in-windows/
I have R, RTOOLS and TurboC4 all in separate folders in C drive. My system is 64bits but I have tried it on both 32 and 64 bit R.
I have written the following code in C
#include <R.h>
void hello(int *n)
{ int i;
for(i=0; i<=*n; i++){
Rprintf("hello, world!this is hell though I'm enjoying it\n");
}
}
and saved with name WORLD.C on separate file the path to which is
C:\TurboC4\TC\BIN
I have also written the following R code
hello2 <- function(n) {
.C("hello", as.integer(n))
}
hello2(5)
and save it with name WORLD.R.
Now I have to prepare my window. I have downloaded RTools33.exe from here https://cran.r-project.org/bin/windows/Rtools/
and set the environment variables manually through this command
PATH=c:\Rtools\bin;c:\Rtools\gcc-4.6.3\bin;c:\Program Files\R\R-3.2.2\bin\i386;
Then Reinstalled system
Before calling the C code in R I have to compile the C code in cmd. I write the following command to change the directory where WORLD.C is saved
cd C:\Users\TurboC4\TC\BIN
which is successful but when I try to compile
C:\Users\TurboC4\TC\BIN> R CMD SHLIB WORLD.c
I get the following error. " 'R' is not recognized as internal or external command, operable ". I have also tried to compile WORLD.C in C and got these two errors. "Unable to open #include R.h>" and "function Rprintf should have a prototype".
Setting Environment is a Problem
I think this is where I am facing problem. I have checked that Rtools33.exe is compatible with R 3.2.x and later. But I am getting the same error on CMD. I have tried different paths environments and have tried it with Different R versions like R- 3.2.2, R- 3.2.0, 2.15.3, 2.15.0. But when I write
"install.packages("RTools33") in any of these R version, I got the following warning message
Warning in install.packages :package ‘RTools33’ is not available (for R version
2.15.0)"
The reason for using different Rs is when you download RTOOLS folder there is a version file which says "Rtools Collection 3.3.0.1959". So I think maybe it a compatibility issue.
There are some instructions on Github page
https://github.com/stan-dev/rstan/wiki/Install-Rtools-for-Windows
"For installing Rtools, attention needs to be paid in a step where we can edit the system PATH so that the C++ compiler that is included in Rtools can be used by R. As indicated by the following step, we need to check this option (not manually edit the system PATH. Once the option is checked, system PATH would be edited to include important folders of Rtools by the installation process)."
So, I have uninstalled RTools and while reinstalling I have checked this option button as well and then retried but all in vein.
I have checked Sys.getenv('PATH') and got
c:\\\\\\\\Rtools\\\\\\\\gcc-4.6.3\\\\\\\\bin;c:\\\\\\\\RTools\\\\\\\\bin;
I have also tried by setting this path in
control pannel-> useraccountandfamilysafety->Useraccounts->change my
environment variable
and then creating new variable with above PATH.
I am still not able to direct R to C++ compiler. Can anybody please figure out what is my mistake?
This problem is now solved. There was just a minor mistake of defining the environment variable. The correct definition is as follows.
c:\Rtools\bin;c:\Rtools\gcc-4.6.3\bin;c:\Program Files\R\R-3.2.2\bin\i386;

Is it possible to recompile some source files on make install using CMAKE

Let me explain the problem. I have a C-code where a search path for additional plugins is included in a C-string. As long as the Program stays in the build directory of cmake the string should include the plugin folder inside the the build directory. For example
char *plugindir = "/home/.../myproject/build/plugins:/usr/share/myproject/plugins";
If the program gets installed via make install I want to change this string to
char *plugindir = "/usr/share/myproject/plugins";
and recompile/relink the program before it gets installed. How can this behaviour be realized using CMAKE?
There are many solutions, I can tell you about 2:
use a config file so the plugindir is not hardcoded but dynamically taken from a config file.
This is the better solution but you can find. This involves the use of some library like this one.
use # directives. This is an easier solution. In the source file you add something like this:
#ifdef DIST
char *plugindir = "/usr/share/myproject/plugins";
#else
char *plugindir = "/home/.../myproject/build/plugins:/usr/share/myproject/plugins";
#endif
And you can use the DIST preprocessor variable (in che C code or in the Makefile) to distinguish between development or "installed" code versions.

Installing a new library in Linux, and accessing it from my C code

I am working on a project which requires me to download and use this. Inside the downloaded folder, when extracted I am presented with three things:
A folder called "include"
A folder called "src"
A file called "Makefile"
After some research, I found out that I have to navigate to the directory which contains these files, and just type in the command make.
It seemed to install the library in my system. So I tried a sample bit of code which should use the library:
csp_conn_t * conn;
csp_packet_t * packet;
csp_socket_t * socket = csp_socket(0);
csp_bind(socket, PORT_4);
csp_listen(socket, MAX_CONNS_IN_Q);
while(1) {
conn = csp_accept(socket, TIMEOUT_MAX);
packet = csp_read(conn, TIMEOUT_NONE);
printf(“%S\r\n”, packet->data);
csp_buffer_free(packet);
csp_close(conn);
}
That's all that was given for the sample server end of the code. So I decided to add these to the top:
#include <csp.h>
#include <csp_buffer.h>
#include <csp_config.h>
#include <csp_endian.h>
#include <csp_interface.h>
#include <csp_platorm.h>
Thinking I was on the right track, I tried to compile the code with gcc, but I was given this error:
csptest_server.c:1: fatal error: csp.h: No such file or directory
compilation terminated.
I thought I may not have installed the library correctly after all, but to make sure, I found out I could check by running this command, and getting this result:
find /usr -iname csp.h
/usr/src/linux-headers-2.6.35-28-generic/include/config/snd/sb16/csp.h
/usr/src/linux-headers-2.6.35-22-generic/include/config/snd/sb16/csp.h
So it seems like the csp.h is installed, maybe I am referencing it incorrectly in the header include line? Any insight? Thanks a lot.
The make command is probably only building the library, but not installing it. You could try sudo make install. This is the "common" method, but I recommend you to check the library's documentation, if any.
The sudo command is only necessary if you have no permissions to write the system's include and library directories, which may be your case.
Another possibility (instead of installing the library) is telling GCC the location of the library's source code and generated binaries (by means of the -I and -L options of the gcc command.
That Makefile will not install anything, just translate the source into a binary format.
The csp.h in the Linux kernel has nothing to do with your project, it's just a naming collision, likely to happen with three letter names.
In your case, I would presume you need to add the include directory to the compilation flags for your server, like gcc -I/path/to/csp/include/csp csptest_server.c.
(Next, you'll run into linker errors because you'll also want to specify -L/path/to/csp -lcsp so that the linker can find the binary code to link to.)

fopen fails mysteriously under Windows

Maybe I just have another black out but, this one line is giving me a lot of troubles:
FILE *fp = fopen("data/world.data", "rb");
This works fine under Linux when compiled with GCC. But when I compile it with Visual Studio, it crashes. fp is always NULL. Both the BIN and the EXE are in the exact same directory. Now, to make things even crazier, when I run the EXE using Wine under Linux... it... works...
I have absolutely not a god damn clue what's going on here. Maybe it's some insanely stupid mistake on my side, but I cannot get this thing to run under Windows :/
Also, I have another program which works just fine, there the data files are also contained in a sub directory named data.
EDIT:
To make it clear neither / NOR `\ * do work.
EDIT 2:
OK I've given up on this, maybe someone has fun trying to figure it out, here's ZIP containing the EXE, Debug Data for VS etc.:
https://dl.dropbox.com/u/2332843/Leaf.zip
EDIT 3:
Compiled it with CodeBlocks and MinGW, works like a charm. Guess it has to do something with MSVC or the Project Settings in VS.
It sounds like data isn't a subdirectory of your current directory when you run the program. By default (for x86 targets) VS will build and run your program from either a DEBUG or RELEASE subdirectory of the base directory you've created for the project. You can modify the directory that will be "current" when it runs though (e.g., project | properties | configuration properties | debugging for VS 2008).
Edit: While Windows requires that you use a backslash as a directory separator at the command line, a forward slash works fine in code -- this is not the source of your problem.
In windows you have to write the following:
FILE *fp = fopen("data\\world.data", "rb");
This is like that because the backslash is a special character (so a backslash in a string is written using \ and a quotation symbol is \" and so with other special characters).
Since this issue happens only on windows. I doubt whether the file is really named "world.data". As you know, the default setting for windows hides the file extention. Is its real name world.data.xxx?
Include a line to GetCurrentDirectory(), to see if you are running from the directory you expected.
When I develop in C#/ C++ on visual studio, I normally get to run it from the debug folder. I don't think it matters if forward slash is used in place of backslash in .net.
I happened to have the same problem, and suddenly i figured it out.
That should be your windows fault.
Let's say, FILE *fp = fopen("data/world.data", "rb"); in windows, if you hide the extensions, then you can see the file data/world.data, but actually it maybe /data/world.dat.txt or somewhat.
So please check the extensions.
Hope it helps!
I ran into this today, and it happened because I used "br" instead of "rb" on that mode argument.
The underlying fopen is throwing an exception of some kind, which only registers as a crash. It's not bothering to return the standard NULL response or set the associated error values.
I'm not sure but it may be because you're using slash instead of (an escaped) backslash in the path?

Resources