trema 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (560) hide show
  1. data/.mono.rant +4107 -0
  2. data/.rspec +1 -0
  3. data/.yardopts +4 -0
  4. data/Doxyfile +1679 -0
  5. data/GPL2 +339 -0
  6. data/Gemfile +22 -0
  7. data/Gemfile.lock +71 -0
  8. data/README.md +135 -0
  9. data/Rakefile +140 -0
  10. data/Rantfile +834 -0
  11. data/VERSION +1 -0
  12. data/build.rb +32 -0
  13. data/cruise.rb +389 -0
  14. data/features/example.dumper.feature +87 -0
  15. data/features/example.learning_switch.feature +39 -0
  16. data/features/example.list_switches.feature +38 -0
  17. data/features/example.message.echo_reply.feature +26 -0
  18. data/features/example.message.echo_request.feature +25 -0
  19. data/features/example.message.features_request.feature +84 -0
  20. data/features/example.message.hello.feature +25 -0
  21. data/features/example.message.set_config.feature +27 -0
  22. data/features/example.multi_learning_switch.feature +135 -0
  23. data/features/example.packetin_filter_config.feature +91 -0
  24. data/features/example.repeater_hub.feature +49 -0
  25. data/features/example.switch_monitor.feature +39 -0
  26. data/features/packetin_filter.feature +49 -0
  27. data/features/step_definitions/kill_steps.rb +30 -0
  28. data/features/step_definitions/log_steps.rb +90 -0
  29. data/features/step_definitions/misc_steps.rb +64 -0
  30. data/features/step_definitions/off_steps.rb +30 -0
  31. data/features/step_definitions/run_steps.rb +91 -0
  32. data/features/step_definitions/send_packets_steps.rb +42 -0
  33. data/features/step_definitions/show_stats_steps.rb +43 -0
  34. data/features/step_definitions/stats_steps.rb +39 -0
  35. data/features/support/env.rb +75 -0
  36. data/features/support/hooks.rb +8 -0
  37. data/features/switch_manager.feature +35 -0
  38. data/features/trema-config.feature +68 -0
  39. data/features/trema.dump_flows.feature +25 -0
  40. data/features/trema.feature +25 -0
  41. data/features/trema.kill.feature +53 -0
  42. data/features/trema.killall.feature +30 -0
  43. data/features/trema.reset_stats.feature +14 -0
  44. data/features/trema.run.feature +46 -0
  45. data/features/trema.send_packets.feature +56 -0
  46. data/features/trema.show_stats.feature +67 -0
  47. data/features/tutorial.hello_trema.feature +27 -0
  48. data/features/tutorial.packet_in.feature +47 -0
  49. data/features/tutorial.switch_info.feature +55 -0
  50. data/ruby/.gitignore +4 -0
  51. data/ruby/blocker.rb +78 -0
  52. data/ruby/extconf.rb +71 -0
  53. data/ruby/sub-process.rb +291 -0
  54. data/ruby/trema/action-common.c +60 -0
  55. data/ruby/trema/action-common.h +42 -0
  56. data/ruby/trema/action-enqueue.c +161 -0
  57. data/ruby/trema/action-enqueue.h +40 -0
  58. data/ruby/trema/action-output.c +169 -0
  59. data/ruby/trema/action-output.h +42 -0
  60. data/ruby/trema/action-set-dl-dst.c +131 -0
  61. data/ruby/trema/action-set-dl-dst.h +44 -0
  62. data/ruby/trema/action-set-dl-src.c +131 -0
  63. data/ruby/trema/action-set-dl-src.h +44 -0
  64. data/ruby/trema/action-set-nw-dst.c +135 -0
  65. data/ruby/trema/action-set-nw-dst.h +42 -0
  66. data/ruby/trema/action-set-nw-src.c +140 -0
  67. data/ruby/trema/action-set-nw-src.h +42 -0
  68. data/ruby/trema/action-set-nw-tos.c +124 -0
  69. data/ruby/trema/action-set-nw-tos.h +42 -0
  70. data/ruby/trema/action-set-tp-dst.c +122 -0
  71. data/ruby/trema/action-set-tp-dst.h +42 -0
  72. data/ruby/trema/action-set-tp-src.c +124 -0
  73. data/ruby/trema/action-set-tp-src.h +42 -0
  74. data/ruby/trema/action-set-vlan-pcp.c +128 -0
  75. data/ruby/trema/action-set-vlan-pcp.h +42 -0
  76. data/ruby/trema/action-set-vlan-vid.c +125 -0
  77. data/ruby/trema/action-set-vlan-vid.h +42 -0
  78. data/ruby/trema/action-strip-vlan.c +81 -0
  79. data/ruby/trema/action-strip-vlan.h +42 -0
  80. data/ruby/trema/action-vendor.c +121 -0
  81. data/ruby/trema/action-vendor.h +42 -0
  82. data/ruby/trema/aggregate-stats-reply.rb +70 -0
  83. data/ruby/trema/app.rb +112 -0
  84. data/ruby/trema/barrier-reply.c +99 -0
  85. data/ruby/trema/barrier-reply.h +46 -0
  86. data/ruby/trema/barrier-request.c +108 -0
  87. data/ruby/trema/barrier-request.h +44 -0
  88. data/ruby/trema/cli.rb +269 -0
  89. data/ruby/trema/command.rb +40 -0
  90. data/ruby/trema/command/dump_flows.rb +62 -0
  91. data/ruby/trema/command/kill.rb +71 -0
  92. data/ruby/trema/command/killall.rb +56 -0
  93. data/ruby/trema/command/reset_stats.rb +61 -0
  94. data/ruby/trema/command/ruby.rb +55 -0
  95. data/ruby/trema/command/run.rb +120 -0
  96. data/ruby/trema/command/send_packets.rb +130 -0
  97. data/ruby/trema/command/shell.rb +61 -0
  98. data/ruby/trema/command/show_stats.rb +84 -0
  99. data/ruby/trema/command/usage.rb +61 -0
  100. data/ruby/trema/command/version.rb +39 -0
  101. data/ruby/trema/controller.c +595 -0
  102. data/ruby/trema/controller.h +44 -0
  103. data/ruby/trema/controller.rb +81 -0
  104. data/ruby/trema/daemon.rb +167 -0
  105. data/ruby/trema/dsl.rb +34 -0
  106. data/ruby/trema/dsl/configuration.rb +153 -0
  107. data/ruby/trema/dsl/context.rb +71 -0
  108. data/ruby/trema/dsl/link.rb +41 -0
  109. data/ruby/trema/dsl/parser.rb +70 -0
  110. data/ruby/trema/dsl/run.rb +49 -0
  111. data/ruby/trema/dsl/runner.rb +165 -0
  112. data/ruby/trema/dsl/stanza.rb +53 -0
  113. data/ruby/trema/dsl/switch.rb +78 -0
  114. data/ruby/trema/dsl/syntax-error.rb +33 -0
  115. data/ruby/trema/dsl/syntax.rb +109 -0
  116. data/ruby/trema/dsl/vhost.rb +108 -0
  117. data/ruby/trema/dsl/vswitch.rb +47 -0
  118. data/ruby/trema/echo-reply.c +107 -0
  119. data/ruby/trema/echo-reply.h +42 -0
  120. data/ruby/trema/echo-request.c +140 -0
  121. data/ruby/trema/echo-request.h +42 -0
  122. data/ruby/trema/error.c +253 -0
  123. data/ruby/trema/error.h +44 -0
  124. data/ruby/trema/exact-match.rb +36 -0
  125. data/ruby/trema/executables.rb +95 -0
  126. data/ruby/trema/features-reply.c +238 -0
  127. data/ruby/trema/features-reply.h +60 -0
  128. data/ruby/trema/features-request.c +109 -0
  129. data/ruby/trema/features-request.h +44 -0
  130. data/ruby/trema/flow-removed.c +275 -0
  131. data/ruby/trema/flow-removed.h +46 -0
  132. data/ruby/trema/flow-stats-reply.rb +109 -0
  133. data/ruby/trema/flow.rb +56 -0
  134. data/ruby/trema/get-config-reply.c +159 -0
  135. data/ruby/trema/get-config-reply.h +52 -0
  136. data/ruby/trema/get-config-request.c +107 -0
  137. data/ruby/trema/get-config-request.h +44 -0
  138. data/ruby/trema/hello.c +110 -0
  139. data/ruby/trema/hello.h +44 -0
  140. data/ruby/trema/host.rb +257 -0
  141. data/ruby/trema/ip.rb +101 -0
  142. data/ruby/trema/link.rb +176 -0
  143. data/ruby/trema/list-switches-reply.c +46 -0
  144. data/ruby/trema/list-switches-reply.h +40 -0
  145. data/ruby/trema/logger.c +162 -0
  146. data/ruby/trema/logger.h +44 -0
  147. data/ruby/trema/mac.rb +151 -0
  148. data/ruby/trema/match.c +594 -0
  149. data/ruby/trema/match.h +36 -0
  150. data/ruby/trema/monkey-patch/integer.rb +35 -0
  151. data/ruby/trema/monkey-patch/integer/base-conversions.rb +36 -0
  152. data/ruby/trema/monkey-patch/integer/ranges.rb +51 -0
  153. data/ruby/trema/monkey-patch/module.rb +33 -0
  154. data/ruby/trema/monkey-patch/module/deprecation.rb +41 -0
  155. data/ruby/trema/monkey-patch/string.rb +33 -0
  156. data/ruby/trema/monkey-patch/string/inflectors.rb +54 -0
  157. data/ruby/trema/network-component.rb +153 -0
  158. data/ruby/trema/ofctl.rb +62 -0
  159. data/ruby/trema/open-vswitch.rb +154 -0
  160. data/ruby/trema/openflow-error.c +191 -0
  161. data/ruby/trema/openflow-error.h +53 -0
  162. data/ruby/trema/openflow-switch.rb +88 -0
  163. data/ruby/trema/ordered-hash.rb +74 -0
  164. data/ruby/trema/packet-queue.rb +178 -0
  165. data/ruby/trema/packet_in.c +736 -0
  166. data/ruby/trema/packet_in.h +46 -0
  167. data/ruby/trema/packetin-filter.rb +126 -0
  168. data/ruby/trema/path.rb +135 -0
  169. data/ruby/trema/phost.rb +69 -0
  170. data/ruby/trema/port-mod.c +226 -0
  171. data/ruby/trema/port-mod.h +36 -0
  172. data/ruby/trema/port-stats-reply.rb +111 -0
  173. data/ruby/trema/port-status.c +156 -0
  174. data/ruby/trema/port-status.h +45 -0
  175. data/ruby/trema/port.c +295 -0
  176. data/ruby/trema/port.h +47 -0
  177. data/ruby/trema/process.rb +76 -0
  178. data/ruby/trema/queue-get-config-reply.c +200 -0
  179. data/ruby/trema/queue-get-config-reply.h +47 -0
  180. data/ruby/trema/queue-get-config-request.c +141 -0
  181. data/ruby/trema/queue-get-config-request.h +44 -0
  182. data/ruby/trema/queue-stats-reply.rb +78 -0
  183. data/ruby/trema/set-config.c +171 -0
  184. data/ruby/trema/set-config.h +44 -0
  185. data/ruby/trema/shell.rb +39 -0
  186. data/ruby/trema/shell/down.rb +39 -0
  187. data/ruby/trema/shell/killall.rb +40 -0
  188. data/ruby/trema/shell/link.rb +61 -0
  189. data/ruby/trema/shell/reset_stats.rb +50 -0
  190. data/ruby/trema/shell/run.rb +67 -0
  191. data/ruby/trema/shell/send_packets.rb +42 -0
  192. data/ruby/trema/shell/show_stats.rb +49 -0
  193. data/ruby/trema/shell/up.rb +43 -0
  194. data/ruby/trema/shell/vhost.rb +44 -0
  195. data/ruby/trema/shell/vswitch.rb +49 -0
  196. data/ruby/trema/stats-helper.rb +65 -0
  197. data/ruby/trema/stats-reply.c +483 -0
  198. data/ruby/trema/stats-reply.h +53 -0
  199. data/ruby/trema/stats-request.c +634 -0
  200. data/ruby/trema/stats-request.h +42 -0
  201. data/ruby/trema/switch-daemon.rb +74 -0
  202. data/ruby/trema/switch-disconnected.c +40 -0
  203. data/ruby/trema/switch-disconnected.h +38 -0
  204. data/ruby/trema/switch-manager.rb +121 -0
  205. data/ruby/trema/switch.rb +37 -0
  206. data/ruby/trema/table-stats-reply.rb +87 -0
  207. data/ruby/trema/timers.rb +97 -0
  208. data/ruby/trema/trema.c +122 -0
  209. data/ruby/trema/tremashark.rb +39 -0
  210. data/ruby/trema/util.rb +84 -0
  211. data/ruby/trema/vendor-request.c +193 -0
  212. data/ruby/trema/vendor-request.h +44 -0
  213. data/ruby/trema/vendor-stats-reply.rb +62 -0
  214. data/ruby/trema/vendor.c +152 -0
  215. data/ruby/trema/vendor.h +52 -0
  216. data/ruby/trema/version.rb +30 -0
  217. data/spec/spec_helper.rb +153 -0
  218. data/spec/support/openflow-message.rb +94 -0
  219. data/spec/trema/action-enqueue_spec.rb +100 -0
  220. data/spec/trema/action-output_spec.rb +116 -0
  221. data/spec/trema/action-set-dl-dst_spec.rb +95 -0
  222. data/spec/trema/action-set-dl-src_spec.rb +92 -0
  223. data/spec/trema/action-set-nw-dst_spec.rb +96 -0
  224. data/spec/trema/action-set-nw-src_spec.rb +97 -0
  225. data/spec/trema/action-set-nw-tos_spec.rb +88 -0
  226. data/spec/trema/action-set-tp-dst_spec.rb +88 -0
  227. data/spec/trema/action-set-tp-src_spec.rb +88 -0
  228. data/spec/trema/action-set-vlan-pcp_spec.rb +91 -0
  229. data/spec/trema/action-set-vlan-vid_spec.rb +91 -0
  230. data/spec/trema/action-strip-vlan_spec.rb +57 -0
  231. data/spec/trema/action-vendor_spec.rb +90 -0
  232. data/spec/trema/app_spec.rb +90 -0
  233. data/spec/trema/barrier-reply_spec.rb +45 -0
  234. data/spec/trema/barrier-request_spec.rb +83 -0
  235. data/spec/trema/cli_spec.rb +160 -0
  236. data/spec/trema/controller_spec.rb +100 -0
  237. data/spec/trema/dsl/configuration_spec.rb +122 -0
  238. data/spec/trema/dsl/link_spec.rb +54 -0
  239. data/spec/trema/dsl/run_spec.rb +78 -0
  240. data/spec/trema/dsl/runner_spec.rb +239 -0
  241. data/spec/trema/dsl/switch_spec.rb +77 -0
  242. data/spec/trema/dsl/syntax_spec.rb +121 -0
  243. data/spec/trema/dsl/vhost_spec.rb +148 -0
  244. data/spec/trema/dsl/vswitch_spec.rb +90 -0
  245. data/spec/trema/echo-reply_spec.rb +49 -0
  246. data/spec/trema/echo-request_spec.rb +75 -0
  247. data/spec/trema/error_spec.rb +142 -0
  248. data/spec/trema/executables_spec.rb +75 -0
  249. data/spec/trema/features-reply_spec.rb +57 -0
  250. data/spec/trema/features-request_spec.rb +66 -0
  251. data/spec/trema/flow-removed_spec.rb +146 -0
  252. data/spec/trema/get-config-reply_spec.rb +43 -0
  253. data/spec/trema/get-config-request_spec.rb +82 -0
  254. data/spec/trema/hello_spec.rb +49 -0
  255. data/spec/trema/host_spec.rb +193 -0
  256. data/spec/trema/link_spec.rb +64 -0
  257. data/spec/trema/list-switches-reply_spec.rb +48 -0
  258. data/spec/trema/logger_spec.rb +48 -0
  259. data/spec/trema/mac_spec.rb +115 -0
  260. data/spec/trema/match_spec.rb +113 -0
  261. data/spec/trema/open-vswitch_spec.rb +123 -0
  262. data/spec/trema/openflow-error_spec.rb +141 -0
  263. data/spec/trema/openflow-switch_spec.rb +56 -0
  264. data/spec/trema/packet-in_spec.rb +168 -0
  265. data/spec/trema/packet-out_spec.rb +128 -0
  266. data/spec/trema/packetin-filter_spec.rb +41 -0
  267. data/spec/trema/port-mod_spec.rb +101 -0
  268. data/spec/trema/port-status_spec.rb +108 -0
  269. data/spec/trema/port_spec.rb +61 -0
  270. data/spec/trema/process_spec.rb +71 -0
  271. data/spec/trema/queue-get-config-reply_spec.rb +66 -0
  272. data/spec/trema/queue-get-config-request_spec.rb +69 -0
  273. data/spec/trema/set-config_spec.rb +80 -0
  274. data/spec/trema/shell/vhost_spec.rb +57 -0
  275. data/spec/trema/shell/vswitch_spec.rb +89 -0
  276. data/spec/trema/stats-reply_spec.rb +306 -0
  277. data/spec/trema/stats-request_spec.rb +151 -0
  278. data/spec/trema/switch-disconnected_spec.rb +58 -0
  279. data/spec/trema/switch-manager_spec.rb +43 -0
  280. data/spec/trema/tremashark_spec.rb +41 -0
  281. data/spec/trema/util_spec.rb +93 -0
  282. data/spec/trema/vendor-request_spec.rb +79 -0
  283. data/src/examples/cbench_switch/README +21 -0
  284. data/src/examples/cbench_switch/cbench-switch.rb +39 -0
  285. data/src/examples/cbench_switch/cbench_switch.c +68 -0
  286. data/src/examples/dumper/dumper.c +370 -0
  287. data/src/examples/dumper/dumper.conf +7 -0
  288. data/src/examples/dumper/dumper.rb +196 -0
  289. data/src/examples/hello_trema/README +13 -0
  290. data/src/examples/hello_trema/hello_trema.c +51 -0
  291. data/src/examples/hello_trema/hello_trema.conf +3 -0
  292. data/src/examples/hello_trema/hello_trema.rb +35 -0
  293. data/src/examples/learning_switch/README +15 -0
  294. data/src/examples/learning_switch/fdb.rb +112 -0
  295. data/src/examples/learning_switch/learning-switch.rb +88 -0
  296. data/src/examples/learning_switch/learning_switch.c +236 -0
  297. data/src/examples/learning_switch/learning_switch.conf +18 -0
  298. data/src/examples/list_switches/README +19 -0
  299. data/src/examples/list_switches/list-switches.rb +45 -0
  300. data/src/examples/list_switches/list_switches.c +81 -0
  301. data/src/examples/list_switches/list_switches.conf +15 -0
  302. data/src/examples/match_compare/match-compare.conf +30 -0
  303. data/src/examples/match_compare/match-compare.rb +99 -0
  304. data/src/examples/multi_learning_switch/README +14 -0
  305. data/src/examples/multi_learning_switch/multi-learning-switch.rb +96 -0
  306. data/src/examples/multi_learning_switch/multi_learning_switch.c +296 -0
  307. data/src/examples/multi_learning_switch/multi_learning_switch.conf +17 -0
  308. data/src/examples/openflow_message/README +11 -0
  309. data/src/examples/openflow_message/echo-reply.rb +59 -0
  310. data/src/examples/openflow_message/echo-request.rb +58 -0
  311. data/src/examples/openflow_message/echo_reply.c +70 -0
  312. data/src/examples/openflow_message/echo_request.c +70 -0
  313. data/src/examples/openflow_message/example.rb +63 -0
  314. data/src/examples/openflow_message/features-request.rb +97 -0
  315. data/src/examples/openflow_message/features_request.c +168 -0
  316. data/src/examples/openflow_message/hello.c +70 -0
  317. data/src/examples/openflow_message/hello.rb +58 -0
  318. data/src/examples/openflow_message/set-config.rb +59 -0
  319. data/src/examples/openflow_message/set_config.c +70 -0
  320. data/src/examples/packet_in/README +15 -0
  321. data/src/examples/packet_in/packet_in.c +55 -0
  322. data/src/examples/packet_in/packet_in.conf +15 -0
  323. data/src/examples/packet_in/packet_in.rb +34 -0
  324. data/src/examples/packetin_filter_config/README +12 -0
  325. data/src/examples/packetin_filter_config/add_filter.c +73 -0
  326. data/src/examples/packetin_filter_config/delete_filter.c +65 -0
  327. data/src/examples/packetin_filter_config/delete_filter_strict.c +75 -0
  328. data/src/examples/packetin_filter_config/dump_filter.c +65 -0
  329. data/src/examples/packetin_filter_config/dump_filter_strict.c +75 -0
  330. data/src/examples/packetin_filter_config/packetin_filter_config.c +134 -0
  331. data/src/examples/packetin_filter_config/packetin_filter_config.conf +7 -0
  332. data/src/examples/packetin_filter_config/utils.c +102 -0
  333. data/src/examples/packetin_filter_config/utils.h +42 -0
  334. data/src/examples/repeater_hub/README +8 -0
  335. data/src/examples/repeater_hub/repeater-hub.rb +43 -0
  336. data/src/examples/repeater_hub/repeater-hub_spec.rb +156 -0
  337. data/src/examples/repeater_hub/repeater_hub.c +83 -0
  338. data/src/examples/repeater_hub/repeater_hub.conf +28 -0
  339. data/src/examples/switch_info/README +13 -0
  340. data/src/examples/switch_info/switch_info.c +80 -0
  341. data/src/examples/switch_info/switch_info.conf +3 -0
  342. data/src/examples/switch_info/switch_info.rb +46 -0
  343. data/src/examples/switch_monitor/switch-monitor.conf +3 -0
  344. data/src/examples/switch_monitor/switch-monitor.rb +58 -0
  345. data/src/examples/switch_monitor/switch_monitor.c +154 -0
  346. data/src/examples/traffic_monitor/counter.c +74 -0
  347. data/src/examples/traffic_monitor/counter.h +48 -0
  348. data/src/examples/traffic_monitor/counter.rb +46 -0
  349. data/src/examples/traffic_monitor/fdb.c +76 -0
  350. data/src/examples/traffic_monitor/fdb.h +50 -0
  351. data/src/examples/traffic_monitor/fdb.rb +44 -0
  352. data/src/examples/traffic_monitor/traffic-monitor.rb +100 -0
  353. data/src/examples/traffic_monitor/traffic_monitor.c +163 -0
  354. data/src/examples/traffic_monitor/traffic_monitor.conf +16 -0
  355. data/src/lib/arp.h +61 -0
  356. data/src/lib/bool.h +49 -0
  357. data/src/lib/buffer.c +305 -0
  358. data/src/lib/buffer.h +56 -0
  359. data/src/lib/byteorder.c +547 -0
  360. data/src/lib/byteorder.h +110 -0
  361. data/src/lib/checks.h +42 -0
  362. data/src/lib/daemon.c +302 -0
  363. data/src/lib/daemon.h +42 -0
  364. data/src/lib/doubly_linked_list.c +281 -0
  365. data/src/lib/doubly_linked_list.h +88 -0
  366. data/src/lib/ether.c +48 -0
  367. data/src/lib/ether.h +94 -0
  368. data/src/lib/etherip.h +46 -0
  369. data/src/lib/event_handler.c +389 -0
  370. data/src/lib/event_handler.h +64 -0
  371. data/src/lib/hash_table.c +417 -0
  372. data/src/lib/hash_table.h +138 -0
  373. data/src/lib/icmp.h +74 -0
  374. data/src/lib/igmp.h +50 -0
  375. data/src/lib/ipv4.h +50 -0
  376. data/src/lib/linked_list.c +199 -0
  377. data/src/lib/linked_list.h +84 -0
  378. data/src/lib/log.c +402 -0
  379. data/src/lib/log.h +78 -0
  380. data/src/lib/match.h +84 -0
  381. data/src/lib/match_table.c +608 -0
  382. data/src/lib/match_table.h +51 -0
  383. data/src/lib/message_queue.c +143 -0
  384. data/src/lib/message_queue.h +61 -0
  385. data/src/lib/messenger.c +1714 -0
  386. data/src/lib/messenger.h +145 -0
  387. data/src/lib/openflow_application_interface.c +1673 -0
  388. data/src/lib/openflow_application_interface.h +329 -0
  389. data/src/lib/openflow_message.c +4051 -0
  390. data/src/lib/openflow_message.h +288 -0
  391. data/src/lib/openflow_service_interface.h +59 -0
  392. data/src/lib/packet_info.c +230 -0
  393. data/src/lib/packet_info.h +209 -0
  394. data/src/lib/packet_parser.c +502 -0
  395. data/src/lib/packetin_filter_interface.c +294 -0
  396. data/src/lib/packetin_filter_interface.h +127 -0
  397. data/src/lib/persistent_storage.c +480 -0
  398. data/src/lib/persistent_storage.h +46 -0
  399. data/src/lib/stat.c +213 -0
  400. data/src/lib/stat.h +44 -0
  401. data/src/lib/tcp.h +67 -0
  402. data/src/lib/timer.c +350 -0
  403. data/src/lib/timer.h +53 -0
  404. data/src/lib/trema.c +710 -0
  405. data/src/lib/trema.h +79 -0
  406. data/src/lib/trema_private.c +177 -0
  407. data/src/lib/trema_private.h +60 -0
  408. data/src/lib/trema_wrapper.c +56 -0
  409. data/src/lib/trema_wrapper.h +64 -0
  410. data/src/lib/udp.h +43 -0
  411. data/src/lib/utility.c +515 -0
  412. data/src/lib/utility.h +67 -0
  413. data/src/lib/wrapper.c +100 -0
  414. data/src/lib/wrapper.h +76 -0
  415. data/src/packetin_filter/README +17 -0
  416. data/src/packetin_filter/packetin_filter.c +575 -0
  417. data/src/switch_manager/README +20 -0
  418. data/src/switch_manager/cookie_table.c +292 -0
  419. data/src/switch_manager/cookie_table.h +72 -0
  420. data/src/switch_manager/dpid_table.c +110 -0
  421. data/src/switch_manager/dpid_table.h +46 -0
  422. data/src/switch_manager/management_interface.h +44 -0
  423. data/src/switch_manager/ofpmsg_recv.c +482 -0
  424. data/src/switch_manager/ofpmsg_recv.h +42 -0
  425. data/src/switch_manager/ofpmsg_send.c +235 -0
  426. data/src/switch_manager/ofpmsg_send.h +50 -0
  427. data/src/switch_manager/secure_channel_listener.c +281 -0
  428. data/src/switch_manager/secure_channel_listener.h +42 -0
  429. data/src/switch_manager/secure_channel_receiver.c +126 -0
  430. data/src/switch_manager/secure_channel_receiver.h +43 -0
  431. data/src/switch_manager/secure_channel_sender.c +126 -0
  432. data/src/switch_manager/secure_channel_sender.h +43 -0
  433. data/src/switch_manager/service_interface.c +181 -0
  434. data/src/switch_manager/service_interface.h +46 -0
  435. data/src/switch_manager/switch.c +538 -0
  436. data/src/switch_manager/switch.h +51 -0
  437. data/src/switch_manager/switch_manager.c +448 -0
  438. data/src/switch_manager/switch_manager.h +63 -0
  439. data/src/switch_manager/switchinfo.h +72 -0
  440. data/src/switch_manager/xid_table.c +184 -0
  441. data/src/switch_manager/xid_table.h +56 -0
  442. data/src/tremashark/README +78 -0
  443. data/src/tremashark/packet_capture.c +357 -0
  444. data/src/tremashark/pcap_private.h +47 -0
  445. data/src/tremashark/pcap_queue.c +197 -0
  446. data/src/tremashark/pcap_queue.h +58 -0
  447. data/src/tremashark/plugin/.gitignore +6 -0
  448. data/src/tremashark/plugin/packet-trema/.gitignore +5 -0
  449. data/src/tremashark/plugin/packet-trema/Makefile +77 -0
  450. data/src/tremashark/plugin/packet-trema/Makefile.am +110 -0
  451. data/src/tremashark/plugin/packet-trema/Makefile.common +31 -0
  452. data/src/tremashark/plugin/packet-trema/moduleinfo.h +41 -0
  453. data/src/tremashark/plugin/packet-trema/packet-trema.c +1659 -0
  454. data/src/tremashark/plugin/packet-trema/plugin.c +31 -0
  455. data/src/tremashark/plugin/user_dlts +2 -0
  456. data/src/tremashark/queue.c +168 -0
  457. data/src/tremashark/queue.h +60 -0
  458. data/src/tremashark/stdin_relay.c +257 -0
  459. data/src/tremashark/syslog_relay.c +247 -0
  460. data/src/tremashark/tremashark.c +556 -0
  461. data/trema +93 -0
  462. data/trema-config +61 -0
  463. data/unittests/buffer_stubs.c +74 -0
  464. data/unittests/cmockery_trema.c +123 -0
  465. data/unittests/cmockery_trema.h +96 -0
  466. data/unittests/lib/buffer_test.c +370 -0
  467. data/unittests/lib/byteorder_test.c +1717 -0
  468. data/unittests/lib/daemon_test.c +664 -0
  469. data/unittests/lib/doubly_linked_list_test.c +346 -0
  470. data/unittests/lib/ether_test.c +127 -0
  471. data/unittests/lib/hash_table_test.c +278 -0
  472. data/unittests/lib/linked_list_test.c +343 -0
  473. data/unittests/lib/log_test.c +459 -0
  474. data/unittests/lib/match_table_test.c +1509 -0
  475. data/unittests/lib/message_queue_test.c +379 -0
  476. data/unittests/lib/messenger_test.c +438 -0
  477. data/unittests/lib/openflow_application_interface_test.c +3488 -0
  478. data/unittests/lib/openflow_message_test.c +7337 -0
  479. data/unittests/lib/packet_info_test.c +544 -0
  480. data/unittests/lib/packet_parser_test.c +703 -0
  481. data/unittests/lib/packetin_filter_interface_test.c +723 -0
  482. data/unittests/lib/persistent_storage_test.c +802 -0
  483. data/unittests/lib/stat_test.c +291 -0
  484. data/unittests/lib/test_packets/arp_rep.cap +0 -0
  485. data/unittests/lib/test_packets/arp_req.cap +0 -0
  486. data/unittests/lib/test_packets/icmp_echo_rep.cap +0 -0
  487. data/unittests/lib/test_packets/icmp_echo_req.cap +0 -0
  488. data/unittests/lib/test_packets/igmp_query_v2.cap +0 -0
  489. data/unittests/lib/test_packets/ipx.cap +0 -0
  490. data/unittests/lib/test_packets/lldp.cap +0 -0
  491. data/unittests/lib/test_packets/lldp_over_ip.cap +0 -0
  492. data/unittests/lib/test_packets/tcp.cap +0 -0
  493. data/unittests/lib/test_packets/tcp_syn.cap +0 -0
  494. data/unittests/lib/test_packets/udp.cap +0 -0
  495. data/unittests/lib/test_packets/udp_frag_head.cap +0 -0
  496. data/unittests/lib/test_packets/udp_frag_next.cap +0 -0
  497. data/unittests/lib/test_packets/vtag_icmp_echo_rep.cap +0 -0
  498. data/unittests/lib/test_packets/vtag_icmp_echo_req.cap +0 -0
  499. data/unittests/lib/timer_test.c +248 -0
  500. data/unittests/lib/trema_private_test.c +323 -0
  501. data/unittests/lib/trema_test.c +985 -0
  502. data/unittests/lib/utility_test.c +628 -0
  503. data/unittests/lib/wrapper_test.c +201 -0
  504. data/unittests/packetin_filter/packetin_filter_test.c +477 -0
  505. data/unittests/switch_manager/switch_manager_test.c +1178 -0
  506. data/unittests/wrapper_stubs.c +39 -0
  507. data/vendor/.gitignore +6 -0
  508. data/vendor/README +30 -0
  509. data/vendor/cmockery-20110428.tar.gz +0 -0
  510. data/vendor/oflops-0.03.tar.gz +0 -0
  511. data/vendor/oflops_no_snmp+1.0.0.patch +340 -0
  512. data/vendor/openflow-1.0.0.tar.gz +0 -0
  513. data/vendor/openflow.git.tar.gz +0 -0
  514. data/vendor/openvswitch-1.2.2.tar.gz +0 -0
  515. data/vendor/ruby-ifconfig-1.2/COPYING +340 -0
  516. data/vendor/ruby-ifconfig-1.2/Changelog +16 -0
  517. data/vendor/ruby-ifconfig-1.2/INSTALL +239 -0
  518. data/vendor/ruby-ifconfig-1.2/README +38 -0
  519. data/vendor/ruby-ifconfig-1.2/Rakefile +14 -0
  520. data/vendor/ruby-ifconfig-1.2/TODO +8 -0
  521. data/vendor/ruby-ifconfig-1.2/ifconfig_examples/darwin.txt +17 -0
  522. data/vendor/ruby-ifconfig-1.2/ifconfig_examples/dragonflybsd.txt +10 -0
  523. data/vendor/ruby-ifconfig-1.2/ifconfig_examples/dragonflybsd_netstat.txt +14 -0
  524. data/vendor/ruby-ifconfig-1.2/ifconfig_examples/freebsd.txt +17 -0
  525. data/vendor/ruby-ifconfig-1.2/ifconfig_examples/freebsd_netstat.txt +24 -0
  526. data/vendor/ruby-ifconfig-1.2/ifconfig_examples/linux.txt +60 -0
  527. data/vendor/ruby-ifconfig-1.2/ifconfig_examples/linux_ethernet.txt +20 -0
  528. data/vendor/ruby-ifconfig-1.2/ifconfig_examples/netbsd.txt +10 -0
  529. data/vendor/ruby-ifconfig-1.2/ifconfig_examples/openbsd.txt +36 -0
  530. data/vendor/ruby-ifconfig-1.2/ifconfig_examples/sunos.txt +10 -0
  531. data/vendor/ruby-ifconfig-1.2/lib/ifconfig.rb +71 -0
  532. data/vendor/ruby-ifconfig-1.2/lib/ifconfig/bsd/ifconfig.rb +72 -0
  533. data/vendor/ruby-ifconfig-1.2/lib/ifconfig/bsd/interface_types.rb +69 -0
  534. data/vendor/ruby-ifconfig-1.2/lib/ifconfig/bsd/network_types.rb +3 -0
  535. data/vendor/ruby-ifconfig-1.2/lib/ifconfig/common/ifconfig.rb +84 -0
  536. data/vendor/ruby-ifconfig-1.2/lib/ifconfig/common/interface_types.rb +130 -0
  537. data/vendor/ruby-ifconfig-1.2/lib/ifconfig/common/network_types.rb +49 -0
  538. data/vendor/ruby-ifconfig-1.2/lib/ifconfig/linux/ifconfig.rb +43 -0
  539. data/vendor/ruby-ifconfig-1.2/lib/ifconfig/linux/interface_types.rb +112 -0
  540. data/vendor/ruby-ifconfig-1.2/lib/ifconfig/linux/network_types.rb +55 -0
  541. data/vendor/ruby-ifconfig-1.2/lib/ifconfig/sunos/ifconfig.rb +38 -0
  542. data/vendor/ruby-ifconfig-1.2/lib/ifconfig/sunos/interface_types.rb +77 -0
  543. data/vendor/ruby-ifconfig-1.2/lib/ifconfig/sunos/network_types.rb +4 -0
  544. data/vendor/ruby-ifconfig-1.2/setup.rb +1306 -0
  545. data/vendor/ruby-ifconfig-1.2/test/test_bsd.rb +35 -0
  546. data/vendor/ruby-ifconfig-1.2/test/test_darwin.rb +33 -0
  547. data/vendor/ruby-ifconfig-1.2/test/test_dragonflybsd.rb +35 -0
  548. data/vendor/ruby-ifconfig-1.2/test/test_helper.rb +4 -0
  549. data/vendor/ruby-ifconfig-1.2/test/test_linux.rb +31 -0
  550. data/vendor/ruby-ifconfig-1.2/test/test_netbsd.rb +33 -0
  551. data/vendor/ruby-ifconfig-1.2/test/test_openbsd.rb +33 -0
  552. data/vendor/ruby-ifconfig-1.2/test/test_sunos.rb +35 -0
  553. data/vendor/ruby-ifconfig-1.2/test/unit/tc_darwin.rb +40 -0
  554. data/vendor/ruby-ifconfig-1.2/test/unit/tc_dragonflybsd.rb +39 -0
  555. data/vendor/ruby-ifconfig-1.2/test/unit/tc_freebsd.rb +40 -0
  556. data/vendor/ruby-ifconfig-1.2/test/unit/tc_linux.rb +49 -0
  557. data/vendor/ruby-ifconfig-1.2/test/unit/tc_netbsd.rb +39 -0
  558. data/vendor/ruby-ifconfig-1.2/test/unit/tc_openbsd.rb +39 -0
  559. data/vendor/ruby-ifconfig-1.2/test/unit/tc_sunos.rb +44 -0
  560. metadata +856 -0
