代码之家  ›  专栏  ›  技术社区  ›  anon

gcc/g++选项,用于将所有对象文件放在单独的目录中

  •  56
  • anon  · 技术社区  · 15 年前

    我想知道为什么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中。但我只是想知道是否有更好的方法。

    编辑 :有几种方法会给构建系统带来实现所需行为的负担(即make、cmake等)。但我认为他们都是 解决办法 因为GCC(以及其他编译器)的弱点。

    8 回复  |  直到 13 年前
        1
  •  65
  •   anon    15 年前

    这是我的一个项目的已切碎的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)
    
        2
  •  15
  •   R Samuel Klatchko    15 年前

    如何更改到目录并从中运行编译:

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

    就是这样。GCC将解释表格的内容。 #include "path/to/header.h" 从目录中开始,文件就存在,因此您不需要修改任何内容。

        3
  •  12
  •   Davide    15 年前

    一个简单但有效的解决方法是在生成文件中的gcc调用之后添加以下内容:

    mv *.o ../builddir/objdir
    

    甚至一个 软清洁 (可能是递归的)编译完成后,比如

    rm -f *.o
    

    find . -name \*.o -exec rm {} \;
    
        4
  •  10
  •   Frank    15 年前

    你可以用一个简单的包装 gcc 这将产生必要的 -o 期权和看涨期权 海湾合作委员会 :

    $ ./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 或更简单,带有 CMake bakefile ,但您特别要求提供一个将功能添加到 海湾合作委员会 我认为唯一的方法就是写这样一个包装器。当然,你也可以修补 海湾合作委员会 来源包括新选项,但这可能很难。

        5
  •  4
  •   DevSolar    15 年前

    我相信你把概念倒过来了…?!

    makefiles背后的思想是,它们只处理自上次构建以来更新的文件,以减少(重新)编译时间。如果在一个编译器运行中将多个文件组合在一起,那么基本上就无法实现这一目标。

    你的例子:

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

    您没有给出与此命令行匹配的“make”规则;但是如果 任何 在三个文件中,您必须运行此行并重新编译 三都 可能根本不需要的文件。它还防止“make”为每个源文件生成单独的编译过程,就像它对单独的编译所做的那样(当使用'-j'选项时,正如我强烈建议的那样)。

    我写了一篇 Makefile tutorial 在其他地方,这涉及到一些额外的细节(例如自动检测源文件,而不是在makefile中硬编码源文件,自动确定包含依赖项,以及内联测试)。

    要获得单独的对象目录,您所要做的就是将适当的目录信息添加到 OBJFILES := 线与 %.o: %.c Makefile 这个教程中的规则。NeilButterworth的答案有一个如何添加目录信息的好例子。

    (如果您要使用教程中描述的depfiles或testfiles,则必须调整 DEPFILES := TSTFILES := 线条加上 %.t: %.c Makefile pdclib.a 也是规则。

        6
  •  1
  •   dimba    15 年前

    我认为告诉pass 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下,因为这两个路径都是绝对路径。因此,应该将“-c”更改为仅命名对象文件。

    我建议你考虑替换makefile,比如 cmake , scons 以及其他。 这将使构建系统既可以用于简单的项目,也可以用于更大的项目。

    例如,请参阅如何使用cmake示例轻松编译。 只需在srcdir/中创建文件cmakelist.txt即可:

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

    现在类型:

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

    仅此而已,对象文件将驻留在builddir/objdir下的某个位置。

    我个人使用CMAKE,发现它非常方便。它自动生成依赖关系并具有其他优点。

        7
  •  1
  •   anon    15 年前

    同时,我通过使用-combine选项找到了一个“中途”解决方案。

    例子:

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

    这个“组合”所有源文件成一个单一的对象文件。

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

        8
  •  0
  •   Jason Orendorff    15 年前

    这是问题之一 autoconf 解决。

    如果你做过 ./configure && make 您知道autoconf是什么:它是一个工具,可以生成那些漂亮的配置脚本。不是每个人都知道的是你可以代替 mkdir mybuild && cd mybuild && ../configure && make 这将神奇地起作用,因为autoconf就是这样的。

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

    如果有源文件正在执行 #include "../banana/peel.h" 您不能更改它们,那么要使其正常工作是一件痛苦的事情(您必须将所有头文件复制或符号链接到构建目录中)。如果你能把源文件改成 #include "libfood/comedy/banana/peel.h" 相反,你已经准备好了。

    autoconf不完全正确 容易的 尤其是对于现有的大型项目。但它有它的优势。