2010-06-22 17 views
15

Mon logiciel a un principal pour un usage normal et un autre pour les tests unitaires. Je l'adorerais s'il y avait une option à gcc pour spécifier quelle fonction "principale" utiliser. Mettez-les dans des fichiers séparés, et spécifiez un fichier .c pour une utilisation normale et un fichier .c pour le test.existe-t-il une option compilateur/éditeur de liens GCC pour changer le nom de main?

+0

Peut-il être fait avec un passage de compilateur? c'est à dire sans "make -D TESTING; make clean; make"? Je reçois un certain confort en expédiant le "même code" que j'ai testé. –

+0

Vous avez seulement besoin du '-D' pour le fichier qui contient votre secteur. J'aurais un makefile qui construit tout, y compris le fichier principal deux fois (une fois avec et une fois sans -D ... notez qu'il doit être compilé avec deux noms de fichiers de sortie différents). Ensuite, reliez tous ensemble deux fois: une fois pour la construction de test, une fois pour la construction normale. – mschaef

Répondre

8

Alternativement, #define tests sur la ligne de commande en utilisant le test builds et utiliser quelque chose comme:

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 
} 
+7

Aussi, juste FYI à l'OP, ajoutez simplement '-DTESTING' à la liste des arguments à gcc. –

+0

préférable d'utiliser -e pour le point d'entrée ou la bande --strip-symbole –

+0

@Alex: "vous ne pouvez pas supprimer une réponse acceptée" :( –

2

Edit: Billy m'a battu à la réponse, mais voici un peu plus fond

Plus directement, Main est généralement plus une fonction de la bibliothèque standard. La chose qui appelle main n'est pas C, mais plutôt la bibliothèque standard. Le système d'exploitation charge l'application, transfère le contrôle au point d'entrée de la bibliothèque (_start dans GCC) et la bibliothèque appelle finalement main. C'est pourquoi le point d'entrée pour une application Windows peut être WinMain, et pas l'habituel. La programmation embarquée peut avoir le même genre de chose. Si vous n'avez pas de bibliothèque standard, vous devez écrire le point d'entrée que la bibliothèque fournit normalement (entre autres choses), et vous pouvez le nommer comme vous voulez.

Dans la chaîne d'outils GCC, vous pouvez également remplacer le point d'entrée de la bibliothèque par le vôtre en utilisant l'option -e. (Pour cette question, vous pouvez également supprimer la bibliothèque entièrement.)


Faites votre propre:

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 
} 

Si vous ne l'aimez pas ifdef, puis écrivez deux modules principaux qui ne contiennent que principale. Liez un pour les tests unitaires et l'autre pour un usage normal.

7

Je suppose que vous utilisez Make ou quelque chose de similaire. Je créerais deux fichiers qui contiennent différentes implémentations de la fonction principale, puis dans le makefile, définir deux cibles séparées qui ont des dépendances identiques sur le reste de vos fichiers, sauf que l'un utilise votre "unité principale test" et l'autre ". Quelque chose comme ceci:

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

comme cible « normale » est plus proche du haut du makefile, puis en tapant « make » choisira par défaut. Vous devrez taper "make unittest" pour construire votre cible de test.

+0

construit avec autotools –

+4

+1: Je préfère largement cette approche pour essayer de fourrer les deux principaux dans la même routine principale et que les options du préprocesseur ou de l'éditeur de liens basculent entre eux –

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 m'a montré le -D et -U

+1

Le seul inconvénient de ceci est que vous devez avoir le code entier de 'main' dans un' define' ... une coloration de la syntaxe de certains IDE sera grise sur tout le code de test (puisqu'il n'est pas normalement défini) ce qui peut devenir assez ennuyeux –

7

Vous pouvez utiliser des macros pour renommer une fonction principale.

#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[]) { ... } 
14

Les autres réponses sont ici tout à fait raisonnable, mais à proprement parler du problème que vous avez est pas vraiment un avec GCC, mais plutôt avec le runtime C. Vous pouvez spécifier un point d'entrée à votre programme en utilisant le drapeau -e à ld.Ma documentation dit:

-e symbol_name

Indique le point d'entrée d'un exécutable principal. Par défaut, le nom de l'entrée est "start" qui se trouve dans crt1.o qui contient le code de la colle à configurer et appelle main().

Cela signifie que vous pouvez remplacer le point d'entrée si vous voulez, mais vous ne voulez pas le faire pour un programme C vous avez l'intention d'exécuter normalement sur votre machine, car start peut faire toutes sortes d'OS choses spécifiques qui est requis avant que votre programme s'exécute. Si vous pouvez implémenter votre propre start, vous pouvez faire ce que vous voulez.

+3

Sur Linux: 1) vous avez aussi besoin de '-nostartfiles' pour GCC, sinon' main' dans 'crt1.o' sera indéfini ne liera pas 2) Vous devez quitter avec un 'exit()' explicite, sinon le 'return' to now segfault 3)' argv' ne sera pas configuré pour vous –

0

Vous peut doivent fonctionner ld séparément pour les utiliser, mais ld soutient scripts pour définir de nombreux aspects du fichier de sortie (y compris le point d'entrée).

+0

Mais 'main' n'est pas l'entrée point (au moins sur mo st systèmes). –

5

je tendance à utiliser des fichiers différents et faire pour tester et construit la production, mais si vous avez un fichier avec

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

et

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

alors les options du compilateur pour sélectionner un ou l'autre comme le principal sont -Dtest_main=main et -Dprod_main=main

+1

Il est peut-être intéressant de souligner que si vous ne pouvez pas renommer le fichier principal dans le fichier de production pour une raison quelconque (comme dans mon cas), vous pouvez inverser les noms des symboles dans le commutateur -D, par exemple '-Dmain = ignored_main' lors de la compilation de la production principale. – ttreitlinger

0

Tout d'abord, vous ne pouvez pas avoir deux fonctions nommées main dans un c ompilation, donc les sources sont dans des fichiers différents ou vous utilisez une compilation conditionnelle. De toute façon, vous devez sortir deux fichiers .o différents. Par conséquent, vous n'avez pas besoin d'un éditeur de liens option; vous venez de passer le fichier .o que vous voulez dans un argument .

Si vous n'aimez pas cela, vous pouvez faire des choses fantaisistes avec dlopen() pour tirer main à partir de n'importe quel fichier objet que vous nommez dynamiquement. Je peux imaginer des situations où cela pourrait être utile, par exemple, vous prenez une approche systématique des tests unitaires, les mettez tous dans un répertoire, et votre code parcourt le répertoire, saisit chaque fichier objet, le charge dynamiquement et exécute ses tests. Mais pour commencer, quelque chose de plus simple est probablement indiqué.

0

Si vous utilisez dans LD "-e symbol_name" (où symbol_name est votre fonction principale, bien sûr), vous devez également "-nostartfiles" sinon l'erreur "undefined reference to main" sera produite.

+3

Je ne pense pas que cela aidera le problème d'OP. Contourner les fichiers de démarrage vous laissera avec un environnement brisé dans lequel la bibliothèque standard ne fonctionnera pas correctement. Je pense que juste «-Dmain» et similaire est ce qui est nécessaire. –

5

J'ai eu le même problème aujourd'hui: m1.c et m2.c tous les deux avaient une fonction principale mais avaient besoin d'être liés et de courir l'un d'entre eux. Solution: l'utilisateur STRIP pour enlever le symbole principal d'un d'entre eux après la compilation, mais avant de lier:

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

se déroulera principale de m2

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

se déroulera principale de m1

Sans bande :

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