bundler 1.15.4 → 1.16.0.pre.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of bundler might be problematic. Click here for more details.

Files changed (251) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +43 -0
  3. data/README.md +12 -7
  4. data/exe/bundle +1 -1
  5. data/exe/bundle_ruby +4 -3
  6. data/lib/bundler.rb +47 -37
  7. data/lib/bundler/build_metadata.rb +38 -0
  8. data/lib/bundler/capistrano.rb +5 -0
  9. data/lib/bundler/cli.rb +155 -67
  10. data/lib/bundler/cli/add.rb +0 -1
  11. data/lib/bundler/cli/binstubs.rb +9 -7
  12. data/lib/bundler/cli/cache.rb +5 -4
  13. data/lib/bundler/cli/check.rb +3 -5
  14. data/lib/bundler/cli/clean.rb +5 -6
  15. data/lib/bundler/cli/common.rb +11 -2
  16. data/lib/bundler/cli/config.rb +2 -1
  17. data/lib/bundler/cli/console.rb +2 -1
  18. data/lib/bundler/cli/doctor.rb +1 -0
  19. data/lib/bundler/cli/exec.rb +2 -1
  20. data/lib/bundler/cli/gem.rb +3 -2
  21. data/lib/bundler/cli/info.rb +0 -1
  22. data/lib/bundler/cli/init.rb +17 -6
  23. data/lib/bundler/cli/inject.rb +1 -0
  24. data/lib/bundler/cli/install.rb +61 -61
  25. data/lib/bundler/cli/issue.rb +1 -1
  26. data/lib/bundler/cli/list.rb +22 -0
  27. data/lib/bundler/cli/lock.rb +0 -1
  28. data/lib/bundler/cli/open.rb +2 -2
  29. data/lib/bundler/cli/outdated.rb +13 -8
  30. data/lib/bundler/cli/package.rb +9 -6
  31. data/lib/bundler/cli/platform.rb +1 -0
  32. data/lib/bundler/cli/plugin.rb +1 -0
  33. data/lib/bundler/cli/pristine.rb +9 -2
  34. data/lib/bundler/cli/show.rb +0 -1
  35. data/lib/bundler/cli/update.rb +31 -5
  36. data/lib/bundler/cli/viz.rb +1 -0
  37. data/lib/bundler/compact_index_client.rb +1 -0
  38. data/lib/bundler/compact_index_client/cache.rb +1 -0
  39. data/lib/bundler/compact_index_client/updater.rb +3 -2
  40. data/lib/bundler/compatibility_guard.rb +14 -0
  41. data/lib/bundler/constants.rb +1 -0
  42. data/lib/bundler/current_ruby.rb +5 -4
  43. data/lib/bundler/definition.rb +140 -95
  44. data/lib/bundler/dep_proxy.rb +2 -0
  45. data/lib/bundler/dependency.rb +6 -7
  46. data/lib/bundler/deployment.rb +1 -1
  47. data/lib/bundler/deprecate.rb +1 -0
  48. data/lib/bundler/dsl.rb +97 -62
  49. data/lib/bundler/endpoint_specification.rb +9 -0
  50. data/lib/bundler/env.rb +63 -27
  51. data/lib/bundler/environment_preserver.rb +26 -6
  52. data/lib/bundler/errors.rb +1 -0
  53. data/lib/bundler/feature_flag.rb +39 -4
  54. data/lib/bundler/fetcher.rb +15 -8
  55. data/lib/bundler/fetcher/base.rb +1 -0
  56. data/lib/bundler/fetcher/compact_index.rb +2 -11
  57. data/lib/bundler/fetcher/dependency.rb +1 -0
  58. data/lib/bundler/fetcher/downloader.rb +1 -0
  59. data/lib/bundler/fetcher/index.rb +1 -0
  60. data/lib/bundler/friendly_errors.rb +2 -1
  61. data/lib/bundler/gem_helper.rb +14 -9
  62. data/lib/bundler/gem_helpers.rb +1 -0
  63. data/lib/bundler/gem_remote_fetcher.rb +1 -0
  64. data/lib/bundler/gem_tasks.rb +1 -0
  65. data/lib/bundler/gem_version_promoter.rb +1 -0
  66. data/lib/bundler/gemdeps.rb +1 -0
  67. data/lib/bundler/graph.rb +1 -0
  68. data/lib/bundler/index.rb +15 -8
  69. data/lib/bundler/injector.rb +25 -22
  70. data/lib/bundler/inline.rb +5 -7
  71. data/lib/bundler/installer.rb +93 -45
  72. data/lib/bundler/installer/gem_installer.rb +2 -0
  73. data/lib/bundler/installer/parallel_installer.rb +73 -42
  74. data/lib/bundler/installer/standalone.rb +1 -0
  75. data/lib/bundler/lazy_specification.rb +2 -1
  76. data/lib/bundler/lockfile_generator.rb +95 -0
  77. data/lib/bundler/lockfile_parser.rb +10 -4
  78. data/lib/bundler/match_platform.rb +1 -0
  79. data/lib/bundler/mirror.rb +6 -3
  80. data/lib/bundler/plugin.rb +1 -0
  81. data/lib/bundler/plugin/api/source.rb +8 -0
  82. data/lib/bundler/plugin/installer.rb +7 -6
  83. data/lib/bundler/plugin/source_list.rb +7 -8
  84. data/lib/bundler/process_lock.rb +24 -0
  85. data/lib/bundler/psyched_yaml.rb +1 -0
  86. data/lib/bundler/remote_specification.rb +1 -0
  87. data/lib/bundler/resolver.rb +138 -191
  88. data/lib/bundler/resolver/spec_group.rb +111 -0
  89. data/lib/bundler/retry.rb +1 -0
  90. data/lib/bundler/ruby_dsl.rb +1 -0
  91. data/lib/bundler/ruby_version.rb +1 -0
  92. data/lib/bundler/rubygems_ext.rb +5 -4
  93. data/lib/bundler/rubygems_gem_installer.rb +23 -0
  94. data/lib/bundler/rubygems_integration.rb +56 -27
  95. data/lib/bundler/runtime.rb +3 -5
  96. data/lib/bundler/settings.rb +177 -76
  97. data/lib/bundler/settings/validator.rb +79 -0
  98. data/lib/bundler/setup.rb +1 -0
  99. data/lib/bundler/shared_helpers.rb +86 -26
  100. data/lib/bundler/similarity_detector.rb +1 -0
  101. data/lib/bundler/source.rb +32 -0
  102. data/lib/bundler/source/gemspec.rb +1 -0
  103. data/lib/bundler/source/git.rb +21 -16
  104. data/lib/bundler/source/git/git_proxy.rb +14 -10
  105. data/lib/bundler/source/metadata.rb +63 -0
  106. data/lib/bundler/source/path.rb +8 -8
  107. data/lib/bundler/source/path/installer.rb +2 -0
  108. data/lib/bundler/source/rubygems.rb +131 -84
  109. data/lib/bundler/source/rubygems/remote.rb +3 -0
  110. data/lib/bundler/source_list.rb +75 -15
  111. data/lib/bundler/spec_set.rb +2 -1
  112. data/lib/bundler/ssl_certs/certificate_manager.rb +2 -1
  113. data/lib/bundler/stub_specification.rb +1 -0
  114. data/lib/bundler/templates/Executable +4 -0
  115. data/lib/bundler/templates/Executable.bundler +105 -0
  116. data/lib/bundler/templates/Gemfile +1 -0
  117. data/lib/bundler/templates/gems.rb +8 -0
  118. data/lib/bundler/templates/newgem/README.md.tt +1 -1
  119. data/lib/bundler/templates/newgem/gitignore.tt +0 -1
  120. data/lib/bundler/templates/newgem/newgem.gemspec.tt +4 -1
  121. data/lib/bundler/templates/newgem/rspec.tt +1 -0
  122. data/lib/bundler/templates/newgem/spec/newgem_spec.rb.tt +0 -2
  123. data/lib/bundler/ui.rb +1 -0
  124. data/lib/bundler/ui/rg_proxy.rb +1 -0
  125. data/lib/bundler/ui/shell.rb +15 -4
  126. data/lib/bundler/ui/silent.rb +1 -0
  127. data/lib/bundler/uri_credentials_filter.rb +1 -0
  128. data/lib/bundler/vendor/fileutils/lib/fileutils.rb +1638 -0
  129. data/lib/bundler/vendor/molinillo/lib/molinillo.rb +2 -0
  130. data/lib/bundler/vendor/molinillo/lib/molinillo/compatibility.rb +26 -0
  131. data/lib/bundler/vendor/molinillo/lib/molinillo/delegates/resolution_state.rb +7 -0
  132. data/lib/bundler/vendor/molinillo/lib/molinillo/delegates/specification_provider.rb +1 -0
  133. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb +3 -2
  134. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/action.rb +1 -0
  135. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb +1 -0
  136. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_vertex.rb +1 -0
  137. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/delete_edge.rb +1 -0
  138. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb +1 -0
  139. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/log.rb +1 -0
  140. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/set_payload.rb +1 -0
  141. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/tag.rb +1 -0
  142. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb +3 -2
  143. data/lib/bundler/vendor/molinillo/lib/molinillo/errors.rb +69 -6
  144. data/lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb +2 -1
  145. data/lib/bundler/vendor/molinillo/lib/molinillo/modules/specification_provider.rb +1 -0
  146. data/lib/bundler/vendor/molinillo/lib/molinillo/modules/ui.rb +3 -1
  147. data/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb +487 -148
  148. data/lib/bundler/vendor/molinillo/lib/molinillo/resolver.rb +1 -0
  149. data/lib/bundler/vendor/molinillo/lib/molinillo/state.rb +8 -4
  150. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb +1 -1
  151. data/lib/bundler/vendored_fileutils.rb +9 -0
  152. data/lib/bundler/vendored_molinillo.rb +1 -0
  153. data/lib/bundler/vendored_persistent.rb +34 -0
  154. data/lib/bundler/vendored_thor.rb +1 -0
  155. data/lib/bundler/version.rb +6 -2
  156. data/lib/bundler/version_ranges.rb +1 -0
  157. data/lib/bundler/vlad.rb +5 -0
  158. data/lib/bundler/worker.rb +1 -0
  159. data/lib/bundler/yaml_serializer.rb +3 -3
  160. data/man/bundle-add.1 +43 -0
  161. data/man/bundle-add.1.txt +40 -0
  162. data/man/bundle-binstubs.1 +40 -0
  163. data/man/bundle-binstubs.1.txt +48 -0
  164. data/man/bundle-binstubs.ronn +14 -0
  165. data/man/bundle-check.1 +31 -0
  166. data/man/bundle-check.1.txt +32 -0
  167. data/man/bundle-clean.1 +24 -0
  168. data/man/bundle-clean.1.txt +26 -0
  169. data/man/bundle-config.1 +455 -0
  170. data/man/bundle-config.1.txt +491 -0
  171. data/man/bundle-config.ronn +133 -79
  172. data/man/bundle-exec.1 +165 -0
  173. data/man/bundle-exec.1.txt +178 -0
  174. data/man/bundle-exec.ronn +7 -0
  175. data/man/bundle-gem.1 +80 -0
  176. data/man/bundle-gem.1.txt +91 -0
  177. data/man/bundle-gem.ronn +2 -1
  178. data/man/bundle-info.1 +20 -0
  179. data/man/bundle-info.1.txt +21 -0
  180. data/man/bundle-init.1 +20 -0
  181. data/man/bundle-init.1.txt +24 -0
  182. data/man/bundle-inject.1 +33 -0
  183. data/man/bundle-inject.1.txt +32 -0
  184. data/man/bundle-install.1 +305 -0
  185. data/man/bundle-install.1.txt +385 -0
  186. data/man/bundle-install.ronn +32 -32
  187. data/man/bundle-list.1 +20 -0
  188. data/man/bundle-list.1.txt +21 -0
  189. data/man/bundle-list.ronn +15 -0
  190. data/man/bundle-lock.1 +84 -0
  191. data/man/bundle-lock.1.txt +93 -0
  192. data/man/bundle-open.1 +32 -0
  193. data/man/bundle-open.1.txt +29 -0
  194. data/man/bundle-outdated.1 +151 -0
  195. data/man/bundle-outdated.1.txt +127 -0
  196. data/man/bundle-outdated.ronn +1 -1
  197. data/man/bundle-package.1 +55 -0
  198. data/man/bundle-package.1.txt +79 -0
  199. data/man/bundle-package.ronn +5 -0
  200. data/man/bundle-platform.1 +61 -0
  201. data/man/bundle-platform.1.txt +57 -0
  202. data/man/bundle-pristine.1 +34 -0
  203. data/man/bundle-pristine.1.txt +44 -0
  204. data/man/bundle-pristine.ronn +24 -3
  205. data/man/bundle-show.1 +23 -0
  206. data/man/bundle-show.1.txt +25 -0
  207. data/man/bundle-update.1 +390 -0
  208. data/man/bundle-update.1.txt +386 -0
  209. data/man/bundle-update.ronn +2 -2
  210. data/man/bundle-viz.1 +39 -0
  211. data/man/bundle-viz.1.txt +38 -0
  212. data/man/bundle-viz.ronn +5 -5
  213. data/man/bundle.1 +132 -0
  214. data/man/bundle.1.txt +113 -0
  215. data/man/bundle.ronn +5 -2
  216. data/man/gemfile.5 +679 -0
  217. data/man/gemfile.5.ronn +31 -0
  218. data/man/gemfile.5.txt +636 -0
  219. data/man/index.txt +23 -0
  220. metadata +21 -36
  221. data/.codeclimate.yml +0 -25
  222. data/.gitignore +0 -18
  223. data/.rspec +0 -3
  224. data/.rubocop.yml +0 -131
  225. data/.rubocop_todo.yml +0 -418
  226. data/.travis.yml +0 -122
  227. data/CODE_OF_CONDUCT.md +0 -42
  228. data/CONTRIBUTING.md +0 -17
  229. data/Rakefile +0 -338
  230. data/bin/rake +0 -19
  231. data/bin/rspec +0 -15
  232. data/bin/rubocop +0 -17
  233. data/bin/with_rubygems +0 -39
  234. data/bundler.gemspec +0 -48
  235. data/doc/README.md +0 -30
  236. data/doc/TROUBLESHOOTING.md +0 -64
  237. data/doc/contributing/BUG_TRIAGE.md +0 -36
  238. data/doc/contributing/COMMUNITY.md +0 -13
  239. data/doc/contributing/GETTING_HELP.md +0 -11
  240. data/doc/contributing/HOW_YOU_CAN_HELP.md +0 -27
  241. data/doc/contributing/ISSUES.md +0 -51
  242. data/doc/contributing/README.md +0 -38
  243. data/doc/development/NEW_FEATURES.md +0 -10
  244. data/doc/development/PULL_REQUESTS.md +0 -40
  245. data/doc/development/README.md +0 -19
  246. data/doc/development/RELEASING.md +0 -9
  247. data/doc/development/SETUP.md +0 -27
  248. data/doc/documentation/README.md +0 -29
  249. data/doc/documentation/VISION.md +0 -26
  250. data/doc/documentation/WRITING.md +0 -54
  251. data/task/release.rake +0 -116
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Bundler
3
4
  module UI
