1# frozen_string_literal: true 2require 'rubygems/command' 3 4## 5# Installs RubyGems itself. This command is ordinarily only available from a 6# RubyGems checkout or tarball. 7 8class Gem::Commands::SetupCommand < Gem::Command 9 HISTORY_HEADER = /^===\s*[\d.a-zA-Z]+\s*\/\s*\d{4}-\d{2}-\d{2}\s*$/.freeze 10 VERSION_MATCHER = /^===\s*([\d.a-zA-Z]+)\s*\/\s*\d{4}-\d{2}-\d{2}\s*$/.freeze 11 12 ENV_PATHS = %w[/usr/bin/env /bin/env].freeze 13 14 def initialize 15 require 'tmpdir' 16 17 super 'setup', 'Install RubyGems', 18 :format_executable => true, :document => %w[ri], 19 :site_or_vendor => 'sitelibdir', 20 :destdir => '', :prefix => '', :previous_version => '', 21 :regenerate_binstubs => true 22 23 add_option '--previous-version=VERSION', 24 'Previous version of RubyGems', 25 'Used for changelog processing' do |version, options| 26 options[:previous_version] = version 27 end 28 29 add_option '--prefix=PREFIX', 30 'Prefix path for installing RubyGems', 31 'Will not affect gem repository location' do |prefix, options| 32 options[:prefix] = File.expand_path prefix 33 end 34 35 add_option '--destdir=DESTDIR', 36 'Root directory to install RubyGems into', 37 'Mainly used for packaging RubyGems' do |destdir, options| 38 options[:destdir] = File.expand_path destdir 39 end 40 41 add_option '--[no-]vendor', 42 'Install into vendorlibdir not sitelibdir' do |vendor, options| 43 options[:site_or_vendor] = vendor ? 'vendorlibdir' : 'sitelibdir' 44 end 45 46 add_option '--[no-]format-executable', 47 'Makes `gem` match ruby', 48 'If Ruby is ruby18, gem will be gem18' do |value, options| 49 options[:format_executable] = value 50 end 51 52 add_option '--[no-]document [TYPES]', Array, 53 'Generate documentation for RubyGems', 54 'List the documentation types you wish to', 55 'generate. For example: rdoc,ri' do |value, options| 56 options[:document] = case value 57 when nil then %w[rdoc ri] 58 when false then [] 59 else value 60 end 61 end 62 63 add_option '--[no-]rdoc', 64 'Generate RDoc documentation for RubyGems' do |value, options| 65 if value 66 options[:document] << 'rdoc' 67 else 68 options[:document].delete 'rdoc' 69 end 70 71 options[:document].uniq! 72 end 73 74 add_option '--[no-]ri', 75 'Generate RI documentation for RubyGems' do |value, options| 76 if value 77 options[:document] << 'ri' 78 else 79 options[:document].delete 'ri' 80 end 81 82 options[:document].uniq! 83 end 84 85 add_option '--[no-]regenerate-binstubs', 86 'Regenerate gem binstubs' do |value, options| 87 options[:regenerate_binstubs] = value 88 end 89 90 add_option('-E', '--[no-]env-shebang', 91 'Rewrite executables with a shebang', 92 'of /usr/bin/env') do |value, options| 93 options[:env_shebang] = value 94 end 95 96 @verbose = nil 97 end 98 99 def check_ruby_version 100 required_version = Gem::Requirement.new '>= 1.8.7' 101 102 unless required_version.satisfied_by? Gem.ruby_version 103 alert_error "Expected Ruby version #{required_version}, is #{Gem.ruby_version}" 104 terminate_interaction 1 105 end 106 end 107 108 def defaults_str # :nodoc: 109 "--format-executable --document ri --regenerate-binstubs" 110 end 111 112 def description # :nodoc: 113 <<-EOF 114Installs RubyGems itself. 115 116RubyGems installs RDoc for itself in GEM_HOME. By default this is: 117 #{Gem.dir} 118 119If you prefer a different directory, set the GEM_HOME environment variable. 120 121RubyGems will install the gem command with a name matching ruby's 122prefix and suffix. If ruby was installed as `ruby18`, gem will be 123installed as `gem18`. 124 125By default, this RubyGems will install gem as: 126 #{Gem.default_exec_format % 'gem'} 127 EOF 128 end 129 130 module MakeDirs 131 def mkdir_p(path, *opts) 132 super 133 (@mkdirs ||= []) << path 134 end 135 end 136 137 def execute 138 @verbose = Gem.configuration.really_verbose 139 140 install_destdir = options[:destdir] 141 142 unless install_destdir.empty? 143 ENV['GEM_HOME'] ||= File.join(install_destdir, 144 Gem.default_dir.gsub(/^[a-zA-Z]:/, '')) 145 end 146 147 check_ruby_version 148 149 require 'fileutils' 150 if Gem.configuration.really_verbose 151 extend FileUtils::Verbose 152 else 153 extend FileUtils 154 end 155 extend MakeDirs 156 157 lib_dir, bin_dir = make_destination_dirs install_destdir 158 159 install_lib lib_dir 160 161 install_executables bin_dir 162 163 remove_old_bin_files bin_dir 164 165 remove_old_lib_files lib_dir 166 167 install_default_bundler_gem 168 169 if mode = options[:dir_mode] 170 @mkdirs.uniq! 171 File.chmod(mode, @mkdirs) 172 end 173 174 say "RubyGems #{Gem::VERSION} installed" 175 176 regenerate_binstubs if options[:regenerate_binstubs] 177 178 uninstall_old_gemcutter 179 180 documentation_success = install_rdoc 181 182 say 183 if @verbose 184 say "-" * 78 185 say 186 end 187 188 if options[:previous_version].empty? 189 options[:previous_version] = Gem::VERSION.sub(/[0-9]+$/, '0') 190 end 191 192 options[:previous_version] = Gem::Version.new(options[:previous_version]) 193 194 show_release_notes 195 196 say 197 say "-" * 78 198 say 199 200 say "RubyGems installed the following executables:" 201 say @bin_file_names.map { |name| "\t#{name}\n" } 202 say 203 204 unless @bin_file_names.grep(/#{File::SEPARATOR}gem$/) 205 say "If `gem` was installed by a previous RubyGems installation, you may need" 206 say "to remove it by hand." 207 say 208 end 209 210 if documentation_success 211 if options[:document].include? 'rdoc' 212 say "Rdoc documentation was installed. You may now invoke:" 213 say " gem server" 214 say "and then peruse beautifully formatted documentation for your gems" 215 say "with your web browser." 216 say "If you do not wish to install this documentation in the future, use the" 217 say "--no-document flag, or set it as the default in your ~/.gemrc file. See" 218 say "'gem help env' for details." 219 say 220 end 221 222 if options[:document].include? 'ri' 223 say "Ruby Interactive (ri) documentation was installed. ri is kind of like man " 224 say "pages for Ruby libraries. You may access it like this:" 225 say " ri Classname" 226 say " ri Classname.class_method" 227 say " ri Classname#instance_method" 228 say "If you do not wish to install this documentation in the future, use the" 229 say "--no-document flag, or set it as the default in your ~/.gemrc file. See" 230 say "'gem help env' for details." 231 say 232 end 233 end 234 end 235 236 237 def install_executables(bin_dir) 238 @bin_file_names = [] 239 240 prog_mode = options[:prog_mode] || 0755 241 242 executables = { 'gem' => 'bin' } 243 executables['bundler'] = 'bundler/exe' if Gem::USE_BUNDLER_FOR_GEMDEPS 244 executables.each do |tool, path| 245 say "Installing #{tool} executable" if @verbose 246 247 Dir.chdir path do 248 bin_files = Dir['*'] 249 250 bin_files -= %w[update_rubygems bundler bundle_ruby] 251 252 bin_files.each do |bin_file| 253 bin_file_formatted = if options[:format_executable] 254 Gem.default_exec_format % bin_file 255 else 256 bin_file 257 end 258 259 dest_file = File.join bin_dir, bin_file_formatted 260 bin_tmp_file = File.join Dir.tmpdir, "#{bin_file}.#{$$}" 261 262 begin 263 bin = File.readlines bin_file 264 bin[0] = shebang 265 266 File.open bin_tmp_file, 'w' do |fp| 267 fp.puts bin.join 268 end 269 270 install bin_tmp_file, dest_file, :mode => prog_mode 271 @bin_file_names << dest_file 272 ensure 273 rm bin_tmp_file 274 end 275 276 next unless Gem.win_platform? 277 278 begin 279 bin_cmd_file = File.join Dir.tmpdir, "#{bin_file}.bat" 280 281 File.open bin_cmd_file, 'w' do |file| 282 file.puts <<-TEXT 283 @ECHO OFF 284 IF NOT "%~f0" == "~f0" GOTO :WinNT 285 @"#{File.basename(Gem.ruby).chomp('"')}" "#{dest_file}" %1 %2 %3 %4 %5 %6 %7 %8 %9 286 GOTO :EOF 287 :WinNT 288 @"#{File.basename(Gem.ruby).chomp('"')}" "%~dpn0" %* 289 TEXT 290 end 291 292 install bin_cmd_file, "#{dest_file}.bat", :mode => prog_mode 293 ensure 294 rm bin_cmd_file 295 end 296 end 297 end 298 end 299 end 300 301 def shebang 302 if options[:env_shebang] 303 ruby_name = RbConfig::CONFIG['ruby_install_name'] 304 @env_path ||= ENV_PATHS.find {|env_path| File.executable? env_path } 305 "#!#{@env_path} #{ruby_name}\n" 306 else 307 "#!#{Gem.ruby}\n" 308 end 309 end 310 311 def install_file(file, dest_dir) 312 dest_file = File.join dest_dir, file 313 dest_dir = File.dirname dest_file 314 unless File.directory? dest_dir 315 mkdir_p dest_dir, :mode => 0755 316 end 317 318 install file, dest_file, :mode => options[:data_mode] || 0644 319 end 320 321 def install_lib(lib_dir) 322 libs = { 'RubyGems' => 'lib' } 323 libs['Bundler'] = 'bundler/lib' if Gem::USE_BUNDLER_FOR_GEMDEPS 324 libs.each do |tool, path| 325 say "Installing #{tool}" if @verbose 326 327 lib_files = rb_files_in path 328 lib_files.concat(template_files) if tool == 'Bundler' 329 330 pem_files = pem_files_in path 331 332 Dir.chdir path do 333 lib_files.each do |lib_file| 334 install_file lib_file, lib_dir 335 end 336 337 pem_files.each do |pem_file| 338 install_file pem_file, lib_dir 339 end 340 end 341 end 342 end 343 344 def install_rdoc 345 gem_doc_dir = File.join Gem.dir, 'doc' 346 rubygems_name = "rubygems-#{Gem::VERSION}" 347 rubygems_doc_dir = File.join gem_doc_dir, rubygems_name 348 349 begin 350 Gem.ensure_gem_subdirectories Gem.dir 351 rescue SystemCallError 352 # ignore 353 end 354 355 if File.writable? gem_doc_dir and 356 (not File.exist? rubygems_doc_dir or 357 File.writable? rubygems_doc_dir) 358 say "Removing old RubyGems RDoc and ri" if @verbose 359 Dir[File.join(Gem.dir, 'doc', 'rubygems-[0-9]*')].each do |dir| 360 rm_rf dir 361 end 362 363 require 'rubygems/rdoc' 364 365 fake_spec = Gem::Specification.new 'rubygems', Gem::VERSION 366 def fake_spec.full_gem_path 367 File.expand_path '../../../..', __FILE__ 368 end 369 370 generate_ri = options[:document].include? 'ri' 371 generate_rdoc = options[:document].include? 'rdoc' 372 373 rdoc = Gem::RDoc.new fake_spec, generate_rdoc, generate_ri 374 rdoc.generate 375 376 return true 377 elsif @verbose 378 say "Skipping RDoc generation, #{gem_doc_dir} not writable" 379 say "Set the GEM_HOME environment variable if you want RDoc generated" 380 end 381 382 return false 383 end 384 385 def install_default_bundler_gem 386 return unless Gem::USE_BUNDLER_FOR_GEMDEPS 387 388 specs_dir = Gem::Specification.default_specifications_dir 389 specs_dir = File.join(options[:destdir], specs_dir) unless Gem.win_platform? 390 mkdir_p specs_dir, :mode => 0755 391 392 # Workaround for non-git environment. 393 gemspec = File.open('bundler/bundler.gemspec', 'rb'){|f| f.read.gsub(/`git ls-files -z`/, "''") } 394 File.open('bundler/bundler.gemspec', 'w'){|f| f.write gemspec } 395 396 bundler_spec = Gem::Specification.load("bundler/bundler.gemspec") 397 bundler_spec.files = Dir.chdir("bundler") { Dir["{*.md,{lib,exe,man}/**/*}"] } 398 bundler_spec.executables -= %w[bundler bundle_ruby] 399 400 # Remove bundler-*.gemspec in default specification directory. 401 Dir.entries(specs_dir). 402 select {|gs| gs.start_with?("bundler-") }. 403 each {|gs| File.delete(File.join(specs_dir, gs)) } 404 405 default_spec_path = File.join(specs_dir, "#{bundler_spec.full_name}.gemspec") 406 Gem.write_binary(default_spec_path, bundler_spec.to_ruby) 407 408 bundler_spec = Gem::Specification.load(default_spec_path) 409 410 # Remove gemspec that was same version of vendored bundler. 411 normal_gemspec = File.join(Gem.default_dir, "specifications", "bundler-#{bundler_spec.version}.gemspec") 412 if File.file? normal_gemspec 413 File.delete normal_gemspec 414 end 415 416 # Remove gem files that were same version of vendored bundler. 417 if File.directory? bundler_spec.gems_dir 418 Dir.entries(bundler_spec.gems_dir). 419 select {|default_gem| File.basename(default_gem) == "bundler-#{bundler_spec.version}" }. 420 each {|default_gem| rm_r File.join(bundler_spec.gems_dir, default_gem) } 421 end 422 423 bundler_bin_dir = bundler_spec.bin_dir 424 bundler_bin_dir = File.join(options[:destdir], bundler_bin_dir) unless Gem.win_platform? 425 mkdir_p bundler_bin_dir, :mode => 0755 426 bundler_spec.executables.each do |e| 427 cp File.join("bundler", bundler_spec.bindir, e), File.join(bundler_bin_dir, e) 428 end 429 430 if Gem.win_platform? 431 require 'rubygems/installer' 432 433 installer = Gem::Installer.for_spec bundler_spec 434 bundler_spec.executables.each do |e| 435 installer.generate_windows_script e, bundler_spec.bin_dir 436 end 437 end 438 439 say "Bundler #{bundler_spec.version} installed" 440 end 441 442 def make_destination_dirs(install_destdir) 443 lib_dir, bin_dir = Gem.default_rubygems_dirs 444 445 unless lib_dir 446 lib_dir, bin_dir = generate_default_dirs(install_destdir) 447 end 448 449 mkdir_p lib_dir, :mode => 0755 450 mkdir_p bin_dir, :mode => 0755 451 452 return lib_dir, bin_dir 453 end 454 455 def generate_default_dirs(install_destdir) 456 prefix = options[:prefix] 457 site_or_vendor = options[:site_or_vendor] 458 459 if prefix.empty? 460 lib_dir = RbConfig::CONFIG[site_or_vendor] 461 bin_dir = RbConfig::CONFIG['bindir'] 462 else 463 # Apple installed RubyGems into libdir, and RubyGems <= 1.1.0 gets 464 # confused about installation location, so switch back to 465 # sitelibdir/vendorlibdir. 466 if defined?(APPLE_GEM_HOME) and 467 # just in case Apple and RubyGems don't get this patched up proper. 468 (prefix == RbConfig::CONFIG['libdir'] or 469 # this one is important 470 prefix == File.join(RbConfig::CONFIG['libdir'], 'ruby')) 471 lib_dir = RbConfig::CONFIG[site_or_vendor] 472 bin_dir = RbConfig::CONFIG['bindir'] 473 else 474 lib_dir = File.join prefix, 'lib' 475 bin_dir = File.join prefix, 'bin' 476 end 477 end 478 479 unless install_destdir.empty? 480 lib_dir = File.join install_destdir, lib_dir.gsub(/^[a-zA-Z]:/, '') 481 bin_dir = File.join install_destdir, bin_dir.gsub(/^[a-zA-Z]:/, '') 482 end 483 484 [lib_dir, bin_dir] 485 end 486 487 def pem_files_in(dir) 488 Dir.chdir dir do 489 Dir[File.join('**', '*pem')] 490 end 491 end 492 493 def rb_files_in(dir) 494 Dir.chdir dir do 495 Dir[File.join('**', '*rb')] 496 end 497 end 498 499 # for installation of bundler as default gems 500 def template_files 501 Dir.chdir "bundler/lib" do 502 (Dir[File.join('bundler', 'templates', '**', '{*,.*}')]). 503 select{|f| !File.directory?(f)} 504 end 505 end 506 507 # for cleanup old bundler files 508 def template_files_in(dir) 509 Dir.chdir dir do 510 (Dir[File.join('templates', '**', '{*,.*}')]). 511 select{|f| !File.directory?(f)} 512 end 513 end 514 515 def remove_old_bin_files(bin_dir) 516 old_bin_files = { 517 'gem_mirror' => 'gem mirror', 518 'gem_server' => 'gem server', 519 'gemlock' => 'gem lock', 520 'gemri' => 'ri', 521 'gemwhich' => 'gem which', 522 'index_gem_repository.rb' => 'gem generate_index', 523 } 524 525 old_bin_files.each do |old_bin_file, new_name| 526 old_bin_path = File.join bin_dir, old_bin_file 527 next unless File.exist? old_bin_path 528 529 deprecation_message = "`#{old_bin_file}` has been deprecated. Use `#{new_name}` instead." 530 531 File.open old_bin_path, 'w' do |fp| 532 fp.write <<-EOF 533#!#{Gem.ruby} 534 535abort "#{deprecation_message}" 536 EOF 537 end 538 539 next unless Gem.win_platform? 540 541 File.open "#{old_bin_path}.bat", 'w' do |fp| 542 fp.puts %{@ECHO.#{deprecation_message}} 543 end 544 end 545 end 546 547 def remove_old_lib_files(lib_dir) 548 lib_dirs = { File.join(lib_dir, 'rubygems') => 'lib/rubygems' } 549 lib_dirs[File.join(lib_dir, 'bundler')] = 'bundler/lib/bundler' if Gem::USE_BUNDLER_FOR_GEMDEPS 550 lib_dirs.each do |old_lib_dir, new_lib_dir| 551 lib_files = rb_files_in(new_lib_dir) 552 lib_files.concat(template_files_in(new_lib_dir)) if new_lib_dir =~ /bundler/ 553 554 old_lib_files = rb_files_in(old_lib_dir) 555 old_lib_files.concat(template_files_in(old_lib_dir)) if old_lib_dir =~ /bundler/ 556 557 to_remove = old_lib_files - lib_files 558 559 to_remove.delete_if do |file| 560 file.start_with? 'defaults' 561 end 562 563 Dir.chdir old_lib_dir do 564 to_remove.each do |file| 565 FileUtils.rm_f file 566 567 warn "unable to remove old file #{file} please remove it by hand" if 568 File.exist? file 569 end 570 end 571 end 572 end 573 574 def show_release_notes 575 release_notes = File.join Dir.pwd, 'History.txt' 576 577 release_notes = 578 if File.exist? release_notes 579 history = File.read release_notes 580 581 history.force_encoding Encoding::UTF_8 582 583 history = history.sub(/^# coding:.*?(?=^=)/m, '') 584 585 text = history.split(HISTORY_HEADER) 586 text.shift # correct an off-by-one generated by split 587 version_lines = history.scan(HISTORY_HEADER) 588 versions = history.scan(VERSION_MATCHER).flatten.map do |x| 589 Gem::Version.new(x) 590 end 591 592 history_string = "" 593 594 until versions.length == 0 or 595 versions.shift < options[:previous_version] do 596 history_string += version_lines.shift + text.shift 597 end 598 599 history_string 600 else 601 "Oh-no! Unable to find release notes!" 602 end 603 604 say release_notes 605 end 606 607 def uninstall_old_gemcutter 608 require 'rubygems/uninstaller' 609 610 ui = Gem::Uninstaller.new('gemcutter', :all => true, :ignore => true, 611 :version => '< 0.4') 612 ui.uninstall 613 rescue Gem::InstallError 614 end 615 616 def regenerate_binstubs 617 require "rubygems/commands/pristine_command" 618 say "Regenerating binstubs" 619 620 args = %w[--all --only-executables --silent] 621 if options[:env_shebang] 622 args << "--env-shebang" 623 end 624 625 command = Gem::Commands::PristineCommand.new 626 command.invoke(*args) 627 end 628 629end 630