Merge pull request #584 from vkbo/simple_pack
Package for Windows without an .exe file
This commit is contained in:
@@ -239,8 +239,8 @@ run:
|
||||
your python executable followed by ``novelWriter.py``. It should look something like this:
|
||||
``C:\...\AppData\Local\Programs\Python\Python38\python.exe novelWriter.py``
|
||||
|
||||
You can also run the ``make.py`` script to generate a single executable, or an installer.
|
||||
See `Build and Install novelWriter`_ for more details or run: ``python make.py help``.
|
||||
You can also run the ``setup.py`` script to generate a single executable, or an installer.
|
||||
See `Build and Install novelWriter`_ for more details or run: ``python setup.py help``.
|
||||
|
||||
.. _python.org: https://www.python.org/downloads/windows/
|
||||
.. _Build and Install novelWriter: https://github.com/vkbo/novelWriter/blob/main/setup/README.md
|
||||
|
||||
@@ -1,333 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
This make script is intended for building distributable packages of
|
||||
novelWriter. These are either:
|
||||
|
||||
* A single file executable named dist/novelWriter(.exe). This is a
|
||||
quite slow option, and the file is fairly big.
|
||||
* A single directory named dist/novelWriter with a novelWriter(.exe),
|
||||
and all dependecies included.
|
||||
* The latter can be combined with a build stage of a setup.exe file if
|
||||
on Windows. This requires Inno Setup to be installed and in path.
|
||||
|
||||
In addition, providing the pip otion will cause the script to try to
|
||||
install all dependencies needed for runing the build, and for running
|
||||
novelWriter itself.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
OS_NONE = 0
|
||||
OS_LINUX = 1
|
||||
OS_WIN = 2
|
||||
OS_DARWIN = 3
|
||||
|
||||
# =============================================================================================== #
|
||||
# Package Installer
|
||||
# =============================================================================================== #
|
||||
|
||||
def installPackages(hostOS):
|
||||
"""Install package dependencies both for this script and for running
|
||||
novelWriter itself.
|
||||
"""
|
||||
print("")
|
||||
print("Installing Dependencies")
|
||||
print("#######################")
|
||||
print("")
|
||||
|
||||
installQueue = ["pip", "pyinstaller", "-r requirements.txt"]
|
||||
if hostOS == OS_DARWIN:
|
||||
installQueue.append("pyobjc")
|
||||
|
||||
pyCmd = [sys.executable, "-m"]
|
||||
pipCmd = ["pip", "install", "--user", "--upgrade"]
|
||||
for stepCmd in installQueue:
|
||||
pkgCmd = stepCmd.split(" ")
|
||||
try:
|
||||
subprocess.call(pyCmd + pipCmd + pkgCmd)
|
||||
except Exception as e:
|
||||
print("Failed with error:")
|
||||
print(str(e))
|
||||
sys.exit(1)
|
||||
|
||||
return
|
||||
|
||||
# =============================================================================================== #
|
||||
# Run PyInstaller on Package
|
||||
# =============================================================================================== #
|
||||
|
||||
def freezePackage(buildWindowed, oneFile, makeSetup, hostOS):
|
||||
"""Run PyInstaller to freeze the packages. This assumes all
|
||||
dependencies are already in place.
|
||||
"""
|
||||
import PyInstaller.__main__ # noqa: E402
|
||||
|
||||
print("")
|
||||
print("Running PyInstaller")
|
||||
print("###################")
|
||||
print("")
|
||||
|
||||
if hostOS == OS_WIN:
|
||||
dotDot = ";"
|
||||
else:
|
||||
dotDot = ":"
|
||||
|
||||
sys.modules["FixTk"] = None
|
||||
instOpt = [
|
||||
"--name=novelWriter",
|
||||
"--clean",
|
||||
"--add-data=%s%s%s" % (os.path.join("nw", "assets"), dotDot, "assets"),
|
||||
"--icon=%s" % os.path.join("nw", "assets", "icons", "novelwriter.ico"),
|
||||
"--exclude-module=PyQt5.QtQml",
|
||||
"--exclude-module=PyQt5.QtBluetooth",
|
||||
"--exclude-module=PyQt5.QtDBus",
|
||||
"--exclude-module=PyQt5.QtMultimedia",
|
||||
"--exclude-module=PyQt5.QtMultimediaWidgets",
|
||||
"--exclude-module=PyQt5.QtNetwork",
|
||||
"--exclude-module=PyQt5.QtNetworkAuth",
|
||||
"--exclude-module=PyQt5.QtNfc",
|
||||
"--exclude-module=PyQt5.QtQuick",
|
||||
"--exclude-module=PyQt5.QtQuickWidgets",
|
||||
"--exclude-module=PyQt5.QtRemoteObjects",
|
||||
"--exclude-module=PyQt5.QtSensors",
|
||||
"--exclude-module=PyQt5.QtSerialPort",
|
||||
"--exclude-module=PyQt5.QtSql",
|
||||
"--exclude-module=FixTk",
|
||||
"--exclude-module=tcl",
|
||||
"--exclude-module=tk",
|
||||
"--exclude-module=_tkinter",
|
||||
"--exclude-module=tkinter",
|
||||
"--exclude-module=Tkinter",
|
||||
]
|
||||
|
||||
if buildWindowed:
|
||||
instOpt.append("--windowed")
|
||||
|
||||
if oneFile and not makeSetup:
|
||||
instOpt.append("--onefile")
|
||||
else:
|
||||
instOpt.append("--onedir")
|
||||
|
||||
instOpt.append("novelWriter.py")
|
||||
|
||||
# Make sample.zip first
|
||||
try:
|
||||
subprocess.call([sys.executable, "setup.py", "sample"])
|
||||
except Exception as e:
|
||||
print("Failed with error:")
|
||||
print(str(e))
|
||||
sys.exit(1)
|
||||
|
||||
PyInstaller.__main__.run(instOpt)
|
||||
|
||||
if not oneFile:
|
||||
# These files are not needed, and take up a fair bit of space.
|
||||
delFiles = []
|
||||
if hostOS == OS_WIN:
|
||||
delFiles = [
|
||||
"Qt5DBus.dll",
|
||||
"Qt5Network.dll",
|
||||
"Qt5Qml.dll",
|
||||
"Qt5QmlModels.dll",
|
||||
"Qt5Quick.dll",
|
||||
"Qt5Quick3D.dll",
|
||||
"Qt5Quick3DAssetImport.dll",
|
||||
"Qt5Quick3DRender.dll",
|
||||
"Qt5Quick3DRuntimeRender.dll",
|
||||
"Qt5Quick3DUtils.dll",
|
||||
"Qt5Sql.dll"
|
||||
]
|
||||
elif hostOS == OS_LINUX:
|
||||
delFiles = [
|
||||
"libQt5DBus.so.5",
|
||||
"libQt5Network.so.5",
|
||||
"libQt5Qml.so.5",
|
||||
"libQt5QmlModels.so.5",
|
||||
"libQt5Quick.so.5",
|
||||
"libQt5Quick3D.so.5",
|
||||
"libQt5Quick3DAssetImport.so.5",
|
||||
"libQt5Quick3DRender.so.5",
|
||||
"libQt5Quick3DRuntimeRender.so.5",
|
||||
"libQt5Quick3DUtils.so.5",
|
||||
"libQt5Sql.so.5"
|
||||
]
|
||||
distDir = os.path.join(os.getcwd(), "dist", "novelWriter")
|
||||
for delFile in delFiles:
|
||||
delPath = os.path.join(distDir, delFile)
|
||||
if os.path.isfile(delPath):
|
||||
print("Deleting file: %s" % delPath)
|
||||
os.unlink(delPath)
|
||||
|
||||
print("")
|
||||
print("Build Finished")
|
||||
print("")
|
||||
print("The novelWriter executable should be in the folder named 'dist'")
|
||||
print("")
|
||||
|
||||
return
|
||||
|
||||
# =============================================================================================== #
|
||||
# Inno Setup Builder
|
||||
# =============================================================================================== #
|
||||
|
||||
def innoSetup():
|
||||
"""Run the Inno Setup tool to build a setup.exe file for Windows.
|
||||
"""
|
||||
print("")
|
||||
print("Running Inno Setup")
|
||||
print("##################")
|
||||
print("")
|
||||
|
||||
# Read the iss template
|
||||
issData = ""
|
||||
with open(os.path.join("setup", "win_setup.iss"), mode="r") as inFile:
|
||||
issData = inFile.read()
|
||||
|
||||
import nw # noqa: E402
|
||||
issData = issData.replace(r"%%version%%", nw.__version__)
|
||||
issData = issData.replace(r"%%dir%%", os.getcwd())
|
||||
|
||||
with open("setup.iss", mode="w+") as outFile:
|
||||
outFile.write(issData)
|
||||
|
||||
try:
|
||||
subprocess.call(["iscc", "setup.iss"])
|
||||
except Exception as e:
|
||||
print("Inno Setup failed with error:")
|
||||
print(str(e))
|
||||
sys.exit(1)
|
||||
|
||||
return
|
||||
|
||||
# =============================================================================================== #
|
||||
# Clean Build and Dist Folders
|
||||
# =============================================================================================== #
|
||||
|
||||
def cleanInstall():
|
||||
"""Recursively delete the 'build' and 'dist' folders.
|
||||
"""
|
||||
print("")
|
||||
print("Cleaning up build environment ...")
|
||||
|
||||
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")
|
||||
|
||||
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")
|
||||
|
||||
print("")
|
||||
|
||||
return
|
||||
|
||||
# =============================================================================================== #
|
||||
# Process Build Steps
|
||||
# =============================================================================================== #
|
||||
|
||||
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
|
||||
|
||||
# Flags and Variables
|
||||
buildWindowed = True
|
||||
oneFile = False
|
||||
makeSetup = False
|
||||
doFreeze = False
|
||||
|
||||
helpMsg = (
|
||||
"\n"
|
||||
"novelWriter Make Tool\n"
|
||||
"=====================\n"
|
||||
"This tool provides build commands for distibuting novelWriter as a\n"
|
||||
"package. The available options are as follows:\n"
|
||||
"\n"
|
||||
"help Print the help message.\n"
|
||||
"freeze Freeze the package and produces a folder of all\n"
|
||||
" dependencies using pyinstaller.\n"
|
||||
"onefile Build a standalone executable with all dependencies\n"
|
||||
" bundled. Implies 'freeze', cannot be used with 'setup'.\n"
|
||||
"pip Run pip to install all package dependencies for\n"
|
||||
" novelWriter and this build tool.\n"
|
||||
"setup Build a setup.exe installer for Windows. This option\n"
|
||||
" automaticall disables the 'onefile' option.\n"
|
||||
"clean This will attempt to delete the 'build' and 'dist'\n"
|
||||
" folders in the current folder.\n"
|
||||
)
|
||||
|
||||
if "help" in sys.argv or len(sys.argv) <= 1:
|
||||
print(helpMsg)
|
||||
sys.exit(0)
|
||||
|
||||
if not os.path.isfile(os.path.join(os.getcwd(), "novelWriter.py")):
|
||||
print("Error: This script must be run in the root folder of novelWriter.")
|
||||
sys.exit(1)
|
||||
|
||||
if not os.path.isdir(os.path.join(os.getcwd(), "nw")):
|
||||
print("Error: This script must be run in the root folder of novelWriter.")
|
||||
sys.exit(1)
|
||||
|
||||
if "clean" in sys.argv:
|
||||
sys.argv.remove("clean")
|
||||
cleanInstall()
|
||||
|
||||
if "pip" in sys.argv:
|
||||
sys.argv.remove("pip")
|
||||
installPackages(hostOS)
|
||||
|
||||
if "freeze" in sys.argv:
|
||||
sys.argv.remove("freeze")
|
||||
doFreeze = True
|
||||
|
||||
if "onefile" in sys.argv:
|
||||
sys.argv.remove("onefile")
|
||||
doFreeze = True
|
||||
oneFile = True
|
||||
|
||||
if "setup" in sys.argv:
|
||||
sys.argv.remove("setup")
|
||||
if hostOS == OS_WIN:
|
||||
oneFile = False
|
||||
makeSetup = True
|
||||
else:
|
||||
print("Error: Argument 'setup' for Inno Setup is Windows only.")
|
||||
sys.exit(1)
|
||||
|
||||
if doFreeze:
|
||||
freezePackage(buildWindowed, oneFile, makeSetup, hostOS)
|
||||
|
||||
if makeSetup:
|
||||
innoSetup()
|
||||
|
||||
# END Main
|
||||
+10
-4
@@ -269,10 +269,16 @@ class Config:
|
||||
logger.verbose("Config path: %s" % self.confPath)
|
||||
logger.verbose("Data path: %s" % self.dataPath)
|
||||
|
||||
self.confFile = self.appHandle+".conf"
|
||||
self.lastPath = os.path.expanduser("~")
|
||||
self.appPath = getattr(sys, "_MEIPASS", os.path.abspath(os.path.dirname(__file__)))
|
||||
self.appRoot = os.path.join(self.appPath, os.path.pardir)
|
||||
self.confFile = self.appHandle+".conf"
|
||||
self.lastPath = os.path.expanduser("~")
|
||||
self.appPath = getattr(sys, "_MEIPASS", os.path.abspath(os.path.dirname(__file__)))
|
||||
self.appRoot = os.path.abspath(os.path.join(self.appPath, os.path.pardir))
|
||||
|
||||
if self.appRoot.endswith(".pyz"):
|
||||
self.appRoot = os.path.abspath(os.path.join(self.appRoot, os.path.pardir))
|
||||
self.appPath = self.appRoot
|
||||
|
||||
# Assets
|
||||
self.assetPath = os.path.join(self.appPath, "assets")
|
||||
self.themeRoot = os.path.join(self.assetPath, "themes")
|
||||
self.dictPath = os.path.join(self.assetPath, "dict")
|
||||
|
||||
@@ -20,20 +20,89 @@ OS_WIN = 2
|
||||
OS_DARWIN = 3
|
||||
|
||||
# =============================================================================================== #
|
||||
# Qt Assistant Documentation Builder
|
||||
# General
|
||||
# =============================================================================================== #
|
||||
|
||||
##
|
||||
# Package Installer (pip)
|
||||
##
|
||||
|
||||
def installPackages(hostOS):
|
||||
"""Install package dependencies both for this script and for running
|
||||
novelWriter itself.
|
||||
"""
|
||||
print("")
|
||||
print("Installing Dependencies")
|
||||
print("#######################")
|
||||
print("")
|
||||
|
||||
installQueue = ["pip", "-r requirements.txt"]
|
||||
if hostOS == OS_DARWIN:
|
||||
installQueue.append("pyobjc")
|
||||
|
||||
pyCmd = [sys.executable, "-m"]
|
||||
pipCmd = ["pip", "install", "--user", "--upgrade"]
|
||||
for stepCmd in installQueue:
|
||||
pkgCmd = stepCmd.split(" ")
|
||||
try:
|
||||
subprocess.call(pyCmd + pipCmd + pkgCmd)
|
||||
except Exception as e:
|
||||
print("Failed with error:")
|
||||
print(str(e))
|
||||
sys.exit(1)
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
# Clean Build and Dist Folders (clean)
|
||||
##
|
||||
|
||||
def cleanInstall():
|
||||
"""Recursively delete the 'build' and 'dist' folders.
|
||||
"""
|
||||
print("")
|
||||
print("Cleaning up build environment ...")
|
||||
|
||||
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")
|
||||
|
||||
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")
|
||||
|
||||
print("")
|
||||
|
||||
return
|
||||
|
||||
# =============================================================================================== #
|
||||
# Additional Buiilds
|
||||
# =============================================================================================== #
|
||||
|
||||
##
|
||||
# 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 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")
|
||||
@@ -85,6 +154,13 @@ def buildQtDocs():
|
||||
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")
|
||||
@@ -92,9 +168,9 @@ def buildQtDocs():
|
||||
|
||||
return
|
||||
|
||||
# =============================================================================================== #
|
||||
# Sample Project ZIP File Builder
|
||||
# =============================================================================================== #
|
||||
##
|
||||
# Sample Project ZIP File Builder (sample)
|
||||
##
|
||||
|
||||
def buildSampleZip():
|
||||
"""Bundle the sample project into a single zip file to be saved into
|
||||
@@ -133,9 +209,313 @@ def buildSampleZip():
|
||||
return
|
||||
|
||||
# =============================================================================================== #
|
||||
# Create Launcher
|
||||
# Python Packaging
|
||||
# =============================================================================================== #
|
||||
|
||||
##
|
||||
# Make Simple Package (winpack)
|
||||
##
|
||||
|
||||
def makeSimplePackage(embedPython):
|
||||
"""Run zipapp to freeze the packages. This assumes zipapp and pip
|
||||
are already installed.
|
||||
"""
|
||||
import urllib.request
|
||||
import zipfile
|
||||
import zipapp
|
||||
|
||||
# Set Up Folder
|
||||
# =============
|
||||
|
||||
if not os.path.isdir("dist"):
|
||||
os.mkdir("dist")
|
||||
|
||||
outDir = os.path.join("dist", "novelWriter")
|
||||
zipDir = os.path.join("dist", "zipapp_temp")
|
||||
libDir = os.path.join(outDir, "lib")
|
||||
if os.path.isdir(zipDir):
|
||||
shutil.rmtree(zipDir)
|
||||
if os.path.isdir(outDir):
|
||||
shutil.rmtree(outDir)
|
||||
|
||||
os.mkdir(outDir)
|
||||
os.mkdir(libDir)
|
||||
|
||||
# Download Python Embeddable
|
||||
# ==========================
|
||||
|
||||
if embedPython:
|
||||
print("")
|
||||
print("# Adding Python Embeddable")
|
||||
print("# ========================")
|
||||
print("")
|
||||
|
||||
pyVers = "%d.%d.%d" % (sys.version_info[:3])
|
||||
zipFile = "python-%s-embed-amd64.zip" % pyVers
|
||||
pyZip = os.path.join("dist", zipFile)
|
||||
if not os.path.isfile(pyZip):
|
||||
pyUrl = f"https://www.python.org/ftp/python/{pyVers}/{zipFile}"
|
||||
print("Downloading: %s" % pyUrl)
|
||||
urllib.request.urlretrieve(pyUrl, pyZip)
|
||||
|
||||
print("Extracting ...")
|
||||
with zipfile.ZipFile(pyZip, "r") as inFile:
|
||||
inFile.extractall(outDir)
|
||||
|
||||
print("Done")
|
||||
print("")
|
||||
|
||||
# Make sample.zip
|
||||
# ===============
|
||||
|
||||
try:
|
||||
buildSampleZip()
|
||||
except Exception as e:
|
||||
print("Failed with error:")
|
||||
print(str(e))
|
||||
sys.exit(1)
|
||||
|
||||
# Copy Package Files
|
||||
# ==================
|
||||
|
||||
print("")
|
||||
print("# Copying Package Files")
|
||||
print("# =====================")
|
||||
print("")
|
||||
|
||||
copyList = ["CHANGELOG.md", "LICENSE.md", "requirements.txt"]
|
||||
iconList = ["novelwriter.ico", "x-novelwriter-project.ico"]
|
||||
cpIgnore = shutil.ignore_patterns("__pycache__")
|
||||
|
||||
print("Copying: nw")
|
||||
shutil.copytree("nw", os.path.join(zipDir, "nw"), ignore=cpIgnore)
|
||||
for copyFile in copyList:
|
||||
print("Copying: %s" % copyFile)
|
||||
shutil.copy2(copyFile, os.path.join(outDir, copyFile))
|
||||
for iconFile in iconList:
|
||||
print("Copying: %s" % iconFile)
|
||||
shutil.copy2(os.path.join("setup", "icons", iconFile), os.path.join(outDir, iconFile))
|
||||
|
||||
# Move assets to outDir as it should not be packed with the rest
|
||||
print("Copying: assets")
|
||||
os.rename(os.path.join(zipDir, "nw", "assets"), os.path.join(outDir, "assets"))
|
||||
|
||||
print("Writing: __main__.py")
|
||||
with open(os.path.join(zipDir, "__main__.py"), mode="w") as outFile:
|
||||
outFile.write(
|
||||
"#!\"pythonw.exe\"\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 nw\n"
|
||||
" nw.main()\n"
|
||||
)
|
||||
print("")
|
||||
|
||||
pyzFile = os.path.join(outDir, "novelWriter.pyz")
|
||||
zipapp.create_archive(zipDir, target=pyzFile, interpreter="python3")
|
||||
|
||||
# Install Dependencies
|
||||
# ====================
|
||||
|
||||
print("")
|
||||
print("# Installing Dependencies")
|
||||
print("# =======================")
|
||||
print("")
|
||||
|
||||
sysCmd = [sys.executable]
|
||||
sysCmd += "-m pip install -r requirements.txt --target".split()
|
||||
sysCmd += [libDir]
|
||||
try:
|
||||
subprocess.call(sysCmd)
|
||||
except Exception as e:
|
||||
print("Failed with error:")
|
||||
print(str(e))
|
||||
sys.exit(1)
|
||||
|
||||
for subDir in os.listdir(libDir):
|
||||
chkDir = os.path.join(libDir, subDir)
|
||||
if os.path.isdir(chkDir) and chkDir.endswith(".dist-info"):
|
||||
shutil.rmtree(chkDir)
|
||||
|
||||
print("")
|
||||
|
||||
# Remove Unneeded Library Files
|
||||
# =============================
|
||||
|
||||
delQtLibs = [
|
||||
"opengl32sw.dll",
|
||||
"Qt5DBus.dll",
|
||||
"Qt5Designer.dll",
|
||||
"Qt5Network.dll",
|
||||
"Qt5OpenGL.dll",
|
||||
"Qt5Qml.dll",
|
||||
"Qt5QmlModels.dll",
|
||||
"Qt5QmlWorkerScript.dll",
|
||||
"Qt5Quick.dll",
|
||||
"Qt5Quick3D.dll",
|
||||
"Qt5Quick3DAssetImport.dll",
|
||||
"Qt5Quick3DRender.dll",
|
||||
"Qt5Quick3DRuntimeRender.dll",
|
||||
"Qt5Quick3DUtils.dll",
|
||||
"Qt5QuickControls2.dll",
|
||||
"Qt5QuickParticles.dll",
|
||||
"Qt5QuickShapes.dll",
|
||||
"Qt5QuickTemplates2.dll",
|
||||
"Qt5QuickTest.dll",
|
||||
"Qt5QuickWidgets.dll",
|
||||
"Qt5Sql.dll",
|
||||
]
|
||||
qtLibDir = os.path.join(libDir, "PyQt5", "Qt", "bin")
|
||||
for libName in delQtLibs:
|
||||
delFile = os.path.join(qtLibDir, libName)
|
||||
if os.path.isfile(delFile):
|
||||
print("Deleting: %s" % delFile)
|
||||
os.unlink(delFile)
|
||||
|
||||
qmlDir = os.path.join(libDir, "PyQt5", "Qt", "qml")
|
||||
if os.path.isdir(qmlDir):
|
||||
shutil.rmtree(qmlDir)
|
||||
|
||||
print("")
|
||||
print("Done!")
|
||||
print("")
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
# Run PyInstaller on Package (freeze, onefile)
|
||||
##
|
||||
|
||||
def freezePackage(buildWindowed, oneFile, makeSetup, hostOS):
|
||||
"""Run PyInstaller to freeze the packages. This assumes all
|
||||
dependencies are already in place.
|
||||
"""
|
||||
try:
|
||||
import PyInstaller.__main__ # noqa: E402
|
||||
except Exception:
|
||||
print("ERROR: Package 'pyinstaller' is missing on this system")
|
||||
sys.exit(1)
|
||||
|
||||
print("")
|
||||
print("Running PyInstaller")
|
||||
print("###################")
|
||||
print("")
|
||||
|
||||
if hostOS == OS_WIN:
|
||||
dotDot = ";"
|
||||
else:
|
||||
dotDot = ":"
|
||||
|
||||
sys.modules["FixTk"] = None
|
||||
instOpt = [
|
||||
"--name=novelWriter",
|
||||
"--clean",
|
||||
"--add-data=%s%s%s" % (os.path.join("nw", "assets"), dotDot, "assets"),
|
||||
"--icon=%s" % os.path.join("nw", "assets", "icons", "novelwriter.ico"),
|
||||
"--exclude-module=PyQt5.QtQml",
|
||||
"--exclude-module=PyQt5.QtBluetooth",
|
||||
"--exclude-module=PyQt5.QtDBus",
|
||||
"--exclude-module=PyQt5.QtMultimedia",
|
||||
"--exclude-module=PyQt5.QtMultimediaWidgets",
|
||||
"--exclude-module=PyQt5.QtNetwork",
|
||||
"--exclude-module=PyQt5.QtNetworkAuth",
|
||||
"--exclude-module=PyQt5.QtNfc",
|
||||
"--exclude-module=PyQt5.QtQuick",
|
||||
"--exclude-module=PyQt5.QtQuickWidgets",
|
||||
"--exclude-module=PyQt5.QtRemoteObjects",
|
||||
"--exclude-module=PyQt5.QtSensors",
|
||||
"--exclude-module=PyQt5.QtSerialPort",
|
||||
"--exclude-module=PyQt5.QtSql",
|
||||
"--exclude-module=FixTk",
|
||||
"--exclude-module=tcl",
|
||||
"--exclude-module=tk",
|
||||
"--exclude-module=_tkinter",
|
||||
"--exclude-module=tkinter",
|
||||
"--exclude-module=Tkinter",
|
||||
]
|
||||
|
||||
if buildWindowed:
|
||||
instOpt.append("--windowed")
|
||||
|
||||
if oneFile and not makeSetup:
|
||||
instOpt.append("--onefile")
|
||||
else:
|
||||
instOpt.append("--onedir")
|
||||
|
||||
instOpt.append("novelWriter.py")
|
||||
|
||||
# Make sample.zip first
|
||||
try:
|
||||
buildSampleZip()
|
||||
except Exception as e:
|
||||
print("Failed with error:")
|
||||
print(str(e))
|
||||
sys.exit(1)
|
||||
|
||||
PyInstaller.__main__.run(instOpt)
|
||||
|
||||
if not oneFile:
|
||||
# These files are not needed, and take up a fair bit of space.
|
||||
delFiles = []
|
||||
if hostOS == OS_WIN:
|
||||
delFiles = [
|
||||
"Qt5DBus.dll",
|
||||
"Qt5Network.dll",
|
||||
"Qt5Qml.dll",
|
||||
"Qt5QmlModels.dll",
|
||||
"Qt5Quick.dll",
|
||||
"Qt5Quick3D.dll",
|
||||
"Qt5Quick3DAssetImport.dll",
|
||||
"Qt5Quick3DRender.dll",
|
||||
"Qt5Quick3DRuntimeRender.dll",
|
||||
"Qt5Quick3DUtils.dll",
|
||||
"Qt5Sql.dll"
|
||||
]
|
||||
elif hostOS == OS_LINUX:
|
||||
delFiles = [
|
||||
"libQt5DBus.so.5",
|
||||
"libQt5Network.so.5",
|
||||
"libQt5Qml.so.5",
|
||||
"libQt5QmlModels.so.5",
|
||||
"libQt5Quick.so.5",
|
||||
"libQt5Quick3D.so.5",
|
||||
"libQt5Quick3DAssetImport.so.5",
|
||||
"libQt5Quick3DRender.so.5",
|
||||
"libQt5Quick3DRuntimeRender.so.5",
|
||||
"libQt5Quick3DUtils.so.5",
|
||||
"libQt5Sql.so.5"
|
||||
]
|
||||
distDir = os.path.join(os.getcwd(), "dist", "novelWriter")
|
||||
for delFile in delFiles:
|
||||
delPath = os.path.join(distDir, delFile)
|
||||
if os.path.isfile(delPath):
|
||||
print("Deleting file: %s" % delPath)
|
||||
os.unlink(delPath)
|
||||
|
||||
print("")
|
||||
print("Build Finished")
|
||||
print("")
|
||||
print("The novelWriter executable should be in the folder named 'dist'")
|
||||
print("")
|
||||
|
||||
return
|
||||
|
||||
# =============================================================================================== #
|
||||
# General Installers
|
||||
# =============================================================================================== #
|
||||
|
||||
##
|
||||
# XDG Installation (xdg-install, launcher)
|
||||
##
|
||||
|
||||
def xdgInstall():
|
||||
"""Will attempt to install icons and make a launcher.
|
||||
"""
|
||||
@@ -278,12 +658,51 @@ def xdgInstall():
|
||||
return
|
||||
|
||||
# =============================================================================================== #
|
||||
# Process Jobs
|
||||
# Windows Installers
|
||||
# =============================================================================================== #
|
||||
|
||||
##
|
||||
# Inno Setup Builder (setup-exe, setup-pyz)
|
||||
##
|
||||
|
||||
def innoSetup(setupType):
|
||||
"""Run the Inno Setup tool to build a setup.exe file for Windows based on either a pyinstaller
|
||||
freeze package (exe) or a zipapp package (pyz).
|
||||
"""
|
||||
print("")
|
||||
print("Running Inno Setup")
|
||||
print("##################")
|
||||
print("")
|
||||
|
||||
# Read the iss template
|
||||
issData = ""
|
||||
with open(os.path.join("setup", "win_setup_%s.iss" % setupType), mode="r") as inFile:
|
||||
issData = inFile.read()
|
||||
|
||||
import nw # noqa: E402
|
||||
issData = issData.replace(r"%%version%%", nw.__version__)
|
||||
issData = issData.replace(r"%%dir%%", os.getcwd())
|
||||
|
||||
with open("setup.iss", mode="w+") as outFile:
|
||||
outFile.write(issData)
|
||||
|
||||
try:
|
||||
subprocess.call(["iscc", "setup.iss"])
|
||||
except Exception as e:
|
||||
print("Inno Setup failed with error:")
|
||||
print(str(e))
|
||||
sys.exit(1)
|
||||
|
||||
return
|
||||
|
||||
# =============================================================================================== #
|
||||
# Process Command Line
|
||||
# =============================================================================================== #
|
||||
|
||||
if __name__ == "__main__":
|
||||
"""Parse command line options and run the commands.
|
||||
"""
|
||||
|
||||
# Detect OS
|
||||
if sys.platform.startswith("linux"):
|
||||
hostOS = OS_LINUX
|
||||
@@ -300,30 +719,75 @@ if __name__ == "__main__":
|
||||
"\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"
|
||||
"This tool provides setup and build commands for installing or distibuting novelWriter\n"
|
||||
"as a package on Linux, Mac and Windows. The available options are as follows:\n"
|
||||
"\n"
|
||||
"General:\n"
|
||||
"\n"
|
||||
" help Print the help message.\n"
|
||||
" pip Install all package dependencies for novelWriter using pip.\n"
|
||||
" clean Will attempt to delete the 'build' and 'dist' folders.\n"
|
||||
"\n"
|
||||
"Additional Builds:\n"
|
||||
"\n"
|
||||
" qthelp Build the help documentation for use with the Qt Assistant. Run before\n"
|
||||
" install to have local help enable in the the installed version.\n"
|
||||
" sample Build the sample project as a zip file. Run before install to enable\n"
|
||||
" creating sample projects in the in-app New Project Wizard.\n"
|
||||
"\n"
|
||||
"Python Packaging:\n"
|
||||
"\n"
|
||||
" pack-pyz Creates a pyz package in a folder with all dependencies using the\n"
|
||||
" zipapp tool. On Windows, python embeddable is added to the folder.\n"
|
||||
" freeze Freeze the package and produces a folder with all dependencies using\n"
|
||||
" the pyinstaller tool. This option is not designed for a specific OS.\n"
|
||||
" onefile Build a standalone executable with all dependencies bundled using the\n"
|
||||
" pyinstaller tool. Implies 'freeze', cannot be used with 'setup-exe'.\n"
|
||||
"\n"
|
||||
"General Installers:\n"
|
||||
"\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"
|
||||
"\n"
|
||||
"Windows Installers:\n"
|
||||
"\n"
|
||||
" setup-exe Build a Windows installer from a pyinstaller freeze package using Inno\n"
|
||||
" Setup. This option automatically disables 'onefile'.\n"
|
||||
" setup-pyz Build a Windows installer from a zipapp package using Inno Setup.\n"
|
||||
)
|
||||
|
||||
# Flags and Variables
|
||||
buildWindowed = True
|
||||
oneFile = False
|
||||
makeSetupExe = False
|
||||
makeSetupPyz = False
|
||||
doFreeze = False
|
||||
simplePack = False
|
||||
embedPython = False
|
||||
|
||||
# General
|
||||
# =======
|
||||
|
||||
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 "pip" in sys.argv:
|
||||
sys.argv.remove("pip")
|
||||
installPackages(hostOS)
|
||||
|
||||
if "clean" in sys.argv:
|
||||
sys.argv.remove("clean")
|
||||
cleanInstall()
|
||||
|
||||
# Additional Builds
|
||||
# =================
|
||||
|
||||
if "qthelp" in sys.argv:
|
||||
sys.argv.remove("qthelp")
|
||||
@@ -333,14 +797,79 @@ if __name__ == "__main__":
|
||||
sys.argv.remove("sample")
|
||||
buildSampleZip()
|
||||
|
||||
# Python Packaging
|
||||
# ================
|
||||
|
||||
if "pack-pyz" in sys.argv:
|
||||
sys.argv.remove("pack-pyz")
|
||||
simplePack = True
|
||||
if hostOS == OS_WIN:
|
||||
embedPython = True
|
||||
|
||||
if "freeze" in sys.argv:
|
||||
sys.argv.remove("freeze")
|
||||
doFreeze = True
|
||||
|
||||
if "onefile" in sys.argv:
|
||||
sys.argv.remove("onefile")
|
||||
doFreeze = True
|
||||
oneFile = True
|
||||
|
||||
# General Installers
|
||||
# ==================
|
||||
|
||||
if "launcher" in sys.argv:
|
||||
sys.argv.remove("launcher")
|
||||
print("The 'launcher' command has been replaced by 'xdg-install'.")
|
||||
sys.exit(1)
|
||||
|
||||
if "xdg-install" in sys.argv:
|
||||
sys.argv.remove("xdg-install")
|
||||
if hostOS == OS_WIN:
|
||||
print("ERROR: xdg-install cannot be used on Windows")
|
||||
print("ERROR: Command 'xdg-install' cannot be used on Windows")
|
||||
sys.exit(1)
|
||||
else:
|
||||
xdgInstall()
|
||||
|
||||
# Windows Installers
|
||||
# ==================
|
||||
|
||||
if "setup-exe" in sys.argv:
|
||||
sys.argv.remove("setup-exe")
|
||||
if hostOS == OS_WIN:
|
||||
oneFile = False
|
||||
makeSetupExe = True
|
||||
makeSetupPyz = False
|
||||
else:
|
||||
print("Error: Command 'setup-exe' for Inno Setup is Windows only.")
|
||||
sys.exit(1)
|
||||
|
||||
if "setup-pyz" in sys.argv:
|
||||
sys.argv.remove("setup-pyz")
|
||||
if hostOS == OS_WIN:
|
||||
makeSetupExe = False
|
||||
makeSetupPyz = True
|
||||
else:
|
||||
print("Error: Command 'setup-pyz' for Inno Setup is Windows only.")
|
||||
sys.exit(1)
|
||||
|
||||
# Actions
|
||||
# =======
|
||||
# For functions that are controlled by multiple flags, or need to be
|
||||
# run in a specific order.
|
||||
|
||||
if simplePack:
|
||||
makeSimplePackage(embedPython)
|
||||
|
||||
if doFreeze:
|
||||
freezePackage(buildWindowed, oneFile, makeSetupExe, hostOS)
|
||||
|
||||
if makeSetupExe:
|
||||
innoSetup("exe")
|
||||
|
||||
if makeSetupPyz:
|
||||
innoSetup("pyz")
|
||||
|
||||
if len(sys.argv) <= 1:
|
||||
# Nothing more to do
|
||||
sys.exit(0)
|
||||
|
||||
+37
-33
@@ -5,44 +5,48 @@ The root folder of the repository contains two scripts for setup and install:
|
||||
|
||||
## Script `setup.py`
|
||||
|
||||
The `setup.py` is a standard Python setup script with a couple of additional options:
|
||||
The `setup.py` is a standard Python setup script with a couple of additional
|
||||
options:
|
||||
|
||||
* `qthelp`: Will attempt to build a single file QtAssistand documentation file.
|
||||
This requires the Qt tools to be installed on the local system, as well as the sphinx build tools
|
||||
for the documentation.
|
||||
* `sample`: Will create a `sample.zip` file in the `nw/assets` folder.
|
||||
This is the file the New Project Wizard uses to generate an example project.
|
||||
If novelWriter is run from source, this file is not needed.
|
||||
* `xdg-install`: Will install novelWriter icons, mimetype, and desktop and menu launcher on Linux desktops.
|
||||
the application. This should work on standard Linux desktops.
|
||||
By default, this is installed for the current user. Run with `sudo` to install system-wide.
|
||||
### General
|
||||
|
||||
To install novelWriter as a local Python package, run:
|
||||
```bash
|
||||
sudo python setup.py install
|
||||
```
|
||||
`help` – Print the help message
|
||||
|
||||
## Script `make.py`
|
||||
`pip` – Install all package dependencies for novelWriter using pip.
|
||||
|
||||
The `make.py` script provides a number of convenient options for building packages if novelWriter.
|
||||
`clean` – Will attempt to delete the `build` and `dist` folders.
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
python make.py [command]
|
||||
```
|
||||
### Additional Builds
|
||||
|
||||
It currently accept the following commands:
|
||||
`qthelp` – Build the help documentation for use with the Qt Assistant. Run
|
||||
before install to have local help enable in the the installed version
|
||||
|
||||
* `help`: Print the help message.
|
||||
* `freeze`: Freeze the package and produces a folder of all dependencies using pyinstaller.
|
||||
* `onefile`: Build a standalone executable with all dependencies bundled.
|
||||
Implies `freeze`, cannot be used with `setup`.
|
||||
* `pip`: Run pip to install all package dependencies for novelWriter and this build tool.
|
||||
* `setup`: Build a setup.exe installer for Windows.
|
||||
This option automaticall disables the `onefile` option.
|
||||
* `clean`: This will attempt to delete the `build` and `dist` folders in the current folder.
|
||||
`sample` – Build the sample project as a zip file. Run before install to enable
|
||||
creating sample projects in the in-app New Project Wizard.
|
||||
|
||||
For instance, to create a Windows installer, run:
|
||||
```bash
|
||||
python make.py freeze setup
|
||||
```
|
||||
### Python Packaging
|
||||
|
||||
`pack-pyz` – Creates a pyz package in a folder with all dependencies using the
|
||||
zipapp tool. This option is intended for Windows deployment.
|
||||
|
||||
`freeze` – Freeze the package and produces a folder with all dependencies using
|
||||
the pyinstaller tool. This option is not designed for a specific OS.
|
||||
|
||||
`onefile` – Build a standalone executable with all dependencies bundled using
|
||||
the pyinstaller tool. Implies `freeze`, cannot be used with `setup-exe`
|
||||
|
||||
### General Installers
|
||||
|
||||
`install` – Installs novelWriter to the system's Python install location. Run
|
||||
as root or with sudo for system-wide install, or as user for single user
|
||||
install.
|
||||
|
||||
`xdg-install` – Install launcher and icons for freedesktop systems. Run as root
|
||||
or with sudo for system-wide install, or as user for single user install.
|
||||
|
||||
### Windows Installers
|
||||
|
||||
`setup-exe` – Build a Windows installer from a pyinstaller freeze package using
|
||||
Inno Setup. This option automatically disables `onefile`.
|
||||
|
||||
`setup-pyz` – Build a Windows installer from a zipapp package using Inno Setup.
|
||||
|
||||
@@ -47,7 +47,6 @@ Source: "{#nwAppDir}\novelWriter\*"; DestDir: "{app}"; Flags: ignoreversion recu
|
||||
[Icons]
|
||||
Name: "{autoprograms}\{#nwAppName}"; Filename: "{app}\{#nwAppExeName}"
|
||||
Name: "{autodesktop}\{#nwAppName}"; Filename: "{app}\{#nwAppExeName}"; Tasks: desktopicon
|
||||
Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\{#nwAppName}"; Filename: "{app}\{#nwAppExeName}"; Tasks: quicklaunchicon
|
||||
|
||||
[Run]
|
||||
Filename: "{app}\{#nwAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(nwAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
|
||||
@@ -56,5 +55,5 @@ Filename: "{app}\{#nwAppExeName}"; Description: "{cm:LaunchProgram,{#StringChang
|
||||
Root: HKA; Subkey: "Software\Classes\.nwx\OpenWithProgids"; ValueType: string; ValueName: "novelWriterProject.nwx"; ValueData: ""; Flags: uninsdeletevalue
|
||||
Root: HKA; Subkey: "Software\Classes\novelWriterProject.nwx"; ValueType: string; ValueName: ""; ValueData: "novelWriter Project File"; Flags: uninsdeletekey
|
||||
Root: HKA; Subkey: "Software\Classes\novelWriterProject.nwx\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\assets\icons\x-novelwriter-project.ico"
|
||||
Root: HKA; Subkey: "Software\Classes\novelWriterProject.nwx\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\novelWriter.exe"" ""%1"""
|
||||
Root: HKA; Subkey: "Software\Classes\Applications\novelWriter.exe\SupportedTypes"; ValueType: string; ValueName: ".nwx"; ValueData: ""
|
||||
Root: HKA; Subkey: "Software\Classes\novelWriterProject.nwx\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#nwAppExeName}"" ""%1"""
|
||||
Root: HKA; Subkey: "Software\Classes\Applications\{#nwAppExeName}\SupportedTypes"; ValueType: string; ValueName: ".nwx"; ValueData: ""
|
||||
@@ -0,0 +1,59 @@
|
||||
; Script generated by the Inno Setup Script Wizard.
|
||||
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
|
||||
|
||||
#define nwAppDir "%%dir%%\dist"
|
||||
#define nwAppName "novelWriter"
|
||||
#define nwAppVersion "%%version%%"
|
||||
#define nwAppPublisher "novelWriter"
|
||||
#define nwAppURL "http://novelWriter.io"
|
||||
#define nwAppExeName "novelWriter.pyz"
|
||||
|
||||
[Setup]
|
||||
; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications.
|
||||
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
|
||||
AppId={{459A75D0-951F-4932-9809-6002EC8E733E}
|
||||
AppName={#nwAppName}
|
||||
AppVersion={#nwAppVersion}
|
||||
AppVerName={#nwAppName} {#nwAppVersion}
|
||||
AppPublisher={#nwAppPublisher}
|
||||
AppPublisherURL={#nwAppURL}
|
||||
AppSupportURL={#nwAppURL}
|
||||
AppUpdatesURL={#nwAppURL}
|
||||
DefaultDirName={autopf}\{#nwAppName}
|
||||
DisableProgramGroupPage=yes
|
||||
; The [Icons] "quicklaunchicon" entry uses {userappdata} but its [Tasks] entry has a proper IsAdminInstallMode Check.
|
||||
UsedUserAreasWarning=no
|
||||
; Uncomment the following line to run in non administrative install mode (install for current user only.)
|
||||
;PrivilegesRequired=lowest
|
||||
PrivilegesRequiredOverridesAllowed=dialog
|
||||
OutputDir={#nwAppDir}
|
||||
OutputBaseFilename=novelwriter-{#nwAppVersion}-win10-amd64-setup
|
||||
Compression=lzma
|
||||
SolidCompression=yes
|
||||
WizardStyle=modern
|
||||
ArchitecturesInstallIn64BitMode=x64
|
||||
ChangesAssociations=yes
|
||||
|
||||
[Languages]
|
||||
Name: "english"; MessagesFile: "compiler:Default.isl"
|
||||
|
||||
[Tasks]
|
||||
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
|
||||
Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked; OnlyBelowVersion: 6.1; Check: not IsAdminInstallMode
|
||||
|
||||
[Files]
|
||||
Source: "{#nwAppDir}\novelWriter\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||
|
||||
[Icons]
|
||||
Name: "{autoprograms}\{#nwAppName}"; Filename: "{app}\pythonw.exe"; Parameters: "{#nwAppExeName}"; IconFilename: "{app}\novelwriter.ico"
|
||||
Name: "{autodesktop}\{#nwAppName}"; Filename: "{app}\pythonw.exe"; Parameters: "{#nwAppExeName}"; IconFilename: "{app}\novelwriter.ico"; Tasks: desktopicon;
|
||||
|
||||
[Run]
|
||||
Filename: "{app}\pythonw.exe"; Parameters: "{#nwAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(nwAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
|
||||
|
||||
[Registry]
|
||||
Root: HKA; Subkey: "Software\Classes\.nwx\OpenWithProgids"; ValueType: string; ValueName: "novelWriterProject.nwx"; ValueData: ""; Flags: uninsdeletevalue
|
||||
Root: HKA; Subkey: "Software\Classes\novelWriterProject.nwx"; ValueType: string; ValueName: ""; ValueData: "novelWriter Project File"; Flags: uninsdeletekey
|
||||
Root: HKA; Subkey: "Software\Classes\novelWriterProject.nwx\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\assets\icons\x-novelwriter-project.ico"
|
||||
Root: HKA; Subkey: "Software\Classes\novelWriterProject.nwx\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\pythonw.exe"" ""{app}\{#nwAppExeName}"" ""%1"""
|
||||
Root: HKA; Subkey: "Software\Classes\Applications\{#nwAppExeName}\SupportedTypes"; ValueType: string; ValueName: ".nwx"; ValueData: ""
|
||||
Reference in New Issue
Block a user