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


106

这是我参加的辩论。我想征询更多意见和观点。

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

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

我希望有一种方法可以追溯代码的历史记录,即使通常将其视为黑匣子也是如此。

有参数还是反参数?


更新:我问了这个问题,因为我真的相信有一个明确的答案。查看所有答复,我可以肯定地说,没有这样的答案。该决定应基于多个参数来做出。阅读以下答案可以为您在决定该问题时应该问自己的问题类型提供很好的指导。

由于上述原因,我目前不会选择可接受的答案。


1
您可能对类似的问题感兴趣:stackoverflow.com/questions/739391/…–
mouviciel

我想说的是,在SubSonic的情况下,保持源代码管理作为一种轻松跟踪(某些)数据库更改的方式可能很有趣,以防万一您没有其他方法来跟踪数据库的历史记录。您的数据库。
Earlz 2011年

1
在我看来,主要的问题是生成类时,不同的开发人员不会得到相同的结果。用于生成它们的配置应检入,并在所有开发人员环境中提供一致的构建。
nawroth 2012年

1
不知道该怎么做,但是我认为这个问题现在应该关闭,因为它太开放了,无法与特定的源代码管理系统或特定类型的生成文件紧密地联系在一起,以进行讨论和讨论。
克里斯·哈克罗

这是一个很大的问题,但是对于SO来说,它也是基于观点的,正如多重矛盾的答案所表明的那样,OP对此也发表了自己的评论。
Flimzy

Answers:


48

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

每次进行构建时,都必须进行一次提交才能使其具有任何值。

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


43
我不同意“每次构建时都必须进行提交”。这不会引起额外的提交,因为唯一会影响提交的是更改代码,从而更改了生成的源。因此,实际上,只有在已经将更改提交给生成的代码的源时,才必须提交生成的代码。
JaredPar

5
同意JaredPar。同样,您的代码生成器可能是一个外部工具,如果您对其进行更新,则生成的代码可能会更改,因此您可能需要提交更改。但是在这种情况下,我还是很想看看源代码控制的变化。
面包车

不同的工具可能会产生不同的来源(至少它们的注释或代码格式可能有所不同)。例如,Idea添加了“由想法生成”注释,而Eclipse没有。
Petr Gladkikh 2013年

1
意见分歧,将这一点标记为答案会传达错误的信息。
浓缩咖啡'18

34

将其放在源代码控制中。使您编写的所有内容的历史记录可供将来的开发人员使用的好处,胜过在同步后偶尔进行重建的轻微麻烦。


18
那不是优势-因为创建它的代码已经签入,所以您已经拥有了“所有编写内容”,可供将来的开发人员使用。
谢恩·梅森

13
@Shane,我强烈不同意。拥有创建它的代码并不等于拥有代码。跟踪错误时,生成时必须包括的任何其他步骤都会带来额外的麻烦。遍历代码的历史要比检出文件的N个版本并重新生成所生成的代码的N个版本要简单得多。
JaredPar

10
将生成的文件放在源代码管理中有时是有益的。例如,如果升级组件(在本例中为SubSonic),则可以轻松检测到所生成源中的更改。这对于跟踪错误和问题可能很有用。我不会将所有生成的代码添加到源代码管理中。有时它非常有用。大多数源代码管理系统都可以让您进行比较,以查看文件是否确实更改,尽管如果您必须手动还原文件(即使唯一的更改是时间戳记),这可能更多是手动过程。
瑞安

18
按照这种逻辑,您还应该检入已编译的目标文件,库和可执行文件。
劳伦斯·贡萨尔维斯

9
您在“原始语言毫无意义”的地方使用哪种代码生成器?关于跟踪用于构建每个版本代码的工具版本的要点,您已经需要为整个工具链解决该问题。毕竟,除非您知道当时使用的是哪个版本的编译器和链接器,否则您如何期望将bug移植到产品的较早版本?代码生成器与C ++ / Java / C#编译器没有什么不同。您可能能够读取其输出的事实并不重要:它的输入就是来源。
劳伦斯·贡萨尔维斯

