Propriedade Maven2 que indica o diretório pai

105

Tenho um projeto de vários módulos, como este:

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

Preciso definir um conjunto de propriedades (que dependem do ambiente no qual desejo lançar meu projeto) no Maven2. Não vou usar <properties>porque tem muitas propriedades ... Assim, utilizo o plugin Propriedades do Maven2 .

Os arquivos de propriedades estão localizados no main-project/diretório. Como posso definir o diretório correto no pom.xml principal, a fim de especificar para qualquer filho onde encontrar o arquivo de propriedades?

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

Se eu definir apenas <file>env_${env}.properties</file>, quando o Maven2 compilar o primeiro módulo, ele não encontrará o main-project/env_dev.propertiesarquivo. Se eu definir <file>../env_${env}.properties</file>, um erro será gerado no nível dos pais ou em qualquer nível de submódulo ...

Romain Linsolas
fonte
1
Basta usar${maven.multiModuleProjectDirectory}
qoomon

Respostas:

164

Tente definir uma propriedade em cada pom para encontrar o diretório principal do projeto.

No pai:

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

Nas crianças:

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

Nos netos:

<properties>
    <main.basedir>${project.parent.parent.basedir}</main.basedir>
</properties>
Argila
fonte
19
Essas propriedades predefinidas foram removidas? ${parent.basedir}não analisa mais nada em 3.0.4 ...
matt5784
7
Sim, isso não funciona. $ {project.parent.basedir} é avaliado como nulo.
Jared
22
Eu tive sucesso com, ${project.basedir}/..mas isso realmente só funciona em projetos de vários módulos que estão em uma hierarquia de diretório estrita.
Jonathan
90
Suspiro. Inacreditável que o Maven torne isso tão difícil.
Stefan Haberl,
5
Como Jonathan acima, tenho que usar caminhos relativos, mas sinto que é melhor usar a file.separatorvariável assim<main.basedir>${project.basedir}${file.separator}..</main.basedir>
conectada em
29

Pelo menos na versão atual do maven (3.6.0), você pode fazer uso de ${maven.multiModuleProjectDirectory}

qoomon
fonte
2
Tentando encontrar um documento sobre isso e parece que é para uso interno, pode ser removido / alterado no futuro MNG-6589
Greg Domjan
Como usá-lo? -1
hey_you
É uma propriedade que você pode usar como faria com outras pessoas.
qoomon
não tenho certeza se devemos usar essa resposta. De acordo com este tíquete , é interno e pode ir a qualquer momento. É também por isso que não está documentado em lugar nenhum. Ainda sem solução limpa
Hilikus
21

Use o plugin do diretório-maven com o objetivo do diretório .

Ao contrário de outras sugestões:

  • Esta solução funciona para projetos de vários módulos.
  • Funciona quer você construa o projeto inteiro ou um submódulo.
  • Funciona quer você execute o maven a partir da pasta raiz ou de um submódulo.
  • Não há necessidade de definir uma propriedade de caminho relativo em cada um dos submódulos!

O plugin permite que você defina uma propriedade de sua escolha para o caminho absoluto de qualquer um dos módulos do projeto. No meu caso, configurei-o para o módulo raiz ... No meu projeto 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>

A partir daí, $ {myproject.basedir} em qualquer submódulo pom sempre tem o caminho do módulo raiz do projeto. E, claro, você pode definir a propriedade para qualquer módulo, não apenas a raiz ...

faiscante
fonte
Não coloque "teste" para a fase. Me causou uma variedade de problemas. Funciona muito bem como mostrado acima.
ElectronicBlacksmith
15

Eu encontrei uma solução para resolver meu problema: eu procuro os arquivos de propriedades usando o plugin Groovy Maven.

Como meu arquivo de propriedades está necessariamente no diretório atual, em ../ ou em .. / .., escrevi um pequeno código do Groovy que verifica essas três pastas.

Aqui está o extrato do meu 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>

Isso está funcionando, mas eu realmente não gosto disso.

Portanto, se você tiver uma solução melhor, não hesite em postar!

Romain Linsolas
fonte
12

Portanto, o problema, a meu ver, é que você não pode obter o caminho absoluto para um diretório pai no maven.

<rant> Já ouvi falar disso como um antipadrão , mas para cada antipadrão existe um caso de uso real e legítimo para ele, e estou farto de saber que o maven me diz que só posso seguir seus padrões. </ discurso retórico>

Então, a solução que descobri foi usar antrun. Experimente isso no pom.xml filho:

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

Se você executar, mvn verifydeverá ver algo assim:

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

Você pode então usar ${main.basedir}qualquer um dos outros plug-ins, etc. Levei um tempo para descobrir isso, então espero que ajude alguém.

Jared
fonte
como faço para passá-lo para o plugin maven-surefire?
Kalpesh Soni
7

Outra alternativa:

no pom pai, use:

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

Nos poms filhos, você pode fazer referência a essa variável.

Advertência principal: força você a sempre executar o comando a partir do diretório principal do pom. Então, se você quiser executar comandos (teste, por exemplo) apenas para algum módulo específico, use esta sintaxe:

teste de mvn --projects

A configuração do surefire para parametizar uma variável "path_to_test_data" pode então ser:

<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>
François Marot
fonte
5

O seguinte pequeno perfil funcionou para mim. Eu precisava dessa configuração para CheckStyle, que coloquei no configdiretório na raiz do projeto, para que possa executá-lo a partir do módulo principal e dos submódulos.

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

