Skip to content

Instantly share code, notes, and snippets.

@greggirwin
Created April 7, 2018 00:25
Show Gist options
  • Save greggirwin/9e6dc837daa7218faee81b81c57f520f to your computer and use it in GitHub Desktop.
Save greggirwin/9e6dc837daa7218faee81b81c57f520f to your computer and use it in GitHub Desktop.
Old R2 MAKE dialect experiment
REBOL []
do %rmake.r
change-dir %./test-files/
spec-1: [
target %xxx.bld
depends on [%xxx-0.txt %xxx-1.txt]
made by [
print ["rebuilding" _target "from" _source]
;print mold _spec/:_target/sources
write _target rejoin [now/time/precise newline read _source]
]
target %xxx-1.txt
depends on %xxx-2.txt
made by [
print ["rebuilding" _target "from" _source]
write _target rejoin [_target tab now/time/precise newline read _source]
]
target %xxx-2.txt
depends on %xxx-3.txt
made by [
print ["rebuilding" _target "from" _source]
;print mold _spec/:_target/sources
write _target rejoin [_target tab now/time/precise newline read _source]
]
]
;res: rmake/build-spec spec-1
;print mold res
rmake/build spec-1 ;res
;rmake/build/part res %xxx.bld
;rmake/build/part res %xxx-2.txt
;-------------------------------------------------------------
; make-doc-pro launcher
mdp: %make-doc-pro.r
;secure [net allow file [allow read allow write ask execute]]
run-mdp: [
print ["rebuilding" _target "from" _source]
do/args mdp get-modes _source 'full-path
]
; I'm not sure if I like having to compose and reduce things to use
; external blocks, but that's what I had to do here for now. Look at
; the first target below for an example.
mdp-spec: compose [
target %mdp-test.html
depends on %mdp-test.txt
made by (reduce [run-mdp])
target %mdp-test2.html
depends on %mdp-test2.txt
made by [
print ["rebuilding" _target "from" _source]
do/args mdp get-modes _source 'full-path
]
]
;res: rmake/build-spec mdp-spec
;print mold res
;rmake/build mdp-spec ;res
;rmake/build/part mdp-spec %mdp-test2.html
;rmake/build/part mdp-spec [%mdp-test.html %mdp-test2.html]
mdp-spec: compose [
target %mdp-test.html depends on %mdp-test.txt made by (reduce [run-mdp])
target %mdp-test2.html depends on %mdp-test2.txt made by (reduce [run-mdp])
]
rmake/build/all-targets mdp-spec
change-dir %..
halt
REBOL [
Title: "rmake"
File: %rmake.r
Author: "Gregg Irwin"
EMail: [email protected]
Version: 0.0.4
Copyright: "Copyright © 2002 Pointillistic Software. All Rights Reserved."
history: [
0.0.1 [
{Initial test version. Handles at least a couple simple test cases.}
]
0.0.2 [
{Added some more smarts and the _spec, _target, and _source words
for accessing what's currently being processed.}
]
0.0.3 [
{BUILD now takes a spec directly so you don't have to build it with
build-spec first. It will do that for you. If you pre-build your
spec, use the /no-parse refinement to tell BUILD not to parse it.}
]
0.0.4 [
{Changed storage for object level vars in recursive call.
It uses a stack now.}
]
]
comment: {
'_spec, '_target, and '_source can be used in your actions to refer to
the current spec, target, and source being processed.
}
]
pull: func [
"Remove and return items from a series."
series [series! none!]
/tail "Pull items from the tail end"
/part "The number of items to pull; one is the default"
range [number!]
/local result
] [
if none? series [return none]
if tail [series: skip system/words/tail series negate any [range 1]]
result: either part [copy/part series range] [pick series 1]
either part [remove/part series range][remove series]
result
]
rmake: make object! [
; Current spec, target, and source in BUILD function. These are here so
; you can reference them in your rules.
_spec:
_target: ; $@ $$@
_source: ; $<
none
; Working vars for dialect parser
spec: copy []
target: sources: actions: none
; used during nested calls
stack: copy []
frame: [_target _source]
push: does [dent-in repend stack frame]
pop: does [dent-out set frame pull/tail/part stack length? frame]
newer?: func [
{Returns true if target-1 was modified more recently than target-2;
false otherwise.}
target-1 [file! url!]
target-2 [file! url!]
][
if not exists? target-1 [return false]
either any [
all [(not none? modified? target-1) (none? modified? target-2)]
(greater? modified? target-1 modified? target-2)
] [true] [false]
]
dent-in: does [insert dump-dent #"^-"]
dent-out: does [remove dump-dent]
dump-dent: copy ""
show-trace: func [val] [
print [dump-dent reduce val]
]
;== START PARSE RULES ==================================
action-rule: ['made 'by set actions [word! | block!]]
source-rule: ['depends 'on set sources [file! | block!]]
; If we wanted to allow, like MAKE, the ability to add dependencies
; to a task in multiple places, I think we should only need to alter
; this to find the existing target in the spec and add to it, rather
; than adding it as a new target.
target-rule: [
'target set target [word! | file!] source-rule action-rule (
append spec target
; The COMPOSE with brackets here forces sources and actions
; to become blocks so we can always FOREACH or DO them.
append/only spec compose/deep [sources [(sources)] actions [(actions)]]
sources: actions: none
)
]
;-- This is the top level rule.
make-rule: [some target-rule]
;== END PARSE RULES ==================================
;-- Build a dependency/action table and return it. Note that there is no
; reason you can't just define the table yourself. Using the dialect may
; be better for documentation, and certainly better for non-programmers,
; though it is a little more verbose.
build-spec: func [
{Build a dependency spec from a MAKE dialect spec.}
make-spec [block!] "The spec for the table, given in the rmake dialect"
][
clear spec
target: sources: actions: none
parse make-spec make-rule
return spec
]
; This uses a depth-first, recursive approach. It does not track
; visited nodes or do anything particularly smart like a real
; topological sort.
build: func [
{Builds the target, using the make-spec as its dependency guide.
If no target is given, the first one in the spec is used.}
make-spec {Spec containing dependencies. Most likely this will
have been built by the build-spec function.}
/part {Build just the specified target(s).}
targets [file! block!]
/all-targets {Force a build of all targets in spec.}
/no-parse {Don't parse make-spec. It's preformatted, either by
being run through build-spec or by hand.}
/trace
][
_spec: either no-parse [make-spec] [build-spec make-spec]
;print ["***" mold _spec]
if all-targets [targets: extract _spec 2]
if all [(not all-targets) (not part)] [
;print ["&&&" mold make-spec]
targets: first _spec
]
;print ["%%%" mold _spec]
targets: compose [(targets)]
;print ["build" mold targets]
foreach target targets [
_target: target
if trace [show-trace ['TARGET: _target]]
;print mold find/skip _spec target 2
either find/skip _spec target 2 [
; Pass 1: Check to see if any sources need to be built.
foreach source _spec/:target/sources [
_source: source
; Check each source to see if it's also a target that might
; need to be rebuilt.
if find/skip _spec source 2 [
; This is a recursive call. We have to store and
; reset our object level vars; the recursive call
; changes them. Yeah, I know, make it properly
; reentrant.
push
either trace [
build/no-parse/part/trace _spec source
][
build/no-parse/part _spec source
]
pop
]
]
; Pass 2: Check to see if the original target now needs to be built.
;print [target _target mold _spec/:_target/sources]
foreach source _spec/:target/sources [
_source: source
if trace [show-trace ['CHECK 'source: _source 'target: _target mold what-dir]]
if newer? source target [
if trace [show-trace ['ACTION 'source: _source 'target: _target mold what-dir]]
;print [tab "do" mold make-spec/:_target/actions]
do bind/copy _spec/:_target/actions 'self
if trace [show-trace ['ACTION-COMPLETE]]
break ; only need to build it once.
]
]
][
print ["Target" mold _target "doesn't exist in spec" mold _spec]
]
]
]
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment