[LINUX Network Programming] Makefile and network programming makefile

Source: Internet
Author: User

[LINUX Network Programming] Makefile and network programming makefile

Some Reading Notes in Chapter 2nd of Linux Network Programming (version 2)


Makefile: defines a series of rules in a project that contains a large number of files to specify the sequence of compiled files, which can be used to manage projects.

Makefile specifies which source files in the project need to be compiled and how to compile, which library files need to be created, how to create these library files, and how to finally generate the executable files we want. The advantage of compiling Makefile for the project is that you can use a line of command to complete "Automated compilation". The only thing you need to do to compile the entire project is to enter the make command at the shell prompt, the entire project is automatically compiled.


First, let's take a look at the GCC (GNU Compiler Collection) in Linux: this is a tool set, including gcc (different from the upper case), g ++ and other compilers and ar, nm and other tool sets.

The GCC compiler has four phases of program Compilation:Pre-compilation-> compilation and Optimization-> assembly-> LinkThe following uses the c code as an example:

Source code (*. c) [pre-compile-E] The pre-processed code (*. i) [compile and optimize-S] compile code (*. s) [Assembly-c] target file (*. o) [LINK] Executable File

The pre-compilation process includes the header files referenced in the program into the source code and replaces some macros.

Compilation and optimization are usually translated into assembly languages. There is a one-to-one relationship between Assembly and machine operation codes.

The target file refers to the binary code that can be recognized by the CPU after compilation and compilation by the compiler. However, some function processes do not have relevant instructions and instructions, so they generally cannot be executed.

The target file must be combined in some way before it can run. This is the link.


Some Command Options (assuming the file name is hello. c ):

Gcc hello. c/* generates an executable file named a. out by default. Not only can gcc be followed *. the c file can also be *. o or others. The same is true for the following options, as long as the file to be processed is prior to the file generation phase */

Gcc-o hello. c/* generates an executable file named after-o, that is, hello */

Gcc-E hello. c/* is precompiled. The default name format generates hello. I */

Gcc-S hello. c/* is precompiled and optimized. The default name is hello. s */

Gcc-c hello. c/* (commonly used) is precompiled, compiled, optimized, and compiled to generate the target file named "hello. o */" by default */




Other options:

Gcc-D macro name, gcc-DOS_LINUX is equivalent to adding # define OS _LINUX during pre-compilation, that is, when # ifdef OS _LINUX appears, the conditions will be met

Gcc-Idir (uppercase I): expands the search path of the header file, including the dir directory (which will be used later)

Gcc-Ldir: expands the search path of the Linked Library Used for linking, including the dir directory. gcc preferentially uses the shared library.

Gcc-static. Only static libraries are used for link.

Gcc-On, n is a number to optimize the program execution speed and space occupation (which will increase the compilation speed at the same time), commonly used is 2, gcc-O2


After learning about some basic options, we will give an example of a small project, which will be explained by means of manual compilation and Makefile "Automated compilation:

Directory structure:


project/    main.c    add/        add_int.c        add_float.c    sub/        sub_int.c        sub_float.c


Main. c: You can note that add. h and sub. h is not associated with main. c at the same level directory, the gcc-I directory option will be used to expand the header file search path to find the real *. h. For undefined functions such as add_int, You need to include the. o file with specific definitions at the same time when linking.

/* main.c */#include <stdio.h>#include "add.h"#include "sub.h"int main(void){int a = 10, b = 12;float x = 1.23456, y = 9.87654321;printf("int a+b IS:%d\n",add_int(a,b));printf("int a-b IS:%d\n",sub_int(a,b));printf("float a+b IS:%f\n",add_float(x,y));printf("float a-b IS:%f\n",sub_float(x,y));return 0;}

Add/add. h

/* add.h */#ifndef __ADD_H__#define __ADD_H__extern int add_int(int a, int b);extern float add_float(float a, float b);#endif


/* add_int.c */int add_int(int a, int b){return a+b;}


/* add_float.c */float add_float(float a, float b){return a+b;}

Sub directory.

Based on the code in the book, convert the c file to the target file and link it to it. If the header file cannot be found during main. c compilation, you must add "-Iadd" and "-Isub" to the file. The example in the book is incorrect.

As you can see, compiling a small project requires so many steps:

gcc -o add/add_int.o -c add/add_int.c gcc -o add/add_float.o -c add/add_float.c gcc -o sub/sub_float.o -c sub/sub_float.c gcc -o sub/sub_int.o -c sub/sub_int.c gcc -o main.o -c main.c -Iadd -Isubgcc -o cacu add/add_int.o add/add_float.o sub/sub_int.o sub/sub_float.o main.o ./cacu

Although, you can use the default gcc rules and the following command to generate executable files:

gcc -o cacu1 add/add_int.c add/add_float.c sub/sub_int.c sub/sub_float.c main.c -Iadd -Isub

However, when source files are frequently modified or there are many files in the project and the relationship is complex, it is very difficult to compile them directly with gcc.


Therefore, we should use Makefile to compile multiple files into executable files by using the make command.

By parsing the rules in the Makefile file, make can automatically execute the corresponding script. The following is a simple Makefile file:

# For the first line of item ":" The "make" command on the left is the default file generated after the correct operation. Therefore, cacu2 is generated, which depends on the ":" file on the right. # Every item is a rule # When scanning from left to right, if a file does not exist, jump to the rule for generating it # Pay Attention to the row below, which starts with the tab key, it cannot be a space. If all dependencies are met, execute the following commands: cacu2: add_int.o add_float.o sub_int.o sub_float.o main. ogcc-o cacu2 add/add_int.o add/add_float.o sub/sub_int.o sub/sub_float.o main. oadd_int.o: add/add_int.c add/add. hgcc-o add/add_int.o-c add/add_int.cadd_float.o: add/add_float.c add/add. hgcc-o add/add_float.o-c add/add_float.csub_int.o: sub/sub_int.c sub/sub. hgcc-o sub/sub_int.o-c sub/sub_int.csub_float.o: sub/sub_float.c sub/sub. hgcc-o sub/sub_float.o-c sub/sub_float.cmain.o: main. c add/add. h sub/sub. hgcc-o main. o-c main. c-Iadd-Isub # clear rules. You can use make clean to actively execute clean: rm-f cacu2 add/add_int.o add/add_float.o sub/sub_int.o sub/sub_float.o main. o

When make is installed, run the make command after compiling Makefile. The system will automatically execute some gcc commands to generate the cacu2 executable file. Then, after making clean, the content generated by make is deleted.

Return to the Makefile file. In fact, by default, make will directly execute the first rule, that is, the cacu2 rule. The system checks the dependencies first, and then runs the following command after successful execution:

$(CC)  -o  $(TARGET)  $(OBJS)  $(CFLAGS)

How to Understand? The above command is actually equivalent to the following one, but the variable extension is used.

gcc -o cacu2 add/add_int.o add/add_float.o sub/sub_int.o sub/sub_float.o main.o -Iadd -Isub -O2

CC = gcc, TARGET = cacu2, OBJS = add/add_int.o add/add_float.o sub/sub_int.o sub/sub_float.o main. o, CFLAGS =-Iadd-Isub-O2

Similarly, the make clean command is like this:

-$(RM)  $(TARGET)  $(OBJS)

Equivalent to rm-f cacu2 add/add_int.o add/add_float.o sub/sub_int.o sub/sub_float.o main. o. Note that there is a "-" before the RM variable. It indicates that no error is reported when the operation fails and the command continues to be executed.


Note the following points:

Use variables in Makefile:

Why are variables introduced? It is inconvenient to add a dependency to a rule, which must be filled in both the dependency and the command line.

There are three types of variables:

1. Pre-Defined variables: variables defined in Makefile can be directly used without definition. (Figure from this link) The usage is the same as that of custom variables. See figure 3.


2. Automatic variables: In compiled statements, the target files and dependent files are often displayed. Automatic variables represent these target files and dependent files.


3. User-Defined variables:

Definition method: variable name = value. For example, OBJS = add_int.o add_float.o... # A row represents a variable.

Usage: $ (variable name ). Eg, $ (OBJS)


/* Rewrite a Makefile here */

# Define some variables CC = gccCFLAGS =-Iadd-Isub-O2TARGET = cacu3OBJS = add/add_int.o add/add_float.o sub/sub_int.o sub/sub_float.o main. o # $ (CC)-o $ (TARGET) $ (OBJS) $ (CFLAGS) $ (TARGET): $ (OBJS) $ (CC)-o $ (TARGET) $ (OBJS) $ (CFLAGS) $ (OBJS): % o: % c $ (CC)-o $ @-c $ <$ (CFLAGS) clean: -$ (RM) $ (TARGET) $ (OBJS)

The variable definition method is used to simplify text maintenance.

$ (CC)-o $ @-c $ <$ (CFLAGS) adopts the so-called "static mode" rule, which is equivalent to multiple rules, as shown above.


Search Path:

There are many directories in a large system. It is inconvenient to manually add a directory using the-I method. Therefore, the VPATH variable is used, which can automatically locate the directory of the specified file and add it to the file.

Usage: VPATH = path1: path2 :...:.

Separated by colons (:). Remember to add the current directory at the end.

In this case, a problem occurs: the target file will be placed in the current directory to pollute the environment! Therefore, define a variable, create a directory named after the variable, and put the *. o file.

/* Rewrite a Makefile here */

