Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
49269bc
feat: add google/re2 and abseil-cpp dependencies
Copilot May 13, 2026
d625ab2
fix: enable ABSL_ENABLE_INSTALL to resolve re2 export set error
Copilot May 13, 2026
eee7191
build: pin abseil to stable 20240722.0
Copilot May 13, 2026
1dd92dd
build: avoid sanitizer breakage with gcc 14 in standalone builds
Copilot May 13, 2026
63e588c
chore: fix sanitizer skip status message wording
Copilot May 13, 2026
907298c
fix: move sanitizer flags after external deps to avoid Abseil GCC+ASa…
Copilot May 13, 2026
e9d7ecc
feat: add RegexStrategy with StdRegexStrategy and Re2RegexStrategy im…
Copilot May 16, 2026
5ce2030
refactor: remove unused pattern members, add RE2 error handling and i…
Copilot May 16, 2026
a29ca14
refactor: split RegexStrategy into separate files per class, use type…
Copilot May 16, 2026
89aae22
feat: make RE2 optional via CCR_USE_RE2, add RegexStrategyFactory
Copilot May 17, 2026
54f7af7
feat: update RegexStrategy::Match to return MatchGroup with positions…
Copilot May 26, 2026
bf801be
refactor: remove matched bool from MatchGroup; use optional<MatchGrou…
Copilot May 26, 2026
c38ec6b
refactor: update Match method signatures to use Matches type; adjust …
daantimmer May 26, 2026
a985e36
feat: add conditional compilation for RE2 support in CMakeLists.txt
daantimmer May 26, 2026
9410cea
fix: rewrite float regex to be RE2-compatible, removing positive look…
Copilot May 26, 2026
16b9446
refactor: update Match method parameters to use const references; imp…
daantimmer May 27, 2026
3aa5c08
ci: add ubuntu no-re2 variant to test matrix
Copilot May 27, 2026
a7c7ffb
ci: extend no-re2 test matrix to windows and macos
Copilot May 27, 2026
d8edcca
ci: simplify no-re2 matrix by adding preset_suffix entry instead of i…
Copilot May 27, 2026
37f8bba
refactor: simplify Re2RegexStrategy by removing unique_ptr and updati…
daantimmer May 27, 2026
977ef3b
Gate find_package(absl) and find_package(re2) on CCR_USE_RE2
Copilot May 28, 2026
412b9ea
fix: construct RE2 with explicit absl::string_view
Copilot May 28, 2026
928f84e
docs: document cmake options and fetch deps usage
Copilot May 28, 2026
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
7 changes: 4 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,21 +82,22 @@ jobs:
os: [macos, windows, ubuntu]
version: [latest]
type: [synchronous]
preset_suffix: ["", "-no-re2"]
fail-fast: false
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: hendrikmuhs/ccache-action@33522472633dbd32578e909b315f5ee43ba878ce # v1.2.22
with:
key: ${{ github.job }}-${{ matrix.os }}-${{ matrix.type }}
key: ${{ github.job }}-${{ matrix.os }}-${{ matrix.type }}${{ matrix.preset_suffix }}
max-size: 2G
- uses: lukka/run-cmake@5d55ea7949e25f69f0ecb516d8d572297e03a956 # v10.9
with:
workflowPreset: "${{ matrix.os }}-Debug-${{ matrix.type }}"
workflowPreset: "${{ matrix.os }}-Debug-${{ matrix.type }}${{ matrix.preset_suffix }}"
- name: Upload test logs
if: ${{ failure() }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: test-logs
path: .build/${{ matrix.os }}-Debug-${{ matrix.type }}/Testing/Temporary/
path: .build/${{ matrix.os }}-Debug-${{ matrix.type }}${{ matrix.preset_suffix }}/Testing/Temporary/
30 changes: 23 additions & 7 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,12 @@ endif()

option(CCR_FETCH_DEPS "Fetch dependencies via FetchContent." ${CCR_DEFAULTOPT} )
option(CCR_BUILD_TESTS "Enable build of the tests" ${CCR_DEFAULTOPT})
option(CCR_USE_RE2 "Use the RE2 regex engine (fetched when CCR_FETCH_DEPS is set, otherwise auto-detected)" ${CCR_DEFAULTOPT})
option(CCR_ENABLE_COVERAGE "Enable compiler flags for code coverage measurements" Off)
option(CCR_ENABLE_TIME_PROFILE "Enable compiler flags for time profiling measurements" Off)

set(CMAKE_POSITION_INDEPENDENT_CODE ON)

if (CCR_STANDALONE AND NOT CCR_ENABLE_COVERAGE)
if (NOT (CMAKE_CXX_COMPILER_ID MATCHES "MSVC" OR CMAKE_CXX_SIMULATE_ID MATCHES "MSVC"))
add_compile_options(-fsanitize=address -fsanitize=undefined)
add_link_options(-fsanitize=address -fsanitize=undefined)
endif()
endif()

if (CCR_BUILD_TESTS)
ccr_enable_testing()
endif()
Expand Down Expand Up @@ -81,6 +75,28 @@ else()
if (CCR_BUILD_TESTS)
find_package(yaml-cpp REQUIRED)
endif()

if (CCR_USE_RE2)
find_package(absl)
find_package(re2)
endif()
endif()

if (TARGET re2::re2)
set(CCR_HAS_RE2 On)
else()
set(CCR_HAS_RE2 Off)
endif()

# Sanitizer flags are set after external dependencies so that fetched third-party
# libraries (abseil, re2) are not compiled with sanitizer instrumentation.
# Abseil's constexpr function-pointer comparisons are not compatible with GCC's
# ASan/UBSan instrumentation, which causes build failures.
if (CCR_STANDALONE AND NOT CCR_ENABLE_COVERAGE)
if (NOT (CMAKE_CXX_COMPILER_ID MATCHES "MSVC" OR CMAKE_CXX_SIMULATE_ID MATCHES "MSVC"))
add_compile_options(-fsanitize=address -fsanitize=undefined)
add_link_options(-fsanitize=address -fsanitize=undefined)
endif()
endif()

add_subdirectory(cucumber_cpp)
Expand Down
109 changes: 109 additions & 0 deletions CMakePresets.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,31 @@
"displayName": "Configuration for macOS Debug and Tests, Single Config Generator, synchronous",
"inherits": "host-single-Debug-synchronous"
},
{
"name": "ubuntu-Debug-synchronous-no-re2",
"displayName": "Configuration for Linux Debug and Tests, Single Config Generator, synchronous, without RE2",
"inherits": "host-single-Debug-synchronous",
"cacheVariables": {
"CCR_USE_RE2": "Off"
}
},
{
"name": "windows-Debug-synchronous-no-re2",
"displayName": "Configuration for Windows Debug and Tests, Single Config Generator, synchronous, without RE2",
"inherits": "host-single-Debug-synchronous",
"toolchainFile": "${sourceDir}/cmake/Windows.MSVC.toolchain.cmake",
"cacheVariables": {
"CCR_USE_RE2": "Off"
}
},
{
"name": "macos-Debug-synchronous-no-re2",
"displayName": "Configuration for macOS Debug and Tests, Single Config Generator, synchronous, without RE2",
"inherits": "host-single-Debug-synchronous",
"cacheVariables": {
"CCR_USE_RE2": "Off"
}
},
{
"name": "host-single-time-profile",
"displayName": "Configuration time profiling",
Expand Down Expand Up @@ -124,6 +149,21 @@
"configuration": "Debug",
"configurePreset": "macos-Debug-synchronous"
},
{
"name": "ubuntu-Debug-synchronous-no-re2",
"configuration": "Debug",
"configurePreset": "ubuntu-Debug-synchronous-no-re2"
},
{
"name": "windows-Debug-synchronous-no-re2",
"configuration": "Debug",
"configurePreset": "windows-Debug-synchronous-no-re2"
},
{
"name": "macos-Debug-synchronous-no-re2",
"configuration": "Debug",
"configurePreset": "macos-Debug-synchronous-no-re2"
},
{
"name": "host-single-time-profile",
"configuration": "Debug",
Expand Down Expand Up @@ -192,6 +232,24 @@
"configurePreset": "macos-Debug-synchronous",
"configuration": "Debug",
"inherits": "defaults"
},
{
"name": "ubuntu-Debug-synchronous-no-re2",
"configurePreset": "ubuntu-Debug-synchronous-no-re2",
"configuration": "Debug",
"inherits": "defaults"
},
{
"name": "windows-Debug-synchronous-no-re2",
"configurePreset": "windows-Debug-synchronous-no-re2",
"configuration": "Debug",
"inherits": "defaults"
},
{
"name": "macos-Debug-synchronous-no-re2",
"configurePreset": "macos-Debug-synchronous-no-re2",
"configuration": "Debug",
"inherits": "defaults"
}
],
"workflowPresets": [
Expand Down Expand Up @@ -245,6 +303,57 @@
"name": "macos-Debug-synchronous"
}
]
},
{
"name": "ubuntu-Debug-synchronous-no-re2",
"steps": [
{
"type": "configure",
"name": "ubuntu-Debug-synchronous-no-re2"
},
{
"type": "build",
"name": "ubuntu-Debug-synchronous-no-re2"
},
{
"type": "test",
"name": "ubuntu-Debug-synchronous-no-re2"
}
]
},
{
"name": "windows-Debug-synchronous-no-re2",
"steps": [
{
"type": "configure",
"name": "windows-Debug-synchronous-no-re2"
},
{
"type": "build",
"name": "windows-Debug-synchronous-no-re2"
},
{
"type": "test",
"name": "windows-Debug-synchronous-no-re2"
}
]
},
{
"name": "macos-Debug-synchronous-no-re2",
"steps": [
{
"type": "configure",
"name": "macos-Debug-synchronous-no-re2"
},
{
"type": "build",
"name": "macos-Debug-synchronous-no-re2"
},
{
"type": "test",
"name": "macos-Debug-synchronous-no-re2"
}
]
}
]
}
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,32 @@ amp-cucumber-cpp-runner is known to build using:

