求务实重行解释意思这几行程序的意思,尤其是是什么命令

小木虫 --- 700万学术达人喜爱的学术科研平台
热门搜索:
&&【求助】请指点这几行语句的意思
【求助】请指点这几行语句的意思
r=secnds(0.)
r=ran(i1,i2)
这个随机数ran(i1,i2)表达什么意思,我编译的时候出现错误。
程序是别人论文后面的,不止一处使用这样的随机数形式。说明文档也看了,有这么一段话
result = RAN (i)
i (Input) Must be an INTEGER(4) variable or array element.
It should initially be set to a large, odd integer value. The RAN function stores a value in the argument that is later used to calculate the next random number.
ran(i1,i2)这样的表达我没见过,请大家帮忙,谢谢,
学术必备与600万学术达人在线互动!
扫描下载送金币命令行Makefile和make命令讲解教程
我们知道make是Linux下的一款程序自动维护工具,配合makefile的使用,就能够根据程序中模块的修改情况,自动判断应该对那些模块重新编译,从而保证软件是由最新的模块构成。本文分为上下两部分,我们将紧紧围绕make在软件开发中的应用展开详细的介绍。
一、都是源文件太多惹得祸
当我们在开发的程序中涉及众多源文件时,常常会引起一些问题。首先,如果程序只有两三个源文件,那么修改代码后直接重新编译全部源文件就行了,但是如果程序的源文件较多,这种简单的处理方式就有问题了。
设想一下,如果我们只修改了一个源文件,却要重新编译所有源文件,那么这显然是在浪费时间。其次,要是只重新编译那些受影响的文件的话,我们又该如何确定这些文件呢?比如我们使用了多个头文件,那么它们会被包含在各个源文件中,修改了某些头文件后,那些源文件受影响,哪些与此无关呢?如果采取拉网式大检查的话,可就费劲了。
由此可以看出,源文件多了可真是件让人头疼的事。幸运的是,实用程序make可以帮我们解决这两个问题――当程序的源文件改变后,它能保证所有受影响的文件都将重新编译,而不受影响的文件则不予编译,这真是太好了。
二、Make程序的命令行选项和参数
我们知道,make程序能够根据程序中各模块的修改情况,自动判断应对哪些模块重新编译,保证软件是由最新的模块构建的。至于检查哪些模块,以及如何构建软件由makefile文件来决定。
虽然make可以在makefile中进行配置,除此之外我们还可以利用make程序的命令行选项对它进行即时配置。Make命令参数的典型序列如下所示:
make [-f makefile文件名][选项][宏定义][目标]
这里用[]括起来的表示是可选的。命令行选项由破折号“C”指明,后面跟选项,如
如果需要多个选项,可以只使用一个破折号,如
也可以每个选项使用一个破折号,如
make Ck Cr
甚至混合使用也行,如
make Ce Ckr
Make命令本身的命令行选项较多,这里只介绍在开发程序时最为常用的三个,它们是:
Ck:如果使用该选项,即使make程序遇到错误也会继续向下运行;如果没有该选项,在遇到第一个错误时make程序马上就会停止,那么后面的错误情况就不得而知了。我们可以利用这个选项来查出所有有编译问题的源文件。
Cn:该选项使make程序进入非执行模式,也就是说将原来应该执行的命令输出,而不是执行。
Cf :指定作为makefile的文件的名称。 如果不用该选项,那么make程序首先在当前目录查找名为makefile的文件,如果没有找到,它就会转而查找名为Makefile的文件。如果您在Linux下使用GNU Make的话,它会首先查找GNUmakefile,之后再搜索makefile和Makefile。按照惯例,许多Linux程序员使用Makefile,因为这样能使Makefile出现在目录中所有以小写字母命名的文件的前面。所以,最好不要使用GNUmakefile这一名称,因为它只适用于make程序的GNU版本。
当我们想构建指定目标的时候,比如要生成某个可执行文件,那么就可以在make命令行中给出该目标的名称;如果命令行中没有给出目标的话,make命令会设法构建makefile中的第一个目标。我们可以利用这一特点,将all作为makefile中的第一个目标,然后将让目标作为all所依赖的目标,这样,当命令行中没有给出目标时,也能确保它会被构建。
三、Makefile概述
上面提到,make命令对于构建具有多个源文件的程序有很大的帮助。事实上,只有make命令还是不够的,前面说过还必用须makefile告诉它要做什么以及怎么做才行,对于程序开发而言,就是告诉make命令应用程序的组织情况。
我们现在对makefile的位置和数量简单说一下。一般情况下,makefile会跟项目的源文件放在同一个目录中。另外,系统中可以有多个makefile,一般说来一个项目使用一个makefile就可以了;如果项目很大的话,我们就可以考虑将它分成较小的部分,然后用不同的makefile来管理项目的不同部分。
make命令和Makefile配合使用,能给我们的项目管理带来极大的便利,除了用于管理源代码的编译之外,还用于建立手册页,同时还能将应用程序安装到指定的目录。
因为Makefile用于描述系统中模块之间的相互依赖关系,以及产生目标文件所要执行的命令,所以,一个makefile由依赖关系和规则两部分内容组成。下面分别加以解释。
依赖关系由一个目标和一组该目标所依赖的源文件组成。这里所说的目标就是将要创建或更新的文件,最常见的是可执行文件。规则用来说明怎样使用所依赖得文件来建立目标文件。
当make命令运行时,会读取makefile来确定要建立的目标文件或其他文件,然后对源文件的日期和时间进行比较,从而决定使用那些规则来创建目标文件。一般情况下,在建立起最终的目标文件之前,肯定免不了要建立一些中间性质的目标文件。这时,Make命令也是使用makefile来确定这些目标文件的创建顺序,以及用于它们的规则序列。
四、makefile中的依赖关系
make程序自动生成和维护通常是可执行模块或应用程序的目标,目标的状态取决于它所依赖的那些模块的状态。Make的思想是为每一块模块都设置一个时间标记,然后根据时间标记和依赖关系来决定哪一些文件需要更新。一旦依赖模块的状态改变了,make就会根据时间标记的新旧执行预先定义的一组命令来生成新的目标。
依赖关系规定了最终得到的应用程序跟生成它的各个源文件之间的关系。如下面的图1描述了可执行文件main对所有的源程序文件及其编译产生的目标文件之间的依赖关系,见下图:
图1& 模块间的依赖关系
就图1而言,我们可以说可执行程序main依赖于main.o、f1.o和ff1.o。与此同时,main.o依赖于main.c和def1.h;f1.o依赖于f1.c、def1.h和def2.h;而ff1.o则依赖于ff1.c、def2.h和def3. h。在makefile中,我们可以用目标名称,加冒号,后跟空格键或tab键,再加上由空格键或tab键分隔的一组用于生产目标模块的文件来描述模块之间的依赖关系。对于上例来说,可以作以下描述:
main: main.o f1.o f2.omain.o: main.c def1.hf1.o: f1.c def1.h def2.hf2.o: f2.c def2.h def3.h
不难发现,上面的各个源文件跟各模块之间的关系具有一个明显的层次结构,如果def2.h发生了变化,那么就需要更新f1.o和f2.o,而f1.o和f2.o发生了变化的话,那么main也需要随之重新构建。
默认时,make程序只更新makefile中的第一个目标,如果希望更新多个目标文件的话,可以使用一个特殊的目标all,假如我们想在一个makefile中更新main和hello这两个程序文件的话,可以加入下列语句达到这个目的:
all: main hello
五、makefile中的规则
除了指明目标和模块之间的依赖关系之外,makefile还要规定相应的规则来描述如何生成目标,或者说使用哪些命令来根据依赖模块产生目标。就上例而言,当make程序发现需要重新构建f1.o的时候,该使用哪些命令来完成呢?很遗憾,到目前为止,虽然make知道哪些文件需要更新,但是却不知道如何进行更新,因为我们还没有告诉它相应的命令。
当然,我们可以使用命令gcc -c f1.c来完成,不过如果我们需要规定一个include目录,或者为将来的调试准备符号信息的话,该怎么办呢?所有这些,都需要在makefile中用相应规则显式地指出。
实际上,makefile是以相关行为基本单位的,相关行用来描述目标、模块及规则(即命令行)三者之间的关系。一个相关行格式通常为:冒号左边是目标(模块)名;冒号右边是目标所依赖的模块名;紧跟着的规则(即命令行)是由依赖模块产生目标所使用的命令。相关行的格式为:
目标:[依赖模块][;命令]
习惯上写成多行形式,如下所示:
目标:[依赖模块]
需要注意的是,如果相关行写成一行,“命令”之前用分号“;”隔开,如果分成多行书写的话,后续的行务必以tab字符为先导。对于makefile而言,空格字符和tab字符是不同的。所有规则所在的行必须以tab键开头,而不是空格键。初学者一定对此保持警惕,因为这是新手最容易疏忽的地方,因为几个空格键跟一个tab键在肉眼是看不出区别的,但make命令却能明察秋毫。
此外,如果在makefile文件中的行尾加上空格键的话,也会导致make命令运行失败。所以,大家一定要小心了,免得耽误许多时间。
六、Makefile文件举例
根据图1的依赖关系,这里给出了一个完整的makefile文件,这个例子很简单,由四个相关行组成,我们将其命名为mymakefile1。文件内容如下所示:
main: main.o f1.o f2.ogcc -o main main.o f1.o f2.omain.o: main.c def1.hgcc -c main.cf1.o: f1.c def1.h def2.hgcc -c f1.cf2.o: f2.c def2.h def3.hgcc -c f2.c
注意,由于我们这里没有使用缺省名makefile 或者Makefile ,所以一定要在make命令行中加上-f选项。如果在没有任何源码的目录下执行命令“make -f Mymakefile1”的话,将收到下面的消息:
make: *** No rule to make target ‘main.c’, needed by ‘main.o’. Stop.
Make命令将makefile中的第一个目标即main作为要构建的文件,所以它会寻找构建该文件所需要的其他模块,并判断出必须使用一个称为main.c的文件。因为迄今尚未建立该文件,而makefile又不知道如何建立它,所以只好报告错误。好了,现在建立这个源文件,为简单起见,我们让头文件为空,创建头文件的具体命令如下:
$ touch def1.h$ touch def2.h$ touch def3.h
我们将main函数放在main.c文件中,让它调用function2和function3,但将这两个函数的定义放在另外两个源文件中。由于这些源文件含有#include命令,所以它们肯定依赖于所包含的头文件。如下所示:
/* main.c */#include &STDLIDEF2.H&#include “def1.h”extern void function2();extern void function3();int main(){function2();function3();exit (EXIT_SUCCESS);}/* f1.c */#include “def1.h”#include “def2.h”void function2() {}/* f2.c */#include “def2.h”#include “def3.h”void function3()
建好源代码后,再次运行make程序,看看情况如何:
$ make -f Mymakefile1gcc -c main.cgcc -c f1.cgcc -c f2.cgcc -o main main.o f1.o f2.o$
好了,这次顺利通过了。这说明Make命令已经正确处理了makefile描述的依赖关系,并确定出了需要建立哪些文件,以及它们的建立顺序。虽然我们在makefile 中首先列出的是如何建立main,但是make还是能够正确的判断出这些文件的处理顺序,并按相应的顺序调用规则部分规定的相应命令来创建这些文件。当这些命令执行时,make程序会按照执行情况来显示这些命令。
如今,我们对def2.h加以变动,来看看makefile能否对此作出相应的回应:
$ touch def2.h
$ make -f Mymakefile1gcc -c f1.cgcc -c f2.cgcc -o main main.o f1.o f2.o$
这说明,当Make命令读取makefile 后,只对受def2.h的变化的影响的模块进行了必要的更新,注意它的更新顺序,它先编译了C程序,最后连接生产了可执行文件。现在,让我们来看看删除目标文件后会发生什么情况,先执行删除,命令如下:
然后运行make命令,如下所示:
$ make -f Mymakefile1gcc -c f1.cgcc -o main main.o f1.o f2.o$
很好,make的行为让我们非常满意。
七、makefile中的宏
在makefile中可以使用诸如XLIB、UIL等类似于Shell变量的标识符,这些标识符在makefile中称为“宏”,它可以代表一些文件名或选项。宏的作用类似于C语言中的define,利用它们来代表某些多处使用而又可能发生变化的内容,可以节省重复修改的工作,还可以避免遗漏。
Make的宏分为两类,一类是用户自己定义的宏,一类是系统内部定义的宏。用户定义的宏必须在makefile或命令行中明确定义,系统定义的宏不由用户定义。我们首先介绍第一种宏。
这里是一个包含宏的makefile文件,我们将其命名为mymakefile2,如下所示:
all: main# 使用的编译器CC = gcc#包含文件所在目录INCLUDE = .# 在开发过程中使用的选项CFLAGS = -g -Wall Cansi# 在发行时使用的选项# CFLAGS = -O -Wall Cansimain: main.o f1.o f2.o$(CC) -o main main.o f1.o f2.omain.o: main.c def1.h$(CC) -I$(INCLUDE) $(CFLAGS) -c main.cf1.o: f1.c def1.h def2.h$(CC) -I$(INCLUDE) $(CFLAGS) -c f1.cf2.o: f2.c def2.h def3.h$(CC) -I$(INCLUDE) $(CFLAGS) -c f2.c
我们看到,在这里有一些注释。在makefile中,注释以#为开头,至行尾结束。注释不仅可以帮助别人理解我们的makefile,如果时间久了,有些东西我们自己也会忘掉,它们对makefile的编写者来说也是很有必要的。
现在言归正传,先看一下宏的定义。我们既可以在make命令行中定义宏,也可以在makefile中定义宏。在makefile中定义宏的基本语法是:
宏标识符=值列表
其中,宏标识符即宏的名称通常全部大写,但它实际上可以由大、小写字母、阿拉伯数字和下划线构成。等号左右的空白符没有严格要求,因为它们最终将被make删除。至于值列表,既可以是零项,也可以是一项或者多项。如:
LIST_VALUE = one two three
当一个宏定义之后,我们就可以通过$(宏标识符)或者${宏标识符}来访问这个标识符所代表的值了。
在makefile中,宏经常用作编译器的选项。很多时候,处于开发阶段的应用程序在编译时是不用优化的,但是却需要调试信息;而正式版本的应用程序却正好相反,没有调试信息的代码不仅所占内存较小,进过优化的代码运行起来也更快。
对于Mymakefile1来说,它假定所用的编译器是gcc,不过在其他的UNIX系统上,更常用的编译器是cc或者c89,而非gcc。如果你想让自己的makefile适用于不同的UNIX操作系统,或者在一个系统上使用其他种类的编译器,这时就不得不对这个makefile中的多处进行修改。
但对于mymakefile2来说则不存在这个问题,我们只需修改一处,即宏定义的值就行了。除了在makefile中定义宏的值之外,我们还可以在make命令行中加以定义,如:
$ make CC=c89
当命令行中的宏定义跟makefile中的定义有冲突时,以命令行中的定义为准。当在makefile文件之外使用时,宏定义必须作为单个参数进行传递,所以要避免使用空格,但是更妥当的方法是使用引号,如:
$ make “CC =&& c89”
这样就不必担心空格所引起的问题了。现在让我们将前面的编译结果删掉,来测试一下mymakefile2的工作情况。命令如下所示:
$ rm *.o main
$ make -f Mymakefile2gcc -I. -g -Wall -ansi -c main.cgcc -I. -g -Wall -ansi -c f1.cgcc -I. -g -Wall -ansi -c f2.cgcc -o main main.o f1.o f2.o$
就像我们看到的那样,Make程序会用相应的定义来替换宏引用$(CC )、$(CFLAGS )和$(INCLUDE),这跟C语言中的宏的用法比较相似。
上面介绍了用户定义的宏,现在介绍make的内部宏。常用的内部宏有:
$? :比目标的修改时间更晚的那些依赖模块表。$@ :当前目标的全路径名。可用于用户定义的目标名的相关行中。$& :比给定的目标文件时间标记更新的依赖文件名。$* :去掉后缀的当前目标名。例如,若当前目标是pro.o,则$*表示pro。
我们在本文中分别介绍了make程序的使用方法,makefile中的依赖关系及规则等基础知识,同时还介绍了一些常用的宏。在下篇文章中,我们会对makefile的高级功能做进一步的介绍。
阅读本文后您有什么感想? 已有
人给出评价!
04-10-1504-10-1504-10-1504-10-1504-10-1504-10-1504-10-0804-10-08
注:您的评论需要经过审核才会显示出来
Copyright &
PC6下载().All Rights Reserved
备案编号:湘ICP备号命令行参数是什么意思?
命令行参数是什么意思?
09-07-16 &
这个名词我们可以分开来看:“命令行&就是:DOC命令行&了,也就是那个黑色的框框。&参数就&是程序里面所需要的&参数&了。合起来&命令行参数”就是“在DOC下输入的程序所需的参数”。
请登录后再发表评论!命令行是什么意思?_百度知道
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。
命令行是什么意思?
我有更好的答案
在Win9x&#47。 他的作用,NT,2000等,都是可视化的界面;command&命令.等等.;Me的开始菜单中的运行程序中键入&quot。在这些系统之前的人们使用的操作系统是DOS系统。大部分的DOS命令都已经在Windows里变成了可视化的界面,但是有一些高级的DOS命令还是要在DOS环境下来执行... 很多很多。DOS系统目前已经没有什么人使用了;命令,可进入命令行界面,但是dos命令却依然存在于我们使用的windows系统之中,可进入命令行界面。 在Win2000/NT的开始菜单中的运行程序中键入&cmd&quot。所以学习命令行对于我们熟练操作Windows系统是很有必要的。 不同的操作系统要用不同的命令进入命令行界面,比如装系统必须在DOS下进行.还有如格式化命令行是在DOS下运行的命令. 在这里,先解释什么是DOS? DOS——Disk Operation System 磁盘操作系统 目前我们常用的操作系统有windows 9x/Me
采纳率:31%
运行。。CMD
回车然后出来CMD 命令窗口你输入的就是命令了
其实就是代码啦!!
为您推荐:
您可能关注的内容
换一换
回答问题,赢新手礼包标签:至少1个,最多5个
GNU Autotools 不黑
我见过很多人抱怨 GNU Autotools 太复杂。事实上它比求解一元二次方程简单多了。学习 GNU Autotools,最好的办法就是动手搭建一个项目,然后根据自己的需求去查 GNU Autotools 的文档与教程,整个过程只是很简单的『加法』运算。
也有人抱怨 GNU Autotools 在 Windows 下不好用。这是事实,不过 GNU Autotools 只是为了类 Unix 系统设计的,也可以说 GNU Autotools 是为了 GNU 这个生态系统设计。错误的使用 GNU Autotools,要是还好用,那就太奇怪了。
如果你确定 GNU Autotools 是你所需要的,那么我毫不吝惜的告诉你,我见过的最好的 GNU Autotools 入门教程在 ,这是一个演示文档风格的教程。如果你稍微有点你耐心,注意力再集中一些,半天功夫应该能够掌握 GNU Autotools 全部知识的七成了,而剩下那三成一般也很少用到。
我这篇文章主要讲一下,如何借助 C 语言、GNU Autotools 以及 GLib 库构建一个 GNU 风格的国际化的命令行程序。由于这个程序是我的一个小项目需要的,所以我有耐心指导我自己如何完成这件事。也就是说,在我开始写这份文档时,我刚开始认真的学习 GNU Autotools 以及 GLib 库的部分功能。不过,这也意味着这篇文档可能会很长。
还需要叮嘱一下,这篇文档是我写给我自己看的,即使你看到有些怪异之处,只当做情节纯属虚构,看不下去也无需自寻烦恼。
我要构建的这个程序名曰 moon,它属于 zero 项目。不要问为什么,因为它就应该叫 moon。于是我创建了 zero/src 目录与 moon.c 文件:
$ mkdir -p zero/src
$ echo "int main(void) {
}" & src/moon.c
也不要问我为什么是在 zero 的 src 目录中创建了 moon.c 文件,因为即使我告诉你这个项目的名字叫 zero,并且
zero 取『道』之意,而 moon 取『阴』之意,你会觉得我在开玩笑,可是我没有。
造月亮的原料
现在,我执行以下命令:
$ echo "AC_INIT(zero, 0.1, )
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_MACRO_DIR([m4])
AM_INIT_AUTOMAKE([foreign -Wall])
AC_PROG_CC
PKG_CHECK_MODULES(WHEEL, [glib-2.0])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_FILES([Makefile src/Makefile])
AC_OUTPUT" & configure.ac
$ mkdir m4
上述命令所完成的任务就是在 zero 目录中创建了 configure.ac 文件,然后将一组以 AC_, AM_ 以及 PKG_ 为前缀的语句写入该文件。
configure.ac 文件其实是 autoconf 的配置文件,它指导着 autoconf 如何生成 configure 脚本。如果不清楚 autoconf 是如何工作的,可阅读『』。
这些以 AC_, AM_ 以及 PKG_ 为前缀的语句都是 m4 宏的调用。如果你不知道 m4 是什么,可阅读『』。
GNU Autotools 是一个工具集,其中比较重要的工具有 autoconf, aclocal, automake, libtool,此外还有一些辅助工具,例如 autoscan, autoheader 之类。还有一个工具 pkg-config ,虽然它不属于 GNU Autotools,但也是非常重要。这些工具提供了一些可在 configure.ac 文件中调用的 m4 宏。例如,以 AC_ 为前缀的宏都是 autoconf 提供的,以 AM_ 为前缀的宏是 automake 提供的,以 PKG_ 为前缀的宏是 pkg-config 提供的。所以,要想弄明白这些宏的含义,就使用 info 去查各个工具的手册。例如,要弄清楚 AC_CONFIG_AUX_DIR,就需要 info autoconf。如果不懂 info 命令的用法,那么你应该 info info。
既然在 AC_CONFIG_FILES 宏参数中设定将来要通过 configure 脚本生成 Makefile 与 src/Makefile 文件,那么就必须提供相应的 Makefile.am 与 src/Makefile.am 文件:
$ echo "ACLOCAL_AMFLAGS = -I m4
SUBDIRS = src" & Makefile.am
$ echo "bin_PROGRAMS = moon
moon_CFLAGS
= \$(WHEEL_CFLAGS) -std=c99
moon_LDADD
= \$(WHEEL_LIBS)
moon_SOURCES = moon.c" & src/Makefile
这样,最基本的 zero 项目就搭建了起来,现在可以造个黯淡无光的月亮了:
$ autoreconf -i
$ mkdir build
$ cd build
$ ../configure
$ src/moon
# 不会输出任何东西,因为它是个空壳子
也可以将 moon 安装到系统中:
$ sudo make install
如果后悔了,还可以卸载:
$ sudo make uninstall
命令行选项解析
现在,我希望 moon 程序能够支持下面这种调用形式:
$ src/moon --entrance="代码的提取入口" --output=foo.c foo.zero
$ src/moon -e "代码的提取入口" -o foo.c foo.zero
要在 C 程序中解决这个问题,直接调用 GLib 库中的命令行选项解析器是最轻省的办法。我将前文中那个什么也没做的 src/moon.c 修改为:
#include &locale.h&
#include &glib.h&
static gchar *zero_entrance = "*";
static gchar *zero_output
= "zero-output.c";
static GOptionEntry moon_entries[] = {
{"entrance", 'e', 0, G_OPTION_ARG_STRING, &zero_entrance,
"Set &chunk& as the entrance for extracting code.", "&chunk&"},
{"output", 'o', 0, G_OPTION_ARG_STRING, &zero_output,
"place output into &file&.", "&file&"},
int main(int argc, char **argv) {
setlocale(LC_ALL, "");
GOptionContext *context = g_option_context_new("zero-file");
g_option_context_add_main_entries(context, moon_entries, NULL);
if (!g_option_context_parse(context, &argc, &argv, NULL)) {
g_error("Commandline option parser failed!");
if (argv[1] == NULL) g_error ("You should give me zero file!");
g_print("%s\n", zero_entrance);
g_print("%s\n", zero_output);
g_print("%s\n", argv[1]);
然后重新编译这个项目,然后运行新的 moon:
$ src/moon --entrance="代码的提取入口" --output=foo.c foo.zero
代码的提取入口
$ src/moon -e "代码的提取入口" -o foo.c foo.zero
代码的提取入口
$ src/moon -h
moon [OPTION...] zero-file
Help Options:
-h, --help
Show help options
Application Options:
-e, --entrance=&chunk&
Set &chunk& as the entrance for extracting code.
-o, --output=&file&
place output into &file&.
$ src/moon
** (moon:21159): ERROR **: You should give me zero file!
fish: Job 2, “src/moon” terminated by signal SIGTRAP (Trace or breakpoint trap)
GLib 库的命令行解析器是如何做到这一切的呢?
首先,我定义了两个全局变量:
static gchar *zero_entrance = "*";
static gchar *zero_output
= "zero-output.c";
然后用 GLib 库提供的 GOptionEntry 结构将这两个全局变量与一个命令行选项数组 moon_entries 中的 2 个元素关联起来:
static GOptionEntry moon_entries[] = {
{"entrance", 'e', 0, G_OPTION_ARG_STRING, &zero_entrance,
"Set &chunk& as the entrance for extracting code.", "&chunk&"},
{"output", 'o', 0, G_OPTION_ARG_STRING, &zero_output,
"place output into &file&.", "&file&"},
至于 GOptionEntry 各个成员的含义,请自行查阅 GLib 手册的『Commandline option parser』部分。
接下来,在 main 函数中,使用 g_option_context_new 创建命令行选项环境 context,并顺便设定这个程序所接受的参数信息为 zero-file。这个参数与 moon_entries 中定义的命令行选项无关,它是程序的参数,不是程序的选项的参数。
GOptionContext *context = g_option_context_new("zero-file");
正是因为我设定了 moon 的参数为 zero-file,所以在执行 moon -h 时会得到以下信息:
$ src/moon -h
moon [OPTION...] zero-file
... ... ...
接下来,就是将 moon_entries 数组添加到命令行选项环境 context 中:
g_option_context_add_main_entries(context, moon_entries, NULL);
然后就可以对命令行进行解析了,即:
if (!g_option_context_parse(context, &argc, &argv, NULL)) {
g_error("Commandline option parser failed!");
如果解析失败,就报错。
g_option_context_parse 函数首先从 argv 中截取符合命令行选项数组成员相符的文本,然后进行解析,将所得参数值赋予相应的变量。在本文的示例中,若我像下面这样执行 moon 命令:
src/moon --entrance="代码的提取入口" --output=foo.c foo.zero
那么 main 函数的 argv 的内容一开始是:
argv[0]: src/moon
argv[1]: --entrance="代码的提取入口"
argv[2]: --output=foo.c
argv[3]: foo.zero
g_option_context_parse 函数会截取 argv[1] 与 argv[2] 进行解析,将所得的值分别赋给 zero_entrance 与 zero_output。它这样一捣乱,argv 的内容就变成了:
argv[0]: src/moon
argv[1]: foo.zero
如果你理解了上述过程,那么下面代码的含义就无需多做解释了。
if (argv[1] == NULL) g_error ("You should give me zero file!");
g_print("%s\n", zero_entrance);
g_print("%s\n", zero_output);
g_print("%s\n", argv[1]);
真正还需要解释的是
#include &locale.h&
setlocale(LC_ALL, "");
如果 src/moon.c 没有这两行代码,那么 g_print 可能就没法正确的显示中文。setlocale(LC_ALL, "") 的意思是对系统 Locale 不作任何假设,这样 moon 程序的 Locale 就会因系统中的 Locale 环境变量的值而异。
我的系统中的 Locale 环境变量的值如下:
LANG=en_US.UTF-8
LC_CTYPE=en_US.UTF-8
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
虽然我的 Locale 环境变量的值都是 en_US.UTF-8,但是它所容纳的字符编码与 zh_CN.UTF-8 是一样的,所以我的系统能够正确的显示中文字符。
为什么我不将系统的 Locale 变量都设成 zh_CN.UTF-8 呢?因为这样做,会让很多支持国际化的程序的输出结果中出现从英文翻译过来的中文信息,而这些中文信息所表达的内容往往不如英文原文准确。
为所有的人制造一个月亮
现在,moon 能够支持命令行了,但是 moon -h 显示的帮助信息都是英文的。虽然我知道,中国同胞们并没有很多人喜欢命令行程序——他们看到命令行,本能的就希望能有个图形窗口界面版本。不过,也许……可能……大概会有人需要 moon -h 在中文环境中能输出中文的帮助信息,同时,也大概会有人需要 moon -h 在日文环境中输出日文的帮助信息。此类需求,不一而足。
为每种语言开发一个专门的版本,以前微软喜欢干这种事……有钱,任性!更节约能源的做法是开发一个软件,让它有能力支持各种语言。能实现这一目的方法有很多,但是借助 GNU gettext 工具会让这件事轻松很多,几乎是零成本的就能构造出一个国际化的 moon。下面说说我的做法。
首先,将 zero/configure.ac 修改为:
AC_INIT(zero, 0.1, )
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_MACRO_DIR([m4])
AM_INIT_AUTOMAKE([foreign -Wall])
AM_GNU_GETTEXT_VERSION([0.19.7])
AM_GNU_GETTEXT([external])
AC_PROG_CC
PKG_CHECK_MODULES(WHEEL, [glib-2.0])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_FILES([Makefile src/Makefile])
也就是在原来的基础上增加了:
AM_GNU_GETTEXT_VERSION([0.19.7])
AM_GNU_GETTEXT([external])
不要问我为什么要这么做,因为我也不清楚,但是如果我想搞清楚,我自然会去 info Automake 文档,或者去看 。
接下来,在 zero 目录中执行 gettextize 命令:
$ gettextize --copy --no-changelog
gettextize 可以将一些有助于你的软件包支持国际化的文件复制到当前目录(zero 目录),然后它提示你应将 /usr/share/gettext/gettext.h 文件复制到项目源码目录。你需要敲一下回车键,告诉 gettextize 你知道了。
gettextize 还会自动的将 configure.ac 文件中的
AC_CONFIG_FILES([Makefile src/Makefile])
AC_CONFIG_FILES([Makefile src/Makefile po/Makefile.in])
这意味着,以后在执行 configure 脚本时,会自动在 po 目录中生成一份 Makefile。这份 Makefile 文件能够自动的帮我们完成一些与国际化有关的繁琐的任务。
接下来,就复制 gettext.h 到 zero/src 目录:
$ cp /usr/share/gettext/gettext.h src
然后将 zero 目录中的 Makefile.am 修改为:
ACLOCAL_AMFLAGS = -I m4
SUBDIRS = po src
也就是告诉未来的 Makefile,与 zero 项目有关的国际化文件都在 po 目录内。
再将 src/Makefile.am 文件修改为:
AM_CPPFLAGS = -DLOCALEDIR=\"$(localedir)\"
bin_PROGRAMS = moon
moon_CFLAGS
= $(WHEEL_CFLAGS) -std=c99
moon_LDADD
= $(WHEEL_LIBS)
moon_SOURCES = moon.c
LDADD = $(LIBINTL)
所做的修改就是告诉未来的 Makefile,应该将一些与 moon 相关的国际化文件安装到系统中的哪个位置,并且将哪些与国际化有关的库连接到 moon 程序中。
接下来,进入 zero/po 目录从基于 Makevars.template 建立 Makevars 文件:
$ cp Makevars.template Makevars
$ sed -i "s/COPYRIGHT_HOLDER = Free Software Foundation, Inc/COPYRIGHT_HOLDER = Garfileo/g" Makevars
$ sed -i "s/MSGID_BUGS_ADDRESS =/MSGID_BUGS_ADDRESS = \$(PACKAGE_BUGREPORT)/g" Makevars
两行 sed 命令是我故弄玄虚,实际上我不想啰哩啰嗦的说,你用你最喜欢的文本编辑器打开 Makevars 文件,然后将其中的
COPYRIGHT_HOLDER = Free Software Foundation, Inc
MSGID_BUGS_ADDRESS =
COPYRIGHT_HOLDER = Garfileo.
MSGID_BUGS_ADDRESS = $(PACKAGE_BUGREPORT)
COPYRIGHT_HOLDER 表示这个项目的责任者,我叫 Garfileo,所以我这么设置。至于将 MSGID_BUGS_ADDRESS 的值设置为 $(PACKAGE_BUGREPORT),这有点莫名其妙,因为我们从未定义过 PACKAGE_BUGREPORT 变量。我告诉你,整个 Autotools 工具链,一直在没停下来玩悄悄的给你生成一大堆环境变量的把戏,你从了它就好了。PACKAGE_BUGREPORT 的值实际上就是 configure.ac 中的 AC_INIT 宏的第三个参数……趁机回顾一下 configure.ac 中的 AC_INIT 宏吧。
再接下来,你应该将项目中凡是含有需要进行国际化的文本的 C 文件写入到 POTFILES.in 中。对于 zero 项目,只需:
$ echo "src/moon.c" && POTFILES.in
现在开始对 moon.c 中的某些文本进行国际化处理。将 src/moon.c 修改为:
#include &config.h&
#include &locale.h&
#include &glib.h&
#include "gettext.h"
#define _(string) gettext(string)
static gchar *zero_entrance = "*";
static gchar *zero_output
= "zero-output.c";
static GOptionEntry moon_entries[] = {
{"entrance", 'e', 0, G_OPTION_ARG_STRING, &zero_entrance,
_("Set &chunk& as the entrance for extracting code."), "&chunk&"},
{"output", 'o', 0, G_OPTION_ARG_STRING, &zero_output,
_("place output into &file&."), "&file&"},
int main(int argc, char **argv) {
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
GOptionContext *context = g_option_context_new("zero-file");
g_option_context_add_main_entries(context, moon_entries, NULL);
if (!g_option_context_parse(context, &argc, &argv, NULL)) {
g_error("Commandline option parser failed!");
if (argv[1] == NULL) g_error (_("You should give me zero file!"));
g_print("%s\n", zero_entrance);
g_print("%s\n", zero_output);
g_print("%s\n", argv[1]);
比未进行国际化时的 src/moon.c 相比,新的 src/moon.c 增加了以下几行代码:
#include &config.h&
#include "gettext.h"
#define _(string) gettext(string)
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
config.h 是 configure 脚本自动生成的,里面定义了一些 C 语言宏。例如,前面谈到的 PACKAGE_BUGREPORT 就定义于其中。
gettext.h 是刚才我从 /usr/share/gettext 目录中复制过来的。
_ 是一个宏,它的参数是 C 字符串。在国际化的 src/moon.c 中,我用这个宏将三个字符串给国际化了,即:
_("Set &chunk& as the entrance for extracting code.")
_("place output into &file&.")
_("You should give me zero file!")
这个宏是个翻译家,它可以将英文文本翻译为另外一种特定语言的文本。
bindtextdomain 函数,可以将系统中的一个存放着国际化文件的目录 LOCALEDIR 与当前要创建的软件包 PACKAGE 关联起来。也就是说,一旦 PACKAGE 被创建了出来,它在运行时,会使用 textdomain 函数从 LOCALEDIR 目录取出国际化文件的内容来用。LOCALEDIR 与 PACKAGE 的值均定义于 config.h 文件中。
现在,理论上 moon 已经支持国际化了,编译一下看看。由于刚才修改了 Autoconf 与 Automake 的配置文件,所以需要在 zero 目录内重新配置一下环境,然后再编译 moon,即:
$ autoreconf -i
$ cd build
$ ../configure
In file included from ../../src/gettext.h:25:0,
from ../../src/moon.c:4:
../../src/moon.c:5:19: error: initializer element is not constant
#define _(string) gettext(string)
结果在编译 moon 时出错了,编译器提示:在
src/moon.c 文件中出现了多处『初始化元素不是常量』的错误。这事怪不得 GNU gettext,而是语法错误。要理解这些错误,不妨编译一下这份 C 代码:
#include &stdio.h&
char *foo = "foo";
char *bar = "bar";
char * foo(void) {
char * bar(void) {
int main(void) {
char *messages[] = {foo(), bar()};
错误的根源在于:C 语言无法通过函数调用的方式对字符串数组进行初始化。这是因为字符串数组的值是在编译期间确定的,而函数的调用却发生在程序运行时。
这事看上去无解了。因为我们要用 GLib 库的命令行选项解析器,必须得初始化一个含字符串的数组,但是 C 编译器坚持说你用 _(string) 宏就是不行。
既然是 GLib 自己惹的事,还是让 GLib 来解决吧。请在 src/moon.c 文件中增加一个头文件 gi18n.h,然后将那些无法通过编译的 _(string) 宏都换成 N_(string),即:
#include &config.h&
#include &locale.h&
#include &glib.h&
#include &glib/gi18n.h&
#include "gettext.h"
#define _(string) gettext(string)
static gchar *zero_entrance = "*";
static gchar *zero_output
= "zero-output.c";
static GOptionEntry moon_entries[] = {
{"entrance", 'e', 0, G_OPTION_ARG_STRING, &zero_entrance,
N_("Set &chunk& as the entrance for extracting code."), "&chunk&"},
{"output", 'o', 0, G_OPTION_ARG_STRING, &zero_output,
N_("place output into &file&."), "&file&"},
int main(int argc, char **argv) {
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
GOptionContext *context = g_option_context_new("zero-file");
g_option_context_add_main_entries(context, moon_entries, NULL);
if (!g_option_context_parse(context, &argc, &argv, NULL)) {
g_error("Commandline option parser failed!");
if (argv[1] == NULL) g_error (_("You should give me zero file!"));
g_print("%s\n", zero_entrance);
g_print("%s\n", zero_output);
g_print("%s\n", argv[1]);
这样就可以消除全部的编译错误。
含字符串的数组并没有变,为什么 N_(string) 就神通广大的让代码通过了编译?因为 GLib 定义的 N_(string) 宏是这样的:
#define N_(strging) (string)
也就是说,N_(string) 只是一个标记,这个标记只是告诉 GNU gettext 的某个负责扫描 po/POTFILES.in 中所记录的 C 文件的那个工具,它包含了一个要进行国际化的文本,但是不进行翻译。具体的翻译过程是在 GLib 命令行解析器内部进行的。
不过,虽然 _(string) 引起的编译错误消除了,但是编译器给出了一个警告:
./../src/moon.c:6:0: warning: "_" redefined
#define _(string) gettext(string)
In file included from ../../src/moon.c:4:0:
/usr/include/glib-2.0/glib/gi18n.h:26:0: note: this is the location of the previous definition
_(String) gettext (String)
警告信息提示,_(string) 宏被重复定义了,在 glib/gi18n.h 中已经定义了这个宏。既然如此,那就从 src/moon.c 中删除 _(string) 的定义语句。最终的 moon.c
当 make 过程无错误亦无警告时,这就意味着我们的 moon 已经普照世界了,最终的 src/moon.c 内容如下:
#include &config.h&
#include &locale.h&
#include &glib.h&
#include &glib/gi18n.h&
#include "gettext.h"
static gchar *zero_entrance = "*";
static gchar *zero_output
= "zero-output.c";
static GOptionEntry moon_entries[] = {
{"entrance", 'e', 0, G_OPTION_ARG_STRING, &zero_entrance,
N_("Set &chunk& as the entrance for extracting code."), "&chunk&"},
{"output", 'o', 0, G_OPTION_ARG_STRING, &zero_output,
N_("place output into &file&."), "&file&"},
int main(int argc, char **argv) {
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
GOptionContext *context = g_option_context_new(_("zero-file"));
g_option_context_add_main_entries(context, moon_entries, NULL);
if (!g_option_context_parse(context, &argc, &argv, NULL)) {
g_error("Commandline option parser failed!");
if (argv[1] == NULL) g_error (_("You should give me zero file!"));
g_print("%s\n", zero_entrance);
g_print("%s\n", zero_output);
g_print("%s\n", argv[1]);
中国的月亮
全世界人看到的是同一个月亮,但是各个国家、各个民族甚至个别的人对这个月亮的称谓却有不同。每一种称谓又都是在某种文化中出现的,既然同样一个月亮出现了不同的称谓,那么世界上那么多的文化是不是也是描述的同一事物呢?
老子说,是这样的……无名,万物之母也;有名,万物之始也。故恒无欲也,以观其妙;恒有欲也,以观其所徼。此两者同出而异名,同谓之玄。玄之又玄,众妙之门。
中国的月亮与外国的月亮,没有什么区别,只有文字与故事上的差异,而这些差异皆是人为,与月亮无关。moon 程序的国际化也是这样,moon 的用户们看到的都是同一个 moon 程序,但是他们是通过 moon 帮助信息的不同的语言版本来理解 moon 的。他们对 moon 的所有理解只不过是表象,moon 本身才是玄妙的东西,而这种玄妙的东西是我创造出来的……虽然我如此厉害,也只不过是个门而已,那些通过 moon 的帮助信息而学会了 moon 的人,他们可能比我更会用 moon。也就是说,我比他们更懂 moon,但是并不意味这我比他们更会用 moon。只有懂玄之又玄,众妙之门的那个人更厉害一些,但是他依然是个门。
为了能够让更多的中国人比我更会用 moon,所以我需要为他们制作中文版本的 moon 帮助信息。用术语描述这个过程,就是 moon 程序的本地化。
要做 moon 程序的本地化,需要先进入 zero/po 目录,你会看到 zero.pot 这个文件。这个文件是在 zero 项目的 make 过程中由 xgettext 工具扫描 src/moon.c 文件自动生成。至于 xgettext 为什么会自动扫描 src/moon.c 而不是其他文件,并非是因为 zero 项目中只有 src/moon.c 这么一个 C 文件,而是因为在 moon 国际化阶段,我们向 po/POTFILES.in 中写入了 src/moon.c,而 xgettext 正是根据 po/POTFILES.in 文件中制定的文件进行扫描的。
xgettext 工具会从 src/moon.c 中扫描什么?它会扫描那些形如 N_(string) 与 _(string) 之类的文本,并认定这些文本都是国际化文本,然后它会将这些文本以及它们在 src/moon.c 中的位置等信息写入 po/zero.pot 文件。现在,打开 po/zero.pot 文件,可以看到以下内容:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR Garfileo.
# This file is distributed under the same license as the zero package.
# FIRST AUTHOR &EMAIL@ADDRESS&, YEAR.
"Project-Id-Version: zero 0.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date:
16:20+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME &EMAIL@ADDRESS&\n"
"Language-Team: LANGUAGE &LL@li.org&\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/ charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
#: src/moon.c:12
msgid "Set &chunk& as the entrance for extracting code."
#: src/moon.c:14
msgid "place output into &file&."
#: src/moon.c:20
msgid "zero-file"
#: src/moon.c:25
msgid "You should give me zero file!"
对 moon 程序进行本地化,实际上就是基于 po/zero.pot 产生一份 po/zh_CN.po 文件,即:
$ msginit -l zh_CN
msginit 程序所做的工作主要是将
zero.pot 复制为 zh_CN.po,并对 zh_CN.po 的文件首部信息进行初始化——例如,msginit 在运行时会停下来,让我告诉它我的 Email 地址。我告诉它
之后,它才开始继续工作。
接下来就是用文本编辑器修改 po/zh_CN.po 文件,主要工作就是设定字符编码,然后将文件中非空的 msgid 对应的字符串翻译成中文字符串。我将 po/zh_CN.po 修改为:
# Chinese translations for zero package.
# Copyright (C) 2016 Garfileo.
# This file is distributed under the same license as the zero package.
"Project-Id-Version: zero 0.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date:
17:24+0800\n"
"PO-Revision-Date:
17:26+0800\n"
"Last-Translator:
"Language-Team: Chinese (simplified)\n"
"Language: zh_CN\n"
"MIME-Version: 1.0\n"
"Content-Type: text/ charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: src/moon.c:12
msgid "Set &chunk& as the entrance for extracting code."
msgstr "将指定的代码块设为代码的提取入口"
#: src/moon.c:14
msgid "place output into &file&."
msgstr "将提取到的代码输出至指定的文件"
#: src/moon.c:23
msgid "zero-file"
msgstr "道文件"
#: src/moon.c:28
msgid "You should give me zero file!"
msgstr "你应当向我提供一份道文件"
使用 msgfmt 命令可以检查修改后的 zh_CN.po 是否合乎规范,即:
$ msgfmt --statistics --check zh_CN.po
4 translated messages.
请认真对比 po/zh_CN.po 与 po/zero.pot 文件,弄清楚我都改了那些内容。
在我确认 zh_CN.po 内容无误后,我就将中文本地化语种『注册』到 po/LINGUAS 文件,即:
$ echo zh_CN && LINGUAS
po/LINGUAS 是提供给 configure 脚本使用的。由 configure 脚本生成的 Makefile 会根据 LINGUAS 中的信息找到 zh_CN.po 文件,然后将它交给 msgfmt 工具处理成 zh_CN.gmo 文件。这份 zh_CN.gmo 文件就是 zh_CN.po 的二进制版本。在 make install 阶段,zh_CN.gmo 会被复制到 $PREFIX/share/locale/zh_CN/LC_MESSAGES 目录,并被改名为 zero.mo!这一切都拜 po/LINGUAS 所赐。
这样,moon 程序的本地化工作就完成了,剩下的事都交给 GNU Autotools 来做:
$ cd ../build
$ ../configure
$ make update-po
$ sudo make install
# moon 默认会被安装到 /usr/local/bin 目录
# zh_CN.gmo 会被复制为 /usr/local/share/locale/zh_CN/LC_MESSAGES/zero.mo 文件
$ which moon
/usr/local/bin/moon
$ ls /usr/local/share/locale/zh_CN/LC_MESSAGES
moon [OPTION...] zero-file
Help Options:
-h, --help
Show help options
Application Options:
-e, --entrance=&chunk&
Set &chunk& as the entrance for extracting code.
-o, --output=&file&
place output into &file&.
$ LANG=zh_CN.UTF-8 moon -h
moon [选项...] 道文件
帮助选项:
-h, --help
显示帮助选项
应用程序选项:
-e, --entrance=&chunk&
将指定的代码块设为代码的提取入口
-o, --output=&file&
将提取到的代码输出至指定的文件
这份文档写了 2 天,上述总结的知识已经用在了我的项目中。我之所以不厌其烦的把它们写出来,是因为四年前我曾经掌握了这些知识中的大部分,但是现在当我需要用它们的时候,我发现已经全忘记了。这样记下来,以后可能就不会那么容易忘记。
如果不通过 GNU Autotools + GNU gettext 来让你的程序支持国际化与本地化,而是徒手使用 GNU gettext,可参考:
3 收藏&&|&&13
你可能感兴趣的文章
2 收藏,125
2 收藏,406
本作品采用 署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可
GTK的项目大多用Autotools,如果是用Qt的话,还是用CMake比较方便。另外跨平台的项目也是用CMake比较合适。
GTK的项目大多用Autotools,如果是用Qt的话,还是用CMake比较方便。另外跨平台的项目也是用CMake比较合适。
分享到微博?
我要该,理由是:}

我要回帖

更多关于 计算机程序构造和解释 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信