Boost logo

Boost-Build :

Subject: Re: [Boost-build] Compiling header files independently as a test.
From: Steven Watanabe (watanabesj_at_[hidden])
Date: 2018-03-17 23:58:47


AMDG

On 03/16/2018 02:13 PM, Thomas Brown via Boost-build wrote:
>
> I have found it useful to (and there are some that require) test
> header file independence by including it by itself in a source file
> and checking that it compiles. For example, the Bloomberg BDE
> project states that the ".h file of each component should contain only
> those #include directives (and forward class declarations) necessary
> to ensure that the header file can compile in isolation."
> (https://github.com/bloomberg/bde/wiki/physical-code-organization).
>
> I have been exploring doing this with Boost.Build and while I've
> figured out a way to get it done, it is not nearly as nice as I'd like
> it to be. Does anyone have a better approach? if not, could we add
> something to testing.jam to support this directly?
>
> I've cobbled together some code that works for me but has several
> limitations that I don't like and don't know how to fix.
>
> 1. I implement the tests in the library's Jamfile since that is where
> the actual header file names are known. I actually put all required
> files (headers, sources, etc.) in the sources list of the alias or
> lib. Is that something others are doing? This forces me to put the
> source file names and header file names into separate variables so I
> can iterate over them. I also do this in order to specify the sources
> for a doxygen target within Boost.Build.
>

  I've never seen that before. Listing the headers
as sources for a library will just cause them to
be ignored.

> I have a separate Jamfile for tests to simplify using just the
> library with other Boost.Build-based projects. I'd prefer to have all
> the tests together, but I don't want to repeat the headers nor use a
> glob.
>
> I might consider using a glob, but that would again be in the
> library definition, not the test file.
>
> 2. I'd really prefer to iterate through the sources of any target (at
> least any alias or lib) to run the compile-header tests in the test
> Jamfile without knowing what they are named. This would actually make
> it possible to have a separate Jamfile to generate doxygen as well.
>

Iterating over the sources of a target is a bad idea.
Instead, use an alias and handle the processing at the
virtual target layer (via generate, a generator, or
a custom target class). In particular, for checking
headers, you can use a composing generator to handle
all of the following:
- compile each header in a separate translation unit.
- include all headers in a single translation unit (useful
  for catching duplicate #include guards)
- link two translation units that #include all headers. (catches
  functions that should be inlined)

> 3. The "compile-header" target might require more information if the
> library has dependencies, which have to be specified in both the
> library and the compile-header targets. This seems to usually be
> handled by depending on the library itself in the compile-header
> target, so maybe it's not a big deal, but it is a bit strange to say
> that compiling the header that is part of libx depends on libx.
>

<use>libx will pick up the usage-requirements, but otherwise
ignore libx, which I think is the behavior you want.

> I've included inline some code that reflects what I am currently
> doing. It is extremely preliminary. I could see, at the minimum,
> adding a "compile-header" and maybe "compile-header-fail" (not
> implemented here)

I don't think that compile-header-fail is useful.

> that generates a source file and attempts to compile
> it to testing.jam. However, this approach still leaves a lot of
> boilerplate to the user and requires the tests are in the same Jamfile
> as the alias / lib target definition.
>
> I would not mind doing the work to get something in if others agree on
> the concept and an approach to implement it.
>
> Best regards,
> Tom
>
> 1. I have a local testing-additions.jam file that adds a "compile-header" rule
>
> ```
> # testing-additions.jam
> import testing ;
>
> # generate a source file and check for compile success
> rule compile-header ( sources : requirements * : target-name ? )
> {
> local s ;
> local t ;
>
> if ! $(target-name)
> {
> target-name = compile_test_$(sources:D=:S=) ;
> }
>
> s = $(target-name).cpp ;
>
> make $(s) : $(sources) : @testing-additions.generate-source-for-header ;
>
> compile $(s) : $(requirements) : $(target-name) ;
> }
>
> actions generate-source-for-header
> {
> echo "" > $(<)
> echo "#include \"$(>)\"" >> $(<)
> echo "int main () { return 0; }" >> $(<)
> echo "" >> $(<)
> }
> ```
>
> 2. Wherever I have header files in my projects (in an alias or a lib,
> etc). I do something like the following.
>
> ```
> # a Jamfile for the header-only library libx
> import testing-additions ;
>
> # other header-only libraries that libx depends on
> alias dep0 ;
> alias dep1 ;
>
> local headers =
> x0.hpp
> x1.hpp
> x2.h
> ;
>
> alias libx
> : # sources
> $(headers)
>
> dep0
> dep1
>
> : # requirements
> : # default-build
> : # usage-requirements
> <include>src
> ;
>
> for h in $(headers)
> {
> compile-header $(h) : <dependency>libx ;
> }
> ```
>


Boost-Build list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk