Add support for custom prefix in built-in checks

This commit is contained in:
Boris Kolpackov 2021-11-24 13:30:54 +02:00
parent d1dc8180d4
commit f0715ced26
5 changed files with 172 additions and 18 deletions

View File

@ -56,6 +56,16 @@ Besides the built-in configuration options, custom substitutions can be
specified as `buildfile` variables in the same way as with the
[`in`][module-in] module. For example:
```
/* config.h.in */
#define PACKAGE_NAME @PACKAGE_NAME@
#define PACKAGE_VERSION @PACKAGE_VERSION@
#undef HAVE_STRLCPY
#undef HAVE_STRLCAT
```
```
h{config}: in{config}
{
@ -73,6 +83,40 @@ h{config}: in{config}
}
```
The build-in checks can be prefixed in order to avoid clashes with similarly
named macros in other headers. This is an especially good idea if the
resulting header is public. To enable this, we specify the prefix with
the `autoconf.prefix` variable and then use the prefixed versions of
the options in the `config.h.in` file. For example:
```
/* config.h.in */
#undef LIBFOO_HAVE_STRLCPY
#undef LIBFOO_HAVE_STRLCAT
```
```
h{config}: in{config}
{
autoconf.prefix = LIBFOO_
}
```
Note that `autoconf.prefix` only affects the lookup of the built-in checks.
Custom substitutions and overrides of build-in checks must include the
prefix. For example:
```
h{config}: in{config}
{
autoconf.prefix = LIBFOO_
LIBFOO_HAVE_STRLCPY = true
}
```
## Adding new checks
To add a check for a new configuration option `<NAME>` simply create the
@ -92,14 +136,21 @@ there should be no double-quotes or backslashes except for line
continuations. For example:
```
// HAVE_FOO
#ifndef _WIN32
# define HAVE_FOO 1
// HAVE_BAR
#if !defined(_WIN32) || \
defined(__MINGW32__)
# define HAVE_BAR 1
#else
# undef HAVE_FOO /* No foo on Windows. */
# undef HAVE_BAR /* No bar on Windows except with MinGW. */
#endif
```
Note also that the module implementation may need to replace `<NAME>` with its
prefixed version if the `autoconf.prefix` functionality is in use (see above).
This is done by textually substituting every occurrence of `<NAME>` that is
separated on both left and right hand sides (that is, both characters
immediately before and after `<NAME>` are not `[A-Za-z0-9_]`).
[module-in]: https://build2.org/build2/doc/build2-build-system-manual.xhtml#module-in
[proj-config]: https://build2.org/build2/doc/build2-build-system-manual.xhtml#proj-config
[checks]: https://github.com/build2/libbuild2-autoconf/tree/master/libbuild2-autoconf/libbuild2/autoconf/checks/

View File

@ -228,3 +228,24 @@ cat config.h >>EOO
#define zzz_TEST_DUMMY1_H 1
#define zzz_TEST_DUMMY2_H 2
EOO
: prefix
:
mkdir build;
ln -s ../../bootstrap.build ../../root.build build/;
cat <<EOI >=config.h.in;
#undef PREFIX_zzz_TEST_DUMMY1_H
#undef PREFIX_zzz_TEST_DUMMY2_H
EOI
$* <<EOI &config.h &config.h.d;
./: h{config}: in{config}
{
autoconf.prefix = PREFIX_
PREFIX_zzz_TEST_DUMMY2_H = '#define PREFIX_zzz_TEST_DUMMY2_H 2'
}
EOI
cat config.h >>EOO
#define PREFIX_zzz_TEST_DUMMY1_H 1
#define PREFIX_zzz_TEST_DUMMY2_H 2
EOO

View File

@ -39,6 +39,10 @@ namespace build2
// `cmake`, and `meson`.
//
vp.insert<string> ("autoconf.flavor");
// Built-in checks prefix.
//
vp.insert<string> ("autoconf.prefix");
}
// Register the rule.

View File

