0

I am trying to create a Makefile in order to generate object files in a subdirectory rather than let them in the src/ folder. Here is the structure of the project:

Trunk
- Server
  - src/
  - include/
- Common
  - src/
  - include/

The Makefile is located in Trunk/Server. Source files are located both in Server/src and Common/src, so the Makefile currently has something like this:

SRC        =        src/main.cpp                  \
                    src/Network.cpp               \
                    ../Common/src/SQLManager.cpp  \
                    ../Common/src/Utils.cpp

I woud like to put generated object files in respective obj folders, so Trunk/Server/obj and Trunk/Common/obj. How can I achieve this? I've found many ways to generate object files in subdirectories (vpath, patsubst and many more) but I can't make any of them work for this folder organization.

Edit: If there is a way to put all object files in Server/obj/, that would be ok too.

Here's the complete Makefile (minus some source files and linked libraries): Edit2: Updated with Didier Trosset's changes

CXX             =       g++

RM              =       rm -vf

NAME            =       Server

SRC             =       src/main.cpp                  \
                        src/Network.cpp               \
                        ../Common/src/SQLManager.cpp  \
                        ../Common/src/Utils.cpp

OBJ             =       $(subst src/,obj/, $(subst .cpp,.o, $(SRC)))

LDFLAGS         =       -lpthread -lm

CPPFLAGS        =       -std=c++0x -pedantic -Wextra -Wall -Wconversion -Iinclude -I../Common/include

all: Server

%.o: %.cpp
        $(CXX) $< -o $@

Server: $(OBJ)
        $(CXX) -o $(NAME) $(OBJ) $(LDFLAGS)

clean:
        $(RM) $(OBJ) *~

fclean: clean
        $(RM) $(NAME)

re: fclean Server

.PHONY: all clean fclean Server
Jukurrpa
  • 4,038
  • 7
  • 43
  • 73
  • A question like this gets asked here almost every other week. Did you try a search and if so can you explain why/how the answers to those questions don't work for you? – eriktous Feb 13 '12 at 14:13
  • I did try to search, and read answers such as these: http://stackoverflow.com/questions/5178125/gnu-make-how-to-get-object-files-in-separate-subdirectory , http://stackoverflow.com/questions/231229/how-to-generate-a-makefile-with-source-in-sub-directories-using-just-one-makefil . But the folder structure isn't exactly the same and the Makefile usually doesn't work at all (can't find `.cpp`s, wrong command line...) – Jukurrpa Feb 13 '12 at 14:21
  • Another question: why do you think this question comes up so often, and why do you think the Makefile usually doesn't work at all? This scheme simply has inherent problems; pattern rules break down because of the need to prefix everything with the correct directory. To maintain your Makefile's and your own sanity, just don't do this. Stick to [Paul's third rule of Makefiles](http://mad-scientist.net/make/rules.html), or migrate to a more advanced build system. – eriktous Feb 13 '12 at 15:11
  • My answer to that question: the insanity is in `make`'s peculiar handling of directories and directory separators. It looks like it was designed to support rules operating on files all in the same directory with cross-directory support sort of hacked in afterwards. – reinierpost Feb 13 '12 at 15:48

3 Answers3

3

Given these definitions, subst should be used to substitute the src part of the directory, and the file extension.

OBJ = $(subst src/,bin/,$(subst .cpp,.o,$(SRC)))

Then, you have to add new pattern rules to compile your source files. (You have to write one pattern rule per directory where your source files are.)

obj/%.o: src/%.cpp
         $(CXX) -c $< -o $@

../Common/obj/%.o: ../Common/src/%.cpp
         $(CXX) -c $< -o $@
Didier Trosset
  • 36,376
  • 13
  • 83
  • 122
  • I tried that (after removing the extra comma after `subst`), and it seems to make GCC look for the `.cpp` files in `obj/` instead of `src/`... I got a `no rule to make target` error. – Jukurrpa Feb 13 '12 at 13:03
  • @Jukurrpa Thanks for the comma. Also, what is the rest of your Makefile. What you posted here is not enough! You need other targets, at least to link your objects. – Didier Trosset Feb 13 '12 at 16:17
  • Yes, I didn't specify the 'usual' rules such as the one calling `$(OBJ)` and then the linking of all objects. I can add them to my original post if that can help you finding a solution :) – Jukurrpa Feb 14 '12 at 12:46
  • @Jukurrpa Please post more of your Makefile. The error does not seem related to the `SRC` or `OBJ` now. – Didier Trosset Feb 14 '12 at 13:13
  • I added my 'original' Makefile. I replaced my `OBJ` with yours and added the `%.o` rule and immediately got `make: *** No rule to make target 'obj/main.cpp', needed by 'server'.` – Jukurrpa Feb 15 '12 at 13:37
  • First, you have to leave the `all` rule as the first rule. Second, you have to replace the `server` rule by the linking rule to create your target (the binary executable file). – Didier Trosset Feb 15 '12 at 14:02
  • Well, the `server` rule is the linking rule... Unless you mean removing `$(OBJ)` on its first line? – Jukurrpa Feb 15 '12 at 14:16
  • Then there's a mismatch between the `server` rule and the `Server` target name. You should use the same. Otherwise, as the rule creates `Server` and the Makefile expects `server` the link will always be done. – Didier Trosset Feb 15 '12 at 17:20
  • Also, I fixed my answer. The `OBJ` variable should also substitute the `.cpp` to `.o`. This will fix the error. – Didier Trosset Feb 15 '12 at 17:21
  • I added the `subst` (btw it seems that one shouldn't put a space after the first comma as make also adds it in the replacement), I now get a `no rule` error for `obj/main.o`. Getting closer? – Jukurrpa Feb 15 '12 at 17:50
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/7756/discussion-between-jukurrpa-and-didier-trosset) – Jukurrpa Feb 15 '12 at 17:55
0

I'm not sure that this will work, but I would try something like:

OBJ := $(join $(addsuffix ../obj/,$(dir $(SRC))),$(notdir $(SRC))
# or, as Didier suggested:
# OBJ := $(subst src/,obj/,$(SRC))

%.o: ../src/%.cpp
    $(CXX) $< -o $@
Eldar Abusalimov
  • 24,387
  • 4
  • 67
  • 71
  • I get the same error as with Didier Trosset's method. It makes GCC look for `.cpp` files in `obj/` folder. – Jukurrpa Feb 13 '12 at 13:26
0

It's not clear exactly what you're trying to do, but this should come close:

OBJ = $(subst src/,obj/,$(SRC:.cpp=.o))

obj/%.o : src/%.cpp
    $(CXX) $< -o $@

../Common/obj/%.o : ../Common/src/%.cpp
    $(CXX) $< -o $@
Beta
  • 96,650
  • 16
  • 149
  • 150
  • 1
    I guess, you meant `%.cpp` instead of `*.cpp`, didn't you? – Eldar Abusalimov Feb 14 '12 at 19:39
  • This works for the files located in `src/`, but not for the ones in `../Common/src`. I tried to change the path of source files and rule to `./src/` so there is no ambiguity between the two, but it didn't work. – Jukurrpa Feb 15 '12 at 13:39