为什么没有人使用 make for Java?

我见过的几乎所有 Java 项目都使用 Maven 或 Ant。它们是很好的工具,我认为任何项目都可以使用它们。但是 制造呢?它用于各种非 Java 项目,并且可以轻松地处理 Java。如果使用 Windows,当然必须下载 make.exe,但 Ant 和 Maven 也不附带 JDK。

在 Java 中使用 make 是否有一些根本性的缺陷?只是因为 Ant 和 Maven 是用 Java 编写的吗?

60146 次浏览

Ant 和后来的 Maven 被设计用来解决由 Make引起的一些令人头疼的问题(同时在这个过程中创建新的问题)。

不久之后,一些开源 Java 项目意识到 Ant 可以解决他们在 Makefile 上遇到的问题... ..。

来自 http://ant.apache.org/faq.html#history

他们是否解决了任何问题或只是创建了一个额外的格式来学习是一个主观的主题。事实上,几乎每一项新发明的历史都是这样的: 创造者说它解决了很多问题,而原始用户说这些都是美德。

它的主要优点是可以与 Java 集成。

我想类似的历史也可能发生在 rake身上。

值得尊敬的 make程序可以很好地处理单独编译的语言,比如 C 和 C + + 。编译一个模块,它使用 #include来拉入其他包含文件的文本,并写入一个对象文件作为输出。编译器在很大程度上是一个一次一个的系统,有一个单独的链接步骤来将目标文件绑定到一个可执行的二进制文件中。

但是,在 Java 中,编译器必须实际使用 import导入的其他类 编译。虽然可以编写一些东西,从 Java 源代码中生成所有必要的依赖项,这样 make就可以按照正确的顺序一次构建一个类,但是这仍然不能处理循环依赖项之类的情况。

Java 编译器还可以通过缓存其他类的已编译结果来提高效率,同时编译依赖于已编译结果的进一步类。这种类型的自动依赖性评估在单独使用 make时是不可能的。

Ant 和 Maven 从一个更“现代”的角度来处理构建依赖关系图和它的管理... ... 但是正如 Oscar 所说,他们在尝试使用 make 解决旧问题时创建了自己的问题。

Maven (和启用 Ivy 的 Ant 设置)比 make 解决的主要问题之一是自动依赖项解析和依赖项 jar 的下载。

这个问题是基于一个不正确的假设: 大量的开发人员 使用 make。见 Java 构建工具: Ant vs Maven。至于为什么开发人员 不会使用 make: 许多开发人员要么从来没有使用过 make,要么使用它,并讨厌它的火焰燃烧超过一千个太阳。因此,他们使用替代工具。

其他所有关于每种技术优点的回答都是正确的。AntMaven可能比 make 更适合 Java,或者正如 Hank Gay 指出的那样,它们可能不适合:)

但是,您问过 Ant 和 Maven 是用 Java 编写的是否重要。尽管在 StackOverflow 上我们不考虑这样的想法(关闭!与编程无关!等等) ,当然,这是事情的一部分。在 Rails 中,我们使用 Rake,C 伙计使用 make,在 Java 中,我们使用 Ant 和 Maven。尽管 Ant 或 Maven 开发人员可能会比其他人更好地照顾 Java 开发人员,但是还有一个问题: 您用什么编写 Ant 任务?爪哇咖啡。如果您是一名 Java 开发人员,这很容易适应。

所以,是的,其中一部分是使用用工具编写的语言编写的工具。

Make 和 Java 的基本问题是 Make 工作的前提是您指定了一个依赖项,然后指定了一个规则来解决这个依赖项。

对于基本 C 语言,通常“要将 main.c 文件转换为 main.o 文件,请运行“ cc main.c”。

你可以在 java 中做到这一点,但是你很快就会学到一些东西。

大多数情况下,javac 编译器启动很慢。

区别在于:

javac Main.java
javac This.java
javac That.java
javac Other.java

还有

javac Main.java This.java That.java Other.java

就是日日夜夜。

数百个类加剧了这种情况,它就变得站不住脚了。

然后结合 Java 倾向于被组织成目录中的文件组这一事实,与 C 和其他倾向于更扁平化结构的文件组相比。Make 对处理文件的层次结构没有太多的直接支持。

Make 也不太擅长在集合级别确定哪些文件过期。

使用 Ant,它将遍历并汇总所有过期的文件,然后一次性编译它们。Make 将简单地调用每个文件上的 java 编译器。让 Make NOT 这样做需要足够的外部工具来真正显示 Make 不能完全胜任这项任务。

这就是为什么像 Ant 和 Maven 这样的替代品会崛起。

我从来没有使用过 GNUMake for Java 项目,但是我曾经使用过 JMK。遗憾的是,它自2002年以来就没有更新过。

它有一些特定于 Java 的功能,但是足够小,可以包含在源代码 tarball 中,而不会显著增加其大小。

现在,我只是假设与我共享代码的任何 Java 开发人员都安装了 Ant。

一个很大的原因是,Ant 和 Maven (以及大多数面向 Java 的 SCM、 CI 和 IDE 工具)都是由 Java 开发人员用 Java 编写的。这使得集成到您的开发环境变得更加简单,并且允许 IDE 和 CI 服务器等其他工具在构建/部署基础结构中集成部分 ant/maven 库。

使脚本倾向于本质上依赖于平台。Java 应该是独立于平台的。因此,如果构建系统只能在一个平台上运行,那么对于多平台的源库来说就是一个问题。

曾经,我在一个使用 gmake 的 Java 项目中工作。我的记忆是模糊的,但 IIRC 我们有一个艰难的时间处理包目录结构,javac 期望。我还记得构建 JAR 文件是一件很麻烦的事情,除非您有一些琐碎的事情。

Ant 是对 Makefiles 的一个面向 XML 配置的改进,而 Maven 是对 Ant 的一个依赖构建工具的改进。有些项目使用这三种方法。我认为 JDK 项目过去常常混合使用 makefile 和 ant。

我认为最有可能的解释是,在一个关键时期(20世纪90年代末) ,有几个因素阻碍了在 Java 社区中使用 make:

  1. 因为 Java 包含多个平台,Java 程序员通常不像只能使用 Unix 环境的程序员(例如 C 和 Perl 程序员)那样擅长使用 Unix 工具。请注意,这是一般情况。毫无疑问,现在和过去都有天赋的 Java 程序员对 Unix 有着深刻的理解。
  2. 因此他们不太擅长使用 make,也不知道如何有效地使用 make。
  3. 虽然可以编写一个简短的 Makefile 来有效地编译 Java,但是需要特别注意以一种独立于平台的方式进行编译。
  4. 因此,对本质上独立于平台的构建工具有很大的需求。
  5. 就是在这个环境中创建了 Ant 和后来的 Maven。

简而言之,虽然 make 几乎可以肯定地用于 Java 项目,但是有一个机会让它成为事实上的 Java 构建工具。那一刻已经过去了。

除非我是无名小卒,否则假设没有人(错误地)使用 make for java 是错误的。

“使用 GNUMake 管理项目”(可在 GFDL 下获得)包含一个完整的章节,专门介绍如何在 Java 项目中使用 make

