Implement -X/--max-errors for RGBASM (#1262)

Co-authored-by: Eldred Habert <me@eldred.fr>
This commit is contained in:
Rangi
2023-12-07 05:42:47 -05:00
committed by GitHub
parent 1fa289f2ee
commit 34b2543c8b
9 changed files with 47 additions and 5 deletions

View File

@@ -43,6 +43,7 @@ _rgbasm_completions() {
[Q]="q-precision:unk" [Q]="q-precision:unk"
[r]="recursion-depth:unk" [r]="recursion-depth:unk"
[W]="warning:warning" [W]="warning:warning"
[X]="max-errors:unk"
) )
# Parse command-line up to current word # Parse command-line up to current word
local opt_ena=true local opt_ena=true

View File

@@ -60,6 +60,7 @@ local args=(
'(-Q --q-precision)'{-Q,--q-precision}'+[Set fixed-point precision]:precision:' '(-Q --q-precision)'{-Q,--q-precision}'+[Set fixed-point precision]:precision:'
'(-r --recursion-depth)'{-r,--recursion-depth}'+[Set maximum recursion depth]:depth:' '(-r --recursion-depth)'{-r,--recursion-depth}'+[Set maximum recursion depth]:depth:'
'(-W --warning)'{-W,--warning}'+[Toggle warning flags]:warning flag:_rgbasm_warnings' '(-W --warning)'{-W,--warning}'+[Toggle warning flags]:warning flag:_rgbasm_warnings'
'(-X --max-errors)'{-X,--max-errors}'+[Set maximum errors before aborting]:maximum errors:'
":assembly sources:_files -g '*.asm'" ":assembly sources:_files -g '*.asm'"
) )

View File

