This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,21 @@
|
||||
Copyright (c) Jim Weirich
|
||||
|
||||
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 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.
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
= RAKE -- Ruby Make
|
||||
|
||||
home :: https://github.com/ruby/rake
|
||||
bugs :: https://github.com/ruby/rake/issues
|
||||
docs :: https://ruby.github.io/rake
|
||||
|
||||
== Description
|
||||
|
||||
Rake is a Make-like program implemented in Ruby. Tasks and dependencies are
|
||||
specified in standard Ruby syntax.
|
||||
|
||||
Rake has the following features:
|
||||
|
||||
* Rakefiles (rake's version of Makefiles) are completely defined in
|
||||
standard Ruby syntax. No XML files to edit. No quirky Makefile
|
||||
syntax to worry about (is that a tab or a space?)
|
||||
|
||||
* Users can specify tasks with prerequisites.
|
||||
|
||||
* Rake supports rule patterns to synthesize implicit tasks.
|
||||
|
||||
* Flexible FileLists that act like arrays but know about manipulating
|
||||
file names and paths.
|
||||
|
||||
* A library of prepackaged tasks to make building rakefiles easier. For example,
|
||||
tasks for building tarballs. (Formerly
|
||||
tasks for building RDoc, Gems, and publishing to FTP were included in rake but they're now
|
||||
available in RDoc, RubyGems, and rake-contrib respectively.)
|
||||
|
||||
* Supports parallel execution of tasks.
|
||||
|
||||
== Installation
|
||||
|
||||
=== Gem Installation
|
||||
|
||||
Download and install rake with the following.
|
||||
|
||||
gem install rake
|
||||
|
||||
== Usage
|
||||
|
||||
=== Simple Example
|
||||
|
||||
First, you must write a "Rakefile" file which contains the build rules. Here's
|
||||
a simple example:
|
||||
|
||||
task default: %w[test]
|
||||
|
||||
task :test do
|
||||
ruby "test/unittest.rb"
|
||||
end
|
||||
|
||||
This Rakefile has two tasks:
|
||||
|
||||
* A task named "test", which -- upon invocation -- will run a unit test file
|
||||
in Ruby.
|
||||
* A task named "default". This task does nothing by itself, but it has exactly
|
||||
one dependency, namely the "test" task. Invoking the "default" task will
|
||||
cause Rake to invoke the "test" task as well.
|
||||
|
||||
Running the "rake" command without any options will cause it to run the
|
||||
"default" task in the Rakefile:
|
||||
|
||||
% ls
|
||||
Rakefile test/
|
||||
% rake
|
||||
(in /home/some_user/Projects/rake)
|
||||
ruby test/unittest.rb
|
||||
....unit test output here...
|
||||
|
||||
Type "rake --help" for all available options.
|
||||
|
||||
== Resources
|
||||
|
||||
=== Rake Information
|
||||
|
||||
* {Rake command-line}[link:doc/command_line_usage.rdoc]
|
||||
* {Writing Rakefiles}[link:doc/rakefile.rdoc]
|
||||
* The original {Rake announcement}[link:doc/rational.rdoc]
|
||||
* Rake {glossary}[link:doc/glossary.rdoc]
|
||||
|
||||
=== Presentations and Articles about Rake
|
||||
|
||||
* Avdi Grimm's rake series:
|
||||
1. {Rake Basics}[https://avdi.codes/rake-part-1-basics/]
|
||||
2. {Rake File Lists}[https://avdi.codes/rake-part-2-file-lists-2/]
|
||||
3. {Rake Rules}[https://avdi.codes/rake-part-3-rules/]
|
||||
4. {Rake Pathmap}[https://avdi.codes/rake-part-4-pathmap/]
|
||||
5. {File Operations}[https://avdi.codes/rake-part-5-file-operations/]
|
||||
6. {Clean and Clobber}[https://avdi.codes/rake-part-6-clean-and-clobber/]
|
||||
7. {MultiTask}[https://avdi.codes/rake-part-7-multitask/]
|
||||
* {Jim Weirich's 2003 RubyConf presentation}[https://web.archive.org/web/20140221123354/http://onestepback.org/articles/buildingwithrake/]
|
||||
* Martin Fowler's article on Rake: https://martinfowler.com/articles/rake.html
|
||||
|
||||
== Other Make Re-envisionings ...
|
||||
|
||||
Rake is a late entry in the make replacement field. Here are links to
|
||||
other projects with similar (and not so similar) goals.
|
||||
|
||||
* https://directory.fsf.org/wiki/Bras -- Bras, one of earliest
|
||||
implementations of "make in a scripting language".
|
||||
* http://www.a-a-p.org -- Make in Python
|
||||
* https://ant.apache.org -- The Ant project
|
||||
* https://search.cpan.org/search?query=PerlBuildSystem -- The Perl Build System
|
||||
* https://www.rubydoc.info/gems/rant/0.5.7/frames -- Rant, another Ruby make tool.
|
||||
|
||||
== Credits
|
||||
|
||||
[<b>Jim Weirich</b>] Who originally created Rake.
|
||||
|
||||
[<b>Ryan Dlugosz</b>] For the initial conversation that sparked Rake.
|
||||
|
||||
[<b>Nobuyoshi Nakada <nobu@ruby-lang.org></b>] For the initial patch for rule support.
|
||||
|
||||
[<b>Tilman Sauerbeck <tilman@code-monkey.de></b>] For the recursive rule patch.
|
||||
|
||||
[<b>Eric Hodel</b>] For aid in maintaining rake.
|
||||
|
||||
[<b>Hiroshi SHIBATA</b>] Maintainer of Rake 10 and later
|
||||
|
||||
== License
|
||||
|
||||
Rake is available under an MIT-style license.
|
||||
|
||||
:include: MIT-LICENSE
|
||||
|
||||
---
|
||||
|
||||
= Other stuff
|
||||
|
||||
Author:: Jim Weirich <jim.weirich@gmail.com>
|
||||
Requires:: Ruby 2.0.0 or later
|
||||
License:: Copyright Jim Weirich.
|
||||
Released under an MIT-style license. See the MIT-LICENSE
|
||||
file included in the distribution.
|
||||
|
||||
== Warranty
|
||||
|
||||
This software is provided "as is" and without any express or implied
|
||||
warranties, including, without limitation, the implied warranties of
|
||||
merchantability and fitness for a particular purpose.
|
||||
|
||||
== Historical
|
||||
|
||||
Rake was originally created by Jim Weirich, who unfortunately passed away in
|
||||
February 2014. This repository was originally hosted at
|
||||
{github.com/jimweirich/rake}[https://github.com/jimweirich/rake/], however
|
||||
with his passing, has been moved to {ruby/rake}[https://github.com/ruby/rake].
|
||||
|
||||
You can view Jim's last commit here:
|
||||
https://github.com/jimweirich/rake/tree/336559f28f55bce418e2ebcc0a57548dcbac4025
|
||||
|
||||
You can {read more about Jim}[https://en.wikipedia.org/wiki/Jim_Weirich] at Wikipedia.
|
||||
|
||||
Thank you for this great tool, Jim. We'll remember you.
|
||||
@@ -0,0 +1,158 @@
|
||||
= Rake Command Line Usage
|
||||
|
||||
Rake is invoked from the command line using:
|
||||
|
||||
% rake [options ...] [VAR=VALUE ...] [targets ...]
|
||||
|
||||
Options are:
|
||||
|
||||
[<tt><em>name</em>=<em>value</em></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 ENV['<em>name</em>'].
|
||||
|
||||
[<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 rules resolution.
|
||||
|
||||
[<tt>--silent (-s)</tt>]
|
||||
Like --quiet, but also suppresses the 'in directory' announcement.
|
||||
|
||||
[<tt>--suppress-backtrace _pattern_ </tt>]
|
||||
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.
|
||||
|
||||
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
|
||||
@@ -0,0 +1,6 @@
|
||||
#include <stdio.h>
|
||||
|
||||
void a()
|
||||
{
|
||||
printf ("In function a\n");
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#include <stdio.h>
|
||||
|
||||
void b()
|
||||
{
|
||||
printf ("In function b\n");
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#include <stdio.h>
|
||||
|
||||
extern void a();
|
||||
extern void b();
|
||||
|
||||
int main ()
|
||||
{
|
||||
a();
|
||||
b();
|
||||
return 0;
|
||||
}
|
||||
@@ -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.
|
||||
@@ -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
|
||||
(<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
|
||||
(<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> </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
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -0,0 +1,622 @@
|
||||
= 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.
|
||||
|
||||
== 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.
|
||||
@@ -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.
|
||||
Executable
+27
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
#--
|
||||
# Copyright (c) 2003, 2004, 2005, 2006, 2007 Jim Weirich
|
||||
#
|
||||
# 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 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.
|
||||
#++
|
||||
|
||||
require "rake"
|
||||
|
||||
Rake.application.run
|
||||
@@ -0,0 +1,70 @@
|
||||
# frozen_string_literal: true
|
||||
#--
|
||||
# Copyright 2003-2010 by Jim Weirich (jim.weirich@gmail.com)
|
||||
#
|
||||
# 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 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.
|
||||
#++
|
||||
|
||||
module Rake; end
|
||||
|
||||
require "rake/version"
|
||||
|
||||
require "rbconfig"
|
||||
require "fileutils"
|
||||
require "singleton"
|
||||
require "monitor"
|
||||
require "optparse"
|
||||
|
||||
require "rake/ext/string"
|
||||
|
||||
require "rake/win32"
|
||||
|
||||
require "rake/linked_list"
|
||||
require "rake/cpu_counter"
|
||||
require "rake/scope"
|
||||
require "rake/task_argument_error"
|
||||
require "rake/rule_recursion_overflow_error"
|
||||
require "rake/rake_module"
|
||||
require "rake/trace_output"
|
||||
require "rake/pseudo_status"
|
||||
require "rake/task_arguments"
|
||||
require "rake/invocation_chain"
|
||||
require "rake/task"
|
||||
require "rake/file_task"
|
||||
require "rake/file_creation_task"
|
||||
require "rake/multi_task"
|
||||
require "rake/dsl_definition"
|
||||
require "rake/file_utils_ext"
|
||||
require "rake/file_list"
|
||||
require "rake/default_loader"
|
||||
require "rake/early_time"
|
||||
require "rake/late_time"
|
||||
require "rake/name_space"
|
||||
require "rake/task_manager"
|
||||
require "rake/application"
|
||||
require "rake/backtrace"
|
||||
|
||||
$trace = false
|
||||
|
||||
# :stopdoc:
|
||||
#
|
||||
# Some top level Constants.
|
||||
|
||||
FileList = Rake::FileList
|
||||
RakeFileUtils = Rake::FileUtilsExt
|
||||
@@ -0,0 +1,861 @@
|
||||
# frozen_string_literal: true
|
||||
require "optparse"
|
||||
|
||||
require "rake/task_manager"
|
||||
require "rake/file_list"
|
||||
require "rake/thread_pool"
|
||||
require "rake/thread_history_display"
|
||||
require "rake/trace_output"
|
||||
require "rake/win32"
|
||||
|
||||
module Rake
|
||||
|
||||
CommandLineOptionError = Class.new(StandardError)
|
||||
|
||||
##
|
||||
# Rake main application object. When invoking +rake+ from the
|
||||
# command line, a Rake::Application object is created and run.
|
||||
|
||||
class Application
|
||||
include TaskManager
|
||||
include TraceOutput
|
||||
|
||||
# The name of the application (typically 'rake')
|
||||
attr_reader :name
|
||||
|
||||
# The original directory where rake was invoked.
|
||||
attr_reader :original_dir
|
||||
|
||||
# Name of the actual rakefile used.
|
||||
attr_reader :rakefile
|
||||
|
||||
# Number of columns on the terminal
|
||||
attr_accessor :terminal_columns
|
||||
|
||||
# List of the top level task names (task names from the command line).
|
||||
attr_reader :top_level_tasks
|
||||
|
||||
# Override the detected TTY output state (mostly for testing)
|
||||
attr_writer :tty_output
|
||||
|
||||
DEFAULT_RAKEFILES = [
|
||||
"rakefile",
|
||||
"Rakefile",
|
||||
"rakefile.rb",
|
||||
"Rakefile.rb"
|
||||
].freeze
|
||||
|
||||
# Initialize a Rake::Application object.
|
||||
def initialize
|
||||
super
|
||||
@name = "rake"
|
||||
@rakefiles = DEFAULT_RAKEFILES.dup
|
||||
@rakefile = nil
|
||||
@pending_imports = []
|
||||
@imported = []
|
||||
@loaders = {}
|
||||
@default_loader = Rake::DefaultLoader.new
|
||||
@original_dir = Dir.pwd
|
||||
@top_level_tasks = []
|
||||
add_loader("rb", DefaultLoader.new)
|
||||
add_loader("rf", DefaultLoader.new)
|
||||
add_loader("rake", DefaultLoader.new)
|
||||
@tty_output = STDOUT.tty?
|
||||
@terminal_columns = ENV["RAKE_COLUMNS"].to_i
|
||||
|
||||
set_default_options
|
||||
end
|
||||
|
||||
# Run the Rake application. The run method performs the following
|
||||
# three steps:
|
||||
#
|
||||
# * Initialize the command line options (+init+).
|
||||
# * Define the tasks (+load_rakefile+).
|
||||
# * Run the top level tasks (+top_level+).
|
||||
#
|
||||
# If you wish to build a custom rake command, you should call
|
||||
# +init+ on your application. Then define any tasks. Finally,
|
||||
# call +top_level+ to run your top level tasks.
|
||||
def run(argv = ARGV)
|
||||
standard_exception_handling do
|
||||
init "rake", argv
|
||||
load_rakefile
|
||||
top_level
|
||||
end
|
||||
end
|
||||
|
||||
# Initialize the command line parameters and app name.
|
||||
def init(app_name="rake", argv = ARGV)
|
||||
standard_exception_handling do
|
||||
@name = app_name
|
||||
begin
|
||||
args = handle_options argv
|
||||
rescue ArgumentError
|
||||
# Backward compatibility for capistrano
|
||||
args = handle_options
|
||||
end
|
||||
load_debug_at_stop_feature
|
||||
collect_command_line_tasks(args)
|
||||
end
|
||||
end
|
||||
|
||||
def load_debug_at_stop_feature
|
||||
return unless ENV["RAKE_DEBUG"]
|
||||
require "debug/session"
|
||||
DEBUGGER__::start no_sigint_hook: true, nonstop: true
|
||||
Rake::Task.prepend Module.new {
|
||||
def execute(*)
|
||||
exception = DEBUGGER__::SESSION.capture_exception_frames(/(exe|bin|lib)\/rake/) do
|
||||
super
|
||||
end
|
||||
|
||||
if exception
|
||||
STDERR.puts exception.message
|
||||
DEBUGGER__::SESSION.enter_postmortem_session exception
|
||||
raise exception
|
||||
end
|
||||
end
|
||||
}
|
||||
rescue LoadError
|
||||
end
|
||||
private :load_debug_at_stop_feature
|
||||
|
||||
# Find the rakefile and then load it and any pending imports.
|
||||
def load_rakefile
|
||||
standard_exception_handling do
|
||||
raw_load_rakefile
|
||||
end
|
||||
end
|
||||
|
||||
# Run the top level tasks of a Rake application.
|
||||
def top_level
|
||||
run_with_threads do
|
||||
if options.show_tasks
|
||||
display_tasks_and_comments
|
||||
elsif options.show_prereqs
|
||||
display_prerequisites
|
||||
else
|
||||
top_level_tasks.each { |task_name| invoke_task(task_name) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Run the given block with the thread startup and shutdown.
|
||||
def run_with_threads
|
||||
thread_pool.gather_history if options.job_stats == :history
|
||||
|
||||
yield
|
||||
|
||||
thread_pool.join if defined?(@thread_pool)
|
||||
if options.job_stats
|
||||
stats = thread_pool.statistics
|
||||
puts "Maximum active threads: #{stats[:max_active_threads]} + main"
|
||||
puts "Total threads in play: #{stats[:total_threads_in_play]} + main"
|
||||
end
|
||||
ThreadHistoryDisplay.new(thread_pool.history).show if
|
||||
options.job_stats == :history
|
||||
end
|
||||
|
||||
# Add a loader to handle imported files ending in the extension
|
||||
# +ext+.
|
||||
def add_loader(ext, loader)
|
||||
ext = ".#{ext}" unless ext =~ /^\./
|
||||
@loaders[ext] = loader
|
||||
end
|
||||
|
||||
# Application options from the command line
|
||||
def options
|
||||
@options ||= Struct.new(
|
||||
:always_multitask, :backtrace, :build_all, :dryrun,
|
||||
:ignore_deprecate, :ignore_system, :job_stats, :load_system,
|
||||
:nosearch, :rakelib, :show_all_tasks, :show_prereqs,
|
||||
:show_task_pattern, :show_tasks, :silent, :suppress_backtrace_pattern,
|
||||
:thread_pool_size, :trace, :trace_output, :trace_rules
|
||||
).new
|
||||
end
|
||||
|
||||
# Return the thread pool used for multithreaded processing.
|
||||
def thread_pool # :nodoc:
|
||||
@thread_pool ||= ThreadPool.new(options.thread_pool_size || Rake.suggested_thread_count-1)
|
||||
end
|
||||
|
||||
# internal ----------------------------------------------------------------
|
||||
|
||||
# Invokes a task with arguments that are extracted from +task_string+
|
||||
def invoke_task(task_string) # :nodoc:
|
||||
name, args = parse_task_string(task_string)
|
||||
t = self[name]
|
||||
t.invoke(*args)
|
||||
end
|
||||
|
||||
def parse_task_string(string) # :nodoc:
|
||||
/^([^\[]+)(?:\[(.*)\])$/ =~ string.to_s
|
||||
|
||||
name = $1
|
||||
remaining_args = $2
|
||||
|
||||
return string, [] unless name
|
||||
return name, [] if remaining_args.empty?
|
||||
|
||||
args = []
|
||||
|
||||
begin
|
||||
/\s*((?:[^\\,]|\\.)*?)\s*(?:,\s*(.*))?$/ =~ remaining_args
|
||||
|
||||
remaining_args = $2
|
||||
args << $1.gsub(/\\(.)/, '\1')
|
||||
end while remaining_args
|
||||
|
||||
return name, args
|
||||
end
|
||||
|
||||
# Provide standard exception handling for the given block.
|
||||
def standard_exception_handling # :nodoc:
|
||||
yield
|
||||
rescue SystemExit
|
||||
# Exit silently with current status
|
||||
raise
|
||||
rescue OptionParser::InvalidOption => ex
|
||||
$stderr.puts ex.message
|
||||
exit(false)
|
||||
rescue Exception => ex
|
||||
# Exit with error message
|
||||
display_error_message(ex)
|
||||
exit_because_of_exception(ex)
|
||||
end
|
||||
|
||||
# Exit the program because of an unhandled exception.
|
||||
# (may be overridden by subclasses)
|
||||
def exit_because_of_exception(ex) # :nodoc:
|
||||
exit(false)
|
||||
end
|
||||
|
||||
# Display the error message that caused the exception.
|
||||
def display_error_message(ex) # :nodoc:
|
||||
trace "#{name} aborted!"
|
||||
display_exception_details(ex)
|
||||
trace "Tasks: #{ex.chain}" if has_chain?(ex)
|
||||
trace "(See full trace by running task with --trace)" unless
|
||||
options.backtrace
|
||||
end
|
||||
|
||||
def display_exception_details(ex) # :nodoc:
|
||||
display_exception_details_seen << ex
|
||||
|
||||
display_exception_message_details(ex)
|
||||
display_exception_backtrace(ex) if ex.backtrace
|
||||
display_cause_details(ex.cause) if has_cause?(ex)
|
||||
end
|
||||
|
||||
def display_cause_details(ex) # :nodoc:
|
||||
return if display_exception_details_seen.include? ex
|
||||
|
||||
trace "\nCaused by:"
|
||||
display_exception_details(ex)
|
||||
end
|
||||
|
||||
def display_exception_details_seen # :nodoc:
|
||||
Thread.current[:rake_display_exception_details_seen] ||= []
|
||||
end
|
||||
|
||||
def has_cause?(ex) # :nodoc:
|
||||
ex.respond_to?(:cause) && ex.cause
|
||||
end
|
||||
|
||||
def display_exception_message_details(ex) # :nodoc:
|
||||
if ex.instance_of?(RuntimeError)
|
||||
trace ex.message
|
||||
elsif ex.respond_to?(:detailed_message)
|
||||
trace "#{ex.class.name}: #{ex.detailed_message(highlight: false)}"
|
||||
else
|
||||
trace "#{ex.class.name}: #{ex.message}"
|
||||
end
|
||||
end
|
||||
|
||||
def display_exception_backtrace(ex) # :nodoc:
|
||||
if options.backtrace
|
||||
trace ex.backtrace.join("\n")
|
||||
else
|
||||
trace Backtrace.collapse(ex.backtrace).join("\n")
|
||||
end
|
||||
end
|
||||
|
||||
# Warn about deprecated usage.
|
||||
#
|
||||
# Example:
|
||||
# Rake.application.deprecate("import", "Rake.import", caller.first)
|
||||
#
|
||||
def deprecate(old_usage, new_usage, call_site) # :nodoc:
|
||||
unless options.ignore_deprecate
|
||||
$stderr.puts "WARNING: '#{old_usage}' is deprecated. " +
|
||||
"Please use '#{new_usage}' instead.\n" +
|
||||
" at #{call_site}"
|
||||
end
|
||||
end
|
||||
|
||||
# Does the exception have a task invocation chain?
|
||||
def has_chain?(exception) # :nodoc:
|
||||
exception.respond_to?(:chain) && exception.chain
|
||||
end
|
||||
private :has_chain?
|
||||
|
||||
# True if one of the files in RAKEFILES is in the current directory.
|
||||
# If a match is found, it is copied into @rakefile.
|
||||
def have_rakefile # :nodoc:
|
||||
@rakefiles.each do |fn|
|
||||
if File.exist?(fn)
|
||||
others = FileList.glob(fn, File::FNM_CASEFOLD)
|
||||
return others.size == 1 ? others.first : fn
|
||||
elsif fn == ""
|
||||
return fn
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
# True if we are outputting to TTY, false otherwise
|
||||
def tty_output? # :nodoc:
|
||||
@tty_output
|
||||
end
|
||||
|
||||
# We will truncate output if we are outputting to a TTY or if we've been
|
||||
# given an explicit column width to honor
|
||||
def truncate_output? # :nodoc:
|
||||
tty_output? || @terminal_columns.nonzero?
|
||||
end
|
||||
|
||||
# Display the tasks and comments.
|
||||
def display_tasks_and_comments # :nodoc:
|
||||
displayable_tasks = tasks.select { |t|
|
||||
(options.show_all_tasks || t.comment) &&
|
||||
t.name =~ options.show_task_pattern
|
||||
}
|
||||
case options.show_tasks
|
||||
when :tasks
|
||||
width = displayable_tasks.map { |t| t.name_with_args.length }.max || 10
|
||||
if truncate_output?
|
||||
max_column = terminal_width - name.size - width - 7
|
||||
else
|
||||
max_column = nil
|
||||
end
|
||||
|
||||
displayable_tasks.each do |t|
|
||||
printf("#{name} %-#{width}s # %s\n",
|
||||
t.name_with_args,
|
||||
max_column ? truncate(t.comment, max_column) : t.comment)
|
||||
end
|
||||
when :describe
|
||||
displayable_tasks.each do |t|
|
||||
puts "#{name} #{t.name_with_args}"
|
||||
comment = t.full_comment || ""
|
||||
comment.split("\n").each do |line|
|
||||
puts " #{line}"
|
||||
end
|
||||
puts
|
||||
end
|
||||
when :lines
|
||||
displayable_tasks.each do |t|
|
||||
t.locations.each do |loc|
|
||||
printf "#{name} %-30s %s\n", t.name_with_args, loc
|
||||
end
|
||||
end
|
||||
else
|
||||
fail "Unknown show task mode: '#{options.show_tasks}'"
|
||||
end
|
||||
end
|
||||
|
||||
def terminal_width # :nodoc:
|
||||
if @terminal_columns.nonzero?
|
||||
result = @terminal_columns
|
||||
else
|
||||
result = unix? ? dynamic_width : 80
|
||||
end
|
||||
(result < 10) ? 80 : result
|
||||
rescue
|
||||
80
|
||||
end
|
||||
|
||||
# Calculate the dynamic width of the
|
||||
def dynamic_width # :nodoc:
|
||||
@dynamic_width ||= (dynamic_width_stty.nonzero? || dynamic_width_tput)
|
||||
end
|
||||
|
||||
def dynamic_width_stty # :nodoc:
|
||||
%x{stty size 2>/dev/null}.split[1].to_i
|
||||
end
|
||||
|
||||
def dynamic_width_tput # :nodoc:
|
||||
%x{tput cols 2>/dev/null}.to_i
|
||||
end
|
||||
|
||||
def unix? # :nodoc:
|
||||
RbConfig::CONFIG["host_os"] =~
|
||||
/(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i
|
||||
end
|
||||
|
||||
def windows? # :nodoc:
|
||||
Win32.windows?
|
||||
end
|
||||
|
||||
def truncate(string, width) # :nodoc:
|
||||
if string.nil?
|
||||
""
|
||||
elsif string.length <= width
|
||||
string
|
||||
else
|
||||
(string[0, width - 3] || "") + "..."
|
||||
end
|
||||
end
|
||||
|
||||
# Display the tasks and prerequisites
|
||||
def display_prerequisites # :nodoc:
|
||||
tasks.each do |t|
|
||||
puts "#{name} #{t.name}"
|
||||
t.prerequisites.each { |pre| puts " #{pre}" }
|
||||
end
|
||||
end
|
||||
|
||||
def trace(*strings) # :nodoc:
|
||||
options.trace_output ||= $stderr
|
||||
trace_on(options.trace_output, *strings)
|
||||
end
|
||||
|
||||
def sort_options(options) # :nodoc:
|
||||
options.sort_by { |opt|
|
||||
opt.select { |o| o.is_a?(String) && o =~ /^-/ }.map(&:downcase).sort.reverse
|
||||
}
|
||||
end
|
||||
private :sort_options
|
||||
|
||||
# A list of all the standard options used in rake, suitable for
|
||||
# passing to OptionParser.
|
||||
def standard_rake_options # :nodoc:
|
||||
sort_options(
|
||||
[
|
||||
["--all", "-A",
|
||||
"Show all tasks, even uncommented ones (in combination with -T or -D)",
|
||||
lambda { |value|
|
||||
options.show_all_tasks = value
|
||||
}
|
||||
],
|
||||
["--backtrace=[OUT]",
|
||||
"Enable full backtrace. OUT can be stderr (default) or stdout.",
|
||||
lambda { |value|
|
||||
options.backtrace = true
|
||||
select_trace_output(options, "backtrace", value)
|
||||
}
|
||||
],
|
||||
["--build-all", "-B",
|
||||
"Build all prerequisites, including those which are up-to-date.",
|
||||
lambda { |value|
|
||||
options.build_all = true
|
||||
}
|
||||
],
|
||||
["--comments",
|
||||
"Show commented tasks only",
|
||||
lambda { |value|
|
||||
options.show_all_tasks = !value
|
||||
}
|
||||
],
|
||||
["--describe", "-D [PATTERN]",
|
||||
"Describe the tasks (matching optional PATTERN), then exit.",
|
||||
lambda { |value|
|
||||
select_tasks_to_show(options, :describe, value)
|
||||
}
|
||||
],
|
||||
["--directory", "-C [DIRECTORY]",
|
||||
"Change to DIRECTORY before doing anything.",
|
||||
lambda { |value|
|
||||
Dir.chdir value
|
||||
@original_dir = Dir.pwd
|
||||
}
|
||||
],
|
||||
["--dry-run", "-n",
|
||||
"Do a dry run without executing actions.",
|
||||
lambda { |value|
|
||||
Rake.verbose(true)
|
||||
Rake.nowrite(true)
|
||||
options.dryrun = true
|
||||
options.trace = true
|
||||
}
|
||||
],
|
||||
["--execute", "-e CODE",
|
||||
"Execute some Ruby code and exit.",
|
||||
lambda { |value|
|
||||
eval(value)
|
||||
exit
|
||||
}
|
||||
],
|
||||
["--execute-print", "-p CODE",
|
||||
"Execute some Ruby code, print the result, then exit.",
|
||||
lambda { |value|
|
||||
puts eval(value)
|
||||
exit
|
||||
}
|
||||
],
|
||||
["--execute-continue", "-E CODE",
|
||||
"Execute some Ruby code, " +
|
||||
"then continue with normal task processing.",
|
||||
lambda { |value| eval(value) }
|
||||
],
|
||||
["--jobs", "-j [NUMBER]",
|
||||
"Specifies the maximum number of tasks to execute in parallel. " +
|
||||
"(default is number of CPU cores + 4)",
|
||||
lambda { |value|
|
||||
if value.nil? || value == ""
|
||||
value = Float::INFINITY
|
||||
elsif value =~ /^\d+$/
|
||||
value = value.to_i
|
||||
else
|
||||
value = Rake.suggested_thread_count
|
||||
end
|
||||
value = 1 if value < 1
|
||||
options.thread_pool_size = value - 1
|
||||
}
|
||||
],
|
||||
["--job-stats [LEVEL]",
|
||||
"Display job statistics. " +
|
||||
"LEVEL=history displays a complete job list",
|
||||
lambda { |value|
|
||||
if value =~ /^history/i
|
||||
options.job_stats = :history
|
||||
else
|
||||
options.job_stats = true
|
||||
end
|
||||
}
|
||||
],
|
||||
["--libdir", "-I LIBDIR",
|
||||
"Include LIBDIR in the search path for required modules.",
|
||||
lambda { |value| $:.push(value) }
|
||||
],
|
||||
["--multitask", "-m",
|
||||
"Treat all tasks as multitasks.",
|
||||
lambda { |value| options.always_multitask = true }
|
||||
],
|
||||
["--no-search", "--nosearch",
|
||||
"-N", "Do not search parent directories for the Rakefile.",
|
||||
lambda { |value| options.nosearch = true }
|
||||
],
|
||||
["--prereqs", "-P",
|
||||
"Display the tasks and dependencies, then exit.",
|
||||
lambda { |value| options.show_prereqs = true }
|
||||
],
|
||||
["--quiet", "-q",
|
||||
"Do not log messages to standard output.",
|
||||
lambda { |value| Rake.verbose(false) }
|
||||
],
|
||||
["--rakefile", "-f [FILENAME]",
|
||||
"Use FILENAME as the rakefile to search for.",
|
||||
lambda { |value|
|
||||
value ||= ""
|
||||
@rakefiles.clear
|
||||
@rakefiles << value
|
||||
}
|
||||
],
|
||||
["--rakelibdir", "--rakelib", "-R RAKELIBDIR",
|
||||
"Auto-import any .rake files in RAKELIBDIR. " +
|
||||
"(default is 'rakelib')",
|
||||
lambda { |value|
|
||||
options.rakelib = value.split(File::PATH_SEPARATOR)
|
||||
}
|
||||
],
|
||||
["--require", "-r MODULE",
|
||||
"Require MODULE before executing rakefile.",
|
||||
lambda { |value|
|
||||
begin
|
||||
require value
|
||||
rescue LoadError => ex
|
||||
begin
|
||||
rake_require value
|
||||
rescue LoadError
|
||||
raise ex
|
||||
end
|
||||
end
|
||||
}
|
||||
],
|
||||
["--rules",
|
||||
"Trace the rules resolution.",
|
||||
lambda { |value| options.trace_rules = true }
|
||||
],
|
||||
["--silent", "-s",
|
||||
"Like --quiet, but also suppresses the " +
|
||||
"'in directory' announcement.",
|
||||
lambda { |value|
|
||||
Rake.verbose(false)
|
||||
options.silent = true
|
||||
}
|
||||
],
|
||||
["--suppress-backtrace PATTERN",
|
||||
"Suppress backtrace lines matching regexp PATTERN. " +
|
||||
"Ignored if --trace is on.",
|
||||
lambda { |value|
|
||||
options.suppress_backtrace_pattern = Regexp.new(value)
|
||||
}
|
||||
],
|
||||
["--system", "-g",
|
||||
"Using system wide (global) rakefiles " +
|
||||
"(usually '~/.rake/*.rake').",
|
||||
lambda { |value| options.load_system = true }
|
||||
],
|
||||
["--no-system", "--nosystem", "-G",
|
||||
"Use standard project Rakefile search paths, " +
|
||||
"ignore system wide rakefiles.",
|
||||
lambda { |value| options.ignore_system = true }
|
||||
],
|
||||
["--tasks", "-T [PATTERN]",
|
||||
"Display the tasks (matching optional PATTERN) " +
|
||||
"with descriptions, then exit. " +
|
||||
"-AT combination displays all the tasks, including those without descriptions.",
|
||||
lambda { |value|
|
||||
select_tasks_to_show(options, :tasks, value)
|
||||
}
|
||||
],
|
||||
["--trace=[OUT]", "-t",
|
||||
"Turn on invoke/execute tracing, enable full backtrace. " +
|
||||
"OUT can be stderr (default) or stdout.",
|
||||
lambda { |value|
|
||||
options.trace = true
|
||||
options.backtrace = true
|
||||
select_trace_output(options, "trace", value)
|
||||
Rake.verbose(true)
|
||||
}
|
||||
],
|
||||
["--verbose", "-v",
|
||||
"Log message to standard output.",
|
||||
lambda { |value| Rake.verbose(true) }
|
||||
],
|
||||
["--version", "-V",
|
||||
"Display the program version.",
|
||||
lambda { |value|
|
||||
puts "rake, version #{Rake::VERSION}"
|
||||
exit
|
||||
}
|
||||
],
|
||||
["--where", "-W [PATTERN]",
|
||||
"Describe the tasks (matching optional PATTERN), then exit.",
|
||||
lambda { |value|
|
||||
select_tasks_to_show(options, :lines, value)
|
||||
options.show_all_tasks = true
|
||||
}
|
||||
],
|
||||
["--no-deprecation-warnings", "-X",
|
||||
"Disable the deprecation warnings.",
|
||||
lambda { |value|
|
||||
options.ignore_deprecate = true
|
||||
}
|
||||
],
|
||||
])
|
||||
end
|
||||
|
||||
def select_tasks_to_show(options, show_tasks, value) # :nodoc:
|
||||
options.show_tasks = show_tasks
|
||||
options.show_task_pattern = Regexp.new(value || "")
|
||||
Rake::TaskManager.record_task_metadata = true
|
||||
end
|
||||
private :select_tasks_to_show
|
||||
|
||||
def select_trace_output(options, trace_option, value) # :nodoc:
|
||||
value = value.strip unless value.nil?
|
||||
case value
|
||||
when "stdout"
|
||||
options.trace_output = $stdout
|
||||
when "stderr", nil
|
||||
options.trace_output = $stderr
|
||||
else
|
||||
fail CommandLineOptionError,
|
||||
"Unrecognized --#{trace_option} option '#{value}'"
|
||||
end
|
||||
end
|
||||
private :select_trace_output
|
||||
|
||||
# Read and handle the command line options. Returns the command line
|
||||
# arguments that we didn't understand, which should (in theory) be just
|
||||
# task names and env vars.
|
||||
def handle_options(argv) # :nodoc:
|
||||
set_default_options
|
||||
|
||||
OptionParser.new do |opts|
|
||||
opts.banner = "#{Rake.application.name} [-f rakefile] {options} targets..."
|
||||
opts.separator ""
|
||||
opts.separator "Options are ..."
|
||||
|
||||
opts.on_tail("-h", "--help", "-H", "Display this help message.") do
|
||||
puts opts
|
||||
exit
|
||||
end
|
||||
|
||||
standard_rake_options.each { |args| opts.on(*args) }
|
||||
opts.environment("RAKEOPT")
|
||||
end.parse(argv)
|
||||
end
|
||||
|
||||
# Similar to the regular Ruby +require+ command, but will check
|
||||
# for *.rake files in addition to *.rb files.
|
||||
def rake_require(file_name, paths=$LOAD_PATH, loaded=$") # :nodoc:
|
||||
fn = file_name + ".rake"
|
||||
return false if loaded.include?(fn)
|
||||
paths.each do |path|
|
||||
full_path = File.join(path, fn)
|
||||
if File.exist?(full_path)
|
||||
Rake.load_rakefile(full_path)
|
||||
loaded << fn
|
||||
return true
|
||||
end
|
||||
end
|
||||
fail LoadError, "Can't find #{file_name}"
|
||||
end
|
||||
|
||||
def find_rakefile_location # :nodoc:
|
||||
here = Dir.pwd
|
||||
until (fn = have_rakefile)
|
||||
Dir.chdir("..")
|
||||
return nil if Dir.pwd == here || options.nosearch
|
||||
here = Dir.pwd
|
||||
end
|
||||
[fn, here]
|
||||
ensure
|
||||
Dir.chdir(Rake.original_dir)
|
||||
end
|
||||
|
||||
def print_rakefile_directory(location) # :nodoc:
|
||||
$stderr.puts "(in #{Dir.pwd})" unless
|
||||
options.silent or original_dir == location
|
||||
end
|
||||
|
||||
def raw_load_rakefile # :nodoc:
|
||||
rakefile, location = find_rakefile_location
|
||||
if (!options.ignore_system) &&
|
||||
(options.load_system || rakefile.nil?) &&
|
||||
system_dir && File.directory?(system_dir)
|
||||
print_rakefile_directory(location)
|
||||
glob("#{system_dir}/*.rake") do |name|
|
||||
add_import name
|
||||
end
|
||||
else
|
||||
fail "No Rakefile found (looking for: #{@rakefiles.join(', ')})" if
|
||||
rakefile.nil?
|
||||
@rakefile = rakefile
|
||||
Dir.chdir(location)
|
||||
print_rakefile_directory(location)
|
||||
Rake.load_rakefile(File.expand_path(@rakefile)) if
|
||||
@rakefile && @rakefile != ""
|
||||
options.rakelib.each do |rlib|
|
||||
glob("#{rlib}/*.rake") do |name|
|
||||
add_import name
|
||||
end
|
||||
end
|
||||
end
|
||||
load_imports
|
||||
end
|
||||
|
||||
def glob(path, &block) # :nodoc:
|
||||
FileList.glob(path.tr("\\", "/")).each(&block)
|
||||
end
|
||||
private :glob
|
||||
|
||||
# The directory path containing the system wide rakefiles.
|
||||
def system_dir # :nodoc:
|
||||
@system_dir ||=
|
||||
begin
|
||||
if ENV["RAKE_SYSTEM"]
|
||||
ENV["RAKE_SYSTEM"]
|
||||
else
|
||||
standard_system_dir
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# The standard directory containing system wide rake files.
|
||||
if Win32.windows?
|
||||
def standard_system_dir #:nodoc:
|
||||
Win32.win32_system_dir
|
||||
end
|
||||
else
|
||||
def standard_system_dir #:nodoc:
|
||||
File.join(File.expand_path("~"), ".rake")
|
||||
end
|
||||
end
|
||||
private :standard_system_dir
|
||||
|
||||
# Collect the list of tasks on the command line. If no tasks are
|
||||
# given, return a list containing only the default task.
|
||||
# Environmental assignments are processed at this time as well.
|
||||
#
|
||||
# `args` is the list of arguments to peruse to get the list of tasks.
|
||||
# It should be the command line that was given to rake, less any
|
||||
# recognised command-line options, which OptionParser.parse will
|
||||
# have taken care of already.
|
||||
def collect_command_line_tasks(args) # :nodoc:
|
||||
@top_level_tasks = []
|
||||
args.each do |arg|
|
||||
if arg =~ /^(\w+)=(.*)$/m
|
||||
ENV[$1] = $2
|
||||
else
|
||||
@top_level_tasks << arg unless arg =~ /^-/
|
||||
end
|
||||
end
|
||||
@top_level_tasks.push(default_task_name) if @top_level_tasks.empty?
|
||||
end
|
||||
|
||||
# Default task name ("default").
|
||||
# (May be overridden by subclasses)
|
||||
def default_task_name # :nodoc:
|
||||
"default"
|
||||
end
|
||||
|
||||
# Add a file to the list of files to be imported.
|
||||
def add_import(fn) # :nodoc:
|
||||
@pending_imports << fn
|
||||
end
|
||||
|
||||
# Load the pending list of imported files.
|
||||
def load_imports # :nodoc:
|
||||
while fn = @pending_imports.shift
|
||||
next if @imported.member?(fn)
|
||||
fn_task = lookup(fn) and fn_task.invoke
|
||||
ext = File.extname(fn)
|
||||
loader = @loaders[ext] || @default_loader
|
||||
loader.load(fn)
|
||||
if fn_task = lookup(fn) and fn_task.needed?
|
||||
fn_task.reenable
|
||||
fn_task.invoke
|
||||
loader.load(fn)
|
||||
end
|
||||
@imported << fn
|
||||
end
|
||||
end
|
||||
|
||||
def rakefile_location(backtrace=caller) # :nodoc:
|
||||
backtrace.map { |t| t[/([^:]+):/, 1] }
|
||||
|
||||
re = /^#{@rakefile}$/
|
||||
re = /#{re.source}/i if windows?
|
||||
|
||||
backtrace.find { |str| str =~ re } || ""
|
||||
end
|
||||
|
||||
def set_default_options # :nodoc:
|
||||
options.always_multitask = false
|
||||
options.backtrace = false
|
||||
options.build_all = false
|
||||
options.dryrun = false
|
||||
options.ignore_deprecate = false
|
||||
options.ignore_system = false
|
||||
options.job_stats = false
|
||||
options.load_system = false
|
||||
options.nosearch = false
|
||||
options.rakelib = %w[rakelib]
|
||||
options.show_all_tasks = false
|
||||
options.show_prereqs = false
|
||||
options.show_task_pattern = nil
|
||||
options.show_tasks = nil
|
||||
options.silent = false
|
||||
options.suppress_backtrace_pattern = nil
|
||||
options.thread_pool_size = Rake.suggested_thread_count
|
||||
options.trace = false
|
||||
options.trace_output = $stderr
|
||||
options.trace_rules = false
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,25 @@
|
||||
# frozen_string_literal: true
|
||||
module Rake
|
||||
module Backtrace # :nodoc: all
|
||||
SYS_KEYS = RbConfig::CONFIG.keys.grep(/(?:[a-z]prefix|libdir)\z/)
|
||||
SYS_PATHS = RbConfig::CONFIG.values_at(*SYS_KEYS).uniq +
|
||||
[ File.join(File.dirname(__FILE__), "..") ]
|
||||
|
||||
SUPPRESSED_PATHS = SYS_PATHS.
|
||||
map { |s| s.tr("\\", "/") }.
|
||||
map { |f| File.expand_path(f) }.
|
||||
reject { |s| s.nil? || s =~ /^ *$/ }
|
||||
SUPPRESSED_PATHS_RE = SUPPRESSED_PATHS.map { |f| Regexp.quote(f) }.join("|")
|
||||
SUPPRESSED_PATHS_RE << "|^<internal:\\w+>"
|
||||
SUPPRESSED_PATHS_RE << "|^org\\/jruby\\/\\w+\\.java" if
|
||||
Object.const_defined?(:RUBY_ENGINE) and RUBY_ENGINE == "jruby"
|
||||
|
||||
SUPPRESS_PATTERN = %r!(\A(#{SUPPRESSED_PATHS_RE})|bin/rake:\d+)!i
|
||||
|
||||
def self.collapse(backtrace)
|
||||
pattern = Rake.application.options.suppress_backtrace_pattern ||
|
||||
SUPPRESS_PATTERN
|
||||
backtrace.reject { |elem| elem =~ pattern }
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,78 @@
|
||||
# frozen_string_literal: true
|
||||
# The 'rake/clean' file defines two file lists (CLEAN and CLOBBER) and
|
||||
# two rake tasks (:clean and :clobber).
|
||||
#
|
||||
# [:clean] Clean up the project by deleting scratch files and backup
|
||||
# files. Add files to the CLEAN file list 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 file list.
|
||||
# The intent of this task is to return a project to its
|
||||
# pristine, just unpacked state.
|
||||
|
||||
require "rake"
|
||||
|
||||
# :stopdoc:
|
||||
|
||||
module Rake
|
||||
module Cleaner
|
||||
extend FileUtils
|
||||
|
||||
module_function
|
||||
|
||||
def cleanup_files(file_names)
|
||||
file_names.each do |file_name|
|
||||
cleanup(file_name)
|
||||
end
|
||||
end
|
||||
|
||||
def cleanup(file_name, **opts)
|
||||
begin
|
||||
opts = { verbose: Rake.application.options.trace }.merge(opts)
|
||||
rm_r file_name, **opts
|
||||
rescue StandardError => ex
|
||||
puts "Failed to remove #{file_name}: #{ex}" unless file_already_gone?(file_name)
|
||||
end
|
||||
end
|
||||
|
||||
def file_already_gone?(file_name)
|
||||
return false if File.exist?(file_name)
|
||||
|
||||
path = file_name
|
||||
prev = nil
|
||||
|
||||
while path = File.dirname(path)
|
||||
return false if cant_be_deleted?(path)
|
||||
break if [prev, "."].include?(path)
|
||||
prev = path
|
||||
end
|
||||
true
|
||||
end
|
||||
private_class_method :file_already_gone?
|
||||
|
||||
def cant_be_deleted?(path_name)
|
||||
File.exist?(path_name) &&
|
||||
(!File.readable?(path_name) || !File.executable?(path_name))
|
||||
end
|
||||
private_class_method :cant_be_deleted?
|
||||
end
|
||||
end
|
||||
|
||||
CLEAN = ::Rake::FileList["**/*~", "**/*.bak", "**/core"]
|
||||
CLEAN.clear_exclude.exclude { |fn|
|
||||
fn.pathmap("%f").downcase == "core" && File.directory?(fn)
|
||||
}
|
||||
|
||||
desc "Remove any temporary products."
|
||||
task :clean do
|
||||
Rake::Cleaner.cleanup_files(CLEAN)
|
||||
end
|
||||
|
||||
CLOBBER = ::Rake::FileList.new
|
||||
|
||||
desc "Remove any generated files."
|
||||
task clobber: [:clean] do
|
||||
Rake::Cleaner.cleanup_files(CLOBBER)
|
||||
end
|
||||
@@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
module Rake
|
||||
##
|
||||
# Mixin for creating easily cloned objects.
|
||||
|
||||
module Cloneable # :nodoc:
|
||||
# The hook that is invoked by 'clone' and 'dup' methods.
|
||||
def initialize_copy(source)
|
||||
super
|
||||
source.instance_variables.each do |var|
|
||||
src_value = source.instance_variable_get(var)
|
||||
value = src_value.clone rescue src_value
|
||||
instance_variable_set(var, value)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,107 @@
|
||||
# frozen_string_literal: true
|
||||
module Rake
|
||||
|
||||
# Based on a script at:
|
||||
# http://stackoverflow.com/questions/891537/ruby-detect-number-of-cpus-installed
|
||||
class CpuCounter # :nodoc: all
|
||||
def self.count
|
||||
new.count_with_default
|
||||
end
|
||||
|
||||
def count_with_default(default=4)
|
||||
count || default
|
||||
rescue StandardError
|
||||
default
|
||||
end
|
||||
|
||||
begin
|
||||
require "etc"
|
||||
rescue LoadError
|
||||
else
|
||||
if Etc.respond_to?(:nprocessors)
|
||||
def count
|
||||
return Etc.nprocessors
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
unless Rake::CpuCounter.method_defined?(:count)
|
||||
Rake::CpuCounter.class_eval <<-'end;', __FILE__, __LINE__+1
|
||||
require 'rbconfig'
|
||||
|
||||
def count
|
||||
if RUBY_PLATFORM == 'java'
|
||||
count_via_java_runtime
|
||||
else
|
||||
case RbConfig::CONFIG['host_os']
|
||||
when /linux/
|
||||
count_via_cpuinfo
|
||||
when /darwin|bsd/
|
||||
count_via_sysctl
|
||||
when /mswin|mingw/
|
||||
count_via_win32
|
||||
else
|
||||
# Try everything
|
||||
count_via_win32 ||
|
||||
count_via_sysctl ||
|
||||
count_via_cpuinfo
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def count_via_java_runtime
|
||||
Java::Java.lang.Runtime.getRuntime.availableProcessors
|
||||
rescue StandardError
|
||||
nil
|
||||
end
|
||||
|
||||
def count_via_win32
|
||||
require 'win32ole'
|
||||
wmi = WIN32OLE.connect("winmgmts://")
|
||||
cpu = wmi.ExecQuery("select NumberOfCores from Win32_Processor") # TODO count hyper-threaded in this
|
||||
cpu.to_enum.first.NumberOfCores
|
||||
rescue StandardError, LoadError
|
||||
nil
|
||||
end
|
||||
|
||||
def count_via_cpuinfo
|
||||
open('/proc/cpuinfo') { |f| f.readlines }.grep(/processor/).size
|
||||
rescue StandardError
|
||||
nil
|
||||
end
|
||||
|
||||
def count_via_sysctl
|
||||
run 'sysctl', '-n', 'hw.ncpu'
|
||||
end
|
||||
|
||||
def run(command, *args)
|
||||
cmd = resolve_command(command)
|
||||
if cmd
|
||||
IO.popen [cmd, *args] do |io|
|
||||
io.read.to_i
|
||||
end
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def resolve_command(command)
|
||||
look_for_command("/usr/sbin", command) ||
|
||||
look_for_command("/sbin", command) ||
|
||||
in_path_command(command)
|
||||
end
|
||||
|
||||
def look_for_command(dir, command)
|
||||
path = File.join(dir, command)
|
||||
File.exist?(path) ? path : nil
|
||||
end
|
||||
|
||||
def in_path_command(command)
|
||||
IO.popen ['which', command] do |io|
|
||||
io.eof? ? nil : command
|
||||
end
|
||||
end
|
||||
end;
|
||||
end
|
||||
@@ -0,0 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
module Rake
|
||||
|
||||
# Default Rakefile loader used by +import+.
|
||||
class DefaultLoader
|
||||
|
||||
##
|
||||
# Loads a rakefile into the current application from +fn+
|
||||
|
||||
def load(fn)
|
||||
Rake.load_rakefile(File.expand_path(fn))
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,196 @@
|
||||
# frozen_string_literal: true
|
||||
# Rake DSL functions.
|
||||
require "rake/file_utils_ext"
|
||||
|
||||
module Rake
|
||||
|
||||
##
|
||||
# DSL is a module that provides #task, #desc, #namespace, etc. Use this
|
||||
# when you'd like to use rake outside the top level scope.
|
||||
#
|
||||
# For a Rakefile you run from the command line this module is automatically
|
||||
# included.
|
||||
|
||||
module DSL
|
||||
|
||||
#--
|
||||
# Include the FileUtils file manipulation functions in the top
|
||||
# level module, but mark them private so that they don't
|
||||
# unintentionally define methods on other objects.
|
||||
#++
|
||||
|
||||
include FileUtilsExt
|
||||
private(*FileUtils.instance_methods(false))
|
||||
private(*FileUtilsExt.instance_methods(false))
|
||||
|
||||
private
|
||||
|
||||
# :call-seq:
|
||||
# task(task_name)
|
||||
# task(task_name: dependencies)
|
||||
# task(task_name, arguments => dependencies)
|
||||
#
|
||||
# Declare a basic task. The +task_name+ is always the first argument. If
|
||||
# the task name contains a ":" it is defined in that namespace.
|
||||
#
|
||||
# The +dependencies+ may be a single task name or an Array of task names.
|
||||
# The +argument+ (a single name) or +arguments+ (an Array of names) define
|
||||
# the arguments provided to the task.
|
||||
#
|
||||
# The task, argument and dependency names may be either symbols or
|
||||
# strings.
|
||||
#
|
||||
# A task with a single dependency:
|
||||
#
|
||||
# task clobber: %w[clean] do
|
||||
# rm_rf "html"
|
||||
# end
|
||||
#
|
||||
# A task with an argument and a dependency:
|
||||
#
|
||||
# task :package, [:version] => :test do |t, args|
|
||||
# # ...
|
||||
# end
|
||||
#
|
||||
# To invoke this task from the command line:
|
||||
#
|
||||
# $ rake package[1.2.3]
|
||||
#
|
||||
def task(*args, &block) # :doc:
|
||||
Rake::Task.define_task(*args, &block)
|
||||
end
|
||||
|
||||
# Declare a file task.
|
||||
#
|
||||
# Example:
|
||||
# file "config.cfg" => ["config.template"] do
|
||||
# open("config.cfg", "w") do |outfile|
|
||||
# open("config.template") do |infile|
|
||||
# while line = infile.gets
|
||||
# outfile.puts line
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
#
|
||||
def file(*args, &block) # :doc:
|
||||
Rake::FileTask.define_task(*args, &block)
|
||||
end
|
||||
|
||||
# Declare a file creation task.
|
||||
# (Mainly used for the directory command).
|
||||
def file_create(*args, &block)
|
||||
Rake::FileCreationTask.define_task(*args, &block)
|
||||
end
|
||||
|
||||
# Declare a set of files tasks to create the given directories on
|
||||
# demand.
|
||||
#
|
||||
# Example:
|
||||
# directory "testdata/doc"
|
||||
#
|
||||
def directory(*args, &block) # :doc:
|
||||
args = args.flat_map { |arg| arg.is_a?(FileList) ? arg.to_a.flatten : arg }
|
||||
result = file_create(*args, &block)
|
||||
dir, _ = *Rake.application.resolve_args(args)
|
||||
dir = Rake.from_pathname(dir)
|
||||
Rake.each_dir_parent(dir) do |d|
|
||||
file_create d do |t|
|
||||
mkdir_p t.name unless File.exist?(t.name)
|
||||
end
|
||||
end
|
||||
result
|
||||
end
|
||||
|
||||
# Declare a task that performs its prerequisites in
|
||||
# parallel. Multitasks does *not* guarantee that its prerequisites
|
||||
# will execute in any given order (which is obvious when you think
|
||||
# about it)
|
||||
#
|
||||
# Example:
|
||||
# multitask deploy: %w[deploy_gem deploy_rdoc]
|
||||
#
|
||||
def multitask(*args, &block) # :doc:
|
||||
Rake::MultiTask.define_task(*args, &block)
|
||||
end
|
||||
|
||||
# Create a new rake namespace and use it for evaluating the given
|
||||
# block. Returns a NameSpace object that can be used to lookup
|
||||
# tasks defined in the namespace.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# ns = namespace "nested" do
|
||||
# # the "nested:run" task
|
||||
# task :run
|
||||
# end
|
||||
# task_run = ns[:run] # find :run in the given namespace.
|
||||
#
|
||||
# Tasks can also be defined in a namespace by using a ":" in the task
|
||||
# name:
|
||||
#
|
||||
# task "nested:test" do
|
||||
# # ...
|
||||
# end
|
||||
#
|
||||
def namespace(name=nil, &block) # :doc:
|
||||
name = name.to_s if name.kind_of?(Symbol)
|
||||
name = name.to_str if name.respond_to?(:to_str)
|
||||
unless name.kind_of?(String) || name.nil?
|
||||
raise ArgumentError, "Expected a String or Symbol for a namespace name"
|
||||
end
|
||||
Rake.application.in_namespace(name, &block)
|
||||
end
|
||||
|
||||
# Declare a rule for auto-tasks.
|
||||
#
|
||||
# Example:
|
||||
# rule '.o' => '.c' do |t|
|
||||
# sh 'cc', '-c', '-o', t.name, t.source
|
||||
# end
|
||||
#
|
||||
def rule(*args, &block) # :doc:
|
||||
Rake::Task.create_rule(*args, &block)
|
||||
end
|
||||
|
||||
# Describes the next rake task. Duplicate descriptions are discarded.
|
||||
# Descriptions are shown with <code>rake -T</code> (up to the first
|
||||
# sentence) and <code>rake -D</code> (the entire description).
|
||||
#
|
||||
# Example:
|
||||
# desc "Run the Unit Tests"
|
||||
# task test: [:build] do
|
||||
# # ... run tests
|
||||
# end
|
||||
#
|
||||
def desc(description) # :doc:
|
||||
Rake.application.last_description = description
|
||||
end
|
||||
|
||||
# Import the partial Rakefiles +fn+. Imported files are loaded
|
||||
# _after_ the current file is completely loaded. This allows the
|
||||
# import statement to appear anywhere in the importing file, and yet
|
||||
# allowing the imported files to depend on objects defined in the
|
||||
# importing file.
|
||||
#
|
||||
# A common use of the import statement is to include files
|
||||
# containing dependency declarations.
|
||||
#
|
||||
# See also the --rakelibdir command line option.
|
||||
#
|
||||
# Example:
|
||||
# import ".depend", "my_rules"
|
||||
#
|
||||
def import(*fns) # :doc:
|
||||
fns.each do |fn|
|
||||
Rake.application.add_import(fn)
|
||||
end
|
||||
end
|
||||
end
|
||||
extend FileUtilsExt
|
||||
end
|
||||
|
||||
# Extend the main object with the DSL commands. This allows top-level
|
||||
# calls to task, etc. to work from a Rakefile without polluting the
|
||||
# object inheritance tree.
|
||||
self.extend Rake::DSL
|
||||
@@ -0,0 +1,22 @@
|
||||
# frozen_string_literal: true
|
||||
module Rake
|
||||
|
||||
# EarlyTime is a fake timestamp that occurs _before_ any other time value.
|
||||
class EarlyTime
|
||||
include Comparable
|
||||
include Singleton
|
||||
|
||||
##
|
||||
# The EarlyTime always comes before +other+!
|
||||
|
||||
def <=>(other)
|
||||
-1
|
||||
end
|
||||
|
||||
def to_s # :nodoc:
|
||||
"<EARLY TIME>"
|
||||
end
|
||||
end
|
||||
|
||||
EARLY = EarlyTime.instance
|
||||
end
|
||||
@@ -0,0 +1,26 @@
|
||||
# frozen_string_literal: true
|
||||
class Module
|
||||
# Check for an existing method in the current class before extending. If
|
||||
# the method already exists, then a warning is printed and the extension is
|
||||
# not added. Otherwise the block is yielded and any definitions in the
|
||||
# block will take effect.
|
||||
#
|
||||
# Usage:
|
||||
#
|
||||
# class String
|
||||
# rake_extension("xyz") do
|
||||
# def xyz
|
||||
# ...
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
#
|
||||
def rake_extension(method) # :nodoc:
|
||||
if method_defined?(method)
|
||||
$stderr.puts "WARNING: Possible conflict with Rake extension: " +
|
||||
"#{self}##{method} already exists"
|
||||
else
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,176 @@
|
||||
# frozen_string_literal: true
|
||||
require "rake/ext/core"
|
||||
|
||||
class String
|
||||
|
||||
rake_extension("ext") do
|
||||
# Replace the file extension with +newext+. If there is no extension on
|
||||
# the string, append the new extension to the end. If the new extension
|
||||
# is not given, or is the empty string, remove any existing extension.
|
||||
#
|
||||
# +ext+ is a user added method for the String class.
|
||||
#
|
||||
# This String extension comes from Rake
|
||||
def ext(newext="")
|
||||
return self.dup if [".", ".."].include? self
|
||||
if newext != ""
|
||||
newext = "." + newext unless newext =~ /^\./
|
||||
end
|
||||
self.chomp(File.extname(self)) << newext
|
||||
end
|
||||
end
|
||||
|
||||
rake_extension("pathmap") do
|
||||
# Explode a path into individual components. Used by +pathmap+.
|
||||
#
|
||||
# This String extension comes from Rake
|
||||
def pathmap_explode
|
||||
head, tail = File.split(self)
|
||||
return [self] if head == self
|
||||
return [tail] if head == "." || tail == "/"
|
||||
return [head, tail] if head == "/"
|
||||
return head.pathmap_explode + [tail]
|
||||
end
|
||||
protected :pathmap_explode
|
||||
|
||||
# Extract a partial path from the path. Include +n+ directories from the
|
||||
# front end (left hand side) if +n+ is positive. Include |+n+|
|
||||
# directories from the back end (right hand side) if +n+ is negative.
|
||||
#
|
||||
# This String extension comes from Rake
|
||||
def pathmap_partial(n)
|
||||
dirs = File.dirname(self).pathmap_explode
|
||||
partial_dirs =
|
||||
if n > 0
|
||||
dirs[0...n]
|
||||
elsif n < 0
|
||||
dirs.reverse[0...-n].reverse
|
||||
else
|
||||
"."
|
||||
end
|
||||
File.join(partial_dirs)
|
||||
end
|
||||
protected :pathmap_partial
|
||||
|
||||
# Perform the pathmap replacement operations on the given path. The
|
||||
# patterns take the form 'pat1,rep1;pat2,rep2...'.
|
||||
#
|
||||
# This String extension comes from Rake
|
||||
def pathmap_replace(patterns, &block)
|
||||
result = self
|
||||
patterns.split(";").each do |pair|
|
||||
pattern, replacement = pair.split(",")
|
||||
pattern = Regexp.new(pattern)
|
||||
if replacement == "*" && block_given?
|
||||
result = result.sub(pattern, &block)
|
||||
elsif replacement
|
||||
result = result.sub(pattern, replacement)
|
||||
else
|
||||
result = result.sub(pattern, "")
|
||||
end
|
||||
end
|
||||
result
|
||||
end
|
||||
protected :pathmap_replace
|
||||
|
||||
# Map the path according to the given specification. The specification
|
||||
# controls the details of the mapping. The following special patterns are
|
||||
# recognized:
|
||||
#
|
||||
# <tt>%p</tt> :: The complete path.
|
||||
# <tt>%f</tt> :: The base file name of the path, with its file extension,
|
||||
# but without any directories.
|
||||
# <tt>%n</tt> :: The file name of the path without its file extension.
|
||||
# <tt>%d</tt> :: The directory list of the path.
|
||||
# <tt>%x</tt> :: The file extension of the path. An empty string if there
|
||||
# is no extension.
|
||||
# <tt>%X</tt> :: Everything *but* the file extension.
|
||||
# <tt>%s</tt> :: The alternate file separator if defined, otherwise use #
|
||||
# the standard file separator.
|
||||
# <tt>%%</tt> :: A percent sign.
|
||||
#
|
||||
# The <tt>%d</tt> specifier can also have a numeric prefix (e.g. '%2d').
|
||||
# If the number is positive, only return (up to) +n+ directories in the
|
||||
# path, starting from the left hand side. If +n+ is negative, return (up
|
||||
# to) +n+ directories from the right hand side of the path.
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# 'a/b/c/d/file.txt'.pathmap("%2d") => 'a/b'
|
||||
# 'a/b/c/d/file.txt'.pathmap("%-2d") => 'c/d'
|
||||
#
|
||||
# Also the <tt>%d</tt>, <tt>%p</tt>, <tt>%f</tt>, <tt>%n</tt>,
|
||||
# <tt>%x</tt>, and <tt>%X</tt> operators can take a pattern/replacement
|
||||
# argument to perform simple string substitutions on a particular part of
|
||||
# the path. The pattern and replacement are separated by a comma and are
|
||||
# enclosed by curly braces. The replacement spec comes after the %
|
||||
# character but before the operator letter. (e.g. "%{old,new}d").
|
||||
# Multiple replacement specs should be separated by semi-colons (e.g.
|
||||
# "%{old,new;src,bin}d").
|
||||
#
|
||||
# Regular expressions may be used for the pattern, and back refs may be
|
||||
# used in the replacement text. Curly braces, commas and semi-colons are
|
||||
# excluded from both the pattern and replacement text (let's keep parsing
|
||||
# reasonable).
|
||||
#
|
||||
# For example:
|
||||
#
|
||||
# "src/org/onestepback/proj/A.java".pathmap("%{^src,class}X.class")
|
||||
#
|
||||
# returns:
|
||||
#
|
||||
# "class/org/onestepback/proj/A.class"
|
||||
#
|
||||
# If the replacement text is '*', then a block may be provided to perform
|
||||
# some arbitrary calculation for the replacement.
|
||||
#
|
||||
# For example:
|
||||
#
|
||||
# "/path/to/file.TXT".pathmap("%X%{.*,*}x") { |ext|
|
||||
# ext.downcase
|
||||
# }
|
||||
#
|
||||
# Returns:
|
||||
#
|
||||
# "/path/to/file.txt"
|
||||
#
|
||||
# This String extension comes from Rake
|
||||
def pathmap(spec=nil, &block)
|
||||
return self if spec.nil?
|
||||
result = "".dup
|
||||
spec.scan(/%\{[^}]*\}-?\d*[sdpfnxX%]|%-?\d+d|%.|[^%]+/) do |frag|
|
||||
case frag
|
||||
when "%f"
|
||||
result << File.basename(self)
|
||||
when "%n"
|
||||
result << File.basename(self).ext
|
||||
when "%d"
|
||||
result << File.dirname(self)
|
||||
when "%x"
|
||||
result << File.extname(self)
|
||||
when "%X"
|
||||
result << self.ext
|
||||
when "%p"
|
||||
result << self
|
||||
when "%s"
|
||||
result << (File::ALT_SEPARATOR || File::SEPARATOR)
|
||||
when "%-"
|
||||
# do nothing
|
||||
when "%%"
|
||||
result << "%"
|
||||
when /%(-?\d+)d/
|
||||
result << pathmap_partial($1.to_i)
|
||||
when /^%\{([^}]*)\}(\d*[dpfnxX])/
|
||||
patterns, operator = $1, $2
|
||||
result << pathmap("%" + operator).pathmap_replace(patterns, &block)
|
||||
when /^%/
|
||||
fail ArgumentError, "Unknown pathmap specifier #{frag} in '#{spec}'"
|
||||
else
|
||||
result << frag
|
||||
end
|
||||
end
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,25 @@
|
||||
# frozen_string_literal: true
|
||||
require "rake/file_task"
|
||||
require "rake/early_time"
|
||||
|
||||
module Rake
|
||||
|
||||
# A FileCreationTask is a file task that when used as a dependency will be
|
||||
# needed if and only if the file has not been created. Once created, it is
|
||||
# not re-triggered if any of its dependencies are newer, nor does trigger
|
||||
# any rebuilds of tasks that depend on it whenever it is updated.
|
||||
#
|
||||
class FileCreationTask < FileTask
|
||||
# Is this file task needed? Yes if it doesn't exist.
|
||||
def needed?
|
||||
!File.exist?(name)
|
||||
end
|
||||
|
||||
# Time stamp for file creation task. This time stamp is earlier
|
||||
# than any other time stamp.
|
||||
def timestamp
|
||||
Rake::EARLY
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,435 @@
|
||||
# frozen_string_literal: true
|
||||
require "rake/cloneable"
|
||||
require "rake/file_utils_ext"
|
||||
require "rake/ext/string"
|
||||
|
||||
module Rake
|
||||
|
||||
##
|
||||
# A FileList is essentially an array with a few helper methods defined to
|
||||
# make file manipulation a bit easier.
|
||||
#
|
||||
# FileLists are lazy. When given a list of glob patterns for possible files
|
||||
# to be included in the file list, instead of searching the file structures
|
||||
# to find the files, a FileList holds the pattern for latter use.
|
||||
#
|
||||
# This allows us to define a number of FileList to match any number of
|
||||
# files, but only search out the actual files when then FileList itself is
|
||||
# actually used. The key is that the first time an element of the
|
||||
# FileList/Array is requested, the pending patterns are resolved into a real
|
||||
# list of file names.
|
||||
#
|
||||
class FileList
|
||||
|
||||
include Cloneable
|
||||
|
||||
# == Method Delegation
|
||||
#
|
||||
# The lazy evaluation magic of FileLists happens by implementing all the
|
||||
# array specific methods to call +resolve+ before delegating the heavy
|
||||
# lifting to an embedded array object (@items).
|
||||
#
|
||||
# In addition, there are two kinds of delegation calls. The regular kind
|
||||
# delegates to the @items array and returns the result directly. Well,
|
||||
# almost directly. It checks if the returned value is the @items object
|
||||
# itself, and if so will return the FileList object instead.
|
||||
#
|
||||
# The second kind of delegation call is used in methods that normally
|
||||
# return a new Array object. We want to capture the return value of these
|
||||
# methods and wrap them in a new FileList object. We enumerate these
|
||||
# methods in the +SPECIAL_RETURN+ list below.
|
||||
|
||||
# List of array methods (that are not in +Object+) that need to be
|
||||
# delegated.
|
||||
ARRAY_METHODS = (Array.instance_methods - Object.instance_methods).map(&:to_s)
|
||||
|
||||
# List of additional methods that must be delegated.
|
||||
MUST_DEFINE = %w[inspect <=>]
|
||||
|
||||
# List of methods that should not be delegated here (we define special
|
||||
# versions of them explicitly below).
|
||||
MUST_NOT_DEFINE = %w[to_a to_ary partition * <<]
|
||||
|
||||
# List of delegated methods that return new array values which need
|
||||
# wrapping.
|
||||
SPECIAL_RETURN = %w[
|
||||
map collect sort sort_by select find_all reject grep
|
||||
compact flatten uniq values_at
|
||||
+ - & |
|
||||
]
|
||||
|
||||
DELEGATING_METHODS = (ARRAY_METHODS + MUST_DEFINE - MUST_NOT_DEFINE).map(&:to_s).sort.uniq
|
||||
|
||||
# Now do the delegation.
|
||||
DELEGATING_METHODS.each do |sym|
|
||||
if SPECIAL_RETURN.include?(sym)
|
||||
ln = __LINE__ + 1
|
||||
class_eval %{
|
||||
def #{sym}(*args, &block)
|
||||
resolve
|
||||
result = @items.send(:#{sym}, *args, &block)
|
||||
self.class.new.import(result)
|
||||
end
|
||||
}, __FILE__, ln
|
||||
else
|
||||
ln = __LINE__ + 1
|
||||
class_eval %{
|
||||
def #{sym}(*args, &block)
|
||||
resolve
|
||||
result = @items.send(:#{sym}, *args, &block)
|
||||
result.object_id == @items.object_id ? self : result
|
||||
end
|
||||
}, __FILE__, ln
|
||||
end
|
||||
end
|
||||
|
||||
GLOB_PATTERN = %r{[*?\[\{]}
|
||||
|
||||
# Create a file list from the globbable patterns given. If you wish to
|
||||
# perform multiple includes or excludes at object build time, use the
|
||||
# "yield self" pattern.
|
||||
#
|
||||
# Example:
|
||||
# file_list = FileList.new('lib/**/*.rb', 'test/test*.rb')
|
||||
#
|
||||
# pkg_files = FileList.new('lib/**/*') do |fl|
|
||||
# fl.exclude(/\bCVS\b/)
|
||||
# end
|
||||
#
|
||||
def initialize(*patterns)
|
||||
@pending_add = []
|
||||
@pending = false
|
||||
@exclude_patterns = DEFAULT_IGNORE_PATTERNS.dup
|
||||
@exclude_procs = DEFAULT_IGNORE_PROCS.dup
|
||||
@items = []
|
||||
patterns.each { |pattern| include(pattern) }
|
||||
yield self if block_given?
|
||||
end
|
||||
|
||||
# Add file names defined by glob patterns to the file list. If an array
|
||||
# is given, add each element of the array.
|
||||
#
|
||||
# Example:
|
||||
# file_list.include("*.java", "*.cfg")
|
||||
# file_list.include %w( math.c lib.h *.o )
|
||||
#
|
||||
def include(*filenames)
|
||||
# TODO: check for pending
|
||||
filenames.each do |fn|
|
||||
if fn.respond_to? :to_ary
|
||||
include(*fn.to_ary)
|
||||
else
|
||||
@pending_add << Rake.from_pathname(fn)
|
||||
end
|
||||
end
|
||||
@pending = true
|
||||
self
|
||||
end
|
||||
alias :add :include
|
||||
|
||||
# Register a list of file name patterns that should be excluded from the
|
||||
# list. Patterns may be regular expressions, glob patterns or regular
|
||||
# strings. In addition, a block given to exclude will remove entries that
|
||||
# return true when given to the block.
|
||||
#
|
||||
# Note that glob patterns are expanded against the file system. If a file
|
||||
# is explicitly added to a file list, but does not exist in the file
|
||||
# system, then an glob pattern in the exclude list will not exclude the
|
||||
# file.
|
||||
#
|
||||
# Examples:
|
||||
# FileList['a.c', 'b.c'].exclude("a.c") => ['b.c']
|
||||
# FileList['a.c', 'b.c'].exclude(/^a/) => ['b.c']
|
||||
#
|
||||
# If "a.c" is a file, then ...
|
||||
# FileList['a.c', 'b.c'].exclude("a.*") => ['b.c']
|
||||
#
|
||||
# If "a.c" is not a file, then ...
|
||||
# FileList['a.c', 'b.c'].exclude("a.*") => ['a.c', 'b.c']
|
||||
#
|
||||
def exclude(*patterns, &block)
|
||||
patterns.each do |pat|
|
||||
if pat.respond_to? :to_ary
|
||||
exclude(*pat.to_ary)
|
||||
else
|
||||
@exclude_patterns << Rake.from_pathname(pat)
|
||||
end
|
||||
end
|
||||
@exclude_procs << block if block_given?
|
||||
resolve_exclude unless @pending
|
||||
self
|
||||
end
|
||||
|
||||
# Clear all the exclude patterns so that we exclude nothing.
|
||||
def clear_exclude
|
||||
@exclude_patterns = []
|
||||
@exclude_procs = []
|
||||
self
|
||||
end
|
||||
|
||||
# A FileList is equal through array equality.
|
||||
def ==(array)
|
||||
to_ary == array
|
||||
end
|
||||
|
||||
# Return the internal array object.
|
||||
def to_a
|
||||
resolve
|
||||
@items
|
||||
end
|
||||
|
||||
# Return the internal array object.
|
||||
def to_ary
|
||||
to_a
|
||||
end
|
||||
|
||||
# Lie about our class.
|
||||
def is_a?(klass)
|
||||
klass == Array || super(klass)
|
||||
end
|
||||
alias kind_of? is_a?
|
||||
|
||||
# Redefine * to return either a string or a new file list.
|
||||
def *(other)
|
||||
result = @items * other
|
||||
case result
|
||||
when Array
|
||||
self.class.new.import(result)
|
||||
else
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
def <<(obj)
|
||||
resolve
|
||||
@items << Rake.from_pathname(obj)
|
||||
self
|
||||
end
|
||||
|
||||
# Resolve all the pending adds now.
|
||||
def resolve
|
||||
if @pending
|
||||
@pending = false
|
||||
@pending_add.each do |fn| resolve_add(fn) end
|
||||
@pending_add = []
|
||||
resolve_exclude
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
def resolve_add(fn) # :nodoc:
|
||||
case fn
|
||||
when GLOB_PATTERN
|
||||
add_matching(fn)
|
||||
else
|
||||
self << fn
|
||||
end
|
||||
end
|
||||
private :resolve_add
|
||||
|
||||
def resolve_exclude # :nodoc:
|
||||
reject! { |fn| excluded_from_list?(fn) }
|
||||
self
|
||||
end
|
||||
private :resolve_exclude
|
||||
|
||||
# Return a new FileList with the results of running +sub+ against each
|
||||
# element of the original list.
|
||||
#
|
||||
# Example:
|
||||
# FileList['a.c', 'b.c'].sub(/\.c$/, '.o') => ['a.o', 'b.o']
|
||||
#
|
||||
def sub(pat, rep)
|
||||
inject(self.class.new) { |res, fn| res << fn.sub(pat, rep) }
|
||||
end
|
||||
|
||||
# Return a new FileList with the results of running +gsub+ against each
|
||||
# element of the original list.
|
||||
#
|
||||
# Example:
|
||||
# FileList['lib/test/file', 'x/y'].gsub(/\//, "\\")
|
||||
# => ['lib\\test\\file', 'x\\y']
|
||||
#
|
||||
def gsub(pat, rep)
|
||||
inject(self.class.new) { |res, fn| res << fn.gsub(pat, rep) }
|
||||
end
|
||||
|
||||
# Same as +sub+ except that the original file list is modified.
|
||||
def sub!(pat, rep)
|
||||
each_with_index { |fn, i| self[i] = fn.sub(pat, rep) }
|
||||
self
|
||||
end
|
||||
|
||||
# Same as +gsub+ except that the original file list is modified.
|
||||
def gsub!(pat, rep)
|
||||
each_with_index { |fn, i| self[i] = fn.gsub(pat, rep) }
|
||||
self
|
||||
end
|
||||
|
||||
# Apply the pathmap spec to each of the included file names, returning a
|
||||
# new file list with the modified paths. (See String#pathmap for
|
||||
# details.)
|
||||
def pathmap(spec=nil, &block)
|
||||
collect { |fn| fn.pathmap(spec, &block) }
|
||||
end
|
||||
|
||||
# Return a new FileList with <tt>String#ext</tt> method applied to
|
||||
# each member of the array.
|
||||
#
|
||||
# This method is a shortcut for:
|
||||
#
|
||||
# array.collect { |item| item.ext(newext) }
|
||||
#
|
||||
# +ext+ is a user added method for the Array class.
|
||||
def ext(newext="")
|
||||
collect { |fn| fn.ext(newext) }
|
||||
end
|
||||
|
||||
# Grep each of the files in the filelist using the given pattern. If a
|
||||
# block is given, call the block on each matching line, passing the file
|
||||
# name, line number, and the matching line of text. If no block is given,
|
||||
# a standard emacs style file:linenumber:line message will be printed to
|
||||
# standard out. Returns the number of matched items.
|
||||
def egrep(pattern, *options)
|
||||
matched = 0
|
||||
each do |fn|
|
||||
begin
|
||||
File.open(fn, "r", *options) do |inf|
|
||||
count = 0
|
||||
inf.each do |line|
|
||||
count += 1
|
||||
if pattern.match(line)
|
||||
matched += 1
|
||||
if block_given?
|
||||
yield fn, count, line
|
||||
else
|
||||
puts "#{fn}:#{count}:#{line}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue StandardError => ex
|
||||
$stderr.puts "Error while processing '#{fn}': #{ex}"
|
||||
end
|
||||
end
|
||||
matched
|
||||
end
|
||||
|
||||
# Return a new file list that only contains file names from the current
|
||||
# file list that exist on the file system.
|
||||
def existing
|
||||
select { |fn| File.exist?(fn) }.uniq
|
||||
end
|
||||
|
||||
# Modify the current file list so that it contains only file name that
|
||||
# exist on the file system.
|
||||
def existing!
|
||||
resolve
|
||||
@items = @items.select { |fn| File.exist?(fn) }.uniq
|
||||
self
|
||||
end
|
||||
|
||||
# FileList version of partition. Needed because the nested arrays should
|
||||
# be FileLists in this version.
|
||||
def partition(&block) # :nodoc:
|
||||
resolve
|
||||
result = @items.partition(&block)
|
||||
[
|
||||
self.class.new.import(result[0]),
|
||||
self.class.new.import(result[1]),
|
||||
]
|
||||
end
|
||||
|
||||
# Convert a FileList to a string by joining all elements with a space.
|
||||
def to_s
|
||||
resolve
|
||||
self.join(" ")
|
||||
end
|
||||
|
||||
# Add matching glob patterns.
|
||||
def add_matching(pattern)
|
||||
self.class.glob(pattern).each do |fn|
|
||||
self << fn unless excluded_from_list?(fn)
|
||||
end
|
||||
end
|
||||
private :add_matching
|
||||
|
||||
# Should the given file name be excluded from the list?
|
||||
#
|
||||
# NOTE: This method was formerly named "exclude?", but Rails
|
||||
# introduced an exclude? method as an array method and setup a
|
||||
# conflict with file list. We renamed the method to avoid
|
||||
# confusion. If you were using "FileList#exclude?" in your user
|
||||
# code, you will need to update.
|
||||
def excluded_from_list?(fn)
|
||||
return true if @exclude_patterns.any? do |pat|
|
||||
case pat
|
||||
when Regexp
|
||||
fn =~ pat
|
||||
when GLOB_PATTERN
|
||||
flags = File::FNM_PATHNAME
|
||||
# Ruby <= 1.9.3 does not support File::FNM_EXTGLOB
|
||||
flags |= File::FNM_EXTGLOB if defined? File::FNM_EXTGLOB
|
||||
File.fnmatch?(pat, fn, flags)
|
||||
else
|
||||
fn == pat
|
||||
end
|
||||
end
|
||||
@exclude_procs.any? { |p| p.call(fn) }
|
||||
end
|
||||
|
||||
DEFAULT_IGNORE_PATTERNS = [
|
||||
/(^|[\/\\])CVS([\/\\]|$)/,
|
||||
/(^|[\/\\])\.svn([\/\\]|$)/,
|
||||
/\.bak$/,
|
||||
/~$/
|
||||
]
|
||||
DEFAULT_IGNORE_PROCS = [
|
||||
proc { |fn| fn =~ /(^|[\/\\])core$/ && !File.directory?(fn) }
|
||||
]
|
||||
|
||||
def import(array) # :nodoc:
|
||||
@items = array
|
||||
self
|
||||
end
|
||||
|
||||
class << self
|
||||
# Create a new file list including the files listed. Similar to:
|
||||
#
|
||||
# FileList.new(*args)
|
||||
def [](*args)
|
||||
new(*args)
|
||||
end
|
||||
|
||||
# Get a sorted list of files matching the pattern. This method
|
||||
# should be preferred to Dir[pattern] and Dir.glob(pattern) because
|
||||
# the files returned are guaranteed to be sorted.
|
||||
def glob(pattern, *args)
|
||||
Dir.glob(pattern, *args).sort
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Rake
|
||||
class << self
|
||||
|
||||
# Yield each file or directory component.
|
||||
def each_dir_parent(dir) # :nodoc:
|
||||
old_length = nil
|
||||
while dir != "." && dir.length != old_length
|
||||
yield(dir)
|
||||
old_length = dir.length
|
||||
dir = File.dirname(dir)
|
||||
end
|
||||
end
|
||||
|
||||
# Convert Pathname and Pathname-like objects to strings;
|
||||
# leave everything else alone
|
||||
def from_pathname(path) # :nodoc:
|
||||
path = path.to_path if path.respond_to?(:to_path)
|
||||
path = path.to_str if path.respond_to?(:to_str)
|
||||
path
|
||||
end
|
||||
end
|
||||
end # module Rake
|
||||
@@ -0,0 +1,58 @@
|
||||
# frozen_string_literal: true
|
||||
require "rake/task"
|
||||
require "rake/early_time"
|
||||
|
||||
module Rake
|
||||
|
||||
# A FileTask is a task that includes time based dependencies. If any of a
|
||||
# FileTask's prerequisites have a timestamp that is later than the file
|
||||
# represented by this task, then the file must be rebuilt (using the
|
||||
# supplied actions).
|
||||
#
|
||||
class FileTask < Task
|
||||
|
||||
# Is this file task needed? Yes if it doesn't exist, or if its time stamp
|
||||
# is out of date.
|
||||
def needed?
|
||||
begin
|
||||
out_of_date?(File.mtime(name)) || @application.options.build_all
|
||||
rescue Errno::ENOENT
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
# Time stamp for file task.
|
||||
def timestamp
|
||||
begin
|
||||
File.mtime(name)
|
||||
rescue Errno::ENOENT
|
||||
Rake::LATE
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Are there any prerequisites with a later time than the given time stamp?
|
||||
def out_of_date?(stamp)
|
||||
all_prerequisite_tasks.any? { |prereq|
|
||||
prereq_task = application[prereq, @scope]
|
||||
if prereq_task.instance_of?(Rake::FileTask)
|
||||
prereq_task.timestamp > stamp || @application.options.build_all
|
||||
else
|
||||
prereq_task.timestamp > stamp
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# Task class methods.
|
||||
#
|
||||
class << self
|
||||
# Apply the scope to the task name according to the rules for this kind
|
||||
# of task. File based tasks ignore the scope when creating the name.
|
||||
def scope_name(scope, task_name)
|
||||
Rake.from_pathname(task_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,132 @@
|
||||
# frozen_string_literal: true
|
||||
require "rbconfig"
|
||||
require "fileutils"
|
||||
|
||||
#--
|
||||
# This a FileUtils extension that defines several additional commands to be
|
||||
# added to the FileUtils utility functions.
|
||||
module FileUtils
|
||||
# Path to the currently running Ruby program
|
||||
RUBY = ENV["RUBY"] || File.join(
|
||||
RbConfig::CONFIG["bindir"],
|
||||
RbConfig::CONFIG["ruby_install_name"] + RbConfig::CONFIG["EXEEXT"]).
|
||||
sub(/.*\s.*/m, '"\&"')
|
||||
|
||||
# Run the system command +cmd+. If multiple arguments are given the command
|
||||
# is run directly (without the shell, same semantics as Kernel::exec and
|
||||
# Kernel::system).
|
||||
#
|
||||
# It is recommended you use the multiple argument form over interpolating
|
||||
# user input for both usability and security reasons. With the multiple
|
||||
# argument form you can easily process files with spaces or other shell
|
||||
# reserved characters in them. With the multiple argument form your rake
|
||||
# tasks are not vulnerable to users providing an argument like
|
||||
# <code>; rm # -rf /</code>.
|
||||
#
|
||||
# If a block is given, upon command completion the block is called with an
|
||||
# OK flag (true on a zero exit status) and a Process::Status object.
|
||||
# Without a block a RuntimeError is raised when the command exits non-zero.
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# sh 'ls -ltr'
|
||||
#
|
||||
# sh 'ls', 'file with spaces'
|
||||
#
|
||||
# # check exit status after command runs
|
||||
# sh %{grep pattern file} do |ok, res|
|
||||
# if !ok
|
||||
# puts "pattern not found (status = #{res.exitstatus})"
|
||||
# end
|
||||
# end
|
||||
#
|
||||
def sh(*cmd, &block)
|
||||
options = (Hash === cmd.last) ? cmd.pop : {}
|
||||
shell_runner = block_given? ? block : create_shell_runner(cmd)
|
||||
|
||||
set_verbose_option(options)
|
||||
verbose = options.delete :verbose
|
||||
noop = options.delete(:noop) || Rake::FileUtilsExt.nowrite_flag
|
||||
|
||||
Rake.rake_output_message sh_show_command cmd if verbose
|
||||
|
||||
unless noop
|
||||
res = (Hash === cmd.last) ? system(*cmd) : system(*cmd, options)
|
||||
status = $?
|
||||
status = Rake::PseudoStatus.new(1) if !res && status.nil?
|
||||
shell_runner.call(res, status)
|
||||
end
|
||||
end
|
||||
|
||||
def create_shell_runner(cmd) # :nodoc:
|
||||
show_command = sh_show_command cmd
|
||||
lambda do |ok, status|
|
||||
ok or
|
||||
fail "Command failed with status (#{status.exitstatus}): " +
|
||||
"[#{show_command}]"
|
||||
end
|
||||
end
|
||||
private :create_shell_runner
|
||||
|
||||
def sh_show_command(cmd) # :nodoc:
|
||||
cmd = cmd.dup
|
||||
|
||||
if Hash === cmd.first
|
||||
env = cmd.first
|
||||
env = env.map { |name, value| "#{name}=#{value}" }.join " "
|
||||
cmd[0] = env
|
||||
end
|
||||
|
||||
cmd.join " "
|
||||
end
|
||||
private :sh_show_command
|
||||
|
||||
def set_verbose_option(options) # :nodoc:
|
||||
unless options.key? :verbose
|
||||
options[:verbose] =
|
||||
(Rake::FileUtilsExt.verbose_flag == Rake::FileUtilsExt::DEFAULT) ||
|
||||
Rake::FileUtilsExt.verbose_flag
|
||||
end
|
||||
end
|
||||
private :set_verbose_option
|
||||
|
||||
# Run a Ruby interpreter with the given arguments.
|
||||
#
|
||||
# Example:
|
||||
# ruby %{-pe '$_.upcase!' <README}
|
||||
#
|
||||
def ruby(*args, **options, &block)
|
||||
if args.length > 1
|
||||
sh(RUBY, *args, **options, &block)
|
||||
else
|
||||
sh("#{RUBY} #{args.first}", **options, &block)
|
||||
end
|
||||
end
|
||||
|
||||
LN_SUPPORTED = [true]
|
||||
|
||||
# Attempt to do a normal file link, but fall back to a copy if the link
|
||||
# fails.
|
||||
def safe_ln(*args, **options)
|
||||
if LN_SUPPORTED[0]
|
||||
begin
|
||||
return options.empty? ? ln(*args) : ln(*args, **options)
|
||||
rescue StandardError, NotImplementedError
|
||||
LN_SUPPORTED[0] = false
|
||||
end
|
||||
end
|
||||
options.empty? ? cp(*args) : cp(*args, **options)
|
||||
end
|
||||
|
||||
# Split a file path into individual directory names.
|
||||
#
|
||||
# Example:
|
||||
# split_all("a/b/c") => ['a', 'b', 'c']
|
||||
#
|
||||
def split_all(path)
|
||||
head, tail = File.split(path)
|
||||
return [tail] if head == "." || tail == "/"
|
||||
return [head, tail] if head == "/"
|
||||
return split_all(head) + [tail]
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,134 @@
|
||||
# frozen_string_literal: true
|
||||
require "rake/file_utils"
|
||||
|
||||
module Rake
|
||||
#
|
||||
# FileUtilsExt provides a custom version of the FileUtils methods
|
||||
# that respond to the <tt>verbose</tt> and <tt>nowrite</tt>
|
||||
# commands.
|
||||
#
|
||||
module FileUtilsExt
|
||||
include FileUtils
|
||||
|
||||
class << self
|
||||
attr_accessor :verbose_flag, :nowrite_flag
|
||||
end
|
||||
|
||||
DEFAULT = Object.new
|
||||
|
||||
FileUtilsExt.verbose_flag = DEFAULT
|
||||
FileUtilsExt.nowrite_flag = false
|
||||
|
||||
FileUtils.commands.each do |name|
|
||||
opts = FileUtils.options_of name
|
||||
default_options = []
|
||||
if opts.include?("verbose")
|
||||
default_options << "verbose: FileUtilsExt.verbose_flag"
|
||||
end
|
||||
if opts.include?("noop")
|
||||
default_options << "noop: FileUtilsExt.nowrite_flag"
|
||||
end
|
||||
|
||||
next if default_options.empty?
|
||||
module_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
||||
def #{name}(*args, **options, &block)
|
||||
super(*args,
|
||||
#{default_options.join(', ')},
|
||||
**options, &block)
|
||||
end
|
||||
EOS
|
||||
end
|
||||
|
||||
# Get/set the verbose flag controlling output from the FileUtils
|
||||
# utilities. If verbose is true, then the utility method is
|
||||
# echoed to standard output.
|
||||
#
|
||||
# Examples:
|
||||
# verbose # return the current value of the
|
||||
# # verbose flag
|
||||
# verbose(v) # set the verbose flag to _v_.
|
||||
# verbose(v) { code } # Execute code with the verbose flag set
|
||||
# # temporarily to _v_. Return to the
|
||||
# # original value when code is done.
|
||||
def verbose(value=nil)
|
||||
oldvalue = FileUtilsExt.verbose_flag
|
||||
FileUtilsExt.verbose_flag = value unless value.nil?
|
||||
if block_given?
|
||||
begin
|
||||
yield
|
||||
ensure
|
||||
FileUtilsExt.verbose_flag = oldvalue
|
||||
end
|
||||
end
|
||||
FileUtilsExt.verbose_flag
|
||||
end
|
||||
|
||||
# Get/set the nowrite flag controlling output from the FileUtils
|
||||
# utilities. If verbose is true, then the utility method is
|
||||
# echoed to standard output.
|
||||
#
|
||||
# Examples:
|
||||
# nowrite # return the current value of the
|
||||
# # nowrite flag
|
||||
# nowrite(v) # set the nowrite flag to _v_.
|
||||
# nowrite(v) { code } # Execute code with the nowrite flag set
|
||||
# # temporarily to _v_. Return to the
|
||||
# # original value when code is done.
|
||||
def nowrite(value=nil)
|
||||
oldvalue = FileUtilsExt.nowrite_flag
|
||||
FileUtilsExt.nowrite_flag = value unless value.nil?
|
||||
if block_given?
|
||||
begin
|
||||
yield
|
||||
ensure
|
||||
FileUtilsExt.nowrite_flag = oldvalue
|
||||
end
|
||||
end
|
||||
oldvalue
|
||||
end
|
||||
|
||||
# Use this function to prevent potentially destructive ruby code
|
||||
# from running when the :nowrite flag is set.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# when_writing("Building Project") do
|
||||
# project.build
|
||||
# end
|
||||
#
|
||||
# The following code will build the project under normal
|
||||
# conditions. If the nowrite(true) flag is set, then the example
|
||||
# will print:
|
||||
#
|
||||
# DRYRUN: Building Project
|
||||
#
|
||||
# instead of actually building the project.
|
||||
#
|
||||
def when_writing(msg=nil)
|
||||
if FileUtilsExt.nowrite_flag
|
||||
$stderr.puts "DRYRUN: #{msg}" if msg
|
||||
else
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
||||
# Send the message to the default rake output (which is $stderr).
|
||||
def rake_output_message(message)
|
||||
$stderr.puts(message)
|
||||
end
|
||||
|
||||
# Check that the options do not contain options not listed in
|
||||
# +optdecl+. An ArgumentError exception is thrown if non-declared
|
||||
# options are found.
|
||||
def rake_check_options(options, *optdecl)
|
||||
h = options.dup
|
||||
optdecl.each do |name|
|
||||
h.delete name
|
||||
end
|
||||
raise ArgumentError, "no such option: #{h.keys.join(' ')}" unless
|
||||
h.empty?
|
||||
end
|
||||
|
||||
extend self
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,57 @@
|
||||
# frozen_string_literal: true
|
||||
module Rake
|
||||
|
||||
# InvocationChain tracks the chain of task invocations to detect
|
||||
# circular dependencies.
|
||||
class InvocationChain < LinkedList
|
||||
|
||||
# Is the invocation already in the chain?
|
||||
def member?(invocation)
|
||||
head == invocation || tail.member?(invocation)
|
||||
end
|
||||
|
||||
# Append an invocation to the chain of invocations. It is an error
|
||||
# if the invocation already listed.
|
||||
def append(invocation)
|
||||
if member?(invocation)
|
||||
fail RuntimeError, "Circular dependency detected: #{to_s} => #{invocation}"
|
||||
end
|
||||
conj(invocation)
|
||||
end
|
||||
|
||||
# Convert to string, ie: TOP => invocation => invocation
|
||||
def to_s
|
||||
"#{prefix}#{head}"
|
||||
end
|
||||
|
||||
# Class level append.
|
||||
def self.append(invocation, chain)
|
||||
chain.append(invocation)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def prefix
|
||||
"#{tail} => "
|
||||
end
|
||||
|
||||
# Null object for an empty chain.
|
||||
class EmptyInvocationChain < LinkedList::EmptyLinkedList
|
||||
@parent = InvocationChain
|
||||
|
||||
def member?(obj)
|
||||
false
|
||||
end
|
||||
|
||||
def append(invocation)
|
||||
conj(invocation)
|
||||
end
|
||||
|
||||
def to_s
|
||||
"TOP"
|
||||
end
|
||||
end
|
||||
|
||||
EMPTY = EmptyInvocationChain.new
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
module Rake
|
||||
module InvocationExceptionMixin
|
||||
# Return the invocation chain (list of Rake tasks) that were in
|
||||
# effect when this exception was detected by rake. May be null if
|
||||
# no tasks were active.
|
||||
def chain
|
||||
@rake_invocation_chain ||= nil
|
||||
end
|
||||
|
||||
# Set the invocation chain in effect when this exception was
|
||||
# detected.
|
||||
def chain=(value)
|
||||
@rake_invocation_chain = value
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,18 @@
|
||||
# frozen_string_literal: true
|
||||
module Rake
|
||||
# LateTime is a fake timestamp that occurs _after_ any other time value.
|
||||
class LateTime
|
||||
include Comparable
|
||||
include Singleton
|
||||
|
||||
def <=>(other)
|
||||
1
|
||||
end
|
||||
|
||||
def to_s
|
||||
"<LATE TIME>"
|
||||
end
|
||||
end
|
||||
|
||||
LATE = LateTime.instance
|
||||
end
|
||||
@@ -0,0 +1,112 @@
|
||||
# frozen_string_literal: true
|
||||
module Rake
|
||||
|
||||
# Polylithic linked list structure used to implement several data
|
||||
# structures in Rake.
|
||||
class LinkedList
|
||||
include Enumerable
|
||||
attr_reader :head, :tail
|
||||
|
||||
# Polymorphically add a new element to the head of a list. The
|
||||
# type of head node will be the same list type as the tail.
|
||||
def conj(item)
|
||||
self.class.cons(item, self)
|
||||
end
|
||||
|
||||
# Is the list empty?
|
||||
# .make guards against a list being empty making any instantiated LinkedList
|
||||
# object not empty by default
|
||||
# You should consider overriding this method if you implement your own .make method
|
||||
def empty?
|
||||
false
|
||||
end
|
||||
|
||||
# Lists are structurally equivalent.
|
||||
def ==(other)
|
||||
current = self
|
||||
while !current.empty? && !other.empty?
|
||||
return false if current.head != other.head
|
||||
current = current.tail
|
||||
other = other.tail
|
||||
end
|
||||
current.empty? && other.empty?
|
||||
end
|
||||
|
||||
# Convert to string: LL(item, item...)
|
||||
def to_s
|
||||
items = map(&:to_s).join(", ")
|
||||
"LL(#{items})"
|
||||
end
|
||||
|
||||
# Same as +to_s+, but with inspected items.
|
||||
def inspect
|
||||
items = map(&:inspect).join(", ")
|
||||
"LL(#{items})"
|
||||
end
|
||||
|
||||
# For each item in the list.
|
||||
def each
|
||||
current = self
|
||||
while !current.empty?
|
||||
yield(current.head)
|
||||
current = current.tail
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
# Make a list out of the given arguments. This method is
|
||||
# polymorphic
|
||||
def self.make(*args)
|
||||
# return an EmptyLinkedList if there are no arguments
|
||||
return empty if !args || args.empty?
|
||||
|
||||
# build a LinkedList by starting at the tail and iterating
|
||||
# through each argument
|
||||
# inject takes an EmptyLinkedList to start
|
||||
args.reverse.inject(empty) do |list, item|
|
||||
list = cons(item, list)
|
||||
list # return the newly created list for each item in the block
|
||||
end
|
||||
end
|
||||
|
||||
# Cons a new head onto the tail list.
|
||||
def self.cons(head, tail)
|
||||
new(head, tail)
|
||||
end
|
||||
|
||||
# The standard empty list class for the given LinkedList class.
|
||||
def self.empty
|
||||
self::EMPTY
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def initialize(head, tail=EMPTY)
|
||||
@head = head
|
||||
@tail = tail
|
||||
end
|
||||
|
||||
# Represent an empty list, using the Null Object Pattern.
|
||||
#
|
||||
# When inheriting from the LinkedList class, you should implement
|
||||
# a type specific Empty class as well. Make sure you set the class
|
||||
# instance variable @parent to the associated list class (this
|
||||
# allows conj, cons and make to work polymorphically).
|
||||
class EmptyLinkedList < LinkedList
|
||||
@parent = LinkedList
|
||||
|
||||
def initialize
|
||||
end
|
||||
|
||||
def empty?
|
||||
true
|
||||
end
|
||||
|
||||
def self.cons(head, tail)
|
||||
@parent.cons(head, tail)
|
||||
end
|
||||
end
|
||||
|
||||
EMPTY = EmptyLinkedList.new
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,54 @@
|
||||
# frozen_string_literal: true
|
||||
module Rake
|
||||
|
||||
# Makefile loader to be used with the import file loader. Use this to
|
||||
# import dependencies from make dependency tools:
|
||||
#
|
||||
# require 'rake/loaders/makefile'
|
||||
#
|
||||
# file ".depends.mf" => [SRC_LIST] do |t|
|
||||
# sh "makedepend -f- -- #{CFLAGS} -- #{t.prerequisites} > #{t.name}"
|
||||
# end
|
||||
#
|
||||
# import ".depends.mf"
|
||||
#
|
||||
# See {Importing Dependencies}[link:doc/rakefile_rdoc.html#label-Importing+Dependencies]
|
||||
# for further details.
|
||||
|
||||
class MakefileLoader
|
||||
include Rake::DSL
|
||||
|
||||
SPACE_MARK = "\0" # :nodoc:
|
||||
|
||||
# Load the makefile dependencies in +fn+.
|
||||
def load(fn) # :nodoc:
|
||||
lines = File.read fn
|
||||
lines.gsub!(/\\ /, SPACE_MARK)
|
||||
lines.gsub!(/#[^\n]*\n/m, "")
|
||||
lines.gsub!(/\\\n/, " ")
|
||||
lines.each_line do |line|
|
||||
process_line(line)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Process one logical line of makefile data.
|
||||
def process_line(line) # :nodoc:
|
||||
file_tasks, args = line.split(":", 2)
|
||||
return if args.nil?
|
||||
dependents = args.split.map { |d| respace(d) }
|
||||
file_tasks.scan(/\S+/) do |file_task|
|
||||
file_task = respace(file_task)
|
||||
file file_task => dependents
|
||||
end
|
||||
end
|
||||
|
||||
def respace(str) # :nodoc:
|
||||
str.tr SPACE_MARK, " "
|
||||
end
|
||||
end
|
||||
|
||||
# Install the handler
|
||||
Rake.application.add_loader("mf", MakefileLoader.new)
|
||||
end
|
||||
@@ -0,0 +1,14 @@
|
||||
# frozen_string_literal: true
|
||||
module Rake
|
||||
|
||||
# Same as a regular task, but the immediate prerequisites are done in
|
||||
# parallel using Ruby threads.
|
||||
#
|
||||
class MultiTask < Task
|
||||
private
|
||||
|
||||
def invoke_prerequisites(task_args, invocation_chain) # :nodoc:
|
||||
invoke_prerequisites_concurrently(task_args, invocation_chain)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,38 @@
|
||||
# frozen_string_literal: true
|
||||
##
|
||||
# The NameSpace class will lookup task names in the scope defined by a
|
||||
# +namespace+ command.
|
||||
|
||||
class Rake::NameSpace
|
||||
|
||||
##
|
||||
# Create a namespace lookup object using the given task manager
|
||||
# and the list of scopes.
|
||||
|
||||
def initialize(task_manager, scope_list)
|
||||
@task_manager = task_manager
|
||||
@scope = scope_list.dup
|
||||
end
|
||||
|
||||
##
|
||||
# Lookup a task named +name+ in the namespace.
|
||||
|
||||
def [](name)
|
||||
@task_manager.lookup(name, @scope)
|
||||
end
|
||||
|
||||
##
|
||||
# The scope of the namespace (a LinkedList)
|
||||
|
||||
def scope
|
||||
@scope.dup
|
||||
end
|
||||
|
||||
##
|
||||
# Return the list of tasks defined in this and nested namespaces.
|
||||
|
||||
def tasks
|
||||
@task_manager.tasks_in_scope(@scope)
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,222 @@
|
||||
# frozen_string_literal: true
|
||||
# Define a package task library to aid in the definition of
|
||||
# redistributable package files.
|
||||
|
||||
require "rake"
|
||||
require "rake/tasklib"
|
||||
|
||||
module Rake
|
||||
|
||||
# Create a packaging task that will package the project into
|
||||
# distributable files (e.g zip archive or tar files).
|
||||
#
|
||||
# The PackageTask will create the following targets:
|
||||
#
|
||||
# +:package+ ::
|
||||
# Create all the requested package files.
|
||||
#
|
||||
# +:clobber_package+ ::
|
||||
# Delete all the package files. This target is automatically
|
||||
# added to the main clobber target.
|
||||
#
|
||||
# +:repackage+ ::
|
||||
# Rebuild the package files from scratch, even if they are not out
|
||||
# of date.
|
||||
#
|
||||
# <tt>"<em>package_dir</em>/<em>name</em>-<em>version</em>.tgz"</tt> ::
|
||||
# Create a gzipped tar package (if <em>need_tar</em> is true).
|
||||
#
|
||||
# <tt>"<em>package_dir</em>/<em>name</em>-<em>version</em>.tar.gz"</tt> ::
|
||||
# Create a gzipped tar package (if <em>need_tar_gz</em> is true).
|
||||
#
|
||||
# <tt>"<em>package_dir</em>/<em>name</em>-<em>version</em>.tar.bz2"</tt> ::
|
||||
# Create a bzip2'd tar package (if <em>need_tar_bz2</em> is true).
|
||||
#
|
||||
# <tt>"<em>package_dir</em>/<em>name</em>-<em>version</em>.zip"</tt> ::
|
||||
# Create a zip package archive (if <em>need_zip</em> is true).
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# Rake::PackageTask.new("rake", "1.2.3") do |p|
|
||||
# p.need_tar = true
|
||||
# p.package_files.include("lib/**/*.rb")
|
||||
# end
|
||||
#
|
||||
class PackageTask < TaskLib
|
||||
# Name of the package (from the GEM Spec).
|
||||
attr_accessor :name
|
||||
|
||||
# Version of the package (e.g. '1.3.2').
|
||||
attr_accessor :version
|
||||
|
||||
# Directory used to store the package files (default is 'pkg').
|
||||
attr_accessor :package_dir
|
||||
|
||||
# True if a gzipped tar file (tgz) should be produced (default is
|
||||
# false).
|
||||
attr_accessor :need_tar
|
||||
|
||||
# True if a gzipped tar file (tar.gz) should be produced (default
|
||||
# is false).
|
||||
attr_accessor :need_tar_gz
|
||||
|
||||
# True if a bzip2'd tar file (tar.bz2) should be produced (default
|
||||
# is false).
|
||||
attr_accessor :need_tar_bz2
|
||||
|
||||
# True if a xz'd tar file (tar.xz) should be produced (default is false)
|
||||
attr_accessor :need_tar_xz
|
||||
|
||||
# True if a zip file should be produced (default is false)
|
||||
attr_accessor :need_zip
|
||||
|
||||
# List of files to be included in the package.
|
||||
attr_accessor :package_files
|
||||
|
||||
# Tar command for gzipped or bzip2ed archives. The default is 'tar'.
|
||||
attr_accessor :tar_command
|
||||
|
||||
# Zip command for zipped archives. The default is 'zip'.
|
||||
attr_accessor :zip_command
|
||||
|
||||
# True if parent directory should be omitted (default is false)
|
||||
attr_accessor :without_parent_dir
|
||||
|
||||
# Create a Package Task with the given name and version. Use +:noversion+
|
||||
# as the version to build a package without a version or to provide a
|
||||
# fully-versioned package name.
|
||||
|
||||
def initialize(name=nil, version=nil)
|
||||
init(name, version)
|
||||
yield self if block_given?
|
||||
define unless name.nil?
|
||||
end
|
||||
|
||||
# Initialization that bypasses the "yield self" and "define" step.
|
||||
def init(name, version)
|
||||
@name = name
|
||||
@version = version
|
||||
@package_files = Rake::FileList.new
|
||||
@package_dir = "pkg"
|
||||
@need_tar = false
|
||||
@need_tar_gz = false
|
||||
@need_tar_bz2 = false
|
||||
@need_tar_xz = false
|
||||
@need_zip = false
|
||||
@tar_command = "tar"
|
||||
@zip_command = "zip"
|
||||
@without_parent_dir = false
|
||||
end
|
||||
|
||||
# Create the tasks defined by this task library.
|
||||
def define
|
||||
fail "Version required (or :noversion)" if @version.nil?
|
||||
@version = nil if :noversion == @version
|
||||
|
||||
desc "Build all the packages"
|
||||
task :package
|
||||
|
||||
desc "Force a rebuild of the package files"
|
||||
task repackage: [:clobber_package, :package]
|
||||
|
||||
desc "Remove package products"
|
||||
task :clobber_package do
|
||||
rm_r package_dir rescue nil
|
||||
end
|
||||
|
||||
task clobber: [:clobber_package]
|
||||
|
||||
[
|
||||
[need_tar, tgz_file, "z"],
|
||||
[need_tar_gz, tar_gz_file, "z"],
|
||||
[need_tar_bz2, tar_bz2_file, "j"],
|
||||
[need_tar_xz, tar_xz_file, "J"]
|
||||
].each do |need, file, flag|
|
||||
if need
|
||||
task package: ["#{package_dir}/#{file}"]
|
||||
file "#{package_dir}/#{file}" =>
|
||||
[package_dir_path] + package_files do
|
||||
chdir(working_dir) { sh @tar_command, "#{flag}cvf", file, target_dir }
|
||||
mv "#{package_dir_path}/#{target_dir}", package_dir if without_parent_dir
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if need_zip
|
||||
task package: ["#{package_dir}/#{zip_file}"]
|
||||
file "#{package_dir}/#{zip_file}" =>
|
||||
[package_dir_path] + package_files do
|
||||
chdir(working_dir) { sh @zip_command, "-r", zip_file, target_dir }
|
||||
mv "#{package_dir_path}/#{zip_file}", package_dir if without_parent_dir
|
||||
end
|
||||
end
|
||||
|
||||
directory package_dir_path => @package_files do
|
||||
@package_files.each do |fn|
|
||||
f = File.join(package_dir_path, fn)
|
||||
fdir = File.dirname(f)
|
||||
mkdir_p(fdir) unless File.exist?(fdir)
|
||||
if File.directory?(fn)
|
||||
mkdir_p(f)
|
||||
else
|
||||
rm_f f
|
||||
safe_ln(fn, f)
|
||||
end
|
||||
end
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
# The name of this package
|
||||
|
||||
def package_name
|
||||
@version ? "#{@name}-#{@version}" : @name
|
||||
end
|
||||
|
||||
# The directory this package will be built in
|
||||
|
||||
def package_dir_path
|
||||
"#{package_dir}/#{package_name}"
|
||||
end
|
||||
|
||||
# The package name with .tgz added
|
||||
|
||||
def tgz_file
|
||||
"#{package_name}.tgz"
|
||||
end
|
||||
|
||||
# The package name with .tar.gz added
|
||||
|
||||
def tar_gz_file
|
||||
"#{package_name}.tar.gz"
|
||||
end
|
||||
|
||||
# The package name with .tar.bz2 added
|
||||
|
||||
def tar_bz2_file
|
||||
"#{package_name}.tar.bz2"
|
||||
end
|
||||
|
||||
# The package name with .tar.xz added
|
||||
|
||||
def tar_xz_file
|
||||
"#{package_name}.tar.xz"
|
||||
end
|
||||
|
||||
# The package name with .zip added
|
||||
|
||||
def zip_file
|
||||
"#{package_name}.zip"
|
||||
end
|
||||
|
||||
def working_dir
|
||||
without_parent_dir ? package_dir_path : package_dir
|
||||
end
|
||||
|
||||
# target directory relative to working_dir
|
||||
def target_dir
|
||||
without_parent_dir ? "." : package_name
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,16 @@
|
||||
# frozen_string_literal: true
|
||||
# Defines a :phony task that you can use as a dependency. This allows
|
||||
# file-based tasks to use non-file-based tasks as prerequisites
|
||||
# without forcing them to rebuild.
|
||||
#
|
||||
# See FileTask#out_of_date? and Task#timestamp for more info.
|
||||
|
||||
require "rake"
|
||||
|
||||
task :phony
|
||||
|
||||
Rake::Task[:phony].tap do |task|
|
||||
def task.timestamp # :nodoc:
|
||||
Time.at 0
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,21 @@
|
||||
# frozen_string_literal: true
|
||||
module Rake
|
||||
|
||||
# Include PrivateReader to use +private_reader+.
|
||||
module PrivateReader # :nodoc: all
|
||||
|
||||
def self.included(base)
|
||||
base.extend(ClassMethods)
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
|
||||
# Declare a list of private accessors
|
||||
def private_reader(*names)
|
||||
attr_reader(*names)
|
||||
private(*names)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,100 @@
|
||||
# frozen_string_literal: true
|
||||
module Rake
|
||||
|
||||
# A Promise object represents a promise to do work (a chore) in the
|
||||
# future. The promise is created with a block and a list of
|
||||
# arguments for the block. Calling value will return the value of
|
||||
# the promised chore.
|
||||
#
|
||||
# Used by ThreadPool.
|
||||
#
|
||||
class Promise # :nodoc: all
|
||||
NOT_SET = Object.new.freeze # :nodoc:
|
||||
|
||||
attr_accessor :recorder
|
||||
|
||||
# Create a promise to do the chore specified by the block.
|
||||
def initialize(args, &block)
|
||||
@mutex = Mutex.new
|
||||
@result = NOT_SET
|
||||
@error = NOT_SET
|
||||
@args = args
|
||||
@block = block
|
||||
end
|
||||
|
||||
# Return the value of this promise.
|
||||
#
|
||||
# If the promised chore is not yet complete, then do the work
|
||||
# synchronously. We will wait.
|
||||
def value
|
||||
unless complete?
|
||||
stat :sleeping_on, item_id: object_id
|
||||
@mutex.synchronize do
|
||||
stat :has_lock_on, item_id: object_id
|
||||
chore
|
||||
stat :releasing_lock_on, item_id: object_id
|
||||
end
|
||||
end
|
||||
error? ? raise(@error) : @result
|
||||
end
|
||||
|
||||
# If no one else is working this promise, go ahead and do the chore.
|
||||
def work
|
||||
stat :attempting_lock_on, item_id: object_id
|
||||
if @mutex.try_lock
|
||||
stat :has_lock_on, item_id: object_id
|
||||
chore
|
||||
stat :releasing_lock_on, item_id: object_id
|
||||
@mutex.unlock
|
||||
else
|
||||
stat :bailed_on, item_id: object_id
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Perform the chore promised
|
||||
def chore
|
||||
if complete?
|
||||
stat :found_completed, item_id: object_id
|
||||
return
|
||||
end
|
||||
stat :will_execute, item_id: object_id
|
||||
begin
|
||||
@result = @block.call(*@args)
|
||||
rescue Exception => e
|
||||
@error = e
|
||||
end
|
||||
stat :did_execute, item_id: object_id
|
||||
discard
|
||||
end
|
||||
|
||||
# Do we have a result for the promise
|
||||
def result?
|
||||
!@result.equal?(NOT_SET)
|
||||
end
|
||||
|
||||
# Did the promise throw an error
|
||||
def error?
|
||||
!@error.equal?(NOT_SET)
|
||||
end
|
||||
|
||||
# Are we done with the promise
|
||||
def complete?
|
||||
result? || error?
|
||||
end
|
||||
|
||||
# free up these items for the GC
|
||||
def discard
|
||||
@args = nil
|
||||
@block = nil
|
||||
end
|
||||
|
||||
# Record execution statistics if there is a recorder
|
||||
def stat(*args)
|
||||
@recorder.call(*args) if @recorder
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,30 @@
|
||||
# frozen_string_literal: true
|
||||
module Rake
|
||||
|
||||
##
|
||||
# Exit status class for times the system just gives us a nil.
|
||||
class PseudoStatus # :nodoc: all
|
||||
attr_reader :exitstatus
|
||||
|
||||
def initialize(code=0)
|
||||
@exitstatus = code
|
||||
end
|
||||
|
||||
def to_i
|
||||
@exitstatus << 8
|
||||
end
|
||||
|
||||
def >>(n)
|
||||
to_i >> n
|
||||
end
|
||||
|
||||
def stopped?
|
||||
false
|
||||
end
|
||||
|
||||
def exited?
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,67 @@
|
||||
# frozen_string_literal: true
|
||||
require "rake/application"
|
||||
|
||||
module Rake
|
||||
|
||||
class << self
|
||||
# Current Rake Application
|
||||
def application
|
||||
@application ||= Rake::Application.new
|
||||
end
|
||||
|
||||
# Set the current Rake application object.
|
||||
def application=(app)
|
||||
@application = app
|
||||
end
|
||||
|
||||
def suggested_thread_count # :nodoc:
|
||||
@cpu_count ||= Rake::CpuCounter.count
|
||||
@cpu_count + 4
|
||||
end
|
||||
|
||||
# Return the original directory where the Rake application was started.
|
||||
def original_dir
|
||||
application.original_dir
|
||||
end
|
||||
|
||||
# Load a rakefile.
|
||||
def load_rakefile(path)
|
||||
load(path)
|
||||
end
|
||||
|
||||
# Add files to the rakelib list
|
||||
def add_rakelib(*files)
|
||||
application.options.rakelib ||= []
|
||||
application.options.rakelib.concat(files)
|
||||
end
|
||||
|
||||
# Make +block_application+ the default rake application inside a block so
|
||||
# you can load rakefiles into a different application.
|
||||
#
|
||||
# This is useful when you want to run rake tasks inside a library without
|
||||
# running rake in a sub-shell.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# Dir.chdir 'other/directory'
|
||||
#
|
||||
# other_rake = Rake.with_application do |rake|
|
||||
# rake.load_rakefile
|
||||
# end
|
||||
#
|
||||
# puts other_rake.tasks
|
||||
|
||||
def with_application(block_application = Rake::Application.new)
|
||||
orig_application = Rake.application
|
||||
|
||||
Rake.application = block_application
|
||||
|
||||
yield block_application
|
||||
|
||||
block_application
|
||||
ensure
|
||||
Rake.application = orig_application
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,27 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "rake/file_list"
|
||||
|
||||
# Load the test files from the command line.
|
||||
argv = ARGV.select do |argument|
|
||||
case argument
|
||||
when /^-/ then
|
||||
argument
|
||||
when /\*/ then
|
||||
Rake::FileList[argument].to_a.each do |file|
|
||||
require File.expand_path file
|
||||
end
|
||||
|
||||
false
|
||||
else
|
||||
path = File.expand_path argument
|
||||
|
||||
abort "\nFile does not exist: #{path}\n\n" unless File.exist?(path)
|
||||
|
||||
require path
|
||||
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
ARGV.replace argv
|
||||
@@ -0,0 +1,20 @@
|
||||
# frozen_string_literal: true
|
||||
module Rake
|
||||
|
||||
# Error indicating a recursion overflow error in task selection.
|
||||
class RuleRecursionOverflowError < StandardError
|
||||
def initialize(*args)
|
||||
super
|
||||
@targets = []
|
||||
end
|
||||
|
||||
def add_target(target)
|
||||
@targets << target
|
||||
end
|
||||
|
||||
def message
|
||||
super + ": [" + @targets.reverse.join(" => ") + "]"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,43 @@
|
||||
# frozen_string_literal: true
|
||||
module Rake
|
||||
class Scope < LinkedList # :nodoc: all
|
||||
|
||||
# Path for the scope.
|
||||
def path
|
||||
map(&:to_s).reverse.join(":")
|
||||
end
|
||||
|
||||
# Path for the scope + the named path.
|
||||
def path_with_task_name(task_name)
|
||||
"#{path}:#{task_name}"
|
||||
end
|
||||
|
||||
# Trim +n+ innermost scope levels from the scope. In no case will
|
||||
# this trim beyond the toplevel scope.
|
||||
def trim(n)
|
||||
result = self
|
||||
while n > 0 && !result.empty?
|
||||
result = result.tail
|
||||
n -= 1
|
||||
end
|
||||
result
|
||||
end
|
||||
|
||||
# Scope lists always end with an EmptyScope object. See Null
|
||||
# Object Pattern)
|
||||
class EmptyScope < EmptyLinkedList
|
||||
@parent = Scope
|
||||
|
||||
def path
|
||||
""
|
||||
end
|
||||
|
||||
def path_with_task_name(task_name)
|
||||
task_name
|
||||
end
|
||||
end
|
||||
|
||||
# Singleton null object for an empty scope.
|
||||
EMPTY = EmptyScope.new
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,434 @@
|
||||
# frozen_string_literal: true
|
||||
require "rake/invocation_exception_mixin"
|
||||
|
||||
module Rake
|
||||
|
||||
##
|
||||
# A Task is the basic unit of work in a Rakefile. Tasks have associated
|
||||
# actions (possibly more than one) and a list of prerequisites. When
|
||||
# invoked, a task will first ensure that all of its prerequisites have an
|
||||
# opportunity to run and then it will execute its own actions.
|
||||
#
|
||||
# Tasks are not usually created directly using the new method, but rather
|
||||
# use the +file+ and +task+ convenience methods.
|
||||
#
|
||||
class Task
|
||||
# List of prerequisites for a task.
|
||||
attr_reader :prerequisites
|
||||
alias prereqs prerequisites
|
||||
|
||||
# List of order only prerequisites for a task.
|
||||
attr_reader :order_only_prerequisites
|
||||
|
||||
# List of actions attached to a task.
|
||||
attr_reader :actions
|
||||
|
||||
# Application owning this task.
|
||||
attr_accessor :application
|
||||
|
||||
# Array of nested namespaces names used for task lookup by this task.
|
||||
attr_reader :scope
|
||||
|
||||
# File/Line locations of each of the task definitions for this
|
||||
# task (only valid if the task was defined with the detect
|
||||
# location option set).
|
||||
attr_reader :locations
|
||||
|
||||
# Has this task already been invoked? Already invoked tasks
|
||||
# will be skipped unless you reenable them.
|
||||
attr_reader :already_invoked
|
||||
|
||||
# Return task name
|
||||
def to_s
|
||||
name
|
||||
end
|
||||
|
||||
def inspect # :nodoc:
|
||||
"<#{self.class} #{name} => [#{prerequisites.join(', ')}]>"
|
||||
end
|
||||
|
||||
# List of sources for task.
|
||||
attr_writer :sources
|
||||
def sources
|
||||
if defined?(@sources)
|
||||
@sources
|
||||
else
|
||||
prerequisites
|
||||
end
|
||||
end
|
||||
|
||||
# List of prerequisite tasks
|
||||
def prerequisite_tasks
|
||||
(prerequisites + order_only_prerequisites).map { |pre| lookup_prerequisite(pre) }
|
||||
end
|
||||
|
||||
def lookup_prerequisite(prerequisite_name) # :nodoc:
|
||||
scoped_prerequisite_task = application[prerequisite_name, @scope]
|
||||
if scoped_prerequisite_task == self
|
||||
unscoped_prerequisite_task = application[prerequisite_name]
|
||||
end
|
||||
unscoped_prerequisite_task || scoped_prerequisite_task
|
||||
end
|
||||
private :lookup_prerequisite
|
||||
|
||||
# List of all unique prerequisite tasks including prerequisite tasks'
|
||||
# prerequisites.
|
||||
# Includes self when cyclic dependencies are found.
|
||||
def all_prerequisite_tasks
|
||||
seen = {}
|
||||
collect_prerequisites(seen)
|
||||
seen.values
|
||||
end
|
||||
|
||||
def collect_prerequisites(seen) # :nodoc:
|
||||
prerequisite_tasks.each do |pre|
|
||||
next if seen[pre.name]
|
||||
seen[pre.name] = pre
|
||||
pre.collect_prerequisites(seen)
|
||||
end
|
||||
end
|
||||
protected :collect_prerequisites
|
||||
|
||||
# First source from a rule (nil if no sources)
|
||||
def source
|
||||
sources.first
|
||||
end
|
||||
|
||||
# Create a task named +task_name+ with no actions or prerequisites. Use
|
||||
# +enhance+ to add actions and prerequisites.
|
||||
def initialize(task_name, app)
|
||||
@name = task_name.to_s
|
||||
@prerequisites = []
|
||||
@actions = []
|
||||
@already_invoked = false
|
||||
@comments = []
|
||||
@lock = Monitor.new
|
||||
@application = app
|
||||
@scope = app.current_scope
|
||||
@arg_names = nil
|
||||
@locations = []
|
||||
@invocation_exception = nil
|
||||
@order_only_prerequisites = []
|
||||
end
|
||||
|
||||
# Enhance a task with prerequisites or actions. Returns self.
|
||||
def enhance(deps=nil, &block)
|
||||
@prerequisites |= deps if deps
|
||||
@actions << block if block_given?
|
||||
self
|
||||
end
|
||||
|
||||
# Name of the task, including any namespace qualifiers.
|
||||
def name
|
||||
@name.to_s
|
||||
end
|
||||
|
||||
# Name of task with argument list description.
|
||||
def name_with_args # :nodoc:
|
||||
if arg_description
|
||||
"#{name}#{arg_description}"
|
||||
else
|
||||
name
|
||||
end
|
||||
end
|
||||
|
||||
# Argument description (nil if none).
|
||||
def arg_description # :nodoc:
|
||||
@arg_names ? "[#{arg_names.join(',')}]" : nil
|
||||
end
|
||||
|
||||
# Name of arguments for this task.
|
||||
def arg_names
|
||||
@arg_names || []
|
||||
end
|
||||
|
||||
# Reenable the task, allowing its tasks to be executed if the task
|
||||
# is invoked again.
|
||||
def reenable
|
||||
@already_invoked = false
|
||||
@invocation_exception = nil
|
||||
end
|
||||
|
||||
# Clear the existing prerequisites, actions, comments, and arguments of a rake task.
|
||||
def clear
|
||||
clear_prerequisites
|
||||
clear_actions
|
||||
clear_comments
|
||||
clear_args
|
||||
self
|
||||
end
|
||||
|
||||
# Clear the existing prerequisites of a rake task.
|
||||
def clear_prerequisites
|
||||
prerequisites.clear
|
||||
self
|
||||
end
|
||||
|
||||
# Clear the existing actions on a rake task.
|
||||
def clear_actions
|
||||
actions.clear
|
||||
self
|
||||
end
|
||||
|
||||
# Clear the existing comments on a rake task.
|
||||
def clear_comments
|
||||
@comments = []
|
||||
self
|
||||
end
|
||||
|
||||
# Clear the existing arguments on a rake task.
|
||||
def clear_args
|
||||
@arg_names = nil
|
||||
self
|
||||
end
|
||||
|
||||
# Invoke the task if it is needed. Prerequisites are invoked first.
|
||||
def invoke(*args)
|
||||
task_args = TaskArguments.new(arg_names, args)
|
||||
invoke_with_call_chain(task_args, InvocationChain::EMPTY)
|
||||
end
|
||||
|
||||
# Same as invoke, but explicitly pass a call chain to detect
|
||||
# circular dependencies.
|
||||
#
|
||||
# If multiple tasks depend on this
|
||||
# one in parallel, they will all fail if the first execution of
|
||||
# this task fails.
|
||||
def invoke_with_call_chain(task_args, invocation_chain)
|
||||
new_chain = Rake::InvocationChain.append(self, invocation_chain)
|
||||
@lock.synchronize do
|
||||
begin
|
||||
if application.options.trace
|
||||
application.trace "** Invoke #{name} #{format_trace_flags}"
|
||||
end
|
||||
|
||||
if @already_invoked
|
||||
if @invocation_exception
|
||||
if application.options.trace
|
||||
application.trace "** Previous invocation of #{name} failed #{format_trace_flags}"
|
||||
end
|
||||
raise @invocation_exception
|
||||
else
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
@already_invoked = true
|
||||
|
||||
invoke_prerequisites(task_args, new_chain)
|
||||
execute(task_args) if needed?
|
||||
rescue Exception => ex
|
||||
add_chain_to(ex, new_chain)
|
||||
@invocation_exception = ex
|
||||
raise ex
|
||||
end
|
||||
end
|
||||
end
|
||||
protected :invoke_with_call_chain
|
||||
|
||||
def add_chain_to(exception, new_chain) # :nodoc:
|
||||
exception.extend(InvocationExceptionMixin) unless
|
||||
exception.respond_to?(:chain)
|
||||
exception.chain = new_chain if exception.chain.nil?
|
||||
end
|
||||
private :add_chain_to
|
||||
|
||||
# Invoke all the prerequisites of a task.
|
||||
def invoke_prerequisites(task_args, invocation_chain) # :nodoc:
|
||||
if application.options.always_multitask
|
||||
invoke_prerequisites_concurrently(task_args, invocation_chain)
|
||||
else
|
||||
prerequisite_tasks.each { |p|
|
||||
prereq_args = task_args.new_scope(p.arg_names)
|
||||
p.invoke_with_call_chain(prereq_args, invocation_chain)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
# Invoke all the prerequisites of a task in parallel.
|
||||
def invoke_prerequisites_concurrently(task_args, invocation_chain)# :nodoc:
|
||||
futures = prerequisite_tasks.map do |p|
|
||||
prereq_args = task_args.new_scope(p.arg_names)
|
||||
application.thread_pool.future(p) do |r|
|
||||
r.invoke_with_call_chain(prereq_args, invocation_chain)
|
||||
end
|
||||
end
|
||||
# Iterate in reverse to improve performance related to thread waiting and switching
|
||||
futures.reverse_each(&:value)
|
||||
end
|
||||
|
||||
# Format the trace flags for display.
|
||||
def format_trace_flags
|
||||
flags = []
|
||||
flags << "first_time" unless @already_invoked
|
||||
flags << "not_needed" unless needed?
|
||||
flags.empty? ? "" : "(" + flags.join(", ") + ")"
|
||||
end
|
||||
private :format_trace_flags
|
||||
|
||||
# Execute the actions associated with this task.
|
||||
def execute(args=nil)
|
||||
args ||= EMPTY_TASK_ARGS
|
||||
if application.options.dryrun
|
||||
application.trace "** Execute (dry run) #{name}"
|
||||
return
|
||||
end
|
||||
application.trace "** Execute #{name}" if application.options.trace
|
||||
application.enhance_with_matching_rule(name) if @actions.empty?
|
||||
if opts = Hash.try_convert(args) and !opts.empty?
|
||||
@actions.each { |act| act.call(self, args, **opts) }
|
||||
else
|
||||
@actions.each { |act| act.call(self, args) }
|
||||
end
|
||||
end
|
||||
|
||||
# Is this task needed?
|
||||
def needed?
|
||||
true
|
||||
end
|
||||
|
||||
# Timestamp for this task. Basic tasks return the current time for their
|
||||
# time stamp. Other tasks can be more sophisticated.
|
||||
def timestamp
|
||||
Time.now
|
||||
end
|
||||
|
||||
# Add a description to the task. The description can consist of an option
|
||||
# argument list (enclosed brackets) and an optional comment.
|
||||
def add_description(description)
|
||||
return unless description
|
||||
comment = description.strip
|
||||
add_comment(comment) if comment && !comment.empty?
|
||||
end
|
||||
|
||||
def comment=(comment) # :nodoc:
|
||||
add_comment(comment)
|
||||
end
|
||||
|
||||
def add_comment(comment) # :nodoc:
|
||||
return if comment.nil?
|
||||
@comments << comment unless @comments.include?(comment)
|
||||
end
|
||||
private :add_comment
|
||||
|
||||
# Full collection of comments. Multiple comments are separated by
|
||||
# newlines.
|
||||
def full_comment
|
||||
transform_comments("\n")
|
||||
end
|
||||
|
||||
# First line (or sentence) of all comments. Multiple comments are
|
||||
# separated by a "/".
|
||||
def comment
|
||||
transform_comments(" / ") { |c| first_sentence(c) }
|
||||
end
|
||||
|
||||
# Transform the list of comments as specified by the block and
|
||||
# join with the separator.
|
||||
def transform_comments(separator, &block)
|
||||
if @comments.empty?
|
||||
nil
|
||||
else
|
||||
block ||= lambda { |c| c }
|
||||
@comments.map(&block).join(separator)
|
||||
end
|
||||
end
|
||||
private :transform_comments
|
||||
|
||||
# Get the first sentence in a string. The sentence is terminated
|
||||
# by the first period, exclamation mark, or the end of the line.
|
||||
# Decimal points do not count as periods.
|
||||
def first_sentence(string)
|
||||
string.split(/(?<=\w)(\.|!)[ \t]|(\.$|!)|\n/).first
|
||||
end
|
||||
private :first_sentence
|
||||
|
||||
# Set the names of the arguments for this task. +args+ should be
|
||||
# an array of symbols, one for each argument name.
|
||||
def set_arg_names(args)
|
||||
@arg_names = args.map(&:to_sym)
|
||||
end
|
||||
|
||||
# Return a string describing the internal state of a task. Useful for
|
||||
# debugging.
|
||||
def investigation
|
||||
result = "------------------------------\n".dup
|
||||
result << "Investigating #{name}\n"
|
||||
result << "class: #{self.class}\n"
|
||||
result << "task needed: #{needed?}\n"
|
||||
result << "timestamp: #{timestamp}\n"
|
||||
result << "pre-requisites: \n"
|
||||
prereqs = prerequisite_tasks
|
||||
prereqs.sort! { |a, b| a.timestamp <=> b.timestamp }
|
||||
prereqs.each do |p|
|
||||
result << "--#{p.name} (#{p.timestamp})\n"
|
||||
end
|
||||
latest_prereq = prerequisite_tasks.map(&:timestamp).max
|
||||
result << "latest-prerequisite time: #{latest_prereq}\n"
|
||||
result << "................................\n\n"
|
||||
return result
|
||||
end
|
||||
|
||||
# Format dependencies parameter to pass to task.
|
||||
def self.format_deps(deps)
|
||||
deps = [deps] unless deps.respond_to?(:to_ary)
|
||||
deps.map { |d| Rake.from_pathname(d).to_s }
|
||||
end
|
||||
|
||||
# Add order only dependencies.
|
||||
def |(deps)
|
||||
@order_only_prerequisites |= Task.format_deps(deps) - @prerequisites
|
||||
self
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# Rake Module Methods
|
||||
#
|
||||
class << self
|
||||
|
||||
# Clear the task list. This cause rake to immediately forget all the
|
||||
# tasks that have been assigned. (Normally used in the unit tests.)
|
||||
def clear
|
||||
Rake.application.clear
|
||||
end
|
||||
|
||||
# List of all defined tasks.
|
||||
def tasks
|
||||
Rake.application.tasks
|
||||
end
|
||||
|
||||
# Return a task with the given name. If the task is not currently
|
||||
# known, try to synthesize one from the defined rules. If no rules are
|
||||
# found, but an existing file matches the task name, assume it is a file
|
||||
# task with no dependencies or actions.
|
||||
def [](task_name)
|
||||
Rake.application[task_name]
|
||||
end
|
||||
|
||||
# TRUE if the task name is already defined.
|
||||
def task_defined?(task_name)
|
||||
Rake.application.lookup(task_name) != nil
|
||||
end
|
||||
|
||||
# Define a task given +args+ and an option block. If a rule with the
|
||||
# given name already exists, the prerequisites and actions are added to
|
||||
# the existing task. Returns the defined task.
|
||||
def define_task(*args, &block)
|
||||
Rake.application.define_task(self, *args, &block)
|
||||
end
|
||||
|
||||
# Define a rule for synthesizing tasks.
|
||||
def create_rule(*args, &block)
|
||||
Rake.application.create_rule(*args, &block)
|
||||
end
|
||||
|
||||
# Apply the scope to the task name according to the rules for
|
||||
# this kind of task. Generic tasks will accept the scope as
|
||||
# part of the name.
|
||||
def scope_name(scope, task_name)
|
||||
scope.path_with_task_name(task_name)
|
||||
end
|
||||
|
||||
end # class << Rake::Task
|
||||
end # class Rake::Task
|
||||
end
|
||||
@@ -0,0 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
module Rake
|
||||
|
||||
# Error indicating an ill-formed task declaration.
|
||||
class TaskArgumentError < ArgumentError
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,109 @@
|
||||
# frozen_string_literal: true
|
||||
module Rake
|
||||
|
||||
##
|
||||
# TaskArguments manage the arguments passed to a task.
|
||||
#
|
||||
class TaskArguments
|
||||
include Enumerable
|
||||
|
||||
# Argument names
|
||||
attr_reader :names
|
||||
|
||||
# Create a TaskArgument object with a list of argument +names+ and a set
|
||||
# of associated +values+. +parent+ is the parent argument object.
|
||||
def initialize(names, values, parent=nil)
|
||||
@names = names
|
||||
@parent = parent
|
||||
@hash = {}
|
||||
@values = values
|
||||
names.each_with_index { |name, i|
|
||||
next if values[i].nil? || values[i] == ""
|
||||
@hash[name.to_sym] = values[i]
|
||||
}
|
||||
end
|
||||
|
||||
# Retrieve the complete array of sequential values
|
||||
def to_a
|
||||
@values.dup
|
||||
end
|
||||
|
||||
# Retrieve the list of values not associated with named arguments
|
||||
def extras
|
||||
@values[@names.length..-1] || []
|
||||
end
|
||||
|
||||
# Create a new argument scope using the prerequisite argument
|
||||
# names.
|
||||
def new_scope(names)
|
||||
values = names.map { |n| self[n] }
|
||||
self.class.new(names, values + extras, self)
|
||||
end
|
||||
|
||||
# Find an argument value by name or index.
|
||||
def [](index)
|
||||
lookup(index.to_sym)
|
||||
end
|
||||
|
||||
# Specify a hash of default values for task arguments. Use the
|
||||
# defaults only if there is no specific value for the given
|
||||
# argument.
|
||||
def with_defaults(defaults)
|
||||
@hash = defaults.merge(@hash)
|
||||
end
|
||||
|
||||
# Enumerates the arguments and their values
|
||||
def each(&block)
|
||||
@hash.each(&block)
|
||||
end
|
||||
|
||||
# Extracts the argument values at +keys+
|
||||
def values_at(*keys)
|
||||
keys.map { |k| lookup(k) }
|
||||
end
|
||||
|
||||
# Returns the value of the given argument via method_missing
|
||||
def method_missing(sym, *args)
|
||||
lookup(sym.to_sym)
|
||||
end
|
||||
|
||||
# Returns a Hash of arguments and their values
|
||||
def to_hash
|
||||
@hash.dup
|
||||
end
|
||||
|
||||
def to_s # :nodoc:
|
||||
inspect
|
||||
end
|
||||
|
||||
def inspect # :nodoc:
|
||||
inspection = @hash.map do |k,v|
|
||||
"#{k.to_s}: #{v.to_s}"
|
||||
end.join(", ")
|
||||
|
||||
"#<#{self.class} #{inspection}>"
|
||||
end
|
||||
|
||||
# Returns true if +key+ is one of the arguments
|
||||
def has_key?(key)
|
||||
@hash.has_key?(key)
|
||||
end
|
||||
alias key? has_key?
|
||||
|
||||
def fetch(*args, &block)
|
||||
@hash.fetch(*args, &block)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def lookup(name) # :nodoc:
|
||||
if @hash.has_key?(name)
|
||||
@hash[name]
|
||||
elsif @parent
|
||||
@parent.lookup(name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
EMPTY_TASK_ARGS = TaskArguments.new([], []) # :nodoc:
|
||||
end
|
||||
@@ -0,0 +1,331 @@
|
||||
# frozen_string_literal: true
|
||||
module Rake
|
||||
|
||||
# The TaskManager module is a mixin for managing tasks.
|
||||
module TaskManager
|
||||
# Track the last comment made in the Rakefile.
|
||||
attr_accessor :last_description
|
||||
|
||||
def initialize # :nodoc:
|
||||
super
|
||||
@tasks = Hash.new
|
||||
@rules = Array.new
|
||||
@scope = Scope.make
|
||||
@last_description = nil
|
||||
end
|
||||
|
||||
def create_rule(*args, &block) # :nodoc:
|
||||
pattern, args, deps, order_only = resolve_args(args)
|
||||
pattern = Regexp.new(Regexp.quote(pattern) + "$") if String === pattern
|
||||
@rules << [pattern, args, deps, order_only, block]
|
||||
end
|
||||
|
||||
def define_task(task_class, *args, &block) # :nodoc:
|
||||
task_name, arg_names, deps, order_only = resolve_args(args)
|
||||
|
||||
original_scope = @scope
|
||||
if String === task_name and
|
||||
not task_class.ancestors.include? Rake::FileTask
|
||||
task_name, *definition_scope = *(task_name.split(":").reverse)
|
||||
@scope = Scope.make(*(definition_scope + @scope.to_a))
|
||||
end
|
||||
|
||||
task_name = task_class.scope_name(@scope, task_name)
|
||||
task = intern(task_class, task_name)
|
||||
task.set_arg_names(arg_names) unless arg_names.empty?
|
||||
if Rake::TaskManager.record_task_metadata
|
||||
add_location(task)
|
||||
task.add_description(get_description(task))
|
||||
end
|
||||
task.enhance(Task.format_deps(deps), &block)
|
||||
task | order_only unless order_only.nil?
|
||||
task
|
||||
ensure
|
||||
@scope = original_scope
|
||||
end
|
||||
|
||||
# Lookup a task. Return an existing task if found, otherwise
|
||||
# create a task of the current type.
|
||||
def intern(task_class, task_name)
|
||||
@tasks[task_name.to_s] ||= task_class.new(task_name, self)
|
||||
end
|
||||
|
||||
# Find a matching task for +task_name+.
|
||||
def [](task_name, scopes=nil)
|
||||
task_name = task_name.to_s
|
||||
self.lookup(task_name, scopes) or
|
||||
enhance_with_matching_rule(task_name) or
|
||||
synthesize_file_task(task_name) or
|
||||
fail generate_message_for_undefined_task(task_name)
|
||||
end
|
||||
|
||||
def generate_message_for_undefined_task(task_name)
|
||||
message = "Don't know how to build task '#{task_name}' "\
|
||||
"(See the list of available tasks with `#{Rake.application.name} --tasks`)"
|
||||
message + generate_did_you_mean_suggestions(task_name)
|
||||
end
|
||||
|
||||
def generate_did_you_mean_suggestions(task_name)
|
||||
return "" unless defined?(::DidYouMean::SpellChecker)
|
||||
|
||||
suggestions = ::DidYouMean::SpellChecker.new(dictionary: @tasks.keys).correct(task_name.to_s)
|
||||
if ::DidYouMean.respond_to?(:formatter)# did_you_mean v1.2.0 or later
|
||||
::DidYouMean.formatter.message_for(suggestions)
|
||||
elsif defined?(::DidYouMean::Formatter) # before did_you_mean v1.2.0
|
||||
::DidYouMean::Formatter.new(suggestions).to_s
|
||||
else
|
||||
""
|
||||
end
|
||||
end
|
||||
|
||||
def synthesize_file_task(task_name) # :nodoc:
|
||||
return nil unless File.exist?(task_name)
|
||||
define_task(Rake::FileTask, task_name)
|
||||
end
|
||||
|
||||
# Resolve the arguments for a task/rule. Returns a tuple of
|
||||
# [task_name, arg_name_list, prerequisites, order_only_prerequisites].
|
||||
def resolve_args(args)
|
||||
if args.last.is_a?(Hash)
|
||||
deps = args.pop
|
||||
resolve_args_with_dependencies(args, deps)
|
||||
else
|
||||
resolve_args_without_dependencies(args)
|
||||
end
|
||||
end
|
||||
|
||||
# Resolve task arguments for a task or rule when there are no
|
||||
# dependencies declared.
|
||||
#
|
||||
# The patterns recognized by this argument resolving function are:
|
||||
#
|
||||
# task :t
|
||||
# task :t, [:a]
|
||||
#
|
||||
def resolve_args_without_dependencies(args)
|
||||
task_name = args.shift
|
||||
if args.size == 1 && args.first.respond_to?(:to_ary)
|
||||
arg_names = args.first.to_ary
|
||||
else
|
||||
arg_names = args
|
||||
end
|
||||
[task_name, arg_names, [], nil]
|
||||
end
|
||||
private :resolve_args_without_dependencies
|
||||
|
||||
# Resolve task arguments for a task or rule when there are
|
||||
# dependencies declared.
|
||||
#
|
||||
# The patterns recognized by this argument resolving function are:
|
||||
#
|
||||
# task :t, order_only: [:e]
|
||||
# task :t => [:d]
|
||||
# task :t => [:d], order_only: [:e]
|
||||
# task :t, [a] => [:d]
|
||||
# task :t, [a] => [:d], order_only: [:e]
|
||||
#
|
||||
def resolve_args_with_dependencies(args, hash) # :nodoc:
|
||||
fail "Task Argument Error" if
|
||||
hash.size != 1 &&
|
||||
(hash.size != 2 || !hash.key?(:order_only))
|
||||
order_only = hash.delete(:order_only)
|
||||
key, value = hash.map { |k, v| [k, v] }.first
|
||||
if args.empty?
|
||||
task_name = key
|
||||
arg_names = []
|
||||
deps = value || []
|
||||
else
|
||||
task_name = args.shift
|
||||
arg_names = key || args.shift|| []
|
||||
deps = value || []
|
||||
end
|
||||
deps = [deps] unless deps.respond_to?(:to_ary)
|
||||
[task_name, arg_names, deps, order_only]
|
||||
end
|
||||
private :resolve_args_with_dependencies
|
||||
|
||||
# If a rule can be found that matches the task name, enhance the
|
||||
# task with the prerequisites and actions from the rule. Set the
|
||||
# source attribute of the task appropriately for the rule. Return
|
||||
# the enhanced task or nil of no rule was found.
|
||||
def enhance_with_matching_rule(task_name, level=0)
|
||||
fail Rake::RuleRecursionOverflowError,
|
||||
"Rule Recursion Too Deep" if level >= 16
|
||||
@rules.each do |pattern, args, extensions, order_only, block|
|
||||
if pattern && pattern.match(task_name)
|
||||
task = attempt_rule(task_name, pattern, args, extensions, block, level)
|
||||
task | order_only unless order_only.nil?
|
||||
return task if task
|
||||
end
|
||||
end
|
||||
nil
|
||||
rescue Rake::RuleRecursionOverflowError => ex
|
||||
ex.add_target(task_name)
|
||||
fail ex
|
||||
end
|
||||
|
||||
# List of all defined tasks in this application.
|
||||
def tasks
|
||||
@tasks.values.sort_by { |t| t.name }
|
||||
end
|
||||
|
||||
# List of all the tasks defined in the given scope (and its
|
||||
# sub-scopes).
|
||||
def tasks_in_scope(scope)
|
||||
prefix = scope.path
|
||||
tasks.select { |t|
|
||||
/^#{prefix}:/ =~ t.name
|
||||
}
|
||||
end
|
||||
|
||||
# Clear all tasks in this application.
|
||||
def clear
|
||||
@tasks.clear
|
||||
@rules.clear
|
||||
end
|
||||
|
||||
# Lookup a task, using scope and the scope hints in the task name.
|
||||
# This method performs straight lookups without trying to
|
||||
# synthesize file tasks or rules. Special scope names (e.g. '^')
|
||||
# are recognized. If no scope argument is supplied, use the
|
||||
# current scope. Return nil if the task cannot be found.
|
||||
def lookup(task_name, initial_scope=nil)
|
||||
initial_scope ||= @scope
|
||||
task_name = task_name.to_s
|
||||
if task_name =~ /^rake:/
|
||||
scopes = Scope.make
|
||||
task_name = task_name.sub(/^rake:/, "")
|
||||
elsif task_name =~ /^(\^+)/
|
||||
scopes = initial_scope.trim($1.size)
|
||||
task_name = task_name.sub(/^(\^+)/, "")
|
||||
else
|
||||
scopes = initial_scope
|
||||
end
|
||||
lookup_in_scope(task_name, scopes)
|
||||
end
|
||||
|
||||
# Lookup the task name
|
||||
def lookup_in_scope(name, scope)
|
||||
loop do
|
||||
tn = scope.path_with_task_name(name)
|
||||
task = @tasks[tn]
|
||||
return task if task
|
||||
break if scope.empty?
|
||||
scope = scope.tail
|
||||
end
|
||||
nil
|
||||
end
|
||||
private :lookup_in_scope
|
||||
|
||||
# Return the list of scope names currently active in the task
|
||||
# manager.
|
||||
def current_scope
|
||||
@scope
|
||||
end
|
||||
|
||||
# Evaluate the block in a nested namespace named +name+. Create
|
||||
# an anonymous namespace if +name+ is nil.
|
||||
def in_namespace(name)
|
||||
name ||= generate_name
|
||||
@scope = Scope.new(name, @scope)
|
||||
ns = NameSpace.new(self, @scope)
|
||||
yield(ns)
|
||||
ns
|
||||
ensure
|
||||
@scope = @scope.tail
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Add a location to the locations field of the given task.
|
||||
def add_location(task)
|
||||
loc = find_location
|
||||
task.locations << loc if loc
|
||||
task
|
||||
end
|
||||
|
||||
# Find the location that called into the dsl layer.
|
||||
def find_location
|
||||
locations = caller
|
||||
i = 0
|
||||
while locations[i]
|
||||
return locations[i + 1] if locations[i] =~ /rake\/dsl_definition.rb/
|
||||
i += 1
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
# Generate an anonymous namespace name.
|
||||
def generate_name
|
||||
@seed ||= 0
|
||||
@seed += 1
|
||||
"_anon_#{@seed}"
|
||||
end
|
||||
|
||||
def trace_rule(level, message) # :nodoc:
|
||||
options.trace_output.puts "#{" " * level}#{message}" if
|
||||
Rake.application.options.trace_rules
|
||||
end
|
||||
|
||||
# Attempt to create a rule given the list of prerequisites.
|
||||
def attempt_rule(task_name, task_pattern, args, extensions, block, level)
|
||||
sources = make_sources(task_name, task_pattern, extensions)
|
||||
prereqs = sources.map { |source|
|
||||
trace_rule level, "Attempting Rule #{task_name} => #{source}"
|
||||
if File.exist?(source) || Rake::Task.task_defined?(source)
|
||||
trace_rule level, "(#{task_name} => #{source} ... EXIST)"
|
||||
source
|
||||
elsif parent = enhance_with_matching_rule(source, level + 1)
|
||||
trace_rule level, "(#{task_name} => #{source} ... ENHANCE)"
|
||||
parent.name
|
||||
else
|
||||
trace_rule level, "(#{task_name} => #{source} ... FAIL)"
|
||||
return nil
|
||||
end
|
||||
}
|
||||
task = FileTask.define_task(task_name, { args => prereqs }, &block)
|
||||
task.sources = prereqs
|
||||
task
|
||||
end
|
||||
|
||||
# Make a list of sources from the list of file name extensions /
|
||||
# translation procs.
|
||||
def make_sources(task_name, task_pattern, extensions)
|
||||
result = extensions.map { |ext|
|
||||
case ext
|
||||
when /%/
|
||||
task_name.pathmap(ext)
|
||||
when %r{/}
|
||||
ext
|
||||
when /^\./
|
||||
source = task_name.sub(task_pattern, ext)
|
||||
source == ext ? task_name.ext(ext) : source
|
||||
when String, Symbol
|
||||
ext.to_s
|
||||
when Proc, Method
|
||||
if ext.arity == 1
|
||||
ext.call(task_name)
|
||||
else
|
||||
ext.call
|
||||
end
|
||||
else
|
||||
fail "Don't know how to handle rule dependent: #{ext.inspect}"
|
||||
end
|
||||
}
|
||||
result.flatten
|
||||
end
|
||||
|
||||
# Return the current description, clearing it in the process.
|
||||
def get_description(task)
|
||||
desc = @last_description
|
||||
@last_description = nil
|
||||
desc
|
||||
end
|
||||
|
||||
class << self
|
||||
attr_accessor :record_task_metadata # :nodoc:
|
||||
TaskManager.record_task_metadata = false
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,12 @@
|
||||
# frozen_string_literal: true
|
||||
require "rake"
|
||||
|
||||
module Rake
|
||||
|
||||
# Base class for Task Libraries.
|
||||
class TaskLib
|
||||
include Cloneable
|
||||
include Rake::DSL
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,189 @@
|
||||
# frozen_string_literal: true
|
||||
require "rake"
|
||||
require "rake/tasklib"
|
||||
|
||||
module Rake
|
||||
|
||||
# Create a task that runs a set of tests.
|
||||
#
|
||||
# Example:
|
||||
# require "rake/testtask"
|
||||
#
|
||||
# Rake::TestTask.new do |t|
|
||||
# t.libs << "test"
|
||||
# t.test_files = FileList['test/test*.rb']
|
||||
# t.verbose = true
|
||||
# end
|
||||
#
|
||||
# If rake is invoked with a "TEST=filename" command line option,
|
||||
# then the list of test files will be overridden to include only the
|
||||
# filename specified on the command line. This provides an easy way
|
||||
# to run just one test.
|
||||
#
|
||||
# If rake is invoked with a "TESTOPTS=options" command line option,
|
||||
# then the given options are passed to the test process after a
|
||||
# '--'. This allows Test::Unit options to be passed to the test
|
||||
# suite.
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# rake test # run tests normally
|
||||
# rake test TEST=just_one_file.rb # run just one test file.
|
||||
# rake test TESTOPTS="-v" # run in verbose mode
|
||||
# rake test TESTOPTS="--runner=fox" # use the fox test runner
|
||||
#
|
||||
class TestTask < TaskLib
|
||||
|
||||
# Name of test task. (default is :test)
|
||||
attr_accessor :name
|
||||
|
||||
# List of directories added to $LOAD_PATH before running the
|
||||
# tests. (default is 'lib')
|
||||
attr_accessor :libs
|
||||
|
||||
# True if verbose test output desired. (default is false)
|
||||
attr_accessor :verbose
|
||||
|
||||
# Test options passed to the test suite. An explicit
|
||||
# TESTOPTS=opts on the command line will override this. (default
|
||||
# is NONE)
|
||||
attr_accessor :options
|
||||
|
||||
# Request that the tests be run with the warning flag set.
|
||||
# E.g. warning=true implies "ruby -w" used to run the tests.
|
||||
# (default is true)
|
||||
attr_accessor :warning
|
||||
|
||||
# Glob pattern to match test files. (default is 'test/test*.rb')
|
||||
attr_accessor :pattern
|
||||
|
||||
# Style of test loader to use. Options are:
|
||||
#
|
||||
# * :rake -- Rake provided test loading script (default).
|
||||
# * :testrb -- Ruby provided test loading script.
|
||||
# * :direct -- Load tests using command line loader.
|
||||
#
|
||||
attr_accessor :loader
|
||||
|
||||
# Array of command line options to pass to ruby when running test loader.
|
||||
attr_accessor :ruby_opts
|
||||
|
||||
# Description of the test task. (default is 'Run tests')
|
||||
attr_accessor :description
|
||||
|
||||
# Task prerequisites.
|
||||
attr_accessor :deps
|
||||
|
||||
# Explicitly define the list of test files to be included in a
|
||||
# test. +list+ is expected to be an array of file names (a
|
||||
# FileList is acceptable). If both +pattern+ and +test_files+ are
|
||||
# used, then the list of test files is the union of the two.
|
||||
def test_files=(list)
|
||||
@test_files = list
|
||||
end
|
||||
|
||||
# Create a testing task.
|
||||
def initialize(name=:test)
|
||||
@name = name
|
||||
@libs = ["lib"]
|
||||
@pattern = nil
|
||||
@options = nil
|
||||
@test_files = nil
|
||||
@verbose = false
|
||||
@warning = true
|
||||
@loader = :rake
|
||||
@ruby_opts = []
|
||||
@description = "Run tests" + (@name == :test ? "" : " for #{@name}")
|
||||
@deps = []
|
||||
if @name.is_a?(Hash)
|
||||
@deps = @name.values.first
|
||||
@name = @name.keys.first
|
||||
end
|
||||
yield self if block_given?
|
||||
@pattern = "test/test*.rb" if @pattern.nil? && @test_files.nil?
|
||||
define
|
||||
end
|
||||
|
||||
# Create the tasks defined by this task lib.
|
||||
def define
|
||||
desc @description
|
||||
task @name => Array(deps) do
|
||||
FileUtilsExt.verbose(@verbose) do
|
||||
puts "Use TESTOPTS=\"--verbose\" to pass --verbose" \
|
||||
", etc. to runners." if ARGV.include? "--verbose"
|
||||
args =
|
||||
"#{ruby_opts_string} #{run_code} " +
|
||||
"#{file_list_string} #{option_list}"
|
||||
ruby args do |ok, status|
|
||||
if !ok && status.respond_to?(:signaled?) && status.signaled?
|
||||
raise SignalException.new(status.termsig)
|
||||
elsif !ok
|
||||
status = "Command failed with status (#{status.exitstatus})"
|
||||
details = ": [ruby #{args}]"
|
||||
message =
|
||||
if Rake.application.options.trace or @verbose
|
||||
status + details
|
||||
else
|
||||
status
|
||||
end
|
||||
|
||||
fail message
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
def option_list # :nodoc:
|
||||
(ENV["TESTOPTS"] ||
|
||||
ENV["TESTOPT"] ||
|
||||
ENV["TEST_OPTS"] ||
|
||||
ENV["TEST_OPT"] ||
|
||||
@options ||
|
||||
"")
|
||||
end
|
||||
|
||||
def ruby_opts_string # :nodoc:
|
||||
opts = @ruby_opts.dup
|
||||
opts.unshift("-I\"#{lib_path}\"") unless @libs.empty?
|
||||
opts.unshift("-w") if @warning
|
||||
opts.join(" ")
|
||||
end
|
||||
|
||||
def lib_path # :nodoc:
|
||||
@libs.join(File::PATH_SEPARATOR)
|
||||
end
|
||||
|
||||
def file_list_string # :nodoc:
|
||||
file_list.map { |fn| "\"#{fn}\"" }.join(" ")
|
||||
end
|
||||
|
||||
def file_list # :nodoc:
|
||||
if ENV["TEST"]
|
||||
FileList[ENV["TEST"]]
|
||||
else
|
||||
result = []
|
||||
result += @test_files.to_a if @test_files
|
||||
result += FileList[@pattern].to_a if @pattern
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
def ruby_version # :nodoc:
|
||||
RUBY_VERSION
|
||||
end
|
||||
|
||||
def run_code # :nodoc:
|
||||
case @loader
|
||||
when :direct
|
||||
"-e \"ARGV.each{|f| require f}\""
|
||||
when :testrb
|
||||
"-S testrb"
|
||||
when :rake
|
||||
"#{__dir__}/rake_test_loader.rb"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,49 @@
|
||||
# frozen_string_literal: true
|
||||
require "rake/private_reader"
|
||||
|
||||
module Rake
|
||||
|
||||
class ThreadHistoryDisplay # :nodoc: all
|
||||
include Rake::PrivateReader
|
||||
|
||||
private_reader :stats, :items, :threads
|
||||
|
||||
def initialize(stats)
|
||||
@stats = stats
|
||||
@items = { _seq_: 1 }
|
||||
@threads = { _seq_: "A" }
|
||||
end
|
||||
|
||||
def show
|
||||
puts "Job History:"
|
||||
stats.each do |stat|
|
||||
stat[:data] ||= {}
|
||||
rename(stat, :thread, threads)
|
||||
rename(stat[:data], :item_id, items)
|
||||
rename(stat[:data], :new_thread, threads)
|
||||
rename(stat[:data], :deleted_thread, threads)
|
||||
printf("%8d %2s %-20s %s\n",
|
||||
(stat[:time] * 1_000_000).round,
|
||||
stat[:thread],
|
||||
stat[:event],
|
||||
stat[:data].map do |k, v| "#{k}:#{v}" end.join(" "))
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def rename(hash, key, renames)
|
||||
if hash && hash[key]
|
||||
original = hash[key]
|
||||
value = renames[original]
|
||||
unless value
|
||||
value = renames[:_seq_]
|
||||
renames[:_seq_] = renames[:_seq_].succ
|
||||
renames[original] = value
|
||||
end
|
||||
hash[key] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,163 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "rake/promise"
|
||||
require "set"
|
||||
|
||||
module Rake
|
||||
|
||||
class ThreadPool # :nodoc: all
|
||||
|
||||
# Creates a ThreadPool object. The +thread_count+ parameter is the size
|
||||
# of the pool.
|
||||
def initialize(thread_count)
|
||||
@max_active_threads = [thread_count, 0].max
|
||||
@threads = Set.new
|
||||
@threads_mon = Monitor.new
|
||||
@queue = Queue.new
|
||||
@join_cond = @threads_mon.new_cond
|
||||
|
||||
@history_start_time = nil
|
||||
@history = []
|
||||
@history_mon = Monitor.new
|
||||
@total_threads_in_play = 0
|
||||
end
|
||||
|
||||
# Creates a future executed by the +ThreadPool+.
|
||||
#
|
||||
# The args are passed to the block when executing (similarly to
|
||||
# Thread#new) The return value is an object representing
|
||||
# a future which has been created and added to the queue in the
|
||||
# pool. Sending #value to the object will sleep the
|
||||
# current thread until the future is finished and will return the
|
||||
# result (or raise an exception thrown from the future)
|
||||
def future(*args, &block)
|
||||
promise = Promise.new(args, &block)
|
||||
promise.recorder = lambda { |*stats| stat(*stats) }
|
||||
|
||||
@queue.enq promise
|
||||
stat :queued, item_id: promise.object_id
|
||||
start_thread
|
||||
promise
|
||||
end
|
||||
|
||||
# Waits until the queue of futures is empty and all threads have exited.
|
||||
def join
|
||||
@threads_mon.synchronize do
|
||||
begin
|
||||
stat :joining
|
||||
@join_cond.wait unless @threads.empty?
|
||||
stat :joined
|
||||
rescue Exception => e
|
||||
stat :joined
|
||||
$stderr.puts e
|
||||
$stderr.print "Queue contains #{@queue.size} items. " +
|
||||
"Thread pool contains #{@threads.count} threads\n"
|
||||
$stderr.print "Current Thread #{Thread.current} status = " +
|
||||
"#{Thread.current.status}\n"
|
||||
$stderr.puts e.backtrace.join("\n")
|
||||
@threads.each do |t|
|
||||
$stderr.print "Thread #{t} status = #{t.status}\n"
|
||||
$stderr.puts t.backtrace.join("\n")
|
||||
end
|
||||
raise e
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Enable the gathering of history events.
|
||||
def gather_history #:nodoc:
|
||||
@history_start_time = Time.now if @history_start_time.nil?
|
||||
end
|
||||
|
||||
# Return a array of history events for the thread pool.
|
||||
#
|
||||
# History gathering must be enabled to be able to see the events
|
||||
# (see #gather_history). Best to call this when the job is
|
||||
# complete (i.e. after ThreadPool#join is called).
|
||||
def history # :nodoc:
|
||||
@history_mon.synchronize { @history.dup }.
|
||||
sort_by { |i| i[:time] }.
|
||||
each { |i| i[:time] -= @history_start_time }
|
||||
end
|
||||
|
||||
# Return a hash of always collected statistics for the thread pool.
|
||||
def statistics # :nodoc:
|
||||
{
|
||||
total_threads_in_play: @total_threads_in_play,
|
||||
max_active_threads: @max_active_threads,
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# processes one item on the queue. Returns true if there was an
|
||||
# item to process, false if there was no item
|
||||
def process_queue_item #:nodoc:
|
||||
return false if @queue.empty?
|
||||
|
||||
# Even though we just asked if the queue was empty, it
|
||||
# still could have had an item which by this statement
|
||||
# is now gone. For this reason we pass true to Queue#deq
|
||||
# because we will sleep indefinitely if it is empty.
|
||||
promise = @queue.deq(true)
|
||||
stat :dequeued, item_id: promise.object_id
|
||||
promise.work
|
||||
return true
|
||||
|
||||
rescue ThreadError # this means the queue is empty
|
||||
false
|
||||
end
|
||||
|
||||
def safe_thread_count
|
||||
@threads_mon.synchronize do
|
||||
@threads.count
|
||||
end
|
||||
end
|
||||
|
||||
def start_thread # :nodoc:
|
||||
@threads_mon.synchronize do
|
||||
next unless @threads.count < @max_active_threads
|
||||
|
||||
t = Thread.new do
|
||||
begin
|
||||
while safe_thread_count <= @max_active_threads
|
||||
break unless process_queue_item
|
||||
end
|
||||
ensure
|
||||
@threads_mon.synchronize do
|
||||
@threads.delete Thread.current
|
||||
stat :ended, thread_count: @threads.count
|
||||
@join_cond.broadcast if @threads.empty?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@threads << t
|
||||
stat(
|
||||
:spawned,
|
||||
new_thread: t.object_id,
|
||||
thread_count: @threads.count)
|
||||
@total_threads_in_play = @threads.count if
|
||||
@threads.count > @total_threads_in_play
|
||||
end
|
||||
end
|
||||
|
||||
def stat(event, data=nil) # :nodoc:
|
||||
return if @history_start_time.nil?
|
||||
info = {
|
||||
event: event,
|
||||
data: data,
|
||||
time: Time.now,
|
||||
thread: Thread.current.object_id,
|
||||
}
|
||||
@history_mon.synchronize { @history << info }
|
||||
end
|
||||
|
||||
# for testing only
|
||||
|
||||
def __queue__ # :nodoc:
|
||||
@queue
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,23 @@
|
||||
# frozen_string_literal: true
|
||||
module Rake
|
||||
module TraceOutput # :nodoc: all
|
||||
|
||||
# Write trace output to output stream +out+.
|
||||
#
|
||||
# The write is done as a single IO call (to print) to lessen the
|
||||
# chance that the trace output is interrupted by other tasks also
|
||||
# producing output.
|
||||
def trace_on(out, *strings)
|
||||
sep = $\ || "\n"
|
||||
if strings.empty?
|
||||
output = sep
|
||||
else
|
||||
output = strings.map { |s|
|
||||
next if s.nil?
|
||||
s.end_with?(sep) ? s : s + sep
|
||||
}.join
|
||||
end
|
||||
out.print(output)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
module Rake
|
||||
VERSION = "13.2.1"
|
||||
|
||||
module Version # :nodoc: all
|
||||
MAJOR, MINOR, BUILD, *OTHER = Rake::VERSION.split "."
|
||||
|
||||
NUMBERS = [MAJOR, MINOR, BUILD, *OTHER]
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,51 @@
|
||||
# frozen_string_literal: true
|
||||
require "rbconfig"
|
||||
|
||||
module Rake
|
||||
# Win 32 interface methods for Rake. Windows specific functionality
|
||||
# will be placed here to collect that knowledge in one spot.
|
||||
module Win32 # :nodoc: all
|
||||
|
||||
# Error indicating a problem in locating the home directory on a
|
||||
# Win32 system.
|
||||
class Win32HomeError < RuntimeError
|
||||
end
|
||||
|
||||
class << self
|
||||
# True if running on a windows system.
|
||||
def windows?
|
||||
RbConfig::CONFIG["host_os"] =~ %r!(msdos|mswin|djgpp|mingw|[Ww]indows)!
|
||||
end
|
||||
|
||||
# The standard directory containing system wide rake files on
|
||||
# Win 32 systems. Try the following environment variables (in
|
||||
# order):
|
||||
#
|
||||
# * HOME
|
||||
# * HOMEDRIVE + HOMEPATH
|
||||
# * APPDATA
|
||||
# * USERPROFILE
|
||||
#
|
||||
# If the above are not defined, the return nil.
|
||||
def win32_system_dir #:nodoc:
|
||||
win32_shared_path = ENV["HOME"]
|
||||
if win32_shared_path.nil? && ENV["HOMEDRIVE"] && ENV["HOMEPATH"]
|
||||
win32_shared_path = ENV["HOMEDRIVE"] + ENV["HOMEPATH"]
|
||||
end
|
||||
|
||||
win32_shared_path ||= ENV["APPDATA"]
|
||||
win32_shared_path ||= ENV["USERPROFILE"]
|
||||
raise Win32HomeError,
|
||||
"Unable to determine home path environment variable." if
|
||||
win32_shared_path.nil? or win32_shared_path.empty?
|
||||
normalize(File.join(win32_shared_path, "Rake"))
|
||||
end
|
||||
|
||||
# Normalize a win32 path so that the slashes are all forward slashes.
|
||||
def normalize(path)
|
||||
path.gsub(/\\/, "/")
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,101 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative "lib/rake/version"
|
||||
|
||||
Gem::Specification.new do |s|
|
||||
s.name = "rake"
|
||||
s.version = Rake::VERSION
|
||||
s.authors = ["Hiroshi SHIBATA", "Eric Hodel", "Jim Weirich"]
|
||||
s.email = ["hsbt@ruby-lang.org", "drbrain@segment7.net", ""]
|
||||
|
||||
s.summary = "Rake is a Make-like program implemented in Ruby"
|
||||
s.description = <<~DESCRIPTION
|
||||
Rake is a Make-like program implemented in Ruby. Tasks and dependencies are
|
||||
specified in standard Ruby syntax.
|
||||
Rake has the following features:
|
||||
* Rakefiles (rake's version of Makefiles) are completely defined in standard Ruby syntax.
|
||||
No XML files to edit. No quirky Makefile syntax to worry about (is that a tab or a space?)
|
||||
* Users can specify tasks with prerequisites.
|
||||
* Rake supports rule patterns to synthesize implicit tasks.
|
||||
* Flexible FileLists that act like arrays but know about manipulating file names and paths.
|
||||
* Supports parallel execution of tasks.
|
||||
DESCRIPTION
|
||||
s.homepage = "https://github.com/ruby/rake"
|
||||
s.licenses = ["MIT"]
|
||||
|
||||
s.metadata = {
|
||||
"bug_tracker_uri" => "https://github.com/ruby/rake/issues",
|
||||
"changelog_uri" => "https://github.com/ruby/rake/blob/v#{s.version}/History.rdoc",
|
||||
"documentation_uri" => "https://ruby.github.io/rake",
|
||||
"source_code_uri" => "https://github.com/ruby/rake/tree/v#{s.version}"
|
||||
}
|
||||
|
||||
s.files = [
|
||||
"History.rdoc",
|
||||
"MIT-LICENSE",
|
||||
"README.rdoc",
|
||||
"doc/command_line_usage.rdoc",
|
||||
"doc/example/Rakefile1",
|
||||
"doc/example/Rakefile2",
|
||||
"doc/example/a.c",
|
||||
"doc/example/b.c",
|
||||
"doc/example/main.c",
|
||||
"doc/glossary.rdoc",
|
||||
"doc/jamis.rb",
|
||||
"doc/proto_rake.rdoc",
|
||||
"doc/rake.1",
|
||||
"doc/rakefile.rdoc",
|
||||
"doc/rational.rdoc",
|
||||
"exe/rake",
|
||||
"lib/rake.rb",
|
||||
"lib/rake/application.rb",
|
||||
"lib/rake/backtrace.rb",
|
||||
"lib/rake/clean.rb",
|
||||
"lib/rake/cloneable.rb",
|
||||
"lib/rake/cpu_counter.rb",
|
||||
"lib/rake/default_loader.rb",
|
||||
"lib/rake/dsl_definition.rb",
|
||||
"lib/rake/early_time.rb",
|
||||
"lib/rake/ext/core.rb",
|
||||
"lib/rake/ext/string.rb",
|
||||
"lib/rake/file_creation_task.rb",
|
||||
"lib/rake/file_list.rb",
|
||||
"lib/rake/file_task.rb",
|
||||
"lib/rake/file_utils.rb",
|
||||
"lib/rake/file_utils_ext.rb",
|
||||
"lib/rake/invocation_chain.rb",
|
||||
"lib/rake/invocation_exception_mixin.rb",
|
||||
"lib/rake/late_time.rb",
|
||||
"lib/rake/linked_list.rb",
|
||||
"lib/rake/loaders/makefile.rb",
|
||||
"lib/rake/multi_task.rb",
|
||||
"lib/rake/name_space.rb",
|
||||
"lib/rake/packagetask.rb",
|
||||
"lib/rake/phony.rb",
|
||||
"lib/rake/private_reader.rb",
|
||||
"lib/rake/promise.rb",
|
||||
"lib/rake/pseudo_status.rb",
|
||||
"lib/rake/rake_module.rb",
|
||||
"lib/rake/rake_test_loader.rb",
|
||||
"lib/rake/rule_recursion_overflow_error.rb",
|
||||
"lib/rake/scope.rb",
|
||||
"lib/rake/task.rb",
|
||||
"lib/rake/task_argument_error.rb",
|
||||
"lib/rake/task_arguments.rb",
|
||||
"lib/rake/task_manager.rb",
|
||||
"lib/rake/tasklib.rb",
|
||||
"lib/rake/testtask.rb",
|
||||
"lib/rake/thread_history_display.rb",
|
||||
"lib/rake/thread_pool.rb",
|
||||
"lib/rake/trace_output.rb",
|
||||
"lib/rake/version.rb",
|
||||
"lib/rake/win32.rb",
|
||||
"rake.gemspec"
|
||||
]
|
||||
s.bindir = "exe"
|
||||
s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
||||
s.require_paths = ["lib"]
|
||||
|
||||
s.required_ruby_version = Gem::Requirement.new(">= 2.3")
|
||||
s.rdoc_options = ["--main", "README.rdoc"]
|
||||
end
|
||||
Reference in New Issue
Block a user