mirror of
https://github.com/gbdev/rgbds.git
synced 2026-01-21 16:01:52 +00:00
Refactor warnings and errors (#1728)
* Remove `err` and `warn`, keep `errx` and `warnx`, using them in RGBGFX too * Separate RGBGFX and RGBLINK warnings/errors from main options * Separate `report` function into `error` and `fatal` messages * Implicit newlines for most RGBASM errors
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
#include "link/output.hpp"
|
||||
#include "link/section.hpp"
|
||||
#include "link/symbol.hpp"
|
||||
#include "link/warning.hpp"
|
||||
|
||||
struct MemoryLocation {
|
||||
uint16_t address;
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "link/patch.hpp"
|
||||
#include "link/section.hpp"
|
||||
#include "link/symbol.hpp"
|
||||
#include "link/warning.hpp"
|
||||
|
||||
bool isDmgMode; // -d
|
||||
char const *linkerScriptName; // -l
|
||||
@@ -44,8 +45,6 @@ bool disablePadding; // -x
|
||||
|
||||
FILE *linkerScript;
|
||||
|
||||
static uint32_t nbErrors = 0;
|
||||
|
||||
std::string const &FileStackNode::dump(uint32_t curLineNo) const {
|
||||
if (data.holds<std::vector<uint32_t>>()) {
|
||||
assume(parent); // REPT nodes use their parent's name
|
||||
@@ -69,72 +68,6 @@ std::string const &FileStackNode::dump(uint32_t curLineNo) const {
|
||||
}
|
||||
}
|
||||
|
||||
void printDiag(
|
||||
char const *fmt, va_list args, char const *type, FileStackNode const *where, uint32_t lineNo
|
||||
) {
|
||||
fputs(type, stderr);
|
||||
fputs(": ", stderr);
|
||||
if (where) {
|
||||
where->dump(lineNo);
|
||||
fputs(": ", stderr);
|
||||
}
|
||||
vfprintf(stderr, fmt, args);
|
||||
putc('\n', stderr);
|
||||
}
|
||||
|
||||
void warning(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...) {
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
printDiag(fmt, args, "warning", where, lineNo);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void error(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...) {
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
printDiag(fmt, args, "error", where, lineNo);
|
||||
va_end(args);
|
||||
|
||||
if (nbErrors != UINT32_MAX) {
|
||||
nbErrors++;
|
||||
}
|
||||
}
|
||||
|
||||
[[gnu::format(printf, 2, 3)]]
|
||||
void argErr(char flag, char const *fmt, ...) {
|
||||
va_list args;
|
||||
|
||||
fprintf(stderr, "error: Invalid argument for option '%c': ", flag);
|
||||
va_start(args, fmt);
|
||||
vfprintf(stderr, fmt, args);
|
||||
va_end(args);
|
||||
putc('\n', stderr);
|
||||
|
||||
if (nbErrors != UINT32_MAX) {
|
||||
nbErrors++;
|
||||
}
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
void fatal(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...) {
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
printDiag(fmt, args, "FATAL", where, lineNo);
|
||||
va_end(args);
|
||||
|
||||
if (nbErrors != UINT32_MAX) {
|
||||
nbErrors++;
|
||||
}
|
||||
|
||||
fprintf(
|
||||
stderr, "Linking aborted after %" PRIu32 " error%s\n", nbErrors, nbErrors == 1 ? "" : "s"
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Short options
|
||||
static char const *optstring = "dhl:m:Mn:O:o:p:S:tVvwx";
|
||||
|
||||
@@ -185,6 +118,19 @@ static void printUsage() {
|
||||
}
|
||||
// LCOV_EXCL_STOP
|
||||
|
||||
[[gnu::format(printf, 1, 2), noreturn]]
|
||||
static void fatalWithUsage(char const *fmt, ...) {
|
||||
va_list ap;
|
||||
fputs("FATAL: ", stderr);
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
putc('\n', stderr);
|
||||
|
||||
printUsage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
enum ScrambledRegion {
|
||||
SCRAMBLE_ROMX,
|
||||
SCRAMBLE_SRAM,
|
||||
@@ -327,14 +273,6 @@ next: // Can't `continue` a `for` loop with this nontrivial iteration logic
|
||||
}
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
void reportErrors() {
|
||||
fprintf(
|
||||
stderr, "Linking failed with %" PRIu32 " error%s\n", nbErrors, nbErrors == 1 ? "" : "s"
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
// Parse options
|
||||
for (int ch; (ch = musl_getopt_long_only(argc, argv, optstring, longopts, nullptr)) != -1;) {
|
||||
@@ -429,11 +367,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
// If no input files were specified, the user must have screwed up
|
||||
if (curArgIndex == argc) {
|
||||
fputs(
|
||||
"FATAL: Please specify an input file (pass `-` to read from standard input)\n", stderr
|
||||
);
|
||||
printUsage();
|
||||
exit(1);
|
||||
fatalWithUsage("Please specify an input file (pass `-` to read from standard input)");
|
||||
}
|
||||
|
||||
// Patch the size array depending on command-line options
|
||||
@@ -461,23 +395,17 @@ int main(int argc, char *argv[]) {
|
||||
script_ProcessScript(linkerScriptName);
|
||||
|
||||
// If the linker script produced any errors, some sections may be in an invalid state
|
||||
if (nbErrors != 0) {
|
||||
reportErrors();
|
||||
}
|
||||
requireZeroErrors();
|
||||
}
|
||||
|
||||
// then process them,
|
||||
sect_DoSanityChecks();
|
||||
if (nbErrors != 0) {
|
||||
reportErrors();
|
||||
}
|
||||
requireZeroErrors();
|
||||
assign_AssignSections();
|
||||
patch_CheckAssertions();
|
||||
|
||||
// and finally output the result.
|
||||
patch_ApplyPatches();
|
||||
if (nbErrors != 0) {
|
||||
reportErrors();
|
||||
}
|
||||
requireZeroErrors();
|
||||
out_WriteFiles();
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "link/sdas_obj.hpp"
|
||||
#include "link/section.hpp"
|
||||
#include "link/symbol.hpp"
|
||||
#include "link/warning.hpp"
|
||||
|
||||
static std::deque<std::vector<Symbol>> symbolLists;
|
||||
static std::vector<std::vector<FileStackNode>> nodes;
|
||||
@@ -445,7 +446,7 @@ void obj_ReadFile(char const *fileName, unsigned int fileID) {
|
||||
file = stdin;
|
||||
}
|
||||
if (!file) {
|
||||
err("Failed to open file \"%s\"", fileName);
|
||||
errx("Failed to open file \"%s\": %s", fileName, strerror(errno));
|
||||
}
|
||||
Defer closeFile{[&] { fclose(file); }};
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <deque>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -20,6 +21,7 @@
|
||||
#include "link/main.hpp"
|
||||
#include "link/section.hpp"
|
||||
#include "link/symbol.hpp"
|
||||
#include "link/warning.hpp"
|
||||
|
||||
static constexpr size_t BANK_SIZE = 0x4000;
|
||||
|
||||
@@ -211,7 +213,7 @@ static void writeROM() {
|
||||
outputFile = stdout;
|
||||
}
|
||||
if (!outputFile) {
|
||||
err("Failed to open output file \"%s\"", outputFileName);
|
||||
errx("Failed to open output file \"%s\": %s", outputFileName, strerror(errno));
|
||||
}
|
||||
}
|
||||
Defer closeOutputFile{[&] {
|
||||
@@ -229,7 +231,7 @@ static void writeROM() {
|
||||
overlayFile = stdin;
|
||||
}
|
||||
if (!overlayFile) {
|
||||
err("Failed to open overlay file \"%s\"", overlayFileName);
|
||||
errx("Failed to open overlay file \"%s\": %s", overlayFileName, strerror(errno));
|
||||
}
|
||||
}
|
||||
Defer closeOverlayFile{[&] {
|
||||
@@ -572,7 +574,7 @@ static void writeSym() {
|
||||
symFile = stdout;
|
||||
}
|
||||
if (!symFile) {
|
||||
err("Failed to open sym file \"%s\"", symFileName);
|
||||
errx("Failed to open sym file \"%s\": %s", symFileName, strerror(errno));
|
||||
}
|
||||
Defer closeSymFile{[&] { fclose(symFile); }};
|
||||
|
||||
@@ -623,7 +625,7 @@ static void writeMap() {
|
||||
mapFile = stdout;
|
||||
}
|
||||
if (!mapFile) {
|
||||
err("Failed to open map file \"%s\"", mapFileName);
|
||||
errx("Failed to open map file \"%s\": %s", mapFileName, strerror(errno));
|
||||
}
|
||||
Defer closeMapFile{[&] { fclose(mapFile); }};
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "link/main.hpp"
|
||||
#include "link/section.hpp"
|
||||
#include "link/symbol.hpp"
|
||||
#include "link/warning.hpp"
|
||||
|
||||
std::deque<Assertion> assertions;
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
#include "link/main.hpp"
|
||||
#include "link/section.hpp"
|
||||
#include "link/warning.hpp"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "link/main.hpp"
|
||||
#include "link/section.hpp"
|
||||
#include "link/symbol.hpp"
|
||||
#include "link/warning.hpp"
|
||||
|
||||
enum NumberType {
|
||||
HEX = 16, // X
|
||||
@@ -307,7 +308,7 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
||||
getToken(nullptr, "'A' line is too short");
|
||||
tmp = parseNumber(where, lineNo, token, numberType);
|
||||
if (tmp & (1 << AREA_PAGING)) {
|
||||
fatal(&where, lineNo, "Internal error: paging is not supported");
|
||||
fatal(&where, lineNo, "Paging is not supported");
|
||||
}
|
||||
curSection->isAddressFixed = tmp & (1 << AREA_ISABS);
|
||||
curSection->isBankFixed = curSection->isAddressFixed;
|
||||
@@ -430,11 +431,8 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
||||
|| (symbolSection && !symbolSection->isAddressFixed)) {
|
||||
sym_AddSymbol(symbol); // This will error out
|
||||
} else if (otherValue != symbolValue) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"error: \"%s\" is defined as %" PRId32 " at ",
|
||||
symbol.name.c_str(),
|
||||
symbolValue
|
||||
errorNoDump(
|
||||
"\"%s\" is defined as %" PRId32 " at ", symbol.name.c_str(), symbolValue
|
||||
);
|
||||
symbol.src->dump(symbol.lineNo);
|
||||
fprintf(stderr, ", but as %" PRId32 " at ", otherValue);
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include "error.hpp"
|
||||
#include "helpers.hpp"
|
||||
|
||||
#include "link/warning.hpp"
|
||||
|
||||
std::vector<std::unique_ptr<Section>> sectionList;
|
||||
std::unordered_map<std::string, size_t> sectionMap; // Indexes into `sectionList`
|
||||
|
||||
@@ -22,9 +24,8 @@ void sect_ForEach(void (*callback)(Section &)) {
|
||||
static void checkAgainstFixedAddress(Section const &target, Section const &other, uint16_t org) {
|
||||
if (target.isAddressFixed) {
|
||||
if (target.org != org) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"error: Section \"%s\" is defined with address $%04" PRIx16 " at ",
|
||||
errorNoDump(
|
||||
"Section \"%s\" is defined with address $%04" PRIx16 " at ",
|
||||
target.name.c_str(),
|
||||
target.org
|
||||
);
|
||||
@@ -36,9 +37,8 @@ static void checkAgainstFixedAddress(Section const &target, Section const &other
|
||||
}
|
||||
} else if (target.isAlignFixed) {
|
||||
if ((org - target.alignOfs) & target.alignMask) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"error: Section \"%s\" is defined with %d-byte alignment (offset %" PRIu16 ") at ",
|
||||
errorNoDump(
|
||||
"Section \"%s\" is defined with %d-byte alignment (offset %" PRIu16 ") at ",
|
||||
target.name.c_str(),
|
||||
target.alignMask + 1,
|
||||
target.alignOfs
|
||||
@@ -55,9 +55,8 @@ static void checkAgainstFixedAddress(Section const &target, Section const &other
|
||||
static bool checkAgainstFixedAlign(Section const &target, Section const &other, int32_t ofs) {
|
||||
if (target.isAddressFixed) {
|
||||
if ((target.org - ofs) & other.alignMask) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"error: Section \"%s\" is defined with address $%04" PRIx16 " at ",
|
||||
errorNoDump(
|
||||
"Section \"%s\" is defined with address $%04" PRIx16 " at ",
|
||||
target.name.c_str(),
|
||||
target.org
|
||||
);
|
||||
@@ -75,9 +74,8 @@ static bool checkAgainstFixedAlign(Section const &target, Section const &other,
|
||||
return false;
|
||||
} else if (target.isAlignFixed
|
||||
&& (other.alignMask & target.alignOfs) != (target.alignMask & ofs)) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"error: Section \"%s\" is defined with %d-byte alignment (offset %" PRIu16 ") at ",
|
||||
errorNoDump(
|
||||
"Section \"%s\" is defined with %d-byte alignment (offset %" PRIu16 ") at ",
|
||||
target.name.c_str(),
|
||||
target.alignMask + 1,
|
||||
target.alignOfs
|
||||
@@ -131,9 +129,8 @@ static void checkFragmentCompat(Section &target, Section &other) {
|
||||
|
||||
static void mergeSections(Section &target, std::unique_ptr<Section> &&other) {
|
||||
if (target.modifier != other->modifier) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"error: Section \"%s\" is defined as SECTION %s at ",
|
||||
errorNoDump(
|
||||
"Section \"%s\" is defined as SECTION %s at ",
|
||||
target.name.c_str(),
|
||||
sectionModNames[target.modifier]
|
||||
);
|
||||
@@ -143,16 +140,15 @@ static void mergeSections(Section &target, std::unique_ptr<Section> &&other) {
|
||||
putc('\n', stderr);
|
||||
exit(1);
|
||||
} else if (other->modifier == SECTION_NORMAL) {
|
||||
fprintf(stderr, "error: Section \"%s\" is defined at ", target.name.c_str());
|
||||
errorNoDump("Section \"%s\" is defined at ", target.name.c_str());
|
||||
target.src->dump(target.lineNo);
|
||||
fputs(", but also at ", stderr);
|
||||
other->src->dump(other->lineNo);
|
||||
putc('\n', stderr);
|
||||
exit(1);
|
||||
} else if (target.type != other->type) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"error: Section \"%s\" is defined with type %s at ",
|
||||
errorNoDump(
|
||||
"Section \"%s\" is defined with type %s at ",
|
||||
target.name.c_str(),
|
||||
sectionTypeInfo[target.type].name.c_str()
|
||||
);
|
||||
@@ -168,9 +164,8 @@ static void mergeSections(Section &target, std::unique_ptr<Section> &&other) {
|
||||
target.isBankFixed = true;
|
||||
target.bank = other->bank;
|
||||
} else if (target.bank != other->bank) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"error: Section \"%s\" is defined with bank %" PRIu32 " at ",
|
||||
errorNoDump(
|
||||
"Section \"%s\" is defined with bank %" PRIu32 " at ",
|
||||
target.name.c_str(),
|
||||
target.bank
|
||||
);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "link/main.hpp"
|
||||
#include "link/section.hpp"
|
||||
#include "link/warning.hpp"
|
||||
|
||||
std::unordered_map<std::string, Symbol *> symbols;
|
||||
std::unordered_map<std::string, std::vector<Symbol *>> localSymbols;
|
||||
@@ -36,7 +37,7 @@ void sym_AddSymbol(Symbol &symbol) {
|
||||
|
||||
// Check if the symbol already exists with a different value
|
||||
if (other && !(symValue && otherValue && *symValue == *otherValue)) {
|
||||
fprintf(stderr, "error: \"%s\" is defined as ", symbol.name.c_str());
|
||||
errorNoDump("\"%s\" is defined as ", symbol.name.c_str());
|
||||
if (symValue) {
|
||||
fprintf(stderr, "%" PRId32, *symValue);
|
||||
} else {
|
||||
|
||||
92
src/link/warning.cpp
Normal file
92
src/link/warning.cpp
Normal file
@@ -0,0 +1,92 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "link/warning.hpp"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "link/main.hpp"
|
||||
|
||||
static uint32_t nbErrors = 0;
|
||||
|
||||
static void printDiag(
|
||||
char const *fmt, va_list args, char const *type, FileStackNode const *where, uint32_t lineNo
|
||||
) {
|
||||
fputs(type, stderr);
|
||||
fputs(": ", stderr);
|
||||
if (where) {
|
||||
where->dump(lineNo);
|
||||
fputs(": ", stderr);
|
||||
}
|
||||
vfprintf(stderr, fmt, args);
|
||||
putc('\n', stderr);
|
||||
}
|
||||
|
||||
void warning(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
printDiag(fmt, args, "warning", where, lineNo);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void error(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
printDiag(fmt, args, "error", where, lineNo);
|
||||
va_end(args);
|
||||
|
||||
if (nbErrors != UINT32_MAX) {
|
||||
nbErrors++;
|
||||
}
|
||||
}
|
||||
|
||||
void errorNoDump(char const *fmt, ...) {
|
||||
va_list args;
|
||||
fputs("error: ", stderr);
|
||||
va_start(args, fmt);
|
||||
vfprintf(stderr, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if (nbErrors != UINT32_MAX) {
|
||||
nbErrors++;
|
||||
}
|
||||
}
|
||||
|
||||
void argErr(char flag, char const *fmt, ...) {
|
||||
va_list args;
|
||||
fprintf(stderr, "error: Invalid argument for option '%c': ", flag);
|
||||
va_start(args, fmt);
|
||||
vfprintf(stderr, fmt, args);
|
||||
va_end(args);
|
||||
putc('\n', stderr);
|
||||
|
||||
if (nbErrors != UINT32_MAX) {
|
||||
nbErrors++;
|
||||
}
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
void fatal(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
printDiag(fmt, args, "FATAL", where, lineNo);
|
||||
va_end(args);
|
||||
|
||||
if (nbErrors != UINT32_MAX) {
|
||||
nbErrors++;
|
||||
}
|
||||
|
||||
fprintf(
|
||||
stderr, "Linking aborted after %" PRIu32 " error%s\n", nbErrors, nbErrors == 1 ? "" : "s"
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void requireZeroErrors() {
|
||||
if (nbErrors != 0) {
|
||||
fprintf(
|
||||
stderr, "Linking failed with %" PRIu32 " error%s\n", nbErrors, nbErrors == 1 ? "" : "s"
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user