4
5
  class Silent
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Bundler
3
4
  module URICredentialsFilter
4
5
  module_function
@@ -0,0 +1,1638 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # = fileutils.rb
4
+ #
5
+ # Copyright (c) 2000-2007 Minero Aoki
6
+ #
7
+ # This program is free software.
8
+ # You can distribute/modify this program under the same terms of ruby.
9
+ #
10
+ # == module Bundler::FileUtils
11
+ #
12
+ # Namespace for several file utility methods for copying, moving, removing, etc.
13
+ #
14
+ # === Module Functions
15
+ #
16
+ # require 'bundler/vendor/fileutils/lib/fileutils'
17
+ #
18
+ # Bundler::FileUtils.cd(dir, options)
19
+ # Bundler::FileUtils.cd(dir, options) {|dir| block }
20
+ # Bundler::FileUtils.pwd()
21
+ # Bundler::FileUtils.mkdir(dir, options)
22
+ # Bundler::FileUtils.mkdir(list, options)
23
+ # Bundler::FileUtils.mkdir_p(dir, options)
24
+ # Bundler::FileUtils.mkdir_p(list, options)
25
+ # Bundler::FileUtils.rmdir(dir, options)
26
+ # Bundler::FileUtils.rmdir(list, options)
27
+ # Bundler::FileUtils.ln(target, link, options)
28
+ # Bundler::FileUtils.ln(targets, dir, options)
29
+ # Bundler::FileUtils.ln_s(target, link, options)
30
+ # Bundler::FileUtils.ln_s(targets, dir, options)
31
+ # Bundler::FileUtils.ln_sf(target, link, options)
32
+ # Bundler::FileUtils.cp(src, dest, options)
33
+ # Bundler::FileUtils.cp(list, dir, options)
34
+ # Bundler::FileUtils.cp_r(src, dest, options)
35
+ # Bundler::FileUtils.cp_r(list, dir, options)
36
+ # Bundler::FileUtils.mv(src, dest, options)
37
+ # Bundler::FileUtils.mv(list, dir, options)
38
+ # Bundler::FileUtils.rm(list, options)
39
+ # Bundler::FileUtils.rm_r(list, options)
40
+ # Bundler::FileUtils.rm_rf(list, options)
41
+ # Bundler::FileUtils.install(src, dest, options)
42
+ # Bundler::FileUtils.chmod(mode, list, options)
43
+ # Bundler::FileUtils.chmod_R(mode, list, options)
44
+ # Bundler::FileUtils.chown(user, group, list, options)
45
+ # Bundler::FileUtils.chown_R(user, group, list, options)
46
+ # Bundler::FileUtils.touch(list, options)
47
+ #
48
+ # The <tt>options</tt> parameter is a hash of options, taken from the list
49
+ # <tt>:force</tt>, <tt>:noop</tt>, <tt>:preserve</tt>, and <tt>:verbose</tt>.
50
+ # <tt>:noop</tt> means that no changes are made. The other three are obvious.
51
+ # Each method documents the options that it honours.
52
+ #
53
+ # All methods that have the concept of a "source" file or directory can take
54
+ # either one file or a list of files in that argument. See the method
55
+ # documentation for examples.
56
+ #
57
+ # There are some `low level' methods, which do not accept any option:
58
+ #
59
+ # Bundler::FileUtils.copy_entry(src, dest, preserve = false, dereference = false)
60
+ # Bundler::FileUtils.copy_file(src, dest, preserve = false, dereference = true)
61
+ # Bundler::FileUtils.copy_stream(srcstream, deststream)
62
+ # Bundler::FileUtils.remove_entry(path, force = false)
63
+ # Bundler::FileUtils.remove_entry_secure(path, force = false)
64
+ # Bundler::FileUtils.remove_file(path, force = false)
65
+ # Bundler::FileUtils.compare_file(path_a, path_b)
66
+ # Bundler::FileUtils.compare_stream(stream_a, stream_b)
67
+ # Bundler::FileUtils.uptodate?(file, cmp_list)
68
+ #
69
+ # == module Bundler::FileUtils::Verbose
70
+ #
71
+ # This module has all methods of Bundler::FileUtils module, but it outputs messages
72
+ # before acting. This equates to passing the <tt>:verbose</tt> flag to methods
73
+ # in Bundler::FileUtils.
74
+ #
75
+ # == module Bundler::FileUtils::NoWrite
76
+ #
77
+ # This module has all methods of Bundler::FileUtils module, but never changes
78
+ # files/directories. This equates to passing the <tt>:noop</tt> flag to methods
79
+ # in Bundler::FileUtils.
80
+ #
81
+ # == module Bundler::FileUtils::DryRun
82
+ #
83
+ # This module has all methods of Bundler::FileUtils module, but never changes
84
+ # files/directories. This equates to passing the <tt>:noop</tt> and
85
+ # <tt>:verbose</tt> flags to methods in Bundler::FileUtils.
86
+ #
87
+
88
+ module Bundler::FileUtils
89
+
90
+ def self.private_module_function(name) #:nodoc:
91
+ module_function name
92
+ private_class_method name
93
+ end
94
+
95
+ #
96
+ # Returns the name of the current directory.
97
+ #
98
+ def pwd
99
+ Dir.pwd
100
+ end
101
+ module_function :pwd
102
+
103
+ alias getwd pwd
104
+ module_function :getwd
105
+
106
+ #
107
+ # Changes the current directory to the directory +dir+.
108
+ #
109
+ # If this method is called with block, resumes to the old
110
+ # working directory after the block execution finished.
111
+ #
112
+ # Bundler::FileUtils.cd('/', :verbose => true) # chdir and report it
113
+ #
114
+ # Bundler::FileUtils.cd('/') do # chdir
115
+ # # ... # do something
116
+ # end # return to original directory
117
+ #
118
+ def cd(dir, verbose: nil, &block) # :yield: dir
119
+ fu_output_message "cd #{dir}" if verbose
120
+ Dir.chdir(dir, &block)
121
+ fu_output_message 'cd -' if verbose and block
122
+ end
123
+ module_function :cd
124
+
125
+ alias chdir cd
126
+ module_function :chdir
127
+
128
+ #
129
+ # Returns true if +new+ is newer than all +old_list+.
130
+ # Non-existent files are older than any file.
131
+ #
132
+ # Bundler::FileUtils.uptodate?('hello.o', %w(hello.c hello.h)) or \
133
+ # system 'make hello.o'
134
+ #
135
+ def uptodate?(new, old_list)
136
+ return false unless File.exist?(new)
137
+ new_time = File.mtime(new)
138
+ old_list.each do |old|
139
+ if File.exist?(old)
140
+ return false unless new_time > File.mtime(old)
141
+ end
142
+ end
143
+ true
144
+ end
145
+ module_function :uptodate?
146
+
147
+ def remove_trailing_slash(dir) #:nodoc:
148
+ dir == '/' ? dir : dir.chomp(?/)
149
+ end
150
+ private_module_function :remove_trailing_slash
151
+
152
+ #
153
+ # Creates one or more directories.
154
+ #
155
+ # Bundler::FileUtils.mkdir 'test'
156
+ # Bundler::FileUtils.mkdir %w( tmp data )
157
+ # Bundler::FileUtils.mkdir 'notexist', :noop => true # Does not really create.
158
+ # Bundler::FileUtils.mkdir 'tmp', :mode => 0700
159
+ #
160
+ def mkdir(list, mode: nil, noop: nil, verbose: nil)
161
+ list = fu_list(list)
162
+ fu_output_message "mkdir #{mode ? ('-m %03o ' % mode) : ''}#{list.join ' '}" if verbose
163
+ return if noop
164
+
165
+ list.each do |dir|
166
+ fu_mkdir dir, mode
167
+ end
168
+ end
169
+ module_function :mkdir
170
+
171
+ #
172
+ # Creates a directory and all its parent directories.
173
+ # For example,
174
+ #
175
+ # Bundler::FileUtils.mkdir_p '/usr/local/lib/ruby'
176
+ #
177
+ # causes to make following directories, if it does not exist.
178
+ #
179
+ # * /usr
180
+ # * /usr/local
181
+ # * /usr/local/lib
182
+ # * /usr/local/lib/ruby
183
+ #
184
+ # You can pass several directories at a time in a list.
185
+ #
186
+ def mkdir_p(list, mode: nil, noop: nil, verbose: nil)
187
+ list = fu_list(list)
188
+ fu_output_message "mkdir -p #{mode ? ('-m %03o ' % mode) : ''}#{list.join ' '}" if verbose
189
+ return *list if noop
190
+
191
+ list.map {|path| remove_trailing_slash(path)}.each do |path|
192
+ # optimize for the most common case
193
+ begin
194
+ fu_mkdir path, mode
195
+ next
196
+ rescue SystemCallError
197
+ next if File.directory?(path)
198
+ end
199
+
200
+ stack = []
201
+ until path == stack.last # dirname("/")=="/", dirname("C:/")=="C:/"
202
+ stack.push path
203
+ path = File.dirname(path)
204
+ end
205
+ stack.pop # root directory should exist
206
+ stack.reverse_each do |dir|
207
+ begin
208
+ fu_mkdir dir, mode
209
+ rescue SystemCallError
210
+ raise unless File.directory?(dir)
211
+ end
212
+ end
213
+ end
214
+
215
+ return *list
216
+ end
217
+ module_function :mkdir_p
218
+
219
+ alias mkpath mkdir_p
220
+ alias makedirs mkdir_p
221
+ module_function :mkpath
222
+ module_function :makedirs
223
+
224
+ def fu_mkdir(path, mode) #:nodoc:
225
+ path = remove_trailing_slash(path)
226
+ if mode
227
+ Dir.mkdir path, mode
228
+ File.chmod mode, path
229
+ else
230
+ Dir.mkdir path
231
+ end
232
+ end
233
+ private_module_function :fu_mkdir
234
+
235
+ #
236
+ # Removes one or more directories.
237
+ #
238
+ # Bundler::FileUtils.rmdir 'somedir'
239
+ # Bundler::FileUtils.rmdir %w(somedir anydir otherdir)
240
+ # # Does not really remove directory; outputs message.
241
+ # Bundler::FileUtils.rmdir 'somedir', :verbose => true, :noop => true
242
+ #
243
+ def rmdir(list, parents: nil, noop: nil, verbose: nil)
244
+ list = fu_list(list)
245
+ fu_output_message "rmdir #{parents ? '-p ' : ''}#{list.join ' '}" if verbose
246
+ return if noop
247
+ list.each do |dir|
248
+ begin
249
+ Dir.rmdir(dir = remove_trailing_slash(dir))
250
+ if parents
251
+ until (parent = File.dirname(dir)) == '.' or parent == dir
252
+ dir = parent
253
+ Dir.rmdir(dir)
254
+ end
255
+ end
256
+ rescue Errno::ENOTEMPTY, Errno::EEXIST, Errno::ENOENT
257
+ end
258
+ end
259
+ end
260
+ module_function :rmdir
261
+
262
+ #
263
+ # :call-seq:
264
+ # Bundler::FileUtils.ln(target, link, force: nil, noop: nil, verbose: nil)
265
+ # Bundler::FileUtils.ln(target, dir, force: nil, noop: nil, verbose: nil)
266
+ # Bundler::FileUtils.ln(targets, dir, force: nil, noop: nil, verbose: nil)
267
+ #
268
+ # In the first form, creates a hard link +link+ which points to +target+.
269
+ # If +link+ already exists, raises Errno::EEXIST.
270
+ # But if the :force option is set, overwrites +link+.
271
+ #
272
+ # Bundler::FileUtils.ln 'gcc', 'cc', verbose: true
273
+ # Bundler::FileUtils.ln '/usr/bin/emacs21', '/usr/bin/emacs'
274
+ #
275
+ # In the second form, creates a link +dir/target+ pointing to +target+.
276
+ # In the third form, creates several hard links in the directory +dir+,
277
+ # pointing to each item in +targets+.
278
+ # If +dir+ is not a directory, raises Errno::ENOTDIR.
279
+ #
280
+ # Bundler::FileUtils.cd '/sbin'
281
+ # Bundler::FileUtils.ln %w(cp mv mkdir), '/bin' # Now /sbin/cp and /bin/cp are linked.
282
+ #
283
+ def ln(src, dest, force: nil, noop: nil, verbose: nil)
284
+ fu_output_message "ln#{force ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if verbose
285
+ return if noop
286
+ fu_each_src_dest0(src, dest) do |s,d|
287
+ remove_file d, true if force
288
+ File.link s, d
289
+ end
290
+ end
291
+ module_function :ln
292
+
293
+ alias link ln
294
+ module_function :link
295
+
296
+ #
297
+ # :call-seq:
298
+ # Bundler::FileUtils.ln_s(target, link, force: nil, noop: nil, verbose: nil)
299
+ # Bundler::FileUtils.ln_s(target, dir, force: nil, noop: nil, verbose: nil)
300
+ # Bundler::FileUtils.ln_s(targets, dir, force: nil, noop: nil, verbose: nil)
301
+ #
302
+ # In the first form, creates a symbolic link +link+ which points to +target+.
303
+ # If +link+ already exists, raises Errno::EEXIST.
304
+ # But if the :force option is set, overwrites +link+.
305
+ #
306
+ # Bundler::FileUtils.ln_s '/usr/bin/ruby', '/usr/local/bin/ruby'
307
+ # Bundler::FileUtils.ln_s 'verylongsourcefilename.c', 'c', force: true
308
+ #
309
+ # In the second form, creates a link +dir/target+ pointing to +target+.
310
+ # In the third form, creates several symbolic links in the directory +dir+,
311
+ # pointing to each item in +targets+.
312
+ # If +dir+ is not a directory, raises Errno::ENOTDIR.
313
+ #
314
+ # Bundler::FileUtils.ln_s Dir.glob('/bin/*.rb'), '/home/foo/bin'
315
+ #
316
+ def ln_s(src, dest, force: nil, noop: nil, verbose: nil)
317
+ fu_output_message "ln -s#{force ? 'f' : ''} #{[src,dest].flatten.join ' '}" if verbose
318
+ return if noop
319
+ fu_each_src_dest0(src, dest) do |s,d|
320
+ remove_file d, true if force
321
+ File.symlink s, d
322
+ end
323
+ end
324
+ module_function :ln_s
325
+
326
+ alias symlink ln_s
327
+ module_function :symlink
328
+
329
+ #
330
+ # :call-seq:
331
+ # Bundler::FileUtils.ln_sf(*args)
332
+ #
333
+ # Same as
334
+ #
335
+ # Bundler::FileUtils.ln_s(*args, force: true)
336
+ #
337
+ def ln_sf(src, dest, noop: nil, verbose: nil)
338
+ ln_s src, dest, force: true, noop: noop, verbose: verbose
339
+ end
340
+ module_function :ln_sf
341
+
342
+ #
343
+ # Copies a file content +src+ to +dest+. If +dest+ is a directory,
344
+ # copies +src+ to +dest/src+.
345
+ #
346
+ # If +src+ is a list of files, then +dest+ must be a directory.
347
+ #
348
+ # Bundler::FileUtils.cp 'eval.c', 'eval.c.org'
349
+ # Bundler::FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6'
350
+ # Bundler::FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6', :verbose => true
351
+ # Bundler::FileUtils.cp 'symlink', 'dest' # copy content, "dest" is not a symlink
352
+ #
353
+ def cp(src, dest, preserve: nil, noop: nil, verbose: nil)
354
+ fu_output_message "cp#{preserve ? ' -p' : ''} #{[src,dest].flatten.join ' '}" if verbose
355
+ return if noop
356
+ fu_each_src_dest(src, dest) do |s, d|
357
+ copy_file s, d, preserve
358
+ end
359
+ end
360
+ module_function :cp
361
+
362
+ alias copy cp
363
+ module_function :copy
364
+
365
+ #
366
+ # Copies +src+ to +dest+. If +src+ is a directory, this method copies
367
+ # all its contents recursively. If +dest+ is a directory, copies
368
+ # +src+ to +dest/src+.
369
+ #
370
+ # +src+ can be a list of files.
371
+ #
372
+ # # Installing Ruby library "mylib" under the site_ruby
373
+ # Bundler::FileUtils.rm_r site_ruby + '/mylib', :force
374
+ # Bundler::FileUtils.cp_r 'lib/', site_ruby + '/mylib'
375
+ #
376
+ # # Examples of copying several files to target directory.
377
+ # Bundler::FileUtils.cp_r %w(mail.rb field.rb debug/), site_ruby + '/tmail'
378
+ # Bundler::FileUtils.cp_r Dir.glob('*.rb'), '/home/foo/lib/ruby', :noop => true, :verbose => true
379
+ #
380
+ # # If you want to copy all contents of a directory instead of the
381
+ # # directory itself, c.f. src/x -> dest/x, src/y -> dest/y,
382
+ # # use following code.
383
+ # Bundler::FileUtils.cp_r 'src/.', 'dest' # cp_r('src', 'dest') makes dest/src,
384
+ # # but this doesn't.
385
+ #
386
+ def cp_r(src, dest, preserve: nil, noop: nil, verbose: nil,
387
+ dereference_root: true, remove_destination: nil)
388
+ fu_output_message "cp -r#{preserve ? 'p' : ''}#{remove_destination ? ' --remove-destination' : ''} #{[src,dest].flatten.join ' '}" if verbose
389
+ return if noop
390
+ fu_each_src_dest(src, dest) do |s, d|
391
+ copy_entry s, d, preserve, dereference_root, remove_destination
392
+ end
393
+ end
394
+ module_function :cp_r
395
+
396
+ #
397
+ # Copies a file system entry +src+ to +dest+.
398
+ # If +src+ is a directory, this method copies its contents recursively.
399
+ # This method preserves file types, c.f. symlink, directory...
400
+ # (FIFO, device files and etc. are not supported yet)
401
+ #
402
+ # Both of +src+ and +dest+ must be a path name.
403
+ # +src+ must exist, +dest+ must not exist.
404
+ #
405
+ # If +preserve+ is true, this method preserves owner, group, and
406
+ # modified time. Permissions are copied regardless +preserve+.
407
+ #
408
+ # If +dereference_root+ is true, this method dereference tree root.
409
+ #
410
+ # If +remove_destination+ is true, this method removes each destination file before copy.
411
+ #
412
+ def copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = false)
413
+ Entry_.new(src, nil, dereference_root).wrap_traverse(proc do |ent|
414
+ destent = Entry_.new(dest, ent.rel, false)
415
+ File.unlink destent.path if remove_destination && File.file?(destent.path)
416
+ ent.copy destent.path
417
+ end, proc do |ent|
418
+ destent = Entry_.new(dest, ent.rel, false)
419
+ ent.copy_metadata destent.path if preserve
420
+ end)
421
+ end
422
+ module_function :copy_entry
423
+
424
+ #
425
+ # Copies file contents of +src+ to +dest+.
426
+ # Both of +src+ and +dest+ must be a path name.
427
+ #
428
+ def copy_file(src, dest, preserve = false, dereference = true)
429
+ ent = Entry_.new(src, nil, dereference)
430
+ ent.copy_file dest
431
+ ent.copy_metadata dest if preserve
432
+ end
433
+ module_function :copy_file
434
+
435
+ #
436
+ # Copies stream +src+ to +dest+.
437
+ # +src+ must respond to #read(n) and
438
+ # +dest+ must respond to #write(str).
439
+ #
440
+ def copy_stream(src, dest)
441
+ IO.copy_stream(src, dest)
442
+ end
443
+ module_function :copy_stream
444
+
445
+ #
446
+ # Moves file(s) +src+ to +dest+. If +file+ and +dest+ exist on the different
447
+ # disk partition, the file is copied then the original file is removed.
448
+ #
449
+ # Bundler::FileUtils.mv 'badname.rb', 'goodname.rb'
450
+ # Bundler::FileUtils.mv 'stuff.rb', '/notexist/lib/ruby', :force => true # no error
451
+ #
452
+ # Bundler::FileUtils.mv %w(junk.txt dust.txt), '/home/foo/.trash/'
453
+ # Bundler::FileUtils.mv Dir.glob('test*.rb'), 'test', :noop => true, :verbose => true
454
+ #
455
+ def mv(src, dest, force: nil, noop: nil, verbose: nil, secure: nil)
456
+ fu_output_message "mv#{force ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if verbose
457
+ return if noop
458
+ fu_each_src_dest(src, dest) do |s, d|
459
+ destent = Entry_.new(d, nil, true)
460
+ begin
461
+ if destent.exist?
462
+ if destent.directory?
463
+ raise Errno::EEXIST, d
464
+ else
465
+ destent.remove_file if rename_cannot_overwrite_file?
466
+ end
467
+ end
468
+ begin
469
+ File.rename s, d
470
+ rescue Errno::EXDEV
471
+ copy_entry s, d, true
472
+ if secure
473
+ remove_entry_secure s, force
474
+ else
475
+ remove_entry s, force
476
+ end
477
+ end
478
+ rescue SystemCallError
479
+ raise unless force
480
+ end
481
+ end
482
+ end
483
+ module_function :mv
484
+
485
+ alias move mv
486
+ module_function :move
487
+
488
+ def rename_cannot_overwrite_file? #:nodoc:
489
+ /emx/ =~ RUBY_PLATFORM
490
+ end
491
+ private_module_function :rename_cannot_overwrite_file?
492
+
493
+ #
494
+ # Remove file(s) specified in +list+. This method cannot remove directories.
495
+ # All StandardErrors are ignored when the :force option is set.
496
+ #
497
+ # Bundler::FileUtils.rm %w( junk.txt dust.txt )
498
+ # Bundler::FileUtils.rm Dir.glob('*.so')
499
+ # Bundler::FileUtils.rm 'NotExistFile', :force => true # never raises exception
500
+ #
501
+ def rm(list, force: nil, noop: nil, verbose: nil)
502
+ list = fu_list(list)
503
+ fu_output_message "rm#{force ? ' -f' : ''} #{list.join ' '}" if verbose
504
+ return if noop
505
+
506
+ list.each do |path|
507
+ remove_file path, force
508
+ end
509
+ end
510
+ module_function :rm
511
+
512
+ alias remove rm
513
+ module_function :remove
514
+
515
+ #
516
+ # Equivalent to
517
+ #
518
+ # Bundler::FileUtils.rm(list, :force => true)
519
+ #
520
+ def rm_f(list, noop: nil, verbose: nil)
521
+ rm list, force: true, noop: noop, verbose: verbose
522
+ end
523
+ module_function :rm_f
524
+
525
+ alias safe_unlink rm_f
526
+ module_function :safe_unlink
527
+
528
+ #
529
+ # remove files +list+[0] +list+[1]... If +list+[n] is a directory,
530
+ # removes its all contents recursively. This method ignores
531
+ # StandardError when :force option is set.
532
+ #
533
+ # Bundler::FileUtils.rm_r Dir.glob('/tmp/*')
534
+ # Bundler::FileUtils.rm_r 'some_dir', :force => true
535
+ #
536
+ # WARNING: This method causes local vulnerability
537
+ # if one of parent directories or removing directory tree are world
538
+ # writable (including /tmp, whose permission is 1777), and the current
539
+ # process has strong privilege such as Unix super user (root), and the
540
+ # system has symbolic link. For secure removing, read the documentation
541
+ # of #remove_entry_secure carefully, and set :secure option to true.
542
+ # Default is :secure=>false.
543
+ #
544
+ # NOTE: This method calls #remove_entry_secure if :secure option is set.
545
+ # See also #remove_entry_secure.
546
+ #
547
+ def rm_r(list, force: nil, noop: nil, verbose: nil, secure: nil)
548
+ list = fu_list(list)
549
+ fu_output_message "rm -r#{force ? 'f' : ''} #{list.join ' '}" if verbose
550
+ return if noop
551
+ list.each do |path|
552
+ if secure
553
+ remove_entry_secure path, force
554
+ else
555
+ remove_entry path, force
556
+ end
557
+ end
558
+ end
559
+ module_function :rm_r
560
+
561
+ #
562
+ # Equivalent to
563
+ #
564
+ # Bundler::FileUtils.rm_r(list, :force => true)
565
+ #
566
+ # WARNING: This method causes local vulnerability.
567
+ # Read the documentation of #rm_r first.
568
+ #
569
+ def rm_rf(list, noop: nil, verbose: nil, secure: nil)
570
+ rm_r list, force: true, noop: noop, verbose: verbose, secure: secure
571
+ end
572
+ module_function :rm_rf
573
+
574
+ alias rmtree rm_rf
575
+ module_function :rmtree
576
+
577
+ #
578
+ # This method removes a file system entry +path+. +path+ shall be a
579
+ # regular file, a directory, or something. If +path+ is a directory,
580
+ # remove it recursively. This method is required to avoid TOCTTOU
581
+ # (time-of-check-to-time-of-use) local security vulnerability of #rm_r.
582
+ # #rm_r causes security hole when:
583
+ #
584
+ # * Parent directory is world writable (including /tmp).
585
+ # * Removing directory tree includes world writable directory.
586
+ # * The system has symbolic link.
587
+ #
588
+ # To avoid this security hole, this method applies special preprocess.
589
+ # If +path+ is a directory, this method chown(2) and chmod(2) all
590
+ # removing directories. This requires the current process is the
591
+ # owner of the removing whole directory tree, or is the super user (root).
592
+ #
593
+ # WARNING: You must ensure that *ALL* parent directories cannot be
594
+ # moved by other untrusted users. For example, parent directories
595
+ # should not be owned by untrusted users, and should not be world
596
+ # writable except when the sticky bit set.
597
+ #
598
+ # WARNING: Only the owner of the removing directory tree, or Unix super
599
+ # user (root) should invoke this method. Otherwise this method does not
600
+ # work.
601
+ #
602
+ # For details of this security vulnerability, see Perl's case:
603
+ #
604
+ # * http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2005-0448
605
+ # * http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2004-0452
606
+ #
607
+ # For fileutils.rb, this vulnerability is reported in [ruby-dev:26100].
608
+ #
609
+ def remove_entry_secure(path, force = false)
610
+ unless fu_have_symlink?
611
+ remove_entry path, force
612
+ return
613
+ end
614
+ fullpath = File.expand_path(path)
615
+ st = File.lstat(fullpath)
616
+ unless st.directory?
617
+ File.unlink fullpath
618
+ return
619
+ end
620
+ # is a directory.
621
+ parent_st = File.stat(File.dirname(fullpath))
622
+ unless parent_st.world_writable?
623
+ remove_entry path, force
624
+ return
625
+ end
626
+ unless parent_st.sticky?
627
+ raise ArgumentError, "parent directory is world writable, Bundler::FileUtils#remove_entry_secure does not work; abort: #{path.inspect} (parent directory mode #{'%o' % parent_st.mode})"
628
+ end
629
+ # freeze tree root
630
+ euid = Process.euid
631
+ File.open(fullpath + '/.') {|f|
632
+ unless fu_stat_identical_entry?(st, f.stat)
633
+ # symlink (TOC-to-TOU attack?)
634
+ File.unlink fullpath
635
+ return
636
+ end
637
+ f.chown euid, -1
638
+ f.chmod 0700
639
+ unless fu_stat_identical_entry?(st, File.lstat(fullpath))
640
+ # TOC-to-TOU attack?
641
+ File.unlink fullpath
642
+ return
643
+ end
644
+ }
645
+ # ---- tree root is frozen ----
646
+ root = Entry_.new(path)
647
+ root.preorder_traverse do |ent|
648
+ if ent.directory?
649
+ ent.chown euid, -1
650
+ ent.chmod 0700
651
+ end
652
+ end
653
+ root.postorder_traverse do |ent|
654
+ begin
655
+ ent.remove
656
+ rescue
657
+ raise unless force
658
+ end
659
+ end
660
+ rescue
661
+ raise unless force
662
+ end
663
+ module_function :remove_entry_secure
664
+
665
+ def fu_have_symlink? #:nodoc:
666
+ File.symlink nil, nil
667
+ rescue NotImplementedError
668
+ return false
669
+ rescue TypeError
670
+ return true
671
+ end
672
+ private_module_function :fu_have_symlink?
673
+
674
+ def fu_stat_identical_entry?(a, b) #:nodoc:
675
+ a.dev == b.dev and a.ino == b.ino
676
+ end
677
+ private_module_function :fu_stat_identical_entry?
678
+
679
+ #
680
+ # This method removes a file system entry +path+.
681
+ # +path+ might be a regular file, a directory, or something.
682
+ # If +path+ is a directory, remove it recursively.
683
+ #
684
+ # See also #remove_entry_secure.
685
+ #
686
+ def remove_entry(path, force = false)
687
+ Entry_.new(path).postorder_traverse do |ent|
688
+ begin
689
+ ent.remove
690
+ rescue
691
+ raise unless force
692
+ end
693
+ end
694
+ rescue
695
+ raise unless force
696
+ end
697
+ module_function :remove_entry
698
+
699
+ #
700
+ # Removes a file +path+.
701
+ # This method ignores StandardError if +force+ is true.
702
+ #
703
+ def remove_file(path, force = false)
704
+ Entry_.new(path).remove_file
705
+ rescue
706
+ raise unless force
707
+ end
708
+ module_function :remove_file
709
+
710
+ #
711
+ # Removes a directory +dir+ and its contents recursively.
712
+ # This method ignores StandardError if +force+ is true.
713
+ #
714
+ def remove_dir(path, force = false)
715
+ remove_entry path, force # FIXME?? check if it is a directory
716
+ end
717
+ module_function :remove_dir
718
+
719
+ #
720
+ # Returns true if the contents of a file +a+ and a file +b+ are identical.
721
+ #
722
+ # Bundler::FileUtils.compare_file('somefile', 'somefile') #=> true
723
+ # Bundler::FileUtils.compare_file('/dev/null', '/dev/urandom') #=> false
724
+ #
725
+ def compare_file(a, b)
726
+ return false unless File.size(a) == File.size(b)
727
+ File.open(a, 'rb') {|fa|
728
+ File.open(b, 'rb') {|fb|
729
+ return compare_stream(fa, fb)
730
+ }
731
+ }
732
+ end
733
+ module_function :compare_file
734
+
735
+ alias identical? compare_file
736
+ alias cmp compare_file
737
+ module_function :identical?
738
+ module_function :cmp
739
+
740
+ #
741
+ # Returns true if the contents of a stream +a+ and +b+ are identical.
742
+ #
743
+ def compare_stream(a, b)
744
+ bsize = fu_stream_blksize(a, b)
745
+ sa = String.new(capacity: bsize)
746
+ sb = String.new(capacity: bsize)
747
+ begin
748
+ a.read(bsize, sa)
749
+ b.read(bsize, sb)
750
+ return true if sa.empty? && sb.empty?
751
+ end while sa == sb
752
+ false
753
+ end
754
+ module_function :compare_stream
755
+
756
+ #
757
+ # If +src+ is not same as +dest+, copies it and changes the permission
758
+ # mode to +mode+. If +dest+ is a directory, destination is +dest+/+src+.
759
+ # This method removes destination before copy.
760
+ #
761
+ # Bundler::FileUtils.install 'ruby', '/usr/local/bin/ruby', :mode => 0755, :verbose => true
762
+ # Bundler::FileUtils.install 'lib.rb', '/usr/local/lib/ruby/site_ruby', :verbose => true
763
+ #
764
+ def install(src, dest, mode: nil, owner: nil, group: nil, preserve: nil,
765
+ noop: nil, verbose: nil)
766
+ if verbose
767
+ msg = +"install -c"
768
+ msg << ' -p' if preserve
769
+ msg << ' -m ' << mode_to_s(mode) if mode
770
+ msg << " -o #{owner}" if owner
771
+ msg << " -g #{group}" if group
772
+ msg << ' ' << [src,dest].flatten.join(' ')
773
+ fu_output_message msg
774
+ end
775
+ return if noop
776
+ uid = fu_get_uid(owner)
777
+ gid = fu_get_gid(group)
778
+ fu_each_src_dest(src, dest) do |s, d|
779
+ st = File.stat(s)
780
+ unless File.exist?(d) and compare_file(s, d)
781
+ remove_file d, true
782
+ copy_file s, d
783
+ File.utime st.atime, st.mtime, d if preserve
784
+ File.chmod fu_mode(mode, st), d if mode
785
+ File.chown uid, gid, d if uid or gid
786
+ end
787
+ end
788
+ end
789
+ module_function :install
790
+
791
+ def user_mask(target) #:nodoc:
792
+ target.each_char.inject(0) do |mask, chr|
793
+ case chr
794
+ when "u"
795
+ mask | 04700
796
+ when "g"
797
+ mask | 02070
798
+ when "o"
799
+ mask | 01007
800
+ when "a"
801
+ mask | 07777
802
+ else
803
+ raise ArgumentError, "invalid `who' symbol in file mode: #{chr}"
804
+ end
805
+ end
806
+ end
807
+ private_module_function :user_mask
808
+
809
+ def apply_mask(mode, user_mask, op, mode_mask) #:nodoc:
810
+ case op
811
+ when '='
812
+ (mode & ~user_mask) | (user_mask & mode_mask)
813
+ when '+'
814
+ mode | (user_mask & mode_mask)
815
+ when '-'
816
+ mode & ~(user_mask & mode_mask)
817
+ end
818
+ end
819
+ private_module_function :apply_mask
820
+
821
+ def symbolic_modes_to_i(mode_sym, path) #:nodoc:
822
+ mode = if File::Stat === path
823
+ path.mode
824
+ else
825
+ File.stat(path).mode
826
+ end
827
+ mode_sym.split(/,/).inject(mode & 07777) do |current_mode, clause|
828
+ target, *actions = clause.split(/([=+-])/)
829
+ raise ArgumentError, "invalid file mode: #{mode_sym}" if actions.empty?
830
+ target = 'a' if target.empty?
831
+ user_mask = user_mask(target)
832
+ actions.each_slice(2) do |op, perm|
833
+ need_apply = op == '='
834
+ mode_mask = (perm || '').each_char.inject(0) do |mask, chr|
835
+ case chr
836
+ when "r"
837
+ mask | 0444
838
+ when "w"
839
+ mask | 0222
840
+ when "x"
841
+ mask | 0111
842
+ when "X"
843
+ if FileTest.directory? path
844
+ mask | 0111
845
+ else
846
+ mask
847
+ end
848
+ when "s"
849
+ mask | 06000
850
+ when "t"
851
+ mask | 01000
852
+ when "u", "g", "o"
853
+ if mask.nonzero?
854
+ current_mode = apply_mask(current_mode, user_mask, op, mask)
855
+ end
856
+ need_apply = false
857
+ copy_mask = user_mask(chr)
858
+ (current_mode & copy_mask) / (copy_mask & 0111) * (user_mask & 0111)
859
+ else
860
+ raise ArgumentError, "invalid `perm' symbol in file mode: #{chr}"
861
+ end
862
+ end
863
+
864
+ if mode_mask.nonzero? || need_apply
865
+ current_mode = apply_mask(current_mode, user_mask, op, mode_mask)
866
+ end
867
+ end
868
+ current_mode
869
+ end
870
+ end
871
+ private_module_function :symbolic_modes_to_i
872
+
873
+ def fu_mode(mode, path) #:nodoc:
874
+ mode.is_a?(String) ? symbolic_modes_to_i(mode, path) : mode
875
+ end
876
+ private_module_function :fu_mode
877
+
878
+ def mode_to_s(mode) #:nodoc:
879
+ mode.is_a?(String) ? mode : "%o" % mode
880
+ end
881
+ private_module_function :mode_to_s
882
+
883
+ #
884
+ # Changes permission bits on the named files (in +list+) to the bit pattern
885
+ # represented by +mode+.
886
+ #
887
+ # +mode+ is the symbolic and absolute mode can be used.
888
+ #
889
+ # Absolute mode is
890
+ # Bundler::FileUtils.chmod 0755, 'somecommand'
891
+ # Bundler::FileUtils.chmod 0644, %w(my.rb your.rb his.rb her.rb)
892
+ # Bundler::FileUtils.chmod 0755, '/usr/bin/ruby', :verbose => true
893
+ #
894
+ # Symbolic mode is
895
+ # Bundler::FileUtils.chmod "u=wrx,go=rx", 'somecommand'
896
+ # Bundler::FileUtils.chmod "u=wr,go=rr", %w(my.rb your.rb his.rb her.rb)
897
+ # Bundler::FileUtils.chmod "u=wrx,go=rx", '/usr/bin/ruby', :verbose => true
898
+ #
899
+ # "a" :: is user, group, other mask.
900
+ # "u" :: is user's mask.
901
+ # "g" :: is group's mask.
902
+ # "o" :: is other's mask.
903
+ # "w" :: is write permission.
904
+ # "r" :: is read permission.
905
+ # "x" :: is execute permission.
906
+ # "X" ::
907
+ # is execute permission for directories only, must be used in conjunction with "+"
908
+ # "s" :: is uid, gid.
909
+ # "t" :: is sticky bit.
910
+ # "+" :: is added to a class given the specified mode.
911
+ # "-" :: Is removed from a given class given mode.
912
+ # "=" :: Is the exact nature of the class will be given a specified mode.
913
+
914
+ def chmod(mode, list, noop: nil, verbose: nil)
915
+ list = fu_list(list)
916
+ fu_output_message sprintf('chmod %s %s', mode_to_s(mode), list.join(' ')) if verbose
917
+ return if noop
918
+ list.each do |path|
919
+ Entry_.new(path).chmod(fu_mode(mode, path))
920
+ end
921
+ end
922
+ module_function :chmod
923
+
924
+ #
925
+ # Changes permission bits on the named files (in +list+)
926
+ # to the bit pattern represented by +mode+.
927
+ #
928
+ # Bundler::FileUtils.chmod_R 0700, "/tmp/app.#{$$}"
929
+ # Bundler::FileUtils.chmod_R "u=wrx", "/tmp/app.#{$$}"
930
+ #
931
+ def chmod_R(mode, list, noop: nil, verbose: nil, force: nil)
932
+ list = fu_list(list)
933
+ fu_output_message sprintf('chmod -R%s %s %s',
934
+ (force ? 'f' : ''),
935
+ mode_to_s(mode), list.join(' ')) if verbose
936
+ return if noop
937
+ list.each do |root|
938
+ Entry_.new(root).traverse do |ent|
939
+ begin
940
+ ent.chmod(fu_mode(mode, ent.path))
941
+ rescue
942
+ raise unless force
943
+ end
944
+ end
945
+ end
946
+ end
947
+ module_function :chmod_R
948
+
949
+ #
950
+ # Changes owner and group on the named files (in +list+)
951
+ # to the user +user+ and the group +group+. +user+ and +group+
952
+ # may be an ID (Integer/String) or a name (String).
953
+ # If +user+ or +group+ is nil, this method does not change
954
+ # the attribute.
955
+ #
956
+ # Bundler::FileUtils.chown 'root', 'staff', '/usr/local/bin/ruby'
957
+ # Bundler::FileUtils.chown nil, 'bin', Dir.glob('/usr/bin/*'), :verbose => true
958
+ #
959
+ def chown(user, group, list, noop: nil, verbose: nil)
960
+ list = fu_list(list)
961
+ fu_output_message sprintf('chown %s %s',
962
+ (group ? "#{user}:#{group}" : user || ':'),
963
+ list.join(' ')) if verbose
964
+ return if noop
965
+ uid = fu_get_uid(user)
966
+ gid = fu_get_gid(group)
967
+ list.each do |path|
968
+ Entry_.new(path).chown uid, gid
969
+ end
970
+ end
971
+ module_function :chown
972
+
973
+ #
974
+ # Changes owner and group on the named files (in +list+)
975
+ # to the user +user+ and the group +group+ recursively.
976
+ # +user+ and +group+ may be an ID (Integer/String) or
977
+ # a name (String). If +user+ or +group+ is nil, this
978
+ # method does not change the attribute.
979
+ #
980
+ # Bundler::FileUtils.chown_R 'www', 'www', '/var/www/htdocs'
981
+ # Bundler::FileUtils.chown_R 'cvs', 'cvs', '/var/cvs', :verbose => true
982
+ #
983
+ def chown_R(user, group, list, noop: nil, verbose: nil, force: nil)
984
+ list = fu_list(list)
985
+ fu_output_message sprintf('chown -R%s %s %s',
986
+ (force ? 'f' : ''),
987
+ (group ? "#{user}:#{group}" : user || ':'),
988
+ list.join(' ')) if verbose
989
+ return if noop
990
+ uid = fu_get_uid(user)
991
+ gid = fu_get_gid(group)
992
+ list.each do |root|
993
+ Entry_.new(root).traverse do |ent|
994
+ begin
995
+ ent.chown uid, gid
996
+ rescue
997
+ raise unless force
998
+ end
999
+ end
1000
+ end
1001
+ end
1002
+ module_function :chown_R
1003
+
1004
+ begin
1005
+ require 'etc'
1006
+ rescue LoadError # rescue LoadError for miniruby
1007
+ end
1008
+
1009
+ def fu_get_uid(user) #:nodoc:
1010
+ return nil unless user
1011
+ case user
1012
+ when Integer
1013
+ user
1014
+ when /\A\d+\z/
1015
+ user.to_i
1016
+ else
1017
+ Etc.getpwnam(user) ? Etc.getpwnam(user).uid : nil
1018
+ end
1019
+ end
1020
+ private_module_function :fu_get_uid
1021
+
1022
+ def fu_get_gid(group) #:nodoc:
1023
+ return nil unless group
1024
+ case group
1025
+ when Integer
1026
+ group
1027
+ when /\A\d+\z/
1028
+ group.to_i
1029
+ else
1030
+ Etc.getgrnam(group) ? Etc.getgrnam(group).gid : nil
1031
+ end
1032
+ end
1033
+ private_module_function :fu_get_gid
1034
+
1035
+ #
1036
+ # Updates modification time (mtime) and access time (atime) of file(s) in
1037
+ # +list+. Files are created if they don't exist.
1038
+ #
1039
+ # Bundler::FileUtils.touch 'timestamp'
1040
+ # Bundler::FileUtils.touch Dir.glob('*.c'); system 'make'
1041
+ #
1042
+ def touch(list, noop: nil, verbose: nil, mtime: nil, nocreate: nil)
1043
+ list = fu_list(list)
1044
+ t = mtime
1045
+ if verbose
1046
+ fu_output_message "touch #{nocreate ? '-c ' : ''}#{t ? t.strftime('-t %Y%m%d%H%M.%S ') : ''}#{list.join ' '}"
1047
+ end
1048
+ return if noop
1049
+ list.each do |path|
1050
+ created = nocreate
1051
+ begin
1052
+ File.utime(t, t, path)
1053
+ rescue Errno::ENOENT
1054
+ raise if created
1055
+ File.open(path, 'a') {
1056
+ ;
1057
+ }
1058
+ created = true
1059
+ retry if t
1060
+ end
1061
+ end
1062
+ end
1063
+ module_function :touch
1064
+
1065
+ private
1066
+
1067
+ module StreamUtils_
1068
+ private
1069
+
1070
+ def fu_windows?
1071
+ /mswin|mingw|bccwin|emx/ =~ RUBY_PLATFORM
1072
+ end
1073
+
1074
+ def fu_copy_stream0(src, dest, blksize = nil) #:nodoc:
1075
+ IO.copy_stream(src, dest)
1076
+ end
1077
+
1078
+ def fu_stream_blksize(*streams)
1079
+ streams.each do |s|
1080
+ next unless s.respond_to?(:stat)
1081
+ size = fu_blksize(s.stat)
1082
+ return size if size
1083
+ end
1084
+ fu_default_blksize()
1085
+ end
1086
+
1087
+ def fu_blksize(st)
1088
+ s = st.blksize
1089
+ return nil unless s
1090
+ return nil if s == 0
1091
+ s
1092
+ end
1093
+
1094
+ def fu_default_blksize
1095
+ 1024
1096
+ end
1097
+ end
1098
+
1099
+ include StreamUtils_
1100
+ extend StreamUtils_
1101
+
1102
+ class Entry_ #:nodoc: internal use only
1103
+ include StreamUtils_
1104
+
1105
+ def initialize(a, b = nil, deref = false)
1106
+ @prefix = @rel = @path = nil
1107
+ if b
1108
+ @prefix = a
1109
+ @rel = b
1110
+ else
1111
+ @path = a
1112
+ end
1113
+ @deref = deref
1114
+ @stat = nil
1115
+ @lstat = nil
1116
+ end
1117
+
1118
+ def inspect
1119
+ "\#<#{self.class} #{path()}>"
1120
+ end
1121
+
1122
+ def path
1123
+ if @path
1124
+ File.path(@path)
1125
+ else
1126
+ join(@prefix, @rel)
1127
+ end
1128
+ end
1129
+
1130
+ def prefix
1131
+ @prefix || @path
1132
+ end
1133
+
1134
+ def rel
1135
+ @rel
1136
+ end
1137
+
1138
+ def dereference?
1139
+ @deref
1140
+ end
1141
+
1142
+ def exist?
1143
+ begin
1144
+ lstat
1145
+ true
1146
+ rescue Errno::ENOENT
1147
+ false
1148
+ end
1149
+ end
1150
+
1151
+ def file?
1152
+ s = lstat!
1153
+ s and s.file?
1154
+ end
1155
+
1156
+ def directory?
1157
+ s = lstat!
1158
+ s and s.directory?
1159
+ end
1160
+
1161
+ def symlink?
1162
+ s = lstat!
1163
+ s and s.symlink?
1164
+ end
1165
+
1166
+ def chardev?
1167
+ s = lstat!
1168
+ s and s.chardev?
1169
+ end
1170
+
1171
+ def blockdev?
1172
+ s = lstat!
1173
+ s and s.blockdev?
1174
+ end
1175
+
1176
+ def socket?
1177
+ s = lstat!
1178
+ s and s.socket?
1179
+ end
1180
+
1181
+ def pipe?
1182
+ s = lstat!
1183
+ s and s.pipe?
1184
+ end
1185
+
1186
+ S_IF_DOOR = 0xD000
1187
+
1188
+ def door?
1189
+ s = lstat!
1190
+ s and (s.mode & 0xF000 == S_IF_DOOR)
1191
+ end
1192
+
1193
+ def entries
1194
+ opts = {}
1195
+ opts[:encoding] = ::Encoding::UTF_8 if fu_windows?
1196
+ Dir.entries(path(), opts)\
1197
+ .reject {|n| n == '.' or n == '..' }\
1198
+ .map {|n| Entry_.new(prefix(), join(rel(), n.untaint)) }
1199
+ end
1200
+
1201
+ def stat
1202
+ return @stat if @stat
1203
+ if lstat() and lstat().symlink?
1204
+ @stat = File.stat(path())
1205
+ else
1206
+ @stat = lstat()
1207
+ end
1208
+ @stat
1209
+ end
1210
+
1211
+ def stat!
1212
+ return @stat if @stat
1213
+ if lstat! and lstat!.symlink?
1214
+ @stat = File.stat(path())
1215
+ else
1216
+ @stat = lstat!
1217
+ end
1218
+ @stat
1219
+ rescue SystemCallError
1220
+ nil
1221
+ end
1222
+
1223
+ def lstat
1224
+ if dereference?
1225
+ @lstat ||= File.stat(path())
1226
+ else
1227
+ @lstat ||= File.lstat(path())
1228
+ end
1229
+ end
1230
+
1231
+ def lstat!
1232
+ lstat()
1233
+ rescue SystemCallError
1234
+ nil
1235
+ end
1236
+
1237
+ def chmod(mode)
1238
+ if symlink?
1239
+ File.lchmod mode, path() if have_lchmod?
1240
+ else
1241
+ File.chmod mode, path()
1242
+ end
1243
+ end
1244
+
1245
+ def chown(uid, gid)
1246
+ if symlink?
1247
+ File.lchown uid, gid, path() if have_lchown?
1248
+ else
1249
+ File.chown uid, gid, path()
1250
+ end
1251
+ end
1252
+
1253
+ def copy(dest)
1254
+ lstat
1255
+ case
1256
+ when file?
1257
+ copy_file dest
1258
+ when directory?
1259
+ if !File.exist?(dest) and descendant_directory?(dest, path)
1260
+ raise ArgumentError, "cannot copy directory %s to itself %s" % [path, dest]
1261
+ end
1262
+ begin
1263
+ Dir.mkdir dest
1264
+ rescue
1265
+ raise unless File.directory?(dest)
1266
+ end
1267
+ when symlink?
1268
+ File.symlink File.readlink(path()), dest
1269
+ when chardev?
1270
+ raise "cannot handle device file" unless File.respond_to?(:mknod)
1271
+ mknod dest, ?c, 0666, lstat().rdev
1272
+ when blockdev?
1273
+ raise "cannot handle device file" unless File.respond_to?(:mknod)
1274
+ mknod dest, ?b, 0666, lstat().rdev
1275
+ when socket?
1276
+ raise "cannot handle socket" unless File.respond_to?(:mknod)
1277
+ mknod dest, nil, lstat().mode, 0
1278
+ when pipe?
1279
+ raise "cannot handle FIFO" unless File.respond_to?(:mkfifo)
1280
+ mkfifo dest, 0666
1281
+ when door?
1282
+ raise "cannot handle door: #{path()}"
1283
+ else
1284
+ raise "unknown file type: #{path()}"
1285
+ end
1286
+ end
1287
+
1288
+ def copy_file(dest)
1289
+ File.open(path()) do |s|
1290
+ File.open(dest, 'wb', s.stat.mode) do |f|
1291
+ IO.copy_stream(s, f)
1292
+ end
1293
+ end
1294
+ end
1295
+
1296
+ def copy_metadata(path)
1297
+ st = lstat()
1298
+ if !st.symlink?
1299
+ File.utime st.atime, st.mtime, path
1300
+ end
1301
+ mode = st.mode
1302
+ begin
1303
+ if st.symlink?
1304
+ begin
1305
+ File.lchown st.uid, st.gid, path
1306
+ rescue NotImplementedError
1307
+ end
1308
+ else
1309
+ File.chown st.uid, st.gid, path
1310
+ end
1311
+ rescue Errno::EPERM, Errno::EACCES
1312
+ # clear setuid/setgid
1313
+ mode &= 01777
1314
+ end
1315
+ if st.symlink?
1316
+ begin
1317
+ File.lchmod mode, path
1318
+ rescue NotImplementedError
1319
+ end
1320
+ else
1321
+ File.chmod mode, path
1322
+ end
1323
+ end
1324
+
1325
+ def remove
1326
+ if directory?
1327
+ remove_dir1
1328
+ else
1329
+ remove_file
1330
+ end
1331
+ end
1332
+
1333
+ def remove_dir1
1334
+ platform_support {
1335
+ Dir.rmdir path().chomp(?/)
1336
+ }
1337
+ end
1338
+
1339
+ def remove_file
1340
+ platform_support {
1341
+ File.unlink path
1342
+ }
1343
+ end
1344
+
1345
+ def platform_support
1346
+ return yield unless fu_windows?
1347
+ first_time_p = true
1348
+ begin
1349
+ yield
1350
+ rescue Errno::ENOENT
1351
+ raise
1352
+ rescue => err
1353
+ if first_time_p
1354
+ first_time_p = false
1355
+ begin
1356
+ File.chmod 0700, path() # Windows does not have symlink
1357
+ retry
1358
+ rescue SystemCallError
1359
+ end
1360
+ end
1361
+ raise err
1362
+ end
1363
+ end
1364
+
1365
+ def preorder_traverse
1366
+ stack = [self]
1367
+ while ent = stack.pop
1368
+ yield ent
1369
+ stack.concat ent.entries.reverse if ent.directory?
1370
+ end
1371
+ end
1372
+
1373
+ alias traverse preorder_traverse
1374
+
1375
+ def postorder_traverse
1376
+ if directory?
1377
+ entries().each do |ent|
1378
+ ent.postorder_traverse do |e|
1379
+ yield e
1380
+ end
1381
+ end
1382
+ end
1383
+ ensure
1384
+ yield self
1385
+ end
1386
+
1387
+ def wrap_traverse(pre, post)
1388
+ pre.call self
1389
+ if directory?
1390
+ entries.each do |ent|
1391
+ ent.wrap_traverse pre, post
1392
+ end
1393
+ end
1394
+ post.call self
1395
+ end
1396
+
1397
+ private
1398
+
1399
+ $fileutils_rb_have_lchmod = nil
1400
+
1401
+ def have_lchmod?
1402
+ # This is not MT-safe, but it does not matter.
1403
+ if $fileutils_rb_have_lchmod == nil
1404
+ $fileutils_rb_have_lchmod = check_have_lchmod?
1405
+ end
1406
+ $fileutils_rb_have_lchmod
1407
+ end
1408
+
1409
+ def check_have_lchmod?
1410
+ return false unless File.respond_to?(:lchmod)
1411
+ File.lchmod 0
1412
+ return true
1413
+ rescue NotImplementedError
1414
+ return false
1415
+ end
1416
+
1417
+ $fileutils_rb_have_lchown = nil
1418
+
1419
+ def have_lchown?
1420
+ # This is not MT-safe, but it does not matter.
1421
+ if $fileutils_rb_have_lchown == nil
1422
+ $fileutils_rb_have_lchown = check_have_lchown?
1423
+ end
1424
+ $fileutils_rb_have_lchown
1425
+ end
1426
+
1427
+ def check_have_lchown?
1428
+ return false unless File.respond_to?(:lchown)
1429
+ File.lchown nil, nil
1430
+ return true
1431
+ rescue NotImplementedError
1432
+ return false
1433
+ end
1434
+
1435
+ def join(dir, base)
1436
+ return File.path(dir) if not base or base == '.'
1437
+ return File.path(base) if not dir or dir == '.'
1438
+ File.join(dir, base)
1439
+ end
1440
+
1441
+ if File::ALT_SEPARATOR
1442
+ DIRECTORY_TERM = "(?=[/#{Regexp.quote(File::ALT_SEPARATOR)}]|\\z)"
1443
+ else
1444
+ DIRECTORY_TERM = "(?=/|\\z)"
1445
+ end
1446
+ SYSCASE = File::FNM_SYSCASE.nonzero? ? "-i" : ""
1447
+
1448
+ def descendant_directory?(descendant, ascendant)
1449
+ /\A(?#{SYSCASE}:#{Regexp.quote(ascendant)})#{DIRECTORY_TERM}/ =~ File.dirname(descendant)
1450
+ end
1451
+ end # class Entry_
1452
+
1453
+ def fu_list(arg) #:nodoc:
1454
+ [arg].flatten.map {|path| File.path(path) }
1455
+ end
1456
+ private_module_function :fu_list
1457
+
1458
+ def fu_each_src_dest(src, dest) #:nodoc:
1459
+ fu_each_src_dest0(src, dest) do |s, d|
1460
+ raise ArgumentError, "same file: #{s} and #{d}" if fu_same?(s, d)
1461
+ yield s, d
1462
+ end
1463
+ end
1464
+ private_module_function :fu_each_src_dest
1465
+
1466
+ def fu_each_src_dest0(src, dest) #:nodoc:
1467
+ if tmp = Array.try_convert(src)
1468
+ tmp.each do |s|
1469
+ s = File.path(s)
1470
+ yield s, File.join(dest, File.basename(s))
1471
+ end
1472
+ else
1473
+ src = File.path(src)
1474
+ if File.directory?(dest)
1475
+ yield src, File.join(dest, File.basename(src))
1476
+ else
1477
+ yield src, File.path(dest)
1478
+ end
1479
+ end
1480
+ end
1481
+ private_module_function :fu_each_src_dest0
1482
+
1483
+ def fu_same?(a, b) #:nodoc:
1484
+ File.identical?(a, b)
1485
+ end
1486
+ private_module_function :fu_same?
1487
+
1488
+ @fileutils_output = $stderr
1489
+ @fileutils_label = ''
1490
+
1491
+ def fu_output_message(msg) #:nodoc:
1492
+ @fileutils_output ||= $stderr
1493
+ @fileutils_label ||= ''
1494
+ @fileutils_output.puts @fileutils_label + msg
1495
+ end
1496
+ private_module_function :fu_output_message
1497
+
1498
+ # This hash table holds command options.
1499
+ OPT_TABLE = {} #:nodoc: internal use only
1500
+ (private_instance_methods & methods(false)).inject(OPT_TABLE) {|tbl, name|
1501
+ (tbl[name.to_s] = instance_method(name).parameters).map! {|t, n| n if t == :key}.compact!
1502
+ tbl
1503
+ }
1504
+
1505
+ #
1506
+ # Returns an Array of method names which have any options.
1507
+ #
1508
+ # p Bundler::FileUtils.commands #=> ["chmod", "cp", "cp_r", "install", ...]
1509
+ #
1510
+ def self.commands
1511
+ OPT_TABLE.keys
1512
+ end
1513
+
1514
+ #
1515
+ # Returns an Array of option names.
1516
+ #
1517
+ # p Bundler::FileUtils.options #=> ["noop", "force", "verbose", "preserve", "mode"]
1518
+ #
1519
+ def self.options
1520
+ OPT_TABLE.values.flatten.uniq.map {|sym| sym.to_s }
1521
+ end
1522
+
1523
+ #
1524
+ # Returns true if the method +mid+ have an option +opt+.
1525
+ #
1526
+ # p Bundler::FileUtils.have_option?(:cp, :noop) #=> true
1527
+ # p Bundler::FileUtils.have_option?(:rm, :force) #=> true
1528
+ # p Bundler::FileUtils.have_option?(:rm, :preserve) #=> false
1529
+ #
1530
+ def self.have_option?(mid, opt)
1531
+ li = OPT_TABLE[mid.to_s] or raise ArgumentError, "no such method: #{mid}"
1532
+ li.include?(opt)
1533
+ end
1534
+
1535
+ #
1536
+ # Returns an Array of option names of the method +mid+.
1537
+ #
1538
+ # p Bundler::FileUtils.options_of(:rm) #=> ["noop", "verbose", "force"]
1539
+ #
1540
+ def self.options_of(mid)
1541
+ OPT_TABLE[mid.to_s].map {|sym| sym.to_s }
1542
+ end
1543
+
1544
+ #
1545
+ # Returns an Array of method names which have the option +opt+.
1546
+ #
1547
+ # p Bundler::FileUtils.collect_method(:preserve) #=> ["cp", "cp_r", "copy", "install"]
1548
+ #
1549
+ def self.collect_method(opt)
1550
+ OPT_TABLE.keys.select {|m| OPT_TABLE[m].include?(opt) }
1551
+ end
1552
+
1553
+ LOW_METHODS = singleton_methods(false) - collect_method(:noop).map(&:intern)
1554
+ module LowMethods
1555
+ private
1556
+ def _do_nothing(*)end
1557
+ ::Bundler::FileUtils::LOW_METHODS.map {|name| alias_method name, :_do_nothing}
1558
+ end
1559
+
1560
+ METHODS = singleton_methods() - [:private_module_function,
1561
+ :commands, :options, :have_option?, :options_of, :collect_method]
1562
+
1563
+ #
1564
+ # This module has all methods of Bundler::FileUtils module, but it outputs messages
1565
+ # before acting. This equates to passing the <tt>:verbose</tt> flag to
1566
+ # methods in Bundler::FileUtils.
1567
+ #
1568
+ module Verbose
1569
+ include Bundler::FileUtils
1570
+ @fileutils_output = $stderr
1571
+ @fileutils_label = ''
1572
+ names = ::Bundler::FileUtils.collect_method(:verbose)
1573
+ names.each do |name|
1574
+ module_eval(<<-EOS, __FILE__, __LINE__ + 1)
1575
+ def #{name}(*args, **options)
1576
+ super(*args, **options, verbose: true)
1577
+ end
1578
+ EOS
1579
+ end
1580
+ private(*names)
1581
+ extend self
1582
+ class << self
1583
+ public(*::Bundler::FileUtils::METHODS)
1584
+ end
1585
+ end
1586
+
1587
+ #
1588
+ # This module has all methods of Bundler::FileUtils module, but never changes
1589
+ # files/directories. This equates to passing the <tt>:noop</tt> flag
1590
+ # to methods in Bundler::FileUtils.
1591
+ #
1592
+ module NoWrite
1593
+ include Bundler::FileUtils
1594
+ include LowMethods
1595
+ @fileutils_output = $stderr
1596
+ @fileutils_label = ''
1597
+ names = ::Bundler::FileUtils.collect_method(:noop)
1598
+ names.each do |name|
1599
+ module_eval(<<-EOS, __FILE__, __LINE__ + 1)
1600
+ def #{name}(*args, **options)
1601
+ super(*args, **options, noop: true)
1602
+ end
1603
+ EOS
1604
+ end
1605
+ private(*names)
1606
+ extend self
1607
+ class << self
1608
+ public(*::Bundler::FileUtils::METHODS)
1609
+ end
1610
+ end
1611
+
1612
+ #
1613
+ # This module has all methods of Bundler::FileUtils module, but never changes
1614
+ # files/directories, with printing message before acting.
1615
+ # This equates to passing the <tt>:noop</tt> and <tt>:verbose</tt> flag
1616
+ # to methods in Bundler::FileUtils.
1617
+ #
1618
+ module DryRun
1619
+ include Bundler::FileUtils
1620
+ include LowMethods
1621
+ @fileutils_output = $stderr
1622
+ @fileutils_label = ''
1623
+ names = ::Bundler::FileUtils.collect_method(:noop)
1624
+ names.each do |name|
1625
+ module_eval(<<-EOS, __FILE__, __LINE__ + 1)
1626
+ def #{name}(*args, **options)
1627
+ super(*args, **options, noop: true, verbose: true)
1628
+ end
1629
+ EOS
1630
+ end
1631
+ private(*names)
1632
+ extend self
1633
+ class << self
1634
+ public(*::Bundler::FileUtils::METHODS)
1635
+ end
1636
+ end
1637
+
1638
+ end