rubygems-update 3.3.26 → 3.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (272) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +35 -0
  3. data/CONTRIBUTING.md +24 -1
  4. data/Manifest.txt +30 -27
  5. data/POLICIES.md +10 -8
  6. data/README.md +2 -2
  7. data/bin/gem +1 -4
  8. data/bin/update_rubygems +1 -1
  9. data/bundler/CHANGELOG.md +59 -0
  10. data/bundler/README.md +2 -2
  11. data/bundler/bundler.gemspec +2 -2
  12. data/bundler/exe/bundle +1 -4
  13. data/bundler/lib/bundler/build_metadata.rb +2 -2
  14. data/bundler/lib/bundler/cli/add.rb +1 -1
  15. data/bundler/lib/bundler/cli/check.rb +1 -1
  16. data/bundler/lib/bundler/cli/common.rb +1 -0
  17. data/bundler/lib/bundler/cli/console.rb +2 -2
  18. data/bundler/lib/bundler/cli/doctor.rb +4 -6
  19. data/bundler/lib/bundler/cli/gem.rb +62 -40
  20. data/bundler/lib/bundler/cli/install.rb +2 -3
  21. data/bundler/lib/bundler/cli/lock.rb +8 -5
  22. data/bundler/lib/bundler/cli/outdated.rb +1 -3
  23. data/bundler/lib/bundler/cli/viz.rb +1 -1
  24. data/bundler/lib/bundler/cli.rb +43 -2
  25. data/bundler/lib/bundler/compact_index_client/cache.rb +1 -1
  26. data/bundler/lib/bundler/compact_index_client/updater.rb +40 -39
  27. data/bundler/lib/bundler/constants.rb +1 -1
  28. data/bundler/lib/bundler/definition.rb +61 -31
  29. data/bundler/lib/bundler/dependency.rb +12 -11
  30. data/bundler/lib/bundler/digest.rb +1 -1
  31. data/bundler/lib/bundler/dsl.rb +1 -1
  32. data/bundler/lib/bundler/env.rb +1 -1
  33. data/bundler/lib/bundler/environment_preserver.rb +1 -0
  34. data/bundler/lib/bundler/errors.rb +1 -11
  35. data/bundler/lib/bundler/fetcher/compact_index.rb +9 -11
  36. data/bundler/lib/bundler/fetcher/dependency.rb +1 -1
  37. data/bundler/lib/bundler/fetcher/downloader.rb +2 -5
  38. data/bundler/lib/bundler/fetcher.rb +2 -6
  39. data/bundler/lib/bundler/force_platform.rb +18 -0
  40. data/bundler/lib/bundler/friendly_errors.rb +0 -3
  41. data/bundler/lib/bundler/gem_version_promoter.rb +52 -86
  42. data/bundler/lib/bundler/graph.rb +3 -3
  43. data/bundler/lib/bundler/index.rb +5 -13
  44. data/bundler/lib/bundler/injector.rb +1 -1
  45. data/bundler/lib/bundler/inline.rb +2 -2
  46. data/bundler/lib/bundler/installer/parallel_installer.rb +0 -31
  47. data/bundler/lib/bundler/installer.rb +6 -16
  48. data/bundler/lib/bundler/lazy_specification.rb +5 -1
  49. data/bundler/lib/bundler/lockfile_parser.rb +5 -5
  50. data/bundler/lib/bundler/man/bundle-add.1 +1 -1
  51. data/bundler/lib/bundler/man/bundle-binstubs.1 +1 -1
  52. data/bundler/lib/bundler/man/bundle-cache.1 +1 -1
  53. data/bundler/lib/bundler/man/bundle-check.1 +1 -1
  54. data/bundler/lib/bundler/man/bundle-clean.1 +1 -1
  55. data/bundler/lib/bundler/man/bundle-config.1 +1 -1
  56. data/bundler/lib/bundler/man/bundle-console.1 +1 -1
  57. data/bundler/lib/bundler/man/bundle-doctor.1 +1 -1
  58. data/bundler/lib/bundler/man/bundle-exec.1 +1 -1
  59. data/bundler/lib/bundler/man/bundle-gem.1 +27 -37
  60. data/bundler/lib/bundler/man/bundle-gem.1.ronn +5 -5
  61. data/bundler/lib/bundler/man/bundle-help.1 +1 -1
  62. data/bundler/lib/bundler/man/bundle-info.1 +1 -1
  63. data/bundler/lib/bundler/man/bundle-init.1 +1 -1
  64. data/bundler/lib/bundler/man/bundle-inject.1 +1 -1
  65. data/bundler/lib/bundler/man/bundle-install.1 +1 -30
  66. data/bundler/lib/bundler/man/bundle-install.1.ronn +0 -29
  67. data/bundler/lib/bundler/man/bundle-list.1 +1 -1
  68. data/bundler/lib/bundler/man/bundle-lock.1 +1 -1
  69. data/bundler/lib/bundler/man/bundle-open.1 +1 -1
  70. data/bundler/lib/bundler/man/bundle-outdated.1 +1 -1
  71. data/bundler/lib/bundler/man/bundle-platform.1 +2 -2
  72. data/bundler/lib/bundler/man/bundle-platform.1.ronn +1 -1
  73. data/bundler/lib/bundler/man/bundle-plugin.1 +1 -1
  74. data/bundler/lib/bundler/man/bundle-pristine.1 +1 -1
  75. data/bundler/lib/bundler/man/bundle-remove.1 +1 -1
  76. data/bundler/lib/bundler/man/bundle-show.1 +1 -1
  77. data/bundler/lib/bundler/man/bundle-update.1 +1 -1
  78. data/bundler/lib/bundler/man/bundle-version.1 +1 -1
  79. data/bundler/lib/bundler/man/bundle-viz.1 +1 -1
  80. data/bundler/lib/bundler/man/bundle.1 +1 -1
  81. data/bundler/lib/bundler/man/gemfile.5 +1 -1
  82. data/bundler/lib/bundler/mirror.rb +5 -7
  83. data/bundler/lib/bundler/plugin/index.rb +4 -4
  84. data/bundler/lib/bundler/plugin/installer/rubygems.rb +0 -4
  85. data/bundler/lib/bundler/resolver/base.rb +7 -11
  86. data/bundler/lib/bundler/resolver/candidate.rb +92 -0
  87. data/bundler/lib/bundler/resolver/incompatibility.rb +15 -0
  88. data/bundler/lib/bundler/resolver/package.rb +63 -0
  89. data/bundler/lib/bundler/resolver/root.rb +25 -0
  90. data/bundler/lib/bundler/resolver/spec_group.rb +26 -36
  91. data/bundler/lib/bundler/resolver.rb +294 -277
  92. data/bundler/lib/bundler/rubygems_ext.rb +11 -6
  93. data/bundler/lib/bundler/rubygems_gem_installer.rb +4 -2
  94. data/bundler/lib/bundler/rubygems_integration.rb +1 -9
  95. data/bundler/lib/bundler/runtime.rb +1 -5
  96. data/bundler/lib/bundler/settings.rb +0 -6
  97. data/bundler/lib/bundler/shared_helpers.rb +1 -0
  98. data/bundler/lib/bundler/source/git/git_proxy.rb +193 -67
  99. data/bundler/lib/bundler/source/git.rb +15 -17
  100. data/bundler/lib/bundler/source/metadata.rb +0 -1
  101. data/bundler/lib/bundler/source/path/installer.rb +1 -22
  102. data/bundler/lib/bundler/source/path.rb +5 -5
  103. data/bundler/lib/bundler/source/rubygems.rb +13 -67
  104. data/bundler/lib/bundler/source_list.rb +8 -2
  105. data/bundler/lib/bundler/spec_set.rb +7 -9
  106. data/bundler/lib/bundler/templates/Executable +1 -1
  107. data/bundler/lib/bundler/templates/Executable.bundler +4 -9
  108. data/bundler/lib/bundler/templates/Executable.standalone +2 -0
  109. data/bundler/lib/bundler/templates/newgem/Cargo.toml.tt +7 -0
  110. data/bundler/lib/bundler/templates/newgem/Gemfile.tt +3 -0
  111. data/bundler/lib/bundler/templates/newgem/README.md.tt +6 -4
  112. data/bundler/lib/bundler/templates/newgem/Rakefile.tt +2 -1
  113. data/bundler/lib/bundler/templates/newgem/circleci/config.yml.tt +12 -0
  114. data/bundler/lib/bundler/templates/newgem/ext/newgem/Cargo.toml.tt +15 -0
  115. data/bundler/lib/bundler/templates/newgem/ext/newgem/{extconf.rb.tt → extconf-c.rb.tt} +0 -0
  116. data/bundler/lib/bundler/templates/newgem/ext/newgem/extconf-rust.rb.tt +6 -0
  117. data/bundler/lib/bundler/templates/newgem/ext/newgem/src/lib.rs.tt +12 -0
  118. data/bundler/lib/bundler/templates/newgem/github/workflows/main.yml.tt +10 -0
  119. data/bundler/lib/bundler/templates/newgem/gitignore.tt +3 -0
  120. data/bundler/lib/bundler/templates/newgem/gitlab-ci.yml.tt +8 -0
  121. data/bundler/lib/bundler/templates/newgem/newgem.gemspec.tt +8 -2
  122. data/bundler/lib/bundler/ui/shell.rb +35 -12
  123. data/bundler/lib/bundler/ui/silent.rb +21 -5
  124. data/bundler/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb +3 -3
  125. data/bundler/lib/bundler/vendor/connection_pool/lib/connection_pool/wrapper.rb +0 -1
  126. data/bundler/lib/bundler/vendor/connection_pool/lib/connection_pool.rb +3 -1
  127. data/bundler/lib/bundler/vendor/fileutils/lib/fileutils.rb +1350 -408
  128. data/bundler/lib/bundler/vendor/net-http-persistent/README.rdoc +1 -1
  129. data/bundler/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb +1 -1
  130. data/bundler/lib/bundler/vendor/pub_grub/LICENSE.txt +21 -0
  131. data/bundler/lib/bundler/vendor/pub_grub/lib/pub_grub/assignment.rb +20 -0
  132. data/bundler/lib/bundler/vendor/pub_grub/lib/pub_grub/basic_package_source.rb +189 -0
  133. data/bundler/lib/bundler/vendor/pub_grub/lib/pub_grub/failure_writer.rb +182 -0
  134. data/bundler/lib/bundler/vendor/pub_grub/lib/pub_grub/incompatibility.rb +151 -0
  135. data/bundler/lib/bundler/vendor/pub_grub/lib/pub_grub/package.rb +43 -0
  136. data/bundler/lib/bundler/vendor/pub_grub/lib/pub_grub/partial_solution.rb +121 -0
  137. data/bundler/lib/bundler/vendor/pub_grub/lib/pub_grub/rubygems.rb +45 -0
  138. data/bundler/lib/bundler/vendor/pub_grub/lib/pub_grub/solve_failure.rb +19 -0
  139. data/bundler/lib/bundler/vendor/pub_grub/lib/pub_grub/static_package_source.rb +53 -0
  140. data/bundler/lib/bundler/vendor/pub_grub/lib/pub_grub/term.rb +105 -0
  141. data/bundler/lib/bundler/vendor/pub_grub/lib/pub_grub/version.rb +3 -0
  142. data/bundler/lib/bundler/vendor/pub_grub/lib/pub_grub/version_constraint.rb +124 -0
  143. data/bundler/lib/bundler/vendor/pub_grub/lib/pub_grub/version_range.rb +409 -0
  144. data/bundler/lib/bundler/vendor/pub_grub/lib/pub_grub/version_solver.rb +240 -0
  145. data/bundler/lib/bundler/vendor/pub_grub/lib/pub_grub/version_union.rb +178 -0
  146. data/bundler/lib/bundler/vendor/pub_grub/lib/pub_grub.rb +31 -0
  147. data/bundler/lib/bundler/vendor/thor/lib/thor/shell/basic.rb +1 -1
  148. data/bundler/lib/bundler/vendor/uri/lib/uri/common.rb +64 -16
  149. data/bundler/lib/bundler/vendor/uri/lib/uri/file.rb +7 -1
  150. data/bundler/lib/bundler/vendor/uri/lib/uri/ftp.rb +2 -1
  151. data/bundler/lib/bundler/vendor/uri/lib/uri/generic.rb +27 -7
  152. data/bundler/lib/bundler/vendor/uri/lib/uri/http.rb +40 -2
  153. data/bundler/lib/bundler/vendor/uri/lib/uri/https.rb +2 -1
  154. data/bundler/lib/bundler/vendor/uri/lib/uri/ldap.rb +1 -1
  155. data/bundler/lib/bundler/vendor/uri/lib/uri/ldaps.rb +2 -1
  156. data/bundler/lib/bundler/vendor/uri/lib/uri/mailto.rb +2 -2
  157. data/bundler/lib/bundler/vendor/uri/lib/uri/rfc2396_parser.rb +13 -7
  158. data/bundler/lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb +10 -5
  159. data/bundler/lib/bundler/vendor/uri/lib/uri/version.rb +1 -1
  160. data/bundler/lib/bundler/vendor/uri/lib/uri/ws.rb +1 -2
  161. data/bundler/lib/bundler/vendor/uri/lib/uri/wss.rb +2 -1
  162. data/bundler/lib/bundler/vendor/uri/lib/uri.rb +3 -2
  163. data/bundler/lib/bundler/vendored_persistent.rb +1 -33
  164. data/bundler/lib/bundler/{vendored_tmpdir.rb → vendored_pub_grub.rb} +1 -1
  165. data/bundler/lib/bundler/version.rb +5 -1
  166. data/bundler/lib/bundler/worker.rb +5 -7
  167. data/bundler/lib/bundler.rb +20 -64
  168. data/lib/rubygems/command_manager.rb +2 -2
  169. data/lib/rubygems/commands/fetch_command.rb +1 -1
  170. data/lib/rubygems/commands/install_command.rb +7 -3
  171. data/lib/rubygems/commands/rdoc_command.rb +3 -2
  172. data/lib/rubygems/commands/setup_command.rb +2 -2
  173. data/lib/rubygems/commands/unpack_command.rb +1 -1
  174. data/lib/rubygems/commands/update_command.rb +1 -7
  175. data/lib/rubygems/config_file.rb +33 -0
  176. data/lib/rubygems/core_ext/kernel_warn.rb +1 -2
  177. data/lib/rubygems/defaults.rb +15 -1
  178. data/lib/rubygems/dependency.rb +4 -1
  179. data/lib/rubygems/dependency_installer.rb +24 -24
  180. data/lib/rubygems/exceptions.rb +1 -3
  181. data/lib/rubygems/ext/builder.rb +3 -3
  182. data/lib/rubygems/ext/cargo_builder/link_flag_converter.rb +9 -5
  183. data/lib/rubygems/ext/cargo_builder.rb +15 -20
  184. data/lib/rubygems/ext/ext_conf_builder.rb +2 -0
  185. data/lib/rubygems/indexer.rb +1 -1
  186. data/lib/rubygems/installer.rb +5 -5
  187. data/lib/rubygems/optparse/lib/optparse.rb +20 -15
  188. data/lib/rubygems/package/tar_header.rb +11 -11
  189. data/lib/rubygems/platform.rb +0 -2
  190. data/lib/rubygems/request_set/gem_dependency_api.rb +104 -104
  191. data/lib/rubygems/requirement.rb +7 -7
  192. data/lib/rubygems/resolver/installer_set.rb +1 -1
  193. data/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb +1 -1
  194. data/lib/rubygems/resolver/molinillo/lib/molinillo/errors.rb +32 -26
  195. data/lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb +1 -1
  196. data/lib/rubygems/security/policies.rb +40 -40
  197. data/lib/rubygems/security/trust_dir.rb +1 -1
  198. data/lib/rubygems/security.rb +3 -16
  199. data/lib/rubygems/source.rb +2 -2
  200. data/lib/rubygems/specification.rb +37 -49
  201. data/lib/rubygems/specification_policy.rb +14 -0
  202. data/lib/rubygems/stub_specification.rb +2 -2
  203. data/lib/rubygems/text.rb +1 -1
  204. data/lib/rubygems/tsort/lib/tsort.rb +308 -310
  205. data/lib/rubygems/update_suggestion.rb +69 -0
  206. data/lib/rubygems/util.rb +1 -5
  207. data/lib/rubygems/validator.rb +1 -1
  208. data/lib/rubygems.rb +8 -3
  209. data/rubygems-update.gemspec +2 -2
  210. data/test/rubygems/helper.rb +7 -3
  211. data/test/rubygems/test_bundled_ca.rb +1 -1
  212. data/test/rubygems/test_exit.rb +6 -0
  213. data/test/rubygems/test_gem.rb +4 -9
  214. data/test/rubygems/test_gem_bundler_version_finder.rb +2 -1
  215. data/test/rubygems/test_gem_command_manager.rb +1 -1
  216. data/test/rubygems/test_gem_commands_install_command.rb +19 -0
  217. data/test/rubygems/test_gem_commands_setup_command.rb +1 -8
  218. data/test/rubygems/test_gem_commands_update_command.rb +6 -6
  219. data/test/rubygems/test_gem_config_file.rb +1 -1
  220. data/test/rubygems/test_gem_dependency.rb +2 -0
  221. data/test/rubygems/test_gem_ext_builder.rb +3 -3
  222. data/test/rubygems/test_gem_ext_cargo_builder/custom_name/Cargo.lock +22 -32
  223. data/test/rubygems/test_gem_ext_cargo_builder/custom_name/Cargo.toml +1 -1
  224. data/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock +22 -32
  225. data/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml +1 -1
  226. data/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/src/lib.rs +12 -0
  227. data/test/rubygems/test_gem_ext_cargo_builder.rb +22 -27
  228. data/test/rubygems/test_gem_ext_cargo_builder_link_flag_converter.rb +16 -16
  229. data/test/rubygems/test_gem_ext_cargo_builder_unit.rb +0 -10
  230. data/test/rubygems/test_gem_indexer.rb +39 -20
  231. data/test/rubygems/test_gem_installer.rb +68 -2
  232. data/test/rubygems/test_gem_package_tar_header.rb +13 -13
  233. data/test/rubygems/test_gem_platform.rb +59 -60
  234. data/test/rubygems/test_gem_remote_fetcher.rb +4 -4
  235. data/test/rubygems/test_gem_request_set.rb +2 -2
  236. data/test/rubygems/test_gem_requirement.rb +1 -1
  237. data/test/rubygems/test_gem_resolver_api_set.rb +12 -12
  238. data/test/rubygems/test_gem_resolver_api_specification.rb +19 -19
  239. data/test/rubygems/test_gem_resolver_git_specification.rb +1 -1
  240. data/test/rubygems/test_gem_security_policy.rb +10 -10
  241. data/test/rubygems/test_gem_security_trust_dir.rb +2 -2
  242. data/test/rubygems/test_gem_specification.rb +50 -37
  243. data/test/rubygems/test_gem_uninstaller.rb +1 -1
  244. data/test/rubygems/test_gem_update_suggestion.rb +208 -0
  245. data/test/rubygems/test_kernel.rb +10 -8
  246. data/test/rubygems/test_require.rb +70 -55
  247. metadata +34 -31
  248. data/bundler/lib/bundler/templates/newgem/travis.yml.tt +0 -6
  249. data/bundler/lib/bundler/vendor/molinillo/LICENSE +0 -9
  250. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/delegates/resolution_state.rb +0 -57
  251. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/delegates/specification_provider.rb +0 -88
  252. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/action.rb +0 -36
  253. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb +0 -66
  254. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_vertex.rb +0 -62
  255. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/delete_edge.rb +0 -63
  256. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb +0 -61
  257. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/log.rb +0 -126
  258. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/set_payload.rb +0 -46
  259. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/tag.rb +0 -36
  260. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb +0 -164
  261. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb +0 -255
  262. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/errors.rb +0 -149
  263. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb +0 -6
  264. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/modules/specification_provider.rb +0 -112
  265. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/modules/ui.rb +0 -67
  266. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb +0 -839
  267. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/resolver.rb +0 -46
  268. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/state.rb +0 -58
  269. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo.rb +0 -11
  270. data/bundler/lib/bundler/vendor/tmpdir/lib/tmpdir.rb +0 -154
  271. data/bundler/lib/bundler/vendored_molinillo.rb +0 -4
  272. data/bundler/lib/bundler/version_ranges.rb +0 -122
