This document provides an overview of best practices for build scripts, including what makes a good build script and common issues with "smelly" build scripts. It discusses qualities of good build scripts like being gold standard, portable, reproducible, standard, and maintainable. Common problems with build scripts are identified such as being hard coded, OS-specific, only working on a specific IDE or machine, relying on undocumented dependencies or processes, being nested, messy, or slow. The document recommends choosing tools that encourage standards like Maven and provides tips for improving Ant and Maven build scripts.
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Build Quality Tips from Java Consultant
1.
2.
3. Who is this guy, anyway?
John Ferguson Smart
Consultant, Trainer, Mentor, Author, Speaker
Specialities:
Java, Groovy/Grails
Software Development Lifecycle
Agile stuff (CI, TDD, BDD,...)
Open Source
4. Agenda
What are we discussing today?
What makes a good build script?
Smelly build scripts
Choosing your tools
Maven tips
Ant tips
6. Build quality - quality builds
What makes a good build script?
Gold Standard
Portable
Reproducible
Standard
Maintainable
7. Build quality - quality builds
Gold Standard
Reference build process
Reference binaries
8. Build quality - quality builds
Portable
Runs anywhere
Runs on any OS
No local dependencies
Environment-specific configurations
Specially-installed software or databases
...
10. Build quality - quality builds
Standard
Knowing what to expect
11. Build quality - quality builds
Maintainable
Think of the next dude
12. Smelly builds
So what makes a poor build script?
1) The hard coded build
2) The OS-specific build
3) The IDE-only build
4) The Magic Machine build
5) The Oral Tradition build
6) The Nested build
7) The Messy Dependencies build
8) The DYI build
9) The untrustworthy build
10) The slow build
13. Paths
..
1/
URLs
Smelly builds
9.
c-
gi
lo
1
eb
00
Passwords
/w
:7
om
ea
.c
/b
me
C:
ac
r.
ve
er
ts
es .>
/t "..
:/ ger
"ti
tp rd=
The hard coded build
ht swo
pas
tt"
sco
e="
nam
ser
n u
<sv
/>
M E }"
S_HO
JBOS
env.
= "$ {
alue
s" v
Environment
jbos
dir.
me="
variables
y na
pert
<pro
21. Smelly builds
The OS-specific build
...
CALL PAUSE.CMD
...
build.cmd
...
:: Check for a non-existent IP address
:: Note: this causes a small extra delay!
IF NOT DEFINED NonExist SET NonExist=10.255.255.254
PING %NonExist% -n 1 -w 100 2>NUL | FIND "TTL=" >NUL
...
pause.cmd
33. Choosing your tools
Encourage/enforce
Standards and Conventions Support standards
standards
3
Easy to read
2
Make up your
own standards
No standards
Hard to read
Ad-hoc scripting Standards and Conventions
34. Choosing your tools
Flexibility and expressiveness
3 Do whatever you
Easy to read
want
2
Encourage/enforce
standards
Hard to read
Makes you stick to conventions Easy to do whatever you want
35. Choosing your tools
Flexibility verses Convention
Build Scripting Rule 1
“A build script will tend to reflect the personality of
it’s developer”
36. Choosing your tools
Flexibility verses Convention
Build Scripting Rule 2
“The more flexible a build script, the more likely it
is to become unmaintainable”
37. Choosing your tools
Flexibility verses Convention
Flexibility is great for some jobs:
Ad-hoc tasks
Some deployment tasks
“Out-of-the-box” stuff
41. Ant tips
Declare your dependencies
Use an Enterprise Repository Manager
Several tool choices:
Maven Ant Tasks
Ivy
42. Ant tips
Using the Maven Ant Tasks
Declare dependencies
Deploy to a Maven Enterprise Repository
<artifact:dependencies pathId="dependency.classpath">
<dependency groupId="junit" artifactId="junit"
version="3.8.2" scope="test"/>
<dependency groupId="javax.servlet" artifactId="servlet-api"
version="2.4" scope="provided"/>
</artifact:dependencies>
43. Ant tips
Make it readable
Write a build script like your source code...
Avoid long targets
Avoid long build scripts
Use descriptive target names
48. Maven tips
Keep it portable
No hard-coding
Define sensible defaults for properties and profiles
Avoid resource filtering for production code
49. Maven tips
Keep it reproducible
Avoid external snapshots
Specify plugin versions
Use consistent environments
50. Maven tips
Consistent environments
Enforcing a minimum Maven version
<?xml version="1.0"?>
<project...>
<modelVersion>4.0.0</modelVersion>
<groupId>com.ciwithhudson.gameoflife</groupId>
<artifactId>gameoflife</artifactId>
<version>0.0.1-SNAPSHOT</version>
Minimum Maven version
<name>gameoflife</name>
<prerequisites>
<maven>2.2.1</maven>
</prerequisites>
51. Maven tips
Consistent environments
Use the same version of Maven
Use a “standard” Maven installation across the organization
Use a global settings.xml file
Store a copy in SCM
Enforce a minimum Maven version in your projects
53. Maven tips
Enforce the Maven version
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.0-beta-1</version>
<executions>
<execution>
<id>enforce-maven-version</id>
<goals>
<goal>enforce</goal>
</goals> Minimum Maven version
<configuration>
<rules>
<requireMavenVersion>
<version>2.2.1</version>
</requireMavenVersion>
</rules>
</configuration>
</execution>
</executions>
</plugin>
54. Maven tips
Enforce the JDK version
All developers should be using the same JDKs
Incompatible bytecode
Different XML parsers
Different Maven behaviour
56. Maven tips
Specify your plugin versions
Undeclared version numbers are bad
Inconsistent builds across different machines
Non-repeatable builds
Plugin changes can break the build
Don’t use SNAPSHOT plugins either
57. Maven tips
Specify your plugin versions
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.0-beta-1</version>
<executions>
<execution>
<id>enforce-versions</id>
<goals>
<goal>enforce</goal>
</goals> Plugin versions must be defined
<configuration>
<rules>
<requirePluginVersions/>
</rules>
</configuration>
</execution>
</executions>
</plugin>
58. Maven tips
Keep it clean
Keep tabs on your dependencies:
What dependencies are you actually using?
What dependencies do you really need?
59. Maven tips
Dependency list
What dependencies are you actually using?
$ mvn dependency:list
[INFO] Scanning for projects... mvn dependency:list
[INFO] Searching repository for plugin with prefix: 'dependency'.
[INFO] ------------------------------------------------------------------------
[INFO] Building babble-core
[INFO] task-segment: [dependency:list]
[INFO] ------------------------------------------------------------------------
[INFO] [dependency:list]
[INFO]
[INFO] The following files have been resolved:
[INFO] antlr:antlr:jar:2.7.6:compile
...
[INFO] commons-collections:commons-collections:jar:2.1.1:compile
[INFO] commons-logging:commons-logging:jar:1.0.4:compile
[INFO] dom4j:dom4j:jar:1.6.1:compile
[INFO] javax.persistence:persistence-api:jar:1.0:compile
[INFO] javax.transaction:jta:jar:1.0.1B:compile
[INFO] junit:junit:jar:4.5:test
[INFO] net.sf.ehcache:ehcache:jar:1.2:compile
[INFO] org.hamcrest:hamcrest-all:jar:1.1:compile
[INFO] org.hibernate:hibernate:jar:3.2.0.ga:compile
[INFO] org.hibernate:hibernate-annotations:jar:3.2.0.ga:compile
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
65. Maven tips
Keep it automated
Plan your release strategy
Use a Repository Manager
Automatic snapshot deployments
Automated releases
66. Maven tips
Maven best practices for CI builds
Use batch mode (-B)
Always check or snapshot updates (-U)
Use a repository per project
Print test failures to stdout (-Dsurefire.useFile=false)
67. Maven tips
Know when to script it
Groovy or Ant scripting is easy in Maven
Call external scripts when appropriate
68. Maven tips
Know when to script it
It’s pretty easy in Maven 2...
<project>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.groovy.maven</groupId>
<artifactId>gmaven-plugin</artifactId>
<version>1.0-rc-5</version>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<source>
println "Hi there I’m compiling ${project.name}"
</source>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
69. Maven tips
Know when to script it
It’s even easier in Maven 3...
project {
build {
$execute(id: 'compilation-script', phase: 'compile') {
println "Hi there I’m compiling ${project.name}"
}
$execute(id: 'validation-script', phase: 'validate') {
println "Hi there I’m validating ${project.name}"
}
...
}
}