使用 Maven 构建 "out of source"
Building "out of source" with Maven
总结:
我想知道如何在不破坏 IDE (Netbeans) 支持的情况下构建由多个 Maven 项目组成的 Maven 应用程序,所有输入都来自只读源,所有输出都到给定的(临时)文件夹。 None 的建议解决方案对我有用,所以我要求详细详细解释我的问题。
详细:
很长一段时间以来,我一直试图将我的 Maven 构建集成到我们的生产环境中,该环境有几个要求:
- 无法访问互联网(所有输入必须受版本控制)
- 源代码树是只读的
- 所有构建工件必须位于构建目录下
- 定义良好的工具链(在版本控制下或在只读虚拟机中)
- 库不知道哪个应用程序使用它们(即 "no parent POM")
- 支持开发周期,最好与IDEs(Netbeans)一起工作,可选
- 为了支持开发周期,最好是增量工作,
可选
这类似于 Out-of-tree build with maven. Is it possible? 和其他与 maven 构建目录相关的问题,但不幸的是 none 的建议解决方案在这里有效。
我可以通过“file:
”访问 "central repository" URL 以及(临时)构建目录下的 "local repository",但我没有找到在不破坏任何其他内容的情况下,有任何方法可以在构建目录下放置 Mavens "target" 目录。
我有几个共享一些库的应用程序。对于每个库,我都有自己的 "parent POM"(违反 DRY 原则,因为我找不到更好的方法)。每个都包含特定于应用程序和环境的设置,例如 distributionManagement 存储库路径,它是使用 ${env.variable}
定义的,以允许构建工具注入系统和应用程序特定的值。提供默认值是为了让使用 Netbeans 的开发人员满意。
不幸的是,这不适用于构建目录。
我可以像 Maven: How to change path to target directory from command line? 那样设置构建目录。然后,由一个父 POM 构建的所有 class 个文件(所有库和应用程序的)将被放入同一个目录中。 Maven 可以 运行 一次并且可以工作 - 但在以后的任何 运行 中它都会失败!至少单元测试会失败,因为 Maven (surefire) 会找到所有项目的所有测试,而不仅仅是 Maven 当前正在处理的那个。因此,它将尝试 运行 在构建库 A 时从应用程序进行单元测试(在我的例子中失败,因为它还需要库 B,而库 B 未作为库 A 中的依赖项提供)。
我也试过 "-DbuildDirectory=$BUILDDIR/maven-target/${project.name}"
这样的结构。这适用于源代码的编译,但同样不适用于测试。尽管 Maven (3.1.1) 在存储文件时正确地评估了 ${project.name}
,但它按字面意义 (!) 将其传递给 class 路径,因此在编译单元测试时 java 找不到测试对象("error: cannot find symbol")。具有正确名称的符号链接 ${project.name}
当然仅适用于一个项目(例如库 A),因为我找不到如何从 pom.xml.
中更改它的方法
目前我最接近的方法是使用全局目标目录并在每次构建之前清理它,但显然这在使用此构建系统进行开发时很糟糕(在没有 IDE 的情况下增量工作)。
现在我什至考虑从构建系统生成 pom.xml 文件并填充值并将所有源复制到构建树。这应该适用于我们定义的发布版本,但在正常开发过程中会不舒服(使用 IDE)。
Maven 有什么技巧吗?或者这真的不可能吗?
已编辑以回答问题
非常感谢您花时间解决我的问题/帮助请求!我尽量回答清楚。
一位队友建议为这一切添加动力:
应该可以在 10 年或 20 年内复制任何版本,即使互联网发生了变化。
Why do you need to change the value of "target"?
因为源代码树是只读的,Maven 无法在其默认位置创建目录。可写构建目录可用,但在源代码树之外。
Maven will not write to /src, so no problem there
"source tree" 不引用构建过程的源/输入中的 "src" 文件夹,而是引用整个结构 - 包括 POM 文件、资源甚至二进制库(如果需要)。所以包含src的目录也包含pom.xml并且属于只读VCS控制"source tree".
Could you give an example of something that does not work with the standard project layout?
这种只读源代码树的一个不同示例可能是:将整个项目结构(带有库的应用程序)刻录到 CD-R 上并确保 "mvn compile test package deploy" 有效。
(只读源代码树要求的一个动机是确保构建过程不会意外(或故意)操纵源代码,这可能会导致根据重复次数自动更改构建工件,从而破坏可重复性.)
using a repository manager
据我了解,存储库管理器是一种复杂的服务,通常 运行 位于主机上。我认为使用 file URLs 而不是如此复杂的服务可以使依赖关系更小。请注意,整个存储库必须进行版本控制,因此无论如何自动更新都不能工作,但据我所知,这是存储库管理器的优势。
Do you use dependency jar's
是的,当然。库和应用程序依赖于多个 JAR。甚至 Maven 也依赖于几个 JAR,例如编译器插件。
If your request states that you need to build all dependencies yourself ?
实际上,我担心情况可能是这样,是的,不幸的是,我知道这将需要付出巨大的努力。即使使用 Debian 软件包,这也很难实现,他们付出了很多努力来实现它。不幸的是,目前我没有看到构建这些库的现实机会。
"...all build artifacts must be below a build directory"- Can you please explain that in detail?
构建过程应该在给定构建目录下创建所有文件,例如 /tmp/$USER/build/$PROJECTNAME/$PROJECTVERSION/$APPLICATION/$VARIANT/
。构建系统可以在其下创建任意结构,但不应更改此之外的任何内容。此树中的文件列表被定义为输出(构建结果)并从那里获取。通常一些二进制文件或 ZIP 文件被复制到版本控制系统。
有些人称之为 "out of tree build"、"build out of source" 或类似的。
well defined toolchain
想法是拥有一个虚拟机映像(在版本控制下),为每个构建创建一个动态的 "clone",安装所需的工具链(来自版本控制),例如构建工具, Maven 安装及其存储库,提供对要构建的特定版本源的访问权限(例如,以只读 ClearCase 视图的形式),运行 构建脚本入口点脚本,收集创建的输出(用于例如一些 "release.zip" 和 "buildlogs.zip") 最后丢弃整个虚拟机,包括它的所有内容。当然,工具链可以完全预装在映像中,只是出于实际原因没有这样做(是的,我们也使用 Docker)。
For impact analysis it's very important thing to know who is using which library ?
是的,这确实是真的。但是,来源应该从中抽象出来,而且通常会这样做。例如,JUnit 的作者并不认识每个使用它的人。我们喜欢在几个项目中同时使用一个库,所以不能提一个父POM,因为有多个父POM。
希望这次我能解释得更好。
编辑 #2 以回答新问题
再次感谢您花时间度过这么长的时间post!我希望这次我能够解释最后遗漏的部分并说清楚。我认为有些问题似乎是对要求的评论。恐怕我们会陷入讨论。
In maven you have a directory structure like:
root
+- pom.xml
+- src
The root directory is under version control. So I don't understand the following:
"source tree" does not reference to the "src" folder within the
sources / inputs for the build process, but to the whole structure -
root
+- pom.xml
+- src
+-- ...
+- target
+-- ...
我在结构中添加了 target
" 以更好地说明问题。正如您所说,根目录受版本控制,因此是只读的。因此,root/pom.xml
、root/src
和 root
本身是只读的。
当 Maven 尝试创建 root/target
时,它会得到一个错误 (Read-only file system
),因为 root
及其所有子文件夹都是只读的。
这里甚至父文件夹都在版本控制之下,在 ClearCase 的情况下,还有更多的只读父目录,但我认为这在这里无关紧要。
We are talking about terra bytes of artifacts....They have to be backed up but not checked into version control.
这超出了范围,但我还是尽量回答。
例如,我现在正在处理的一个 ClearCase 实例有 2.2 TB,确切地说是万亿字节。例如,如果标准要求对发布之间的所有更改进行可追溯,则备份可能还不够。但是我认为这超出了我的问题范围。
a file base repository does not work well and needed to be copied to each machine where you use it which is a hassle having a kind of network file system which is mounted to many machines. A repository manager works http(s) based which much more simpler to handle..
我认为这超出了范围并且与我的问题无关,但我还是尝试回答。
问题在于,为了可重复性,您需要在接下来的十年或二十年内将此 http(s) 的所有内容存档并保留 运行ning。有历史,因为可能需要重现五年前的状态。当然,所需的软件包可能不再在 Internet 上可用(还记得 Codehaus.org 吗?)。具有挑战性的任务。
a repository manager is much more simpler and keeps the conventions
不幸的是它不满足要求,或者要求高价(管理额外的虚拟机加上存储包的版本控制)。
building all dependencies yourself I think that is simply not worth the effort. I don't a real advantage of that
我认为这样的需求并不少见(https://en.wikipedia.org/wiki/Source_code_escrow),但恐怕我们跑题了,开始讨论需求了。
If the JUnit team can access the request from maven central (which can be done) they can see who is using JUnit
我认为这是题外话,但是 JUnit 开发人员应该如何了解所有 "my" 个使用 JUnit 的项目?
Where is the relationship to the parent pom?
如果我造成了混淆,我很抱歉,这只是一个旁注。我认为这超出了我的问题范围。不过我会尽量回答的。
为了避免冗余,Maven 中的一种方法是使用父 POM 并从中继承。例如,您可以在父 POM 中定义 distributionManagement
并使库 POM 继承它。当您有项目特定的 distributionManagement
要求时,您需要项目特定的父 POM 文件。如果两个项目共享同一个库,那么库应该继承两个项目 POM 中的哪一个(作为父项)?在聚合器 POM 中设置 distributionManagement
不起作用,因为它不会传播。
编辑 #3 解释只读
你好@JF Meier
谢谢你的评论。
只读意味着不能写入,所以Maven不能创建目标目录。因此,javac 无法存储 class 文件并且编译中止。
可能太抽象了,我来举个完整的真实例子吧:
cat pom.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>test</groupId>
<artifactId>mavenproject1</artifactId>
<version>1.0-SNAPSHOT</version>
</project>
cat src/main/java/Hello.java
:
public class HelloWorld {
public static void main(String[] args) { System.out.println("Hello, World!"); }
}
现在从 shell 复制并粘贴(我剪了一些无聊的行):
steffen@node1:/view/dev_test_project_v1/vobs/playground/steffen/minimal_pom $ mvn compile
[...]
[ERROR] Failure executing javac, but could not parse the error:
javac: directory not found: /view/dev_test_project_v1/vobs/playground/steffen/minimal_pom/target/classes
这是一个问题。我必须纠正一个小细节,它没有显示 Read-only file sytem
错误,我认为是一个错误,但在 strace 中我们可以看到它:
21192 mkdir("/view/dev_test_project_v1/vobs/playground/steffen/minimal_pom/target/classes", 0777) = -1 ENOENT (No such file or directory)
21192 mkdir("/view/dev_test_project_v1/vobs/playground/steffen/minimal_pom/target", 0777) = -1 EROFS (Read-only file system)
21192 mkdir("/view/dev_test_project_v1/vobs/playground/steffen/minimal_pom/target", 0777) = -1 EROFS (Read-only file system)
这会导致 javac: directory not found
错误。
只读:
steffen@node1:/view/dev_test_project_v1/vobs/playground/steffen/minimal_pom $ mkdir -p target/classes
mkdir: cannot create directory ‘target’: Read-only file system
mkdir 工具正确显示错误消息。
没有答案..
第一件事是你不能上网。好的,这可以简单地通过使用存储库管理器来完成。这是 Maven 和公司环境中其他构建工具的默认设置。
您使用依赖 jar 的(库?)吗?
如果您的请求声明您需要自己构建所有依赖项?这将是一项巨大的工作,因此意味着您需要拥有组织内所有库的所有源代码,并且您需要维护所有 updates/changes 给他们..(巨大的努力)。
我不明白你的要求:
source tree is read-only
如果你有一个版本控制系统,通常存储库有身份验证,这意味着你可以控制是否有人能够提交(写入)它..
..all build artifacts must be below a build directory..
能否详细解释一下?
以下是我能理解的:
well defined toolchain (under version version control or in read-only Virtual Machine)
通常你用你使用的工具链定义一个环境(当然应该签入版本控制)。
libraries shall not know which application use them (i.e. "no parent POM")
一个parent的用法不代表一个图书馆不知道谁在使用这个图书馆?但除此之外我不明白这个要求?对于影响分析,了解谁在使用哪个库非常重要?
你提到你已经通过我不推荐的文件协议配置了中央仓库..此外 target
目录是 Maven 的约定...我看不出改变这个有什么好处?这有什么问题?
因此,根据您的更新,我在此处添加另一个更新:
Why do you need to change the value of "target"?
Because the source tree is read-only, Maven cannot create the
directory at its default position. A writable build directory is
available, but outside the source code tree.
您似乎误解了 target
目录的概念,或者您误解了 Maven 的工作原理。
target
目录旨在包含所有 generated/compiled 未 未 签入源代码管理的内容。或者换句话说,target
目录在签入期间默认被忽略,永远不会也不应该被签入版本控制。
Maven will not write to /src, so no problem there
在 maven 中,您的目录结构如下:
root
+- pom.xml
+- src
+-- ...
根目录受版本控制。所以我不明白以下内容:
"source tree" does not reference to the "src" folder within the
sources / inputs for the build process, but to the whole structure -
including POM files, resources and even binary libs if needed. So the
directory that contains src also contains pom.xml and belongs to
read-only VCS controlled "source tree".
进入下一点:
As I understood, a repository manager is a complex service typically
running on a host. I think using file URLs instead of such a complex
service keeps the dependencies smaller. Please note that the whole
repository has to be version controlled, so automatically updating
must not work anyway, but as I understand is the advantage of a
repository manager.
整个存储库应该是版本控制的。听起来您没有在公司环境中进行实际构建的经验。我们正在谈论 terra bytes 的工件....它们必须备份但不检查到版本控制中。
原因很简单,因为每个工件都是由它的坐标唯一定义的,groupId、artifactId、version。
除了你给出的论点之外,设置的复杂性比你想象的要简单。除此之外,基于文件的存储库不能很好地工作,并且需要将其复制到您使用它的每台机器上,这是一种安装在许多机器上的网络文件系统的麻烦。存储库管理器基于 http(s) 工作,处理起来更简单..
进入下一点:
The build process is supposed to create all files below a given build
directory, for example
/tmp/$USER/build/$PROJECTNAME/$PROJECTVERSION/$APPLICATION/$VARIANT/.
Maven 在 target
目录中执行此操作..
The build system can create arbitrary structures below that, but is
not supposed to change anything outside this. A list of files within
this tree is defined as output (build result) and taken from there.
Typically some binaries or ZIP files are copied to version control
system. Some call this "out of tree build", "build out of source" or
similar.
正如我在生成的工件之前提到的,我不建议将它们置于版本控制之下,因为存储库管理器要简单得多并保持约定...
回到您自己构建所有依赖项的观点,我认为这根本不值得付出努力。我没有真正的优势...特别是因为您可以使用来自 Maven 存储库(或来自您组织内部的存储库管理器)的所有基于正确坐标的工件...
下一点:
Yes, this surely is very true. However, the sources should abstract
from it, and usually do. For example, the authors of JUnit dont' know
everybody who is using it.
如果 JUnit 团队可以从 Maven Central 访问请求(可以做到),他们可以看到谁在使用 JUnit。这在组织内部通过使用存储库管理器的访问日志和提取要简单得多使用信息...
我不明白的是:
We like to use a library in several
projects at the same time, so we cannot mention a parent POM, because
there are multiple parent POMs.
这在 Maven 中称为简单依赖项。与父 pom 的关系在哪里?您似乎误解/或不了解 Maven 的工作原理..?
让我将以下内容添加到 khmarbaise 的回答中。
我没有看到您的 "in ten years reproducible" 标准 Maven 结构问题。你只是:
- 从版本控制系统检出源到一个空目录。
- 在此目录中使用 Maven 构建您的项目。
- 将结果部署到任何你想要的地方。
版本控制系统中的源代码没有改变,每次都会完全一样。您只需要确保您只在依赖项中使用发布版本(而不是 SNAPSHOT 版本),这样 Nexus/Artifactory 每次都会给您相同的工件。
使用 Maven 且没有 Internet 的树外构建
如果其他人面临类似的需求,我将以示例的形式描述我的解决方案。我理解并高兴地接受 大多数人肯定不想要这个 或者根本不需要 "out-of-tree" 使用 Maven 构建。这是为像我这样别无选择的极少数人准备的。
所以这不适用于 "normal Maven builds",但仅适用于问题中描述的特定要求(无法访问互联网,除 /tmp 文件夹外的所有内容都是只读的)。这样的配置可以是虚拟机中的构建系统,该虚拟机 运行 来自 VW 模板,由 Jenkins 作业动态实例化。
A word about ClearCase
This approach is not ClearCase specific, but the example uses ClearCase paths.
The following example uses a read-only ClearCase view toolchain_view
mounted to /view/toolchain_view/
(containing Maven, all Java packages and other build tooling) and a read-only source code view (containing an application) available below "/view/steffen_hellojava_source_view/". Instead of "/view/toolchain_view/", someone could use "/opt/vendor/name/toolchain/version/" or such and for the sources use any other version control system, so this approach is not ClearCase-specific.
For those who don't know ClearCase a word about it: The view behaves similar like a NFS file system, where the server selects the file content based on a version description. The file contents itself is in a database called Versioned Object Base (VOB). Using a so called Configuration Specification (CS) someone can define which content version to show for which file (element) in form of selection rules (such as selecting by a LABEL). For example:
---[ toolchain-1_4_7_0.cs ]---------------------------------------->8=======
element /vobs/playvob/toolchain/... TOOLCHAIN_VERSION_1_4_7_0
# TOOLCHAIN_VERSION_1_4_7_0 automatically also selects:
# element /vobs/playvob/toolchain/3p/maven/... TOOLCHAIN_MAVEN_3_1_1_0
# element /vobs/playvob/toolchain/3p/maven-repo/... TOOLCHAIN_MAVENREPO_1_0_1_0
# element /vobs/playvob/toolchain/3p/java/... TOOLCHAIN_JAVA_1_7_0_U60
=======8<-------------------------------------------------------------------
Now a ClearCase view such as "toolchain_view" can be created and configured to use these selection rules cleartool -tag toolchain_view -setcs toolchain-1_4_7_0.cs
. In the example it is mounted as /view/toolchain_view/
, which prefixes the element (file) paths.
配置 Maven
现在我们需要将 Maven 配置为
- 仅将我们的文件结构
/view/toolchain_view/vobs/playvob/toolchain/3p/maven-repo/
用作中央存储库
- 将本地存储库存储在
/tmp
和 下方
target
目录也在 /tmp
下面
前两个可以在 Maven 设置文件中配置,但不幸的是,显然最后一个似乎需要在 POM 文件中进行特定更改。
mvn_settings.xml
此处是 Maven 的 --global-settings
文件的摘录。我把它压缩了一点。重要的是对两个中央存储库使用 file:///
URL,并将其作为一个且唯一的镜像强制执行:
<!-- Automatically generated by toolchain creator scripts.
This sets the maven-repo version to the correct value, for example
toolchain version 1.4.7.0 defines Maven repo version 1.0.1. -->
<settings ...>
<localRepository>${env.MAVEN_LOCAL_REPO}</localRepository>
<profiles>
<profile>
<id>1</id>
<activation><activeByDefault>true</activeByDefault></activation>
<repositories><repository>
<id>3rdparty</id><name>third party repo</name>
<url>file:///view/toolchain_view/vobs/playvob/toolchain/3p/maven-repo/</url>
<snapshots><enabled>true</enabled><updatePolicy>never</updatePolicy></snapshots>
<releases><enabled>true</enabled><updatePolicy>never</updatePolicy></releases>
</repository></repositories>
<pluginRepositories><pluginRepository>
<id>3rdparty</id><name>third party repo</name>
<url>file:///view/toolchain_view/vobs/playvob/toolchain/3p/maven-repo/</url>
<snapshots><enabled>true</enabled><updatePolicy>never</updatePolicy></snapshots>
<releases><enabled>true</enabled><updatePolicy>never</updatePolicy></releases>
</pluginRepository></pluginRepositories>
</profile>
</profiles>
<!-- Unfortunately **required** for file-based "central repository": -->
<mirrors><mirror>
<id>dehe_repo_1.0.1</id><name>Vendor Name Central 1.0.1</name>
<url>file:///view/toolchain_view/vobs/playvob/toolchain/3p/maven-repo/</url>
<mirrorOf>*,!3rdparty</mirrorOf>
</mirror></mirrors>
</settings>
为了简化部署,我们可以使用包含版本号的路径,例如 /opt/vendor/name/toolchain/1.4.7.0/mvn_settings.xml
,它可以由自己的 Debian 软件包提供。
通过设置环境变量将 localRepository
指向构建特定的(临时)目录允许在除 "out-of-tree" 构建所需的临时目录之外的所有内容都是只读的情况下进行构建。
使用 IDE (Netbeans) 构建时,我们也可以使用此设置文件,但通常不这样做会更舒服。然而,在这种情况下,有人必须注意不要不小心添加依赖项。如果这些未包含在固定的 Maven Repo 中,构建系统上的编译将中断。
hellojava/pom.xml
为了支持树外构建,我们还需要将 target
文件夹移出只读源代码树(通常它们在 pom.xml
和 [=28= 旁边创建] 在 Java 包目录中,它本身处于版本控制之下,因此在这里是只读的)。这是通过使用两个属性实现的:buildDirectory
和 deployDirectory
。默认的 buildDirectory 是正常的 target
文件夹,因此当不设置 buildDirectory 时,Maven 会正常构建。这很好,因为我们不需要 IDE (Netbeans) 的特定 POM 文件。
Surefire生成单元测试报告,当然也需要到我们的build目录下。
<project>
...
<properties>
<project.skipTests>false</project.skipTests>
<project.testFailureIgnore>false</project.testFailureIgnore>
<buildDirectory>${project.basedir}/target</buildDirectory>
<deployDirectory>defaultDeploy</deployDirectory>
</properties>
...
<build>
<!-- -->
<directory>${buildDirectory}/hellojava</directory>
<!-- -->
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12.4</version>
<configuration>
<skipTests>${project.skipTests}</skipTests>
<testFailureIgnore>${project.testFailureIgnore}</testFailureIgnore>
<workingDirectory>${project.build.directory}/test-run</workingDirectory>
</configuration>
</plugin>
</plugins>
</build>
...
<distributionManagement>
<repository>
<id>build-integration</id>
<name>Deployment Repository</name>
<url>file:///${deployDirectory}</url>
</repository>
</distributionManagement>
...
</project>
综合起来
现在必须通过命令行参数设置此 POM 文件中的属性值,并且必须配置 MAVEN_LOCAL_REPO
,例如:
#!/bin/bash
# Normally in a wrapper which is automatically generated by toolchain creator.
# The path to the pom.xml and build directories then of course are parameters.
export JAVA_HOME="/view/toolchain_view/vobs/playvob/toolchain/3p/java/"
export PATH="/view/toolchain_view/vobs/playvob/toolchain/bin:$PATH"
# or: export PATH="/opt/vendor/name/toolchain/1.4.7.0/bin:$PATH"
: ${MAVEN_LOCAL_REPO:="$HOME/.m2/repository"}
: ${MAVEN_OPTS:="-XX:PermSize=64m -XX:MaxPermSize=192m"}
export MAVEN_LOCAL_REPO="/tmp/steffen-build/hellojava/mavenbuild/repo"
/view/toolchain_view/vobs/playvob/toolchain/3p/maven/bin/mvn -e \
--global-settings /view/toolchain_view/vobs/playvob/toolchain/mvn_settings.xml \
-DbuildDirectory=/tmp/steffen-build/hellojava/mavenbuild/target/ \
-DdeployDirectory=/tmp/steffen-build/hellojava/mavenbuild/output/ \
-Dproject.skipTests \
-f /view/steffen_hellojava_source_view/hellojava/pom.xml \
compile package deploy
现在/tmp
可以是"read-only system"上的RAM盘了。所有工件都写在专用构建目录下。我们可以在 DVD 或只读 NFS 存档服务器上拥有工具链和完整的源代码树,并且仍然可以编译它,而无需访问 Internet。这应该在 20 年后仍然有效,即使 Maven Central 已重命名或其他。
当然,包装脚本可以隐藏所有细节。就我而言,它们集成在基于 cmake 的构建系统中,顶级构建目录由 Jenkins 作业配置。
检查要求
对于原始问题中的每个要求,我们可以检查此方法是否满足它:
- 无法访问互联网(所有输入必须受版本控制)
- 好的,"downlading" 来自
file:///
- 源代码树是只读的
- 好的,适用于只读
/view
树
- 所有构建工件必须在构建目录下
- OK,Maven Local Repository 通过设置配置
MAVEN_LOCAL_REPO
- 定义良好的工具链(在版本控制下或在只读虚拟机中)
- 好的,在
/view/
或 /opt
中,所有版本都是 "pinned"(已修复)
- 库不知道哪个应用程序使用它们(即 "no parent POM")
- 好的,但不是很好,因为需要调整所有 POM 文件
- 支持开发周期,最好与IDEs (Netbeans) 一起使用,可选
- 好的,相同的 POM 文件适用于 IDEs
- 支持开发周期,最好是增量工作,可选
- 好的,只要保留构建树和本地存储库,Maven 就会增量工作
所以所有(非常具体的)输入要求都已满足。好的。所以这实现了没有互联网的树外构建。
总结: 我想知道如何在不破坏 IDE (Netbeans) 支持的情况下构建由多个 Maven 项目组成的 Maven 应用程序,所有输入都来自只读源,所有输出都到给定的(临时)文件夹。 None 的建议解决方案对我有用,所以我要求详细详细解释我的问题。
详细: 很长一段时间以来,我一直试图将我的 Maven 构建集成到我们的生产环境中,该环境有几个要求:
- 无法访问互联网(所有输入必须受版本控制)
- 源代码树是只读的
- 所有构建工件必须位于构建目录下
- 定义良好的工具链(在版本控制下或在只读虚拟机中)
- 库不知道哪个应用程序使用它们(即 "no parent POM")
- 支持开发周期,最好与IDEs(Netbeans)一起工作,可选
- 为了支持开发周期,最好是增量工作, 可选
这类似于 Out-of-tree build with maven. Is it possible? 和其他与 maven 构建目录相关的问题,但不幸的是 none 的建议解决方案在这里有效。
我可以通过“file:
”访问 "central repository" URL 以及(临时)构建目录下的 "local repository",但我没有找到在不破坏任何其他内容的情况下,有任何方法可以在构建目录下放置 Mavens "target" 目录。
我有几个共享一些库的应用程序。对于每个库,我都有自己的 "parent POM"(违反 DRY 原则,因为我找不到更好的方法)。每个都包含特定于应用程序和环境的设置,例如 distributionManagement 存储库路径,它是使用 ${env.variable}
定义的,以允许构建工具注入系统和应用程序特定的值。提供默认值是为了让使用 Netbeans 的开发人员满意。
不幸的是,这不适用于构建目录。
我可以像 Maven: How to change path to target directory from command line? 那样设置构建目录。然后,由一个父 POM 构建的所有 class 个文件(所有库和应用程序的)将被放入同一个目录中。 Maven 可以 运行 一次并且可以工作 - 但在以后的任何 运行 中它都会失败!至少单元测试会失败,因为 Maven (surefire) 会找到所有项目的所有测试,而不仅仅是 Maven 当前正在处理的那个。因此,它将尝试 运行 在构建库 A 时从应用程序进行单元测试(在我的例子中失败,因为它还需要库 B,而库 B 未作为库 A 中的依赖项提供)。
我也试过 "-DbuildDirectory=$BUILDDIR/maven-target/${project.name}"
这样的结构。这适用于源代码的编译,但同样不适用于测试。尽管 Maven (3.1.1) 在存储文件时正确地评估了 ${project.name}
,但它按字面意义 (!) 将其传递给 class 路径,因此在编译单元测试时 java 找不到测试对象("error: cannot find symbol")。具有正确名称的符号链接 ${project.name}
当然仅适用于一个项目(例如库 A),因为我找不到如何从 pom.xml.
目前我最接近的方法是使用全局目标目录并在每次构建之前清理它,但显然这在使用此构建系统进行开发时很糟糕(在没有 IDE 的情况下增量工作)。
现在我什至考虑从构建系统生成 pom.xml 文件并填充值并将所有源复制到构建树。这应该适用于我们定义的发布版本,但在正常开发过程中会不舒服(使用 IDE)。
Maven 有什么技巧吗?或者这真的不可能吗?
已编辑以回答问题
非常感谢您花时间解决我的问题/帮助请求!我尽量回答清楚。
一位队友建议为这一切添加动力:
应该可以在 10 年或 20 年内复制任何版本,即使互联网发生了变化。
Why do you need to change the value of "target"?
因为源代码树是只读的,Maven 无法在其默认位置创建目录。可写构建目录可用,但在源代码树之外。
Maven will not write to /src, so no problem there
"source tree" 不引用构建过程的源/输入中的 "src" 文件夹,而是引用整个结构 - 包括 POM 文件、资源甚至二进制库(如果需要)。所以包含src的目录也包含pom.xml并且属于只读VCS控制"source tree".
Could you give an example of something that does not work with the standard project layout?
这种只读源代码树的一个不同示例可能是:将整个项目结构(带有库的应用程序)刻录到 CD-R 上并确保 "mvn compile test package deploy" 有效。
(只读源代码树要求的一个动机是确保构建过程不会意外(或故意)操纵源代码,这可能会导致根据重复次数自动更改构建工件,从而破坏可重复性.)
using a repository manager
据我了解,存储库管理器是一种复杂的服务,通常 运行 位于主机上。我认为使用 file URLs 而不是如此复杂的服务可以使依赖关系更小。请注意,整个存储库必须进行版本控制,因此无论如何自动更新都不能工作,但据我所知,这是存储库管理器的优势。
Do you use dependency jar's
是的,当然。库和应用程序依赖于多个 JAR。甚至 Maven 也依赖于几个 JAR,例如编译器插件。
If your request states that you need to build all dependencies yourself ?
实际上,我担心情况可能是这样,是的,不幸的是,我知道这将需要付出巨大的努力。即使使用 Debian 软件包,这也很难实现,他们付出了很多努力来实现它。不幸的是,目前我没有看到构建这些库的现实机会。
"...all build artifacts must be below a build directory"- Can you please explain that in detail?
构建过程应该在给定构建目录下创建所有文件,例如 /tmp/$USER/build/$PROJECTNAME/$PROJECTVERSION/$APPLICATION/$VARIANT/
。构建系统可以在其下创建任意结构,但不应更改此之外的任何内容。此树中的文件列表被定义为输出(构建结果)并从那里获取。通常一些二进制文件或 ZIP 文件被复制到版本控制系统。
有些人称之为 "out of tree build"、"build out of source" 或类似的。
well defined toolchain
想法是拥有一个虚拟机映像(在版本控制下),为每个构建创建一个动态的 "clone",安装所需的工具链(来自版本控制),例如构建工具, Maven 安装及其存储库,提供对要构建的特定版本源的访问权限(例如,以只读 ClearCase 视图的形式),运行 构建脚本入口点脚本,收集创建的输出(用于例如一些 "release.zip" 和 "buildlogs.zip") 最后丢弃整个虚拟机,包括它的所有内容。当然,工具链可以完全预装在映像中,只是出于实际原因没有这样做(是的,我们也使用 Docker)。
For impact analysis it's very important thing to know who is using which library ?
是的,这确实是真的。但是,来源应该从中抽象出来,而且通常会这样做。例如,JUnit 的作者并不认识每个使用它的人。我们喜欢在几个项目中同时使用一个库,所以不能提一个父POM,因为有多个父POM。
希望这次我能解释得更好。
编辑 #2 以回答新问题
再次感谢您花时间度过这么长的时间post!我希望这次我能够解释最后遗漏的部分并说清楚。我认为有些问题似乎是对要求的评论。恐怕我们会陷入讨论。
In maven you have a directory structure like:
root +- pom.xml +- src
The root directory is under version control. So I don't understand the following:
"source tree" does not reference to the "src" folder within the sources / inputs for the build process, but to the whole structure -
root
+- pom.xml
+- src
+-- ...
+- target
+-- ...
我在结构中添加了 target
" 以更好地说明问题。正如您所说,根目录受版本控制,因此是只读的。因此,root/pom.xml
、root/src
和 root
本身是只读的。
当 Maven 尝试创建 root/target
时,它会得到一个错误 (Read-only file system
),因为 root
及其所有子文件夹都是只读的。
这里甚至父文件夹都在版本控制之下,在 ClearCase 的情况下,还有更多的只读父目录,但我认为这在这里无关紧要。
We are talking about terra bytes of artifacts....They have to be backed up but not checked into version control.
这超出了范围,但我还是尽量回答。 例如,我现在正在处理的一个 ClearCase 实例有 2.2 TB,确切地说是万亿字节。例如,如果标准要求对发布之间的所有更改进行可追溯,则备份可能还不够。但是我认为这超出了我的问题范围。
a file base repository does not work well and needed to be copied to each machine where you use it which is a hassle having a kind of network file system which is mounted to many machines. A repository manager works http(s) based which much more simpler to handle..
我认为这超出了范围并且与我的问题无关,但我还是尝试回答。 问题在于,为了可重复性,您需要在接下来的十年或二十年内将此 http(s) 的所有内容存档并保留 运行ning。有历史,因为可能需要重现五年前的状态。当然,所需的软件包可能不再在 Internet 上可用(还记得 Codehaus.org 吗?)。具有挑战性的任务。
a repository manager is much more simpler and keeps the conventions
不幸的是它不满足要求,或者要求高价(管理额外的虚拟机加上存储包的版本控制)。
building all dependencies yourself I think that is simply not worth the effort. I don't a real advantage of that
我认为这样的需求并不少见(https://en.wikipedia.org/wiki/Source_code_escrow),但恐怕我们跑题了,开始讨论需求了。
If the JUnit team can access the request from maven central (which can be done) they can see who is using JUnit
我认为这是题外话,但是 JUnit 开发人员应该如何了解所有 "my" 个使用 JUnit 的项目?
Where is the relationship to the parent pom?
如果我造成了混淆,我很抱歉,这只是一个旁注。我认为这超出了我的问题范围。不过我会尽量回答的。
为了避免冗余,Maven 中的一种方法是使用父 POM 并从中继承。例如,您可以在父 POM 中定义 distributionManagement
并使库 POM 继承它。当您有项目特定的 distributionManagement
要求时,您需要项目特定的父 POM 文件。如果两个项目共享同一个库,那么库应该继承两个项目 POM 中的哪一个(作为父项)?在聚合器 POM 中设置 distributionManagement
不起作用,因为它不会传播。
编辑 #3 解释只读
你好@JF Meier
谢谢你的评论。
只读意味着不能写入,所以Maven不能创建目标目录。因此,javac 无法存储 class 文件并且编译中止。
可能太抽象了,我来举个完整的真实例子吧:
cat pom.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>test</groupId>
<artifactId>mavenproject1</artifactId>
<version>1.0-SNAPSHOT</version>
</project>
cat src/main/java/Hello.java
:
public class HelloWorld {
public static void main(String[] args) { System.out.println("Hello, World!"); }
}
现在从 shell 复制并粘贴(我剪了一些无聊的行):
steffen@node1:/view/dev_test_project_v1/vobs/playground/steffen/minimal_pom $ mvn compile
[...]
[ERROR] Failure executing javac, but could not parse the error:
javac: directory not found: /view/dev_test_project_v1/vobs/playground/steffen/minimal_pom/target/classes
这是一个问题。我必须纠正一个小细节,它没有显示 Read-only file sytem
错误,我认为是一个错误,但在 strace 中我们可以看到它:
21192 mkdir("/view/dev_test_project_v1/vobs/playground/steffen/minimal_pom/target/classes", 0777) = -1 ENOENT (No such file or directory)
21192 mkdir("/view/dev_test_project_v1/vobs/playground/steffen/minimal_pom/target", 0777) = -1 EROFS (Read-only file system)
21192 mkdir("/view/dev_test_project_v1/vobs/playground/steffen/minimal_pom/target", 0777) = -1 EROFS (Read-only file system)
这会导致 javac: directory not found
错误。
只读:
steffen@node1:/view/dev_test_project_v1/vobs/playground/steffen/minimal_pom $ mkdir -p target/classes
mkdir: cannot create directory ‘target’: Read-only file system
mkdir 工具正确显示错误消息。
没有答案..
第一件事是你不能上网。好的,这可以简单地通过使用存储库管理器来完成。这是 Maven 和公司环境中其他构建工具的默认设置。
您使用依赖 jar 的(库?)吗?
如果您的请求声明您需要自己构建所有依赖项?这将是一项巨大的工作,因此意味着您需要拥有组织内所有库的所有源代码,并且您需要维护所有 updates/changes 给他们..(巨大的努力)。
我不明白你的要求:
source tree is read-only
如果你有一个版本控制系统,通常存储库有身份验证,这意味着你可以控制是否有人能够提交(写入)它..
..all build artifacts must be below a build directory..
能否详细解释一下?
以下是我能理解的:
well defined toolchain (under version version control or in read-only Virtual Machine)
通常你用你使用的工具链定义一个环境(当然应该签入版本控制)。
libraries shall not know which application use them (i.e. "no parent POM")
一个parent的用法不代表一个图书馆不知道谁在使用这个图书馆?但除此之外我不明白这个要求?对于影响分析,了解谁在使用哪个库非常重要?
你提到你已经通过我不推荐的文件协议配置了中央仓库..此外 target
目录是 Maven 的约定...我看不出改变这个有什么好处?这有什么问题?
因此,根据您的更新,我在此处添加另一个更新:
Why do you need to change the value of "target"?
Because the source tree is read-only, Maven cannot create the directory at its default position. A writable build directory is available, but outside the source code tree.
您似乎误解了 target
目录的概念,或者您误解了 Maven 的工作原理。
target
目录旨在包含所有 generated/compiled 未 未 签入源代码管理的内容。或者换句话说,target
目录在签入期间默认被忽略,永远不会也不应该被签入版本控制。
Maven will not write to /src, so no problem there
在 maven 中,您的目录结构如下:
root
+- pom.xml
+- src
+-- ...
根目录受版本控制。所以我不明白以下内容:
"source tree" does not reference to the "src" folder within the sources / inputs for the build process, but to the whole structure - including POM files, resources and even binary libs if needed. So the directory that contains src also contains pom.xml and belongs to read-only VCS controlled "source tree".
进入下一点:
As I understood, a repository manager is a complex service typically running on a host. I think using file URLs instead of such a complex service keeps the dependencies smaller. Please note that the whole repository has to be version controlled, so automatically updating must not work anyway, but as I understand is the advantage of a repository manager.
整个存储库应该是版本控制的。听起来您没有在公司环境中进行实际构建的经验。我们正在谈论 terra bytes 的工件....它们必须备份但不检查到版本控制中。
原因很简单,因为每个工件都是由它的坐标唯一定义的,groupId、artifactId、version。
除了你给出的论点之外,设置的复杂性比你想象的要简单。除此之外,基于文件的存储库不能很好地工作,并且需要将其复制到您使用它的每台机器上,这是一种安装在许多机器上的网络文件系统的麻烦。存储库管理器基于 http(s) 工作,处理起来更简单..
进入下一点:
The build process is supposed to create all files below a given build directory, for example /tmp/$USER/build/$PROJECTNAME/$PROJECTVERSION/$APPLICATION/$VARIANT/.
Maven 在 target
目录中执行此操作..
The build system can create arbitrary structures below that, but is not supposed to change anything outside this. A list of files within this tree is defined as output (build result) and taken from there. Typically some binaries or ZIP files are copied to version control system. Some call this "out of tree build", "build out of source" or similar.
正如我在生成的工件之前提到的,我不建议将它们置于版本控制之下,因为存储库管理器要简单得多并保持约定...
回到您自己构建所有依赖项的观点,我认为这根本不值得付出努力。我没有真正的优势...特别是因为您可以使用来自 Maven 存储库(或来自您组织内部的存储库管理器)的所有基于正确坐标的工件...
下一点:
Yes, this surely is very true. However, the sources should abstract from it, and usually do. For example, the authors of JUnit dont' know everybody who is using it.
如果 JUnit 团队可以从 Maven Central 访问请求(可以做到),他们可以看到谁在使用 JUnit。这在组织内部通过使用存储库管理器的访问日志和提取要简单得多使用信息...
我不明白的是:
We like to use a library in several projects at the same time, so we cannot mention a parent POM, because there are multiple parent POMs.
这在 Maven 中称为简单依赖项。与父 pom 的关系在哪里?您似乎误解/或不了解 Maven 的工作原理..?
让我将以下内容添加到 khmarbaise 的回答中。
我没有看到您的 "in ten years reproducible" 标准 Maven 结构问题。你只是:
- 从版本控制系统检出源到一个空目录。
- 在此目录中使用 Maven 构建您的项目。
- 将结果部署到任何你想要的地方。
版本控制系统中的源代码没有改变,每次都会完全一样。您只需要确保您只在依赖项中使用发布版本(而不是 SNAPSHOT 版本),这样 Nexus/Artifactory 每次都会给您相同的工件。
使用 Maven 且没有 Internet 的树外构建
如果其他人面临类似的需求,我将以示例的形式描述我的解决方案。我理解并高兴地接受 大多数人肯定不想要这个 或者根本不需要 "out-of-tree" 使用 Maven 构建。这是为像我这样别无选择的极少数人准备的。
所以这不适用于 "normal Maven builds",但仅适用于问题中描述的特定要求(无法访问互联网,除 /tmp 文件夹外的所有内容都是只读的)。这样的配置可以是虚拟机中的构建系统,该虚拟机 运行 来自 VW 模板,由 Jenkins 作业动态实例化。
A word about ClearCase
This approach is not ClearCase specific, but the example uses ClearCase paths.
The following example uses a read-only ClearCase view
toolchain_view
mounted to/view/toolchain_view/
(containing Maven, all Java packages and other build tooling) and a read-only source code view (containing an application) available below "/view/steffen_hellojava_source_view/". Instead of "/view/toolchain_view/", someone could use "/opt/vendor/name/toolchain/version/" or such and for the sources use any other version control system, so this approach is not ClearCase-specific.For those who don't know ClearCase a word about it: The view behaves similar like a NFS file system, where the server selects the file content based on a version description. The file contents itself is in a database called Versioned Object Base (VOB). Using a so called Configuration Specification (CS) someone can define which content version to show for which file (element) in form of selection rules (such as selecting by a LABEL). For example:
---[ toolchain-1_4_7_0.cs ]---------------------------------------->8======= element /vobs/playvob/toolchain/... TOOLCHAIN_VERSION_1_4_7_0 # TOOLCHAIN_VERSION_1_4_7_0 automatically also selects: # element /vobs/playvob/toolchain/3p/maven/... TOOLCHAIN_MAVEN_3_1_1_0 # element /vobs/playvob/toolchain/3p/maven-repo/... TOOLCHAIN_MAVENREPO_1_0_1_0 # element /vobs/playvob/toolchain/3p/java/... TOOLCHAIN_JAVA_1_7_0_U60 =======8<-------------------------------------------------------------------
Now a ClearCase view such as "toolchain_view" can be created and configured to use these selection rules
cleartool -tag toolchain_view -setcs toolchain-1_4_7_0.cs
. In the example it is mounted as/view/toolchain_view/
, which prefixes the element (file) paths.
配置 Maven
现在我们需要将 Maven 配置为
- 仅将我们的文件结构
/view/toolchain_view/vobs/playvob/toolchain/3p/maven-repo/
用作中央存储库 - 将本地存储库存储在
/tmp
和 下方
target
目录也在/tmp
下面
前两个可以在 Maven 设置文件中配置,但不幸的是,显然最后一个似乎需要在 POM 文件中进行特定更改。
mvn_settings.xml
此处是 Maven 的 --global-settings
文件的摘录。我把它压缩了一点。重要的是对两个中央存储库使用 file:///
URL,并将其作为一个且唯一的镜像强制执行:
<!-- Automatically generated by toolchain creator scripts.
This sets the maven-repo version to the correct value, for example
toolchain version 1.4.7.0 defines Maven repo version 1.0.1. -->
<settings ...>
<localRepository>${env.MAVEN_LOCAL_REPO}</localRepository>
<profiles>
<profile>
<id>1</id>
<activation><activeByDefault>true</activeByDefault></activation>
<repositories><repository>
<id>3rdparty</id><name>third party repo</name>
<url>file:///view/toolchain_view/vobs/playvob/toolchain/3p/maven-repo/</url>
<snapshots><enabled>true</enabled><updatePolicy>never</updatePolicy></snapshots>
<releases><enabled>true</enabled><updatePolicy>never</updatePolicy></releases>
</repository></repositories>
<pluginRepositories><pluginRepository>
<id>3rdparty</id><name>third party repo</name>
<url>file:///view/toolchain_view/vobs/playvob/toolchain/3p/maven-repo/</url>
<snapshots><enabled>true</enabled><updatePolicy>never</updatePolicy></snapshots>
<releases><enabled>true</enabled><updatePolicy>never</updatePolicy></releases>
</pluginRepository></pluginRepositories>
</profile>
</profiles>
<!-- Unfortunately **required** for file-based "central repository": -->
<mirrors><mirror>
<id>dehe_repo_1.0.1</id><name>Vendor Name Central 1.0.1</name>
<url>file:///view/toolchain_view/vobs/playvob/toolchain/3p/maven-repo/</url>
<mirrorOf>*,!3rdparty</mirrorOf>
</mirror></mirrors>
</settings>
为了简化部署,我们可以使用包含版本号的路径,例如 /opt/vendor/name/toolchain/1.4.7.0/mvn_settings.xml
,它可以由自己的 Debian 软件包提供。
通过设置环境变量将 localRepository
指向构建特定的(临时)目录允许在除 "out-of-tree" 构建所需的临时目录之外的所有内容都是只读的情况下进行构建。
使用 IDE (Netbeans) 构建时,我们也可以使用此设置文件,但通常不这样做会更舒服。然而,在这种情况下,有人必须注意不要不小心添加依赖项。如果这些未包含在固定的 Maven Repo 中,构建系统上的编译将中断。
hellojava/pom.xml
为了支持树外构建,我们还需要将 target
文件夹移出只读源代码树(通常它们在 pom.xml
和 [=28= 旁边创建] 在 Java 包目录中,它本身处于版本控制之下,因此在这里是只读的)。这是通过使用两个属性实现的:buildDirectory
和 deployDirectory
。默认的 buildDirectory 是正常的 target
文件夹,因此当不设置 buildDirectory 时,Maven 会正常构建。这很好,因为我们不需要 IDE (Netbeans) 的特定 POM 文件。
Surefire生成单元测试报告,当然也需要到我们的build目录下。
<project>
...
<properties>
<project.skipTests>false</project.skipTests>
<project.testFailureIgnore>false</project.testFailureIgnore>
<buildDirectory>${project.basedir}/target</buildDirectory>
<deployDirectory>defaultDeploy</deployDirectory>
</properties>
...
<build>
<!-- -->
<directory>${buildDirectory}/hellojava</directory>
<!-- -->
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12.4</version>
<configuration>
<skipTests>${project.skipTests}</skipTests>
<testFailureIgnore>${project.testFailureIgnore}</testFailureIgnore>
<workingDirectory>${project.build.directory}/test-run</workingDirectory>
</configuration>
</plugin>
</plugins>
</build>
...
<distributionManagement>
<repository>
<id>build-integration</id>
<name>Deployment Repository</name>
<url>file:///${deployDirectory}</url>
</repository>
</distributionManagement>
...
</project>
综合起来
现在必须通过命令行参数设置此 POM 文件中的属性值,并且必须配置 MAVEN_LOCAL_REPO
,例如:
#!/bin/bash
# Normally in a wrapper which is automatically generated by toolchain creator.
# The path to the pom.xml and build directories then of course are parameters.
export JAVA_HOME="/view/toolchain_view/vobs/playvob/toolchain/3p/java/"
export PATH="/view/toolchain_view/vobs/playvob/toolchain/bin:$PATH"
# or: export PATH="/opt/vendor/name/toolchain/1.4.7.0/bin:$PATH"
: ${MAVEN_LOCAL_REPO:="$HOME/.m2/repository"}
: ${MAVEN_OPTS:="-XX:PermSize=64m -XX:MaxPermSize=192m"}
export MAVEN_LOCAL_REPO="/tmp/steffen-build/hellojava/mavenbuild/repo"
/view/toolchain_view/vobs/playvob/toolchain/3p/maven/bin/mvn -e \
--global-settings /view/toolchain_view/vobs/playvob/toolchain/mvn_settings.xml \
-DbuildDirectory=/tmp/steffen-build/hellojava/mavenbuild/target/ \
-DdeployDirectory=/tmp/steffen-build/hellojava/mavenbuild/output/ \
-Dproject.skipTests \
-f /view/steffen_hellojava_source_view/hellojava/pom.xml \
compile package deploy
现在/tmp
可以是"read-only system"上的RAM盘了。所有工件都写在专用构建目录下。我们可以在 DVD 或只读 NFS 存档服务器上拥有工具链和完整的源代码树,并且仍然可以编译它,而无需访问 Internet。这应该在 20 年后仍然有效,即使 Maven Central 已重命名或其他。
当然,包装脚本可以隐藏所有细节。就我而言,它们集成在基于 cmake 的构建系统中,顶级构建目录由 Jenkins 作业配置。
检查要求
对于原始问题中的每个要求,我们可以检查此方法是否满足它:
- 无法访问互联网(所有输入必须受版本控制)
- 好的,"downlading" 来自
file:///
- 好的,"downlading" 来自
- 源代码树是只读的
- 好的,适用于只读
/view
树
- 好的,适用于只读
- 所有构建工件必须在构建目录下
- OK,Maven Local Repository 通过设置配置
MAVEN_LOCAL_REPO
- OK,Maven Local Repository 通过设置配置
- 定义良好的工具链(在版本控制下或在只读虚拟机中)
- 好的,在
/view/
或/opt
中,所有版本都是 "pinned"(已修复)
- 好的,在
- 库不知道哪个应用程序使用它们(即 "no parent POM")
- 好的,但不是很好,因为需要调整所有 POM 文件
- 支持开发周期,最好与IDEs (Netbeans) 一起使用,可选
- 好的,相同的 POM 文件适用于 IDEs
- 支持开发周期,最好是增量工作,可选
- 好的,只要保留构建树和本地存储库,Maven 就会增量工作
所以所有(非常具体的)输入要求都已满足。好的。所以这实现了没有互联网的树外构建。