amp-cucumber-cpp-runner is not supposed to be used standalone. It is possible to add amp-cucumber-cpp-runner as a submodule to your own project or to use cmake's FetchContent to add amp-cucumber-cpp-runner as a cmake build dependency.

En example project is provided which shows most features of amp-cucumber-cpp-runner and how to configure a cmake project to use amp-cucumber-cpp-runner. The simplest solution is to simply add a dependency on `cucumber-cpp-runner` like so:
An example project is provided which shows most features of amp-cucumber-cpp-runner and how to configure a cmake project to use amp-cucumber-cpp-runner.

### CMake options

The following CMake options are supported:

- `CCR_FETCH_DEPS` (default: `Off` when used as a dependency): fetch third-party dependencies via `FetchContent`.
- `CCR_BUILD_TESTS` (default: `Off` when used as a dependency): enable building unit/integration tests in this repository.
- `CCR_USE_RE2` (default: `Off` when used as a dependency): enable optional RE2 usage (`abseil-cpp` + `re2`).
- `CCR_ENABLE_COVERAGE` (default: `Off`): enable compiler flags for coverage measurements.
- `CCR_ENABLE_TIME_PROFILE` (default: `Off`): enable compiler time-trace profiling flags (Clang).

### FetchContent example

If you want this project to fetch its dependencies, set `CCR_FETCH_DEPS` explicitly to `On`:

