Skip to content

Commit 34c49ff

Browse files
committed
Add code generator
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
1 parent 0fd237d commit 34c49ff

File tree

16 files changed

+1993
-2
lines changed

16 files changed

+1993
-2
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,7 @@ DerivedData
8080

8181
# clangd cache
8282
/.cache
83+
84+
# Auto-generated protocol wrapper classes (generated at CMake configure time)
85+
/include/xrpl/protocol_autogen/transactions/
86+
/include/xrpl/protocol_autogen/ledger_objects/

cmake/XrplCore.cmake

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,15 +74,23 @@ add_module(xrpl protocol)
7474
target_link_libraries(xrpl.libxrpl.protocol PUBLIC xrpl.libxrpl.crypto xrpl.libxrpl.json)
7575

7676
# Level 05
77+
add_module(xrpl protocol_autogen)
78+
target_link_libraries(xrpl.libxrpl.protocol_autogen PUBLIC xrpl.libxrpl.protocol)
79+
80+
# Set up code generation for protocol_autogen module
81+
include(XrplProtocolAutogen)
82+
setup_protocol_autogen()
83+
84+
# Level 06
7785
add_module(xrpl core)
7886
target_link_libraries(xrpl.libxrpl.core PUBLIC xrpl.libxrpl.basics xrpl.libxrpl.json
7987
xrpl.libxrpl.protocol)
8088

81-
# Level 06
89+
# Level 07
8290
add_module(xrpl resource)
8391
target_link_libraries(xrpl.libxrpl.resource PUBLIC xrpl.libxrpl.protocol)
8492

