我应该在源代码管理中存储生成的代码

时间:2009-05-21 16:53:17

标签: version-control code-generation

这是我正在参与的一场辩论。我希望得到更多的意见和观点。

我们有一些在构建时生成的类来处理数据库操作(在这种特殊情况下,使用SubSonic,但我不认为这对于这个问题非常重要)。生成设置为Visual Studio中的预构建步骤。因此,每次开发人员(或官方构建过程)运行构建时,都会生成这些类,然后将其编译到项目中。

现在有些人声称,将这些类保存在源代码管理中可能会导致混淆,以防您获得的代码与您自己环境中生成的代码不匹配。

我希望有一种方法可以追溯代码的历史记录,即使它通常被视为黑盒子。

任何论据或反驳论点?

<小时/> 更新:我问过这个问题,因为我真的相信有一个明确的答案。看看所有回复,我可以高度肯定地说,没有这样的答案。应根据多个参数做出决定。阅读下面的答案可以为您在决定此问题时应该问自己的问题类型提供一个非常好的指导。

由于上述原因,此时我不会选择接受的答案。

27 个答案:

答案 0 :(得分:49)

将其保存在源代码管理中比它的价值更麻烦。

每次进行构建时都必须提交一个提交,以使其成为任何值。

通常我们将生成的代码(idl,jaxb等等)留在我工作的源代码控制之外,这从来就不是问题

答案 1 :(得分:30)

每当我想在我自己的个人仓库中显示对源树的更改时,所有“生成的文件”都会显示为已更改并需要进行评估。

我希望有一个更清晰的修改列表,其中只包含已执行的实际更新,而不是自动生成的更改。

将它们删除,然后在构建之后,在每个生成的文件上添加“忽略”。

答案 2 :(得分:27)

将其放入源代码控制中。拥有您为未来开发人员编写的所有内容的历史记录的优势超过了同步后偶尔重建的轻微痛苦。

答案 3 :(得分:24)

以这种方式看待它:你检查你的目标文件到源代码管理?生成的源文件是构建工件,就像目标文件,库和可执行文件一样。它们应该被对待。大多数人认为你不应该将生成的目标文件和可执行文件检查到源代码控制中。相同的参数适用于生成的源。

如果您需要查看生成文件的历史版本,可以同步到其源的历史版本并重建。

将生成的任何类型的文件检入源代码控制类似于数据库非规范化。 偶尔有理由这样做(通常用于性能),但这应该非常谨慎,因为一旦数据非规范化,维护正确性和一致性变得更加困难。

答案 4 :(得分:19)

我想说你应该避免将任何生成的代码(或其他工件)添加到源代码控制中。如果生成的代码对于给定的输入是相同的,那么您可以查看要扩散的版本并生成用于比较的代码。

答案 5 :(得分:16)

我称之为DRY原则。如果您已经在存储库中拥有用于在构建时生成这些代码文件的“源文件”,则不需要“两次”提交相同的代码。

此外,如果例如某天代码生成中断,您可以通过这种方式避免一些问题。

答案 6 :(得分:14)

我真的不认为你应该检查它们。

当然,生成的代码中的任何变化要么是噪声 - 环境之间的变化,要么是由于其他原因而发生的变化 - 例如数据库中的更改。如果您的数据库的创建脚本(或任何其他依赖项)在源代码控制中,那么为什么还需要生成的脚本呢?

答案 7 :(得分:13)

