一天精通GNU/Make

基本makefile 结构... 1

Makefile 变量... 3

i) 贮存一个文件名列表。... 3

ii) 贮存可执行文件名。... 4

iii) 贮存编译器旗标。... 4

内部变量... 4

隐含规则(Implicit Rules)5

假象目的(Phony Targets)5

函数(Functions)6

一个比较有效的makefile. 7

一个更好的makefile. 8

ifneg...else...endif11

'filter-out' 函数... 11

'addprefix'11

everything:11

总结... 12

 

 

怎样分解项目

   i) 不要用一个 header 文件指向多个源码文件(例外:程序包 的 header 文件)。用一个 header定义一个源码文件的方式 会更有效,也更容易查寻。否则改变一个源文件的结构(并且 它的 header 文件)就必须重新编译好几个文件。

    

   ii) 如果可以的话,完全可以用超过一个的 header 文件来指向同 一个源码文件。有时将不可公开调用的函数原型,类型定义 等等,从它们的C源码文件中分离出来是非常有用的。使用一个 header 文件装公开符号,用另一个装私人符号意味着如果 你改变了这个源码文件的内部结构,你可以只是重新编译它而不需要重新编译那些使用它的公开 header 文件的其它的源文 件。

    

   iii) 不要在多个 header 文件中重复定义信息。 如果需要, 在其中一个 header 文件里 #include 另一个,但 是不要重复输入相同的 header 信息两次。原因是如果你以后改变了这个信息,你只需要把它改变一次,不用搜索并改变另外一 个重复的信息。

    

   iv) 在每一个源码文件里,#include 那些声明了源码文件中的符 号的所有 header 文件。这样一来,你在源码文件和 header 文件对某些函数做出的矛盾声明可以比较容易的被编译器发现。

    

   2) GNU Make 工具

   ~~~~~~~~~~~~~~~~

    

      基本 makefile 结构

    

   GNU Make 的主要工作是读进一个文本文件, makefile 。这个文 件里主要是有关哪些文件(‘target’目的文件)是从哪些别的 文件(‘dependencies’依靠文件)中产生的,用什么命令来进行这个产生过程。有了这些信息, make 会检查磁碟上的文件,如果 目的文件的时间戳(该文件生成或被改动时的时间)比至少它的一个依靠文件旧的话, make 就执行相应的命令,以便更新目的文件。 (目的文件不一定是最后的可执行档,它可以是任何一个文件。)

   makefile 一般被叫做“makefile”或“Makefile”。当然你可以 在 make 的命令行指定别的文件名。如果你不特别指定,它会寻 找“makefile”或“Makefile”,因此使用这两个名字是最简单 的。

    一个 makefile 主要含有一系列的规则,如下:

    

    :...

   (tab)

   (tab)

    .

    .

    例如,考虑以下的 makefile :

    

    === makefile 开始 ===

   myprog : foo.o bar.o

    gcc foo.o bar.o -o myprog

    

   foo.o : foo.c foo.h bar.h

    gcc -c foo.c -o foo.o

    

   bar.o : bar.c bar.h

    gcc -c bar.c -o bar.o

   === makefile 结束 ===

    

    这是一个非常基本的 makefile —— make 从最上面开始,把上 面第一个目的,‘myprog’,做为它的主要目标(一个它需要保 证其总是最新的最终目标)。给出的规则说明只要文件‘myprog’ 比文件‘foo.o’或‘bar.o’中的任何一个旧,下一行的命令将 会被执行。

    但是,在检查文件 foo.o 和 bar.o 的时间戳之前,它会往下查 找那些把 foo.o 或 bar.o 做为目标文件的规则。它找到的关于 foo.o 的规则,该文件的依靠文件是 foo.c, foo.h 和 bar.h 。 它从下面再找不到生成这些依靠文件的规则,它就开始检查磁碟 上这些依靠文件的时间戳。如果这些文件中任何一个的时间戳比 foo.o 的新,命令 'gcc -o foo.o foo.c' 将会执行,从而更新 文件 foo.o 。

    接下来对文件 bar.o 做类似的检查,依靠文件在这里是文件 bar.c 和 bar.h 。

    现在, make 回到‘myprog’的规则。如果刚才两个规则中的任 何一个被执行,myprog 就需要重建(因为其中一个 .o 档就会比 ‘myprog’新),因此连接命令将被执行。

    希望到此,你可以看出使用 make 工具来建立程序的好处——前 一章中所有繁琐的检查步骤都由 make 替你做了:检查时间戳。 你的源码文件里一个简单改变都会造成那个文件被重新编译(因 为 .o 文件依靠 .c 文件),进而可执行文件被重新连接(因为 .o 文件被改变了)。其实真正的得益是在当你改变一个 header 档的时候——你不再需要记住那个源码文件依靠它,因为所有的 资料都在 makefile 里。 make 会很轻松的替你重新编译所有那 些因依靠这个 header 文件而改变了的源码文件,如有需要,再 进行重新连接。

    当然,你要确定你在 makefile 中所写的规则是正确无误的,只 列出那些在源码文件中被 #include 的 header 档……

    

   2.2 编写 make 规则 (Rules)

    

    最明显的(也是最简单的)编写规则的方法是一个一个的查 看源码文件,把它们的目标文件做为目的,而C源码文件和被它 #include 的 header 档做为依靠文件。但是你也要把其它被这些 header 档 #include 的 header 档也列为依靠文件,还有那些被包括的文件所包括的文件……然后你会发现要对越来越多的文件 进行管理,然后你的头发开始脱落,你的脾气开始变坏,你的脸色变成菜色,你走在路上开始跟电线杆子碰撞,终于你捣毁你的 电脑显示器,停止编程。到低有没有些容易点儿的方法呢?

    当然有!向编译器要!在编译每一个源码文件的时候,它实在应 该知道应该包括什么样的 header 档。使用 gcc 的时候,用 -M 开关,它会为每一个你给它的C文件输出一个规则,把目标文件 做为目的,而这个C文件和所有应该被 #include 的 header 文 件将做为依靠文件。注意这个规则会加入所有 header 文件,包 括被角括号(`<', `>')和双引号(`"')所包围的文件。其实我们可以相当肯定系统 header 档(比如 stdio.h, stdlib.h 等等)不会 被我们更改,如果你用 -MM 来代替 -M 传递给 gcc,那些用角括 号包围的 header 档将不会被包括。(这会节省一些编译时间)

    由 gcc 输出的规则不会含有命令部分;你可以自己写入你的命令或者什么也不写,而让 make 使用它的隐含的规则(参考下面的 2.4 节)。

    

 

 Makefile 变量

 

    

    上面提到 makefiles 里主要包含一些规则。它们包含的其它的东 西是变量定义。

   makefile 里的变量就像一个环境变量(environmentvariable)。 事实上,环境变量在 make 过程中被解释成 make 的变量。这些 变量是大小写敏感的,一般使用大写字母。它们可以从几乎任何地方被引用,也可以被用来做很多事情,比如:

    

    i) 贮存一个文件名列表。

       在上面的例子里,生成可执行文件的 规则包含一些目标文件名做为依靠。在这个规则的命令行里同样的那些文件被输送给 gcc 做为命令参数。如果在这 里使用一个变数来贮存所有的目标文件名,加入新的目标文件会变的简单而且较不易出错。

    ii) 贮存可执行文件名。

       如果你的项目被用在一个非 gcc 的系 统里,或者如果你想使用一个不同的编译器,你必须将所有使用编译器的地方改成用新的编译器名。但是如果使用一 个变量来代替编译器名,那么你只需要改变一个地方,其它所有地方的命令名就都改变了。

    iii) 贮存编译器旗标。

       假设你想给你所有的编译命令传递一组 相同的选项(例如 -Wall -O -g);如果你把这组选项存 入一个变量,那么你可以把这个变量放在所有呼叫编译器 的地方。而当你要改变选项的时候,你只需在一个地方改变这个变量的内容。

    要设定一个变量,你只要在一行的开始写下这个变量的名字,后 面跟一个 = 号,后面跟你要设定的这个变量的值。以后你要引用 这个变量,写一个 $ 符号,后面是围在括号里的变量名。比如在 下面,我们把前面的 makefile 利用变量重写一遍:

    

      

   === makefile 开始 ===

   OBJS = foo.o bar.o

   CC = gcc

   CFLAGS = -Wall -O -g

    

   myprog : $(OBJS)

     $(CC) $(OBJS) -o myprog

    

   foo.o : foo.c foo.h bar.h

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

    

   bar.o : bar.c bar.h

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

   === makefile 结束 ===

    

      内部变量

    还有一些设定好的内部变量,它们根据每一个规则内容定义。三个 比较有用的变量是 $@, $<和 $^ (这些变量不需要括号括住)。

       $@扩展成: 当前规则的目的文件名

       $<扩展成: 依靠列表中的第一个依靠文件

       $^扩展成: 整个依靠的列表(除掉了里面所有重 复的文件名)。

 

