Thank you for your interest in contributing to PCRE4J! This document provides guidelines and instructions for contributing.
Please use the bug report template to report bugs. Include as much detail as possible, including your Java version, operating system, and PCRE2 library version.
Use the feature request template to suggest new features or enhancements.
- Fork the repository and create a branch from
main - Make your changes
- Ensure the build passes (see Build Instructions below)
- Submit a pull request against
main
The main branch has the following protection rules:
- Required status checks: The
packageCI job must pass before merging (this transitively requireslintand allcompatibilitymatrix jobs to pass as well) - Branch must be up to date: PRs must be up to date with
mainbefore merging - Required reviews: At least 1 approving review is required
- Stale review dismissal: Approvals are dismissed when new commits are pushed
- Signed commits: All commits must be signed (how to sign commits)
- Admin enforcement: These rules apply to everyone, including administrators
- No force pushes: Force pushes to
mainare not allowed - No branch deletion: The
mainbranch cannot be deleted
All commits must be signed off to certify that you have the right to submit the contribution under the project's license. Add a Signed-off-by line to every commit message:
(feat) add new feature
Signed-off-by: Your Name <your.email@example.com>
Use git commit -s to automatically add this line. If you forget, you can amend the last commit with git commit --amend -s.
- Line length: 120 characters (enforced by Checkstyle)
- Indentation: 4 spaces (no tabs)
- Charset: UTF-8 with LF line endings
- Copyright header: Required on all source files (LGPL-3.0 notice)
- JavaDoc: Required on all public APIs
Use the format (type) brief description:
(feat)— new feature(fix)— bug fix(chore)— maintenance (build, dependencies, CI)(docs)— documentation changes
Examples:
(feat) regex: implement CANON_EQ flag support(fix) matcher: handle empty region anchor semantics(chore) gradle: upgrade to 9.3.0
PCRE2 must be installed on your system:
- Ubuntu/Debian:
sudo apt install libpcre2-8-0 - macOS:
brew install pcre2 - Windows: Download PCRE2 DLL and add to PATH
# Full build with tests
./gradlew build -Dpcre2.library.path=/usr/lib/x86_64-linux-gnu
# macOS (Apple Silicon)
./gradlew build -Dpcre2.library.path=/opt/homebrew/lib
# macOS (Intel)
./gradlew build -Dpcre2.library.path=/usr/local/lib
# Code style check
./gradlew checkstyleMain checkstyleTestEnsure the following pass:
./gradlew build— compilation and tests./gradlew checkstyleMain checkstyleTest— code style
If your changes add new PCRE2 API bindings, update PCRE2_API.md to mark the API as implemented.
If your changes update dependencies, regenerate the verification metadata:
./gradlew --write-verification-metadata sha256,pgp --export-keys helpThis updates gradle/verification-metadata.xml and gradle/verification-keyring.keys with checksums and PGP signatures for the new dependencies. Commit both files with your dependency change.
PCRE4J supports two native backends — JNA and FFM — and tests must verify behavior across both. The test infrastructure provides several patterns for backend instantiation depending on the test's scope.
Most tests in the lib and regex modules use JUnit 5 parameterized tests with a shared BackendProvider that supplies both backend instances. Each test method runs once per backend automatically:
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.pcre4j.api.IPcre2;
@ParameterizedTest
@MethodSource("org.pcre4j.test.BackendProvider#parameters")
void myTest(IPcre2 api) {
var code = new Pcre2Code(api, "\\d+");
// ... assertions using api
}BackendProvider (in lib/src/testFixtures) loads backends reflectively to avoid compile-time coupling:
public static Stream<Arguments> parameters() {
return Stream.of(
Arguments.of(loadBackend("org.pcre4j.jna.Pcre2")),
Arguments.of(loadBackend("org.pcre4j.ffm.Pcre2"))
);
}Use this pattern when testing lib or regex classes that accept an IPcre2 parameter and should behave identically regardless of backend.
The lib/src/testFixtures directory defines contract test interfaces — Java interfaces with default test methods that act as reusable test traits:
public interface Pcre2MatchingContractTest<T extends IPcre2> {
T getApi();
@Test
default void plainStringMatch() {
var code = new Pcre2Code(getApi(), "42", EnumSet.noneOf(Pcre2CompileOption.class), null);
// ... assertions
}
}There are 13 contract interfaces covering all PCRE2 functionality areas (configuration, matching, substitution, substrings, match context, DFA matching, compile context, serialization, JIT, pattern conversion, callout, miscellaneous, and UTF width support).
The abstract org.pcre4j.test.Pcre2Tests base class aggregates all 12 contract interfaces. Each backend module extends this class and provides its own backend instance:
// In jna/src/test/java/org/pcre4j/jna/Pcre2Tests.java
public class Pcre2Tests extends org.pcre4j.test.Pcre2Tests {
public Pcre2Tests() {
super(new Pcre2()); // Direct JNA backend instantiation
}
@Override
public IPcre2 createApi(Pcre2UtfWidth width) {
return new Pcre2(width);
}
}The FFM backend follows the same structure. This pattern guarantees both backends run identical contract tests and also allows backend-specific tests (e.g., JNA callback handling via com.sun.jna.Callback vs FFM upcall stubs via MethodHandle).
Some tests don't need a backend at all — they test utility logic, enum constants, or bootstrap error handling:
// In lib/src/test/java/org/pcre4j/Pcre4jTests.java
public class Pcre4jTests {
@BeforeEach
void resetSingleton() throws Exception {
Field apiField = Pcre4j.class.getDeclaredField("api");
apiField.setAccessible(true);
apiField.set(null, null);
}
@Test
void api_beforeSetup_throwsIllegalStateException() {
assertThrows(IllegalStateException.class, () -> Pcre4j.api());
}
}Use this pattern for tests that exercise backend-independent code paths.
| Scenario | Pattern | Example |
|---|---|---|
Testing lib/regex classes against both backends |
Parameterized with BackendProvider |
Pcre2CodeTests, PatternTests |
| Adding low-level PCRE2 API contract tests | Contract test interface + Pcre2Tests base class |
Pcre2MatchingContractTest |
| Testing backend-specific behavior (callbacks, FFI) | Backend-specific test methods in jna/ffm Pcre2Tests |
JNA CalloutEnumerateCallback tests |
| Testing utilities, enums, or bootstrap logic | Backend-agnostic tests (no parameterization) | Pcre4jTests, Pcre2EnumTests |
By contributing to PCRE4J, you agree that your contributions will be licensed under the GNU Lesser General Public License v3.0.