Build Tools: Make, Rake, Ant, Gradle
invocation | variables | strings | arrays | targets and prerequisites | recipes | rules | files and directories | compilation | templates | maven repositories | libraries and namespaces | recursion

build terminology

javascript builds:

java builds: standard directory layout | artifacts and repositories | maven targets | pom.xml

windows builds: nmake | msbuild

make rake ant gradle
version used
 
GNU Make 3.81 0.9 1.10 4.0
show version
 
make --version $ rake --version $ ant -version $ gradle --version
name of build file
 
Makefile Rakefile build.xml build.gradle
hello world $ cat > Makefile
hello:
        @echo Hello, World!

$ make hello
$ cat > Rakefile
task :hello do
  puts "Hello, World!"
end

$ rake hello
$ cat > build.xml
<project>
  <target name="hello">
    <echo>Hello, World!</echo>
  </target>
</project>

$ ant hello
$ cat build.gradle
task hello {
  doLast {
    println 'Hello world!'
  }
}
build hello world $ cat > Makefile
hello:
        gcc -o $@ $@.c

$ cat > hello.c
#include <stdio.h>

int
main(int argc, char *argv[]) {
  printf("Hello, World!\n");
}

$ make

$ ./hello
Hello, World!
$ cat > Rakefile
task :default => ["hello"]

file "hello" => ["hello.c"] do
  sh "gcc -o hello hello.c"
end

$ cat > hello.c
#include <stdio.h>

int
main(int argc, char *argv[]) {
  printf("Hello, World!\n");
}

$ rake

$ ./hello
Hello, World!
$ cat > build.xml
<project default="hello">
  <target name="hello">
    <javac srcdir="."
           includeantruntime="false"
           destdir="."/>
  </target>
</project>

$ cat > Hello.java
public class Hello {
  public static void
  main(String[] args) {
    String msg = "Hello, World!";
    System.out.println(msg);
  }
}

$ ant

$ java Hello
Hello, World!
$ cat > build.gradle
apply plugin: 'java'

$ mkdir -p src/main/java
$ cat > src/main/java/Hello.java
  public static void
  main(String[] args) {
    String msg = "Hello, World!";
    System.out.println(msg);
  }
}

$ gradle build

$ java -cp build/classes/java/main Hello
Hello, World!
statement separator Variable definition terminated by newline unless escaped with a preceding backslash.

Target definition terminated by the first subsequent line without a leading tab.

Within recipes statements are terminated by newline unless escaped with a preceding backslash.
newline or ;

newlines not separators inside (), [], {}, triple quote literals, or after backslash: \
A statement is delimited by an XML start tag and its matching end tag.
comment
 
# comment # comment <!-- comment --> // comment

/* comment
   another comment */
invocation
make rake ant gradle
specify build file $ make -f PATH [TARGET]
$ make --makefile PATH [TARGET]
$ rake -f PATH [TARGET] ...
$ rake --rakefile PATH [TARGET] ...
$ ant -f PATH [TARGET] ...
$ ant -buildfile PATH [TARGET] ...
$ gradle -b PATH [TARGET] ...
$ gradle --build-file PATH [TARGET] ...
dry run
 
$ make -n [TARGET] ...
$ make --dry-run [TARGET] ...
$ rake -n [TARGET] ...
$ rake --dry-run [TARGET] ...
none $ gradle -m [TARGET] ...
$ gradle --dry-run [TARGET] ...
keep going after errors $ make -k [TARGET] ...
$ make --keep-going [TARGET] ...
none $ ant -k [TARGET] ...
$ ant -keep-going [TARGET] ...
$ gradle --continue [TARGET] ...
run jobs in parallel $ make -j 10 [TARGET] ...
$ make --jobs=10 [TARGET] ...
none none
list targets $ env -i make -nRrp | grep -v '^#' $ rake -T
$ rake --tasks
$ ant -p
$ ant -projecthelp
$ gradle tasks --all
touch targets $ make -t [TARGET] ...
$ make --touch [TARGET] ...
none none
always rebuild $ make -B [TARGET] ...
$ make --always-make [TARGET] ...
none none
up-to-date test $ make -q [TARGET] ...
$ make --question [TARGET] ...
none none
silent $ make -s [TARGET] ...
$ make --quiet [TARGET] ...
$ rake -s [TARGET] ...
$ rake --silent [TARGET] ...
$ ant -S [TARGET] ...
$ ant -silent [TARGET] ...
$ gradle -q [TARGET] ...
$ gradle --quiet [TARGET] ...
variables
make rake ant gradle
set and access variable msg := hello

