Files
chromebrew/tools/getrealdeps.rb
github-actions[bot] 2ef65cad20 Rebuild uutils_coreutils, add core binaries to armv7l systems (#11928)
* rebuild uutils_coreutils, add 32-bit core binaries for armv7l

Signed-off-by: Satadru Pramanik <satadru@gmail.com>

* More shebang adjustments.

Signed-off-by: Satadru Pramanik <satadru@gmail.com>

---------

Signed-off-by: Satadru Pramanik <satadru@gmail.com>
Co-authored-by: Satadru Pramanik <satadru@gmail.com>
2025-05-21 23:49:08 +00:00

210 lines
9.3 KiB
Ruby
Executable File

#!/usr/local/bin/ruby
# getrealdeps version 1.7 (for Chromebrew)
# Author: Satadru Pramanik (satmandu) satadru at gmail dot com
require 'fileutils'
crew_local_repo_root = `git rev-parse --show-toplevel 2> /dev/null`.chomp
# When invoked from crew, pwd is CREW_DEST_DIR, so crew_local_repo_root
# is empty.
if crew_local_repo_root.to_s.empty?
require_relative '../lib/const'
else
require File.join(crew_local_repo_root, 'lib/const')
end
if ARGV.include?('--use-crew-dest-dir')
ARGV.delete('--use-crew-dest-dir')
@opt_use_crew_dest_dir = true
end
# Exit quickly if an invalid package name is given.
if ARGV[0].nil? || ARGV[0].empty? || ARGV[0].include?('#')
puts 'Getrealdeps checks for the runtime dependencies of a package.'
puts 'The runtime dependencies are added if the package file is missing them.'
puts 'Usage: getrealdeps.rb [--use_crew_dest_dir] <packagename>'
exit 1
end
# Search for which packages have a needed library in CREW_LIB_PREFIX.
# This is a subset of what crew whatprovides gives.
def whatprovidesfxn(pkgdepslcl, pkg)
filelcl = if pkgdepslcl.include?(CREW_LIB_PREFIX)
`#{@grep} --exclude #{pkg}.filelist --exclude #{pkgfilelist} --exclude={"#{CREW_PREFIX}/etc/crew/meta/*_build.filelist"} "#{pkgdepslcl}$" "#{CREW_PREFIX}"/etc/crew/meta/*.filelist`
else
`#{@grep} --exclude #{pkg}.filelist --exclude #{pkgfilelist} --exclude={"#{CREW_PREFIX}/etc/crew/meta/*_build.filelist"} "^#{CREW_LIB_PREFIX}.*#{pkgdepslcl}$" "#{CREW_PREFIX}"/etc/crew/meta/*.filelist`
end
filelcl.gsub(/.filelist.*/, '').gsub(%r{.*/}, '').split("\n").uniq.join("\n").gsub(':', '')
end
def main(pkg)
puts "Checking for the runtime dependencies of #{pkg}..."
if @opt_use_crew_dest_dir
define_singleton_method('pkgfilelist') { File.join(CREW_DEST_DIR, 'filelist') }
abort('Pkg was not built.') unless File.exist?(pkgfilelist)
else
define_singleton_method('pkgfilelist') { "#{CREW_PREFIX}/etc/crew/meta/#{pkg}.filelist" }
# Package needs to be installed for package filelist to be populated.
unless File.exist?(pkgfilelist)
puts "Installing #{pkg} because it is not installed."
system("yes | crew install #{pkg}")
end
abort("Package #{pkg} either does not exist or does not contain any libraries.") unless File.exist?(pkgfilelist)
end
# Speed up grep.
ENV['LC_ALL'] = 'C'
# Install grep if a functional local copy does not exist.
if system('grep --version > /dev/null 2>&1')
@grep = 'grep'
else
system('crew install grep')
@grep = "#{CREW_PREFIX}/bin/grep"
end
# Gawk is needed for adding dependencies.
unless system('gawk -W version > /dev/null 2>&1')
puts "\nThe inplace replacement functionality of gawk is used to add missing dependencies to package files."
system('crew install gawk')
end
# upx is needed to expand compressed binaries to check for dependencies.
unless system('upx --version > /dev/null 2>&1')
puts "\nUpx is needed to expand compressed binaries."
system('crew install upx')
end
# What files does the package provide.
pkgfiles = File.read(pkgfilelist).split("\n").uniq
# Look at files in CREW_DEST_DIR instead of assuming the package is
# normally installed, which lets us avoid installing the package if it
# was just built.
pkgfiles.map! { |item| item.prepend(CREW_DEST_DIR) } if @opt_use_crew_dest_dir
FileUtils.rm_rf("/tmp/deps/#{pkg}")
# Remove files we don't care about, such as man files and non-binaries.
pkgfiles = pkgfiles.reject { |i| !File.file?(i.chomp) || File.read(i.chomp, 4) != "\x7FELF" || i.include?('.zst') }
# Use readelf to determine library dependencies, as
# this doesn't almost run a program like using ldd would.
pkgdepsfiles = pkgfiles.map do |i|
system("upx -d #{i} > /dev/null 2>&1")
FileUtils.mkdir_p("/tmp/deps/#{pkg}/")
`readelf -d "#{i}" 2>/dev/null | #{@grep} NEEDED | awk '{print $5}' | sed 's/\\[//g' | sed 's/\\]//g' | awk '!x[$0]++' | tee /tmp/deps/#{pkg}/#{File.basename(i)}`
end
pkgdepsfiles = pkgdepsfiles.map do |filedeps|
filedeps.split("\n")
end.flatten.compact.uniq
# Figure out which Chromebrew packages provide the relevant deps.
pkgdeps = pkgdepsfiles.map { |file| whatprovidesfxn(file, pkg) }.sort.reject { |i| i.include?(pkg) }.map { |i| i.split("\n") }.flatten.uniq
# Massage the glibc entries in the dependency list.
pkgdeps = pkgdeps.map { |i| i.gsub(/glibc_build.*/, 'glibc') }.uniq
pkgdeps = pkgdeps.map { |i| i.gsub(/glibc_lib.*/, 'glibc_lib') }.uniq.map(&:strip).reject(&:empty?)
# Massage the gcc entries in the dependency list.
pkgdeps = pkgdeps.map { |i| i.gsub('gcc_build', 'gcc_lib') }.uniq
# Massage the llvm entries in the dependency list.
pkgdeps = pkgdeps.map { |i| i.gsub('llvm_build', 'llvm_lib') }.uniq
# Leave early if we didn't find any dependencies.
return if pkgdeps.empty?
# Look for missing runtime dependencies.
missingpkgdeps = pkgdeps.reject { |i| File.read("#{CREW_PREFIX}/lib/crew/packages/#{pkg}.rb").include?("depends_on '#{i}'") unless File.read("#{CREW_PREFIX}/lib/crew/packages/#{pkg}.rb").include?("depends_on '#{i}' => :build") }
# Do not add llvm*_{dev,lib} runtime deps if this is a llvm*_build
# package as those packages are created from the llvm build package
# after it is built.
if /llvm.*_build/.match(pkg)
missingpkgdeps.delete_if { |d| /llvm.*_*/.match(d) }
pkgdeps.delete_if { |d| /llvm.*_*/.match(d) }
end
# Do not add llvm_build runtime deps if this is a llvm*_{dev,lib}
# package, as the llvm build package should only be a build dep for
# these packages.
if /llvm.*_lib/.match(pkg) || /llvm.*_dev/.match(pkg) || /libclc/.match(pkg) || /openmp/.match(pkg)
missingpkgdeps.delete_if { |d| /llvm.*_build/.match(d) }
pkgdeps.delete_if { |d| /llvm.*_build/.match(d) }
end
if /llvm.*_lib/.match(pkg)
missingpkgdeps.delete_if { |d| /llvm_lib/.match(d) }
pkgdeps.delete_if { |d| /llvm_lib/.match(d) }
end
# Do not add gcc_{dev,lib} runtime deps if this is a gcc_build
# package as those packages are created from the gcc_build package
# after it is built.
if /gcc_build/.match(pkg)
missingpkgdeps.delete_if { |d| /gcc.*_*/.match(d) }
pkgdeps.delete_if { |d| /gcc.*_*/.match(d) }
end
# Do not add gcc_build runtime deps if this is a gcc_{dev,lib}
# package, as the gcc_build package should only be a build dep for
# these packages.
if /gcc_lib/.match(pkg) || /gcc_dev/.match(pkg) || /libssp/.match(pkg)
missingpkgdeps.delete_if { |d| /gcc_build/.match(d) }
pkgdeps.delete_if { |d| /gcc_build/.match(d) }
end
if /gcc_lib/.match(pkg)
missingpkgdeps.delete_if { |d| /gcc_lib/.match(d) }
pkgdeps.delete_if { |d| /gcc_lib/.match(d) }
end
puts "\nPackage #{pkg} has runtime library dependencies on these packages:"
pkgdeps.each do |i|
puts " depends_on '#{i}' # R"
end
# Leave if we didn't find any missing dependencies.
return if missingpkgdeps.empty?
puts "\nPackage file #{pkg}.rb is missing these runtime library dependencies:"
puts " depends_on '#{missingpkgdeps.join("' # R\n depends_on '")}' # R"
missingpkgdeps.each do |adddep|
puts " depends_on '#{adddep}' # R"
# Add missing dependencies to the package.
puts "\n Adding deps: #{adddep}"
gawk_cmd = if File.foreach("#{CREW_PREFIX}/lib/crew/packages/#{pkg}.rb").grep(/depends_on/).any?
# This files contains dependencies already, so add new deps after existing dependencies.
"gawk -i inplace -v dep=\" depends_on '#{adddep}' # R\" 'FNR==NR{ if (/depends_on/) p=NR; next} 1; FNR==p{ print dep }' #{CREW_PREFIX}/lib/crew/packages/#{pkg}.rb #{CREW_PREFIX}/lib/crew/packages/#{pkg}.rb"
else
# This files doesn't contain deps, so just add new deps.
"gawk -i inplace -v dep=\" depends_on '#{adddep}' # R\" 'FNR==NR{ if (/})/) p=NR; next} 1; FNR==p{ print \"\\n\" dep }' #{CREW_PREFIX}/lib/crew/packages/#{pkg}.rb #{CREW_PREFIX}/lib/crew/packages/#{pkg}.rb"
end
system(gawk_cmd)
end
# Check for and delete old runtime dependencies.
# Its unsafe to do this with other dependencies, because the packager might know something we don't.
lines_to_delete = {}
File.readlines("#{CREW_PREFIX}/lib/crew/packages/#{pkg}.rb").each_with_index do |line, line_number|
# Find all the explicitly marked runtime dependencies.
dep = line.match(/ depends_on '(.*)' # R/)
# Basically just a nil check, but this way we avoid matching twice.
next unless dep
# Skip unless the runtime dependency in the package does not match the runtime dependencies we've found.
next unless pkgdeps.none?(dep[1])
# Skip if we're dealing with a glibc, glibc_lib or gcc_lib dependency-- these are architecture dependent sometimes?
next if %w[glibc glibc_lib gcc_lib].include?(dep[1])
# Record the line content as the key and the line number (incremented by one because the index starts at 0) as the value.
lines_to_delete[line] = line_number + 1
end
# Leave if there aren't any old runtime dependencies.
return if lines_to_delete.empty?
puts "\nPackage file #{pkg}.rb has these outdated runtime library dependencies:"
puts lines_to_delete.keys
system("gawk -i inplace 'NR != #{lines_to_delete.values.join(' && NR != ')}' #{CREW_PREFIX}/lib/crew/packages/#{pkg}.rb")
end
ARGV.each do |package|
main(package.chomp('rb'))
end