Não funcionará com módulos aninhados, mas tenho certeza que pode ser modificado para isso usando vários perfis com diferentes exists. (Não tenho ideia de por que deveria haver "../ .." na tag de verificação e apenas ".." na própria propriedade substituída, mas funciona apenas dessa maneira.)

Vic
fonte
Não sei por que isso funciona (o extra ../), mas parece a solução mais limpa (eu também estava tendo problemas com a configuração checkstyle.xml)
RockMeetHardplace
5

No meu caso, funciona assim:

...
<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>
ilya1245
fonte
3

Encontrei uma solução para resolver este problema: 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>
Caille
fonte
2
Isso pode nem sempre ser seguro; $ {parent.relativePath} pode incluir um nome de arquivo, por exemplo, "../pom.xml"
pimlottc
3

Você está no projeto C, o projeto C é o submódulo de B e B é o submódulo de A. Você tenta alcançar o src/test/config/etcdiretório do módulo D a partir do projeto C. D também é o submódulo de A. A seguinte expressão torna isso possível obter o caminho URI:

-Dparameter=file:/${basedir}/../../D/src/test/config/etc
Fatih Tekin
fonte
2
<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>
  ...

fonte
1

Em uma resposta a outra pergunta , mostrei como o plug-in de propriedades do maven poderia ser estendido para usar descritores de propriedade externos definidos nas dependências do Maven.

Você poderia estender essa ideia para ter vários jars descritores, cada um com o nome do ambiente como parte do artifactId, contendo $ {env} .properties. Em seguida, você pode usar a propriedade para selecionar o jar e o arquivo de propriedades apropriados, por exemplo:

<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>
Vendedor rico
fonte
1

Acabei de melhorar o excelente script acima para escrever a propriedade no arquivo de propriedades do pai raiz:

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()
Tcharl
fonte
0

Eu acho que se você usar o padrão de extensão usado no exemplo para o plugin findbugs e multimodule, você pode ser capaz de definir propriedades globais relacionadas a caminhos absolutos. Usa um top

exemplo para multi módulo

O pom de nível superior tem um projeto build-config não relacionado e um app-parent para os módulos do projeto multimodule. O app-parent usa a extensão para se vincular ao projeto build-config e obter recursos dele. Isso é usado para transportar arquivos de configuração comuns para os módulos. Pode ser um canal para propriedades também. Você pode escrever o diretório principal em um arquivo de propriedade consumido pelo build-config. (parece muito complexo)

O problema é que um novo nível superior deve ser adicionado ao projeto multimódulo para que isso funcione. Eu tentei evitar um projeto build-config verdadeiramente não relacionado, mas era confuso e parecia frágil.

Peter Kahn
fonte
0

Isso estende a resposta do romaintaz, que é incrível porque resolve o problema e também aponta claramente a funcionalidade ausente do maven. Peguei uma versão posterior do plugin e adicionei o caso em que o projeto poderia ter mais de 3 níveis de profundidade.

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

Decidi não usar uma propriedade para definir o nome do arquivo. Observe que se o build.properties não for encontrado, ele girará para sempre. Eu adicionei uma detecção de dir .git, mas não queria complicar demais a resposta, então ela não é mostrada aqui.

  <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>
Bruce Edge
fonte
0

Eu precisava resolver um problema semelhante para o repositório local colocado no projeto principal do projeto multimódulo. Essencialmente, o caminho real era ${basedir}/ lib. Finalmente resolvi isso em meu parent.pom:

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

Isso basedirsempre mostra para o módulo local atual, que não há como obter o caminho para o projeto "mestre" (vergonha de Maven). Alguns dos meus submódulos são um dir mais profundo, alguns são dois dirs mais profundos, mas todos eles são submódulos diretos do pai que define a URL do repo.

Portanto, isso não resolve o problema em geral. Você sempre pode combiná-la com a resposta aceita de Clay e definir alguma outra propriedade - funciona bem e precisa ser redefinida apenas para casos em que o valor deparent.pom não é bom o suficiente. Ou você pode apenas reconfigurar o plugin - o que você faz apenas em artefatos POM (pais de outros submódulos). O valor extraído na propriedade é provavelmente melhor se você precisar dele em mais lugares, especialmente quando nada na configuração do plugin muda.

Usar basedirno valor era a parte essencial aqui, porque a URL file://${project.parent.relativePath}/libnão queria fazer o truque (removi uma barra para torná-la relativa). Usar uma propriedade que me dá um bom caminho absoluto e, em seguida, ir em relação a ela era necessário.

Quando o caminho não é URL / URI, provavelmente não é um problema descartá-lo basedir.

virgo47
fonte
0

Eu acessei o diretório acima usando $ {basedir} .. \ src \

Secelean Monica
fonte
1
sim mas não. Para o sub-module1, ele apontará para o diretório de module2, não para main-project.
Romain Linsolas de
-1

Você tentou ../../env_${env}.properties?

Normalmente fazemos o seguinte quando o módulo 2 está no mesmo nível que os submódulos

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

Eu acho que ../ .. deixaria você pular dois níveis. Caso contrário, você pode entrar em contato com os autores do plug-in e ver se esse é um problema conhecido.

sal
fonte
Se eu colocar ../../env.props no pom.xml principal, receberei um erro quando o Maven2 tentar construir o pom principal e todos os moduleX. Esta configuração irá de fato funcionar apenas para todos os submódulos ...
Romain Linsolas