Racketize the `version' collection.

Also some other style things, and get rid of the redundant "doc.txt".
This commit is contained in:
Eli Barzilay 2012-06-20 12:46:02 -04:00
parent 303aaec2b4
commit 2c058f5f03
6 changed files with 225 additions and 293 deletions

View File

@ -1,4 +1,4 @@
#lang scheme/base
#lang racket/base
(define version-url "http://download.racket-lang.org/version.txt")
(define timeout 30)
@ -13,9 +13,9 @@
(require scheme/tcp)
(define (url->port url)
(define-values (host path)
(define-values [host path]
(apply values (cdr (regexp-match #rx"^http://([^/:@]+)(/.*)$" url))))
(define-values (i o) (tcp-connect host 80))
(define-values [i o] (tcp-connect host 80))
(fprintf o "GET ~a HTTP/1.0\r\nHost: ~a\r\n\r\n" path host)
(flush-output o)
(close-output-port o)
@ -27,29 +27,26 @@
(case-lambda
[(what) `(error ,what)]
[(what more)
`(error ,what
,(cond [(list? more) (format "~a" more)]
[(exn? more) (format "(~a)" (exn-message more))]
[else (format "(~a)" more)]))]))
`(error ,what ,(cond [(list? more) (format "~a" more)]
[(exn? more) (format "(~a)" (exn-message more))]
[else (format "(~a)" more)]))]))
(define (with-timeout timeout thunk)
(define result #f)
(let ([r (sync/timeout timeout
(thread (lambda ()
(set! result
(with-handlers
([void (lambda (e)
(error-value "internal error" e))])
(thunk))))))])
(if r result (error-value "timeout"))))
(define r (sync/timeout timeout
(thread (λ ()
(set! result
(with-handlers
([void (λ (e)
(error-value "internal error" e))])
(thunk)))))))
(if r result (error-value "timeout")))
(define (check-version-raw)
(let/ec escape
(define (err . args) (escape (apply error-value args)))
(define-syntax try
(syntax-rules ()
[(_ expr error-message)
(with-handlers ([void (lambda (e) (err error-message e))]) expr)]))
(define-syntax-rule (try expr error-message)
(with-handlers ([void (λ (e) (err error-message e))]) expr))
;; Get server information, carefully
(define version-info
(parameterize ([current-input-port
@ -60,38 +57,35 @@
(cond [(assq key version-info) => cadr]
[else (err (format "no `~s' in response" key) version-info)]))
(define (getver key)
(let ([ver (get key)])
(if (valid-version? ver)
ver
(err "bad version string from server" key))))
(define ver (get key))
(if (valid-version? ver) ver (err "bad version string from server" key)))
(unless (and (list? version-info)
(andmap (lambda (x)
(and (list? x)
(= 2 (length x))
(symbol? (car x))
(string? (cadr x))))
(andmap (λ (x) (and (list? x)
(= 2 (length x))
(symbol? (car x))
(string? (cadr x))))
version-info))
(err "bad response from server" version-info))
;; Make a decision
(let ([current (version)]
[stable (getver 'stable)]
[recent (getver 'recent)])
(cond
;; we have the newest version (can be > if we have a build from git)
[(version<=? recent current) 'ok]
;; we're stable, but there's a newer version
[(version<=? stable current) `(ok-but ,recent)]
;; new version out -- no alphas or we have an alpha => show recent
[(or (equal? recent stable)
(and (alpha-version? current)
;; but if we have an alpha that is older then the current
;; stable then go to the next case
(version<=? stable current)))
`(newer ,recent)]
;; new version out, we have an outdated stable, there is also an alpha
;; (alternatively, we have an alpha that is older than the current
;; stable)
[else `(newer ,stable ,recent)]))))
(define current (version))
(define stable (getver 'stable))
(define recent (getver 'recent))
(cond
;; we have the newest version (can be > if we have a build from git)
[(version<=? recent current) 'ok]
;; we're stable, but there's a newer version
[(version<=? stable current) `(ok-but ,recent)]
;; new version out -- no alphas or we have an alpha => show recent
[(or (equal? recent stable)
(and (alpha-version? current)
;; but if we have an alpha that is older then the current
;; stable then go to the next case
(version<=? stable current)))
`(newer ,recent)]
;; new version out, we have an outdated stable, there is also an alpha
;; (alternatively, we have an alpha that is older than the current
;; stable)
[else `(newer ,stable ,recent)])))
;; Check the version on the server and compare to our version. Possible return
;; values (message is always a string):

View File

@ -1,47 +0,0 @@
_version_: Racket version checking
==================================
The version collection contains several version-related pieces that
are used by Racket. First, the "check.ss" module provides a single
function:
> (check-version)
This procedure checks the currently available version on the Racket
website (doanload.racket-lang.org), and returns a value that indicates
your current state -- one of these:
* `ok
You're fine.
* `(ok-but ,version)
You have a fine stable version, but note that there is a newer
alpha version available
* `(newer ,version)
You have an old version, please upgrade to `version'
* `(newer ,version ,alpha)
You have an old-but-stable version, please upgrade to `version';
you may consider also the newer alpha version
* `(error ,message [,additional-info])
An error occurred, the message is a string that indicates the
error.
The third (optional) value is a string that can be shown as the
system error that happened. This string will always be
parenthesized, so `message' is a short error and
`(string-append message " " additional-info)' is a verbose one.
The second functionality that is provided by this collection is in the
_patchlevel_ module. This module provides a single value:
> patchlevel
which is an integer that indicates the current patch level. This is
normally zero, but may be updated by patches to DrRacket.
Finally, the "tool.rkt" makes DrRacket use both features:
* the patchlevel appears as a version `pN' suffix in DrRacket (but the
binary version as reported by `(version)' is not changed);
* it is possible to periodically check whether a new Racket
distribution is available for download.

View File

@ -1,5 +1,5 @@
;; This file contains the current patch level of Racket.
;; It is usually `0' in the repository, changes only when a patch is made.
#lang scheme/base
#lang racket/base
(define patchlevel 0)
(provide patchlevel)

View File

@ -1,6 +1,6 @@
#lang scheme/gui
#lang racket/gui
(require scheme/unit scheme/class framework drscheme/tool
(require racket/unit racket/class framework drracket/tool
browser/external string-constants
"patchlevel.rkt"
"check.rkt")
@ -29,10 +29,10 @@
(define top (and (pair? top?) (car top?)))
;; wait until the definitions are instantiated, return top-level window
(define (wait-for-definitions)
(let ([ws (get-top-level-windows)])
(if (null? ws)
(begin (sleep 1) (wait-for-definitions))
(car ws))))
(define ws (get-top-level-windows))
(if (null? ws)
(begin (sleep 1) (wait-for-definitions))
(car ws)))
#| ;; Cute code, but may resize the window if too much space, and people
;; didn't like this way of asking if you want update checks.
;; show a message and a disable button
@ -40,112 +40,108 @@
(define (show-message first-time?)
;; No info display if we got some non-drscheme window by accident
(cond
[(with-handlers ([void (lambda _ #f)]) (send top get-info-panel)) =>
(lambda (info)
(sleep 3) ; wait to make this appearance visible
(let* ([-check "Checking for updates..."]
[-about "About to auto-check for updates, you can"]
[p (make-object horizontal-panel% info)]
[m (make-object message% (if first-time? -about -check) p)]
[b (make-object button% "Disable" p disable)])
(send info change-children (lambda (l) (cons p (remq p l))))
(when first-time?
(let ([m1 (make-object message% "these checks" p)])
(sleep 20)
(send p change-children (lambda (l) (remq m1 l))))
(send m set-label -check))
(sleep 2) ; wait before and after check to make it visible
(set! hide-message
(lambda now?
(unless (and (pair? now?) (car now?)) (sleep 1))
(send info change-children (lambda (l) (remq p l)))
(set! hide-message void))))
#t)] ; return #t so that the check starts
[(with-handlers ([void (λ _ #f)]) (send top get-info-panel)) =>
(λ (info)
(sleep 3) ; wait to make this appearance visible
(define -check "Checking for updates...")
(define -about "About to auto-check for updates, you can")
(define p (make-object horizontal-panel% info))
(define m (make-object message% (if first-time? -about -check) p))
(define b (make-object button% "Disable" p disable))
(send info change-children (λ (l) (cons p (remq p l))))
(when first-time?
(define m1 (make-object message% "these checks" p))
(sleep 20)
(send p change-children (λ (l) (remq m1 l)))
(send m set-label -check))
(sleep 2) ; wait before and after check to make it visible
(set! hide-message
(λ ([now? #f])
(unless now? (sleep 1))
(send info change-children (λ (l) (remq p l)))
(set! hide-message void)))
#t)] ; return #t so that the check starts
[else #f])) ; no standard window -- return #f to skip the whole thing
|#
;; show results in a dialog in a non-modal dialog (if it was not an
;; explicit call) , so the window can be left around as a reminder.
(define (message style fmt . args)
(define (run)
(let-values ([(result new-enabled?)
(message+check-box/custom
(string-constant version:results-title)
(apply format fmt args)
(string-constant version:do-periodic-checks)
(string-constant ok)
(and (eq? 'newer style)
(string-constant version:take-me-there))
#f
(and explicit? top)
`(,@(case style
[(#f) '()] [(newer) '(stop)] [else (list style)])
,@(if enabled? '(checked) '())
default=1))])
(unless (eq? enabled? new-enabled?)
(preferences:set 'updates:enabled? (if new-enabled? 'yes 'no))
(set! enabled? new-enabled?))
result))
(define-values [result new-enabled?]
(message+check-box/custom
(string-constant version:results-title)
(apply format fmt args)
(string-constant version:do-periodic-checks)
(string-constant ok)
(and (eq? 'newer style) (string-constant version:take-me-there))
#f
(and explicit? top)
`(,@(case style
[(#f) '()] [(newer) '(stop)] [else (list style)])
,@(if enabled? '(checked) '())
default=1)))
(unless (eq? enabled? new-enabled?)
(preferences:set 'updates:enabled? (if new-enabled? 'yes 'no))
(set! enabled? new-enabled?))
result)
(if explicit?
(run)
;; non-modal
(parameterize ([current-eventspace (make-eventspace)]) (run))))
;; main checker
(define (check)
(let ([result #f])
;; run the check in a thread, with a chance to abort it
(let* ([d #f]
[t (thread (lambda ()
(set! result (check-version))
(when d (send d show #f))))])
(unless (sync/timeout .4 t) ; still checking, pop message
(when explicit? ; unless it's an automatic check
(queue-callback
(lambda ()
(set! d (new (class dialog%
(super-new
[label (string-constant version:update-check)]
[parent #f])
(make-object message%
(string-constant version:connecting-server)
this)
(make-object button%
(string-constant abort) this
(lambda (b e)
(kill-thread t)
(send this show #f))
'(border))
(send this center))))
(send d show #t)))
(sleep/yield .5))
(thread-wait t)))
(cond
[(and (pair? result) (eq? 'newer (car result)))
(when (equal? 2 (message 'newer "Racket v~a ~a ~a"
(cadr result)
(string-constant version:now-available-at)
download-url))
;; 2 = go there
(send-url download-url)
;; (sleep 1) ((application-quit-handler))
)]
;; implicit auto-check => show a message only if there is a newer
;; version => the rest are only for explicit calls
[(not explicit?) (void)]
[(eq? result 'ok)
(message #f (string-constant version:plt-up-to-date))]
[(not (pair? result)) (void)] ; either #f (canceled) or ok
[else (case (car result)
[(error)
(message 'stop "~a: ~a~a"
(string-constant error) (cadr result)
(if (pair? (cddr result))
(string-append "\n" (caddr result)) ""))]
[(ok-but)
(message 'caution "~a,\n~a (v~a)"
(string-constant version:plt-up-to-date)
(string-constant version:but-newer-alpha)
(cadr result))]
[else (error 'check-for-updates "internal error")])])))
(define result #f)
;; run the check in a thread, with a chance to abort it
(let ([d #f])
(define t (thread (λ () (set! result (check-version))
(when d (send d show #f)))))
(unless (sync/timeout .4 t) ; still checking, pop message
(when explicit? ; unless it's an automatic check
(queue-callback
(λ ()
(set! d (new (class dialog%
(super-new
[label (string-constant version:update-check)]
[parent #f])
(make-object message%
(string-constant version:connecting-server)
this)
(make-object button%
(string-constant abort) this
(λ (b e) (kill-thread t) (send this show #f))
'(border))
(send this center))))
(send d show #t)))
(sleep/yield .5))
(thread-wait t)))
(cond
[(and (pair? result) (eq? 'newer (car result)))
(when (equal? 2 (message 'newer "Racket v~a ~a ~a"
(cadr result)
(string-constant version:now-available-at)
download-url))
;; 2 = go there
(send-url download-url)
;; (sleep 1) ((application-quit-handler))
)]
;; implicit auto-check => show a message only if there is a newer
;; version => the rest are only for explicit calls
[(not explicit?) (void)]
[(eq? result 'ok)
(message #f (string-constant version:plt-up-to-date))]
[(not (pair? result)) (void)] ; either #f (canceled) or ok
[else (case (car result)
[(error)
(message 'stop "~a: ~a~a"
(string-constant error) (cadr result)
(if (pair? (cddr result))
(string-append "\n" (caddr result)) ""))]
[(ok-but)
(message 'caution "~a,\n~a (v~a)"
(string-constant version:plt-up-to-date)
(string-constant version:but-newer-alpha)
(cadr result))]
[else (error 'check-for-updates "internal error")])]))
;; start the check if enabled and enough time passed
(when (or explicit? enabled?)
(unless top (set! top (wait-for-definitions)))
@ -163,25 +159,25 @@
(define (phase1) (void))
(define (phase2)
(preferences:add-to-warnings-checkbox-panel
(lambda (panel)
(let ([b (make-object check-box%
(string-constant version:do-periodic-checks)
panel
(lambda (b e)
(preferences:set 'updates:enabled?
(if (send b get-value) 'yes 'no))))])
(preferences:add-callback
'updates:enabled?
(lambda (p v) (send b set-value (is-enabled? v))))
(send b set-value
(is-enabled? (preferences:get 'updates:enabled?))))))
(λ (panel)
(define b
(make-object check-box%
(string-constant version:do-periodic-checks)
panel
(λ (b e) (preferences:set 'updates:enabled?
(if (send b get-value) 'yes 'no)))))
(preferences:add-callback
'updates:enabled?
(λ (p v) (send b set-value (is-enabled? v))))
(send b set-value
(is-enabled? (preferences:get 'updates:enabled?)))))
(drscheme:get/extend:extend-unit-frame
(lambda (f%)
(λ (f%)
(class f%
(define/override (help-menu:after-about m)
(make-object menu-item%
(string-constant version:update-menu-item) m
(lambda (b e) (check-for-updates this)))
(λ (b e) (check-for-updates this)))
(super help-menu:after-about m))
(super-new))))
(thread check-for-updates))

View File

@ -1,4 +1,4 @@
#lang scheme/base
#lang racket/base
(provide valid-version? version->list version<? version<=? alpha-version?
version->integer)
@ -17,16 +17,15 @@
;; returns a list of 4 integers (see src/mzscheme/src/schvers.h)
(define (version->list str)
(let ([ver (map string->number (regexp-split #rx"[.]" str))])
(case (length ver)
[(2) (append ver '(0 0))]
[(3) (append ver '(0))]
[(4) ver]
[else (error 'version->list "bad version: ~e" str)])))
(define ver (map string->number (regexp-split #rx"[.]" str)))
(case (length ver)
[(2) (append ver '(0 0))]
[(3) (append ver '(0))]
[(4) ver]
[else (error 'version->list "bad version: ~e" str)]))
(define (version<? a b)
(let loop ([a (version->list a)]
[b (version->list b)])
(let loop ([a (version->list a)] [b (version->list b)])
(cond [(null? a) #f]
[(< (car a) (car b)) #t]
[(> (car a) (car b)) #f]
@ -36,10 +35,10 @@
(or (equal? a b) (version<? a b)))
(define (alpha-version? v)
(let ([l (version->list v)])
(or ((list-ref l 1) . >= . 90)
((list-ref l 2) . >= . 900)
((list-ref l 3) . >= . 900))))
(define l (version->list v))
(or ((list-ref l 1) . >= . 90)
((list-ref l 2) . >= . 900)
((list-ref l 3) . >= . 900)))
;; returns an integer representing the version (XXYYZZZWWW) or #f if invalid
;; works for pre v4 versions too
@ -54,20 +53,19 @@
[(< n 49) ver]
;; old versions (earliest useful is 49, changed at 3.99)
[(<= 49 n 379)
(let*-values
([(q r) (quotient/remainder n 100)]
[(sfx) (substring ver (cdar m))]
[(sfx) (cond [(equal? sfx "") ""]
;; NNNpN -> N.NN.N
[(regexp-match? #rx"^p[0-9]" sfx)
(string-append "." (substring sfx 1))]
;; NNN.N -> N.NN.0.N (not a release version)
[(regexp-match? #rx"^[.]" sfx)
(string-append ".0" sfx)]
[else #f])])
(and sfx (format "~a.~a~a" q r sfx)))]
(define-values [q r] (quotient/remainder n 100))
(define sfx (let ([sfx (substring ver (cdar m))])
(cond [(equal? sfx "") ""]
;; NNNpN -> N.NN.N
[(regexp-match? #rx"^p[0-9]" sfx)
(string-append "." (substring sfx 1))]
;; NNN.N -> N.NN.0.N (not a release version)
[(regexp-match? #rx"^[.]" sfx)
(string-append ".0" sfx)]
[else #f])))
(and sfx (format "~a.~a~a" q r sfx))]
;; bad strings
[else #f]))
(and v (valid-version? v)
(foldl (lambda (ver mul acc) (+ ver (* mul acc))) 0
(foldl (λ (ver mul acc) (+ ver (* mul acc))) 0
(version->list v) '(0 100 1000 1000))))

View File

@ -12,8 +12,8 @@
@title{Version: Racket Version Checking}
The version collection contains several version-related pieces that
are used by Racket. See also @racket[version] from
The @racketmodname[version] collection contains several version-related
pieces that are used by Racket. See also @racket[version] from
@racketmodname[racket/base].
@; ----------------------------------------------------------------------
@ -39,36 +39,34 @@ Checks the currently available version on the PLT website
(@selflink["http://download.racket-lang.org"]) and returns a value that
indicates the current state of the curent installation:
@itemize[
@itemize[
@item{@racket[`ok] --- You're fine.}
@item{@racket[`ok] --- You're fine.}
@item{@racket[`(ok-but ,_version)] --- You have a fine stable
version, but note that there is a newer alpha version available
numbered @racket[_version].}
@item{@racket[`(ok-but ,_version)] --- You have a fine stable
version, but note that there is a newer alpha version available
numbered @racket[_version].}
@item{@racket[`(newer ,_version)] --- You have an old
version. Please upgrade to @racket[_version].}
@item{@racket[`(newer ,_version)] --- You have an old
version. Please upgrade to @racket[_version].}
@item{@racket[`(newer ,_version ,_alpha)] --- You have an
old-but-stable version, please upgrade to @racket[_version]; you
may consider also the newer alpha version numbered
@racket[_alpha].}
@item{@racket[`(newer ,_version ,_alpha)] --- You have an
old-but-stable version, please upgrade to @racket[_version]; you
may consider also the newer alpha version numbered
@racket[_alpha].}
@item{@racket[`(error ,_message)] --- An error occurred, and
@racket[_message] is a string that indicates the error.}
@item{@racket[`(error ,_message)] --- An error occurred, and
@racket[_message] is a string that indicates the error.}
@item{@racket[`(error ,_message ,_additional-info)] --- An error
occurred; @racket[_message] is a string that indicates the
error, and @racket[_additional-info] is a string containing a
system error. The @racket[_additional-info] content is always
parenthesizes, so @racket[message] is a short error and
@racket[(string-append message " " additional-info)] is a
verbose one.}
@item{@racket[`(error ,_message ,_additional-info)] --- An error
occurred; @racket[_message] is a string that indicates the
error, and @racket[_additional-info] is a string containing a
system error. The @racket[_additional-info] content is always
parenthesizes, so @racket[message] is a short error and
@racket[(string-append message " " additional-info)] is a
verbose one.}
]
}
]}
@; ----------------------------------------------------------------------
@ -80,11 +78,11 @@ The @racket[version/tool] library implements a DrRacket tool that
@itemize[
@item{makes the patchlevel display as a version @tt{p}@nonterm{N}
@item{makes the patchlevel display as a version @tt{p}@nonterm{N}
suffix in DrRacket (though the base verion reported by
@racket[(version)] is not changed);}
@item{if enabled by the user, periodically checks whether a
@item{if enabled by the user, periodically checks whether a
new Racket distribution is available for download.}
]
@ -94,48 +92,41 @@ The @racket[version/tool] library implements a DrRacket tool that
@section{Version Utilities}
@defmodule[version/utils]{
The @racketmodname[version/utils] library provides a few of convenient
utilities for dealing with version strings. Unless explicitly noted,
these functions do not handle legacy versions of Racket.}
The @racketmodname[version/utils] library provides a few of convenient
utilities for dealing with version strings. Unless explicitly noted,
these functions do not handle legacy versions of Racket.}
@defproc[(valid-version? [str string?]) boolean?]{
Returns @racket[#t] if @racket[str] is a valid Racket version
string, @racket[#f] otherwise.}
Returns @racket[#t] if @racket[str] is a valid Racket version
string, @racket[#f] otherwise.}
@defproc[(version->list [str valid-version?])
(list integer? integer? integer? integer?)]{
Returns a list of four numbers that the given version string
represent. @racket[str] is assumed to be a valid version.}
Returns a list of four numbers that the given version string
represent. @racket[str] is assumed to be a valid version.}
@defproc[(version<? [str1 valid-version?] [str2 valid-version?]) boolean?]{
Returns @racket[#t] if @racket[str1] represents a version that is
strictly smaller than @racket[str2], @racket[#f] otherwise.
@racket[str1] and @racket[str2] are assumed to be valid versions.}
Returns @racket[#t] if @racket[str1] represents a version that is
strictly smaller than @racket[str2], @racket[#f] otherwise.
@racket[str1] and @racket[str2] are assumed to be valid versions.}
@defproc[(version<=? [str1 valid-version?] [str2 valid-version?]) boolean?]{
Returns @racket[#t] if @racket[str1] represents a version that is
smaller than or equal to @racket[str2], @racket[#f] otherwise.
@racket[str1] and @racket[str2] are assumed to be valid versions.}
Returns @racket[#t] if @racket[str1] represents a version that is
smaller than or equal to @racket[str2], @racket[#f] otherwise.
@racket[str1] and @racket[str2] are assumed to be valid versions.}
@defproc[(alpha-version? [str valid-version?]) boolean?]{
Returns @racket[#t] if the version that @racket[str] represents is an
alpha version. @racket[str] is assumed to be a valid version.}
Returns @racket[#t] if the version that @racket[str] represents is an
alpha version. @racket[str] is assumed to be a valid version.}
@defproc[(version->integer [str string?]) (or/c integer? false/c)]{
Converts the version string into an integer. For version
@racket["X.YY.ZZZ.WWW"], the result will be @racketvalfont{XYYZZZWWW}.
This function works also for legacy Racket versions, by
translating @racket["XYY.ZZZ"] to @racketvalfont{XYYZZZ000}. The
resulting integer can thefore be used to conveniently compare any two
(valid) version strings. If the version string is invalid the
resulting value is @racket[#f].
Converts the version string into an integer. For version
@racket["X.YY.ZZZ.WWW"], the result will be @racketvalfont{XYYZZZWWW}.
This function works also for legacy Racket versions, by
translating @racket["XYY.ZZZ"] to @racketvalfont{XYYZZZ000}. The
resulting integer can thefore be used to conveniently compare any two
(valid) version strings. If the version string is invalid the
resulting value is @racket[#f].
Note that this is the only function that deals with legacy version
strings.}
Note that this is the only function that deals with legacy version
strings.}