Improve file open error messages

Define open_(o|i)fstream_and_log_error in log.h to:
- quote 'filename'
- add error cause to easier troubleshoot
- use existing consistent string style
- easily allows OS specific message

Introduce it when file are opened and add it where error message
was missing.
This commit is contained in:
Marc Emery 2026-04-14 21:36:30 +02:00
parent 28fefe6172
commit 45093a336a
24 changed files with 81 additions and 89 deletions

View File

@ -609,13 +609,13 @@ int CommandHandler::executeMain(std::unique_ptr<Context> ctx)
try {
if (vm.count("json")) {
std::string filename = vm["json"].as<std::string>();
std::ifstream f(filename);
auto f = open_ifstream_and_log_error(filename, "JSON file");
if (!parse_json(f, filename, w.getContext()))
log_error("Loading design failed.\n");
if (vm.count("sdc")) {
std::string sdc_filename = vm["sdc"].as<std::string>();
std::ifstream sdc_stream(sdc_filename);
auto sdc_stream = open_ifstream_and_log_error(sdc_filename, "SDC file");
w.getContext()->read_sdc(sdc_stream);
}
@ -635,13 +635,14 @@ int CommandHandler::executeMain(std::unique_ptr<Context> ctx)
#endif
if (vm.count("json")) {
std::string filename = vm["json"].as<std::string>();
std::ifstream f(filename);
auto f = open_ifstream_and_log_error(filename, "'--json' file");
if (!parse_json(f, filename, ctx.get()))
log_error("Loading design failed.\n");
if (vm.count("sdc")) {
std::string sdc_filename = vm["sdc"].as<std::string>();
std::ifstream sdc_stream(sdc_filename);
auto sdc_stream = open_ifstream_and_log_error(sdc_filename, "SDC file");
ctx->read_sdc(sdc_stream);
}
@ -703,24 +704,20 @@ int CommandHandler::executeMain(std::unique_ptr<Context> ctx)
if (vm.count("write")) {
std::string filename = vm["write"].as<std::string>();
std::ofstream f(filename);
auto f = open_ofstream_and_log_error(filename, "JSON '--write' file");
if (!write_json_file(f, filename, ctx.get()))
log_error("Saving design failed.\n");
}
if (vm.count("sdf")) {
std::string filename = vm["sdf"].as<std::string>();
std::ofstream f(filename);
if (!f)
log_error("Failed to open SDF file '%s' for writing.\n", filename.c_str());
auto f = open_ofstream_and_log_error(filename, "SDF file");
ctx->writeSDF(f, vm.count("sdf-cvc"));
}
if (vm.count("report")) {
std::string filename = vm["report"].as<std::string>();
std::ofstream f(filename);
if (!f)
log_error("Failed to open report file '%s' for writing.\n", filename.c_str());
auto f = open_ofstream_and_log_error(filename, "report file");
ctx->writeJsonReport(f);
}
@ -778,7 +775,7 @@ void CommandHandler::load_json(Context *ctx, std::string filename)
setupContext(ctx);
setupArchContext(ctx);
{
std::ifstream f(filename);
auto f = open_ifstream_and_log_error(filename, "JSON file");
if (!parse_json(f, filename, ctx))
log_error("Loading design failed.\n");
}

View File

@ -17,6 +17,7 @@
*
*/
#include <cerrno>
#include <list>
#include <map>
#include <set>
@ -187,6 +188,28 @@ void log_error(const char *format, ...)
logv_error(format, ap);
}
std::ifstream open_ifstream_and_log_error(std::string filename, const char *file_description)
{
std::ifstream file(filename);
if (!file.is_open()) {
log_error("Failed to open %s '%s': %s.\n", file_description, filename.c_str(),
std::error_code(errno, std::generic_category()).message().c_str());
}
return file;
}
std::ofstream open_ofstream_and_log_error(std::string filename, const char *file_description)
{
std::ofstream file(filename);
if (!file.is_open()) {
log_error("Failed to open %s '%s' for writing: %s.\n", file_description, filename.c_str(),
std::error_code(errno, std::generic_category()).message().c_str());
}
return file;
}
void log_break()
{
if (log_newline_count < 2)

View File

@ -20,8 +20,8 @@
#ifndef LOG_H
#define LOG_H
#include <fstream>
#include <functional>
#include <ostream>
#include <set>
#include <stdarg.h>
#include <stdio.h>
@ -30,6 +30,8 @@
#include "hashlib.h"
#include "nextpnr_namespaces.h"
#include <fstream>
NEXTPNR_NAMESPACE_BEGIN
typedef std::function<void(std::string)> log_write_type;
@ -88,6 +90,12 @@ static inline void log_assert_worker(bool cond, const char *expr, const char *fi
#define log_abort() log_error("Abort in %s:%d.\n", __FILE__, __LINE__)
/// open `filename`, if error log "Failed to open {file_description} '{filename}'" with cause
std::ifstream open_ifstream_and_log_error(std::string filename, const char *file_description);
/// open `filename`, if error log "Failed to open {file_description} '{filename}' for writing" with cause
std::ofstream open_ofstream_and_log_error(std::string filename, const char *file_description);
NEXTPNR_NAMESPACE_END
#endif

View File

@ -50,9 +50,7 @@ bool operator==(const PortRef &a, const PortRef &b) { return (a.cell == b.cell)
// Load a JSON file into a design
void parse_json_shim(std::string filename, Context &d)
{
std::ifstream inf(filename);
if (!inf)
throw std::runtime_error("failed to open file " + filename);
auto inf = open_ifstream_and_log_error(filename, "JSON file");
parse_json(inf, filename, &d);
}

View File

@ -145,7 +145,7 @@ struct SVGWriter
void Context::writeSVG(const std::string &filename, const std::string &flags) const
{
std::ofstream out(filename);
auto out = open_ofstream_and_log_error(filename, "SVG file");
SVGWriter(this, out)(flags);
}

View File

@ -21,6 +21,7 @@
#define STATIC_UTIL_H
#include <fstream>
#include "log.h"
#include "nextpnr_assertions.h"
#include "nextpnr_namespaces.h"
@ -105,6 +106,10 @@ struct FFTArray
void write_csv(const std::string &filename) const
{
std::ofstream out(filename);
if (!out.is_open()) {
log_error("Failed to open CSV file for writing '%s': %s.\n", filename.c_str(),
std::error_code(errno, std::generic_category()).message().c_str());
}
NPNR_ASSERT(out);
for (int y = 0; y < m_height; y++) {
for (int x = 0; x < m_width; x++) {

View File

@ -1718,26 +1718,19 @@ struct Router2
if (!cfg.heatmap.empty()) {
{
std::string filename(cfg.heatmap + "_congestion_by_wiretype_" + std::to_string(iter) + ".csv");
std::ofstream cong_map(filename);
if (!cong_map)
log_error("Failed to open congestion-by-wiretype heatmap %s for writing.\n", filename.c_str());
auto cong_map = open_ofstream_and_log_error(filename, "congestion-by-wiretype heatmap");
write_congestion_by_wiretype_heatmap(cong_map);
log_info(" wrote congestion-by-wiretype heatmap to %s.\n", filename.c_str());
}
{
std::string filename(cfg.heatmap + "_utilisation_by_wiretype_" + std::to_string(iter) + ".csv");
std::ofstream cong_map(filename);
if (!cong_map)
log_error("Failed to open utilisation-by-wiretype heatmap %s for writing.\n", filename.c_str());
auto cong_map = open_ofstream_and_log_error(filename, "utilisation-by-wiretype heatmap");
write_utilisation_by_wiretype_heatmap(cong_map);
log_info(" wrote utilisation-by-wiretype heatmap to %s.\n", filename.c_str());
}
{
std::string filename(cfg.heatmap + "_congestion_by_coordinate_" + std::to_string(iter) + ".csv");
std::ofstream cong_map(filename);
if (!cong_map)
log_error("Failed to open congestion-by-coordinate heatmap %s for writing.\n",
filename.c_str());
auto cong_map = open_ofstream_and_log_error(filename, "congestion-by-coordinate heatmap");
write_congestion_by_coordinate_heatmap(cong_map);
log_info(" wrote congestion-by-coordinate heatmap to %s.\n", filename.c_str());
}

View File

@ -1364,10 +1364,7 @@ struct ECP5Bitgen
void run(const std::string &base_config_file)
{
if (!base_config_file.empty()) {
std::ifstream config_file(base_config_file);
if (!config_file) {
log_error("failed to open base config file '%s'\n", base_config_file.c_str());
}
auto config_file = open_ifstream_and_log_error(base_config_file, "base config file");
config_file >> cc;
} else {
switch (ctx->args.type) {
@ -1599,7 +1596,7 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
// Configure chip
if (!text_config_file.empty()) {
std::ofstream out_config(text_config_file);
auto out_config = open_ofstream_and_log_error(text_config_file, "text config file");
out_config << bitgen.cc;
}
}

View File

@ -266,11 +266,9 @@ void ECP5CommandHandler::customAfterLoad(Context *ctx)
if (vm.count("lpf")) {
std::vector<std::string> files = vm["lpf"].as<std::vector<std::string>>();
for (const auto &filename : files) {
std::ifstream in(filename);
if (!in)
log_error("failed to open LPF file '%s'\n", filename.c_str());
auto in = open_ifstream_and_log_error(filename, "LPF file");
if (!ctx->apply_lpf(filename, in))
log_error("failed to parse LPF file '%s'\n", filename.c_str());
log_error("Failed to parse LPF file '%s'\n", filename.c_str());
}
for (auto &cell : ctx->cells) {

View File

@ -213,10 +213,7 @@ struct FabulousImpl : ViaductAPI
std::ifstream open_data_rel(const std::string &postfix)
{
const std::string filename(fab_root + postfix);
std::ifstream in(filename);
if (!in)
log_error("failed to open data file '%s' (is FAB_ROOT set correctly?)\n", filename.c_str());
return in;
return open_ifstream_and_log_error(filename, "data file (is FAB_ROOT set correctly?)");
}
std::string fab_root;

View File

@ -537,10 +537,7 @@ struct FABulousDesignConstraints
void apply_constraints()
{
std::ifstream in(filename);
if (!in) {
log_error("failed to open constraint file\n");
}
auto in = open_ifstream_and_log_error(filename, "constraint file");
std::string line;
std::string accumulated_line;

View File

@ -519,9 +519,7 @@ struct BitstreamBackend
void GateMateImpl::write_bitstream(const std::string &device, const std::string &filename)
{
std::ofstream out(filename);
if (!out)
log_error("Failed to open file '%s' for writing (%s).\n", filename.c_str(), strerror(errno));
auto out = open_ofstream_and_log_error(filename, "bitstream file");
BitstreamBackend be(ctx, this, device, out);
be.write_bitstream();

View File

@ -411,9 +411,7 @@ struct GateMateCCFReader
void GateMateImpl::parse_ccf(const std::string &filename)
{
std::ifstream in(filename);
if (!in)
log_error("Failed to open CCF file '%s'.\n", filename.c_str());
auto in = open_ifstream_and_log_error(filename, "CCF file");
GateMateCCFReader reader(ctx, this, in);
reader.run();
}

View File

@ -267,10 +267,8 @@ void GowinImpl::pack()
{
if (ctx->settings.count(ctx->id("cst.filename"))) {
std::string filename = ctx->settings[ctx->id("cst.filename")].as_string();
std::ifstream in(filename);
if (!in) {
log_error("failed to open CST file '%s'\n", filename.c_str());
}
auto in = open_ifstream_and_log_error(filename, "CST file");
if (!gowin_apply_constraints(ctx, in)) {
log_error("failed to parse CST file '%s'\n", filename.c_str());
}

View File

@ -694,9 +694,7 @@ struct BitstreamJsonBackend
void NgUltraImpl::write_bitstream_json(const std::string &filename)
{
std::ofstream out(filename);
if (!out)
log_error("failed to open file %s for writing (%s)\n", filename.c_str(), strerror(errno));
auto out = open_ofstream_and_log_error(filename, "bitstream json file");
BitstreamJsonBackend be(ctx, this, out);
be.write_json();

View File

@ -37,9 +37,7 @@ NEXTPNR_NAMESPACE_BEGIN
void NgUltraImpl::parse_csv(const std::string &filename)
{
std::ifstream in(filename);
if (!in)
log_error("failed to open CSV file '%s'\n", filename.c_str());
auto in = open_ifstream_and_log_error(filename, "CSV file");
log_info("Parsing CSV file..\n");
std::string line;
std::string linebuf;

View File

@ -264,8 +264,7 @@ struct FasmBackend
boost::erase_all(loc, "_T1");
boost::replace_all(loc, "IOI_OLOGIC", "OLOGIC_Y");
// the replacements transformed it into : LIOI3_X0Y73.OLOGIC_Y1
out << loc << "."
<< "ZINV_T1" << std::endl;
out << loc << "." << "ZINV_T1" << std::endl;
}
}
return;
@ -1822,9 +1821,7 @@ struct FasmBackend
void XilinxImpl::write_fasm(const std::string &filename)
{
std::ofstream out(filename);
if (!out)
log_error("failed to open file %s for writing (%s)\n", filename.c_str(), strerror(errno));
auto out = open_ofstream_and_log_error(filename, "FASM file");
FasmBackend be(this->ctx, this, out);
be.write_fasm();

View File

@ -36,9 +36,7 @@ NEXTPNR_NAMESPACE_BEGIN
void XilinxImpl::parse_xdc(const std::string &filename)
{
std::ifstream in(filename);
if (!in)
log_error("failed to open XDC file '%s'\n", filename.c_str());
auto in = open_ifstream_and_log_error(filename, "XDC file");
log_info("Parsing XDC file...\n");
std::string line;
std::string linebuf;

View File

@ -32,9 +32,8 @@ NEXTPNR_NAMESPACE_BEGIN
static void write_bitstream(const Context *ctx, std::string asc_file)
{
std::ofstream out(asc_file);
if (!out)
log_error("Failed to open output file %s\n", asc_file.c_str());
auto out = open_ofstream_and_log_error(asc_file, "output file");
write_asc(ctx, out);
}

View File

@ -98,7 +98,7 @@ void Ice40CommandHandler::customAfterLoad(Context *ctx)
{
if (vm.count("pcf")) {
std::string filename = vm["pcf"].as<std::string>();
std::ifstream pcf(filename);
auto pcf = open_ifstream_and_log_error(filename, "PCF file");
if (!apply_pcf(ctx, filename, pcf))
log_error("Loading PCF failed.\n");
} else {
@ -109,7 +109,7 @@ void Ice40CommandHandler::customBitstream(Context *ctx)
{
if (vm.count("asc")) {
std::string filename = vm["asc"].as<std::string>();
std::ofstream f(filename);
auto f = open_ofstream_and_log_error(filename, "ASC file");
write_asc(ctx, f);
}
}
@ -121,7 +121,7 @@ void Ice40CommandHandler::setupArchContext(Context *ctx)
if (vm.count("read")) {
std::string filename = vm["read"].as<std::string>();
std::ifstream f(filename);
auto f = open_ifstream_and_log_error(filename, "ASC file");
if (!read_asc(ctx, f))
log_error("Loading ASC failed.\n");
}

View File

@ -797,7 +797,7 @@ void write_bitstream(Context *ctx, std::string text_config_file)
// Configure chip
if (!text_config_file.empty()) {
std::ofstream out_config(text_config_file);
auto out_config = open_ofstream_and_log_error(text_config_file, "text config file");
out_config << bitgen.cc;
}
}

View File

@ -89,9 +89,7 @@ void MachXO2CommandHandler::customAfterLoad(Context *ctx)
if (vm.count("lpf")) {
std::vector<std::string> files = vm["lpf"].as<std::vector<std::string>>();
for (const auto &filename : files) {
std::ifstream in(filename);
if (!in)
log_error("failed to open LPF file '%s'\n", filename.c_str());
auto in = open_ifstream_and_log_error(filename, "LPF file");
if (!ctx->apply_lpf(filename, in))
log_error("failed to parse LPF file '%s'\n", filename.c_str());
}

View File

@ -17,6 +17,7 @@
*
*/
#include <cerrno>
#include <fstream>
#include "command.h"
#include "design_utils.h"
@ -62,8 +63,10 @@ void MistralCommandHandler::customBitstream(Context *ctx)
ctx->cyclonev->rbf_save(data);
std::ofstream out(filename, std::ios::binary);
if (!out)
log_error("Failed to open output RBF file %s.\n", filename.c_str());
if (!out.is_open()) {
log_error("Failed to open RBF file '%s' for writing: %s.\n", filename.c_str(),
std::error_code(errno, std::generic_category()).message().c_str());
}
out.write(reinterpret_cast<const char *>(data.data()), data.size());
}
}
@ -85,9 +88,7 @@ void MistralCommandHandler::customAfterLoad(Context *ctx)
{
if (vm.count("qsf")) {
std::string filename = vm["qsf"].as<std::string>();
std::ifstream in(filename);
if (!in)
log_error("Failed to open input QSF file %s.\n", filename.c_str());
auto in = open_ifstream_and_log_error(filename, "input QSF file");
ctx->read_qsf(in);
}
}

View File

@ -62,9 +62,7 @@ void NexusCommandHandler::customBitstream(Context *ctx)
{
if (vm.count("fasm")) {
std::string filename = vm["fasm"].as<std::string>();
std::ofstream out(filename);
if (!out)
log_error("Failed to open output FASM file %s.\n", filename.c_str());
auto out = open_ofstream_and_log_error(filename, "output FASM file");
ctx->write_fasm(out);
}
}
@ -101,9 +99,7 @@ void NexusCommandHandler::customAfterLoad(Context *ctx)
{
if (vm.count("pdc")) {
std::string filename = vm["pdc"].as<std::string>();
std::ifstream in(filename);
if (!in)
log_error("Failed to open input PDC file %s.\n", filename.c_str());
auto in = open_ifstream_and_log_error(filename, "input PDC file");
ctx->read_pdc(in);
}
}