mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-24 20:12:07 +00:00
Use colored/styled text output for diagnostics and usage info (#1775)
This commit is contained in:
@@ -72,7 +72,7 @@ bool charmap_ForEach(
|
||||
});
|
||||
|
||||
mapFunc(charmap.name);
|
||||
for (auto [nodeIdx, mapping] : mappings) {
|
||||
for (auto const &[nodeIdx, mapping] : mappings) {
|
||||
charFunc(mapping, charmap.nodes[nodeIdx].value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "helpers.hpp"
|
||||
#include "linkdefs.hpp"
|
||||
#include "platform.hpp" // S_ISDIR (stat macro)
|
||||
#include "style.hpp"
|
||||
#include "verbosity.hpp"
|
||||
|
||||
#include "asm/lexer.hpp"
|
||||
@@ -63,19 +64,27 @@ std::string const &FileStackNode::dump(uint32_t curLineNo) const {
|
||||
if (std::holds_alternative<std::vector<uint32_t>>(data)) {
|
||||
assume(parent); // REPT nodes use their parent's name
|
||||
std::string const &lastName = parent->dump(lineNo);
|
||||
style_Set(stderr, STYLE_CYAN, false);
|
||||
fputs(" -> ", stderr);
|
||||
style_Set(stderr, STYLE_CYAN, true);
|
||||
fputs(lastName.c_str(), stderr);
|
||||
fputs(reptChain().c_str(), stderr);
|
||||
style_Set(stderr, STYLE_CYAN, false);
|
||||
fprintf(stderr, "(%" PRIu32 ")", curLineNo);
|
||||
style_Reset(stderr);
|
||||
return lastName;
|
||||
} else {
|
||||
if (parent) {
|
||||
parent->dump(lineNo);
|
||||
style_Set(stderr, STYLE_CYAN, false);
|
||||
fputs(" -> ", stderr);
|
||||
}
|
||||
std::string const &nodeName = name();
|
||||
style_Set(stderr, STYLE_CYAN, true);
|
||||
fputs(nodeName.c_str(), stderr);
|
||||
style_Set(stderr, STYLE_CYAN, false);
|
||||
fprintf(stderr, "(%" PRIu32 ")", curLineNo);
|
||||
style_Reset(stderr);
|
||||
return nodeName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <unordered_map>
|
||||
|
||||
#include "helpers.hpp"
|
||||
#include "style.hpp"
|
||||
#include "util.hpp"
|
||||
#include "verbosity.hpp"
|
||||
|
||||
@@ -845,9 +846,15 @@ void lexer_DumpStringExpansions() {
|
||||
for (Expansion &exp : lexerState->expansions) {
|
||||
// Only register EQUS expansions, not string args
|
||||
if (exp.name) {
|
||||
fprintf(stderr, "while expanding symbol \"%s\"\n", exp.name->c_str());
|
||||
style_Set(stderr, STYLE_CYAN, false);
|
||||
fputs("while expanding symbol \"", stderr);
|
||||
style_Set(stderr, STYLE_CYAN, true);
|
||||
fputs(exp.name->c_str(), stderr);
|
||||
style_Set(stderr, STYLE_CYAN, false);
|
||||
fputs("\"\n", stderr);
|
||||
}
|
||||
}
|
||||
style_Reset(stderr);
|
||||
}
|
||||
|
||||
// Functions to discard non-tokenized characters
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "extern/getopt.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "parser.hpp" // Generated from parser.y
|
||||
#include "style.hpp"
|
||||
#include "usage.hpp"
|
||||
#include "util.hpp" // UpperMap
|
||||
#include "verbosity.hpp"
|
||||
@@ -36,7 +37,7 @@ static std::unordered_map<std::string, std::vector<StateFeature>> stateFileSpecs
|
||||
static char const *optstring = "b:D:Eg:hI:M:o:P:p:Q:r:s:VvW:wX:";
|
||||
|
||||
// Variables for the long-only options
|
||||
static int depType; // Variants of `-M`
|
||||
static int longOpt; // `--color` and variants of `-M`
|
||||
|
||||
// Equivalent long options
|
||||
// Please keep in the same order as short opts.
|
||||
@@ -53,11 +54,6 @@ static option const longopts[] = {
|
||||
{"help", no_argument, nullptr, 'h'},
|
||||
{"include", required_argument, nullptr, 'I'},
|
||||
{"dependfile", required_argument, nullptr, 'M'},
|
||||
{"MC", no_argument, &depType, 'C'},
|
||||
{"MG", no_argument, &depType, 'G'},
|
||||
{"MP", no_argument, &depType, 'P'},
|
||||
{"MQ", required_argument, &depType, 'Q'},
|
||||
{"MT", required_argument, &depType, 'T'},
|
||||
{"output", required_argument, nullptr, 'o'},
|
||||
{"preinclude", required_argument, nullptr, 'P'},
|
||||
{"pad-value", required_argument, nullptr, 'p'},
|
||||
@@ -68,27 +64,34 @@ static option const longopts[] = {
|
||||
{"verbose", no_argument, nullptr, 'v'},
|
||||
{"warning", required_argument, nullptr, 'W'},
|
||||
{"max-errors", required_argument, nullptr, 'X'},
|
||||
{nullptr, no_argument, nullptr, 0 }
|
||||
{"color", required_argument, &longOpt, 'c'},
|
||||
{"MC", no_argument, &longOpt, 'C'},
|
||||
{"MG", no_argument, &longOpt, 'G'},
|
||||
{"MP", no_argument, &longOpt, 'P'},
|
||||
{"MQ", required_argument, &longOpt, 'Q'},
|
||||
{"MT", required_argument, &longOpt, 'T'},
|
||||
{nullptr, no_argument, nullptr, 0 },
|
||||
};
|
||||
|
||||
// clang-format off: long string literal
|
||||
static Usage usage(
|
||||
"Usage: rgbasm [-EhVvw] [-b chars] [-D name[=value]] [-g chars] [-I path]\n"
|
||||
" [-M depend_file] [-MC] [-MG] [-MP] [-MT target_file] [-MQ target_file]\n"
|
||||
" [-o out_file] [-P include_file] [-p pad_value] [-Q precision]\n"
|
||||
" [-r depth] [-s features:state_file] [-W warning] [-X max_errors]\n"
|
||||
" <file>\n"
|
||||
"Useful options:\n"
|
||||
" -E, --export-all export all labels\n"
|
||||
" -M, --dependfile <path> set the output dependency file\n"
|
||||
" -o, --output <path> set the output object file\n"
|
||||
" -p, --pad-value <value> set the value to use for `ds'\n"
|
||||
" -s, --state <features>:<path> set an output state file\n"
|
||||
" -V, --version print RGBASM version and exit\n"
|
||||
" -W, --warning <warning> enable or disable warnings\n"
|
||||
"\n"
|
||||
"For help, use `man rgbasm' or go to https://rgbds.gbdev.io/docs/\n"
|
||||
);
|
||||
// clang-format off: nested initializers
|
||||
static Usage usage = {
|
||||
.name = "rgbasm",
|
||||
.flags = {
|
||||
"[-EhVvw]", "[-b chars]", "[-D name[=value]]", "[-g chars]", "[-I path]",
|
||||
"[-M depend_file]", "[-MC]", "[-MG]", "[-MP]", "[-MT target_file]", "[-MQ target_file]",
|
||||
"[-o out_file]", "[-P include_file]", "[-p pad_value]", "[-Q precision]", "[-r depth]",
|
||||
"[-s features:state_file]", "[-W warning]", "[-X max_errors]", "<file>",
|
||||
},
|
||||
.options = {
|
||||
{{"-E", "--export-all"}, {"export all labels"}},
|
||||
{{"-M", "--dependfile <path>"}, {"set the output dependency file"}},
|
||||
{{"-o", "--output <path>"}, {"set the output object file"}},
|
||||
{{"-p", "--pad-value <value>"}, {"set the value to use for `ds'"}},
|
||||
{{"-s", "--state <features>:<path>"}, {"set an output state file"}},
|
||||
{{"-V", "--version"}, {"print RGBASM version and exit"}},
|
||||
{{"-W", "--warning <warning>"}, {"enable or disable warnings"}},
|
||||
},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
// LCOV_EXCL_START
|
||||
@@ -160,7 +163,7 @@ static void verboseOutputConfig(int argc, char *argv[]) {
|
||||
"char",
|
||||
"macro",
|
||||
};
|
||||
for (auto [name, features] : stateFileSpecs) {
|
||||
for (auto const &[name, features] : stateFileSpecs) {
|
||||
fprintf(stderr, "\t - %s: ", name == "-" ? "<stdout>" : name.c_str());
|
||||
for (size_t i = 0; i < features.size(); ++i) {
|
||||
if (i > 0) {
|
||||
@@ -449,7 +452,17 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
// Long-only options
|
||||
case 0:
|
||||
switch (depType) {
|
||||
switch (longOpt) {
|
||||
case 'c':
|
||||
if (!strcasecmp(musl_optarg, "always")) {
|
||||
style_Enable(true);
|
||||
} else if (!strcasecmp(musl_optarg, "never")) {
|
||||
style_Enable(false);
|
||||
} else if (strcasecmp(musl_optarg, "auto")) {
|
||||
fatal("Invalid argument for option '--color'");
|
||||
}
|
||||
break;
|
||||
|
||||
case 'C':
|
||||
options.missingIncludeState = GEN_CONTINUE;
|
||||
break;
|
||||
@@ -465,7 +478,7 @@ int main(int argc, char *argv[]) {
|
||||
case 'Q':
|
||||
case 'T': {
|
||||
std::string newTarget = musl_optarg;
|
||||
if (depType == 'Q') {
|
||||
if (longOpt == 'Q') {
|
||||
newTarget = escapeMakeChars(newTarget);
|
||||
}
|
||||
if (!options.targetFileName.empty()) {
|
||||
@@ -549,7 +562,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
out_WriteObject();
|
||||
|
||||
for (auto [name, features] : stateFileSpecs) {
|
||||
for (auto const &[name, features] : stateFileSpecs) {
|
||||
out_WriteState(name, features);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "diagnostics.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "itertools.hpp"
|
||||
#include "style.hpp"
|
||||
|
||||
#include "asm/fstack.hpp"
|
||||
#include "asm/lexer.hpp"
|
||||
@@ -64,14 +65,24 @@ Diagnostics<WarningLevel, WarningID> warnings = {
|
||||
// clang-format on
|
||||
|
||||
static void printDiag(
|
||||
char const *fmt, va_list args, char const *type, char const *flagfmt, char const *flag
|
||||
char const *fmt,
|
||||
va_list args,
|
||||
char const *type,
|
||||
StyleColor color,
|
||||
char const *flagfmt,
|
||||
char const *flag
|
||||
) {
|
||||
fputs(type, stderr);
|
||||
fputs(": ", stderr);
|
||||
style_Set(stderr, color, true);
|
||||
fprintf(stderr, "%s: ", type);
|
||||
if (fstk_DumpCurrent()) {
|
||||
fprintf(stderr, flagfmt, flag);
|
||||
putc(':', stderr);
|
||||
if (flagfmt) {
|
||||
style_Set(stderr, color, true);
|
||||
fprintf(stderr, flagfmt, flag);
|
||||
}
|
||||
fputs("\n ", stderr);
|
||||
}
|
||||
style_Reset(stderr);
|
||||
vfprintf(stderr, fmt, args);
|
||||
putc('\n', stderr);
|
||||
|
||||
@@ -82,13 +93,16 @@ static void incrementErrors() {
|
||||
// This intentionally makes 0 act as "unlimited"
|
||||
warnings.incrementErrors();
|
||||
if (warnings.nbErrors == options.maxErrors) {
|
||||
style_Set(stderr, STYLE_RED, true);
|
||||
fprintf(
|
||||
stderr,
|
||||
"Assembly aborted after the maximum of %" PRIu64 " error%s! (configure with "
|
||||
"'-X/--max-errors')\n",
|
||||
"Assembly aborted after the maximum of %" PRIu64 " error%s!",
|
||||
warnings.nbErrors,
|
||||
warnings.nbErrors == 1 ? "" : "s"
|
||||
);
|
||||
style_Set(stderr, STYLE_RED, false);
|
||||
fputs(" (configure with '-X/--max-errors')\n", stderr);
|
||||
style_Reset(stderr);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
@@ -97,17 +111,19 @@ void error(char const *fmt, ...) {
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
printDiag(fmt, args, "error", ":", nullptr);
|
||||
printDiag(fmt, args, "error", STYLE_RED, nullptr, nullptr);
|
||||
va_end(args);
|
||||
|
||||
incrementErrors();
|
||||
}
|
||||
|
||||
void error(std::function<void()> callback) {
|
||||
style_Set(stderr, STYLE_RED, true);
|
||||
fputs("error: ", stderr);
|
||||
if (fstk_DumpCurrent()) {
|
||||
fputs(":\n ", stderr);
|
||||
}
|
||||
style_Reset(stderr);
|
||||
callback();
|
||||
putc('\n', stderr);
|
||||
lexer_DumpStringExpansions();
|
||||
@@ -120,7 +136,7 @@ void fatal(char const *fmt, ...) {
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
printDiag(fmt, args, "FATAL", ":", nullptr);
|
||||
printDiag(fmt, args, "FATAL", STYLE_RED, nullptr, nullptr);
|
||||
va_end(args);
|
||||
|
||||
exit(1);
|
||||
@@ -128,12 +144,14 @@ void fatal(char const *fmt, ...) {
|
||||
|
||||
void requireZeroErrors() {
|
||||
if (warnings.nbErrors != 0) {
|
||||
style_Set(stderr, STYLE_RED, true);
|
||||
fprintf(
|
||||
stderr,
|
||||
"Assembly aborted with %" PRIu64 " error%s!\n",
|
||||
warnings.nbErrors,
|
||||
warnings.nbErrors == 1 ? "" : "s"
|
||||
);
|
||||
style_Reset(stderr);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
@@ -149,11 +167,11 @@ void warning(WarningID id, char const *fmt, ...) {
|
||||
break;
|
||||
|
||||
case WarningBehavior::ENABLED:
|
||||
printDiag(fmt, args, "warning", ": [-W%s]", flag);
|
||||
printDiag(fmt, args, "warning", STYLE_YELLOW, " [-W%s]", flag);
|
||||
break;
|
||||
|
||||
case WarningBehavior::ERROR:
|
||||
printDiag(fmt, args, "error", ": [-Werror=%s]", flag);
|
||||
printDiag(fmt, args, "error", STYLE_RED, " [-Werror=%s]", flag);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user