Skip to content

Commit

Permalink
Generate feature tests, in two stages. (#747)
Browse files Browse the repository at this point in the history
Generate feature tests, in two stages.

This is yet another attempt at #732: go back to writing all feature test
macros to a config header, so that we can build libpqxx itself and the
client application against the same version of the language — even if
the user actually builds them with different options that may affect the
feature set.

Things I wanted for this work:
1. Less of that repetitive configuration.  It's tedious and error-prone.
2. Reduced risk of inconsistency between autoconf and CMake.
3. Portability.
4. Simple way to add a feature check based on a C++ feature test macro.
5. Reasonable simplifity.
6. Fast build configuration.

I think this solution satisfies most of these.  A new configuration file
`cxx_features.txt` lets me define features that we detect simply by
checking a C++ feature test macro.  (We'll still need to enter each
libpqxx feature macro that we want to expose into `configitems`.)

A first Python script, `generate_cxx_checks.py` generates the C++ code
snippets that check for each of the C++ feature check macros.  It just
adds those to `config-tests/`.

The C++ files inside `config-tests/` now have names that correspond
_directly_ to the libpqxx feature macros.  It eliminates the pointless
separate file name, but also, it lets me generate autoconf and CMake
config based on just the file names!

Then, a second script `generate_check_config.py` produces pieces of
autoconf and CMake configuration to actually run these checks.  The
respective main configurations use include directives to incorporate
these configs.

These pieces of configuration will reference both the pre-existing,
hand-written feature test snippets, and the ones we just generated from
C++ feature test macros.  At this stage there's no difference between
the two kinds.

I believe this is as portable as it ever was, because nothing really
changes structurally.  Yes, the scripts are tailored for recent Python
language versions, but I think that's about it.  Of course the C++
feature test macros were introduced in C++20 so they're not going to
do much good for C++17.  But that should stop mattering soon, as we
transition to C++20 as the minimum language version.

I believe the new system is not the simplest thing, but is easier to
manage than all those manual compilation checks across two build
systems.  That was just painful.  And who knows, some day this new
approach may support adding yet another build system.

The only thing that hasn't improved is build configuration speed.  The
only positive thing I can say there is that the new mechanism generates
all the extra files during `autogen.sh`, so that part shouldn't affect
build configuration speeds.  But it's not going to be as fast as my
last plan for this work.  (That experiment checked all the C++ feature
test macros by running a single piece of generated C++ code through the
preprocessor, and generating a new config header from it.)
  • Loading branch information
jtv authored Nov 21, 2023
1 parent 7586846 commit 12b50dd
Show file tree
Hide file tree
Showing 74 changed files with 1,483 additions and 980 deletions.
1 change: 1 addition & 0 deletions Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ am__aclocal_m4_deps = $(top_srcdir)/config/m4/libtool.m4 \
$(top_srcdir)/config/m4/ltsugar.m4 \
$(top_srcdir)/config/m4/ltversion.m4 \
$(top_srcdir)/config/m4/lt~obsolete.m4 \
$(top_srcdir)/pqxx_cxx_feature_checks.ac \
$(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
Expand Down
4 changes: 3 additions & 1 deletion NEWS
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
7.8.2
7.9.0
- Put all feature tests back in config header. (#732)
- Automate integration of feature tests into both CMake & autoconf. (#747)
- Fix broken `to_buf()` on `zview`. (#728)
- Support `PQfsize()` and `PQfmod()`. (#727)
- Implement conversion from `string_view` to string. (#728)
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
7.8.2
7.9.0
6 changes: 6 additions & 0 deletions autogen.sh
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ expand_templates $(find . -name \*.template)
substitute include/pqxx/version.hxx.template >include/pqxx/version.hxx
substitute include/pqxx/doc/mainpage.md.template >include/pqxx/doc/mainpage.md

# Generate feature test snippets for C++ features that we simply detect by
# checking a C++ feature test macro.
./tools/generate_cxx_checks.py

# Generate autoconf and CMake configuration for our feature test snippets.
./tools/generate_check_config.py

autoheader
libtoolize --force --automake --copy
Expand Down
75 changes: 6 additions & 69 deletions cmake/config.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -51,80 +51,17 @@ endif()
set(CMAKE_REQUIRED_DEFINITIONS ${CMAKE_CXX${CMAKE_CXX_STANDARD}_STANDARD_COMPILE_OPTION})
set(CMAKE_REQUIRED_QUIET ON)

try_compile(
PQXX_HAVE_GCC_PURE
${PROJECT_BINARY_DIR}
SOURCES ${PROJECT_SOURCE_DIR}/config-tests/gcc_pure.cxx)
try_compile(
PQXX_HAVE_GCC_VISIBILITY
${PROJECT_BINARY_DIR}
SOURCES ${PROJECT_SOURCE_DIR}/config-tests/gcc_visibility.cxx)
# C++20: Assume support.
try_compile(
PQXX_HAVE_LIKELY
${PROJECT_BINARY_DIR}
SOURCES ${PROJECT_SOURCE_DIR}/config-tests/likely.cxx)
try_compile(
PQXX_HAVE_CXA_DEMANGLE
${PROJECT_BINARY_DIR}
SOURCES ${PROJECT_SOURCE_DIR}/config-tests/cxa_demangle.cxx)
# C++20: Assume support.
try_compile(
PQXX_HAVE_CONCEPTS
${PROJECT_BINARY_DIR}
SOURCES ${PROJECT_SOURCE_DIR}/config-tests/concepts.cxx)
# C++20: Assume support.
try_compile(
PQXX_HAVE_SPAN
${PROJECT_BINARY_DIR}
SOURCES ${PROJECT_SOURCE_DIR}/config-tests/span.cxx)
try_compile(
PQXX_HAVE_CHARCONV_FLOAT
${PROJECT_BINARY_DIR}
SOURCES ${PROJECT_SOURCE_DIR}/config-tests/charconv_float.cxx)
try_compile(
PQXX_HAVE_CHARCONV_INT
${PROJECT_BINARY_DIR}
SOURCES ${PROJECT_SOURCE_DIR}/config-tests/charconv_int.cxx)
try_compile(
PQXX_HAVE_PATH
${PROJECT_BINARY_DIR}
SOURCES ${PROJECT_SOURCE_DIR}/config-tests/fs.cxx)
try_compile(
PQXX_HAVE_THREAD_LOCAL
${PROJECT_BINARY_DIR}
SOURCES ${PROJECT_SOURCE_DIR}/config-tests/thread_local.cxx)
try_compile(
PQXX_HAVE_SLEEP_FOR
${PROJECT_BINARY_DIR}
SOURCES ${PROJECT_SOURCE_DIR}/config-tests/sleep_for.cxx)
try_compile(
PQXX_HAVE_STRERROR_R
${PROJECT_BINARY_DIR}
SOURCES ${PROJECT_SOURCE_DIR}/config-tests/strerror_r.cxx)
try_compile(
PQXX_HAVE_STRERROR_S
${PROJECT_BINARY_DIR}
SOURCES ${PROJECT_SOURCE_DIR}/config-tests/strerror_s.cxx)
try_compile(
PQXX_HAVE_YEAR_MONTH_DAY
${PROJECT_BINARY_DIR}
SOURCES ${PROJECT_SOURCE_DIR}/config-tests/year_month_day.cxx)
# C++20: Assume support.
try_compile(
PQXX_HAVE_CMP
${PROJECT_BINARY_DIR}
SOURCES ${PROJECT_SOURCE_DIR}/config-tests/cmp.cxx)

try_compile(
need_fslib
${PROJECT_BINARY_DIR}
SOURCES ${PROJECT_SOURCE_DIR}/config-tests/need_fslib.cxx)
if(!need_fslib)
# Incorporate feature checks based on C++ feature test mac
include(pqxx_cxx_feature_checks)

# This variable is set by one of the snippets in config-tests.
if(!no_need_fslib)
# TODO: This may work for gcc 8, but some clang versions may need -lc++fs.
link_libraries(stdc++fs)
endif()


# check_cxx_source_compiles requires CMAKE_REQUIRED_DEFINITIONS to specify
# compiling arguments.
# Workaround: Pop CMAKE_REQUIRED_DEFINITIONS
Expand Down
108 changes: 108 additions & 0 deletions cmake/pqxx_cxx_feature_checks.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Configuration for feature checks.
# Generated by generate_check_config.py.
try_compile(
PQXX_HAVE_CHARCONV_FLOAT
${PROJECT_BINARY_DIR}
SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_CHARCONV_FLOAT.cxx
)
try_compile(
PQXX_HAVE_CHARCONV_INT
${PROJECT_BINARY_DIR}
SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_CHARCONV_INT.cxx
)
try_compile(
PQXX_HAVE_CMP
${PROJECT_BINARY_DIR}
SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_CMP.cxx
)
try_compile(
PQXX_HAVE_CONCEPTS
${PROJECT_BINARY_DIR}
SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_CONCEPTS.cxx
)
try_compile(
PQXX_HAVE_CXA_DEMANGLE
${PROJECT_BINARY_DIR}
SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_CXA_DEMANGLE.cxx
)
try_compile(
PQXX_HAVE_GCC_PURE
${PROJECT_BINARY_DIR}
SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_GCC_PURE.cxx
)
try_compile(
PQXX_HAVE_GCC_VISIBILITY
${PROJECT_BINARY_DIR}
SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_GCC_VISIBILITY.cxx
)
try_compile(
PQXX_HAVE_LIKELY
${PROJECT_BINARY_DIR}
SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_LIKELY.cxx
)
try_compile(
PQXX_HAVE_MULTIDIM
${PROJECT_BINARY_DIR}
SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_MULTIDIM.cxx
)
try_compile(
PQXX_HAVE_PATH
${PROJECT_BINARY_DIR}
SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_PATH.cxx
)
try_compile(
PQXX_HAVE_POLL
${PROJECT_BINARY_DIR}
SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_POLL.cxx
)
try_compile(
PQXX_HAVE_SLEEP_FOR
${PROJECT_BINARY_DIR}
SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_SLEEP_FOR.cxx
)
try_compile(
PQXX_HAVE_SOURCE_LOCATION
${PROJECT_BINARY_DIR}
SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_SOURCE_LOCATION.cxx
)
try_compile(
PQXX_HAVE_SPAN
${PROJECT_BINARY_DIR}
SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_SPAN.cxx
)
try_compile(
PQXX_HAVE_SSIZE
${PROJECT_BINARY_DIR}
SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_SSIZE.cxx
)
try_compile(
PQXX_HAVE_STRERROR_R
${PROJECT_BINARY_DIR}
SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_STRERROR_R.cxx
)
try_compile(
PQXX_HAVE_STRERROR_S
${PROJECT_BINARY_DIR}
SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_STRERROR_S.cxx
)
try_compile(
PQXX_HAVE_THREAD_LOCAL
${PROJECT_BINARY_DIR}
SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_THREAD_LOCAL.cxx
)
try_compile(
PQXX_HAVE_UNREACHABLE
${PROJECT_BINARY_DIR}
SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_UNREACHABLE.cxx
)
try_compile(
PQXX_HAVE_YEAR_MONTH_DAY
${PROJECT_BINARY_DIR}
SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_YEAR_MONTH_DAY.cxx
)
try_compile(
no_need_fslib
${PROJECT_BINARY_DIR}
SOURCES ${PROJECT_SOURCE_DIR}/config-tests/no_need_fslib.cxx
)
# End of config.
File renamed without changes.
File renamed without changes.
1 change: 1 addition & 0 deletions config-tests/cmp.cxx → config-tests/PQXX_HAVE_CMP.cxx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Test for C++20 std::cmp_greater etc. support.
// C++20: Assume support.
#include <utility>


Expand Down
11 changes: 11 additions & 0 deletions config-tests/PQXX_HAVE_CONCEPTS.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Feature check for 'PQXX_HAVE_CONCEPTS'.
// Generated by generate_cxx_checks.py.
#include <version>
#if !defined(__cpp_concepts)
# error "No PQXX_HAVE_CONCEPTS: __cpp_concepts is not set."
#endif
#if !__cpp_concepts
# error "No PQXX_HAVE_CONCEPTS: __cpp_concepts is false."
#endif

int main() {}
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Test for C++20 [[likely]] and [[unlikely]] attributes.
// C++20: Assume support.

int main(int argc, char **)
{
Expand Down
11 changes: 11 additions & 0 deletions config-tests/PQXX_HAVE_MULTIDIM.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Feature check for 'PQXX_HAVE_MULTIDIM'.
// Generated by generate_cxx_checks.py.
#include <version>
#if !defined(__cpp_multidimensional_subscript)
# error "No PQXX_HAVE_MULTIDIM: __cpp_multidimensional_subscript is not set."
#endif
#if !__cpp_multidimensional_subscript
# error "No PQXX_HAVE_MULTIDIM: __cpp_multidimensional_subscript is false."
#endif

int main() {}
File renamed without changes.
File renamed without changes.
File renamed without changes.
11 changes: 11 additions & 0 deletions config-tests/PQXX_HAVE_SOURCE_LOCATION.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Feature check for 'PQXX_HAVE_SOURCE_LOCATION'.
// Generated by generate_cxx_checks.py.
#include <version>
#if !defined(__cpp_lib_source_location)
# error "No PQXX_HAVE_SOURCE_LOCATION: __cpp_lib_source_location is not set."
#endif
#if !__cpp_lib_source_location
# error "No PQXX_HAVE_SOURCE_LOCATION: __cpp_lib_source_location is false."
#endif

int main() {}
11 changes: 11 additions & 0 deletions config-tests/PQXX_HAVE_SPAN.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Feature check for 'PQXX_HAVE_SPAN'.
// Generated by generate_cxx_checks.py.
#include <version>
#if !defined(__cpp_lib_span)
# error "No PQXX_HAVE_SPAN: __cpp_lib_span is not set."
#endif
#if !__cpp_lib_span
# error "No PQXX_HAVE_SPAN: __cpp_lib_span is false."
#endif

int main() {}
11 changes: 11 additions & 0 deletions config-tests/PQXX_HAVE_SSIZE.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Feature check for 'PQXX_HAVE_SSIZE'.
// Generated by generate_cxx_checks.py.
#include <version>
#if !defined(__cpp_lib_ssize)
# error "No PQXX_HAVE_SSIZE: __cpp_lib_ssize is not set."
#endif
#if !__cpp_lib_ssize
# error "No PQXX_HAVE_SSIZE: __cpp_lib_ssize is false."
#endif

int main() {}
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Test for std::to_string/std::from_string for floating-point types.
// TODO: Probably no longer needed once we always have float std::charconv.
#include <iostream>
#include <sstream>

Expand Down
11 changes: 11 additions & 0 deletions config-tests/PQXX_HAVE_UNREACHABLE.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Feature check for 'PQXX_HAVE_UNREACHABLE'.
// Generated by generate_cxx_checks.py.
#include <version>
#if !defined(__cpp_lib_unreachable)
# error "No PQXX_HAVE_UNREACHABLE: __cpp_lib_unreachable is not set."
#endif
#if !__cpp_lib_unreachable
# error "No PQXX_HAVE_UNREACHABLE: __cpp_lib_unreachable is false."
#endif

int main() {}
File renamed without changes.
30 changes: 17 additions & 13 deletions config-tests/README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
Configuration tests
===================

Libpqxx comes with support for different build systems: the GNU autotools,
CMake, Visual Studio's "nmake", and raw GNU "make" on Windows.
Libpqxx comes with support for two different build systems: the GNU autotools,
and CMake.

For several of these build systems, we need to test things like "does this
compiler environment support `std::to_chars` for floating-point types?"
We need to teach both of these to test things like "does this compiler
environment support `std::to_chars` for floating-point types?"

We test these things by trying to compile a particular snippet of code, and
seeing whether that succeeds.
In both build systems we test these things by trying to compile a particular
snippet of code, found in this directry, and seeing whether that succeeds.

To avoid duplicating those snippets for multiple build systems, we put them
here. Both the autotools configuration and the CMake configuration can refer to
them that way.
them that way. We generate autoconf and cmake configuration automatically to
inject those checks, avoiding tedious repetition.

It took a bit of nasty magic to read a C++ source file into m4 and treat it as
a string literal, without macro expansion. There is every chance that I missed
something, so be prepared for tests failing for unexpected reasons! Some C++
syntax may end up having an unforeseen meaning in m4, and screw up the handling
of the code snippet. Re-configure, and read your logs carefully after editing
these snippets.
Some of the checks are based on C++20 feature test macros. We generate those
automatically using `tools/generate_cxx_checks.py`.

It took a bit of nasty magic to read a C++ source file into m4 for the autoconf
side and treat it as a string literal, without macro expansion. There is every
chance that I missed something, so be prepared for tests failing for unexpected
reasons! Some C++ syntax may end up having an unforeseen meaning in m4, and
screw up the handling of the code snippet. Re-configure, and read your logs
carefully after editing these snippets.
21 changes: 0 additions & 21 deletions config-tests/concepts.cxx

This file was deleted.

File renamed without changes.
8 changes: 0 additions & 8 deletions config-tests/span.cxx

This file was deleted.

1 change: 1 addition & 0 deletions config/Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ am__aclocal_m4_deps = $(top_srcdir)/config/m4/libtool.m4 \
$(top_srcdir)/config/m4/ltsugar.m4 \
$(top_srcdir)/config/m4/ltversion.m4 \
$(top_srcdir)/config/m4/lt~obsolete.m4 \
$(top_srcdir)/pqxx_cxx_feature_checks.ac \
$(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
Expand Down
Loading

0 comments on commit 12b50dd

Please sign in to comment.