当一个包从同一项目中的另一个包导入时,我需要使构建失败。我怎么能做得这么干净?

I need to fail the build when a package imports from another package within the same project. How can I do so cleanly?

出于各种原因(不管这是否是一个明智的想法)我们有一个包含多个包的项目(比如 onetwothree)结构相似。还有一个 common 包裹。

我希望构建在每个包中的某些内容导入 common 中不存在的内容时失败,例如因为您不小心接受了错误的导入完成,或者因为 Eclipse 在将错误修复从 one 复制粘贴到 two.

时悄悄带来了错误的导入

有效的方法是将所有源文件(每个包除外)复制到一个临时文件夹并尝试构建结果:

<target name="enforce-no-cross-imports">
    <phony-build without="one"/>
    <phony-build without="two"/>
    <phony-build without="three"/>
</target>

<macrodef name="phony-build">
    <attribute name="without" />
    <sequential>
        <echo message="Checking there are no cross imports to the @{without} package." />
        <mkdir dir="${java.io.tmpdir}/my-awesome-project/phony-build-@{without}/src" />
        <mkdir dir="${java.io.tmpdir}/my-awesome-project/phony-build-@{without}/bin" />
        <copy todir="${java.io.tmpdir}/my-awesome-project/phony-build-@{without}/src">
            <fileset dir="${src.dir}">
                <include name="**/*.java"/>
                <exclude name="**/@{without}/"/>
            </fileset>
        </copy>
        <javac srcdir="${java.io.tmpdir}/my-awesome-project/phony-build-@{without}/src"
               destdir="${java.io.tmpdir}/my-awesome-project/phony-build-@{without}/bin"
               classpathref="classpath" encoding="UTF-8" nowarn="on"/>
        <delete dir="${java.io.tmpdir}/my-awesome-project" />
    </sequential>
</macrodef>

但是,当构建 DOES 中断时,错误显然发生在 ${java.io.tmpdir} 内部。

enforce-no-cross-imports:
     [echo] Checking there are no cross imports to the one package.
    [mkdir] Created dir: C:\Users\me\AppData\Local\Temp\my-awesome-project\phony-build-one\src
    [mkdir] Created dir: C:\Users\me\AppData\Local\Temp\my-awesome-project\phony-build-one\bin
     [copy] Copying 47 files to C:\Users\me\AppData\Local\Temp\my-awesome-project\phony-build-one\src
    [javac] Compiling 47 source files to C:\Users\me\AppData\Local\Temp\my-awesome-project\phony-build-one\bin
    [javac] C:\Users\me\AppData\Local\Temp\my-awesome-project\phony-build-one\src\my\awesome\project\two\very\long\path\SomeController.java:43: package my.awesome.project.one.very.long.path.SomeConstantsClass does not exist
    [javac]     public static final String TEST = my.awesome.project.one.very.long.path.SomeConstantsClass.TEST;

导致无法点击文件名直接跳转到issue。这给编译过程增加了太多的摩擦:这已经够糟糕的了,我实际上将项目构建时间增加了三倍。

我认为我可以做的是 复制文件并在 javac 任务上使用 excludes 属性:

excludes: Comma- or space-separated list of files (may be specified using wildcard patterns) that must be excluded; no files (except default excludes) are excluded when omitted.

...除了这 不会 导致构建中断。发生的情况是 javac 不会编译其他包,但它仍会查看它们以进行确认。打开 verbose 显示此日志行:

    [javac] [checking my.awesome.project.one.very.long.path.SomeConstantsClass]

includeDestClasses 似乎是另一个有用的标志,但将其设置为 false 并没有帮助。 <compilerarg value="-implicit:none"/> 也没有帮助。

我如何告诉 javac 它确实确实需要从构建中排除这些文件,即使它们就在那里?

或者,我如何获取 javac 的输出并对其进行 运行 查找替换,以便控制台输出显示正确的路径?

您可以使用 ant-contribcompilewithwallsverifydesign 任务,但像这样强制分离的最安全方法是将模块隔离到单独的源目录中并独立编译它们.您将首先构建公共模块,然后在其类路径上使用 "common" 的已编译 类 构建每个其他模块,但 而不是 类 任何其他模块。当您使用更高级别的构建工具(如 Maven)进行构建时,这是默认方法。如果你有这样的目录结构:

  • build.xml
    • 库 JAR 文件
  • 普通
    • 源码
  • 一个
    • 源码
  • 两个
    • 源码

那么您可以将构建构建为

<macrodef name="compile-module">
  <attribute name="module" />
  <element name="depends" implicit="true" optional="true"/>
  <sequential>
    <javac srcDir="@{module}/src" destDir="@{module}/classes"
           encoding="UTF-8" nowarn="on">
      <classpath>
        <path refid="classpath" />
        <depends/>
      </classpath>
    </javac>
  </sequential>
</macrodef>

<compile-module name="common" />
<compile-module name="one">
  <pathelement location="common/classes" />
</compile-module>
<compile-module name="two">
  <pathelement location="common/classes" />
</compile-module>
<compile-module name="three">
  <pathelement location="common/classes" />
</compile-module>

您在问题中提到了 Eclipse,因此您需要在那里做类似的事情。 Eclipse 允许一个项目的根目录位于另一个项目中,因此您可以创建一个主 Eclipse 项目 "myapp-common" 指向您的项目根目录,仅将 common/src 作为其源目录(而 common/classes 作为相应的输出目录),然后创建单独的 "myapp-one"、"myapp-two" 等项目,根植于 onetwo 等文件夹,每个项目都取决于 "myapp-common"项目。这样你就永远不会让 Eclipse 提供 "wrong" 自动完成,因为项目一根本看不到项目二,反之亦然。