say_hello:
        @echo $(msg)

# alternate syntax:
say_hello2:
        @echo ${msg}
msg = "hello"

task :say_hello do
  puts msg
end
<property name="msg" value="hello"/>

<target name="say_hello">
  <echo>${msg}</echo>
</target>
def msg = 'hello'

task say_hello {
  doLast {
    println msg
  }
}
value types Variables contain strings.

Arrays of values are stored as strings with values delimited by spaces.
Ruby value types:

String, Fixnum, Float, Array, Hash, …
Groovy value types:

Integer, BigDecimal, ArrayList, String, …
undefined variable access evaluates as empty string raises NameError no variable name substitution occurs; i.e. the variable name itself with the dollar sign and the braces appear in the string in a recipe an exception is raised and task fails
redefine variable last definition overwrites previous definition last definition overwrites previous definition later definitions are ignored; use var for a mutable variable gradle script fails to compile
append to variable src_files := foo.c

# sets src_files to "foo.c bar.c":
src_files += bar.c

src_files:
        echo $(src_files)
src_files = ['foo.c']
src_files << 'bar.c'

task :src_files do
  puts src_files.join(' ')
end
none def src_files = ['foo.java']

src_files += 'bar.java'

task print_src_files {
  doLast {
    println src_files
  }
}
conditionally define variable ifeq ($(ENV),test)
db := test
else
db := prod
endif

# also ifneq
if ENV["ENV"] == "test"
  db = "test"
else
  db = "prod"
end
<property environment="env"/>

<condition property="db"
           value="test"
           else="prod">
  <equals arg1="${env.ENV}" arg2="test"/>
</condition>
def db = "prod"
if ("$System.env.ENV" == "test") {
  db = "test"
}
environment variable invoker := $(USER) invoker = ENV['USER'] <property environment="env"/>

<property name="invoker"
          value="${env.USER}"/>
def invoker = "$System.env.USER"
set variable if doesn't exist database ?= dev_db database = dev_db if database.nil? <condition property="database" value="dev_db">
  <not><isset property="database"/></not>
</condition>
fatal error if variable not set ifeq ($(PASSWORD),)
$(error PASSWORD not set)
endif
raise "password not set" if password.nil? <fail message="password not set" unless="password"/>
warn if variable not set ifeq ($(ACCOUNT),)
$(warning ACCOUNT not set; setting to $(USER))
ACCOUNT := $(USER)
endif
shell command substitution yyyymmdd = $(shell date +'%Y%m%d') yyyymmdd = `date +'%Y%m%d'` <exec executable="date"
      outputproperty="yyyymmdd">
  <arg value="+'%Y%m%d'"/>
</exec>
strings
make rake ant gradle
string literal
newline in literal
literal escapes
concatenate
trim EMPTY :=
FOO := $(EMPTY) foo $(EMPTY)
FOO2 := $(strip $(FOO))
pattern substitution obj = $(src: %.c = %.o)

# or:
obj = $(patsubst %.c,%.o,$(src))
obj = src.sub('\.c$', '.o')
global substitution american := meter.txt center.csv
british := $(subst ter,tre,$(american))
american = ['meter.txt', 'center.csv']
british = american.map do |o|
  o.sub(/ter/, 'tre')
