首页>参考读物>计算机科学与技术>软件与程序设计

深入理解软件构造系统:原理与最佳实践
作者 : (加)Peter Smith 著
译者 : 仲田 等译
出版日期 : 2012-05-23
ISBN : 978-7-111-38226-3
定价 : 89.00元
扩展资源下载
扩展信息
语种 : 简体中文
页数 : 426
开本 : 16
原书名 : Software Build Systems: Principles and Experience
原出版社: Addison-Wesley UK
属性分类: 店面
包含CD :
绝版 : 未绝版
图书简介

构造系统在软件开发过程中处于核心地位,它的正确性和性能,在一定程度上决定了软件开发成果的质量和软件开发过程的效率。本书作者作为一名软件构造系统专家,总结了自己在构造系统开发和维护方面的多年经验,对软件构造系统的原理进行了深入浅出的剖析,并通过各种实际使用场景,对几种最流行的构造工具进行了对比分析,另外还讨论了构造系统的性能优化、规模提升等高级主题。本书分为四部分,涵盖以下内容:第一部分:基础知识,介绍构造系统的概念和相关主题;第二部分:构造工具,结合实际场景案例,对GNU Make、Ant、SCons、CMake和Eclipse IDE这五种构造工具进行分析比较,品评优劣;第三部分:高级主题,对依赖关系、元数据、软件打包与安装、构造机器、工具管理等高级主题进行讨论;第四部分:提升规模,讨论了在大规模构造系统的环境下,如何降低复杂性,提高构造运行速度。
本书适合软件开发相关人员,包含开发人员、项目经理、软件团队负责人、构造专家等阅读。

图书特色

本书深入彻底地解析了软件的构造过程,包括软件构造过程中需要做出的各种选择、可能遇到的各种困难,以及原理与最佳实践。我不仅要向所有的软件构造工程师推荐这本书,还要向所有的软件开发人员推荐,因为软件开发过程中有效性的关键在于具备一套精心设计的构造过程。
—— Kevin Bodie Pitney Bowes公司软件开发总裁
本书向我们清晰地展示了软件构造的原理与细节,内容涵盖构造软件产品需要用到的所有工具和技术,以及要避免的各种错误。无论是构造系统新手,还是经验丰富的构造系统工程师,本书对他们来说都具有足够的吸引力。
—— Monte Davidoff Alluvial软件公司软件开发咨询师

功能欠缺的构造系统可能会对开发人员的生产效率产生巨大的影响。错误的依赖关系、中断的编译错误、失效的软件实体、缓慢的编译速度,以及费时费力的手工处理,这些都是被人诟病的构造系统存在的问题。在本书中,软件生产效率专家Peter Smith向我们展示了如何实现能够解决以上所有问题的构造系统,使我们以更快的速度和更低的成本交付可靠的软件产品。
本书深入解析了高效构造系统背后的核心原理,并调查研究了系统的特性和使用场景。本书是作者多年创建并维护各种构造系统的经验结晶,能帮助我们在选择工具和方法时做出依据充分的决策,并避开常见的陷阱和错误。本书还提供了丰富的实用示例,以及在Java、C、C++和C#等多种环境中总结的经验教训。

本书主要内容:
系统讲解造系统的基础知识,包括源树、构造工具以及编译工具。
比较5种领先的构造工具:GNU Make、Ant、SCons、CMake和Eclipse IDE的集成构造特性。
确保准确进行依赖关系检查,高效进行增量式构造。
使用元数据帮助进行调试、性能分析和为源代码编制文档。
打包软件,以备在目标机器上安装。
包含管理复杂的版本控制系统、构造机器和编译工具的最佳实践。
如果你是一名开发人员,本书将向你展示在构造系统的建设和维护过程中涉及的各种问题,使之最符合团队的需要;如果你是一名管理人员,你会学习如何对团队的构造系统进行评估和效能改进;如果你是一名软件构造专家,无论面临多么严苛的要求,通过学习本书,你都能很好地优化构造系统的性能和可伸缩性。


作者简介










