 1111706378
			
		
	
	
		1111706378
		
	
	
	
	
		
			
			The main thing is that this DSL allows us to provide an
interface that can be serialized to the JSON API.
Changes:
- Homebrew::Service
  - Adds `#service_name` and `#plist_name` methods
    - Each is now included in the `#serialize` method as well
  - Eval block on instantiation
    - Before we lazy evaluated this but the cost is not significant
      and it complicated the code a bunch. This only gets called
      during install, when evaluating caveats and in the `brew service`
      command. It skips this evaluation if the service block isn't there.
  - Add `#command?` helper to avoid `#command.blank?` and `#command.present?`
- Formula
  - `#service` now returns a service whenever it's called. This call is
    hidden behind a call to `#service?` most of the time anyway so this
    should be fine.
  - `#plist_name` and `#service_name` now call the methods of the same name
    on the service class. This should have already been in the service object
    to begin with and keeping these methods here helps preserve backwards
    compatibility with people who were overwriting these methods before.
- Caveats
  - Prefer `service#command?`
  - Add helpers for checking on service commands
    - This duplicates some of the work in `brew services`. Maybe we should
      merge that repo in at some point.
  - Check for installed service at `#plist_name` or `#service_name`. I think
    this should be used instead of `Keg#plist_installed?` which checked for any plist file.
    We should think about deprecating `#plist_installed?` in the future.
  - Stop using `ps aux | grep #{formula.plist_name}` to check for service files
    because it was inaccurate (it always returns true on my machine) because the grep
    process is started before the ps process.
  - Note: The behavior is the same as it was before. This means that caveats
    only show up for custom service files on install or if they're already installed.
    Otherwise it won't show up in `brew info`. This is because it has to check
    first if the service file has been installed.
- Utils::Service
  - Add utils for evaluating if a service is installed and running. This duplicates
    some of the work already found in `brew services`. We should seriously consider
    merging `brew services` with the main brew repo in the future since it's already
    tightly coupled to the code in the main repo.
- Formulary.load_formula_from_api
  - Be more explicit about which types can be deserialized into run params since
    it is now possible for run params to be nil.
- Update and add tests
		
	
			
		
			
				
	
	
		
			207 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
			
		
		
	
	
			207 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
| # typed: true
 | |
| # frozen_string_literal: true
 | |
| 
 | |
| require "language/python"
 | |
| require "utils/service"
 | |
| 
 | |
| # A formula's caveats.
 | |
| #
 | |
| # @api private
 | |
| class Caveats
 | |
|   extend Forwardable
 | |
| 
 | |
|   attr_reader :formula
 | |
| 
 | |
|   def initialize(formula)
 | |
|     @formula = formula
 | |
|   end
 | |
| 
 | |
|   def caveats
 | |
|     caveats = []
 | |
|     begin
 | |
|       build = formula.build
 | |
|       formula.build = Tab.for_formula(formula)
 | |
|       string = formula.caveats.to_s
 | |
|       caveats << "#{string.chomp}\n" unless string.empty?
 | |
|     ensure
 | |
|       formula.build = build
 | |
|     end
 | |
|     caveats << keg_only_text
 | |
| 
 | |
|     valid_shells = [:bash, :zsh, :fish].freeze
 | |
|     current_shell = Utils::Shell.preferred || Utils::Shell.parent
 | |
|     shells = if current_shell.present? &&
 | |
|                 (shell_sym = current_shell.to_sym) &&
 | |
|                 valid_shells.include?(shell_sym)
 | |
|       [shell_sym]
 | |
|     else
 | |
|       valid_shells
 | |
|     end
 | |
|     shells.each do |shell|
 | |
|       caveats << function_completion_caveats(shell)
 | |
|     end
 | |
| 
 | |
|     caveats << service_caveats
 | |
|     caveats << elisp_caveats
 | |
|     caveats.compact.join("\n")
 | |
|   end
 | |
| 
 | |
|   delegate [:empty?, :to_s] => :caveats
 | |
| 
 | |
|   def keg_only_text(skip_reason: false)
 | |
|     return unless formula.keg_only?
 | |
| 
 | |
|     s = if skip_reason
 | |
|       ""
 | |
|     else
 | |
|       <<~EOS
 | |
|         #{formula.name} is keg-only, which means it was not symlinked into #{HOMEBREW_PREFIX},
 | |
|         because #{formula.keg_only_reason.to_s.chomp}.
 | |
|       EOS
 | |
|     end.dup
 | |
| 
 | |
|     if formula.bin.directory? || formula.sbin.directory?
 | |
|       s << <<~EOS
 | |
| 
 | |
|         If you need to have #{formula.name} first in your PATH, run:
 | |
|       EOS
 | |
|       s << "  #{Utils::Shell.prepend_path_in_profile(formula.opt_bin.to_s)}\n" if formula.bin.directory?
 | |
|       s << "  #{Utils::Shell.prepend_path_in_profile(formula.opt_sbin.to_s)}\n" if formula.sbin.directory?
 | |
|     end
 | |
| 
 | |
|     if formula.lib.directory? || formula.include.directory?
 | |
|       s << <<~EOS
 | |
| 
 | |
|         For compilers to find #{formula.name} you may need to set:
 | |
|       EOS
 | |
| 
 | |
|       s << "  #{Utils::Shell.export_value("LDFLAGS", "-L#{formula.opt_lib}")}\n" if formula.lib.directory?
 | |
| 
 | |
|       s << "  #{Utils::Shell.export_value("CPPFLAGS", "-I#{formula.opt_include}")}\n" if formula.include.directory?
 | |
| 
 | |
|       if which("pkg-config", ORIGINAL_PATHS) &&
 | |
|          ((formula.lib/"pkgconfig").directory? || (formula.share/"pkgconfig").directory?)
 | |
|         s << <<~EOS
 | |
| 
 | |
|           For pkg-config to find #{formula.name} you may need to set:
 | |
|         EOS
 | |
| 
 | |
|         if (formula.lib/"pkgconfig").directory?
 | |
|           s << "  #{Utils::Shell.export_value("PKG_CONFIG_PATH", "#{formula.opt_lib}/pkgconfig")}\n"
 | |
|         end
 | |
| 
 | |
|         if (formula.share/"pkgconfig").directory?
 | |
|           s << "  #{Utils::Shell.export_value("PKG_CONFIG_PATH", "#{formula.opt_share}/pkgconfig")}\n"
 | |
|         end
 | |
|       end
 | |
|     end
 | |
|     s << "\n" unless s.end_with?("\n")
 | |
|     s
 | |
|   end
 | |
| 
 | |
|   private
 | |
| 
 | |
|   def keg
 | |
|     @keg ||= [formula.prefix, formula.opt_prefix, formula.linked_keg].map do |d|
 | |
|       Keg.new(d.resolved_path)
 | |
|     rescue
 | |
|       nil
 | |
|     end.compact.first
 | |
|   end
 | |
| 
 | |
|   def function_completion_caveats(shell)
 | |
|     return unless keg
 | |
|     return unless which(shell.to_s, ORIGINAL_PATHS)
 | |
| 
 | |
|     completion_installed = keg.completion_installed?(shell)
 | |
|     functions_installed = keg.functions_installed?(shell)
 | |
|     return if !completion_installed && !functions_installed
 | |
| 
 | |
|     installed = []
 | |
|     installed << "completions" if completion_installed
 | |
|     installed << "functions" if functions_installed
 | |
| 
 | |
|     root_dir = formula.keg_only? ? formula.opt_prefix : HOMEBREW_PREFIX
 | |
| 
 | |
|     case shell
 | |
|     when :bash
 | |
|       <<~EOS
 | |
|         Bash completion has been installed to:
 | |
|           #{root_dir}/etc/bash_completion.d
 | |
|       EOS
 | |
|     when :zsh
 | |
|       <<~EOS
 | |
|         zsh #{installed.join(" and ")} have been installed to:
 | |
|           #{root_dir}/share/zsh/site-functions
 | |
|       EOS
 | |
|     when :fish
 | |
|       fish_caveats = +"fish #{installed.join(" and ")} have been installed to:"
 | |
|       fish_caveats << "\n  #{root_dir}/share/fish/vendor_completions.d" if completion_installed
 | |
|       fish_caveats << "\n  #{root_dir}/share/fish/vendor_functions.d" if functions_installed
 | |
|       fish_caveats.freeze
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   def elisp_caveats
 | |
|     return if formula.keg_only?
 | |
|     return unless keg
 | |
|     return unless keg.elisp_installed?
 | |
| 
 | |
|     <<~EOS
 | |
|       Emacs Lisp files have been installed to:
 | |
|         #{HOMEBREW_PREFIX}/share/emacs/site-lisp/#{formula.name}
 | |
|     EOS
 | |
|   end
 | |
| 
 | |
|   def service_caveats
 | |
|     return if !formula.plist && !formula.service? && !Utils::Service.installed?(formula) && !keg&.plist_installed?
 | |
|     return if formula.service? && !formula.service.command? && !Utils::Service.installed?(formula)
 | |
| 
 | |
|     s = []
 | |
| 
 | |
|     command = if formula.service.command?
 | |
|       formula.service.manual_command
 | |
|     else
 | |
|       formula.plist_manual
 | |
|     end
 | |
| 
 | |
|     return <<~EOS if !Utils::Service.launchctl? && formula.plist
 | |
|       #{Formatter.warning("Warning:")} #{formula.name} provides a launchd plist which can only be used on macOS!
 | |
|       You can manually execute the service instead with:
 | |
|         #{command}
 | |
|     EOS
 | |
| 
 | |
|     # Brew services only works with these two tools
 | |
|     return <<~EOS if !Utils::Service.systemctl? && !Utils::Service.launchctl? && formula.service.command?
 | |
|       #{Formatter.warning("Warning:")} #{formula.name} provides a service which can only be used on macOS or systemd!
 | |
|       You can manually execute the service instead with:
 | |
|         #{command}
 | |
|     EOS
 | |
| 
 | |
|     startup = formula.service.requires_root? || formula.plist_startup
 | |
|     if Utils::Service.running?(formula)
 | |
|       s << "To restart #{formula.full_name} after an upgrade:"
 | |
|       s << "  #{startup ? "sudo " : ""}brew services restart #{formula.full_name}"
 | |
|     elsif startup
 | |
|       s << "To start #{formula.full_name} now and restart at startup:"
 | |
|       s << "  sudo brew services start #{formula.full_name}"
 | |
|     else
 | |
|       s << "To start #{formula.full_name} now and restart at login:"
 | |
|       s << "  brew services start #{formula.full_name}"
 | |
|     end
 | |
| 
 | |
|     if formula.plist_manual || formula.service.command?
 | |
|       s << "Or, if you don't want/need a background service you can just run:"
 | |
|       s << "  #{command}"
 | |
|     end
 | |
| 
 | |
|     # pbpaste is the system clipboard tool on macOS and fails with `tmux` by default
 | |
|     # check if this is being run under `tmux` to avoid failing
 | |
|     if ENV["HOMEBREW_TMUX"] && !quiet_system("/usr/bin/pbpaste")
 | |
|       s << "" << "WARNING: brew services will fail when run under tmux."
 | |
|     end
 | |
| 
 | |
|     "#{s.join("\n")}\n" unless s.empty?
 | |
|   end
 | |
| end
 |