Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

Week 2

Download as pdf or txt
Download as pdf or txt
You are on page 1of 8

Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor

s Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make

Textbook Reading (Hanly and Koffman) Outline


UNIX and C Programming (COMP1000) Header Files

Lecture 2: Environments Compiling


For more information, see the weekly reading list on Blackboard.
The Preprocessor
Updated: 5th August, 2019 ▶ Chapter 12: Programming in the Large
Unfortunately, some examples in this chapter use structs. We Linking
Department of Computing won’t discuss structs until lecture 6, so ignore them for now.
Curtin University
Access
Copyright © 2019, Curtin University
CRICOS Provide Code: 00301J The Shell

Make

. . . . . . . . . . . .
. . . . . . . . . . . .

1/67 2/67 3/67


Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make

Multi-file C Programs Calling Functions in Different .c Files External Declarations


▶ Each .c file is compiled separately. ▶ We need a declaration:
▶ But you’ll need to make function calls between files.
calc.c
▶ Source files should not get too long. calc.c double square(double n) {
▶ Giant .c files are difficult to work with. double square(double n) { return n * n;
▶ So, most C programs are split into multiple files. return n * n; }
▶ Separate .c files can be used to group related functions. } /* Not visible from main.c! */
▶ And they should be related! main.c
▶ Files containing unrelated functions are also difficult to work main.c double square(double n); /* Declaration */
with. int main(void) {
▶ One .c file should contain the main() function. double result, input = 5; int main(void) {
result = square(input); double result, input = 5;
... result = square(input);
} /* Compiler doesn’t know what "square" means. */ ...
. . . . . . . . } /* Compiler is happy with this. */ . . . .
. . . . ▶ How do we fix this? . . . . . . . .

4/67 5/67 6/67


Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make

But then things get messy. . . Don’t Repeat Yourself (the DRY principle) Header File — Example
calc.c ▶ We could end up repeating the same declarations many times. calc.c
double square(double n) { return n * n; } double square(double n) { return n * n; }
▶ Say calc.c has 5 functions, and 10 other files use those
double cube(double n) { return n * n * n; } double cube(double n) { return n * n * n; }
functions.
▶ That’s 50 declarations.
main.c ▶ This is a very bad idea. calc.h (header file)
double square(double n); /* Declarations */ ▶ Copying and pasting is easy, but. . . double square(double n); /* Declarations */
double cube(double n); ▶ Changing those declarations is time consuming and prone to double cube(double n);
... mistakes.
result = square(5) + cube(5); ▶ So, we put all the declarations for calc.c in a “header file”. main.c
▶ When a file needs to use a function from calc.c, we write: #include "calc.h" /* Include header file */
aardvark.c
...
double square(double n); /* Repeated from above */ #include "calc.h" /* Note: .h not .c */
result = square(5) + cube(5);
double cube(double n);
... ▶ This takes the declarations in the header file calc.h and puts (Put “#include "calc.h"” in all files using square() or
printf("%lf %lf\n", square(x), cube(y)); . . . . them right here. . . . .
cube().) . . . .
. . . . . . . . . . . .

7/67 8/67 9/67


Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make

Header Files — Summary The Compilation Process The Compilation Process


C code C code
In a multi-file C program: Preprocessing
▶ Each .c file has a corresponding .h (header) file.
▶ (Except perhaps for the .c file containing main.) Preprocessed C code
▶ A header file declares the functions in its .c file.
▶ To call those functions from a different .c file, that .c file
must #include the right header file.
▶ Each .c file also #includes its own header file.
▶ e.g. calc.c would also have “#include "calc.h"”.
▶ Not strictly necessary for now.
▶ Will become necessary later on, when we declare types.

Executable file Executable file


. . . . . . . . . . . .
. . . . . . . . . . . .

10/67 11/67 11/67


Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make

The Compilation Process The Compilation Process The Compilation Process


C code C code C code
Preprocessing Preprocessing Preprocessing
Preprocessed C code Preprocessed C code Preprocessed C code
Compilation Compilation Compilation
Assembler code Assembler code Assembler code
Assembly Assembly
Object file Object file
Linking
Executable file Executable file Executable file
. . . . . . . . . . . .
. . . . . . . . . . . .

11/67 11/67 11/67


Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make

The Preprocessor The #include Directive The #define Directive


