1

I have the following project directory structure:

drwxr-xr-x+ 1 account Domain Users     0 Aug 20 16:16 ./
drwxr-xr-x+ 1 account Domain Users     0 Aug 20 08:48 ../
drwxr-xr-x+ 1 account Domain Users     0 Aug 20 10:55 include/
-rw-r--r--  1 account Domain Users   597 Aug 20 16:16 Makefile
drwxr-xr-x+ 1 account Domain Users     0 Aug 20 10:55 obj/
drwxr-xr-x+ 1 account Domain Users     0 Aug 20 10:55 src/

in the src folder: main.cpp foo.cpp bar.cpp

in the include folder: foo.h bar.h

When I run the following Makefile, the object files are created in the project directory, not the obj directory as I might expect from my Makefile:

1 ODIR = obj
2 SDIR = src
3 IDIR = include
4
5 _OBJS = main.o foo.o bar.o
6 OBJS = $(patsubst %,$(ODIR)/%,$(_OBJS))
7
8 CC = g++
9
10 CFLAGS = -w
11
12 PROG = program
13
14 #VPATH = src:include:obj:../src:../include:../obj
15
16 all: $(OBJS)
17     $(CC) $(CFLAGS) -I$(IDIR) $(OBJS) -o $(PROG)
18
19 $(ODIR)/main.o: $(SDIR)/main.cpp $(IDIR)/foo.h $(IDIR)/bar.h
20     $(CC) $(CFLAGS) -I$(IDIR) -c $(SDIR)/main.cpp
21
22 $(ODIR)/foo.o: $(SDIR)/foo.cpp $(IDIR)/foo.h
23     $(CC) $(CFLAGS) -I$(IDIR) -c $(SDIR)/foo.cpp
24
25 $(ODIR)/bar.o: $(SDIR)/bar.cpp $(IDIR)/bar.h
26     $(CC) $(CFLAGS) -I$(IDIR) -c $(SDIR)/bar.cpp
27
28 clean:
29     rm -f $(PROG) $(OBJS)

Can anybody clue me in on why this is happening? My linker doesn't see the object files because it thinks the files are in the obj directory.

Etan Reisner
  • 77,877
  • 8
  • 106
  • 148
brandonto
  • 125
  • 2
  • 10
  • 6
    `-o $@` would seem to be missing from your flags for each .o rule. As in `$(CC) $(CFLAGS) -I$(IDIR) -c -o $@ $(SDIR)/main.cpp`. See [Automatic variables in Gnu make](https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html) for other handy vars. – WhozCraig Aug 20 '14 at 20:55
  • Try [remake](http://bashdb.sourceforge.net/remake/) `-x` – Basile Starynkevitch Aug 20 '14 at 22:02
  • 2
    After you follow @WhozCraig's advice (which should be worked into an answer), you can collapse the three object rules (`$(ODIR)/main.o`, `$(ODIR)/foo.o` and `$(ODIR)/bar.o`) into one rule. – Beta Aug 20 '14 at 22:28
  • @WhozCraig Thanks! This worked! Can you make this into an answer so I can accept it? And elaborate for other newbies (%@ contains the whole path+file, whereas leaving it out means just the file) – brandonto Aug 20 '14 at 22:33
  • @Beta How would I collapse them into one rule? They have different dependencies, main depends on [foo.h, bar.h], foo depends on [foo.h], bar depends on [bar.h]. (As you can see, I'm still very new to self-compiling without using an IDE, stuff like this confuse me) – brandonto Aug 20 '14 at 22:36
  • Don't feel bad about being confused-- Make is a tricky tool with a hard learning curve. – Beta Aug 21 '14 at 01:19

2 Answers2

3

To elaborate a little on WhozCraig's answer:

Start with this rule:

$(ODIR)/main.o: $(SDIR)/main.cpp $(IDIR)/foo.h $(IDIR)/bar.h
    $(CC) $(CFLAGS) -I$(IDIR) -c $(SDIR)/main.cpp

The command ($(CC) ...) doesn't specify an output file, so he compiler will put the resultant object file (main.o) in the working directory, which is the directory in which you invoked Make, which is the project directory. Note that Make and the compiler don't talk to each other very much. The compiler knows what source files it should scan, but nobody told it anything about obj/, so it infers the destination file name (main.o) from the name of the source file; Make knows that the target should be obj/main.o, but it doesn't know what the compiler is actually doing. (In fact, it doesn't know that the command it's executing invokes a compiler.)

We can fix this problem by adding a term to the command, specifying the destination file name:

$(ODIR)/main.o: $(SDIR)/main.cpp $(IDIR)/foo.h $(IDIR)/bar.h
    $(CC) $(CFLAGS) -I$(IDIR) -c $(SDIR)/main.cpp -o $(ODIR)/main.o

Now the compiler will put main.o in the right place-- and Make doesn't notice any difference, it's still just passing a command to the shell.

We notice a lot of redundancy in this rule (e.g. $(ODIR)/main.o spelled out twice), so we reduce it by using automatic variables:

$(ODIR)/main.o: $(SDIR)/main.cpp $(IDIR)/foo.h $(IDIR)/bar.h
    $(CC) $(CFLAGS) -I$(IDIR) -c $< -o $@

Where $< expands to the first thing in the prerequisite list (namely $(SDIR)/main.cpp), and $@ expands to the target name (namely $(ODIR)/main.o). And for reasons that will become clear in a minute, we split the header dependencies into a separate rule for the same target:

$(ODIR)/main.o: $(IDIR)/foo.h $(IDIR)/bar.h

$(ODIR)/main.o: $(SDIR)/main.cpp $(IDIR)/foo.h $(IDIR)/bar.h
    $(CC) $(CFLAGS) -I$(IDIR) -c $< -o $@

Yes, we can do that.

Now we look at the object rules:

$(ODIR)/main.o: $(IDIR)/foo.h $(IDIR)/bar.h
$(ODIR)/foo.o: $(IDIR)/foo.h
$(ODIR)/bar.o: $(IDIR)/bar.h

$(ODIR)/main.o: $(SDIR)/main.cpp
    $(CC) $(CFLAGS) -I$(IDIR) -c $< -o $@

$(ODIR)/foo.o: $(SDIR)/foo.cpp
    $(CC) $(CFLAGS) -I$(IDIR) -c $< -o $@

$(ODIR)/bar.o: $(SDIR)/bar.cpp
    $(CC) $(CFLAGS) -I$(IDIR) -c $< -o $@

Again we see redundancy-- the last three rules are almost identical! We can make them into a single pattern rule:

$(ODIR)/main.o: $(IDIR)/foo.h $(IDIR)/bar.h
$(ODIR)/foo.o: $(IDIR)/foo.h
$(ODIR)/bar.o: $(IDIR)/bar.h

$(ODIR)/%.o: $(SDIR)/%.cpp
    $(CC) $(CFLAGS) -I$(IDIR) -c $< -o $@

We could go even further, but that's enough for one day.

Beta
  • 96,650
  • 16
  • 149
  • 150
  • +1 totally worth the up-vote for the extra mile of work. I haven't got the initiative to roll the full dependency-generation gauntlet at this, but this gets one step closer. nice job. – WhozCraig Aug 21 '14 at 04:24
  • @WhozCraig: Thank you. The full dependency-generation gauntlet is a thing of beauty, but the OP isn't ready; first exposure to it can give even an experienced user whiplash. – Beta Aug 21 '14 at 04:37
2

The target specifier for your compilation commands is missing. ex: When executing this:

 $(CC) $(CFLAGS) -I$(IDIR) -c $(SDIR)/main.cpp

there is no target so the default is the current working directory (in your case, the root folder where you Makefile resides.

Gnu make provides a plethora of automatic variables for substitution in recipes, each to provide some particle of the recipe for your use in forming your commands. See here for the full gambit, but I believe you'll want something like this:

 $(CC) $(CFLAGS) -I$(IDIR) -c -o $@ $(SDIR)/main.cpp

From the Gnu documentation :

$@

The file name of the target of the rule. If the target is an archive member, then $@ is the name of the archive file. In a pattern rule that has multiple targets (see Introduction to Pattern Rules), $@ is the name of whichever target caused the rule's recipe to be run.

Or something similar to that.

On a separate note, there are a multitude of ways for building automatic dependencies for your projects. One common method (though a wee bit hard to follow) is presented in this answer on StackOverflow. The question itself is based on this paper discussing automatic dependency generation techniques.

It fundamentally comes down to generating a .d file for each source file, and within the .d file is a list of header files generated via the -MMD command switch option. Describing i in detail is beyond the scope of this question/answer, but definitely take a look and do a little googling on the subject. Between gnu-make and the dependency generator for cc, you can build some remarkably simply Makefile's that do an incredible amount of work for you auto-magically.

Best of luck.

Community
  • 1
  • 1
WhozCraig
  • 65,258
  • 11
  • 75
  • 141