http://www-personal.umich.edu/~ppannuto/writings/makefiles.html

Makefiles

Makefiles (or, the GNU automake system), is a programming language all its own, and you can do some pretty spectacular things with them (it). This is a basic introduction to what you'll need to write useful makefiles.

Targets

The goal of any Makefile is to build all of its targets. Ultimately, a Makefile is a series of rules to accomplish this task. Makefiles break down then into the following:

  1. VARIABLE DECLARATIONS
  2.  
  3. TARGET: DEPENDENCIES
  4. RULES TO BUILD TARGET

Let's break this down...

Variables

Makefiles have variables; these are very useful for hopefully obvious reasons. Some common variables include:

  1. # Hashes are comments in Makefiles
  2. # Some variables are automatically defined, for example CC is the
  3. # default C compiler and CXX the default c++ complier, but we can
  4. # override them if we like:
  5. CC = clang
  6.  
  7. # CFLAGS are the flags to use when *compiling*
  8. CFLAGS=-m32 -c
  9. # LFLAGS are the flags to use when *linking*
  10. LFLAGS=-ldl
  11.  
  12. # Variables can inherit from other variables
  13. # (note the $ for accessing the value of a variable):
  14. CFLAGS_DBG=$(CFLAGS) -g -Wall -Werror
  15. LFLAGS_DBG=$(LFLAGS) -g
  16. # EXE, OUT, TARGET, or GOAL are all some common examples of your end goal
  17. EXE=disk_sched

To access a variable in a makefile, simply use $(VAR_NAME) note the parentheses. Variables in Makefiles are just like #defines in C/C++, simple string substitution.

Targets, dependencies, and rules

  1. tab space space
  2. | | |
  3. target: dependency1 dependency2 dependency3
  4. rule1
  5. rule2
  6. rule3
  7. ^^^^^^^^
  8. |
  9. tab

These three pieces work together:

  • target is the "thing" (or things) at the beginning of a line before the colon. A target may be an arbitrary string ( "foo" ) or a file ( thread.o ).
  • dependencies are the "things" found on the same line, after one tab character from a target. Dependencies may be any legal target. Targets listed in dependencies do not have to be listed in the Makefile as a target (e.g., libinterrupt.a is a legal target)
  • rules are a list of commands to execute to satisfy the target they are listed under. A rule may be *any* regular command (e.g., echo "Running rule foo", or $CC $CFLAGS bar.c)

From MAKE(1):

  1. make [ -f makefile ] [ options ] ... [ targets ] ...

So, when you run

  1. $ make

make will choose the default makefile "Makefile", with no options, and it will try to build the default target, which is the first target listed in the file, in this case "all".

If the target "all" looks like this:

  1. EXE=disk_sched
  2.  
  3. all: $(EXE)

Then make will try to satisfy all the dependencies of the rule "all", in this case by building the target $(EXE)

A simple example

Consider the very simple Makefile, whose goal is to build "a":

  1. # You should 'man touch' if you aren't familiar with the command
  2. EXE=a
  3. CC=touch
  4.  
  5. all: $(EXE)
  6.  
  7. $(EXE): b
  8. $(CC) a

Let's walk through what happens when we run

  1. $ make
  1. make opens the file "Makefile" and tries to satisfy the target "all"
  2. "all" depends on $(EXE), or "a". Look up the target "a"
  3. $(EXE), or "a" depends on "b". Look up the target "b"
  4. Target "b" is not listed in the Makefile as a target. Check for the file "b" in the system
  5. Uh-oh, can't find "b" anywhere...
  1. $ make
  2. make: *** No rule to make target `b', needed by `a'. Stop.

