See More

// Copyright (c) 2022-present INESC-ID. // Distributed under the MIT license that can be found in the LICENSE file. #include #include #include #include #include #include #if defined(_WIN32) #include #elif defined(__linux__) #include #include #elif defined(__APPLE__) #include #endif #include #include "cpp2rust_lib.h" #include "logging.h" namespace fs = std::filesystem; namespace { llvm::cl::OptionCategory cpp2rust_cmdargs("Cpp2Rust options"); llvm::cl::opt Verbose("verbose", llvm::cl::desc("Enable verbose logging"), llvm::cl::init(false), llvm::cl::cat(cpp2rust_cmdargs)); llvm::cl::opt<:string> CcFile("file", llvm::cl::desc("Path to the C++ file"), llvm::cl::value_desc("file.cpp"), llvm::cl::cat(cpp2rust_cmdargs)); llvm::cl::opt<:string> BuildDir("dir", llvm::cl::desc("Directory that contains compile_commands.json"), llvm::cl::value_desc("dir"), llvm::cl::cat(cpp2rust_cmdargs)); llvm::cl::opt<:string> RsFile("o", llvm::cl::desc("Path to the Rust file"), llvm::cl::value_desc("output.rs"), llvm::cl::Required, llvm::cl::cat(cpp2rust_cmdargs)); llvm::cl::opt<:string> Model("model", llvm::cl::desc( "Name of the translation model (unsafe, refcount [default])"), llvm::cl::value_desc("model"), llvm::cl::init("refcount"), llvm::cl::cat(cpp2rust_cmdargs)); llvm::cl::opt<:string> RulesDir("rules", llvm::cl::desc("Directory where translation rules are located"), llvm::cl::value_desc("rules"), llvm::cl::cat(cpp2rust_cmdargs)); llvm::cl::list<:string> CXXFlags("cxxflags", llvm::cl::desc("Additional CXXFLAGS"), llvm::cl::value_desc("cxxflags"), llvm::cl::ZeroOrMore, llvm::cl::cat(cpp2rust_cmdargs)); } // namespace // Get the directory of the running executable static fs::path GetExecutableDir() { #if defined(_WIN32) char path[MAX_PATH]; GetModuleFileNameA(NULL, path, MAX_PATH); return fs::path(path).parent_path(); #elif defined(__linux__) char path[PATH_MAX]; ssize_t count = readlink("/proc/self/exe", path, PATH_MAX); return fs::path(std::string_view(path, std::max((ssize_t)0, count))) .parent_path(); #elif defined(__APPLE__) uint32_t size = 0; _NSGetExecutablePath(nullptr, &size); // get path length std::vector buffer(size); _NSGetExecutablePath(buffer.data(), &size); return fs::path(buffer.data()).parent_path(); #endif return "."; } static bool HasIRFiles(const fs::path &dir) { std::error_code ec; for (auto it = fs::recursive_directory_iterator(dir, ec); !ec && it != fs::recursive_directory_iterator(); it.increment(ec)) { if (!it->is_directory()) { continue; } const auto &p = it->path(); if (fs::exists(p / "ir_src.json") && (fs::exists(p / "ir_unsafe.json") || fs::exists(p / "ir_refcount.json"))) { return true; } } return false; } static bool ResolveRulesDir() { std::array<:path> candidates = { fs::path("./rules"), GetExecutableDir().parent_path() / "rules"}; for (const auto &dir : candidates) { if (fs::exists(dir) && fs::is_directory(dir) && HasIRFiles(dir)) { RulesDir = fs::canonical(dir).string(); llvm::errs() << "Using rules directory: " << RulesDir << '\n'; return true; } } return false; } int main(int argc, char *argv[]) { llvm::cl::HideUnrelatedOptions(cpp2rust_cmdargs); llvm::cl::ParseCommandLineOptions(argc, argv); cpp2rust::SetVerbose(Verbose); if (CcFile.empty() && BuildDir.empty()) { llvm::errs() << "ERROR: please provide either --file or --dir\n"; return EXIT_FAILURE; } if (!CcFile.empty() && !BuildDir.empty()) { llvm::errs() << "ERROR: please provide only one of --file or --dir\n"; return EXIT_FAILURE; } if (!BuildDir.empty() && !CXXFlags.empty()) { llvm::errs() << "ERROR: can't combine --dir with --cxxflags\n"; return EXIT_FAILURE; } auto model = cpp2rust::Model::kRefCount; if (Model == "refcount") { // ok } else if (Model == "unsafe") { model = cpp2rust::Model::kUnsafe; } else { llvm::errs() << "ERROR: unknown model: " << Model << '\n'; return EXIT_FAILURE; } std::string cc_code; if (!CcFile.empty()) { std::ifstream file(CcFile); if (!file) { llvm::errs() << "ERROR: failed to open " << CcFile << '\n'; return EXIT_FAILURE; } cc_code = {std::istreambuf_iterator(file), std::istreambuf_iterator()}; if (cc_code.empty()) { llvm::errs() << "ERROR: empty source file\n"; return EXIT_FAILURE; } } std::vector<:string_view> cxx_flags(CXXFlags.begin(), CXXFlags.end()); if (RulesDir.empty() && !ResolveRulesDir()) { llvm::errs() << "ERROR: rules directory not found. " "Please specify one with --rules\n"; return EXIT_FAILURE; } auto rs_code = BuildDir.empty() ? cpp2rust::TranspileSrc(cc_code, model, cxx_flags, RulesDir, CcFile) : cpp2rust::TranspileDir(BuildDir, model, RulesDir); if (rs_code.empty()) { llvm::errs() << "ERROR: empty output file\n"; return EXIT_FAILURE; } std::ofstream file(RsFile); if (!file) { llvm::errs() << "ERROR: failed to open " << RsFile << '\n'; return EXIT_FAILURE; } file << rs_code; file.close(); // call rustfmt. std::string rustfmt_command = "rustfmt +" RUST_STABLE_VERSION " --edition 2024 " + RsFile; if (std::system(rustfmt_command.c_str()) != 0) { llvm::errs() << "ERROR: failed to run rustfmt\n"; return EXIT_FAILURE; } return EXIT_SUCCESS; }