78

I am wondering why gcc/g++ doesn't have an option to place the generated object files into a specified directory.

For example:

mkdir builddir
mkdir builddir/objdir
cd srcdir

gcc -c file1.c file2.c file3.c **--outdir=**../builddir/objdir

I know that it's possible to achive this with separate -o options given to the compiler, e.g.:

gcc -c file1.c -o ../builddir/objdir/file1.o
gcc -c file2.c -o ../builddir/objdir/file2.o
gcc -c file3.c -o ../builddir/objdir/file3.o

... and I know that I can write Makefiles via VPATH and vpath directives to simplify this.

But that's a lot of work in a complex build environment.

I could also use

gcc -c file1.c file2.c file3.c

But when I use this approach my srcdir is full of .o garbage afterwards.

So I think that an option with the semantics of --outdir would be very useful.

What is your opinion?

EDIT: our Makefiles are written in such a way that .o files actually placed into builddir/obj. But I am simply wondering if there might be a better approach.

EDIT: There are several approaches which place the burden to achieve the desired behavior to the build system (aka Make, CMake etc.). But I consider them all as being workarounds for a weakness of gcc (and other compilers too).

  • You mentioned a complex build environment, so if you're using autotools, you can configure and build outside your source directory. – Greg Bacon Nov 29 '09 at 00:51
  • no, that's not an option for me. I think that autotools are too complex to learn, not for me, but for others in the team. Option --outdir would be easy to understand even if you are not a unix wizard. –  Nov 29 '09 at 09:52
  • 3
    Nice nick. ;-) Autotools have the advantage that they shouldn't require much attention once written (just like a well-written Makefile), so it's unneccessary to train your whole team in them. – DevSolar Dec 15 '09 at 09:06
  • 3
    regarding Autotools: they are a real nightmare if something is not working as expected. –  Dec 15 '09 at 19:21
  • 2
    If you use CMake instead you don't have to deal with autotools directly and still get the benefit of autotools-style configuration/build. – Dean Michael Dec 16 '09 at 18:36
  • options `-c` and `-o` together are not recommanded. – Sandburg Nov 02 '18 at 14:08

10 Answers10

87

This is the chopped down makefile for one of my projects, which compiles the sources in 'src' and places the .o files in the directory "obj". The key bit is the the use of the patsubst() function - see the GNU make manual (which is actually a pretty good read) for details:

OUT = lib/alib.a
CC = g++
ODIR = obj
SDIR = src
INC = -Iinc

_OBJS = a_chsrc.o a_csv.o a_enc.o a_env.o a_except.o \
        a_date.o a_range.o a_opsys.o
OBJS = $(patsubst %,$(ODIR)/%,$(_OBJS))


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

$(OUT): $(OBJS) 
    ar rvs $(OUT) $^

.PHONY: clean

