15

My software has one main for normal use and a different one for unit tests. I would just love it if there was an option to gcc to specify which "main" function to use.

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Arthur Ulfeldt
  • 90,827
  • 27
  • 201
  • 284
  • Can this be done with one compiler pass? ie without "make -D TESTING; make clean; make"? I get some comfort in shipping the "same code" that I tested. – Arthur Ulfeldt Jun 22 '10 at 23:31
  • You only need the '-D' for the file that contains your mains. I'd have one makefile that builds everything, including the main file twice (once with and once without -D... note that it has to be compiled with two different output file names). Then link it all together twice: once for the testing build, once for the normal build. – mschaef Jun 22 '10 at 23:34
  • this question predates that question and has answers that are more specific to this use. The accepted answer is the better choice for this question than the answer to that question. (and both of these questions are ancient) I'd like to unlink them. – Arthur Ulfeldt Oct 31 '19 at 20:35

11 Answers11

15

The other answers here are quite reasonable, but strictly speaking the problem you have is not really one with GCC, but rather with the C runtime. You can specify an entry point to your program using the -e flag to ld. My documentation says:

-e symbol_name

Specifies the entry point of a main executable. By default the entry name is "start" which is found in crt1.o which contains the glue code need to set up and call main().

That means you can override the entry point if you like, but you may not want to do that for a C program you intend to run normally on your machine, since start might do all kinds of OS specific stuff that's required before your program runs. If you can implement your own start, you could do what you want.

Carl Norum
  • 219,201
  • 40
  • 422
  • 469
  • 4
    On Linux: 1) you also need `-nostartfiles` for GCC, or else `main` in `crt1.o` will be undefined and it won't link 2) You must exit with an explicit `exit()`, or else the `return` to nowhere will segfault 3) `argv` won't be setup for you – Ciro Santilli OurBigBook.com Jul 13 '15 at 14:12
9

Put them in separate files, and specify one .c file for normal use, and one .c file for testing.

Alternately, #define testing on the commandline using test builds and use something like:

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
}
Billy ONeal
  • 104,103
  • 58
  • 317
  • 552
8

You can use macros to rename one function to 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[] ) { ... }
drawnonward
  • 53,459
  • 16
  • 107
  • 112
7

I'm assuming that you're using Make or something similar. I would create two files that contain different implementations of the main function, then in the makefile, define two separate targets that have identical dependencies on the rest of your files, except one uses your "unit test main" and the other your "normal main". Something like this:

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

As long as the "normal" target is closer to the top of the makefile, then typing "make" will choose it by default. You would have to type "make unittest" to build your test target.

Paul
  • 2,698
  • 22
  • 27
  • 4
    +1: I vastly prefer this approach to trying to cram both mains into the same main routine and have preprocessor defines or linker options switch between them. – Owen S. Jun 23 '10 at 00:15
6

I'd tend to use different files and make for testing and production builds, but if you did have a file with

int test_main (int argc, char*argv[])

and

int prod_main (int argc, char*argv[])

then the compiler options to select one or the other as the main are -Dtest_main=main and -Dprod_main=main

Pete Kirkham
  • 48,893
  • 5
  • 92
  • 171
  • 2
    Maybe worth pointing out that if you can't rename the main in the production file for some reason (as happened in my case), you can swap the symbol names in the -D switch, i.e. make it `-Dmain=ignored_main` when compiling the production main. – tomger May 06 '11 at 14:48
5

I had the same problem today: m1.c and m2.c both had a main function but needed to be linked and run one of them. Solution: user STRIP to remove the main symbol from one of them after compilation but before linking:

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

will run main from m2

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

will run main from m1

Without strip:

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
jand
  • 51
  • 1
  • 2
2

Edit: Billy beat me to the answer, but here's a bit more background

More directly, main is usually more a function of the standard library. The thing that calls main isn't C, but rather the standard library. The OS loads the application, transfers control to the library's entry point (_start in GCC), and the library ultimately calls main. This is why the entry point for a windows application can be WinMain, and not the usual. Embedded programming can have the same sort of thing. If you don't have a standard library, you have to write the entry point that the library normally provides (among other things), and you can name it whatever you want.

In the GCC toolchain, you can also replace the library's entry point with your own by using the -e option. (For that matter, you can also remove the library entirely.)


Make your own:

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
}

If you don't like ifdef, then write two main modules that contain only main. Link one in for unit tests and the other in for normal use.

mschaef
  • 1,620
  • 10
  • 12
1

If you use into LD "-e symbol_name" (where symbol_name is your main function, of course) you need also "-nostartfiles" otherwise error of "undefined reference to main" will be produced.

Piotr Sobiegraj
  • 1,775
  • 16
  • 26
Disorder75
  • 11
  • 1
  • 3
    I don't think this will help with OP's problem. Bypassing the start files will leave you with a broken environment in which the standard library will not work properly. I think just `-Dmain` and similar is what's needed. – R.. GitHub STOP HELPING ICE Oct 01 '12 at 19:00
0
#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 showed me the -D and -U

Delgan
  • 18,571
  • 11
  • 90
  • 141
vpit3833
  • 7,817
  • 2
  • 25
  • 25
  • 1
    The only downside of this is that you need to have the entire code of `main` inside a `define`... some syntax coloring of some IDEs will grey out all the testing code (since it's not normally defined) which can become quite annoying. – Billy ONeal Jun 22 '10 at 23:30
0

You may have to run ld separately to use them, but ld supports scripts to define many aspects of the output file (including the entry point).

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
0

First of all, you can't have two functions named main in one compilation, so either the sources are in different files or you're using conditional compilation. Either way you've got to get two different .o files out. Therefore, you don't need a linker option; you just pass the .o file that you want in as an argument.

If you don't like that, you can do fancy things with dlopen() to pull main from any object file you name dynamically. I can imagine circumstances where this might be useful—say, you take a systematic approach to unit tests, just put them all in a directory, and your code walks the directory, grabbing each object file, dynamically loading it, and running its tests. But to get started, something simpler is probably indicated.

Norman Ramsey
  • 198,648
  • 61
  • 360
  • 533