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
Ant
Gradle
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
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 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>