make make-temporary-file use the source location for its template argument

(This involves making make-temporary-file be a macro instead of a procedure.)
This commit is contained in:
Robby Findler 2011-08-29 19:33:40 -05:00
parent d2d02cc7a2
commit d4f4f754f1
3 changed files with 99 additions and 37 deletions

View File

@ -1,5 +1,7 @@
#lang racket/base #lang racket/base
(require "path.rkt") (require "path.rkt"
(for-syntax racket/base
setup/path-to-relative))
(provide delete-directory/files (provide delete-directory/files
copy-directory/files copy-directory/files
@ -37,7 +39,6 @@
other-write-bit other-write-bit
other-execute-bit) other-execute-bit)
(require "private/portlines.rkt") (require "private/portlines.rkt")
;; utility: sorted dirlist so functions are deterministic ;; utility: sorted dirlist so functions are deterministic
@ -83,41 +84,86 @@
(unless (directory-exists? dir) (unless (directory-exists? dir)
(make-directory dir)))) (make-directory dir))))
(define (make-temporary-file [template "mztmp~a"] [copy-from #f] [base-dir #f]) (define-syntax (make-temporary-file stx)
(with-handlers ([exn:fail:contract? (with-syntax ([app (datum->syntax stx #'#%app stx)])
(lambda (x) (syntax-case stx ()
(raise-type-error 'make-temporary-file [x (identifier? #'x) #'make-temporary-file/proc]
"format string for 1 argument" [(_)
template))]) (let ()
(format template void)) (define line (syntax-line stx))
(unless (or (not copy-from) (define col (syntax-column stx))
(path-string? copy-from) (define source (syntax-source stx))
(eq? copy-from 'directory)) (define pos (syntax-position stx))
(raise-type-error 'make-temporary-file (define str-src (cond
"path, valid-path string, 'directory, or #f" [(path? source)
copy-from)) (path->relative-string/library source)]
(unless (or (not base-dir) (path-string? base-dir)) [(string? source)
(raise-type-error 'make-temporary-file source]
"path, valid-path, string, or #f" [else #f]))
base-dir)) (define str-loc (cond
(let ([tmpdir (find-system-path 'temp-dir)]) [(and line col) (format "-~a-~a" line col)]
(let loop ([s (current-seconds)] [pos (format "--~a" pos)]
[ms (inexact->exact (truncate (current-inexact-milliseconds)))]) [else ""]))
(let ([name (let ([n (format template (format "~a~a" s ms))]) (define combined-str
(cond [base-dir (build-path base-dir n)] (cond
[(relative-path? n) (build-path tmpdir n)] [str-src (string-append str-src str-loc)]
[else n]))]) [else (string-append "mztmp" str-loc)]))
(with-handlers ([exn:fail:filesystem:exists? (define sanitized-str
(lambda (x) (regexp-replace*
;; try again with a new name #rx"[<>]"
(loop (- s (random 10)) (regexp-replace* #rx"[\\/:]" combined-str "-")
(+ ms (random 10))))]) ""))
(if copy-from (define max-len 40) ;; must be even
(if (eq? copy-from 'directory) (define not-too-long-str
(make-directory name) (cond
(copy-file copy-from name)) [(< max-len (string-length sanitized-str))
(close-output-port (open-output-file name))) (string-append (substring sanitized-str 0 (- (/ max-len 2) 2))
name))))) "..."
(substring sanitized-str 0 (- (/ max-len 2) 1)))]
[else
sanitized-str]))
#`(app make-temporary-file/proc #,(string-append not-too-long-str "_~a")))]
[(_ . whatever)
#'(app make-temporary-file/proc . whatever)])))
(define make-temporary-file/proc
(let ()
(define (make-temporary-file [template "mztmp~a"] [copy-from #f] [base-dir #f])
(with-handlers ([exn:fail:contract?
(lambda (x)
(raise-type-error 'make-temporary-file
"format string for 1 argument"
template))])
(format template void))
(unless (or (not copy-from)
(path-string? copy-from)
(eq? copy-from 'directory))
(raise-type-error 'make-temporary-file
"path, valid-path string, 'directory, or #f"
copy-from))
(unless (or (not base-dir) (path-string? base-dir))
(raise-type-error 'make-temporary-file
"path, valid-path, string, or #f"
base-dir))
(let ([tmpdir (find-system-path 'temp-dir)])
(let loop ([s (current-seconds)]
[ms (inexact->exact (truncate (current-inexact-milliseconds)))])
(let ([name (let ([n (format template (format "~a~a" s ms))])
(cond [base-dir (build-path base-dir n)]
[(relative-path? n) (build-path tmpdir n)]
[else n]))])
(with-handlers ([exn:fail:filesystem:exists?
(lambda (x)
;; try again with a new name
(loop (- s (random 10))
(+ ms (random 10))))])
(if copy-from
(if (eq? copy-from 'directory)
(make-directory name)
(copy-file copy-from name))
(close-output-port (open-output-file name)))
name)))))
make-temporary-file))
(define (with-pref-params thunk) (define (with-pref-params thunk)
(parameterize ([read-case-sensitive #f] (parameterize ([read-case-sensitive #f]

View File

@ -887,6 +887,12 @@ provided and non-@racket[#f], in which case the
file name generated from @racket[template] is combined with file name generated from @racket[template] is combined with
@racket[directory] to obtain a full path. @racket[directory] to obtain a full path.
The @racket[template] argument's default is only the string @racket["mztmp~a"]
when there is no source location information for the callsite of
@racket[make-temporary-file] (or if @racket[make-temporary-file] is
used in a higher-order position). If there is such information, then the template
string is built based on the source location.
If @racket[copy-from-filename] is provided as path, the temporary file If @racket[copy-from-filename] is provided as path, the temporary file
is created as a copy of the named file (using @racket[copy-file]). If is created as a copy of the named file (using @racket[copy-file]). If
@racket[copy-from-filename] is @racket[#f], the temporary file is @racket[copy-from-filename] is @racket[#f], the temporary file is

View File

@ -295,6 +295,16 @@
(err/rt-test (open-output-file (build-path (current-directory) "baddir" "x")) (err/rt-test (open-output-file (build-path (current-directory) "baddir" "x"))
exn:fail:filesystem?) exn:fail:filesystem?)
(let ([tf (make-temporary-file)])
(let-values ([(base name dir?) (split-path tf)])
(test #t 'make-temporary-file-uses-srcloc (and (regexp-match #rx"file.rktl" (path->bytes name)) #t)))
(delete-file tf))
(let ([tf ((λ (t) (t)) make-temporary-file)])
(test #t 'make-temporary-file-in-ho-position (file-exists? tf))
(delete-file tf))
(define tempfilename (make-temporary-file)) (define tempfilename (make-temporary-file))
(when (file-exists? tempfilename) (when (file-exists? tempfilename)
(delete-file tempfilename)) (delete-file tempfilename))