1
0
Fork
You've already forked jon
0
A Rake-like tool written in and for Backpack Ruby
  • Ruby 100%
Chris Reuter 0289fa615b Minor tweaks:
- Added a .gitignore
- Added build instructions to README.md
2026年01月07日 08:37:48 -05:00
test Imported the project from dev branch. 2026年01月06日 14:54:58 -05:00
.gitignore Minor tweaks: 2026年01月07日 08:37:48 -05:00
AUTHORS Imported the project from dev branch. 2026年01月06日 14:54:58 -05:00
jon.rb Imported the project from dev branch. 2026年01月06日 14:54:58 -05:00
Jonfile Imported the project from dev branch. 2026年01月06日 14:54:58 -05:00
LICENSE Imported the project from dev branch. 2026年01月06日 14:54:58 -05:00
README.md Minor tweaks: 2026年01月07日 08:37:48 -05:00

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:

  1. The name is always a string containing a valid filename.
  2. 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:

  1. The main (top) task is either provided on the command line or defaults to a task named :default.

  2. 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.

  3. 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.)
  4. 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 file rule 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.)