|
|
|
Lecture 14 — make and gdb
|
|
|
|
|
compilation
|
|
|
|
|
.c file -> preprocessor -> preprocessed C file -> compiler -> .o file -> linker -> executable
|
|
|
|
|
preprocessor handles preprocessor directives like #include and #define
|
|
|
|
|
outputs pure C code (i.e., C code without any preprocessor directives)
|
|
|
|
|
one file at a time
|
|
|
|
|
compiler parses source code and compiles it into binary form (object file)
|
|
|
|
|
one file at a time
|
|
|
|
|
object files can refer to symbols that are not defined
|
|
|
|
|
stage at which syntax and type errors are produced
|
|
|
|
|
linker produces final executable by combining object files
|
|
|
|
|
handles multiple files at once
|
|
|
|
|
replaces references to undefined symbols with correct address
|
|
|
|
|
common errors are missing definitions and duplicate definitions
|
|
|
|
|
make
|
|
|
|
|
one way to think about compiling C programs is in terms of dependencies (i.e., files depend on other files)
|
|
|
|
|
a .o file depends on its .c file and the .h files that .c file includes
|
|
|
|
|
the executable depends on the .o files
|
|
|
|
|
to compile something, its dependencies need to be available
|
|
|
|
|
we can represent dependencies as a directed acyclic graph (DAG)
|
|
|
|
|
a graph is a structure consisting of nodes and edges (a tree is a type of graph)
|
|
|
|
|
directed means the edges have a direction (they are one-way arrows)
|
|
|
|
|
acyclic means there are no cycles (loops) in the graph
|
|
|
|
|
exercise: why would the representation of dependencies need to be acyclic?
|
|
|
|
|
a depends on b depends on c depends on a means nothing could ever be compiled
|
|
|
|
|
exercise: draw the DAG for the following code (include .o files and final executable talk) speak.c: #include “speak.h” shout.c: #include “speak.h” #include “shout.h” main.c: #include “speak.h” #include “shout.h”
|
|
|
|
|
talk -> speak.o shout.o main.o speak.o -> speak.c speak.h shout.o -> shout.c speak.h shout.h main.o -> main.c speak.h shout.h
|
|
|
|
|
managing all the dependencies for a large program could get very complicated and time consuming
|
|
|
|
|
write a program to take care of the compilation
|
|
|
|
|
Bash script would work…
|
|
|
|
|
…but what if we don’t want to recompile the entire codebase each time?
|
|
|
|
|
exercise: when do we need to recompile?
|
|
|
|
|
when the compilation result (.o file, executable) is older than the corresponding source files
|
|
|
|
|
could be pretty tricky to do in Bash
|
|
|
|
|
answer: make
|
|
|
|
|
a Unix program with its own specialized scripting language
|
|
|
|
|
consists of rules of the form target … : prerequisites … recipe …
|
|
|
|
|
target … is a list of the outputs
|
|
|
|
|
prerequisites … is a list of the dependencies
|
|
|
|
|
recipe … is a series of shell commands to run when a target doesn’t exist or a target is older than a prerequisite
|
|
|
|
|
if any prerequisites appear as targets in other rules, those rules are checked first
|
|
|
|
|
make takes in a program in this language
|
|
|
|
|
running make by itself will look for a file named makefile or Makefile
|
|
|
|
|
the first rule listed in the file is checked
|
|
|
|
|
rules for prerequisites of the first rules are checked, and so on down the chain of prerequisites
|
|
|
|
|
let’s write a makefile for out linked list program
|
|
|
|
|
see posted file
|
|
|
|
|
gdb
|
|
|
|
|
no debugging symbols found means you forget to compile with -g
|
|
|
|
|
help COMMAND to get information about that command
|
|
|
|
|
break FILENAME:LINE_NUMBER to set a breakpoint
|
|
|
|
|
gdb will pause program execution whenever it reaches this line
|
|
|
|
|
break FUNCTION_NAME to pause whenever that function is called
|
|
|
|
|
info locals to see all local variables
|
|
|
|
|
info args to see all arguments to current function
|
|
|
|
|
step to move execution on step forward
|
|
|
|
|
next line or into function call
|
|
|
|
|
entering a blank line will repeat the previous command
|
|
|
|
|
finish to return from the current function
|
|
|
|
|
continue to resume execution
|
|
|
|
|
display EXPR to print EXPR after each command
|
|
|
|
|
x EXPR examine memory at address EXPR
|
|
|