我的一些同事确信将构建工件提交到subversion存储库是个好主意.这样的论点就是这样,测试机器上的安装和更新很容易 - 只需"svn up"!
我敢肯定有一些重要的论据反对这种不好的做法,但我能想到的都是蹩脚的,比如"它需要更多的空间".没有做到这一点的最佳原因是什么?我们应该采取哪些其他方法呢?
这适用于Java代码,如果这会产生影响.一切都是从Eclipse编译的(没有自动PDE构建).
当我说添加构建工件时,我的意思是提交看起来像这样:
"Added the new Whizbang feature" M src/foo/bar/Foo.java M bin/Foo.jar
每个代码更改都有相应的生成的jar文件.
在我看来,代码存储库应该只包含源代码以及编译此源代码所需的第三方库(在构建过程中也可以使用某些依赖管理工具检索第三方库).生成的二进制文件不应与源代码一起签入.
我认为你的问题是你没有适当的构建脚本.这就是为什么从源代码构建二进制文件涉及一些工作,如启动eclipse,导入项目,调整classpathes等...
如果有适当的构建脚本,可以使用以下命令来获取二进制文件:
svn update; ant dist
我认为不检查二进制文件和源代码的最重要原因是存储库的最终大小.这将导致:
版本控制系统服务器上的存储库更大,空间可能太少
版本控制系统服务器和客户端之间的流量很大
更新时间更长(假设您从互联网上进行SVN更新...)
另一个原因可能是:
源代码很容易比较,因此版本控制系统的许多功能都很有意义.但你不能轻易比较二进制文件......
此外,您的方法如上所述在我看来引入了很多开销.如果开发人员忘记更新相应的jar文件怎么办?
首先,Subversion(以及现在所有其他人)不是源代码控制管理器(我一直认为SCM意味着软件配置管理),而是版本控制系统.这意味着它们存储对存储在其中的东西的更改,它不一定是源代码,它可以是图像文件,位图资源,配置文件(文本或xml),各种东西.建立二进制文件不应被视为此列表的一部分只有一个原因,那是因为您可以重建它们.
但是,请考虑为什么要将已发布的二进制文件存储在那里.
首先,它是一个帮助您的系统,而不是告诉您应该如何构建应用程序.让计算机为您工作,而不是对您.那么如果存储二进制文件占用空间怎么办呢 - 你有数百GB的磁盘空间和超快的网络.将二进制对象存储在那里并不是什么大问题(而十年前它可能是一个问题 - 这也许是人们认为SCM中的二进制文件是一种不好的做法).
其次,作为开发人员,您可能习惯使用系统来重建任何版本的应用程序,但其他可能使用它的人(例如qa,test,support)可能不会.这意味着您需要一个替代系统来存储二进制文件,实际上,您已经拥有了这样一个系统,它就是您的SCM!利用它.
第三,您假设您可以从源重建.显然,您将所有源代码存储在那里,但是您不存储编译器,库,sdks以及所需的所有其他相关位.当有人出现时会发生什么,并问"你可以建立我2年前发布的版本,客户对该版本有问题".2年是如今的永恒,你甚至拥有当时使用的相同编译器吗?当您检查所有源代码时发现新更新的sdk与您的源不兼容并且失败并出现错误会发生什么?您是否擦除了开发框并重新安装所有依赖项以构建此应用程序?你能记住所有的依赖关系吗?!
最后一点是最重要的一点,为了节省几k的磁盘空间,如果不是数周的痛苦,你可能会花费几天时间.(并且索德定律还说,无论你需要重建哪个应用程序都需要最晦涩,难以设置的依赖,你很高兴摆脱它)
因此,将二进制文件存储在SCM中,不要担心琐事.
PS.我们将每个二进制文件都放在每个项目的"release"目录中,然后当我们想要更新一台机器时,我们使用一个特殊的"setup"项目,除了svn:externals之外什么都没有.您导出安装项目并完成它,因为它获取正确的东西并将它们放入正确的目录结构中.
像Hudson这样的持续集成服务器可以存档构建工件.它没有帮助你的论证"为什么不",但至少它是另一种选择.
我敢肯定,有很多反对这种不良做法的论据
您有错误的假设,即将"构建工件"提交给版本控制是一个坏主意(除非您错误地表达了您的问题).它不是.
确实非常重要的是,在版本控制中保留你所谓的"构建工件".除此之外,您还应该保留编译器和其他任何用于将源文件集转换为成品的其他内容.
从现在开始的五年内,您肯定会使用不同的编译器和不同的构建环境,无论出于何种原因,可能无法编译今天的项目版本.修复遗留版本中的错误可能是一个简单的小改动,将变成将旧软件移植到当前编译器和构建工具的噩梦,只需重新编译具有单行更改的源文件.
所以,你没有理由害怕在版本控制中存储"构建工件".您可能想要做的是将它们放在不同的地方.
我建议将它们分开:
ProjectName |--- /trunk | |--- /build | | |--- /bin <-- compilers go here | | |--- /lib <-- libraries (*.dll, *.jar) go here | | '--- /object <-- object files (*.class, *.jar) go here | '--- /source <-- sources (*.java) go here | |--- package1 <-- sources (*.java) go here | |--- package2 <-- sources (*.java) go here
您必须配置IDE或构建脚本以将对象文件放在/ ProjectName/trunk/build/object中(甚至可能在.../source下重新创建目录结构).
这样,您可以让用户选择签出/ ProjectName/trunk以获取完整的构建环境,或者/ ProjectName/trunk/source来获取应用程序的源.
在../build/bin和../build/lib中,您必须放置用于编译最终产品的编译器和库,用于将软件发送给用户的编译器和库.在5年或10年内,您将拥有它们,可供您在某些情况下使用.
如果你知道为什么, "将构建工件提交到subversion存储库"可能是一个好主意.
对于发布管理目的而言,这是一个好主意,更具体地说是:
如果构建工件不仅仅是exe(或dll或...),还包括:
一些配置文件
一些脚本来启动/停止/重启你的工件
一些sql来更新你的数据库
一些来源(压缩成文件)以方便调试
一些文档(javadoc在文件中压缩)
那么将构建工件和所有相关文件存储在VCS中是个好主意.
(因为它不再只是"重建"工件,而是"检索"所有那些将使该工件运行的额外文件)
假设您需要在不同的环境中部署许多工件(测试,认证,预生产,生产).
如果:
你会产生很多构建工件
那些工件很难从头开始重新创建
然后在VCS中拥有这些工件是个好主意,以避免重新创建它们.
您可以从环境到环境查询它们.
但你需要记住:
1 /您无法存储您在VCS中创建的每个工件:您为持续集成目的而构建的所有中间构建都不能存储在VCS中(或者您最终会得到一个包含许多无用版本的二进制文件的大型存储库).
只需要引用认证和生产目的所需的版本.
对于中间构建,您需要一个外部存储库(maven或共享目录),以便快速发布/测试这些构建.
2 /您不应该将它们存储在同一个Subversion存储库中,因为您的开发承诺(修订版号)比您的重要版本(认为值得认证和生产部署的版本)更频繁.
这意味着存储在第二个存储库中的工件必须具有标记(或属性)的命名约定,以便轻松检索构建它们的开发的修订版号.