bpm 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (261) hide show
  1. data/.gitignore +4 -0
  2. data/.gitmodules +0 -0
  3. data/Gemfile +16 -0
  4. data/README.md +55 -0
  5. data/TODO.md +3 -0
  6. data/bin/bpm +8 -0
  7. data/bpm.gemspec +41 -0
  8. data/lib/bpm.rb +18 -0
  9. data/lib/bpm/cli.rb +6 -0
  10. data/lib/bpm/cli/base.rb +358 -0
  11. data/lib/bpm/cli/owner.rb +46 -0
  12. data/lib/bpm/credentials.rb +36 -0
  13. data/lib/bpm/default.json +9 -0
  14. data/lib/bpm/generator.rb +69 -0
  15. data/lib/bpm/init_generator.rb +43 -0
  16. data/lib/bpm/libgems_ext.rb +11 -0
  17. data/lib/bpm/libgems_ext/config_file.rb +33 -0
  18. data/lib/bpm/libgems_ext/dependency_installer.rb +150 -0
  19. data/lib/bpm/libgems_ext/installer.rb +39 -0
  20. data/lib/bpm/libgems_ext/libgems.rb +39 -0
  21. data/lib/bpm/libgems_ext/spec_fetcher.rb +11 -0
  22. data/lib/bpm/local.rb +76 -0
  23. data/lib/bpm/package.rb +277 -0
  24. data/lib/bpm/pipeline.rb +100 -0
  25. data/lib/bpm/pipeline/directive_processor.rb +47 -0
  26. data/lib/bpm/pipeline/generated_asset.rb +68 -0
  27. data/lib/bpm/pipeline/transport_processor.rb +32 -0
  28. data/lib/bpm/project.rb +427 -0
  29. data/lib/bpm/project_generator.rb +34 -0
  30. data/lib/bpm/remote.rb +104 -0
  31. data/lib/bpm/repository.rb +18 -0
  32. data/lib/bpm/server.rb +44 -0
  33. data/lib/bpm/version.rb +3 -0
  34. data/spec/cli/add_spec.rb +159 -0
  35. data/spec/cli/build_spec.rb +87 -0
  36. data/spec/cli/fetch_spec.rb +135 -0
  37. data/spec/cli/fetched_spec.rb +52 -0
  38. data/spec/cli/init_spec.rb +50 -0
  39. data/spec/cli/list_spec.rb +74 -0
  40. data/spec/cli/login_spec.rb +110 -0
  41. data/spec/cli/new_spec.rb +92 -0
  42. data/spec/cli/owner_spec.rb +114 -0
  43. data/spec/cli/push_spec.rb +73 -0
  44. data/spec/cli/remove_spec.rb +84 -0
  45. data/spec/cli/unpack_spec.rb +72 -0
  46. data/spec/cli/unyank_spec.rb +73 -0
  47. data/spec/cli/yank_spec.rb +73 -0
  48. data/spec/credentials_spec.rb +23 -0
  49. data/spec/fixtures/badrake-0.8.7.spd +0 -0
  50. data/spec/fixtures/builder-3.0.0.spd +0 -0
  51. data/spec/fixtures/bundler-1.1.pre.spd +0 -0
  52. data/spec/fixtures/coffee-1.0.1.pre.spd +0 -0
  53. data/spec/fixtures/core-test-0.4.9.spd +0 -0
  54. data/spec/fixtures/core-test/bin/cot +3 -0
  55. data/spec/fixtures/core-test/lib/main.js +1 -0
  56. data/spec/fixtures/core-test/package.json +46 -0
  57. data/spec/fixtures/core-test/resources/runner.css +1 -0
  58. data/spec/fixtures/core-test/tests/test.js +1 -0
  59. data/spec/fixtures/custom_generator-1.0.spd +0 -0
  60. data/spec/fixtures/custom_generator/lib/main.js +1 -0
  61. data/spec/fixtures/custom_generator/package.json +12 -0
  62. data/spec/fixtures/custom_generator/templates/init/project.json +19 -0
  63. data/spec/fixtures/custom_generator/templates/project/app.js +1 -0
  64. data/spec/fixtures/custom_generator/templates/project/lib/main.js +0 -0
  65. data/spec/fixtures/custom_generator/templates/project_generator.rb +20 -0
  66. data/spec/fixtures/hello_world/LICENSE +19 -0
  67. data/spec/fixtures/hello_world/README.md +21 -0
  68. data/spec/fixtures/hello_world/assets/bpm_packages.js +13 -0
  69. data/spec/fixtures/hello_world/assets/bpm_styles.css +14 -0
  70. data/spec/fixtures/hello_world/assets/papa-smurf.jpg +0 -0
  71. data/spec/fixtures/hello_world/hello_world.json +20 -0
  72. data/spec/fixtures/hello_world/lib/main.js +9 -0
  73. data/spec/fixtures/hello_world/packages/custom_package/assets/dummy.txt +1 -0
  74. data/spec/fixtures/hello_world/packages/custom_package/css/sample_styles.css +3 -0
  75. data/spec/fixtures/hello_world/packages/custom_package/custom_dir/basic-module.js +1 -0
  76. data/spec/fixtures/hello_world/packages/custom_package/lib/main.js +1 -0
  77. data/spec/fixtures/hello_world/packages/custom_package/package.json +22 -0
  78. data/spec/fixtures/hello_world/tests/main-test.js +8 -0
  79. data/spec/fixtures/highline-1.6.1.spd +0 -0
  80. data/spec/fixtures/ivory-0.0.1.spd +0 -0
  81. data/spec/fixtures/jquery-1.4.3.spd +0 -0
  82. data/spec/fixtures/optparse-1.0.1.spd +0 -0
  83. data/spec/fixtures/rake-0.8.6.spd +0 -0
  84. data/spec/fixtures/rake-0.8.7.spd +0 -0
  85. data/spec/fixtures/simple_hello/assets/bpm_packages.js +1 -0
  86. data/spec/fixtures/simple_hello/lib/main.js +1 -0
  87. data/spec/fixtures/spade-0.5.0.spd +0 -0
  88. data/spec/fixtures/src/README.txt +1 -0
  89. data/spec/fixtures/src/bundler-1.1.pre/.gitignore +22 -0
  90. data/spec/fixtures/src/bundler-1.1.pre/CHANGELOG.md +646 -0
  91. data/spec/fixtures/src/bundler-1.1.pre/ISSUES.md +47 -0
  92. data/spec/fixtures/src/bundler-1.1.pre/LICENSE +21 -0
  93. data/spec/fixtures/src/bundler-1.1.pre/README.md +29 -0
  94. data/spec/fixtures/src/bundler-1.1.pre/UPGRADING.md +103 -0
  95. data/spec/fixtures/src/bundler-1.1.pre/bin/bundle +20 -0
  96. data/spec/fixtures/src/bundler-1.1.pre/lib/bundler.js +23 -0
  97. data/spec/fixtures/src/bundler-1.1.pre/lib/bundler/definition.js +14 -0
  98. data/spec/fixtures/src/bundler-1.1.pre/lib/bundler/dependency.js +15 -0
  99. data/spec/fixtures/src/bundler-1.1.pre/lib/bundler/dsl.js +9 -0
  100. data/spec/fixtures/src/bundler-1.1.pre/lib/bundler/ui.js +6 -0
  101. data/spec/fixtures/src/bundler-1.1.pre/man/bundle-config.ronn +90 -0
  102. data/spec/fixtures/src/bundler-1.1.pre/man/bundle-exec.ronn +111 -0
  103. data/spec/fixtures/src/bundler-1.1.pre/man/bundle-install.ronn +314 -0
  104. data/spec/fixtures/src/bundler-1.1.pre/man/bundle-package.ronn +59 -0
  105. data/spec/fixtures/src/bundler-1.1.pre/man/bundle-update.ronn +176 -0
  106. data/spec/fixtures/src/bundler-1.1.pre/man/bundle.ronn +80 -0
  107. data/spec/fixtures/src/bundler-1.1.pre/man/gemfile.5.ronn +279 -0
  108. data/spec/fixtures/src/bundler-1.1.pre/man/index.txt +6 -0
  109. data/spec/fixtures/src/bundler-1.1.pre/package.json +19 -0
  110. data/spec/fixtures/src/coffee-1.0.1.pre/bin/cake +0 -0
  111. data/spec/fixtures/src/coffee-1.0.1.pre/bin/coffee +0 -0
  112. data/spec/fixtures/src/coffee-1.0.1.pre/lib/coffee.js +0 -0
  113. data/spec/fixtures/src/coffee-1.0.1.pre/lib/coffee/base.js +0 -0
  114. data/spec/fixtures/src/coffee-1.0.1.pre/lib/coffee/mocha/chai.js +0 -0
  115. data/spec/fixtures/src/coffee-1.0.1.pre/package.json +19 -0
  116. data/spec/fixtures/src/coffee-1.0.1.pre/qunit/coffee/test.js +0 -0
  117. data/spec/fixtures/src/coffee-1.0.1.pre/qunit/test.js +0 -0
  118. data/spec/fixtures/src/jquery/lib/main.js +7179 -0
  119. data/spec/fixtures/src/jquery/package.json +15 -0
  120. data/spec/fixtures/src/rake-0.8.6/CHANGES +436 -0
  121. data/spec/fixtures/src/rake-0.8.6/MIT-LICENSE +21 -0
  122. data/spec/fixtures/src/rake-0.8.6/README +196 -0
  123. data/spec/fixtures/src/rake-0.8.6/Rakefile +430 -0
  124. data/spec/fixtures/src/rake-0.8.6/TODO +20 -0
  125. data/spec/fixtures/src/rake-0.8.6/bin/rake +31 -0
  126. data/spec/fixtures/src/rake-0.8.6/doc/command_line_usage.rdoc +102 -0
  127. data/spec/fixtures/src/rake-0.8.6/doc/example/Rakefile1 +38 -0
  128. data/spec/fixtures/src/rake-0.8.6/doc/example/Rakefile2 +35 -0
  129. data/spec/fixtures/src/rake-0.8.6/doc/example/a.c +6 -0
  130. data/spec/fixtures/src/rake-0.8.6/doc/example/b.c +6 -0
  131. data/spec/fixtures/src/rake-0.8.6/doc/example/main.c +11 -0
  132. data/spec/fixtures/src/rake-0.8.6/doc/glossary.rdoc +51 -0
  133. data/spec/fixtures/src/rake-0.8.6/doc/jamis.rb +591 -0
  134. data/spec/fixtures/src/rake-0.8.6/doc/proto_rake.rdoc +127 -0
  135. data/spec/fixtures/src/rake-0.8.6/doc/rake.1.gz +0 -0
  136. data/spec/fixtures/src/rake-0.8.6/doc/rakefile.rdoc +534 -0
  137. data/spec/fixtures/src/rake-0.8.6/doc/rational.rdoc +151 -0
  138. data/spec/fixtures/src/rake-0.8.6/doc/release_notes/rake-0.4.14.rdoc +23 -0
  139. data/spec/fixtures/src/rake-0.8.6/doc/release_notes/rake-0.4.15.rdoc +35 -0
  140. data/spec/fixtures/src/rake-0.8.6/doc/release_notes/rake-0.5.0.rdoc +53 -0
  141. data/spec/fixtures/src/rake-0.8.6/doc/release_notes/rake-0.5.3.rdoc +78 -0
  142. data/spec/fixtures/src/rake-0.8.6/doc/release_notes/rake-0.5.4.rdoc +46 -0
  143. data/spec/fixtures/src/rake-0.8.6/doc/release_notes/rake-0.6.0.rdoc +141 -0
  144. data/spec/fixtures/src/rake-0.8.6/doc/release_notes/rake-0.7.0.rdoc +119 -0
  145. data/spec/fixtures/src/rake-0.8.6/doc/release_notes/rake-0.7.1.rdoc +59 -0
  146. data/spec/fixtures/src/rake-0.8.6/doc/release_notes/rake-0.7.2.rdoc +121 -0
  147. data/spec/fixtures/src/rake-0.8.6/doc/release_notes/rake-0.7.3.rdoc +47 -0
  148. data/spec/fixtures/src/rake-0.8.6/doc/release_notes/rake-0.8.0.rdoc +114 -0
  149. data/spec/fixtures/src/rake-0.8.6/doc/release_notes/rake-0.8.2.rdoc +165 -0
  150. data/spec/fixtures/src/rake-0.8.6/doc/release_notes/rake-0.8.3.rdoc +112 -0
  151. data/spec/fixtures/src/rake-0.8.6/doc/release_notes/rake-0.8.4.rdoc +147 -0
  152. data/spec/fixtures/src/rake-0.8.6/doc/release_notes/rake-0.8.5.rdoc +53 -0
  153. data/spec/fixtures/src/rake-0.8.6/doc/release_notes/rake-0.8.6.rdoc +55 -0
  154. data/spec/fixtures/src/rake-0.8.6/install.rb +88 -0
  155. data/spec/fixtures/src/rake-0.8.6/lib/rake.rb +2502 -0
  156. data/spec/fixtures/src/rake-0.8.6/lib/rake/alt_system.rb +108 -0
  157. data/spec/fixtures/src/rake-0.8.6/lib/rake/classic_namespace.rb +8 -0
  158. data/spec/fixtures/src/rake-0.8.6/lib/rake/clean.rb +33 -0
  159. data/spec/fixtures/src/rake-0.8.6/lib/rake/contrib/compositepublisher.rb +24 -0
  160. data/spec/fixtures/src/rake-0.8.6/lib/rake/contrib/ftptools.rb +153 -0
  161. data/spec/fixtures/src/rake-0.8.6/lib/rake/contrib/publisher.rb +75 -0
  162. data/spec/fixtures/src/rake-0.8.6/lib/rake/contrib/rubyforgepublisher.rb +18 -0
  163. data/spec/fixtures/src/rake-0.8.6/lib/rake/contrib/sshpublisher.rb +47 -0
  164. data/spec/fixtures/src/rake-0.8.6/lib/rake/contrib/sys.rb +209 -0
  165. data/spec/fixtures/src/rake-0.8.6/lib/rake/gempackagetask.rb +97 -0
  166. data/spec/fixtures/src/rake-0.8.6/lib/rake/loaders/makefile.rb +42 -0
  167. data/spec/fixtures/src/rake-0.8.6/lib/rake/packagetask.rb +184 -0
  168. data/spec/fixtures/src/rake-0.8.6/lib/rake/rake_test_loader.rb +5 -0
  169. data/spec/fixtures/src/rake-0.8.6/lib/rake/rdoctask.rb +209 -0
  170. data/spec/fixtures/src/rake-0.8.6/lib/rake/ruby182_test_unit_fix.rb +23 -0
  171. data/spec/fixtures/src/rake-0.8.6/lib/rake/runtest.rb +23 -0
  172. data/spec/fixtures/src/rake-0.8.6/lib/rake/tasklib.rb +23 -0
  173. data/spec/fixtures/src/rake-0.8.6/lib/rake/testtask.rb +161 -0
  174. data/spec/fixtures/src/rake-0.8.6/lib/rake/win32.rb +55 -0
  175. data/spec/fixtures/src/rake-0.8.6/package.json +18 -0
  176. data/spec/fixtures/src/rake-0.8.6/test/capture_stdout.rb +26 -0
  177. data/spec/fixtures/src/rake-0.8.7/CHANGES +440 -0
  178. data/spec/fixtures/src/rake-0.8.7/MIT-LICENSE +21 -0
  179. data/spec/fixtures/src/rake-0.8.7/README +196 -0
  180. data/spec/fixtures/src/rake-0.8.7/Rakefile +430 -0
  181. data/spec/fixtures/src/rake-0.8.7/TODO +20 -0
  182. data/spec/fixtures/src/rake-0.8.7/bin/rake +31 -0
  183. data/spec/fixtures/src/rake-0.8.7/doc/command_line_usage.rdoc +102 -0
  184. data/spec/fixtures/src/rake-0.8.7/doc/example/Rakefile1 +38 -0
  185. data/spec/fixtures/src/rake-0.8.7/doc/example/Rakefile2 +35 -0
  186. data/spec/fixtures/src/rake-0.8.7/doc/example/a.c +6 -0
  187. data/spec/fixtures/src/rake-0.8.7/doc/example/b.c +6 -0
  188. data/spec/fixtures/src/rake-0.8.7/doc/example/main.c +11 -0
  189. data/spec/fixtures/src/rake-0.8.7/doc/glossary.rdoc +51 -0
  190. data/spec/fixtures/src/rake-0.8.7/doc/jamis.rb +591 -0
  191. data/spec/fixtures/src/rake-0.8.7/doc/proto_rake.rdoc +127 -0
  192. data/spec/fixtures/src/rake-0.8.7/doc/rake.1.gz +0 -0
  193. data/spec/fixtures/src/rake-0.8.7/doc/rakefile.rdoc +534 -0
  194. data/spec/fixtures/src/rake-0.8.7/doc/rational.rdoc +151 -0
  195. data/spec/fixtures/src/rake-0.8.7/doc/release_notes/rake-0.4.14.rdoc +23 -0
  196. data/spec/fixtures/src/rake-0.8.7/doc/release_notes/rake-0.4.15.rdoc +35 -0
  197. data/spec/fixtures/src/rake-0.8.7/doc/release_notes/rake-0.5.0.rdoc +53 -0
  198. data/spec/fixtures/src/rake-0.8.7/doc/release_notes/rake-0.5.3.rdoc +78 -0
  199. data/spec/fixtures/src/rake-0.8.7/doc/release_notes/rake-0.5.4.rdoc +46 -0
  200. data/spec/fixtures/src/rake-0.8.7/doc/release_notes/rake-0.6.0.rdoc +141 -0
  201. data/spec/fixtures/src/rake-0.8.7/doc/release_notes/rake-0.7.0.rdoc +119 -0
  202. data/spec/fixtures/src/rake-0.8.7/doc/release_notes/rake-0.7.1.rdoc +59 -0
  203. data/spec/fixtures/src/rake-0.8.7/doc/release_notes/rake-0.7.2.rdoc +121 -0
  204. data/spec/fixtures/src/rake-0.8.7/doc/release_notes/rake-0.7.3.rdoc +47 -0
  205. data/spec/fixtures/src/rake-0.8.7/doc/release_notes/rake-0.8.0.rdoc +114 -0
  206. data/spec/fixtures/src/rake-0.8.7/doc/release_notes/rake-0.8.2.rdoc +165 -0
  207. data/spec/fixtures/src/rake-0.8.7/doc/release_notes/rake-0.8.3.rdoc +112 -0
  208. data/spec/fixtures/src/rake-0.8.7/doc/release_notes/rake-0.8.4.rdoc +147 -0
  209. data/spec/fixtures/src/rake-0.8.7/doc/release_notes/rake-0.8.5.rdoc +53 -0
  210. data/spec/fixtures/src/rake-0.8.7/doc/release_notes/rake-0.8.6.rdoc +55 -0
  211. data/spec/fixtures/src/rake-0.8.7/doc/release_notes/rake-0.8.7.rdoc +55 -0
  212. data/spec/fixtures/src/rake-0.8.7/install.rb +88 -0
  213. data/spec/fixtures/src/rake-0.8.7/lib/rake.rb +2506 -0
  214. data/spec/fixtures/src/rake-0.8.7/lib/rake/alt_system.rb +108 -0
  215. data/spec/fixtures/src/rake-0.8.7/lib/rake/classic_namespace.rb +8 -0
  216. data/spec/fixtures/src/rake-0.8.7/lib/rake/clean.rb +33 -0
  217. data/spec/fixtures/src/rake-0.8.7/lib/rake/contrib/compositepublisher.rb +24 -0
  218. data/spec/fixtures/src/rake-0.8.7/lib/rake/contrib/ftptools.rb +153 -0
  219. data/spec/fixtures/src/rake-0.8.7/lib/rake/contrib/publisher.rb +75 -0
  220. data/spec/fixtures/src/rake-0.8.7/lib/rake/contrib/rubyforgepublisher.rb +18 -0
  221. data/spec/fixtures/src/rake-0.8.7/lib/rake/contrib/sshpublisher.rb +47 -0
  222. data/spec/fixtures/src/rake-0.8.7/lib/rake/contrib/sys.rb +209 -0
  223. data/spec/fixtures/src/rake-0.8.7/lib/rake/gempackagetask.rb +97 -0
  224. data/spec/fixtures/src/rake-0.8.7/lib/rake/loaders/makefile.rb +42 -0
  225. data/spec/fixtures/src/rake-0.8.7/lib/rake/packagetask.rb +184 -0
  226. data/spec/fixtures/src/rake-0.8.7/lib/rake/rake_test_loader.rb +5 -0
  227. data/spec/fixtures/src/rake-0.8.7/lib/rake/rdoctask.rb +209 -0
  228. data/spec/fixtures/src/rake-0.8.7/lib/rake/ruby182_test_unit_fix.rb +23 -0
  229. data/spec/fixtures/src/rake-0.8.7/lib/rake/runtest.rb +23 -0
  230. data/spec/fixtures/src/rake-0.8.7/lib/rake/tasklib.rb +23 -0
  231. data/spec/fixtures/src/rake-0.8.7/lib/rake/testtask.rb +161 -0
  232. data/spec/fixtures/src/rake-0.8.7/lib/rake/win32.rb +55 -0
  233. data/spec/fixtures/src/rake-0.8.7/package.json +18 -0
  234. data/spec/fixtures/src/rake-0.8.7/test/capture_stdout.rb +26 -0
  235. data/spec/fixtures/src/spade/lib/main.js +1 -0
  236. data/spec/fixtures/src/spade/package.json +15 -0
  237. data/spec/fixtures/transporter/lib/main.js +3 -0
  238. data/spec/fixtures/transporter/packages/transport/lib/wrapper.js +5 -0
  239. data/spec/fixtures/transporter/packages/transport/package.json +21 -0
  240. data/spec/fixtures/transporter/transporter.json +18 -0
  241. data/spec/gauntlet_spec.rb +33 -0
  242. data/spec/package_spec.rb +319 -0
  243. data/spec/pipeline_spec.rb +213 -0
  244. data/spec/project_spec.rb +130 -0
  245. data/spec/spec_helper.rb +42 -0
  246. data/spec/support/cli.rb +93 -0
  247. data/spec/support/env.rb +18 -0
  248. data/spec/support/fake.rb +53 -0
  249. data/spec/support/fake_gem_server.rb +72 -0
  250. data/spec/support/fake_gemcutter.rb +50 -0
  251. data/spec/support/matchers.rb +32 -0
  252. data/spec/support/path.rb +63 -0
  253. data/spec/support/project.rb +43 -0
  254. data/templates/init/assets/bpm_packages.js +7 -0
  255. data/templates/init/assets/bpm_styles.css +7 -0
  256. data/templates/init/project.json +16 -0
  257. data/templates/project/LICENSE +19 -0
  258. data/templates/project/README.md +21 -0
  259. data/templates/project/app.js +1 -0
  260. data/templates/project/index.html +13 -0
  261. metadata +627 -0
