LinuxQuestions.org
Review your favorite Linux distribution.
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices

Reply
 
Search this Thread
Old 03-08-2010, 08:57 PM   #1
Jerry Mcguire
Member
 
Registered: Jul 2009
Location: Hong Kong SAR
Distribution: RedHat, Fedora
Posts: 136

Rep: Reputation: 17
Makefiles and subdirectories


Hi all, I have questions about how to make good use of makefile.

I've used the very basics of a Makefile before such as this:
Code:
SHELL=/bin/bash

all: sts2msw.exe

sts2msw.exe: sts2msw.o sts2msw_cls.o
	g++ -o $@ $^
sts2msw.o: sts2msw.cc sts2msw_cls.h
...
It works fine when the Makefile, the source codes, and the output executables are in the same directory.

If I want to organize the files into subdirectories like this

~\MyProj\
~\MyProj\src\ <- contains the *.cc *.c *.h and Makefile
~\MyProj\bin\ <- contains the output exe's.

How should the above Makefile be changed?

Also, any suggestions about where to store the intermediate *.o files?

Thank you.
 
Old 03-09-2010, 12:56 AM   #2
grail
Guru
 
Registered: Sep 2009
Location: Perth
Distribution: Manjaro
Posts: 7,411

Rep: Reputation: 1873Reputation: 1873Reputation: 1873Reputation: 1873Reputation: 1873Reputation: 1873Reputation: 1873Reputation: 1873Reputation: 1873Reputation: 1873Reputation: 1873
How about:

Code:
TOPDIR := ~\MyProj
SRCDIR := $(TOPDIR)\src
BINDIR := $(TOPDIR)\bin

SOURCES := $(shell find $(SRCDIR) -name "*.cc") // obviously change this if only c files
OBJECTS := $(patsubst %.c, %.o, $(SOURCES))

CC := g++ // again change to gcc if required

ALLFILES := $(SOURCES) // add headers and the like if required

.PHONY: all

all: sts2msw

sts2msw: $(OBJECTS)
    $(CC) $^ -o $(BINDIR)/$@
 
Old 03-09-2010, 01:18 AM   #3
YetAnotherDave
Member
 
Registered: Feb 2005
Posts: 94

Rep: Reputation: 17
How about this for a makefile:

Code:
INTERMEDIATE_DIR=../intermediates
OFILES=$(foreach bname, $(basename $(CFILES)), $(INTERMEDIATE_DIR)/$(bname).o)

CFILES=sts2msw.cc sts2msw_cls.cc
EFILE=../bin/sts2ms2.exe

all: $(EFILE)

$(INTERMEDIATE_DIR)/%.o: %.cc
        g++ -c -o $@ $<

$(EFILE): $(OFILES)
        g++ -o $@ $^
I think it does most of what you want.

For dependencies based on include files, see : Advanced Auto-Dependency Generation
 
Old 03-09-2010, 09:35 AM   #4
grail
Guru
 
Registered: Sep 2009
Location: Perth
Distribution: Manjaro
Posts: 7,411

Rep: Reputation: 1873Reputation: 1873Reputation: 1873Reputation: 1873Reputation: 1873Reputation: 1873Reputation: 1873Reputation: 1873Reputation: 1873Reputation: 1873Reputation: 1873
Well I know I already answered, but I think I got something a bit more robust: (happy for some comments on what I could improve)

Code:
TOPDIR  := ~/MyProj/
SRCDIR  := $(TOPDIR)src/
OBJDIR  := $(TOPDIR)obj/
BINDIR  := $(TOPDIR)bin/
NAME    := sts2msw
EXE     := $(BINDIR)$(NAME)

SFILES  := cc
OFILES  := o
CC      := g++
CFLAGS  := -c -Wall

SOURCES := $(shell find $(SRCDIR) -name "*.$(SFILES)")
OBJECTS := $(patsubst $(SRCDIR)%.$(SFILES), $(OBJDIR)%.$(OFILES), $(SOURCES))

ALLFILES := $(SOURCES)

.PHONY: all clean

all:    $(EXE)

$(EXE): $(OBJECTS)
        @$(CC) $^ -o $@

$(OBJDIR)%$(OFILES):    $(SRCDIR)%$(SFILES)
        @$(CC) $(CFLAGS) $< -o $@

clean:
        @rm -f $(OBJECTS) $(EXE)
 
