Reputation: 16533
I wanted to learn how to make a shared library in C++. So I found some logic to generate Mandelbrot PPM images, and encapsulated it in src/mandelbrot.cpp
(with accompanying header file include/mandelbrot.cpp
). Directory tree looks like:
$ tree
.
├── Makefile
├── include
│ └── mandelbrot.h
├── lib
│ └── mandelbrot.dylib
└── src
├── client.cpp
├── main
├── main.cpp
├── mandelbrot.cpp
└── mandelbrot.o
The goal is for src/client.cpp
to use lib/mandelbrot.dylib
to draw the fractal without having access to src/mandelbrot.cpp
or src/mandelbrot.o
.
Makefile:
.PHONY=clean
main: src/mandelbrot.o
clang++ src/main.cpp -o src/main src/mandelbrot.o -I ./include
src/mandelbrot.o:
clang++ -c src/mandelbrot.cpp -o src/mandelbrot.o -I ./include
clean:
rm src/*.o
rm lib/*.dylib
lib/mandelbrot.dylib:
clang++ -dynamiclib -o lib/mandelbrot.dylib src/mandelbrot.cpp -I ./include
src/client: lib/mandelbrot.dylib
clang++ src/client.cpp -o src/client -L ./lib -I ./include
Running the executable without the dylib works:
$ make main
clang++ src/main.cpp -o src/main src/mandelbrot.o -I ./include
$ ./src/main # runs fine!
But I can't get my shared library to link when compiling src/client.cpp
:
$ make src/client
clang++ src/client.cpp -o src/client -L ./lib -I ./include
Undefined symbols for architecture x86_64:
"fractal::Mandelbrot::writeToFile(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)", referenced from:
_main in client-e344c7.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [src/client] Error 1
Even though the symbol seems to be in the dylib:
$ file lib/mandelbrot.dylib
lib/mandelbrot.dylib: Mach-O 64-bit dynamically linked shared library x86_64
$ nm -C lib/mandelbrot.dylib | grep writeToFile
0000000000001460 T fractal::Mandelbrot::writeToFile(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
I'm trying to dig in deeper and understand the linking/compilation process. How do I make the setup I have work to demonstrate what I want to do?
Upvotes: 0
Views: 3332
Reputation: 16533
Credit goes to @Daniel for finding the answer, but a bit about why.
I can make the compilation work by renaming mandelbrot.dylib
-> libmandelbrot.dylib
as suggested by @daniel's answer.
I dug into man ld
(the linker) and found the following definition:
-lx This option tells the linker to search for libx.dylib or libx.a in the library search path. If string x is of the form y.o, then that file is searched for in the same places, but without prepending
`lib' or appending `.a' or `.dylib' to the filename.
Thus, if you want to arbitrarily name your dynamically loaded libraries (and for some reason don't like the magic of prepending libxxxx to the name, you'd have to do this (I have confirmed this works):
lib/whatever.o:
clang++ -dynamiclib -o lib/whatever.o src/mandelbrot.cpp -I ./include
src/client: lib/whatever.o
clang++ src/client.cpp -o src/client -L ./lib -I ./include -l whatever.o
TL;DR = By having the -l
option as a filename ending in .o
, the linker looks directly in the directory specified by -L
for the exact filename specified in -l
. Otherwise -l foo -L /path/
searches for /path/libfoo.[dylib|a]
Upvotes: 2
Reputation: 71
Try naming your library like libmandelbrot.dylib to match the -lmandelbrot flag
Upvotes: 2