资深软件开发工程师和软件构造系统专家,专注于软件生产效率的探索和研究,对各种新型软件工具的选用与开发、软件项目管理、IT基础设施项目管理、基于软件工具的流程改进,以及如何使企业的现有流程实现自动化等能帮助企业提高软件生产效率的一系列核心问题都有非常深入的认识,实践经验极为丰富。
Peter毕业于哥伦比亚大学,拥有计算机科学博士学位,研究方向是编译器和语言设计。他曾在大学任教,主要教授编译器设计、编程语言设计、软件工程和计算机网络等方面的课程。此外,他还是OOPSLA(面向对象编程、系统、语言与应用)协会的委员。

图书前言

你是软件开发人员吗?你想知道构造系统的工作原理吗?如果你正在阅读这本书,你会对上述两个问题作出肯定的回答。不过,许多软件开发人员却对自己编写的程序是如何编译出来的兴趣寥寥,大多数人只想简单地点击按钮,把源代码转变成可执行程序即可。如果他们需要修复缺陷,只会修改源代码,然后再次点击相同的按钮。他们的乐趣在于看到自己的程序实现所有预期的功能,对他们来说,构造系统只是隐身幕后的一件必要工具而已。
  当源文件的规模稍微大一些,就需要某种形式的自动化构造系统。这种系统可以是个shell脚本,供你在每次源代码修改之后运行;也可以是个makefile,它了解源文件和目标文件之间的关系;或是个更复杂的构造框架,它可以扩展到成千上万个源文件的处理规模。
  如果你曾在UNIX或Windows命令行环境中写过程序代码,那么以下代码看起来就很眼熟:
  本例对5个C语言文件进行编译和链接,创建一个名为sorter的独立可执行程序。对于使用集成开发环境(Integrated Development Environment,IDE)的人来说,以上内容可能比较陌生,但它就是等同于在IDE环境中创建一个含有5个源文件的项目,然后在工具栏点击【build】按钮。
  在进行过几次手工编译程序之后,你可能想把这些命令保存到一个shell脚本中,然后在每次修改代码后重新运行这个脚本。另外,你也可以从命令行历史中检索命令,然后在每次修改代码后再次执行相同的命令。
  如果你基本了解Make工具,就可以创建自己的makefile,并在每次需要重新构造时输入make命令。使用Make的好处是它仅当源文件在上次编译之后发生变化时,才对程序进行重新构造。以下是用来编译sorter程序的简单makefile示例:
  如果你熟悉Make,就会立即看出这不是个编写makefile的好方法。第一个错误是把源文件列出了两次:第一次是用于指定依赖关系,第二次是用在编译命令中。第二个错误是每次重新构造程序时,会把所有源文件都编译一次,即使它们未做任何修改。另外,这里只字未提C文件可能具有头文件依赖关系。
  更好的解决办法是分解编译步骤,使每个源文件的编译和重新编译都与其他文件无关。另外应当建立依赖文件(以.d为扩展名)来跟踪头文件的使用情况。改进建议还有很多,我们与其陷入所有技术细节,不如直接来看满足全部要求的makefile最终版本:

  这就是全部,用最少量的makefile代码,最大限度地减少重复劳动。很简单,是不是?
  不过,如果你是一名开发人员而非构造专家,你真的理解上面的例子吗?经验丰富的Make专家当然理解这些语法,而且会提出更高效的建议来实现相同的效果。但对我们这些只想有个【build】按钮的大多数人来说,第一次总要浪费大量时间才能让makefile正常奏效。
  实现和维护构造系统常常是复杂的。设计得糟糕的构造系统可能会浪费大量时间,如果某个文件应当却未被重新编译的话。当规模上升到成千上万个源文件,开发人员可能会浪费半天时间来跟踪排查问题,最后发现从零开始构造(删除所有目标文件)是唯一的解决办法。看来【build】按钮涉及的工作真不少!