So, make failed because it couldn't satisfy all the dependencies of "a". Let's help it out a little bit:

  1. $ touch b
  2. $ make
  1. make opens the file "Makefile" and tries to satisfy the target "all"
  2. "all" depends on $(EXE), or "a". Look up the target "a"
  3. $(EXE), or "a" depends on "b". Look up the target "b"
  4. Target "b" is not listed in the Makefile as a target. Check for the file "b" -- success!
  5. All of "a"'s dependencies are satisfied, so start running the rules required to build "a"
  6. Execute $(CC) a, or "touch a"
  7. All of "a"'s rules are done, so "a" is complete
  8. All of "all"'s dependencies are satisfied, so start running the rules required to build "all"
  9. There are no rules for "all", so make has succeeded!
  1. $ make
  2. touch a

Now, what happens if we run make again?

  1. $ make
  2. make: Nothing to be done for `all'.

What happened? It turns out Step 4, "Check for the file b" is a bit more complicated. We said that "a" depends on "b". So, when make starts to build "a", it notices that a copy of "a" already exists (from our last 'build'); it also notices that "b" hasn't changed since the last time we built "a". So, if "a" only depends on "b", and "b" hasn't changed since the last time we built "a", then "a" is 'up to date'. This is the most useful feature of Makefiles, but it's important to understand how it works; that is, "a" will only be rebuilt if "b" has changed.

How does make know if "b" has changed since we last built "a"? It uses file timestamps. Every time you write a file, it's last modified time is updated. Since the last time we built "a" after "b" already existed, "a"'s timestamp was more recent than "b"'s. If "b"'s timestamp were more recent, then "a" would be rebuilt.

Let's see everything we've learned in action:

Working with the same Makefile:

  1. EXE=a
  2. CC=touch
  3.  
  4. all: $(EXE)
  5.  
  6. $(EXE): b
  7. $(CC) a

And staring clean...

  1. $ rm a b
  2. $ make
  3. make: *** No rule to make target `b', needed by `a'. Stop.
  4. $ touch b
  5. $ make
  6. touch a
  7. $ make
  8. make: Nothing to be done for `all'.
  9. $ touch b
  10. $ make
  11. touch a

Notice in the last example, since "b" had been updated, "a" had to be rebuilt.

Make sure you understand everything in this example before moving on.

A More Complicated Example

Our goal now is the build my_project, which is made up of main.c, other.c, and library.o - where library.o is some prebuilt library. Don't worry if this looks complicated, we'll break it down in a moment.

  1. #CC=gcc, remember, we don't need this one, make defines it for us
  2. CFLAGS=-m32 -c
  3. # -m32, library.o is 32-bit, so we want to force a 32-bit build
  4. # -c, for the compile step, just build objects
  5. LFLAGS=
  6. OBJS=main.o other.o
  7. LIBS=thread.o
  8. EXE=my_project
  9.  
  10. all: $(EXE)
  11.  
  12. $(EXE): $(OBJS) $(LIBS)
  13. $(CC) $(LFLAGS) $(OBJS) $(LIBS) -o $(EXE)
  14.  
  15. main.o: main.c
  16. $(CC) $(CFLAGS) main.c
  17.  
  18. other.o: other.c
  19. $(CC) $(CFLAGS) other.c
  20.  
  21. clean:
  22. rm -f $(OBJS) $(EXE)

One of the biggest reasons makefiles look complicated is all of the variables can seem to hide what's actually happening. Let's look at this same Makefile with all of the variables substituted:

  1. all: my_project
  2.  
  3. my_project: main.o other.o thread.o
  4. gcc main.o other.o thread.o -o my_project
  5.  
  6. main.o: main.c
  7. gcc -m32 -c main.c
  8.  
  9. other.o: other.c
  10. gcc -m32 -c other.c
  11.  
  12. clean:
  13. rm -f main.o other.o my_project

A little better... now let's walk through what's happening:

  1. make 'all', which depends on my_project
  2. To make 'my_project', there must exist an output 'my_project' which is newer than 'main.o', 'other.o', and 'thread.o'
  3. Check for 'main.o', it doesn't exist
  4. Check for 'main.c', it does exist
  5. Run "gcc -m32 -c main.c"
  6. Done with main.o
  7. Check for 'other.o', it does exist
  8. Compare other.o with other.c
  9. other.c is newer than other.o
  10. So run "gcc -m32 -c other.c"
  11. Done with other.o
  12. Check for 'thread.o', it does exist
  13. There are no other rules for thread.o, so Done with thread.o
  14. All of my_project's dependencies are now satisified
  15. Compare my_project (if it exists) to main.o, other.o, and thread.o
  16. main.o and other.o are both newer than my_project
  17. So run "gcc main.o other.o thread.o -o my_project
  18. Done with my_project
  19. All of "all"'s dependencies are satisfied
  20. So done
What's "clean"?

The target clean is a conventional thing to include in Makefiles that will remove all of the files built by make. It is a convenient thing to include and is also an interesting example. Let us observe what happens when we run

  1. $ make clean
  1. make target 'clean' (NOTE: target 'all' is NOT built, we specified a different target)
  2. Check for existence of target clean, fails
  3. clean has no dependencies
  4. run "rm -f main.o other.o my_project"

So what happens if you...

  1. $ touch clean
  2. $ make
  3. <output snipped, try it!>

To fix this problem, read here about PHONY targets.

A really complicated example, and why Makefiles are cool!

Using the same scenario as the previous example, let's add some testing:

  1. CFLAGS=-m32 -c
  2. # -m32, library.o is 32-bit, so we want to force a 32-bit build
  3. # -c, for the compile step, just build objects
  4. LFLAGS=
  5. OBJS=main.o other.o
  6. LIBS=thread.o
  7. EXE=my_project
  8.  
  9. all: $(EXE)
  10.  
  11. $(EXE): $(OBJS) $(LIBS)
  12. $(CC) $(LFLAGS) $(OBJS) $(LIBS) -o $(EXE)
  13.  
  14. main.o: main.c
  15. $(CC) $(CFLAGS) main.c
  16.  
  17. other.o: other.c
  18. $(CC) $(CFLAGS) other.c
  19.  
  20. clean:
  21. rm -f $(OBJS) $(EXE)
  22. rm -f tests/*.out
  23.  
  24. # The '@' symbol at the start of a line suppresses output
  25. test1: $(EXE) tests/1.good
  26. @rm -f tests/1.out
  27. @./$(EXE) > tests/1.out
  28. @diff tests/1.good tests/1.out && echo "Test 1 PASSED" || echo "Test 1 FAILED"
  29.  
  30. test2: $(EXE) tests/2.good
  31. @rm -f tests/2.out
  32. @./$(EXE) > tests/2.out
  33. @diff tests/2.good tests/2.out && echo "Test 2 PASSED" || echo "Test 2 FAILED"
  34.  
  35. tests: test1 test2

What happens when you run 'make tests'?

Makefiles 介绍的更多相关文章

  1. 羽夏 MakeFile 简明教程

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.该文章根据 GNU Make Manual 进行汉化处理并作出自己的整理,一是我对 Make 的学习记录,二是对大家学习 MakeF ...

  2. PostgreSQL9.2.4内核源码结构介绍

    PostgreSQL的源代码可以随意获得,其开源协议也允许研究者任意修改,这里介绍一下PostgreSQL的源码结构以及部分实现机制.下载PostgreSQL源代码并减压后,其一级目录结构如下图: P ...

  3. 很详细、很移动的Linux makefile教程:介绍,总述,书写规则,书写命令,使用变量,使用条件推断,使用函数,Make 的运行,隐含规则 使用make更新函数库文件 后序

    很详细.很移动的Linux makefile 教程 内容如下: Makefile 介绍 Makefile 总述 书写规则 书写命令 使用变量 使用条件推断 使用函数 make 的运行 隐含规则 使用m ...

  4. (转)Makefile介绍

    2. Makefile介绍 make命令执行时,需要一个Makefile文件,以告诉make命令需要怎么样的去编译和链接程序. 首先,我们用一个示例来说明Makefile的书写规则.以便给大家一个感性 ...

  5. qt configure参数配置介绍

    ======================================全文是按照./configure -help来翻译的==================================== ...

  6. 深度学习开源工具——caffe介绍

    本页是转载caffe的一个介绍,之前的页面图都down了,更新一下. 目录 简介 要点记录 提问 总结 简介 报告时间是北京时间 12月14日 凌晨一点到两点,主讲人是 Caffe 团队的核心之一 E ...

  7. Makefile 介绍

    makefile:是告诉编译器(交叉工具链)如何去编译.链接一个工程的规则.   一.概述 什 么是makefile?或许很多Winodws的程序员都不知道这个东西,因为那些Windows的IDE都为 ...

  8. flatbuffer介绍和用法

    介绍 flatbuffer是google发布的一个跨平台序列化框架具有如下特点 1.对序列化的数据不需要打包和拆包 2.内存和效率速度高,扩展灵活 3.代码依赖较少 4.强类型设计,编译期即可完成类型 ...

  9. 【Makefile】2-Makefile的介绍及原理

    目录 前言 概念 Chapter 2:介绍 2.1 makefile的规则 2.3 make 是如何工作的 ** 2.5 让 make 自动推导 2.8 Makefile 里面有什么 2.9 Make ...

随机推荐

  1. EffectiveC#7--选择恒定的原子值类型数据

    1.恒定类型就是一但它们被创建,它们(的值)就是固定的. 恒定类型可以很好的在基于哈希代码的集合上工作.以Object.GetHashCode()方法返回的值,对同一个实例是必须相同的 2.一个客户类 ...

  2. SQLServer 跨服务器查询的两个办法

    网上搜了跨服务器查询的办法,大概就是Linked Server(预存连接方式并保证连接能力)和OpenDataSource(写在语句中,可移植性强).根据使用函数的不同,性能差别显而易见...虽然很简 ...

  3. Android 控件 -------- AutoCompleteTextView 动态匹配内容,例如 百度搜索提示下拉列表功能

    AutoCompleteTextView 支持基本的自动完成功能,适用在各种搜索功能中,并且可以根据自己的需求设置他的默认显示数据.两个控件都可以很灵活的预置匹配的那些数据,并且可以设置输入多少值时开 ...

  4. fullcalendar .net版本

    实现了基本的增删改和拖拽,先记与此,抽时间继续优化和完善. 参考链接:http://www.helloweba.com/tag-fullcalendar.html 参考demo:   http://f ...

  5. Comparator和Comparable在排序中的应用

    http://blog.csdn.net/iisgirl/article/details/7269833

  6. git commit的--amend选项

    git commit --amend常常用来修改某个branch上最顶端的commit,大多数情况下,这个命令给人的感觉是用新的commit替换了原来的commit.git commit --amen ...

  7. css布局学习笔记之max-width

    设置块级元素的 width 可以阻止它从左到右撑满容器.然后你就可以设置左右外边距为 auto 来使其水平居中.元素会占据你所指定的宽度,然后剩余的宽度会一分为二成为左右外边距. div{ width ...

  8. php error_log 详解

    定义和用法 error_log() 函数向服务器错误记录.文件或远程目标发送一个错误. 成功,返回 true,否则返回 false. error_log(error,type,destination, ...

  9. ZendFramework 环境部署

    查看源码 int_autoloader.php 文件中,发现应用了一个 AutoloaderFactory 的命名空间,路径写得是相对路径,所以需要在 php.ini 中定义一个 inclde_pat ...

  10. Starship Troopers(HDU 1011 树形DP)

    题意: 给定n个定点和m个士兵,n个定点最终构成一棵树,每个定点有一定x个bugs和y个value,每20个bug需要消耗一个士兵,不足20也消耗一个,然后最终收获y个value,只有父节点被占领后子 ...