File: project.R

package info (click to toggle)
r-cran-pkgmaker 0.32.10-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,192 kB
  • sloc: sh: 13; makefile: 2
file content (348 lines) | stat: -rw-r--r-- 12,044 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
# Utils for R package projects
# 
# Author: Renaud Gaujoux
# Created: May 1, 2013
###############################################################################

is_pattern <- function(x){
  grepl('[*^?)$([]', x)
}
# match with exact or regular expression lookup
match_mix <- function(x, table, nomatch = NA_integer_, ignore.case = FALSE){
  
  # find regular expression patterns
  is_reg <- is_pattern(table)
  case <- function(x) if( ignore.case ) tolower(x) else x
  hit <- match(case(x), case(table[!is_reg]), nomatch = nomatch)
  
  if( any(is_reg) ){
    reg_match <- unlist_(sapply(table[is_reg], grep, x, ignore.case = ignore.case, simplify = FALSE))
    reg_match <- reg_match[!duplicated(reg_match)]
    hit[reg_match] <- pmin(hit[reg_match], match(names(reg_match), table), na.rm = TRUE)
    
  }
  
  hit[!is.na(hit)]
}

#' Test for Package Root Directory
#' 
#' Tells if a directory is a package directory, i.e. that it contains
#' a \code{DESCRIPTION} file.
#' 
#' @param x path to the directory to test
#' @param error logical that indicates if an error should be raised
#' if the directory is not a package directory.
#' 
#' @keywords internal
is_package_path <- function(x, error = FALSE) {
	if (is.null(x)) return(FALSE)
	x <- normalizePath(x, mustWork = FALSE)
	x <- gsub("\\\\$", "", x)
	desc_path <- file.path(x, "DESCRIPTION")
	if( !error ){
		file.exists(x) && file.exists(desc_path)
	}else{
		if ( !file.exists(x) ) stop("Can't find directory ", x, call. = FALSE)
		if ( !file.info(x)$isdir ) stop(x, " is not a directory", call. = FALSE)
		if (!file.exists(desc_path)) stop("No DESCRIPTION file found in ", x, call. = FALSE)
		TRUE
	}
}


#' Find Path to Development Package Root Directory
#' 
#' Development packages are looked-up according to rules
#' defined in a file \code{.Rpackages} in the user's home directory. 
#' 
#' @section Specification of package path:
#' Package paths are specified in a list with:
#'   * unnamed elements: character strings give path to directories to lookup for sub-directories that match
#' exactly the package's name;
#'   * named element containing character strings: these are paths that are looked up only for packages that 
#' match the element name. If the element name contains any of the characters `*?()$^\\][`, then it is matched using 
#' regular expression.
#' 
#' @param x name of the development package to lookup.
#' @param error logical that indicates if an error is thrown when the project root directory 
#' could not be found.
#' 
#' @return A character string containing the path to the package.
#' @export
find_devpackage <- function(x, error = TRUE) 
{
	
	
	if (is_package_path(x)) {
		return(x)
	}
	
	config_path <- "~/.Rpackages"
	if (!file.exists(config_path)) {
		return(NULL)
	}
	config_path <- path.expand(config_path)
	lookup <- source(config_path)$value
  
  default_lookup <- lookup$default
  lookup <- lookup[!names(lookup) %in% 'default']
  
  # check for a match
  i <- c(match_mix(x, names(lookup), ignore.case = TRUE), which(names(lookup) %in% ''))
  if( length(i) ){
      reg_spec <- is_pattern(names(lookup))
      for(k in i){
        val <- lookup[[k]]
        n <- names(lookup)[k]
        if( grepl('*', val, fixed = TRUE) ){ # check path like a/b/*/c
          p <- gsub('*', x, val, fixed = TRUE)
          if( is_package_path(p) ){
            message("Loading path resolved by ", n, ':', val)
            return(p)
            
          }
          
        } else if( !reg_spec[k] && is_package_path(val) ){ # exact match
          message("Loading path resolved by ", n, ':', val)
          return(val) 
          
        }else if( reg_spec[k] ){
          paths <- list.dirs(val, full.names = TRUE, recursive = FALSE)
          x2 <- sub(n, "", x)
          x2[!nzchar(x2)] <- x[!nzchar(x2)]
          hit <- match(x2, basename(paths))
          p <- paths[hit[1L]]
          if( length(hit) && is_package_path(p) ){
            message("Loading path resolved by ", n, ':', val)
            return(p)
            
          }
          
        }
      }
      
  }
  
  if( !is.null(default_lookup) ) {
		default_loc <- default_lookup(x)
		if ( is_package_path(default_loc, error = error) ) {
      message("Loading path resolved by default lookup")
			return(default_loc)
		}
	}
  
  if( error ) message("Could not find package directory for project ", x) 
	NULL
}

