I have copied
cs50.h from the appliance. Now, what's the command that should be run to compile this library using gcc or clang on any other Linux distro (e.g., Ubuntu 14.04 in my case)?
I have copied
Compiling a library is not as easy as compiling an executable. Let's analyze the steps together.
We need to compile our library source code into position-independent code (PIC):
gcc -c -Wall -Werror -fpic cs50.c
Now we need to actually turn this object file into a shared library. We’ll call it cs50.so:
gcc -shared -o libcs50.so cs50.o
As you can see, that was actually pretty easy. We have a shared library. Let’s compile our main.c and link it with libcs50. We’ll call our final program “test.” Note that the -lcs50 option is not looking for cs50.o, but libcs50.so. GCC assumes that all libraries start with ‘lib’ and end with .so or .a (.so is for shared object or shared libraries, and .a is for archive, or statically linked libraries).
gcc -Wall -o test main.c -lcs50 /usr/bin/ld: cannot find -lcs50 collect2: ld returned 1 exit status
The linker doesn’t know where to find libcs50. GCC has a list of places it looks by default, but our directory is not in that list. We need to tell GCC where to find libcs50.so. We will do that with the -L option. In this example, we will use the current directory, /home/username/cs50:
gcc -L/home/username/cs50 -Wall -o test main.c -lcs50
Good, no errors. Now let’s run our program:
./test ./test: error while loading shared libraries: libcs50.so: cannot open shared object file: No such file or directory
The loader can’t find the shared library. We didn’t install it in a standard location, so we need to give the loader a little help. We have a couple of options: we can use the environment variable LD_LIBRARY_PATH for this, or rpath. Let’s take a look first at LD_LIBRARY_PATH:
There’s nothing in there. Let’s fix that by prepending our working directory to the existing LD_LIBRARY_PATH:
$ LD_LIBRARY_PATH=/home/username/cs50:$LD_LIBRARY_PATH $ ./test ./test: error while loading shared libraries: libcs50.so: cannot open shared object file: No such file or directory
What happened? Our directory is in LD_LIBRARY_PATH, but we didn’t export it. In Linux, if you don’t export the changes to an environment variable, they won’t be inherited by the child processes. The loader and our test program didn’t inherit the changes we made. Thankfully, the fix is easy:
$ export LD_LIBRARY_PATH=/home/username/cs50:$LD_LIBRARY_PATH $ ./test This is a shared library test... Hello, I'm a shared library
LD_LIBRARY_PATH is great for quick tests and for systems on which you don’t have admin privileges. As a downside, however, exporting the LD_LIBRARY_PATH variable means it may cause problems with other programs you run that also rely on LD_LIBRARY_PATH if you don’t reset it to its previous state when you’re done.
Now let’s try rpath (first we’ll clear LD_LIBRARY_PATH to ensure it’s rpath that’s finding our library). Rpath, or the run path, is a way of embedding the location of shared libraries in the executable itself, instead of relying on default locations or environment variables. We do this during the linking stage. Notice the lengthy “-Wl,-rpath=/home/username/cs50” option. The -Wl portion sends comma-separated options to the linker, so we tell it to send the -rpath option to the linker with our working directory.
$ unset LD_LIBRARY_PATH $ gcc -L/home/username/cs50 -Wl,-rpath=/home/username/cs50 -Wall -o test main.c -lfoo $ ./test This is a shared library test... Hello, I'm a shared library
Excellent, it worked. The rpath method is great because each program gets to list its shared library locations independently, so there are no issues with different programs looking in the wrong paths like there were for LD_LIBRARY_PATH.
What if we want to install our library so everybody on the system can use it? For that, you will need admin privileges. You will need this for two reasons: first, to put the library in a standard location, probably /usr/lib or /usr/local/lib, which normal users don’t have write access to. Second, you will need to modify the ld.so config file and cache. As root, do the following:
$ cp /home/username/foo/libfoo.so /usr/lib $ chmod 0755 /usr/lib/libfoo.so
Now the file is in a standard location, with correct permissions, readable by everybody. We need to tell the loader it’s available for use, so let’s update the cache:
That should create a link to our shared library and update the cache so it’s available for immediate use. Let’s double check:
$ ldconfig -p | grep cs50 libcs50.so (libc6) => /usr/lib/libcs50.so
Now our library is installed. Before we test it, we have to clean up a few things:
Clear our LD_LIBRARY_PATH once more, just in case:
$ unset LD_LIBRARY_PATH
Re-link our executable. Notice we don’t need the -L option since our library is stored in a default location and we aren’t using the rpath option:
$ gcc -Wall -o test main.c -lcs50
Let’s make sure we’re using the /usr/lib instance of our library using ldd:
$ ldd test | grep foo libfoo.so => /usr/lib/libfoo.so (0x00a42000)
Good, now let’s run it:
$ ./test This is a shared library test... Hello, I'm a shared library
Update: please follow the instructions per https://github.com/cs50/libcs50#installation instead!
A shorter answer could be this.
First become root, as with:
sudo su -
Then install the CS50 Library as follows:
apt-get install gcc wget http://mirror.cs50.net/library50/c/library50-c-5.zip unzip library50-c-5.zip rm -f library50-c-5.zip cd library50-c-5 gcc -c -ggdb -std=c99 cs50.c -o cs50.o ar rcs libcs50.a cs50.o chmod 0644 cs50.h libcs50.a mkdir -p /usr/local/include chmod 0755 /usr/local/include mv -f cs50.h /usr/local/include mkdir -p /usr/local/lib chmod 0755 /usr/local/lib mv -f libcs50.a /usr/local/lib cd .. rm -rf library50-c-5