summaryrefslogtreecommitdiff
path: root/tool/lib/_tmpdir.rb
blob: fd429dab37525c8c48b5a3b9e4812e6080524ec3 (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
template = "rubytest."

# This path is only for tests.
# Assume the directory by these environment variables are safe.
base = [ENV["TMPDIR"], ENV["TMP"], "/tmp"].find do |tmp|
  next unless tmp and tmp.size <= 50 and File.directory?(tmp)
  # On macOS, the default TMPDIR is very long, inspite of UNIX socket
  # path length is limited.
  #
  # Also Rubygems creates its own temporary directory per tests, and
  # some tests copy the full path of gemhome there.  In that caes, the
  # path contains both temporary names twice, and can exceed path name
  # limit very easily.
  tmp
end
begin
  tmpdir = File.join(base, template + Random.new_seed.to_s(36)[-6..-1])
  Dir.mkdir(tmpdir, 0o700)
rescue Errno::EEXIST
  retry
end
# warn "tmpdir(#{tmpdir.size}) = #{tmpdir}"

pid = $$
END {
  if pid == $$
    begin
      Dir.rmdir(tmpdir)
    rescue Errno::ENOENT
    rescue Errno::ENOTEMPTY
      require_relative "colorize"
      colorize = Colorize.new
      ls = Struct.new(:colorize) do
        def mode_inspect(m, s)
          [
            (m & 0o4 == 0 ? ?- : ?r),
            (m & 0o2 == 0 ? ?- : ?w),
            (m & 0o1 == 0 ? (s ? s.upcase : ?-) : (s || ?x)),
          ]
        end
        def decorate_path(path, st)
          case
          when st.directory?
            color = "bold;blue"
            type = "/"
          when st.symlink?
            color = "bold;cyan"
            # type = "@"
          when st.executable?
            color = "bold;green"
            type = "*"
          when path.end_with?(".gem")
            color = "green"
          end
          colorize.decorate(path, color) + (type || "")
        end
        def list_tree(parent, indent = "", &block)
          children = Dir.children(parent).map do |child|
            [child, path = File.join(parent, child), File.lstat(path)]
          end
          nlink_width = children.map {|child, path, st| st.nlink}.max.to_s.size
          size_width = children.map {|child, path, st| st.size}.max.to_s.size

          children.each do |child, path, st|
            m = st.mode
            m = [
              (st.file? ? ?- : st.ftype[0]),
              mode_inspect(m >> 6, (?s unless m & 04000 == 0)),
              mode_inspect(m >> 3, (?s unless m & 02000 == 0)),
              mode_inspect(m,      (?t unless m & 01000 == 0)),
            ].join("")
            warn sprintf("%s* %s %*d %*d %s % s%s",
                         indent, m, nlink_width, st.nlink, size_width, st.size,
                         st.mtime.to_s, decorate_path(child,  st),
                         (" -> " + decorate_path(File.readlink(path), File.stat(path)) if
                           st.symlink?))
            if st.directory?
              list_tree(File.join(parent, child), indent + "  ", &block)
            end
            yield path, st if block
          end
        end
      end.new(colorize)
      warn colorize.notice("Children under ")+colorize.fail(tmpdir)+":"
      Dir.chdir(tmpdir) do
        ls.list_tree(".") do |path, st|
          if st.directory?
            Dir.rmdir(path)
          else
            File.unlink(path)
          end
        end
      end
      require "fileutils"
      FileUtils.rm_rf(tmpdir)
    end
  end
}

ENV["TMPDIR"] = ENV["SPEC_TEMP_DIR"] = ENV["GEM_TEST_TMPDIR"] = tmpdir