#' Load Development Package
#' 
#' @param pkg name of the package/project to load.
#' @param reset logical that indicates if the package should be reloaded (passed to \code{\link[devtools]{load_all}}.
#' @param ... other arguments passed to \code{\link[devtools]{load_all}}.
#' @param utests logical that indicates if an environment containing the unit test functions should be created.
#' If \code{TRUE} this environment is accessible at \code{pkgname::UnitTests$test.filename.r$function.name}.
#' @param verbose logical that indicates if log messages should be printed.
#' @param addlib logical that indicates if the \code{lib/} sub-directory, if it exists, should be prepended 
#' to the library path. 
#' This enables to control the version of the loaded dependencies.
#' @param character.only logical that indicates if argument \var{pkg} should be evaluated or taken litteral. 
#' @param try.library logicatl that indicates if projects that could not be found should be looked up in 
#' the installed packages.
#' 
#' @return Invisibly the `package` object of the loaded package.
#' 
#' @export 
load_project <- function(pkg, reset = FALSE, ..., utests = TRUE, verbose=FALSE, addlib=TRUE, character.only = FALSE, try.library = FALSE) {
	
	if( !character.only ){
		pkg <- deparse(substitute(pkg))
		pkg <- sub("^\"(.*)\"$", "\\1", pkg)
	}
	
  # lookup dev package root directory
	devpkg_path <- find_devpackage(pkg, error = !try.library)
  # load from installed pacakges if not found and requested
  if( is.null(devpkg_path) ){
    if( !try.library ) return(invisible())
    message(sprintf("Trying to load installed package %s ... ", pkg), appendLF = FALSE)
    library(pkg, character.only = TRUE, quietly = TRUE)
    message('OK')
    return(invisible())
    
  }
  
	pkg <- devpkg_path 

  if( !requireNamespace('devtools') ){
    stop("Could not load package: required package 'devtools' is not installed.")
  }
  
	# add ../lib to the path if necessary
	if( addlib && is.character(tp <- pkg) ){
		tp <- as.package(tp)
		pdir <- normalizePath(file.path(dirname(tp$path), "lib"), mustWork=FALSE)
		if( file_test('-d', pdir) && !is.element(pdir, .libPaths()) ){
			message("Adding to .libPaths: '", pdir, "'")
			olibs <- .libPaths()
			.libPaths(c(pdir, .libPaths()))
			on.exit( .libPaths(olibs), add=TRUE )
		}
	}
	devpkg <- as.package(pkg)
	
	# load package
	op <- options(verbose=verbose)
	on.exit(options(op), add=TRUE)  
	devtools::load_all(pkg, reset = reset, ...)
	#
	
	# source unit test files if required
	udir <- file.path(devpkg$path, 'inst', c('tests', 'unitTests'))
	if( utests && length(w <- which(file.exists(udir))) ){
		message("# Sourcing unit test directory ... ", appendLF = FALSE)
		f <- list.files(udir[w[1L]], pattern = "\\.[Rr]$", full.names=TRUE)
		if( length(f) ){
#			if( !requireNamespace('RUnit') ) stop("Missing required dependency 'RUnit' to load unit tests")
			# create unit test environment
      ns_env <- getDevtoolsFunction('ns_env')
			utest_env <- new.env(parent = ns_env(devpkg))
			assign('UnitTests', utest_env, ns_env(devpkg))
			# source test files in separate sub-environments
			sapply(f, function(f){
						e <- new.env(parent = utest_env)
						assign(basename(f), e, utest_env)
						sys.source(f, envir = e)
					})
		}
		message('OK [', length(f), ']')
		# reload to export the unit test environment
		devtools::load_all(pkg, reset = FALSE, ...)
	}
	#
	
	invisible(devpkg)
}

#' @describeIn load_project shortcut for `load_project(..., try.library = TRUE)`, to load project
#' code from installed library if not found as a development project.
#' All its arguments are passed to `load_project`.
#' @export
library_project <- function(...) load_project(..., try.library = TRUE)

getDevtoolsFunction <- function(name){
  
  if( qrequire('devtools') && !is.null(fun <- ns_get(name, 'devtools')) ) return(fun)
  if( qrequire('pkgload') && !is.null(fun <- ns_get(name, 'pkgload')) ) return(fun)
  if( qrequire('pkgbuild') && !is.null(fun <- ns_get(name, envir = 'pkgbuild')) ) return(fun)
  
}

is_Mac <- function(check.gui=FALSE){
	is.mac <- (length(grep("darwin", R.version$platform)) > 0)
	# return TRUE is running on Mac (adn optionally through GUI)
	is.mac && (!check.gui || .Platform$GUI == 'AQUA')
}

R_OS <- function(){
	if( is_Mac() ) 'MacOS'
	else .Platform$OS.type
}

packageMakefile <- function(package=NULL, template=NULL, temp = FALSE, print = TRUE){
	
	capture.output(suppressMessages({
		library(pkgmaker)
        if( !requireNamespace('devtools', quietly = TRUE) ) 
                stop("Package 'devtools' is required to generate a package Makefile")
						
	}))
#	defMakeVar <- pkgmaker::defMakeVar
#	subMakeVar <- pkgmaker::subMakeVar
	
	project_path <- getwd()
	project_name <- basename(project_path)
	subproject_path_part <- ''
	if( is.null(package) || isString(package) ){
		if( isString(package) && !nzchar(package) ) package <- NULL
		lookup_dir <- c('pkg', '.')
		if( !is.null(package) ){
			lookup_dir <- c(package, lookup_dir, file.path('pkg', package))
			subproject_path_part <- file.path(package, '')
		}
		pdir <- file.path(lookup_dir, 'DESCRIPTION')
		if( !length(sd <- which(is.file(pdir))) ){
			stop("Could not detect package source directory")
		}
		package <- pdir[sd[1L]]
	}
	package <- normalizePath(package)
	p <- pkg <- as.package(dirname(package));
	pdir <- package_dir <- p[['path']];
	
	## create makefile from template
	# load template makefile
	if( is.null(template) ){
		template <- packagePath('package.mk', package='pkgmaker')
	}
	l <- paste(readLines(template), collapse="\n")
	
	# user
	cuser <- Sys.info()["user"]
	l <- defMakeVar('AUTHOR_USER', cuser, l)
	l <- defMakeVar('R_PACKAGE', pkg$package, l)
	# R_PACKAGE_PATH
	l <- defMakeVar('R_PACKAGE_PATH', package_dir, l)
	# R_PACKAGE_PROJECT
	l <- defMakeVar('R_PACKAGE_PROJECT', project_name, l)
	# R_PACKAGE_PROJECT_PATH
	l <- defMakeVar('R_PACKAGE_PROJECT_PATH', project_path, l)
	l <- defMakeVar('R_PACKAGE_SUBPROJECT_PATH_PART', subproject_path_part, l)
	# R_BIN
	l <- subMakeVar('R_BIN', R.home('bin'), l)
    # REPO_DIRS
    repo_dirs <- gsub("^\\./", "", sapply(c('source', 'win.binary', 'mac.binary'), contrib.url, repos = '.'))
    l <- defMakeVar('REPO_DIRS', paste0(repo_dirs, collapse = ' '), l)
    # BUILD_DIR
    l <- defMakeVar('BUILD_DIR', file.path(repo_dirs['source'], ''), l)
	# R_PACKAGE_TAR_GZ
	pkg_targz <- file.path(repo_dirs['source'], package_buildname(p, 'source'))
	l <- defMakeVar('R_PACKAGE_TAR_GZ', pkg_targz, l)
    # R_PACKAGE_ZIP
	pkg_zip <- file.path(repo_dirs['win.binary'], package_buildname(p, 'win.binary'))
	l <- defMakeVar('R_PACKAGE_ZIP', pkg_zip, l)
    # R_PACKAGE_TGZ
	pkg_mac <- file.path(repo_dirs['mac.binary'], package_buildname(p, 'mac.binary'))
	l <- defMakeVar('R_PACKAGE_TGZ', pkg_mac, l)
	# R_PACKAGE_TYPE	
	l <- defMakeVar('R_PACKAGE_OS', R_OS(), l)
	#

    # auto-conf variables
    init_var <- list(version = pkg$version)
    if( is.dir(file.path(package_dir, 'vignettes')) ) 
        init_var <- c(init_var, has_vignettes=TRUE)
    # dump variables
    if( length(init_var) ){
        init_var <- setNames(init_var, paste0('R_PACKAGE_', toupper(names(init_var))))
        init_var_str <- str_out(init_var, Inf, use.names = TRUE, sep = "\n")
        l <- subMakeVar('INIT_CHECKS', init_var_str, l)
    }
	
	# R_CMD_CHECK
	rlibs <- ''
    if( is.dir(devlib <- file.path(dirname(pdir), 'lib')) ){
		rlibs <- paste0("R_LIBS=", devlib, ' ')
	}
    l <- subMakeVar('R_LIBS', rlibs, l)
	#

	# create makefile
	mk <- if( temp ) tempfile('package_', tmpdir='.', fileext='.mk') else 'package.mk'
	cat(l, file=mk)
	if ( print ){
		cat(mk)
	}
	invisible(l)
}