aboutsummaryrefslogtreecommitdiffstats
path: root/Library/Homebrew/extend/fileutils.rb
blob: 6286e48c01b61eb8b347fe400cbbfc58e87d17e2 (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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
require "fileutils"
require "tmpdir"
require "etc"

# Homebrew extends Ruby's `FileUtils` to make our code more readable.
# @see https://ruby-doc.org/stdlib-2.0.0/libdoc/fileutils/rdoc/FileUtils.html Ruby's FileUtils API
module FileUtils
  # Create a temporary directory then yield. When the block returns,
  # recursively delete the temporary directory. Passing opts[:retain]
  # or calling `do |staging| ... staging.retain!` in the block will skip
  # the deletion and retain the temporary directory's contents.
  def mktemp(prefix = name, opts = {})
    Mktemp.new(prefix, opts).run do |staging|
      yield staging
    end
  end

  module_function :mktemp

  # Performs mktemp's functionality, and tracks the results.
  # Each instance is only intended to be used once.
  class Mktemp
    include FileUtils

    # Path to the tmpdir used in this run, as a Pathname.
    attr_reader :tmpdir

    def initialize(prefix = name, opts = {})
      @prefix = prefix
      @retain = opts[:retain]
      @quiet = false
    end

    # Instructs this Mktemp to retain the staged files
    def retain!
      @retain = true
    end

    # True if the staged temporary files should be retained
    def retain?
      @retain
    end

    # Instructs this Mktemp to not emit messages when retention is triggered
    def quiet!
      @quiet = true
    end

    def to_s
      "[Mktemp: #{tmpdir} retain=#{@retain} quiet=#{@quiet}]"
    end

    def run
      @tmpdir = Pathname.new(Dir.mktmpdir("#{@prefix}-", HOMEBREW_TEMP))

      # Make sure files inside the temporary directory have the same group as the
      # brew instance.
      #
      # Reference from `man 2 open`
      # > When a new file is created, it is given the group of the directory which
      # contains it.
      group_id = if HOMEBREW_BREW_FILE.grpowned?
        HOMEBREW_BREW_FILE.stat.gid
      else
        Process.gid
      end
      begin
        chown(nil, group_id, tmpdir)
      rescue Errno::EPERM
        opoo "Failed setting group \"#{Etc.getgrgid(group_id).name}\" on #{tmpdir}"
      end

      begin
        Dir.chdir(tmpdir) { yield self }
      ensure
        ignore_interrupts { rm_rf(tmpdir) } unless retain?
      end
    ensure
      if retain? && !@tmpdir.nil? && !@quiet
        ohai "Kept temporary files"
        puts "Temporary files retained at #{@tmpdir}"
      end
    end
  end

  # @private
  alias old_mkdir mkdir

  # A version of mkdir that also changes to that folder in a block.
  def mkdir(name, &_block)
    mkdir_p(name)
    return unless block_given?
    chdir name do
      yield
    end
  end
  module_function :mkdir

  # Run `scons` using a Homebrew-installed version rather than whatever is in the `PATH`.
  def scons(*args)
    system Formulary.factory("scons").opt_bin/"scons", *args
  end

  # Run `make` 3.81 or newer.
  # Uses the system make on Leopard and newer, and the
  # path to the actually-installed make on Tiger or older.
  def make(*args)
    if Utils.popen_read("/usr/bin/make", "--version").match(/Make (\d\.\d+)/)[1] > "3.80"
      make_path = "/usr/bin/make"
    else
      make = Formula["make"].opt_bin/"make"
      make_path = make.exist? ? make.to_s : (Formula["make"].opt_bin/"gmake").to_s
    end

    if superenv?
      make_name = File.basename(make_path)
      with_env(HOMEBREW_MAKE: make_name) do
        system "make", *args
      end
    else
      system make_path, *args
    end
  end

  if method_defined?(:ruby)
    # @private
    alias old_ruby ruby
  end

  # Run the `ruby` Homebrew is using rather than whatever is in the `PATH`.
  def ruby(*args)
    system RUBY_PATH, *args
  end

  # Run `xcodebuild` without Homebrew's compiler environment variables set.
  def xcodebuild(*args)
    removed = ENV.remove_cc_etc
    system "xcodebuild", *args
  ensure
    ENV.update(removed)
  end
end