2

我需要使用一个附加构建器来设置一个 Eclipse 项目,该构建器增强了早期构建器(最好是 Eclipse 自己的)生成的 Java 字节码。我设法让这个构建器正确运行并增强了 Eclipse Java 构建器的输出,但几秒钟后 Eclipse 重新运行了它的 Java 构建器并重新设置了字节码。它不会重新运行我的增强生成器。

我的设置

  • 作为“Gradle 项目”导入 Eclipse 2019-12(使用 Buildship)。
  • 手动(并使用 Gradle 自动添加)自定义Ant构建器(最终调用 Gradle)以增强 Eclipse Java 构建器在 bin/main 中生成的代码。此构建器设置为在Manual BuildAuto Build上运行,而不是After a "Clean"during a "Clean"
  • 默认情况下,上面的最终有三个构建器,从上到下:1. Gradle Project Builder, 2.Java Builder和 3.我的字节码增强构建器(是的,它在最后列出)。

我尝试过的替代方案

  1. 将我的构建器设置为在“清洁”之后/期间运行的一些组合也没有成功。不确定这些与哪些确切事件有关,真的。
  2. 让建造者在之后刷新项目......而且也没有 - 没有帮助。
  3. 尝试使用 Gradle 脚本中的以下位删除 Java Builder(不起作用 - 它会自行恢复):

    eclipse {
        project {
            file {
                whenMerged { projectFile ->
                    projectFile.buildCommands.removeAll { it.name == 'org.eclipse.jdt.core.javabuilder' }
                }
            }
        }
     }
    
  4. 尝试手动禁用 Java 构建器并让我的字节码增强构建器也构建文件本身(使用 Gradle)。这将存储org.eclipse.jdt.core.javabuilder.launch具有以下内容的以下文件文件...但重新启动构建器时会重新启用:

    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <launchConfiguration type="org.eclipse.ant.AntBuilderLaunchConfigurationType">
    <booleanAttribute key="org.eclipse.ui.externaltools.ATTR_BUILDER_ENABLED" value="false"/>
    <stringAttribute key="org.eclipse.ui.externaltools.ATTR_DISABLED_BUILDER" value="org.eclipse.jdt.core.javabuilder"/>
    <mapAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS"/>
    <booleanAttribute key="org.eclipse.ui.externaltools.ATTR_TRIGGERS_CONFIGURED" value="true"/>
    </launchConfiguration>
    
  5. 我尝试(但失败)查找是否有一些工作区文件(而不是项目文件)被更改(以及)以禁用 Java 构建器。

问题

  1. 为编译后字节码增强设置 Eclipse 的“正确”方法是什么?
  2. 是什么导致 Eclipse 重新运行以前的构建器而不重新运行我的?
  3. 有没有办法解决(1)?
  4. 如何可靠地禁用 Java 构建器?

任何人都可以帮忙吗?谢谢!

更新附加细节 我添加了 12 个构建器,并让它们都将输出附加到同一个日志文件以进行研究。12 个额外的构建器只是提供信息 - 4 个在 Java 构建器之前,4 个在 Java 和增强构建器之间,4 个在增强构建器之后。12 个中的每一个都只在四种条件之一中运行(因此为 3x4)。它们排列如下:

  1. Gradle 项目生成器
  2. 1a-after-clean(仅在 "Clean" 之后运行)
  3. 1b-manual(仅在手动构建期间运行)
  4. 1c-auto(仅在自动构建期间运行)
  5. 1d-during-clean(仅在 "Clean" 期间运行)
  6. Java 生成器
  7. 2a-after-clean(仅在 "Clean" 之后运行)
  8. 2b-manual(仅在手动构建期间运行)
  9. 2c-auto(仅在自动构建期间运行)
  10. 2d-during-clean(仅在 "Clean" 期间运行)
  11. 字节码增强生成器
  12. 3a-after-clean(仅在 "Clean" 之后运行)
  13. 3b-manual(仅在手动构建期间运行)
  14. 3c-auto(仅在自动构建期间运行)
  15. 3d-during-clean(仅在“清洁”期间运行)

12 个信息构建器中的每一个都写下时间、名称和所选测试类的大小。未增强的长度为 46243 字节。增强后它变为 53338 字节长。

这是仅在此项目上运行“清理”后的日志(启用“自动构建”):

20:19:19
1d-during-clean
-rw-r--r--  1 Learner  ...\...  46243  3 Mar 20:10 Test.class

20:19:19
2d-during-clean
-rw-r--r--  1 Learner  ...\...  46243  3 Mar 20:10 Test.class

20:19:20
1c-auto
-rw-r--r--  1 Learner  ...\...  46243  3 Mar 20:10 Test.class

20:19:27
2c-auto
-rw-r--r--  1 Learner  ...\...  46243  3 Mar 20:19 Test.class


Buildfile: /.../some-ant.xml

run-gradle:
        [echo] Running Gradle: --parallel :...:enhanceEclipseBytecode
        ...
        [java] > Task :...:enhanceBytecode
        [java] Enhanced class: ...Test in ...
        ...
        [java] Enhanced 205 classes.
        [java] > Task :...:enhanceEclipseBytecode
        [java] BUILD SUCCESSFUL in 15s
        [java] 2 actionable tasks: 2 executed
BUILD SUCCESSFUL
Total time: 15 seconds
20:19:44
1c-auto
-rw-r--r--  1 Learner  ...\...  53338  3 Mar 20:19 Test.class

20:19:46
1c-auto
-rw-r--r--  1 Learner  ...\...  46243  3 Mar 20:19 Test.class

20:19:46
2c-auto
-rw-r--r--  1 Learner  ...\...  46243  3 Mar 20:19 Test.class

20:19:46
3b-manual
-rw-r--r--  1 Learner  ...\...  46243  3 Mar 20:19 Test.class

20:19:46
3c-auto
-rw-r--r--  1 Learner  ...\...  46243  3 Mar 20:19 Test.class

20:19:46
3d-during-clean
-rw-r--r--  1 Learner  ...\...  46243  3 Mar 20:19 Test.class

20:19:57
1c-auto
-rw-r--r--  1 Learner  ...\...  46243  3 Mar 20:19 Test.class

20:19:57
2c-auto
-rw-r--r--  1 Learner  ...\...  46243  3 Mar 20:19 Test.class

20:19:57
3b-manual
-rw-r--r--  1 Learner  ...\...  46243  3 Mar 20:19 Test.class

20:19:57
3c-auto
-rw-r--r--  1 Learner  ...\...  46243  3 Mar 20:19 Test.class

20:19:57
3d-during-clean
-rw-r--r--  1 Learner  ...\...  46243  3 Mar 20:19 Test.class

更新 2:重现的最小示例

  1. 创建一个文件夹 - 随意命名。
  2. 在该文件夹中创建包含以下内容的build.grade文件:

    buildscript {
        repositories {
            mavenCentral()
        }
    
        dependencies {
            classpath 'org.hibernate:hibernate-gradle-plugin:5.4.2.Final'
        }
    }
    
    plugins {
        id 'java'
        id 'eclipse'
    }
    
    apply plugin: 'org.hibernate.orm'
    
    repositories {
        mavenCentral()
    }
    
    dependencies {
        implementation 'org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Final'
    }
    
    hibernate {
        sourceSets = [ project.sourceSets.main ]
        enhance {
            enableLazyInitialization = true;
            enableDirtyTracking = true;
            enableAssociationManagement = false;
            enableExtendedEnhancement = false;
        }
    }
    
  3. 在那里也创建一个 src/main/java/learner/TestEntity.java 如下:

    package learner;
    
    import javax.persistence.*;
    
    @Entity
    public class TestEntity {
        @Id
        @Column(name = "id", nullable = false, updatable = false)
        private Long id = null;
    
        @Column(name = "name", columnDefinition = "TEXT")
        private String name = null;
    
        public Long getId() {
            return id;
        }
    
        public void setId(final Long id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(final String name) {
            this.name = name;
        }
    }
    
  4. 执行gradle compileJavabuild/classes/java/main/learner/TestEntity.class在 ASCII 或十六进制查看器中打开生成的二进制文件并观察$$_hibernate_write_name其中的内容。

  5. 将此项目作为 Gradle 项目导入 Eclipse(比如 2019-12)并构建它。打开结果bin/main/learner/TestEntity.class并观察不到这些。
4

1 回答 1

1

有一些事情我不清楚/错误,并且找不到相关文档来了解详细信息。以下是正确执行此操作所需了解的摘要(其中一些我从一开始就正确,但不是全部):

  1. Eclipse 中的 Gradle/Buildship 集成尝试利用 Eclipse 的内部编译器。这是 Eclipse 的设计,在开发过程中有其自身的优势......以及在这样的时期的缺点 - 无法利用外部/生产构建器(在本例中为 Gradle 模型)进行构建。由于这个原因,在 Gradle 中运行的任何字节码增强(插件或非插件)在 Eclipse 中根本没有影响(除非它们做了一些 Eclipse 特定的事情)。(我猜对了)
  2. 要在 Eclipse 中执行字节码增强(不是 apt 或某些 Eclipse 插件可以做的),必须添加自定义构建器并将它们放置在默认 Java 构建器之后(手动或自动)。(我也猜对了)
  3. 开箱即用的 Eclipse 提供了两种构建器——“Ant”和“Program”。那里没有 Gradle。“程序”类不是跨平台的,只有 Ant 是。Ant 可用于做事或以跨平台方式启动 Gradle(Java exec on Gradle 的 main() 方法)。(我也猜对了)
  4. [我在这里错了] Eclipse 提供了四个“事件”,可以将 Ant 构建器绑定到它们:After a "Clean"Manual BuildAuto Buildduring a "Clean"。我不明白他们什么时候跑。例如,我认为“在清洁期间”在清洁发生时运行并且在“清洁”之后运行,以允许自定义构建器进行自己的清洁后清洁。我认为如果启用了“自动构建”或在“清理”对话框中选中了“立即构建”,那么这将是自动构建。事实并非如此在“清洁”之后实际上是指构建步骤,而不是清理,并且不会跟随 *Auto Build” 步骤(该步骤仅在保存编辑并启用“Build Automatically”时运行。在构建器的 *.launch 文件中,这要多得多很明显 - After a "Clean"实际上被调用full并且Auto and Manual builds 被调用autoincremental. 这意味着除了Auto BuildManual Build之外,en 增强构建器必须设置为在After a "Clean"上运行,并且不应该设置为在“清洁”期间运行。我的错误是仅将其设置为运行 自动手动构建。
  5. [我在这里也错了]我在增强构建器的构建选项选项卡中指定了相关资源的工作集。我将这些资源设置为源代码(待增强)和(包含增强器)。由于这些在大多数构建中都不会更改,因此 Eclipse 选择不运行构建器。现在显而易见的 20-20 愿景事实是,此构建器的相关资源是 Java 构建器的输出二进制文件(和build.gradlebuild.gradle),而不是 Java 源代码。然而,这不是完全正确的选择(孤立地),因为在我们的例子中,Eclipse 以无限循环结束 - 它认为增强器更改了二进制文件,并且由于它设置为在二进制文件更改时运行,因此运行再次构建。我们根本不能不设置相关资源,因为这似乎意味着“一切/任何事情”。增强器必须以这样的方式制作,甚至不接触已经增强的文件[更新],这还不够。继续阅读。

我仍然不确定为什么我用来研究这个的信息构建器将他们的输出附加到公共日志文件中,而不是按时间顺序排列。我只能假设这与 Eclipse 的输出缓冲和定期写入这些文件有关。

[更新 1]

  1. [I DIDN'T KNOW THIS] Eclipse 在Preferences -> Java -> Compiler -> Building -> Output 文件夹中有一个工作区设置(复选框,每个项目都可以覆盖),名为“ Rebuild class files modified by others ”,官方描述为“指示是否已经被别人修改过的class文件,应该重建以撤消修改。 "。默认情况下,它是未选中/关闭的,这似乎适合这种情况。然而,这并不像宣传的那样工作. 无论设置是什么,Eclipse 的 Java Builder 都会对其输出更改做出反应(就好像它们是输入一样,我称之为缺陷)并“撤消修改”,从而导致无限的构建循环。我发现这个症状报告了很多次 - 搜索这个。使用“正确”设置并且没有黑客攻击,Java 构建器会不断撤消修改,并且 Eclipse 会不断重新运行它们,无论设置如何都会导致无限循环。
  2. [HACK 似乎可以正常工作]除了正确设置上述所有内容之外,我还修改了增强器以确保两件事(可能只需要一个或两个都需要,不确定):(a)现有*.class文件不会被删除和重新创建但是重写并且(b)它们的最后修改时间更改回增强之前的时间。即使文件大小不同,这似乎也足以欺骗 Eclipse 的修改检测以跳出循环。这适用于 Eclipse 2019-12 (4.14.0.v20191210-0610),它可能会停止使用任何更新。我希望他们到那时修复无限构建循环缺陷。
于 2020-03-04T21:37:55.347 回答