0%

Linux程序设计(一)

课程笔记,随便记记

对应PPT 1-2章

INTRODUCTION

Linux简介

An Introduction to UNIX, Linux, and GNU

Unix操作系统最初是由贝尔实验室开发的,当时的贝尔实验室是电信业巨头—-AT&T(美国电报电话公司)旗下的一员。

UNIX是在20世纪70年代为DEC(数字设备公司)的PDP系列计算机开发的。

UNIX Philosophy

  • Simplicity(简单性)

  • Focus(集中性)

  • Reusable Components(可重用组件)

  • Filters(过滤器)

  • Open File Formats(开放的文件格式)

  • Flexibility(灵活性)

What Is Linux?

Linux是类UNIX内核(操作系统的低级内核)的免费分布式实现。
Linux是由赫尔辛基大学的Linus Torvalds在互联网上的UNIX程序员的帮助下开发的。
现在,使用许多不同类型的CPU的Linux版本可用于各种计算机系统。

发行版简介

分发通常在CD-ROM上,它不仅包含内核,还包含许多其他编程工具和实用程序。

发行版通常附带安装程序和附加文档,以帮助您安装自己的Linux系统。

一些著名的发行版(尤其是在Intelx86和奔腾系列处理器上)是Red Hat Linux、SuSE Linux和Debian GNU/Linux。

Linux编程

Linux系统可以使用大量的编程语言,其中许多语言都是免费的,可以从CD-ROM集合或Internet上的FTP存档站点获得。

下面是Linux程序员可用的编程语言的部分列表:

Ada、 C /C++、Eiffel 、 Forth 、 Fortran、Icon 、 Java JavaScript.

Linux程序

Linux应用程序由两种特殊类型的文件:可执行文件和脚本。

  • 可执行文件是可由计算机直接运行的程序;

  • 脚本是另一个程序(解释器)要遵循的指令集合。(脚本文件类似于Windows中的.bat文件、.cmd文件)

C程序

常用gcc选项

1
2
3
4
5
6
7
8
9
10
11
12
-ansi        只支持ANSI标准的C语法。
-c 只编译,不连接成为可执行文件。编译器只是由输入的.c等源代码文件生成.o为后缀的目标文件,通常用于编译不包含主程序的子程序文件。
-E 只运行C预编译器。
-g 生成调试信息,GNU调试器可利用该信息。
-S 停止于编译步骤之后,不进行汇编步骤,最后的输出文件类型为汇编源代码文件。GCC 产生的汇编语言文件的缺省扩展名是 .s 。
-O1 让gcc对源代码进行基本优化,这些优化在大多数情况下都会使程序执行得更快。
-O2 让gcc产生尽可能小和尽可能快的代码,-O2选项将使编译的速度比使用-O1时慢,但通常产生的代码执行速度会更快。
-o FILE 确定输出文件的名称为FILE,同时这个名称不能和源文件同名。如果不给出这个选项,gcc就给出系统预设的可执行文件a.out。
-Wall 生成所有警告信息。
-w 不生成任何警告信息。
-IDIRECTORY 指定额外的头文件搜索路径DIRECTORY。
-LDIRECTORY 指定额外的函数库搜索路径DIRECTORY。

开发系统路线图

应用

  • 系统提供的用于一般用途的应用程序,包括程序开发,可以在 /usr/bin 中找到。

  • 系统管理员为特定主机或本地网络添加的应用程序通常位于 /usr/local/bin/opt 中。

  • 功能特性和编程系统可能有自己的目录结构和程序目录。

头文件

  • 对于C,它们几乎总是位于 /usr/include 及其子目录中。

  • 其他编程系统也将包括存储在目录中的文件,这些文件由适当的编译器自动搜索。

  • 通过向C编译器指定-I标志,我们可以在子目录或非标准位置使用include文件。例如,

$gcc-I/usr/openwin/include fred.c

库文件

  • 库是为可重用而编写的预编译函数的集合。通常,它们由一组相关函数组成,以执行一个共同的任务。

  • 标准系统库通常存储在 /lib/usr/lib 中。

  • 库文件名总是以lib开头。名称的最后一部分以点开头,并指定库的类型:

    • .a用于传统的静态库

    • .so用于共享库

静态库

  • 当程序需要使用存储在库中的函数时,它包含一个声明该函数的头文件。

  • 把程序和链接器结合成一个程序库和一个可执行程序。

  • 静态库,也被称为archives(归档文件)的名称,通常以.a结尾。

  • 我们可以很容易地用我们自己的程序库来创建和维护gcc的静态库。