end
string join $(join /etc/,passwd) File.join("/etc", "passwd")
arrays
make rake ant gradle
foreach dirs := etc bin src lib
files := $(foreach d,$(dirs),$(wildcard $d/*))
if SRC1 := $(wildcard *.cpp)
SRC2 := $(wildcard *.cxx)
# non-empty strings are "true":
SRC := $(if $(SRC1),$(SRC1),$(SRC2))
filter, filter-out FILES := foo.c foo.h bar.c bar.h
HEADERS := $(filter %.h,$(FILES))
OTHERS := $(filter-out %.h,$(FILES))
sort LIST := foo foo bar
# also removes dupes:
SORTED := $(sort $(LIST))
words NAMES := foo bar baz
# 3:
NAME_COUNT := $(words $(NAMES))
word NAMES := foo bar baz
FOO := $(word 1,$(NAMES))
BAR := $(word 2,$(NAMES))
BAZ := $(word 3,$(NAMES))
wordlist NAMES := foo bar baz
FOO_BAR := $(wordlist 1,2,$(NAMES))
BAR_BAZ := $(wordlist 2,3,$(NAMES))
EMPTY := $(worldlist 1,0,$(NAMES))
firstword, lastword NAMES := foo bar baz
FOO := $(firstword $(NAMES))
BAZ := $(lastword $(NAMES))
targets and prerequisites
make rake ant gradle
file target foo:
        touch $@
file :foo do
  touch "foo"
end
<target name="check-foo">
  <available file="foo" property="foo.present"/>
</target>

<target name="do-if-foo" depends="check-foo" if="foo.present">
  FOO
</target>
file target with prerequisites main.o: main.c common.h
        gcc -c -o main.o main.c
file "main.o" => ["main.c", "common.h"] do
  sh "gcc -c -o main.o main.c"
end
order only prerequisite build:
        mkdir -p build

build/output.csv: | build
        generate-output.py > $@
file_create :build do
  mkdir_p "build"
end

file "build/output.csv" => :build do
  touch "build/output.csv"
end
phony target .PHONY: clean

clean:
        -rm *.o
task :clean do
  sh rm *.o
end
<target name="clean">
  <delete>
    <fileset dir="." includes="*.o"/>
  </delete>
</target>
prerequisite search path VPATH := src:lib

# will work if foo.c, src/foo.c
# or lib/foo.c exist:

foo: foo.c
        gcc -o $@ $<
<project default="hello" basedir="src">
  <!-- paths relative to ./src -->
</project>
phony target with prerequisites
phony target with parallelizable prerequisites multitask <parallel>...</parallel>
default target # if .DEFAULT_GOAL variable is not set the
# first target that does not start
# with a period is executed.

.DEFAULT_GOAL := foo
# error to invoke Rake without a target when no :default
# task is defined

task :default => [:foo]

task :foo do
  puts "foo"
end
<!-- if no target specifed on cmd line
or as a default ant does nothing successfully -->

<project default="foo">
  <target name="foo">
    <echo>foo</echo>
  </target>
</project>
defaultTasks 'foo'

task foo {
  doLast {
    println 'foo'
  }
}
target with argument echo.%:
    @echo $*
delete target on error .DELETE_ON_ERROR:
do not delete if intermediate .SECONDARY
do not delete if interrupted .PRECIOUS
conditionally define target all:
ifeq ($(USER), root )
        echo do not run as root
else
        gcc $(SRC_FILES)
endif

# also ifneq
recipes
make rake ant gradle
shared recipe # if run as 'make frobnicate', $@ contains
# 'frobnicate'

frobnicate twiddle:
        gcc -o $@ $@.c
shared target $ cat > Makefile.common
clean::
        -rm -f *.o

$ cat > Makefile
include Makefile.common

clean::
        -rm -f *.pyc

# removes .o and .pyc files:
$ make clean
multiple target recipe
dummy file technique
foo.tab.cpp foo.tab.hpp: parser

parser: foo.ypp
        bison -d $<
        touch $@
multiline variable define SETUP =
echo performing setup...
mkdir -p build
date +'%s' > build/start.timestamp
endef

foo.txt:
        $(SETUP)
        process $@
universal recipe %:
        echo target is $@
empty recipe nothing: ; task :nothing <target name="nothing"></target>
invoke shell ls_etc:
        ls /etc
task :ls_etc do
  sh "ls /etc"
end
<exec executable="ls">
  <arg value="/etc/"/>
</exec>
invoke shell in different directory ls_etc:
        (cd /etc; ls)
<exec executable="ls" dir="/etc"/>
configure shell SHELL := /bin/bash
.SHELLFLAGS := -o pipefail
suppress echoing foo:
        @echo building $@...
        gcc -o $@ $@.c
ignore error clean:
        -rm *.o
shell variable identify_invoker:
        echo $$USER
prerequisite variables
first, all, newer than target
$< $^ $?
target variable $@
shared prefix variable $*
set variable in recipe get_date:
        $(eval DATE := $(shell date))

echo_date: get_date
        echo $(DATE)
date = nil

task :get_date do
  date = Time.now
end

task :echo_date => :get_date do
  puts date
end
prompt user none task :foo do
  STDOUT.print "Enter foo: "
  input = STDIN.gets.strip
  STDOUT.puts "foo: #{input}"
end
<target name="foo">
  <input message="Enter foo:" addproperty="foo"/>
  <echo>foo: ${foo}</echo>
</target>
fatal error foo:
        echo foo not implemented
        false
<target name="foo">
  <fail message="foo not implemented"/>
</target>
rules
make rake ant gradle
pattern rule %.o: %.c
        $(CC) -o $@ $<
rule '.o' => '.c' do |t|
  sh "gcc -c -o #{t.name} #{t.source}"
end
multiple target rule %.tab.cpp %.tab.hpp: %.ypp
        bison -d $<
built-in rules # make behaves as if the following
# rules are defined:

%.o: %.c
        $(CC) $(CPPFLAGS) $(CFLAGS) -c $<
don't use built-in rules $ make -r [TARGET] ...
files and directories
make rake ant gradle
glob filenames
 
src := $(wildcard *.c) src = Dir.glob('*.rb')

# lazy evaluation:
src = FileList.new('*.c')
<fileset id="javaFiles" dir=".">
  <include name="*.java"/>
</fileset>

<pathconvert pathsep=" "
             property="src"
             refid="javaFiles"/>
recursively glob filenames src := $(shell find . -name '*.c') src = Dir.glob('**/*.c')

# lazy evaluation:
src = FileList.new('**/*.c')
path join $(join /etc/,passwd) File.join("/etc", "passwd")
dirname and basename # leaves trailing slash (can operate
# on multiple arguments):

$(dir /etc/passwd)

$(notdir /etc/passwd)
# no trailing slash:
File.dirname("/etc/passwd")

File.basename("/etc/passwd")
<dirname property="file_dir" file="/etc/passwd"/>
<echo>${file_dir}</echo>

<basename property="file_base" file="/etc/passwd"/>
<echo>${file_base}</echo>
root and suffix # README
$(basename README.txt)

# .txt
$(suffix README.txt)
"README.txt"[/(\.[^.]+)$/, 1]

??
realpath realpath_example:
        ln -s /tmp t
        $(realpath t)
abspath $(abspath ..) File.expand_path("..")
delete file clean:
        rm foo.o
task :clean do
  rm "foo.o"
end
<target name="clean">
  <delete file="foo.o"/>
</target>
delete files matching pattern clean:
        rm *.o
objects = Rake::FileList["*.o"]

task :clean do
  rm objects
end
<target name="clean">
  <delete>
    <fileset dir=".">
      <include name="*.o"/>
    </fileset>
  </delete>
</target>
delete directory and contents clean:
        rm -rf build
task :clean do
  rm_rf "build"
end
<target name="clean">
  <delete dir="build"/>
</target>
copy file

don't overwrite, overwrite, to directory
copy_example:
        cp -n bar baz
        cp bar baz
        cp bar /tmp
task :copy_example do
  sh "cp -n bar baz"
  cp("bar", "quux")
  cp("bar", "/tmp")
end
<target name="copy-example">
  <copy file="bar" tofile="baz"/>
  <copy file="bar" tofile="baz"
        overwrite="true"/>
  <copy file="bar" todir="/tmp"/>
</target>
copy directory and contents copy_example:
        cp -R foo.d bar.d
task :copy_example
  cp_r("foo.d", "bar.d")
end
<target name="copy-example">
  <copy todir="bar.d">
    <fileset dir="foo.d"/>
  </copy>
</target>
move file move_example:
        mv -n bar baz
        mv bar2 quux
        mv bar3 /tmp
task :move_example do
  sh "mv -n bar baz"
  mv("bar2", "quux")
  mv("bar3", "/tmp")
end
<target name="move-example">
  <move file="bar" tofile="baz"/>
  <move file="bar" tofile="baz"
        overwrite="true"/>
  <move file="bar" todir="/tmp"/>
</target>
make directory # fails if foo.d exists:
foo.d:
        mkdir $@
# fails if foo.d exists:
task :foo_d do
  mkdir "foo.d"
end
<!-- succeeds if foo.d exists -->
<target name="foo-dir">
  <mkdir dir="foo.d"/>
</target>
make directory and parents foo/bar/baz.d:
        mkdir -p $@
task :bar_dir do
  mkdir_p("foo/bar/baz.d")
end
<target name="baz-dir">
  <mkdir dir="foo/bar/baz.d"/>
</target>
symbolic link altfoo: foo
        ln -s $< $@
task :altfoo do
  ln_s("foo", "altfoo")
end
<target name="altfoo">
  <symlink link="altfoo" resource="foo"/>
</target>
synchronize directories backup_foo:
        mkdir -p backups
        rsync -ur --delete foo backups
task :backup_foo do
  mkdir_p "backups"
  sh "rsync -ur —delete foo backups"
end
<target name="backup-foo">
  <sync todir="backups/foo">
    <fileset dir="foo"/>
  </sync>
</target>
extract from tarball stuff: stuff.tar.gz
        tar xf $<
create tarball stuff.tar.gz: stuff
        tar cfz $@ $<
create jar file class.list:
        find target -name '*.class'

example.jar: class.list
        jar cf $@ @class.list
<jar jarfile="${dist}/lib/MyProject-${DSTAMP}.jar" basedir="${build}"/>
apply patch patch.foo: foo.diff foo
        patch -p0 < $<
gzip gzip test.tar <gzip src="test.tar" destfile="test.tar.gz"/>
gunzip gunzip test.tar.gz <gunzip src="test.tar.gz"/>
zip
chmod chmod
chown
chgrp
mktemp
mktemp -d
touch touch foo file :foo
  touch :foo
end
wget url := http://www.google.com

robots.txt:
        wget $(url)/$@
curl url := http://www.google.com

robots.txt:
        curl $(url)/$@ > $@
test -e test -e <available>
test -f test -f <available type="file">
test d test -d <available type="dir">
test -L
test -r
test -w
text -x
compilation
make rake ant gradle
javac <javac srcdir="${src}" destdir="${build}"/>
create dependencies
flex
bison
templates
make rake ant gradle
create file from template $ cat Makefile
all: example.txt

%.txt: %.m4
        m4 -Dyear=2000 < $< > $@

$ cat example.m4
Replace `year' with year.

$ make

$ cat example.txt
Replace year with 2000.
$ cat Rakefile
require 'erb'

file "example.txt" => ["example.erb"] do
year = '2000'
  template = ERB.new(File.open("example.erb").read, nil, '-')
  File.open("example.txt", 'w') do |f|
    f.puts template.result(binding)
  end
end

$ cat example.erb
Replace <%%= year -%> with <%= year -%>.

$ rake example.txt

$ cat example.txt
Replace <%= year -%> with 2000.
$ cat build.xml
<project>
  <target name="example">
    <filter token="year" value="2000"/>
    <copy todir="output" filtering="true">
      <fileset file="example.txt"/>
    </copy>
  </target>
</project>

$ cat example.txt
Replace @@year@@ with @year@.

$ ant example

$ cat output/example.txt
Replace @2000@ with 2000.
maven repositories
make rake ant gradle
build file with artifact dependency download Apache Ivy JAR and put it in ~/.ant/lib

$ cat ivy.xml
<ivy-module version="2.0">
  <info organisation="org.example"
        module="example"/>
  <dependencies>
    <dependency org="commons-io"
                name="commons-io"
                rev="1.3.2"/>
  </dependencies>
</ivy-module>

$ cat build.xml
<project xmlns:ivy="antlib:org.apache.ivy.ant"
         name="example">
  <target name="resolve">
    <ivy:retrieve />
  </target>
  <ivy:cachepath pathid="example.classpath"/>
  <target name="compile" depends="resolve">
    <javac srcdir="."
           classpathref="example.classpath"/>
  </target>
</project>
set repository
libraries and namespaces
make rake ant gradle
include include ./Makefile.common load './Rakefile.common' <import file="./common.xml"/>
also <include>
include if exists -include ./Makefile.optional rakefile_lib = './Rakefile.optional'

if File.exists? rakefile_lib
  load rakefile_lib
end
namespace none $ cat Rakefile
namespace :foo do
  task :bar do
    puts "foo:bar"
  end
end

task :bar => ["foo:bar"] do
  puts "bar"
end

$ rake bar
foo:bar
bar

$ rake foo:bar
foo:bar
recursion
make rake ant gradle
recursive invocation foo:
        $(MAKE) -C subdir $@
export variable export foo none
export all variables export none
unexport variable unexport foo none
_____________ _________________________________________________________ _________________________________________________________ _________________________________________________________ _________________________________________________________

version used

The version used for this reference sheet.

show version

How to get the version of the build tool.

name of build file

The customary name for the build file.

hello world

How to use the build tool to write to standard out.

build hello world

How to build an executable which writes to standard out.

statement separator

How statements are terminated.

comment

How to put a comment in the build file.

Invocation

specify build file

How to use to use a build file other than the default.

dry run

How to do a dry run. Commands that would be performed to build the target are echoed, but no actions are performed.

keep going after errors

When multiple targets are specified, how to keep going even if some of the targets fail to build.

run jobs in parallel

How to run recipes in parallel.

make:

list targets

List the targets which can be specified on the command line.

make:

touch targets

How to run touch on all targets without building them. This updates the last modified timestamp to the present and creates an empty file if the target does not exist.

always rebuild

How to rebuild all targets, even if they are already built and up-to-date.

up-to-date test

How to test whether the targets are up-to-date. Returns a exit status of zero if they are.

silent

How to run the build tool silently, or at least with minimal output.

Variables

set and access variable

How to set a variable; how to use the value stored in a variable.

make

Make variables are implemented as macro substitutions. There is no difference between the $(foo) and ${foo} syntax. There is, however, a difference between using := and = to perform variable assignment.

:= is the immediate assignment operator. It expands variables on the right hand side when the assignment is performed. A variable defined by immediate assignment is called simply expanded. The immediate assignment behaves like assignment in other programming languages.

= is the deferred assignment operator. It expands variables on the right each time the variable on the left is referenced. A variable defined by deferred assignment is called recursively expanded. Variables can be used on the left side of a deferred assignment before they are defined. If a $(wilcard …) or $(shell …) function is used on the right side, the value of the variable may be different each time it is referenced. Deferred assignment should perhaps be regarded as a misfeature of the original version of make.

The flavor function can be used to determine whether a variable is simple or recursive:

rec = foo
sim := foo

rec sim:
        # echoes "recursive" or "simple":
        @echo $(flavor $@)

The variable expressions $(foo) or ${foo} can be used on the left side of an assignment. The variables are immediately evaluated to determine the name of the variable being defined.

Whitespace after an assignment operator is trimmed. It is nevertheless possible to set a variable to a space:

empty :=
space := $(empty) $(empty)

undefined variable access

What happens when a variable which is undefined is accessed.

redefine variable

What happens when a variable is redefined.

append to variable

How to append to variable.

conditionally define variable

How to conditionally define a variable.

environment variable

How to access an environment variable.

make:

Every environment variable becomes a make variable.

The origin function can be used to identify which variables were environment variables.

example of the origin function and a list of return values

set variable if doesn't exist

How to set a variable if it is not already defined.

raise error if variable not set

How to exit before executing any recipes if a variable is not set.

warn if variable not set

How to write a message to standard error if a variable is not set.

Strings

pattern substitution

global substitution

make

The comma is used as an argument separator. If a comma appears in a match pattern or a replacement pattern, one must store the pattern in a variable:

comma := ,
comma_list := foo,bar,baz _
semicolon_list := $(subst $(comma),;,$(comma_liost))

shell command substitution

How to get the output of a shell command as a string.

Arrays

foreach

dirname and basename

Targets and Prerequisites

file target

A target which creates a file. The target should not execute if the file exists and is more recent than its prerequisites.

file target with prerequisites

order only prerequisite

How to define a prerequisite which will be built if it does not exist before the target, but will not trigger a re-build of the target if it is newer than the target.

Directories should usually be order only prerequisites, because the last modification timestamp of a directory is updated whenever a file is added to or removed from the directory.

phony target

A target which always executes, even if a file of the same name exists.

make:

A target can be declared phony by making it a prerequisite for the .PHONY target. This ensures execution of the recipe even if a file with the same name is created in the Makefile directory.

Older versions of Make which don't support .PHONY use the following idiom to ensure execution:

clean: FORCE
        rm $(objects)
FORCE:

default target

Which target is executed if the build tool is run without an argument; how to specify the target which is executed if the build tool is run without an argument.

universal target

How to define a default recipe.

Recipes

shared recipe

How to define targets with a common recipe.

shared target

How to define a target with multiple recipes. If the target is invoked, all recipes are executed.

make:

If a target has multiple recipes, all of them must use the double colon syntax. The recipes are executed in the order in which they are defined in the Makefile.

Each recipe can have its own prerequisites. Recipes for which the the prerequisites exist and the target is newer than the prerequisites will not execute.

empty recipe

invoke shell

multiline variable

How to define a variable containing newlines.

Rules

variables used by built-in rules for c

Many of the variables have an accompanying variables for setting flags:

  • AR: ARFLAGS
  • AS: ASFLAGS
  • CC: CFLAGS
  • CXX: CXXFLAGS
  • CPP: CPPFLAGS
  • ld: LDFLAGS
  • LEX: LFLAGS
  • YACC: YFLAGS

Files and Directories

glob filenames

How to match files in a directory using pattern matching.

Shell-style file pattern matching uses ? to match a single character and * to match zero or more characters.

recursively glob filenames

delete file

delete files matching pattern

delete directory and contents

copy file

copy directory and contents

move file

make directory

make directory and parents

symbolic link

Compilation

Templates

create file from template

How to generate a file from a template with macro expansion.

Maven Repositories

Libraries and Namespaces

include

Recursion

recursive invocation

How to invoke the build tool on a build file in a subdirectory.

make:

Using $(MAKE) instead of make guarantees that the same version of make is used and passes along command line options from the original invocation.

Make

If precautions are taken it is possible to write a Makefile which will build on a variety of POSIX systems:

A Makefile consists of rules which have the following format:

TARGET ... : PREREQUISITE ...
        ACTION
        ...

The rule consists of usually one target, zero or more prerequisites, and zero or more actions. The actions are collectively called the recipe for the rule.

When multiple targets are provides the effect is the same as defining separate rules (one for each target) with the same prerequisites and actions. The targets are not necessarily synonyms since the actions can inspect the $@ variable to get the target name.

When the target is invoked, make will first execute any of the prerequisites which can be defined using rules. If make cannot find a rule for a prerequisite it will exit with an error. Then make will execute the recipe.

The actions of a recipe are a sequence of lines, each starting with a tab character and containing a shell expression. If the last character on an action line is a backslash \, then the action continues on the following line. make will interpret any makefile variables in the action and then fork a shell to execute the shell expression. Makefile variables start with a dollar sign. Use duplication to pass a dollar sign character to the shell. Make echoes the action before executing it, but this can be suppressed by putting an ampersand after the tab character.

Here is an example of defining and using a Makefile variable:

hello = Hello, World!

hello :
        @echo $(hello)

There is target with the same name as the variable. Targets and variables live in separate namespaces so there is no conflict.

Here is an example of how to define a suffix rule. In the recipe $@ refers to the target and $< refers to the dependency.

%.html: %.md
        markdown $< > $@
  • files with the same name as targets, .PHONY
  • invoking make, default rule

Rake

rake.rubyforge.org

Ant

Apache Ant Manual

Gradle

Gradle User Guide

Build Terminology

A project is a directory and the files it contains.

A repository is a project under version control.

A build generates files that are not kept under version control.

A build script is code which performs a build.

A build tool executes a build script.

An install copies files from a project to locations on the local host outside of the project. Alternatively an install creates links from locations on the local host outside of the project to files inside the project.

A deploy places files from a project on a remote host.

A target is a file in a project which is built instead of kept under version control.

A dependency is a file that must exist to build a target.

The dependency graph describes the files that must exist to build a target. Each node is a file. Each directed edge points from a target to a dependency of that target.

A recipe is code which builds a target from the dependencies.

A rule is a recipe which can build multiple targets of a type. A rule usually exploits conventions in how the files are named.

A task is code executed by build tool which does not create a target.

JavaScript Builds

Java Builds

Maven standard directory layout

Introduction to the Standard Directory Layout
Sbt Directory Structure

The Maven Standard Directory Layout prescribes the following directory structure for JVM projects:

  • src/main/java
  • src/main/language
  • src/main/resources
  • src/main/filters
  • src/test/java
  • src/test/language
  • src/test/resources
  • src/test/filters
  • target
  • README.txt
  • pom.xml
  • build.xml

Source code goes into src. Java source code, other than tests, goes into src/main/java. Class files and other files generated by the build system go into target. Build files are in the root directory.

Sbt uses the standard directory layout. It uses these additional directories and files:

  • src/main/scala
  • src/test/scala
  • project
  • build.sbt

The project directory contains additional .sbt and .scala files which are part of the build system.

artifacts and repositories

Maven repositories call the entities which they make available artifacts. Usually they are JAR files. Each artifact is identified by a groupId, artifactId and version. The groupId is sometimes the reversed domain name of the organization that produced the artifact.

An organization may set up its own repository, but there is a widely used public repository called the Central Repository which has a web site for browsing the available artifacts.

targets

validate
compile
test
package create JAR file
integration-test
verify
install copy package to local repository
deploy copy package to remote repository
clean
site create documentation

pom.xml

Create the file src/main/java/Example.java to be compiled:

import java.io.File;
import java.io.IOException;
import org.apache.commons.io.FileUtils;

public class Example {
    public static void main(String[] arg) {
        File f = new File("/tmp/foo/bar/baz");
        try {
            FileUtils.deleteDirectory(f);
        }
        catch (IOException e) {

        }
    }
}

Create a pom.xml file:

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.example</groupId>
  <artifactId>example</artifactId>
  <version>1.0</version>
  <packaging>jar</packaging>

  <name>Maven Example</name>
  <url>http://example.org</url>

  <dependencies>
    <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>1.3.2</version>
    </dependency>
  </dependencies>
</project>

Compile the code and package it in a JAR:

$ mvn compile
$ mvn package

Windows Builds

nmake

NMAKE Reference

Visual Studio includes two tools for building on Windows: NMAKE and MSBuild.

NMAKE is similar to Unix make. Recipes use the Windows command prompt instead of a Unix shell.

msbuild

MSBuild Reference

MSBuild is similar to Ant. It uses an XML file to configure the build. The XML file has a .proj suffix.

To get the version of MSBuild that is installed:

>  msbuild /version

To run an MSBuild file which echoes "Hello, World!":

> type hello.proj
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Target Name="Build">
    <Message Text="Hello, World!" />
  </Target>
</Project>

> msbuild

If the working directory contains multiple .proj files, we must specify which one to use:

> msbuild hello.proj

If a project contains multiple targets, we can use the /t switch to specify the target. We can specify multiple targets if we separate them by commas or semicolons:

> type two.proj
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Target Name="foo">
    <Message Text="foo" />
  </Target>

  <Target Name="bar">
    <Message Text="bar" />
  </Target>
</Project>

> msbuild /t:foo two.proj

> msbuild /t:bar two.proj

> msbuild /t:foo,bar two.proj

A build file can specify the default target to be run using the DefaultTargets attribute of the Project element. Separate the names of multiple targets with semicolons:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
         DefaultTargets="foo;bar">
  <Target Name="foo">
    <Message Text="foo" />
  </Target>

  <Target Name="bar">
    <Message Text="bar" />
  </Target>
</Project>

If there is no default target and no /t switch, the first target in the build file is invoked.

If there are tasks which can be run in parallel, we can instruct MSBuild to use multiple cores. The /m switch is similar to the -j flag of make:

> msbuild /m:10

We can define properties (like in Ant) at the top of a build file and later use them in targets:

> type hello2.proj
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <Msg>Hello, World!</Msg>
  </PropertyGroup>

  <Target Name="Build">
    <Message Text="$(Msg)" />
  </Target>
</Project>

> msbuild hello2.proj

We can override the value of a property when we invoke MSbuild:

> msbuild /p:Msg="Goodbye, World!" hello2.proj

We can define a property containing the value of an environment variable:

<PropertyGroup>
  <Invoker>$(USERNAME)</Invoker>
</PropertyGroup>
issue tracker | content of this page licensed under creative commons attribution-sharealike 3.0