31

每当我想在自己的个人存储库上显示对源树的更改时,所有“生成的文件”都会显示为已更改,需要进行调试。

我希望有一个更干净的修改列表,其中仅包括已执行的实际更新,而不包括自动生成的更改。

保留它们,然后在构建后,在每个生成的文件上添加“忽略”。


3
另外,在更新时,您可能会遇到一些奇怪的冲突,VCS会认为这些冲突需要解决,但在下次构建时实际上会自行解决。更不用说日志中的混乱了,我认为这甚至比本地树中的混乱还糟。
rmeador

5
我所处的位置,除非它们确实已更改,否则它们不会显示为“已更改”。如果它们已重新生成但仍具有相同的内容,那么唯一不同的是文件创建/修改日期,系统认为它们没有更改,一切都很好。
乔尔·科洪

+1我只想对我编写的代码负责,而不是由某些工具包生成的某些代码负责,这些代码可能在当时无法复制(但是有人可能会花费大量时间进行尝试)
dkretz 09年

4
我见过自动生成工具,它们每次运行时都会更新时间戳。我诅咒他们。
基辅利

25

这样看:您是否将目标文件签入源代码管理中?生成的源文件是构建工件,就像目标文件,库和可执行文件一样。它们应该被相同地对待。大多数人认为您不应该将生成的目标文件和可执行文件检入源代码管理中。相同的参数适用于生成的源。

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

将生成的各种文件检查到源代码管理中都类似于数据库非规范化。有偶尔的理由这样做(通常用于性能),但这应该只是非常小心,因为它变得更加困难,一旦数据被规格化,以保持正确性和一致性来完成。


20

我要说的是,您应该避免将任何生成的代码(或其他工件)添加到源代码管理中。如果生成的代码对于给定的输入是相同的,那么您只需检查要比较的版本并生成代码进行比较即可。


1
仅供参考,我在这里共享了一个脚本来进行比较:stackoverflow.com/a/16754923/105137
kostmo,

17

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

另外,例如,如果某天代码生成中断,您可能会用这种方式避免一些问题。


15

不,出于三个原因。

  1. 源代码是重现应用程序快照的所有必要和充分的条件,这些快照应包含当前或先前的某个时间点-仅此而已。这意味着部分原因是某人应对签入的所有内容负责。通常,我很高兴对自己编写的代码负责,而不是对自己编写的代码所生成的代码负责。

  2. 我不希望有人通过使用可能是或不是最新的中间代码(更重要的是,我不想对此承担责任)尝试从主要资源中删除构建的捷径。诱使某些人陷入有关基于部分构建的中间代码调试冲突的毫无意义的过程中。

  3. 一旦进入源代码管理,我将对此负责。它在那里,b。它是最新的,并且c。它可以可靠地与那里的其他所有东西集成。这包括在我不再使用它时将其删除。责任越少越好。


14

我真的不认为您应该签入它们。

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


8

一般规则是no,但是如果要花一些时间来生成代码(由于数据库访问,Web服务等),那么您可能希望将缓存的版本保存在源代码管理中,从而避免了所有人的痛苦。

您的工具还需要意识到这一点,并在需要时处理从源代码管理中签出,太多的工具决定无缘无故地从源代码管理中签出。
一个好的工具将使用缓存的版本,而无需触摸它(也不修改文件上的时间步长)。

另外,您还需要在生成的代码中放置较大的警告,以使人们无法修改该文件,顶部的警告是不够的,您必须每十几行重复一次。


6

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

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


5

别说了。

如果您要检查生成的文件,那么您在做错什么。出问题的地方可能有所不同,可能是您的构建过程效率低下或其他原因,但我看不到永远是一个好主意。历史记录应与源文件关联,而不是与生成的文件关联。

它给最终解决差异,找到不再由构建生成的文件然后删除它们的人带来了麻烦。

