I ran into a problem when the linker complains about undefined reference to <function>
after compiling all the objects and files, even though functions are declared and defined. I've been researching for my problem for an entire day, but without any success.
Here's my (simplified) Makefile
:
CC := clang
CFLAGS := -g
BIN := a.out
BUILDDIR := ./build/
SRCDIR := ./src/
INCDIR := ./include/
CFLAGS += -I$(INCDIR)
SRCEXCL := $(SRCDIR)file3.c
INCEXCL := $(INCDIR)file3.h
SOURCES := $(filter-out $(SRCEXCL), $(wildcard $(SRCDIR)*.c))
INCLUDE := $(filter-out $(INCEXCL), $(wildcard $(INCDIR)*.h))
OBJECTS := $(addprefix $(BUILDDIR), $(subst $(SRCDIR),, $(SOURCES:.c=.o)))
.PHONY: all
all: $(BIN) run clean
$(BIN): main.c $(OBJECTS)
$(CC) $(CFLAGS) -o $@ $< $(OBJECTS)
$(OBJECTS): | $(BUILDDIR)
$(BUILDDIR)%.o: $(SOURCES) $(INCLUDE) build
$(CC) $(CFLAGS) -c $< -o $@
build:
mkdir -p $(BUILDDIR)
run:
./$(BIN)
clean:
rm -rf $(BUILDDIR) $(BIN)
So I have a couple files that I'd like to be compiled into an executable. Here they are in order:
./include/shared.h
:
#pragma once
#ifndef _DATA_H_
#define _DATA_H_
#define CHECK_BIT(num, pos) ((num) & (pos))
// just some enum
enum bits {
ONE = 1, TWO = 2, THREE = 4,
FOUR = 8, FIVE = 16, SIX = 32,
SEVEN = 64, EIGHT = 128, NINE = 256,
INVALID = -1
};
#endif // _DATA_H_
./include/file1.h
:
#pragma once
#ifndef _FILE1_H_
#define _FILE1_H_
#include <shared.h>
#include <stdint.h>
// just some function
enum bits some_bit_func(uint16_t number);
#endif // _FILE1_H_
./src/file1.c
:
#include <file1.h>
enum bits some_bit_func(uint16_t number)
{
enum bits result;
// 100% valid code here
return result
}
./src/file2.c
:
#include <shared.h>
#include <stdint.h>
uint16_t some_func(uint16_t number)
{
return CHECK_BIT(number, FOUR);
}
main.c
:
#include <file1.h>
#include <stdio.h>
#include <sys/types.h>
extern uint16_t some_func(uint16_t number);
int main(void)
{
size_t arr_size = 3;
uint16_t numbers[] = { 5, 9, 7 };
for (size_t i = 0; i < arr_size; i++) {
printf("some_func(%d):[%d]: %d\n", numbers[i], i, some_func(numbers[i]));
printf("some_bit_func(%d):[%d]: %d\n\n", numbers[i], i, some_bit_func(numbers[i]));
}
return 0;
}
There's also two files I don't want to include in compilation. They're empty actually:
./src/file3.c
:
#include <file3.h>
./include/file3.h
:
#pragma once
#ifndef _FILE3_H_
#define _FILE3_H_
#endif // _FILE3_H_
The error I get after running make
command is:
mkdir -p ./build/
clang -g -I./include/ -c src/file1.c -o build/file1.o
clang -g -I./include/ -c src/file1.c -o build/file2.o
clang -g -I./include/ -o a.out main.c ./build/file1.o ./build/file2.o
/usr/bin/ld: ./build/file2.o: in function `some_bit_func':
/home/rat/dev/some_project/src/file1.c:4: multiple definition of `some_bit_func'; ./build/file1.o:/home/rat/dev/some_project/src/file1.c:4: first defined here
/usr/bin/ld: /tmp/main-6cf008.o: in function `main':
/home/rat/dev/some_project/main.c:13: undefined reference to `some_func'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [Makefile:21: a.out] Error 1
Basically, I'm currently trying to use something like this (Makefile
) in some quite bigger project, but the errors produced by the compiler are the same actually.
I tried a lot of things I could found on the internet, but none of it helped me.
Also, I think it is worth mentioning that it works ONLY if I compile all the objects and link them by hand (without using Makefile
) like this:
mkdir -p build
clang -g -I./include -c src/file1.c -o build/file1.o
clang -g -I./include -c src/file1.c -o build/file2.o
clang -g -I./include -o a.out main.c build/file1.o build/file2.o
I ran into a problem when the linker complains about undefined reference to <function>
after compiling all the objects and files, even though functions are declared and defined. I've been researching for my problem for an entire day, but without any success.
Here's my (simplified) Makefile
:
CC := clang
CFLAGS := -g
BIN := a.out
BUILDDIR := ./build/
SRCDIR := ./src/
INCDIR := ./include/
CFLAGS += -I$(INCDIR)
SRCEXCL := $(SRCDIR)file3.c
INCEXCL := $(INCDIR)file3.h
SOURCES := $(filter-out $(SRCEXCL), $(wildcard $(SRCDIR)*.c))
INCLUDE := $(filter-out $(INCEXCL), $(wildcard $(INCDIR)*.h))
OBJECTS := $(addprefix $(BUILDDIR), $(subst $(SRCDIR),, $(SOURCES:.c=.o)))
.PHONY: all
all: $(BIN) run clean
$(BIN): main.c $(OBJECTS)
$(CC) $(CFLAGS) -o $@ $< $(OBJECTS)
$(OBJECTS): | $(BUILDDIR)
$(BUILDDIR)%.o: $(SOURCES) $(INCLUDE) build
$(CC) $(CFLAGS) -c $< -o $@
build:
mkdir -p $(BUILDDIR)
run:
./$(BIN)
clean:
rm -rf $(BUILDDIR) $(BIN)
So I have a couple files that I'd like to be compiled into an executable. Here they are in order:
./include/shared.h
:
#pragma once
#ifndef _DATA_H_
#define _DATA_H_
#define CHECK_BIT(num, pos) ((num) & (pos))
// just some enum
enum bits {
ONE = 1, TWO = 2, THREE = 4,
FOUR = 8, FIVE = 16, SIX = 32,
SEVEN = 64, EIGHT = 128, NINE = 256,
INVALID = -1
};
#endif // _DATA_H_
./include/file1.h
:
#pragma once
#ifndef _FILE1_H_
#define _FILE1_H_
#include <shared.h>
#include <stdint.h>
// just some function
enum bits some_bit_func(uint16_t number);
#endif // _FILE1_H_
./src/file1.c
:
#include <file1.h>
enum bits some_bit_func(uint16_t number)
{
enum bits result;
// 100% valid code here
return result
}
./src/file2.c
:
#include <shared.h>
#include <stdint.h>
uint16_t some_func(uint16_t number)
{
return CHECK_BIT(number, FOUR);
}
main.c
:
#include <file1.h>
#include <stdio.h>
#include <sys/types.h>
extern uint16_t some_func(uint16_t number);
int main(void)
{
size_t arr_size = 3;
uint16_t numbers[] = { 5, 9, 7 };
for (size_t i = 0; i < arr_size; i++) {
printf("some_func(%d):[%d]: %d\n", numbers[i], i, some_func(numbers[i]));
printf("some_bit_func(%d):[%d]: %d\n\n", numbers[i], i, some_bit_func(numbers[i]));
}
return 0;
}
There's also two files I don't want to include in compilation. They're empty actually:
./src/file3.c
:
#include <file3.h>
./include/file3.h
:
#pragma once
#ifndef _FILE3_H_
#define _FILE3_H_
#endif // _FILE3_H_
The error I get after running make
command is:
mkdir -p ./build/
clang -g -I./include/ -c src/file1.c -o build/file1.o
clang -g -I./include/ -c src/file1.c -o build/file2.o
clang -g -I./include/ -o a.out main.c ./build/file1.o ./build/file2.o
/usr/bin/ld: ./build/file2.o: in function `some_bit_func':
/home/rat/dev/some_project/src/file1.c:4: multiple definition of `some_bit_func'; ./build/file1.o:/home/rat/dev/some_project/src/file1.c:4: first defined here
/usr/bin/ld: /tmp/main-6cf008.o: in function `main':
/home/rat/dev/some_project/main.c:13: undefined reference to `some_func'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [Makefile:21: a.out] Error 1
Basically, I'm currently trying to use something like this (Makefile
) in some quite bigger project, but the errors produced by the compiler are the same actually.
I tried a lot of things I could found on the internet, but none of it helped me.
Also, I think it is worth mentioning that it works ONLY if I compile all the objects and link them by hand (without using Makefile
) like this:
mkdir -p build
clang -g -I./include -c src/file1.c -o build/file1.o
clang -g -I./include -c src/file1.c -o build/file2.o
clang -g -I./include -o a.out main.c build/file1.o build/file2.o
Share
Improve this question
asked Mar 28 at 0:45
ratrat
411 silver badge4 bronze badges
1
|
1 Answer
Reset to default 4$(BUILDDIR)%.o: $(SOURCES) $(INCLUDE) build
is wrong. That says each object file depends on every source file, because $(SOURCES)
is always a list of all the source files and does not change.
Then, in $(CC) $(CFLAGS) -c $< -o $@
, the $<
says to use the first prerequisite file. So the first source file is used to build each object file.
You need $(BUILDDIR)%.o: $(SRCDIR)/%.c $(INCLUDE) build
, which says that each object file depends on its corresponding source file. The %
is a placeholder that make
replaces for each file matching the pattern.
The $(INCLUDE)
in the dependencies is also incorrect, although this will just cause object files to be rebuilt needlessly instead of incorrectly. The list of prerequisites should list only files that each object file actually depends on. To use a pattern with that, you need all files to follow the same pattern. But you currently have a file1.h
but no file2.h
. An empty file2.h
could workaround that.
clang -g -I./include -c src/file1.c -o build/file2.o
seems to be at least one of your problems. – pmacfarlane Commented Mar 28 at 1:10