すべてのオブジェクトファイルを個別のディレクトリに配置するgcc / g ++オプション

StackOverflow https://stackoverflow.com/questions/1814270

  •  06-07-2019
  •  | 
  •  

質問

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
``
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top