nsiswriter.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. import re
  2. import sys
  3. PY2 = sys.version_info[0] == 2
  4. class NSISFileWriter(object):
  5. """Write an .nsi script file by filling in a template.
  6. """
  7. def __init__(self, template_file, definitions=None):
  8. """Instantiate an NSISFileWriter
  9. :param str template_file: Path to the .nsi template
  10. :param dict definitions: Mapping of name to value (values will be quoted)
  11. """
  12. self.template_file = template_file
  13. self.definitions = definitions or {}
  14. self.files = []
  15. self.directories = []
  16. self.shortcuts = {}
  17. self.template_fields = {
  18. ';INSTALL_FILES': self.files_install,
  19. ';INSTALL_DIRECTORIES': self.dirs_install,
  20. ';INSTALL_SHORTCUTS': self.shortcuts_install,
  21. ';UNINSTALL_FILES': self.files_uninstall,
  22. ';UNINSTALL_DIRECTORIES': self.dirs_uninstall,
  23. ';UNINSTALL_SHORTCUTS': self.shortcuts_uninstall,
  24. }
  25. if PY2:
  26. self.template_fields.update({
  27. ';PYLAUNCHER_INSTALL': self.pylauncher_install,
  28. ';PYLAUNCHER_HELP': self.pylauncher_help})
  29. def write(self, target):
  30. """Fill out the template and write the result to 'target'.
  31. :param str target: Path to the file to be written
  32. """
  33. with open(target, 'w') as fout, open(self.template_file) as fin:
  34. self.write_definitions(fout)
  35. for line in fin:
  36. fout.write(line)
  37. l = line.strip()
  38. if l in self.template_fields:
  39. indent = re.match('\s*', line).group(0)
  40. for fillline in self.template_fields[l]():
  41. fout.write(indent+fillline+'\n')
  42. def write_definitions(self, f):
  43. """Write definition lines at the start of the file.
  44. :param f: A text-mode writable file handle
  45. """
  46. for name, value in self.definitions.items():
  47. f.write('!define {} "{}"\n'.format(name, value))
  48. # Template fillers
  49. # ----------------
  50. # These return an iterable of lines to fill after a given template field
  51. def files_install(self):
  52. for file in self.files:
  53. yield 'File "{}"'.format(file)
  54. def dirs_install(self):
  55. for dir in self.directories:
  56. yield 'SetOutPath "$INSTDIR\{}"'.format(dir)
  57. yield 'File /r "{}\*.*"'.format(dir)
  58. yield 'SetOutPath "$INSTDIR"'
  59. def shortcuts_install(self):
  60. if len(self.shortcuts) == 1:
  61. scname, sc = next(iter(self.shortcuts.items()))
  62. yield 'CreateShortCut "$SMPROGRAMS\{}.lnk" "{}" \'"$INSTDIR\{}"\' \\'.format(\
  63. scname, ('py' if sc['console'] else 'pyw'), sc['script'])
  64. yield ' "$INSTDIR\{}"'.format(sc['icon'])
  65. return
  66. # Multiple shortcuts - make a folder
  67. yield 'CreateDirectory "$SMPROGRAMS\${PRODUCT_NAME}"'
  68. for scname, sc in self.shortcuts.items():
  69. yield 'CreateShortCut "$SMPROGRAMS\${{PRODUCT_NAME}}\{}.lnk" "{}" \\'.format(\
  70. scname, ('py' if sc['console'] else 'pyw'))
  71. yield ' \'"$INSTDIR\{}"\' "$INSTDIR\{}"'.format(sc['script'], sc['icon'])
  72. def files_uninstall(self):
  73. for file in self.files:
  74. yield 'Delete "$INSTDIR\{}"'.format(file)
  75. def dirs_uninstall(self):
  76. for dir in self.directories:
  77. yield 'RMDir /r "$INSTDIR\{}"'.format(dir)
  78. def shortcuts_uninstall(self):
  79. if len(self.shortcuts) == 1:
  80. scname = next(iter(self.shortcuts))
  81. yield 'Delete "$SMPROGRAMS\{}.lnk"'.format(scname)
  82. else:
  83. yield 'RMDir /r "$SMPROGRAMS\${PRODUCT_NAME}"'
  84. def pylauncher_install(self):
  85. return ["Section \"PyLauncher\" sec_pylauncher",
  86. " File \"launchwin${ARCH_TAG}.msi\"",
  87. " ExecWait 'msiexec /i \"$INSTDIR\launchwin${ARCH_TAG}.msi\" /qb ALLUSERS=1'",
  88. " Delete $INSTDIR\launchwin${ARCH_TAG}.msi",
  89. "SectionEnd",
  90. ]
  91. def pylauncher_help(self):
  92. return ["StrCmp $0 ${sec_pylauncher} 0 +2",
  93. "SendMessage $R0 ${WM_SETTEXT} 0 \"STR:The Python launcher. \\",
  94. " This is required for ${PRODUCT_NAME} to run.\"",
  95. ]