What commands will make tool display? - makefile

The following is a previous exam question which I lost quite a few marks on, and I'm still not sure why.
Lets say I have a directory containing the following files:
fileB.txt makefile scriptC programA.h programA.c
And a makefile whose contents are as follows :
fileE.txt: fileD.txt programA
programA fileD.txt >| fileE.txt
programA.o: programA.h programA.c
gcc -c -Wall programA.c
programA: programA.o
gcc -o programA programA.o
fileD.txt: fileB.txt scriptC
scriptC fileB.txt >| fileD.txt
CC=gcc
CFLAGS=-Wall
a) The first time you type make in the command prompt, what commands will the make tool display and execute?
b) Now lets say you update, or touch, fileB.txt, what commands will the make tool display then?
I can't replicate the program to try it myself, since I don't actually know what the program is doing. I can predict the output of simple make files, but I've never seen one like the one below, i.e using pipes and txt files. A simple explanation concerning the problem would be very helpful.

fileE.txt: fileD.txt programA
programA fileD.txt >| fileE.txt
programA.o: programA.h programA.c
gcc -c -Wall programA.c
programA: programA.o
gcc -o programA programA.o
fileD.txt: fileB.txt scriptC
scriptC fileB.txt >| fileD.txt
CC=gcc
CFLAGS=-Wall
Let's try this live/blind.
The default goal/target for this makefile is fileE.txt so that's what make will try to build by default
fileE.txt depends on fileD.txt and programA
make will then see if fileD.txt exists (it doesn't) so it will try to create it
fileD.txt depends on fileB.txt and scriptC
Both of those exist already so make will run the rule for fileD.txt
Output: scriptC fileB.txt >| fileD.txt
It doesn't matter what scriptC does here because fileD.txt is created by the shell redirection
fileD.txt is now up to date and make moves on to programA
programA does not exist so make tries to build it
programA depends on programA.o which doesn't exist so make tries to build it
programA.o depends on programA.h and programA.c
Both of those exist so make will run the rule for programA.o
Output: gcc -c -Wall programA.c
gcc creates programA.o
programA.o is now up to date so make moves on
All of programA's prerequisites are now built so make runs the rule for it
Output: gcc -o programA programA.o
All the prerequisites of fileE.txt are now up to date so make runs the rule for it
Output: programA fileD.txt >| fileE.txt
make is done
Summing up just the output from the above steps you get:
scriptC fileB.txt >| fileD.txt
gcc -c -Wall programA.c
gcc -o programA programA.o
programA fileD.txt >| fileE.txt
Now if you touch fileB.txt at this point and run `make again you get:
The default goal/target for this makefile is fileE.txt so that's what make will try to build by default
fileE.txt depends on fileD.txt and programA
make will then see if fileD.txt exists (it does) and will check its prerequisites
fileB.txt is newer than fileD.txt so make will mark fileD.txt as needing to be rebuilt
scriptC is older than fileD.txt but that doesn't change anything
The prerequisites of fileD.txt are now finished and fileD.txt needs to be rebuilt
Output: scriptC fileB.txt >| fileD.txt
fileD.txt is now up to date so make moves on
programA is older than fileE.txt (and is itself up to date) so make moves on
All the prerequisites of fileE.txt are now up to date and some were built and are now newer so make will run the rule to build fileE.txt
Output: programA fileD.txt >| fileE.txt
Summing up just the output from the above steps you get:
scriptC fileB.txt >| fileD.txt
programA fileD.txt >| fileE.txt

Use the following reasoning/rules:
Just follow the targets dependencies
If a dependency is not present it gets build (or if there is not target for it, an error occurs)
If a timestamp of the dependency is newer, the target gets (re)build.
The first target encoutered in your Makefile is executed (if no targets are given by the make command)
So assuming no targets are given rule 4 is used and fileE.txt is the 'active' target.
fileE.txt requires that fileD.txt and programA are available (rule 1). Both are not available, so they need to be build (rule 2).
fileD.txt requires that fileB.txt and scriptC are available (rule 1). No targets for these dependencies are defined, so they do not get rebuild. And fileD.txt can be build (rule 2).
programA requires that programA.o is available (rule 1). A target is provided so programA.o can be build (rule 2). Next, programA can be build (rule 2).
Now all requirements for fileE.txt are met, so it can be build as well.
Same goes when fileB.txt gets touched, but you need to apply rule 3 as well.
Using this (partial) example and information you should be able to determine which commands are executed (and on which order).

a) The first time you type make in the command prompt, what commands will the make tool display and execute?
At first the first target in the makefile is executed. Here it is fileE.txt
Since fileE.txt depends on fileD.txt and programA these will be your target now.
Similarly based on your target and dependencies the final target is obtained.
Below is the sequence in which the commands are executed. Comments inline
scriptC fileB.txt >| fileD.txt /* ouput is fileD.txt*/
gcc -c -Wall programA.c /ouput is programA.o needed by programA/
gcc -o programA programA.o /ouput is programA needed by fileE.txt/
programA fileD.txt >| fileE.txt /the final output file fileE.txt/
b) Now lets say you update, or touch, fileB.txt, what commands will the make tool display then?
Following commands are executed.
scriptC fileB.txt >| fileD.txt /* ouput is fileD.txt*/
programA fileD.txt >| fileE.txt /the final output file fileE.txt/
In this case, programA is not rebuilt as it already available.

Related

Writing compilation rule before linking rule in makefile doesn't produce executable [duplicate]

My makefile have two lines:
Rule_1
File.o : File.cpp
g++ -c File.cpp -o File.o
Rule_2
File : File.o
g++ File.o -o File
I want any change in File.cpp leads to regeneration of both File.o and File. This is done by:
Makfile:
Rule_2
Rule_1
But the following does not work and only regenerates File.o
Rule_1
Rule_2
Why? I note that, I am not using any all: in my makefile. With all: File both methods above work.
I assume your make tool is GNU Make.
Unless you specify targets on the commandline, like:
make File
make will by default attempt to make the first target
in the makefile, which in your problem case is File.o.
See How make Processes a Makefile
When you add:
all: File
at the top, all is the first target, which depends on File,
which in turn depends on File.o.
And I suggest you replace that with:
.PHONY: all
all: File
See Phony targets

makefile rule to update a variable number of targets with a batch compile

We have a proprietary compiler that can take a number of input files and process them at once:
compiler a.in
# produces a.out
compiler a.in b.in c.in
# produces a.out b.out c.out
The reason to do that is that is saves a lot of time for initialization. For thousands of files the batch version is orders of magnitude faster than compiling files individually. We also run a post-processor on the files.
Now, I have this in the (GNU) makefile, which is not taking advantage of the batch processing capabilities and updates files one by one. I want to update it to use batch compilation:
.INTERMEDIATE: $(TMP)
$(TMP): $(TMPDIR)/%.tmp: $(SRCDIR)/%.in |$(TMPDIR)
compiler $< -o $#
$(RESULT): $(RESDIR)/%.out: $(TMPDIR)/%.tmp $(SRCDIR)/%.in
post-process $< -o $#
How would I rewrite the first rule to recompile all files that have been modified with a single command, perhaps, using $?? The second rule needs to stay there and work the same.
If you are able to require GNU make 4.3+, then your life is quite simple, you can take advantage of grouped targets, like this (note the &:):
a.out b.out c.out &: a.in b.in c.in
compiler $^
If you can't require a recent version of GNU make, you're relegated to using "sentinel files", like this:
a.out b.out c.out : .sentinal ;
.sentinal: a.in b.in c.in
compiler $^
#touch $#
(be sure to include the trailing semicolon on the first rule...)

'make: nothing to be done for p1 and' no rule to make target 'clean''

I'm trying to create a makefile titled 'p1' for a project.
When I try the command make p1 it returns with make: nothing to be done for p1
Also, when I try the command make p1 clean it returns no rule to make p1 'clean.' Stop
Here is my makefile:
a.out: main.o P1LinkedList.o const_iterator.o iterator.o Node.o
g++ -std=c++11 main.o const_iterator.o iterator.o Node.o
main.o:
g++ -std=c++11 -c main.cpp
P1LinkedList.o:
g++ -std=c++11 -c P1LinkedList.cpp
iterator.o:
g++ -std=c++11 -c iterator.cpp
const_iterator.o:
g++ -std=c++11 -c const_iterator.cpp
Node.o:
g++ -std=c++11 -c Node.cpp
depend:
g++ -MM main.cpp > p1.dep
clean:
rm -f a.out *.o
What do I need to fix to have the makefile compile .o files from my .cpp files and how do I fix the issue with the clean command?
Edit:
Here are the commands I've used to compile manually:
Helens-Air:p1a helenade$ g++ -std=c++11 *.cpp
Helens-Air:p1a helenade$ ./a.out
^^ and this just continues with the program execution from there
We may have to take this in stages.
First, you seem to misunderstand the difference between a makefile name and a target name. This appears to have been a miscommunication between you and your teacher, but it's easy to clear up.
Suppose you have a makefile named "Makefile", containing the following:
foo:
#echo running the foo rule
bar:
#echo running the bar rule
If you make foo, you will get:
running the foo rule
The argument (foo) tells Make which target to attempt to build. And how did Make know which makefile to use? (After all, you could have a dozen makefiles in the working directory.) You can specify which makefile to use, but if you don't then by default Make will look for a makefile named Makefile (or makefile or GNUmakefile, don't worry about this for now). To specify a makefile with another name, like "Buildfile", you can use the -f flag:
make -f Buildfile
So "p1" ought to have been the name of a target, not a makefile. Within the makefile, rename your a.out rule to p1. Then rename the whole makefile to Makefile. Then
make p1
should work (or at least run).
Edit:
I'll go out on a limb. In the a.out rule (which should now be called the p1 rule), I notice that you have left P1LinkedList.o out of the list of object files to be linked. So try changing it:
p1: main.o P1LinkedList.o const_iterator.o iterator.o Node.o
g++ -std=c++11 main.o P1LinkedList.o const_iterator.o iterator.o Node.o
If that works, you can simplify it with an automatic variable:
p1: main.o P1LinkedList.o const_iterator.o iterator.o Node.o
g++ -std=c++11 $^
And there will be other small improvements you can make.
If it doesn't work, try ls *.cpp and see if you've overlooked some other source file.

What is causing the error `make: Nothing to be done for 'x.o'.` for some x?

I added a new target (main.o) to an existing project:
DATS_FILES = main.dats
HFILES = config.h es.h gc.h input.h prim.h print.h sigmsgs.h \
stdenv.h syntax.h term.h var.h
CFILES = access.c closure.c conv.c dict.c eval.c except.c fd.c gc.c glob.c \
glom.c input.c heredoc.c list.c c_main.c match.c open.c opt.c \
prim-ctl.c prim-etc.c prim-io.c prim-sys.c prim.c print.c proc.c \
sigmsgs.c signal.c split.c status.c str.c syntax.c term.c token.c \
tree.c util.c var.c vec.c version.c y.tab.c dump.c
src :
#echo ${OTHER} ${CFILES} ${HFILES} ${DATS_FILES}
list.o : list.c es.h config.h stdenv.h gc.h
main.o : main.dats
match.o : match.c es.h config.h stdenv.h
As can be seen above, I have tried to give the new source .dats file the same status as the .c files in the project, which have no problems building.
If I try to build the target directly I get:
make main.o
make: Nothing to be done for 'main.o'.
This happens even if I run touch main.dats. If I compile main.dats manually after make fails, then run make again, the project finishes building and the output executable runs without issue. Here is the complete Makefile.in.
you need to add a rule to specify to make how to re-create main.o starting from main.dats. For C files make knows what to do, but for .dats files it doesn't. In particular, you have to change:
main.o : main.dats
with
main.o : main.dats
(your-compiler) (your-compiler-options) -o main.o main.dats
(assuming that is the syntax in your compiler for specifying input and output files)
IMPORTANT: indentation of the second (and the following) lines have to be done with tabs and not spaces, because that's how make works.
In your case (assuming .dats is the extension for dynamic ATS language) I think it should be
main.o : main.dats
patscc -c -o main.o main.dats
edit: if you have more than one .dats file to compile you can add a generic rule that teach make to invoke the right compiler for them (thanks to Toby for the syntax)
%.o : %.dats
patscc -c -o $# $<
I am not sure what is the priority for application when both a main.c and main.dats are present.

What is the importance of target in makefile?

I am learning how to create makefile on a Linux distro.
I am using the following code (I know it can be written in a small form, but the long form is intentional) to properly understand the behavior of makefile
test: test.o
cc -o test test.o
test.o: test.c
cc -c test.c
clean:
rm test.o
Now, when I use make and make clean in the shell, they are working as intended.
However, I want to know the importance of target in makefile. Hence, started by changing test.o: test.c line to test2.o: test.c and typed make in the shell; my initial guess was that there would be a file in my home directory called test2.o, but that's not the case, I still see test.o being created again.
So, the above behavior begs my question, what is the important of target component in makefile?
The 'target' is the file which Make checks to determine whether it needs to execute the commands associated with the target at all.
I.e. if you change test.o: test.c to test2.o: test.c, Make sees that test2.o does not exist and hence executes the command cc -c test.c -- which still only creates test.o. Hence, if you re-run make, you will see that the compiler is executed again because test.o still does not exist.
In the original version, test.o: test.c, the compiler will only be executed if test.o does not exist, or if the modification time of test.c is newer than that of test.o.
The target becomes available in the commands section as a variable $#, which can be used to define what gets built.
In your makefile you had:
test2.o: test.c
cc -c test.c
Because you didn't tell the compiler what the output would be as part of the cc command, it created test.o from test.c, which is the default behaviour. If you run cc -c file.c it will generate file.o by default.
You need to specify the destination file as part of the commands run for generating the target, so:
test2.o: test.c
cc -c test.c -o $#
Would cause the compiler to generate the test2.o file appropriately.
At a fundamental level, a makefile is nothing more that a set of targets, dependencies for the targets and the sets of commands for making those targets. You have to ensure that as part of the build process, the final product from a set of commands is the target in order to have a properly functioning makefile.
The compiler doesn't know anything about the fact that it's being run in the makefile.
There are a bunch of automatic rules, pre-created by the default make system. These include rules for making .o files from .c files - it knows that it needs to compile a file using the following rule and commands:
%.o: %.c
# commands to execute (built-in):
$(COMPILE.c) $(OUTPUT_OPTION) $<
where COMPILE.c:
COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
and OUTPUT_OPTION is:
OUTPUT_OPTION = -o $#
CC defaults to cc, CFLAGS defaults to empty, CPPFLAGS defaults to empty and TARGET_ARCH defaults to empty. You can see these definitions using make -p
So the resulting command is:
cc -c -o $# $<
Where $# is the name of the target and $< is the first item in the list of dependencies. This pattern matches all target files called <something>.o where there is an existing file called <something>.c. If there's a request to build test.o as a target then it will compile a file called test.c, because that file exists and matches these rules.
tl;dr
Your test2.o rule is never executed. test.o is created by make's implicit rule.
Let's take this apart.
test.o: test.c
cc -c test.c
This is a rule.
The general syntax for a rule is:
targets : prerequisites
recipee
So, test.o is the target, and test.c the prerequisite.
If:
the target (file) does not exist, or
(one of) the prerequisite(s) is newer than the target,
the recipee is executed (which should, but does not have to, create the target).
So, let's look at your Makefile:
test: test.o
cc -o test test.o
test.o: test.c
cc -c test.c
When you say make test, you want to create the target test. This target has test.o as prerequisite.
For test.o exists another rule, with test.c as prerequisite. So that rule gets checked and executed first (compiling your source to object code), before the test prerequisite is checked, and the recipee run if required (linking your object code to executable format).
Hence, started by changing test.o: test.c line to test2.o: test.c and typed make in the shell; my initial guess was that there would be a file in my home directory called test2.o, but that's not the case, I still see test.o being created again.
No target has a test2.o prerequisite, and you did not ask for that to be build specifically (make test2.o), so the recipee for test2.o is never executed.
But test still has test.o as a prerequisite. As there is no explicit rule for a target of that name in your Makefile, make substitutes it with its implicit rule for creating a .o file from an existing .c file...
The default output file from cc -c test.c is test.o. If you want it to create test2.o, you need to tell it explicitly:
test2.o: test.c
cc -o test2.o -c test.c
cc doesn't know anything about the makefile or what target it's being run from.
The importance of targets is that they're used for finding all the dependencies. So the first rule in your makefile says that test is dependent on test.o: before you can create test, you first need to create test.o, and if test.o has changed, you need to rebuild test.
The commands below the target are expected to do whatever it takes to create the target. But you have to code that explicitly (although there are some macros that can automatically substitute targets and dependencies into the command line -- these are mostly useful when the target contains a wildcard pattern).

Resources