Tentative fix to include package dependencies.
This commit is contained in:
parent
3428c9fb53
commit
50e019262a
|
@ -91,11 +91,22 @@ class Build():
|
||||||
"""Calculate exclusions and other variables."""
|
"""Calculate exclusions and other variables."""
|
||||||
|
|
||||||
if self.verbose:
|
if self.verbose:
|
||||||
print("--- Preliminary system checks ---")
|
print("--- Preliminary Phase ---")
|
||||||
|
|
||||||
if isinstance(shutil.which('apt'), str):
|
if isinstance(shutil.which('apt'), str):
|
||||||
# APT is found in path. We assume we can find dependencies.
|
# APT is found in path. We assume we can find dependencies.
|
||||||
self.check_dependencies = True
|
self.check_dependencies = True
|
||||||
|
|
||||||
|
|
||||||
|
if self.verbose:
|
||||||
|
print("Updating system packages cache.")
|
||||||
|
# Updating package cache
|
||||||
|
subprocess.run(['sudo', 'apt', 'update'], check=True, stdout=subprocess.DEVNULL)
|
||||||
|
if self.verbose:
|
||||||
|
print("Ensuring apt-file is installed and updated.")
|
||||||
|
# Updating apt-file cache
|
||||||
|
subprocess.run(['sudo', 'apt', 'install', 'apt-file', '-y'], check=True, stdout=subprocess.DEVNULL)
|
||||||
|
subprocess.run(['sudo', 'apt-file', 'update'], check=True, stdout=subprocess.DEVNULL)
|
||||||
else:
|
else:
|
||||||
print("CAUTION: your system seems not to include a working version of apt.\nThis will cause the AppImage to leverage system libraries when run.")
|
print("CAUTION: your system seems not to include a working version of apt.\nThis will cause the AppImage to leverage system libraries when run.")
|
||||||
self.check_dependencies = False
|
self.check_dependencies = False
|
||||||
|
@ -237,12 +248,12 @@ class Build():
|
||||||
def build(self):
|
def build(self):
|
||||||
"""Building all the versions."""
|
"""Building all the versions."""
|
||||||
|
|
||||||
if self.verbose:
|
|
||||||
print("--- Building Phase ---")
|
|
||||||
|
|
||||||
if self.found:
|
if self.found:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if self.verbose:
|
||||||
|
print("--- Building Phase ---")
|
||||||
|
|
||||||
# Preparation tasks
|
# Preparation tasks
|
||||||
self.appnamedir = os.path.join(self.builddir, self.appname)
|
self.appnamedir = os.path.join(self.builddir, self.appname)
|
||||||
os.makedirs(self.appnamedir, exist_ok=True)
|
os.makedirs(self.appnamedir, exist_ok=True)
|
||||||
|
@ -256,6 +267,13 @@ class Build():
|
||||||
|
|
||||||
# Build the requested version.
|
# Build the requested version.
|
||||||
self.__unpackbuild__()
|
self.__unpackbuild__()
|
||||||
|
self.__prepare_contents__()
|
||||||
|
if self.check_dependencies:
|
||||||
|
if self.verbose:
|
||||||
|
print("Searching for dependent libraries, it might take a while.")
|
||||||
|
|
||||||
|
self.__missing_dependencies__()
|
||||||
|
self.__finalize_build__()
|
||||||
|
|
||||||
|
|
||||||
def checksums(self):
|
def checksums(self):
|
||||||
|
@ -505,6 +523,7 @@ class Build():
|
||||||
subprocess.run(shlex.split(
|
subprocess.run(shlex.split(
|
||||||
f"tar xzf {self.download_path}/{archive}"), check=True)
|
f"tar xzf {self.download_path}/{archive}"), check=True)
|
||||||
|
|
||||||
|
def __prepare_contents__(self):
|
||||||
# create appimagedir
|
# create appimagedir
|
||||||
if self.verbose:
|
if self.verbose:
|
||||||
print("---- Preparing the build ----")
|
print("---- Preparing the build ----")
|
||||||
|
@ -512,6 +531,9 @@ class Build():
|
||||||
os.makedirs(self.appimagedir, exist_ok = True)
|
os.makedirs(self.appimagedir, exist_ok = True)
|
||||||
|
|
||||||
# At this point, let's decompress the deb packages
|
# At this point, let's decompress the deb packages
|
||||||
|
if self.verbose:
|
||||||
|
print("Unpacking main archives")
|
||||||
|
|
||||||
subprocess.run(shlex.split(
|
subprocess.run(shlex.split(
|
||||||
r"find .. -iname '*.deb' -exec dpkg -x {} . \;"
|
r"find .. -iname '*.deb' -exec dpkg -x {} . \;"
|
||||||
), cwd=self.appimagedir, check=True)
|
), cwd=self.appimagedir, check=True)
|
||||||
|
@ -524,6 +546,9 @@ class Build():
|
||||||
), cwd=self.appimagedir, check=True)
|
), cwd=self.appimagedir, check=True)
|
||||||
|
|
||||||
# Changing desktop file
|
# Changing desktop file
|
||||||
|
if self.verbose:
|
||||||
|
print("Preparing .desktop file.")
|
||||||
|
|
||||||
subprocess.run(shlex.split(
|
subprocess.run(shlex.split(
|
||||||
r"find . -iname startcenter.desktop -exec cp {} . \;"
|
r"find . -iname startcenter.desktop -exec cp {} . \;"
|
||||||
), cwd=self.appimagedir, check=True)
|
), cwd=self.appimagedir, check=True)
|
||||||
|
@ -533,6 +558,8 @@ class Build():
|
||||||
r"startcenter.desktop"
|
r"startcenter.desktop"
|
||||||
), cwd=self.appimagedir, check=False)
|
), cwd=self.appimagedir, check=False)
|
||||||
|
|
||||||
|
if self.verbose:
|
||||||
|
print("Preparing icon file.")
|
||||||
subprocess.run(shlex.split(
|
subprocess.run(shlex.split(
|
||||||
r"find . -name '*startcenter.png' -path '*hicolor*48x48*' " +
|
r"find . -name '*startcenter.png' -path '*hicolor*48x48*' " +
|
||||||
r"-exec cp {} . \;"
|
r"-exec cp {} . \;"
|
||||||
|
@ -542,62 +569,10 @@ class Build():
|
||||||
cmd = subprocess.run(shlex.split(
|
cmd = subprocess.run(shlex.split(
|
||||||
r"find -iname soffice.bin -print"
|
r"find -iname soffice.bin -print"
|
||||||
), cwd=self.appimagedir, check = True, capture_output=True)
|
), cwd=self.appimagedir, check = True, capture_output=True)
|
||||||
main_executable = os.path.abspath(os.path.join(
|
self.main_executable = os.path.abspath(os.path.join(
|
||||||
self.appimagedir,
|
self.appimagedir,
|
||||||
cmd.stdout.strip().decode('utf-8')))
|
cmd.stdout.strip().decode('utf-8')))
|
||||||
|
|
||||||
# If the system permits it, we leverage lddcollect
|
|
||||||
# to find the packages that contain .so dependencies in the main build.
|
|
||||||
if self.check_dependencies:
|
|
||||||
if self.verbose:
|
|
||||||
print("Checking for dependent libraries")
|
|
||||||
|
|
||||||
import lddcollect
|
|
||||||
# We first process the ELF
|
|
||||||
raw = lddcollect.process_elf(main_executable, verbose = False, dpkg = True)
|
|
||||||
|
|
||||||
# If all works as expected, we obtain a tuple of:
|
|
||||||
# (debian_packages, all_libraries, files_not_found)
|
|
||||||
(debian_packages, all_libraries, not_found) = raw
|
|
||||||
|
|
||||||
if len(debian_packages) != 0:
|
|
||||||
# We need, first, to download those packages.
|
|
||||||
debs = [ x.split(':')[0] for x in debian_packages ]
|
|
||||||
downloadpath = os.path.abspath(os.path.join(self.builddir, 'dependencies'))
|
|
||||||
os.makedirs(downloadpath)
|
|
||||||
|
|
||||||
if self.verbose:
|
|
||||||
print("Downloading missing dependencies, please wait.")
|
|
||||||
|
|
||||||
# Updating package cache
|
|
||||||
subprocess.run(['sudo', 'apt', 'update'], check=True)
|
|
||||||
# Updating apt-file cache
|
|
||||||
subprocess.run(['sudo', 'apt-file', 'update'], check=True)
|
|
||||||
|
|
||||||
# Let's try to find and install also other libraries
|
|
||||||
additional = list(dict.fromkeys([ libfinderhelper(x) for x in not_found ]))
|
|
||||||
debs.extend(additional)
|
|
||||||
|
|
||||||
# We download the missing dependencies leveraging apt
|
|
||||||
subprocess.run(shlex.split(
|
|
||||||
r"apt download " + " ".join(debs)
|
|
||||||
), cwd=downloadpath, check=True)
|
|
||||||
|
|
||||||
# then we install them inside a temporary path
|
|
||||||
temporary = os.path.abspath(os.path.join(downloadpath, 'temp'))
|
|
||||||
os.makedirs(temporary)
|
|
||||||
subprocess.run(shlex.split(
|
|
||||||
r"find " + downloadpath + r" -iname \*.deb -exec dpkg -x {} " + temporary + r" \;"
|
|
||||||
), cwd=self.builddir, check=True)
|
|
||||||
|
|
||||||
# We are finally copying the .so files in the same path as main_executable
|
|
||||||
libdirs = [ 'lib/x86_64-linux-gnu', 'usr/lib/x86_64-linux-gnu' ]
|
|
||||||
for libdir in libdirs:
|
|
||||||
fulllibdir = os.path.abspath(os.path.join(temporary, libdir))
|
|
||||||
subprocess.run(shlex.split(
|
|
||||||
f"cp -Ra {fulllibdir}/. {os.path.dirname(main_executable)}/"
|
|
||||||
), cwd=temporary, check=True)
|
|
||||||
|
|
||||||
# Find the name of the binary called in the desktop file.
|
# Find the name of the binary called in the desktop file.
|
||||||
binaryname = ''
|
binaryname = ''
|
||||||
with open(
|
with open(
|
||||||
|
@ -621,6 +596,70 @@ class Build():
|
||||||
r"-exec ln -sf {} ./%s \;" % binaryname
|
r"-exec ln -sf {} ./%s \;" % binaryname
|
||||||
), cwd=bindir, check=True)
|
), cwd=bindir, check=True)
|
||||||
|
|
||||||
|
def __missing_dependencies__(self):
|
||||||
|
"""Finds and copy in the appimagedir any missing libraries."""
|
||||||
|
# If the system permits it, we leverage lddcollect
|
||||||
|
# to find the packages that contain .so dependencies in the main build.
|
||||||
|
import lddcollect
|
||||||
|
# We first process the ELF
|
||||||
|
raw = lddcollect.process_elf(self.main_executable, verbose = False, dpkg = True)
|
||||||
|
|
||||||
|
# If all works as expected, we obtain a tuple of:
|
||||||
|
# (debian_packages, all_libraries, files_not_found)
|
||||||
|
(debian_packages, all_libraries, not_found) = raw
|
||||||
|
|
||||||
|
if len(debian_packages) != 0:
|
||||||
|
# Creating temporary folders
|
||||||
|
debs = [ x.split(':')[0] for x in debian_packages ]
|
||||||
|
downloadpath = os.path.abspath(os.path.join(self.builddir, 'dependencies'))
|
||||||
|
os.makedirs(downloadpath)
|
||||||
|
|
||||||
|
|
||||||
|
if self.verbose:
|
||||||
|
print("Downloading missing dependencies, please wait.")
|
||||||
|
|
||||||
|
# Let's try to find and install also other libraries
|
||||||
|
additional = list(dict.fromkeys([ Helpers.lib_to_deb(x) for x in not_found ]))
|
||||||
|
debs.extend(additional)
|
||||||
|
|
||||||
|
# It seems the download command does not download dependencies of
|
||||||
|
# the packages.
|
||||||
|
if self.verbose:
|
||||||
|
print("Constructing the dependency tree.")
|
||||||
|
|
||||||
|
for deb in debian_packages:
|
||||||
|
debs.extend(Helpers.deb_dependencies(deb))
|
||||||
|
|
||||||
|
# Re-cleaning up the dependency tree
|
||||||
|
debs = list(dict.fromkeys(debs))
|
||||||
|
|
||||||
|
# We download the missing dependencies leveraging apt
|
||||||
|
subprocess.run(shlex.split(
|
||||||
|
r"apt download " + " ".join(debs)
|
||||||
|
), cwd=downloadpath, check=True)
|
||||||
|
|
||||||
|
# then we install them inside a temporary path
|
||||||
|
temporary = os.path.abspath(os.path.join(downloadpath, 'temp'))
|
||||||
|
os.makedirs(temporary)
|
||||||
|
subprocess.run(shlex.split(
|
||||||
|
r"find " + downloadpath + r" -iname \*.deb -exec dpkg -x {} " + temporary + r" \;"
|
||||||
|
), cwd=self.builddir, check=True)
|
||||||
|
|
||||||
|
# We are finally copying the .so files in the same path as main_executable
|
||||||
|
libdirs = [ 'lib/x86_64-linux-gnu', 'usr/lib/x86_64-linux-gnu' ]
|
||||||
|
for libdir in libdirs:
|
||||||
|
fulllibdir = os.path.abspath(os.path.join(temporary, libdir))
|
||||||
|
subprocess.run(shlex.split(
|
||||||
|
f"cp -Ra {fulllibdir}/. {os.path.dirname(self.main_executable)}/"
|
||||||
|
), cwd=temporary, check=True)
|
||||||
|
|
||||||
|
if self.debug:
|
||||||
|
with open(os.path.abspath(os.builddir, 'dependencies.lst'), 'w', encoding="utf-8") as deplist:
|
||||||
|
deplist.write("\n".join(debs))
|
||||||
|
|
||||||
|
def __finalize_build__(self):
|
||||||
|
if self.verbose:
|
||||||
|
print("Finalizing build...")
|
||||||
# Cleaning up AppDir
|
# Cleaning up AppDir
|
||||||
cleanup_dirs = [ 'etc', 'lib', 'lib64', 'usr/lib', 'usr/local' ]
|
cleanup_dirs = [ 'etc', 'lib', 'lib64', 'usr/lib', 'usr/local' ]
|
||||||
for local in cleanup_dirs:
|
for local in cleanup_dirs:
|
||||||
|
@ -678,7 +717,27 @@ class Build():
|
||||||
shutil.rmtree(self.builddir)
|
shutil.rmtree(self.builddir)
|
||||||
|
|
||||||
|
|
||||||
def libfinderhelper(libraryname):
|
class Helpers:
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def deb_dependencies(package_name):
|
||||||
|
"""Returns the array of the dependencies of that package."""
|
||||||
|
|
||||||
|
# First pass: find dependency of that package in raw output
|
||||||
|
pass1 = subprocess.Popen(shlex.split(
|
||||||
|
f"apt-cache depends --recurse --no-recommends --no-suggests --no-conflicts --no-breaks --no-replaces --no-enhances --no-pre-depends {package_name}"
|
||||||
|
), stdout=subprocess.PIPE)
|
||||||
|
|
||||||
|
# Second pass: only grep interesting lines.
|
||||||
|
pass2 = subprocess.Popen(shlex.split(
|
||||||
|
r"grep '^\w'"
|
||||||
|
), stdin=pass1.stdout, stdout=subprocess.PIPE, encoding='utf-8')
|
||||||
|
stdout, stderr = pass2.communicate()
|
||||||
|
|
||||||
|
return stdout.strip().split("\n")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def lib_to_deb(libraryname):
|
||||||
"""Uses system tools to identify the missing package."""
|
"""Uses system tools to identify the missing package."""
|
||||||
|
|
||||||
libsearch = subprocess.run(shlex.split(
|
libsearch = subprocess.run(shlex.split(
|
||||||
|
|
Loading…
Reference in New Issue