CMake seems to prepend linker flags at the front of a GCC compilation command, instead of appending it at the end. How to make CMake append linker flags?
Here is a simple example to reproduce the problem.
Consider this C++ code that uses clock_gettime:
// main.cpp
#include <iostream>
#include <time.h>
int main()
{
timespec t;
clock_gettime(CLOCK_REALTIME, &t);
std::cout << t.tv_sec << std::endl;
return 0;
}
This is a CMakeLists.txt to compile the C++ file above:
cmake_minimum_required(VERSION 2.8)
set(CMAKE_EXE_LINKER_FLAGS "-lrt")
add_executable(helloapp main.cpp)
Note that we have added -lrt since it has the definition of clock_gettime.
Compiling this using:
$ ls
CMakeLists.txt main.cpp
$ mkdir build
$ cd build
$ cmake ..
$ make VERBOSE=1
Which throws up this error, even though you can see -lrt in the command:
/usr/bin/c++ -lrt CMakeFiles/helloapp.dir/main.cpp.o -o helloapp -rdynamic
CMakeFiles/helloapp.dir/main.cpp.o: In function `main':
main.cpp:(.text+0x15): undefined reference to `clock_gettime'
collect2: ld returned 1 exit status
make[2]: *** [helloapp] Error 1
The problem here is the C++ compilation command generated by CMake has -lrt prepended at the front. The compilation works fine if it had been:
/usr/bin/c++ CMakeFiles/helloapp.dir/main.cpp.o -o helloapp -rdynamic -lrt
How to make CMake append the linker flags at the end?
In general you can't (I think), but in the specific case that you want to link against a particular library, you should be using the syntax
target_link_libraries(helloapp rt)
instead. CMake knows that this corresponds to passing -lrt on the linker command line.
Related
I'm having an issue with a makefile compiling and I'm not sure where to start diagnosing this. It was generated for a colleague's OS X system, and I'm trying to implement it on my linux system. It worked on the OS X computer. I've updated library locations to represent where they live on my computer - and this is likely the biggest source of error, as gfortran procedure shouldn't be different, no?
The included files: file0.i, ... fileN.i all live in the same directory as the makefile.
I'm certain if I could compile the first object file I could get through the rest and complete my PhD or save the world or something.
A snippet of the file follows:
# %W% %G%
# Makefile for directory ~/Documents/workstuff/project/program
#
fflags = -O3 -I. -I/usr/local/include -frecord-marker=4 -ffree-form
## -fdefault-real-8 -fdefault-double-8
lflags = -L/usr/local/lib -lnetcdf -lnetcdff
# for debugging, use these options
fflags = -g
lflags = -g
chem = ~/Documents/workstuff/project/chem
main.o: $(chem)/code/main.f file0.i file1.i file2.i
gfortran -c $(fflags) $(chem)/code/main.f
And I receive the following error:
~/Documents/workstuff/project/program/chem/code/main.f:11: Error: Can't open included file 'file0.i'
makefile:14: recipe for target 'main.o' failed
make: *** [main.o] Error 1
I thought it might be an executable issue, so we went from 644 to 744:
username$ file file0.i
file0.i: ASCII text
username$ stat -c '%A %a %n' file0.i
-rwxr--r-- 744 file0.i
Still same error. Double-check that my flags are pointing to the right place:
username$ nf-config --fflags
-I/usr/local/include
username$ nf-config --flibs
-L/usr/local/lib -lnetcdff -lnetcdf -lnetcdf
For what its worth, file0.i contains the following, which I have of course filled with nonsense numbers for sharing online:
$Id: file0.i,v 1.12 2012/12/31 04:25:23 username Exp $
PARAMETER (NLT=19,NHT=51,DZETA=0.5/3.,Psurf=100.)
PARAMETER (NLT1=NLT+1,NHT1=NHT+2,NLT2=3*NLT+1,NHT2=4*NHT+1)
PARAMETER (NDYEAR=33,NTN=75,NTCV=14,NLV=21,NPOL=8,NGSP=3)
PARAMETER (NDIST=111,TD1=110.,NVC=1,NVSP=1,NVTIME=3)
Does anything obvious stick out to anyone?
I do not know gfortran very well, so I may be completely wrong, but for gcc, if a C source file contains:
#include <foo.h>
and if gcc is called with -I., foo.h will be searched in the same directory as the source file, not in the directory where you call gcc from. Example:
$ ls foo
bar.c bar.h
$ cat foo/bar.c
#include <bar.h>
int main(int argc, char **argv) {
return 0;
}
$ gcc -I. foo/bar.c
foo/bar.c:1:10: error: 'bar.h' file not found with <angled> include; use "quotes" instead
#include <bar.h>
^~~~~~~
"bar.h"
1 error generated.
$ gcc -Ifoo foo/bar.c
$
So, it could be that you should put your header files in the same directory as your source file or use a -I<path> where <path> is the absolute path of where you call gfortran from.
I'm trying to combine object files created from C++ files into an executable using gcc. Unfortunately, gcc is giving me thousands of undefined reference errors to strings, arrays, etc.
I am doing this on a Windows machine, so no terminal commands; only cmd commands.
I'm simply doing:
gcc a.o b.o c.o -o prgm.exe
What am I missing/doing wrong?
EDIT:
I recreated the .o files with g++ doing:
g++ a.cpp -g -c -Wall -std=c++0x -lSDLmain -lSDL -lSDL_image -lSDL_ttf -IC:\SDL-1.2.14\include -o a.o, where a.cpp and a.o are the directories where i keep the files, not the g++ directory
Then, I did g++ a.o b.o c.o -o prgm.exe. This gave dozens (I guess that's an improvement?) errors like
undefined reference to `_SDL_SetColorKey'
but I included SDL didnt I?
The final error from this is:
c:/program files (x86)/codeblocks/mingw/bin/../lib/gcc/mingw32/4.7.0/../../../li
bmingw32.a(main.o):main.c:(.text.startup+0xa7): undefined reference to `_WinMain
#16'
collect2.exe: error: ld returned 1 exit status
int main(int argc, char * argv[]) is in the code
You are trying to link a C++ program with the C linker. You need to use g++ instead of gcc.
Generally speaking gcc is for compiling/linking C, while g++ is for C++. IIRC compiling C++-code with gcc works by virtue of dispatching according to the file extension. Linking C++ code with gcc however does not work, since it won't link the C++ standard libraries, resulting in your undefined reference errors.
If this does not solve your problem, you might want to give us a more concrete description of your errors and your system.
Based upon your updates then I think you'd need to do the following:
g++ a.cpp b.cpp c.cpp -g -Wall -IC:\SDL-1.2.14\include -LC:\SDL-1.2.14\lib -std=c++0x -lSDLmain -lSDL -lSDL_image -lSDL_ttf -o prgm.exe
I'm guessing C:\SDL-1.2.14\lib exists based upon where the headers are located.
GCC is the C compiler. Your code is C++ so you need to use G++ to do the linking:
g++ a.o b.o c.o -o prgm.exe
This automatically adds the C++ libraries to the link line, resolving many if not all of your missing references.
after upgrading to Ubuntu 11.10, I've found that many of my old and current developments can't be compiled anymore. I've reduced the problem to a simple example:
#include <X11/Xlib.h>
int main() {
Display* display = XOpenDisplay(":0.0");
XCloseDisplay(display);
return 0;
}
Compiling it using:
g++ -lX11 test.cpp
or
g++ -c -o test.o test.cpp
g++ -lX11 -o test test.o
Causes a failure to happen:
/tmp/ccBAOpzy.o: In function `main':
test.cpp:(.text+0x11): undefined reference to `XOpenDisplay'
test.cpp:(.text+0x21): undefined reference to `XCloseDisplay'
Any ideas? I've found that some linker stuff has changed in 11.10:
https://wiki.ubuntu.com/NattyNarwhal/ToolchainTransition
But still doesn't explain these problems.
g++ -lX11 -o test test.o
Above command is incorrect. Try this instead:
g++ test.o -lX11
Explanation of why the order matters here.
Also, you should never call your executables test on UNIX.
I am observing a difference when trying to do the same operation on GCC 4.4 and GCC 4.5. Because the code I am doing this with is proprietary, I am unable to provide it, but I am observing a similar failure with this simple test case.
What I am basically trying to do is have one shared library (libb) depend on another shared library (liba). When loading libb, I assume that liba should be loaded as well - even though libb is not necessarily using the symbols in liba.
What I am observing is when I compile with GCC 4.4, I observe that the liba is loaded, but if I compile with GCC 4.5, libb is not loaded.
I have a small test case that consists of two files, a.c and b.c . The contents of the files:
//a.c
int a(){
return 0;
}
//b.c
int b(){
return 0;
}
//c.c
#include <stdio.h>
int a();
int b();
int main()
{
printf("%d\n", a()+b());
return 0;
}
//test.sh
$CC -o liba.so a.c -shared
$CC -o libb.so b.c -shared -L. -la -Wl,-rpath-link .
$CC c.c -L. -lb -Wl,-rpath-link .
LD_LIBRARY_PATH=. ./a.out
This is my output with different versions of GCC
$ CC=gcc-4.4 ./test.sh
1
$ CC=gcc-4.5 ./test.sh
/tmp/cceJhAqy.o: In function `main':
c.c:(.text+0xf): undefined reference to `a'
collect2: ld returned 1 exit status
./test.sh: line 4: ./a.out: No such file or directory
$ CC=gcc-4.6 ./test.sh
/tmp/ccoovR0x.o: In function `main':
c.c:(.text+0xf): undefined reference to `a'
collect2: ld returned 1 exit status
./test.sh: line 4: ./a.out: No such file or directory
$
Can anyone explain what is happening? Another extra bit of information is that ldd on libb.so does show liba.so on GCC 4.4 but not on GCC 4.5.
EDIT
I changed test.sh to the following:
$CC -shared -o liba.so a.c
$CC -L. -Wl,--no-as-needed -Wl,--copy-dt-needed-entries -la -shared -o libb.so b.c -Wl,-rpath-link .
$CC -L. c.c -lb -Wl,-rpath-link .
LD_LIBRARY_PATH=. ./a.out
This gave the following output with GCC 4.5:
/usr/bin/ld: /tmp/cc5IJ8Ks.o: undefined reference to symbol 'a'
/usr/bin/ld: note: 'a' is defined in DSO ./liba.so so try adding it to the linker command line
./liba.so: could not read symbols: Invalid operation
collect2: ld returned 1 exit status
./test.sh: line 4: ./a.out: No such file or directory
There seems to have been changes in how DT_NEEDED libraries are treated during linking by ld. Here's the relevant part of current man ld:
With --copy-dt-needed-entries dynamic libraries mentioned on the command
line will be recursively searched, following their DT_NEEDED tags to other libraries, in order to resolve symbols required by the output binary. With the
default setting however the searching of dynamic libraries that follow it will stop with the dynamic library itself. No DT_NEEDED links will be traversed
to resolve symbols.
(part of the --copy-dt-needed-entries section).
Some time between GCC 4.4 and GCC 4.5 (apparently, see some reference here - can't find anything really authoritative), the default was changed from the recursive search, to no recursive search (as you are seeing with the newer GCCs).
In any case, you can (and should) fix it by specifying liba in your final link step:
$CC c.c -L. -lb -la -Wl,-rpath-link .
You can check that this linker setting is indeed (at least part of) the issue by running with your newer compilers and this command line:
$CC c.c -L. -Wl,--copy-dt-needed-entries -lb -Wl,--no-copy-dt-needed-entries \
-Wl,-rpath-link .
I have a working setup, where all files are in the same directory (Desktop). The Terminal output is like so:
$ gcc -c mymath.c
$ ar r mymath.a mymath.o
ar: creating archive mymath.a
$ ranlib mymath.a
$ gcc test.c mymath.a -o test
$ ./test
Hello World!
3.14
1.77
10.20
The files:
mymath.c:
float mysqrt(float n) {
return 10.2;
}
test.c:
#include <math.h>
#include <stdio.h>
#include "mymath.h"
main() {
printf("Hello World!\n");
float x = sqrt(M_PI);
printf("%3.2f\n", M_PI);
printf("%3.2f\n", sqrt(M_PI));
printf("%3.2f\n", mysqrt(M_PI));
return 0;
}
Now, I move the archive mymath.a into a subdirectory /temp. I haven't been able to get the linking to work:
$ gcc test.c mymath.a -o test -l/Users/telliott_admin/Desktop/temp/mymath.a
i686-apple-darwin10-gcc-4.2.1: mymath.a: No such file or directory
$ gcc test.c -o test -I/Users/telliott_admin/Desktop/temp -lmymath
ld: library not found for -lmymath
collect2: ld returned 1 exit status
What am I missing? What resources would you recommend?
Update: Thanks for your help. All answers were basically correct. I blogged about it here.
$ gcc test.c /Users/telliott_admin/Desktop/temp/mymath.a -o test
edit: gcc only needs the full path to the library for static libraries. You use -L to give a path where gcc should search in conjunction with -l.
To include the math libraries, use -lm, not -lmath. Also, you need to use -L with the subdirectory to include the library when linking (-I just includes the header for compiling).
You can compile and link with:
gcc test.c -o test -I/Users/telliott_admin/Desktop/temp /Users/telliott_admin/Desktop/temp/mymath.a
or with
gcc test.c -o test -I/Users/telliott_admin/Desktop/temp -L/Users/telliott_admin/Desktop/temp -lmymath
where mymath.a is renamed libmymath.a.
See link text for comments (search for "bad programming") on the practices of using -l:
In order for ld to find a library with -l, it must be named according to the pattern libyourname.a. Then you use -lmymath
So, there is no way to get it to take /temp/mymath.a with -l.
If you named it libmymath.a, then -L/temp -lmymath would find it.