签入生成文件的人正在等待着痛苦的世界!


4

在一种特殊情况下,您需要检入生成的文件:当您可能需要在无法使用用于生成其他文件的工具的系统上构建时。我和我一起使用的一个经典示例是Lex和Yacc代码。因为我们开发的运行时系统必须在各种各样的平台和体系结构上构建和运行,所以我们只能依靠目标系统来拥有C和C ++编译器,而不能为生成用于接口定义的词法分析代码所需的工具翻译者。因此,当我们更改语法时,我们签入生成的代码以对其进行解析。


2
类似的注释适用于autoconf / automake生成的文件;即使生成了大多数人,他们也会签入./configure和Makefile.in文件-大多数用户(和许多开发人员)不需要重建它们,并且通过签入这些文件,您不需要安装自动工具建立。
Stobor

1
是的,我们也将配置脚本和生成的Make依赖项存储在版本控制中。
Phil Miller

4

迟到了...反正...

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

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

具体来说,我使用编写的代码生成器进行工作:无需在版本控制下维护生成的源代码。我什至要说,由于生成器达到了一定的成熟度,尽管输入(例如模型描述)发生了变化,但我不必观察生成的代码的内容。


3

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

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


3

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

  • 知道每个交付的构建中都有哪些更改和错误修复。
  • 从原始源代码开始,能够完全复制任何交付的构建。不管使用哪种语言,自动生成的代码均不算作“源代码”。

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

第二个含义是,当客户或最终用户报告您一年前发布的某个版本中的错误时,您可以返回该版本,重现该错误,对其进行修复,并证明它是您的修复消除了该错误,而不是编译器的一些干扰和其他修复。

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

因此,现在回答您的问题:如果您可以完成上述所有操作,则无需记录任何中间表示形式,因为无论如何,您肯定会得到相同的答案。如果您不能完成上述所有操作,那么所有赌注都将落空,因为您永远无法保证两次做相同的事情并获得相同的答案。因此,您也可以将所有.o文件置于版本控制之下。


2

真的要看 最终,目标是能够复制您所需要的东西。如果您能够准确地重新生成二进制文件,则无需存储它们。但是您需要记住,为了重新创建内容,您可能首先需要进行准确的配置,这不仅意味着您的源代码,还意味着您的构建环境,您的IDE甚至其他库,生成器或其他内容,以及所使用的确切配置(版本)。

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

因此,您将存储生成的代码吗:我会拒绝。发布的二进制文件或可交付成果,包括您将用来复制它们的工具。然后,无需将它们存储在源代码管理中,只需对这些文件进行良好的备份即可。


“这不仅意味着您的源代码,而且还意味着您的构建环境,您的IDE,甚至其他库,生成器或其他东西”。\ n这就是我要检查的所有东西。与您的应用具有相同构建版本(即,您一次输入“ make”),请检查源。如果不这样做,则签入二进制文件
KeyserSoze,2009年

2

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

我不建议您保存或不保存生成的文件,无论您以哪种方式决定是否不让主题专家参与决策,都可能是错误的。

我的两分钱。


您提出了一个有趣的观点,并且不要亲自反对,这只是出于在快速发展的开发环境中的实际目的,这是不切实际的。为什么在任何情况下自动生成的代码都将携带与内容或IP相关的任何数据?我建议,一般而言,客户将无法掌握源代码控制自动生成代码的含义,并且通常不应向该客户提供此选项。恕我直言,要承担一个假想的和不太可能的法律情况,这是太多的开销和费用。
克里斯·哈克罗

在我目前所处的领域中,保险(我们的大客户)至少应保持10年的一切。我们构建了完善的工具来生成WCF服务。客户保留生成的代码,模板和整个内容。但这就是我的客户。猜猜您错过了我要提出的观点,即“这取决于客户的需求”和“无论您决定是否不让主题专家参与的任何决定,您都可能错了”。如果以某种方式这是一个错误的答案,或者让您觉得-1更好,那么很乐意提供帮助。请参阅我的答案上方评论中的“ womp”。
James Fleming

