CS 3700 Labs

Lab 2: Makefiles, Project Deployment

Pre-lab Instructions

Following the instructions from the last lab, be sure to have installed VirtualBox and Vagrant and downloaded the box 'khoury/CentOS-7.6'

Project Submissions

Everyone will be submitting their complete projects through a series of scripts hosted on login-student, collectively known as the 'turnin system.' To access them you need to be ssh'd into login-student, which can be accomplished with the following command in a Terminal on Linux and Mac OS, and PowerShell on Windows:

$ ssh <Your Khoury Username>@login.ccs.neu.edu

The scripts are located in the folder /course/cs3700sp20/bin/. To be able to use the turnin system, you must first register for the course with:

$ /course/cs3700sp20/bin/register-student <Your NEU ID here>

You can only run register-student once. If you enter an incorrect NEU ID when registering, you must contact your instructor to request a change, otherwise we will not be able to include your projects when calculating your final grades!

Make

Make can best be described as the original build tool. Originally written in the late 1980s by Richard Stallman and Roland McGrath, make is by far the single most available build tool, being the defacto default on Linux and available on both Windows and Mac OS.

On Interpreted languages

For languages that can be interpreted naively by the machine (eg. Python, Ruby, Bash, Racket) make serves little purpose. Ensuring that the correct interpreter is referred to and that the file is executable (see note below) is sufficient. However, As part of this class, always provide a Makefile, even if it is empty.

Note: The Shebang and Permissions

In linux, every plain-text executable file requires the magic sequence "#!" followed by the fully qualified path of the interpreter to be called. Eg:

#!/bin/bash
#!/usr/bin/env python3
#!/usr/bin/env ruby
...

If you don't do so, the shell will attempt to interpret the file natively, resulting in errors that look like the following:

$ ./script.py
./script.py: line 3: syntax error near unexpected token `('
./script.py: line 3: `def print_example():'

If you have a shebang and it's not working, eg:

$ ./script.py
bash: ./script.py: Permission denied

Check that the script is marked as executable:

$ ls -l ./script.py
-rw-rw-r-- 1 tsachleben tsachleben 99 Jan 14 22:08 script.py

And make it executable as necessary:

$ chmod +x script.py

Anatomy of a Makefile

 
# Makefile (Example 1)
# This line is a Comment

target: prerequisit(s)
 "\t" recipe_step1
 "\t" recipe_step2
 "\t" ...
# In total, the above three pieces form a "rule"

Makefiles consist of one or more comments and one or more 'rules', of which, the first is the default recipe. When executing make without any arguments (as we will when building your projects) make will assume you want the default recipe.

Deferring to other build tools

For languages that come with their own build tool(s) (Golang, Rust's Cargo) your makefile bridges the gap to let make know what on the build tool to call. Eg.

# Makefile (Example 2)

client: 
  go build cmd/client
# Makefile (Example 3)

client: 
  cargo build

Java

Java and other JVM languages are unusual, in that they must be compiled yet are interpretated at runtime. As such, Java projects that need to be executable at a command-line can have a Makefile and a script that calls the java VM. Eg.

# Makefile (Example 4)

all: main.class

main.class: main.java
  javac main.java
#!/bin/bash
# example

java -classpath $(pwd) main

Intermediate Make

In addition to comments and recipes (described above) make has several other faculties that extend the very basic functionality.

Variables

Make allows you to make your life easier when having to type something multiple times through the use of variables

# Makefile (Example 5)

SRC_DIR = ./src
CLASS_DIR = ./class

all: $(CLASS_DIR)/main.class

$(CLASS_DIR)/main.class: $(SRC_DIR)/main.java

Make Patterns

Make allows for defining a rule on a class of files through the use of the % token. The % token acts as a glob expression (ie the regex .*) for matching classes of file names in a rule. Eg. the target %.o matches all files ending in the .o extension. To enable meaningful use of the % token, make provides a set of special variables that are dynamically defined per-rule. Some of the most frequently used are listed below.

$@ # Name of the matched target
$< # Name of the _first_ prerequisite 
$? # All prerequisites that are newer than the target
$^ # All prerequisites

Let's apply these to our java example from before:

# Makefile (Example 6)

SRC_DIR = ./src
CLASS_DIR = ./cls

all: $(CLASS_DIR)/main.class $(CLASS_DIR)/include.class
  jar cvfm example.jar Manifest.mf $?

$(CLASS_DIR)/%.class: $(SRC_DIR)/%.java
  javac -d $(CLASS_DIR) $<

.PHONY: all

Configuring Make's behavior

Several 'directives' exist to change how make executes. Some affect the how make interprets the Makefile as a whole, while others change the behavior of individual recipes

Other artifacts in make

Make is a very clever tool, and can build/move/funge/acquire just about any artifact you want it to. If we feel your Makefile is doing something off-color, we may review your project for academic integrity. If you are doing anything unusual, please clear it with a Professor or Lead TA before submitting!

References

make manual