Home > other >  How do properly give archive files in Makefile?
How do properly give archive files in Makefile?

Time:08-10

When I run make tests, I get the following output:

cc -g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG  build/liblcthw.a    tests/list_tests.c  -ldl -o tests/list_tests
In file included from tests/list_tests.c:1:
tests/list_tests.c: In function ‘main’:
/usr/bin/ld: /tmp/ccBX85Eg.o: in function `test_create':
/home/taimoorzaeem/Documents/cprogs/lcthw/liblcthw/tests/list_tests.c:11: undefined reference to `List_create'
/usr/bin/ld: /tmp/ccBX85Eg.o: in function `test_destroy':
/home/taimoorzaeem/Documents/cprogs/lcthw/liblcthw/tests/list_tests.c:19: undefined reference to `List_clear_destroy'
--- a lot of similar errors

It turns out, if a run the same command like cc -g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG tests/list_tests.c build/liblcthw.a -ldl -o tests/list_tests, it works perfectly fine.

This seems like an issue with specifying .a files in Makefiles. What changes do I need to make in my Makefile to run the make tests like the mentioned command. Is there a recommended way to specify .a files in Makefile?

My directory structure looks like this:

.
├── bin
├── build
│   ├── liblcthw.a
│   └── liblcthw.so
├── LICENSE
├── Makefile
├── README.md
├── src
│   └── lcthw
│       ├── dbg.h
│       ├── list.c
│       ├── list.h
│       └── list.o
└── tests
    ├── list_tests.c
    ├── minunit.h
    └── runtests.sh

This is how my Makefile looks:

CFLAGS=-g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG $(OPTFLAGS)
LIBS=-ldl $(OPTLIBS)
LDLIBS=-ldl
PREFIX?=/usr/local

SOURCES=$(wildcard src/**/*.c src/*.c)
OBJECTS=$(patsubst %.c, %.o, $(SOURCES))

TEST_SRC=$(wildcard tests/*_tests.c)
TESTS=$(patsubst %.c,%,$(TEST_SRC))

TARGET=build/liblcthw.a
SO_TARGET=$(patsubst %.a,%.so,$(TARGET))

#The target build
all: $(TARGET) $(SO_TARGET) tests

dev: CFLAGS=-g -Wall -Isrc -Wall -Wextra $(OPTFLAGS)
dev: all

$(TARGET): CFLAGS  = -fPIC
$(TARGET): build $(OBJECTS)
    ar rcs $@ $(OBJECTS)
    ranlib $@

$(SO_TARGET): $(TARGET) $(OBJECTS)
    $(CC) -shared -o $@ $(OBJECTS)

build:
    @mkdir -p build
    @mkdir -p bin

# The Unit Tests
.PHONY: tests
tests: CFLAGS  = $(TARGET)
tests: $(TESTS)
    sh ./tests/runtests.sh

valgrind:
    VALGRIND="valgrind --log-file=/tmp/valgrind-%p.log" $(MAKE)

# The cleaner
clean:
    rm -rf build $(OBJECTS) $(TESTS)
    rm -f tests/tests.log
    find . -name "*.gc*" -exec rm {} \;
    rm -rf `find . -name "*.dSYM" -print`

# The install
install: all
    install -d $(DESTDIR)/$(PREFIX)/lib/
    install $(TARGET) $(DESTDIR)/$(PREFIX)/lib/

# The checker
BADFUNCS='[^_.>a-zA-Z0-9](str(n?cpy|n?cat|xfrm|n?dup|str|pbrk|tok|_)|stpn?cpy|a?sn?printf|byte_)'
check:
    @echo Files with potentially dangerous functions
    @egrep $(BADFUNCS) $(SOURCES) || true

CodePudding user response:

The answer to your question is mentioned in the comments but just to be very specific: the difference between your working and not-working versions is the order of the arguments. This has nothing to do with make or makefiles specifically: if you copy the identical command that make ran and paste it at your shell prompt, you'll get the same error so it's clearly not make-related.

The order of arguments to the compiler matters (not always, not for all options, but often). When you link code you always want all your object files (.o) to come first, then libraries (both static (.a) and shared (.so)) to come afterwards. And, if you have multiple libraries, they have to be ordered as well: the "highest level" libraries come first and the "lower-level" libraries (libraries needed by other libraries) come afterward.

As for your makefile, as mentioned in the comments you can't add libraries to your CFLAGS variable; this is wrong:

tests: CFLAGS  = $(TARGET)

that's why you have your library on your link line before your object files, because CFLAGS comes before object files in the default recipe you're using.

You should use this instead:

$(TESTS): $(TARGET)

this ensures that your library is built before your tests, and also that they will be included in the link line (after the list_tests.c)

  • Related