@@ -6,103 +6,181 @@ rescue LoadError
6
6
  # for make mjit-headers
7
7
  end
8
8
 
9
+ # Namespace for file utility methods for copying, moving, removing, etc.
9
10
  #
10
- # = fileutils.rb
11
+ # == What's Here
11
12
  #
12
- # Copyright (c) 2000-2007 Minero Aoki
13
+ # First, what’s elsewhere. \Module \Bundler::FileUtils:
13
14
  #
14
- # This program is free software.
15
- # 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).
16
18
  #
17
- # == module Bundler::FileUtils
19
+ # Here, module \Bundler::FileUtils provides methods that are useful for:
18
20
  #
19
- # 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].
20
29
  #
21
- # === Module Functions
30
+ # === Creating
22
31
  #
23
- # 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
24
40
  #
25
- # Bundler::FileUtils.cd(dir, **options)
26
- # Bundler::FileUtils.cd(dir, **options) {|dir| block }
27
- # Bundler::FileUtils.pwd()
28
- # Bundler::FileUtils.mkdir(dir, **options)
29
- # Bundler::FileUtils.mkdir(list, **options)
30
- # Bundler::FileUtils.mkdir_p(dir, **options)
31
- # Bundler::FileUtils.mkdir_p(list, **options)
32
- # Bundler::FileUtils.rmdir(dir, **options)
33
- # Bundler::FileUtils.rmdir(list, **options)
34
- # Bundler::FileUtils.ln(target, link, **options)
35
- # Bundler::FileUtils.ln(targets, dir, **options)
36
- # Bundler::FileUtils.ln_s(target, link, **options)
37
- # Bundler::FileUtils.ln_s(targets, dir, **options)
38
- # Bundler::FileUtils.ln_sf(target, link, **options)
39
- # Bundler::FileUtils.cp(src, dest, **options)
40
- # Bundler::FileUtils.cp(list, dir, **options)
41
- # Bundler::FileUtils.cp_r(src, dest, **options)
42
- # Bundler::FileUtils.cp_r(list, dir, **options)
43
- # Bundler::FileUtils.mv(src, dest, **options)
44
- # Bundler::FileUtils.mv(list, dir, **options)
45
- # Bundler::FileUtils.rm(list, **options)
46
- # Bundler::FileUtils.rm_r(list, **options)
47
- # Bundler::FileUtils.rm_rf(list, **options)
48
- # Bundler::FileUtils.install(src, dest, **options)
49
- # Bundler::FileUtils.chmod(mode, list, **options)
50
- # Bundler::FileUtils.chmod_R(mode, list, **options)
51
- # Bundler::FileUtils.chown(user, group, list, **options)
52
- # Bundler::FileUtils.chown_R(user, group, list, **options)
53
- # Bundler::FileUtils.touch(list, **options)
41
+ # === Deleting
54
42
  #
55
- # Possible <tt>options</tt> are:
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.
56
52
  #
57
- # <tt>:force</tt> :: forced operation (rewrite files if exist, remove
58
- # directories if not empty, etc.);
59
- # <tt>:verbose</tt> :: print command to be run, in bash syntax, before
60
- # performing it;
61
- # <tt>:preserve</tt> :: preserve object's group, user and modification
62
- # time on copying;
63
- # <tt>:noop</tt> :: no changes are made (usable in combination with
64
- # <tt>:verbose</tt> which will print the command to run)
53
+ # === Querying
65
54
  #
66
- # Each method documents the options that it honours. See also ::commands,
67
- # ::options and ::options_of methods to introspect which command have which
68
- # options.
55
+ # - ::pwd, ::getwd: Returns the path to the working directory.
56
+ # - ::uptodate?: Returns whether a given entry is newer than given other entries.
69
57
  #
70
- # All methods that have the concept of a "source" file or directory can take
71
- # either one file or a list of files in that argument. See the method
72
- # documentation for examples.
58
+ # === Setting
73
59
  #
74
- # There are some `low level' methods, which do not accept keyword arguments:
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.
75
67
  #
76
- # Bundler::FileUtils.copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = false)
77
- # Bundler::FileUtils.copy_file(src, dest, preserve = false, dereference = true)
78
- # Bundler::FileUtils.copy_stream(srcstream, deststream)
79
- # Bundler::FileUtils.remove_entry(path, force = false)
80
- # Bundler::FileUtils.remove_entry_secure(path, force = false)
81
- # Bundler::FileUtils.remove_file(path, force = false)
82
- # Bundler::FileUtils.compare_file(path_a, path_b)
83
- # Bundler::FileUtils.compare_stream(stream_a, stream_b)
84
- # Bundler::FileUtils.uptodate?(file, cmp_list)
68
+ # === Comparing
85
69
  #
86
- # == module Bundler::FileUtils::Verbose
70
+ # - ::compare_file, ::cmp, ::identical?: Returns whether two entries are identical.
71
+ # - ::compare_stream: Returns whether two streams are identical.
87
72
  #
88
- # This module has all methods of Bundler::FileUtils module, but it outputs messages
89
- # before acting. This equates to passing the <tt>:verbose</tt> flag to methods
90
- # in Bundler::FileUtils.
73
+ # === Copying
91
74
  #
92
- # == module Bundler::FileUtils::NoWrite
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.
93
83
  #
94
- # This module has all methods of Bundler::FileUtils module, but never changes
95
- # files/directories. This equates to passing the <tt>:noop</tt> flag to methods
96
- # in Bundler::FileUtils.
84
+ # === Moving
97
85
  #
98
- # == module Bundler::FileUtils::DryRun
86
+ # - ::mv, ::move: Moves entries.
99
87
  #
100
- # This module has all methods of Bundler::FileUtils module, but never changes
101
- # files/directories. This equates to passing the <tt>:noop</tt> and
102
- # <tt>:verbose</tt> flags to methods in Bundler::FileUtils.
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].
103
181
  #
104
182
  module Bundler::FileUtils
105
- VERSION = "1.4.1"
183
+ VERSION = "1.7.0"
106
184
 
107
185
  def self.private_module_function(name) #:nodoc:
108
186
  module_function name
@@ -110,7 +188,13 @@ module Bundler::FileUtils
110
188
  end
111
189
 
112
190
  #
113
- # Returns the name of the current directory.
191
+ # Returns a string containing the path to the current directory:
192
+ #
193
+ # Bundler::FileUtils.pwd # => "/rdoc/fileutils"
194
+ #
195
+ # Bundler::FileUtils.getwd is an alias for Bundler::FileUtils.pwd.
196
+ #
197
+ # Related: Bundler::FileUtils.cd.
114
198
  #
115
199
  def pwd
116
200
  Dir.pwd
@@ -120,19 +204,40 @@ module Bundler::FileUtils
120
204
  alias getwd pwd
121
205
  module_function :getwd
122
206
 
207
+ # Changes the working directory to the given +dir+, which
208
+ # should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments]:
209
+ #
210
+ # With no block given,
211
+ # changes the current directory to the directory at +dir+; returns zero:
212
+ #
213
+ # Bundler::FileUtils.pwd # => "/rdoc/fileutils"
214
+ # Bundler::FileUtils.cd('..')
215
+ # Bundler::FileUtils.pwd # => "/rdoc"
216
+ # Bundler::FileUtils.cd('fileutils')
217
+ #
218
+ # With a block given, changes the current directory to the directory
219
+ # at +dir+, calls the block with argument +dir+,
220
+ # and restores the original current directory; returns the block's value:
123
221
  #
124
- # Changes the current directory to the directory +dir+.
222
+ # Bundler::FileUtils.pwd # => "/rdoc/fileutils"
223
+ # Bundler::FileUtils.cd('..') { |arg| [arg, Bundler::FileUtils.pwd] } # => ["..", "/rdoc"]
224
+ # Bundler::FileUtils.pwd # => "/rdoc/fileutils"
125
225
  #
126
- # If this method is called with block, resumes to the previous
127
- # working directory after the block execution has finished.
226
+ # Keyword arguments:
128
227
  #
129
- # Bundler::FileUtils.cd('/') # change directory
228
+ # - <tt>verbose: true</tt> - prints an equivalent command:
130
229
  #
131
- # Bundler::FileUtils.cd('/', verbose: true) # change directory and report it
230
+ # Bundler::FileUtils.cd('..')
231
+ # Bundler::FileUtils.cd('fileutils')
132
232
  #
133
- # Bundler::FileUtils.cd('/') do # change directory
134
- # # ... # do something
135
- # end # return to original directory
233
+ # Output:
234
+ #
235
+ # cd ..
236
+ # cd fileutils
237
+ #
238
+ # Bundler::FileUtils.chdir is an alias for Bundler::FileUtils.cd.
239
+ #
240
+ # Related: Bundler::FileUtils.pwd.
136
241
  #
137
242
  def cd(dir, verbose: nil, &block) # :yield: dir
138
243
  fu_output_message "cd #{dir}" if verbose
@@ -146,11 +251,19 @@ module Bundler::FileUtils
146
251
  module_function :chdir
147
252
 
148
253
  #
149
- # Returns true if +new+ is newer than all +old_list+.
150
- # Non-existent files are older than any file.
254
+ # Returns +true+ if the file at path +new+
255
+ # is newer than all the files at paths in array +old_list+;
256
+ # +false+ otherwise.
257
+ #
258
+ # Argument +new+ and the elements of +old_list+
259
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments]:
151
260
  #
152
- # Bundler::FileUtils.uptodate?('hello.o', %w(hello.c hello.h)) or \
153
- # system 'make hello.o'
261
+ # Bundler::FileUtils.uptodate?('Rakefile', ['Gemfile', 'README.md']) # => true
262
+ # Bundler::FileUtils.uptodate?('Gemfile', ['Rakefile', 'README.md']) # => false
263
+ #
264
+ # A non-existent file is considered to be infinitely old.
265
+ #
266
+ # Related: Bundler::FileUtils.touch.
154
267
  #
155
268
  def uptodate?(new, old_list)
156
269
  return false unless File.exist?(new)