@@ -5,7 +5,7 @@
#include "helpers.hpp" #include "helpers.hpp"
extern unsigned int nbErrors; extern unsigned int nbErrors, maxErrors;
enum WarningState { enum WarningState {
WARNING_DEFAULT, WARNING_DEFAULT,

View File

@@ -24,6 +24,7 @@
.Op Fl Q Ar fix_precision .Op Fl Q Ar fix_precision
.Op Fl r Ar recursion_depth .Op Fl r Ar recursion_depth
.Op Fl W Ar warning .Op Fl W Ar warning
.Op Fl X Ar max_errors
.Ar asmfile .Ar asmfile
.Sh DESCRIPTION .Sh DESCRIPTION
The The
@@ -176,6 +177,13 @@ See the
section for a list of warnings. section for a list of warnings.
.It Fl w .It Fl w
Disable all warning output, even when turned into errors. Disable all warning output, even when turned into errors.
.It Fl X Ar max_errors , Fl \-max-errors Ar max_errors
If more than this number of errors (not warnings) occur, then abort the assembly process;
.Fl X 0
disables this behavior.
The default is 100 if
.Nm
is printing errors to a terminal, and 0 otherwise.
.El .El
.Sh DIAGNOSTICS .Sh DIAGNOSTICS
Warnings are diagnostic messages that indicate possibly erroneous behavior that does not necessarily compromise the assembling process. Warnings are diagnostic messages that indicate possibly erroneous behavior that does not necessarily compromise the assembling process.

View File

@@ -4,6 +4,7 @@
#include <errno.h> #include <errno.h>
#include <float.h> #include <float.h>
#include <inttypes.h> #include <inttypes.h>
#include <limits.h>
#include <math.h> #include <math.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
@@ -83,7 +84,7 @@ static char *make_escape(char const *str)
} }
// Short options // Short options
static const char *optstring = "b:D:Eg:Hhi:I:LlM:o:P:p:Q:r:VvW:w"; static const char *optstring = "b:D:Eg:Hhi:I:LlM:o:P:p:Q:r:VvW:wX:";
// Variables for the long-only options // Variables for the long-only options
static int depType; // Variants of `-M` static int depType; // Variants of `-M`
@@ -110,6 +111,7 @@ static struct option const longopts[] = {
{ "MG", no_argument, &depType, 'G' }, { "MG", no_argument, &depType, 'G' },
{ "MP", no_argument, &depType, 'P' }, { "MP", no_argument, &depType, 'P' },
{ "MT", required_argument, &depType, 'T' }, { "MT", required_argument, &depType, 'T' },
{ "warning", required_argument, NULL, 'W' },
{ "MQ", required_argument, &depType, 'Q' }, { "MQ", required_argument, &depType, 'Q' },
{ "output", required_argument, NULL, 'o' }, { "output", required_argument, NULL, 'o' },
{ "preinclude", required_argument, NULL, 'P' }, { "preinclude", required_argument, NULL, 'P' },
@@ -119,6 +121,7 @@ static struct option const longopts[] = {
{ "version", no_argument, NULL, 'V' }, { "version", no_argument, NULL, 'V' },
{ "verbose", no_argument, NULL, 'v' }, { "verbose", no_argument, NULL, 'v' },
{ "warning", required_argument, NULL, 'W' }, { "warning", required_argument, NULL, 'W' },
{ "max-errors", required_argument, NULL, 'X' },
{ NULL, no_argument, NULL, 0 } { NULL, no_argument, NULL, 0 }
}; };
@@ -128,7 +131,7 @@ static void printUsage(void)
"Usage: rgbasm [-EHhLlVvw] [-b chars] [-D name[=value]] [-g chars] [-I path]\n" "Usage: rgbasm [-EHhLlVvw] [-b chars] [-D name[=value]] [-g chars] [-I path]\n"
" [-M depend_file] [-MG] [-MP] [-MT target_file] [-MQ target_file]\n" " [-M depend_file] [-MG] [-MP] [-MT target_file] [-MQ target_file]\n"
" [-o out_file] [-P include_file] [-p pad_value] [-Q precision]\n" " [-o out_file] [-P include_file] [-p pad_value] [-Q precision]\n"
" [-r depth] [-W warning] <file>\n" " [-r depth] [-W warning] [-X max_errors] <file>\n"
"Useful options:\n" "Useful options:\n"
" -E, --export-all export all labels\n" " -E, --export-all export all labels\n"
" -M, --dependfile <path> set the output dependency file\n" " -M, --dependfile <path> set the output dependency file\n"
@@ -171,6 +174,10 @@ int main(int argc, char *argv[])
char const *dependFileName = NULL; char const *dependFileName = NULL;
size_t targetFileNameLen = 0; size_t targetFileNameLen = 0;
// Maximum of 100 errors only applies if rgbasm is printing errors to a terminal.
if (isatty(STDERR_FILENO))
maxErrors = 100;
for (int ch; (ch = musl_getopt_long_only(argc, argv, optstring, longopts, NULL)) != -1;) { for (int ch; (ch = musl_getopt_long_only(argc, argv, optstring, longopts, NULL)) != -1;) {
switch (ch) { switch (ch) {
char *endptr; char *endptr;
@@ -316,6 +323,19 @@ int main(int argc, char *argv[])
warnings = false; warnings = false;
break; break;
unsigned int maxValue;
case 'X':
maxValue = strtoul(musl_optarg, &endptr, 0);
if (musl_optarg[0] == '\0' || *endptr != '\0')
errx("Invalid argument for option 'X'");
if (maxValue > UINT_MAX)
errx("Argument for option 'X' must be between 0 and %u", UINT_MAX);
maxErrors = maxValue;
break;
// Long-only options // Long-only options
case 0: case 0:
switch (depType) { switch (depType) {
@@ -399,8 +419,7 @@ int main(int argc, char *argv[])
sect_CheckUnionClosed(); sect_CheckUnionClosed();
if (nbErrors != 0) if (nbErrors != 0)
errx("Assembly aborted (%u error%s)!", nbErrors, errx("Assembly aborted (%u error%s)!", nbErrors, nbErrors == 1 ? "" : "s");
nbErrors == 1 ? "" : "s");
// If parse aborted due to missing an include, and `-MG` was given, exit normally // If parse aborted due to missing an include, and `-MG` was given, exit normally
if (failedOnMissingInclude) if (failedOnMissingInclude)

View File

@@ -16,6 +16,7 @@
#include "itertools.hpp" #include "itertools.hpp"
unsigned int nbErrors = 0; unsigned int nbErrors = 0;
unsigned int maxErrors = 0;
static const enum WarningState defaultWarnings[ARRAY_SIZE(warningStates)] = { static const enum WarningState defaultWarnings[ARRAY_SIZE(warningStates)] = {
AT(WARNING_ASSERT) WARNING_ENABLED, AT(WARNING_ASSERT) WARNING_ENABLED,
@@ -343,7 +344,12 @@ void error(char const *fmt, ...)
va_start(args, fmt); va_start(args, fmt);
printDiag(fmt, args, "error: ", ":\n ", NULL); printDiag(fmt, args, "error: ", ":\n ", NULL);
va_end(args); va_end(args);
// This intentionally makes 0 act as "unlimited" (or at least "limited to sizeof(unsigned)")
nbErrors++; nbErrors++;
if (nbErrors == maxErrors)
errx("The maximum of %u error%s was reached (configure with \"-X/--max-errors\"); assembly aborted!",
maxErrors, maxErrors == 1 ? "" : "s");
} }
[[noreturn]] void fatalerror(char const *fmt, ...) [[noreturn]] void fatalerror(char const *fmt, ...)

3
test/asm/max-errors.asm Normal file
View File

@@ -0,0 +1,3 @@
section "s", rom0
db 42
println @ ; causes an error before println occurs

3
test/asm/max-errors.err Normal file
View File

@@ -0,0 +1,3 @@
error: max-errors.asm(3):
Expected constant expression: PC is not constant at assembly time
error: The maximum of 1 error was reached (configure with "-X/--max-errors"); assembly aborted!

View File

@@ -0,0 +1 @@
-Weverything -X 1