Sunday, September 30, 2012

转载: 如何写make file 文件

什么是makefile?或许很多Winodws的程序员都不知道这个东西,因为那些Windows的IDE都为你做了这个工作,但我觉得要作一个好的和professional的程序员,makefile还是要懂。这就好像现在有这么多的HTML的编辑器,但如果你想成为一个专业人士,你还是要了解HTML的标识的含义。特别在Unix下的软件编译,你就不能不自己写makefile了,会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力。 

因为,makefile关系到了整个工程的编译规则。一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令。 

makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefile都成为了一种在工程方面的编译方法。 

现在讲述如何写makefile的文章比较少,这是我想写这篇文章的原因。当然,不同产商的make各不相同,也有不同的语法,但其本质都是在“文件依赖性”上做文章,这里,我仅对GNU的make进行讲述,我的环境是RedHat Linux 8.0,make的版本是3.80。必竟,这个make是应用最为广泛的,也是用得最多的。而且其还是最遵循于IEEE 1003.2-1992 标准的(POSIX.2)。 

在这篇文档中,将以C/C++的源码作为我们基础,所以必然涉及一些关于C/C++的编译的知识,相关于这方面的内容,还请各位查看相关的编译器的文档。这里所默认的编译器是UNIX下的GCC和CC。 

  

关于程序的编译和链接 
—————————— 

在此,我想多说关于程序编译的一些规范和方法,一般来说,无论是C、C++、还是pas,首先要把源文件编译成中间代码文件,在Windows下也就是 .obj 文件,UNIX下是 .o 文件,即 Object File,这个动作叫做编译(compile)。然后再把大量的Object File合成执行文件,这个动作叫作链接(link)。 

编译时,编译器需要的是语法的正确,函数与变量的声明的正确。对于后者,通常是你需要告诉编译器头文件的所在位置(头文件中应该只是声明,而定义应该放在C/C++文件中),只要所有的语法正确,编译器就可以编译出中间目标文件。一般来说,每个源文件都应该对应于一个中间目标文件(O文件或是OBJ文件)。 

链接时,主要是链接函数和全局变量,所以,我们可以使用这些中间目标文件(O文件或是OBJ文件)来链接我们的应用程序。链接器并不管函数所在的源文件,只管函数的中间目标文件(Object File),在大多数时候,由于源文件太多,编译生成的中间目标文件太多,而在链接时需要明显地指出中间目标文件名,这对于编译很不方便,所以,我们要给中间目标文件打个包,在Windows下这种包叫“库文件”(Library File),也就是 .lib 文件,在UNIX下,是Archive File,也就是 .a 文件。 

总结一下,源文件首先会生成中间目标文件,再由中间目标文件生成执行文件。在编译时,编译器只检测程序语法,和函数、变量是否被声明。如果函数未被声明,编译器会给出一个警告,但可以生成Object File。而在链接程序时,链接器会在所有的Object File中找寻函数的实现,如果找不到,那到就会报链接错误码(Linker Error),在VC下,这种错误一般是:Link 2001错误,意思说是说,链接器未能找到函数的实现。你需要指定函数的Object File. 

好,言归正传,GNU的make有许多的内容,闲言少叙,还是让我们开始吧。 

  

Makefile 介绍 
——————— 

make命令执行时,需要一个 Makefile 文件,以告诉make命令需要怎么样的去编译和链接程序。 

首先,我们用一个示例来说明Makefile的书写规则。以便给大家一个感兴认识。这个示例来源于GNU的make使用手册,在这个示例中,我们的工程有8个C文件,和3个头文件,我们要写一个Makefile来告诉make命令如何编译和链接这几个文件。我们的规则是: 
    1)如果这个工程没有编译过,那么我们的所有C文件都要编译并被链接。 
    2)如果这个工程的某几个C文件被修改,那么我们只编译被修改的C文件,并链接目标程序。 
    3)如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的C文件,并链接目标程序。 

只要我们的Makefile写得够好,所有的这一切,我们只用一个make命令就可以完成,make命令会自动智能地根据当前的文件修改的情况来确定哪些文件需要重编译,从而自己编译所需要的文件和链接目标程序。 


一、Makefile的规则 

在讲述这个Makefile之前,还是让我们先来粗略地看一看Makefile的规则。 

    target ... : prerequisites ... 
            command 
            ... 
            ... 

    target也就是一个目标文件,可以是Object File,也可以是执行文件。还可以是一个标签(Label),对于标签这种特性,在后续的“伪目标”章节中会有叙述。 

    prerequisites就是,要生成那个target所需要的文件或是目标。 

    command也就是make需要执行的命令。(任意的Shell命令) 

这是一个文件的依赖关系,也就是说,target这一个或多个的目标文件依赖于prerequisites中的文件,其生成规则定义在command中。说白一点就是说,prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。这就是Makefile的规则。也就是Makefile中最核心的内容。 

说到底,Makefile的东西就是这样一点,好像我的这篇文档也该结束了。呵呵。还不尽然,这是Makefile的主线和核心,但要写好一个Makefile还不够,我会以后面一点一点地结合我的工作经验给你慢慢到来。内容还多着呢。:) 


二、一个示例 

正如前面所说的,如果一个工程有3个头文件,和8个C文件,我们为了完成前面所述的那三个规则,我们的Makefile应该是下面的这个样子的。 

    edit : main.o kbd.o command.o display.o \ 
           insert.o search.o files.o utils.o 
            cc -o edit main.o kbd.o command.o display.o \ 
                       insert.o search.o files.o utils.o 

    main.o : main.c defs.h 
            cc -c main.c 
    kbd.o : kbd.c defs.h command.h 
            cc -c kbd.c 
    command.o : command.c defs.h command.h 
            cc -c command.c 
    display.o : display.c defs.h buffer.h 
            cc -c display.c 
    insert.o : insert.c defs.h buffer.h 
            cc -c insert.c 
    search.o : search.c defs.h buffer.h 
            cc -c search.c 
    files.o : files.c defs.h buffer.h command.h 
            cc -c files.c 
    utils.o : utils.c defs.h 
            cc -c utils.c 
    clean : 
            rm edit main.o kbd.o command.o display.o \ 
               insert.o search.o files.o utils.o 

反斜杠(\)是换行符的意思。这样比较便于Makefile的易读。我们可以把这个内容保存在文件为“Makefile”或“makefile”的文件中,然后在该目录下直接输入命令“make”就可以生成执行文件edit。如果要删除执行文件和所有的中间目标文件,那么,只要简单地执行一下“make clean”就可以了。 

在这个makefile中,目标文件(target)包含:执行文件edit和中间目标文件(*.o),依赖文件(prerequisites)就是冒号后面的那些 .c 文件和 .h文件。每一个 .o 文件都有一组依赖文件,而这些 .o 文件又是执行文件 edit 的依赖文件。依赖关系的实质上就是说明了目标文件是由哪些文件生成的,换言之,目标文件是哪些文件更新的。 

在定义好依赖关系后,后续的那一行定义了如何生成目标文件的操作系统命令,一定要以一个Tab键作为开头。记住,make并不管命令是怎么工作的,他只管执行所定义的命令。make会比较targets文件和prerequisites文件的修改日期,如果prerequisites文件的日期要比targets文件的日期要新,或者target不存在的话,那么,make就会执行后续定义的命令。 

这里要说明一点的是,clean不是一个文件,它只不过是一个动作名字,有点像C语言中的lable一样,其冒号后什么也没有,那么,make就不会自动去找文件的依赖性,也就不会自动执行其后所定义的命令。要执行其后的命令,就要在make命令后明显得指出这个lable的名字。这样的方法非常有用,我们可以在一个makefile中定义不用的编译或是和编译无关的命令,比如程序的打包,程序的备份,等等。

三、make是如何工作的 

在默认的方式下,也就是我们只输入make命令。那么, 

    1、make会在当前目录下找名字叫“Makefile”或“makefile”的文件。 
    2、如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“edit”这个文件,并把这个文件作为最终的目标文件。 
    3、如果edit文件不存在,或是edit所依赖的后面的 .o 文件的文件修改时间要比edit这个文件新,那么,他就会执行后面所定义的命令来生成edit这个文件。 
    4、如果edit所依赖的.o文件也存在,那么make会在当前文件中找目标为.o文件的依赖性,如果找到则再根据那一个规则生成.o文件。(这有点像一个堆栈的过程) 
    5、当然,你的C文件和H文件是存在的啦,于是make会生成 .o 文件,然后再用 .o 文件生命make的终极任务,也就是执行文件edit了。 

这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make根本不理。make只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起,我就不工作啦。 

通过上述分析,我们知道,像clean这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行,不过,我们可以显示要make执行。即命令——“make clean”,以此来清除所有的目标文件,以便重编译。 

于是在我们编程中,如果这个工程已被编译过了,当我们修改了其中一个源文件,比如file.c,那么根据我们的依赖性,我们的目标file.o会被重编译(也就是在这个依性关系后面所定义的命令),于是file.o的文件也是最新的啦,于是file.o的文件修改时间要比edit要新,所以edit也会被重新链接了(详见edit目标文件后定义的命令)。 

而如果我们改变了“command.h”,那么,kdb.o、command.o和files.o都会被重编译,并且,edit会被重链接。 


四、makefile中使用变量 

在上面的例子中,先让我们看看edit的规则: 

      edit : main.o kbd.o command.o display.o \ 
                  insert.o search.o files.o utils.o 
            cc -o edit main.o kbd.o command.o display.o \ 
                       insert.o search.o files.o utils.o 

我们可以看到[.o]文件的字符串被重复了两次,如果我们的工程需要加入一个新的[.o]文件,那么我们需要在两个地方加(应该是三个地方,还有一个地方在clean中)。当然,我们的makefile并不复杂,所以在两个地方加也不累,但如果makefile变得复杂,那么我们就有可能会忘掉一个需要加入的地方,而导致编译失败。所以,为了makefile的易维护,在makefile中我们可以使用变量。makefile的变量也就是一个字符串,理解成C语言中的宏可能会更好。 

比如,我们声明一个变量,叫objects, OBJECTS, objs, OBJS, obj, 或是 OBJ,反正不管什么啦,只要能够表示obj文件就行了。我们在makefile一开始就这样定义: 

     objects = main.o kbd.o command.o display.o \ 
              insert.o search.o files.o utils.o 

于是,我们就可以很方便地在我们的makefile中以“$(objects)”的方式来使用这个变量了,于是我们的改良版makefile就变成下面这个样子: 

    objects = main.o kbd.o command.o display.o \ 
              insert.o search.o files.o utils.o 

    edit : $(objects) 
            cc -o edit $(objects) 
    main.o : main.c defs.h 
            cc -c main.c 
    kbd.o : kbd.c defs.h command.h 
            cc -c kbd.c 
    command.o : command.c defs.h command.h 
            cc -c command.c 
    display.o : display.c defs.h buffer.h 
            cc -c display.c 
    insert.o : insert.c defs.h buffer.h 
            cc -c insert.c 
    search.o : search.c defs.h buffer.h 
            cc -c search.c 
    files.o : files.c defs.h buffer.h command.h 
            cc -c files.c 
    utils.o : utils.c defs.h 
            cc -c utils.c 
    clean : 
            rm edit $(objects) 


于是如果有新的 .o 文件加入,我们只需简单地修改一下 objects 变量就可以了。 

关于变量更多的话题,我会在后续给你一一道来。 


五、让make自动推导 

GNU的make很强大,它可以自动推导文件以及文件依赖关系后面的命令,于是我们就没必要去在每一个[.o]文件后都写上类似的命令,因为,我们的make会自动识别,并自己推导命令。 

只要make看到一个[.o]文件,它就会自动的把[.c]文件加在依赖关系中,如果make找到一个whatever.o,那么whatever.c,就会是whatever.o的依赖文件。并且 cc -c whatever.c 也会被推导出来,于是,我们的makefile再也不用写得这么复杂。我们的是新的makefile又出炉了。 


    objects = main.o kbd.o command.o display.o \ 
              insert.o search.o files.o utils.o 

    edit : $(objects) 
            cc -o edit $(objects) 

    main.o : defs.h 
    kbd.o : defs.h command.h 
    command.o : defs.h command.h 
    display.o : defs.h buffer.h 
    insert.o : defs.h buffer.h 
    search.o : defs.h buffer.h 
    files.o : defs.h buffer.h command.h 
    utils.o : defs.h 

    .PHONY : clean 
    clean : 
            rm edit $(objects) 

这种方法,也就是make的“隐晦规则”。上面文件内容中,“.PHONY”表示,clean是个伪目标文件。 

关于更为详细的“隐晦规则”和“伪目标文件”,我会在后续给你一一道来。 


六、另类风格的makefile 

即然我们的make可以自动推导命令,那么我看到那堆[.o]和[.h]的依赖就有点不爽,那么多的重复的[.h],能不能把其收拢起来,好吧,没有问题,这个对于make来说很容易,谁叫它提供了自动推导命令和文件的功能呢?来看看最新风格的makefile吧。 

    objects = main.o kbd.o command.o display.o \ 
              insert.o search.o files.o utils.o 

    edit : $(objects) 
            cc -o edit $(objects) 

    $(objects) : defs.h 
    kbd.o command.o files.o : command.h 
    display.o insert.o search.o files.o : buffer.h 

    .PHONY : clean 
    clean : 
            rm edit $(objects) 

这种风格,让我们的makefile变得很简单,但我们的文件依赖关系就显得有点凌乱了。鱼和熊掌不可兼得。还看你的喜好了。我是不喜欢这种风格的,一是文件的依赖关系看不清楚,二是如果文件一多,要加入几个新的.o文件,那就理不清楚了。 


七、清空目标文件的规则 

每个Makefile中都应该写一个清空目标文件(.o和执行文件)的规则,这不仅便于重编译,也很利于保持文件的清洁。这是一个“修养”(呵呵,还记得我的《编程修养》吗)。一般的风格都是: 

        clean: 
            rm edit $(objects) 

更为稳健的做法是: 

        .PHONY : clean 
        clean : 
                -rm edit $(objects) 

前面说过,.PHONY意思表示clean是一个“伪目标”,。而在rm命令前面加了一个小减号的意思就是,也许某些文件出现问题,但不要管,继续做后面的事。当然,clean的规则不要放在文件的开头,不然,这就会变成make的默认目标,相信谁也不愿意这样。不成文的规矩是——“clean从来都是放在文件的最后”。 


上面就是一个makefile的概貌,也是makefile的基础,下面还有很多makefile的相关细节,准备好了吗?准备好了就来。 

一、Makefile里有什么? 

Makefile里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释。 

1、显式规则。显式规则说明了,如何生成一个或多的的目标文件。这是由Makefile的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令。 

2、隐晦规则。由于我们的make有自动推导的功能,所以隐晦的规则可以让我们比较粗糙地简略地书写Makefile,这是由make所支持的。 

3、变量的定义。在Makefile中我们要定义一系列的变量,变量一般都是字符串,这个有点你C语言中的宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上。 

4、文件指示。其包括了三个部分,一个是在一个Makefile中引用另一个Makefile,就像C语言中的include一样;另一个是指根据某些情况指定Makefile中的有效部分,就像C语言中的预编译#if一样;还有就是定义一个多行的命令。有关这一部分的内容,我会在后续的部分中讲述。 

5、注释。Makefile中只有行注释,和UNIX的Shell脚本一样,其注释是用“#”字符,这个就像C/C++中的“//”一样。如果你要在你的Makefile中使用“#”字符,可以用反斜框进行转义,如:“\#”。 

最后,还值得一提的是,在Makefile中的命令,必须要以[Tab]键开始。 


二、Makefile的文件名 

默认的情况下,make命令会在当前目录下按顺序找寻文件名为“GNUmakefile”、“makefile”、“Makefile”的文件,找到了解释这个文件。在这三个文件名中,最好使用“Makefile”这个文件名,因为,这个文件名第一个字符为大写,这样有一种显目的感觉。最好不要用“GNUmakefile”,这个文件是GNU的make识别的。有另外一些make只对全小写的“makefile”文件名敏感,但是基本上来说,大多数的make都支持“makefile”和“Makefile”这两种默认文件名。 

当然,你可以使用别的文件名来书写Makefile,比如:“Make.Linux”,“Make.Solaris”,“Make.AIX”等,如果要指定特定的Makefile,你可以使用make的“-f”和“--file”参数,如:make -f Make.Linux或make --file Make.AIX。 


三、引用其它的Makefile 

在Makefile使用include关键字可以把别的Makefile包含进来,这很像C语言的#include,被包含的文件会原模原样的放在当前文件的包含位置。include的语法是: 

    include <filename>; 

    filename可以是当前操作系统Shell的文件模式(可以保含路径和通配符) 

在include前面可以有一些空字符,但是绝不能是[Tab]键开始。include和<filename>;可以用一个或多个空格隔开。举个例子,你有这样几个Makefile:a.mkb.mkc.mk,还有一个文件叫foo.make,以及一个变量$(bar),其包含了e.mkf.mk,那么,下面的语句: 

    include foo.make *.mk $(bar) 

    等价于: 

    include foo.make a.mk b.mk c.mk e.mk f.mk 

make命令开始时,会把找寻include所指出的其它Makefile,并把其内容安置在当前的位置。就好像C/C++的#include指令一样。如果文件都没有指定绝对路径或是相对路径的话,make会在当前目录下首先寻找,如果当前目录下没有找到,那么,make还会在下面的几个目录下找: 

    1、如果make执行时,有“-I”或“--include-dir”参数,那么make就会在这个参数所指定的目录下去寻找。 
    2、如果目录<prefix>;/include(一般是:/usr/local/bin或/usr/include)存在的话,make也会去找。 

如果有文件没有找到的话,make会生成一条警告信息,但不会马上出现致命错误。它会继续载入其它的文件,一旦完成makefile的读取,make会再重试这些没有找到,或是不能读取的文件,如果还是不行,make才会出现一条致命信息。如果你想让make不理那些无法读取的文件,而继续执行,你可以在include前加一个减号“-”。如: 

    -include <filename>; 
    其表示,无论include过程中出现什么错误,都不要报错继续执行。和其它版本make兼容的相关命令是sinclude,其作用和这一个是一样的。 


四、环境变量 MAKEFILES  

如果你的当前环境中定义了环境变量MAKEFILES,那么,make会把这个变量中的值做一个类似于include的动作。这个变量中的值是其它的Makefile,用空格分隔。只是,它和include不同的是,从这个环境变中引入的Makefile的“目标”不会起作用,如果环境变量中定义的文件发现错误,make也会不理。 

但是在这里我还是建议不要使用这个环境变量,因为只要这个变量一被定义,那么当你使用make时,所有的Makefile都会受到它的影响,这绝不是你想看到的。在这里提这个事,只是为了告诉大家,也许有时候你的Makefile出现了怪事,那么你可以看看当前环境中有没有定义这个变量。 


五、make的工作方式 

GNU的make工作时的执行步骤入下:(想来其它的make也是类似) 

    1、读入所有的Makefile。 
    2、读入被include的其它Makefile。 
    3、初始化文件中的变量。 
    4、推导隐晦规则,并分析所有规则。 
    5、为所有的目标文件创建依赖关系链。 
    6、根据依赖关系,决定哪些目标要重新生成。 
    7、执行生成命令。 

1-5步为第一个阶段,6-7为第二个阶段。第一个阶段中,如果定义的变量被使用了,那么,make会把其展开在使用的位置。但make并不会完全马上展开,make使用的是拖延战术,如果变量出现在依赖关系的规则中,那么仅当这条依赖被决定要使用了,变量才会在其内部展开。 

当然,这个工作方式你不一定要清楚,但是知道这个方式你也会对make更为熟悉。有了这个基础,后续部分也就容易看懂了。
书写规则 
———— 

规则包含两个部分,一个是依赖关系,一个是生成目标的方法。 

在Makefile中,规则的顺序是很重要的,因为,Makefile中只应该有一个最终目标,其它的目标都是被这个目标所连带出来的,所以一定要让make知道你的最终目标是什么。一般来说,定义在Makefile中的目标可能会有很多,但是第一条规则中的目标将被确立为最终的目标。如果第一条规则中的目标有很多个,那么,第一个目标会成为最终的目标。make所完成的也就是这个目标。 

好了,还是让我们来看一看如何书写规则。 


一、规则举例 

    foo.o : foo.c defs.h       # foo模块 
            cc -c -g foo.c 

看到这个例子,各位应该不是很陌生了,前面也已说过,foo.o是我们的目标,foo.c和defs.h是目标所依赖的源文件,而只有一个命令“cc -c -g foo.c”(以Tab键开头)。这个规则告诉我们两件事: 

    1、文件的依赖关系,foo.o依赖于foo.c和defs.h的文件,如果foo.c和defs.h的文件日期要比foo.o文件日期要新,或是foo.o不存在,那么依赖关系发生。 
    2、如果生成(或更新)foo.o文件。也就是那个cc命令,其说明了,如何生成foo.o这个文件。(当然foo.c文件include了defs.h文件) 


二、规则的语法 

      targets : prerequisites 
        command 
        ... 

      或是这样:  

      targets : prerequisites ; command 
            command 
            ... 

targets是文件名,以空格分开,可以使用通配符。一般来说,我们的目标基本上是一个文件,但也有可能是多个文件。 

command是命令行,如果其不与“target:prerequisites”在一行,那么,必须以[Tab键]开头,如果和prerequisites在一行,那么可以用分号做为分隔。(见上) 

prerequisites也就是目标所依赖的文件(或依赖目标)。如果其中的某个文件要比目标文件要新,那么,目标就被认为是“过时的”,被认为是需要重生成的。这个在前面已经讲过了。 

如果命令太长,你可以使用反斜框(‘\’)作为换行符。make对一行上有多少个字符没有限制。规则告诉make两件事,文件的依赖关系和如何成成目标文件。 

一般来说,make会以UNIX的标准Shell,也就是/bin/sh来执行命令。 


三、在规则中使用通配符 

如果我们想定义一系列比较类似的文件,我们很自然地就想起使用通配符。make支持三各通配符:“*”,“?”和“[...]”。这是和Unix的B-Shell是相同的。 

波浪号(“~”)字符在文件名中也有比较特殊的用途。如果是“~/test”,这就表示当前用户的$HOME目录下的test目录。而“~hchen/test”则表示用户hchen的宿主目录下的test目录。(这些都是Unix下的小知识了,make也支持)而在Windows或是MS-DOS下,用户没有宿主目录,那么波浪号所指的目录则根据环境变量“HOME”而定。 

通配符代替了你一系列的文件,如“*.c”表示所以后缀为c的文件。一个需要我们注意的是,如果我们的文件名中有通配符,如:“*”,那么可以用转义字符“\”,如“\*”来表示真实的“*”字符,而不是任意长度的字符串。 

好吧,还是先来看几个例子吧: 

    clean: 
         rm -f *.o 

    上面这个例子我不不多说了,这是操作系统Shell所支持的通配符。这是在命令中的通配符。 

    print: *.c 
         lpr -p $? 
         touch print 

    上面这个例子说明了通配符也可以在我们的规则中,目标print依赖于所有的[.c]文件。其中的“$?”是一个自动化变量,我会在后面给你讲述。 

    objects = *.o 

    上面这个例子,表示了,通符同样可以用在变量中。并不是说[*.o]会展开,不!objects的值就是“*.o”。Makefile中的变量其实就是C/C++中的宏。如果你要让通配符在变量中展开,也就是让objects的值是所有[.o]的文件名的集合,那么,你可以这样: 

    objects := $(wildcard *.o) 

这种用法由关键字“wildcard”指出,关于Makefile的关键字,我们将在后面讨论。 


四、文件搜寻 

在一些大的工程中,有大量的源文件,我们通常的做法是把这许多的源文件分类,并存放在不同的目录中。所以,当make需要去找寻文件的依赖关系时,你可以在文件前加上路径,但最好的方法是把一个路径告诉make,让make在自动去找。 

Makefile文件中的特殊变量“VPATH”就是完成这个功能的,如果没有指明这个变量,make只会在当前的目录中去找寻依赖文件和目标文件。如果定义了这个变量,那么,make就会在当当前目录找不到的情况下,到所指定的目录中去找寻文件了。 

    VPATH = src:../headers 

上面的的定义指定两个目录,“src”和“../headers”,make会按照这个顺序进行搜索。目录由“冒号”分隔。(当然,当前目录永远是最高优先搜索的地方) 

另一个设置文件搜索路径的方法是使用make的“vpath”关键字(注意,它是全小写的),这不是变量,这是一个make的关键字,这和上面提到的那个VPATH变量很类似,但是它更为灵活。它可以指定不同的文件在不同的搜索目录中。这是一个很灵活的功能。它的使用方法有三种: 

    1、vpath <pattern>; <directories>; 

    为符合模式<pattern>;的文件指定搜索目录<directories>;。 

    2、vpath <pattern>; 

    清除符合模式<pattern>;的文件的搜索目录。 

    3、vpath 

    清除所有已被设置好了的文件搜索目录。 

vapth使用方法中的<pattern>;需要包含“%”字符。“%”的意思是匹配零或若干字符,例如,“%.h”表示所有以“.h”结尾的文件。<pattern>;指定了要搜索的文件集,而<directories>;则指定了<pattern>;的文件集的搜索的目录。例如: 

    vpath %.h ../headers 

该语句表示,要求make在“../headers”目录下搜索所有以“.h”结尾的文件。(如果某文件在当前目录没有找到的话) 

我们可以连续地使用vpath语句,以指定不同搜索策略。如果连续的vpath语句中出现了相同的<pattern>;,或是被重复了的<pattern>;,那么,make会按照vpath语句的先后顺序来执行搜索。如: 

    vpath %.c foo 
    vpath %   blish 
    vpath %.c bar 

其表示“.c”结尾的文件,先在“foo”目录,然后是“blish”,最后是“bar”目录。 

    vpath %.c foo:bar 
    vpath %   blish 

而上面的语句则表示“.c”结尾的文件,先在“foo”目录,然后是“bar”目录,最后才是“blish”目录。 


五、伪目标 

最早先的一个例子中,我们提到过一个“clean”的目标,这是一个“伪目标”, 

    clean: 
            rm *.o temp 

正像我们前面例子中的“clean”一样,即然我们生成了许多文件编译文件,我们也应该提供一个清除它们的“目标”以备完整地重编译而用。 (以“make clean”来使用该目标) 

因为,我们并不生成“clean”这个文件。“伪目标”并不是一个文件,只是一个标签,由于“伪目标”不是文件,所以make无法生成它的依赖关系和决定它是否要执行。我们只有通过显示地指明这个“目标”才能让其生效。当然,“伪目标”的取名不能和文件名重名,不然其就失去了“伪目标”的意义了。 

当然,为了避免和文件重名的这种情况,我们可以使用一个特殊的标记“.PHONY”来显示地指明一个目标是“伪目标”,向make说明,不管是否有这个文件,这个目标就是“伪目标”。 

    .PHONY : clean 

只要有这个声明,不管是否有“clean”文件,要运行“clean”这个目标,只有“make clean”这样。于是整个过程可以这样写: 

     .PHONY: clean 
    clean: 
            rm *.o temp 

伪目标一般没有依赖的文件。但是,我们也可以为伪目标指定所依赖的文件。伪目标同样可以作为“默认目标”,只要将其放在第一个。一个示例就是,如果你的Makefile需要一口气生成若干个可执行文件,但你只想简单地敲一个make完事,并且,所有的目标文件都写在一个Makefile中,那么你可以使用“伪目标”这个特性: 

    all : prog1 prog2 prog3 
    .PHONY : all 

    prog1 : prog1.o utils.o 
            cc -o prog1 prog1.o utils.o 

    prog2 : prog2.o 
            cc -o prog2 prog2.o 

    prog3 : prog3.o sort.o utils.o 
            cc -o prog3 prog3.o sort.o utils.o 

我们知道,Makefile中的第一个目标会被作为其默认目标。我们声明了一个“all”的伪目标,其依赖于其它三个目标。由于伪目标的特性是,总是被执行的,所以其依赖的那三个目标就总是不如“all”这个目标新。所以,其它三个目标的规则总是会被决议。也就达到了我们一口气生成多个目标的目的。“.PHONY : all”声明了“all”这个目标为“伪目标”。 

随便提一句,从上面的例子我们可以看出,目标也可以成为依赖。所以,伪目标同样也可成为依赖。看下面的例子: 

    .PHONY: cleanall cleanobj cleandiff 

    cleanall : cleanobj cleandiff 
            rm program 

    cleanobj : 
            rm *.o 

    cleandiff : 
            rm *.diff 

“make clean”将清除所有要被清除的文件。“cleanobj”和“cleandiff”这两个伪目标有点像“子程序”的意思。我们可以输入“make cleanall”和“make cleanobj”和“make cleandiff”命令来达到清除不同种类文件的目的。
六、多目标 

Makefile的规则中的目标可以不止一个,其支持多目标,有可能我们的多个目标同时依赖于一个文件,并且其生成的命令大体类似。于是我们就能把其合并起来。当然,多个目标的生成规则的执行命令是同一个,这可能会可我们带来麻烦,不过好在我们的可以使用一个自动化变量“$@”(关于自动化变量,将在后面讲述),这个变量表示着目前规则中所有的目标的集合,这样说可能很抽象,还是看一个例子吧。 

    bigoutput littleoutput : text.g 
            generate text.g -$(subst output,,$@) >; $@ 

    上述规则等价于: 

    bigoutput : text.g 
            generate text.g -big >; bigoutput 
    littleoutput : text.g 
            generate text.g -little >; littleoutput 

    其中,-$(subst output,,$@)中的“$”表示执行一个Makefile的函数,函数名为subst,后面的为参数。关于函数,将在后面讲述。这里的这个函数是截取字符串的意思,“$@”表示目标的集合,就像一个数组,“$@”依次取出目标,并执于命令。 


七、静态模式 

静态模式可以更加容易地定义多目标的规则,可以让我们的规则变得更加的有弹性和灵活。我们还是先来看一下语法: 

    <targets ...>;: <target-pattern>;: <prereq-patterns ...>; 
            <commands>; 
            ... 


    targets定义了一系列的目标文件,可以有通配符。是目标的一个集合。 

    target-parrtern是指明了targets的模式,也就是的目标集模式。 

    prereq-parrterns是目标的依赖模式,它对target-parrtern形成的模式再进行一次依赖目标的定义。 

这样描述这三个东西,可能还是没有说清楚,还是举个例子来说明一下吧。如果我们的<target-parrtern>;定义成“%.o”,意思是我们的<target>;集合中都是以“.o”结尾的,而如果我们的<prereq-parrterns>;定义成“%.c”,意思是对<target-parrtern>;所形成的目标集进行二次定义,其计算方法是,取<target-parrtern>;模式中的“%”(也就是去掉了[.o]这个结尾),并为其加上[.c]这个结尾,形成的新集合。 

所以,我们的“目标模式”或是“依赖模式”中都应该有“%”这个字符,如果你的文件名中有“%”那么你可以使用反斜杠“\”进行转义,来标明真实的“%”字符。 

看一个例子: 

    objects = foo.o bar.o 

    all: $(objects) 

    $(objects): %.o: %.c 
            $(CC) -c $(CFLAGS) $< -o $@ 


上面的例子中,指明了我们的目标从$object中获取,“%.o”表明要所有以“.o”结尾的目标,也就是“foo.o bar.o”,也就是变量$object集合的模式,而依赖模式“%.c”则取模式“%.o”的“%”,也就是“foo bar”,并为其加下“.c”的后缀,于是,我们的依赖目标就是“foo.c bar.c”。而命令中的“$<”和“$@”则是自动化变量,“$<”表示所有的依赖目标集(也就是“foo.c bar.c”),“$@”表示目标集(也就是“foo.o bar.o”)。于是,上面的规则展开后等价于下面的规则: 

    foo.o : foo.c 
            $(CC) -c $(CFLAGS) foo.c -o foo.o 
    bar.o : bar.c 
            $(CC) -c $(CFLAGS) bar.c -o bar.o 

