There were several issues in your Makefile
. And, I had to guess a bit about the correct file hierarchy. And, your list.c
was incomplete
The big bug in your Makefile
was this line which you used to build your tests:
tests: CFLAGS += $(TARGET)
$(TARGET)
is your library .a
file. By specifying the command this way, whenever you were building a test you got a command (e.g.):
cc -o mytest1 -O2 -g lib.a mytest1.c
The problem is that lib.a
gets scanned for dependencies before mytest1.c
is compiled, so the symbols that mytest1.c
wants from lib.a
are not known by the linker, so nothing is pulled from the library.
The correct command is:
cc -o mytest1 -O2 -g mytest1.c lib.a
To specify this correctly in the Makefile
, see the changes below.
I've fixed your Makefile
and annotated the above bug, along with a few others:
# NOTE/BUG: there are issues with _not_ using full path. they _can_ be solved
# without doing this, but this makes things easier
SRC := $(shell pwd)
# NOTE: to just _build_ the tests but _not_ try to run them, specify:
# RUNTESTS=tests
# on the command line
RUNTESTS ?= runtests
# NOTE: cosmetic change to library name
###LIBNAME = YOUR_LIBRARY
LIBNAME = lcthw
CFLAGS += -g
CFLAGS += -O2
CFLAGS += -Wall
# NOTE: IMO, -Wextra is overkill and causes more problems than it's worth
# what I do is something like this and then do:
# make CMDLINE_CFLAGS=-Wextra
# on those rare occasions where it might be useful
###CFLAGS += -Wextra
CFLAGS += $(CMDLINE_CFLAGS)
CFLAGS += -I$(SRC) -rdynamic -DNDEBUG $(OPTFLAGS)
LIBS=-ldl $(OPTLIBS)
PREFIX?=/usr/local
# library sources and objects
# NOTE/BUG: the wildcard was failing
###SOURCES=$(wildcard src/**/*.c src/*.c)
SOURCES=$(wildcard lcthw/*.c)
OBJECTS=$(patsubst %.c,%.o,$(SOURCES))
# test sources and objects
TEST_SRC=$(wildcard tests/*_tests.c)
TEST_OBJS=$(patsubst %.c,%.o,$(TEST_SRC))
TESTS=$(patsubst %.o,%,$(TEST_OBJS))
# library target
TARGET=build/lib$(LIBNAME).a
SO_TARGET=$(patsubst %.a,%.so,$(TARGET))
TARGETS += $(TARGET)
# NOTE: comment the following out to _not_ build the shared library -- it's
# _not_ used but will be built now (to use it, change TARGET to SO_TARGET when
# building TESTS)
TARGETS += $(SO_TARGET)
# The Target Build
all: $(TARGETS) $(RUNTESTS)
###dev: CFLAGS=-g -Wall -Isrc -Wall -Wextra $(OPTFLAGS)
dev: CFLAGS=-g -Wall -I$(SRC) -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
# NOTE/BUG: in order to link properly $(TARGET) must be the last part of the
# command
#
# (i.e.) doing CFLAGS += $(TARGET) produced the equivalent of:
# $(CC) -o $@ $(CFLAGS) $(TARGET) $@.c
#
# instead of:
# $(CC) -o $@ $(CFLAGS) $@.c $(TARGET)
#
# the reason the first method failed was because the library was scanned
# _before_ the .c was compiled, so it didn't know to pull any .o files from
# it
#
# the second example _will_ work and is closer to what you originally specified,
# but my personal preference is to always build the .o files:
$(TESTS):
$(CC) -c $(CFLAGS) -o $@.o $@.c
$(CC) -o $@ $(CFLAGS) $@.o $(TARGET)
# just build the tests
.PHONY: tests
tests: $(TESTS)
# build and run the tests
.PHONY: runtests
runtests: $(TESTS)
sh ./tests/runtests.sh
valgrind:
VALGRIND="valgrind --log-file=/tmp/valgrind-%p.log" $(MAKE)
# The Cleaner
clean:
rm -f *.o
rm -rf bin build $(OBJECTS) $(TESTS)
rm -f $(TEST_OBJS)
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
Reminder: Due to the way code blocks are posted on SO, the tabs in the makefile get converted to spaces. So, when you pull the makefile, you'll need to convert leading whitespace on a line to a single tab. Otherwise, you'll see (e.g.) the dreaded Makefile:59: *** missing separator. Stop.
Here's the file hierarchy I ended up with:
elixir/ex30.c
elixir/lcthw/list.c
elixir/lcthw/list.h
elixir/libex30.c
elixir/Makefile
elixir/tests/dbg.h
elixir/tests/libex30_tests.c
elixir/tests/list_tests.c
elixir/tests/minunit.h
elixir/tests/runtests.sh
The makefile works correctly against that. Note that elixir
could be (e.g.) /home/elixir/projects/.../mylib
as the makefile now does a pwd
But, if [inadvertently] put things in the wrong place, you can certainly move them, but you may have to adjust the Makefile
a bit.
Also, even though list.c
was posted, it was incomplete and defined only about half of the functions in list.h
. This bug was masked by the primary makefile bug. So, I added dummy versions to get a clean build:
#include <lcthw/list.h>
//#include <lcthw/dbg.h>
List *List_create()
{
return calloc(1, sizeof(List));
}
void List_destroy(List *list)
{
LIST_FOREACH(list, first, next, cur) {
if(cur->prev) {
free(cur->prev);
}
}
free(list->last);
free(list);
}
void List_clear(List *list)
{
LIST_FOREACH(list, first, next, cur) {
free(cur->value);
}
}
void List_clear_destroy(List *list)
{
List_clear(list);
List_destroy(list);
}
void
List_push(List *list, void *value)
{
}
void *
List_pop(List *list)
{
return NULL;
}
void
List_unshift(List *list, void *value)
{
}
void *
List_shift(List *list)
{
return NULL;
}
void *
List_remove(List *list, ListNode *node)
{
return NULL;
}
The output I'm getting after the fixes looks something like this:
cc -g -O2 -Wall -I/home/myhome/elixir -rdynamic -DNDEBUG -fPIC -c -o lcthw/list.o lcthw/list.c
ar rcs build/liblcthw.a lcthw/list.o
ranlib build/liblcthw.a
cc -shared -o build/liblcthw.so lcthw/list.o
cc -c -g -O2 -Wall -I/home/myhome/elixir -rdynamic -DNDEBUG -o tests/list_tests.o tests/list_tests.c
cc -o tests/list_tests -g -O2 -Wall -I/home/myhome/elixir -rdynamic -DNDEBUG tests/list_tests.o build/liblcthw.a
cc -c -g -O2 -Wall -I/home/myhome/elixir -rdynamic -DNDEBUG -o tests/libex30_tests.o tests/libex30_tests.c
cc -o tests/libex30_tests -g -O2 -Wall -I/home/myhome/elixir -rdynamic -DNDEBUG tests/libex30_tests.o build/liblcthw.a
sh ./tests/runtests.sh
Running unit tests:
----
RUNNING: ./tests/libex30_tests
ALL TESTS PASSED
Tests run: 4
tests/libex30_tests PASS
----
RUNNING: ./tests/list_tests
FAILED: Wrong last value.
Tests run: 2
ERROR in test tests/list_tests: here's tests/tests.log
------
DEBUG tests/libex30_tests.c:32:all_tests:
----- test_failures
DEBUG tests/libex30_tests.c:33:all_tests:
----- test_dlclose
DEBUG tests/list_tests.c:113:main: ----- RUNNING: ./tests/list_tests
DEBUG tests/list_tests.c:103:all_tests:
----- test_create
DEBUG tests/list_tests.c:104:all_tests:
----- test_push_pop
[ERROR] (tests/list_tests.c:32: errno: None) Wrong last value.
Makefile:96: recipe for target 'runtests' failed
make: *** [runtests] Error 1
UPDATE:
I apologize for the mistake. I realize I have mixed the two projects...after updating the third time...I have corrected the mistake and i have given the file hierarchy. Could you take a look at the updated post? It has less files and does not really use the linked list code.
Notice the output. It did run the non-list tests successfully. If you fill in the dummy functions in list.c
, the list tests would also work.
As it is, instead of a subdir of src
for the ex30
project, it would be cleaner to use a subdir of ex30
.
But, what I suspect what you really want [ultimately] is something like:
elixir/common/dbg.h
elixir/common/rules.mk
elixir/ex30/ex30.c
elixir/ex30/libex30.c
elixir/ex30/Makefile
elixir/lcthw/list.c
elixir/lcthw/list.h
elixir/lcthw/Makefile
elixir/tests/libex30_tests.c
elixir/tests/list_tests.c
elixir/tests/Makefile
elixir/tests/minunit.h
elixir/tests/runtests.sh
Note that the bulk of Makefile
would be moved to rules.mk
and each Makefile
would be just a few lines. And, you can add more projects with a similar sub-hierarchy.
IMO, a possibly cleaner variant is:
elixir/common/dbg.h
elixir/common/rules.mk
elixir/ex30/ex30.c
elixir/ex30/libex30.c
elixir/ex30/libex30_tests.c
elixir/ex30/Makefile
elixir/lcthw/list.c
elixir/lcthw/list.h
elixir/lcthw/list_tests.c
elixir/lcthw/Makefile
elixir/tests/Makefile
elixir/tests/minunit.h
elixir/tests/runtests.sh
The src
subdir could be added for each project.
Along with build
. But, what I'd recommend for build
is a top level directory that has per-project subdirs:
elixir/build/ex30/libex30_tests
elixir/build/ex30/libex30_tests.o
elixir/build/lcthw/lcthw.a
elixir/build/lcthw/list.o
elixir/build/lcthw/list_tests.o
elixir/build/lcthw/list_tests
This has the advantage that the source tree hierarchy is not cluttered with .o
, .a
and executables (i.e.) things that get rebuilt. This makes setup for git
easier because you don't need a per-project .gitignore
file.
YMMV, but, I've done this for my own stuff in every conceivable organization, and this is the one I've settled on, after much trial and error.
If that is okay with you I can adjust accordingly and repost.
To give you a more concrete example of a more powerful rules.mk
file, here is one I use a lot for SO questions.
Note: This is just an example. It can not be used by itself because it relies upon several wrapper scripts to operate. Don't try to use or adapt it. The best route for you is to build up your own rules.mk
from scratch, using your original Makefile
as a starting point.
# rules/rules.mk -- ovrstk rules control
#
# options:
# GDB -- enable debug symbols
# 0 -- normal
# 1 -- use -O0 and define _USE_GDB_=1
#
# CLANG -- use clang instead of gcc
# 0 -- use gcc
# 1 -- use clang
#
# CVERBOSE -- build verbosity
# 0 -- normal
# 1 -- add -v to cc and -Wl,--verbose to ld
#
# MKVERBOSE -- makefile/rules verbosity
# 0 -- normal
# 1 -- verbose
#
# M32 -- cross-build to 32 bit mode
# 0 -- native build
# 1 -- build for i386
#
# BNC -- enable benchmarks
# 0 -- normal mode
# 1 -- enable benchmarks for function enter/exit pairs
# 2 -- add min/max
#
# XCFLAGS -- extra command line CFLAGS
# XDFLAGS -- extra command line DFLAGS
#
# DOT_I -- generate preprocessor output
# DOT_S -- generate assembler output
#
# GLIB -- added options for glib
#
# symbols:
# DFLAGS -- -D options
# CFLAGS -- compiler options
#
# PREP -- targets to execute before ALL
# ALL -- things to build (automatically uses LIBNAME/PGMTGT)
#
# LIBNAME -- library name to build (e.g. foo.a) -- can be multiple
# OLIB -- list of .o files to build LIBNAME
# OLIB-<libname> -- list of .o files to build <libname>
#
# PGMTGT -- program to build (e.g. fludger) -- can be multiple
# OLIST -- list of .o files to build PGMTGT
# OLIST-<pgmname> -- list of .o files to build <pgmname>
#
# CLEAN -- things to clean (automatically uses a bunch of stuff)
#
# NOPROTO -- do not generate prototypes
# FINLINE -- allow gcc to inline functions automatically
# WNOERROR -- inhibit -Werror
# WEXTRA -- add -Wextra
ifdef OVRPUB
ifndef SDIR
SDIR := $(shell pwd)
STAIL := $(notdir $(SDIR))
endif
ifndef GENTOP
GENTOP := $(dir $(SDIR))
endif
ifndef GENDIR
GENDIR := $(GENTOP)/$(STAIL)
endif
ifndef ODIR
ODIR := $(GENDIR)
endif
NOPROTO := 1
endif
# disable prototype generation
ifdef NOPROTO
PROTOLST := true
PROTOGEN := @true
else
PROTOLST := qproto
PROTOGEN := @qproto
PROTOALL := proto
endif
ifndef SDIR
$(error rules: SDIR not defined)
endif
ifndef ODIR
$(error rules: ODIR not defined)
endif
ifndef GENDIR
$(error rules: GENDIR not defined)
endif
ifndef GENTOP
$(error rules: GENTOP not defined)
endif
ifndef _rules_mk_
_rules_mk_ = 1
CLEAN += $(LIBNAME) $(PGMTGT)
ifndef NOPROTO
CLEAN += *.proto
endif
CLEAN += *.a
CLEAN += *.o
CLEAN += *.i
CLEAN += *.dis
CLEAN += *.lst
CLEAN += *.TMP
QPROTO := $(shell $(PROTOLST) -i -l -O$(GENTOP) $(SDIR)/*.c $(CPROTO))
HDEP += $(QPROTO)
###VPATH += $(GENDIR)
###VPATH += $(SDIR)
ifdef INCLUDE_MK
-include $(INCLUDE_MK)
endif
ifdef M32
CFLAGS += -m32
endif
ifdef CVERBOSE
CFLAGS += -v
endif
ifdef MKVERBOSE
MSG := @echo
else
MSG := @true
endif
ifdef GSYM
CFLAGS += -gdwarf-2
endif
ifdef GDB
CFLAGS += -gdwarf-2
DFLAGS += -D_USE_GDB_
else
CFLAGS += -O2
endif
ifdef DEBUG
DFLAGS += -DDEBUG=$(DEBUG)
endif
ifndef ZPRT
DFLAGS += -D_USE_ZPRT_=0
endif
ifdef BNC
DFLAGS += -D_USE_BNC_=$(BNC)
endif
ifdef GLIB
_GLIB = glib-2.0
DFLAGS += $(shell pkg-config --cflags $(_GLIB))
STDLIB += $(shell pkg-config --libs $(_GLIB))
endif
ifdef CLANG
CC := clang
CXX := clang++
else
ifdef CPLUS
ifeq ($(STDLIB),)
STDLIB += -lstdc++
endif
endif
endif
# alternate -std
# NOTE: clang++ does not understand c++17
ifdef CSTD
_CSTD := $(CSTD)
ifdef CLANG
ifdef CPLUS
ifeq ($(CSTD),c++17)
_CSTD := c++1y
endif
endif
endif
CFLAGS += -std=$(_CSTD)
endif
ifdef MPI
export PATH := /usr/lib64/openmpi/bin:$(PATH)
CC := mpicc
CXX := mpicxx
endif
DFLAGS += -I$(GENTOP)
DFLAGS += -I$(OVRTOP)
DFLAGS += -I$(OVRBNC)
CFLAGS += -Wall
ifndef WNOERROR
CFLAGS += -Werror
endif
ifdef WEXTRA
CFLAGS += -Wextra
endif
CFLAGS += -Wno-unknown-pragmas
CFLAGS += -Wempty-body
CFLAGS += -fno-diagnostics-color
ifdef DOT_I
NOLDC := @true
COPTS += -E -P
O := i
endif
ifdef DOT_S
NOLDC := @true
COPTS += -S
SOPTS += -E -P
O := s
endif
ifndef COPTS
COPTS += -c
O := o
endif
ifeq ($(O),o)
ALL += $(LIBNAME) $(PGMTGT)
else
ALL += $(addsuffix .$(O),$(basename $(PGMTGT) $(OLIST)))
endif
ifndef FINLINE
# NOTE: we now need this to prevent inlining (enabled at -O2)
ifndef CLANG
CFLAGS += -fno-inline-small-functions
endif
# NOTE: we now need this to prevent inlining (enabled at -O3)
CFLAGS += -fno-inline-functions
endif
ifdef FIXREG
ifndef CLANG
CFLAGS += $(FIXREG)
endif
endif
ifndef F95
F95 := f95
endif
ifndef LDC
ifdef FTN
LDC = $(F95)
endif
endif
ifndef LDC
ifdef CPLUS
LDC = $(CXX)
else
LDC = $(CC)
endif
endif
# FIXME/CAE -- gold wiki page says use -Wl but it seems to have no effect
ifndef CLANG
###LDC += -Wl,-fuse-ld=ld.blah
endif
ifdef CVERBOSE
LDC += -Wl,-verbose=2
endif
ifdef LIBLIST
LIBLIST := $(addprefix $(GENTOP)/,$(LIBLIST))
endif
CFLAGS += $(XCFLAGS)
DFLAGS += $(XDFLAGS)
CFLAGS += $(DFLAGS)
endif
all: $(PREP) $(PROTOALL) $(ALL)
# C
%.o: %.c $(HDEP)
$(MSG) %.o %.c
$(CC) $(CFLAGS) $(COPTS) -o $*.$(O) $<
%.i: %.c
$(MSG) %.i %.c
cpp $(DFLAGS) -P $< > $*.i
%.s: %.c
$(MSG) %.s %.c
$(CC) $(CFLAGS) -S -o $*.s $<
# C++
%.o: %.cpp $(HDEP)
$(MSG) %.o %.cpp
$(CXX) $(CFLAGS) $(COPTS) -o $*.$(O) $<
%.i: %.cpp
$(MSG) %.i %.cpp
cpp $(DFLAGS) -P $< > $*.i
%.s: %.cpp
$(MSG) %.s %.cpp
$(CXX) $(CFLAGS) -S -o $*.s $<
# asm
%.o: %.s $(HDEP)
$(MSG) %.o %.s
$(AS) $(AFLAGS) -o $*.$(O) $<
%.o: %.S $(HDEP)
$(MSG) %.o %.S
$(CC) $(CFLAGS) $(COPTS) $(SOPTS) -o $*.$(O) $<
# fortran
%.o: %.f95 $(HDEP)
$(MSG) %.o %.f95
$(F95) -c -o $*.$(O) $<
.SECONDEXPANSION:
###.SUFFIXES:
# build a library (type (2) build)
$(LIBNAME):: $(OLIB) $$(OLIB-$$@)
$(NOLDC) ar rv $@ $^
# build programs
$(PGMTGT):: $$@.o $(OLIST) $$(OLIST-$$@) $(LIBLIST)
$(NOLDC) $(LDC) $(CFLAGS) -o $@ $^ $(STDLIB)
.PHONY: proto
proto::
$(PROTOGEN) -i -v -O$(GENTOP) $(SDIR)/*.c $(CPROTO)
.PHONY: clean
clean::
rm -f $(CLEAN)
.PHONY: help
help::
egrep '^#' $(SDIR)/Makefile
Here is a per-project Makefile
that uses it:
# fastread/Makefile -- make file for fastread
#
# SO: read line by line in the most efficient way platform specific
# SO: questions/33616284
ifndef _fastread_mk_
_fastread_mk_ = 1
LIBNAME = fastread
OLIB += rdgets.o
OLIB += rdmmap.o
OLIB += lib.o
OLIB += node1.o
OLIB += node2.o
PGMTGT = xrdfile
OLIST += rdgets.o
OLIST += rdmmap.o
OLIST += lib.o
OLIST += node1.o
OLIST += node2.o
###LIBLIST = fastread/fastread.a
HDEP += fastread.h
DFLAGS += -I$(SDIR)
endif
include $(OVRTOP)/rules/rules.mk