メインの名前を変更するGCCコンパイラ/リンカーオプションはありますか?
-
29-09-2019 - |
質問
私のソフトウェアには、通常の使用には1つのメインと、単体テスト用のメインが異なります。 GCCが使用する「メイン」関数を指定するオプションがあった場合、私はそれが大好きです。
解決
それらを個別のファイルに入れ、通常の使用のために1つの.cファイルを指定し、テスト用の1つの.cファイルを指定します。
代わりに、 #define
テストビルドを使用してコマンドラインでテストし、次のようなものを使用します。
int main(int argc, char *argv[])
{
#ifdef TESTING
return TestMain(argc, argv);
#else
return NormalMain(argc, argv);
#endif
}
int TestMain(int argc, char *argv[])
{
// Do testing in here
}
int NormalMain(int argc, char *argv[])
{
//Do normal stuff in here
}
他のヒント
ここでの他の答えは非常に合理的ですが、厳密に言えば、あなたが抱えている問題は、GCCではなく、むしろCランタイムでの問題です。を使用してプログラムへのエントリポイントを指定できます -e
フラグに ld
. 。私のドキュメントは言っています:
-e symbol_name
メイン実行可能ファイルのエントリポイントを指定します。デフォルトでは、エントリ名は「start」です。これは、crt1.oにあります。
つまり、必要に応じてエントリポイントをオーバーライドできますが、マシンで正常に実行する予定のCプログラムのためにそれをしたくないかもしれません。 start
プログラムが実行される前に必要なあらゆる種類のOS固有のものを実行する場合があります。あなたがあなた自身を実装できるなら start
, 、あなたはあなたが望むことをすることができます。
私はあなたがメーカーか似たようなものを使用していると仮定しています。メイン関数の異なる実装を含む2つのファイルを作成し、MakeFileでは、1つが「ユニットテストメイン」を使用し、もう1つの「通常のメイン」を使用して、ファイルの残りの部分に同一の依存関係を持つ2つの別々のターゲットを定義します。 "。このようなもの:
normal: main_normal.c file1.c file2.c
unittest: main_unittest.c file1.c file2.c
「通常の」ターゲットがMakeFileの上部に近い限り、「Make」を入力するとデフォルトで選択します。テストターゲットを構築するには、「UNITSTest」と入力する必要があります。
マクロを使用して、1つの関数をメインに変更できます。
#ifdef TESTING
#define test_main main
#else
#define real_main main
#endif
int test_main( int argc, char *argv[] ) { ... }
int real_main( int argc, char *argv[] ) { ... }
私は異なるファイルを使用してテストと生産のビルドを作成する傾向がありますが、あなたがでファイルを持っていた場合
int test_main (int argc, char*argv[])
と
int prod_main (int argc, char*argv[])
次に、メインのようにどちらかを選択するコンパイラオプションが -Dtest_main=main
と -Dprod_main=main
今日も同じ問題がありました。M1.CとM2.Cはどちらも主要な機能がありましたが、リンクしてそのうちの1つを実行する必要がありました。解決策:編集後にリンクする前に、メインシンボルをそのうちの1つから削除するユーザーストリップ:
gcc -c m1.c m2.c; strip --strip-symbol main m1.o; gcc m1.o m2.o; ./a.out
M2からメインを実行します
gcc -c m1.c m2.c; strip --strip-symbol main m2.o; gcc m1.o m2.o; ./a.out
M1からメインを実行します
ストリップなし:
gcc - m1.c m2.c
m2.o: In function `main':
m2.c:(.text+0x0): multiple definition of `main'
m1.o:m1.c:(.text+0x0): first defined here
collect2: ld returned 1 exit status
編集: ビリーは私を答えに打ち負かしましたが、ここにもう少し背景があります
より直接的に、メインは通常、標準ライブラリの機能です。呼ぶもの main
cではなく、標準的なライブラリです。 OSはアプリケーションをロードし、制御をライブラリのエントリポイントに転送します(_start
GCC)、およびライブラリは最終的に呼び出します main
. 。これが、Windowsアプリケーションのエントリポイントができる理由です WinMain
, 、通常ではありません。埋め込まれたプログラミングには、同じ種類のものを持つことができます。標準的なライブラリがない場合は、ライブラリが通常提供するエントリポイントを(とりわけ)書く必要があります。
GCCツールチェーンでは、ライブラリのエントリポイントを使用して自分のエントリポイントに置き換えることもできます。 -e
オプション。 (その点については、ライブラリを完全に削除することもできます。)
あなた自身を作る:
int main(int argc, char *argv[])
{
#if defined(BUILD_UNIT_TESTS)
return main_unittest(argc, argv);
#endif
#if defined(BUILD_RUNTIME)
return main_run(argc, argv);
#endif
}
気に入らない場合 ifdef
, 、メインのみを含む2つのメインモジュールを書きます。単体テストのために1つをリンクし、もう1つは通常の使用の場合はリンクします。
君 五月 実行する必要があります ld
それらを別々に使用しますが、LDはサポートしています スクリプト 出力ファイルの多くの側面を定義するには(エントリポイントを含む)。
まず第一に、あなたは2つの機能という名前を持つことはできません main
1つのコンピレーションでは、ソースが異なるファイルにあるか、条件付きコンパイルを使用しています。いずれにせよ、2つの異なる.oファイルを取得する必要があります。したがって、リンカーは必要ありません オプション;必要な.oファイルを渡すだけです 口論.
あなたがそれを気に入らないなら、あなたは派手なことをすることができます dlopen()
引っ張る main
任意のオブジェクトファイルから動的に名前を付けます。これが役立つ可能性のある状況を想像できます。たとえば、ユニットテストに対して体系的なアプローチを取って、すべてをディレクトリに入れて、コードをディレクトリに留め、各オブジェクトファイルをつかみ、動的にロードし、テストを実行します。しかし、始めるためには、おそらくより簡単なことが示されています。
に使用する場合 LD "-e symbol_name"
(もちろん、Symbol_nameがメイン関数です)あなたも必要です "-nostartfiles"
それ以外の場合はエラー "undefined reference to main"
生産されます。
#ifdef TESTING
int main()
{
/* testing code here */
}
#else
int main()
{
/* normal code here */
}
#endif
$ gcc -DTESTING=1 -o a.out filename.c #building for testing
$ gcc -UTESTING -o a.out filename.c #building for normal purposes
man gcc
-dと-uを見せてくれました