Should I store generated code in source control

这是我正在参与的一场辩论,我想听听更多的意见和观点。

我们有一些类是在构建时生成的,用于处理 DB 操作(在这个特定的例子中,使用 SubSonic,但是我不认为它对这个问题非常重要)。生成被设置为 VisualStudio 中的预生成步骤。因此,每次开发人员(或官方构建过程)运行构建时,都会生成这些类,然后将其编译到项目中。

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

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

有什么反对意见吗?


更新: 我问这个问题,因为我真的相信有一个明确的答案。看着所有的回答,我可以高度肯定地说,没有这样的答案。决策应该基于多个参数。阅读下面的答案可以提供一个非常好的指南,你应该问自己类型的问题时,必须决定这个问题。

由于上面提到的原因,此时我不会选择一个被接受的答案。

19534 次浏览

把它放到源代码控制中。对于未来的开发人员来说,拥有所有编写内容的历史记录的好处超过了在同步之后偶尔进行重新构建所带来的轻微痛苦。

我会争取。如果您正在使用一个检出代码、修改构建编号、构建软件然后测试它的持续集成过程,那么将该代码作为存储库的一部分会更简单、更容易。

此外,它还是你拍摄软件资源库的每一张“快照”的重要组成部分。如果它是软件的一部分,那么它就应该是存储库的一部分。

将它保存在源代码管理中会带来更多的麻烦。

You have to do a commit every time you do a build for it to be any value.

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

每次我想在我的个人回购中显示源代码树的更改时,所有“生成的文件”都会显示为已更改并需要提交。

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

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

我想说的是,您应该避免向源代码控制添加任何生成的代码(或其他构件)。如果生成的代码对于给定的输入是相同的,那么您可以检查您想要区分的版本并生成代码进行比较。

我真的觉得你不该让他们入住。

当然,生成的代码中的任何变化要么是噪音——环境之间的变化,要么是其他因素导致的变化——例如数据库中的变化。如果 DB 的创建脚本(或任何其他依赖项)在源代码控制中,那么为什么还需要生成的脚本呢?

通常的规则是 没有,但是如果生成代码需要时间(因为数据库访问、 Web 服务等) ,那么您可能需要在源代码控制中保存一个缓存版本,并为每个人节省时间。

Your tooling also need to be aware of this and handle checking-out from the source control when needed, too many tools decide to check out from the source control without any reason.
一个好的工具将使用缓存版本而不触及它(也不修改文件上的时间步骤)。

另外你还需要在生成的代码中放一个大警告,让人们不要修改文件,在顶部放一个警告是不够的,你必须每隔十几行就重复一次。

I call the DRY principle. If you already have the "source files" in the repository which are used to generate these code files at build time, there is no need to have the same code committed "twice".

另外,如果有一天代码生成中断了,您也可以通过这种方式避免一些问题。

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

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

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

可以这样看: 是否将目标文件签入源代码管理?生成的源文件是构建构件,就像目标文件、库和可执行文件一样。他们应该得到同样的对待。大多数人会认为,您不应该将生成的对象文件和可执行文件检查到源代码控制中。同样的参数也适用于生成的源。

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

Checking generated files of any sort into source control is analogous to database denormalization. There are 偶尔 reasons to do this (typically for performance), but this should be done only with great care as it becomes much harder to maintain correctness and consistency once the data is denormalized.

在一些项目中,我将生成的代码添加到源代码控制中,但这实际上取决于。我的基本原则是,如果生成的代码是编译器的固有部分,那么我不会添加它。如果生成的代码来自外部工具,比如本例中的 SubSonic,那么我将把 If 添加到源代码控制中。如果您定期升级组件,那么我希望知道生成的源代码中的更改,以防出现 bug 或问题。

As far as generated code needing to be checked in, a worst case scenario is manually differencing the files and reverting the files if necessary. If you are using svn, you can add a pre-commit hook in svn to deny a commit if the file hasn't really changed.

一般来说,生成的代码不需要存储在源代码控制中,因为这段代码的修订历史可以通过生成它的代码的修订历史来跟踪!

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

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

我将保留源代码树的生成文件 出去,但是将它 进去作为一个单独的构建树。

例如:

  1. checkin/out/modify/merge source normally (w/o any generated files)
  2. 在适当的情况下,将源树签出到一个干净的构建树中
  3. 构建完成后,检查所有“重要”文件(“真正的”源文件、可执行文件 + 生成的源文件) ,这些文件必须用于审计/监管目的。这将为您提供所有适当生成的代码 + 可执行文件 + 任何东西的历史记录,以及与发布/测试快照等相关的时间增量,并与日常开发解耦。

