zoukankan      html  css  js  c++  java
  • 操作系统(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工具规定的。另外还有CXXFLAGSCPPFLAGS,前者用来给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

  • 相关阅读:
    【CSDN博客之星评选】我为什么坚持写博客
    关于纯css布局的概况
    IIS服务器下301跳转是怎么样实现的?
    如何使用数据库保存session的方法简介
    PHP如何通过SQL语句将数据写入MySQL数据库呢?
    PHP中文函数顺序排列一数组且其序数不变
    angular实时显示checkbox被选中的元素
    oracle查询正在执行的语句以及正被锁的对象
    angular中ng-repeat去重
    接口自动化测试框架--http请求的get、post方法的实现
  • 原文地址:https://www.cnblogs.com/yejianying/p/xuetangx_os_5.html
Copyright © 2011-2022 走看看