Skip to the content.

Quick links for this challenge

Quick links to reference sections

These tend not to vary much from challenge to challenge.

Overview

The purpose of this challenge is help you become more familiar with writing makefiles. You only task is to write the Makefile. That’s it.

Learning Objectives

  1. Writing your own Makefile
  2. Defining make variables
  3. Using one or more make functions
  4. Using make for tasks beyond compiling / software construction

How big is this challenge?

You need to fill in a Makefile. Our solution required about 15 non-blank lines of text. The tricky part is using make variables, functions, and a couple of kinds of rules, in some ways you’ve not seen before.

Getting Started

To get started, follow these steps:

  1. Download the starter code.
  2. Unzip the project-make-makefile.zip with the following command unzip -d PROJECT project-make-makefile.zip. This will create a new directory called PROJECT. You can replace PROJECT with a directory name of your choice.
  3. cd into the PROJECT directory and investigate the project.

If you follow the above steps correctly, you should have the following folder structure after unzipping (assuming the project name is “PROJECT”):

PROJECT/
  include/
  input/
  lib/
  obj/
  src/
  test/
  Makefile

After you have the code extracted you should go ahead and investigate. You can run make from the command line and your project will build and produce a potential error results. See more information below.

Code Structure

This exercise contains the following important folders:

Testing The Makefile

The natural way to casually test the Makefile is to just run make and see if it compiles copysome.c and generates the executable copysome. You can also check the output directory and see if all the files are generated with the correct number of lines copied.

You will similarly want to test make -e NUMLINES=20 or something (that’ll make more sense after reading the instructions).

TODO

Requirements

So the only thing you need to do is write the Makefile, but this makefile needs to do a number of things by default (make) or if given the target all (make all). Here are the things, which should each be their own target, connected as necessary using pre-requisites.

1. Compile src/copysome.c into an executable copysome, if necessary.

Pretty self explanatory, you have a sole source file src/copysome.c, and need to use it to produce an executable named copysome (at the PROJECT directory level).

You should one rule to build obj/copysome.o from src/copysome.c, and another rule to build copysome from obj/copysome.o. This is just good practice to get used to, though it’s not absolutely necessary in this small project.

2. Create the output directory, if necessary.

Note that the output directory does not exists by default. The Linux command mkdir -p will be useful for this Make target.

3. Run copysome on all input files

copysome takes arguments in the form ./copysome [-n num_lines] [in_file] [out_file]

The Makefile should have a variable NUMLINES that is set to 10. (One can override the value of this variable with -e NUMLINES=[num_lines], e.g, -e NUMLINES=20.)

And so for each file input/[file_name].in, an associated file named output/[file_name].out should be produced that contains the first NUMLINES lines of [file_name].in. Note that the rule here should be a pattern rule (see the slides!). Also, this rule needs both copysome and output as additional pre-requisites (why?).

4. You should also provide a clean target.

This target should delete the output folder and everything in it, as well as your .o file(s).

Tips/Specific Points

TABS! For the love of all that is good, beware the fact that Makefiles need tabs (not spaces). We highly recommend you try and make whitespace characters visible in the editor of your choice.

Also, beware that a Makefile must be named exactly Makefile with no extension. Some systems “helpfully” add a .txt if you don’t watch out for it.

The Makefile should be placed in the top directory of the project (not in src).

We want to emphasise the simplicity and power of the makefile rule structure. The basic rules are of the form:

[rule_name/output]: [dependencies]
	[commands]

(Notice that tab before [commands]!)

This is generally seen in the form of compiling files, such as:

challenge_test: test/test.cpp
	gcc test/test.cpp -o challenge_test -lstdc++ -lgtest -lgtest_main -lpthread

(Where you could have the all rule claim challenge_test as a dependency, or invoke make challenge_test directly.)

But commands in Makefiles are not restricted to compilation commands. An example is a clean rule:

clean:
	rm -f output/*.out
	rm -f copysome obj/copysome.o

(So make clean will not recursively consider any dependencies, but will invoke the rm commands.)

In this challenge you will also find use for pattern rules, which have the form:

output_pattern: input_pattern
	commands

where output_pattern and input_pattern have a % in them. For example, to build a .xyz file from a .abc file:

%.xyx: %.abc
	commands

You will also find use for at least one static pattern rule, which restricts a pattern rule to apply to specific outputs:

outputs: output_pattern: input_pattern
	commands

Your Makefile should not assume any specific .in files exist. Rather, it should process every .in file in the input directory. The wildcard make function will be helpful for discovering the input files, and the patsubst function can help create the names of the correspinding output files. Our presention described how to describe a dependency for each file in a set of files determined by previous variable settings in the Makefile (static pattern rule), which will also be helpful. Other commands that may be useful include mkdir -p for guaranteeing the existence of a directory, rm -f for deleting a file that may or may not exist in a clean rule, and likewise rm -rf for deleting a directory that may or may not exist and everything in it.

Good Makefile style suggests that each rule should take care of only one kind of action, but may have dependencies than can ultimately cause other things to happen. Consider, for example, what is needed in order to run copysome - it needs to exist, and its output directory also needs to exist. That suggests possible dependencies in your rule to run copysome, etc.

A final reminder. A Makefile is not procedural, i.e., not something that executes top to bottom. Rules can appear in any order, with the one exception that the first rule is the default rule (most commonly with the target all, meaning “make everything”).

In sum, you don’t need a lot of lines of Makefile “code”, but (as in any programming) they need to be the right lines!

Autograder

The autograder is used to test your code more deeply. If you follow the specifications of this exercise exactly then you should be able to pass all of the tests that you are provided and all of the tests the autograder is using to check your solution.

To run the autograder on your solution you must upload your Makefile to Gradescope.

General Information and Project Policies

Academic Honesty

All work that is completed in this assignment is your own. You may talk to other students about the problems you are to solve, however, you may not share code in any way. What you submit *must be your own work.

You may not use any code that is posted on the internet. If you are not sure it is in your best interest to contact the course staff. We will be using software that will compare your code to other students in the course as well as online resources. It is very easy for us to detect similar submissions and will result in a failure for the exercise or possibly a failure for the course. Please, do not do this. It is important to be academically honest and submit your work only. Please review the UMass Academic Honesty Policy and Procedures so you are aware of what this means.

Copying partial or whole solutions, obtained from other students or elsewhere, is academic dishonesty. Do not share your code with your classmates, and do not use your classmates’ code. If you are confused about what constitutes academic dishonesty you should re-read the course policies. We assume you have read the course policies in detail and by submitting this project you have provided your virtual signature in agreement with these policies.

Gradescope

We use Gradescope to run our autograding software and record your grade for these assignments. You may submit this assignment as many times as possible up to the due date. If you encounter a problem with the autograder you should contact the course staff immediately.

Submission

You must submit your Makefile. To do this you will need to download the Makefile from the EdLab environment to your local machine then upload Makefile to Gradescope. Gradescope will run your submission in our autograder environment and give you a report of what tests passed and which did not. You are welcome to submit as many times as you would like.

A word of caution: some means of downloading the Makefile will add a .txt extension (why they do this, or why they think that would be a civilised thing to do is beyond me). The file you submit to Gradescope must be named Makefile, not Makefile.txt. I recommend making extensions visible on your machine to make sure you notice anything off.