Skip to content

CXX-2767 Add macro guards tests for bsoncxx and mongocxx #1043

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 25 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
ba2b9b4
Move BSONCXX_ENUM macro guard into types.hpp
eramongodb Sep 28, 2023
5e86e60
Use object library for catch/main.cpp
eramongodb Oct 19, 2023
9fa9fb9
Add include/ and lib/ to target include directories
eramongodb Sep 26, 2023
fcf4d0f
Add macro guard tests
eramongodb Sep 28, 2023
f27c87f
Fix issues preventing standalone header inclusion
eramongodb Sep 28, 2023
5af1cd0
Fix missing postlude header include directives
eramongodb Sep 28, 2023
ea89972
Fix missing prelude header include directives
eramongodb Oct 2, 2023
307a0c3
Add macro guards tests to EVG config
eramongodb Oct 19, 2023
e08ca0f
Refactor macro guard test commands into MacroGuardTest
eramongodb Oct 19, 2023
0227c6a
Update substituted variable name to match MacroGuardTest refactor
eramongodb Oct 25, 2023
617c5dc
Rename PROJECT_TEST_PROPERTIES -> PROJECT_TEST_PROPERTIES_TARGET
eramongodb Oct 25, 2023
f024bef
Use bracket comment for documentation
eramongodb Oct 25, 2023
9a3c573
Use PARSED_KEYWORDS_MISSING_VALUES instead of string comparison
eramongodb Oct 25, 2023
7a02a2a
Take advantage of multi-glob support
eramongodb Oct 25, 2023
2bee306
Rename include/exclude parameters to distinguish expected use
eramongodb Oct 25, 2023
21e506f
Fix exclude regexes with incorrect path prefix
eramongodb Oct 25, 2023
508fa2a
Reduce verbosity of linking with test properties target
eramongodb Oct 25, 2023
20c5f0e
Reformat argument lists with indentation
eramongodb Oct 25, 2023
28addeb
Reduce macro guard targets to a single static library
eramongodb Oct 25, 2023
097aeea
Add missing include for to_json() in to_string.hh
eramongodb Oct 25, 2023
a1bcba8
Fix glob expression for included headers
eramongodb Oct 25, 2023
d87a87d
Simplify and improve robustness of test library targets
eramongodb Oct 25, 2023
f271cb1
Merge remote-tracking branch 'upstream/master' into HEAD
eramongodb Oct 26, 2023
506ee47
CXX-2769 Document out-of-place BSONCXX_ENUM guards in macro guard hea…
eramongodb Oct 25, 2023
c1b2fc6
CXX-2770 Workaround missing postlude headers via a test macro
eramongodb Oct 25, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .evergreen/compile.sh
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,15 @@ esac

cd build
"${cmake_binary}" -G "$GENERATOR" "-DCMAKE_BUILD_TYPE=${BUILD_TYPE}" -DBUILD_TESTING=ON -DMONGOCXX_ENABLE_SLOW_TESTS=ON -DENABLE_UNINSTALL=ON "$@" ..

if [[ "${COMPILE_MACRO_GUARD_TESTS:-"OFF"}" == "ON" ]]; then
# We only need to compile the macro guard tests.
"${cmake_binary}" -DENABLE_MACRO_GUARD_TESTS=ON ..
"${cmake_binary}" --build . --config $BUILD_TYPE --target test_bsoncxx_macro_guards test_mongocxx_macro_guards -- $CMAKE_BUILD_OPTS
exit # Nothing else to be done.
fi

# Regular build and install routine.
"${cmake_binary}" --build . --config $BUILD_TYPE -- $CMAKE_BUILD_OPTS
"${cmake_binary}" --build . --config $BUILD_TYPE --target install -- $CMAKE_BUILD_OPTS
"${cmake_binary}" --build . --config $BUILD_TYPE --target $CMAKE_EXAMPLES_TARGET -- $CMAKE_BUILD_OPTS
Expand Down
12 changes: 12 additions & 0 deletions .mci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,7 @@ functions:
set -o errexit
set -o pipefail
export BUILD_TYPE=${build_type}
export COMPILE_MACRO_GUARD_TESTS=${COMPILE_MACRO_GUARD_TESTS}
export PATH="${extra_path}:$PATH"
export RUN_DISTCHECK=${RUN_DISTCHECK}

