353 lines
10 KiB
Python
Executable File
353 lines
10 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
The main setup script for novelWriter.
|
|
|
|
It runs the standard setuptool.setup() with all options taken from the
|
|
setup.cfg file.
|
|
|
|
In addition, a few specialised commands are available. These are
|
|
described in the help text in the main section.
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import shutil
|
|
import subprocess
|
|
|
|
OS_NONE = 0
|
|
OS_LINUX = 1
|
|
OS_WIN = 2
|
|
OS_DARWIN = 3
|
|
|
|
# =============================================================================================== #
|
|
# Qt Assistant Documentation Builder
|
|
# =============================================================================================== #
|
|
|
|
def buildQtDocs():
|
|
"""This function will build the documentation as a Qt help file. The
|
|
file is then copied into the nw/assets/help directory and can be
|
|
included in builds.
|
|
|
|
Depends on packages:
|
|
* pip install sphinx
|
|
* pip install sphinx-rtd-theme
|
|
* pip install sphinxcontrib-qthelp
|
|
|
|
It also requires the qhelpgenerator to be available on the system.
|
|
"""
|
|
buildDir = os.path.join("docs", "build", "qthelp")
|
|
helpDir = os.path.join("nw", "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")
|
|
sys.exit(1)
|
|
else:
|
|
print("Documentation build: OK")
|
|
print("")
|
|
|
|
return
|
|
|
|
# =============================================================================================== #
|
|
# Sample Project ZIP File Builder
|
|
# =============================================================================================== #
|
|
|
|
def buildSampleZip():
|
|
"""Bundle the sample project into a single zip file to be saved into
|
|
the nw/assets folder for further bundling into builds.
|
|
"""
|
|
print("")
|
|
print("Building Sample ZIP File")
|
|
print("========================")
|
|
print("")
|
|
|
|
srcSample = "sample"
|
|
dstSample = os.path.join("nw", "assets", "sample.zip")
|
|
|
|
if os.path.isdir(srcSample):
|
|
if os.path.isfile(dstSample):
|
|
os.unlink(dstSample)
|
|
|
|
from zipfile import ZipFile
|
|
|
|
with ZipFile(dstSample, "w") as zipObj:
|
|
print("Compressing: nwProject.nwx")
|
|
zipObj.write(os.path.join(srcSample, "nwProject.nwx"), "nwProject.nwx")
|
|
for docFile in os.listdir(os.path.join(srcSample, "content")):
|
|
print("Compressing: content/%s" % docFile)
|
|
srcDoc = os.path.join(srcSample, "content", docFile)
|
|
zipObj.write(srcDoc, "content/"+docFile)
|
|
|
|
else:
|
|
print("Error: Could not find sample project source directory.")
|
|
sys.exit(1)
|
|
|
|
print("")
|
|
print("Built file: %s" % dstSample)
|
|
print("")
|
|
|
|
return
|
|
|
|
# =============================================================================================== #
|
|
# Create Launcher
|
|
# =============================================================================================== #
|
|
|
|
def xdgInstall():
|
|
"""Will attempt to install icons and make a launcher.
|
|
"""
|
|
print("")
|
|
print("XDG Install")
|
|
print("===========")
|
|
print("")
|
|
|
|
# Find Executable(s)
|
|
# ==================
|
|
|
|
exOpts = []
|
|
|
|
testExec = shutil.which("novelWriter")
|
|
if testExec is not None:
|
|
exOpts.append(testExec)
|
|
|
|
testExec = shutil.which("novelwriter")
|
|
if testExec is not None:
|
|
exOpts.append(testExec)
|
|
|
|
testExec = os.path.join(os.getcwd(), "novelWriter.py")
|
|
if os.path.isfile(testExec):
|
|
exOpts.append(testExec)
|
|
|
|
useExec = ""
|
|
nOpts = len(exOpts)
|
|
if nOpts == 0:
|
|
print("Error: No executables for novelWriter found.")
|
|
sys.exit(1)
|
|
elif nOpts == 1:
|
|
useExec = exOpts[0]
|
|
else:
|
|
print("Found multiple novelWriter executables:")
|
|
print("")
|
|
for iExec, anExec in enumerate(exOpts):
|
|
print(" [%d] %s" % (iExec, anExec))
|
|
print("")
|
|
intVal = int(input("Please select which novelWriter executable to use: "))
|
|
print("")
|
|
|
|
if intVal >= 0 and intVal < nOpts:
|
|
useExec = exOpts[intVal]
|
|
else:
|
|
print("Error: Invalid selection.")
|
|
sys.exit(1)
|
|
|
|
print("Using executable: %s " % useExec)
|
|
print("")
|
|
|
|
# Create and Install Launcher
|
|
# ===========================
|
|
|
|
desktopData = ""
|
|
with open("./setup/novelwriter.desktop", mode="r") as inFile:
|
|
desktopData = inFile.read()
|
|
|
|
desktopData = desktopData.replace(r"%%exec%%", useExec)
|
|
with open("./novelwriter.desktop", mode="w+") as outFile:
|
|
outFile.write(desktopData)
|
|
|
|
exCode = subprocess.call(
|
|
["xdg-desktop-menu", "install", "--novendor", "./novelwriter.desktop"]
|
|
)
|
|
if exCode == 0:
|
|
print("Installed menu desktop file")
|
|
else:
|
|
print(f"Error {exCode}: Could not install menu desktop file")
|
|
|
|
exCode = subprocess.call(
|
|
["xdg-desktop-icon", "install", "--novendor", "./novelwriter.desktop"]
|
|
)
|
|
if exCode == 0:
|
|
print("Installed icon desktop file")
|
|
else:
|
|
print(f"Error {exCode}: Could not install icon desktop file")
|
|
|
|
print("")
|
|
|
|
# Install MimeType
|
|
# ================
|
|
|
|
exCode = subprocess.call([
|
|
"xdg-mime", "install",
|
|
"./setup/mime/x-novelwriter-project.xml"
|
|
])
|
|
if exCode == 0:
|
|
print("Installed mimetype")
|
|
else:
|
|
print(f"Error {exCode}: Could not install mimetype")
|
|
|
|
print("")
|
|
|
|
# Install Icons
|
|
# =============
|
|
|
|
sizeArr = ["16", "22", "24", "32", "48", "64", "96", "128", "256", "512"]
|
|
|
|
# App Icon
|
|
for aSize in sizeArr:
|
|
exCode = subprocess.call([
|
|
"xdg-icon-resource", "install",
|
|
"--novendor", "--noupdate",
|
|
"--context", "apps",
|
|
"--size", aSize,
|
|
f"./setup/icons/scaled/icon-novelwriter-{aSize}.png",
|
|
"novelwriter"
|
|
])
|
|
if exCode == 0:
|
|
print(f"Installed app icon size {aSize}")
|
|
else:
|
|
print(f"Error {exCode}: Could not install app icon size {aSize}")
|
|
|
|
# Mimetype
|
|
for aSize in sizeArr:
|
|
exCode = subprocess.call([
|
|
"xdg-icon-resource", "install",
|
|
"--noupdate",
|
|
"--context", "mimetypes",
|
|
"--size", aSize,
|
|
f"./setup/icons/scaled/mime-novelwriter-{aSize}.png",
|
|
"application-x-novelwriter-project"
|
|
])
|
|
if exCode == 0:
|
|
print(f"Installed mime icon size {aSize}")
|
|
else:
|
|
print(f"Error {exCode}: Could not install mime icon size {aSize}")
|
|
|
|
# Update Cache
|
|
exCode = subprocess.call(["xdg-icon-resource", "forceupdate"])
|
|
if exCode == 0:
|
|
print("Updated icon cache")
|
|
else:
|
|
print(f"Error {exCode}: Could not update icon cache")
|
|
|
|
print("")
|
|
print("Done!")
|
|
print("")
|
|
|
|
return
|
|
|
|
# =============================================================================================== #
|
|
# Process Jobs
|
|
# =============================================================================================== #
|
|
|
|
if __name__ == "__main__":
|
|
"""Parse command line options and run the commands.
|
|
"""
|
|
# Detect OS
|
|
if sys.platform.startswith("linux"):
|
|
hostOS = OS_LINUX
|
|
elif sys.platform.startswith("darwin"):
|
|
hostOS = OS_DARWIN
|
|
elif sys.platform.startswith("win32"):
|
|
hostOS = OS_WIN
|
|
elif sys.platform.startswith("cygwin"):
|
|
hostOS = OS_WIN
|
|
else:
|
|
hostOS = OS_NONE
|
|
|
|
helpMsg = (
|
|
"\n"
|
|
"novelWriter Setup Tool\n"
|
|
"======================\n"
|
|
"This tool provides some additional setup commands for novelWriter.\n"
|
|
"\n"
|
|
"help Print this help message.\n"
|
|
"qthelp Build the help documentation for use with the Qt Assistant.\n"
|
|
" Run before install to enable in the the installed version.\n"
|
|
"sample Build the sample project as a zip file.\n"
|
|
" Run before install to enable creating sample projects.\n"
|
|
"install Installs novelWriter to the system's Python install location.\n"
|
|
" Run as root or with sudo for system-wide install, or as\n"
|
|
" user for single user install.\n"
|
|
"xdg-install Install launcher and icons for freedesktop systems.\n"
|
|
" Run as root or with sudo for system-wide install, or as\n"
|
|
" user for single user install.\n"
|
|
)
|
|
|
|
if "help" in sys.argv:
|
|
sys.argv.remove("help")
|
|
print(helpMsg)
|
|
sys.exit(0)
|
|
|
|
if "launcher" in sys.argv:
|
|
sys.argv.remove("launcher")
|
|
print("The 'launcher' option has been replaced by 'xdg-install'.")
|
|
sys.exit(1)
|
|
|
|
if "qthelp" in sys.argv:
|
|
sys.argv.remove("qthelp")
|
|
buildQtDocs()
|
|
|
|
if "sample" in sys.argv:
|
|
sys.argv.remove("sample")
|
|
buildSampleZip()
|
|
|
|
if "xdg-install" in sys.argv:
|
|
sys.argv.remove("xdg-install")
|
|
if hostOS == OS_WIN:
|
|
print("ERROR: xdg-install cannot be used on Windows")
|
|
sys.exit(1)
|
|
else:
|
|
xdgInstall()
|
|
|
|
if len(sys.argv) <= 1:
|
|
# Nothing more to do
|
|
sys.exit(0)
|
|
|
|
# Run the standard setup
|
|
import setuptools # noqa: F401
|
|
setuptools.setup()
|
|
|
|
# END Main
|