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
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:
make
s built-in rule for building object files from .c source filesNot to mention, a pretty clear path toward actual working builds.