c++rrstudiosinktmb

Exporting g++ compilation error messages from TMB library to a text file


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?


Solution

  • 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