利用这些变量,我们可以把上面的 makefile 写成:

    

   === makefile 开始 ===

   OBJS = foo.o bar.o

   CC = gcc

   CFLAGS = -Wall -O -g

    

   myprog : $(OBJS)

    $(CC) $^ -o $@

    

   foo.o : foo.c foo.h bar.h

    $(CC) $(CFLAGS) -c $<-o $@

    

   bar.o : bar.c bar.h

    $(CC) $(CFLAGS) -c $<-o $@

   === makefile 结束 ===

    

    你可以用变量做许多其它的事情,特别是当你把它们和函数混合 使用的时候。如果需要更进一步的了解,请参考 GNU Make 手册。 ('man make', 'man makefile')

    

    隐含规则 (Implicit Rules)

    

    请注意,在上面的例子里,几个产生 .o 文件的命令都是一样的。 都是从 .c 文件和相关文件里产生 .o 文件,这是一个标准的步 骤。其实 make 已经知道怎么做——它有一些叫做隐含规则的内 置的规则,这些规则告诉它当你没有给出某些命令的时候,应该 怎么办。

    如果你把生成 foo.o 和 bar.o 的命令从它们的规则中删除, make 将会查找它的隐含规则,然后会找到一个适当的命令。它的命令会 使用一些变量,因此你可以按照你的想法来设定它:它使用变量 CC 做为编译器(象我们在前面的例子),并且传递变量 CFLAGS (给 C 编译器,C++ 编译器用 CXXFLAGS ),CPPFLAGS ( C 预处理器旗标), TARGET_ARCH (现在不用考虑这个),然后它加 入旗标 '-c' ,后面跟变量 $<(第一个依靠名),然后是旗 标 '-o' 跟变量 $@ (目的文件名)。一个C编译的具体命令将 会是:

   $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $<-o $@

    当然你可以按照你自己的需要来定义这些变量。这就是为什么用 gcc 的 -M 或 -MM 开关输出的码可以直接用在一个makefile 里。

    

    假象目的 (Phony Targets)

    

    假设你的一个项目最后需要产生两个可执行文件。你的主要目标 是产生两个可执行文件,但这两个文件是相互独立的——如果一个文件需要重建,并不影响另一个。你可以使用“假象目的”来 达到这种效果。一个假象目的跟一个正常的目的几乎是一样的,只是这个目的文件是不存在的。因此, make 总是会假设它需要 被生成,当把它的依赖文件更新后,就会执行它的规则里的命令行。

    如果在我们的 makefile 开始处输入:

    

   all : exec1 exec2

    

    其中 exec1 和 exec2 是我们做为目的的两个可执行文件。 make 把这个 'all' 做为它的主要目的,每次执行时都会尝试把 'all' 更新。但既然这行规则里没有哪个命令来作用在一个叫 'all' 的 实际文件(事实上 all 并不会在磁碟上实际产生),所以这个规 则并不真的改变 'all' 的状态。可既然这个文件并不存在,所以 make 会尝试更新 all 规则,因此就检查它的依靠 exec1, exec2 是否需要更新,如果需要,就把它们更新,从而达到我们的目的。

    假象目的也可以用来描述一组非预设的动作。例如,你想把所有由 make 产生的文件删除,你可以在 makefile 里设立这样一个规则:

    

   veryclean :

    rm *.o

    rm myprog

    

    前提是没有其它的规则依靠这个 'veryclean' 目的,它将永远 不会被执行。但是,如果你明确的使用命令 'make veryclean' , make 会把这个目的做为它的主要目标,执行那些 rm 命令。

    如果你的磁碟上存在一个叫 veryclean 文件,会发生什么事?这 时因为在这个规则里没有任何依靠文件,所以这个目的文件一定是最新的了(所有的依靠文件都已经是最新的了),所以既使用户明 确命令 make 重新产生它,也不会有任何事情发生。解决方法是标 明所有的假象目的(用 .PHONY),这就告诉 make 不用检查它们 是否存在于磁碟上,也不用查找任何隐含规则,直接假设指定的目的需要被更新。在 makefile 里加入下面这行包含上面规则的规则:

    

   .PHONY : veryclean

    

    就可以了。注意,这是一个特殊的 make 规则,make 知道 .PHONY 是一个特殊目的,当然你可以在它的依靠里加入你想用的任何假象 目的,而 make 知道它们都是假象目的。

    

   函数 (Functions)

    

   makefile 里的函数跟它的变量很相似——使用的时候,你用一个 $ 符号跟开括号,函数名,空格后跟一列由逗号分隔的参数,最后 用关括号结束。例如,在 GNU Make 里有一个叫 'wildcard' 的函 数,它有一个参数,功能是展开成一列所有符合由其参数描述的文件名,文件间以空格间隔。你可以像下面所示使用这个命令:

    

   SOURCES = $(wildcard *.c)

    

    这行会产生一个所有以 '.c' 结尾的文件的列表,然后存入变量 SOURCES 里。当然你不需要一定要把结果存入一个变量。

    另一个有用的函数是 patsubst ( patten substitude, 匹配替 换的缩写)函数。它需要3个参数——第一个是一个需要匹配的式样,第二个表示用什么来替换它,第三个是一个需要被处理的由空格分隔的字列。例如,处理那个经过上面定义后的变量,

    

   OBJS = $(patsubst %.c,%.o,$(SOURCES))

    

    这行将处理所有在 SOURCES 字列中的字(一列文件名),如果它的 结尾是 '.c' ,就用 '.o' 把 '.c' 取代。注意这里的 % 符号将匹 配一个或多个字符,而它每次所匹配的字串叫做一个‘柄’(stem) 。 在第二个参数里, % 被解读成用第一参数所匹配的那个柄。

    

     一个比较有效的 makefile

    

    利用我们现在所学的,我们可以建立一个相当有效的 makefile 。 这个 makefile 可以完成大部分我们需要的依靠检查,不用做太大的改变就可直接用在大多数的项目里。

    首先我们需要一个基本的 makefile 来建我们的程序。我们可以让 它搜索当前目录,找到源码文件,并且假设它们都是属于我们的项目的,放进一个叫 SOURCES 的变量。这里如果也包含所有的 *.cc 文件,也许会更保险,因为源码文件可能是 C++ 码的。

    

   SOURCES = $(wildcard *.c *.cc)

    

    利用 patsubst ,我们可以由源码文件名产生目标文件名,我们需 要编译出这些目标文件。如果我们的源码文件既有 .c 文件,也有 .cc 文件,我们需要使用相嵌的 patsubst 函数呼叫:

    

   OBJS = $(patsubst %.c,%.o,$(patsubst %.cc,%.o,$(SOURCES)))

    

    最里面一层 patsubst 的呼叫会对 .cc 文件进行后缀替代,产生的结 果被外层的 patsubst 呼叫处理,进行对 .c 文件后缀的替代。

    现在我们可以设立一个规则来建可执行文件:

    

   myprog : $(OBJS)

    gcc -o myprog $(OBJS)

    

    进一步的规则不一定需要, gcc 已经知道怎么去生成目标文件 (object files) 。下面我们可以设定产生依靠信息的规则:

    

   depends : $(SOURCES)

    gcc -M $(SOURCES) > depends

    

    在这里如果一个叫 'depends' 的文件不存在,或任何一个源码文件 比一个已存在的 depends 文件新,那么一个 depends 文件会被生 成。depends 文件将会含有由 gcc 产生的关于源码文件的规则(注 意 -M 开关)。现在我们要让make 把这些规则当做makefile 档 的一部分。这里使用的技巧很像 C 语言中的 #include 系统——我 们要求 make 把这个文件 include 到 makefile 里,如下:

    

   include depends

    

   GNU Make 看到这个,检查'depends' 目的是否更新了,如果没有, 它用我们给它的命令重新产生 depends 档。然后它会把这组(新) 规则包含进来,继续处理最终目标 'myprog' 。当看到有关 myprog 的规则,它会检查所有的目标文件是否更新——利用 depends 文件 里的规则,当然这些规则现在已经是更新过的了。

    这个系统其实效率很低,因为每当一个源码文件被改动,所有的源码 文件都要被预处理以产生一个新的 'depends' 文件。而且它也不是 100% 的安全,这是因为当一个 header 档被改动,依靠信息并不会 被更新。但就基本工作来说,它也算相当有用的了。

    

  一个更好的 makefile

    

    这是一个我为我大多数项目设计的 makefile 。它应该可以不需要修 改的用在大部分项目里。我主要把它用在 djgpp 上,那是一个 DOS 版的 gcc 编译器。因此你可以看到执行的命令名、 'alleg' 程序包、 和 RM -F 变量都反映了这一点。

    

   === makefile 开始 ===

    

   ######################################

    #

    #Generic makefile

    #

    #by George Foot

    #email: george.foot@merton.ox.ac.uk

    #

    #Copyright (c) 1997 George Foot

    #All rights reserved.

    #保留所有版权

    #

    #No warranty, no liability;

    #you use this at your own risk.

    #没保险,不负责

    #你要用这个,你自己担风险

    #

    #You are free to modify and

    #distribute this without giving

    #credit to the original author.

    #你可以随便更改和散发这个文件

    #而不需要给原作者什么荣誉。

    #(你好意思?)

    #

   ######################################

    

   ### Customising

    #用户设定

    #

    #Adjust the following if necessary; EXECUTABLE is the target

    #executable's filename, and LIBS is a list of libraries to link in

    #(e.g. alleg, stdcx, iostr, etc). You can override these on make's

    #command line of course, if you prefer to do it that way.

    #

    #如果需要,调整下面的东西。EXECUTABLE 是目标的可执行文件名,LIBS

    #是一个需要连接的程序包列表(例如 alleg, stdcx, iostr 等等)。当然你

    #可以在 make 的命令行覆盖它们,你愿意就没问题。

    #

    

   EXECUTABLE := mushroom.exe

   LIBS := alleg

    

    #Now alter any implicit rules' variables if you like, e.g.:

    #

    #现在来改变任何你想改动的隐含规则中的变量,例如

    

   CFLAGS := -g -Wall -O3 -m486

   CXXFLAGS := $(CFLAGS)

    

    #The next bit checks to see whether rm is in your djgpp bin

    #directory; if not it uses del instead, but this can cause (harmless)

    #`File not found' error messages. If you are not using DOS at all,

    #set the variable to something which will unquestioningly remove

    #files.

    #

    #下面先检查你的 djgpp 命令目录下有没有 rm 命令,如果没有,我们使用

    #del 命令来代替,但有可能给我们'File not found' 这个错误信息,这没

    #什么大碍。如果你不是用DOS ,把它设定成一个删文件而不废话的命令。

    #(其实这一步在 UNIX 类的系统上是多余的,只是方便 DOS 用户。 UNIX

    #用户可以删除这5行命令。)

    

   ifneq ($(wildcard $(DJDIR)/bin/rm.exe),)

   RM-F := rm -f

   else

   RM-F := del

   endif

    

    #You shouldn't need to change anything below this point.

    #

    #从这里开始,你应该不需要改动任何东西。(我是不太相信,太NB了!)

    

   SOURCE := $(wildcard *.c) $(wildcard *.cc)

   OBJS := $(patsubst %.c,%.o,$(patsubst %.cc,%.o,$(SOURCE)))

   DEPS := $(patsubst %.o,%.d,$(OBJS))

   MISSING_DEPS := $(filter-out $(wildcard $(DEPS)),$(DEPS))

   MISSING_DEPS_SOURCES := $(wildcard $(patsubst %.d,%.c,$(MISSING_DEPS)) \

   $(patsubst %.d,%.cc,$(MISSING_DEPS)))

   CPPFLAGS += -MD

    

   .PHONY : everything deps objs clean veryclean rebuild

    

   everything : $(EXECUTABLE)

    

   deps : $(DEPS)

    

   objs : $(OBJS)

    

   clean :

    @$(RM-F) *.o

    @$(RM-F) *.d

    

   veryclean: clean

    @$(RM-F) $(EXECUTABLE)

    

   rebuild: veryclean everything

    

   ifneq ($(MISSING_DEPS),)

   $(MISSING_DEPS) :

    @$(RM-F) $(patsubst %.d,%.o,$@)

   endif

    

   -include $(DEPS)

    

   $(EXECUTABLE) : $(OBJS)

    gcc -o $(EXECUTABLE) $(OBJS) $(addprefix -l,$(LIBS))

    

   === makefile 结束 ===

    

    有几个地方值得解释一下的。首先,我在定义大部分变量的时候使 用的是 := 而不是 = 符号。它的作用是立即把定义中参考到的函 数和变量都展开了。如果使用 = 的话,函数和变量参考会留在那 儿,就是说改变一个变量的值会导致其它变量的值也被改变。例 如:

    

    A= foo

    B = $(A)

    #现在 B 是 $(A) ,而 $(A) 是 'foo' 。

    A= bar

    #现在 B 仍然是 $(A) ,但它的值已随着变成 'bar' 了。

    B:= $(A)

    #现在 B 的值是 'bar' 。

    A= foo

    #B 的值仍然是 'bar' 。

    

   make 会忽略在 # 符号后面直到那一行结束的所有文字。

    

    ifneg...else...endif

       系统是 makefile 里让某一部分码有条件的 失效/有效的工具。 ifeq 使用两个参数,如果它们相同,它把直 到 else (或者 endif ,如果没有 else 的话)的一段码加进 makefile 里;如果不同,把 else 到 endif 间的一段码加入 makefile (如果有 else )。 ifneq 的用法刚好相反。

    'filter-out' 函数

       使用两个用空格分开的列表,它把第二列表中所 有的存在于第一列表中的项目删除。我用它来处理 DEPS 列表,把所 有已经存在的项目都删除,而只保留缺少的那些。

    我前面说过, CPPFLAGS 存有用于隐含规则中传给预处理器的一些 旗标。而 -MD 开关类似 -M 开关,但是从源码文件 .c 或 .cc 中形成的文件名是使用后缀 .d 的(这就解释了我形成 DEPS 变量的 步骤)。DEPS 里提到的文件后来用'-include' 加进了 makefile 里,它隐藏了所有因文件不存在而产生的错误信息。

    如果任何依靠文件不存在, makefile 会把相应的 .o 文件从磁碟 上删除,从而使得 make 重建它。因为CPPFLAGS 指定了 -MD , 它的 .d 文件也被重新产生。

      'addprefix'

        函数把第二个参数列表的每一项前缀上第一个参数值。

    这个 makefile 的那些目的是(这些目的可以传给 make 的命令行 来直接选用):

    everything:

(预设) 更新主要的可执行程序,并且为每一个源码文件生成或更新一个 '.d' 文件和一个 '.o' 文件。

   deps: 只是为每一个源码程序产生或更新一个 '.d' 文件。

   objs: 为每一个源码程序生成或更新'.d' 文件和目标文件。

   clean: 删除所有中介/依靠文件(*.d 和 *.o )。

   veryclean: 做 `clean' 和删除可执行文件。

   rebuild: 先做 `veryclean'然后`everything' ;既完全重建。

    除了预设的 everything 以外,这里头只有 clean , veryclean , 和 rebuild 对用户是有意义的。

    我还没有发现当给出一个源码文件的目录,这个 makefile 会失败的 情况,除非依靠文件被弄乱。如果这种弄乱的情况发生了,只要输入 `make clean' ,所有的目标文件和依靠文件会被删除,问题就应该被解决了。当然,最好不要把它们弄乱。如果你发现在某种情况下这 个 makefile 文件不能完成它的工作,请告诉我,我会把它整好的。

    

    总结

   ~~~~~~~~~~~~~~~

    

    我希望这篇文章足够详细的解释了多文件项目是怎么运作的,也说明了 怎样安全而合理的使用它。到此,你应该可以轻松的利用 GNU Make 工 具来管理小型的项目,如果你完全理解了后面几个部分的话,这些对于你来说应该没什么困难。

   GNU Make 是一件强大的工具,虽然它主要是用来建立程序,它还有很多 别的用处。如果想要知道更多有关这个工具的知识,它的句法,函数, 和许多别的特点,你应该参看它的参考文件 (info pages, 别的 GNU 工具也一样,看它们的 info pages. )。

    

    CScene 官方网站:http://cscene.differnet.org

    CScene 官方电邮:cscene@mindless.com

   This page is Copyright ? 1997 By C Scene. All Rights Reserved

 

 

 

 


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值