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指令来编写Makefiles来简化它。

但是在复杂的构build环境中这是很多工作。

我也可以使用

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

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

所以我认为使用–outdir语义的选项会非常有用。

你有什么意见?

编辑 :我们的Makefiles是这样编写的.o文件实际上放在builddir / obj中。 但我只是想知道是否有更好的方法。

编辑 :有几种方法可以将构build系统(aka Make,CMake等)实现所需的行为。 但是我认为它们都是gcc(也是其他编译器)弱点的解决方法

这是我的一个项目的makefile文件,它编译'src'中的源代码并将.o文件放在目录“obj”中。 关键是使用patsubst()函数 – 请参阅GNU make手册(实际上这是一个相当不错的阅读)了解详细信息:

 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) 

如何切换到目录并从那里运行编译:

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

而已。 gcc将解释文件所在目录中包含的#include "path/to/header.h"forms,因此不需要修改任何内容。

你可以使用一个简单的gcc包装器来生成必要的-o选项并调用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 

这是最简单的gcc_wrap脚本:

 #!/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: $!"); } 

当然,标准的方法是用Makefiles或者简单的方法来解决这个问题,但是你特别要求给gcc增加function的解决scheme,我认为唯一的方法就是编写这样一个包装器。 当然,你也可以修补gcc来源,包括新的选项,但这可能是困难的。

一个简单而有效的解决方法是在Makefile中调用gcc之后添加以下内容:

 mv *.o ../builddir/objdir 

甚至是在编译完成之后进行软清理 (可能是recursion),就像

 rm -f *.o 

要么

 find . -name \*.o -exec rm {} \; 

我相信你把这个概念倒退了…?

Makefiles背后的想法是,它们只处理自上次构build以来更新的文件,以减less(重新)编译时间。 如果你在一个编译器运行中把多个文件放在一起,你基本上就是失败了。

你的例子:

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

你没有给这个命令行的“make”规则。 但是如果三个文件中的任何一个已经被更新了,你必须运行这一行,并重新编译所有这三个文件,这些文件可能根本就不需要。 它也保持从每个源文件中产生一个独立的编译过程,就像单独编译(当使用'-j'选项时,正如我强烈build议的那样)。

我在别处写了一个Makefile教程 ,这个教程涉及到一些额外的细节(例如自动检测源文件,而不是在Makefile中进行硬编码,自动确定包含依赖项以及内联testing)。

所有你需要做的事情就是把相应的目录信息添加到OBJFILES :=行和该教程的%.o: %.c Makefile规则中。 尼尔·巴特沃斯的答案有一个很好的例子来说明如何添加目录信息。

(如果您想使用本教程中所述的DEPFILES或TESTFILES,则必须修改DEPFILES :=TSTFILES :=行以及%.t: %.c Makefile pdclib.a规则。

我认为告诉通过gcc没有一个单独的选项来说哪里把目标文件,因为它已经有了它。 它是“-c” – 它说在什么目录中放置对象。

只有目录的额外标志必须改变“-c”的变化。 例如:

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

你不能把/a/b/c/file.o放在/ a1 / a2 / a3下,因为这两个path都是绝对的。 因此,“-c”应该改为只命名对象文件。

我build议你考虑更换makefile,比如cmake , scons等。 这对于简单的项目以及对于更大的项目都能够实现构build系统。

例如,看看如何使用cmake编译你的例子很容易。 只需在srcdir /中创build文件CMakeList.txt:

 cmake_minimum_required(VERSION 2.6) project(test) add_library(test file1.c file2c file3.c) 

现在input:

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

就是这样,对象文件将位于builddir / objdir下的某个位置。

我个人使用cmake,发现它非常方便。 它会自动生成依赖关系,并具有其他好处。

同时我使用-combine选项find了一个“中途”解决scheme。

例:

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

这个“结合”所有的源文件到一个单一的目标文件。

但是,这仍然是“中途”,因为只有一个源文件发生变化时,它需要重新编译所有内容。

这是autoconf解决的问题之一。

如果你曾经完成./configure && make你知道autoconf是什么:它是生成这些好的configuration脚本的工具。 不是每个人都知道的是,你可以改为使用mkdir mybuild && cd mybuild && ../configure && make ,那样会奇迹般地工作,因为autoconf mkdir mybuild && cd mybuild && ../configure && make很棒。

configure脚本在build目录中生成Makefiles。 然后整个构build过程发生在那里。 所以所有的构build文件自然出现在那里,而不是在源码树中。

如果你的源代码文件正在执行#include "../banana/peel.h"并且你不能改变它们,那么把这个工作正确的话是很#include "../banana/peel.h" (你必须将所有的头文件复制或者符号链接到编译目录)。 如果你可以改变源文件来说#include "libfood/comedy/banana/peel.h" ,那么你就全部设置好了。

autoconf并不是很容易 ,特别是对于大型的现有项目。 但是它有它的优点。