From 211ab290d098b0b1feeba0c917ea7799e89a6fdf Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 8 Jul 2022 14:06:56 -0700 Subject: [PATCH 1/7] bring .desktop into compliance with Desktop Entry specification 1.1 validated with `desktop-file-validate` --- setup/data/novelwriter.desktop | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/setup/data/novelwriter.desktop b/setup/data/novelwriter.desktop index 2666fa13..8b140ffc 100644 --- a/setup/data/novelwriter.desktop +++ b/setup/data/novelwriter.desktop @@ -1,10 +1,9 @@ [Desktop Entry] Type=Application -Encoding=UTF-8 Name=novelWriter Comment=A markdown-like text editor for planning and writing novels Exec=novelwriter %f Icon=novelwriter Categories=Qt;Office;WordProcessor; Terminal=false -MimeType=application/x-novelwriter-project +MimeType=application/x-novelwriter-project; From b363259a723a24377b5a35b8d2d22825ebe5737a Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 8 Jul 2022 14:08:13 -0700 Subject: [PATCH 2/7] fix spelling --- setup/description_short.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup/description_short.txt b/setup/description_short.txt index ac106560..d3d30690 100644 --- a/setup/description_short.txt +++ b/setup/description_short.txt @@ -2,5 +2,5 @@ novelWriter is a plain text editor designed for writing novels assembled from many smaller text documents. It uses a minimal formatting syntax inspired by Markdown, and adds a meta data syntax for comments, synopsis, and cross-referencing. It's designed to be a simple text editor that allows for -easy organisation of text and notes, using human readable text files as +easy organization of text and notes, using human readable text files as storage for robustness. From 073a9acfb3b7e3f4c9378cc39570b347e4103e47 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 8 Jul 2022 15:26:03 -0700 Subject: [PATCH 3/7] add a method to automate the building of an Appimage package --- setup.py | 223 ++++++++++++++++++++++++++++++++++ setup/novelwriter.appdata.xml | 21 ++++ 2 files changed, 244 insertions(+) create mode 100644 setup/novelwriter.appdata.xml diff --git a/setup.py b/setup.py index f72ce936..b7ce8318 100755 --- a/setup.py +++ b/setup.py @@ -835,10 +835,225 @@ def makeForLaunchpad(doSign=False, isFirst=False, isSnapshot=False): return +## +# Make Appimage (build-appimage) +## + +def makeAppimage(sysArgs): + """Build an Appimage + """ + + import argparse + import platform + import glob + + try: + import python_appimage + except ImportError: + print( + "ERROR: Package 'python-appimage' is missing on this system.\n" + " Please run 'pip install --user python-appimage' to install it.\n" + ) + sys.exit(1) + + print("") + print("Build Appimage") + print("==============") + print("") + + plat = platform.machine() + + parser = argparse.ArgumentParser(prog='build_appimage', + description='Build an Appimage', + epilog='see https://appimage.org/ for more details') + parser.add_argument('-l', '--linux-tag', nargs='?', default=f"manylinux2014_{plat}", + help=( + 'linux compatibility tag (e.g. manylinux1_x86_64) \n' + 'see https://python-appimage.readthedocs.io/en/latest/#available-python-appimages \n' + 'and https://github.com/pypa/manylinux for a list of valid tags' + )) + parser.add_argument('-p', '--python-version', nargs='?', default='3.11', + help='python version (e.g. 3.11)') + + args, unknown = parser.parse_known_args(sysArgs) + + linuxTag = args.linux_tag + pythonVer = args.python_version + + # Version Info + # ============ + + numVers, hexVers, relDate = extractVersion() + pkgVers = compactVersion(numVers) + relDate = datetime.datetime.strptime(relDate, "%Y-%m-%d") + print("") + + # Set Up Folder + # ============= + + bldDir = "dist_appimage" + bldPkg = f"novelwriter_{pkgVers}" + outDir = f"{bldDir}/{bldPkg}" + imageDir = f"{bldDir}/appimage" + + # Set Up Folders + # ============== + + if not os.path.isdir(bldDir): + os.mkdir(bldDir) + + if os.path.isdir(outDir): + print("Removing old build files ...") + print("") + shutil.rmtree(outDir) + + os.mkdir(outDir) + + if os.path.isdir(imageDir): + print("Removing old build metadata files ...") + print("") + shutil.rmtree(imageDir) + + os.mkdir(imageDir) + + # Remove old Appimages + outFiles = glob.glob(f"{bldDir}/*.AppImage") + + if outFiles: + print("Removing old Appimages") + print("") + for image in outFiles: + try: + os.remove(image) + except OSError: + print("Error while deleting file : ", image) + + # Build Additional Assets + # ======================= + + buildQtI18n() + buildSampleZip() + buildPdfManual() + + # Copy novelWriter Source + # ======================= + + print("Copying novelWriter source ...") + print("") + + for nPath, _, nFiles in os.walk("novelwriter"): + if nPath.endswith("__pycache__"): + print("Skipped: %s" % nPath) + continue + + pPath = f"{outDir}/{nPath}" + if not os.path.isdir(pPath): + os.mkdir(pPath) + + fCount = 0 + for fFile in nFiles: + nFile = f"{nPath}/{fFile}" + pFile = f"{pPath}/{fFile}" + + if fFile.endswith(".pyc"): + print("Skipped: %s" % nFile) + continue + + shutil.copyfile(nFile, pFile) + fCount += 1 + + print("Copied: %s/* [Files: %d]" % (nPath, fCount)) + + print("") + print("Copying or generating additional files ...") + print("") + + # Copy/Write Root Files + # ===================== + + copyFiles = ["LICENSE.md", "CREDITS.md", "CHANGELOG.md", "pyproject.toml"] + for copyFile in copyFiles: + shutil.copyfile(copyFile, f"{outDir}/{copyFile}") + print("Copied: %s" % copyFile) + + writeFile(f"{outDir}/MANIFEST.in", ( + "include LICENSE.md\n" + "include CREDITS.md\n" + "include CHANGELOG.md\n" + "include data/*\n" + "recursive-include novelwriter/assets *\n" + )) + print("Wrote: MANIFEST.in") + + writeFile(f"{outDir}/setup.py", ( + "import setuptools\n" + "setuptools.setup()\n" + )) + print("Wrote: setup.py") + + setupCfg = readFile("setup.cfg").replace( + "file: setup/description_pypi.md", "file: data/description_short.txt" + ) + writeFile(f"{outDir}/setup.cfg", setupCfg) + print("Wrote: setup.cfg") + + # Write Metadata + # ============== + + appDescription = readFile("setup/description_short.txt") + appdataXML = readFile("setup/novelwriter.appdata.xml").format(description=appDescription) + writeFile(f"{imageDir}/novelwriter.appdata.xml", appdataXML) + print("Wrote: novelwriter.appdata.xml") + + writeFile(f"{imageDir}/entrypoint.sh", ( + '#! /bin/bash \n' + '{{ python-executable }} -sE ${APPDIR}/opt/python{{ python-version }}/bin/novelwriter "$@"' + )) + print("Wrote: entrypoint.sh") + + writeFile(f"{imageDir}/requirements.txt", os.path.abspath(outDir)) + print("Wrote: requirements.txt") + + shutil.copyfile("setup/data/novelwriter.desktop", f"{imageDir}/novelwriter.desktop") + print("Copied: setup/data/novelwriter.desktop") + + shutil.copyfile("setup/icons/novelwriter.svg", f"{imageDir}/novelwriter.svg") + print("Copied: setup/icons/novelwriter.svg") + + shutil.copyfile("setup/data/hicolor/256x256/apps/novelwriter.png", + f"{imageDir}/novelwriter.png") + print("Copied: setup/data/hicolor/256x256/apps/novelwriter.png") + + # Build Appimage + # ============== + + try: + subprocess.call( + ["python", "-m", "python_appimage", "build", "app", + "-l", linuxTag, "-p", pythonVer, "appimage"], cwd=bldDir) + except Exception as exc: + print("Appimage build: FAILED") + print("") + print(str(exc)) + print("") + print("Dependencies:") + print(" * pip install python-appimage") + print("") + sys.exit(1) + + outFile = glob.glob(f"{bldDir}/*.AppImage")[0] + shaFile = makeCheckSum(os.path.basename(outFile), cwd=bldDir) + + toUpload(outFile) + toUpload(shaFile) + + return + ## # Make Windows Setup EXE (build-win-exe) ## + def makeWindowsEmbedded(sysArgs): """Set up a package with embedded Python and dependencies for Windows installation. @@ -1679,6 +1894,14 @@ if __name__ == "__main__": makeWindowsEmbedded(sys.argv) sys.exit(0) # Don't continue execution + if "build-appimage" in sys.argv: + sys.argv.remove("build-appimage") + if hostOS == OS_LINUX: + makeAppimage(sys.argv) + else: + print("ERROR: Command 'build-ubuntu' can only be used on Linux") + sys.exit(1) + # General Installers # ================== diff --git a/setup/novelwriter.appdata.xml b/setup/novelwriter.appdata.xml new file mode 100644 index 00000000..98a6e33b --- /dev/null +++ b/setup/novelwriter.appdata.xml @@ -0,0 +1,21 @@ + + + novelwriter + GPL-3.0 + GPL-3.0 + novelWriter + A markdown-like text editor for planning and writing novels + +

