操作系统(5)实验0——makefile的写法
之前GCC那部分我提到过,gcc啥啥啥啥傻傻的那个指令只能够编译简单的代码,如果要干大事(例如突然心血来潮写个c开头的神经网络库之类的),还是要写Makefile来编译。其实在Windows下通常用IDE,例如Visual Studio,那个所谓的“项目”就有点像Makefile。通常Windows系统下这类IDE会自动帮你配置了编译时需要的东西,而Linux环境下我们需要自己来写Makefile来实现IDE的效果,听起来会麻烦点,实际上掌握了技巧之后就那样。
这部分实验指导书里面写的东西不多,但是我觉得有必要详细拿出来讲讲,毕竟很有用。
前提与假设
这里假设使用的make是GNU的make(不同厂商的make对应的makefile写法不一样,make可以理解为根据makefile来编译链接程序的工具)。下面我们通过一个简单的例子来看makefile的具体作用、功能,以及使用方式。
简单的例子
假设当前目录下有以下文件:

当你想要编译的时候,就会使用下面的指令:
gcc -o hellomake hellomake.c hellofunc.c -I.
上面指令就是指定输出结果名字为hellomake,编译两个*.c文件,而-I用来高数gcc在同一个目录下寻找用到的链接文件(.h头文件),其实这个选项还可以指定gcc去别的目录下寻找别的库,详情可以看这里。上面的-I.不是误写,不要忽略掉.,这是指定在当前目录搜索链接库的意思。
但是这只是在写小工程的时候才可以这么干,如果你要编译一个有20+个.c文件,链接需要30+个库的工程呢?很显然直接使用gcc相关指令来编译是不现实的,而且每次编译都要重新输入那么一长串指令,很麻烦。那么,我们这时候想到的第一个办法就是,直接将这个指令保存为一个文件,用的时候直接复制出来运行不就简单很多了?
而makefile恰好就有这个功能,你只需要将这个指令直接输入到makefile中,在直接用到的时候直接使用指令make,工具make就会直接帮你运行makefile中的这个命令。此时makefile应该这样写:
hellomake: hellomake.c hellofunc.c hellomake.h
gcc -o hellomake hellomake.c hellofunc.c -I.
当直接使用指令make并且没有提供任何参数的时候,make就会直接执行第一条规则,因为我们这里只有一条,所以使用指令make就会直接执行我们想要让他执行的编译指令了。注意gcc前面有一个tab,不能少了这个tab。
既然提到了规则,那么就介绍下makefile中的规则写法:

