1#!/usr/bin/env python 2# create-win-installer.py 3# 4# This file is part of Charm, a task-based time tracking application. 5# 6# Copyright (C) 2016-2018 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com 7# 8# Author: Hannah von Reth <hannah.vonreth@kdab.com> 9# 10# This program is free software; you can redistribute it and/or modify 11# it under the terms of the GNU General Public License as published by 12# the Free Software Foundation, either version 2 of the License, or 13# (at your option) any later version. 14# 15# This program is distributed in the hope that it will be useful, 16# but WITHOUT ANY WARRANTY; without even the implied warranty of 17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18# GNU General Public License for more details. 19# 20# You should have received a copy of the GNU General Public License 21# along with this program. If not, see <http://www.gnu.org/licenses/>. 22 23from __future__ import print_function 24 25import os 26import subprocess 27import sys 28import shutil 29import argparse 30import glob 31 32class DeployHelper(object): 33 def __init__(self, args): 34 self.args = args 35 self.gitDir = os.path.realpath(os.path.join(os.path.dirname(__file__), "..")) 36 37 self.deployImage = os.path.realpath("./deployImage") 38 if not os.path.exists(self.args.buildDir): 39 self._die("Build dir %s does not exist" % self.args.buildDir) 40 41 42 def _die(self, message): 43 print(message, file=sys.stderr) 44 exit(1) 45 46 def _logExec(self, command): 47 print(command) 48 sys.stdout.flush() 49 subprocess.check_call(command, shell=True) 50 51 def _copyToImage(self, src, destDir = None, deploy = True): 52 if destDir is None: 53 destDir = self.deployImage 54 print("Copy %s to %s" % (src, destDir)) 55 shutil.copy(src, destDir) 56 if deploy: 57 self._logExec("windeployqt --%s --dir \"%s\" --qmldir \"%s\" \"%s\"" % (self.args.buildType, self.deployImage, self.gitDir, src)) 58 59 def _sign(self, fileName): 60 if self.args.sign: 61 self._logExec("signtool.exe sign -t http://timestamp.globalsign.com/scripts/timestamp.dll -fd SHA256 -v \"%s\"" % fileName) 62 63 def cleanImage(self): 64 if os.path.exists(self.deployImage): 65 shutil.rmtree(self.deployImage) 66 os.makedirs(self.deployImage) 67 68 def _locateDll(self, dllName, fatal=True): 69 pathext = os.environ.get("PATHEXT") 70 os.environ["PATHEXT"] = ".dll" 71 found = shutil.which(dllName, path=os.pathsep.join([self.deployImage, os.environ.get("PATH")])) 72 os.environ["PATHEXT"] = pathext 73 if not found and fatal: 74 self._die("Unable to locate %s" % dllName) 75 return found 76 77 def deploy(self): 78 if self.args.deployDlls: 79 for dll in self.args.deployDlls.split(";"): 80 self._copyToImage(self._locateDll(dll)) 81 82 app = os.path.join(self.args.buildDir, self.args.applicationFileName) 83 shutil.copy(app, self.deployImage) 84 self._logExec("windeployqt --%s --compiler-runtime --dir \"%s\" --qmldir \"%s\" \"%s\"" % (self.args.buildType, self.deployImage, self.gitDir, app)) 85 86 for folder in self.args.pluginFolders.split(";"): 87 for f in glob.glob(os.path.join(self.args.buildDir, folder, "*.dll")): 88 self._copyToImage(f) 89 90 if self.args.deployOpenSSL: 91 foundSomething = False 92 for dll in ["libeay32.dll", "libssl32.dll", "ssleay32.dll" ]: 93 src = os.path.join(self.args.deployOpenSSL, dll ) 94 if not os.path.exists(src): 95 src = self._locateDll(dll, fatal=False) 96 if src: 97 foundSomething = True 98 print(src) 99 self._copyToImage(src, deploy=False) 100 if not foundSomething: 101 self._die("Failed to deploy openssl") 102 103 if self.args.sign: 104 for f in glob.glob(os.path.join(self.deployImage, "**/*.exe"), recursive=True): 105 self._sign(f) 106 for f in glob.glob(os.path.join(self.deployImage, "**/*.dll"), recursive=True): 107 self._sign(f) 108 109 def makeInstaller(self): 110 if self.args.architecture == "x64": 111 programDir = "$PROGRAMFILES64" 112 else: 113 programDir = "$PROGRAMFILES" 114 115 defines = {} 116 defines["productName"] = self.args.productName 117 defines["companyName"] = self.args.companyName 118 defines["productVersion"] = self.args.productVersion 119 defines["setupname"] = self.args.installerName 120 defines["applicationName"] = os.path.basename(self.args.applicationFileName) 121 defines["applicationIcon"] = self.args.applicationIcon 122 defines["programFilesDir"] = programDir 123 defines["deployDir"] = self.deployImage 124 defines["productLicence"] = "" if not self.args.productLicence else "!insertmacro MUI_PAGE_LICENSE \"%s\"" % self.args.productLicence 125 126 redist = "vcredist_%s.exe" % self.args.architecture 127 if os.path.exists(os.path.join(self.deployImage, redist)): 128 defines["vcredist"] = "vcredist_%s.exe" % self.args.architecture 129 else: 130 defines["vcredist"] = "none" 131 132 133 definestring = "" 134 for key in defines: 135 definestring += " /D%s=\"%s\"" % (key, defines[key]) 136 137 command = "makensis /NOCD %s %s" %\ 138 (definestring, os.path.join(os.path.dirname(__file__), "NullsoftInstaller.nsi")) 139 self._logExec(command) 140 installer = os.path.realpath(self.args.installerName) 141 self._sign(installer) 142 print("""Generated package file: %s """ % installer) 143 144 145if __name__ == "__main__": 146 parser = argparse.ArgumentParser() 147 parser.add_argument("--architecture", action = "store", default="x64" ) 148 parser.add_argument("--buildType", action = "store", default="release" ) 149 parser.add_argument("--installerName", action = "store" ) 150 parser.add_argument("--applicationFileName", action = "store" ) 151 parser.add_argument("--applicationIcon", action = "store" ) 152 parser.add_argument("--buildDir", action = "store" ) 153 parser.add_argument("--pluginFolders", action = "store", default="" ) 154 parser.add_argument("--productName", action = "store" ) 155 parser.add_argument("--companyName", action = "store" ) 156 parser.add_argument("--productVersion", action = "store" ) 157 parser.add_argument("--productLicence", action = "store" ) 158 parser.add_argument("--deployDlls", action = "store" ) 159 parser.add_argument("--deployOpenSSL", action = "store" ) 160 161 parser.add_argument("--sign", action = "store_true", default=False) 162 163 164 args = parser.parse_args() 165 166 helper = DeployHelper(args) 167 helper.cleanImage() 168 helper.deploy() 169 helper.makeInstaller() 170