© Adam L. Davis 2020
A. L. DavisModern Programming Made Easyhttps://doi.org/10.1007/978-1-4842-5569-8_13

13. Building

Adam L. Davis1 
(1)
Oviedo, FL, USA
 

The build process is typically one of compiling the source files of a project, running tests, and producing a finished product or products.

In some companies there are whole teams whose sole job is maintaining and updating the build process. In any project of any size, it helps to have a good automated build tool.

There are many other build tools, but we’re just going to cover three:
  • Ant1

  • Maven2

  • Gradle3

Ant

Ant is the first really popular project builder for Java that existed. It is XML-based and requires you to create tasks in XML that can be executed by Ant. An Ant build file is typically named build.xml and has a <project> root element.

A task is a division of work. Tasks depend on other tasks. For example, the jar task usually depends on the compile task. Although Maven threw away the task concept, it was used again in Gradle.

Critics of Ant complain that it uses XML (a declarative and highly verbose format) and requires a lot of work to do simple things. However, it was a very popular tool for many years and is still used in many places.

Maven

Maven is an XML-based declarative project manager. Maven is used for building Java projects but is capable of much more. Maven is also a set of standards that allows Java/JVM developers to easily define and integrate dependencies into large projects. Maven somewhat replaces Ant but can also integrate with it and other build tools.

Maven was mostly a reaction to the huge number of open source libraries Java projects tend to rely on. It has a built-in standard for dependency management (managing the inter-dependencies of open source libraries).

Although Maven is an Apache open source project, it could be said that the core of Maven is Maven Central, a repository of open source libraries run by Sonatype, the company behind Maven. There are many other repositories that follow the Maven standard, such as JFrog’s jCenter,4 so you are not restricted to Maven Central.

Note

Ivy5 is a similar build tool, but is more closely related to Ant.

Many build tools, such as Ivy and Gradle, build on top of Maven’s concept.

Using Maven

The main file that defines a Maven project is the POM (Project Object Model) . The POM file is written in XML and contains all of the dependencies, plug-ins, properties, and configuration data that is specific to the current project. The POM file is generally composed of the following:
  • Basic properties (artifactId, groupId, name, version, packaging)

  • Dependencies

  • Plug-ins

There is a Maven plug-in for every major Java-based IDE out there (Eclipse, NetBeans, and IntelliJ IDEA), and they are very helpful. You can use the Maven plug-in to create your project, add dependencies, and edit your POM files.

Starting a New Project

There is a simple way to create a new configuration file (pom.xml) and project folders using the archetype:generate command.
   mvn archetype:generate
That will list all the different kinds of projects you can create. Pick a number representing the type of project you want (there are thousands of options right now), then answer some questions, such as the name of your project. After that process, run the following command to build the project:
1   mvn package

If you want to use any additional third-party libraries, you will have to edit the POM to include each dependency. Fortunately, most IDEs make it easy to add dependencies to the POM.

../images/435475_2_En_13_Chapter/435475_2_En_13_Figa_HTML.jpg Maven: The Complete Reference6 is available online if you want to learn more.

Life Cycle

Maven uses a declarative style (unlike Ant, which uses a more imperative approach). This means that instead of listing the steps to take, you describe what should happen during certain phases of the build. The phases in Maven are built in and are listed as follows (and execute in this order):
  1. 1.

    validate: Validates that the project is correct and all necessary information is available

     
  2. 2.

    compile: Compiles the source code of the project

     
  3. 3.

    test: Tests the compiled source code, using a suitable unit-testing framework

     
  4. 4.

    package: Takes the compiled code and packages it in its distributable format, such as a JAR

     
  5. 5.

    integration-test: Processes and deploys the package, if necessary, into an environment in which integration tests can be run

     
  6. 6.

    verify: Runs any checks to verify that the package is valid and meets quality criteria

     
  7. 7.

    install: Installs the package into the local repository, for use as a dependency in other projects locally

     
  8. 8.

    deploy: Copies, in an integration or release environment, the final package to the remote repository, for sharing with other developers and projects

    ../images/435475_2_En_13_Chapter/435475_2_En_13_Figb_HTML.jpg There are more phases,7 but you don’t need to know all of them until you are doing more complex builds.

     

Executing Code

Sometimes, however, you just need more control over your build. In Maven, you can execute Groovy code, Ant build files, and Scala code, and you can even write your own plug-ins in Groovy.

For example, you can put Groovy code in your POM file in the following way:
 1   <plugin>
 2    <groupId>org.codehaus.groovy.maven</groupId>
 3    <artifactId>gmaven-plugin</artifactId>
 4    <executions>
 5     <execution>
 6       <id>groovy-magic</id>
 7       <phase>prepare-package</phase>
 8       <goals>
 9         <goal>execute</goal>
10       </goals>
11         <configuration>
12           <source>
13             def depFile =
14             new File(project.build.outputDirectory, 'deps.txt')
15            project.dependencies.each {
16               depFile.write(
17                   "${it.groupId}:${it.artifactId}:${it.version}")
18             }
19             ant.copy(todir: project.build.outputDirectory ) {
20               fileset(dir: project.build.sourceDirectory)
21             }
22           </source>
23         </configuration>
24       </execution>
25     </executions>
26   </plugin>

The preceding code would write out every dependency of the project into the file deps.txt. Then it would copy all of the source files into the project.build.outputDirectory (usually target/classes).

../images/435475_2_En_13_Chapter/435475_2_En_13_Figc_HTML.jpg See Chapters 2, 3, and 4 in The Maven Cookbook.8

Gradle

Gradle is an automated build tool with a Groovy native DSL (domain-specific language) for defining project builds. It also has a Kotlin native DSL and is the official build tool for Android projects.

The Gradle web site has this to say about Gradle as of writing:

From mobile apps to microservices, from small startups to big enterprises, Gradle helps teams build, automate and deliver better software, faster.

gradle.​org9

Getting Started with Gradle

To get started easily, you can use Gradle’s built-in init plugin. After installing Gradle, run the following command:
gradle init

When prompted you can choose a Java application with Junit 4 tests and a Groovy build script (type 2, enter, 3, enter, 1, enter, 1, enter). This will create a build.gradle file, a settings.gradle file, gradlew, and gradlew.bat (which allow you to run Gradle from any system without even installing it) and some basic code and a test.

Projects and Tasks

Each Gradle build is composed of one or more projects and each project is composed of one or more tasks.

The core of the Gradle build is the build.gradle file, which is called the build script. Tasks can be defined by writing task, then a task name, followed by a closure. For example:
1   task upper doLast {
2           String someString = 'test'
3           println "Original: $someString"
4           println "Uppercase: " + someString.toUpperCase()
5   }
Much as in Ant, a task can depend on other tasks, which means they must be run before the task. You can specify dependsOn with any number of task names as a List within your task definition. For example:
1  task buildApp(dependsOn: [clean, jar]) {
2      // define task here
3  }

Tasks can contain any Groovy code (or Kotlin if using the Kotlin DSL), but you can take advantage of some of the many existing Gradle plugins to quickly produce a dependable and fast build.

Plugins

Gradle core has very little built-in. It has powerful plugins which allow it to be very flexible. A plugin can do one or more of the following:
  • Add tasks to the project (e.g., compile, test).

  • Pre-configure added tasks with useful defaults.

  • Add dependency configurations to the project.

  • Add new properties and methods to existing type, via extensions.

We’re going to concentrate on building Java-based projects, so we’ll be using the java plugin; however, Gradle is not limited to Java projects.

For example, at the beginning of your build.gradle file, you should see something like the following code:
1   plugins {
2      id 'java'
3      id 'application'
4   }

This enables the java plugin and the application plugin.

Gradle uses standard project organization conventions. For example, it expects to find your production Java source code under src/main/java and your test Java source code under src/test/java. This is the same style expected by Maven.

Dependencies

Every Java project tends to rely on many open source projects to be built. Gradle builds on the concepts that came before it, so you can easily define your dependencies using a simple DSL, such as in the following example:
 1   plugins { id 'java' }
 2
 3   sourceCompatibility = 1.11
 4
 5   repositories {
 6      mavenLocal()
 7      mavenCentral()
 8   }
 9
10   dependencies {
11      implementation 'com.google.guava:guava:23.0'
12      implementation 'io.reactivex.rxjava2:rxjava:2.2.10'
13      testImplementation group: 'junit', name: 'junit', version: '4.12+'
14      testImplementation "org.mockito:mockito-core:1.9.5"
15   }

This build script uses sourceCompatibility to define the Java source code version of Java 11 (which is used during compilation). Next, it tells Maven to use the local repository first (mavenLocal), then Maven Central.

In the dependencies block, this build script defines two dependencies for the implementation scope and two for testImplementation scope. Jars in the testImplementation scope are only used in tests and won’t be included in any final products.

The line for JUnit shows the more verbose style for defining dependencies. The “+” here means that version or greater.

Do First and Last

You can use doFirst and doLast to specify code that should run before and after your tasks. For example, let’s take our task from earlier and add some additional code:
1  task buildApp(dependsOn: [clean, jar]) {
2      doFirst { println "starting to build" }
3      doLast { println "done building" }
4  }
This would print out “starting to build” before the task is run and “done building” after it completes. Type gradle buildApp in your command prompt to run this task. You should see output much like the following:
> Task :buildApp
starting to build
done building
BUILD SUCCESSFUL in 983ms
4 actionable tasks: 4 executed

Your jar file will now be located in the build/libs/ directory of your project.

../images/435475_2_En_13_Chapter/435475_2_En_13_Figd_HTML.jpg Gradle has a huge online user guide available online at gradle.​org.10

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset