beeref/tools/find_linux_libs.py

175 lines
4.4 KiB
Python
Executable file

#!/usr/bin/env python3
# Create JSON with Linux libs needed for BeeRef appimage
import argparse
import json
import logging
import os
import pathlib
import re
import subprocess
import sys
from urllib import request
parser = argparse.ArgumentParser(
description=('Create JSON with Linux libs needed for BeeRef appimage'))
parser.add_argument(
'pid',
nargs=1,
default=None,
help='PID of running BeeRef process')
parser.add_argument(
'-l', '--loglevel',
default='INFO',
choices=list(logging._nameToLevel.keys()),
help='log level for console output')
parser.add_argument(
'--jsonfile',
default='linux_libs.json',
help='JSON input/output file')
parser.add_argument(
'--check-appimage',
default=False,
action='store_true',
help='Check a running appimage process for missing libraries')
args = parser.parse_args()
def strip_minor_versions(path):
# foo2.so.2.1.1 -> foo2.so.2
return re.sub('(.so.[0-9]*)[.0-9]*$', r'\1', path)
def what_links_to(path):
links = set()
dirname = os.path.dirname(path)
for filename in os.listdir(dirname):
filename = os.path.join(dirname, filename)
if (os.path.islink(filename)
and str(pathlib.Path(filename).resolve()) == path):
links.add(filename)
return sorted(links, key=len)
def is_lib(path):
return ('.so' in path
and os.path.expanduser('~') not in path
and 'python3' not in path
and 'mesa-diverted' not in path)
def iter_lsofoutput(output):
for line in output.splitlines():
line = line.split()
if line[3] == 'mem':
path = line[-1]
if is_lib(path):
yield path
PID = args.pid[0]
logger = logging.getLogger(__name__)
logging.basicConfig(level=getattr(logging, args.loglevel))
result = subprocess.run(('lsof', '-p', PID), capture_output=True)
assert result.returncode == 0, result.stderr
output = result.stdout.decode('utf-8')
if args.check_appimage:
logger.info('Checking appimage...')
errors = False
for lib in iter_lsofoutput(output):
if 'mount_BeeRef' not in lib:
print(f'Not in appimage: {lib}')
errors = True
if not errors:
print('No missing libs found.')
sys.exit()
libs = []
if os.path.exists(args.jsonfile):
logger.info(f'Reading from: {args.jsonfile}')
with open(args.jsonfile, 'r') as f:
data = json.loads(f.read())
known_libs = data['libs']
packages = set(data['packages'])
excludes = set(data['excludes'])
else:
logger.info(f'No file {args.jsonfile}; starting from scratch')
known_libs = []
packages = set()
excludes = set()
for lib in iter_lsofoutput(output):
links = what_links_to(lib)
if len(links) == 0:
pass
elif len(links) == 1:
lib = links[0]
else:
logger.warning(f'Double check: {lib} {links}')
lib = links[0]
if lib in known_libs:
logger.debug(f'Found known lib: {lib}')
else:
logger.debug(f'Found new lib: {lib}')
libs.append(lib)
for lib in libs:
result = subprocess.run(('apt-file', 'search', lib), capture_output=True)
if result.returncode != 0:
logger.warning(f'Fix manually: {lib}')
continue
output = result.stdout.decode('utf-8')
pkgs = set()
for line in output.splitlines():
pkg = line.split(': ')[0]
if not (pkg.endswith('-dev') or pkg.endswith('-dbg')):
pkgs.add(pkg)
if len(pkgs) == 1:
pkg = pkgs.pop()
logger.debug(f'Found package: {pkg}')
packages.add(pkg)
else:
logger.warning(f'Fix manually: {lib}')
# Find the libs we shouldn't include in the appimage
with request.urlopen(
'https://raw.githubusercontent.com/AppImageCommunity/pkg2appimage/'
'master/excludelist') as f:
response = f.read().decode()
exclude_masterlist = set()
for line in response.splitlines():
if not line or line.startswith('#'):
continue
line = line.split()[0]
line = strip_minor_versions(line)
exclude_masterlist.add(line)
for ex in exclude_masterlist:
for lib in (libs + known_libs):
if lib.endswith(ex):
excludes.add(ex)
continue
logger.info(f'Writing to: {args.jsonfile}')
with open(args.jsonfile, 'w') as f:
data = {'libs': sorted(libs + known_libs),
'packages': sorted(packages),
'excludes': sorted(excludes)}
f.write(json.dumps(data, indent=4))