Files
chromebrew/crew
satmandu 767fa55523 crew: Run ldconfig on install; Adjust where reinstall removes package to be reinstalled. (#4538)
* ldconfig  on install; move reinstall remove

Run ldconfig after package installs so apps don't complain about missing just installed libraries. Also, do reinstalls as part of the upgrade path so that package remove happens after the package to be reinstalled has been downloaded. This fixes upgrades on curl (or its dependencies) erroring out on being unable to download the new package using curl.

* bump version
2020-10-30 13:06:12 -05:00

1090 lines
33 KiB
Ruby
Executable File

#!/usr/bin/env ruby
require_relative 'lib/color'
# Disallow sudo
abort "Chromebrew should not be run as root.".lightred if Process.uid == 0
require 'find'
require 'net/http'
require 'uri'
require 'digest/sha2'
require 'json'
require 'fileutils'
require_relative 'lib/const'
require_relative 'lib/util'
# Add lib to LOAD_PATH
$LOAD_PATH.unshift "#{CREW_LIB_PATH}lib"
DOC = <<DOCOPT
Chromebrew - Package manager for Chrome OS http://skycocker.github.io/chromebrew/
Usage:
crew build [-k|--keep] [-v|--verbose] <name> ...
crew const [<name> ...]
crew download [-v|--verbose] <name> ...
crew files <name> ...
crew help [<command>]
crew install [-k|--keep] [-s|--build-from-source] [-S|--recursive-build] [-v|--verbose] <name> ...
crew list (available|installed|compatible|incompatible)
crew postinstall <name> ...
crew reinstall [-k|--keep] [-s|--build-from-source] [-S|--recursive-build] [-v|--verbose] <name> ...
crew remove [-v|--verbose] <name> ...
crew search [-v|--verbose] [<name> ...]
crew update
crew upgrade [-k|--keep] [-s|--build-from-source] [-v|--verbose] [<name> ...]
crew whatprovides <name> ...
-k --keep Keep the `CREW_BREW_DIR` (#{CREW_BREW_DIR}) directory.
-s --build-from-source Build from source even if pre-compiled binary exists.
-S --recursive-build Build from source, including all dependencies, even if pre-compiled binaries exist.
-V --version Display the crew version.
-v --verbose Show extra information.
-h --help Show this screen.
version #{CREW_VERSION}
DOCOPT
# Set XZ_OPT environment variable for build command.
# If CREW_XZ_OPT is defined, use it by default. Use `-7e`, otherwise.
if ENV["CREW_XZ_OPT"].to_s == ''
ENV["XZ_OPT"] = "-7e -T #{CREW_NPROC}"
else
ENV["XZ_OPT"] = ENV["CREW_XZ_OPT"]
end
# Parse arguments using docopt
require_relative 'lib/docopt'
begin
args = Docopt::docopt(DOC)
rescue Docopt::Exit => e
if ARGV[0] then
if ARGV[0] == '-V' or ARGV[0] == '--version' then
puts "#{CREW_VERSION}"
exit 0
end
if ARGV[0] != '-h' and ARGV[0] != '--help' then
puts "Could not understand \"crew #{ARGV.join(' ')}\".".lightred
cmds = ["build", "const", "download", "files", "help", "install", "list", "postinstall", "reinstall", "remove", "search", "update", "upgrade", "whatprovides"]
# Looking for similar commands
if not cmds.include?(ARGV[0]) then
similar = cmds.select {|word| edit_distance(ARGV[0], word) < 4}
if not similar.empty? then
puts "Did you mean?"
similar.each {|sug| puts " #{sug}"}
end
end
end
end
puts e.message
exit 1
end
@opt_keep = args["--keep"]
@opt_verbose = args["--verbose"]
@opt_src = args["--build-from-source"]
@opt_recursive = args["--recursive-build"]
@device = JSON.parse(File.read(CREW_CONFIG_PATH + 'device.json'), symbolize_names: true)
#symbolize also values
@device.each do |key, elem|
@device[key] = @device[key].to_sym rescue @device[key]
end
def print_package(pkgName, extra = false)
search pkgName, true
status = ''
status = 'installed' if @device[:installed_packages].any? do |elem| elem[:name] == pkgName end
status = 'incompatible' unless @device[:compatible_packages].any? do |elem| elem[:name] == pkgName end
case status
when 'installed'
print @pkg.name.lightgreen
when 'incompatible'
print @pkg.name.lightred
else
print @pkg.name
end
print ": #{@pkg.description}" if @pkg.description
if extra
puts ""
puts @pkg.homepage if @pkg.homepage
print "version #{@pkg.version}"
end
puts ""
end
def set_package (pkgName, silent = false)
require CREW_LIB_PATH + 'packages/' + pkgName
@pkg = Object.const_get(pkgName.capitalize)
@pkg.build_from_source = true if @opt_recursive
@pkg.name = pkgName
print_package(pkgName, true) unless silent
end
def list_packages
Find.find (CREW_LIB_PATH + 'packages') do |filename|
if File.extname(filename) == '.rb'
print_package File.basename filename, '.rb'
end
end
end
def list_available
Find.find (CREW_LIB_PATH + 'packages') do |filename|
@notInstalled = true
Find.find(CREW_CONFIG_PATH + 'meta/') do |packageList|
packageName = File.basename filename, '.rb'
@notInstalled = nil if packageList == CREW_CONFIG_PATH + 'meta/' + packageName + '.filelist'
end
if @notInstalled and File.extname(filename) == '.rb'
pkgName = File.basename filename, '.rb'
set_package pkgName, true
if @pkg.compatibility.include? 'all' or @pkg.compatibility.include? ARCH
puts pkgName
else
puts pkgName.lightred
end
end
end
end
def list_installed
Dir["#{CREW_CONFIG_PATH}/meta/*.directorylist"].sort.map { |f|
File.basename(f, ".directorylist").lightgreen
}
end
def list_compatible(compat = true)
Find.find (CREW_LIB_PATH + 'packages') do |filename|
if File.extname(filename) == '.rb'
pkgName = File.basename filename, '.rb'
set_package pkgName, true
if compat
if @pkg.compatibility.include? 'all' or @pkg.compatibility.include? ARCH
if File.exist? CREW_CONFIG_PATH + 'meta/' + pkgName + '.filelist'
puts pkgName.lightgreen
else
puts pkgName
end
end
else
unless @pkg.compatibility.include? 'all' or @pkg.compatibility.include? ARCH
puts pkgName.lightred
end
end
end
end
end
def generate_compatible
@device[:compatible_packages] = []
Find.find (CREW_LIB_PATH + 'packages') do |filename|
if File.extname(filename) == '.rb'
pkgName = File.basename filename, '.rb'
set_package pkgName, true
if @pkg.compatibility.include? 'all' or @pkg.compatibility.include? ARCH
#add to compatible packages
@device[:compatible_packages].push(name: @pkg.name)
end
end
end
File.open(CREW_CONFIG_PATH + 'device.json', 'w') do |file|
output = JSON.parse @device.to_json
file.write JSON.pretty_generate(output)
end
end
def search (pkgName, silent = false)
Find.find (CREW_LIB_PATH + 'packages') do |filename|
return set_package(pkgName, silent) if filename == CREW_LIB_PATH + 'packages/' + pkgName + '.rb'
end
abort "Package #{pkgName} not found. :(".lightred unless silent
end
def regexp_search(pkgName)
results = Dir["#{CREW_LIB_PATH}packages/*.rb"].sort \
.select { |f| File.basename(f, '.rb') =~ Regexp.new(pkgName, true) } \
.collect { |f| File.basename(f, '.rb') } \
.each { |f| print_package(f, @opt_verbose) }
if results.empty?
Find.find ("#{CREW_LIB_PATH}packages/") do |packageName|
if File.file? packageName
package = File.basename packageName, '.rb'
search package, true
if ( @pkg.description =~ /#{pkgName}/i )
print_package(package, @opt_verbose)
results.push(package)
end
end
end
end
abort "Package #{pkgName} not found. :(".lightred unless results.length > 0
end
def help (pkgName)
case pkgName
when "build"
puts "Build package(s)."
puts "Usage: crew build [-k|--keep] [-v|--verbose] <package1> [<package2> ...]"
puts "Build package(s) from source and place the archive and checksum in the current working directory."
puts "If `-k` or `--keep` is present, the `CREW_BREW_DIR` (#{CREW_BREW_DIR}) directory will remain."
puts "If `-v` or `--verbose` is present, extra information will be displayed."
when "const"
puts "Display constant(s)."
puts "Usage: crew const [<const1> <const2> ...]"
puts "If no constants are provided, all constants will be displayed."
when "download"
puts "Download package(s)."
puts "Usage: crew download [-v|--verbose] <package1> [<package2> ...]"
puts "Download package(s) to `CREW_BREW_DIR` (#{CREW_BREW_DIR}), but don't install."
puts "If `-v` or `--verbose` is present, extra information will be displayed."
when "files"
puts "Display installed files of package(s)."
puts "Usage: crew files <package1> [<package2> ...]"
puts "The package(s) must be currently installed."
when "install"
puts "Install package(s)."
puts "Usage: crew install [-k|--keep] [-s|--build-from-source] [-S|--recursive-build] [-v|--verbose] <package1> [<package2> ...]"
puts "The package(s) must have a valid name. Use `crew search <pattern>` to search for packages to install."
puts "If `-k` or `--keep` is present, the `CREW_BREW_DIR` (#{CREW_BREW_DIR}) directory will remain."
puts "If `-s` or `--build-from-source` is present, the package(s) will be compiled instead of installed via binary."
puts "If `-S` or `--recursive-build` is present, the package(s), including all dependencies, will be compiled instead of installed via binary."
puts "If `-v` or `--verbose` is present, extra information will be displayed."
when "list"
puts "List packages"
puts "Usage: crew list available|installed|compatible|incompatible"
when "postinstall"
puts "Display postinstall messages of package(s)."
puts "Usage: crew postinstall <package1> [<package2> ...]"
puts "The package(s) must be currently installed."
when "reinstall"
puts "Remove and install package(s)."
puts "Usage: crew reinstall [-k|--keep] [-s|--build-from-source] [-S|--recursive-build] [-v|--verbose] <package1> [<package2> ...]"
puts "If `-k` or `--keep` is present, the `CREW_BREW_DIR` (#{CREW_BREW_DIR}) directory will remain."
puts "If `-s` or `--build-from-source` is present, the package(s) will be compiled instead of installed via binary."
puts "If `-S` or `--recursive-build` is present, the package(s), including all dependencies, will be compiled instead of installed via binary."
puts "If `-v` or `--verbose` is present, extra information will be displayed."
when "remove"
puts "Remove package(s)."
puts "Usage: crew remove [-v|--verbose] <package1> [<package2> ...]"
puts "The package(s) must be currently installed."
puts "If `-v` or `--verbose` is present, extra information will be displayed."
when "search"
puts "Look for package(s)."
puts "Usage: crew search [-v|--verbose] [<pattern> ...]"
puts "If <pattern> is omitted, all packages will be returned."
puts "If the package color is " + "green".lightgreen + ", it means the package is installed."
puts "If the package color is " + "red".lightred + ", it means the architecture is not supported."
puts "The <pattern> string can also contain regular expressions."
puts "If `-v` or `--verbose` is present, homepage and version will be displayed."
puts "Examples:"
puts " crew search ^lib".lightblue + " will display all packages that start with `lib`."
puts " crew search audio".lightblue + " will display all packages with `audio` in the name."
puts " crew search | grep -i audio".lightblue + " will display all packages with `audio` in the name or description."
puts " crew search git -v".lightblue + " will display packages with `git` in the name along with homepage and version."
when "update"
puts "Update crew."
puts "Usage: crew update"
puts "This only updates crew itself. Use `crew upgrade` to update packages."
when "upgrade"
puts "Update package(s)."
puts "Usage: crew upgrade [-v|--verbose] [-s|--build-from-source] <package1> [<package2> ...]"
puts "If package(s) are omitted, all packages will be updated. Otherwise, specific package(s) will be updated."
puts "Use `crew update` to update crew itself."
puts "If `-s` or `--build-from-source` is present, the package(s) will be compiled instead of upgraded via binary."
puts "If `-v` or `--verbose` is present, extra information will be displayed."
when "whatprovides"
puts "Determine which package(s) contains file(s)."
puts "Usage: crew whatprovides <pattern> ..."
puts "The <pattern> is a search string which can contain regular expressions."
else
puts "Available commands: build, const, download, files, help, install, list, postinstall, reinstall ,remove, search, update, upgrade, whatprovides"
end
end
def const (var)
if var
value = eval(var)
puts "#{var}=#{value}"
else
vars = [
'ARCH',
'ARCH_LIB',
'CHROMEOS_RELEASE',
'CREW_BREW_DIR',
'CREW_BUILD',
'CREW_CMAKE_LIBSUFFIX_OPTIONS',
'CREW_CMAKE_OPTIONS',
'CREW_CONFIG_PATH',
'CREW_DEST_DIR',
'CREW_DEST_HOME',
'CREW_DEST_LIB_PREFIX',
'CREW_DEST_MAN_PREFIX',
'CREW_DEST_PREFIX',
'CREW_LIB_PATH',
'CREW_LIB_PREFIX',
'CREW_LIB_SUFFIX',
'CREW_MAN_PREFIX',
'CREW_MESON_OPTIONS',
'CREW_NOT_COMPRESS',
'CREW_NOT_STRIP',
'CREW_NPROC',
'CREW_OPTIONS',
'CREW_PREFIX',
'CREW_VERSION',
'HOME',
'LIBC_VERSION',
'USER'
]
vars.each { |var|
value = eval(var)
puts "#{var}=#{value}"
}
end
end
def files (pkgName)
filelist = "#{CREW_PREFIX}/etc/crew/meta/#{pkgName}.filelist"
if File.exists? "#{filelist}"
system "sort #{filelist}"
lines = `wc -l "#{filelist}"`.strip.split(' ')[0].to_i
size = `cat #{filelist} | xargs -I {} du -b '{}' | awk '{ print; total += $1 }; END { print total }' | numfmt --suffix B --to=si | tail -1`
puts "Total found: #{lines}".lightgreen
puts "Disk usage: #{size}".lightgreen
else
puts "Package #{pkgName} is not installed. :(".lightred
end
end
def whatprovides (pkgName)
fileArray = []
Find.find (CREW_CONFIG_PATH + 'meta/') do |packageList|
if File.file? packageList
if packageList[/\.filelist$/]
packageName = File.basename packageList, '.filelist'
File.readlines(packageList).each do |line|
found = line[/#{Regexp.new(pkgName)}/]
if found
fileLine = packageName + ': ' + line
if not fileArray.include? fileLine
fileArray.push(fileLine)
end
end
end
end
end
end
if not fileArray.empty?
fileArray.sort.each do |item|
puts item
end
puts "\nTotal found: #{fileArray.length}".lightgreen
end
end
def update
abort "`crew update` is used to update crew itself. Use `crew upgrade <package1> [<package2> ...]` to update specific packages.".orange if @pkgName
#update package lists
Dir.chdir CREW_LIB_PATH do
system "git fetch origin master"
system "git reset --hard origin/master"
end
puts "Package lists, crew, and library updated."
#update compatible packages
puts "Generating compatible packages..."
generate_compatible
#check for outdated installed packages
puts "Checking for package updates..."
canBeUpdated = 0
@device[:installed_packages].each do |package|
search package[:name], true
if package[:version] != @pkg.version
canBeUpdated += 1
puts @pkg.name + " could be updated from " + package[:version] + " to " + @pkg.version
end
end
if canBeUpdated > 0
puts ""
puts "Run `crew upgrade` to update all packages or `crew upgrade <package1> [<package2> ...]` to update specific packages."
else
puts "Your software is up to date.".lightgreen
end
end
def upgrade
if @pkgName
currentVersion = nil
@device[:installed_packages].each do |package|
if package[:name] == @pkg.name
currentVersion = package[:version]
end
end
if currentVersion != @pkg.version
puts "Updating #{@pkg.name}..."
@pkg.in_upgrade = true
resolve_dependencies_and_install
@pkg.in_upgrade = false
else
puts "#{@pkg.name} is already up to date.".lightgreen
end
else
# Make a installed packages list belong to the dependency order
dependencies = []
@device[:installed_packages].each do |package|
# skip package if it is dependent other packages previously checked
next if dependencies.include? package[:name]
# add package itself
dependencies = [ package[:name] ].concat(dependencies)
# expand depencencies and add it to the dependencies list
search package[:name], true
exp_dep = expand_dependencies
dependencies = exp_dep.concat(dependencies)
end
dependencies.uniq!
# Check version number of installed package and make a target list
toBeUpdated = []
dependencies.each do |dep|
package = @device[:installed_packages].find {|pkg| pkg[:name] == dep}
next unless package
search package[:name], true
if package[:version] != @pkg.version
toBeUpdated.push(package[:name])
end
end
if toBeUpdated.length > 0
puts "Updating packages..."
toBeUpdated.each do |package|
search package
puts "Updating " + @pkg.name + "..." if @opt_verbose
@pkg.in_upgrade = true
resolve_dependencies_and_install
@pkg.in_upgrade = false
end
puts "Packages have been updated.".lightgreen
else
puts "Your software is already up to date.".lightgreen
end
end
end
def download
url = @pkg.get_url(@device[:architecture])
source = @pkg.is_source?(@device[:architecture])
if !url
abort "No precompiled binary or source is available for #{@device[:architecture]}.".lightred
elsif !source
puts "Precompiled binary available, downloading..."
elsif @pkg.build_from_source
puts "Downloading source..."
else
puts "No precompiled binary available for your platform, downloading source..."
end
uri = URI.parse url
filename = File.basename(uri.path)
if source
sha256sum = @pkg.source_sha256
else
sha256sum = @pkg.binary_sha256[@device[:architecture]]
end
Dir.chdir CREW_BREW_DIR do
if @opt_verbose then
system('curl', '-v', '--retry', '3', '--progress-bar', '-C', '-', '--insecure', '-L', '-#', url, '-o', filename)
else
system('curl', '--retry', '3', '--progress-bar', '-C', '-', '--insecure', '-L', '-#', url, '-o', filename)
end
abort 'Checksum mismatch. :/ Try again.'.lightred unless
Digest::SHA256.hexdigest( File.read("./#{filename}") ) == sha256sum
end
puts "Archive downloaded".lightgreen
return {source: source, filename: filename}
end
def unpack (meta)
extract_dir = "#{meta[:filename]}.dir"
target_dir = nil
Dir.chdir CREW_BREW_DIR do
Dir.mkdir("#{extract_dir}") unless Dir.exist?("#{extract_dir}")
case File.basename meta[:filename]
when /\.zip$/i
puts "Unpacking archive using 'unzip', this may take a while..."
_verbopt = @opt_verbose ? '-v' : '-qq'
system "unzip", _verbopt, "-d", "#{extract_dir}", meta[:filename]
when /\.(tar(\.(gz|bz2|xz|lz))?|tgz|tbz|txz)$/i
puts "Unpacking archive using 'tar', this may take a while..."
_verbopt = @opt_verbose ? 'v' : ''
system "tar", "x#{_verbopt}f", meta[:filename], "-C", "#{extract_dir}"
end
if meta[:source] == true
# Check the number of directories in the archive
entries = Dir["#{extract_dir}/*"]
entries = Dir["#{extract_dir}/."] if entries.empty?
if entries.length == 0
abort "Empty archive: #{meta[:filename]}".lightred
elsif entries.length == 1 && File.directory?(entries.first)
# Use `extract_dir/dir_in_archive` if there is only one directory.
target_dir = entries.first
else
# Use `extract_dir` otherwise
target_dir = extract_dir
end
else
# Use `extract_dir` for binary distribution
target_dir = extract_dir
end
end
return CREW_BREW_DIR + target_dir
end
def build_and_preconfigure (target_dir)
Dir.chdir target_dir do
puts "Building from source, this may take a while..."
# Rename *.la files temporily to *.la_tmp to avoid
# libtool: link: '*.la' is not a valid libtool archive.
# See https://gnunet.org/faq-la-files and
# https://stackoverflow.com/questions/42963653/libquadmath-la-is-not-a-valid-libtool-archive-when-configuring-openmpi-with-g
puts "Rename all *.la files to *.la_tmp".lightblue
if @opt_verbose then
system "find #{CREW_LIB_PREFIX} -type f -name *.la -print0 | xargs --null -I{} mv -v {} {}_tmp"
else
system "find #{CREW_LIB_PREFIX} -type f -name *.la -print0 | xargs --null -I{} mv {} {}_tmp"
end
@pkg.in_build = true
@pkg.patch
@pkg.prebuild
@pkg.build
@pkg.in_build = false
# wipe crew destdir
if @opt_verbose then
system "rm -rvf #{CREW_DEST_DIR}/*"
else
system "rm -rf #{CREW_DEST_DIR}/*"
end
puts "Preconfiguring package..."
@pkg.install
# Rename all *.la_tmp back to *.la to avoid
# cannot access '*.la': No such file or directory
puts "Rename all *.la_tmp files back to *.la".lightblue
if @opt_verbose then
system "find #{CREW_LIB_PREFIX} -type f -name '*.la_tmp' -exec sh -c 'mv -v \"$1\" \"${1%.la_tmp}.la\"' _ {} \\;"
else
system "find #{CREW_LIB_PREFIX} -type f -name '*.la_tmp' -exec sh -c 'mv \"$1\" \"${1%.la_tmp}.la\"' _ {} \\;"
end
end
end
def pre_install (dest_dir)
Dir.chdir dest_dir do
puts "Performing pre-install..."
@pkg.preinstall
end
end
def post_install (dest_dir)
Dir.chdir dest_dir do
puts "Performing post-install..."
@pkg.postinstall
end
end
def compress_doc (dir)
# check whether crew should compress
return if CREW_NOT_COMPRESS || ENV['CREW_NOT_COMPRESS'] || !File.exist?("#{CREW_PREFIX}/bin/compressdoc")
if Dir.exist? dir
system "find #{dir} -type f ! -perm -200 | xargs -r chmod u+w"
if @opt_verbose then
system "compressdoc -v --gzip -9 #{dir}"
else
system "compressdoc --gzip -9 #{dir}"
end
end
end
def prepare_package (destdir)
Dir.chdir destdir do
# compress manual files
compress_doc "#{CREW_DEST_PREFIX}/man"
compress_doc "#{CREW_DEST_PREFIX}/info"
compress_doc "#{CREW_DEST_PREFIX}/share/man"
compress_doc "#{CREW_DEST_PREFIX}/share/info"
# create file list
system "find . -type f > ../filelist"
system "find . -type l >> ../filelist"
system "cut -c2- ../filelist > filelist"
# create directory list
system "find . -type d > ../dlist"
system "cut -c2- ../dlist > dlistcut"
system "tail -n +2 dlistcut > dlist"
# remove temporary files
if @opt_verbose then
system "rm -vf dlistcut ../dlist ../filelist"
else
system "rm -f dlistcut ../dlist ../filelist"
end
end
end
def strip_find_files (find_cmd, strip_option = "")
# check whether crew should strip
return if CREW_NOT_STRIP || ENV['CREW_NOT_STRIP'] || !File.exist?("#{CREW_PREFIX}/bin/strip")
# run find_cmd and strip only ar or ELF files
system "#{find_cmd} | xargs -r chmod u+w"
system "#{find_cmd} | xargs -r sh -c 'for i in \"$0\" \"$@\"; do case \"$(head -c 4 $i)\" in ?ELF|\!?ar) echo \"$i\";; esac ; done' | xargs -r strip #{strip_option}"
end
def install_package (pkgdir)
Dir.chdir pkgdir do
# install filelist, dlist and binary files
puts "Performing install..."
FileUtils.mv 'dlist', CREW_CONFIG_PATH + "meta/#{@pkg.name}.directorylist"
FileUtils.mv 'filelist', CREW_CONFIG_PATH + "meta/#{@pkg.name}.filelist"
# Strip libraries with -S
strip_find_files "find . -type f -name 'lib*.a' -print", "-S"
strip_find_files "find . -type f -name 'lib*.so*' -print", "-S"
# Strip binaries but not compressed archives
strip_find_files "find . -type f ! -iname '*\.bz2' ! -iname '*\.gz' ! -iname '*\.lha' ! -iname '*\.lz' ! -iname '*\.rar' ! -iname '*\.tar' ! -iname '*\.tbz' ! -iname '*\.tgz' ! -iname '\*.txz' ! -iname '*\.xz' ! -iname '*\.zip' -perm /111 -print | sed -e '/lib.*\.a$/d' -e '/lib.*\.so/d'"
if @opt_verbose then
if Dir.exists? "#{pkgdir}/home" then
system "tar cvf - ./usr/* ./home/* | (cd /; tar xp --keep-directory-symlink -f -)"
else
system "tar cvf - ./usr/* | (cd /; tar xp --keep-directory-symlink -f -)"
end
else
if Dir.exists? "#{pkgdir}/home" then
system "tar cf - ./usr/* ./home/* | (cd /; tar xp --keep-directory-symlink -f -)"
else
system "tar cf - ./usr/* | (cd /; tar xp --keep-directory-symlink -f -)"
end
end
end
end
def resolve_dependencies_and_install
begin
origin = @pkg.name
resolve_dependencies
search origin, true
install
rescue InstallError => e
abort "#{@pkg.name} failed to install: #{e.to_s}".lightred
ensure
# cleanup
unless @opt_keep
Dir.chdir CREW_BREW_DIR do
if @opt_verbose then
system "rm -rvf *"
else
system "rm -rf *"
end
system "mkdir dest" # this is a little ugly, feel free to find a better way
end
end
end
puts "#{@pkg.name.capitalize} installed!".lightgreen
end
def expand_dependencies
@dependencies = []
def push_dependencies
if @pkg.is_binary?(@device[:architecture]) ||
(!@pkg.in_upgrade && !@pkg.build_from_source && @device[:installed_packages].any? { |pkg| pkg[:name] == @pkg.name })
# retrieve name of dependencies that doesn't contain :build tag
check_deps = @pkg.dependencies.select {|k, v| !v.include?(:build)}.map {|k, v| k}
else
# retrieve name of all dependencies
check_deps = @pkg.dependencies.map {|k, v| k}
end
# check all dependencies recursively
check_deps.each do |dep|
# build unique dependencies list
unless @dependencies.include?(dep) || dep == @pkgName
@dependencies << dep
search dep, true
push_dependencies
end
end
end
push_dependencies
end
def resolve_dependencies
expand_dependencies
# leave only not installed packages in dependencies
@dependencies.select! {|name| @device[:installed_packages].none? {|pkg| pkg[:name] == name}}
return if @dependencies.empty?
puts "The following packages also need to be installed: "
i = 0
last_deps = []
last_packages = ["curl", "ghc", "mandb", "gtk3", "sommelier"]
@dependencies.each do |dep|
if last_packages.include?(dep)
@dependencies.delete_at(i)
last_deps.push(dep)
end
i += 1
end
@dependencies.concat last_deps.sort
@dependencies.each do |dep|
print dep + " "
end
puts ""
print "Do you agree? [Y/n] "
response = STDIN.getc
case response
when "n"
abort "No changes made."
when "\n", "y", "Y"
puts "Proceeding..."
proceed = true
else
puts "I don't understand `#{response}`. :(".lightred
abort "No changes made."
end
if proceed
@dependencies.each do |dep|
search dep
install
end
end
end
def install
if !@pkg.in_upgrade && @device[:installed_packages].any? { |pkg| pkg[:name] == @pkg.name }
puts "Package #{@pkg.name} already installed, skipping...".lightgreen
return
end
unless @pkg.is_fake?
meta = download
target_dir = unpack meta
if meta[:source] == true
abort "You don't have a working C compiler. Run `crew install buildessential` to get one and try again.".lightred unless system("gcc", "--version")
# build from source and place binaries at CREW_DEST_DIR
# CREW_DEST_DIR contains usr/local/... hierarchy
build_and_preconfigure target_dir
# prepare filelist and dlist at CREW_DEST_DIR
prepare_package CREW_DEST_DIR
# use CREW_DEST_DIR
dest_dir = CREW_DEST_DIR
else
# use extracted binary directory
dest_dir = target_dir
end
end
# remove it just before the file copy
if @pkg.in_upgrade
puts "Removing since upgrade or reinstall..."
remove @pkg.name
end
unless @pkg.is_fake?
# perform pre-install process
pre_install dest_dir
# perform install process
install_package dest_dir
# perform post-install process
post_install dest_dir
end
#add to installed packages
@device[:installed_packages].push(name: @pkg.name, version: @pkg.version)
File.open(CREW_CONFIG_PATH + 'device.json', 'w') do |file|
output = JSON.parse @device.to_json
file.write JSON.pretty_generate(output)
end
# Update shared library cache after install is complete.
system "#{CREW_PREFIX}/sbin/ldconfig || true"
end
def resolve_dependencies_and_build
begin
origin = @pkg.name
# mark current package as which is required to compile from source
@pkg.build_from_source = true
resolve_dependencies
search origin, true
build_package Dir.pwd
rescue InstallError => e
abort "#{@pkg.name} failed to build: #{e.to_s}".lightred
ensure
#cleanup
unless @opt_keep
Dir.chdir CREW_BREW_DIR do
if @opt_verbose then
system "rm -rvf *"
else
system "rm -rf *"
end
system "mkdir dest" #this is a little ugly, feel free to find a better way
end
end
end
puts "#{@pkg.name} is built!".lightgreen
end
def build_package (pwd)
abort "It is not possible to build a fake package".lightred if @pkg.is_fake?
abort "It is not possible to build without source".lightred if !@pkg.is_source?(@device[:architecture])
# download source codes and unpack it
meta = download
target_dir = unpack meta
# build from source and place binaries at CREW_DEST_DIR
build_and_preconfigure target_dir
# call check method here. this check method is called by this function only,
# therefore it is possible place time consuming tests in the check method.
if Dir.exist? target_dir
Dir.chdir target_dir do
puts "Checking..."
@pkg.check
end
end
# prepare filelist and dlist at CREW_DEST_DIR
prepare_package CREW_DEST_DIR
# build package from filelist, dlist and binary files in CREW_DEST_DIR
puts "Archiving..."
archive_package pwd
end
def archive_package (pwd)
pkg_name = "#{@pkg.name}-#{@pkg.version}-chromeos-#{@device[:architecture]}.tar.xz"
Dir.chdir CREW_DEST_DIR do
if @opt_verbose then
system "tar cJvf #{pwd}/#{pkg_name} *"
else
system "tar cJf #{pwd}/#{pkg_name} *"
end
end
Dir.chdir pwd do
system "sha256sum #{pkg_name} > #{pkg_name}.sha256"
end
end
def remove (pkgName)
#make sure the package is actually installed
unless @device[:installed_packages].any? { |pkg| pkg[:name] == pkgName }
puts "Package #{pkgName} isn't installed.".lightred
return
end
#if the filelist exists, remove the files and directories installed by the package
if File.file?("#{CREW_CONFIG_PATH}meta/#{pkgName}.filelist")
Dir.chdir CREW_CONFIG_PATH do
#remove all files installed by the package
File.open("meta/#{pkgName}.filelist").each_line do |line|
begin
puts "Removing file " + line.chomp + "".lightred if @opt_verbose
File.unlink line.chomp
rescue => exception #swallow exception
end
end
#remove all directories installed by the package
File.readlines("meta/#{pkgName}.directorylist").reverse.each do |line|
begin
puts "Removing directory " + line.chomp + "".lightred if @opt_verbose
Dir.rmdir line.chomp
rescue => exception #swallow exception
end
end
#remove the file and directory list
File.unlink "meta/#{pkgName}.filelist"
File.unlink "meta/#{pkgName}.directorylist"
end
end
#remove from installed packages
puts "Removing package " + pkgName + "".lightred if @opt_verbose
@device[:installed_packages].each do |elem|
@device[:installed_packages].delete elem if elem[:name] == pkgName
end
#update the device manifest
File.open(CREW_CONFIG_PATH + 'device.json', 'w') do |file|
out = JSON.parse @device.to_json
file.write JSON.pretty_generate(out)
end
puts "#{pkgName.capitalize} removed!".lightgreen
end
def build_command (args)
args["<name>"].each do |name|
@pkgName = name
search @pkgName
resolve_dependencies_and_build
end
end
def download_command (args)
args["<name>"].each do |name|
@pkgName = name
search @pkgName
download
end
end
def const_command (args)
unless args["<name>"].empty?
args["<name>"].each do |name|
const name
end
else
const nil
end
end
def files_command (args)
args["<name>"].each do |name|
@pkgName = name
search @pkgName
files name
end
end
def help_command (args)
if args["<command>"]
help args["<command>"]
else
puts "Usage: crew help <command>"
help nil
end
end
def install_command (args)
args["<name>"].each do |name|
@pkgName = name
search @pkgName
@pkg.build_from_source = true if @opt_src or @opt_recursive
resolve_dependencies_and_install
end
end
def list_command (args)
if args['available']
list_available
elsif args['installed']
puts list_installed
elsif args['compatible']
list_compatible true
elsif args['incompatible']
list_compatible false
end
end
def postinstall_command (args)
args["<name>"].each do |name|
set_package name, true
if @device[:installed_packages].any? do |elem| elem[:name] == name end
@pkg.postinstall
else
puts "Package #{name} is not installed. :(".lightred
end
end
end
def reinstall_command (args)
args["<name>"].each do |name|
@pkgName = name
search @pkgName
@pkg.build_from_source = true if @opt_src or @opt_recursive
if @pkgName
@pkg.in_upgrade = true
resolve_dependencies_and_install
@pkg.in_upgrade = false
end
end
end
def remove_command (args)
args["<name>"].each do |name|
remove name
end
end
def search_command (args)
args["<name>"].each do |name|
regexp_search name
end.empty? and begin
list_packages
end
end
def update_command (args)
update
end
def upgrade_command (args)
args["<name>"].each do |name|
@pkgName = name
search @pkgName
@pkg.build_from_source = true if @opt_src
upgrade
end.empty? and begin
upgrade
end
end
def whatprovides_command (args)
args["<name>"].each do |name|
whatprovides name
end
end
def is_command (name)
return false if name =~ /^[-<]/
return true
end
command_name = args.find { |k, v| v && is_command(k) } [0]
function = command_name + "_command"
send(function, args)