tags:

views:

101

answers:

4

I have the following Makefile rules:

DIR = src
SOURCES = $(shell find $(DIR) -name "*.cpp")
OBJS := $(SOURCES:.cpp=.o)

With this definition all my .o files are located in the same directories (and sub-directories) as their .cpp counterparts. Such allocation principle turns the project directory into a mess very soon (with 50+ files). I would like to make a new specific folder for .o files and place them there. I need to write a make-rule that will convert every .o file name like the following:

foo/bar/test.o —> objects/foo-bar-test.o

How can I create such a rule in Makefile? Thanks in advance!

+1  A: 

You can try to start with something like this:

DIR = src
SOURCES = $(shell find $(DIR) -name "*.cpp")
OBJS := $(SOURCES:%.cpp=obj/%.o)

obj/%.o: %.cpp
    $(CC) -c -o $@ $^

If you don't want to mess with default build rules (taking Michael Aaron Safyan's advice), you could try to go along the lines of:

DIR = src
SOURCES = $(shell find $(DIR) -name "*.cpp")
ODIR=obj
OBJS:= $(SOURCES:%.cpp=obj/%.o)
CP=cp
MKDIR=mkdir -p

$(ODIR)/%.cpp : %.cpp
    $(MKDIR) $(DIR $@)
    $(CP) $< $@

Which will copy the cpp file to the obj directory before it is compiled there (note that this may cause some problems if you're relying on include files to reside in the same directory of the source file)

adamk
This won't change output .o names according to source names in subdirectories, causing two files with the same name in different subdirectories to compile into the same .o
Diego Sevilla
The key disadvantage here is that we should create directories inside `/obj`. In my original question I was interested how can I convert `foo/bar/test.o` to `foo-bar-test.o`. Can you help?
Vincenzo
No filename clashes would happen here, as under obj there would be an exact directory structure as under the source directory.
adamk
+3  A: 

I use a rule like this:

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

where

ODIR = object file output directory
SDIR = .cpp source file directory
INC = list of -I flags
CFLAGS = the usual
anon
@Neil, in this case we create a structure of files similar to the original structure, but in another directory. I am interested to change file names on-fly, as explained above.
Vincenzo
+1  A: 

It is generally not a good idea to override the default build rules, as using the defaults leads to greater portability, and changing them can be incredibly fragile (believe me, I've tried to do what you've asked before. It may work for one platform, but then try to cross-compile the library or build it on a different system and you're toast). I have two suggestions for you that you will hopefully find agreeable.

  1. Define a "clean" target that cleans up the mess of ".o" files, or better yet:
  2. Use the C++ Project Template (which uses CMake), which puts ".o" files in a "build" directory.
Michael Aaron Safyan
@Michael, thanks for the link, I will review it and will adapt to my project in some time.
Vincenzo
+1  A: 

I'm not sure if you can specify patterns with these modification directly, but you always can use macros (at least in GNU make). An example could be this:

define export_rule
objects/$(patsubst %.cpp,%.o,$(subst /,-,$(1))) : $(1)
    <<your rules here>>
endef


SOURCES = $(shell find $(DIR) -name "*.cpp")

$(foreach s,$(SOURCES), $(eval $(call export_rule,$(s))))

This defines the exact rules you want, avoiding (as some solutions proposed above) the name clashes in case two files with the same name reside in different directories.

Diego Sevilla
Can you please explain how I can use `$@` inside `<<<your rules here>>>`? Thanks!
Vincenzo
You have to scape normal make variables. You should use `$$@` instead. Also, you would need another rule that adds all the generated .o files so that your program can depend on them.
Diego Sevilla
Now works perfectly, many thanks again! :)
Vincenzo
Please note that the files `src/test-list.c` and `src/test/list.c` would create the same `.o` file, thus clashing as well.
adamk
@adamk, agree, but in my projects all file names are similar to class names, thus `-` sign is forbidden. But again, thanks for the comment!
Vincenzo