@@ -0,0 +1,55 @@
1
+ = Rake 0.8.6 Released
2
+
3
+ Rake version 0.8.5 introduced greatly improved support for executing
4
+ commands on Windows. The "sh" command now has the same semantics on
5
+ Windows that it has on Unix based platforms.
6
+
7
+ Rake version 0.8.6 includes minor fixes the the RDoc generation.
8
+
9
+ == Changes
10
+
11
+ === New Features / Enhancements in Version 0.8.5
12
+
13
+ * Improved implementation of the Rake system command for Windows.
14
+ (patch from James M. Lawrence/quix)
15
+
16
+ * Support for Ruby 1.9's improved system command. (patch from James
17
+ M. Lawrence/quix)
18
+
19
+ * Rake now includes the configured extension when invoking an
20
+ executable (Config::CONFIG['EXEEXT])
21
+
22
+ === Bug Fixes in Version 0.8.5
23
+
24
+ * Environment variable keys are now correctly cased (it matters in
25
+ some implementations).
26
+
27
+ == What is Rake
28
+
29
+ Rake is a build tool similar to the make program in many ways. But
30
+ instead of cryptic make recipes, Rake uses standard Ruby code to
31
+ declare tasks and dependencies. You have the full power of a modern
32
+ scripting language built right into your build tool.
33
+
34
+ == Availability
35
+
36
+ The easiest way to get and install rake is via RubyGems ...
37
+
38
+ gem install rake (you may need root/admin privileges)
39
+
40
+ Otherwise, you can get it from the more traditional places:
41
+
42
+ Home Page:: http://rake.rubyforge.org/
43
+ Download:: http://rubyforge.org/project/showfiles.php?group_id=50
44
+ GitHub:: git://github.com/jimweirich/rake.git
45
+
46
+ == Thanks
47
+
48
+ As usual, it was input from users that drove a alot of these changes. The
49
+ following people either contributed patches, made suggestions or made
50
+ otherwise helpful comments. Thanks to ...
51
+
52
+ * James M. Lawrence/quix
53
+ * Luis Lavena
54
+
55
+ -- Jim Weirich
@@ -0,0 +1,88 @@
1
+ require 'rbconfig'
2
+ require 'find'
3
+ require 'ftools'
4
+
5
+ include Config
6
+
7
+ $ruby = CONFIG['ruby_install_name']
8
+
9
+ ##
10
+ # Install a binary file. We patch in on the way through to
11
+ # insert a #! line. If this is a Unix install, we name
12
+ # the command (for example) 'rake' and let the shebang line
13
+ # handle running it. Under windows, we add a '.rb' extension
14
+ # and let file associations to their stuff
15
+ #
16
+
17
+ def installBIN(from, opfile)
18
+
19
+ tmp_dir = nil
20
+ for t in [".", "/tmp", "c:/temp", $bindir]
21
+ stat = File.stat(t) rescue next
22
+ if stat.directory? and stat.writable?
23
+ tmp_dir = t
24
+ break
25
+ end
26
+ end
27
+
28
+ fail "Cannot find a temporary directory" unless tmp_dir
29
+ tmp_file = File.join(tmp_dir, "_tmp")
30
+
31
+ File.open(from) do |ip|
32
+ File.open(tmp_file, "w") do |op|
33
+ ruby = File.join($realbindir, $ruby)
34
+ op.puts "#!#{ruby} -w"
35
+ op.write ip.read
36
+ end
37
+ end
38
+
39
+ opfile += ".rb" if CONFIG["target_os"] =~ /mswin/i
40
+ File::install(tmp_file, File.join($bindir, opfile), 0755, true)
41
+ File::unlink(tmp_file)
42
+ end
43
+
44
+ $sitedir = CONFIG["sitelibdir"]
45
+ unless $sitedir
46
+ version = CONFIG["MAJOR"]+"."+CONFIG["MINOR"]
47
+ $libdir = File.join(CONFIG["libdir"], "ruby", version)
48
+ $sitedir = $:.find {|x| x =~ /site_ruby/}
49
+ if !$sitedir
50
+ $sitedir = File.join($libdir, "site_ruby")
51
+ elsif $sitedir !~ Regexp.quote(version)
52
+ $sitedir = File.join($sitedir, version)
53
+ end
54
+ end
55
+
56
+ $bindir = CONFIG["bindir"]
57
+
58
+ $realbindir = $bindir
59
+
60
+ bindir = CONFIG["bindir"]
61
+ if (destdir = ENV['DESTDIR'])
62
+ $bindir = destdir + $bindir
63
+ $sitedir = destdir + $sitedir
64
+
65
+ File::makedirs($bindir)
66
+ File::makedirs($sitedir)
67
+ end
68
+
69
+ rake_dest = File.join($sitedir, "rake")
70
+ File::makedirs(rake_dest, true)
71
+ File::chmod(0755, rake_dest)
72
+
73
+ # The library files
74
+
75
+ files = Dir.chdir('lib') { Dir['**/*.rb'] }
76
+
77
+ for fn in files
78
+ fn_dir = File.dirname(fn)
79
+ target_dir = File.join($sitedir, fn_dir)
80
+ if ! File.exist?(target_dir)
81
+ File.makedirs(target_dir)
82
+ end
83
+ File::install(File.join('lib', fn), File.join($sitedir, fn), 0644, true)
84
+ end
85
+
86
+ # and the executable
87
+
88
+ installBIN("bin/rake", "rake")
@@ -0,0 +1,2502 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #--
4
+
5
+ # Copyright 2003, 2004, 2005, 2006, 2007, 2008 by Jim Weirich (jim@weirichhouse.org)
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to
9
+ # deal in the Software without restriction, including without limitation the
10
+ # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11
+ # sell copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included in
15
+ # all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23
+ # IN THE SOFTWARE.
24
+ #++
25
+ #
26
+ # = Rake -- Ruby Make
27
+ #
28
+ # This is the main file for the Rake application. Normally it is referenced
29
+ # as a library via a require statement, but it can be distributed
30
+ # independently as an application.
31
+
32
+ RAKEVERSION = '0.8.6'
33
+
34
+ require 'rbconfig'
35
+ require 'fileutils'
36
+ require 'singleton'
37
+ require 'monitor'
38
+ require 'optparse'
39
+ require 'ostruct'
40
+
41
+ require 'rake/win32'
42
+
43
+ $trace = false
44
+
45
+ ######################################################################
46
+ # Rake extensions to Module.
47
+ #
48
+ class Module
49
+ # Check for an existing method in the current class before extending. IF
50
+ # the method already exists, then a warning is printed and the extension is
51
+ # not added. Otherwise the block is yielded and any definitions in the
52
+ # block will take effect.
53
+ #
54
+ # Usage:
55
+ #
56
+ # class String
57
+ # rake_extension("xyz") do
58
+ # def xyz
59
+ # ...
60
+ # end
61
+ # end
62
+ # end
63
+ #
64
+ def rake_extension(method)
65
+ if method_defined?(method)
66
+ $stderr.puts "WARNING: Possible conflict with Rake extension: #{self}##{method} already exists"
67
+ else
68
+ yield
69
+ end
70
+ end
71
+ end # module Module
72
+
73
+
74
+ ######################################################################
75
+ # User defined methods to be added to String.
76
+ #
77
+ class String
78
+ rake_extension("ext") do
79
+ # Replace the file extension with +newext+. If there is no extension on
80
+ # the string, append the new extension to the end. If the new extension
81
+ # is not given, or is the empty string, remove any existing extension.
82
+ #
83
+ # +ext+ is a user added method for the String class.
84
+ def ext(newext='')
85
+ return self.dup if ['.', '..'].include? self
86
+ if newext != ''
87
+ newext = (newext =~ /^\./) ? newext : ("." + newext)
88
+ end
89
+ self.chomp(File.extname(self)) << newext
90
+ end
91
+ end
92
+
93
+ rake_extension("pathmap") do
94
+ # Explode a path into individual components. Used by +pathmap+.
95
+ def pathmap_explode
96
+ head, tail = File.split(self)
97
+ return [self] if head == self
98
+ return [tail] if head == '.' || tail == '/'
99
+ return [head, tail] if head == '/'
100
+ return head.pathmap_explode + [tail]
101
+ end
102
+ protected :pathmap_explode
103
+
104
+ # Extract a partial path from the path. Include +n+ directories from the
105
+ # front end (left hand side) if +n+ is positive. Include |+n+|
106
+ # directories from the back end (right hand side) if +n+ is negative.
107
+ def pathmap_partial(n)
108
+ dirs = File.dirname(self).pathmap_explode
109
+ partial_dirs =
110
+ if n > 0
111
+ dirs[0...n]
112
+ elsif n < 0
113
+ dirs.reverse[0...-n].reverse
114
+ else
115
+ "."
116
+ end
117
+ File.join(partial_dirs)
118
+ end
119
+ protected :pathmap_partial
120
+
121
+ # Preform the pathmap replacement operations on the given path. The
122
+ # patterns take the form 'pat1,rep1;pat2,rep2...'.
123
+ def pathmap_replace(patterns, &block)
124
+ result = self
125
+ patterns.split(';').each do |pair|
126
+ pattern, replacement = pair.split(',')
127
+ pattern = Regexp.new(pattern)
128
+ if replacement == '*' && block_given?
129
+ result = result.sub(pattern, &block)
130
+ elsif replacement
131
+ result = result.sub(pattern, replacement)
132
+ else
133
+ result = result.sub(pattern, '')
134
+ end
135
+ end
136
+ result
137
+ end
138
+ protected :pathmap_replace
139
+
140
+ # Map the path according to the given specification. The specification
141
+ # controls the details of the mapping. The following special patterns are
142
+ # recognized:
143
+ #
144
+ # * <b>%p</b> -- The complete path.
145
+ # * <b>%f</b> -- The base file name of the path, with its file extension,
146
+ # but without any directories.
147
+ # * <b>%n</b> -- The file name of the path without its file extension.
148
+ # * <b>%d</b> -- The directory list of the path.
149
+ # * <b>%x</b> -- The file extension of the path. An empty string if there
150
+ # is no extension.
151
+ # * <b>%X</b> -- Everything *but* the file extension.
152
+ # * <b>%s</b> -- The alternate file separater if defined, otherwise use
153
+ # the standard file separator.
154
+ # * <b>%%</b> -- A percent sign.
155
+ #
156
+ # The %d specifier can also have a numeric prefix (e.g. '%2d'). If the
157
+ # number is positive, only return (up to) +n+ directories in the path,
158
+ # starting from the left hand side. If +n+ is negative, return (up to)
159
+ # |+n+| directories from the right hand side of the path.
160
+ #
161
+ # Examples:
162
+ #
163
+ # 'a/b/c/d/file.txt'.pathmap("%2d") => 'a/b'
164
+ # 'a/b/c/d/file.txt'.pathmap("%-2d") => 'c/d'
165
+ #
166
+ # Also the %d, %p, %f, %n, %x, and %X operators can take a
167
+ # pattern/replacement argument to perform simple string substititions on a
168
+ # particular part of the path. The pattern and replacement are speparated
169
+ # by a comma and are enclosed by curly braces. The replacement spec comes
170
+ # after the % character but before the operator letter. (e.g.
171
+ # "%{old,new}d"). Muliple replacement specs should be separated by
172
+ # semi-colons (e.g. "%{old,new;src,bin}d").
173
+ #
174
+ # Regular expressions may be used for the pattern, and back refs may be
175
+ # used in the replacement text. Curly braces, commas and semi-colons are
176
+ # excluded from both the pattern and replacement text (let's keep parsing
177
+ # reasonable).
178
+ #
179
+ # For example:
180
+ #
181
+ # "src/org/onestepback/proj/A.java".pathmap("%{^src,bin}X.class")
182
+ #
183
+ # returns:
184
+ #
185
+ # "bin/org/onestepback/proj/A.class"
186
+ #
187
+ # If the replacement text is '*', then a block may be provided to perform
188
+ # some arbitrary calculation for the replacement.
189
+ #
190
+ # For example:
191
+ #
192
+ # "/path/to/file.TXT".pathmap("%X%{.*,*}x") { |ext|
193
+ # ext.downcase
194
+ # }
195
+ #
196
+ # Returns:
197
+ #
198
+ # "/path/to/file.txt"
199
+ #
200
+ def pathmap(spec=nil, &block)
201
+ return self if spec.nil?
202
+ result = ''
203
+ spec.scan(/%\{[^}]*\}-?\d*[sdpfnxX%]|%-?\d+d|%.|[^%]+/) do |frag|
204
+ case frag
205
+ when '%f'
206
+ result << File.basename(self)
207
+ when '%n'
208
+ result << File.basename(self).ext
209
+ when '%d'
210
+ result << File.dirname(self)
211
+ when '%x'
212
+ result << File.extname(self)
213
+ when '%X'
214
+ result << self.ext
215
+ when '%p'
216
+ result << self
217
+ when '%s'
218
+ result << (File::ALT_SEPARATOR || File::SEPARATOR)
219
+ when '%-'
220
+ # do nothing
221
+ when '%%'
222
+ result << "%"
223
+ when /%(-?\d+)d/
224
+ result << pathmap_partial($1.to_i)
225
+ when /^%\{([^}]*)\}(\d*[dpfnxX])/
226
+ patterns, operator = $1, $2
227
+ result << pathmap('%' + operator).pathmap_replace(patterns, &block)
228
+ when /^%/
229
+ fail ArgumentError, "Unknown pathmap specifier #{frag} in '#{spec}'"
230
+ else
231
+ result << frag
232
+ end
233
+ end
234
+ result
235
+ end
236
+ end
237
+ end # class String
238
+
239
+ ##############################################################################
240
+ module Rake
241
+
242
+ # Errors -----------------------------------------------------------
243
+
244
+ # Error indicating an ill-formed task declaration.
245
+ class TaskArgumentError < ArgumentError
246
+ end
247
+
248
+ # Error indicating a recursion overflow error in task selection.
249
+ class RuleRecursionOverflowError < StandardError
250
+ def initialize(*args)
251
+ super
252
+ @targets = []
253
+ end
254
+
255
+ def add_target(target)
256
+ @targets << target
257
+ end
258
+
259
+ def message
260
+ super + ": [" + @targets.reverse.join(' => ') + "]"
261
+ end
262
+ end
263
+
264
+ # --------------------------------------------------------------------------
265
+ # Rake module singleton methods.
266
+ #
267
+ class << self
268
+ # Current Rake Application
269
+ def application
270
+ @application ||= Rake::Application.new
271
+ end
272
+
273
+ # Set the current Rake application object.
274
+ def application=(app)
275
+ @application = app
276
+ end
277
+
278
+ # Return the original directory where the Rake application was started.
279
+ def original_dir
280
+ application.original_dir
281
+ end
282
+
283
+ end
284
+
285
+ ####################################################################
286
+ # Mixin for creating easily cloned objects.
287
+ #
288
+ module Cloneable
289
+ # Clone an object by making a new object and setting all the instance
290
+ # variables to the same values.
291
+ def dup
292
+ sibling = self.class.new
293
+ instance_variables.each do |ivar|
294
+ value = self.instance_variable_get(ivar)
295
+ new_value = value.clone rescue value
296
+ sibling.instance_variable_set(ivar, new_value)
297
+ end
298
+ sibling.taint if tainted?
299
+ sibling
300
+ end
301
+
302
+ def clone
303
+ sibling = dup
304
+ sibling.freeze if frozen?
305
+ sibling
306
+ end
307
+ end
308
+
309
+ ####################################################################
310
+ # Exit status class for times the system just gives us a nil.
311
+ class PseudoStatus
312
+ attr_reader :exitstatus
313
+ def initialize(code=0)
314
+ @exitstatus = code
315
+ end
316
+ def to_i
317
+ @exitstatus << 8
318
+ end
319
+ def >>(n)
320
+ to_i >> n
321
+ end
322
+ def stopped?
323
+ false
324
+ end
325
+ def exited?
326
+ true
327
+ end
328
+ end
329
+
330
+ ####################################################################
331
+ # TaskAguments manage the arguments passed to a task.
332
+ #
333
+ class TaskArguments
334
+ include Enumerable
335
+
336
+ attr_reader :names
337
+
338
+ # Create a TaskArgument object with a list of named arguments
339
+ # (given by :names) and a set of associated values (given by
340
+ # :values). :parent is the parent argument object.
341
+ def initialize(names, values, parent=nil)
342
+ @names = names
343
+ @parent = parent
344
+ @hash = {}
345
+ names.each_with_index { |name, i|
346
+ @hash[name.to_sym] = values[i] unless values[i].nil?
347
+ }
348
+ end
349
+
350
+ # Create a new argument scope using the prerequisite argument
351
+ # names.
352
+ def new_scope(names)
353
+ values = names.collect { |n| self[n] }
354
+ self.class.new(names, values, self)
355
+ end
356
+
357
+ # Find an argument value by name or index.
358
+ def [](index)
359
+ lookup(index.to_sym)
360
+ end
361
+
362
+ # Specify a hash of default values for task arguments. Use the
363
+ # defaults only if there is no specific value for the given
364
+ # argument.
365
+ def with_defaults(defaults)
366
+ @hash = defaults.merge(@hash)
367
+ end
368
+
369
+ def each(&block)
370
+ @hash.each(&block)
371
+ end
372
+
373
+ def method_missing(sym, *args, &block)
374
+ lookup(sym.to_sym)
375
+ end
376
+
377
+ def to_hash
378
+ @hash
379
+ end
380
+
381
+ def to_s
382
+ @hash.inspect
383
+ end
384
+
385
+ def inspect
386
+ to_s
387
+ end
388
+
389
+ protected
390
+
391
+ def lookup(name)
392
+ if @hash.has_key?(name)
393
+ @hash[name]
394
+ elsif ENV.has_key?(name.to_s)
395
+ ENV[name.to_s]
396
+ elsif ENV.has_key?(name.to_s.upcase)
397
+ ENV[name.to_s.upcase]
398
+ elsif @parent
399
+ @parent.lookup(name)
400
+ end
401
+ end
402
+ end
403
+
404
+ EMPTY_TASK_ARGS = TaskArguments.new([], [])
405
+
406
+ ####################################################################
407
+ # InvocationChain tracks the chain of task invocations to detect
408
+ # circular dependencies.
409
+ class InvocationChain
410
+ def initialize(value, tail)
411
+ @value = value
412
+ @tail = tail
413
+ end
414
+
415
+ def member?(obj)
416
+ @value == obj || @tail.member?(obj)
417
+ end
418
+
419
+ def append(value)
420
+ if member?(value)
421
+ fail RuntimeError, "Circular dependency detected: #{to_s} => #{value}"
422
+ end
423
+ self.class.new(value, self)
424
+ end
425
+
426
+ def to_s
427
+ "#{prefix}#{@value}"
428
+ end
429
+
430
+ def self.append(value, chain)
431
+ chain.append(value)
432
+ end
433
+
434
+ private
435
+
436
+ def prefix
437
+ "#{@tail.to_s} => "
438
+ end
439
+
440
+ class EmptyInvocationChain
441
+ def member?(obj)
442
+ false
443
+ end
444
+ def append(value)
445
+ InvocationChain.new(value, self)
446
+ end
447
+ def to_s
448
+ "TOP"
449
+ end
450
+ end
451
+
452
+ EMPTY = EmptyInvocationChain.new
453
+
454
+ end # class InvocationChain
455
+
456
+ end # module Rake
457
+
458
+ module Rake
459
+
460
+ ###########################################################################
461
+ # A Task is the basic unit of work in a Rakefile. Tasks have associated
462
+ # actions (possibly more than one) and a list of prerequisites. When
463
+ # invoked, a task will first ensure that all of its prerequisites have an
464
+ # opportunity to run and then it will execute its own actions.
465
+ #
466
+ # Tasks are not usually created directly using the new method, but rather
467
+ # use the +file+ and +task+ convenience methods.
468
+ #
469
+ class Task
470
+ # List of prerequisites for a task.
471
+ attr_reader :prerequisites
472
+
473
+ # List of actions attached to a task.
474
+ attr_reader :actions
475
+
476
+ # Application owning this task.
477
+ attr_accessor :application
478
+
479
+ # Comment for this task. Restricted to a single line of no more than 50
480
+ # characters.
481
+ attr_reader :comment
482
+
483
+ # Full text of the (possibly multi-line) comment.
484
+ attr_reader :full_comment
485
+
486
+ # Array of nested namespaces names used for task lookup by this task.
487
+ attr_reader :scope
488
+
489
+ # Return task name
490
+ def to_s
491
+ name
492
+ end
493
+
494
+ def inspect
495
+ "<#{self.class} #{name} => [#{prerequisites.join(', ')}]>"
496
+ end
497
+
498
+ # List of sources for task.
499
+ attr_writer :sources
500
+ def sources
501
+ @sources ||= []
502
+ end
503
+
504
+ # First source from a rule (nil if no sources)
505
+ def source
506
+ @sources.first if defined?(@sources)
507
+ end
508
+
509
+ # Create a task named +task_name+ with no actions or prerequisites. Use
510
+ # +enhance+ to add actions and prerequisites.
511
+ def initialize(task_name, app)
512
+ @name = task_name.to_s
513
+ @prerequisites = []
514
+ @actions = []
515
+ @already_invoked = false
516
+ @full_comment = nil
517
+ @comment = nil
518
+ @lock = Monitor.new
519
+ @application = app
520
+ @scope = app.current_scope
521
+ @arg_names = nil
522
+ end
523
+
524
+ # Enhance a task with prerequisites or actions. Returns self.
525
+ def enhance(deps=nil, &block)
526
+ @prerequisites |= deps if deps
527
+ @actions << block if block_given?
528
+ self
529
+ end
530
+
531
+ # Name of the task, including any namespace qualifiers.
532
+ def name
533
+ @name.to_s
534
+ end
535
+
536
+ # Name of task with argument list description.
537
+ def name_with_args # :nodoc:
538
+ if arg_description
539
+ "#{name}#{arg_description}"
540
+ else
541
+ name
542
+ end
543
+ end
544
+
545
+ # Argument description (nil if none).
546
+ def arg_description # :nodoc:
547
+ @arg_names ? "[#{(arg_names || []).join(',')}]" : nil
548
+ end
549
+
550
+ # Name of arguments for this task.
551
+ def arg_names
552
+ @arg_names || []
553
+ end
554
+
555
+ # Reenable the task, allowing its tasks to be executed if the task
556
+ # is invoked again.
557
+ def reenable
558
+ @already_invoked = false
559
+ end
560
+
561
+ # Clear the existing prerequisites and actions of a rake task.
562
+ def clear
563
+ clear_prerequisites
564
+ clear_actions
565
+ self
566
+ end
567
+
568
+ # Clear the existing prerequisites of a rake task.
569
+ def clear_prerequisites
570
+ prerequisites.clear
571
+ self
572
+ end
573
+
574
+ # Clear the existing actions on a rake task.
575
+ def clear_actions
576
+ actions.clear
577
+ self
578
+ end
579
+
580
+ # Invoke the task if it is needed. Prerequites are invoked first.
581
+ def invoke(*args)
582
+ task_args = TaskArguments.new(arg_names, args)
583
+ invoke_with_call_chain(task_args, InvocationChain::EMPTY)
584
+ end
585
+
586
+ # Same as invoke, but explicitly pass a call chain to detect
587
+ # circular dependencies.
588
+ def invoke_with_call_chain(task_args, invocation_chain) # :nodoc:
589
+ new_chain = InvocationChain.append(self, invocation_chain)
590
+ @lock.synchronize do
591
+ if application.options.trace
592
+ puts "** Invoke #{name} #{format_trace_flags}"
593
+ end
594
+ return if @already_invoked
595
+ @already_invoked = true
596
+ invoke_prerequisites(task_args, new_chain)
597
+ execute(task_args) if needed?
598
+ end
599
+ end
600
+ protected :invoke_with_call_chain
601
+
602
+ # Invoke all the prerequisites of a task.
603
+ def invoke_prerequisites(task_args, invocation_chain) # :nodoc:
604
+ @prerequisites.each { |n|
605
+ prereq = application[n, @scope]
606
+ prereq_args = task_args.new_scope(prereq.arg_names)
607
+ prereq.invoke_with_call_chain(prereq_args, invocation_chain)
608
+ }
609
+ end
610
+
611
+ # Format the trace flags for display.
612
+ def format_trace_flags
613
+ flags = []
614
+ flags << "first_time" unless @already_invoked
615
+ flags << "not_needed" unless needed?
616
+ flags.empty? ? "" : "(" + flags.join(", ") + ")"
617
+ end
618
+ private :format_trace_flags
619
+
620
+ # Execute the actions associated with this task.
621
+ def execute(args=nil)
622
+ args ||= EMPTY_TASK_ARGS
623
+ if application.options.dryrun
624
+ puts "** Execute (dry run) #{name}"
625
+ return
626
+ end
627
+ if application.options.trace
628
+ puts "** Execute #{name}"
629
+ end
630
+ application.enhance_with_matching_rule(name) if @actions.empty?
631
+ @actions.each do |act|
632
+ case act.arity
633
+ when 1
634
+ act.call(self)
635
+ else
636
+ act.call(self, args)
637
+ end
638
+ end
639
+ end
640
+
641
+ # Is this task needed?
642
+ def needed?
643
+ true
644
+ end
645
+
646
+ # Timestamp for this task. Basic tasks return the current time for their
647
+ # time stamp. Other tasks can be more sophisticated.
648
+ def timestamp
649
+ @prerequisites.collect { |p| application[p].timestamp }.max || Time.now
650
+ end
651
+
652
+ # Add a description to the task. The description can consist of an option
653
+ # argument list (enclosed brackets) and an optional comment.
654
+ def add_description(description)
655
+ return if ! description
656
+ comment = description.strip
657
+ add_comment(comment) if comment && ! comment.empty?
658
+ end
659
+
660
+ # Writing to the comment attribute is the same as adding a description.
661
+ def comment=(description)
662
+ add_description(description)
663
+ end
664
+
665
+ # Add a comment to the task. If a comment alread exists, separate
666
+ # the new comment with " / ".
667
+ def add_comment(comment)
668
+ if @full_comment
669
+ @full_comment << " / "
670
+ else
671
+ @full_comment = ''
672
+ end
673
+ @full_comment << comment
674
+ if @full_comment =~ /\A([^.]+?\.)( |$)/
675
+ @comment = $1
676
+ else
677
+ @comment = @full_comment
678
+ end
679
+ end
680
+ private :add_comment
681
+
682
+ # Set the names of the arguments for this task. +args+ should be
683
+ # an array of symbols, one for each argument name.
684
+ def set_arg_names(args)
685
+ @arg_names = args.map { |a| a.to_sym }
686
+ end
687
+
688
+ # Return a string describing the internal state of a task. Useful for
689
+ # debugging.
690
+ def investigation
691
+ result = "------------------------------\n"
692
+ result << "Investigating #{name}\n"
693
+ result << "class: #{self.class}\n"
694
+ result << "task needed: #{needed?}\n"
695
+ result << "timestamp: #{timestamp}\n"
696
+ result << "pre-requisites: \n"
697
+ prereqs = @prerequisites.collect {|name| application[name]}
698
+ prereqs.sort! {|a,b| a.timestamp <=> b.timestamp}
699
+ prereqs.each do |p|
700
+ result << "--#{p.name} (#{p.timestamp})\n"
701
+ end
702
+ latest_prereq = @prerequisites.collect{|n| application[n].timestamp}.max
703
+ result << "latest-prerequisite time: #{latest_prereq}\n"
704
+ result << "................................\n\n"
705
+ return result
706
+ end
707
+
708
+ # ----------------------------------------------------------------
709
+ # Rake Module Methods
710
+ #
711
+ class << self
712
+
713
+ # Clear the task list. This cause rake to immediately forget all the
714
+ # tasks that have been assigned. (Normally used in the unit tests.)
715
+ def clear
716
+ Rake.application.clear
717
+ end
718
+
719
+ # List of all defined tasks.
720
+ def tasks
721
+ Rake.application.tasks
722
+ end
723
+
724
+ # Return a task with the given name. If the task is not currently
725
+ # known, try to synthesize one from the defined rules. If no rules are
726
+ # found, but an existing file matches the task name, assume it is a file
727
+ # task with no dependencies or actions.
728
+ def [](task_name)
729
+ Rake.application[task_name]
730
+ end
731
+
732
+ # TRUE if the task name is already defined.
733
+ def task_defined?(task_name)
734
+ Rake.application.lookup(task_name) != nil
735
+ end
736
+
737
+ # Define a task given +args+ and an option block. If a rule with the
738
+ # given name already exists, the prerequisites and actions are added to
739
+ # the existing task. Returns the defined task.
740
+ def define_task(*args, &block)
741
+ Rake.application.define_task(self, *args, &block)
742
+ end
743
+
744
+ # Define a rule for synthesizing tasks.
745
+ def create_rule(*args, &block)
746
+ Rake.application.create_rule(*args, &block)
747
+ end
748
+
749
+ # Apply the scope to the task name according to the rules for
750
+ # this kind of task. Generic tasks will accept the scope as
751
+ # part of the name.
752
+ def scope_name(scope, task_name)
753
+ (scope + [task_name]).join(':')
754
+ end
755
+
756
+ end # class << Rake::Task
757
+ end # class Rake::Task
758
+
759
+
760
+ ###########################################################################
761
+ # A FileTask is a task that includes time based dependencies. If any of a
762
+ # FileTask's prerequisites have a timestamp that is later than the file
763
+ # represented by this task, then the file must be rebuilt (using the
764
+ # supplied actions).
765
+ #
766
+ class FileTask < Task
767
+
768
+ # Is this file task needed? Yes if it doesn't exist, or if its time stamp
769
+ # is out of date.
770
+ def needed?
771
+ ! File.exist?(name) || out_of_date?(timestamp)
772
+ end
773
+
774
+ # Time stamp for file task.
775
+ def timestamp
776
+ if File.exist?(name)
777
+ File.mtime(name.to_s)
778
+ else
779
+ Rake::EARLY
780
+ end
781
+ end
782
+
783
+ private
784
+
785
+ # Are there any prerequisites with a later time than the given time stamp?
786
+ def out_of_date?(stamp)
787
+ @prerequisites.any? { |n| application[n].timestamp > stamp}
788
+ end
789
+
790
+ # ----------------------------------------------------------------
791
+ # Task class methods.
792
+ #
793
+ class << self
794
+ # Apply the scope to the task name according to the rules for this kind
795
+ # of task. File based tasks ignore the scope when creating the name.
796
+ def scope_name(scope, task_name)
797
+ task_name
798
+ end
799
+ end
800
+ end # class Rake::FileTask
801
+
802
+ ###########################################################################
803
+ # A FileCreationTask is a file task that when used as a dependency will be
804
+ # needed if and only if the file has not been created. Once created, it is
805
+ # not re-triggered if any of its dependencies are newer, nor does trigger
806
+ # any rebuilds of tasks that depend on it whenever it is updated.
807
+ #
808
+ class FileCreationTask < FileTask
809
+ # Is this file task needed? Yes if it doesn't exist.
810
+ def needed?
811
+ ! File.exist?(name)
812
+ end
813
+
814
+ # Time stamp for file creation task. This time stamp is earlier
815
+ # than any other time stamp.
816
+ def timestamp
817
+ Rake::EARLY
818
+ end
819
+ end
820
+
821
+ ###########################################################################
822
+ # Same as a regular task, but the immediate prerequisites are done in
823
+ # parallel using Ruby threads.
824
+ #
825
+ class MultiTask < Task
826
+ private
827
+ def invoke_prerequisites(args, invocation_chain)
828
+ threads = @prerequisites.collect { |p|
829
+ Thread.new(p) { |r| application[r].invoke_with_call_chain(args, invocation_chain) }
830
+ }
831
+ threads.each { |t| t.join }
832
+ end
833
+ end
834
+ end # module Rake
835
+
836
+ ## ###########################################################################
837
+ # Task Definition Functions ...
838
+
839
+ # Declare a basic task.
840
+ #
841
+ # Example:
842
+ # task :clobber => [:clean] do
843
+ # rm_rf "html"
844
+ # end
845
+ #
846
+ def task(*args, &block)
847
+ Rake::Task.define_task(*args, &block)
848
+ end
849
+
850
+
851
+ # Declare a file task.
852
+ #
853
+ # Example:
854
+ # file "config.cfg" => ["config.template"] do
855
+ # open("config.cfg", "w") do |outfile|
856
+ # open("config.template") do |infile|
857
+ # while line = infile.gets
858
+ # outfile.puts line
859
+ # end
860
+ # end
861
+ # end
862
+ # end
863
+ #
864
+ def file(*args, &block)
865
+ Rake::FileTask.define_task(*args, &block)
866
+ end
867
+
868
+ # Declare a file creation task.
869
+ # (Mainly used for the directory command).
870
+ def file_create(args, &block)
871
+ Rake::FileCreationTask.define_task(args, &block)
872
+ end
873
+
874
+ # Declare a set of files tasks to create the given directories on demand.
875
+ #
876
+ # Example:
877
+ # directory "testdata/doc"
878
+ #
879
+ def directory(dir)
880
+ Rake.each_dir_parent(dir) do |d|
881
+ file_create d do |t|
882
+ mkdir_p t.name if ! File.exist?(t.name)
883
+ end
884
+ end
885
+ end
886
+
887
+ # Declare a task that performs its prerequisites in parallel. Multitasks does
888
+ # *not* guarantee that its prerequisites will execute in any given order
889
+ # (which is obvious when you think about it)
890
+ #
891
+ # Example:
892
+ # multitask :deploy => [:deploy_gem, :deploy_rdoc]
893
+ #
894
+ def multitask(args, &block)
895
+ Rake::MultiTask.define_task(args, &block)
896
+ end
897
+
898
+ # Create a new rake namespace and use it for evaluating the given block.
899
+ # Returns a NameSpace object that can be used to lookup tasks defined in the
900
+ # namespace.
901
+ #
902
+ # E.g.
903
+ #
904
+ # ns = namespace "nested" do
905
+ # task :run
906
+ # end
907
+ # task_run = ns[:run] # find :run in the given namespace.
908
+ #
909
+ def namespace(name=nil, &block)
910
+ Rake.application.in_namespace(name, &block)
911
+ end
912
+
913
+ # Declare a rule for auto-tasks.
914
+ #
915
+ # Example:
916
+ # rule '.o' => '.c' do |t|
917
+ # sh %{cc -o #{t.name} #{t.source}}
918
+ # end
919
+ #
920
+ def rule(*args, &block)
921
+ Rake::Task.create_rule(*args, &block)
922
+ end
923
+
924
+ # Describe the next rake task.
925
+ #
926
+ # Example:
927
+ # desc "Run the Unit Tests"
928
+ # task :test => [:build]
929
+ # runtests
930
+ # end
931
+ #
932
+ def desc(description)
933
+ Rake.application.last_description = description
934
+ end
935
+
936
+ # Import the partial Rakefiles +fn+. Imported files are loaded _after_ the
937
+ # current file is completely loaded. This allows the import statement to
938
+ # appear anywhere in the importing file, and yet allowing the imported files
939
+ # to depend on objects defined in the importing file.
940
+ #
941
+ # A common use of the import statement is to include files containing
942
+ # dependency declarations.
943
+ #
944
+ # See also the --rakelibdir command line option.
945
+ #
946
+ # Example:
947
+ # import ".depend", "my_rules"
948
+ #
949
+ def import(*fns)
950
+ fns.each do |fn|
951
+ Rake.application.add_import(fn)
952
+ end
953
+ end
954
+
955
+ #############################################################################
956
+ # This a FileUtils extension that defines several additional commands to be
957
+ # added to the FileUtils utility functions.
958
+ #
959
+ module FileUtils
960
+ RUBY = File.join(
961
+ Config::CONFIG['bindir'],
962
+ Config::CONFIG['ruby_install_name'] + Config::CONFIG['EXEEXT']).
963
+ sub(/.*\s.*/m, '"\&"')
964
+
965
+ OPT_TABLE['sh'] = %w(noop verbose)
966
+ OPT_TABLE['ruby'] = %w(noop verbose)
967
+
968
+ # Run the system command +cmd+. If multiple arguments are given the command
969
+ # is not run with the shell (same semantics as Kernel::exec and
970
+ # Kernel::system).
971
+ #
972
+ # Example:
973
+ # sh %{ls -ltr}
974
+ #
975
+ # sh 'ls', 'file with spaces'
976
+ #
977
+ # # check exit status after command runs
978
+ # sh %{grep pattern file} do |ok, res|
979
+ # if ! ok
980
+ # puts "pattern not found (status = #{res.exitstatus})"
981
+ # end
982
+ # end
983
+ #
984
+ def sh(*cmd, &block)
985
+ options = (Hash === cmd.last) ? cmd.pop : {}
986
+ unless block_given?
987
+ show_command = cmd.join(" ")
988
+ show_command = show_command[0,42] + "..." unless $trace
989
+ # TODO code application logic heref show_command.length > 45
990
+ block = lambda { |ok, status|
991
+ ok or fail "Command failed with status (#{status.exitstatus}): [#{show_command}]"
992
+ }
993
+ end
994
+ if RakeFileUtils.verbose_flag == :default
995
+ options[:verbose] = true
996
+ else
997
+ options[:verbose] ||= RakeFileUtils.verbose_flag
998
+ end
999
+ options[:noop] ||= RakeFileUtils.nowrite_flag
1000
+ rake_check_options options, :noop, :verbose
1001
+ rake_output_message cmd.join(" ") if options[:verbose]
1002
+ unless options[:noop]
1003
+ res = rake_system(*cmd)
1004
+ status = $?
1005
+ status = PseudoStatus.new(1) if !res && status.nil?
1006
+ block.call(res, status)
1007
+ end
1008
+ end
1009
+
1010
+ def rake_system(*cmd)
1011
+ Rake::AltSystem.system(*cmd)
1012
+ end
1013
+ private :rake_system
1014
+
1015
+ # Run a Ruby interpreter with the given arguments.
1016
+ #
1017
+ # Example:
1018
+ # ruby %{-pe '$_.upcase!' <README}
1019
+ #
1020
+ def ruby(*args,&block)
1021
+ options = (Hash === args.last) ? args.pop : {}
1022
+ if args.length > 1 then
1023
+ sh(*([RUBY] + args + [options]), &block)
1024
+ else
1025
+ sh("#{RUBY} #{args.first}", options, &block)
1026
+ end
1027
+ end
1028
+
1029
+ LN_SUPPORTED = [true]
1030
+
1031
+ # Attempt to do a normal file link, but fall back to a copy if the link
1032
+ # fails.
1033
+ def safe_ln(*args)
1034
+ unless LN_SUPPORTED[0]
1035
+ cp(*args)
1036
+ else
1037
+ begin
1038
+ ln(*args)
1039
+ rescue StandardError, NotImplementedError => ex
1040
+ LN_SUPPORTED[0] = false
1041
+ cp(*args)
1042
+ end
1043
+ end
1044
+ end
1045
+
1046
+ # Split a file path into individual directory names.
1047
+ #
1048
+ # Example:
1049
+ # split_all("a/b/c") => ['a', 'b', 'c']
1050
+ #
1051
+ def split_all(path)
1052
+ head, tail = File.split(path)
1053
+ return [tail] if head == '.' || tail == '/'
1054
+ return [head, tail] if head == '/'
1055
+ return split_all(head) + [tail]
1056
+ end
1057
+ end
1058
+
1059
+ #############################################################################
1060
+ # RakeFileUtils provides a custom version of the FileUtils methods that
1061
+ # respond to the <tt>verbose</tt> and <tt>nowrite</tt> commands.
1062
+ #
1063
+ module RakeFileUtils
1064
+ include FileUtils
1065
+
1066
+ class << self
1067
+ attr_accessor :verbose_flag, :nowrite_flag
1068
+ end
1069
+ RakeFileUtils.verbose_flag = :default
1070
+ RakeFileUtils.nowrite_flag = false
1071
+
1072
+ $fileutils_verbose = true
1073
+ $fileutils_nowrite = false
1074
+
1075
+ FileUtils::OPT_TABLE.each do |name, opts|
1076
+ default_options = []
1077
+ if opts.include?(:verbose) || opts.include?("verbose")
1078
+ default_options << ':verbose => RakeFileUtils.verbose_flag'
1079
+ end
1080
+ if opts.include?(:noop) || opts.include?("noop")
1081
+ default_options << ':noop => RakeFileUtils.nowrite_flag'
1082
+ end
1083
+
1084
+ next if default_options.empty?
1085
+ module_eval(<<-EOS, __FILE__, __LINE__ + 1)
1086
+ def #{name}( *args, &block )
1087
+ super(
1088
+ *rake_merge_option(args,
1089
+ #{default_options.join(', ')}
1090
+ ), &block)
1091
+ end
1092
+ EOS
1093
+ end
1094
+
1095
+ # Get/set the verbose flag controlling output from the FileUtils utilities.
1096
+ # If verbose is true, then the utility method is echoed to standard output.
1097
+ #
1098
+ # Examples:
1099
+ # verbose # return the current value of the verbose flag
1100
+ # verbose(v) # set the verbose flag to _v_.
1101
+ # verbose(v) { code } # Execute code with the verbose flag set temporarily to _v_.
1102
+ # # Return to the original value when code is done.
1103
+ def verbose(value=nil)
1104
+ oldvalue = RakeFileUtils.verbose_flag
1105
+ RakeFileUtils.verbose_flag = value unless value.nil?
1106
+ if block_given?
1107
+ begin
1108
+ yield
1109
+ ensure
1110
+ RakeFileUtils.verbose_flag = oldvalue
1111
+ end
1112
+ end
1113
+ RakeFileUtils.verbose_flag
1114
+ end
1115
+
1116
+ # Get/set the nowrite flag controlling output from the FileUtils utilities.
1117
+ # If verbose is true, then the utility method is echoed to standard output.
1118
+ #
1119
+ # Examples:
1120
+ # nowrite # return the current value of the nowrite flag
1121
+ # nowrite(v) # set the nowrite flag to _v_.
1122
+ # nowrite(v) { code } # Execute code with the nowrite flag set temporarily to _v_.
1123
+ # # Return to the original value when code is done.
1124
+ def nowrite(value=nil)
1125
+ oldvalue = RakeFileUtils.nowrite_flag
1126
+ RakeFileUtils.nowrite_flag = value unless value.nil?
1127
+ if block_given?
1128
+ begin
1129
+ yield
1130
+ ensure
1131
+ RakeFileUtils.nowrite_flag = oldvalue
1132
+ end
1133
+ end
1134
+ oldvalue
1135
+ end
1136
+
1137
+ # Use this function to prevent protentially destructive ruby code from
1138
+ # running when the :nowrite flag is set.
1139
+ #
1140
+ # Example:
1141
+ #
1142
+ # when_writing("Building Project") do
1143
+ # project.build
1144
+ # end
1145
+ #
1146
+ # The following code will build the project under normal conditions. If the
1147
+ # nowrite(true) flag is set, then the example will print:
1148
+ # DRYRUN: Building Project
1149
+ # instead of actually building the project.
1150
+ #
1151
+ def when_writing(msg=nil)
1152
+ if RakeFileUtils.nowrite_flag
1153
+ puts "DRYRUN: #{msg}" if msg
1154
+ else
1155
+ yield
1156
+ end
1157
+ end
1158
+
1159
+ # Merge the given options with the default values.
1160
+ def rake_merge_option(args, defaults)
1161
+ if Hash === args.last
1162
+ defaults.update(args.last)
1163
+ args.pop
1164
+ end
1165
+ args.push defaults
1166
+ args
1167
+ end
1168
+ private :rake_merge_option
1169
+
1170
+ # Send the message to the default rake output (which is $stderr).
1171
+ def rake_output_message(message)
1172
+ $stderr.puts(message)
1173
+ end
1174
+ private :rake_output_message
1175
+
1176
+ # Check that the options do not contain options not listed in +optdecl+. An
1177
+ # ArgumentError exception is thrown if non-declared options are found.
1178
+ def rake_check_options(options, *optdecl)
1179
+ h = options.dup
1180
+ optdecl.each do |name|
1181
+ h.delete name
1182
+ end
1183
+ raise ArgumentError, "no such option: #{h.keys.join(' ')}" unless h.empty?
1184
+ end
1185
+ private :rake_check_options
1186
+
1187
+ extend self
1188
+ end
1189
+
1190
+ #############################################################################
1191
+ # Include the FileUtils file manipulation functions in the top level module,
1192
+ # but mark them private so that they don't unintentionally define methods on
1193
+ # other objects.
1194
+
1195
+ include RakeFileUtils
1196
+ private(*FileUtils.instance_methods(false))
1197
+ private(*RakeFileUtils.instance_methods(false))
1198
+
1199
+ ######################################################################
1200
+ module Rake
1201
+
1202
+ ###########################################################################
1203
+ # A FileList is essentially an array with a few helper methods defined to
1204
+ # make file manipulation a bit easier.
1205
+ #
1206
+ # FileLists are lazy. When given a list of glob patterns for possible files
1207
+ # to be included in the file list, instead of searching the file structures
1208
+ # to find the files, a FileList holds the pattern for latter use.
1209
+ #
1210
+ # This allows us to define a number of FileList to match any number of
1211
+ # files, but only search out the actual files when then FileList itself is
1212
+ # actually used. The key is that the first time an element of the
1213
+ # FileList/Array is requested, the pending patterns are resolved into a real
1214
+ # list of file names.
1215
+ #
1216
+ class FileList
1217
+
1218
+ include Cloneable
1219
+
1220
+ # == Method Delegation
1221
+ #
1222
+ # The lazy evaluation magic of FileLists happens by implementing all the
1223
+ # array specific methods to call +resolve+ before delegating the heavy
1224
+ # lifting to an embedded array object (@items).
1225
+ #
1226
+ # In addition, there are two kinds of delegation calls. The regular kind
1227
+ # delegates to the @items array and returns the result directly. Well,
1228
+ # almost directly. It checks if the returned value is the @items object
1229
+ # itself, and if so will return the FileList object instead.
1230
+ #
1231
+ # The second kind of delegation call is used in methods that normally
1232
+ # return a new Array object. We want to capture the return value of these
1233
+ # methods and wrap them in a new FileList object. We enumerate these
1234
+ # methods in the +SPECIAL_RETURN+ list below.
1235
+
1236
+ # List of array methods (that are not in +Object+) that need to be
1237
+ # delegated.
1238
+ ARRAY_METHODS = (Array.instance_methods - Object.instance_methods).map { |n| n.to_s }
1239
+
1240
+ # List of additional methods that must be delegated.
1241
+ MUST_DEFINE = %w[to_a inspect]
1242
+
1243
+ # List of methods that should not be delegated here (we define special
1244
+ # versions of them explicitly below).
1245
+ MUST_NOT_DEFINE = %w[to_a to_ary partition *]
1246
+
1247
+ # List of delegated methods that return new array values which need
1248
+ # wrapping.
1249
+ SPECIAL_RETURN = %w[
1250
+ map collect sort sort_by select find_all reject grep
1251
+ compact flatten uniq values_at
1252
+ + - & |
1253
+ ]
1254
+
1255
+ DELEGATING_METHODS = (ARRAY_METHODS + MUST_DEFINE - MUST_NOT_DEFINE).collect{ |s| s.to_s }.sort.uniq
1256
+
1257
+ # Now do the delegation.
1258
+ DELEGATING_METHODS.each_with_index do |sym, i|
1259
+ if SPECIAL_RETURN.include?(sym)
1260
+ ln = __LINE__+1
1261
+ class_eval %{
1262
+ def #{sym}(*args, &block)
1263
+ resolve
1264
+ result = @items.send(:#{sym}, *args, &block)
1265
+ FileList.new.import(result)
1266
+ end
1267
+ }, __FILE__, ln
1268
+ else
1269
+ ln = __LINE__+1
1270
+ class_eval %{
1271
+ def #{sym}(*args, &block)
1272
+ resolve
1273
+ result = @items.send(:#{sym}, *args, &block)
1274
+ result.object_id == @items.object_id ? self : result
1275
+ end
1276
+ }, __FILE__, ln
1277
+ end
1278
+ end
1279
+
1280
+ # Create a file list from the globbable patterns given. If you wish to
1281
+ # perform multiple includes or excludes at object build time, use the
1282
+ # "yield self" pattern.
1283
+ #
1284
+ # Example:
1285
+ # file_list = FileList.new('lib/**/*.rb', 'test/test*.rb')
1286
+ #
1287
+ # pkg_files = FileList.new('lib/**/*') do |fl|
1288
+ # fl.exclude(/\bCVS\b/)
1289
+ # end
1290
+ #
1291
+ def initialize(*patterns)
1292
+ @pending_add = []
1293
+ @pending = false
1294
+ @exclude_patterns = DEFAULT_IGNORE_PATTERNS.dup
1295
+ @exclude_procs = DEFAULT_IGNORE_PROCS.dup
1296
+ @exclude_re = nil
1297
+ @items = []
1298
+ patterns.each { |pattern| include(pattern) }
1299
+ yield self if block_given?
1300
+ end
1301
+
1302
+ # Add file names defined by glob patterns to the file list. If an array
1303
+ # is given, add each element of the array.
1304
+ #
1305
+ # Example:
1306
+ # file_list.include("*.java", "*.cfg")
1307
+ # file_list.include %w( math.c lib.h *.o )
1308
+ #
1309
+ def include(*filenames)
1310
+ # TODO: check for pending
1311
+ filenames.each do |fn|
1312
+ if fn.respond_to? :to_ary
1313
+ include(*fn.to_ary)
1314
+ else
1315
+ @pending_add << fn
1316
+ end
1317
+ end
1318
+ @pending = true
1319
+ self
1320
+ end
1321
+ alias :add :include
1322
+
1323
+ # Register a list of file name patterns that should be excluded from the
1324
+ # list. Patterns may be regular expressions, glob patterns or regular
1325
+ # strings. In addition, a block given to exclude will remove entries that
1326
+ # return true when given to the block.
1327
+ #
1328
+ # Note that glob patterns are expanded against the file system. If a file
1329
+ # is explicitly added to a file list, but does not exist in the file
1330
+ # system, then an glob pattern in the exclude list will not exclude the
1331
+ # file.
1332
+ #
1333
+ # Examples:
1334
+ # FileList['a.c', 'b.c'].exclude("a.c") => ['b.c']
1335
+ # FileList['a.c', 'b.c'].exclude(/^a/) => ['b.c']
1336
+ #
1337
+ # If "a.c" is a file, then ...
1338
+ # FileList['a.c', 'b.c'].exclude("a.*") => ['b.c']
1339
+ #
1340
+ # If "a.c" is not a file, then ...
1341
+ # FileList['a.c', 'b.c'].exclude("a.*") => ['a.c', 'b.c']
1342
+ #
1343
+ def exclude(*patterns, &block)
1344
+ patterns.each do |pat|
1345
+ @exclude_patterns << pat
1346
+ end
1347
+ if block_given?
1348
+ @exclude_procs << block
1349
+ end
1350
+ resolve_exclude if ! @pending
1351
+ self
1352
+ end
1353
+
1354
+
1355
+ # Clear all the exclude patterns so that we exclude nothing.
1356
+ def clear_exclude
1357
+ @exclude_patterns = []
1358
+ @exclude_procs = []
1359
+ calculate_exclude_regexp if ! @pending
1360
+ self
1361
+ end
1362
+
1363
+ # Define equality.
1364
+ def ==(array)
1365
+ to_ary == array
1366
+ end
1367
+
1368
+ # Return the internal array object.
1369
+ def to_a
1370
+ resolve
1371
+ @items
1372
+ end
1373
+
1374
+ # Return the internal array object.
1375
+ def to_ary
1376
+ to_a
1377
+ end
1378
+
1379
+ # Lie about our class.
1380
+ def is_a?(klass)
1381
+ klass == Array || super(klass)
1382
+ end
1383
+ alias kind_of? is_a?
1384
+
1385
+ # Redefine * to return either a string or a new file list.
1386
+ def *(other)
1387
+ result = @items * other
1388
+ case result
1389
+ when Array
1390
+ FileList.new.import(result)
1391
+ else
1392
+ result
1393
+ end
1394
+ end
1395
+
1396
+ # Resolve all the pending adds now.
1397
+ def resolve
1398
+ if @pending
1399
+ @pending = false
1400
+ @pending_add.each do |fn| resolve_add(fn) end
1401
+ @pending_add = []
1402
+ resolve_exclude
1403
+ end
1404
+ self
1405
+ end
1406
+
1407
+ def calculate_exclude_regexp
1408
+ ignores = []
1409
+ @exclude_patterns.each do |pat|
1410
+ case pat
1411
+ when Regexp
1412
+ ignores << pat
1413
+ when /[*?]/
1414
+ Dir[pat].each do |p| ignores << p end
1415
+ else
1416
+ ignores << Regexp.quote(pat)
1417
+ end
1418
+ end
1419
+ if ignores.empty?
1420
+ @exclude_re = /^$/
1421
+ else
1422
+ re_str = ignores.collect { |p| "(" + p.to_s + ")" }.join("|")
1423
+ @exclude_re = Regexp.new(re_str)
1424
+ end
1425
+ end
1426
+
1427
+ def resolve_add(fn)
1428
+ case fn
1429
+ when %r{[*?\[\{]}
1430
+ add_matching(fn)
1431
+ else
1432
+ self << fn
1433
+ end
1434
+ end
1435
+ private :resolve_add
1436
+
1437
+ def resolve_exclude
1438
+ calculate_exclude_regexp
1439
+ reject! { |fn| exclude?(fn) }
1440
+ self
1441
+ end
1442
+ private :resolve_exclude
1443
+
1444
+ # Return a new FileList with the results of running +sub+ against each
1445
+ # element of the oringal list.
1446
+ #
1447
+ # Example:
1448
+ # FileList['a.c', 'b.c'].sub(/\.c$/, '.o') => ['a.o', 'b.o']
1449
+ #
1450
+ def sub(pat, rep)
1451
+ inject(FileList.new) { |res, fn| res << fn.sub(pat,rep) }
1452
+ end
1453
+
1454
+ # Return a new FileList with the results of running +gsub+ against each
1455
+ # element of the original list.
1456
+ #
1457
+ # Example:
1458
+ # FileList['lib/test/file', 'x/y'].gsub(/\//, "\\")
1459
+ # => ['lib\\test\\file', 'x\\y']
1460
+ #
1461
+ def gsub(pat, rep)
1462
+ inject(FileList.new) { |res, fn| res << fn.gsub(pat,rep) }
1463
+ end
1464
+
1465
+ # Same as +sub+ except that the oringal file list is modified.
1466
+ def sub!(pat, rep)
1467
+ each_with_index { |fn, i| self[i] = fn.sub(pat,rep) }
1468
+ self
1469
+ end
1470
+
1471
+ # Same as +gsub+ except that the original file list is modified.
1472
+ def gsub!(pat, rep)
1473
+ each_with_index { |fn, i| self[i] = fn.gsub(pat,rep) }
1474
+ self
1475
+ end
1476
+
1477
+ # Apply the pathmap spec to each of the included file names, returning a
1478
+ # new file list with the modified paths. (See String#pathmap for
1479
+ # details.)
1480
+ def pathmap(spec=nil)
1481
+ collect { |fn| fn.pathmap(spec) }
1482
+ end
1483
+
1484
+ # Return a new FileList with <tt>String#ext</tt> method applied
1485
+ # to each member of the array.
1486
+ #
1487
+ # This method is a shortcut for:
1488
+ #
1489
+ # array.collect { |item| item.ext(newext) }
1490
+ #
1491
+ # +ext+ is a user added method for the Array class.
1492
+ def ext(newext='')
1493
+ collect { |fn| fn.ext(newext) }
1494
+ end
1495
+
1496
+
1497
+ # Grep each of the files in the filelist using the given pattern. If a
1498
+ # block is given, call the block on each matching line, passing the file
1499
+ # name, line number, and the matching line of text. If no block is given,
1500
+ # a standard emac style file:linenumber:line message will be printed to
1501
+ # standard out.
1502
+ def egrep(pattern, *options)
1503
+ each do |fn|
1504
+ open(fn, "rb", *options) do |inf|
1505
+ count = 0
1506
+ inf.each do |line|
1507
+ count += 1
1508
+ if pattern.match(line)
1509
+ if block_given?
1510
+ yield fn, count, line
1511
+ else
1512
+ puts "#{fn}:#{count}:#{line}"
1513
+ end
1514
+ end
1515
+ end
1516
+ end
1517
+ end
1518
+ end
1519
+
1520
+ # Return a new file list that only contains file names from the current
1521
+ # file list that exist on the file system.
1522
+ def existing
1523
+ select { |fn| File.exist?(fn) }
1524
+ end
1525
+
1526
+ # Modify the current file list so that it contains only file name that
1527
+ # exist on the file system.
1528
+ def existing!
1529
+ resolve
1530
+ @items = @items.select { |fn| File.exist?(fn) }
1531
+ self
1532
+ end
1533
+
1534
+ # FileList version of partition. Needed because the nested arrays should
1535
+ # be FileLists in this version.
1536
+ def partition(&block) # :nodoc:
1537
+ resolve
1538
+ result = @items.partition(&block)
1539
+ [
1540
+ FileList.new.import(result[0]),
1541
+ FileList.new.import(result[1]),
1542
+ ]
1543
+ end
1544
+
1545
+ # Convert a FileList to a string by joining all elements with a space.
1546
+ def to_s
1547
+ resolve
1548
+ self.join(' ')
1549
+ end
1550
+
1551
+ # Add matching glob patterns.
1552
+ def add_matching(pattern)
1553
+ Dir[pattern].each do |fn|
1554
+ self << fn unless exclude?(fn)
1555
+ end
1556
+ end
1557
+ private :add_matching
1558
+
1559
+ # Should the given file name be excluded?
1560
+ def exclude?(fn)
1561
+ calculate_exclude_regexp unless @exclude_re
1562
+ fn =~ @exclude_re || @exclude_procs.any? { |p| p.call(fn) }
1563
+ end
1564
+
1565
+ DEFAULT_IGNORE_PATTERNS = [
1566
+ /(^|[\/\\])CVS([\/\\]|$)/,
1567
+ /(^|[\/\\])\.svn([\/\\]|$)/,
1568
+ /\.bak$/,
1569
+ /~$/
1570
+ ]
1571
+ DEFAULT_IGNORE_PROCS = [
1572
+ proc { |fn| fn =~ /(^|[\/\\])core$/ && ! File.directory?(fn) }
1573
+ ]
1574
+ # @exclude_patterns = DEFAULT_IGNORE_PATTERNS.dup
1575
+
1576
+ def import(array)
1577
+ @items = array
1578
+ self
1579
+ end
1580
+
1581
+ class << self
1582
+ # Create a new file list including the files listed. Similar to:
1583
+ #
1584
+ # FileList.new(*args)
1585
+ def [](*args)
1586
+ new(*args)
1587
+ end
1588
+ end
1589
+ end # FileList
1590
+ end
1591
+
1592
+ module Rake
1593
+ class << self
1594
+
1595
+ # Yield each file or directory component.
1596
+ def each_dir_parent(dir) # :nodoc:
1597
+ old_length = nil
1598
+ while dir != '.' && dir.length != old_length
1599
+ yield(dir)
1600
+ old_length = dir.length
1601
+ dir = File.dirname(dir)
1602
+ end
1603
+ end
1604
+ end
1605
+ end # module Rake
1606
+
1607
+ # Alias FileList to be available at the top level.
1608
+ FileList = Rake::FileList
1609
+
1610
+ #############################################################################
1611
+ module Rake
1612
+
1613
+ # Default Rakefile loader used by +import+.
1614
+ class DefaultLoader
1615
+ def load(fn)
1616
+ Kernel.load(File.expand_path(fn))
1617
+ end
1618
+ end
1619
+
1620
+ # EarlyTime is a fake timestamp that occurs _before_ any other time value.
1621
+ class EarlyTime
1622
+ include Comparable
1623
+ include Singleton
1624
+
1625
+ def <=>(other)
1626
+ -1
1627
+ end
1628
+
1629
+ def to_s
1630
+ "<EARLY TIME>"
1631
+ end
1632
+ end
1633
+
1634
+ EARLY = EarlyTime.instance
1635
+ end # module Rake
1636
+
1637
+ #############################################################################
1638
+ # Extensions to time to allow comparisons with an early time class.
1639
+ #
1640
+ class Time
1641
+ alias rake_original_time_compare :<=>
1642
+ def <=>(other)
1643
+ if Rake::EarlyTime === other
1644
+ - other.<=>(self)
1645
+ else
1646
+ rake_original_time_compare(other)
1647
+ end
1648
+ end
1649
+ end # class Time
1650
+
1651
+ module Rake
1652
+
1653
+ ####################################################################
1654
+ # The NameSpace class will lookup task names in the the scope
1655
+ # defined by a +namespace+ command.
1656
+ #
1657
+ class NameSpace
1658
+
1659
+ # Create a namespace lookup object using the given task manager
1660
+ # and the list of scopes.
1661
+ def initialize(task_manager, scope_list)
1662
+ @task_manager = task_manager
1663
+ @scope = scope_list.dup
1664
+ end
1665
+
1666
+ # Lookup a task named +name+ in the namespace.
1667
+ def [](name)
1668
+ @task_manager.lookup(name, @scope)
1669
+ end
1670
+
1671
+ # Return the list of tasks defined in this and nested namespaces.
1672
+ def tasks
1673
+ @task_manager.tasks_in_scope(@scope)
1674
+ end
1675
+ end # NameSpace
1676
+
1677
+
1678
+ ####################################################################
1679
+ # The TaskManager module is a mixin for managing tasks.
1680
+ module TaskManager
1681
+ # Track the last comment made in the Rakefile.
1682
+ attr_accessor :last_description
1683
+ alias :last_comment :last_description # Backwards compatibility
1684
+
1685
+ def initialize
1686
+ super
1687
+ @tasks = Hash.new
1688
+ @rules = Array.new
1689
+ @scope = Array.new
1690
+ @last_description = nil
1691
+ end
1692
+
1693
+ def create_rule(*args, &block)
1694
+ pattern, arg_names, deps = resolve_args(args)
1695
+ pattern = Regexp.new(Regexp.quote(pattern) + '$') if String === pattern
1696
+ @rules << [pattern, deps, block]
1697
+ end
1698
+
1699
+ def define_task(task_class, *args, &block)
1700
+ task_name, arg_names, deps = resolve_args(args)
1701
+ task_name = task_class.scope_name(@scope, task_name)
1702
+ deps = [deps] unless deps.respond_to?(:to_ary)
1703
+ deps = deps.collect {|d| d.to_s }
1704
+ task = intern(task_class, task_name)
1705
+ task.set_arg_names(arg_names) unless arg_names.empty?
1706
+ task.add_description(@last_description)
1707
+ @last_description = nil
1708
+ task.enhance(deps, &block)
1709
+ task
1710
+ end
1711
+
1712
+ # Lookup a task. Return an existing task if found, otherwise
1713
+ # create a task of the current type.
1714
+ def intern(task_class, task_name)
1715
+ @tasks[task_name.to_s] ||= task_class.new(task_name, self)
1716
+ end
1717
+
1718
+ # Find a matching task for +task_name+.
1719
+ def [](task_name, scopes=nil)
1720
+ task_name = task_name.to_s
1721
+ self.lookup(task_name, scopes) or
1722
+ enhance_with_matching_rule(task_name) or
1723
+ synthesize_file_task(task_name) or
1724
+ fail "Don't know how to build task '#{task_name}'"
1725
+ end
1726
+
1727
+ def synthesize_file_task(task_name)
1728
+ return nil unless File.exist?(task_name)
1729
+ define_task(Rake::FileTask, task_name)
1730
+ end
1731
+
1732
+ # Resolve the arguments for a task/rule. Returns a triplet of
1733
+ # [task_name, arg_name_list, prerequisites].
1734
+ def resolve_args(args)
1735
+ if args.last.is_a?(Hash)
1736
+ deps = args.pop
1737
+ resolve_args_with_dependencies(args, deps)
1738
+ else
1739
+ resolve_args_without_dependencies(args)
1740
+ end
1741
+ end
1742
+
1743
+ # Resolve task arguments for a task or rule when there are no
1744
+ # dependencies declared.
1745
+ #
1746
+ # The patterns recognized by this argument resolving function are:
1747
+ #
1748
+ # task :t
1749
+ # task :t, [:a]
1750
+ # task :t, :a (deprecated)
1751
+ #
1752
+ def resolve_args_without_dependencies(args)
1753
+ task_name = args.shift
1754
+ if args.size == 1 && args.first.respond_to?(:to_ary)
1755
+ arg_names = args.first.to_ary
1756
+ else
1757
+ arg_names = args
1758
+ end
1759
+ [task_name, arg_names, []]
1760
+ end
1761
+ private :resolve_args_without_dependencies
1762
+
1763
+ # Resolve task arguments for a task or rule when there are
1764
+ # dependencies declared.
1765
+ #
1766
+ # The patterns recognized by this argument resolving function are:
1767
+ #
1768
+ # task :t => [:d]
1769
+ # task :t, [a] => [:d]
1770
+ # task :t, :needs => [:d] (deprecated)
1771
+ # task :t, :a, :needs => [:d] (deprecated)
1772
+ #
1773
+ def resolve_args_with_dependencies(args, hash) # :nodoc:
1774
+ fail "Task Argument Error" if hash.size != 1
1775
+ key, value = hash.map { |k, v| [k,v] }.first
1776
+ if args.empty?
1777
+ task_name = key
1778
+ arg_names = []
1779
+ deps = value
1780
+ elsif key == :needs
1781
+ task_name = args.shift
1782
+ arg_names = args
1783
+ deps = value
1784
+ else
1785
+ task_name = args.shift
1786
+ arg_names = key
1787
+ deps = value
1788
+ end
1789
+ deps = [deps] unless deps.respond_to?(:to_ary)
1790
+ [task_name, arg_names, deps]
1791
+ end
1792
+ private :resolve_args_with_dependencies
1793
+
1794
+ # If a rule can be found that matches the task name, enhance the
1795
+ # task with the prerequisites and actions from the rule. Set the
1796
+ # source attribute of the task appropriately for the rule. Return
1797
+ # the enhanced task or nil of no rule was found.
1798
+ def enhance_with_matching_rule(task_name, level=0)
1799
+ fail Rake::RuleRecursionOverflowError,
1800
+ "Rule Recursion Too Deep" if level >= 16
1801
+ @rules.each do |pattern, extensions, block|
1802
+ if md = pattern.match(task_name)
1803
+ task = attempt_rule(task_name, extensions, block, level)
1804
+ return task if task
1805
+ end
1806
+ end
1807
+ nil
1808
+ rescue Rake::RuleRecursionOverflowError => ex
1809
+ ex.add_target(task_name)
1810
+ fail ex
1811
+ end
1812
+
1813
+ # List of all defined tasks in this application.
1814
+ def tasks
1815
+ @tasks.values.sort_by { |t| t.name }
1816
+ end
1817
+
1818
+ # List of all the tasks defined in the given scope (and its
1819
+ # sub-scopes).
1820
+ def tasks_in_scope(scope)
1821
+ prefix = scope.join(":")
1822
+ tasks.select { |t|
1823
+ /^#{prefix}:/ =~ t.name
1824
+ }
1825
+ end
1826
+
1827
+ # Clear all tasks in this application.
1828
+ def clear
1829
+ @tasks.clear
1830
+ @rules.clear
1831
+ end
1832
+
1833
+ # Lookup a task, using scope and the scope hints in the task name.
1834
+ # This method performs straight lookups without trying to
1835
+ # synthesize file tasks or rules. Special scope names (e.g. '^')
1836
+ # are recognized. If no scope argument is supplied, use the
1837
+ # current scope. Return nil if the task cannot be found.
1838
+ def lookup(task_name, initial_scope=nil)
1839
+ initial_scope ||= @scope
1840
+ task_name = task_name.to_s
1841
+ if task_name =~ /^rake:/
1842
+ scopes = []
1843
+ task_name = task_name.sub(/^rake:/, '')
1844
+ elsif task_name =~ /^(\^+)/
1845
+ scopes = initial_scope[0, initial_scope.size - $1.size]
1846
+ task_name = task_name.sub(/^(\^+)/, '')
1847
+ else
1848
+ scopes = initial_scope
1849
+ end
1850
+ lookup_in_scope(task_name, scopes)
1851
+ end
1852
+
1853
+ # Lookup the task name
1854
+ def lookup_in_scope(name, scope)
1855
+ n = scope.size
1856
+ while n >= 0
1857
+ tn = (scope[0,n] + [name]).join(':')
1858
+ task = @tasks[tn]
1859
+ return task if task
1860
+ n -= 1
1861
+ end
1862
+ nil
1863
+ end
1864
+ private :lookup_in_scope
1865
+
1866
+ # Return the list of scope names currently active in the task
1867
+ # manager.
1868
+ def current_scope
1869
+ @scope.dup
1870
+ end
1871
+
1872
+ # Evaluate the block in a nested namespace named +name+. Create
1873
+ # an anonymous namespace if +name+ is nil.
1874
+ def in_namespace(name)
1875
+ name ||= generate_name
1876
+ @scope.push(name)
1877
+ ns = NameSpace.new(self, @scope)
1878
+ yield(ns)
1879
+ ns
1880
+ ensure
1881
+ @scope.pop
1882
+ end
1883
+
1884
+ private
1885
+
1886
+ # Generate an anonymous namespace name.
1887
+ def generate_name
1888
+ @seed ||= 0
1889
+ @seed += 1
1890
+ "_anon_#{@seed}"
1891
+ end
1892
+
1893
+ def trace_rule(level, message)
1894
+ puts "#{" "*level}#{message}" if Rake.application.options.trace_rules
1895
+ end
1896
+
1897
+ # Attempt to create a rule given the list of prerequisites.
1898
+ def attempt_rule(task_name, extensions, block, level)
1899
+ sources = make_sources(task_name, extensions)
1900
+ prereqs = sources.collect { |source|
1901
+ trace_rule level, "Attempting Rule #{task_name} => #{source}"
1902
+ if File.exist?(source) || Rake::Task.task_defined?(source)
1903
+ trace_rule level, "(#{task_name} => #{source} ... EXIST)"
1904
+ source
1905
+ elsif parent = enhance_with_matching_rule(source, level+1)
1906
+ trace_rule level, "(#{task_name} => #{source} ... ENHANCE)"
1907
+ parent.name
1908
+ else
1909
+ trace_rule level, "(#{task_name} => #{source} ... FAIL)"
1910
+ return nil
1911
+ end
1912
+ }
1913
+ task = FileTask.define_task({task_name => prereqs}, &block)
1914
+ task.sources = prereqs
1915
+ task
1916
+ end
1917
+
1918
+ # Make a list of sources from the list of file name extensions /
1919
+ # translation procs.
1920
+ def make_sources(task_name, extensions)
1921
+ extensions.collect { |ext|
1922
+ case ext
1923
+ when /%/
1924
+ task_name.pathmap(ext)
1925
+ when %r{/}
1926
+ ext
1927
+ when /^\./
1928
+ task_name.ext(ext)
1929
+ when String
1930
+ ext
1931
+ when Proc
1932
+ if ext.arity == 1
1933
+ ext.call(task_name)
1934
+ else
1935
+ ext.call
1936
+ end
1937
+ else
1938
+ fail "Don't know how to handle rule dependent: #{ext.inspect}"
1939
+ end
1940
+ }.flatten
1941
+ end
1942
+
1943
+ end # TaskManager
1944
+
1945
+ ######################################################################
1946
+ # Rake main application object. When invoking +rake+ from the
1947
+ # command line, a Rake::Application object is created and run.
1948
+ #
1949
+ class Application
1950
+ include TaskManager
1951
+
1952
+ # The name of the application (typically 'rake')
1953
+ attr_reader :name
1954
+
1955
+ # The original directory where rake was invoked.
1956
+ attr_reader :original_dir
1957
+
1958
+ # Name of the actual rakefile used.
1959
+ attr_reader :rakefile
1960
+
1961
+ # List of the top level task names (task names from the command line).
1962
+ attr_reader :top_level_tasks
1963
+
1964
+ DEFAULT_RAKEFILES = ['rakefile', 'Rakefile', 'rakefile.rb', 'Rakefile.rb'].freeze
1965
+
1966
+ # Initialize a Rake::Application object.
1967
+ def initialize
1968
+ super
1969
+ @name = 'rake'
1970
+ @rakefiles = DEFAULT_RAKEFILES.dup
1971
+ @rakefile = nil
1972
+ @pending_imports = []
1973
+ @imported = []
1974
+ @loaders = {}
1975
+ @default_loader = Rake::DefaultLoader.new
1976
+ @original_dir = Dir.pwd
1977
+ @top_level_tasks = []
1978
+ add_loader('rb', DefaultLoader.new)
1979
+ add_loader('rf', DefaultLoader.new)
1980
+ add_loader('rake', DefaultLoader.new)
1981
+ @tty_output = STDOUT.tty?
1982
+ end
1983
+
1984
+ # Run the Rake application. The run method performs the following three steps:
1985
+ #
1986
+ # * Initialize the command line options (+init+).
1987
+ # * Define the tasks (+load_rakefile+).
1988
+ # * Run the top level tasks (+run_tasks+).
1989
+ #
1990
+ # If you wish to build a custom rake command, you should call +init+ on your
1991
+ # application. The define any tasks. Finally, call +top_level+ to run your top
1992
+ # level tasks.
1993
+ def run
1994
+ standard_exception_handling do
1995
+ init
1996
+ load_rakefile
1997
+ top_level
1998
+ end
1999
+ end
2000
+
2001
+ # Initialize the command line parameters and app name.
2002
+ def init(app_name='rake')
2003
+ standard_exception_handling do
2004
+ @name = app_name
2005
+ handle_options
2006
+ collect_tasks
2007
+ end
2008
+ end
2009
+
2010
+ # Find the rakefile and then load it and any pending imports.
2011
+ def load_rakefile
2012
+ standard_exception_handling do
2013
+ raw_load_rakefile
2014
+ end
2015
+ end
2016
+
2017
+ # Run the top level tasks of a Rake application.
2018
+ def top_level
2019
+ standard_exception_handling do
2020
+ if options.show_tasks
2021
+ display_tasks_and_comments
2022
+ elsif options.show_prereqs
2023
+ display_prerequisites
2024
+ else
2025
+ top_level_tasks.each { |task_name| invoke_task(task_name) }
2026
+ end
2027
+ end
2028
+ end
2029
+
2030
+ # Add a loader to handle imported files ending in the extension
2031
+ # +ext+.
2032
+ def add_loader(ext, loader)
2033
+ ext = ".#{ext}" unless ext =~ /^\./
2034
+ @loaders[ext] = loader
2035
+ end
2036
+
2037
+ # Application options from the command line
2038
+ def options
2039
+ @options ||= OpenStruct.new
2040
+ end
2041
+
2042
+ # private ----------------------------------------------------------------
2043
+
2044
+ def invoke_task(task_string)
2045
+ name, args = parse_task_string(task_string)
2046
+ t = self[name]
2047
+ t.invoke(*args)
2048
+ end
2049
+
2050
+ def parse_task_string(string)
2051
+ if string =~ /^([^\[]+)(\[(.*)\])$/
2052
+ name = $1
2053
+ args = $3.split(/\s*,\s*/)
2054
+ else
2055
+ name = string
2056
+ args = []
2057
+ end
2058
+ [name, args]
2059
+ end
2060
+
2061
+ # Provide standard execption handling for the given block.
2062
+ def standard_exception_handling
2063
+ begin
2064
+ yield
2065
+ rescue SystemExit => ex
2066
+ # Exit silently with current status
2067
+ raise
2068
+ rescue OptionParser::InvalidOption => ex
2069
+ # Exit silently
2070
+ exit(false)
2071
+ rescue Exception => ex
2072
+ # Exit with error message
2073
+ $stderr.puts "#{name} aborted!"
2074
+ $stderr.puts ex.message
2075
+ if options.trace
2076
+ $stderr.puts ex.backtrace.join("\n")
2077
+ else
2078
+ $stderr.puts ex.backtrace.find {|str| str =~ /#{@rakefile}/ } || ""
2079
+ $stderr.puts "(See full trace by running task with --trace)"
2080
+ end
2081
+ exit(false)
2082
+ end
2083
+ end
2084
+
2085
+ # True if one of the files in RAKEFILES is in the current directory.
2086
+ # If a match is found, it is copied into @rakefile.
2087
+ def have_rakefile
2088
+ @rakefiles.each do |fn|
2089
+ if File.exist?(fn)
2090
+ others = Dir.glob(fn, File::FNM_CASEFOLD)
2091
+ return others.size == 1 ? others.first : fn
2092
+ elsif fn == ''
2093
+ return fn
2094
+ end
2095
+ end
2096
+ return nil
2097
+ end
2098
+
2099
+ # True if we are outputting to TTY, false otherwise
2100
+ def tty_output?
2101
+ @tty_output
2102
+ end
2103
+
2104
+ # Override the detected TTY output state (mostly for testing)
2105
+ def tty_output=( tty_output_state )
2106
+ @tty_output = tty_output_state
2107
+ end
2108
+
2109
+ # We will truncate output if we are outputting to a TTY or if we've been
2110
+ # given an explicit column width to honor
2111
+ def truncate_output?
2112
+ tty_output? || ENV['RAKE_COLUMNS']
2113
+ end
2114
+
2115
+ # Display the tasks and comments.
2116
+ def display_tasks_and_comments
2117
+ displayable_tasks = tasks.select { |t|
2118
+ t.comment && t.name =~ options.show_task_pattern
2119
+ }
2120
+ if options.full_description
2121
+ displayable_tasks.each do |t|
2122
+ puts "#{name} #{t.name_with_args}"
2123
+ t.full_comment.split("\n").each do |line|
2124
+ puts " #{line}"
2125
+ end
2126
+ puts
2127
+ end
2128
+ else
2129
+ width = displayable_tasks.collect { |t| t.name_with_args.length }.max || 10
2130
+ max_column = truncate_output? ? terminal_width - name.size - width - 7 : nil
2131
+ displayable_tasks.each do |t|
2132
+ printf "#{name} %-#{width}s # %s\n",
2133
+ t.name_with_args, max_column ? truncate(t.comment, max_column) : t.comment
2134
+ end
2135
+ end
2136
+ end
2137
+
2138
+ def terminal_width
2139
+ if ENV['RAKE_COLUMNS']
2140
+ result = ENV['RAKE_COLUMNS'].to_i
2141
+ else
2142
+ result = unix? ? dynamic_width : 80
2143
+ end
2144
+ (result < 10) ? 80 : result
2145
+ rescue
2146
+ 80
2147
+ end
2148
+
2149
+ # Calculate the dynamic width of the
2150
+ def dynamic_width
2151
+ @dynamic_width ||= (dynamic_width_stty.nonzero? || dynamic_width_tput)
2152
+ end
2153
+
2154
+ def dynamic_width_stty
2155
+ %x{stty size 2>/dev/null}.split[1].to_i
2156
+ end
2157
+
2158
+ def dynamic_width_tput
2159
+ %x{tput cols 2>/dev/null}.to_i
2160
+ end
2161
+
2162
+ def unix?
2163
+ RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i
2164
+ end
2165
+
2166
+ def windows?
2167
+ Win32.windows?
2168
+ end
2169
+
2170
+ def truncate(string, width)
2171
+ if string.length <= width
2172
+ string
2173
+ else
2174
+ ( string[0, width-3] || "" ) + "..."
2175
+ end
2176
+ end
2177
+
2178
+ # Display the tasks and prerequisites
2179
+ def display_prerequisites
2180
+ tasks.each do |t|
2181
+ puts "#{name} #{t.name}"
2182
+ t.prerequisites.each { |pre| puts " #{pre}" }
2183
+ end
2184
+ end
2185
+
2186
+ # A list of all the standard options used in rake, suitable for
2187
+ # passing to OptionParser.
2188
+ def standard_rake_options
2189
+ [
2190
+ ['--classic-namespace', '-C', "Put Task and FileTask in the top level namespace",
2191
+ lambda { |value|
2192
+ require 'rake/classic_namespace'
2193
+ options.classic_namespace = true
2194
+ }
2195
+ ],
2196
+ ['--describe', '-D [PATTERN]', "Describe the tasks (matching optional PATTERN), then exit.",
2197
+ lambda { |value|
2198
+ options.show_tasks = true
2199
+ options.full_description = true
2200
+ options.show_task_pattern = Regexp.new(value || '')
2201
+ }
2202
+ ],
2203
+ ['--dry-run', '-n', "Do a dry run without executing actions.",
2204
+ lambda { |value|
2205
+ verbose(true)
2206
+ nowrite(true)
2207
+ options.dryrun = true
2208
+ options.trace = true
2209
+ }
2210
+ ],
2211
+ ['--execute', '-e CODE', "Execute some Ruby code and exit.",
2212
+ lambda { |value|
2213
+ eval(value)
2214
+ exit
2215
+ }
2216
+ ],
2217
+ ['--execute-print', '-p CODE', "Execute some Ruby code, print the result, then exit.",
2218
+ lambda { |value|
2219
+ puts eval(value)
2220
+ exit
2221
+ }
2222
+ ],
2223
+ ['--execute-continue', '-E CODE',
2224
+ "Execute some Ruby code, then continue with normal task processing.",
2225
+ lambda { |value| eval(value) }
2226
+ ],
2227
+ ['--libdir', '-I LIBDIR', "Include LIBDIR in the search path for required modules.",
2228
+ lambda { |value| $:.push(value) }
2229
+ ],
2230
+ ['--prereqs', '-P', "Display the tasks and dependencies, then exit.",
2231
+ lambda { |value| options.show_prereqs = true }
2232
+ ],
2233
+ ['--quiet', '-q', "Do not log messages to standard output.",
2234
+ lambda { |value| verbose(false) }
2235
+ ],
2236
+ ['--rakefile', '-f [FILE]', "Use FILE as the rakefile.",
2237
+ lambda { |value|
2238
+ value ||= ''
2239
+ @rakefiles.clear
2240
+ @rakefiles << value
2241
+ }
2242
+ ],
2243
+ ['--rakelibdir', '--rakelib', '-R RAKELIBDIR',
2244
+ "Auto-import any .rake files in RAKELIBDIR. (default is 'rakelib')",
2245
+ lambda { |value| options.rakelib = value.split(':') }
2246
+ ],
2247
+ ['--require', '-r MODULE', "Require MODULE before executing rakefile.",
2248
+ lambda { |value|
2249
+ begin
2250
+ require value
2251
+ rescue LoadError => ex
2252
+ begin
2253
+ rake_require value
2254
+ rescue LoadError => ex2
2255
+ raise ex
2256
+ end
2257
+ end
2258
+ }
2259
+ ],
2260
+ ['--rules', "Trace the rules resolution.",
2261
+ lambda { |value| options.trace_rules = true }
2262
+ ],
2263
+ ['--no-search', '--nosearch', '-N', "Do not search parent directories for the Rakefile.",
2264
+ lambda { |value| options.nosearch = true }
2265
+ ],
2266
+ ['--silent', '-s', "Like --quiet, but also suppresses the 'in directory' announcement.",
2267
+ lambda { |value|
2268
+ verbose(false)
2269
+ options.silent = true
2270
+ }
2271
+ ],
2272
+ ['--system', '-g',
2273
+ "Using system wide (global) rakefiles (usually '~/.rake/*.rake').",
2274
+ lambda { |value| options.load_system = true }
2275
+ ],
2276
+ ['--no-system', '--nosystem', '-G',
2277
+ "Use standard project Rakefile search paths, ignore system wide rakefiles.",
2278
+ lambda { |value| options.ignore_system = true }
2279
+ ],
2280
+ ['--tasks', '-T [PATTERN]', "Display the tasks (matching optional PATTERN) with descriptions, then exit.",
2281
+ lambda { |value|
2282
+ options.show_tasks = true
2283
+ options.show_task_pattern = Regexp.new(value || '')
2284
+ options.full_description = false
2285
+ }
2286
+ ],
2287
+ ['--trace', '-t', "Turn on invoke/execute tracing, enable full backtrace.",
2288
+ lambda { |value|
2289
+ options.trace = true
2290
+ verbose(true)
2291
+ }
2292
+ ],
2293
+ ['--verbose', '-v', "Log message to standard output.",
2294
+ lambda { |value| verbose(true) }
2295
+ ],
2296
+ ['--version', '-V', "Display the program version.",
2297
+ lambda { |value|
2298
+ puts "rake, version #{RAKEVERSION}"
2299
+ exit
2300
+ }
2301
+ ]
2302
+ ]
2303
+ end
2304
+
2305
+ # Read and handle the command line options.
2306
+ def handle_options
2307
+ options.rakelib = ['rakelib']
2308
+
2309
+ OptionParser.new do |opts|
2310
+ opts.banner = "rake [-f rakefile] {options} targets..."
2311
+ opts.separator ""
2312
+ opts.separator "Options are ..."
2313
+
2314
+ opts.on_tail("-h", "--help", "-H", "Display this help message.") do
2315
+ puts opts
2316
+ exit
2317
+ end
2318
+
2319
+ standard_rake_options.each { |args| opts.on(*args) }
2320
+ end.parse!
2321
+
2322
+ # If class namespaces are requested, set the global options
2323
+ # according to the values in the options structure.
2324
+ if options.classic_namespace
2325
+ $show_tasks = options.show_tasks
2326
+ $show_prereqs = options.show_prereqs
2327
+ $trace = options.trace
2328
+ $dryrun = options.dryrun
2329
+ $silent = options.silent
2330
+ end
2331
+ end
2332
+
2333
+ # Similar to the regular Ruby +require+ command, but will check
2334
+ # for *.rake files in addition to *.rb files.
2335
+ def rake_require(file_name, paths=$LOAD_PATH, loaded=$")
2336
+ return false if loaded.include?(file_name)
2337
+ paths.each do |path|
2338
+ fn = file_name + ".rake"
2339
+ full_path = File.join(path, fn)
2340
+ if File.exist?(full_path)
2341
+ load full_path
2342
+ loaded << fn
2343
+ return true
2344
+ end
2345
+ end
2346
+ fail LoadError, "Can't find #{file_name}"
2347
+ end
2348
+
2349
+ def find_rakefile_location
2350
+ here = Dir.pwd
2351
+ while ! (fn = have_rakefile)
2352
+ Dir.chdir("..")
2353
+ if Dir.pwd == here || options.nosearch
2354
+ return nil
2355
+ end
2356
+ here = Dir.pwd
2357
+ end
2358
+ [fn, here]
2359
+ ensure
2360
+ Dir.chdir(Rake.original_dir)
2361
+ end
2362
+
2363
+ def raw_load_rakefile # :nodoc:
2364
+ rakefile, location = find_rakefile_location
2365
+ if (! options.ignore_system) &&
2366
+ (options.load_system || rakefile.nil?) &&
2367
+ system_dir && File.directory?(system_dir)
2368
+ puts "(in #{Dir.pwd})" unless options.silent
2369
+ glob("#{system_dir}/*.rake") do |name|
2370
+ add_import name
2371
+ end
2372
+ else
2373
+ fail "No Rakefile found (looking for: #{@rakefiles.join(', ')})" if
2374
+ rakefile.nil?
2375
+ @rakefile = rakefile
2376
+ Dir.chdir(location)
2377
+ puts "(in #{Dir.pwd})" unless options.silent
2378
+ $rakefile = @rakefile if options.classic_namespace
2379
+ load File.expand_path(@rakefile) if @rakefile && @rakefile != ''
2380
+ options.rakelib.each do |rlib|
2381
+ glob("#{rlib}/*.rake") do |name|
2382
+ add_import name
2383
+ end
2384
+ end
2385
+ end
2386
+ load_imports
2387
+ end
2388
+
2389
+ def glob(path, &block)
2390
+ Dir[path.gsub("\\", '/')].each(&block)
2391
+ end
2392
+ private :glob
2393
+
2394
+ # The directory path containing the system wide rakefiles.
2395
+ def system_dir
2396
+ @system_dir ||=
2397
+ begin
2398
+ if ENV['RAKE_SYSTEM']
2399
+ ENV['RAKE_SYSTEM']
2400
+ else
2401
+ standard_system_dir
2402
+ end
2403
+ end
2404
+ end
2405
+
2406
+ # The standard directory containing system wide rake files.
2407
+ if Win32.windows?
2408
+ def standard_system_dir #:nodoc:
2409
+ Win32.win32_system_dir
2410
+ end
2411
+ else
2412
+ def standard_system_dir #:nodoc:
2413
+ File.join(File.expand_path('~'), '.rake')
2414
+ end
2415
+ end
2416
+ private :standard_system_dir
2417
+
2418
+ # Collect the list of tasks on the command line. If no tasks are
2419
+ # given, return a list containing only the default task.
2420
+ # Environmental assignments are processed at this time as well.
2421
+ def collect_tasks
2422
+ @top_level_tasks = []
2423
+ ARGV.each do |arg|
2424
+ if arg =~ /^(\w+)=(.*)$/
2425
+ ENV[$1] = $2
2426
+ else
2427
+ @top_level_tasks << arg unless arg =~ /^-/
2428
+ end
2429
+ end
2430
+ @top_level_tasks.push("default") if @top_level_tasks.size == 0
2431
+ end
2432
+
2433
+ # Add a file to the list of files to be imported.
2434
+ def add_import(fn)
2435
+ @pending_imports << fn
2436
+ end
2437
+
2438
+ # Load the pending list of imported files.
2439
+ def load_imports
2440
+ while fn = @pending_imports.shift
2441
+ next if @imported.member?(fn)
2442
+ if fn_task = lookup(fn)
2443
+ fn_task.invoke
2444
+ end
2445
+ ext = File.extname(fn)
2446
+ loader = @loaders[ext] || @default_loader
2447
+ loader.load(fn)
2448
+ @imported << fn
2449
+ end
2450
+ end
2451
+
2452
+ # Warn about deprecated use of top level constant names.
2453
+ def const_warning(const_name)
2454
+ @const_warning ||= false
2455
+ if ! @const_warning
2456
+ $stderr.puts %{WARNING: Deprecated reference to top-level constant '#{const_name}' } +
2457
+ %{found at: #{rakefile_location}} # '
2458
+ $stderr.puts %{ Use --classic-namespace on rake command}
2459
+ $stderr.puts %{ or 'require "rake/classic_namespace"' in Rakefile}
2460
+ end
2461
+ @const_warning = true
2462
+ end
2463
+
2464
+ def rakefile_location
2465
+ begin
2466
+ fail
2467
+ rescue RuntimeError => ex
2468
+ ex.backtrace.find {|str| str =~ /#{@rakefile}/ } || ""
2469
+ end
2470
+ end
2471
+ end
2472
+ end
2473
+
2474
+
2475
+ class Module
2476
+ # Rename the original handler to make it available.
2477
+ alias :rake_original_const_missing :const_missing
2478
+
2479
+ # Check for deprecated uses of top level (i.e. in Object) uses of
2480
+ # Rake class names. If someone tries to reference the constant
2481
+ # name, display a warning and return the proper object. Using the
2482
+ # --classic-namespace command line option will define these
2483
+ # constants in Object and avoid this handler.
2484
+ def const_missing(const_name)
2485
+ case const_name
2486
+ when :Task
2487
+ Rake.application.const_warning(const_name)
2488
+ Rake::Task
2489
+ when :FileTask
2490
+ Rake.application.const_warning(const_name)
2491
+ Rake::FileTask
2492
+ when :FileCreationTask
2493
+ Rake.application.const_warning(const_name)
2494
+ Rake::FileCreationTask
2495
+ when :RakeApp
2496
+ Rake.application.const_warning(const_name)
2497
+ Rake::Application
2498
+ else
2499
+ rake_original_const_missing(const_name)
2500
+ end
2501
+ end
2502
+ end