构造系统逐渐复杂的原因
  构造系统会十分复杂并难以维护,这可能会让你感到惊讶。如今图形化用户界面是如此普及,你当然期望构造工具也会同样简单易用。遗憾的是,许多人把创建构造系统看成是一种黑色艺术。只有少数知识渊博的大师,才知道构造工具的完整语法,或依赖关系体系的具体细节。尽管基于IDE的构造工具已解决了部分问题,但它们仍无法承载大规模构造系统的复杂度。
  在大多数情况下,一般软件产品的起始版本都来源于少量的源文件,这些文件被编译和链接形成程序。在这种情况下,一个简单的makefile就足以满足需要,而且这个makefile只需从用户手册的makefile模板中复制一些内容,花几个小时修改一下就可以轻松出炉。在几个月内,除了向其中增加新的源文件和库文件,没有人需要修改这个构造系统。
  再过一段时间,人们开始发现这个构造过程中的问题。例如,当文件需要重新编译时却没有编译;当文件本身或其依赖的数据都没有变化时,文件却被错误地重新编译了;文件可能在一次构造过程中被编译了几遍,导致构造速度很慢。很快,“永远采用‘clean 构造’(即首先删除所有目标文件)”或“修改相关文件迫使其重新编译”等做法,就变成了人们的共识。
  当简单的构造系统变得难以使用,makefile专家就需要反思构造系统的设计,这可能需要创建一个框架来解决所有的构造问题,并把实现细节与最终用户隔离开。例如,软件开发人员想看到的是源文件、库文件、文件中所用编译标志参数等的清单,但对依赖关系是如何管理的却毫无兴趣。例如:

  我们的最终目标是得到一个正确而易用的构造系统,并将所有的复杂性隐藏在framework.mk文件中。对于只想有个【build】按钮的软件开发人员来说,这是个理想的解决方案。
  这种框架式方法在一段时间内很有效果,效率也颇高,但在未来的某个时间就会开始问题频发,对于那些在几年内不断增长的成功软件产品来说尤其如此。这种适用于中小规模产品的构造系统,当产品规模增长时将不再适用。
  考虑一下,如果要对购自第三方供应商的新代码模块进行集成,你会怎么做?新代码已有自己的构造系统,与你原来产品的构造框架并不相同。当开发人员修改代码时,就在新增代码和现有代码库之间建立了相互依赖关系,这就要求构造系统能理解更复杂的文件间关系。最终结果是单方或双方的构造框架需要进行重大修改,甚至可能是推翻重做。
  随着时间的推移,框架也在不断增长,从而使框架的维护工作变得困难重重。在某些情况下,框架的原作者已经离开,无法进行修改,只有让不熟悉的生手做这项工作。缺乏足够构造经验的开发人员常常采用走捷径留后患的方法来构造软件,如后文所述,这些方法包括:写得很糟糕的shell脚本、大量使用符号链接,最糟糕的是源文件重复拷贝。构造过程变得机关重重,没人能够轻而易举地进行维护。
  悲哀的是,许多组织并不觉得需要完善自己的构造系统。如果组织的管理者是其他领域(例如电脑游戏、通信、商业应用等)的专家,他们会将热情全部投注在创造产品上,增加产品的新功能,力图以之打动最终用户。在他们看来,构造系统只是产品生命周期中必要的一环,但完善构造系统并不是自己的责任。这一工作当然也从不出现在公司的企业目标或季度开发计划中。
  如你所见,本书通篇讨论了在设计构造系统时必须考虑的大量问题。这不仅仅是拥有一个随叫随到的makfile大师之类的事,还应当让开发环境保持一种可维护的状态。把钱和时间花在清理构造系统上是值得的,这可以让软件开发团队的生产率提升很多倍。
