Introduction • Use of makefiles to manage the build process • Declarative, imperative and relational rules • Environment variables, phony targets, automatic variables, macros and pattern matching in makefiles • Creating recursive makefiles
Build Management • During the implementation phase, the process for constructing a system should be engineered • What are the steps needed to build the system? • Who is authorised to build a system? • Individual programmers, build/configuration managers • When are system builds performed?
Build Management and Tools • Most modern programming environments have build management capabilities built into them • E.g. a Java development environment typically has the notion of a “project” and it can compile all project files in the correct order (and it only compiles files dependent on a change)
UNIX Build Management • In UNIX environments, a common management tool is “make” • Make uses three specification styles • Declarative • Imperative • Relational • These styles are combined into “the makefile”
Specification Styles • Operational or Imperative • Describes the actions to be taken • Descriptive or Declarative • Describes the desired properties • Structural or Relational • Describes relationships between things
The Make Specification Language • Dependencies between things (modules, files, etc.) are relational • Rules for creating new things are declarative • The Actions needed to carry out the rules are imperative
Make Command Line $ make • Make will look for a file called makefile or Makefile • Make looks inside the file for a target • A target can be a file to be generated (but not necessarily!) • Different targets can be specified - frequently all and clean - and called as make all. If no target specified (i.e. just make is called) then it picks the first in the file
A Typical Make Rule T1 is the target • T2 and T3 are dependencies - T1 depends on T2 and T3. These are other targets in the file • A1 and A2 are actions written as a tab followed by a list of UNIX (shell) commands T1: T2 T3 A1 A2
Example Makefile Targets javac calls a UNIX Java compiler. • java runs a UNIX Java program. • jar archives a set of Java classes in one JAR file. main1.class: main1.java javac main1.java Action
Example Makefile main1.class: mysubroutines.jar main1.java javac main1.java mysubroutines.class: mysubroutines.java javac mysubroutines.java mysubroutines.jar: mysubroutines.class jar cvf mysubroutines.jar mysubroutines.class
More on Rules target: dependencies actions • Target and dependencies are generally files. • If any dependency is modified more recently than its target then make performs the associated actions.
More on Rules (2) • An action can be any shell command, one per line. Each action must begin with a tab. All variables used in actions must have ( ) brackets round them – e.g. $(PATH). Different to SHELL programming. • Typically, actions create the target file from the dependency files. • GOTCHA: Watch out for actions that do not actually create their target.
More on Actions • Actions do not have to invoke a compiler • Targets do not have to be files • Targets, like clean which do not create files are called phony targets
Example Makefile all: main1.class main1.class: mysubroutines.jar main1.java javac main1.java mysubroutines.class: mysubroutines.java javac mysubroutines.java mysubroutines.jar: mysubroutines.class jar cvf mysubroutines.jar mysubroutines.class
NOTE: Remember this will remove files! clean: rm *.class rm *.jar
Using Environment Variables • Installing in bin isn’t much use if you don’t know where it is! • Make has variable-like objects known as macros. • Environment variables, like $HOME and $PATH can be detected by make. INSTALLDIR = $(HOME)/bin install: $(INSTALLDIR)/program $(INSTALLDIR)/program: program cp program $(HOME)/bin/program
Another Makefile INSTALLDIR = $(HOME)/bin all: main1.class main1.class: mysubroutines.jar main1.java javac main1.java install: $(INSTALLDIR)/main1.class $(INSTALLDIR)/main1.class: main1.class cp main1.class $(INSTALLDIR)/main1.class
Make Macros • A Macro holds a string value • This string is defined using an equal sign and preceded by a dollar sign. • Without the brackets make assumes that the name is just one letter long: $INSTALLDIR is interpreted as $(I)NSTALLDIR
Macro Substitution • Make performs strict textual replacement to work out what variables are, so the following two rules are equivalent: program: output.o g++ output.o -o program FOO = o pr$(FOO)gram: $(FOO)utput.$(FOO) g++ $(FOO)utput.$(FOO) -$(FOO) pr$(FOO)gram
Increased Abstraction • Macros increase the level of abstraction in a Makefile mysubroutines.jar: mysubroutines.class main2.class jar cvf mysubroutines.class main2.class • is equivalent to JARFILE = mysubroutines.jar OBJECTS = mysubroutines.class main2.class $(JARFILE): $(OBJECTS) jar cvf $(OBJECTS)
Automatic Variables • Make has a special feature called automatic variables • Automatic variables can only be used within the actions of a make rule – its value depends on the target and dependencies of the rule.
Automatic Variables (2) • $@ • The target of the rule • $< • The first dependency • $ˆ • All the dependencies • $? • All of the dependencies that are newer than the target. • $* • The stem of a pattern matching rule
Example of Automatic Variable Use mysubroutines.jar: mysubroutines.class jar mysubroutines.jar mysubroutines.class mysubroutines.jar: mysubroutines.class jar $@ $< mysubs.jar: mysubroutines.class myutil.class jar mysubs.jar mysubroutines.class myutil.class mysubs.jar: mysubroutines.class myutil.class jar $@ $ˆ
Pattern Matching • NOTE: These are not present in all versions of Make – but are on unnc-cslinux and most Linux based versions. • Suppose you have a lot of classes in a directory and you want to compile them individually. • You could have a rule for each class
Pattern Matching (2) exercise1.class: exercise1.java javac exercise1.java exercise2.class: exercise2.java javac exercise2.java • Or you could use a pattern: %.class: %.java javac $<
Example Makefile INSTALLDIR = $(HOME)/bin all: main1.class install: $(INSTALLDIR)/main1.class $(INSTALLDIR)/main1.class: main1.class cp main1.class $(INSTALLDIR)/main1.class
%.class: %.java javac $< clean: rm *.class rm *.jar
Benefits of Pattern Matching • Scalability • The same rule can apply to thousands (or more) files. • Compactness • Small compact specifications are easier to understand and debug. • These are similar to the benefits of using wild-cards and regular expressions.
Managing More Complex Projects with Make • Many large projects will contain a lot of subprograms, the code will be spread over several directories and all will have to be compiled to get the final system to work. • You can manage this using several Makefiles if you chose. • Use cd in the Actions to enter a subdirectory and then you can call a makefile there.
Example of a Recursive Makefile all: cd interface; make cd program1; make cd program2; make clean cd interface; make clean cd program1; make clean cd program2; make clean
Summary • Using a tool, like make, for installation can make your life easier. It saves remembering dependencies, and typing compiler flags etc. • It will also make life easier for anyone else who wants to use your code. They only have to type make. • Typically all distributions contain a README (or possibly install.txt) which should tell you what to do, in particular if the Makefile needs to be edited.
Dag • Can we get circular dependencies? • Think about building blocks • Can one block depend on more than one block. • Can many blocks depend on only one block.
make will only re-build things that need to be re-built (object or executables that depend on files that have been modified since the last time the objects or executables were built). • target... : dependencies ... • command • ... • ... • Please note: Each command line must begin with a tab character.
myMaker2 • EXAMPLE • HelloMum.class: HelloMum.java • javac HelloMum.java • $ make -f myMaker2 • javac HelloMum.java • $ make -f myMaker2 • make: `HelloMum.class' is up to date.
HelloMum.class: HelloMum.java • echo compiling HelloMum • javac HelloMum.java • echo finished compiling HelloMum • clean: • rm *.class
Touch (to illustrate) • make -f myMaker2 • make: `HelloMum.class' is up to date. • $ touch HelloMum.java • $ make -f myMaker2 • echo compiling HelloMum • compiling HelloMum • javac HelloMum.java • echo finished compiling HelloMum • finished compiling HelloMum
macros • A macro is just text replacement • $(variable), not ${variable} as in shells
javac people.java • people.java:4: cannot find symbol • symbol : variable person • location: class people • person = new person(); • ^ • 1 error
CIRCULAR REFERENCE • HelloMum.java: HelloMum.java • javac HelloMum.java • make -f myMaker2 • make: Circular HelloMum.java <- HelloMum.java dependency dropped. • make: `HelloMum.java' is up to date.
Question • Do we need make files for scripts?
Online tutorial • http://www.eng.hawaii.edu/Tutor/Make/index.html