@@ -170,12 +283,39 @@ module Bundler::FileUtils
170
283
  private_module_function :remove_trailing_slash
171
284
 
172
285
  #
173
- # Creates one or more directories.
286
+ # Creates directories at the paths in the given +list+
287
+ # (a single path or an array of paths);
288
+ # returns +list+ if it is an array, <tt>[list]</tt> otherwise.
289
+ #
290
+ # Argument +list+ or its elements
291
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
292
+ #
293
+ # With no keyword arguments, creates a directory at each +path+ in +list+
294
+ # by calling: <tt>Dir.mkdir(path, mode)</tt>;
295
+ # see {Dir.mkdir}[https://docs.ruby-lang.org/en/master/Dir.html#method-c-mkdir]:
296
+ #
297
+ # Bundler::FileUtils.mkdir(%w[tmp0 tmp1]) # => ["tmp0", "tmp1"]
298
+ # Bundler::FileUtils.mkdir('tmp4') # => ["tmp4"]
299
+ #
300
+ # Keyword arguments:
301
+ #
302
+ # - <tt>mode: <i>mode</i></tt> - also calls <tt>File.chmod(mode, path)</tt>;
303
+ # see {File.chmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-chmod].
304
+ # - <tt>noop: true</tt> - does not create directories.
305
+ # - <tt>verbose: true</tt> - prints an equivalent command:
306
+ #
307
+ # Bundler::FileUtils.mkdir(%w[tmp0 tmp1], verbose: true)
308
+ # Bundler::FileUtils.mkdir(%w[tmp2 tmp3], mode: 0700, verbose: true)
309
+ #
310
+ # Output:
174
311
  #
175
- # Bundler::FileUtils.mkdir 'test'
176
- # Bundler::FileUtils.mkdir %w(tmp data)
177
- # Bundler::FileUtils.mkdir 'notexist', noop: true # Does not really create.
178
- # Bundler::FileUtils.mkdir 'tmp', mode: 0700
312
+ # mkdir tmp0 tmp1
313
+ # mkdir -m 700 tmp2 tmp3
314
+ #
315
+ # Raises an exception if any path points to an existing
316
+ # file or directory, or if for any reason a directory cannot be created.
317
+ #
318
+ # Related: Bundler::FileUtils.mkdir_p.
179
319
  #
180
320
  def mkdir(list, mode: nil, noop: nil, verbose: nil)
181
321
  list = fu_list(list)
@@ -189,40 +329,56 @@ module Bundler::FileUtils
189
329
  module_function :mkdir
190
330
 
191
331
  #
192
- # Creates a directory and all its parent directories.
193
- # For example,
332
+ # Creates directories at the paths in the given +list+
333
+ # (a single path or an array of paths),
334
+ # also creating ancestor directories as needed;
335
+ # returns +list+ if it is an array, <tt>[list]</tt> otherwise.
336
+ #
337
+ # Argument +list+ or its elements
338
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
339
+ #
340
+ # With no keyword arguments, creates a directory at each +path+ in +list+,
341
+ # along with any needed ancestor directories,
342
+ # by calling: <tt>Dir.mkdir(path, mode)</tt>;
343
+ # see {Dir.mkdir}[https://docs.ruby-lang.org/en/master/Dir.html#method-c-mkdir]:
344
+ #
345
+ # Bundler::FileUtils.mkdir_p(%w[tmp0/tmp1 tmp2/tmp3]) # => ["tmp0/tmp1", "tmp2/tmp3"]
346
+ # Bundler::FileUtils.mkdir_p('tmp4/tmp5') # => ["tmp4/tmp5"]
347
+ #
348
+ # Keyword arguments:
349
+ #
350
+ # - <tt>mode: <i>mode</i></tt> - also calls <tt>File.chmod(mode, path)</tt>;
351
+ # see {File.chmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-chmod].
352
+ # - <tt>noop: true</tt> - does not create directories.
353
+ # - <tt>verbose: true</tt> - prints an equivalent command:
354
+ #
355
+ # Bundler::FileUtils.mkdir_p(%w[tmp0 tmp1], verbose: true)
356
+ # Bundler::FileUtils.mkdir_p(%w[tmp2 tmp3], mode: 0700, verbose: true)
194
357
  #
195
- # Bundler::FileUtils.mkdir_p '/usr/local/lib/ruby'
358
+ # Output:
196
359
  #
197
- # causes to make following directories, if they do not exist.
360
+ # mkdir -p tmp0 tmp1
361
+ # mkdir -p -m 700 tmp2 tmp3
198
362
  #
199
- # * /usr
200
- # * /usr/local
201
- # * /usr/local/lib
202
- # * /usr/local/lib/ruby
363
+ # Raises an exception if for any reason a directory cannot be created.
203
364
  #
204
- # You can pass several directories at a time in a list.
365
+ # Bundler::FileUtils.mkpath and Bundler::FileUtils.makedirs are aliases for Bundler::FileUtils.mkdir_p.
366
+ #
367
+ # Related: Bundler::FileUtils.mkdir.
205
368
  #
206
369
  def mkdir_p(list, mode: nil, noop: nil, verbose: nil)
207
370
  list = fu_list(list)
208
371
  fu_output_message "mkdir -p #{mode ? ('-m %03o ' % mode) : ''}#{list.join ' '}" if verbose
209
372
  return *list if noop
210
373
 
211
- list.map {|path| remove_trailing_slash(path)}.each do |path|
212
- # optimize for the most common case
213
- begin
214
- fu_mkdir path, mode
215
- next
216
- rescue SystemCallError
217
- next if File.directory?(path)
218
- end
374
+ list.each do |item|
375
+ path = remove_trailing_slash(item)
219
376
 
220
377
  stack = []
221
- until path == stack.last # dirname("/")=="/", dirname("C:/")=="C:/"
378
+ until File.directory?(path) || File.dirname(path) == path
222
379
  stack.push path
223
380
  path = File.dirname(path)
224
381
  end
225
- stack.pop # root directory should exist
226
382
  stack.reverse_each do |dir|
227
383
  begin
228
384
  fu_mkdir dir, mode
@@ -253,12 +409,39 @@ module Bundler::FileUtils
253
409
  private_module_function :fu_mkdir
254
410
 
255
411
  #
256
- # Removes one or more directories.
412
+ # Removes directories at the paths in the given +list+
413
+ # (a single path or an array of paths);
414
+ # returns +list+, if it is an array, <tt>[list]</tt> otherwise.
415
+ #
416
+ # Argument +list+ or its elements
417
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
418
+ #
419
+ # With no keyword arguments, removes the directory at each +path+ in +list+,
420
+ # by calling: <tt>Dir.rmdir(path)</tt>;
421
+ # see {Dir.rmdir}[https://docs.ruby-lang.org/en/master/Dir.html#method-c-rmdir]:
422
+ #
423
+ # Bundler::FileUtils.rmdir(%w[tmp0/tmp1 tmp2/tmp3]) # => ["tmp0/tmp1", "tmp2/tmp3"]
424
+ # Bundler::FileUtils.rmdir('tmp4/tmp5') # => ["tmp4/tmp5"]
425
+ #
426
+ # Keyword arguments:
427
+ #
428
+ # - <tt>parents: true</tt> - removes successive ancestor directories
429
+ # if empty.
430
+ # - <tt>noop: true</tt> - does not remove directories.
431
+ # - <tt>verbose: true</tt> - prints an equivalent command:
257
432
  #
258
- # Bundler::FileUtils.rmdir 'somedir'
259
- # Bundler::FileUtils.rmdir %w(somedir anydir otherdir)
260
- # # Does not really remove directory; outputs message.
261
- # Bundler::FileUtils.rmdir 'somedir', verbose: true, noop: true
433
+ # Bundler::FileUtils.rmdir(%w[tmp0/tmp1 tmp2/tmp3], parents: true, verbose: true)
434
+ # Bundler::FileUtils.rmdir('tmp4/tmp5', parents: true, verbose: true)
435
+ #
436
+ # Output:
437
+ #
438
+ # rmdir -p tmp0/tmp1 tmp2/tmp3
439
+ # rmdir -p tmp4/tmp5
440
+ #
441
+ # Raises an exception if a directory does not exist
442
+ # or if for any reason a directory cannot be removed.
443
+ #
444
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
262
445
  #
263
446
  def rmdir(list, parents: nil, noop: nil, verbose: nil)
264
447
  list = fu_list(list)
@@ -279,26 +462,62 @@ module Bundler::FileUtils
279
462
  end
280
463
  module_function :rmdir
281
464
 
465
+ # Creates {hard links}[https://en.wikipedia.org/wiki/Hard_link].
466
+ #
467
+ # Arguments +src+ (a single path or an array of paths)
468
+ # and +dest+ (a single path)
469
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
470
+ #
471
+ # When +src+ is the path to an existing file
472
+ # and +dest+ is the path to a non-existent file,
473
+ # creates a hard link at +dest+ pointing to +src+; returns zero:
474
+ #
475
+ # Dir.children('tmp0/') # => ["t.txt"]
476
+ # Dir.children('tmp1/') # => []
477
+ # Bundler::FileUtils.ln('tmp0/t.txt', 'tmp1/t.lnk') # => 0
478
+ # Dir.children('tmp1/') # => ["t.lnk"]
479
+ #
480
+ # When +src+ is the path to an existing file
481
+ # and +dest+ is the path to an existing directory,
482
+ # creates a hard link at <tt>dest/src</tt> pointing to +src+; returns zero:
282
483
  #
283
- # :call-seq:
284
- # Bundler::FileUtils.ln(target, link, force: nil, noop: nil, verbose: nil)
285
- # Bundler::FileUtils.ln(target, dir, force: nil, noop: nil, verbose: nil)
286
- # Bundler::FileUtils.ln(targets, dir, force: nil, noop: nil, verbose: nil)
484
+ # Dir.children('tmp2') # => ["t.dat"]
485
+ # Dir.children('tmp3') # => []
486
+ # Bundler::FileUtils.ln('tmp2/t.dat', 'tmp3') # => 0
487
+ # Dir.children('tmp3') # => ["t.dat"]
287
488
  #
288
- # In the first form, creates a hard link +link+ which points to +target+.
289
- # If +link+ already exists, raises Errno::EEXIST.
290
- # But if the +force+ option is set, overwrites +link+.
489
+ # When +src+ is an array of paths to existing files
490
+ # and +dest+ is the path to an existing directory,
491
+ # then for each path +target+ in +src+,
492
+ # creates a hard link at <tt>dest/target</tt> pointing to +target+;
493
+ # returns +src+:
291
494
  #
292
- # Bundler::FileUtils.ln 'gcc', 'cc', verbose: true
293
- # Bundler::FileUtils.ln '/usr/bin/emacs21', '/usr/bin/emacs'
495
+ # Dir.children('tmp4/') # => []
496
+ # Bundler::FileUtils.ln(['tmp0/t.txt', 'tmp2/t.dat'], 'tmp4/') # => ["tmp0/t.txt", "tmp2/t.dat"]
497
+ # Dir.children('tmp4/') # => ["t.dat", "t.txt"]
294
498
  #
295
- # In the second form, creates a link +dir/target+ pointing to +target+.
296
- # In the third form, creates several hard links in the directory +dir+,
297
- # pointing to each item in +targets+.
298
- # If +dir+ is not a directory, raises Errno::ENOTDIR.
499
+ # Keyword arguments:
299
500
  #
300
- # Bundler::FileUtils.cd '/sbin'
301
- # Bundler::FileUtils.ln %w(cp mv mkdir), '/bin' # Now /sbin/cp and /bin/cp are linked.
501
+ # - <tt>force: true</tt> - overwrites +dest+ if it exists.
502
+ # - <tt>noop: true</tt> - does not create links.
503
+ # - <tt>verbose: true</tt> - prints an equivalent command:
504
+ #
505
+ # Bundler::FileUtils.ln('tmp0/t.txt', 'tmp1/t.lnk', verbose: true)
506
+ # Bundler::FileUtils.ln('tmp2/t.dat', 'tmp3', verbose: true)
507
+ # Bundler::FileUtils.ln(['tmp0/t.txt', 'tmp2/t.dat'], 'tmp4/', verbose: true)
508
+ #
509
+ # Output:
510
+ #
511
+ # ln tmp0/t.txt tmp1/t.lnk
512
+ # ln tmp2/t.dat tmp3
513
+ # ln tmp0/t.txt tmp2/t.dat tmp4/
514
+ #
515
+ # Raises an exception if +dest+ is the path to an existing file
516
+ # and keyword argument +force+ is not +true+.
517
+ #
518
+ # Bundler::FileUtils#link is an alias for Bundler::FileUtils#ln.
519
+ #
520
+ # Related: Bundler::FileUtils.link_entry (has different options).
302
521
  #
303
522
  def ln(src, dest, force: nil, noop: nil, verbose: nil)
304
523
  fu_output_message "ln#{force ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if verbose
@@ -313,28 +532,103 @@ module Bundler::FileUtils
313
532
  alias link ln
314
533
  module_function :link
315
534
 
