Add support for checks reuse
Specifically: 1. A check's option name can now be marked as "unprefixable": `// <NAME>!`. 2. A check can now list a number of base checks: `// <NAME> : <BASE>...`. 3. Duplicate options are now automatically suppressed.
This commit is contained in:
parent
4e8713e8e9
commit
c588672b93
48
README.md
48
README.md
@ -14,8 +14,10 @@ program to check if the feature is present. Instead, they are set to static
|
|||||||
expected values based on the platform/compiler macro checks (see note at the
|
expected values based on the platform/compiler macro checks (see note at the
|
||||||
beginning of [Project Configuration][proj-config] for rationale).
|
beginning of [Project Configuration][proj-config] for rationale).
|
||||||
|
|
||||||
See [`libbuild2/autoconf/checks/`][checks] for the list of available build-in
|
See [`libbuild2/autoconf/checks/`][checks] for the list of available built-in
|
||||||
checks.
|
checks. Submit requests for new checks as issues. Submit implementations of
|
||||||
|
new checks (or any other improvements) as PRs or patches.
|
||||||
|
|
||||||
|
|
||||||
## Using in your projects
|
## Using in your projects
|
||||||
|
|
||||||
@ -74,7 +76,7 @@ h{config}: in{config}
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
This mechanism can also be used to override the build-in checks, for example:
|
This mechanism can also be used to override the built-in checks, for example:
|
||||||
|
|
||||||
```
|
```
|
||||||
h{config}: in{config}
|
h{config}: in{config}
|
||||||
@ -83,7 +85,7 @@ h{config}: in{config}
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The build-in checks can be prefixed in order to avoid clashes with similarly
|
The built-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
|
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
|
resulting header is public. To enable this, we specify the prefix with
|
||||||
the `autoconf.prefix` variable and then use the prefixed versions of
|
the `autoconf.prefix` variable and then use the prefixed versions of
|
||||||
@ -104,7 +106,7 @@ h{config}: in{config}
|
|||||||
```
|
```
|
||||||
|
|
||||||
Note that `autoconf.prefix` only affects the lookup of the built-in checks.
|
Note that `autoconf.prefix` only affects the lookup of the built-in checks.
|
||||||
Custom substitutions and overrides of build-in checks must include the
|
Custom substitutions and overrides of built-in checks must include the
|
||||||
prefix. For example:
|
prefix. For example:
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -116,24 +118,37 @@ h{config}: in{config}
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Note also that some built-in check names are *unprefixable*, usually because
|
||||||
|
they are standard macro names (for example, `BYTE_ORDER`) that on some
|
||||||
|
platforms come from system headers (for example, `<sys/endian.h>` on FreeBSD).
|
||||||
|
Such checks have `!` after their names on the first line of their
|
||||||
|
implementation files (for example, `// BYTE_ORDER!`).
|
||||||
|
|
||||||
|
|
||||||
## Adding new checks
|
## Adding new checks
|
||||||
|
|
||||||
To add a check for a new configuration option `<NAME>` simply create the
|
To add a check for a new configuration option `<NAME>` simply create the
|
||||||
`<NAME>.h` header file with the corresponding check and place it into
|
`<NAME>.h` header file (preserving the case) with the corresponding check and
|
||||||
[`libbuild2/autoconf/checks/`][checks] (use existing checks for inspiration).
|
place it into [`libbuild2/autoconf/checks/`][checks] (use existing checks for
|
||||||
|
inspiration).
|
||||||
|
|
||||||
The first line in this header file must be in the form:
|
The first line in this header file must be in the form:
|
||||||
|
|
||||||
```
|
```
|
||||||
// <NAME>
|
// <NAME>[!] [: <BASE>...]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If the name is followed by the `!` modifier, then it is *unprefixable* (see
|
||||||
|
the previous section for detail). The name can also be followed by `:` and a
|
||||||
|
list of base checks. Such checks are automatically inserted before the rest of
|
||||||
|
the lines in the resulting substitution.
|
||||||
|
|
||||||
Subsequent lines should be C-style comments or preprocessor directives that
|
Subsequent lines should be C-style comments or preprocessor directives that
|
||||||
`#define` or `#undef` `<NAME>` depending on whether the feature is available
|
`#define` or `#undef` `<NAME>` depending on whether the feature is available
|
||||||
(though there can be idiosyncrasies; see `const.h`, for example). Note that
|
(though there can be idiosyncrasies; see `const.h`, for example). Note that
|
||||||
there should be no double-quotes or backslashes except for line
|
there should be no double-quotes or backslashes except for line
|
||||||
continuations. For example:
|
continuations. For example, to add a check for option `HAVE_BAR`, we could
|
||||||
|
create the `HAVE_BAR.h` header file with the following content:
|
||||||
|
|
||||||
```
|
```
|
||||||
// HAVE_BAR
|
// HAVE_BAR
|
||||||
@ -148,10 +163,17 @@ continuations. For example:
|
|||||||
```
|
```
|
||||||
|
|
||||||
Note also that the module implementation may need to replace `<NAME>` with its
|
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).
|
prefixed version (unless it is unprefixable) if the `autoconf.prefix`
|
||||||
This is done by textually substituting every occurrence of `<NAME>` that is
|
functionality is in use (see above). This is done by textually substituting
|
||||||
separated on both left and right hand sides (that is, both characters
|
every occurrence of `<NAME>` that is separated on both left and right hand
|
||||||
immediately before and after `<NAME>` are not `[A-Za-z0-9_]`).
|
sides (that is, both characters immediately before and after `<NAME>` are not
|
||||||
|
`[A-Za-z0-9_]`).
|
||||||
|
|
||||||
|
Within a file duplicate checks are automatically suppressed. And if multiple
|
||||||
|
files are involved, then the user is expected to employ the `autoconf.prefix`
|
||||||
|
functionality to avoid clashes across files. However, this does not help
|
||||||
|
unprefixable names and, as a result, such checks should be implemented in
|
||||||
|
ways that deal with duplication (for example, include guards).
|
||||||
|
|
||||||
[module-in]: https://build2.org/build2/doc/build2-build-system-manual.xhtml#module-in
|
[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
|
[proj-config]: https://build2.org/build2/doc/build2-build-system-manual.xhtml#proj-config
|
||||||
|
@ -38,7 +38,10 @@ cat <<EOI >=config.h.in;
|
|||||||
#undef ZERO
|
#undef ZERO
|
||||||
#undef VALUE
|
#undef VALUE
|
||||||
|
|
||||||
# undef TRUE
|
#undef TRUE
|
||||||
|
#undef FALSE
|
||||||
|
|
||||||
|
# undef MAYBE
|
||||||
|
|
||||||
#undef CUSTOM_LINE
|
#undef CUSTOM_LINE
|
||||||
#undef CUSTOM_BLOCK
|
#undef CUSTOM_BLOCK
|
||||||
@ -54,6 +57,7 @@ $* <<EOI &config.h &config.h.d;
|
|||||||
ONE = 1
|
ONE = 1
|
||||||
ZERO = [uint64] 000
|
ZERO = [uint64] 000
|
||||||
VALUE = [uint64] 0123
|
VALUE = [uint64] 0123
|
||||||
|
MAYBE = true
|
||||||
|
|
||||||
CUSTOM_LINE = '#define CUSTOM 123'
|
CUSTOM_LINE = '#define CUSTOM 123'
|
||||||
CUSTOM_BLOCK = \
|
CUSTOM_BLOCK = \
|
||||||
@ -76,7 +80,10 @@ cat config.h >>EOO
|
|||||||
#undef ZERO
|
#undef ZERO
|
||||||
#define VALUE 123
|
#define VALUE 123
|
||||||
|
|
||||||
#define TRUE 1
|
/* TRUE already defined. */
|
||||||
|
/* FALSE already defined. */
|
||||||
|
|
||||||
|
#define MAYBE 1
|
||||||
|
|
||||||
#define CUSTOM 123
|
#define CUSTOM 123
|
||||||
/* Make sure we do not redefine CUSTOM. */
|
/* Make sure we do not redefine CUSTOM. */
|
||||||
@ -101,15 +108,22 @@ cat <<EOI >=config.h.cmake;
|
|||||||
#cmakedefine ZERO
|
#cmakedefine ZERO
|
||||||
#cmakedefine VALUE
|
#cmakedefine VALUE
|
||||||
|
|
||||||
# cmakedefine TRUE
|
#cmakedefine TRUE
|
||||||
|
#cmakedefine FALSE
|
||||||
|
|
||||||
|
# cmakedefine MAYBE
|
||||||
|
|
||||||
#cmakedefine CUSTOM_LINE
|
#cmakedefine CUSTOM_LINE
|
||||||
#cmakedefine CUSTOM_BLOCK
|
#cmakedefine CUSTOM_BLOCK
|
||||||
#cmakedefine CUSTOM_BLOCK @version@
|
#cmakedefine CUSTOM_BLOCK1 @version@
|
||||||
|
|
||||||
#cmakedefine TRUE true
|
#cmakedefine TRUE1 true
|
||||||
#cmakedefine FALSE false
|
#cmakedefine FALSE1 false
|
||||||
#cmakedefine VALUE @VALUE@ /* @version@ */
|
#cmakedefine VALUE1 @VALUE1@ /* @version@ */
|
||||||
|
|
||||||
|
#cmakedefine TRUE1 true
|
||||||
|
#cmakedefine FALSE1 false
|
||||||
|
#cmakedefine VALUE1 @VALUE1@ /* @version@ */
|
||||||
|
|
||||||
#cmakedefine zzz_TEST_DUMMY1_H @zzz_TEST_DUMMY1_H@
|
#cmakedefine zzz_TEST_DUMMY1_H @zzz_TEST_DUMMY1_H@
|
||||||
#cmakedefine zzz_TEST_DUMMY2_H
|
#cmakedefine zzz_TEST_DUMMY2_H
|
||||||
@ -122,6 +136,7 @@ $* <<EOI &config.h &config.h.d;
|
|||||||
ONE = 1
|
ONE = 1
|
||||||
ZERO = [uint64] 000
|
ZERO = [uint64] 000
|
||||||
VALUE = [uint64] 0123
|
VALUE = [uint64] 0123
|
||||||
|
MAYBE = true
|
||||||
|
|
||||||
CUSTOM_LINE = '#define CUSTOM 123'
|
CUSTOM_LINE = '#define CUSTOM 123'
|
||||||
CUSTOM_BLOCK = \
|
CUSTOM_BLOCK = \
|
||||||
@ -132,6 +147,18 @@ $* <<EOI &config.h &config.h.d;
|
|||||||
#endif
|
#endif
|
||||||
'
|
'
|
||||||
|
|
||||||
|
CUSTOM_BLOCK1 = \
|
||||||
|
'
|
||||||
|
/* Make sure we do not redefine CUSTOM1. */
|
||||||
|
#ifndef CUSTOM1
|
||||||
|
# define CUSTOM1 123
|
||||||
|
#endif
|
||||||
|
'
|
||||||
|
|
||||||
|
TRUE1 = true
|
||||||
|
FALSE1 = [bool] false
|
||||||
|
VALUE1 = [uint64] 0123
|
||||||
|
|
||||||
zzz_TEST_DUMMY2_H = '#define zzz_TEST_DUMMY2_H 2'
|
zzz_TEST_DUMMY2_H = '#define zzz_TEST_DUMMY2_H 2'
|
||||||
}
|
}
|
||||||
EOI
|
EOI
|
||||||
@ -144,21 +171,28 @@ cat config.h >>EOO
|
|||||||
#undef ZERO
|
#undef ZERO
|
||||||
#define VALUE 123
|
#define VALUE 123
|
||||||
|
|
||||||
#define TRUE 1
|
/* TRUE already defined. */
|
||||||
|
/* FALSE already defined. */
|
||||||
|
|
||||||
|
#define MAYBE 1
|
||||||
|
|
||||||
#define CUSTOM 123
|
#define CUSTOM 123
|
||||||
/* Make sure we do not redefine CUSTOM. */
|
/* Make sure we do not redefine CUSTOM. */
|
||||||
#ifndef CUSTOM
|
#ifndef CUSTOM
|
||||||
# define CUSTOM 123
|
# define CUSTOM 123
|
||||||
#endif
|
#endif
|
||||||
/* Make sure we do not redefine CUSTOM. */
|
/* Make sure we do not redefine CUSTOM1. */
|
||||||
#ifndef CUSTOM
|
#ifndef CUSTOM1
|
||||||
# define CUSTOM 123
|
# define CUSTOM1 123
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define TRUE true
|
#define TRUE1 true
|
||||||
#undef FALSE
|
#undef FALSE1
|
||||||
#define VALUE 123 /* 1.2.3 */
|
#define VALUE1 123 /* 1.2.3 */
|
||||||
|
|
||||||
|
/* TRUE1 already defined. */
|
||||||
|
/* FALSE1 already defined. */
|
||||||
|
/* VALUE1 already defined. */
|
||||||
|
|
||||||
#define zzz_TEST_DUMMY1_H 1
|
#define zzz_TEST_DUMMY1_H 1
|
||||||
#define zzz_TEST_DUMMY2_H 2
|
#define zzz_TEST_DUMMY2_H 2
|
||||||
@ -177,7 +211,10 @@ cat <<EOI >=config.h.in;
|
|||||||
#mesondefine ZERO
|
#mesondefine ZERO
|
||||||
#mesondefine VALUE
|
#mesondefine VALUE
|
||||||
|
|
||||||
# mesondefine TRUE
|
#mesondefine TRUE
|
||||||
|
#mesondefine FALSE
|
||||||
|
|
||||||
|
# mesondefine MAYBE
|
||||||
|
|
||||||
#mesondefine CUSTOM_LINE
|
#mesondefine CUSTOM_LINE
|
||||||
#mesondefine CUSTOM_BLOCK
|
#mesondefine CUSTOM_BLOCK
|
||||||
@ -195,6 +232,7 @@ $* <<EOI &config.h &config.h.d;
|
|||||||
ONE = 1
|
ONE = 1
|
||||||
ZERO = [uint64] 000
|
ZERO = [uint64] 000
|
||||||
VALUE = [uint64] 0123
|
VALUE = [uint64] 0123
|
||||||
|
MAYBE = true
|
||||||
|
|
||||||
CUSTOM_LINE = '#define CUSTOM 123'
|
CUSTOM_LINE = '#define CUSTOM 123'
|
||||||
CUSTOM_BLOCK = \
|
CUSTOM_BLOCK = \
|
||||||
@ -217,7 +255,10 @@ cat config.h >>EOO
|
|||||||
#undef ZERO
|
#undef ZERO
|
||||||
#define VALUE 123
|
#define VALUE 123
|
||||||
|
|
||||||
#define TRUE 1
|
/* TRUE already defined. */
|
||||||
|
/* FALSE already defined. */
|
||||||
|
|
||||||
|
#define MAYBE 1
|
||||||
|
|
||||||
#define CUSTOM 123
|
#define CUSTOM 123
|
||||||
/* Make sure we do not redefine CUSTOM. */
|
/* Make sure we do not redefine CUSTOM. */
|
||||||
@ -236,6 +277,7 @@ ln -s ../../bootstrap.build ../../root.build build/;
|
|||||||
cat <<EOI >=config.h.in;
|
cat <<EOI >=config.h.in;
|
||||||
#undef PREFIX_zzz_TEST_DUMMY1_H
|
#undef PREFIX_zzz_TEST_DUMMY1_H
|
||||||
#undef PREFIX_zzz_TEST_DUMMY2_H
|
#undef PREFIX_zzz_TEST_DUMMY2_H
|
||||||
|
#undef zzz_TEST_DUMMY3_H
|
||||||
EOI
|
EOI
|
||||||
$* <<EOI &config.h &config.h.d;
|
$* <<EOI &config.h &config.h.d;
|
||||||
./: h{config}: in{config}
|
./: h{config}: in{config}
|
||||||
@ -248,4 +290,114 @@ $* <<EOI &config.h &config.h.d;
|
|||||||
cat config.h >>EOO
|
cat config.h >>EOO
|
||||||
#define PREFIX_zzz_TEST_DUMMY1_H 1
|
#define PREFIX_zzz_TEST_DUMMY1_H 1
|
||||||
#define PREFIX_zzz_TEST_DUMMY2_H 2
|
#define PREFIX_zzz_TEST_DUMMY2_H 2
|
||||||
|
#define zzz_TEST_DUMMY3_H 1
|
||||||
|
EOO
|
||||||
|
|
||||||
|
: base
|
||||||
|
:
|
||||||
|
mkdir build;
|
||||||
|
ln -s ../../bootstrap.build ../../root.build build/;
|
||||||
|
cat <<EOI >=config.h.in;
|
||||||
|
#undef PREFIX_zzz_TEST_DUMMY4_H
|
||||||
|
EOI
|
||||||
|
$* <<EOI &config.h &config.h.d;
|
||||||
|
./: h{config}: in{config}
|
||||||
|
{
|
||||||
|
autoconf.prefix = PREFIX_
|
||||||
|
}
|
||||||
|
EOI
|
||||||
|
cat config.h >>EOO
|
||||||
|
#define PREFIX_zzz_TEST_DUMMY1_H 1
|
||||||
|
|
||||||
|
#define zzz_TEST_DUMMY3_H 1
|
||||||
|
|
||||||
|
#ifdef PREFIX_zzz_TEST_DUMMY1_H
|
||||||
|
# define PREFIX_zzz_TEST_DUMMY4_H zzz_TEST_DUMMY3_H
|
||||||
|
#endif /*PREFIX_zzz_TEST_DUMMY1_H*/
|
||||||
|
EOO
|
||||||
|
|
||||||
|
: base-duplicate
|
||||||
|
:
|
||||||
|
mkdir build;
|
||||||
|
ln -s ../../bootstrap.build ../../root.build build/;
|
||||||
|
cat <<EOI >=config.h.in;
|
||||||
|
#undef PREFIX_zzz_TEST_DUMMY1_H
|
||||||
|
|
||||||
|
#undef zzz_TEST_DUMMY3_H
|
||||||
|
|
||||||
|
#undef PREFIX_zzz_TEST_DUMMY4_H
|
||||||
|
EOI
|
||||||
|
$* <<EOI &config.h &config.h.d;
|
||||||
|
./: h{config}: in{config}
|
||||||
|
{
|
||||||
|
autoconf.prefix = PREFIX_
|
||||||
|
}
|
||||||
|
EOI
|
||||||
|
cat config.h >>EOO
|
||||||
|
#define PREFIX_zzz_TEST_DUMMY1_H 1
|
||||||
|
|
||||||
|
#define zzz_TEST_DUMMY3_H 1
|
||||||
|
|
||||||
|
/* Base PREFIX_zzz_TEST_DUMMY1_H already defined. */
|
||||||
|
|
||||||
|
/* Base zzz_TEST_DUMMY3_H already defined. */
|
||||||
|
|
||||||
|
#ifdef PREFIX_zzz_TEST_DUMMY1_H
|
||||||
|
# define PREFIX_zzz_TEST_DUMMY4_H zzz_TEST_DUMMY3_H
|
||||||
|
#endif /*PREFIX_zzz_TEST_DUMMY1_H*/
|
||||||
|
EOO
|
||||||
|
|
||||||
|
: base-custom
|
||||||
|
:
|
||||||
|
mkdir build;
|
||||||
|
ln -s ../../bootstrap.build ../../root.build build/;
|
||||||
|
cat <<EOI >=config.h.in;
|
||||||
|
#undef PREFIX_zzz_TEST_DUMMY4_H
|
||||||
|
EOI
|
||||||
|
$* <<EOI &config.h &config.h.d;
|
||||||
|
./: h{config}: in{config}
|
||||||
|
{
|
||||||
|
autoconf.prefix = PREFIX_
|
||||||
|
|
||||||
|
PREFIX_zzz_TEST_DUMMY1_H = '#define PREFIX_zzz_TEST_DUMMY1_H 10'
|
||||||
|
zzz_TEST_DUMMY3_H = '#define zzz_TEST_DUMMY3_H 30'
|
||||||
|
}
|
||||||
|
EOI
|
||||||
|
cat config.h >>EOO
|
||||||
|
#define PREFIX_zzz_TEST_DUMMY1_H 10
|
||||||
|
|
||||||
|
#define zzz_TEST_DUMMY3_H 30
|
||||||
|
|
||||||
|
#ifdef PREFIX_zzz_TEST_DUMMY1_H
|
||||||
|
# define PREFIX_zzz_TEST_DUMMY4_H zzz_TEST_DUMMY3_H
|
||||||
|
#endif /*PREFIX_zzz_TEST_DUMMY1_H*/
|
||||||
|
EOO
|
||||||
|
|
||||||
|
: base-recursive
|
||||||
|
:
|
||||||
|
mkdir build;
|
||||||
|
ln -s ../../bootstrap.build ../../root.build build/;
|
||||||
|
cat <<EOI >=config.h.in;
|
||||||
|
#undef PREFIX_zzz_TEST_DUMMY5_H
|
||||||
|
EOI
|
||||||
|
$* <<EOI &config.h &config.h.d;
|
||||||
|
./: h{config}: in{config}
|
||||||
|
{
|
||||||
|
autoconf.prefix = PREFIX_
|
||||||
|
}
|
||||||
|
EOI
|
||||||
|
cat config.h >>EOO
|
||||||
|
#define zzz_TEST_DUMMY3_H 1
|
||||||
|
|
||||||
|
#define PREFIX_zzz_TEST_DUMMY1_H 1
|
||||||
|
|
||||||
|
/* Base zzz_TEST_DUMMY3_H already defined. */
|
||||||
|
|
||||||
|
#ifdef PREFIX_zzz_TEST_DUMMY1_H
|
||||||
|
# define PREFIX_zzz_TEST_DUMMY4_H zzz_TEST_DUMMY3_H
|
||||||
|
#endif /*PREFIX_zzz_TEST_DUMMY1_H*/
|
||||||
|
|
||||||
|
#ifdef zzz_TEST_DUMMY3_H
|
||||||
|
# define PREFIX_zzz_TEST_DUMMY5_H PREFIX_zzz_TEST_DUMMY4_H
|
||||||
|
#endif /*zzz_TEST_DUMMY3_H*/
|
||||||
EOO
|
EOO
|
||||||
|
@ -8,7 +8,7 @@ lib{build2-autoconf}: {hxx ixx txx cxx}{* -checks} {hxx cxx}{checks} \
|
|||||||
$impl_libs $intf_libs
|
$impl_libs $intf_libs
|
||||||
|
|
||||||
# - File name must be the exact variable name plus .h (used for sorting).
|
# - File name must be the exact variable name plus .h (used for sorting).
|
||||||
# - First line in the file should be `// <NAME>`.
|
# - First line in the file should be `// <NAME> [<MODIFIER>] [:<BASE>...]`.
|
||||||
# - No double-quote or backslash escaping except for line continuations.
|
# - No double-quote or backslash escaping except for line continuations.
|
||||||
#
|
#
|
||||||
# NOTE: remember to update README.md if changing anything here.
|
# NOTE: remember to update README.md if changing anything here.
|
||||||
@ -28,11 +28,11 @@ lib{build2-autoconf}: {hxx ixx txx cxx}{* -checks} {hxx cxx}{checks} \
|
|||||||
h = $path($>[0])
|
h = $path($>[0])
|
||||||
s = $path($>[1])
|
s = $path($>[1])
|
||||||
|
|
||||||
# We regularize the output with a dummy start entry plus add two end dummies
|
# We regularize the output with a dummy start entry plus add five end
|
||||||
# that are used in tests.
|
# dummies that are used in tests.
|
||||||
#
|
#
|
||||||
n = $size($<)
|
n = $size($<)
|
||||||
n += 3
|
n += 6
|
||||||
|
|
||||||
cat <<"EOI" >$h
|
cat <<"EOI" >$h
|
||||||
namespace build2
|
namespace build2
|
||||||
@ -42,6 +42,8 @@ lib{build2-autoconf}: {hxx ixx txx cxx}{* -checks} {hxx cxx}{checks} \
|
|||||||
struct check
|
struct check
|
||||||
{
|
{
|
||||||
const char* name;
|
const char* name;
|
||||||
|
const char* modifier; // ! or empty
|
||||||
|
const char* base; // base names or empty
|
||||||
const char* value;
|
const char* value;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -55,13 +57,13 @@ lib{build2-autoconf}: {hxx ixx txx cxx}{* -checks} {hxx cxx}{checks} \
|
|||||||
|
|
||||||
const build2::autoconf::check build2::autoconf::checks[$n] = {
|
const build2::autoconf::check build2::autoconf::checks[$n] = {
|
||||||
{
|
{
|
||||||
\"\",
|
\"\", \"\", \"\",
|
||||||
|
|
||||||
\"\"
|
\"\"
|
||||||
EOI
|
EOI
|
||||||
|
|
||||||
cat $i | sed -n \
|
cat $i | sed -n \
|
||||||
-e 's|^// ([^ ]+) *$|},\n\n{\n"\1",\n|p' \
|
-e 's|^// ([^ !:]+) *(!)? *(: *(( *[^ ]+)+))? *$|},\n\n{\n"\1", "\2", "\4",\n|p' \
|
||||||
-e 's|^(.*)\\$|"\1\\\\\\n"|p' \
|
-e 's|^(.*)\\$|"\1\\\\\\n"|p' \
|
||||||
-e 's|^(.*)$|"\1\\n"|p' \
|
-e 's|^(.*)$|"\1\\n"|p' \
|
||||||
- >>$s
|
- >>$s
|
||||||
@ -70,15 +72,37 @@ lib{build2-autoconf}: {hxx ixx txx cxx}{* -checks} {hxx cxx}{checks} \
|
|||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"zzz_TEST_DUMMY1_H",
|
"zzz_TEST_DUMMY1_H", "", "",
|
||||||
|
|
||||||
"#define zzz_TEST_DUMMY1_H 1"
|
"#define zzz_TEST_DUMMY1_H 1\n"
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"zzz_TEST_DUMMY2_H",
|
"zzz_TEST_DUMMY2_H", "", "",
|
||||||
|
|
||||||
"#define zzz_TEST_DUMMY2_H 1"
|
"#define zzz_TEST_DUMMY2_H 1\n"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"zzz_TEST_DUMMY3_H", "!", "",
|
||||||
|
|
||||||
|
"#define zzz_TEST_DUMMY3_H 1\n"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"zzz_TEST_DUMMY4_H", "", "zzz_TEST_DUMMY1_H zzz_TEST_DUMMY3_H",
|
||||||
|
|
||||||
|
"#ifdef zzz_TEST_DUMMY1_H\n"
|
||||||
|
"# define zzz_TEST_DUMMY4_H zzz_TEST_DUMMY3_H\n"
|
||||||
|
"#endif /*zzz_TEST_DUMMY1_H*/\n"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"zzz_TEST_DUMMY5_H", "", "zzz_TEST_DUMMY3_H zzz_TEST_DUMMY4_H",
|
||||||
|
|
||||||
|
"#ifdef zzz_TEST_DUMMY3_H\n"
|
||||||
|
"# define zzz_TEST_DUMMY5_H zzz_TEST_DUMMY4_H\n"
|
||||||
|
"#endif /*zzz_TEST_DUMMY3_H*/\n"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
EOI
|
EOI
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#include <libbuild2/autoconf/rule.hxx>
|
#include <libbuild2/autoconf/rule.hxx>
|
||||||
|
|
||||||
#include <cstring> // strcmp()
|
#include <cstring> // strcmp(), strchr()
|
||||||
|
|
||||||
#include <libbuild2/depdb.hxx>
|
#include <libbuild2/depdb.hxx>
|
||||||
#include <libbuild2/target.hxx>
|
#include <libbuild2/target.hxx>
|
||||||
@ -23,6 +23,7 @@ namespace build2
|
|||||||
{
|
{
|
||||||
autoconf::flavor flavor;
|
autoconf::flavor flavor;
|
||||||
string prefix;
|
string prefix;
|
||||||
|
map<string, string> checks; // Checks already seen.
|
||||||
};
|
};
|
||||||
|
|
||||||
rule::
|
rule::
|
||||||
@ -78,7 +79,7 @@ namespace build2
|
|||||||
//
|
//
|
||||||
string p (cast_empty<string> (t["autoconf.prefix"]));
|
string p (cast_empty<string> (t["autoconf.prefix"]));
|
||||||
|
|
||||||
t.data (match_data {f, move (p)});
|
t.data (match_data {f, move (p), {}});
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
@ -174,22 +175,35 @@ namespace build2
|
|||||||
string& v (*ov);
|
string& v (*ov);
|
||||||
|
|
||||||
// As an extension, we allow replacing the entire line with a
|
// As an extension, we allow replacing the entire line with a
|
||||||
// potentially multi-line block of preprocessor directives. To detect
|
// potentially multi-line block of preprocessor directives or a
|
||||||
// this, we look for a line that starts with `#` after whitespaces.
|
// single-line C comment (used to suppress duplicates). To detect the
|
||||||
|
// former, we look for a line that starts with `#` after whitespaces.
|
||||||
|
// To detect the latter, we look for a single line that starts with
|
||||||
|
// `/*`.
|
||||||
//
|
//
|
||||||
size_t p (0);
|
size_t p (0);
|
||||||
for (;; ++p)
|
for (;; ++p)
|
||||||
{
|
{
|
||||||
|
bool c (p == 0);
|
||||||
|
|
||||||
skip_ws (v, p);
|
skip_ws (v, p);
|
||||||
|
|
||||||
if (v[p] == '#')
|
if (v[p] == '#')
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
if (c)
|
||||||
|
c = (v[p] == '/' && v[p + 1] == '*');
|
||||||
|
|
||||||
p = v.find ('\n', p);
|
p = v.find ('\n', p);
|
||||||
|
|
||||||
if (p == string::npos)
|
if (p == string::npos)
|
||||||
|
{
|
||||||
|
if (c)
|
||||||
|
p = 0;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
optional<bool> r;
|
optional<bool> r;
|
||||||
if (p != string::npos)
|
if (p != string::npos)
|
||||||
@ -444,12 +458,24 @@ namespace build2
|
|||||||
{
|
{
|
||||||
assert (*flags == 1);
|
assert (*flags == 1);
|
||||||
|
|
||||||
|
match_data& md (t.data<match_data> ());
|
||||||
|
|
||||||
// If this is a special substitution, then look in our catalog of
|
// If this is a special substitution, then look in our catalog of
|
||||||
// built-in checks. Specifically, the plan is as follows:
|
// built-in checks. Specifically, the plan is as follows:
|
||||||
//
|
//
|
||||||
// 1. Look in the catalog and fall through if not found.
|
// 1. Suppress if a duplicate (we do it regardless of whether it is
|
||||||
|
// from the catalog or custom).
|
||||||
//
|
//
|
||||||
// 2. If found, then check for a custom value falling through if
|
// Which name should we store, prefixed or prefixless? If we store
|
||||||
|
// prefixed, then it won't be easy to match bases which are
|
||||||
|
// specified without a prefix: we will have to add the prefix
|
||||||
|
// unless the check is unprefixable, which we can only know by
|
||||||
|
// looking it up. So we store prefixless. Actually, it's convenient
|
||||||
|
// to store both.
|
||||||
|
//
|
||||||
|
// 2. Look in the catalog and fall through if not found.
|
||||||
|
//
|
||||||
|
// 3. If found, then check for a custom value falling through if
|
||||||
// found.
|
// found.
|
||||||
//
|
//
|
||||||
// Here things get a bit tricky: while a stray HAVE_* buildfile
|
// Here things get a bit tricky: while a stray HAVE_* buildfile
|
||||||
@ -460,15 +486,15 @@ namespace build2
|
|||||||
// While this clashes with the in.null semantics, it's 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.
|
// easy to set the variable to the real default value as to null.
|
||||||
//
|
//
|
||||||
// 3. Return the build-in value form the catalog.
|
// 4. Return the build-in value from the catalog.
|
||||||
//
|
//
|
||||||
const char* pn (nullptr); // Prefix-less name.
|
const char* pn (nullptr); // Prefixless name.
|
||||||
|
|
||||||
const string& p (t.data<match_data> ().prefix);
|
const string& p (md.prefix);
|
||||||
if (!p.empty ())
|
if (!p.empty ())
|
||||||
{
|
{
|
||||||
// Note that if there is no prefix, then we don't look for a
|
// Note that if there is no prefix, then we only look for an
|
||||||
// built-in check.
|
// unprefixable built-in check.
|
||||||
//
|
//
|
||||||
if (n.size () > p.size () && n.compare (0, p.size (), p) == 0)
|
if (n.size () > p.size () && n.compare (0, p.size (), p) == 0)
|
||||||
pn = n.c_str () + p.size ();
|
pn = n.c_str () + p.size ();
|
||||||
@ -476,31 +502,69 @@ namespace build2
|
|||||||
else
|
else
|
||||||
pn = n.c_str ();
|
pn = n.c_str ();
|
||||||
|
|
||||||
if (pn != nullptr)
|
const char* en (pn != nullptr ? pn : n.c_str ()); // Effective name.
|
||||||
|
|
||||||
|
// Note: this line must be recognizable by substitute_special().
|
||||||
|
//
|
||||||
|
if (md.checks.find (en) != md.checks.end ())
|
||||||
|
return "/* " + n + " already defined. */";
|
||||||
|
|
||||||
|
md.checks.emplace (en, n);
|
||||||
|
|
||||||
|
// If up is true, only look for an unprefixable check.
|
||||||
|
//
|
||||||
|
auto find = [] (const char* n, bool up = false) -> const check*
|
||||||
{
|
{
|
||||||
const check* e (checks + sizeof (checks) / sizeof (*checks));
|
const check* e (checks + sizeof (checks) / sizeof (*checks));
|
||||||
const check* i (lower_bound (checks, e,
|
const check* i (lower_bound (checks, e,
|
||||||
pn,
|
n,
|
||||||
[] (const check& c, const char* pn)
|
[] (const check& c, const char* n)
|
||||||
{
|
{
|
||||||
return strcmp (c.name, pn) < 0;
|
return strcmp (c.name, n) < 0;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Note: original name in lookup.
|
return (i != e &&
|
||||||
//
|
strcmp (i->name, n) == 0 &&
|
||||||
if (i != e && strcmp (i->name, pn) == 0 && !t[n])
|
(!up || strchr (i->modifier, '!') != nullptr)) ? i : nullptr;
|
||||||
{
|
};
|
||||||
string r (i->value);
|
|
||||||
|
|
||||||
// Add "back" the prefix.
|
// Note: original name in variable lookup.
|
||||||
//
|
//
|
||||||
if (!p.empty ())
|
const check* c (find (en, pn == nullptr));
|
||||||
|
|
||||||
|
if (c != nullptr && !t[n])
|
||||||
|
{
|
||||||
|
// The plan is as follows: keep adding base checks (suppressing
|
||||||
|
// duplicates) followed by the main check while prefixing all the
|
||||||
|
// already seen names (unless unprefixable).
|
||||||
|
//
|
||||||
|
string r;
|
||||||
|
small_vector<string, 1> ns;
|
||||||
|
|
||||||
|
auto append = [&r] (const char* v)
|
||||||
|
{
|
||||||
|
// Separate values with a blank line.
|
||||||
|
//
|
||||||
|
if (!r.empty ())
|
||||||
|
{
|
||||||
|
if (r.back () != '\n')
|
||||||
|
r += '\n';
|
||||||
|
|
||||||
|
r += '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
r += v;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto prefix = [&p, &ns, &r, b = size_t (0)] () mutable
|
||||||
{
|
{
|
||||||
auto sep = [] (char c) { return !alnum (c) && c != '_'; };
|
auto sep = [] (char c) { return !alnum (c) && c != '_'; };
|
||||||
|
|
||||||
size_t m (n.size () - p.size ()); // Prefix-less name length.
|
for (const string& n: ns)
|
||||||
|
{
|
||||||
|
size_t m (n.size ()); // Prefix-less name length.
|
||||||
|
|
||||||
for (size_t i (0); (i = r.find (pn, i)) != string::npos; i += m)
|
for (size_t i (b); (i = r.find (n, i)) != string::npos; i += m)
|
||||||
{
|
{
|
||||||
if ((i == 0 || sep (r[i - 1])) &&
|
if ((i == 0 || sep (r[i - 1])) &&
|
||||||
(i + m == r.size () || sep (r[i + m])))
|
(i + m == r.size () || sep (r[i + m])))
|
||||||
@ -511,9 +575,109 @@ namespace build2
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return r;
|
b = r.size ();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Base checks.
|
||||||
|
//
|
||||||
|
// @@ TODO: detect cycles (currently we just prune, like an
|
||||||
|
// include guard).
|
||||||
|
//
|
||||||
|
// @@ TODO: add special mode (autoconf.test=true) that verifies
|
||||||
|
// on module load that all the bases are resolvable.
|
||||||
|
// Then add a test that triggers it (ideally we would
|
||||||
|
// want this at build-time, but that won't be easy in
|
||||||
|
// Buildscript).
|
||||||
|
//
|
||||||
|
auto base = [this,
|
||||||
|
&l, &t, a, &null,
|
||||||
|
&md, &p, &ns,
|
||||||
|
&find, &append, &prefix] (const string& n,
|
||||||
|
const char* bs,
|
||||||
|
const auto& base) -> void
|
||||||
|
{
|
||||||
|
auto df = make_diag_frame (
|
||||||
|
[&n] (const diag_record& dr)
|
||||||
|
{
|
||||||
|
dr << info << "while resolving base options for " << n;
|
||||||
|
});
|
||||||
|
|
||||||
|
for (size_t b (0), e (0); next_word (bs, b, e); )
|
||||||
|
{
|
||||||
|
string pn (bs, b, e - b); // Prefixless name.
|
||||||
|
|
||||||
|
// First supress duplicates.
|
||||||
|
//
|
||||||
|
{
|
||||||
|
auto i (md.checks.find (pn));
|
||||||
|
if (i != md.checks.end ())
|
||||||
|
{
|
||||||
|
append (("/* Base " +
|
||||||
|
i->second +
|
||||||
|
" already defined. */").c_str ());
|
||||||
|
|
||||||
|
if (pn != i->second)
|
||||||
|
ns.push_back (move (pn));
|
||||||
|
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const check* c (find (pn.c_str ()));
|
||||||
|
|
||||||
|
// While it may be overridden by the user, we should also have
|
||||||
|
// the entry in the built-in catalog (where we get the
|
||||||
|
// unprefixable flag).
|
||||||
|
//
|
||||||
|
if (c == nullptr)
|
||||||
|
fail (l) << "unknown base option " << pn;
|
||||||
|
|
||||||
|
// Derive the prefixed name.
|
||||||
|
//
|
||||||
|
bool up (strchr (c->modifier, '!') != nullptr);
|
||||||
|
|
||||||
|
string n ((up ? string () : p) + pn);
|
||||||
|
|
||||||
|
md.checks.emplace (pn, n);
|
||||||
|
|
||||||
|
if (t[n])
|
||||||
|
append (
|
||||||
|
this->in::rule::lookup (l, a, t, n, nullopt, null).c_str ());
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (*c->base != '\0')
|
||||||
|
base (n, c->base, base);
|
||||||
|
|
||||||
|
append (c->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!p.empty ())
|
||||||
|
{
|
||||||
|
if (!up)
|
||||||
|
ns.push_back (move (pn));
|
||||||
|
|
||||||
|
prefix ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (*c->base != '\0')
|
||||||
|
base (n, c->base, base);
|
||||||
|
|
||||||
|
// Main check.
|
||||||
|
//
|
||||||
|
append (c->value);
|
||||||
|
|
||||||
|
if (!p.empty ())
|
||||||
|
{
|
||||||
|
if (pn != nullptr) // Not unprefixable.
|
||||||
|
ns.push_back (pn);
|
||||||
|
|
||||||
|
prefix ();
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return in::rule::lookup (l, a, t, n, nullopt, null);
|
return in::rule::lookup (l, a, t, n, nullopt, null);
|
||||||
|
Loading…
Reference in New Issue
Block a user