# 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)
# Possible <tt>options</tt> are:
# <tt>:force</tt> :: forced operation (rewrite files if exist, remove
# directories if not empty, etc.);
# <tt>:verbose</tt> :: print command to be run, in bash syntax, before
# <tt>:preserve</tt> :: preserve object's group, user and modification
# <tt>:noop</tt> :: no changes are made (usable in combination with
# <tt>:verbose</tt> which will print the command to run)
# Each method documents the options that it honours. See also ::commands,
# ::options and ::options_of methods to introspect which command have which
# 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 keyword arguments:
# FileUtils.copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = 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 previous
# working directory after the block execution has finished.
# FileUtils.cd('/') # change directory
# FileUtils.cd('/', verbose: true) # change directory and report it
# FileUtils.cd('/') do # change directory
# end # return to original directory
def cd(dir, verbose: nil, &block) # :yield: dir
fu_output_message "cd #{dir}" if verbose
result = Dir.chdir(dir, &block)
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 they do 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
path = remove_trailing_slash(item)
# optimize for the most common case
next if File.directory?(path)
until path == stack.last # dirname("/")=="/", dirname("C:/")=="C:/"
path = File.dirname(path)
break if File.directory?(path)
stack.pop if path == stack.last # 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
# Hard link +src+ to +dest+. If +src+ is a directory, this method links
# all its contents recursively. If +dest+ is a directory, links
# +src+ can be a list of files.
# If +dereference_root+ is true, this method dereference tree root.
# If +remove_destination+ is true, this method removes each destination file before copy.
# FileUtils.rm_r site_ruby + '/mylib', force: true
# FileUtils.cp_lr 'lib/', site_ruby + '/mylib'
# # Examples of linking several files to target directory.
# FileUtils.cp_lr %w(mail.rb field.rb debug/), site_ruby + '/tmail'
# FileUtils.cp_lr Dir.glob('*.rb'), '/home/aamine/lib/ruby', noop: true, verbose: true
# # If you want to link all contents of a directory instead of the
# # directory itself, c.f. src/x -> dest/x, src/y -> dest/y,
# # use the following code.
# FileUtils.cp_lr 'src/.', 'dest' # cp_lr('src', 'dest') makes dest/src, but this doesn't.
def cp_lr(src, dest, noop: nil, verbose: nil,
dereference_root: true, remove_destination: false)
fu_output_message "cp -lr#{remove_destination ? ' --remove-destination' : ''} #{[src,dest].flatten.join ' '}" if verbose
fu_each_src_dest(src, dest) do |s, d|
link_entry s, d, dereference_root, remove_destination
# 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 <tt>force</tt> 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
# Hard links a file system entry +src+ to +dest+.
# If +src+ is a directory, this method links its contents recursively.
# Both of +src+ and +dest+ must be a path name.
# +src+ must exist, +dest+ must not exist.
# If +dereference_root+ is true, this method dereferences the tree root.
# If +remove_destination+ is true, this method removes each destination file before copy.
def link_entry(src, dest, dereference_root = false, remove_destination = false)
Entry_.new(src, nil, dereference_root).traverse do |ent|
destent = Entry_.new(dest, ent.rel, false)
File.unlink destent.path if remove_destination && File.file?(destent.path)
module_function :link_entry
# 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.
# If +dereference_root+ is true, this method dereference tree root.
# If +remove_destination+ is true, this method removes each destination file before copy.
# # Installing Ruby library "mylib" under the site_ruby
# FileUtils.rm_r site_ruby + '/mylib', force: true
# 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, false).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))