生成されたオブジェクトファイルを指定されたディレクトリに配置するオプションが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のセマンティクスを持つオプションは非常に便利だと思います。
あなたの意見は何ですか?
[〜#〜] edit [〜#〜]:Makefileは、.oファイルが実際にbuilddir/objに配置されるように記述されています。しかし、もっと良いアプローチがあるのではないかと思っています。
[〜#〜] edit [〜#〜]:ビルドシステム(別名Make、CMakeなど)に望ましい動作を実現するための負担をかけるアプローチがいくつかあります。しかし、私はそれらをすべて回避策と見なします。これはgcc(および他のコンパイラも)の弱点です。
これは、私のプロジェクトの1つで切り取られたメイクファイルで、ソースを「src」にコンパイルし、.oファイルをディレクトリ「obj」に配置します。重要な点はpatsubst()関数の使用です-詳細についてはGNU make manual(実際にはかなり読みやすい)を参照してください:
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
を呼び出すことができます。
$ ./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の背後にある考え方は、(再)コンパイル時間を削減するために、最後のビルド以降に更新されたファイルのみを処理するということです。 1回のコンパイラー実行で複数のファイルをまとめて束ねると、基本的にその目的に反します。
あなたの例:
gcc -c file1.c file2.c file3.c **--outdir=**../builddir/objdir
このコマンドラインに対応する「make」ルールを指定しませんでした。ただし、3つのファイルのanyが更新された場合、この行を実行し、3つすべてのファイルを再コンパイルする必要がありますファイル。これはまったく必要ない場合があります。また、個別のコンパイルの場合と同様に、「make」が各ソースファイルの個別のコンパイルプロセスを生成しないようにします(「-j」オプションを使用する場合は強くお勧めします)。
私は Makefileチュートリアル を書きました。これはいくつかの追加の詳細になります(Makefileでハードコードされる代わりにソースファイルを自動検出する、インクルード依存関係を自動決定する、インラインテストなど) 。
別のオブジェクトディレクトリを取得するために必要な作業は、適切なディレクトリ情報をそのチュートリアルのOBJFILES :=
行と%.o: %.c Makefile
ルールに追加することだけです。 Neil Butterworthの答えには、ディレクトリ情報を追加する方法のいい例があります。
(チュートリアルの説明に従ってDEPFILESまたはTESTFILESを使用する場合は、DEPFILES :=
およびTSTFILES :=
行に加えて%.t: %.c Makefile pdclib.a
ルールも適合させる必要があります。)
一方、-combineオプションを使用して、「中途半端な」ソリューションを見つけました。
例:
mkdir builddir
mkdir builddir/objdir
cd srcdir
gcc -combine -c file1.c file2.c file3.c -o ../builddir/objdir/all-in-one.o
これにより、すべてのソースファイルが1つのオブジェクトファイルに「結合」されます。
ただし、1つのソースファイルのみが変更されたときにすべてを再コンパイルする必要があるため、これはまだ「途中」です。
パスgccに伝えることは、オブジェクトファイルを配置する場所を指定するための別個のオプションがないと思います。 「-c」です-オブジェクトを置くディレクトリを指定します。
ディレクトリのみに追加フラグを設定すると、「-c」の意味を変更する必要があります。例えば:
gcc -c file.c -o /a/b/c/file.o --put-object-in-dir-non-existing-option /a1/a2/a3
両方のパスが絶対パスであるため、/ a1/a2/a3の下に/a/b/c/file.oを配置することはできません。したがって、「-c」はオブジェクトファイルのみを指定するように変更する必要があります。
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を使用していますが、非常に便利です。自動的に依存関係を生成し、他の利点があります。
私は同じことを理解しようとしています。私にとってこれはうまくいきました
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
``
これは問題の1つです autoconf が解決します。
これまでに./configure && make
autoconfが何であるかを知っています。これは、これらのNice構成スクリプトを生成するツールです。誰もが知っているわけではないのは、代わりにmkdir mybuild && cd mybuild && ../configure && make
そしてautoconfはそのように素晴らしいので、それは魔法のように動作します。
configure
スクリプトは、ビルドディレクトリにMakefileを生成します。その後、ビルドプロセス全体がそこで行われます。したがって、すべてのビルドファイルは、ソースツリーではなく、当然そこに表示されます。
ソースファイルに#include "../banana/peel.h"
そして、あなたはそれらを変更することができません、そして、それはこの仕事を正しくするのが苦痛です(すべてのヘッダファイルをビルドディレクトリにコピーするかシンボリックリンクしなければなりません)。ソースファイルを変更して#include "libfood/comedy/banana/peel.h"
代わりに、設定はすべて完了です。
特に大規模な既存プロジェクトでは、autoconfは正確にeasyではありません。しかし、それには利点があります。
個人的には単一のファイルに対してこれを行いますが、
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を使用して上記の回答を好むでしょう。