… другими словами, первый шаг к использованию Maven для управления зависимостями, но НЕ сборок. Это ирония Maven … они объединили две вещи (управление зависимостями и сборки) таким образом, что они делают полезную (управление зависимостями) болезненной, потому что система сборки настолько ужасна.
В качестве промежуточного шага между полным Maven и (наиболее вероятно) Gradle , я искал способ использовать Maven для зависимостей только таким образом, который совместим с Eclipse … без использования часто ненадежного и ненадежного плагина M2Eclipse .
В любом случае, вместо того, чтобы предполагать, что зависимости могут измениться в любой момент времени , давайте предположим, что когда я изменяю зависимости (вручную редактируя pom.xml), я знаю это и готов запустить команду, чтобы вызвать Eclipse (и мой Сборка на основе муравьев) в соответствии.
Во-первых, мой pom.xml:
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>myapp</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.apache.tapestry</groupId>
<artifactId>tapestry-hibernate</artifactId>
<version>${tapestry-version}</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<artifactId>log4j</artifactId>
<groupId>log4j</groupId>
</exclusion>
<exclusion>
<artifactId>slf4j-log4j12</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.tapestry</groupId>
<artifactId>tapestry-test</artifactId>
<version>5.2.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>2.4.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-search</artifactId>
<version>3.1.1.GA</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>0.9.17</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.5.8</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>8.4-701.jdbc4</version>
</dependency>
<dependency>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
<version>2.4.0</version>
<type>jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.howardlewisship</groupId>
<artifactId>tapx-datefield</artifactId>
<version>${tapx-version}</version>
</dependency>
<dependency>
<groupId>com.howardlewisship</groupId>
<artifactId>tapx-prototype</artifactId>
<version>${tapx-version}</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>5.9</version>
<scope>test</scope>
</dependency>
</dependencies>
<repositories>
<repository>
<id>tapestry360-snapshots</id>
<url>http://tapestry.formos.com/maven-snapshot-repository/</url>
</repository>
<repository>
<id>repository.jboss.org</id>
<url>http://repository.jboss.org/maven2/</url>
</repository>
</repositories>
<properties>
<tapestry-version>5.1.0.5</tapestry-version>
<lucene-version>2.4.1</lucene-version>
<tapx-version>1.0.0-SNAPSHOT</tapx-version>
</properties>
</project>
(Это было адаптировано из одного из POM моего клиента).
Затем файл сборки Ant, который компилирует это, выполняет тесты и создает WAR:
<project name="example" xmlns:mvn="urn:maven-artifact-ant">
<property name="classes.dir" value="target/classes" />
<property name="test.classes.dir" value="target/test-classes" />
<property name="web.lib.dir" value="target/web-libs" />
<property name="webapp.dir" value="src/main/webapp" />
<property name="webinf.dir" value="${webapp.dir}/WEB-INF" />
<path id="compile.path">
<fileset dir="lib/provided" includes="*.jar"/>
<fileset dir="lib/runtime" includes="*.jar" />
</path>
<path id="test.path">
<path refid="compile.path" />
<pathelement path="${classes.dir}" />
<fileset dir="lib/test" includes="*.jar" />
</path>
<target name="clean" description="Delete all derived files.">
<delete dir="target" quiet="true" />
</target>
<!-- Assumes that Maven's Ant library is installed in ${ANT_HOME}/lib/ext. -->
<target name="-setup-maven">
<typedef resource="org/apache/maven/artifact/ant/antlib.xml" uri="urn:maven-artifact-ant" />
<mvn:pom id="pom" file="pom.xml" />
</target>
<macrodef name="copy-libs">
<attribute name="filesetrefid" />
<attribute name="todir" />
<sequential>
<mkdir dir="@{todir}" />
<copy todir="@{todir}">
<fileset refid="@{filesetrefid}" />
<mapper type="flatten" />
</copy>
</sequential>
</macrodef>
<macrodef name="rebuild-lib">
<attribute name="base" />
<attribute name="scope" />
<attribute name="libs.id" default="@{base}.libs" />
<attribute name="src.id" default="@{base}.src" />
<sequential>
<mvn:dependencies pomrefid="pom" filesetid="@{libs.id}" sourcesFilesetid="@{src.id}" scopes="@{scope}" />
<copy-libs filesetrefid="@{libs.id}" todir="lib/@{base}" />
<copy-libs filesetrefid="@{src.id}" todir="lib/@{base}-src" />
</sequential>
</macrodef>
<target name="refresh-libraries" depends="-setup-maven" description="Downloads runtime and test libraries as per POM.">
<delete dir="lib" quiet="true" />
<rebuild-lib base="provided" scope="provided"/>
<rebuild-lib base="runtime" scope="runtime,compile" />
<rebuild-lib base="test" scope="test" />
<echo>
*** Use the rebuild-classpath command to update the Eclipse .classpath file.</echo>
</target>
<target name="compile" description="Compile main source code.">
<mkdir dir="${classes.dir}" />
<javac srcdir="src/main/java" destdir="${classes.dir}" debug="true" debuglevel="lines,vars,source">
<classpath refid="compile.path" />
</javac>
</target>
<target name="compile-tests" depends="compile" description="Compile test sources.">
<mkdir dir="${test.classes.dir}" />
<javac srcdir="src/test/java" destdir="${test.classes.dir}" debug="true" debuglevel="lines,vars,source">
<classpath refid="test.path" />
</javac>
</target>
<target name="run-tests" depends="compile-tests" description="Run unit and integration tests.">
<taskdef resource="testngtasks" classpathref="test.path" />
<testng haltonfailure="true">
<classpath>
<path refid="test.path" />
<pathelement path="${test.classes.dir}" />
</classpath>
<xmlfileset dir="src/test/conf" includes="testng.xml" />
</testng>
</target>
<target name="war" depends="run-tests,-setup-maven" description="Assemble WAR file.">
<!-- Copy and flatten the libraries ready for packaging. -->
<mkdir dir="${web.lib.dir}" />
<copy todir="${web.lib.dir}" flatten="true">
<fileset dir="lib/runtime" />
</copy>
<jar destfile="${web.lib.dir}/${pom.artifactId}-${pom.version}.jar" index="true">
<fileset dir="src/main/resources" />
<fileset dir="${classes.dir}" />
</jar>
<war destfile="target/${pom.artifactId}-${pom.version}.war">
<fileset dir="${webapp.dir}" />
<lib dir="${web.lib.dir}" />
</war>
</target>
</project>
Ключевая цель здесь — обновить библиотеки, которая удаляет каталог lib, а затем снова заполняет его. Он создает подпапку для каждой области (lib / предоставляются, lib / runtime, lib / test) и другую подпапку для исходных файлов JAR (lib / предоставляются-src, lib / runtime-src и т. Д.).
Так как это поможет Eclipse? Рубин на помощь:
#!/usr/bin/ruby
# Rebuild the .classpath file based on the contents of lib/runtime, etc.
# Probably easier using XML Generator but don't have the docs handy
def process_scope(f, scope)
# Now find the actual JARs and add an entry for each one.
dir = "lib/#{scope}"
return unless File.exists?(dir)
Dir.entries(dir).select { |name| name =~ /\.jar$/ }.sort.each do |name|
f.write %{ <classpathentry kind="lib" path="#{dir}/#{name}"}
srcname = dir + "-src/" + name.gsub(/\.jar$/, "-sources.jar")
if File.exist?(srcname)
f.write %{ sourcepath="#{srcname}"}
end
f.write %{/>\n}
end
end
File.open(".classpath", "w") do |f|
f.write %{<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src/main/java"/>
<classpathentry kind="lib" path="src/main/resources"/>
<classpathentry kind="src" path="src/test/java"/>
<classpathentry kind="lib" path="src/test/resources"/>
<classpathentry kind="output" path="target/classes"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
}
process_scope(f, "provided")
process_scope(f, "runtime")
process_scope(f, "test")
f.write %{
</classpath>
}
end
Это довольно хорошо для получаса работы. Раньше это было намного сложнее (в Maven 2.0.9), но новый атрибут scopes в задаче зависимостей Maven имеет все значение.
Используя это, у вас остается выбор: либо вы не регистрируете .classpath и содержимое папки lib, в этом случае вам нужно выполнить цель и скрипт, чтобы быть функциональными … или вы просто проверяете все в. Я использую GitHub для своего репозитория проекта … дополнительное пространство для нескольких МБ библиотек не является проблемой и гарантирует, что я могу установить точный путь к классу, необходимый другим разработчикам в проекте, ни с одним из обычных Maven догадки. Я с нетерпением жду, чтобы никогда не сказать «Но это работает для меня?» или «Какую версию всего, что вы установили?» или «Попробуйте чистую сборку, закройте и снова откройте ваш проект, а затем удалите и добавьте поддержку зависимостей Maven, а затем снова пожертвуйте небольшим козлом».
Следующий? Упаковка большей части этого в библиотеку Ant, чтобы я мог легко использовать ее в разных проектах … или потратить время на изучение Gradle и позволить ему обрабатывать большую часть этого отвлекающего мусора.