2

这里有赞成和反对的良好论据。作为记录,我在Visual Studio中构建了T4生成系统,并且我们默认的开箱即用选项可以使生成的代码被检入。如果您不想检入,则必须加倍努力。

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

如果您未检入输出,则必须先升级所有生成的代码,然后再升级生成器或修改输入,以便将其与新版本的输出进行比较。我认为这是一个相当繁琐的过程,但是在检入输出的情况下,将新输出与存储库进行比较很简单。

在这一点上,合理的问题是:“为什么要关心生成的代码中的更改?” (尤其是与目标代码相比。)我认为有几个关键原因,这些原因可以归结为当前的最新技术水平,而不是任何内在的问题。

  1. 您可以编写与生成的代码紧密结合的手写代码。如今,obj文件的整体情况并非如此。当生成的代码发生更改时,令人遗憾的是,经常需要更改一些手写代码以匹配它们。人们通常不会在生成的代码中观察到与可扩展性点的高度向后兼容性。

  2. 生成的代码只是改变其行为。您不会容忍编译器这样做,但是公平地讲,应用程序级代码生成器针对的是具有更多可接受解决方案的不同问题领域。重要的是要查看您对先前行为所做的假设是否已被打破。

  3. 您只是不100%相信发行人对发行人的输出。即使生成器工具不是使用编译器供应商的严格工具构建和维护的,它们也有很多价值。版本1.0可能对于您的应用程序来说是非常稳定的,但现在1.1版可能会对您的用例造成一些干扰。另外,您可以更改输入值,然后发现自己正在使用以前从未使用过的新发生器-可能会对结果感到惊讶。

从本质上讲,所有这些事情都取决于工具的成熟度-大多数业务应用程序代码生成器都没有达到编译器甚至lex / yacc级别工具多年来的水平。


2

双方都有有效和合理的论据,很难就共同的事情达成共识。版本控制系统(VCS)跟踪开发人员放入其中的文件,并假设VCS中的文件是由开发人员手工制作的,并且开发人员对文件的历史记录和版本之间的更改都很感兴趣。这个假设使两个概念相等:“我要在结帐时获取该文件。” 和“我对该文件的更改感兴趣”。

现在,双方的论点可以这样改写:

  • “我要在结帐时获取所有这些生成的文件,因为我没有在该计算机上生成这些文件的工具。”
  • “我不应该将它们放入VCS,因为我对更改此文件不感兴趣。”

幸运的是,这两个要求似乎并没有根本上的冲突。通过扩展当前的VCS,应该可以同时拥有两者。换句话说,这是一个错误的困境。如果我们仔细考虑一下,不难发现问题源于VCS的假设。VCS应该将开发人员手工制作的文件与不是开发人员手工制作的文件区分开,而恰好在此VCS内部。对于第一类文件(通常称为源文件(代码)),VCS现在做得很好。据我所知,对于后一类,VCS还没有这样的概念。

摘要

我将以git为例来说明我的意思。

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

聚苯乙烯

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


1

我会争取。如果您正在使用一个持续集成过程来检出代码,修改内部版本号,构建软件然后对其进行测试,则将代码作为存储库的一部分会变得越来越容易。

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


5
我喜欢-1的驱动器。如果您不同意,请不要投票-投票其他答案。请保存否决票,以获取错误答案。这是一个主观的问题。
womp

1

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


2
您是否签入目标文件(.o)?
KeyserSoze

1

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

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

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


我不同意。如果你让一个手动过程,它得到打破,没有人会注意到,直到它的时间来重新运行它。如果它是每天在生成服务器上生成的(以及在执行“干净”生成时每台开发人员的计算机上生成的),那么您不会感到惊讶。
2009年

