Class: Vcvars::CLI
- Inherits:
-
Object
- Object
- Vcvars::CLI
- Defined in:
- lib/vcvars/cli.rb
Overview
Command-line interface for the ‘vcvars` executable.
Class Method Summary collapse
Instance Method Summary collapse
- #cmd_doctor(argv) ⇒ Object
- #cmd_env(argv) ⇒ Object
- #cmd_exec(argv) ⇒ Object
- #cmd_new(argv) ⇒ Object
-
#cmd_version ⇒ Object
— commands ————————————————————.
- #cmd_where(argv) ⇒ Object
-
#extract_opt(argv, name) ⇒ Object
Pulls “–opt value” or “–opt=value” out of argv (mutating it).
-
#parse_exec_args(argv) ⇒ Object
Splits exec args into [arch, command].
- #print_env(delta, format) ⇒ Object
- #print_help ⇒ Object
-
#run(argv) ⇒ Object
Returns a process exit status (Integer).
- #sh_escape(value) ⇒ Object
Class Method Details
.start(argv) ⇒ Object
8 9 10 |
# File 'lib/vcvars/cli.rb', line 8 def self.start(argv) new.run(argv) end |
Instance Method Details
#cmd_doctor(argv) ⇒ Object
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/vcvars/cli.rb', line 46 def cmd_doctor(argv) require "vcvars/doctor" deep = !argv.include?("--quick") checks = Doctor.run(deep: deep) checks.each do |c| puts "#{c.icon} #{c.label}" next unless c.detail && !c.detail.to_s.empty? c.detail.to_s.each_line { |line| puts " #{line.chomp}" } end fails = checks.count { |c| c.status == :fail } warns = checks.count { |c| c.status == :warn } puts if fails.zero? extra = warns.positive? ? " (#{warns} warning#{'s' if warns > 1})" : "" puts "Summary: no blocking problems#{extra}." 0 else puts "Summary: #{fails} problem#{'s' if fails > 1} found — see remedies above." 1 end end |
#cmd_env(argv) ⇒ Object
86 87 88 89 90 91 92 |
# File 'lib/vcvars/cli.rb', line 86 def cmd_env(argv) format = (extract_opt(argv, "--format") || "bat").downcase arch = extract_opt(argv, "--arch") delta = arch ? Vcvars.env(arch: arch) : Vcvars.env print_env(delta, format) 0 end |
#cmd_exec(argv) ⇒ Object
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/vcvars/cli.rb', line 94 def cmd_exec(argv) arch, command = parse_exec_args(argv) if command.nil? || command.empty? warn "vcvars exec: no command given." warn "Example: vcvars exec -- rake compile" return 1 end arch ? Vcvars.activate!(arch: arch) : Vcvars.activate! ok = system(*command) if ok.nil? warn "vcvars exec: failed to run #{command.first.inspect} (not found?)." return 127 end $?.exitstatus || (ok ? 0 : 1) end |
#cmd_new(argv) ⇒ Object
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 |
# File 'lib/vcvars/cli.rb', line 112 def cmd_new(argv) require "vcvars/scaffold" force = argv.delete("--force") ? true : false dir = extract_opt(argv, "--dir") name = argv.shift unless name warn "vcvars new: missing NAME." warn "Example: vcvars new my_ext" return 1 end scaffold = Scaffold.new(name, dir: dir) created = scaffold.generate(force: force) puts "Created gem '#{scaffold.name}' (module #{scaffold.module_name}) in #{scaffold.dest}:" prefix = scaffold.dest + File::SEPARATOR created.each { |f| puts " #{f.start_with?(prefix) ? f[prefix.length..] : f}" } puts puts "Next steps:" puts " cd #{scaffold.dest}" puts " bundle install" puts " rake compile && rake test" 0 end |
#cmd_version ⇒ Object
— commands ————————————————————
41 42 43 44 |
# File 'lib/vcvars/cli.rb', line 41 def cmd_version puts "vcvars #{Vcvars::VERSION}" 0 end |
#cmd_where(argv) ⇒ Object
71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/vcvars/cli.rb', line 71 def cmd_where(argv) arch = extract_opt(argv, "--arch") inst = arch ? Vcvars.locate(arch: arch) : Vcvars.locate if inst puts inst.to_s puts "vcvars script: #{inst.vcvars}" puts "target arch: #{inst.arch}" 0 else warn "vcvars: no Visual Studio with the C++ tools was found." warn "Install the \"Desktop development with C++\" workload, then retry." 1 end end |
#extract_opt(argv, name) ⇒ Object
Pulls “–opt value” or “–opt=value” out of argv (mutating it). Returns the value or nil.
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
# File 'lib/vcvars/cli.rb', line 163 def extract_opt(argv, name) if (i = argv.index(name)) value = argv[i + 1] if value.nil? argv.slice!(i, 1) raise Error, "#{name} requires a value." end argv.slice!(i, 2) return value end prefix = "#{name}=" if (i = argv.index { |a| a.to_s.start_with?(prefix) }) value = argv[i][prefix.length..] argv.slice!(i, 1) return value end nil end |
#parse_exec_args(argv) ⇒ Object
Splits exec args into [arch, command]. Flags (a leading –arch, or anything before a “–” separator) are parsed; the rest is the command.
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/vcvars/cli.rb', line 142 def parse_exec_args(argv) if (sep = argv.index("--")) head = argv[0...sep] command = argv[(sep + 1)..] [extract_opt(head, "--arch"), command] else rest = argv.dup arch = nil # Only honor flags that appear before the command. if rest.first == "--arch" rest.shift arch = rest.shift elsif rest.first.to_s.start_with?("--arch=") arch = rest.shift.split("=", 2)[1] end [arch, rest] end end |
#print_env(delta, format) ⇒ Object
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 |
# File 'lib/vcvars/cli.rb', line 182 def print_env(delta, format) keys = delta.keys.sort_by(&:upcase) case format when "bat", "cmd" keys.each { |k| puts %{set "#{k}=#{delta[k]}"} } when "powershell", "ps", "ps1" keys.each { |k| puts %{$env:#{k} = '#{delta[k].to_s.gsub("'", "''")}'} } when "sh", "bash" keys.each { |k| puts %{export #{k}="#{sh_escape(delta[k])}"} } when "dotenv", "env" keys.each { |k| puts "#{k}=#{delta[k]}" } when "json" require "json" puts JSON.pretty_generate(delta) else raise Error, "unknown --format #{format.inspect} " \ "(use bat, powershell, sh, dotenv, or json)." end end |
#print_help ⇒ Object
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 |
# File 'lib/vcvars/cli.rb', line 206 def print_help puts <<~HELP vcvars #{Vcvars::VERSION} — load the MSVC build environment for native Ruby extensions. Usage: vcvars doctor [--quick] Diagnose the MSVC extension toolchain vcvars where [--arch ARCH] Show the located Visual Studio + vcvars script vcvars env [--arch ARCH] [--format bat|powershell|sh|dotenv|json] Print the MSVC env vars (the vcvars delta) vcvars exec [--arch ARCH] -- CMD [ARGS...] Run CMD inside the MSVC environment vcvars new NAME [--dir DIR] [--force] Scaffold a new MSVC-ready C-extension gem vcvars version vcvars help Examples: vcvars doctor vcvars exec -- rake compile vcvars exec -- nmake vcvars env --format powershell | Invoke-Expression vcvars new my_ext In a Rakefile, `require "vcvars/rake"` before Rake::ExtensionTask.new to auto-load the toolchain so `rake compile` works without a Developer Prompt. HELP end |
#run(argv) ⇒ Object
Returns a process exit status (Integer).
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 |
# File 'lib/vcvars/cli.rb', line 13 def run(argv) argv = argv.dup command = argv.shift case command when "doctor" then cmd_doctor(argv) when "where" then cmd_where(argv) when "env" then cmd_env(argv) when "exec" then cmd_exec(argv) when "new" then cmd_new(argv) when "version", "-v", "--version" then cmd_version when nil, "help", "-h", "--help" then print_help; 0 else warn "vcvars: unknown command #{command.inspect}\n\n" print_help 1 end rescue Vcvars::Error, ArgumentError => e # ArgumentError covers user input like an unsupported `--arch` value, which # Locator.vcvars_name raises; report it the same clean way as other errors. warn "vcvars: #{e.}" 1 rescue Interrupt 130 end |
#sh_escape(value) ⇒ Object
202 203 204 |
# File 'lib/vcvars/cli.rb', line 202 def sh_escape(value) value.to_s.gsub(/[\\"$`]/) { |c| "\\#{c}" } end |