| 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]$", |
| 21 | full.names=TRUE, recursive=TRUE) |
| 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") |
| 36 | |
| 37 | if (file.exists(file.path(path,"src"))) { |
| 38 | # C code -- Warning: src/tests folder should not be listed |
| 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 | |
| 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' |
| 67 | file.create(pkgLoadFile) |
| 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(' |
| 96 | CC = ', cc, ' |
| 97 | INCLUDES = -I/usr/include/R/ -I/usr/local/include -I/usr/share/R/include |
| 98 | LIBRARIES = -L/usr/lib -L/usr/lib/R/lib -lR -lm |
| 99 | CFLAGS = -DNDEBUG -fpic -march=native -mtune=generic -O2 -pipe \\ |
| 100 | -fstack-protector --param=ssp-buffer-size=4 -D_FORTIFY_SOURCE=2 |
| 101 | LDFLAGS = -shared -Wl,-O1,--sort-common,--as-needed,-z,relro |
| 102 | LIB = ', paste(file.path("..",pkgName), .Platform$dynlib.ext, sep=''), ' |
| 103 | SRCS = ', paste(codeFiles, sep='', collapse=' '), ' |
| 104 | HEDS = ', paste(headerFiles, sep='', collapse=' '), ' |
| 105 | OBJS = ', paste(objectFiles, sep='', collapse= ' '), ' |
| 106 | all: $(LIB) |
| 107 | $(LIB) : $(OBJS) |
| 108 | $(CC) $(CFLAGS) $(LDFLAGS) $^ -o $(LIB) $(LIBRARIES)', sep='') |
| 109 | compileObjects = "" |
| 110 | for (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 |
| 117 | clean: |
| 118 | rm -f $(OBJS) ./.depend |
| 119 | delib: |
| 120 | rm -f $(LIB) |
| 121 | depend: .depend |
| 122 | .depend: $(SRCS) $(HEDS) |
| 123 | rm -f ./.depend |
| 124 | $(CC) -MM $^ > ./.depend |
| 125 | include .depend |
| 126 | ', sep='') |
| 127 | |
| 128 | # Write it to disk |
| 129 | writeLines(makefile, file.path(pkdev_path,"pkgs",pkgName,"src","Makefile")) |
| 130 | } |