最近,我的一位同事说了这些话:"构建服务器从同一源代码生成的连续APK(可执行文件)可能不一样"。此讨论的上下文是在构建X上执行的QA是否也适用于构建Y,它由相同的源代码由相同的构建服务器(以相同方式配置)执行。
我认为由于各种因素(例如,不同的时间戳),生成的可执行文件可能不完全相同,但问题是它们是否可以在功能上不同。
我能想到的唯一情况是,相同的源代码可以产生不同的功能,这就是多线程问题:在多线程代码同步不正确的情况下,执行了不同的重新排序/优化操作在编译时可能会影响这个不良同步的代码并改变其功能行为。
我的问题是:
欢迎链接到任何相关材料。
答案 0 :(得分:5)
我认为不同的功能可能仅由环境差异引起,或者您使用的是第三方库的快照版本,因此在一段时间后更新。
一些建议: 如果可以重建它,使用详细的构建工具模式(例如maven中的-X)并逐行比较输出与差异程序答案 1 :(得分:5)
在少数情况下肯定是可能的。我假设你正在使用Gradle来构建你的Android应用程序。
案例1:您正在使用版本通配符附带的第三方依赖项,例如:
#lang racket
(define file (make-temporary-file))
;; run the editor you want here and pass it the file name
(system* (find-executable-path "vim") (path->string file))
;; do whatever processing you want to do on the file here
(file->string file)
在这种情况下,依赖项可能会发生变化,因此强烈建议使用显式依赖项版本。
案例2 :您正在使用Gradle的buildConfigFields将环境信息注入您的应用。这些值将注入您应用的compile somelib.1+
课程。根据您使用这些值的方式,应用程序行为可能会因连续构建而异。
案例3 :在连续版本之间更新CI上的JDK。虽然我认为极不可能,但您的应用行为可能会根据其编译方式而改变。例如,您可能在JDK中遇到一个在更高版本中得到修复的边缘情况,导致以前工作过的代码采取不同的行为。
我认为这回答了你的第一个问题和第二个问题。
编辑:对不起,我想我错过了OP的一些重要信息。我的案例2是您BuildConfig
的示例,案例3违反了您的e.g. different timestamp
。我会在这里留下答案。
答案 2 :(得分:4)
如果相同的源代码可以在同一台机器/配置上产生不同的结果,我们可能无法进行编程。
当语言级别,操作系统或某些其他依赖项发生更改时,总会有一个选项可以解决问题。如果所有这些都改变了构建的时间,那么你将不得不做一些根本错误的。
使用android / gradle,一般导致不同行为或错误的一个可能原因是在+
文件中使用build.gradle
库版本。这就是为什么你应该避免这样做,因为连续的构建可以获取更新/不同的版本,因此你有不同的源代码,因此它可以创建一个功能不同的可执行文件。
良好的构建应始终可重复。这意味着给定相同的配置它应该具有相同的结果。如果不是,你就永远不能依赖任何东西,而且必须对所有事情进行全面的回归测试。
由相同构建服务器从相同源代码执行的连续构建可能在功能上不同
没有。如上所述,如果使用相同的版本,相同的源代码,它应该产生相同的行为。除非你做错了。
[...]这些差异是否仅限于错误同步的多线程代码?
这意味着您的编译器存在错误。虽然这是可能的,但这种可能性极小。
[...]可以改变的其他部分是什么?
除了时间戳和内部版本号外,在给定相同的源代码和配置的情况下, 应该更改。
在构建中包含单元(和其他)测试始终是个好主意。这样,您可以测试每个构建的特定行为。
答案 3 :(得分:1)
它们应该是相同的,除了:
构建系统中存在线程/优化问题。
硬件故障构建环境中的CPU / RAM / HDD问题
构建系统本身或构建脚本中与时间/平台相关的代码
因此,如果您使用完全相同版本的构建系统在相同的硬件上构建完全相同的代码,相同的操作系统版本和您的代码不会特别依赖于构建时间结果应该相同。他们甚至应该有完全相同的支票金额和大小。
如果您的代码不依赖于像Gradle / Maven那样在构建时从Internet上下载的外部模块,那么结果也是一样的 - 您不能因为它们而对这些库提供同样的权限不在版本控制中。此外,可能存在依赖,其中模块版本指定不完全(如2.0。+),因此如果维护者更新此模块,您的构建系统将使用更新的一个 - >所以基本上你的构建源自不同的源代码。
正如有人提到在构建服务器上使用单元测试是确保构建稳定且不包含明显错误的好习惯。
答案 4 :(得分:0)
虽然这个问题涉及Java / Android,但Jon Skeet在博客中写了different C# parsers treating some Unicode characters differently,主要是因为Unicode字符数据库的变化。
在他的例子中,蒙古语元音分隔符(U + 180E)被认为是空白字符或标识符中允许的字符,在变量赋值中产生不同的结果。
答案 5 :(得分:-1)
绝对有可能。您可以构建一个示例程序,每次启动时它的功能都会不同。
想象一下策略设计模式,它允许您在运行时选择算法并加载基于RNG的算法。