cmakefilecunit

Multiple definition of 'main' first defined here when using CUnit tests


I am new at using CUnit and writing a C program. I have this error printing when I run make and then make test :

/usr/bin/ld: test_errors.o: in function 'main':
test_errors.c:(.text+0x35): multiple definition of `main'; motcache.o:motcache.c:(.text+0x0): first defined here
/usr/bin/ld: test_file_handling.o: in function `main':
test_file_handling.c:(.text+0x96): multiple definition of `main'; motcache.o:motcache.c:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status
make: *** [Makefile:21: link_test] Error 1

I have this in my Makefile :

CC = gcc
OPTIONS = -Wall -Wextra
EXE = motcache
SRC_DIR = src
TESTS_DIR = test

all : start

compile :
    $(CC) $(OPTIONS) -c $(SRC_DIR)/*.c

test: run_test

link: compile
    $(CC) *.o -o $(EXE)

compile_test :
    $(CC) $(OPTIONS) -c $(TESTS_DIR)/*.c

link_test : compile_test
    $(CC) $(OPTIONS) *.o -o cunit_tests -lcunit

run_test: link_test
    ./cunit_tests

clean:
    rm -f $(EXE) *.o cunit_tests

start: link
    ./$(EXE)

This is my test_file_handling.c that test a function that simply open a file :

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include "CUnit/CUnit.h"
#include "CUnit/Basic.h"
#include "../src/file_handling.h"

void test_open_file_success(void){
    char *argv[] = {"./motcache", "mandoline.txt"};
    FILE *fptr = open_file(argv);
    CU_ASSERT_PTR_NOT_NULL(fptr);
    fclose(fptr);
}

int main(){
    CU_initialize_registry();

    CU_pSuite suite = CU_add_suite("Ouverture de fichiers", NULL, NULL);
    CU_add_test(suite, "Test d'ouverture de fichier avec succès", test_open_file_success);

    CU_basic_set_mode(CU_BRM_VERBOSE);
    CU_basic_run_tests();
    CU_cleanup_registry();

    return 0;
}

And this is my main file motcache.c :

#include <stdio.h>
#include "motcache.h"
#include "errors.h"
#include "file_handling.h"

int main(){
   return 0;
}

I am struggling to solve this issue since I am still struggling to understand. Is it not allowed to have a main method in each of my functions? My code works and the test work fine if the methods don't have the same name. I would be glad to take any advice or help in this issue. Thank you


Solution

  • I am struggling to solve this issue since I am still struggling to understand. Is it not allowed to have a main method in each of my functions?

    C does not have methods, only functions. C source files are a unit of organization, but they do not correspond to anything much like what in C++ or Java is called a "method", much less a "class". In particular, they do not establish namespaces for functions or variable. C has only one, global, namespace for "ordinary identifiers", which include function and variable names, among other things.

    So although yes, you can have main() functions in multiple files, you cannot have multiple main() functions in the in the same program. Therefore, if you have two object files, each built from a source file that contains a main() function, you cannot link them together. And that's exactly what you're trying to do.

    Perhaps you thought that you were building program cunit_tests from only the sources in $(TESTS_DIR)/. I suspect that's not feasible if the tests are actually to do any useful testing, but in any case, it's not what the rules you have written produce. All the object files are being built in the top-level directory, and then all of them are picked up by the *.o glob in your link command(s).

    Part of the problem is that you've written your makefile much like a procedural program. If that's what you want, then writing directly in shell script is more flexible for that purpose. You would be giving up all the features that make make such a long-lived and widely used tool, but you're not actually using any of those now, anyway. And shell scripts are what people used before make.

    Or you could play to make's strengths by:

    That would also set you up to benefit from some of make's convenience features, especially its built-in rules. That might look something like this:

    # Prefer conventional names for flags variables
    CFLAGS = -Wall -Wextra
    
    EXE = motcache
    TEST_EXE = cunit_tests
    SRC_DIR = src
    TESTS_DIR = test
    
    # All object files contributing to the primary executable only:
    EXE_OBJS = $(SRC_DIR)/motcache.o  ... and maybe others ...
    
    # All object files contributing to the tests only:
    TEST_OBJS = $(TESTS_DIR)/test_file_handling.o  ... and probably others ...
    
    # All object files contributing to both the primary executable and the tests
    SHARED_OBJS = .. probably some $(SRC_DIR)/foo.o ...
    
    all: $(EXE)
    
    $(EXE): $(SHARED_OBJS) $(EXE_OBJS)
            $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^
    
    $(TEST_EXE): $(SHARED_OBJS) $(TEST_OBJS)
            $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ -lcunit
    
    run_test: $(TEST_EXE)
            ./$(TEST_EXE)
    
    clean:
            rm -f $(TEST_EXE) $(EXE) $(TEST_OBJS) $(SHARED_OBJS) $(OBJS)
    
    .PHONY: all clean run_test
    

    It's up to you to fill out the values of the *_OBJS variables appropriately. And at least until you are more familiar with make, do follow the example and specify the object files as in the same directories as their corresponding sources.

    If you were building multiple separate test executables then you would want to modify that a bit, but that is left as an exercise.

    Overall, that gets you:

    Not to mention, a pretty clear path toward actual working builds.