Abstract
Modern build managers provide support for
so that all of these can be handled quickly and efficiently as part of the normal build.
Baselines may include multiple 3rd-party libraries.
We may rely on specific version of our chosen libaries.
Those libraries may require other libraries.
Example: Report Accumulator
A project of mine, this page lists the 3rd party libraries required by the project.
The page lists various stages of the build.
E.g., for testCompile, I requested JUnit 4.12 8 Which in turned needed hamcrest-core 1.3
For the findbugs stage, I requested findbugs 3.0.1
A repository is a collection of packages (usually libraries) and metadata about packages
The Java world is dominated by
Maven Central (a.k.a ibiblio)
Try searching each of these for junit
It’s common for development organizations to host their own repository.
Local repositories can be
or little more than a file drop
Packages are typically identified by
The group or organization
org.apache.ant
, edu.odu.cs.extract
junit
The artifact name
junit
, hamcrest-core
, apache-commons
Version number
4.12
4.11+
: 4.11 or laterlatest.integration
: latest official release1.2-SNAPSHOT
: a version under development. The stapshot modifier serves as a notice that the actual package may be updated without a change in version number.Not every library that your project needs is needed all of the time.
junit
would only be needed when compiling and running the unit tests.findbugs
, checkstyle
(discussed later in the semester) are only used during report generation.So build managers allow you to import libraries in selected scopes which indicate when the library is actually needed.
This affects
CLASSPATH
and other settings made available to the build steps.Examine the pom.xml file and look for the dependencies
section.
You’ll see something like
<dependencies>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependencies>
[4.12,]
to get versions 4.12 or greaterWhen the build is run (e.g. mvn package
):
Maven does a transitive search over the dependencies for a project
Maven then downloads the required libraries automatically
~/.m2
)How does Maven know whether junit itself depends on other libraries?
Here is the actual published content for Junit 4.12.
The .pom
file is the metadata.
Look inside. Search for <dependencies>
. You’ll find
<dependencies>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<version>1.3</version>
<scope>compile</scope>
</dependencies>
This is the same kind of info that we put into our own pom.xml file
Technically, our project has two repositories
Team projects will often employ an intermediate shared repository
In pom.xml:
<repositories>
<repository>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>central</id>
<name>libs-release</name>
<url>http://archive350.cs.odu.edu:8080/artifactory/libs-release</url>
</repository>
</repositories>
YAAP1 , Ivy adds the dependency management features pioneered by Maven to ant
Technically can run as a standalone application, but really the ant integration is key.
Use is similar to the dependencies/repositories portion of Maven POMs
Ivy can retrieve from and publish to the established Maven repositories
Again, let’s start with the build.xml
for an Ant
project.
E.g.,
<project name="codeAnnotation" basedir="." default="build">
<record name="ant.log" action="start" append="false" />
<taskdef classpath="JFlex.jar" classname="JFlex.anttask.JFlexTask" name="jflex" /> ➀
<echo>loading build-${os.name}.paths</echo>
<include file="build-${os.name}.paths"/> ➁
<target name="generateSource">
<mkdir dir="src/main/java"/>
<jflex file="src/main/jflex/code2html.flex"
destdir="src/main/java"/>
<jflex file="src/main/jflex/code2tex.flex"
destdir="src/main/java"/>
<jflex file="src/main/jflex/list2html.flex"
destdir="src/main/java"/>
<jflex file="src/main/jflex/list2tex.flex"
destdir="src/main/java"/>
</target>
<target name="compile" depends="generateSource">
<mkdir dir="target/classes"/>
<javac srcdir="src/main/java" destdir="target/classes"
source="1.7" includeantruntime="false"/>
</target>
<target name="compile-tests" depends="compile">
<mkdir dir="target/test-classes"/>
<javac srcdir="src/test/java" destdir="target/test-classes"
source="1.7" includeantruntime="false">
<classpath refid="testCompilationPath"/>
</javac>
</target>
<target name="test" depends="compile-tests">
<property name="mypath" refid="testExecutionPath"/>
<echo>testExecutioPath is ${mypath}</echo>
<echoproperties/>
<mkdir dir="target/test-results/details"/>
<junit printsummary="yes"
haltonfailure="yes" fork="no"
>
<classpath refid="testExecutionPath"/>
<formatter type="xml"/>
<batchtest todir="target/test-results/details">
<fileset dir="target/test-classes">
<include name="**/*Test*.class"/>
</fileset>
</batchtest>
</junit>
<junitreport todir="target/test-results">
<fileset dir="target/test-results/details">
<include name="TEST-*.xml"/>
</fileset>
<report format="frames" todir="target/test-results/html"/>
</junitreport>
</target>
<target name="build" depends="test">
<jar destfile="codeAnnotation.jar" basedir="target/classes">
<manifest>
<attribute name="Main-Class"
value="edu.odu.cs.code2html.Code2HTML"/>
</manifest>
</jar>
</target>
<target name="clean">
<delete dir="target"/>
</target>
</project>
Remove any hard-coded paths to libraries,. e.g.
<project>
<path id="testCompilationPath">
<pathelement location="/usr/share/java/junit4.jar"/>
<pathelement path="target/classes"/>
</path>
<path id="testExecutionPath">
<pathelement location="/usr/share/java/junit4.jar"/>
<pathelement path="target/classes"/>
<pathelement path="target/test-classes"/>
</path>
</project>
Start by having our build file actually load Ivy
<project name="codeAnnotation" basedir="." default="build"
xmlns:ivy="antlib:org.apache.ivy.ant">
<record name="ant.log" action="start" append="false" />
<property name="ivy.install.version" value="2.3.0"/>
<property name="ivy.jar.dir" value="${basedir}/ivy"/>
<property name="ivy.jar.file" value="${ivy.jar.dir}/ivy.jar"/>
<target name="download-ivy" unless="skip.download">
<mkdir dir="${ivy.jar.dir}"/>
<echo message="installing ivy..."/>
<get src="http://repo1.maven.org/maven2/org/apache/ivy/ivy/${ivy.install.version}/ivy-${ivy.install.version}.jar"
dest="${ivy.jar.file}" skipexisting="true"/>
</target>
<target name="install-ivy" depends="download-ivy"
description="--> install ivy">
<path id="ivy.lib.path">
<fileset dir="${ivy.jar.dir}" includes="*.jar"/>
</path>
<taskdef resource="org/apache/ivy/ant/antlib.xml"
uri="antlib:org.apache.ivy.ant" classpathref="ivy.lib.path"/>
</target>
⋮
<target name="generateSource">
⋮
</target>
<target name="compile" depends="generateSource">
⋮
</project>
The download-ivy
target fetches a specific version of Ivy from a Maven repository, storing it in lib/
skipexisting
attribute avoids downloading if we already have a copy.The install-ivy
target binds the ivy:
namespace (from the root element) to a class in the downloaded Ivy jar.
We list the libraries our project needs in a separate ivy.xml file
<ivy-module version="2.0">
<dependencies>
<dependency org="de.jflex" name="jflex" rev="1.4.3"/>
<dependency org="junit" name="junit" rev="4.10"/>
</dependencies>
</ivy-module>
This should look familiar to the section from the Maven pom:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
</dependencies>
Once Ivy is installed, we can invoke it:
<project name="codeAnnotation" basedir="." default="build"
xmlns:ivy="antlib:org.apache.ivy.ant">
⋮
<target name="resolve-ivy" depends="install-ivy">
<ivy:resolve/> ➀
<ivy:cachepath pathid="ivy.path" /> ➁
<taskdef classpath="JFlex.jar"
classname="JFlex.anttask.JFlexTask" name="jflex" /> ➂
</target>
<target name="generateSource" depends="resolve-ivy">
<mkdir dir="target/gen/java"/>
<jflex file="src/main/jflex/code2html.flex" ➃
destdir="target/gen/java"/>
<jflex file="src/main/jflex/code2tex.flex"
destdir="target/gen/java"/>
⋮
➀ The ivy:resolve
task fetches the libraries we need.
~/.ivy
).➁ This creates an Ant path named ivy.path
containing the list of all libraries fetched.
Includes both the ones we explicitly named in our ivy.xml
and ones that those depended upon.
➂ Here we can bind the task name for the JFlex library that we have loaded,
<jflex ... >
taskMaking the build depend on the Ivy sequence guarantees that the library availability will be managed at each build
… to use the newly downloaded libraries.
<project name="codeAnnotation" basedir="." default="build"
xmlns:ivy="antlib:org.apache.ivy.ant">
<record name="ant.log" action="start" append="false" />
<path id="testCompilationPath">
<pathelement path="target/classes"/>
<path refid="ivy.path"/>
</path>
<path id="testExecutionPath">
<pathelement path="target/classes"/>
<pathelement path="target/test-classes"/>
<path refid="ivy.path"/>
</path>
<property name="ivy.install.version" value="2.3.0"/>
<property name="ivy.jar.dir" value="${basedir}/ivy"/>
<property name="ivy.jar.file" value="${ivy.jar.dir}/ivy.jar"/>
<property name="build.dir" value="build"/>
<property name="src.dir" value="src"/>
<target name="download-ivy" unless="skip.download">
<mkdir dir="${ivy.jar.dir}"/>
<echo message="installing ivy..."/>
<get src="http://repo1.maven.org/maven2/org/apache/ivy/ivy/${ivy.install.version}/ivy-${ivy.install.version}.jar"
dest="${ivy.jar.file}" usetimestamp="true"/>
</target>
<target name="install-ivy" depends="download-ivy"
description="--> install ivy">
<path id="ivy.lib.path">
<fileset dir="${ivy.jar.dir}" includes="*.jar"/>
</path>
<taskdef resource="org/apache/ivy/ant/antlib.xml"
uri="antlib:org.apache.ivy.ant" classpathref="ivy.lib.path"/>
</target>
<target name="resolve-ivy" depends="install-ivy">
<ivy:retrieve/>
<echo>ivy.default.ivy.user.dir is ${ivy.default.ivy.user.dir}</echo>
<taskdef classpath="JFlex.jar"
classname="JFlex.anttask.JFlexTask" name="jflex" />
</target>
<target name="generateSource" depends="resolve-ivy">
<mkdir dir="target/gen/java"/>
<jflex file="src/main/jflex/code2html.flex"
destdir="target/gen/java"/>
<jflex file="src/main/jflex/code2tex.flex"
destdir="target/gen/java"/>
<jflex file="src/main/jflex/list2html.flex"
destdir="target/gen/java"/>
<jflex file="src/main/jflex/list2tex.flex"
destdir="target/gen/java"/>
</target>
<target name="compile" depends="generateSource">
<mkdir dir="target/classes"/>
<javac srcdir="target/gen/java" destdir="target/classes"
source="1.6" includeantruntime="false"/>
<javac srcdir="src/main/java" destdir="target/classes"
source="1.6" includeantruntime="false"/>
</target>
<target name="compile-tests" depends="compile">
<mkdir dir="target/test-classes"/>
<javac srcdir="src/test/java" destdir="target/test-classes"
source="1.6" includeantruntime="false">
<classpath refid="testCompilationPath"/>
</javac>
</target>
<target name="test" depends="compile-tests">
<mkdir dir="target/test-results/details"/>
<junit printsummary="yes"
haltonfailure="yes" fork="no"
>
<classpath refid="testExecutionPath"/>
<formatter type="xml"/>
<batchtest todir="target/test-results/details">
<fileset dir="target/test-classes">
<include name="**/*Test*.class"/>
</fileset>
</batchtest>
</junit>
<junitreport todir="target/test-results">
<fileset dir="target/test-results/details">
<include name="TEST-*.xml"/>
</fileset>
<report format="frames" todir="target/test-results/html"/>
</junitreport>
</target>
<target name="build" depends="test">
<jar destfile="codeAnnotation.jar" basedir="target/classes">
<manifest>
<attribute name="Main-Class"
value="edu.odu.cs.code2html.Code2HTML"/>
</manifest>
</jar>
<zip destfile="target/codeAnnotation-src.zip">
<fileset dir=".">
<include name="*.xml"/>
<include name="test.cpp"/>
<include name="*.css.cpp"/>
<include name="src/**/*"/>
<exclude name="**/*~"/>
<exclude name="target/**/*"/>
</fileset>
</zip>
</target>
<target name="clean">
<delete dir="target"/>
</target>
<target name="cleaner" depends="clean">
<delete dir="lib"/>
</target>
<target name="cleanest" depends="cleaner">
<delete dir="ivy"/>
</target>
</project>
We have made sure that ivy.path
is a list of all 3rd party libraries downloaded by Ivy.
Ivy can use most maven repositories.
Choice of repositories is controlled by an ivysettings.xml
file.
ibiblio
service that is Maven’s default.An example of an ivysettings.xml
file:
<?xml version="1.0" encoding="UTF-8"?>
<ivy-settings>
<settings defaultResolver="default" />
<resolvers>
<chain name="default"> ➀
<ibiblio name="central" m2compatible="true"/> ➁
</chain>
</resolvers>
</ivy-settings>
➀ A “chain” means that we want Ivy to search these repositories in sequence.
In this case, I only have one thing in the chain, but for other projects I may add additional repositories.
➁ Look in the Maven Central archive.
The developers of Ivy provide an Eclipse plugin, IvyDE.
ivy.xml
file.As is often the case, gradle
tends to provide simpler syntax to do much the same things as Maven and Ant.
In build.gradle
:
apply plugin: 'java'
⋮
repositories {
jcenter() ➀
// Use my own CS dept repo
ivy { ➁
url 'https://secweb.cs.odu.edu/~zeil/ivyrepo'
}
}
dependencies { ➂
compile 'net.sf.saxon:saxon-dom:8.7+'
testCompile 'junit:junit:4.12'
testRuntime 'edu.odu.cs.zeil:reportAccumulator:1.1+'
}
Here you see both the choice of libraries ➂ and of repositories ➀, ➁.
The dependencies indicate
The scope (e.g., testCompile
, which actually includes compiling and running of the unit tests)
The package, specified as organization:package:versionNumber
Each of the build/library managers we have discussed have support in Eclipse
May provide special editors for editing the build manager configuration files.
Provide ways to launch the build manager from within Eclipse
Sometimes it’s easier to launch separately in a separate window, e.g., from the command line from the Gradle GUI mode.
Remember to “refresh” the project in Eclipse afterwards
Most importantly, allows Eclipse Java projects to use any 3rd-party libraries fetched by the build/library manager.
Otherwise the built-in Eclipse compilation and smart-editing features would mistakenly believe that your code was full of references to non-existent classes and funnctions.
May allow some management of the cache of old libraries.
Can also hope for convenience functions
Two plugins currently
Allows you to create new Maven projects or to import existing ones into Eclipse.
Support for Ant is built-in to Eclipse
The developers of Ivy provide an Eclipse plugin, IvyDE.
Allows you to add a “Library” to your Eclipse project’s build path
This library holds everything that Ivy fetches based upon your ivy.xml
file.
Eclipse has various Gradle plugins available.
The most popular seems to be the BuildShip package, available from the Gradle Marketplace (in the Eclipse Help menu).
Allows import (only) of existing Gradle projects
Allows launching Gradle from within Eclipse
Allows Eclipse Java projects to find 3rd-party libraries loaded by Gradle.
Plug-ins to build managers are generally handled separately from other items, though the syntax is often similar.
For example, in gradle, plug-ins are handled in a buildscript
section:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'org.hidetake:gradle-ssh-plugin:2.2.0+'
}
}
buildscript
area are almost identical to the way we handle normal packages.Maven has a similar approach, isolating plug-ins in a <plugins>
section.
Publishing artifacts to a repository is also supported by maven, Ant/Ivy, & Gradle.
mainly because most repositories are open to download, but require login credentials to upload.
Example: in Gradle build.xml
publishing {
publications {
ivyJava(IvyPublication) {
organisation 'edu.odu.cs.zeil'
module 'cowem-plugin'
revision project.version
descriptor.status = 'integration' // milestone, release
descriptor.branch = 'v1'
from components.java
}
}
repositories {
ivy {
name 'ivyRepo'
url 'sftp://atria.cs.odu.edu:22/home/zeil/secure_html/ivyrepo'
// Readable via https://www.cs.odu.edu/~zeil/ivyrepo
credentials {
⋮
}
}
}
}
Providing login credentials to a build is problematic.
A common work-around is to store a few build “properties” in a file in the user’s home directory, not kept as part of the package version control.
Example: in Gradle build.xml
// Credentials are loaded from ~/.gradle/gradle.properties
if(project.hasProperty("ivyRepoUser")){ ➀
ext.ivyRepoUser = "$ivyRepoUser";
} else {
ext.ivyRepoUser = "user";
}
if(project.hasProperty("ivyRepoPass")){
ext.ivyRepoPass = "$ivyRepoPass";
} else {
ext.ivyRepoPass = "password";
}
⋮
publishing {
⋮
credentials { ➁
username project.ivyRepoUser
password project.ivyRepoPass
}
⋮
}
➀ looks to see if properties ivyRepoUser
and ivyRepoPass
were loaded from ~/.gradle/gradle.properties
. If so, it adds them as “extension” properties of the current gradle project.
➁ passes those new extension properties as login credentials to a repository.
If the person running the build di not have a ~/.gradle/gradle.properties
or it did not contain the properties ivyRepoUser
and ivyRepoPass
proeprties, then attempted uploads to the repository would use a default username and password that would, presumably, fail.
1: Yet Another Apache Project