… другими словами, первый шаг к использованию 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 и позволить ему обрабатывать большую часть этого отвлекающего мусора.