Romain Linsolas
Romain Linsolas

Reputation: 81667

Maven2 property that indicates the parent directory

I have a multi-modules project, like this one:

main-project/
    module1/
    module2/
        sub-module1/
        sub-module2/
        sub-module3/
        ...
    module3/
    module4/
    ...

I need to define a set of properties (that are dependent of the environment on which I want to release my project) in Maven2. I will not use <properties> as there is lots of properties... Thus, I use the Properties Maven2 plugin.

The properties files are located in the main-project/ directory. How can I set the correct directory in the main pom.xml, in order to specify to any children where to find the properties file?

<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>

If I set only <file>env_${env}.properties</file>, then when Maven2 compiles the first module, it will not find the main-project/env_dev.properties file. If I set <file>../env_${env}.properties</file>, then an error will be raised at the parent level, or at any sub-module level...

Upvotes: 132

Views: 215809

Answers (19)

Rons
Rons

Reputation: 1

This worked for me in the multi-project setup

In the parent pom file :

<properties> <main.basedir>${project.basedir}\..</main.basedir> </properties>

In the sub-project access these properties like:

${main.basedir}/

Upvotes: 0

qoomon
qoomon

Reputation: 5325

At least in current maven version (3.6.0) you can make use of ${maven.multiModuleProjectDirectory}

Upvotes: 54

Francois Marot
Francois Marot

Reputation: 1195

Another alternative:

in the parent pom, use:

<properties>
   <rootDir>${session.executionRootDirectory}</rootDir>
<properties>

In the children poms, you can reference this variable.

Main caveat: It forces you to always execute command from the main parent pom directory. Then if you want to run commands (test for example) only for some specific module, use this syntax:

mvn test --projects

The configuration of surefire to parametize a "path_to_test_data" variable may then be:

<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>

Upvotes: 7

sparkyd
sparkyd

Reputation: 560

Use directory-maven-plugin with directory-of goal.

Unlike other suggestions:

  • This solution works for multi-module projects.
  • It works whether you build the whole project or a sub-module.
  • It works whether you run maven from the root folder or a sub-module.
  • There's no need to set a relative path property in each and every sub-module!

The plugin lets you set a property of your choice to the absolute-path of any of the project's modules. In my case I set it to the root module... In my project root 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>

From then on, ${myproject.basedir} in any sub-module pom always has the path of the project root module. And of course, you can set the property to any module, not just the root...

Upvotes: 32

Secelean Monica
Secelean Monica

Reputation: 7

I accessed the dir above using ${basedir}..\src\

Upvotes: -1

virgo47
virgo47

Reputation: 2333

I needed to solve similar problem for local repository placed in the main project of multi-module project. Essentially the real path was ${basedir}/lib. Finally I settled on this in my parent.pom:

<repository>
    <id>local-maven-repo</id>
    <url>file:///${basedir}/${project.parent.relativePath}/lib</url>
</repository>

