Rewritten make_windows.py script

This commit is contained in:
Veronica K. B. Olsen
2020-10-17 13:56:27 +02:00
parent 3b63dd49dc
commit f0adc59d4c
+217 -101
View File
@@ -16,72 +16,22 @@ novelWriter itself.
import os import os
import sys import sys
import getopt import shutil
import subprocess import subprocess
if not sys.platform.startswith("win32"): OS_NONE = 0
print("ERROR: This script is intended for Windows only.") OS_LINUX = 1
sys.exit(1) OS_WIN = 2
OS_DARWIN = 3
# Defaults # =============================================================================================== #
buildWindowed = True # Package Installer
runPip = False # =============================================================================================== #
oneFile = False
makeSetup = False
innoSetup = None
# Parse Options def installPackages():
shortOpt = "hd"
longOpt = [
"help",
"debug",
"pip",
"onefile",
"setup",
"inno=",
]
helpMsg = (
"\n"
"novelWriter Install Script for Windows\n"
"\n"
"Usage:\n"
" -h, --help Print this message.\n"
" --pip Install dependecies first.\n"
" --onefile Create a single executable file.\n"
" --setup Make Inno Setup file.\n"
" --inno= Path to the Inno Setup exec.\n"
" -d, --debug Build novelWriter for debugging. To debug novelwriter after build,\n"
" run it from command line with the debug options. Please check the\n"
" novelWriter --help output for details.\n"
)
try:
inOpts, inArgs = getopt.getopt(sys.argv[1:], shortOpt, longOpt)
except getopt.GetoptError:
print(helpMsg)
sys.exit(1)
for inOpt, inArg in inOpts:
if inOpt in ("-h", "--help"):
print(helpMsg)
sys.exit(0)
elif inOpt in ("-d", "--debug"):
buildWindowed = False
elif inOpt == "--pip":
runPip = True
elif inOpt == "--onefile":
oneFile = True
elif inOpt == "--setup":
makeSetup = True
elif inOpt == "--inno":
innoSetup = inArg
# Run pip
if runPip:
print("") print("")
print("###########################") print("Installing Dependencies")
print(" Installing Dependencies") print("#######################")
print("###########################")
print("") print("")
try: try:
subprocess.call([ subprocess.call([
@@ -101,16 +51,32 @@ if runPip:
print(str(e)) print(str(e))
sys.exit(1) sys.exit(1)
# Run pyinstaller return
print("")
print("#######################") # =============================================================================================== #
print(" Running PyInstaller") # Run PyInstaller on Package
print("#######################") # =============================================================================================== #
print("")
instOpt = [ 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 = ":"
instOpt = [
"--name=novelWriter", "--name=novelWriter",
"--clean", "--clean",
"--add-data=%s;%s" % (os.path.join("nw", "assets"), "assets"), "--add-data=%s%s%s" % (os.path.join("nw", "assets"), dotDot, "assets"),
"--icon=%s" % os.path.join("nw", "assets", "icons", "novelwriter.ico"), "--icon=%s" % os.path.join("nw", "assets", "icons", "novelwriter.ico"),
"--exclude-module=PyQt5.QtQml", "--exclude-module=PyQt5.QtQml",
"--exclude-module=PyQt5.QtBluetooth", "--exclude-module=PyQt5.QtBluetooth",
@@ -126,61 +92,90 @@ instOpt = [
"--exclude-module=PyQt5.QtSensors", "--exclude-module=PyQt5.QtSensors",
"--exclude-module=PyQt5.QtSerialPort", "--exclude-module=PyQt5.QtSerialPort",
"--exclude-module=PyQt5.QtSql", "--exclude-module=PyQt5.QtSql",
] ]
if buildWindowed: if buildWindowed:
instOpt.append("--windowed") instOpt.append("--windowed")
if oneFile and not makeSetup: if oneFile and not makeSetup:
instOpt.append("--onefile") instOpt.append("--onefile")
else: else:
instOpt.append("--onedir") instOpt.append("--onedir")
instOpt.append("novelWriter.py") instOpt.append("novelWriter.py")
# Make sample.zip first # Make sample.zip first
print("Building sample.zip") try:
try:
subprocess.call([sys.executable, "setup.py", "sample"]) subprocess.call([sys.executable, "setup.py", "sample"])
except Exception as e: except Exception as e:
print("Failed with error:") print("Failed with error:")
print(str(e)) print(str(e))
sys.exit(1) sys.exit(1)
import PyInstaller.__main__ # noqa: E402 PyInstaller.__main__.run(instOpt)
PyInstaller.__main__.run(instOpt)
if not oneFile: if not oneFile:
# These dll files are not nee3ded, and take up a fair bit of space. # These files are not needed, and take up a fair bit of space.
delIfExists = [ delFiles = []
"Qt5DBus.dll", "Qt5Network.dll", "Qt5Qml.dll", "Qt5QmlModels.dll", "Qt5Quick.dll", if hostOS == OS_WIN:
"Qt5Quick3D.dll", "Qt5Quick3DAssetImport.dll", "Qt5Quick3DRender.dll", delFiles = [
"Qt5Quick3DRuntimeRender.dll", "Qt5Quick3DUtils.dll", "Qt5Sql.dll" "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") distDir = os.path.join(os.getcwd(), "dist", "novelWriter")
for delFile in delIfExists: for delFile in delFiles:
delPath = os.path.join(distDir, delFile) delPath = os.path.join(distDir, delFile)
if os.path.isfile(delPath): if os.path.isfile(delPath):
print("Deleting file: %s" % delPath) print("Deleting file: %s" % delPath)
os.unlink(delPath) os.unlink(delPath)
print("") print("")
print("Build Finished") print("Build Finished")
print("") print("")
print("If everything went well, the novelWriter executable should be in the folder named 'dist'") print("The novelWriter executable should be in the folder named 'dist'")
print("") print("")
if makeSetup: return
# =============================================================================================== #
# Inno Setup Builder
# =============================================================================================== #
def innoSetup(innoExec):
"""Run the Inno Setup tool to build a setup.exe file for Windows.
"""
print("") print("")
print("######################") print("Running Inno Setup")
print(" Running Inno Setup") print("##################")
print("######################")
print("") print("")
if innoSetup is None: if innoExec is None:
innoSetup = "C:\\Program Files (x86)\\Inno Setup 6\\ISCC.exe" innoExec = "C:\\Program Files (x86)\\Inno Setup 6\\ISCC.exe"
if not os.path.isfile(innoSetup): if not os.path.isfile(innoExec):
print("ERROR: Cannot fine Inno Setup's ISCC.exe file.") print("ERROR: Cannot fine Inno Setup's ISCC.exe file.")
print(" Looked in: %s" % innoSetup) print(" Looked in: %s" % innoExec)
print(" Please provide a path with the --inno= option.") print(" Please provide a path with the --inno= option.")
sys.exit(1) sys.exit(1)
@@ -197,8 +192,129 @@ if makeSetup:
outFile.write(issData) outFile.write(issData)
try: try:
subprocess.call([innoSetup, "setup.iss"]) subprocess.call([innoExec, "setup.iss"])
except Exception as e: except Exception as e:
print("Failed with error:") print("Failed with error:")
print(str(e)) print(str(e))
sys.exit(1) 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
innoExec = None
if "help" in sys.argv:
print(
"\n"
"novelWriter Make Tool\n"
"=====================\n"
"This tool provides build commands for distibuting novelWriter as\n"
"a package. The available options are as follows:\n"
"\n"
"pip Run pip to install all package dependencies for\n"
" novelWriter and this build tool.\n"
"onefile Build a standalone executable with all dependencies\n"
" bundled. This does not produce a setup.exe on Windows.\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"
)
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()
sys.exit(0)
if "pip" in sys.argv:
sys.argv.remove("pip")
installPackages()
if "onefile" in sys.argv:
sys.argv.remove("onefile")
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)
freezePackage(buildWindowed, oneFile, makeSetup, hostOS)
if makeSetup:
innoSetup(innoExec)
# END Main