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并不是很容易 ,特别是对于大型的现有项目。 但是它有它的优点。