一. gcc简单用法
# 1. 仅将原始码编译成为目标文件,并不制作连结等功能:
[guest@test guest]# gcc -c hello.c
# 会自动的产生 hello.o 这个文件,但是并不会产生 binary 执行档。
# 2. 在编译的时候,依据作业环境给予最佳化执行速度
[guest@test guest]# gcc -O hello.c -c
# 会自动的产生 hello.o 这个文件,并且进行最佳化喔!
# 3. 在进行 binary file 制作时,将连结的函式库与相关的路径填入
[guest@test guest]# gcc sin.c -lm -L/usr/lib -I/usr/include
# 这个指令较常下达在最终连结成 binary file 的时候,
# -lm 指的是 libm.so 或 libm.a 这个函式库文件;
# -L 后面接的路径是刚刚上面那个函式库的搜寻目录;
# -I 后面接的是原始码内的 include 文件之所在目录。
# 4. 将编译的结果输出成某个特定档名
[guest@test guest]# gcc -o hello hello.c
# -o 后片接的是要输出的 binary file 檔名
# 5. 在编译的时候,输出较多的讯息说明
[guest@test guest]# gcc -o hello hello.c -Wall
# 加入 -Wall 之后,程序的编译会变的较为严谨一点,
# 所以警告讯息也会显示出来!
一. make简单用法
1. 为什么要用make?
先来想象一个案例,假设我的执行档里面包含了四个原始码文件,分别是 main.c haha.c sin_value.c cos_value.c 这四个文件,由于这四个文件里面包含了相关性,并且还用到数学函式在里面,所以如果您想要让这个程序可以跑,那么就需要这样编译:
[guest@test guest]# gcc -c main.c
[guest@test guest]# gcc -c haha.c
[guest@test guest]# gcc -c sin_value.c
[guest@test guest]# gcc -c cos_value.c
# 先以上面的动作制作出四个目标文件,然后再进行下面的动作:
[guest@test guest]# gcc -o main main.o haha.o sin_value.o \
> cos_value.o -lm -L/usr/lib -L/lib
# 这样就可以制作出 main 这个执行档啰!执行看看吧!
[guest@test guest]# ./main
HaHa! I'm the King of the world
0.706825
0.707388
呵呵!要做好多动作啊!真是麻烦,如果可以的话,能不能一个步骤就给他完成上面所有的动作呢?试看看在这个目录下建立一个 makefile 文件,内容如下:
# 1. 先建立编译的规则
[guest@test guest]# vi makefile
main: main.o haha.o sin_value.o cos_value.o
gcc -o main main.o haha.o sin_value.o cos_value.o -lm
# 注意: gcc 之前是 <tab> 按键产生的空格!
# 2. 尝试建立规则
[guest@test guest]# rm -f main *.o <==先将之前的目标文件去除
[guest@test guest]# make
cc -c -o main.o main.c
cc -c -o haha.o haha.c
cc -c -o sin_value.o sin_value.c
cc -c -o cos_value.o cos_value.c
gcc -o main main.o haha.o sin_value.o cos_value.o -lm
# 这个时候 make 会主动去读取 makefile 这个文件的内容,
# 并根据内容直接去给他编译起相关的执行档啰!
# 3. 如果再执行一次 make 会怎样?!
[guest@test guest]# make
make: `main' is up to date.
# 看到了吧?!是否很方便呢?!
或许您会说:『如果我建立一个 shell 脚本来将上面的所有动作都集结在一起,不是具有同样的效果吗?』呵呵!效果当然不一样,以上面的测试为例,我们仅写出 main 需要的目标文件,结果 make 会主动的去判断每个目标文件相关的原始码文件,并直接予以编译,最后再直接进行连结的动作!哈哈!真的是很方便啊!此外,如果我们更动过某些原始码文件,则 make 也可以主动的判断哪一个原始码与相关的目标文件文件有更新过,并仅更新该文件,如此一来,将可大大的节省很多编译的时间呢!要知道,某些程序在进行编译的行为时,会消耗很多的 CPU 资源呢!所以说, make 有这些好处:
• 简化编译时所需要下达的指令;
• 若在编译完成之后,修改了某个原始码文件,则 make 仅会针对被修改了的文件进行编译,其它的 object file 不会被更动;
• 最后可以依照相依性来更新( update )执行档。
既然 make 有这么多的优点,那么我们当然就得好好的了解一下 make 这个令人关心的家伙啦!而 make 里面最需要注意的大概就是那个规则文件,也就是 makefile 这个文件的语法啦!底下我们针对 makefile 的语法来加以介绍啰。
2. make 的基本语法与变量
基本的 makefile 规则是这样的:
目标(target): 目标文件1 目标文件2
<tab> gcc -o 欲建立的执行文件 目标文件1 目标文件2
那个目标(target)就是我们想要建立的信息,而目标文件是具有相关性的,那建立执行文件的语法就是以 <tab> 按键开头的那一行!特别给他留意喔,『
命令列必须要以 tab 按键作为开头』才行!他的规则基本上是这样的:
• 在 makefile 当中的 # 代表注释;
• <tab> 要在命令行的第一个字符;
• 目标(target)与相依文件(就是目标文件)之间需以『:』隔开。
同样的,我们以刚刚上一个小节的范例进一步说明,如果我想要有两个以上的执行动作时,例如下达一个指令就直接清除掉所有的目标文件与执行文件,该如何制作呢?
# 1. 先建立编译的规则
[guest@test guest]# vi makefile
main: main.o haha.o sin_value.o cos_value.o
gcc -o main main.o haha.o sin_value.o cos_value.o -lm
clean:
rm -f main main.o haha.o sin_value.o cos_value.o
# 2. 测试看看:
[guest@test guest]# make clean
rm -f main main.o haha.o sin_value.o cos_value.o
如此一来,我们的 makefile 里面就具有至少两个标的,分别是 main 与 clean ,如果我们想要建立 main 的话,输入『make main』,如果想要清除有的没的,输入『make clean』即可啊!而如果想要先清除目标文件再编译 main 这个程序的话,就可以这样输入:『make clean main』,如下所示:
[guest@test guest]# make clean main
rm -f main main.o haha.o sin_value.o cos_value.o
cc -c -o main.o main.c
cc -c -o haha.o haha.c
cc -c -o sin_value.o sin_value.c
cc -c -o cos_value.o cos_value.c
gcc -o main main.o haha.o sin_value.o cos_value.o -lm
这样就很清楚了吧!?但是,您是否会觉得,咦! makefile 里面怎么重复的数据这么多啊!呵呵!没错!所以我们可以再藉由 shell script 那时学到的『变数』来更简化 makefile 喔:
[guest@test guest]# vi makefile
LIBS = -lm
OBJS = main.o haha.o sin_value.o cos_value.o
main: ${OBJS}
gcc -o main ${OBJS} ${LIBS}
clean:
rm -f main ${OBJS}
与 bash shell script 的语法有点不太相同,变量的基本语法为:
• 变量与变量内容以『=』隔开,同时两边可以具有空格;
• 变量左边不可以有 <tab> ,例如上面范例的第一行 LIBS 左边不可以是 <tab>;
• 变量与变量内容在『=』两边不能具有『:』;
• 在习惯上,变数最好是以『大写字母』为主;
• 运用变量时,以 ${变量} 或 $(变量) 使用;
• 在该 shell 的环境变量是可以被套用的,例如提到的 CFLAGS 这个变数!
• 在指令列模式也可以给予变量。
由于 gcc 在进行编译的行为时,会主动的去读取 CFLAGS 这个环境变量,所以,您可以直接在 shell 定义出这个环境变量,也可以在 makefile 文件里面去定义,更可以在指令列当中给予这个咚咚呢!例如:
[guest@test guest]# make clean main "CFLAGS=-Wall"
# 这个动作在上 make 进行编译时,会去取用 CFLAGS 的变量内容!
也可以这样:
[guest@test guest]# vi makefile
LIBS = -lm
OBJS = main.o haha.o sin_value.o cos_value.o
CFLAGS = -Wall
main: ${OBJS}
gcc -o main ${OBJS} ${LIBS}
clean:
rm -f main ${OBJS}
咦!我可以利用指令列进行环境变量的输入,也可以在文件内直接指定环境变量,那万一这个 CFLAGS 的内容在指令列与 makefile 里面并不相同时,以那个方式输入的为主?呵呵!环境变量取用的规则是这样的:
• make 指令列后面加上的环境变量为优先;
• makefile 里面指定的环境变量第二;
• shell 原本具有的环境变量第三。
此外,还有一些特殊的变量需要了解的喔:
• $@:代表目前的标的(target)
所以我也可以将 makefile 改成:
[guest@test guest]# vi makefile
LIBS = -lm
OBJS = main.o haha.o sin_value.o cos_value.o
CFLAGS = -Wall
main: ${OBJS}
gcc -o $@ ${OBJS} ${LIBS} <==那个 $@ 就是 main !
clean:
rm -f main ${OBJS}
这样是否稍微了解了 makefile ( 也可能是 Makefile ) 的基本语法?这对于您未来自行修改原始码的编译规则时,是很有帮助的喔! ^_^!
三. Tarball 安装的基本步骤
所谓 Tarball 文件,其实就是将套件的所有原始码文件先以 tar 打包,然后再以压缩技术来压缩,通常最常见的就是以 gzip 来压缩了。因为利用了 tar 与 gzip 的功能,所以 tarball 文件一般的附档名就会写成 *.tar.gz 或者是简写为 *.tgz !
1. Tarball 安装的基本步骤
以 Tarball 方式释出的套件是需要重新编译可执行的 binary file 的。而 Tarball 是以 tar 这个指令来打包与压缩的文件,所以啦,当然就需要先将 Tarball 解压缩,然后到原始码所在的目录下进行 makefile 的建立,再以 make 来进行编译与安装的动作啊!所以整个安装的基础动作大多是这样的:
(1) 将 tarball 文件在 /usr/local/src 目录下解压缩;
(2) 进入新建立的目录底下,去查阅 INSTALL 与 README 等相关文件内容(很重要的步骤!);
(3) 根据 INSTALL/README 的内容察看并安装好一些相依的套件( 非必要 );
(4) 以自动侦测程序( configure 或 config )检测工作环境,并建立 Makefile 这个文件;
(5) 以 make 这个程序并使用该目录下的 Makefile 做为他的参数设定档,来进行 make ( 编译或其它 )的动作;
(6) 以 make 这个程序和 Makefile 参数设置文件,根据 install 这个目标( target )的指定来安装到正确的路径!
当makefile 在制作出来之后,里头会有相当多的目标( target ),最常见的就是 install 与 clean 啰!通常『make clean』代表着将目标文件( object file )清除掉,『make』则是将原始码进行编译而已。注意喔!编译完成的可执行文件与相关的设定文件还在原始码所在的目录当中!因此,最后要进行『make install』来将编译完成的所有内容都给他安装到正确的路径去,这样就可以使用该套件啦!
下面简要说明一下大部分的 tarball 软件安装的命令执行方式:
./configure :
这个步骤就是在生成 Makefile 这的文件啰!通常程序开发者会写一个脚本来检查您的 Linux 系统及相关的套件属性等等,这个步骤相当的重要,因为未来您的安装信息都是这一步骤内完成的!另外,这个步骤的相关信息应该要参考一下该目录下的 README 或 INSTALL 相关的文件!!基本上,这个步骤完成之后会建立( 或修改 )一个 Makefile 。
make clean:
make 会读取 Makefile 中关于 clean 的工作。这个步骤不一定会有,但是最好执行一下!
make:
make 会依据 Makefile 当中的默认工作进行编译!编译的工作主要是进行 gcc 来将原始码编译成为可以被执行的 object files ,但是这些 object files 通常还需要一些函式库之类的 link 后,才能产生一个完整的执行档!使用make 就是要将原始码编译成为可以被执行的可执文件,而这个可执行文件会放置在目前所在的目录之下,尚未被安装到预定安装的目录中。
make install:
通常这就是最后的安装步骤了,make 会依据 Makefile 这个文件里面关于 install 的项目,将上一个步骤所编译完成的数据给他安装到预定的目录中,就完成安装啦!
2. Tarball 安装的注意事项
Tarball 为什么要在 /usr/local/src 里面解压缩?
通常,Linux 版本发布安装的套件大多是在 /usr 里面的,而使用者自行安装的套件则建议放置在 /usr/local里面。这是考虑到管理使用者所安装套件的便利性。怎么说呢?我们晓得几乎每个套件都会提供在线说明的服务,那就是 info 与 man 的功能。在预设的情况下, man 会去搜寻 /usr/local/man 里面的说明文件,因此,如果我们将套件安装在 /usr/local 底下的话,那么自然安装完成之后,该套件的说明文件就可以被找到了。此外,如果您所管理的主机其实是由多人共同管理的,或者是如同学校里面,一部主机是由学生管理的,但是学生总会毕业吧?所以需要进行交接,如果大家都将套件安装在 /usr/local 底下,那么管理上不就显的特别的容易吗?!所以啰,通常我们会建议大家将自己安装的套件放置在 /usr/local 下,至于原始码( Tarball )则建议放置在 /usr/local/src下面。
让我们先来看一看 Linux 版本默认的安装套件的路径会用到哪些?我们以 apache 这个软件来说明的:
/etc/httpd
/usr/lib
/usr/bin
/usr/share/man
我们会发现套件的内容大致上是摆在 etc, lib, bin, man 等目录当中,分别代表『设置文件、函式库、执行文件、在线帮助文件』。
那么你是以 tarball 来安装时呢?如果是放在默认的 /usr/local 里面,由于 /usr/local 原本就预设这几个目录了,所以你的数据就会被放在:
/usr/local/etc
/usr/local/bin
/usr/local/lib
/usr/local/man
但是如果你每个套件都选择在这个默认的路径下安装的话,那么所有的套件的文件都将放置在这四个目录当中,因此,如果你都安装在这个目录下的话,那么未来再想要升级或移除的时候,就会比较难以追查文件的来源啰!而如果您在安装的时候选择的是单独的目录,例如我将 apache 安装在 /usr/local/apache 当中,那么您的文件目录就会变成:
/usr/local/apache/etc
/usr/local/apache/bin
/usr/local/apache/lib
/usr/local/apache/man
单一套件的文件都在同一个目录之下,那么要移除该套件就简单的多了!只要将该目录移除即可视为该套件已经被移除啰!以上面为例,我想要移除 apache 只要下达『rm -rf /usr/local/apache』就算移除这个套件啦!当然啰,实际安装的时候还是得视该软件的 Makefile 里头的 install 信息才能知道到底他的安装情况为何的。因为例如 sendmail 的安装就很麻烦......这个方式虽然有利于套件的移除,但不晓得您有没有发现,我们在执行某些指令的时候,与该指令是否在 PATH 这个环境变量所记录的路径有关,以上面为例,我的 /usr/local/apache/bin 肯定是不在 PATH 里面的,所以执行 apache 的指令就得要利用绝对路径了,否则就得将这个 /usr/local/apache/bin 加入 PATH 里面。另外,那个 /usr/local/apache/man 也需要加入 man page 搜寻的路径当中啊!
除此之外, Tarball 在升级的时候也是挺困扰的,怎么说呢?我们还是以 apache 来说明好了。WWW 服务器为了考虑互动性,所以通常会将 PHP+MySQL+Apache 一起安装起来,果真如此的话,那么每个套件在安装的时候『都有一定的顺序与程序!』因为他们三者之间具有相关性,所以安装时必需要三者同时考虑到他们的函式库与相关的编译参数。那么如果今天我只要升级 PHP 呢?有的时候因为只有涉及动态函式库的升级,那么我只要升级 PHP 即可!其它的部分或许影响不大。但是如果今天 PHP 需要重新编译的模块比较多,那么可能会连带的,连 Apache 这个程序也需要重新编译过才行!真是有点给他头痛的!没办法啦!使用 tarball 确实有他的优点啦,但是在这方面,确实也有一定的难度。
由于 Tarball 在升级与安装上面具有这些特色,亦即 Tarball 在卸载上有些难度( 如果您没有好好规划的话),所以,为了方便 Tarball 的管理,通常我们会这样建议使用者:
(1) 最好将 tarball 的原始数据解压缩到 /usr/local/src 当中。
(2) 安装时,最好安装到 /usr/local 这个默认路径下。
(3) 考虑将来的卸载步骤,最好可以将每个套件单独的安装在 /usr/local 下面。例如安装 rp-pppoe-2.6.tar.gz 时,则可以指定该套件需要安装于 /usr/local/rp-pppoe 当中,如此一来,该套件会将所有的数据都写入 /usr/local/rp-pppoe 当中,因此,未来如果要移除该套件,只要将该目录删除即可视为成功的移除了!
(4) 加上man path。单独安装某个套件在某一特定路径下的作法,会导致当有 man page 的时候,使用默认的 MANPATH 找不到相关的帮助文件内容。这个时候就必须要将 man page 的路径加到/etc/man.config 文件中!否则使用 man 也查询不到指令的使用方法的。以上面的例子为例,如果是安装了 /usr/local/rp-pppoe 当中,通常 man page 会放在 /usr/local/rp-pppoe/man 当中,所以,您就必需要在 /etc/man.config 里面差不多 40~50 行左右的地方,加入底下这一行:
MANPATH /usr/local/rp-pppoe/man
这样就可以使用 man 来查询资料啰!