【问题标题】:gcc/g++ option to place all object files into separate directorygcc/g++ 选项将所有目标文件放到单独的目录中
【发布时间】:2010-12-21 07:39:45
【问题描述】:

我想知道为什么 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(以及其他编译器)的弱点的解决方法

【问题讨论】:

  • 您提到了一个复杂的构建环境,所以如果您使用自动工具,您可以在源目录之外进行配置和构建。
  • 不,这不是我的选择。我认为 autotools 太复杂而无法学习,不是为了我,而是为了团队中的其他人。即使您不是 unix 向导,选项 --outdir 也很容易理解。
  • 尼斯尼克。 ;-) Autotools 的优点是它们一旦编写就不需要太多关注(就像编写良好的 Makefile 一样),因此没有必要对整个团队进行培训。
  • 关于 Autotools:如果某些东西没有按预期工作,它们将是一场真正的噩梦。
  • 如果您改用 CMake,您不必直接处理 autotools,仍然可以享受 autotools 风格的配置/构建的好处。

标签: gcc g++


【解决方案1】:

我个人对单个文件这样做,

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 文件夹将保存所有对象、预处理和程序集文件。

这是一种粗略的做事方式,我更喜欢使用 Makefiles 的上述答案。

【讨论】:

    【解决方案2】:

    我试图找出同样的事情。对我来说这有效

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

    【讨论】:

      【解决方案3】:

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

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

      就是这样。 gcc 会将 #include "path/to/header.h" 形式的包含解释为从文件存在的目录开始,因此您无需修改​​任何内容。

      【讨论】:

      • 我知道。但是 - 正如你所提到的 - 那么我必须改变所有的相对包括。这可能是一个真正的痛苦,尤其是当你有 3rd 方库时。拥有 --outdir 就这么简单
      • 不,您不需要更改您的相对包含。您需要做的就是向 gcc 添加一个额外的 -I 命令行选项,该选项指向 src 目录。
      • @Samuel:对于给定示例的相对包含,您是对的。但它是真实项目的一个极其精简的版本。例如:我有 30 多个 src-dirs,其中包含很多相关的跨目录。
      • +1。简而言之,我认为这是唯一真正适合您的方法。您可以通过将源树中的每个头文件符号链接到构建树中来暴力破解它。
      • 我仔细检查过,我之前关于需要一个新的 -I 的 cmet 是不正确的。 gcc(至少版本 3.4.5 和 4.4.0)都从源文件的位置开始解释 #include "" 的相对路径。
      【解决方案4】:

      您可以在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解决问题,或者更简单,用CMakebakefile解决问题,但你特别要求一个解决方案,将功能添加到gcc,我认为唯一的方法是编写这样的包装器。当然,您也可以修补 gcc 源以包含新选项,但这可能很难。

      【讨论】:

      • 'bakefile' 对我来说是新的。非常感谢!
      【解决方案5】:

      这是我的一个项目的精简生成文件,它编译'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)
      

      【讨论】:

      • 不错。目前我以类似的方式进行操作。 (注意:我问这个问题是因为我想知道是否有更好的方法)
      • 良好的标准溶液。为简化起见,使用XDIR? 摆脱目标
      • 是的,那是外籍人士的东西——我应该早点把它砍掉的。
      【解决方案6】:

      这是autoconf 解决的问题之一。

      如果您曾经使用过./configure &amp;&amp; make,您就会知道什么是 autoconf:它是生成那些漂亮的配置脚本的工具。不是每个人都知道的是,您可以改为使用mkdir mybuild &amp;&amp; cd mybuild &amp;&amp; ../configure &amp;&amp; make,这会神奇地起作用,因为 autoconf 这样做很棒。

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

      如果您有源文件在执行#include "../banana/peel.h" 并且您无法更改它们,那么让这项工作正常工作很痛苦(您必须将所有头文件复制或符号链接到构建目录中)。如果您可以将源文件更改为 #include "libfood/comedy/banana/peel.h",则一切就绪。

      autoconf 并不完全简单,尤其是对于现有的大型项目。但它有它的优点。

      【讨论】:

      • 您可以将它与 bakefile 一起使用,一旦您在 XML 文件中指定了您的要求,它将为您生成所有内容。
      • 糟糕,抱歉,我看到 cmets 中已经提到了 autotools。哦,好吧!
      【解决方案7】:

      我相信你的概念倒退了......?!

      Makefile 背后的想法是,它们只处理自上次构建以来已更新的文件,以减少(重新)编译时间。如果你在一次编译器运行中将多个文件捆绑在一起,你基本上就达不到这个目的了。

      你的例子:

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

      你没有给出这个命令行的“make”规则;但如果三个文件中的任何文件已更新,则必须运行此行,并重新编译所有三个文件,这可能根本没有必要。它还可以防止“make”为每个源文件生成单独的编译过程,就像单独编译一样(使用“-j”选项时,我强烈建议)。

      我在别处写了一个Makefile tutorial,其中涉及一些额外的细节(例如自动检测您的源文件而不是将它们硬编码在 Makefile 中、自动确定包含依赖项和内联测试)。

      要获得单独的对象目录,您只需将适当的目录信息添加到该教程中的OBJFILES := 行和%.o: %.c Makefile 规则即可。 Neil Butterworth 的回答有一个很好的例子来说明如何添加目录信息。

      (如果您想按照教程中的说明使用 DEPFILES 或 TESTFILES,您还必须调整 DEPFILES :=TSTFILES := 行加上 %.t: %.c Makefile pdclib.a 规则。)

      【讨论】:

      • @DevSolar:我知道 Makefiles 打算只处理更改的文件并以这种方式获得加速。但是,当“gcc -c $(?) --outdir=../builddir/objdir”之类的东西成为可能时,它甚至可以加快速度。这样就不需要为每个文件单独启动编译器。相反,我们可以只调用一次 gcc 来执行“bunch-compile”。
      • 优秀的答案,+1 用于回答“为什么 gcc 不这样做”而不是“我怎样才能更容易地做到这一点”。
      • @ Vokuhila-Oliba:或者你可以让可执行的 gcc(它只是编译器本身的前端)调用编译器作为某种服务器进程,它产生线程而不是设置在每次调用时启动一个新进程。三个观察: 1)这将有助于现有的 Makefile 而不是需要新的自定义语法; 2)我完全不确定GCC是否还没有这样做; 3) 在任何情况下,这都是提交给 GCC 邮件列表的问题,而不是 StackOverflow。
      【解决方案8】:

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

      例子:

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

      这会将所有源文件“组合”成一个目标文件。

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

      【讨论】:

      【解决方案9】:

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

      mv *.o ../builddir/objdir
      

      甚至是编译完成后的soft-clean(可能是递归的),比如

      rm -f *.o
      

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

      【讨论】:

      • 如果有bar/foo.cbaz/foo.c这样的源文件,则无法解决,因为两个目标文件会在当前目录中相互覆盖。
      • @dehmann - 这个问题是关于摆脱目标文件,而不是防止 gcc 覆盖它们
      • Makefile 不会获取有关目标文件时间的信息,因此它将编译已编译的文件。
      【解决方案10】:

      我认为告诉 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,例如 cmakescons 等。 这将能够为简单的项目以及更大的项目实现构建系统。

      例如,查看如何使用 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 并发现它非常方便。它会自动生成依赖项并具有其他优点。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-22
        • 2011-07-07
        • 2011-05-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-13
        相关资源
        最近更新 更多