add tests to sample package; fix load/unload behavior
[pkgdev.git] / R / load.R
CommitLineData
156f8ca6
BA
1# Core function to load a package (source R files and compile/load C libraries)
2# @param path Location of the (non-standard) package to be loaded
3# @param cc Compilator to be used (e.g. 'gcc -std=gnu99' [default])
4.pkgdev.load = function(path, cc) {
5
6 # Get package name from path
7 pathTokens = strsplit(path, c(.Platform$file.sep))[[1]]
8 pkgName = pathTokens[length(pathTokens)]
9
10 # Create base directory for pkgName under R_HOME_USER/pkgdev/pkgs/pkgName (if not existing)
11 pkdev_path = file.path(Sys.getenv("R_HOME_USER"), "pkgdev")
12 dir.create(file.path(pkdev_path,"pkgs",pkgName), showWarnings=FALSE)
13
14 # R code first
15 # Warning: R/tests folder should not be sourced
16 forbiddenPath = file.path(path,"R","tests")
17 for (fileOrDir in list.files( file.path(path,"R"),full.names=TRUE )) {
18 if (fileOrDir != forbiddenPath) {
19 if (file.info(fileOrDir)$isdir) {
20 rFiles = list.files(fileOrDir, pattern="\\.[RrSsq]$",
672594cf 21 full.names=TRUE, recursive=TRUE)
156f8ca6
BA
22 # NOTE: potential unexported functions are not hidden;
23 # the developer is assumed to handle this
24 lapply(rFiles, source)
25 }
26 else source(fileOrDir)
27 }
28 }
29
30 # Also load datasets (if any)
31 rData = list.files(file.path(path,"data"),pattern="\\.R(d|D)ata$",full.names=TRUE)
32 lapply(rData, load)
33
34 # This file tells if the package is currently loaded
35 pkgLoadFile = file.path(pkdev_path,"pkgs",pkgName,"loaded")
672594cf 36
156f8ca6
BA
37 if (file.exists(file.path(path,"src"))) {
38 # C code -- Warning: src/tests folder should not be listed
d28c1db9
BA
39 cFiles = c(
40 list.files(file.path(path,"src","sources"),pattern="\\.[cChH]$",
41 full.names=TRUE, recursive=TRUE, no..=TRUE),
42 list.files(file.path(path,"src","adapters"),pattern="\\.[cChH]$",
43 full.names=TRUE, recursive=TRUE, no..=TRUE))
44
156f8ca6
BA
45 # Create folder R_HOME_USER/pkgdev/pkgs/pkgName/src (if not existing)
46 dir.create(file.path(pkdev_path,"pkgs",pkgName,"src"), showWarnings=FALSE)
47
48 # Generate suitable Makefile (all object files go at R_HOME_USER/pkgdev/pkgs/pkgName/src)
49 .generateMakefileLoad(path, cFiles, pkgName, cc)
50
51 # Compile in the right folder (R_HOME_USER/pkgdev/pkgs/pkgName/src)
52 save_wd = getwd()
53 setwd( file.path(pkdev_path,"pkgs",pkgName,"src") )
54 library(parallel)
55 system( paste( Sys.getenv("MAKE"), "depend", sep=' ') )
56 system( paste( Sys.getenv("MAKE"), "-j", detectCores(), "all", sep=' ') )
57 setwd(save_wd)
58
59 # Finally load library
60 sharedLib =
61 file.path(pkdev_path,"pkgs",pkgName,paste(pkgName,.Platform$dynlib.ext,sep=''))
62 if (file.exists(pkgLoadFile)) dyn.unload(sharedLib)
63 dyn.load(sharedLib)
64 }
65
66 # Mark package as 'loaded'
672594cf 67 file.create(pkgLoadFile)
156f8ca6
BA
68}
69
70# Generate appropriate Makefile under R_HOME_USER/pkgdev/pkgs/pkgName/src
71.generateMakefileLoad = function(path, cFiles, pkgName, cc) {
72
73 # Preparation: separate cFiles into codes and headers
74 codeFiles = grep(".*(c|C)$", cFiles, value=TRUE)
75 headerFiles = grep(".*(h|H)$", cFiles, value=TRUE)
76
77 # objectFiles = all .o files in current folder, duplicating file structure under path/src/
78 basePathFrom = file.path(path, "src")
79 pkdev_path = file.path(Sys.getenv("R_HOME_USER"), "pkgdev")
80 basePathTo = file.path(pkdev_path,"pkgs",pkgName,"src")
81 for (fileOrDir in list.files(basePathFrom, recursive=TRUE, include.dirs=TRUE)) {
82 if (file.info(file.path(basePathFrom,fileOrDir))$isdir) {
83 # Process folders only
84 dir.create(file.path(basePathTo,fileOrDir),showWarnings=FALSE,recursive=TRUE)
85 }
86 }
87 objectFiles = c()
88 for (codeFile in codeFiles) {
89 objectFiles = c(
90 objectFiles,
91 sub("(.*)\\.(c|C)$","\\1\\.o", sub(basePathFrom,basePathTo,codeFile,fixed=TRUE)))
92 }
93
94 # Build Makefile
95 makefile = paste('
96CC = ', cc, '
97INCLUDES = -I/usr/include/R/ -I/usr/local/include -I/usr/share/R/include
98LIBRARIES = -L/usr/lib -L/usr/lib/R/lib -lR -lm
99CFLAGS = -DNDEBUG -fpic -march=native -mtune=generic -O2 -pipe \\
100 -fstack-protector --param=ssp-buffer-size=4 -D_FORTIFY_SOURCE=2
101LDFLAGS = -shared -Wl,-O1,--sort-common,--as-needed,-z,relro
102LIB = ', paste(file.path("..",pkgName), .Platform$dynlib.ext, sep=''), '
103SRCS = ', paste(codeFiles, sep='', collapse=' '), '
104HEDS = ', paste(headerFiles, sep='', collapse=' '), '
105OBJS = ', paste(objectFiles, sep='', collapse= ' '), '
106all: $(LIB)
107$(LIB) : $(OBJS)
108 $(CC) $(CFLAGS) $(LDFLAGS) $^ -o $(LIB) $(LIBRARIES)', sep='')
109 compileObjects = ""
110for (i in 1:length(codeFiles)) {
111 compileObjects = paste(compileObjects, '
112', objectFiles[i], ' : ', codeFiles[i], '
113 $(CC) $(INCLUDES) $(CFLAGS) -c $< -o $@', sep='')
114}
115 makefile = paste(makefile, compileObjects, '
116.PHONY: clean delib depend
117clean:
118 rm -f $(OBJS) ./.depend
119delib:
120 rm -f $(LIB)
121depend: .depend
122.depend: $(SRCS) $(HEDS)
123 rm -f ./.depend
124 $(CC) -MM $^ > ./.depend
125include .depend
126', sep='')
127
128 # Write it to disk
129 writeLines(makefile, file.path(pkdev_path,"pkgs",pkgName,"src","Makefile"))
130}