构造系统的真正成本
  如果你还未相信一套可靠的构造系统的重要性,那就想想真正的成本。也就是说,如果你没有一套良好的构造系统,将会付出多大代价?这种代价并不反映在财务报表上,而是隐藏在软件开发人员日常生产率之中。
  一项行业调查表明(参见参考文献[1]),开发人员因为构造问题导致平均生产率降低12%,而有些被调查对象声称20%~30%也并非罕见。值得注意的是,这项调查关注的是少于20人的小型开发团队,他们一般不会碰到大型软件所遭遇的规模度问题。
  我们先假定在你的团队中,所有软件开发人员在遇到构造系统问题时会损失10%的工时。你对此产生何种反应取决于你以前软件项目的经验。对有些人来说,10%可能是了不得的大问题,而对许多组织来说,10%只是入门级程度。
  这10%的生产率损失在哪里?回想一下你的团队以前几乎肯定碰到过的一些典型问题:
  错误的依赖关系导致编译失败:构造系统获取的依赖关系信息有误,从而导致部分源代码无法正确地重新编译。当发生这种情况时,开发人员会投入所有时间,力图完成一次成功的构造。而构造错误信息不仅晦涩难解,而且与开发人员刚刚修改的代码范围完全无关。如果不解决此类构造问题,他们就无法继续有效开展工作。
  错误的依赖关系导致无效的软件实体:和上面的情况类似,错误的依赖关系导致部分代码编译失常。然而,构造系统并没有给出编译错误,但程序却生成无效的输出结果。这样一来,开发人员和测试人员看到的是代码中问题重重,而他们常常以为是自己的原因,而不是怀疑构造系统。开发人员要浪费一两天的时间来排查测试错误,最后才发现问题并非是自己修改代码造成的。而采用全新的构造树来开始编译,这些问题就会销声匿迹。
  编译速度太慢:大型软件系统更经常遇到这一问题,而小型软件可以在几分钟之内构造完成。如果你的软件代码库需要很多小时的编译时间,那么当开发人员在等待编译完成时,他们的时间就被完全浪费了。这个问题对于增量式构造来说尤其严重,只修改一个源文件,就可能导致5~10分钟的延迟,然后才能重新运行程序。
  你可能会想,在等待编译完成时,开发人员可以做其他工作,这样就更有效率。但情况并不总是这样。开发人员有许多种“等待”活动,例如阅读最新头条新闻、更新社交网站内容、再喝一杯咖啡,或者起身与朋友聊天。但即使开发人员可以在构造过程中进行此类多任务,也会由于在不同任务环境之间切换而带来生产率损耗。开发人员的注意力可能被分散,而完全想不起来他们刚刚在做的事情。
  花时间更新构造描述文件:如果软件构造框架艰深难懂,开发人员就可能需要咨询专家才能进行修改。例如,如果想把新的源文件类型或新的编译工具加入进来,他们必须先与构造专家咨询讨论。有时要等上几天,构造专家才有空提供帮助;然后,构造专家可能要花一两个星期研究才能完成这项工作。
  如果你现在相信了10%的生产率损耗是个实际的数字,那么由此带来的财务成本是多少?对此进行评估的最好办法,是计算出你的公司工资总额支出的10%是多少。如果你是义务编写软件(这在开源世界中很普遍),那么这种评估方法就无效了,但其成本数额同样是所有人关注的主题。
  假定你有10名软件工程师,每人每年的工资总额是7.5万美元。这个额度是偏高还是偏低因城市而异,因此最好根据你自己的实际情况进行评估。会计对此估算结果可能会提出质疑,因为还要考虑额外成本,例如员工医疗福利、电费、房租、停车费,以及开发人员享受的其他专项福利。因此,我们暂且假定每年花在每个开发人员身上的成本是15万美元。
  由此得到开发人员解决构造问题的总成本是:
  10% × 每年15万美元 × 10名开发人员 = 每年15万美元
  这相当于一名全职开发人员一年不干活的成本!假定每年的工作日是250天,那么你的公司仅仅由于构造问题,每天就要付出600美元的代价!
  如果你是一位软件经理,你会想什么办法来提高收益?是继续为团队因为构造问题所浪费的时间每天支出600美元,还是付出一两个月的600美元/天的代价来雇佣一名构造专家,彻底解决此类问题?你自己的组织要选择何种方式,绝对值得认真考虑。请别忘了,公司的利润来自两个方面:要么通过加大产品销售而增加收入,要么通过在起始环节削减产品的生产成本。
