我想知道为什么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(以及其他编译器)弱点的解决方法。
这是我的一个项目的简化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"
形式的包含解释为从文件存在的目录开始,因此您无需修改任何内容。
一个简单但有效的解决方法是在Makefile中的gcc调用之后添加以下内容:
mv *.o ../builddir/objdir
或者甚至在编译完成后进行软清理(可能是递归的),比如
rm -f *.o
要么
find . -name \*.o -exec rm {} \;
您可以使用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
或CMake
来解决bakefile
或更简单的问题,但是您特别要求为gcc
添加功能的解决方案,我认为唯一的方法是编写这样的包装器。当然,您也可以修补gcc
源以包含新选项,但这可能很难。
我相信你有倒退的概念......?!
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
规则。)
我认为告诉传递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,发现它非常方便。它会自动生成依赖项并具有其他好处。
同时我通过使用-combine选项找到了“中途”解决方案。
例:
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是什么:它是生成那些不错的配置脚本的工具。不是每个人都知道的是,你可以改为做mkdir mybuild && cd mybuild && ../configure && make
而且神奇地工作,因为autoconf就是那么棒了。
configure
脚本在构建目录中生成Makefile。然后整个构建过程就在那里发生。因此,所有构建文件自然出现在那里,而不是在源树中。
如果您有源文件执行#include "../banana/peel.h"
并且您无法更改它们,那么使这项工作正常是很痛苦的(您必须将所有头文件复制或符号链接到构建目录中)。如果您可以将源文件更改为#include "libfood/comedy/banana/peel.h"
,那么您已经完成了设置。
autoconf并不容易,特别是对于现有的大型项目。但它有其优点。
我想弄清楚同样的事情。对我来说这很有效
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
``