Linux makefile

gcc

GCC is the compiler used to compile code. There are some common output files after compilation: A static library (document).c Preprocessed C language source code. H HEADER file of C language source code. I Preprocessed C language source code

GCC compilation process:

C and h files are preprocessed to generate. I files, which are then compiled to generate assembly language code. S files are processed to generate object files.

Let’s create a file and write a simple code

#include <stdio.h>
int main(a)
{
    printf("HelloWorld");
}
Copy the code

pretreatment

Preprocessing instruction:

gcc -E helloWorld.c -o helloWorld.i
Copy the code

There is an extra. I file after executionThen through the LS -L command to view the details of the file, you can see the size of the file, after preprocessing from 59B to 16335B, increased several timesThe reason for the larger size: we referred to the header file stdio.h before, and after the preprocessing, all the contents in.h were copied to this file, so the size of the file became larger.

You can view the details of the file from less Helloworld. I:

To produce assembler

The -s command is mainly used here. As you can see from the following figure, it can operate directly on.c and.i files because the preprocessing operation is already integrated in -s

Generate object file

In the same way, -o also integrates preprocessing and assembler operations, so it can operate directly on.c files:Let’s look at the details of the file:Here you can see that the.o file is smaller than the.s and.i files, because the.o file is just an executable file, but it doesn’t have the ability to execute. The.o file is the machine code that converts the assembly file into a binary file.

Executable files

GCC can read binary.o files directly, so you can use this instruction to generate an executable: GCC helloWorld. O -o myHello

Look at the file size of myHello, which is larger again. As we mentioned earlier, the.o file is an executable file, but it is not executable. To make it executable, GCC needs to add some configuration to the original.o files to make it executable.

digression

As you can see, we can compile.c directly to generate the executable, so what’s the use of the process we learned earlier to generate.i,.s,.o. Although the instructions are now integrated and can be used directly, we still need to know a little bit about these processes, because unlike Java, which is an interpreted language, C is a compiled language. For example, sometimes a program may go wrong not in a.C file, but in some other file. So these fundamental things still need to be understood.

In addition, this method is only used when the header file is an official library. If you use another library, this step will be wrong.

Makefile

Concept: When we compile with GCC, if we have many.c files, we need to enter instructions one by one, which is too cumbersome, so the Makefile comes into being. Makefiles are essentially scripts that simplify many repetitive operations.

Multifile compilation

So I’m going to start with a bunch of dot c files and dot h files, and what I’m going to do here is add, subtract and divide three functions, and I’m going to write them in C.Then myproject.c writes the main function:

#include <stdio.h>
#include "myadd.h"
#include "mysub.h"
#include "mymul.h"

void main(a)
{
    int n1 = 5;
    int n2 = 20;
    int n3 = 3;
    printf("%d\n", myAdd(n1, n2));
    printf("%d\n", mySub(n2, n3));
    printf("%d\n", myMul(n1, n2));
}
Copy the code

How to compile several files is as follows:Obviously, doing this is cumbersome. There are many instructions to execute, which we can do with makefiles.

The makefile rules

target:depend command

The target file can be an intermediate file or the final execution file depend, which refers to the file required to generate the target file or the command to be executed by the target command: make

For example, in this directive:

gcc -c mymul.c -o mymul.o
Copy the code

Mymul.c is a dependency and mymul.o is a target

Order of makefile execution: the first makefile is executed by default. When the first makefile is executed, it looks for all dependencies. If not, it looks for any scripts that can generate dependencies. Example:

test:prog.o code.o gcc -o test prog.o code.o prog.o:prog.c gcc -c prog.c -o prog.o code.o:code.c gcc -c code.c -o code.o  clean: rm -f *.oCopy the code

O and code. O files are generated by compiling the. C file. Then run the first command to generate the required test file.

A simple makefile instance

Write a makefile:

myProject: myProject.o myadd.o mysub.o mymul.o
	gcc *.o -o myProject
myProject.o: myProject.c
	gcc -c myProject.c -o myProject.o
myadd.o:myadd.c
	gcc -c myadd.c -o myadd.o
mysub.o:mysub.c
	gcc -c mysub.c -o mysub.o
mymul.o:mymul.c
	gcc -c mymul.c -o mymul.o
clean:
	rm -f *.o
Copy the code

As shown in the figure, once you have written the Makefile, it is very convenient to simply type make to execute the instructions you have already writtenOnce all is compiled, if you want to remove unnecessary files, such as.o files, you can make clean the file you wrote in the makefile.

Makefile variables and wildcards

The variable is the string OBJ = a, B, c — OBJ is a, B, and C, OBJ := a, b, c — OBJ is a, b, c — OBJ is a, b, c — OBJ is a, b, c — OBJ is a, b, c — OBJ is a, b, c — OBJ is a, b, c — OBJ is a, b, c — OBJ is a, b, c — OBJ is a, b, c — — matches an unknown thing

So, the original code can evolve like this:

OBJ:=myadd.o mysub.o mymul.o
OBJ+=myProject.o

myProject:$(OBJ)
	gcc $(OBJ) -o myProject

*.o:*.c
	gcc -c *.c -o *.o

clean:
	rm -f *.o myProject
Copy the code

The visible code is cleaner and easier to change later, but it could be cleaner.

$@ — represents the target file $^ — represents the dependent file

The code evolves again:

target=myProject
OBJ:=myadd.o mysub.o mymul.o
OBJ+=myProject.o

CC:=gcc
CFLAGS:=-c

$(target):$(OBJ)
	$(CC) $^ -o $@

*.o:*.c
	$(CC) $(CFLAGS) $^ -o $@

clean:
	rm -f *.o $(target)
Copy the code

In this way, the subsequent modification can directly change the amount, very convenient