11# ' Compile C++ code
22# '
3- # ' [cpp_source()] compiles and loads a single C++ file for use in R.
3+ # ' [cpp_source()] compiles and loads one or more C++ files for use in R.
44# ' [cpp_function()] compiles and loads a single function for use in R.
55# ' [cpp_eval()] evaluates a single C++ expression and returns the result.
66# '
77# ' Within C++ code you can use `[[cpp11::linking_to("pkgxyz")]]` to link to
88# ' external packages. This is equivalent to putting those packages in the
99# ' `LinkingTo` field in a package DESCRIPTION.
1010# '
11- # ' @param file A file containing C++ code to compile
11+ # ' @param file One or more files containing C++ code to compile
1212# ' @param code If non-null, the C++ code to compile
1313# ' @param env The R environment where the R wrapping functions should be defined.
1414# ' @param clean If `TRUE`, cleanup the files after sourcing
6767# ' @export
6868cpp_source <- function (file , code = NULL , env = parent.frame(), clean = TRUE , quiet = TRUE , cxx_std = Sys.getenv(" CXX_STD" , " CXX11" ), dir = tempfile()) {
6969 stop_unless_installed(c(" brio" , " callr" , " cli" , " decor" , " desc" , " glue" , " tibble" , " vctrs" ))
70- if (! missing(file ) && ! file.exists(file )) {
70+
71+ if (! missing(file ) && ! all(file.exists(file ))) {
72+ file <- file [! file.exists(file )][[1L ]]
7173 stop(" Can't find `file` at this path:\n " , file , " \n " , call. = FALSE )
7274 }
7375
@@ -83,12 +85,13 @@ cpp_source <- function(file, code = NULL, env = parent.frame(), clean = TRUE, qu
8385 }
8486 brio :: write_lines(code , file )
8587 }
88+
8689 if (! any(tools :: file_ext(file ) %in% c(" cpp" , " cc" ))) {
8790 stop(" `file` must have a `.cpp` or `.cc` extension" )
8891 }
8992
90- name <- generate_cpp_name( file )
91- package <- tools :: file_path_sans_ext( name )
93+ package <- generate_package_name( )
94+ name <- vcapply( file , generate_cpp_name )
9295
9396 orig_dir <- normalizePath(dirname(file ), winslash = " /" )
9497 new_dir <- normalizePath(file.path(dir , " src" ), winslash = " /" )
@@ -112,7 +115,7 @@ cpp_source <- function(file, code = NULL, env = parent.frame(), clean = TRUE, qu
112115 funs <- get_registered_functions(all_decorations , " cpp11::register" , quiet = quiet )
113116 cpp_functions_definitions <- generate_cpp_functions(funs , package = package )
114117
115- cpp_path <- file.path(dirname( new_file_path ) , " cpp11.cpp" )
118+ cpp_path <- file.path(new_dir , " cpp11.cpp" )
116119 brio :: write_lines(c(' #include "cpp11/declarations.hpp"' , " using namespace ::cpp11;" , cpp_functions_definitions ), cpp_path )
117120
118121 linking_to <- union(get_linking_to(all_decorations ), " cpp11" )
@@ -130,7 +133,14 @@ cpp_source <- function(file, code = NULL, env = parent.frame(), clean = TRUE, qu
130133 brio :: write_lines(makevars_content , file.path(new_dir , " Makevars" ))
131134
132135 source_files <- normalizePath(c(new_file_path , cpp_path ), winslash = " /" )
133- res <- callr :: rcmd(" SHLIB" , source_files , user_profile = TRUE , show = ! quiet , wd = new_dir )
136+
137+ shared_lib_name <- paste0(package , .Platform $ dynlib.ext )
138+
139+ shlib_args <- c(
140+ source_files ,
141+ paste0(" --output=" , shared_lib_name )
142+ )
143+ res <- callr :: rcmd(" SHLIB" , shlib_args , user_profile = TRUE , show = ! quiet , wd = new_dir )
134144 if (res $ status != 0 ) {
135145 error_messages <- res $ stderr
136146
@@ -140,30 +150,51 @@ cpp_source <- function(file, code = NULL, env = parent.frame(), clean = TRUE, qu
140150 stop(" Compilation failed." , call. = FALSE )
141151 }
142152
143- shared_lib <- file.path(dir , " src" , paste0(tools :: file_path_sans_ext(new_file_name ), .Platform $ dynlib.ext ))
144153 r_path <- file.path(dir , " R" , " cpp11.R" )
145154 brio :: write_lines(r_functions , r_path )
146155 source(r_path , local = env )
147156
148- dyn.load(shared_lib , local = TRUE , now = TRUE )
157+ shared_lib_path <- file.path(dir , " src" , shared_lib_name )
158+
159+ dyn.load(shared_lib_path , local = TRUE , now = TRUE )
149160}
150161
151162the <- new.env(parent = emptyenv())
152163the $ count <- 0L
153164
154- generate_cpp_name <- function (name , loaded_dlls = c(" cpp11" , names(getLoadedDLLs()))) {
165+ generate_cpp_name <- function (
166+ name ,
167+ loaded_dlls = c(" cpp11" , names(getLoadedDLLs()))
168+ ) {
155169 ext <- tools :: file_ext(name )
156170 root <- tools :: file_path_sans_ext(basename(name ))
157- count <- 2
158- new_name <- root
159- while (new_name %in% loaded_dlls ) {
160- new_name <- sprintf(" %s_%i" , root , count )
161- count <- count + 1
162- }
163- sprintf(" %s.%s" , new_name , ext )
171+ root <- make_unique(root , loaded_dlls )
172+ sprintf(" %s.%s" , root , ext )
164173}
165174
175+ generate_package_name <- function (
176+ loaded_dlls = c(" cpp11" , names(getLoadedDLLs()))
177+ ) {
178+ name <- paste0(
179+ " package_" ,
180+ paste0(sample(letters , 10 , replace = TRUE ), collapse = " " )
181+ )
182+ name <- make_unique(name , loaded_dlls )
183+ name
184+ }
185+
186+ # Adds `_2`, `_3`, etc to `x` until it is unique
187+ make_unique <- function (x , conflicts ) {
188+ new <- x
166189
190+ count <- 2L
191+ while (new %in% conflicts ) {
192+ new <- paste0(x , " _" , count )
193+ count <- count + 1L
194+ }
195+
196+ new
197+ }
167198
168199generate_include_paths <- function (packages ) {
169200 out <- character (length(packages ))
0 commit comments