Subversion/Mercurial/Git/etc 中可能有很好的方法将两个地方的真正源文件的历史记录绑定在一起。

这要看情况。最终,我们的目标是能够复制你所拥有的,如果需要的话。如果能够准确地重新生成二进制文件,就没有必要存储它们。但是你需要记住,为了重新创建你的东西,你可能需要你的确切配置,这不仅意味着你的源代码,还意味着你的构建环境,你的 IDE,甚至可能是其他的库,生成器或东西,在你使用的确切配置(版本)。

如果我们将构建环境升级到更新的版本,甚至升级到其他供应商的版本,我们就会在项目中遇到麻烦,因为我们无法重新创建以前的完全二进制文件。当要部署的二进制文件依赖于某种散列时,尤其是在安全环境中,并且由于编译器升级或其他原因,重新创建的文件会有所不同,这是一个真正的痛苦。

所以,你会存储生成的代码: 我会说不。发布的二进制文件或可交付文件,包括您使用我将存储的工具复制它们。然后,不需要将它们存储在源代码管理中,只需要对这些文件进行很好的备份。

我(遗憾地)最终将很多派生源置于源代码控制之下,因为我远程工作的人要么懒得设置适当的构建环境,要么不具备设置该环境的技能,因此派生源的构建完全正确。(说到 Gnu 自动工具,我自己就是其中之一!我不能使用三个不同的系统,每个系统都使用不同的 autotools 版本,而且只能使用那个版本。)

This sort of difficulty probably applies more to part-time, volunteer, open-source projects than to paid projects where the person paying the bills can insist on a uniform build environment.

当您这样做的时候,您基本上是承诺只在一个站点上构建派生文件,或者只在正确配置的站点上构建派生文件。您的 Makefile (或任何东西)应该被设置为注意它们运行的位置,并且应该拒绝重新派生源,除非它们知道它们正在安全的构建站点上运行。

如果它是源代码的一部分,那么不管是谁或什么生成了它,它都应该放在源代码控制中。您希望源代码管理反映系统的当前状态,而不必重新生成它。

No, for three reasons.

  1. Source code is everything necessary and sufficient to reproduce a snapshot of your application as of some current or previous point in time - nothing more and nothing less. Part of what this implies is that someone is responsible for everything checked in. Generally I'm happy to be responsible for the code I write, but not the code that's generated as a consequence of what I write.

  2. 我不希望有人试图通过使用中间代码(可能是当前的,也可能不是当前的,更重要的是,我不想承担责任)来从主源代码中快捷构建对于一些人来说,在基于部分构建的中间代码中调试冲突这种毫无意义的过程中陷入其中,是不是太诱人了。

  3. 一旦它进入源代码控制,我就会承担责任,a。它在那里,b。它是最新的,c。它与其他所有东西可靠地集成在一起。包括当我不再使用它的时候移除它。这种责任越少越好。

由于许多原因,在源代码控制中绝对有生成的代码。我在重申很多人已经说过的话,但是我这样做的一些原因是

  1. 有了源代码管理中的代码文件,您就可以不使用 VisualStudio 预生成步骤编译代码。
  2. 在对两个版本进行全面比较时,最好能够知道生成的代码是否在这两个标记之间发生了更改,而不必手动检查。
  3. 如果代码生成器本身发生了更改,那么您需要确保对生成的代码所做的更改是适当的。也就是说。如果生成器发生了变化,但是输出不应该发生变化,那么当您提交代码时,以前生成的代码和现在生成的代码之间将没有差异。

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

来得有点晚... 总之..。

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

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

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

看来双方的意见都很有说服力。我建议你阅读所有最受欢迎的答案,然后决定哪些论点适用于你的具体情况。

UPDATE: I asked this question since I really believed there is one definitive answer. Looking at all the responses, I could say with high level of certainty, that there is no such answer. The decision should be made based on more than one parameter. Reading the other answers could provide a very good guideline to the types of questions you should be asking yourself when having to decide on this issue.

别说了。

如果您正在检入生成的文件,那么您正在做错误的事情。问题可能不同,可能是您的构建过程效率低下,或者其他原因,但我认为 ever不是一个好主意。历史记录应该与源文件相关联,而不是与生成的文件相关联。

它只会给那些最终试图解决差异、找到不再由构建生成的文件然后删除它们的人带来麻烦,等等。

一个痛苦的世界等待着那些谁签入生成的文件!

组态管理(版本控制只是其中一部分)的工作是能够做到以下几点:

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

第一种方法确保当你告诉客户或最终用户“你上周报告的 bug 已经修复,新功能已经添加”时,他们不会在两个小时后回来说“不,还没有”。这也确保了他们不会问“为什么它要做 X?”?我们从来没有要过 X。

第二种意思是,当客户或最终用户报告一年前发布的某个版本的 bug 时,你可以回到那个版本,重现这个 bug,修复它,并证明它是你的修复程序已经消除了 bug,而不是编译器的某些扰动和其他修复程序。

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

现在回答你的问题: 如果你能做到以上所有,那么你就不需要记录任何中间表示,因为你保证无论如何都会得到相同的答案。如果你不能做到以上所有,那么所有的赌注都取消了,因为你永远不能保证做同样的事情两次,得到同样的答案。所以你最好把你所有的。在版本控制下的文件。

正确答案是“视情况而定”,这取决于客户的需求是什么。 如果您可以将代码回滚到特定的版本,并在没有它的情况下经受住任何外部审计,那么您仍然没有站稳脚跟。作为开发人员,我们需要考虑的不仅仅是“噪音”、痛苦和磁盘空间,还有我们被赋予生成知识产权的角色这一事实,这可能会有法律后果。你能向法官证明你有能力重建一个网站就像两年前顾客看到的那样吗?

我不是建议你保存或不保存生成文件,不管你怎么决定,如果你没有涉及主题专家的决定,你可能是错误的。

我的意见。

这里提出的支持和反对意见都很有说服力。 为了便于记录,我在 VisualStudio 中构建了 T4生成系统,并且我们的默认开箱即用选项导致生成的代码被签入。如果你不想签到的话,你得再努力一点。

对我来说,主要考虑的是在更新输入或生成器本身时对生成的输出进行区分。

如果没有检入输出,那么在升级生成器或修改输入之前,必须获取所有生成代码的副本,以便能够将其与新版本的输出进行比较。我认为这是一个相当繁琐的过程,但是对于签入输出,只需要将新输出与存储库区分开来即可。

At this point, it is reasonable to ask "Why do you care about changes in generated code?" (Especially as compared to object code.) I believe there are a few key reasons, which come down to the current state of the art rather than any inherent problem.

  1. 您可以编写与生成代码紧密结合的手写代码。现在的 obj 文件大体上不是这样的。当生成的代码发生变化时,令人遗憾的是,有些手写代码需要更改才能匹配。在生成的代码中,人们通常不会观察到向后兼容性与可扩展性点的高度兼容性。

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

  3. 您只是不能100% 信任生成器从一个版本到另一个版本的输出。生成器工具具有很大的价值,即使它们没有像编译器供应商那样严格地构建和维护。发行版1.0对于您的应用程序来说可能非常稳定,但是现在对于您的用例来说,可能1.1有一些小故障。或者,您可以更改输入值,并发现您正在练习生成器的一个新部分,这是您以前从未使用过的——您可能会对结果感到惊讶。

基本上,所有这些都归结为工具的成熟度——大多数业务应用程序代码生成器并没有达到编译器或者甚至是 lex/yacc 级别的工具多年来的水平。

双方都有正当合理的论据,很难在一些共同点上达成一致。版本控制系统(VCS)跟踪文件 开发人员投入其中,并假设 VCS 中的文件是由开发人员手工制作的,开发人员对历史感兴趣 and change between any revision of the files. This assumption equalize the two concepts, "I want to get this file when I do checkout." and "I am interested in the change of this file."

现在,双方的论点可以换个说法:

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

Fortunately, it seems that the two requirements are not conflicting fundamentally. With some extension of current VCSs, it should be possible to have 都有。换句话说,这是一个错误的困境。如果我们思考一会儿,就不难意识到问题来源于 VCS 所持有的假设。VCS 应该区分由开发人员手工制作的文件和不是由开发人员手工制作的文件,而是恰好在文件内部的文件 这个 VCS。对于第一类文件,我们通常称之为源文件(代码) ,VCS 现在已经做得很好了。对于后一类,VCS 有 据我所知,还没有这样的概念。

摘要

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

  • git status should not show generated files by default.
  • git commit应该包含生成的文件作为快照。
  • 在默认情况下,git diff不应该显示生成的文件。

附言

Git 钩子可以作为一种解决方案使用,但是如果 Git 本地支持它,那就太好了。gitignore不符合我们的要求,因为被忽略了 文件不会进入 VCSs.enter code here