如何自己编译apue.3e中代码 & 学习写makefile
本来是搜pthread的相关资料,看blog发现很多linux程序员都看的一本神书《APUE》,里面有系统的两章内容专门讲pthread(不过是用c语言做的代码示例,这个不碍事,还是归到原来linux c++分类中了),决定把这本书打印出来,过一下这两章内容。这个系列后面的日志会根据APUE书中的内容来。
这篇日志说的内容与APUE没有直接关系,但是却是由APUE引发的。
背景是这样的:
(1)对于我这个只在windows下用vs等IDE写过一些c程序,不知道gcc是干啥的人来说,在unix下搞c就有困难(这个感觉有点儿类似于只学过开自动挡的汽车,一下子让你开手动挡了)。
(2)上来APUE的第一个demo就不能编译通过,因为APUE的所有demo代码中都包含一行include “apue.h”。翻开/apue/include/apue.h看了一眼:都是一些宏定义和函数声明,一些函数的具体实现都在/apue/lib/中了。这些东西怎么整合起来?以前在windows下好像都是用IDE建立一个工程,剩下的都是IDE给做好了。但是现在用的mac,将来在公司要用的是Linux,学会在linux下开发c c++的工程项目是早晚的事情,躲不开。
(3)如果是python这种脚本语言,只要有python解释器就OK了;但是c语言这种强类型的语言的源代码是如何变成可执行的二进制文件的?这个问题我也说不清楚。(非计算机科班出身,没学过编译原理)
上面的三个问题,恐怕也是很多只在win下开发过程序的人遇到的问题,而我就是其中一员。下面记录下解决上面困难的过程。
记录一下遇到的各种问题,通过查阅资料和推理一点儿点儿摸索着解决了(纯自己记录,非小白可直接忽略,不敢浪费大家时间):
(一)gcc -I(搞清楚include "...")
原书的demo中 第一行是include "apue.h",但自己意识到“apue.h”不在当前路径下,应该把apue.h所在的路径完整写出来。 于是照猫画虎的代码如下:
#include "../apue.3e/include/apue.h" #include <pthread.h> void * thr_fn1(void *arg) { printf(("thread 1 returning\n")); return ((void *)1); } void * thr_fn2(void *arg) { printf(("thread 2 exiting")); pthread_exit((void *)2); } int main(int argc, char const *argv[]) { int err; pthread_t tid1, tid2; void *tret; err = pthread_create(&tid1, NULL, thr_fn1, NULL); err_exit(err, "can't"); return 0; }
但马上觉得上述的代码有问题:如果apue.h的路径改了,那岂不是所有源代码中的include “apue.h”都要跟着改?这样肯定不科学。
于是查阅了一下gcc命令(http://blog.sina.com.cn/s/blog_57295811010008pj.html),知道了-I这个选项,可以把include "..."包含的文件路径放在-I后面。于是做了如下修改:
#include "apue.h"
并执行命令:gcc -I../apue.3e/include 11.3.c -pthread
得到了如下结果:
报错的内容就是说err_exit()这个函数找不到吧(这个时候不知道ld是什么意思,linker是啥也不知道)
于是推理一下:可能是只有apue.h头文件,gcc的过程中没有找到实际函数实现吧。
去翻翻发现apue.h中有一句话:
void err_exit(int, const char *, ...) __attribute__((noreturn));
这就是一句函数声明。
那么函数实体在哪里呢?到这里有点儿瞎,因为原书给的文件很大,那么多的文件上哪找err_exit呢?
即使找到的err_exit()的函数定义,又怎么让包含err_exit()函数定义的这个文件与demo文件合在一起呢?线索断了。
(二)make命令以及Makefile (在unix下自己完成win下IDE完成的事情)
接着(一),瞎查了半天没有什么结果,纠结了一会儿。
一个偶然的想法让我在解决问题的路上去学了下make以及Makefile。
由于书上的demo毕竟是在ubuntu上运行的,而我的电脑是mac系统,虽说都是unix,但是会不会是我的系统有问题或者gcc版本这类的问题导致不能编译通过呢?于是,我就想试试,能不能在APUE提供的源代码目录下编译通过呢?
这时候,我找到了APUE提供的源码文件夹中的Makefile文件,如下:
ROOT=.. EXTRALIBS=-pthread PLATFORM=$(shell $(ROOT)/systype.sh) include $(ROOT)/Make.defines.$(PLATFORM) BAR = ifeq "$(PLATFORM)" "macos" TLOCK = EXTRALIBS=-pthread else TLOCK = timedlock endif ifeq "$(PLATFORM)" "linux" BAR = barrier EXTRALIBS=-pthread -lrt -lbsd endif ifeq "$(PLATFORM)" "freebsd" BAR = barrier EXTRALIBS=-pthread endif ifeq "$(PLATFORM)" "solaris" BAR = barrier EXTRALIBS=-lpthread -lrt endif PROGS = badexit2 cleanup exitstatus threadid all: $(PROGS) condvar.o maketimeout.o mutex1.o mutex2.o mutex3.o rwlock.o $(TLOCK) $(BAR) $(PROGS): $(LIBAPUE) $(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS) $(LDLIBS) clean: rm -f $(PROGS) $(TEMPFILES) *.o $(TLOCK) $(BAR) include $(ROOT)/Make.libapue.inc
之前听说过,Makefile貌似是告诉系统去怎么编译源代码这类的。于是,上网搜了如下的资料,对make和Makefile进行了下突击:
gcc -ansi -I../include -Wall -DMACOS -D_DARWIN_C_SOURCE threadid.c -o threadid -L../lib -lapue -pthread
自己运行的命令照比make执行的命令主要少了红字的那一块。原来,是-L -l告诉了gcc,函数err_exit()的具体实现在哪里的啊。
==================================================
插播:突然想起来自己买过一本书《程序员的自我修养》,里面讲了编译、链接什么的。
于是赶紧读了读,搞清楚点儿。也是在那本书中,知道了“链接”是一个很重要的过程,把
不同模块拼在一起组成最后的完整的程序。
插播结束。
==================================================
-l后面接的“apue”应该是库文件的名字(linux静态库文件一般以libXXX.a的形式,XXX就是-l后面跟着的名字),于是自然就想看看这个apue库文件的样子
(四)sublime的陷阱 (有些格式的文件,sublime不显示)
先找了下原书提供的代码文件中的lib文件夹;果然,找到了err_exit()函数的实现文件:error.c
大家可以看到,本人用的编辑器是sublime text2。为什么要提一下这个编辑器?后面马上说,如何被编辑器的设置给坑了。
看看sublime左侧边栏中,lib文件夹下也没有这样的文件啊,难倒还有其他的坑么?到这里又有些纠结了
(1)现在源文件所在目录已经找到了
(2)也知道-L把外部的库文件路径告诉gcc,-l告诉gcc应该具体用哪个库文件了
于是我在网上搜了一下(如这篇blog,http://www.cnblogs.com/showna/articles/1013399.html),库文件确实都是lib开头,.a .dylib结尾的这类啊。难倒库文件在lib文件夹中丢了?还是有其他的说道?。
正在郁闷的时候,用iTerm命令行进入了lib文件夹下面,突然展现了惊人的一幕:
喵了个咪的,这个apue库文件.a、编译后的目标文件.o都有啊!原来只是sublime的左边栏中没有这些.o和.a文件啊。
于是,知道被sublime给坑了。很快上网搜到了原因,在sublime的Preferences选中中有如下的几行默认配置:
在sublime默认中,.a .o .lib这些扩展名的文件都不显示啊!!!
到了这里,终于把这一系列问题搞清楚了(这个过程大概持续了一白天),总结下原因:
(1)对编辑器特性不熟悉(不知道有些文件还自动过滤了不显示),这个只能靠实践去磨了
(2)对于程序由源代码到可执行文件的原理不了解。以前在windows平台很多事情都VS做了,用Eclipse写Java就更省事儿了;现在需要自己写Makefile去做,就要把每个环节都搞清楚。这个光靠实践不行,还得去系统的学学,比如《程序员的自我修养》这本书就是非常好的内容。遂决定读。