```cmake
FetchContent_Declare(cucumber_cpp
GIT_REPOSITORY https://github.com/philips-software/amp-cucumber-cpp-runner.git
GIT_TAG main
)

set(CCR_FETCH_DEPS On CACHE BOOL "Fetch amp-cucumber-cpp-runner dependencies")
# optional:
# set(CCR_USE_RE2 On CACHE BOOL "Enable RE2 backend")

FetchContent_MakeAvailable(cucumber_cpp)

add_executable(my_test_runner)
Expand Down
20 changes: 20 additions & 0 deletions cucumber_cpp/library/cucumber_expression/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,13 @@ target_sources(cucumber_cpp.library.cucumber_expression PRIVATE
MatchRange.hpp
ParameterRegistry.cpp
ParameterRegistry.hpp
RegexStrategy.hpp
RegexStrategyFactory.cpp
RegexStrategyFactory.hpp
RegularExpression.cpp
RegularExpression.hpp
StdRegexStrategy.cpp
StdRegexStrategy.hpp
TreeRegexp.cpp
TreeRegexp.hpp
)
Expand All @@ -33,6 +38,21 @@ target_link_libraries(cucumber_cpp.library.cucumber_expression PUBLIC
fmt::fmt
)

if (CCR_HAS_RE2)
target_sources(cucumber_cpp.library.cucumber_expression PRIVATE
Re2RegexStrategy.cpp
Re2RegexStrategy.hpp
)

target_compile_definitions(cucumber_cpp.library.cucumber_expression PRIVATE
CCR_HAS_RE2
)

target_link_libraries(cucumber_cpp.library.cucumber_expression PUBLIC
re2::re2
)
endif()

if (CCR_BUILD_TESTS)
add_subdirectory(test)
endif()
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ namespace cucumber_cpp::library::cucumber_expression
{
const static std::string integerNegativeRegex{ R"__(-?\d+)__" };
const static std::string integerPositiveRegex{ R"__(\d+)__" };
const static std::string floatRegex{ R"__((?=.*\d.*)[-+]?\d*(?:\.(?=\d.*))?\d*(?:\d+[E][+-]?\d+)?)__" };
const static std::string floatRegex{ R"__([-+]?(?:\d+(?:\.\d+)?|\.\d+)(?:[Ee][+-]?\d+)?)__" };
Comment thread
daantimmer marked this conversation as resolved.
const static std::string stringRegex{ R"__("([^"\\]*(\\.[^"\\]*)*)"|'([^'\\]*(\\.[^'\\]*)*)')__" };
const static std::string wordRegex{ R"__([^\s]+)__" };
const static std::string anonymousRegex{ R"__(.*)__" };
Expand Down
48 changes: 48 additions & 0 deletions cucumber_cpp/library/cucumber_expression/Re2RegexStrategy.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#include "cucumber_cpp/library/cucumber_expression/Re2RegexStrategy.hpp"
#include "absl/strings/string_view.h"
#include "cucumber_cpp/library/cucumber_expression/RegexStrategy.hpp"
#include <cstddef>
#include <optional>
#include <re2/re2.h>
#include <stdexcept>
#include <string>
#include <string_view>
#include <vector>

namespace cucumber_cpp::library::cucumber_expression
{
Re2RegexStrategy::Re2RegexStrategy(std::string_view pattern)
: re2{ absl::string_view{ pattern.data(), pattern.size() } }
{
if (!re2.ok())
throw std::invalid_argument(re2.error());
}
Comment thread
daantimmer marked this conversation as resolved.

std::optional<Matches> Re2RegexStrategy::Match(std::string_view text) const
{
const int nCaptures = re2.NumberOfCapturingGroups();
const int nSubmatch = nCaptures + 1;
std::vector<absl::string_view> submatch(static_cast<std::size_t>(nSubmatch));

if (!re2.Match(text, 0, static_cast<int>(text.size()), RE2::UNANCHORED, submatch.data(), nSubmatch))
return std::nullopt;

Matches result;
result.reserve(static_cast<std::size_t>(nSubmatch));
for (const auto& piece : submatch)
{
if (piece.data() == nullptr)
result.emplace_back(std::nullopt);
else
{
const auto start = static_cast<std::size_t>(piece.data() - text.data());
result.emplace_back(MatchGroup{
.value = std::string(piece),
.start = start,
.end = start + piece.size(),
});
}
}
return result;
}
}
22 changes: 22 additions & 0 deletions cucumber_cpp/library/cucumber_expression/Re2RegexStrategy.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#ifndef CUCUMBER_EXPRESSION_RE2REGEXSTRATEGY_HPP
#define CUCUMBER_EXPRESSION_RE2REGEXSTRATEGY_HPP

#include "cucumber_cpp/library/cucumber_expression/RegexStrategy.hpp"
#include "re2/re2.h"
#include <optional>
#include <string_view>

namespace cucumber_cpp::library::cucumber_expression
{
struct Re2RegexStrategy : RegexStrategy
{
explicit Re2RegexStrategy(std::string_view pattern);

[[nodiscard]] std::optional<Matches> Match(std::string_view text) const override;

private:
re2::RE2 re2;
};
}

#endif
30 changes: 30 additions & 0 deletions cucumber_cpp/library/cucumber_expression/RegexStrategy.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#ifndef CUCUMBER_EXPRESSION_REGEXSTRATEGY_HPP
#define CUCUMBER_EXPRESSION_REGEXSTRATEGY_HPP

#include <cstddef>
#include <optional>
#include <string>
#include <string_view>
#include <vector>

namespace cucumber_cpp::library::cucumber_expression
{
struct MatchGroup
{
std::string value;
std::size_t start;
std::size_t end;
};

using Matches = std::vector<std::optional<MatchGroup>>;

struct RegexStrategy
{
RegexStrategy() = default;
virtual ~RegexStrategy() = default;

[[nodiscard]] virtual std::optional<Matches> Match(std::string_view text) const = 0;
};
}

#endif
Loading
Loading