Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

precleanCompilation deletes .o files created by Rcpp::sourceCpp, causing compilation to fail for C++ linked via nimbleExternalCall #1461

Open
jmhewitt opened this issue Jun 3, 2024 · 3 comments

Comments

@jmhewitt
Copy link

jmhewitt commented Jun 3, 2024

I noticed nimble's new precleanCompilation option #1393 in version 1.1.0, introduced to fix #1368, caused some of my project code to break on OSX (M2, Ventura 13.1) and Linux (Rocky Linux 8.6). I can keep my project code running by setting nimble::nimbleOptions(precleanCompilation=FALSE), but wanted to document the issue in case others run into the same. I also wanted to ask if you thought it might be possible to re-implement the precleanCompilation option to be more precise in which files it deletes? Or, can you recommend another fix? I recognize this is probably a low-priority need. An MWE to reproduce and resolve the issue is below.

I use Rcpp::sourceCpp (Rcpp version 1.0.11) to manage C++ include paths while compiling cpp files with dependencies on the RcppEigen and BH packages, for example, so I can call Boost's differential equation solver from nimble models. The precleanCompilation option deletes object files Rcpp::sourceCpp creates, which prevents nimble models with nimbleExternalCall functions from compiling. The MWE does only depends on Rcpp and nimbleExternalCall, though.

Thanks!

# MWE modified from help("nimbleExternalCall")

library(nimble)

sink('add1.h')
cat('
 extern "C" {
 void my_internal_function(double *p, double*ans, int n);
 }
')
sink()
sink('add1.cpp') 
cat('
 #include <cstdio>
 #include "add1.h"
 void my_internal_function(double *p, double *ans, int n) {
   printf("In my_internal_function\\n");
     /* cat reduces the double slash to single slash */ 
   for(int i = 0; i < n; i++) 
     ans[i] = p[i] + 1.0;
 }
')
sink()

# modification: use Rcpp::sourceCpp to compile instead of direct system call
# system('g++ add1.cpp -c -o add1.o')

# compile files
Rcpp::sourceCpp(file = 'add1.cpp')

# find compiled file
ofile = normalizePath(
  path = dir(
    path = getOption("rcpp.cache.dir", tempdir()), 
    pattern = 'add1.o', 
    full.names = TRUE, 
    recursive = TRUE
  ),
  winslash = '/'
)

Radd1 <- nimbleExternalCall(
  function(x = double(1), ans = double(1), n = integer()){}, 
  Cfun =  'my_internal_function',
  headerFile = file.path(getwd(), 'add1.h'), 
  returnType = void(),
  oFile = ofile
)

# check: Rcpp output file exists
file.exists(ofile)

# Error: will not compile when  nimbleOptions('precleanCompilation') == TRUE
CRadd1 <- compileNimble(Radd1)

# precleanCompilation unintentionally deletes the required object file
file.exists(ofile)

# rebuild object file
Rcpp::sourceCpp(file = 'add1.cpp')

# Success: will compile when precleanCompilation is disabled
nimbleOptions(precleanCompilation=FALSE)
CRadd1 <- compileNimble(Radd1)
@paciorek
Copy link
Contributor

paciorek commented Jun 5, 2024

Note from Perry: Given that the --preclean fixed a bug we only ever saw during testing, we could decide that when nimbleExternalCall has been used, it will always turn off the --preclean option. Or we could at least add a note to its roxygen.

@jmhewitt
Copy link
Author

jmhewitt commented Jun 14, 2024

Both sound like fine options since I agree that at the moment it mostly feels like two corner cases bumping into each other, thanks!

@paciorek
Copy link
Contributor

For now I am just adding a note to the roxygen for nimbleExternalCall. On quick glance I didn't see an easy way to "partially" preclean.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants