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


162

我见过的几乎每个Java项目都使用Maven或Ant。它们是很好的工具,我认为几乎任何项目都可以使用它们。但是,什么都发生在使?它用于各种非Java项目,并且可以轻松处理Java。当然,如果使用Windows,则必须下载make.exe,但JDK并未随附Ant和Maven。

与Java一起使用时,make是否存在一些基本缺陷?仅仅是因为Ant和Maven是用Java编写的吗?


37
如果您四处移动东西,而不仅仅是调用编译器(甚至是调用编译器),那么特定于平台的东西将很难处理make。对于跨平台语言来说,拥有仅在一个系统上可用的makefile并不是一件好事。
乔伊,2010年

顺便说一句,Gradle和Gant(在较小程度上)都是Java领域的新参与者。Maven或Ant是错误的二分法。
迈克尔·复活节

@Michael Easter,错误的二分法是Maven / Ant vs. Make。如果我没看错的话,真正的二分法应该是Gradle / Maven / Gant / Ant vs. Make。但这很难说:)
Dan Rosenstark 2010年

Answers:


192

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不执行此操作需要足够的外部工具才能真正表明Make不能完全胜任该任务。

这就是为什么诸如Ant和Maven之类的替代产品兴起的原因。


6
因此,为了在庞大的Java项目中使用make,有必要维护所有已更改的.java文件的列表,然后在末尾调用javac吗?这对我来说听起来不理想。这是我到目前为止看到的最好的答案。
User1 2010年

32
我喜欢这个。本质上,您说的是需要Ant来解决javac太慢的事实。
cmcginty 2010年

25
不可以。如果使用Javac,它的运行速度并不慢,因为它被设计成可以使用的。如果一次使用它来编译一个文件,则速度很慢。
Stephen C

7
@Casey javac不太慢。JVM启动太慢。
托尔比约恩Ravn的安德森

7
GNU Make至少具有$?自动变量,该变量扩展为“比目标更新的所有先决条件”。模式规则还具有多个目标的功能,这些目标仅运行一次配方即可更新所有.class文件。将其与文件,文本功能(如$(wildcard),)的一些巧妙用法结合使用$(shell)$(eval)您可以教会makefile查找分散在目录布局中的构建目标。
Tanz87

33

这个古老的make程序可以很好地处理C和C ++等单独编译的语言。您编译一个模块,该模块#include用于提取其他包含文件的文本,并写入单个目标文件作为输出。编译器是一个一次性的系统,具有单独的链接步骤,用于将目标文件绑定到可执行二进制文件中。

但是,在Java中,编译器必须实际编译使用导入的其他类import。尽管可以编写一些东西来从Java源代码生成所有必要的依赖关系,因此make可以一次以正确的顺序构建类,但这仍然无法处理诸如循环依赖关系之类的情况。

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


7
这似乎更像是make vs javac的答案,而不是make vs ant / maven的答案。根据您的答案,为什么有人不能只使用make + javac(一次给javac一个完整的包或“模块”,因此make中隐藏了循环依赖项)?蚂蚁或行家会为该方法带来任何好处吗?
劳伦斯·贡萨尔维斯

1
@Laurence:您可以一次给javac一个完整的程序包,但是它将重新编译该程序包中的所有内容(因为这就是您要执行的操作)。确实,Java编译器非常快,但是如果让它确定更改某些类后重新编译所需的最小类,则它甚至更快。
Greg Hewgill'2

您是否要告诉Javac只编译“主”类,然后让它自动构建依赖的东西?最后我检查了一下(诚然,可能在1.4中),这非常不可靠。-Xdepend稍好一些(但速度较慢,但​​仍然损坏),他们在1.3中删除了该标志。
劳伦斯·贡萨尔维斯

4
此外,这仍然不能解释为什么我要使用Ant或Maven而不是直接使用javac ...
Laurence Gonsalves 2010年

28

该问题基于错误的假设:大量开发人员确实使用make。请参阅Java构建工具:Ant与Maven。至于为什么使用make开发人员:许多开发人员从未使用过make,或者使用它并以燃烧超过一千个太阳的烈火来讨厌它。因此,他们使用替代工具。


10
我们用它,火比一千一百个太阳还热。
10年

4
@reccles:它只是对建筑工程的仇恨还是自己制造?Ant,Maven或其他方法如何会更好(即,使该类成为一个糟糕的工具)?
User1 2010年

5
@ User1 make具有许多“功能”,这些功能在编写时可能就很有意义了,但现在更像是错误,例如,您必须在某些位置使用TAB字符而不是空格。那种事情可能不会打扰那些真正有经验的人make,但它会使我们其他人发疯。
Hank Gay

4
@HankGuy:让您的编辑者担心这些细节。如果您的编辑器不能正确处理制表符<->空间设置,请换一个新的编辑器,您会更快乐。但是您说的很对,许多功能已经过时,例如处理动态依赖项的方式(make depend有人吗?)
D.Shawley 2010年

3
@ User1这是我们正在构建的项目的规模。我们有专职人员维护make文件和构建依赖项。使用过Maven之后,我发现它更易于管理。现在已经说过,行家也不是完美的。尝试找出与规定的设置稍有不同的构建所需的XML设置,没有什么比这更令人生气了。
10年

28

实际上,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+=$$<)

4
真好!我正在寻找类似的东西。通用经典版可以正常工作时,无需为每种语言使用不同的构建工具。谢谢!
rsp

17

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

但是,您问过用Java编写Ant和Maven是否重要。尽管在StackOverflow上我们不考虑这样的想法(已关闭!与编程无关!等等),但是这当然是其中的一部分。在轨道上,我们使用Rake,C dudes使用make,在Java中,我们使用Ant和Maven。虽然Ant或Maven开发人员确实会比其他开发人员更好地照顾Java开发人员,但还有另一个问题:您在其中编写Ant任务是什么?Java。如果您是Java开发人员,那很容易。

是的,部分原因是使用以您使用的语言编写的工具。


7
当Java社区迷恋XML时,Ant也应运而生。(这并不是说XML没有位置。)
Laurence Gonsalves 2010年

3
@Laurence贡萨尔维斯,这是如此真实。但是,请不要在SO上谈论时尚:)当时我在教Java开发人员,而一切都是XML。现在它仍然是XML,但是没人在乎。
Dan Rosenstark'2

1
工具注释是一个有趣的注释。make来自UNIX背景,因此可以通过编写有用的紧凑型实用程序并将它们一起管道化来完成该工具。这就是为什么大多数拼接是使用Shell命令完成的原因。
D.Shawley'2

2
@D。Shawley,关于make小型Unix实用程序的所有说法都是对的。GIT也是该过程的孩子。就个人而言,我不会说这不是更好。但这对于Java程序员来说是一个巨大的范式转变。Ant与Java的思维方式更为一致。
丹·罗森斯塔克

12

蚂蚁和后来的Maven旨在解决某些头痛Make(在此过程中创建新头痛),这只是进化。

不久之后,几个开源Java项目意识到Ant可以解决Makefiles所带来的问题。

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

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

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

我想类似的历史也会 rake


4
这不是很具体。Maven解决了由make引起的哪些头痛问题?
劳伦斯·贡萨尔维斯

1
@贡萨尔维斯:嗯,这是每项新技术的主观方面之一,替代技术的创造者说它解决了很多问题,而替代技术的创造者说这些不是缺陷,而是美德等等。我认为在这种特殊情况下,Java集成和交叉编译是开箱即用的
OscarRyz 2010年

2
[参考您对答案的编辑,而不是您最近的评论]仍然不能解释解决了什么问题,只是创建者ant声称问题已解决...:-/我的印象是Ant最初是为了创建成为Make的简单替代方法。随着时间的流逝,人们发现它缺少一些东西,因此他们添加了功能,直到Ant变得和make一样复杂,但是内置了binutils(make严重依赖于cp,rm等外部工具),并且当然有XML语法。
劳伦斯·贡萨尔维斯