@ -1,5 +1,8 @@
#include <libbuild2/autoconf/rule.hxx>
#include <cstring> // strcmp()
#include <libbuild2/depdb.hxx>
#include <libbuild2/target.hxx>
#include <libbuild2/algorithm.hxx>
#include <libbuild2/diagnostics.hxx>
@ -19,11 +22,9 @@ namespace build2
struct match_data
{
autoconf::flavor flavor;
string prefix;
};
static_assert (sizeof (match_data) <= target::data_size,
"insufficient space");
rule::
rule ()
: in::rule ("autoconf.in 1",
@ -31,6 +32,8 @@ namespace build2
'@' /* symbol */,
false /* strict */)
{
static_assert (sizeof (match_data) <= target::data_size,
"insufficient space");
}
recipe rule::
@ -71,11 +74,41 @@ namespace build2
}
}
t.data (match_data {f});
// Get the prefix if any.
//
string p (cast_empty<string> (t["autoconf.prefix"]));
t.data (match_data {f, move (p)});
return r;
}
void rule::
perform_update_depdb (action, const target& t, depdb& dd) const
{
tracer trace ("autoconf::rule::perform_update_depdb");
const match_data& md (t.data<match_data> ());
// Then the flavor.
//
if (dd.expect (md.flavor == flavor::autoconf ? "autoconf" :
md.flavor == flavor::cmake ? "cmake" :
"meson") != nullptr)
l4 ([&]{trace << "flavor mismatch forcing update of" << t;});
// Then the prefix.
//
if (dd.expect (md.prefix) != nullptr)
l4 ([&]{trace << "prefix mismatch forcing update of" << t;});
// Note that at first it may seem necessary to add something like the
// full module version in lieu of the built-in checks database hash.
// Note, however, that any changes will be automatically taken care of
// (and with better precision) as part of the substituted variable value
// checks by in::rule.
}
void rule::
process (const location& l,
action a, const target& t,
@ -424,21 +457,63 @@ namespace build2
// possible. Since there is no way to undefine a buildfile variable
// (becasue we could always see a value from the outer scope), we
// will treat null as an indication to use the built-in check.
// While this clashes with the in.null semantics, it'ss just as
// While this clashes with the in.null semantics, it's just as
// easy to set the variable to the real default value as to null.
//
// 3. Return the build-in value form the catalog.
//
const check* e (checks + sizeof (checks) / sizeof (*checks));
const check* i (lower_bound (checks, e,
n,
[] (const check& c, const string& n)
{
return n.compare (c.name) > 0;
}));
const char* pn (nullptr); // Prefix-less name.
if (i != e && n == i->name && !t[n])
return i->value;
const string& p (t.data<match_data> ().prefix);
if (!p.empty ())
{
// Note that if there is no prefix, then we don't look for a
// built-in check.
//
if (n.size () > p.size () && n.compare (0, p.size (), p) == 0)
pn = n.c_str () + p.size ();
}
else
pn = n.c_str ();
if (pn != nullptr)
{
const check* e (checks + sizeof (checks) / sizeof (*checks));
const check* i (lower_bound (checks, e,
pn,
[] (const check& c, const char* pn)
{
return strcmp (c.name, pn) < 0;
}));
// Note: original name in lookup.
//
if (i != e && strcmp (i->name, pn) == 0 && !t[n])
{
string r (i->value);
// Add "back" the prefix.
//
if (!p.empty ())
{
auto sep = [] (char c) { return !alnum (c) && c != '_'; };
size_t m (n.size () - p.size ()); // Prefix-less name length.
for (size_t i (0); (i = r.find (pn, i)) != string::npos; i += m)
{
if ((i == 0 || sep (r[i - 1])) &&
(i + m == r.size () || sep (r[i + m])))
{
r.insert (i, p);
i += p.size ();
}
}
}
return r;
}
}
}
return in::rule::lookup (l, a, t, n, nullopt, null);

View File

@ -24,6 +24,9 @@ namespace build2
virtual recipe
apply (action, target&) const override;
virtual void
perform_update_depdb (action, const target&, depdb&) const override;
virtual void
process (const location&,
action, const target&,