Greeting, LLVM wizards,
We are using Clang and LLVM in an application to compile and execute C++ code on the fly. If the code fails to compile, I would like to be able to pop up a dialog box telling the user why. But warnings and error messages go straight to standard error.
I have tried passing a raw_string_stream to TextDiagnosticPrinter instead of errs(), but that seems to have no effect. What is that stream actually used for? Am I doing something wrong? I have attached the code we use to compile (simplified a bit).
Thanks,
Geoff
IntrusiveRefCntPtr diag_opts(new DiagnosticOptions);
std::string diagnostics;
raw_string_ostream diag_stream(diagnostics);
TextDiagnosticPrinter *diag_client =
new TextDiagnosticPrinter(diag_stream, diag_opts.get());
IntrusiveRefCntPtr diag_ids(new DiagnosticIDs);
DiagnosticsEngine diags(diag_ids, diag_opts.get(), diag_client);
Driver driver(executable, llvm::sys::getDefaultTargetTriple(), diags);
SmallVector<const char*, 16> args;
// various arguments…
std::unique_ptr compilation(driver.BuildCompilation(args));
const JobList& jobs = compilation->getJobs();
const Command& cmd = llvm::cast(*jobs.begin());
const ArgStringList& ccargs = cmd.getArguments();
std::unique_ptr invocation(new CompilerInvocation);
CompilerInvocation::CreateFromArgs(
invocation,
const_cast<const char*>(ccargs.data()),
const_cast<const char**>(ccargs.data()) + ccargs.size(),
diags);
CompilerInstance clang;
clang.setInvocation(std::move(invocation));
clang.createDiagnostics();
std::unique_ptr action(
new EmitLLVMOnlyAction(&my_llvm_context));
if (!clang.ExecuteAction(*action)) {
if (diag_stream.str().empty()) {
setError(“Compilation failed: see standard error for details.”);
} else {
setError(“Compilation failed:\n” + diag_stream.str());
}
}
Greetings, clangspeople,
We are using Clang and LLVM in an application to compile and execute C++ code on the fly. If the code fails to compile, I would like to be able to pop up a dialog box telling the user why. But warnings and error messages go straight to standard error.
I have tried passing a raw_string_ostream to TextDiagnosticPrinter instead of errs(), but that seems to have no effect. What is that stream actually used for? Am I doing something wrong? I have attached the code we use to compile (simplified a bit).
Thanks,
Geoff
IntrusiveRefCntPtr diag_opts(new DiagnosticOptions);
std::string diagnostics;
raw_string_ostream diag_stream(diagnostics);
TextDiagnosticPrinter *diag_client =
new TextDiagnosticPrinter(diag_stream, diag_opts.get());
IntrusiveRefCntPtr diag_ids(new DiagnosticIDs);
DiagnosticsEngine diags(diag_ids, diag_opts.get(), diag_client);
Driver driver(executable, llvm::sys::getDefaultTargetTriple(), diags);
SmallVector<const char*, 16> args;
// various arguments…
std::unique_ptr compilation(driver.BuildCompilation(args));
const JobList& jobs = compilation->getJobs();
const Command& cmd = llvm::cast(*jobs.begin());
const ArgStringList& ccargs = cmd.getArguments();
std::unique_ptr invocation(new CompilerInvocation);
CompilerInvocation::CreateFromArgs(
invocation,
const_cast<const char*>(ccargs.data()),
const_cast<const char**>(ccargs.data()) + ccargs.size(),
diags);
CompilerInstance clang;
clang.setInvocation(std::move(invocation));
clang.createDiagnostics();
std::unique_ptr action(
new EmitLLVMOnlyAction(&my_llvm_context));
if (!clang.ExecuteAction(*action)) {
if (diag_stream.str().empty()) {
setError(“Compilation failed: see standard error for details.”);
} else {
setError(“Compilation failed:\n” + diag_stream.str());
}
}
The DiagnosticsEngine you pass to the Driver is only used for diagnostics from the driver itself, not the resulting compilation. createDiagnostics() takes a DiagnosticsEngine as a parameter. (If you don’t explicitly pass one, it will create one for you, which prints diagnostics to stderr.) -Eli
Aha! Thank you very much, Eli. That does the trick.
[Actually, though, you have to pass createDiagnostics() a DiagnosticConsumer, which in my case is my TextDiagnosticPrinter.]
Geoff
Well, if anybody on this list is interested, the solution was pointed out to me by Eli Friedman on the cfe-dev list (it is in fact a Clang problem).
The DiagnosticsEngine passed to the Driver is used only by the Driver itself, not by the resulting compilation. To retrieve the diagnostics from the compilation, I have to pass my TextDiagnosticPrinter to CompilerInstance::createDiagnostics().
Thanks to Eli.
We have the same problem when using the C interface, however. When clang_parseTranslationUnit() fails, there seems to be no way to recover the diagnostics, which are printed to standard error. Apparently clang_getDiagnosticSetFromTU() is there to give you access to diagnostics, but it is only of use if you have a translation unit for it…
Geoff