我有一个多模块项目,比如这个:
main-project/
module1/
module2/
sub-module1/
sub-module2/
sub-module3/
...
module3/
module4/
...
我需要在 Maven2 中定义一组属性(取决于我想要发布我的项目的环境)。我不会使用 <properties>
,因为有很多属性...因此,我使用 Properties Maven2 plugin。
属性文件位于 main-project/
目录中。如何在主 pom.xml 中设置正确的目录,以便指定任何子项在哪里可以找到属性文件?
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>properties-maven-plugin</artifactId>
<version>1.0-alpha-1</version>
<executions>
<execution>
<phase>initialize</phase>
<goals>
<goal>read-project-properties</goal>
</goals>
<configuration>
<files>
<file>???/env_${env}.properties</file>
</files>
</configuration>
</execution>
</executions>
</plugin>
如果我只设置了<file>env_${env}.properties</file>
,那么当Maven2编译第一个模块时,它会找不到main-project/env_dev.properties
文件。如果我设置 <file>../env_${env}.properties</file>
,则会在父级别或任何子模块级别引发错误...
${maven.multiModuleProjectDirectory}
尝试在每个 pom 中设置一个属性以找到主项目目录。
在父级中:
<properties>
<main.basedir>${project.basedir}</main.basedir>
</properties>
在孩子们:
<properties>
<main.basedir>${project.parent.basedir}</main.basedir>
</properties>
在孙辈中:
<properties>
<main.basedir>${project.parent.parent.basedir}</main.basedir>
</properties>
至少在当前的 Maven 版本(3.6.0)中,您可以使用 ${maven.multiModuleProjectDirectory}
使用 directory-maven-plugin with directory-of goal。
与其他建议不同:
该解决方案适用于多模块项目。
无论您构建整个项目还是子模块,它都有效。
无论您是从根文件夹还是子模块运行 maven,它都有效。
无需在每个子模块中设置相对路径属性!
该插件允许您将您选择的属性设置为任何项目模块的绝对路径。在我的情况下,我将它设置为根模块......在我的项目根 pom 中:
<plugin>
<groupId>org.commonjava.maven.plugins</groupId>
<artifactId>directory-maven-plugin</artifactId>
<version>0.1</version>
<executions>
<execution>
<id>directories</id>
<goals>
<goal>directory-of</goal>
</goals>
<phase>initialize</phase>
<configuration>
<property>myproject.basedir</property>
<project>
<groupId>com.my.domain</groupId>
<artifactId>my-root-artifact</artifactId>
</project>
</configuration>
</execution>
</executions>
</plugin>
从那时起,任何子模块 pom 中的 ${myproject.basedir} 始终具有项目根模块的路径。当然,您可以将属性设置为任何模块,而不仅仅是根...
我找到了解决问题的方法:我使用 Groovy Maven 插件搜索属性文件。
由于我的属性文件必须在当前目录、../ 或 ../.. 中,我编写了一个小的 Groovy 代码来检查这三个文件夹。
这是我的 pom.xml 的摘录:
<!-- Use Groovy to search the location of the properties file. -->
<plugin>
<groupId>org.codehaus.groovy.maven</groupId>
<artifactId>gmaven-plugin</artifactId>
<version>1.0-rc-5</version>
<executions>
<execution>
<phase>validate</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<source>
import java.io.File;
String p = project.properties['env-properties-file'];
File f = new File(p);
if (!f.exists()) {
f = new File("../" + p);
if (!f.exists()) {
f = new File("../../" + p);
}
}
project.properties['env-properties-file-by-groovy'] = f.getAbsolutePath();
</source>
</configuration>
</execution>
</executions>
</plugin>
<!-- Now, I can load the properties file using the new 'env-properties-file-by-groovy' property. -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>properties-maven-plugin</artifactId>
<version>1.0-alpha-1</version>
<executions>
<execution>
<phase>initialize</phase>
<goals>
<goal>read-project-properties</goal>
</goals>
<configuration>
<files>
<file>${env-properties-file-by-groovy}</file>
</files>
</configuration>
</execution>
</executions>
</plugin>
这是有效的,但我真的不喜欢它。
因此,如果您有更好的解决方案,请不要犹豫发布!
所以我看到的问题是你无法在maven中获得父目录的绝对路径。
<咆哮> I've heard this talked about as an anti-pattern,但对于每个反模式,都有真实、合法的用例,我厌倦了 maven 告诉我我只能遵循他们的模式。</rant>
所以我发现的解决方法是使用antrun。在子 pom.xml 中试试这个:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.7</version>
<executions>
<execution>
<id>getMainBaseDir</id>
<phase>validate</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<exportAntProperties>true</exportAntProperties>
<target>
<!--Adjust the location below to your directory structure -->
<property name="main.basedir" location="./.." />
<echo message="main.basedir=${main.basedir}"/>
</target>
</configuration>
</execution>
</executions>
</plugin>
如果您运行 mvn verify
,您应该会看到如下内容:
main:
[echo] main.basedir=C:\src\parent.project.dir.name
然后,您可以在任何其他插件等中使用 ${main.basedir}
。我花了一段时间才弄清楚这一点,所以希望它对其他人有所帮助。
另一种选择:
在父 pom 中,使用:
<properties>
<rootDir>${session.executionRootDirectory}</rootDir>
<properties>
在子 pom 中,您可以引用此变量。
主要警告:它强制您始终从主父 pom 目录执行命令。然后,如果您只想为某些特定模块运行命令(例如测试),请使用以下语法:
mvn 测试 --projects
用于参数化“path_to_test_data”变量的surefire配置可能是:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire.plugin.version}</version>
<configuration>
<systemPropertyVariables>
<path_to_test_data>${rootDir}/../testdata</path_to_test_data>
</systemPropertyVariables>
</configuration>
</plugin>
以下小配置文件对我有用。我需要为 CheckStyle 进行这样的配置,我将其放入项目根目录的 config
目录中,这样我就可以从主模块和子模块运行它。
<profile>
<id>root-dir</id>
<activation>
<file>
<exists>${project.basedir}/../../config/checkstyle.xml</exists>
</file>
</activation>
<properties>
<project.config.path>${project.basedir}/../config</project.config.path>
</properties>
</profile>
它不适用于嵌套模块,但我确信可以使用具有不同 exists
的多个配置文件对其进行修改。 (我不知道为什么验证标签中应该有“../..”,而被覆盖的属性本身只有“..”,但它只能以这种方式工作。)
就我而言,它的工作原理如下:
...
<properties>
<main_dir>${project.parent.relativePath}/..</main_dir>
</properties>
...
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>properties-maven-plugin</artifactId>
<version>1.0-alpha-1</version>
<executions>
<execution>
<phase>initialize</phase>
<goals>
<goal>read-project-properties</goal>
</goals>
<configuration>
<files>
<file>${main_dir}/maven_custom.properties</file>
</files>
</configuration>
</execution>
</executions>
</plugin>
我找到了解决这个问题的方法:使用 ${parent.relativePath}
<parent>
<artifactId>xxx</artifactId>
<groupId>xxx</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<build>
<filters>
<filter>${parent.relativePath}/src/main/filters/filter-${env}.properties</filter>
</filters>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
<plugins>
<plugin>
<groupId>org.codehaus.groovy.maven</groupId>
<artifactId>gmaven-plugin</artifactId>
<version>1.0</version>
<executions>
<execution>
<phase>validate</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<source>
import java.io.File
project.properties.parentdir = "${pom.basedir}"
while (new File(new File(project.properties.parentdir).parent, 'pom.xml').exists()) {
project.properties.parentdir = new File(project.properties.parentdir).parent
}
</source>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>properties-maven-plugin</artifactId>
<version>1.0-alpha-2</version>
<executions>
<execution>
<phase>initialize</phase>
<goals>
<goal>read-project-properties</goal>
</goals>
<configuration>
<files>
<file>${parentdir}/build.properties</file>
</files>
</configuration>
</execution>
</executions>
</plugin>
...
您在项目 C 中,项目 C 是 B 的子模块,B 是 A 的子模块。您尝试从项目 C 到达模块 D 的 src/test/config/etc
目录。D 也是 A 的子模块。以下表达式可以获取 URI小路:
-Dparameter=file:/${basedir}/../../D/src/test/config/etc
在 answer to another question 中,我展示了如何扩展 maven-properties-plugin 以使用 Maven 依赖项中定义的外部属性描述符。
您可以将该想法扩展为具有多个描述符 jar,每个描述符 jar 都将环境名称作为 artifactId 的一部分,其中包含 ${env}.properties。然后可以使用属性来选择合适的jar和属性文件,例如:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>properties-ext-maven-plugin</artifactId>
<version>0.0.1</version>
<executions>
<execution>
<id>read-properties</id>
<phase>initialize</phase>
<goals>
<goal>read-project-properties</goal>
</goals>
</execution>
</executions>
<configuration>
<filePaths>
<!--assume the descriptor project has a file in the root of the jar -->
<filePath>${env}.properties</filePath>
</filePaths>
</configuration>
<dependencies>
<!-- reference the properties jar for the particular environment-->
<dependency>
<groupId>some.descriptor.group</groupId>
<artifactId>env-${env}-descriptor</artifactId>
<version>0.0.1</version>
</dependency>
</dependencies>
</plugin>
我只是从上面改进了 groovy 脚本以将属性写入根父属性文件中:
import java.io.*;
String p = project.properties['env-properties-file']
File f = new File(p)
if (f.exists()) {
try{
FileWriter fstream = new FileWriter(f.getAbsolutePath())
BufferedWriter out = new BufferedWriter(fstream)
String propToSet = f.getAbsolutePath().substring(0, f.getAbsolutePath().lastIndexOf(File.separator))
if (File.separator != "/") {
propToSet = propToSet.replace(File.separator,File.separator+File.separator+File.separator)
}
out.write("jacoco.agent = " + propToSet + "/lib/jacocoagent.jar")
out.close()
}catch (Exception e){
}
}
String ret = "../"
while (!f.exists()) {
f = new File(ret + p)
ret+= "../"
}
project.properties['env-properties-file-by-groovy'] = f.getAbsolutePath()
我认为,如果您使用示例中用于 findbugs 插件和多模块的扩展模式,您可能能够设置与绝对路径相关的全局属性。它使用顶部
顶层 pom 有一个不相关的 build-config 项目和一个 app-parent 用于多模块项目的模块。 app-parent 使用扩展将自己链接到 build-config 项目并从中获取资源。这用于将通用配置文件传送到模块。它也可能是属性的管道。您可以将顶部目录写入 build-config 使用的属性文件。 (好像太复杂了)
问题是必须在多模块项目中添加一个新的顶层才能使其工作。我试图避开一个真正不相关的构建配置项目,但它很笨拙而且看起来很脆弱。
这扩展了 romaintaz 的答案,这很棒,因为它解决了问题并且还清楚地指出了 maven 缺少的功能。我选择了更高版本的插件,并添加了项目深度可能超过 3 层的情况。
<pluginManagement>
<plugins>
..
<plugin>
<groupId>org.codehaus.gmaven</groupId>
<artifactId>groovy-maven-plugin</artifactId>
<version>2.0</version>
</plugin>
..
</plugins>
</pluginManagement>
我选择不使用属性来定义文件名。请注意,如果未找到 build.properties,这将永远旋转。我添加了一个 .git 目录检测,但不想使响应过于复杂,所以这里没有显示。
<plugin>
<groupId>org.codehaus.gmaven</groupId>
<artifactId>groovy-maven-plugin</artifactId>
<executions>
<execution>
<phase>validate</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<source>
import java.io.File;
String p = "build.properties";
while(true) {
File f = new File(p);
if(f.exists()) {
project.properties['project-properties-file'] = f.getAbsolutePath();
break;
}
else {
p = "../${p}";
}
}
</source>
</configuration>
</execution>
</executions>
</plugin>
对于放置在多模块项目的主项目中的本地存储库,我需要解决类似的问题。本质上,真正的路径是 ${basedir}
/lib。最后我在我的 parent.pom
中解决了这个问题:
<repository>
<id>local-maven-repo</id>
<url>file:///${basedir}/${project.parent.relativePath}/lib</url>
</repository>
basedir
始终显示给当前本地模块,无法获得“主”项目的路径(Maven 的耻辱)。我的一些子模块更深一个目录,一些更深两个目录,但它们都是定义 repo URL 的父级的直接子模块。
所以这通常不能解决问题。您可能总是将它与 Clay 接受的答案结合起来并定义一些其他属性 - 工作正常,仅在 parent.pom
的值不够好的情况下才需要重新定义。或者您可能只是重新配置插件 - 您只在 POM 工件(其他子模块的父级)中执行此操作。如果您在更多地方需要它,提取到属性中的值可能会更好,特别是当插件配置中没有任何更改时。
在值中使用 basedir
是这里必不可少的部分,因为 URL file://${project.parent.relativePath}/lib
不想这样做(我删除了一个斜杠以使其相对)。使用能给我提供良好绝对路径的属性,然后从它开始相对是必要的。
当路径不是 URL/URI 时,删除 basedir
可能不是这样的问题。
我使用 ${basedir}..\src\ 访问了上面的目录
sub-module1
,它将指向 module2
的目录,而不是 main-project
。
这在多项目设置中对我有用
在父 pom 文件中:
<properties> <main.basedir>${project.basedir}\..</main.basedir> </properties>
在子项目中访问这些属性,例如:
${main.basedir}/
你试过 ../../env_${env}.properties
吗?
当module2与子模块处于同一级别时,通常我们会执行以下操作
<modules>
<module>../sub-module1</module>
<module>../sub-module2</module>
<module>../sub-module3</module>
</modules>
我认为 ../.. 会让你跳上两个级别。如果没有,您可能需要联系插件作者,看看这是否是一个已知问题。
${parent.basedir}
不再解析为 3.0.4 中的任何内容...${project.basedir}/..
上取得了成功,但这实际上只适用于严格目录层次结构中的多模块项目。file.separator
变量<main.basedir>${project.basedir}${file.separator}..</main.basedir>