第一部分 基础知识第1章 构造系统概述 2
1.1 什么是构造系统 2
1.1.1 编译型语言 3
1.1.2 解释型语言 3
1.1.3 Web应用 4
1.1.4 单元测试 5
1.1.5 静态分析 5
1.1.6 文档生成 6
1.2 构造系统的各个组成部分 6
1.2.1 版本控制工具 7
1.2.2 源树与目标树 7
1.2.3 编译工具和构造工具 8
1.2.4 构造机器 9
1.2.5 发布打包与目标机器 9
1.3 构造过程和构造描述 11
1.4 如何使用构造系统 12
构造管理工具 12
1.5 构造系统的质量 13
本章小结 14
第2章 基于Make的构造系统 15
2.1 Calculator示例 15
2.2 创建一个简单的makefile 17
2.3 对这个makefile进行简化 19
2.4 额外的构造任务 20
2.5 框架的运用 21
本章小结 23
第3章 程序的运行时视图 24
3.1 可执行程序 24
3.1.1 原生机器码 25
3.1.2 单体系统镜像 25
3.1.3 程序完全解释执行 26
3.1.4 解释型字节码 26
3.2 程序库 28
3.2.1 静态链接 28
3.2.2 动态链接 29
3.3 配置文件和数据文件 30
3.4 分布式程序 30
本章小结 31
第4章 文件类型与编译工具 33
4.1 C/C++ 34
4.1.1 编译工具 34
4.1.2 源文件 35
4.1.3 汇编语言文件 37
4.1.4 目标文件 38
4.1.5 可执行程序 40
4.1.6 静态程序库 40
4.1.7 动态程序库 41
4.1.8 C++编译 42
4.2 Java 43
4.2.1 编译工具 43
4.2.2 源文件 44
4.2.3 目标文件 45
4.2.4 可执行程序 47
4.2.5 程序库 48
4.3 C# 48
4.3.1 编译工具 49
4.3.2 源文件 49
4.3.3 可执行程序 51
4.3.4 程序库 53
4.4 其他文件类型 55
4.4.1 基于UML的代码生成 56
4.4.2 图形图像 57
4.4.3 XML配置文件 58
4.4.4 国际化与资源绑定 58
本章小结 59
第5章 子标的与构造变量 60
5.1 针对子标的进行构造 61
5.2 针对软件的不同版本进行构造 62
5.2.1 指定构造变量 63
5.2.2 对代码的定制调整 65
5.3 针对不同的目标系统架构进行构造 68
5.3.1 多重编译器 68
5.3.2 面向指定平台的文件/功能 69
5.3.3 多个目标树 69
本章小结 71
第二部分 构造工具现实场景 75
场景1:源代码放在单个目录中 75
场景2:源代码放在多个目录中 76
场景3:定义新的编译工具 76
场景4:针对多个变量进行构造 77
场景5:清除构造树 77
场景6:对不正确的构造结果进行调试 78
第6章 Make 79
6.1 GNU Make编程语言 80
6.1.1 makefile规则:用来建立依赖关系图 80
6.1.2 makefile规则的类型 81
6.1.3 makefile变量 82
6.1.4 内置变量和规则 84
6.1.5 数据结构与函数 85
6.1.6 理解程序流程 87
6.1.7 进一步阅读资料 90
6.2 现实世界的构造系统场景 90
6.2.1 场景1:源代码放在单个目录中 90
6.2.2 场景2(a):源代码放在多个目录中 92
6.2.3 场景2(b):对多个目录进行迭代式Make操作 93
6.2.4 场景2(c):对多个目录进行包含式Make操作 96
6.2.5 场景3:定义新的编译工具 101
6.2.6 场景4:针对多个变量进行构造 102
6.2.7 场景5:清除构造树 104
6.2.8 场景6:对不正确的构造结果进行调试 105
6.3 赞扬与批评 107
6.3.1 赞扬 107
6.3.2 批评 108
6.3.3 评价 109
6.4 其他类似工具 110
6.4.1 Berkeley Make 110
6.4.2 NMake 111
6.4.3 ElectricAccelerator和Spark Build 111
本章小结 113
第7章 Ant 115
7.1 Ant编程语言 116
7.1.1 比“Hello World”稍多一些 116
7.1.2 标的的定义和使用 118
7.1.3 Ant的控制流 119
7.1.4 属性的定义 120
7.1.5 内置的和可选的任务 122
7.1.6 选择多个文件和目录 125
7.1.7 条件 126
7.1.8 扩展Ant语言 127
7.1.9 进一步阅读资料 128
7.2 现实世界的构造系统场景 129
7.2.1 场景1:源代码放在单个目录中 129
7.2.2 场景2(a):源代码放在多个目录中 130
7.2.3 场景2(b):多个目录,多个build.xml文件 130
7.2.4 场景3:定义新的编译工具 133
7.2.5 场景4:针对多个变量进行构造 136
7.2.6 场景5:清除构造树 140
7.2.7 场景6:对不正确的构造结果进行调试 141
7.3 赞扬与批评 142
7.3.1 赞扬 143
7.3.2 批评 143
7.3.3 评价 144
7.4 其他类似工具 144
7.4.1 NAnt 144
7.4.2 MS Build 145
本章小结 146
第8章 SCons 147
8.1 SCons编程语言 148
8.1.1 Python编程语言 148
8.1.2 简单编译 151
8.1.3 管理构造环境 154
8.1.4 程序流程和依赖关系分析 157
8.1.5 决定何时重新编译 158
8.1.6 扩展该语言 160
8.1.7 其他有趣的特性 162
8.1.8 进一步阅读资料 163
8.2 现实世界的构造系统场景 163
8.2.1 场景1:源代码放在单个目录中 163
8.2.2 场景2(a):源代码放在多个目录中 163
8.2.3 场景2(b):多个SConstruct文件 164
8.2.4 场景3:定义新的编译工具 165
8.2.5 场景4:针对多个变量进行构造 167
8.2.6 场景5:清除构造树 168
8.2.7 场景6:对不正确的构造结果进行调试 169
8.3 赞扬与批评 171
8.3.1 赞扬 171
8.3.2 批评 172
8.3.3 评价 173
8.4 其他类似工具 173
8.4.1 Cons 173
8.4.2 Rake 174
本章小结 176
第9章 CMake 177
9.1 CMake编程语言 178
9.1.1 CMake语言基础 178
9.1.2 构造可执行程序和程序库 179
9.1.3 控制流 182
9.1.4 跨平台支持 184
9.1.5 生成原生构造系统 185
9.1.6 其他有趣的特性以及进一步阅读资料 190
9.2 现实世界的构造系统场景 191
9.2.1 场景1:源代码放在单个目录中 191
9.2.2 场景2:源代码放在多个目录中 191
9.2.3 场景3:定义新的编译工具 192
9.2.4 场景4:针对多个变量进行构造 193
9.2.5 场景5:清除构造树 194
9.2.6 场景6:对不正确的构造结果进行调试 194
9.3 赞扬与批评 195
9.3.1 赞扬 195
9.3.2 批评 195
9.3.3 评价 196
9.4 其他类似工具 196
9.4.1 Automake 196
9.4.2 Qmake 197
本章小结 197
第10章 Eclipse 199
10.1 Eclipse的概念和GUI 199
10.1.1 创建项目 200
10.1.2 构造项目 206
10.1.3 运行项目 210
10.1.4 使用内部项目模型 212
10.1.5 其他构造特性 213
10.1.6 进一步阅读资料 214
10.2 现实世界的构造系统场景 215
10.2.1 场景1:源代码放在单个目录中 215
10.2.2 场景2:源代码放在多个目录中 216
10.2.3 场景3:定义新的编译工具 217
10.2.4 场景4:针对多个变量进行构造 217
10.2.5 场景5:清除构造树 220
10.2.6 场景6:对不正确的构造结果进行调试 220
10.3 赞扬与批评 221
10.3.1 赞扬 221
10.3.2 批评 221
10.3.3 评价 222
10.4 其他类似工具 222
本章小结 224
第三部分 高级主题第11章 依赖关系 226
11.1 依赖关系图 227
11.1.1 增量式编译 228
11.1.2 完全、增量式和子标的构造 228
11.2 依赖关系错误导致的问题 229
11.2.1 问题:依赖关系缺失导致运行时错误 229
11.2.2 问题:依赖关系缺失导致编译错误 230
11.2.3 问题:多余的依赖关系导致大量不必要的重新构造 231
11.2.4 问题:多余的依赖关系导致依赖关系分析失败 231
11.2.5 问题:循环依赖关系 232
11.2.6 问题:以隐式队列顺序替代依赖关系 232
11.2.7 问题:Clean标的什么也清除不了 233
11.3 步骤一:计算依赖关系图 233
11.3.1 获取确切的依赖关系 234
11.3.2 把依赖关系图缓存起来 236
11.3.3 对缓存的依赖关系图进行更新 237
11.4 步骤二:判断哪些文件已过期 239
11.4.1 基于时间戳的方法 240
11.4.2 基于校验和的方法 241
11.4.3 标志参数比较 242
11.4.4 其他高级方法 243
11.5 步骤三:为编译步骤排定队列顺序 243
本章小结 246
第12章 运用元数据进行构造 247
12.1 调试支持 247
12.2 性能分析支持 249
12.3 代码覆盖分析支持 250
12.4 源代码文档化 251
12.5 单元测试 253
12.6 静态分析 256
12.7 向构造系统加入元数据 257
本章小结 258
第13章 软件打包与安装 259
13.1 归档文件 260
13.1.1 用于打包的脚本 260
13.1.2 其他归档文件格式 262
13.1.3 对打包脚本的改进 263
13.2 包管理工具 265
13.2.1 RPM包管理工具格式 265
13.2.2 rpm build过程 266
13.2.3 RPM规格文件示例 267
13.2.4 根据规格文件创建RPM文件 272
13.2.5 安装RPM示例 274
13.3 定制式GUI安装工具 275
13.3.1 Nullsoft Scriptable Install System(NSIS) 276
13.3.2 安装工具脚本 277
13.3.3 定义安装页面 280
13.3.4 许可授权页面 281
13.3.5 选择安装目录 282
13.3.6 主要组件 282
13.3.7 可选组件 283
13.3.8 定制页面 285
13.3.9 安装页面和卸载程序 286
本章小结 288
第14章 版本管理 289
14.1 对哪些东西进行版本控制 290
14.1.1 构造描述文件 290
14.1.2 对工具的引用 292
14.1.3 大型二进制文件 296
14.1.4 对源树的配置 296
14.2 哪些东西不应当放到源树中 297
14.2.1 生成的文件被保存到源树中 297
14.2.2 生成的文件被纳入到版本控制中 299
14.2.3 构造管理脚本 299
14.3 版本编号 300
14.3.1 版本编号体系 300
14.3.2 协调并更新版本号 301
14.3.3 版本号的保存与检索 302
本章小结 303
第15章 构造机器 305
15.1 原生编译与跨平台编译 306
15.1.1 原生编译 306
15.1.2 跨平台编译 306
15.1.3 异构环境 307
15.2 集中式开发环境 307
15.2.1 构造机器为何有差异 308
15.2.2 管理多个构造机器 310
15.3 开源开发环境 312
15.4 GNU Autoconf 315
15.4.1 高层次工作流 315
15.4.2 Autoconf示例 317
15.4.3 运行autoheader和autoconf 319
15.4.4 在构造机器上运行configure脚本 320
15.4.5 使用配置信息 322
本章小结 323
第16章 工具管理 324
16.1 工具管理的规则 324
16.1.1 规则1:做笔记 324
16.1.2 规则2:对源代码进行版本控制 325
16.1.3 规则3:定期升级工具 326
16.1.4 规则4:对工具的二进制文件进行版本控制 327
16.1.5 对规则的破坏 329
16.2 编写自己的编译工具 329
用Lex和Yacc编写定制工具 330
本章小结 332
第四部分 提升规模第17章 降低最终用户面对的复杂性 334
17.1 构造框架 334
17.1.1 面向开发人员的构造描述 335
17.1.2 面向框架的构造描述 336
17.1.3 惯例优先于配置 336
17.1.4 构造工具示例:Maven 337
17.2 避免支持多个构造变量的原因 338
17.2.1 需要测试更多的构造变量 338
17.2.2 代码会变得混乱 339
17.2.3 构造时间会增多 340
17.2.4 需要更多磁盘空间 340
17.3 降低复杂性的各种技术方法 340
17.3.1 使用现代构造工具 340
17.3.2 自动检测依赖关系 341
17.3.3 把生成的文件放在源树之外 341
17.3.4 确保正确清除构造树 341
17.3.5 碰到第一个错误即中止构造 342
17.3.6 提供有意义的错误信息 343
17.3.7 校验输入参数 343
17.3.8 不要把构造脚本搞得过分复杂 344
17.3.9 避免使用晦涩的语言特性 344
17.3.10 不要用环境变量控制构造过程 345
17.3.11 确保构造形成的发布版与调试版保持相似 345
17.3.12 准确显示正在执行的命令 346
17.3.13 把对工具的引用纳入版本控制 347
17.3.14 把构造指令纳入版本控制 347
17.3.15 自动检测编译标志参数的变化 347
17.3.16 不要在构造系统中调用版本控制工具 347
17.3.17 尽量频繁地进行持续集成 348
17.3.18 统一使用一种构造机器 348
17.3.19 统一使用一种编译器 348
17.3.20 避免遗留#ifdefs的垃圾代码 348
17.3.21 使用有意义的符号名 349
17.3.22 删除垃圾代码 349
17.3.23 不要复制源文件 350
17.3.24 使用统一的构造系统 350
17.4 对构造系统进行规划充分、人力充足的改进 351
本章小结 352
第18章 管理构造规模 353
18.1 单体模型构造存在的问题 354
18.2 组件式软件 355
18.2.1 使用组件的好处 357
18.2.2 组件到底是什么 358
18.2.3 把多个组件集成到单个产品中 361
18.3 人员和过程管理 364
18.3.1 开发团队的结构 365
18.3.2 组件版本队列管理 367
18.3.3 管理组件缓存 368
18.3.4 协调软件新特性的开发 370
18.4 Apache Ivy 372
本章小结 373
第19章 更快的构造 375
19.1 度量构造系统性能 375
19.1.1 启动阶段的性能度量 375
19.1.2 编译阶段的性能度量 382
19.1.3 性能度量工具 386
19.1.4 修正问题:改进性能 388
19.2 构造减免:消除不必要的重新构造 389
19.2.1 目标文件缓存 389
19.2.2 智能依赖关系 391
19.2.3 构造减免的其他技术方法 395
19.3 并行 396
19.3.1 构造集群/云 396
19.3.2 并行构造工具 397
19.3.3 对可伸缩性的限制 398
19.4 减少磁盘使用 398
本章小结 400
参考文献 401