Files
novelWriter/make.py
T
2020-10-17 15:05:58 +02:00

324 lines
9.9 KiB
Python
Executable File

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
This script will either build:
* A single file executable named dist/novelWriter.exe. This is a quite
slow option, and the file is fairly big. Option --onefile
* A single directory named dist/novelWriter with a novelWriter.exe, and
all dependecies included. This is the default.
* The latter can be combined with a build stage of a setup.exe file
named setup-novelwriter-<version>.exe. Option --setup.
In addition, providing the --pip flag 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():
print("")
print("Installing Dependencies")
print("#######################")
print("")
try:
subprocess.call([
sys.executable, "-m",
"pip", "install", "--user", "--upgrade", "pip"
])
subprocess.call([
sys.executable, "-m",
"pip", "install", "--user", "--upgrade", "pyinstaller"
])
subprocess.call([
sys.executable, "-m",
"pip", "install", "--user", "--upgrade", "-r", "requirements.txt"
])
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 = ":"
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",
]
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"
"freeze Freeze the package and produces a folder of all\n"
" dependecies 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()
sys.exit(0)
if "pip" in sys.argv:
sys.argv.remove("pip")
installPackages()
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