如何自己编译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
"));
    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进行了下突击:

中文的blog:
youtube上的20分钟视频:
视频中提到的可以查阅的规范资料:http://www.gnu.org/software/make/manual/make.html
OK,看过上面的内容,我就试试在这个文件夹下直接make吧。得到了下面的结果:
编译通过了!并且运行了可执行文件,也与书中的结果一样。
这就证明了,这个demo源代码是可以被正确编译调试的,肯定是我中间哪个环节没弄对。线索又续上了。
 
(三)gcc -L -l (链接的过程)
接着(二),上图中执行make命令是对pthreads这一章所有demo的源代码进行编译了,我从中抽出来了关心的那一条:
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去做,就要把每个环节都搞清楚。这个光靠实践不行,还得去系统的学学,比如《程序员的自我修养》这本书就是非常好的内容。遂决定读。

原文地址:https://www.cnblogs.com/xbf9xbf/p/4755458.html