▶ Inserts the contents of a file into the source code. ▶ Assigns a name to a constant value.
▶ A “macro-language”, separate from the real C language itself. ▶ In practice, used to access functions defined in other files. ▶ Everywhere that name occurs, the preprocessor replaces it
▶ Consists of “directives”, each starting with #, embedded in C ▶ (Similar to import in Java.) with the given value.
code. ▶ Always place these at the top of your code. ▶ Used to create constants and “macros”.
▶ (No semicolons required, unlike C itself.) ▶ Only #include .h (header) files, never .c files. ▶ Sometimes useful to place in a header file.

C code (with directives) Examples Examples


#include <stdio.h> #define PI 3.141592654
Preprocessing
#include "myfunctions.h" #define OUTPUT_STRING "Hello World!"

C code (without directives)


▶ Use <. . . > for standard header files (in pre-defined directories). ▶ There is no equals sign!
. . . .
▶ Use ". . . " for your own header files, in the current directory. . . . .
▶ PI and OUTPUT_STRING are not variables! . . . .
. . . . . . . . . . . .

12/67 13/67 14/67


Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make

#define — True and False #define — Macros #define — Macro Bracketing


▶ Small snippets of code with parameters (but not functions).
▶ Works by substitution.
▶ Place brackets around macro parameters.
▶ C89 has no “boolean” data type (though C99 does). Example (before preprocessing) ▶ Place brackets around the entire macro definition.
▶ Use int instead.
#define SQUARE(x) ((x) * (x))
▶ Create constants representing “true” and “false”. Bad example (valid but dangerous)
Common definition int squareSum(int a, int b) { #define SQUARE(x) x * x
return SQUARE(a + b);
#define FALSE 0 }
#define TRUE !FALSE Good example
(Why might you want to put this in a header file?) Result (after preprocessing) #define SQUARE(x) ((x) * (x))
int squareSum(int a, int b) {
return ((a + b) * (a + b));
. . . . . . . . . . . .
. . . . } . . . . . . . .

15/67 16/67 17/67


Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make

The #ifdef and #endif directives More Conditional Compilation Avoiding Multiple Inclusion
A segment of code is only compiled if a given name has been
#defined (“conditional compilation”). ▶ With the #include directive, some files may be included
multiple times.
Example ▶ Preprocessor names can be defined on the compiler
▶ This may cause problems in some situations.
command-line as well:
#define DEBUG 1
[user@pc]$ gcc main.c -o program -D DEBUG=1 ▶ Can be avoided using conditional compilation.
...
int i, sum = 0; ▶ #ifndef checks that a given name has not been defined. Example (where “. . . ” is the normal header file contents)
for(i = 0; i < 100; i++) { ▶ #else is available for convenience.
sum += i; #ifndef FILENAME_H
▶ #if and #elif are slightly more flexible versions. #define FILENAME_H
#ifdef DEBUG
▶ These all work like an if-else statement, but at compile ...
printf("%d ", sum);
#endif time (not run time). #endif
}
Only the first inclusion will count, because FILENAME_H will be
Without the first line, the preprocessor edits out the printf(). . . . . . . . .
defined from then on. . . . .
. . . . . . . . . . . .

18/67 19/67 20/67


Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make

Assembly Language Machine Code Object Files


▶ “Compiling” translates source code into “assembly language”.
▶ This is a simple language, but extremely verbose and barely
human readable. ▶ The machine-code form of a program is the code that actually ▶ Preprocessing, compiling and assembly are just sequential
▶ Different brands of CPU often require different assembly runs. steps:
languages. ▶ Machine code is a compacted form of assembly language. ▶ One input and one output each.
▶ An “assembler” (e.g. the one inside gcc) translates assembly ▶ Each instruction takes only a few bytes (many only 1 byte). ▶ Together, all three are often called “compiling”.
code into machine code. ▶ The last output is an object (.o) file for each .c file.
▶ There are no names, spacing or syntactical constructs.
addNumbers: ▶ (This has nothing to do with object orientation!)
▶ None of it makes sense when simply printed on the screen.
pushl %ebp ▶ .o files contain compiled code, but they are not executable.
▶ Much of it cannot be printed at all, because it doesn’t match
movl %esp, %ebp ▶ They contain functions and function calls that have not yet
movl 12(%ebp), %eax any printable characters.
been “linked” to each other.
movl 8(%ebp), %edx ▶ That is, it is not human-readable!
leal (%edx,%eax), %eax
popl %ebp
. . . . . . . . . . . .
ret . . . . . . . . . . . .

21/67 22/67 23/67


Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make

Linking Linking with gcc Libraries


▶ Takes one or multiple .o files and produces a single executable. ▶ To compile a single .c file into an object (.o) file without
▶ Often you’ll need to link to an external library.
▶ Determines how to physically arrange functions in memory. linking:
▶ Sometimes you’ll need more than just the #include directive.
▶ Connects function calls to the actual functions, by translating [user@pc]$ gcc -c filename.c
their names to their memory locations. ▶ To link multiple .o files into an executable file: Example
▶ This makes function calls possible (especially between files)! Under Linux, if you use the math library, you need to:
[user@pc]$ gcc obj1.o obj2.o ... -o executable
▶ For example: ▶ place “#include <math.h>” at the top of your code, and
Source files: main.c other1.c other2.c
▶ when linking, use the “-lm” switch (lowercase L, not 1).
[user@pc]$ gcc -c main.c -Wall -pedantic -ansi
[user@pc]$ gcc -lm main.o calc.o -o mathyprogram
Object files: main.o other1.o other2.o [user@pc]$ gcc -c other1.c -Wall -pedantic -ansi

[user@pc]$ gcc -c other2.c -Wall -pedantic -ansi Linker switches


The “-lm” switch is specific to the math library. For other
Executable file: program [user@pc]$ gcc main.o other1.o other2.o -o prog libraries, see the manpage.
. . . . . . . . . . . .
. . . . . . . . . . . .

24/67 25/67 26/67


Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make

Storage class specifiers Local variables Global variables (evil!)


▶ A “local” variable is the kind you already know. ▶ Created outside functions — stand-alone.
▶ These keywords can form part of a declaration: auto, ▶ Created inside functions. ▶ Accessible from all functions.
▶ Only accessible from within that same function. ▶ Possibly accessible across different source files.
register, typedef, extern and static. e.g.
▶ Can also be restricted to one pair of braces in a function. ▶ Creates a mess! (High coupling.)
static double x = 3.0; ▶ Avoid global variables. Instead, use parameters to pass data
Example
between functions! (This discussion is merely FYI.)
▶ They are not part of the datatype, but indicate where a
void theFunction(void) { Example
variable is stored.
▶ auto is the default for “local” variables (and is almost never int x = 0; /* Only exists in theFunction. */
explicitly written). ... #include <stdio.h>
▶ register tries to store variables in CPU registers. if(...) {
▶ typedef causes the declaration to create a new datatype, int y = 5; /* Only exists in the ’if’. */ int globalVar = 42; /* Never do this! */
rather than a variable. (This is discussed in lectures 3 and 6). ....
▶ static and extern are discussed on the next few slides. } void printGV(void) {
} printf("%d", globalVar); /* prints 42 */
. . . . . . . . . . . .
. . . . . . . . } . . . .

27/67 28/67 29/67


Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make

The extern storage class The static storage class Static local variables
▶ extern can be used on both function and variable ▶ static can also be used on function and variable declarations. ▶ Ordinary local variables disappear when a function ends.
declarations. ▶ static local variables don’t disappear.
▶ Two distinct meanings, depending on where it occurs:
▶ It means that the definition occurs somewhere else. ▶ They keep their values between function calls.
▶ For functions, this is true anyway, so extern is redundant. 1. static makes a function (or global variable) inaccessible ▶ Initialised only once, when the function is first called.
▶ Consider these equivalent declarations: from outside this file.
Example
float theFunction(int x, int y); static void privateFunc(int x, int y) { ... }
void count(void) {
extern float theFunction(int x, int y); ▶ Good practice for functions that don’t need to be accessed static int counter = 0;
elsewhere. counter++;
The extern form often appears in header files, but only as a ▶ Vaguely similar to the “private” keyword in OO languages (like printf("%d\n", counter);
reminder. C++ or Java), but don’t confuse .c files with classes! }
▶ For variables, extern allows you to access global variables
2. static makes a local variable persistant throughout the
across files. This is both complicated and dangerous, so we program’s runtime. counter increases each time count() is called, and never resets
won’t waste our time on it! . . . . . . . . until the program ends. . . . .
. . . . . . . . . . . .

30/67 31/67 32/67


Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make

The Terminal vs. the Shell Shells UNIX/Shell Commands (1)


▶ We’ve talked a lot about how to run gcc, but let’s step back.
The Terminal
▶ Many different shells are available: ▶ gcc is one command among many.
▶ The window where input and output occurs.
▶ sh (Bourne shell) and bash (Bourne Again shell); ▶ You should be familar with a few other commands as well:
▶ Takes a program’s output and displays it. ▶ csh (C shell) and tcsh; ▶ ls — list files;
▶ Gives a program its input from the keyboard. ▶ ksh (Korn shell); ▶ cd — change directory;
▶ zsh (Z shell); ▶ pwd — show the present working directory;
▶ ash (Almquist shell); ▶ cat — concatenate and output files (or just one file);
The Shell ▶ cmd.exe (the Windows command prompt); ▶ cp — copy files;
▶ A program that runs other programs within the terminal. ▶ And others. ▶ mv — move files;
▶ These all do essentially the same thing, in different ways. ▶ rm — remove (delete) files;
▶ Interprets and executes your commands.
▶ mkdir — make directory;
▶ Temporarily gives control of the terminal to another program, ▶ bash is the most popular on Linux and OS X. ▶ rmdir — remove directory;
when you run it. ▶ echo — print a message;
▶ Etc.
. . . . . . . . . . . .
. . . . . . . . . . . .

33/67 34/67 35/67


Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make

UNIX/Shell Commands (2) Executing Commands Command Arguments


▶ Command “arguments” (or “parameters”) are strings of text,
▶ The shell provides you with a “prompt”: supplied to a command or program. (A kind of user input.)
When you type in a command: ▶ The user is free to enter any number of arguments.
[user@pc]$ 1. The shell performs various “expansions” and “substitutions”: ▶ Lecture 4 will discuss how to use them in C programs.
As you know, this is where you enter commands. (The prompt ▶ The symbols *, ?, ~ and [. . . ] are “expanded”. ▶ Arguments starting with a dash (“-”) are called “switches”,
itself also contains useful information, if you look closely.) ▶ Variables are replaced with their values (discussed shortly). “options” or “flags”.
▶ Many other things. . . ▶ Switches alter the behaviour of a command.
▶ Use man (“manual”) to get help on a given command:
2. Your input is broken up into words, separated by whitespace. ▶ Some switches take their own argument (e.g. “-o” for gcc).
[user@pc]$ man cat Usually:
(Note: man can also be used with standard C functions.) ▶ The first word is the command name.
▶ The other words are the parameters. command name ordinary argument
▶ Alternatively, most commands let you do this:
(Note: this is only a simplistic overview.) [user@pc]$ gcc -Wall prog.c -o prog
[user@pc]$ cat --help
switch switch with argument
. . . . . . . . . . . .
. . . . . . . . . . . .

36/67 37/67 38/67


Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make

The Command Name Asynchronous Commands Shell Variables


The command name itself might be: ▶ In the shell, all variables are strings.
▶ An “builtin” command — a feature of the shell itself: ▶ No declarations needed.
[user@pc]$ source file.sh ▶ To run a command “in the background”, end it with “&” ▶ They are created and assigned like this:

▶ A filename containing a “/” — an executable file to run: ▶ Particularly useful for commands/programs that open GUI varname="Some text"
windows: ▶ No spaces except inside quotes! Really.
[user@pc]$ ./program apple banana caroot
[user@pc]$ gedit somefile.txt & ▶ They are accessed using a $ sign:
▶ An executable file in the “search path”:
Here, you can still use the shell while gedit is running.
echo The string is $varname
[user@pc]$ ls -l Desktop ▶ Backgrounded programs can still output to the terminal, even
while you’re typing a command ▶ The shell will replace “$varname” with “Some text”.
▶ The shell searches a list of directories for the file “ls”. ▶ echo will then display “The string is Some text”.
▶ The directories to search are specified by a variable called PATH ▶ This can lead to confusion (just be aware of it!)
(typically containing /bin and /usr/bin). ▶ Use {. . . } around the variable name if necessary:

▶ Standard UNIX commands may be either builtins or files in echo ${varname}y stuff
the search path. . . . . . . . .
This will print out “Some texty stuff”. . . . .
. . . . . . . . . . . .

39/67 40/67 41/67


Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make

Environment Variables Common Environment Variables PATH-like Variables


▶ Every program has an “environment”, consisting of ▶ PATH — a list of directories to search for commands. ▶ PATH, CLASSPATH and LD_LIBRARY_PATH all contain a list of
“environment variables” containing system settings. ▶ CLASSPATH — a list of directories to search for Java classes. directories.
▶ These are inherited when the program starts.
▶ LD_LIBRARY_PATH — a list of directories to search for native ▶ Directories are separated by “:”.
▶ They can be accessed just like normal shell variables:
shared libraries. ▶ PATH might be defined like this:
echo $ENVVAR ▶ USER — your username.
export PATH=/bin:/usr/bin:/opt/bin
▶ Where ENVVAR is just an example, not a real variable. ▶ HOSTNAME — the name of the computer.
▶ The uppercase is conventional for environment variables. ▶ HOME — your home directory. ▶ We can append or prepend directories like this:
▶ To modify them in bash, use the export builtin command: ▶ PWD — the current directory.
export PATH=${PATH}:/usr/local/bin
ENVVAR="new value" ▶ SHELL — the current shell (e.g. /bin/bash).
export ENVVAR ▶ TERM — the type of terminal being used.
export PATH=/usr/local/bin:${PATH}
▶ Many more (often application-specific).
OR equivalently:
(Note: CLASSPATH is Java-specific, and doesn’t really have Here, PATH is set to a new string, which contains the old
export ENVVAR="new value" string.
. . . .
. . . .
anything to do with UNIX.) . . . .
. . . .
. . . .
. . . .

42/67 43/67 44/67


Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make

Pathname Expansion Tilde (“~”) Expansion Expansion — Examples


▶ A word containing *, ? or [...] will undergo “pathname ▶ The symbol ~ is replaced with your home directory.
Pattern Expands to. . .
expansion”. ▶ For example:
▶ The shell will treat the word as a pattern, where: * all files (not starting with “.”) in the current directory.
ls ~/Desktop
▶ ? stands for any single character. ? all one-letter files.
▶ * stands for any sequence of characters (including zero). *.c all files ending in “.c”.
▶ [...] stands for a single character from the given set. ▶ ~ becomes /home/username.
▶ The actual command line is “ls /home/username/Desktop”.
abc*.o all files starting with “abc” and ending with “.o”.
For example, [abcm-z] stands for “a”, “b”, “c” or any [A-Z]* all files starting with an uppercase letter.
character from “m” to “z”. ▶ You can also get to other people’s home directories. .* all files starting with “.” (not matched by any of the
▶ [^...] stands for a single character not from the given set.
▶ Put their username after the ~: above patterns).
▶ The shell replaces the pattern with a list of matching files. .[ˆ.]* all files starting with one “.” only.
▶ However, files starting with “.” are ignored (unless the pattern ls ~/joe
*/* all files in subdirectories of the current directory.
itself starts with “.”) ~/def/* all files in def, in your home directory.
▶ This lists the files in joe’s home directory, if joe exists.
▶ If no files match, the pattern is left unchanged. ▶ You will rarely have permission to do this.
. . . . . . . . . . . .
. . . . . . . . . . . .

45/67 46/67 47/67


Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make

Quoting (1) Quoting (2) Aliases


▶ Backslashes and quotes prevent certain shell actions. ▶ Aliases allow you to create shortcuts for commands, or to
▶ This is useful when: redefine commands.
▶ We want a normal “*”, “&”, etc. character.
▶ Compared to shell variables:
▶ We want to have whitespace inside a word.
▶ Aliases also have a name, replaced by a textual value.
▶ Any single character preceeded by a backslash is taken literally ▶ Quotes are often used when assigning variables: ▶ Aliases only apply to the first word in a command, and there
echo Some special characters: \&, \*, \\, \" varname="Some Text Containing Whitespace" are no $ signs.
▶ You define an alias like this:
▶ Everything in single quotes (’. . . ’) is taken literally: (Without the quotes, the whitespace would make this illegal.)
alias name=’command param1 param2 ...’
echo ’${PATH} ==’ ${PATH} ▶ Quotes are also used when dealing with strange filenames.
▶ Double quotes (". . . ") allow variable substitutions and ▶ Aliases are commonly used to specify default parameters:
backslashes only: alias rm=’rm -i’
gedit "strange *\"file${var}.txt"
▶ After this, typing “rm” will actually invoke “rm -i”.
If var contains “X”, gedit will open “strange *"fileX.txt”. . . . . . . . .
▶ -i causes rm to ask you before deleting a file! . . . .
. . . . . . . . . . . .

48/67 49/67 50/67


Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make

Settings Files Make Makefiles


▶ A “makefile” tells “Make” what to create, when, and how.
▶ bash reads several files containing lists of shell commands. ▶ Contains a series of “rules”.
▶ When you log in, bash reads: ▶ Each rule consists of:
▶ Compiling large programs can take a long time.
1. /etc/profile — a system-wide configuration file. Target – the file to create/update.
▶ When a small change is made, you don’t need to recompile Prerequisites – the file(s) needed to make it.
2. ~/.profile (or ~/.bash_profile, or ~/.bash_login) — a
user-specific configuration file. the whole program. Recipe – the command(s) to make it, indented with a
These set up environment variables — PATH, CLASSPATH, etc. ▶ Recompile an object file when: single TAB character (not with spaces).
▶ When you open a new terminal (after you log in): ▶ The original .c file changes.
▶ Any of the included .h files change. targetfile : prerequisite1 prerequisite2 ...
▶ bash reads ~/.bashrc only. command1
▶ This is where you can put alias definitions! (Or any other ▶ The “make” tool helps automate this, and generally makes command2
shell-specific settings.) compiling easier. ...
▶ When you log out:
▶ bash reads ~/.bash_logout. ▶ A makefile has several rules, one after another.
▶ Makefiles can also have comments (documentation) starting
. . . .
. . . .
. . . .
. . . .
with #. . . . .
. . . .

51/67 52/67 53/67


Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make

What Make Does What About the Other Rules? Makefiles and C
▶ To run make: ▶ A prerequisite in one rule might be a target in another (with ▶ Make is very powerful, but we’ll focus on a limited subset of it.
[user@pc]$ make its own prerequisites). ▶ To create a makefile for a C application, we’ll have:
▶ One rule (the first rule) to create the executable.
▶ Make looks for a file called “Makefile” (exactly), and reads it. endfile : middlefile1 middlefile2 ...
▶ One rule to create each object file.
▶ Make looks at the first rule, by default. commandA
▶ The executable’s prerequisites are the object (.o) files.
▶ This is usually fine, but you can specify a different target: ▶ Each .o file’s prerequisites are:
middlefile1 : startfile1 startfile2
[user@pc]$ make anothertarget ▶ A single .c file with the same name;
commandB
▶ Any .h files #included by the .c file;
▶ Make asks these questions: ... ▶ Any other .h files #included by those .h files, and so on.
▶ Does the target file exist? ▶ Why are all the .h files included in the prerequisites?
▶ If so, make runs the other rule as well.
▶ Is the target newer than all its prerequisites? ▶ If .h files change, we would like Make to recompile the .c file.
▶ Does the secondary target (“middlefile1” above) exist?
▶ If so, the target is “up-to-date”, and nothing happens. ▶ Is the secondary targer newer than its own prerequisites? ▶ The contents of the .h files (particularly macros and constants)
▶ If not, make will run the command(s) to (re)create it. ▶ Typically, most makefile rules are connected in this way. will affect the compiled result.
▶ Make just assumes the commands will create the target file. ▶ Make will create middlefile1 and endfile, in that order. ▶ No rules to create .c or .h files.
▶ You must provide the correct commands. . . . .
▶ Once created, make will update them as needed. . . . .
▶ These files are created by the programmer! . . . .
. . . . . . . . . . . .

54/67 55/67 56/67


Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make

Makefile Example Makefile Diagram Makefile Example – Rule for the Executable
▶ Say our C application has these files:
▶ First, we write a rule for making the executable:
main.c – contains the main function.
aardvark.c – contains aardvark-related functions. program : main.o aardvark.o narwal.o
aardvark.h – contains aardvark-related declarations. gcc main.o aardvark.o narwal.o -o program
narwal.c – contains narwal-related functions.
▶ We make “program”, given main.o, aardvark.o and
narwal.h – contains narwal-related declarations.
narwal.o.
▶ main.c and aardvark.c both contain this line: ▶ This is the linking step.
▶ At first, these .o files don’t exist, but we’ll create them too in
#include "aardvark.h"
other makefile rules.
▶ main.c and narwal.c both contain this line: ▶ Make will run the gcc command if:
▶ program does not exist, or
#include "narwal.h" ▶ program is older than any of the .o files.

. . . . . . . . . . . .
. . . . . . . . . . . .

57/67 58/67 59/67


Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make

Makefile Example – Rules for the Object Files (1) Makefile Example – Rules for the Object Files (2) Makefile Example – Put Together
▶ Next, we write a rule for each .o file. ▶ A makefile is a single file, so let’s see it altogether:
1. Create main.o, based on main.c and both .h files. program : main.o aardvark.o narwal.o
2. Create aardvark.o, based on aardvark.c/.h. ▶ The final two rules, for completeness.
gcc main.o aardvark.o narwal.o -o program
3. Create narwal.o, based on narwal.c/.h.
aardvark.o : aardvark.c aardvark.h
▶ For example: gcc -c aardvark.c -Wall -ansi -pedantic main.o : main.c aardvark.h narwal.h
main.o : main.c aardvark.h narwal.h gcc -c main.c -Wall -ansi -pedantic
gcc -c main.c -Wall -ansi -pedantic narwal.o : narwal.c narwal.h
gcc -c narwal.c -Wall -ansi -pedantic aardvark.o : aardvark.c aardvark.h
▶ This is the compiling step (“-c” for compile only; don’t link). gcc -c aardvark.c -Wall -ansi -pedantic
▶ Make will run this command if: ▶ aardvark.o does not depend on narwal.h (and vice versa).
▶ main.o does not exist, or ▶ They only #include one .h file each. narwal.o : narwal.c narwal.h
▶ main.o is older than main.c or either .h file. gcc -c narwal.c -Wall -ansi -pedantic
▶ Notice that the .h files are not part of the command.
▶ gcc knows about the .h files, because the .c file tells it. ▶ Typing “make” will run each of these commands, if needed.
▶ However, we’re not quite finished yet!
. . . . . . . . . . . .
. . . . . . . . . . . .

60/67 61/67 62/67


Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make

Cleaning Up Make Variables Traditional Make Variables


▶ Makefile rules can do anything, not just compile or link. ▶ Makefiles have their own variables (a bit like shell variables).
▶ Why?
▶ Traditionally there’s also a “clean” rule to remove all ▶ To avoid repetition. ▶ You should use at least the following variables (for the
generated files (object and executable files). ▶ To allow easy configuration changes. purposes of this unit):
Example CFLAGS: Flags for the C compiler, used for each .o file rule.
Common definition
OBJ: A list of .o files, used in the executable rule and the
clean: CFLAGS = -Wall -pedantic -ansi clean rule.
rm -f program main.o aardvark.o narwal.o ... ▶ Others to consider:
main.o : main.c aardvark.h narwal.h EXEC: The executable filename, used in the executable and
gcc -c main.c $(CFLAGS)
▶ To execute this rule, run: clean rules.
... CC: The C compiler command. Mostly this is just gcc, but
[user@pc]$ make clean
in principle, on other platforms, it can change.
▶ This is a bit of a hack. It works because the file “clean” is ▶ We’d do the same for the other two .o rules.
never actually created (hopefully). ▶ So, we can modify them in one place.
. . . . . . . . . . . .
. . . . . . . . . . . .

63/67 64/67 65/67


Header Files Compiling The Preprocessor Linking Access The Shell Make Header Files Compiling The Preprocessor Linking Access The Shell Make

A Better Example Makefile Coming Up


CC = gcc
CFLAGS = -Wall -pedantic -ansi
OBJ = main.o aardvark.o narwal.o
EXEC = program
▶ Next week’s lecture will introduce you to pointers!
$(EXEC) : $(OBJ)
$(CC) $(OBJ) -o $(EXEC) ▶ Make sure you do the tutorial exercises — you will use header
files and Makefiles throughout the rest of the unit.
main.o : main.c aardvark.h narwal.h
$(CC) -c main.c $(CFLAGS)

... # Similar rules for aardvark.o and narwal.o.

clean:
. . . . . . . .
rm -f $(EXEC) $(OBJ) . . . . . . . .

66/67 67/67

You might also like