diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..e47e983 --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,4 @@ +0.1.0 +- Initial split from racket-makefile/rash. +- Depend on racket-makefile, never the other way around. +- Provide Rash line syntax for the first-class make procedure. diff --git a/LICENSE b/LICENSE index 3a87446..152ea81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,18 +1,21 @@ MIT License -Copyright (c) 2026 hans +Copyright (c) 2026 Hans Dijkema -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and -associated documentation files (the "Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the -following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all copies or substantial -portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT -LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO -EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -USE OR OTHER DEALINGS IN THE SOFTWARE. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 8810363..5a39fbf 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,37 @@ # rash-makefile -racket-makefile functionality provided to the rash shell. \ No newline at end of file +`rash-makefile` is the Rash integration layer for `racket-makefile`. +The core `racket-makefile` package deliberately has no dependency on Rash. + +```racket +#lang rash + +(require rash-makefile) + +(makefile demo + (default-target all) + (phony status all) + + (target status + (displayln "status")) + + (target all + (deps status) + (displayln "all"))) +``` + +After Run, Rash line mode accepts: + +```text +make all +make status +``` + +Ordinary Racket expression mode still uses the same first-class procedure: + +```racket +(make 'all) +(values make) +``` + +Bare identifiers in a Rash `make` line are converted to symbols. Parenthesized arguments remain ordinary Racket expressions. diff --git a/examples/Makefile.rkt b/examples/Makefile.rkt new file mode 100644 index 0000000..c6c38c5 --- /dev/null +++ b/examples/Makefile.rkt @@ -0,0 +1,17 @@ +#lang rash + +(require rash-makefile) + +(makefile demo + (default-target status) + (phony status clean all) + + (target status + (displayln "status target")) + + (target clean + (displayln "clean target")) + + (target all + (deps status) + (displayln "all target"))) diff --git a/info.rkt b/info.rkt new file mode 100644 index 0000000..8886484 --- /dev/null +++ b/info.rkt @@ -0,0 +1,17 @@ +#lang info + +(define pkg-authors '(hnmdijkema)) +(define version "0.1.0") +(define license 'MIT) +(define collection "rash-makefile") +(define pkg-desc + "Rash line-syntax integration for racket-makefile.") + +(define scribblings + '(("scrbl/rash-makefile.scrbl" () (library 0)))) + +(define deps + '("base" "racket-makefile" "linea" "rash")) + +(define build-deps + '("racket-doc" "rackunit-lib" "scribble-lib")) diff --git a/main.rkt b/main.rkt new file mode 100644 index 0000000..f19d7b8 --- /dev/null +++ b/main.rkt @@ -0,0 +1,44 @@ +#lang racket/base + +(require (except-in racket-makefile make) + (only-in racket-makefile [make make/procedure]) + (for-syntax racket/base + syntax/parse + linea/line-macro-prop)) + +(provide (except-out (all-from-out racket-makefile) make/procedure) + make) + +;; In an ordinary Racket expression, `make` remains the first-class procedure +;; exported by racket-makefile. In Linea/Rash line syntax the same identifier +;; gets a second meaning: bare target names are converted to symbols before the +;; procedure is called. +(begin-for-syntax + (define (target-argument->expression stx) + (syntax-parse stx + [(quote value) + stx] + [id:id + #`(quote #,(syntax-e #'id))] + [_ + stx])) + + (struct make-line-binding (target) + #:property prop:procedure + (λ (self stx) + (syntax-parse stx + [id:id + (make-line-binding-target self)] + [(id argument ...) + #`(#,(make-line-binding-target self) argument ...)])) + #:property prop:line-macro + (λ (self stx) + (syntax-parse stx + [(_ argument ...) + (define arguments + (map target-argument->expression + (syntax->list #'(argument ...)))) + #`(#,(make-line-binding-target self) #,@arguments)])))) + +(define-syntax make + (make-line-binding #'make/procedure)) diff --git a/scrbl/rash-makefile.scrbl b/scrbl/rash-makefile.scrbl new file mode 100644 index 0000000..66f62e6 --- /dev/null +++ b/scrbl/rash-makefile.scrbl @@ -0,0 +1,45 @@ +#lang scribble/manual + +@(require (for-label racket + racket-makefile + rash-makefile)) + +@title{rash-makefile} +@author["Hans Dijkema / hans@dijkewijk.nl"] + +@defmodule[rash-makefile] + +@tt{rash-makefile} provides the Rash/Linea adapter for @racketmodname[racket-makefile]. The core @tt{racket-makefile} package remains independent of Rash. + +@section{Using the adapter} + +@verbatim{ +#lang rash + +(require rash-makefile) + +(makefile demo + (default-target all) + (phony status all) + + (target status + (displayln "status")) + + (target all + (deps status) + (displayln "all"))) +} + +In Rash line mode, bare target names are converted to symbols: + +@verbatim{ +make status +make all +} + +The ordinary Racket form remains available in the same module: + +@racketblock[ +(make 'status) +(make 'all) +] diff --git a/tests/rash.rkt b/tests/rash.rkt new file mode 100644 index 0000000..62e4150 --- /dev/null +++ b/tests/rash.rkt @@ -0,0 +1,48 @@ +#lang racket + +(require rackunit + racket/file + linea/read) + +(define tmp (make-temporary-file "rash-makefile~a" 'directory)) + +(dynamic-wind + void + (λ () + (define makefile-path (build-path tmp "Makefile.rkt")) + + (call-with-output-file makefile-path + #:exists 'truncate/replace + (λ (out) + (displayln "#lang rash" out) + (displayln "(require rash-makefile)" out) + (displayln "(makefile demo" out) + (displayln " (default-target status)" out) + (displayln " (phony status)" out) + (displayln " (target status" out) + (displayln " (call-with-output-file \"status.out\"" out) + (displayln " #:exists 'truncate/replace" out) + (displayln " (λ (o) (display \"status\" o)))))" out))) + + (define module-path `(file ,(path->string makefile-path))) + + (parameterize ([current-directory tmp]) + (dynamic-require module-path #f)) + (check-false (file-exists? (build-path tmp "status.out"))) + + (define ns (module->namespace module-path)) + (parameterize ([current-directory tmp] + [current-namespace ns]) + (define line + (linea-read-syntax 'drracket-interaction + (open-input-string "make status\n"))) + (eval line) + (check-true (file-exists? (build-path tmp "status.out"))) + (delete-file (build-path tmp "status.out")) + + (eval #'(make 'status)) + (check-true (file-exists? (build-path tmp "status.out"))) + (check-true (eval #'(procedure? make))) + (check-true (eval #'(procedure? makefile-target-demo-status))))) + (λ () + (delete-directory/files tmp #:must-exist? #f)))