Files
chromebrew/lib/package.rb
Maximilian Downey Twiss de24923ee8 Add Rubocop CI (#7083)
* Rename IgnoredPatterns to AllowedPatterns.

* Exclude docopt.rb (not our code) from Rubocop

* Disable Style/RedundantReturn

* Disable Style/MutableConstant

* Disable Style/NumericLiterals

* Set Layout/IndentationStyle to spaces

* Temporarily disable various cops.

* Add Rubocop CI via Octocop

* Lint tree with rubocop -A -c .rubocop.yml

Co-authored-by: Satadru Pramanik <satadru@gmail.com>
2022-08-22 13:31:25 -04:00

274 lines
10 KiB
Ruby

require 'English'
require_relative 'package_helpers'
class Package
property :description, :homepage, :version, :license, :compatibility,
:binary_url, :binary_sha256, :source_url, :source_sha256,
:git_branch, :git_hashtag
boolean_property = %i[conflicts_ok git_fetchtags gnome is_fake is_musl
is_static no_compile_needed no_env_options no_fhs
no_patchelf no_zstd patchelf git_clone_deep
no_git_submodules]
create_placeholder :preflight, # Function for checks to see if install should occur.
:patch, # Function to perform patch operations prior to build from source.
:prebuild, # Function to perform pre-build operations prior to build from source.
:build, # Function to perform build from source.
:postbuild, # Function to perform post-build for both source build and binary distribution.
:check, # Function to perform check from source build. (executes only during `crew build`)
:preinstall, # Function to perform pre-install operations prior to install.
:install, # Function to perform install from source build.
:postinstall, # Function to perform post-install for both source build and binary distribution.
:remove # Function to perform after package removal.
class << self
attr_accessor :name, :is_dep, :in_build, :build_from_source, :in_upgrade
end
def self.load_package(pkgFile, pkgName = File.basename(pkgFile, '.rb'))
# self.load_package: load a package under 'Package' class scope
#
className = pkgName.capitalize
# read and eval package script under 'Package' class
class_eval( File.read(pkgFile, encoding: Encoding::UTF_8), pkgFile ) unless const_defined?("Package::#{className}")
pkgObj = const_get(className)
pkgObj.name = pkgName
return pkgObj
end
def self.dependencies
# We need instance variable in derived class, so not define it here,
# base class. Instead of define it, we initialize it in a function
# called from derived classees.
@dependencies ||= {}
end
def self.get_deps_list(pkgName = name, hash: false, include_build_deps: 'auto', include_self: false,
pkgTags: [], highlight_build_deps: true, exclude_buildessential: false, top_level: true)
# get_deps_list: get dependencies list of pkgName (current package by default)
#
# pkgName: package to check dependencies, current package by default
# hash: return result in nested hash, used by `print_deps_tree` (`bin/crew`)
#
# include_build_deps: if set to true, force all build dependencies to be returned.
# if set to false, all build dependencies will not be returned
# if set to "auto" (default), return build dependencies if pre-built binaries not available
#
# include_self: include #{pkgName} itself in returned result, only used in recursive calls (see `expandedDeps` below)
# highlight_build_deps: include corresponding symbols in return value, you can convert it to actual ascii color codes later
# exclude_buildessential: do not insert `buildessential` dependency automatically
#
# top_level: if set to true, return satisfied dependencies
# (dependencies that might be a sub-dependency of a dependency that checked before),
# always set to false if this function is called in recursive loop (see `expandedDeps` below)
#
@checked_list ||= {} # create @checked_list placeholder if not exist
# add current package to @checked_list for preventing extra checks
@checked_list.merge!({ pkgName => pkgTags })
pkgObj = load_package("#{CREW_PACKAGES_PATH}/#{pkgName}.rb")
is_source = pkgObj.is_source?(ARCH.to_sym) or pkgObj.build_from_source
deps = pkgObj.dependencies
# append buildessential to deps if building from source is needed/specified
if ((include_build_deps == true) || ((include_build_deps == 'auto') && is_source)) && \
!pkgObj.no_compile_needed? && \
!exclude_buildessential && \
!@checked_list.keys.include?('buildessential')
deps = { 'buildessential' => [:build] }.merge(deps)
end
# parse dependencies recursively
expandedDeps = deps.uniq.map do |dep, depTags|
# check build dependencies only if building from source is needed/specified
next unless (include_build_deps == true) || \
((include_build_deps == 'auto') && is_source) || \
!depTags.include?(:build)
# overwrite tags if parent dependency is a build dependency
# (for build dependencies highlighting)
tags = pkgTags.include?(:build) ? pkgTags : depTags
if @checked_list.keys.none?(dep)
# check dependency by calling this function recursively
next send(__method__, dep,
hash: hash,
pkgTags: tags,
include_build_deps: include_build_deps,
highlight_build_deps: highlight_build_deps,
exclude_buildessential: exclude_buildessential,
include_self: true,
top_level: false)
elsif hash && top_level
# will be dropped here if current dependency is already checked and #{top_level} is set to true
#
# the '+' symbol tell `print_deps_tree` (`bin/crew`) to color this package as "satisfied dependency"
# the '*' symbol tell `print_deps_tree` (`bin/crew`) to color this package as "build dependency"
if highlight_build_deps && tags.include?(:build)
next { "+*#{dep}*+" => [] }
elsif highlight_build_deps
next { "+#{dep}+" => [] }
else
next { dep => [] }
end
end
end.compact
if hash
# the '*' symbol tell `print_deps_tree` (`bin/crew`) to color this package as "build dependency"
if highlight_build_deps && pkgTags.include?(:build)
return { "*#{pkgName}*" => expandedDeps }
else
return { pkgName => expandedDeps }
end
elsif include_self
# return pkgName itself if this function is called as a recursive loop (see `expandedDeps`)
return [expandedDeps, pkgName].flatten
else
# if this function is called outside of this function, return parsed dependencies only
return expandedDeps.flatten
end
end
boolean_property.each do |prop|
self.class.__send__(:attr_reader, prop.to_s)
class_eval <<~EOT, __FILE__, __LINE__ + 1
def self.#{prop} (#{prop} = nil)
@#{prop} = true if #{prop}
!!@#{prop}
end
EOT
instance_eval <<~EOY, __FILE__, __LINE__ + 1
def self.#{prop}
@#{prop} = true
end
EOY
# Adds the symbol? method
define_singleton_method("#{prop}?") do
@prop = instance_variable_get("@#{prop}")
!!@prop
end
end
def self.depends_on(dependency = nil)
@dependencies ||= {}
if dependency
# add element in "[ name, [ tag1, tag2, ... ] ]" format
if dependency.is_a?(Hash)
if dependency.first[1].is_a?(Array)
# parse "depends_on name => [ tag1, tag2 ]"
@dependencies.store(dependency.first[0], dependency.first[1])
else
# parse "depends_on name => tag"
@dependencies.store(dependency.first[0], [dependency.first[1]])
end
else
# parse "depends_on name"
@dependencies.store(dependency, [])
end
end
@dependencies
end
def self.get_url(architecture)
if !@build_from_source && @binary_url && @binary_url.key?(architecture)
return @binary_url[architecture]
elsif @source_url.respond_to?(:has_key?)
return @source_url.key?(architecture) ? @source_url[architecture] : nil
else
return @source_url
end
end
def self.get_binary_url(architecture)
return @binary_url.key?(architecture) ? @binary_url[architecture] : nil
end
def self.get_source_url(architecture)
return @source_url.key?(architecture) ? @source_url[architecture] : nil
end
def self.get_sha256(architecture)
if !@build_from_source && @binary_sha256 && @binary_sha256.key?(architecture)
return @binary_sha256[architecture]
elsif @source_sha256.respond_to?(:has_key?)
return @source_sha256.key?(architecture) ? @source_sha256[architecture] : nil
else
return @source_sha256
end
end
def self.get_extract_dir
"#{name}.#{Time.now.utc.strftime('%Y%m%d%H%M%S')}.dir"
end
def self.is_binary?(architecture)
if !@build_from_source && @binary_url && @binary_url.key?(architecture)
return true
else
return false
end
end
def self.is_source?(architecture)
if is_binary?(architecture) || is_fake?
return false
else
return true
end
end
def self.system(*args, **opt_args)
@crew_env_options_hash = if no_env_options?
{ 'CREW_DISABLE_ENV_OPTIONS' => '1' }
else
CREW_ENV_OPTIONS_HASH
end
# add "-j#" argument to "make" at compile-time, if necessary
# Order of precedence to assign the number of processors:
# 1. The value of '-j#' from the package make argument
# 2. The value of ENV["CREW_NPROC"]
# 3. The value of `nproc`.strip
# See lib/const.rb for more details
# add exception option to opt_args
opt_args.merge!(exception: true) unless opt_args.key?(:exception)
# extract env hash
if args[0].is_a?(Hash)
env = @crew_env_options_hash.merge(args[0])
args.delete_at(0) # remove env hash from args array
else
env = @crew_env_options_hash
end
# after removing the env hash, all remaining args must be command args
cmd_args = args
# add -j arg to build commands
if args.size == 1
# involve a shell if the command is passed in one single string
cmd_args = ['bash', '-c', cmd_args[0].sub(/^(make)\b/, "\\1 -j#{CREW_NPROC}")]
elsif cmd_args[0] == 'make'
cmd_args.insert(1, "-j#{CREW_NPROC}")
end
begin
Kernel.system(env, *cmd_args, **opt_args)
rescue StandardError => e
# print failed line number and error message
puts "#{e.backtrace[1]}: #{e.message}".orange
raise InstallError, "`#{env.map { |k, v| "#{k}=\"#{v}\"" }.join(' ')} #{cmd_args.join(' ')}` exited with #{$CHILD_STATUS.exitstatus}".lightred
end
end
end