2
是的,但是当新的替代方法的创建者能做的最好的事情是说“这解决了旧的替代方法所遇到的问题”时,却没有实际说出这些问题对那些考虑使用哪种选择的人没有太大帮助。Ant会解决在make上遇到的问题,还是会在为我介绍新问题时解决我认为不是问题的事情?
劳伦斯·贡萨尔维斯

9

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


6

我认为最可能的解释是,在关键时期(1990年代后期),有几个因素阻止了Java社区中对make的使用:

  1. 因为Java包含多个平台,所以Java程序员通常不像通常局限于Unix环境的程序员(例如C和Perl程序员)那样擅长使用Unix工具。请注意,这是一般的。毫无疑问,有并且有天赋的Java程序员对Unix有深刻的理解。
  2. 因此,他们不太擅长make,也不知道如何有效地使用make。
  3. 尽管可以编写一个简短而简单的Makefile来有效地编译Java,但是需要采取与平台无关的方式来特别小心。
  4. 因此,有一种对于本质上与平台无关的构建工具的需求。
  5. 正是在这种环境中创建了Ant和后来的Maven。

简而言之,尽管make最肯定可以用于Java项目,但仍有一点机会使其成为事实上的Java构建工具。那一刻过去了。


5

Make脚本倾向于固有地依赖于平台。Java应该是平台无关的。因此,拥有一个只能在一个平台上运行的多平台源库构建系统是一个问题。


5

简短答案:因为 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脚本。即使有makeWindows 的端口,它也必须与端口捆绑在一起,才能使Windows正常工作,该端口bash包括用于文件系统的POSIX仿真层。

这有两个变种:

  1. MSYS 它试图将POSIX转换限制为文件路径,因此在运行不是专门为其设计的外部工具时可能会遇到麻烦。

  2. cygwin它提供了完整的POSIX仿真。但是,生成的程序仍倾向于依赖该仿真层。

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

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

最后一个未成年人:

甚至C程序也不会原生使用make

您最初可能不会注意到这一点,但是C程序通常不附带Makefile。它们附带了CMakeLists.txtbash生成实际脚本的配置脚本Makefile。相反,使用构建的Java程序的源代码ant附带了ant预先构建的脚本。A Makefile是其他工具的产物-这make本身不适合用作构建工具。ant是独立的,可处理Java构建过程所需的一切,而无需任何其他要求或依赖性。

ant在任何平台上运行时,它都可以正常工作。您无法使用来实现make。它与平台和配置的依赖程度令人难以置信。


4

除非我不是一个人,否则没有人会错误地使用make for java是错误的。

“使用GNU Make管理项目”(在GFDL下可用)包含一整章专门讨论make与Java项目一起使用。

由于它包含一长串(并且希望很公平)使用make代替其他工具的利弊清单,因此您可能想看看那里。(请参阅:http : //oreilly.com/catalog/make3/book/


这是准确的摘要吗?make(各种口味)可用于Java,但有些麻烦。AntMaven(和jmake吗?)做了Java需要/喜欢的一些特殊事情,以使Java项目更快,更容易构建。它们还可以用于非Java项目的与平台无关的构建过程,但是针对Java进行了更多调整。
Phil Perry

3

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


1

一个很大的原因是Ant和Maven(以及大多数针对Java的SCM,CI和IDE工具)都是由Java开发人员/为Java开发人员编写的。这样可以更轻松地集成到您的开发环境中,并允许其他工具(例如IDE和CI服务器)在构建/部署基础架构中集成部分ant / maven库。


1

从前,我从事使用gmake的Java项目。我的回忆很模糊,但是IIRC很难处理javac期望的软件包目录结构。我还记得,除非您有一些琐碎的事情,否则构建JAR文件很麻烦。


1

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

它可以帮助您AntVsMake


我真的很想知道投票否决的原因。
hagello

0

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


0

我从未将GNU Make用于Java项目,但曾经使用jmk。遗憾的是,自2002年以来就没有进行过更新。

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

如今,我只假定与我共享代码的任何Java开发人员都已安装Ant。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.