5

I am having a terrible time getting the code in Exercise 32 of Learning C the Hard Way to compile.

I have copied verbatim the code from the creator's GitHub repo and even cloned on a fresh repository. I've looked through other repositories, tried on my MacOSX in addition to Ubuntu, etc. Nothing that I seem to do will compile.

I am using Ubuntu 12.04 (see below).

Here is my file structure (note -- this is the file structure when I only included the files directly from Exercise 32. Obviously, when I clone the git repository I get way more files. I used diff to make sure all my files, including my make file, in my pared down version exactly resemble the git repository):

$ pwd
/usr/local/me/code/C/liblcthw


$ ls
bin  LICENSE  Makefile  README.md  src  tests

$ ls tests/
list_tests.c  minunit.h  runtests.sh

$ ls src/lcthw/
dbg.h  list.c  list.h

Here is my make command.

$ make
cc -g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG  -fPIC   -c -o src/lcthw/list.o src/lcthw/list.c
ar rcs build/liblcthw.a src/lcthw/list.o
ranlib build/liblcthw.a
cc -g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG  build/liblcthw.a    tests/list_tests.c   -o tests/list_tests
tests/list_tests.c: In function ‘main’:
tests/list_tests.c:111:1: warning: parameter ‘argc’ set but not used [-Wunused-but-set-parameter]
/tmp/ccXlNfMl.o: In function `test_create':
/usr/local/me/code/C/liblcthw/tests/list_tests.c:13: undefined reference to `List_create'
/tmp/ccXlNfMl.o: In function `test_destroy':
/usr/local/me/code/C/liblcthw/tests/list_tests.c:22: undefined reference to `List_clear_destroy'
/tmp/ccXlNfMl.o: In function `test_push_pop':
/usr/local/me/code/C/liblcthw/tests/list_tests.c:31: undefined reference to `List_push'
/usr/local/me/code/C/liblcthw/tests/list_tests.c:34: undefined reference to `List_push'
/usr/local/me/code/C/liblcthw/tests/list_tests.c:37: undefined reference to `List_push'
/usr/local/me/code/C/liblcthw/tests/list_tests.c:41: undefined reference to `List_pop'
/usr/local/me/code/C/liblcthw/tests/list_tests.c:44: undefined reference to `List_pop'
/usr/local/me/code/C/liblcthw/tests/list_tests.c:47: undefined reference to `List_pop'
/tmp/ccXlNfMl.o: In function `test_shift':
/usr/local/me/code/C/liblcthw/tests/list_tests.c:56: undefined reference to `List_shift'
/usr/local/me/code/C/liblcthw/tests/list_tests.c:59: undefined reference to `List_shift'
/usr/local/me/code/C/liblcthw/tests/list_tests.c:62: undefined reference to `List_shift'
/tmp/ccXlNfMl.o: In function `test_remove':
/usr/local/me/code/C/liblcthw/tests/list_tests.c:74: undefined reference to `List_remove'
/tmp/ccXlNfMl.o: In function `test_unshift':
/usr/local/me/code/C/liblcthw/tests/list_tests.c:86: undefined reference to `List_unshift'
/usr/local/me/code/C/liblcthw/tests/list_tests.c:89: undefined reference to `List_unshift'
collect2: ld returned 1 exit status
make: *** [tests/list_tests] Error 1

I am using Ubuntu 12.04

$ lsb_release -a
LSB Version:    core-2.0-amd64:core-2.0-noarch:core-3.0-amd64:core-3.0-noarch:core-3.1-amd64:core-3.1-noarch:core-3.2-amd64:core-3.2-noarch:core-4.0-amd64:core-4.0-noarch:cxx-3.0-amd64:cxx-3.0-noarch:cxx-3.1-amd64:cxx-3.1-noarch:cxx-3.2-amd64:cxx-3.2-noarch:cxx-4.0-amd64:cxx-4.0-noarch:desktop-3.1-amd64:desktop-3.1-noarch:desktop-3.2-amd64:desktop-3.2-noarch:desktop-4.0-amd64:desktop-4.0-noarch:graphics-2.0-amd64:graphics-2.0-noarch:graphics-3.0-amd64:graphics-3.0-noarch:graphics-3.1-amd64:graphics-3.1-noarch:graphics-3.2-amd64:graphics-3.2-noarch:graphics-4.0-amd64:graphics-4.0-noarch:printing-3.2-amd64:printing-3.2-noarch:printing-4.0-amd64:printing-4.0-noarch:qt4-3.1-amd64:qt4-3.1-noarch
Distributor ID: Ubuntu
Description:    Ubuntu 12.04.2 LTS
Release:        12.04
Codename:       precise
bmargulies
  • 97,814
  • 39
  • 186
  • 310
eb80
  • 4,700
  • 5
  • 25
  • 30
  • list_tests.c refers to `` and either cc can't find this, or it can find it but it's a different version located elsewhere. – MicroVirus May 25 '14 at 01:21
  • 1
    Try `cc -g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG tests/list_tests.c -o tests/list_tests build/liblcthw.a` manually. Does that work? – Lee Duhem May 25 '14 at 01:22
  • 4
    In `cc -g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG build/liblcthw.a tests/list_tests.c -o tests/list_tests` try moving `build/liblcthw.a` to the end – M.M May 25 '14 at 03:24
  • This question might be `mis-marked` as a duplicate of http://stackoverflow.com/questions/45135/linker-order-gcc by _n.m._ and _alk_. While it is a linker-order-gcc related issue on a Linux system, it does not seem to be so on OS X. In addition, the linker-order-gcc question does not address how to correct the problem in a `makefile`. Perhaps the `marked as duplicate` flag can be reconsidered? – Mahonri Moriancumer May 26 '14 at 01:04
  • @MahonriMoriancumer if the link order is not an issue on Mac OS X, then shouldn't the errors be very different from those shown? – n. m. could be an AI May 26 '14 at 04:35
  • @n.m., Correct. As per the **Note** at the bottom of the answer, there was a very different error (and only that single error) on OS X to do with an inline function which required a local function prototype. The answer deals with the stated environment of the question, getting it all to work on Ubuntu 12.04. – Mahonri Moriancumer May 26 '14 at 04:45

1 Answers1

9

The Exercise 32 code downloaded from the github repo is platform specific for a BSD system (and perhaps OS X). Specifically, there are a couple required symbols for a successful build that are present on a BSD sysem, but which are most likely not present on a (Linux) Ubuntu 12.04 system. These include:

mergesort
heapsort

Nevertheless, the library and unit tests can all be successfully compiled, with the exception of one unit test that (eventually) will require the above symbols. Moving the single problematic test from the tests directory will allow the other tests to compile on a (Linux) SuSE SLES 11 system, if a slight change is made to a flaw in the makefile shown below.

After downloading the source mentioned in the question:

.../liblcthw-master> ll
total 28
drwxr-xr-x 2 mahonri users 4096 May 24 21:30 bin
drwxr-xr-x 2 mahonri users 4096 May 24 21:53 build
-rw-r--r-- 1 mahonri users 1548 May  8  2012 LICENSE
-rw-r--r-- 1 mahonri users 1139 May  8  2012 Makefile
-rw-r--r-- 1 mahonri users 1069 May  8  2012 README.md
drwxr-xr-x 3 mahonri users 4096 May 24 21:52 src
drwxr-xr-x 2 mahonri users 4096 May 24 21:53 tests

Flaws can be found in Makefile. Here is the flawed portion of the Makefile, as downloaded:

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

One issue is that placing the value of $(TARGET) build/liblcthw.a in the end of CFLAGS doesn't work. This is due to CFLAGS targeting the compiler, not the linker. Hence, the resulting 'tests' targets are not linked with the library.

Another issue is that at least one of the 'test' applications requires a math library.

To fix these issues, the Makefile must be modified to something similar to the following:

# The Unit Tests
.PHONY: tests
tests: LDLIBS += -lm -L./build -llcthw
tests: $(TESTS)
       sh ./tests/runtests.sh

