Initial module implementation
At this stage it handles Autoconf (#undef), CMake (#cmakedefine), and Meson (#mesondefine) special lines but does not have any built-in tests.
This commit is contained in:
parent
19fcde9ddc
commit
ef54e197e9
@ -8,8 +8,8 @@ CMake and Meson variants.
|
||||
|
||||
Similar to Autoconf, the module provides built-in support for a number of
|
||||
common `HAVE_*` configuration options. However, the values of these options
|
||||
are not discovered by probing, such as trying to compile a test program to
|
||||
check if the header is present. Instead, they are set to the expected values
|
||||
based on the platform/compiler macros.
|
||||
are not discovered by dynamic probing, such as trying to compile a test
|
||||
program to check if the header is present. Instead, they are set to static
|
||||
expected values based on the platform/compiler macros.
|
||||
|
||||
[module-in]: https://build2.org/build2/doc/build2-build-system-manual.xhtml#module-in
|
||||
|
24
libbuild2-autoconf-tests/.gitignore
vendored
Normal file
24
libbuild2-autoconf-tests/.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
# Compiler/linker output.
|
||||
#
|
||||
*.d
|
||||
*.t
|
||||
*.i
|
||||
*.i.*
|
||||
*.ii
|
||||
*.ii.*
|
||||
*.o
|
||||
*.obj
|
||||
*.gcm
|
||||
*.pcm
|
||||
*.ifc
|
||||
*.so
|
||||
*.dll
|
||||
*.a
|
||||
*.lib
|
||||
*.exp
|
||||
*.pdb
|
||||
*.ilk
|
||||
*.exe
|
||||
*.exe.dlls/
|
||||
*.exe.manifest
|
||||
*.pc
|
1
libbuild2-autoconf-tests/AUTHORS
Symbolic link
1
libbuild2-autoconf-tests/AUTHORS
Symbolic link
@ -0,0 +1 @@
|
||||
../AUTHORS
|
1
libbuild2-autoconf-tests/LICENSE
Symbolic link
1
libbuild2-autoconf-tests/LICENSE
Symbolic link
@ -0,0 +1 @@
|
||||
../LICENSE
|
3
libbuild2-autoconf-tests/README.md
Normal file
3
libbuild2-autoconf-tests/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# libbuild2-autoconf-tests
|
||||
|
||||
Tests package for the Autoconf emulation build system module for `build2`.
|
1
libbuild2-autoconf-tests/basics/buildfile
Normal file
1
libbuild2-autoconf-tests/basics/buildfile
Normal file
@ -0,0 +1 @@
|
||||
./: testscript
|
208
libbuild2-autoconf-tests/basics/testscript
Normal file
208
libbuild2-autoconf-tests/basics/testscript
Normal file
@ -0,0 +1,208 @@
|
||||
# Note: --silent rather than --quiet to suppress module update diagnostics.
|
||||
#
|
||||
test.options += --no-default-options --serial-stop --silent --buildfile -
|
||||
|
||||
# We disable bdep auto-synchronization since we will potentially be updating
|
||||
# the module from multiple parallel tests. Note that we've made sure it is
|
||||
# up-to-date by pre-loading it in the tests project (see root.build for
|
||||
# details).
|
||||
#
|
||||
+export BDEP_SYNC=0
|
||||
|
||||
# Note that we are using the libbuild2-autoconf-tests project as our
|
||||
# amalgamation in order to get the module import location and config.c.
|
||||
#
|
||||
+cat <<EOI >=bootstrap.build
|
||||
project = basics
|
||||
#amalgamation =
|
||||
subprojects =
|
||||
|
||||
version = 1.2.3
|
||||
EOI
|
||||
|
||||
+cat <<EOI >=root.build
|
||||
using c
|
||||
using autoconf
|
||||
EOI
|
||||
|
||||
: basics-autoconf
|
||||
:
|
||||
mkdir build;
|
||||
ln -s ../../bootstrap.build ../../root.build build/;
|
||||
cat <<EOI >=config.h.in;
|
||||
#define VERSION "@version@"
|
||||
|
||||
#undef TRUE
|
||||
#undef FALSE
|
||||
#undef ONE
|
||||
#undef ZERO
|
||||
#undef VALUE
|
||||
|
||||
# undef TRUE
|
||||
|
||||
#undef CUSTOM_LINE
|
||||
#undef CUSTOM_BLOCK
|
||||
EOI
|
||||
$* <<EOI &config.h &config.h.d;
|
||||
./: h{config}: in{config}
|
||||
{
|
||||
TRUE = true
|
||||
FALSE = [bool] false
|
||||
ONE = 1
|
||||
ZERO = [uint64] 000
|
||||
VALUE = [uint64] 0123
|
||||
|
||||
CUSTOM_LINE = '#define CUSTOM 123'
|
||||
CUSTOM_BLOCK = \
|
||||
'
|
||||
/* Make sure we do not redefine CUSTOM. */
|
||||
#ifndef CUSTOM
|
||||
# define CUSTOM 123
|
||||
#endif
|
||||
'
|
||||
}
|
||||
EOI
|
||||
cat config.h >>EOO
|
||||
#define VERSION "1.2.3"
|
||||
|
||||
#define TRUE 1
|
||||
#undef FALSE
|
||||
#define ONE 1
|
||||
#undef ZERO
|
||||
#define VALUE 123
|
||||
|
||||
#define TRUE 1
|
||||
|
||||
#define CUSTOM 123
|
||||
/* Make sure we do not redefine CUSTOM. */
|
||||
#ifndef CUSTOM
|
||||
# define CUSTOM 123
|
||||
#endif
|
||||
EOO
|
||||
|
||||
: basics-cmake
|
||||
:
|
||||
mkdir build;
|
||||
ln -s ../../bootstrap.build ../../root.build build/;
|
||||
cat <<EOI >=config.h.in;
|
||||
#define VERSION "@version@"
|
||||
|
||||
#cmakedefine TRUE
|
||||
#cmakedefine FALSE
|
||||
#cmakedefine ONE
|
||||
#cmakedefine ZERO
|
||||
#cmakedefine VALUE
|
||||
|
||||
# cmakedefine TRUE
|
||||
|
||||
#cmakedefine CUSTOM_LINE
|
||||
#cmakedefine CUSTOM_BLOCK
|
||||
#cmakedefine CUSTOM_BLOCK @version@
|
||||
|
||||
#cmakedefine TRUE true
|
||||
#cmakedefine FALSE false
|
||||
#cmakedefine VALUE @VALUE@ /* @version@ */
|
||||
EOI
|
||||
$* <<EOI &config.h &config.h.d;
|
||||
./: h{config}: in{config}
|
||||
{
|
||||
autoconf.flavor = cmake
|
||||
|
||||
TRUE = true
|
||||
FALSE = [bool] false
|
||||
ONE = 1
|
||||
ZERO = [uint64] 000
|
||||
VALUE = [uint64] 0123
|
||||
|
||||
CUSTOM_LINE = '#define CUSTOM 123'
|
||||
CUSTOM_BLOCK = \
|
||||
'
|
||||
/* Make sure we do not redefine CUSTOM. */
|
||||
#ifndef CUSTOM
|
||||
# define CUSTOM 123
|
||||
#endif
|
||||
'
|
||||
}
|
||||
EOI
|
||||
cat config.h >>EOO
|
||||
#define VERSION "1.2.3"
|
||||
|
||||
#define TRUE 1
|
||||
#undef FALSE
|
||||
#define ONE 1
|
||||
#undef ZERO
|
||||
#define VALUE 123
|
||||
|
||||
#define TRUE 1
|
||||
|
||||
#define CUSTOM 123
|
||||
/* Make sure we do not redefine CUSTOM. */
|
||||
#ifndef CUSTOM
|
||||
# define CUSTOM 123
|
||||
#endif
|
||||
/* Make sure we do not redefine CUSTOM. */
|
||||
#ifndef CUSTOM
|
||||
# define CUSTOM 123
|
||||
#endif
|
||||
|
||||
#define TRUE true
|
||||
#undef FALSE
|
||||
#define VALUE 123 /* 1.2.3 */
|
||||
EOO
|
||||
|
||||
: basics-meson
|
||||
:
|
||||
mkdir build;
|
||||
ln -s ../../bootstrap.build ../../root.build build/;
|
||||
cat <<EOI >=config.h.in;
|
||||
#define VERSION "@version@"
|
||||
|
||||
#mesondefine TRUE
|
||||
#mesondefine FALSE
|
||||
#mesondefine ONE
|
||||
#mesondefine ZERO
|
||||
#mesondefine VALUE
|
||||
|
||||
# mesondefine TRUE
|
||||
|
||||
#mesondefine CUSTOM_LINE
|
||||
#mesondefine CUSTOM_BLOCK
|
||||
EOI
|
||||
$* <<EOI &config.h &config.h.d;
|
||||
./: h{config}: in{config}
|
||||
{
|
||||
autoconf.flavor = meson
|
||||
|
||||
TRUE = true
|
||||
FALSE = [bool] false
|
||||
ONE = 1
|
||||
ZERO = [uint64] 000
|
||||
VALUE = [uint64] 0123
|
||||
|
||||
CUSTOM_LINE = '#define CUSTOM 123'
|
||||
CUSTOM_BLOCK = \
|
||||
'
|
||||
/* Make sure we do not redefine CUSTOM. */
|
||||
#ifndef CUSTOM
|
||||
# define CUSTOM 123
|
||||
#endif
|
||||
'
|
||||
}
|
||||
EOI
|
||||
cat config.h >>EOO
|
||||
#define VERSION "1.2.3"
|
||||
|
||||
#define TRUE 1
|
||||
#undef FALSE
|
||||
#define ONE 1
|
||||
#undef ZERO
|
||||
#define VALUE 123
|
||||
|
||||
#define TRUE 1
|
||||
|
||||
#define CUSTOM 123
|
||||
/* Make sure we do not redefine CUSTOM. */
|
||||
#ifndef CUSTOM
|
||||
# define CUSTOM 123
|
||||
#endif
|
||||
EOO
|
4
libbuild2-autoconf-tests/build/.gitignore
vendored
Normal file
4
libbuild2-autoconf-tests/build/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
/config.build
|
||||
/root/
|
||||
/bootstrap/
|
||||
build/
|
6
libbuild2-autoconf-tests/build/bootstrap.build
Normal file
6
libbuild2-autoconf-tests/build/bootstrap.build
Normal file
@ -0,0 +1,6 @@
|
||||
project = libbuild2-autoconf-tests
|
||||
|
||||
using version
|
||||
using config
|
||||
using test
|
||||
using dist
|
12
libbuild2-autoconf-tests/build/root.build
Normal file
12
libbuild2-autoconf-tests/build/root.build
Normal file
@ -0,0 +1,12 @@
|
||||
# Load the module to make sure it is up-to-date before we start running the
|
||||
# tests. Failed that, we may try to update it from multiple tests in parallel.
|
||||
# Also, our tests use this project as their amalgamation in order to get the
|
||||
# module import location (as well as the C module configuration).
|
||||
#
|
||||
using autoconf
|
||||
|
||||
using c
|
||||
|
||||
# @@ Should we be able to do just test = $build.path?
|
||||
#
|
||||
testscript{*}: test = $recall($build.path)
|
1
libbuild2-autoconf-tests/buildfile
Normal file
1
libbuild2-autoconf-tests/buildfile
Normal file
@ -0,0 +1 @@
|
||||
./: {*/ -build/} doc{README.md} legal{LICENSE AUTHORS} manifest
|
11
libbuild2-autoconf-tests/manifest
Normal file
11
libbuild2-autoconf-tests/manifest
Normal file
@ -0,0 +1,11 @@
|
||||
: 1
|
||||
name: libbuild2-autoconf-tests
|
||||
version: 0.1.0-a.0.z
|
||||
project: build2
|
||||
summary: Tests for the Autoconf emulation build system module for build2
|
||||
license: MIT ; MIT License.
|
||||
description-file: README.md
|
||||
url: https://github.com/build2/libbuild2-autoconf
|
||||
email: users@build2.org
|
||||
depends: * build2 >= 0.15.0-
|
||||
depends: * bpkg >= 0.15.0-
|
24
libbuild2-autoconf/.gitignore
vendored
Normal file
24
libbuild2-autoconf/.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
# Compiler/linker output.
|
||||
#
|
||||
*.d
|
||||
*.t
|
||||
*.i
|
||||
*.i.*
|
||||
*.ii
|
||||
*.ii.*
|
||||
*.o
|
||||
*.obj
|
||||
*.gcm
|
||||
*.pcm
|
||||
*.ifc
|
||||
*.so
|
||||
*.dll
|
||||
*.a
|
||||
*.lib
|
||||
*.exp
|
||||
*.pdb
|
||||
*.ilk
|
||||
*.exe
|
||||
*.exe.dlls/
|
||||
*.exe.manifest
|
||||
*.pc
|
1
libbuild2-autoconf/AUTHORS
Symbolic link
1
libbuild2-autoconf/AUTHORS
Symbolic link
@ -0,0 +1 @@
|
||||
../AUTHORS
|
1
libbuild2-autoconf/LICENSE
Symbolic link
1
libbuild2-autoconf/LICENSE
Symbolic link
@ -0,0 +1 @@
|
||||
../LICENSE
|
1
libbuild2-autoconf/README.md
Symbolic link
1
libbuild2-autoconf/README.md
Symbolic link
@ -0,0 +1 @@
|
||||
../README.md
|
4
libbuild2-autoconf/build/.gitignore
vendored
Normal file
4
libbuild2-autoconf/build/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
/config.build
|
||||
/root/
|
||||
/bootstrap/
|
||||
build/
|
7
libbuild2-autoconf/build/bootstrap.build
Normal file
7
libbuild2-autoconf/build/bootstrap.build
Normal file
@ -0,0 +1,7 @@
|
||||
project = libbuild2-autoconf
|
||||
|
||||
using version
|
||||
using config
|
||||
using test
|
||||
using install
|
||||
using dist
|
6
libbuild2-autoconf/build/export.build
Normal file
6
libbuild2-autoconf/build/export.build
Normal file
@ -0,0 +1,6 @@
|
||||
$out_root/
|
||||
{
|
||||
include libbuild2/autoconf/
|
||||
}
|
||||
|
||||
export $out_root/libbuild2/autoconf/$import.target
|
16
libbuild2-autoconf/build/root.build
Normal file
16
libbuild2-autoconf/build/root.build
Normal file
@ -0,0 +1,16 @@
|
||||
cxx.std = latest
|
||||
|
||||
using cxx
|
||||
|
||||
hxx{*}: extension = hxx
|
||||
ixx{*}: extension = ixx
|
||||
txx{*}: extension = txx
|
||||
cxx{*}: extension = cxx
|
||||
|
||||
if ($cxx.target.system == 'win32-msvc')
|
||||
cc.poptions += -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS
|
||||
|
||||
if ($cxx.class == 'msvc')
|
||||
cc.coptions += /wd4251 /wd4275 /wd4800
|
||||
elif ($cxx.id == 'gcc')
|
||||
cxx.coptions += -Wno-maybe-uninitialized -Wno-free-nonheap-object # libbutl
|
1
libbuild2-autoconf/buildfile
Normal file
1
libbuild2-autoconf/buildfile
Normal file
@ -0,0 +1 @@
|
||||
./: {*/ -build/} doc{README.md} legal{LICENSE AUTHORS} manifest
|
3
libbuild2-autoconf/libbuild2/autoconf/.gitignore
vendored
Normal file
3
libbuild2-autoconf/libbuild2/autoconf/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# Generated version header.
|
||||
#
|
||||
version.hxx
|
47
libbuild2-autoconf/libbuild2/autoconf/buildfile
Normal file
47
libbuild2-autoconf/libbuild2/autoconf/buildfile
Normal file
@ -0,0 +1,47 @@
|
||||
intf_libs = # Interface dependencies.
|
||||
impl_libs = # Implementation dependencies.
|
||||
|
||||
import impl_libs += build2%lib{build2} # Implied interface dependency.
|
||||
import impl_libs += build2%lib{build2-in}
|
||||
|
||||
lib{build2-autoconf}: {hxx ixx txx cxx}{**} $impl_libs $intf_libs
|
||||
|
||||
# Build options.
|
||||
#
|
||||
cxx.poptions =+ "-I$out_root" "-I$src_root"
|
||||
|
||||
obja{*}: cxx.poptions += -DLIBBUILD2_AUTOCONF_STATIC_BUILD
|
||||
objs{*}: cxx.poptions += -DLIBBUILD2_AUTOCONF_SHARED_BUILD
|
||||
|
||||
# Export options.
|
||||
#
|
||||
lib{build2-autoconf}:
|
||||
{
|
||||
cxx.export.poptions = "-I$out_root" "-I$src_root"
|
||||
cxx.export.libs = $intf_libs
|
||||
}
|
||||
|
||||
liba{build2-autoconf}: cxx.export.poptions += -DLIBBUILD2_AUTOCONF_STATIC
|
||||
libs{build2-autoconf}: cxx.export.poptions += -DLIBBUILD2_AUTOCONF_SHARED
|
||||
|
||||
# For pre-releases use the complete version to make sure they cannot be used
|
||||
# in place of another pre-release or the final version. See the version module
|
||||
# for details on the version.* variable values.
|
||||
#
|
||||
if $version.pre_release
|
||||
lib{build2-autoconf}: bin.lib.version = "-$version.project_id"
|
||||
else
|
||||
lib{build2-autoconf}: bin.lib.version = "-$version.major.$version.minor"
|
||||
|
||||
# Embed the build system core version as our load suffix.
|
||||
#
|
||||
libs{build2-autoconf}: bin.lib.load_suffix = "-$build.version.interface"
|
||||
|
||||
# Install into the libbuild2/autoconf/ subdirectory of, say, /usr/include/
|
||||
# recreating subdirectories.
|
||||
#
|
||||
{hxx ixx txx}{*}:
|
||||
{
|
||||
install = include/libbuild2/autoconf/
|
||||
install.subdirs = true
|
||||
}
|
38
libbuild2-autoconf/libbuild2/autoconf/export.hxx
Normal file
38
libbuild2-autoconf/libbuild2/autoconf/export.hxx
Normal file
@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
// Normally we don't export class templates (but do complete specializations),
|
||||
// inline functions, and classes with only inline member functions. Exporting
|
||||
// classes that inherit from non-exported/imported bases (e.g., std::string)
|
||||
// will end up badly. The only known workarounds are to not inherit or to not
|
||||
// export. Also, MinGW GCC doesn't like seeing non-exported functions being
|
||||
// used before their inline definition. The workaround is to reorder code. In
|
||||
// the end it's all trial and error.
|
||||
|
||||
#if defined(LIBBUILD2_AUTOCONF_STATIC) // Using static.
|
||||
# define LIBBUILD2_AUTOCONF_SYMEXPORT
|
||||
#elif defined(LIBBUILD2_AUTOCONF_STATIC_BUILD) // Building static.
|
||||
# define LIBBUILD2_AUTOCONF_SYMEXPORT
|
||||
#elif defined(LIBBUILD2_AUTOCONF_SHARED) // Using shared.
|
||||
# ifdef _WIN32
|
||||
# define LIBBUILD2_AUTOCONF_SYMEXPORT __declspec(dllimport)
|
||||
# else
|
||||
# define LIBBUILD2_AUTOCONF_SYMEXPORT
|
||||
# endif
|
||||
#elif defined(LIBBUILD2_AUTOCONF_SHARED_BUILD) // Building shared.
|
||||
# ifdef _WIN32
|
||||
# define LIBBUILD2_AUTOCONF_SYMEXPORT __declspec(dllexport)
|
||||
# else
|
||||
# define LIBBUILD2_AUTOCONF_SYMEXPORT
|
||||
# endif
|
||||
#else
|
||||
// If none of the above macros are defined, then we assume we are being used
|
||||
// by some third-party build system that cannot/doesn't signal the library
|
||||
// type. Note that this fallback works for both static and shared libraries
|
||||
// provided the library only exports functions (in other words, no global
|
||||
// exported data) and for the shared case the result will be sub-optimal
|
||||
// compared to having dllimport. If, however, your library does export data,
|
||||
// then you will probably want to replace the fallback with the (commented
|
||||
// out) error since it won't work for the shared case.
|
||||
//
|
||||
# define LIBBUILD2_AUTOCONF_SYMEXPORT // Using static or shared.
|
||||
#endif
|
75
libbuild2-autoconf/libbuild2/autoconf/init.cxx
Normal file
75
libbuild2-autoconf/libbuild2/autoconf/init.cxx
Normal file
@ -0,0 +1,75 @@
|
||||
#include <libbuild2/autoconf/init.hxx>
|
||||
|
||||
#include <libbuild2/scope.hxx>
|
||||
#include <libbuild2/variable.hxx>
|
||||
#include <libbuild2/diagnostics.hxx>
|
||||
|
||||
#include <libbuild2/autoconf/rule.hxx>
|
||||
|
||||
namespace build2
|
||||
{
|
||||
namespace autoconf
|
||||
{
|
||||
static const rule rule_;
|
||||
|
||||
//-
|
||||
// The `autoconf` module.
|
||||
//-
|
||||
bool
|
||||
init (scope& rs,
|
||||
scope& bs,
|
||||
const location& l,
|
||||
bool,
|
||||
bool,
|
||||
module_init_extra&)
|
||||
{
|
||||
tracer trace ("autoconf::init");
|
||||
l5 ([&]{trace << "for " << bs;});
|
||||
|
||||
// Load in.base (in.* variables, in{} target type).
|
||||
//
|
||||
load_module (rs, rs, "in.base", l);
|
||||
|
||||
// Enter variables.
|
||||
//
|
||||
{
|
||||
auto& vp (rs.var_pool ());
|
||||
|
||||
// Configuration file flavor. Valid values are `autoconf` (default),
|
||||
// `cmake`, and `meson`.
|
||||
//
|
||||
vp.insert<string> ("autoconf.flavor");
|
||||
}
|
||||
|
||||
// Register the rule.
|
||||
//
|
||||
// @@ TODO: this will be ambiguous, for example, version.in rule. Note
|
||||
// also that if we register it for cc{}, then it will always take
|
||||
// precedence over version.in, which is probably something we don't
|
||||
// want. In fact, we would have liked it to be of lower precedence
|
||||
// since version.in will only match if there is dependency on
|
||||
// manifest.
|
||||
//
|
||||
rs.insert_rule<file> (perform_update_id, "autoconf.in", rule_);
|
||||
rs.insert_rule<file> (perform_clean_id, "autoconf.in", rule_);
|
||||
rs.insert_rule<file> (configure_update_id, "autoconf.in", rule_);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const module_functions mod_functions[] =
|
||||
{
|
||||
// NOTE: don't forget to also update the documentation in init.hxx if
|
||||
// changing anything here.
|
||||
|
||||
{"autoconf", nullptr, init},
|
||||
{nullptr, nullptr, nullptr}
|
||||
};
|
||||
|
||||
const module_functions*
|
||||
build2_autoconf_load ()
|
||||
{
|
||||
return mod_functions;
|
||||
}
|
||||
}
|
||||
}
|
20
libbuild2-autoconf/libbuild2/autoconf/init.hxx
Normal file
20
libbuild2-autoconf/libbuild2/autoconf/init.hxx
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <libbuild2/types.hxx>
|
||||
#include <libbuild2/utility.hxx>
|
||||
|
||||
#include <libbuild2/module.hxx>
|
||||
|
||||
#include <libbuild2/autoconf/export.hxx>
|
||||
|
||||
namespace build2
|
||||
{
|
||||
namespace autoconf
|
||||
{
|
||||
//-
|
||||
// Module `autoconf` does not require bootstrapping.
|
||||
//-
|
||||
extern "C" LIBBUILD2_AUTOCONF_SYMEXPORT const module_functions*
|
||||
build2_autoconf_load ();
|
||||
}
|
||||
}
|
355
libbuild2-autoconf/libbuild2/autoconf/rule.cxx
Normal file
355
libbuild2-autoconf/libbuild2/autoconf/rule.cxx
Normal file
@ -0,0 +1,355 @@
|
||||
#include <libbuild2/autoconf/rule.hxx>
|
||||
|
||||
#include <libbuild2/target.hxx>
|
||||
#include <libbuild2/algorithm.hxx>
|
||||
#include <libbuild2/diagnostics.hxx>
|
||||
|
||||
namespace build2
|
||||
{
|
||||
namespace autoconf
|
||||
{
|
||||
enum class flavor {autoconf, cmake, meson};
|
||||
|
||||
struct match_data
|
||||
{
|
||||
autoconf::flavor flavor;
|
||||
};
|
||||
|
||||
static_assert (sizeof (match_data) <= target::data_size,
|
||||
"insufficient space");
|
||||
|
||||
rule::
|
||||
rule ()
|
||||
: in::rule ("autoconf.in 1",
|
||||
"autoconf.in",
|
||||
'@' /* symbol */,
|
||||
false /* strict */)
|
||||
{
|
||||
}
|
||||
|
||||
recipe rule::
|
||||
apply (action a, target& t) const
|
||||
{
|
||||
// Determine and cache the configuration file flavor.
|
||||
//
|
||||
flavor f (flavor::autoconf);
|
||||
if (const string* s = cast_null<string> (t["autoconf.flavor"]))
|
||||
{
|
||||
if (*s == "cmake")
|
||||
f = flavor::cmake;
|
||||
else if (*s == "meson")
|
||||
f = flavor::meson;
|
||||
else if (*s != "autoconf")
|
||||
fail << "invalid configuration file flavor '" << *s << "'";
|
||||
}
|
||||
|
||||
t.data (match_data {f});
|
||||
|
||||
return in::rule::apply (a, t);
|
||||
}
|
||||
|
||||
void rule::
|
||||
process (const location& l,
|
||||
action a, const target& t,
|
||||
depdb& dd, size_t dd_skip,
|
||||
string& s, size_t b,
|
||||
const char* nl,
|
||||
char sym,
|
||||
bool strict,
|
||||
const optional<string>& null) const
|
||||
{
|
||||
auto ws = [] (char c)
|
||||
{
|
||||
return c == ' ' || c == '\t';
|
||||
};
|
||||
|
||||
auto skip_ws = [&ws] (const string& s, size_t& b)
|
||||
{
|
||||
for (; ws (s[b]); ++b) ;
|
||||
};
|
||||
|
||||
// Scan a C identifier returning its size. Return 0 if it's not a valid
|
||||
// identifier or is followed by a non-whitespace.
|
||||
//
|
||||
auto read_id = [&ws] (const string& s, size_t b) -> size_t
|
||||
{
|
||||
size_t i (b);
|
||||
char c;
|
||||
for (; (c = s[i]) == '_' || (i == b ? alpha (c) : alnum (c)); ++i) ;
|
||||
|
||||
return i != b && (c == '\0' || ws (c)) ? i - b : 0;
|
||||
};
|
||||
|
||||
size_t i (b);
|
||||
|
||||
flavor f (t.data<match_data> ().flavor);
|
||||
|
||||
// Substitute special #undef/#cmakedfine/#mesondefine line. If value is
|
||||
// false, then do not append the value to #define.
|
||||
//
|
||||
// Return true if it was substituted with #define, false if with #undef,
|
||||
// and nullopt if with a custom block of preprocessor directives.
|
||||
//
|
||||
auto substitute_special = [&skip_ws, this,
|
||||
&l,
|
||||
a, &t,
|
||||
&dd, &dd_skip,
|
||||
nl, strict, &null,
|
||||
&s] (const string& name,
|
||||
bool value = true) -> optional<bool>
|
||||
{
|
||||
// Note that we must be careful here with going too ad hoc since there
|
||||
// is a parallel debdb validation logic in in::rule which calls
|
||||
// substitute().
|
||||
//
|
||||
optional<string> ov (
|
||||
substitute (l, a, t, dd, dd_skip, name, strict, null));
|
||||
|
||||
assert (ov); // C identifier is a valid variable name.
|
||||
string& v (*ov);
|
||||
|
||||
// As an extension, we allow replacing the entire line with a
|
||||
// potentially multi-line block of preprocessor directives. To detect
|
||||
// this, we look for a line that starts with `#` after whitespaces.
|
||||
//
|
||||
size_t p (0);
|
||||
for (;; ++p)
|
||||
{
|
||||
skip_ws (v, p);
|
||||
|
||||
if (v[p] == '#')
|
||||
break;
|
||||
|
||||
p = v.find ('\n', p);
|
||||
|
||||
if (p == string::npos)
|
||||
break;
|
||||
}
|
||||
|
||||
optional<bool> r;
|
||||
if (p != string::npos)
|
||||
{
|
||||
s.clear ();
|
||||
|
||||
// Skip leading and trailing newlines.
|
||||
//
|
||||
for (; v.back () == '\n'; v.pop_back ()) ;
|
||||
for (p = 0; v[p] == '\n'; ++p) ;
|
||||
|
||||
for (;; ++p)
|
||||
{
|
||||
size_t b (p);
|
||||
p = v.find ('\n', p);
|
||||
|
||||
s.append (v, b, p - b);
|
||||
|
||||
if (p == string::npos)
|
||||
break;
|
||||
|
||||
s.append (nl); // Last line is added by our caller.
|
||||
}
|
||||
}
|
||||
// Otherwise translate false/0 to #undef and true to 1.
|
||||
//
|
||||
else if (v == "false" || v == "0")
|
||||
{
|
||||
s = "#undef ";
|
||||
s += name;
|
||||
r = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (v == "true")
|
||||
v = "1";
|
||||
|
||||
s = "#define ";
|
||||
s += name;
|
||||
if (value)
|
||||
{
|
||||
s += ' ';
|
||||
s += v;
|
||||
}
|
||||
r = true;
|
||||
}
|
||||
|
||||
return r;
|
||||
};
|
||||
|
||||
// Deal with special lines of each flavor. Return if the line has has
|
||||
// been handled and fall through to use the normal substitution logic.
|
||||
//
|
||||
switch (f)
|
||||
{
|
||||
case flavor::autoconf:
|
||||
{
|
||||
// Autoconf recognizes two forms of special lines:
|
||||
//
|
||||
// #undef NAME
|
||||
// #define NAME 0
|
||||
//
|
||||
// However, the latter form as well as comments after NAME in the
|
||||
// former form are "strongly discouraged". So for now we only
|
||||
// recognize #undef, especially seeing that it's the dominant form
|
||||
// in the wild.
|
||||
//
|
||||
skip_ws (s, i);
|
||||
|
||||
if (s[i] == '#')
|
||||
{
|
||||
++i;
|
||||
skip_ws (s, i);
|
||||
|
||||
if (s.compare (i, 5, "undef") == 0 && ws (s[i + 5]))
|
||||
{
|
||||
i += 5;
|
||||
skip_ws (s, i);
|
||||
|
||||
size_t n (read_id (s, i));
|
||||
|
||||
if (n == 0)
|
||||
fail (l) << "expected identifier after #undef";
|
||||
|
||||
string name (s, i, n);
|
||||
|
||||
i += n;
|
||||
skip_ws (s, i);
|
||||
|
||||
// Let's not bother with detecting comments seeing that it's
|
||||
// strongly discouraged.
|
||||
//
|
||||
if (s[i] != '\0')
|
||||
fail (l) << "junk after identifier " << name;
|
||||
|
||||
substitute_special (name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case flavor::cmake:
|
||||
{
|
||||
// CMake recognizes two forms of special lines:
|
||||
//
|
||||
// #cmakedefine NAME [VALUE]
|
||||
// #cmakedefine01 NAME
|
||||
//
|
||||
// The first variant is replaced with #define if NAME is "set to any
|
||||
// value not considered false" and to (commented out) #undef
|
||||
// otherwise. In the former case, if VALUE is present, then it is
|
||||
// processed normally with @-substitutions. So in a sense there are
|
||||
// two entities at play: variable NAME controls #define/#undef while
|
||||
// VALUE -- the value in case of #define. The following patterns are
|
||||
// fairly common:
|
||||
//
|
||||
// #cmakedefine HAVE_MEMORY_H
|
||||
// #cmakedefine HAVE_MEMORY_H 1
|
||||
// #cmakedefine SIZEOF_LONG @SIZEOF_LONG@
|
||||
//
|
||||
// But also:
|
||||
//
|
||||
// #cmakedefine const
|
||||
// #cmakedefine const @HAVE_CONST@
|
||||
// #cmakedefine size_t @SIZE_T@
|
||||
//
|
||||
// The #cmakedefine01 variant is always replaced with #define,
|
||||
// either with value 1 if NAME is true and 0 otherwise. It's doesn't
|
||||
// appear to be used much in practice so we are not going to bother
|
||||
// with it.
|
||||
//
|
||||
skip_ws (s, i);
|
||||
|
||||
if (s[i] == '#')
|
||||
{
|
||||
++i;
|
||||
skip_ws (s, i);
|
||||
|
||||
if (s.compare (i, 11, "cmakedefine") == 0 && ws (s[i + 11]))
|
||||
{
|
||||
i += 11;
|
||||
skip_ws (s, i);
|
||||
|
||||
size_t n (read_id (s, i));
|
||||
|
||||
if (n == 0)
|
||||
fail (l) << "expected identifier after #cmakedefine";
|
||||
|
||||
string name (s, i, n);
|
||||
|
||||
i += n;
|
||||
skip_ws (s, i);
|
||||
|
||||
// If there is value, then save it for later (see below).
|
||||
//
|
||||
optional<string> value;
|
||||
if (s[i] != '\0')
|
||||
value = string (s, i);
|
||||
|
||||
optional<bool> r (substitute_special (name, !value));
|
||||
|
||||
if (!value || !r || !*r)
|
||||
return;
|
||||
|
||||
// Append the value and fall through to the normal substitution
|
||||
// logic.
|
||||
//
|
||||
s += ' ';
|
||||
b = s.size (); // Start substituting from here.
|
||||
s += *value;
|
||||
}
|
||||
else if (s.compare (i, 13, "cmakedefine01") == 0 && ws (s[i + 13]))
|
||||
fail(l) << "#cmakedefine01 is not yet supported";
|
||||
}
|
||||
break;
|
||||
}
|
||||
case flavor::meson:
|
||||
{
|
||||
// Meson recognizes only one special form:
|
||||
//
|
||||
// #mesondefine NAME
|
||||
//
|
||||
// It is replaced with commented out #undef if NAME is not set, with
|
||||
// #undef if NAME is set to boolean false, with #define without a
|
||||
// value if it is set to boolean true, and to #define with the value
|
||||
// of NAME if it is set to integer or string.
|
||||
//
|
||||
// Since we don't do commented out #undef, we will treat the first
|
||||
// two cases the same. We will also map the third case to value 1,
|
||||
// similar to Autoconf.
|
||||
//
|
||||
skip_ws (s, i);
|
||||
|
||||
if (s[i] == '#')
|
||||
{
|
||||
++i;
|
||||
skip_ws (s, i);
|
||||
|
||||
if (s.compare (i, 11, "mesondefine") == 0 && ws (s[i + 11]))
|
||||
{
|
||||
i += 11;
|
||||
skip_ws (s, i);
|
||||
|
||||
size_t n (read_id (s, i));
|
||||
|
||||
if (n == 0)
|
||||
fail (l) << "expected identifier after #mesondefine";
|
||||
|
||||
string name (s, i, n);
|
||||
|
||||
i += n;
|
||||
skip_ws (s, i);
|
||||
|
||||
if (s[i] != '\0')
|
||||
fail (l) << "junk after identifier " << name;
|
||||
|
||||
substitute_special (name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
in::rule::process (l, a, t, dd, dd_skip, s, b, nl, sym, strict, null);
|
||||
}
|
||||
}
|
||||
}
|
38
libbuild2-autoconf/libbuild2/autoconf/rule.hxx
Normal file
38
libbuild2-autoconf/libbuild2/autoconf/rule.hxx
Normal file
@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <libbuild2/types.hxx>
|
||||
#include <libbuild2/utility.hxx>
|
||||
|
||||
#include <libbuild2/in/rule.hxx>
|
||||
|
||||
namespace build2
|
||||
{
|
||||
namespace autoconf
|
||||
{
|
||||
// Process a config.h.in file.
|
||||
//
|
||||
// Note that to be usable as a drop-in replacement we make the default
|
||||
// substitution symbol '@' and the mode -- lax. The user, however, is
|
||||
// still able to override both of these choices with the corresponding
|
||||
// in.* variables.
|
||||
//
|
||||
class rule: public in::rule
|
||||
{
|
||||
public:
|
||||
rule ();
|
||||
|
||||
virtual recipe
|
||||
apply (action, target&) const override;
|
||||
|
||||
virtual void
|
||||
process (const location&,
|
||||
action, const target&,
|
||||
depdb&, size_t,
|
||||
string&, size_t,
|
||||
const char*,
|
||||
char,
|
||||
bool,
|
||||
const optional<string>&) const override;
|
||||
};
|
||||
}
|
||||
}
|
13
libbuild2-autoconf/manifest
Normal file
13
libbuild2-autoconf/manifest
Normal file
@ -0,0 +1,13 @@
|
||||
: 1
|
||||
name: libbuild2-autoconf
|
||||
version: 0.1.0-a.0.z
|
||||
project: build2
|
||||
summary: Autoconf emulation build system module for build2
|
||||
license: MIT ; MIT License.
|
||||
description-file: README.md
|
||||
url: https://github.com/build2/libbuild2-autoconf
|
||||
email: users@build2.org
|
||||
build-warning-email: builds@build2.org
|
||||
depends: * build2 >= 0.15.0-
|
||||
depends: * bpkg >= 0.15.0-
|
||||
tests: * libbuild2-autoconf-tests == $
|
4
packages.manifest
Normal file
4
packages.manifest
Normal file
@ -0,0 +1,4 @@
|
||||
: 1
|
||||
location: libbuild2-autoconf/
|
||||
:
|
||||
location: libbuild2-autoconf-tests/
|
Loading…
Reference in New Issue
Block a user