Gcc/g + + 选项将所有对象文件放入单独的目录

我想知道为什么 gcc/g + + 没有将生成的对象文件放入指定目录的选项。

例如:

mkdir builddir
mkdir builddir/objdir
cd srcdir


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

我知道可以通过给编译器的单独 -o 选项来实现这一点,例如:

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

... 我知道我可以通过 VPATH 和 VPATH 指令来编写 Makefile 来简化这个过程。

但是,在复杂的构建环境中,这需要做大量的工作。

我也可以用

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

但是当我使用这种方法时,我的 srcdir 之后就会充满了.o 垃圾。

因此,我认为具有—— outdir 语义的选项将非常有用。

你有什么看法?

编辑 : 我们的 Makefile 以这样一种方式编写:。O 文件实际放入 builddir/obj。但我只是想知道是否有更好的方法。

EDIT : 有几种方法会给构建系统带来实现所需行为的负担(又名 Make、 CMake 等)。但我认为它们都是 变通办法,因为 gcc 有缺陷(其他编译器也是如此)。

118428 次浏览

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.

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.

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 {} \;

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.

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)

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.)

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.

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.

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
``

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.