clean:
    rm -f $(ODIR)/*.o $(OUT)
24

How about changing to the directory and running the compile from there:

cd builddir/objdir
gcc ../../srcdir/file1.c ../../srcdir/file2.c ../../srcdir/file3.c

That's it. gcc will interpret includes of the form #include "path/to/header.h" as starting in the directory the file exists so you don't need to modify anything.

R Samuel Klatchko
  • 74,869
  • 16
  • 134
  • 187
  • 4
    I know. But - as you mentioned - then I have to change all of the relative includes. That can be a real pain, especially when you have 3rd party libs. Having --outdir would be so simple –  Nov 29 '09 at 09:49
  • 2
    No, you shouldn't need to change your relative includes. All you need to do is add a single extra -I command line option to gcc that points to the src directory. – R Samuel Klatchko Dec 13 '09 at 00:22
  • @Samuel: you are right regarding the relative includes for the given example. But it is an extremly stripped down version of the real project. For example: I have more then 30 src-dirs with a lot of relative inter-directory includes. –  Dec 15 '09 at 19:04
  • +1. In short, I think this is the only approach that will actually work for you. You can brute-force it by symlinking every header file in the source tree into the build tree. – Jason Orendorff Dec 16 '09 at 19:38
  • 3
    I double checked and my earlier comments about needing a new -I are incorrect. gcc (at least version 3.4.5 and 4.4.0) both interpret relatives paths for `#include ""` starting at the location of the source file. – R Samuel Klatchko Dec 17 '09 at 01:31
  • @RSamuelKlatchko alas, it doesn't work. I just tried: as a result all the object files in the directory with the makefile. – Hi-Angel Jun 11 '14 at 11:29
  • @anon One instance of _gcc_ per object file with _make_ is actually a performance _improvement_. How so? `make -j5` will run five compiles at once on your 4 CPU machine. This is the whole point of _make_ really. – bobbogo Jul 03 '15 at 11:31
19

A trivial but effective workaround is to add the following right after the gcc call in your Makefile:

mv *.o ../builddir/objdir

or even a soft-clean (possibly recursive) after the compilation is done, like

rm -f *.o

or

find . -name \*.o -exec rm {} \;
Davide
  • 17,098
  • 11
  • 52
  • 68
  • 1
    It does not solve it if there are source files like `bar/foo.c` and `baz/foo.c` because the two object files will overwrite each other in the current directory. – Frank Dec 16 '09 at 19:01
  • 1
    @dehmann - this question is about getting rid of object files, not about preventing gcc to overwrite them – Davide Dec 16 '09 at 20:22
  • 1
    Makefile will not get information about object files times so, it will compile already compiled files. – bartekordek Dec 18 '16 at 18:44
12

You can use a simple wrapper around gcc that will generate the necessary -o options and call gcc:

$ ./gcc-wrap -c file1.c file2.c file3.c --outdir=obj 
gcc -o obj/file1.o -c file1.c
gcc -o obj/file2.o -c file2.c
gcc -o obj/file3.o -c file3.c

Here is such a gcc_wrap script in its simplest form:

#!/usr/bin/perl -w

use File::Spec;
use File::Basename;
use Getopt::Long;
Getopt::Long::Configure(pass_through);

my $GCC = "gcc";
my $outdir = ".";
GetOptions("outdir=s" => \$outdir)
    or die("Options error");

my @c_files;
while(-f $ARGV[-1]){
    push @c_files, pop @ARGV;
}
die("No input files") if(scalar @c_files == 0);

foreach my $c_file (reverse @c_files){
    my($filename, $c_path, $suffix) = fileparse($c_file, ".c");
    my $o_file = File::Spec->catfile($outdir, "$filename.o");
    my $cmd = "$GCC -o $o_file @ARGV $c_file";
    print STDERR "$cmd\n";
    system($cmd) == 0 or die("Could not execute $cmd: $!");
}

Of course, the standard way is to solve the problem with Makefiles, or simpler, with CMake or bakefile, but you specifically asked for a solution that adds the functionality to gcc, and I think the only way is to write such a wrapper. Of course, you could also patch the gcc sources to include the new option, but that might be hard.

Frank
  • 64,140
  • 93
  • 237
  • 324
8

I believe you got the concept backwards...?!

The idea behind Makefiles is that they only process the files that have been updated since the last build, to cut down on (re-)compilation times. If you bunch multiple files together in one compiler run, you basically defeat that purpose.

Your example:

gcc -c file1.c file2.c file3.c **--outdir=**../builddir/objdir

You didn't give the 'make' rule that goes with this command line; but if any of the three files has been updated, you have to run this line, and recompile all three files, which might not be necessary at all. It also keeps 'make' from spawning a seperate compilation process for each source file, as it would do for seperate compilation (when using the '-j' option, as I would strongly suggest).

I wrote a Makefile tutorial elsewhere, which goes into some extra detail (such as auto-detecting your source files instead of having them hard-coded in the Makefile, auto-determining include dependencies, and inline testing).

All you would have to do to get your seperate object directory would be to add the appropriate directory information to the OBJFILES := line and the %.o: %.c Makefile rule from that tutorial. Neil Butterworth's answer has a nice example of how to add the directory information.

(If you want to use DEPFILES or TESTFILES as described in the tutorial, you'd have to adapt the DEPFILES := and TSTFILES := lines plus the %.t: %.c Makefile pdclib.a rule, too.)

DevSolar
  • 67,862
  • 21
  • 134
  • 209
  • @DevSolar: I know that Makefiles intendend to process only the changed files and to gain a speedup this way. But it could even speed up a lot more when something like "gcc -c $(?) --outdir=../builddir/objdir" would be possible. In such a way it would not be necessary to startup the compiler for each file individually. Instead we could do a "bunch-compile" with only one call to gcc. –  Dec 15 '09 at 19:10
  • Excellent answer, +1 for answering "why doesn't gcc do this" instead of "how can I make this happen more easily". – pra Dec 15 '09 at 21:14
  • @ Vokuhila-Oliba: Or you could make the executable gcc (which is merely a front-end to the compiler proper) invoke the compiler proper as some kind of server process, which spawns threads instead of setting up a new process on each call. Three observations: 1) This would help already existing Makefiles instead of requiring a new, custom syntax; 2) I am not at all sure if GCC doesn't do this already; 3) In any case this would be an issue to put up to the GCC mailing list, not StackOverflow. – DevSolar Dec 16 '09 at 00:13
2

I think that telling pass gcc doesn't have an separate option to say where to put object file, since it already has it. It's "-c" - it says in what directory to put object.

Having additional flag for directory only must change meening of "-c". For example:

gcc -c file.c -o /a/b/c/file.o --put-object-in-dir-non-existing-option /a1/a2/a3

You can not put /a/b/c/file.o under /a1/a2/a3, since both paths are absolute. Thus "-c" should be changed to name object file only.

I advise you to consider a replacement of makefile, like cmake, scons and other. This will enable to implement build system as for for simple project as well as for bigger one too.

See for example how it's easy to compile using cmake your example. Just create file CMakeList.txt in srcdir/:

cmake_minimum_required(VERSION 2.6)
project(test) 

add_library(test file1.c file2c file3.c) 

And now type:

mkdir -p builddir/objdir
cd builddir/objdir
cmake ../../srcdir
make

That's all, object files will reside somewhere under builddir/objdir.

I personaly use cmake and find it very convinient. It automatically generates dependencies and has other goodies.

dimba
  • 26,717
  • 34
  • 141
  • 196
  • What do you mean -c says where to put the object? No it doesn't. -c simply means: I want you to "compile" (c for compile). It's a Boolean switch, either you are compiling, with -c, or not compiling, without -c. The -o refers to the *output* file (o for output). This is not a Boolean switch. It is a key value pair, so it's `-o ` (as 2 arguments). I think the answer to the original question is more literally: you can't. gcc doesn't have that option. All these answers are work-arounds to use other tools (make, CMake, etc.) to make gcc do this thing. – Rich Jahn Mar 06 '23 at 16:36
2

Meanwhile I found a "half-way" solution by using the -combine option.

Example:

mkdir builddir
mkdir builddir/objdir
cd srcdir

gcc -combine -c file1.c file2.c file3.c -o ../builddir/objdir/all-in-one.o

this "combines" all source files into one single object file.

However, this is still "half-way" because it needs to recompile everything when only one source file changes.

2

I am trying to figure out the same thing. For me this worked

CC = g++
CFLAGS = -g -Wall -Iinclude
CV4LIBS = `pkg-config --libs opencv4`
CV4FLAGS = `pkg-config --cflags opencv4`

default: track

track:  main.o
    $(CC) -o track $(CV4LIBS) ./obj/main.o

ALLFLAGS = $(CFLAGS) $(CV4FLAGS)
main.o: ./src/main.cpp ./include/main.hpp
    $(CC) $(ALLFLAGS) -c ./src/main.cpp $(CV4LIBS) -o ./obj/main.o
``
1

This is among the problems autoconf solves.

If you've ever done ./configure && make you know what autoconf is: it's the tool that generates those nice configure scripts. What not everyone knows is that you can instead do mkdir mybuild && cd mybuild && ../configure && make and that will magically work, because autoconf is awesome that way.

The configure script generates Makefiles in the build directory. Then the entire build process happens there. So all the build files naturally appear there, not in the source tree.

If you have source files doing #include "../banana/peel.h" and you can't change them, then it's a pain to make this work right (you have to copy or symlink all the header files into the build directory). If you can change the source files to say #include "libfood/comedy/banana/peel.h" instead, then you're all set.

autoconf is not exactly easy, especially for a large existing project. But it has its advantages.

Jason Orendorff
  • 42,793
  • 6
  • 62
  • 96
-1

Personally for single files I do this,

rm -rf temps; mkdir temps; cd temps/ ; gcc -Wall -v --save-temps  ../thisfile.c ; cd ../ ; geany thisfile.c temps/thisfile.s temps/thisfile.i

temps folder will keep all the object, preprocessed and assembly files.

This is a crude way of doing things and I would prefer above answers using Makefiles.

nikhil chaubey
  • 369
  • 1
  • 5
  • 11