bundler 1.17.3 → 2.6.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (426) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +3354 -1258
  3. data/LICENSE.md +18 -19
  4. data/README.md +10 -15
  5. data/bundler.gemspec +15 -33
  6. data/exe/bundle +8 -10
  7. data/exe/bundler +1 -1
  8. data/lib/bundler/.document +1 -0
  9. data/lib/bundler/build_metadata.rb +5 -13
  10. data/lib/bundler/capistrano.rb +5 -5
  11. data/lib/bundler/checksum.rb +254 -0
  12. data/lib/bundler/ci_detector.rb +75 -0
  13. data/lib/bundler/cli/add.rb +29 -15
  14. data/lib/bundler/cli/binstubs.rb +13 -5
  15. data/lib/bundler/cli/cache.rb +24 -17
  16. data/lib/bundler/cli/check.rb +7 -5
  17. data/lib/bundler/cli/clean.rb +1 -1
  18. data/lib/bundler/cli/common.rb +50 -14
  19. data/lib/bundler/cli/config.rb +171 -86
  20. data/lib/bundler/cli/console.rb +3 -6
  21. data/lib/bundler/cli/doctor.rb +29 -12
  22. data/lib/bundler/cli/exec.rb +9 -25
  23. data/lib/bundler/cli/fund.rb +36 -0
  24. data/lib/bundler/cli/gem.rb +268 -53
  25. data/lib/bundler/cli/info.rb +51 -18
  26. data/lib/bundler/cli/init.rb +7 -3
  27. data/lib/bundler/cli/inject.rb +2 -2
  28. data/lib/bundler/cli/install.rb +55 -73
  29. data/lib/bundler/cli/issue.rb +9 -8
  30. data/lib/bundler/cli/list.rb +19 -11
  31. data/lib/bundler/cli/lock.rb +56 -26
  32. data/lib/bundler/cli/open.rb +10 -7
  33. data/lib/bundler/cli/outdated.rb +159 -128
  34. data/lib/bundler/cli/platform.rb +8 -6
  35. data/lib/bundler/cli/plugin.rb +23 -12
  36. data/lib/bundler/cli/pristine.rb +39 -26
  37. data/lib/bundler/cli/remove.rb +1 -2
  38. data/lib/bundler/cli/show.rb +7 -7
  39. data/lib/bundler/cli/update.rb +51 -19
  40. data/lib/bundler/cli/viz.rb +1 -1
  41. data/lib/bundler/cli.rb +399 -390
  42. data/lib/bundler/compact_index_client/cache.rb +55 -77
  43. data/lib/bundler/compact_index_client/cache_file.rb +148 -0
  44. data/lib/bundler/compact_index_client/gem_parser.rb +32 -0
  45. data/lib/bundler/compact_index_client/parser.rb +84 -0
  46. data/lib/bundler/compact_index_client/updater.rb +72 -84
  47. data/lib/bundler/compact_index_client.rb +61 -73
  48. data/lib/bundler/constants.rb +9 -2
  49. data/lib/bundler/current_ruby.rb +20 -21
  50. data/lib/bundler/definition.rb +663 -505
  51. data/lib/bundler/dependency.rb +38 -71
  52. data/lib/bundler/deployment.rb +1 -1
  53. data/lib/bundler/digest.rb +71 -0
  54. data/lib/bundler/dsl.rb +171 -152
  55. data/lib/bundler/endpoint_specification.rb +43 -17
  56. data/lib/bundler/env.rb +11 -18
  57. data/lib/bundler/environment_preserver.rb +17 -8
  58. data/lib/bundler/errors.rb +115 -14
  59. data/lib/bundler/feature_flag.rb +15 -39
  60. data/lib/bundler/fetcher/base.rb +12 -12
  61. data/lib/bundler/fetcher/compact_index.rb +41 -47
  62. data/lib/bundler/fetcher/dependency.rb +4 -8
  63. data/lib/bundler/fetcher/downloader.rb +27 -20
  64. data/lib/bundler/fetcher/gem_remote_fetcher.rb +16 -0
  65. data/lib/bundler/fetcher/index.rb +6 -33
  66. data/lib/bundler/fetcher.rb +109 -90
  67. data/lib/bundler/force_platform.rb +16 -0
  68. data/lib/bundler/friendly_errors.rb +50 -55
  69. data/lib/bundler/gem_helper.rb +81 -46
  70. data/lib/bundler/gem_helpers.rb +78 -29
  71. data/lib/bundler/gem_tasks.rb +1 -1
  72. data/lib/bundler/gem_version_promoter.rb +68 -109
  73. data/lib/bundler/graph.rb +11 -11
  74. data/lib/bundler/index.rb +74 -82
  75. data/lib/bundler/injector.rb +58 -26
  76. data/lib/bundler/inline.rb +59 -35
  77. data/lib/bundler/installer/gem_installer.rb +29 -29
  78. data/lib/bundler/installer/parallel_installer.rb +38 -68
  79. data/lib/bundler/installer/standalone.rb +76 -16
  80. data/lib/bundler/installer.rb +60 -135
  81. data/lib/bundler/lazy_specification.rb +161 -63
  82. data/lib/bundler/lockfile_generator.rb +14 -5
  83. data/lib/bundler/lockfile_parser.rb +150 -109
  84. data/lib/bundler/man/bundle-add.1 +76 -0
  85. data/lib/bundler/man/bundle-add.1.ronn +87 -0
  86. data/{man → lib/bundler/man}/bundle-binstubs.1 +15 -22
  87. data/{man/bundle-binstubs.ronn → lib/bundler/man/bundle-binstubs.1.ronn} +8 -7
  88. data/lib/bundler/man/bundle-cache.1 +68 -0
  89. data/lib/bundler/man/bundle-cache.1.ronn +108 -0
  90. data/{man → lib/bundler/man}/bundle-check.1 +7 -14
  91. data/{man/bundle-check.ronn → lib/bundler/man/bundle-check.1.ronn} +7 -2
  92. data/{man → lib/bundler/man}/bundle-clean.1 +4 -11
  93. data/{man/bundle-clean.ronn → lib/bundler/man/bundle-clean.1.ronn} +1 -1
  94. data/{man → lib/bundler/man}/bundle-config.1 +80 -260
  95. data/{man/bundle-config.ronn → lib/bundler/man/bundle-config.1.ronn} +104 -98
  96. data/lib/bundler/man/bundle-console.1 +33 -0
  97. data/lib/bundler/man/bundle-console.1.ronn +39 -0
  98. data/{man → lib/bundler/man}/bundle-doctor.1 +5 -19
  99. data/{man/bundle-doctor.ronn → lib/bundler/man/bundle-doctor.1.ronn} +1 -1
  100. data/lib/bundler/man/bundle-env.1 +9 -0
  101. data/lib/bundler/man/bundle-env.1.ronn +10 -0
  102. data/{man → lib/bundler/man}/bundle-exec.1 +20 -78
  103. data/{man/bundle-exec.ronn → lib/bundler/man/bundle-exec.1.ronn} +12 -10
  104. data/lib/bundler/man/bundle-fund.1 +22 -0
  105. data/lib/bundler/man/bundle-fund.1.ronn +25 -0
  106. data/lib/bundler/man/bundle-gem.1 +87 -0
  107. data/lib/bundler/man/bundle-gem.1.ronn +149 -0
  108. data/lib/bundler/man/bundle-help.1 +9 -0
  109. data/lib/bundler/man/bundle-help.1.ronn +12 -0
  110. data/lib/bundler/man/bundle-info.1 +17 -0
  111. data/lib/bundler/man/bundle-info.1.ronn +21 -0
  112. data/{man → lib/bundler/man}/bundle-init.1 +8 -13
  113. data/{man/bundle-init.ronn → lib/bundler/man/bundle-init.1.ronn} +5 -2
  114. data/lib/bundler/man/bundle-inject.1 +31 -0
  115. data/{man/bundle-inject.ronn → lib/bundler/man/bundle-inject.1.ronn} +12 -2
  116. data/{man → lib/bundler/man}/bundle-install.1 +65 -155
  117. data/{man/bundle-install.ronn → lib/bundler/man/bundle-install.1.ronn} +66 -57
  118. data/lib/bundler/man/bundle-issue.1 +45 -0
  119. data/lib/bundler/man/bundle-issue.1.ronn +37 -0
  120. data/lib/bundler/man/bundle-licenses.1 +9 -0
  121. data/lib/bundler/man/bundle-licenses.1.ronn +10 -0
  122. data/{man → lib/bundler/man}/bundle-list.1 +9 -24
  123. data/{man/bundle-list.ronn → lib/bundler/man/bundle-list.1.ronn} +10 -7
  124. data/{man → lib/bundler/man}/bundle-lock.1 +25 -34
  125. data/{man/bundle-lock.ronn → lib/bundler/man/bundle-lock.1.ronn} +25 -4
  126. data/lib/bundler/man/bundle-open.1 +32 -0
  127. data/{man/bundle-open.ronn → lib/bundler/man/bundle-open.1.ronn} +10 -1
  128. data/{man → lib/bundler/man}/bundle-outdated.1 +23 -75
  129. data/{man/bundle-outdated.ronn → lib/bundler/man/bundle-outdated.1.ronn} +21 -22
  130. data/lib/bundler/man/bundle-platform.1 +49 -0
  131. data/{man/bundle-platform.ronn → lib/bundler/man/bundle-platform.1.ronn} +14 -7
  132. data/lib/bundler/man/bundle-plugin.1 +58 -0
  133. data/lib/bundler/man/bundle-plugin.1.ronn +63 -0
  134. data/{man → lib/bundler/man}/bundle-pristine.1 +5 -16
  135. data/{man/bundle-pristine.ronn → lib/bundler/man/bundle-pristine.1.ronn} +1 -1
  136. data/{man → lib/bundler/man}/bundle-remove.1 +4 -14
  137. data/{man/bundle-remove.ronn → lib/bundler/man/bundle-remove.1.ronn} +1 -1
  138. data/{man → lib/bundler/man}/bundle-show.1 +7 -11
  139. data/{man/bundle-show.ronn → lib/bundler/man/bundle-show.1.ronn} +4 -0
  140. data/{man → lib/bundler/man}/bundle-update.1 +35 -148
  141. data/{man/bundle-update.ronn → lib/bundler/man/bundle-update.1.ronn} +21 -12
  142. data/lib/bundler/man/bundle-version.1 +22 -0
  143. data/lib/bundler/man/bundle-version.1.ronn +24 -0
  144. data/{man → lib/bundler/man}/bundle-viz.1 +9 -18
  145. data/{man/bundle-viz.ronn → lib/bundler/man/bundle-viz.1.ronn} +9 -3
  146. data/{man → lib/bundler/man}/bundle.1 +19 -53
  147. data/{man/bundle.ronn → lib/bundler/man/bundle.1.ronn} +14 -9
  148. data/{man → lib/bundler/man}/gemfile.5 +139 -356
  149. data/{man → lib/bundler/man}/gemfile.5.ronn +134 -97
  150. data/{man → lib/bundler/man}/index.txt +9 -1
  151. data/lib/bundler/match_metadata.rb +17 -0
  152. data/lib/bundler/match_platform.rb +2 -3
  153. data/lib/bundler/match_remote_metadata.rb +29 -0
  154. data/lib/bundler/materialization.rb +59 -0
  155. data/lib/bundler/mirror.rb +10 -12
  156. data/lib/bundler/plugin/api/source.rb +34 -18
  157. data/lib/bundler/plugin/api.rb +1 -1
  158. data/lib/bundler/plugin/dsl.rb +1 -1
  159. data/lib/bundler/plugin/events.rb +24 -0
  160. data/lib/bundler/plugin/index.rb +44 -9
  161. data/lib/bundler/plugin/installer/git.rb +0 -4
  162. data/lib/bundler/plugin/installer/path.rb +18 -0
  163. data/lib/bundler/plugin/installer/rubygems.rb +1 -9
  164. data/lib/bundler/plugin/installer.rb +63 -27
  165. data/lib/bundler/plugin/source_list.rb +5 -1
  166. data/lib/bundler/plugin.rb +131 -45
  167. data/lib/bundler/process_lock.rb +10 -14
  168. data/lib/bundler/remote_specification.rb +22 -10
  169. data/lib/bundler/resolver/base.rb +118 -0
  170. data/lib/bundler/resolver/candidate.rb +82 -0
  171. data/lib/bundler/resolver/incompatibility.rb +15 -0
  172. data/lib/bundler/resolver/package.rb +90 -0
  173. data/lib/bundler/resolver/root.rb +25 -0
  174. data/lib/bundler/resolver/spec_group.rb +60 -68
  175. data/lib/bundler/resolver.rb +454 -303
  176. data/lib/bundler/retry.rb +6 -6
  177. data/lib/bundler/ruby_dsl.rb +51 -7
  178. data/lib/bundler/ruby_version.rb +23 -38
  179. data/lib/bundler/rubygems_ext.rb +357 -98
  180. data/lib/bundler/rubygems_gem_installer.rb +131 -65
  181. data/lib/bundler/rubygems_integration.rb +149 -591
  182. data/lib/bundler/runtime.rb +51 -51
  183. data/lib/bundler/safe_marshal.rb +31 -0
  184. data/lib/bundler/self_manager.rb +206 -0
  185. data/lib/bundler/settings.rb +271 -135
  186. data/lib/bundler/setup.rb +23 -12
  187. data/lib/bundler/shared_helpers.rb +127 -117
  188. data/lib/bundler/similarity_detector.rb +3 -3
  189. data/lib/bundler/source/git/git_proxy.rb +326 -127
  190. data/lib/bundler/source/git.rb +207 -88
  191. data/lib/bundler/source/metadata.rb +19 -18
  192. data/lib/bundler/source/path/installer.rb +11 -32
  193. data/lib/bundler/source/path.rb +39 -38
  194. data/lib/bundler/source/rubygems/remote.rb +3 -4
  195. data/lib/bundler/source/rubygems.rb +223 -255
  196. data/lib/bundler/source/rubygems_aggregate.rb +68 -0
  197. data/lib/bundler/source.rb +33 -11
  198. data/lib/bundler/source_list.rb +131 -66
  199. data/lib/bundler/source_map.rb +71 -0
  200. data/lib/bundler/spec_set.rb +239 -94
  201. data/lib/bundler/stub_specification.rb +77 -39
  202. data/lib/bundler/templates/Executable +3 -5
  203. data/lib/bundler/templates/Executable.bundler +23 -19
  204. data/lib/bundler/templates/Executable.standalone +4 -4
  205. data/lib/bundler/templates/Gemfile +0 -2
  206. data/lib/bundler/templates/newgem/CHANGELOG.md.tt +5 -0
  207. data/lib/bundler/templates/newgem/CODE_OF_CONDUCT.md.tt +104 -46
  208. data/lib/bundler/templates/newgem/Cargo.toml.tt +7 -0
  209. data/lib/bundler/templates/newgem/Gemfile.tt +19 -2
  210. data/lib/bundler/templates/newgem/README.md.tt +18 -16
  211. data/lib/bundler/templates/newgem/Rakefile.tt +44 -6
  212. data/lib/bundler/templates/newgem/bin/console.tt +1 -4
  213. data/lib/bundler/templates/newgem/circleci/config.yml.tt +25 -0
  214. data/lib/bundler/templates/newgem/ext/newgem/Cargo.toml.tt +15 -0
  215. data/lib/bundler/templates/newgem/ext/newgem/extconf-c.rb.tt +10 -0
  216. data/lib/bundler/templates/newgem/ext/newgem/extconf-rust.rb.tt +6 -0
  217. data/lib/bundler/templates/newgem/ext/newgem/newgem.c.tt +1 -1
  218. data/lib/bundler/templates/newgem/ext/newgem/src/lib.rs.tt +12 -0
  219. data/lib/bundler/templates/newgem/github/workflows/main.yml.tt +37 -0
  220. data/lib/bundler/templates/newgem/gitignore.tt +3 -0
  221. data/lib/bundler/templates/newgem/gitlab-ci.yml.tt +18 -0
  222. data/lib/bundler/templates/newgem/lib/newgem/version.rb.tt +2 -0
  223. data/lib/bundler/templates/newgem/lib/newgem.rb.tt +4 -2
  224. data/lib/bundler/templates/newgem/newgem.gemspec.tt +37 -40
  225. data/lib/bundler/templates/newgem/rubocop.yml.tt +8 -0
  226. data/lib/bundler/templates/newgem/sig/newgem.rbs.tt +8 -0
  227. data/lib/bundler/templates/newgem/spec/newgem_spec.rb.tt +2 -0
  228. data/lib/bundler/templates/newgem/spec/spec_helper.rb.tt +2 -1
  229. data/lib/bundler/templates/newgem/standard.yml.tt +3 -0
  230. data/lib/bundler/templates/newgem/test/minitest/test_helper.rb.tt +6 -0
  231. data/lib/bundler/templates/newgem/test/{newgem_test.rb.tt → minitest/test_newgem.rb.tt} +3 -1
  232. data/lib/bundler/templates/newgem/test/test-unit/newgem_test.rb.tt +15 -0
  233. data/lib/bundler/templates/newgem/test/test-unit/test_helper.rb.tt +6 -0
  234. data/lib/bundler/ui/rg_proxy.rb +2 -2
  235. data/lib/bundler/ui/shell.rb +64 -23
  236. data/lib/bundler/ui/silent.rb +33 -6
  237. data/lib/bundler/ui.rb +3 -3
  238. data/lib/bundler/uri_credentials_filter.rb +11 -5
  239. data/lib/bundler/uri_normalizer.rb +23 -0
  240. data/lib/bundler/vendor/.document +1 -0
  241. data/lib/bundler/vendor/connection_pool/.document +1 -0
  242. data/lib/bundler/vendor/connection_pool/LICENSE +20 -0
  243. data/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb +174 -0
  244. data/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb +3 -0
  245. data/lib/bundler/vendor/connection_pool/lib/connection_pool/wrapper.rb +56 -0
  246. data/lib/bundler/vendor/connection_pool/lib/connection_pool.rb +175 -0
  247. data/lib/bundler/vendor/fileutils/.document +1 -0
  248. data/lib/bundler/vendor/fileutils/COPYING +56 -0
  249. data/lib/bundler/vendor/fileutils/lib/fileutils.rb +1490 -432
  250. data/lib/bundler/vendor/net-http-persistent/.document +1 -0
  251. data/lib/bundler/vendor/net-http-persistent/README.rdoc +82 -0
  252. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/connection.rb +41 -0
  253. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/pool.rb +65 -0
  254. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/timed_stack_multi.rb +79 -0
  255. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb +362 -484
  256. data/lib/bundler/vendor/pub_grub/.document +1 -0
  257. data/lib/bundler/vendor/pub_grub/LICENSE.txt +21 -0
  258. data/lib/bundler/vendor/pub_grub/lib/pub_grub/assignment.rb +20 -0
  259. data/lib/bundler/vendor/pub_grub/lib/pub_grub/basic_package_source.rb +189 -0
  260. data/lib/bundler/vendor/pub_grub/lib/pub_grub/failure_writer.rb +182 -0
  261. data/lib/bundler/vendor/pub_grub/lib/pub_grub/incompatibility.rb +150 -0
  262. data/lib/bundler/vendor/pub_grub/lib/pub_grub/package.rb +43 -0
  263. data/lib/bundler/vendor/pub_grub/lib/pub_grub/partial_solution.rb +121 -0
  264. data/lib/bundler/vendor/pub_grub/lib/pub_grub/rubygems.rb +45 -0
  265. data/lib/bundler/vendor/pub_grub/lib/pub_grub/solve_failure.rb +19 -0
  266. data/lib/bundler/vendor/pub_grub/lib/pub_grub/static_package_source.rb +61 -0
  267. data/lib/bundler/vendor/pub_grub/lib/pub_grub/term.rb +105 -0
  268. data/lib/bundler/vendor/pub_grub/lib/pub_grub/version.rb +3 -0
  269. data/lib/bundler/vendor/pub_grub/lib/pub_grub/version_constraint.rb +129 -0
  270. data/lib/bundler/vendor/pub_grub/lib/pub_grub/version_range.rb +411 -0
  271. data/lib/bundler/vendor/pub_grub/lib/pub_grub/version_solver.rb +248 -0
  272. data/lib/bundler/vendor/pub_grub/lib/pub_grub/version_union.rb +178 -0
  273. data/lib/bundler/vendor/pub_grub/lib/pub_grub.rb +31 -0
  274. data/lib/bundler/vendor/securerandom/.document +1 -0
  275. data/lib/bundler/vendor/securerandom/COPYING +56 -0
  276. data/lib/bundler/vendor/securerandom/lib/securerandom.rb +102 -0
  277. data/lib/bundler/vendor/thor/.document +1 -0
  278. data/lib/bundler/vendor/thor/LICENSE.md +20 -0
  279. data/lib/bundler/vendor/thor/lib/thor/actions/create_file.rb +4 -3
  280. data/lib/bundler/vendor/thor/lib/thor/actions/create_link.rb +3 -2
  281. data/lib/bundler/vendor/thor/lib/thor/actions/directory.rb +8 -18
  282. data/lib/bundler/vendor/thor/lib/thor/actions/empty_directory.rb +1 -1
  283. data/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb +27 -20
  284. data/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb +34 -13
  285. data/lib/bundler/vendor/thor/lib/thor/actions.rb +47 -28
  286. data/lib/bundler/vendor/thor/lib/thor/base.rb +200 -54
  287. data/lib/bundler/vendor/thor/lib/thor/command.rb +34 -18
  288. data/lib/bundler/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb +10 -0
  289. data/lib/bundler/vendor/thor/lib/thor/error.rb +74 -0
  290. data/lib/bundler/vendor/thor/lib/thor/group.rb +15 -4
  291. data/lib/bundler/vendor/thor/lib/thor/invocation.rb +2 -1
  292. data/lib/bundler/vendor/thor/lib/thor/line_editor/basic.rb +1 -1
  293. data/lib/bundler/vendor/thor/lib/thor/line_editor/readline.rb +6 -6
  294. data/lib/bundler/vendor/thor/lib/thor/line_editor.rb +2 -2
  295. data/lib/bundler/vendor/thor/lib/thor/nested_context.rb +29 -0
  296. data/lib/bundler/vendor/thor/lib/thor/parser/argument.rb +17 -1
  297. data/lib/bundler/vendor/thor/lib/thor/parser/arguments.rb +35 -15
  298. data/lib/bundler/vendor/thor/lib/thor/parser/option.rb +45 -13
  299. data/lib/bundler/vendor/thor/lib/thor/parser/options.rb +86 -13
  300. data/lib/bundler/vendor/thor/lib/thor/parser.rb +4 -4
  301. data/lib/bundler/vendor/thor/lib/thor/rake_compat.rb +3 -2
  302. data/lib/bundler/vendor/thor/lib/thor/runner.rb +51 -40
  303. data/lib/bundler/vendor/thor/lib/thor/shell/basic.rb +99 -148
  304. data/lib/bundler/vendor/thor/lib/thor/shell/color.rb +6 -43
  305. data/lib/bundler/vendor/thor/lib/thor/shell/column_printer.rb +29 -0
  306. data/lib/bundler/vendor/thor/lib/thor/shell/html.rb +4 -49
  307. data/lib/bundler/vendor/thor/lib/thor/shell/table_printer.rb +118 -0
  308. data/lib/bundler/vendor/thor/lib/thor/shell/terminal.rb +42 -0
  309. data/lib/bundler/vendor/thor/lib/thor/shell/wrapped_printer.rb +38 -0
  310. data/lib/bundler/vendor/thor/lib/thor/shell.rb +6 -6
  311. data/lib/bundler/vendor/thor/lib/thor/util.rb +26 -9
  312. data/lib/bundler/vendor/thor/lib/thor/version.rb +1 -1
  313. data/lib/bundler/vendor/thor/lib/thor.rb +182 -17
  314. data/lib/bundler/vendor/tsort/.document +1 -0
  315. data/lib/bundler/vendor/tsort/LICENSE.txt +22 -0
  316. data/lib/bundler/vendor/tsort/lib/tsort.rb +455 -0
  317. data/lib/bundler/vendor/uri/.document +1 -0
  318. data/lib/bundler/vendor/uri/COPYING +56 -0
  319. data/lib/bundler/vendor/uri/lib/uri/common.rb +876 -0
  320. data/lib/bundler/vendor/uri/lib/uri/file.rb +100 -0
  321. data/lib/bundler/vendor/uri/lib/uri/ftp.rb +267 -0
  322. data/lib/bundler/vendor/uri/lib/uri/generic.rb +1578 -0
  323. data/lib/bundler/vendor/uri/lib/uri/http.rb +125 -0
  324. data/lib/bundler/vendor/uri/lib/uri/https.rb +23 -0
  325. data/lib/bundler/vendor/uri/lib/uri/ldap.rb +261 -0
  326. data/lib/bundler/vendor/uri/lib/uri/ldaps.rb +22 -0
  327. data/lib/bundler/vendor/uri/lib/uri/mailto.rb +293 -0
  328. data/lib/bundler/vendor/uri/lib/uri/rfc2396_parser.rb +546 -0
  329. data/lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb +206 -0
  330. data/lib/bundler/vendor/uri/lib/uri/version.rb +6 -0
  331. data/lib/bundler/vendor/uri/lib/uri/ws.rb +83 -0
  332. data/lib/bundler/vendor/uri/lib/uri/wss.rb +23 -0
  333. data/lib/bundler/vendor/uri/lib/uri.rb +104 -0
  334. data/lib/bundler/vendored_fileutils.rb +1 -6
  335. data/lib/bundler/vendored_net_http.rb +23 -0
  336. data/lib/bundler/vendored_persistent.rb +1 -42
  337. data/lib/bundler/{vendored_molinillo.rb → vendored_pub_grub.rb} +1 -1
  338. data/lib/bundler/vendored_securerandom.rb +12 -0
  339. data/lib/bundler/vendored_thor.rb +2 -2
  340. data/lib/bundler/vendored_timeout.rb +12 -0
  341. data/lib/bundler/vendored_tsort.rb +4 -0
  342. data/lib/bundler/vendored_uri.rb +21 -0
  343. data/lib/bundler/version.rb +5 -20
  344. data/lib/bundler/vlad.rb +3 -3
  345. data/lib/bundler/worker.rb +26 -15
  346. data/lib/bundler/yaml_serializer.rb +21 -13
  347. data/lib/bundler.rb +364 -230
  348. metadata +186 -218
  349. data/exe/bundle_ruby +0 -60
  350. data/lib/bundler/cli/package.rb +0 -49
  351. data/lib/bundler/compatibility_guard.rb +0 -14
  352. data/lib/bundler/dep_proxy.rb +0 -48
  353. data/lib/bundler/gem_remote_fetcher.rb +0 -43
  354. data/lib/bundler/gemdeps.rb +0 -29
  355. data/lib/bundler/psyched_yaml.rb +0 -37
  356. data/lib/bundler/ssl_certs/certificate_manager.rb +0 -66
  357. data/lib/bundler/ssl_certs/index.rubygems.org/GlobalSignRootCA.pem +0 -21
  358. data/lib/bundler/ssl_certs/rubygems.global.ssl.fastly.net/DigiCertHighAssuranceEVRootCA.pem +0 -23
  359. data/lib/bundler/ssl_certs/rubygems.org/AddTrustExternalCARoot.pem +0 -25
  360. data/lib/bundler/templates/gems.rb +0 -8
  361. data/lib/bundler/templates/newgem/ext/newgem/extconf.rb.tt +0 -3
  362. data/lib/bundler/templates/newgem/test/test_helper.rb.tt +0 -4
  363. data/lib/bundler/templates/newgem/travis.yml.tt +0 -7
  364. data/lib/bundler/vendor/molinillo/lib/molinillo/compatibility.rb +0 -26
  365. data/lib/bundler/vendor/molinillo/lib/molinillo/delegates/resolution_state.rb +0 -57
  366. data/lib/bundler/vendor/molinillo/lib/molinillo/delegates/specification_provider.rb +0 -81
  367. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/action.rb +0 -36
  368. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb +0 -66
  369. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_vertex.rb +0 -62
  370. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/delete_edge.rb +0 -63
  371. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb +0 -61
  372. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/log.rb +0 -126
  373. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/set_payload.rb +0 -46
  374. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/tag.rb +0 -36
  375. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb +0 -136
  376. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb +0 -223
  377. data/lib/bundler/vendor/molinillo/lib/molinillo/errors.rb +0 -143
  378. data/lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb +0 -6
  379. data/lib/bundler/vendor/molinillo/lib/molinillo/modules/specification_provider.rb +0 -101
  380. data/lib/bundler/vendor/molinillo/lib/molinillo/modules/ui.rb +0 -67
  381. data/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb +0 -837
  382. data/lib/bundler/vendor/molinillo/lib/molinillo/resolver.rb +0 -46
  383. data/lib/bundler/vendor/molinillo/lib/molinillo/state.rb +0 -58
  384. data/lib/bundler/vendor/molinillo/lib/molinillo.rb +0 -12
  385. data/lib/bundler/vendor/net-http-persistent/lib/net/http/faster.rb +0 -27
  386. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/ssl_reuse.rb +0 -129
  387. data/lib/bundler/vendor/thor/lib/thor/core_ext/io_binary_read.rb +0 -12
  388. data/lib/bundler/vendor/thor/lib/thor/core_ext/ordered_hash.rb +0 -129
  389. data/lib/bundler/version_ranges.rb +0 -76
  390. data/man/bundle-add.1 +0 -58
  391. data/man/bundle-add.1.txt +0 -52
  392. data/man/bundle-add.ronn +0 -40
  393. data/man/bundle-binstubs.1.txt +0 -48
  394. data/man/bundle-check.1.txt +0 -33
  395. data/man/bundle-clean.1.txt +0 -26
  396. data/man/bundle-config.1.txt +0 -529
  397. data/man/bundle-doctor.1.txt +0 -44
  398. data/man/bundle-exec.1.txt +0 -178
  399. data/man/bundle-gem.1 +0 -80
  400. data/man/bundle-gem.1.txt +0 -91
  401. data/man/bundle-gem.ronn +0 -78
  402. data/man/bundle-info.1 +0 -20
  403. data/man/bundle-info.1.txt +0 -21
  404. data/man/bundle-info.ronn +0 -17
  405. data/man/bundle-init.1.txt +0 -34
  406. data/man/bundle-inject.1 +0 -33
  407. data/man/bundle-inject.1.txt +0 -32
  408. data/man/bundle-install.1.txt +0 -396
  409. data/man/bundle-list.1.txt +0 -43
  410. data/man/bundle-lock.1.txt +0 -93
  411. data/man/bundle-open.1 +0 -32
  412. data/man/bundle-open.1.txt +0 -29
  413. data/man/bundle-outdated.1.txt +0 -131
  414. data/man/bundle-package.1 +0 -55
  415. data/man/bundle-package.1.txt +0 -79
  416. data/man/bundle-package.ronn +0 -72
  417. data/man/bundle-platform.1 +0 -61
  418. data/man/bundle-platform.1.txt +0 -57
  419. data/man/bundle-pristine.1.txt +0 -44
  420. data/man/bundle-remove.1.txt +0 -34
  421. data/man/bundle-show.1.txt +0 -27
  422. data/man/bundle-update.1.txt +0 -391
  423. data/man/bundle-viz.1.txt +0 -39
  424. data/man/bundle.1.txt +0 -116
  425. data/man/gemfile.5.txt +0 -653
  426. /data/lib/bundler/{ssl_certs → man}/.document +0 -0
@@ -1,91 +1,187 @@
1
1
  # frozen_string_literal: true
2
+
3
+ begin
4
+ require 'rbconfig'
5
+ rescue LoadError
6
+ # for make rjit-headers
7
+ end
8
+
9
+ # Namespace for file utility methods for copying, moving, removing, etc.
2
10
  #
3
- # = fileutils.rb
11
+ # == What's Here
4
12
  #
5
- # Copyright (c) 2000-2007 Minero Aoki
13
+ # First, what’s elsewhere. \Module \Bundler::FileUtils:
6
14
  #
7
- # This program is free software.
8
- # You can distribute/modify this program under the same terms of ruby.
15
+ # - Inherits from {class Object}[https://docs.ruby-lang.org/en/master/Object.html].
16
+ # - Supplements {class File}[https://docs.ruby-lang.org/en/master/File.html]
17
+ # (but is not included or extended there).
9
18
  #
10
- # == module Bundler::FileUtils
19
+ # Here, module \Bundler::FileUtils provides methods that are useful for:
11
20
  #
12
- # Namespace for several file utility methods for copying, moving, removing, etc.
21
+ # - {Creating}[rdoc-ref:FileUtils@Creating].
22
+ # - {Deleting}[rdoc-ref:FileUtils@Deleting].
23
+ # - {Querying}[rdoc-ref:FileUtils@Querying].
24
+ # - {Setting}[rdoc-ref:FileUtils@Setting].
25
+ # - {Comparing}[rdoc-ref:FileUtils@Comparing].
26
+ # - {Copying}[rdoc-ref:FileUtils@Copying].
27
+ # - {Moving}[rdoc-ref:FileUtils@Moving].
28
+ # - {Options}[rdoc-ref:FileUtils@Options].
13
29
  #
14
- # === Module Functions
30
+ # === Creating
15
31
  #
16
- # require 'bundler/vendor/fileutils/lib/fileutils'
32
+ # - ::mkdir: Creates directories.
33
+ # - ::mkdir_p, ::makedirs, ::mkpath: Creates directories,
34
+ # also creating ancestor directories as needed.
35
+ # - ::link_entry: Creates a hard link.
36
+ # - ::ln, ::link: Creates hard links.
37
+ # - ::ln_s, ::symlink: Creates symbolic links.
38
+ # - ::ln_sf: Creates symbolic links, overwriting if necessary.
39
+ # - ::ln_sr: Creates symbolic links relative to targets
17
40
  #
18
- # Bundler::FileUtils.cd(dir, options)
19
- # Bundler::FileUtils.cd(dir, options) {|dir| block }
20
- # Bundler::FileUtils.pwd()
21
- # Bundler::FileUtils.mkdir(dir, options)
22
- # Bundler::FileUtils.mkdir(list, options)
23
- # Bundler::FileUtils.mkdir_p(dir, options)
24
- # Bundler::FileUtils.mkdir_p(list, options)
25
- # Bundler::FileUtils.rmdir(dir, options)
26
- # Bundler::FileUtils.rmdir(list, options)
27
- # Bundler::FileUtils.ln(target, link, options)
28
- # Bundler::FileUtils.ln(targets, dir, options)
29
- # Bundler::FileUtils.ln_s(target, link, options)
30
- # Bundler::FileUtils.ln_s(targets, dir, options)
31
- # Bundler::FileUtils.ln_sf(target, link, options)
32
- # Bundler::FileUtils.cp(src, dest, options)
33
- # Bundler::FileUtils.cp(list, dir, options)
34
- # Bundler::FileUtils.cp_r(src, dest, options)
35
- # Bundler::FileUtils.cp_r(list, dir, options)
36
- # Bundler::FileUtils.mv(src, dest, options)
37
- # Bundler::FileUtils.mv(list, dir, options)
38
- # Bundler::FileUtils.rm(list, options)
39
- # Bundler::FileUtils.rm_r(list, options)
40
- # Bundler::FileUtils.rm_rf(list, options)
41
- # Bundler::FileUtils.install(src, dest, options)
42
- # Bundler::FileUtils.chmod(mode, list, options)
43
- # Bundler::FileUtils.chmod_R(mode, list, options)
44
- # Bundler::FileUtils.chown(user, group, list, options)
45
- # Bundler::FileUtils.chown_R(user, group, list, options)
46
- # Bundler::FileUtils.touch(list, options)
41
+ # === Deleting
47
42
  #
48
- # The <tt>options</tt> parameter is a hash of options, taken from the list
49
- # <tt>:force</tt>, <tt>:noop</tt>, <tt>:preserve</tt>, and <tt>:verbose</tt>.
50
- # <tt>:noop</tt> means that no changes are made. The other three are obvious.
51
- # Each method documents the options that it honours.
43
+ # - ::remove_dir: Removes a directory and its descendants.
44
+ # - ::remove_entry: Removes an entry, including its descendants if it is a directory.
45
+ # - ::remove_entry_secure: Like ::remove_entry, but removes securely.
46
+ # - ::remove_file: Removes a file entry.
47
+ # - ::rm, ::remove: Removes entries.
48
+ # - ::rm_f, ::safe_unlink: Like ::rm, but removes forcibly.
49
+ # - ::rm_r: Removes entries and their descendants.
50
+ # - ::rm_rf, ::rmtree: Like ::rm_r, but removes forcibly.
51
+ # - ::rmdir: Removes directories.
52
52
  #
53
- # All methods that have the concept of a "source" file or directory can take
54
- # either one file or a list of files in that argument. See the method
55
- # documentation for examples.
53
+ # === Querying
56
54
  #
57
- # There are some `low level' methods, which do not accept any option:
55
+ # - ::pwd, ::getwd: Returns the path to the working directory.
56
+ # - ::uptodate?: Returns whether a given entry is newer than given other entries.
58
57
  #
59
- # Bundler::FileUtils.copy_entry(src, dest, preserve = false, dereference = false)
60
- # Bundler::FileUtils.copy_file(src, dest, preserve = false, dereference = true)
61
- # Bundler::FileUtils.copy_stream(srcstream, deststream)
62
- # Bundler::FileUtils.remove_entry(path, force = false)
63
- # Bundler::FileUtils.remove_entry_secure(path, force = false)
64
- # Bundler::FileUtils.remove_file(path, force = false)
65
- # Bundler::FileUtils.compare_file(path_a, path_b)
66
- # Bundler::FileUtils.compare_stream(stream_a, stream_b)
67
- # Bundler::FileUtils.uptodate?(file, cmp_list)
58
+ # === Setting
68
59
  #
69
- # == module Bundler::FileUtils::Verbose
60
+ # - ::cd, ::chdir: Sets the working directory.
61
+ # - ::chmod: Sets permissions for an entry.
62
+ # - ::chmod_R: Sets permissions for an entry and its descendants.
63
+ # - ::chown: Sets the owner and group for entries.
64
+ # - ::chown_R: Sets the owner and group for entries and their descendants.
65
+ # - ::touch: Sets modification and access times for entries,
66
+ # creating if necessary.
70
67
  #
71
- # This module has all methods of Bundler::FileUtils module, but it outputs messages
72
- # before acting. This equates to passing the <tt>:verbose</tt> flag to methods
73
- # in Bundler::FileUtils.
68
+ # === Comparing
74
69
  #
75
- # == module Bundler::FileUtils::NoWrite
70
+ # - ::compare_file, ::cmp, ::identical?: Returns whether two entries are identical.
71
+ # - ::compare_stream: Returns whether two streams are identical.
76
72
  #
77
- # This module has all methods of Bundler::FileUtils module, but never changes
78
- # files/directories. This equates to passing the <tt>:noop</tt> flag to methods
79
- # in Bundler::FileUtils.
73
+ # === Copying
80
74
  #
81
- # == module Bundler::FileUtils::DryRun
75
+ # - ::copy_entry: Recursively copies an entry.
76
+ # - ::copy_file: Copies an entry.
77
+ # - ::copy_stream: Copies a stream.
78
+ # - ::cp, ::copy: Copies files.
79
+ # - ::cp_lr: Recursively creates hard links.
80
+ # - ::cp_r: Recursively copies files, retaining mode, owner, and group.
81
+ # - ::install: Recursively copies files, optionally setting mode,
82
+ # owner, and group.
82
83
  #
83
- # This module has all methods of Bundler::FileUtils module, but never changes
84
- # files/directories. This equates to passing the <tt>:noop</tt> and
85
- # <tt>:verbose</tt> flags to methods in Bundler::FileUtils.
84
+ # === Moving
85
+ #
86
+ # - ::mv, ::move: Moves entries.
87
+ #
88
+ # === Options
89
+ #
90
+ # - ::collect_method: Returns the names of methods that accept a given option.
91
+ # - ::commands: Returns the names of methods that accept options.
92
+ # - ::have_option?: Returns whether a given method accepts a given option.
93
+ # - ::options: Returns all option names.
94
+ # - ::options_of: Returns the names of the options for a given method.
95
+ #
96
+ # == Path Arguments
97
+ #
98
+ # Some methods in \Bundler::FileUtils accept _path_ arguments,
99
+ # which are interpreted as paths to filesystem entries:
100
+ #
101
+ # - If the argument is a string, that value is the path.
102
+ # - If the argument has method +:to_path+, it is converted via that method.
103
+ # - If the argument has method +:to_str+, it is converted via that method.
104
+ #
105
+ # == About the Examples
106
+ #
107
+ # Some examples here involve trees of file entries.
108
+ # For these, we sometimes display trees using the
109
+ # {tree command-line utility}[https://en.wikipedia.org/wiki/Tree_(command)],
110
+ # which is a recursive directory-listing utility that produces
111
+ # a depth-indented listing of files and directories.
112
+ #
113
+ # We use a helper method to launch the command and control the format:
114
+ #
115
+ # def tree(dirpath = '.')
116
+ # command = "tree --noreport --charset=ascii #{dirpath}"
117
+ # system(command)
118
+ # end
119
+ #
120
+ # To illustrate:
121
+ #
122
+ # tree('src0')
123
+ # # => src0
124
+ # # |-- sub0
125
+ # # | |-- src0.txt
126
+ # # | `-- src1.txt
127
+ # # `-- sub1
128
+ # # |-- src2.txt
129
+ # # `-- src3.txt
130
+ #
131
+ # == Avoiding the TOCTTOU Vulnerability
132
+ #
133
+ # For certain methods that recursively remove entries,
134
+ # there is a potential vulnerability called the
135
+ # {Time-of-check to time-of-use}[https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use],
136
+ # or TOCTTOU, vulnerability that can exist when:
137
+ #
138
+ # - An ancestor directory of the entry at the target path is world writable;
139
+ # such directories include <tt>/tmp</tt>.
140
+ # - The directory tree at the target path includes:
141
+ #
142
+ # - A world-writable descendant directory.
143
+ # - A symbolic link.
144
+ #
145
+ # To avoid that vulnerability, you can use this method to remove entries:
146
+ #
147
+ # - Bundler::FileUtils.remove_entry_secure: removes recursively
148
+ # if the target path points to a directory.
149
+ #
150
+ # Also available are these methods,
151
+ # each of which calls \Bundler::FileUtils.remove_entry_secure:
152
+ #
153
+ # - Bundler::FileUtils.rm_r with keyword argument <tt>secure: true</tt>.
154
+ # - Bundler::FileUtils.rm_rf with keyword argument <tt>secure: true</tt>.
155
+ #
156
+ # Finally, this method for moving entries calls \Bundler::FileUtils.remove_entry_secure
157
+ # if the source and destination are on different file systems
158
+ # (which means that the "move" is really a copy and remove):
159
+ #
160
+ # - Bundler::FileUtils.mv with keyword argument <tt>secure: true</tt>.
161
+ #
162
+ # \Method \Bundler::FileUtils.remove_entry_secure removes securely
163
+ # by applying a special pre-process:
164
+ #
165
+ # - If the target path points to a directory, this method uses methods
166
+ # {File#chown}[https://docs.ruby-lang.org/en/master/File.html#method-i-chown]
167
+ # and {File#chmod}[https://docs.ruby-lang.org/en/master/File.html#method-i-chmod]
168
+ # in removing directories.
169
+ # - The owner of the target directory should be either the current process
170
+ # or the super user (root).
171
+ #
172
+ # WARNING: You must ensure that *ALL* parent directories cannot be
173
+ # moved by other untrusted users. For example, parent directories
174
+ # should not be owned by untrusted users, and should not be world
175
+ # writable except when the sticky bit is set.
176
+ #
177
+ # For details of this security vulnerability, see Perl cases:
178
+ #
179
+ # - {CVE-2005-0448}[https://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2005-0448].
180
+ # - {CVE-2004-0452}[https://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2004-0452].
86
181
  #
87
-
88
182
  module Bundler::FileUtils
183
+ # The version number.
184
+ VERSION = "1.7.3"
89
185
 
90
186
  def self.private_module_function(name) #:nodoc:
91
187
  module_function name
@@ -93,7 +189,11 @@ module Bundler::FileUtils
93
189
  end
94
190
 
95
191
  #
96
- # Returns the name of the current directory.
192
+ # Returns a string containing the path to the current directory:
193
+ #
194
+ # Bundler::FileUtils.pwd # => "/rdoc/fileutils"
195
+ #
196
+ # Related: Bundler::FileUtils.cd.
97
197
  #
98
198
  def pwd
99
199
  Dir.pwd
@@ -103,22 +203,44 @@ module Bundler::FileUtils
103
203
  alias getwd pwd
104
204
  module_function :getwd
105
205
 
206
+ # Changes the working directory to the given +dir+, which
207
+ # should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments]:
208
+ #
209
+ # With no block given,
210
+ # changes the current directory to the directory at +dir+; returns zero:
211
+ #
212
+ # Bundler::FileUtils.pwd # => "/rdoc/fileutils"
213
+ # Bundler::FileUtils.cd('..')
214
+ # Bundler::FileUtils.pwd # => "/rdoc"
215
+ # Bundler::FileUtils.cd('fileutils')
216
+ #
217
+ # With a block given, changes the current directory to the directory
218
+ # at +dir+, calls the block with argument +dir+,
219
+ # and restores the original current directory; returns the block's value:
220
+ #
221
+ # Bundler::FileUtils.pwd # => "/rdoc/fileutils"
222
+ # Bundler::FileUtils.cd('..') { |arg| [arg, Bundler::FileUtils.pwd] } # => ["..", "/rdoc"]
223
+ # Bundler::FileUtils.pwd # => "/rdoc/fileutils"
106
224
  #
107
- # Changes the current directory to the directory +dir+.
225
+ # Keyword arguments:
108
226
  #
109
- # If this method is called with block, resumes to the old
110
- # working directory after the block execution finished.
227
+ # - <tt>verbose: true</tt> - prints an equivalent command:
111
228
  #
112
- # Bundler::FileUtils.cd('/', :verbose => true) # chdir and report it
229
+ # Bundler::FileUtils.cd('..')
230
+ # Bundler::FileUtils.cd('fileutils')
113
231
  #
114
- # Bundler::FileUtils.cd('/') do # chdir
115
- # # ... # do something
116
- # end # return to original directory
232
+ # Output:
233
+ #
234
+ # cd ..
235
+ # cd fileutils
236
+ #
237
+ # Related: Bundler::FileUtils.pwd.
117
238
  #
118
239
  def cd(dir, verbose: nil, &block) # :yield: dir
119
240
  fu_output_message "cd #{dir}" if verbose
120
- Dir.chdir(dir, &block)
241
+ result = Dir.chdir(dir, &block)
121
242
  fu_output_message 'cd -' if verbose and block
243
+ result
122
244
  end
123
245
  module_function :cd
124
246
 
@@ -126,11 +248,19 @@ module Bundler::FileUtils
126
248
  module_function :chdir
127
249
 
128
250
  #
129
- # Returns true if +new+ is newer than all +old_list+.
130
- # Non-existent files are older than any file.
251
+ # Returns +true+ if the file at path +new+
252
+ # is newer than all the files at paths in array +old_list+;
253
+ # +false+ otherwise.
254
+ #
255
+ # Argument +new+ and the elements of +old_list+
256
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments]:
257
+ #
258
+ # Bundler::FileUtils.uptodate?('Rakefile', ['Gemfile', 'README.md']) # => true
259
+ # Bundler::FileUtils.uptodate?('Gemfile', ['Rakefile', 'README.md']) # => false
260
+ #
261
+ # A non-existent file is considered to be infinitely old.
131
262
  #
132
- # Bundler::FileUtils.uptodate?('hello.o', %w(hello.c hello.h)) or \
133
- # system 'make hello.o'
263
+ # Related: Bundler::FileUtils.touch.
134
264
  #
135
265
  def uptodate?(new, old_list)
136
266
  return false unless File.exist?(new)
@@ -150,12 +280,39 @@ module Bundler::FileUtils
150
280
  private_module_function :remove_trailing_slash
151
281
 
152
282
  #
153
- # Creates one or more directories.
283
+ # Creates directories at the paths in the given +list+
284
+ # (a single path or an array of paths);
285
+ # returns +list+ if it is an array, <tt>[list]</tt> otherwise.
154
286
  #
155
- # Bundler::FileUtils.mkdir 'test'
156
- # Bundler::FileUtils.mkdir %w( tmp data )
157
- # Bundler::FileUtils.mkdir 'notexist', :noop => true # Does not really create.
158
- # Bundler::FileUtils.mkdir 'tmp', :mode => 0700
287
+ # Argument +list+ or its elements
288
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
289
+ #
290
+ # With no keyword arguments, creates a directory at each +path+ in +list+
291
+ # by calling: <tt>Dir.mkdir(path, mode)</tt>;
292
+ # see {Dir.mkdir}[https://docs.ruby-lang.org/en/master/Dir.html#method-c-mkdir]:
293
+ #
294
+ # Bundler::FileUtils.mkdir(%w[tmp0 tmp1]) # => ["tmp0", "tmp1"]
295
+ # Bundler::FileUtils.mkdir('tmp4') # => ["tmp4"]
296
+ #
297
+ # Keyword arguments:
298
+ #
299
+ # - <tt>mode: <i>mode</i></tt> - also calls <tt>File.chmod(mode, path)</tt>;
300
+ # see {File.chmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-chmod].
301
+ # - <tt>noop: true</tt> - does not create directories.
302
+ # - <tt>verbose: true</tt> - prints an equivalent command:
303
+ #
304
+ # Bundler::FileUtils.mkdir(%w[tmp0 tmp1], verbose: true)
305
+ # Bundler::FileUtils.mkdir(%w[tmp2 tmp3], mode: 0700, verbose: true)
306
+ #
307
+ # Output:
308
+ #
309
+ # mkdir tmp0 tmp1
310
+ # mkdir -m 700 tmp2 tmp3
311
+ #
312
+ # Raises an exception if any path points to an existing
313
+ # file or directory, or if for any reason a directory cannot be created.
314
+ #
315
+ # Related: Bundler::FileUtils.mkdir_p.
159
316
  #
160
317
  def mkdir(list, mode: nil, noop: nil, verbose: nil)
161
318
  list = fu_list(list)
@@ -169,40 +326,56 @@ module Bundler::FileUtils
169
326
  module_function :mkdir
170
327
 
171
328
  #
172
- # Creates a directory and all its parent directories.
173
- # For example,
329
+ # Creates directories at the paths in the given +list+
330
+ # (a single path or an array of paths),
331
+ # also creating ancestor directories as needed;
332
+ # returns +list+ if it is an array, <tt>[list]</tt> otherwise.
333
+ #
334
+ # Argument +list+ or its elements
335
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
336
+ #
337
+ # With no keyword arguments, creates a directory at each +path+ in +list+,
338
+ # along with any needed ancestor directories,
339
+ # by calling: <tt>Dir.mkdir(path, mode)</tt>;
340
+ # see {Dir.mkdir}[https://docs.ruby-lang.org/en/master/Dir.html#method-c-mkdir]:
174
341
  #
175
- # Bundler::FileUtils.mkdir_p '/usr/local/lib/ruby'
342
+ # Bundler::FileUtils.mkdir_p(%w[tmp0/tmp1 tmp2/tmp3]) # => ["tmp0/tmp1", "tmp2/tmp3"]
343
+ # Bundler::FileUtils.mkdir_p('tmp4/tmp5') # => ["tmp4/tmp5"]
176
344
  #
177
- # causes to make following directories, if it does not exist.
345
+ # Keyword arguments:
178
346
  #
179
- # * /usr
180
- # * /usr/local
181
- # * /usr/local/lib
182
- # * /usr/local/lib/ruby
347
+ # - <tt>mode: <i>mode</i></tt> - also calls <tt>File.chmod(mode, path)</tt>;
348
+ # see {File.chmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-chmod].
349
+ # - <tt>noop: true</tt> - does not create directories.
350
+ # - <tt>verbose: true</tt> - prints an equivalent command:
183
351
  #
184
- # You can pass several directories at a time in a list.
352
+ # Bundler::FileUtils.mkdir_p(%w[tmp0 tmp1], verbose: true)
353
+ # Bundler::FileUtils.mkdir_p(%w[tmp2 tmp3], mode: 0700, verbose: true)
354
+ #
355
+ # Output:
356
+ #
357
+ # mkdir -p tmp0 tmp1
358
+ # mkdir -p -m 700 tmp2 tmp3
359
+ #
360
+ # Raises an exception if for any reason a directory cannot be created.
361
+ #
362
+ # Bundler::FileUtils.mkpath and Bundler::FileUtils.makedirs are aliases for Bundler::FileUtils.mkdir_p.
363
+ #
364
+ # Related: Bundler::FileUtils.mkdir.
185
365
  #
186
366
  def mkdir_p(list, mode: nil, noop: nil, verbose: nil)
187
367
  list = fu_list(list)
188
368
  fu_output_message "mkdir -p #{mode ? ('-m %03o ' % mode) : ''}#{list.join ' '}" if verbose
189
369
  return *list if noop
190
370
 
191
- list.map {|path| remove_trailing_slash(path)}.each do |path|
192
- # optimize for the most common case
193
- begin
194
- fu_mkdir path, mode
195
- next
196
- rescue SystemCallError
197
- next if File.directory?(path)
198
- end
371
+ list.each do |item|
372
+ path = remove_trailing_slash(item)
199
373
 
200
374
  stack = []
201
- until path == stack.last # dirname("/")=="/", dirname("C:/")=="C:/"
375
+ until File.directory?(path) || File.dirname(path) == path
202
376
  stack.push path
203
377
  path = File.dirname(path)
204
378
  end
205
- stack.pop # root directory should exist
206
379
  stack.reverse_each do |dir|
207
380
  begin
208
381
  fu_mkdir dir, mode
@@ -233,52 +406,113 @@ module Bundler::FileUtils
233
406
  private_module_function :fu_mkdir
234
407
 
235
408
  #
236
- # Removes one or more directories.
409
+ # Removes directories at the paths in the given +list+
410
+ # (a single path or an array of paths);
411
+ # returns +list+, if it is an array, <tt>[list]</tt> otherwise.
412
+ #
413
+ # Argument +list+ or its elements
414
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
415
+ #
416
+ # With no keyword arguments, removes the directory at each +path+ in +list+,
417
+ # by calling: <tt>Dir.rmdir(path)</tt>;
418
+ # see {Dir.rmdir}[https://docs.ruby-lang.org/en/master/Dir.html#method-c-rmdir]:
237
419
  #
238
- # Bundler::FileUtils.rmdir 'somedir'
239
- # Bundler::FileUtils.rmdir %w(somedir anydir otherdir)
240
- # # Does not really remove directory; outputs message.
241
- # Bundler::FileUtils.rmdir 'somedir', :verbose => true, :noop => true
420
+ # Bundler::FileUtils.rmdir(%w[tmp0/tmp1 tmp2/tmp3]) # => ["tmp0/tmp1", "tmp2/tmp3"]
421
+ # Bundler::FileUtils.rmdir('tmp4/tmp5') # => ["tmp4/tmp5"]
422
+ #
423
+ # Keyword arguments:
424
+ #
425
+ # - <tt>parents: true</tt> - removes successive ancestor directories
426
+ # if empty.
427
+ # - <tt>noop: true</tt> - does not remove directories.
428
+ # - <tt>verbose: true</tt> - prints an equivalent command:
429
+ #
430
+ # Bundler::FileUtils.rmdir(%w[tmp0/tmp1 tmp2/tmp3], parents: true, verbose: true)
431
+ # Bundler::FileUtils.rmdir('tmp4/tmp5', parents: true, verbose: true)
432
+ #
433
+ # Output:
434
+ #
435
+ # rmdir -p tmp0/tmp1 tmp2/tmp3
436
+ # rmdir -p tmp4/tmp5
437
+ #
438
+ # Raises an exception if a directory does not exist
439
+ # or if for any reason a directory cannot be removed.
440
+ #
441
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
242
442
  #
243
443
  def rmdir(list, parents: nil, noop: nil, verbose: nil)
244
444
  list = fu_list(list)
245
445
  fu_output_message "rmdir #{parents ? '-p ' : ''}#{list.join ' '}" if verbose
246
446
  return if noop
247
447
  list.each do |dir|
248
- begin
249
- Dir.rmdir(dir = remove_trailing_slash(dir))
250
- if parents
448
+ Dir.rmdir(dir = remove_trailing_slash(dir))
449
+ if parents
450
+ begin
251
451
  until (parent = File.dirname(dir)) == '.' or parent == dir
252
452
  dir = parent
253
453
  Dir.rmdir(dir)
254
454
  end
455
+ rescue Errno::ENOTEMPTY, Errno::EEXIST, Errno::ENOENT
255
456
  end
256
- rescue Errno::ENOTEMPTY, Errno::EEXIST, Errno::ENOENT
257
457
  end
258
458
  end
259
459
  end
260
460
  module_function :rmdir
261
461
 
462
+ # Creates {hard links}[https://en.wikipedia.org/wiki/Hard_link].
463
+ #
464
+ # Arguments +src+ (a single path or an array of paths)
465
+ # and +dest+ (a single path)
466
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
262
467
  #
263
- # :call-seq:
264
- # Bundler::FileUtils.ln(target, link, force: nil, noop: nil, verbose: nil)
265
- # Bundler::FileUtils.ln(target, dir, force: nil, noop: nil, verbose: nil)
266
- # Bundler::FileUtils.ln(targets, dir, force: nil, noop: nil, verbose: nil)
468
+ # When +src+ is the path to an existing file
469
+ # and +dest+ is the path to a non-existent file,
470
+ # creates a hard link at +dest+ pointing to +src+; returns zero:
267
471
  #
268
- # In the first form, creates a hard link +link+ which points to +target+.
269
- # If +link+ already exists, raises Errno::EEXIST.
270
- # But if the :force option is set, overwrites +link+.
472
+ # Dir.children('tmp0/') # => ["t.txt"]
473
+ # Dir.children('tmp1/') # => []
474
+ # Bundler::FileUtils.ln('tmp0/t.txt', 'tmp1/t.lnk') # => 0
475
+ # Dir.children('tmp1/') # => ["t.lnk"]
271
476
  #
272
- # Bundler::FileUtils.ln 'gcc', 'cc', verbose: true
273
- # Bundler::FileUtils.ln '/usr/bin/emacs21', '/usr/bin/emacs'
477
+ # When +src+ is the path to an existing file
478
+ # and +dest+ is the path to an existing directory,
479
+ # creates a hard link at <tt>dest/src</tt> pointing to +src+; returns zero:
274
480
  #
275
- # In the second form, creates a link +dir/target+ pointing to +target+.
276
- # In the third form, creates several hard links in the directory +dir+,
277
- # pointing to each item in +targets+.
278
- # If +dir+ is not a directory, raises Errno::ENOTDIR.
481
+ # Dir.children('tmp2') # => ["t.dat"]
482
+ # Dir.children('tmp3') # => []
483
+ # Bundler::FileUtils.ln('tmp2/t.dat', 'tmp3') # => 0
484
+ # Dir.children('tmp3') # => ["t.dat"]
279
485
  #
280
- # Bundler::FileUtils.cd '/sbin'
281
- # Bundler::FileUtils.ln %w(cp mv mkdir), '/bin' # Now /sbin/cp and /bin/cp are linked.
486
+ # When +src+ is an array of paths to existing files
487
+ # and +dest+ is the path to an existing directory,
488
+ # then for each path +target+ in +src+,
489
+ # creates a hard link at <tt>dest/target</tt> pointing to +target+;
490
+ # returns +src+:
491
+ #
492
+ # Dir.children('tmp4/') # => []
493
+ # Bundler::FileUtils.ln(['tmp0/t.txt', 'tmp2/t.dat'], 'tmp4/') # => ["tmp0/t.txt", "tmp2/t.dat"]
494
+ # Dir.children('tmp4/') # => ["t.dat", "t.txt"]
495
+ #
496
+ # Keyword arguments:
497
+ #
498
+ # - <tt>force: true</tt> - overwrites +dest+ if it exists.
499
+ # - <tt>noop: true</tt> - does not create links.
500
+ # - <tt>verbose: true</tt> - prints an equivalent command:
501
+ #
502
+ # Bundler::FileUtils.ln('tmp0/t.txt', 'tmp1/t.lnk', verbose: true)
503
+ # Bundler::FileUtils.ln('tmp2/t.dat', 'tmp3', verbose: true)
504
+ # Bundler::FileUtils.ln(['tmp0/t.txt', 'tmp2/t.dat'], 'tmp4/', verbose: true)
505
+ #
506
+ # Output:
507
+ #
508
+ # ln tmp0/t.txt tmp1/t.lnk
509
+ # ln tmp2/t.dat tmp3
510
+ # ln tmp0/t.txt tmp2/t.dat tmp4/
511
+ #
512
+ # Raises an exception if +dest+ is the path to an existing file
513
+ # and keyword argument +force+ is not +true+.
514
+ #
515
+ # Related: Bundler::FileUtils.link_entry (has different options).
282
516
  #
283
517
  def ln(src, dest, force: nil, noop: nil, verbose: nil)
284
518
  fu_output_message "ln#{force ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if verbose
@@ -293,27 +527,187 @@ module Bundler::FileUtils
293
527
  alias link ln
294
528
  module_function :link
295
529
 
530
+ # Creates {hard links}[https://en.wikipedia.org/wiki/Hard_link].
531
+ #
532
+ # Arguments +src+ (a single path or an array of paths)
533
+ # and +dest+ (a single path)
534
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
535
+ #
536
+ # If +src+ is the path to a directory and +dest+ does not exist,
537
+ # creates links +dest+ and descendents pointing to +src+ and its descendents:
538
+ #
539
+ # tree('src0')
540
+ # # => src0
541
+ # # |-- sub0
542
+ # # | |-- src0.txt
543
+ # # | `-- src1.txt
544
+ # # `-- sub1
545
+ # # |-- src2.txt
546
+ # # `-- src3.txt
547
+ # File.exist?('dest0') # => false
548
+ # Bundler::FileUtils.cp_lr('src0', 'dest0')
549
+ # tree('dest0')
550
+ # # => dest0
551
+ # # |-- sub0
552
+ # # | |-- src0.txt
553
+ # # | `-- src1.txt
554
+ # # `-- sub1
555
+ # # |-- src2.txt
556
+ # # `-- src3.txt
557
+ #
558
+ # If +src+ and +dest+ are both paths to directories,
559
+ # creates links <tt>dest/src</tt> and descendents
560
+ # pointing to +src+ and its descendents:
561
+ #
562
+ # tree('src1')
563
+ # # => src1
564
+ # # |-- sub0
565
+ # # | |-- src0.txt
566
+ # # | `-- src1.txt
567
+ # # `-- sub1
568
+ # # |-- src2.txt
569
+ # # `-- src3.txt
570
+ # Bundler::FileUtils.mkdir('dest1')
571
+ # Bundler::FileUtils.cp_lr('src1', 'dest1')
572
+ # tree('dest1')
573
+ # # => dest1
574
+ # # `-- src1
575
+ # # |-- sub0
576
+ # # | |-- src0.txt
577
+ # # | `-- src1.txt
578
+ # # `-- sub1
579
+ # # |-- src2.txt
580
+ # # `-- src3.txt
581
+ #
582
+ # If +src+ is an array of paths to entries and +dest+ is the path to a directory,
583
+ # for each path +filepath+ in +src+, creates a link at <tt>dest/filepath</tt>
584
+ # pointing to that path:
585
+ #
586
+ # tree('src2')
587
+ # # => src2
588
+ # # |-- sub0
589
+ # # | |-- src0.txt
590
+ # # | `-- src1.txt
591
+ # # `-- sub1
592
+ # # |-- src2.txt
593
+ # # `-- src3.txt
594
+ # Bundler::FileUtils.mkdir('dest2')
595
+ # Bundler::FileUtils.cp_lr(['src2/sub0', 'src2/sub1'], 'dest2')
596
+ # tree('dest2')
597
+ # # => dest2
598
+ # # |-- sub0
599
+ # # | |-- src0.txt
600
+ # # | `-- src1.txt
601
+ # # `-- sub1
602
+ # # |-- src2.txt
603
+ # # `-- src3.txt
604
+ #
605
+ # Keyword arguments:
606
+ #
607
+ # - <tt>dereference_root: false</tt> - if +src+ is a symbolic link,
608
+ # does not dereference it.
609
+ # - <tt>noop: true</tt> - does not create links.
610
+ # - <tt>remove_destination: true</tt> - removes +dest+ before creating links.
611
+ # - <tt>verbose: true</tt> - prints an equivalent command:
612
+ #
613
+ # Bundler::FileUtils.cp_lr('src0', 'dest0', noop: true, verbose: true)
614
+ # Bundler::FileUtils.cp_lr('src1', 'dest1', noop: true, verbose: true)
615
+ # Bundler::FileUtils.cp_lr(['src2/sub0', 'src2/sub1'], 'dest2', noop: true, verbose: true)
616
+ #
617
+ # Output:
618
+ #
619
+ # cp -lr src0 dest0
620
+ # cp -lr src1 dest1
621
+ # cp -lr src2/sub0 src2/sub1 dest2
622
+ #
623
+ # Raises an exception if +dest+ is the path to an existing file or directory
624
+ # and keyword argument <tt>remove_destination: true</tt> is not given.
625
+ #
626
+ # Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
627
+ #
628
+ def cp_lr(src, dest, noop: nil, verbose: nil,
629
+ dereference_root: true, remove_destination: false)
630
+ fu_output_message "cp -lr#{remove_destination ? ' --remove-destination' : ''} #{[src,dest].flatten.join ' '}" if verbose
631
+ return if noop
632
+ fu_each_src_dest(src, dest) do |s, d|
633
+ link_entry s, d, dereference_root, remove_destination
634
+ end
635
+ end
636
+ module_function :cp_lr
637
+
638
+ # Creates {symbolic links}[https://en.wikipedia.org/wiki/Symbolic_link].
639
+ #
640
+ # Arguments +src+ (a single path or an array of paths)
641
+ # and +dest+ (a single path)
642
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
643
+ #
644
+ # If +src+ is the path to an existing file:
645
+ #
646
+ # - When +dest+ is the path to a non-existent file,
647
+ # creates a symbolic link at +dest+ pointing to +src+:
648
+ #
649
+ # Bundler::FileUtils.touch('src0.txt')
650
+ # File.exist?('dest0.txt') # => false
651
+ # Bundler::FileUtils.ln_s('src0.txt', 'dest0.txt')
652
+ # File.symlink?('dest0.txt') # => true
653
+ #
654
+ # - When +dest+ is the path to an existing file,
655
+ # creates a symbolic link at +dest+ pointing to +src+
656
+ # if and only if keyword argument <tt>force: true</tt> is given
657
+ # (raises an exception otherwise):
658
+ #
659
+ # Bundler::FileUtils.touch('src1.txt')
660
+ # Bundler::FileUtils.touch('dest1.txt')
661
+ # Bundler::FileUtils.ln_s('src1.txt', 'dest1.txt', force: true)
662
+ # FileTest.symlink?('dest1.txt') # => true
663
+ #
664
+ # Bundler::FileUtils.ln_s('src1.txt', 'dest1.txt') # Raises Errno::EEXIST.
665
+ #
666
+ # If +dest+ is the path to a directory,
667
+ # creates a symbolic link at <tt>dest/src</tt> pointing to +src+:
296
668
  #
297
- # :call-seq:
298
- # Bundler::FileUtils.ln_s(target, link, force: nil, noop: nil, verbose: nil)
299
- # Bundler::FileUtils.ln_s(target, dir, force: nil, noop: nil, verbose: nil)
300
- # Bundler::FileUtils.ln_s(targets, dir, force: nil, noop: nil, verbose: nil)
669
+ # Bundler::FileUtils.touch('src2.txt')
670
+ # Bundler::FileUtils.mkdir('destdir2')
671
+ # Bundler::FileUtils.ln_s('src2.txt', 'destdir2')
672
+ # File.symlink?('destdir2/src2.txt') # => true
301
673
  #
302
- # In the first form, creates a symbolic link +link+ which points to +target+.
303
- # If +link+ already exists, raises Errno::EEXIST.
304
- # But if the :force option is set, overwrites +link+.
674
+ # If +src+ is an array of paths to existing files and +dest+ is a directory,
675
+ # for each child +child+ in +src+ creates a symbolic link <tt>dest/child</tt>
676
+ # pointing to +child+:
305
677
  #
306
- # Bundler::FileUtils.ln_s '/usr/bin/ruby', '/usr/local/bin/ruby'
307
- # Bundler::FileUtils.ln_s 'verylongsourcefilename.c', 'c', force: true
678
+ # Bundler::FileUtils.mkdir('srcdir3')
679
+ # Bundler::FileUtils.touch('srcdir3/src0.txt')
680
+ # Bundler::FileUtils.touch('srcdir3/src1.txt')
681
+ # Bundler::FileUtils.mkdir('destdir3')
682
+ # Bundler::FileUtils.ln_s(['srcdir3/src0.txt', 'srcdir3/src1.txt'], 'destdir3')
683
+ # File.symlink?('destdir3/src0.txt') # => true
684
+ # File.symlink?('destdir3/src1.txt') # => true
308
685
  #
309
- # In the second form, creates a link +dir/target+ pointing to +target+.
310
- # In the third form, creates several symbolic links in the directory +dir+,
311
- # pointing to each item in +targets+.
312
- # If +dir+ is not a directory, raises Errno::ENOTDIR.
686
+ # Keyword arguments:
313
687
  #
314
- # Bundler::FileUtils.ln_s Dir.glob('/bin/*.rb'), '/home/foo/bin'
688
+ # - <tt>force: true</tt> - overwrites +dest+ if it exists.
689
+ # - <tt>relative: false</tt> - create links relative to +dest+.
690
+ # - <tt>noop: true</tt> - does not create links.
691
+ # - <tt>verbose: true</tt> - prints an equivalent command:
315
692
  #
316
- def ln_s(src, dest, force: nil, noop: nil, verbose: nil)
693
+ # Bundler::FileUtils.ln_s('src0.txt', 'dest0.txt', noop: true, verbose: true)
694
+ # Bundler::FileUtils.ln_s('src1.txt', 'destdir1', noop: true, verbose: true)
695
+ # Bundler::FileUtils.ln_s('src2.txt', 'dest2.txt', force: true, noop: true, verbose: true)
696
+ # Bundler::FileUtils.ln_s(['srcdir3/src0.txt', 'srcdir3/src1.txt'], 'destdir3', noop: true, verbose: true)
697
+ #
698
+ # Output:
699
+ #
700
+ # ln -s src0.txt dest0.txt
701
+ # ln -s src1.txt destdir1
702
+ # ln -sf src2.txt dest2.txt
703
+ # ln -s srcdir3/src0.txt srcdir3/src1.txt destdir3
704
+ #
705
+ # Related: Bundler::FileUtils.ln_sf.
706
+ #
707
+ def ln_s(src, dest, force: nil, relative: false, target_directory: true, noop: nil, verbose: nil)
708
+ if relative
709
+ return ln_sr(src, dest, force: force, noop: noop, verbose: verbose)
710
+ end
317
711
  fu_output_message "ln -s#{force ? 'f' : ''} #{[src,dest].flatten.join ' '}" if verbose
318
712
  return if noop
319
713
  fu_each_src_dest0(src, dest) do |s,d|
@@ -326,29 +720,156 @@ module Bundler::FileUtils
326
720
  alias symlink ln_s
327
721
  module_function :symlink
328
722
 
329
- #
330
- # :call-seq:
331
- # Bundler::FileUtils.ln_sf(*args)
332
- #
333
- # Same as
334
- #
335
- # Bundler::FileUtils.ln_s(*args, force: true)
723
+ # Like Bundler::FileUtils.ln_s, but always with keyword argument <tt>force: true</tt> given.
336
724
  #
337
725
  def ln_sf(src, dest, noop: nil, verbose: nil)
338
726
  ln_s src, dest, force: true, noop: noop, verbose: verbose
339
727
  end
340
728
  module_function :ln_sf
341
729
 
730
+ # Like Bundler::FileUtils.ln_s, but create links relative to +dest+.
731
+ #
732
+ def ln_sr(src, dest, target_directory: true, force: nil, noop: nil, verbose: nil)
733
+ options = "#{force ? 'f' : ''}#{target_directory ? '' : 'T'}"
734
+ dest = File.path(dest)
735
+ srcs = Array(src)
736
+ link = proc do |s, target_dir_p = true|
737
+ s = File.path(s)
738
+ if target_dir_p
739
+ d = File.join(destdirs = dest, File.basename(s))
740
+ else
741
+ destdirs = File.dirname(d = dest)
742
+ end
743
+ destdirs = fu_split_path(File.realpath(destdirs))
744
+ if fu_starting_path?(s)
745
+ srcdirs = fu_split_path((File.realdirpath(s) rescue File.expand_path(s)))
746
+ base = fu_relative_components_from(srcdirs, destdirs)
747
+ s = File.join(*base)
748
+ else
749
+ srcdirs = fu_clean_components(*fu_split_path(s))
750
+ base = fu_relative_components_from(fu_split_path(Dir.pwd), destdirs)
751
+ while srcdirs.first&. == ".." and base.last&.!=("..") and !fu_starting_path?(base.last)
752
+ srcdirs.shift
753
+ base.pop
754
+ end
755
+ s = File.join(*base, *srcdirs)
756
+ end
757
+ fu_output_message "ln -s#{options} #{s} #{d}" if verbose
758
+ next if noop
759
+ remove_file d, true if force
760
+ File.symlink s, d
761
+ end
762
+ case srcs.size
763
+ when 0
764
+ when 1
765
+ link[srcs[0], target_directory && File.directory?(dest)]
766
+ else
767
+ srcs.each(&link)
768
+ end
769
+ end
770
+ module_function :ln_sr
771
+
772
+ # Creates {hard links}[https://en.wikipedia.org/wiki/Hard_link]; returns +nil+.
773
+ #
774
+ # Arguments +src+ and +dest+
775
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
776
+ #
777
+ # If +src+ is the path to a file and +dest+ does not exist,
778
+ # creates a hard link at +dest+ pointing to +src+:
779
+ #
780
+ # Bundler::FileUtils.touch('src0.txt')
781
+ # File.exist?('dest0.txt') # => false
782
+ # Bundler::FileUtils.link_entry('src0.txt', 'dest0.txt')
783
+ # File.file?('dest0.txt') # => true
784
+ #
785
+ # If +src+ is the path to a directory and +dest+ does not exist,
786
+ # recursively creates hard links at +dest+ pointing to paths in +src+:
787
+ #
788
+ # Bundler::FileUtils.mkdir_p(['src1/dir0', 'src1/dir1'])
789
+ # src_file_paths = [
790
+ # 'src1/dir0/t0.txt',
791
+ # 'src1/dir0/t1.txt',
792
+ # 'src1/dir1/t2.txt',
793
+ # 'src1/dir1/t3.txt',
794
+ # ]
795
+ # Bundler::FileUtils.touch(src_file_paths)
796
+ # File.directory?('dest1') # => true
797
+ # Bundler::FileUtils.link_entry('src1', 'dest1')
798
+ # File.file?('dest1/dir0/t0.txt') # => true
799
+ # File.file?('dest1/dir0/t1.txt') # => true
800
+ # File.file?('dest1/dir1/t2.txt') # => true
801
+ # File.file?('dest1/dir1/t3.txt') # => true
802
+ #
803
+ # Keyword arguments:
804
+ #
805
+ # - <tt>dereference_root: true</tt> - dereferences +src+ if it is a symbolic link.
806
+ # - <tt>remove_destination: true</tt> - removes +dest+ before creating links.
807
+ #
808
+ # Raises an exception if +dest+ is the path to an existing file or directory
809
+ # and keyword argument <tt>remove_destination: true</tt> is not given.
810
+ #
811
+ # Related: Bundler::FileUtils.ln (has different options).
812
+ #
813
+ def link_entry(src, dest, dereference_root = false, remove_destination = false)
814
+ Entry_.new(src, nil, dereference_root).traverse do |ent|
815
+ destent = Entry_.new(dest, ent.rel, false)
816
+ File.unlink destent.path if remove_destination && File.file?(destent.path)
817
+ ent.link destent.path
818
+ end
819
+ end
820
+ module_function :link_entry
821
+
822
+ # Copies files.
823
+ #
824
+ # Arguments +src+ (a single path or an array of paths)
825
+ # and +dest+ (a single path)
826
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
827
+ #
828
+ # If +src+ is the path to a file and +dest+ is not the path to a directory,
829
+ # copies +src+ to +dest+:
830
+ #
831
+ # Bundler::FileUtils.touch('src0.txt')
832
+ # File.exist?('dest0.txt') # => false
833
+ # Bundler::FileUtils.cp('src0.txt', 'dest0.txt')
834
+ # File.file?('dest0.txt') # => true
835
+ #
836
+ # If +src+ is the path to a file and +dest+ is the path to a directory,
837
+ # copies +src+ to <tt>dest/src</tt>:
838
+ #
839
+ # Bundler::FileUtils.touch('src1.txt')
840
+ # Bundler::FileUtils.mkdir('dest1')
841
+ # Bundler::FileUtils.cp('src1.txt', 'dest1')
842
+ # File.file?('dest1/src1.txt') # => true
342
843
  #
343
- # Copies a file content +src+ to +dest+. If +dest+ is a directory,
344
- # copies +src+ to +dest/src+.
844
+ # If +src+ is an array of paths to files and +dest+ is the path to a directory,
845
+ # copies from each +src+ to +dest+:
345
846
  #
346
- # If +src+ is a list of files, then +dest+ must be a directory.
847
+ # src_file_paths = ['src2.txt', 'src2.dat']
848
+ # Bundler::FileUtils.touch(src_file_paths)
849
+ # Bundler::FileUtils.mkdir('dest2')
850
+ # Bundler::FileUtils.cp(src_file_paths, 'dest2')
851
+ # File.file?('dest2/src2.txt') # => true
852
+ # File.file?('dest2/src2.dat') # => true
347
853
  #
348
- # Bundler::FileUtils.cp 'eval.c', 'eval.c.org'
349
- # Bundler::FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6'
350
- # Bundler::FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6', :verbose => true
351
- # Bundler::FileUtils.cp 'symlink', 'dest' # copy content, "dest" is not a symlink
854
+ # Keyword arguments:
855
+ #
856
+ # - <tt>preserve: true</tt> - preserves file times.
857
+ # - <tt>noop: true</tt> - does not copy files.
858
+ # - <tt>verbose: true</tt> - prints an equivalent command:
859
+ #
860
+ # Bundler::FileUtils.cp('src0.txt', 'dest0.txt', noop: true, verbose: true)
861
+ # Bundler::FileUtils.cp('src1.txt', 'dest1', noop: true, verbose: true)
862
+ # Bundler::FileUtils.cp(src_file_paths, 'dest2', noop: true, verbose: true)
863
+ #
864
+ # Output:
865
+ #
866
+ # cp src0.txt dest0.txt
867
+ # cp src1.txt dest1
868
+ # cp src2.txt src2.dat dest2
869
+ #
870
+ # Raises an exception if +src+ is a directory.
871
+ #
872
+ # Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
352
873
  #
353
874
  def cp(src, dest, preserve: nil, noop: nil, verbose: nil)
354
875
  fu_output_message "cp#{preserve ? ' -p' : ''} #{[src,dest].flatten.join ' '}" if verbose
@@ -362,26 +883,105 @@ module Bundler::FileUtils
362
883
  alias copy cp
363
884
  module_function :copy
364
885
 
365
- #
366
- # Copies +src+ to +dest+. If +src+ is a directory, this method copies
367
- # all its contents recursively. If +dest+ is a directory, copies
368
- # +src+ to +dest/src+.
369
- #
370
- # +src+ can be a list of files.
371
- #
372
- # # Installing Ruby library "mylib" under the site_ruby
373
- # Bundler::FileUtils.rm_r site_ruby + '/mylib', :force
374
- # Bundler::FileUtils.cp_r 'lib/', site_ruby + '/mylib'
375
- #
376
- # # Examples of copying several files to target directory.
377
- # Bundler::FileUtils.cp_r %w(mail.rb field.rb debug/), site_ruby + '/tmail'
378
- # Bundler::FileUtils.cp_r Dir.glob('*.rb'), '/home/foo/lib/ruby', :noop => true, :verbose => true
379
- #
380
- # # If you want to copy all contents of a directory instead of the
381
- # # directory itself, c.f. src/x -> dest/x, src/y -> dest/y,
382
- # # use following code.
383
- # Bundler::FileUtils.cp_r 'src/.', 'dest' # cp_r('src', 'dest') makes dest/src,
384
- # # but this doesn't.
886
+ # Recursively copies files.
887
+ #
888
+ # Arguments +src+ (a single path or an array of paths)
889
+ # and +dest+ (a single path)
890
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
891
+ #
892
+ # The mode, owner, and group are retained in the copy;
893
+ # to change those, use Bundler::FileUtils.install instead.
894
+ #
895
+ # If +src+ is the path to a file and +dest+ is not the path to a directory,
896
+ # copies +src+ to +dest+:
897
+ #
898
+ # Bundler::FileUtils.touch('src0.txt')
899
+ # File.exist?('dest0.txt') # => false
900
+ # Bundler::FileUtils.cp_r('src0.txt', 'dest0.txt')
901
+ # File.file?('dest0.txt') # => true
902
+ #
903
+ # If +src+ is the path to a file and +dest+ is the path to a directory,
904
+ # copies +src+ to <tt>dest/src</tt>:
905
+ #
906
+ # Bundler::FileUtils.touch('src1.txt')
907
+ # Bundler::FileUtils.mkdir('dest1')
908
+ # Bundler::FileUtils.cp_r('src1.txt', 'dest1')
909
+ # File.file?('dest1/src1.txt') # => true
910
+ #
911
+ # If +src+ is the path to a directory and +dest+ does not exist,
912
+ # recursively copies +src+ to +dest+:
913
+ #
914
+ # tree('src2')
915
+ # # => src2
916
+ # # |-- dir0
917
+ # # | |-- src0.txt
918
+ # # | `-- src1.txt
919
+ # # `-- dir1
920
+ # # |-- src2.txt
921
+ # # `-- src3.txt
922
+ # Bundler::FileUtils.exist?('dest2') # => false
923
+ # Bundler::FileUtils.cp_r('src2', 'dest2')
924
+ # tree('dest2')
925
+ # # => dest2
926
+ # # |-- dir0
927
+ # # | |-- src0.txt
928
+ # # | `-- src1.txt
929
+ # # `-- dir1
930
+ # # |-- src2.txt
931
+ # # `-- src3.txt
932
+ #
933
+ # If +src+ and +dest+ are paths to directories,
934
+ # recursively copies +src+ to <tt>dest/src</tt>:
935
+ #
936
+ # tree('src3')
937
+ # # => src3
938
+ # # |-- dir0
939
+ # # | |-- src0.txt
940
+ # # | `-- src1.txt
941
+ # # `-- dir1
942
+ # # |-- src2.txt
943
+ # # `-- src3.txt
944
+ # Bundler::FileUtils.mkdir('dest3')
945
+ # Bundler::FileUtils.cp_r('src3', 'dest3')
946
+ # tree('dest3')
947
+ # # => dest3
948
+ # # `-- src3
949
+ # # |-- dir0
950
+ # # | |-- src0.txt
951
+ # # | `-- src1.txt
952
+ # # `-- dir1
953
+ # # |-- src2.txt
954
+ # # `-- src3.txt
955
+ #
956
+ # If +src+ is an array of paths and +dest+ is a directory,
957
+ # recursively copies from each path in +src+ to +dest+;
958
+ # the paths in +src+ may point to files and/or directories.
959
+ #
960
+ # Keyword arguments:
961
+ #
962
+ # - <tt>dereference_root: false</tt> - if +src+ is a symbolic link,
963
+ # does not dereference it.
964
+ # - <tt>noop: true</tt> - does not copy files.
965
+ # - <tt>preserve: true</tt> - preserves file times.
966
+ # - <tt>remove_destination: true</tt> - removes +dest+ before copying files.
967
+ # - <tt>verbose: true</tt> - prints an equivalent command:
968
+ #
969
+ # Bundler::FileUtils.cp_r('src0.txt', 'dest0.txt', noop: true, verbose: true)
970
+ # Bundler::FileUtils.cp_r('src1.txt', 'dest1', noop: true, verbose: true)
971
+ # Bundler::FileUtils.cp_r('src2', 'dest2', noop: true, verbose: true)
972
+ # Bundler::FileUtils.cp_r('src3', 'dest3', noop: true, verbose: true)
973
+ #
974
+ # Output:
975
+ #
976
+ # cp -r src0.txt dest0.txt
977
+ # cp -r src1.txt dest1
978
+ # cp -r src2 dest2
979
+ # cp -r src3 dest3
980
+ #
981
+ # Raises an exception of +src+ is the path to a directory
982
+ # and +dest+ is the path to a file.
983
+ #
984
+ # Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
385
985
  #
386
986
  def cp_r(src, dest, preserve: nil, noop: nil, verbose: nil,
387
987
  dereference_root: true, remove_destination: nil)
@@ -393,26 +993,59 @@ module Bundler::FileUtils
393
993
  end
394
994
  module_function :cp_r
395
995
 
996
+ # Recursively copies files from +src+ to +dest+.
997
+ #
998
+ # Arguments +src+ and +dest+
999
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
1000
+ #
1001
+ # If +src+ is the path to a file, copies +src+ to +dest+:
1002
+ #
1003
+ # Bundler::FileUtils.touch('src0.txt')
1004
+ # File.exist?('dest0.txt') # => false
1005
+ # Bundler::FileUtils.copy_entry('src0.txt', 'dest0.txt')
1006
+ # File.file?('dest0.txt') # => true
1007
+ #
1008
+ # If +src+ is a directory, recursively copies +src+ to +dest+:
396
1009
  #
397
- # Copies a file system entry +src+ to +dest+.
398
- # If +src+ is a directory, this method copies its contents recursively.
399
- # This method preserves file types, c.f. symlink, directory...
400
- # (FIFO, device files and etc. are not supported yet)
1010
+ # tree('src1')
1011
+ # # => src1
1012
+ # # |-- dir0
1013
+ # # | |-- src0.txt
1014
+ # # | `-- src1.txt
1015
+ # # `-- dir1
1016
+ # # |-- src2.txt
1017
+ # # `-- src3.txt
1018
+ # Bundler::FileUtils.copy_entry('src1', 'dest1')
1019
+ # tree('dest1')
1020
+ # # => dest1
1021
+ # # |-- dir0
1022
+ # # | |-- src0.txt
1023
+ # # | `-- src1.txt
1024
+ # # `-- dir1
1025
+ # # |-- src2.txt
1026
+ # # `-- src3.txt
401
1027
  #
402
- # Both of +src+ and +dest+ must be a path name.
403
- # +src+ must exist, +dest+ must not exist.
1028
+ # The recursive copying preserves file types for regular files,
1029
+ # directories, and symbolic links;
1030
+ # other file types (FIFO streams, device files, etc.) are not supported.
404
1031
  #
405
- # If +preserve+ is true, this method preserves owner, group, and
406
- # modified time. Permissions are copied regardless +preserve+.
1032
+ # Keyword arguments:
407
1033
  #
408
- # If +dereference_root+ is true, this method dereference tree root.
1034
+ # - <tt>dereference_root: true</tt> - if +src+ is a symbolic link,
1035
+ # follows the link.
1036
+ # - <tt>preserve: true</tt> - preserves file times.
1037
+ # - <tt>remove_destination: true</tt> - removes +dest+ before copying files.
409
1038
  #
410
- # If +remove_destination+ is true, this method removes each destination file before copy.
1039
+ # Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
411
1040
  #
412
1041
  def copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = false)
