هل يوجد خيار برنامج التحويل البرمجي/رابط GCC لتغيير اسم Main؟

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

  •  29-09-2019
  •  | 
  •  

سؤال

يمتلك برامجتي واحدة رئيسية للاستخدام العادي وذات مختلف لاختبارات الوحدة. أحب ذلك فقط إذا كان هناك خيار لـ GCC لتحديد الوظيفة "الرئيسية" التي يجب استخدامها.

هل كانت مفيدة؟

المحلول

ضعها في ملفات منفصلة ، وحدد ملف .c واحد للاستخدام العادي ، وملف .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
}

نصائح أخرى

الإجابات الأخرى هنا معقولة للغاية ، ولكن بشكل صارم أن المشكلة التي تواجهها ليست حقًا مع مجلس التعاون الخليجي ، بل مع وقت التشغيل C. يمكنك تحديد نقطة إدخال لبرنامجك باستخدام -e العلم إلى ld. توثيقي تقول:

-e symbol_name

يحدد نقطة دخول أداة تنفيذ رئيسية. بشكل افتراضي ، يكون اسم الإدخال "ابدأ" موجودًا في CRT1.O والذي يحتوي على رمز الغراء الذي يحتاج إلى إعداد واتصال MAIN ().

هذا يعني أنه يمكنك تجاوز نقطة الدخول إذا أردت ، ولكن قد لا ترغب في القيام بذلك لبرنامج C الذي تنوي تشغيله بشكل طبيعي على جهازك ، منذ ذلك الحين start قد تفعل جميع أنواع الأشياء المحددة لنظام التشغيل المطلوب قبل تشغيل البرنامج. إذا كنت تستطيع تنفيذ خاصتك start, ، يمكنك أن تفعل ما تريد.

أفترض أنك تستخدم Make أو شيء مشابه. أود إنشاء ملفين يحتويان على تطبيقات مختلفة للوظيفة الرئيسية ، ثم في Makefile ، حدد هدفين منفصلين لهما تبعيات متطابقة على بقية ملفاتك ، باستثناء واحد يستخدم "اختبار الوحدة الرئيسي" والآخر "الرئيسي العادي الخاص بك" ". شيء من هذا القبيل:

normal: main_normal.c file1.c file2.c
unittest: main_unittest.c file1.c file2.c

طالما أن الهدف "الطبيعي" هو أقرب إلى الجزء العلوي من Makefile ، فإن كتابة "Make" ستختاره افتراضيًا. سيكون عليك كتابة "جعل" غير متطرف "لبناء هدف الاختبار الخاص بك.

يمكنك استخدام وحدات الماكرو لإعادة تسمية وظيفة واحدة إلى الرئيسية.

#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 وظيفة رئيسية ولكن يجب ربطها وتشغيل واحدة منها. الحل: شريط المستخدم لإزالة الرمز الرئيسي من أحدهم بعد التجميع ولكن قبل الربط:

gcc -c m1.c m2.c;  strip --strip-symbol main m1.o; gcc m1.o m2.o; ./a.out

سيتم تشغيل MAIN من M2

gcc -c m1.c m2.c;  strip --strip-symbol main m2.o; gcc m1.o m2.o; ./a.out

سيتم تشغيل MAIN من 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 ، بل المكتبة القياسية. يقوم نظام التشغيل بتحميل التطبيق ، ويتحكم في نقطة دخول المكتبة (_start في مجلس التعاون الخليجي) ، والمكتبة في نهاية المطاف 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, ، ثم اكتب وحدتين رئيسيتين يحتويان على الرئيسية فقط. ربط واحد في اختبارات الوحدة والآخر للاستخدام العادي.

أنت مايو يجب الجري ld بشكل منفصل لاستخدامها ، لكن LD يدعم نصوص لتحديد العديد من جوانب ملف الإخراج (بما في ذلك نقطة الدخول).

بادئ ذي بدء ، لا يمكنك أن تسمى وظيفتين main في مجموعة واحدة ، لذلك إما المصادر في ملفات مختلفة أو كنت تستخدم التجميع الشرطي. في كلتا الحالتين ، يجب عليك الحصول على ملفين مختلفين. لذلك ، لا تحتاج إلى رابط اختيار; ؛ أنت فقط تمرير ملف .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

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top