Learning Opportunities
This puzzle can be solved using the following concepts. Practice using these concepts and improve your skills.
Statement
Goal
A Makefile is a build script that describes dependencies between dated files, and the actions required to build a target file from its prerequisite files; actions are executed in an order such that every prerequisite is up to date before it is needed.Your task is to implement a simplified make utility: read a list of preexistingFiles with associated fileTimes, a list of goalTargets to build, and a Makefile, then print the actions that would be executed.
Makefile syntax
A Makefile is a sequence of rules and actions, with optional comments.
Comments
A
Whitespace
After using
Rules
A rule associates a target with zero or more prerequisites. Each of these is generally a file, but can be any valid token string:
target : [prereq1 ...]
To the left of the
A target may appear in more than one rule. Its prerequisites are the union of prerequisites in all its rules, and its actions are the concatenation of all associated rule actions, in order of appearance.
Actions
An action is any line that begins with a
Built-in macros
Within an action, built-in macros may appear as whole tokens. They are replaced by their expansions before the line is printed:
| Macro | Expands to
|-------|-----------
| $@ | The target being built
| $< | The first prerequisite of the associated rule
| $^ | All distinct prerequisites of the rule, space-separated,
| | preserving their order in the rule
File times
A fileTime is an integer timestamp associated with a file. The higher the fileTime, the more recently the file was last updated. A file that does not exist has no fileTime.
Circular dependencies
A circular dependency exists if any target is a prerequisite of itself, either directly or indirectly (even if the cycle involves targets not being built). The build is aborted prior
to executing any action, and the output is a single line:
Minimal Build
- Only requested goalTargets and their direct or indirect prerequisites are built.
- A target is only built if it does not exist or it has a fileTime <= the fileTime of any of its prerequisites.
- When a target is built, its fileTime is updated to a value greater than any preexisting file's fileTime.
Build Order
- goalTargets are built in the order they are given in the input.
- A target is built only after all of its prerequisites are built.
- A target is built at most once, even if it is a prerequisite of multiple other targets.
- Each target is built to completion, starting with its prerequisites, before proceeding to other targets.
- A target's prerequisites are built in lexicographic order.
Build Output
- When a target is built, each of its actions from all rules is printed, in the order they appear in the Makefile.
- An action is printed as a single line of single-space-separated tokens, with all built-in macros expanded.
- A target that has no actions produces no output, but its prerequisites are still built, and its fileTime is still updated.
- After all goalTargets are built, print
Example
The following Makefile compiles two source files into object files, then links them into a program.
all: util.o main.o
gcc -o $@ $^
util.o: util.c
gcc -c $< -o $@
main.o: main.c
gcc -c $< -o $@
Assume the sole goalTarget is
The actions output are:
gcc -c main.c -o main.o
gcc -c util.c -o util.o
gcc -o all util.o main.o
[Build complete]
Note that
Input
Line 1: An integer nFiles, the number of preexisting files.
Next nFiles lines: A preexistingFile and its integer timestamp fileTime, one file per line.
Next Line: An integer nGoalTargets, the number of goal targets to build.
Next Line: Space-delimited goalTargets, the nGoalTargets goal targets to build.
Next Line: An integer nLines, the number of lines in the Makefile.
Next nLines lines: The contents of the Makefile, one makefileLine at a time.
Next nFiles lines: A preexistingFile and its integer timestamp fileTime, one file per line.
Next Line: An integer nGoalTargets, the number of goal targets to build.
Next Line: Space-delimited goalTargets, the nGoalTargets goal targets to build.
Next Line: An integer nLines, the number of lines in the Makefile.
Next nLines lines: The contents of the Makefile, one makefileLine at a time.
Output
If a circular dependency is detected:
A single line:
[Circular dependencies detected]
If a circular dependency is not detected:
One line per action executed: The tokens of the action, separated by single spaces,
with all built-in macros already expanded. Actions are printed in the order they are executed.
Final line:
[Build complete]
A single line:
If a circular dependency is not detected:
One line per action executed: The tokens of the action, separated by single spaces,
with all built-in macros already expanded. Actions are printed in the order they are executed.
Final line:
Constraints
0 <= nFiles <= 20
1 <= length(preexistingFile) <= 100
1 <= length(goalTarget) <= 100
0 <= fileTime <= 1000
1 <= nGoalTargets <= 20
1 <= nLines <= 100
0 <= length(makefileLine) <= 100
preexistingFile consists of printable ASCII characters and does not contain whitespace,$ , : , or # .
All fileTime values may be assumed to be in the past.
Within the Makefile:
- All Makefiles are syntactically valid.
- At least one rule is defined.
- Every file consists of 1-100 printable ASCII characters and does not contain whitespace,$ , : , or # .
- Every prerequisite and every goalTarget is either a preexistingFile or is the target of at least one rule.
-$< is only used in actions with a rule having at least one prerequisite.
- Every action is preceded by at least one rule.
- Every action has at least one token.
-TAB characters appear only as the first character of an action.
1 <= length(preexistingFile) <= 100
1 <= length(goalTarget) <= 100
0 <= fileTime <= 1000
1 <= nGoalTargets <= 20
1 <= nLines <= 100
0 <= length(makefileLine) <= 100
preexistingFile consists of printable ASCII characters and does not contain whitespace,
All fileTime values may be assumed to be in the past.
Within the Makefile:
- All Makefiles are syntactically valid.
- At least one rule is defined.
- Every file consists of 1-100 printable ASCII characters and does not contain whitespace,
- Every prerequisite and every goalTarget is either a preexistingFile or is the target of at least one rule.
-
- Every action is preceded by at least one rule.
- Every action has at least one token.
-
Example
Input
2 main.c 100 util.c 100 1 all 6 all: util.o main.o gcc -o $@ $^ util.o: util.c gcc -c $< -o $@ main.o: main.c gcc -c $< -o $@
Output
gcc -c main.c -o main.o gcc -c util.c -o util.o gcc -o all util.o main.o [Build complete]
A higher resolution is required to access the IDE