413
- Entry_.new(src, nil, dereference_root).wrap_traverse(proc do |ent|
1042
+ if dereference_root
1043
+ src = File.realpath(src)
1044
+ end
1045
+
1046
+ Entry_.new(src, nil, false).wrap_traverse(proc do |ent|
414
1047
  destent = Entry_.new(dest, ent.rel, false)
415
- File.unlink destent.path if remove_destination && File.file?(destent.path)
1048
+ File.unlink destent.path if remove_destination && (File.file?(destent.path) || File.symlink?(destent.path))
416
1049
  ent.copy destent.path
417
1050
  end, proc do |ent|
418
1051
  destent = Entry_.new(dest, ent.rel, false)
@@ -421,9 +1054,25 @@ module Bundler::FileUtils
421
1054
  end
422
1055
  module_function :copy_entry
423
1056
 
1057
+ # Copies file from +src+ to +dest+, which should not be directories.
1058
+ #
1059
+ # Arguments +src+ and +dest+
1060
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
1061
+ #
1062
+ # Examples:
424
1063
  #
425
- # Copies file contents of +src+ to +dest+.
426
- # Both of +src+ and +dest+ must be a path name.
1064
+ # Bundler::FileUtils.touch('src0.txt')
1065
+ # Bundler::FileUtils.copy_file('src0.txt', 'dest0.txt')
1066
+ # File.file?('dest0.txt') # => true
1067
+ #
1068
+ # Keyword arguments:
1069
+ #
1070
+ # - <tt>dereference: false</tt> - if +src+ is a symbolic link,
1071
+ # does not follow the link.
1072
+ # - <tt>preserve: true</tt> - preserves file times.
1073
+ # - <tt>remove_destination: true</tt> - removes +dest+ before copying files.
1074
+ #
1075
+ # Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
427
1076
  #
428
1077
  def copy_file(src, dest, preserve = false, dereference = true)
429
1078
  ent = Entry_.new(src, nil, dereference)
@@ -432,25 +1081,79 @@ module Bundler::FileUtils
432
1081
  end
433
1082
  module_function :copy_file
434
1083
 
1084
+ # Copies \IO stream +src+ to \IO stream +dest+ via
1085
+ # {IO.copy_stream}[https://docs.ruby-lang.org/en/master/IO.html#method-c-copy_stream].
435
1086
  #
436
- # Copies stream +src+ to +dest+.
437
- # +src+ must respond to #read(n) and
438
- # +dest+ must respond to #write(str).
1087
+ # Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
439
1088
  #
440
1089
  def copy_stream(src, dest)
441
1090
  IO.copy_stream(src, dest)
442
1091
  end
443
1092
  module_function :copy_stream
444
1093
 
445
- #
446
- # Moves file(s) +src+ to +dest+. If +file+ and +dest+ exist on the different
447
- # disk partition, the file is copied then the original file is removed.
448
- #
449
- # Bundler::FileUtils.mv 'badname.rb', 'goodname.rb'
450
- # Bundler::FileUtils.mv 'stuff.rb', '/notexist/lib/ruby', :force => true # no error
451
- #
452
- # Bundler::FileUtils.mv %w(junk.txt dust.txt), '/home/foo/.trash/'
453
- # Bundler::FileUtils.mv Dir.glob('test*.rb'), 'test', :noop => true, :verbose => true
1094
+ # Moves entries.
1095
+ #
1096
+ # Arguments +src+ (a single path or an array of paths)
1097
+ # and +dest+ (a single path)
1098
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
1099
+ #
1100
+ # If +src+ and +dest+ are on different file systems,
1101
+ # first copies, then removes +src+.
1102
+ #
1103
+ # May cause a local vulnerability if not called with keyword argument
1104
+ # <tt>secure: true</tt>;
1105
+ # see {Avoiding the TOCTTOU Vulnerability}[rdoc-ref:FileUtils@Avoiding+the+TOCTTOU+Vulnerability].
1106
+ #
1107
+ # If +src+ is the path to a single file or directory and +dest+ does not exist,
1108
+ # moves +src+ to +dest+:
1109
+ #
1110
+ # tree('src0')
1111
+ # # => src0
1112
+ # # |-- src0.txt
1113
+ # # `-- src1.txt
1114
+ # File.exist?('dest0') # => false
1115
+ # Bundler::FileUtils.mv('src0', 'dest0')
1116
+ # File.exist?('src0') # => false
1117
+ # tree('dest0')
1118
+ # # => dest0
1119
+ # # |-- src0.txt
1120
+ # # `-- src1.txt
1121
+ #
1122
+ # If +src+ is an array of paths to files and directories
1123
+ # and +dest+ is the path to a directory,
1124
+ # copies from each path in the array to +dest+:
1125
+ #
1126
+ # File.file?('src1.txt') # => true
1127
+ # tree('src1')
1128
+ # # => src1
1129
+ # # |-- src.dat
1130
+ # # `-- src.txt
1131
+ # Dir.empty?('dest1') # => true
1132
+ # Bundler::FileUtils.mv(['src1.txt', 'src1'], 'dest1')
1133
+ # tree('dest1')
1134
+ # # => dest1
1135
+ # # |-- src1
1136
+ # # | |-- src.dat
1137
+ # # | `-- src.txt
1138
+ # # `-- src1.txt
1139
+ #
1140
+ # Keyword arguments:
1141
+ #
1142
+ # - <tt>force: true</tt> - if the move includes removing +src+
1143
+ # (that is, if +src+ and +dest+ are on different file systems),
1144
+ # ignores raised exceptions of StandardError and its descendants.
1145
+ # - <tt>noop: true</tt> - does not move files.
1146
+ # - <tt>secure: true</tt> - removes +src+ securely;
1147
+ # see details at Bundler::FileUtils.remove_entry_secure.
1148
+ # - <tt>verbose: true</tt> - prints an equivalent command:
1149
+ #
1150
+ # Bundler::FileUtils.mv('src0', 'dest0', noop: true, verbose: true)
1151
+ # Bundler::FileUtils.mv(['src1.txt', 'src1'], 'dest1', noop: true, verbose: true)
1152
+ #
1153
+ # Output:
1154
+ #
1155
+ # mv src0 dest0
1156
+ # mv src1.txt src1 dest1
454
1157
  #
455
1158
  def mv(src, dest, force: nil, noop: nil, verbose: nil, secure: nil)
456
1159
  fu_output_message "mv#{force ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if verbose
@@ -461,13 +1164,12 @@ module Bundler::FileUtils
461
1164
  if destent.exist?
462
1165
  if destent.directory?
463
1166
  raise Errno::EEXIST, d
464
- else
465
- destent.remove_file if rename_cannot_overwrite_file?
466
1167
  end
467
1168
  end
468
1169
  begin
469
1170
  File.rename s, d
470
- rescue Errno::EXDEV
1171
+ rescue Errno::EXDEV,
1172
+ Errno::EPERM # move from unencrypted to encrypted dir (ext4)
471
1173
  copy_entry s, d, true
472
1174
  if secure
473
1175
  remove_entry_secure s, force
@@ -485,18 +1187,32 @@ module Bundler::FileUtils
485
1187
  alias move mv
486
1188
  module_function :move
487
1189
 
488
- def rename_cannot_overwrite_file? #:nodoc:
489
- /emx/ =~ RUBY_PLATFORM
490
- end
491
- private_module_function :rename_cannot_overwrite_file?
492
-
1190
+ # Removes entries at the paths in the given +list+
1191
+ # (a single path or an array of paths)
1192
+ # returns +list+, if it is an array, <tt>[list]</tt> otherwise.
1193
+ #
1194
+ # Argument +list+ or its elements
1195
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
493
1196
  #
494
- # Remove file(s) specified in +list+. This method cannot remove directories.
495
- # All StandardErrors are ignored when the :force option is set.
1197
+ # With no keyword arguments, removes files at the paths given in +list+:
496
1198
  #
497
- # Bundler::FileUtils.rm %w( junk.txt dust.txt )
498
- # Bundler::FileUtils.rm Dir.glob('*.so')
499
- # Bundler::FileUtils.rm 'NotExistFile', :force => true # never raises exception
1199
+ # Bundler::FileUtils.touch(['src0.txt', 'src0.dat'])
1200
+ # Bundler::FileUtils.rm(['src0.dat', 'src0.txt']) # => ["src0.dat", "src0.txt"]
1201
+ #
1202
+ # Keyword arguments:
1203
+ #
1204
+ # - <tt>force: true</tt> - ignores raised exceptions of StandardError
1205
+ # and its descendants.
1206
+ # - <tt>noop: true</tt> - does not remove files; returns +nil+.
1207
+ # - <tt>verbose: true</tt> - prints an equivalent command:
1208
+ #
1209
+ # Bundler::FileUtils.rm(['src0.dat', 'src0.txt'], noop: true, verbose: true)
1210
+ #
1211
+ # Output:
1212
+ #
1213
+ # rm src0.dat src0.txt
1214
+ #
1215
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
500
1216
  #
501
1217
  def rm(list, force: nil, noop: nil, verbose: nil)
502
1218
  list = fu_list(list)
@@ -512,10 +1228,16 @@ module Bundler::FileUtils
512
1228
  alias remove rm
513
1229
  module_function :remove
514
1230
 
1231
+ # Equivalent to:
515
1232
  #
516
- # Equivalent to
1233
+ # Bundler::FileUtils.rm(list, force: true, **kwargs)
517
1234
  #
518
- # Bundler::FileUtils.rm(list, :force => true)
1235
+ # Argument +list+ (a single path or an array of paths)
1236
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
1237
+ #
1238
+ # See Bundler::FileUtils.rm for keyword arguments.
1239
+ #
1240
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
519
1241
  #
520
1242
  def rm_f(list, noop: nil, verbose: nil)
521
1243
  rm list, force: true, noop: noop, verbose: verbose
@@ -525,24 +1247,55 @@ module Bundler::FileUtils
525
1247
  alias safe_unlink rm_f
526
1248
  module_function :safe_unlink
527
1249
 
1250
+ # Removes entries at the paths in the given +list+
1251
+ # (a single path or an array of paths);
1252
+ # returns +list+, if it is an array, <tt>[list]</tt> otherwise.
1253
+ #
1254
+ # Argument +list+ or its elements
1255
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
1256
+ #
1257
+ # May cause a local vulnerability if not called with keyword argument
1258
+ # <tt>secure: true</tt>;
1259
+ # see {Avoiding the TOCTTOU Vulnerability}[rdoc-ref:FileUtils@Avoiding+the+TOCTTOU+Vulnerability].
1260
+ #
1261
+ # For each file path, removes the file at that path:
528
1262
  #
529
- # remove files +list+[0] +list+[1]... If +list+[n] is a directory,
530
- # removes its all contents recursively. This method ignores
531
- # StandardError when :force option is set.
1263
+ # Bundler::FileUtils.touch(['src0.txt', 'src0.dat'])
1264
+ # Bundler::FileUtils.rm_r(['src0.dat', 'src0.txt'])
1265
+ # File.exist?('src0.txt') # => false
1266
+ # File.exist?('src0.dat') # => false
532
1267
  #
533
- # Bundler::FileUtils.rm_r Dir.glob('/tmp/*')
534
- # Bundler::FileUtils.rm_r 'some_dir', :force => true
1268
+ # For each directory path, recursively removes files and directories:
535
1269
  #
536
- # WARNING: This method causes local vulnerability
537
- # if one of parent directories or removing directory tree are world
538
- # writable (including /tmp, whose permission is 1777), and the current
539
- # process has strong privilege such as Unix super user (root), and the
540
- # system has symbolic link. For secure removing, read the documentation
541
- # of #remove_entry_secure carefully, and set :secure option to true.
542
- # Default is :secure=>false.
1270
+ # tree('src1')
1271
+ # # => src1
1272
+ # # |-- dir0
1273
+ # # | |-- src0.txt
1274
+ # # | `-- src1.txt
1275
+ # # `-- dir1
1276
+ # # |-- src2.txt
1277
+ # # `-- src3.txt
1278
+ # Bundler::FileUtils.rm_r('src1')
1279
+ # File.exist?('src1') # => false
543
1280
  #
544
- # NOTE: This method calls #remove_entry_secure if :secure option is set.
545
- # See also #remove_entry_secure.
1281
+ # Keyword arguments:
1282
+ #
1283
+ # - <tt>force: true</tt> - ignores raised exceptions of StandardError
1284
+ # and its descendants.
1285
+ # - <tt>noop: true</tt> - does not remove entries; returns +nil+.
1286
+ # - <tt>secure: true</tt> - removes +src+ securely;
1287
+ # see details at Bundler::FileUtils.remove_entry_secure.
1288
+ # - <tt>verbose: true</tt> - prints an equivalent command:
1289
+ #
1290
+ # Bundler::FileUtils.rm_r(['src0.dat', 'src0.txt'], noop: true, verbose: true)
1291
+ # Bundler::FileUtils.rm_r('src1', noop: true, verbose: true)
1292
+ #
1293
+ # Output:
1294
+ #
1295
+ # rm -r src0.dat src0.txt
1296
+ # rm -r src1
1297
+ #
1298
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
546
1299
  #
547
1300
  def rm_r(list, force: nil, noop: nil, verbose: nil, secure: nil)
548
1301
  list = fu_list(list)
@@ -558,13 +1311,20 @@ module Bundler::FileUtils
558
1311
  end
559
1312
  module_function :rm_r
560
1313
 
1314
+ # Equivalent to:
561
1315
  #
562
- # Equivalent to
1316
+ # Bundler::FileUtils.rm_r(list, force: true, **kwargs)
563
1317
  #
564
- # Bundler::FileUtils.rm_r(list, :force => true)
1318
+ # Argument +list+ or its elements
1319
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
565
1320
  #
566
- # WARNING: This method causes local vulnerability.
567
- # Read the documentation of #rm_r first.
1321
+ # May cause a local vulnerability if not called with keyword argument
1322
+ # <tt>secure: true</tt>;
1323
+ # see {Avoiding the TOCTTOU Vulnerability}[rdoc-ref:FileUtils@Avoiding+the+TOCTTOU+Vulnerability].
1324
+ #
1325
+ # See Bundler::FileUtils.rm_r for keyword arguments.
1326
+ #
1327
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
568
1328
  #
569
1329
  def rm_rf(list, noop: nil, verbose: nil, secure: nil)
570
1330
  rm_r list, force: true, noop: noop, verbose: verbose, secure: secure
@@ -574,37 +1334,20 @@ module Bundler::FileUtils
574
1334
  alias rmtree rm_rf
575
1335
  module_function :rmtree
576
1336
 
1337
+ # Securely removes the entry given by +path+,
1338
+ # which should be the entry for a regular file, a symbolic link,
1339
+ # or a directory.
577
1340
  #
578
- # This method removes a file system entry +path+. +path+ shall be a
579
- # regular file, a directory, or something. If +path+ is a directory,
580
- # remove it recursively. This method is required to avoid TOCTTOU
581
- # (time-of-check-to-time-of-use) local security vulnerability of #rm_r.
582
- # #rm_r causes security hole when:
583
- #
584
- # * Parent directory is world writable (including /tmp).
585
- # * Removing directory tree includes world writable directory.
586
- # * The system has symbolic link.
587
- #
588
- # To avoid this security hole, this method applies special preprocess.
589
- # If +path+ is a directory, this method chown(2) and chmod(2) all
590
- # removing directories. This requires the current process is the
591
- # owner of the removing whole directory tree, or is the super user (root).
592
- #
593
- # WARNING: You must ensure that *ALL* parent directories cannot be
594
- # moved by other untrusted users. For example, parent directories
595
- # should not be owned by untrusted users, and should not be world
596
- # writable except when the sticky bit set.
1341
+ # Argument +path+
1342
+ # should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
597
1343
  #
598
- # WARNING: Only the owner of the removing directory tree, or Unix super
599
- # user (root) should invoke this method. Otherwise this method does not
600
- # work.
1344
+ # Avoids a local vulnerability that can exist in certain circumstances;
1345
+ # see {Avoiding the TOCTTOU Vulnerability}[rdoc-ref:FileUtils@Avoiding+the+TOCTTOU+Vulnerability].
601
1346
  #
602
- # For details of this security vulnerability, see Perl's case:
1347
+ # Optional argument +force+ specifies whether to ignore
1348
+ # raised exceptions of StandardError and its descendants.
603
1349
  #
604
- # * http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2005-0448
605
- # * http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2004-0452
606
- #
607
- # For fileutils.rb, this vulnerability is reported in [ruby-dev:26100].
1350
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
608
1351
  #
609
1352
  def remove_entry_secure(path, force = false)
610
1353
  unless fu_have_symlink?
@@ -626,22 +1369,38 @@ module Bundler::FileUtils
626
1369
  unless parent_st.sticky?
627
1370
  raise ArgumentError, "parent directory is world writable, Bundler::FileUtils#remove_entry_secure does not work; abort: #{path.inspect} (parent directory mode #{'%o' % parent_st.mode})"
628
1371
  end
1372
+
629
1373
  # freeze tree root
630
1374
  euid = Process.euid
631
- File.open(fullpath + '/.') {|f|
632
- unless fu_stat_identical_entry?(st, f.stat)
633
- # symlink (TOC-to-TOU attack?)
634
- File.unlink fullpath
635
- return
636
- end
637
- f.chown euid, -1
638
- f.chmod 0700
639
- unless fu_stat_identical_entry?(st, File.lstat(fullpath))
640
- # TOC-to-TOU attack?
641
- File.unlink fullpath
642
- return
643
- end
644
- }
1375
+ dot_file = fullpath + "/."
1376
+ begin
1377
+ File.open(dot_file) {|f|
1378
+ unless fu_stat_identical_entry?(st, f.stat)
1379
+ # symlink (TOC-to-TOU attack?)
1380
+ File.unlink fullpath
1381
+ return
1382
+ end
1383
+ f.chown euid, -1
1384
+ f.chmod 0700
1385
+ }
1386
+ rescue Errno::EISDIR # JRuby in non-native mode can't open files as dirs
1387
+ File.lstat(dot_file).tap {|fstat|
1388
+ unless fu_stat_identical_entry?(st, fstat)
1389
+ # symlink (TOC-to-TOU attack?)
1390
+ File.unlink fullpath
1391
+ return
1392
+ end
1393
+ File.chown euid, -1, dot_file
1394
+ File.chmod 0700, dot_file
1395
+ }
1396
+ end
1397
+
1398
+ unless fu_stat_identical_entry?(st, File.lstat(fullpath))
1399
+ # TOC-to-TOU attack?
1400
+ File.unlink fullpath
1401
+ return
1402
+ end
1403
+
645
1404
  # ---- tree root is frozen ----
646
1405
  root = Entry_.new(path)
647
1406
  root.preorder_traverse do |ent|
@@ -676,12 +1435,17 @@ module Bundler::FileUtils
676
1435
  end
677
1436
  private_module_function :fu_stat_identical_entry?
678
1437
 
1438
+ # Removes the entry given by +path+,
1439
+ # which should be the entry for a regular file, a symbolic link,
1440
+ # or a directory.
1441
+ #
1442
+ # Argument +path+
1443
+ # should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
679
1444
  #
680
- # This method removes a file system entry +path+.
681
- # +path+ might be a regular file, a directory, or something.
682
- # If +path+ is a directory, remove it recursively.
1445
+ # Optional argument +force+ specifies whether to ignore
1446
+ # raised exceptions of StandardError and its descendants.
683
1447
  #
684
- # See also #remove_entry_secure.
1448
+ # Related: Bundler::FileUtils.remove_entry_secure.
685
1449
  #
686
1450
  def remove_entry(path, force = false)
687
1451
  Entry_.new(path).postorder_traverse do |ent|
@@ -696,9 +1460,16 @@ module Bundler::FileUtils
696
1460
  end
697
1461
  module_function :remove_entry
698
1462
 
1463
+ # Removes the file entry given by +path+,
1464
+ # which should be the entry for a regular file or a symbolic link.
1465
+ #
1466
+ # Argument +path+
1467
+ # should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
699
1468
  #
700
- # Removes a file +path+.
701
- # This method ignores StandardError if +force+ is true.
1469
+ # Optional argument +force+ specifies whether to ignore
1470
+ # raised exceptions of StandardError and its descendants.
1471
+ #
1472
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
702
1473
  #
703
1474
  def remove_file(path, force = false)
704
1475
  Entry_.new(path).remove_file
@@ -707,20 +1478,32 @@ module Bundler::FileUtils
707
1478
  end
708
1479
  module_function :remove_file
709
1480
 
1481
+ # Recursively removes the directory entry given by +path+,
1482
+ # which should be the entry for a regular file, a symbolic link,
1483
+ # or a directory.
1484
+ #
1485
+ # Argument +path+
1486
+ # should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
710
1487
  #
711
- # Removes a directory +dir+ and its contents recursively.
712
- # This method ignores StandardError if +force+ is true.
1488
+ # Optional argument +force+ specifies whether to ignore
1489
+ # raised exceptions of StandardError and its descendants.
1490
+ #
1491
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
713
1492
  #
714
1493
  def remove_dir(path, force = false)
715
1494
  remove_entry path, force # FIXME?? check if it is a directory
716
1495
  end
717
1496
  module_function :remove_dir
718
1497
 
1498
+ # Returns +true+ if the contents of files +a+ and +b+ are identical,
1499
+ # +false+ otherwise.
1500
+ #
1501
+ # Arguments +a+ and +b+
1502
+ # should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
719
1503
  #
720
- # Returns true if the contents of a file +a+ and a file +b+ are identical.
1504
+ # Bundler::FileUtils.identical? and Bundler::FileUtils.cmp are aliases for Bundler::FileUtils.compare_file.
721
1505
  #
722
- # Bundler::FileUtils.compare_file('somefile', 'somefile') #=> true
723
- # Bundler::FileUtils.compare_file('/dev/null', '/dev/urandom') #=> false
1506
+ # Related: Bundler::FileUtils.compare_stream.
724
1507
  #
725
1508
  def compare_file(a, b)
726
1509
  return false unless File.size(a) == File.size(b)
@@ -737,13 +1520,20 @@ module Bundler::FileUtils
737
1520
  module_function :identical?
738
1521
  module_function :cmp
739
1522
 
1523
+ # Returns +true+ if the contents of streams +a+ and +b+ are identical,
1524
+ # +false+ otherwise.
1525
+ #
1526
+ # Arguments +a+ and +b+
1527
+ # should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
740
1528
  #
741
- # Returns true if the contents of a stream +a+ and +b+ are identical.
1529
+ # Related: Bundler::FileUtils.compare_file.
742
1530
  #
743
1531
  def compare_stream(a, b)
744
1532
  bsize = fu_stream_blksize(a, b)
1533
+
745
1534
  sa = String.new(capacity: bsize)
746
1535
  sb = String.new(capacity: bsize)
1536
+
747
1537
  begin
748
1538
  a.read(bsize, sa)
749
1539
  b.read(bsize, sb)
@@ -753,13 +1543,69 @@ module Bundler::FileUtils
753
1543
  end
754
1544
  module_function :compare_stream
755
1545
 
1546
+ # Copies a file entry.
1547
+ # See {install(1)}[https://man7.org/linux/man-pages/man1/install.1.html].
1548
+ #
1549
+ # Arguments +src+ (a single path or an array of paths)
1550
+ # and +dest+ (a single path)
1551
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments];
1552
+ #
1553
+ # If the entry at +dest+ does not exist, copies from +src+ to +dest+:
1554
+ #
1555
+ # File.read('src0.txt') # => "aaa\n"
1556
+ # File.exist?('dest0.txt') # => false
1557
+ # Bundler::FileUtils.install('src0.txt', 'dest0.txt')
1558
+ # File.read('dest0.txt') # => "aaa\n"
1559
+ #
1560
+ # If +dest+ is a file entry, copies from +src+ to +dest+, overwriting:
1561
+ #
1562
+ # File.read('src1.txt') # => "aaa\n"
1563
+ # File.read('dest1.txt') # => "bbb\n"
1564
+ # Bundler::FileUtils.install('src1.txt', 'dest1.txt')
1565
+ # File.read('dest1.txt') # => "aaa\n"
1566
+ #
1567
+ # If +dest+ is a directory entry, copies from +src+ to <tt>dest/src</tt>,
1568
+ # overwriting if necessary:
756
1569
  #
757
- # If +src+ is not same as +dest+, copies it and changes the permission
758
- # mode to +mode+. If +dest+ is a directory, destination is +dest+/+src+.
759
- # This method removes destination before copy.
1570
+ # File.read('src2.txt') # => "aaa\n"
1571
+ # File.read('dest2/src2.txt') # => "bbb\n"
1572
+ # Bundler::FileUtils.install('src2.txt', 'dest2')
1573
+ # File.read('dest2/src2.txt') # => "aaa\n"
760
1574
  #
761
- # Bundler::FileUtils.install 'ruby', '/usr/local/bin/ruby', :mode => 0755, :verbose => true
762
- # Bundler::FileUtils.install 'lib.rb', '/usr/local/lib/ruby/site_ruby', :verbose => true
1575
+ # If +src+ is an array of paths and +dest+ points to a directory,
1576
+ # copies each path +path+ in +src+ to <tt>dest/path</tt>:
1577
+ #
1578
+ # File.file?('src3.txt') # => true
1579
+ # File.file?('src3.dat') # => true
1580
+ # Bundler::FileUtils.mkdir('dest3')
1581
+ # Bundler::FileUtils.install(['src3.txt', 'src3.dat'], 'dest3')
1582
+ # File.file?('dest3/src3.txt') # => true
1583
+ # File.file?('dest3/src3.dat') # => true
1584
+ #
1585
+ # Keyword arguments:
1586
+ #
1587
+ # - <tt>group: <i>group</i></tt> - changes the group if not +nil+,
1588
+ # using {File.chown}[https://docs.ruby-lang.org/en/master/File.html#method-c-chown].
1589
+ # - <tt>mode: <i>permissions</i></tt> - changes the permissions.
1590
+ # using {File.chmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-chmod].
1591
+ # - <tt>noop: true</tt> - does not copy entries; returns +nil+.
1592
+ # - <tt>owner: <i>owner</i></tt> - changes the owner if not +nil+,
1593
+ # using {File.chown}[https://docs.ruby-lang.org/en/master/File.html#method-c-chown].
1594
+ # - <tt>preserve: true</tt> - preserve timestamps
1595
+ # using {File.utime}[https://docs.ruby-lang.org/en/master/File.html#method-c-utime].
1596
+ # - <tt>verbose: true</tt> - prints an equivalent command:
1597
+ #
1598
+ # Bundler::FileUtils.install('src0.txt', 'dest0.txt', noop: true, verbose: true)
1599
+ # Bundler::FileUtils.install('src1.txt', 'dest1.txt', noop: true, verbose: true)
1600
+ # Bundler::FileUtils.install('src2.txt', 'dest2', noop: true, verbose: true)
1601
+ #
1602
+ # Output:
1603
+ #
1604
+ # install -c src0.txt dest0.txt
1605
+ # install -c src1.txt dest1.txt
1606
+ # install -c src2.txt dest2
1607
+ #
1608
+ # Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
763
1609
  #
764
1610
  def install(src, dest, mode: nil, owner: nil, group: nil, preserve: nil,
765
1611
  noop: nil, verbose: nil)
@@ -779,7 +1625,13 @@ module Bundler::FileUtils
779
1625
  st = File.stat(s)
780
1626
  unless File.exist?(d) and compare_file(s, d)
781
1627
  remove_file d, true
782
- copy_file s, d
1628
+ if d.end_with?('/')
1629
+ mkdir_p d
1630
+ copy_file s, d + File.basename(s)
1631
+ else
1632
+ mkdir_p File.expand_path('..', d)
1633
+ copy_file s, d
1634
+ end
783
1635
  File.utime st.atime, st.mtime, d if preserve
784
1636
  File.chmod fu_mode(mode, st), d if mode
785
1637
  File.chown uid, gid, d if uid or gid
@@ -800,7 +1652,7 @@ module Bundler::FileUtils
800
1652
  when "a"
801
1653
  mask | 07777
802
1654
  else
803
- raise ArgumentError, "invalid `who' symbol in file mode: #{chr}"
1655
+ raise ArgumentError, "invalid 'who' symbol in file mode: #{chr}"
804
1656
  end
805
1657
  end
806
1658
  end
@@ -819,11 +1671,8 @@ module Bundler::FileUtils
819
1671
  private_module_function :apply_mask
820
1672
 
821
1673
  def symbolic_modes_to_i(mode_sym, path) #:nodoc:
822
- mode = if File::Stat === path
823
- path.mode
824
- else
825
- File.stat(path).mode
826
- end
1674
+ path = File.stat(path) unless File::Stat === path
1675
+ mode = path.mode
827
1676
  mode_sym.split(/,/).inject(mode & 07777) do |current_mode, clause|
828
1677
  target, *actions = clause.split(/([=+-])/)
829
1678
  raise ArgumentError, "invalid file mode: #{mode_sym}" if actions.empty?
@@ -840,7 +1689,7 @@ module Bundler::FileUtils
840
1689
  when "x"
841
1690
  mask | 0111
842
1691
  when "X"
843
- if FileTest.directory? path
1692
+ if path.directory?
844
1693
  mask | 0111
845
1694
  else
846
1695
  mask
@@ -857,7 +1706,7 @@ module Bundler::FileUtils
857
1706
  copy_mask = user_mask(chr)
858
1707
  (current_mode & copy_mask) / (copy_mask & 0111) * (user_mask & 0111)
859
1708
  else
860
- raise ArgumentError, "invalid `perm' symbol in file mode: #{chr}"
1709
+ raise ArgumentError, "invalid 'perm' symbol in file mode: #{chr}"
861
1710
  end
862
1711
  end
863
1712
 
@@ -880,37 +1729,78 @@ module Bundler::FileUtils
880
1729
  end
881
1730
  private_module_function :mode_to_s
882
1731
 
1732
+ # Changes permissions on the entries at the paths given in +list+
1733
+ # (a single path or an array of paths)
1734
+ # to the permissions given by +mode+;
1735
+ # returns +list+ if it is an array, <tt>[list]</tt> otherwise:
1736
+ #
1737
+ # - Modifies each entry that is a regular file using
1738
+ # {File.chmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-chmod].
1739
+ # - Modifies each entry that is a symbolic link using
1740
+ # {File.lchmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-lchmod].
1741
+ #
1742
+ # Argument +list+ or its elements
1743
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
1744
+ #
1745
+ # Argument +mode+ may be either an integer or a string:
1746
+ #
1747
+ # - \Integer +mode+: represents the permission bits to be set:
1748
+ #
1749
+ # Bundler::FileUtils.chmod(0755, 'src0.txt')
1750
+ # Bundler::FileUtils.chmod(0644, ['src0.txt', 'src0.dat'])
1751
+ #
1752
+ # - \String +mode+: represents the permissions to be set:
1753
+ #
1754
+ # The string is of the form <tt>[targets][[operator][perms[,perms]]</tt>, where:
1755
+ #
1756
+ # - +targets+ may be any combination of these letters:
1757
+ #
1758
+ # - <tt>'u'</tt>: permissions apply to the file's owner.
1759
+ # - <tt>'g'</tt>: permissions apply to users in the file's group.
1760
+ # - <tt>'o'</tt>: permissions apply to other users not in the file's group.
1761
+ # - <tt>'a'</tt> (the default): permissions apply to all users.
1762
+ #
1763
+ # - +operator+ may be one of these letters:
1764
+ #
1765
+ # - <tt>'+'</tt>: adds permissions.
1766
+ # - <tt>'-'</tt>: removes permissions.
1767
+ # - <tt>'='</tt>: sets (replaces) permissions.
1768
+ #
1769
+ # - +perms+ (may be repeated, with separating commas)
1770
+ # may be any combination of these letters:
1771
+ #
1772
+ # - <tt>'r'</tt>: Read.
1773
+ # - <tt>'w'</tt>: Write.
1774
+ # - <tt>'x'</tt>: Execute (search, for a directory).
1775
+ # - <tt>'X'</tt>: Search (for a directories only;
1776
+ # must be used with <tt>'+'</tt>)
1777
+ # - <tt>'s'</tt>: Uid or gid.
1778
+ # - <tt>'t'</tt>: Sticky bit.
1779
+ #
1780
+ # Examples:
1781
+ #
1782
+ # Bundler::FileUtils.chmod('u=wrx,go=rx', 'src1.txt')
1783
+ # Bundler::FileUtils.chmod('u=wrx,go=rx', '/usr/bin/ruby')
1784
+ #
1785
+ # Keyword arguments:
1786
+ #
1787
+ # - <tt>noop: true</tt> - does not change permissions; returns +nil+.
1788
+ # - <tt>verbose: true</tt> - prints an equivalent command:
1789
+ #
1790
+ # Bundler::FileUtils.chmod(0755, 'src0.txt', noop: true, verbose: true)
1791
+ # Bundler::FileUtils.chmod(0644, ['src0.txt', 'src0.dat'], noop: true, verbose: true)
1792
+ # Bundler::FileUtils.chmod('u=wrx,go=rx', 'src1.txt', noop: true, verbose: true)
1793
+ # Bundler::FileUtils.chmod('u=wrx,go=rx', '/usr/bin/ruby', noop: true, verbose: true)
1794
+ #
1795
+ # Output:
1796
+ #
1797
+ # chmod 755 src0.txt
1798
+ # chmod 644 src0.txt src0.dat
1799
+ # chmod u=wrx,go=rx src1.txt
1800
+ # chmod u=wrx,go=rx /usr/bin/ruby
1801
+ #
1802
+ # Related: Bundler::FileUtils.chmod_R.
883
1803
  #
884
- # Changes permission bits on the named files (in +list+) to the bit pattern
885
- # represented by +mode+.
886
- #
887
- # +mode+ is the symbolic and absolute mode can be used.
888
- #
889
- # Absolute mode is
890
- # Bundler::FileUtils.chmod 0755, 'somecommand'
891
- # Bundler::FileUtils.chmod 0644, %w(my.rb your.rb his.rb her.rb)
892
- # Bundler::FileUtils.chmod 0755, '/usr/bin/ruby', :verbose => true
893
- #
894
- # Symbolic mode is
895
- # Bundler::FileUtils.chmod "u=wrx,go=rx", 'somecommand'
896
- # Bundler::FileUtils.chmod "u=wr,go=rr", %w(my.rb your.rb his.rb her.rb)
897
- # Bundler::FileUtils.chmod "u=wrx,go=rx", '/usr/bin/ruby', :verbose => true
898
- #
899
- # "a" :: is user, group, other mask.
900
- # "u" :: is user's mask.
901
- # "g" :: is group's mask.
902
- # "o" :: is other's mask.
903
- # "w" :: is write permission.
904
- # "r" :: is read permission.
905
- # "x" :: is execute permission.
906
- # "X" ::
907
- # is execute permission for directories only, must be used in conjunction with "+"
908
- # "s" :: is uid, gid.
909
- # "t" :: is sticky bit.
910
- # "+" :: is added to a class given the specified mode.
911
- # "-" :: Is removed from a given class given mode.
912
- # "=" :: Is the exact nature of the class will be given a specified mode.
913
-
914
1804
  def chmod(mode, list, noop: nil, verbose: nil)
915
1805
  list = fu_list(list)
916
1806
  fu_output_message sprintf('chmod %s %s', mode_to_s(mode), list.join(' ')) if verbose
@@ -921,12 +1811,7 @@ module Bundler::FileUtils
921
1811
  end
922
1812
  module_function :chmod
923
1813
 
924
- #
925
- # Changes permission bits on the named files (in +list+)
926
- # to the bit pattern represented by +mode+.
927
- #
928
- # Bundler::FileUtils.chmod_R 0700, "/tmp/app.#{$$}"
929
- # Bundler::FileUtils.chmod_R "u=wrx", "/tmp/app.#{$$}"
1814
+ # Like Bundler::FileUtils.chmod, but changes permissions recursively.
930
1815
  #
931
1816
  def chmod_R(mode, list, noop: nil, verbose: nil, force: nil)
932
1817
  list = fu_list(list)
@@ -946,15 +1831,68 @@ module Bundler::FileUtils
946
1831
  end
947
1832
  module_function :chmod_R
948
1833
 
1834
+ # Changes the owner and group on the entries at the paths given in +list+
1835
+ # (a single path or an array of paths)
1836
+ # to the given +user+ and +group+;
1837
+ # returns +list+ if it is an array, <tt>[list]</tt> otherwise:
1838
+ #
1839
+ # - Modifies each entry that is a regular file using
1840
+ # {File.chown}[https://docs.ruby-lang.org/en/master/File.html#method-c-chown].
1841
+ # - Modifies each entry that is a symbolic link using
1842
+ # {File.lchown}[https://docs.ruby-lang.org/en/master/File.html#method-c-lchown].
1843
+ #
1844
+ # Argument +list+ or its elements
1845
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
1846
+ #
1847
+ # User and group:
1848
+ #
1849
+ # - Argument +user+ may be a user name or a user id;
1850
+ # if +nil+ or +-1+, the user is not changed.
1851
+ # - Argument +group+ may be a group name or a group id;
1852
+ # if +nil+ or +-1+, the group is not changed.
1853
+ # - The user must be a member of the group.
1854
+ #
1855
+ # Examples:
1856
+ #
1857
+ # # One path.
1858
+ # # User and group as string names.
1859
+ # File.stat('src0.txt').uid # => 1004
1860
+ # File.stat('src0.txt').gid # => 1004
1861
+ # Bundler::FileUtils.chown('user2', 'group1', 'src0.txt')
1862
+ # File.stat('src0.txt').uid # => 1006
1863
+ # File.stat('src0.txt').gid # => 1005
1864
+ #
1865
+ # # User and group as uid and gid.
1866
+ # Bundler::FileUtils.chown(1004, 1004, 'src0.txt')
1867
+ # File.stat('src0.txt').uid # => 1004
1868
+ # File.stat('src0.txt').gid # => 1004
1869
+ #
1870
+ # # Array of paths.
1871
+ # Bundler::FileUtils.chown(1006, 1005, ['src0.txt', 'src0.dat'])
949
1872
  #
950
- # Changes owner and group on the named files (in +list+)
951
- # to the user +user+ and the group +group+. +user+ and +group+
952
- # may be an ID (Integer/String) or a name (String).
953
- # If +user+ or +group+ is nil, this method does not change
954
- # the attribute.
1873
+ # # Directory (not recursive).
1874
+ # Bundler::FileUtils.chown('user2', 'group1', '.')
955
1875
  #
956
- # Bundler::FileUtils.chown 'root', 'staff', '/usr/local/bin/ruby'
957
- # Bundler::FileUtils.chown nil, 'bin', Dir.glob('/usr/bin/*'), :verbose => true
1876
+ # Keyword arguments:
1877
+ #
1878
+ # - <tt>noop: true</tt> - does not change permissions; returns +nil+.
1879
+ # - <tt>verbose: true</tt> - prints an equivalent command:
1880
+ #
1881
+ # Bundler::FileUtils.chown('user2', 'group1', 'src0.txt', noop: true, verbose: true)
1882
+ # Bundler::FileUtils.chown(1004, 1004, 'src0.txt', noop: true, verbose: true)
1883
+ # Bundler::FileUtils.chown(1006, 1005, ['src0.txt', 'src0.dat'], noop: true, verbose: true)
1884
+ # Bundler::FileUtils.chown('user2', 'group1', path, noop: true, verbose: true)
1885
+ # Bundler::FileUtils.chown('user2', 'group1', '.', noop: true, verbose: true)
1886
+ #
1887
+ # Output:
1888
+ #
1889
+ # chown user2:group1 src0.txt
1890
+ # chown 1004:1004 src0.txt
1891
+ # chown 1006:1005 src0.txt src0.dat
1892
+ # chown user2:group1 src0.txt
1893
+ # chown user2:group1 .
1894
+ #
1895
+ # Related: Bundler::FileUtils.chown_R.
958
1896
  #
959
1897
  def chown(user, group, list, noop: nil, verbose: nil)
960
1898
  list = fu_list(list)
@@ -970,15 +1908,7 @@ module Bundler::FileUtils
970
1908
  end
971
1909
  module_function :chown
972
1910
 
973
- #
974
- # Changes owner and group on the named files (in +list+)
975
- # to the user +user+ and the group +group+ recursively.
976
- # +user+ and +group+ may be an ID (Integer/String) or
977
- # a name (String). If +user+ or +group+ is nil, this
978
- # method does not change the attribute.
979
- #
980
- # Bundler::FileUtils.chown_R 'www', 'www', '/var/www/htdocs'
981
- # Bundler::FileUtils.chown_R 'cvs', 'cvs', '/var/cvs', :verbose => true
1911
+ # Like Bundler::FileUtils.chown, but changes owner and group recursively.
982
1912
  #
983
1913
  def chown_R(user, group, list, noop: nil, verbose: nil, force: nil)
984
1914
  list = fu_list(list)
@@ -1001,11 +1931,6 @@ module Bundler::FileUtils
1001
1931
  end
1002
1932
  module_function :chown_R
1003
1933
 
1004
- begin
1005
- require 'etc'
1006
- rescue LoadError # rescue LoadError for miniruby
1007
- end
1008
-
1009
1934
  def fu_get_uid(user) #:nodoc:
1010
1935
  return nil unless user
1011
1936
  case user
@@ -1014,6 +1939,7 @@ module Bundler::FileUtils
1014
1939
  when /\A\d+\z/
1015
1940
  user.to_i
1016
1941
  else
1942
+ require 'etc'
1017
1943
  Etc.getpwnam(user) ? Etc.getpwnam(user).uid : nil
1018
1944
  end
1019
1945
  end
@@ -1027,17 +1953,56 @@ module Bundler::FileUtils
1027
1953
  when /\A\d+\z/
1028
1954
  group.to_i
1029
1955
  else
1956
+ require 'etc'
1030
1957
  Etc.getgrnam(group) ? Etc.getgrnam(group).gid : nil
1031
1958
  end
1032
1959
  end
1033
1960
  private_module_function :fu_get_gid
1034
1961
 
1962
+ # Updates modification times (mtime) and access times (atime)
1963
+ # of the entries given by the paths in +list+
1964
+ # (a single path or an array of paths);
1965
+ # returns +list+ if it is an array, <tt>[list]</tt> otherwise.
1966
+ #
1967
+ # By default, creates an empty file for any path to a non-existent entry;
1968
+ # use keyword argument +nocreate+ to raise an exception instead.
1035
1969
  #
1036
- # Updates modification time (mtime) and access time (atime) of file(s) in
1037
- # +list+. Files are created if they don't exist.
1970
+ # Argument +list+ or its elements
1971
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
1038
1972
  #
1039
- # Bundler::FileUtils.touch 'timestamp'
1040
- # Bundler::FileUtils.touch Dir.glob('*.c'); system 'make'
1973
+ # Examples:
1974
+ #
1975
+ # # Single path.
1976
+ # f = File.new('src0.txt') # Existing file.
1977
+ # f.atime # => 2022-06-10 11:11:21.200277 -0700
1978
+ # f.mtime # => 2022-06-10 11:11:21.200277 -0700
1979
+ # Bundler::FileUtils.touch('src0.txt')
1980
+ # f = File.new('src0.txt')
1981
+ # f.atime # => 2022-06-11 08:28:09.8185343 -0700
1982
+ # f.mtime # => 2022-06-11 08:28:09.8185343 -0700
1983
+ #
1984
+ # # Array of paths.
1985
+ # Bundler::FileUtils.touch(['src0.txt', 'src0.dat'])
1986
+ #
1987
+ # Keyword arguments:
1988
+ #
1989
+ # - <tt>mtime: <i>time</i></tt> - sets the entry's mtime to the given time,
1990
+ # instead of the current time.
1991
+ # - <tt>nocreate: true</tt> - raises an exception if the entry does not exist.
1992
+ # - <tt>noop: true</tt> - does not touch entries; returns +nil+.
1993
+ # - <tt>verbose: true</tt> - prints an equivalent command:
1994
+ #
1995
+ # Bundler::FileUtils.touch('src0.txt', noop: true, verbose: true)
1996
+ # Bundler::FileUtils.touch(['src0.txt', 'src0.dat'], noop: true, verbose: true)
1997
+ # Bundler::FileUtils.touch(path, noop: true, verbose: true)
1998
+ #
1999
+ # Output:
2000
+ #
2001
+ # touch src0.txt
2002
+ # touch src0.txt src0.dat
2003
+ # touch src0.txt
2004
+ #
2005
+ # Related: Bundler::FileUtils.uptodate?.
1041
2006
  #
1042
2007
  def touch(list, noop: nil, verbose: nil, mtime: nil, nocreate: nil)
1043
2008
  list = fu_list(list)
@@ -1064,18 +2029,22 @@ module Bundler::FileUtils
1064
2029
 
1065
2030
  private
1066
2031
 
1067
- module StreamUtils_
2032
+ module StreamUtils_ # :nodoc:
2033
+
1068
2034
  private
1069
2035
 
1070
- def fu_windows?
1071
- /mswin|mingw|bccwin|emx/ =~ RUBY_PLATFORM
2036
+ case (defined?(::RbConfig) ? ::RbConfig::CONFIG['host_os'] : ::RUBY_PLATFORM)
2037
+ when /mswin|mingw/
2038
+ def fu_windows?; true end #:nodoc:
2039
+ else
2040
+ def fu_windows?; false end #:nodoc:
1072
2041
  end
1073
2042
 
1074
2043
  def fu_copy_stream0(src, dest, blksize = nil) #:nodoc:
1075
2044
  IO.copy_stream(src, dest)
1076
2045
  end
1077
2046
 
1078
- def fu_stream_blksize(*streams)
2047
+ def fu_stream_blksize(*streams) #:nodoc:
1079
2048
  streams.each do |s|
1080
2049
  next unless s.respond_to?(:stat)
1081
2050
  size = fu_blksize(s.stat)
@@ -1084,14 +2053,14 @@ module Bundler::FileUtils
1084
2053
  fu_default_blksize()
1085
2054
  end
1086
2055
 
1087
- def fu_blksize(st)
2056
+ def fu_blksize(st) #:nodoc:
1088
2057
  s = st.blksize
1089
2058
  return nil unless s
1090
2059
  return nil if s == 0
1091
2060
  s
1092
2061
  end
1093
2062
 
1094
- def fu_default_blksize
2063
+ def fu_default_blksize #:nodoc:
1095
2064
  1024
1096
2065
  end
1097
2066
  end
@@ -1192,10 +2161,12 @@ module Bundler::FileUtils
1192
2161
 
1193
2162
  def entries
1194
2163
  opts = {}
1195
- opts[:encoding] = ::Encoding::UTF_8 if fu_windows?
1196
- Dir.entries(path(), opts)\
1197
- .reject {|n| n == '.' or n == '..' }\
1198
- .map {|n| Entry_.new(prefix(), join(rel(), n.untaint)) }
2164
+ opts[:encoding] = fu_windows? ? ::Encoding::UTF_8 : path.encoding
2165
+
2166
+ files = Dir.children(path, **opts)
2167
+
2168
+ untaint = RUBY_VERSION < '2.7'
2169
+ files.map {|n| Entry_.new(prefix(), join(rel(), untaint ? n.untaint : n)) }
1199
2170
  end
1200
2171
 
1201
2172
  def stat
@@ -1240,6 +2211,7 @@ module Bundler::FileUtils
1240
2211
  else
1241
2212
  File.chmod mode, path()
1242
2213
  end
2214
+ rescue Errno::EOPNOTSUPP
1243
2215
  end
1244
2216
 
1245
2217
  def chown(uid, gid)
@@ -1250,6 +2222,22 @@ module Bundler::FileUtils
1250
2222
  end
1251
2223
  end
1252
2224
 
2225
+ def link(dest)
2226
+ case
2227
+ when directory?
2228
+ if !File.exist?(dest) and descendant_directory?(dest, path)
2229
+ raise ArgumentError, "cannot link directory %s to itself %s" % [path, dest]
2230
+ end
2231
+ begin
2232
+ Dir.mkdir dest
2233
+ rescue
2234
+ raise unless File.directory?(dest)
2235
+ end
2236
+ else
2237
+ File.link path(), dest
2238
+ end
2239
+ end
2240
+
1253
2241
  def copy(dest)
1254
2242
  lstat
1255
2243
  case
@@ -1266,18 +2254,21 @@ module Bundler::FileUtils
1266
2254
  end
1267
2255
  when symlink?
1268
2256
  File.symlink File.readlink(path()), dest
1269
- when chardev?
1270
- raise "cannot handle device file" unless File.respond_to?(:mknod)
1271
- mknod dest, ?c, 0666, lstat().rdev
1272
- when blockdev?
1273
- raise "cannot handle device file" unless File.respond_to?(:mknod)
1274
- mknod dest, ?b, 0666, lstat().rdev
2257
+ when chardev?, blockdev?
2258
+ raise "cannot handle device file"
1275
2259
  when socket?
1276
- raise "cannot handle socket" unless File.respond_to?(:mknod)
1277
- mknod dest, nil, lstat().mode, 0
2260
+ begin
2261
+ require 'socket'
2262
+ rescue LoadError
2263
+ raise "cannot handle socket"
2264
+ else
2265
+ raise "cannot handle socket" unless defined?(UNIXServer)
2266
+ end
2267
+ UNIXServer.new(dest).close
2268
+ File.chmod lstat().mode, dest
1278
2269
  when pipe?
1279
2270
  raise "cannot handle FIFO" unless File.respond_to?(:mkfifo)
1280
- mkfifo dest, 0666
2271
+ File.mkfifo dest, lstat().mode
1281
2272
  when door?
1282
2273
  raise "cannot handle door: #{path()}"
1283
2274
  else
@@ -1315,7 +2306,7 @@ module Bundler::FileUtils
1315
2306
  if st.symlink?
1316
2307
  begin
1317
2308
  File.lchmod mode, path
1318
- rescue NotImplementedError
2309
+ rescue NotImplementedError, Errno::EOPNOTSUPP
1319
2310
  end
1320
2311
  else
1321
2312
  File.chmod mode, path
@@ -1374,13 +2365,21 @@ module Bundler::FileUtils
1374
2365
 
1375
2366
  def postorder_traverse
1376
2367
  if directory?
1377
- entries().each do |ent|
2368
+ begin
2369
+ children = entries()
2370
+ rescue Errno::EACCES
2371
+ # Failed to get the list of children.
2372
+ # Assuming there is no children, try to process the parent directory.
2373
+ yield self
2374
+ return
2375
+ end
2376
+
2377
+ children.each do |ent|
1378
2378
  ent.postorder_traverse do |e|
1379
2379
  yield e
1380
2380
  end
1381
2381
  end
1382
2382
  end
1383
- ensure
1384
2383
  yield self
1385
2384
  end
1386
2385
 
@@ -1396,14 +2395,14 @@ module Bundler::FileUtils
1396
2395
 
1397
2396
  private
1398
2397
 
1399
- $fileutils_rb_have_lchmod = nil
2398
+ @@fileutils_rb_have_lchmod = nil
1400
2399
 
1401
2400
  def have_lchmod?
1402
2401
  # This is not MT-safe, but it does not matter.
1403
- if $fileutils_rb_have_lchmod == nil
1404
- $fileutils_rb_have_lchmod = check_have_lchmod?
2402
+ if @@fileutils_rb_have_lchmod == nil
2403
+ @@fileutils_rb_have_lchmod = check_have_lchmod?
1405
2404
  end
1406
- $fileutils_rb_have_lchmod
2405
+ @@fileutils_rb_have_lchmod
1407
2406
  end
1408
2407
 
1409
2408
  def check_have_lchmod?
@@ -1414,14 +2413,14 @@ module Bundler::FileUtils
1414
2413
  return false
1415
2414
  end
1416
2415
 
1417
- $fileutils_rb_have_lchown = nil
2416
+ @@fileutils_rb_have_lchown = nil
1418
2417
 
1419
2418
  def have_lchown?
1420
2419
  # This is not MT-safe, but it does not matter.
1421
- if $fileutils_rb_have_lchown == nil
1422
- $fileutils_rb_have_lchown = check_have_lchown?
2420
+ if @@fileutils_rb_have_lchown == nil
2421
+ @@fileutils_rb_have_lchown = check_have_lchown?
1423
2422
  end
1424
- $fileutils_rb_have_lchown
2423
+ @@fileutils_rb_have_lchown
1425
2424
  end
1426
2425
 
1427
2426
  def check_have_lchown?
@@ -1435,7 +2434,15 @@ module Bundler::FileUtils
1435
2434
  def join(dir, base)
1436
2435
  return File.path(dir) if not base or base == '.'
1437
2436
  return File.path(base) if not dir or dir == '.'
1438
- File.join(dir, base)
2437
+ begin
2438
+ File.join(dir, base)
2439
+ rescue EncodingError
2440
+ if fu_windows?
2441
+ File.join(dir.encode(::Encoding::UTF_8), base.encode(::Encoding::UTF_8))
2442
+ else
2443
+ raise
2444
+ end
2445
+ end
1439
2446
  end
1440
2447
 
1441
2448
  if File::ALT_SEPARATOR
@@ -1443,10 +2450,13 @@ module Bundler::FileUtils
1443
2450
  else
1444
2451
  DIRECTORY_TERM = "(?=/|\\z)"
1445
2452
  end
1446
- SYSCASE = File::FNM_SYSCASE.nonzero? ? "-i" : ""
1447
2453
 
1448
2454
  def descendant_directory?(descendant, ascendant)
1449
- /\A(?#{SYSCASE}:#{Regexp.quote(ascendant)})#{DIRECTORY_TERM}/ =~ File.dirname(descendant)
2455
+ if File::FNM_SYSCASE.nonzero?
2456
+ File.expand_path(File.dirname(descendant)).casecmp(File.expand_path(ascendant)) == 0
2457
+ else
2458
+ File.expand_path(File.dirname(descendant)) == File.expand_path(ascendant)
2459
+ end
1450
2460
  end
1451
2461
  end # class Entry_
1452
2462
 
@@ -1463,15 +2473,15 @@ module Bundler::FileUtils
1463
2473
  end
1464
2474
  private_module_function :fu_each_src_dest
1465
2475
 
1466
- def fu_each_src_dest0(src, dest) #:nodoc:
2476
+ def fu_each_src_dest0(src, dest, target_directory = true) #:nodoc:
1467
2477
  if tmp = Array.try_convert(src)
1468
2478
  tmp.each do |s|
1469
2479
  s = File.path(s)
1470
- yield s, File.join(dest, File.basename(s))
2480
+ yield s, (target_directory ? File.join(dest, File.basename(s)) : dest)
1471
2481
  end
1472
2482
  else
1473
2483
  src = File.path(src)
1474
- if File.directory?(dest)
2484
+ if target_directory and File.directory?(dest)
1475
2485
  yield src, File.join(dest, File.basename(src))
1476
2486
  else
1477
2487
  yield src, File.path(dest)
@@ -1485,16 +2495,66 @@ module Bundler::FileUtils
1485
2495
  end
1486
2496
  private_module_function :fu_same?
1487
2497
 
1488
- @fileutils_output = $stderr
1489
- @fileutils_label = ''
1490
-
1491
2498
  def fu_output_message(msg) #:nodoc:
1492
- @fileutils_output ||= $stderr
1493
- @fileutils_label ||= ''
1494
- @fileutils_output.puts @fileutils_label + msg
2499
+ output = @fileutils_output if defined?(@fileutils_output)
2500
+ output ||= $stdout
2501
+ if defined?(@fileutils_label)
2502
+ msg = @fileutils_label + msg
2503
+ end
2504
+ output.puts msg
1495
2505
  end
1496
2506
  private_module_function :fu_output_message
1497
2507
 
2508
+ def fu_split_path(path) #:nodoc:
2509
+ path = File.path(path)
2510
+ list = []
2511
+ until (parent, base = File.split(path); parent == path or parent == ".")
2512
+ list << base
2513
+ path = parent
2514
+ end
2515
+ list << path
2516
+ list.reverse!
2517
+ end
2518
+ private_module_function :fu_split_path
2519
+
2520
+ def fu_relative_components_from(target, base) #:nodoc:
2521
+ i = 0
2522
+ while target[i]&.== base[i]
2523
+ i += 1
2524
+ end
2525
+ Array.new(base.size-i, '..').concat(target[i..-1])
2526
+ end
2527
+ private_module_function :fu_relative_components_from
2528
+
2529
+ def fu_clean_components(*comp) #:nodoc:
2530
+ comp.shift while comp.first == "."
2531
+ return comp if comp.empty?
2532
+ clean = [comp.shift]
2533
+ path = File.join(*clean, "") # ending with File::SEPARATOR
2534
+ while c = comp.shift
2535
+ if c == ".." and clean.last != ".." and !(fu_have_symlink? && File.symlink?(path))
2536
+ clean.pop
2537
+ path.chomp!(%r((?<=\A|/)[^/]+/\z), "")
2538
+ else
2539
+ clean << c
2540
+ path << c << "/"
2541
+ end
2542
+ end
2543
+ clean
2544
+ end
2545
+ private_module_function :fu_clean_components
2546
+
2547
+ if fu_windows?
2548
+ def fu_starting_path?(path) #:nodoc:
2549
+ path&.start_with?(%r(\w:|/))
2550
+ end
2551
+ else
2552
+ def fu_starting_path?(path) #:nodoc:
2553
+ path&.start_with?("/")
2554
+ end
2555
+ end
2556
+ private_module_function :fu_starting_path?
2557
+
1498
2558
  # This hash table holds command options.
1499
2559
  OPT_TABLE = {} #:nodoc: internal use only
1500
2560
  (private_instance_methods & methods(false)).inject(OPT_TABLE) {|tbl, name|
@@ -1502,62 +2562,66 @@ module Bundler::FileUtils
1502
2562
  tbl
1503
2563
  }
1504
2564
 
2565
+ public
2566
+
2567
+ # Returns an array of the string names of \Bundler::FileUtils methods
2568
+ # that accept one or more keyword arguments:
1505
2569
  #
1506
- # Returns an Array of method names which have any options.
1507
- #
1508
- # p Bundler::FileUtils.commands #=> ["chmod", "cp", "cp_r", "install", ...]
2570
+ # Bundler::FileUtils.commands.sort.take(3) # => ["cd", "chdir", "chmod"]
1509
2571
  #
1510
2572
  def self.commands
1511
2573
  OPT_TABLE.keys
1512
2574
  end
1513
2575
 
2576
+ # Returns an array of the string keyword names:
1514
2577
  #
1515
- # Returns an Array of option names.
1516
- #
1517
- # p Bundler::FileUtils.options #=> ["noop", "force", "verbose", "preserve", "mode"]
2578
+ # Bundler::FileUtils.options.take(3) # => ["noop", "verbose", "force"]
1518
2579
  #
1519
2580
  def self.options
1520
2581
  OPT_TABLE.values.flatten.uniq.map {|sym| sym.to_s }
1521
2582
  end
1522
2583
 
2584
+ # Returns +true+ if method +mid+ accepts the given option +opt+, +false+ otherwise;
2585
+ # the arguments may be strings or symbols:
1523
2586
  #
1524
- # Returns true if the method +mid+ have an option +opt+.
1525
- #
1526
- # p Bundler::FileUtils.have_option?(:cp, :noop) #=> true
1527
- # p Bundler::FileUtils.have_option?(:rm, :force) #=> true
1528
- # p Bundler::FileUtils.have_option?(:rm, :preserve) #=> false
2587
+ # Bundler::FileUtils.have_option?(:chmod, :noop) # => true
2588
+ # Bundler::FileUtils.have_option?('chmod', 'secure') # => false
1529
2589
  #
1530
2590
  def self.have_option?(mid, opt)
1531
2591
  li = OPT_TABLE[mid.to_s] or raise ArgumentError, "no such method: #{mid}"
1532
2592
  li.include?(opt)
1533
2593
  end
1534
2594
 
2595
+ # Returns an array of the string keyword name for method +mid+;
2596
+ # the argument may be a string or a symbol:
1535
2597
  #
1536
- # Returns an Array of option names of the method +mid+.
1537
- #
1538
- # p Bundler::FileUtils.options_of(:rm) #=> ["noop", "verbose", "force"]
2598
+ # Bundler::FileUtils.options_of(:rm) # => ["force", "noop", "verbose"]
2599
+ # Bundler::FileUtils.options_of('mv') # => ["force", "noop", "verbose", "secure"]
1539
2600
  #
1540
2601
  def self.options_of(mid)
1541
2602
  OPT_TABLE[mid.to_s].map {|sym| sym.to_s }
1542
2603
  end
1543
2604
 
2605
+ # Returns an array of the string method names of the methods
2606
+ # that accept the given keyword option +opt+;
2607
+ # the argument must be a symbol:
1544
2608
  #
1545
- # Returns an Array of method names which have the option +opt+.
1546
- #
1547
- # p Bundler::FileUtils.collect_method(:preserve) #=> ["cp", "cp_r", "copy", "install"]
2609
+ # Bundler::FileUtils.collect_method(:preserve) # => ["cp", "copy", "cp_r", "install"]
1548
2610
  #
1549
2611
  def self.collect_method(opt)
1550
2612
  OPT_TABLE.keys.select {|m| OPT_TABLE[m].include?(opt) }
1551
2613
  end
1552
2614
 
1553
- LOW_METHODS = singleton_methods(false) - collect_method(:noop).map(&:intern)
1554
- module LowMethods
2615
+ private
2616
+
2617
+ LOW_METHODS = singleton_methods(false) - collect_method(:noop).map(&:intern) # :nodoc:
2618
+ module LowMethods # :nodoc: internal use only
1555
2619
  private
1556
2620
  def _do_nothing(*)end
1557
2621
  ::Bundler::FileUtils::LOW_METHODS.map {|name| alias_method name, :_do_nothing}
1558
2622
  end
1559
2623
 
1560
- METHODS = singleton_methods() - [:private_module_function,
2624
+ METHODS = singleton_methods() - [:private_module_function, # :nodoc:
1561
2625
  :commands, :options, :have_option?, :options_of, :collect_method]
1562
2626
 
1563
2627
  #
@@ -1567,8 +2631,6 @@ module Bundler::FileUtils
1567
2631
  #
1568
2632
  module Verbose
1569
2633
  include Bundler::FileUtils
1570
- @fileutils_output = $stderr
1571
- @fileutils_label = ''
1572
2634
  names = ::Bundler::FileUtils.collect_method(:verbose)
1573
2635
  names.each do |name|
1574
2636
  module_eval(<<-EOS, __FILE__, __LINE__ + 1)
@@ -1592,8 +2654,6 @@ module Bundler::FileUtils
1592
2654
  module NoWrite
1593
2655
  include Bundler::FileUtils
1594
2656
  include LowMethods
1595
- @fileutils_output = $stderr
1596
- @fileutils_label = ''
1597
2657
  names = ::Bundler::FileUtils.collect_method(:noop)
1598
2658
  names.each do |name|
1599
2659
  module_eval(<<-EOS, __FILE__, __LINE__ + 1)
@@ -1618,8 +2678,6 @@ module Bundler::FileUtils
1618
2678
  module DryRun
1619
2679
  include Bundler::FileUtils
1620
2680
  include LowMethods
1621
- @fileutils_output = $stderr
1622
- @fileutils_label = ''
1623
2681
  names = ::Bundler::FileUtils.collect_method(:noop)
1624
2682
  names.each do |name|
1625
2683
  module_eval(<<-EOS, __FILE__, __LINE__ + 1)