# A serious error is found, because there is no Terminator at the end of the Makefile file line, so it is best not to blindly comment in the back, but to wrap the line .. # Define some variables and introduce VPATHCC = gccCFLAGS =-Iadd-Isub-O2 # Place all target files in this folder OBJSDIR = objsVPATH = add: sub :. TARGET = cacu4 # OBJS = add/add_int.o add/add_float.o sub/sub_int.o sub/sub_float.o main. oOBJS = add_int.o add_float.o sub_int.o sub_float.o main. o # After VPATH is defined, the folder name in OBJS does not need to be written. # Check whether the directory exists. $ (TARGET): $ (OBJSDIR) $ (OBJS) $ (CC) -o $ (TARGET) $ (OBJSDIR )/*. o $ (CFLAGS) $ (OBJS): % o: % c $ (CC)-o $ (OBJSDIR)/$ @-c $ <$ (CFLAGS) # mkdir-p create all lost parent directories $ (OBJSDIR): mkdir-p. /$ @ clean:-$ (RM) $ (TARGET) $ (OBJSDIR )/*. o


Automatic derivation rules: When the command make is used to compile c language files with the extension c, the source file compilation rules do not need to be explicitly given. According to the default rules, find the corresponding. c file through the. o file in the dependency and compile it into the target file to satisfy the dependency.

# When you use the make command to compile c language files with the extension c, the source file compilation rules do not need to be explicitly given. (A default compilation rule will be used) -- make's implicit rule CC = gccCFLAGS =-Iadd-Isub-O2VPATH = add: sub :. TARGET = cacu5OBJS = add_int.o add_float.o sub_int.o sub_float.o main. o $ (TARGET): $ (OBJS) $ (CC)-o $ (TARGET) $ (OBJS) $ (CFLAGS) clean:-$ (RM) $ (TARGET) $ (OBJS)


Recursive make:

When many people develop programs in multiple directories, and each person is responsible for a module, and files are in a relatively independent directory, the compilation of Code maintained by the same Makefile will be very poor.


1. recursive call method: The make command can call Makefile of each subdirectory recursively.

If Makefile exists in the directory add and sub, you can use the following two methods (the second method is better ):

add:    cd add && $(MAKE)
add:    $(MAKE) -C add

Both indicate entering the add directory, and then executing the make command


2. Master Makefile: Call $ (MAKE)-C. If some variables in the Makefile of the general control need to be passed to the lower Makefile, you can use the export command.

The Makefile code implementation of the general control is as follows:

export CC = gccCFLAGS = -Iadd -Isub -O2TARGET = cacu6export OBJSDIR = ${shell pwd}/objs$(TARGET):$(OBJSDIR) main.o$(MAKE) -C add$(MAKE) -C sub$(CC) -o $(TARGET) $(OBJSDIR)/*.omain.o:main.c$(CC) -o $(OBJSDIR)/$@ -c $^ $(CFLAGS)$(OBJSDIR):mkdir -p $(OBJSDIR)clean:-$(RM) $(TARGET) $(OBJSDIR)/*.o

The above code is different from the book. Note that CC must be export to the sub-Makefile. Otherwise, cc instead of gcc will be used by default.

$ {Shell pwd} indicates to execute the pwd command in shell, that is, to obtain the current path

All the generated target files (*. o) are stored in the objs directory of the project directory.

The following is the subdirectory Makefile:


OBJS = add_int.o add_float.oall:$(OBJS)$(OBJS):%o:%c$(CC) -o $(OBJSDIR)/$@ -c $< -O2clean:-$(RM) $(OBJS)


OBJS = sub_int.o sub_float.oall:$(OBJS)$(OBJS):%o:%c$(CC) -o $(OBJSDIR)/$@ -c $< -O2clean:-$(RM) $(OBJS)

The result is as follows:


3. Functions in Makefile

  • Retrieve the matching PATTERN file name wildcard: searches for all PATTERN-compliant file names in the current directory. The returned values are a list of (PATTERN-compliant) file names separated by spaces. Prototype: $ (wildcard PATTERN), for example: $ (wildcard *. c)
  • Patsubst: this function is equivalent to the replace function. It searches for words separated by spaces in the string text and replaces the matching strings with other strings. Prototype: $ (patsubst pattern, replacement, text), for example: $ (patsubst %. c, %. o, $ (wildcard *. c ))
  • Loop function foreach: extract the VAR words separated by spaces in the LIST, execute the TEXT expression, and output the words after processing. Prototype: $ (foreach VAR, LIST, TEXT), for example: $ (foreach dir, $ (DIRS), $ (wildcard $ (dir)/*. c ))

The following is a function-based Makefile file rewritten based on the Makefile file in the book:

CC = gccCFLAGS = -Iadd -Isub -O2TARGET = cacu7DIR = add sub .FILES = $(foreach dir, $(DIR), $(wildcard $(dir)/*.c) )OBJS = $(patsubst %c, %o, $(FILES))$(TARGET):$(OBJS) main.o$(CC) -o $@ $^ -O2$(OBJS):%o:%c$(CC) -o $@ -c $< $(CFLAGS)clean:-$(RM) $(TARGET) $(OBJS)


----- Split line -----


For the above Makefile example and the entire project code, see TyrusChin-Github.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.