raptorinstallermaker.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. # Copyright (c) 2009-2011 Nokia Corporation and/or its subsidiary(-ies).
  2. # All rights reserved.
  3. # This component and the accompanying materials are made available
  4. # under the terms of the License "Eclipse Public License v1.0"
  5. # which accompanies this distribution, and is available
  6. # at the URL "http://www.eclipse.org/legal/epl-v10.html".
  7. #
  8. # Initial Contributors:
  9. # Nokia Corporation - initial contribution.
  10. #
  11. # Contributors:
  12. #
  13. # Description:
  14. # Raptor installer maker script - generates a Windows installer for Raptor using
  15. # the NSIS package in the accompanying directory. Works on Windows and Linux.
  16. import optparse
  17. import os
  18. import os.path
  19. import re
  20. import shutil
  21. import stat
  22. import subprocess
  23. import sys
  24. import tempfile
  25. import unzip
  26. import zipfile
  27. tempdir = ""
  28. makensis_success = None
  29. zipfile_success = None
  30. def generateinstallerversion(sbshome = None):
  31. shellenv = os.environ.copy()
  32. shellenv["PYTHONPATH"] = os.path.join(sbshome)
  33. raptorversioncommand = "python -c \"import raptor.version; print(raptor.version.numericversion())\""
  34. # Raptor version is obtained from raptor.version module's numericversion function.
  35. sbs_version_matcher = re.compile(".*(\d+\.\d+\.\d+).*", re.I)
  36. # Create Raptor subprocess
  37. versioncommand = subprocess.Popen(raptorversioncommand, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=shellenv)
  38. raptorversion = ""
  39. # Get all the lines matching the RE
  40. for line in versioncommand.stdout.readlines():
  41. res = sbs_version_matcher.match(line)
  42. if res:
  43. raptorversion = res.group(1)
  44. print("Successfully determined Raptor version {0}".format(raptorversion))
  45. versioncommand.wait() # Wait for process to end
  46. return raptorversion
  47. def unzipnsis(pathtozip):
  48. global tempdir
  49. tempdir = tempfile.mkdtemp()
  50. un = unzip.unzip()
  51. print("Unzipping NSIS to {0}...".format(tempdir))
  52. un.extract(pathtozip, tempdir)
  53. print("Done.")
  54. # Ensure the correct executable is called
  55. dotexe=""
  56. if "win" in sys.platform.lower():
  57. dotexe=".exe"
  58. makensispath = os.path.join(tempdir, "NSIS", "makensis" + dotexe)
  59. if not "win" in sys.platform.lower():
  60. os.chmod(makensispath, stat.S_IRWXU)
  61. return makensispath
  62. def runmakensis(nsiscommand):
  63. # Create makensis subprocess
  64. print("Running NSIS command\n{0}".format(nsiscommand))
  65. makensis = subprocess.Popen(nsiscommand, shell=True)
  66. makensis.wait() # Wait for process to end
  67. return makensis.returncode
  68. def cleanup():
  69. """ Clean up tempdir """
  70. global tempdir
  71. print("Cleaning up temporary directory {0}".format(tempdir))
  72. shutil.rmtree(tempdir,True)
  73. print("Done.")
  74. def writeLicense(win32supportdirs):
  75. """ Create the license file from the raptor license, plus the NSIS
  76. license, plus the license for the tools we're using from the
  77. win32 support folders
  78. Returns the file object and the file name as a tuple"""
  79. licensetxt = tempfile.mkstemp()
  80. (licensetxtfile, licensetxtname) = licensetxt # Decode the tuple
  81. licensetxtfile = os.fdopen(licensetxtfile,"w") # Reopen as a writeable file object
  82. raptorlicense = os.path.join(options.sbshome,"license.txt")
  83. if os.path.exists(raptorlicense):
  84. with open(raptorlicense,"r") as f:
  85. shutil.copyfileobj(f,licensetxtfile)
  86. nsisdir = os.path.join(options.sbshome,"util","install-windows")
  87. nsisnotices = os.path.join(nsisdir,"notices.txt")
  88. if os.path.exists(nsisnotices):
  89. print("Using notices.txt from {0}".format(nsisdir))
  90. licensetxtfile.write("\n---\n\n")
  91. with open(nsisnotices,"r") as f:
  92. shutil.copyfileobj(f,licensetxtfile)
  93. for directory in win32supportdirs:
  94. dir = win32supportdirs[directory]
  95. # Check for a notices.txt file
  96. noticesfile = os.path.join(dir,"notices.txt")
  97. if os.path.exists(noticesfile):
  98. print("Using notices.txt from {0}".format(dir))
  99. licensetxtfile.write("\n---\n\n")
  100. with open(noticesfile,"r") as f:
  101. shutil.copyfileobj(f,licensetxtfile)
  102. licensetxtfile.close()
  103. return (licensetxtfile,licensetxtname) # (File object, filename)
  104. def __writeDirTreeToArchive(zip, dirlist, sbshome, win32supportdirs=False):
  105. """Auxilliary function to write all files in each directory tree of dirlist into the
  106. open archive "zip" assuming valid sbshome; destination path is tweaked for win32supportdirs,
  107. so set this to true when writing files into $SBS_HOME/win32"""
  108. for name in dirlist:
  109. if name == None:
  110. continue
  111. files = os.walk(os.path.join(sbshome, name))
  112. for dirtuple in files:
  113. filenames = dirtuple[2]
  114. dirname = dirtuple[0]
  115. for file in filenames:
  116. # Filter out unwanted files
  117. if not file.lower().endswith(".pyc") and \
  118. not file.lower().endswith(".project") and \
  119. not file.lower().endswith(".cproject") and \
  120. not file.lower().endswith(".pydevproject"):
  121. origin = os.path.join(dirname, file)
  122. # For the win32 support directories, the destination is different
  123. if win32supportdirs:
  124. destination = os.path.join("sbs", "win32", os.path.basename(name.rstrip(os.sep)),
  125. dirname.replace(name, "").strip(os.sep), file)
  126. else:
  127. destination = os.path.join("sbs", dirname.rstrip(os.sep).replace(sbshome, "").strip(os.sep), file)
  128. print("Compressing {0}\tto\t{1}".format(origin, destination))
  129. zip.write(origin, destination)
  130. def writeZip(filename, sbshome, sbsbvdir, sbscygwindir, sbsmingwdir, sbspythondir, license):
  131. """Write a zip archive with file name "filename" assuming SBS_HOME is sbshome, and
  132. that sbsbvdir, sbscygwindir, sbsmingwdir, sbspythondir are the win32 support directories."""
  133. # *Files* in the top level SBS_HOME directory
  134. sbshome_files = ["RELEASE-NOTES.html"]
  135. # Directories in SBS_HOME
  136. sbshome_dirs = ["bin", "examples", "lib", "notes", "python", "raptor",
  137. "schema", "style", os.sep.join(["win32", "bin"])]
  138. # Win32 support directories
  139. win32_dirs = [sbsbvdir, sbscygwindir, sbsmingwdir, sbspythondir]
  140. try:
  141. # Open the zip archive for writing; if a file with the same
  142. # name exists, it will be truncated to zero bytes before
  143. # writing commences
  144. zip = zipfile.ZipFile(filename, "w", zipfile.ZIP_DEFLATED)
  145. # Write the license file into the archive
  146. zip.write(license, os.path.join("sbs","license.txt"))
  147. # Write the files in the top-level of SBS_HOME into the archive
  148. for name in sbshome_files:
  149. origin = os.path.join(sbshome, name)
  150. destination = os.path.join("sbs", name)
  151. print("Compressing {0}\tto\t{1}".format(origin, destination))
  152. zip.write(origin, destination)
  153. # Write all files in the the directories in the top-level of SBS_HOME into the archive
  154. print("Reading the sbs directories...")
  155. __writeDirTreeToArchive(zip, sbshome_dirs, sbshome, win32supportdirs=False)
  156. print("Writing sbs directories to the archive is complete.")
  157. # Write all files in the the win32 support directories in the top-level of SBS_HOME into the archive
  158. print("Reading the win32 support directories")
  159. __writeDirTreeToArchive(zip, win32_dirs, sbshome, win32supportdirs=True)
  160. print("Writing win32 support directories to the archive is complete.")
  161. zip.close()
  162. print("Zipoutput: \"{0}\"".format(os.path.join(os.getcwd(), filename)))
  163. print("Zip file creation successful.")
  164. return 0
  165. except Exception, e:
  166. print("Error: failed to create zip file: {0}".format(str(e)))
  167. return 2
  168. if __name__ == "__main__":
  169. # Create CLI and parse it
  170. parser = optparse.OptionParser()
  171. win32_msg = "Can be a full/relatitve path; prefix with \"WIN32SUPPORT\\\" to be relative to the Win32 support directory. Omitting this value will assume a default to a path inside the Win32 support directory."
  172. parser.add_option("-s", "--sbs-home", dest="sbshome", help="Path to use as SBS_HOME environment variable. If not present the script exits.")
  173. parser.add_option("-w", "--win32-support", dest="win32support", help="Path to Win32 support directory. If not present the script exits.")
  174. parser.add_option("-b", "--bv", dest="bv", help="Path to Binary variation CPP \"root\" directory. " + win32_msg)
  175. parser.add_option("--nobv", dest="nobv", help="Do not include the binary variation CPP (ignores any --bv option).", action="store_true" , default=False)
  176. parser.add_option("-c", "--cygwin", dest="cygwin", help="Path to Cygwin \"root\" directory. " + win32_msg)
  177. parser.add_option("-m", "--mingw", dest="mingw", help="Path to MinGW \"root\" directory. " + win32_msg)
  178. parser.add_option("-p", "--python", dest="python", help="Path to Python \"root\" directory. " + win32_msg)
  179. version_msg = "This will be present in the Raptor installer's file name and the installer's pages."
  180. parser.add_option("--prefix", dest="versionprefix", type="string", default="", help="A string to use as a prefix to the Raptor version string. " + version_msg)
  181. parser.add_option("--postfix", dest="versionpostfix", type="string", default="", help="A string to use as a postfix to the Raptor version string. " + version_msg)
  182. parser.add_option("--noclean", dest="noclean", help="Do not clean up the temporary directory created during the run.", action="store_true" , default=False)
  183. parser.add_option("--noexe", dest="noexe", help="Do not create a Windows .exe installer of the Raptor installation.", action="store_true" , default=False)
  184. parser.add_option("--nozip", dest="nozip", help="Do not create a zip archive of the Raptor installation.", action="store_true" , default=False)
  185. (options, args) = parser.parse_args()
  186. # Required directories inside the win32-support directory (i.e. the win32-support repository).
  187. win32supportdirs = {"cygwin":"cygwin", "mingw":"mingw", "python":"python27"}
  188. if not options.nobv:
  189. win32supportdirs["bv"] = "bv"
  190. if options.sbshome == None:
  191. print("ERROR: no SBS_HOME passed in. Exiting...")
  192. sys.exit(2)
  193. elif not os.path.isdir(options.sbshome):
  194. print("ERROR: the specified SBS_HOME directory \"{0}\" does not exist. Cannot build installer. Exiting...".format(options.sbshome))
  195. sys.exit(2)
  196. if options.win32support == None:
  197. print("ERROR: no win32support directory specified. Unable to proceed. Exiting...")
  198. sys.exit(2)
  199. else:
  200. # Check for command line overrides to defaults
  201. for directory in win32supportdirs:
  202. print("Checking for location \"{0}\"...".format(directory))
  203. value = getattr(options, directory)
  204. if value != None: # Command line override
  205. if value.lower().startswith("win32support"):
  206. # Strip off "WIN32SUPPORT\" and join to Win32 support location
  207. win32supportdirs[directory] = os.path.join(options.win32support, value[13:])
  208. else:
  209. # Relative to current directory
  210. win32supportdirs[directory] = value
  211. print("\tUsing commandline override value: \"{0}\"".format(str(value)))
  212. else: # Use default location
  213. win32supportdirs[directory] = os.path.join(options.win32support, win32supportdirs[directory])
  214. print("\tDefaulting to: \"{0}\"".format(str(win32supportdirs[directory])))
  215. # Check that all the specified directories exist and exit if any of them is missing.
  216. for directory in win32supportdirs:
  217. dir = win32supportdirs[directory]
  218. if os.path.isdir(dir):
  219. print("Found directory {0}".format(dir))
  220. else:
  221. print("ERROR: directory {0} does not exist. Cannot build installer. Exiting...".format(dir))
  222. sys.exit(2)
  223. # For grabbing a copy of nsis:
  224. sys.path.append(options.sbshome)
  225. from raptor import urlget
  226. # Create the license file
  227. (licensetxtfile,licensetxtname) = writeLicense(win32supportdirs)
  228. raptorversion = options.versionprefix + generateinstallerversion(options.sbshome) + options.versionpostfix
  229. print("Using Raptor version {0} ...".format(raptorversion))
  230. if not options.noexe:
  231. got_zip = False
  232. nsis_zip = "NSIS.zip"
  233. try:
  234. s = os.stat(nsis_zip)
  235. got_zip = True
  236. except OSError,e:
  237. for url in [ "http://rene.europe.nokia.com/~raptorbot/files/NSIS.zip",
  238. "http://projects.developer.nokia.com/raptor/files/NSIS.zip" ]:
  239. try:
  240. print("Attempting to download {0} from {1}".format(nsis_zip,url))
  241. urlget.get_http(url,nsis_zip)
  242. got_zip = True
  243. print("Download ok")
  244. break # no need to download the next one
  245. except Exception,e:
  246. print("WARNING: couldn't get {0} from {1}: {2}".format(nsis_zip, url, str(e)))
  247. continue
  248. if not got_zip:
  249. print("ERROR: don't have {0} and couldn't download it".format(nsis_zip))
  250. sys.exit(3)
  251. makensispath = unzipnsis("." + os.sep + "NSIS.zip")
  252. command_string = "{makensis} -DRAPTOR_LOCATION={sbs_home} " \
  253. "{bvopt} -DCYGWIN_LOCATION={cygwin} " \
  254. "-DMINGW_LOCATION={mingw} -DPYTHON_LOCATION={python} " \
  255. "-DLICENSE_FILE={license} " \
  256. "-DRAPTOR_VERSION={sbs_version} {nsis_script}"
  257. if options.nobv:
  258. bvopt = ""
  259. else:
  260. bvopt = "-DBV_LOCATION={bv}".format(bv = win32supportdirs["bv"])
  261. nsiscommand = command_string.format(makensis = makensispath,
  262. sbs_home = options.sbshome,
  263. bvopt = bvopt,
  264. cygwin = win32supportdirs["cygwin"],
  265. mingw = win32supportdirs["mingw"],
  266. python = win32supportdirs["python"],
  267. license = licensetxtname,
  268. sbs_version = raptorversion,
  269. nsis_script = os.path.join(options.sbshome, "util", "install-windows", "raptorinstallerscript.nsi")
  270. )
  271. # On Linux, we need to run makensis via Bash, so that it can find all its
  272. # internal libraries and header files etc. Makensis fails unless it
  273. # is executed this way on Linux.
  274. if "lin" in sys.platform.lower():
  275. nsiscommand = "bash -c \"{0}\"".format(nsiscommand)
  276. makensis_success = runmakensis(nsiscommand)
  277. # Only clean NSIS installation in the temporary directory if requested
  278. if not options.noclean:
  279. cleanup()
  280. else:
  281. print("Not cleaning makensis in {0}".format(makensispath))
  282. else:
  283. print("Not creating .exe as requested.")
  284. # Only create zip archive if required
  285. if not options.nozip:
  286. filename = "sbs-" + raptorversion + ".zip"
  287. if options.nobv:
  288. bvopt = None
  289. else:
  290. bvopt = win32supportdirs["bv"]
  291. zipfile_success = writeZip(filename, options.sbshome, bvopt, win32supportdirs["cygwin"], win32supportdirs["mingw"], win32supportdirs["python"], licensetxtname)
  292. else:
  293. print("Not creating zip archive as requested.")
  294. if not options.noclean:
  295. os.unlink(licensetxtname)
  296. if not options.noexe and makensis_success != None:
  297. print("Makensis Windows installer creation completed and exited with code {0}".format(makensis_success))
  298. if not options.nozip and zipfile_success != None:
  299. print("Zip file creation completed and exited with code {0}".format(zipfile_success))
  300. print("Finished.")