{description}

+
+ novelwriter.desktop + https://novelwriter.io/ + + + https://novelwriter.io/images/screenshot-multi.png + + + + novelwriter.desktop + +
\ No newline at end of file From 3dc4d1443fc58ec902962873b333b00d4c2654a0 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 8 Jul 2022 18:40:54 -0700 Subject: [PATCH 4/7] ensure prefix matching of `build-appimage` args do not shadow normal setup move defaults to pyhton 3.10 and manylinux2010 for better compatability cleanup --- setup.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/setup.py b/setup.py index b7ce8318..15144a99 100755 --- a/setup.py +++ b/setup.py @@ -866,16 +866,16 @@ def makeAppimage(sysArgs): parser = argparse.ArgumentParser(prog='build_appimage', description='Build an Appimage', epilog='see https://appimage.org/ for more details') - parser.add_argument('-l', '--linux-tag', nargs='?', default=f"manylinux2014_{plat}", + parser.add_argument('--linux-tag', nargs='?', default=f"manylinux2010_{plat}", help=( 'linux compatibility tag (e.g. manylinux1_x86_64) \n' 'see https://python-appimage.readthedocs.io/en/latest/#available-python-appimages \n' 'and https://github.com/pypa/manylinux for a list of valid tags' )) - parser.add_argument('-p', '--python-version', nargs='?', default='3.11', - help='python version (e.g. 3.11)') + parser.add_argument('--python-version', nargs='?', default='3.10', + help='python version (e.g. 3.10)') - args, unknown = parser.parse_known_args(sysArgs) + args, unparsedArgs = parser.parse_known_args(sysArgs) linuxTag = args.linux_tag pythonVer = args.python_version @@ -1047,7 +1047,7 @@ def makeAppimage(sysArgs): toUpload(outFile) toUpload(shaFile) - return + return unparsedArgs ## # Make Windows Setup EXE (build-win-exe) @@ -1897,9 +1897,9 @@ if __name__ == "__main__": if "build-appimage" in sys.argv: sys.argv.remove("build-appimage") if hostOS == OS_LINUX: - makeAppimage(sys.argv) + sys.argv = makeAppimage(sys.argv) # Build appimage and prune it's args else: - print("ERROR: Command 'build-ubuntu' can only be used on Linux") + print("ERROR: Command 'build-appimage' can only be used on Linux") sys.exit(1) # General Installers From 82a5da391fa0392e6b9c594002638e267ee2c3ea Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 8 Jul 2022 18:53:01 -0700 Subject: [PATCH 5/7] - ensure `dist_appimage` removed during cleanup - cleanup --- setup.py | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/setup.py b/setup.py index 15144a99..fdf8e072 100755 --- a/setup.py +++ b/setup.py @@ -197,6 +197,7 @@ def cleanBuildDirs(): removeFolder("dist") removeFolder("dist_deb") removeFolder("dist_minimal") + removeFolder("dist_appimage") removeFolder("novelWriter.egg-info") print("") @@ -863,17 +864,24 @@ def makeAppimage(sysArgs): plat = platform.machine() - parser = argparse.ArgumentParser(prog='build_appimage', - description='Build an Appimage', - epilog='see https://appimage.org/ for more details') - parser.add_argument('--linux-tag', nargs='?', default=f"manylinux2010_{plat}", - help=( - 'linux compatibility tag (e.g. manylinux1_x86_64) \n' - 'see https://python-appimage.readthedocs.io/en/latest/#available-python-appimages \n' - 'and https://github.com/pypa/manylinux for a list of valid tags' - )) - parser.add_argument('--python-version', nargs='?', default='3.10', - help='python version (e.g. 3.10)') + parser = argparse.ArgumentParser( + prog="build_appimage", + description="Build an Appimage", + epilog="see https://appimage.org/ for more details", + ) + parser.add_argument( + "--linux-tag", + nargs="?", + default=f"manylinux2010_{plat}", + help=( + "linux compatibility tag (e.g. manylinux1_x86_64) \n" + "see https://python-appimage.readthedocs.io/en/latest/#available-python-appimages \n" + "and https://github.com/pypa/manylinux for a list of valid tags" + ), + ) + parser.add_argument( + "--python-version", nargs="?", default="3.10", help="python version (e.g. 3.10)" + ) args, unparsedArgs = parser.parse_known_args(sysArgs) From 895beb6aa10457c4f360755141c8b7544d173730 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Fri, 15 Jul 2022 16:36:45 +0200 Subject: [PATCH 6/7] Revert change of spelling from UK to US --- setup/description_short.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup/description_short.txt b/setup/description_short.txt index d3d30690..ac106560 100644 --- a/setup/description_short.txt +++ b/setup/description_short.txt @@ -2,5 +2,5 @@ novelWriter is a plain text editor designed for writing novels assembled from many smaller text documents. It uses a minimal formatting syntax inspired by Markdown, and adds a meta data syntax for comments, synopsis, and cross-referencing. It's designed to be a simple text editor that allows for -easy organization of text and notes, using human readable text files as +easy organisation of text and notes, using human readable text files as storage for robustness. From 338d158baedaedede1207c988442ba75d5dba401 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Fri, 15 Jul 2022 16:39:12 +0200 Subject: [PATCH 7/7] Make some minor changes to the setup script --- setup.py | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/setup.py b/setup.py index fdf8e072..3bbefb9f 100755 --- a/setup.py +++ b/setup.py @@ -837,19 +837,19 @@ def makeForLaunchpad(doSign=False, isFirst=False, isSnapshot=False): ## -# Make Appimage (build-appimage) +# Make AppImage (build-appimage) ## -def makeAppimage(sysArgs): +def makeAppImage(sysArgs): """Build an Appimage """ + import glob import argparse import platform - import glob try: - import python_appimage + import python_appimage # noqa F401 except ImportError: print( "ERROR: Package 'python-appimage' is missing on this system.\n" @@ -858,21 +858,19 @@ def makeAppimage(sysArgs): sys.exit(1) print("") - print("Build Appimage") + print("Build AppImage") print("==============") print("") - plat = platform.machine() - parser = argparse.ArgumentParser( prog="build_appimage", - description="Build an Appimage", + description="Build an AppImage", epilog="see https://appimage.org/ for more details", ) parser.add_argument( "--linux-tag", nargs="?", - default=f"manylinux2010_{plat}", + default=f"manylinux2010_{platform.machine()}", help=( "linux compatibility tag (e.g. manylinux1_x86_64) \n" "see https://python-appimage.readthedocs.io/en/latest/#available-python-appimages \n" @@ -891,7 +889,7 @@ def makeAppimage(sysArgs): # Version Info # ============ - numVers, hexVers, relDate = extractVersion() + numVers, _, relDate = extractVersion() pkgVers = compactVersion(numVers) relDate = datetime.datetime.strptime(relDate, "%Y-%m-%d") print("") @@ -928,7 +926,7 @@ def makeAppimage(sysArgs): outFiles = glob.glob(f"{bldDir}/*.AppImage") if outFiles: - print("Removing old Appimages") + print("Removing old AppImages") print("") for image in outFiles: try: @@ -1036,11 +1034,12 @@ def makeAppimage(sysArgs): # ============== try: - subprocess.call( - ["python", "-m", "python_appimage", "build", "app", - "-l", linuxTag, "-p", pythonVer, "appimage"], cwd=bldDir) + subprocess.call([ + sys.executable, "-m", "python_appimage", "build", "app", + "-l", linuxTag, "-p", pythonVer, "appimage" + ], cwd=bldDir) except Exception as exc: - print("Appimage build: FAILED") + print("AppImage build: FAILED") print("") print(str(exc)) print("") @@ -1804,6 +1803,8 @@ if __name__ == "__main__": " Add --snapshot to make a snapshot package.", " build-win-exe Build a setup.exe file with Python embedded for Windows.", " The package must be built from a minimal windows zip file.", + " build-appimage Build an AppImage. Argument --linux-tag defaults to", + " manylinux1_x86_64 / i386, and --python-version to 3.10.", "", "System Install:", "", @@ -1905,7 +1906,7 @@ if __name__ == "__main__": if "build-appimage" in sys.argv: sys.argv.remove("build-appimage") if hostOS == OS_LINUX: - sys.argv = makeAppimage(sys.argv) # Build appimage and prune it's args + sys.argv = makeAppImage(sys.argv) # Build appimage and prune its args else: print("ERROR: Command 'build-appimage' can only be used on Linux") sys.exit(1)