That basedir always shows to current local module, there is no way to get path to "master" project (Maven's shame). Some of my submodules are one dir deeper, some are two dirs deeper, but all of them are direct submodules of the parent that defines the repo URL.

So this does not resolve the problem in general. You may always combine it with Clay's accepted answer and define some other property - works fine and needs to be redefined only for cases where the value from parent.pom is not good enough. Or you may just reconfigure the plugin - which you do only in POM artifacts (parents of other sub-modules). Value extracted into property is probably better if you need it on more places, especially when nothing in the plugin configuration changes.

Using basedir in the value was the essential part here, because URL file://${project.parent.relativePath}/lib did not want to do the trick (I removed one slash to make it relative). Using property that gives me good absolute path and then going relative from it was necessary.

When the path is not URL/URI, it probably is not such a problem to drop basedir.

Upvotes: 1

Bruce Edge
Bruce Edge

Reputation: 2189

This extends romaintaz's answer, which is awesome in that solves the problem and also clearly points out maven's missing functionality. I picked up a later version of the plugin, and added the case where the project could be more than 3 levels deep.

<pluginManagement>
  <plugins>
    ..
    <plugin>
      <groupId>org.codehaus.gmaven</groupId>
      <artifactId>groovy-maven-plugin</artifactId>
      <version>2.0</version>
    </plugin>
    ..
  </plugins>
</pluginManagement>

I elected not to use a property to define the filename. Note if the build.properties is not found this will spin forever. I added a .git dir detection, but didn't want to over complicate the response so it's not shown here.

  <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>

Upvotes: 0

ilya1245
ilya1245

Reputation: 69

In my case it works like this:

...
<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>

Upvotes: 6

Vic
Vic

Reputation: 1888

The following small profile worked for me. I needed such a configuration for CheckStyle, which I put into the config directory in the root of the project, so I can run it from the main module and from submodules.

<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>

It won't work for nested modules, but I'm sure it can be modified for that using several profiles with different exists's. (I have no idea why there should be "../.." in the verification tag and just ".." in the overriden property itself, but it works only in that way.)

Upvotes: 6

fatih tekin
fatih tekin

Reputation: 31

You are in project C, project C is submodule of B and B is submodule of A. You try to reach module D's src/test/config/etc directory from project C. D is also submodule of A. The following expression makes this possible to get the URI path:

-Dparameter=file:/${basedir}/../../D/src/test/config/etc

Upvotes: 3

Jared
Jared

Reputation: 1897

So the problem as I see it is that you can't get the absolute path to a parent directory in maven.

<rant> I've heard this talked about as an anti-pattern, but for every anti-pattern there is real, legitimate use case for it, and I'm sick of maven telling me I can only follow their patterns.</rant>

So the work around I found was to use antrun. Try this in the child 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>

If you run mvn verify you should see something like this:

main:
     [echo] main.basedir=C:\src\parent.project.dir.name

You can then use ${main.basedir} in any of the other plugins, etc. Took me a while to figure this out, so hope it helps someone else.

Upvotes: 12

Caille
Caille

Reputation: 51

I've found a solution to solve this problem: use ${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>

Upvotes: 5

Tcharl
Tcharl

Reputation: 341

I just improve the groovy script from above to write the property in the root parent properties file:

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()

Upvotes: 0

Rich Seller
Rich Seller

Reputation: 84088

In an answer to another question I showed how the maven-properties-plugin could be extended to use external property descriptors defined in Maven dependencies.

You could extend that idea to have multiple descriptor jars, each with the environment name as part of the artifactId, containing a ${env}.properties. Then you can use the property to select the appropriate jar and properties file, for example:

<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>

Upvotes: 1

Peter Kahn
Peter Kahn

Reputation: 13046

I think that if you use the extension pattern used in the example for findbugs plugin & multimodule you may be able to set global properties related to absolute paths. It uses a top

example for multi module

The top level pom has an unrelated build-config project and a app-parent for the modules of the multimodule project. The app-parent uses extension to link itself to the build-config project and obtain resources from it. This is used to carry common config files to the modules. It may be a conduit for properties as well. You could write the top dir to a property file consumed by the build-config. (it seems too complex)

The problem is that a new top level must be added to the multi-module project to make this work. I tried to side step with a truly unrelated build-config project but it was kludgy and seemed brittle.

Upvotes: 0

dulanov
dulanov

Reputation:

<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>
  ...

Upvotes: 3

Clay
Clay

Reputation: 3010

Try setting a property in each pom to find the main project directory.

In the parent:

<properties>
    <main.basedir>${project.basedir}</main.basedir>
</properties>

In the children:

<properties>
    <main.basedir>${project.parent.basedir}</main.basedir>
</properties>

In the grandchildren:

<properties>
    <main.basedir>${project.parent.parent.basedir}</main.basedir>
</properties>

Upvotes: 206

Romain Linsolas
Romain Linsolas

Reputation: 81667

I've found a solution to solve my problem: I search the properties files using the Groovy Maven plugin.

As my properties file is necessarily in current directory, in ../ or in ../.., I wrote a small Groovy code that checks these three folders.

Here is the extract of my 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>

This is working, but I don't really like it.

So, if you have a better solution, do not hesitate to post!

Upvotes: 16

sal
sal

Reputation: 23633

Did you try ../../env_${env}.properties ?

Normally we do the following when module2 is on the same level as the sub-modules

<modules>
    <module>../sub-module1</module>
    <module>../sub-module2</module>
    <module>../sub-module3</module>
</modules>

I would think the ../.. would let you jump up two levels. If not, you might want to contact the plug in authors and see if this is a known issue.

Upvotes: -2

Related Questions