zoukankan      html  css  js  c++  java
  • Makefiles 介绍

    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:

    	VARIABLE DECLARATIONS
    
    	TARGET:	DEPENDENCIES
    		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:

    	# Hashes are comments in Makefiles
    	# Some variables are automatically defined, for example CC is the
    	# default C compiler and CXX the default c++ complier, but we can
    	# override them if we like:
    	CC = clang
    
    	# CFLAGS are the flags to use when *compiling*
    	CFLAGS=-m32 -c
    	# LFLAGS are the flags to use when *linking*
    	LFLAGS=-ldl
    
    	# Variables can inherit from other variables
    	# (note the $ for accessing the value of a variable):
    	CFLAGS_DBG=$(CFLAGS) -g -Wall -Werror
    	LFLAGS_DBG=$(LFLAGS) -g
    	# EXE, OUT, TARGET, or GOAL are all some common examples of your end goal
    	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

           tab         space       space
           |           |           |
    target:	dependency1 dependency2 dependency3
    	rule1
    	rule2
    	rule3
    ^^^^^^^^
    |
    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):

    	make [ -f makefile ] [ options ] ... [ targets ] ...
    	
    So, when you run
    	$ 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:

    	EXE=disk_sched
    
    	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":

    	# You should 'man touch' if you aren't familiar with the command
    	EXE=a
    	CC=touch
    
    	all:	$(EXE)
    
    	$(EXE):	b
    		$(CC) a
    	
    Let's walk through what happens when we run
    $ 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...
    	$ make
    	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:
    $ touch b
    $ 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!
    	$ make
    	touch a
    	

    Now, what happens if we run make again?

    $ make
    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:

    	EXE=a
    	CC=touch
    
    	all:	$(EXE)
    
    	$(EXE):	b
    		$(CC) a
    	
    And staring clean...
    $ rm a b
    $ make
    make: *** No rule to make target `b', needed by `a'.  Stop.
    $ touch b
    $ make
    touch a
    $ make
    make: Nothing to be done for `all'.
    $ touch b
    $ make
    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.

    #CC=gcc, remember, we don't need this one, make defines it for us
    CFLAGS=-m32 -c
    # -m32, library.o is 32-bit, so we want to force a 32-bit build
    # -c, for the compile step, just build objects
    LFLAGS=
    OBJS=main.o other.o
    LIBS=thread.o
    EXE=my_project
    
    all:	$(EXE)
    
    $(EXE):	$(OBJS) $(LIBS)
    	$(CC) $(LFLAGS) $(OBJS) $(LIBS) -o $(EXE)
    
    main.o:	main.c
    	$(CC) $(CFLAGS) main.c
    
    other.o:	other.c
    	$(CC) $(CFLAGS) other.c
    
    clean:
    	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:

    all:	my_project
    
    my_project:	main.o other.o thread.o
    	gcc main.o other.o thread.o -o my_project
    
    main.o:	main.c
    	gcc -m32 -c main.c
    
    other.o:	other.c
    	gcc -m32 -c other.c
    
    clean:
    	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

    $ 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...
    $ touch clean
    $ make
    <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:

    CFLAGS=-m32 -c
    # -m32, library.o is 32-bit, so we want to force a 32-bit build
    # -c, for the compile step, just build objects
    LFLAGS=
    OBJS=main.o other.o
    LIBS=thread.o
    EXE=my_project
    
    all:	$(EXE)
    
    $(EXE):	$(OBJS) $(LIBS)
    	$(CC) $(LFLAGS) $(OBJS) $(LIBS) -o $(EXE)
    
    main.o:	main.c
    	$(CC) $(CFLAGS) main.c
    
    other.o:	other.c
    	$(CC) $(CFLAGS) other.c
    
    clean:
    	rm -f $(OBJS) $(EXE)
    	rm -f tests/*.out
    
    # The '@' symbol at the start of a line suppresses output
    test1:	$(EXE) tests/1.good
    	@rm -f tests/1.out
    	@./$(EXE) > tests/1.out
    	@diff tests/1.good tests/1.out && echo "Test 1 PASSED" || echo "Test 1 FAILED"
    
    test2:	$(EXE) tests/2.good
    	@rm -f tests/2.out
    	@./$(EXE) > tests/2.out
    	@diff tests/2.good tests/2.out && echo "Test 2 PASSED" || echo "Test 2 FAILED"
    
    tests:	test1 test2
    	

    What happens when you run 'make tests'?

  • 相关阅读:
    Python 函数内变量的作用域
    python return 及lambda函数
    python 函数的定义及传参
    Python 集合
    python 内置函数
    Python 字典
    Python 元组
    LoaRunner性能测试系统学习教程:Windows计数器(2)
    LoaRunner性能测试系统学习教程:操作系统监控(1)
    LoaRunner性能测试系统学习教程:结果分析实践之Summary View(8)
  • 原文地址:https://www.cnblogs.com/datascientist/p/3468408.html
Copyright © 2011-2022 走看看