如果将数据访问层代码检入源代码管理,应该不会感到意外,因为人们将被迫更新代码。如果有人偶然更改了构建机器上的代码生成工具的版本,而开发人员在其开发机器上使用的是旧版本(也许是不同的代码分支),那么就会头疼。我建议他从构建过程中删除代码生成步骤,因为它们不是代码生成器的维护者。

1

我(遗憾地)结束了将很多派生源置于源代码控制之下的原因,因为我与那些不愿为建立适当的构建环境而烦恼或没有技能来设置它的人进行远程合作派生的来源是完全正确的。(谈到Gnu自动工具,我本人就是其中之一!我不能使用三个不同的系统,每个系统都可以使用不同版本的自动工具,并且只能使用该版本。)

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

执行此操作时,基本上是在致力于仅在一个站点或仅在正确配置的站点上构建派生文件。应该设置您的Makefile(或其他文件)以注意到它们在哪里运行,并且除非他们知道它们在安全的构建站点上运行,否则应拒绝重新派生源。


1

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


“无需重新生成它。” 所以您签入编译的二进制文件?您是否还签入目标平台的版本?该策略无法很好地扩展。:(
dss539

1
那让我投了反对票?当然,您无需检入已编译的二进制文件(除非它们来自第三方库),因为它们可以从您的源代码中重新生成。我在谈论必须重新生成生成的代码而不是二进制文件。但是,嘿,如果您想误解我的意思,那就继续吧……
mezoid

这个答案是不值得的!至少,将生成的代码放入SC(也许放在明确标识的地方)似乎是合理的,以便至少您可以将用于生成对象的代码的哈希值与要编写的新代码进行比较。生成新的版本。有趣的是,这个问题是两极化的。
rp。

1

出于多种原因,在源代码管理中绝对拥有生成的代码。我重申很多人已经说过的话,但是我这样做的某些原因是

  1. 在源代码管理中使用代码文件,您无需使用Visual Studio的预构建步骤就可以编译代码。
  2. 当您在两个版本之间进行全面比较时,很高兴知道生成的代码是否在这两个标签之间发生了更改,而无需手动检查它。
  3. 如果代码生成器本身发生了更改,那么您将需要确保对生成的代码所做的更改也进行了适当的更改。例如,如果生成器发生了变化,但输出却没有发生变化,那么当您提交代码时,先前生成的代码与现在生成的代码之间将没有区别。

1
而且您的代码生成器本身不在源代码管理中,因为...?
杰弗里·汉汀

@Jeffrey:我从未说过代码生成器不在源代码管理中。
Joe Enos 2010年

我知道,我只是在开玩笑。:-)我发现许多基于CodeDom的代码生成器都喜欢以随机顺序生成其输出,因此,为了实现可重复性(并因此能够轻松判断所生成的代码是否每次运行都发生变化,我可以这样)。编写了一个例程,将a的内容CodeCompileUnit按规范顺序排序。
Jeffrey Hantin

0

我会将生成的文件放在源代码树之外,但将其放在单独的生成树中。

例如工作流是

  1. 正常签入/签出/修改/合并源(不包含任何生成的文件)
  2. 在适当的时候,将源树检出到干净的构建树中
  3. 构建后,检入必须存在的所有“重要”文件(“实际”源文件,可执行文件+生成的源文件),以进行审核/监管。这将为您提供所有适当的已生成代码+可执行文件+所有内容的历史记录,这些历史记录与发布/测试快照等相关的时间增量以及与日常开发分离的时间增量。

Subversion / Mercurial / Git / etc中可能有很好的方法将两个地方的真实源文件的历史联系在一起。


0

双方似乎都有很强的说服力。我建议阅读所有票数最高的答案,然后确定适用于您的特定情况的论点。

更新:我问了这个问题,因为我真的相信有一个明确的答案。查看所有答复,我可以肯定地说,没有这样的答案。该决定应基于多个参数来做出。阅读其他答案可以为您在决定该问题时应该问自己的问题类型提供很好的指导。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.