c++ linking and compiling flags - c++

I may have a stupid question but as no question is stupid i'll ask it... let's imagine i have the files matrix.hpp and matrix.cpp. In those files i use assert(...) to make sure that some condition is respected. I compile this file and get a matrix.o file. Now i will use this matrix.o file in many different programs, some of them are only tests and need to check the assert(...) conditions, others are working programs that don't need these checks.
My question is : can i compile the matrix.o without the -DNDEBUG flag, thus in general, the assert(...) condition will be checked. but when i link the .o files for a program which doesn't need the checks, i add this flag without recompiling the matrix.o file.
To be more precise, would this do what i want :
# the test program with the "assert(..)" checks
test:test.o matrix.o
gcc -o $# $^
test.o:test.cpp matrix.hpp
gcc -c $^
# the real program without the "assert(..)" checks
prog:prog.o matrix.o
gcc -o $# $^ -DNDEBUG
prog.o:prog.cpp matrix.hpp
gcc -c -DNDEBUG $^
# the matrix.o that can be either checked or not if the -DNDEBUG flag
# is given when the .o files are linked
matrix.o:matrix.cpp matrix.hpp
gcc -c $^
ok thank you for your answer ! So i can't do that simply using the flags -DNDEBUG. What if each time i use "assert(...)" in the matrix files i add :
#ifdef CHECK
assert(...)
#endif
and now when i compile the "test" program i use a CHECK flag but not with the "prog" program ? I guess it won't work either...

The short answer is no. Depending on your exact circumstances there might be some clever tricks you can pull (e.g. linking in a different "assert failed" function).
Have you considered throwing an exception instead of asserting? Then, 'prog' and 'test' could take different approaches to handling it.

No, not with GCC. I see two options:
compile two versions of matrix.o and link the appropriate version into each program, or
replace assert with a manual check that throws an exception.
The latter option obviously has some runtime cost even in the non-test programs, so use it with care (not inside an inner loop).

Related

Understanding Makefile. make cannot link armadillo library

