Defining path variable for a make file - makefile

In my assignment instructions, I was told to make a
Makefile without absolute directory names or derived binary files. Execution of make with no parameters should build the target program "wordpairs". Assume that the environment variable GET_WORD is defined as the pathname of a directory which contains directories "include" and "lib" containing getWord.h and libget.a
My directory for my code contains: pic
And my Makefile is:
#DIR := ${GET_WORD}
DIR := ${CURDIR}
Main : getWord.o crc64.o sTools.o hashingTools.o Main.c libget.a
gcc -g -o wordpairs Main.c getWord.o crc64.o sTools.o hashingTools.o $(DIR)/lib/libget.a
getWord.o : getWord.c getWord.h
cc -c $(DIR)/include/getWord.c $(DIR)/include/getWord.h
# cc -c getWord.c getWord.h
crc64.o : crc64.c crc64.h
cc -c crc64.c crc64.h
sTools.o : sTools.c sTools.h
cc -c sTools.c sTools.h
hashingTools.o : hashingTools.c hashingTools.h
cc -c hashingTools.c hashingTools.h
clean :
rm $(DIR)/include/*.h.gch
But when I run make, I get
make: *** No rule to make target 'getWord.c', needed by 'getWord.o'. Stop.
The files are in the folders include/lib.
I only understand the basic of make files, so can someone help me out how to achieve what I was assigned to do? What's causing this error?
(updated makefile code)

So apparently I just need to define GET_WORD variable where the grade can change it to a specific location. The thing is that apparently you cant call variables in the requirement file line (see how I didnt specific anything for getWord.o)
GET_WORD = /home/tam#change this!
wordpairs : Main.c crc64.o hashingTools.o sTools.o getWord.o
gcc -o wordpairs $^ -I ${GET_WORD}/include ${GET_WORD}/lib/libget.a
getWord.o :
cc -c ${GET_WORD}/include/getWord.c ${GET_WORD}/include/getWord.h
crc64.o : crc64.c crc64.h
cc -c $^
hashingTools.o : hashingTools.c hashingTools.h
cc -c $^
sTools.o : sTools.c sTools.h
cc -c $^
clean :
rm wordpairs crc64.o hashingTools.o sTools.o *.h.gch
#echo $(GET_WORD) ${GET_WORD}

getWord.o : getWord.c getWord.h looks for these files in the current directory.
You should apparently use GET_WORD to define a couple of additional variables, not redefine it to something else than what's given explicitly in the assignment.
If you figure out how to set INC to the include/ directory, your rule for these files should look something like
getWord.o : $(INC)/getWord.c $(INC)/getWord.h
cc -c $^
Notice how a dependency on a file in another directory must include the directory name; and how make won't actually look at how to make a new thing until the dependency resolution forces it to (and even then it doesn't know that a string in the command it runs is equal to one of the dependencies just because the strings are identical, let alone then when they are different, as in your attempt).
Notice also the use of $^ to say "the things in the dependencies". Generally, you want to avoid repeating information - if you change something, you don't want to change it in many places because it's easy to forget (see also DRY principle.)
... I don't think the professor wants the .c file in the include directory, actually, though; just the .h file would be my guess.
As a further aside, to have just make without arguments create a particular target, put it as the first target in your Makefile.
I hope this is sufficient to help you see how to solve your assigment!

Related

gcc - What does ../ (dot dot slash) mean in a variable in a Makefile?

I have searched for hours for an answer to this. I am new to gcc and Makefiles.
I have a Makefile in some source code that looks like this:
CC=gcc
SRCDIR=src
BINDIR=../bin
CFLAGS= -flag
LIBS= -lthing
...
$(BINDIR)/program_name: $(SRCDIR)/program_name.c
$(CC) $(CFLAGS) $(SRCDIR)/program_name.c -o $(BINDIR)/program_name $(LIBS)
I understand what all of this means except what ../ in BINDIR is meant to do. When I make the Makefile, I get the error message:
/usr/bin/ld: cannot open output file ../bin/program_name: No such file or directory
collect2: error: ld returned 1 exit status
Makefile:20: recipe for target '../bin/program_name' failed
make: *** [../bin/program_name] Error 1
My guess is that the original author of this Makefile meant that the bin folder should go in the parent directory of where the Makefile is located. I know when using the Linux CLI command cd that the dot dot means go up a directory. Is that what this is trying to achieve?
To automatically create the $(BINDIR) directory before it is actually needed you must declare it as a prerequisite (dependence) of any target that uses it. But each time its content changes its timestamp also changes. So, declaring it as a regular prerequisite is not the best thing to do because the targets depending on it would be re-built without real reason, just because the content of $(BINDIR) changed.
This is why make also supports order-only prerequisites (OOPs):
$(BINDIR)/program_name: $(SRCDIR)/program_name.c | $(BINDIR)
$(CC) $(CFLAGS) $< -o $# $(LIBS)
$(BINDIR):
mkdir -p $#
Note the | that introduces the list of OOPs. An OOP is built if it does not exist, which causes the targets depending on it to be (re-)built too. But if it exists make does not even consider its last modification time. Even if some target depending on it is older, it is not rebuilt just because of that.
Note: I also used the $< and $# automatic variables. In the rule's recipe they expand as the first prerequisite ($(SRCDIR)/program_name.c) and the target ($(BINDIR)/program_name), respectively. They are highly recommended: less typing, less errors prone, more generic rules... they have many good properties.
Your makefile is missing a rule to create the BINDIR directory - if it doesn't exist, your link line won't be able to put the resulting binary there! A rule like this one should do it:
$(BINDIR):
mkdir -p $(BINDIR)
Just make sure that any other rules (like the one in your question) also depend on this directory!

makefile: dependency not build

The question was edited after MadScientist's answer. See history for the original makefile, but the problem stays the same.
I have a small makefile:
DEPFLAGS=-MD -Mo $(OUTDIR)/$*.Td
POSTCOMPILE=#mv -f $(OUTDIR)/$*.Td $(OUTDIR)/$*.d && touch $#
VPATH=../src
OUTDIR=../out
SOURCES:=$(notdir $(wildcard ../src/*.c))
OBJECTS:=$(SOURCES:%.c=$(OUTDIR)/%.o)
all: $(OBJECTS) $(OBJECTS:%.o=%.d)
$(OUTDIR)/%.o : %.c
$(OUTDIR)/%.o : %.c $(OUTDIR)/%.d
#$(CC) $(DEPFLAGS) -c $< -o $#
#$(POSTCOMPILE)
$(OUTDIR)/%.d : ;
.PRECIOUS: $(OUTDIR)/%.d
Directory structure looks like:
src
contains file.c
out
empty, after make: contains file.o and file.d
make
contains the makefile
When I call the makefile everything works fine and two files are generated: file.o and file.d
However, when I delete file.d nothing happens. I would expect that make finds a missing dependency for file.c and starts a rebuild. Why doesn't it happen?
Make version is 3.81 built for i386-pc-mingw32 under Windows 7.
Marking a file as .PRECIOUS does not remove all aspects of it's "intermediateness". All it does is prevent it from being deleted, but this feature of intermediate files is still in effect:
If an ordinary file b does not exist, and make considers a target that depends on b, it invariably creates b and then updates the target from b. But if b is an intermediate file, then make can leave well enough alone. It won’t bother updating b, or the ultimate target, unless some prerequisite of b is newer than that target or there is some other reason to update that target.
This is why your .d file is not recreated. In order for it to be recreated you need to ensure it's not an intermediate file. Fortunately this is trivial to do: you just need to mention the files explicitly somewhere as a target or prerequisite. You can do it like this:
all: $(OBJECTS) $(SOURCES:%.c=$(OUTDIR)/%.d)
Or if you prefer like this:
depends: $(SOURCES:%.c=$(OUTDIR)/%.d)
which would allow you to run make depends to update the dependency files, if you wanted to.
I'll just point out in passing that this method of managing dependencies is considered outdated. There's a better, more advanced way it can be done described here among other places.
(I'll be a horrific necromancer here, but I've ran into same problem, and found that actual issue isn't one mentioned in answer or comments here)
Dependency rule generated by compiler by default sports file name with ALL suffixes replaced by single suffix .o and path removed. Which doesn't match the pattern of rule in makefile.
For gcc 4.x and later correct options would be
$(OUTDIR)/%.o : %.c $(OUTDIR)/%.d
#$(CC) -MF $(OUTDIR)/$*.Td -MT $# -c $< -o $#
Mo flag no longer exist, you have to use only MF flag to specify dependency file name.MT flag allows to provide a literal line for target name.

GNU make in newly created subdirectory

First - I know there are a lot of discussions similar to this, but I've spent hours without them working for me.
My makefile first creates a directory named by the current date and time. I then have the makefile append to a header file a line which creates a string with this directory name. For this reason, I first need to copy all the source files (including the header) into the newly created subdirectory, so that I can preserve the original header and only modify the header (in the subdirectory) which will be used for compilation. I would then like to build in that new directory.
My trouble is getting make to properly build the .o files in the new subdirectory. The solution I've found is to have
$(NOW)%.o: $(NOW)%.cpp
$(CC) -c $(FLAGS) $<
where $(NOW)$ is the subdirectory name. The issue is that my $(FLAGS) seem to be ignored: the output is, roughly
g++ -c -o <.o file> <.cpp file>
(Yes, there is actually extra introduced space between g++ and -c.) Whereas building in the top level directory a la
%.o: %.cpp
$(CC) -c $(FLAGS) $<
correctly outputs
g++ -c <my flags> -o <.o file> <.cpp file>
To summarize, I am unable to compile normally by transferring the source files to a newly-created subdirectory and building the .o files in that directory. TYIA.
Ad John points out, there's no way to definitively diagnose your problem with the tiny bit of makefile you provided, because the error is not in the code you provided, it's in some other part of your makefile. You need to provide a SSCCE ideally, but if not that then at least we need to see how the NOW variable is set and the linker rule so we know what make is trying to build.
I should also point out that by convention you should not use CC to hold the C++ compiler; the CC variable holds the C compiler. Use CXX for the C++ compiler and CXXFLAGS for the C++ compiler flags.
One possibility is that you are assigning the NOW variable using a recursive assignment so that the timestamp is recreated every time the variable is evaluated; it could be that the timestamp changes over the lifetime of the makefile.
The other very common problem is that you created the pattern rule, but make is not using it because the targets make wants to build don't match the pattern.
So for example, if your link line looks like this:
SRCS = foo.cpp
OBJS = $(SRC:.cpp=.o)
myprog: $(OBJS)
$(CXX) ...
$(NOW)%.o : $(NOW)%.cpp
$(CXX) ...
then your pattern will not be matched because make is trying to build the file foo.o and your rule tells it how to build $(NOW)foo.o which are not the same thing.

Compile multiple executables from multiple source directories to single bin directory using makefile

I'm trying to create a makefile for a suite of programs that I am working on. The programs are all written in fortran and the source files are contained in different directories. I can't seem how to figure out how to get things to work. My current sumfile is
#Compiler and compiler flag variables
FCOMP=/usr/local/bin/gfortran
F_FLAGS=-O2 -fbounds-check -Wall
F_FLAGSDB=-g -fbounds-check -Wall
#paths to libraries
COMMON_LIB=/usr/local/lib/libspc_common.a
SPICE_LIB=/usr/local/lib/spicelib.a
# Paths to directories
BIN_DIR=BIN
# Get file names of component source files
#get names of files in src1
FORT_FILES=$(wildcard ./SRC1/*.f)
#get names of files in src2
FORTFILES+=$(wildcard ./SRC2/*.f)
#get names of files in src3
FORTFILES+=$(wildcard ./SRC3/*.f)
#get file names for output
EXE_FILES=$(addprefix $(BIN_DIR),$(notdir $(patsubst %.f, % , $(FORTFILES))))
# make commands
# Set the default option to compile the library with optimization
default: all
# create all command
all: $(EXE_FILES)
#echo toolkit has been built with optimization
#If compiling for debugging replace the compiler flags to remove optimization and add debugging
debug: F_FLAGS=$(F_FLAGSDB)
#Run compiler with debugging flags
debug: $(EXE_FILES)
#echo toolkit has been built with debugging
# Compile all of the source files into executables
$(EXE_FILES): % : %.f
$(FCOMP) $(F_FLAGS) $^ $(COMMON_LIB) $(SPICE_LIB) -o $(BIN_DIR)/$#
# install the library in /usr/local/lib
install:
cp -p $(BIN_DIR)* /usr/local/bin/toolkit/
# remove executable files for a clean build
clean:
rm $(BIN_DIR)*
The problem I am running into is that I get the following error when I try to run make:
make: *** No rule to make target `Display.f', needed by `Display'. Stop.
which I am assuming is because I have lost the directory that the source file comes from. Can someone help me here? I am totally stuck and don't know how to proceed.
In addition (this is more a general question about make), is there a way to tell make to recompile everything if the COMMON_LIB changes?
Thanks for your help!
Suppose your source files are
SRC1/alpha.f
SRC1/beta.f
SRC2/gamma.f
SRC3/delta.f
1) There is a flaw here:
EXE_FILES=$(addprefix $(BIN_DIR),$(notdir $(patsubst %.f, % , $(FORTFILES))))
This will produce
BINalpha BINbeta BINgamma BINdelta
when I think you intended
BIN/alpha BIN/beta BIN/gamma BIN/delta
A simple fix:
EXE_FILES=$(addprefix $(BIN_DIR)/,$(notdir $(patsubst %.f, % , $(FORTFILES))))
2) Now look at the static pattern rule:
$(EXE_FILES): % : %.f
...
So to build BIN/alpha, Make must first find BIN/alpha.f, which doesn't exist. To make it look for alpha.f, do this:
$(EXE_FILES): $(BIN_DIR)/% : %.f
...
3) How to find the sources?
You could do some delicate coding to help Make remember where it found alpha.f, but there's no need when we can use the vpath directive:
vpath %.f SRC1 SRC2 SRC3
4) One last look at that rule:
This command:
$(FCOMP) $(F_FLAGS) $^ $(COMMON_LIB) $(SPICE_LIB) -o $(BIN_DIR)/$#
Will produce e.g. BIN/BIN/alpha, which is silly. A non-PHONY Make rule should produce a file whose name is the target of the rule. It prevents a lot of trouble.
$(FCOMP) $(F_FLAGS) $^ $(COMMON_LIB) $(SPICE_LIB) -o $#
A few further refinements may be possible, once you have this working perfectly.

Issue with Makefile in top level directory, sources in subdirectories

I have a directory called project.
It contains two sub-directories called client and server, and a makefile called Makefile.
client and server have got source files called client.c and server.c, respectively.
I don't have any separate makefiles in the subdirectories for sources belonging to that directory. All builds are done by the single makefile. The Makefile code is
FLAGS = -W -Wall -g -pthread
SERV =./server/server.c #My server code
CLI =./client/client.c #My client code
build:svr cnt
svr: $(SERV)
cc $(FLAGS) $(SERV) -o ./server/server.out
cnt: $(CLI)
cc $(FLAGS) $(CLI) -o ./client/client.out
Now I ran make cnt and it replied
cc -W -Wall -g -pthread ./client/client.c -o ./client/client.out
The problem is all the subsequent make cnt commands end up compiling it again and outputting the above text even though I'm not changing ./client/client.c
I'm stuck here. Don't know what to do.
What I want to do is:
With make cnt, compile client/client.c and output its executable in the client/ directory
With make svr, compile server/server.c and output its executable in server/ directory.
And with make, compile bothserver/server.candclient/client.c` and output their executables in their respective directories
But since I don't have any executables called svr and `cnt the problem I am having isn't solved.
If I change the target to ./client/client.out instead of cnt and then call make client/client.out then it would be fine, exactly what I need but I don't want to enter long command make client/client.out in my terminal
The workaround I have got is as follows
cnt: $(CLI)
cc $(FLAGS) $(CLI) -o cnt
cp cnt ./client/client.out
But not quite satisfied with it. I'm sure what I want to do is really simple and there should be some convenient way around doing it. So how can I do that?
Let's formulate what you want. You want target named `cnt' not to be rebuilt. The makefile you've written knows nothing about client.out file, because it only appears in shell commands within a rule. Make program doesn't read information from shell commands, it only does substitutions there and executes them.
When makefile chooses the targets it will rebuild (`cnt' is one of these targets), it compares update time of a target file with update time of its prerequsites. Since at the time you run ``make cnt'' the file named cnt is absent, the target named so is considered as requiring update. So commands are run and yield no file named cnt, so next make run will consider it for updating as well.
There are two possible solutions. The first one is to give targets same names as of the file, that the rule commands will generate. So, you might end up like this:
client/client.out: $(CLI)
cc $(FLAGS) $(CLI) -o ./client/client.out
Your questioin has nothing to do with directories, by the way. You should also read about .PHONY directive, use $(CC) instead of cc and read gnu make manual, which might be very helpful.
Try this:
SERVO =./server/server.out
CLIO =./client/client.out
.PHONY: srv cnt build
build: svr cnt
svr: $(SERVO)
cnt: $(CLIO)
$(SERVO) $(CLIO): %.out : %.c
cc $(FLAGS) $^ -o $#
Now you can make srv, make cnt, or just make.
There are slightly more sophisticated things you can do, but this should be enough for now.

Resources