316
- #
317
- # Hard link +src+ to +dest+. If +src+ is a directory, this method links
318
- # all its contents recursively. If +dest+ is a directory, links
319
- # +src+ to +dest/src+.
320
- #
321
- # +src+ can be a list of files.
322
- #
323
- # If +dereference_root+ is true, this method dereference tree root.
324
- #
325
- # If +remove_destination+ is true, this method removes each destination file before copy.
326
- #
327
- # Bundler::FileUtils.rm_r site_ruby + '/mylib', force: true
328
- # Bundler::FileUtils.cp_lr 'lib/', site_ruby + '/mylib'
329
- #
330
- # # Examples of linking several files to target directory.
331
- # Bundler::FileUtils.cp_lr %w(mail.rb field.rb debug/), site_ruby + '/tmail'
332
- # Bundler::FileUtils.cp_lr Dir.glob('*.rb'), '/home/aamine/lib/ruby', noop: true, verbose: true
333
- #
334
- # # If you want to link all contents of a directory instead of the
335
- # # directory itself, c.f. src/x -> dest/x, src/y -> dest/y,
336
- # # use the following code.
337
- # Bundler::FileUtils.cp_lr 'src/.', 'dest' # cp_lr('src', 'dest') makes dest/src, but this doesn't.
535
+ # Creates {hard links}[https://en.wikipedia.org/wiki/Hard_link].
536
+ #
537
+ # Arguments +src+ (a single path or an array of paths)
538
+ # and +dest+ (a single path)
539
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
540
+ #
541
+ # If +src+ is the path to a directory and +dest+ does not exist,
542
+ # creates links +dest+ and descendents pointing to +src+ and its descendents:
543
+ #
544
+ # tree('src0')
545
+ # # => src0
546
+ # # |-- sub0
547
+ # # | |-- src0.txt
548
+ # # | `-- src1.txt
549
+ # # `-- sub1
550
+ # # |-- src2.txt
551
+ # # `-- src3.txt
552
+ # File.exist?('dest0') # => false
553
+ # Bundler::FileUtils.cp_lr('src0', 'dest0')
554
+ # tree('dest0')
555
+ # # => dest0
556
+ # # |-- sub0
557
+ # # | |-- src0.txt
558
+ # # | `-- src1.txt
559
+ # # `-- sub1
560
+ # # |-- src2.txt
561
+ # # `-- src3.txt
562
+ #
563
+ # If +src+ and +dest+ are both paths to directories,
564
+ # creates links <tt>dest/src</tt> and descendents
565
+ # pointing to +src+ and its descendents:
566
+ #
567
+ # tree('src1')
568
+ # # => src1
569
+ # # |-- sub0
570
+ # # | |-- src0.txt
571
+ # # | `-- src1.txt
572
+ # # `-- sub1
573
+ # # |-- src2.txt
574
+ # # `-- src3.txt
575
+ # Bundler::FileUtils.mkdir('dest1')
576
+ # Bundler::FileUtils.cp_lr('src1', 'dest1')
577
+ # tree('dest1')
578
+ # # => dest1
579
+ # # `-- src1
580
+ # # |-- sub0
581
+ # # | |-- src0.txt
582
+ # # | `-- src1.txt
583
+ # # `-- sub1
584
+ # # |-- src2.txt
585
+ # # `-- src3.txt
586
+ #
587
+ # If +src+ is an array of paths to entries and +dest+ is the path to a directory,
588
+ # for each path +filepath+ in +src+, creates a link at <tt>dest/filepath</tt>
589
+ # pointing to that path:
590
+ #
591
+ # tree('src2')
592
+ # # => src2
593
+ # # |-- sub0
594
+ # # | |-- src0.txt
595
+ # # | `-- src1.txt
596
+ # # `-- sub1
597
+ # # |-- src2.txt
598
+ # # `-- src3.txt
599
+ # Bundler::FileUtils.mkdir('dest2')
600
+ # Bundler::FileUtils.cp_lr(['src2/sub0', 'src2/sub1'], 'dest2')
601
+ # tree('dest2')
602
+ # # => dest2
603
+ # # |-- sub0
604
+ # # | |-- src0.txt
605
+ # # | `-- src1.txt
606
+ # # `-- sub1
607
+ # # |-- src2.txt
608
+ # # `-- src3.txt
609
+ #
610
+ # Keyword arguments:
611
+ #
612
+ # - <tt>dereference_root: false</tt> - if +src+ is a symbolic link,
613
+ # does not dereference it.
614
+ # - <tt>noop: true</tt> - does not create links.
615
+ # - <tt>remove_destination: true</tt> - removes +dest+ before creating links.
616
+ # - <tt>verbose: true</tt> - prints an equivalent command:
617
+ #
618
+ # Bundler::FileUtils.cp_lr('src0', 'dest0', noop: true, verbose: true)
619
+ # Bundler::FileUtils.cp_lr('src1', 'dest1', noop: true, verbose: true)
620
+ # Bundler::FileUtils.cp_lr(['src2/sub0', 'src2/sub1'], 'dest2', noop: true, verbose: true)
621
+ #
622
+ # Output:
623
+ #
624
+ # cp -lr src0 dest0
625
+ # cp -lr src1 dest1
626
+ # cp -lr src2/sub0 src2/sub1 dest2
627
+ #
628
+ # Raises an exception if +dest+ is the path to an existing file or directory
629
+ # and keyword argument <tt>remove_destination: true</tt> is not given.
630
+ #
631
+ # Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
338
632
  #
339
633
  def cp_lr(src, dest, noop: nil, verbose: nil,
340
634
  dereference_root: true, remove_destination: false)
@@ -346,27 +640,81 @@ module Bundler::FileUtils
346
640
  end
347
641
  module_function :cp_lr
348
642
 
643
+ # Creates {symbolic links}[https://en.wikipedia.org/wiki/Symbolic_link].
644
+ #
645
+ # Arguments +src+ (a single path or an array of paths)
646
+ # and +dest+ (a single path)
647
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
648
+ #
649
+ # If +src+ is the path to an existing file:
650
+ #
651
+ # - When +dest+ is the path to a non-existent file,
652
+ # creates a symbolic link at +dest+ pointing to +src+:
653
+ #
654
+ # Bundler::FileUtils.touch('src0.txt')
655
+ # File.exist?('dest0.txt') # => false
656
+ # Bundler::FileUtils.ln_s('src0.txt', 'dest0.txt')
657
+ # File.symlink?('dest0.txt') # => true
349
658
  #
350
- # :call-seq:
351
- # Bundler::FileUtils.ln_s(target, link, force: nil, noop: nil, verbose: nil)
352
- # Bundler::FileUtils.ln_s(target, dir, force: nil, noop: nil, verbose: nil)
353
- # Bundler::FileUtils.ln_s(targets, dir, force: nil, noop: nil, verbose: nil)
659
+ # - When +dest+ is the path to an existing file,
660
+ # creates a symbolic link at +dest+ pointing to +src+
661
+ # if and only if keyword argument <tt>force: true</tt> is given
662
+ # (raises an exception otherwise):
354
663
  #
355
- # In the first form, creates a symbolic link +link+ which points to +target+.
356
- # If +link+ already exists, raises Errno::EEXIST.
357
- # But if the <tt>force</tt> option is set, overwrites +link+.
664
+ # Bundler::FileUtils.touch('src1.txt')
665
+ # Bundler::FileUtils.touch('dest1.txt')
666
+ # Bundler::FileUtils.ln_s('src1.txt', 'dest1.txt', force: true)
667
+ # FileTest.symlink?('dest1.txt') # => true
358
668
  #
359
- # Bundler::FileUtils.ln_s '/usr/bin/ruby', '/usr/local/bin/ruby'
360
- # Bundler::FileUtils.ln_s 'verylongsourcefilename.c', 'c', force: true
669
+ # Bundler::FileUtils.ln_s('src1.txt', 'dest1.txt') # Raises Errno::EEXIST.
361
670
  #
362
- # In the second form, creates a link +dir/target+ pointing to +target+.
363
- # In the third form, creates several symbolic links in the directory +dir+,
364
- # pointing to each item in +targets+.
365
- # If +dir+ is not a directory, raises Errno::ENOTDIR.
671
+ # If +dest+ is the path to a directory,
672
+ # creates a symbolic link at <tt>dest/src</tt> pointing to +src+:
366
673
  #
367
- # Bundler::FileUtils.ln_s Dir.glob('/bin/*.rb'), '/home/foo/bin'
674
+ # Bundler::FileUtils.touch('src2.txt')
675
+ # Bundler::FileUtils.mkdir('destdir2')
676
+ # Bundler::FileUtils.ln_s('src2.txt', 'destdir2')
677
+ # File.symlink?('destdir2/src2.txt') # => true
368
678
  #
369
- def ln_s(src, dest, force: nil, noop: nil, verbose: nil)
679
+ # If +src+ is an array of paths to existing files and +dest+ is a directory,
680
+ # for each child +child+ in +src+ creates a symbolic link <tt>dest/child</tt>
681
+ # pointing to +child+:
682
+ #
683
+ # Bundler::FileUtils.mkdir('srcdir3')
684
+ # Bundler::FileUtils.touch('srcdir3/src0.txt')
685
+ # Bundler::FileUtils.touch('srcdir3/src1.txt')
686
+ # Bundler::FileUtils.mkdir('destdir3')
687
+ # Bundler::FileUtils.ln_s(['srcdir3/src0.txt', 'srcdir3/src1.txt'], 'destdir3')
688
+ # File.symlink?('destdir3/src0.txt') # => true
689
+ # File.symlink?('destdir3/src1.txt') # => true
690
+ #
691
+ # Keyword arguments:
692
+ #
693
+ # - <tt>force: true</tt> - overwrites +dest+ if it exists.
694
+ # - <tt>relative: false</tt> - create links relative to +dest+.
695
+ # - <tt>noop: true</tt> - does not create links.
696
+ # - <tt>verbose: true</tt> - prints an equivalent command:
697
+ #
698
+ # Bundler::FileUtils.ln_s('src0.txt', 'dest0.txt', noop: true, verbose: true)
699
+ # Bundler::FileUtils.ln_s('src1.txt', 'destdir1', noop: true, verbose: true)
700
+ # Bundler::FileUtils.ln_s('src2.txt', 'dest2.txt', force: true, noop: true, verbose: true)
701
+ # Bundler::FileUtils.ln_s(['srcdir3/src0.txt', 'srcdir3/src1.txt'], 'destdir3', noop: true, verbose: true)
702
+ #
703
+ # Output:
704
+ #
705
+ # ln -s src0.txt dest0.txt
706
+ # ln -s src1.txt destdir1
707
+ # ln -sf src2.txt dest2.txt
708
+ # ln -s srcdir3/src0.txt srcdir3/src1.txt destdir3
709
+ #
710
+ # Bundler::FileUtils.symlink is an alias for Bundler::FileUtils.ln_s.
711
+ #
712
+ # Related: Bundler::FileUtils.ln_sf.
713
+ #
714
+ def ln_s(src, dest, force: nil, relative: false, target_directory: true, noop: nil, verbose: nil)
715
+ if relative
716
+ return ln_sr(src, dest, force: force, noop: noop, verbose: verbose)
717
+ end
370
718
  fu_output_message "ln -s#{force ? 'f' : ''} #{[src,dest].flatten.join ' '}" if verbose
371
719
  return if noop
372
720
  fu_each_src_dest0(src, dest) do |s,d|
@@ -379,29 +727,95 @@ module Bundler::FileUtils
379
727
  alias symlink ln_s
380
728
  module_function :symlink
381
729
 
382
- #
383
- # :call-seq:
384
- # Bundler::FileUtils.ln_sf(*args)
385
- #
386
- # Same as
387
- #
388
- # Bundler::FileUtils.ln_s(*args, force: true)
730
+ # Like Bundler::FileUtils.ln_s, but always with keyword argument <tt>force: true</tt> given.
389
731
  #
390
732
  def ln_sf(src, dest, noop: nil, verbose: nil)
391
733
  ln_s src, dest, force: true, noop: noop, verbose: verbose
392
734
  end
393
735
  module_function :ln_sf
394
736
 
737
+ # Like Bundler::FileUtils.ln_s, but create links relative to +dest+.
738
+ #
739
+ def ln_sr(src, dest, target_directory: true, force: nil, noop: nil, verbose: nil)
740
+ options = "#{force ? 'f' : ''}#{target_directory ? '' : 'T'}"
741
+ dest = File.path(dest)
742
+ srcs = Array(src)
743
+ link = proc do |s, target_dir_p = true|
744
+ s = File.path(s)
745
+ if target_dir_p
746
+ d = File.join(destdirs = dest, File.basename(s))
747
+ else
748
+ destdirs = File.dirname(d = dest)
749
+ end
750
+ destdirs = fu_split_path(File.realpath(destdirs))
751
+ if fu_starting_path?(s)
752
+ srcdirs = fu_split_path((File.realdirpath(s) rescue File.expand_path(s)))
753
+ base = fu_relative_components_from(srcdirs, destdirs)
754
+ s = File.join(*base)
755
+ else
756
+ srcdirs = fu_clean_components(*fu_split_path(s))
757
+ base = fu_relative_components_from(fu_split_path(Dir.pwd), destdirs)
758
+ while srcdirs.first&. == ".." and base.last&.!=("..") and !fu_starting_path?(base.last)
759
+ srcdirs.shift
760
+ base.pop
761
+ end
762
+ s = File.join(*base, *srcdirs)
763
+ end
764
+ fu_output_message "ln -s#{options} #{s} #{d}" if verbose
765
+ next if noop
766
+ remove_file d, true if force
767
+ File.symlink s, d
768
+ end
769
+ case srcs.size
770
+ when 0
771
+ when 1
772
+ link[srcs[0], target_directory && File.directory?(dest)]
773
+ else
774
+ srcs.each(&link)
775
+ end
776
+ end
777
+ module_function :ln_sr
778
+
779
+ # Creates {hard links}[https://en.wikipedia.org/wiki/Hard_link]; returns +nil+.
395
780
  #
396
- # Hard links a file system entry +src+ to +dest+.
397
- # If +src+ is a directory, this method links its contents recursively.
781
+ # Arguments +src+ and +dest+
782
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
398
783
  #
399
- # Both of +src+ and +dest+ must be a path name.
400
- # +src+ must exist, +dest+ must not exist.
784
+ # If +src+ is the path to a file and +dest+ does not exist,
785
+ # creates a hard link at +dest+ pointing to +src+:
401
786
  #
402
- # If +dereference_root+ is true, this method dereferences the tree root.
787
+ # Bundler::FileUtils.touch('src0.txt')
788
+ # File.exist?('dest0.txt') # => false
789
+ # Bundler::FileUtils.link_entry('src0.txt', 'dest0.txt')
790
+ # File.file?('dest0.txt') # => true
403
791
  #
404
- # If +remove_destination+ is true, this method removes each destination file before copy.
792
+ # If +src+ is the path to a directory and +dest+ does not exist,
793
+ # recursively creates hard links at +dest+ pointing to paths in +src+:
794
+ #
795
+ # Bundler::FileUtils.mkdir_p(['src1/dir0', 'src1/dir1'])
796
+ # src_file_paths = [
797
+ # 'src1/dir0/t0.txt',
798
+ # 'src1/dir0/t1.txt',
799
+ # 'src1/dir1/t2.txt',
800
+ # 'src1/dir1/t3.txt',
801
+ # ]
802
+ # Bundler::FileUtils.touch(src_file_paths)
803
+ # File.directory?('dest1') # => true
804
+ # Bundler::FileUtils.link_entry('src1', 'dest1')
805
+ # File.file?('dest1/dir0/t0.txt') # => true
806
+ # File.file?('dest1/dir0/t1.txt') # => true
807
+ # File.file?('dest1/dir1/t2.txt') # => true
808
+ # File.file?('dest1/dir1/t3.txt') # => true
809
+ #
810
+ # Keyword arguments:
811
+ #
812
+ # - <tt>dereference_root: true</tt> - dereferences +src+ if it is a symbolic link.
813
+ # - <tt>remove_destination: true</tt> - removes +dest+ before creating links.
814
+ #
815
+ # Raises an exception if +dest+ is the path to an existing file or directory
816
+ # and keyword argument <tt>remove_destination: true</tt> is not given.
817
+ #
818
+ # Related: Bundler::FileUtils.ln (has different options).
405
819
  #
406
820
  def link_entry(src, dest, dereference_root = false, remove_destination = false)
407
821
  Entry_.new(src, nil, dereference_root).traverse do |ent|
@@ -412,16 +826,59 @@ module Bundler::FileUtils
412
826
  end
413
827
  module_function :link_entry
414
828
 
829
+ # Copies files.
830
+ #
831
+ # Arguments +src+ (a single path or an array of paths)
832
+ # and +dest+ (a single path)
833
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
834
+ #
835
+ # If +src+ is the path to a file and +dest+ is not the path to a directory,
836
+ # copies +src+ to +dest+:
837
+ #
838
+ # Bundler::FileUtils.touch('src0.txt')
839
+ # File.exist?('dest0.txt') # => false
840
+ # Bundler::FileUtils.cp('src0.txt', 'dest0.txt')
841
+ # File.file?('dest0.txt') # => true
415
842
  #
416
- # Copies a file content +src+ to +dest+. If +dest+ is a directory,
417
- # copies +src+ to +dest/src+.
843
+ # If +src+ is the path to a file and +dest+ is the path to a directory,
844
+ # copies +src+ to <tt>dest/src</tt>:
418
845
  #
