rook 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/COPYING ADDED
@@ -0,0 +1,340 @@
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 2, June 1991
3
+
4
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
5
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
6
+ Everyone is permitted to copy and distribute verbatim copies
7
+ of this license document, but changing it is not allowed.
8
+
9
+ Preamble
10
+
11
+ The licenses for most software are designed to take away your
12
+ freedom to share and change it. By contrast, the GNU General Public
13
+ License is intended to guarantee your freedom to share and change free
14
+ software--to make sure the software is free for all its users. This
15
+ General Public License applies to most of the Free Software
16
+ Foundation's software and to any other program whose authors commit to
17
+ using it. (Some other Free Software Foundation software is covered by
18
+ the GNU Library General Public License instead.) You can apply it to
19
+ your programs, too.
20
+
21
+ When we speak of free software, we are referring to freedom, not
22
+ price. Our General Public Licenses are designed to make sure that you
23
+ have the freedom to distribute copies of free software (and charge for
24
+ this service if you wish), that you receive source code or can get it
25
+ if you want it, that you can change the software or use pieces of it
26
+ in new free programs; and that you know you can do these things.
27
+
28
+ To protect your rights, we need to make restrictions that forbid
29
+ anyone to deny you these rights or to ask you to surrender the rights.
30
+ These restrictions translate to certain responsibilities for you if you
31
+ distribute copies of the software, or if you modify it.
32
+
33
+ For example, if you distribute copies of such a program, whether
34
+ gratis or for a fee, you must give the recipients all the rights that
35
+ you have. You must make sure that they, too, receive or can get the
36
+ source code. And you must show them these terms so they know their
37
+ rights.
38
+
39
+ We protect your rights with two steps: (1) copyright the software, and
40
+ (2) offer you this license which gives you legal permission to copy,
41
+ distribute and/or modify the software.
42
+
43
+ Also, for each author's protection and ours, we want to make certain
44
+ that everyone understands that there is no warranty for this free
45
+ software. If the software is modified by someone else and passed on, we
46
+ want its recipients to know that what they have is not the original, so
47
+ that any problems introduced by others will not reflect on the original
48
+ authors' reputations.
49
+
50
+ Finally, any free program is threatened constantly by software
51
+ patents. We wish to avoid the danger that redistributors of a free
52
+ program will individually obtain patent licenses, in effect making the
53
+ program proprietary. To prevent this, we have made it clear that any
54
+ patent must be licensed for everyone's free use or not licensed at all.
55
+
56
+ The precise terms and conditions for copying, distribution and
57
+ modification follow.
58
+
59
+ GNU GENERAL PUBLIC LICENSE
60
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61
+
62
+ 0. This License applies to any program or other work which contains
63
+ a notice placed by the copyright holder saying it may be distributed
64
+ under the terms of this General Public License. The "Program", below,
65
+ refers to any such program or work, and a "work based on the Program"
66
+ means either the Program or any derivative work under copyright law:
67
+ that is to say, a work containing the Program or a portion of it,
68
+ either verbatim or with modifications and/or translated into another
69
+ language. (Hereinafter, translation is included without limitation in
70
+ the term "modification".) Each licensee is addressed as "you".
71
+
72
+ Activities other than copying, distribution and modification are not
73
+ covered by this License; they are outside its scope. The act of
74
+ running the Program is not restricted, and the output from the Program
75
+ is covered only if its contents constitute a work based on the
76
+ Program (independent of having been made by running the Program).
77
+ Whether that is true depends on what the Program does.
78
+
79
+ 1. You may copy and distribute verbatim copies of the Program's
80
+ source code as you receive it, in any medium, provided that you
81
+ conspicuously and appropriately publish on each copy an appropriate
82
+ copyright notice and disclaimer of warranty; keep intact all the
83
+ notices that refer to this License and to the absence of any warranty;
84
+ and give any other recipients of the Program a copy of this License
85
+ along with the Program.
86
+
87
+ You may charge a fee for the physical act of transferring a copy, and
88
+ you may at your option offer warranty protection in exchange for a fee.
89
+
90
+ 2. You may modify your copy or copies of the Program or any portion
91
+ of it, thus forming a work based on the Program, and copy and
92
+ distribute such modifications or work under the terms of Section 1
93
+ above, provided that you also meet all of these conditions:
94
+
95
+ a) You must cause the modified files to carry prominent notices
96
+ stating that you changed the files and the date of any change.
97
+
98
+ b) You must cause any work that you distribute or publish, that in
99
+ whole or in part contains or is derived from the Program or any
100
+ part thereof, to be licensed as a whole at no charge to all third
101
+ parties under the terms of this License.
102
+
103
+ c) If the modified program normally reads commands interactively
104
+ when run, you must cause it, when started running for such
105
+ interactive use in the most ordinary way, to print or display an
106
+ announcement including an appropriate copyright notice and a
107
+ notice that there is no warranty (or else, saying that you provide
108
+ a warranty) and that users may redistribute the program under
109
+ these conditions, and telling the user how to view a copy of this
110
+ License. (Exception: if the Program itself is interactive but
111
+ does not normally print such an announcement, your work based on
112
+ the Program is not required to print an announcement.)
113
+
114
+ These requirements apply to the modified work as a whole. If
115
+ identifiable sections of that work are not derived from the Program,
116
+ and can be reasonably considered independent and separate works in
117
+ themselves, then this License, and its terms, do not apply to those
118
+ sections when you distribute them as separate works. But when you
119
+ distribute the same sections as part of a whole which is a work based
120
+ on the Program, the distribution of the whole must be on the terms of
121
+ this License, whose permissions for other licensees extend to the
122
+ entire whole, and thus to each and every part regardless of who wrote it.
123
+
124
+ Thus, it is not the intent of this section to claim rights or contest
125
+ your rights to work written entirely by you; rather, the intent is to
126
+ exercise the right to control the distribution of derivative or
127
+ collective works based on the Program.
128
+
129
+ In addition, mere aggregation of another work not based on the Program
130
+ with the Program (or with a work based on the Program) on a volume of
131
+ a storage or distribution medium does not bring the other work under
132
+ the scope of this License.
133
+
134
+ 3. You may copy and distribute the Program (or a work based on it,
135
+ under Section 2) in object code or executable form under the terms of
136
+ Sections 1 and 2 above provided that you also do one of the following:
137
+
138
+ a) Accompany it with the complete corresponding machine-readable
139
+ source code, which must be distributed under the terms of Sections
140
+ 1 and 2 above on a medium customarily used for software interchange; or,
141
+
142
+ b) Accompany it with a written offer, valid for at least three
143
+ years, to give any third party, for a charge no more than your
144
+ cost of physically performing source distribution, a complete
145
+ machine-readable copy of the corresponding source code, to be
146
+ distributed under the terms of Sections 1 and 2 above on a medium
147
+ customarily used for software interchange; or,
148
+
149
+ c) Accompany it with the information you received as to the offer
150
+ to distribute corresponding source code. (This alternative is
151
+ allowed only for noncommercial distribution and only if you
152
+ received the program in object code or executable form with such
153
+ an offer, in accord with Subsection b above.)
154
+
155
+ The source code for a work means the preferred form of the work for
156
+ making modifications to it. For an executable work, complete source
157
+ code means all the source code for all modules it contains, plus any
158
+ associated interface definition files, plus the scripts used to
159
+ control compilation and installation of the executable. However, as a
160
+ special exception, the source code distributed need not include
161
+ anything that is normally distributed (in either source or binary
162
+ form) with the major components (compiler, kernel, and so on) of the
163
+ operating system on which the executable runs, unless that component
164
+ itself accompanies the executable.
165
+
166
+ If distribution of executable or object code is made by offering
167
+ access to copy from a designated place, then offering equivalent
168
+ access to copy the source code from the same place counts as
169
+ distribution of the source code, even though third parties are not
170
+ compelled to copy the source along with the object code.
171
+
172
+ 4. You may not copy, modify, sublicense, or distribute the Program
173
+ except as expressly provided under this License. Any attempt
174
+ otherwise to copy, modify, sublicense or distribute the Program is
175
+ void, and will automatically terminate your rights under this License.
176
+ However, parties who have received copies, or rights, from you under
177
+ this License will not have their licenses terminated so long as such
178
+ parties remain in full compliance.
179
+
180
+ 5. You are not required to accept this License, since you have not
181
+ signed it. However, nothing else grants you permission to modify or
182
+ distribute the Program or its derivative works. These actions are
183
+ prohibited by law if you do not accept this License. Therefore, by
184
+ modifying or distributing the Program (or any work based on the
185
+ Program), you indicate your acceptance of this License to do so, and
186
+ all its terms and conditions for copying, distributing or modifying
187
+ the Program or works based on it.
188
+
189
+ 6. Each time you redistribute the Program (or any work based on the
190
+ Program), the recipient automatically receives a license from the
191
+ original licensor to copy, distribute or modify the Program subject to
192
+ these terms and conditions. You may not impose any further
193
+ restrictions on the recipients' exercise of the rights granted herein.
194
+ You are not responsible for enforcing compliance by third parties to
195
+ this License.
196
+
197
+ 7. If, as a consequence of a court judgment or allegation of patent
198
+ infringement or for any other reason (not limited to patent issues),
199
+ conditions are imposed on you (whether by court order, agreement or
200
+ otherwise) that contradict the conditions of this License, they do not
201
+ excuse you from the conditions of this License. If you cannot
202
+ distribute so as to satisfy simultaneously your obligations under this
203
+ License and any other pertinent obligations, then as a consequence you
204
+ may not distribute the Program at all. For example, if a patent
205
+ license would not permit royalty-free redistribution of the Program by
206
+ all those who receive copies directly or indirectly through you, then
207
+ the only way you could satisfy both it and this License would be to
208
+ refrain entirely from distribution of the Program.
209
+
210
+ If any portion of this section is held invalid or unenforceable under
211
+ any particular circumstance, the balance of the section is intended to
212
+ apply and the section as a whole is intended to apply in other
213
+ circumstances.
214
+
215
+ It is not the purpose of this section to induce you to infringe any
216
+ patents or other property right claims or to contest validity of any
217
+ such claims; this section has the sole purpose of protecting the
218
+ integrity of the free software distribution system, which is
219
+ implemented by public license practices. Many people have made
220
+ generous contributions to the wide range of software distributed
221
+ through that system in reliance on consistent application of that
222
+ system; it is up to the author/donor to decide if he or she is willing
223
+ to distribute software through any other system and a licensee cannot
224
+ impose that choice.
225
+
226
+ This section is intended to make thoroughly clear what is believed to
227
+ be a consequence of the rest of this License.
228
+
229
+ 8. If the distribution and/or use of the Program is restricted in
230
+ certain countries either by patents or by copyrighted interfaces, the
231
+ original copyright holder who places the Program under this License
232
+ may add an explicit geographical distribution limitation excluding
233
+ those countries, so that distribution is permitted only in or among
234
+ countries not thus excluded. In such case, this License incorporates
235
+ the limitation as if written in the body of this License.
236
+
237
+ 9. The Free Software Foundation may publish revised and/or new versions
238
+ of the General Public License from time to time. Such new versions will
239
+ be similar in spirit to the present version, but may differ in detail to
240
+ address new problems or concerns.
241
+
242
+ Each version is given a distinguishing version number. If the Program
243
+ specifies a version number of this License which applies to it and "any
244
+ later version", you have the option of following the terms and conditions
245
+ either of that version or of any later version published by the Free
246
+ Software Foundation. If the Program does not specify a version number of
247
+ this License, you may choose any version ever published by the Free Software
248
+ Foundation.
249
+
250
+ 10. If you wish to incorporate parts of the Program into other free
251
+ programs whose distribution conditions are different, write to the author
252
+ to ask for permission. For software which is copyrighted by the Free
253
+ Software Foundation, write to the Free Software Foundation; we sometimes
254
+ make exceptions for this. Our decision will be guided by the two goals
255
+ of preserving the free status of all derivatives of our free software and
256
+ of promoting the sharing and reuse of software generally.
257
+
258
+ NO WARRANTY
259
+
260
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261
+ FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262
+ OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263
+ PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264
+ OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266
+ TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267
+ PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268
+ REPAIR OR CORRECTION.
269
+
270
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272
+ REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273
+ INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274
+ OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275
+ TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276
+ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277
+ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278
+ POSSIBILITY OF SUCH DAMAGES.
279
+
280
+ END OF TERMS AND CONDITIONS
281
+
282
+ How to Apply These Terms to Your New Programs
283
+
284
+ If you develop a new program, and you want it to be of the greatest
285
+ possible use to the public, the best way to achieve this is to make it
286
+ free software which everyone can redistribute and change under these terms.
287
+
288
+ To do so, attach the following notices to the program. It is safest
289
+ to attach them to the start of each source file to most effectively
290
+ convey the exclusion of warranty; and each file should have at least
291
+ the "copyright" line and a pointer to where the full notice is found.
292
+
293
+ <one line to give the program's name and a brief idea of what it does.>
294
+ Copyright (C) <year> <name of author>
295
+
296
+ This program is free software; you can redistribute it and/or modify
297
+ it under the terms of the GNU General Public License as published by
298
+ the Free Software Foundation; either version 2 of the License, or
299
+ (at your option) any later version.
300
+
301
+ This program is distributed in the hope that it will be useful,
302
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
303
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304
+ GNU General Public License for more details.
305
+
306
+ You should have received a copy of the GNU General Public License
307
+ along with this program; if not, write to the Free Software
308
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
309
+
310
+
311
+ Also add information on how to contact you by electronic and paper mail.
312
+
313
+ If the program is interactive, make it output a short notice like this
314
+ when it starts in an interactive mode:
315
+
316
+ Gnomovision version 69, Copyright (C) year name of author
317
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
318
+ This is free software, and you are welcome to redistribute it
319
+ under certain conditions; type `show c' for details.
320
+
321
+ The hypothetical commands `show w' and `show c' should show the appropriate
322
+ parts of the General Public License. Of course, the commands you use may
323
+ be called something other than `show w' and `show c'; they could even be
324
+ mouse-clicks or menu items--whatever suits your program.
325
+
326
+ You should also get your employer (if you work as a programmer) or your
327
+ school, if any, to sign a "copyright disclaimer" for the program, if
328
+ necessary. Here is a sample; alter the names:
329
+
330
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
331
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
332
+
333
+ <signature of Ty Coon>, 1 April 1989
334
+ Ty Coon, President of Vice
335
+
336
+ This General Public License does not permit incorporating your program into
337
+ proprietary programs. If your program is a subroutine library, you may
338
+ consider it more useful to permit linking proprietary applications with the
339
+ library. If this is what you want to do, use the GNU Library General
340
+ Public License instead of this License.
@@ -0,0 +1,69 @@
1
+ .=title: README
2
+ .?lastupdated: $Date$
3
+
4
+
5
+ .$ About
6
+
7
+ Rook is a tool for SCM such as Make, Rake, Ant, and so on.
8
+ Rook is inspired by Cook which is made by Peter Miller.
9
+ http://www.canb.auug.org.au/~millerp/cook/cook.html
10
+
11
+
12
+ .$ Installation
13
+
14
+ Just copy bin/rook file into a proper directory such as
15
+ /usr/local/bin or /usr/bin.
16
+
17
+
18
+ .$ Usage
19
+
20
+ Install Rook and type `rook --help'.
21
+
22
+
23
+ .$ Terms
24
+
25
+ .[ Product ]
26
+ Product is an output of cooking.
27
+
28
+ .[ Ingredient ]
29
+ Ingredient is materials needed for cooking.
30
+
31
+ .[ Recipe ]
32
+ Recipe is a method how to cook product.
33
+
34
+ .[ Generic recipe ]
35
+ Generic recipe is a recipe whose product is
36
+ specified as regular expression.
37
+
38
+ .[ Specific recipe ]
39
+ Specific recipe is a recipe whose product is
40
+ specified as filename.
41
+
42
+ .[ Symbolic recipe ]
43
+ Symbolic recipe is a recipe whose product is
44
+ specified as Symbol.
45
+
46
+
47
+ .$ Example
48
+
49
+ See `examples/' directory.
50
+
51
+
52
+ .$ To Do
53
+
54
+ .* [_] Documentation
55
+ .* [_] Fingerprint support
56
+ .* [_] Dependency Tree
57
+ .* [_] Plugin architecture
58
+
59
+
60
+ .$ License
61
+
62
+ GPL ver2
63
+
64
+
65
+ .$ Copyright
66
+
67
+ Copyright(c) makoto kuwata all rights reserved
68
+
69
+
@@ -0,0 +1,885 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ ###
4
+ ### rook - a SCM(Software Cooking Method) tool, like `make', `cook', or `rake'
5
+ ###
6
+ ### copryright(c) 2005 makoto kuwata all rights reserverd
7
+ ###
8
+ ### $Id: rook 11 2005-04-29 12:26:31Z kwatch $
9
+ ### $Release: 0.0.1$
10
+ ###
11
+
12
+ require 'fileutils'
13
+
14
+ ## force FileUtils to print message to $stdout
15
+ FileUtils.instance_eval "@fileutils_output = $stdout"
16
+
17
+ module Rook
18
+
19
+ VERSION = ('$Rev: 11 $' =~ /(\d+)/) && $1
20
+ LAST_UPDATE = ('$Date: 2005-04-29 21:26:31 +0900 (Fri, 29 Apr 2005) $' =~ /([-\d]+)/) && $1
21
+ RELEASE = ('$Release: 0.0.1$' =~ /([\.\d]+)/) && $1
22
+
23
+ $rook_defaults = {
24
+ :bookname => 'Rookbook', # or 'Cookbook.rb'
25
+ :target => :default, # or :start
26
+ :descriptions => {
27
+ :all => 'create all products',
28
+ :clean => 'remove by-products',
29
+ :clear => 'remove all products and by-products',
30
+ :deploy => 'deploy products',
31
+ :config => 'configure',
32
+ :setup => 'setup',
33
+ :install => 'install products',
34
+ #:start => 'start cooking',
35
+ :default => 'default recipe to start'
36
+ },
37
+ }
38
+ $rook_prompt = "rook$ "
39
+ $rook_current_recipe = nil
40
+ $rook_properties = {}
41
+ $rook_context = {}
42
+ $rook_recipe_desc = nil
43
+
44
+
45
+ class RookError < StandardError
46
+ end
47
+
48
+ class RecipeNotFoundError < RookError
49
+ end
50
+
51
+ class CommandOptionError < RookError
52
+ end
53
+
54
+ class GenericRecipeExecutionError < RookError
55
+ end
56
+
57
+ class RecipeExecutionError < RookError
58
+ end
59
+
60
+ class RecipeError < RookError
61
+ end
62
+
63
+ class IngredientNotFoundError < RookError
64
+ end
65
+
66
+ class CookbookNotFoundError < RookError
67
+ end
68
+
69
+ class AssersionError
70
+ end
71
+
72
+
73
+ ##
74
+ ##
75
+ ##
76
+ class Cookbook
77
+ def initialize(bookname)
78
+ @bookname = bookname
79
+ @specific_recipes = []
80
+ @generic_recipes = []
81
+ @symbolic_recipes = []
82
+ end
83
+ attr_reader :bookname
84
+ attr_reader :specific_recipes, :generic_recipes, :symbolic_recipes
85
+
86
+ def add_specific_recipe(recipe)
87
+ @specific_recipes.unshift(recipe)
88
+ recipe.cookbook = self
89
+ end
90
+
91
+ def add_generic_recipe(recipe)
92
+ @generic_recipes.unshift(recipe)
93
+ recipe.cookbook = self
94
+ end
95
+
96
+ def add_symbolic_recipe(recipe)
97
+ @symbolic_recipes.unshift(recipe)
98
+ recipe.cookbook = self
99
+ end
100
+
101
+ def add_recipe(recipe)
102
+ recipe.add_to_cookbook(self)
103
+ end
104
+
105
+ def lookup(product, index=0)
106
+ if product.is_a?(String) && product[0] == ?:
107
+ product = product[1, product.length-1].intern
108
+ end
109
+ recipe = nil
110
+ if index == 0
111
+ case product
112
+ when Symbol
113
+ recipe = @symbolic_recipes.find { |r| r.product === product }
114
+ when String
115
+ recipe = @specific_recipes.find { |r| r.product === product }
116
+ if !recipe
117
+ recipe = @generic_recipes.find { |r| r.product === product }
118
+ recipe = recipe.specify(product) if recipe
119
+ end
120
+ end
121
+ else
122
+ case product
123
+ when Symbol
124
+ recipes = @symbolic_recipes.find_all { |r| r.product === product }
125
+ recipe = recipes[index]
126
+ when String
127
+ recipes = @specific_recipes.find_all { |r| r.product === product }
128
+ recipes2 = @generic_recipes.find_all { |r| r.product === product }
129
+ recipes.concat(recipes2)
130
+ recipe = recipe[index]
131
+ recipe = specify(product) if recipe
132
+ end
133
+ end
134
+ return recipe
135
+ end
136
+
137
+ def create_context()
138
+ context = {
139
+ :cookbook => self,
140
+ :dress_level => 0,
141
+ }
142
+ return context
143
+ end
144
+
145
+ def _execute()
146
+ ## prework
147
+ rook_context_bkup = $rook_context
148
+ $rook_context = create_context()
149
+ unless test(?f, @bookname)
150
+ raise CookbookNotFoundError.new("`#{@bookname}': cookbook not found.")
151
+ end
152
+ load(@bookname) ## load cookbook
153
+ ## main body
154
+ yield()
155
+ ## postwork
156
+ $rook_context = rook_context_bkup
157
+ end
158
+ private :_execute
159
+
160
+ def start(targets)
161
+ _execute() do
162
+ targets.each do |target|
163
+ target = target[1,target.length-1].intern if target.is_a?(String) && target[0] == ?:
164
+ recipe = lookup(target)
165
+ raise AssertionError.new("recipe.class=#{recipe.class.name}") if recipe.kind_of?(GenericRecipe)
166
+ raise RecipeNotFoundError.new("#{target}: recipe not found.") unless recipe
167
+ recipe.execute()
168
+ end
169
+ end
170
+ end
171
+
172
+ def listup()
173
+ _listup(@symbolic_recipes, false)
174
+ _listup(@specific_recipes, false)
175
+ _listup(@generic_recipes, false)
176
+ end
177
+
178
+ def listup_all()
179
+ _listup(@symbolic_recipes, true)
180
+ _listup(@specific_recipes, true)
181
+ _listup(@generic_recipes, true)
182
+ end
183
+
184
+ def _listup(recipe_list, flag_all=true)
185
+ _execute() do
186
+ list = []; hash = {}
187
+ recipe_list.each do |recipe|
188
+ next if hash.key?(recipe.product)
189
+ next unless flag_all || recipe.desc
190
+ hash[recipe.product] = true
191
+ list << recipe
192
+ end
193
+ list.reverse.each do |recipe|
194
+ product = recipe.product
195
+ display_name = recipe.display_name
196
+ #display_name = product.is_a?(String) ? product : product.inspect
197
+ description = recipe.desc || '(no description)'
198
+ puts(" %-20s - %s" % [ display_name, description ])
199
+ end
200
+ end
201
+ end
202
+
203
+ end
204
+
205
+
206
+ ##
207
+ ##
208
+ ##
209
+ class Recipe
210
+
211
+ def add_to_cookbook
212
+ raise AssertionError.new("Recipe#add_to_cookbook() not implemented.")
213
+ end
214
+
215
+ def initialize(product, *ingredients, &steps)
216
+ @product = product
217
+ @ingredients = []
218
+ ingredients.each do |ingred|
219
+ add_ingredient(ingred)
220
+ end if ingredients
221
+ @steps = steps
222
+ @matched = nil
223
+ @cookbook = nil
224
+ @desc = nil
225
+ end
226
+
227
+ def self.create(product, *ingredients, &steps)
228
+ self.new(product, *ingredients, &steps)
229
+ end
230
+
231
+ attr_accessor :product, :ingredients, :steps, :matched, :cookbook, :desc
232
+
233
+ alias :prod :product
234
+ alias :ingreds :ingredients
235
+ def ingredient
236
+ return @ingredients[0]
237
+ end
238
+ alias :ingred :ingredient
239
+
240
+ attr_writer :display_name
241
+ def display_name
242
+ return @display_name || @product
243
+ end
244
+
245
+ def add_ingredient(ingred)
246
+ case ingred
247
+ when String, Symbol, Proc, Hash
248
+ @ingredients << ingred
249
+ when Array
250
+ ingred.each do |ingred2|
251
+ add_ingredient(ingred2)
252
+ end
253
+ when Regexp
254
+ raise ReicpeError.new("#{ingred.to_s}: regexp is not avairable as ingredient.")
255
+ else
256
+ raise RecipeError.new("`#{ingred.to_s}'(class #{ingred.class.name}): ingredient is invalid object.")
257
+ end
258
+ end
259
+
260
+ def execute(depth=0)
261
+ bkup = $rook_current_recipe
262
+ $rook_current_recipe = self
263
+ puts "### #{self.product.to_s}" unless $rook_properties[:QUIET]
264
+ @steps.call(self) if @steps
265
+ $rook_current_recipe = bkup
266
+ return true
267
+ end
268
+
269
+ def specify(product)
270
+ raise AssertionError.new("Root#specify() is called.")
271
+ end
272
+
273
+ end
274
+
275
+
276
+ ##
277
+ ##
278
+ ##
279
+ class SpecificRecipe < Recipe
280
+
281
+ def add_to_cookbook(cookbook)
282
+ cookbook.add_specific_recipe(self)
283
+ end
284
+
285
+ def initialize(product, *ingredients, &steps)
286
+ super(product, *ingredients, &steps)
287
+ end
288
+
289
+ def self.create(product, *ingredients, &steps)
290
+ recipe = self.new(product, *ingredients, &steps)
291
+ if ingredients.find { |ingred| ingred.is_a?(Hash) }
292
+ raise RecipeError.new("`#{product}': specific recipe can't take a hash as an ingredient.")
293
+ end
294
+ return recipe
295
+ end
296
+
297
+ def specify(product)
298
+ return self
299
+ end
300
+
301
+ def execute(depth=0)
302
+ flag_execute = false
303
+ if @ingredients.empty?
304
+ flag_execute = true
305
+ else
306
+ if test(?e, @product)
307
+ prod_mtime = File.mtime(@product)
308
+ else
309
+ prod_mtime = nil
310
+ flag_execute = true
311
+ end
312
+ @ingredients.each do |ingred|
313
+ ingred_recipe = cookbook().lookup(ingred)
314
+ if ingred_recipe
315
+ flag = ingred_recipe.execute(depth+1)
316
+ flag_execute = true if flag
317
+ end
318
+ if !flag_execute && _ingred_newer?(ingred, prod_mtime)
319
+ flag_execute = true
320
+ end
321
+ end
322
+ unless $rook_properties[:noact]
323
+ @ingredients.each do |ingred|
324
+ if ingred.is_a?(String) && !test(?e, ingred)
325
+ raise IngredientNotFoundError.new("`#{ingred}': ingredient not found (required by `#{@product}').")
326
+ end
327
+ end
328
+ end
329
+ end
330
+ if flag_execute || $rook_properties[:FORCE] || (depth == 0 && $rook_properties[:force])
331
+ begin
332
+ super(depth)
333
+ rescue RecipeExecutionError => ex
334
+ File.delete(@product) if test(?f, @product)
335
+ raise ex
336
+ end
337
+ end
338
+ return flag_execute
339
+ end
340
+
341
+ def _ingred_newer?(ingred, prod_mtime)
342
+ return ingred.is_a?(String) && test(?e, ingred) && prod_mtime && prod_mtime < File.mtime(ingred)
343
+ end
344
+
345
+ end
346
+
347
+
348
+ ##
349
+ ##
350
+ ##
351
+ class GenericRecipe < Recipe
352
+ def add_to_cookbook(cookbook)
353
+ cookbook.add_generic_recipe(self)
354
+ end
355
+
356
+ def initialize(product_pattern, *ingredients, &steps)
357
+ super(product_pattern, *ingredients, &steps)
358
+ end
359
+
360
+ def self.create(product_pattern, *ingredients, &steps)
361
+ recipe = self.new(product_pattern, *ingredients, &steps)
362
+ return recipe
363
+ end
364
+
365
+ def display_name
366
+ return @display_name || @product.inspect
367
+ end
368
+
369
+ ## generates specific recipe
370
+ def specify(product)
371
+ ingred_list = []
372
+ pattern = @product
373
+ pattern =~ product
374
+ m = [ $&, $1, $2, $3, $4, $5 ]
375
+ @ingredients.each do |ingred|
376
+ case ingred
377
+ when String
378
+ ingred2 = expand_matched(ingred, m)
379
+ ingred_list << ingred2
380
+ when Symbol
381
+ ingred_list << ingred
382
+ when Proc
383
+ result = ingred.call(product)
384
+ ingred_list << result if result
385
+ when Hash
386
+ ingred.each do |key, val|
387
+ case key
388
+ when :if_exists
389
+ ingred2 = expand_matched(val, m)
390
+ ingred_list << ingred2 if test(?e, ingred2)
391
+ else
392
+ raise RecipeError.new("`:#{key.id2name}': invalid keyword.")
393
+ end
394
+ end
395
+ else
396
+ raise RecipeError.new("#{product.to_s}(class #{product.class.name}): invalid product.")
397
+ end
398
+ end if @ingredients
399
+ recipe = SpecificRecipe.new(product, *ingred_list, &@steps)
400
+ recipe.matched = m
401
+ recipe.cookbook = self.cookbook()
402
+ return recipe
403
+ end
404
+
405
+ def execute(depth=0)
406
+ raise GenericRecipeExecutionError.new("cannot execute generic recipe.")
407
+ end
408
+
409
+ end
410
+
411
+
412
+ ##
413
+ ##
414
+ ##
415
+ class SymbolicRecipe < Recipe
416
+
417
+ def add_to_cookbook(cookbook)
418
+ cookbook.add_symbolic_recipe(self)
419
+ end
420
+
421
+ def initialize(product_pattern, *ingredients, &steps)
422
+ super(product_pattern, *ingredients, &steps)
423
+ end
424
+
425
+ def self.create(product, *ingredients, &steps)
426
+ recipe = self.new(product, *ingredients, &steps)
427
+ if ingredients.find { |ingred| ingred.is_a?(Hash) }
428
+ raise RecipeError.new("`#{product}': symbolic recipe can't take a hash as an ingredient.")
429
+ end
430
+ return recipe
431
+ end
432
+
433
+ def display_name
434
+ return @display_name || @product.inspect
435
+ end
436
+
437
+ def execute(depth=0)
438
+ @ingredients.each do |ingred|
439
+ recipe = cookbook().lookup(ingred)
440
+ if recipe
441
+ flag = recipe.execute(depth+1)
442
+ flag_execute = true if flag
443
+ end
444
+ end
445
+ super(depth)
446
+ return true
447
+ #flag_execute = false
448
+ #if @ingredients.empty?
449
+ # flag_execute = true
450
+ #else
451
+ # @ingredients.each do |ingred|
452
+ # recipe = cookbook().lookup(ingred)
453
+ # if recipe
454
+ # flag = recipe.execute(depth+1)
455
+ # flag_execute = true if flag
456
+ # end
457
+ # end
458
+ #end
459
+ #if flag_execute
460
+ # super(depth)
461
+ #end
462
+ #return flag_execute
463
+ end
464
+
465
+ def specify(product)
466
+ raise AssertionError.new("SymbolicRecipe#specify is called.")
467
+ end
468
+ end
469
+ end
470
+
471
+
472
+ module RookCommand
473
+
474
+ ##
475
+ ##
476
+ ##
477
+ def desc(description=nil)
478
+ $rook_recipe_desc = description
479
+ end
480
+
481
+ def recipe(product, *ingredients, &steps)
482
+ cookbook = $rook_context[:cookbook]
483
+ case product
484
+ when String
485
+ recipe = Rook::SpecificRecipe.create(product, *ingredients, &steps)
486
+ cookbook.add_specific_recipe(recipe)
487
+ when Regexp
488
+ recipe = Rook::GenericRecipe.create(product, *ingredients, &steps)
489
+ cookbook.add_generic_recipe(recipe)
490
+ when Symbol
491
+ recipe = Rook::SymbolicRecipe.create(product, *ingredients, &steps)
492
+ cookbook.add_symbolic_recipe(recipe)
493
+ end
494
+ ## set description
495
+ if $rook_recipe_desc == nil
496
+ recipe.desc = $rook_defaults[:descriptions][product]
497
+ elsif $rook_recipe_desc == false
498
+ recipe.desc = nil
499
+ else
500
+ recipe.desc = $rook_recipe_desc
501
+ end
502
+ $rook_recipe_desc = nil
503
+ return recipe
504
+ end
505
+
506
+ def grecipe(product, *ingredients, &steps)
507
+ ## convert wildcard ('*', '?', and '{}') into Regexp
508
+ s = product.to_s
509
+ s = Regexp::quote(s)
510
+ s.gsub!(/(\\\?+)/) { |m| '(' + ('.' * m.length) + ')' }
511
+ s.gsub!(/(\\\*)/, '(.*?)')
512
+ s.gsub!(/(\\\{(.*?)\\\})/) { '(' + $2.gsub(/\\\|/, '|') + ')' }
513
+ s.gsub!(/%/, '(.*?)')
514
+ regexp = Regexp::compile("^#{s}$")
515
+ r = recipe(regexp, *ingredients, &steps)
516
+ r.display_name = product
517
+ return r
518
+ end
519
+
520
+ def rook(*targets)
521
+ bookname = $rook_context[:cookbook].bookname
522
+ cookbook = Rook::Cookbook.new(bookname)
523
+ targets = expand_matched(targets, m()) if m()
524
+ cookbook.start(targets)
525
+ end
526
+
527
+ def dress(product=nil)
528
+ product = $rook_current_recipe.product if !product
529
+ $rook_context[:dress_level] += 1
530
+ recipe = $root_context[:cookbook].lookup(product, $rook_context[:dress_level])
531
+ recipe.execute()
532
+ $rook_context[:dress_level] -= 1
533
+ end
534
+
535
+ def set_prompt(prompt)
536
+ $rook_prompt = prompt
537
+ end
538
+
539
+ def get_prompt()
540
+ $rook_prompt
541
+ end
542
+
543
+
544
+ def expand_matched(obj, m=nil)
545
+ m = $rook_current_recipe.matched unless m
546
+ return obj unless m
547
+ case obj
548
+ when String
549
+ if m
550
+ obj = obj.dup
551
+ obj.gsub!(/\{0\}/, m[0]) if m[0]
552
+ obj.gsub!(/\{1\}/, m[1]) if m[1]
553
+ obj.gsub!(/\{2\}/, m[2]) if m[2]
554
+ obj.gsub!(/\{3\}/, m[3]) if m[3]
555
+ obj.gsub!(/\{4\}/, m[4]) if m[4]
556
+ obj.gsub!(/\{5\}/, m[5]) if m[5]
557
+ end
558
+ when Array
559
+ obj = obj.collect { |item| expand_matched(item, m) }
560
+ end
561
+ return obj
562
+ end
563
+ private :expand_matched
564
+
565
+ def m()
566
+ return $rook_current_recipe.matched
567
+ end
568
+
569
+ def _cmd(cmd_str, prompt, ignore_error)
570
+ cmd_str = expand_matched(cmd_str, m()) if m()
571
+ print get_prompt(), cmd_str, "\n" unless $rook_properties[:quiet]
572
+ unless $rook_properties[:noact]
573
+ result = system(cmd_str)
574
+ else
575
+ result = true
576
+ end
577
+ unless result || ignore_error
578
+ raise Rook::RecipeExecutionError.new("command failed: `#{cmd_str}'")
579
+ end
580
+ return result
581
+ end
582
+ private :_cmd
583
+
584
+ def cmd(cmd_str, prompt=$rook_prompt)
585
+ _cmd(cmd_str, prompt, false)
586
+ end
587
+ alias :sh :cmd
588
+ alias :cmd :cmd
589
+ #alias :do :cmd
590
+
591
+ def cmd!(cmd_str, prompt=$rook_prompt)
592
+ cmd(cmd_str, prompt, true)
593
+ end
594
+ alias sh! cmd!
595
+ #alias do! cmd!
596
+
597
+ def echo(*args)
598
+ args = expand_matched(args, m()) if m()
599
+ unless $rook_properties[:noact]
600
+ echo_n(*args)
601
+ puts
602
+ end
603
+ end
604
+
605
+ def echo_n(*args)
606
+ unless $rook_properties[:noact]
607
+ print args.flatten.join(" ")
608
+ end
609
+ end
610
+
611
+
612
+ def chdir(dir, &block)
613
+ dir = expand_matched(dir, m()) if m()
614
+ opts = {}
615
+ opts[:noop] = true if $rook_properties[:noact]
616
+ opts[:verbose] = true if ! $rook_properties[:quiet]
617
+ print get_prompt() if opts[:verbose]
618
+ FileUtils.chdir(dir, opts, &block)
619
+ end
620
+ alias :cd :chdir
621
+
622
+ def _command1(command, args)
623
+ args = args.flatten
624
+ args = expand_matched(args, m()) if m()
625
+ opts = {}
626
+ opts[:noop] = true if $rook_properties[:noact]
627
+ opts[:verbose] = true if ! $rook_properties[:quiet]
628
+ print get_prompt() if opts[:verbose]
629
+ FileUtils.__send__(command, args, opts)
630
+ end
631
+ private :_command1
632
+
633
+ def mkdir(*dirs)
634
+ _command1(:mkdir, dirs)
635
+ end
636
+
637
+ def mkdir_p(*dirs)
638
+ _command1(:mkdir_p, dirs)
639
+ end
640
+
641
+ def rmdir(*dirs)
642
+ _command1(:rmdir, dirs)
643
+ end
644
+
645
+ def remove(*files)
646
+ _command1(:rm, files)
647
+ end
648
+ alias :rm :remove
649
+
650
+ def remove_r(*files)
651
+ _command1(:rm_r, files)
652
+ end
653
+ alias :rm_r :remove_r
654
+
655
+ def remove_rf(*files)
656
+ _command1(:rm_rf, files)
657
+ end
658
+ alias :rm_rf :remove_rf
659
+
660
+
661
+ def _command1_ifexists(command, files)
662
+ list = files.flatten.find_all { |f| test(?e, f) }
663
+ _command1(command, list) unless list.empty?
664
+ end
665
+
666
+ def delete(*files)
667
+ _command1_ifexists(:rm, files)
668
+ end
669
+ alias :del :delete
670
+
671
+ def delete_r(*files)
672
+ _command1_ifexists(:rm_r, files)
673
+ end
674
+ alias :del_r :delete_r
675
+
676
+ def delete_rf(*files)
677
+ _command1_ifexists(:rm_rf, files)
678
+ end
679
+ alias :del_rf :delete_rf
680
+
681
+
682
+ def _command2(command, args)
683
+ args = args.flatten
684
+ args = expand_matched(args, m()) if m()
685
+ src = dest = nil
686
+ if args.length == 1
687
+ src = args[0]
688
+ elsif args.length > 1
689
+ dest = args.pop
690
+ src = args
691
+ end
692
+ opts = {}
693
+ opts[:noop] = true if $rook_properties[:noact]
694
+ opts[:verbose] = true if !$rook_properties[:quiet]
695
+ print get_prompt() if opts[:verbose]
696
+ FileUtils.__send__(command, src, dest, opts)
697
+ end
698
+ private :_command2
699
+
700
+ def copy(*args)
701
+ _command2(:cp, args)
702
+ end
703
+ alias :cp :copy
704
+
705
+ def copy_r(*args)
706
+ _command2(:cp_r, args)
707
+ end
708
+ alias :cp_r :copy_r
709
+
710
+ def move(*args)
711
+ _command2(:mv, args)
712
+ end
713
+ alias :mv :move
714
+
715
+ def store(*args, &filter)
716
+ args = args.flatten
717
+ args = expand_matched(args, m()) if m()
718
+ store_to = args.pop
719
+ src_files = []
720
+ args.each do |file|
721
+ src_files.concat Dir.glob(file)
722
+ end
723
+ src_files.delete_if { |f| !filter.call(f) } if filter
724
+ if ! $rook_properties[:quiet]
725
+ print get_prompt()
726
+ print "store #{args.join(' ')} #{store_to}"
727
+ puts
728
+ end
729
+ if ! $rook_properties[:noact]
730
+ src_files.each do |f|
731
+ srcdir = File::dirname(f)
732
+ destdir = "#{store_to}/#{srcdir}"
733
+ FileUtils.mkdir_p(destdir) unless test(?d, destdir)
734
+ FileUtils.cp_r(f, destdir) unless test(?d, f)
735
+ end
736
+ end
737
+ end
738
+
739
+ end
740
+
741
+
742
+ module Rook
743
+ ##
744
+ ##
745
+ ##
746
+ class Main
747
+ def initialize(argv = ARGV)
748
+ @argv = argv
749
+ end
750
+
751
+ def execute
752
+ options, properties = parse_argv(@argv)
753
+ if properties[:help]
754
+ properties.delete(:help)
755
+ options[?h] = true
756
+ end
757
+ if properties[:version]
758
+ properties.delete(:version)
759
+ options[?v] = true
760
+ end
761
+ if options[?h] || options[?v]
762
+ command = File::basename($0)
763
+ puts "#{command} - a SCM (Software Cooking Method) tool" if options[?h]
764
+ puts version() if options[?v]
765
+ puts usage() if options[?h]
766
+ return
767
+ end
768
+ properties[:noact] = true if options[?n]
769
+ properties[:quiet] = true if options[?q]
770
+ properties[:quiet] = properties[:QUIET] = true if options[?Q]
771
+ properties[:force] = true if options[?f]
772
+ properties[:force] = properties[:FORCE] = true if options[?F]
773
+ $rook_properties = properties
774
+ $rook_prompt = options[?p] if options[?p]
775
+ #
776
+ bookname = options[?b] || $rook_defaults[:bookname]
777
+ cookbook = Cookbook.new(bookname)
778
+ if options[?l] || options[?T]
779
+ cookbook.listup()
780
+ elsif options[?L]
781
+ cookbook.listup_all()
782
+ elsif !@argv.empty?
783
+ targets = @argv
784
+ cookbook.start(targets)
785
+ elsif cookbook.lookup($rook_defaults[:target]) == nil
786
+ puts "*** target is not specified."
787
+ cookbook.listup()
788
+ else
789
+ target = $rook_defaults[:target]
790
+ cookbook.start([target])
791
+ end
792
+ end
793
+
794
+ def usage()
795
+ command = File::basename($0)
796
+ s = ""
797
+ s << "usage: #{command} [options] target-product\n"
798
+ s << " -h : help\n"
799
+ s << " -v : version\n"
800
+ s << " -b cookbook : cookbook (default `#{$rook_defaults[:bookname]}')\n"
801
+ s << " -p prompt : set prompt\n"
802
+ s << " -n : no execute\n"
803
+ s << " -q : quiet\n"
804
+ s << " -Q : more quiet\n"
805
+ s << " -l : list described recipes\n"
806
+ s << " -L : list all recipes\n"
807
+ s << " -f : force\n"
808
+ s << " -F : force strongly\n"
809
+ end
810
+
811
+ def version()
812
+ return "Release: #{Rook::RELEASE} (Revision: #{Rook::VERSION})"
813
+ end
814
+
815
+ def parse_argv(argv)
816
+ options = []
817
+ properties = {}
818
+ while argv[0] && argv[0][0] == ?-
819
+ optstr = argv.shift.dup
820
+ optstr.gsub!(/\A-/, '')
821
+ if optstr[0] == ?-
822
+ optstr.gsub!(/\A-/, '')
823
+ if optstr =~ /\A([-\w]+)(=(.*))?\z/
824
+ prop_name = $1.gsub(/-/, '_')
825
+ prop_value = $3
826
+ properties[prop_name.intern] = prop_value == nil ? true : parse_value(prop_value)
827
+ end
828
+ else
829
+ while optstr && !optstr.empty?
830
+ optchar = optstr[0]
831
+ optstr.gsub!(/\A./, '')
832
+ case optchar
833
+ when ?h, ?v, ?n, ?q, ?Q, ?f, ?F, ?l, ?L
834
+ options[optchar] = true
835
+ when ?b, ?p
836
+ arg = optstr.empty? ? ARGV.shift : opstr
837
+ optstr = nil
838
+ unless arg
839
+ case optchar
840
+ when ?f ; msg = "-#{optchar.chr}: cookbook required."
841
+ when ?p ; msg = "-#{optchar.chr}: prompt required."
842
+ end
843
+ raise CommandOptionError.new(msg)
844
+ end
845
+ options[optchar] = arg
846
+ else
847
+ raise CommandOptionError.new("-#{optchar.chr}: invalid option.")
848
+ end
849
+ end
850
+ end
851
+ end
852
+ return options, properties
853
+ end
854
+
855
+ def parse_value(value)
856
+ case value
857
+ when 'true', 'yes' ; return true
858
+ when 'false', 'no' ; return false
859
+ when 'nil', 'null' ; return nil
860
+ when /\A\d+\z/ ; return value.to_i # eval(value)
861
+ when /\A\d+\.\d+\z/ ; return value.to_f # eval(value)
862
+ when /\A\/(.*)\/\z/ ; return Regexp.compile($1) # eval(value)
863
+ when /\A:(.*)\z/ ; return $1.intern # eval(value)
864
+ when /\A'.*'\z/ ; return eval(value)
865
+ when /\A".*"\z/ ; return eval(value) # unsafe?
866
+ else ; return value
867
+ end
868
+ end
869
+ end
870
+
871
+ end
872
+
873
+
874
+ #if __FILE__ == $0
875
+ include RookCommand
876
+ begin
877
+ main = Rook::Main.new(ARGV)
878
+ main.execute
879
+ rescue Rook::RookError => ex
880
+ $stderr.puts "[ERROR] #{ex.message}"
881
+ #if ex.class == CommandOptionError
882
+ # $stderr.puts main.usage()
883
+ #end
884
+ end
885
+ #end