Compare commits

..

3 Commits

9 changed files with 84 additions and 327 deletions

5
.gitignore vendored
View File

@ -1,6 +1,3 @@
venv
test
build
dist dist
loaih.egg-info venv
**/__pycache__ **/__pycache__

View File

@ -1,8 +1,5 @@
# vim:sts=4:sw=4 FROM python:3.9-slim
FROM python:3.9-slim-bullseye
RUN mkdir /build && \ RUN pip install loaih
pip install loaih
WORKDIR /build
ENTRYPOINT [ "/usr/local/bin/loaih" ] ENTRYPOINT [ "/usr/local/bin/loaih" ]
CMD [ "--help" ] CMD [ "--help" ]

View File

@ -1,19 +0,0 @@
# vim:sts=4:sw=4
FROM python:3.9-slim-bullseye
RUN mkdir /build && \
apt update && apt install -y git && \
rm -rf /var/lib/apt/lists/* && \
cd /root && \
git clone https://git.libreitalia.org/libreitalia/loaih.git && \
cd loaih && git checkout dependencies && \
python3 -m venv venv && \
. venv/bin/activate && \
pip install build && \
python3 -m build && \
pip install dist/loaih*.whl && \
deactivate && \
ln -sf /root/loaih/venv/bin/loaih /usr/local/bin/loaih
WORKDIR /build
ENTRYPOINT [ "/usr/local/bin/loaih" ]
CMD [ "--help" ]

View File

@ -11,13 +11,15 @@ if [[ ${retval} -ne 0 ]]; then
# for the sake of consistency, let's make the check_updates.sh script # for the sake of consistency, let's make the check_updates.sh script
# executable # executable
chmod +x check_updates.sh chmod +x check_updates.sh
# Updating runtime
if [[ -d venv ]]; then if [[ -d venv ]]; then
source venv/bin/activate source venv/bin/activate
fi fi
pip3 uninstall -y loaih pip3 uninstall -y loaih
# build the actual toolkit # build the actual toolkit
python3 -m build -w python3 -m build --sdist
pip3 install dist/loaih*.whl; rv=$? pip3 install dist/*.tar.gz; rv=$?
if [[ -d venv ]]; then if [[ -d venv ]]; then
deactivate deactivate
fi fi

View File

@ -1,4 +1,3 @@
# vim:sts=4:sw=4
[build-system] [build-system]
requires = ["hatchling"] requires = ["hatchling"]
build-backend = "hatchling.build" build-backend = "hatchling.build"
@ -6,51 +5,46 @@ build-backend = "hatchling.build"
[project] [project]
name = "loaih" name = "loaih"
dynamic = ["version"] dynamic = ["version"]
authors = [
{ name = "Emiliano Vavassori", email = "syntaxerrormmm@gmail.com" },
]
description = "LOAIH - LibreOffice AppImage Helpers, help build a LibreOffice AppImage" description = "LOAIH - LibreOffice AppImage Helpers, help build a LibreOffice AppImage"
readme = "README.md" readme = "README.md"
license = "MIT" authors = [
requires-python = ">= 3.6" { name = "Emiliano Vavassori", email = "syntaxerrormmm@libreoffice.org" },
]
dependencies = [ dependencies = [
"click", "click",
"lddcollect",
"lxml", "lxml",
"python-magic", "packaging",
"pyyaml", "pyyaml",
"requests" "requests",
] ]
classifiers = [ classifiers = [
"Development Status :: 5 - Production/Stable", "Development Status :: 5 - Production/Stable",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.6",
"Environment :: Console", "Environment :: Console",
"Intended Audience :: Developers", "Intended Audience :: Information Technology",
"Intended Audience :: End Users/Desktop",
"Intended Audience :: System Administrators",
"License :: OSI Approved :: MIT License", "License :: OSI Approved :: MIT License",
"Natural Language :: English",
"Operating System :: POSIX :: Linux", "Operating System :: POSIX :: Linux",
"Topic :: Office/Business", "Programming Language :: Python :: 3",
"Topic :: Software Development :: Build Tools", "Topic :: Office/Business :: Office Suites",
"Topic :: Software Development :: Quality Assurance", "Topic :: Software Development :: Quality Assurance",
"Topic :: Software Development :: Testing", "Topic :: System :: Software Distribution",
"Topic :: Software Development :: User Interfaces"
] ]
[tool.hatch.version]
path = "src/loaih/version.py"
[project.scripts] [project.scripts]
loaih = "loaih.script:cli" loaih = "loaih.script:cli"
[project.urls] [project.urls]
Homepage = "https://git.libreitalia.org/LibreItalia/loaih/" Homepage = "https://pypi.org/project/loaih/"
Documentation = "https://git.libreitalia.org/libreitalia/loaih/wiki"
Repository = "https://git.libreitalia.org/libreitalia/loaih/"
Issues = "https://git.libreitalia.org/libreitalia/loaih/issues/"
[tool.hatch.version] [publish.index]
path = "src/loaih/version.py"
[tool.hatch.build.targets.sdist] [publish.index.repos.pypi]
include = [ url = "https://upload.pypi.org/legacy/"
"src/loaih",
] [publish.index.repos.testpypi]
url = "https://test.pypi.org/legacy/"

View File

@ -134,7 +134,7 @@ class Solver():
retval.query = self.text retval.query = self.text
retval.branch = self.branch retval.branch = self.branch
retval.version = self.version retval.version = self.version
if retval.branch != 'daily' and retval.branch != 'prerelease': if retval.branch != 'daily':
retval.urls['x86_64'] = self.baseurl + 'x86_64/' retval.urls['x86_64'] = self.baseurl + 'x86_64/'
try: try:
@ -234,8 +234,6 @@ class NamedSolver(Solver):
fullversion: str = str(archived_versions[-1]) fullversion: str = str(archived_versions[-1])
self.baseurl = ARCHIVE + fullversion + 'deb/' self.baseurl = ARCHIVE + fullversion + 'deb/'
self.version = fullversion.rstrip('/') self.version = fullversion.rstrip('/')
if self.branch == 'prerelease':
self.baseurl = PRERELEASE
return self.version return self.version

View File

@ -3,7 +3,6 @@
"""Classes and functions to build an AppImage.""" """Classes and functions to build an AppImage."""
import os import os
import datetime
import glob import glob
import subprocess import subprocess
import shutil import shutil
@ -12,7 +11,6 @@ import shlex
import tempfile import tempfile
import hashlib import hashlib
import requests import requests
import magic
import loaih import loaih
@ -29,7 +27,6 @@ class Collection(list):
arch = [ x for x in arch if version.urls[x] != '-' ] arch = [ x for x in arch if version.urls[x] != '-' ]
self.extend([ Build(version, ar) for ar in arch ]) self.extend([ Build(version, ar) for ar in arch ])
class BuildException(Exception): pass
class Build(): class Build():
"""Builds a single version.""" """Builds a single version."""
@ -39,12 +36,10 @@ class Build():
LANGBASIC = [ 'en-GB' ] LANGBASIC = [ 'en-GB' ]
ARCHSTD = [ 'x86', 'x86_64' ] ARCHSTD = [ 'x86', 'x86_64' ]
def __init__(self, version: loaih.Version, arch, debug=False): def __init__(self, version: loaih.Version, arch):
self.debug = debug
self.version = version self.version = version
self.tidy_folder = True self.tidy_folder = True
self.verbose = True self.verbose = True
self.check_dependencies = False
self.arch = arch self.arch = arch
self.short_version = str.join('.', self.version.version.split('.')[0:2]) self.short_version = str.join('.', self.version.version.split('.')[0:2])
self.branch_version = self.version.branch self.branch_version = self.version.branch
@ -93,27 +88,6 @@ class Build():
def calculate(self): def calculate(self):
"""Calculate exclusions and other variables.""" """Calculate exclusions and other variables."""
if self.verbose:
print("--- Preliminary Phase ---")
if self.debug and isinstance(shutil.which('apt'), str):
# APT is found in path. We assume we can find dependencies.
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:
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
if self.verbose: if self.verbose:
print("--- Calculate Phase ---") print("--- Calculate Phase ---")
@ -171,6 +145,7 @@ class Build():
# was already built. # was already built.
name = self.appimagefilename name = self.appimagefilename
url = self.storage_path.rstrip('/') + self.full_path + '/' url = self.storage_path.rstrip('/') + self.full_path + '/'
matching = []
try: try:
if len(loaih.match_xpath(url, f"//a[contains(@href,'{name}')]/@href")) > 0: if len(loaih.match_xpath(url, f"//a[contains(@href,'{name}')]/@href")) > 0:
# Already built. # Already built.
@ -182,6 +157,8 @@ class Build():
else: else:
# Repo is local # Repo is local
command = f"find {self.full_path} -name {self.appimagefilename}" command = f"find {self.full_path} -name {self.appimagefilename}"
res = subprocess.run(shlex.split(command), res = subprocess.run(shlex.split(command),
capture_output=True, capture_output=True,
@ -197,7 +174,7 @@ class Build():
print(f"Found requested AppImage: {self.appimagefilename}.") print(f"Found requested AppImage: {self.appimagefilename}.")
def download(self, compact=False): def download(self):
"""Downloads the contents of the URL as it was a folder.""" """Downloads the contents of the URL as it was a folder."""
if self.verbose: if self.verbose:
@ -217,27 +194,19 @@ class Build():
self.found = True self.found = True
# Identifying downloads # Identifying downloads
self.tarballs = [ x for x in loaih.match_xpath(self.url, "//td/a/text()") if x.endswith('tar.gz') and 'deb' in x and self.version.version in x ] self.tarballs = [ x for x in loaih.match_xpath(self.url, "//td/a/text()") if x.endswith('tar.gz') and 'deb' in x ]
self.download_tarballs = []
# Issue #5: manage a limited number of downloads and not the full set.
if compact:
self.download_tarballs = self.__select_tarballs__()
else:
self.download_tarballs = self.tarballs
# Create and change directory to the download location # Create and change directory to the download location
os.makedirs(self.download_path, exist_ok = True) os.makedirs(self.download_path, exist_ok = True)
os.chdir(self.download_path) os.chdir(self.download_path)
for archive in self.download_tarballs: for archive in self.tarballs:
# If the archive is already there, do not do anything. # If the archive is already there, do not do anything.
if os.path.exists(archive): if os.path.exists(archive):
continue continue
# Download the archive # Download the archive
try: try:
self.__download_archive_debug__(archive) self.__download_archive__(archive)
except Exception as error: except Exception as error:
print(f"Failed to download {archive}: {error}.") print(f"Failed to download {archive}: {error}.")
@ -248,32 +217,25 @@ class Build():
def build(self): def build(self):
"""Building all the versions.""" """Building all the versions."""
if self.found:
return
if self.verbose: if self.verbose:
print("--- Building Phase ---") print("--- Building Phase ---")
if self.found:
return
# 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)
# And then cd to the appname folder. # And then cd to the appname folder.
os.chdir(self.appnamedir) os.chdir(self.appnamedir)
# Download appimagetool from github # Download appimagetool from github
appimagetoolurl = r"https://github.com/AppImage/appimagetool/releases/" appimagetoolurl = r"https://github.com/AppImage/AppImageKit/releases/"
appimagetoolurl += f"download/continuous/appimagetool-{self.arch}.AppImage" appimagetoolurl += f"download/continuous/appimagetool-{self.arch}.AppImage"
self.__download__(appimagetoolurl, 'appimagetool') self.__download__(appimagetoolurl, 'appimagetool')
os.chmod('appimagetool', 0o755) os.chmod('appimagetool', 0o755)
# 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):
@ -453,33 +415,9 @@ class Build():
with open(f"{file}.md5", 'w', encoding='utf-8') as checkfile: with open(f"{file}.md5", 'w', encoding='utf-8') as checkfile:
checkfile.write(f"{retval.hexdigest()} {os.path.basename(file)}") checkfile.write(f"{retval.hexdigest()} {os.path.basename(file)}")
def __download_archive__(self, archive) -> str: def __download_archive__(self, archive):
return self.__download__(self.url, archive) return self.__download__(self.url, archive)
def __download_archive_debug__(self, archive) -> str:
"""Analyses the downloaded archive to prevent issues with unpacking."""
break_control = 0
testedfilename = ""
while break_control <= 5:
timenow = datetime.datetime.now()
testedfilename, resource = self.__download_debug__(self.url, archive)
mime = magic.Magic(mime=True)
mimetype = mime.from_file(testedfilename)
if mimetype == 'application/gzip':
return testedfilename
# On the contrary, we will dump a logfile, remove the download and
# redo the download.
with open(os.path.join(self.download_path, 'downloadfailure.log'), 'a') as logfile:
logfile.write(f"{timenow.isoformat()};{resource.url};{mimetype}\n")
os.unlink(testedfilename)
break_control += 1
# If it arrives here, 5 attempts to download the archive have failed.
raise BuildException(f"All downloads failed for {archive}. Exiting.")
def __download__(self, url: str, filename: str): def __download__(self, url: str, filename: str):
basename = filename basename = filename
if '/' in filename: if '/' in filename:
@ -497,63 +435,40 @@ class Build():
file.write(chunk) file.write(chunk)
return filename return filename
def __download_debug__(self, url: str, filename: str) -> tuple[str, requests.Response]: def __unpackbuild__(self):
basename = filename # We start by filtering out tarballs from the list
if '/' in filename: buildtarballs = [ self.tarballs[0] ]
basename = filename.split('/')[-1]
full_url = url
if url.endswith('/'):
# URL has to be completed with basename of filename
full_url = url + basename
with requests.get(full_url, stream=True, timeout=10) as resource:
resource.raise_for_status()
with open(filename, 'wb') as file:
for chunk in resource.iter_content(chunk_size=8192):
file.write(chunk)
return filename, resource
def __select_tarballs__(self):
retval = [ self.tarballs[0] ]
# Let's process standard languages and append results to the # Let's process standard languages and append results to the
# buildtarball # buildtarball
if self.language == 'basic': if self.language == 'basic':
if self.offline_help: if self.offline_help:
retval.extend([ x for x in self.tarballs if 'pack_en-GB' in x ]) buildtarballs.extend([ x for x in self.tarballs if 'pack_en-GB' in x ])
else: else:
retval.extend([ x for x in self.tarballs if 'langpack_en-GB' in x]) buildtarballs.extend([ x for x in self.tarballs if 'langpack_en-GB' in x])
elif self.language == 'standard': elif self.language == 'standard':
for lang in Build.LANGSTD: for lang in Build.LANGSTD:
if self.offline_help: if self.offline_help:
retval.extend([ x for x in self.tarballs if 'pack_' + lang in x ]) buildtarballs.extend([ x for x in self.tarballs if 'pack_' + lang in x ])
else: else:
retval.extend([ x for x in self.tarballs if 'langpack_' + lang in x ]) buildtarballs.extend([ x for x in self.tarballs if 'langpack_' + lang in x ])
elif self.language == 'full': elif self.language == 'full':
if self.offline_help: if self.offline_help:
# We need also all help. Let's replace buildtarball with the # We need also all help. Let's replace buildtarball with the
# whole bunch # whole bunch
retval = self.tarballs buildtarballs = self.tarballs
else: else:
retval.extend([ x for x in self.tarballs if 'langpack' in x ]) buildtarballs.extend([ x for x in self.tarballs if 'langpack' in x ])
else: else:
# Looping for each language in self.language # Looping for each language in self.language
for lang in self.language.split(","): for lang in self.language.split(","):
if self.offline_help: if self.offline_help:
retval.extend([ x for x in self.tarballs buildtarballs.extend([ x for x in self.tarballs
if 'pack' + lang in x ]) if 'pack' + lang in x ])
else: else:
retval.extend([ x for x in self.tarballs buildtarballs.extend([ x for x in self.tarballs
if 'langpack' + lang in x ]) if 'langpack' + lang in x ])
return retval
def __unpackbuild__(self):
# We start by filtering out tarballs from the list
buildtarballs = self.__select_tarballs__()
os.chdir(self.appnamedir) os.chdir(self.appnamedir)
# Unpacking the tarballs # Unpacking the tarballs
@ -564,17 +479,13 @@ 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 ----")
self.appimagedir = os.path.join(self.appnamedir, self.appname + '.AppDir') self.appimagedir = os.path.join(self.builddir, self.appname, self.appname + '.AppDir')
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)
@ -587,9 +498,6 @@ 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)
@ -599,21 +507,11 @@ 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 {} . \;"
), cwd=self.appimagedir, check=True) ), cwd=self.appimagedir, check=True)
# Finding path to main executable
cmd = subprocess.run(shlex.split(
r"find -iname soffice.bin -print"
), cwd=self.appimagedir, check = True, capture_output=True)
self.main_executable = os.path.abspath(os.path.join(
self.appimagedir,
cmd.stdout.strip().decode('utf-8')))
# 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(
@ -629,91 +527,19 @@ class Build():
#binary_exec = subprocess.run(shlex.split(r"awk 'BEGIN { FS = \"=\" } /^Exec/ { print $2; exit }' startcenter.desktop | awk '{ print $1 }'"), cwd=self.appimagedir, text=True, encoding='utf-8') #binary_exec = subprocess.run(shlex.split(r"awk 'BEGIN { FS = \"=\" } /^Exec/ { print $2; exit }' startcenter.desktop | awk '{ print $1 }'"), cwd=self.appimagedir, text=True, encoding='utf-8')
#binaryname = binary_exec.stdout.strip("\n") #binaryname = binary_exec.stdout.strip("\n")
# Creating a soft link so the executable in the desktop file is present
bindir=os.path.join(self.appimagedir, 'usr', 'bin') bindir=os.path.join(self.appimagedir, 'usr', 'bin')
os.makedirs(bindir, exist_ok = True) os.makedirs(bindir, exist_ok = True)
subprocess.run(shlex.split( subprocess.run(shlex.split(
r"find ../../opt -iname soffice.bin -path '*program*' " + r"find ../../opt -iname soffice -path '*program*' " +
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 = raw[0]
not_found = raw[2]
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.path.join(self.storage_path, '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
cleanup_dirs = [ 'etc', 'lib', 'lib64', 'usr/lib', 'usr/local' ]
for local in cleanup_dirs:
shutil.rmtree(os.path.abspath(os.path.join(self.appimagedir, local)), ignore_errors=True)
# Download AppRun from github # Download AppRun from github
# With the new appimagetool, this shouldn't be needed anymore. apprunurl = r"https://github.com/AppImage/AppImageKit/releases/"
#apprunurl = r"https://github.com/AppImage/AppImageKit/releases/" apprunurl += f"download/continuous/AppRun-{self.arch}"
#apprunurl += f"download/continuous/AppRun-{self.arch}" dest = os.path.join(self.appimagedir, 'AppRun')
#dest = os.path.join(self.appimagedir, 'AppRun') self.__download__(apprunurl, dest)
#self.__download__(apprunurl, dest) os.chmod(dest, 0o755)
#os.chmod(dest, 0o755)
# Dealing with extra options # Dealing with extra options
buildopts = [] buildopts = []
@ -727,19 +553,19 @@ class Build():
buildopts_str = str.join(' ', buildopts) buildopts_str = str.join(' ', buildopts)
# Build the number-specific build # Build the number-specific build
command = f"{self.appnamedir}/appimagetool {buildopts_str} -v {self.appimagedir}"
if self.verbose: if self.verbose:
print("---- Start building ----") print("---- Start building ----")
print(f"DEBUG: {command}") subprocess.run(shlex.split(
subprocess.run(shlex.split(command), f"{self.appnamedir}/appimagetool {buildopts_str} -v " +
env={ "VERSION": self.appversion }, f"./{self.appname}.AppDir/"
check=True) ), env={ "VERSION": self.appversion }, check=True)
print("---- End building ----") print("---- End building ----")
else: else:
subprocess.run(shlex.split(command), subprocess.run(shlex.split(
env={ "VERSION": self.appversion }, f"{self.appnamedir}/appimagetool {buildopts_str} -v " +
stdout=subprocess.DEVNULL, f"./{self.appname}.AppDir/"
stderr=subprocess.DEVNULL, check=True) ), env={ "VERSION": self.appversion }, stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL, check=True)
if self.verbose: if self.verbose:
print(f"Built AppImage version {self.appversion}") print(f"Built AppImage version {self.appversion}")
@ -755,36 +581,5 @@ class Build():
def __del__(self): def __del__(self):
"""Destructor""" """Destructor"""
if not self.debug: # Cleaning up build directory
# Cleaning up build directory shutil.rmtree(self.builddir)
shutil.rmtree(self.builddir)
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 = pass2.communicate()[0]
return stdout.strip().split("\n")
@staticmethod
def lib_to_deb(libraryname):
"""Uses system tools to identify the missing package."""
libsearch = subprocess.run(shlex.split(
f"sudo apt-file find -lx {libraryname}$"
), check=True, capture_output=True)
candidate = [ x for x in libsearch.stdout.decode('utf-8').split('\n') if 'lib' in x ][0]
return candidate

View File

@ -9,12 +9,10 @@ import json
import click import click
import yaml import yaml
import loaih import loaih
import loaih.version
import loaih.build import loaih.build
@click.group() @click.group()
@click.version_option(loaih.version.version)
def cli(): def cli():
"""Helps with command line commands.""" """Helps with command line commands."""
@ -59,9 +57,8 @@ def getversion(query, jsonout, default_to_current):
@click.option('--updatable', '-u', is_flag=True, default=False, help="Create an updatable AppImage (compatible with zsync2). Default: not updatable") @click.option('--updatable', '-u', is_flag=True, default=False, help="Create an updatable AppImage (compatible with zsync2). Default: not updatable")
@click.option('--download-path', '-d', default='./downloads', type=str, help="Path to the download folder. Default: ./downloads") @click.option('--download-path', '-d', default='./downloads', type=str, help="Path to the download folder. Default: ./downloads")
@click.option('--repo-path', '-r', default='.', type=str, help="Path to the final storage of the AppImage. Default: current directory") @click.option('--repo-path', '-r', default='.', type=str, help="Path to the final storage of the AppImage. Default: current directory")
@click.option('--debug', 'debug', is_flag=True, default=False, help="Activate debug options.")
@click.argument('query') @click.argument('query')
def build(arch, language, offline, portable, updatable, download_path, repo_path, check, checksums, sign, keep, query, debug): def build(arch, language, offline, portable, updatable, download_path, repo_path, check, checksums, sign, keep, query):
"""Builds an Appimage with the provided options.""" """Builds an Appimage with the provided options."""
# Multiple query support # Multiple query support
@ -90,7 +87,6 @@ def build(arch, language, offline, portable, updatable, download_path, repo_path
for myquery in queries: for myquery in queries:
for appbuild in loaih.build.Collection(myquery, arches): for appbuild in loaih.build.Collection(myquery, arches):
# Configuration phase # Configuration phase
appbuild.debug = debug
appbuild.tidy_folder = False appbuild.tidy_folder = False
appbuild.language = language appbuild.language = language
appbuild.offline_help = offline appbuild.offline_help = offline
@ -106,7 +102,7 @@ def build(arch, language, offline, portable, updatable, download_path, repo_path
if check: if check:
appbuild.check() appbuild.check()
appbuild.download(compact = True) appbuild.download()
appbuild.build() appbuild.build()
if checksums: if checksums:
appbuild.checksums() appbuild.checksums()

View File

@ -1,4 +1 @@
#!/usr/bin/env python3 __version__ = "1.3.3"
# encoding: utf-8
version = "1.4.0rc2"