共享库

  • 共享库与静态库存储在相同的位置,但共享库具有不同的文件名后缀。
  • 系统可以安排一个共享库的单个副本同时供多个应用程序使用,并只在磁盘上存储一次。
  • 另一个好处是共享库可以独立于依赖它的应用程序进行更新。
  • 通过运行ldd,我们可以看到程序需要哪些共享库。

Linux命令摘要

Starting & Stopping

1
2
3
4
5
6
shutdown -h now   现在关闭系统,不要重新启动
halt 停止所有进程-同上
shutdown -r 5 5分钟后关闭系统并重新启动
shutdown -r now 现在关闭系统并重新启动
reboot 停止所有进程,然后重新启动-同上
startx 启动X系统

Files and Directory

1
2
3
4
5
6
7
8
9
10
cd /home          进入目录'/ home’
cd .. 返回上一级目录
cd 进入根目录
pwd 显示工作目录的路径
ls –l 显示文件和目录的详细信息
mkdir dir1 创建一个名叫'dir1'的
ln –s file1 lnk1 创建指向文件或目录的符号链接(软链接)
ln file1 lnk1 创建指向文件或目录的物理链接
touch –t 0712250000 fileditest
修改文件或目录的时间戳-(YYMMDDhhmm)

Finding files and text within files

1
2
3
4
5
6
find / -name fname          从根目录开始,查找名为fname的文件
find / -name “*fname*” 从根目录开始,查找包含字符串fname的文件
locate missingfilename 使用locate命令查找一个名为missingfilename的文件-假设您已经使用了updatedb命令(请参见下一个)
updatedb 在附加到linux根目录的所有文件系统上创建或更新文件数据库
which missingfilename 显示包含名为missingfilename的可执行文件的子目录
grep textstringtofind /dir 从名为dir的目录开始,查找并列出所有包含textstringtofind的文件

Moving, copying, deleting & viewing files

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ls -l                       使用长格式列出当前目录中的文件
ls -F 列出当前目录中的文件并指示文件类型
ls -laC 以长格式列出当前目录中的所有文件,并按列显示
rm name 删除名为名称的文件或目录
rm -rf name 杀死整个目录,包括文件和子目录
cp filename /home/dirname 将名为filename的文件复制到/home/dirname目录
mv filename /home/dirname 将名为filename的文件移动到/home/dirname目录
cat filetoview 显示名为filetoview的文件
man -k keyword 显示包含keyword的手册页
more filetoview 显示名为filetoview一次查看一页的文件,使用空格进入下一页
head filetoview 显示名为filetoview的文件的前10行
head -20 filetoview 显示名为filetoview的文件的前20行
tail filetoview 显示名为filetoview的文件的最后10行
tail -20 filetoview 显示名为filetoview的文件的最后20行

Installing software for Linux

1
2
3
4
5
6
7
8
9
rpm -ihv name.rpm      安装名为name的rpm包
rpm -Uhv name.rpm 升级名为name的rpm包
rpm -e package 删除名为package的rpm包
rpm -l package 列出包中名为package的文件
rpm -ql package 列出这些文件并声明包的已安装版本
rpm -i --force package 重新安装名为name的rpm软件包,删除了其中的部分(不是使用rpm-e删除)
tar -zxvf archive.tar.gz or tar -zxvf archive.tgz
解压缩包含压缩文件的存档文件中的文件
./configure 执行脚本,准备要编译的已安装文件

User Administration

1
2
3
4
5
6
adduser accountname   创建一个名为accountname的新用户
passwd accountname 为帐号命名一个新密码
groupadd group_name 创建一个新组
groupdel group_name 删除群组
su 从当前登录名以超级用户身份登录
exit 停止成为超级用户并恢复为普通用户

文件权限

使用命令 ls -l ,会出现文件权限信息,例如:drwxr-xr-x

Read = 4 Write = 2 Execute = 1

通过为每个用户类型提供 chmod 命令和适当的八进制代码,可以更改文件许可权。 例如 chmod 7 6 4 filename 将使文件名为R + W + X的文件名为所有者,R + W为组,其他为R。

chmod 7 5 5 拥有者的完全权限,拥有对群组和其他人的读取和执行访问权限。

chmod +x filename 使名为filename的文件对所有用户可执行。

Process Management

