今天看到了Makefile这个新东西,之前从没了解过,这就记录下来
Makefile基础 :
关于基础,有个人写的很好,我这个博客转载了他的文章,你可以翻翻看。
make命令会自动读取当前目录下的Makefile
文件,完成相应的编译步骤。
Makefile由一组规则(Rule)组成,每条规则的格式是:
- target ... : prerequisites ...
- command1
- command2
- ...
- main: main.o stack.o maze.o
- gcc main.o stack.o maze.o -o main
main
是这条规则的目标(Target),main.o
、stack.o
和maze.o
是这条规则的条件(Prerequisite)。
目标和条件之间的关系是:欲更新目标,必须首先更新它的所有条件;所有条件中只要有一个条件被更新了,目标也必须随之被更新。所谓“更新”就是执行一遍规则中的命令列表,命令列表中的每条命令必须以一个Tab开头,
注意不能是空格,Makefile的格式不像C语言的缩进那么随意,对于Makefile中的每个以Tab开头的命令,make
会创建一个Shell进程去执行它。
通常Makefile都会有一个clean
规则,用于清除编译过程中产生的二进制文件,保留源文件:
- clean:
- @echo "cleanning project"
- -rm main *.o
- @echo "clean completed"
- $ make clean
- cleanning project
- rm main *.o
- clean completed
make
的命令行中指定一个目标(例如
clean
),则更新这个目标,如果不指定目标则更新Makefile中第一条规则的目标(缺省目标)。
命令前面加@
和-
字符的效果:
如果make
执行的命令前面加了@
字符,则不显示命令本身而只显示它的结果;
通常make
执行的命令如果出错(该命令的退出状态非0)就立刻终止,不再执行后续命令,但如果命令前面加了-
号,即使这条命令出错,make
也会继续执行后续命令。
通常rm
命令和mkdir
命令前面要加-
号,因为rm
要删除的文件可能不存在,mkdir
要创建的目录可能已存在,这两个命令都有可能出错,但这种错误是应该忽略的。
文件名则不一定是Makefile
。事实上,执行make
命令时,是按照GNUmakefile
、makefile
、Makefile
的顺序找到第一个存在的文件并执行它,不过还是建议使用Makefile
做文件名。除了GNU make
,有些UNIX系统的make
命令不是GNU make
,不会查找GNUmakefile
这个文件名,如果你写的Makefile包含GNU make
的特殊语法,可以起名GNUmakefile
,否则不建议用这个文件名。
隐含规则和模式规则:
make的隐含规则数据库可以用make -p
命令打印,打印出来的格式也是Makefile的格式,包括很多变量和规则。
#号在Makefile中表示单行注释,就像C语言的//
注释一样。CC
是一个Makefile变量,用CC = cc
定义和赋值,用$(CC)
取它的值,其值应该是cc
。
$@和$<
是两个特殊的变量,$@
的取值为规则中的目标,$<
的取值为规则中的第一个条件。%.o: %.c
是一种特殊的规则,称为模式规则(Pattern Rule)。
一个目标依赖于若干条件,现在换个角度,以条件为中心,Makefile还可以这么写:
- main: main.o stack.o maze.o
- gcc main.o stack.o maze.o -o main
- main.o stack.o maze.o: main.h
- main.o maze.o: maze.h
- main.o stack.o: stack.h
- clean:
- -rm main *.o
- .PHONY: clean
make
会拆成几条单目标的规则来处理,例如
- target1 target2: prerequisite1 prerequisite2
- command $< -o $@
- target1: prerequisite1 prerequisite2
- command prerequisite1 -o target1
- target2: prerequisite1 prerequisite2
- command prerequisite1 -o target2
$@
的取值不同。
变量:
- foo = $(bar)
- bar = Huh?
- all:
- @echo $(foo)
我们执行make
将会打出Huh?
。当make
读到foo = $(bar)
时,确定foo
的值是$(bar)
,但并不立即展开$(bar)
,然后读到bar = Huh?
,确定bar
的值是Huh?
,然后在执行规则all:
的命令列表时才需要展开$(foo)
,得到$(bar)
,再展开$(bar)
,得到Huh?
。因此,虽然bar
的定义写在foo
之后,$(foo)
展开还是能够取到$(bar)
的值。
有时候我们希望make
在遇到变量定义时立即展开,可以用:=
运算符,例如:
- x := foo
- y := $(x) bar
- all:
- @echo "-$(y)-"
运算符是?=
,例如foo ?= $(bar)
的意思是:如果foo
没有定义过,那么?=
相当于=
,定义foo
的值是$(bar)
,但不立即展开;如果先前已经定义了foo
,则什么也不做,不会给foo
重新赋值。
常用的特殊变量有:
-
$@
,表示规则中的目标。 -
$<
,表示规则中的第一个条件。 -
$?
,表示规则中所有比目标新的条件,组成一个列表,以空格分隔。 -
$^
,表示规则中的所有条件,组成一个列表,以空格分隔。
- main: main.o stack.o maze.o
- gcc main.o stack.o maze.o -o mainv
- main: main.o stack.o maze.o
- gcc $^ -o $@
常用的
make
命令行选项-n
选项只打印要执行的命令,而不会真的执行命令,这个选项有助于我们检查Makefile写得是否正确,由于Makefile不是顺序执行的,用这个选项可以先看看命令的执行顺序,确认无误了再真正执行命令。-C选项可以切换到另一个目录执行那个目录下的Makefile,
在
make
命令行也可以用=
或:=
定义变量,如果这次编译我想加调试选项-g
,但我不想每次编译都加-g
选项,可以在命令行定义CFLAGS
变量,而不必修改Makefile编译完了再改回来:$ make CFLAGS=-g cc -g -c -o main.o main.c cc -g -c -o stack.o stack.c cc -g -c -o maze.o maze.c gcc main.o stack.o maze.o -o main
如果在Makefile中也定义了
CFLAGS
变量,则命令行的值覆盖Makefile中的值。
模板很多,可以在网上搜索下看看。
下面附一个makefile 典型模板:
- # Makefile for iPhone Application for Xcode gcc compiler (SDK Headers)
- IPHONE_IP=192.168.1.115
- DARWINVERSION=10
- GCCVERSION=4.2.1
- SDKVERSION=3.1
- ENVP=IPHONEOS_DEPLOYMENT_TARGET=3.0
- PROJECTNAME=iColorCall
- APPFOLDER=$(PROJECTNAME).app
- INSTALLFOLDER=$(PROJECTNAME).app
- SDK=/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS$(SDKVERSION).sdk
- CC=/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/arm-apple-darwin$(DARWINVERSION)-gcc-$(GCCVERSION)
- CPP=/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/arm-apple-darwin$(DARWINVERSION)-g++-$(GCCVERSION)
- LD=$(CC)
- LDFLAGS += -lobjc \
- -ObjC++ \
- -fobjc-exceptions \
- -march=armv6 \
- -mcpu=arm1176jzf-s \
- -fobjc-call-cxx-cdtors \
- -multiply_defined suppress \
- -framework CoreFoundation \
- -framework Foundation \
- -framework UIKit \
- -framework CoreGraphics \
- -framework AddressBookUI \
- -framework AddressBook \
- -framework QuartzCore \
- -framework OpenGLES \
- -framework CoreTelephony \
- -L"$(SDK)/usr/lib" \
- -F"$(SDK)/System/Library/Frameworks" \
- -F"$(SDK)/System/Library/PrivateFrameworks"
- CFLAGS += -I"/Developer/Platforms/iPhoneOS.platform/Developer/usr/lib/gcc/arm-apple-darwin$(DARWINVERSION)/(GCCVERSION)/include/" \
- -I"$(SDK)/usr/include" \
- -I"/Developer/Platforms/iPhoneOS.platform/Developer/usr/include/" \
- -F"$(SDK)/System/Library/Frameworks" \
- -F"$(SDK)/System/Library/PrivateFrameworks" \
- -I./Classes \
- -I./Classes/Glgif \
- -I./Classes/Engine \
- -march=armv6 \
- -mcpu=arm1176jzf-s \
- -DDEBUG -std=c99 \
- -Diphoneos_version_min=3.0 \
- -DTARGET_OS_IPHONE \
- -DSP3 \
- -DLOG_APP_DEBUG_ENABLE \
- -DHEX_OUT_ENABLE
- #-DWINKS_LONG_SOCKET
- #-DWINKS_SOCKET_WRITE_CATCH
- #-DIPHONE_GIF_PREVIEW_TEST
- #-DTEST_STATIC_IMG_PREVIEW
- CPPFLAGS=$CFLAGS
- BUILDDIR=./build/$(SDKVERSION)
- SRCDIR=./Classes
- SRCDIR2=./Classes/Engine
- SRCDIR3=./Classes/Glgif
- RESDIR=./Resources
- OBJS=$(patsubst %.m,%.o,$(wildcard $(SRCDIR2)/*.m)) \
- $(patsubst %.c,%.o,$(wildcard $(SRCDIR2)/*.c)) \
- $(patsubst %.cpp,%.o,$(wildcard $(SRCDIR2)/*.cpp)) \
- $(patsubst %.m,%.o,$(wildcard $(SRCDIR)/*.m)) \
- $(patsubst %.c,%.o,$(wildcard $(SRCDIR)/*.c)) \
- $(patsubst %.cpp,%.o,$(wildcard $(SRCDIR)/*.cpp)) \
- $(patsubst %.mm,%.o,$(wildcard $(SRCDIR)/*.mm)) \
- $(patsubst %.m,%.o,$(wildcard $(SRCDIR3)/*.m)) \
- $(patsubst %.c,%.o,$(wildcard $(SRCDIR3)/*.c)) \
- $(patsubst %.cpp,%.o,$(wildcard $(SRCDIR3)/*.cpp)) \
- $(patsubst %.m,%.o,$(wildcard *.m))
- PCH=$(wildcard *.pch)
- RESOURCES=$(wildcard $(RESDIR)/*)
- NIBS=$(patsubst %.xib,%.nib,$(wildcard *.xib))
- all: dist
- $(PROJECTNAME): $(OBJS)
- $(LD) $(LDFLAGS) -o $@ $^
- %.o: %.m
- $(ENVP) $(CC) -c $(CFLAGS) $< -o $@
- %.o: %.c
- $(ENVP) $(CC) -c $(CFLAGS) $< -o $@
- %.o: %.cpp
- $(ENVP) $(CPP) -c $(CPPFLAGS) $< -o $@
- %.o: %.mm
- $(ENVP) $(CC) -c $(CFLAGS) $< -o $@
- %.nib: %.xib
- ibtool $< --compile $@
- dist: $(PROJECTNAME) $(NIBS)
- rm -rf $(BUILDDIR)
- mkdir -p $(BUILDDIR)/$(APPFOLDER)
- cp -r $(RESOURCES) $(BUILDDIR)/$(APPFOLDER)
- cp Info.plist $(BUILDDIR)/$(APPFOLDER)/Info.plist
- @echo "APPL????" > $(BUILDDIR)/$(APPFOLDER)/PkgInfo
- cp -r English.lproj $(BUILDDIR)/$(APPFOLDER)/English.lproj/
- cp -r zh_CN.lproj $(BUILDDIR)/$(APPFOLDER)/zh_CN.lproj/
- cp -r zh_HK.lproj $(BUILDDIR)/$(APPFOLDER)/zh_HK.lproj/
- rm -rf $(BUILDDIR)/$(APPFOLDER)/English.lproj/.svn
- rm -rf $(BUILDDIR)/$(APPFOLDER)/zh_CN.lproj/.svn
- rm -rf $(BUILDDIR)/$(APPFOLDER)/zh_HK.lproj/.svn
- mv $(NIBS) $(BUILDDIR)/$(APPFOLDER)
- export CODESIGN_ALLOCATE=/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/codesign_allocate; ./ldid_intel -S $(PROJECTNAME)
- mv $(PROJECTNAME) $(BUILDDIR)/$(APPFOLDER)
- installapp: uninstallapp
- #只上传程序不上传资源, 节省安装时间
- scp -r $(BUILDDIR)/$(APPFOLDER)/$(PROJECTNAME) root@$(IPHONE_IP):/Applications/$(INSTALLFOLDER)/$(PROJECTNAME)
- ssh root@$(IPHONE_IP) 'killall SpringBoard'
- uninstallapp:
- #只删除程序
- ssh root@$(IPHONE_IP) 'rm -fr /Applications/$(INSTALLFOLDER)/$(PROJECTNAME)'
- install: uninstall
- #ssh root@$(IPHONE_IP) 'rm -f /private/var/mobile/Library/Caches/com.apple.mobile.installation.plist'
- #上传程序及资源
- scp -r $(BUILDDIR)/$(APPFOLDER) root@$(IPHONE_IP):/Applications/$(INSTALLFOLDER)
- ssh root@$(IPHONE_IP) 'killall SpringBoard'
- uninstall:
- #删除程序及资源
- ssh root@$(IPHONE_IP) 'rm -fr /Applications/$(INSTALLFOLDER)'
- install_respring:
- scp respring_arm root@$(IPHONE_IP):/usr/bin/respring
- clean:
- @rm -f $(SRCDIR)/*.o *.o
- @rm -f $(SRCDIR2)/*.o *.o
- @rm -f $(SRCDIR3)/*.o *.o
- @rm -rf $(BUILDDIR)
- @rm -f $(PROJECTNAME)
- log:
- scp root@$(IPHONE_IP):/var/mobile/appLog ~/Desktop/appLog/appLog
- clearlog:
- ssh root@$(IPHONE_IP) 'rm -fr /var/mobile/appLog'