I'm trying to compile a model with gcc (c++) in R (using the TMB package). The errors are so numerous that in Rstudio, I can't even scroll up to see the beginning of them. Therefore, I would like to print everything in the console (messages, errors and warnings) to a text file. This way I could also compare different model outputs (or specifically, their fails).
I tried the following things:
fileConn<-file("Fail.txt")
writeLines(compile("Mymodel.cpp"), fileConn)
close(fileConn)
=> Gives me an empty file (which I more or less expected)
zz <- file("Fail.txt", open = "wt")
sink(zz, type = "message")
compile("Mymodel.cpp")
sink()
=> only prints the final error
What did I overlook?
You haven't provided your compile()
function, but I assume it runs a system command that calls g++
to compile Mymodel.cpp
.
In this case, the g++
process will print its error output to stderr. The only way to capture this output using R is to call system2()
with stderr=T
. Note that system()
does not have the capability to capture stderr directly (although it can capture stdout via intern=T
, and you could add 2>&1
to the shell command to capture stderr along with it, so that's another viable option). sink()
does not capture system command output; it only captures R output.
I recommend passing variadic arguments from your compile()
function to the system2()
call, thus parameterizing whether your call to compile()
results in g++
's stderr going to the terminal or to the return value of your compile()
function. Here's how this would be done:
write('error!','test1.cpp'); ## generate a test file with invalid C++
compile <- function(file,...) system2('g++',file,...);
compile('test1.cpp'); ## output lost to the terminal
## test1.cpp:1:1: error: ‘error’ does not name a type
## error!
## ^
output <- compile('test1.cpp',stdout=T,stderr=T); ## capture output
## Warning message:
## running command ''g++' 'test1.cpp' 2>&1' had status 1
output;
## [1] "test1.cpp:1:1: error: ‘error’ does not name a type"
## [2] " error!"
## [3] " ^"
## attr(,"status")
## [1] 1
write(output,'output.txt'); ## write output to a text file
cat(readLines('output.txt'),sep='\n'); ## show it
## test1.cpp:1:1: error: ‘error’ does not name a type
## error!
## ^
If you really want to capture all output generated within your compile()
function, then you can combine the above solution with sink()
as demonstrated here: How to save all console output to file in R?.
In this case, I'd recommend scrapping the variadic arguments idea and going with a single additional argument to compile()
, which will take an output file name to which all output will be written.
This will require several predications on the missingness of the additional argument:
write('error!','test1.cpp'); ## generate a test file with invalid C++
compile <- function(file,outputFile) {
if (!missing(outputFile)) {
outputCon <- file(outputFile,'wt'); ## require file name
sink(outputCon);
sink(outputCon,type='message'); ## must sink messages separately
warn.old <- options(warn=1)$warn; ## necessary to capture warnings as they occur
}; ## end if
cat('some random output 1\n');
if (!missing(outputFile)) {
output <- system2('g++',file,stdout=T,stderr=T); ## before flush to get warnings
sink(); ## force flush before appending system command output
sink(type='message');
outputCon <- file(outputFile,'at'); ## must reopen connection for appending
write(output,outputCon);
sink(outputCon);
sink(outputCon,type='message');
} else {
system2('g++',file);
}; ## end if
cat('some random output 2\n');
if (!missing(outputFile)) {
sink();
sink(type='message');
options(warn=warn.old);
}; ## end if
}; ## end compile()
compile('test1.cpp'); ## output lost to the terminal
## some random output 1
## test1.cpp:1:1: error: ‘error’ does not name a type
## error!
## ^
## some random output 2
compile('test1.cpp','output.txt'); ## internally capture all output
cat(readLines('output.txt'),sep='\n'); ## show it
## some random output 1
## Warning: running command ''g++' test1.cpp 2>&1' had status 1
## test1.cpp:1:1: error: ‘error’ does not name a type
## error!
## ^
## some random output 2