Add bin and edit workflow
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 9s

This commit is contained in:
2026-09-16 13:11:16 -06:00
parent c8ac4fcae5
commit 4cee170d66
17576 changed files with 895740 additions and 2 deletions
@@ -0,0 +1,171 @@
= Rake Command Line Usage
Rake is invoked from the command line using:
% rake [options ...] [VAR=VALUE ...] [targets ...]
Options are:
[<tt>name=value</tt>]
Set the environment variable <em>name</em> to <em>value</em>
during the execution of the <b>rake</b> command. You can access
the value by using <tt>ENV['name']</tt>.
[<tt>--all</tt> (-A)]
Used in combination with the -T and -D options, will force
those options to show all the tasks, even the ones without comments.
[<tt>--backtrace</tt>{=_output_} (-n)]
Enable a full backtrace (i.e. like --trace, but without the task
tracing details). The _output_ parameter is optional, but if
specified it controls where the backtrace output is sent. If
_output_ is <tt>stdout</tt>, then backtrace output is directed to
standard output. If _output_ is <tt>stderr</tt>, or if it is
missing, then the backtrace output is sent to standard error.
[<tt>--comments</tt>]
Used in combination with the -W options to force the output to
contain commented options only. This is the reverse of
<tt>--all</tt>.
[<tt>--describe</tt> _pattern_ (-D)]
Describe the tasks (matching optional PATTERN), then exit.
[<tt>--dry-run</tt> (-n)]
Do a dry run. Print the tasks invoked and executed, but do not
actually execute any of the actions.
[<tt>--execute</tt> _code_ (-e)]
Execute some Ruby code and exit.
[<tt>--execute-print</tt> _code_ (-p)]
Execute some Ruby code, print the result, and exit.
[<tt>--execute-continue</tt> _code_ (-E)]
Execute some Ruby code, then continue with normal task processing.
[<tt>--help</tt> (-H)]
Display some help text and exit.
[<tt>--jobs</tt> _number_ (-j)]
Specifies the maximum number of concurrent threads allowed. Rake
will allocate threads as needed up to this maximum number.
If omitted, Rake will attempt to estimate the number of CPUs on
the system and add 4 to that number.
The concurrent threads are used to execute the <tt>multitask</tt>
prerequisites. Also see the <tt>-m</tt> option which turns all
tasks into multitasks.
Sample values:
(no -j) : Allow up to (# of CPUs + 4) number of threads
--jobs : Allow unlimited number of threads
--jobs=1 : Allow only one thread (the main thread)
--jobs=16 : Allow up to 16 concurrent threads
[<tt>--job-stats</tt> _level_]
Display job statistics at the completion of the run. By default,
this will display the requested number of active threads (from the
-j options) and the maximum number of threads in play at any given
time.
If the optional _level_ is <tt>history</tt>, then a complete trace
of task history will be displayed on standard output.
[<tt>--libdir</tt> _directory_ (-I)]
Add _directory_ to the list of directories searched for require.
[<tt>--multitask</tt> (-m)]
Treat all tasks as multitasks. ('make/drake' semantics)
[<tt>--nosearch</tt> (-N)]
Do not search for a Rakefile in parent directories.
[<tt>--prereqs</tt> (-P)]
Display a list of all tasks and their immediate prerequisites.
[<tt>--quiet</tt> (-q)]
Do not echo commands from FileUtils.
[<tt>--rakefile</tt> _filename_ (-f)]
Use _filename_ as the name of the rakefile. The default rakefile
names are +rakefile+ and +Rakefile+ (with +rakefile+ taking
precedence). If the rakefile is not found in the current
directory, +rake+ will search parent directories for a match. The
directory where the Rakefile is found will become the current
directory for the actions executed in the Rakefile.
[<tt>--rakelibdir</tt> _rakelibdir_ (-R)]
Auto-import any .rake files in RAKELIBDIR. (default is 'rakelib')
[<tt>--require</tt> _name_ (-r)]
Require _name_ before executing the Rakefile.
[<tt>--rules</tt>]
Trace the resolution of rules used to create tasks.
[<tt>--silent (-s)</tt>]
Like --quiet, but also suppresses the 'in directory' announcement.
[<tt>--suppress-backtrace</tt> _pattern_]
Line matching the regular expression _pattern_ will be removed
from the backtrace output. Note that the --backtrace option is the
full backtrace without these lines suppressed.
[<tt>--system</tt> (-g)]
Use the system wide (global) rakefiles. The project Rakefile is
ignored. By default, the system wide rakefiles are used only if no
project Rakefile is found. On Unix-like system, the system wide
rake files are located in $HOME/.rake. On a windows system they
are stored in $APPDATA/Rake.
[<tt>--no-system</tt> (-G)]
Use the project level Rakefile, ignoring the system-wide (global)
rakefiles.
[<tt>--tasks</tt> <em>pattern</em> (-T)]
Display a list of the major tasks and their comments. Comments
are defined using the "desc" command. If a pattern is given, then
only tasks matching the pattern are displayed.
[<tt>--trace</tt>{=_output_} (-t)]
Turn on invoke/execute tracing. Also enable full backtrace on
errors. The _output_ parameter is optional, but if specified it
controls where the trace output is sent. If _output_ is
<tt>stdout</tt>, then trace output is directed to standard output.
If _output_ is <tt>stderr</tt>, or if it is missing, then trace
output is sent to standard error.
[<tt>--verbose</tt> (-v)]
Echo the Sys commands to standard output.
[<tt>--version</tt> (-V)]
Display the program version and exit.
[<tt>--where</tt> <em>pattern</em> (-W)]
Display tasks that match <em>pattern</em> and the file and line
number where the task is defined. By default this option will
display all tasks, not just the tasks that have descriptions.
[<tt>--no-deprecation-warnings</tt> (-X)]
Do not display the deprecation warnings.
== Environment Variables
[<tt>RAKEOPT</tt>]
Command line options can be specified in the <tt>RAKEOPT</tt>
environment variable. These options will be processed as if they
were given on the command line. This is useful for setting default
options that you want to use with every rake invocation.
For example, setting:
export RAKEOPT="-s --trace"
would cause rake to run silently with tracing enabled by default.
In addition, any command line option of the form
<em>VAR</em>=<em>VALUE</em> will be added to the environment hash
<tt>ENV</tt> and may be tested in the Rakefile.
@@ -0,0 +1,38 @@
# Example Rakefile -*- ruby -*-
task :default => [:main]
file "a.o" => ["a.c"] do |t|
src = t.name.sub(/\.o$/, '.c')
sh "gcc #{src} -c -o #{t.name}"
end
file "b.o" => ["b.c"] do |t|
src = t.name.sub(/\.o$/, '.c')
sh "gcc #{src} -c -o #{t.name}"
end
file "main.o" => ["main.c"] do |t|
src = t.name.sub(/\.o$/, '.c')
sh "gcc #{src} -c -o #{t.name}"
end
OBJFILES = ["a.o", "b.o", "main.o"]
task :obj => OBJFILES
file "main" => OBJFILES do |t|
sh "gcc -o #{t.name} main.o a.o b.o"
end
task :clean do
rm_f FileList['*.o']
Dir['*~'].each { |fn| rm_f fn }
end
task :clobber => [:clean] do
rm_f "main"
end
task :run => ["main"] do
sh "./main"
end
@@ -0,0 +1,35 @@
# Example Rakefile -*- ruby -*-
# Using the power of Ruby
task :default => [:main]
def ext(fn, newext)
fn.sub(/\.[^.]+$/, newext)
end
SRCFILES = Dir['*.c']
OBJFILES = SRCFILES.collect { |fn| ext(fn,".o") }
OBJFILES.each do |objfile|
srcfile = ext(objfile, ".c")
file objfile => [srcfile] do |t|
sh "gcc #{srcfile} -c -o #{t.name}"
end
end
file "main" => OBJFILES do |t|
sh "gcc -o #{t.name} main.o a.o b.o"
end
task :clean do
rm_f FileList['*.o']
Dir['*~'].each { |fn| rm_f fn }
end
task :clobber => [:clean] do
rm_f "main"
end
task :run => ["main"] do
sh "./main"
end
+6
View File
@@ -0,0 +1,6 @@
#include <stdio.h>
void a()
{
printf ("In function a\n");
}
+6
View File
@@ -0,0 +1,6 @@
#include <stdio.h>
void b()
{
printf ("In function b\n");
}
+11
View File
@@ -0,0 +1,11 @@
#include <stdio.h>
extern void a();
extern void b();
int main ()
{
a();
b();
return 0;
}
+42
View File
@@ -0,0 +1,42 @@
= Glossary
action ::
Code to be executed in order to perform a task. Actions in a Rakefile are
specified in a code block. (Usually delimited by +do+/+end+ pairs.)
execute ::
When a task is executed, all of its actions are performed in the order they
were defined. Note that, unlike <tt>invoke</tt>, <tt>execute</tt> always
executes the actions (without invoking or executing the prerequisites).
file task (Rake::FileTask) ::
A file task is a task whose purpose is to create a file (which has the same
name as the task). When invoked, a file task will only execute if one or
more of the following conditions are true.
1. The associated file does not exist.
2. A prerequisite has a later time stamp than the existing file.
Because normal Tasks always have the current time as timestamp, a FileTask
that has a normal Task prerequisite will always execute.
invoke ::
When a task is invoked, first we check to see if it has been invoked before.
If it has been, then nothing else is done. If this is the first time it has
been invoked, then we invoke each of its prerequisites. Finally, we check
to see if we need to execute the actions of this task by calling
Rake::Task#needed?. If the task is needed, we execute its actions.
NOTE: Prerequisites are still invoked even if the task is not needed.
prerequisites ::
Every task has a (possibly empty) set of prerequisites. A prerequisite P to
Task T is itself a task that must be invoked before Task T.
rule ::
A rule is a recipe for synthesizing a task when no task is explicitly
defined. Rules generally synthesize file tasks.
task (Rake::Task) ::
The basic unit of work in a Rakefile. A task has a name, a set of
prerequisites, and a list of actions to be performed.
+592
View File
@@ -0,0 +1,592 @@
# frozen_string_literal: true
module RDoc
module Page
FONTS = "\"Bitstream Vera Sans\", Verdana, Arial, Helvetica, sans-serif"
STYLE = <<CSS
a {
color: #00F;
text-decoration: none;
}
a:hover {
color: #77F;
text-decoration: underline;
}
body, td, p {
font-family: %fonts%;
background: #FFF;
color: #000;
margin: 0px;
font-size: small;
}
#content {
margin: 2em;
}
#description p {
margin-bottom: 0.5em;
}
.sectiontitle {
margin-top: 1em;
margin-bottom: 1em;
padding: 0.5em;
padding-left: 2em;
background: #005;
color: #FFF;
font-weight: bold;
border: 1px dotted black;
}
.attr-rw {
padding-left: 1em;
padding-right: 1em;
text-align: center;
color: #055;
}
.attr-name {
font-weight: bold;
}
.attr-desc {
}
.attr-value {
font-family: monospace;
}
.file-title-prefix {
font-size: large;
}
.file-title {
font-size: large;
font-weight: bold;
background: #005;
color: #FFF;
}
.banner {
background: #005;
color: #FFF;
border: 1px solid black;
padding: 1em;
}
.banner td {
background: transparent;
color: #FFF;
}
h1 a, h2 a, .sectiontitle a, .banner a {
color: #FF0;
}
h1 a:hover, h2 a:hover, .sectiontitle a:hover, .banner a:hover {
color: #FF7;
}
.dyn-source {
display: none;
background: #FFE;
color: #000;
border: 1px dotted black;
margin: 0.5em 2em 0.5em 2em;
padding: 0.5em;
}
.dyn-source .cmt {
color: #00F;
font-style: italic;
}
.dyn-source .kw {
color: #070;
font-weight: bold;
}
.method {
margin-left: 1em;
margin-right: 1em;
margin-bottom: 1em;
}
.description pre {
padding: 0.5em;
border: 1px dotted black;
background: #FFE;
}
.method .title {
font-family: monospace;
font-size: large;
border-bottom: 1px dashed black;
margin-bottom: 0.3em;
padding-bottom: 0.1em;
}
.method .description, .method .sourcecode {
margin-left: 1em;
}
.description p, .sourcecode p {
margin-bottom: 0.5em;
}
.method .sourcecode p.source-link {
text-indent: 0em;
margin-top: 0.5em;
}
.method .aka {
margin-top: 0.3em;
margin-left: 1em;
font-style: italic;
text-indent: 2em;
}
h1 {
padding: 1em;
border: 1px solid black;
font-size: x-large;
font-weight: bold;
color: #FFF;
background: #007;
}
h2 {
padding: 0.5em 1em 0.5em 1em;
border: 1px solid black;
font-size: large;
font-weight: bold;
color: #FFF;
background: #009;
}
h3, h4, h5, h6 {
padding: 0.2em 1em 0.2em 1em;
border: 1px dashed black;
color: #000;
background: #AAF;
}
.sourcecode > pre {
padding: 0.5em;
border: 1px dotted black;
background: #FFE;
}
CSS
XHTML_PREAMBLE = %{<?xml version="1.0" encoding="%charset%"?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
}
HEADER = XHTML_PREAMBLE + <<ENDHEADER
<html>
<head>
<title>%title%</title>
<meta http-equiv="Content-Type" content="text/html; charset=%charset%" />
<link rel="stylesheet" href="%style_url%" type="text/css" media="screen" />
<script language="JavaScript" type="text/javascript">
// <![CDATA[
function toggleSource( id )
{
var elem
var link
if( document.getElementById )
{
elem = document.getElementById( id )
link = document.getElementById( "l_" + id )
}
else if ( document.all )
{
elem = eval( "document.all." + id )
link = eval( "document.all.l_" + id )
}
else
return false;
if( elem.style.display == "block" )
{
elem.style.display = "none"
link.innerHTML = "show source"
}
else
{
elem.style.display = "block"
link.innerHTML = "hide source"
}
}
function openCode( url )
{
window.open( url, "SOURCE_CODE", "width=400,height=400,scrollbars=yes" )
}
// ]]>
</script>
</head>
<body>
ENDHEADER
FILE_PAGE = <<HTML
<table border='0' cellpadding='0' cellspacing='0' width="100%" class='banner'>
<tr><td>
<table width="100%" border='0' cellpadding='0' cellspacing='0'><tr>
<td class="file-title" colspan="2"><span class="file-title-prefix">File</span><br />%short_name%</td>
<td align="right">
<table border='0' cellspacing="0" cellpadding="2">
<tr>
<td>Path:</td>
<td>%full_path%
IF:cvsurl
&nbsp;(<a href="%cvsurl%">CVS</a>)
ENDIF:cvsurl
</td>
</tr>
<tr>
<td>Modified:</td>
<td>%dtm_modified%</td>
</tr>
</table>
</td></tr>
</table>
</td></tr>
</table><br>
HTML
###################################################################
CLASS_PAGE = <<HTML
<table width="100%" border='0' cellpadding='0' cellspacing='0' class='banner'><tr>
<td class="file-title"><span class="file-title-prefix">%classmod%</span><br />%full_name%</td>
<td align="right">
<table cellspacing=0 cellpadding=2>
<tr valign="top">
<td>In:</td>
<td>
START:infiles
HREF:full_path_url:full_path:
IF:cvsurl
&nbsp;(<a href="%cvsurl%">CVS</a>)
ENDIF:cvsurl
END:infiles
</td>
</tr>
IF:parent
<tr>
<td>Parent:</td>
<td>
IF:par_url
<a href="%par_url%">
ENDIF:par_url
%parent%
IF:par_url
</a>
ENDIF:par_url
</td>
</tr>
ENDIF:parent
</table>
</td>
</tr>
</table>
HTML
###################################################################
METHOD_LIST = <<HTML
<div id="content">
IF:diagram
<table cellpadding='0' cellspacing='0' border='0' width="100%"><tr><td align="center">
%diagram%
</td></tr></table>
ENDIF:diagram
IF:description
<div class="description">%description%</div>
ENDIF:description
IF:requires
<div class="sectiontitle">Required Files</div>
<ul>
START:requires
<li>HREF:aref:name:</li>
END:requires
</ul>
ENDIF:requires
IF:toc
<div class="sectiontitle">Contents</div>
<ul>
START:toc
<li><a href="#%href%">%secname%</a></li>
END:toc
</ul>
ENDIF:toc
IF:methods
<div class="sectiontitle">Methods</div>
<ul>
START:methods
<li>HREF:aref:name:</li>
END:methods
</ul>
ENDIF:methods
IF:includes
<div class="sectiontitle">Included Modules</div>
<ul>
START:includes
<li>HREF:aref:name:</li>
END:includes
</ul>
ENDIF:includes
START:sections
IF:sectitle
<div class="sectiontitle"><a nem="%secsequence%">%sectitle%</a></div>
IF:seccomment
<div class="description">
%seccomment%
</div>
ENDIF:seccomment
ENDIF:sectitle
IF:classlist
<div class="sectiontitle">Classes and Modules</div>
%classlist%
ENDIF:classlist
IF:constants
<div class="sectiontitle">Constants</div>
<table border='0' cellpadding='5'>
START:constants
<tr valign='top'>
<td class="attr-name">%name%</td>
<td>=</td>
<td class="attr-value">%value%</td>
</tr>
IF:desc
<tr valign='top'>
<td>&nbsp;</td>
<td colspan="2" class="attr-desc">%desc%</td>
</tr>
ENDIF:desc
END:constants
</table>
ENDIF:constants
IF:attributes
<div class="sectiontitle">Attributes</div>
<table border='0' cellpadding='5'>
START:attributes
<tr valign='top'>
<td class='attr-rw'>
IF:rw
[%rw%]
ENDIF:rw
</td>
<td class='attr-name'>%name%</td>
<td class='attr-desc'>%a_desc%</td>
</tr>
END:attributes
</table>
ENDIF:attributes
IF:method_list
START:method_list
IF:methods
<div class="sectiontitle">%type% %category% methods</div>
START:methods
<div class="method">
<div class="title">
IF:callseq
<a name="%aref%"></a><b>%callseq%</b>
ENDIF:callseq
IFNOT:callseq
<a name="%aref%"></a><b>%name%</b>%params%
ENDIF:callseq
IF:codeurl
[ <a href="javascript:openCode('%codeurl%')">source</a> ]
ENDIF:codeurl
</div>
IF:m_desc
<div class="description">
%m_desc%
</div>
ENDIF:m_desc
IF:aka
<div class="aka">
This method is also aliased as
START:aka
<a href="%aref%">%name%</a>
END:aka
</div>
ENDIF:aka
IF:sourcecode
<div class="sourcecode">
<p class="source-link">[ <a href="javascript:toggleSource('%aref%_source')" id="l_%aref%_source">show source</a> ]</p>
<div id="%aref%_source" class="dyn-source">
<pre>
%sourcecode%
</pre>
</div>
</div>
ENDIF:sourcecode
</div>
END:methods
ENDIF:methods
END:method_list
ENDIF:method_list
END:sections
</div>
HTML
FOOTER = <<ENDFOOTER
</body>
</html>
ENDFOOTER
BODY = HEADER + <<ENDBODY
!INCLUDE! <!-- banner header -->
<div id="bodyContent">
#{METHOD_LIST}
</div>
#{FOOTER}
ENDBODY
########################## Source code ##########################
SRC_PAGE = XHTML_PREAMBLE + <<HTML
<html>
<head><title>%title%</title>
<meta http-equiv="Content-Type" content="text/html; charset=%charset%">
<style>
.ruby-comment { color: green; font-style: italic }
.ruby-constant { color: #4433aa; font-weight: bold; }
.ruby-identifier { color: #222222; }
.ruby-ivar { color: #2233dd; }
.ruby-keyword { color: #3333FF; font-weight: bold }
.ruby-node { color: #777777; }
.ruby-operator { color: #111111; }
.ruby-regexp { color: #662222; }
.ruby-value { color: #662222; font-style: italic }
.kw { color: #3333FF; font-weight: bold }
.cmt { color: green; font-style: italic }
.str { color: #662222; font-style: italic }
.re { color: #662222; }
</style>
</head>
<body bgcolor="white">
<pre>%code%</pre>
</body>
</html>
HTML
########################## Index ################################
FR_INDEX_BODY = <<HTML
!INCLUDE!
HTML
FILE_INDEX = XHTML_PREAMBLE + <<HTML
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=%charset%">
<style>
<!--
body {
background-color: #EEE;
font-family: #{FONTS};
color: #000;
margin: 0px;
}
.banner {
background: #005;
color: #FFF;
padding: 0.2em;
font-size: small;
font-weight: bold;
text-align: center;
}
.entries {
margin: 0.25em 1em 0 1em;
font-size: x-small;
}
a {
color: #00F;
text-decoration: none;
white-space: nowrap;
}
a:hover {
color: #77F;
text-decoration: underline;
}
-->
</style>
<base target="docwin">
</head>
<body>
<div class="banner">%list_title%</div>
<div class="entries">
START:entries
<a href="%href%">%name%</a><br>
END:entries
</div>
</body></html>
HTML
CLASS_INDEX = FILE_INDEX
METHOD_INDEX = FILE_INDEX
INDEX = XHTML_PREAMBLE + <<HTML
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>%title%</title>
<meta http-equiv="Content-Type" content="text/html; charset=%charset%">
</head>
<frameset cols="20%,*">
<frameset rows="15%,35%,50%">
<frame src="fr_file_index.html" title="Files" name="Files" />
<frame src="fr_class_index.html" name="Classes" />
<frame src="fr_method_index.html" name="Methods" />
</frameset>
IF:inline_source
<frame src="%initial_page%" name="docwin">
ENDIF:inline_source
IFNOT:inline_source
<frameset rows="80%,20%">
<frame src="%initial_page%" name="docwin">
<frame src="blank.html" name="source">
</frameset>
ENDIF:inline_source
<noframes>
<body bgcolor="white">
Click <a href="html/index.html">here</a> for a non-frames
version of this page.
</body>
</noframes>
</frameset>
</html>
HTML
end
end
+127
View File
@@ -0,0 +1,127 @@
= Original Prototype Rake
This is the original 100 line prototype rake program.
---
#!/usr/bin/env ruby
require 'ftools'
class Task
TASKS = Hash.new
attr_reader :prerequisites
def initialize(task_name)
@name = task_name
@prerequisites = []
@actions = []
end
def enhance(deps=nil, &block)
@prerequisites |= deps if deps
@actions << block if block_given?
self
end
def name
@name.to_s
end
def invoke
@prerequisites.each { |n| Task[n].invoke }
execute if needed?
end
def execute
return if @triggered
@triggered = true
@actions.collect { |act| result = act.call(self) }.last
end
def needed?
true
end
def timestamp
Time.now
end
class << self
def [](task_name)
TASKS[intern(task_name)] or fail "Don't know how to rake #{task_name}"
end
def define_task(args, &block)
case args
when Hash
fail "Too Many Target Names: #{args.keys.join(' ')}" if args.size > 1
fail "No Task Name Given" if args.size < 1
task_name = args.keys[0]
deps = args[task_name]
else
task_name = args
deps = []
end
deps = deps.collect {|d| intern(d) }
get(task_name).enhance(deps, &block)
end
def get(task_name)
name = intern(task_name)
TASKS[name] ||= self.new(name)
end
def intern(task_name)
(Symbol === task_name) ? task_name : task_name.intern
end
end
end
class FileTask < Task
def needed?
return true unless File.exist?(name)
latest_prereq = @prerequisites.collect{|n| Task[n].timestamp}.max
return false if latest_prereq.nil?
timestamp < latest_prereq
end
def timestamp
File.new(name.to_s).mtime
end
end
def task(args, &block)
Task.define_task(args, &block)
end
def file(args, &block)
FileTask.define_task(args, &block)
end
def sys(cmd)
puts cmd
system(cmd) or fail "Command Failed: [#{cmd}]"
end
def rake
begin
here = Dir.pwd
while ! File.exist?("Rakefile")
Dir.chdir("..")
fail "No Rakefile found" if Dir.pwd == here
here = Dir.pwd
end
puts "(in #{Dir.pwd})"
load "./Rakefile"
ARGV.push("default") if ARGV.size == 0
ARGV.each { |task_name| Task[task_name].invoke }
rescue Exception => ex
puts "rake aborted ... #{ex.message}"
puts ex.backtrace.find {|str| str =~ /Rakefile/ } || ""
end
end
if __FILE__ == $0 then
rake
end
+156
View File
@@ -0,0 +1,156 @@
.Dd June 12, 2016
.Dt RAKE 1
.Os rake 11.2.2
.Sh NAME
.Nm rake
.Nd make-like build utility for Ruby
.Sh SYNOPSIS
.Nm
.Op Fl f Ar rakefile
.Op Ar options
.Ar targets ...
.Sh DESCRIPTION
.Nm
is a
.Xr make 1 Ns -like
build utility for Ruby.
Tasks and dependencies are specified in standard Ruby syntax.
.Sh OPTIONS
.Bl -tag -width Ds
.It Fl m , Fl -multitask
Treat all tasks as multitasks.
.It Fl B , Fl -build-all
Build all prerequisites, including those which are up\-to\-date.
.It Fl j , Fl -jobs Ar num_jobs
Specifies the maximum number of tasks to execute in parallel (default is number of CPU cores + 4).
.El
.Ss Modules
.Bl -tag -width Ds
.It Fl I , Fl -libdir Ar libdir
Include
.Ar libdir
in the search path for required modules.
.It Fl r , Fl -require Ar module
Require
.Ar module
before executing
.Pa rakefile .
.El
.Ss Rakefile location
.Bl -tag -width Ds
.It Fl f , Fl -rakefile Ar filename
Use
.Ar filename
as the rakefile to search for.
.It Fl N , Fl -no-search , Fl -nosearch
Do not search parent directories for the Rakefile.
.It Fl G , Fl -no-system , Fl -nosystem
Use standard project Rakefile search paths, ignore system wide rakefiles.
.It Fl R , Fl -rakelib Ar rakelibdir , Fl -rakelibdir Ar rakelibdir
Auto-import any .rake files in
.Ar rakelibdir
(default is
.Sq rakelib )
.It Fl g , Fl -system
Use system-wide (global) rakefiles (usually
.Pa ~/.rake/*.rake ) .
.El
.Ss Debugging
.Bl -tag -width Ds
.It Fl -backtrace Ns = Ns Ar out
Enable full backtrace.
.Ar out
can be
.Dv stderr
(default) or
.Dv stdout .
.It Fl t , Fl -trace Ns = Ns Ar out
Turn on invoke/execute tracing, enable full backtrace.
.Ar out
can be
.Dv stderr
(default) or
.Dv stdout .
.It Fl -suppress-backtrace Ar pattern
Suppress backtrace lines matching regexp
.Ar pattern .
Ignored if
.Fl -trace
is on.
.It Fl -rules
Trace the rules resolution.
.It Fl n , Fl -dry-run
Do a dry run without executing actions.
.It Fl T , Fl -tasks Op Ar pattern
Display the tasks (matching optional
.Ar pattern )
with descriptions, then exit.
.It Fl D , Fl -describe Op Ar pattern
Describe the tasks (matching optional
.Ar pattern ) ,
then exit.
.It Fl W , Fl -where Op Ar pattern
Describe the tasks (matching optional
.Ar pattern ) ,
then exit.
.It Fl P , Fl -prereqs
Display the tasks and dependencies, then exit.
.It Fl e , Fl -execute Ar code
Execute some Ruby code and exit.
.It Fl p , Fl -execute-print Ar code
Execute some Ruby code, print the result, then exit.
.It Fl E , Fl -execute-continue Ar code
Execute some Ruby code, then continue with normal task processing.
.El
.Ss Information
.Bl -tag -width Ds
.It Fl v , Fl -verbose
Log message to standard output.
.It Fl q , Fl -quiet
Do not log messages to standard output.
.It Fl s , Fl -silent
Like
.Fl -quiet ,
but also suppresses the
.Sq in directory
announcement.
.It Fl X , Fl -no-deprecation-warnings
Disable the deprecation warnings.
.It Fl -comments
Show commented tasks only
.It Fl A , Fl -all
Show all tasks, even uncommented ones (in combination with
.Fl T
or
.Fl D )
.It Fl -job-stats Op Ar level
Display job statistics.
If
.Ar level
is
.Sq history ,
displays a complete job list.
.It Fl V , Fl -version
Display the program version.
.It Fl h , Fl H , Fl -help
Display a help message.
.El
.Sh SEE ALSO
The complete documentation for
.Nm rake
has been installed at
.Pa /usr/share/doc/rake-doc/html/index.html .
It is also available online at
.Lk https://ruby.github.io/rake .
.Sh AUTHORS
.An -nosplit
.Nm
was written by
.An Jim Weirich Aq Mt jim@weirichhouse.org .
.Pp
This manual was created by
.An Caitlin Matos Aq Mt caitlin.matos@zoho.com
for the Debian project (but may be used by others).
It was inspired by the manual by
.An Jani Monoses Aq Mt jani@iv.ro
for the Ubuntu project.
+635
View File
@@ -0,0 +1,635 @@
= Rakefile Format
First of all, there is no special format for a Rakefile. A Rakefile
contains executable Ruby code. Anything legal in a ruby script is
allowed in a Rakefile.
Now that we understand there is no special syntax in a Rakefile, there
are some conventions that are used in a Rakefile that are a little
unusual in a typical Ruby program. Since a Rakefile is tailored to
specifying tasks and actions, the idioms used in a Rakefile are
designed to support that.
So, what goes into a Rakefile?
== Tasks
Tasks are the main unit of work in a Rakefile. Tasks have a name
(usually given as a symbol or a string), a list of prerequisites (more
symbols or strings) and a list of actions (given as a block).
=== Simple Tasks
A task is declared by using the +task+ method. +task+ takes a single
parameter that is the name of the task.
task :name
=== Tasks with Prerequisites
Any prerequisites are given as a list (enclosed in square brackets)
following the name and an arrow (=>).
task :name => [:prereq1, :prereq2]
*NOTE:* Although this syntax looks a little funky, it is legal
Ruby. We are constructing a hash where the key is :name and the value
for that key is the list of prerequisites. It is equivalent to the
following ...
hash = Hash.new
hash[:name] = [:prereq1, :prereq2]
task(hash)
You can also use strings for task names and prerequisites, rake doesn't care.
This is the same task definition:
task 'name' => %w[prereq1 prereq2]
As is this:
task name: %w[prereq1 prereq2]
We'll prefer this style for regular tasks with prerequisites throughout the
rest of the document. Using an array of strings for the prerequisites means
you will need to make fewer changes if you need to move tasks into namespaces
or perform other refactorings.
=== Tasks with Actions
Actions are defined by passing a block to the +task+ method. Any Ruby
code can be placed in the block. The block may reference the task
object via the block parameter.
task name: [:prereq1, :prereq2] do |t|
# actions (may reference t)
end
=== Multiple Definitions
A task may be specified more than once. Each specification adds its
prerequisites and actions to the existing definition. This allows one
part of a rakefile to specify the actions and a different rakefile
(perhaps separately generated) to specify the dependencies.
For example, the following is equivalent to the single task
specification given above.
task :name
task name: :prereq1
task name: %w[prereq2]
task :name do |t|
# actions
end
== File Tasks
Some tasks are designed to create a file from one or more other files.
Tasks that generate these files may be skipped if the file already
exists. File tasks are used to specify file creation tasks.
File tasks are declared using the +file+ method (instead of the +task+
method). In addition, file tasks are usually named with a string
rather than a symbol.
The following file task creates a executable program (named +prog+)
given two object files named +a.o+ and +b.o+. The tasks
for creating +a.o+ and +b.o+ are not shown.
file "prog" => ["a.o", "b.o"] do |t|
sh "cc -o #{t.name} #{t.prerequisites.join(' ')}"
end
== Directory Tasks
It is common to need to create directories upon demand. The
+directory+ convenience method is a short-hand for creating a FileTask
that creates the directory. For example, the following declaration
...
directory "testdata/examples/doc"
is equivalent to ...
file "testdata" do |t| mkdir t.name end
file "testdata/examples" => ["testdata"] do |t| mkdir t.name end
file "testdata/examples/doc" => ["testdata/examples"] do |t| mkdir t.name end
The +directory+ method does not accept prerequisites or actions, but
both prerequisites and actions can be added later. For example ...
directory "testdata"
file "testdata" => ["otherdata"]
file "testdata" do
cp Dir["standard_data/*.data"], "testdata"
end
== Tasks with Parallel Prerequisites
Rake allows parallel execution of prerequisites using the following syntax:
multitask copy_files: %w[copy_src copy_doc copy_bin] do
puts "All Copies Complete"
end
In this example, +copy_files+ is a normal rake task. Its actions are
executed whenever all of its prerequisites are done. The big
difference is that the prerequisites (+copy_src+, +copy_bin+ and
+copy_doc+) are executed in parallel. Each of the prerequisites are
run in their own Ruby thread, possibly allowing faster overall runtime.
=== Secondary Prerequisites
If any of the primary prerequisites of a multitask have common secondary
prerequisites, all of the primary/parallel prerequisites will wait
until the common prerequisites have been run.
For example, if the <tt>copy_<em>xxx</em></tt> tasks have the
following prerequisites:
task copy_src: :prep_for_copy
task copy_bin: :prep_for_copy
task copy_doc: :prep_for_copy
Then the +prep_for_copy+ task is run before starting all the copies in
parallel. Once +prep_for_copy+ is complete, +copy_src+, +copy_bin+,
and +copy_doc+ are all run in parallel. Note that +prep_for_copy+ is
run only once, even though it is referenced in multiple threads.
=== Thread Safety
The Rake internal data structures are thread-safe with respect
to the multitask parallel execution, so there is no need for the user
to do extra synchronization for Rake's benefit. However, if there are
user data structures shared between the parallel prerequisites, the
user must do whatever is necessary to prevent race conditions.
== Tasks with Arguments
Prior to version 0.8.0, rake was only able to handle command line
arguments of the form NAME=VALUE that were passed into Rake via the
ENV hash. Many folks had asked for some kind of simple command line
arguments, perhaps using "--" to separate regular task names from
argument values on the command line. The problem is that there was no
easy way to associate positional arguments on the command line with
different tasks. Suppose both tasks :a and :b expect a command line
argument: does the first value go with :a? What if :b is run first?
Should it then get the first command line argument.
Rake 0.8.0 solves this problem by explicitly passing values directly
to the tasks that need them. For example, if I had a release task
that required a version number, I could say:
rake release[0.8.2]
And the string "0.8.2" will be passed to the :release task. Multiple
arguments can be passed by separating them with a comma, for example:
rake name[john,doe]
Just a few words of caution. The rake task name and its arguments
need to be a single command line argument to rake. This generally
means no spaces. If spaces are needed, then the entire name +
argument string should be quoted. Something like this:
rake "name[billy bob, smith]"
(Quoting rules vary between operating systems and shells, so make sure
you consult the proper docs for your OS/shell).
=== Tasks that Expect Parameters
Parameters are only given to tasks that are setup to expect them. In
order to handle named parameters, the task declaration syntax for
tasks has been extended slightly.
For example, a task that needs a first name and last name might be
declared as:
task :name, [:first_name, :last_name]
The first argument is still the name of the task (:name in this case).
The next two arguments are the names of the parameters expected by
:name in an array (:first_name and :last_name in the example).
To access the values of the parameters, the block defining the task
behaviour can now accept a second parameter:
task :name, [:first_name, :last_name] do |t, args|
puts "First name is #{args.first_name}"
puts "Last name is #{args.last_name}"
end
The first argument of the block "t" is always bound to the current
task object. The second argument "args" is an open-struct like object
that allows access to the task arguments. Extra command line
arguments to a task are ignored.
If you wish to specify default values for the arguments, you can use
the with_defaults method in the task body. Here is the above example
where we specify default values for the first and last names:
task :name, [:first_name, :last_name] do |t, args|
args.with_defaults(:first_name => "John", :last_name => "Dough")
puts "First name is #{args.first_name}"
puts "Last name is #{args.last_name}"
end
=== Tasks that Expect Parameters and Have Prerequisites
Tasks that use parameters have a slightly different format for
prerequisites. Use the arrow notation to indicate the prerequisites
for tasks with arguments. For example:
task :name, [:first_name, :last_name] => [:pre_name] do |t, args|
args.with_defaults(:first_name => "John", :last_name => "Dough")
puts "First name is #{args.first_name}"
puts "Last name is #{args.last_name}"
end
=== Tasks that take Variable-length Parameters
Tasks that need to handle a list of values as a parameter can use the
extras method of the args variable. This allows for tasks that can
loop over a variable number of values, and its compatible with named
parameters as well:
task :email, [:message] do |t, args|
mail = Mail.new(args.message)
recipients = args.extras
recipients.each do |target|
mail.send_to(target)
end
end
There is also the convenience method to_a that returns all parameters
in the sequential order they were given, including those associated
with named parameters.
=== Deprecated Task Parameters Format
There is an older format for declaring task parameters that omitted
the task argument array and used the :needs keyword to introduce the
dependencies. That format is still supported for compatibility, but
is not recommended for use. The older format may be dropped in future
versions of rake.
== Accessing Task Programmatically
Sometimes it is useful to manipulate tasks programmatically in a
Rakefile. To find a task object use Rake::Task.[].
=== Programmatic Task Example
For example, the following Rakefile defines two tasks. The :doit task
simply prints a simple "DONE" message. The :dont class will lookup
the doit class and remove (clear) all of its prerequisites and
actions.
task :doit do
puts "DONE"
end
task :dont do
Rake::Task[:doit].clear
end
Running this example:
$ rake doit
(in /Users/jim/working/git/rake/x)
DONE
$ rake dont doit
(in /Users/jim/working/git/rake/x)
$
The ability to programmatically manipulate tasks gives rake very
powerful meta-programming capabilities w.r.t. task execution, but
should be used with caution.
== Rules
When a file is named as a prerequisite, but does not have a file task
defined for it, Rake will attempt to synthesize a task by looking at a
list of rules supplied in the Rakefile.
Suppose we were trying to invoke task "mycode.o", but no task is
defined for it. But the rakefile has a rule that look like this ...
rule '.o' => ['.c'] do |t|
sh "cc #{t.source} -c -o #{t.name}"
end
This rule will synthesize any task that ends in ".o". It has a
prerequisite a source file with an extension of ".c" must exist. If
Rake is able to find a file named "mycode.c", it will automatically
create a task that builds "mycode.o" from "mycode.c".
If the file "mycode.c" does not exist, rake will attempt
to recursively synthesize a rule for it.
When a task is synthesized from a rule, the +source+ attribute of the
task is set to the matching source file. This allows us to write
rules with actions that reference the source file.
=== Advanced Rules
Any regular expression may be used as the rule pattern. Additionally,
a proc may be used to calculate the name of the source file. This
allows for complex patterns and sources.
The following rule is equivalent to the example above.
rule( /\.o$/ => [
proc {|task_name| task_name.sub(/\.[^.]+$/, '.c') }
]) do |t|
sh "cc #{t.source} -c -o #{t.name}"
end
*NOTE:* Because of a _quirk_ in Ruby syntax, parenthesis are
required on *rule* when the first argument is a regular expression.
The following rule might be used for Java files ...
rule '.class' => [
proc { |tn| tn.sub(/\.class$/, '.java').sub(/^classes\//, 'src/') }
] do |t|
java_compile(t.source, t.name)
end
*NOTE:* +java_compile+ is a hypothetical method that invokes the
java compiler.
=== Implicit File Tasks
When a task is not defined but a file with that name exists, Rake
automatically creates an implicit file task for it. For example:
$ rake hello_world # Error: task not found
$ touch hello_world # Create a file with the same name
$ rake hello_world # Now succeeds automatically
Use the <tt>--rules</tt> command line option to trace how rules are
resolved when searching for tasks; note that creation of implicit file
tasks is not traced.
== Importing Dependencies
Any ruby file (including other rakefiles) can be included with a
standard Ruby +require+ command. The rules and declarations in the
required file are just added to the definitions already accumulated.
Because the files are loaded _before_ the rake targets are evaluated,
the loaded files must be "ready to go" when the rake command is
invoked. This makes generated dependency files difficult to use. By
the time rake gets around to updating the dependencies file, it is too
late to load it.
The +import+ command addresses this by specifying a file to be loaded
_after_ the main rakefile is loaded, but _before_ any targets on the
command line are invoked. In addition, if the file name matches an
explicit task, that task is invoked before loading the file. This
allows dependency files to be generated and used in a single rake
command invocation.
Example:
require 'rake/loaders/makefile'
file ".depends.mf" => [SRC_LIST] do |t|
sh "makedepend -f- -- #{CFLAGS} -- #{t.prerequisites} > #{t.name}"
end
import ".depends.mf"
If ".depends" does not exist, or is out of date w.r.t. the source
files, a new ".depends" file is generated using +makedepend+ before
loading.
== Comments
Standard Ruby comments (beginning with "#") can be used anywhere it is
legal in Ruby source code, including comments for tasks and rules.
However, if you wish a task to be described using the "-T" switch,
then you need to use the +desc+ command to describe the task.
Example:
desc "Create a distribution package"
task package: %w[ ... ] do ... end
The "-T" switch (or "--tasks" if you like to spell things out) will
display a list of tasks that have a description. If you use +desc+ to
describe your major tasks, you have a semi-automatic way of generating
a summary of your Rake file.
$ rake -T
(in /home/.../rake)
rake clean # Remove any temporary products.
rake clobber # Remove any generated file.
rake clobber_rdoc # Remove rdoc products
rake contrib_test # Run tests for contrib_test
rake default # Default Task
rake install # Install the application
rake lines # Count lines in the main rake file
rake rdoc # Build the rdoc HTML Files
rake rerdoc # Force a rebuild of the RDOC files
rake test # Run tests
rake testall # Run all test targets
Only tasks with descriptions will be displayed with the "-T" switch.
Use "-P" (or "--prereqs") to get a list of all tasks and their
prerequisites.
== Namespaces
As projects grow (and along with it, the number of tasks), it is
common for task names to begin to clash. For example, if you might
have a main program and a set of sample programs built by a single
Rakefile. By placing the tasks related to the main program in one
namespace, and the tasks for building the sample programs in a
different namespace, the task names will not interfere with each other.
For example:
namespace "main" do
task :build do
# Build the main program
end
end
namespace "samples" do
task :build do
# Build the sample programs
end
end
task build: %w[main:build samples:build]
Referencing a task in a separate namespace can be achieved by
prefixing the task name with the namespace and a colon
(e.g. "main:build" refers to the :build task in the +main+ namespace).
Nested namespaces are supported.
Note that the name given in the +task+ command is always the unadorned
task name without any namespace prefixes. The +task+ command always
defines a task in the current namespace.
=== FileTasks
File task names are not scoped by the namespace command. Since the
name of a file task is the name of an actual file in the file system,
it makes little sense to include file task names in name space.
Directory tasks (created by the +directory+ command) are a type of
file task and are also not affected by namespaces.
=== Name Resolution
When looking up a task name, rake will start with the current
namespace and attempt to find the name there. If it fails to find a
name in the current namespace, it will search the parent namespaces
until a match is found (or an error occurs if there is no match).
The "rake" namespace is a special implicit namespace that refers to
the toplevel names.
If a task name begins with a "^" character, the name resolution will
start in the parent namespace. Multiple "^" characters are allowed.
Here is an example file with multiple :run tasks and how various names
resolve in different locations.
task :run
namespace "one" do
task :run
namespace "two" do
task :run
# :run => "one:two:run"
# "two:run" => "one:two:run"
# "one:two:run" => "one:two:run"
# "one:run" => "one:run"
# "^run" => "one:run"
# "^^run" => "rake:run" (the top level task)
# "rake:run" => "rake:run" (the top level task)
end
# :run => "one:run"
# "two:run" => "one:two:run"
# "^run" => "rake:run"
end
# :run => "rake:run"
# "one:run" => "one:run"
# "one:two:run" => "one:two:run"
== FileLists
FileLists are the way Rake manages lists of files. You can treat a
FileList as an array of strings for the most part, but FileLists
support some additional operations.
=== Creating a FileList
Creating a file list is easy. Just give it the list of file names:
fl = FileList['file1.rb', file2.rb']
Or give it a glob pattern:
fl = FileList['*.rb']
== Odds and Ends
=== do/end versus { }
Blocks may be specified with either a +do+/+end+ pair, or with curly
braces in Ruby. We _strongly_ recommend using +do+/+end+ to specify the
actions for tasks and rules. Because the rakefile idiom tends to
leave off parentheses on the task/file/rule methods, unusual
ambiguities can arise when using curly braces.
For example, suppose that the method +object_files+ returns a list of
object files in a project. Now we use +object_files+ as the
prerequisites in a rule specified with actions in curly braces.
# DON'T DO THIS!
file "prog" => object_files {
# Actions are expected here (but it doesn't work)!
}
Because curly braces have a higher precedence than +do+/+end+, the
block is associated with the +object_files+ method rather than the
+file+ method.
This is the proper way to specify the task ...
# THIS IS FINE
file "prog" => object_files do
# Actions go here
end
== Rakefile Path
When issuing the +rake+ command in a terminal, Rake will look
for a Rakefile in the current directory. If a Rakefile is not found,
it will search parent directories until one is found.
For example, if a Rakefile resides in the +project/+ directory,
moving deeper into the project's directory tree will not have an adverse
effect on rake tasks:
$ pwd
/home/user/project
$ cd lib/foo/bar
$ pwd
/home/user/project/lib/foo/bar
$ rake run_pwd
/home/user/project
As far as rake is concerned, all tasks are run from the directory in
which the Rakefile resides.
=== Multiple Rake Files
Not all tasks need to be included in a single Rakefile. Additional
rake files (with the file extension "+.rake+") may be placed in
+rakelib+ directory located at the top level of a project (i.e.
the same directory that contains the main +Rakefile+).
Also, rails projects may include additional rake files in the
+lib/tasks+ directory.
=== Clean and Clobber Tasks
Through <tt>require 'rake/clean'</tt> Rake provides +clean+ and +clobber+
tasks:
+clean+ ::
Clean up the project by deleting scratch files and backup files. Add files
to the +CLEAN+ FileList to have the +clean+ target handle them.
+clobber+ ::
Clobber all generated and non-source files in a project. The task depends
on +clean+, so all the +CLEAN+ files will be deleted as well as files in the
+CLOBBER+ FileList. The intent of this task is to return a project to its
pristine, just unpacked state.
You can add file names or glob patterns to both the +CLEAN+ and +CLOBBER+
lists.
=== Phony Task
The phony task can be used as a dependency to allow file-based tasks to use
non-file-based-tasks as prerequisites without forcing them to rebuild. You
can <tt>require 'rake/phony'</tt> to add the +phony+ task.
----
== See
* README.rdoc -- Main documentation for Rake.
+151
View File
@@ -0,0 +1,151 @@
= Why rake?
Ok, let me state from the beginning that I never intended to write this
code. I'm not convinced it is useful, and I'm not convinced anyone
would even be interested in it. All I can say is that Why's onion truck
must by been passing through the Ohio valley.
What am I talking about? ... A Ruby version of Make.
See, I can sense you cringing already, and I agree. The world certainly
doesn't need yet another reworking of the "make" program. I mean, we
already have "ant". Isn't that enough?
It started yesterday. I was helping a coworker fix a problem in one of
the Makefiles we use in our project. Not a particularly tough problem,
but during the course of the conversation I began lamenting some of the
shortcomings of make. In particular, in one of my makefiles I wanted to
determine the name of a file dynamically and had to resort to some
simple scripting (in Ruby) to make it work. "Wouldn't it be nice if you
could just use Ruby inside a Makefile" I said.
My coworker (a recent convert to Ruby) agreed, but wondered what it
would look like. So I sketched the following on the whiteboard...
"What if you could specify the make tasks in Ruby, like this ..."
task "build" do
java_compile(...args, etc ...)
end
"The task function would register "build" as a target to be made,
and the block would be the action executed whenever the build
system determined that it was time to do the build target."
We agreed that would be cool, but writing make from scratch would be WAY
too much work. And that was the end of that!
... Except I couldn't get the thought out of my head. What exactly
would be needed to make the about syntax work as a make file? Hmmm, you
would need to register the tasks, you need some way of specifying
dependencies between tasks, and some way of kicking off the process.
Hey! What if we did ... and fifteen minutes later I had a working
prototype of Ruby make, complete with dependencies and actions.
I showed the code to my coworker and we had a good laugh. It was just
about a page worth of code that reproduced an amazing amount of the
functionality of make. We were both truly stunned with the power of
Ruby.
But it didn't do everything make did. In particular, it didn't have
timestamp based file dependencies (where a file is rebuilt if any of its
prerequisite files have a later timestamp). Obviously THAT would be a
pain to add and so Ruby Make would remain an interesting experiment.
... Except as I walked back to my desk, I started thinking about what
file based dependencies would really need. Rats! I was hooked again,
and by adding a new class and two new methods, file/timestamp
dependencies were implemented.
Ok, now I was really hooked. Last night (during CSI!) I massaged the
code and cleaned it up a bit. The result is a bare-bones replacement
for make in exactly 100 lines of code.
For the curious, you can see it at ...
* doc/proto_rake.rdoc
Oh, about the name. When I wrote the example Ruby Make task on my
whiteboard, my coworker exclaimed "Oh! I have the perfect name: Rake ...
Get it? Ruby-Make. Rake!" He said he envisioned the tasks as leaves
and Rake would clean them up ... or something like that. Anyways, the
name stuck.
Some quick examples ...
A simple task to delete backup files ...
task :clean do
Dir['*~'].each {|fn| rm fn rescue nil}
end
Note that task names are symbols (they are slightly easier to type
than quoted strings ... but you may use quoted string if you would
rather). Rake makes the methods of the FileUtils module directly
available, so we take advantage of the <tt>rm</tt> command. Also note
the use of "rescue nil" to trap and ignore errors in the <tt>rm</tt>
command.
To run it, just type "rake clean". Rake will automatically find a
Rakefile in the current directory (or above!) and will invoke the
targets named on the command line. If there are no targets explicitly
named, rake will invoke the task "default".
Here's another task with dependencies ...
task :clobber => [:clean] do
rm_r "tempdir"
end
Task :clobber depends upon task :clean, so :clean will be run before
:clobber is executed.
Files are specified by using the "file" command. It is similar to the
task command, except that the task name represents a file, and the task
will be run only if the file doesn't exist, or if its modification time
is earlier than any of its prerequisites.
Here is a file based dependency that will compile "hello.cc" to
"hello.o".
file "hello.cc"
file "hello.o" => ["hello.cc"] do |t|
srcfile = t.name.sub(/\.o$/, ".cc")
sh %{g++ #{srcfile} -c -o #{t.name}}
end
I normally specify file tasks with string (rather than symbols). Some
file names can't be represented by symbols. Plus it makes the
distinction between them more clear to the casual reader.
Currently writing a task for each and every file in the project would be
tedious at best. I envision a set of libraries to make this job
easier. For instance, perhaps something like this ...
require 'rake/ctools'
Dir['*.c'].each do |fn|
c_source_file(fn)
end
where "c_source_file" will create all the tasks need to compile all the
C source files in a directory. Any number of useful libraries could be
created for rake.
That's it. There's no documentation (other than whats in this
message). Does this sound interesting to anyone? If so, I'll continue
to clean it up and write it up and publish it on RAA. Otherwise, I'll
leave it as an interesting exercise and a tribute to the power of Ruby.
Why /might/ rake be interesting to Ruby programmers. I don't know,
perhaps ...
* No weird make syntax (only weird Ruby syntax :-)
* No need to edit or read XML (a la ant)
* Platform independent build scripts.
* Will run anywhere Ruby exists, so no need to have "make" installed.
If you stay away from the "sys" command and use things like
'ftools', you can have a perfectly platform independent
build script. Also rake is only 100 lines of code, so it can
easily be packaged along with the rest of your code.
So ... Sorry for the long rambling message. Like I said, I never
intended to write this code at all.