1
2
3
4
5
6
7
ps             显示您当前活动的进程
top 显示所有正在运行的进程
kill pid 杀死进程ID pid
killall proc 杀死所有名为proc的进程
bg 列出已停止或后台作业;在后台恢复已停止的作业
fg 将最新工作带到前台
fg n 将工作n带到前台

System Info

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
date                显示当前日期和时间 E.G date 041217002007.00
cal 显示本月的日历
uptime 显示当前正常运行时间
w 显示在线用户
whoami 显示您的登录身份
finger user 显示用户信息
uname -a 显示内核信息
cat /proc/cpuinfo cpu信息
cat /proc/meminfo 内存信息
man command 显示命令手册
df 显示磁盘使用情况
du 显示目录空间使用情况
free 显示内存和交换使用情况
whereis app 显示app的可能位置
which app 显示默认情况下将运行哪个应用

Vi使用指南

Vi是一个流行的文本编辑器,保证在所有Linux和Unix发行版上都能使用,它非常适合从命令行快速编辑配置文件。

有两种方法可以启动Vi:

  • vi—-打开未命名的空白文件。

  • vi filename—-开始编辑空白命名文档或打开文件进行编辑。

Vi有3种模式:

  • 命令模式
  • 插入模式
  • 末行模式

Vi以命令模式开始。

  • 在命令模式下按 i 进入插入模式。
  • Esc 返回命令模式
  • : 从命令模式到末行模式

Insert/Replace mode

像在任何文本编辑器中一样输入。

Command mode/Last-line mode

  1. 保存
1
2
3
:w            将已命名的文件写入磁盘
:w filename 保存未命名的文档或使用新的文件名保存文档。
:w! 覆盖现有文件(而不是正在编辑的文件)所需的文件名。
  1. 退出
1
2
3
:q    退出Vi,但仅当文档未被修改时
:q! 不保存文档就退出Vi
:wq 保存文档并退出Vi
  1. 打开
1
2
:e filename   打开文件进行编辑,或开始新的命名文档
:r filename 在当前行和下一行之间添加文件filename的内容
  1. 移动
1
2
3
4
5
[PageUp]    上移1页。在前面加上一个数字可以向上移动那么多页
[PageDn] 下移1页。在前面加一个数字可以向下移动那么多页
gg 移到文档开头
G 移到文档末尾
:n 移动到特定行的开头
  1. 撤销和重做
1
2
u         撤消
[Ctrl]r 恢复
  1. 删除(剪切)
1
2
3
dd           删除(剪切)当前行
Ndd 删除(剪切)当前行加上下面的n-1行
x or [Del] 删除(剪切)当前字符
  1. 拖拉(复制)并粘贴
1
2
3
4
5
6
yy or Y     拖拉(复制)当前行
nyy or nY 拖拉(复制)当前行和下面的n-1行
p 在当前行和下一行之间粘贴文本
np 在当前行和下一行之间粘贴文本n次
P 在当前行和上一行之间粘贴文本
nP 在当前行和上一行之间粘贴文本,n次
  1. 搜索
1
2
3
4
/text   从光标中查找下一个文本实例,其中text是要查找的字符串。如果找不到任何内容,那么Vi将从文档的开始处折回光标
?text 与上面相同,但搜索方向相反
n 下一次出现(在任一方向)
N 上一次发生
  1. 替换
1
2
3
4
:s/old/new     用new替换old的下一个实例
:s/old/new/g 用new替换old的所有实例
rcharacter 用字符替换当前字符
nrcharacter 将当前字符加上下一个n-1个字符替换为字符

Debugging and Make

错误类型

功能定义错误:如果程序的功能被错误地定义了,它就肯定不能完成预定的工作。所以,在开始设计之前,你必须确认自己知道并理解这个程序究竟是用来干什么的。

设计规划错误:无论程序的规模大小,在创建它们之前都需要有一个设计规划的阶段。对程序员来说,一定要花点时间思考:如何构造程序,需要什么样的数据结构,它又应该如何在程序中使用。

代码编写错误:当然,每个人都会出现键入错误。在程序中遇到错误时,要重新阅读源代码或与其他人进行讨论。

可以试着在纸上执行程序的核心代码,这个过程被称为干运行。针对那些重要的例程,先记下它们的输入值,然后逐步手工计算出结果。

一般调试技术

一般方法是先运行程序并观察其输出结果,如果不能正常工作,我们就需要决定应该采取哪些措施。

(1)可以修改程序然后重新尝试(代码检查-试运行-出错法)

(2)在程序中增加一些语句以获得更多关于程序内部运行情况的信息(取样法)

(3)可以直接检查程序的运行情况(受控执行法)

调试的5个阶段

Testing(测试): 找出存在哪些缺陷或错误

Stabilization(固化): 使错误可重现

Localization(定位): 标识负责的代码行

Correction(纠正): 修复代码

Verification(验证): 确保修复程序有效

取样法

检测是向程序中添加代码,目的是收集有关程序运行时行为的更多信息。
添加 printf 调用以在程序执行的不同阶段打印出变量的值是很常见的。
但是我们应该知道,只要更改程序,该过程就需要进行额外的编辑和编译,当然,在修复错误之后,我们将需要删除代码。

第一种技巧是用C语言的预处理器有选择的包括取样代码,这样只需重新编译程序就可以达到包含或去除调试代码的目的。
我们可以使用编译器标志 -DDEBUG 来编译程序,以定义DEBUG符号并包括多余的代码,也可以不排除它。

C语言预处理器定义的一些宏可以帮助我们进行调试。这些宏在扩展后会提供当前编译操作的相关信息。

Macro Description
__LINE__ A decimal representing the current line number
__FILE__ A string representing the current file name
__DATE__ A string of the form “Mmm dd yyyy”,the current date
__TIME__ A string of the form”hh:mm:ss”,the current time

使用GDB调试

gdb本身是一个基于文本的应用程序,但是它确实提供了一些捷径来帮助执行重复性任务。
许多版本具有历史记录的命令行编辑,因此您可以向后滚动并再次执行相同的命令(尝试使用光标键)。
所有版本都支持“空命令”。 按Enter键将再次执行最后一个命令。 当使用step或next命令逐行浏览程序时,此功能特别有用。

linux下gdb调试方法与技巧整理

多个源文件的问题

在编写小型程序时,许多人只需在编辑后通过重新编译所有文件即可重建其应用程序。
但是,对于较大的程序,这种简单方法的一些问题变得很明显。 编辑-编译-测试周期的时间将增加。
当只更改了一个文件时,即使是最有耐心的程序员也希望避免重新编译所有文件。
当创建多个头文件并将其包含在不同的源文件中时,可能会出现更困难的问题。

make命令和Makefiles

您必须提供一个文件,告诉您如何构造您的应用程序。 该文件称为makefile。
生成文件最常与项目的其他源文件位于同一目录中。
如果您有一个大型项目,则可以选择对项目的不同部分使用单独的Makefile对其进行管理。
make命令和makefile的组合为管理项目提供了非常强大的工具。

The Syntax of Makefiles

一个makefile由一组依赖关系和规则组成。

  • 依赖项具有目标(要创建的文件)和依赖项依赖的一组源文件。
  • 规则描述了如何从相关文件中创建目标。
  • 通常,目标是单个可执行文件。

make文件由make命令读取,该命令确定要创建的目标文件,然后比较源文件的日期和时间,以决定需要调用哪些规则来构造目标。
通常,必须先创建其他中间目标,然后才能制定最终目标。
make命令使用makefile来确定目标的生成顺序以及要调用的规则的正确顺序。

Options and Parameters to make

make程序本身有几个选项。 三种最常用的是

1
2
3
-k           它告诉make在发现错误时继续进行。
-n 它告诉make打印出实际上会执行的操作。
-f <文件名> 它允许您告诉make将哪个文件用作其makefile。

要告诉make建立一个特定的目标,您可以传递目标名称作为参数。 如果不这样做,make会尝试创建makefile中列出的第一个目标。
许多程序员将所有目标都指定为其makefile中的第一个目标,然后将其他目标列为所有目标的依赖项。
当没有指定目标时,此约定使makefile在默认情况下应尝试构建的目标明确。

  1. Dependencies(依赖关系)

依赖关系指定最终应用程序中的每个文件与源文件的关系。
在makefile中,我们通过编写目标名称,冒号,空格或制表符,然后用空格或制表符分隔的用于创建目标文件的文件列表来编写这些规则。

我们的示例的依赖项列表是

1
2
3
4
myapp: main.o 2.o 3.o
main.o: main.c a.h
2.o: 2.c a.h b.h
3.o: 3.c b.h c.h

这表示myapp依赖于main.o,2.o和3.o,main.o依赖于main.c和a.h,依此类推。
这组依赖关系提供了一个层次结构,显示了源文件之间的关系。
如果我们希望制作多个文件,则可以全部使用phony目标。 假设我们的应用程序由二进制文件myapp和手册页myapp.1组成。

我们可以用以下行来指定

1
all: myapp myapp.l
  1. Rules(规则)

makefile的第二部分指定描述如何创建目标的规则。

在上一节的示例中,在make命令确定2.o需要重建之后,应使用哪个命令?
如果我们需要指定一个include目录或设置符号信息选项以进行以后的调试该怎么办?
我们可以通过在makefile中指定显式规则来做到这一点。

大多数规则由一个简单的命令组成,该命令可以在命令行中键入。 对于我们的示例,我们将创建第一个makefile,

Makefile1:

1
2
3
4
5
6
7
8
myapp: main.o 2.o 3.o
gcc -o myapp main.o 2.o 3.o
main.o: main.c a.h
gcc -c main.c
2.o: 2.c a.h b.h
gcc -c 2.c
3.o: 3.c b.h c.h
gcc -c 3.c

我们使用-f选项调用make命令,因为我们的makefile没有通常的默认名称makefile或Makefile。
如果我们在不包含源代码的目录中调用此代码,则会收错误消息。

Comments in a Makefile

生成文件中的注释以#开头,并继续到该行的末尾。
像在C源文件中一样,makefile中的注释可以帮助作者和其他人了解编写文件时的意图。

Macros(宏) in a Makefile

但是,对于包含大量文件的项目,它们也往往很大且不灵活。
我们通过写入MACRONAME = value来在makefile中定义一个宏,然后通过写入 $(MACRONAME) 或 $ {MACRONAME}来访问MACRONAME的值。
通过将行的其余部分保留在=空白之后,可以将宏的值设置为blank(空)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
all: myapp
# Which compiler
CC = gcc
# Where are include files kept
INCLUDE = .
# Options for development
CFLAGS = -g -Wall -ansi
# Options for release
# CFLAGS = -O -Wall -ansi
myapp: main.o 2.o 3.o
$(CC) -O myapp main.o 2.o 3.o
main.o: main.c a.h
$(CC) -I$(INCLUDE) $(CFLAGS) -c main.c
2.o: 2.c a.h b.h
$(CC) -I$(INCLUDE) $(CFLAGS) -c 2.c
3.o: 3.c b.h c.h
$(CC) -I$(INCLUDE) $(CFLAGS) -c 3.c

实际上,make具有几个特殊的内部宏,您可以使用这些宏使makefile更加简洁。
这些宏中的每一个仅在使用前就展开,因此宏的含义可能会随着Makefile的进行而变化。

1
2
3
$^ List of prerequisites of the current
$@ Name of the current target
$< Name of the first prerequisite

Multiple Targets

通常,制作多个目标文件或将几组命令收集到一个位置通常很有用。 我们可以扩展我们的makefile文件来做到这一点。
让我们添加一个“清除”选项以删除不需要的对象,并添加一个“安装”选项以将完成的应用程序移动到另一个目录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
all: myapp
# Which compiler
CC = gcc
# Where to install
INSTDIR = /usr/local/bin
# Where are include files kept
INCLUDE = .
# Options for development
CFLAGS = -g -Wall -ansi
# Options for release
# CFLAGS = -O -Wall -ansi
myapp: main.o 2.o 3.o
$(CC) -O myapp main.o 2.o 3.o
main.o: main.c a.h
$(CC) -I$(INCLUDE) $(CFLAGS) -c main.c
2.o: 2.c a.h b.h
$(CC) -I$(INCLUDE) $(CFLAGS) -c 2.c
3.o: 3.c b.h c.h
$(CC) -I$(INCLUDE) $(CFLAGS) -c 3.c

1
2
3
4
5
6
7
8
9
10
11
12
clean:
-rm main.o 2.o 3.o
install: myapp
@if [ -d $(INSTDIR) ]; \
then \
cp myapp $(INSTDIR); \
chmod a+x $(INSTDIR)/myapp; \
chmod og-w $(INSTDIR)/myapp; \
echo "Installed in $(INSTDIR)"; \
else \
echo "Sorry, $(INSTDIR) does not exist"; \
fi

首先,当我们执行make而不指定目标时,默认行为是构建目标myapp。
下一个要点与清洁和安装这两个附加目标有关。 该命令以-开头,它告诉make忽略命令的结果。
安装目标取决于myapp,因此要知道在执行其他安装命令之前,它必须先创建myapp。
该命令以@符号开头,告诉make在执行规则之前不要在标准输出上打印命令。

安装目标一个接一个地执行几个命令,以将应用程序安装在其最终的存放位置。 在执行下一个命令之前,它不会检查每个命令是否成功。
如果仅在前一个命令成功后才执行后续命令非常重要,那么我们可以编写由&&连接的命令,如下所示:

1
2
3
4
5
6
7
8
9
@if [ -d $(INSTDIR) ]; \
then \
cp myapp $(INSTDIR) && \
chmod a+x $(INSTDIR)/myapp && \
chmod og-w $(INSTDIR)/myapp && \
echo "Installed in $(INSTDIR)"; \
else \
echo "Sorry, $(INSTDIR) does not exist"; false; \
fi

Suffix and Pattern Rules

我们看到内置规则在使用时都有后缀,所以给出带有某个特定后缀的文件时,make命令知道应该用哪个规则来创建带有另一个不同后缀名的文件。

  • 最常见的一条规则是,通过一个以.c为后缀名的文件创建出一个以.o为后缀名的文件。

要添加新的后缀规则,我们首先在makefile中添加一行,以告知make有关新后缀的信息; 然后,我们可以使用该新后缀编写一条规则。

nmake uses the special syntax

.<old_suffix>.<new_suffix>:

定义用于从具有相同基本名称但具有旧后缀的文件中创建具有新后缀的文件的一般规则。
这是我们制作文件的一部分,其中包含将.cpp文件转换为.o文件的新通用规则:

1
2
3
.SUFFIXES:    .cpp
.cpp.o:
$(CC) -xc++ $(CFLAGS) -I$(INCLUDE) -c $<

make的最新版本包括实现相同效果的替代语法,以及更多其他功能。

1
2
%.cpp: %o
$(CC) -xc++ $(CFLAGS) -I$(INCLUDE) -c $<

Managing Libraries with make

在处理较大的项目时,通常可以使用库来管理多个编译产品。
语法为lib(file.o),这表示目标文件file.o,存储在库lib.a中。
make命令具有一个用于管理库的内置规则,该规则通常等效于以下内容:

1
2
3
.c.a:
$(CC) -c $(CFLAGS) $<
$(AR) $(ARFLAGS) $@ $*.o

对于大型项目,一种比较方便的做法是用函数库来管理多个编译产品。

$*不包括后缀名的当前依赖文件的名字

\$(AR)\$(ARFLAGS) 通常分别默认为命令ar和选项rv。
简洁的语法告诉我们,要从.c文件到.a库,它必须应用两个规则:

  • 第一条规则说,它必须编译源文件并生成一个对象。
  • 第二条规则说使用ar命令修改库,添加新的目标文件。

Managing a Library

更改应用程序,使文件2.o和3.o保存在名为mylib.a的库中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
all: myapp
# Which compiler
CC = gcc
# Where to install
INSTDIR = /usr/local/bin
# Where are include files kept
INCLUDE = .
# Options for development
CFLAGS = -g -Wall -ansi
# Options for release
# CFLAGS = -O -Wall -ansi
# Local Libraries
MYLIB = mylib.a
myapp: main.o $(MYLIB)
$(CC) -o myapp main.o $(MYLIB)
$(MYLIB): $(MYLIB)(2.o) $(MYLIB) 3.o
main.o: main.c a.h
2.o: 2.c a.h b.h
3.o: 3.c b.h c.h
clean:
-rm main.o 2.o 3.o $(MYLIB)
install: myapp
@if [ -d $(INSTDIR) ]; \
then \
cp myapp $(INSTDIR); \
chmod a+x $(INSTDIR)/myapp; \
chmod og-w $(INSTDIR)/myapp; \
echo "Installed in $(INSTDIR)"; \
else \
echo "Sorry, $(INSTDIR) does not exist"; \
fi

Makefiles and Subdirectories

对于大型的项目,有时我们希望能把组成一个函数库的几个文件从主文件中分离出来,并将它们保存到一个子目录中。

首先在子目录中编写出第二个makefile文件,它的作用是编译该子目录下的源文件并将它们保存到一个函数库中,然后将该库文件拷贝到上一级的主目录中。

在主目录中的makefile文件包含一个用于制作函数库的规则,该规则会调用第二个makfile文件。

Make命令调用这条规则来创建函数库时,它将切换到子目录mylibdirectory中,然后调用一个新的make命令来管理函数库。由于make会针对每个命令调用一个新的shell,使用第二个makefile文件的make命令本身又没有执行cd命令,但它又必须在一个不同的目录下创建函数库,为解决这一问题,我们用括号将这两个命令括起来,以使它们只被一个单独的shell处理

1
2
mylib.a:
(cdmylibdirctory;$(MAKE))