diff --git a/.gitignore b/.gitignore index be078dd5..e7099146 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,7 @@ /build/ /deploy/ /dist/ -/dist_deb/ +/dist_*/ *.spec *.egg-info setup.iss diff --git a/novelwriter/assets/icons/novelwriter.ico b/novelwriter/assets/icons/novelwriter.ico index df883296..24de4e24 100644 Binary files a/novelwriter/assets/icons/novelwriter.ico and b/novelwriter/assets/icons/novelwriter.ico differ diff --git a/novelwriter/assets/icons/x-novelwriter-project.ico b/novelwriter/assets/icons/x-novelwriter-project.ico index b349aa30..c4937b3b 100644 Binary files a/novelwriter/assets/icons/x-novelwriter-project.ico and b/novelwriter/assets/icons/x-novelwriter-project.ico differ diff --git a/setup.cfg b/setup.cfg index 17dc8205..8f2dcd24 100644 --- a/setup.cfg +++ b/setup.cfg @@ -5,7 +5,7 @@ author = Veronica Berglyd Olsen author_email = code@vkbo.net description = A markdown-like text editor for planning and writing novels url = https://novelwriter.io -long_description = file: setup/long_description.md +long_description = file: setup/description_pypi.md long_description_content_type = text/markdown license_file = LICENSE.md license = GNU General Public License v3 diff --git a/setup.py b/setup.py index 1be0014f..8c5a63d8 100755 --- a/setup.py +++ b/setup.py @@ -88,18 +88,18 @@ def sysCall(callArgs): return stdOut.decode("utf-8"), stdErr.decode("utf-8"), sysP.returncode -def createLauncher(execName, writePath): - """Generate a novelwriter.desktop file with a given exec name. +def readFile(fileName): + """Read an entire file return as a string. """ - desktopData = "" - with open(os.path.join("setup", "data", "novelwriter.desktop"), mode="r") as inFile: - desktopData = inFile.read() + with open(fileName, mode="r") as inFile: + return inFile.read() - desktopData = desktopData.replace("Exec=novelwriter", f"Exec={execName}") - with open(os.path.join(writePath, "novelwriter.desktop"), mode="w+") as outFile: - outFile.write(desktopData) - return +def writeFile(fileName, writeText): + """Write string to file + """ + with open(fileName, mode="w+") as outFile: + outFile.write(writeText) # =============================================================================================== # @@ -148,30 +148,23 @@ def cleanInstall(): """ print("") print("Cleaning up build environment ...") + print("") - buildDir = os.path.join(os.getcwd(), "build") - if os.path.isdir(buildDir): - try: - shutil.rmtree(buildDir) - print("Deleted folder 'build'") - except Exception as e: - print("Error: Cannot delete 'build' folder.") - print(str(e)) - sys.exit(1) - else: - print("Folder 'build' not found") + def removeFolder(rmDir): + if os.path.isdir(rmDir): + try: + shutil.rmtree(rmDir) + print("Deleted: %s" % rmDir) + except OSError: + pass + else: + print("Missing: %s" % rmDir) - distDir = os.path.join(os.getcwd(), "dist") - if os.path.isdir(distDir): - try: - shutil.rmtree(distDir) - print("Deleted folder 'dist'") - except Exception as e: - print("Error: Cannot delete 'dist' folder.") - print(str(e)) - sys.exit(1) - else: - print("Folder 'dist' not found") + removeFolder("build") + removeFolder("dist") + removeFolder("dist_deb") + removeFolder("dist_minimal") + removeFolder("novelWriter.egg-info") print("") @@ -183,81 +176,7 @@ def cleanInstall(): # =============================================================================================== # ## -# Qt Assistant Documentation Builder (qthelp) -## - -def buildQtDocs(): - """This function will build the documentation as a Qt help file. The - file is then copied into the novelwriter/assets/help directory and - can be included in builds. - """ - buildDir = os.path.join("docs", "build", "qthelp") - helpDir = os.path.join("novelwriter", "assets", "help") - - inFile = "novelWriter.qhcp" - outFile = "novelWriter.qhc" - datFile = "novelWriter.qch" - - print("") - print("Building Documentation") - print("======================") - print("") - - buildFail = False - try: - subprocess.call(["make", "-C", "docs", "qthelp"]) - except Exception as e: - print("QtHelp Build Error:") - print(str(e)) - buildFail = True - - try: - subprocess.call(["qhelpgenerator", os.path.join(buildDir, inFile)]) - except Exception as e: - print("QtHelp Build Error:") - print(str(e)) - buildFail = True - - if not os.path.isdir(helpDir): - try: - os.mkdir(helpDir) - except Exception as e: - print("QtHelp Build Error:") - print(str(e)) - buildFail = True - - try: - if os.path.isfile(os.path.join(helpDir, outFile)): - os.unlink(os.path.join(helpDir, outFile)) - if os.path.isfile(os.path.join(helpDir, datFile)): - os.unlink(os.path.join(helpDir, datFile)) - os.rename(os.path.join(buildDir, outFile), os.path.join(helpDir, outFile)) - os.rename(os.path.join(buildDir, datFile), os.path.join(helpDir, datFile)) - except Exception as e: - print("QtHelp Build Error:") - print(str(e)) - buildFail = True - - print("") - if buildFail: - print("Documentation build: FAILED") - print("") - print("Dependencies:") - print(" * pip install sphinx") - print(" * pip install sphinx-rtd-theme") - print(" * pip install sphinxcontrib-qthelp") - print("") - print("It also requires the qhelpgenerator to be available on the system.") - sys.exit(1) - else: - print("Documentation build: OK") - print("") - - return - - -## -# Html or PDF Documentation Builder (docs, docs_pdf) +# Build PDF Manual (manual) ## def buildPdfManual(): @@ -416,39 +335,13 @@ def makeMinimalPackage(targetOS): """ from zipfile import ZipFile, ZIP_DEFLATED - # Get the version - numVers, _, _ = extractVersion() - - # Make sample.zip first - try: - buildSampleZip() - except Exception as e: - print("Failed with error:") - print(str(e)) - sys.exit(1) - - # Build docs - try: - buildPdfManual() - except Exception as e: - print("Failed with error:") - print(str(e)) - sys.exit(1) - - # Make translation files - try: - buildQtI18n() - except Exception as e: - print("Failed with error:") - print(str(e)) - sys.exit(1) - print("") print("Building Minimal ZIP File") print("=========================") - if not os.path.isdir("dist"): - os.mkdir("dist") + bldDir = "dist_minimal" + if not os.path.isdir(bldDir): + os.mkdir(bldDir) if targetOS == OS_LINUX: targName = "-linux" @@ -463,28 +356,35 @@ def makeMinimalPackage(targetOS): targName = "" print("") + # Build Additional Assets + # ======================= + + buildQtI18n() + buildSampleZip() + buildPdfManual() + + # Build Minimal Zip + # ================= + + numVers, _, _ = extractVersion() zipFile = f"novelWriter-{numVers}-minimal{targName}.zip" - outFile = os.path.join("dist", zipFile) + outFile = os.path.join(bldDir, zipFile) if os.path.isfile(outFile): os.unlink(outFile) rootFiles = [ - "CHANGELOG.md", - "LICENSE.md", "README.md", + "LICENSE.md", + "CREDITS.md", + "CHANGELOG.md", "requirements.txt", "setup.py", + "setup.cfg", + "pyproject.toml", ] with ZipFile(outFile, "w", compression=ZIP_DEFLATED, compresslevel=9) as zipObj: - if targetOS != OS_WIN: - # Not needed for Windows as the icons are in novelwriter/assets/icons - for nRoot, _, nFiles in os.walk("setup"): - print("Adding Folder: %s [%d files]" % (nRoot, len(nFiles))) - for aFile in nFiles: - zipObj.write(os.path.join(nRoot, aFile)) - for nRoot, _, nFiles in os.walk("novelwriter"): if nRoot.endswith("__pycache__"): print("Skipping Folder: %s" % nRoot) @@ -504,7 +404,17 @@ def makeMinimalPackage(targetOS): print("Adding File: windows_install.bat") zipObj.write(os.path.join("setup", "windows_uninstall.bat"), "windows_uninstall.bat") print("Adding File: windows_uninstall.bat") - else: + + else: # Linux and Mac + # Add icons + for nRoot, _, nFiles in os.walk(os.path.join("setup", "data")): + print("Adding Folder: %s [%d files]" % (nRoot, len(nFiles))) + for aFile in nFiles: + zipObj.write(os.path.join(nRoot, aFile)) + + zipObj.write(os.path.join("setup", "description_pypi.md")) + print("Adding File: setup/description_pypi.md") + zipObj.write("novelWriter.py") print("Adding File: novelWriter.py") @@ -518,9 +428,12 @@ def makeMinimalPackage(targetOS): print("") print("Created File: %s" % outFile) + # Create Checksum File + # ==================== + try: shaFile = open(outFile+".sha256", mode="w") - subprocess.call(["sha256sum", zipFile], stdout=shaFile, cwd="dist") + subprocess.call(["sha256sum", zipFile], stdout=shaFile, cwd=bldDir) shaFile.close() print("SHA256 Sum: %s" % (outFile+".sha256")) except Exception as e: @@ -611,54 +524,59 @@ def makeDebianPackage(): print("Copying or generating additional files ...") print("") - # Root Files + # Copy/Write Root Files + # ===================== - copyFiles = [ - "setup.cfg", - "pyproject.toml", - "LICENSE.md", - "CREDITS.md", - "CHANGELOG.md", - ] + copyFiles = ["CREDITS.md", "CHANGELOG.md", "LICENSE.md", "pyproject.toml"] for copyFile in copyFiles: shutil.copyfile(copyFile, f"{outDir}/{copyFile}") print("Copied: %s" % copyFile) - with open(f"{outDir}/MANIFEST.in", mode="w") as outFile: - outFile.write( - "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}/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") - with open(f"{outDir}/setup.py", mode="w") as outFile: - outFile.write( - "import setuptools\n" - "setuptools.setup()\n" - ) - print("Wrote: setup.py") + writeFile(f"{outDir}/setup.py", ( + "import setuptools\n" + "setuptools.setup()\n" + )) + print("Wrote: setup.py") - # Copy Debian and Data Folders + 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") + + # Copy/Write Debian Files + # ======================= shutil.copytree("setup/debian", debDir) print("Copied: debian/*") + + writeFile(f"{debDir}/changelog", ( + f"novelwriter ({pkgVers}) unstable; urgency=medium\n\n" + f" * Update to version {pkgVers}\n\n" + f" -- Veronica Berglyd Olsen {pkgDate}\n" + )) + print("Wrote: debian/changelog") + + # Copy/Write Data Files + # ===================== + shutil.copytree("setup/data", datDir) print("Copied: data/*") - # Generate debian/changelog File - - with open(f"{debDir}/changelog", mode="w") as outFile: - outFile.write( - f"novelwriter ({pkgVers}) unstable; urgency=medium\n\n" - f" * Update to version {pkgVers}\n\n" - f" -- Veronica Berglyd Olsen {pkgDate}\n" - ) - print("Wrote: debian/changelog") + shutil.copyfile("setup/description_short.txt", f"{outDir}/data/description_short.txt") + print("Copied: data/description_short.txt") # Build Package + # ============= print("") print("Running dpkg-buildpackage ...") @@ -729,15 +647,12 @@ def makeSimplePackage(embedPython): print("Done") print("") - # Make sample.zip - # =============== + # Build Additional Assets + # ======================= - try: - buildSampleZip() - except Exception as e: - print("Failed with error:") - print(str(e)) - sys.exit(1) + buildQtI18n() + buildSampleZip() + buildPdfManual() # Copy Package Files # ================== @@ -747,7 +662,7 @@ def makeSimplePackage(embedPython): print("# =====================") print("") - copyList = ["CHANGELOG.md", "LICENSE.md", "requirements.txt"] + copyList = ["CREDITS.md", "CHANGELOG.md", "LICENSE.md", "requirements.txt"] iconList = ["novelwriter.ico", "x-novelwriter-project.ico"] cpIgnore = shutil.ignore_patterns("__pycache__") @@ -765,22 +680,21 @@ def makeSimplePackage(embedPython): os.rename(os.path.join(zipDir, "novelwriter", "assets"), os.path.join(outDir, "assets")) print("Writing: __main__.py") - with open(os.path.join(zipDir, "__main__.py"), mode="w") as outFile: - outFile.write( - "#!/usr/bin/env python3\n" - "\n" - "import os\n" - "import sys\n" - "\n" - "sys.path.insert(\n" - " 0, os.path.abspath(\n" - " os.path.join(os.path.dirname(__file__), os.path.pardir, \"lib\")\n" - " )\n" - ")\n\n" - "if __name__ == \"__main__\":\n" - " import novelwriter\n" - " novelwriter.main()\n" - ) + writeFile(os.path.join(zipDir, "__main__.py"), ( + "#!/usr/bin/env python3\n" + "\n" + "import os\n" + "import sys\n" + "\n" + "sys.path.insert(\n" + " 0, os.path.abspath(\n" + " os.path.join(os.path.dirname(__file__), os.path.pardir, \"lib\")\n" + " )\n" + ")\n\n" + "if __name__ == \"__main__\":\n" + " import novelwriter\n" + " novelwriter.main()\n" + )) print("") pyzFile = os.path.join(outDir, "novelWriter.pyz") @@ -917,7 +831,9 @@ def xdgInstall(): # =========================== # Generate launcher - createLauncher(useExec, ".") + desktopData = readFile(os.path.join("setup", "data", "novelwriter.desktop")) + desktopData = desktopData.replace("Exec=novelwriter", f"Exec={useExec}") + writeFile("novelwriter.desktop", desktopData) # Remove old desktop icon exCode = subprocess.call( @@ -1284,7 +1200,7 @@ def winUninstall(): # =============================================================================================== # ## -# Inno Setup Builder (setup-exe, setup-pyz) +# Inno Setup Builder (setup-pyz) ## def innoSetup(): @@ -1296,16 +1212,11 @@ def innoSetup(): print("") # Read the iss template - issData = "" - with open(os.path.join("setup", "win_setup_pyz.iss"), mode="r") as inFile: - issData = inFile.read() - numVers, _, _ = extractVersion() + issData = readFile(os.path.join("setup", "win_setup_pyz.iss")) issData = issData.replace(r"%%version%%", numVers) issData = issData.replace(r"%%dir%%", os.getcwd()) - - with open("setup.iss", mode="w+") as outFile: - outFile.write(issData) + writeFile("setup.iss", issData) try: subprocess.call(["iscc", "setup.iss"]) @@ -1371,7 +1282,6 @@ if __name__ == "__main__": "Additional Builds:", "", " manual Build the help documentation as PDF (requires LaTeX).", - " qthelp Build the help documentation for use with the Qt Assistant.", " qtlupdate Update the translation files for internationalisation.", " qtlrelease Build the language files for internationalisation.", " sample Build the sample project zip file and add it to assets.", @@ -1422,9 +1332,8 @@ if __name__ == "__main__": if "version" in sys.argv: sys.argv.remove("version") - numVers, hexVers, _ = extractVersion() - print("Semantic Version: %s" % numVers) - print("Hexadecimal Version: %s" % hexVers) + print("Checking source version info ...") + extractVersion() sys.exit(0) if "pip" in sys.argv: @@ -1442,10 +1351,6 @@ if __name__ == "__main__": sys.argv.remove("manual") buildPdfManual() - if "qthelp" in sys.argv: - sys.argv.remove("qthelp") - buildQtDocs() - if "qtlrelease" in sys.argv: sys.argv.remove("qtlrelease") buildQtI18n() diff --git a/setup/data/hicolor/128x128/apps/novelwriter.png b/setup/data/hicolor/128x128/apps/novelwriter.png index bf4abdcf..3bef1318 100644 Binary files a/setup/data/hicolor/128x128/apps/novelwriter.png and b/setup/data/hicolor/128x128/apps/novelwriter.png differ diff --git a/setup/data/hicolor/128x128/mimetypes/application-x-novelwriter-project.png b/setup/data/hicolor/128x128/mimetypes/application-x-novelwriter-project.png index 8142127e..dbaac947 100644 Binary files a/setup/data/hicolor/128x128/mimetypes/application-x-novelwriter-project.png and b/setup/data/hicolor/128x128/mimetypes/application-x-novelwriter-project.png differ diff --git a/setup/data/hicolor/16x16/mimetypes/application-x-novelwriter-project.png b/setup/data/hicolor/16x16/mimetypes/application-x-novelwriter-project.png index 2329f757..132b2f18 100644 Binary files a/setup/data/hicolor/16x16/mimetypes/application-x-novelwriter-project.png and b/setup/data/hicolor/16x16/mimetypes/application-x-novelwriter-project.png differ diff --git a/setup/data/hicolor/24x24/mimetypes/application-x-novelwriter-project.png b/setup/data/hicolor/24x24/mimetypes/application-x-novelwriter-project.png index 15854e5b..be91bcd5 100644 Binary files a/setup/data/hicolor/24x24/mimetypes/application-x-novelwriter-project.png and b/setup/data/hicolor/24x24/mimetypes/application-x-novelwriter-project.png differ diff --git a/setup/data/hicolor/256x256/apps/novelwriter.png b/setup/data/hicolor/256x256/apps/novelwriter.png index 0874b9c0..143b1ef8 100644 Binary files a/setup/data/hicolor/256x256/apps/novelwriter.png and b/setup/data/hicolor/256x256/apps/novelwriter.png differ diff --git a/setup/data/hicolor/256x256/mimetypes/application-x-novelwriter-project.png b/setup/data/hicolor/256x256/mimetypes/application-x-novelwriter-project.png index 3282a720..ce82f04b 100644 Binary files a/setup/data/hicolor/256x256/mimetypes/application-x-novelwriter-project.png and b/setup/data/hicolor/256x256/mimetypes/application-x-novelwriter-project.png differ diff --git a/setup/data/hicolor/32x32/mimetypes/application-x-novelwriter-project.png b/setup/data/hicolor/32x32/mimetypes/application-x-novelwriter-project.png index 78889afa..32fc47cc 100644 Binary files a/setup/data/hicolor/32x32/mimetypes/application-x-novelwriter-project.png and b/setup/data/hicolor/32x32/mimetypes/application-x-novelwriter-project.png differ diff --git a/setup/data/hicolor/48x48/mimetypes/application-x-novelwriter-project.png b/setup/data/hicolor/48x48/mimetypes/application-x-novelwriter-project.png index f99ea044..5fd7c22a 100644 Binary files a/setup/data/hicolor/48x48/mimetypes/application-x-novelwriter-project.png and b/setup/data/hicolor/48x48/mimetypes/application-x-novelwriter-project.png differ diff --git a/setup/data/hicolor/64x64/mimetypes/application-x-novelwriter-project.png b/setup/data/hicolor/64x64/mimetypes/application-x-novelwriter-project.png index b941a2f2..d1382130 100644 Binary files a/setup/data/hicolor/64x64/mimetypes/application-x-novelwriter-project.png and b/setup/data/hicolor/64x64/mimetypes/application-x-novelwriter-project.png differ diff --git a/setup/long_description.md b/setup/description_pypi.md similarity index 97% rename from setup/long_description.md rename to setup/description_pypi.md index ff60a17e..8c15b1be 100644 --- a/setup/long_description.md +++ b/setup/description_pypi.md @@ -1,4 +1,4 @@ - + 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 diff --git a/setup/description_short.txt b/setup/description_short.txt new file mode 100644 index 00000000..ac106560 --- /dev/null +++ b/setup/description_short.txt @@ -0,0 +1,6 @@ +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 +storage for robustness. diff --git a/setup/icons/novelwriter.ico b/setup/icons/novelwriter.ico index df883296..24de4e24 100644 Binary files a/setup/icons/novelwriter.ico and b/setup/icons/novelwriter.ico differ diff --git a/setup/icons/x-novelwriter-project.ico b/setup/icons/x-novelwriter-project.ico index b349aa30..c4937b3b 100644 Binary files a/setup/icons/x-novelwriter-project.ico and b/setup/icons/x-novelwriter-project.ico differ diff --git a/setup/make_clean.sh b/setup/make_clean.sh new file mode 100755 index 00000000..28d715eb --- /dev/null +++ b/setup/make_clean.sh @@ -0,0 +1,9 @@ +#!/bin/bash +set -e + +if [ ! -f setup.py ]; then + echo "Must be called from the root folder of the source" + exit 1 +fi + +python3 setup.py clean diff --git a/setup/make_pip.sh b/setup/make_pip.sh new file mode 100755 index 00000000..6d733429 --- /dev/null +++ b/setup/make_pip.sh @@ -0,0 +1,27 @@ +#!/bin/bash +set -e + +if [ ! -f setup.py ]; then + echo "Must be called from the root folder of the source" + exit 1 +fi + +echo "" +echo " Building Packages" +echo "================================================================================" +echo "" +python3 setup.py sdist bdist_wheel + +echo "" +echo " Checking Packages" +echo "================================================================================" +echo "" +twine check dist/* + +echo "" +echo " Done!" +echo "================================================================================" +echo "" +echo " To upload packages to PyPi, run:" +echo " twine upload Source/dist/*" +echo "" diff --git a/setup/make_release.sh b/setup/make_release.sh new file mode 100755 index 00000000..38034295 --- /dev/null +++ b/setup/make_release.sh @@ -0,0 +1,21 @@ +#!/bin/bash +set -e + +if [ ! -f setup.py ]; then + echo "Must be called from the root folder of the source" + exit 1 +fi + +echo "" +echo " Building Minimal Packages" +echo "================================================================================" +echo "" +python3 setup.py minimal-zip --target-win +python3 setup.py minimal-zip --target-linux +python3 setup.py minimal-zip --target-darwin + +echo "" +echo " Building Linux Packages" +echo "================================================================================" +echo "" +python3 setup.py build-deb diff --git a/setup/novelwriter.png b/setup/novelwriter.png index e7f169cd..143b1ef8 100644 Binary files a/setup/novelwriter.png and b/setup/novelwriter.png differ diff --git a/setup/novelwriter_text.png b/setup/novelwriter_text.png new file mode 100644 index 00000000..e7f169cd Binary files /dev/null and b/setup/novelwriter_text.png differ