Linux C++:make&makefile 基础问题(一)

最近编译项目动不动就make或者makefile一下,这里简单总结一下;

基础概念:

make,makefile简单来说就是系统脚本来实现批量和自动化编译的一种形式;

对于gcc下常见的编译,采用单文件编译;

例如:
有三个文件:

hellomake.c源文件:

// hellomake.c
#include <hellomake.h>

int main() {
  // call a function in another file
  myPrintHelloMake();

  return(0);
}

hellofunc.c源文件:

// hellofunc.c
#include <stdio.h>
#include <hellomake.h>

void myPrintHelloMake(void) {

  printf("Hello makefiles!\n");

  return;
}

hellomake.h源文件:

/*
example include file
*/

void myPrintHelloMake(void);

因此,可以通过命令行+gcc进行处理;

gcc -o hellomake hellomake.c hellofunc.c -I.

这里注意下基本的语法形式;

  1. gcc代表使用c编译;
  2. -o代表输出到哪里;
  3. hellomake这里代表生成的最终执行文件;
  4. hellomake.c,hellofunc.c为编译的源文件;
  5. -I代表在当前目录下寻找头文件;

这里还需要注意下,gcc的编译顺序;

对于之前学过的系统结构和编译原理来讲,编译的过程应该分为四个阶段;
在这里插入图片描述

  1. 预处理:将头文件以及宏定义拓展到代码中,这里由hello.c->hello.i文件;
  2. 编译:将.i的与处理文件编译为汇编程序,这里由hello.i->hello.s;
  3. 汇编:将.s汇编文件转化为目标文件,目标文件为二进制格式,这里由hello-.s->hello.o;
  4. 链接:将多个目标文件链接为一个最终的可执行文件,这里由hello.o->hello;

所以按照流程上来讲,gcc应该顺序执行四步,才能得到最终的执行文件;

但是gcc可以偷懒,直接利用-o加上源文件,一步得到最终的可执行文件;

但是如果是多文件,或者进行修改,亦或是编译命令丢失,都有可能要重新编译;

因此为了简便make/makefile应运而生;

说白了就是通过一个脚简化编译流程;

具体形式:

makefile的具体形式如下所示:

目标文件:依赖文件
     命令1
     命令2
     ...
     命令n

目标文件是指,我们期望获得的文件;
依赖文件则是指,我们期望获得文件需要从什么地方来;
而命令则是指我们如何将依赖文件转化为目标文件;

例如,对于上述gcc指令,我们就可以写为:

hellomake: hellomake.c hellofunc.c
     gcc -o hellomake hellomake.c hellofunc.c -I.

大意为,我们需要hellomake执行文件,来源为hellomake.c以及hellofunc.c;

生成的指令为:

gcc -o hellomake hellomake.c hellofunc.c -I.

但是上述并不会之编译更改后的文件;

对于上述可以有以下拓展:

CC=gcc
CFLAGS=-I.

hellomake: hellomake.o hellofunc.o
     $(CC) -o hellomake hellomake.o hellofunc.o

这里前两行可以视为cpp内的宏定义;

注意一下目标和依赖形式;

这里不再采用.c的源文件格式,而是直接依赖于.o目标文件;

这里意为,如果要执行文件hellomake,则必须要先编译生成.o目标文件;

因此,执行语句为连接语句,将.o模块链接即可;

但是这里还是要加上头文件,不然会编译报错,相当于指定编译目标文件的时候,哪些源文件依赖哪些头文件;

CC=gcc
CFLAGS=-I.
DEPS = hellomake.h

%.o: %.c $(DEPS)
	$(CC) -c -o $@ $< $(CFLAGS)

hellomake: hellomake.o hellofunc.o 
	$(CC) -o hellomake hellomake.o hellofunc.o 

上述DEPS宏定义标记了源文件;

其中%.o以及%.c类似于通配符,代表某个或者某些以后缀名为结束的文件;

也声明了.o文件应该由哪个.c文件而来,头文件是谁;

参数 -c 指示编译器产生 .o 目标文件;

参数 $@ 和 $^ 分别指代规则第一行中的冒号的左右两边的内容;

所以可以拼成一条完整的编译目标模块的语句;

因此,利用宏定义,也可以改写成如下,更方便:

CC=gcc
CFLAGS=-I.
DEPS = hellomake.h
OBJ = hellomake.o hellofunc.o 

%.o: %.c $(DEPS)
	$(CC) -c -o $@ $< $(CFLAGS)

hellomake: $(OBJ)
	$(CC) -o $@ $^ $(CFLAGS)

版权声明:本文为m0_66776925原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。