由于它包含了一个长长的(希望是公平的)列表,列出了使用 make 而不是其他工具的优缺点,你可能会想看一看。(见: http://oreilly.com/catalog/make3/book/)

实际上,make 可以用一个命令处理所有过时 Java 文件的重新编译。如果您不想编译目录中的所有文件或者想要一个特定的顺序,请更改第一行..。

JAVA_FILES:=$(wildcard *.java)
#
# the rest is independent of the directory
#
JAVA_CLASSES:=$(patsubst %.java,%.class,$(JAVA_FILES))


.PHONY: classes
LIST:=


classes: $(JAVA_CLASSES)
if [ ! -z "$(LIST)" ] ; then \
javac $(LIST) ; \
fi


$(JAVA_CLASSES) : %.class : %.java
$(eval LIST+=$$<)

ApacheAnt 一点也不像 Make。Make 是关于描述文件之间的依赖关系,以及如何构建文件。Ant 是关于“任务”之间的依赖关系,实际上更像是一种将构建脚本粘合在一起的方法。

它可能有助于你 AntVsMake

简短的回答: 因为 make不好,即使是在 C 语言的前端,你也会看到许多替代品出现。

长话短说: make有几个缺陷,几乎不适合编译 C 语言,完全不适合编译 Java。如果愿意,您可以强制它编译 Java,但是可能会遇到问题,其中一些问题没有合适的解决方案或变通方法。这里有一些:

依赖解析

make固有地期望文件之间具有类似树的依赖关系,其中一个文件是构建多个其他文件的输出。在处理头文件时,这在 C 语言中已经适得其反。make需要生成一个特定于 make的包含文件,以表示 C 文件对其头文件的依赖关系,因此对后者的更改将导致重新构建前者。但是,由于 C 文件本身不会重新创建(只是重新构建) ,make 通常需要将目标指定为 .PHONY。幸运的是,GCC 支持自动生成这些文件。

在 Java 中,依赖关系可以是循环的,而且没有用于以 make格式自动生成类依赖关系的工具。相反,antDepend任务可以直接读取类文件,确定导入哪些类,如果其中任何类过期,则删除该类文件。如果没有这些,任何重要的依赖关系都可能导致您被迫使用重复的干净构建,从而消除使用构建工具的任何优势。

文件名中的空格

虽然 Java 和 C 都不鼓励在源代码文件名中使用空格,但是在 make中,即使空格位于文件路径中,也可能出现问题。例如,考虑一下,如果源代码存在于 C:\My Documents\My Code\program\src中。这足以打破 make。这是因为 make将文件名视为字符串。ant将路径视为特殊对象。

扫描生成文件

make要求显式设置为每个目标生成哪些文件。ant允许指定要自动扫描源文件的文件夹。这看起来似乎是一个小小的便利,但是考虑到在 Java 中每个新类都需要一个新文件。向项目中添加文件很快就会成为一个大麻烦。

make最大的问题是:

Make 依赖于 POSIX

Java 的座右铭是“到处运行一次编译”。但是将这种编译限制在基于 POSIX 的系统中(在这种系统中 Java 支持实际上是最差的)并不是我们的初衷。

make中的构建规则本质上是小的 bash脚本。即使有一个到 Windows 的 make端口,为了使其正常工作,它必须与一个包含文件系统的 POSIX 模拟层的 bash端口绑定。

这种情况有两种:

  1. MSYS试图将 POSIX 转换限制到文件路径,因此在运行外部工具时可能会出现不愉快的陷阱。

  2. cygwin提供了一个完整的 POSIX 仿真,但是最终的程序仍然依赖于这个仿真层。

因此,在 Windows 上,标准的构建工具甚至根本不是 make,而是 MSBuild,它也是一个基于 XML 的工具,原则上更接近于 ant

相比之下,ant是用 Java 构建的,可以在任何地方运行,并且包含称为“任务”的内部工具,用于以独立于平台的方式操作文件和执行命令。它的通用性足以让您在 Windows 中使用 ant比使用 make更容易地构建 C 程序。

最后一个小问题:

即使 C 程序本身也不使用 make

您可能最初没有注意到这一点,但是 C 程序通常不附带 Makefile。它们附带了 CMakeLists.txtbash配置脚本,该脚本生成实际的 Makefile。相比之下,使用 ant构建的 Java 程序的源代码是预先构建的 ant脚本。Makefile是其他工具的产品-这就是为什么 make不适合单独作为构建工具。ant是独立的,它可以处理 Java 构建过程所需的所有内容,而不需要任何额外的要求或依赖项。

当您在任何平台上运行 ant时,它只是工作(tm)。用 make是不可能做到的。它极大地依赖于平台和配置。