本书的重点
  你花时间阅读本书的原因有二:
  为了理解构造系统背后的基本原理:本书对构造系统的特性和应用场景进行了全面彻底的研究,让你可以理解构造工具的工作方式。
  为了获得构造系统的更多经验:本书融汇了作者运用多种不同工具在创建和维护构造系统方面的多年经验,通过阅读本书,你就可以避免以前构造系统开发人员所犯的初级错误。
  有了以上知识,你就可以做出明智的选择,采用何种构造工具,如何建设可靠的构造系统,以及如何预见可能影响生产率的陷阱和错误。从此,软件的构造过程将变得更快、更容易和更可靠。
  还要提醒一下,本书不包括以下内容:
  本书不是操作手册:本书不提供任何特定构造工具或技术的操作手册内容,仅介绍少量示例片断(例如第2章)。流行的构造工具都有各自专门的网站和书籍,为你讲解每项语法和语义的细节。请参阅这些书籍,了解每种构造工具的更详细信息。
  本书不展示构造系统的全套功能:尽管本书包含许多示例,展示如何使用每种构造工具及很多支持工具,但并不从头至尾演示如何创建一个全功能的构造系统。同上,你应当参阅每种构造工具的文档,来查阅完整的创建示例。
  当然,首先应该阅读本书,这样你就能理解每种构造工具的优缺点,从而可以判断出你自己的构造系统应该使用哪些特性。
  本书并未限定于某种开发环境或编程语言,而是从多种不同角度来提供示例并介绍概念:
  C/C++语言构造:这可能是最经典的构造类型了,其构造风格起源于20世纪70年代,到现在一直变化不大。近年来它面临的挑战是文件数量的增长,以及如何处理已用于典型软件产品的第三方程序库。
  Java语言构造:Java语言在20世纪90年代后期逐渐流行,对构造系统的设计产生了相当深远的影响。举个例子,Java的源文件所保存的目录层次结构必须要与软件包的结构相匹配。
  C#语言构造:C语言、C++和Java都是平台无关的编程语言,因此可以用于任何操作系统(例如Linux、Solaris、Mac OS X和Windows),而C#语言的构造环境则更倾向于微软公司的做事方式。
  除了覆盖多种编程语言,本书还讨论了开发大型软件产品的两种不同方式:
  单体构造:此种方式是在一次构造过程中,整个代码库是从源代码编译成一个可执行程序。这是小型程序的常用构造方式,但它的可伸缩性不好,因为采用此种方式来构造大型软件,会产生巨大的源树,消耗极长的编译时间。
  组件构造:与单体构造相反,本方式把源代码划分为多个层级,分别单独编译,最后把各个预构造的组件合并成最终的可执行程序。
  最后提醒一下,本书讲述的内容并不遵循“Make是C/C++开发所选择的主要工具”、“所有Java和C#软件应当在IDE中进行构造”等常规假定。
预期读者
  本书主要面向软件相关开发人员,包含以下几类读者:
  开发人员:如果你是一名有多年源代码开发经验但只有少量构造系统经验的开发人员,可以通过本书学习如何建设和维护构造系统,还可以学习各种用来描述构造过程的工具。
  项目经理:通过本书,你可以学会较高层次的概念和技巧,而无须深入了解太多复杂细节。运用这些知识,你就可以评估团队所做的工作,并向他们提出恰当的“指引方向式”问题。
  构造专家:即使你有建设构造系统的多年经验,也可以从本书学到一些新东西。本书不仅展示了你可能从未用过的现代构造工具,而且关于大型构造系统可伸缩性和性能方面的讨论,会让你在以后编写构造框架时三思而后行。
本书的组织结构
  本书主要分为四个部分,每个部分讨论构造系统的角度略有差异。根据自身经验和所关心的细节层次的不同,你可以选择重点阅读本书的某几部分。新手开发人员应当重点阅读第一部分和第二部分,有经验的读者可以跳过第一部分,重点阅读第二、三、四部分。
第一部分:基础知识
  第一部分面向没有太多构造经验的软件开发人员,对构造系统进行简要介绍。即使有经验的读者也应快速浏览这些章节,以确保对基本概念有个完整的了解。例如, C/C++开发人员可以学到关于C#语言的新知识。
  第1章介绍了构造系统的高层次概念,例如源树和目标树、构造工具、编译工具等。第2章为从未接触过Make的读者提供了如何编写makefile的快速指南。第3章描述了程序在计算机中运行的结构,以便讲解构造系统要生成哪些东西。  第4章对构造过程使用的各种输入输出文件类型进行了详细讲解,并用C/C++、Java和C#语言进行举例。第5章描述了构造变量背后的基本思想,而构造变量本身则在后续章节有更详细的介绍。
  阅读完第一部分,你会对构造系统设计的相关基本概念有充分理解。
第二部分:构造工具
  本书第二部分对5种构造工具进行了研究比较。在现有各种工具中筛选这5种工具时,既要考虑到它们的流行程度,也要考虑到它们各自体现了软件构造的特定方法。每个章节首先介绍构造工具的语法,然后描述该工具的主要应用场景。为了提供有意义的比较结果,所有章节都使用了同一套标准示例。
  第6章讨论了GNU Make工具,它是C/C++开发的最常用工具。第7章研究了Ant工具,它是Java编译的事实标准。第8章研究了新近出现的SCons工具,它采用Python语言来描述构造过程。第9章展示了CMake工具,它根据构造过程的高层次描述,生成一套原生构造系统(例如基于Make的系统)。第10章描述了Eclipse IDE中与构造相关的特性。
  阅读完第二部分,你会了解构造工具的当前状况,并理解每种工具的优缺点。
第三部分:高级主题
  第三部分讨论更高级的构造系统概念,例如依赖关系分析、软件打包与安装、版本管理,以及构造机器和编译工具的管理。这些章节假定你有较复杂软件项目的工作经验,可以理解这里讨论的内容。
  第11章详细探讨了各种依赖关系检查技术,用于判断哪些文件是否必须重新编译。第12章展示了构造系统如何生成元数据来帮助进行调试、性能分析和源代码文档编写。第13章提供了相关示例,展示如何进行软件打包,使之达到可安装到目标机器的状态。第14章研究了与构造系统有关的版本控制方面的问题。第15章提供了关于构造机器管理的最佳经验做法(构造机器是指软件在其上进行编译的机器)。第16章对编译工具进行了类似讨论。
  阅读完第三部分,你会理解关于建设构造系统的许多高级主题,并了解部分最佳经验做法。
第四部分:提升规模
  本书的最后一部分讨论了在构造系统设计时如何适应大型软件产品。随着软件产品的规模不断增长,它会面临可伸缩性问题,例如复杂度增长、磁盘空间占用急剧增多、构造时间也变长了。所有这些问题都会降低软件开发的生产率。
  第17章提出了降低最终用户能感觉到的构造系统复杂度的几种方法。第18章描述了怎样把大型软件产品划分为多个组件,让开发更有效率。最后,第19章讨论了对软件构造时间进行度量和改进的技术方法。
  阅读完第四部分,关于如何设计能够适应规模增长的小型构造系统,你就会有更好的认识。
小结
  建设高质量构造系统不是件容易的事,但如果做不到的话,将导致软件团队产生重大问题。如果源代码应当重新编译却没有进行,你的团队成员将会面临更长的构造时间或无规律的构造失败,他们还可能把时间浪费在对无效软件实体进行调试排错上。因此,投入一定时间来确保构造系统正常工作是非常值得的。
  使用质量低劣的构造系统的真正成本,可以用金钱来衡量。对于一般的软件组织,他们会发现开发人员把10%的时间浪费在解决构造问题上,这也能推算出每年浪费多少钱。
  本书讲解了很多构造系统概念,介绍了一系列常用的构造工具,提供了部分最佳实践做法,并讨论了关于建设与维护大型构造系统方面的问题。