419
- # If +src+ is a list of files, then +dest+ must be a directory.
846
+ # Bundler::FileUtils.touch('src1.txt')
847
+ # Bundler::FileUtils.mkdir('dest1')
848
+ # Bundler::FileUtils.cp('src1.txt', 'dest1')
849
+ # File.file?('dest1/src1.txt') # => true
420
850
  #
421
- # Bundler::FileUtils.cp 'eval.c', 'eval.c.org'
422
- # Bundler::FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6'
423
- # Bundler::FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6', verbose: true
424
- # Bundler::FileUtils.cp 'symlink', 'dest' # copy content, "dest" is not a symlink
851
+ # If +src+ is an array of paths to files and +dest+ is the path to a directory,
852
+ # copies from each +src+ to +dest+:
853
+ #
854
+ # src_file_paths = ['src2.txt', 'src2.dat']
855
+ # Bundler::FileUtils.touch(src_file_paths)
856
+ # Bundler::FileUtils.mkdir('dest2')
857
+ # Bundler::FileUtils.cp(src_file_paths, 'dest2')
858
+ # File.file?('dest2/src2.txt') # => true
859
+ # File.file?('dest2/src2.dat') # => true
860
+ #
861
+ # Keyword arguments:
862
+ #
863
+ # - <tt>preserve: true</tt> - preserves file times.
864
+ # - <tt>noop: true</tt> - does not copy files.
865
+ # - <tt>verbose: true</tt> - prints an equivalent command:
866
+ #
867
+ # Bundler::FileUtils.cp('src0.txt', 'dest0.txt', noop: true, verbose: true)
868
+ # Bundler::FileUtils.cp('src1.txt', 'dest1', noop: true, verbose: true)
869
+ # Bundler::FileUtils.cp(src_file_paths, 'dest2', noop: true, verbose: true)
870
+ #
871
+ # Output:
872
+ #
873
+ # cp src0.txt dest0.txt
874
+ # cp src1.txt dest1
875
+ # cp src2.txt src2.dat dest2
876
+ #
877
+ # Raises an exception if +src+ is a directory.
878
+ #
879
+ # Bundler::FileUtils.copy is an alias for Bundler::FileUtils.cp.
880
+ #
881
+ # Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
425
882
  #
426
883
  def cp(src, dest, preserve: nil, noop: nil, verbose: nil)
427
884
  fu_output_message "cp#{preserve ? ' -p' : ''} #{[src,dest].flatten.join ' '}" if verbose
@@ -435,30 +892,105 @@ module Bundler::FileUtils
435
892
  alias copy cp
436
893
  module_function :copy
437
894
 
438
- #
439
- # Copies +src+ to +dest+. If +src+ is a directory, this method copies
440
- # all its contents recursively. If +dest+ is a directory, copies
441
- # +src+ to +dest/src+.
442
- #
443
- # +src+ can be a list of files.
444
- #
445
- # If +dereference_root+ is true, this method dereference tree root.
446
- #
447
- # If +remove_destination+ is true, this method removes each destination file before copy.
448
- #
449
- # # Installing Ruby library "mylib" under the site_ruby
450
- # Bundler::FileUtils.rm_r site_ruby + '/mylib', force: true
451
- # Bundler::FileUtils.cp_r 'lib/', site_ruby + '/mylib'
452
- #
453
- # # Examples of copying several files to target directory.
454
- # Bundler::FileUtils.cp_r %w(mail.rb field.rb debug/), site_ruby + '/tmail'
455
- # Bundler::FileUtils.cp_r Dir.glob('*.rb'), '/home/foo/lib/ruby', noop: true, verbose: true
456
- #
457
- # # If you want to copy all contents of a directory instead of the
458
- # # directory itself, c.f. src/x -> dest/x, src/y -> dest/y,
459
- # # use following code.
460
- # Bundler::FileUtils.cp_r 'src/.', 'dest' # cp_r('src', 'dest') makes dest/src,
461
- # # but this doesn't.
895
+ # Recursively copies files.
896
+ #
897
+ # Arguments +src+ (a single path or an array of paths)
898
+ # and +dest+ (a single path)
899
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
900
+ #
901
+ # The mode, owner, and group are retained in the copy;
902
+ # to change those, use Bundler::FileUtils.install instead.
903
+ #
904
+ # If +src+ is the path to a file and +dest+ is not the path to a directory,
905
+ # copies +src+ to +dest+:
906
+ #
907
+ # Bundler::FileUtils.touch('src0.txt')
908
+ # File.exist?('dest0.txt') # => false
909
+ # Bundler::FileUtils.cp_r('src0.txt', 'dest0.txt')
910
+ # File.file?('dest0.txt') # => true
911
+ #
912
+ # If +src+ is the path to a file and +dest+ is the path to a directory,
913
+ # copies +src+ to <tt>dest/src</tt>:
914
+ #
915
+ # Bundler::FileUtils.touch('src1.txt')
916
+ # Bundler::FileUtils.mkdir('dest1')
917
+ # Bundler::FileUtils.cp_r('src1.txt', 'dest1')
918
+ # File.file?('dest1/src1.txt') # => true
919
+ #
920
+ # If +src+ is the path to a directory and +dest+ does not exist,
921
+ # recursively copies +src+ to +dest+:
922
+ #
923
+ # tree('src2')
924
+ # # => src2
925
+ # # |-- dir0
926
+ # # | |-- src0.txt
927
+ # # | `-- src1.txt
928
+ # # `-- dir1
929
+ # # |-- src2.txt
930
+ # # `-- src3.txt
931
+ # Bundler::FileUtils.exist?('dest2') # => false
932
+ # Bundler::FileUtils.cp_r('src2', 'dest2')
933
+ # tree('dest2')
934
+ # # => dest2
935
+ # # |-- dir0
936
+ # # | |-- src0.txt
937
+ # # | `-- src1.txt
938
+ # # `-- dir1
939
+ # # |-- src2.txt
940
+ # # `-- src3.txt
941
+ #
942
+ # If +src+ and +dest+ are paths to directories,
943
+ # recursively copies +src+ to <tt>dest/src</tt>:
944
+ #
945
+ # tree('src3')
946
+ # # => src3
947
+ # # |-- dir0
948
+ # # | |-- src0.txt
949
+ # # | `-- src1.txt
950
+ # # `-- dir1
951
+ # # |-- src2.txt
952
+ # # `-- src3.txt
953
+ # Bundler::FileUtils.mkdir('dest3')
954
+ # Bundler::FileUtils.cp_r('src3', 'dest3')
955
+ # tree('dest3')
956
+ # # => dest3
957
+ # # `-- src3
958
+ # # |-- dir0
959
+ # # | |-- src0.txt
960
+ # # | `-- src1.txt
961
+ # # `-- dir1
962
+ # # |-- src2.txt
963
+ # # `-- src3.txt
964
+ #
965
+ # If +src+ is an array of paths and +dest+ is a directory,
966
+ # recursively copies from each path in +src+ to +dest+;
967
+ # the paths in +src+ may point to files and/or directories.
968
+ #
969
+ # Keyword arguments:
970
+ #
971
+ # - <tt>dereference_root: false</tt> - if +src+ is a symbolic link,
972
+ # does not dereference it.
973
+ # - <tt>noop: true</tt> - does not copy files.
974
+ # - <tt>preserve: true</tt> - preserves file times.
975
+ # - <tt>remove_destination: true</tt> - removes +dest+ before copying files.
976
+ # - <tt>verbose: true</tt> - prints an equivalent command:
977
+ #
978
+ # Bundler::FileUtils.cp_r('src0.txt', 'dest0.txt', noop: true, verbose: true)
979
+ # Bundler::FileUtils.cp_r('src1.txt', 'dest1', noop: true, verbose: true)
980
+ # Bundler::FileUtils.cp_r('src2', 'dest2', noop: true, verbose: true)
981
+ # Bundler::FileUtils.cp_r('src3', 'dest3', noop: true, verbose: true)
982
+ #
983
+ # Output:
984
+ #
985
+ # cp -r src0.txt dest0.txt
986
+ # cp -r src1.txt dest1
987
+ # cp -r src2 dest2
988
+ # cp -r src3 dest3
989
+ #
990
+ # Raises an exception of +src+ is the path to a directory
991
+ # and +dest+ is the path to a file.
992
+ #
993
+ # Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
462
994
  #
463
995
  def cp_r(src, dest, preserve: nil, noop: nil, verbose: nil,
464
996
  dereference_root: true, remove_destination: nil)
@@ -470,21 +1002,50 @@ module Bundler::FileUtils
470
1002
  end
471
1003
  module_function :cp_r
472
1004
 
1005
+ # Recursively copies files from +src+ to +dest+.
1006
+ #
1007
+ # Arguments +src+ and +dest+
1008
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
473
1009
  #
474
- # Copies a file system entry +src+ to +dest+.
475
- # If +src+ is a directory, this method copies its contents recursively.
476
- # This method preserves file types, c.f. symlink, directory...
477
- # (FIFO, device files and etc. are not supported yet)
1010
+ # If +src+ is the path to a file, copies +src+ to +dest+:
478
1011
  #
479
- # Both of +src+ and +dest+ must be a path name.
480
- # +src+ must exist, +dest+ must not exist.
1012
+ # Bundler::FileUtils.touch('src0.txt')
1013
+ # File.exist?('dest0.txt') # => false
1014
+ # Bundler::FileUtils.copy_entry('src0.txt', 'dest0.txt')
1015
+ # File.file?('dest0.txt') # => true
481
1016
  #
482
- # If +preserve+ is true, this method preserves owner, group, and
483
- # modified time. Permissions are copied regardless +preserve+.
1017
+ # If +src+ is a directory, recursively copies +src+ to +dest+:
484
1018
  #
485
- # If +dereference_root+ is true, this method dereference tree root.
1019
+ # tree('src1')
1020
+ # # => src1
1021
+ # # |-- dir0
1022
+ # # | |-- src0.txt
1023
+ # # | `-- src1.txt
1024
+ # # `-- dir1
1025
+ # # |-- src2.txt
1026
+ # # `-- src3.txt
1027
+ # Bundler::FileUtils.copy_entry('src1', 'dest1')
1028
+ # tree('dest1')
1029
+ # # => dest1
1030
+ # # |-- dir0
1031
+ # # | |-- src0.txt
1032
+ # # | `-- src1.txt
1033
+ # # `-- dir1
1034
+ # # |-- src2.txt
1035
+ # # `-- src3.txt
486
1036
  #
487
- # If +remove_destination+ is true, this method removes each destination file before copy.
1037
+ # The recursive copying preserves file types for regular files,
1038
+ # directories, and symbolic links;
1039
+ # other file types (FIFO streams, device files, etc.) are not supported.
1040
+ #
1041
+ # Keyword arguments:
1042
+ #
1043
+ # - <tt>dereference_root: true</tt> - if +src+ is a symbolic link,
1044
+ # follows the link.
1045
+ # - <tt>preserve: true</tt> - preserves file times.
1046
+ # - <tt>remove_destination: true</tt> - removes +dest+ before copying files.
1047
+ #
1048
+ # Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
488
1049
  #
489
1050
  def copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = false)
490
1051
  if dereference_root
@@ -502,9 +1063,25 @@ module Bundler::FileUtils
502
1063
  end
503
1064
  module_function :copy_entry
504
1065
 
1066
+ # Copies file from +src+ to +dest+, which should not be directories.
1067
+ #
1068
+ # Arguments +src+ and +dest+
1069
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
1070
+ #
1071
+ # Examples:
505
1072
  #
506
- # Copies file contents of +src+ to +dest+.
507
- # Both of +src+ and +dest+ must be a path name.
1073
+ # Bundler::FileUtils.touch('src0.txt')
1074
+ # Bundler::FileUtils.copy_file('src0.txt', 'dest0.txt')
1075
+ # File.file?('dest0.txt') # => true
1076
+ #
1077
+ # Keyword arguments:
1078
+ #
1079
+ # - <tt>dereference: false</tt> - if +src+ is a symbolic link,
1080
+ # does not follow the link.
1081
+ # - <tt>preserve: true</tt> - preserves file times.
1082
+ # - <tt>remove_destination: true</tt> - removes +dest+ before copying files.
1083
+ #
1084
+ # Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
508
1085
  #
509
1086
  def copy_file(src, dest, preserve = false, dereference = true)
510
1087
  ent = Entry_.new(src, nil, dereference)
@@ -513,25 +1090,81 @@ module Bundler::FileUtils
513
1090
  end
514
1091
  module_function :copy_file
515
1092
 
1093
+ # Copies \IO stream +src+ to \IO stream +dest+ via
1094
+ # {IO.copy_stream}[https://docs.ruby-lang.org/en/master/IO.html#method-c-copy_stream].
516
1095
  #
517
- # Copies stream +src+ to +dest+.
518
- # +src+ must respond to #read(n) and
519
- # +dest+ must respond to #write(str).
1096
+ # Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
520
1097
  #
521
1098
  def copy_stream(src, dest)
522
1099
  IO.copy_stream(src, dest)
523
1100
  end
524
1101
  module_function :copy_stream
525
1102
 
526
- #
527
- # Moves file(s) +src+ to +dest+. If +file+ and +dest+ exist on the different
528
- # disk partition, the file is copied then the original file is removed.
529
- #
530
- # Bundler::FileUtils.mv 'badname.rb', 'goodname.rb'
531
- # Bundler::FileUtils.mv 'stuff.rb', '/notexist/lib/ruby', force: true # no error
532
- #
533
- # Bundler::FileUtils.mv %w(junk.txt dust.txt), '/home/foo/.trash/'
534
- # Bundler::FileUtils.mv Dir.glob('test*.rb'), 'test', noop: true, verbose: true
1103
+ # Moves entries.
1104
+ #
1105
+ # Arguments +src+ (a single path or an array of paths)
1106
+ # and +dest+ (a single path)
1107
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
1108
+ #
1109
+ # If +src+ and +dest+ are on different file systems,
1110
+ # first copies, then removes +src+.
1111
+ #
1112
+ # May cause a local vulnerability if not called with keyword argument
1113
+ # <tt>secure: true</tt>;
1114
+ # see {Avoiding the TOCTTOU Vulnerability}[rdoc-ref:FileUtils@Avoiding+the+TOCTTOU+Vulnerability].
1115
+ #
1116
+ # If +src+ is the path to a single file or directory and +dest+ does not exist,
1117
+ # moves +src+ to +dest+:
1118
+ #
1119
+ # tree('src0')
1120
+ # # => src0
1121
+ # # |-- src0.txt
1122
+ # # `-- src1.txt
1123
+ # File.exist?('dest0') # => false
1124
+ # Bundler::FileUtils.mv('src0', 'dest0')
1125
+ # File.exist?('src0') # => false
1126
+ # tree('dest0')
1127
+ # # => dest0
1128
+ # # |-- src0.txt
1129
+ # # `-- src1.txt
1130
+ #
1131
+ # If +src+ is an array of paths to files and directories
1132
+ # and +dest+ is the path to a directory,
1133
+ # copies from each path in the array to +dest+:
1134
+ #
1135
+ # File.file?('src1.txt') # => true
1136
+ # tree('src1')
1137
+ # # => src1
1138
+ # # |-- src.dat
1139
+ # # `-- src.txt
1140
+ # Dir.empty?('dest1') # => true
1141
+ # Bundler::FileUtils.mv(['src1.txt', 'src1'], 'dest1')
1142
+ # tree('dest1')
1143
+ # # => dest1
1144
+ # # |-- src1
1145
+ # # | |-- src.dat
1146
+ # # | `-- src.txt
1147
+ # # `-- src1.txt
1148
+ #
1149
+ # Keyword arguments:
1150
+ #
1151
+ # - <tt>force: true</tt> - if the move includes removing +src+
1152
+ # (that is, if +src+ and +dest+ are on different file systems),
1153
+ # ignores raised exceptions of StandardError and its descendants.
1154
+ # - <tt>noop: true</tt> - does not move files.
1155
+ # - <tt>secure: true</tt> - removes +src+ securely;
1156
+ # see details at Bundler::FileUtils.remove_entry_secure.
1157
+ # - <tt>verbose: true</tt> - prints an equivalent command:
1158
+ #
1159
+ # Bundler::FileUtils.mv('src0', 'dest0', noop: true, verbose: true)
1160
+ # Bundler::FileUtils.mv(['src1.txt', 'src1'], 'dest1', noop: true, verbose: true)
1161
+ #
1162
+ # Output:
1163
+ #
1164
+ # mv src0 dest0
1165
+ # mv src1.txt src1 dest1
1166
+ #
1167
+ # Bundler::FileUtils.move is an alias for Bundler::FileUtils.mv.
535
1168
  #
536
1169
  def mv(src, dest, force: nil, noop: nil, verbose: nil, secure: nil)
537
1170
  fu_output_message "mv#{force ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if verbose
@@ -565,13 +1198,34 @@ module Bundler::FileUtils
565
1198
  alias move mv
566
1199
  module_function :move
567
1200
 
1201
+ # Removes entries at the paths in the given +list+
1202
+ # (a single path or an array of paths)
1203
+ # returns +list+, if it is an array, <tt>[list]</tt> otherwise.
1204
+ #
1205
+ # Argument +list+ or its elements
1206
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
1207
+ #
1208
+ # With no keyword arguments, removes files at the paths given in +list+:
1209
+ #
1210
+ # Bundler::FileUtils.touch(['src0.txt', 'src0.dat'])
1211
+ # Bundler::FileUtils.rm(['src0.dat', 'src0.txt']) # => ["src0.dat", "src0.txt"]
1212
+ #
1213
+ # Keyword arguments:
1214
+ #
1215
+ # - <tt>force: true</tt> - ignores raised exceptions of StandardError
1216
+ # and its descendants.
1217
+ # - <tt>noop: true</tt> - does not remove files; returns +nil+.
1218
+ # - <tt>verbose: true</tt> - prints an equivalent command:
1219
+ #
1220
+ # Bundler::FileUtils.rm(['src0.dat', 'src0.txt'], noop: true, verbose: true)
568
1221
  #
569
- # Remove file(s) specified in +list+. This method cannot remove directories.
570
- # All StandardErrors are ignored when the :force option is set.
1222
+ # Output:
571
1223
  #
572
- # Bundler::FileUtils.rm %w( junk.txt dust.txt )
573
- # Bundler::FileUtils.rm Dir.glob('*.so')
574
- # Bundler::FileUtils.rm 'NotExistFile', force: true # never raises exception
1224
+ # rm src0.dat src0.txt
1225
+ #
1226
+ # Bundler::FileUtils.remove is an alias for Bundler::FileUtils.rm.
1227
+ #
1228
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
575
1229
  #
576
1230
  def rm(list, force: nil, noop: nil, verbose: nil)
577
1231
  list = fu_list(list)
@@ -587,10 +1241,18 @@ module Bundler::FileUtils
587
1241
  alias remove rm
588
1242
  module_function :remove
589
1243
 
1244
+ # Equivalent to:
1245
+ #
1246
+ # Bundler::FileUtils.rm(list, force: true, **kwargs)
1247
+ #
1248
+ # Argument +list+ (a single path or an array of paths)
1249
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
1250
+ #
1251
+ # See Bundler::FileUtils.rm for keyword arguments.
590
1252
  #
591
- # Equivalent to
1253
+ # Bundler::FileUtils.safe_unlink is an alias for Bundler::FileUtils.rm_f.
592
1254
  #
593
- # Bundler::FileUtils.rm(list, force: true)
1255
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
594
1256
  #
595
1257
  def rm_f(list, noop: nil, verbose: nil)
596
1258
  rm list, force: true, noop: noop, verbose: verbose
@@ -600,24 +1262,55 @@ module Bundler::FileUtils
600
1262
  alias safe_unlink rm_f
601
1263
  module_function :safe_unlink
602
1264
 
1265
+ # Removes entries at the paths in the given +list+
1266
+ # (a single path or an array of paths);
1267
+ # returns +list+, if it is an array, <tt>[list]</tt> otherwise.
1268
+ #
1269
+ # Argument +list+ or its elements
1270
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
1271
+ #
1272
+ # May cause a local vulnerability if not called with keyword argument
1273
+ # <tt>secure: true</tt>;
1274
+ # see {Avoiding the TOCTTOU Vulnerability}[rdoc-ref:FileUtils@Avoiding+the+TOCTTOU+Vulnerability].
1275
+ #
1276
+ # For each file path, removes the file at that path:
1277
+ #
1278
+ # Bundler::FileUtils.touch(['src0.txt', 'src0.dat'])
1279
+ # Bundler::FileUtils.rm_r(['src0.dat', 'src0.txt'])
1280
+ # File.exist?('src0.txt') # => false
1281
+ # File.exist?('src0.dat') # => false
603
1282
  #
604
- # remove files +list+[0] +list+[1]... If +list+[n] is a directory,
605
- # removes its all contents recursively. This method ignores
606
- # StandardError when :force option is set.
1283
+ # For each directory path, recursively removes files and directories:
607
1284
  #
608
- # Bundler::FileUtils.rm_r Dir.glob('/tmp/*')
609
- # Bundler::FileUtils.rm_r 'some_dir', force: true
1285
+ # tree('src1')
1286
+ # # => src1
1287
+ # # |-- dir0
1288
+ # # | |-- src0.txt
1289
+ # # | `-- src1.txt
1290
+ # # `-- dir1
1291
+ # # |-- src2.txt
1292
+ # # `-- src3.txt
1293
+ # Bundler::FileUtils.rm_r('src1')
1294
+ # File.exist?('src1') # => false
610
1295
  #
611
- # WARNING: This method causes local vulnerability
612
- # if one of parent directories or removing directory tree are world
613
- # writable (including /tmp, whose permission is 1777), and the current
614
- # process has strong privilege such as Unix super user (root), and the
615
- # system has symbolic link. For secure removing, read the documentation
616
- # of remove_entry_secure carefully, and set :secure option to true.
617
- # Default is <tt>secure: false</tt>.
1296
+ # Keyword arguments:
618
1297
  #
619
- # NOTE: This method calls remove_entry_secure if :secure option is set.
620
- # See also remove_entry_secure.
1298
+ # - <tt>force: true</tt> - ignores raised exceptions of StandardError
1299
+ # and its descendants.
1300
+ # - <tt>noop: true</tt> - does not remove entries; returns +nil+.
1301
+ # - <tt>secure: true</tt> - removes +src+ securely;
1302
+ # see details at Bundler::FileUtils.remove_entry_secure.
1303
+ # - <tt>verbose: true</tt> - prints an equivalent command:
1304
+ #
1305
+ # Bundler::FileUtils.rm_r(['src0.dat', 'src0.txt'], noop: true, verbose: true)
1306
+ # Bundler::FileUtils.rm_r('src1', noop: true, verbose: true)
1307
+ #
1308
+ # Output:
1309
+ #
1310
+ # rm -r src0.dat src0.txt
1311
+ # rm -r src1
1312
+ #
1313
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
621
1314
  #
622
1315
  def rm_r(list, force: nil, noop: nil, verbose: nil, secure: nil)
623
1316
  list = fu_list(list)
@@ -633,13 +1326,22 @@ module Bundler::FileUtils
633
1326
  end
634
1327
  module_function :rm_r
635
1328
 
1329
+ # Equivalent to:
636
1330
  #
637
- # Equivalent to
1331
+ # Bundler::FileUtils.rm_r(list, force: true, **kwargs)
638
1332
  #
639
- # Bundler::FileUtils.rm_r(list, force: true)
1333
+ # Argument +list+ or its elements
1334
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
640
1335
  #
641
- # WARNING: This method causes local vulnerability.
642
- # Read the documentation of rm_r first.
1336
+ # May cause a local vulnerability if not called with keyword argument
1337
+ # <tt>secure: true</tt>;
1338
+ # see {Avoiding the TOCTTOU Vulnerability}[rdoc-ref:FileUtils@Avoiding+the+TOCTTOU+Vulnerability].
1339
+ #
1340
+ # See Bundler::FileUtils.rm_r for keyword arguments.
1341
+ #
1342
+ # Bundler::FileUtils.rmtree is an alias for Bundler::FileUtils.rm_rf.
1343
+ #
1344
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
643
1345
  #
644
1346
  def rm_rf(list, noop: nil, verbose: nil, secure: nil)
645
1347
  rm_r list, force: true, noop: noop, verbose: verbose, secure: secure
@@ -649,37 +1351,20 @@ module Bundler::FileUtils
649
1351
  alias rmtree rm_rf
650
1352
  module_function :rmtree
651
1353
 
1354
+ # Securely removes the entry given by +path+,
1355
+ # which should be the entry for a regular file, a symbolic link,
1356
+ # or a directory.
652
1357
  #
653
- # This method removes a file system entry +path+. +path+ shall be a
654
- # regular file, a directory, or something. If +path+ is a directory,
655
- # remove it recursively. This method is required to avoid TOCTTOU
656
- # (time-of-check-to-time-of-use) local security vulnerability of rm_r.
657
- # #rm_r causes security hole when:
658
- #
659
- # * Parent directory is world writable (including /tmp).
660
- # * Removing directory tree includes world writable directory.
661
- # * The system has symbolic link.
1358
+ # Argument +path+
1359
+ # should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
662
1360
  #
663
- # To avoid this security hole, this method applies special preprocess.
664
- # If +path+ is a directory, this method chown(2) and chmod(2) all
665
- # removing directories. This requires the current process is the
666
- # owner of the removing whole directory tree, or is the super user (root).
1361
+ # Avoids a local vulnerability that can exist in certain circumstances;
1362
+ # see {Avoiding the TOCTTOU Vulnerability}[rdoc-ref:FileUtils@Avoiding+the+TOCTTOU+Vulnerability].
667
1363
  #
668
- # WARNING: You must ensure that *ALL* parent directories cannot be
669
- # moved by other untrusted users. For example, parent directories
670
- # should not be owned by untrusted users, and should not be world
671
- # writable except when the sticky bit set.
1364
+ # Optional argument +force+ specifies whether to ignore
1365
+ # raised exceptions of StandardError and its descendants.
672
1366
  #
673
- # WARNING: Only the owner of the removing directory tree, or Unix super
674
- # user (root) should invoke this method. Otherwise this method does not
675
- # work.
676
- #
677
- # For details of this security vulnerability, see Perl's case:
678
- #
679
- # * https://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2005-0448
680
- # * https://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2004-0452
681
- #
682
- # For fileutils.rb, this vulnerability is reported in [ruby-dev:26100].
1367
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
683
1368
  #
684
1369
  def remove_entry_secure(path, force = false)
685
1370
  unless fu_have_symlink?
@@ -767,12 +1452,17 @@ module Bundler::FileUtils
767
1452
  end
768
1453
  private_module_function :fu_stat_identical_entry?
769
1454
 
1455
+ # Removes the entry given by +path+,
1456
+ # which should be the entry for a regular file, a symbolic link,
1457
+ # or a directory.
770
1458
  #
771
- # This method removes a file system entry +path+.
772
- # +path+ might be a regular file, a directory, or something.
773
- # If +path+ is a directory, remove it recursively.
1459
+ # Argument +path+
1460
+ # should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
774
1461
  #
775
- # See also remove_entry_secure.
1462
+ # Optional argument +force+ specifies whether to ignore
1463
+ # raised exceptions of StandardError and its descendants.
1464
+ #
1465
+ # Related: Bundler::FileUtils.remove_entry_secure.
776
1466
  #
777
1467
  def remove_entry(path, force = false)
778
1468
  Entry_.new(path).postorder_traverse do |ent|
@@ -787,9 +1477,16 @@ module Bundler::FileUtils
787
1477
  end
788
1478
  module_function :remove_entry
789
1479
 
1480
+ # Removes the file entry given by +path+,
1481
+ # which should be the entry for a regular file or a symbolic link.
1482
+ #
1483
+ # Argument +path+
1484
+ # should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
790
1485
  #
791
- # Removes a file +path+.
792
- # This method ignores StandardError if +force+ is true.
1486
+ # Optional argument +force+ specifies whether to ignore
1487
+ # raised exceptions of StandardError and its descendants.
1488
+ #
1489
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
793
1490
  #
794
1491
  def remove_file(path, force = false)
795
1492
  Entry_.new(path).remove_file
@@ -798,20 +1495,32 @@ module Bundler::FileUtils
798
1495
  end
799
1496
  module_function :remove_file
800
1497
 
1498
+ # Recursively removes the directory entry given by +path+,
1499
+ # which should be the entry for a regular file, a symbolic link,
1500
+ # or a directory.
1501
+ #
1502
+ # Argument +path+
1503
+ # should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
801
1504
  #
802
- # Removes a directory +dir+ and its contents recursively.
803
- # This method ignores StandardError if +force+ is true.
1505
+ # Optional argument +force+ specifies whether to ignore
1506
+ # raised exceptions of StandardError and its descendants.
1507
+ #
1508
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
804
1509
  #
805
1510
  def remove_dir(path, force = false)
806
1511
  remove_entry path, force # FIXME?? check if it is a directory
807
1512
  end
808
1513
  module_function :remove_dir
809
1514
 
1515
+ # Returns +true+ if the contents of files +a+ and +b+ are identical,
1516
+ # +false+ otherwise.
1517
+ #
1518
+ # Arguments +a+ and +b+
1519
+ # should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
810
1520
  #
811
- # Returns true if the contents of a file +a+ and a file +b+ are identical.
1521
+ # Bundler::FileUtils.identical? and Bundler::FileUtils.cmp are aliases for Bundler::FileUtils.compare_file.
812
1522
  #
813
- # Bundler::FileUtils.compare_file('somefile', 'somefile') #=> true
814
- # Bundler::FileUtils.compare_file('/dev/null', '/dev/urandom') #=> false
1523
+ # Related: Bundler::FileUtils.compare_stream.
815
1524
  #
816
1525
  def compare_file(a, b)
817
1526
  return false unless File.size(a) == File.size(b)
@@ -828,19 +1537,19 @@ module Bundler::FileUtils
828
1537
  module_function :identical?
829
1538
  module_function :cmp
830
1539
 
1540
+ # Returns +true+ if the contents of streams +a+ and +b+ are identical,
1541
+ # +false+ otherwise.
831
1542
  #
832
- # Returns true if the contents of a stream +a+ and +b+ are identical.
1543
+ # Arguments +a+ and +b+
1544
+ # should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
1545
+ #
1546
+ # Related: Bundler::FileUtils.compare_file.
833
1547
  #
834
1548
  def compare_stream(a, b)
835
1549
  bsize = fu_stream_blksize(a, b)
836
1550
 
837
- if RUBY_VERSION > "2.4"
838
- sa = String.new(capacity: bsize)
839
- sb = String.new(capacity: bsize)
840
- else
841
- sa = String.new
842
- sb = String.new
843
- end
1551
+ sa = String.new(capacity: bsize)
1552
+ sb = String.new(capacity: bsize)
844
1553
 
845
1554
  begin
846
1555
  a.read(bsize, sa)
@@ -851,13 +1560,69 @@ module Bundler::FileUtils
851
1560
  end
852
1561
  module_function :compare_stream
853
1562
 
1563
+ # Copies a file entry.
1564
+ # See {install(1)}[https://man7.org/linux/man-pages/man1/install.1.html].
1565
+ #
1566
+ # Arguments +src+ (a single path or an array of paths)
1567
+ # and +dest+ (a single path)
1568
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments];
1569
+ #
1570
+ # If the entry at +dest+ does not exist, copies from +src+ to +dest+:
1571
+ #
1572
+ # File.read('src0.txt') # => "aaa\n"
1573
+ # File.exist?('dest0.txt') # => false
1574
+ # Bundler::FileUtils.install('src0.txt', 'dest0.txt')
1575
+ # File.read('dest0.txt') # => "aaa\n"
1576
+ #
1577
+ # If +dest+ is a file entry, copies from +src+ to +dest+, overwriting:
1578
+ #
1579
+ # File.read('src1.txt') # => "aaa\n"
1580
+ # File.read('dest1.txt') # => "bbb\n"
1581
+ # Bundler::FileUtils.install('src1.txt', 'dest1.txt')
1582
+ # File.read('dest1.txt') # => "aaa\n"
854
1583
  #
855
- # If +src+ is not same as +dest+, copies it and changes the permission
856
- # mode to +mode+. If +dest+ is a directory, destination is +dest+/+src+.
857
- # This method removes destination before copy.
1584
+ # If +dest+ is a directory entry, copies from +src+ to <tt>dest/src</tt>,
1585
+ # overwriting if necessary:
858
1586
  #
859
- # Bundler::FileUtils.install 'ruby', '/usr/local/bin/ruby', mode: 0755, verbose: true
860
- # Bundler::FileUtils.install 'lib.rb', '/usr/local/lib/ruby/site_ruby', verbose: true
1587
+ # File.read('src2.txt') # => "aaa\n"
1588
+ # File.read('dest2/src2.txt') # => "bbb\n"
1589
+ # Bundler::FileUtils.install('src2.txt', 'dest2')
1590
+ # File.read('dest2/src2.txt') # => "aaa\n"
1591
+ #
1592
+ # If +src+ is an array of paths and +dest+ points to a directory,
1593
+ # copies each path +path+ in +src+ to <tt>dest/path</tt>:
1594
+ #
1595
+ # File.file?('src3.txt') # => true
1596
+ # File.file?('src3.dat') # => true
1597
+ # Bundler::FileUtils.mkdir('dest3')
1598
+ # Bundler::FileUtils.install(['src3.txt', 'src3.dat'], 'dest3')
1599
+ # File.file?('dest3/src3.txt') # => true
1600
+ # File.file?('dest3/src3.dat') # => true
1601
+ #
1602
+ # Keyword arguments:
1603
+ #
1604
+ # - <tt>group: <i>group</i></tt> - changes the group if not +nil+,
1605
+ # using {File.chown}[https://docs.ruby-lang.org/en/master/File.html#method-c-chown].
1606
+ # - <tt>mode: <i>permissions</i></tt> - changes the permissions.
1607
+ # using {File.chmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-chmod].
1608
+ # - <tt>noop: true</tt> - does not copy entries; returns +nil+.
1609
+ # - <tt>owner: <i>owner</i></tt> - changes the owner if not +nil+,
1610
+ # using {File.chown}[https://docs.ruby-lang.org/en/master/File.html#method-c-chown].
1611
+ # - <tt>preserve: true</tt> - preserve timestamps
1612
+ # using {File.utime}[https://docs.ruby-lang.org/en/master/File.html#method-c-utime].
1613
+ # - <tt>verbose: true</tt> - prints an equivalent command:
1614
+ #
1615
+ # Bundler::FileUtils.install('src0.txt', 'dest0.txt', noop: true, verbose: true)
1616
+ # Bundler::FileUtils.install('src1.txt', 'dest1.txt', noop: true, verbose: true)
1617
+ # Bundler::FileUtils.install('src2.txt', 'dest2', noop: true, verbose: true)
1618
+ #
1619
+ # Output:
1620
+ #
1621
+ # install -c src0.txt dest0.txt
1622
+ # install -c src1.txt dest1.txt
1623
+ # install -c src2.txt dest2
1624
+ #
1625
+ # Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
861
1626
  #
862
1627
  def install(src, dest, mode: nil, owner: nil, group: nil, preserve: nil,
863
1628
  noop: nil, verbose: nil)
@@ -917,11 +1682,8 @@ module Bundler::FileUtils
917
1682
  private_module_function :apply_mask
918
1683
 
919
1684
  def symbolic_modes_to_i(mode_sym, path) #:nodoc:
920
- mode = if File::Stat === path
921
- path.mode
922
- else
923
- File.stat(path).mode
924
- end
1685
+ path = File.stat(path) unless File::Stat === path
1686
+ mode = path.mode
925
1687
  mode_sym.split(/,/).inject(mode & 07777) do |current_mode, clause|
926
1688
  target, *actions = clause.split(/([=+-])/)
927
1689
  raise ArgumentError, "invalid file mode: #{mode_sym}" if actions.empty?
@@ -938,7 +1700,7 @@ module Bundler::FileUtils
938
1700
  when "x"
939
1701
  mask | 0111
940
1702
  when "X"
941
- if FileTest.directory? path
1703
+ if path.directory?
942
1704
  mask | 0111
943
1705
  else
944
1706
  mask
@@ -978,37 +1740,78 @@ module Bundler::FileUtils
978
1740
  end
979
1741
  private_module_function :mode_to_s
980
1742
 
1743
+ # Changes permissions on the entries at the paths given in +list+
1744
+ # (a single path or an array of paths)
1745
+ # to the permissions given by +mode+;
1746
+ # returns +list+ if it is an array, <tt>[list]</tt> otherwise:
1747
+ #
1748
+ # - Modifies each entry that is a regular file using
1749
+ # {File.chmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-chmod].
1750
+ # - Modifies each entry that is a symbolic link using
1751
+ # {File.lchmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-lchmod].
1752
+ #
1753
+ # Argument +list+ or its elements
1754
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
1755
+ #
1756
+ # Argument +mode+ may be either an integer or a string:
1757
+ #
1758
+ # - \Integer +mode+: represents the permission bits to be set:
1759
+ #
1760
+ # Bundler::FileUtils.chmod(0755, 'src0.txt')
1761
+ # Bundler::FileUtils.chmod(0644, ['src0.txt', 'src0.dat'])
1762
+ #
1763
+ # - \String +mode+: represents the permissions to be set:
1764
+ #
1765
+ # The string is of the form <tt>[targets][[operator][perms[,perms]]</tt>, where:
1766
+ #
1767
+ # - +targets+ may be any combination of these letters:
1768
+ #
1769
+ # - <tt>'u'</tt>: permissions apply to the file's owner.
1770
+ # - <tt>'g'</tt>: permissions apply to users in the file's group.
1771
+ # - <tt>'o'</tt>: permissions apply to other users not in the file's group.
1772
+ # - <tt>'a'</tt> (the default): permissions apply to all users.
1773
+ #
1774
+ # - +operator+ may be one of these letters:
1775
+ #
1776
+ # - <tt>'+'</tt>: adds permissions.
1777
+ # - <tt>'-'</tt>: removes permissions.
1778
+ # - <tt>'='</tt>: sets (replaces) permissions.
1779
+ #
1780
+ # - +perms+ (may be repeated, with separating commas)
1781
+ # may be any combination of these letters:
1782
+ #
1783
+ # - <tt>'r'</tt>: Read.
1784
+ # - <tt>'w'</tt>: Write.
1785
+ # - <tt>'x'</tt>: Execute (search, for a directory).
1786
+ # - <tt>'X'</tt>: Search (for a directories only;
1787
+ # must be used with <tt>'+'</tt>)
1788
+ # - <tt>'s'</tt>: Uid or gid.
1789
+ # - <tt>'t'</tt>: Sticky bit.
1790
+ #
1791
+ # Examples:
1792
+ #
1793
+ # Bundler::FileUtils.chmod('u=wrx,go=rx', 'src1.txt')
1794
+ # Bundler::FileUtils.chmod('u=wrx,go=rx', '/usr/bin/ruby')
1795
+ #
1796
+ # Keyword arguments:
1797
+ #
1798
+ # - <tt>noop: true</tt> - does not change permissions; returns +nil+.
1799
+ # - <tt>verbose: true</tt> - prints an equivalent command:
1800
+ #
1801
+ # Bundler::FileUtils.chmod(0755, 'src0.txt', noop: true, verbose: true)
1802
+ # Bundler::FileUtils.chmod(0644, ['src0.txt', 'src0.dat'], noop: true, verbose: true)
1803
+ # Bundler::FileUtils.chmod('u=wrx,go=rx', 'src1.txt', noop: true, verbose: true)
1804
+ # Bundler::FileUtils.chmod('u=wrx,go=rx', '/usr/bin/ruby', noop: true, verbose: true)
1805
+ #
1806
+ # Output:
1807
+ #
1808
+ # chmod 755 src0.txt
1809
+ # chmod 644 src0.txt src0.dat
1810
+ # chmod u=wrx,go=rx src1.txt
1811
+ # chmod u=wrx,go=rx /usr/bin/ruby
1812
+ #
1813
+ # Related: Bundler::FileUtils.chmod_R.
981
1814
  #
982
- # Changes permission bits on the named files (in +list+) to the bit pattern
983
- # represented by +mode+.
984
- #
985
- # +mode+ is the symbolic and absolute mode can be used.
986
- #
987
- # Absolute mode is
988
- # Bundler::FileUtils.chmod 0755, 'somecommand'
989
- # Bundler::FileUtils.chmod 0644, %w(my.rb your.rb his.rb her.rb)
990
- # Bundler::FileUtils.chmod 0755, '/usr/bin/ruby', verbose: true
991
- #
992
- # Symbolic mode is
993
- # Bundler::FileUtils.chmod "u=wrx,go=rx", 'somecommand'
994
- # Bundler::FileUtils.chmod "u=wr,go=rr", %w(my.rb your.rb his.rb her.rb)
995
- # Bundler::FileUtils.chmod "u=wrx,go=rx", '/usr/bin/ruby', verbose: true
996
- #
997
- # "a" :: is user, group, other mask.
998
- # "u" :: is user's mask.
999
- # "g" :: is group's mask.
1000
- # "o" :: is other's mask.
1001
- # "w" :: is write permission.
1002
- # "r" :: is read permission.
1003
- # "x" :: is execute permission.
1004
- # "X" ::
1005
- # is execute permission for directories only, must be used in conjunction with "+"
1006
- # "s" :: is uid, gid.
1007
- # "t" :: is sticky bit.
1008
- # "+" :: is added to a class given the specified mode.
1009
- # "-" :: Is removed from a given class given mode.
1010
- # "=" :: Is the exact nature of the class will be given a specified mode.
1011
-
1012
1815
  def chmod(mode, list, noop: nil, verbose: nil)
1013
1816
  list = fu_list(list)
1014
1817
  fu_output_message sprintf('chmod %s %s', mode_to_s(mode), list.join(' ')) if verbose
@@ -1019,12 +1822,7 @@ module Bundler::FileUtils
1019
1822
  end
1020
1823
  module_function :chmod
1021
1824
 
1022
- #
1023
- # Changes permission bits on the named files (in +list+)
1024
- # to the bit pattern represented by +mode+.
1025
- #
1026
- # Bundler::FileUtils.chmod_R 0700, "/tmp/app.#{$$}"
1027
- # Bundler::FileUtils.chmod_R "u=wrx", "/tmp/app.#{$$}"
1825
+ # Like Bundler::FileUtils.chmod, but changes permissions recursively.
1028
1826
  #
1029
1827
  def chmod_R(mode, list, noop: nil, verbose: nil, force: nil)
1030
1828
  list = fu_list(list)
@@ -1044,15 +1842,68 @@ module Bundler::FileUtils
1044
1842
  end
1045
1843
  module_function :chmod_R
1046
1844
 
1845
+ # Changes the owner and group on the entries at the paths given in +list+
1846
+ # (a single path or an array of paths)
1847
+ # to the given +user+ and +group+;
1848
+ # returns +list+ if it is an array, <tt>[list]</tt> otherwise:
1849
+ #
1850
+ # - Modifies each entry that is a regular file using
1851
+ # {File.chown}[https://docs.ruby-lang.org/en/master/File.html#method-c-chown].
1852
+ # - Modifies each entry that is a symbolic link using
1853
+ # {File.lchown}[https://docs.ruby-lang.org/en/master/File.html#method-c-lchown].
1854
+ #
1855
+ # Argument +list+ or its elements
1856
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
1047
1857
  #
1048
- # Changes owner and group on the named files (in +list+)
1049
- # to the user +user+ and the group +group+. +user+ and +group+
1050
- # may be an ID (Integer/String) or a name (String).
1051
- # If +user+ or +group+ is nil, this method does not change
1052
- # the attribute.
1858
+ # User and group:
1053
1859
  #
1054
- # Bundler::FileUtils.chown 'root', 'staff', '/usr/local/bin/ruby'
1055
- # Bundler::FileUtils.chown nil, 'bin', Dir.glob('/usr/bin/*'), verbose: true
1860
+ # - Argument +user+ may be a user name or a user id;
1861
+ # if +nil+ or +-1+, the user is not changed.
1862
+ # - Argument +group+ may be a group name or a group id;
1863
+ # if +nil+ or +-1+, the group is not changed.
1864
+ # - The user must be a member of the group.
1865
+ #
1866
+ # Examples:
1867
+ #
1868
+ # # One path.
1869
+ # # User and group as string names.
1870
+ # File.stat('src0.txt').uid # => 1004
1871
+ # File.stat('src0.txt').gid # => 1004
1872
+ # Bundler::FileUtils.chown('user2', 'group1', 'src0.txt')
1873
+ # File.stat('src0.txt').uid # => 1006
1874
+ # File.stat('src0.txt').gid # => 1005
1875
+ #
1876
+ # # User and group as uid and gid.
1877
+ # Bundler::FileUtils.chown(1004, 1004, 'src0.txt')
1878
+ # File.stat('src0.txt').uid # => 1004
1879
+ # File.stat('src0.txt').gid # => 1004
1880
+ #
1881
+ # # Array of paths.
1882
+ # Bundler::FileUtils.chown(1006, 1005, ['src0.txt', 'src0.dat'])
1883
+ #
1884
+ # # Directory (not recursive).
1885
+ # Bundler::FileUtils.chown('user2', 'group1', '.')
1886
+ #
1887
+ # Keyword arguments:
1888
+ #
1889
+ # - <tt>noop: true</tt> - does not change permissions; returns +nil+.
1890
+ # - <tt>verbose: true</tt> - prints an equivalent command:
1891
+ #
1892
+ # Bundler::FileUtils.chown('user2', 'group1', 'src0.txt', noop: true, verbose: true)
1893
+ # Bundler::FileUtils.chown(1004, 1004, 'src0.txt', noop: true, verbose: true)
1894
+ # Bundler::FileUtils.chown(1006, 1005, ['src0.txt', 'src0.dat'], noop: true, verbose: true)
1895
+ # Bundler::FileUtils.chown('user2', 'group1', path, noop: true, verbose: true)
1896
+ # Bundler::FileUtils.chown('user2', 'group1', '.', noop: true, verbose: true)
1897
+ #
1898
+ # Output:
1899
+ #
1900
+ # chown user2:group1 src0.txt
1901
+ # chown 1004:1004 src0.txt
1902
+ # chown 1006:1005 src0.txt src0.dat
1903
+ # chown user2:group1 src0.txt
1904
+ # chown user2:group1 .
1905
+ #
1906
+ # Related: Bundler::FileUtils.chown_R.
1056
1907
  #
