feat: Add code generator for transactions and ledger entries - #6443
Conversation
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
c4c852a to
672f35a
Compare
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
|
Is there a good strategy for adding new non-autogen functions to these objects? e.g for #6408 |
I think as the first step, we can use those typed ledger entries to build those views. If we want to polish it a bit further, I think it'd be nice to inherit from those ledger entries to provide functionalities. i.e. anything under protocol_autogen is purely generated code and it doesn't come with any functionalities, and things in xrpl/ledger are more transactor-facing, and then, we'll end up with things like |
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
Ok yeah I like that solution. |
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #6443 +/- ##
=========================================
+ Coverage 79.7% 81.4% +1.7%
=========================================
Files 878 988 +110
Lines 68116 74411 +6295
Branches 7563 7559 -4
=========================================
+ Hits 54300 60599 +6299
+ Misses 13816 13812 -4
🚀 New features to boost your workflow:
|
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
…-generator Signed-off-by: JCW <a1q123456@users.noreply.github.com> # Conflicts: # cmake/XrplCore.cmake # cmake/XrplInstall.cmake
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
| execute_process( | ||
| COMMAND | ||
| ${VENV_PYTHON} "${GENERATE_TX_SCRIPT}" "${TRANSACTIONS_MACRO}" | ||
| --header-dir "${AUTOGEN_HEADER_DIR}/transactions" --test-dir | ||
| "${AUTOGEN_TEST_DIR}/transactions" --sfields-macro | ||
| "${SFIELDS_MACRO}" | ||
| WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" | ||
| RESULT_VARIABLE TX_GEN_RESULT | ||
| OUTPUT_VARIABLE TX_GEN_OUTPUT | ||
| ERROR_VARIABLE TX_GEN_ERROR | ||
| ) |
There was a problem hiding this comment.
How does cmake understand that it doesn't need to run this each time? Shouldn't we use add_custom_command() here instead because it allows to specify outputs and mark the process as code generation?
There was a problem hiding this comment.
We currently are only able to regenerate when any of the dependent files change.
There was a problem hiding this comment.
Could you please explain where we set what cmake should check to decide whether to call this execute_process() or not?
There was a problem hiding this comment.
execute_process always runs but cmake won't reconfigure automatically if the files on this list get modified.
set_property(
DIRECTORY
APPEND
PROPERTY
CMAKE_CONFIGURE_DEPENDS
"${TRANSACTIONS_MACRO}"
"${LEDGER_ENTRIES_MACRO}"
"${SFIELDS_MACRO}"
"${GENERATE_TX_SCRIPT}"
"${GENERATE_LEDGER_SCRIPT}"
"${SCRIPTS_DIR}/macro_parser_common.py"
"${SCRIPTS_DIR}/templates/Transaction.h.mako"
"${SCRIPTS_DIR}/templates/TransactionTests.cpp.mako"
"${SCRIPTS_DIR}/templates/LedgerEntry.h.mako"
"${SCRIPTS_DIR}/templates/LedgerEntryTests.cpp.mako"
"${REQUIREMENTS_FILE}"
)There was a problem hiding this comment.
Does it mean that if I clean build directory and call cmake it will always overwrite existing generated files?
There was a problem hiding this comment.
yeah, it currently always runs the code generator when we configure the project, the code above only ensures that cmake will automatically reconfigure the project when those files get modified (it works the same as how it works after we add or remove a source file - it automatically reconfigures)
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
97eab0a to
17ceb53
Compare
…-generator Signed-off-by: JCW <a1q123456@users.noreply.github.com> # Conflicts: # cmake/XrplCompiler.cmake
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
Quick note for reviewers: files under
protocol_autogen/transactionsandprotocol_autogen/ledger_entriesare auto-generated. I don't think it's worth putting so much effort on those files unless you'd like to check if they're identical after re-generation, or if you want to see how those things look like.High Level Overview of Change
This PR adds code generators to generate type wrappers for transactions and ledger entries. The generators are python scripts and we generate code during configuration. This approach provides 2 benefits:
transactions.macro,ledger_entries.macro,sfields.macro, or the template files.CODEGEN_VENV_DIRHow we'll use the wrappers
Building a transaction
Wrapping a transaction
Building a ledger entry
Modifying a ledger entry
auto ammNew = AMMBuilder{amm} .setOwnerNode(newOwnerNode) .build();Wrapping a ledger entry
Context of Change
Type of Change
.gitignore, formatting, dropping support for older tooling)API Impact
libxrplchange (any change that may affectlibxrplor dependents oflibxrpl)Future Tasks
Keylettyped so that we'll retrieve a typed ledger entry wrapper from view.read()Q&A
Q1: Why add Python as a build dependency?
A: Python is already used elsewhere in the project (e.g., rippled-workload). The code generation runs at configure-time and takes ~1 second. Generated files are committed to version control, so builds work offline — Python is only needed when modifying .macro files or templates.
Q2: Why configure-time generation instead of build-time or a manual step?
A: Configure-time ensures generated headers exist before IDE indexing occurs, providing proper code completion and navigation. A pre-commit hook runs too late (after editing), and a manual step risks stale files.
Q3: What will consume these generated wrappers?
A:
A new JTx test framework (next PR) using builders instead of JSON/functors
Future: typed Keylets, typed transactor wrappers
Example of the new pattern:
auto payment = PaymentBuilder{accountId, destination, amount} .setFlags(...) .sign(pk, sk) .build(); env.submit(payment);Q4: Why not use X-macros instead of Python?
A: An X-macro approach was attempted but rejected due to complexity. Mapping sfAccount =>
SF_ACCOUNT::type::value_typeand handling optional vs required fields required extensive template metaprogramming (partial specialisations, SFINAE). The Python approach is significantly more readable and maintainable.Q5: Why not parse
TxFormats.cppdirectly instead of maintaining.macrofiles?A: The .macro files ARE the source of truth. TxFormats.cpp includes them via
#includetransactions.macro"`. Parsing C++ would require libclang and correct build parameters, adding significant complexity.Q6: Why Mako over Jinja2?
A: Because we use it in ripple-workload already, we do not have to learn another template syntax. Both are functionally equivalent for this use case; Mako allows more natural Python embedding if needed.
Q7: What happens if code generation fails?
A: The build fails with FATAL_ERROR. There's no silent fallback to stale files, explicit failure is preferred over hidden staleness.
Q8: Why are generated files committed to version control?
A:
Allows builds without Python/network access
Provides visibility into what changed when .macro files are modified
Acts as a cache for faster CI
IDE can index files immediately after clone
Q9: Are there tests for the code generator?
A: Yes, we have unit test templates and auto-generated unit tests.
Q10: What's the timeline for consumer code?
A: The new JTx PR (using these wrappers) is the next focus after this PR is merged.
Q11: Is there any existing tool that can do this?
A: Not really, lots of the code generation tools want to take over the serialisation part, and we're doing it ourselves. e.g. protoc could be a candidate, but it comes with a couple of problems unless we overhaul the system completely:
SF_ACCOUNTand the value type isSF_ACCOUNT::type::value_type. It's currently impossible to do it without a plugin in protoc.Q12: How do we add a new code generator for another macro file?
A: As most of the functionality is implemented in
macro_parser_common.py(like cleaning up the macro file to remove#defineand#include), we can do it fairly simply.scripts/generate_tx_classes.py)cmake/XrplProtocolAutogen.cmake