Module: Vcdeps::Runner
- Defined in:
- lib/vcdeps/runner.rb
Overview
The ONLY place a vcpkg argv is constructed and spawned. Every --x-* flag
lives in args so an upstream rename is a one-line fix (R§3/§5.13), and the
runner test pins the exact argv. Combined child output is streamed to out
line by line (never silent during a multi-minute port build); on any abnormal
unwind the child is TerminateProcess'd and reaped so vcpkg.exe never outlives
the call (§3.2).
Constant Summary collapse
- MARKER_NAME =
".vcdeps-complete"- LOG_TAIL_MAX =
4000
Class Method Summary collapse
-
.child_env(tool_root) ⇒ Object
The child environment: telemetry OFF by default (a gem install must not phone home, §5.15), VCPKG_ROOT pinned to the tool's own root.
-
.install_args(exe, triplet, manifest_dir, install_root, home) ⇒ Object
Build the
vcpkg installargv (§5.13 pin). -
.install_error_message(command, status, combined) ⇒ Object
Compose the InstallError message, appending the offline hint when the output looks network-shaped (§5.17).
-
.log_tail(combined) ⇒ Object
Last <= LOG_TAIL_MAX chars, UTF-8 scrubbed.
-
.marker_path(install_root) ⇒ Object
---- completion marker (§2.5) -------------------------------------------.
- .marker_present?(install_root) ⇒ Boolean
-
.run(command, env:, chdir:, out:) ⇒ Object
Generic streamed runner shared by install! and baseline!.
-
.run_install(command, env:, chdir:, out:) ⇒ Object
Run
vcpkg install. -
.write_marker!(install_root, key, tool_version) ⇒ Object
Write the marker atomically: temp file then File.rename (atomic on NTFS).
Class Method Details
.child_env(tool_root) ⇒ Object
The child environment: telemetry OFF by default (a gem install must not phone home, §5.15), VCPKG_ROOT pinned to the tool's own root. All other vcpkg env (binary cache, downloads) passes through untouched (§5.18).
37 38 39 40 41 42 |
# File 'lib/vcdeps/runner.rb', line 37 def child_env(tool_root) env = {} env["VCPKG_DISABLE_METRICS"] = "1" unless ENV["VCDEPS_METRICS"] == "1" env["VCPKG_ROOT"] = tool_root if tool_root env end |
.install_args(exe, triplet, manifest_dir, install_root, home) ⇒ Object
Build the vcpkg install argv (§5.13 pin). All --x-* roots redirect out
of the gem tree into
22 23 24 25 26 27 28 29 30 31 32 |
# File 'lib/vcdeps/runner.rb', line 22 def install_args(exe, triplet, manifest_dir, install_root, home) [ exe, "install", "--triplet", triplet, "--x-manifest-root=#{manifest_dir}", "--x-install-root=#{install_root}", "--x-buildtrees-root=#{File.join(home, 'blds')}", "--x-packages-root=#{File.join(home, 'pkgs')}" ] end |
.install_error_message(command, status, combined) ⇒ Object
Compose the InstallError message, appending the offline hint when the output looks network-shaped (§5.17).
111 112 113 114 115 116 117 118 |
# File 'lib/vcdeps/runner.rb', line 111 def (command, status, combined) msg = "vcdeps: `#{File.basename(command.first)} #{command[1]}` exited #{status}." if combined =~ /(failed to (fetch|download)|could not resolve|network)/i msg += " First install of this baseline requires network access; " \ "see `vcdeps doctor` checks 9-10." end msg end |
.log_tail(combined) ⇒ Object
Last <= LOG_TAIL_MAX chars, UTF-8 scrubbed.
104 105 106 107 |
# File 'lib/vcdeps/runner.rb', line 104 def log_tail(combined) tail = combined.length > LOG_TAIL_MAX ? combined[-LOG_TAIL_MAX..] : combined tail.to_s.dup.force_encoding("UTF-8").scrub("�") end |
.marker_path(install_root) ⇒ Object
---- completion marker (§2.5) -------------------------------------------
122 123 124 |
# File 'lib/vcdeps/runner.rb', line 122 def marker_path(install_root) File.join(install_root, MARKER_NAME) end |
.marker_present?(install_root) ⇒ Boolean
126 127 128 |
# File 'lib/vcdeps/runner.rb', line 126 def marker_present?(install_root) File.exist?(marker_path(install_root)) end |
.run(command, env:, chdir:, out:) ⇒ Object
Generic streamed runner shared by install! and baseline!. Raises InstallError on nonzero exit; the network-failure hint (§5.17) is appended by the caller via install_error so this stays generic.
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 |
# File 'lib/vcdeps/runner.rb', line 54 def run(command, env:, chdir:, out:) combined = +"" Open3.popen2e(env, *command, chdir: chdir) do |_stdin, oe, wt| pid = wt.pid oe.binmode begin # Buffered each_line dispatches to a scheduler's io_read hook on mswin # (§6); reads block in VM I/O with the GVL released. Scrub to UTF-8. oe.each_line do |raw| line = raw.dup.force_encoding("UTF-8").scrub("�") combined << line out&.print(line) out&.flush if out.respond_to?(:flush) end status = wt.value unless status.success? raise InstallError.new( (command, status.exitstatus, combined), command: command, status: status.exitstatus, log_tail: log_tail(combined) ) end rescue InstallError raise rescue Exception # rubocop:disable Lint/RescueException # ANY abnormal unwind (Interrupt, Timeout, a raise from the out block): # kill THIS vcpkg.exe so its install-root lock handle is released. We # do it HERE, before Open3's block teardown tries to drain the pipe # (which would otherwise block on a still-running child). TerminateProcess # is not recursive — helper processes vcpkg spawned may briefly survive # (§3.2/§5.16). The pipe is then closed so the drain returns at once. begin Process.kill(:KILL, pid) rescue Errno::ESRCH, RangeError nil end begin oe.close rescue IOError nil end raise end end combined end |
.run_install(command, env:, chdir:, out:) ⇒ Object
Run vcpkg install. Streams combined output to out (nil to silence),
returns the captured combined output on success, raises InstallError on a
nonzero exit. command is the full argv (already built by install_args).
47 48 49 |
# File 'lib/vcdeps/runner.rb', line 47 def run_install(command, env:, chdir:, out:) run(command, env: env, chdir: chdir, out: out) end |
.write_marker!(install_root, key, tool_version) ⇒ Object
Write the marker atomically: temp file then File.rename (atomic on NTFS).
131 132 133 134 135 136 137 138 139 140 141 142 143 |
# File 'lib/vcdeps/runner.rb', line 131 def write_marker!(install_root, key, tool_version) payload = JSON.generate( "key" => key, "tool_version" => tool_version.to_s, "finished_at" => Time.now.utc.iso8601 ) final = marker_path(install_root) tmp = "#{final}.tmp.#{Process.pid}" File.write(tmp, payload) File.rename(tmp, final) ensure File.unlink(tmp) if tmp && File.exist?(tmp) && !File.exist?(final) end |