I am new to C++ and I am having trouble understanding how Makefiles do their thing with the g++ compiler.
I have successfully installed armadillo library (via apt) and have a very simple c++ program test.cpp, like the one below:
#include <iostream>
#include <armadillo>
using namespace std;
int main()
{
arma::mat A;
A << -1 << 2 << arma::endr
<< 3 << 5;
cout << A << endl;
arma::fmat B;
B.randu(4,5);
cout << B;
return 0;
}
This works just fine if I compile manually like this:
g++ src/test.cpp -std=c++11 -Wall -o test -DARMA_DONT_USE_WRAPPER -lopenblas -llapack
I can manually run the program and it delivers the matrices as expected.
On the other hand, I have the Makefile template from the VSCode C/C++ Extension, which I have modifed slightly for including the LAPACK an BLAS Fortran libraries:
########################################################################
####################### Makefile Template ##############################
########################################################################
# Compiler settings - Can be customized.
CC = g++
CXXFLAGS = -std=c++11 -Wall
LDFLAGS = -DARMA_DONT_USE_WRAPPER -lopenblas -llapack
# Makefile settings - Can be customized.
APPNAME = test
EXT = .cpp
SRCDIR = src
OBJDIR = obj
############## Do not change anything from here downwards! #############
SRC = $(wildcard $(SRCDIR)/*$(EXT))
OBJ = $(SRC:$(SRCDIR)/%$(EXT)=$(OBJDIR)/%.o)
DEP = $(OBJ:$(OBJDIR)/%.o=%.d)
# UNIX-based OS variables & settings
RM = rm
DELOBJ = $(OBJ)
# Windows OS variables & settings
DEL = del
EXE = .exe
WDELOBJ = $(SRC:$(SRCDIR)/%$(EXT)=$(OBJDIR)\\%.o)
########################################################################
####################### Targets beginning here #########################
########################################################################
all: $(APPNAME)
# Builds the app
$(APPNAME): $(OBJ)
$(CC) $(CXXFLAGS) -o $# $^ $(LDFLAGS)
# Creates the dependecy rules
%.d: $(SRCDIR)/%$(EXT)
#$(CPP) $(CFLAGS) $< -MM -MT $(#:%.d=$(OBJDIR)/%.o) >$#
# Includes all .h files
-include $(DEP)
# Building rule for .o files and its .c/.cpp in combination with all .h
$(OBJDIR)/%.o: $(SRCDIR)/%$(EXT)
$(CC) $(CXXFLAGS) -o $# -c $<
################### Cleaning rules for Unix-based OS ###################
# Cleans complete project
.PHONY: clean
clean:
$(RM) $(DELOBJ) $(DEP) $(APPNAME)
# Cleans only all files with the extension .d
.PHONY: cleandep
cleandep:
$(RM) $(DEP)
#################### Cleaning rules for Windows OS #####################
# Cleans complete project
.PHONY: cleanw
cleanw:
$(DEL) $(WDELOBJ) $(DEP) $(APPNAME)$(EXE)
# Cleans only all files with the extension .d
.PHONY: cleandepw
cleandepw:
$(DEL) $(DEP)
I have passed the needed libraries under LDFLAGS = -DARMA_DONT_USE_WRAPPER -lopenblas -llapack. Nevertheless, this solution does not work. It looks to me like the compiler is unable to find the armadillo library, so I must have linked it somehow wrongly. It delivers:
g++ -std=c++11 -Wall -o test obj/test.o -DARMA_DONT_USE_WRAPPER -lopenblas -llapack
/usr/bin/ld: obj/test.o: in function `TLS wrapper function for arma::arma_rng_cxx11_instance':
test.cpp:(.text._ZTWN4arma23arma_rng_cxx11_instanceE[_ZTWN4arma23arma_rng_cxx11_instanceE]+0x25): undefined reference to `arma::arma_rng_cxx11_instance'
collect2: error: ld returned 1 exit status
make: *** [Makefile:36: test] Error 1
So, aside from the obvious question (Why does this not work?), I would as well appreciate if someone could help me clarify as well the following aspects:
On the one hand, rom the message error it seems that the command run g++ -std=c++11 -Wall -o test obj/test.o -DARMA_DONT_USE_WRAPPER -lopenblas -llapack does not include the name of the cpp file I wrote (as opposed to in my manual compilation, in which it works). Nevertheless, if I do not use armadillo, the Makefile recipe above works just fine. I see the Makefile somehow looking for all cpp files in the source code folder SRC = $(wildcard $(SRCDIR)/*$(EXT)), but I cannot see where is this forwarded to the compiler. Can someone help me with that?
The other thing is that, in my manual compilation, it seems to make no difference to pass the LAPACK and BLAS libraries as CXXFLAGS or LDFLAGS, meaning both of the following commands:
g++ src/test.cpp -std=c++11 -Wall -DARMA_DONT_USE_WRAPPER -lopenblas -llapack -o test
and
g++ src/test.cpp -std=c++11 -Wall -o test -DARMA_DONT_USE_WRAPPER -lopenblas -llapack
work just fine. As far as I have been able to read, I understood the flags before -o are meant for the compiler, and those after are meant for the "linker" (whatever that is). Can someone explain me what are the main differences between the CXXFLAGS and LDFLAGS? Why both combinations work? And what is the linker?
Thank you very much for your help.
Best,
D.
The other answer is a good general introduction to compilation but if you want to know what is happening in your situation you need to first understand that answer and the difference between source files, object files, and executable files and the way that they work, then go deeper to figure out what's wrong.
As far as I have been able to read, I understood the flags before -o are meant for the compiler, and those after are meant for the "linker" (whatever that is)
No, that is not right.
Turning source files into an executable involves several steps each managed by a different tool. The compiler front-end (e.g., g++) manages the order of these. Each of these may use different options, and whenever the compiler front-end invokes one of these tools it will pass the appropriate flags from the command line for that tool. It's not the case that "only" flags before or after -o are passed to different tools; it doesn't matter where on the command line they live.
The tools involved with compilation, in the order in which they're invoked, are:
Preprocessor: this handles #include and #ifdef and #define, etc. (the lines that start with # in your source). The preprocessor takes the options -D, -I, and some others.
Compiler: this turns your source code (after preprocessing to handle all the included files etc.) into assembly code which is very low-level: basically machine code but in ASCII form. This does the bulk of the work including optimization etc. Flags like -O2, -g, and many others are used by this tool.
Assembler: this turns the assembly code into a binary format for your CPU and generates an object file (foo.o).
Linker: this takes one or more object files plus libraries and turns them into an executable. This tool uses options like -L and -l to find libraries.
There's a separate tool, the archiver (ar) which is not invoked by the compiler front-end, which is used to turn object files (foo.o) into static libraries (libfoo.a).
Note, the above is a "classical" view of building: newer compilers munge the above steps together sometimes to get either better error messages or better optimization or both.
Most of the time the first three steps are all done by a single invocation of the compiler front-end: it turns a source file into an object file. You do this once for each source file. Then at the end, another invocation of the compiler front-end takes those object files and builds an executable.
If you look at the output make prints you'll see these two steps. First you'll see the compilation step, which is controlled by this make rule:
$(OBJDIR)/%.o: $(SRCDIR)/%$(EXT)
$(CC) $(CXXFLAGS) -o $# -c $<
and runs this command:
g++ -std=c++11 -Wall -o obj/test.o -c src/test.cpp
The -c option here tells the compiler, "do all the steps up to and including the compile step, then stop and don't do the link step".
Then you will see your link command, which is controlled by this make rule:
$(APPNAME): $(OBJ)
$(CC) $(CXXFLAGS) -o $# $^ $(LDFLAGS)
and runs this command:
g++ -std=c++11 -Wall -o test obj/test.o -DARMA_DONT_USE_WRAPPER -lopenblas -llapack
What do you notice about this? The -DARMA_DONT_USE_WRAPPER is a preprocessor option, but you're passing it to the link step and not passing it to the compile step. That means when the source is compiled, that option is not present and so whatever operation it was intended to suppress (using a wrapper apparently) is not being suppressed.
You need to put preprocessor options in a make variable that is sent to the compiler / preprocessor, so it should be this:
CXXFLAGS = -std=c++11 -Wall -DARMA_DONT_USE_WRAPPER
LDFLAGS = -lopenblas -llapack
Be sure to run clean before trying to build again.
One minor thing, but generally you should use CXX for your C++ compiler and CC for your C compiler (these are the usual conventions). If you do end up trying to compile C++ source with a C compiler you are likely to have problems. Less so the other way round.
So what it happening? Roughly speaking, you have two steps:
Compilation
Linking
When you compile a small exe, you can combine these into a single steps. Makefiles generally don't as two steps is more general.
For compilation the input has a .cpp suffix and you are passing the -c flag to tell the compiler to just compile. This will result in an object file (.o suffix).
For linking, there is no -c. The inputs are object files and the output is your application.
Other suffixes are possible (.cxx, .CC etc.).
There are 4 commonly used make variables
CPPFLAGS for preprocessor flags, can be used for C and C++ compilation
CFLAGS for flags specific to C compilation
CXXFLAGS for flags specific to C++ compilation
LDFLAGS for flags specific to linking
Historically, ld was the linker (and hence LDFLAGS), but it isn't smart enough to handle C++ linking well on its own. So now it is usually the C++ compiler that performs the task of "linker driver", that is g++ controls the linking that ld does.
Finally, your specific problem. You should add the armadillo library to LDFLAGS. The best way to do that is to just add -larmadillo. If armadillo is not installed in a 'standard' location like /usr/lib then you may need to additional arguments such as
-L/path//to/armadillo_lib -Wl,-rpath,/path//to/armadillo_lib
(the first one tells the linker where the library is, the second one puts that path into the executable so that is also knows where the library is).

How to fix error : g++.exe : cannot specify -o with -c or -S and multiple compilations

the program no run. how to fix it ( i use C-Free 4.0)
? g++.exe : cannot specify -o with -c or -S and multiple compilations
Can you include the entire build command that you are using? My guess is that you are trying to compile multiple files into an executable in a single command (such as g++ file1.cc file2.cc file3.cc -o file.exe), but you've also got a -c flag in the commpilation command.
For reference, normally when you compile, two things happen. First, source code gets turned into machine code. Second, machine code gets linked to produce an executable.
In gcc/g++, you can compile one source file into machine code by using the -c flag. You can link one (or many) machine code file(s) into an executable by using the -o flag. There's a shorthand where you can compile and link all in one step using the -o flag (but that's generally not a good idea, because then any change to any file requires you to recompile everything).
As for -S, that's for when you want to generate assembly code from source code. I'm guessing that's not what you are doing, though.
Here's an example, just to round it all out. Suppose that you have files file1.cc and file2.cc, and there is a main() function in file1.cc. Then you can create machine code like this:
g++ file1.cc -c
g++ file2.cc -c
This will result in there being two new files, file1.o and file2.o.
Next, you can link them like this:
g++ file1.o file2.o -o file.exe
This will produce file.exe, the final executable that you can run.

order of dependencies in make

Since it is said that order of dependencies does matter in a makefile. I wish to understand how:
finalObjectFile: x.o main.o y.o
g++ x.o main.o y.o -o finalObjectFile
main.o: header/x.h source/main.cpp
g++ -c source/main.cpp
x.o: header/x.h source/x.cpp
g++ -c source/x.cpp
y.o: source/y.cpp header/x.h
g++ -c source/y.cpp
In the above code I have swapped the positions of the header file x.h:
x.o: header/x.h source/x.cpp
g++ -c source/x.cpp
y.o: source/y.cpp header/x.h
g++ -c source/y.cpp
but the output doesn't get effected!
Which kind of dependencies are ACTUALLY important to be in order?
In the context shown, there is no significant difference between header before source and source before header. The rules say (for example):
x.o must be more recent than header/x.h and source/x.cpp.
It doesn't matter which sequence those two dependencies appear in; if either dependency is newer than x.o, the command will be executed.
When does the order matter? That's a bit trickier to exemplify, but might be a factor if you have multiple intermediate files that get generated as you go. It might especially be a problem if some of the commands you execute affect multiple files but you don't properly tell make about all those interactions.
However, dependency order normally isn't a problem.
It matters in real make files, because you tend to use packaged rules.
In a rule, $< is replaced by the first prerequisite. Thus:
compile = g++ -c $< -o $#
x.o : source/x.cpp header/x.h
$(compile)
y.o : header/y.h source/y.cpp
$(compile)
The first rule will compile source/x.cpp, as desired. The second rule
will attempt to compile header/y.h, which is definitely not what is
wanted.
Since this is a far more typical (and rational) way of writing
makefiles, you do have to respect order, at least with regards to which
element comes first.
Another case it might matter (but which shouldn't be the case in a well
written makefile): in many cases, make will resolve the dependencies
in a left to right order; i.e. given:
x.o : source/x.cpp header/x1.h header/x2.h
...
make will first check that source/x.cpp is up to date, and build it
if necessary, and then do the same for header/x1.h and header/x2.h,
in that order. If you have any non-stated dependencies, which require
header/x1.h to be built before header/x2.h, then changing their
order will also cause problems. This is, however, an error in the
makefile; the dependency should be made explicit (e.g. a rule with
header/x1.h : header/x2.h). Because this order is not guaranteed
otherwise; in the case of GNU make, for example, it may be violated if
you use the -j option.
header/x.h, then swapping the order may cause problems.

Am I using a Makefile for C++ correctly?

Makefile
default:
(!) g++ -Werror -Wunused-variable -Wunused-value -Wunused-function -Wfloat-equal -Wall -ansi -o main -pedantic-errors main.cpp
'/home/HomeName/Desktop/main'
I have been using this code to compile a C++ file. Is this a good way of using this code in the makefile? Moreover, I wonder if the line marked with (!) has the compiler options in the correct order.
So, your makefile itself does not contain the '(!)' marking, I believe.
What you have 'works'. It compiles the program with a stringent set of options and then runs it by absolute pathname.
However, it is not very flexible:
it will only build main if you run make
it will always build main even if you built it a moment ago
if you run make main, it will use a different set of commands to build the program, and it won't run the program.
It would be better - it would allow you to move the code more easily - if the line to run the program used the current directory.
And it would be better if you used some of the built-in features of make.
The C++ and C compilers are very tolerant of various orders for their options; what you have is OK.
Inside make, the C++ compiler is known by the macro CXX; it takes a set of flags defined by CXXFLAGS. You could, therefore, use:
CXX = g++
CXXFLAGS_W = -Werror -Wunused-variable -Wunused-value -Wunused-function \
-Wfloat-equal -Wall
CXXFLAGS_M = -ansi -pedantic-errors
CXXFLAGS = ${CXXFLAGS_M} ${CXFLAGS_W}
all: main
./main
This allows you to run make, make all and make main and get the program main built. If you use either of the first two, the program will also be run. It will only recompile the program if the source has changed since it was last compiled. If you have other programs in the directory, say 'exercise2.cpp' and 'exercise3.cpp', then you'd be able to say make exercise2 exercise3 and those would now be compiled in much the same way that main is.
If you really wanted to run the program after building it (probably not something you'd do in the long-term), then you'd probably rewrite the compilation rule (assuming GNU Make):
% : %.cpp
${CXX} ${CXXFLAGS} -o $# $*.cpp
./$#
If you have a classic or POSIX variant of make, you'd write:
.cpp:
${CXX} ${CXXFLAGS} -o $# $*.cpp
./$#
The notation using '%' is more flexible when it is available.

C++, the "Old Fashioned" way

I have been learning C++ in school to create small command-line programs.
However, I have only built my projects with IDEs, including VS08 and QtCreator.
I understand the process behind building a project: compile source to object code, then link them into an executable that is platform specific (.exe, .app, etc). I also know most projects also use make to streamline the process of compiling and linking multiple source and header files.
The thing is, although IDEs do all this under the hood, making life very easy, I don't really know what is really happening, and feel that I need to get accustomed to building projects the "old fashioned way": from the command line, using the tool chain explicitly.
I know what Makefiles are, but not how to write them.
I know what gcc does, but not how to use it.
I know what the linker does, but not how to use it.
What I am looking for, is either an explanation, or link to a tutorial that explains, the workflow for a C++ project, from first writing the code up to running the produced executable.
I would really like to know the what, how, and why of building C++.
(If it makes any difference, I am running Mac OS X, with gcc 4.0.1 and make 3.81)
Thanks!
Compiling
Let's say you want to write a simple 'hello world' application. You have 3 files, hello.cpp hello-writer.cpp and hello-writer.h, the contents being
// hello-writer.h
void WriteHello(void);
// hello-writer.cpp
#include "hello-writer.h"
#include <stdio>
void WriteHello(void){
std::cout<<"Hello World"<<std::endl;
}
// hello.cpp
#include "hello-writer.h"
int main(int argc, char ** argv){
WriteHello();
}
The *.cpp files are converted to object files by g++, using the commands
g++ -c hello.cpp -o hello.o
g++ -c hello-writer.cpp -o hello-writer.o
The -c flag skips the linking for the moment. To link all the modules together requires running
g++ hello.o hello-writer.o -o hello
creating the program hello. If you need to link in any external libraries you add them to this line, eg -lm for the math library. The actual library files would look something like libm.a or libm.so, you ignore the suffix and the 'lib' part of the filename when adding the linker flag.
Makefile
To automate the build process you use a makefile, which consists of a series of rules, listing a thing to create and the files needed to create it. For instance, hello.o depends on hello.cpp and hello-writer.h, its rule is
hello.o:hello.cpp hello-writer.h
g++ -c hello.cpp -o hello.o # This line must begin with a tab.
If you want to read the make manual, it tells you how to use variables and automatic rules to simplify things. You should be able to just write
hello.o:hello.cpp hello-writer.h
and the rule will be created automagically. The full makefile for the hello example is
all:hello
hello:hello.o hello-writer.o
g++ hello.o hello-writer.o -o hello
hello.o:hello.cpp hello-writer.h
g++ -c hello.cpp -o hello.o
hello-writer.o:hello-writer.cpp hello-writer.h
g++ -c hello-writer.cpp -o hello-writer.o
Remember that indented lines must start with tabs. Not that not all rules need an actual file, the all target just says create hello. It is common for this to be the first rule in the makefile, the first being automatically created when you run make.
With all this set up you should then be able to go to a command line and run
$ make
$ ./hello
Hello World
More advanced Makefile stuff
There are also some useful variables that you can define in your makefile, which include
CXX: c++ compiler
CXXFLAGS:
Additional flags to pass to the
compiler (E.g include directories
with -I)
LDFLAGS: Additional flags to
pass to the linker
LDLIBS: Libraries
to link
CC: c compiler (also used to
link)
CPPFLAGS: preprocessor flags
Define variables using =, add to variables using +=.
The default rule to convert a .cpp file to a .o file is
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $#
where $< is the first dependancy and $# is the output file. Variables are expanded by enclosing them in $(), this rule will be run with the pattern hello.o:hello.cpp
Similarly the default linker rule is
$(CC) $(LDFLAGS) $^ -o $# $(LDLIBS)
where $^ is all of the prerequisites. This rule will be run with the pattern hello:hello.o hello-writer.o. Note that this uses the c compiler, if you don't want to override this rule and are using c++ add the library -lstdc++ to LDLIBS with the line
LDLIBS+=-lstdc++
in the makefile.
Finally, if you don't list the dependancies of a .o file make can find them itself, so a minimal makefile might be
LDFLAGS=-lstdc++
all:hello
hello:hello.o hello-writer.o
Note that this ignores the dependancy of the two files on hello-writer.h, so if the header is modified the program won't be rebuilt. If you're interested, check the -MD flag in the gcc docs for how you can automatically generate this dependancy.
Final makefile
A reasonable final makefile would be
// Makefile
CC=gcc
CXX=g++
CXXFLAGS+=-Wall -Wextra -Werror
CXXFLAGS+=-Ipath/to/headers
LDLIBS+=-lstdc++ # You could instead use CC = $(CXX) for the same effect
# (watch out for c code though!)
all:hello # default target
hello:hello.o hello-world.o # linker
hello.o:hello.cpp hello-world.h # compile a module
hello-world.o:hello-world.cpp hello-world.h # compile another module
$(CXX) $(CXXFLAGS) -c $< -o $# # command to run (same as the default rule)
# expands to g++ -Wall ... -c hello-world.cpp -o hello-world.o
A simple example is often useful to show the basic procedure, so:
Sample gcc usage to compile C++ files:
$ g++ -c file1.cpp # compile object files
[...]
$ g++ -c file2.cpp
[...]
$ g++ -o program file1.o file2.o # link program
[...]
$ ./program # run program
To use make to do this build, the following Makefile could be used:
# main target, with dependencies, followed by build command (indented with <tab>)
program: file1.o file2.o
g++ -o program file1.o file2.o
# rules for object files, with dependencies and build commands
file1.o: file1.cpp file1.h
g++ -c file1.cpp
file2.o: file2.cpp file2.h file1.h
g++ -c file2.cpp
Sample Makefile usage:
$ make # build it
[...]
$ ./program # run it
For all the details you can look at the Gnu make manual and GCC's documentation.
I know what Makefiles are, but not how to write them.
The make syntax is horrible, but the GNU make docs aren't bad. The main syntax is:
<target> : <dependency> <dependency> <dep...>
<tab> <command>
<tab> <command>
Which defines commands to build the target from the given dependencies.
Reading docs and examples is probably how most people learn makefiles, as there are many flavors of make with their own slight differences. Download some projects (pick something known to work on your system, so you can actually try it out), look at the build system, and see how they work.
You should also try building a simple make (strip out a bunch of the harder features for your first version); I think this is one case where that will give you a much better grasp on the situation.
I know what gcc does, but not how to use it.
Again, man g++, info pages, and other documentation is useful, but the main use when you call it directly (instead of through a build system) will be:
g++ file.cpp -o name # to compile and link
g++ file.cpp other.cpp -o name # to compile multiple files and link as "name"
You can also write your own shell script (below is my ~/bin/c++ simplified) to incorporate $CXXFLAGS so you won't forget:
#!/bin/sh
g++ $CXXFLAGS "$#"
You can include any other option as well. Now you can set that environment variable ($CXXFLAGS, the standard variable for C++ flags) in your .bashrc or similar, or redefine it in a particular session, for working without a makefile (which make does do just fine, too).
Also use the -v flag to see details on what g++ does, including...
I know what the linker does, but not how to use it.
The linker is what takes the object files and links them, as I'm sure you know, but g++ -v will show you the exact command it uses. Compare gcc -v file.cpp (gcc can work with C++ files) and g++ -v file.cpp to see the difference in linker commands that often causes the first to fail, for example. Make also shows the commands as it runs them by default.
You are better off not using the linker directly, because it is much simpler to use either gcc or g++ and give them specific linker options if required.
Just to throw this out there, the complete gcc documentation can be found here: http://www.delorie.com/gnu/docs/gcc/gcc_toc.html
compiler takes a cpp and turns into an object file which contains native code and some information about that native code
a linker takes the object files and lays out an excutable using the extra information in the object file.... it finds all the references to the same things and links them up, and makes and image useful for the operating system to know how to load all the code into memory.
check out object file formats to get a better understanding of what the compiler produces
http://en.wikipedia.org/wiki/Object_file (different compilers use different formats)
also check out (for gcc)
http://pages.cs.wisc.edu/~beechung/ref/gcc-intro.html on what you type at the command line
You might also look into Autoproject, which sets up automake and autoconf files, which makes it easier for people to compile your packages on different platforms: http://packages.debian.org/unstable/devel/autoproject
I like this quirky intro to building a hello world program with gcc, Linux-based but the command-line stuff should work fine on OS/X. In particular, it walks you through making some common mistakes and seeing the error messages.
Holy Compilers, Robin, the darn thing worked!
This is what has helped me to learn the autoconf, automake, ...:
http://www.bioinf.uni-freiburg.de/~mmann/HowTo/automake.html
It is a nice tutorial progresses from a simple helloworld to more advanced structures with libraries etc.