试想,如果我们的“%.o”有几百个,那种我们只要用这种很简单的“静态模式规则”就可以写完一堆规则,实在是太有效率了。“静态模式规则”的用法很灵活,如果用得好,那会一个很强大的功能。再看一个例子: 


    files = foo.elc bar.o lose.o 

    $(filter %.o,$(files)): %.o: %.c 
            $(CC) -c $(CFLAGS) $< -o $@ 
    $(filter %.elc,$(files)): %.elc: %.el 
            emacs -f batch-byte-compile $< 


$(filter %.o,$(files))表示调用Makefile的filter函数,过滤“$filter”集,只要其中模式为“%.o”的内容。其的它内容,我就不用多说了吧。这个例字展示了Makefile中更大的弹性。 


八、自动生成依赖性 

在Makefile中,我们的依赖关系可能会需要包含一系列的头文件,比如,如果我们的main.c中有一句“#include "defs.h"”,那么我们的依赖关系应该是: 

    main.o : main.c defs.h 

但是,如果是一个比较大型的工程,你必需清楚哪些C文件包含了哪些头文件,并且,你在加入或删除头文件时,也需要小心地修改Makefile,这是一个很没有维护性的工作。为了避免这种繁重而又容易出错的事情,我们可以使用C/C++编译的一个功能。大多数的C/C++编译器都支持一个“-M”的选项,即自动找寻源文件中包含的头文件,并生成一个依赖关系。例如,如果我们执行下面的命令: 

    cc -M main.c 

其输出是: 

    main.o : main.c defs.h 

于是由编译器自动生成的依赖关系,这样一来,你就不必再手动书写若干文件的依赖关系,而由编译器自动生成了。需要提醒一句的是,如果你使用GNU的C/C++编译器,你得用“-MM”参数,不然,“-M”参数会把一些标准库的头文件也包含进来。 

    gcc -M main.c的输出是: 

    main.o: main.c defs.h /usr/include/stdio.h /usr/include/features.h \ 
         /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h \ 
         /usr/lib/gcc-lib/i486-suse-linux/2.95.3/include/stddef.h \ 
         /usr/include/bits/types.h /usr/include/bits/pthreadtypes.h \ 
         /usr/include/bits/sched.h /usr/include/libio.h \ 
         /usr/include/_G_config.h /usr/include/wchar.h \ 
         /usr/include/bits/wchar.h /usr/include/gconv.h \ 
         /usr/lib/gcc-lib/i486-suse-linux/2.95.3/include/stdarg.h \ 
         /usr/include/bits/stdio_lim.h 


    gcc -MM main.c的输出则是: 

    main.o: main.c defs.h 

那么,编译器的这个功能如何与我们的Makefile联系在一起呢。因为这样一来,我们的Makefile也要根据这些源文件重新生成,让Makefile自已依赖于源文件?这个功能并不现实,不过我们可以有其它手段来迂回地实现这一功能。GNU组织建议把编译器为每一个源文件的自动生成的依赖关系放到一个文件中,为每一个“name.c”的文件都生成一个“name.d”的Makefile文件,[.d]文件中就存放对应[.c]文件的依赖关系。 

于是,我们可以写出[.c]文件和[.d]文件的依赖关系,并让make自动更新或自成[.d]文件,并把其包含在我们的主Makefile中,这样,我们就可以自动化地生成每个文件的依赖关系了。 

这里,我们给出了一个模式规则来产生[.d]文件: 

    %.d: %.c 
            @set -e; rm -f $@; \ 
             $(CC) -M $(CPPFLAGS) $< >; $@.$$$$; \ 
             sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ >; $@; \ 
             rm -f $@.$$$$ 


这个规则的意思是,所有的[.d]文件依赖于[.c]文件,“rm -f $@”的意思是删除所有的目标,也就是[.d]文件,第二行的意思是,为每个依赖文件“$<”,也就是[.c]文件生成依赖文件,“$@”表示模式“%.d”文件,如果有一个C文件是name.c,那么“%”就是“name”,“$$$$”意为一个随机编号,第二行生成的文件有可能是“name.d.12345”,第三行使用sed命令做了一个替换,关于sed命令的用法请参看相关的使用文档。第四行就是删除临时文件。 

总而言之,这个模式要做的事就是在编译器生成的依赖关系中加入[.d]文件的依赖,即把依赖关系: 

    main.o : main.c defs.h 

转成: 

    main.o main.d : main.c defs.h 

于是,我们的[.d]文件也会自动更新了,并会自动生成了,当然,你还可以在这个[.d]文件中加入的不只是依赖关系,包括生成的命令也可一并加入,让每个[.d]文件都包含一个完赖的规则。一旦我们完成这个工作,接下来,我们就要把这些自动生成的规则放进我们的主Makefile中。我们可以使用Makefile的“include”命令,来引入别的Makefile文件(前面讲过),例如: 

    sources = foo.c bar.c 

    include $(sources:.c=.d) 

上述语句中的“$(sources:.c=.d)”中的“.c=.d”的意思是做一个替换,把变量$(sources)所有[.c]的字串都替换成[.d],关于这个“替换”的内容,在后面我会有更为详细的讲述。当然,你得注意次序,因为include是按次来载入文件,最先载入的[.d]文件中的目标会成为默认目标。

书写命令 
———— 

每条规则中的命令和操作系统Shell的命令行是一致的。make会一按顺序一条一条的执行命令,每条命令的开头必须以[Tab]键开头,除非,命令是紧跟在依赖规则后面的分号后的。在命令行之间中的空格或是空行会被忽略,但是如果该空格或空行是以Tab键开头的,那么make会认为其是一个空命令。 

我们在UNIX下可能会使用不同的Shell,但是make的命令默认是被“/bin/sh”——UNIX的标准Shell解释执行的。除非你特别指定一个其它的Shell。Makefile中,“#”是注释符,很像C/C++中的“//”,其后的本行字符都被注释。 

一、显示命令 

通常,make会把其要执行的命令行在命令执行前输出到屏幕上。当我们用“@”字符在命令行前,那么,这个命令将不被make显示出来,最具代表性的例子是,我们用这个功能来像屏幕显示一些信息。如: 

    @echo 正在编译XXX模块...... 

当make执行时,会输出“正在编译XXX模块......”字串,但不会输出命令,如果没有“@”,那么,make将输出: 

    echo 正在编译XXX模块...... 
    正在编译XXX模块...... 

如果make执行时,带入make参数“-n”或“--just-print”,那么其只是显示命令,但不会执行命令,这个功能很有利于我们调试我们的Makefile,看看我们书写的命令是执行起来是什么样子的或是什么顺序的。 

而make参数“-s”或“--slient”则是全面禁止命令的显示。 

  

二、命令执行 

当依赖目标新于目标时,也就是当规则的目标需要被更新时,make会一条一条的执行其后的命令。需要注意的是,如果你要让上一条命令的结果应用在下一条命令时,你应该使用分号分隔这两条命令。比如你的第一条命令是cd命令,你希望第二条命令得在cd之后的基础上运行,那么你就不能把这两条命令写在两行上,而应该把这两条命令写在一行上,用分号分隔。如: 

    示例一: 
        exec: 
                cd /home/hchen 
                pwd 

    示例二: 
        exec: 
                cd /home/hchen; pwd 

当我们执行“make exec”时,第一个例子中的cd没有作用,pwd会打印出当前的Makefile目录,而第二个例子中,cd就起作用了,pwd会打印出“/home/hchen”。 

make一般是使用环境变量SHELL中所定义的系统Shell来执行命令,默认情况下使用UNIX的标准Shell——/bin/sh来执行命令。但在MS-DOS下有点特殊,因为MS-DOS下没有SHELL环境变量,当然你也可以指定。如果你指定了UNIX风格的目录形式,首先,make会在SHELL所指定的路径中找寻命令解释器,如果找不到,其会在当前盘符中的当前目录中寻找,如果再找不到,其会在PATH环境变量中所定义的所有路径中寻找。MS-DOS中,如果你定义的命令解释器没有找到,其会给你的命令解释器加上诸如“.exe”、“.com”、“.bat”、“.sh”等后缀。 



三、命令出错 

每当命令运行完后,make会检测每个命令的返回码,如果命令返回成功,那么make会执行下一条命令,当规则中所有的命令成功返回后,这个规则就算是成功完成了。如果一个规则中的某个命令出错了(命令退出码非零),那么make就会终止执行当前规则,这将有可能终止所有规则的执行。 

有些时候,命令的出错并不表示就是错误的。例如mkdir命令,我们一定需要建立一个目录,如果目录不存在,那么mkdir就成功执行,万事大吉,如果目录存在,那么就出错了。我们之所以使用mkdir的意思就是一定要有这样的一个目录,于是我们就不希望mkdir出错而终止规则的运行。 

为了做到这一点,忽略命令的出错,我们可以在Makefile的命令行前加一个减号“-”(在Tab键之后),标记为不管命令出不出错都认为是成功的。如: 

   clean: 
            -rm -f *.o 

还有一个全局的办法是,给make加上“-i”或是“--ignore-errors”参数,那么,Makefile中所有命令都会忽略错误。而如果一个规则是以“.IGNORE”作为目标的,那么这个规则中的所有命令将会忽略错误。这些是不同级别的防止命令出错的方法,你可以根据你的不同喜欢设置。 

还有一个要提一下的make的参数的是“-k”或是“--keep-going”,这个参数的意思是,如果某规则中的命令出错了,那么就终目该规则的执行,但继续执行其它规则。 



四、嵌套执行make 

在一些大的工程中,我们会把我们不同模块或是不同功能的源文件放在不同的目录中,我们可以在每个目录中都书写一个该目录的Makefile,这有利于让我们的Makefile变得更加地简洁,而不至于把所有的东西全部写在一个Makefile中,这样会很难维护我们的Makefile,这个技术对于我们模块编译和分段编译有着非常大的好处。 

例如,我们有一个子目录叫subdir,这个目录下有个Makefile文件,来指明了这个目录下文件的编译规则。那么我们总控的Makefile可以这样书写: 

    subsystem: 
            cd subdir && $(MAKE) 

其等价于: 

    subsystem: 
            $(MAKE) -C subdir 

定义$(MAKE)宏变量的意思是,也许我们的make需要一些参数,所以定义成一个变量比较利于维护。这两个例子的意思都是先进入“subdir”目录,然后执行make命令。 

我们把这个Makefile叫做“总控Makefile”,总控Makefile的变量可以传递到下级的Makefile中(如果你显示的声明),但是不会覆盖下层的Makefile中所定义的变量,除非指定了“-e”参数。 

如果你要传递变量到下级Makefile中,那么你可以使用这样的声明: 

    export <variable ...>; 

如果你不想让某些变量传递到下级Makefile中,那么你可以这样声明:  

    unexport <variable ...>; 

如: 
     
    示例一: 

        export variable = value 

        其等价于: 

        variable = value 
        export variable 

        其等价于: 

        export variable := value 

        其等价于: 

        variable := value 
        export variable 

    示例二: 

        export variable += value 

        其等价于: 

        variable += value 
        export variable 

如果你要传递所有的变量,那么,只要一个export就行了。后面什么也不用跟,表示传递所有的变量。 

需要注意的是,有两个变量,一个是SHELL,一个是MAKEFLAGS,这两个变量不管你是否export,其总是要传递到下层Makefile中,特别是MAKEFILES变量,其中包含了make的参数信息,如果我们执行“总控Makefile”时有make参数或是在上层Makefile中定义了这个变量,那么MAKEFILES变量将会是这些参数,并会传递到下层Makefile中,这是一个系统级的环境变量。 

但是make命令中的有几个参数并不往下传递,它们是“-C”,“-f”,“-h”“-o”和“-W”(有关Makefile参数的细节将在后面说明),如果你不想往下层传递参数,那么,你可以这样来: 

    subsystem: 
            cd subdir && $(MAKE) MAKEFLAGS= 

如果你定义了环境变量MAKEFLAGS,那么你得确信其中的选项是大家都会用到的,如果其中有“-t”,“-n”,和“-q”参数,那么将会有让你意想不到的结果,或许会让你异常地恐慌。 

还有一个在“嵌套执行”中比较有用的参数,“-w”或是“--print-directory”会在make的过程中输出一些信息,让你看到目前的工作目录。比如,如果我们的下级make目录是“/home/hchen/gnu/make”,如果我们使用“make -w”来执行,那么当进入该目录时,我们会看到: 

    make: Entering directory `/home/hchen/gnu/make'. 

而在完成下层make后离开目录时,我们会看到: 

    make: Leaving directory `/home/hchen/gnu/make' 

当你使用“-C”参数来指定make下层Makefile时,“-w”会被自动打开的。如果参数中有“-s”(“--slient”)或是“--no-print-directory”,那么,“-w”总是失效的。 



五、定义命令包 

如果Makefile中出现一些相同命令序列,那么我们可以为这些相同的命令序列定义一个变量。定义这种命令序列的语法以“define”开始,以“endef”结束,如: 

    define run-yacc 
    yacc $(firstword $^) 
    mv y.tab.c $@ 
    endef 

这里,“run-yacc”是这个命令包的名字,其不要和Makefile中的变量重名。在“define”和“endef”中的两行就是命令序列。这个命令包中的第一个命令是运行Yacc程序,因为Yacc程序总是生成“y.tab.c”的文件,所以第二行的命令就是把这个文件改改名字。还是把这个命令包放到一个示例中来看看吧。 

    foo.c : foo.y 
            $(run-yacc) 

我们可以看见,要使用这个命令包,我们就好像使用变量一样。在这个命令包的使用中,命令包“run-yacc”中的“$^”就是“foo.y”,“$@”就是“foo.c”(有关这种以“$”开头的特殊变量,我们会在后面介绍),make在执行命令包时,命令包中的每个命令会被依次独立执行。

使用变量 
———— 

在Makefile中的定义的变量,就像是C/C++语言中的宏一样,他代表了一个文本字串,在Makefile中执行的时候其会自动原模原样地展开在所使用的地方。其与C/C++所不同的是,你可以在Makefile中改变其值。在Makefile中,变量可以使用在“目标”,“依赖目标”,“命令”或是Makefile的其它部分中。 

变量的命名字可以包含字符、数字,下划线(可以是数字开头),但不应该含有“:”、“#”、“=”或是空字符(空格、回车等)。变量是大小写敏感的,“foo”、“Foo”和“FOO”是三个不同的变量名。传统的Makefile的变量名是全大写的命名方式,但我推荐使用大小写搭配的变量名,如:MakeFlags。这样可以避免和系统的变量冲突,而发生意外的事情。 

有一些变量是很奇怪字串,如“$<”、“$@”等,这些是自动化变量,我会在后面介绍。 

一、变量的基础 

变量在声明时需要给予初值,而在使用时,需要给在变量名前加上“$”符号,但最好用小括号“()”或是大括号“{}”把变量给包括起来。如果你要使用真实的“$”字符,那么你需要用“$$”来表示。 

变量可以使用在许多地方,如规则中的“目标”、“依赖”、“命令”以及新的变量中。先看一个例子: 

    objects = program.o foo.o utils.o 
    program : $(objects) 
            cc -o program $(objects) 

    $(objects) : defs.h 

变量会在使用它的地方精确地展开,就像C/C++中的宏一样,例如: 

    foo = c 
    prog.o : prog.$(foo) 
            $(foo)$(foo) -$(foo) prog.$(foo) 

展开后得到: 

    prog.o : prog.c 
            cc -c prog.c 

当然,千万不要在你的Makefile中这样干,这里只是举个例子来表明Makefile中的变量在使用处展开的真实样子。可见其就是一个“替代”的原理。 

另外,给变量加上括号完全是为了更加安全地使用这个变量,在上面的例子中,如果你不想给变量加上括号,那也可以,但我还是强烈建议你给变量加上括号。 


二、变量中的变量 

在定义变量的值时,我们可以使用其它变量来构造变量的值,在Makefile中有两种方式来在用变量定义变量的值。 

先看第一种方式,也就是简单的使用“=”号,在“=”左侧是变量,右侧是变量的值,右侧变量的值可以定义在文件的任何一处,也就是说,右侧中的变量不一定非要是已定义好的值,其也可以使用后面定义的值。如: 

    foo = $(bar) 
    bar = $(ugh) 
    ugh = Huh? 

    all: 
            echo $(foo) 

我们执行“make all”将会打出变量$(foo)的值是“Huh?”( $(foo)的值是$(bar),$(bar)的值是$(ugh),$(ugh)的值是“Huh?”)可见,变量是可以使用后面的变量来定义的。 

这个功能有好的地方,也有不好的地方,好的地方是,我们可以把变量的真实值推到后面来定义,如: 

    CFLAGS = $(include_dirs) -O 
    include_dirs = -Ifoo -Ibar 

当“CFLAGS”在命令中被展开时,会是“-Ifoo -Ibar -O”。但这种形式也有不好的地方,那就是递归定义,如: 

    CFLAGS = $(CFLAGS) -O 

    或: 

    A = $(B) 
    B = $(A) 

这会让make陷入无限的变量展开过程中去,当然,我们的make是有能力检测这样的定义,并会报错。还有就是如果在变量中使用函数,那么,这种方式会让我们的make运行时非常慢,更糟糕的是,他会使用得两个make的函数“wildcard”和“shell”发生不可预知的错误。因为你不会知道这两个函数会被调用多少次。 

为了避免上面的这种方法,我们可以使用make中的另一种用变量来定义变量的方法。这种方法使用的是“:=”操作符,如: 

    x := foo 
    y := $(x) bar 
    x := later 

其等价于: 

    y := foo bar 
    x := later 

值得一提的是,这种方法,前面的变量不能使用后面的变量,只能使用前面已定义好了的变量。如果是这样: 

    y := $(x) bar 
    x := foo 

那么,y的值是“bar”,而不是“foo bar”。 

上面都是一些比较简单的变量使用了,让我们来看一个复杂的例子,其中包括了make的函数、条件表达式和一个系统变量“MAKELEVEL”的使用: 

    ifeq (0,${MAKELEVEL}) 
    cur-dir   := $(shell pwd) 
    whoami    := $(shell whoami) 
    host-type := $(shell arch) 
    MAKE := ${MAKE} host-type=${host-type} whoami=${whoami} 
    endif 

关于条件表达式和函数,我们在后面再说,对于系统变量“MAKELEVEL”,其意思是,如果我们的make有一个嵌套执行的动作(参见前面的“嵌套使用make”),那么,这个变量会记录了我们的当前Makefile的调用层数。 

下面再介绍两个定义变量时我们需要知道的,请先看一个例子,如果我们要定义一个变量,其值是一个空格,那么我们可以这样来: 

    nullstring := 
    space := $(nullstring) # end of the line 

nullstring是一个Empty变量,其中什么也没有,而我们的space的值是一个空格。因为在操作符的右边是很难描述一个空格的,这里采用的技术很管用,先用一个Empty变量来标明变量的值开始了,而后面采用“#”注释符来表示变量定义的终止,这样,我们可以定义出其值是一个空格的变量。请注意这里关于“#”的使用,注释符“#”的这种特性值得我们注意,如果我们这样定义一个变量: 

    dir := /foo/bar    # directory to put the frobs in 

dir这个变量的值是“/foo/bar”,后面还跟了4个空格,如果我们这样使用这样变量来指定别的目录——“$(dir)/file”那么就完蛋了。 

还有一个比较有用的操作符是“?=”,先看示例: 

    FOO ?= bar 

其含义是,如果FOO没有被定义过,那么变量FOO的值就是“bar”,如果FOO先前被定义过,那么这条语将什么也不做,其等价于: 

    ifeq ($(origin FOO), undefined) 
      FOO = bar 
    endif 


三、变量高级用法 

这里介绍两种变量的高级使用方法,第一种是变量值的替换。 

我们可以替换变量中的共有的部分,其格式是“$(var:a=b)”或是“${var:a=b}”,其意思是,把变量“var”中所有以“a”字串“结尾”的“a”替换成“b”字串。这里的“结尾”意思是“空格”或是“结束符”。 

还是看一个示例吧: 

    foo := a.o b.o c.o 
    bar := $(foo:.o=.c) 

这个示例中,我们先定义了一个“$(foo)”变量,而第二行的意思是把“$(foo)”中所有以“.o”字串“结尾”全部替换成“.c”,所以我们的“$(bar)”的值就是“a.c b.c c.c”。 

另外一种变量替换的技术是以“静态模式”(参见前面章节)定义的,如: 

    foo := a.o b.o c.o 
    bar := $(foo:%.o=%.c) 

这依赖于被替换字串中的有相同的模式,模式中必须包含一个“%”字符,这个例子同样让$(bar)变量的值为“a.c b.c c.c”。  

第二种高级用法是——“把变量的值再当成变量”。先看一个例子: 

    x = y 
    y = z 
    a := $($(x)) 

在这个例子中,$(x)的值是“y”,所以$($(x))就是$(y),于是$(a)的值就是“z”。(注意,是“x=y”,而不是“x=$(y)”) 

我们还可以使用更多的层次: 

    x = y 
    y = z 
    z = u 
    a := $($($(x))) 

这里的$(a)的值是“u”,相关的推导留给读者自己去做吧。 

让我们再复杂一点,使用上“在变量定义中使用变量”的第一个方式,来看一个例子: 

    x = $(y) 
    y = z 
    z = Hello 
    a := $($(x)) 

这里的$($(x))被替换成了$($(y)),因为$(y)值是“z”,所以,最终结果是:a:=$(z),也就是“Hello”。 

再复杂一点,我们再加上函数: 

    x = variable1 
    variable2 := Hello 
    y = $(subst 1,2,$(x)) 
    z = y 
    a := $($($(z))) 

这个例子中,“$($($(z)))”扩展为“$($(y))”,而其再次被扩展为“$($(subst 1,2,$(x)))”。$(x)的值是“variable1”,subst函数把“variable1”中的所有“1”字串替换成“2”字串,于是,“variable1”变成“variable2”,再取其值,所以,最终,$(a)的值就是$(variable2)的值——“Hello”。(喔,好不容易) 

在这种方式中,或要可以使用多个变量来组成一个变量的名字,然后再取其值: 

    first_second = Hello 
    a = first 
    b = second 
    all = $($a_$b) 

这里的“$a_$b”组成了“first_second”,于是,$(all)的值就是“Hello”。 

再来看看结合第一种技术的例子: 

    a_objects := a.o b.o c.o 
    1_objects := 1.o 2.o 3.o 

    sources := $($(a1)_objects:.o=.c) 

这个例子中,如果$(a1)的值是“a”的话,那么,$(sources)的值就是“a.c b.c c.c”;如果$(a1)的值是“1”,那么$(sources)的值是“1.c 2.c 3.c”。 

再来看一个这种技术和“函数”与“条件语句”一同使用的例子: 

    ifdef do_sort 
    func := sort 
    else 
    func := strip 
    endif 

    bar := a d b g q c 

    foo := $($(func) $(bar)) 

这个示例中,如果定义了“do_sort”,那么:foo := $(sort a d b g q c),于是$(foo)的值就是“a b c d g q”,而如果没有定义“do_sort”,那么:foo := $(sort a d b g q c),调用的就是strip函数。 