To build liblcthw.a, and build the 'test' applications, run make against the updated Makefile in this directory:

.../liblcthw-master> ll
total 28
drwxr-xr-x 2 mahonri users 4096 May 24 21:30 bin
drwxr-xr-x 2 mahonri users 4096 May 24 21:53 build
-rw-r--r-- 1 mahonri users 1548 May  8  2012 LICENSE
-rw-r--r-- 1 mahonri users 1139 May  8  2012 Makefile
-rw-r--r-- 1 mahonri users 1069 May  8  2012 README.md
drwxr-xr-x 3 mahonri users 4096 May 24 21:52 src
drwxr-xr-x 2 mahonri users 4096 May 24 21:53 tests
.../liblcthw-master> make
cc -g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG  -fPIC   -c -o src/lcthw/bstree.o src/lcthw/bstree.c
cc -g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG  -fPIC   -c -o src/lcthw/bstrlib.o src/lcthw/bstrlib.c
cc -g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG  -fPIC   -c -o src/lcthw/darray_algos.o src/lcthw/darray_algos.c
src/lcthw/darray_algos.c: In function ‘DArray_heapsort’:
src/lcthw/darray_algos.c:12: warning: implicit declaration of function ‘heapsort’
src/lcthw/darray_algos.c: In function ‘DArray_mergesort’:
src/lcthw/darray_algos.c:17: warning: implicit declaration of function ‘mergesort’
cc -g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG  -fPIC   -c -o src/lcthw/darray.o src/lcthw/darray.c
cc -g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG  -fPIC   -c -o src/lcthw/hashmap_algos.o src/lcthw/hashmap_algos.c
cc -g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG  -fPIC   -c -o src/lcthw/hashmap.o src/lcthw/hashmap.c
cc -g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG  -fPIC   -c -o src/lcthw/list_algos.o src/lcthw/list_algos.c
cc -g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG  -fPIC   -c -o src/lcthw/list.o src/lcthw/list.c
cc -g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG  -fPIC   -c -o src/lcthw/radixmap.o src/lcthw/radixmap.c
cc -g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG  -fPIC   -c -o src/lcthw/ringbuffer.o src/lcthw/ringbuffer.c
cc -g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG  -fPIC   -c -o src/lcthw/sarray.o  src/lcthw/sarray.c
src/lcthw/sarray.c: In function ‘SuffixArray_create’:
src/lcthw/sarray.c:71: warning: implicit declaration of function ‘qsort_r’
cc -g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG  -fPIC   -c -o src/lcthw/stats.o src/lcthw/stats.c
cc -g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG  -fPIC   -c -o src/lcthw/tstree.o src/lcthw/tstree.c
ar rcs build/liblcthw.a src/lcthw/bstree.o src/lcthw/bstrlib.o src/lcthw/darray_algos.o src/lcthw/darray.o src/lcthw/hashmap_algos.o src/lcthw/hashmap.o src/lcthw/list_algos.o src/lcthw/list.o src/lcthw/radixmap.o src/lcthw/ringbuffer.o src/lcthw/sarray.o src/lcthw/stats.o src/lcthw/tstree.o
ranlib build/liblcthw.a
cc -g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG     tests/bstree_tests.c  -L./build -llcthw -o tests/bstree_tests
cc -g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG     tests/bstr_tests.c  -L./build -llcthw -o tests/bstr_tests
cc -g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG     tests/darray_algos_tests.c  -L./build -llcthw -o tests/darray_algos_tests
...

Then, the makefile continues on to run the test executables:

