# frozen_string_literal: true
# Copyright (c) 2000-2007 Minero Aoki
# This program is free software.
# You can distribute/modify this program under the same terms of ruby.
# Namespace for several file utility methods for copying, moving, removing, etc.
# FileUtils.cd(dir, options)
# FileUtils.cd(dir, options) {|dir| block }
# FileUtils.mkdir(dir, options)
# FileUtils.mkdir(list, options)
# FileUtils.mkdir_p(dir, options)
# FileUtils.mkdir_p(list, options)
# FileUtils.rmdir(dir, options)
# FileUtils.rmdir(list, options)
# FileUtils.ln(target, link, options)
# FileUtils.ln(targets, dir, options)
# FileUtils.ln_s(target, link, options)
# FileUtils.ln_s(targets, dir, options)
# FileUtils.ln_sf(target, link, options)
# FileUtils.cp(src, dest, options)
# FileUtils.cp(list, dir, options)
# FileUtils.cp_r(src, dest, options)
# FileUtils.cp_r(list, dir, options)
# FileUtils.mv(src, dest, options)
# FileUtils.mv(list, dir, options)
# FileUtils.rm(list, options)
# FileUtils.rm_r(list, options)
# FileUtils.rm_rf(list, options)
# FileUtils.install(src, dest, options)
# FileUtils.chmod(mode, list, options)
# FileUtils.chmod_R(mode, list, options)
# FileUtils.chown(user, group, list, options)
# FileUtils.chown_R(user, group, list, options)
# FileUtils.touch(list, options)
# The <tt>options</tt> parameter is a hash of options, taken from the list
# <tt>:force</tt>, <tt>:noop</tt>, <tt>:preserve</tt>, and <tt>:verbose</tt>.
# <tt>:noop</tt> means that no changes are made. The other three are obvious.
# Each method documents the options that it honours.
# All methods that have the concept of a "source" file or directory can take
# either one file or a list of files in that argument. See the method
# documentation for examples.
# There are some `low level' methods, which do not accept any option:
# FileUtils.copy_entry(src, dest, preserve = false, dereference = false)
# FileUtils.copy_file(src, dest, preserve = false, dereference = true)
# FileUtils.copy_stream(srcstream, deststream)
# FileUtils.remove_entry(path, force = false)
# FileUtils.remove_entry_secure(path, force = false)
# FileUtils.remove_file(path, force = false)
# FileUtils.compare_file(path_a, path_b)
# FileUtils.compare_stream(stream_a, stream_b)
# FileUtils.uptodate?(file, cmp_list)
# == module FileUtils::Verbose
# This module has all methods of FileUtils module, but it outputs messages
# before acting. This equates to passing the <tt>:verbose</tt> flag to methods
# == module FileUtils::NoWrite
# This module has all methods of FileUtils module, but never changes
# files/directories. This equates to passing the <tt>:noop</tt> flag to methods
# == module FileUtils::DryRun
# This module has all methods of FileUtils module, but never changes
# files/directories. This equates to passing the <tt>:noop</tt> and
# <tt>:verbose</tt> flags to methods in FileUtils.
def self.private_module_function(name) #:nodoc:
private_class_method name
# Returns the name of the current directory.
# Changes the current directory to the directory +dir+.
# If this method is called with block, resumes to the old
# working directory after the block execution finished.
# FileUtils.cd('/', :verbose => true) # chdir and report it
# FileUtils.cd('/') do # chdir
# end # return to original directory
def cd(dir, verbose: nil, &block) # :yield: dir
fu_output_message "cd #{dir}" if verbose
fu_output_message 'cd -' if verbose and block
# Returns true if +new+ is newer than all +old_list+.
# Non-existent files are older than any file.
# FileUtils.uptodate?('hello.o', %w(hello.c hello.h)) or \
def uptodate?(new, old_list)
return false unless File.exist?(new)
new_time = File.mtime(new)
return false unless new_time > File.mtime(old)
module_function :uptodate?
def remove_trailing_slash(dir) #:nodoc:
dir == '/' ? dir : dir.chomp(?/)
private_module_function :remove_trailing_slash
# Creates one or more directories.
# FileUtils.mkdir %w( tmp data )
# FileUtils.mkdir 'notexist', :noop => true # Does not really create.
# FileUtils.mkdir 'tmp', :mode => 0700
def mkdir(list, mode: nil, noop: nil, verbose: nil)
fu_output_message "mkdir #{mode ? ('-m %03o ' % mode) : ''}#{list.join ' '}" if verbose
# Creates a directory and all its parent directories.
# FileUtils.mkdir_p '/usr/local/lib/ruby'
# causes to make following directories, if it does not exist.
# You can pass several directories at a time in a list.
def mkdir_p(list, mode: nil, noop: nil, verbose: nil)
fu_output_message "mkdir -p #{mode ? ('-m %03o ' % mode) : ''}#{list.join ' '}" if verbose
list.map {|path| remove_trailing_slash(path)}.each do |path|
# optimize for the most common case
next if File.directory?(path)
until path == stack.last # dirname("/")=="/", dirname("C:/")=="C:/"
path = File.dirname(path)
stack.pop # root directory should exist
stack.reverse_each do |dir|
raise unless File.directory?(dir)
module_function :makedirs
def fu_mkdir(path, mode) #:nodoc:
path = remove_trailing_slash(path)
private_module_function :fu_mkdir
# Removes one or more directories.
# FileUtils.rmdir 'somedir'
# FileUtils.rmdir %w(somedir anydir otherdir)
# # Does not really remove directory; outputs message.
# FileUtils.rmdir 'somedir', :verbose => true, :noop => true
def rmdir(list, parents: nil, noop: nil, verbose: nil)
fu_output_message "rmdir #{parents ? '-p ' : ''}#{list.join ' '}" if verbose
Dir.rmdir(dir = remove_trailing_slash(dir))
until (parent = File.dirname(dir)) == '.' or parent == dir
rescue Errno::ENOTEMPTY, Errno::EEXIST, Errno::ENOENT
# FileUtils.ln(target, link, force: nil, noop: nil, verbose: nil)
# FileUtils.ln(target, dir, force: nil, noop: nil, verbose: nil)
# FileUtils.ln(targets, dir, force: nil, noop: nil, verbose: nil)
# In the first form, creates a hard link +link+ which points to +target+.
# If +link+ already exists, raises Errno::EEXIST.
# But if the :force option is set, overwrites +link+.
# FileUtils.ln 'gcc', 'cc', verbose: true
# FileUtils.ln '/usr/bin/emacs21', '/usr/bin/emacs'
# In the second form, creates a link +dir/target+ pointing to +target+.
# In the third form, creates several hard links in the directory +dir+,
# pointing to each item in +targets+.
# If +dir+ is not a directory, raises Errno::ENOTDIR.
# FileUtils.ln %w(cp mv mkdir), '/bin' # Now /sbin/cp and /bin/cp are linked.
def ln(src, dest, force: nil, noop: nil, verbose: nil)
fu_output_message "ln#{force ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if verbose
fu_each_src_dest0(src, dest) do |s,d|
remove_file d, true if force
# FileUtils.ln_s(target, link, force: nil, noop: nil, verbose: nil)
# FileUtils.ln_s(target, dir, force: nil, noop: nil, verbose: nil)
# FileUtils.ln_s(targets, dir, force: nil, noop: nil, verbose: nil)
# In the first form, creates a symbolic link +link+ which points to +target+.
# If +link+ already exists, raises Errno::EEXIST.
# But if the :force option is set, overwrites +link+.
# FileUtils.ln_s '/usr/bin/ruby', '/usr/local/bin/ruby'
# FileUtils.ln_s 'verylongsourcefilename.c', 'c', force: true
# In the second form, creates a link +dir/target+ pointing to +target+.
# In the third form, creates several symbolic links in the directory +dir+,
# pointing to each item in +targets+.
# If +dir+ is not a directory, raises Errno::ENOTDIR.
# FileUtils.ln_s Dir.glob('/bin/*.rb'), '/home/foo/bin'
def ln_s(src, dest, force: nil, noop: nil, verbose: nil)
fu_output_message "ln -s#{force ? 'f' : ''} #{[src,dest].flatten.join ' '}" if verbose
fu_each_src_dest0(src, dest) do |s,d|
remove_file d, true if force
# FileUtils.ln_s(*args, force: true)
def ln_sf(src, dest, noop: nil, verbose: nil)
ln_s src, dest, force: true, noop: noop, verbose: verbose
# Copies a file content +src+ to +dest+. If +dest+ is a directory,
# copies +src+ to +dest/src+.
# If +src+ is a list of files, then +dest+ must be a directory.
# FileUtils.cp 'eval.c', 'eval.c.org'
# FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6'
# FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6', :verbose => true
# FileUtils.cp 'symlink', 'dest' # copy content, "dest" is not a symlink
def cp(src, dest, preserve: nil, noop: nil, verbose: nil)
fu_output_message "cp#{preserve ? ' -p' : ''} #{[src,dest].flatten.join ' '}" if verbose
fu_each_src_dest(src, dest) do |s, d|
# Copies +src+ to +dest+. If +src+ is a directory, this method copies
# all its contents recursively. If +dest+ is a directory, copies
# +src+ can be a list of files.
# # Installing Ruby library "mylib" under the site_ruby
# FileUtils.rm_r site_ruby + '/mylib', :force
# FileUtils.cp_r 'lib/', site_ruby + '/mylib'
# # Examples of copying several files to target directory.
# FileUtils.cp_r %w(mail.rb field.rb debug/), site_ruby + '/tmail'
# FileUtils.cp_r Dir.glob('*.rb'), '/home/foo/lib/ruby', :noop => true, :verbose => true
# # If you want to copy all contents of a directory instead of the
# # directory itself, c.f. src/x -> dest/x, src/y -> dest/y,
# FileUtils.cp_r 'src/.', 'dest' # cp_r('src', 'dest') makes dest/src,
def cp_r(src, dest, preserve: nil, noop: nil, verbose: nil,
dereference_root: true, remove_destination: nil)
fu_output_message "cp -r#{preserve ? 'p' : ''}#{remove_destination ? ' --remove-destination' : ''} #{[src,dest].flatten.join ' '}" if verbose
fu_each_src_dest(src, dest) do |s, d|
copy_entry s, d, preserve, dereference_root, remove_destination
# Copies a file system entry +src+ to +dest+.
# If +src+ is a directory, this method copies its contents recursively.
# This method preserves file types, c.f. symlink, directory...
# (FIFO, device files and etc. are not supported yet)
# Both of +src+ and +dest+ must be a path name.
# +src+ must exist, +dest+ must not exist.
# If +preserve+ is true, this method preserves owner, group, and
# modified time. Permissions are copied regardless +preserve+.
# If +dereference_root+ is true, this method dereference tree root.
# If +remove_destination+ is true, this method removes each destination file before copy.
def copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = false)
Entry_.new(src, nil, dereference_root).wrap_traverse(proc do |ent|
destent = Entry_.new(dest, ent.rel, false)
File.unlink destent.path if remove_destination && (File.file?(destent.path) || File.symlink?(destent.path))
destent = Entry_.new(dest, ent.rel, false)
ent.copy_metadata destent.path if preserve
module_function :copy_entry
# Copies file contents of +src+ to +dest+.
# Both of +src+ and +dest+ must be a path name.
def copy_file(src, dest, preserve = false, dereference = true)
ent = Entry_.new(src, nil, dereference)
ent.copy_metadata dest if preserve
module_function :copy_file
# Copies stream +src+ to +dest+.
# +src+ must respond to #read(n) and
# +dest+ must respond to #write(str).
def copy_stream(src, dest)
IO.copy_stream(src, dest)
module_function :copy_stream
# Moves file(s) +src+ to +dest+. If +file+ and +dest+ exist on the different
# disk partition, the file is copied then the original file is removed.
# FileUtils.mv 'badname.rb', 'goodname.rb'
# FileUtils.mv 'stuff.rb', '/notexist/lib/ruby', :force => true # no error
# FileUtils.mv %w(junk.txt dust.txt), '/home/foo/.trash/'
# FileUtils.mv Dir.glob('test*.rb'), 'test', :noop => true, :verbose => true
def mv(src, dest, force: nil, noop: nil, verbose: nil, secure: nil)
fu_output_message "mv#{force ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if verbose
fu_each_src_dest(src, dest) do |s, d|
destent = Entry_.new(d, nil, true)
destent.remove_file if rename_cannot_overwrite_file?
remove_entry_secure s, force
def rename_cannot_overwrite_file? #:nodoc:
private_module_function :rename_cannot_overwrite_file?
# Remove file(s) specified in +list+. This method cannot remove directories.
# All StandardErrors are ignored when the :force option is set.
# FileUtils.rm %w( junk.txt dust.txt )
# FileUtils.rm Dir.glob('*.so')