致谢
  如果没有我妻子Grace的鼎力支持,本书不可能完成。许多个夜晚和周末,我都躲在我的“安乐窝”里,敲着键盘,神游物外。Grace理解我写这本书的重要意义(它列在我的遗愿清单上),她的耐心和支持让本书得以面世。还要感谢Stan(我们的马尔济斯比熊犬),它终于体会到坐在地板上通常比坐在我的笔记本电脑或键盘上更舒服。
  感谢我的父母,Sally和Smithy,允许我在他们餐厅的桌上编写几个章节的内容。我还要感谢他们多年来纠正我的拼写和语法错误,让我更轻松地写出本书这么多的内容。
  我要感谢接纳本书予以出版的Pearson Education团队提供的支持,感谢Raina Chrobak、Chris Zahn和Chris Guzikowski在本书的写作和编辑过程中给予的指导,也感谢初稿评审人从实践者或构造系统专家的角度提出的反馈意见,他们是Monte Davidoff、Jeffrey Overbey、J. T. Conklin、Kevin Bodie、Brad Appleton、John Pantone和Usman Muzaffar。
  然后,我还要感谢Kevin Cheek和Bob McLaren,以及爱立信公司团队中的其他人,他们允许我重新协商我的长期合同,让我有足够的时间来写书。还要感谢许多朋友和同事,他们贡献出自己全部的构造系统经验,我希望他们的每条经验都已体现在本书中。
  最后,必须感谢为构造工具的设计和制造作出贡献的每个人。大多数软件项目都使用某种构造工具,这让构造系统成为软件技术的关键环节之一。而创造这些工具的人们,并不总是得到应有的尊重。

专家评论

“本书对软件构造过程进行了彻底而深入的研究,其中包括在精心设计的构造过程中所做出的各种选择,相关的利弊,以及遇到的难点。我不仅要向所有的软件构造工程师推荐这本书,还要向所有的软件开发人员推荐,因为软件开发过程有效性的关键,在于具备一套精心设计的构造过程。”
    —Kevin Bodie,Pitney Bowes公司软件开发主管  

  “在软件开发项目中,构造系统常常不受重视,而它实际上是软件开发项目的重要组成部分之一。本书对构造系统进行了精彩而详尽的解说,其中,仅对构造系统相关生产率所进行的讨论,其价值就足以抵得上为阅读本书所花的时间。”
    —John M. Pantone,Objectech公司副总裁、IT教育者和课程研发人  

  “作者为我们探寻软件构造系统的世界提供了一幅深入浅出的蓝图,其中融汇了他多年的实践经验,涵盖了构造工程师常用的几乎所有类型的工具。本书体裁得当,文字精练,而且相当深入。我向在工作中用到构造系统的所有人推荐这本书。”
    —Jeff Overbey,Photran公司项目副主管  

  “本书指导我们如何看待构造软件,本书涵盖构造软件产品所用的工具和技术,以及我们要避免的各种歧路迷途。对于构造系统新手和经验丰富的构造系统工程师来说,本书都具有足够的吸引力。”
    —Monte Davidoff,Alluvial软件公司软件开发咨询师

上架指导

计算机\软件工程

封底文字

“本书对软件构造过程进行了彻底而深入的研究,其中包括在精心设计的构造过程中所做出的各种选择、相关的利弊,以及遇到的难点。我不仅要向所有的软件构造工程师推荐这本书,还要向所有的软件开发人员推荐,因为软件开发过程有效性的关键,在于具备一套精心设计的构造过程。”

——Kevin Bodie,Pitney Bowes公司软件开发总裁


功能欠缺的构造系统对开发人员的生产率可能造成巨大影响。错误的依赖关系、中断的编译错误、失效的软件实体、缓慢的编译速度,以及费时费力的手工处理,这些都是不入流的构造系统带来的问题。在本书中,软件生产率专家Peter Smith向你展示如何实现能够解决以上所有问题的构造系统,使你可以速度更快、成本更低地交付可靠的软件产品。
作者解析了高效构造系统背后的核心原理,对系统特性和使用场景进行了调查研究。然后,他把自己创建并维护各式构造系统的多年经验融汇进来,帮助你在选择工具和方法时做出依据充分的决策,并避开常见的陷阱和错误。本书介绍了丰富的实用示例,以及Java、C++、C和C#等多种环境中总结的经验教训,此类经验分享贯穿全书。
本书主要内容:
•掌握构造系统的概念,包括源树、构造工具以及编译工具。
•比较5种领先的构造工具:GNU Make、Ant、SCons、CMake和Eclipse IDE的集成构造特性。
•确保准确进行依赖关系检查,高效进行增量式构造。
•使用元数据帮助进行调试、性能分析和为源代码编制文档。
•打包软件,以备在目标机器上安装。
•关于管理复杂的版本控制系统、构造机器和编译工具的最佳实践做法。
如果你是一名开发人员,本书将向你展示在构造系统的建设和维护过程中涉及的各种问题,使之最符合团队的需要;如果你是一名管理人员,你会学习如何对团队的构造系统进行评估和效能改进;如果你是一名软件构造专家,你会学到无论面临多么严苛的要求,如何优化构造系统的性能和可伸缩性。

作者简介

(加)Peter Smith 著:Peter Smith是Arapiki Solutions公司的合伙人,该公司为软件开发生产率的改进提供技术领先的新工具和新方法引入。他擅长软件开发基础设施的规划与部署,并采用最佳实践做法进行优化。

译者简介

仲田 等译:暂无简介

译者序

软件构造是大多数开发人员熟知的、软件开发过程中不可缺少的一个工作环节。在这一过程中,软件构造系统借助各种工具,将软件从源代码形式,通过编译、链接等处理步骤,转换成最终可执行程序形式。一般开发人员只了解软件构造系统的基本概念和常规操作方法,很少有人深究软件构造系统背后的工作原理和技术细节。
  本书作者是一名软件构造系统专家,他融汇自己在构造系统开发和维护方面的多年经验,对软件构造系统的原理进行了深入浅出的剖析,并通过各种实际使用场景,对几种最流行的构造工具进行了对比分析,另外还讨论了构造系统的性能优化、规模提升等高级主题。
  本书分为四部分,共19章,涵盖以下内容:
  第一部分: 基础知识,介绍构造系统的概念和相关主题。
  第二部分: 构造工具,结合实际场景案例,对GNU Make、Ant、SCons、CMake和Eclipse IDE这五种构造工具进行分析比较,品评优劣。
  第三部分: 高级主题,对依赖关系、元数据、软件打包与安装、构造机器、工具管理等高级主题进行讨论。
  第四部分: 提升规模,讨论了在大规模构造系统的环境下,如何降低复杂性,提高构造运行速度。
  译者来自软件开发行业,虽有多年从业经验,使用过多种开发语言,但对构造系统涉猎不深,平常只满足于普通操作使用,很少深究背后的原理。通过翻译本书,对构造系统也有了更全面、更深入的理解,深感构造系统虽不起眼,但却在软件开发过程中处于核心地位,它的正确性和性能,在一定程度上决定了软件开发成果的质量和软件开发过程的效率。
  本书的翻译得到了机械工业出版社陈冀康编辑的大力支持,在此深表感谢。另外,张建炜、吴畏、汪燕、张贺、张鄂军也参与了本书的翻译工作,在此一并表示感谢。
  由于译者水平有限,译文中如有错漏之处,恳请读者不吝批评指正。

图书目录

对本书的赞誉
译者序
前 言
致 谢
作者介绍
第一部分 基础知识
第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 创建一个简单的makefile17
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 Java43
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章 Make79
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 Make110
6.4.2 NMake111
6.4.3 ElectricAccelerator和Spark Build111
本章小结113
第7章 Ant115
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 NAnt144
7.4.2 MS Build145
本章小结146
第8章 SCons147
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 Cons173
8.4.2 Rake174
本章小结176
第9章 CMake177
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 Automake196
9.4.2 Qmake197
本章小结197
第10章 Eclipse199
10.1 Eclipse的概念和GUI199
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 Autoconf315
15.4.1 高层次工作流315
15.4.2 Autoconf示例317
15.4.3 运行autoheader和autoconf319
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 构造工具示例:Maven337
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 Ivy372
本章小结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

教学资源推荐
作者: 朱鸣华,刘旭,麟杨微,罗晓芳,李慧,孙大为,赵晶
作者: 郑阿奇
参考读物推荐
作者: (美)Gary Gruver, Mike Young, Pat Fulghum 著
作者: (美) William F. Punch Richard Enbody 著
作者: [美]乔纳森·E.斯坦哈特 (Jonathan E. Steinhart ) 著