Ron*_*lev 96 version-control code-generation
这是我正在参与的辩论.我想得到更多的意见和观点.
我们有一些在构建时生成的类来处理数据库操作(在这个特定情况下,使用SubSonic,但我不认为这对于这个问题非常重要).生成设置为Visual Studio中的预构建步骤.因此,每次开发人员(或官方构建过程)运行构建时,都会生成这些类,然后将其编译到项目中.
现在有些人声称,在源代码控制中保存这些类可能会导致混淆,以防您获得的代码与您自己的环境中生成的代码不匹配.
我想有办法追溯代码的历史,即使它通常被视为黑盒子.
任何论据或反驳论点?
由于上述原因,此时我不会选择接受的答案.
Gle*_*len 49
将它保存在源代码管理中比它的价值更麻烦.
每次进行构建时都必须提交一个提交,以使其成为任何值.
通常我们将生成的代码(idl,jaxb等等)留在我工作的源代码控制之外,这从来就不是问题
Kie*_*eli 31
每当我想在我自己的个人仓库中显示对源树的更改时,所有"生成的文件"都会显示为已更改并需要进行评估.
我更希望有一个更清晰的修改列表,其中只包含已执行的实际更新,而不是自动生成的更改.
将它们保留,然后在构建之后,在每个生成的文件上添加"忽略".
Jar*_*Par 28
把它放在源代码控制中.拥有您为未来开发人员编写的所有内容的历史记录的优势超过了同步后偶尔重建的轻微痛苦.
Lau*_*ves 25
这样看:你检查你的目标文件到源代码管理?生成的源文件是构建工件,就像目标文件,库和可执行文件一样.它们应该被对待.大多数人认为你不应该将生成的目标文件和可执行文件检查到源代码控制中.相同的参数适用于生成的源.
如果需要查看生成文件的历史版本,可以同步到其源的历史版本并重建.
将生成的任何类型的文件检入源控件类似于数据库非规范化.有偶尔的理由这样做(通常用于性能),但这应该只是非常小心,因为它变得更加困难,一旦数据被规格化,以保持正确性和一致性来完成.
Joh*_*ith 17
我称之为DRY原则.如果您已经在存储库中拥有用于在构建时生成这些代码文件的"源文件",则不需要将"相同的代码"提交"两次".
此外,如果例如代码生成有一天会中断,您可以通过这种方式避免一些问题.
tha*_*att 14
我真的不认为你应该检查它们.
当然,生成的代码中的任何变化都可能是噪声 - 环境之间的变化,或者由于其他原因而发生的变化 - 例如数据库中的变化.如果您的数据库的创建脚本(或任何其他依赖项)在源代码管理中,那么为什么还需要生成的脚本呢?
dkr*_*etz 13
不,有三个原因.
源代码是一些必要的,足以在当前或以前的某个时间点重现应用程序的快照 - 仅此而已.这意味着有人负责检查所有内容.通常我很乐意对我编写的代码负责,但不会因为我编写的代码而生成代码.
我不希望有人试图通过使用可能是也可能不是最新的中间代码来尝试从主要来源快捷构建构建(更重要的是我不想承担责任.)并且它也是如此诱惑某些人陷入无意义的过程中,基于部分构建调试中间代码中的冲突.
一旦它在源代码管理中,我就承担责任.它在那里,b.它是最新的,和c.它可以与那里的其他一切可靠地融为一体.这包括在我不再使用它时删除它.责任越少越好.
一般规则是否定的,但是如果生成代码需要时间(因为数据库访问,Web服务等),那么您可能希望在源代码管理中保存缓存版本并为每个人节省痛苦.
您的工具还需要了解这一点,并在需要时处理源控件的检出,太多工具决定从源控件中检出而没有任何理由.
一个好的工具将使用缓存版本而不触及它(也不修改文件的时间步长).
此外,您需要在生成的代码中添加大警告,以便人们不修改文件,顶部的警告是不够的,您必须每十几行重复一次.
这里提出了支持和反对的充分论据。作为记录,我在 Visual Studio 中构建了 T4 生成系统,我们默认的开箱即用选项会导致生成的代码被签入。如果您不想签入,则必须更加努力。
对我来说,关键的考虑因素是在输入或生成器本身更新时区分生成的输出。
如果您没有签入输出,则必须在升级生成器或修改输入之前获取所有生成代码的副本,以便能够将其与新版本的输出进行比较。我认为这是一个相当乏味的过程,但是通过签入输出,将新输出与存储库进行比较就很简单了。
此时,有理由问“为什么您关心生成代码的更改?” (特别是与目标代码相比。)我相信有几个关键原因,这些原因归结为当前的技术水平而不是任何固有的问题。
您可以编写与生成的代码紧密结合的手写代码。如今 obj 文件的整体情况并非如此。当生成的代码发生更改时,很遗憾,经常会出现一些手写代码需要更改以匹配的情况。人们通常不会在生成的代码中观察到与可扩展点的高度向后兼容性。
生成的代码只是改变其行为。您不会容忍编译器出现这种情况,但公平地说,应用程序级代码生成器正在针对不同领域的问题提供更广泛的可接受的解决方案。重要的是要看看你对以前行为所做的假设现在是否被打破了。
您只是不 100% 信任生成器从一个版本到另一个版本的输出。生成器工具具有很多价值,即使它们不是按照编译器供应商的严格构建和维护的。版本 1.0 可能对于您的应用程序来说是完全稳定的,但也许 1.1 现在对于您的用例来说有一些小故障。或者,您更改输入值并发现您正在使用以前未使用过的新生成器 - 可能您会对结果感到惊讶。
本质上,所有这些事情都归结为工具成熟度 - 大多数业务应用程序代码生成器还没有达到编译器甚至 lex/yacc 级工具多年来的水平。
我们不存储生成的DB代码:因为它是生成的,所以您可以从源文件中的任何给定版本随意获取它.存储它就像存储字节码等.
现在,您需要确保在给定版本中使用的代码生成器可用!较新的版本可以生成不同的代码......
在某些项目中,我将生成的代码添加到源代码管理中,但这确实取决于。我的基本准则是,如果生成的代码是编译器的固有部分,那么我不会添加它。如果生成的代码来自外部工具,例如本例中的 SubSonic,那么我会将 if 添加到源代码管理中。如果您定期升级组件,那么我想知道生成的源代码中的更改,以防出现错误或问题。
至于需要签入的生成代码,最坏的情况是手动区分文件并在必要时还原文件。如果你使用 svn,你可以在 svn 中添加一个 pre-commit 钩子来拒绝提交,如果文件没有真正改变。
在一种特殊情况下,您希望签入生成的文件:当您可能需要在用于生成其他文件的工具不可用的系统上进行构建时。这方面的经典示例,也是我使用的示例,是 Lex 和 Yacc 代码。因为我们开发的运行时系统必须在各种各样的平台和架构上构建和运行,所以我们只能依靠目标系统来拥有 C 和 C++ 编译器,而不是为我们的接口定义生成词法/解析代码所需的工具翻译。因此,当我们更改语法时,我们会检查生成的代码以对其进行解析。
到的有点晚了……反正……
您会将编译器的中间文件放入源代码版本控制中吗?在代码生成的情况下,根据定义,源代码是生成器的输入,而生成的代码可以被视为“真实”源代码和构建的应用程序之间的中间文件。
所以我会说:不要将生成的代码置于版本控制之下,而是将生成器及其输入置于版本控制之下。
具体来说,我使用我编写的代码生成器:我从来不必在版本控制下维护生成的源代码。我什至会说,由于生成器达到了一定的成熟度,尽管输入(例如模型描述)发生了变化,但我不必观察生成代码的内容。
把它拿出来.
如果您正在检入生成的文件,那么您做错了.什么是错的可能会有所不同,这可能是因为你的构建过程是低效的,还是其他什么东西,但我不能看到它永远是一个好主意.历史记录应与源文件相关联,而不是与生成的文件相关联.
对于那些最终试图解决差异,找到不再由构建生成的文件然后删除它们等的人来说,这只会让人头疼.
一个痛苦的世界等待那些签入生成文件的人!