我的软件有一个用于正常使用的主要软件和一个用于单元测试的不同软件。如果 gcc 有一个选项来指定要使用的“主”函数,我会很高兴的。
11 回答
这里的其他答案很合理,但严格来说,你遇到的问题并不是 GCC,而是 C 运行时。您可以使用 to-e
标志指定程序的入口点ld
。我的文档说:
-e symbol_name
指定主可执行文件的入口点。默认情况下,条目名称为“start”,可在 crt1.o 中找到,其中包含需要设置和调用 main() 的胶水代码。
这意味着您可以根据需要覆盖入口点,但您可能不想为打算在您的机器上正常运行的 C 程序执行此操作,因为start
可能会在程序运行之前执行各种特定于操作系统的操作。如果你可以实现你自己的start
,你可以做你想做的。
将它们放在单独的文件中,并指定一个.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
}
您可以使用宏将一个函数重命名为 main。
#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[] ) { ... }
我假设您正在使用 Make 或类似的东西。我将创建两个包含 main 函数的不同实现的文件,然后在 makefile 中,定义两个独立的目标,它们对其余文件具有相同的依赖关系,除了一个使用您的“单元测试 main”,另一个使用您的“普通 main” ”。像这样的东西:
normal: main_normal.c file1.c file2.c
unittest: main_unittest.c file1.c file2.c
只要“正常”目标更靠近 makefile 的顶部,然后键入“make”将默认选择它。您必须输入“make unittest”来构建您的测试目标。
我倾向于使用不同的文件并进行测试和生产构建,但如果你确实有一个文件
int test_main (int argc, char*argv[])
和
int prod_main (int argc, char*argv[])
然后编译器选项选择一个或另一个作为主要是-Dtest_main=main
和-Dprod_main=main
我今天遇到了同样的问题:m1.c 和 m2.c 都有一个 main 函数,但需要链接并运行其中一个。解决方案:用户 STRIP 在编译后但在链接之前从其中一个中删除主符号:
gcc -c m1.c m2.c; strip --strip-symbol main m1.o; gcc m1.o m2.o; ./a.out
将从 m2 运行 main
gcc -c m1.c m2.c; strip --strip-symbol main m2.o; gcc m1.o m2.o; ./a.out
将从 m1 运行 main
无带:
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 通常更多的是标准库的函数。调用main
的不是 C,而是标准库。操作系统加载应用程序,将控制权转移到库的入口点(_start
在 GCC 中),库最终调用main
. 这就是为什么 Windows 应用程序的入口点可以是WinMain
,而不是通常的。嵌入式编程可以有同样的东西。如果您没有标准库,则必须编写该库通常提供的入口点(除其他外),您可以随意命名。
-e
在 GCC 工具链中,您还可以使用该选项将库的入口点替换为您自己的入口点。(就此而言,您也可以完全删除该库。)
制作你自己的:
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
,那么编写两个只包含 main 的主要模块。链接一个用于单元测试,另一个用于正常使用。
如果你使用 into LD "-e symbol_name"
(其中 symbol_name 是你的主要功能,当然)你也需要"-nostartfiles"
否则"undefined reference to main"
会产生错误。
首先,您不能main
在一个编译中命名两个函数,因此源代码位于不同的文件中,或者您正在使用条件编译。无论哪种方式,您都必须获得两个不同的 .o 文件。因此,您不需要链接器选项;您只需将所需的 .o 文件作为参数传递。
如果你不喜欢这样,你可以做一些花哨的事情dlopen()
来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
您可能必须ld
单独运行才能使用它们,但 ld 支持脚本来定义输出文件的许多方面(包括入口点)。