在规则中,如果是第一次执行规则或者执行规则对应的prerequisites中的文件被更新了,那么在执行规则的时候才会运行规则对应的command,否则就不会执行。例如已经执行过一次hellomake规则了,如果没有更改hellomake.c、hellofunc.c、hellomake.h的话,那么再执行一次hellomake规则是不会执行下面的gcc ...指令的;如果更改了hellomake.c,例如修改了printf里面的内容,那么再执行一次make,就会调用规则对应的指令gcc ...,更新编译出来的程序。
基本上到这里就可以明白Makefile的基本规则了,后面的就基本上是锦上添花的功能而已。
引入变量
假设我们突然想要换一个编译器,或者改一改编译时候的参数设置,那么我们最好定义一些变量来实现这样的功能,直接上makefile内容:
CC=gcc
CFLAGS=-I.
hellomake: hellomake.o hellofunc.o
$(CC) -o hellomake hellomake.o hellofunc.o
这个还是很容易懂得,可能你会觉得CFLAGS那里有点奇怪,其实这是默认给C编译器(这里是gcc)指定额外配置用的,是make工具规定的。另外还有CXXFLAGS、CPPFLAGS,前者用来给C++编译器指定额外参数,后者同时给C和C++编译器指定额外参数。不过有些时候CPPFLAGS只给C++编译器指定额外参数,所以具体情况还是要具体分析。注意-o后面的名字,一定是.o的,这和上面有点不一样,但是结果和上面一样。之所以放在规则里面(prerequisites部分)以及command里面,是因为这样可以让make知道在编译出hellomake之前要先编译后面的.o文件对应的.c部分,即能够让编译器理解它们之间的依赖关系。
但是这个makefile其实有一个问题,那就是如果修改了.h文件,那么再一次make的时候是不会编译的,因为make此时没有追踪相关的.h文件的变化。所以我们需要加上相关的规则来实现对.h文件的追踪。
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 ...那部分,主要讲解%.o这部分规则。首先,我们这里定义的是一个适用于所有.o为结尾的规则,我们将对应的.c结尾的文件二号$(DEPS)对应的文件放在prerequisites那部分,这样make就会去追踪这些文件的变化。最后在command部分,-c意思是让编译器编译出.o文件,-o $@意思是将编译出来的文件用规则左侧的名字规则来命名(例如hellomake.o),最后的$<指的是DEPS中的第一个文件,CFLAGS用来指示额外的参数。
但是还是麻烦,所以我们进一步“抽象”,把hellomake那一条规则改改
CC=gcc
CFLAGS=-I.
DEPS = hellomake.h
OBJ = hellomake.o hellofunc.o
%.o: %.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)
hellomake: $(OBJ)
$(CC) -o $@ $^ $(CFLAGS)
$@代表:左边,$^代表右边(看键盘上的位置就知道哪个左哪个右了,这个不用记)。这样我们只需要改OBJ就可以应付更加复杂的项目了。
引入目录
但是上面那样虽然makefile看起来还好,在项目的目录里面就显得比较杂乱,各种头文件和源代码混杂在一起显得比较没条理,所以我们通常都会将头文件集中放在一个文件夹里面,将源代码集中放在一个文件夹里面。
IDIR =../include
CC=gcc
CFLAGS=-I$(IDIR)
ODIR=obj
LDIR =../lib
LIBS=-lm
_DEPS = hellomake.h
DEPS = $(patsubst %,$(IDIR)/%,$(_DEPS))
_OBJ = hellomake.o hellofunc.o
OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ))
$(ODIR)/%.o: %.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)
hellomake: $(OBJ)
$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
.PHONY: clean
clean:
rm -f $(ODIR)/*.o *~ core $(INCDIR)/*~
注意,这个makefile文件时放在项目文件夹下的src文件夹里面的。大概是xx(项目名)/src这样子。LIBS用来引入其他的库(通常安装在系统中,这里可以直接引用),例如这里用了-lm来引用数学的库。patsubst用来替换通配符,即变量中的%,最终会被替换成具体的文件名。

.PHONY用来避免文件使用make clean的时候真的去make一个名为clean的文件,用来确保确实跑的是定义好了的clean规则。这里的clean规则用来删掉编译出来的东西。其他的基本和上面的一样,所以就不多说了。
大概就这些,其实这个要深入进去的话还有很多可以说的,但是今天就记到这里。
参考
跟我一起写 Makefile(一)
A Simple Makefile Tutorial
Makefile cheatsheet速成用的cheatsheet,不过不建议一开始就看这个
CFLAGS, CCFLAGS, CXXFLAGS - what exactly do these variables control?
Makefile之patsubst
操作系统(5)实验0——makefile的写法的更多相关文章
- YII 1.0 常用CURD写法
<?php //yii1.0 curd简单写法 //查询 Yii::app()->db->createCommand($sql)->queryAll();//查询所有行数据 ...
- gtk编译之makefile的写法(之一)
在学习c语言GUI编程时想必大家都会遇见这样一个问题买就是每次编译都要敲`pkg-config --cflags --libs gtk+-2.0`这个烦恼吧 这是我们可以编写一个makefile文件这 ...
- 多个文件目录下Makefile的写法
1.前言 目前从事于linux下程序开发,涉及到多个文件,多个目录,这时候编译文件的任务量比较大,需要写Makefile.关于Makefile的详细内容可以参考网上流传非常广泛的<跟我一起写Ma ...
- 简单makefile的写法
一个项目下的文件比较多,如果单个的输入,比较费劲,所以就需要把编译过程写进一个MakeFile文件中. 下面建立5个文件,3个cxx文件,2个hxx头文件 //filename main.cxx #i ...
- FZU操作系统课程实验 实验一
实验1 [实验名称]:并发程序设计(实验1) [实验目的]:掌握在程序中创建新进程的方法, 观察并理解多道程序并发运行的现象. [实验原理]:fork():建立子进程.子进程得到父进程地址空间的一个复 ...
- 系统功能调用Windows操作系统原理实验
一.实验目的 1.熟悉操作系统的系统功能调用. 2.掌握用C语言实现系统功能调用的方法和步骤. 3.掌握利用10H号功能调用(BIOS的显示I/O功能调用)来实现对屏幕的操作与控制. 二.实验内容 1 ...
- 操作系统之实验二Step1-有序顺序表
实验二Step1-有序顺序表 专业:商业软件工程 班级:商软2班 姓名:甘佳萍 学号:201406114207 实验要求:初始化 输入数组元素个数. 输入n个数,排序输出. 存 ...
- 华为服务器操作系统EulerOS V2.0
平台: linux 类型: 虚拟机镜像 软件包: java-1.8.0 php-5.4.16 python-2.7.5 qt-4.8.5 tomcat-7.0.69 basic software eu ...
- 实验0 安装GLUT包及工程的创建与运行
下面将对Windows下在MicroSoft Visual C++2010(简称MSVC)环境下的OpenGL编程进行简单介绍. 1.安装GLUT工具包 GLUT不是OpenGL所必须的,但它会给我们 ...
随机推荐
- LCA统计
读入挂 inline void read(int &v) { v = ; ; ; ') { if (c == '-') { p = -; } c = getchar(); } ') { v = ...
- Mac OS找不到/usr/include文件夹的解决办法
Mojave最新解决方案:终端执行: xcode-select --install #完成后执行 sudo installer -pkg /Library/Developer/CommandLineT ...
- python接口自动化三(登录绕开验证码及发帖)
前言 有些登录的接口会有验证码:短信验证码,图形验证码等,这种登录的话验证码参数可以从后台获取的(或者查数据库最直接). 获取不到也没关系,可以通过添加cookie的方式绕过验证码. 但是这里需要明确 ...
- mysql NULL函数 语法
mysql NULL函数 语法 作用:如果表中的某个列是可选的,那么我们可以在不向该列添加值的情况下插入新记录或更新已有的记录.这意味着该字段将以 NULL 值保存. 说明:NULL 值的处理方式与其 ...
- web上传文件夹
文件夹数据库处理逻辑 publicclass DbFolder { JSONObject root; public DbFolder() { this.root = new JSONObject(); ...
- Mac 找文件或文件夹,以及开启其他程序,截图快捷键
Mac 图形化界面对操作惯 Win 的人来说比较奇怪. 有一组超级有用的快捷键,control + 空格 按下后会出现一个搜索框,输入计算机上任何你想要找的资源即可打开. 截取全屏:快捷键(Shift ...
- struts2.3.20+spring4.0.2+hibernate4.3.4框架整合
一.创建web工程,搭建Struts框架开发环境: 这里只导入了项目中所需要的重要的jar包,以后根据业务要求继续导入相关的包. 步骤1::导入struts框架所需的jar包 步骤2:在web.xml ...
- jQuery file upload callback options
autoUpload By default, files added to the widget are uploaded as soon as the user clicks on the star ...
- LinkedList Stack
- sed将一个文件插入到另一个文件(合并两个文件)
将before.sh的内容插入到catalina.sh的第一行之后 sed -i '1r /srv/tomcat8/bin/before.sh' /srv/tomcat8/bin/catalina.s ...