3

Consider the following test project:

test.h:

#ifndef TEST_H
#define TEST_H

void test1(int);
void test2(int);

#endif /* TEST_H */

text.c:

#include "test.h"

void test1(int x) { (void) x; }

Oops, I forgot to define test2()! I would like some kind of feedback when I do this, preferably refusal to compile although a warning at least would be nice. However GCC 10.2 (on Ubuntu 20.10) compiles it fine with no warnings:

gcc -Wall -Wextra -Wpedantic -std=c11 -o libtest.o -c test.c

I think I understand why: what if test2() is actually meant to come from another library, maybe a system library? Make it the problem of whichever program ends up linking everything into an executable! But I want to know about it before then. In this case, it's not declared in any included header file. It's not called anywhere. Can that be detected?

I've tried:

  • --no-undefined which resulted in gcc: error: unrecognized command-line option ‘--no-undefined’; did you mean ‘-Wno-undef’?
  • -Wno-undef - accepted but no warning
  • -z,defs - accepted but no warning
  • -Wimplicit-function-declaration - accepted but no warning
  • -Werror=missing-declarations - I know this is for the opposite situation but I was getting desperate.
detly
  • 29,332
  • 18
  • 93
  • 152
  • 1
    It is the _linker_ (e.g. `ld`) _would_ call this out as undefined (the compiler _can't_). But, because `text.c` does _not_ actually _use_ `test2`, the compiler treats the forward _declaration_ for `test2` as _just_ that. It does _not_ put a reference to it in `text.o`. A declaration and _not_ a usage/reference. This is perfectly fine and what one normally wants ... – Craig Estey Feb 22 '21 at 03:01
  • 1
    ... For example, if one does `#include ` but never actually calls (e.g.) `cos`, we don't want complaints about a _missing_ `cos` if we _never_ use it [and forget to do `-lm`]. In this case, we might only need the `#define M_PI ...` from the `.h` and not call any functions, so we don't _need_ `-lm`. To get what you want, add a usage to `text.c` (e.g.): `int main(void); test1(); test2(); return 0; }` The linker will now search for `test2` and _not_ find it, so it will generate an error. – Craig Estey Feb 22 '21 at 03:03
  • 3
    Another [more passive] way to force a check is the take the _address_ of the function: `#include "test.h" void test1(int x) { (void) x; } void *check_defined[] = { test1, test2 }; void main(void) { }` – Craig Estey Feb 22 '21 at 03:20
  • @CraigEstey These are all valid points; I ask because sometimes GCC has some checks that carve out exceptions to rules like this for common use cases or workflow issues. – detly Feb 22 '21 at 03:33
  • 1
    You do know that your `gcc` command doesn't create a static library? It creates a static *executable* with the unusual name of `libtest.a`. To create a static library you first compile the `.o` files with `gcc -c` and then use `ar`. – Nate Eldredge Feb 22 '21 at 05:12
  • @NateEldredge You're right about `ar` but are you sure about the executable part? The `-c` flag is there, I don't get any `undefined reference to main` errors, and `file` tells me it's a library. – detly Feb 22 '21 at 05:25
  • 1
    I take it back, with `-c` you have created an *object file* with the unusual name of `libtest.a`. On my system `file` tells me it's `ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped`. A real static library is described as `current ar archive`. – Nate Eldredge Feb 22 '21 at 05:31
  • @NateEldredge I went the route of taking static out of the question title since it's irrelevant. It should all be consistent now. – detly Feb 22 '21 at 07:18

1 Answers1

2

This isn't possible, as no linking is performed at the stage of assembling a static library.

I'd suggest having a "test container" for your library. Set up your build system to build the test executable any time you are building the library. It could even just be a single .c file in the same directory as the library sources, but obviously not in the list of objects that are part of the library.

The test executable calls all of the functions that you wish to be entry points for the library.

Probably that is something you should be doing anyway in order to test the library's functionality before doing a release.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • This is the approach I currently use and it's good advice. I was wondering if it's possible to stop before attempting to build the test harness. But, if it's not, it's not. – detly Feb 22 '21 at 03:32