require "formula_support" require "formula_lock" require "formula_pin" require "hardware" require "bottles" require "build_environment" require "build_options" require "formulary" require "software_spec" require "install_renamed" require "pkg_version" require "tap" require "keg" require "migrator" # A formula provides instructions and metadata for Homebrew to install a piece # of software. Every Homebrew formula is a {Formula}. # All subclasses of {Formula} (and all Ruby classes) have to be named # `UpperCase` and `not-use-dashes`. # A formula specified in `this-formula.rb` should have a class named # `ThisFormula`. Homebrew does enforce that the name of the file and the class # correspond. # Make sure you check with `brew search` that the name is free! # @abstract # @see SharedEnvExtension # @see FileUtils # @see Pathname # @see http://www.rubydoc.info/github/Homebrew/brew/file/share/doc/homebrew/Formula-Cookbook.md Formula Cookbook # @see https://github.com/bbatsov/ruby-style-guide Ruby Style Guide # #
class Wget < Formula # homepage "https://www.gnu.org/software/wget/" # url "https://ftp.gnu.org/gnu/wget/wget-1.15.tar.gz" # sha256 "52126be8cf1bddd7536886e74c053ad7d0ed2aa89b4b630f76785bac21695fcd" # # def install # system "./configure", "--prefix=#{prefix}" # system "make", "install" # end # endclass Formula include FileUtils include Utils::Inreplace extend Enumerable # @!method inreplace(paths, before = nil, after = nil) # Actually implemented in {Utils::Inreplace.inreplace}. # Sometimes we have to change a bit before we install. Mostly we # prefer a patch but if you need the `prefix` of this formula in the # patch you have to resort to `inreplace`, because in the patch # you don't have access to any var defined by the formula. Only # HOMEBREW_PREFIX is available in the embedded patch. # inreplace supports regular expressions. #
inreplace "somefile.cfg", /look[for]what?/, "replace by #{bin}/tool"# @see Utils::Inreplace.inreplace # The name of this {Formula}. # e.g. `this-formula` attr_reader :name # The fully-qualified name of this {Formula}. # For core formula it's the same as {#name}. # e.g. `homebrew/tap-name/this-formula` attr_reader :full_name # The full path to this {Formula}. # e.g. `/usr/local/Library/Taps/homebrew/homebrew-core/Formula/this-formula.rb` attr_reader :path # The {Tap} instance associated with this {Formula}. # If it's
nil
, then this formula is loaded from path or URL.
# @private
attr_reader :tap
# The stable (and default) {SoftwareSpec} for this {Formula}
# This contains all the attributes (e.g. URL, checksum) that apply to the
# stable version of this formula.
# @private
attr_reader :stable
# The development {SoftwareSpec} for this {Formula}.
# Installed when using `brew install --devel`
# `nil` if there is no development version.
# @see #stable
# @private
attr_reader :devel
# The HEAD {SoftwareSpec} for this {Formula}.
# Installed when using `brew install --HEAD`
# This is always installed with the version `HEAD` and taken from the latest
# commit in the version control system.
# `nil` if there is no HEAD version.
# @see #stable
# @private
attr_reader :head
# The currently active {SoftwareSpec}.
# @see #determine_active_spec
attr_reader :active_spec
protected :active_spec
# A symbol to indicate currently active {SoftwareSpec}.
# It's either :stable, :devel or :head
# @see #active_spec
# @private
attr_reader :active_spec_sym
# most recent modified time for source files
# @private
attr_reader :source_modified_time
# Used for creating new Homebrew versions of software without new upstream
# versions.
# @see .revision
attr_reader :revision
# The current working directory during builds.
# Will only be non-`nil` inside {#install}.
attr_reader :buildpath
# The current working directory during tests.
# Will only be non-`nil` inside {#test}.
attr_reader :testpath
# When installing a bottle (binary package) from a local path this will be
# set to the full path to the bottle tarball. If not, it will be `nil`.
# @private
attr_accessor :local_bottle_path
# The {BuildOptions} for this {Formula}. Lists the arguments passed and any
# {#options} in the {Formula}. Note that these may differ at different times
# during the installation of a {Formula}. This is annoying but the result of
# state that we're trying to eliminate.
# @return [BuildOptions]
attr_accessor :build
# @private
def initialize(name, path, spec)
@name = name
@path = path
@revision = self.class.revision || 0
if path == Formulary.core_path(name)
@tap = CoreTap.instance
@full_name = name
elsif path.to_s =~ HOMEBREW_TAP_PATH_REGEX
@tap = Tap.fetch($1, $2)
@full_name = "#{@tap}/#{name}"
else
@tap = nil
@full_name = name
end
set_spec :stable
set_spec :devel
set_spec :head
@active_spec = determine_active_spec(spec)
@active_spec_sym = if head?
:head
elsif devel?
:devel
else
:stable
end
validate_attributes!
@build = active_spec.build
@pin = FormulaPin.new(self)
end
# @private
def set_active_spec(spec_sym)
spec = send(spec_sym)
raise FormulaSpecificationError, "#{spec_sym} spec is not available for #{full_name}" unless spec
@active_spec = spec
@active_spec_sym = spec_sym
validate_attributes!
@build = active_spec.build
end
private
def set_spec(name)
spec = self.class.send(name)
if spec.url
spec.owner = self
instance_variable_set("@#{name}", spec)
end
end
def determine_active_spec(requested)
spec = send(requested) || stable || devel || head
spec || raise(FormulaSpecificationError, "formulae require at least a URL")
end
def validate_attributes!
if name.nil? || name.empty? || name =~ /\s/
raise FormulaValidationError.new(full_name, :name, name)
end
url = active_spec.url
if url.nil? || url.empty? || url =~ /\s/
raise FormulaValidationError.new(full_name, :url, url)
end
val = version.respond_to?(:to_str) ? version.to_str : version
if val.nil? || val.empty? || val =~ /\s/
raise FormulaValidationError.new(full_name, :version, val)
end
end
public
# Is the currently active {SoftwareSpec} a {#stable} build?
# @private
def stable?
active_spec == stable
end
# Is the currently active {SoftwareSpec} a {#devel} build?
# @private
def devel?
active_spec == devel
end
# Is the currently active {SoftwareSpec} a {#head} build?
# @private
def head?
active_spec == head
end
# @private
def bottle_unneeded?
active_spec.bottle_unneeded?
end
# @private
def bottle_disabled?
active_spec.bottle_disabled?
end
# @private
def bottle_disable_reason
active_spec.bottle_disable_reason
end
# Does the currently active {SoftwareSpec} has any bottle?
# @private
def bottle_defined?
active_spec.bottle_defined?
end
# Does the currently active {SoftwareSpec} has an installable bottle?
# @private
def bottled?
active_spec.bottled?
end
# @private
def bottle_specification
active_spec.bottle_specification
end
# The Bottle object for the currently active {SoftwareSpec}.
# @private
def bottle
Bottle.new(self, bottle_specification) if bottled?
end
# The description of the software.
# @see .desc
def desc
self.class.desc
end
# The homepage for the software.
# @see .homepage
def homepage
self.class.homepage
end
# The version for the currently active {SoftwareSpec}.
# The version is autodetected from the URL and/or tag so only needs to be
# declared if it cannot be autodetected correctly.
# @see .version
def version
active_spec.version
end
# The {PkgVersion} for this formula with {version} and {#revision} information.
def pkg_version
PkgVersion.new(version, revision)
end
# A named Resource for the currently active {SoftwareSpec}.
# Additional downloads can be defined as {#resource}s.
# {Resource#stage} will create a temporary directory and yield to a block.
# resource("additional_files").stage { bin.install "my/extra/tool" }def resource(name) active_spec.resource(name) end # An old name for the formula def oldname @oldname ||= if tap formula_renames = tap.formula_renames if formula_renames.value?(name) formula_renames.to_a.rassoc(name).first end end end # All of aliases for the formula def aliases @aliases ||= if tap tap.alias_reverse_table[full_name] || [] else [] end end # The {Resource}s for the currently active {SoftwareSpec}. def resources active_spec.resources.values end # The {Dependency}s for the currently active {SoftwareSpec}. # @private def deps active_spec.deps end # The {Requirement}s for the currently active {SoftwareSpec}. # @private def requirements active_spec.requirements end # The cached download for the currently active {SoftwareSpec}. # @private def cached_download active_spec.cached_download end # Deletes the download for the currently active {SoftwareSpec}. # @private def clear_cache active_spec.clear_cache end # The list of patches for the currently active {SoftwareSpec}. # @private def patchlist active_spec.patches end # The options for the currently active {SoftwareSpec}. # @private def options active_spec.options end # The deprecated options for the currently active {SoftwareSpec}. # @private def deprecated_options active_spec.deprecated_options end # The deprecated option flags for the currently active {SoftwareSpec}. # @private def deprecated_flags active_spec.deprecated_flags end # If a named option is defined for the currently active {SoftwareSpec}. def option_defined?(name) active_spec.option_defined?(name) end # All the {.fails_with} for the currently active {SoftwareSpec}. # @private def compiler_failures active_spec.compiler_failures end # If this {Formula} is installed. # This is actually just a check for if the {#installed_prefix} directory # exists and is not empty. # @private def installed? (dir = installed_prefix).directory? && dir.children.length > 0 end # If at least one version of {Formula} is installed. # @private def any_version_installed? require "tab" installed_prefixes.any? { |keg| (keg/Tab::FILENAME).file? } end # @private # The `LinkedKegs` directory for this {Formula}. # You probably want {#opt_prefix} instead. def linked_keg Pathname.new("#{HOMEBREW_LIBRARY}/LinkedKegs/#{name}") end # The latest prefix for this formula. Checks for {#head}, then {#devel} # and then {#stable}'s {#prefix} # @private def installed_prefix if head && (head_prefix = prefix(PkgVersion.new(head.version, revision))).directory? head_prefix elsif devel && (devel_prefix = prefix(PkgVersion.new(devel.version, revision))).directory? devel_prefix elsif stable && (stable_prefix = prefix(PkgVersion.new(stable.version, revision))).directory? stable_prefix else prefix end end # The currently installed version for this formula. Will raise an exception # if the formula is not installed. # @private def installed_version Keg.new(installed_prefix).version end # The directory in the cellar that the formula is installed to. # This directory contains the formula's name and version. def prefix(v = pkg_version) Pathname.new("#{HOMEBREW_CELLAR}/#{name}/#{v}") end # The parent of the prefix; the named directory in the cellar containing all # installed versions of this software # @private def rack prefix.parent end # All of current installed prefix directories. # @private def installed_prefixes rack.directory? ? rack.subdirs : [] end # All of current installed kegs. # @private def installed_kegs installed_prefixes.map { |dir| Keg.new(dir) } end # The directory where the formula's binaries should be installed. # This is symlinked into `HOMEBREW_PREFIX` after installation or with # `brew link` for formulae that are not keg-only. # # Need to install into the {.bin} but the makefile doesn't mkdir -p prefix/bin? #
bin.mkpath# # No `make install` available? #
bin.install "binary1"def bin prefix+"bin" end # The directory where the formula's documentation should be installed. # This is symlinked into `HOMEBREW_PREFIX` after installation or with # `brew link` for formulae that are not keg-only. def doc share+"doc"+name end # The directory where the formula's headers should be installed. # This is symlinked into `HOMEBREW_PREFIX` after installation or with # `brew link` for formulae that are not keg-only. # # No `make install` available? #
include.install "example.h"def include prefix+"include" end # The directory where the formula's info files should be installed. # This is symlinked into `HOMEBREW_PREFIX` after installation or with # `brew link` for formulae that are not keg-only. def info share+"info" end # The directory where the formula's libraries should be installed. # This is symlinked into `HOMEBREW_PREFIX` after installation or with # `brew link` for formulae that are not keg-only. # # No `make install` available? #
lib.install "example.dylib"def lib prefix+"lib" end # The directory where the formula's binaries should be installed. # This is not symlinked into `HOMEBREW_PREFIX`. # It is also commonly used to install files that we do not wish to be # symlinked into HOMEBREW_PREFIX from one of the other directories and # instead manually create symlinks or wrapper scripts into e.g. {#bin}. def libexec prefix+"libexec" end # The root directory where the formula's manual pages should be installed. # This is symlinked into `HOMEBREW_PREFIX` after installation or with # `brew link` for formulae that are not keg-only. # Often one of the more specific `man` functions should be used instead # e.g. {#man1} def man share+"man" end # The directory where the formula's man1 pages should be installed. # This is symlinked into `HOMEBREW_PREFIX` after installation or with # `brew link` for formulae that are not keg-only. # # No `make install` available? #
man1.install "example.1"def man1 man+"man1" end # The directory where the formula's man2 pages should be installed. # This is symlinked into `HOMEBREW_PREFIX` after installation or with # `brew link` for formulae that are not keg-only. def man2 man+"man2" end # The directory where the formula's man3 pages should be installed. # This is symlinked into `HOMEBREW_PREFIX` after installation or with # `brew link` for formulae that are not keg-only. # # No `make install` available? #
man3.install "man.3"def man3 man+"man3" end # The directory where the formula's man4 pages should be installed. # This is symlinked into `HOMEBREW_PREFIX` after installation or with # `brew link` for formulae that are not keg-only. def man4 man+"man4" end # The directory where the formula's man5 pages should be installed. # This is symlinked into `HOMEBREW_PREFIX` after installation or with # `brew link` for formulae that are not keg-only. def man5 man+"man5" end # The directory where the formula's man6 pages should be installed. # This is symlinked into `HOMEBREW_PREFIX` after installation or with # `brew link` for formulae that are not keg-only. def man6 man+"man6" end # The directory where the formula's man7 pages should be installed. # This is symlinked into `HOMEBREW_PREFIX` after installation or with # `brew link` for formulae that are not keg-only. def man7 man+"man7" end # The directory where the formula's man8 pages should be installed. # This is symlinked into `HOMEBREW_PREFIX` after installation or with # `brew link` for formulae that are not keg-only. def man8 man+"man8" end # The directory where the formula's `sbin` binaries should be installed. # This is symlinked into `HOMEBREW_PREFIX` after installation or with # `brew link` for formulae that are not keg-only. # Generally we try to migrate these to {#bin} instead. def sbin prefix+"sbin" end # The directory where the formula's shared files should be installed. # This is symlinked into `HOMEBREW_PREFIX` after installation or with # `brew link` for formulae that are not keg-only. # # Need a custom directory? #
(share/"concept").mkpath# # Installing something into another custom directory? #
(share/"concept2").install "ducks.txt"# # Install `./example_code/simple/ones` to share/demos #
(share/"demos").install "example_code/simple/ones"# # Install `./example_code/simple/ones` to share/demos/examples #
(share/"demos").install "example_code/simple/ones" => "examples"def share prefix+"share" end # The directory where the formula's shared files should be installed, # with the name of the formula appended to avoid linking conflicts. # This is symlinked into `HOMEBREW_PREFIX` after installation or with # `brew link` for formulae that are not keg-only. # # No `make install` available? #
pkgshare.install "examples"def pkgshare prefix+"share"+name end # The directory where Emacs Lisp files should be installed, with the # formula name appended to avoid linking conflicts. # # Install an Emacs mode included with a software package: #
elisp.install "contrib/emacs/example-mode.el"def elisp prefix+"share/emacs/site-lisp"+name end # The directory where the formula's Frameworks should be installed. # This is symlinked into `HOMEBREW_PREFIX` after installation or with # `brew link` for formulae that are not keg-only. # This is not symlinked into `HOMEBREW_PREFIX`. def frameworks prefix+"Frameworks" end # The directory where the formula's kernel extensions should be installed. # This is symlinked into `HOMEBREW_PREFIX` after installation or with # `brew link` for formulae that are not keg-only. # This is not symlinked into `HOMEBREW_PREFIX`. def kext_prefix prefix+"Library/Extensions" end # The directory where the formula's configuration files should be installed. # Anything using `etc.install` will not overwrite other files on e.g. upgrades # but will write a new file named `*.default`. # This directory is not inside the `HOMEBREW_CELLAR` so it is persisted # across upgrades. def etc (HOMEBREW_PREFIX+"etc").extend(InstallRenamed) end # The directory where the formula's variable files should be installed. # This directory is not inside the `HOMEBREW_CELLAR` so it is persisted # across upgrades. def var HOMEBREW_PREFIX+"var" end # The directory where the formula's Bash completion files should be # installed. # This is symlinked into `HOMEBREW_PREFIX` after installation or with # `brew link` for formulae that are not keg-only. def bash_completion prefix+"etc/bash_completion.d" end # The directory where the formula's ZSH completion files should be # installed. # This is symlinked into `HOMEBREW_PREFIX` after installation or with # `brew link` for formulae that are not keg-only. def zsh_completion share+"zsh/site-functions" end # The directory where the formula's fish completion files should be # installed. # This is symlinked into `HOMEBREW_PREFIX` after installation or with # `brew link` for formulae that are not keg-only. def fish_completion share+"fish/vendor_completions.d" end # The directory used for as the prefix for {#etc} and {#var} files on # installation so, despite not being in `HOMEBREW_CELLAR`, they are installed # there after pouring a bottle. # @private def bottle_prefix prefix+".bottle" end # The directory where the formula's installation logs will be written. # @private def logs HOMEBREW_LOGS+name end # This method can be overridden to provide a plist. # For more examples read Apple's handy manpage: # https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man5/plist.5.html #
def plist; <<-EOS.undent # # #def plist nil end alias_method :startup_plist, :plist # The generated launchd {.plist} service name. def plist_name "homebrew.mxcl."+name end # The generated launchd {.plist} file path. def plist_path prefix+(plist_name+".plist") end # @private def plist_manual self.class.plist_manual end # @private def plist_startup self.class.plist_startup end # A stable path for this formula, when installed. Contains the formula name # but no version number. Only the active version will be linked here if # multiple versions are installed. # # This is the prefered way to refer a formula in plists or from another # formula, as the path is stable even when the software is updated. ## # EOS #end# Label ##{plist_name} #ProgramArguments ## ##{opt_bin}/example #--do-this #RunAtLoad ## KeepAlive ## StandardErrorPath #/dev/null #StandardOutPath #/dev/null #
args << "--with-readline=#{Formula["readline"].opt_prefix}" if build.with? "readline"def opt_prefix Pathname.new("#{HOMEBREW_PREFIX}/opt/#{name}") end def opt_bin opt_prefix+"bin" end def opt_include opt_prefix+"include" end def opt_lib opt_prefix+"lib" end def opt_libexec opt_prefix+"libexec" end def opt_sbin opt_prefix+"sbin" end def opt_share opt_prefix+"share" end def opt_pkgshare opt_prefix+"share"+name end def opt_elisp opt_prefix+"share/emacs/site-lisp"+name end def opt_frameworks opt_prefix+"Frameworks" end # Indicates that this formula supports bottles. (Not necessarily that one # should be used in the current installation run.) # Can be overridden to selectively disable bottles from formulae. # Defaults to true so overridden version does not have to check if bottles # are supported. # Replaced by {.pour_bottle}'s `satisfy` method if it is specified. def pour_bottle? true end # @private def pour_bottle_check_unsatisfied_reason self.class.pour_bottle_check_unsatisfied_reason end # Can be overridden to run commands on both source and bottle installation. def post_install; end # @private def post_install_defined? method(:post_install).owner == self.class end # @private def run_post_install build, self.build = self.build, Tab.for_formula(self) post_install ensure self.build = build end # Tell the user about any caveats regarding this package. # @return [String] #
def caveats # <<-EOS.undent # Are optional. Something the user should know? # EOS # end# #
def caveats # s = <<-EOS.undent # Print some important notice to the user when `brew infodef caveats nil end # rarely, you don't want your library symlinked into the main prefix # see gettext.rb for an example def keg_only? keg_only_reason && keg_only_reason.valid? end # @private def keg_only_reason self.class.keg_only_reason end # sometimes the formula cleaner breaks things # skip cleaning paths in a formula with a class method like this: # skip_clean "bin/foo", "lib/bar" # keep .la files with: # skip_clean :la # @private def skip_clean?(path) return true if path.extname == ".la" && self.class.skip_clean_paths.include?(:la) to_check = path.relative_path_from(prefix).to_s self.class.skip_clean_paths.include? to_check end # Sometimes we accidentally install files outside prefix. After we fix that, # users will get nasty link conflict error. So we create a whitelist here to # allow overwriting certain files. e.g. # link_overwrite "bin/foo", "lib/bar" # link_overwrite "share/man/man1/baz-*" # @private def link_overwrite?(path) # Don't overwrite files not created by Homebrew. return false unless path.stat.uid == HOMEBREW_BREW_FILE.stat.uid # Don't overwrite files belong to other keg except when that # keg's formula is deleted. begin keg = Keg.for(path) rescue NotAKegError, Errno::ENOENT # file doesn't belong to any keg. else tab_tap = Tab.for_keg(keg).tap return false if tab_tap.nil? # this keg doesn't below to any core/tap formula, most likely coming from a DIY install. begin Formulary.factory(keg.name) rescue FormulaUnavailableError # formula for this keg is deleted, so defer to whitelist rescue TapFormulaAmbiguityError, TapFormulaWithOldnameAmbiguityError return false # this keg belongs to another formula else return false # this keg belongs to another formula end end to_check = path.relative_path_from(HOMEBREW_PREFIX).to_s self.class.link_overwrite_paths.any? do |p| p == to_check || to_check.start_with?(p.chomp("/") + "/") || /^#{Regexp.escape(p).gsub('\*', ".*?")}$/ === to_check end end def skip_cxxstdlib_check? false end # @private def require_universal_deps? false end # @private def patch unless patchlist.empty? ohai "Patching" patchlist.each(&:apply) end end # yields |self,staging| with current working directory set to the uncompressed tarball # where staging is a Mktemp staging context # @private def brew stage do |staging| staging.retain! if ARGV.keep_tmp? prepare_patches begin yield self, staging rescue StandardError staging.retain! if ARGV.interactive? || ARGV.debug? raise ensure cp Dir["config.log", "CMakeCache.txt"], logs end end end # @private def lock @lock = FormulaLock.new(name) @lock.lock if oldname && (oldname_rack = HOMEBREW_CELLAR/oldname).exist? && oldname_rack.resolved_path == rack @oldname_lock = FormulaLock.new(oldname) @oldname_lock.lock end end # @private def unlock @lock.unlock unless @lock.nil? @oldname_lock.unlock unless @oldname_lock.nil? end # @private def outdated_versions @outdated_versions ||= begin all_versions = [] older_or_same_tap_versions = [] if oldname && !rack.exist? && (dir = HOMEBREW_CELLAR/oldname).directory? && !dir.subdirs.empty? && tap == Tab.for_keg(dir.subdirs.first).tap raise Migrator::MigrationNeededError.new(self) end installed_kegs.each do |keg| version = keg.version all_versions << version older_version = pkg_version <= version tab_tap = Tab.for_keg(keg).tap if tab_tap.nil? || tab_tap == tap || older_version older_or_same_tap_versions << version end end if older_or_same_tap_versions.all? { |v| pkg_version > v } all_versions.sort! else [] end end end # @private def outdated? outdated_versions.any? rescue Migrator::MigrationNeededError true end # @private def pinnable? @pin.pinnable? end # @private def pinned? @pin.pinned? end # @private def pinned_version @pin.pinned_version end # @private def pin @pin.pin end # @private def unpin @pin.unpin end # @private def ==(other) instance_of?(other.class) && name == other.name && active_spec == other.active_spec end alias_method :eql?, :== # @private def hash name.hash end # @private def <=>(other) return unless Formula === other name <=> other.name end def to_s name end # @private def inspect "#` is # called or when brewing a formula. # This is optional. You can use all the vars like #{version} here. # EOS # s += "Some issue only on older systems" if MacOS.version < :mountain_lion # s # end
def install # system "./configure", "--prefix=#{prefix}" # system "make", "install" # enddef install end protected def setup_home(home) # keep Homebrew's site-packages in sys.path when using system Python user_site_packages = home/"Library/Python/2.7/lib/python/site-packages" user_site_packages.mkpath (user_site_packages/"homebrew.pth").write <<-EOS.undent import site; site.addsitedir("#{HOMEBREW_PREFIX}/lib/python2.7/site-packages") import sys, os; sys.path = (os.environ["PYTHONPATH"].split(os.pathsep) if "PYTHONPATH" in os.environ else []) + ["#{HOMEBREW_PREFIX}/lib/python2.7/site-packages"] + sys.path EOS end public # To call out to the system, we use the `system` method and we prefer # you give the args separately as in the line below, otherwise a subshell # has to be opened first. #
system "./bootstrap.sh", "--arg1", "--prefix=#{prefix}"# # For CMake we have some necessary defaults in {#std_cmake_args}: #
system "cmake", ".", *std_cmake_args# # If the arguments given to configure (or make or cmake) are depending # on options defined above, we usually make a list first and then # use the `args << if
args = ["--with-option1", "--with-option2"] # # # Most software still uses `configure` and `make`. # # Check with `./configure --help` what our options are. # system "./configure", "--disable-debug", "--disable-dependency-tracking", # "--disable-silent-rules", "--prefix=#{prefix}", # *args # our custom arg list (needs `*` to unpack) # # # If there is a "make", "install" available, please use it! # system "make", "install"def system(cmd, *args) verbose = ARGV.verbose? verbose_using_dots = !ENV["HOMEBREW_VERBOSE_USING_DOTS"].nil? # remove "boring" arguments so that the important ones are more likely to # be shown considering that we trim long ohai lines to the terminal width pretty_args = args.dup if cmd == "./configure" && !verbose pretty_args.delete "--disable-dependency-tracking" pretty_args.delete "--disable-debug" end pretty_args.each_index do |i| if pretty_args[i].to_s.start_with? "import setuptools" pretty_args[i] = "import setuptools..." end end ohai "#{cmd} #{pretty_args*" "}".strip @exec_count ||= 0 @exec_count += 1 logfn = "#{logs}/%02d.%s" % [@exec_count, File.basename(cmd).split(" ").first] logs.mkpath File.open(logfn, "w") do |log| log.puts Time.now, "", cmd, args, "" log.flush if verbose rd, wr = IO.pipe begin pid = fork do rd.close log.close exec_cmd(cmd, args, wr, logfn) end wr.close if verbose_using_dots last_dot = Time.at(0) while buf = rd.gets log.puts buf # make sure dots printed with interval of at least 1 min. if (Time.now - last_dot) > 60 print "." $stdout.flush last_dot = Time.now end end puts else while buf = rd.gets log.puts buf puts buf end end ensure rd.close end else pid = fork { exec_cmd(cmd, args, log, logfn) } end Process.wait(pid) $stdout.flush unless $?.success? log_lines = ENV["HOMEBREW_FAIL_LOG_LINES"] log_lines ||= "15" log.flush if !verbose || verbose_using_dots puts "Last #{log_lines} lines from #{logfn}:" Kernel.system "/usr/bin/tail", "-n", log_lines, logfn end log.puts require "cmd/config" require "build_environment" env = ENV.to_hash Homebrew.dump_verbose_config(log) log.puts Homebrew.dump_build_env(env, log) raise BuildError.new(self, cmd, args, env) end end end # @private def eligible_kegs_for_cleanup eligible_for_cleanup = [] if installed? eligible_kegs = installed_kegs.select { |k| pkg_version > k.version } if eligible_kegs.any? eligible_kegs.each do |keg| if keg.linked? opoo "Skipping (old) #{keg} due to it being linked" else eligible_for_cleanup << keg end end end elsif installed_prefixes.any? && !pinned? # If the cellar only has one version installed, don't complain # that we can't tell which one to keep. Don't complain at all if the # only installed version is a pinned formula. opoo "Skipping #{full_name}: most recent version #{pkg_version} not installed" end eligible_for_cleanup end private def exec_cmd(cmd, args, out, logfn) ENV["HOMEBREW_CC_LOG_PATH"] = logfn # TODO: system "xcodebuild" is deprecated, this should be removed soon. if cmd.to_s.start_with? "xcodebuild" ENV.remove_cc_etc end # Turn on argument filtering in the superenv compiler wrapper. # We should probably have a better mechanism for this than adding # special cases to this method. if cmd == "python" setup_py_in_args = %w[setup.py build.py].include?(args.first) setuptools_shim_in_args = args.any? { |a| a.to_s.start_with? "import setuptools" } if setup_py_in_args || setuptools_shim_in_args ENV.refurbish_args end end $stdout.reopen(out) $stderr.reopen(out) out.close args.collect!(&:to_s) exec(cmd, *args) rescue nil puts "Failed to execute: #{cmd}" exit! 1 # never gets here unless exec threw or failed end def stage active_spec.stage do |staging| @source_modified_time = active_spec.source_modified_time @buildpath = Pathname.pwd env_home = buildpath/".brew_home" mkdir_p env_home old_home, ENV["HOME"] = ENV["HOME"], env_home setup_home env_home begin yield staging ensure @buildpath = nil ENV["HOME"] = old_home end end end def prepare_patches active_spec.add_legacy_patches(patches) if respond_to?(:patches) patchlist.grep(DATAPatch) { |p| p.path = path } patchlist.each do |patch| patch.verify_download_integrity(patch.fetch) if patch.external? end end def self.method_added(method) case method when :brew raise "You cannot override Formula#brew in class #{name}" when :test define_method(:test_defined?) { true } when :options instance = allocate specs.each do |spec| instance.options.each do |opt, desc| spec.option(opt[/^--(.+)$/, 1], desc) end end remove_method(:options) end end # The methods below define the formula DSL. class << self include BuildEnvironmentDSL # The reason for why this software is not linked (by default) to # {::HOMEBREW_PREFIX}. # @private attr_reader :keg_only_reason # @!attribute [w] # A one-line description of the software. Used by users to get an overview # of the software and Homebrew maintainers. # Shows when running `brew info`. # #
desc "Example formula"attr_rw :desc # @!attribute [w] homepage # The homepage for the software. Used by users to get more information # about the software and Homebrew maintainers as a point of contact for # e.g. submitting patches. # Can be opened with running `brew home`. # #
homepage "https://www.example.com"attr_rw :homepage # The `:startup` attribute set by {.plist_options}. # @private attr_reader :plist_startup # The `:manual` attribute set by {.plist_options}. # @private attr_reader :plist_manual # If `pour_bottle?` returns `false` the user-visible reason to display for # why they cannot use the bottle. # @private attr_accessor :pour_bottle_check_unsatisfied_reason # @!attribute [w] revision # Used for creating new Homebrew versions of software without new upstream # versions. For example, if we bump the major version of a library this # {Formula} {.depends_on} then we may need to update the `revision` of this # {Formula} to install a new version linked against the new library version. # `0` if unset. # #
revision 1attr_rw :revision # A list of the {.stable}, {.devel} and {.head} {SoftwareSpec}s. # @private def specs @specs ||= [stable, devel, head].freeze end # @!attribute [w] url # The URL used to download the source for the {#stable} version of the formula. # We prefer `https` for security and proxy reasons. # Optionally specify the download strategy with `:using => ...` # `:git`, `:hg`, `:svn`, `:bzr`, `:cvs`, # `:curl` (normal file download. Will also extract.) # `:nounzip` (without extracting) # `:post` (download via an HTTP POST) # `S3DownloadStrategy` (download from S3 using signed request) # #
url "https://packed.sources.and.we.prefer.https.example.com/archive-1.2.3.tar.bz2"#
url "https://some.dont.provide.archives.example.com", :using => :git, :tag => "1.2.3", :revision => "db8e4de5b2d6653f66aea53094624468caad15d2"def url(val, specs = {}) stable.url(val, specs) end # @!attribute [w] version # The version string for the {#stable} version of the formula. # The version is autodetected from the URL and/or tag so only needs to be # declared if it cannot be autodetected correctly. # #
version "1.2-final"def version(val = nil) stable.version(val) end # @!attribute [w] mirror # Additional URLs for the {#stable} version of the formula. # These are only used if the {.url} fails to download. It's optional and # there can be more than one. Generally we add them when the main {.url} # is unreliable. If {.url} is really unreliable then we may swap the # {.mirror} and {.url}. # #
mirror "https://in.case.the.host.is.down.example.com" # mirror "https://in.case.the.mirror.is.down.example.comdef mirror(val) stable.mirror(val) end # @!attribute [w] sha256 # @scope class # To verify the {#cached_download}'s integrity and security we verify the # SHA-256 hash matches what we've declared in the {Formula}. To quickly fill # this value you can leave it blank and run `brew fetch --force` and it'll # tell you the currently valid value. # #
sha256 "2a2ba417eebaadcb4418ee7b12fe2998f26d6e6f7fda7983412ff66a741ab6f7"Checksum::TYPES.each do |type| define_method(type) { |val| stable.send(type, val) } end # @!attribute [w] bottle # Adds a {.bottle} {SoftwareSpec}. # This provides a pre-built binary package built by the Homebrew maintainers for you. # It will be installed automatically if there is a binary package for your platform # and you haven't passed or previously used any options on this formula. # # If you maintain your own repository, you can add your own bottle links. # https://github.com/Homebrew/brew/blob/master/share/doc/homebrew/Bottles.md # You can ignore this block entirely if submitting to Homebrew/Homebrew, It'll be # handled for you by the Brew Test Bot. # #
bottle do # root_url "https://example.com" # Optional root to calculate bottle URLs # prefix "/opt/homebrew" # Optional HOMEBREW_PREFIX in which the bottles were built. # cellar "/opt/homebrew/Cellar" # Optional HOMEBREW_CELLAR in which the bottles were built. # revision 1 # Making the old bottle outdated without bumping the version/revision of the formula. # sha256 "4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865" => :el_capitan # sha256 "53c234e5e8472b6ac51c1ae1cab3fe06fad053beb8ebfd8977b010655bfdd3c3" => :yosemite # sha256 "1121cfccd5913f0a63fec40a6ffd44ea64f9dc135c66634ba001d10bcf4302a2" => :mavericks # end# # Only formulae where the upstream URL breaks or moves frequently, require compile # or have a reasonable amount of patches/resources should be bottled. # Formulae which do not meet the above requirements should not be bottled. # # Formulae which should not be bottled & can be installed without any compile # required should be tagged with: #
bottle :unneeded# # Otherwise formulae which do not meet the above requirements and should not # be bottled should be tagged with: #
bottle :disable, "reasons"def bottle(*args, &block) stable.bottle(*args, &block) end # @private def build stable.build end # @!attribute [w] stable # Allows adding {.depends_on} and {#patch}es just to the {.stable} {SoftwareSpec}. # This is required instead of using a conditional. # It is preferrable to also pull the {url} and {.sha256} into the block if one is added. # #
stable do # url "https://example.com/foo-1.0.tar.gz" # sha256 "2a2ba417eebaadcb4418ee7b12fe2998f26d6e6f7fda7983412ff66a741ab6f7" # # depends_on "libxml2" # depends_on "libffi" # enddef stable(&block) @stable ||= SoftwareSpec.new return @stable unless block_given? @stable.instance_eval(&block) end # @!attribute [w] devel # Adds a {.devel} {SoftwareSpec}. # This can be installed by passing the `--devel` option to allow # installing non-stable (e.g. beta) versions of software. # #
devel do # url "https://example.com/archive-2.0-beta.tar.gz" # sha256 "2a2ba417eebaadcb4418ee7b12fe2998f26d6e6f7fda7983412ff66a741ab6f7" # # depends_on "cairo" # depends_on "pixman" # enddef devel(&block) @devel ||= SoftwareSpec.new return @devel unless block_given? @devel.instance_eval(&block) end # @!attribute [w] head # Adds a {.head} {SoftwareSpec}. # This can be installed by passing the `--HEAD` option to allow # installing software directly from a branch of a version-control repository. # If called as a method this provides just the {url} for the {SoftwareSpec}. # If a block is provided you can also add {.depends_on} and {#patch}es just to the {.head} {SoftwareSpec}. # The download strategies (e.g. `:using =>`) are the same as for {url}. # `master` is the default branch and doesn't need stating with a `:branch` parameter. #
head "https://we.prefer.https.over.git.example.com/.git"#
head "https://example.com/.git", :branch => "name_of_branch", :revision => "abc123"# or (if autodetect fails): #
head "https://hg.is.awesome.but.git.has.won.example.com/", :using => :hgdef head(val = nil, specs = {}, &block) @head ||= HeadSoftwareSpec.new if block_given? @head.instance_eval(&block) elsif val @head.url(val, specs) else @head end end # Additional downloads can be defined as resources and accessed in the # install method. Resources can also be defined inside a stable, devel, or # head block. This mechanism replaces ad-hoc "subformula" classes. #
resource "additional_files" do # url "https://example.com/additional-stuff.tar.gz" # sha256 "c6bc3f48ce8e797854c4b865f6a8ff969867bbcaebd648ae6fd825683e59fef2" # enddef resource(name, klass = Resource, &block) specs.each do |spec| spec.resource(name, klass, &block) unless spec.resource_defined?(name) end end def go_resource(name, &block) specs.each { |spec| spec.go_resource(name, &block) } end # The dependencies for this formula. Use strings for the names of other # formulae. Homebrew provides some :special dependencies for stuff that # requires certain extra handling (often changing some ENV vars or # deciding if to use the system provided version or not.) #
# `:build` means this dep is only needed during build. # depends_on "cmake" => :build#
depends_on "homebrew/dupes/tcl-tk" => :optional#
# `:recommended` dependencies are built by default. # # But a `--without-...` option is generated to opt-out. # depends_on "readline" => :recommended#
# `:optional` dependencies are NOT built by default. # # But a `--with-...` options is generated. # depends_on "glib" => :optional#
# If you need to specify that another formula has to be built with/out # # certain options (note, no `--` needed before the option): # depends_on "zeromq" => "with-pgm" # depends_on "qt" => ["with-qtdbus", "developer"] # Multiple options.#
# Optional and enforce that boost is built with `--with-c++11`. # depends_on "boost" => [:optional, "with-c++11"]#
# If a dependency is only needed in certain cases: # depends_on "sqlite" if MacOS.version == :leopard # depends_on :xcode # If the formula really needs full Xcode. # depends_on :tex # Homebrew does not provide a Tex Distribution. # depends_on :fortran # Checks that `gfortran` is available or `FC` is set. # depends_on :mpi => :cc # Needs MPI with `cc` # depends_on :mpi => [:cc, :cxx, :optional] # Is optional. MPI with `cc` and `cxx`. # depends_on :macos => :lion # Needs at least Mac OS X "Lion" aka. 10.7. # depends_on :apr # If a formula requires the CLT-provided apr library to exist. # depends_on :arch => :intel # If this formula only builds on Intel architecture. # depends_on :arch => :x86_64 # If this formula only builds on Intel x86 64-bit. # depends_on :arch => :ppc # Only builds on PowerPC? # depends_on :ld64 # Sometimes ld fails on `MacOS.version < :leopard`. Then use this. # depends_on :x11 # X11/XQuartz components. Non-optional X11 deps should go in Homebrew/Homebrew-x11 # depends_on :osxfuse # Permits the use of the upstream signed binary or our source package. # depends_on :tuntap # Does the same thing as above. This is vital for Yosemite and above. # depends_on :mysql => :recommended#
# It is possible to only depend on something if # # `build.with?` or `build.without? "another_formula"`: # depends_on :mysql # allows brewed or external mysql to be used # depends_on :postgresql if build.without? "sqlite" # depends_on :hg # Mercurial (external or brewed) is needed# #
# If any Python >= 2.7 < 3.x is okay (either from OS X or brewed): # depends_on :python#
# to depend on Python >= 2.7 but use system Python where possible # depends_on :python if MacOS.version <= :snow_leopard#
# Python 3.x if the `--with-python3` is given to `brew install example` # depends_on :python3 => :optionaldef depends_on(dep) specs.each { |spec| spec.depends_on(dep) } end # @!attribute [w] option # Options can be used as arguments to `brew install`. # To switch features on/off: `"with-something"` or `"with-otherthing"`. # To use other software: `"with-other-software"` or `"without-foo"` # Note, that for {.depends_on} that are `:optional` or `:recommended`, options # are generated automatically. # # There are also some special options: # - `:universal`: build a universal binary/library (e.g. on newer Intel Macs # this means a combined x86_64/x86 binary/library). #
option "with-spam", "The description goes here without a dot at the end"#
option "with-qt", "Text here overwrites the autogenerated one from 'depends_on "qt" => :optional'"#
option :universaldef option(name, description = "") specs.each { |spec| spec.option(name, description) } end # @!attribute [w] deprecated_option # Deprecated options are used to rename options and migrate users who used # them to newer ones. They are mostly used for migrating non-`with` options # (e.g. `enable-debug`) to `with` options (e.g. `with-debug`). #
deprecated_option "enable-debug" => "with-debug"def deprecated_option(hash) specs.each { |spec| spec.deprecated_option(hash) } end # External patches can be declared using resource-style blocks. #
patch do # url "https://example.com/example_patch.diff" # sha256 "c6bc3f48ce8e797854c4b865f6a8ff969867bbcaebd648ae6fd825683e59fef2" # end# # A strip level of `-p1` is assumed. It can be overridden using a symbol # argument: #
patch :p0 do # url "https://example.com/example_patch.diff" # sha256 "c6bc3f48ce8e797854c4b865f6a8ff969867bbcaebd648ae6fd825683e59fef2" # end# # Patches can be declared in stable, devel, and head blocks. This form is # preferred over using conditionals. #
stable do # patch do # url "https://example.com/example_patch.diff" # sha256 "c6bc3f48ce8e797854c4b865f6a8ff969867bbcaebd648ae6fd825683e59fef2" # end # end# # Embedded (`__END__`) patches are declared like so: #
patch :DATA # patch :p0, :DATA# # Patches can also be embedded by passing a string. This makes it possible # to provide multiple embedded patches while making only some of them # conditional. #
patch :p0, "..."def patch(strip = :p1, src = nil, &block) specs.each { |spec| spec.patch(strip, src, &block) } end # Defines launchd plist handling. # # Does your plist need to be loaded at startup? #
plist_options :startup => true# # Or only when necessary or desired by the user? #
plist_options :manual => "foo"# # Or perhaps you'd like to give the user a choice? Ooh fancy. #
plist_options :startup => "true", :manual => "foo start"def plist_options(options) @plist_startup = options[:startup] @plist_manual = options[:manual] end # @private def conflicts @conflicts ||= [] end # If this formula conflicts with another one. #
conflicts_with "imagemagick", :because => "because this is just a stupid example"def conflicts_with(*names) opts = Hash === names.last ? names.pop : {} names.each { |name| conflicts << FormulaConflict.new(name, opts[:because]) } end def skip_clean(*paths) paths.flatten! # Specifying :all is deprecated and will become an error skip_clean_paths.merge(paths) end # @private def skip_clean_paths @skip_clean_paths ||= Set.new end # Software that will not be sym-linked into the `brew --prefix` will only # live in its Cellar. Other formulae can depend on it and then brew will # add the necessary includes and libs (etc.) during the brewing of that # other formula. But generally, keg_only formulae are not in your PATH # and not seen by compilers if you build your own software outside of # Homebrew. This way, we don't shadow software provided by OS X. #
keg_only :provided_by_osx#
keg_only "because I want it so"def keg_only(reason, explanation = "") @keg_only_reason = KegOnlyReason.new(reason, explanation) end # Pass :skip to this method to disable post-install stdlib checking def cxxstdlib_check(check_type) define_method(:skip_cxxstdlib_check?) { true } if check_type == :skip end # Marks the {Formula} as failing with a particular compiler so it will fall back to others. # For Apple compilers, this should be in the format: #
fails_with :llvm do # :llvm is really llvm-gcc # build 2334 # cause "Segmentation fault during linking." # end # # fails_with :clang do # build 600 # cause "multiple configure and compile errors" # end# # The block may be omitted, and if present the build may be omitted; # if so, then the compiler will be blacklisted for *all* versions. # # `major_version` should be the major release number only, for instance # '4.8' for the GCC 4.8 series (4.8.0, 4.8.1, etc.). # If `version` or the block is omitted, then the compiler will be # blacklisted for all compilers in that series. # # For example, if a bug is only triggered on GCC 4.8.1 but is not # encountered on 4.8.2: # #
fails_with :gcc => '4.8' do # version '4.8.1' # enddef fails_with(compiler, &block) specs.each { |spec| spec.fails_with(compiler, &block) } end def needs(*standards) specs.each { |spec| spec.needs(*standards) } end # Test (is required for new formula and makes us happy). # @return [Boolean] # # The block will create, run in and delete a temporary directory. # # We are fine if the executable does not error out, so we know linking # and building the software was ok. #
system bin/"foobar", "--version"# #
(testpath/"test.file").write <<-EOS.undent # writing some test file, if you need to # EOS # assert_equal "OK", shell_output("test_command test.file").strip# # Need complete control over stdin, stdout? #
require "open3" # Open3.popen3("#{bin}/example", "argument") do |stdin, stdout, _| # stdin.write("some text") # stdin.close # assert_equal "result", stdout.read # end# # The test will fail if it returns false, or if an exception is raised. # Failed assertions and failed `system` commands will raise exceptions. def test(&block) define_method(:test, &block) end # Defines whether the {Formula}'s bottle can be used on the given Homebrew # installation. # # For example, if the bottle requires the Xcode CLT to be installed a # {Formula} would declare: #
pour_bottle? do # reason "The bottle needs the Xcode CLT to be installed." # satisfy { MacOS::CLT.installed? } # end# # If `satisfy` returns `false` then a bottle will not be used and instead # the {Formula} will be built from source and `reason` will be printed. def pour_bottle?(&block) @pour_bottle_check = PourBottleCheck.new(self) @pour_bottle_check.instance_eval(&block) end # @private def link_overwrite(*paths) paths.flatten! link_overwrite_paths.merge(paths) end # @private def link_overwrite_paths @link_overwrite_paths ||= Set.new end end end