@@ -0,0 +1,4107 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # .mono.rant - Monolithic rant script, autogenerated by rant-import 0.5.7.
4
+ #
5
+ # Copyright (C) 2005 Stefan Lang <langstefan@gmx.at>
6
+ #
7
+ # This program is free software.
8
+ # You can distribute/modify this program under the terms of
9
+ # the GNU LGPL, Lesser General Public License version 2.1.
10
+
11
+
12
+ require 'getoptlong'
13
+
14
+
15
+ require 'rbconfig'
16
+
17
+ unless Process::Status.method_defined?(:success?) # new in 1.8.2
18
+ class Process::Status
19
+ def success?; exitstatus == 0; end
20
+ end
21
+ end
22
+ unless Regexp.respond_to? :union # new in 1.8.1
23
+ def Regexp.union(*patterns)
24
+ return /(?!)/ if patterns.empty?
25
+ Regexp.new(patterns.join("|"))
26
+ end
27
+ end
28
+ if RUBY_VERSION < "1.8.2"
29
+ class Array
30
+ undef_method :flatten, :flatten!
31
+ def flatten
32
+ cp = self.dup
33
+ cp.flatten!
34
+ cp
35
+ end
36
+ def flatten!
37
+ res = []
38
+ flattened = false
39
+ self.each { |e|
40
+ if e.respond_to? :to_ary
41
+ res.concat(e.to_ary)
42
+ flattened = true
43
+ else
44
+ res << e
45
+ end
46
+ }
47
+ if flattened
48
+ replace(res)
49
+ flatten!
50
+ self
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ class String
57
+ def _rant_sub_ext(ext, new_ext = nil)
58
+ if new_ext
59
+ self.sub(/#{Regexp.escape ext}$/, new_ext)
60
+ else
61
+ self.sub(/(\.[^.]*$)|$/, ".#{ext}")
62
+ end
63
+ end
64
+ end
65
+
66
+ module Rant
67
+ VERSION = '0.5.7'
68
+
69
+ @__rant_no_value__ = Object.new.freeze
70
+ def self.__rant_no_value__
71
+ @__rant_no_value__
72
+ end
73
+
74
+ module Env
75
+ OS = ::Config::CONFIG['target']
76
+ RUBY = ::Config::CONFIG['ruby_install_name']
77
+ RUBY_BINDIR = ::Config::CONFIG['bindir']
78
+ RUBY_EXE = File.join(RUBY_BINDIR, RUBY + ::Config::CONFIG["EXEEXT"])
79
+
80
+ @@zip_bin = false
81
+ @@tar_bin = false
82
+
83
+ if OS =~ /mswin/i
84
+ def on_windows?; true; end
85
+ else
86
+ def on_windows?; false; end
87
+ end
88
+
89
+ def have_zip?
90
+ if @@zip_bin == false
91
+ @@zip_bin = find_bin "zip"
92
+ end
93
+ !@@zip_bin.nil?
94
+ end
95
+ def have_tar?
96
+ if @@tar_bin == false
97
+ @@tar_bin = find_bin "tar"
98
+ end
99
+ !@@tar_bin.nil?
100
+ end
101
+ def pathes
102
+ path = ENV[on_windows? ? "Path" : "PATH"]
103
+ return [] unless path
104
+ path.split(on_windows? ? ";" : ":")
105
+ end
106
+ def find_bin bin_name
107
+ if on_windows?
108
+ bin_name_exe = nil
109
+ if bin_name !~ /\.[^\.]{1,3}$/i
110
+ bin_name_exe = bin_name + ".exe"
111
+ end
112
+ pathes.each { |dir|
113
+ file = File.join(dir, bin_name)
114
+ return file if test(?f, file)
115
+ if bin_name_exe
116
+ file = File.join(dir, bin_name_exe)
117
+ return file if test(?f, file)
118
+ end
119
+ }
120
+ else
121
+ pathes.each { |dir|
122
+ file = File.join(dir, bin_name)
123
+ return file if test(?x, file)
124
+ }
125
+ end
126
+ nil
127
+ end
128
+ def shell_path path
129
+ if on_windows?
130
+ path = path.tr("/", "\\")
131
+ if path.include? ' '
132
+ '"' + path + '"'
133
+ else
134
+ path
135
+ end
136
+ else
137
+ if path.include? ' '
138
+ "'" + path + "'"
139
+ else
140
+ path
141
+ end
142
+ end
143
+ end
144
+ extend self
145
+ end # module Env
146
+
147
+ module Sys
148
+ def sp(arg)
149
+ if arg.respond_to? :to_ary
150
+ arg.to_ary.map{ |e| sp e }.join(' ')
151
+ else
152
+ _escaped_path arg
153
+ end
154
+ end
155
+ def escape(arg)
156
+ if arg.respond_to? :to_ary
157
+ arg.to_ary.map{ |e| escape e }.join(' ')
158
+ else
159
+ _escaped arg
160
+ end
161
+ end
162
+ if Env.on_windows?
163
+ def _escaped_path(path)
164
+ _escaped(path.to_s.tr("/", "\\"))
165
+ end
166
+ def _escaped(arg)
167
+ sarg = arg.to_s
168
+ return sarg unless sarg.include?(" ")
169
+ sarg << "\\" if sarg[-1].chr == "\\"
170
+ "\"#{sarg}\""
171
+ end
172
+ def regular_filename(fn)
173
+ fn.to_str.tr("\\", "/").gsub(%r{/{2,}}, "/")
174
+ end
175
+ else
176
+ def _escaped_path(path)
177
+ path.to_s.gsub(/(?=\s)/, "\\")
178
+ end
179
+ alias _escaped _escaped_path
180
+ def regular_filename(fn)
181
+ fn.to_str.gsub(%r{/{2,}}, "/")
182
+ end
183
+ end
184
+ private :_escaped_path
185
+ private :_escaped
186
+ def split_all(path)
187
+ names = regular_filename(path).split(%r{/})
188
+ names[0] = "/" if names[0] && names[0].empty?
189
+ names
190
+ end
191
+ extend self
192
+ end # module Sys
193
+
194
+
195
+ ROOT_RANTFILE = "root.rant"
196
+ SUB_RANTFILE = "sub.rant"
197
+ RANTFILES = [ "Rantfile", "rantfile", ROOT_RANTFILE ]
198
+
199
+ CODE_IMPORTS = []
200
+
201
+ class RantAbortException < StandardError
202
+ end
203
+
204
+ class RantDoneException < StandardError
205
+ end
206
+
207
+ class Error < StandardError
208
+ end
209
+
210
+ module Generators
211
+ end
212
+
213
+ module RantVar
214
+
215
+ class Error < Rant::Error
216
+ end
217
+
218
+ class ConstraintError < Error
219
+
220
+ attr_reader :constraint, :val
221
+
222
+ def initialize(constraint, val, msg = nil)
223
+ @msg = msg
224
+ @constraint = constraint
225
+ @val = val
226
+ end
227
+
228
+ def message
229
+ val_desc = @val.inspect
230
+ val_desc[7..-1] = "..." if val_desc.length > 10
231
+ "#{val_desc} doesn't match constraint: #@constraint"
232
+ end
233
+ end
234
+
235
+ class NotAConstraintFactoryError < Error
236
+ attr_reader :obj
237
+ def initialize(obj, msg = nil)
238
+ @msg = msg
239
+ @obj = obj
240
+ end
241
+ def message
242
+ obj_desc = @obj.inspect
243
+ obj_desc[7..-1] = "..." if obj_desc.length > 10
244
+ "#{obj_desc} is not a valid constraint factory"
245
+ end
246
+ end
247
+
248
+ class InvalidVidError < Error
249
+ def initialize(vid, msg = nil)
250
+ @msg = msg
251
+ @vid = vid
252
+ end
253
+ def message
254
+ vid_desc = @vid.inspect
255
+ vid_desc[7..-1] = "..." if vid_desc.length > 10
256
+ "#{vid_desc} is not a valid var identifier"
257
+ end
258
+ end
259
+
260
+ class InvalidConstraintError < Error
261
+ end
262
+
263
+ class QueryError < Error
264
+ end
265
+
266
+ class Space
267
+
268
+ @@env_ref = Object.new
269
+
270
+ def initialize
271
+ @store = {}
272
+ @constraints = {}
273
+ end
274
+
275
+ def query(*args, &block)
276
+ case args.size
277
+ when 0
278
+ raise QueryError, "no arguments", caller
279
+ when 1
280
+ arg = args.first
281
+ if Hash === arg
282
+ if arg.size == 1
283
+ arg.each { |k,v|
284
+ self[k] = v if self[k].nil?
285
+ }
286
+ self
287
+ else
288
+ init_all arg
289
+ end
290
+ else
291
+ self[arg]
292
+ end
293
+ when 2, 3
294
+ vid, cf, val = *args
295
+ constrain vid,
296
+ get_factory(cf).rant_constraint
297
+ self[vid] = val if val
298
+ else
299
+ raise QueryError, "too many arguments"
300
+ end
301
+ end
302
+
303
+ def restrict vid, ct, *ct_args
304
+ if vid.respond_to? :to_ary
305
+ vid.to_ary.each { |v| restrict(v, ct, *ct_args) }
306
+ else
307
+ constrain vid,
308
+ get_factory(ct).rant_constraint(*ct_args)
309
+ end
310
+ self
311
+ end
312
+
313
+ def get_factory id
314
+ if String === id || Symbol === id
315
+ id = Constraints.const_get(id) rescue nil
316
+ end
317
+ unless id.respond_to? :rant_constraint
318
+ raise NotAConstraintFactoryError.new(id), caller
319
+ end
320
+ id
321
+ end
322
+ private :get_factory
323
+
324
+ def [](vid)
325
+ vid = RantVar.valid_vid vid
326
+ val = @store[vid]
327
+ val.equal?(@@env_ref) ? ENV[vid] : val
328
+ end
329
+
330
+ def []=(vid, val)
331
+ vid = RantVar.valid_vid(vid)
332
+ c = @constraints[vid]
333
+ if @store[vid] == @@env_ref
334
+ ENV[vid] = c ? c.filter(val) : val
335
+ else
336
+ @store[vid] = c ? c.filter(val) : val
337
+ end
338
+ end
339
+
340
+ def env(*vars)
341
+ vars.flatten.each { |var|
342
+ vid = RantVar.valid_vid(var)
343
+ cur_val = @store[vid]
344
+ next if cur_val == @@env_ref
345
+ ENV[vid] = cur_val unless cur_val.nil?
346
+ @store[vid] = @@env_ref
347
+ }
348
+ nil
349
+ end
350
+
351
+ def set_all hash
352
+ unless Hash === hash
353
+ raise QueryError,
354
+ "set_all argument has to be a hash"
355
+ end
356
+ hash.each_pair { |k, v|
357
+ self[k] = v
358
+ }
359
+ end
360
+
361
+ def init_all hash
362
+ unless Hash === hash
363
+ raise QueryError,
364
+ "init_all argument has to be a hash"
365
+ end
366
+ hash.each_pair { |k, v|
367
+ self[k] = v if self[k].nil?
368
+ }
369
+ end
370
+
371
+ def constrain vid, constraint
372
+ vid = RantVar.valid_vid(vid)
373
+ unless RantVar.valid_constraint? constraint
374
+ raise InvalidConstraintError, constraint
375
+ end
376
+ @constraints[vid] = constraint
377
+ if @store.member? vid
378
+ begin
379
+ val = @store[vid]
380
+ @store[vid] = constraint.filter(@store[vid])
381
+ rescue
382
+ @store[vid] = constraint.default
383
+ raise ConstraintError.new(constraint, val)
384
+ end
385
+ else
386
+ @store[vid] = constraint.default
387
+ end
388
+ end
389
+
390
+ def has_var?(vid)
391
+ !self[vid].nil?
392
+ end
393
+
394
+ def _set(vid, val) #:nodoc:
395
+ @store[vid] = val
396
+ end
397
+
398
+ def _get(vid) #:nodoc:
399
+ @store[vid]
400
+ end
401
+
402
+ def _init(vid, val) #:nodoc:
403
+ @store[vid] ||= val
404
+ end
405
+
406
+ end # class Space
407
+
408
+ module Constraint
409
+ def matches? val
410
+ filter val
411
+ true
412
+ rescue
413
+ return false
414
+ end
415
+ end
416
+
417
+ def valid_vid(obj)
418
+ case obj
419
+ when String: obj
420
+ when Symbol: obj.to_s
421
+ else
422
+ if obj.respond_to? :to_str
423
+ obj.to_str
424
+ else
425
+ raise InvalidVidError.new(obj)
426
+ end
427
+ end
428
+ end
429
+
430
+ def valid_constraint?(obj)
431
+ obj.respond_to?(:filter) &&
432
+ obj.respond_to?(:matches?) &&
433
+ obj.respond_to?(:default)
434
+ end
435
+
436
+ module_function :valid_constraint?, :valid_vid
437
+
438
+ module Constraints
439
+ class AutoList
440
+ include Constraint
441
+ class << self
442
+ alias rant_constraint new
443
+ end
444
+ def filter(val)
445
+ if val.respond_to? :to_ary
446
+ val.to_ary
447
+ elsif val.nil?
448
+ raise ConstraintError.new(self, val)
449
+ else
450
+ [val]
451
+ end
452
+ end
453
+ def default
454
+ []
455
+ end
456
+ def to_s
457
+ "list or single, non-nil value"
458
+ end
459
+ end
460
+ end # module Constraints
461
+ end # module RantVar
462
+ end # module Rant
463
+
464
+
465
+ require 'fileutils'
466
+
467
+
468
+ module Rant
469
+ def FileList(arg)
470
+ if arg.respond_to?(:to_rant_filelist)
471
+ arg.to_rant_filelist
472
+ elsif arg.respond_to?(:to_ary)
473
+ FileList.new(arg.to_ary)
474
+ else
475
+ raise TypeError,
476
+ "cannot convert #{arg.class} into Rant::FileList"
477
+ end
478
+ end
479
+ module_function :FileList
480
+ class FileList
481
+ include Enumerable
482
+
483
+ ESC_SEPARATOR = Regexp.escape(File::SEPARATOR)
484
+ ESC_ALT_SEPARATOR = File::ALT_SEPARATOR ?
485
+ Regexp.escape(File::ALT_SEPARATOR) : nil
486
+
487
+ class << self
488
+ def [](*patterns)
489
+ new.hide_dotfiles.include(*patterns)
490
+ end
491
+ def glob(*patterns)
492
+ fl = new.hide_dotfiles.ignore(".", "..").include(*patterns)
493
+ if block_given? then yield fl else fl end
494
+ end
495
+ def glob_all(*patterns)
496
+ fl = new.ignore(".", "..").include(*patterns)
497
+ if block_given? then yield fl else fl end
498
+ end
499
+ end
500
+
501
+ def initialize(store = [])
502
+ @pending = false
503
+ @def_glob_dotfiles = true
504
+ @items = store
505
+ @ignore_rx = nil
506
+ @keep = {}
507
+ @actions = []
508
+ end
509
+ alias _object_dup dup
510
+ private :_object_dup
511
+ def dup
512
+ c = _object_dup
513
+ c.items = @items.dup
514
+ c.actions = @actions.dup
515
+ c.ignore_rx = @ignore_rx.dup if @ignore_rx
516
+ c.instance_variable_set(:@keep, @keep.dup)
517
+ c
518
+ end
519
+ def copy
520
+ c = _object_dup
521
+ c.items = @items.map { |entry| entry.dup }
522
+ c.actions = @actions.dup
523
+ c.ignore_rx = @ignore_rx.dup if @ignore_rx
524
+ h_keep = {}
525
+ @keep.each_key { |entry| h_keep[entry] = true }
526
+ c.instance_variable_set(:@keep, h_keep)
527
+ c
528
+ end
529
+ def glob_dotfiles?
530
+ @def_glob_dotfiles
531
+ end
532
+ def glob_dotfiles=(flag)
533
+ @def_glob_dotfiles = flag ? true : false
534
+ end
535
+ def hide_dotfiles
536
+ @def_glob_dotfiles = false
537
+ self
538
+ end
539
+ def glob_dotfiles
540
+ @def_glob_dotfiles = true
541
+ self
542
+ end
543
+
544
+ protected
545
+ attr_accessor :actions, :items
546
+ attr_accessor :pending
547
+ attr_accessor :ignore_rx
548
+
549
+ public
550
+ def each(&block)
551
+ resolve if @pending
552
+ @items.each(&block)
553
+ self
554
+ end
555
+ def to_ary
556
+ resolve if @pending
557
+ @items
558
+ end
559
+ alias to_a to_ary
560
+ alias entries to_ary # entries: defined in Enumerable
561
+ def to_rant_filelist
562
+ self
563
+ end
564
+ def +(other)
565
+ if other.respond_to? :to_rant_filelist
566
+ c = other.to_rant_filelist.dup
567
+ c.actions.concat(@actions)
568
+ c.items.concat(@items)
569
+ c.pending = !c.actions.empty?
570
+ c
571
+ elsif other.respond_to? :to_ary
572
+ c = dup
573
+ c.actions <<
574
+ [:apply_ary_method_1, :concat, other.to_ary.dup]
575
+ c.pending = true
576
+ c
577
+ else
578
+ raise TypeError,
579
+ "cannot add #{other.class} to Rant::FileList"
580
+ end
581
+ end
582
+ def <<(file)
583
+ @actions << [:apply_ary_method_1, :push, file]
584
+ @keep[file] = true
585
+ @pending = true
586
+ self
587
+ end
588
+ def keep(entry)
589
+ @keep[entry] = true
590
+ @items << entry
591
+ self
592
+ end
593
+ def concat(ary)
594
+ if @pending
595
+ ary = ary.to_ary.dup
596
+ @actions << [:apply_ary_method_1, :concat, ary]
597
+ else
598
+ ix = ignore_rx and ary = ary.to_ary.reject { |f| f =~ ix }
599
+ @items.concat(ary)
600
+ end
601
+ self
602
+ end
603
+ def size
604
+ resolve if @pending
605
+ @items.size
606
+ end
607
+ alias length size
608
+ def empty?
609
+ resolve if @pending
610
+ @items.empty?
611
+ end
612
+ def join(sep = ' ')
613
+ resolve if @pending
614
+ @items.join(sep)
615
+ end
616
+ def pop
617
+ resolve if @pending
618
+ @items.pop
619
+ end
620
+ def push(entry)
621
+ resolve if @pending
622
+ @items.push(entry) if entry !~ ignore_rx
623
+ self
624
+ end
625
+ def shift
626
+ resolve if @pending
627
+ @items.shift
628
+ end
629
+ def unshift(entry)
630
+ resolve if @pending
631
+ @items.unshift(entry) if entry !~ ignore_rx
632
+ self
633
+ end
634
+ if Object.method_defined?(:fcall) || Object.method_defined?(:funcall) # in Ruby 1.9 like __send__
635
+ @@__send_private__ = Object.method_defined?(:fcall) ? :fcall : :funcall
636
+ def resolve
637
+ @pending = false
638
+ @actions.each{ |action| self.__send__(@@__send_private__, *action) }.clear
639
+ ix = ignore_rx
640
+ if ix
641
+ @items.reject! { |f| f =~ ix && !@keep[f] }
642
+ end
643
+ self
644
+ end
645
+ else
646
+ def resolve
647
+ @pending = false
648
+ @actions.each{ |action| self.__send__(*action) }.clear
649
+ ix = ignore_rx
650
+ if ix
651
+ @items.reject! { |f| f =~ ix && !@keep[f] }
652
+ end
653
+ self
654
+ end
655
+ end
656
+ def include(*pats)
657
+ @def_glob_dotfiles ? glob_all(*pats) : glob_unix(*pats)
658
+ end
659
+ alias glob include
660
+ def glob_unix(*patterns)
661
+ patterns.flatten.each { |pat|
662
+ @actions << [:apply_glob_unix, pat]
663
+ }
664
+ @pending = true
665
+ self
666
+ end
667
+ def glob_all(*patterns)
668
+ patterns.flatten.each { |pat|
669
+ @actions << [:apply_glob_all, pat]
670
+ }
671
+ @pending = true
672
+ self
673
+ end
674
+ if RUBY_VERSION < "1.8.2"
675
+ FN_DOTFILE_RX_ = ESC_ALT_SEPARATOR ?
676
+ /(^|(#{ESC_SEPARATOR}|#{ESC_ALT_SEPARATOR})+)\..*
677
+ ((#{ESC_SEPARATOR}|#{ESC_ALT_SEPARATOR})+|$)/x :
678
+ /(^|#{ESC_SEPARATOR}+)\..* (#{ESC_SEPARATOR}+|$)/x
679
+ def apply_glob_unix(pattern)
680
+ inc_files = Dir.glob(pattern)
681
+ unless pattern =~ /(^|\/)\./
682
+ inc_files.reject! { |fn| fn =~ FN_DOTFILE_RX_ }
683
+ end
684
+ @items.concat(inc_files)
685
+ end
686
+ else
687
+ def apply_glob_unix(pattern)
688
+ @items.concat(Dir.glob(pattern))
689
+ end
690
+ end
691
+ private :apply_glob_unix
692
+ def apply_glob_all(pattern)
693
+ @items.concat(Dir.glob(pattern, File::FNM_DOTMATCH))
694
+ end
695
+ private :apply_glob_all
696
+ def exclude(*patterns)
697
+ patterns.each { |pat|
698
+ if Regexp === pat
699
+ @actions << [:apply_exclude_rx, pat]
700
+ else
701
+ @actions << [:apply_exclude, pat]
702
+ end
703
+ }
704
+ @pending = true
705
+ self
706
+ end
707
+ def ignore(*patterns)
708
+ patterns.each { |pat|
709
+ add_ignore_rx(Regexp === pat ? pat : mk_all_rx(pat))
710
+ }
711
+ @pending = true
712
+ self
713
+ end
714
+ def add_ignore_rx(rx)
715
+ @ignore_rx =
716
+ if @ignore_rx
717
+ Regexp.union(@ignore_rx, rx)
718
+ else
719
+ rx
720
+ end
721
+ end
722
+ private :add_ignore_rx
723
+ def apply_exclude(pattern)
724
+ @items.reject! { |elem|
725
+ File.fnmatch?(pattern, elem, File::FNM_DOTMATCH) && !@keep[elem]
726
+ }
727
+ end
728
+ private :apply_exclude
729
+ def apply_exclude_rx(rx)
730
+ @items.reject! { |elem|
731
+ elem =~ rx && !@keep[elem]
732
+ }
733
+ end
734
+ private :apply_exclude_rx
735
+ def exclude_name(*names)
736
+ names.each { |name|
737
+ @actions << [:apply_exclude_rx, mk_all_rx(name)]
738
+ }
739
+ @pending = true
740
+ self
741
+ end
742
+ alias shun exclude_name
743
+ if File::ALT_SEPARATOR
744
+ def mk_all_rx(file)
745
+ /(^|(#{ESC_SEPARATOR}|#{ESC_ALT_SEPARATOR})+)#{Regexp.escape(file)}
746
+ ((#{ESC_SEPARATOR}|#{ESC_ALT_SEPARATOR})+|$)/x
747
+ end
748
+ else
749
+ def mk_all_rx(file)
750
+ /(^|#{ESC_SEPARATOR}+)#{Regexp.escape(file)}
751
+ (#{ESC_SEPARATOR}+|$)/x
752
+ end
753
+ end
754
+ private :mk_all_rx
755
+ def exclude_path(*patterns)
756
+ patterns.each { |pat|
757
+ @actions << [:apply_exclude_path, pat]
758
+ }
759
+ @pending = true
760
+ self
761
+ end
762
+ def apply_exclude_path(pattern)
763
+ flags = File::FNM_DOTMATCH|File::FNM_PATHNAME
764
+ @items.reject! { |elem|
765
+ File.fnmatch?(pattern, elem, flags) && !@keep[elem]
766
+ }
767
+ end
768
+ private :apply_exclude
769
+ def select(&block)
770
+ d = dup
771
+ d.actions << [:apply_select, block]
772
+ d.pending = true
773
+ d
774
+ end
775
+ alias find_all select
776
+ def apply_select blk
777
+ @items = @items.select(&blk)
778
+ end
779
+ private :apply_select
780
+ def map(&block)
781
+ d = dup
782
+ d.actions << [:apply_ary_method, :map!, block]
783
+ d.pending = true
784
+ d
785
+ end
786
+ alias collect map
787
+ def sub_ext(ext, new_ext=nil)
788
+ map { |f| f._rant_sub_ext ext, new_ext }
789
+ end
790
+ def ext(ext_str)
791
+ sub_ext(ext_str)
792
+ end
793
+ def arglist
794
+ Rant::Sys.sp to_ary
795
+ end
796
+ alias to_s arglist
797
+ alias object_inspect inspect
798
+ def uniq!
799
+ @actions << [:apply_ary_method, :uniq!]
800
+ @pending = true
801
+ self
802
+ end
803
+ def sort!
804
+ @actions << [:apply_ary_method, :sort!]
805
+ @pending = true
806
+ self
807
+ end
808
+ def map!(&block)
809
+ @actions << [:apply_ary_method, :map!, block]
810
+ @pending = true
811
+ self
812
+ end
813
+ def reject!(&block)
814
+ @actions << [:apply_ary_method, :reject!, block]
815
+ @pending = true
816
+ self
817
+ end
818
+ private
819
+ def apply_ary_method(meth, block=nil)
820
+ @items.send meth, &block
821
+ end
822
+ def apply_ary_method_1(meth, arg1, block=nil)
823
+ @items.send meth, arg1, &block
824
+ end
825
+ end # class FileList
826
+ end # module Rant
827
+
828
+ if RUBY_VERSION == "1.8.3"
829
+ module FileUtils
830
+ METHODS = singleton_methods - %w(private_module_function
831
+ commands options have_option? options_of collect_method)
832
+ module Verbose
833
+ class << self
834
+ public(*::FileUtils::METHODS)
835
+ end
836
+ public(*::FileUtils::METHODS)
837
+ end
838
+ end
839
+ end
840
+
841
+ if RUBY_VERSION < "1.8.1"
842
+ module FileUtils
843
+ undef_method :fu_list
844
+ def fu_list(arg)
845
+ arg.respond_to?(:to_ary) ? arg.to_ary : [arg]
846
+ end
847
+ end
848
+ end
849
+
850
+ module Rant
851
+ class RacFileList < FileList
852
+
853
+ attr_reader :subdir
854
+ attr_reader :basedir
855
+
856
+ def initialize(rac, store = [])
857
+ super(store)
858
+ @rac = rac
859
+ @subdir = @rac.current_subdir
860
+ @basedir = Dir.pwd
861
+ @ignore_hash = nil
862
+ @add_ignore_args = []
863
+ update_ignore_rx
864
+ end
865
+ def dup
866
+ c = super
867
+ c.instance_variable_set(
868
+ :@add_ignore_args, @add_ignore_args.dup)
869
+ c
870
+ end
871
+ def copy
872
+ c = super
873
+ c.instance_variable_set(
874
+ :@add_ignore_args, @add_ignore_args.map { |e| e.dup })
875
+ c
876
+ end
877
+ alias filelist_ignore ignore
878
+ def ignore(*patterns)
879
+ @add_ignore_args.concat patterns
880
+ self
881
+ end
882
+ def ignore_rx
883
+ update_ignore_rx
884
+ @ignore_rx
885
+ end
886
+ alias filelist_resolve resolve
887
+ def resolve
888
+ Sys.cd(@basedir) { filelist_resolve }
889
+ end
890
+ def each_cd(&block)
891
+ old_pwd = Dir.pwd
892
+ Sys.cd(@basedir)
893
+ filelist_resolve if @pending
894
+ @items.each(&block)
895
+ ensure
896
+ Sys.cd(old_pwd)
897
+ end
898
+ private
899
+ def update_ignore_rx
900
+ ri = @rac.var[:ignore]
901
+ ri = ri ? (ri + @add_ignore_args) : @add_ignore_args
902
+ rh = ri.hash
903
+ unless rh == @ignore_hash
904
+ @ignore_rx = nil
905
+ filelist_ignore(*ri)
906
+ @ignore_hash = rh
907
+ end
908
+ end
909
+ end # class RacFileList
910
+
911
+ class MultiFileList
912
+
913
+ attr_reader :cur_list
914
+
915
+ def initialize(rac)
916
+ @rac = rac
917
+ @cur_list = RacFileList.new(@rac)
918
+ @lists = [@cur_list]
919
+ end
920
+
921
+ def each_entry(&block)
922
+ @lists.each { |list|
923
+ list.each_cd(&block)
924
+ }
925
+ end
926
+
927
+ def add(filelist)
928
+ @cur_list = filelist
929
+ @lists << filelist
930
+ self
931
+ end
932
+
933
+ def method_missing(sym, *args, &block)
934
+ if @cur_list && @cur_list.respond_to?(sym)
935
+ if @cur_list.subdir == @rac.current_subdir
936
+ @cur_list.send(sym, *args, &block)
937
+ else
938
+ add(RacFileList.new(@rac))
939
+ @cur_list.send(sym, *args, &block)
940
+ end
941
+ else
942
+ super
943
+ end
944
+ end
945
+ end # class MultiFileList
946
+
947
+ class CommandError < StandardError
948
+ attr_reader :cmd
949
+ attr_reader :status
950
+ def initialize(cmd, status=nil, msg=nil)
951
+ @msg = msg
952
+ @cmd = cmd
953
+ @status = status
954
+ end
955
+ def message
956
+ if !@msg && cmd
957
+ if status
958
+ "Command failed with status #{status.exitstatus}:\n" +
959
+ "[#{cmd}]"
960
+ else
961
+ "Command failed:\n[#{cmd}]"
962
+ end
963
+ else
964
+ @msg
965
+ end
966
+ end
967
+ end
968
+
969
+ module Sys
970
+ include ::FileUtils::Verbose
971
+
972
+ @symlink_supported = true
973
+ class << self
974
+ attr_accessor :symlink_supported
975
+ end
976
+
977
+ def fu_output_message(msg) #:nodoc:
978
+ end
979
+ private :fu_output_message
980
+
981
+ def fu_each_src_dest(src, *rest)
982
+ src = src.to_ary if src.respond_to? :to_ary
983
+ super(src, *rest)
984
+ end
985
+ private :fu_each_src_dest
986
+
987
+ def sh(*cmd_args, &block)
988
+ cmd_args.flatten!
989
+ cmd = cmd_args.join(" ")
990
+ fu_output_message cmd
991
+ success = system(*cmd_args)
992
+ if block_given?
993
+ block[$?]
994
+ elsif !success
995
+ raise CommandError.new(cmd, $?)
996
+ end
997
+ end
998
+
999
+ def ruby(*args, &block)
1000
+ if args.empty?
1001
+ sh(Env::RUBY_EXE, '', &block)
1002
+ else
1003
+ sh(args.unshift(Env::RUBY_EXE), &block)
1004
+ end
1005
+ end
1006
+ def cd(dir, &block)
1007
+ fu_output_message "cd #{dir}"
1008
+ orig_pwd = Dir.pwd
1009
+ Dir.chdir dir
1010
+ if block
1011
+ begin
1012
+ block.arity == 0 ? block.call : block.call(Dir.pwd)
1013
+ ensure
1014
+ fu_output_message "cd -"
1015
+ Dir.chdir orig_pwd
1016
+ end
1017
+ else
1018
+ self
1019
+ end
1020
+ end
1021
+
1022
+ def safe_ln(src, dest)
1023
+ dest = dest.to_str
1024
+ src = src.respond_to?(:to_ary) ? src.to_ary : src.to_str
1025
+ unless Sys.symlink_supported
1026
+ cp(src, dest)
1027
+ else
1028
+ begin
1029
+ ln(src, dest)
1030
+ rescue Exception # SystemCallError # Errno::EOPNOTSUPP
1031
+ Sys.symlink_supported = false
1032
+ cp(src, dest)
1033
+ end
1034
+ end
1035
+ end
1036
+
1037
+ def ln_f(src, dest)
1038
+ ln(src, dest, :force => true)
1039
+ end
1040
+
1041
+ def split_path(str)
1042
+ str.split(Env.on_windows? ? ";" : ":")
1043
+ end
1044
+
1045
+ if Env.on_windows?
1046
+ def root_dir?(path)
1047
+ path == "/" || path == "\\" ||
1048
+ path =~ %r{\A[a-zA-Z]+:(\\|/)\Z}
1049
+ end
1050
+ def absolute_path?(path)
1051
+ path =~ %r{\A([a-zA-Z]+:)?(/|\\)}
1052
+ end
1053
+ else
1054
+ def root_dir?(path)
1055
+ path == "/"
1056
+ end
1057
+ def absolute_path?(path)
1058
+ path =~ %r{\A/}
1059
+ end
1060
+ end
1061
+
1062
+ extend self
1063
+
1064
+ if RUBY_VERSION >= "1.8.4" # needed by 1.9.0, too
1065
+ class << self
1066
+ public(*::FileUtils::METHODS)
1067
+ end
1068
+ public(*::FileUtils::METHODS)
1069
+ end
1070
+
1071
+ end # module Sys
1072
+
1073
+ class SysObject
1074
+ include Sys
1075
+ def initialize(rant)
1076
+ @rant = rant or
1077
+ raise ArgumentError, "rant application required"
1078
+ end
1079
+ def ignore(*patterns)
1080
+ @rant.var[:ignore].concat(patterns)
1081
+ nil
1082
+ end
1083
+ def filelist(arg = Rant.__rant_no_value__)
1084
+ if Rant.__rant_no_value__.equal?(arg)
1085
+ RacFileList.new(@rant)
1086
+ elsif arg.respond_to?(:to_rant_filelist)
1087
+ arg.to_rant_filelist
1088
+ elsif arg.respond_to?(:to_ary)
1089
+ RacFileList.new(@rant, arg.to_ary)
1090
+ else
1091
+ raise TypeError,
1092
+ "cannot convert #{arg.class} into Rant::FileList"
1093
+ end
1094
+ end
1095
+ def [](*patterns)
1096
+ RacFileList.new(@rant).hide_dotfiles.include(*patterns)
1097
+ end
1098
+ def glob(*patterns, &block)
1099
+ fl = RacFileList.new(@rant).hide_dotfiles.include(*patterns)
1100
+ fl.ignore(".", "..")
1101
+ if block_given? then yield fl else fl end
1102
+ end
1103
+ def glob_all(*patterns, &block)
1104
+ fl = RacFileList.new(@rant).include(*patterns)
1105
+ fl.ignore(".", "..") # use case: "*.*" as pattern
1106
+ if block_given? then yield fl else fl end
1107
+ end
1108
+ def expand_path(path)
1109
+ File.expand_path(@rant.project_to_fs_path(path))
1110
+ end
1111
+ private
1112
+ def fu_output_message(cmd)
1113
+ @rant.cmd_msg cmd
1114
+ end
1115
+ end
1116
+
1117
+
1118
+ class TaskFail < StandardError
1119
+ def initialize(task, orig, msg)
1120
+ @task = task
1121
+ @orig = orig
1122
+ @msg = msg
1123
+ end
1124
+ def exception
1125
+ self
1126
+ end
1127
+ def task
1128
+ @task
1129
+ end
1130
+ def tname
1131
+ @task ? @task.name : nil
1132
+ end
1133
+ def orig
1134
+ @orig
1135
+ end
1136
+ def msg
1137
+ @msg
1138
+ end
1139
+ end
1140
+
1141
+ class Rantfile
1142
+ attr_reader :tasks, :path
1143
+ attr_accessor :project_subdir
1144
+ def initialize(path)
1145
+ @path = path or raise ArgumentError, "path required"
1146
+ @tasks = []
1147
+ @project_subdir = nil
1148
+ end
1149
+ alias to_s path
1150
+ alias to_str path
1151
+ end # class Rantfile
1152
+
1153
+ module Node
1154
+
1155
+ INVOKE_OPT = {}.freeze
1156
+
1157
+ T0 = Time.at(0).freeze
1158
+
1159
+ attr_reader :name
1160
+ attr_reader :rac
1161
+ attr_accessor :description
1162
+ attr_accessor :rantfile
1163
+ attr_accessor :line_number
1164
+ attr_accessor :project_subdir
1165
+
1166
+ def initialize
1167
+ @description = nil
1168
+ @rantfile = nil
1169
+ @line_number = nil
1170
+ @run = false
1171
+ @project_subdir = ""
1172
+ @success = nil
1173
+ end
1174
+
1175
+ def reference_name
1176
+ sd = rac.current_subdir
1177
+ case sd
1178
+ when "": full_name
1179
+ when project_subdir: name
1180
+ else "@#{full_name}".sub(/^@#{Regexp.escape sd}\//, '')
1181
+ end
1182
+ end
1183
+
1184
+ alias to_s reference_name
1185
+ alias to_rant_target name
1186
+
1187
+ def full_name
1188
+ sd = project_subdir
1189
+ sd.empty? ? name : File.join(sd, name)
1190
+ end
1191
+
1192
+ def ch
1193
+ {:file => rantfile.to_str, :ln => line_number}
1194
+ end
1195
+
1196
+ def goto_task_home
1197
+ @rac.goto_project_dir project_subdir
1198
+ end
1199
+
1200
+ def file_target?
1201
+ false
1202
+ end
1203
+
1204
+ def done?
1205
+ @success
1206
+ end
1207
+
1208
+ def needed?
1209
+ invoke(:needed? => true)
1210
+ end
1211
+
1212
+ def run?
1213
+ @run
1214
+ end
1215
+
1216
+ def invoke(opt = INVOKE_OPT)
1217
+ return circular_dep if run?
1218
+ @run = true
1219
+ begin
1220
+ return !done? if opt[:needed?]
1221
+ self.run if !done?
1222
+ @success = true
1223
+ ensure
1224
+ @run = false
1225
+ end
1226
+ end
1227
+
1228
+ def fail msg = nil, orig = nil
1229
+ raise TaskFail.new(self, orig, msg)
1230
+ end
1231
+
1232
+ def each_target
1233
+ end
1234
+
1235
+ def has_actions?
1236
+ defined? @block and @block
1237
+ end
1238
+
1239
+ def dry_run
1240
+ text = "Executing #{name.dump}"
1241
+ text << " [NOOP]" unless has_actions?
1242
+ @rac.cmd_msg text
1243
+ action_descs.each { |ad|
1244
+ @rac.cmd_print " - "
1245
+ @rac.cmd_msg ad.sub(/\n$/, '').gsub(/\n/, "\n ")
1246
+ }
1247
+ end
1248
+
1249
+ private
1250
+ def run
1251
+ goto_task_home
1252
+ return if @rac.running_task(self)
1253
+ return unless has_actions?
1254
+ @receiver.pre_run(self) if defined? @receiver and @receiver
1255
+ @block.arity == 0 ? @block.call : @block[self] if @block
1256
+ end
1257
+
1258
+ def action_descs
1259
+ descs = []
1260
+ if defined? @receiver and @receiver
1261
+ descs.concat(@receiver.pre_action_descs)
1262
+ end
1263
+ @block ? descs << action_block_desc : descs
1264
+ end
1265
+
1266
+ def action_block_desc
1267
+ @block.inspect =~ /^#<Proc:[\da-z]+@(.+):(\d+)>$/i
1268
+ fn, ln = $1, $2
1269
+ "Ruby Proc at #{fn.sub(/^#{Regexp.escape @rac.rootdir}\//, '')}:#{ln}"
1270
+ end
1271
+
1272
+ def circular_dep
1273
+ rac.warn_msg "Circular dependency on task `#{full_name}'."
1274
+ false
1275
+ end
1276
+ end # module Node
1277
+
1278
+
1279
+ def self.init_import_nodes__default(rac, *rest)
1280
+ rac.node_factory = DefaultNodeFactory.new
1281
+ end
1282
+
1283
+ class DefaultNodeFactory
1284
+ def new_task(rac, name, pre, blk)
1285
+ Task.new(rac, name, pre, &blk)
1286
+ end
1287
+ def new_file(rac, name, pre, blk)
1288
+ FileTask.new(rac, name, pre, &blk)
1289
+ end
1290
+ def new_dir(rac, name, pre, blk)
1291
+ DirTask.new(rac, name, pre, &blk)
1292
+ end
1293
+ def new_source(rac, name, pre, blk)
1294
+ SourceNode.new(rac, name, pre, &blk)
1295
+ end
1296
+ def new_custom(rac, name, pre, blk)
1297
+ UserTask.new(rac, name, pre, &blk)
1298
+ end
1299
+ def new_auto_subfile(rac, name, pre, blk)
1300
+ AutoSubFileTask.new(rac, name, pre, &blk)
1301
+ end
1302
+ end
1303
+
1304
+ class Task
1305
+ include Node
1306
+
1307
+ attr_accessor :receiver
1308
+
1309
+ def initialize(rac, name, prerequisites = [], &block)
1310
+ super()
1311
+ @rac = rac or raise ArgumentError, "rac not given"
1312
+ @name = name or raise ArgumentError, "name not given"
1313
+ @pre = prerequisites || []
1314
+ @pre_resolved = false
1315
+ @block = block
1316
+ @run = false
1317
+ @receiver = nil
1318
+ end
1319
+
1320
+ def prerequisites
1321
+ @pre.collect { |pre| pre.to_s }
1322
+ end
1323
+ alias deps prerequisites
1324
+
1325
+ def source
1326
+ @pre.first.to_s
1327
+ end
1328
+
1329
+ def has_actions?
1330
+ @block or @receiver && @receiver.has_pre_action?
1331
+ end
1332
+
1333
+ def <<(pre)
1334
+ @pre_resolved = false
1335
+ @pre << pre
1336
+ end
1337
+
1338
+ def invoked?
1339
+ !@success.nil?
1340
+ end
1341
+
1342
+ def fail?
1343
+ @success == false
1344
+ end
1345
+
1346
+ def enhance(deps = nil, &blk)
1347
+ if deps
1348
+ @pre_resolved = false
1349
+ @pre.concat deps
1350
+ end
1351
+ if @block
1352
+ if blk
1353
+ first_block = @block
1354
+ @block = lambda { |t|
1355
+ first_block[t]
1356
+ blk[t]
1357
+ }
1358
+ end
1359
+ else
1360
+ @block = blk
1361
+ end
1362
+ end
1363
+
1364
+ def invoke(opt = INVOKE_OPT)
1365
+ return circular_dep if @run
1366
+ @run = true
1367
+ begin
1368
+ return if done?
1369
+ internal_invoke opt
1370
+ ensure
1371
+ @run = false
1372
+ end
1373
+ end
1374
+
1375
+ def internal_invoke(opt, ud_init = true)
1376
+ goto_task_home
1377
+ update = ud_init || opt[:force]
1378
+ dep = nil
1379
+ uf = false
1380
+ each_dep { |dep|
1381
+ if dep.respond_to? :timestamp
1382
+ handle_timestamped(dep, opt) && update = true
1383
+ elsif Node === dep
1384
+ handle_node(dep, opt) && update = true
1385
+ else
1386
+ dep, uf = handle_non_node(dep, opt)
1387
+ uf && update = true
1388
+ dep
1389
+ end
1390
+ }
1391
+ if @receiver
1392
+ goto_task_home
1393
+ update = true if @receiver.update?(self)
1394
+ end
1395
+ return update if opt[:needed?]
1396
+ run if update
1397
+ @success = true
1398
+ update
1399
+ rescue StandardError => e
1400
+ @success = false
1401
+ self.fail(nil, e)
1402
+ end
1403
+ private :internal_invoke
1404
+
1405
+ def handle_node(dep, opt)
1406
+ dep.invoke opt
1407
+ end
1408
+
1409
+ def handle_timestamped(dep, opt)
1410
+ dep.invoke opt
1411
+ end
1412
+
1413
+ def handle_non_node(dep, opt)
1414
+ @rac.err_msg "Unknown task `#{dep}',",
1415
+ "referenced in `#{rantfile.path}', line #{@line_number}!"
1416
+ self.fail
1417
+ end
1418
+
1419
+ def each_dep
1420
+ t = nil
1421
+ if @pre_resolved
1422
+ return @pre.each { |t| yield(t) }
1423
+ end
1424
+ my_full_name = full_name
1425
+ my_project_subdir = project_subdir
1426
+ @pre.map! { |t|
1427
+ if Node === t
1428
+ if t.full_name == my_full_name
1429
+ nil
1430
+ else
1431
+ yield(t)
1432
+ t
1433
+ end
1434
+ else
1435
+ t = t.to_s if Symbol === t
1436
+ if t == my_full_name #TODO
1437
+ nil
1438
+ else
1439
+ selection = @rac.resolve t,
1440
+ my_project_subdir
1441
+ if selection.empty?
1442
+ yield(t)
1443
+ else
1444
+ selection.each { |st| yield(st) }
1445
+ selection
1446
+ end
1447
+ end
1448
+ end
1449
+ }
1450
+ if @pre.kind_of? Rant::FileList
1451
+ @pre.resolve
1452
+ else
1453
+ @pre.flatten!
1454
+ @pre.compact!
1455
+ end
1456
+ @pre_resolved = true
1457
+ end
1458
+ end # class Task
1459
+
1460
+ class UserTask < Task
1461
+
1462
+ def initialize(*args)
1463
+ super
1464
+ @block = nil
1465
+ @needed = nil
1466
+ @target_files = nil
1467
+ yield self if block_given?
1468
+ end
1469
+
1470
+ def act(&block)
1471
+ @block = block
1472
+ end
1473
+
1474
+ def needed(&block)
1475
+ @needed = block
1476
+ end
1477
+
1478
+ def file_target?
1479
+ @target_files and @target_files.include? @name
1480
+ end
1481
+
1482
+ def each_target(&block)
1483
+ goto_task_home
1484
+ @target_files.each(&block) if @target_files
1485
+ end
1486
+
1487
+ def file_target(*args)
1488
+ args.flatten!
1489
+ args << @name if args.empty?
1490
+ if @target_files
1491
+ @target_files.concat(args)
1492
+ else
1493
+ @target_files = args
1494
+ end
1495
+ end
1496
+
1497
+ def invoke(opt = INVOKE_OPT)
1498
+ return circular_dep if @run
1499
+ @run = true
1500
+ begin
1501
+ return if done?
1502
+ internal_invoke(opt, ud_init_by_needed)
1503
+ ensure
1504
+ @run = false
1505
+ end
1506
+ end
1507
+
1508
+ private
1509
+ def ud_init_by_needed
1510
+ if @needed
1511
+ goto_task_home
1512
+ @needed.arity == 0 ? @needed.call : @needed[self]
1513
+ end
1514
+ end
1515
+ end # class UserTask
1516
+
1517
+ class FileTask < Task
1518
+
1519
+ def initialize(*args)
1520
+ super
1521
+ @ts = T0
1522
+ end
1523
+
1524
+ def file_target?
1525
+ true
1526
+ end
1527
+
1528
+ def invoke(opt = INVOKE_OPT)
1529
+ return circular_dep if @run
1530
+ @run = true
1531
+ begin
1532
+ return if done?
1533
+ goto_task_home
1534
+ if File.exist? @name
1535
+ @ts = File.mtime @name
1536
+ internal_invoke opt, false
1537
+ else
1538
+ @ts = T0
1539
+ internal_invoke opt, true
1540
+ end
1541
+ ensure
1542
+ @run = false
1543
+ end
1544
+ end
1545
+
1546
+ def timestamp(opt = INVOKE_OPT)
1547
+ File.exist?(@name) ? File.mtime(@name) : T0
1548
+ end
1549
+
1550
+ def handle_node(dep, opt)
1551
+ return true if dep.file_target? && dep.invoke(opt)
1552
+ if File.exist? dep.name
1553
+ File.mtime(dep.name) > @ts
1554
+ elsif !dep.file_target?
1555
+ @rac.err_msg @rac.pos_text(rantfile.path, line_number),
1556
+ "in prerequisites: no such file: `#{dep.full_name}'"
1557
+ self.fail
1558
+ end
1559
+ end
1560
+
1561
+ def handle_timestamped(dep, opt)
1562
+ return true if dep.invoke opt
1563
+ dep.timestamp(opt) > @ts
1564
+ end
1565
+
1566
+ def handle_non_node(dep, opt)
1567
+ goto_task_home # !!??
1568
+ unless File.exist? dep
1569
+ @rac.err_msg @rac.pos_text(rantfile.path, line_number),
1570
+ "in prerequisites: no such file or task: `#{dep}'"
1571
+ self.fail
1572
+ end
1573
+ [dep, File.mtime(dep) > @ts]
1574
+ end
1575
+
1576
+ def each_target
1577
+ goto_task_home
1578
+ yield name
1579
+ end
1580
+ end # class FileTask
1581
+
1582
+ module AutoInvokeDirNode
1583
+ private
1584
+ def run
1585
+ goto_task_home
1586
+ return if @rac.running_task(self)
1587
+ dir = File.dirname(name)
1588
+ @rac.build dir unless dir == "." || dir == "/"
1589
+ return unless @block
1590
+ @block.arity == 0 ? @block.call : @block[self]
1591
+ end
1592
+ end
1593
+
1594
+ class AutoSubFileTask < FileTask
1595
+ include AutoInvokeDirNode
1596
+ end
1597
+
1598
+ class DirTask < Task
1599
+
1600
+ def initialize(*args)
1601
+ super
1602
+ @ts = T0
1603
+ @isdir = nil
1604
+ end
1605
+
1606
+ def invoke(opt = INVOKE_OPT)
1607
+ return circular_dep if @run
1608
+ @run = true
1609
+ begin
1610
+ return if done?
1611
+ goto_task_home
1612
+ @isdir = test(?d, @name)
1613
+ if @isdir
1614
+ @ts = @block ? test(?M, @name) : Time.now
1615
+ internal_invoke opt, false
1616
+ else
1617
+ @ts = T0
1618
+ internal_invoke opt, true
1619
+ end
1620
+ ensure
1621
+ @run = false
1622
+ end
1623
+ end
1624
+
1625
+ def file_target?
1626
+ true
1627
+ end
1628
+
1629
+ def handle_node(dep, opt)
1630
+ return true if dep.file_target? && dep.invoke(opt)
1631
+ if File.exist? dep.name
1632
+ File.mtime(dep.name) > @ts
1633
+ elsif !dep.file_target?
1634
+ @rac.err_msg @rac.pos_text(rantfile.path, line_number),
1635
+ "in prerequisites: no such file: `#{dep.full_name}'"
1636
+ self.fail
1637
+ end
1638
+ end
1639
+
1640
+ def handle_timestamped(dep, opt)
1641
+ return @block if dep.invoke opt
1642
+ @block && dep.timestamp(opt) > @ts
1643
+ end
1644
+
1645
+ def handle_non_node(dep, opt)
1646
+ goto_task_home
1647
+ unless File.exist? dep
1648
+ @rac.err_msg @rac.pos_text(rantfile.path, line_number),
1649
+ "in prerequisites: no such file or task: `#{dep}'"
1650
+ self.fail
1651
+ end
1652
+ [dep, @block && File.mtime(dep) > @ts]
1653
+ end
1654
+
1655
+ def run
1656
+ return if @rac.running_task(self)
1657
+ @rac.sys.mkdir @name unless @isdir
1658
+ if @block
1659
+ @block.arity == 0 ? @block.call : @block[self]
1660
+ goto_task_home
1661
+ @rac.sys.touch @name
1662
+ end
1663
+ end
1664
+
1665
+ def each_target
1666
+ goto_task_home
1667
+ yield name
1668
+ end
1669
+ end # class DirTask
1670
+
1671
+ class SourceNode
1672
+ include Node
1673
+ def initialize(rac, name, prerequisites = [])
1674
+ super()
1675
+ @rac = rac
1676
+ @name = name or raise ArgumentError, "name not given"
1677
+ @pre = prerequisites
1678
+ @run = false
1679
+ @ts = nil
1680
+ end
1681
+ def prerequisites
1682
+ @pre
1683
+ end
1684
+ def timestamp(opt = INVOKE_OPT)
1685
+ return @ts if @ts
1686
+ goto_task_home
1687
+ if File.exist?(@name)
1688
+ @ts = File.mtime @name
1689
+ else
1690
+ rac.abort_at(ch, "SourceNode: no such file -- #@name")
1691
+ end
1692
+ sd = project_subdir
1693
+ @pre.each { |f|
1694
+ nodes = rac.resolve f, sd
1695
+ if nodes.empty?
1696
+ if File.exist? f
1697
+ mtime = File.mtime f
1698
+ @ts = mtime if mtime > @ts
1699
+ else
1700
+ rac.abort_at(ch,
1701
+ "SourceNode: no such file -- #{f}")
1702
+ end
1703
+ else
1704
+ nodes.each { |node|
1705
+ node.invoke(opt)
1706
+ if node.respond_to? :timestamp
1707
+ node_ts = node.timestamp(opt)
1708
+ goto_task_home
1709
+ @ts = node_ts if node_ts > @ts
1710
+ else
1711
+ rac.abort_at(ch,
1712
+ "SourceNode can't depend on #{node.name}")
1713
+ end
1714
+ }
1715
+ end
1716
+ }
1717
+ @ts
1718
+ end
1719
+ def invoke(opt = INVOKE_OPT)
1720
+ false
1721
+ end
1722
+ def related_sources
1723
+ @pre
1724
+ end
1725
+ end # class SourceNode
1726
+
1727
+ module Generators
1728
+ class Task
1729
+ def self.rant_gen(rac, ch, args, &block)
1730
+ unless args.size == 1
1731
+ rac.abort("Task takes only one argument " +
1732
+ "which has to be like one given to the " +
1733
+ "`task' function")
1734
+ end
1735
+ rac.prepare_task(args.first, nil, ch) { |name,pre,blk|
1736
+ rac.node_factory.new_custom(rac, name, pre, block)
1737
+ }
1738
+ end
1739
+ end
1740
+ class Directory
1741
+ def self.rant_gen(rac, ch, args, &block)
1742
+ case args.size
1743
+ when 1
1744
+ name, pre = rac.normalize_task_arg(args.first, ch)
1745
+ self.task(rac, ch, name, pre, &block)
1746
+ when 2
1747
+ basedir = args.shift
1748
+ if basedir.respond_to? :to_str
1749
+ basedir = basedir.to_str
1750
+ else
1751
+ rac.abort_at(ch,
1752
+ "Directory: basedir argument has to be a string.")
1753
+ end
1754
+ name, pre = rac.normalize_task_arg(args.first, ch)
1755
+ self.task(rac, ch, name, pre, basedir, &block)
1756
+ else
1757
+ rac.abort_at(ch, "Directory takes one argument, " +
1758
+ "which should be like one given to the `task' command.")
1759
+ end
1760
+ end
1761
+
1762
+ def self.task(rac, ch, name, prerequisites=[], basedir=nil, &block)
1763
+ dirs = ::Rant::Sys.split_all(name)
1764
+ if dirs.empty?
1765
+ rac.abort_at(ch,
1766
+ "Not a valid directory name: `#{name}'")
1767
+ end
1768
+ path = basedir
1769
+ last_task = nil
1770
+ task_block = nil
1771
+ desc_for_last = rac.pop_desc
1772
+ dirs.each { |dir|
1773
+ pre = [path]
1774
+ pre.compact!
1775
+ if dir.equal?(dirs.last)
1776
+ rac.cx.desc desc_for_last
1777
+
1778
+ dp = prerequisites.dup
1779
+ pre.each { |elem| dp << elem }
1780
+ pre = dp
1781
+
1782
+ task_block = block
1783
+ end
1784
+ path = path.nil? ? dir : File.join(path, dir)
1785
+ last_task = rac.prepare_task({:__caller__ => ch,
1786
+ path => pre}, task_block) { |name,pre,blk|
1787
+ rac.node_factory.new_dir(rac, name, pre, blk)
1788
+ }
1789
+ }
1790
+ last_task
1791
+ end
1792
+ end # class Directory
1793
+ class SourceNode
1794
+ def self.rant_gen(rac, ch, args)
1795
+ unless args.size == 1
1796
+ rac.abort_at(ch, "SourceNode takes one argument.")
1797
+ end
1798
+ if block_given?
1799
+ rac.abort_at(ch, "SourceNode doesn't take a block.")
1800
+ end
1801
+ rac.prepare_task(args.first, nil, ch) { |name, pre, blk|
1802
+ rac.node_factory.new_source(rac, name, pre, blk)
1803
+ }
1804
+ end
1805
+ end
1806
+ class Rule
1807
+ def self.rant_gen(rac, ch, args, &block)
1808
+ unless args.size == 1
1809
+ rac.abort_at(ch, "Rule takes only one argument.")
1810
+ end
1811
+ rac.abort_at(ch, "Rule: block required.") unless block
1812
+ arg = args.first
1813
+ target = nil
1814
+ src_arg = nil
1815
+ if Symbol === arg
1816
+ target = ".#{arg}"
1817
+ elsif arg.respond_to? :to_str
1818
+ target = arg.to_str
1819
+ elsif Regexp === arg
1820
+ target = arg
1821
+ elsif Hash === arg && arg.size == 1
1822
+ arg.each_pair { |target, src_arg| }
1823
+ src_arg = src_arg.to_str if src_arg.respond_to? :to_str
1824
+ target = target.to_str if target.respond_to? :to_str
1825
+ src_arg = ".#{src_arg}" if Symbol === src_arg
1826
+ target = ".#{target}" if Symbol === target
1827
+ else
1828
+ rac.abort_at(ch, "Rule argument " +
1829
+ "has to be a hash with one key-value pair.")
1830
+ end
1831
+ esc_target = nil
1832
+ target_rx = case target
1833
+ when String
1834
+ esc_target = Regexp.escape(target)
1835
+ /#{esc_target}$/
1836
+ when Regexp
1837
+ target
1838
+ else
1839
+ rac.abort_at(ch, "rule target has " +
1840
+ "to be a string or regular expression")
1841
+ end
1842
+ src_proc = case src_arg
1843
+ when String, Array
1844
+ unless String === target
1845
+ rac.abort(ch, "rule target has to be " +
1846
+ "a string if source is a string")
1847
+ end
1848
+ if src_arg.kind_of? String
1849
+ lambda { |name|
1850
+ name.sub(/#{esc_target}$/, src_arg)
1851
+ }
1852
+ else
1853
+ lambda { |name|
1854
+ src_arg.collect { |s_src|
1855
+ s_src = ".#{s_src}" if Symbol === s_src
1856
+ name.sub(/#{esc_target}$/, s_src)
1857
+ }
1858
+ }
1859
+ end
1860
+ when Proc: src_arg
1861
+ when nil: lambda { |name| [] }
1862
+ else
1863
+ rac.abort_at(ch, "rule source has to be a " +
1864
+ "String, Array or Proc")
1865
+ end
1866
+ rac.resolve_hooks <<
1867
+ (block.arity == 2 ? Hook : FileHook).new(
1868
+ rac, ch, target_rx, src_proc, block)
1869
+ nil
1870
+ end
1871
+ class Hook
1872
+ attr_accessor :target_rx
1873
+ def initialize(rant, ch, target_rx, src_proc, block)
1874
+ @rant = rant
1875
+ @ch = ch
1876
+ @target_rx = target_rx
1877
+ @src_proc = src_proc
1878
+ @block = block
1879
+ end
1880
+ def call(target, rel_project_dir)
1881
+ if @target_rx =~ target
1882
+ have_src = true
1883
+ src = @src_proc[target]
1884
+ if src.respond_to? :to_ary
1885
+ have_src = src.to_ary.all? { |s|
1886
+ have_src?(rel_project_dir, s)
1887
+ }
1888
+ else
1889
+ have_src = have_src?(rel_project_dir, src)
1890
+ end
1891
+ if have_src
1892
+ create_nodes(rel_project_dir, target, src)
1893
+ end
1894
+ end
1895
+ end
1896
+ alias [] call
1897
+ private
1898
+ def have_src?(rel_project_dir, name)
1899
+ return true unless
1900
+ @rant.rec_save_resolve(name, self, rel_project_dir).empty?
1901
+ test(?e, @rant.abs_path(rel_project_dir, name))
1902
+ end
1903
+ def create_nodes(rel_project_dir, target, deps)
1904
+ @rant.goto_project_dir rel_project_dir
1905
+ case nodes = @block[target, deps]
1906
+ when Array: nodes
1907
+ when Node: [nodes]
1908
+ else
1909
+ @rant.abort_at(@ch, "Block has to " +
1910
+ "return Node or array of Nodes.")
1911
+ end
1912
+ end
1913
+ end
1914
+ class FileHook < Hook
1915
+ private
1916
+ def have_src?(rel_project_dir, name)
1917
+ test(?e, @rant.abs_path(rel_project_dir, name)) or
1918
+ @rant.rec_save_resolve(name, self, rel_project_dir
1919
+ ).any? { |t| t.file_target? }
1920
+ end
1921
+ def create_nodes(rel_project_dir, target, deps)
1922
+ @rant.goto_project_dir rel_project_dir
1923
+ t = @rant.file(:__caller__ => @ch,
1924
+ target => deps, &@block)
1925
+ [t]
1926
+ end
1927
+ end
1928
+ end # class Rule
1929
+ class Action
1930
+ def self.rant_gen(rac, ch, args, &block)
1931
+ case args.size
1932
+ when 0:
1933
+ unless (rac[:tasks] || rac[:stop_after_load])
1934
+ yield
1935
+ end
1936
+ when 1:
1937
+ rx = args.first
1938
+ unless rx.kind_of? Regexp
1939
+ rac.abort_at(ch, "Action: argument has " +
1940
+ "to be a regular expression.")
1941
+ end
1942
+ rac.resolve_hooks << self.new(rac, block, rx)
1943
+ nil
1944
+ else
1945
+ rac.abort_at(ch, "Action: too many arguments.")
1946
+ end
1947
+ end
1948
+ def initialize(rant, block, rx)
1949
+ @rant = rant
1950
+ @subdir = @rant.current_subdir
1951
+ @block = block
1952
+ @rx = rx
1953
+ end
1954
+ def call(target, rel_project_dir)
1955
+ if target =~ @rx
1956
+ @rant.resolve_hooks.delete(self)
1957
+ @rant.goto_project_dir @subdir
1958
+ @block.call
1959
+ @rant.resolve(target, rel_project_dir)
1960
+ end
1961
+ end
1962
+ alias [] call
1963
+ end
1964
+ end # module Generators
1965
+ end # module Rant
1966
+
1967
+ Rant::MAIN_OBJECT = self
1968
+
1969
+ class String
1970
+ alias sub_ext _rant_sub_ext
1971
+ def to_rant_target
1972
+ self
1973
+ end
1974
+ end
1975
+
1976
+ module Rant::Lib
1977
+ def parse_caller_elem(elem)
1978
+ return { :file => "", :ln => 0 } unless elem
1979
+ if elem =~ /^(.+):(\d+)(?::|$)/
1980
+ { :file => $1, :ln => $2.to_i }
1981
+ else
1982
+ $stderr.puts "parse_caller_elem: #{elem.inspect}"
1983
+ { :file => elem, :ln => 0 }
1984
+ end
1985
+
1986
+ end
1987
+ module_function :parse_caller_elem
1988
+ end # module Lib
1989
+
1990
+ module Rant::Console
1991
+ RANT_PREFIX = "rant: "
1992
+ ERROR_PREFIX = "[ERROR] "
1993
+ WARN_PREFIX = "[WARNING] "
1994
+ def msg_prefix
1995
+ if defined? @msg_prefix and @msg_prefix
1996
+ @msg_prefix
1997
+ else
1998
+ RANT_PREFIX
1999
+ end
2000
+ end
2001
+ def msg(*text)
2002
+ pre = msg_prefix
2003
+ $stderr.puts "#{pre}#{text.join("\n" + ' ' * pre.length)}"
2004
+ end
2005
+ def vmsg(importance, *text)
2006
+ msg(*text) if verbose >= importance
2007
+ end
2008
+ def err_msg(*text)
2009
+ pre = msg_prefix + ERROR_PREFIX
2010
+ $stderr.puts "#{pre}#{text.join("\n" + ' ' * pre.length)}"
2011
+ end
2012
+ def warn_msg(*text)
2013
+ pre = msg_prefix + WARN_PREFIX
2014
+ $stderr.puts "#{pre}#{text.join("\n" + ' ' * pre.length)}"
2015
+ end
2016
+ def ask_yes_no text
2017
+ $stderr.print msg_prefix + text + " [y|n] "
2018
+ case $stdin.readline
2019
+ when /y|yes/i: true
2020
+ when /n|no/i: false
2021
+ else
2022
+ $stderr.puts(' ' * msg_prefix.length +
2023
+ "Please answer with `yes' or `no'")
2024
+ ask_yes_no text
2025
+ end
2026
+ end
2027
+ def prompt text
2028
+ $stderr.print msg_prefix + text
2029
+ input = $stdin.readline
2030
+ input ? input.chomp : input
2031
+ end
2032
+ def option_listing opts
2033
+ rs = ""
2034
+ opts.each { |lopt, *opt_a|
2035
+ if opt_a.size == 2
2036
+ mode, desc = opt_a
2037
+ else
2038
+ sopt, mode, desc = opt_a
2039
+ end
2040
+ next unless desc # "private" option
2041
+ optstr = ""
2042
+ arg = nil
2043
+ if mode != GetoptLong::NO_ARGUMENT
2044
+ if desc =~ /(\b[A-Z_]{2,}\b)/
2045
+ arg = $1
2046
+ end
2047
+ end
2048
+ if lopt
2049
+ optstr << lopt
2050
+ if arg
2051
+ optstr << " " << arg
2052
+ end
2053
+ optstr = optstr.ljust(30)
2054
+ end
2055
+ if sopt
2056
+ optstr << " " unless optstr.empty?
2057
+ optstr << sopt
2058
+ if arg
2059
+ optstr << " " << arg
2060
+ end
2061
+ end
2062
+ rs << " #{optstr}\n"
2063
+ rs << " #{desc.split("\n").join("\n ")}\n"
2064
+ }
2065
+ rs
2066
+ end
2067
+ extend self
2068
+ end # module Rant::Console
2069
+
2070
+ module RantContext
2071
+ include Rant::Generators
2072
+
2073
+ Env = Rant::Env
2074
+ FileList = Rant::FileList
2075
+
2076
+ def task(targ, &block)
2077
+ rant.task(targ, &block)
2078
+ end
2079
+
2080
+ def file(targ, &block)
2081
+ rant.file(targ, &block)
2082
+ end
2083
+
2084
+ def enhance(targ, &block)
2085
+ rant.enhance(targ, &block)
2086
+ end
2087
+
2088
+ def desc(*args)
2089
+ rant.desc(*args)
2090
+ end
2091
+
2092
+ def gen(*args, &block)
2093
+ rant.gen(*args, &block)
2094
+ end
2095
+
2096
+ def import(*args, &block)
2097
+ rant.import(*args, &block)
2098
+ end
2099
+
2100
+ def plugin(*args, &block)
2101
+ rant.plugin(*args, &block)
2102
+ end
2103
+
2104
+ def subdirs(*args)
2105
+ rant.subdirs(*args)
2106
+ end
2107
+
2108
+ def source(opt, rantfile = nil)
2109
+ rant.source(opt, rantfile)
2110
+ end
2111
+
2112
+ def sys(*args, &block)
2113
+ rant.sys(*args, &block)
2114
+ end
2115
+
2116
+ def var(*args, &block)
2117
+ rant.var(*args, &block)
2118
+ end
2119
+
2120
+ def make(*args, &block)
2121
+ rant.make(*args, &block)
2122
+ end
2123
+
2124
+ end # module RantContext
2125
+
2126
+ class RantAppContext
2127
+ include RantContext
2128
+
2129
+ def initialize(app)
2130
+ @__rant__ = app
2131
+ end
2132
+
2133
+ def rant
2134
+ @__rant__
2135
+ end
2136
+
2137
+ def method_missing(sym, *args)
2138
+ Rant::MAIN_OBJECT.send(sym, *args)
2139
+ rescue NoMethodError
2140
+ raise NameError, "NameError: undefined local " +
2141
+ "variable or method `#{sym}' for main:Object", caller
2142
+ end
2143
+ end
2144
+
2145
+ module Rant
2146
+
2147
+ @__rant__ = nil
2148
+ class << self
2149
+
2150
+ def run(first_arg=nil, *other_args)
2151
+ other_args = other_args.flatten
2152
+ args = first_arg.nil? ? ARGV.dup : ([first_arg] + other_args)
2153
+ if rant && !rant.run?
2154
+ rant.run(args.flatten)
2155
+ else
2156
+ @__rant__ = Rant::RantApp.new
2157
+ rant.run(args)
2158
+ end
2159
+ end
2160
+
2161
+ def rant
2162
+ @__rant__
2163
+ end
2164
+ end
2165
+
2166
+ end # module Rant
2167
+
2168
+ class Rant::RantApp
2169
+ include Rant::Console
2170
+
2171
+ class AutoLoadNodeFactory
2172
+ def initialize(rant)
2173
+ @rant = rant
2174
+ end
2175
+ def method_missing(sym, *args, &block)
2176
+ @rant.import "nodes/default"
2177
+ @rant.node_factory.send(sym, *args, &block)
2178
+ end
2179
+ end
2180
+
2181
+
2182
+
2183
+ OPTIONS = [
2184
+ [ "--help", "-h", GetoptLong::NO_ARGUMENT,
2185
+ "Print this help and exit." ],
2186
+ [ "--version", "-V", GetoptLong::NO_ARGUMENT,
2187
+ "Print version of Rant and exit." ],
2188
+ [ "--verbose", "-v", GetoptLong::NO_ARGUMENT,
2189
+ "Print more messages to stderr." ],
2190
+ [ "--quiet", "-q", GetoptLong::NO_ARGUMENT,
2191
+ "Don't print commands." ],
2192
+ [ "--err-commands", GetoptLong::NO_ARGUMENT,
2193
+ "Print failed commands and their exit status." ],
2194
+ [ "--directory","-C", GetoptLong::REQUIRED_ARGUMENT,
2195
+ "Run rant in DIRECTORY." ],
2196
+ [ "--cd-parent","-c", GetoptLong::NO_ARGUMENT,
2197
+ "Run rant in parent directory with Rantfile." ],
2198
+ [ "--look-up", "-u", GetoptLong::NO_ARGUMENT,
2199
+ "Look in parent directories for root Rantfile." ],
2200
+ [ "--rantfile", "-f", GetoptLong::REQUIRED_ARGUMENT,
2201
+ "Process RANTFILE instead of standard rantfiles.\n" +
2202
+ "Multiple files may be specified with this option." ],
2203
+ [ "--force-run","-a", GetoptLong::REQUIRED_ARGUMENT,
2204
+ "Force rebuild of TARGET and all dependencies." ],
2205
+ [ "--dry-run", "-n", GetoptLong::NO_ARGUMENT,
2206
+ "Print info instead of actually executing actions." ],
2207
+ [ "--tasks", "-T", GetoptLong::NO_ARGUMENT,
2208
+ "Show a list of all described tasks and exit." ],
2209
+
2210
+
2211
+ [ "--import", "-i", GetoptLong::REQUIRED_ARGUMENT, nil ],
2212
+ [ "--stop-after-load", GetoptLong::NO_ARGUMENT, nil ],
2213
+ [ "--trace-abort", GetoptLong::NO_ARGUMENT, nil ],
2214
+ ]
2215
+
2216
+ ROOT_DIR_ID = "@"
2217
+ ESCAPE_ID = "\\"
2218
+
2219
+ attr_reader :args
2220
+ attr_reader :rantfiles
2221
+ attr_reader :force_targets
2222
+ attr_reader :plugins
2223
+ attr_reader :context
2224
+ alias cx context
2225
+ attr_reader :tasks
2226
+ attr_reader :imports
2227
+ attr_reader :current_subdir
2228
+ attr_reader :resolve_hooks
2229
+ attr_reader :rootdir
2230
+
2231
+ attr_accessor :node_factory
2232
+
2233
+ def initialize
2234
+ @args = []
2235
+ @context = RantAppContext.new(self)
2236
+ @sys = ::Rant::SysObject.new(self)
2237
+ @rantfiles = []
2238
+ @tasks = {}
2239
+ @opts = {
2240
+ :verbose => 0,
2241
+ :quiet => false,
2242
+ }
2243
+ @rootdir = Dir.pwd # root directory of project
2244
+ @arg_rantfiles = [] # rantfiles given in args
2245
+ @arg_targets = [] # targets given in args
2246
+ @force_targets = [] # targets given with -a option
2247
+ @run = false # run method was called at least once
2248
+ @done = false # run method was successful
2249
+ @plugins = []
2250
+ @var = Rant::RantVar::Space.new
2251
+ @var.query :ignore, :AutoList, []
2252
+ @imports = []
2253
+
2254
+ @task_desc = nil
2255
+ @last_build_subdir = ""
2256
+
2257
+ @current_subdir = ""
2258
+ @resolve_hooks = []
2259
+
2260
+ @node_factory = AutoLoadNodeFactory.new(self)
2261
+ end
2262
+
2263
+ def [](opt)
2264
+ @opts[opt]
2265
+ end
2266
+
2267
+ def []=(opt, val)
2268
+ @opts[opt] = val
2269
+ end
2270
+
2271
+ def expand_path(subdir, path)
2272
+ case path
2273
+ when nil: subdir.dup
2274
+ when "": subdir.dup
2275
+ when /^@/: path.sub(/^@/, '')
2276
+ else
2277
+ path = path.sub(/^\\(?=@)/, '')
2278
+ if subdir.empty?
2279
+ path
2280
+ else
2281
+ File.join(subdir, path)
2282
+ end
2283
+ end
2284
+ end
2285
+ def resolve_root_ref(path)
2286
+ return File.join(@rootdir, path[1..-1]) if path =~ /^@/
2287
+ path.sub(/^\\(?=@)/, '')
2288
+ end
2289
+ def project_to_fs_path(path)
2290
+ sub = expand_path(@current_subdir, path)
2291
+ sub.empty? ? @rootdir : File.join(@rootdir, sub)
2292
+ end
2293
+ def abs_path(subdir, fn)
2294
+ return fn if Rant::Sys.absolute_path?(fn)
2295
+ path = File.join(@rootdir, subdir, fn)
2296
+ path.gsub!(%r{/+}, "/")
2297
+ path.sub!(%r{/$}, "") if path.length > 1
2298
+ path
2299
+ end
2300
+ def goto(dir)
2301
+ goto_project_dir(expand_path(@current_subdir, dir))
2302
+ end
2303
+ def goto_project_dir(dir='')
2304
+ @current_subdir = dir
2305
+ abs_path = @current_subdir.empty? ?
2306
+ @rootdir : File.join(@rootdir, @current_subdir)
2307
+ unless Dir.pwd == abs_path
2308
+ Dir.chdir abs_path
2309
+ vmsg 1, "in #{abs_path}"
2310
+ end
2311
+ end
2312
+
2313
+ def run?
2314
+ @run
2315
+ end
2316
+
2317
+ def done?
2318
+ @done
2319
+ end
2320
+
2321
+ def run(*args)
2322
+ @run = true
2323
+ @args.concat(args.flatten)
2324
+ orig_pwd = @rootdir = Dir.pwd
2325
+ process_args
2326
+ Dir.chdir(@rootdir) rescue abort $!.message
2327
+ load_rantfiles
2328
+
2329
+ raise Rant::RantDoneException if @opts[:stop_after_load]
2330
+
2331
+ @plugins.each { |plugin| plugin.rant_start }
2332
+ if @opts[:tasks]
2333
+ show_descriptions
2334
+ raise Rant::RantDoneException
2335
+ end
2336
+ run_tasks
2337
+ raise Rant::RantDoneException
2338
+ rescue Rant::RantDoneException
2339
+ @done = true
2340
+ @plugins.each { |plugin| plugin.rant_done }
2341
+ return 0
2342
+ rescue Rant::RantAbortException
2343
+ $stderr.puts "rant aborted!"
2344
+ return 1
2345
+ rescue Exception => e
2346
+ ch = get_ch_from_backtrace(e.backtrace)
2347
+ if ch && !@opts[:trace_abort]
2348
+ err_msg(pos_text(ch[:file], ch[:ln]), e.message)
2349
+ else
2350
+ err_msg e.message, e.backtrace[0..4]
2351
+ end
2352
+ $stderr.puts "rant aborted!"
2353
+ return 1
2354
+ ensure
2355
+ Dir.chdir @rootdir if test ?d, @rootdir
2356
+ hooks = var._get("__at_return__")
2357
+ hooks.each { |hook| hook.call } if hooks
2358
+ @plugins.each { |plugin| plugin.rant_plugin_stop }
2359
+ @plugins.each { |plugin| plugin.rant_quit }
2360
+ Dir.chdir orig_pwd
2361
+ end
2362
+
2363
+
2364
+ def desc(*args)
2365
+ if args.empty? || (args.size == 1 && args.first.nil?)
2366
+ @task_desc = nil
2367
+ else
2368
+ @task_desc = args.join("\n")
2369
+ end
2370
+ end
2371
+
2372
+ def task(targ, &block)
2373
+ prepare_task(targ, block) { |name,pre,blk|
2374
+ @node_factory.new_task(self, name, pre, blk)
2375
+ }
2376
+ end
2377
+
2378
+ def file(targ, &block)
2379
+ prepare_task(targ, block) { |name,pre,blk|
2380
+ @node_factory.new_file(self, name, pre, blk)
2381
+ }
2382
+ end
2383
+
2384
+ def gen(*args, &block)
2385
+ ch = Rant::Lib::parse_caller_elem(caller[1])
2386
+ generator = args.shift
2387
+ unless generator.respond_to? :rant_gen
2388
+ abort_at(ch,
2389
+ "gen: First argument has to be a task-generator.")
2390
+ end
2391
+ generator.rant_gen(self, ch, args, &block)
2392
+ end
2393
+
2394
+ def import(*args, &block)
2395
+ ch = Rant::Lib::parse_caller_elem(caller[1])
2396
+ if block
2397
+ warn_msg pos_text(ch[:file], ch[:ln]),
2398
+ "import: ignoring block"
2399
+ end
2400
+ args.flatten.each { |arg|
2401
+ unless String === arg
2402
+ abort_at(ch, "import: only strings allowed as arguments")
2403
+ end
2404
+ unless @imports.include? arg
2405
+ unless Rant::CODE_IMPORTS.include? arg
2406
+ begin
2407
+ vmsg 2, "import #{arg}"
2408
+ require "rant/import/#{arg}"
2409
+ rescue LoadError => e
2410
+ abort_at(ch, "No such import - #{arg}")
2411
+ end
2412
+ Rant::CODE_IMPORTS << arg.dup
2413
+ end
2414
+ init_msg = "init_import_#{arg.gsub(/[^\w]/, '__')}"
2415
+ Rant.send init_msg, self if Rant.respond_to? init_msg
2416
+ @imports << arg.dup
2417
+ end
2418
+ }
2419
+ end
2420
+
2421
+ def plugin(*args, &block)
2422
+ clr = caller[1]
2423
+ ch = Rant::Lib::parse_caller_elem(clr)
2424
+ name = nil
2425
+ pre = []
2426
+ ln = ch[:ln] || 0
2427
+ file = ch[:file]
2428
+
2429
+ pl_name = args.shift
2430
+ pl_name = pl_name.to_str if pl_name.respond_to? :to_str
2431
+ pl_name = pl_name.to_s if pl_name.is_a? Symbol
2432
+ unless pl_name.is_a? String
2433
+ abort(pos_text(file, ln),
2434
+ "Plugin name has to be a string or symbol.")
2435
+ end
2436
+ lc_pl_name = pl_name.downcase
2437
+ import_name = "plugin/#{lc_pl_name}"
2438
+ unless Rant::CODE_IMPORTS.include? import_name
2439
+ begin
2440
+ require "rant/plugin/#{lc_pl_name}"
2441
+ Rant::CODE_IMPORTS << import_name
2442
+ rescue LoadError
2443
+ abort(pos_text(file, ln),
2444
+ "no such plugin library -- #{lc_pl_name}")
2445
+ end
2446
+ end
2447
+ pl_class = nil
2448
+ begin
2449
+ pl_class = ::Rant::Plugin.const_get(pl_name)
2450
+ rescue NameError, ArgumentError
2451
+ abort(pos_text(file, ln),
2452
+ "no such plugin -- #{pl_name}")
2453
+ end
2454
+
2455
+ plugin = pl_class.rant_plugin_new(self, ch, *args, &block)
2456
+ @plugins << plugin
2457
+ vmsg 2, "Plugin `#{plugin.rant_plugin_name}' registered."
2458
+ plugin.rant_plugin_init
2459
+ plugin
2460
+ end
2461
+
2462
+ def enhance(targ, &block)
2463
+ prepare_task(targ, block) { |name,pre,blk|
2464
+ t = resolve(name).last
2465
+ if t
2466
+ unless t.respond_to? :enhance
2467
+ abort("Can't enhance task `#{name}'")
2468
+ end
2469
+ t.enhance(pre, &blk)
2470
+ return t
2471
+ end
2472
+ warn_msg "enhance \"#{name}\": no such task",
2473
+ "Generating a new file task with the given name."
2474
+ @node_factory.new_file(self, name, pre, blk)
2475
+ }
2476
+ end
2477
+
2478
+ def source(opt, rantfile = nil)
2479
+ unless rantfile
2480
+ rantfile = opt
2481
+ opt = nil
2482
+ end
2483
+ make_rf = opt != :n && opt != :now
2484
+ rf, is_new = rantfile_for_path(rantfile)
2485
+ return false unless is_new
2486
+ make rantfile if make_rf
2487
+ unless File.exist? rf.path
2488
+ abort("source: No such file -- #{rantfile}")
2489
+ end
2490
+
2491
+ load_file rf
2492
+ end
2493
+
2494
+ def subdirs(*args)
2495
+ args.flatten!
2496
+ ch = Rant::Lib::parse_caller_elem(caller[1])
2497
+ args.each { |arg|
2498
+ if arg.respond_to? :to_str
2499
+ arg = arg.to_str
2500
+ else
2501
+ abort_at(ch, "subdirs: arguments must be strings")
2502
+ end
2503
+ loaded = false
2504
+ prev_subdir = @current_subdir
2505
+ begin
2506
+ goto arg
2507
+ if test(?f, Rant::SUB_RANTFILE)
2508
+ path = Rant::SUB_RANTFILE
2509
+ else
2510
+ path = rantfile_in_dir
2511
+ end
2512
+ if path
2513
+ if defined? @initial_subdir and
2514
+ @initial_subdir == @current_subdir
2515
+ rf, is_new = rantfile_for_path(path, false)
2516
+ @rantfiles.unshift rf if is_new
2517
+ else
2518
+ rf, is_new = rantfile_for_path(path)
2519
+ end
2520
+ load_file rf if is_new
2521
+ elsif !@opts[:no_warn_subdir]
2522
+ warn_msg(pos_text(ch[:file], ch[:ln]),
2523
+ "subdirs: No Rantfile in subdir `#{arg}'.")
2524
+ end
2525
+ ensure
2526
+ goto_project_dir prev_subdir
2527
+ end
2528
+ }
2529
+ rescue SystemCallError => e
2530
+ abort_at(ch, "subdirs: " + e.message)
2531
+ end
2532
+
2533
+ def sys(*args, &block)
2534
+ args.empty? ? @sys : @sys.sh(*args, &block)
2535
+ end
2536
+
2537
+ def var(*args, &block)
2538
+ args.empty? ? @var : @var.query(*args, &block)
2539
+ end
2540
+
2541
+ def pop_desc
2542
+ td = @task_desc
2543
+ @task_desc = nil
2544
+ td
2545
+ end
2546
+
2547
+ def abort(*msg)
2548
+ err_msg(msg) unless msg.empty?
2549
+ $stderr.puts caller if @opts[:trace_abort]
2550
+ raise Rant::RantAbortException
2551
+ end
2552
+
2553
+ def abort_at(ch, *msg)
2554
+ err_msg(pos_text(ch[:file], ch[:ln]), msg)
2555
+ $stderr.puts caller if @opts[:trace_abort]
2556
+ raise Rant::RantAbortException
2557
+ end
2558
+
2559
+ def show_help
2560
+ puts "rant [-f Rantfile] [Options] [targets]"
2561
+ puts
2562
+ puts "Options are:"
2563
+ print option_listing(OPTIONS)
2564
+ end
2565
+
2566
+ def show_descriptions
2567
+ tlist = select_tasks { |t| t.description }
2568
+ def_target = target_list.first
2569
+ if tlist.empty?
2570
+ puts "rant # => " + list_task_names(
2571
+ resolve(def_target)).join(', ')
2572
+ msg "No described tasks."
2573
+ return
2574
+ end
2575
+ prefix = "./build.rb "
2576
+ infix = " # "
2577
+ name_length = (tlist.map{ |t| t.to_s.length } << 7).max
2578
+ cmd_length = prefix.length + name_length
2579
+ unless tlist.first.to_s == def_target
2580
+ defaults = list_task_names(
2581
+ resolve(def_target)).join(', ')
2582
+ puts "#{prefix}#{' ' * name_length}#{infix}=> #{defaults}"
2583
+ end
2584
+ tlist.each { |t|
2585
+ print(prefix + t.to_s.ljust(name_length) + infix)
2586
+ dt = t.description.sub(/\s+$/, "")
2587
+ puts dt.gsub(/\n/, "\n" + ' ' * cmd_length + infix + " ")
2588
+ }
2589
+ true
2590
+ end
2591
+
2592
+ def list_task_names(*tasks)
2593
+ rsl = []
2594
+ tasks.flatten.each { |t|
2595
+ if t.respond_to?(:has_actions?) && t.has_actions?
2596
+ rsl << t
2597
+ elsif t.respond_to? :prerequisites
2598
+ if t.prerequisites.empty?
2599
+ rsl << t
2600
+ else
2601
+ t.prerequisites.each { |pre|
2602
+ rsl.concat(list_task_names(
2603
+ resolve(pre, t.project_subdir)))
2604
+ }
2605
+ end
2606
+ else
2607
+ rsl << t
2608
+ end
2609
+ }
2610
+ rsl
2611
+ end
2612
+ private :list_task_names
2613
+
2614
+ def verbose
2615
+ @opts[:verbose]
2616
+ end
2617
+
2618
+ def quiet?
2619
+ @opts[:quiet]
2620
+ end
2621
+
2622
+ def pos_text(file, ln)
2623
+ t = "in file `#{file}'"
2624
+ t << ", line #{ln}" if ln && ln > 0
2625
+ t << ": "
2626
+ end
2627
+
2628
+ def cmd_msg(cmd)
2629
+ puts cmd unless quiet?
2630
+ end
2631
+
2632
+ def cmd_print(text)
2633
+ print text unless quiet?
2634
+ $stdout.flush
2635
+ end
2636
+
2637
+ def cmd_targets
2638
+ @force_targets + @arg_targets
2639
+ end
2640
+
2641
+ def running_task(task)
2642
+ if @current_subdir != @last_build_subdir
2643
+ cmd_msg "(in #{@current_subdir.empty? ?
2644
+ @rootdir : @current_subdir})"
2645
+ @last_build_subdir = @current_subdir
2646
+ end
2647
+ if @opts[:dry_run]
2648
+ task.dry_run
2649
+ true
2650
+ end
2651
+ end
2652
+
2653
+ private
2654
+ def have_any_task?
2655
+ !@tasks.empty?
2656
+ end
2657
+
2658
+ def target_list
2659
+ if !have_any_task? && @resolve_hooks.empty?
2660
+ abort("No tasks defined for this rant application!")
2661
+ end
2662
+
2663
+ target_list = @force_targets + @arg_targets
2664
+ if target_list.empty?
2665
+ def_tasks = resolve "default"
2666
+ unless def_tasks.empty?
2667
+ target_list << "default"
2668
+ else
2669
+ @rantfiles.each { |f|
2670
+ first = f.tasks.first
2671
+ if first
2672
+ target_list << first.reference_name
2673
+ break
2674
+ end
2675
+ }
2676
+ end
2677
+ end
2678
+ target_list
2679
+ end
2680
+
2681
+ def run_tasks
2682
+ target_list.each { |target|
2683
+ if build(target) == 0
2684
+ abort("Don't know how to make `#{target}'.")
2685
+ end
2686
+ }
2687
+ end
2688
+
2689
+ def make(target, *args, &block)
2690
+ ch = nil
2691
+ if target.respond_to? :to_hash
2692
+ targ = target.to_hash
2693
+ ch = Rant::Lib.parse_caller_elem(caller[1])
2694
+ abort_at(ch, "make: too many arguments") unless args.empty?
2695
+ tn = nil
2696
+ prepare_task(targ, block, ch) { |name,pre,blk|
2697
+ tn = name
2698
+ @node_factory.new_file(self, name, pre, blk)
2699
+ }
2700
+ build(tn)
2701
+ elsif target.respond_to? :to_rant_target
2702
+ rt = target.to_rant_target
2703
+ opt = args.shift
2704
+ unless args.empty?
2705
+ ch ||= Rant::Lib.parse_caller_elem(caller[1])
2706
+ abort_at(ch, "make: too many arguments")
2707
+ end
2708
+ if block
2709
+ ch ||= Rant::Lib.parse_caller_elem(caller[1])
2710
+ prepare_task(rt, block, ch) { |name,pre,blk|
2711
+ @node_factory.new_file(self, name, pre, blk)
2712
+ }
2713
+ build(rt)
2714
+ else
2715
+ build(rt, opt||{})
2716
+ end
2717
+ elsif target.respond_to? :rant_gen
2718
+ ch = Rant::Lib.parse_caller_elem(caller[1])
2719
+ rv = target.rant_gen(self, ch, args, &block)
2720
+ unless rv.respond_to? :to_rant_target
2721
+ abort_at(ch, "make: invalid generator return value")
2722
+ end
2723
+ build(rv.to_rant_target)
2724
+ rv
2725
+ else
2726
+ ch = Rant::Lib.parse_caller_elem(caller[1])
2727
+ abort_at(ch,
2728
+ "make: generator or target as first argument required.")
2729
+ end
2730
+ end
2731
+ public :make
2732
+
2733
+ def build(target, opt = {})
2734
+ opt[:force] = true if @force_targets.delete(target)
2735
+ opt[:dry_run] = @opts[:dry_run]
2736
+ matching_tasks = 0
2737
+ old_subdir = @current_subdir
2738
+ old_pwd = Dir.pwd
2739
+ resolve(target).each { |t|
2740
+ unless opt[:type] == :file && !t.file_target?
2741
+ matching_tasks += 1
2742
+ begin
2743
+ t.invoke(opt)
2744
+ rescue Rant::TaskFail => e
2745
+ err_task_fail(e)
2746
+ abort
2747
+ end
2748
+ end
2749
+ }
2750
+ @current_subdir = old_subdir
2751
+ Dir.chdir old_pwd
2752
+ matching_tasks
2753
+ end
2754
+ public :build
2755
+
2756
+ def resolve(task_name, rel_project_dir = @current_subdir)
2757
+ s = @tasks[expand_path(rel_project_dir, task_name)]
2758
+ case s
2759
+ when nil
2760
+ @resolve_hooks.each { |s|
2761
+ s = s[task_name, rel_project_dir]
2762
+ return s if s
2763
+ }
2764
+ []
2765
+ when Rant::Node: [s]
2766
+ else # assuming list of tasks
2767
+ s
2768
+ end
2769
+ end
2770
+ public :resolve
2771
+
2772
+ def rec_save_resolve(task_name, excl_hook, rel_project_dir = @current_subdir)
2773
+ s = @tasks[expand_path(rel_project_dir, task_name)]
2774
+ case s
2775
+ when nil
2776
+ @resolve_hooks.each { |s|
2777
+ next if s == excl_hook
2778
+ s = s[task_name, rel_project_dir]
2779
+ return s if s
2780
+ }
2781
+ []
2782
+ when Rant::Node: [s]
2783
+ else
2784
+ s
2785
+ end
2786
+ end
2787
+ public :rec_save_resolve
2788
+
2789
+ def at_resolve(&block)
2790
+ @resolve_hooks << block if block
2791
+ end
2792
+ public :at_resolve
2793
+
2794
+ def at_return(&block)
2795
+ hooks = var._get("__at_return__")
2796
+ if hooks
2797
+ hooks << block
2798
+ else
2799
+ var._set("__at_return__", [block])
2800
+ end
2801
+ end
2802
+ public :at_return
2803
+
2804
+ def select_tasks
2805
+ selection = []
2806
+ @rantfiles.each { |rf|
2807
+ rf.tasks.each { |t|
2808
+ selection << t if yield t
2809
+ }
2810
+ }
2811
+ selection
2812
+ end
2813
+ public :select_tasks
2814
+
2815
+ def load_rantfiles
2816
+ unless @arg_rantfiles.empty?
2817
+ @arg_rantfiles.each { |fn|
2818
+ if test(?f, fn)
2819
+ rf, is_new = rantfile_for_path(fn)
2820
+ load_file rf if is_new
2821
+ else
2822
+ abort "No such file -- #{fn}"
2823
+ end
2824
+ }
2825
+ return
2826
+ end
2827
+ return if have_any_task?
2828
+ fn = rantfile_in_dir
2829
+ if @opts[:cd_parent]
2830
+ old_root = @rootdir
2831
+ until fn or @rootdir == "/"
2832
+ @rootdir = File.dirname(@rootdir)
2833
+ fn = rantfile_in_dir(@rootdir)
2834
+ end
2835
+ if @rootdir != old_root and fn
2836
+ Dir.chdir @rootdir
2837
+ cmd_msg "(in #@rootdir)"
2838
+ end
2839
+ end
2840
+ if fn
2841
+ rf, is_new = rantfile_for_path(fn)
2842
+ load_file rf if is_new
2843
+ return
2844
+ end
2845
+ have_sub_rantfile = test(?f, Rant::SUB_RANTFILE)
2846
+ if have_sub_rantfile || @opts[:look_up]
2847
+ cur_dir = Dir.pwd
2848
+ until cur_dir == "/"
2849
+ cur_dir = File.dirname(cur_dir)
2850
+ Dir.chdir cur_dir
2851
+ fn = rantfile_in_dir
2852
+ if fn
2853
+ @initial_subdir = @rootdir.sub(
2854
+ /^#{Regexp.escape cur_dir}\//, '')
2855
+ @rootdir = cur_dir
2856
+ cmd_msg "(root is #@rootdir, in #@initial_subdir)"
2857
+ @last_build_subdir = @initial_subdir
2858
+ rf, is_new = rantfile_for_path(fn)
2859
+ load_file rf if is_new
2860
+ goto_project_dir @initial_subdir
2861
+ if have_sub_rantfile
2862
+ rf, is_new = rantfile_for_path(
2863
+ Rant::SUB_RANTFILE, false)
2864
+ if is_new
2865
+ @rantfiles.unshift rf
2866
+ load_file rf
2867
+ end
2868
+ end
2869
+ break
2870
+ end
2871
+ end
2872
+ end
2873
+ if @rantfiles.empty?
2874
+ abort("No Rantfile found, looking for:",
2875
+ Rant::RANTFILES.join(", "))
2876
+ end
2877
+ end
2878
+
2879
+ def load_file(rantfile)
2880
+ vmsg 1, "source #{rantfile}"
2881
+ @context.instance_eval(File.read(rantfile), rantfile)
2882
+ end
2883
+ private :load_file
2884
+
2885
+ def rantfile_in_dir(dir=nil)
2886
+ ::Rant::RANTFILES.each { |rfn|
2887
+ path = dir ? File.join(dir, rfn) : rfn
2888
+ return path if test ?f, path
2889
+ }
2890
+ nil
2891
+ end
2892
+
2893
+ def process_args
2894
+ old_argv = ARGV.dup
2895
+ ARGV.replace(@args.dup)
2896
+ cmd_opts = GetoptLong.new(*OPTIONS.collect { |lst| lst[0..-2] })
2897
+ cmd_opts.quiet = true
2898
+ cmd_opts.each { |opt, value|
2899
+ case opt
2900
+ when "--verbose": @opts[:verbose] += 1
2901
+ when "--version"
2902
+ puts "rant #{Rant::VERSION}"
2903
+ raise Rant::RantDoneException
2904
+ when "--help"
2905
+ show_help
2906
+ raise Rant::RantDoneException
2907
+ when "--directory"
2908
+ @rootdir = File.expand_path(value)
2909
+ when "--rantfile"
2910
+ @arg_rantfiles << value
2911
+ when "--force-run"
2912
+ @force_targets << value
2913
+ when "--import"
2914
+ import value
2915
+ else
2916
+ @opts[opt.sub(/^--/, '').tr('-', "_").to_sym] = true
2917
+ end
2918
+ }
2919
+ rescue GetoptLong::Error => e
2920
+ abort(e.message)
2921
+ ensure
2922
+ rem_args = ARGV.dup
2923
+ ARGV.replace(old_argv)
2924
+ rem_args.each { |ra|
2925
+ if ra =~ /(^[^=]+)=([^=]+)$/
2926
+ vmsg 2, "var: #$1=#$2"
2927
+ @var[$1] = $2
2928
+ else
2929
+ @arg_targets << ra
2930
+ end
2931
+ }
2932
+ end
2933
+
2934
+ def prepare_task(targ, block, clr = caller[2])
2935
+
2936
+ if targ.is_a? Hash
2937
+ targ.reject! { |k, v| clr = v if k == :__caller__ }
2938
+ end
2939
+ ch = Hash === clr ? clr : Rant::Lib::parse_caller_elem(clr)
2940
+
2941
+ name, pre = normalize_task_arg(targ, ch)
2942
+
2943
+ file, is_new = rantfile_for_path(ch[:file])
2944
+ nt = yield(name, pre, block)
2945
+ nt.rantfile = file
2946
+ nt.project_subdir = @current_subdir
2947
+ nt.line_number = ch[:ln]
2948
+ nt.description = @task_desc
2949
+ @task_desc = nil
2950
+ file.tasks << nt
2951
+ hash_task nt
2952
+ nt
2953
+ end
2954
+ public :prepare_task
2955
+
2956
+ def hash_task(task)
2957
+ n = task.full_name
2958
+ et = @tasks[n]
2959
+ case et
2960
+ when nil
2961
+ @tasks[n] = task
2962
+ when Rant::Node
2963
+ mt = [et, task]
2964
+ @tasks[n] = mt
2965
+ else # assuming list of tasks
2966
+ et << task
2967
+ end
2968
+ end
2969
+
2970
+ def normalize_task_arg(targ, ch)
2971
+ name = nil
2972
+ pre = []
2973
+
2974
+ if targ.is_a? Hash
2975
+ if targ.empty?
2976
+ abort_at(ch, "Empty hash as task argument, " +
2977
+ "task name required.")
2978
+ end
2979
+ if targ.size > 1
2980
+ abort_at(ch, "Too many hash elements, " +
2981
+ "should only be one.")
2982
+ end
2983
+ targ.each_pair { |k,v|
2984
+ name = normalize_task_name(k, ch)
2985
+ pre = v
2986
+ }
2987
+ unless ::Rant::FileList === pre
2988
+ if pre.respond_to? :to_ary
2989
+ pre = pre.to_ary.dup
2990
+ pre.map! { |elem|
2991
+ normalize_task_name(elem, ch)
2992
+ }
2993
+ else
2994
+ pre = [normalize_task_name(pre, ch)]
2995
+ end
2996
+ end
2997
+ else
2998
+ name = normalize_task_name(targ, ch)
2999
+ end
3000
+
3001
+ [name, pre]
3002
+ end
3003
+ public :normalize_task_arg
3004
+
3005
+ def normalize_task_name(arg, ch)
3006
+ return arg if arg.is_a? String
3007
+ if Symbol === arg
3008
+ arg.to_s
3009
+ elsif arg.respond_to? :to_str
3010
+ arg.to_str
3011
+ else
3012
+ abort_at(ch, "Task name has to be a string or symbol.")
3013
+ end
3014
+ end
3015
+
3016
+ def rantfile_for_path(path, register=true)
3017
+ abs_path = File.expand_path(path)
3018
+ file = @rantfiles.find { |rf| rf.path == abs_path }
3019
+ if file
3020
+ [file, false]
3021
+ else
3022
+ file = Rant::Rantfile.new abs_path
3023
+ file.project_subdir = @current_subdir
3024
+ @rantfiles << file if register
3025
+ [file, true]
3026
+ end
3027
+ end
3028
+
3029
+ def get_ch_from_backtrace(backtrace)
3030
+ backtrace.each { |clr|
3031
+ ch = ::Rant::Lib.parse_caller_elem(clr)
3032
+ if ::Rant::Env.on_windows?
3033
+ return ch if @rantfiles.any? { |rf|
3034
+ rf.path.tr("\\", "/").sub(/^\w\:/, '') ==
3035
+ ch[:file].tr("\\", "/").sub(/^\w\:/, '')
3036
+ }
3037
+ else
3038
+ return ch if @rantfiles.any? { |rf|
3039
+ rf.path == ch[:file]
3040
+ }
3041
+ end
3042
+ }
3043
+ nil
3044
+ end
3045
+
3046
+ def err_task_fail(e)
3047
+ msg = []
3048
+ t_msg = ["Task `#{e.tname}' fail."]
3049
+ orig = e
3050
+ loop { orig = orig.orig; break unless Rant::TaskFail === orig }
3051
+ if orig && orig != e && !(Rant::RantAbortException === orig)
3052
+ ch = get_ch_from_backtrace(orig.backtrace)
3053
+ msg << pos_text(ch[:file], ch[:ln]) if ch
3054
+ unless Rant::CommandError === orig && !@opts[:err_commands]
3055
+ msg << orig.message
3056
+ msg << orig.backtrace[0..4] unless ch
3057
+ end
3058
+ end
3059
+ if e.msg && !e.msg.empty?
3060
+ ch = get_ch_from_backtrace(e.backtrace)
3061
+ t_msg.unshift(e.msg)
3062
+ t_msg.unshift(pos_text(ch[:file], ch[:ln])) if ch
3063
+ end
3064
+ err_msg msg unless msg.empty?
3065
+ err_msg t_msg
3066
+ end
3067
+ end # class Rant::RantApp
3068
+
3069
+
3070
+
3071
+
3072
+ module Rant end
3073
+ module Rant::C
3074
+ module Include
3075
+ def parse_includes(src)
3076
+ if src.respond_to? :to_str
3077
+ src = src.to_str
3078
+ else
3079
+ raise ArgumentError, "src has to be a string"
3080
+ end
3081
+ s_includes = []
3082
+ l_includes = []
3083
+ in_block_comment = false
3084
+ prev_line = nil
3085
+ src.each { |line|
3086
+ line.chomp!
3087
+ if block_start_i = line.index("/*")
3088
+ c_start_i = line.index("//")
3089
+ if !c_start_i || block_start_i < c_start_i
3090
+ if block_end_i = line.index("*/")
3091
+ if block_end_i > block_start_i
3092
+ line[block_start_i..block_end_i+1] = ""
3093
+ end
3094
+ end
3095
+ end
3096
+ end
3097
+ if prev_line
3098
+ line = prev_line << line
3099
+ prev_line = nil
3100
+ end
3101
+ if line =~ /\\$/
3102
+ prev_line = line.chomp[0...line.length-1]
3103
+ end
3104
+ if in_block_comment
3105
+ in_block_comment = false if line =~ %r|\*/|
3106
+ next
3107
+ end
3108
+ case line
3109
+ when /\s*#\s*include\s+"([^"]+)"/
3110
+ l_includes << $1
3111
+ when /\s*#\s*include\s+<([^>]+)>/
3112
+ s_includes << $1
3113
+ when %r|(?!//)[^/]*/\*|
3114
+ in_block_comment = true
3115
+ end
3116
+ }
3117
+ [s_includes, l_includes]
3118
+ end
3119
+ module_function :parse_includes
3120
+ end # module Include
3121
+ end # module Rant::C
3122
+
3123
+ module Rant::Generators::C end
3124
+ class Rant::Generators::C::Dependencies
3125
+ def self.rant_gen(rac, ch, args, &block)
3126
+ c_files, out_fn, include_pathes, opts = nil
3127
+ if block
3128
+ rac.warn_msg "C::Dependencies: ignoring block"
3129
+ end
3130
+ case args.size
3131
+ when 0 # noop
3132
+ when 1
3133
+ farg = args.first
3134
+ Hash === farg ? (opts = farg) : (out_fn = farg)
3135
+ when 2
3136
+ out_fn = args.first
3137
+ opts = args[1]
3138
+ else
3139
+ rac.abort_at(ch,
3140
+ "C::Dependencies takes one or two arguments.")
3141
+ end
3142
+ correct_case = false
3143
+ if opts
3144
+ if opts.respond_to? :to_hash
3145
+ opts = opts.to_hash
3146
+ else
3147
+ rac.abort_at(ch,
3148
+ "C::Dependencies: second argument has to be a hash.")
3149
+ end
3150
+ opts.each { |k, v|
3151
+ case k
3152
+ when :sources
3153
+ c_files = v
3154
+ when :search, :search_pathes, :include_pathes
3155
+ include_pathes =
3156
+ if v.respond_to? :to_str
3157
+ [v.to_str]
3158
+ else
3159
+ v
3160
+ end
3161
+ when :correct_case
3162
+ correct_case = !!v
3163
+ else
3164
+ rac.abort_at(ch,
3165
+ "C::Dependencies: no such option -- #{k}")
3166
+ end
3167
+ }
3168
+ end
3169
+ out_fn ||= "c_dependencies"
3170
+ c_files ||= rac.cx.sys["**/*.{c,cpp,cc,h,hpp}"]
3171
+ include_pathes ||= ["."]
3172
+ if out_fn.respond_to? :to_str
3173
+ out_fn = out_fn.to_str
3174
+ else
3175
+ rac.abort_at(ch, "filename has to be a string")
3176
+ end
3177
+ unless ::Rant::FileList === c_files
3178
+ if c_files.respond_to? :to_ary
3179
+ c_files = c_files.to_ary
3180
+ else
3181
+ rac.abort_at(ch, "sources has to be a list of files")
3182
+ end
3183
+ end
3184
+ unless ::Rant::FileList === include_pathes
3185
+ if include_pathes.respond_to? :to_ary
3186
+ include_pathes = include_pathes.to_ary
3187
+ else
3188
+ rac.abort_at(ch,
3189
+ "search has to be a list of directories")
3190
+ end
3191
+ end
3192
+ rac.cx.file({:__caller__ => ch, out_fn => c_files}) do |t|
3193
+ tmp_rac = ::Rant::RantApp.new
3194
+ depfile_ts = Time.at(0)
3195
+ if File.exist? t.name
3196
+ tmp_rac.source(t.name)
3197
+ depfile_ts = File.mtime(t.name)
3198
+ end
3199
+ rf_str = ""
3200
+ c_files.each { |cf|
3201
+ f_task = nil
3202
+ unless test(?f, cf)
3203
+ rac.warn_msg "#{t.name}: no such file -- #{cf}"
3204
+ next
3205
+ end
3206
+ f_task = tmp_rac.tasks[cf.to_str]
3207
+ deps = f_task ? f_task.prerequisites : nil
3208
+ if !deps or File.mtime(cf) > depfile_ts
3209
+ rac.cmd_msg "scanning #{cf}"
3210
+ std_includes, local_includes =
3211
+ ::Rant::C::Include.parse_includes(File.read(cf))
3212
+ deps = []
3213
+ (std_includes + local_includes).each { |fn|
3214
+ path = existing_file(
3215
+ include_pathes, fn, correct_case)
3216
+ deps << path if path
3217
+ }
3218
+ end
3219
+ rf_str << file_deps(cf, deps) << "\n"
3220
+ }
3221
+ rac.vmsg 1, "writing C source dependencies to #{t.name}"
3222
+ open(t.name, "w") { |f|
3223
+ f.puts
3224
+ f.puts "# #{t.name}"
3225
+ f.puts "# C source dependencies generated by Rant #{Rant::VERSION}"
3226
+ f.puts "# WARNING: Modifications to this file will get lost!"
3227
+ f.puts
3228
+ f.write rf_str
3229
+ }
3230
+ end
3231
+ end
3232
+ def self.existing_file(dirs, fn, correct_case)
3233
+ dirs.each { |dir|
3234
+ path = dir == "." ? fn : File.join(dir, fn)
3235
+ if test ?f, path
3236
+ return path unless correct_case
3237
+ found_file = File.basename(fn)
3238
+ found_in_dir = File.dirname(File.join(dir, fn))
3239
+ Dir.entries(found_in_dir).each { |dentry|
3240
+ return File.join(found_in_dir, dentry) if dentry.downcase == found_file.downcase
3241
+ }
3242
+ end
3243
+ }
3244
+ nil
3245
+ end
3246
+ def self.file_deps(target, deps)
3247
+ s = "gen SourceNode, #{target.to_str.dump} => "
3248
+ s << "[#{ deps.map{ |fn| fn.to_str.dump }.join(', ')}]"
3249
+ end
3250
+ end # class Rant::Generators::C::Dependencies
3251
+
3252
+
3253
+
3254
+ class Rant::Generators::DirectedRule
3255
+ def self.rant_gen(rac, ch, args, &block)
3256
+ unless args.size == 1
3257
+ rac.abort_at(ch, "DirectedRule takes one arguments.")
3258
+ end
3259
+ h = args.first
3260
+ if h.respond_to? :to_hash
3261
+ h = h.to_hash
3262
+ else
3263
+ rac.abort_at(ch, "Argument has to be a hash.")
3264
+ end
3265
+ ts_h, dir_h = nil, nil
3266
+ h.each { |k, v| v.respond_to?(:to_ary) ?
3267
+ dir_h = { k => v } :
3268
+ ts_h = { k => v }
3269
+ }
3270
+ unless dir_h
3271
+ rac.abort_at(ch,
3272
+ "Source directory argument has to be a list.")
3273
+ end
3274
+ target, source = nil, nil
3275
+ ts_h.each { |target, source| }
3276
+ target_dir, source_dirs = nil, nil
3277
+ dir_h.each { |target_dir, source_dirs| }
3278
+ if target_dir.respond_to? :to_str
3279
+ target_dir = target_dir.to_str
3280
+ else
3281
+ rac.abort_at(ch, "String required as target directory.")
3282
+ end
3283
+ if source_dirs.respond_to? :to_ary
3284
+ source_dirs = source_dirs.to_ary
3285
+ elsif source_dirs.respond_to? :to_str
3286
+ source_dirs = [source_dirs.to_str]
3287
+ else
3288
+ rac.abort_at(ch,
3289
+ "List of strings or string required for source directories.")
3290
+ end
3291
+ target = ".#{target}" if Symbol === target
3292
+ source = ".#{source}" if Symbol === source
3293
+ if target.respond_to? :to_str
3294
+ target = target.to_str
3295
+ else
3296
+ rac.abort_at(ch, "target has to be a string")
3297
+ end
3298
+ if source.respond_to? :to_str
3299
+ source = source.to_str
3300
+ else
3301
+ rac.abort_at(ch, "source has to be a string or symbol")
3302
+ end
3303
+ blk = self.new(rac, ch, target_dir, source_dirs,
3304
+ target, source, &block)
3305
+ blk.define_hook
3306
+ blk
3307
+ end
3308
+ def initialize(rac, ch, target_dir, source_dirs,
3309
+ target, source, &block)
3310
+ @rac = rac
3311
+ @ch = ch
3312
+ @source_dirs = source_dirs
3313
+ @target_dir = target_dir
3314
+ @target = target.sub(/^\./, '')
3315
+ @target_rx = /#{Regexp.escape(target)}$/
3316
+ @source = source.sub(/^\./, '')
3317
+ @esc_target_dir = Regexp.escape(target_dir)
3318
+ @block = block
3319
+ end
3320
+ def [](name, rel_project_dir)
3321
+ if name =~ /^#@esc_target_dir\// && name =~ @target_rx
3322
+ fn = File.basename(name)
3323
+ src_fn = fn.sub_ext(@source)
3324
+ src = nil
3325
+ @source_dirs.each { |d|
3326
+ path = File.join(d, src_fn)
3327
+ (src = path) && break if test(?e, path)
3328
+ }
3329
+ if src
3330
+ [@rac.prepare_task({name => src}, @block, @ch) { |name,pre,blk|
3331
+ @rac.node_factory.new_auto_subfile(@rac, name, pre, blk)
3332
+ }]
3333
+ else
3334
+ nil
3335
+ end
3336
+ else
3337
+ nil
3338
+ end
3339
+ end
3340
+ def define_hook
3341
+ @rac.resolve_hooks << self
3342
+ end
3343
+ def each_target(&block)
3344
+ @rac.cx.sys["#@target_dir/*"].each { |entry|
3345
+ yield entry if entry =~ @target_rx
3346
+ }
3347
+ end
3348
+ def candidates
3349
+ sources.map { |src|
3350
+ File.join(@target_dir, File.basename(src).sub_ext(@target))
3351
+ }
3352
+ end
3353
+ def sources
3354
+ cl = []
3355
+ @source_dirs.each { |dir|
3356
+ cl.concat(@rac.cx.sys["#{dir}/*.#@source"])
3357
+ }
3358
+ cl
3359
+ end
3360
+ end # class Rant::Generators::DirectedRule
3361
+
3362
+
3363
+
3364
+ class Rant::Generators::Clean
3365
+ def self.rant_gen(rac, ch, args, &block)
3366
+ if args.size > 1
3367
+ rac.abort_at(ch, "Clean doesn't take more than one argument.")
3368
+ end
3369
+ tname = args.first || "clean"
3370
+
3371
+ case rac.var[tname]
3372
+ when nil
3373
+ rac.var[tname] = Rant::MultiFileList.new(rac)
3374
+ when Rant::RacFileList
3375
+ ml = Rant::MultiFileList.new(rac)
3376
+ rac.var[tname] = ml.add(rac.var[tname])
3377
+ when Rant::MultiFileList
3378
+ else
3379
+ rac.abort_at(ch,
3380
+ "var `#{tname}' already exists.",
3381
+ "Clean uses var with the same name as the task name.")
3382
+ end
3383
+
3384
+ rac.task :__caller__ => ch, tname => [] do |t|
3385
+ rac.var[tname].each_entry { |entry|
3386
+ if test ?e, entry
3387
+ if test ?f, entry
3388
+ rac.cx.sys.rm_f entry
3389
+ else
3390
+ rac.cx.sys.rm_rf entry
3391
+ end
3392
+ end
3393
+ }
3394
+ end
3395
+ end
3396
+ end # class Rant::Generators::Clean
3397
+
3398
+
3399
+
3400
+
3401
+ module Rant; end
3402
+ module Rant::Archive; end
3403
+ module Rant::Archive::Minitar; end
3404
+
3405
+ class Rant::Archive::Minitar::PosixHeader
3406
+ FIELDS = %w(name mode uid gid size mtime checksum typeflag linkname) +
3407
+ %w(magic version uname gname devmajor devminor prefix)
3408
+
3409
+ FIELDS.each { |field| attr_reader field.intern }
3410
+
3411
+ HEADER_PACK_FORMAT = "a100a8a8a8a12a12a7aaa100a6a2a32a32a8a8a155"
3412
+ HEADER_UNPACK_FORMAT = "Z100A8A8A8A12A12A8aZ100A6A2Z32Z32A8A8Z155"
3413
+
3414
+ def self.new_from_stream(stream)
3415
+ data = stream.read(512)
3416
+ fields = data.unpack(HEADER_UNPACK_FORMAT)
3417
+ name = fields.shift
3418
+ mode = fields.shift.oct
3419
+ uid = fields.shift.oct
3420
+ gid = fields.shift.oct
3421
+ size = fields.shift.oct
3422
+ mtime = fields.shift.oct
3423
+ checksum = fields.shift.oct
3424
+ typeflag = fields.shift
3425
+ linkname = fields.shift
3426
+ magic = fields.shift
3427
+ version = fields.shift.oct
3428
+ uname = fields.shift
3429
+ gname = fields.shift
3430
+ devmajor = fields.shift.oct
3431
+ devminor = fields.shift.oct
3432
+ prefix = fields.shift
3433
+
3434
+ empty = (data == "\0" * 512)
3435
+
3436
+ new(:name => name, :mode => mode, :uid => uid, :gid => gid,
3437
+ :size => size, :mtime => mtime, :checksum => checksum,
3438
+ :typeflag => typeflag, :magic => magic, :version => version,
3439
+ :uname => uname, :gname => gname, :devmajor => devmajor,
3440
+ :devminor => devminor, :prefix => prefix, :empty => empty)
3441
+ end
3442
+
3443
+ def initialize(vals)
3444
+ unless vals[:name] && vals[:size] && vals[:prefix] && vals[:mode]
3445
+ raise ArgumentError
3446
+ end
3447
+
3448
+ vals[:mtime] ||= 0
3449
+ vals[:checksum] ||= ""
3450
+ vals[:typeflag] ||= "0"
3451
+ vals[:magic] ||= "ustar"
3452
+ vals[:version] ||= "00"
3453
+
3454
+ FIELDS.each do |field|
3455
+ instance_variable_set("@#{field}", vals[field.intern])
3456
+ end
3457
+ @empty = vals[:empty]
3458
+ end
3459
+
3460
+ def empty?
3461
+ @empty
3462
+ end
3463
+
3464
+ def to_s
3465
+ update_checksum
3466
+ header(@checksum)
3467
+ end
3468
+
3469
+ def update_checksum
3470
+ hh = header(" " * 8)
3471
+ @checksum = oct(calculate_checksum(hh), 6)
3472
+ end
3473
+
3474
+ private
3475
+ def oct(num, len)
3476
+ if num.nil?
3477
+ "\0" * (len + 1)
3478
+ else
3479
+ "%0#{len}o" % num
3480
+ end
3481
+ end
3482
+
3483
+ def calculate_checksum(hdr)
3484
+ hdr.unpack("C*").inject { |aa, bb| aa + bb }
3485
+ end
3486
+
3487
+ def header(chksum)
3488
+ arr = [name, oct(mode, 7), oct(uid, 7), oct(gid, 7), oct(size, 11),
3489
+ oct(mtime, 11), chksum, " ", typeflag, linkname, magic, version,
3490
+ uname, gname, oct(devmajor, 7), oct(devminor, 7), prefix]
3491
+ str = arr.pack(HEADER_PACK_FORMAT)
3492
+ str + "\0" * ((512 - str.size) % 512)
3493
+ end
3494
+ end
3495
+
3496
+ require 'fileutils'
3497
+ require 'find'
3498
+
3499
+ module Rant::Archive::Minitar
3500
+ VERSION = "0.5.1"
3501
+
3502
+ class NonSeekableStream < StandardError; end
3503
+ class BlockRequired < ArgumentError; end
3504
+ class ClosedStream < StandardError; end
3505
+ class FileNameTooLong < StandardError; end
3506
+ class UnexpectedEOF < StandardError; end
3507
+
3508
+ class Writer
3509
+ class RestrictedStream
3510
+ def initialize(anIO)
3511
+ @io = anIO
3512
+ end
3513
+
3514
+ def write(data)
3515
+ @io.write(data)
3516
+ end
3517
+ end
3518
+
3519
+ class BoundedStream < Rant::Archive::Minitar::Writer::RestrictedStream
3520
+ class FileOverflow < RuntimeError; end
3521
+
3522
+ attr_reader :limit
3523
+ attr_reader :written
3524
+
3525
+ def initialize(io, limit)
3526
+ @io = io
3527
+ @limit = limit
3528
+ @written = 0
3529
+ end
3530
+
3531
+ def write(data)
3532
+ raise FileOverflow if (data.size + @written) > @limit
3533
+ @io.write(data)
3534
+ @written += data.size
3535
+ data.size
3536
+ end
3537
+ end
3538
+
3539
+ def self.open(anIO)
3540
+ writer = Writer.new(anIO)
3541
+
3542
+ return writer unless block_given?
3543
+
3544
+ begin
3545
+ res = yield writer
3546
+ ensure
3547
+ writer.close
3548
+ end
3549
+
3550
+ res
3551
+ end
3552
+
3553
+ def initialize(anIO)
3554
+ @io = anIO
3555
+ @closed = false
3556
+ end
3557
+
3558
+ def add_file_simple(name, opts = {}) # :yields BoundedStream:
3559
+ raise Rant::Archive::Minitar::BlockRequired unless block_given?
3560
+ raise Rant::Archive::Minitar::ClosedStream if @closed
3561
+
3562
+ name, prefix = split_name(name)
3563
+
3564
+ header = { :name => name, :mode => opts[:mode], :mtime => opts[:mtime],
3565
+ :size => opts[:size], :gid => opts[:gid], :uid => opts[:uid],
3566
+ :prefix => prefix }
3567
+ header = Rant::Archive::Minitar::PosixHeader.new(header).to_s
3568
+ @io.write(header)
3569
+
3570
+ os = BoundedStream.new(@io, opts[:size])
3571
+ yield os
3572
+
3573
+ min_padding = opts[:size] - os.written
3574
+ @io.write("\0" * min_padding)
3575
+ remainder = (512 - (opts[:size] % 512)) % 512
3576
+ @io.write("\0" * remainder)
3577
+ end
3578
+
3579
+ def add_file(name, opts = {}) # :yields RestrictedStream, +opts+:
3580
+ raise Rant::Archive::Minitar::BlockRequired unless block_given?
3581
+ raise Rant::Archive::Minitar::ClosedStream if @closed
3582
+ raise Rant::Archive::Minitar::NonSeekableStream unless @io.respond_to?(:pos=)
3583
+
3584
+ name, prefix = split_name(name)
3585
+ init_pos = @io.pos
3586
+ @io.write("\0" * 512) # placeholder for the header
3587
+
3588
+ yield RestrictedStream.new(@io), opts
3589
+
3590
+ size = @io.pos - (init_pos + 512)
3591
+ remainder = (512 - (size % 512)) % 512
3592
+ @io.write("\0" * remainder)
3593
+
3594
+ final_pos = @io.pos
3595
+ @io.pos = init_pos
3596
+
3597
+ header = { :name => name, :mode => opts[:mode], :mtime => opts[:mtime],
3598
+ :size => size, :gid => opts[:gid], :uid => opts[:uid],
3599
+ :prefix => prefix }
3600
+ header = Rant::Archive::Minitar::PosixHeader.new(header).to_s
3601
+ @io.write(header)
3602
+ @io.pos = final_pos
3603
+ end
3604
+
3605
+ def mkdir(name, opts = {})
3606
+ raise ClosedStream if @closed
3607
+ name, prefix = split_name(name)
3608
+ header = { :name => name, :mode => opts[:mode], :typeflag => "5",
3609
+ :size => 0, :gid => opts[:gid], :uid => opts[:uid],
3610
+ :mtime => opts[:mtime], :prefix => prefix }
3611
+ header = Rant::Archive::Minitar::PosixHeader.new(header).to_s
3612
+ @io.write(header)
3613
+ nil
3614
+ end
3615
+
3616
+ def flush
3617
+ raise ClosedStream if @closed
3618
+ @io.flush if @io.respond_to?(:flush)
3619
+ end
3620
+
3621
+ def close
3622
+ return if @closed
3623
+ @io.write("\0" * 1024)
3624
+ @closed = true
3625
+ end
3626
+
3627
+ private
3628
+ def split_name(name)
3629
+ raise FileNameTooLong if name.size > 256
3630
+ if name.size <= 100
3631
+ prefix = ""
3632
+ else
3633
+ parts = name.split(/\//)
3634
+ newname = parts.pop
3635
+
3636
+ nxt = ""
3637
+
3638
+ loop do
3639
+ nxt = parts.pop
3640
+ break if newname.size + 1 + nxt.size > 100
3641
+ newname = "#{nxt}/#{newname}"
3642
+ end
3643
+
3644
+ prefix = (parts + [nxt]).join("/")
3645
+
3646
+ name = newname
3647
+
3648
+ raise FileNameTooLong if name.size > 100 || prefix.size > 155
3649
+ end
3650
+ return name, prefix
3651
+ end
3652
+ end
3653
+
3654
+ class Reader
3655
+ module InvalidEntryStream
3656
+ def read(len = nil); raise ClosedStream; end
3657
+ def getc; raise ClosedStream; end
3658
+ def rewind; raise ClosedStream; end
3659
+ end
3660
+
3661
+ class EntryStream
3662
+ Rant::Archive::Minitar::PosixHeader::FIELDS.each do |field|
3663
+ attr_reader field.intern
3664
+ end
3665
+
3666
+ def initialize(header, anIO)
3667
+ @io = anIO
3668
+ @name = header.name
3669
+ @mode = header.mode
3670
+ @uid = header.uid
3671
+ @gid = header.gid
3672
+ @size = header.size
3673
+ @mtime = header.mtime
3674
+ @checksum = header.checksum
3675
+ @typeflag = header.typeflag
3676
+ @linkname = header.linkname
3677
+ @magic = header.magic
3678
+ @version = header.version
3679
+ @uname = header.uname
3680
+ @gname = header.gname
3681
+ @devmajor = header.devmajor
3682
+ @devminor = header.devminor
3683
+ @prefix = header.prefix
3684
+ @read = 0
3685
+ @orig_pos = @io.pos
3686
+ end
3687
+
3688
+ def read(len = nil)
3689
+ return nil if @read >= @size
3690
+ len ||= @size - @read
3691
+ max_read = [len, @size - @read].min
3692
+ ret = @io.read(max_read)
3693
+ @read += ret.size
3694
+ ret
3695
+ end
3696
+
3697
+ def getc
3698
+ return nil if @read >= @size
3699
+ ret = @io.getc
3700
+ @read += 1 if ret
3701
+ ret
3702
+ end
3703
+
3704
+ def directory?
3705
+ @typeflag == "5"
3706
+ end
3707
+ alias_method :directory, :directory?
3708
+
3709
+ def file?
3710
+ @typeflag == "0"
3711
+ end
3712
+ alias_method :file, :file?
3713
+
3714
+ def eof?
3715
+ @read >= @size
3716
+ end
3717
+
3718
+ def pos
3719
+ @read
3720
+ end
3721
+
3722
+ def rewind
3723
+ raise NonSeekableStream unless @io.respond_to?(:pos=)
3724
+ @io.pos = @orig_pos
3725
+ @read = 0
3726
+ end
3727
+
3728
+ def bytes_read
3729
+ @read
3730
+ end
3731
+
3732
+ def full_name
3733
+ if @prefix != ""
3734
+ File.join(@prefix, @name)
3735
+ else
3736
+ @name
3737
+ end
3738
+ end
3739
+
3740
+ def close
3741
+ invalidate
3742
+ end
3743
+
3744
+ private
3745
+ def invalidate
3746
+ extend InvalidEntryStream
3747
+ end
3748
+ end
3749
+
3750
+ def self.open(anIO)
3751
+ reader = Reader.new(anIO)
3752
+
3753
+ return reader unless block_given?
3754
+
3755
+ begin
3756
+ res = yield reader
3757
+ ensure
3758
+ reader.close
3759
+ end
3760
+
3761
+ res
3762
+ end
3763
+
3764
+ def initialize(anIO)
3765
+ @io = anIO
3766
+ @init_pos = anIO.pos
3767
+ end
3768
+
3769
+ def each(&block)
3770
+ each_entry(&block)
3771
+ end
3772
+
3773
+ def rewind
3774
+ if @init_pos == 0
3775
+ raise NonSeekableStream unless @io.respond_to?(:rewind)
3776
+ @io.rewind
3777
+ else
3778
+ raise NonSeekableStream unless @io.respond_to?(:pos=)
3779
+ @io.pos = @init_pos
3780
+ end
3781
+ end
3782
+
3783
+ def each_entry
3784
+ loop do
3785
+ return if @io.eof?
3786
+
3787
+ header = Rant::Archive::Minitar::PosixHeader.new_from_stream(@io)
3788
+ return if header.empty?
3789
+
3790
+ entry = EntryStream.new(header, @io)
3791
+ size = entry.size
3792
+
3793
+ yield entry
3794
+
3795
+ skip = (512 - (size % 512)) % 512
3796
+
3797
+ if @io.respond_to?(:seek)
3798
+ @io.seek(size - entry.bytes_read, IO::SEEK_CUR)
3799
+ else
3800
+ pending = size - entry.bytes_read
3801
+ while pending > 0
3802
+ bread = @io.read([pending, 4096].min).size
3803
+ raise UnexpectedEOF if @io.eof?
3804
+ pending -= bread
3805
+ end
3806
+ end
3807
+ @io.read(skip) # discard trailing zeros
3808
+ entry.close
3809
+ end
3810
+ end
3811
+
3812
+ def close
3813
+ end
3814
+ end
3815
+
3816
+ class Input
3817
+ include Enumerable
3818
+
3819
+ def self.open(input)
3820
+ stream = Input.new(input)
3821
+ return stream unless block_given?
3822
+
3823
+ begin
3824
+ res = yield stream
3825
+ ensure
3826
+ stream.close
3827
+ end
3828
+
3829
+ res
3830
+ end
3831
+
3832
+ def initialize(input)
3833
+ if input.respond_to?(:read)
3834
+ @io = input
3835
+ else
3836
+ @io = open(input, "rb")
3837
+ end
3838
+ @tarreader = Rant::Archive::Minitar::Reader.new(@io)
3839
+ end
3840
+
3841
+ def each(&block)
3842
+ @tarreader.each { |entry| yield entry }
3843
+ ensure
3844
+ @tarreader.rewind
3845
+ end
3846
+
3847
+ def extract_entry(destdir, entry) # :yields action, name, stats:
3848
+ stats = {
3849
+ :current => 0,
3850
+ :currinc => 0,
3851
+ :entry => entry
3852
+ }
3853
+
3854
+ if entry.directory?
3855
+ dest = File.join(destdir, entry.full_name)
3856
+
3857
+ yield :dir, entry.full_name, stats if block_given?
3858
+
3859
+ if Rant::Archive::Minitar.dir?(dest)
3860
+ begin
3861
+ FileUtils.chmod(entry.mode, dest)
3862
+ rescue Exception
3863
+ nil
3864
+ end
3865
+ else
3866
+ FileUtils.mkdir_p(dest, :mode => entry.mode)
3867
+ FileUtils.chmod(entry.mode, dest)
3868
+ end
3869
+
3870
+ fsync_dir(dest)
3871
+ fsync_dir(File.join(dest, ".."))
3872
+ return
3873
+ else # it's a file
3874
+ destdir = File.join(destdir, File.dirname(entry.full_name))
3875
+ FileUtils.mkdir_p(destdir, :mode => 0755)
3876
+
3877
+ destfile = File.join(destdir, File.basename(entry.full_name))
3878
+ FileUtils.chmod(0600, destfile) rescue nil # Errno::ENOENT
3879
+
3880
+ yield :file_start, entry.full_name, stats if block_given?
3881
+
3882
+ File.open(destfile, "wb", entry.mode) do |os|
3883
+ loop do
3884
+ data = entry.read(4096)
3885
+ break unless data
3886
+
3887
+ stats[:currinc] = os.write(data)
3888
+ stats[:current] += stats[:currinc]
3889
+
3890
+ yield :file_progress, entry.full_name, stats if block_given?
3891
+ end
3892
+ os.fsync
3893
+ end
3894
+
3895
+ FileUtils.chmod(entry.mode, destfile)
3896
+ fsync_dir(File.dirname(destfile))
3897
+ fsync_dir(File.join(File.dirname(destfile), ".."))
3898
+
3899
+ yield :file_done, entry.full_name, stats if block_given?
3900
+ end
3901
+ end
3902
+
3903
+ def tar
3904
+ @tarreader
3905
+ end
3906
+
3907
+ def close
3908
+ @io.close
3909
+ @tarreader.close
3910
+ end
3911
+
3912
+ private
3913
+ def fsync_dir(dirname)
3914
+ dir = open(dirname, 'rb')
3915
+ dir.fsync
3916
+ rescue # ignore IOError if it's an unpatched (old) Ruby
3917
+ nil
3918
+ ensure
3919
+ dir.close if dir rescue nil
3920
+ end
3921
+ end
3922
+
3923
+ class Output
3924
+ def self.open(output)
3925
+ stream = Output.new(output)
3926
+ return stream unless block_given?
3927
+
3928
+ begin
3929
+ res = yield stream
3930
+ ensure
3931
+ stream.close
3932
+ end
3933
+
3934
+ res
3935
+ end
3936
+
3937
+ def initialize(output)
3938
+ if output.respond_to?(:write)
3939
+ @io = output
3940
+ else
3941
+ @io = ::File.open(output, "wb")
3942
+ end
3943
+ @tarwriter = Rant::Archive::Minitar::Writer.new(@io)
3944
+ end
3945
+
3946
+ def tar
3947
+ @tarwriter
3948
+ end
3949
+
3950
+ def close
3951
+ @tarwriter.close
3952
+ @io.close
3953
+ end
3954
+ end
3955
+
3956
+ class << self
3957
+ def dir?(path)
3958
+ File.directory?((path[-1] == ?/) ? path : "#{path}/")
3959
+ end
3960
+
3961
+ def open(dest, mode = "r", &block)
3962
+ case mode
3963
+ when "r"
3964
+ Input.open(dest, &block)
3965
+ when "w"
3966
+ Output.open(dest, &block)
3967
+ else
3968
+ raise "Unknown open mode for Rant::Archive::Minitar.open."
3969
+ end
3970
+ end
3971
+
3972
+ def pack_file(entry, outputter) #:yields action, name, stats:
3973
+ outputter = outputter.tar if outputter.kind_of?(Rant::Archive::Minitar::Output)
3974
+
3975
+ stats = {}
3976
+
3977
+ if entry.kind_of?(Hash)
3978
+ name = entry[:name]
3979
+
3980
+ entry.each { |kk, vv| stats[kk] = vv unless vv.nil? }
3981
+ else
3982
+ name = entry
3983
+ end
3984
+
3985
+ name = name.sub(%r{\./}, '')
3986
+ stat = File.stat(name)
3987
+ stats[:mode] ||= stat.mode
3988
+ stats[:mtime] ||= stat.mtime
3989
+ stats[:size] = stat.size
3990
+
3991
+ if RUBY_PLATFORM =~ /win32/
3992
+ stats[:uid] = nil
3993
+ stats[:gid] = nil
3994
+ else
3995
+ stats[:uid] ||= stat.uid
3996
+ stats[:gid] ||= stat.gid
3997
+ end
3998
+
3999
+ case
4000
+ when File.file?(name)
4001
+ outputter.add_file_simple(name, stats) do |os|
4002
+ stats[:current] = 0
4003
+ yield :file_start, name, stats if block_given?
4004
+ File.open(name, "rb") do |ff|
4005
+ until ff.eof?
4006
+ stats[:currinc] = os.write(ff.read(4096))
4007
+ stats[:current] += stats[:currinc]
4008
+ yield :file_progress, name, stats if block_given?
4009
+ end
4010
+ end
4011
+ yield :file_done, name, stats if block_given?
4012
+ end
4013
+ when dir?(name)
4014
+ yield :dir, name, stats if block_given?
4015
+ outputter.mkdir(name, stats)
4016
+ else
4017
+ raise "Don't yet know how to pack this type of file."
4018
+ end
4019
+ end
4020
+
4021
+ def pack(src, dest, recurse_dirs = true, &block)
4022
+ Output.open(dest) do |outp|
4023
+ if src.kind_of?(Array)
4024
+ src.each do |entry|
4025
+ pack_file(entry, outp, &block)
4026
+ if dir?(entry) and recurse_dirs
4027
+ Dir["#{entry}/**/**"].each do |ee|
4028
+ pack_file(ee, outp, &block)
4029
+ end
4030
+ end
4031
+ end
4032
+ else
4033
+ Find.find(src) do |entry|
4034
+ pack_file(entry, outp, &block)
4035
+ end
4036
+ end
4037
+ end
4038
+ end
4039
+
4040
+ def unpack(src, dest, files = [], &block)
4041
+ Input.open(src) do |inp|
4042
+ if File.exist?(dest) and (not dir?(dest))
4043
+ raise "Can't unpack to a non-directory."
4044
+ elsif not File.exist?(dest)
4045
+ FileUtils.mkdir_p(dest)
4046
+ end
4047
+
4048
+ inp.each do |entry|
4049
+ if files.empty? or files.include?(entry.full_name)
4050
+ inp.extract_entry(dest, entry, &block)
4051
+ end
4052
+ end
4053
+ end
4054
+ end
4055
+ end
4056
+ end
4057
+
4058
+ module Rant
4059
+ module Sys
4060
+ def unpack_tgz(archive, opts={})
4061
+ output_dir = opts[:to] || opts[:in] || "."
4062
+ mkpath output_dir unless test ?d, output_dir
4063
+ if Env.have_tar?
4064
+ sh "tar", "-xzf", archive, "-C", output_dir
4065
+ else
4066
+ minitar_unpack(archive, output_dir)
4067
+ end
4068
+ nil
4069
+ end
4070
+ private
4071
+ def minitar_tgz(fn, files, opts)
4072
+ require 'zlib'
4073
+ fu_output_message "minitar #{fn}"
4074
+ files = files.to_ary if files.respond_to? :to_ary
4075
+ tgz = Zlib::GzipWriter.new(File.open(fn, 'wb'))
4076
+ Rant::Archive::Minitar.pack(files, tgz, opts[:recurse])
4077
+ nil
4078
+ end
4079
+ def minitar_unpack(archive, output_dir)
4080
+ fu_output_message "unpacking #{archive} in #{output_dir}"
4081
+ require 'zlib'
4082
+ tgz = Zlib::GzipReader.new(File.open(archive, 'rb'))
4083
+ Archive::Minitar.unpack(tgz, output_dir)
4084
+ end
4085
+ end # module Sys
4086
+ end # module Rant
4087
+
4088
+ $".concat(['rant/rantlib.rb', 'rant/init.rb', 'rant/rantvar.rb', 'rant/rantsys.rb', 'rant/import/filelist/core.rb', 'rant/node.rb', 'rant/import/nodes/default.rb', 'rant/coregen.rb', 'rant/import/c/dependencies.rb', 'rant/c/include.rb', 'rant/import/directedrule.rb', 'rant/import/clean.rb', 'rant/import/sys/tgz.rb', 'rant/archive/minitar.rb'])
4089
+ Rant::CODE_IMPORTS.concat %w(c/dependencies directedrule nodes/default clean sys/tgz
4090
+ )
4091
+
4092
+ # Catch a `require "rant"', sad...
4093
+ alias require_backup_by_rant require
4094
+ def require libf
4095
+ if libf == "rant"
4096
+ # TODO: needs rework! look at lib/rant.rb
4097
+ self.class.instance_eval { include Rant }
4098
+ else
4099
+ begin
4100
+ require_backup_by_rant libf
4101
+ rescue
4102
+ raise $!, caller
4103
+ end
4104
+ end
4105
+ end
4106
+
4107
+ exit Rant.run