aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author_ov4 <ov4@ov4.org>2026-10-21 14:06:54 +0000
committer_ov4 <ov4@ov4.org>2026-10-21 14:06:54 +0000
commit899c2af576124df2918fd2ae976434df0ebbae5b (patch)
treebb67a979eca30a6d447c2819576b81dc84741f17
parentb4ffb2d9498560557821714e2d4651279dd6817c (diff)
feat(driver): color diagnostics like gcc with -fdiagnostics-color
Add -fdiagnostics-color[=auto|always|never] and -fno-diagnostics-color. The default auto mode colors only a terminal stderr (TERM set and not dumb on Unix; a console that accepts VT sequences on Windows), so test harnesses, the desktop bundle and other pipes stay plain. Every diagnostic label now comes from cps::diagnosticPrefix, which emits gcc's default GCC_COLORS bytes: bold locus, bold red error/fatal error, bold magenta warning, bold cyan note. compilation terminated., runtime messages of the compiled program and lld output are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
-rw-r--r--AGENTS/ARCH/DRIVER.md10
-rw-r--r--doc/cpsc.1.md5
-rw-r--r--include/cps/Diagnostics.h14
-rw-r--r--lib/CodeGen/CodeGen.cc2
-rw-r--r--plugin/AGENTS/ARCH.md3
-rw-r--r--tools/driver/EmbeddedRunner.cc43
-rw-r--r--tools/driver/NativeCompiler.cc9
-rw-r--r--tools/driver/StandardLibrary.cc9
-rw-r--r--tools/driver/main.cc101
9 files changed, 151 insertions, 45 deletions
diff --git a/AGENTS/ARCH/DRIVER.md b/AGENTS/ARCH/DRIVER.md
index d571509..1b37d01 100644
--- a/AGENTS/ARCH/DRIVER.md
+++ b/AGENTS/ARCH/DRIVER.md
@@ -4,7 +4,8 @@ Driver behavior (`tools/driver/main.cc`). CodeGen-side unit and entry-point inva
[CONVENTIONS/EXCEPTIONS_AND_UNITS.md](../CONVENTIONS/EXCEPTIONS_AND_UNITS.md); diagnostic
style is in [CONVENTIONS/SYMBOLS_AND_ERRORS.md](../CONVENTIONS/SYMBOLS_AND_ERRORS.md).
-- One or more source files, with optional `-S`, `-o path`, `-r` and `-no-std`; options may appear among inputs.
+- One or more source files, with optional `-S`, `-o path`, `-r`, `-no-std` and
+ `-fdiagnostics-color[=auto|always|never]` / `-fno-diagnostics-color`; options may appear among inputs.
Each stream is parsed independently. INCLUDE paths are relative to the including file,
matched against command-line inputs with fs::equivalent. The driver rejects repeated
inputs and computes transitive symbol visibility; cycles do not copy statements.
@@ -36,7 +37,12 @@ style is in [CONVENTIONS/SYMBOLS_AND_ERRORS.md](../CONVENTIONS/SYMBOLS_AND_ERROR
`compilation terminated.` once on compilation failure, including argument errors.
Lexer/parser/codegen diagnostics use the gcc layout `<path>:<line>: error: <message>`
(Diagnostics.h), with the path as given on the command line; driver-level errors
- use `cpsc: error:` / `cpsc: fatal error:`.
+ use `cpsc: error:` / `cpsc: fatal error:`. Every label comes from
+ `cps::diagnosticPrefix`, which emits gcc's default colors when
+ `cps::DiagnosticColors` is set. The driver scans the color options right after
+ `-h`, before any other diagnostic; the last valid one wins, and `auto` requires a
+ terminal stderr (Unix: `TERM` set and not `dumb`; Windows: a console that accepts
+ VT sequences). `compilation terminated.` and lld's own output are not colored.
Execution-stage failures do not print that line. Check the exit code rather
than grepping stderr.
- Successful compilation overwrites the output, but paths aliasing any input source
diff --git a/doc/cpsc.1.md b/doc/cpsc.1.md
index b156d6f..6a81a8a 100644
--- a/doc/cpsc.1.md
+++ b/doc/cpsc.1.md
@@ -6,7 +6,7 @@ cpsc - compile and run CIE pseudocode
# SYNOPSIS
-**cpsc** [**-r**] [**-S**] [**-no-std**] [**-o** *output*] *source* [*source* ...]
+**cpsc** [**-r**] [**-S**] [**-no-std**] [**-fdiagnostics-color**[=*when*]] [**-o** *output*] *source* [*source* ...]
**cpsc** **-h**
@@ -39,6 +39,9 @@ The embedded standard library is loaded and linked automatically. Its std.cps im
**-no-std**
: Disable automatic standard-library loading and linking. Explicit INCLUDE and INCLUDE_OBJ inputs still work.
+**-fdiagnostics-color**[=*when*], **-fno-diagnostics-color**
+: Color compiler diagnostics like gcc: the `file:line:` or `cpsc:` prefix in bold, `error:` and `fatal error:` in bold red, `warning:` in bold magenta and `note:` in bold cyan. *when* is `auto` (the default), `always` or `never`; **-fdiagnostics-color** alone means `always` and **-fno-diagnostics-color** means `never`. The last such option wins. `auto` colors only when stderr is a terminal: on Unix/macOS `TERM` must also be set and not `dumb`; on Windows the console must accept ANSI escape sequences. Runtime diagnostics of the compiled program are never colored.
+
# NATIVE COMPILATION
Native compilation uses LLVM, the Clang driver and LLD embedded in `cpsc`. Required startup objects, runtime archives and system-library link inputs are embedded too and unpacked temporarily when linking. No external compiler, linker or development SDK is needed. Generate programs for the compiler's own operating system and CPU architecture; cross-compilation is not supported.
diff --git a/include/cps/Diagnostics.h b/include/cps/Diagnostics.h
index 3492f46..4bcb877 100644
--- a/include/cps/Diagnostics.h
+++ b/include/cps/Diagnostics.h
@@ -9,13 +9,25 @@ enum class Severity { Error, Warning, Note, Fatal };
// Path of the unit being diagnosed, as given on the command line.
inline std::string DiagnosticSource;
+// Set by the driver from -fdiagnostics-color.
+inline bool DiagnosticColors = false;
+
// gcc layout: "<locus>: <label>: "; an empty locus prints the label alone.
+// With colors, the bytes match gcc's default GCC_COLORS: bold locus
+// (including its colon), then the label in bold red/magenta/cyan.
inline std::string diagnosticPrefix(Severity S, const std::string &Locus = "cpsc") {
const char *Label = S == Severity::Error ? "error: "
: S == Severity::Warning ? "warning: "
: S == Severity::Note ? "note: "
: "fatal error: ";
- return (Locus.empty() ? "" : Locus + ": ") + Label;
+ if (!DiagnosticColors) return (Locus.empty() ? "" : Locus + ": ") + Label;
+ const char *Color = S == Severity::Warning ? "01;35"
+ : S == Severity::Note ? "01;36"
+ : "01;31";
+ auto Span = [](const char *SGR, const std::string &Text) {
+ return std::string("\33[") + SGR + "m\33[K" + Text + "\33[m\33[K";
+ };
+ return (Locus.empty() ? "" : Span("01", Locus + ":") + " ") + Span(Color, Label);
}
// Prints "<path>:<line>: <label>: <message>\n" (just "<path>:" when the line is
diff --git a/lib/CodeGen/CodeGen.cc b/lib/CodeGen/CodeGen.cc
index e184b19..7b58b5b 100644
--- a/lib/CodeGen/CodeGen.cc
+++ b/lib/CodeGen/CodeGen.cc
@@ -366,7 +366,7 @@ void CodeGen::compile(const std::vector<SourceUnit> &Sources) {
Builder->CreateRet(ConstantInt::get(Type::getInt32Ty(*TheContext), 1));
if (verifyModule(*TheModule, &errs())) {
- errs() << "fatal error: LLVM module verification failed\n";
+ errs() << cps::diagnosticPrefix(cps::Severity::Fatal, "") << "LLVM module verification failed\n";
HadError = true;
}
}
diff --git a/plugin/AGENTS/ARCH.md b/plugin/AGENTS/ARCH.md
index 0f585bb..eb97e63 100644
--- a/plugin/AGENTS/ARCH.md
+++ b/plugin/AGENTS/ARCH.md
@@ -70,7 +70,8 @@ profile does not change an existing terminal. Windows bash profiles and non-POSI
Unix shells are outside the supported terminal set.
Save cancellation starts nothing. Save or terminal setup failures show an English
-error message; compiler and program diagnostics go directly to the terminal.
+error message; compiler and program diagnostics go directly to the terminal,
+where cpsc colors its own diagnostics like gcc.
Terminal execution follows Code Runner's reuse behavior: users must finish or
interrupt an interactive program with Ctrl+C before running another file.
diff --git a/tools/driver/EmbeddedRunner.cc b/tools/driver/EmbeddedRunner.cc
index 2833cfd..b635e0f 100644
--- a/tools/driver/EmbeddedRunner.cc
+++ b/tools/driver/EmbeddedRunner.cc
@@ -3,6 +3,7 @@
#include "NativeCompiler.h"
#include "llvm/Object/ObjectFile.h"
#include "cps/CodeGen.h"
+#include "cps/Diagnostics.h"
#include "llvm/ExecutionEngine/Orc/AbsoluteSymbols.h"
#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"
#include "llvm/ExecutionEngine/Orc/LLJIT.h"
@@ -194,17 +195,20 @@ int runEmbedded(const cps::CodeGen &CG, const std::vector<std::string> &Objects)
// Pipes are fully buffered by default. Use the host CRT's stdout, also
// preferred by the JIT on Windows, so OUTPUT reaches desktop clients now.
if (std::setvbuf(stdout, nullptr, _IONBF, 0) != 0) {
- llvm::errs() << "error: cannot configure embedded stdout\n";
+ llvm::errs() << cps::diagnosticPrefix(cps::Severity::Error, "")
+ << "cannot configure embedded stdout\n";
return 1;
}
if (llvm::InitializeNativeTarget() || llvm::InitializeNativeTargetAsmPrinter()) {
- llvm::errs() << "error: cannot initialize the embedded LLVM target\n";
+ llvm::errs() << cps::diagnosticPrefix(cps::Severity::Error, "")
+ << "cannot initialize the embedded LLVM target\n";
return 1;
}
auto JTMBOrErr = llvm::orc::JITTargetMachineBuilder::detectHost();
if (!JTMBOrErr) {
- llvm::errs() << "error: cannot detect the embedded JIT target: "
+ llvm::errs() << cps::diagnosticPrefix(cps::Severity::Error, "")
+ << "cannot detect the embedded JIT target: "
<< llvm::toString(JTMBOrErr.takeError()) << "\n";
return 1;
}
@@ -227,19 +231,22 @@ int runEmbedded(const cps::CodeGen &CG, const std::vector<std::string> &Objects)
auto JITOrErr = Builder.create();
if (!JITOrErr) {
- llvm::errs() << "error: cannot create the embedded LLVM engine: "
+ llvm::errs() << cps::diagnosticPrefix(cps::Severity::Error, "")
+ << "cannot create the embedded LLVM engine: "
<< llvm::toString(JITOrErr.takeError()) << "\n";
return 1;
}
std::unique_ptr<llvm::orc::LLJIT> J = std::move(*JITOrErr);
if (auto Err = defineRuntimeSymbols(*J)) {
- llvm::errs() << "error: cannot define embedded runtime symbols: "
+ llvm::errs() << cps::diagnosticPrefix(cps::Severity::Error, "")
+ << "cannot define embedded runtime symbols: "
<< llvm::toString(std::move(Err)) << "\n";
return 1;
}
if (auto Err = loadCxxRuntime(*J)) {
- llvm::errs() << "error: cannot load embedded C++ runtime: "
+ llvm::errs() << cps::diagnosticPrefix(cps::Severity::Error, "")
+ << "cannot load embedded C++ runtime: "
<< llvm::toString(std::move(Err)) << "\n";
return 1;
}
@@ -250,7 +257,8 @@ int runEmbedded(const cps::CodeGen &CG, const std::vector<std::string> &Objects)
auto CRT = llvm::orc::DynamicLibrarySearchGenerator::Load(
"msvcrt.dll", J->getDataLayout().getGlobalPrefix());
if (!CRT) {
- llvm::errs() << "error: cannot load the embedded C runtime: "
+ llvm::errs() << cps::diagnosticPrefix(cps::Severity::Error, "")
+ << "cannot load the embedded C runtime: "
<< llvm::toString(CRT.takeError()) << "\n";
return 1;
}
@@ -262,7 +270,8 @@ int runEmbedded(const cps::CodeGen &CG, const std::vector<std::string> &Objects)
llvm::orc::ThreadSafeModule TSM =
llvm::orc::cloneExternalModuleToContext(CG.getModule(), TSCtx);
if (!TSM) {
- llvm::errs() << "error: cannot clone the embedded module for JIT execution\n";
+ llvm::errs() << cps::diagnosticPrefix(cps::Severity::Error, "")
+ << "cannot clone the embedded module for JIT execution\n";
return 1;
}
TSM.withModuleDo([&](llvm::Module &M) {
@@ -271,32 +280,37 @@ int runEmbedded(const cps::CodeGen &CG, const std::vector<std::string> &Objects)
});
if (auto Err = J->addIRModule(J->getMainJITDylib(), std::move(TSM))) {
- llvm::errs() << "error: embedded LLVM execution failed: "
+ llvm::errs() << cps::diagnosticPrefix(cps::Severity::Error, "")
+ << "embedded LLVM execution failed: "
<< llvm::toString(std::move(Err)) << "\n";
return 1;
}
for (const auto &Path : Objects) {
auto Buffer = llvm::MemoryBuffer::getFile(Path);
if (!Buffer) {
- llvm::errs() << "error: cannot read object file '" << Path << "': "
+ llvm::errs() << cps::diagnosticPrefix(cps::Severity::Error, "")
+ << "cannot read object file '" << Path << "': "
<< Buffer.getError().message() << "\n";
return 1;
}
if (auto Err = J->addObjectFile(std::move(*Buffer))) {
- llvm::errs() << "error: cannot load object file '" << Path << "': "
+ llvm::errs() << cps::diagnosticPrefix(cps::Severity::Error, "")
+ << "cannot load object file '" << Path << "': "
<< llvm::toString(std::move(Err)) << "\n";
return 1;
}
}
if (auto Err = J->initialize(J->getMainJITDylib())) {
- llvm::errs() << "error: embedded LLVM execution failed: "
+ llvm::errs() << cps::diagnosticPrefix(cps::Severity::Error, "")
+ << "embedded LLVM execution failed: "
<< llvm::toString(std::move(Err)) << "\n";
return 1;
}
auto MainSymOrErr = J->lookup("main");
if (!MainSymOrErr) {
- llvm::errs() << "error: embedded LLVM could not resolve main: "
+ llvm::errs() << cps::diagnosticPrefix(cps::Severity::Error, "")
+ << "embedded LLVM could not resolve main: "
<< llvm::toString(MainSymOrErr.takeError()) << "\n";
return 1;
}
@@ -306,7 +320,8 @@ int runEmbedded(const cps::CodeGen &CG, const std::vector<std::string> &Objects)
// Run platform deinitializers while JIT code is still mapped; frame
// deregistration runs when the JIT releases its memory below.
if (auto Err = J->deinitialize(J->getMainJITDylib())) {
- llvm::errs() << "error: embedded LLVM execution failed: "
+ llvm::errs() << cps::diagnosticPrefix(cps::Severity::Error, "")
+ << "embedded LLVM execution failed: "
<< llvm::toString(std::move(Err)) << "\n";
return 1;
}
diff --git a/tools/driver/NativeCompiler.cc b/tools/driver/NativeCompiler.cc
index e09fe98..324785f 100644
--- a/tools/driver/NativeCompiler.cc
+++ b/tools/driver/NativeCompiler.cc
@@ -1,5 +1,6 @@
#include "NativeCompiler.h"
#include "cps/CodeGen.h"
+#include "cps/Diagnostics.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Driver/Compilation.h"
@@ -48,7 +49,7 @@ struct TemporaryDirectory {
};
bool fail(const llvm::Twine &Message) {
- llvm::errs() << "cpsc: fatal error: " << Message << "\n";
+ llvm::errs() << cps::diagnosticPrefix(cps::Severity::Fatal) << Message << "\n";
return false;
}
@@ -58,8 +59,10 @@ class DriverDiagnostics : public clang::DiagnosticConsumer {
clang::DiagnosticConsumer::HandleDiagnostic(Level, Info);
llvm::SmallString<256> Message;
Info.FormatDiagnostic(Message);
- llvm::errs() << "cpsc: " << (Level >= clang::DiagnosticsEngine::Error
- ? "error: " : "warning: ") << Message << "\n";
+ llvm::errs() << cps::diagnosticPrefix(Level >= clang::DiagnosticsEngine::Error
+ ? cps::Severity::Error
+ : cps::Severity::Warning)
+ << Message << "\n";
}
};
diff --git a/tools/driver/StandardLibrary.cc b/tools/driver/StandardLibrary.cc
index ac88745..7680054 100644
--- a/tools/driver/StandardLibrary.cc
+++ b/tools/driver/StandardLibrary.cc
@@ -1,4 +1,5 @@
#include "StandardLibrary.h"
+#include "cps/Diagnostics.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
@@ -19,7 +20,8 @@ StandardLibrary::~StandardLibrary() {
bool StandardLibrary::load(std::vector<std::string> &Inputs) {
auto Error = llvm::sys::fs::createUniqueDirectory("cpsc-std", Directory);
if (Error) {
- llvm::errs() << "cpsc: fatal error: cannot extract standard library: " << Error.message() << "\n";
+ llvm::errs() << cps::diagnosticPrefix(cps::Severity::Fatal)
+ << "cannot extract standard library: " << Error.message() << "\n";
return false;
}
for (const auto &File : LibraryFiles) {
@@ -32,8 +34,9 @@ bool StandardLibrary::load(std::vector<std::string> &Inputs) {
Error = Output.error();
}
if (Error) {
- llvm::errs() << "cpsc: fatal error: cannot extract standard library file '"
- << File.Name << "': " << Error.message() << "\n";
+ llvm::errs() << cps::diagnosticPrefix(cps::Severity::Fatal)
+ << "cannot extract standard library file '" << File.Name
+ << "': " << Error.message() << "\n";
Output.clear_error();
return false;
}
diff --git a/tools/driver/main.cc b/tools/driver/main.cc
index b705843..10b665b 100644
--- a/tools/driver/main.cc
+++ b/tools/driver/main.cc
@@ -20,6 +20,22 @@
#include "llvm/Support/raw_ostream.h"
#include <fstream>
#include <string>
+#ifdef _WIN32
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+#include <windows.h>
+#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
+#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
+#endif
+#else
+#include <cstdlib>
+#include <cstring>
+#include <unistd.h>
+#endif
namespace {
@@ -28,11 +44,30 @@ int compilationTerminated() {
return 1;
}
+bool isColorOption(const std::string &Arg) {
+ return Arg == "-fdiagnostics-color" || Arg == "-fno-diagnostics-color" ||
+ Arg.rfind("-fdiagnostics-color=", 0) == 0;
+}
+
+// gcc's "auto": color only a terminal stderr. On Windows the console must
+// also accept ANSI escape sequences.
+bool colorTerminal() {
+#ifdef _WIN32
+ HANDLE Error = GetStdHandle(STD_ERROR_HANDLE);
+ DWORD Mode;
+ return GetConsoleMode(Error, &Mode) &&
+ SetConsoleMode(Error, Mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
+#else
+ const char *Term = std::getenv("TERM");
+ return Term && std::strcmp(Term, "dumb") != 0 && isatty(STDERR_FILENO);
+#endif
+}
+
#ifdef CPSC_RUN_PATH
bool checkLliVersion(llvm::StringRef LliPath) {
llvm::SmallString<256> VersionPath;
if (auto EC = llvm::sys::fs::createTemporaryFile("cpsc-lli-version", "txt", VersionPath)) {
- llvm::errs() << "cpsc: fatal error: cannot check lli version: " << EC.message() << "\n";
+ llvm::errs() << cps::diagnosticPrefix(cps::Severity::Fatal) << "cannot check lli version: " << EC.message() << "\n";
return false;
}
llvm::FileRemover TemporaryVersion(VersionPath);
@@ -41,7 +76,7 @@ bool checkLliVersion(llvm::StringRef LliPath) {
std::nullopt, {llvm::StringRef(""), llvm::StringRef(VersionPath), llvm::StringRef("")},
5, 0, &ExecutionError);
if (Result != 0) {
- llvm::errs() << "cpsc: fatal error: cannot check lli version at '" << LliPath
+ llvm::errs() << cps::diagnosticPrefix(cps::Severity::Fatal) << "cannot check lli version at '" << LliPath
<< "' (status " << Result << ")";
if (!ExecutionError.empty()) llvm::errs() << ": " << ExecutionError;
llvm::errs() << "\n";
@@ -49,18 +84,18 @@ bool checkLliVersion(llvm::StringRef LliPath) {
}
auto Version = llvm::MemoryBuffer::getFile(VersionPath);
if (!Version) {
- llvm::errs() << "cpsc: fatal error: cannot read lli version: "
+ llvm::errs() << cps::diagnosticPrefix(cps::Severity::Fatal) << "cannot read lli version: "
<< Version.getError().message() << "\n";
return false;
}
llvm::SmallVector<llvm::StringRef, 2> Matches;
if (!llvm::Regex("LLVM version ([0-9]+)\\.").match((*Version)->getBuffer(), &Matches)) {
- llvm::errs() << "cpsc: fatal error: cannot identify LLVM version of lli at '"
+ llvm::errs() << cps::diagnosticPrefix(cps::Severity::Fatal) << "cannot identify LLVM version of lli at '"
<< LliPath << "'; LLVM 23 is required\n";
return false;
}
if (Matches[1] != "23") {
- llvm::errs() << "cpsc: fatal error: -r requires lli from LLVM 23; found LLVM "
+ llvm::errs() << cps::diagnosticPrefix(cps::Severity::Fatal) << "-r requires lli from LLVM 23; found LLVM "
<< Matches[1] << " at '" << LliPath << "'\n";
return false;
}
@@ -88,6 +123,32 @@ int main(int argc, char **argv) {
}
}
+ // Like gcc, the last valid -fdiagnostics-color option wins, and it is
+ // applied before any other diagnostic.
+ enum { ColorAuto, ColorAlways, ColorNever } ColorMode = ColorAuto;
+ std::string BadColor;
+ for (int I = 1; I < argc; ++I) {
+ std::string Arg = argv[I];
+ if (Arg == "-fdiagnostics-color" || Arg == "-fdiagnostics-color=always")
+ ColorMode = ColorAlways;
+ else if (Arg == "-fdiagnostics-color=auto")
+ ColorMode = ColorAuto;
+ else if (Arg == "-fdiagnostics-color=never" || Arg == "-fno-diagnostics-color")
+ ColorMode = ColorNever;
+ else if (isColorOption(Arg) && BadColor.empty())
+ BadColor = Arg;
+ }
+ // colorTerminal() also enables escape sequences on a Windows console.
+ const bool Terminal = ColorMode != ColorNever && colorTerminal();
+ cps::DiagnosticColors = ColorMode == ColorAlways || Terminal;
+ if (!BadColor.empty()) {
+ llvm::errs() << cps::diagnosticPrefix(cps::Severity::Error)
+ << "unrecognized argument in option '" << BadColor << "'\n"
+ << cps::diagnosticPrefix(cps::Severity::Note)
+ << "valid arguments to '-fdiagnostics-color=' are: always auto never\n";
+ return compilationTerminated();
+ }
+
std::vector<std::string> InputPaths;
std::string OutputPath;
bool Run = false;
@@ -103,16 +164,18 @@ int main(int argc, char **argv) {
NoStd = true;
} else if (Arg == "-o") {
if (!OutputPath.empty()) {
- llvm::errs() << "cpsc: error: -o may only be specified once\n";
+ llvm::errs() << cps::diagnosticPrefix(cps::Severity::Error) << "-o may only be specified once\n";
return compilationTerminated();
}
if (I + 1 == argc || argv[I + 1][0] == '-' || argv[I + 1][0] == '\0') {
- llvm::errs() << "cpsc: error: -o requires an output path\n";
+ llvm::errs() << cps::diagnosticPrefix(cps::Severity::Error) << "-o requires an output path\n";
return compilationTerminated();
}
OutputPath = argv[++I];
+ } else if (isColorOption(Arg)) {
+ continue;
} else if (Arg.empty() || Arg[0] == '-') {
- llvm::errs() << "cpsc: error: unknown argument '" << Arg << "'\n";
+ llvm::errs() << cps::diagnosticPrefix(cps::Severity::Error) << "unknown argument '" << Arg << "'\n";
return compilationTerminated();
} else {
InputPaths.push_back(Arg);
@@ -120,7 +183,7 @@ int main(int argc, char **argv) {
}
if (InputPaths.empty()) {
- llvm::errs() << "cpsc: fatal error: no input files\n";
+ llvm::errs() << cps::diagnosticPrefix(cps::Severity::Fatal) << "no input files\n";
return compilationTerminated();
}
#ifdef CPSC_RUN_PATH
@@ -130,13 +193,13 @@ int main(int argc, char **argv) {
#ifdef CPSC_RUN_PATH
auto Lli = llvm::sys::findProgramByName("lli");
if (!Lli) {
- llvm::errs() << "cpsc: fatal error: -r requires lli from LLVM 23 in PATH: " << Lli.getError().message() << "\n";
+ llvm::errs() << cps::diagnosticPrefix(cps::Severity::Fatal) << "-r requires lli from LLVM 23 in PATH: " << Lli.getError().message() << "\n";
return compilationTerminated();
}
LliPath = *Lli;
if (!checkLliVersion(LliPath)) return compilationTerminated();
#elif defined(CPSC_RUN_NONE)
- llvm::errs() << "cpsc: fatal error: -r is disabled in this build (CPSC_RUN_MODE=NONE)\n";
+ llvm::errs() << cps::diagnosticPrefix(cps::Severity::Fatal) << "-r is disabled in this build (CPSC_RUN_MODE=NONE)\n";
return compilationTerminated();
#endif
}
@@ -150,13 +213,13 @@ int main(int argc, char **argv) {
const auto &InputPath = InputPaths[I];
for (size_t J = 0; J < I; ++J) {
if (llvm::sys::fs::equivalent(InputPath, InputPaths[J])) {
- llvm::errs() << "cpsc: error: duplicate input file '" << InputPath << "'\n";
+ llvm::errs() << cps::diagnosticPrefix(cps::Severity::Error) << "duplicate input file '" << InputPath << "'\n";
return compilationTerminated();
}
}
std::ifstream Input(InputPath, std::ios::binary);
if (!Input) {
- llvm::errs() << "cpsc: fatal error: cannot open source file '" << InputPath << "'\n";
+ llvm::errs() << cps::diagnosticPrefix(cps::Severity::Fatal) << "cannot open source file '" << InputPath << "'\n";
return compilationTerminated();
}
cps::DiagnosticSource = InputPath;
@@ -164,7 +227,7 @@ int main(int argc, char **argv) {
cps::Parser Parser(Lex);
Programs[I] = Parser.Parse();
if (Input.bad()) {
- llvm::errs() << "cpsc: fatal error: cannot read source file '" << InputPath << "'\n";
+ llvm::errs() << cps::diagnosticPrefix(cps::Severity::Fatal) << "cannot read source file '" << InputPath << "'\n";
return compilationTerminated();
}
if (Lex.hadError() || Parser.hadError()) return compilationTerminated();
@@ -274,7 +337,7 @@ int main(int argc, char **argv) {
}
for (const auto &InputPath : InputPaths) {
if (Save && llvm::sys::fs::equivalent(InputPath, OutputPath)) {
- llvm::errs() << "cpsc: error: output file must not be the source file\n";
+ llvm::errs() << cps::diagnosticPrefix(cps::Severity::Error) << "output file must not be the source file\n";
return compilationTerminated();
}
}
@@ -301,7 +364,7 @@ int main(int argc, char **argv) {
EC = llvm::sys::fs::openFileForWrite(IRPath, OutputFD);
}
if (EC) {
- llvm::errs() << "cpsc: fatal error: cannot open output file '" << IRPath
+ llvm::errs() << cps::diagnosticPrefix(cps::Severity::Fatal) << "cannot open output file '" << IRPath
<< "': " << EC.message() << "\n";
return compilationTerminated();
}
@@ -309,7 +372,7 @@ int main(int argc, char **argv) {
CG.print(Output);
Output.close();
if (Output.has_error()) {
- llvm::errs() << "cpsc: fatal error: cannot write output file '" << IRPath
+ llvm::errs() << cps::diagnosticPrefix(cps::Severity::Fatal) << "cannot write output file '" << IRPath
<< "': " << Output.error().message() << "\n";
Output.clear_error();
return compilationTerminated();
@@ -319,7 +382,7 @@ int main(int argc, char **argv) {
#ifdef CPSC_RUN_PATH
llvm::SmallString<256> RunPath(IRPath);
if (auto PathError = llvm::sys::fs::make_absolute(RunPath)) {
- llvm::errs() << "cpsc: error: cannot resolve IR path: " << PathError.message() << "\n";
+ llvm::errs() << cps::diagnosticPrefix(cps::Severity::Error) << "cannot resolve IR path: " << PathError.message() << "\n";
return 1;
}
std::string ExecutionError;
@@ -330,7 +393,7 @@ int main(int argc, char **argv) {
int Result = llvm::sys::ExecuteAndWait(LliPath, LliArgv,
std::nullopt, {}, 0, 0, &ExecutionError);
if (Result < 0) {
- llvm::errs() << "cpsc: error: lli execution failed (status " << Result << "): "
+ llvm::errs() << cps::diagnosticPrefix(cps::Severity::Error) << "lli execution failed (status " << Result << "): "
<< ExecutionError << "\n";
return 1;
}