diff --git a/lib/package.rb b/lib/package.rb index e60aa4d5d..6b297eed0 100644 --- a/lib/package.rb +++ b/lib/package.rb @@ -2,6 +2,7 @@ require 'English' require_relative 'const' require_relative 'color' require_relative 'package_helpers' +require_relative 'selector' class Package property :description, :homepage, :version, :license, :compatibility, diff --git a/lib/selector.rb b/lib/selector.rb new file mode 100644 index 000000000..9e2043341 --- /dev/null +++ b/lib/selector.rb @@ -0,0 +1,93 @@ +# selector.rb: Prompt user to choose an option in a list of options +# See packages/hunspell.rb for example usage +require_relative 'color' + +class Selector + @@default_prompt = { + heading: 'There are %{total_opts} provider(s) for this package: ', + countdown: 'Default selected in %%2.2i second(s). Enter your choice [1 = %{default}]: ' + } + + def initialize(options, prompt = @@default_prompt, timeout = 10) + @options = options + @timeout = timeout + + # substitute expressions in the message ("%{variable}") + @prompt = prompt.transform_values {|p| format(p, { total_opts: @options.size, default: @options[0][:value] }) } + end + + def show_prompt + # show_prompt(): Show prompt based on given options + warn "#{@prompt[:heading]}\n\n" + + @options.each_with_index do |opt, i| + $stderr.print " #{i + 1}: #{opt[:value]}" + $stderr.print " (#{opt[:description]})" if opt[:description] + $stderr.print "\n" + end + + warn "\n" + + # only show prompt when crew is running in an interactive terminal + if $stdin.isatty + begin + fire_timer { Thread.kill(@io_read) } + start_reading + + @io_read.join + ensure + Thread.kill(@countdown) + end + end + + if @io_read.nil? || @io_read[:input].to_s.chomp.empty? + # empty input or timeout + warn "Selected \"#{@options[0][:value]}\" by default.".yellow + choice = 1 + elsif Integer(@io_read[:input], exception: false)&.between?(1, @options.size) + # when input is valid (is an integer and in range) + choice = @io_read[:input].to_i + else + # invalid input + warn <<~EOT.yellow + I don't understand "#{@io_read[:input]}". :( + + Selected "#{@options[0][:value]}" by default. + EOT + choice = 1 + end + + # return result + return @options[choice - 1][:value] + end + + private + + def fire_timer(&when_timeout) + # fire_timer(): start a timer in separate thread + @countdown = Thread.new do + @timeout.downto(0).each do |remaining_time| + # print current countdown + $stderr.print "\r#{format(@prompt[:countdown], remaining_time)}" + + if remaining_time.zero? + warn "\nTime expired.\n".yellow + when_timeout.call + else + sleep 1 + end + end + end + end + + def start_reading + # start_reading(): read from terminal in separate thread + @io_read = Thread.new do + # discard any input in the input buffer + $stdin.read_nonblock(1024) + rescue IO::WaitReadable + ensure + Thread.current[:input] = $stdin.getc + end + end +end diff --git a/packages/hunspell.rb b/packages/hunspell.rb index c0b96b3df..7138d6855 100644 --- a/packages/hunspell.rb +++ b/packages/hunspell.rb @@ -2,37 +2,22 @@ require 'package' class Hunspell < Package description 'Hunspell is a spell checker and morphological analyzer library' - homepage 'http://hunspell.github.io/' - version '1.7.0' + homepage 'https://hunspell.github.io/' + version '1.x' license 'MPL-1.1, GPL-2 and LGPL-2.1' compatibility 'all' - source_url 'https://github.com/hunspell/hunspell/archive/v1.7.0.tar.gz' - source_sha256 'bb27b86eb910a8285407cf3ca33b62643a02798cf2eef468c0a74f6c3ee6bc8a' is_fake - if ARGV[0] == 'install' - puts - puts 'Enter your preferred language:' - puts '1 = American English' - puts '2 = Français' - puts '3 = Español' - puts '0 = Cancel' + def self.preflight + if ARGV.include?('install') + options = [ + { value: 'hunspell_en_us', description: 'American English' }, + { value: 'hunspell_fr_fr', description: 'Français' }, + { value: 'hunspell_es_any', description: 'Español' } + ] - while version = $stdin.gets.chomp - case version - when '1' - depends_on 'hunspell_en_us' - break - when '2' - depends_on 'hunspell_fr_fr' - break - when '3' - depends_on 'hunspell_es_any' - break - when '0' - abort - end + depends_on Selector.new(options).show_prompt end end end diff --git a/packages/jdk.rb b/packages/jdk.rb index 02c773fcd..c021459a9 100644 --- a/packages/jdk.rb +++ b/packages/jdk.rb @@ -1,43 +1,46 @@ require 'package' class Jdk < Package - description 'The JDK is a development environment for building applications, applets, and components using the Java programming language.' - homepage 'https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html' - version '8u333-18.0.1.1' + description 'The Oracle JDK is a development environment for building applications, applets, and components using the Java programming language.' + homepage 'https://www.oracle.com/java' license 'Oracle-BCLA-JavaSE' compatibility 'all' + # full version number extracted from jdk*.rb recipes + @avail_jdk_ver = Dir["#{CREW_PACKAGES_PATH}/jdk*.rb"].to_h do |pkgFile| + jdk_majver = pkgFile[/jdk(\d+)$/, 1] + pkg = Package.load_package(pkgFile) + + [jdk_majver, pkg.version] + end + + version "#{@avail_jdk_ver.to_a[0][1]}-#{@avail_jdk_ver.to_a[-1][1]}" + is_fake def self.preflight - jdkver = `java -version 2>&1 | head -1 | cut -d'"' -f2`.chomp - unless jdkver.empty? || jdkver.include?('No such file or directory') || jdkver.include?('not found') - majver = `java -version 2>&1 | head -1 | cut -d'"' -f2 | cut -d'.' -f1`.chomp - majver = '8' if majver == '1' - puts "Package jdk#{majver} already installed.".lightgreen - abort "Enter `crew remove jdk#{majver} && crew install jdk` to install a different version." - end - puts - puts 'Select version:' - puts ' 8 = JDK 8u333' - if ARCH == 'x86_64' - puts '11 = JDK 11.0.16.1' - puts '17 = JDK 17.0.3.1' - puts '18 = JDK 18.0.1.1' - end - puts ' 0 = Cancel' + if ARGV.include?('install') + jdk_exec = File.join(CREW_PREFIX, 'bin', 'java') - while version = $stdin.gets.chomp - case version - when '8', '11', '17', '18' - depends_on "jdk#{version}" - break - when '0' - abort - break - else - puts "\nPlease select from one of the options or enter 0 to cancel." + if File.exist?(jdk_exec) + jdk_ver_str = `#{jdk_exec} -version 2>&1` + jdk_ver = jdk_ver_str[/version "(.+?)"/, 1] + jdk_major_ver = jdk_ver.match?(/^1.8/) ? '8' : jdk_ver.partition('.')[0] + + is_openjdk = jdk_ver_str.include?('openjdk') + pkg_branding = is_openjdk ? 'OpenJDK' : 'Oracle JDK' + pkg_prefix = is_openjdk ? 'openjdk' : 'jdk' + + abort <<~EOT.yellow + + #{pkg_branding} #{jdk_ver} installed. + + Run "crew remove #{pkg_prefix}#{jdk_major_ver}; crew install #{name}" to install another version of JDK + EOT end + + options = @avail_jdk_ver.map {|majver, ver| { value: "jdk#{majver}", description: "Oracle JDK #{ver}" } } + depends_on Selector.new(options).show_prompt end end end