すべてのオブジェクトファイルを個別のディレクトリに配置する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ディレクティブを介してMakefileを作成できることを知っています。
しかし、これは複雑なビルド環境では多くの作業です。
使用することもできます
gcc -c file1.c file2.c file3.c
しかし、このアプローチを使用すると、srcdirに.oガベージがいっぱいになります。
だから--outdirのセマンティクスを持つオプションは非常に便利だと思います。
あなたの意見はどうですか?
編集:Makefileは、.oファイルが実際にbuilddir / objに配置されるように作成されます。しかし、私は単にもっと良いアプローチがあるのではないかと思っています。
編集:ビルドシステム(別名Make、CMakeなど)に望ましい動作を実現するための負担をかけるいくつかのアプローチがあります。しかし、これらはすべて、gcc(および他のコンパイラも)の弱点に対する回避策であると考えています。
解決
これは、私のプロジェクトの1つで切り取られたメイクファイルで、「src」のソースをコンパイルし、ディレクトリ<!> quot; obj <!> quot;に.oファイルを配置します。重要な点は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
コンパイルが完了した後のまたは soft-clean (おそらく再帰的)
rm -f *.o
または
find . -name \*.o -exec rm {} \;
必要なgcc
オプションを生成し、-o
を呼び出すgcc_wrap
の簡単なラッパーを使用できます:
$ ./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
以下に、最も単純な形式のMakefiles
スクリプトを示します。
#!/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: $!");
}
もちろん、標準的な方法はCMake
、またはより単純な、bakefile
または<=>を使用して問題を解決することですが、<=>に機能を追加するソリューションを具体的に求めました。唯一の方法は、そのようなラッパーを書くことです。もちろん、新しいオプションを含めるために<=>ソースをパッチすることもできますが、それは難しいかもしれません。
あなたはコンセプトを後方に持っていると思います...?!
Makefileの背後にある考え方は、(再)コンパイル時間を削減するために、最後のビルド以降に更新されたファイルのみを処理するということです。 1回のコンパイラーの実行で複数のファイルをまとめて束ねると、基本的にその目的に反します。
あなたの例:
gcc -c file1.c file2.c file3.c **--outdir=**../builddir/objdir
このコマンドラインで使用する「make」ルールを指定しませんでした。ただし、3つのファイルの が更新されている場合は、この行を実行し、 3つすべてのファイルを再コンパイルする必要があります。また、個別のコンパイル(「-j」オプションを使用する場合は強くお勧めします)の場合と同様に、「make」が各ソースファイルに対して個別のコンパイルプロセスを生成しないようにします。
Makefileチュートリアルを他の場所で作成しました。ソースファイルをMakefileにハードコードする代わりに、ソースファイルを依存関係に自動決定し、インラインテストします。
個別のオブジェクトディレクトリを取得するために必要な作業は、適切なディレクトリ情報をそのチュートリアルのOBJFILES :=
行と%.o: %.c Makefile
ルールに追加することだけです。 Neil Butterworthの答えには、ディレクトリ情報を追加する方法の良い例があります。
(チュートリアルで説明されているようにDEPFILESまたはTESTFILESを使用する場合は、DEPFILES :=
およびTSTFILES :=
行に加えて%.t: %.c Makefile pdclib.a
ルールも適合させる必要があります。)
pass gccにオブジェクトファイルを配置する場所を指定する別のオプションはありません。 <!> quot; -c <!> quot; -オブジェクトを配置するディレクトリを指定します。
ディレクトリのみに追加フラグを設定すると、<!> quot; -c <!> quot;の意味を変更する必要があります。 例:
gcc -c file.c -o /a/b/c/file.o --put-object-in-dir-non-existing-option /a1/a2/a3
両方のパスが絶対パスであるため、/ a1 / b2 / c / file.oを/ a1 / a2 / a3の下に配置することはできません。したがって、<!> quot; -c <!> quot;オブジェクトファイル名のみに変更する必要があります。
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を使用していますが、非常に便利です。依存関係を自動的に生成し、他にも便利な機能があります。
その間に<!> quot; half-way <!> quot;を見つけました。 -combineオプションを使用した解決策。
例:
mkdir builddir
mkdir builddir/objdir
cd srcdir
gcc -combine -c file1.c file2.c file3.c -o ../builddir/objdir/all-in-one.o
this <!> quot; combines <!> quot;すべてのソースファイルを単一のオブジェクトファイルに。
ただし、これはまだ<!> quot; half-way <!> quot;です。 1つのソースファイルのみが変更された場合、すべてを再コンパイルする必要があるためです。
これは、 autoconf が解決する問題の1つです。
これまでに./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
``