From a03f84606dff0ae800cafbbc0c7615ac0d8583df Mon Sep 17 00:00:00 2001 From: Anton Korobeynikov Date: Thu, 20 Jun 2024 10:38:43 -0700 Subject: [PATCH] Get rid of custom implementation of Utils::PathName (#4721) * Get rid of custom implementation of Utils::PathName. - Switch to std::filesystem::path - Simplify and cleanup code aroung - Get rid of (some) cstrings around paths * Get rid of Util::PathName alias * Resolve a fixme --- backends/bmv2/common/options.h | 6 +- backends/bmv2/psa_switch/main.cpp | 4 +- backends/bmv2/psa_switch/psaSwitch.cpp | 2 +- backends/bmv2/simple_switch/main.cpp | 6 +- backends/bmv2/simple_switch/simpleSwitch.cpp | 2 +- backends/dpdk/backend.cpp | 10 +-- backends/dpdk/control-plane/bfruntime_ext.cpp | 9 +- backends/dpdk/dpdkContext.cpp | 5 +- backends/dpdk/dpdkContext.h | 11 +-- backends/dpdk/main.cpp | 12 +-- backends/dpdk/options.h | 22 ++--- backends/dpdk/tdiConf.cpp | 21 ++--- backends/ebpf/ebpfBackend.cpp | 20 ++--- backends/ebpf/ebpfOptions.cpp | 4 +- backends/ebpf/ebpfOptions.h | 4 +- backends/ebpf/ebpfProgram.cpp | 14 +-- backends/ebpf/ebpfProgram.h | 6 +- backends/ebpf/p4c-ebpf.cpp | 2 +- backends/graphs/controls.cpp | 8 +- backends/graphs/controls.h | 2 +- backends/graphs/graph_visitor.cpp | 29 ++---- backends/graphs/graph_visitor.h | 16 ++-- backends/graphs/graphs.h | 2 +- backends/graphs/p4c-graphs.cpp | 8 +- backends/graphs/parsers.cpp | 8 +- backends/graphs/parsers.h | 3 +- backends/p4fmt/options.cpp | 4 +- backends/p4fmt/options.h | 4 +- backends/p4fmt/p4fmt.cpp | 4 +- backends/p4test/p4test.cpp | 4 +- backends/p4tools/common/options.cpp | 2 +- backends/p4tools/modules/smith/smith.cpp | 4 +- .../small-step/p4_asserts_parser_test.cpp | 9 +- .../bmv2/test/small-step/reachability.cpp | 4 +- backends/p4tools/modules/testgen/testgen.cpp | 6 +- backends/tc/backend.cpp | 35 +++----- backends/tc/ebpfCodeGen.cpp | 9 +- backends/tc/options.h | 7 +- backends/tc/tc.cpp | 16 ++-- backends/ubpf/ubpfBackend.cpp | 24 ++--- backends/ubpf/ubpfProgram.cpp | 4 +- backends/ubpf/ubpfProgram.h | 4 +- control-plane/p4RuntimeSerializer.cpp | 5 +- frontends/common/options.cpp | 4 +- frontends/common/options.h | 6 +- frontends/common/parseInput.h | 10 +-- frontends/common/parser_options.cpp | 43 +++++---- frontends/common/parser_options.h | 5 +- frontends/p4/fromv1.0/programStructure.cpp | 9 +- frontends/p4/frontend.cpp | 13 ++- frontends/parsers/parserDriver.cpp | 14 +-- frontends/parsers/parserDriver.h | 13 +-- ir/id.h | 2 + lib/CMakeLists.txt | 2 - lib/README.md | 4 - lib/nullstream.cpp | 7 +- lib/nullstream.h | 7 +- lib/path.cpp | 90 ------------------- lib/path.h | 73 --------------- test/CMakeLists.txt | 1 - test/gtest/parser_unroll.cpp | 6 +- test/gtest/path_test.cpp | 89 ------------------ tools/ir-generator/generator.cpp | 6 +- 63 files changed, 227 insertions(+), 558 deletions(-) delete mode 100644 lib/path.cpp delete mode 100644 lib/path.h delete mode 100644 test/gtest/path_test.cpp diff --git a/backends/bmv2/common/options.h b/backends/bmv2/common/options.h index 108904e0cc..931aec7bda 100644 --- a/backends/bmv2/common/options.h +++ b/backends/bmv2/common/options.h @@ -28,7 +28,7 @@ class BMV2Options : public CompilerOptions { /// Generate externs. bool emitExterns = false; /// File to output to. - cstring outputFile = nullptr; + std::filesystem::path outputFile; /// Read from json. bool loadIRFromJson = false; @@ -44,7 +44,7 @@ class BMV2Options : public CompilerOptions { registerOption( "-o", "outfile", [this](const char *arg) { - outputFile = cstring(arg); + outputFile = arg; return true; }, "Write output to outfile"); @@ -52,7 +52,7 @@ class BMV2Options : public CompilerOptions { "--fromJSON", "file", [this](const char *arg) { loadIRFromJson = true; - file = cstring(arg); + file = arg; return true; }, "Use IR representation from JsonFile dumped previously," diff --git a/backends/bmv2/psa_switch/main.cpp b/backends/bmv2/psa_switch/main.cpp index 4694e02fad..9df0897a54 100644 --- a/backends/bmv2/psa_switch/main.cpp +++ b/backends/bmv2/psa_switch/main.cpp @@ -98,7 +98,7 @@ int main(int argc, char *const argv[]) { try { toplevel = midEnd.process(program); if (::errorCount() > 1 || toplevel == nullptr || toplevel->getMain() == nullptr) return 1; - if (options.dumpJsonFile) + if (!options.dumpJsonFile.empty()) JSONGenerator(*openFile(options.dumpJsonFile, true), true) << program << std::endl; } catch (const std::exception &bug) { std::cerr << bug.what() << std::endl; @@ -119,7 +119,7 @@ int main(int argc, char *const argv[]) { } if (::errorCount() > 0) return 1; - if (!options.outputFile.isNullOrEmpty()) { + if (!options.outputFile.empty()) { std::ostream *out = openFile(options.outputFile, false); if (out != nullptr) { backend->serialize(*out); diff --git a/backends/bmv2/psa_switch/psaSwitch.cpp b/backends/bmv2/psa_switch/psaSwitch.cpp index d3bd0c7853..9f7d110395 100644 --- a/backends/bmv2/psa_switch/psaSwitch.cpp +++ b/backends/bmv2/psa_switch/psaSwitch.cpp @@ -314,7 +314,7 @@ void PsaSwitchBackend::convert(const IR::ToplevelBlock *tlb) { } } program->apply(toJson); - json->add_program_info(options.file); + json->add_program_info(cstring(options.file)); json->add_meta_info(); } diff --git a/backends/bmv2/simple_switch/main.cpp b/backends/bmv2/simple_switch/main.cpp index cfcfde15ef..2b64e505d2 100644 --- a/backends/bmv2/simple_switch/main.cpp +++ b/backends/bmv2/simple_switch/main.cpp @@ -15,6 +15,7 @@ limitations under the License. */ #include +#include #include #include @@ -27,7 +28,6 @@ limitations under the License. #include "frontends/common/applyOptionsPragmas.h" #include "frontends/common/parseInput.h" #include "frontends/p4/frontend.h" -#include "fstream" #include "ir/ir.h" #include "ir/json_generator.h" #include "ir/json_loader.h" @@ -99,7 +99,7 @@ int main(int argc, char *const argv[]) { try { toplevel = midEnd.process(program); if (::errorCount() > 1 || toplevel == nullptr || toplevel->getMain() == nullptr) return 1; - if (options.dumpJsonFile && !options.loadIRFromJson) + if (!options.dumpJsonFile.empty() && !options.loadIRFromJson) JSONGenerator(*openFile(options.dumpJsonFile, true), true) << program << std::endl; } catch (const std::exception &bug) { std::cerr << bug.what() << std::endl; @@ -120,7 +120,7 @@ int main(int argc, char *const argv[]) { } if (::errorCount() > 0) return 1; - if (!options.outputFile.isNullOrEmpty()) { + if (!options.outputFile.empty()) { std::ostream *out = openFile(options.outputFile, false); if (out != nullptr) { backend->serialize(*out); diff --git a/backends/bmv2/simple_switch/simpleSwitch.cpp b/backends/bmv2/simple_switch/simpleSwitch.cpp index e154cedcd5..eb8abea291 100644 --- a/backends/bmv2/simple_switch/simpleSwitch.cpp +++ b/backends/bmv2/simple_switch/simpleSwitch.cpp @@ -1207,7 +1207,7 @@ void SimpleSwitchBackend::convert(const IR::ToplevelBlock *tlb) { // called (e.g. resubmit or generate_digest) BMV2::nextId("field_lists"_cs); BMV2::nextId("learn_lists"_cs); - json->add_program_info(options.file); + json->add_program_info(cstring(options.file)); json->add_meta_info(); // convert all enums to json diff --git a/backends/dpdk/backend.cpp b/backends/dpdk/backend.cpp index 815a06c4e6..9d65a32980 100644 --- a/backends/dpdk/backend.cpp +++ b/backends/dpdk/backend.cpp @@ -116,12 +116,12 @@ void DpdkBackend::convert(const IR::ToplevelBlock *tlb) { new DpdkArchLast(), new VisitFunctor([this, genContextJson] { // Serialize context json object into user specified file - if (!options.ctxtFile.isNullOrEmpty()) { - std::ostream *out = openFile(options.ctxtFile, false); - if (out != nullptr) { + if (!options.ctxtFile.empty()) { + if (std::ostream *out = openFile(options.ctxtFile, false)) { genContextJson->serializeContextJson(out); - } - out->flush(); + out->flush(); + } else + ::error(ErrorType::ERR_IO, "Could not open file: %1%", options.ctxtFile); } }), new ReplaceHdrMetaField(), diff --git a/backends/dpdk/control-plane/bfruntime_ext.cpp b/backends/dpdk/control-plane/bfruntime_ext.cpp index 693735baf5..5c5949923e 100644 --- a/backends/dpdk/control-plane/bfruntime_ext.cpp +++ b/backends/dpdk/control-plane/bfruntime_ext.cpp @@ -221,14 +221,7 @@ const Util::JsonObject *BFRuntimeSchemaGenerator::genSchema() const { auto *json = new Util::JsonObject(); if (isTDI) { - cstring progName = options.file; - auto fileName = progName.findlast('/'); - // Handle the case when input file is in the current working directory. - // fileName would be null in that case, hence progName should remain unchanged. - if (fileName) progName = cstring(fileName); - auto fileext = cstring(progName.find(".")); - progName = cstring(progName.replace(fileext, cstring::empty)); - progName = progName.trim("/\t\n\r"); + auto progName = options.file.stem(); json->emplace("program_name"_cs, progName); json->emplace("build_date"_cs, cstring(options.getBuildDate())); json->emplace("compile_command"_cs, cstring(options.getCompileCommand())); diff --git a/backends/dpdk/dpdkContext.cpp b/backends/dpdk/dpdkContext.cpp index c418099a61..9a74f0eeef 100644 --- a/backends/dpdk/dpdkContext.cpp +++ b/backends/dpdk/dpdkContext.cpp @@ -23,10 +23,9 @@ limitations under the License. namespace DPDK { cstring DpdkContextGenerator::removePipePrefix(cstring tableName) { - if (!options.bfRtSchema.isNullOrEmpty() || !options.tdiFile.isNullOrEmpty()) { + if (!options.bfRtSchema.empty() || !options.tdiFile.empty()) { cstring tablename = cstring(tableName.find('.')); - tablename = tablename.trim(".\t\n\r"); - return tablename; + return tablename.trim(".\t\n\r"); } return tableName; } diff --git a/backends/dpdk/dpdkContext.h b/backends/dpdk/dpdkContext.h index 11e49a218d..001f658c8f 100644 --- a/backends/dpdk/dpdkContext.h +++ b/backends/dpdk/dpdkContext.h @@ -82,21 +82,14 @@ struct externAttributes { /// Program level information for context json. struct TopLevelCtxt { - cstring progName; + std::string progName; cstring buildDate; cstring compileCommand; cstring compilerVersion; void initTopLevelCtxt(DpdkOptions &options) { buildDate = options.getBuildDate(); compileCommand = options.getCompileCommand(); - progName = options.file; - auto fileName = progName.findlast('/'); - // Handle the case when input file is in the current working directory. - // fileName would be null in that case, hence progName should remain unchanged. - if (fileName) progName = cstring(fileName); - auto fileext = progName.find("."); - progName = progName.replace(cstring(fileext), cstring::empty); - progName = progName.trim("/\t\n\r"); + progName = options.file.stem(); compilerVersion = options.compilerVersion; } }; diff --git a/backends/dpdk/main.cpp b/backends/dpdk/main.cpp index 67f4d522b5..f332a7d9cb 100644 --- a/backends/dpdk/main.cpp +++ b/backends/dpdk/main.cpp @@ -52,7 +52,7 @@ void generateTDIBfrtJson(bool isTDI, const IR::P4Program *program, DPDK::DpdkOpt "pna"_cs, new P4::ControlPlaneAPI::Standard::PNAArchHandlerBuilderForDPDK()); auto p4Runtime = P4::generateP4Runtime(program, options.arch); - cstring filename = isTDI ? options.tdiFile : options.bfRtSchema; + std::filesystem::path filename = isTDI ? options.tdiFile : options.bfRtSchema; auto p4rt = new P4::BFRT::BFRuntimeSchemaGenerator(*p4Runtime.p4Info, isTDI, options); std::ostream *out = openFile(filename, false); if (!out) { @@ -115,14 +115,14 @@ int main(int argc, char *const argv[]) { P4::serializeP4RuntimeIfRequired(program, options); if (::errorCount() > 0) return 1; - if (!options.tdiBuilderConf.isNullOrEmpty()) { + if (!options.tdiBuilderConf.empty()) { DPDK::TdiBfrtConf::generate(program, options); } - if (!options.bfRtSchema.isNullOrEmpty()) { + if (!options.bfRtSchema.empty()) { generateTDIBfrtJson(false, program, options); } - if (!options.tdiFile.isNullOrEmpty()) { + if (!options.tdiFile.empty()) { generateTDIBfrtJson(true, program, options); } @@ -133,7 +133,7 @@ int main(int argc, char *const argv[]) { try { toplevel = midEnd.process(program); if (::errorCount() > 1 || toplevel == nullptr || toplevel->getMain() == nullptr) return 1; - if (options.dumpJsonFile) + if (!options.dumpJsonFile.empty()) JSONGenerator(*openFile(options.dumpJsonFile, true), true) << program << std::endl; } catch (const std::exception &bug) { std::cerr << bug.what() << std::endl; @@ -146,7 +146,7 @@ int main(int argc, char *const argv[]) { backend->convert(toplevel); if (::errorCount() > 0) return 1; - if (!options.outputFile.isNullOrEmpty()) { + if (!options.outputFile.empty()) { std::ostream *out = openFile(options.outputFile, false); if (out != nullptr) { backend->codegen(*out); diff --git a/backends/dpdk/options.h b/backends/dpdk/options.h index f4ec5166f6..4315e993c8 100644 --- a/backends/dpdk/options.h +++ b/backends/dpdk/options.h @@ -23,15 +23,15 @@ namespace DPDK { class DpdkOptions : public CompilerOptions { public: - cstring bfRtSchema = cstring::empty; + std::filesystem::path bfRtSchema; /// File to output to. - cstring outputFile = nullptr; + std::filesystem::path outputFile; /// File to output TDI JSON to. - cstring tdiFile = cstring::empty; + std::filesystem::path tdiFile; /// File to output context JSON to. - cstring ctxtFile = cstring::empty; + std::filesystem::path ctxtFile; /// File to output the TDI builder configuration to. - cstring tdiBuilderConf = cstring::empty; + std::filesystem::path tdiBuilderConf; /// Read from JSON. bool loadIRFromJson = false; /// Enable/disable Egress pipeline in PSA. @@ -58,35 +58,35 @@ class DpdkOptions : public CompilerOptions { registerOption( "--bf-rt-schema", "file", [this](const char *arg) { - bfRtSchema = cstring(arg); + bfRtSchema = arg; return true; }, "Generate and write BF-RT JSON schema to the specified file"); registerOption( "-o", "outfile", [this](const char *arg) { - outputFile = cstring(arg); + outputFile = arg; return true; }, "Write output to outfile"); registerOption( "--tdi-builder-conf", "file", [this](const char *arg) { - tdiBuilderConf = cstring(arg); + tdiBuilderConf = arg; return true; }, "Generate and write the TDI builder configuration to the specified file"); registerOption( "--tdi", "file", [this](const char *arg) { - tdiFile = cstring(arg); + tdiFile = arg; return true; }, "Generate and write TDI JSON to the specified file"); registerOption( "--context", "file", [this](const char *arg) { - ctxtFile = cstring(arg); + ctxtFile = arg; return true; }, "Generate and write context JSON to the specified file"); @@ -94,7 +94,7 @@ class DpdkOptions : public CompilerOptions { "--fromJSON", "file", [this](const char *arg) { loadIRFromJson = true; - file = cstring(arg); + file = arg; return true; }, "Use IR representation from JsonFile dumped previously," diff --git a/backends/dpdk/tdiConf.cpp b/backends/dpdk/tdiConf.cpp index cdeef5e60c..199fcdfaa0 100644 --- a/backends/dpdk/tdiConf.cpp +++ b/backends/dpdk/tdiConf.cpp @@ -68,33 +68,33 @@ std::optional TdiBfrtConf::findPipeName(const IR::P4Program *prog, } void TdiBfrtConf::generate(const IR::P4Program *prog, DPDK::DpdkOptions &options) { - if (options.outputFile.isNullOrEmpty()) { + if (options.outputFile.empty()) { ::error(ErrorType::ERR_UNEXPECTED, "No output file provided. Unable to generate correct TDI builder config file."); return; } - auto inputFile = std::filesystem::absolute(options.file.c_str()); - auto outFile = std::filesystem::absolute(options.outputFile.c_str()); + auto inputFile = std::filesystem::absolute(options.file); + auto outFile = std::filesystem::absolute(options.outputFile); auto outDir = outFile.parent_path(); - auto tdiFile = std::filesystem::absolute(options.tdiBuilderConf.c_str()); + auto tdiFile = std::filesystem::absolute(options.tdiBuilderConf); auto programName = inputFile.stem(); - if (options.bfRtSchema.isNullOrEmpty()) { - options.bfRtSchema = cstring((outDir / programName).replace_filename("json").c_str()); + if (options.bfRtSchema.empty()) { + options.bfRtSchema = (outDir / programName).replace_filename("json"); ::warning( "BF-Runtime Schema file name not provided, but is required for the TDI builder " "configuration. Generating file %1%", options.bfRtSchema); } - if (options.ctxtFile.isNullOrEmpty()) { - options.ctxtFile = cstring((outDir / "context.json").c_str()); + if (options.ctxtFile.empty()) { + options.ctxtFile = outDir / "context.json"; ::warning( "DPDK context file name not provided, but is required for the TDI builder " "configuration. Generating file %1%", options.ctxtFile); } - auto contextFile = std::filesystem::absolute(options.ctxtFile.c_str()); + auto contextFile = std::filesystem::absolute(options.ctxtFile); auto bfRtSchema = std::filesystem::absolute(options.bfRtSchema.c_str()); auto pipeName = findPipeName(prog, options); @@ -103,6 +103,7 @@ void TdiBfrtConf::generate(const IR::P4Program *prog, DPDK::DpdkOptions &options } // TODO: Ideally, this should be a template. We could use Inja, but this adds another // dependency. + // FIXME: Just use absl::StrSubstitute std::stringstream ss; ss << R"""({ "chip_list": [ @@ -149,7 +150,7 @@ void TdiBfrtConf::generate(const IR::P4Program *prog, DPDK::DpdkOptions &options if (out.is_open()) { out << ss; } else { - ::error(ErrorType::ERR_IO, "Could not open file: %1%", tdiFile.c_str()); + ::error(ErrorType::ERR_IO, "Could not open file: %1%", tdiFile); return; } out.close(); diff --git a/backends/ebpf/ebpfBackend.cpp b/backends/ebpf/ebpfBackend.cpp index 163fd87653..002c278a12 100644 --- a/backends/ebpf/ebpfBackend.cpp +++ b/backends/ebpf/ebpfBackend.cpp @@ -37,19 +37,14 @@ void emitFilterModel(const EbpfOptions &options, Target *target, const IR::Tople auto ebpfprog = new EBPFProgram(options, toplevel->getProgram(), refMap, typeMap, toplevel); if (!ebpfprog->build()) return; - if (options.outputFile.isNullOrEmpty()) return; + if (options.outputFile.empty()) return; - cstring cfile = options.outputFile; - auto cstream = openFile(cfile, false); + auto *cstream = openFile(options.outputFile, false); if (cstream == nullptr) return; - cstring hfile; - const char *dot = cfile.findlast('.'); - if (dot == nullptr) - hfile = cfile + ".h"; - else - hfile = cfile.before(dot) + ".h"; - auto hstream = openFile(hfile, false); + std::filesystem::path hfile = options.outputFile; + hfile.replace_extension(".h"); + auto *hstream = openFile(hfile, false); if (hstream == nullptr) return; ebpfprog->emitH(&h, hfile); @@ -96,10 +91,9 @@ void run_ebpf_backend(const EbpfOptions &options, const IR::ToplevelBlock *tople auto backend = new EBPF::PSASwitchBackend(options, target, refMap, typeMap); backend->convert(toplevel); - if (options.outputFile.isNullOrEmpty()) return; + if (options.outputFile.empty()) return; - cstring cfile = options.outputFile; - auto cstream = openFile(cfile, false); + auto cstream = openFile(options.outputFile, false); if (cstream == nullptr) return; backend->codegen(*cstream); diff --git a/backends/ebpf/ebpfOptions.cpp b/backends/ebpf/ebpfOptions.cpp index fc96635a32..315fbb2160 100644 --- a/backends/ebpf/ebpfOptions.cpp +++ b/backends/ebpf/ebpfOptions.cpp @@ -23,7 +23,7 @@ EbpfOptions::EbpfOptions() { registerOption( "-o", "outfile", [this](const char *arg) { - outputFile = cstring(arg); + outputFile = arg; return true; }, "Write output to outfile"); @@ -42,7 +42,7 @@ EbpfOptions::EbpfOptions() { "--fromJSON", "file", [this](const char *arg) { loadIRFromJson = true; - file = cstring(arg); + file = arg; return true; }, "Use IR representation from JsonFile dumped previously," diff --git a/backends/ebpf/ebpfOptions.h b/backends/ebpf/ebpfOptions.h index f642f17209..2aab050ab2 100644 --- a/backends/ebpf/ebpfOptions.h +++ b/backends/ebpf/ebpfOptions.h @@ -17,8 +17,6 @@ limitations under the License. #ifndef BACKENDS_EBPF_EBPFOPTIONS_H_ #define BACKENDS_EBPF_EBPFOPTIONS_H_ -#include - #include "frontends/common/options.h" enum XDP2TC { XDP2TC_NONE, XDP2TC_META, XDP2TC_HEAD, XDP2TC_CPUMAP }; @@ -26,7 +24,7 @@ enum XDP2TC { XDP2TC_NONE, XDP2TC_META, XDP2TC_HEAD, XDP2TC_CPUMAP }; class EbpfOptions : public CompilerOptions { public: /// file to output to - cstring outputFile = nullptr; + std::filesystem::path outputFile; /// read from json bool loadIRFromJson = false; /// Externs generation diff --git a/backends/ebpf/ebpfProgram.cpp b/backends/ebpf/ebpfProgram.cpp index a70883dab3..721fe10869 100644 --- a/backends/ebpf/ebpfProgram.cpp +++ b/backends/ebpf/ebpfProgram.cpp @@ -82,17 +82,11 @@ bool EBPFProgram::build() { return true; } -void EBPFProgram::emitC(CodeBuilder *builder, cstring header) { +void EBPFProgram::emitC(CodeBuilder *builder, const std::filesystem::path &header) { emitGeneratedComment(builder); - // Find the last occurrence of a folder slash (Linux only) - const char *header_stripped = header.findlast('/'); - if (header_stripped) - // Remove the path from the header - builder->appendFormat("#include \"%s\"", header_stripped + 1); - else - // There is no prepended path, just include the header - builder->appendFormat("#include \"%s\"", header.c_str()); + // Remove the path from the header + builder->appendFormat("#include \"%s\"", header.filename()); builder->newline(); builder->target->emitIncludes(builder); @@ -162,7 +156,7 @@ void EBPFProgram::emitGeneratedComment(CodeBuilder *builder) { builder->newline(); } -void EBPFProgram::emitH(CodeBuilder *builder, cstring) { +void EBPFProgram::emitH(CodeBuilder *builder, const std::filesystem::path &) { emitGeneratedComment(builder); builder->appendLine("#ifndef _P4_GEN_HEADER_"); builder->appendLine("#define _P4_GEN_HEADER_"); diff --git a/backends/ebpf/ebpfProgram.h b/backends/ebpf/ebpfProgram.h index 385f3d412e..6abe2fdddf 100644 --- a/backends/ebpf/ebpfProgram.h +++ b/backends/ebpf/ebpfProgram.h @@ -103,8 +103,10 @@ class EBPFProgram : public EBPFObject { public: virtual void emitCommonPreamble(CodeBuilder *builder); virtual void emitGeneratedComment(CodeBuilder *builder); - virtual void emitH(CodeBuilder *builder, cstring headerFile); // emits C headers - virtual void emitC(CodeBuilder *builder, cstring headerFile); // emits C program + virtual void emitH(CodeBuilder *builder, + const std::filesystem::path &headerFile); // emits C headers + virtual void emitC(CodeBuilder *builder, + const std::filesystem::path &headerFile); // emits C program DECLARE_TYPEINFO(EBPFProgram, EBPFObject); }; diff --git a/backends/ebpf/p4c-ebpf.cpp b/backends/ebpf/p4c-ebpf.cpp index 1e56271fbe..f497318a54 100644 --- a/backends/ebpf/p4c-ebpf.cpp +++ b/backends/ebpf/p4c-ebpf.cpp @@ -81,7 +81,7 @@ void compile(EbpfOptions &options) { EBPF::MidEnd midend; midend.addDebugHook(hook); auto toplevel = midend.run(options, program); - if (options.dumpJsonFile) + if (!options.dumpJsonFile.empty()) JSONGenerator(*openFile(options.dumpJsonFile, true)) << program << std::endl; if (::errorCount() > 0) return; diff --git a/backends/graphs/controls.cpp b/backends/graphs/controls.cpp index a1b75738d2..ccc5f57a25 100644 --- a/backends/graphs/controls.cpp +++ b/backends/graphs/controls.cpp @@ -34,7 +34,7 @@ using Graph = ControlGraphs::Graph; Graph *ControlGraphs::ControlStack::pushBack(Graph ¤tSubgraph, const cstring &name) { auto &newSubgraph = currentSubgraph.create_subgraph(); auto fullName = getName(name); - boost::get_property(newSubgraph, boost::graph_name) = "cluster"_cs + fullName; + boost::get_property(newSubgraph, boost::graph_name) = "cluster" + fullName; boost::get_property(newSubgraph, boost::graph_graph_attribute)["label"_cs] = boost::get_property(currentSubgraph, boost::graph_name) + (fullName != "" ? "." + fullName : fullName); @@ -69,8 +69,8 @@ bool ControlGraphs::ControlStack::isEmpty() const { return subgraphs.empty(); } using vertex_t = ControlGraphs::vertex_t; ControlGraphs::ControlGraphs(P4::ReferenceMap *refMap, P4::TypeMap *typeMap, - const cstring &graphsDir) - : refMap(refMap), typeMap(typeMap), graphsDir(graphsDir) { + std::filesystem::path graphsDir) + : refMap(refMap), typeMap(typeMap), graphsDir(std::move(graphsDir)) { visitDagOnce = false; } @@ -84,7 +84,7 @@ bool ControlGraphs::preorder(const IR::PackageBlock *block) { Graph *g_ = new Graph(); g = g_; instanceName = std::nullopt; - boost::get_property(*g_, boost::graph_name) = name; + boost::get_property(*g_, boost::graph_name) = name.string(); BUG_CHECK(controlStack.isEmpty(), "Invalid control stack state"); g = controlStack.pushBack(*g_, cstring::empty); start_v = add_vertex("__START__"_cs, VertexType::OTHER); diff --git a/backends/graphs/controls.h b/backends/graphs/controls.h index 3709b3b3d2..4fd0ac83cb 100644 --- a/backends/graphs/controls.h +++ b/backends/graphs/controls.h @@ -36,7 +36,7 @@ class ControlGraphs : public Graphs { std::vector subgraphs{}; }; - ControlGraphs(P4::ReferenceMap *refMap, P4::TypeMap *typeMap, const cstring &graphsDir); + ControlGraphs(P4::ReferenceMap *refMap, P4::TypeMap *typeMap, std::filesystem::path graphsDir); bool preorder(const IR::PackageBlock *block) override; bool preorder(const IR::ControlBlock *block) override; diff --git a/backends/graphs/graph_visitor.cpp b/backends/graphs/graph_visitor.cpp index 39f9810a8e..9ce4970f20 100644 --- a/backends/graphs/graph_visitor.cpp +++ b/backends/graphs/graph_visitor.cpp @@ -18,15 +18,14 @@ #include "graphs.h" #include "lib/nullstream.h" -#include "lib/path.h" namespace graphs { -void Graph_visitor::writeGraphToFile(const Graph &g, const cstring &name) { - auto path = Util::PathName(graphsDir).join(name + ".dot"); - auto out = openFile(path.toString(), false); +void Graph_visitor::writeGraphToFile(const Graph &g, const std::string &name) { + auto path = graphsDir / (name + ".dot"); + auto out = openFile(path, false); if (out == nullptr) { - ::error(ErrorType::ERR_IO, "Failed to open file %1%", path.toString()); + ::error(ErrorType::ERR_IO, "Failed to open file %1%", path); return; } // Custom label writers not supported with subgraphs, so we populate @@ -135,7 +134,7 @@ void Graph_visitor::forLoopFullGraph(std::vector &graphsArray, fullGrap // set subg properties boost::get_property(subfg, boost::graph_name) = - "cluster" + Util::toString(opts->cluster_i++); + "cluster" + std::to_string(opts->cluster_i++); boost::get_property(subfg, boost::graph_graph_attribute)["label"_cs] = boost::get_property(*g_, boost::graph_name); boost::get_property(subfg, boost::graph_graph_attribute)["style"_cs] = "bold"_cs; @@ -185,7 +184,7 @@ void Graph_visitor::process(std::vector &controlGraphsArray, if (fullGraph) { fullGraphOpts opts; - boost::get_property(opts.fg, boost::graph_name) = "fullGraph"_cs; + boost::get_property(opts.fg, boost::graph_name) = "fullGraph"; // Enables edges with tails between clusters. boost::get_property(opts.fg, boost::graph_graph_attribute)["compound"_cs] = "true"_cs; @@ -193,31 +192,21 @@ void Graph_visitor::process(std::vector &controlGraphsArray, forLoopFullGraph(controlGraphsArray, &opts, PrevType::Parser); GraphAttributeSetter()(opts.fg); - writeGraphToFile(opts.fg, "fullGraph"_cs); + writeGraphToFile(opts.fg, "fullGraph"); } if (jsonOut) { json = new Util::JsonObject(); // Remove '.p4' and path from program name. - auto file_without_p4 = (filename.findlast('.') == nullptr) - ? filename - : filename.before(filename.findlast('.')); - const char *file_without_path = file_without_p4; - if (file_without_p4.findlast('/') != nullptr) { - file_without_path = file_without_p4.findlast('/') + 1; // char* without '/' - } - - json->emplace("name"_cs, file_without_path); + json->emplace("name"_cs, filename.stem()); programBlocks = new Util::JsonArray(); json->emplace("nodes"_cs, programBlocks); forLoopJson(parserGraphsArray, PrevType::Parser); forLoopJson(controlGraphsArray, PrevType::Control); - std::ofstream file; - auto path = Util::PathName(graphsDir).join("fullGraph.json"_cs); - file.open(path.toString()); + std::ofstream file(graphsDir / "fullGraph.json"); file << json->toString() << std::endl; file.close(); } diff --git a/backends/graphs/graph_visitor.h b/backends/graphs/graph_visitor.h index d3135126f1..e3385c9693 100644 --- a/backends/graphs/graph_visitor.h +++ b/backends/graphs/graph_visitor.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#include + #include #include @@ -61,13 +63,13 @@ class Graph_visitor : public Graphs { /// @param graphs option to output graph for each function block /// @param fullGraph option to create fullGraph /// @param jsonOut option to create json fullGraph. - Graph_visitor(const cstring &graphsDir, const bool graphs, const bool fullGraph, - const bool jsonOut, const cstring &filename) - : graphsDir(graphsDir), + Graph_visitor(std::filesystem::path graphsDir, const bool graphs, const bool fullGraph, + const bool jsonOut, std::filesystem::path filename) + : graphsDir(std::move(graphsDir)), graphs(graphs), fullGraph(fullGraph), jsonOut(jsonOut), - filename(filename) {} + filename(std::move(filename)) {} /// @brief Maps VertexType to string /// @param v_type VertexType to map /// @return string representation of v_type. @@ -92,7 +94,7 @@ class Graph_visitor : public Graphs { /// /// @param g boost graph /// @param name file name - void writeGraphToFile(const Graph &g, const cstring &name); + void writeGraphToFile(const Graph &g, const std::string &name); private: /// Loops over vector graphsArray with boost graphs, creating json representation of CFG @@ -112,13 +114,13 @@ class Graph_visitor : public Graphs { Util::JsonObject *json = nullptr; // stores json that will be outputted Util::JsonArray *programBlocks = nullptr; // stores objects in top level array "nodes" - const cstring graphsDir; + std::filesystem::path graphsDir; /// options const bool graphs; // output boost graphs to files const bool fullGraph; // merge boost graphs into one CFG, and output to file const bool jsonOut; // iterate over boost graphs, and create json representation of these // graphs - const cstring filename; + const std::filesystem::path filename; }; } // namespace graphs diff --git a/backends/graphs/graphs.h b/backends/graphs/graphs.h index 28adbbb33a..db5446492e 100644 --- a/backends/graphs/graphs.h +++ b/backends/graphs/graphs.h @@ -124,7 +124,7 @@ class Graphs : public Inspector { boost::edge_attribute_t, GraphvizAttributes, boost::property>>; using graphProperties = boost::property< - boost::graph_name_t, cstring, + boost::graph_name_t, std::string, boost::property< boost::graph_graph_attribute_t, GraphvizAttributes, boost::propertyname); - boost::get_property(*g_, boost::graph_name) = parser->name; + boost::get_property(*g_, boost::graph_name) = parser->name.string(); std::map nodes; unsigned int iter = 0; diff --git a/backends/graphs/parsers.h b/backends/graphs/parsers.h index 284869ca84..fa112f07cf 100644 --- a/backends/graphs/parsers.h +++ b/backends/graphs/parsers.h @@ -23,7 +23,6 @@ #include "ir/ir.h" #include "lib/cstring.h" #include "lib/nullstream.h" -#include "lib/path.h" #include "lib/safe_vector.h" namespace graphs { @@ -43,7 +42,7 @@ class ParserGraphs : public Graphs { std::map> states; public: - ParserGraphs(P4::ReferenceMap *refMap, const cstring &graphsDir); + ParserGraphs(P4::ReferenceMap *refMap, std::filesystem::path graphsDir); Graph *CreateSubGraph(Graph ¤tSubgraph, const cstring &name); void postorder(const IR::P4Parser *parser) override; diff --git a/backends/p4fmt/options.cpp b/backends/p4fmt/options.cpp index 5d250362be..0df95affe2 100644 --- a/backends/p4fmt/options.cpp +++ b/backends/p4fmt/options.cpp @@ -6,12 +6,12 @@ P4fmtOptions::P4fmtOptions() { registerOption( "-o", "outfile", [this](const char *arg) { - outFile = cstring(arg); + outFile = arg; return true; }, "Write formatted output to outfile"); } -const cstring &P4fmtOptions::outputFile() const { return outFile; } +const std::filesystem::path &P4fmtOptions::outputFile() const { return outFile; } } // namespace P4Fmt diff --git a/backends/p4fmt/options.h b/backends/p4fmt/options.h index d6bd6e4a6f..3fd994fed3 100644 --- a/backends/p4fmt/options.h +++ b/backends/p4fmt/options.h @@ -15,11 +15,11 @@ class P4fmtOptions : public CompilerOptions { P4fmtOptions &operator=(const P4fmtOptions &) = default; P4fmtOptions &operator=(P4fmtOptions &&) = delete; - const cstring &outputFile() const; + const std::filesystem::path &outputFile() const; private: /// File to output to. - cstring outFile = nullptr; + std::filesystem::path outFile; }; using P4FmtContext = P4CContextWithOptions; diff --git a/backends/p4fmt/p4fmt.cpp b/backends/p4fmt/p4fmt.cpp index f355f62aa9..ca0c6a6e6b 100644 --- a/backends/p4fmt/p4fmt.cpp +++ b/backends/p4fmt/p4fmt.cpp @@ -23,13 +23,13 @@ int main(int argc, char *const argv[]) { std::ostream *out = nullptr; // Write to stdout in absence of an output file. - if (options.outputFile().isNullOrEmpty()) { + if (options.outputFile().empty()) { 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.outputFile().string()); options.usage(); return EXIT_FAILURE; } diff --git a/backends/p4test/p4test.cpp b/backends/p4test/p4test.cpp index eff0ed611f..d0ec0c38c1 100644 --- a/backends/p4test/p4test.cpp +++ b/backends/p4test/p4test.cpp @@ -69,7 +69,7 @@ class P4TestOptions : public CompilerOptions { "--fromJSON", "file", [this](const char *arg) { loadIRFromJson = true; - file = cstring(arg); + file = arg; return true; }, "read previously dumped json instead of P4 source code"); @@ -182,7 +182,7 @@ int main(int argc, char *const argv[]) { } } if (program) { - if (options.dumpJsonFile) + if (!options.dumpJsonFile.empty()) JSONGenerator(*openFile(options.dumpJsonFile, true), true) << program << std::endl; if (options.debugJson) { std::stringstream ss1, ss2; diff --git a/backends/p4tools/common/options.cpp b/backends/p4tools/common/options.cpp index b4671dab6b..a104b1a2a8 100644 --- a/backends/p4tools/common/options.cpp +++ b/backends/p4tools/common/options.cpp @@ -76,7 +76,7 @@ std::optional AbstractP4cToolOptions::process( usage(); return std::nullopt; } - P4CContext::get().options().file = cstring(remainingArgs->at(0)); + P4CContext::get().options().file = remainingArgs->at(0); if (!validateOptions()) { return std::nullopt; diff --git a/backends/p4tools/modules/smith/smith.cpp b/backends/p4tools/modules/smith/smith.cpp index 87e6ab53d7..8c7658259f 100644 --- a/backends/p4tools/modules/smith/smith.cpp +++ b/backends/p4tools/modules/smith/smith.cpp @@ -55,8 +55,8 @@ int Smith::mainImpl(const CompilerResult & /*result*/) { auto &smithOptions = P4Tools::SmithOptions::get(); // Use a default name if no specific output name is provided. - if (outputFile == nullptr) { - outputFile = cstring("out.p4"); + if (outputFile.empty()) { + outputFile = "out.p4"; } auto *ostream = openFile(outputFile, false); if (ostream == nullptr) { diff --git a/backends/p4tools/modules/testgen/targets/bmv2/test/small-step/p4_asserts_parser_test.cpp b/backends/p4tools/modules/testgen/targets/bmv2/test/small-step/p4_asserts_parser_test.cpp index a0ba39f3a9..e550ba147e 100644 --- a/backends/p4tools/modules/testgen/targets/bmv2/test/small-step/p4_asserts_parser_test.cpp +++ b/backends/p4tools/modules/testgen/targets/bmv2/test/small-step/p4_asserts_parser_test.cpp @@ -26,6 +26,7 @@ #include "backends/p4tools/modules/testgen/test/gtest_utils.h" /// Variables are declared in "test/gtest/env.h" which is already included in reachability.cpp +// FIXME: Make these Util::Path extern const char *sourcePath; extern const char *buildPath; @@ -56,13 +57,7 @@ ConstraintsVector loadExample(const char *curFile, bool flag) { auto *originalEnv = getenv("P4C_16_INCLUDE_PATH"); setenv("P4C_16_INCLUDE_PATH", includeDir.c_str(), 1); const IR::P4Program *program = nullptr; - options.file = cstring(sourcePath); - options.file += curFile; - if (access(options.file, 0) != 0) { - // Subpath for bf-p4c-compilers. - options.file = cstring(sourcePath); - options.file += curFile; - } + options.file = std::filesystem::path(sourcePath) / curFile; program = P4::parseP4File(options); if (originalEnv == nullptr) { unsetenv("P4C_16_INCLUDE_PATH"); diff --git a/backends/p4tools/modules/testgen/targets/bmv2/test/small-step/reachability.cpp b/backends/p4tools/modules/testgen/targets/bmv2/test/small-step/reachability.cpp index 4a51c644a1..5dcc6d40bf 100644 --- a/backends/p4tools/modules/testgen/targets/bmv2/test/small-step/reachability.cpp +++ b/backends/p4tools/modules/testgen/targets/bmv2/test/small-step/reachability.cpp @@ -54,8 +54,8 @@ ReturnedInfo loadExampleForReachability(const char *curFile) { auto *originalEnv = getenv("P4C_16_INCLUDE_PATH"); setenv("P4C_16_INCLUDE_PATH", includeDir.c_str(), 1); const IR::P4Program *program = nullptr; - options.file = cstring(sourcePath); - options.file += curFile; + options.file = sourcePath; + options.file /= curFile; program = P4::parseP4File(options); if (originalEnv == nullptr) { unsetenv("P4C_16_INCLUDE_PATH"); diff --git a/backends/p4tools/modules/testgen/testgen.cpp b/backends/p4tools/modules/testgen/testgen.cpp index 4dc92b80f7..298dd4eeaf 100644 --- a/backends/p4tools/modules/testgen/testgen.cpp +++ b/backends/p4tools/modules/testgen/testgen.cpp @@ -105,8 +105,8 @@ int generateAndWriteAbstractTests(const TestgenOptions &testgenOptions, /// If the test name is not provided, use the steam of the input file name as test name. if (testgenOptions.testBaseName.has_value()) { testPath = testgenOptions.testBaseName.value().c_str(); - } else if (cstring inputFile = P4CContext::get().options().file) { - testPath = std::filesystem::path(inputFile.c_str()).stem(); + } else if (!P4CContext::get().options().file.empty()) { + testPath = P4CContext::get().options().file.stem(); } else { ::error("Neither a file nor test base name was set. Can not infer a test name."); } @@ -159,7 +159,7 @@ std::optional generateTestsImpl(std::optionalgetPipelineName(); + std::string progName = tcIR->getPipelineName().string(); if (ebpf_program == nullptr) return; EBPF::CodeBuilder c(target), p(target), h(target); ebpf_program->emit(&c); @@ -131,10 +131,8 @@ void Backend::serialize() const { if (::errorCount() > 0) { return; } - cstring outputFile = progName + ".template"; - if (!options.outputFolder.isNullOrEmpty()) { - outputFile = options.outputFolder + outputFile; - } + std::filesystem::path outputFile = options.outputFolder / (progName + ".template"); + auto outstream = openFile(outputFile, false); if (outstream != nullptr) { *outstream << pipeline->toString(); @@ -145,14 +143,10 @@ void Backend::serialize() const { std::filesystem::perms::others_all, std::filesystem::perm_options::add); } - cstring parserFile = progName + "_parser.c"; - cstring postParserFile = progName + "_control_blocks.c"; - cstring headerFile = progName + "_parser.h"; - if (!options.outputFolder.isNullOrEmpty()) { - parserFile = options.outputFolder + parserFile; - postParserFile = options.outputFolder + postParserFile; - headerFile = options.outputFolder + headerFile; - } + std::filesystem::path parserFile = options.outputFolder / (progName + "_parser.c"); + std::filesystem::path postParserFile = options.outputFolder / (progName + "_control_blocks.c"); + std::filesystem::path headerFile = options.outputFolder / (progName + "_parser.h"); + auto cstream = openFile(postParserFile, false); auto pstream = openFile(parserFile, false); auto hstream = openFile(headerFile, false); @@ -185,21 +179,12 @@ bool Backend::serializeIntrospectionJson(std::ostream &out) const { } void ConvertToBackendIR::setPipelineName() { - cstring path = options.file; - if (path != nullptr) { - pipelineName = path; - } else { + if (options.file.empty()) { ::error("filename is not given in command line option"); return; } - auto fileName = path.findlast('/'); - if (fileName) { - pipelineName = cstring(fileName); - pipelineName = pipelineName.replace("/"_cs, ""_cs); - } - auto fileext = cstring(pipelineName.find(".")); - pipelineName = pipelineName.replace(fileext, ""_cs); - pipelineName = pipelineName.trim(); + + pipelineName = cstring(options.file.stem()); } bool ConvertToBackendIR::preorder(const IR::P4Program *p) { diff --git a/backends/tc/ebpfCodeGen.cpp b/backends/tc/ebpfCodeGen.cpp index 57787cbf43..b69a323029 100644 --- a/backends/tc/ebpfCodeGen.cpp +++ b/backends/tc/ebpfCodeGen.cpp @@ -25,14 +25,7 @@ void PNAEbpfGenerator::emitPNAIncludes(EBPF::CodeBuilder *builder) const { builder->appendLine("#include \"pna.h\""); } -cstring PNAEbpfGenerator::getProgramName() const { - auto progName = options.file; - auto filename = progName.findlast('/'); - if (filename) progName = cstring(filename); - progName = progName.exceptLast(3); - progName = progName.trim("/\t\n\r"); - return progName; -} +cstring PNAEbpfGenerator::getProgramName() const { return cstring(options.file.stem()); } void PNAEbpfGenerator::emitPreamble(EBPF::CodeBuilder *builder) const { emitCommonPreamble(builder); diff --git a/backends/tc/options.h b/backends/tc/options.h index 2051f098ee..76c8ee47a6 100644 --- a/backends/tc/options.h +++ b/backends/tc/options.h @@ -19,16 +19,13 @@ and limitations under the License. #include "backends/ebpf/ebpfOptions.h" #include "frontends/common/options.h" -#include "lib/cstring.h" namespace TC { -using namespace P4::literals; - class TCOptions : public CompilerOptions { public: // file to output to - cstring outputFolder = ""_cs; + std::filesystem::path outputFolder; bool DebugOn = false; // tracing eBPF code execution bool emitTraceMessages = false; @@ -40,7 +37,7 @@ class TCOptions : public CompilerOptions { registerOption( "-o", "output Directory", [this](const char *arg) { - outputFolder = cstring(arg); + outputFolder = arg; return true; }, "Write pipeline template, introspection json and C output to given directory"); diff --git a/backends/tc/tc.cpp b/backends/tc/tc.cpp index d460f3a851..0315635ec6 100644 --- a/backends/tc/tc.cpp +++ b/backends/tc/tc.cpp @@ -80,7 +80,7 @@ int main(int argc, char *const argv[]) { ::error("Cannot process input file. Program does not contain a 'main' module"); return 1; } - if (options.dumpJsonFile) + if (!options.dumpJsonFile.empty()) JSONGenerator(*openFile(options.dumpJsonFile, true)) << toplevel << std::endl; } catch (const Util::P4CExceptionBase &bug) { std::cerr << bug.what() << std::endl; @@ -91,25 +91,19 @@ int main(int argc, char *const argv[]) { } TC::Backend backend(toplevel, &midEnd.refMap, &midEnd.typeMap, options); if (!backend.process()) return 1; - cstring progName = backend.tcIR->getPipelineName(); - cstring introspecFile = progName + ".json"; - if (!options.outputFolder.isNullOrEmpty()) { - if (options.outputFolder.get(options.outputFolder.size() - 1) != '/') { - options.outputFolder = options.outputFolder + '/'; - } - introspecFile = options.outputFolder + introspecFile; - } + std::string progName = backend.tcIR->getPipelineName().string(); + std::string introspecFile = options.outputFolder / (progName + ".json"); std::ostream *outIntro = openFile(introspecFile, false); if (outIntro != nullptr) { bool serialized = backend.serializeIntrospectionJson(*outIntro); if (!serialized) { - std::remove(introspecFile); + std::remove(introspecFile.c_str()); return 1; } } backend.serialize(); if (::errorCount() > 0) { - std::remove(introspecFile); + std::remove(introspecFile.c_str()); return 1; } return ::errorCount() > 0; diff --git a/backends/ubpf/ubpfBackend.cpp b/backends/ubpf/ubpfBackend.cpp index d5e847e388..f0f4f483f2 100644 --- a/backends/ubpf/ubpfBackend.cpp +++ b/backends/ubpf/ubpfBackend.cpp @@ -47,30 +47,26 @@ void run_ubpf_backend(const EbpfOptions &options, const IR::ToplevelBlock *tople } UBPFTypeFactory::createFactory(typeMap); - auto prog = new UBPFProgram(options, toplevel->getProgram(), refMap, typeMap, toplevel); + auto *prog = new UBPFProgram(options, toplevel->getProgram(), refMap, typeMap, toplevel); if (!prog->build()) return; - if (options.outputFile.isNullOrEmpty()) return; + if (options.outputFile.empty()) return; - cstring cfile = options.outputFile; - auto cstream = openFile(cfile, false); + auto *cstream = openFile(options.outputFile, false); if (cstream == nullptr) return; - cstring hfile; - const char *dot = cfile.findlast('.'); - if (dot == nullptr) - hfile = cfile + ".h"; - else - hfile = cfile.before(dot) + ".h"; - auto hstream = openFile(hfile, false); + std::filesystem::path hfile = options.outputFile; + hfile.replace_extension(".h"); + + auto *hstream = openFile(hfile, false); if (hstream == nullptr) return; UbpfCodeBuilder c(target); UbpfCodeBuilder h(target); prog->emitH(&h, hfile); - prog->emitC(&c, UBPF::extract_file_name(hfile.c_str())); + prog->emitC(&c, hfile.filename()); *cstream << c.toString(); *hstream << h.toString(); @@ -78,8 +74,4 @@ void run_ubpf_backend(const EbpfOptions &options, const IR::ToplevelBlock *tople hstream->flush(); } -std::string extract_file_name(const std::string &fullPath) { - const size_t lastSlashIndex = fullPath.find_last_of("/\\"); - return fullPath.substr(lastSlashIndex + 1); -} } // namespace UBPF diff --git a/backends/ubpf/ubpfProgram.cpp b/backends/ubpf/ubpfProgram.cpp index 590cdf8a6e..7091a6f2a0 100644 --- a/backends/ubpf/ubpfProgram.cpp +++ b/backends/ubpf/ubpfProgram.cpp @@ -60,7 +60,7 @@ bool UBPFProgram::build() { return success; } -void UBPFProgram::emitC(UbpfCodeBuilder *builder, cstring headerFile) { +void UBPFProgram::emitC(UbpfCodeBuilder *builder, const std::filesystem::path &headerFile) { emitGeneratedComment(builder); builder->appendFormat("#include \"%s\"", headerFile); @@ -126,7 +126,7 @@ void UBPFProgram::emitC(UbpfCodeBuilder *builder, cstring headerFile) { builder->blockEnd(true); } -void UBPFProgram::emitH(EBPF::CodeBuilder *builder, cstring) { +void UBPFProgram::emitH(EBPF::CodeBuilder *builder, const std::filesystem::path &) { emitGeneratedComment(builder); builder->appendLine("#ifndef _P4_GEN_HEADER_"); builder->appendLine("#define _P4_GEN_HEADER_"); diff --git a/backends/ubpf/ubpfProgram.h b/backends/ubpf/ubpfProgram.h index 82493e009b..1706254139 100644 --- a/backends/ubpf/ubpfProgram.h +++ b/backends/ubpf/ubpfProgram.h @@ -62,8 +62,8 @@ class UBPFProgram : public EBPF::EBPFProgram { } bool build() override; - void emitC(UbpfCodeBuilder *builder, cstring headerFile); - void emitH(EBPF::CodeBuilder *builder, cstring headerFile) override; + void emitC(UbpfCodeBuilder *builder, const std::filesystem::path &headerFile); + void emitH(EBPF::CodeBuilder *builder, const std::filesystem::path &headerFile) override; void emitPreamble(EBPF::CodeBuilder *builder) override; void emitTypes(EBPF::CodeBuilder *builder) override; void emitTableDefinition(EBPF::CodeBuilder *builder) const; diff --git a/control-plane/p4RuntimeSerializer.cpp b/control-plane/p4RuntimeSerializer.cpp index 32aef714f0..332c62c5bf 100644 --- a/control-plane/p4RuntimeSerializer.cpp +++ b/control-plane/p4RuntimeSerializer.cpp @@ -1581,6 +1581,7 @@ void P4RuntimeSerializer::serializeP4RuntimeIfRequired(const IR::P4Program *prog void P4RuntimeSerializer::serializeP4RuntimeIfRequired(const P4RuntimeAPI &p4Runtime, const CompilerOptions &options) { + // FIXME: get rid of cstring here std::vector files; std::vector formats; @@ -1594,7 +1595,7 @@ void P4RuntimeSerializer::serializeP4RuntimeIfRequired(const P4RuntimeAPI &p4Run for (unsigned i = 0; i < files.size(); i++) { cstring file = files.at(i); P4::P4RuntimeFormat format = formats.at(i); - std::ostream *out = openFile(file, false); + std::ostream *out = openFile(file.string(), false); if (!out) { ::error(ErrorType::ERR_IO, "Couldn't open P4Runtime API file: %1%", file); continue; @@ -1616,7 +1617,7 @@ void P4RuntimeSerializer::serializeP4RuntimeIfRequired(const P4RuntimeAPI &p4Run for (unsigned i = 0; i < files.size(); i++) { cstring file = files.at(i); P4::P4RuntimeFormat format = formats.at(i); - std::ostream *out = openFile(file, false); + std::ostream *out = openFile(file.string(), false); if (!out) { ::error(ErrorType::ERR_IO, "Couldn't open P4Runtime static entries file: %1%", options.p4RuntimeEntriesFile); diff --git a/frontends/common/options.cpp b/frontends/common/options.cpp index 85efbe60c5..8f89dc2dc3 100644 --- a/frontends/common/options.cpp +++ b/frontends/common/options.cpp @@ -53,7 +53,7 @@ CompilerOptions::CompilerOptions() : ParserOptions() { registerOption( "--toJSON", "file", [this](const char *arg) { - dumpJsonFile = cstring(arg); + dumpJsonFile = arg; return true; }, "Dump the compiler IR after the midend as JSON in the specified file."); @@ -74,7 +74,7 @@ CompilerOptions::CompilerOptions() : ParserOptions() { registerOption( "--pp", "file", [this](const char *arg) { - prettyPrintFile = cstring(arg); + prettyPrintFile = arg; return true; }, "Pretty-print the program in the specified file."); diff --git a/frontends/common/options.h b/frontends/common/options.h index 1bd6704ad9..8a842fb96d 100644 --- a/frontends/common/options.h +++ b/frontends/common/options.h @@ -19,6 +19,8 @@ limitations under the License. #ifndef FRONTENDS_COMMON_OPTIONS_H_ #define FRONTENDS_COMMON_OPTIONS_H_ +#include + #include "parser_options.h" // for p4::P4RuntimeFormat definition #include "control-plane/p4RuntimeTypes.h" @@ -52,7 +54,7 @@ class CompilerOptions : public ParserOptions { // passes. std::vector passesToExcludeBackend; // Dump a JSON representation of the IR in the file. - cstring dumpJsonFile = nullptr; + std::filesystem::path dumpJsonFile; // Dump and undump the IR tree. bool debugJson = false; // if this flag is true, compile program in non-debug mode. @@ -70,7 +72,7 @@ class CompilerOptions : public ParserOptions { // Choose format for P4Runtime API description. P4::P4RuntimeFormat p4RuntimeFormat = P4::P4RuntimeFormat::BINARY; // Pretty-print the program in the specified file. - cstring prettyPrintFile = nullptr; + std::filesystem::path prettyPrintFile; // Target. cstring target = nullptr; // Architecture. diff --git a/frontends/common/parseInput.h b/frontends/common/parseInput.h index 9139471bb4..0f8eab69d2 100644 --- a/frontends/common/parseInput.h +++ b/frontends/common/parseInput.h @@ -31,7 +31,7 @@ class P4Program; namespace P4 { template -static const IR::P4Program *parseV1Program(Input &stream, const char *sourceFile, +static const IR::P4Program *parseV1Program(Input &stream, std::string_view sourceFile, unsigned sourceLine, std::optional debugHook = std::nullopt) { // We load the model before parsing the input file, so that the SourceInfo @@ -67,7 +67,7 @@ const IR::P4Program *parseP4File(ParserOptions &options) { "compiler context"); FILE *in = nullptr; if (options.doNotPreprocess) { - in = fopen(options.file, "r"); + in = fopen(options.file.c_str(), "r"); if (in == nullptr) { ::error(ErrorType::ERR_NOT_FOUND, "%1%: No such file or directory.", options.file); return nullptr; @@ -77,9 +77,9 @@ const IR::P4Program *parseP4File(ParserOptions &options) { if (::errorCount() > 0 || in == nullptr) return nullptr; } - auto result = options.isv1() - ? parseV1Program(in, options.file, 1, options.getDebugHook()) - : P4ParserDriver::parse(in, options.file); + auto result = options.isv1() ? parseV1Program(in, options.file.string(), 1, + options.getDebugHook()) + : P4ParserDriver::parse(in, options.file.string()); if (options.doNotPreprocess) { fclose(in); } else { diff --git a/frontends/common/parser_options.cpp b/frontends/common/parser_options.cpp index 1d5e502a52..649888bc97 100644 --- a/frontends/common/parser_options.cpp +++ b/frontends/common/parser_options.cpp @@ -26,13 +26,14 @@ limitations under the License. #include #include +#include "absl/strings/escaping.h" +#include "absl/strings/str_format.h" #include "frontends/p4/toP4/toP4.h" #include "ir/json_generator.h" #include "lib/exceptions.h" #include "lib/exename.h" #include "lib/log.h" #include "lib/nullstream.h" -#include "lib/path.h" /* CONFIG_PKGDATADIR is defined by cmake at compile time to be the same as * CMAKE_INSTALL_PREFIX This is only valid when the compiler is built and @@ -288,7 +289,7 @@ ParserOptions::ParserOptions() : Util::Options(defaultMessage) { registerOption( "--dump", "folder", [this](const char *arg) { - dumpFolder = cstring(arg); + dumpFolder = arg; return true; }, "[Compiler debugging] Folder where P4 programs are dumped\n"); @@ -331,7 +332,7 @@ void ParserOptions::setInputFile() { usage(); exit(1); } else { - file = cstring(remainingOptions.at(0)); + file = remainingOptions.at(0); } } @@ -405,7 +406,7 @@ FILE *ParserOptions::preprocess() { FILE *in = nullptr; if (file == "-") { - file = ""_cs; + file = ""; in = stdin; } else { #ifdef __clang__ @@ -414,10 +415,8 @@ FILE *ParserOptions::preprocess() { std::string cmd("cpp"); #endif - if (file == nullptr) file = cstring::empty; - if (file.find(' ')) file = "\""_cs + file + "\""_cs; cmd += " -C -undef -nostdinc -x assembler-with-cpp " + preprocessor_options.string() + - getIncludePath() + " " + file.string(); + getIncludePath() + " " + absl::CEscape(file.native()); if (Log::verbose()) std::cerr << "Invoking preprocessor " << std::endl << cmd << std::endl; in = popen(cmd.c_str(), "r"); @@ -454,11 +453,14 @@ void ParserOptions::closePreprocessedInput(FILE *inputStream) const { // From (folder, file.ext, suffix) returns // folder/file-suffix.ext -static cstring makeFileName(cstring folder, cstring name, cstring baseSuffix) { - Util::PathName filename(name); - Util::PathName newName(filename.getBasename() + baseSuffix + "." + filename.getExtension()); - auto result = Util::PathName(folder).join(newName.toString()); - return result.toString(); +static std::filesystem::path makeFileName(const std::filesystem::path &folder, + const std::filesystem::path &name, + std::string_view baseSuffix) { + std::filesystem::path newName(name.stem()); + newName += baseSuffix; + newName += name.extension(); + + return folder / newName; } bool ParserOptions::isv1() const { return langVersion == ParserOptions::FrontendVersion::P4_14; } @@ -466,7 +468,7 @@ bool ParserOptions::isv1() const { return langVersion == ParserOptions::Frontend void ParserOptions::dumpPass(const char *manager, unsigned seq, const char *pass, const IR::Node *node) const { if (strncmp(pass, "P4::", 4) == 0) pass += 4; - cstring name = cstring(manager) + "_" + Util::toString(seq) + "_" + pass; + std::string name = absl::StrCat(manager, "_", seq, "_", pass); if (Log::verbose()) std::cerr << name << std::endl; for (auto s : top4) { @@ -485,18 +487,15 @@ void ParserOptions::dumpPass(const char *manager, unsigned seq, const char *pass exit(1); } if (match) { - // FIXME: switch to sane printing via abseil - char buf[16]; - snprintf(buf, sizeof(buf), "-%04zu-", ++dump_uid); - cstring suffix = cstring(buf) + name; - cstring filename = file; - if (filename == "-") filename = "tmp.p4"_cs; - - cstring fileName = makeFileName(dumpFolder, filename, suffix); + std::string suffix = absl::StrFormat("-%04zu-%s", ++dump_uid, name); + std::filesystem::path fileName = + makeFileName(dumpFolder, (file == "-" ? "tmp.p4" : file), suffix); + std::unique_ptr stream{openFile(fileName, true)}; if (stream != nullptr) { if (Log::verbose()) std::cerr << "Writing program to " << fileName << std::endl; - P4::ToP4 toP4(stream.get(), Log::verbose(), file); + // FIXME: Accept path here + P4::ToP4 toP4(stream.get(), Log::verbose(), cstring(file)); if (noIncludes) { toP4.setnoIncludesArg(true); } diff --git a/frontends/common/parser_options.h b/frontends/common/parser_options.h index 1da48b0dee..3328394e8f 100644 --- a/frontends/common/parser_options.h +++ b/frontends/common/parser_options.h @@ -19,6 +19,7 @@ limitations under the License. #ifndef FRONTENDS_COMMON_PARSER_OPTIONS_H_ #define FRONTENDS_COMMON_PARSER_OPTIONS_H_ +#include #include #include @@ -63,7 +64,7 @@ class ParserOptions : public Util::Options { // options to pass to preprocessor cstring preprocessor_options = cstring::empty; // file to compile (- for stdin) - cstring file = nullptr; + std::filesystem::path file; // if true preprocess only bool doNotCompile = false; // Compiler version. @@ -73,7 +74,7 @@ class ParserOptions : public Util::Options { // substrings matched against pass names std::vector top4; // debugging dumps of programs written in this folder - cstring dumpFolder = cstring::literal("."); + std::filesystem::path dumpFolder = "."; // If false, optimization of callee parsers (subparsers) inlining is disabled. bool optimizeParserInlining = false; // Expect that the only remaining argument is the input file. diff --git a/frontends/p4/fromv1.0/programStructure.cpp b/frontends/p4/fromv1.0/programStructure.cpp index 385aa233d9..0098326801 100644 --- a/frontends/p4/fromv1.0/programStructure.cpp +++ b/frontends/p4/fromv1.0/programStructure.cpp @@ -28,7 +28,6 @@ limitations under the License. #include "frontends/parsers/parserDriver.h" #include "lib/big_int_util.h" #include "lib/bitops.h" -#include "lib/path.h" namespace P4V1 { using namespace P4::literals; @@ -668,8 +667,8 @@ void ProgramStructure::include(cstring filename, cstring ppoptions) { // paths. check the environment and add these to the command // line for the preporicessor char *drvP4IncludePath = getenv("P4C_16_INCLUDE_PATH"); - Util::PathName path(drvP4IncludePath ? drvP4IncludePath : p4includePath); - path = path.join(filename); + std::filesystem::path path(drvP4IncludePath ? drvP4IncludePath : p4includePath); + path /= std::string(filename); CompilerOptions options; if (ppoptions) { @@ -677,10 +676,10 @@ void ProgramStructure::include(cstring filename, cstring ppoptions) { options.preprocessor_options += ppoptions; } options.langVersion = CompilerOptions::FrontendVersion::P4_16; - options.file = path.toString(); + options.file = path; if (!::errorCount()) { if (FILE *file = options.preprocess()) { - auto code = P4::P4ParserDriver::parse(file, options.file); + auto code = P4::P4ParserDriver::parse(file, options.file.string()); if (code && !::errorCount()) for (auto decl : code->objects) declarations->push_back(decl); options.closePreprocessedInput(file); diff --git a/frontends/p4/frontend.cpp b/frontends/p4/frontend.cpp index 70325ea3f0..bcacebaa11 100644 --- a/frontends/p4/frontend.cpp +++ b/frontends/p4/frontend.cpp @@ -26,7 +26,6 @@ limitations under the License. #include "frontends/p4/typeMap.h" #include "ir/ir.h" #include "lib/nullstream.h" -#include "lib/path.h" // Passes #include "actionsInlining.h" #include "checkConstants.h" @@ -92,9 +91,9 @@ This pass outputs the program as a P4 source file. */ class PrettyPrint : public Inspector { /// output file - cstring ppfile; + std::filesystem::path ppfile; /// The file that is being compiled. - cstring inputfile; + std::filesystem::path inputfile; public: explicit PrettyPrint(const CompilerOptions &options) { @@ -103,10 +102,10 @@ class PrettyPrint : public Inspector { inputfile = options.file; } bool preorder(const IR::P4Program *program) override { - if (!ppfile.isNullOrEmpty()) { - Util::PathName path(ppfile); - std::ostream *ppStream = openFile(path.toString(), true); - P4::ToP4 top4(ppStream, false, inputfile); + if (!ppfile.empty()) { + std::ostream *ppStream = openFile(ppfile, true); + // FIXME: ToP4 should accept PathName + P4::ToP4 top4(ppStream, false, cstring(inputfile)); (void)program->apply(top4); } return false; // prune diff --git a/frontends/parsers/parserDriver.cpp b/frontends/parsers/parserDriver.cpp index 0734b0c85a..e63e6e008f 100644 --- a/frontends/parsers/parserDriver.cpp +++ b/frontends/parsers/parserDriver.cpp @@ -113,7 +113,7 @@ void AbstractParserDriver::onParseError(const Util::SourceInfo &location, P4ParserDriver::P4ParserDriver() : structure(new Util::ProgramStructure), nodes(new IR::Vector()) {} -bool P4ParserDriver::parse(AbstractP4Lexer &lexer, const char *sourceFile, +bool P4ParserDriver::parse(AbstractP4Lexer &lexer, std::string_view sourceFile, unsigned sourceLine /* = 1 */) { // Create and configure the parser. P4Parser parser(*this, lexer); @@ -132,7 +132,8 @@ bool P4ParserDriver::parse(AbstractP4Lexer &lexer, const char *sourceFile, return true; } -/* static */ const IR::P4Program *P4ParserDriver::parse(std::istream &in, const char *sourceFile, +/* static */ const IR::P4Program *P4ParserDriver::parse(std::istream &in, + std::string_view sourceFile, unsigned sourceLine /* = 1 */) { LOG1("Parsing P4-16 program " << sourceFile); @@ -142,7 +143,7 @@ bool P4ParserDriver::parse(AbstractP4Lexer &lexer, const char *sourceFile, return new IR::P4Program(driver.nodes->srcInfo, *driver.nodes); } -/* static */ const IR::P4Program *P4ParserDriver::parse(FILE *in, const char *sourceFile, +/* static */ const IR::P4Program *P4ParserDriver::parse(FILE *in, std::string_view sourceFile, unsigned sourceLine /* = 1 */) { AutoStdioInputStream inputStream(in); return parse(inputStream.get(), sourceFile, sourceLine); @@ -154,7 +155,7 @@ const T *P4ParserDriver::parse(P4AnnotationLexer::Type type, const Util::SourceI LOG3("Parsing P4-16 annotation " << srcInfo); P4AnnotationLexer lexer(type, srcInfo, body); - if (!parse(lexer, srcInfo.getSourceFile())) { + if (!parse(lexer, srcInfo.getSourceFile().string_view())) { return nullptr; } @@ -283,7 +284,8 @@ namespace V1 { V1ParserDriver::V1ParserDriver() : global(new IR::V1Program) {} -/* static */ const IR::V1Program *V1ParserDriver::parse(std::istream &in, const char *sourceFile, +/* static */ const IR::V1Program *V1ParserDriver::parse(std::istream &in, + std::string_view sourceFile, unsigned sourceLine /* = 1 */) { LOG1("Parsing P4-14 program " << sourceFile); @@ -304,7 +306,7 @@ V1ParserDriver::V1ParserDriver() : global(new IR::V1Program) {} return driver.global; } -/* static */ const IR::V1Program *V1ParserDriver::parse(FILE *in, const char *sourceFile, +/* static */ const IR::V1Program *V1ParserDriver::parse(FILE *in, std::string_view sourceFile, unsigned sourceLine /* = 1 */) { AutoStdioInputStream inputStream(in); return parse(inputStream.get(), sourceFile, sourceLine); diff --git a/frontends/parsers/parserDriver.h b/frontends/parsers/parserDriver.h index be2c29de0f..9b22e65aaa 100644 --- a/frontends/parsers/parserDriver.h +++ b/frontends/parsers/parserDriver.h @@ -20,6 +20,7 @@ limitations under the License. #include #include #include +#include #include "frontends/p4/symbol_table.h" #include "frontends/parsers/p4/abstractP4Lexer.hpp" @@ -109,9 +110,10 @@ class P4ParserDriver final : public AbstractParserDriver { * set the initial source location. * @returns a P4Program object if parsing was successful, or null otherwise. */ - static const IR::P4Program *parse(std::istream &in, const char *sourceFile, + static const IR::P4Program *parse(std::istream &in, std::string_view sourceFile, + unsigned sourceLine = 1); + static const IR::P4Program *parse(FILE *in, std::string_view sourceFile, unsigned sourceLine = 1); - static const IR::P4Program *parse(FILE *in, const char *sourceFile, unsigned sourceLine = 1); /** * Parses a P4-16 annotation body. @@ -202,7 +204,7 @@ class P4ParserDriver final : public AbstractParserDriver { P4ParserDriver(); /// Common functionality for parsing. - bool parse(AbstractP4Lexer &lexer, const char *sourceFile, unsigned sourceLine = 1); + bool parse(AbstractP4Lexer &lexer, std::string_view sourceFile, unsigned sourceLine = 1); /// Common functionality for parsing annotation bodies. template @@ -238,9 +240,10 @@ class V1ParserDriver final : public P4::AbstractParserDriver { * set the initial source location. * @returns a V1Program object if parsing was successful, or null otherwise. */ - static const IR::V1Program *parse(std::istream &in, const char *sourceFile, + static const IR::V1Program *parse(std::istream &in, std::string_view sourceFile, + unsigned sourceLine = 1); + static const IR::V1Program *parse(FILE *in, std::string_view sourceFile, unsigned sourceLine = 1); - static const IR::V1Program *parse(FILE *in, const char *sourceFile, unsigned sourceLine = 1); protected: friend class V1::V1Lexer; diff --git a/ir/id.h b/ir/id.h index c1d6ee578a..ede6526b38 100644 --- a/ir/id.h +++ b/ir/id.h @@ -53,6 +53,8 @@ struct ID : Util::IHasSourceInfo { bool operator!=(const char *a) const { return name != a; } explicit operator bool() const { return name; } operator cstring() const { return name; } + std::string string() const { return name.string(); } + std::string_view string_view() const { return name.string_view(); } bool isDontCare() const { return name == "_"; } Util::SourceInfo getSourceInfo() const override { return srcInfo; } cstring toString() const override { return originalName.isNullOrEmpty() ? name : originalName; } diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index d02a6168a4..21204b63dc 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -35,7 +35,6 @@ set(LIBP4CTOOLKIT_SRCS nethash.cpp nullstream.cpp options.cpp - path.cpp source_file.cpp stringify.cpp timer.cpp @@ -78,7 +77,6 @@ set(LIBP4CTOOLKIT_HDRS options.h ordered_map.h ordered_set.h - path.h range.h safe_vector.h set.h diff --git a/lib/README.md b/lib/README.md index d7c79e21ac..97269ae5d2 100644 --- a/lib/README.md +++ b/lib/README.md @@ -71,10 +71,6 @@ A simple ostream that does nothing. Represents compiler command-line options. -##### path.h, path.cpp - -Simple system-independent pathname abstraction. - ##### range.h Iterators over numeric ranges. diff --git a/lib/nullstream.cpp b/lib/nullstream.cpp index 31a82ebd64..9436bbe9f3 100644 --- a/lib/nullstream.cpp +++ b/lib/nullstream.cpp @@ -18,9 +18,10 @@ limitations under the License. #include // IWYU pragma: keep -// FIXME: this should accept string_view instead -std::ostream *openFile(cstring name, bool nullOnError) { - if (name.isNullOrEmpty()) { +#include "lib/error.h" + +std::ostream *openFile(const std::filesystem::path &name, bool nullOnError) { + if (name.empty()) { if (nullOnError) return new nullstream(); ::error(ErrorType::ERR_INVALID, "Empty name for openFile"); return nullptr; diff --git a/lib/nullstream.h b/lib/nullstream.h index 3bc84e2271..28973cb654 100644 --- a/lib/nullstream.h +++ b/lib/nullstream.h @@ -17,13 +17,11 @@ limitations under the License. #ifndef LIB_NULLSTREAM_H_ #define LIB_NULLSTREAM_H_ +#include #include #include #include -#include "cstring.h" -#include "error.h" - template > class basic_nullbuf final : public std::basic_streambuf { typename traits::int_type overflow(typename traits::int_type c) { @@ -46,6 +44,7 @@ typedef onullstream nullstream; // If nullOnError is 'true', on error a nullstream is returned // otherwise a nullptr is returned -std::ostream *openFile(cstring name, bool nullOnError); +// FIXME: This should return unique_ptr instead to track lifetime +std::ostream *openFile(const std::filesystem::path &name, bool nullOnError); #endif /* LIB_NULLSTREAM_H_ */ diff --git a/lib/path.cpp b/lib/path.cpp deleted file mode 100644 index 7bcfc206bf..0000000000 --- a/lib/path.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/* -Copyright 2013-present Barefoot Networks, Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -#include "path.h" - -#include - -#include "algorithm.h" - -namespace Util { - -PathName PathName::empty = PathName(""); -const char PathName::pathSeparators[2] = {'/', '\\'}; - -const char *PathName::findLastSeparator() const { - const char *found = nullptr; - // Not the most efficient, but should be fine for short strings - for (char c : pathSeparators) { - const char *f = str.findlast(c); - if (f == nullptr) continue; - if (found == nullptr) - found = f; - else if (f < found) - found = f; - } - return found; -} - -PathName PathName::getFilename() const { - if (str.isNullOrEmpty()) return *this; - - const char *lastSeparator = findLastSeparator(); - if (lastSeparator == 0) return *this; - - return PathName(lastSeparator + 1); -} - -cstring PathName::getExtension() const { - PathName filename = getFilename(); - if (filename.isNullOrEmpty()) return filename.str; - - const char *dot = filename.str.findlast('.'); - if (dot == nullptr) return cstring::empty; - return cstring(dot + 1); -} - -PathName PathName::getFolder() const { - if (str.isNullOrEmpty()) return *this; - - const char *lastSeparator = findLastSeparator(); - if (lastSeparator == 0) return PathName::empty; - - return PathName(str.before(lastSeparator)); -} - -cstring PathName::getBasename() const { - PathName file = getFilename(); - const char *dot = file.str.findlast('.'); - if (dot == nullptr) return file.str; - return file.str.before(dot); -} - -PathName PathName::join(cstring component) const { - if (component.isNullOrEmpty()) throw std::logic_error("Empty string for pathname component"); - if (str.isNullOrEmpty()) return PathName(component); - char last = str[str.size() - 1]; - for (char c : pathSeparators) { - if (c == last) { - auto result = str + component; - return PathName(result); - } - } - auto result = str + separator() + component; - return PathName(result); -} - -} // namespace Util diff --git a/lib/path.h b/lib/path.h deleted file mode 100644 index 9e28369236..0000000000 --- a/lib/path.h +++ /dev/null @@ -1,73 +0,0 @@ -/* -Copyright 2013-present Barefoot Networks, Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -#ifndef LIB_PATH_H_ -#define LIB_PATH_H_ - -/* - It's 2015 and C++ still does not have a portable way to manipulate pathnames. - This code is not portable, but at least the interfaces should be. -*/ - -#include "cstring.h" - -namespace Util { -using namespace P4::literals; - -// Represents a filename path, e.g., /usr/local/bin/file.exe -// FIXME: Can we replace with std::filesystem? -class PathName final { - private: - static const char pathSeparators[2]; - cstring str; - - const char *findLastSeparator() const; - - public: - static inline cstring separator() { -#ifdef _WIN32 - return "\\"_cs; -#else - return "/"_cs; -#endif - } - - PathName(cstring str) : str(str) {} // NOLINT(runtime/explicit) - PathName(const char *str) : str(str) {} // NOLINT(runtime/explicit) - PathName(const std::string &str) : str(str) {} // NOLINT(runtime/explicit) - // get the file name extension. It starts at the last dot. - // e.g, exe - cstring getExtension() const; - // extract just the filename, including the extension - // e.g., file.exe - PathName getFilename() const; - // extract the filename without folder, excluding the extension - // e.g., file - cstring getBasename() const; - // extract the folder - // e.g., /usr/local/bin - PathName getFolder() const; - cstring toString() const { return str; } - bool isNullOrEmpty() const { return str.isNullOrEmpty(); } - bool operator==(const PathName &other) const { return str == other.str; } - bool operator!=(const PathName &other) const { return str != other.str; } - PathName join(cstring component) const; - - static PathName empty; -}; -} // namespace Util - -#endif /* LIB_PATH_H_ */ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5969155959..7e8b31d65d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -46,7 +46,6 @@ set (GTEST_UNITTEST_SOURCES gtest/ordered_map.cpp gtest/ordered_set.cpp gtest/parser_unroll.cpp - gtest/path_test.cpp gtest/p4runtime.cpp gtest/source_file_test.cpp gtest/strength_reduction.cpp diff --git a/test/gtest/parser_unroll.cpp b/test/gtest/parser_unroll.cpp index 2c417572fe..f04f69ba42 100644 --- a/test/gtest/parser_unroll.cpp +++ b/test/gtest/parser_unroll.cpp @@ -66,9 +66,9 @@ const IR::P4Program *load_model(const char *curFile, CompilerOptions &options) { auto originalEnv = getenv("P4C_16_INCLUDE_PATH"); setenv("P4C_16_INCLUDE_PATH", includeDir.c_str(), 1); options.loopsUnrolling = true; - options.file = cstring(sourcePath); - options.file += "testdata/p4_16_samples/"; - options.file += curFile; + options.file = sourcePath; + options.file /= "testdata/p4_16_samples"; + options.file /= curFile; auto program = P4::parseP4File(options); if (!originalEnv) unsetenv("P4C_16_INCLUDE_PATH"); diff --git a/test/gtest/path_test.cpp b/test/gtest/path_test.cpp deleted file mode 100644 index 3c302db2e6..0000000000 --- a/test/gtest/path_test.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* -Copyright 2013-present Barefoot Networks, Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -#include "lib/path.h" - -#include - -namespace Util { - -TEST(Util, PathName) { -#if _WIN32 - FAIL() << "PathName tests for WIN32 not yet written"; -#endif - - { - PathName path = "/usr/local/bin/file.exe"; - auto ext = path.getExtension(); - EXPECT_EQ("exe", ext); - - PathName file = path.getFilename(); - EXPECT_EQ("file.exe", file.toString()); - - auto base = path.getBasename(); - EXPECT_EQ("file", base); - - PathName folder = path.getFolder(); - EXPECT_EQ("/usr/local/bin", folder.toString()); - } - - { - PathName path = "/usr/local/bin/"; - auto ext = path.getExtension(); - EXPECT_EQ("", ext); - - PathName file = path.getFilename(); - EXPECT_EQ("", file.toString()); - - auto base = path.getBasename(); - EXPECT_EQ("", base); - - PathName folder = path.getFolder(); - EXPECT_EQ("/usr/local/bin", folder.toString()); - } - - { - PathName path = "file.exe"; - auto ext = path.getExtension(); - EXPECT_EQ("exe", ext); - - PathName file = path.getFilename(); - EXPECT_EQ("file.exe", file.toString()); - - auto base = path.getBasename(); - EXPECT_EQ("file", base); - - PathName folder = path.getFolder(); - EXPECT_EQ("", folder.toString()); - } - - { - PathName path = ""; - EXPECT_TRUE(path.isNullOrEmpty()); - PathName grow = path.join("x"_cs); - EXPECT_EQ("x", grow.toString()); - EXPECT_FALSE(grow.isNullOrEmpty()); - - grow = grow.join("y"_cs); - EXPECT_EQ("x/y", grow.toString()); - - path = PathName("x/"); - grow = path.join("y"_cs); - EXPECT_EQ("x/y", grow.toString()); - } -} - -} // namespace Util diff --git a/tools/ir-generator/generator.cpp b/tools/ir-generator/generator.cpp index e2dc54c381..d6fdb1f334 100644 --- a/tools/ir-generator/generator.cpp +++ b/tools/ir-generator/generator.cpp @@ -52,15 +52,15 @@ int main(int argc, char *argv[]) { usage(argv[0]); return 1; case 'o': - header = openFile(cstring(optarg), false); + header = openFile(optarg, false); if (header == nullptr) return 1; break; case 'i': - impl = openFile(cstring(optarg), false); + impl = openFile(optarg, false); if (impl == nullptr) return 1; break; case 't': - t = openFile(cstring(optarg), false); + t = openFile(optarg, false); if (t == nullptr) return 1; break; case 'P':