lollercoaster
lollercoaster

Reputation: 16533

Creating dynamic library on Mac OSX using clang++

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

Answers (2)

lollercoaster
lollercoaster

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

Daniel Mortara
Daniel Mortara

Reputation: 71

Try naming your library like libmandelbrot.dylib to match the -lmandelbrot flag

Upvotes: 2

Related Questions