sh ./tests/runtests.sh
Running unit tests:
----
RUNNING: ./tests/bstree_tests
ALL TESTS PASSED
Tests run: 5
tests/bstree_tests PASS
tests/bstr_tests PASS
----
RUNNING: ./tests/darray_tests
ALL TESTS PASSED
Tests run: 8
tests/darray_tests PASS
----
RUNNING: ./tests/hashmap_algos_tests
ALL TESTS PASSED
Tests run: 4
tests/hashmap_algos_tests PASS
----
RUNNING: ./tests/hashmap_tests
ALL TESTS PASSED
Tests run: 5
tests/hashmap_tests PASS
----
RUNNING: ./tests/list_algos_tests
ALL TESTS PASSED
Tests run: 2
tests/list_algos_tests PASS
----
RUNNING: ./tests/list_tests
ALL TESTS PASSED
Tests run: 6
tests/list_tests PASS
----
RUNNING: ./tests/queue_tests
ALL TESTS PASSED
Tests run: 3
tests/queue_tests PASS
----
RUNNING: ./tests/radixmap_tests
ALL TESTS PASSED
Tests run: 1
tests/radixmap_tests PASS
----
RUNNING: ./tests/ringbuffer_tests
ALL TESTS PASSED
Tests run: 3
tests/ringbuffer_tests PASS
----
RUNNING: ./tests/sarray_tests
./tests/runtests.sh: line 3:  1033 Segmentation fault      $VALGRIND ./$i 2>>  tests/tests.log
ERROR in test tests/sarray_tests: here's tests/tests.log
------
DEBUG tests/ringbuffer_tests.c:60: ----- RUNNING: ./tests/ringbuffer_tests
DEBUG tests/ringbuffer_tests.c:53: 
----- test_create
DEBUG tests/ringbuffer_tests.c:54: 
----- test_read_write
DEBUG tests/ringbuffer_tests.c:55: 
----- test_destroy
DEBUG tests/sarray_tests.c:46: ----- RUNNING: ./tests/sarray_tests
DEBUG tests/sarray_tests.c:39: 
----- test_create
make: *** [tests] Error 1

NOTE: The same flaw didn't seem to impact an OS X 8 build (which is a puzzlement to myself). A separate flaw has to be fixed in the OS X 8 version of the build. Specifically, the following line in list_algos.c:

...

inline List *List_merge(List *left, List *right, List_compare cmp)
{
   List *result = List_create();
...

The inline function requires a function prototype. Change to the following:

...

extern List *List_merge(List *left, List *right, List_compare cmp);
inline List *List_merge(List *left, List *right, List_compare cmp)
{
   List *result = List_create();
...

After this single modification, the OS X version was able to make successfully.

Mahonri Moriancumer
  • 5,993
  • 2
  • 18
  • 28
  • @MahonriMoriancumer absolutely awesome answer! This fixed the issue and I am not able to build the unit tests and understand why. Thanks a million! – eb80 May 26 '14 at 12:40
  • Running this with _gcc 4.9.0_ and _make 4.0_, adding `tests: LDLIB += $(TARGET)` was sufficient. Thanks for the detailed answer – ivan-k Jun 07 '14 at 23:50
  • @Manbroski. Thanks, your solution worked for me. Unfortunately Mahonri Moriancumer's did not. Running with cc (Ubuntu 5.3.1-14ubuntu2.1) 5.3.1 20160413 and Gnu make 4.1. Perhaps Mahonri's is going against a later test scenario than the one in Chapter 32. Thanks both, anyhow! Nice insight. – jetimms Jun 14 '16 at 03:07
  • @MahonriMoriancumer Thank you so much for your answer. Can I ask you to elaborate on why the `List_merge` inline function needs a function prototype here? I had the compile error issue on `List_merge` function and the solution in your answer solved it. Before `List_merge` function in list_algos.c file, there is `ListNode_swap` inline function. And, it is used in `List_bubble_sort` function. I was wondering why `ListNode_swap` function doesn't need a function prototype to compile, while `List_merge` function needs one, even though both functions are a inline function. – Keita Jan 08 '17 at 11:17
  • @MahonriMoriancumer [This is the link to `ListNode_swap` inline function definition code.](https://github.com/zedshaw/liblcthw/blob/master/src/lcthw/list_algos.c#L4) [And this is the link to the same compile error issue posted on GitHub issue page, which I had.](https://github.com/zedshaw/liblcthw/issues/19) – Keita Jan 08 '17 at 11:18