zoukankan      html  css  js  c++  java
  • Makefile的初学教程

    进入大二以后天天写代码,不如就重新使用自己的blog吧。感觉看自己之前写的东西重新学习还挺不错的。

    Makefile可以帮助你编译。原来搞OI的时候都是一个cpp文件直接编译运行就行了。不过现在假设你要写一个很简单的小计算器,每一个cpp文件执行一种算术方法,你如果一个一个编译的话,大概是要写成这样的:

    g++ -c -Wall main.cpp -o main.o
    g++ -c -Wall add.cpp -o add.o
    g++ -c -Wall sub.cpp -o sub.o
    g++ -c -Wall mul.cpp -o mul.o
    g++ -c -Wall div.cpp -o div.o
    
    g++ -o main main.o add.o sub.o mul.o div.o
    

    这样写起来很麻烦,所以Makefile应运而生。

    1.基本规则

    Makefile的核心规则(核心语法?)大概是这样的:
    targets : prerequisites
    commands

    其中targets是目标文件(目前大多数你写的程序都只有一个执行文件,可以先理解为执行文件),prerequisites是用于生成target的文件或目标。
    表示输入一个Tab缩进(这个非常重要!!),commands 是你要执行的命令(shell命令)。

    写一个比较简单的,只有3个cpp的makefile示例:

    main : main.cpp printhello.cpp factorial.cpp
      g++ -o main main.cpp printhello.cpp factorial.cpp
    

    在终端输入make就可以运行makefile了。之后./main来执行程序。

    2.使用变量

    Makefile中使用变量其实很像C++中的宏(#define),大致写法是:

    CC = g++
    TARGET = main
    OBJ = main.o printhello.o factorial.o
    
    $(TARGET) : $(OBJ)
      $(CC) -o $(TARGET) $(OBJ)
    

    其中,CC,TARGET,OBJ等赋值符号左边的叫做变量(variables),其实只看代码的话,和上一个是完全一样的,我们把宏替换一下就是一样的代码了。
    这个主要是用于减少代码量,写起来更加简洁。

    3.文件依赖(我也不知道是不是该叫这个名字)

    有的时候只有一个或几个cpp被改动,我们是不需要重新编译所有文件的。设想如果有上百个cpp,只改动其中一个就要全部重新编译显然非常浪费时间,因此我们可以将我们的代码改写一下:

    CC = g++
    TARGET = main
    OBJ = main.o printhello.o factorial.o
    
    main.o: main.cpp
      $(CC) -c main.cpp
    
    
    printhello.o: printhello.cpp
      $(CC) -c printhello.cpp
    
    
    factorial.o: factorial.cpp
      $(CC) -c factorial.cpp
    
    

    假如我们只改变了main.cpp,那么重新执行make的时候他就只会重新编译main.cpp,而不会把所有cpp都重新编译一次了。
    不过现在的问题是代码量增加了很多,如果有上百个cpp我们显然是吃不消的,这时候就需要使用文件依赖。大致意思是,每一个.o文件都是.cpp文件编译而成的。所以我们将代码采取如下写法:

    CC = g++
    TARGET = main
    OBJ = main.o printhello.o factorial.o
    CFLAGS = -c -Wall
    
    $(TARGET) : $(OBJ)
      $(CC) -o $@ $(OBJ)
    
    %.o : %.cpp
      $(CC) $(CFLAGS) $< -o $@
    
    #本段中$@表示所有目标文件,$^表示所有prerequisites文件,$<表示第一个prerequsites文件。
    #因为每一个.o依赖于一个.cpp,所以这里写$<或者$^都是可以的。
    #注意这里前后两个$@表示的文件是不同的,第一个表示的是TARGET(main),即可执行文件,而第二个表示的这个.o文件对应的.cpp文件。
    
    

    注意其中一行%.o : %.cpp,这一段是一个规则,大致意思就是我们的文件依赖,所有的.o都是依赖于.cpp文件的。

    4.使用函数

    Makefile中的函数非常多,这里只简单介绍两种,wildcard和patsubst。
    我们的代码已经很简洁了,但是看OBJ一行,如果我们有上百个.cpp,也就会有上百个.o,那手动输入进去还是很麻烦的。
    解决方法就是使用wildcard(通配符)函数,这个函数可以自动搜索路径中的一类文件(我们这里先假定为cpp文件),用法如下:

    SRC = $(wildcard ./*.cpp)
    

    现在SRC就找到了路径中所有的.cpp文件,之后我们使用字符串函数patsubst,它可以批量处理字符串,我们的目标是把所有.cpp改成.o,用法如下:

    SRC = $(wildcard ./*.cpp)
    OBJ = $(patsubst %.cpp, %.o, $(SRC))
    target:
      @echo $(SRC)
      @echo $(OBJ)
    

    target后面是输出语句,可以让你验证一下找的是不是正确的文件。

    那么现在我们就可以非常简洁的写好makefile了。直接上最终版的代码吧。src是存储所有cpp文件的文件夹,inc是存储所有hpp(h)文件的文件夹,makefile文件应该和这两个文件夹处在一个子目录下。

    #第一行这里写 = src也行
    SRC_DIR = ./src
    SOURCE = $(wildcard $(SRC_DIR)/*.cpp)
    OBJ = $(patsubst %.cpp, %.o, $(SOURCE))
    TARGET = main
    INCLUDE = -I./inc 
    #这里是在inc里找所有引用的hpp文件
    
    CC = g++
    CFLAGS = -c -Wall
    
    $(TARGET) : $(OBJ)
      $(CC) -o $@  $(OBJ)
    %.o : %.cpp
      $(CC) $(CFLAGS) $< -o $@ $(INCLUDE)
    
    .PHONY : clean
    clean:
      rm -f $(SRC_DIR)/*.o $(TARGET)
    

    最后.PHONY : clean这里是clean函数,用于清理多余的.o文件。使用方法是在终端输入make clean即可。

    本文章只包含一些非常初级的makefile用法,如果想学更多,可以看这里或者这里

    当你意识到,每个上一秒都成为永恒。
  • 相关阅读:
    LeetCode 242. Valid Anagram (验证变位词)
    LeetCode 205. Isomorphic Strings (同构字符串)
    LeetCode 204. Count Primes (质数的个数)
    LeetCode 202. Happy Number (快乐数字)
    LeetCode 170. Two Sum III
    LeetCode 136. Single Number (落单的数)
    LeetCode 697. Degree of an Array (数组的度)
    LeetCode 695. Max Area of Island (岛的最大区域)
    Spark中的键值对操作
    各种排序算法总结
  • 原文地址:https://www.cnblogs.com/captain1/p/15328320.html
Copyright © 2011-2022 走看看