当然,“把变量的值再当成变量”这种技术,同样可以用在操作符的左边: 

    dir = foo 
    $(dir)_sources := $(wildcard $(dir)/*.c) 
    define $(dir)_print 
    lpr $($(dir)_sources) 
    endef 

这个例子中定义了三个变量:“dir”,“foo_sources”和“foo_print”。 


四、追加变量值 

我们可以使用“+=”操作符给变量追加值,如: 

    objects = main.o foo.o bar.o utils.o 
    objects += another.o 

于是,我们的$(objects)值变成:“main.o foo.o bar.o utils.o another.o”(another.o被追加进去了) 

使用“+=”操作符,可以模拟为下面的这种例子: 

    objects = main.o foo.o bar.o utils.o 
    objects := $(objects) another.o 

所不同的是,用“+=”更为简洁。 

如果变量之前没有定义过,那么,“+=”会自动变成“=”,如果前面有变量定义,那么“+=”会继承于前次操作的赋值符。如果前一次的是“:=”,那么“+=”会以“:=”作为其赋值符,如: 

    variable := value 
    variable += more 

等价于: 

    variable := value 
    variable := $(variable) more 

但如果是这种情况:  

    variable = value 
    variable += more 

由于前次的赋值符是“=”,所以“+=”也会以“=”来做为赋值,那么岂不会发生变量的递补归定义,这是很不好的,所以make会自动为我们解决这个问题,我们不必担心这个问题。 


五、override 指示符 

如果有变量是通常make的命令行参数设置的,那么Makefile中对这个变量的赋值会被忽略。如果你想在Makefile中设置这类参数的值,那么,你可以使用“override”指示符。其语法是: 

    override <variable>; = <value>; 

    override <variable>; := <value>; 

当然,你还可以追加: 

    override <variable>; += <more text>; 

对于多行的变量定义,我们用define指示符,在define指示符前,也同样可以使用ovveride指示符,如: 

    override define foo 
    bar 
    endef

六、多行变量 
  
还有一种设置变量值的方法是使用define关键字。使用define关键字设置变量的值可以有换行,这有利于定义一系列的命令(前面我们讲过“命令包”的技术就是利用这个关键字)。 

define指示符后面跟的是变量的名字,而重起一行定义变量的值,定义是以endef关键字结束。其工作方式和“=”操作符一样。变量的值可以包含函数、命令、文字,或是其它变量。因为命令需要以[Tab]键开头,所以如果你用define定义的命令变量中没有以[Tab]键开头,那么make就不会把其认为是命令。 

下面的这个示例展示了define的用法: 

    define two-lines 
    echo foo 
    echo $(bar) 
    endef 


七、环境变量 

make运行时的系统环境变量可以在make开始运行时被载入到Makefile文件中,但是如果Makefile中已定义了这个变量,或是这个变量由make命令行带入,那么系统的环境变量的值将被覆盖。(如果make指定了“-e”参数,那么,系统环境变量将覆盖Makefile中定义的变量) 

因此,如果我们在环境变量中设置了“CFLAGS”环境变量,那么我们就可以在所有的Makefile中使用这个变量了。这对于我们使用统一的编译参数有比较大的好处。如果Makefile中定义了CFLAGS,那么则会使用Makefile中的这个变量,如果没有定义则使用系统环境变量的值,一个共性和个性的统一,很像“全局变量”和“局部变量”的特性。 

当make嵌套调用时(参见前面的“嵌套调用”章节),上层Makefile中定义的变量会以系统环境变量的方式传递到下层的Makefile中。当然,默认情况下,只有通过命令行设置的变量会被传递。而定义在文件中的变量,如果要向下层Makefile传递,则需要使用exprot关键字来声明。(参见前面章节) 

当然,我并不推荐把许多的变量都定义在系统环境中,这样,在我们执行不用的Makefile时,拥有的是同一套系统变量,这可能会带来更多的麻烦。 


八、目标变量 

前面我们所讲的在Makefile中定义的变量都是“全局变量”,在整个文件,我们都可以访问这些变量。当然,“自动化变量”除外,如“$<”等这种类量的自动化变量就属于“规则型变量”,这种变量的值依赖于规则的目标和依赖目标的定义。 

当然,我样同样可以为某个目标设置局部变量,这种变量被称为“Target-specific Variable”,它可以和“全局变量”同名,因为它的作用范围只在这条规则以及连带规则中,所以其值也只在作用范围内有效。而不会影响规则链以外的全局变量的值。 

其语法是: 

    <target ...>; : <variable-assignment>; 

    <target ...>; : overide <variable-assignment>; 

<variable-assignment>;可以是前面讲过的各种赋值表达式,如“=”、“:=”、“+=”或是“?=”。第二个语法是针对于make命令行带入的变量,或是系统环境变量。 

这个特性非常的有用,当我们设置了这样一个变量,这个变量会作用到由这个目标所引发的所有的规则中去。如:

    prog : CFLAGS = -g 
    prog : prog.o foo.o bar.o 
            $(CC) $(CFLAGS) prog.o foo.o bar.o 

    prog.o : prog.c 
            $(CC) $(CFLAGS) prog.c 

    foo.o : foo.c 
            $(CC) $(CFLAGS) foo.c 

    bar.o : bar.c 
            $(CC) $(CFLAGS) bar.c 
  
在这个示例中,不管全局的$(CFLAGS)的值是什么,在prog目标,以及其所引发的所有规则中(prog.o foo.o bar.o的规则),$(CFLAGS)的值都是“-g” 


九、模式变量 

在GNU的make中,还支持模式变量(Pattern-specific Variable),通过上面的目标变量中,我们知道,变量可以定义在某个目标上。模式变量的好处就是,我们可以给定一种“模式”,可以把变量定义在符合这种模式的所有目标上。 

我们知道,make的“模式”一般是至少含有一个“%”的,所以,我们可以以如下方式给所有以[.o]结尾的目标定义目标变量: 

    %.o : CFLAGS = -O 

同样,模式变量的语法和“目标变量”一样: 

    <pattern ...>; : <variable-assignment>; 

    <pattern ...>; : override <variable-assignment>; 

override同样是针对于系统环境传入的变量,或是make命令行指定的变量。 
  


使用条件判断 
—————— 

使用条件判断,可以让make根据运行时的不同情况选择不同的执行分支。条件表达式可以是比较变量的值,或是比较变量和常量的值。 

一、示例 

下面的例子,判断$(CC)变量是否“gcc”,如果是的话,则使用GNU函数编译目标。 

    libs_for_gcc = -lgnu 
    normal_libs = 

    foo: $(objects) 
    ifeq ($(CC),gcc) 
            $(CC) -o foo $(objects) $(libs_for_gcc) 
    else 
            $(CC) -o foo $(objects) $(normal_libs) 
    endif 

可见,在上面示例的这个规则中,目标“foo”可以根据变量“$(CC)”值来选取不同的函数库来编译程序。 

我们可以从上面的示例中看到三个关键字:ifeq、else和endif。ifeq的意思表示条件语句的开始,并指定一个条件表达式,表达式包含两个参数,以逗号分隔,表达式以圆括号括起。else表示条件表达式为假的情况。endif表示一个条件语句的结束,任何一个条件表达式都应该以endif结束。 

当我们的变量$(CC)值是“gcc”时,目标foo的规则是: 

    foo: $(objects) 
            $(CC) -o foo $(objects) $(libs_for_gcc) 

而当我们的变量$(CC)值不是“gcc”时(比如“cc”),目标foo的规则是: 

    foo: $(objects) 
            $(CC) -o foo $(objects) $(normal_libs) 

当然,我们还可以把上面的那个例子写得更简洁一些: 

    libs_for_gcc = -lgnu 
    normal_libs = 

    ifeq ($(CC),gcc) 
      libs=$(libs_for_gcc) 
    else 
      libs=$(normal_libs) 
    endif 

    foo: $(objects) 
            $(CC) -o foo $(objects) $(libs) 


二、语法 

条件表达式的语法为: 

    <conditional-directive>; 
    <text-if-true>; 
    endif 

以及: 

    <conditional-directive>; 
    <text-if-true>; 
    else 
    <text-if-false>; 
    endif 

其中<conditional-directive>;表示条件关键字,如“ifeq”。这个关键字有四个。 

第一个是我们前面所见过的“ifeq” 

    ifeq (<arg1>;, <arg2>;)  
    ifeq '<arg1>;' '<arg2>;'  
    ifeq "<arg1>;" "<arg2>;"  
    ifeq "<arg1>;" '<arg2>;'  
    ifeq '<arg1>;' "<arg2>;"  

比较参数“arg1”和“arg2”的值是否相同。当然,参数中我们还可以使用make的函数。如: 

    ifeq ($(strip $(foo)),) 
    <text-if-empty>; 
    endif 

这个示例中使用了“strip”函数,如果这个函数的返回值是空(Empty),那么<text-if-empty>;就生效。 

第二个条件关键字是“ifneq”。语法是: 

    ifneq (<arg1>;, <arg2>;)  
    ifneq '<arg1>;' '<arg2>;'  
    ifneq "<arg1>;" "<arg2>;"  
    ifneq "<arg1>;" '<arg2>;'  
    ifneq '<arg1>;' "<arg2>;"  

其比较参数“arg1”和“arg2”的值是否相同,如果不同,则为真。和“ifeq”类似。 

第三个条件关键字是“ifdef”。语法是: 

    ifdef <variable-name>;  

如果变量<variable-name>;的值非空,那到表达式为真。否则,表达式为假。当然,<variable-name>;同样可以是一个函数的返回值。注意,ifdef只是测试一个变量是否有值,其并不会把变量扩展到当前位置。还是来看两个例子: 

    示例一: 
    bar = 
    foo = $(bar) 
    ifdef foo 
    frobozz = yes 
    else 
    frobozz = no 
    endif 

    示例二: 
    foo = 
    ifdef foo 
    frobozz = yes 
    else 
    frobozz = no 
    endif 

第一个例子中,“$(frobozz)”值是“yes”,第二个则是“no”。 

第四个条件关键字是“ifndef”。其语法是: 

    ifndef <variable-name>; 

这个我就不多说了,和“ifdef”是相反的意思。 

在<conditional-directive>;这一行上,多余的空格是被允许的,但是不能以[Tab]键做为开始(不然就被认为是命令)。而注释符“#”同样也是安全的。“else”和“endif”也一样,只要不是以[Tab]键开始就行了。 

特别注意的是,make是在读取Makefile时就计算条件表达式的值,并根据条件表达式的值来选择语句,所以,你最好不要把自动化变量(如“$@”等)放入条件表达式中,因为自动化变量是在运行时才有的。 

而且,为了避免混乱,make不允许把整个条件语句分成两部分放在不同的文件中。

使用函数 
———— 

在Makefile中可以使用函数来处理变量,从而让我们的命令或是规则更为的灵活和具有智能。make所支持的函数也不算很多,不过已经足够我们的操作了。函数调用后,函数的返回值可以当做变量来使用。 


一、函数的调用语法 

函数调用,很像变量的使用,也是以“$”来标识的,其语法如下: 

    $(<function>; <arguments>;) 

或是 

    ${<function>; <arguments>;} 

这里,<function>;就是函数名,make支持的函数不多。<arguments>;是函数的参数,参数间以逗号“,”分隔,而函数名和参数之间以“空格”分隔。函数调用以“$”开头,以圆括号或花括号把函数名和参数括起。感觉很像一个变量,是不是?函数中的参数可以使用变量,为了风格的统一,函数和变量的括号最好一样,如使用“$(subst a,b,$(x))”这样的形式,而不是“$(subst a,b,${x})”的形式。因为统一会更清楚,也会减少一些不必要的麻烦。 

还是来看一个示例: 

    comma:= , 
    empty:= 
    space:= $(empty) $(empty) 
    foo:= a b c 
    bar:= $(subst $(space),$(comma),$(foo)) 

在这个示例中,$(comma)的值是一个逗号。$(space)使用了$(empty)定义了一个空格,$(foo)的值是“a b c”,$(bar)的定义用,调用了函数“subst”,这是一个替换函数,这个函数有三个参数,第一个参数是被替换字串,第二个参数是替换字串,第三个参数是替换操作作用的字串。这个函数也就是把$(foo)中的空格替换成逗号,所以$(bar)的值是“a,b,c”。 


二、字符串处理函数 

$(subst <from>;,<to>;,<text>;)  

    名称:字符串替换函数——subst。 
    功能:把字串<text>;中的<from>;字符串替换成<to>;。 
    返回:函数返回被替换过后的字符串。 

    示例: 
         
        $(subst ee,EE,feet on the street), 
         
        把“feet on the street”中的“ee”替换成“EE”,返回结果是“fEEt on the strEEt”。 


$(patsubst <pattern>;,<replacement>;,<text>;)  

    名称:模式字符串替换函数——patsubst。 
    功能:查找<text>;中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合模式<pattern>;,如果匹配的话,则以<replacement>;替换。这里,<pattern>;可以包括通配符“%”,表示任意长度的字串。如果<replacement>;中也包含“%”,那么,<replacement>;中的这个“%”将是<pattern>;中的那个“%”所代表的字串。(可以用“\”来转义,以“\%”来表示真实含义的“%”字符) 
    返回:函数返回被替换过后的字符串。 

    示例: 

        $(patsubst %.c,%.o,x.c.c bar.c) 

        把字串“x.c.c bar.c”符合模式[%.c]的单词替换成[%.o],返回结果是“x.c.o bar.o” 

    备注: 

        这和我们前面“变量章节”说过的相关知识有点相似。如: 

        “$(var:<pattern>;=<replacement>;)” 
         相当于 
        “$(patsubst <pattern>;,<replacement>;,$(var))”, 

         而“$(var: <suffix>;=<replacement>;)” 
         则相当于 
         “$(patsubst %<suffix>;,%<replacement>;,$(var))”。 

         例如有:objects = foo.o bar.o baz.o, 
         那么,“$(objects:.o=.c)”和“$(patsubst %.o,%.c,$(objects))”是一样的。 

$(strip <string>;) 

    名称:去空格函数——strip。 
    功能:去掉<string>;字串中开头和结尾的空字符。 
    返回:返回被去掉空格的字符串值。 
    示例: 
         
        $(strip a b c ) 

        把字串“a b c ”去到开头和结尾的空格,结果是“a b c”。 

$(findstring <find>;,<in>;) 

    名称:查找字符串函数——findstring。 
    功能:在字串<in>;中查找<find>;字串。 
    返回:如果找到,那么返回<find>;,否则返回空字符串。 
    示例: 

        $(findstring a,a b c) 
        $(findstring a,b c) 

        第一个函数返回“a”字符串,第二个返回“”字符串(空字符串) 

$(filter <pattern...>;,<text>;) 

    名称:过滤函数——filter。 
    功能:以<pattern>;模式过滤<text>;字符串中的单词,保留符合模式<pattern>;的单词。可以有多个模式。 
    返回:返回符合模式<pattern>;的字串。 
    示例: 

        sources := foo.c bar.c baz.s ugh.h 
        foo: $(sources) 
                cc $(filter %.c %.s,$(sources)) -o foo 

        $(filter %.c %.s,$(sources))返回的值是“foo.c bar.c baz.s”。 

$(filter-out <pattern...>;,<text>;) 

    名称:反过滤函数——filter-out。 
    功能:以<pattern>;模式过滤<text>;字符串中的单词,去除符合模式<pattern>;的单词。可以有多个模式。 
    返回:返回不符合模式<pattern>;的字串。 
    示例: 

        objects=main1.o foo.o main2.o bar.o 
        mains=main1.o main2.o 
         
        $(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。 
         
$(sort <list>;) 

    名称:排序函数——sort。 
    功能:给字符串<list>;中的单词排序(升序)。 
    返回:返回排序后的字符串。 
    示例:$(sort foo bar lose)返回“bar foo lose” 。 
    备注:sort函数会去掉<list>;中相同的单词。 

$(word <n>;,<text>;) 

    名称:取单词函数——word。 
    功能:取字符串<text>;中第<n>;个单词。(从一开始) 
    返回:返回字符串<text>;中第<n>;个单词。如果<n>;比<text>;中的单词数要大,那么返回空字符串。 
    示例:$(word 2, foo bar baz)返回值是“bar”。 

$(wordlist <s>;,<e>;,<text>;)   

    名称:取单词串函数——wordlist。 
    功能:从字符串<text>;中取从<s>;开始到<e>;的单词串。<s>;和<e>;是一个数字。 
    返回:返回字符串<text>;中从<s>;到<e>;的单词字串。如果<s>;比<text>;中的单词数要大,那么返回空字符串。如果<e>;大于<text>;的单词数,那么返回从<s>;开始,到<text>;结束的单词串。 
    示例: $(wordlist 2, 3, foo bar baz)返回值是“bar baz”。 

$(words <text>;) 

    名称:单词个数统计函数——words。 
    功能:统计<text>;中字符串中的单词个数。 
    返回:返回<text>;中的单词数。 
    示例:$(words, foo bar baz)返回值是“3”。 
    备注:如果我们要取<text>;中最后的一个单词,我们可以这样:$(word $(words <text>;),<text>;)。 

$(firstword <text>;) 

    名称:首单词函数——firstword。 
    功能:取字符串<text>;中的第一个单词。 
    返回:返回字符串<text>;的第一个单词。 
    示例:$(firstword foo bar)返回值是“foo”。 
    备注:这个函数可以用word函数来实现:$(word 1,<text>;)。 

以上,是所有的字符串操作函数,如果搭配混合使用,可以完成比较复杂的功能。这里,举一个现实中应用的例子。我们知道,make使用“VPATH”变量来指定“依赖文件”的搜索路径。于是,我们可以利用这个搜索路径来指定编译器对头文件的搜索路径参数CFLAGS,如: 

    override CFLAGS += $(patsubst %,-I%,$(subst :, ,$(VPATH))) 

    如果我们的“$(VPATH)”值是“src:../headers”,那么“$(patsubst %,-I%,$(subst :, ,$(VPATH)))”将返回“-Isrc -I../headers”,这正是cc或gcc搜索头文件路径的参数。 


三、文件名操作函数 

下面我们要介绍的函数主要是处理文件名的。每个函数的参数字符串都会被当做一个或是一系列的文件名来对待。

$(dir <names...>;)  

    名称:取目录函数——dir。 
    功能:从文件名序列<names>;中取出目录部分。目录部分是指最后一个反斜杠(“/”)之前的部分。如果没有反斜杠,那么返回“./”。 
    返回:返回文件名序列<names>;的目录部分。 
    示例: $(dir src/foo.c hacks)返回值是“src/ ./”。 

$(notdir <names...>;)  

    名称:取文件函数——notdir。 
    功能:从文件名序列<names>;中取出非目录部分。非目录部分是指最后一个反斜杠(“/”)之后的部分。 
    返回:返回文件名序列<names>;的非目录部分。 
    示例: $(notdir src/foo.c hacks)返回值是“foo.c hacks”。 
  
$(suffix <names...>;)  
     
    名称:取后缀函数——suffix。 
    功能:从文件名序列<names>;中取出各个文件名的后缀。 
    返回:返回文件名序列<names>;的后缀序列,如果文件没有后缀,则返回空字串。 
    示例:$(suffix src/foo.c src-1.0/bar.c hacks)返回值是“.c .c”。 

$(basename <names...>;) 

    名称:取前缀函数——basename。 
    功能:从文件名序列<names>;中取出各个文件名的前缀部分。 
    返回:返回文件名序列<names>;的前缀序列,如果文件没有前缀,则返回空字串。 
    示例:$(basename src/foo.c src-1.0/bar.c hacks)返回值是“src/foo src-1.0/bar hacks”。 

$(addsuffix <suffix>;,<names...>;)  

    名称:加后缀函数——addsuffix。 
    功能:把后缀<suffix>;加到<names>;中的每个单词后面。 
    返回:返回加过后缀的文件名序列。 
    示例:$(addsuffix .c,foo bar)返回值是“foo.c bar.c”。 

$(addprefix <prefix>;,<names...>;)  

    名称:加前缀函数——addprefix。 
    功能:把前缀<prefix>;加到<names>;中的每个单词后面。 
    返回:返回加过前缀的文件名序列。 
    示例:$(addprefix src/,foo bar)返回值是“src/foo src/bar”。 

$(join <list1>;,<list2>;) 

    名称:连接函数——join。 
    功能:把<list2>;中的单词对应地加到<list1>;的单词后面。如果<list1>;的单词个数要比<list2>;的多,那么,<list1>;中的多出来的单词将保持原样。如果<list2>;的单词个数要比<list1>;多,那么,<list2>;多出来的单词将被复制到<list2>;中。 
    返回:返回连接过后的字符串。 
    示例:$(join aaa bbb , 111 222 333)返回值是“aaa111 bbb222 333”。

四、foreach 函数 
  

foreach函数和别的函数非常的不一样。因为这个函数是用来做循环用的,Makefile中的foreach函数几乎是仿照于Unix标准Shell(/bin/sh)中的for语句,或是C-Shell(/bin/csh)中的foreach语句而构建的。它的语法是: 

  

    $(foreach <var>;,<list>;,<text>;) 

  

这个函数的意思是,把参数<list>;中的单词逐一取出放到参数<var>;所指定的变量中,然后再执行<text>;所包含的表达式。每一次<text>;会返回一个字符串,循环过程中,<text>;的所返回的每个字符串会以空格分隔,最后当整个循环结束时,<text>;所返回的每个字符串所组成的整个字符串(以空格分隔)将会是foreach函数的返回值。 

  

所以,<var>;最好是一个变量名,<list>;可以是一个表达式,而<text>;中一般会使用<var>;这个参数来依次枚举<list>;中的单词。举个例子: 

  

    names := a b c d 

    files := $(foreach n,$(names),$(n).o) 

  

上面的例子中,$(name)中的单词会被挨个取出,并存到变量“n”中,“$(n).o”每次根据“$(n)”计算出一个值,这些值以空格分隔,最后作为foreach函数的返回,所以,$(files)的值是“a.o b.o c.o d.o”。 

  

注意,foreach中的<var>;参数是一个临时的局部变量,foreach函数执行完后,参数<var>;的变量将不在作用,其作用域只在foreach函数当中。 

  

  

五、if 函数 
  

if函数很像GNU的make所支持的条件语句——ifeq(参见前面所述的章节),if函数的语法是: 

  

    $(if <condition>;,<then-part>;)  

  

或是 

  

    $(if <condition>;,<then-part>;,<else-part>;) 

  

可见,if函数可以包含“else”部分,或是不含。即if函数的参数可以是两个,也可以是三个。<condition>;参数是if的表达式,如果其返回的为非空字符串,那么这个表达式就相当于返回真,于是,<then-part>;会被计算,否则<else-part>;会被计算。 

  

而if函数的返回值是,如果<condition>;为真(非空字符串),那个<then-part>;会是整个函数的返回值,如果<condition>;为假(空字符串),那么<else-part>;会是整个函数的返回值,此时如果<else-part>;没有被定义,那么,整个函数返回空字串。 

  

所以,<then-part>;和<else-part>;只会有一个被计算。 

  

  

六、call函数 
  

call函数是唯一一个可以用来创建新的参数化的函数。你可以写一个非常复杂的表达式,这个表达式中,你可以定义许多参数,然后你可以用call函数来向这个表达式传递参数。其语法是: 

  

    $(call <expression>;,<parm1>;,<parm2>;,<parm3>;...) 

  

当make执行这个函数时,<expression>;参数中的变量,如$(1),$(2),$(3)等,会被参数<parm1>;,<parm2>;,<parm3>;依次取代。而<expression>;的返回值就是call函数的返回值。例如: 

    reverse =  $(1) $(2) 

    foo = $(call reverse,a,b) 



那么,foo的值就是“a b”。当然,参数的次序是可以自定义的,不一定是顺序的,如: 

  

    reverse =  $(2) $(1) 

    foo = $(call reverse,a,b) 



此时的foo的值就是“b a”。 

  

  

七、origin函数 
origin函数不像其它的函数,他并不操作变量的值,他只是告诉你你的这个变量是哪里来的?其语法是: 

  

    $(origin <variable>;) 

  

注意,<variable>;是变量的名字,不应该是引用。所以你最好不要在<variable>;中使用“$”字符。Origin函数会以其返回值来告诉你这个变量的“出生情况”,下面,是origin函数的返回值: 

  

“undefined” 

      如果<variable>;从来没有定义过,origin函数返回这个值“undefined”。 

  

“default” 

      如果<variable>;是一个默认的定义,比如“CC”这个变量,这种变量我们将在后面讲述。 

  

“environment” 

      如果<variable>;是一个环境变量,并且当Makefile被执行时,“-e”参数没有被打开。 

  

“file” 

      如果<variable>;这个变量被定义在Makefile中。 

  

“command line” 

      如果<variable>;这个变量是被命令行定义的。 

  

“override” 

      如果<variable>;是被override指示符重新定义的。 

  

“automatic” 

      如果<variable>;是一个命令运行中的自动化变量。关于自动化变量将在后面讲述。 

  

这些信息对于我们编写Makefile是非常有用的,例如,假设我们有一个Makefile其包了一个定义文件Make.def,在Make.def中定义了一个变量“bletch”,而我们的环境中也有一个环境变量“bletch”,此时,我们想判断一下,如果变量来源于环境,那么我们就把之重定义了,如果来源于Make.def或是命令行等非环境的,那么我们就不重新定义它。于是,在我们的Makefile中,我们可以这样写: 

  

    ifdef bletch 

    ifeq "$(origin bletch)" "environment" 

    bletch = barf, gag, etc. 

    endif 

    endif 

  

当然,你也许会说,使用override关键字不就可以重新定义环境中的变量了吗?为什么需要使用这样的步骤?是的,我们用override是可以达到这样的效果,可是override过于粗暴,它同时会把从命令行定义的变量也覆盖了,而我们只想重新定义环境传来的,而不想重新定义命令行传来的。 

  

  

八、shell函数 
  

shell函数也不像其它的函数。顾名思义,它的参数应该就是操作系统Shell的命令。它和反引号“`”是相同的功能。这就是说,shell函数把执行操作系统命令后的输出作为函数返回。于是,我们可以用操作系统命令以及字符串处理命令awk,sed等等命令来生成一个变量,如: 

  

    contents := $(shell cat foo) 

  

    files := $(shell echo *.c) 

  

注意,这个函数会新生成一个Shell程序来执行命令,所以你要注意其运行性能,如果你的Makefile中有一些比较复杂的规则,并大量使用了这个函数,那么对于你的系统性能是有害的。特别是Makefile的隐晦的规则可能会让你的shell函数执行的次数比你想像的多得多。 

  

  

九、控制make的函数 
  

make提供了一些函数来控制make的运行。通常,你需要检测一些运行Makefile时的运行时信息,并且根据这些信息来决定,你是让make继续执行,还是停止。 

  

$(error <text ...>;) 

  

    产生一个致命的错误,<text ...>;是错误信息。注意,error函数不会在一被使用就会产生错误信息,所以如果你把其定义在某个变量中,并在后续的脚本中使用这个变量,那么也是可以的。例如: 

  

    示例一: 

    ifdef ERROR_001 

    $(error error is $(ERROR_001)) 

    endif 

  

    示例二: 

    ERR = $(error found an error!) 

    .PHONY: err 

    err: ; $(ERR) 

  

    示例一会在变量ERROR_001定义了后执行时产生error调用,而示例二则在目录err被执行时才发生error调用。 

  

$(warning <text ...>;) 

  

     这个函数很像error函数,只是它并不会让make退出,只是输出一段警告信息,而make继续执行。

make 的运行 
—————— 

一般来说,最简单的就是直接在命令行下输入make命令,make命令会找当前目录的makefile来执行,一切都是自动的。但也有时你也许只想让make重编译某些文件,而不是整个工程,而又有的时候你有几套编译规则,你想在不同的时候使用不同的编译规则,等等。本章节就是讲述如何使用make命令的。 

一、make的退出码 

make命令执行后有三个退出码: 

    0 —— 表示成功执行。 
    1 —— 如果make运行时出现任何错误,其返回1。 
    2 —— 如果你使用了make的“-q”选项,并且make使得一些目标不需要更新,那么返回2。 

Make的相关参数我们会在后续章节中讲述。 


二、指定Makefile 

前面我们说过,GNU make找寻默认的Makefile的规则是在当前目录下依次找三个文件——“GNUmakefile”、“makefile”和“Makefile”。其按顺序找这三个文件,一旦找到,就开始读取这个文件并执行。 

当前,我们也可以给make命令指定一个特殊名字的Makefile。要达到这个功能,我们要使用make的“-f”或是“--file”参数(“--makefile”参数也行)。例如,我们有个makefile的名字是“hchen.mk”,那么,我们可以这样来让make来执行这个文件: 

    make –f hchen.mk 

如果在make的命令行是,你不只一次地使用了“-f”参数,那么,所有指定的makefile将会被连在一起传递给make执行。 


三、指定目标 

一般来说,make的最终目标是makefile中的第一个目标,而其它目标一般是由这个目标连带出来的。这是make的默认行为。当然,一般来说,你的makefile中的第一个目标是由许多个目标组成,你可以指示make,让其完成你所指定的目标。要达到这一目的很简单,需在make命令后直接跟目标的名字就可以完成(如前面提到的“make clean”形式) 

任何在makefile中的目标都可以被指定成终极目标,但是除了以“-”打头,或是包含了“=”的目标,因为有这些字符的目标,会被解析成命令行参数或是变量。甚至没有被我们明确写出来的目标也可以成为make的终极目标,也就是说,只要make可以找到其隐含规则推导规则,那么这个隐含目标同样可以被指定成终极目标。 

有一个make的环境变量叫“MAKECMDGOALS”,这个变量中会存放你所指定的终极目标的列表,如果在命令行上,你没有指定目标,那么,这个变量是空值。这个变量可以让你使用在一些比较特殊的情形下。比如下面的例子: 

    sources = foo.c bar.c 
    ifneq ( $(MAKECMDGOALS),clean) 
    include $(sources:.c=.d) 
    endif 

基于上面的这个例子,只要我们输入的命令不是“make clean”,那么makefile会自动包含“foo.d”和“bar.d”这两个makefile。 

使用指定终极目标的方法可以很方便地让我们编译我们的程序,例如下面这个例子: 

    .PHONY: all 
    all: prog1 prog2 prog3 prog4 

从这个例子中,我们可以看到,这个makefile中有四个需要编译的程序——“prog1”, “prog2”, “prog3”和 “prog4”,我们可以使用“make all”命令来编译所有的目标(如果把all置成第一个目标,那么只需执行“make”),我们也可以使用“make prog2”来单独编译目标“prog2”。 

即然make可以指定所有makefile中的目标,那么也包括“伪目标”,于是我们可以根据这种性质来让我们的makefile根据指定的不同的目标来完成不同的事。在Unix世界中,软件发布时,特别是GNU这种开源软件的发布时,其makefile都包含了编译、安装、打包等功能。我们可以参照这种规则来书写我们的makefile中的目标。 

     “all” 
        这个伪目标是所有目标的目标,其功能一般是编译所有的目标。 
     “clean” 
        这个伪目标功能是删除所有被make创建的文件。 
     “install” 
        这个伪目标功能是安装已编译好的程序,其实就是把目标执行文件拷贝到指定的目标中去。 
     “print” 
        这个伪目标的功能是例出改变过的源文件。 
     “tar” 
        这个伪目标功能是把源程序打包备份。也就是一个tar文件。 
     “dist” 
        这个伪目标功能是创建一个压缩文件,一般是把tar文件压成Z文件。或是gz文件。 
     “TAGS” 
        这个伪目标功能是更新所有的目标,以备完整地重编译使用。 
     “check”和“test” 
        这两个伪目标一般用来测试makefile的流程。 

当然一个项目的makefile中也不一定要书写这样的目标,这些东西都是GNU的东西,但是我想,GNU搞出这些东西一定有其可取之处(等你的UNIX下的程序文件一多时你就会发现这些功能很有用了),这里只不过是说明了,如果你要书写这种功能,最好使用这种名字命名你的目标,这样规范一些,规范的好处就是——不用解释,大家都明白。而且如果你的makefile中有这些功能,一是很实用,二是可以显得你的makefile很专业(不是那种初学者的作品)。 


四、检查规则 

有时候,我们不想让我们的makefile中的规则执行起来,我们只想检查一下我们的命令,或是执行的序列。于是我们可以使用make命令的下述参数: 

    “-n” 
    “--just-print” 
    “--dry-run” 
    “--recon” 
    不执行参数,这些参数只是打印命令,不管目标是否更新,把规则和连带规则下的命令打印出来,但不执行,这些参数对于我们调试makefile很有用处。 

    “-t” 
    “--touch” 
    这个参数的意思就是把目标文件的时间更新,但不更改目标文件。也就是说,make假装编译目标,但不是真正的编译目标,只是把目标变成已编译过的状态。 

    “-q” 
    “--question” 
    这个参数的行为是找目标的意思,也就是说,如果目标存在,那么其什么也不会输出,当然也不会执行编译,如果目标不存在,其会打印出一条出错信息。 

    “-W <file>;” 
    “--what-if=<file>;” 
    “--assume-new=<file>;” 
    “--new-file=<file>;” 
    这个参数需要指定一个文件。一般是是源文件(或依赖文件),Make会根据规则推导来运行依赖于这个文件的命令,一般来说,可以和“-n”参数一同使用,来查看这个依赖文件所发生的规则命令。 

另外一个很有意思的用法是结合“-p”和“-v”来输出makefile被执行时的信息(这个将在后面讲述)。 


五、make的参数 

下面列举了所有GNU make 3.80版的参数定义。其它版本和产商的make大同小异,不过其它产商的make的具体参数还是请参考各自的产品文档。 

“-b” 
“-m” 
这两个参数的作用是忽略和其它版本make的兼容性。 

“-B” 
“--always-make” 
认为所有的目标都需要更新(重编译)。 

“-C <dir>;” 
“--directory=<dir>;” 
指定读取makefile的目录。如果有多个“-C”参数,make的解释是后面的路径以前面的作为相对路径,并以最后的目录作为被指定目录。如:“make –C ~hchen/test –C prog”等价于“make –C ~hchen/test/prog”。 

“—debug[=<options>;]” 
输出make的调试信息。它有几种不同的级别可供选择,如果没有参数,那就是输出最简单的调试信息。下面是<options>;的取值: 
    a —— 也就是all,输出所有的调试信息。(会非常的多) 
    b —— 也就是basic,只输出简单的调试信息。即输出不需要重编译的目标。 
    v —— 也就是verbose,在b选项的级别之上。输出的信息包括哪个makefile被解析,不需要被重编译的依赖文件(或是依赖目标)等。 
    i —— 也就是implicit,输出所以的隐含规则。 
    j —— 也就是jobs,输出执行规则中命令的详细信息,如命令的PID、返回码等。 
    m —— 也就是makefile,输出make读取makefile,更新makefile,执行makefile的信息。 

“-d” 
相当于“--debug=a”。 

“-e” 
“--environment-overrides” 
指明环境变量的值覆盖makefile中定义的变量的值。 

“-f=<file>;” 
“--file=<file>;” 
“--makefile=<file>;” 
指定需要执行的makefile。 

“-h” 
“--help” 
显示帮助信息。 

“-i” 
“--ignore-errors” 
在执行时忽略所有的错误。 

“-I <dir>;” 
“--include-dir=<dir>;” 
指定一个被包含makefile的搜索目标。可以使用多个“-I”参数来指定多个目录。 

“-j [<jobsnum>;]” 
“--jobs[=<jobsnum>;]” 
指同时运行命令的个数。如果没有这个参数,make运行命令时能运行多少就运行多少。如果有一个以上的“-j”参数,那么仅最后一个“-j”才是有效的。(注意这个参数在MS-DOS中是无用的) 

“-k” 
“--keep-going” 
出错也不停止运行。如果生成一个目标失败了,那么依赖于其上的目标就不会被执行了。 

“-l <load>;” 
“--load-average[=<load]” 
“—max-load[=<load>;]” 
指定make运行命令的负载。 

“-n” 
“--just-print” 
“--dry-run” 
“--recon” 
仅输出执行过程中的命令序列,但并不执行。 

“-o <file>;” 
“--old-file=<file>;” 
“--assume-old=<file>;” 
不重新生成的指定的<file>;,即使这个目标的依赖文件新于它。 

“-p” 
“--print-data-base” 
输出makefile中的所有数据,包括所有的规则和变量。这个参数会让一个简单的makefile都会输出一堆信息。如果你只是想输出信息而不想执行makefile,你可以使用“make -qp”命令。如果你想查看执行makefile前的预设变量和规则,你可以使用“make –p –f /dev/null”。这个参数输出的信息会包含着你的makefile文件的文件名和行号,所以,用这个参数来调试你的makefile会是很有用的,特别是当你的环境变量很复杂的时候。 

“-q” 
“--question” 
不运行命令,也不输出。仅仅是检查所指定的目标是否需要更新。如果是0则说明要更新,如果是2则说明有错误发生。 

“-r” 
“--no-builtin-rules” 
禁止make使用任何隐含规则。 

“-R” 
“--no-builtin-variabes” 
禁止make使用任何作用于变量上的隐含规则。 

“-s” 
“--silent” 
“--quiet” 
在命令运行时不输出命令的输出。 

“-S” 
“--no-keep-going” 
“--stop” 
取消“-k”选项的作用。因为有些时候,make的选项是从环境变量“MAKEFLAGS”中继承下来的。所以你可以在命令行中使用这个参数来让环境变量中的“-k”选项失效。 

“-t” 
“--touch” 
相当于UNIX的touch命令,只是把目标的修改日期变成最新的,也就是阻止生成目标的命令运行。 

“-v” 
“--version” 
输出make程序的版本、版权等关于make的信息。 

“-w” 
“--print-directory” 
输出运行makefile之前和之后的信息。这个参数对于跟踪嵌套式调用make时很有用。 

“--no-print-directory” 
禁止“-w”选项。 

“-W <file>;” 
“--what-if=<file>;” 
“--new-file=<file>;” 
“--assume-file=<file>;” 
假定目标<file>;需要更新,如果和“-n”选项使用,那么这个参数会输出该目标更新时的运行动作。如果没有“-n”那么就像运行UNIX的“touch”命令一样,使得<file>;的修改时间为当前时间。 

“--warn-undefined-variables” 
只要make发现有未定义的变量,那么就输出警告信息。

隐含规则 
———— 

在我们使用Makefile时,有一些我们会经常使用,而且使用频率非常高的东西,比如,我们编译C/C++的源程序为中间目标文件(Unix下是[.o]文件,Windows下是[.obj]文件)。本章讲述的就是一些在Makefile中的“隐含的”,早先约定了的,不需要我们再写出来的规则。 

“隐含规则”也就是一种惯例,make会按照这种“惯例”心照不喧地来运行,那怕我们的Makefile中没有书写这样的规则。例如,把[.c]文件编译成[.o]文件这一规则,你根本就不用写出来,make会自动推导出这种规则,并生成我们需要的[.o]文件。 

“隐含规则”会使用一些我们系统变量,我们可以改变这些系统变量的值来定制隐含规则的运行时的参数。如系统变量“CFLAGS”可以控制编译时的编译器参数。 

我们还可以通过“模式规则”的方式写下自己的隐含规则。用“后缀规则”来定义隐含规则会有许多的限制。使用“模式规则”会更回得智能和清楚,但“后缀规则”可以用来保证我们Makefile的兼容性。 
我们了解了“隐含规则”,可以让其为我们更好的服务,也会让我们知道一些“约定俗成”了的东西,而不至于使得我们在运行Makefile时出现一些我们觉得莫名其妙的东西。当然,任何事物都是矛盾的,水能载舟,亦可覆舟,所以,有时候“隐含规则”也会给我们造成不小的麻烦。只有了解了它,我们才能更好地使用它。 


一、使用隐含规则 

如果要使用隐含规则生成你需要的目标,你所需要做的就是不要写出这个目标的规则。那么,make会试图去自动推导产生这个目标的规则和命令,如果make可以自动推导生成这个目标的规则和命令,那么这个行为就是隐含规则的自动推导。当然,隐含规则是make事先约定好的一些东西。例如,我们有下面的一个Makefile: 

    foo : foo.o bar.o 
            cc –o foo foo.o bar.o $(CFLAGS) $(LDFLAGS) 

我们可以注意到,这个Makefile中并没有写下如何生成foo.o和bar.o这两目标的规则和命令。因为make的“隐含规则”功能会自动为我们自动去推导这两个目标的依赖目标和生成命令。 

make会在自己的“隐含规则”库中寻找可以用的规则,如果找到,那么就会使用。如果找不到,那么就会报错。在上面的那个例子中,make调用的隐含规则是,把[.o]的目标的依赖文件置成[.c],并使用C的编译命令“cc –c $(CFLAGS) [.c]”来生成[.o]的目标。也就是说,我们完全没有必要写下下面的两条规则: 

    foo.o : foo.c 
            cc –c foo.c $(CFLAGS) 
    bar.o : bar.c 
        cc –c bar.c $(CFLAGS) 

因为,这已经是“约定”好了的事了,make和我们约定好了用C编译器“cc”生成[.o]文件的规则,这就是隐含规则。 

当然,如果我们为[.o]文件书写了自己的规则,那么make就不会自动推导并调用隐含规则,它会按照我们写好的规则忠实地执行。 

还有,在make的“隐含规则库”中,每一条隐含规则都在库中有其顺序,越靠前的则是越被经常使用的,所以,这会导致我们有些时候即使我们显示地指定了目标依赖,make也不会管。如下面这条规则(没有命令): 

    foo.o : foo.p 

依赖文件“foo.p”(Pascal程序的源文件)有可能变得没有意义。如果目录下存在了“foo.c”文件,那么我们的隐含规则一样会生效,并会通过“foo.c”调用C的编译器生成foo.o文件。因为,在隐含规则中,Pascal的规则出现在C的规则之后,所以,make找到可以生成foo.o的C的规则就不再寻找下一条规则了。如果你确实不希望任何隐含规则推导,那么,你就不要只写出“依赖规则”,而不写命令。 


二、隐含规则一览 

这里我们将讲述所有预先设置(也就是make内建)的隐含规则,如果我们不明确地写下规则,那么,make就会在这些规则中寻找所需要规则和命令。当然,我们也可以使用make的参数“-r”或“--no-builtin-rules”选项来取消所有的预设置的隐含规则。 

当然,即使是我们指定了“-r”参数,某些隐含规则还是会生效,因为有许多的隐含规则都是使用了“后缀规则”来定义的,所以,只要隐含规则中有“后缀列表”(也就一系统定义在目标.SUFFIXES的依赖目标),那么隐含规则就会生效。默认的后缀列表是:.out, .a, .ln, .o, .c, .cc, .C, .p, .f, .F, .r, .y, .l, .s, .S, .mod, .sym, .def, .h, .info, .dvi, .tex, .texinfo, .texi, .txinfo, .w, .ch .web, .sh, .elc, .el。具体的细节,我们会在后面讲述。 

还是先来看一看常用的隐含规则吧。 

1、编译C程序的隐含规则。 
“<n>;.o”的目标的依赖目标会自动推导为“<n>;.c”,并且其生成命令是“$(CC) –c $(CPPFLAGS) $(CFLAGS)” 

2、编译C++程序的隐含规则。 
“<n>;.o”的目标的依赖目标会自动推导为“<n>;.cc”或是“<n>;.C”,并且其生成命令是“$(CXX) –c $(CPPFLAGS) $(CFLAGS)”。(建议使用“.cc”作为C++源文件的后缀,而不是“.C”) 

3、编译Pascal程序的隐含规则。 
“<n>;.o”的目标的依赖目标会自动推导为“<n>;.p”,并且其生成命令是“$(PC) –c  $(PFLAGS)”。 

4、编译Fortran/Ratfor程序的隐含规则。 
“<n>;.o”的目标的依赖目标会自动推导为“<n>;.r”或“<n>;.F”或“<n>;.f”,并且其生成命令是: 
    “.f”  “$(FC) –c  $(FFLAGS)” 
    “.F”  “$(FC) –c  $(FFLAGS) $(CPPFLAGS)” 
    “.f”  “$(FC) –c  $(FFLAGS) $(RFLAGS)” 

5、预处理Fortran/Ratfor程序的隐含规则。 
“<n>;.f”的目标的依赖目标会自动推导为“<n>;.r”或“<n>;.F”。这个规则只是转换Ratfor或有预处理的Fortran程序到一个标准的Fortran程序。其使用的命令是: 
    “.F”  “$(FC) –F $(CPPFLAGS) $(FFLAGS)” 
    “.r”  “$(FC) –F $(FFLAGS) $(RFLAGS)” 

6、编译Modula-2程序的隐含规则。 
“<n>;.sym”的目标的依赖目标会自动推导为“<n>;.def”,并且其生成命令是:“$(M2C) $(M2FLAGS) $(DEFFLAGS)”。“<n.o>;” 的目标的依赖目标会自动推导为“<n>;.mod”,并且其生成命令是:“$(M2C) $(M2FLAGS) $(MODFLAGS)”。 

7、汇编和汇编预处理的隐含规则。 
“<n>;.o” 的目标的依赖目标会自动推导为“<n>;.s”,默认使用编译品“as”,并且其生成命令是:“$(AS) $(ASFLAGS)”。“<n>;.s” 的目标的依赖目标会自动推导为“<n>;.S”,默认使用C预编译器“cpp”,并且其生成命令是:“$(AS) $(ASFLAGS)”。 

8、链接Object文件的隐含规则。 
“<n>;”目标依赖于“<n>;.o”,通过运行C的编译器来运行链接程序生成(一般是“ld”),其生成命令是:“$(CC) $(LDFLAGS) <n>;.o $(LOADLIBES) $(LDLIBS)”。这个规则对于只有一个源文件的工程有效,同时也对多个Object文件(由不同的源文件生成)的也有效。例如如下规则: 

    x : y.o z.o 

并且“x.c”、“y.c”和“z.c”都存在时,隐含规则将执行如下命令: 

    cc -c x.c -o x.o 
    cc -c y.c -o y.o 
    cc -c z.c -o z.o 
    cc x.o y.o z.o -o x 
    rm -f x.o 
    rm -f y.o 
    rm -f z.o 

如果没有一个源文件(如上例中的x.c)和你的目标名字(如上例中的x)相关联,那么,你最好写出自己的生成规则,不然,隐含规则会报错的。 

9、Yacc C程序时的隐含规则。 
“<n>;.c”的依赖文件被自动推导为“n.y”(Yacc生成的文件),其生成命令是:“$(YACC) $(YFALGS)”。(“Yacc”是一个语法分析器,关于其细节请查看相关资料) 

10、Lex C程序时的隐含规则。 
“<n>;.c”的依赖文件被自动推导为“n.l”(Lex生成的文件),其生成命令是:“$(LEX) $(LFALGS)”。(关于“Lex”的细节请查看相关资料) 

11、Lex Ratfor程序时的隐含规则。 
“<n>;.r”的依赖文件被自动推导为“n.l”(Lex生成的文件),其生成命令是:“$(LEX) $(LFALGS)”。 

12、从C程序、Yacc文件或Lex文件创建Lint库的隐含规则。 
“<n>;.ln” (lint生成的文件)的依赖文件被自动推导为“n.c”,其生成命令是:“$(LINT) $(LINTFALGS) $(CPPFLAGS) -i”。对于“<n>;.y”和“<n>;.l”也是同样的规则。 


三、隐含规则使用的变量 

在隐含规则中的命令中,基本上都是使用了一些预先设置的变量。你可以在你的makefile中改变这些变量的值,或是在make的命令行中传入这些值,或是在你的环境变量中设置这些值,无论怎么样,只要设置了这些特定的变量,那么其就会对隐含规则起作用。当然,你也可以利用make的“-R”或“--no–builtin-variables”参数来取消你所定义的变量对隐含规则的作用。 

例如,第一条隐含规则——编译C程序的隐含规则的命令是“$(CC) –c $(CFLAGS) $(CPPFLAGS)”。Make默认的编译命令是“cc”,如果你把变量“$(CC)”重定义成“gcc”,把变量“$(CFLAGS)”重定义成“-g”,那么,隐含规则中的命令全部会以“gcc –c -g $(CPPFLAGS)”的样子来执行了。 

我们可以把隐含规则中使用的变量分成两种:一种是命令相关的,如“CC”;一种是参数相的关,如“CFLAGS”。下面是所有隐含规则中会用到的变量: 

1、关于命令的变量。 

AR  
    函数库打包程序。默认命令是“ar”。  
AS  
    汇编语言编译程序。默认命令是“as”。 
CC  
    C语言编译程序。默认命令是“cc”。 
CXX  
    C++语言编译程序。默认命令是“g++”。 
CO  
    从 RCS文件中扩展文件程序。默认命令是“co”。 
CPP  
    C程序的预处理器(输出是标准输出设备)。默认命令是“$(CC) –E”。 
FC  
    Fortran 和 Ratfor 的编译器和预处理程序。默认命令是“f77”。 
GET  
    从SCCS文件中扩展文件的程序。默认命令是“get”。  
LEX  
    Lex方法分析器程序(针对于C或Ratfor)。默认命令是“lex”。 
PC  
    Pascal语言编译程序。默认命令是“pc”。 
YACC  
    Yacc文法分析器(针对于C程序)。默认命令是“yacc”。 
YACCR  
    Yacc文法分析器(针对于Ratfor程序)。默认命令是“yacc –r”。 
MAKEINFO  
    转换Texinfo源文件(.texi)到Info文件程序。默认命令是“makeinfo”。 
TEX  
    从TeX源文件创建TeX DVI文件的程序。默认命令是“tex”。 
TEXI2DVI  
    从Texinfo源文件创建军TeX DVI 文件的程序。默认命令是“texi2dvi”。 
WEAVE  
    转换Web到TeX的程序。默认命令是“weave”。 
CWEAVE  
    转换C Web 到 TeX的程序。默认命令是“cweave”。 
TANGLE  
    转换Web到Pascal语言的程序。默认命令是“tangle”。 
CTANGLE  
    转换C Web 到 C。默认命令是“ctangle”。 
RM  
    删除文件命令。默认命令是“rm –f”。 

2、关于命令参数的变量 

下面的这些变量都是相关上面的命令的参数。如果没有指明其默认值,那么其默认值都是空。 

ARFLAGS  
    函数库打包程序AR命令的参数。默认值是“rv”。 
ASFLAGS  
    汇编语言编译器参数。(当明显地调用“.s”或“.S”文件时)。  
CFLAGS  
    C语言编译器参数。 
CXXFLAGS  
    C++语言编译器参数。 
COFLAGS  
    RCS命令参数。  
CPPFLAGS  
    C预处理器参数。( C 和 Fortran 编译器也会用到)。 
FFLAGS  
    Fortran语言编译器参数。 
GFLAGS  
    SCCS “get”程序参数。 
LDFLAGS  
    链接器参数。(如:“ld”) 
LFLAGS  
    Lex文法分析器参数。 
PFLAGS  
    Pascal语言编译器参数。 
RFLAGS  
    Ratfor 程序的Fortran 编译器参数。 
YFLAGS  
    Yacc文法分析器参数。  


四、隐含规则链 

有些时候,一个目标可能被一系列的隐含规则所作用。例如,一个[.o]的文件生成,可能会是先被Yacc的[.y]文件先成[.c],然后再被C的编译器生成。我们把这一系列的隐含规则叫做“隐含规则链”。 

在上面的例子中,如果文件[.c]存在,那么就直接调用C的编译器的隐含规则,如果没有[.c]文件,但有一个[.y]文件,那么Yacc的隐含规则会被调用,生成[.c]文件,然后,再调用C编译的隐含规则最终由[.c]生成[.o]文件,达到目标。 

我们把这种[.c]的文件(或是目标),叫做中间目标。不管怎么样,make会努力自动推导生成目标的一切方法,不管中间目标有多少,其都会执着地把所有的隐含规则和你书写的规则全部合起来分析,努力达到目标,所以,有些时候,可能会让你觉得奇怪,怎么我的目标会这样生成?怎么我的makefile发疯了? 

在默认情况下,对于中间目标,它和一般的目标有两个地方所不同:第一个不同是除非中间的目标不存在,才会引发中间规则。第二个不同的是,只要目标成功产生,那么,产生最终目标过程中,所产生的中间目标文件会被以“rm -f”删除。 

通常,一个被makefile指定成目标或是依赖目标的文件不能被当作中介。然而,你可以明显地说明一个文件或是目标是中介目标,你可以使用伪目标“.INTERMEDIATE”来强制声明。(如:.INTERMEDIATE : mid ) 

你也可以阻止make自动删除中间目标,要做到这一点,你可以使用伪目标“.SECONDARY”来强制声明(如:.SECONDARY : sec)。你还可以把你的目标,以模式的方式来指定(如:%.o)成伪目标“.PRECIOUS”的依赖目标,以保存被隐含规则所生成的中间文件。 

在“隐含规则链”中,禁止同一个目标出现两次或两次以上,这样一来,就可防止在make自动推导时出现无限递归的情况。 

Make会优化一些特殊的隐含规则,而不生成中间文件。如,从文件“foo.c”生成目标程序“foo”,按道理,make会编译生成中间文件“foo.o”,然后链接成“foo”,但在实际情况下,这一动作可以被一条“cc”的命令完成(cc –o foo foo.c),于是优化过的规则就不会生成中间文件。

五、定义模式规则 

你可以使用模式规则来定义一个隐含规则。一个模式规则就好像一个一般的规则,只是在规则中,目标的定义需要有"%"字符。"%"的意思是表示一个或多个任意字符。在依赖目标中同样可以使用"%",只是依赖目标中的"%"的取值,取决于其目标。 

有一点需要注意的是,"%"的展开发生在变量和函数的展开之后,变量和函数的展开发生在make载入Makefile时,而模式规则中的"%"则发生在运行时。 


1、模式规则介绍 

模式规则中,至少在规则的目标定义中要包含"%",否则,就是一般的规则。目标中的"%"定义表示对文件名的匹配,"%"表示长度任意的非空字符串。例如:"%.c"表示以".c"结尾的文件名(文件名的长度至少为3),而"s.%.c"则表示以"s."开头,".c"结尾的文件名(文件名的长度至少为5)。 

如果"%"定义在目标中,那么,目标中的"%"的值决定了依赖目标中的"%"的值,也就是说,目标中的模式的"%"决定了依赖目标中"%"的样子。例如有一个模式规则如下: 

    %.o : %.c ; <command ......>; 

其含义是,指出了怎么从所有的[.c]文件生成相应的[.o]文件的规则。如果要生成的目标是"a.o b.o",那么"%c"就是"a.c b.c"。 

一旦依赖目标中的"%"模式被确定,那么,make会被要求去匹配当前目录下所有的文件名,一旦找到,make就会规则下的命令,所以,在模式规则中,目标可能会是多个的,如果有模式匹配出多个目标,make就会产生所有的模式目标,此时,make关心的是依赖的文件名和生成目标的命令这两件事。 


2、模式规则示例 

下面这个例子表示了,把所有的[.c]文件都编译成[.o]文件. 

    %.o : %.c 
            $(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@ 

其中,"$@"表示所有的目标的挨个值,"$<"表示了所有依赖目标的挨个值。这些奇怪的变量我们叫"自动化变量",后面会详细讲述。 

下面的这个例子中有两个目标是模式的: 

    %.tab.c %.tab.h: %.y 
            bison -d $< 

这条规则告诉make把所有的[.y]文件都以"bison -d <n>;.y"执行,然后生成"<n>;.tab.c"和"<n>;.tab.h"文件。(其中,"<n>;"表示一个任意字符串)。如果我们的执行程序"foo"依赖于文件"parse.tab.o"和"scan.o",并且文件"scan.o"依赖于文件"parse.tab.h",如果"parse.y"文件被更新了,那么根据上述的规则,"bison -d parse.y"就会被执行一次,于是,"parse.tab.o"和"scan.o"的依赖文件就齐了。(假设,"parse.tab.o"由"parse.tab.c"生成,和"scan.o"由"scan.c"生成,而"foo"由"parse.tab.o"和"scan.o"链接生成,而且foo和其[.o]文件的依赖关系也写好,那么,所有的目标都会得到满足) 


3、自动化变量 

在上述的模式规则中,目标和依赖文件都是一系例的文件,那么我们如何书写一个命令来完成从不同的依赖文件生成相应的目标?因为在每一次的对模式规则的解析时,都会是不同的目标和依赖文件。 

自动化变量就是完成这个功能的。在前面,我们已经对自动化变量有所提涉,相信你看到这里已对它有一个感性认识了。所谓自动化变量,就是这种变量会把模式中所定义的一系列的文件自动地挨个取出,直至所有的符合模式的文件都取完了。这种自动化变量只应出现在规则的命令中。 

下面是所有的自动化变量及其说明: 

$@ 
    表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,"$@"就是匹配于目标中模式定义的集合。 

$% 
    仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是"foo.a(bar.o)",那么,"$%"就是"bar.o","$@"就是"foo.a"。如果目标不是函数库文件(Unix下是[.a],Windows下是[.lib]),那么,其值为空。 

$< 
    依赖目标中的第一个目标名字。如果依赖目标是以模式(即"%")定义的,那么"$<"将是符合模式的一系列的文件集。注意,其是一个一个取出来的。 

$? 
    所有比目标新的依赖目标的集合。以空格分隔。 

$^ 
    所有的依赖目标的集合。以空格分隔。如果在依赖目标中有多个重复的,那个这个变量会去除重复的依赖目标,只保留一份。 

$+ 
    这个变量很像"$^",也是所有依赖目标的集合。只是它不去除重复的依赖目标。 

$*  
   这个变量表示目标模式中"%"及其之前的部分。如果目标是"dir/a.foo.b",并且目标的模式是"a.%.b",那么,"$*"的值就是"dir/a.foo"。这个变量对于构造有关联的文件名是比较有较。如果目标中没有模式的定义,那么"$*"也就不能被推导出,但是,如果目标文件的后缀是make所识别的,那么"$*"就是除了后缀的那一部分。例如:如果目标是"foo.c",因为".c"是make所能识别的后缀名,所以,"$*"的值就是"foo"。这个特性是GNU make的,很有可能不兼容于其它版本的make,所以,你应该尽量避免使用"$*",除非是在隐含规则或是静态模式中。如果目标中的后缀是make所不能识别的,那么"$*"就是空值。 

当你希望只对更新过的依赖文件进行操作时,"$?"在显式规则中很有用,例如,假设有一个函数库文件叫"lib",其由其它几个object文件更新。那么把object文件打包的比较有效率的Makefile规则是: 

    lib : foo.o bar.o lose.o win.o 
            ar r lib $? 

在上述所列出来的自动量变量中。四个变量($@、$<、$%、$*)在扩展时只会有一个文件,而另三个的值是一个文件列表。这七个自动化变量还可以取得文件的目录名或是在当前目录下的符合模式的文件名,只需要搭配上"D"或"F"字样。这是GNU make中老版本的特性,在新版本中,我们使用函数"dir"或"notdir"就可以做到了。"D"的含义就是Directory,就是目录,"F"的含义就是File,就是文件。 

下面是对于上面的七个变量分别加上"D"或是"F"的含义: 

$(@D) 
    表示"$@"的目录部分(不以斜杠作为结尾),如果"$@"值是"dir/foo.o",那么"$(@D)"就是"dir",而如果"$@"中没有包含斜杠的话,其值就是"."(当前目录)。 

$(@F) 
    表示"$@"的文件部分,如果"$@"值是"dir/foo.o",那么"$(@F)"就是"foo.o","$(@F)"相当于函数"$(notdir $@)"。 

"$(*D)" 
"$(*F)" 
    和上面所述的同理,也是取文件的目录部分和文件部分。对于上面的那个例子,"$(*D)"返回"dir",而"$(*F)"返回"foo" 

"$(%D)" 
"$(%F)" 
    分别表示了函数包文件成员的目录部分和文件部分。这对于形同"archive(member)"形式的目标中的"member"中包含了不同的目录很有用。 

"$(<D)" 
"$(<F)" 
    分别表示依赖文件的目录部分和文件部分。 

"$(^D)" 
"$(^F)" 
    分别表示所有依赖文件的目录部分和文件部分。(无相同的) 

"$(+D)" 
"$(+F)" 
    分别表示所有依赖文件的目录部分和文件部分。(可以有相同的) 

"$(?D)" 
"$(?F)" 
    分别表示被更新的依赖文件的目录部分和文件部分。 

最后想提醒一下的是,对于"$<",为了避免产生不必要的麻烦,我们最好给$后面的那个特定字符都加上圆括号,比如,"$(<)"就要比"$<"要好一些。 

还得要注意的是,这些变量只使用在规则的命令中,而且一般都是"显式规则"和"静态模式规则"(参见前面"书写规则"一章)。其在隐含规则中并没有意义。 

4、模式的匹配 

一般来说,一个目标的模式有一个有前缀或是后缀的"%",或是没有前后缀,直接就是一个"%"。因为"%"代表一个或多个字符,所以在定义好了的模式中,我们把"%"所匹配的内容叫做"茎",例如"%.c"所匹配的文件"test.c"中"test"就是"茎"。因为在目标和依赖目标中同时有"%"时,依赖目标的"茎"会传给目标,当做目标中的"茎"。 

当一个模式匹配包含有斜杠(实际也不经常包含)的文件时,那么在进行模式匹配时,目录部分会首先被移开,然后进行匹配,成功后,再把目录加回去。在进行"茎"的传递时,我们需要知道这个步骤。例如有一个模式"e%t",文件"src/eat"匹配于该模式,于是"src/a"就是其"茎",如果这个模式定义在依赖目标中,而被依赖于这个模式的目标中又有个模式"c%r",那么,目标就是"src/car"。("茎"被传递) 


5、重载内建隐含规则 

你可以重载内建的隐含规则(或是定义一个全新的),例如你可以重新构造和内建隐含规则不同的命令,如: 

    %.o : %.c 
            $(CC) -c $(CPPFLAGS) $(CFLAGS) -D$(date) 

你可以取消内建的隐含规则,只要不在后面写命令就行。如: 

    %.o : %.s 

同样,你也可以重新定义一个全新的隐含规则,其在隐含规则中的位置取决于你在哪里写下这个规则。朝前的位置就靠前。 


六、老式风格的"后缀规则" 

后缀规则是一个比较老式的定义隐含规则的方法。后缀规则会被模式规则逐步地取代。因为模式规则更强更清晰。为了和老版本的Makefile兼容,GNU make同样兼容于这些东西。后缀规则有两种方式:"双后缀"和"单后缀"。 

双后缀规则定义了一对后缀:目标文件的后缀和依赖目标(源文件)的后缀。如".c.o"相当于"%o : %c"。单后缀规则只定义一个后缀,也就是源文件的后缀。如".c"相当于"% : %.c"。 

后缀规则中所定义的后缀应该是make所认识的,如果一个后缀是make所认识的,那么这个规则就是单后缀规则,而如果两个连在一起的后缀都被make所认识,那就是双后缀规则。例如:".c"和".o"都是make所知道。因而,如果你定义了一个规则是".c.o"那么其就是双后缀规则,意义就是".c"是源文件的后缀,".o"是目标文件的后缀。如下示例: 

    .c.o: 
            $(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $< 

后缀规则不允许任何的依赖文件,如果有依赖文件的话,那就不是后缀规则,那些后缀统统被认为是文件名,如:

    .c.o: foo.h 
            $(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $< 

这个例子,就是说,文件".c.o"依赖于文件"foo.h",而不是我们想要的这样: 

    %.o: %.c foo.h 
            $(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $< 

后缀规则中,如果没有命令,那是毫无意义的。因为他也不会移去内建的隐含规则。 

而要让make知道一些特定的后缀,我们可以使用伪目标".SUFFIXES"来定义或是删除,如: 

    .SUFFIXES: .hack .win 

把后缀.hack和.win加入后缀列表中的末尾。 

    .SUFFIXES:              # 删除默认的后缀 
    .SUFFIXES: .c .o .h   # 定义自己的后缀 

先清楚默认后缀,后定义自己的后缀列表。 

make的参数"-r"或"-no-builtin-rules"也会使用得默认的后缀列表为空。而变量"SUFFIXE"被用来定义默认的后缀列表,你可以用".SUFFIXES"来改变后缀列表,但请不要改变变量"SUFFIXE"的值。 


七、隐含规则搜索算法 

比如我们有一个目标叫 T。下面是搜索目标T的规则的算法。请注意,在下面,我们没有提到后缀规则,原因是,所有的后缀规则在Makefile被载入内存时,会被转换成模式规则。如果目标是"archive(member)"的函数库文件模式,那么这个算法会被运行两次,第一次是找目标T,如果没有找到的话,那么进入第二次,第二次会把"member"当作T来搜索。 

1、把T的目录部分分离出来。叫D,而剩余部分叫N。(如:如果T是"src/foo.o",那么,D就是"src/",N就是"foo.o") 

2、创建所有匹配于T或是N的模式规则列表。 

3、如果在模式规则列表中有匹配所有文件的模式,如"%",那么从列表中移除其它的模式。 

4、移除列表中没有命令的规则。 

5、对于第一个在列表中的模式规则: 
    1)推导其"茎"S,S应该是T或是N匹配于模式中"%"非空的部分。 
    2)计算依赖文件。把依赖文件中的"%"都替换成"茎"S。如果目标模式中没有包含斜框字符,而把D加在第一个依赖文件的开头。 
3)测试是否所有的依赖文件都存在或是理当存在。(如果有一个文件被定义成另外一个规则的目标文件,或者是一个显式规则的依赖文件,那么这个文件就叫"理当存在") 
    4)如果所有的依赖文件存在或是理当存在,或是就没有依赖文件。那么这条规则将被采用,退出该算法。 

6、如果经过第5步,没有模式规则被找到,那么就做更进一步的搜索。对于存在于列表中的第一个模式规则: 
    1)如果规则是终止规则,那就忽略它,继续下一条模式规则。 
2)计算依赖文件。(同第5步) 
3)测试所有的依赖文件是否存在或是理当存在。 
4)对于不存在的依赖文件,递归调用这个算法查找他是否可以被隐含规则找到。 
5)如果所有的依赖文件存在或是理当存在,或是就根本没有依赖文件。那么这条规则被采用,退出该算法。 

7、如果没有隐含规则可以使用,查看".DEFAULT"规则,如果有,采用,把".DEFAULT"的命令给T使用。 

一旦规则被找到,就会执行其相当的命令,而此时,我们的自动化变量的值才会生成。

使用make更新函数库文件 
——————————— 

函数库文件也就是对Object文件(程序编译的中间文件)的打包文件。在Unix下,一般是由命令"ar"来完成打包工作。 

一、函数库文件的成员 

一个函数库文件由多个文件组成。你可以以如下格式指定函数库文件及其组成: 

    archive(member) 

这个不是一个命令,而一个目标和依赖的定义。一般来说,这种用法基本上就是为了"ar"命令来服务的。如: 

    foolib(hack.o) : hack.o 
            ar cr foolib hack.o 

如果要指定多个member,那就以空格分开,如: 

    foolib(hack.o kludge.o) 

其等价于: 

    foolib(hack.o) foolib(kludge.o) 

你还可以使用Shell的文件通配符来定义,如: 

    foolib(*.o) 


二、函数库成员的隐含规则 

当make搜索一个目标的隐含规则时,一个特殊的特性是,如果这个目标是"a(m)"形式的,其会把目标变成"(m)"。于是,如果我们的成员是"%.o"的模式定义,并且如果我们使用"make foo.a(bar.o)"的形式调用Makefile时,隐含规则会去找"bar.o"的规则,如果没有定义bar.o的规则,那么内建隐含规则生效,make会去找bar.c文件来生成bar.o,如果找得到的话,make执行的命令大致如下: 

    cc -c bar.c -o bar.o 
    ar r foo.a bar.o 
    rm -f bar.o 

还有一个变量要注意的是"$%",这是专属函数库文件的自动化变量,有关其说明请参见"自动化变量"一节。 


三、函数库文件的后缀规则 

你可以使用"后缀规则"和"隐含规则"来生成函数库打包文件,如: 

    .c.a: 
            $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $*.o 
            $(AR) r $@ $*.o 
            $(RM) $*.o 

其等效于: 

    (%.o) : %.c 
            $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $*.o 
            $(AR) r $@ $*.o 
            $(RM) $*.o 


四、注意事项 

在进行函数库打包文件生成时,请小心使用make的并行机制("-j"参数)。如果多个ar命令在同一时间运行在同一个函数库打包文件上,就很有可以损坏这个函数库文件。所以,在make未来的版本中,应该提供一种机制来避免并行操作发生在函数打包文件上。 

但就目前而言,你还是应该不要尽量不要使用"-j"参数。 

  

后序 
—— 

终于到写结束语的时候了,以上基本上就是GNU make的Makefile的所有细节了。其它的产商的make基本上也就是这样的,无论什么样的make,都是以文件的依赖性为基础的,其基本是都是遵循一个标准的。这篇文档中80%的技术细节都适用于任何的make,我猜测"函数"那一章的内容可能不是其它make所支持的,而隐含规则方面,我想不同的make会有不同的实现,我没有精力来查看GNU的make和VC的nmake、BCB的make,或是别的UNIX下的make有些什么样的差别,一是时间精力不够,二是因为我基本上都是在Unix下使用make,以前在SCO Unix和IBM的AIX,现在在Linux、Solaris、HP-UX、AIX和Alpha下使用,Linux和Solaris下更多一点。不过,我可以肯定的是,在Unix下的make,无论是哪种平台,几乎都使用了Richard Stallman开发的make和cc/gcc的编译器,而且,基本上都是GNU的make(公司里所有的UNIX机器上都被装上了GNU的东西,所以,使用GNU的程序也就多了一些)。GNU的东西还是很不错的,特别是使用得深了以后,越来越觉得GNU的软件的强大,也越来越觉得GNU的在操作系统中(主要是Unix,甚至Windows)"杀伤力"。 

对于上述所有的make的细节,我们不但可以利用make这个工具来编译我们的程序,还可以利用make来完成其它的工作,因为规则中的命令可以是任何Shell之下的命令,所以,在Unix下,你不一定只是使用程序语言的编译器,你还可以在Makefile中书写其它的命令,如:tar、awk、mail、sed、cvs、compress、ls、rm、yacc、rpm、ftp……等等,等等,来完成诸如"程序打包"、"程序备份"、"制作程序安装包"、"提交代码"、"使用程序模板"、"合并文件"等等五花八门的功能,文件操作,文件管理,编程开发设计,或是其它一些异想天开的东西。比如,以前在书写银行交易程序时,由于银行的交易程序基本一样,就见到有人书写了一些交易的通用程序模板,在该模板中把一些网络通讯、数据库操作的、业务操作共性的东西写在一个文件中,在这些文件中用些诸如"@@@N、###N"奇怪字串标注一些位置,然后书写交易时,只需按照一种特定的规则书写特定的处理,最后在make时,使用awk和sed,把模板中的"@@@N、###N"等字串替代成特定的程序,形成C文件,然后再编译。这个动作很像数据库的"扩展C"语言(即在C语言中用"EXEC SQL"的样子执行SQL语句,在用cc/gcc编译之前,需要使用"扩展C"的翻译程序,如cpre,把其翻译成标准C)。如果你在使用make时有一些更为绝妙的方法,请记得告诉我啊。 

回头看看整篇文档,不觉记起几年前刚刚开始在Unix下做开发的时候,有人问我会不会写Makefile时,我两眼发直,根本不知道在说什么。一开始看到别人在vi中写完程序后输入"!make"时,还以为是vi的功能,后来才知道有一个Makefile在作怪,于是上网查啊查,那时又不愿意看英文,发现就根本没有中文的文档介绍Makefile,只得看别人写的Makefile,自己瞎碰瞎搞才积累了一点知识,但在很多地方完全是知其然不知所以然。后来开始从事UNIX下产品软件的开发,看到一个400人年,近200万行代码的大工程,发现要编译这样一个庞然大物,如果没有Makefile,那会是多么恐怖的一样事啊。于是横下心来,狠命地读了一堆英文文档,才觉得对其掌握了。但发现目前网上对Makefile介绍的文章还是少得那么的可怜,所以想写这样一篇文章,共享给大家,希望能对各位有所帮助。 

现在我终于写完了,看了看文件的创建时间,这篇技术文档也写了两个多月了。发现,自己知道是一回事,要写下来,跟别人讲述又是另外一回事,而且,现在越来越没有时间专研技术细节,所以在写作时,发现在阐述一些细节问题时很难做到严谨和精练,而且对先讲什么后讲什么不是很清楚,所以,还是参考了一些国外站点上的资料和题纲,以及一些技术书籍的语言风格,才得以完成。整篇文档的提纲是基于GNU的Makefile技术手册的提纲来书写的,并结合了自己的工作经验,以及自己的学习历程。因为从来没有写过这么长,这么细的文档,所以一定会有很多地方存在表达问题,语言歧义或是错误。因些,我迫切地得等待各位给我指证和建议,以及任何的反馈。 

最后,还是利用这个后序,介绍一下自己。我目前从事于所有Unix平台下的软件研发,主要是做分布式计算/网格计算方面的系统产品软件,并且我对于下一代的计算机革命——网格计算非常地感兴趣,对于分布式计算、P2P、Web Service、J2EE技术方向也很感兴趣,同时,对于项目实施、团队管理、项目管理也小有心得,希望同样和我战斗在“技术和管理并重”的阵线上的年轻一代,能够和我多多地交流。我的MSN是:haoel@hotmail.com(常用),QQ是:753640(不常用)。(注:请勿给我MSN的邮箱发信,由于hotmail的垃圾邮件导致我拒收这个邮箱的所有来信) 

我欢迎任何形式的交流,无论是讨论技术还是管理,或是其它海阔天空的东西。除了政治和娱乐新闻我不关心,其它只要积极向上的东西我都欢迎! 

最最后,我还想介绍一下make程序的设计开发者。 

  

首当其冲的是: Richard Stallman   

开源软件的领袖和先驱,从来没有领过一天工资,从来没有使用过Windows操作系统。对于他的事迹和他的软件以及他的思想,我无需说过多的话,相信大家对这个人并不比我陌生,这是他的主页:http://www.stallman.org/ 。这里只贴上一张他的近照:

第二位是:Roland McGrath   

个人主页是:http://www.frob.com/~roland/ ,下面是他的一些事迹: 

1)  合作编写了并维护GNU make。 

2)  和Thomas Bushnell一同编写了GNU Hurd。 

3)  编写并维护着GNU C library。  

4)  合作编写并维护着部分的GNU Emacs。  

  

在此,向这两位开源项目的斗士致以最真切的敬意。 


(全文完

Monday, July 16, 2012

zz 我的百老汇看戏攻略:各戏戏评及最便宜票的买法

来源: 潘凯的日志

我去年九月份离开美东来到加州,到现在已经四个多月了。这四个月,我一直想动笔写一个百老汇看戏攻略,给每个戏一个简短的戏评,分享每个戏最便宜票的买法,一直拖到今天才动笔。

 

在我离开纽约以前,我本打算看完所有那时正在上演的Broadway Shows,结果最后还有两个没有看成。不过已经看成26个了,足够满足了。

 

如果有人问我,去纽约最值得干的事情是什么,我一定会回答:看戏!全世界最好的音乐剧和英语话剧都汇集在百老汇。这,才是所有New Yorkers的精神食粮。纽约是个脏乱差,危险喧嚣嘈杂,地铁里充满了尿味的城市。但正是这些精神食粮,让纽约成为我最喜爱的,念念不忘的城市。纽约是天堂和地狱的结合体,broadway shows,一定是这天堂中最重要的一部分。

 

注意,我上面说百老汇有世界上最好的英语话剧,而不是所有话剧。对比了中国话剧和百老汇的话剧之后,我觉得咱中国的顶级话剧跟百老汇的没什么差距其实。。。而音乐剧,那百老汇一定是全世界最好的,只有全世界最好的戏才有资格拿到百老汇演,全世界最好的演员都梦想来百老汇演出。当然英国的西区剧院(West End)也很好,不过还是不如百老汇的。

 

首先明确一个概念,什么叫做Broadway shows。 Broadway shows就是所有在Broadway theaters 上演的音乐剧(Musical)和话剧(Play)。那么什么是Broadway theaters呢?不是指Broadway大街周围的剧院,而是指获批准加入Broadway theater联盟的剧院。这些戏院总共有40家,其中39家分布在44街到53街的时代广场附近,另1家在66街的林肯中心。这些剧院是所有纽约剧院的精华。还有一些相对较好的剧院,只能叫做off broadway theaters,这些剧院也有个别是分布在时代广场周围的,但是它们并不是顶级的Broadway theaters。

 

在这里插一段,有关音乐剧(Musical),和歌剧(Opera)的区别。简单的讲,音乐剧的是为了讲故事而唱,而歌剧是为了唱而讲故事。所以说,音乐剧的重心在于故事和表演,歌剧的重点在于唱腔。现在,大家明白为什么京剧叫Beijing Opera而不是Beijing Musical了吧,因为京剧的重点是唱腔而不是讲故事。Broadway shows里是没有歌剧(Opera)的,只有音乐剧(Musical)和话剧(Play)。

 

提起百老汇,不得不说到托尼奖(Tony Award)。托尼奖是和奥斯卡,格莱美等等并列的世界顶级奖项,评选当年最好的音乐剧和话剧。与奥斯卡,格莱美不一样的是,只有Broadway shows有参选托尼奖的资格。但是托尼奖仍然是权威的世界顶级奖项,因为大家默认世界上最好的戏都在百老汇呢。托尼奖每年评出最佳话剧,最佳重排话剧,最佳音乐剧,最佳重排音乐剧四个顶级奖项。为什么要有重拍话剧和重排音乐剧呢?那是因为百老汇每年都会上演很多几年前或者几十年前上演过的戏,把这些戏和原创戏放在一起评选是不公平的。当然,托尼奖也有最佳男女主角男女配角导演编剧这些重量级奖项,还有最佳舞台效果作词作曲乐队灯光音效服装舞蹈编排这些技术奖项。

 

有人说,这么顶级的戏,票价很贵吧,错。如果你是学生,看了我的这篇文章,绝大多数的戏你都能花40美元以下的票价看到,大多数都在20多美元左右。如果你已经不是学生了,你能以40块以下的价格买到大多数的戏票。(有一些戏院只给学生特价票哦)

 

好了,那该怎么买到最便宜的戏票呢?一定不要上网买票!一定不要去位于时代广场最中间的那个人山人海的总售票处买票!这些都是中介,而且只卖正价票!去哪买票呢?你想要看什么戏,就去那个戏院的售票处(Box Office)买票。

 

每个戏的最便宜的票的卖法都不一样。总体来说分三种:

1. General Rush ticket/Student rush ticket. 所谓Rush ticket,指的是在戏的上演当天去买的票。这些戏院会留一定量的当天的票在当天卖。戏院售票处在周一到周六是10点开门,周日是12点开门。如果是很好的戏,你会发现9点多就会有个长队等在戏院外,这就是大家在排队等着买rush ticket呢。当然,Student rush ticket是你必须出示学生证才能买。大多数Student rush都是一张学生证能买两张票的。小秘密:其实查的不是很严,你拿一张过期的学生证,售票员不一定看得清哦。

2. Stand Room Only (SRO) Ticket. 所谓SRO,不是站票,而是位置不那么好的票。有些剧院卖这样的票。但是我发现几乎所有的SRO的位置都还不错,只是远了点,没关系的。唯一的卖真正“站票”的戏,是神戏the Book of Mormon. 那是因为它实在是太火了。

3. Lottery rush ticket,抽奖票。就是在开戏前两个半小时,去拿个签,开戏前两小时抽奖。如果能抽到,就能花二三十买第一排的票,注意,是第一排哦!那真是爽死了。在去年八九月的时候,有四个戏有Lottery ticket,我人品很好抽到过其中的两个。另外的两个,你永远也抽不到的。因为每天只有十几张票,而会有500多人来抽票,妈的那帮时代广场的banker们估计每天下楼遛达的时候都过来抽一次。。。这两个戏,是Wicked,和神戏the Book of Mormon.

还有一个特例,是War Horse,我在下面再说怎么买War Horse的便宜票。

 

下面评论一些我认为值得去看的戏吧,并附上其最便宜的买票方式。注意,便宜票价这些都是去年八九月份的情况,有可能那个戏院已经改制度了。而一些我没看过的戏,我也不知道它们的便宜票是怎么卖的,不过应该都是上面那三种。我找到一个链接,上面具体说每个戏的买票方式。

http://www.playbill.com/celebritybuzz/article/82428-Broadway-Rush-Lottery-and-Standing-Room-Only-Policies

不过这个文章是11年12月的。至于最新的最便宜票的买票的方式怎么查,我当年的方式是,一大早一个剧院一个剧院跑去问。。。有当天的便宜就买,没有就记住这一家的方式然后换一家去买。。。

 

大家想去看某一个戏,怎么知道演这个戏的剧院的在哪呢?Google啊。给出个链接:

http://www.theatermania.com/broadway/theater-map/

另外提一句,Theatermania.com 这个网站很好,如果你像我一样看戏上瘾了的话,就上这个网站吧。

 

如果听不懂怎么办?先看剧透再去看戏啊。没关系百老汇的悬疑成分都不怎么重,看了剧透也不影响的。剧透在哪找?英文剧透上wiki,中文剧透往下看。

 

好啦,言归正传,说说我当年看的,且现在还在上演的戏吧。唉。。。我上网看了一眼,更新了好多啊,有好多我觉得好看的戏都下线了。。。算了不提了。

按字母顺序排列。有些已经下线的戏,但是正在全美巡演的,我也写上来了。

 

Anything Goes  一切皆有可能  特别推荐

便宜票:General Rush,$30

11年的托尼奖最佳重排音乐剧,最佳女主角,最佳编舞。这戏是翻排了1934年的一个喜剧,如果光看剧情的话,一点吸引力都没有,巨俗无比,只是个有关在轮船上泡妞的老式喜剧。但是,因为有了最佳女主角Sutton Foster,一切都不一样了。换句话说,这个戏就是为了去看Sutton Foster的。她唱歌巨好跳舞巨好身材巨好长得巨漂亮演的巨卖力,可御姐可萝莉,可做害羞状扮清纯,可坐在男人身上蹭大腿,实在是人间极品啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊!!!!我了个去不行了我控制不住了太喜欢她了。。。其中一曲主题曲Anything Goes极其热闹欢乐,群舞特别漂亮,Sutton Foster领唱领舞。在我看来,正式靠这个曲段,Anything Goes力压The Book of Mormon,拿到了11年托尼奖最佳编舞。

如果你去看戏的时候,恰好Sutton Foster不在,换B角来演的,那你就只能自认倒霉了。。。我的建议就是,像这种某个人特别强的戏,要挑周六晚上或者周日下午来看,一般都是A角在演。当然,也有其他更难以预料的情况,比如我知道Sutton Foster曾经出差过一周,论坛上的人都开始骂街了。。。

 本戏12年4月下线,想看的要抓紧了。

 

 

 

Billy Elliot   比利艾略特   推荐

这个戏已经在百老汇演了5年,12年1月8号刚刚下线。但是这个戏很经典,剧团会去各大城市巡演。比如现在旧金山就有演。

06年的托尼奖最大赢家,最佳音乐剧最佳男主角最佳剧本等等。作曲是英国歌皇Elton John。故事讲的是80年代英国矿工大罢工,一个矿工家的穷小男孩偶尔接触了芭蕾舞,遭到家里反对,而且罢工时还有各种流血冲突。但是他有个好老师。最后凭着自己的努力和执着,考上了皇家芭蕾舞学院。

正太控和萝莉控会很喜欢这个戏的。Billy是个帅正太。Billy的最好的朋友也是个很萌的小正太,有同性恋倾向的他每天想着亲Billy。还有20个和Billy一起学舞的小萝莉,穿着芭蕾舞服,非常可爱!

我觉得这个戏的神奇之处在于,你得找三四个十一二岁的小男孩演男主角(因为小男孩要轮着上演男主,它们还在上学呢。。。)这几个小男孩要芭蕾舞基础很过硬,还要能唱能说能演。真是很佩服。但问题是这三四个演男主角的小男孩水平可能参差不齐,我当时看的那个小男主角,芭蕾跳的很好,演技也不错,但是唱功就不咋地了。。。

 

 

 

The Book of Mormon    摩门经    特别特别特别推荐,神戏必看!

在我11年9月离开纽约时,本戏12年3月以前的200块以下的正价票已经卖光了。便宜票有两种,一种是lottery ticket,正像我上面所说,会有500多人抽十几张票,路都被堵上了,你是不可能抽到了。另一种就是$27的SRO了,这是真正的站票,会在开戏前一小时开始出售,总共十多张。我的买票方式是:晚上8点的戏,我下午1点搬个小板凳带着书去剧院门口排队。然后下午3点,这个队就排满了。然后队伍里的人吃吃喝喝聊天到七点买票。我后面的大哥,已经是第三次看这个戏了,还是排队5个小时买到的票。这也是我最推荐的买票方式。最后我是靠在栏杆上站着看完两个半小时的戏的,不过一点也没觉得累!

本戏是11年托尼奖最佳音乐剧最佳女配角最佳导演最佳编剧以及几乎所有的技术奖项。而且,被誉为本世纪最牛逼的托尼奖音乐剧。

这个剧本是南方公园(South Park)的那帮人搞出来的,所以,这个戏也被誉为史上最脏的戏。看戏之前,先查清楚cunt,scrotum,maggot,clitoris,circumcise等等这些词汇:)。当然,你还要对摩门教有个基础的了解。摩门教可不是基督教的一派,它的正式名称是The Church of Jesus Christ of Latter-day Saints(耶稣基督后期圣徒教会)。他们除了信旧约和新约,还信the Book of Mormon(摩门经)。他们认为摩门经也是上帝留给人类的。19世纪,摩门教的创教先知,纽约州人Joseph Smith(斯密・约瑟)在自家后院挖出了刻着字的金盘子,然后一个天使告诉他这个是上帝留下来的第三本书(前两本是旧约和新约)。他把这些字重新写成了摩门经,发展了很多信徒,但是一般美国人都驱赶他们这些异端。于是他们到处迁徙,Joseph Smith也在迁徙中被杀了。但是他发展了最重要的摩门领袖,把摩门教发展壮大的领袖Brigham Young(杨百翰)。杨百翰带领摩门信徒定居到了犹他州的盐湖城(Salt Lake City)。现在犹他州的大多数居民都是摩门信徒。最近摩门教风头很盛,因为共和党有两个总统候选人是摩门教徒:现在票数领先并很可能将最终当选共和党总统候选人的Mitt Romney,和前任驻华大使洪博培。也就是说,下任美国总统很可能是摩门信徒(以前所有的美国总统都是基督徒或天主教徒)。总体来说,摩门教是很教人向善的宗教,盐湖城的犯罪率从来都是美国最低。摩门信徒也都很纯良,要不然这戏也不敢拿摩门教开涮。

言归正传,本戏一开始,出现了一群摩门年轻人准备去世界各地传教。这里面就有两个男主角:高帅富和矮胖挫。他们被分配去乌干达传教。高帅富从小就是好学生,当然信心满满要去发展更多非洲人进入摩门教会。而矮胖挫从小就脑子不灵光,他自己也觉得自己是个sb。他对非洲的唯一了解是狮子王,所以他把自己定位成高帅富的追随者和跟屁虫,也很高兴地去了。但是一去乌干达,发现当地黑人贫穷愚昧地发指,大多数人都有艾滋病。当地传说f**k处女就能治愈艾滋病,但是处女不多了,于是男人就f**k babies。。。还有个杀人不眨眼的军阀统治他们。当地人经常被基督徒来传教,但是生活还是很悲惨,于是他们都不信上帝。高帅富就说,他们那些传教的都是基督徒,我们摩门教比他们牛逼,于是开始给当地人讲摩门经。当地人根本不理,还给他们唱了首歌,叫fuck you god. 于是高帅富很无奈地想退出。但矮胖挫很执着地继续传教,给黑人们编了很多自己胡编的小段子,大多数来源于星球大战的情节。。。他告诉当地人不可以f**k baby, 就说Joseph Smith当年也得了爱滋,要f**k baby被上帝制止了。坏军阀强制所有女孩circumcise clitoris,他就编造说当年杨百翰也要circumcise自己女儿的clitoris,结果被上帝惩罚,自己的脸被变成了一个clitoris,后来被Joseph Smith救了才治好。。。各种胡编乱造把当地黑人扯服了,其中还吸引了一个很纯的黑人女孩(托尼奖最佳女配)。黑女孩跟她的乡亲们一起试图理解摩门经,她说salt lake city其实是想象中的天堂,并不真正存在。摩门经中的故事(其实都是矮胖挫编的)虽然粗俗,但都是能真正帮助我们的,真正能让我们与人为善,脱离苦海并且更接近天堂的。于是矮胖挫发展了很多黑人接受洗礼成为摩门信徒。摩门教总部听说了他们在非洲的成就,很高兴就来视察。当地人就基于矮胖挫的给他们讲的故事,搞了个有关伟大先知Joseph Smith的戏给摩门教总部的人看。这一段,我操,太脏太搞笑了!整个戏院里所有观众笑的屎都要出来了!结果当然摩门教总部的人就怒了,说你们都不是摩门教徒!所有的传教士也都被开除了。。。于是他们在非洲创立了新的宗教,帮助当地人向善并且更加接近天堂,就连杀人不眨眼的军阀都被发展进来了。

别看这个戏很脏,但是制作特别精良,歌曲特别好听,舞台效果特别好(包揽了一堆托尼的技术奖项)。这个戏也很有深意的。导演接受采访说,这部戏是“无神论者对所有宗教的致敬”。导演暗示的是,所有的宗教一开始,其实都像矮胖挫那样,为了帮助当地人,而胡编乱造的,但是宗教却给人带来了无比积极的意义。为了体现这种思想,他们只能拿最善良淳朴的摩门教开涮了,你让他们开涮基督教伊斯兰教,给他们十个胆子他们也不敢啊!最后,我还看到了摩门教总部对这部戏的评价,他们甚至都没有谴责,只是说:“这部戏也许能带给你一晚上的开心,但是摩门经,却是能指引你一辈子的。”我擦!摩门教果然纯良啊,陡然对摩门教好感大增!

 

 

 

 

Chicago   芝加哥    推荐

便宜票:General Rush $36.50

经典!70年代的戏,当年遗憾没有拿到托尼奖。97年重排,拿到最佳重排音乐剧。04年改编的电影拿到奥斯卡。成为几个拿到托尼奖和奥斯卡奖的戏之一。另外的是什么?音乐之声,西区故事,窈窕淑女等等。

剧情就不多说了,大家去查查电影剧情就好了,电影是完全尊重音乐剧原著的。讲的是一个黑幕事件。一心想出名的女主角谋杀了情夫。而男主角,他的律师控制媒体,煽动民众。女主角反倒在监狱成名了。最后被无罪释放。与女二号(另一女杀人犯,原来的大明星)组成了监狱女杀人犯二人组重出江湖。

这个戏讲的是美国的法律黑幕,媒体被操控,民众易被煽动。04年我看电影的时候只是觉得剧情很有趣,歌很好听。现在想想,很多国内的事件都有芝加哥的影子。比如药家鑫事件等等。不多说了。

整个戏的曲风都是爵士和布鲁斯,特别好听而且够劲。开场曲All that jazz(爵士春秋),中间的cell block tango(牢房探戈),还有女主角丈夫唱的Mister cellophane(透明先生),都是名曲。

看着戏的乐趣还在于,前一天把电影看了,第二天再来看戏,看看同样的剧情同样的歌,处理方法有什么不同。同样的台词,不同的演员会有什么不同的语气。总的来说,电影和戏都很好,但是我感觉电影比戏要好一些。电影的演员阵容也比戏强大。电影中扮演女二号而拿到奥斯卡最佳女配的凯瑟琳泽塔琼斯(我的最爱)本身就拿过托尼奖最佳女主,唱功表演都是顶级。女一号是芮妮齐薇格演的,也是很牛逼的演员。而我去看戏时,这戏刚刚换班子,一群主角都是新换的,可能磨合的不够好。不知现在怎么样了。

 

 

 

Follies    讽刺剧     不怎么推荐(个人意见)

便宜票: student rush $37

11年8月底新上映我就去看了。重排的70年代拿过托尼奖的戏。此戏演员阵容可谓强大,饰演女主角的老女人Bernadette Peters是两次托尼奖得住。其他演员也都是很牛逼的。但是此戏给我的感觉就像是一群牛逼的老biang们出来玩票的,不太认真。也许是我看的太早他们还没磨合好呢吧。

剧情是这样的,20年代有个很火的舞台剧叫Follies,然后60年代那个戏院要拆了。于是戏院的主管就把当年的主演们请回来了,让他们搞个重聚,顺便告别一下他们曾经辉煌的地方。那些曾经的帅哥美女都已经老了,但是他们年轻的魂魄还都留在戏院里。两对男女主角在他们年轻魂魄的注视下,发生了四角关系。

我觉得剧情是特别好的,尤其那种年轻纯真的魂魄看着年老色衰志向已和当年不一样的自己的感觉。只是本戏的处理有些拖沓了,曲风我也不太喜欢,演员也不在状态。。。大家可以去看看,毕竟这是曾经拿过托尼奖的戏,而且那群老演员们都是重量级的,没准他们现在已经来状态了呢。

12年1月22日下线,要看赶紧去啊!

 

 

 

Hair       毛      重磅推荐神戏啊!可惜已经下线了

但是!这个剧团正在全美巡演!我知道他们前一段时间在达拉斯和费城都演过。如果这个戏在你的城市上演,一定要去看啊!

这个1968年戏是有很高历史地位的。它恰好在越战最关键的时间发出了要做爱不要作战的宣言(Make love not war)。当时由于题材敏感,过于性解放(有全裸情节和各种性暗示动作),争议特别大。曾被观众往舞台扔炸弹炸死过演员,去墨西哥城演了一场就被全城人民赶回了美国等等。。。此后它被多次重排,几乎每次重排都能拿托尼奖最佳重排音乐剧。

这个戏讲的是越战期间纽约贫民区一伙二十多个人的年轻嬉皮士的故事,主题是嬉皮文化,性解放,反战。给我的感觉是,这个戏只是给了一个剧本的雏形,而让二十多个不同肤色的演员(也有华人女孩儿哦)在舞台上天性释放。这二十个演员会在台上台下窜来窜去,彻底打破第四堵墙(演员和观众的界限)。我当时人品好抽中了这个戏的lottery ticket,坐在了第一排,爽死我了。被演员各种蹭啊,还会被发反战的小传单和小白花,被握住手喊反战宣言。最后一首曲子时前排观众会被叫到台上和演员一起唱一起谢幕。如果你坐在第二层,也没关系,因为也会有演员跑到第二层去带动你的。这部戏上半部分特别high,下半部分特别感人,我后面的几个中年女人是哭着看完的,也许是这个题材触动了她们当年的神经吧。

注意:二十多人集体全裸发生在第一幕最后,当全裸发生时,千万不要显得很惊讶,要装得很明白那些角色的情绪,是他们的愤怒情绪爆发导致不得不全裸的,这不是色情。明白了吗?至少我周围的观众都是这样做的。另外,一定要抓紧看,当时我还没看够呢,就暗场了。。。

 

 

 

Jersey Boys      泽西男孩          特别推荐

便宜票:Student Rush: $27

一个jukebox musical(点唱机音乐剧,就是用很多老歌串成的戏)。05年的托尼奖最佳音乐剧和最佳男主。这是唯一一个获得托尼奖的jukebox musical

本戏讲的是披头士之前美国最火的男孩组合Four Seasons(四季乐队)的故事。一开始讲的是四季乐队的建队史,这四个天才男孩聚在一起,但每个人都对乐队有自己的理解。所以这个戏分成了四个部分,每部分以其中一个男孩的视角讲述故事。最后四人意见不合,乐队解散,主唱Frankie Valli单飞后成为歌王。他单飞后又有一段辛酸的故事。

这个戏用的全是Four Seasons和Frankie Valli的老歌。那些60年代的老歌真的特别特别好听。Frankie Valli的那首Can't Take My Eyes Off You是很有名的,看戏前应该听一听。

 

 

 

The Lion King      狮子王           我没看过但是肯定推荐

虽然没看过,但是也写在这里代表我的怨念。这个戏没有便宜票卖,只有最低$140的正价票。我看不起。。。。可以肯定是极为牛逼的儿童剧。以后有儿子带他来看吧。。。

 

 

 

Mamma Mia      妈妈咪呀          推荐

便宜票:Student Rush: $31.50

又High又热闹,情节简单音乐好听的jukebox musical(点唱机音乐剧,就是用很多老歌串成的戏),世界游客的最爱!

歌曲用的全是ABBA乐队的歌,编剧把这些歌串成了一个简单的洗具。小女主角要结婚,想让爸爸来参加婚礼。但根据她妈的日记,他有三个可能的爸。结果她发信把三个爸都叫来了,她妈一看慌了!Mamma Mia!我艹老情人们都来了,羞煞了人家的小心肝啊也么哥。最后不光小女主角完美结婚了,她妈也跟其中一个老男人旧情复燃了,她妈的两个闺蜜跟另外两个老男人谈恋爱了,真是欢乐的大团圆啊!最后还要加唱三首歌,全场观众一起扭啊!对了,观众里面会有很多Gay的,因为ABBA乐队的歌是小Gay们的最爱,至于为什么,我也不知道。。。

另外:改编的电影特别烂,虽然有007出演,千万别看。

 

 

 

Mary Poppins     玛丽波平斯阿姨     特别推荐

便宜票:Student Rush: $32

除了狮子王之外,迪斯尼的又一儿童剧力作。成人去看也没有任何问题。华丽的舞台效果,很炫的魔法,女主角很漂亮,男主角很帅,还会脑袋朝下“站”在房顶上一遍跳舞一边唱。

 

 

 

Memphis       孟菲斯         这个是好戏啊强力特别推荐!

便宜票:Student Rush: $26.50

10年的托尼奖最佳音乐剧。美国的主旋律剧,黑白种族和谐主题。讲的是美国第一位玩黑人音乐的白人DJ的故事。故事发生在50年代的美国,音乐之城孟菲斯还在深深的种族隔离中。男主角,放荡不羁的小白人Huey误入地下黑人音乐酒吧,迷上了黑人音乐,也迷上了酒吧老板的妹妹,爱好唱歌的大眼睛黑人小美女Felicia。后来他找到了电台DJ的工作,违反规定放了黑人音乐,没想到一下子火了。但是因为种族隔离,Huey和Felicia只能偷偷恋爱,不然就会被市民殴打,而且huey他妈和Felicia他哥也都强烈反对他们的结合。Huey在电台力捧Felicia,Felicia也在当地也成名了。后来Huey当上电视台主播,各种顺风顺水,家庭也支持他俩的爱情了。但是在电视直播中他吻了Felicia导致他丢了工作。纽约的星探让Felicia去纽约发展,这是Felicia的成名梦想。Felicia想带着Huey走,但是Huey不愿意离开家乡孟菲斯。几年后,Felicia成名全美巡演,又在孟菲斯遇到了落魄的Huey。

曲风是50年代的黑人音乐,非常够劲。女主角是我见过的最好看的黑女人了!这个戏我看了两遍。

 

 

 

The Phantom of the Opera     歌剧魅影       经典中的经典当然推荐了

便宜票:SRO $26.5,只有周一到周四有$26.5的SRO。这个SRO票就是坐得远一点而已,不怎么影响观看的。要提前一两周去剧院买。不然可能抢不到。

88年几乎包揽了所有的托尼奖,在百老汇演了23年长盛不衰,是百老汇史上最长时间的戏。(第二名是82-00年上演了18年的Cat)于此同时,世界各地都在上演,是全世界影响最大的broadway show了。

曲风是歌剧风格加上流行元素。剧情就不多说了。总之很经典一定要看哦!

 

 

 

Priscilla Queen of the Desert    沙漠妖姬         这个是好戏啊强力特别推荐!

便宜票:Lottery : $40。我当年看的时候他们家的Lottery制度刚刚施行,没多少人抽,我就抽中了,坐第一排看的。现在不知道会不会有很多人去抽票了。。。

同性恋戏。腐女必看!去了之后,你就明白什么叫帅哥都有男朋友了。观众里那一对对啊,都是超级帅啊有木有!!直男去了没准就弯了,啊开个玩笑啊,直男看了也不会反感的。舞台特别炫,情节特别high!

故事发生在澳大利亚。Tick是在悉尼工作的变装皇后(drag queen,就是男扮女装上台演出的人),当然也是个同性恋。他原来还是直男的时候在澳大利亚中部小镇Alice Springs娶妻生子。现在他的前妻当上了小镇里的赌场经理,就邀请他去表演,顺便看看他的儿子。于是,Tick叫上了他的男朋友Felicia,和男变女的变性人Bernadette同去。三个男(女?)主角买了辆大巴士,把里面装饰得特别漂亮,然后就上路了。一路上吃喝玩乐。当然也遭遇了各种劫难,因为守旧的传统人民经常追打他们。。。Bernadette也找到了自己人生伴侣,一个被越南老婆折磨得不行的汽车修理工,也是主角里唯一的直男。后来他们终于到达了目的地,成功地Tick前妻的赌场里演出。Tick的前妻和8岁儿子也对Tick完全理解,儿子还要随Tick一起去悉尼游玩。作为一个Gay,他的人生终极理想全实现了!!

别看情节挺简单的,但是舞台处理比我说的复杂多了!各种Bling Bling啊!演Felicia的那个演员真是绝了,长得帅还各种抛媚眼啊,但是也没引起我这样的绝对直男的丝毫不适。那个8岁的儿子也特别出彩,一曲You will always in my mind萌爆了!

 

 

Rent             吉屋出租                经典当然推荐了可惜08年下线了,现在off-broadway有上演

Rent此段一开始在本文中没有,因为评论中很多人提到,我就放上来了。Rent从96年开始上演了12年,08年下线了。我人品很好,08年来美国短期游玩时去了纽约两天,看到了最后几场的Rent。可惜时间过去太久了,加上我当年英语不好没太弄懂,情节都淡忘了。。。

96年的托尼奖最佳音乐剧。这也是在全球很有影响力的一部戏。

其实Rent就翻译成”租金“就好了,”吉屋出租“是改编的电影的中文译名,很误导人。

故事讲的是一群纽约下东区一群交不起的租金的年轻人为了生存而挣扎的故事。故事有关贫穷,艾滋病,同性恋,死亡,还有各种各样有关底层人民的社会问题。曲风是摇滚。

如果你特别感兴趣就去off broadway看吧。网址http://siteforrent.com/new/

 

 

 

Rock of Ages    摇滚岁月       一般推荐

便宜票:Student Rush: $27

一个jukebox musical(点唱机音乐剧,就是用很多老歌串成的戏)。有志摇滚青年追求寻梦小女生的故事。歌曲都是老的著名摇滚歌曲。It will rock you so hard!!如果你很喜欢摇滚,那就强烈推荐了!

 

 

 

Sister Act     修女也疯狂      推荐

便宜票:General Rush: $26.50

很好很轻松的喜剧。改编自同名电影。歌很好听,前半部分比较沉闷,后半部分很high。最后的舞台效果特别绚丽。

 

 

 

Spider-Man: Turn off the Dark    蜘蛛侠之滚你大爷的黑暗界        烂戏不推荐

便宜票:General Rush: $35

号称史上投资最大的broadway show。其实很坑爹,论坛上一堆骂的。

其实就是蜘蛛侠1的剧情啦,投资大是因为蜘蛛侠和老怪兽要掉钢丝飞来飞去,是在观众上面飞哦,要是出事故会砸死观众的(话说他们排练时真出事故了,摔残了一个演员)。而且他们一边飞一边打架一边唱,确实比较佩服。除此以外,毫无是处。曲子很难听,场景布置和舞蹈编排特别粗糙,小怪兽们的衣服也特别山寨。吐槽无力了。

 

 

 

Wicked      邪恶       特别推荐

便宜票:Lottery ticket: $26.50。但是,几百人抽十几张票你就别想了。所以,只能买最低90刀的正价票了。。。

没有拿到托尼奖的超级好戏,百老汇最牛逼的传奇作曲家Stephen Schwartz写的本子。

这是个基于绿野仙踪那个奇幻设定的政治寓言。绿野仙踪里有个浑身是绿的大坏蛋叫Wicked Witch of the West(西方邪恶女巫)。本戏的主角就是她。讲她是如何被世人误解的。

浑身是绿,人见人厌的女孩Alphaba,和金发美女,人见人爱的Glinda,是魔法大学的好朋友。他们一起去Oz国最高统治者:the Wonderful Wizard of Oz那里实习,结果发现the Wonderful Wizard of Oz其实是魔法大学校长的傀儡,而且看起来不错的魔法大学校长是个坏人。结果这两个女孩选择了不同的人生道路。Alphaba选择了反抗,被统治者污蔑为Wicked Witch of the West,她所有的好的政治理想和努力全部被民众理解为坏的。而Glinda投靠了统治者,被封为Good Witch of the North。受到全民爱戴。后来两个女孩又联手除掉了坏统治者。Alphaba最后做出了自己被杀的假像,和王子远走高飞,而Glinda当上了新的统治者,对民众说自己杀了Wicked Witch,受到了万民景仰和爱戴。

我理解这戏的主题是好的政治理想不会有好的民意和好的结果。民意所向往往是那个欺骗民众的人。

这个戏的另一个亮点的是音乐。其他音乐剧里的歌都跟一般歌曲差不多,分成几段,曲调重复。但是Wicked里的歌是真正的叙事歌,曲调几乎不重复的。有点像电影配乐。

 

 

War Horse     战马      这个是好戏啊强力特别推荐!

便宜票买法:去戏院没有特价票卖。唯一的特价票是去这个网页http://lct.org/linctixMain.htm注册,如果你是30岁以下,能以30美元买一张票。

11年托尼奖最佳话剧

这是这个清单里唯一的话剧。那是因为,话剧都更新得特别快,一般只上演几个月。我在八月份之前看的话剧,现在都已经下线了,只剩下这个最经典的战马。还记得我上面说到broadway theaters总共有40家,其中39家在时代广场附近,1家在林肯中心吗?演战马的戏院就是那家在林肯中心的。

一战前,一个英国农场的小男孩Albert养了一匹马Joey,和它特别有感情。一站爆发,他贪财的爸爸把joey卖给了英军。英军军官骑着Joey战死,Joey被德军俘获,用来拉伤员。Joey还被一个德国小女孩和德国老爷爷养了一阵。最后在沙场上,受伤的Joey倒在英军和德军的战地中央,导致了两军的停火。最后,为了寻找Joey而参战的Albert神奇地和Joey重聚了。

看完简介,你会发现,我擦,这个话剧的主角居然是一匹马!这是什么情况?实际上,Joey是由四个人控制的模型!一开始,你还会不停地注意到这四个人,可到了后来,你就完全忽略这四个人了,只关注这匹马了。其实,这个戏中还有好多马,以及鹅,小鸟等等动物,都是人控制的模型,特别神奇。

剧情很史诗的,在舞台上完美地演绎了各种惨烈的战况和英伦田园风光。

现在同名电影也在上映,斯皮尔伯格看了话剧之后导的。很好看。电影剧情和话剧剧情有一些不一样。不过我不喜欢电影版的一点是,电影里把德军描写的太坏了。而在话剧版中,英军和德军士兵都是很纯良的,只是各为其主而已。其实一战本就是不分正邪的战争,帝国主义狗咬狗而已,普通士兵都是一样的。

你还可以去看原著小说,很薄,亚马逊上五块钱一本。

 

 

最近新上演了很多戏,我都没看过,不过推荐一下Godspell和Chinglish。

Godspell和Wicked是同一个作曲家写的,应该不错。

Chinglish是拿中美文化差异当笑料的话剧,作者是唯一拿过托尼奖的华人编剧�哲�。我不知道这戏有没有拿咱中国人开涮。不过有个剧评是这样的:In hilarious Chinglish, the Chinese tiger roars, American business trembles. Laughs and sexual pleasure in translation. A shrewd, timely and razor sharp comedy! 应该不错。

跟进:已有好友指出Chinglish此戏不怎么地,跟春晚小品似的,包袱都是污蔑中国人的。

再跟进:又有网友看了此戏,表示此戏是政治批评戏,不能跟春晚小品类比的。

我表示,我没有看过此戏,到底怎样还是大家看了再说。

 

 

我的小收藏:

在高高低低的舞台上体味人生,在别人的故事里流自己的泪。

 pk   12年1月

补注:这里说的便宜票买法以及其他情况,都是我当时看戏时的。现在可能有个别剧院调整政策了,我就不知道了。

补注2:这个清单里只列出了我看过且现在正在上演或巡演的戏。比如评论中频繁提到的Les Misérables(悲惨世界)是03年停演的,我09年才来美留学,所以不好意思我没有看过。How to succeed in business without really trying 我很惭愧地也没有看过所以没有列出,不过也在这里推荐一下吧。这个戏几个月前一直是Daniel Radcliffe(哈利波特饰演者)演的男主角,不过现在已经换人了。还有Catch me if you can, Baby it's you, Jerusalem, Master Class, the Adams Family等戏,虽然我看过,但是已经在百老汇下线了,且没听说有巡演,所以也没有列出来。

补注3:转载请注明出处。

Wednesday, March 14, 2012

俞敏洪:我让女儿主动学习的秘密 zz

俞敏洪:我让女儿主动学习的秘密

来源:张朋(书生报国)日志

我之所以能够在这谈谈家庭教育的心得,确实是因为我接触的孩子太多了。从我在北大开始教书,一直到后来办新东方,到现在从新东方走出去的700多万学生。

 

我看到过很多幸福的学生,也看到了很多被耽误的学生;看到了很多家长因为孩子的成功感到无比的幸福,也看到了很多家长因为孩子的不成功而痛不欲生;我看到很多贫困家庭最后因为孩子有出息而生活的满心幸福;我也看到很多亿万富翁的家庭,最后因为孩子问题父母甚至患上精神分裂症。

 

好多父母都和我说,俞老师,我把我所有的钱都捐给你,只要你能把我的孩子变成正常的、幸福的孩子。我常常对他们说,现在你们说这个已经太晚了,因为孩子是从一出生开始到18岁就基本定型了,所有的事情都要在18岁以前做完。  

  

中国有句古话说:3岁看大,7岁看老。现在中国社会处于巨大的转型期中,从农业社会一下进入了商业社会。而现在的中国对商业道德和商业伦理本身的认识又不完整,中国人在商业社会里坑蒙拐骗的现象比比皆是。很多小企业都陷入这样一个慌乱中间,而在这样一个慌乱中我们应该怎样教育孩子?比如说我们教育孩子诚恳,但孩子诚恳后又在外面受别人欺负;我们教育孩子善良,结果我们发现善良的孩子得不到社会的认可。我们就会反思,是不是该教孩子投机取巧一点,是不是该教孩子更加凶悍一点。我们成人做的一些事情,有些时候会不知不觉给孩子展示了不好的东西,对孩子本身的价值观、世界观、人生观的形成带来重大影响。因此,我们教育孩子的方针出了问题。

  

    家庭教育的头等大事——人品教育


  

  

现在的中国进入了对家庭教育非常复杂的时代,社会里面有标准,家庭里面有标准,而且,这些标准还有可能是错误的标准。比如说,我们的标准是要求孩子在班里进前5名,孩子只要能考上北大、清华就算成功,到哈佛、耶鲁就更加成功。这虽然是标准,但在我看来,都是错误的标准。

 

为什么错误,因为它最后变成了家庭的惟一标准。而孩子的成长有很多标准,比如说人品是否高洁是一个标准,个性是否健康是一个标准,做事是否有创造力、想象力是一个标准,有没有吃苦精神、有没有勤奋精神是一个标准,有没有摔倒在地上敢于爬起来的精神也是一个标准。中国现在的社会,把成功限定在一个特别狭小的范围之内,对孩子们来说考高分,成绩好就是标准;孩子五六岁会弹钢琴,就会得到奖赏。我们中国的成人世界里从不以一个人拥有多少真心朋友作为成功的一个标准,我们成人世界里的标准就是这个人有钱,有地位,有财富。  

  我自己深深的陷入到这样的痛苦之中,许多人见到我后对我说的第一句话是: “俞老师,你现在也进入中国富豪榜了!”没有人会说:“俞老师,你的人品不错。”第二句是:“你作为新东方的掌门人,新东方很赚钱啊。”假如说有一天新东方倒闭了,我一分钱都没有了,还有人认为我是一个成功者吗?如果我成了一个身无分文,必须要靠自己的力气赚钱的人时,还会有人过来告诉我:“俞老师,你真是一个了不起的人物。”那时候我才觉得这个人是我真正的朋友,真正认可了我的成功。

我本人认为自己是一个成功者,不是因为我有新东方,我赚了钱,而是我有一个做人的基本准则,我始终认为我是一个善良的人,我是一个心中没有邪恶念头的人,从小到大,我没有做过一件伤害别人的事情。当然有时候话语上一不小心说了一句伤害别人的话,这是有可能的。有些话脱口而出,别人会觉得不舒服,我可以向他表示道歉,但是伤天害理的事情我肯定没有做过。  

  我觉得我成功的第二点,在于我非常愿意去帮助别人。其实一个人无论在什么地位都是可以帮助别人的。我现在有条件可以向甘肃的地震灾区捐款300万元,可以为甘南建造一所新东方希望小学,我很乐意去帮助他们。当我们什么都没有的时候,你也可以帮助别人。

大家都认为做事情需要自己有才能,但还有另外一种办法,我举了一个简单的例子。我从小就特别热爱劳动,我干农活,干家务活,我在 14岁的时候,就获得了我们县里的插秧冠军,17岁时,我是县里优秀的手扶拖拉机手,我父母的勤劳带动我的勤劳。我这个人成绩一直不好也不坏,老师根本就不关心我。但我想引起老师和同学的注意,所以我从小学一年级起就一直打扫教室卫生。到了北大以后我养成了一个习惯,每天为宿舍打扫卫生,这一打扫就干了四年。所以我们宿舍从来没排过卫生值日表。另外,我每天都拎着宿舍的水壶去给同学打水,把它当作一种体育锻炼。大家看我打水习惯了,最后还出现这样一种情况,有的时候我忘了打水,同学就说:“俞敏洪怎么还不去打水?”但是我并不觉得打水是一件多么吃亏的事情,因为大家都是同学,互相帮助是理所当然的。

有人说我傻,有人问我这样打水有什么好处。我相信好处是会有的,你做一件善事,它的回报今年可能会出现,也有可能是10年后出现。如果你做了一件好事,当天就要求回报,那你一定是个势利眼,也是个心胸狭窄的人。你不要求回报,回报也会来。当你有困难的时候,周围的人都觉得你是好人,他们能不伸手帮你吗?

当然,我打水的时候并没有想到我有困难时他们会来帮我。但是10年后的1995年,新东方已经做到了一定规模,我希望找合作者,就跑到了美国和加拿大去寻找我的那些同学。那我为了诱惑他们回来特意换了一大把美元,每天在美国非常大方地花钱,想让他们知道在中国也能赚钱,我想大概这样就能让他们回来。

后来他们回来了,但是给了我一个十分意外的理由。他们说:“俞敏洪,我们回来是冲着你过去为我们扫了4年的地,打了4年水。”他们说:“我们知道,你有这样的一种精神,所以你有饭吃肯定不会给我们粥喝。”这些人的加入奠定了新东方发展的基础,新东方才会不断地做大,做成美国的上市公司,做成了今天的规模。

  从我的经历我想告诉父母,如果你的孩子没有教育好,在外惹是生非,别人指责你的孩子时就会说:“这个孩子家教不好!”他们不会说老师没教好,新东方外语没教好,叔叔阿姨没教好,人家会说:“有其父必有其子,有其母必有其女!”你的孩子没出息,问题一定出在家庭教育上。孩子长大后出问题,一定是父母种下的根源。孩子的成功与否,与父母对孩子的家庭教育是否正确息息相关。我们教孩子教的是人品和道德,是否把孩子的人品和道德教育好,是孩子一辈子成功与否的关键。只要他是被尊重的人物,他的一辈子就会活得特别顺利。

父母亲可以想一下,你们是想做一个倍受欢迎的人,还是一个处处被防范的人?好多家长无形中都在教孩子小家子气,教孩子占便宜,教孩子怎样想办法超过别人,把别人踩在脚下,最后的结果是,孩子不明白在这个世界上怎样生存。你给孩子什么东西,孩子未来就是什么样的人。 

我女儿学习水平现在处于中等,但我从来不以此作为女儿是否要努力的标准。我和我老婆的教育理念相反。我老婆是女儿不进前5名就会生气,而我刚刚相反,我女儿第15名我很高兴,我每次都会对女儿说:“你看你们班40个同学,你15 名,后面还有25个人,你多厉害!”

从孩子一辈子的角度来说,你的孩子分数是好是坏,进北大还是进普通大学,没有任何的本质区别。真正能把孩子一辈子距离拉大的,是与他为人处事有关系的人品问题。人品的树立来自于榜样的力量,父母作为孩子的榜样是不能逃避的。我们要求孩子的东西没有效果,是因为父母没有做到。比如说,家长一边打麻将,一边呵斥孩子去学习,孩子当然不愿意学习。当父亲说:“老子没出息,你也想跟老子一样没出息吗?”孩子就会想,你说你没出息,那打麻将时我看你也挺开心,你没上大学也挺开心,为什么非要我上大学呢?父母是孩子最好的榜样。但是我在树立榜样方面遇到很多问题,我的工作是要用电脑的,但是我打开电脑,我女儿就会以为我在做其他的事情。虽然我对女儿讲我是在工作,但是我女儿不相信。没办法,后来只要我女儿学习,我就拿一本书在我女儿旁边看,这样一来,就有一个规矩感在里面,榜样起到一个非常重要的作用。  

  

  做任何一件事情,你都要想一想,我有没有伤害别人。比如说,我们大人有的时候喜欢在背后说邻居和同事的坏话,而且说的时候不回避孩子。我跟我老婆就出现过这样的情况,我们议论东家长西家短,哪个人人品好,哪个人人品不好,哪个人小气,哪个人不小气。结果有一次我女儿从学校回来,跟我讲她们班哪个同学好,哪个同学不好,哪个同学小气,哪个同学势利眼。我跟女儿说不能说同学的坏话,同学天天跟你在一起,她们身上如果有你认为的缺点,你放在心里,更重要的是你要学习同学们的优点。我女儿立刻反问一句:“你跟妈妈在一起的时候,不也议论很多叔叔阿姨吗?我为什么不能说同学的坏话?”一句话让我无言以对。从此以后我就跟老婆定了一个规矩,在孩子面前不能议论别人的缺点,一定要说对方的好话。

  培养孩子对生命的爱心——心情教育 

  如上所述,现在家庭教育要做的头等大事就是教育孩子有良好的人品,第二就是要进行心情教育。我女儿天生就比较内向,是一个很文静的孩子,我儿子就很外向,每天都要爬上爬下好几次,我女儿做事情慢条斯理,我儿子就比较急躁,这些都是天生的不同。除此之外,孩子的智商也不同,我目前还没有看出我女儿和儿子智商上有什么不同之处,因为他们年龄差距比较小。我们会发现这样的情况,在一个班里,有的孩子永远是第一名或者第二名,他智商确实高。而我们的孩子不管怎么努力都在中间,甚至后面。在这样的情况下,家长们应该怎么办?孩子是有区别的,但是只要你的孩子没到白痴水平,只要孩子能正常的走进教室上课,就能够做出重要的大事来。比如胡雪岩只上了两个月的学,但最后变成了最厉害的商人,而且还心怀大志,这就是很好的例子。  

  我是中国企业家协会的副会长,这个协会有一千多个企业家,北大清华复旦毕业的还不到二十个,剩下的全是普通高校毕业的,甚至还有的是农民、工人、大专生、中专生等等,但他们都成为了成功的企业家。所以教育只能决定人生的一部分,不能决定一切。  

  我们家长必须有一个本领,在任何情况下,既能督促孩子进步,又能保持孩子的自尊不受伤害。你要保证孩子得第一名高兴,得最后一名也高兴,让孩子觉得活在世界上很美好,培养孩子对于生命的热爱,是家长最重要的任务之一。美国电影《当幸福来敲门》里的男主角是美国最有钱的人士之一,但他曾经也是穷光蛋,家里贫困,没上过大学,靠推销东西为生。有一次他在纽交所门前碰到了一个满面笑容的人,那个人是个股票经纪人,他就以为当股票经纪人很幸福,于是他就努力当股票经纪人。后来他认识了一个股票经纪人培训班里的人,那个人开始看不起他,后来发现他玩魔方很厉害,而这个人恰巧也有同样的爱好,于是被这个人的魔方技巧深深的吸引了,他们两个也成为了好朋友。这个人因此获得了机会,变成了美国最有名的股票经纪人,实现了亿万富翁的梦想。  

  这个电影是根据真人真事改编的,在影片的最后,这个人用了半个小时的时间讲述他自己的故事。他启发我们每一个人,真正的幸福不是别人送来的,而是自己努力争取来的。而现在的家长对孩子是十分娇惯的,孩子要什么就给什么。比如孩子看上一个玩具,家长不买他就不走,最后家长没办法只能给买了。于是孩子就有了这样的心理,只要他坚持家长就会让步。我在给儿子买东西前,会分析他要的东西是否合理,如果不合理就坚决不买,如果合理也不能轻而易举的给他买,我是必须让孩子完成一项任务才给他买。比如我女儿在国外上学时,班上的孩子每人都有一台笔记本电脑。我家虽然有台式电脑,但是用笔记本电脑会比较方便,而且家里也不缺钱,不给她买也不对。我就告诉她,爸爸一定给你买,但是你要知道,这个世界上没有白白得来的东西,你现在已经是一个大孩子了,需要付出劳动获得想要的东西。如果她能背诵下来30篇新东方的英文励志文章,就给她买。结果两个星期不到,她就背得滚瓜烂熟了,我就给她买了笔记本电脑。这样的教育方式让孩子学会了付出才能得到的道理,也把孩子喜欢的方式变成她做另外一件事的动力。  

  给予适当鼓励,激发无限潜力——鼓励教育  

  关于如何鼓励孩子的问题,我先讲一个故事。美国有一位著名的物理学家,上初中刚开始学物理的时候,他的物理成绩很差,只考了8分。物理老师找他谈话,让他好好学物理。学生说我不喜欢就是学不好,老师特别聪明,他告诉这个学生,别的同学都是60分及格,你下次只要考到9分就算及格。学生一想我随便划个勾就能及格,很容易,于是就答应了,结果下次考试考了28分。虽然28分,老师还是没有理由在全班面前表扬他,因为还是不及格的分数。这个老师很聪明,她让全班同学把上次的考试成绩和这次的成绩做一个减法,上次考了90,这次还是 90,一减就是0,上次95,这次93,一减就是2,这样减到最后,就一个同学剩下了20分,就是这个同学。老师把所有同学两次考试的分数差写在黑板上,问了个问题:“哪个同学进步最大?”全班同学异口同声说某某,这是一个铁的事实,因为只有他一个人进步了20分。这种鼓励方式不但没有侮辱色彩,而且有很大激励色彩。这个学生一下就兴奋起来,他想无论我考到48、68、88都是全班进步最大的,我有无数进步的空间。老师做了件聪明的事情,孩子就觉得有广阔进步的空间,从此这个孩子就喜欢上了物理,并最终成为了全世界最伟大的物理学家之一。  

  老师的教育方式十分重要,但是家长的教育方式更重要,因为孩子更加看重家长的一言一行。  

  我举一个简单的例子。假如你的孩子是全班最后一名,你怎么对待你的孩子呢?大部分家长的反应都是打一顿或者骂一顿。孩子明明知道自己错了,但是没有办法,他就考了0分你怎么办呢?你还不断的伤害他,到最后他可能就不学了。我建议家长朋友们站在长远的角度上去想一想,第一名和最后一名到底有多少差距。难道没上大学孩子一辈子就没出息了吗?绝对不是!就像我刚才说的,只要把孩子教育好了,不管做什么都是有出息的。我可以举自己的例子。在高考的时候,我第一年没考上,第二年没考上,第三年才考上了。我用了三年的时间和我的同学拼了一年,我在北大毕业的时候,我们全班50个同学,我是全班倒数第五名,但成绩并没有阻碍我做成自己的事业。  

  人的成长是一辈子的事情,绝对不是你在小学、大学所得的分数决定的。分数对于孩子来说很重要,但是不能因为孩子分数低就认为孩子没出息。请家长一定要改一改判断成功的标准。孩子分数高是成功的标准之一,但是千万不要把它当作是必然的标准,更不能当做是惟一的标准。否则你的孩子就会受到无数的伤害,因为任何一次考试只有一个第一名。如果一个班里有40名学生,难道除了第一名,其余的39个孩子都是笨蛋吗?都是失败者吗?绝对不是!所以我有一种自我解嘲说法:其实当第一名也确实不容易,因为他走得很辛苦,往前面一看,一个追赶的人都没有,往后面一看,全班都在追自己,追的自己半死不活,所以第一名没有什么好当的。当最后一名多幸福呀,往前一看,都是被自己追的半死不活的人,往后一看,一个追自己的人都没有,你想这种生活多潇洒呀!  

  当然这种观点不能和孩子说,你一定要学会怎么鼓励孩子。这次考倒数第一,下次我会鼓励他考倒数第二,这次他考20分,下次我会鼓励他考30分。千万不要说你的孩子,这次考了60分,你告诉他下次不考90分就别进家门。你这么说,有的孩子当然会拼命学,但有的孩子拼了命也到不了90分,那他怎么办呢?难道真的不让他进家门么?你肯定让他进家门,你让他进家门就意味着你说话不算数,孩子以后就知道了,我爸爸妈妈是说话不算数的,那我以后就无所谓了,他反正不会不让我进家门,父母就这么被孩子刺中了。  

  所以做父母的一定要记清楚,绝对不能让孩子吃住你,绝对不能要什么给什么。教育孩子和驯养动物是一模一样的,现在很多做父母的还不如动物园的驯兽员。驯兽员驯动物有两种方式,第一,鼓励,绝对的鼓励;第二,限定,甚至是惩罚。我曾经问一个驯养海豚的驯兽员,为什么他训的海豚能从水中跃出,并从那么高的圈钻过去?他说其实海豚开始是不会做的,他先把这个圈放在水里面,被训的海豚谁钻过去就给它一条鱼吃,没钻过去的就不给吃。海豚也不笨,很快明白了吃鱼的方法,后来海豚就形成了一种条件反射。当然动物肯定不能像我们人一样那么聪明,比如说我告诉你,这个圈你钻过去,我就让你上北大,你肯定就钻过去了,因为你知道这个东西对你有利。海豚不会知道利益的关系,但是海豚明白一个道理,只要我钻过这个圈我就有鱼吃,没钻过去就会挨饿。紧接着驯兽员就把这个圈提出水面一半,让海豚继续钻。海豚发现圈不在水中了,只能跳过去。逐渐的,圈被不断提高,大概要提10-20次,需要一年左右的时间,海豚跳圈的训练就完成了。大家有没有注意,在看海豚表演的时候,凡是从那个圈跳过去的海豚一定会游到驯养员的身边去要鱼吃,因为它知道每次跳过去都能吃到东西。  

  从上面海豚的例子中家长应该明白一个道理,鼓励和限定在教育孩子的过程中十分重要。比如说孩子得了0分,你要鼓励孩子下次考到10分,那孩子一想,考到10分还是挺容易的,你就可以加些条件。比如说这个孩子爱吃麦当劳,你就可以把它当成一种诱惑,答应孩子考到10分,就带他去吃麦当劳,然后再把孩子的分数一点一点加上去。比如说你的孩子考了60分,你千万不要说下回你要考到90 分,或者把孩子臭骂一顿。你要对孩子说,60分已经不错了,下回你能不能考到70分,我不把你跟班里的同学比,我只看你今天要比昨天进步,你明天要比今天进步,这个孩子的分数在潜移默化中就上去了。他每上去一点,你都要鼓励他。当分数考得更低的时候,你要告诉孩子,这次可能是偶然的失误,爸爸妈妈支持你,千万不要伤害孩子学习的积极性。  

  在鼓励的同时,父母也要给孩子定规矩。我儿子特别喜欢吃冰激凌,我老婆每天就让他吃很多,把牙齿都吃坏了。我看这样下去不是办法,就规定他一天只能吃一个冰激凌,而且必须吃完晚饭半小时以后才能吃。我儿子那时刚4岁多,不知道半小时是什么概念。我就告诉他,那个长的针走到什么地方就是半个小时。我儿子一会儿看一下那个钟,半小时看了100多次。总算熬到半小时了,迫不及待的吃到了冰激凌。第二天时,就变成看十几次了,到了第三天就变成看两三次了,到第四天他知道反正一时半会儿也不能吃,就玩去了,等到他想起来吃的时候半小时已经过去了。只要是正确的事情,父母都要鼓励孩子,同时要给孩子定规矩。立了规矩以后,父母一定不能随便更改,更改就会出现问题。  

  最后我要谈家长会犯的另外一种毛病,叫做才能综合症。父母特别想让孩子变成小天才,其实这是父母的虚荣心在作怪。中国父母会犯两个毛病,第一,凡是自己不会的东西会让孩子会,希望孩子在某个领域中获得成就。所以很多家长就强迫孩子从小学这个学那个,没有任何意义。孩子最重要的是让他慢慢的成长。我女儿很喜欢弹钢琴,从5岁就开始弹,弹着弹着就变味了,一级一级的考上去,每个星期都要上课,每次都坐一个小时不能动,结果把我女儿弹钢琴的兴趣全部扼杀掉了。我女儿不想学了,而我老婆强迫她学。我问我老婆,你的孩子在10岁的时候就过了钢琴十级,请问她10岁以后还学不学弹钢琴?如果她10岁以后不学,那从她1岁到10岁学钢琴有什么用?你如果不是想把孩子培养成一个伟大的钢琴家,那么你让孩子10岁就通过十级考试是没有道理的。第二,你让孩子这么学,让她对钢琴失去兴趣了,她根本就不爱弹钢琴了。我和女儿说,爸爸不强迫你学,也不让你考级。爸爸这辈子很后悔的事情就是不会演奏乐器,演奏乐器可以排解郁闷。学习钢琴原则上不是为了让你考级,而是为了帮你寻找抒发心情的渠道。未来你一定会进入社交圈,如果你能在朋友唱歌的时候进行钢琴伴奏,这样你会得到别人的尊敬。如果你不想学,能不能保持你现在的水平。我女儿其实是很喜欢钢琴的,作业做累了,就弹半小时钢琴。这以后她每个礼拜跟老师学一次,学习钢琴的兴趣也越来越浓了。  

  综上所述,家庭教育最重要的是人品教育,其次是心情教育,第三是鼓励教育。最后一点,家长要让孩子学会吃苦。  

  最后我想告诉家长朋友们,你把所有的一切,你的生命、财富、地位全部交给孩子,你的孩子终身都不一定会幸福。你只有教会孩子如何做一个成功的人,教会孩子学会追求自己的目标,学会享受达到目标以后的幸福感和满足感,你的孩子才会获得真正的幸福。

 

  (俞敏洪的成功,是不是德的成功?可是多少人还是以术第一,私心第一,这样怎么可能成功?)

Wednesday, February 8, 2012

据说是世界上最健康的作息,借以明志,再不通宵

据说是世界上最健康的作息,借以明志,再不通宵

7:30:起床。英国威斯敏斯特大学的研究人员发现,那些在早上5:22―7:21 分起床的人,其血液中有一种能引起心脏病的物质含量较高,因此,在7:21之后起床对身体健康更加有益。
打开台灯。“一醒来,就将灯打开,这样将会重新调整体内的生物钟,调整睡眠和醒来模式。”拉夫堡大学睡眠研究中心教授吉姆・霍恩说。
喝一杯水。水是身体内成千上万化学反应得以进行的必需物质。早上喝一杯清水,可以补充晚上的缺水状态。 

7:30―8:00:在早饭之前刷牙。“在早饭之前刷牙可以防止牙齿的腐蚀,因为刷牙之后,可以在牙齿外面涂上一层含氟的保护层。要么,就等早饭之后半小时再刷牙。”英国牙齿协会健康和安全研究人员戈登・沃特金斯说。 

8:00―8:30:吃早饭。“早饭必须吃,因为它可以帮助你维持血糖水平的稳定。”伦敦大学国王学院营养师凯文・威尔伦说。早饭可以吃燕麦粥等,这类食物具有较低的血糖指数。 

8:30―9:00:避免运动。来自布鲁奈尔大学的研究人员发现,在早晨进行锻炼的运动员更容易感染疾病,因为免疫系统在这个时间的功能最弱。步行上班。马萨诸塞州大学医学院的研究人员发现,每天走路的人,比那些久坐不运动的人患感冒病的几率低25%。 

9:30:开始一天中最困难的工作。纽约睡眠中心的研究人员发现,大部分人在每天醒来的一两个小时内头脑最清醒。 

10:30:让眼睛离开屏幕休息一下。如果你使用电脑工作,那么每工作一小时,就让眼睛休息3分钟。 

11:00:吃点水果。这是一种解决身体血糖下降的好方法。吃一个橙子或一些红色水果,这样做能同时补充体内的铁含量和维生素C含量。 

13:00:在面包上加一些豆类蔬菜。你需要一顿可口的午餐,并且能够缓慢地释放能量。“烘烤的豆类食品富含纤维素,番茄酱可以当作是蔬菜的一部分。”维伦博士说。 

14:30―15:30:午休一小会儿。雅典的一所大学研究发现,那些每天中午午休30分钟或更长时间,每周至少午休3次的人,因心脏病死亡的几率会下降37%。 

16:00:喝杯酸奶。这样做可以稳定血糖水平。在每天三餐之间喝些酸牛奶,有利于心脏健康。 

17:00―19:00:锻炼身体。根据体内的生物钟,这个时间是运动的最佳时间,舍菲尔德大学运动学医生瑞沃・尼克说。 

19:30:晚餐少吃点。晚饭吃太多,会引起血糖升高,并增加消化系统的负担,影响睡眠。晚饭应该多吃蔬菜,少吃富含卡路里和蛋白质的食物。吃饭时要细嚼慢咽。 

21:45:看会电视。这个时间看会儿电视放松一下,有助于睡眠,但要注意,尽量不要躺在床上看电视,这会影响睡眠质量。 

23:00:洗个热水澡。“体温的适当降低有助于放松和睡眠。”拉夫堡大学睡眠研究中心吉姆・霍恩教授说。 

23:30:上床睡觉。如果你早上7点30起床,现在入睡可以保证你享受8小时充足的睡眠。 
         任何试图更改生物钟的行为,都将给身体留下莫名其妙的疾病,20、30年之后再后悔,已经来不及了。 
一、晚上9-11点为免疫系统(淋巴)排毒时间,此段时间应安静或听音乐。 

二、晚间11-凌晨1点,肝的排毒,需在熟睡中进行。 

三、凌晨1-3点,胆的排毒,亦同。 

四、凌晨3-5点,肺的排毒。此即为何咳嗽的人在这段时间咳得最剧烈,因排毒动作已走到肺;不应用止咳药,以免抑制废积物的排除。 

五、凌晨5-7点,大肠的排毒,应上厕所排便。 

六、凌晨7-9点,小肠大量吸收营养的时段,应吃早餐。疗病者最好早吃,在6点半前,养生者在7点半前,不吃早餐者应改变习惯,即使拖到9、10点吃都比不吃好。 

七、半夜至凌晨4点为脊椎造血时段,必须熟睡,不宜熬夜。

热巧克力 hot chocolate

总时长:40分钟

分量:四人份

食材:

9盎司(约合255克)质量上乘的半甜巧克力,将它们切碎。

6大勺无盐黄油,把它们切成小块。

4枚大鸡蛋

四分之一杯砂糖

微甜的发泡鲜奶油适量(非必需)。

制作步骤:

1. 将烤箱预加热到华氏350度。把四个标准一杯容量的耐高温咖啡杯、马克杯或者容量为8盎司的模具摆放在烘焙盘或烤盘中。

2. 双层煮锅加水置于火上,待水烧到微微沸腾时,将巧克力和黄油放在煮锅内隔水融化。间或搅拌几次,直到巧克力和黄油完全融化,然后将煮锅端下放在一旁备用。

3. 将鸡蛋打入碗内,加入适量糖搅拌均匀,然后把碗置于微微沸腾的水上,搅拌至感觉到温热为止。

4. 将碗端出,用电动搅拌器将蛋液打到变得发泡蓬松为止,时间为3至5分钟。将蛋液平缓地拌入巧克力与黄油混合物中。

5. 用勺子将混合物盛入杯中,然后在烘焙盘中加入足够的高温热水,加到位于杯子的一半高度即可。将烘焙盘放入烤箱烤15至20分钟,直到表层色泽变得不再光亮,最后将杯子从烤盘上小心取出。

6. 趁热享用或者待其冷却到室温时享用,有需要的话,还可浇上一大勺发泡鲜奶油。布丁可以冷藏长达一天时间,如需重新加热,先将它们置于室温环境放置一会儿,然后放入华氏350 度的烤箱内加热到温热,时间约为5分钟。

Monday, February 6, 2012

转载:某基金公司经理北大演讲――很全面的投资学个人观

开场白

今天很高兴有机会能和北大的年轻校友们一起探讨投资的一些理论。
我对投资理论研究一直就很有兴趣。95年进入这个行业,从操盘手到基金经理到出来自己做,感触和体会还是很多的。
投资是项很辛苦和很寂寞的工作。投资体系庞大而且复杂,就象一座很大的森林,我们要耗用一生去研究它,会发现有很多值得探索的东西。很多年以来我都这么做,持续思考,但一直没有做系统性的总结。所以今天我也感谢大家给我这个机会,让我全面的反思一下投资的一些重大领域的理论框架。
今天的讲座,我的想法是和大家交流一些东西,而不是我要教导大家什么东西。今天的分析框架分四个部分:宏观经济、股市波动性、公司经营和公司估值。在投资这座森林里,这四个分支是比较重要的,对每一个分支我们都需要花很多心力去研究。以我认为,投资就是要不断建立一些理论框架,并在情况发生改变后有勇气不断打破它而重建。我们看巴菲特、费雪等大师,都有非常清晰的理论体系,操作背后都有一套投资哲学在支持。我们也是在努力学习和思考他们的投资哲学,所谓"有缘人得之"。我希望能够学习他们的精华,并结合中国的实际情况,建立一套自己的体系。
有一本很有名的书叫《黑天鹅》,它的主题是说,我们辛辛苦苦建立了很多投资模型,去管理我们日常的投资。投资风险可以用一条高斯曲线来表达,大部分风险都在某一个区域里面,我们努力去规避最主要的这样一些风险,但是一次极小概率意外的出现,就可以颠覆一切,将辛辛苦苦建立的模型打破,很多财富从而化为乌有。但我想,巴菲特从60
年代做投资以来,经历过无数次大大小小的所谓黑天鹅事件,他不但存活下来了,而且活得很好,他是怎么做到的?2008年《黑天鹅》这本书的出版,让投资界很震惊,都在反思。黑天鹅事件是不可避免的,关健是我们如何去应对,让我们的资产不断增值,活得更好。这是促使我去思考的另一个重要原因。
我谈谈研究的基本思路。首先我认为,在市场的每个具体阶段,我所研究的这四个分支都有一些非常重要的核心矛盾,有些非常敏感的因素。如果我们不能挖掘出在这个阶段里最核心的矛盾是什么,那就只能说明一个问题:我们还不够努力,我们还没有挖掘出本质和规律所在,所以要不断去探求。过一段时间新的情况出来了,最核心的矛盾改变了,我们又要重新挖掘。我们始终要努力抓住最核心的矛盾。
其次,这四个理论框架里的每一点,我都希望与巴菲特的投资思想作一个印证。巴菲特对这些问题都有论述,零散地分布在巴菲特的年报和各种访谈里面。巴菲特在投资界的修炼已经达到非常高的境界。他的很多说法,我们要加以印证。我自己感觉这几年我们国人对巴菲特的理解很粗疏。中国人很喜欢以简驭繁,不少人把他的投资理念简单理解为不管宏观面、买了好股票就长期拿着不放就能赚大钱,这种绝对化的理解是有问题的,所以我们看到很多所谓巴菲特的信徒在2008年都吃了很大的亏。有些地方是我们误解了巴菲特的思想,以及有些地方是巴菲特讲的不清楚
一.宏观经济分析
(一)用"单一规则"分析经济周期
研究经济就是研究周期。我尝试用"单一规则"来分析经济周期。"单一规则"是借鉴弗里德曼的说法,他认为,当代一切经济活动都离不开货币信用形式,一切经济政策的调节都是通过货币的吞吐(扩张或收缩)来发挥作用。一切经济的变量,都和货币有关系。货币推动力是说明产量、就业和物价变化的最主要因素,而货币推动力最可靠的测量指标就是货币供应量。由于货币供应量的变动取决于货币当局的行为,因此,货币当局就能通过控制货币供应量来调节整个经济。政府只需要将货币供应的增长控制在既适度又稳定的水平上就可以了,方法就是将货币供应的增长率相对固定,这就是所谓的"单一规则",除此以外,不应该也用不着对经济多加干预,市场会自己进行调节。这是弗里德曼最核心的思想。
70 年代开始,美国采取这种方式成功解决了通胀和失业率较高同时并存的经济难题(这是凯恩斯主义所不能解决的问题)。之后,这种货币主张被很多西方国家所接受,成为发达国家经济政策最重要的理论。但是显然,我们国家并没有这么做。
(二) 一切通胀问题都是货币问题
一切通胀问题都是货币问题,这是弗里德曼最著名的论断。通胀这个词语容易产生歧义,CPI
并不是最好的指标,货币购买力这个名词可能更好,大家更容易理解。最近几年货币发的很厉害,买房子越来越辛苦,教育医疗等支出也让老姓感觉很头疼,我们的购买力水平显然在严重下降。我认为,我们可以用货币发行总量的变化大致估算货币购买力水平,具体到某一项资产价格,我们可以用供求关系来加以判断。我们可以用这种办法来分析房价、劳动力和原材料价格。比如劳动力,高善文的一篇报告说,2007
年下半年我们国家已经迈过了刘易斯拐点,农民工不仅仅要求满足最低生存需要就算了,他们对生活有了更高的追求。劳动力供需矛盾发生了深刻变化,因此工人工资在上涨,农产品价格也涨得很厉害,由于农村劳动生产率提高很困难,因此农产品价格上涨是不可逆转的,而且会长期维持,发改委说是有人故意炒作,这是推卸责任的做法。CPI
会长期比PPI 涨幅高,这也是由于劳动力价格上涨引起的,韩国和日本在迈过了刘易斯拐点后,就是这种情况。另外判断有色金属价格,全球量化宽松条件下,不仅中国资金起作用,全球资金都起作用,当然只有涨了。怎么去衡量货币供应量呢?有很多指标,比如M1
增速、M2 增速、M1 增速-M2 增速、M2/GDP 等,后面还有介绍。
(三)产能增加和过剩周期、房地产周期是最敏感的实体经济周期
货币是如此重要,我们要研究什么实体经济的变动会引起货币供应量的重大变化。我们国家货币规模(M2)的变化主要由两个方面引起,一个是信贷规模的扩张,第二是外汇结汇,还有小部分是央行负债。
从2010 年来看,信贷规模占67%的权重,外汇结算占28%权重。是什么影响信贷规模的变化呢?很大一部分是来自固定资产投资。根据2009
年中国统计年报,城镇固定资产投资达到19.4 万亿元,采矿、制造、交通运输等和产能直接相关的投资,达到11.1
万亿,占比57%。而公共产品的提供,如水利、环境教育、卫生文化等固定资产投资只占30%,这和国外很不一样。我们的固定资产投资主要不是为了满足老百姓的娱乐生活消费,而是为了满足产能增加的需要。所以,高善文的文章《十年之痒》,结论我很认同,产能增加和过剩周期的确是中国经济最核心的周期。
我们的产能扩张了,无非是为了满足两个需求:国内消费需求和国外消费需求(即出口)。但是,国内消费需求要有很大增长是很困难的。2009
年居民零售消费总额增长15%,2010
年至今是18.3%,已经不错了,未来也不会有很大增长,这和我国的政治经济环境是直接相关的。我们的社保、医疗体系等都不完善,尤其是养老问题很严重,现在大家的养老金并非由国家统筹,还是基本由县市一级统筹,投资渠道只有国债和存款,这几年收益率还不到2%,在高通胀下这些资产严重缩水,寅吃卯粮,赤字严重,等到你们退休后,能拿到的退休金有可能是不够的,不足以维持你退休前的生活,这个问题会越来越严重,会成为最影响中国人幸福感的事情。那你还敢随便消费吗?另外中国政府与民争利的情况很严重。这几年国家财政增长很快,2006
年增长22%、2007 年增长32%,2008 年金融危机还增长19%,2009
年增长11%。今年前十个月增长22%,但是居民工资收入增长水平却很低,大家拼命呼吁,但个税起征点还是顽强地维持在2000元不变。可见政府并不想藏富于民,而是希望将更多钱放在库房里。由于有如此多的顾虑,除非政治体制有大的改革,我不认为消费会给经济增长带来惊喜。看"一二五"规划时,说要培育中国消费市场成为世界级的市场,我觉得是很困难的。产能的扩张还是要寄托在出口上。自从2001
年中国加入WTO 出口持续大幅增长,我们是世贸体系最大的赢家。我们的出口依存度是很高的,2009 年出口占GDP
是24%,出口金额与居民零售总额的比重是65:100。2009年出口总额达到1.2 万亿美金,超越德国成为全球第一。2008
年金融危机出口下滑了一点,导致东莞工厂减少了15-20%,引起工人失业,社会动荡。现在国家推动城镇化建设,产能向中西部转移,加大基础建设,这是一个重大国策,这部分产能转移很大一部分是出口带来的。因此要非常重视出口未来的潜力。
简化一点,我认为产能增加和过剩周期的核心是进出口周期。衡量指标上,进出口增速和出口产品的结构比例,还有PMI
等。高善文认为产能增加及过剩周期是核心周期,但没有说房地产周期,是不完整的。房地产周期,影响也很重大。2000年之后,要解释中国经济的增长奇迹,除了加入世贸带动出口增长外,另外一个原因就是房地产。03-07
年地产狂飙带动经济发展,08-09
年金融风暴,政府采用启动房地产的方式来启动中国经济,算是把经济从泥潭里拉了出来,当然代价也很大。关心地产周期,衡量指标主要有:房地产投资增速、房屋销售量、新屋动工量、房价收入比、房租回报率等。
地产对经济影响有多大呢?我们看几组数据。
第一,房地产所占财富比重。根据国际货币基金组织的一个统计,2004 当年全球可变现资产的规模是170-220
万亿美金,住宅和商业地产90-130 万亿,股票35-40 万亿,黄金很少只有1.6-2
万亿。因此从全球角度来看,地产占人类财富的比重超过50%。而且由于货币贬值,全球不动产价格持续走高,今天地产所占财富的比重应该更高。而中国呢,有一个大概的测算,2009
年7 月中国房子的总值超过90 万亿,现在可能已经超过100 万亿了。而我们的GDP 2009 年是34.5 万亿,今年大概是37.5
万亿。房价总价值超过GPD 的两倍多。未来城镇化后,农村土地价格变得更值钱。房地产所占财富比重非常惊人,一点点的波动对整体经济都会产生重大影响。
第二,房地产对M2 的吸纳。09 年初M2 是47.5 万亿,年底时是60.6 万亿,一年增加13.1 万亿。而09
年地产销售总额是4.4 万亿,占新增M2 的34%。09 年房地产投资完成额是3.62
万亿,占了27.6%,一部分卖出去了,还有一部分没卖出去。房地产销售额加上已经建好但还没有卖出去的,估计占新增M2
超过四成。和房地产业配套的水泥、建材生产等,占比也很重,按照10--20%的比例算,所有这些因素加在一起,房地产及配套产业对新增货币吸收比重可能在60%左右。这是09
年增加那么多货币却没有发生很严重通胀的其中一个原因。
第三,房地产对贷款的吸纳。存量部分,09
年末中国银行业对房地产业,包括对房地产公司和对贷款购房的老百姓,总的贷款占总体贷款规模的23%。增量部分,09 年全年新增贷款接近10
万亿,其中房地业所占比重约22%。在西方发达国家,这一块比重也很厉害。它们还能发CDO 和CDS,杠杆很高,在08
年巅峰时曾达到几十万亿美金的规模。因此,次级债问题爆发,房价闻风下跌,西方的金融机构普遍都崩溃了,前车可鉴啊。从最近花旗、富国银行的业绩报告看,房地产贷款占总体信贷的比重仍达40%多。因此,这是一个全球问题,房地产业对货币和经济的影响都很大。
(四) 中国货币政策的主要特点和未来可能的经济风险
1. 唯一目标是经济增长
中国货币政策的目标,全球罕见,说是兼顾,实际上只有一个目标那就是经济增长,其他三个传统目标都不重要,包括通胀在内。而在西方发达国家,控制通胀往往是最重要的目标,即使为了刺激经济增长而降息,也要以通胀不恶化为前提。在中国,因为经济增长会带动就业,这关乎民心稳固,所以无论付出多大代价都要拉动经济增长。由此所带来的最糟糕的问题之一,就是制度性地容忍负利率。
2. 制度性地容忍负利率
负利率从经济学角度看很可怕,长期负利率会让资源错配,让资产价格不合理上涨。因为担心货币放在银行里贬值,货币只好不断流动去寻找投资增值的机会,这会加快货币周转,从而令通胀加剧。过去历次通胀时期,政府都迟迟不愿意加息,因为担心加息会对经济增长带来影响。上一次是2007
年,下半年开始通胀升温,12 月时CPI 已经是6.5%,而一年定期利率这才刚从3.87%调整到4.14%,2008 年4
月通胀达到高点8.5%,而利率在这期间却没有做任何调整。最近CPI
也不断上升,我们的加息过程也很缓慢。实际上我们是最早从全球经济危机中走出泥潭的。现在CPI已经是4.4%了,但是我们的利率直到上个月才从2.25%上调到2.5%。上个星期看到社科院一篇文章,说我们的CPI权重设置有问题,这几年通胀可能被低估了7%,目前4.4%的CPI
我觉得也是不真实的,直观感觉是6-7%,甚至更高。长期容忍负利率从经济学上看是非常可怕的事情。但是这种政策目前看不到大的实质性的调整,实质上是用全体人民的财富来补贴银行,这是一件极不负责任的事情。在西方发达国家,控制通胀是所有央行最核心的事情。包括这一波的量化宽松,美联储大肆放水,但也定下前提是核心通胀率不可超过
2%,超过了就停。美国现在通胀大概是0.2%--0.3%。
3. 中国货币之谜
我们的M2 长期高速增长,但是为什么CPI
却没有高涨呢?这就是"中国货币之迷"。有个指标M2/GDP,从91年的88.8%增长到00 年的135.7%,08 年是151.3%,09
年178%,现在185%左右,在全球主要经济体里排名第一,美国是60%左右。每年的M2 增长指标,多年来央行定在15%左右,而08 年1
月之后增速惊人。对比一下美国,2008 年1月,我国M2是41.7 万亿,到2009 年1 月增到49.6
万亿,增长18.94%。美国从7154 万亿美元增加到8123 万亿美元,增速10.36%。2009 年底,我们的M2 已经62.56
万亿,当年增长26.13%,而同期美国增长2.29%。今年10 月份对比去年底,增长11.86%,同期美国是3.29%。我们目前的M2
是69.98 万亿,美国大概相当于58.3 万亿人民币,我们比美国多11.69 万亿人民币,但我们今年的GDP 是37.5
万亿,美国大概相当于99 万亿人民币,将近我们的三倍,但是我们的M2比美国还大。这三年来,我们的M2
货币增长69%,而美国是16%。当然中美M2 计算方式有些不同,但是大 的方面应该差不多。
同时我们看另外一组数据,易纲多年前的一篇文章,中国1978 年到1992,中国的M1 和M2 分别增长了20
倍,但是,官方物价指数和自由市场价格指数也只增长了1.25 倍和1.41 倍。货币流通量增长如此惊人,CPI
增长却不是很多,超额货币去哪里了?我认为最好的解释是,我们有大量的产能和资产对M2
较好的吸收。1978--1999年间,中国整体经济高速发展,百废俱兴,产能不足情况非常严重,大量外资被吸引进来,大量投资工业区,购买设备,修建高速公路等,货币增长再多也能吸收,所以通胀虽然出现了,但并不太严重。
4. 房地产景气难以持续
08-09 年间,货币超量发行,前面我介绍过,房地产吸纳了大量的增量资金。大量购房客把钱交给房地产商,房地产商再去向政府买地,再投资建楼。货币从分散到高度集中,都汇聚到房地产商那里了。房地产商拿到了售楼款,M2是不变的,并不是说新增的货币被消耗光了。货币和实体经济的关系,周其仁说,这是水和面的关系,水多了就加面。中国的面,最敏感的就是出口和房地产,我觉得现在都到了很难持续的程度。看下我们最新的房地产数据,1-10
月份, 全国房地产施工面积达到36.98 亿平方米,同比增长28.3%;竣工面积4.2 亿平方米,新开工18.13
亿平方米,明年可供销售的面积可能达到30 至40 亿平方米。我们算算每年能销售多少面积,2009 年销售了9.3 亿平方米,今年以来,前10
个月销售面积是7.24 亿平方米,今年大概能卖掉10
亿平方米,明年的供应量接近需求量的三倍多至四倍,所以明年房屋的销售供给是很充分的。另外,在负利率的情况下,房子一推出来马上就将被抢购一空,地产商说这是由于刚性需求强烈所致。这个刚性怎么看?有没有水分?大部分的小两口,双方父母帮忙给首付,小两口每月交按揭,是现在很常见的一种购房现象。耗用两代人的财富去买房子,压力是非常沉重的,有很多买房的行为并不是出于自住的需要,而是投资的需要,甚至可以理解这是一种储蓄行为,是在负利率情况下,为避免财富缩水而不得已而为之的一种行为。这种"真实需求"既真实又不真实,我估计有
30%,甚至更多的购房行为,是我说的这种情况。如果有一天我们的利率加上去,不是负利率了,我们才能看清事情的真相。房子的供应量可能是过分充足了,而我们的自住需求可能并没有现在所看到的那么高。以我的观察,现在整体房价已经到了不可再持续、不可再承受的地步。看一些简单的指标,租金回报率只有2.5%,一些大城市可能更低。小两口买一套房子,不吃不渴都要二三十年,这种日子还怎么过?这对中国人整体的财富是很大的伤害。而且父母年纪大了,养老怎么办?对豪宅来说,中国财富加速集中,有钱人越来越有钱,所以豪宅影响不大,一不小心可能还有较大的空间。但是,普通房子的价格可能无法再持续增长了。中央政府看的很清楚,所以利用各种手段来限制买房,来压制房价增长空间。一方面房价问题已经严重影响了老百姓的生活,甚至引起社会动荡;另一方面还有一种阴谋论,据说美国这一波大放水是一个大阴谋,美元在美国停留并不多,很多都换成其他国家的货币流出去了,集中流到了新兴发展国家,抬高了当地的资产价格。某一天美国经济缓过气来了,美元一走强,货币回流,资金一撤,楼股齐跌,汇率急贬,新兴国家的经济可能会受到重创。现在这种阴谋现在都成了阳谋了,全世界各国都在采取措施限制热钱流入。我自己判断,我们这三年货币发行如此泛滥,而且负利率,房价一再上涨引发羊群效应,大家都去买房子。现在房地产业感觉已经到了阶段性增长的末期,泡沫是不小的,需要一个较长的时间去消化,甚至不排除较大调整的可能性。现在多空双方处于僵持的状态,如果某天出现一些经济问题,例如出口不景气了,房价走势会变得很敏感。
5. 出口也面临压力
出口压力很大。去年超越德国成为世界第一出口国。今年1-10 月份,出口额和09 年同比增幅有百分之三十六,但和2008
年同期相比增长只有6% (这是在人民币汇率对美元只上升了2.5%的情况下做到的,而同期亚洲多个主要国家汇率上升了6%---16%)。我们已经是全球出口老大,增长潜力当然是有限的。今年的广交会情况一般,石广生给自己定的目标是明年出口增长9%。但是,我觉得,如果只有9%,增速就太低了,会引发很多问题。明年出口有很明显的压力,不利因素很多,汇率上涨、劳动力价格不可以逆转的提高、农产品价格上升,还有原材料价格上涨等。美国经济虽然还不错,但是属于无就业的增长,预期几年内可能都是这种情况,这样美国的消费就很难起来。欧洲比较糟糕,除德国外普遍举步维艰,爱尔兰意大利葡萄牙等这些国家,长远来看它们国家财政都是要破产的,对中国未来的出口增长都不是什么好消息。我们国家的出口,也许2011
年还可以,但是到2012 或者2013 年就需要很谨慎了。出口事关我国货币发行的增量。2004-08 年,外汇结汇占新增货币M2
接近50%,09-10 年约占28%。如果出口顺差缩小,对我们的货币环境都有较大影响,对资产价格也会有较大影响。而且我们货币如此滥发,真实通胀如此之高,如果某天经济形势不太好,资金外流,人民币升值的进程甚至可能会逆转。这些都是水和面的问题,水太多了,前所未有,但是我们的面不够。我们的通胀很有可能会起来,因此要做坏的打算,甚至不排除恶性通货膨胀的可能性。
今年年底M2 会达到71 万亿左右,仍然维持15%增长的话,明年83 万亿,后年96 万亿左右。2012 年GDP
按照8%的增长也就44 万亿。M2/GDP 将是218%,非常惊人。这是可持续的吗?现在大家在讨论明年的信贷规模是多少,6万亿、6.5
万亿还是7 万亿,我认为大家没有看清楚问题的关健所在。我们现在应该考虑的是,趁现在全世界经济还可以,怎么迅速进行一些调整,产业如何升级,房地产价格较高,怎么通过一些方法让泡沫逐步释放。如果明年仍然维持六七万亿的信贷规模,可能会带来很严重的恶性通货膨胀。现在我们国家的经济,已经到了好消息不太敏感、坏消息很敏感的时候,如果有一天出口不太顺利,我担心会产生骨牌效应。我很关心出口和房地产的形势。有很多角度可以观察中国经济,我选择"单一规则"---用货币增量这个角度。我们国家现在的经济局面有点类似95
年通胀之初,当年通胀最高达到27%,当时朱�基选择硬着陆,大幅加息,压制了通胀,奠定了后来很多年经济发展的基础。现在当然没有人可以象朱�基那么铁腕,但是我们要认真考虑怎么给经济逐步降温,不断回收流动性,给民企更多发展空间,有更多可投资的领域。需要更多的面,把水吸收掉。未来,我希望能有较大的加息,以及我们的汇率可以升的更快一些,控制热钱的注入。整体来讲,明年下半年至后年,很多不好的经济问题会逐步浮现出水面。但我不认为现在大家已经比较充分的意识到这一点。
(五)对巴菲特的误解
很多人认为巴菲特教导我们潜心研究公司而不必关心宏观经济,这种认识是错的。1968
末他关闭合伙基金公司是很明智的,后来美国经济和股市都陷入长期的调整。2008
年金融危机之前,长达三四年的时间,巴菲特的公司资产有四五百亿美元的现金,一直都没有做大的投资,直到金融危机之后,他才开始大规模投资,能用的现金都用完了,而且还采用发行新股方式收购铁路公司。他是这波金融危机最大的赢家。巴菲特是真正研究宏观经济的高手。判断非常准确。宏观面对股市影响实在是太大了,想做好投资,不花心思研究是不行的。
二.股市波动性
(一) 钟摆
经验表明,投资者的情绪总是从极度悲观到极度乐观,股市因此总是大幅波动。股市的波动性恰如一个钟摆,总是从一个极端走向另一个极端。有波动,就有机会,有大的波动,就有大的暴利的机会。一方面我推崇价值投资,但另一方面,我认为选时远比选股更重要。我们要作判断,在股市的低潮要尽量回避,在高潮时也要有减持的勇气。有一个知名投资人赵丹阳,2007
年3500 点时就清仓了,后来股市涨到6000 多点,利润最丰厚的一段他是完全没有享受到,我想他是不是建立了一个模型,认为3500
已经估值到顶了,因此就不玩了。这很可惜。股市的波动性非常复杂,也非常吸引人,我们要掌握其中规律。
(二) 股市和宏观面直接紧密相关
宏观面对公司业绩、整体融资环境和市场信心都有重大影响,直接影响股市。我想股市大的波动有七八成的原因是宏观面的变化。经验表明,股市一般在宏观面见顶之前三至六个月见顶,在宏观面见底前三至六个月见底。
(三)股市波动的本质是资金的运动
钱涌向哪个公司,哪个公司价格就会上涨。股价波动,最本质的的原因就是资金运动。巴菲特的价值投资理论也是解释资金运动的其中一种理论,而不是相反,公司有竞争力,业绩不错,因此大家长远看好它,资金追捧它,因此股票价格就上涨了。有很多不同的投资理念,实质都是解释特定情况下一些资金的运动规律。股市资金运动可由以下几个因素作出判断:
1.货币增速


研究市场资金动向的指标,比较敏感的有M1 增速,M2 增速,M1 增速--M2
增速等。有很多实证分析,非常值得重视。给大家展示两幅图,上面这幅图很有名气,是招商证券的研究员罗毅写的,叫《M1 定买卖》,发表于去年11
月份3300 点左右的时候,当时M1 的增速达到34%, 他说市场很危险,是极好的卖点,市场可能会回调两成至2300
点,不幸被他准确言中了。他从数据推演出,从1996 年到现在,如果每次M1
增速接近10%就买入股票,超过20%就卖出,投资标的是深成指,从1996 年到09 年11 月,投资收益率是95 倍。
高善文也有一篇报告《钱多了,钱少了》分析M1--M2的剪刀差,判断股票市场资金的充裕性,从而做出买卖操作,也挺有见地的。
2.PEPB
市场整体的PEPB 水平是衡量市场整体估值水平比较好的指标,也是评判资金参与投资股市风险的一个很好的指标。2001
年最疯狂的时候,两市整体PE 是70 倍。2007 年10 月,市场6200 点,历史新高,当时深市PE 是69 倍,上海是50.6
倍。目前全体A 股PE 以中报来计算22 倍,去除银行股后是30 倍,如果再去掉两油和煤炭,整体PE 大概36 倍。中小板55
倍,创业板74 倍。PB 水平大概4 倍,去掉银行股大概是3 倍多一点。毫无疑问目前创业板和中小板整体估值绝对是过高了。情形就像00
年的网络股狂潮,估值高得不可议,但大家会说,公司业绩增长很快,股价还能涨。类似的情形历史上出现过很多次,每次泡沫发生前,都认为这次没事,因为这次和以往是"真的不一样了",但结果呢,"每次都是一样"的。目前创业板的泡沫是很明显的,是不可持续的,什么时候破掉只是一个时间问题。对比几个指数,恒生指数,以今年计算16
倍左右,道琼斯是14 倍,标普500 大概15 倍。历史上道琼斯平均是20-25 倍,纳斯达克高一点,25-30
倍左右。这么看美国香港还可以,A 股总体来看也不算很高,但也不低了,中小板和创业板很高,这个泡沫需要慢慢消化。
3.资金成本----利率
利率和通胀率直接相关。利率的倒数往往是市场的价格中枢,即常态下可接受的PE 水平。如果利率不断下调 ,那合理PE
也要调高。日本股市的PE 有五六十倍,因为日元的利率将近零。在美国,一个经验是,如果利率低于4%,即使利率再往下调,投资者可接受的合理的PE
也就是25 倍,,哪怕利率降低到2%,市场PE
也很难往上调到那里去,这是为什么呢?原来,从70年代起一个主流的观点认为,适度的通胀是有益的,3%--4%是合理的水平。我查了一下1997-2008
年金融危机未发生前11 年的数据,美国的通胀率在3%左右。大家预期即使现在通胀高于4%,,美联储也会想方设法压低通胀至4%
以下,这就是利率低于4%之后,美市PE 往上调整会出现钝化的原因。今年10 月19
号美联储主席伯南克的在一个重要会议上说,大部分货币官员认为核心通胀率应保持在2%或稍低的范围,这是伯南克在公开讲话中第一次如此清晰地把一个具体数字作为美国央行的通胀目标。现在很多分析认为,美国维持这样的低通胀水平可能会持续多年。如果市场普遍这样认同了,那我认为道琼斯指数可以有系统性的提升估值的机会,也许合理的PE
水平就不是25 倍了,说不定市场可以接受30 倍左右的水平,现在道琼斯的PE 只有14
倍,所以我对美股挺乐观的,美股创新高相信只是时间问题。现在港股的利率也很低,也可能跟随美元利率长期维持,港股的前景也值得看好。
4.换手率
市场能不能上涨,要看多空双方的力量比较,换手率非常值得重视。在低点往往成交极度萎缩,因为想买的人少了,在高位,一般放天量,因为想卖的人多了。虽然说买和卖的金额无论在什么点位都是均等的,但实际上决定方是不一样的。如果在比较高的位置换手率急剧扩大,是很值得警惕的信号。我最近测算了一下,10
月26 号那一天,深沪总市值是31 万亿,流通市值是19.4 万亿,成交金额约5200
亿,换手率(成交金额/流通市值)2.7%,成交金额除以总市值是1.68%。创业板和中小板的换手率分别都超过5%。看看2007 年10
月份,平均总市值28 万亿,流通市值8.91万亿,平均成交金额1951
亿,换手率是2.18%,平均成交金额除以总市值是0.697%。现在的换手率比当时的换手率还要高。成交金额除以总市值是当时的两倍以上。所以,这么高的换手率不是很好的事情,由此判断A股的上升空间可能有限。现已经达到5000
亿了,上到3500 点就需要6000-7000 亿,上升到4000 点可能需要8000 亿水平,能持续吗?
看看香港,10 月26 号,总市值20.8 万亿港币,成交金额900
亿,换手率0.4%,和历史上的高水平还有较大距离,从这个意义上看,香港股票向上走的空间大一些。
(四)反射理论
反射理论是索罗斯提出的。我觉得没有比它更好的解释市场波动性的理论。有人总结,在投资方面人类常见的情绪有16
种,比如代表性效应:认为趋势会一直持续下去;错误共识效应:高估与我们所见略同者的人数;选择性认知:只愿意聆听有利于我们判断的意见,对不同的意见置之不理。人类的这些情绪会影响股票市场的表现,索罗斯据此提出了反射理论。
反射理论的核心是指投资者与市场之间会互相影响。投资者根据掌握的资讯预期市场走势并据此行动,而其行动事实上也反过来影响市场走势,甚至改变市场原本可能的走势,一个大的趋势一旦形成后,二者会互相加强,互相巩固,直至"无可动摇",索罗斯称之为"加速期",当认知和真实差距很大时,形势就会失去控制,直至逆转。
这个理论,有点抽象,但很有意义,操作性也很强。他根据这个理论有过多次成功的操作,当然也有失败的。他的做法是,冷静地研判市场会否出现一个大的趋势,先做一个假设,然后不断根据市场本身及投资者情绪的变化,看这个假设的趋势是否能够确立,从而进行布局。这个理论里面,有一个"加速期",我觉得是非常有价值的概念,可以据此研判市场是否在顶部或底部。我总结它有几个特征:
1.市场形成高度的共识,,包括分析师、基金等形成高度共识。
2.在顶部时,总有些举足轻重的公司进行大规模融资,市场解读为利好,还要上涨。我最有印象是07 年8 月的时候,万科以31
元,一个很高的价格,配股融资大概约100 亿元,消息传出之初,万科的价格从20 多,一口气涨到36块钱去了。真正实施时,股价直奔41
元,市场把它解读为重大利好。之后,万科走势一落千丈,最低跌到三块多(除权后)。
3.在底部时,一些举足轻重的企业回购股票 ,市场不理不睬,还要下跌。我们看到04~05
年的时候,这种情况很普遍,一些非常优秀的公司大量回购股票,但市场置若罔闻。因为很多时候,公司比任何人都要了解他自己的实际情况,高位时,它觉得可能高估了,会考虑以增发的方式,拿到更多的钱,在低位时,它觉得,价格太低估了,这么好的价格,我为什么不买进它注销呢?
4.在一个很短时间里,股指或者一些非常重要的公司,会出现突然间加速上升或加速下跌的情形,这要高度警惕,形势可能要逆转了。黎明前的黑暗,会特别的黑,登顶之时,有人会发疯。
(五)旁观者
下面我谈谈"旁观者"的问题。
基于反射理论,索罗斯认为,一般人很难判断股票市场的走势,为什么呢?因为你本身就是市场,你已经和这个市场融为一体,互相影响,你的判断影响股市的走势,股市的走势反过来又影响你的预期。那我们怎么能够走出这个误区,研判市场大势呢?我想应该做个"旁观者",和这个市场若即若离,才能"旁观者清"。如果和这个市场离得很远,就很难发现本质、发现主要矛盾;走得太近,各种锁碎消息充斥市场,会让我们迷失方向,干扰我们对主要矛盾的判断。我们要做一个bystander,以旁观者的心态看待市场,我们要离这个市场既远又近,始终把握主要矛盾,客观认识这个市场。如何做一个"Bystander",这是一个重要课题,对我而言,始终这样要求自己。
这是成功投资的关键所在。
但是,非常难。(整理演讲稿时,我想补充一点,就是"一个意见都不能提"。我多年前做投资总监,年少轻狂,无知者无畏,经常对上市公司的经营指手划脚,实际上公司是什么样子就是什么样子,从来就没有因为你的意见发生过什么改变。就如一个人是很难被改变的,一个公司亦然。企业经常环境非常复杂,不在第一线的管理层,根本很难根据形势改变提出对策。我们提出的意见,99%不是废话就是错误的,1%也许有点真知灼见,但是,如果是一个好公司,它自然会意识到,对一个坏公司,再怎么提,也没有用。如果我提出意见,我就会想,公司如果按我的意见这么去做,经营方面就会如何如何。这往往是一厢情意的,而且会严重影响对这个企业的客观判断。我们所需要做的事情就是观察,持续观察!这是做一个"旁观者"的重要一部分。)
(六)对巴菲特的误解
巴菲特说过,纽交所即使停市三年,对他都没有什么影响。费雪也说过,在股市中有两种挣钱的方式,第一种是挣股市波动的钱,第二种是挣企业业绩增长所带来的钱。听起来,他们似乎认为研究股市的波动性不重要,研究公司的增长就够了,价格波动和公司业绩增长是两件不相干的事情,很多人接受了这种说法,他们认为买进一个好公司,预期长期会有增长,就不管股市了,长期守着它。07
年高潮的时间,这种论调很流行,有人买了万科招行,就打算放个20
年,结果是损失惨重。实际上,巴菲特早就讲过:当别人恐惧时我贪婪,别人贪婪时我恐惧。他说股市停市对他的投资没有影响,只是想特别强调我们选股时要立足长远,而不能太关心股市短期的波动。很多人片面理解了他的话。公司上市后,就具备了双重属性。一方面,它是一家公司,要持续经营,另一方面,它是一个交易品种,就会有波动性。两个属性都很重要,都要研究。
巴菲特在高潮时会卖股票,在低潮时他很开心,因为买股票发财的时机又到了。07 年的时候,中石油,涨了5 倍后,他卖掉了。08
年,在金融风暴最激烈的时候,他不但把能买的钱都买了,而且还通过增发股票的方式收购铁路公司。
另外一方面,巴菲特个人的持股和他的上市公司(伯克希尔)的持股,不是一个概念。在08 年7
月,他曾经接受过一个电台的采访。他说,我之前的个人的投资100%是国债,现在开始是满手的股票。不久后还有一次,他接受采访时说,我的持仓里面大部分是富国银行。大家可能认为他公司有什么股票,他的个人投资也是什么股票。公司持股在大跌时没卖,他自己的持股也不卖。实际上,情况不是这样的。市场不好的时候,他自己也曾经空仓,彻底空仓。而他的公司,目前股票仓位只占公司总资产的20%多,即使市场不好,公司的仓位也必须保持,这是没办法的事情。但是很多中国投资者并不了解这个情况。
另外我认为,投资股票跟做企业是不一样的。做企业的时候,无论是高潮还是低潮你都要持续经营。不能说高潮的时候,就把它卖掉,低潮的时候再把它接回来,没有这种事情。一些伟大的企业,低潮的时候它要抓紧时间进行收购,进行扩张。但是我们做股票不是那么回事,高潮时把它卖出去,低潮时把买回来,才是明智的选择。
而且,一个公司,在陷入低潮的时候,没有人可以告诉你它一定可以东山再起,因为不可预知的因素太多了,一些技术方面的变化,一些管理团队的变动,可能都会对它的持续经营造成致命的影响,有些坎可能过不去就过不去了。我们死守一个公司,风险实在非常非常高。最近的一个例子是柯达,几十年的大蓝筹,现在一败涂地。但我所说的意思不是指普通意义上的波段操作。我觉得中短期的波动,是难以预测的;但是一个长期的波段,周期方面重大的波段,是可以分析和预期的。我们一定要研究
三.公司经营分析
(一)公司本质:价值观和执行力
我认为,一个公司就是它的价值观加上执行力。
价值观,我的理解有三点:第一点是它的愿景,它想成为一个什么样的公司,第二点它如何定义和员工的关系,第三点它如何定义的和客户的关系。这些就是价值观的全部。
讲个例子,万科,我很熟悉的公司。它的愿景是做一个纯住宅的公司,至少在08
年之前是这样,后来做了一点调整,也做一点商业地产。而且它想成为一个令人尊重的公司,它整体的住宅设计、质量,尤其是物业管理都做得很好。
它如何对待员工呢?它每年都会请盖洛普做一个员工满意度调查,一直都很高。它有非常尊重员工的文化,希望和员工一起共同成长,经常做培训,并给员工不错的的待遇。和客户的关系呢?我买过万科的房子,我觉得万科的物业管理在行业里是遥遥领先的。它的服务理念,它对住户的承诺真是非同凡响。所以它不但有价值观,而且它是真正这样去做的。虽然我几年前就把它的股票卖掉了,但我仍然认为它有机会成为一家伟大的公司。
问一个普通人,他的人生哲学是什么,他不清楚,甚至认为这是自寻烦恼。只有最优秀的人,他会认真思考总结,他有一套完整清晰的为人处事的哲学,他就是这样做的。公司亦然。
执行力包括:一是员工赏罚晋升机制,二是质量管理,三是客户关系管理,四是研发和创新机制。
(二)选股标准
1 .护城河
我评估公司是否优秀和强大的指标只有一个,那就是巴菲特常说的护城河,它的深度到底有多深。除此之外,我认为无须第二个指标去评判。没有比这更核心的东西了。护城河一般源于专利技术、服务质量或者产品质量。Intel,它的护城河是技术专利,万科星巴克这些公司,它的护城河是它的服务和质量。形成很深的护城河是一件非常困难的事情。
研究公司,看它的报表,只是研究的一小部分。感受它的价值观,评估它的护城河的深度,不是件容易的事情。一个公司"软"的东西,比"硬"的东西更重要。企业经营非常复杂,而且变化很快,从这个意义上说,我认为,公司基本上是无法预测的。从长期看,护城河很深的公司,总是不断会给人带来惊喜;护城河很浅的公司,就会不断给人带来坏消息。你选择一家公司,它有一个好的团队,有很深的护城河,就行了,剩下的事情,你去预测它,其实是浪费你的时间,但你可以去跟踪它,观察它。我如此强调护城河,因为这是成功选股的关键。
2.三点标准
我这几个标准是消费类公司的选股标准,其实稍稍修改一下,别的公司也适用。我自己是真的这样去选股的。
第一:善待客户。对于消费类公司,我们一定要注重客户对它的产品有多满意,投诉机制是否顺畅。这点非常非常重要。只有客户满意度很高的公司,才值得我们研究。如果客户对这家公司的产品不满意,那多好的财务数据都是没有用的。
第二:善待员工。一些非常优秀的公司,员工对公司的满意度都是很高的。他们会为身为公司的一员为荣。像万科,毕业生如果能去万科-----地产界的黄埔军校,他觉得挺骄傲的。在互联网业,如果能到腾讯或者到百度,这个员工也觉得挺自豪的,能去这个公司很不容易啊,证明自己很优秀,他也愿意为这个公司有所付出。我经常问他们员工工资收入水平,晋升是否主要是内部产生,每年是否有一定的淘汰,工作是否有成就感。要了解这些,就必须和公司的各个阶层,包括高中低层都有接触。要了解员工的士气,最好的办法是问最前线的员工,看他们是否满意,斗志是否高昂,如果他们对公司诸多指责,那么就要小心一点。我们要深入公司,和各级员工建立一些私人的关系。
第三:产品创新。在超市,看一看公司有没有经常推出新产品,卖得好不好。如果你是招行的股东,经常去他们的网点,看看他们的服务好不好;如果你是腾讯的股东,你可以下载他们的软件,看看有没有些很酷的新功能,和别的公司的产品性能上对比一下。要亲自体会。商业竞争实在太激烈了,新产品的研发和创新是利润持续增长的保证。
最好的消费类公司的产品,不仅仅是一种产品,而是一种生活方式,如果我们有幸能遇到这类公司,就可以做为长期投资目标。巴菲特当年投资的《华盛顿邮报》,美国中产阶级就读这份报纸,觉得是一种身份象征。你看凤姐,她读的是《故事会》《知音》这些,说明她品味很高,知识渊博,这也是一种身份的象征。你在星巴克一坐,你会觉得自己是个小资了。腾讯的产品,当然由于最近和360
斗,名声不太好,你看很多年轻人,生活在QQ 上,聊QQ、玩QQ游戏、QQ 空间、QQ 秀、听QQ
音乐,偷瓜偷菜等等,当它成为一种生活方式的时候,你就发现它的黏性特别强。
如果一家公司它的产品有这种特质,往往有机会成为一家伟大的公司。如果它的产品甚至能达到某种垄断的程度,那就更好了。再举腾讯这个例子,这次名气受损得厉害。但是虽然你觉得不好吧,但最后大家还是要用它。垄断就是这么好的一件事情。虽然你不满意,但是又不能找到可以替代的东西,只要就用它了。企业总会有些行差踏错的时候,不好的时候,垄断性的产品,就能始终维系和客户的关系,有巨大的抵抗风险的能力,在碰到顺境的时候,它会比任何对手都要增长迅猛。所以碰到垄断型的企业,市场会给很高的溢价。像三一重工、中联重科这样的企业,基本是这两大寡头垄断了中国的某个细分市场,具有很强的提价能力、议价能力。对投资而言,垄断是比较好的事情,是重要的战略投资标的。
从财务角度来看,简单谈谈,我们希望有好的ROE,有好的毛利率,少负债,最好不负债。依我的选股标准,如果公司的毛利水平没有40~50%,那根本连看都不要去看它。毛利水平很低的公司,降价的时候,风险很大,抗通胀的能力很弱,不是一个好的长线投资的标的。
(三)公司的生命周期
我把一个公司的生命周期分为:初创期,成长期,平台期和衰落期。长期投资于不同周期的公司,获得的回报是完全不一样的。从一开始投资的时候,就要建立一个框架,它到底处于什么的一个阶段。我最喜欢投资的公司,是处于成长期的公司,它已经脱离了初创期的危险,公司业绩开始大幅成长,那么我们有机会获得丰厚的利润。处于平台期的公司,是不值得长期投资的,最多只有一些阶段性的交易机会。
整个成长期,我把它再细分成高速成长期、稳健成长期和缓慢成长期,接着进入平台期。我先给大家介绍这个概念,到后面谈到公司估值时再和大家详细讨论,这关系到股票买点和卖点的问题。
那么我们怎样判断公司处于成长期的什么阶段呢?这是一件重要的事情,大致可从四个方面判断:首先,要看市场容量,有时候要看细分市场的容量,韦尔奇有个"数一数二"的策略,是指在细分市场里数一数二;第二,看核心产品的市场份额,如果份额已经很高了,那就意味着成长起来就比较困难了,如果比较低,业绩增长的潜力就大些;第三,要看公司新产品储备;第四,要看它的并购策略,尤其对科技公司。
(四)巴菲特的遗憾
巴菲特常有谈论选择公司的标准,他非常强调护城河,但是,很少谈论公司处于什么成长周期,我觉得这是一件非常遗憾的事情,甚至误导了不少中国投资者。实际上,从98
年开始,他的股票投资做得非常一般,真正成功的投资只有中石油和比亚迪,但相对他管理的资金规模,金额都不大。在98
年之前,他有过非常成功的投资,例如吉利剃须刀、可口可乐、盖可保险、喜诗糖果、运通信用卡、富国银行,无一例外全都是消费类公司,而且都处于比较高速增长的阶段,股价都涨得很好。98年之后,也许他公司规模太大了,或者他年纪太大了,以至于他很难体验一些新的产品,对消费产品的的敏锐性就不够了,所以就难以重演当年的神话了。现在他投资了很多能源、公共事业类的公司,这些公司的成长能力当然不会很好。因此,我们学习巴菲特选股,是指学习他在98
年以前的选股,而不是现在。可口可乐,是一个很好的例子,现在的可口可乐股价,比98 年跌了30%。他在02~03
年的时候检讨过,说在98年的时候就应该把他卖掉,因为当时可口可乐在中国这样的新兴市场里的占有率已经很高了。上个月,央视采访巴菲特,但斌向他提了这个问题,说你的可口可乐十年来跌了
30%,你怎么看啊?巴菲特当时顾左右而言他,说公司很优秀啊之类的话,随便对付过去了。我觉得挺很遗憾的。一个公司,哪怕它的护城河再深,它的管理团队再优秀,当它过了成长期,进入平台期,从长期来看,它给你的回报往往是不理想的。万科是一个非常优秀的公司,我非常欣赏,但是可能最好的成长期已经过了,可能就不见得是很好的投资品种了。
四.公司估值
(一)买点和卖点
巴菲特反复强调"安全边际",好公司在较高折扣时买入,才有安全边际,才有好的收益。如何给公司估值呢?他提出了一个现金流折现的模型,这个模型理论上是放之四海而皆准的。它要求预测公司未来五至十年的增长速度并计算其未来现金流,然后根据一个合理的折现率,计算出今天公司值多少钱。例如现在公司每股值10
块钱,当跌到5 块钱的时候就买进它;未来当升到20 块钱的时候,就卖掉。可见,这个模型不但是个估值体系,而且还是个操作模型。
但是我发现,根据这个模型去估值、去操作,非常困难。
首先。因为我们很难预测公司未来五年或十年的增长速度,折现率多少比较合适,也很难判断,因为折现率理论上和利率相关,利率理论上和通胀率相关,要判断通胀率谈何容易。由于要计算的时间很长,增长速度或者折现率稍稍作些调整,计算出来的估值会出现巨大的差异。我有些朋友在07
年8 月份40 块钱买入万科,当时万科已经3000
多亿市值了,他自己也用模型算了一下,觉得也不贵啊。除了现金流折现这种模型,现在还出现了更多的估值模型,其实原理基本是一样的,同样问题很多。根据反射理论,在股价涨跌的过程中,我们的预期会不断发生改变。大家如果多看看一些券商的研究报告,就会发现这个问题。股价涨了,它的估值模型的参数就变了,目标价就提高了,股价如果超过设定的目标价后,分析师继续调整参数,目标价更高了。如果跌了,处理过程也一样。今天是一个估值,股价大涨或大跌后,即使公司的经营没有发生任何改变,公司又是另外一个估值。在股市头部和股市底部,相隔几个月,一个公司的所谓估值可以相差几倍,尤其是一些周期类公司,分析师的报告看起来都很有道理。07
年7 月份的时候,看看不同券商的地产研究报告,普遍将万科的目标价调到五六十元,甚至七八十元的,这都是市场上最有影响力的地产分析师的报告。是不是很可笑?由于各种不同模型的参数设置的随意性很大,以至所谓公司估值,成了一个"任人打扮的小姑娘"。实际上,不是分析师给这个公司估值,而是当时的股价给公司估值。这很有趣吧?我越研究,就越发现,估值这件事情,很不靠谱。公司无所谓估值,你不能给它一个准确的估值。如果非要说有估值,那干脆说它有好几个估值。研究买点和卖点,哪个价格值得买进,哪个价格可以卖出,比研究公司值多少钱更有意义,操作性更强,也许更符合股市波动性的特点。
那是不是我们就根本不要管什么主流估值模型了?不是,这些模型的假设都是有一定道理的,我们要了解市场的共识到底是怎么回事,然后才能利用它来发财。我之所以说,所有的估值都不靠谱,是因为我了解市场上基本上所有的模型,这些模型都有些什么重大的缺陷。这样我们才能更好地判断买点和卖点。
(二)市场中的主流估值方法
简单分类一下,市场有三种估值方法。
1. 成长性估值
第一种是成长性估值,我们又可以分为两类:一类是低速成长公司估值,另一类是高速成长公司估值。给低速成长公司估值,有个模型叫做所谓的"戴维斯双杀"。你以比较低的市盈率买入一些低速成长的公司,举个例子,比如说以10
倍的市盈率买入一个公司,它每年的成长也不高,10%吧,持续3 年之后,市场认为它的增长比较稳定,因此给它的合理PE 从10 倍提到了13
倍。你的回报是1.1*1.1*1.1*1.3,挣了超过70%的钱。这个模型可靠吗?见仁见智,我很怀疑。对于一些比较稳定的公用事业类公司,由于产能扩张引起低速增长,可能有些道理。了解一下吧。
对于比较高速成长的公司,情况会比较复杂,现实中它的波动性会非常强,价格容易大起大落,谈所谓估值基本没有意义,我们要讨论的是以下几个问题:
(1)卖点
我介绍过,一个处于成长期的公司,成长期又可以分为高速成长期、稳健成长期和缓慢成长期,然后进入平台期。我认真研究过,在高速成长期的后期,或者是稳健成长期的中后期,市场会给公司一个很高的估值,PE
会很高。在高速成长期的末期,市场往往会极度不理性地给它一个不可思议的PE。我们看01
年的时候,网络股泡沫就是这样吹起来的,有些网络公司之前可能以每年400~500%的速度在增长,市场就认为这种速度可以长期持续,一个没有任何赢利的公司也可以卖出一个天价,这当然是一个好的卖点。高速成长期之后,到了稳健成长期,在它的中后段,市场给他的PE
会稍稍回落,但仍然能维持一个比较高的估值水平,这也是一个比较好的卖点。一旦进入缓慢成长期,价格往往会大幅下跌,它的PE
水平就会大幅下滑。看看Google,一家伟大的公司,稳健成长过去之后,现在的PE 水平大概只有24 倍。微软也是,微软现在的价格是2000
年最高价的一半不到,PE 只有 11 倍,过了稳健成长期,公司价值可能大缩水,所以判断它现在处于什么成长阶段,是何等重要的一件事。
另外,涨不动的时候,也是一个卖点。怎么解释这个问题?我们经常会琢磨,公司经营情况如何,这个公司的目标价是多少。如果有一天,发现它涨不动了,各种好消息都出来了,它还是不涨。那它现在是什么价格,那它就值什么价。不要和市场过不去。这个道理听起来容易,做起来不容易。
(2)买点
好公司碰到不好的时候,是最佳的买入机会。或者是由于股市大跌,或者是行业遇上低潮,或者是公司碰到了一件倒霉的事情,导致价格大跌,那都是很好的买入机会。
巴菲特说过熊市是投资者的好朋友。每次碰到股市大跌的时候,都是好的买点,所以我们应该很Hi
才对.,而不应该愁眉苦脸。当年他买运通,那时公司刚发生了色拉油丑闻,股票跌得一塌糊涂,但巴菲特认为这一事件并不影响公司长远的核心竞争力,因此加码买进。当年买万科也是,05
年上半年,公司发展得很好,但行业受到宏观调控,市场如惊弓之鸟,一路暴泻,股价跌到你不敢相信为止。这种机会,太难得了。
另外,跌不动的时机,就是买入的时机。举个例子,08 年10
月份的时候,金融风暴,跌得非常厉害,港股从32000点一路往下狂跌,当时的PE 是25 倍多,到底能跌到哪呢?是20000 点吗?18000
吗?不是,跌到10600 点,PE 是6.3倍,令人难以置信。腾讯当时从70 多块跌到40 块,还曾经下探到过35 块。跌到40
元的时候,按当年的业绩算,PE 在23倍左右,仍然是香港,甚至全世界最昂贵的股票之一,还能跌吗?为什么就不能跌到20 块钱,10
块钱?如果跌到10 块钱,就是6
倍多的PE,和市场差不多。那个时候,还连接发生了几件事情:百度出了个"央视门",央视爆光它的搜索广告里有假药广告,大家口诛笔伐,股价从400
多元暴跌到102 元;腾讯广告部的一个领导出了一点丑闻,据说是一些广告费用不清不楚,那叫"广告门",投资者很担心;这还不算,马化腾在41
块钱左右,出了大约1186 万股,震惊市场,他为什么这么做,是不是对公司不看好了?大家吓坏了。但是,这么多很坏的消息出来后,40块钱左右它就跌不动了,我们还能指望它能跌得更多吗?要不要再等等?实际上,跌不动了,就是一个最好的买点。说来容易,做起来也很难。需要冷静分析公司业务,更需要勇气。
(3)和宏观面的相关性
有些公司的业绩和宏观面是强相关,有些是弱相关,还有些比较中性。对强相关的公司,我们在判断买卖点的时候要有定力,要耐心等待周期的顶部区域或者底部区域。对弱相关的公司,我们更多的是了解公司本身基本面的情况,而不必太关心宏观面。这有很多的教训,比如说一些消费类的公司,其实和宏观经济的走势相关度并不太高,很多人等啊等啊,总想在宏观面最差的时候才动手买入,这会错过很多机会。所以我们从一开始就要建立一个框架,判断公司和宏观面是何种相关。
(4)对巴菲特的误解
很多人认为巴菲特买股票后就是"长线持有,一直不卖出"。巴菲特也曾经讲过:"如果不想持有公司10 年,就不要持有10
分钟",事实真是如此吗?巴菲特从60 年代开始至2008 年,有数据可查的,他一共投资了200 多只公司,持有超过3 年的,只有22
只。可见,护城河的深度是需要经常作评估的。一旦公司的经营有问题,就要走人。或者认为未来增长空间有限,也要出货。巴菲特在07
年赚了五六倍卖出中石油就是一个好的案例。
2.重组估值
重组估值,不是我的研究对象。但我觉得大家可能有兴趣。我给大家推荐一篇文章,大家可以百度一下。李XX的《展望'99
股市:谁是重组大黑马?》。他是我以前的一个同事,99 年写了这篇文章。他有严谨的思路,经过认真分析,筛选出20
多只公司,事实情况是,他推荐的这20
多只重组公司基本上都是后来一两年的超级大黑马。直到现在,我都认为这是非常精彩、非常有深度的一篇文章,我们可以学习他的选股思路。后来他自己做私募了,做得很好,收获颇丰。所以我觉得,一个人的财富,和他的的深度直接相关。
3.相对估值
还有相对估值,其实也并不是我研究的重点。刚好有一个朋友做了分析师,有时候向我请教,我就帮忙总结了一下。相信在座很多人,可能经常捕捉热点进行操作,可能也会感兴趣。所谓的相对估值,它的特点是不在乎公司的好坏,而是在乎公司是否在中短期能够上涨。它现在是券商和基金主流的估值方法,讲究的是资产配置、板块轮动、博弈。当然他们也做基本面分析,但不是最重要的,它的核心是敏感性分析,对经营指标、行业政策、经济指标(利率等)、资金喜好等,进行一些敏感性分析。我在此抛砖引玉,希望能给大家开拓一下思路。
(1)总结一个行业里几类公司的盈利模式,各种模式之间的优缺点,核心的几个经营指标是什么。对一个具体公司,什么经营数据的变化,可能对他的经营产生重大的影响。比如说连锁店的商业和百货类的商业,最重要的营运指标是不一样的。地产公司,它的地储备和土地成本,对业绩的影响很重大,对股价影响也很敏感。对网游类的公司,ARPU
值、平均同时在线人数这些指标,就很敏感。当这些营运指标出现比较大的变化的时候,就需要你非常认真去关注了,可能会引起股票价格的比较大的波动。
(2)行业政策对公司经营业绩的影响。比如行业标准、出口退税、税收政策的变动,对公司影响是怎么样的,哪些公司会受益,哪些公司不太好,提前要做好功课。
(3)宏观经济指标,例如利率上调对公司的影响。简单而言,对负债率高的公司影响就比较大一些,对轻资产低负债的公司影响就会小一些。
(4 )资金偏好的敏感性。讲几个例子。一是市值,小市值的公司比较容易受到资金的喜爱。在中国,中小板、创业板的估值比一般A
股公司的高一些,我认为是可以理解的,市值小嘛,我认为这是合理的事情,但也不能过高;二是机构持仓的比重,如果机构持仓某只股票比例已经很高了,那么股价再涨就难了,如果持仓比例比较低,才容易跑出黑马;三是在行情反弹的时候,行业内估值相对比较低的公司,比较容易受到资金青睐;四是有代表性的新公司的IPO,其定价会影响已上市的同类公司,等等。
(5)一些公司经营情况很糟糕,股价很低,这种公司往往是敏感度很高的公司。它经营方面稍稍有改善,就可能对业绩产生重大影响,同进也存在一些重组、收购和资产注入的可能性,容易成为黑马。
(6)高度关注公司的增发和回购。
(7)关注管理层激励。公司业绩是很容易受到操控的。我们要认真研究管理层激励的具体条款。它可能会对公司的未来业绩有所指引。
还有更多,希望大家不断总结。
此外,对于一个分析师,他的中心任务是持续地系统性地寻找黑马和回避风险,我们可以做两个测试和一个评估。第一个测试叫股价强弱测试,还有一个是好消息坏消息测试。它们的理论模型是这样的,叫内部消息泄露模型,或者叫老鼠仓模型。比如说一个公司已经公布季度业绩了,三个月之后才会再次公布业绩。我做一个假设,这三个月里面公司没有发生重大的事情,行业方面也没有发生什么重大的事情。那么在这3
个月里,能影响股价波动的,应该只有大盘整体的走势。三个月内股价的波幅减掉股指的波幅,按道理应该是一条直线,对不对?但是情况一般并不是这样的,往往有波动,甚至大的波动。为什么会这样?因为发生了内部消息泄露。一些内部人,比如说董事长、总经理、财务总监或者销售总监等,了解公司的经营情况,消息就这样泄露出去了,他的亲人,或者好的朋友,或者一些关系较好的基金经理,就会不断地买进或者卖出,导致股价有所波动。
股价强弱测试:就是周期性,如一周或一月,对同一个行业里的一批个股,或者找一批最有相关性的个股,比较彼此走势的相对强弱。如果发现某个公司股价持续相对走强或持续相对走弱,作为分析师,应该打个电话或登门拜访了解一下情况了。
第二是好消息坏消息测试:就是当公司遇上好消息或坏消息时,对它的股价表现进行打分。例如政府出台了一个地产利空,地产公司平均跌幅在5%,但某个地产公司只跌了1%,甚至还涨了,那就给它打个很高的分数。或者,公司公布了一个重大利空,分析师猜测应该会下跌5%,但是它只跌了1%,甚至还上涨了,那也给它打个高的分数。如果这种情况持续的话,可能是由于公司经营方面起了某些变化,那么分析师就应该关注了。
坚持运用这两个工具,因为都是可以量化的,可能有助于系统性地分析公司情况。
另外,可以进行一个分析师评级评估。有份量的分析师对公司股价走势的影响会比较大。我们要了解他们是如何估值的,主流的估值模型是怎么样的。我们要分析其平均预期,也就是所谓的市场共识。自己研究这公司后,如果发现公司业绩比市场共识要高得多,那可能是一个好的买点;如果远低于市场的共识,那就是一个好的卖点。另外,如果发现很多分析师高度一致看多或看空的时候,往往是好的买卖点。
五.小结
现在我想做一个小结。市场里有很多种投资理念,每一种都有成功的案例,都有可取之处。我所坚持的理念,和大家分享一下。说简单也很简单。
首先,分析宏观周期,每三五年就是一个经济周期。我们要耐心观察;
其次,制定十分苛刻的选股标准,耐心等候合适的买点;
最后,既然公司是无所谓估值的,我们要和市场若即若离,关注市场的情绪。
六.投资的境界
和做人一样,投资也是有不同境界的。巴菲特的境界很高,高山仰止,我们应该有信心见贤思齐。
有一天我看《庄子》里的《庖丁解牛》,豁然开朗,觉得我们做投资能达到的境界,原来和庖丁没有分别。
《庖丁解牛》故事很精彩。文惠君要找庖丁讲讲他是如何解牛的,为什么水平如此之高,听完之后,文惠君说:"善哉,吾闻庖丁之言,得养生焉。"
庖丁说,宰牛之初,"所见无非牛者",三年之后,"未尝见全牛也"
;下刀时,"依乎天理","因其固然"。我的理解是,任何事物都有脉络可寻,对每一个重要节点,都要认真把握,掌握其规律。例如投资,是一件很复杂的事情,宏观面、波动性、公司经营、估值体系这些节点都很重要,我们要全盘考虑,发现规律。普通屠夫宰牛,一个月就要换刀,好的屠夫宰牛,一年就要换刀,因为他们总是猛割猛砍,当然刀很容易就折了。庖丁不一样了,十九年了,一把刀没换过。
他说,"彼节者有间,而刀刃者无厚;以无厚入有间,恢恢乎其于游刃必有余地矣"。他对骨头关节这些地方,不是一轮猛砍,而是用非常薄的刀,游走于"有间"(空隙)之中,自然游刃有余。
我的理解是,投资是件很寂寞的工作,很多时候是自己对自己心灵的拷问,会遇到很多困难,股价的涨跌,也会对我们的心灵产生很大的压力和影响。我们要力争发现一些重大的趋势,包括宏观面的、或者公司经营层面的,在困难堆积如山的茫茫前程中寻找一条坦途,尽可能回避一些非常痛苦的选择。垃圾堆里找黄金,非常困难;大势很不好,有些聪明人横刀立马,火中取栗搏反弹,很不容易;巴菲特一位很有智慧的好朋沃尔夫曾教育巴菲特:"沃伦,如果你在一个黄金堆里找一根黄金做的针,那么找这根针就不是一个更好的选择。"
可是看一看周围,到处都是拿着放大镜去寻找这根针的聪明人。简而言之,我们要去挣一些容易挣的钱,不要去挣一些很难挣的钱。容易挣的钱呢,容易挣很多倍。很难挣的钱呢,即使我们很努力,付出非常多,但还是很难挣钱。
庖丁解牛毕,"提刀而立,为之四顾,为之踌躇满志",这种感觉太好了,有志于投资的各位,让我们一起努力,共同达至庖丁的境界。