Expand Down Expand Up @@ -957,6 +958,14 @@ tasks:
vars:
ENABLE_TESTS: OFF

- name: compile_macro_guard_tests
commands:
- func: "setup"
- func: "fetch_c_driver_source"
- func: "compile"
vars:
COMPILE_MACRO_GUARD_TESTS: ON

- name: compile_and_test_auth_with_shared_libs
commands:
- func: "setup"
Expand Down Expand Up @@ -1806,6 +1815,7 @@ buildvariants:
tasks:
- name: clang-tidy
- name: compile_without_tests
- name: compile_macro_guard_tests
- name: test_atlas_task_group_search_indexes

- name: ubuntu2204-debug-gcc
Expand All @@ -1820,6 +1830,7 @@ buildvariants:
- ubuntu2204-small
tasks:
- name: compile_without_tests
- name: compile_macro_guard_tests

- name: ubuntu2204-debug-clang
display_name: "Ubuntu 22.04 Debug (Clang)"
Expand All @@ -1833,6 +1844,7 @@ buildvariants:
- ubuntu2204-small
tasks:
- name: compile_without_tests
- name: compile_macro_guard_tests

- name: mongohouse-ubuntu
display_name: "Mongohouse Test"
Expand Down
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,7 @@ set(THIRD_PARTY_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src/third_party)
set(DATA_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/data)

option(ENABLE_TESTS "Build MongoDB C++ Driver tests." ON)
option(ENABLE_MACRO_GUARD_TESTS "Enable targets for macro guard compile tests." OFF)

if(ENABLE_TESTS)
enable_testing()
Expand Down
3 changes: 3 additions & 0 deletions cmake/BsoncxxUtil.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,11 @@ function(bsoncxx_add_library TARGET OUTPUT_NAME LINK_TYPE)
${TARGET}
PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include/bsoncxx/v_noabi>
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/lib/bsoncxx/v_noabi>
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/lib>
$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/lib/bsoncxx/v_noabi>
$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/lib>
)
target_compile_definitions(${TARGET} PRIVATE ${libbson_definitions})
endfunction(bsoncxx_add_library)
1 change: 1 addition & 0 deletions cmake/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ add_subdirectory(make_dist)

set(cmake_MODULES
ParseVersion.cmake
MacroGuardTest.cmake
BsoncxxUtil.cmake
MongocxxUtil.cmake
)
Expand Down
135 changes: 135 additions & 0 deletions cmake/MacroGuardTest.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
#[==[
Define a build-time static library target whose compilation asserts that
header files are properly guarding config macros using inclusion of
prelude/postlude headers.

Usage:

add_macro_guard_test(
PROJECT_NAME <name>
PROJECT_TEST_PROPERTIES_TARGET <target>
INCLUDE_PATTERNS [pattern...]
[EXCLUDE_REGEXES [pattern...]]
GUARDED_MACROS [macro...]
)

PROJECT_NAME
Either "bsoncxx" or "mongocxx".

PROJECT_TEST_PROPERTIES_TARGET
Either the bsoncxx_test_properties or mongocxx_test_properties target.

GUARDED_MACROS
List of macros that should be guarded by prelude/postlude headers.

INCLUDE_PATTERNS
List of regex filters for headers to be added to the tests. Must be
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
List of regex filters for headers to be added to the tests. Must be
List of glob expressions for headers to be added to the tests. Must be

relative to PROJECT_SOURCE_DIR.

EXCLUDE_REGEXES
List of regex filters for headers to be excluded. Applied after
INCLUDE_PATTERNS.
]==]
function(add_macro_guard_test)
set(opt_args "")
set(single_args "PROJECT_NAME;PROJECT_TEST_PROPERTIES_TARGET")
set(multi_args "GUARDED_MACROS;INCLUDE_PATTERNS;EXCLUDE_REGEXES")

cmake_parse_arguments(PARSED "${opt_args}" "${single_args}" "${multi_args}" ${ARGN})

if(NOT "${PARSED_UNPARSED_ARGUMENTS}" STREQUAL "")
message(FATAL_ERROR "unrecognized argument: ${PARSED_UNPARSED_ARGUMENTS}")
endif()

foreach(required_arg PROJECT_NAME PROJECT_TEST_PROPERTIES_TARGET GUARDED_MACROS INCLUDE_PATTERNS)
if("${required_arg}" IN_LIST PARSED_KEYWORDS_MISSING_VALUES)
message(FATAL_ERROR "missing value for required argument ${required_arg}")
endif()
endforeach()

list(TRANSFORM PARSED_INCLUDE_PATTERNS PREPEND "${PROJECT_SOURCE_DIR}/")
file(GLOB_RECURSE GUARDED_HEADERS
LIST_DIRECTORIES false
RELATIVE ${PROJECT_SOURCE_DIR}
${PARSED_INCLUDE_PATTERNS}
)

foreach(filter ${PARSED_EXCLUDE_REGEXES})
list(FILTER GUARDED_HEADERS EXCLUDE REGEX "${filter}")
endforeach()

set(MACRO_GUARD_TEST_PRELUDE "")

# Check and set initial state.
foreach(macro ${PARSED_GUARDED_MACROS})
string(APPEND MACRO_GUARD_TEST_PRELUDE
"#if defined(${macro})\n"
"#error \"${macro} is already defined\"\n"
"#endif\n"
"#define ${macro} macro guard test\n"
"\n"
)
endforeach()

# Implement as recursive algorithm for C++11 compatibility.
string(APPEND MACRO_GUARD_TEST_PRELUDE
"static constexpr bool compare_equal(const char* lhs, const char* rhs) {\n"
" return (*lhs == *rhs) && (*lhs == '\\0' || compare_equal(lhs + 1, rhs + 1));\n"
"}\n"
"\n"
"static_assert(compare_equal(\"abc\", \"abc\"), \"compare_equal() sanity check failed\");\n"
"static_assert(!compare_equal(\"abc\", \"def\"), \"compare_equal() sanity check failed\");\n"
"\n"
"#define _TO_STR(x) #x\n"
"#define TO_STR(x) _TO_STR(x)\n"
"\n"
)
Comment on lines +75 to +86
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use a [[bracket string]] to avoid the quoting and escaping.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am preferential towards being consistent with the other quoted arguments that require variable expansions (incompatible with bracket arguments) rather than using a bracket argument that requires unusual indentation to avoid it being reflected in the generated source file:

    # Check and set initial state.
    foreach(macro ${PARSED_GUARDED_MACROS})
        string(APPEND MACRO_GUARD_TEST_PRELUDE
            "#if defined(${macro})\n"
            "#error \"${macro} is already defined\"\n"
            "#endif\n"
            "#define ${macro} macro guard test\n"
            "\n"
        )
    endforeach()

    # Implement as recursive algorithm for C++11 compatibility.
    string(APPEND MACRO_GUARD_TEST_PRELUDE [[
static constexpr bool compare_equal(const char* lhs, const char* rhs) {
    return (*lhs == *rhs) && (*lhs == '\0' || compare_equal(lhs + 1, rhs + 1));
}

static_assert(compare_equal("abc", "abc"), "compare_equal() sanity check failed");
static_assert(!compare_equal("abc", "def"), "compare_equal() sanity check failed");

#define _TO_STR(x) #x
#define TO_STR(x) _TO_STR(x)

    ]])

Would you rather we still use a bracket argument here?

Copy link
Contributor

@vector-of-bool vector-of-bool Oct 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll leave it up to you. In such cases I don't usually mind odd indentation in generated sources, and I use string(CONFIGURE) to do variable substitutions within large bracket strings.


add_library(test_${PARSED_PROJECT_NAME}_macro_guards STATIC EXCLUDE_FROM_ALL)
target_link_libraries(test_${PARSED_PROJECT_NAME}_macro_guards PRIVATE ${PARSED_PROJECT_TEST_PROPERTIES_TARGET})

# Test each header individually.
foreach(header ${GUARDED_HEADERS})
set(MACRO_GUARD_TEST "${MACRO_GUARD_TEST_PRELUDE}")

# Strip the subdir.
string(REGEX REPLACE "^(include|lib|test)/(.*)$" "\\1" subdir "${header}")
string(REGEX REPLACE "^(include|lib|test)/(.*)$" "\\2" header ${header})

# Apply include prefix to test headers.
if("${subdir}" STREQUAL "test")
set(relheader "${PARSED_PROJECT_NAME}/test/${header}")
else()
set(relheader "${header}")
endif()

# CXX-2770: workaround missing postlude header includes.
string(TOUPPER "${PARSED_PROJECT_NAME}" project_name_upper)
string(APPEND MACRO_GUARD_TEST "#define ${project_name_upper}_TEST_MACRO_GUARDS_FIX_MISSING_POSTLUDE\n\n")

# The include directive.
string(APPEND MACRO_GUARD_TEST "#include <${relheader}>\n\n")

# Test all guarded macros have been properly restored.
foreach(macro ${PARSED_GUARDED_MACROS})
string(APPEND MACRO_GUARD_TEST
"static_assert(\n"
" compare_equal(TO_STR(${macro}),\"macro guard test\"),\n"
" \"${macro} was not correctly restored by <${relheader}>\"\n"
");\n"
"\n"
)
endforeach()

# e.g. bsoncxx/v_noabi/bsoncxx/document/view.hpp -> bsoncxx-v_noabi-bsoncxx-document-view.cpp
string(REPLACE "/" "-" test_name "${header}")
string(REGEX REPLACE "^(.*)\\.(hh|hpp)$" "\\1" test_name "${test_name}")

# e.g. macro_guards/(include|lib|test)/bsoncxx-v_noabi-bsoncxx-document-view.cpp
configure_file(test_macro_guards.cpp.in macro_guards/${subdir}/${test_name}.cpp)

target_sources(test_${PARSED_PROJECT_NAME}_macro_guards PRIVATE
${CMAKE_CURRENT_BINARY_DIR}/macro_guards/${subdir}/${test_name}.cpp
)
endforeach()
endfunction()
3 changes: 3 additions & 0 deletions cmake/MongocxxUtil.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,11 @@ function(mongocxx_add_library TARGET OUTPUT_NAME LINK_TYPE)
${TARGET}
PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include/mongocxx/v_noabi>
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/lib/mongocxx/v_noabi>
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/lib>
$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/lib/mongocxx/v_noabi>
$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/lib>
)
target_compile_definitions(${TARGET} PRIVATE ${libmongoc_definitions})
endfunction(mongocxx_add_library)
5 changes: 4 additions & 1 deletion src/bsoncxx/cmake/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ if(1)
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT runtime
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT dev
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/bsoncxx/v_noabi
INCLUDES DESTINATION
${CMAKE_INSTALL_INCLUDEDIR}/bsoncxx/v_noabi
${CMAKE_INSTALL_INCLUDEDIR}
)

install(EXPORT bsoncxx_targets
Expand All @@ -76,6 +78,7 @@ endif()
if(1)
set(PACKAGE_INCLUDE_INSTALL_DIRS
${CMAKE_INSTALL_INCLUDEDIR}/bsoncxx/v_noabi
${CMAKE_INSTALL_INCLUDEDIR}
${BSONCXX_POLY_MNMLSTC_DEPRECATED_INCLUDE_DIRS}
)
set(PACKAGE_LIBRARY_INSTALL_DIRS ${CMAKE_INSTALL_LIBDIR})
Expand Down
2 changes: 1 addition & 1 deletion src/bsoncxx/cmake/libbsoncxx-static.pc.in
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@ Description: The MongoDB C++11 BSON Library
URL: http://github.com/mongodb/mongo-cxx-driver
Version: @BSONCXX_VERSION@
Requires: libbson-static-@LIBBSON_REQUIRED_ABI_VERSION@ >= @LIBBSON_REQUIRED_VERSION@
Cflags: @BSONCXX_POLY_MNMLSTC_PKGCONFIG_STATIC_INCLUDE_DIRS@-I${includedir}/bsoncxx/v_noabi -DBSONCXX_STATIC
Cflags: @BSONCXX_POLY_MNMLSTC_PKGCONFIG_STATIC_INCLUDE_DIRS@-I${includedir}/bsoncxx/v_noabi -I${includedir} -DBSONCXX_STATIC
Libs: -L${libdir} -lbsoncxx-static
2 changes: 1 addition & 1 deletion src/bsoncxx/cmake/libbsoncxx.pc.in
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ Name: libbsoncxx
Description: The MongoDB C++11 BSON Library
URL: http://github.com/mongodb/mongo-cxx-driver
Version: @BSONCXX_VERSION@
Cflags: @BSONCXX_POLY_MNMLSTC_PKGCONFIG_INCLUDE_DIRS@-I${includedir}/bsoncxx/v_noabi
Cflags: @BSONCXX_POLY_MNMLSTC_PKGCONFIG_INCLUDE_DIRS@-I${includedir}/bsoncxx/v_noabi -I${includedir}
Libs: -L${libdir} -lbsoncxx
Original file line number Diff line number Diff line change
Expand Up @@ -186,3 +186,8 @@ class array : public list {
} // namespace builder
} // namespace v_noabi
} // namespace bsoncxx

// CXX-2770: missing include of postlude header.
#if defined(BSONCXX_TEST_MACRO_GUARDS_FIX_MISSING_POSTLUDE)
#include <bsoncxx/config/postlude.hpp>
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,6 @@
#undef BSONCXX_VERSION_PATCH
#pragma pop_macro("BSONCXX_VERSION_PATCH")

// bsoncxx/types.hpp
#ifdef BSONCXX_ENUM
static_assert(false, "BSONCXX_ENUM must be undef'ed");
#endif
#pragma pop_macro("BSONCXX_ENUM")

// export.hpp (generated by CMake)
#undef BSONCXX_API_H
#pragma pop_macro("BSONCXX_API_H")
Expand All @@ -72,3 +66,9 @@ static_assert(false, "BSONCXX_ENUM must be undef'ed");
// prelude.hpp
#undef BSONCXX_UNREACHABLE
#pragma pop_macro("BSONCXX_UNREACHABLE")

// CXX-2769: out-of-place, but remains for backward compatibility.
#ifdef BSONCXX_ENUM
static_assert(false, "BSONCXX_ENUM must be undef'ed");
#endif
#pragma pop_macro("BSONCXX_ENUM")
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,6 @@
#pragma push_macro("BSONCXX_VERSION_PATCH")
#undef BSONCXX_VERSION_PATCH

// bsoncxx/types.hpp
#pragma push_macro("BSONCXX_ENUM")
#undef BSONCXX_ENUM

// export.hpp (generated by CMake)
#pragma push_macro("BSONCXX_API_H")
#undef BSONCXX_API_H
Expand Down Expand Up @@ -73,6 +69,10 @@
#undef BSONCXX_UNREACHABLE
#define BSONCXX_UNREACHABLE std::abort()

// CXX-2769: out-of-place, but remains for backward compatibility.
#pragma push_macro("BSONCXX_ENUM")
#undef BSONCXX_ENUM

// Doxygen does not account for generated header files.
// Document globally applicable macros and namespaces here.

Expand Down
27 changes: 18 additions & 9 deletions src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,6 @@
// limitations under the License.

#pragma once
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wfloat-equal"
#elif defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wfloat-equal"
#endif

#include <chrono>
#include <cstring>
Expand All @@ -32,6 +25,17 @@

#include <bsoncxx/config/prelude.hpp>

#pragma push_macro("BSONCXX_ENUM")
#undef BSONCXX_ENUM

#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wfloat-equal"
#elif defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wfloat-equal"
#endif

namespace bsoncxx {
inline namespace v_noabi {
///
Expand Down Expand Up @@ -683,10 +687,15 @@ BSONCXX_INLINE bool operator==(const b_maxkey&, const b_maxkey&) {
} // namespace v_noabi
} // namespace bsoncxx

#include <bsoncxx/config/postlude.hpp>

#if defined(__clang__)
#pragma clang diagnostic pop
#elif defined(__GNUC__)
#pragma GCC diagnostic pop
#endif

#ifdef BSONCXX_ENUM
static_assert(false, "BSONCXX_ENUM must be undef'ed");
#endif
#pragma pop_macro("BSONCXX_ENUM")

#include <bsoncxx/config/postlude.hpp>
Original file line number Diff line number Diff line change
Expand Up @@ -330,3 +330,5 @@ BSONCXX_INLINE void convert_from_libbson(bson_value_t* v, bsoncxx::types::b_arra
} // namespace types
} // namespace v_noabi
} // namespace bsoncxx

#include <bsoncxx/config/private/postlude.hh>
Loading