1

I've an issue with object creation for a cpp files residing in 2 different directories as show below:

- Project-xyz
  - Hello
    - Hello.cpp
  - World
    - Main.cpp
    - World.cpp
    - Makefile

For better clarity here is how the code looks (just a dummy code).

hello/Hello.h

#ifndef HELLO_H
#define HELLO_H

void HelloPrint();

#endif // HELLO_H

hello/Hell.cpp

#include <iostream>
#include "Hello.h"

void HelloPrint()
{
  std::cout <<"Hello" << std::endl;
}

world/World.h

 #ifndef WORLD_H
 #define WORLD_H 

 void WorldPrint();

 #endif // WORLD_H

world/World.cpp

#include <iostream>
#include "World.h"

void WorldPrint()
{
 std::cout <<"World!" << std::endl;
}

world/Makefile Using a makefile given in here. Which is working fine. But it doesn't create any obj files.

# set non-optional compiler flags here
CXXFLAGS += -std=c++11 -Wall -Wextra -pedantic-errors

# set non-optional preprocessor flags here
# eg. project specific include directories
CPPFLAGS +=

# find cpp files in subdirectories
SOURCES := $(shell find . -name '*.cpp')
SOURCES += $(shell find ../hello -name '*.cpp')

# find headers
HEADERS := $(shell find . -name '*.h')

OUTPUT := HelloWorld

# Everything depends on the output
all: $(OUTPUT)

# The output depends on sources and headers
$(OUTPUT): $(SOURCES) $(HEADERS)
    $(CXX) $(CXXFLAGS) $(CPPFLAGS) -o $(OUTPUT) $(SOURCES)

clean:
     $(RM) $(OUTPUT)

I'm looking at a solution which uses the makefile which generates the obj files in specific directory as shown here.

Now I'm trying to generate the obj files for hello/hello.cpp file in world/objdir/hello.o using the Below make file. Which is not working. How to get it working?

world/Makefile

GCC=g++
CPPFLAGS=-c -Wall
LDFLAGS=
OBJDIR=objdir
OBJ=$(addprefix $(OBJDIR)/, $(patsubst %.cpp, %.o, $(wildcard *.cpp)))
TARGET=HelloWorld

.PHONY: all clean

all: $(OBJDIR) $(TARGET)

$(OBJDIR):
    mkdir $(OBJDIR)

$(OBJDIR)/%.o: %.cpp
    $(GCC) $(CPPFLAGS) -c $< -o $@
$(TARGET): $(OBJ)
    $(GCC) $(LDFLAGS) -o $@ $^
clean:
    @rm -f $(TARGET) $(wildcard *.o)
    @rm -rf $(OBJDIR)

When i try to run the make it throws the following error.

g++ -c -Wall -c main.cpp ../hello/Hello.cpp -o objdir/main.o
g++: fatal error: cannot specify -o with -c, -S or -E with multiple files
compilation terminated.
make: *** [objdir/main.o] Error 4

It is not able to generate the obj files for the hello/Hello.cpp file and hence throwing this error.

Any help from the makefile Masters is appreciated! :)

Community
  • 1
  • 1
SantMan
  • 77
  • 6
  • 1
    -o must be used in link "phase": `link: $(GCC) -o $(TARGET) $(OBJ)`. BTW there isn't a main in your project – LPs Mar 31 '17 at 12:56
  • Oh sorry I missed to add that. It is just a simple code which I tried to explain the problem. Here it goes #include "../hello/Hello.h" #include "World.h" int main() { HelloPrint(); WorldPrint(); } – SantMan Mar 31 '17 at 16:51

3 Answers3

1

Adding vpath %.cpp ../hello along with @gaoithe's solution has solved this issue.

vpath %.cpp ../hello
GCC=g++
CPPFLAGS=-c -Wall
LDFLAGS=
OBJDIR=objdir
OBJ=$(addprefix $(OBJDIR)/, $(patsubst %.cpp, %.o, $(wildcard *.cpp)))
OBJ+=$(addprefix $(OBJDIR)/, $(patsubst %.cpp, %.o, $(notdir $(wildcard ../hello/*.cpp))))
TARGET=HelloWorld

.PHONY: all clean

all: $(OBJDIR) $(TARGET)

$(OBJDIR):
    mkdir $(OBJDIR)

$(OBJDIR)/%.o: %.cpp
    $(GCC) $(CPPFLAGS) -c $< -o $@
$(TARGET): $(OBJ)
    $(GCC) $(LDFLAGS) -o $@ $^
clean:
    @rm -f $(TARGET) $(wildcard *.o)
    @rm -rf $(OBJDIR)

now

$ ls objdir/
 Hello.o  main.o   World.o  

Experts need to evaluate the new makefile and confirm if this is a scale able solution?

Any inputs on optimising/enhancing the current makefile and help in reducing the build time is highly appreciated.

SantMan
  • 77
  • 6
0

You cannot compile multiple c or cpp files at a time AND specify ONE object file for them. See also this question Compile multiple C source fles into a unique object file The following command gives you error:

g++ -c -Wall -c main.cpp ../hello/Hello.cpp -o objdir/main.o

The following compiles to object files giving you a Main.o and Hello.o and World.o in the current directory:

g++ -I./hello -c -Wall -c world/Main.cpp hello/Hello.cpp world/World.cpp

You could compile each cpp file one by one and generate object file for each then link them together. This is how any big project with multiple source code files is build. By hand do this:

cd world
g++ -I../hello -c -Wall -c Main.cpp -o objdir/Main.o
g++ -c -Wall -c ../hello/Hello.cpp -o objdir/Hello.o
g++ -c -Wall -c World.cpp -o objdir/World.o
# link:
g++ objdir/Main.o objdir/Hello.o objdir/World.o -o HelloWorld

from man gcc

   -c  Compile or assemble the source files, but do not link.  The linking stage simply is not done.  The ultimate output is in the form of an object file for each source file.

   -o file
       Place output in file file.  This applies to whatever sort of output is being produced, whether it be an executable file, an object file, an assembler file or preprocessed C code.

Answer: I meant to not directly answer :-P but it is a little bit tricky. Here is what you need to look at in your Makefile:

# you want to add reference to hello as include dir
#  for compiling Main.cpp, In CPPFLAGS add -I ../hello

# the OBJ list is missing a reference to files in hello directory:
OBJ=$(addprefix $(OBJDIR)/, $(patsubst %.cpp, %.o, $(wildcard *.cpp)))
OBJ+=$(addprefix $(OBJDIR)/, $(patsubst %.cpp, %.o, $(notdir $(wildcard ../hello/*.cpp))))

# no change needed for the %.cpp 
# new rule needed to allow it to find hello dir:

$(OBJDIR)/%.o: 
    $(GCC) $(CPPFLAGS) -c $< -o $@
$(OBJDIR)/%.o: ../hello/%.cpp
    $(GCC) $(CPPFLAGS) -c $< -o $@

read: https://www.gnu.org/software/make/manual/html_node/File-Name-Functions.html

Community
  • 1
  • 1
gaoithe
  • 4,218
  • 3
  • 30
  • 38
  • Thanks for your inputs. I tried your suggested changes but still I'm getting the following error. mkdir objdir g++ -c -Wall -c World.cpp -o objdir/World.o g++ -c -Wall -c main.cpp -o objdir/main.o make: *** No rule to make target 'objdir/Hello.o', needed by HelloWorld'. Stop. This basically means Hello.o is still not getting generated under **"world/objdir" ** directory. Any other suggestions? I would gladly try it and get back. – SantMan Mar 31 '17 at 17:39
  • Check the $(OBJDIR)/%.o: ../hello/%.cpp rule. Do you have it in ok? and is there a ../hello/Hello.cpp file for it ? – gaoithe Apr 03 '17 at 08:58
0

It's weird that you want to access directories above current dir, since you cannot make files with names contain ..

You need this to compile multiple files with similar names, for example: in World directory you may have another file named Hello/Hello.cpp, then you have to compile both ../Hello/Hello.cpp and Hello/Hello.cpp. Although they are two different files, there is no way to produce object files with different names. You might replace .. with something like UPPERDIRECTORY but then if you have another file to compile with name UPPERDIRECTORY/Hello/Hello.cpp, you're screwed...

Anyway, here's a "temporary fix":

CXX=g++
CPPFLAGS=-Wall
LDFLAGS=
OBJDIR=objdir
TARGET=HelloWorld

# PLACE ALL NEEDED DIRECTORIES HERE
# List of directories that contains all needed cpp files
MODULES=. ../Hello

# Get all cpp source file names
SRCS=$(foreach module, $(MODULES), $(wildcard $(module)/*.cpp))   #*/weird SO comment syntax

# Convert them to object file names + $(OBJDIR) prefix
OBJS=$(SRCS:%.cpp=$(OBJDIR)/%.o)

# Get all directories of $(OBJS) so we can create object files with same name
# but locate in different dirs, for example:
#             Hello.cpp -> objdir/Hello.o                      (need to create objdir/)
#       Hello/Hello.cpp -> objdir/Hello/Hello.o                (need to create objdir/Hello/)
#    ../Hello/Hello.cpp -> objdir/UPPERDIRECTORY/Hello/Hello.o (need to create objdir/UPPERDIRECTORY/Hello/)
ALL_OBJ_DIRS=$(subst ..,UPPERDIRECTORY, $(sort $(dir $(OBJS))))

.PHONY: all clean

all: $(TARGET)

# Rule to make all needed object directories
$(ALL_OBJ_DIRS):
    @mkdir -p $@

# Rule to make object files
$(OBJDIR)/%.o: %.cpp | $(ALL_OBJ_DIRS)
# We need to replace .. with UPPERDIRECTORY in .o (output) path,
# but keep .cpp (input) path as it is
    $(CXX) -c $(CPPFLAGS) $< -o $(subst ..,UPPERDIRECTORY,$@)

# Rule to make target file
$(TARGET): $(OBJS)
# We need to replace .. with UPPERDIRECTORY in all $(OBJS) paths
    $(CXX) $(subst ..,UPPERDIRECTORY,$^) $(LDFLAGS) -o $@

clean:
    @rm -f $(TARGET)
    @rm -rf $(OBJDIR)

If I were you, I would place Makefile on top of all cpp files to avoid ..

project/
  src/
    Makefile
    Hello/
      Hello.h
      Hello.cpp
    World/
      World.h
      World.cpp
      main.cpp

Edit: you can replace / with _ in object file names so now you can include .. in your object file names:

GCC=g++
CPPFLAGS=-Wall
LDFLAGS=
OBJDIR=obj
TARGET=HelloWorld

# Modules: directories that contains all needed cpp files
MODULES=. ../Hello
# Get all cpp source file names
SRCS_WITHCURRENTDIR=$(foreach module, $(MODULES), $(wildcard $(module)/*.cpp))   #*/weird SO comment syntax
SRCS=$(SRCS_WITHCURRENTDIR:./%=%) # remove ./
# Convert them to object file names + $(OBJDIR) prefix
OBJS=$(SRCS:%.cpp=$(OBJDIR)/%.o)
OBJ_PREFIX=OBJ

.PHONY: all clean

all: $(OBJDIR) $(TARGET)

$(OBJDIR):
    @mkdir -p $@

$(OBJDIR)/%.o: %.cpp
    $(GCC) -c $(CPPFLAGS) $< -o $(addprefix $(OBJDIR)/$(OBJ_PREFIX),$(subst /,_,$(patsubst $(OBJDIR)/%,%,$@)))

$(TARGET): $(OBJS)
    $(GCC) $(addprefix $(OBJDIR)/$(OBJ_PREFIX),$(subst /,_,$(patsubst $(OBJDIR)/%,%,$^))) $(LDFLAGS) -o $@

clean:
    @rm -f $(TARGET)
    @rm -rf $(OBJDIR)

make clean all gives

g++ -c -Wall main.cpp -o obj/OBJmain.o
g++ -c -Wall World.cpp -o obj/OBJWorld.o
g++ -c -Wall ../Hello/Hello.cpp -o obj/OBJ.._Hello_Hello.o
g++ obj/OBJmain.o obj/OBJWorld.o obj/OBJ.._Hello_Hello.o  -o HelloWorld

3 object files in obj/: OBJmain.o, OBJWorld.o, OBJ.._Hello_Hello.o

tnt
  • 1,174
  • 2
  • 10
  • 14
  • Thanks Tntxtnt. I completely agree on your point on me getting screwed! :) Just to give some clarity why it is not possible to have a makefile under "Hello" directory. Basically i'm using Apache thrift to auto generate some code and it generates some code in "Hello" directory and in the "World" directory I'll make use of the "Hello.cpp" file which is generated. So I cannot have a makefile in "Hello" directory for sure. But I do have a Makefile in the Parent Directory of the project. Which inturn should recursively call the makefile in respective sub folders. – SantMan Mar 31 '17 at 17:03
  • This helps me in building the code in modular way and I need not give build from the top level. Instead I can build in my local directory. If you have any better approach to this problem. I'm open for your suggestions. I'll try out your suggested solution and get back. – SantMan Mar 31 '17 at 17:04
  • I found a similar situation [here](http://stackoverflow.com/questions/4102469/makefile-to-put-object-files-from-source-files-different-directories-into-a-sing). Where you may be required have dependecy of single makefile across the different directory structure. – SantMan Mar 31 '17 at 17:53
  • I think you can't compile files with same name using `vpath` though. If there is no such name collision then it's good. On second though I think you can rename the output `.o` from `../Hello/Hello.o` to for example `OBJ.._Hello_Hello.o` (replace `/` with `_` and add prefix `OBJ`) so you can compile files with same name. – tnt Mar 31 '17 at 18:51