So this seems like a bug in mzR which is the main backend / parser for proteomics and metabolomics data. Someone should try to fix this.
Unfortunately, mzR depends on several pieces of software which are written by other people, but bundled with the package. Martin's traceback suggests that some of these files are (partially?) encrypted. This might be hard to address. Best, Kasper On Wed, Sep 21, 2016 at 12:43 PM, Dan Tenenbaum <dtene...@fredhutch.org> wrote: > One thing I notice is that the crash does not happen every time. I have > successfully built the package on morelia by hand with "R CMD build". > > Similarly I can source the stangled vignette without a crash sometimes. > But when it does crash, this is what I see: > > > source("msPurity-vignette.R", echo=TRUE, max=Inf) > > > ## ------------------------------------------------------------ > ------------ > > library(msPurity) > Loading required package: Rcpp > > > msmsPths <- list.files(system.file("extdata", "lcms", "mzML", > package="msPurityData"), full.names = TRUE, pattern = "MSMS") > > > msPths <- list.files(system.file("extdata", "lcms", "mzML", > package="msPurityData"), full.names = TRUE, pattern = "LCMS_") > > > ## ------------------------------------------------------------ > ------------ > > pa <- purityA(msmsPths) > > *** caught segfault *** > address 0x29800000297, cause 'memory not mapped' > > Traceback: > 1: .External(list(name = "CppMethod__invoke_notvoid", address = <pointer: > 0x7fb56861cb10>, dll = list(name = "Rcpp", path = > "/Library/Frameworks/R.framework/Versions/3.3/Resources/library/Rcpp/libs/Rcpp.so", > dynamicLookup = TRUE, handle = <pointer: 0x7fb568606180>, > info = <pointer: 0x1078f8000>), numParameters = -1L), <pointer: > 0x7fb5686ae9c0>, <pointer: 0x7fb56868cf80>, .pointer, ...) > 2: object@backend$getPeakList(x) > 3: FUN(X[[i]], ...) > 4: lapply(X = X, FUN = FUN, ...) > 5: sapply(scans, function(x) object@backend$getPeakList(x)$peaks, > simplify = FALSE) > 6: sapply(scans, function(x) object@backend$getPeakList(x)$peaks, > simplify = FALSE) > 7: .local(object, ...) > 8: mzR::peaks(mr) > 9: mzR::peaks(mr) > 10: getscans(filepth) > 11: assessPuritySingle(filepth = pa@fileList[[i]], mostIntense = > mostIntense, nearest = nearest, offsets = offsets, plotP = plotP, > plotdir = plotdir, interpol = interpol, iwNorm = iwNorm, iwNormFun = > iwNormFun, ilim = ilim) > 12: eval(expr, envir, enclos) > 13: eval(xpr, envir = envir) > 14: doTryCatch(return(expr), name, parentenv, handler) > 15: tryCatchOne(expr, names, parentenv, handlers[[1L]]) > 16: tryCatchList(expr, classes, parentenv, handlers) > 17: tryCatch(eval(xpr, envir = envir), error = function(e) e) > 18: doTryCatch(return(expr), name, parentenv, handler) > 19: tryCatchOne(expr, names, parentenv, handlers[[1L]]) > 20: tryCatchList(expr, classes, parentenv, handlers) > 21: tryCatch({ repeat { args <- nextElem(it) if > (obj$verbose) { cat(sprintf("evaluation # %d:\n", i)) > print(args) } for (a in names(args)) assign(a, args[[a]], pos > = envir, inherits = FALSE) r <- tryCatch(eval(xpr, envir > = envir), error = function(e) e) if (obj$verbose) { > cat("result of evaluating expression:\n") print(r) } > tryCatch(accumulator(list(r), i), error = function(e) { > cat("error calling combine function:\n") print(e) > NULL }) i <- i + 1 }}, error = function(e) { if > (!identical(conditionMessage(e), "StopIteration")) > stop(simpleError(conditionMessage(e), expr))}) > 22: e$fun(obj, substitute(ex), parent.frame(), e$data) > 23: operator(foreach::foreach(i = 1:length(pa@fileList), .packages = > "mzR"), assessPuritySingle(filepth = pa@fileList[[i]], mostIntense = > mostIntense, nearest = nearest, offsets = offsets, plotP = plotP, > plotdir = plotdir, interpol = interpol, iwNorm = iwNorm, > iwNormFun = iwNormFun, ilim = ilim)) > 24: purityA(msmsPths) > 25: eval(expr, envir, enclos) > 26: eval(ei, envir) > 27: withVisible(eval(ei, envir)) > 28: source("msPurity-vignette.R", echo = TRUE, max = Inf) > > Possible actions: > 1: abort (with core dump, if enabled) > 2: normal R exit > 3: exit R without saving workspace > 4: exit R saving workspace > Selection: > > > sessionInfo() > R version 3.3.1 (2016-06-21) > Platform: x86_64-apple-darwin13.4.0 (64-bit) > Running under: OS X 10.9.5 (Mavericks) > > locale: > [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8 > > attached base packages: > [1] parallel stats graphics grDevices utils datasets methods > [8] base > > other attached packages: > [1] xcms_1.49.6 Biobase_2.33.3 ProtGenerics_1.5.1 > [4] BiocGenerics_0.19.2 mzR_2.7.4 msPurity_0.99.6 > [7] Rcpp_0.12.7 > > loaded via a namespace (and not attached): > [1] sapa_2.0-2 magrittr_1.5 ifultools_2.0-4 > [4] MASS_7.3-45 splines_3.3.1 BiocParallel_1.7.8 > [7] lattice_0.20-34 foreach_1.4.3 splus2R_1.2-2 > [10] stringr_1.1.0 fastcluster_1.1.21 plyr_1.8.4 > [13] tools_3.3.1 grid_3.3.1 snow_0.4-1 > [16] iterators_1.0.8 survival_2.39-5 multtest_2.29.0 > [19] doSNOW_1.0.14 Matrix_1.2-7.1 RColorBrewer_1.1-2 > [22] reshape2_1.4.1 S4Vectors_0.11.16 codetools_0.2-14 > [25] MassSpecWavelet_1.39.0 stringi_1.1.1 compiler_3.3.1 > [28] stats4_3.3.1 RANN_2.5 > > ----- Original Message ----- > > From: "Thomas Lawson" <thomas.nigel.law...@gmail.com> > > To: "Martin Morgan" <martin.mor...@roswellpark.org> > > Cc: "bioc-devel" <Bioc-devel@r-project.org> > > Sent: Wednesday, September 21, 2016 8:42:20 AM > > Subject: Re: [Bioc-devel] msPurity build fail on Mac OS X (morelia) > > > Thanks for reply. Some of those errors are a bit cryptic for me also. > > > > I have not heard of the valgrind functionality before in R. I will test a > > few things out with valgrind and hopefully I can pinpoint the error a bit > > more. > > > > Thanks again. > > > > Tom > > > > On Wed, Sep 21, 2016 at 12:50 PM, Martin Morgan < > > martin.mor...@roswellpark.org> wrote: > > > >> On 09/20/2016 05:18 AM, Thomas Lawson wrote: > >> > >>> Hi BioConductor community, > >>> > >>> My package (msPurity) is passing the build on the Linux (*zin1*) and > >>> Windows servers (*moscato1*) but failing on the Mac OS X server > >>> (*morelia*). > >>> Also I cannot seem to replicate the failure either on a local > installation > >>> of Mac OS X (el captain) or with Travis CI. > >>> > >>> I should probably note that for Travis CI I did have to install the > >>> msPurityData dependency directly (without Bioconductor). See line 10 > >>> https://raw.githubusercontent.com/Viant-Metabolomics/msPurit > >>> y/master/.travis.yml > >>> > >>> The error I think is coming from a function I have that uses the > >>> mzR::peaks() function but I am struggling to see why I am getting the > >>> error. > >>> > >>> Any help or suggestions would be really appreciated. > >>> > >> > >> Hi Tom -- > >> > >> This might be fun! > >> > >> You can see from > >> > >> > >> http://bioconductor.org/checkResults/3.4/bioc-LATEST/morelia > >> -R-instpkgs.html > >> > >> (linked from http://bioconductor.org/checkResults/3.4/bioc-LATEST/index > . > >> html '1633' installed packages) that in fact msPurityData is installed. > >> Also, segfaults are rarely the result of missing packages. Instead, it > is > >> likely due to errors in C code of one sort or another. On my linux, I > made > >> sure I was using the 'devel' version of Bioconductor, and that all of my > >> packages were up-to-date via biocLite(). I then checked out msPurity > from > >> svn, changed into the msPurity directory and installed it > >> > >> R CMD INSTALL . > >> > >> then I changed to the vignettes directory, Stangled the source code > >> > >> cd vignettes > >> R CMD Stangle msPurity-vignette.Rmd > >> > >> (by the way, the products of build the package / vignette, > >> msPurity-vignette.R should not be in svn). > >> > >> I then ran the vignette under valgrind > >> > >> msPurity/vignettes$ R -d valgrind -f msPurity-vignette.R > >> > >> leading to > >> > >> > pa <- purityA(msmsPths) > >> ==19611== Mismatched free() / delete / delete [] > >> ==19611== at 0x4C2EDEB: free (in /usr/lib/valgrind/vgpreload_me > >> mcheck-amd64-linux.so) > >> ==19611== by 0x11296DA5: cRamp::cRamp(char const*, bool) > (cramp.cpp:98) > >> ==19611== by 0x1129FC87: RcppRamp::open(char const*, bool) > >> (RcppRamp.cpp:23) > >> ==19611== by 0x112B49C4: Rcpp::CppMethod2<RcppRamp, void, char > const*, > >> bool>::operator()(RcppRamp*, SEXPREC**) (Module_generated_CppMethod.h: > 215) > >> ==19611== by 0x112B0FBF: Rcpp::class_<RcppRamp>:: > invoke_void(SEXPREC*, > >> SEXPREC*, SEXPREC**, int) (class.h:212) > >> ==19611== by 0xED73CA0: CppMethod__invoke_void(SEXPREC*) > >> (Module.cpp:200) > >> ==19611== by 0x4F0DFD0: do_External (dotcode.c:548) > >> ==19611== by 0x4F47F9E: Rf_eval (eval.c:713) > >> ==19611== by 0x4F4A6B7: do_begin (eval.c:1807) > >> ==19611== by 0x4F47D90: Rf_eval (eval.c:685) > >> ==19611== by 0x4F4964C: Rf_applyClosure (eval.c:1135) > >> ==19611== by 0x4F47B6C: Rf_eval (eval.c:732) > >> ==19611== Address 0x1b8a4220 is 0 bytes inside a block of size 400 > alloc'd > >> ==19611== at 0x4C2E0EF: operator new(unsigned long) (in > >> /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) > >> ==19611== by 0x11296949: cRamp::do_ramp(long, eWhatToRead) > >> (cramp.cpp:215) > >> ==19611== by 0x11296D9D: cRamp::cRamp(char const*, bool) > (cramp.cpp:97) > >> ==19611== by 0x1129FC87: RcppRamp::open(char const*, bool) > >> (RcppRamp.cpp:23) > >> ==19611== by 0x112B49C4: Rcpp::CppMethod2<RcppRamp, void, char > const*, > >> bool>::operator()(RcppRamp*, SEXPREC**) (Module_generated_CppMethod.h: > 215) > >> ==19611== by 0x112B0FBF: Rcpp::class_<RcppRamp>:: > invoke_void(SEXPREC*, > >> SEXPREC*, SEXPREC**, int) (class.h:212) > >> ==19611== by 0xED73CA0: CppMethod__invoke_void(SEXPREC*) > >> (Module.cpp:200) > >> ==19611== by 0x4F0DFD0: do_External (dotcode.c:548) > >> ==19611== by 0x4F47F9E: Rf_eval (eval.c:713) > >> ==19611== by 0x4F4A6B7: do_begin (eval.c:1807) > >> ==19611== by 0x4F47D90: Rf_eval (eval.c:685) > >> ==19611== by 0x4F4964C: Rf_applyClosure (eval.c:1135) > >> ==19611== > >> > >> which from http://valgrind.org/docs/manual/mc-manual.html#mc- > manual.rudefn > >> means that memory allocated with new[] is being deallocated with free > >> (rather than delete / delete[] > >> > >> Remarkably, this change to mzR removes this particular valgind problem > >> > >> Index: src/cramp.cpp > >> =================================================================== > >> --- src/cramp.cpp (revision 121179) > >> +++ src/cramp.cpp (working copy) > >> @@ -95,7 +95,7 @@ > >> // if (m_runInfo->m_data.scanCount < 0) { // undeclared > scan > >> count > >> // this will provoke reading of index, which sets scan count > >> rampScanInfo* tmp = getScanHeaderInfo ( 1 ); > >> - free(tmp); > >> + delete(tmp); > >> // } > >> // END HENRY > >> } > >> > >> but doesn't get us out of the woods -- valgrind now complains > >> > >> > > >> > xset <- xcms::xcmsSet(msmsPths) > >> > >> vex: the `impossible' happened: > >> isZeroU > >> vex storage: T total 3029292920 bytes allocated > >> vex storage: P total 640 bytes allocated > >> > >> valgrind: the 'impossible' happened: > >> LibVEX called failure_exit(). > >> > >> host stacktrace: > >> ==20822== at 0x38083F48: ??? (in /usr/lib/valgrind/memcheck-amd > >> 64-linux) > >> ==20822== by 0x38084064: ??? (in /usr/lib/valgrind/memcheck-amd > >> 64-linux) > >> ... > >> sched status: > >> running_tid=1 > >> > >> Thread 1: status = VgTs_Runnable (lwpid 20822) > >> ==20822== at 0x25A294E0: ??? (in /lib/x86_64-linux-gnu/libcrypt > >> o.so.1.0.0) > >> ==20822== by 0x25A086FF: EC_POINT_mul (in > /lib/x86_64-linux-gnu/libcrypt > >> o.so.1.0.0) > >> ==20822== by 0xB67335F: ??? > >> ==20822== by 0xCF7F76F: ??? > >> ==20822== by 0x5EB461A205EFD6FF: ??? > >> ==20822== by 0xC221D2F: ??? > >> ==20822== by 0x25A10E47: EC_KEY_check_key (in > >> /lib/x86_64-linux-gnu/libcrypto.so.1.0.0) > >> ==20822== by 0x25A11260: EC_KEY_set_public_key_affine_coordinates > (in > >> /lib/x86_64-linux-gnu/libcrypto.so.1.0.0) > >> ==20822== by 0x25ACA882: ??? (in /lib/x86_64-linux-gnu/libcrypt > >> o.so.1.0.0) > >> ==20822== by 0x25AC637F: ??? (in /lib/x86_64-linux-gnu/libcrypt > >> o.so.1.0.0) > >> ==20822== by 0x25AC5A33: ??? (in /lib/x86_64-linux-gnu/libcrypt > >> o.so.1.0.0) > >> ==20822== by 0x2599970C: FIPS_mode_set (in > >> /lib/x86_64-linux-gnu/libcrypto.so.1.0.0) > >> ==20822== by 0x25995F89: OPENSSL_init_library (in > >> /lib/x86_64-linux-gnu/libcrypto.so.1.0.0) > >> ==20822== by 0x40104E9: call_init.part.0 (dl-init.c:72) > >> ==20822== by 0x40105FA: call_init (dl-init.c:30) > >> ==20822== by 0x40105FA: _dl_init (dl-init.c:120) > >> ==20822== by 0x4015711: dl_open_worker (dl-open.c:575) > >> ==20822== by 0x4010393: _dl_catch_error (dl-error.c:187) > >> ==20822== by 0x4014BD8: _dl_open (dl-open.c:660) > >> ==20822== by 0x6C80F08: dlopen_doit (dlopen.c:66) > >> ==20822== by 0x4010393: _dl_catch_error (dl-error.c:187) > >> ==20822== by 0x6C81570: _dlerror_run (dlerror.c:163) > >> ==20822== by 0x6C80FA0: dlopen@@GLIBC_2.2.5 (dlopen.c:87) > >> ==20822== by 0x4EA93F0: AddDLL (Rdynload.c:537) > >> ==20822== by 0x4EA996B: R_moduleCdynload (Rdynload.c:917) > >> ==20822== by 0x4F68979: internet_Init (internet.c:79) > >> ==20822== by 0x4F68AF2: R_newsock (internet.c:115) > >> ==20822== by 0x4EF39C0: do_sockconn (connections.c:3196) > >> ==20822== by 0x4F3B587: bcEval (eval.c:5658) > >> ... > >> > >> which frankly is too cryptic for me -- it seems perhaps like a call > >> opening a socket connection is going wrong, but I really don't know if > that > >> is cause or effect, or even relevant to your problem. > >> > >> The bottom line is that this is likely a problem in the C code of one of > >> the packages that you are using. > >> > >> Do you or a colleague have the expertise to work through this? > >> > >> Martin > >> > >> > >>> Kind regards, > >>> Tom > >>> > >>> > >>> https://github.com/Bioconductor-mirror/msPurity > >>> http://bioconductor.org/checkResults/devel/bioc-LATEST/ > >>> msPurity/morelia-buildsrc.html > >>> https://bioconductor.org/packages/devel/bioc/html/msPurity.html > >>> > >>> Travis Mac build report: > >>> https://travis-ci.org/Viant-Metabolomics/msPurity/jobs/159938167 > >>> > >>> > >>> ############################################################ > >>> ################## > >>> ############################################################ > >>> ################## > >>> ### > >>> ### Running command: > >>> ### > >>> ### /Library/Frameworks/R.framework/Versions/Current/Resources/bin/R > >>> CMD build --keep-empty-dirs --no-resave-data msPurity > >>> ### > >>> ############################################################ > >>> ################## > >>> ############################################################ > >>> ################## > >>> > >>> > >>> * checking for file ‘msPurity/DESCRIPTION’ ... OK > >>> * preparing ‘msPurity’: > >>> * checking DESCRIPTION meta-information ... OK > >>> * installing the package to build vignettes > >>> * creating vignettes ...sh: line 1: 62889 Segmentation fault: 11 > >>> '/Library/Frameworks/R.framework/Resources/bin/Rscript' --vanilla > >>> --default-packages= -e "tools::buildVignettes(dir = '.', tangle = > >>> TRUE)" > '/tmp/RtmpCXdH7K/xshellf59d5efb2aea' 2>&1 > >>> ERROR > >>> > >>> *** caught segfault *** > >>> address 0x0, cause 'memory not mapped' > >>> > >>> Traceback: > >>> 1: .External(list(name = "CppMethod__invoke_notvoid", address = > >>> <pointer: 0x7fb83b714be0>, dll = list(name = "Rcpp", path = > >>> "/Library/Frameworks/R.framework/Versions/3.3/Resources/ > >>> library/Rcpp/libs/Rcpp.so", > >>> dynamicLookup = TRUE, handle = <pointer: 0x7fb83b70ed10>, > >>> info = <pointer: 0x1052f30c0>), numParameters = -1L), <pointer: > >>> 0x7fb83b590710>, <pointer: 0x7fb83b5af740>, .pointer, ...) > >>> 2: object@backend$getPeakList(x) > >>> 3: FUN(X[[i]], ...) > >>> 4: lapply(X = X, FUN = FUN, ...) > >>> 5: sapply(scans, function(x) object@backend$getPeakList(x)$peaks, > >>> simplify = FALSE) > >>> 6: sapply(scans, function(x) object@backend$getPeakList(x)$peaks, > >>> simplify = FALSE) > >>> 7: .local(object, ...) > >>> 8: mzR::peaks(mr) > >>> 9: mzR::peaks(mr) > >>> 10: .fun(piece, ...) > >>> 11: (function (i) { piece <- pieces[[i]] if (.inform) { > >>> res <- try(.fun(piece, ...)) if (inherits(res, "try-error")) { > >>> piece <- paste(utils::capture.output(print(piece)), > >>> collapse = "\n") stop("with piece ", i, ": \n", > >>> piece, call. = FALSE) } } else { res <- > >>> .fun(piece, ...) } progress$step() res})(2L) > >>> 12: .Call(loop_apply_, as.integer(n), f, env) > >>> 13: loop_apply(n, do.ply) > >>> 14: llply(.data = pieces, .fun = .fun, ..., .progress = .progress, > >>> .inform = .inform, .parallel = .parallel, .paropts = .paropts) > >>> 15: plyr::alply(files, 1, function(x) { mr <- mzR::openMSfile(x) > >>> scan_peaks <- mzR::peaks(mr) return(scan_peaks)}) > >>> 16: getscans(filepths) > >>> 17: purityX(xset, offsets = c(0.5, 0.5), xgroups = c(1, 2)) > >>> 18: eval(expr, envir, enclos) > >>> 19: eval(expr, envir, enclos) > >>> 20: withVisible(eval(expr, envir, enclos)) > >>> 21: withCallingHandlers(withVisible(eval(expr, envir, enclos)), > >>> warning = wHandler, error = eHandler, message = mHandler) > >>> 22: handle(ev <- withCallingHandlers(withVisible(eval(expr, envir, > >>> enclos)), warning = wHandler, error = eHandler, message = mHandler)) > >>> 23: evaluate_call(expr, parsed$src[[i]], envir = envir, enclos = > >>> enclos, debug = debug, last = i == length(out), use_try = > >>> stop_on_error != 2L, keep_warning = keep_warning, keep_message > >>> = keep_message, output_handler = output_handler) > >>> 24: evaluate(code, envir = env, new_device = FALSE, keep_warning = > >>> !isFALSE(options$warning), keep_message = > >>> !isFALSE(options$message), stop_on_error = if (options$error && > >>> options$include) 0L else 2L, output_handler = > >>> knit_handlers(options$render, options)) > >>> 25: in_dir(opts_knit$get("root.dir") %n% input_dir(), evaluate(code, > >>> envir = env, new_device = FALSE, keep_warning = > >>> !isFALSE(options$warning), keep_message = > >>> !isFALSE(options$message), stop_on_error = if (options$error && > >>> options$include) 0L else 2L, output_handler = > >>> knit_handlers(options$render, options))) > >>> 26: block_exec(params) > >>> 27: call_block(x) > >>> 28: process_group.block(group) > >>> 29: process_group(group) > >>> 30: withCallingHandlers(if (tangle) process_tangle(group) else > >>> process_group(group), error = function(e) { setwd(wd) > >>> cat(res, sep = "\n", file = output %n% "") message("Quitting > >>> from lines ", paste(current_lines(i), collapse = "-"), " > >>> (", knit_concord$get("infile"), ") ") }) > >>> 31: process_file(text, output) > >>> 32: knitr::knit(knit_input, knit_output, envir = envir, quiet = quiet, > >>> encoding = encoding) > >>> 33: rmarkdown::render(file, encoding = encoding, quiet = quiet, envir > >>> = globalenv()) > >>> 34: vweave_rmarkdown(...) > >>> 35: engine$weave(file, quiet = quiet, encoding = enc) > >>> 36: doTryCatch(return(expr), name, parentenv, handler) > >>> 37: tryCatchOne(expr, names, parentenv, handlers[[1L]]) > >>> 38: tryCatchList(expr, classes, parentenv, handlers) > >>> 39: tryCatch({ engine$weave(file, quiet = quiet, encoding = enc) > >>> setwd(startdir) find_vignette_product(name, by = "weave", engine = > >>> engine)}, error = function(e) { stop(gettextf("processing vignette > >>> '%s' failed with diagnostics:\n%s", file, > >>> conditionMessage(e)), domain = NA, call. = FALSE)}) > >>> 40: tools::buildVignettes(dir = ".", tangle = TRUE) > >>> An irrecoverable exception occurred. R is aborting now ... > >>> > >>> [[alternative HTML version deleted]] > >>> > >>> _______________________________________________ > >>> Bioc-devel@r-project.org mailing list > >>> https://stat.ethz.ch/mailman/listinfo/bioc-devel > >>> > >>> > >> > >> This email message may contain legally privileged and/or confidential > >> information. If you are not the intended recipient(s), or the employee > or > >> agent responsible for the delivery of this message to the intended > >> recipient(s), you are hereby notified that any disclosure, copying, > >> distribution, or use of this email message is prohibited. If you have > >> received this message in error, please notify the sender immediately by > >> e-mail and delete this email message from your computer. Thank you. > >> > > > > [[alternative HTML version deleted]] > > > > _______________________________________________ > > Bioc-devel@r-project.org mailing list > > https://stat.ethz.ch/mailman/listinfo/bioc-devel > > _______________________________________________ > Bioc-devel@r-project.org mailing list > https://stat.ethz.ch/mailman/listinfo/bioc-devel > [[alternative HTML version deleted]] _______________________________________________ Bioc-devel@r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/bioc-devel