Old 03-09-2010, 11:34 AM   #5
YetAnotherDave
Member
 
Registered: Feb 2005
Posts: 94

Rep: Reputation: 17
Nice!

A couple of comments:

If you use a "~" to define TOPDIR, then the shell translates that to a path when you do the find resulting in SOURCES containing full pathnames that don't contain the "~". Then the patsubst command does not match SRCDIR because it *does* contain a "~". This problem goes away if you define TOPDIR as $(HOME)/MyProj/ instead of ~/Myproj/

I like to explicitly list all the sources that should be combined into an executable rather than using find or some other shell command to get them. This way you don't have to worry about some temporary copy of a source file getting pulled into your executable. You also don't pay the price of invoking a shell *every* time you run make, even if you don't even need the list of sources. That's just a personal preference though.

Here's a slightly modified version of your makefile that addresses these two points:

Code:
TOPDIR   := $(HOME)/MyProj/
SRCDIR   := $(TOPDIR)src/
OBJDIR   := $(TOPDIR)obj/
BINDIR   := $(TOPDIR)bin/
NAME     := sts2msw
CC_NAMES := sts2msw.cc  sts2msw_cls.cc
EXE      := $(BINDIR)$(NAME)
all: exe

# Everything below this point is generic and could be put into a common 
# make include file to be included in other makefiles.

SFILES   := cc
OFILES   := o
CC       := g++
CFLAGS   := -c -Wall

SOURCES := $(foreach sname, $(CC_NAMES), $(SRCDIR)$(sname))
OBJECTS := $(patsubst $(SRCDIR)%.$(SFILES), $(OBJDIR)%.$(OFILES), $(SOURCES))

ALLFILES := $(SOURCES)

.PHONY: all clean exe

exe:    $(EXE)

$(EXE): $(OBJECTS)
	        $(CC) $^ -o $@

$(OBJDIR)%$(OFILES):    $(SRCDIR)%$(SFILES)
	        $(CC) $(CFLAGS) $< -o $@

clean:
	        rm -f $(OBJECTS) $(EXE
 
Old 03-10-2010, 12:58 AM   #6
Jerry Mcguire
Member
 
Registered: Jul 2009
Location: Hong Kong SAR
Distribution: RedHat, Fedora
Posts: 136

Original Poster
Rep: Reputation: 17
Thanks for replying.

I see that the approach is to resolve the files into their fullpath and name using variables and macros.

e.g.
originally
Code:
a.exe: a.o c.o common.o
	g++ -o $@ $^
b.exe: b.o c.o
	g++ -o $@ $^

a.o: a.cc a.h common.h
b.o: b.cc b.h common.h
c.o: c.cc c.h
common.o: common.cc common.h
may become
Code:
SRCDIR := ./
OBJDR := ../obj/
BINDIR := ../bin/

$(BINDIR)a.exe: $(OBJDR)a.o $(OBJDR)c.o $(OBJDR)common.o
	g++ -o $@ $^
$(BINDIR)b.exe: $(OBJDR)b.o $(OBJDR)c.o
	g++ -o $@ $^

$(OBJDR)a.o: $(SRCDIR)a.cc $(SRCDIR)a.h $(SRCDIR)common.h
$(OBJDR)b.o: $(SRCDIR)b.cc $(SRCDIR)b.h $(SRCDIR)common.h
$(OBJDR)c.o: $(SRCDIR)c.cc $(SRCDIR)c.h
$(OBJDR)common.o: $(SRCDIR)common.cc $(SRCDIR)common.h
It is quite annoying if there are a dozen executables in the project and a hundred sources and headers. I don't think I'm alone out here. Any suggestions to simplify?

What I am looking for, is if there are techniques that can tell MAKE the *.o are located in $(OBJDR), and *.cc *.h are in $(SRCDIR), and *.exe in $(BINDIR), and the like?

Last edited by Jerry Mcguire; 03-10-2010 at 01:04 AM.
 
Old 03-10-2010, 10:02 AM   #7
grail
Guru
 
Registered: Sep 2009
Location: Perth
Distribution: Manjaro
Posts: 7,411

Rep: Reputation: 1873Reputation: 1873Reputation: 1873Reputation: 1873Reputation: 1873Reputation: 1873Reputation: 1873Reputation: 1873Reputation: 1873Reputation: 1873Reputation: 1873
Ok Jerry ... (I hope you appreciate this as it took me a lot more hours than it probably should have (mainly cause I am doing this for the first time inages ))

Anyhoo, here we go:

1. Under the 'src' directory, place all your files in the respective directories named after the executable (ie. src/a, src/b, etc)
2. In each of these place their own Makefile (good part here is that these are all identical files, so make 1 copy/paste)
This file looks like:

Code:
TOPDIR  := $(HOME)
OBJDIR  := $(TOPDIR)obj/
BINDIR  := $(TOPDIR)bin/
NAME    := $(notdir $(SRCNAME)) //Magic happens here
EXE     := $(BINDIR)$(NAME)

SFILES  := cc
OFILES  := o
CC      := g++
CFLAGS  := -c -Wall

SOURCES := $(wildcard *.$(SFILES))
OBJECTS := $(patsubst %$(SFILES), $(OBJDIR)%$(OFILES), $(SOURCES))

ALLFILES := $(SOURCES)

.PHONY: all clean test

all:    $(EXE)

$(EXE): $(OBJECTS)
        @$(CC) $^ -o $@

$(OBJDIR)%$(OFILES):    %$(SFILES)
        @$(CC) $(CFLAGS) $< -o $@
3. Place the following Makefile in the top level dir (ie where src, obj and bin are):

Code:
ALLSOURCES := $(wildcard src/*)

.PHONY:	sources $(ALLSOURCES)

sources:	$(ALLSOURCES)

$(ALLSOURCES):
	export SRCNAME=$(notdir $@); \
	$(MAKE) -C $@
I would lay odds there is a better way, but hey ... this worked to compile a couple of different executables for me
 
Old 03-11-2010, 08:58 PM   #8
Jerry Mcguire
Member
 
Registered: Jul 2009
Location: Hong Kong SAR
Distribution: RedHat, Fedora
Posts: 136

Original Poster
Rep: Reputation: 17
Thank you for spending time helping. But it doesn't work yet. Where do you put the common sources?

The tree is like like now
Code:
.
|-- Makefile
|-- bin
|-- obj
|-- src
|   |-- common.cc
|   |-- common.h
|   |-- a
|   |   |-- makefile
|   |   |-- a.cc
|   |   |-- a1.cc
|   |   |-- a1.h
|   |   |-- a2.cc
|   |   `-- a2.h
|   `-- b
|       |-- makefile
|       |-- b.cc
|       |-- b1.cc
|       `-- b1.h
`-- tree.txt
Note the Makefile and makefile's are different for easy reference.

The TOPDIR in makefile has been modified to
TOPDIR := /home/jerry/dev/

At the $TOPDIR, 'make' gives the following error:
Code:
$ make
export SRCNAME=common.cc; \
        make -C /home/jerry/dev/src/common.cc
make: *** /home/jerry/dev/src/common.cc: Not a directory.  Stop.
make: *** [/home/jerry/dev/src/common.cc] Error 2
---

but I think I understand what you are doing. I don't know if I make any sense to you...

Basically, organise the unique source files in a subdirectories and the common source files in the upper level directories. Have the make program traverse to the bottom level subdir so that the dependencies are built in the way.

I will be back with a solution!
 
Old 03-11-2010, 09:34 PM   #9
YetAnotherDave
Member
 
Registered: Feb 2005
Posts: 94

Rep: Reputation: 17
Jerry, is your intention to build three different executables, one for directory a, one for b, and one for src? Or do you want to build just one executable that combines code from all three directories?

Never mind. You answered that in your post #6 on this thread.

Last edited by YetAnotherDave; 03-11-2010 at 10:01 PM.
 
Old 03-12-2010, 03:38 AM   #10
grail
Guru
 
Registered: Sep 2009
Location: Perth
Distribution: Manjaro
Posts: 7,411

Rep: Reputation: 1873Reputation: 1873Reputation: 1873Reputation: 1873Reputation: 1873Reputation: 1873Reputation: 1873Reputation: 1873Reputation: 1873Reputation: 1873Reputation: 1873
Hey Jerry

I see where you are coming from, and again I am still wrapping my own head around some of this stuff, but,
based on the new criteria of "common" files, if they are not creating an executable (ie common.exe)
then I would place them at the top level (ie that is in the TOPDIR) the put COMMON := <you know the rest>
in the top Makefile and export prior to call to compile the rest, this will then get past to all subsequent
calls to make and you can simply combine with SOURCES.

Hmmm ... thinking as I am typing, the other alternative is to place them in SRC directory and have the ALLSOURCES
script look only for items without a suffix, but then in the Makefiles in the sources directories do an include
of all files under dir name (ie src/a) as well as under SRC.

btw. the site that has helped me enormously is the following:

http://www.gnu.org/software/make/manual/make.html

and this part should help with some of the above I have explained:

http://www.gnu.org/software/make/man...Name-Functions
 
Old 03-14-2010, 10:01 PM   #11
Jerry Mcguire
Member
 
Registered: Jul 2009
Location: Hong Kong SAR
Distribution: RedHat, Fedora
Posts: 136

Original Poster
Rep: Reputation: 17
I think the issue is at the rule definition:

target : dependencies

Although dependencies can be searched through VPATH or vpath, one must clearly spell out where to find and put target. Thus,

Code:
VPATH := $(SRCDIR):$(OBJDIR)  #bad example anyway
$(BINDIR)/a.exe : a.o a1.o a2.o common.o
$(BINDIR)/b.exe : b.o b1.o common.o
$(OBJDIR)/a.o : a.cc common.h
$(OBJDIR)/a1.o : a1.cc a1.h
$(OBJDIR)/a2.o : a2.cc a2.h
...
I would be nice if make can just resolve the location of the file by its suffix:

Code:
resolve %.exe,$(BINDIR)  # my humble wish only
resolve %.o,$(OBJDIR)   # my humble wish only
a.exe : a.o a1.o a2.o common.o
b.exe : b.o b1.o common.o
a.o : a.cc common.h
a1.o : a1.cc a1.h
a2.o : a2.cc a2.h
...
 
Old 03-16-2010, 08:07 PM   #12
YetAnotherDave
Member
 
Registered: Feb 2005
Posts: 94

Rep: Reputation: 17
Hi Jerry,

I don't know if this is still of interest or not.

That said, here are makefiles that should do most of what you have in mind. Include file dependencies are not provided but that can be retrofitted.

At the top level : makefile.include
Code:
OBJDIR   := $(TOPDIR)/obj
BINDIR   := $(TOPDIR)/bin

SFILES   := cc
OFILES   := o
CC       := g++
CFLAGS   := -c -Wall

EXE      := $(BINDIR)/$(EXE_NAME)

SOURCES := $(foreach sname, $(CC_NAMES), $(abspath $(sname)))
OBJECTS := $(patsubst $(SRCDIR)/%.$(SFILES), $(OBJDIR)/%.$(OFILES), $(SOURCES))

.PHONY: all alldefault clean exe

exe:    $(EXE)

$(EXE): $(OBJECTS)
        mkdir -p $(dir $@)
        $(CC) $^ -o $@

$(OBJDIR)/%$(OFILES):    $(SRCDIR)/%$(SFILES)
        mkdir -p $(dir $@)
        $(CC) $(CFLAGS) $< -o $@

clean:
        rm -f $(OBJECTS) $(EXE)
In the "src/a" directory: makefile
Code:
TOPDIR   := $(HOME)/MyProj
EXE_NAME := a
CC_NAMES := a.cc a1.cc a2.cc $(TOPDIR)/src/common.cc

include $(TOPDIR)/makefile.include
In the "src/b" directory: makefile
Code:
TOPDIR   := $(HOME)/MyProj
EXE_NAME := b
CC_NAMES := b.cc b1.cc ../common.cc

include $(TOPDIR)/makefile.include
So, in summary, to build an executable, specify TOPDIR, EXE_NAME, and CC_NAMES and include makefile.include.

Note that in "src/a/makefile" the common.cc file is absolute path and in "src/b/makefile" the same file is specified via relative path. Either one works.

There's room for a lot of polishing here - I kept it minimal for ease of understanding.

In particular, if you want to do something with include file dependencies this is a good reference: Advanced Auto-Dependency Generation
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Makefiles...... :( Idk blaaa Linux - Newbie 4 02-01-2010 07:27 PM
makefiles... DEF. Programming 2 04-20-2009 01:46 PM
Makefiles deveshs Linux - Software 2 05-02-2005 05:26 AM
Those makefiles... boku Programming 10 03-01-2005 08:19 AM
MAKEFILEs shinpadsmt Linux - Newbie 3 02-21-2004 02:52 AM


All times are GMT -5. The time now is 02:34 PM.

Main Menu
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration