I was given a makefile that looks like this, and told not to change it.
all: clean flex scanner.lex bison -d parser.ypp g++ -std=c++11 -o hw2 *.c *.cpp clean: rm -f lex.yy.c rm -f parser.tab.*pp rm -f hw2
I am trying to run this makefile in a folder with files named: scanner.lex, parser.ypp, output.hpp and output.cpp
I copied it to a file like this:
all:
clean flex scanner.lex bison -d parser.ypp g++ -std=c++11 -o hw2 *.c *.cpp
clean:
rm -f lex.yy.c rm -f parser.tab.*pp rm -f hw2
When I run the make command in my terminal I get an error:
clean flex scanner.lex bison -d parser.ypp g++ -std=c++11 -o hw2 *.c *.cpp
/bin/sh: clean: command not found
make: *** [all] Error 127
Am I doing something wrong? Again, I was given this line and told not to change it.
Thanks a lot.
Line breaks are essential in most computer environments. If you were given a Makefile without the line breaks and you try to cut it randomly you will have difficulties before if finally works. Try this, maybe:
all: clean
flex scanner.lex
bison -d parser.ypp
g++ -std=c++11 -o hw2 *.c *.cpp
clean:
rm -f lex.yy.c
rm -f parser.tab.*pp
rm -f hw2
And use tabs to indent the indented lines, not spaces.
Explanations: all and clean are what is called a target in make parlance. They are the names of the things you want make to do. clean to delete some files, all to do everything else. The
target: prerequisite1 prerequisite2...
recipe1
recipe2
...
template is the basic make template. It means that target depends on prerequisite1, prerequisite2 and that in order to build it make shall pass recipe1 to the shell for execution, then recipe2...
Note that this Makefile is poorly written. As all and clean are not real file names they should be declared as phony, such that, if a file with that name exists make does the job anyway. As is, it wouldn't. Give it a try:
$ make all
$ touch clean
$ make clean
make: 'clean' is up to date.
See? Because a file named clean exists you cannot make clean anymore, make considers that there is nothing to do for clean. Add this at the beginning of your Makefile:
.PHONY: all clean
A second issue is that make works by comparing last modification times of targets and prerequisites to decide if targets must be rebuilt or not. With your Makefile make will always recompile everything, even if the inputs did not change and the outputs are up-to-date. This is a waste. A better (but untested) Makefile would be something like:
.PHONY: all clean
CFILES := $(filter-out lex.yy.c,$(wildcard *.c))
CPPFILES := $(filter-out parser.tab.cpp,$(wildcard *.cpp))
all: hw2
hw2: lex.yy.c parser.tab.cpp $(CFILES) $(CPPFILES)
g++ -std=c++11 -o $# $^
lex.yy.c: scanner.lex
flex $<
parser.tab.cpp: parser.ypp
bison -d $<
clean:
rm -f lex.yy.c
rm -f parser.tab.*pp
rm -f hw2
Understanding it and why it is better is left as an exercise.
Related
I just started learning make today. I have several assembly files that I want to compile and then join into a single file. For now I have two files in my tree but the makefile code should be able to handle more. So here is what the files look like.
Src/Boot/MBR.asm
Src/Boot/SecondStage/Bootloader.asm
I want to compile each of these files into the Bin/ directory that the makefile is located in, where the files should end up like this
Bin/MBR.bin
Bin/Bootloader.bin
then I will concentrate these two files into one single file os-image.img
So far I came up with the following
AS := nasm
ASFLAGS_BIN := -fbin
SRCDIR := Src
BINDIR := Bin
BOOTASM = $(shell find $(SRCDIR) -name '*.asm')
BOOTBIN = $(addprefix $(BINDIR)/, $(addsuffix .bin, $(basename $(notdir $(BOOTASM)))))
build: clean compile
cat $(BOOTBIN) > Bin/os-image.img
compile: $(BOOTBIN)
$(BOOTBIN) : $(BOOTASM)
$(AS) $(ASFLAGS_BIN) $< -o $#
clean:
rm -rf $(BINDIR)/%.bin
The output when I type make to shell is the following
rm -rf Bin/%.bin
nasm -fbin Src/Boot/second/Bootloader.asm -o Bin/Bootloader.bin
nasm -fbin Src/Boot/second/Bootloader.asm -o Bin/MBR.bin
cat Bin/Bootloader.bin Bin/MBR.bin > Bin/os-image.img
The expected output is:
rm -rf Bin/%.bin
nasm -fbin Src/Boot/second/Bootloader.asm -o Bin/Bootloader.bin
nasm -fbin Src/Boot/second/MBR.asm -o Bin/MBR.bin
cat Bin/Bootloader.bin Bin/MBR.bin > Bin/os-image.img
Obviously the problem is in here
$(BOOTBIN) : $(BOOTASM)
$(AS) $(ASFLAGS_BIN) $< -o $#
However I couldn't understand how I should be able to achieve what I want since I am pretty inexperienced at this.
So the question is:
How should I get each prerequisite that corresponds to the related target? or something similar to that.
Thanks in advance.
You can do this with VPATH --
BOOTASM = $(shell find $(SRCDIR) -name '*.asm')
BOOTBIN = $(addprefix $(BINDIR)/, $(addsuffix .bin, $(basename $(notdir $(BOOTASM)))))
VPATH=$(sort $(dir $(BOOTASM))
$(BOOTBIN) : %.bin : %.asm
$(AS) $(ASFLAGS_BIN) $< -o $#
But, read the third rule of makefiles before you got to far down the vpath road...
Another thing you should be aware of -- for
build: clean compile
cat $(BOOTBIN) > Bin/os-image.img
Then clean is not guaranteed to run before compile (and in fact, on a parallel system they might both try to run at the same time...). Obviously this would not be what you want. Either make compile depend on clean (but then it will clean every time you try to compile), or create a seperate clean_then_compile : clean target, which runs the compile command on its own.
I am still figuring out why in the below Makefile, when I execute it in the command like make clean all it runs twice the "clean all" target part?
FLAGS = -g -Wall -Wextra -Werror
a.out : check.cpp
$(CXX) $(CFLAGS) -o $# $<
clean all:
rm -rf *.o *.out *.txt
The target "clean all:" is equivalent to:
clean:
rm -rf *.o *.out *.txt
all:
rm -rf *.o *.out *.txt
You can't have spaces in target names, each word would be considered a target. So when you do make clean all, make thinks you want to build target "clean" and target "all" and so you have 2 targets being executed. It does look like a mistake in the makefile since having all doing the same as clean is weird.
Consider the following makefile:
TARGET=fmake
TARGET2=test_second
fmake: $(TARGET2).c foo.c\
$(TARGET).c test.h clean
$(CC) -o $(TARGET) $(TARGET).c foo.c
$(CC) -o $(TARGET2) $(TARGET2).c
foo.c:
echo Some text
clean:
rm -f fmake test_second
CC=$(VAR2)
VAR2=gcc
After the make bash command the following display
rm -f fmake test_second
gcc -o fmake fmake.c foo.c
gcc -o test_second test_second.c
As said here foo.c doesn't processed because there is no dependencies for this target. But both foo.c and clean have no dependencies, but clean is processed. Why?
Because a file called foo.c exists, whereas no file called clean exists. So Make thinks that one needs to be made. Note that clean should really be declared as a phony target.
I have the following simple makefile
#all: binsem.a ut.a ph
FLAGS = -Wall -L./
binsem.a:
gcc $(FLAGS) -c binsem.c
ar rcu libbinsem.a binsem.o
ranlib libbinsem.a
ut.a:
gcc $(FLAGS) -c ut.c
ar rcu libut.a ut.o
ranlib libut.a
clean:
rm -f *.o
rm -f a.out
rm -f *~
rm -f ph
rm -f *a
The problem is it only generates binsem.a and not ut.a, probably because of dependencies issues.
I tried looking at the flags but did not find the answer.
Thanks a lot.
By default, if you don't specify a target on the command line, make will build the first target it finds (and it's dependencies if it has any). Your first target is binsem.a, and you don't list any dependencies, so that's the only thing that gets built.
Try something like adding this at the top:
all: binsem.a ut.a
And mention the dependencies in your other targets:
binsem.a: binsem.c
...
ut.a: ut.c
Is it ever a bad idea to include a Makefile as a dependency for a make target?
Eg.
hello.o: hello.cxx Makefile
$(CXX) -c $(CFLAGS) $< -o $#
That way anytime the Makefile is modified the target is recompiled.
No its not a bad idea. Conventionally we never do that but if you have makefile calling other makefile then including it would be a great idea though.
I believe what you're trying to do is run clean (or other equivalent target) whenever the Makefile gets modified.
This can be achieved so. (I've been using this recipe in couple of my C/C++ projects).
CLEANUP_TRIGGER := .makefile
BASE_MAKEFILE := $(firstword $(MAKEFILE_LIST))
FINAL_TARGET := hello.o
all: $(CLEANUP_TRIGGER) $(FINAL_TARGET)
hello.o : hello.c
$(CXX) -c $(CFLAGS) $< -o $#
$(CLEANUP_TRIGGER): $(BASE_MAKEFILE)
if [ -f $(CLEANUP_TRIGGER) ]; then $(MAKE) clean; fi
touch $#
clean:
rm -rf *.o
rm -f $(CLEANUP_TRIGGER)
.PHONY: all clean
The essence is to make sure CLEANUP_TRIGGER is part of the rules which get invoked commonly, run make clean whenever Makefile is newer than CLEANUP_TRIGGER.