1057
1908
  def chown(user, group, list, noop: nil, verbose: nil)
1058
1909
  list = fu_list(list)
@@ -1068,15 +1919,7 @@ module Bundler::FileUtils
1068
1919
  end
1069
1920
  module_function :chown
1070
1921
 
1071
- #
1072
- # Changes owner and group on the named files (in +list+)
1073
- # to the user +user+ and the group +group+ recursively.
1074
- # +user+ and +group+ may be an ID (Integer/String) or
1075
- # a name (String). If +user+ or +group+ is nil, this
1076
- # method does not change the attribute.
1077
- #
1078
- # Bundler::FileUtils.chown_R 'www', 'www', '/var/www/htdocs'
1079
- # Bundler::FileUtils.chown_R 'cvs', 'cvs', '/var/cvs', verbose: true
1922
+ # Like Bundler::FileUtils.chown, but changes owner and group recursively.
1080
1923
  #
1081
1924
  def chown_R(user, group, list, noop: nil, verbose: nil, force: nil)
1082
1925
  list = fu_list(list)
@@ -1127,12 +1970,50 @@ module Bundler::FileUtils
1127
1970
  end
1128
1971
  private_module_function :fu_get_gid
1129
1972
 
1973
+ # Updates modification times (mtime) and access times (atime)
1974
+ # of the entries given by the paths in +list+
1975
+ # (a single path or an array of paths);
1976
+ # returns +list+ if it is an array, <tt>[list]</tt> otherwise.
1977
+ #
1978
+ # By default, creates an empty file for any path to a non-existent entry;
1979
+ # use keyword argument +nocreate+ to raise an exception instead.
1980
+ #
1981
+ # Argument +list+ or its elements
1982
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
1983
+ #
1984
+ # Examples:
1985
+ #
1986
+ # # Single path.
1987
+ # f = File.new('src0.txt') # Existing file.
1988
+ # f.atime # => 2022-06-10 11:11:21.200277 -0700
1989
+ # f.mtime # => 2022-06-10 11:11:21.200277 -0700
1990
+ # Bundler::FileUtils.touch('src0.txt')
1991
+ # f = File.new('src0.txt')
1992
+ # f.atime # => 2022-06-11 08:28:09.8185343 -0700
1993
+ # f.mtime # => 2022-06-11 08:28:09.8185343 -0700
1130
1994
  #
1131
- # Updates modification time (mtime) and access time (atime) of file(s) in
1132
- # +list+. Files are created if they don't exist.
1995
+ # # Array of paths.
1996
+ # Bundler::FileUtils.touch(['src0.txt', 'src0.dat'])
1133
1997
  #
1134
- # Bundler::FileUtils.touch 'timestamp'
1135
- # Bundler::FileUtils.touch Dir.glob('*.c'); system 'make'
1998
+ # Keyword arguments:
1999
+ #
2000
+ # - <tt>mtime: <i>time</i></tt> - sets the entry's mtime to the given time,
2001
+ # instead of the current time.
2002
+ # - <tt>nocreate: true</tt> - raises an exception if the entry does not exist.
2003
+ # - <tt>noop: true</tt> - does not touch entries; returns +nil+.
2004
+ # - <tt>verbose: true</tt> - prints an equivalent command:
2005
+ #
2006
+ # Bundler::FileUtils.touch('src0.txt', noop: true, verbose: true)
2007
+ # Bundler::FileUtils.touch(['src0.txt', 'src0.dat'], noop: true, verbose: true)
2008
+ # Bundler::FileUtils.touch(path, noop: true, verbose: true)
2009
+ #
2010
+ # Output:
2011
+ #
2012
+ # touch src0.txt
2013
+ # touch src0.txt src0.dat
2014
+ # touch src0.txt
2015
+ #
2016
+ # Related: Bundler::FileUtils.uptodate?.
1136
2017
  #
1137
2018
  def touch(list, noop: nil, verbose: nil, mtime: nil, nocreate: nil)
1138
2019
  list = fu_list(list)
@@ -1290,14 +2171,9 @@ module Bundler::FileUtils
1290
2171
 
1291
2172
  def entries
1292
2173
  opts = {}
1293
- opts[:encoding] = ::Encoding::UTF_8 if fu_windows?
2174
+ opts[:encoding] = fu_windows? ? ::Encoding::UTF_8 : path.encoding
1294
2175
 
1295
- files = if Dir.respond_to?(:children)
1296
- Dir.children(path, **opts)
1297
- else
1298
- Dir.entries(path(), **opts)
1299
- .reject {|n| n == '.' or n == '..' }
1300
- end
2176
+ files = Dir.children(path, **opts)
1301
2177
 
1302
2178
  untaint = RUBY_VERSION < '2.7'
1303
2179
  files.map {|n| Entry_.new(prefix(), join(rel(), untaint ? n.untaint : n)) }
@@ -1345,6 +2221,7 @@ module Bundler::FileUtils
1345
2221
  else
1346
2222
  File.chmod mode, path()
1347
2223
  end
2224
+ rescue Errno::EOPNOTSUPP
1348
2225
  end
1349
2226
 
1350
2227
  def chown(uid, gid)
@@ -1439,7 +2316,7 @@ module Bundler::FileUtils
1439
2316
  if st.symlink?
1440
2317
  begin
1441
2318
  File.lchmod mode, path
1442
- rescue NotImplementedError
2319
+ rescue NotImplementedError, Errno::EOPNOTSUPP
1443
2320
  end
1444
2321
  else
1445
2322
  File.chmod mode, path
@@ -1498,13 +2375,21 @@ module Bundler::FileUtils
1498
2375
 
1499
2376
  def postorder_traverse
1500
2377
  if directory?
1501
- entries().each do |ent|
2378
+ begin
2379
+ children = entries()
2380
+ rescue Errno::EACCES
2381
+ # Failed to get the list of children.
2382
+ # Assuming there is no children, try to process the parent directory.
2383
+ yield self
2384
+ return
2385
+ end
2386
+
2387
+ children.each do |ent|
1502
2388
  ent.postorder_traverse do |e|
1503
2389
  yield e
1504
2390
  end
1505
2391
  end
1506
2392
  end
1507
- ensure
1508
2393
  yield self
1509
2394
  end
1510
2395
 
@@ -1559,7 +2444,15 @@ module Bundler::FileUtils
1559
2444
  def join(dir, base)
1560
2445
  return File.path(dir) if not base or base == '.'
1561
2446
  return File.path(base) if not dir or dir == '.'
1562
- File.join(dir, base)
2447
+ begin
2448
+ File.join(dir, base)
2449
+ rescue EncodingError
2450
+ if fu_windows?
2451
+ File.join(dir.encode(::Encoding::UTF_8), base.encode(::Encoding::UTF_8))
2452
+ else
2453
+ raise
2454
+ end
2455
+ end
1563
2456
  end
1564
2457
 
1565
2458
  if File::ALT_SEPARATOR
@@ -1590,15 +2483,15 @@ module Bundler::FileUtils
1590
2483
  end
1591
2484
  private_module_function :fu_each_src_dest
1592
2485
 
1593
- def fu_each_src_dest0(src, dest) #:nodoc:
2486
+ def fu_each_src_dest0(src, dest, target_directory = true) #:nodoc:
1594
2487
  if tmp = Array.try_convert(src)
1595
2488
  tmp.each do |s|
1596
2489
  s = File.path(s)
1597
- yield s, File.join(dest, File.basename(s))
2490
+ yield s, (target_directory ? File.join(dest, File.basename(s)) : dest)
1598
2491
  end
1599
2492
  else
1600
2493
  src = File.path(src)
1601
- if File.directory?(dest)
2494
+ if target_directory and File.directory?(dest)
1602
2495
  yield src, File.join(dest, File.basename(src))
1603
2496
  else
1604
2497
  yield src, File.path(dest)
@@ -1614,7 +2507,7 @@ module Bundler::FileUtils
1614
2507
 
1615
2508
  def fu_output_message(msg) #:nodoc:
1616
2509
  output = @fileutils_output if defined?(@fileutils_output)
1617
- output ||= $stderr
2510
+ output ||= $stdout
1618
2511
  if defined?(@fileutils_label)
1619
2512
  msg = @fileutils_label + msg
1620
2513
  end
@@ -1622,6 +2515,56 @@ module Bundler::FileUtils
1622
2515
  end
1623
2516
  private_module_function :fu_output_message
1624
2517
 
2518
+ def fu_split_path(path)
2519
+ path = File.path(path)
2520
+ list = []
2521
+ until (parent, base = File.split(path); parent == path or parent == ".")
2522
+ list << base
2523
+ path = parent
2524
+ end
2525
+ list << path
2526
+ list.reverse!
2527
+ end
2528
+ private_module_function :fu_split_path
2529
+
2530
+ def fu_relative_components_from(target, base) #:nodoc:
2531
+ i = 0
2532
+ while target[i]&.== base[i]
2533
+ i += 1
2534
+ end
2535
+ Array.new(base.size-i, '..').concat(target[i..-1])
2536
+ end
2537
+ private_module_function :fu_relative_components_from
2538
+
2539
+ def fu_clean_components(*comp)
2540
+ comp.shift while comp.first == "."
2541
+ return comp if comp.empty?
2542
+ clean = [comp.shift]
2543
+ path = File.join(*clean, "") # ending with File::SEPARATOR
2544
+ while c = comp.shift
2545
+ if c == ".." and clean.last != ".." and !(fu_have_symlink? && File.symlink?(path))
2546
+ clean.pop
2547
+ path.chomp!(%r((?<=\A|/)[^/]+/\z), "")
2548
+ else
2549
+ clean << c
2550
+ path << c << "/"
2551
+ end
2552
+ end
2553
+ clean
2554
+ end
2555
+ private_module_function :fu_clean_components
2556
+
2557
+ if fu_windows?
2558
+ def fu_starting_path?(path)
2559
+ path&.start_with?(%r(\w:|/))
2560
+ end
2561
+ else
2562
+ def fu_starting_path?(path)
2563
+ path&.start_with?("/")
2564
+ end
2565
+ end
2566
+ private_module_function :fu_starting_path?
2567
+
1625
2568
  # This hash table holds command options.
1626
2569
  OPT_TABLE = {} #:nodoc: internal use only
1627
2570
  (private_instance_methods & methods(false)).inject(OPT_TABLE) {|tbl, name|
@@ -1631,50 +2574,49 @@ module Bundler::FileUtils
1631
2574
 
1632
2575
  public
1633
2576
 
2577
+ # Returns an array of the string names of \Bundler::FileUtils methods
2578
+ # that accept one or more keyword arguments:
1634
2579
  #
1635
- # Returns an Array of names of high-level methods that accept any keyword
1636
- # arguments.
1637
- #
1638
- # p Bundler::FileUtils.commands #=> ["chmod", "cp", "cp_r", "install", ...]
2580
+ # Bundler::FileUtils.commands.sort.take(3) # => ["cd", "chdir", "chmod"]
1639
2581
  #
1640
2582
  def self.commands
1641
2583
  OPT_TABLE.keys
1642
2584
  end
1643
2585
 
2586
+ # Returns an array of the string keyword names:
1644
2587
  #
1645
- # Returns an Array of option names.
1646
- #
1647
- # p Bundler::FileUtils.options #=> ["noop", "force", "verbose", "preserve", "mode"]
2588
+ # Bundler::FileUtils.options.take(3) # => ["noop", "verbose", "force"]
1648
2589
  #
1649
2590
  def self.options
1650
2591
  OPT_TABLE.values.flatten.uniq.map {|sym| sym.to_s }
1651
2592
  end
1652
2593
 
2594
+ # Returns +true+ if method +mid+ accepts the given option +opt+, +false+ otherwise;
2595
+ # the arguments may be strings or symbols:
1653
2596
  #
1654
- # Returns true if the method +mid+ have an option +opt+.
1655
- #
1656
- # p Bundler::FileUtils.have_option?(:cp, :noop) #=> true
1657
- # p Bundler::FileUtils.have_option?(:rm, :force) #=> true
1658
- # p Bundler::FileUtils.have_option?(:rm, :preserve) #=> false
2597
+ # Bundler::FileUtils.have_option?(:chmod, :noop) # => true
2598
+ # Bundler::FileUtils.have_option?('chmod', 'secure') # => false
1659
2599
  #
1660
2600
  def self.have_option?(mid, opt)
1661
2601
  li = OPT_TABLE[mid.to_s] or raise ArgumentError, "no such method: #{mid}"
1662
2602
  li.include?(opt)
1663
2603
  end
1664
2604
 
2605
+ # Returns an array of the string keyword name for method +mid+;
2606
+ # the argument may be a string or a symbol:
1665
2607
  #
1666
- # Returns an Array of option names of the method +mid+.
1667
- #
1668
- # p Bundler::FileUtils.options_of(:rm) #=> ["noop", "verbose", "force"]
2608
+ # Bundler::FileUtils.options_of(:rm) # => ["force", "noop", "verbose"]
2609
+ # Bundler::FileUtils.options_of('mv') # => ["force", "noop", "verbose", "secure"]
1669
2610
  #
1670
2611
  def self.options_of(mid)
1671
2612
  OPT_TABLE[mid.to_s].map {|sym| sym.to_s }
1672
2613
  end
1673
2614
 
2615
+ # Returns an array of the string method names of the methods
2616
+ # that accept the given keyword option +opt+;
2617
+ # the argument must be a symbol:
1674
2618
  #
1675
- # Returns an Array of methods names which have the option +opt+.
1676
- #
1677
- # p Bundler::FileUtils.collect_method(:preserve) #=> ["cp", "cp_r", "copy", "install"]
2619
+ # Bundler::FileUtils.collect_method(:preserve) # => ["cp", "copy", "cp_r", "install"]
1678
2620
  #
1679
2621
  def self.collect_method(opt)
1680
2622
  OPT_TABLE.keys.select {|m| OPT_TABLE[m].include?(opt) }