85-
# Level 07
93+
# Level 08
8694
add_module(xrpl net)
8795
target_link_libraries(xrpl.libxrpl.net PUBLIC xrpl.libxrpl.basics xrpl.libxrpl.json
8896
xrpl.libxrpl.protocol xrpl.libxrpl.resource)
@@ -140,6 +148,7 @@ target_link_modules(
140148
net
141149
nodestore
142150
protocol
151+
protocol_autogen
143152
rdb
144153
resource
145154
server

cmake/XrplInstall.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ install(TARGETS common
2929
xrpl.libxrpl.net
3030
xrpl.libxrpl.nodestore
3131
xrpl.libxrpl.protocol
32+
xrpl.libxrpl.protocol_autogen
3233
xrpl.libxrpl.resource
3334
xrpl.libxrpl.server
3435
xrpl.libxrpl.shamap

cmake/XrplProtocolAutogen.cmake

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
#[===================================================================[
2+
Protocol Autogen - Code generation for protocol wrapper classes
3+
#]===================================================================]
4+
5+
# Function to set up code generation for protocol_autogen module
6+
# This runs at configure time to generate C++ wrapper classes from macro files
7+
function (setup_protocol_autogen)
8+
# Directory paths
9+
set(MACRO_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include/xrpl/protocol/detail")
10+
set(AUTOGEN_HEADER_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include/xrpl/protocol_autogen")
11+
set(SCRIPTS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/scripts")
12+
13+
# Input macro files
14+
set(TRANSACTIONS_MACRO "${MACRO_DIR}/transactions.macro")
15+
set(LEDGER_ENTRIES_MACRO "${MACRO_DIR}/ledger_entries.macro")
16+
set(SFIELDS_MACRO "${MACRO_DIR}/sfields.macro")
17+
18+
# Python scripts
19+
set(GENERATE_TX_SCRIPT "${SCRIPTS_DIR}/generate_tx_classes.py")
20+
set(GENERATE_LEDGER_SCRIPT "${SCRIPTS_DIR}/generate_ledger_classes.py")
21+
set(REQUIREMENTS_FILE "${SCRIPTS_DIR}/requirements.txt")
22+
23+
# Create output directories
24+
file(MAKE_DIRECTORY "${AUTOGEN_HEADER_DIR}/transactions")
25+
file(MAKE_DIRECTORY "${AUTOGEN_HEADER_DIR}/ledger_objects")
26+
27+
# Find Python3 - check if already found by Conan or find it ourselves
28+
if (NOT Python3_EXECUTABLE)
29+
find_package(Python3 COMPONENTS Interpreter QUIET)
30+
endif ()
31+
32+
if (NOT Python3_EXECUTABLE)
33+
# Try finding python3 executable directly
34+
find_program(Python3_EXECUTABLE NAMES python3 python)
35+
endif ()
36+
37+
if (NOT Python3_EXECUTABLE)
38+
message(FATAL_ERROR "Python3 not found. Code generation cannot proceed.")
39+
return()
40+
endif ()
41+
42+
message(STATUS "Using Python3 for code generation: ${Python3_EXECUTABLE}")
43+
44+
# Set up Python virtual environment for code generation
45+
set(VENV_DIR "${CMAKE_CURRENT_BINARY_DIR}/codegen_venv")
46+
47+
# Determine the Python executable path in the venv
48+
if (WIN32)
49+
set(VENV_PYTHON "${VENV_DIR}/Scripts/python.exe")
50+
set(VENV_PIP "${VENV_DIR}/Scripts/pip.exe")
51+
else ()
52+
set(VENV_PYTHON "${VENV_DIR}/bin/python")
53+
set(VENV_PIP "${VENV_DIR}/bin/pip")
54+
endif ()
55+
56+
# Check if venv needs to be created or updated
57+
set(VENV_NEEDS_UPDATE FALSE)
58+
if (NOT EXISTS "${VENV_PYTHON}")
59+
set(VENV_NEEDS_UPDATE TRUE)
60+
message(STATUS "Creating Python virtual environment for code generation...")
61+
elseif ("${REQUIREMENTS_FILE}" IS_NEWER_THAN "${VENV_DIR}/.requirements_installed")
62+
set(VENV_NEEDS_UPDATE TRUE)
63+
message(STATUS "Updating Python virtual environment (requirements changed)...")
64+
endif ()
65+
66+
# Create/update virtual environment if needed
67+
if (VENV_NEEDS_UPDATE)
68+
message(STATUS "Setting up Python virtual environment at ${VENV_DIR}")
69+
execute_process(COMMAND ${Python3_EXECUTABLE} -m venv "${VENV_DIR}"
70+
RESULT_VARIABLE VENV_RESULT ERROR_VARIABLE VENV_ERROR)
71+
if (NOT VENV_RESULT EQUAL 0)
72+
message(FATAL_ERROR "Failed to create virtual environment: ${VENV_ERROR}")
73+
endif ()
74+
75+
message(STATUS "Installing Python dependencies...")
76+
execute_process(COMMAND ${VENV_PIP} install --upgrade pip RESULT_VARIABLE PIP_UPGRADE_RESULT
77+
OUTPUT_QUIET ERROR_VARIABLE PIP_UPGRADE_ERROR)
78+
if (NOT PIP_UPGRADE_RESULT EQUAL 0)
79+
message(WARNING "Failed to upgrade pip: ${PIP_UPGRADE_ERROR}")
80+
endif ()
81+
82+
execute_process(COMMAND ${VENV_PIP} install -r "${REQUIREMENTS_FILE}"
83+
RESULT_VARIABLE PIP_INSTALL_RESULT ERROR_VARIABLE PIP_INSTALL_ERROR)
84+
if (NOT PIP_INSTALL_RESULT EQUAL 0)
85+
message(FATAL_ERROR "Failed to install Python dependencies: ${PIP_INSTALL_ERROR}")
86+
endif ()
87+
88+
# Mark requirements as installed
89+
file(TOUCH "${VENV_DIR}/.requirements_installed")
90+
message(STATUS "Python virtual environment ready")
91+
endif ()
92+
93+
# Generate transaction classes at configure time
94+
message(STATUS "Generating transaction classes from transactions.macro...")
95+
execute_process(COMMAND ${VENV_PYTHON} "${GENERATE_TX_SCRIPT}" "${TRANSACTIONS_MACRO}"
96+
--header-dir "${AUTOGEN_HEADER_DIR}/transactions" --sfields-macro
97+
"${SFIELDS_MACRO}"
98+
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
99+
RESULT_VARIABLE TX_GEN_RESULT
100+
OUTPUT_VARIABLE TX_GEN_OUTPUT
101+
ERROR_VARIABLE TX_GEN_ERROR)
102+
if (NOT TX_GEN_RESULT EQUAL 0)
103+
message(FATAL_ERROR "Failed to generate transaction classes:\n${TX_GEN_ERROR}")
104+
else ()
105+
message(STATUS "Transaction classes generated successfully")
106+
endif ()
107+
108+
# Generate ledger entry classes at configure time
109+
message(STATUS "Generating ledger entry classes from ledger_entries.macro...")
110+
execute_process(COMMAND ${VENV_PYTHON} "${GENERATE_LEDGER_SCRIPT}" "${LEDGER_ENTRIES_MACRO}"
111+
--header-dir "${AUTOGEN_HEADER_DIR}/ledger_objects" --sfields-macro
112+
"${SFIELDS_MACRO}"
113+
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
114+
RESULT_VARIABLE LEDGER_GEN_RESULT
115+
OUTPUT_VARIABLE LEDGER_GEN_OUTPUT
116+
ERROR_VARIABLE LEDGER_GEN_ERROR)
117+
if (NOT LEDGER_GEN_RESULT EQUAL 0)
118+
message(FATAL_ERROR "Failed to generate ledger entry classes:\n${LEDGER_GEN_ERROR}")
119+
else ()
120+
message(STATUS "Ledger entry classes generated successfully")
121+
endif ()
122+
123+
# Add the generated header directory to the module's include path
124+
target_include_directories(
125+
xrpl.libxrpl.protocol_autogen PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
126+
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
127+
128+
# Install generated headers
129+
install(DIRECTORY "${AUTOGEN_HEADER_DIR}/"
130+
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/xrpl/protocol_autogen" FILES_MATCHING
131+
PATTERN "*.h")
132+
endfunction ()
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
#pragma once
2+
3+
#include <xrpl/protocol/LedgerFormats.h>
4+
#include <xrpl/protocol/SField.h>
5+
#include <xrpl/protocol/STLedgerEntry.h>
6+
7+
#include <optional>
8+
#include <string>
9+
10+
namespace xrpl::ledger_entries {
11+
12+
/**
13+
* @brief Base class for type-safe ledger entry wrappers.
14+
*
15+
* This class provides common functionality for all ledger entry types,
16+
* including access to common fields (sfLedgerIndex, sfLedgerEntryType, sfFlags).
17+
*
18+
* This is an immutable wrapper around SLE (Serialized Ledger Entry).
19+
* Use the corresponding Builder classes to construct new ledger entries.
20+
*/
21+
class LedgerEntryBase
22+
{
23+
public:
24+
/**
25+
* @brief Construct a ledger entry wrapper from an existing SLE object.
26+
* @param sle The underlying serialized ledger entry to wrap
27+
*/
28+
explicit LedgerEntryBase(SLE const& sle) : sle_(sle)
29+
{
30+
}
31+
32+
/**
33+
* @brief Get the ledger entry type.
34+
* @return The type of this ledger entry
35+
*/
36+
[[nodiscard]]
37+
LedgerEntryType
38+
getType() const
39+
{
40+
return sle_.getType();
41+
}
42+
43+
/**
44+
* @brief Get the key (index) of this ledger entry.
45+
*
46+
* The key uniquely identifies this ledger entry in the ledger state.
47+
* @return A constant reference to the 256-bit key
48+
*/
49+
[[nodiscard]]
50+
uint256 const&
51+
getKey() const
52+
{
53+
return sle_.key();
54+
}
55+
56+
// Common field getters (from LedgerFormats.cpp commonFields)
57+
58+
/**
59+
* @brief Get the ledger index (sfLedgerIndex).
60+
*
61+
* This field is OPTIONAL and represents the index of the ledger entry.
62+
* @return The ledger index if present, std::nullopt otherwise
63+
*/
64+
[[nodiscard]]
65+
std::optional<uint256>
66+
getLedgerIndex() const
67+
{
68+
if (sle_.isFieldPresent(sfLedgerIndex))
69+
{
70+
return sle_.at(sfLedgerIndex);
71+
}
72+
return std::nullopt;
73+
}
74+
75+
/**
76+
* @brief Check if the ledger entry has a ledger index.
77+
* @return true if sfLedgerIndex is present, false otherwise
78+
*/
79+
[[nodiscard]]
80+
bool
81+
hasLedgerIndex() const
82+
{
83+
return sle_.isFieldPresent(sfLedgerIndex);
84+
}
85+
86+
/**
87+
* @brief Get the ledger entry type field (sfLedgerEntryType).
88+
*
89+
* This field is REQUIRED for all ledger entries and indicates the type
90+
* of the ledger entry (e.g., AccountRoot, RippleState, Offer, etc.).
91+
* @return The ledger entry type as a 16-bit unsigned integer
92+
*/
93+
[[nodiscard]]
94+
uint16_t
95+
getLedgerEntryType() const
96+
{
97+
return sle_.at(sfLedgerEntryType);
98+
}
99+
100+
/**
101+
* @brief Get the flags field (sfFlags).
102+
*
103+
* This field is REQUIRED for all ledger entries and contains
104+
* type-specific flags that modify the behavior of the ledger entry.
105+
* @return The flags value as a 32-bit unsigned integer
106+
*/
107+
[[nodiscard]]
108+
std::uint32_t
109+
getFlags() const
110+
{
111+
return sle_.at(sfFlags);
112+
}
113+
114+
/**
115+
* @brief Get the underlying SLE object.
116+
*
117+
* Provides direct access to the wrapped serialized ledger entry object
118+
* for cases where the type-safe accessors are insufficient.
119+
* @return A constant reference to the underlying SLE object
120+
*/
121+
[[nodiscard]]
122+
SLE const&
123+
getSle() const
124+
{
125+
return sle_;
126+
}
127+
128+
protected:
129+
/** @brief The underlying serialized ledger entry being wrapped. */
130+
SLE const& sle_;
131+
};
132+
133+
} // namespace xrpl::ledger_entries
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#pragma once
2+
3+
#include <xrpl/json/json_value.h>
4+
#include <xrpl/protocol/SField.h>
5+
#include <xrpl/protocol/STLedgerEntry.h>
6+
#include <xrpl/protocol/STObject.h>
7+
8+
namespace xrpl::ledger_entries {
9+
10+
/**
11+
* Base class for all ledger entry builders.
12+
* Provides common field setters that are available for all ledger entry types.
13+
*/
14+
template <typename Derived>
15+
class LedgerEntryBuilderBase
16+
{
17+
public:
18+
/**
19+
* Set the ledger index.
20+
* @param value Ledger index
21+
* @return Reference to the derived builder for method chaining.
22+
*/
23+
Derived&
24+
setLedgerIndex(uint256 const& value)
25+
{
26+
object_[sfLedgerIndex] = value;
27+
return static_cast<Derived&>(*this);
28+
}
29+
30+
/**
31+
* Set the flags.
32+
* @param value Flags value
33+
* @return Reference to the derived builder for method chaining.
34+
*/
35+
Derived&
36+
setFlags(uint32_t value)
37+
{
38+
object_.setFieldU32(sfFlags, value);
39+
return static_cast<Derived&>(*this);
40+
}
41+
42+
/**
43+
* @brief Factory method to create a new instance of the derived builder.
44+
*
45+
* Creates a default-constructed builder instance. It is recommended to use
46+
* this factory method instead of directly constructing the derived type to
47+
* avoid creating unnecessary temporary objects.
48+
* @return A new instance of the derived builder type
49+
*/
50+
static Derived
51+
create()
52+
{
53+
return Derived{};
54+
}
55+
56+
/**
57+
* @brief Factory method to create an instance of the derived builder from an existing SLE.
58+
*
59+
* Creates a builder instance initialized with data from an existing serialized
60+
* ledger entry. It is recommended to use this factory method instead of directly
61+
* constructing the derived type to avoid creating unnecessary temporary objects.
62+
* @param sle The existing serialized ledger entry to initialize from
63+
* @return A new instance of the derived builder type initialized with the SLE data
64+
*/
65+
static Derived
66+
create(SLE const& sle)
67+
{
68+
return Derived{sle};
69+
}
70+
71+
protected:
72+
STObject object_{sfLedgerEntry};
73+
};
74+
75+
} // namespace xrpl::ledger_entries

0 commit comments

Comments
 (0)