??xml version="1.0" encoding="utf-8" standalone="yes"?>
Most UNIX® and Linux® programs are built by running make
. The make utility reads a file (generally named either "makefile" or "Makefile," but hereafter merely referred to as "a makefile") that contains instructions and performs various actions to build a program. In many build processes, the makefile is itself generated entirely by other software; for instance, the autoconf/automake
programs are used to develop build routines. Other programs may ask you to directly edit a makefile, and of course, new development may require you to write one.
The phrase "the make utility" is misleading. There are at least three distinct variants in common use: GNU make, System V make, and Berkeley make. Each grew from a core specification from the early UNIX days, and each adds new features. This results in a difficult situation: fairly commonly used features, such as including other files in a makefile by reference, cannot be done portably! The decision to simply write a program to create makefiles is one solution. Because GNU make is free and widely distributed, some developers simply code for it; similarly, a few projects with BSD origins require you to use Berkeley make (which is also free).
Less common but still relevant are Jörg Schilling's smake
, and the absentee fifth member of the family, historical make, which defines the common feature subset that all the others share. While smake is not the default make on any system, it's a good make implementation, and some programs (especially Schilling's) use it by preference.
Let's review some of the most common problems you'll encounter when working with makefiles.
To debug make, you have to be able to read a makefile. As you know, the purpose of a makefile is to give instructions for building a program. One of make's key features is dependency management: make attempts to rebuild only what it has to when a program is updated. In general, this is expressed through a series of dependency rules. A dependency rule looks like this:
target: dependencies instructions |
The main problem people encounter when writing their first makefile is visible in this construction; or rather, invisible. The indentation is a tab. It is not any number of spaces. The Berkeley make error message for a file using spaces in this format is not terribly helpful:
make: "Makefile" line 2: Need an operator make: Fatal errors encountered -- cannot continue |
GNU make, while still unable to process the file, gives a more helpful suggestion:
Makefile::2: *** missing separator (did you mean TAB instead of 8 spaces?). Stop. |
Note that both the dependencies and the instructions are optional; only the target and the colon are required. So, that's the syntax. What are the semantics? The semantics are that, if make wishes to build target
, it will first look at dependencies. In fact, it will recursively attempt to build them; if the dependencies in turn have dependencies, those will be dealt with before this rule continues. If target
exists and is at least as new as all of the items listed in dependencies
, nothing is done. If target
does not exist, or one or more dependencies is newer, then make executes instructions
. Dependencies are processed in the order that they are specified. If no dependencies are specified, the instructions are followed anyway. Dependencies are also called sources.
If a target is given on the command line (for example, make foo
), then make will attempt to build that target. Otherwise, it will try to build the first target listed in the file. One convention some developers use is to have the first target look like this:
default: all |
Some people assume that make uses this rule because it's named "default." Not so; it's used because it's the first rule in the file. You could name it anything you wanted, but the name "default" is a good choice because it communicates to the reader. Remember that your makefile is going to be read by humans, not just make programs.
In general, it is asserted that a target's function is to create a file from other files. In fact, this is not always the case. Most makefiles have at least a couple of rules that never create a target. Consider the following example rule:
all: hello goodbye fibonacci |
This rule instructs make -- if it wishes to build the target all
-- to first make sure that hello, goodbye, and fibonacci are up to date. Then...nothing. No instructions are provided. After this rule is complete, no file named "all" is created. The target is a fake. The technical term used in some varieties of make is "phony."
Phony targets are used for organizational purposes and are a wonderful thing in writing a clear and legible makefile. For instance, one often sees rules like this:
build: clean all install |
This specifies an order of operations for the build process.
A few special targets are defined that have special effects on make, providing a configuration mechanism. The exact set varies from one implementation to another; the most common is the .SUFFIXES
target, the "sources" for which are a series of patterns to be added to the list of recognized file suffixes. Special targets don't count for purposes of the usual rule that make builds the first target in the makefile by default.
Some versions of make allow special sources to be specified along with the dependencies for a given target, such as .IGNORE
, which indicates that errors from commands used to build this target should be ignored, as though a dash preceded them. These flags are not especially portable but may be necessary to understand in debugging a makefile.
There are implicit rules in make for performing generic transforms based on filename suffixes. For instance, with no makefile present, create a file called "hello.c" and run make hello
:
$ make hello
cc -O2 -o hello hello.c
|
makefiles for larger programs may simply specify a list of object modules they need (hello.o, world.o, and so on), then provide a rule for converting .c files to .o files:
.c.o: cc $(CFLAGS) -c $< |
In fact, most make utilities have a rule very much like this built in already; if you ask make to build file.o, and it has file.c, it'll do the right thing. The term "$<" is a special predefined make variable that refers to the "source" for a rule. That brings us to make variables.
Generic rules depend on the declaration of "suffixes," which make then recognizes as filename extensions rather than part of a name.
The make program uses variables to make it easier to reuse common values. The most set value is probably CFLAGS
. A few things should be clarified about make variables. They are not necessarily environment variables. If no make variable with a given name exists, make will check for environment variables; however, that doesn't mean that make variables are exported to the environment. The precedence rules are arcane; in general, the order from highest to lowest precedence is this:
Thus, environment variables are used only if a variable is not set in any makefile or on the command line. (Note: parent makefile variables are sometimes, but not always, passed down. The rules, as you may have guessed, vary from one flavor of make to another.)
A common problem people run into with make is variables inexplicably being replaced by parts of their names: for instance, $CFLAGS
being replaced by "FLAGS". To refer to a make variable, put its name in parentheses: $(CFLAGS)
. Otherwise, you get $C
followed by FLAGS
.
A number of variables have special meanings that are a function of the rule they're being used in. The most commonly used are:
$<
- The source from which the target is to be made
$*
- The base name of the target (no extensions or directory)
$@
- The full name of the target Berkeley make deprecates these variables, but they are (for now) still portable. Sort of portable, anyway; the exact definitions may vary between make implementations. Anything complicated you write with these will likely end up specific to a given implementation.
It's occasionally desirable to perform some sort of task beyond the scope of what can be done portably in make. The conventional solution, since make runs everything through the shell, is to write an inline shell script. Here's how.
First, be aware that while shell scripts are traditionally written on multiple lines, they can be compressed to a single line with semicolons to separate statements. Second, be aware that this is illegible. The solution is a compromise: write the script with the usual indentation, but with each line ending with "; \". This ends each shell command syntactically (with a semicolon) but makes the text part of a single make command that will be passed to the shell all at once. For instance, the following might show up in a top-level makefile:
all: for i in $(ALLDIRS) ; \ do ( cd $$i ; $(MAKE) all ) ; \ done |
This illustrates the three things to keep in mind. First, the use of semicolons and backslashes. Second, the use of $(VARIABLE)
for make variables. Third, the use of $$
to pass a dollar sign to the shell. That's it! It really is that easy.
By default, make prints every command it runs, and aborts if any command fails. In some cases, it's possible that a command will appear to fail, but you will want the build to continue. If the first character of a command is a hyphen (-), the remainder of the line is executed, but its exit status is ignored.
If you don't want to echo a command, prefix it with an at-sign (@). This is most commonly used for displaying messages:
all: @echo "Beginning build at:" @date @echo "--------" |
Without the @ signs, this would produce the output:
echo "Beginning build at:" Beginning build at: date Sun Jun 18 01:13:21 CDT 2006 echo "--------" -------- |
While the @ sign doesn't really change what make does, it's a very popular feature.
![]() |
|
Some things that everyone wants to do cannot be portably done. But there are some workarounds to be had.
One of the most frustrating historical compatibility problems is handling inclusion in makefiles. Historical make implementations didn't always provide a way to do this, although all modern varieties appear to. The GNU make syntax is simply include file
. The traditional Berkeley syntax is .include "file"
. At least one Berkeley make now supports the GNU notation as well, but not all do. The portable solution, discovered by both autoconf
and Imake
, is just to include every variable assignment you think you might want.
Some programs simply require the use of GNU make. Some require Berkeley make. Still others, smake. If you need include files badly enough, it's not unthinkable to simply specify a make utility that your tree will be built with. (Of the three portable ones distributed in source, my favorite is Berkeley make.)
Getting variables to nested builds
There is no really good way to do this. If you use an include file, you run into the portability problem of including the file cleanly. If you set the variables in every file, it becomes very hard to overwrite them all. If you set them only in a top-level file, independent builds in subdirectories will fail because the variables aren't set!
Depending on your version of make, one reasonably good solution is to conditionally set variables in every file only if they are not already set; then a change in the top-level file will affect subdirectories in a full build. Of course, then going into a subdirectory and running make there will get different and incompatible results...
This is amplified by the lack of include files, as anyone who has ever struggled with Imake's multi-thousand-line makefiles can attest.
Some writers advocate a simpler solution: don't use recursive make at all. For most projects, this is entirely feasible and can dramatically simplify (and speed up!) compilation. The article by Peter Miller, "Recursive Make Considered Harmful" (see Resources) is probably the canonical source.
![]() |
|
What to do when there's a problem
First, don't panic. Developers have been having weird problems with make since before a complete version was written. Implicit rules, unexpected variable substitutions, and syntax errors from embedded shell scripts are just where the fun begins.
Read the error message. Is it actually from make, or is it from something make is calling? If you have a nested build, you might need to thread up through a bunch of error messages to find the actual error.
If a program isn't being found, first check to see whether it's installed. If it is, check paths; some developers have the habit of specifying the absolute path to a program in makefiles, which may fail on other systems. If you installed something in /opt, and the makefile refers to /usr/local/bin, the build will fail. Fix the path.
Check your clock; more importantly, look at the dates of files in your build tree, and other files on your system, and your clock. The behavior of make when confronted with chronologically inconsistent input data can range from harmless to surreal. If you had clock problems (some "new" files datestamped in 1970, for instance), you will need to fix those. The "touch" utility is your friend. The error messages you will get from clock skew will not generally be obvious.
If the error messages you're getting imply that you have syntax errors, or tons of variables unset or set incorrectly, try a different version of make; for instance, some programs will get cryptic errors from gmake but build fine with smake. Really weird errors may indicate that you're running a Berkeley makefile with GNU make, or vice versa. Programs native to Linux often assume GNU make and fail in inexplicable ways with anything else, even if there's no hint of this in the documentation.
Debugging flags can be pretty useful. For GNU make, the -d
flag will give you a HUGE amount of information, some of it useful. For Berkeley make, the -d
flag takes a set of flags; -d A
is the complete set, or you can use subsets; for instance, -d vx
will give debugging information about variable assignments (v
) and cause all commands to be run through sh -x
so the shell will echo the exact command it received. The -n
debugging flag causes make to print a list of things it thinks it needs to do; it's not always correct, but it often gives you an idea of what's wrong.
Your goal in debugging a makefile is to figure out what make is trying to build, and what commands it thinks will build it. If make is picking the right commands, and they're failing, you may be done debugging make -- or you may not. For instance, if the attempt to compile a program fails due to unresolved symbols, it's possible that an earlier phase of the build process did something wrong! If you are unable to figure out what's wrong with a command, and it looks like it should work, it's quite possible that one of the files already created by make was created incorrectly.
![]() |
|
As is often the case, the primary documentation for GNU make is not available in man format, unfortunately; you have to use the info system instead, and you can't just run man make
and look for information about it. However, the documentation is reasonably complete.
It is fairly hard to get good documentation on the "safe subset" of features that all implementations support. The Berkeley and GNU make documentation both tend to mention when they're describing an extension, but it's good to test things before relying too heavily on guesses about the exact boundaries.
Over time, drift between the BSDs has resulted in subtle differences between their implementations of make. Of the three, NetBSD is the one for which make is most actively supported on other systems; the NetBSD pkgsrc system is in use on other platforms, and relies heavily on NetBSD's implementation of make.
转自:http://www-128.ibm.com/developerworks/library/l-debugmake.html
![]() |
public void runBare() throws Throwable { setUp(); try { runTest(); } finally { tearDown(); } } |
protected void setUp() throws Exception {} protected void tearDown() throws Exception {} |
转蝲Q?a >http://fly-net-cn.javaeye.com/blog/78611
定义一l算法,每个算法都装hQƈ且它们之间可以互换。策略模式ɘq些法在客L(fng)调用它们的时候能够互不媄响地变化?
{略模式使开发h员能够开发出p多可替换的部分组成的软gQƈ且各个部分之间是p接的关系。弱q接的特性软gh更强的可扩展性,易于l护Q更重要的是Q它大大提高了Y件的可重用性?
Z说明{略模式Q我们将首先讨论一下在Swing中是如何利用{略模式来绘制组件边界的Q然后讨论在Swing中用策略模式带来的好处Q最后讨论如何在软g中实现策略模式?
Ҏ(gu)有的SwinglgQ例如按钮、列表单{,都还可以l制Ҏ(gu)。在Swing中提供了各种 Ҏ(gu)cdQ例如bevel、etched、line、titled{。Swinglg的边框是通过JComponentcLl制的,该类是所有Swing lg的基c,实现了所有Swinglg公共的功能。在JComponent中有一个paintBorder()Ҏ(gu)Q该Ҏ(gu)为组件绘制边框。Swing?开发h员可以象下面的例子中所C那hl制Ҏ(gu)Q?
|
h意上面的代码只是一U假设,事实上Swing的开发h员ƈ没有q样实现 paintBorderQ)Ҏ(gu)。在上面的代码中Q在JComponent中绘制边框的代码被直接写入了paintBorderQ)Ҏ(gu)中,q意味着 JComponent和绘制边框的功能被紧密地l合在了一赗很自然地大家会联想到如果需要实CU新的边框类型,开发h员必M改至三处代码:首先?加一个常量,该常量代表新d的边框的cd|其次需要在Switch语句中增加一个case语句Q最后开发h员需要实现paintXXXBorder Q)Ҏ(gu)Q其中XXX代表新边框的名称?
很显然要扩展上面paintBorder()Ҏ(gu)的功能是一件很困难的事情,不仅 仅是因ؓ开发h员需要增加一U新的边框类型,更麻烦的是开发h员很难修改JComponentcRJComponentcdl被~译CSwing的开?工具中,如果开发h员想修改它的话,必须获得Swing的源代码Q修改后重新~译Swing。同时在用户的计机上与需要用新~译的Swing API。另外所有的Swinglg都可以用开发h员新d的边框类型。有可能开发h员只希望新的Ҏ(gu)被某些组件用,但是现在开发h员无法对使用该边?的组件进行限制?
开发h员有更好的实现方法吗Q答案就是策略模式。通过{略模式Q可以将 JComponent和实现绘制边框的代码分离开来,q样开发h员在增加或修改绘制边框的代码使就不需要修改JComponent的代码。通过应用{略?式,开发h员将变化的概念(在这个例子中是绘制边框)装hQ然后通过一个Border接口QɽE序能够重用l制Ҏ(gu)的功能。下面让我们来看 JComponent是如何利用策略模式来实现l制Ҏ(gu)的功能的Q?
|
上面的paintBorder()Ҏ(gu)通过一个border对象l制了组件的Ҏ(gu)?q样border对象替代了前一个例子中的JComponent装了边框绘制的功能。我们还应该注意到JComponent一个对自己的引用传递给?Border.paintBorderQ)Ҏ(gu)Q这是因为Border的实例必ȝ道它对应的组件的信息Q这U方式通常被称为委托。通过q种方式Q一个对 象可以将功能委托l另一个对象来实现?
在JComponentcM引用了一个Border对象Q通过JComponent.getBorderQ)Ҏ(gu)可以获得该Border对象。下面的代码演示了如何设定和获得Border对象Q?
|
当开发h员通过JComponent.setBorderQ)Ҏ(gu)讑֮了一个组件的 Ҏ(gu)后,JComponentcdZ个属性更C件。如果新的边框和以前的边框不同的话,setBorderQ)Ҏ(gu)重新绘制边框?getBorderQ)Ҏ(gu)仅仅q回对Border对象的引用。图1昄了Border的类l构图:
通过cȝ构图我们可以看到QJComponentcM保存了一个对Border对象的引用。由于Border是一个接口,Swinglg可以使用M一个实CBorder接口的类?
现在我们已经知道了JComponent是如何利用策略模式来l制lg的边框的。下面让我们通过实现一个新的边框类型来试一下它的可扩展性?
?中是一个有三个JPanel对象的小E序Q每个JPanel对象有各自不同的Ҏ(gu)Q每个边框对应一个HandleBorder实例?
|
HandleBordercȝ承了 javax.swing.border.AbstractBordercdƈ重写了paintBorderQ)和getBorderInsetsQ)?HandleBorder是如何实现的其实q不重要Q重要的是由于Swing使用了策略模型,开发h员能够很方便地增加新的边框类型。下面的代码昄了如 何用HandleBordercR在q个例子中创Z三个JPanel对象QƈҎ(gu)个JPanel对象讑֮一个HandleBorder实例作ؓҎ(gu)?
|
q记得在上面的例子中曾提到在有些情况下,对组件的引用会作为参C递给 Border.paintBorderQ)Ҏ(gu)。虽然上面的HandleBordercL有保存对lg的引用,但是有些情况下Border接口的实现类?使用到对lg的引用ƈ从中获得关于lg的信息。例如在EtchedBorder中,paintBorderQ)Ҏ(gu)通过对组件的引用获得它对应的lg的阴 影和高光Ԍ
|
通过以下步骤Q开发h员可以很Ҏ(gu)地在软g中实现策略模型:
1Q对{略对象定义一个公共接口?
2Q编写策略类Q该cdC上面的公共接口?
3Q在使用{略对象的类中保存一个对{略对象的引用?
4Q在使用{略对象的类中,实现对策略对象的set和getҎ(gu)?
在SwingҎ(gu)的例子中Q公共接口是javax.swing.Border。策略类是LineBorder、EtchedBorder、HandleBorder{。而用策略对象的cLJComponent?
转蝲Q?a >http://fly-net-cn.javaeye.com/blog/78615
作者:江南白衣Q原文出处: http://blog.csdn.net/calvinxiu/archive/2007/03/06/1522032.aspx
q个领域没有什?畅销?Q可能读者中本来是开发设计h员与目l理占了多数Q真正定位ؓ架构师而且做的也是架构师工作的不多吧,你懂的尽是偏?ni)人生?/p>
1.软g架构入门 《Software Architecture in Practice,2nd Edition--软g构架实践(W???/strong> W一版是W九(ji)届JOLT作品Q一本被引用很多的架构书?/p>
《Documenting Software Architectures --软g构架~档?/strong> W?3届JOLT大奖作品Q捕h构的q程Q?a id=AjaxHolder_Comments_CommentList_ctl16_NameLink target=_blank>徐昊 一、Software Architecture?/h2>
《Large-Scale Software Architecture --大型软g体系l构Q用UML实践指南?br> C架构师的入行指南Q从什么是架构和架构师一直到以构件ؓ_度的大型系l架构UML实例?/p>
2. 软g架构q阶
《Applied Software Architecture --实用软g体系l构?/strong>
另一本被引用很多的架构之书?br>
《The Art of Software Architecture --软g体系l构的艺术?/strong>
薄薄的一本,上面几本的理论抽象与薄化?br>
《Evaluating Software Architectures --软g构架评估?
SEI出品Q正儿八l的ATAM架构评估Ҏ(gu)和两个评估案例,带介绍了SAAM和ARID?strong>
RUP、UML?+1视图始终是架构师界最通用的东西,L一U向世界妥协的方式?br> 3.《UML和模式应?W??--Applying UML and Patterns 3rd?/strong> 开发h员有GOF23 PatternQ架构师同样也有架构师的Pattern。不同领域的架构师需要不同的知识?br>1. 公共领域 《Object Oriented Reengineering Patterns--软g再造:面向对象的Y件再工程模式 ?/strong> 著名的POSA2与POSA3?br> 《Code Complete 2 --代码大全2?/strong> 《The Pragmatic Programmer --E序员修g道:从小工到专家?/strong> 《The Art of Unix Programming --UNIX~程艺术?/strong> 刚好Head Firstpd开头都有一D|人如何读书的话,再加工整理如下:
1. RUP 文档
《RUP 7.0.1文档?/strong>在Rational Method Composer 7.0试用版之中,是最好架构师的行动指南与自助?br>《The Rational Unified Process:An Introduction.3rd Edition --RUPD(W???/strong>是RUP2003文档的羃略版Q在啃文档细节前可以先拿中文版来M下?br> 但公叔RL充满对RUP的误?strong>Q《The Rational Unified Process Made Easy:A Practitioner's Guide to the RUP--Rationall一q程Q实践者指南?/strong>是排除各U误解的RUP实施指南?br>
2. UML2工具?br>《UML2 Toolkit--UML2.0工具》或者《UML_aW??-UML Distilled 3rd?/strong>都不错,后一本略薄。UML2.0相比UML1.x Q对于模型派的架构师来说是天大的恩物Q一定要补课?/p>
UML+RUP作的OOADq程?/p>
三、特定领域篇
《Domain-Specific Application Frameworks --特定领域应用框架Q行业的框架体验?br> ozzzzzz推荐Q介l了30个特定领域特定框架的设计。我自己最喜欢看h家的设计与思考?/p>
逆向工程与再工程的模式,架构师整天都要和旧系l打交道Q接手一个架构师已跑路,文档不全的系l开?.0版本?br>
《Head First Design Patterns?nbsp;
最好的GOF23l典设计模式阐释Q适合被[GAMMA95]折磨的架构师拿来复习Q中文版卛_发行?br>
2. Java EE领域
《Patterns of Enterprise Application Architecture --企业应用架构模式?
Martin Fowlerl典Q企业应用各Layers上的模式?nbsp;
《Effective Enterprise Java--中文版?/strong>
Neward, Ted作品Q作者学贯东?.Net与Java)Q像写Blog一P每一里面都有大量的信息?/p>
3. EAI/SOA领域
《Enterprise Integration Patterns --企业集成模式Q设计、构建及部v消息传递解x案?/strong>
4. |络与后台服务编E领?br>《Pattern-Oriented Software Architecture, Volume 2 --面向模式的Y件体pȝ??:用于q发和网l化对象的模式?br>
《Pattern-Oriented Software Architecture, Volume 3 --面向模式的Y件体pȝ构卷3Q资源管理模式?/strong>
四、闲书篇
一本你教育弟时的代言人?/p>
一本你启发弟的代a人?/p>
五、高效读书心?/h2>
1.量阅读中文?/strong>
虽然有h英文很强Q有的翻译很差,但AnyWay 中文阅读与理解的旉Q略M快速定位的速度q是要快一些?br>
2.xҎ(gu)、ȝW记与交?/strong>
虽然׃Q但发现最有效的读书方式还是不断的刉脂Ҏ(gu)Q读书时在重要的文字下划U,把自q心得写在|?br> 在明天复习一ơ批注,最好可以有I重新整理笔讎ͼ或者拿来与论?nbsp;
3.大量思考或重复记忆
看书最郁闷的事情就是看完之后脑袋空IZ。技术书q好点,虽然看的时候可能很辛苦Q但像学会了骑单RQ之后再骑的时候L会的Q而偏设计与管理的书,最Ҏ(gu)的事情就是看的时候很快,看完没什么留下到实践中?br> 所以,我们不能以看说的速度来看设计书,要寻找思考的ZQ思考是最好的记忆?br> 又或者,大量的重复记忆,重复多遍直到无意识的记忆?nbsp;
4.Z工学
那些见缝插针的时间与地点不是看这个书单的好地斏V?br> 环境不要有电(sh)视,音乐{强输入源,而微风阳光鸟语等p入源则有助活跃大脑?br> 看书时大量的喝水?br> 如果发现自己的大脑已l疲累,已经在Q光掠qȝQ就要休息?br> 留给大脑消化的时_看完书不要接着看其他有隑ֺ的书或事情?/p>