From 63595490f488963ce4c8074828c8314d5e03643c Mon Sep 17 00:00:00 2001 From: Nitish Kumar Date: Fri, 7 Jun 2024 20:41:50 +0530 Subject: [PATCH] Add a formatter binary skeleton as P4C back end (#4710) * init: p4 formatter Signed-off-by: Nitish * eliminate implicit char* -> cstring conversion in `P4fmtOptions` --------- Signed-off-by: Nitish --- CMakeLists.txt | 4 +++ backends/p4fmt/CMakeLists.txt | 14 ++++++++++ backends/p4fmt/README.md | 27 ++++++++++++++++++ backends/p4fmt/options.cpp | 17 ++++++++++++ backends/p4fmt/options.h | 29 +++++++++++++++++++ backends/p4fmt/p4fmt.cpp | 52 +++++++++++++++++++++++++++++++++++ 6 files changed, 143 insertions(+) create mode 100644 backends/p4fmt/CMakeLists.txt create mode 100644 backends/p4fmt/README.md create mode 100644 backends/p4fmt/options.cpp create mode 100644 backends/p4fmt/options.h create mode 100644 backends/p4fmt/p4fmt.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 02edc3d13a8..cb848e2bab9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,7 @@ OPTION (ENABLE_EBPF "Build the EBPF backend (required for the full test suite)" OPTION (ENABLE_UBPF "Build the uBPF backend (required for the full test suite)" ON) OPTION (ENABLE_DPDK "Build the DPDK backend (required for the full test suite)" ON) OPTION (ENABLE_P4TC "Build the P4TC backend" ON) +OPTION (ENABLE_P4FMT "Build the P4FMT backend" ON) OPTION (ENABLE_P4TEST "Build the P4Test backend (required for the full test suite)" ON) OPTION (ENABLE_TEST_TOOLS "Build the P4Tools development platform" OFF) OPTION (ENABLE_P4C_GRAPHS "Build the p4c-graphs backend" ON) @@ -479,6 +480,9 @@ endif () if (ENABLE_TEST_TOOLS) add_subdirectory (backends/p4tools) endif () +if (ENABLE_P4FMT) + add_subdirectory (backends/p4fmt) +endif () if (ENABLE_UBPF) add_subdirectory (backends/ubpf) endif () diff --git a/backends/p4fmt/CMakeLists.txt b/backends/p4fmt/CMakeLists.txt new file mode 100644 index 00000000000..282c8b35495 --- /dev/null +++ b/backends/p4fmt/CMakeLists.txt @@ -0,0 +1,14 @@ +set(FMT_SRCS + p4fmt.cpp + options.cpp +) + +add_executable(p4fmt ${FMT_SRCS}) +target_link_libraries(p4fmt ${P4C_LIBRARIES} ${P4C_LIB_DEPS}) +add_dependencies(p4fmt frontend) + +add_custom_target(p4formatter + COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_BINARY_DIR}/p4fmt ${P4C_BINARY_DIR}/p4fmt +) + +add_dependencies(p4c_driver p4formatter) diff --git a/backends/p4fmt/README.md b/backends/p4fmt/README.md new file mode 100644 index 00000000000..d4dc7da9421 --- /dev/null +++ b/backends/p4fmt/README.md @@ -0,0 +1,27 @@ +# p4fmt (P4 Formatter) + +p4fmt is a WIP formatter for P4. It's in a highly experimental phase +and, not yet stable/reliable for general use. +Contributions and feedbacks from the community +would be highly appreciated. + +## Build +- Setup P4C correctly, from [here](https://github.com/p4lang/p4c#dependencies). + +- Follow the instructions here to [build](https://github.com/p4lang/p4c#installing-p4c-from-source). + +Later `p4fmt` executable can be found inside the `p4c/build/` dir, and can be invoked as `./build/p4fmt` from p4c's root dir. + +## Usage +- Takes an output file with a `-o` flag and writes to it. + + `./build/p4fmt -o ` + + - If an output file is not provided with `-o` flag, it just prints the output on the stdout. + + `./build/p4fmt ` + +Sample Usage: + + ./build/p4fmt/p4fmt sample.p4 + ./build/p4fmt/p4fmt sample.p4 -o out.p4 diff --git a/backends/p4fmt/options.cpp b/backends/p4fmt/options.cpp new file mode 100644 index 00000000000..5d250362be9 --- /dev/null +++ b/backends/p4fmt/options.cpp @@ -0,0 +1,17 @@ +#include "options.h" + +namespace P4Fmt { + +P4fmtOptions::P4fmtOptions() { + registerOption( + "-o", "outfile", + [this](const char *arg) { + outFile = cstring(arg); + return true; + }, + "Write formatted output to outfile"); +} + +const cstring &P4fmtOptions::outputFile() const { return outFile; } + +} // namespace P4Fmt diff --git a/backends/p4fmt/options.h b/backends/p4fmt/options.h new file mode 100644 index 00000000000..d6bd6e4a6f9 --- /dev/null +++ b/backends/p4fmt/options.h @@ -0,0 +1,29 @@ +#ifndef BACKENDS_P4FMT_OPTIONS_H_ +#define BACKENDS_P4FMT_OPTIONS_H_ + +#include "frontends/common/options.h" +#include "frontends/common/parser_options.h" + +namespace P4Fmt { + +class P4fmtOptions : public CompilerOptions { + public: + P4fmtOptions(); + virtual ~P4fmtOptions() = default; + P4fmtOptions(const P4fmtOptions &) = default; + P4fmtOptions(P4fmtOptions &&) = delete; + P4fmtOptions &operator=(const P4fmtOptions &) = default; + P4fmtOptions &operator=(P4fmtOptions &&) = delete; + + const cstring &outputFile() const; + + private: + /// File to output to. + cstring outFile = nullptr; +}; + +using P4FmtContext = P4CContextWithOptions; + +} // namespace P4Fmt + +#endif /* BACKENDS_P4FMT_OPTIONS_H_ */ diff --git a/backends/p4fmt/p4fmt.cpp b/backends/p4fmt/p4fmt.cpp new file mode 100644 index 00000000000..758d924a51e --- /dev/null +++ b/backends/p4fmt/p4fmt.cpp @@ -0,0 +1,52 @@ +#include + +#include "frontends/common/parseInput.h" +#include "frontends/common/parser_options.h" +#include "frontends/p4/toP4/toP4.h" +#include "ir/ir.h" +#include "lib/compile_context.h" +#include "lib/cstring.h" +#include "lib/error.h" +#include "lib/nullstream.h" +#include "options.h" +#include "test/gtest/helpers.h" + +int main(int argc, char *const argv[]) { + AutoCompileContext autoP4FmtContext(new P4Fmt::P4FmtContext); + auto &options = P4Fmt::P4FmtContext::get().options(); + + if (options.process(argc, argv) == nullptr) { + return EXIT_FAILURE; + } + + options.setInputFile(); + + std::ostream *out = nullptr; + + // Write to stdout in absence of an output file. + if (options.outputFile().isNullOrEmpty()) { + out = &std::cout; + } else { + out = openFile(options.outputFile(), false); + if (!(*out)) { + ::error(ErrorType::ERR_NOT_FOUND, "%2%: No such file or directory.", + options.outputFile()); + options.usage(); + return EXIT_FAILURE; + } + } + + const IR::P4Program *program = P4::parseP4File(options); + + if (program == nullptr && ::errorCount() != 0) { + return EXIT_FAILURE; + } + + auto top4 = P4::ToP4(out, false); + + *out << "\n############################## INITIAL ##############################\n"; + // Print the program before running front end passes. + program->apply(top4); + + return ::errorCount() > 0 ? EXIT_FAILURE : EXIT_SUCCESS; +}