summaryrefslogtreecommitdiff
path: root/tool/lib/path.rb
blob: 5582b2851e46ba92890392a23bd15caa44ae639f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
module Path
  module_function

  def clean(path)
    path = "#{path}/".gsub(/(\A|\/)(?:\.\/)+/, '\1').tr_s('/', '/')
    nil while path.sub!(/[^\/]+\/\.\.\//, '')
    path
  end

  def relative(path, base)
    path = clean(path)
    base = clean(base)
    path, base = [path, base].map{|s|s.split("/")}
    until path.empty? or base.empty? or path[0] != base[0]
      path.shift
      base.shift
    end
    path, base = [path, base].map{|s|s.join("/")}
    if base.empty?
      path
    elsif base.start_with?("../") or File.absolute_path?(base)
      File.expand_path(path)
    else
      base.gsub!(/[^\/]+/, '..')
      File.join(base, path)
    end
  end

  def clean_link(src, dest)
    begin
      link = File.readlink(dest)
    rescue
    else
      return if link == src
      File.unlink(dest)
    end
    yield src, dest
  end

  # Extensions to FileUtils

  module Mswin
    def ln_safe(src, dest, *opt)
      cmd = ["mklink", dest.tr("/", "\\"), src.tr("/", "\\")]
      cmd[1, 0] = opt
      return if system("cmd", "/c", *cmd)
      # TODO: use RUNAS or something
      puts cmd.join(" ")
    end

    def ln_dir_safe(src, dest)
      ln_safe(src, dest, "/d")
    end
  end

  module HardlinkExcutable
    def ln_exe(src, dest)
      ln(src, dest, force: true)
    end
  end

  def ln_safe(src, dest)
    ln_sf(src, dest)
  rescue Errno::ENOENT
    # Windows disallows to create broken symboic links, probably because
    # it is a kind of reparse points.
    raise if File.exist?(src)
  end

  alias ln_dir_safe ln_safe
  alias ln_exe ln_safe

  def ln_relative(src, dest, executable = false)
    return if File.identical?(src, dest)
    parent = File.dirname(dest)
    File.directory?(parent) or mkdir_p(parent)
    if executable
      return (ln_exe(src, dest) if File.exist?(src))
    end
    clean_link(relative(src, parent), dest) {|s, d| ln_safe(s, d)}
  end

  def ln_dir_relative(src, dest)
    return if File.identical?(src, dest)
    parent = File.dirname(dest)
    File.directory?(parent) or mkdir_p(parent)
    clean_link(relative(src, parent), dest) {|s, d| ln_dir_safe(s, d)}
  end

  case (CROSS_COMPILING || RUBY_PLATFORM)
  when /linux|darwin|solaris/
    prepend HardlinkExcutable
    extend HardlinkExcutable
  when /mingw|mswin/
    unless File.respond_to?(:symlink)
      prepend Mswin
      extend Mswin
    end
  else
  end
end