Reputation: 65
FINAL UPDATE:
Problem solved thanks to @Useless!
Changed $(EXET): $(OBJS)
to $(EXET): $(OSRC)
.
UPDATE: Sept 29, 2020
Following @MadScientist's suggestions, I edited and removed the unnecessary parts from the Makefile. However, the line OSRC := $(addprefix $(ODIR)/, $(OBJS))
doesn't seem to have any effect because the *.o
object files are now compiled into the workspace folder, not the dedicated ./obj
folder, which is my origin problem.
#############################################################
## THIS IS A TEST PROJECT ##
#############################################################
vpath %.cpp src
vpath %.h hdr
#############################################################
## BUILD TASKS ##
#############################################################
HDIR := hdr
#SRCS := src
ODIR := obj
EXET := a
OBJS := main.o mainhdr.o testcode.o
OSRC := $(addprefix $(ODIR)/, $(OBJS))
CXX := g++
CXXFLAGS := -I$(HDIR) -g -Wall -std=c++17
## BUILD DIRECTIVE:
all: $(EXET)
$(EXET): $(OBJS)
$(CXX) $(CXXFLAGS) $^ -o $@
$(ODIR)/%.o: %.cpp
$(CXX) -c $(CXXFLAGS) $^ -o $@
#############################################################
## CLEAN TASK ##
#############################################################
.PHONY: clean
clean:
rm -r $(EXET) *.o
ORIGINAL QUESTION:
I've been pulling my hair out trying to no avail to get Make to link prebuilt objects from a specific directory. I got my Makefile to compile the object files into a different directory, but I'm basically stuck after that.
#############################################################
## THIS IS A TEST PROJECT ##
#############################################################
.SUFFIXES:
.SUFFIXES: .o .h .cpp
vpath %.cpp src
vpath %.h hdr
vpath %.o out
%.h : %.cpp
#############################################################
## BUILD TASKS ##
#############################################################
HDIR := hdr
#SRCS := src #UNUSED
ODIR := obj
EXE := a
OBJS := main.o mainhdr.o testcode.o
## This line added in reference to this thread:
## https://stackoverflow.com/questions/9178285/how-can-makefile-use-separate-directories-for-source-code-and-binaries
## But it doesn't seem to work either
OSRC := $(addprefix $(ODIR)/, $(OBJS))
CPP := g++
CPPFLAGS := -I$(HDIR) -c -g -Wall -std=c++17
## BUILD DIRECTIVE:
all: $(EXE)
## PROBLEM: Doesn't seem to work when prefixed with the output dir:
#$(EXE): $(ODIR)/$(OBJS)
$(EXE): $(OBJS)
$(CPP) -o %@ %^
## This compiles the objects into the output dir just fine.
%.o: %.cpp
# $(CPP) $(CPPFLAGS) $< -o $@
$(CPP) $(CPPFLAGS) $< -o $(ODIR)/$@
#############################################################
## CLEAN TASK ##
#############################################################
.PHONY: clean
clean:
rm -r $(EXE) $(ODIR)/*.o
## End of Makefile ##########################################
Debugging effort so far:
I added the .o
filetype to .SUFFIXES:
but I doubt that it does anything. Make only seems to care about the headers and source files.
I did some more research and I added this part later:
## This line added in reference to this thread:
## https://stackoverflow.com/questions/9178285/how-can-makefile-use-separate-directories-for-source-code-and-binaries
## But it doesn't seem to work either
OSRC := $(addprefix $(ODIR)/, $(OBJS))
However, it doesn't seem to have any effect at all. Make just couldn't find the object directory either.
$(CPP) $(CPPFLAGS) $< -o $(ODIR)/$@
Shouldn't it be fixed somehow to be consistent with the $(addprefix)
directive?
Quick references from GNU Make in case I'm missing something:
$@ target
%< first prerequisite
$^ prerequisites
The following is simple test code:
// cat hdr/mainhdr.h
#ifndef MAINHDR_H
#define MAINHDR_H
#include "testcode.h"
#define BIGCONST 777
void testFunc();
#endif
// cat hdr/testcode.h
#ifndef TESTCODE_H
#define TESTCODE_H
#include <iostream>
#include <string>
#define SMALLCONST 25
class NameTag {
private:
std::string f;
std::string l;
public:
NameTag(std::string first="", std::string last="");
void setFirstN(std::string );
void setLastN(std::string );
std::string getFirstN() const;
std::string getLastN() const;
void operator=(const NameTag &);
};
#endif
// cat src/main.cpp
#include "mainhdr.h"
int main () {
testFunc();
return 0;
}
// cat src/mainhdr.cpp
#include "mainhdr.h"
void testFunc() {
std::cout << "THIS IS A TEST STRING!\n";
std::cout << "BIGCONST = " << BIGCONST << std::endl;
std::cout << "SMALLCONST = " << SMALLCONST << std::endl;
NameTag someDude("Good", "Dude");
std::cout << someDude.getFirstN() << " " << someDude.getLastN() << std::endl;
}
// cat src/testcode.cpp
#include "testcode.h"
NameTag::NameTag(std::string first, std::string last) {
f = first;
l = last;
}
void NameTag::setFirstN(std::string first) {
f = first;
}
void NameTag::setLastN(std::string last) {
l = last;
}
std::string NameTag::getFirstN() const {
return f;
}
std::string NameTag::getLastN() const {
return l;
}
void NameTag::operator=(const NameTag &right) {
f = right.f;
l = right.l;
}
Upvotes: 0
Views: 86
Reputation: 100781
There are a number of issues here, some real problems and some just not useful things.
.SUFFIXES:
.SUFFIXES: .o .h .cpp
These lines are useless because you're using pattern rules. .SUFFIXES
is only used if you're writing suffix rules.
vpath %.o out
This is not useful. You should not ever use vpath to find targets. You can only use it to find source files. Also, you never seem to use out
anywhere else in the makefile.
%.h : %.cpp
I'm not sure what this is trying to do, but it's actually not doing anything.
OBJS := main.o mainhdr.o testcode.o
OSRC := $(addprefix $(ODIR)/, $(OBJS))
This is good, but it's useless because you never actually use the variable OSRC
anywhere else in your makefile.
CPP := g++
CPPFLAGS := -I$(HDIR) -c -g -Wall -std=c++17
FYI, common practice in makefiles is to use CXX
for the C++ compiler, not CPP
. CPP
(in common makefile parlance) refers to the C preprocessor. Of course this is just a suggestion.
## PROBLEM: Doesn't seem to work when prefixed with the output dir:
#$(EXE): $(ODIR)/$(OBJS)
Sure this won't work. So $(ODIR)
is obj
and $(OBJS)
is main.o mainhdr.o testcode.o
. That means that this:
$(ODIR)/$(OBJS)
obviously expands to this:
obj/main.o mainhdr.o testcode.o
which is clearly not what you want. The above version, using addprefix
to prepend the directory to each word in the variable not just putting it at the start of the entire variable expansion, is what you want.
$(EXE): $(OBJS)
$(CPP) -o %@ %^
You set OSRC
to the right thing, but then you still use $(OBJS)
here so it's not helping you.
Also, obviously you mean $@
and $^
here not %@
and %^
.
## This compiles the objects into the output dir just fine.
%.o: %.cpp
$(CPP) $(CPPFLAGS) $< -o $(ODIR)/$@
No, it doesn't. I mean yes, it does compile objects into the output dir, but not "just fine". Any time you have a recipe that builds some other file which is not exactly $@
, that recipe is wrong. Here your recipe builds $(ODIR)/$@
, not $@
, so it's definitely wrong.
You want to write it like this:
$(ODIR)/%.o: %.cpp
$(CPP) $(CPPFLAGS) $< -o $@
Also just a note, IMO it's poor practice to put an output control flag like -c
into the CPPFLAGS
(or, using the standard names, CXXFLAGS
). You should put flags like that into the recipe directly, not in the variable.
Usually it's wise to include the $(CXXFLAGS)
variable in your link line, as well as in your compile line, so any compiler flags which are relevant to the linker are seen. That can't work if the variable contains -c
as well.
Upvotes: 1