不,有三个原因。

  1. 源代码是在当前或以前的某个时间点重现应用程序快照所必需且足够的一切 - 仅此而已。这意味着有人负责检查所有内容。通常我很乐意对我编写的代码负责,但不会因为我写的内容而生成代码。

  2. 我不希望有人试图通过使用可能是也可能不是最新的中间代码来尝试从主要来源快捷构建构建(更重要的是我不想承担责任。对于某些人而言,如果基于部分构建调试中间代码中的冲突,就会陷入毫无意义的过程中,这太诱人了。

  3. 一旦进入源代码管理,我就要承担责任。它在那里,b。它是最新的,和c。它可以与那里的其他一切可靠地融为一体。这包括在我不再使用它时删除它。责任越少越好。

答案 8 :(得分:8)

一般规则是,但如果生成代码需要时间(因为数据库访问,Web服务等),那么您可能希望在源代码管理中保存缓存版本并拯救每个人的痛苦。

您的工具还需要注意这一点,并在需要时处理来自源控件的检出,太多工具决定无任何理由从源控件中检出。
一个好的工具将使用缓存版本而不触及它(也不修改文件的时间步长)。

此外,您需要在生成的代码中添加大警告,以便人们不修改文件,顶部的警告是不够的,您必须每十几行重复一次。

答案 9 :(得分:6)

我们不存储生成的数据库代码:因为它是生成的,所以您可以从源文件中的任何给定版本随意获取它。存储它就像存储字节码等。

现在,您需要确保在给定版本中使用的代码生成器可用!较新的版本可以生成不同的代码...

答案 10 :(得分:5)

不管它。

如果您正在检入生成的文件,那么您做错了什么。可能有什么不同可能会有所不同,可能是您的构建过程效率低下,或其他什么,但我无法看到它永远是个好主意。历史记录应该与源文件相关联,而不是与生成的文件相关联。

对于那些最终尝试解决差异,找到不再由构建生成的文件然后删除它们等的人来说,这只会让人头疼。

等待签入生成文件的人会有痛苦的世界!

答案 11 :(得分:4)

迟到了......反正......

您是否将编译器的中间文件放入源版本控制? 在代码生成的情况下,根据定义,源代码是生成器的输入,而生成的代码可以被视为“真实”源和构建应用程序之间的中间文件。

所以我要说:不要将生成的代码置于版本控制之下,而是生成器及其输入。

具体来说,我使用的是我编写的代码生成器:我从来不必在版本控制下维护生成的源代码。我甚至会说,由于生成器达到了一定的成熟度级别,我不必观察生成代码的内容,尽管输入(例如模型描述)已经改变。

答案 12 :(得分:4)

有一种特殊情况,您要检入生成的文件:何时需要在用于生成其他文件的工具不可用的系统上构建。这个和我合作的经典例子是Lex和Yacc代码。因为我们开发了一个必须在各种各样的平台和体系结构上构建和运行的运行时系统,所以我们只能依靠目标系统来拥有C和C ++编译器,而不是为我们的接口定义生成lexing / parsing代码所需的工具。翻译。因此,当我们更改语法时,我们会检查生成的代码来解析它。

答案 13 :(得分:3)

在某些项目中,我将生成的代码添加到源代码控制中,但它确实取决于。我的基本准则是如果生成的代码是编译器的固有部分,那么我将不会添加它。如果生成的代码来自外部工具,例如本例中的SubSonic,那么我将添加if来源控件。如果您定期升级组件,那么我想知道生成的源中的更改,以防出现错误或问题。

就需要签入的生成代码而言,最坏的情况是手动区分文件并在必要时还原文件。如果你正在使用svn,你可以在svn中添加一个预提交钩子,如果文件没有真正改变,则拒绝提交。

答案 14 :(得分:2)

双方都有充分合理的论据,很难就共同点达成一致。版本控制系统(VCS)跟踪文件 开发人员加入它,并假设VCS中的文件是由开发人员手工制作的,开发人员对历史感兴趣 并在文件的任何修订之间进行更改。这个假设使两个概念相等,“我想在结帐时获取此文件。”和“我是 对此文件的更改感兴趣。“

现在,双方的论点可以改为:

  • “我想在结账时获取所有这些生成的文件,因为我没有在这台机器上生成它们的工具。”
  • “我不应该把它们放到VCS中,因为我对这个文件的更改不感兴趣。”

幸运的是,似乎这两个要求并没有从根本上相互冲突。随着当前VCS的一些扩展,它应该是可能的 都。换句话说,这是一个虚假的困境。如果我们思考一段时间,就不难发现问题源于VCS所持有的假设。 VCSS 应该区分由开发人员手工制作的文件,而不是由开发人员手工制作的文件,但恰好在里面 这个VCS。对于第一类文件,我们通常称之为源文件(代码),VCS现在已经做得很好。对于后一类,VCS有 据我所知,还没有这样的概念。

摘要

我将把git作为一个例子来说明我的意思。

  • git status默认情况下不应显示生成的文件。
  • git commit应将生成的文件包含为快照。
  • git diff默认情况下不应显示生成的文件。

PS

Git钩子可以用作解决方法,但如果git本身支持它会很棒。 gitignore不符合我们的要求,因为被忽略了 文件不会进入VCS。enter code here

答案 15 :(得分:2)

这里提出了赞成和反对的好论据。 为了记录,我在Visual Studio中构建了T4生成系统,我们的默认开箱即用选项会导致生成的代码被签入。如果您不想签入,则必须更加努力。

对我来说,关键考虑因素是在输入或生成器本身更新时区分生成的输出。

如果您没有签入输出,则必须在升级生成器或修改输入之前复制所有生成的代码,以便能够将其与新版本的输出进行比较。我认为这是一个相当繁琐的过程,但是通过检查输出,将新输出与存储库区分开来是一件简单的事。

此时,有理由问“你为什么关心生成代码的变化?” (特别是与目标代码相比。) 我认为有几个关键原因归结为当前的技术水平,而不是任何固有的问题。

  1. 您可以制作与生成的代码紧密相关的手写代码。这些天整体上并不是obj文件的情况。当生成的代码发生变化时,很可能经常出现一些手写代码需要更改才能匹配的情况。人们通常不会在生成的代码中观察到与扩展点的高度向后兼容性。

  2. 生成的代码只是改变了它的行为。您不会从编译器中容忍这一点,但公平地说,应用程序级代码生成器针对不同的问题领域,提供更广泛的可接受解决方案。重要的是要看看你对先前行为做出的假设是否已被打破。

  3. 您只是不会100%信任您的发电机从发布到发布的输出。即使它们不是由编译器供应商的严格构建和维护,生成器工具也有很多价值。 1.0版本可能对您的应用程序来说非常稳定,但1.1可能会为您的用例带来一些小问题。或者,您可以更改输入值,并发现您正在使用之前未使用的新生成器 - 可能会对结果感到惊讶。

  4. 基本上所有这些都归结为工具的成熟度 - 大多数业务应用程序代码生成器并不接近编译器甚至lex / yacc级工具多年来的水平。

答案 16 :(得分:2)

正确答案是“它取决于”。这取决于客户的需求。 如果您可以将代码回滚到特定版本并在没有它的情况下经受任何外部审计,那么您仍然没有坚定的基础。作为开发者,我们不仅要考虑“噪音”,痛苦和磁盘空间,还要考虑我们的任务是产生知识产权的角色,并且可能存在法律后果。您是否能够向法官证明您能够以两年前客户看到的方式重新生成网站?

我不是建议您保存或不保存gen'd文件,无论您决定是否未涉及主题事项专家,您可能错误的决定。

我的两分钱。

答案 17 :(得分:2)

这取决于。最终,目标是能够在需要时再现您所拥有的东西。如果您能够完全重新生成二进制文件,则无需存储它们。但你需要记住,为了重新创建你的东西​​,你可能需要你首先完成的确切配置,这不仅意味着你的源代码,还意味着你的构建环境,你的IDE,甚至其他库,您使用的确切配置(版本),生成器或东西。

我在项目中遇到了麻烦,我们将构建环境升级到新版本甚至是其他供应商,我们无法重新创建之前的二进制文件。当要使用的二进制文件依赖于某种哈希时,这是一个真正的痛苦,特别是在安全的环境中,并且由于编译器升级或其他原因,重新创建的文件在某种程度上有所不同。

那么,你会存储生成的代码:我会说不。发布的二进制文件或可交付成果,包括您存储的工具。然后,不需要将它们存储在源代码控制中,只需对这些文件进行良好的备份。

答案 18 :(得分:2)

配置管理(版本控制只是其中一部分)的工作是能够执行以下操作:

  • 了解每个交付的构建中已经进行了哪些更改和错误修复。
  • 从原始源代码开始,能够准确再现任何交付的构建。无论语言如何,自动生成的代码都不算作“源代码”。

第一个确保当您告诉客户或最终用户“您上周报告的错误已修复且新功能已添加”时,他们两小时后才回来并说“不是没有”。它还确保他们不会说“为什么它在做X?我们从未要求X”。

第二个意味着当客户端或最终用户报告您在一年前发布的某个版本中的错误时,您可以回到该版本,重现该错误,修复它,并证明您的修复已经消除了错误,而不是编译器和其他修复程序的一些扰动。

这意味着您的编译器,库等也需要成为CM的一部分。

现在回答你的问题:如果你可以做到以上所有,那么你不需要记录任何中间表示,因为你保证得到相同的答案。如果你不能做到以上所有,那么所有的赌注都是关闭的,因为你永远不能保证两次做同样的事情并得到相同的答案。因此,您也可以将所有.o文件置于版本控制之下。

答案 19 :(得分:1)

我会争辩。如果您正在使用检查代码的持续集成过程,修改构建号,构建软件然后对其进行测试,那么将该代码作为存储库的一部分就更简单,更容易。

此外,它是您对软件存储库的每个“快照”的一部分。如果它是软件的一部分,那么它应该是存储库的一部分。

答案 20 :(得分:1)

由于许多原因,绝对在源代码管理中生成了代码。我重申了许多人已经说过的话,但我做的一些原因是

  1. 使用源代码控制中的代码文件,您可以在不使用Visual Studio预构建步骤的情况下编译代码。
  2. 当你在两个版本之间进行完全比较时,很高兴知道生成的代码是否在这两个标签之间发生了变化,而不必手动检查它。
  3. 如果代码生成器本身发生更改,那么您需要确保对生成的代码所做的更改会发生相应的更改。即如果您的生成器发生了变化,但输出不应该改变,那么当您提交代码时,之前生成的内容与现在生成的代码中的内容之间没有区别。

答案 21 :(得分:1)

我(遗憾地)最终将大量衍生资源置于源代码管理之下,因为我与那些无法建立适当构建环境或无法设置其技能的人远程工作这样派生的资源就完全正确。 (当谈到Gnu autotools时,我自己就是那些人之一!我无法使用三个不同的系统,每个系统都使用不同版本的autotools - 只有那个版本。)

这种困难可能更多地适用于兼职,志愿者,开源项目,而不是付费项目,其中支付账单的人可以坚持统一的构建环境。

执行此操作时,您基本上只承诺在一个站点或仅在正确配置的站点上构建派生文件。您的Makefile(或其他)应该设置为注意它们的运行位置,并且应该拒绝重新获取源,除非它们知道它们在安全的构建站点上运行。

答案 22 :(得分:1)

通常,生成的代码不需要存储在源代码控制中,因为此代码的修订历史记录可以通过生成它的代码的修订历史记录来跟踪!

然而,听起来OP使用生成的代码作为应用程序的数据访问层而不是手动编写一个。在这种情况下,我将更改构建过程,并将代码提交给源代码控制,因为它是运行时代码的关键组件。如果开发人员需要为不同的分支使用不同版本的工具,这也会消除构建过程中对代码生成工具的依赖。

似乎代码只需要生成一次而不是每次构建。当开发人员需要添加/删除/更改对象访问数据库的方式时,应该再次生成代码,就像进行手动修改一样。这加快了构建过程,允许对数据访问层进行手动优化,并以简单的方式保留数据访问层的历史记录。

答案 23 :(得分:1)

我会说是的,你想把它置于源代码控制之下。从配置管理的角度来看,需要控制用于生成软件构建的所有内容,以便可以重新创建它。我理解生成的代码可以很容易地重新创建,但可以说它不一样,因为日期/时间戳在两个构建之间会有所不同。在政府等一些领域,他们需要很多时间才能做到这一点。

答案 24 :(得分:0)

看起来双方都有非常强烈和令人信服的意见。我建议阅读所有最高投票的答案,然后决定哪些参数适用于您的具体案例。

更新:我问过这个问题,因为我真的相信有一个确定的答案。看看所有回复,我可以高度肯定地说,没有这样的答案。应根据多个参数做出决定。阅读其他答案可以为您在决定此问题时应该问自己的问题提供一个非常好的指导。

答案 25 :(得分:0)

如果它是源代码的一部分,则应将其置于源代码管理中,无论是谁或是什么生成它。您希望源代码控制反映系统的当前状态,而不必重新生成它。

答案 26 :(得分:0)

我会保留源树的生成文件 out ,但将放在单独的构建树中。

e.g。工作流程

  1. 正常签入/退出/修改/合并源(没有任何生成的文件)
  2. 在适当的时候,将源代码树签出到一个干净的构建树
  3. 构建之后,检查所有必须出于审计/监管目的的“重要”文件(“真实”源文件,可执行文件+生成的源文件)。这将为您提供所有适当生成的代码+可执行文件+的任何历史记录,以及与发布/测试快照等相关的时间增量,并与日常开发分离。
  4. Subversion / Mercurial / Git / etc可能有很好的方法将两个地方的真实源文件的历史记录结合在一起。