[libc-dev] Header files, include paths and gtest

Siva Chandra via libc-dev libc-dev at lists.llvm.org
Sat Jan 11 10:54:00 PST 2020


tl;dr - gtest is unittesting framework which uses the standard
library. A circular dependency occurs when using gtest to test the
standard library itself. [1] [2]

To begin the long story, let me say a little about header files and
include paths in llvm-libc. One of the implementation rules we want to
follow is that header files should only be included using their local
path in the implementation and test `.cpp` files. The same rule also
is imposed on the internal header files like `src/sys/mman/mmap.h`.
That is, the internal implementation header files like `mmap.h` should
include `mman.h` using the full internal path `include/mmap.h` and not
`<sys/mman.h>` or `"sys/mman.h"`. Likewise, the mmap unittest should
include `sys/mman.h` using the full internal path `include/sys/mman.h`
and not `<sys/mman.h>` or `"sys/mman.h"`. We need an implementation
rule like this because we want to ensure that our implementations and
tests only pick pieces from llvm-libc and not from the system-libc.

I confess that I have accidentally, and also due to oversight in some
cases, broken this rule in a few places. So, once I realized I had
broken this rule, I tried fixing them. This attempt to fix is what
brings us to the topic of this email.

As far as the implementations in the `src` directory go, I did not
face any problem fixing the places at which the rule was broken.
However, as I started touching the tests, I ran into problems. The
problem specifically was because of gtest. We use gtest for our
unittests, but `gtest.h` includes string.h, time.h etc directly [3].
As parts of a few standards require that some of the header files
include other header files, llvm-libc's include paths are setup to
lookup standard library headers from llvm-libc first. Because of this,
gtest ends up getting llvm-libc's headers. We do not want gtest to be
using llvm-libc's headers as it is not compiled against them. We
cannot also change the include path order to enable gtest to get the
system libc's headers because then, llvm-libc's public headers which
include other public headers, will end up picking up systems libc
headers.

I could not come up with a clean solution to this problem other than
disallowing gtest for unittests. Which means, we need a replacement
which does not cause the problems I described above. For consistency,
we want the interface of this replacement to be exactly like that of
gtest. We do not however need this replacement to be as featureful as
gtest (to begin with at least). As a demonstration, I have implemented
a simple and lightweight framework and put it on my github account:
https://github.com/sivachandra/unittest. I have also tested it with
llvm-libc and verified that it solves the above header file problem
and can be used as a drop-in replacement for gtest. Hence, I propose
that we put this unittest framework in the llvm-libc tree (of course
after a code review) and use it for setting up llvm-libc unittests. At
the interface level, the experience will not be any different from
using gtest (except that of including something different from
`gtest/gtest.h`.)

[1] At a fundamental level, gtest is not designed to test standard libraries.
[2] Circular dependency can be of two kinds: header files and symbols.
By ensuring that the tests call in to llvm-libc using internal names,
we have avoided the circular dependeny wrt the symbols. So, when you
link gtest with a test, we can be confident that gtest's library code
does not call into llvm-libc.
[3] To make our problem worse, LLVM's gtest pulls in headers from the
LLVM Support library, which in turn pull in a large number of standard
headers.

Thanks,
Siva Chandra


More information about the libc-dev mailing list