|
|
||
|---|---|---|
| test | Imported the project from dev branch. | |
| .gitignore | Minor tweaks: | |
| AUTHORS | Imported the project from dev branch. | |
| jon.rb | Imported the project from dev branch. | |
| Jonfile | Imported the project from dev branch. | |
| LICENSE | Imported the project from dev branch. | |
| README.md | Minor tweaks: | |
Jon: A Minimal Rake-like Tool
Jon is a Make-like program written in and for Backpack Ruby. It is modeled after Rake, which is also a Make-like program written in Ruby.
More specifically, jon lets you define a set of tasks (i.e. named Ruby code blocks) which may specify other tasks as dependencies, then execute the one you specify on the command line after first recursively executing all of the dependencies. A task may be associated with a file, in which case (as with Make) it can be made to run only if a dependant file is newer than it.
Running jon
When run with no arguments jon attempts to load Jonfile and run
target default.
You can specify an alternate jonfile and/or target on the command-line:
jon -f project.jon test
You can also set environment variables on the command line:
jon -f project.jon CC=gcc test
--tasks lists all tasks:
jon -f project.jon --tasks
jon -f project.jon --tasks --all # includes undocumented tasks
And, of course, --help will give you the complete summary.
One caution: if the command-line dependency is the name of a file, it must contain at least one "non-word" character. Most filenames tend to contain a '.' so this is usually not a problem, but if it is, you will need to use something like './my_filename'.
Example
desc "Build and test jon"
task :default => [:build, :test] do
puts "Done."
end
desc "Build jon"
task build: ["jon"]
file "jon" => ["jon.rb"] do
sh "rm -f jon"
sh "monolith --strip jon.rb"
end
desc "Run all tests"
task test: [:build] do
sh "mruby test/runner.rb"
end
end
desc "Delete all build products"
task :clean do
sh "rm -f test/testfiles/*.tmp "
sh "rm -f jon *.mrb"
end
Specification
The Jonfile DSL
The Jonfile DSL defines three functions for setting up tasks. They can be called anywhere but will result in a runtime error if invoked after the initial import of the Jonfile.
They are:
task
task is used to define a basic (named) task:
task :do_thing, [:dependency] do
... ruby code ...
end
The first argument is the task name. It MUST be a symbol, not a string.
The second argument is the list of dependencies. They may be symbols
or strings, but these are not interchangeable. A symbol is always a
named task and a string is always a file task. That is, 'foo'
and :foo are different dependencies.
You may specify a file (string) dependency for a file that is not declared anywhere else as long as that file exists. If it does not exist, it is an error.
The result of the block is ignored. To make a block fail, use
taskfail().
task accepts two signatures:
task :name, [...dependencies...] { body }
task name: [...dependencies...] { body }
Note that the array of dependencies and the block are both optional.
file
file is like task but is used to define a file task:
file `foo.o`, ["foo.c"] do
sh "gcc -c foo.o foo.c"
end
It is mostly identical to task but:
- The name is always a string containing a valid filename.
- The block MUST create or modify the file.
file accepts the same signatures as task, but because the name
cannot be a symbol, the second form looks like this:
file `foo.o` => ["foo.c"] { ... }
desc
desc takes one argument, a string, which is then used as the
description for the next task to be defined:
desc "do the thing"
task :do_the_thing { ... }
The string should be one short line.
These will be printed when running jon --tasks and select which
tasks are printed when --all is not used.
Runtime Functions
jon provides the following functions for use in task blocks. These
are available anywhere in the Jonfile.
taskfail "message"
taskfail is used to signal that the current task has failed:
taskfail "oopsie!"
It primarily raises the exception TaskFailure but may do other
helpful things in the future.
sh "command", throw: true
sh executes a string in the current shell:
sh "cp src.txt src.bak"
Fails with taskfail if the command returns a non-zero status
unless throw: is false. I that case, it returns the exit
status.
capture "command", "arg1", "arg2", throw:true, stderr:false, ignore:false
capture executes a command, captures its output as a string, and
returns it.
files = capture("ls", "-1")
If stderr: is true, captures both stdout and stderr as one
interleaved string, otherwise only captures stdout.
On failure, it throws a TaskFailure unless:
If throw is false, returns nil.
If ignore is true, returns the captured output as a string. This
overrides throw.
RubyExe
RubyExe is a constant containing jon's best guess at the location
of the relevant Ruby interpreter. It falls back to "mruby" if nothing
else can be found.
(Note that Backpack Ruby makes it possible to build jon as a
standalone executable, so there may not actually be another Ruby
interpreter at all.)
Task Execution
Jon tasks are run as follows:
-
The main (top) task is either provided on the command line or defaults to a task named
:default. -
For each task, the dependencies are run first, in the order they are specified. This is recursive; that is, each dependency has its own dependencies executed first. Circular dependencies are an error, as are missing dependencies.
-
A task runs if it is in the dependency chain and one of the following is true:
- It is a named task
- One of its dependencies has run. (This means you can force a file task to always run by giving it a named dependency.)
- It is a file task and the file does not exist.
- It is a file task and has a dependency on a file that is newer than it. (I.e. the typical Make rule.)
-
Each task is executed no more than once. If a dependency contains a task that has already run, it is not run again (but it is considered "run" for the purpose of deciding whether or not to run the depending task.)
Things to keep in mind:
- File dependencies can be implicit. That is, you can specify an
existing file as a dependency without a corresponding
filerule and this will work as long as the file already exists.
Building and testing
If you have Backpack Ruby in your path, just go to the project's root directory and type
mruby jon.rb
Requirements
jon was written for Backpack Ruby but can also be run with CRuby
3.0.0 or later and JRuby 9.4.0.0 or later.
The Name
"fake rake" -> "frake" -> "Jonathan Frakes" -> "jon"
(This is not intended to imply any kind of endorsement from or association with actor and director Jonathan Frakes.)