faq.rst 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. FAQs
  2. ====
  3. Building on other platforms
  4. ---------------------------
  5. You can use Pynsist to build Windows installers from a Linux or Mac system.
  6. You'll need to install NSIS so that the ``makensis`` command is available.
  7. Here's how to do that on some common platforms:
  8. * Debian/Ubuntu: ``sudo apt-get install nsis``
  9. * Fedora: ``sudo dnf install mingw32-nsis``
  10. * Mac with `Homebrew <https://brew.sh/>`__: ``brew install makensis``
  11. Installing Pynsist itself is the same on all platforms::
  12. pip install pynsist
  13. If your package relies on compiled extension modules, like
  14. PyQt4, lxml or numpy, you'll need to ensure that the installer is built with
  15. Windows versions of these packages. There are a few options for this:
  16. - List them under ``pypi_wheels`` in the :ref:`Include section <cfg_include>`
  17. of your config file. Pynsist will download Windows-compatible wheels from
  18. PyPI. This is the easiest option if the dependency publishes wheels.
  19. - Get the importable packages/modules, either from a Windows installation, or
  20. by extracting them from an installer. Copy them into a folder called
  21. ``pynsist_pkgs``, next to your ``installer.cfg`` file. Pynsist will
  22. copy everything in this folder to the build directory.
  23. - Include exe/msi installers for those modules, and modify the ``.nsi`` template
  24. to extract and run these during installation. This can make your installer
  25. bigger and slower, and it may create unwanted start menu shortcuts
  26. (e.g. PyQt4 does), so it's a last resort. However, if the
  27. installer sets up other things on the system, you may need to do this.
  28. When running on non-Windows systems, Pynsist will bundle a 32-bit version of
  29. Python by default, though you can override this :ref:`in the config file <cfg_python>`.
  30. Whichever method you use, compiled libraries must have the same bit-ness as
  31. the version of Python that's installed.
  32. .. _faq-data-files:
  33. Using data files
  34. ----------------
  35. Applications often need data files along with their code. The easiest way to use
  36. data files with Pynsist is to store them in a Python package (a directory with
  37. a ``__init__.py`` file) you're creating for your application. They will be
  38. copied automatically, and modules in that package can locate them using
  39. ``__file__`` like this::
  40. data_file_path = os.path.join(os.path.dirname(__file__), 'file.dat')
  41. If you don't want to put data files inside a Python package, you will need to
  42. list them in the ``files`` key of the ``[Include]`` section of the config file.
  43. Your code can find them relative to the location of the launch script running your
  44. application (``sys.modules['__main__'].__file__``).
  45. .. note::
  46. The techniques above work for fixed data files which you ship with your
  47. application. For files which your app will *write*, you should use another
  48. location, because an app installed systemwide cannot write files in its
  49. install directory. Use the ``APPDATA`` or ``LOCALAPPDATA`` environment
  50. variables as locations to write hidden data files (`what's the difference?
  51. <https://superuser.com/a/21462/209976>`__)::
  52. writable_file = os.path.join(os.environ['LOCALAPPDATA'], 'MyApp', 'file.dat')
  53. .. _faq-subprocess:
  54. Running subprocesses
  55. --------------------
  56. There are a few things to be aware of if your code needs to run a subprocess:
  57. * The ``python`` command may not be found, or may be another version of Python.
  58. Use :data:`sys.executable` to get the path of the Python executable running
  59. your application.
  60. * Commands which are normally installed by your Python dependencies, such as
  61. ``sphinx-build`` or ``pygmentize``, won't be available when your app is
  62. installed. You can often launch the same thing from an importable
  63. module by running something like ``{sys.executable} -m sphinx``.
  64. * When your application runs as a GUI (without a console), subprocesses launched
  65. with :data:`sys.executable` don't have anywhere to write output. This makes
  66. debugging harder, and the subprocess can get stuck trying to write output.
  67. You can capture output in your code and print it (sending it to the log file
  68. described under :ref:`log-file`)::
  69. res = subprocess.run([sys.executable, "-c", "print('hello')"],
  70. text=True, capture_output=True)
  71. print(res.stdout)
  72. print(res.stderr)
  73. If you want a console window to appear for your subprocess, check if
  74. :data:`sys.executable` points to ``pythonw.exe``, and use ``python.exe`` in
  75. the same folder instead::
  76. python = sys.executable
  77. if python.endswith('pythonw.exe'):
  78. python = python.removesuffix('pythonw.exe') + 'python.exe'
  79. subprocess.run([python, "-c", "print('hello'); input('Press enter')"])
  80. The console will close as soon as the subprocess finishes, so the example
  81. above uses :func:`input` to wait for input and give the user time to see it.
  82. .. _faq-no-wheels:
  83. Bundling packages which don't have wheels on PyPI
  84. -------------------------------------------------
  85. Most modern Python packages release packages in the 'wheel' format, which
  86. Pynsist can download and use automatically (``pypi_wheels`` in the config file).
  87. But some older packages and packages with certain kinds of complexity don't do
  88. this.
  89. If you need to include a package which doesn't release wheels, you can build
  90. your own wheels and :ref:`include them <cfg_include>` with either the
  91. ``extra_wheel_sources`` or the ``local_wheels`` config options.
  92. Run :samp:`pip wheel {package-name}` to build a wheel of a package on PyPI.
  93. If the package contains only Python code, this should always work.
  94. If the package contains compiled extensions (typically C code), and does not
  95. publish wheels on PyPI, you will need to build the wheels on Windows, and you
  96. will need a suitable compiler installed. See `Packaging binary extensions
  97. <https://packaging.python.org/guides/packaging-binary-extensions/>`_ in the
  98. Python packaging user guide for more details. If you're not familiar with
  99. building Python extension modules, this can be difficult, so you might want to
  100. think about whether you can solve the problem without that package.
  101. .. note::
  102. If a package is maintained but doesn't publish wheels, you could ask its
  103. maintainers to consider doing so. But be considerate! They may have reasons
  104. not to publish wheels, it may mean a lot of work for them, and they may have
  105. been asked before. Don't assume that it's their responsibility to build
  106. wheels, and do look for existing discussions on the topic before starting a
  107. new one.
  108. .. _faq-tkinter:
  109. Packaging with tkinter
  110. ----------------------
  111. Because Pynsist makes use of the "bundled" versions of Python the ``tkinter``
  112. module isn't included by default. If your application relies on ``tkinter`` for
  113. a GUI then you need to find the following assets:
  114. * The ``tcl`` directory in the root directory of a Windows installation of
  115. Python. This needs to come from the same Python version and bitness (i.e.
  116. 32-bit or 64-bit) as the Python you are bundling into the installer.
  117. * The ``_tkinter.pyd``, ``tcl86t.dll`` and ``tk86t.dll`` libraries in the
  118. ``DLLs`` directory of the version of Python your are using in your app. As
  119. above, these must be the same bitness and version as your target version of
  120. Python.
  121. * The ``_tkinter.lib`` file in the ``libs`` directory of the version of Python
  122. you are using in your app. Same caveats as above.
  123. The ``tcl`` directory should be copied into the root of your project (i.e. in
  124. the directory that contains ``installer.cfg``) and renamed to ``lib``
  125. (this is important!).
  126. Create a new directory in the root of your project called ``pynsist_pkgs`` and
  127. copy over the other four files mentioned above into it (so it contains
  128. ``_tkinter.lib``, ``_tkinter.pyd``, ``tcl86t.dll`` and ``tk86t.dll``).
  129. Finally, in your ``.cfg`` file ensure the ``packages`` section contains
  130. ``tkinter`` and ``_tkinter``, and the ``files`` section contains ``lib``, like
  131. this::
  132. packages=
  133. tkinter
  134. _tkinter
  135. files=lib
  136. Build your installer and test it. You'll know everything is in the right place
  137. if the directory into which your application is installed contains a ``lib``
  138. directory containing the contents of the original ``tcl`` directory and the
  139. ``pkgs`` directory contains the remaining four files. If things still don't
  140. work check the bitness and Python version associated with these assets and
  141. make sure they're the same as the version of Python installed with your
  142. application.
  143. .. note::
  144. A future version of Pynsist might automate some of this procedure to make
  145. distributing tkinter applications easier.
  146. ``DLL load failed`` errors
  147. --------------------------
  148. Importing compiled extension modules in your application may fail with errors
  149. like this::
  150. ImportError: DLL load failed: The specified module could not be found.
  151. This means that the Python module it's trying to load needs a DLL which isn't
  152. there. Unfortunately, the error message doesn't say which DLL is missing, and
  153. there's no simple way to identify it.
  154. The traceback should show which import failed. The module that was being
  155. imported should be a file with a ``.pyd`` extension. You can use a program
  156. called `Dependency Walker <https://www.dependencywalker.com/>`_ on this file
  157. to work out what DLLs it needs and which are missing, though you may need to
  158. adjust the 'module search order' to avoid some false negatives.
  159. Once you've worked out what is missing, you'll need to make it available.
  160. This may mean bundling extra DLLs as :ref:`data files <faq-data-files>`.
  161. If you do this, it's up to you to ensure you have the right to redistribute them.
  162. Code signing
  163. ------------
  164. People trying to use your installer will see an 'Unknown publisher' warning.
  165. To avoid this, you can sign it with a digital certificate. See
  166. `Mozilla's instructions on signing executables using Mono
  167. <https://developer.mozilla.org/en-US/docs/Mozilla/Developer_guide/Build_Instructions/Signing_an_executable_with_Authenticode>`__,
  168. or `this guide from Adafruit on signing an installer
  169. <https://learn.adafruit.com/how-to-sign-windows-drivers-installer/making-an-installer#sign-the-installer>`__.
  170. Signing requires a certificate from a provider trusted by Microsoft.
  171. As of summer 2017, these are the cheapest options I can find:
  172. * Certum's `open source code signing certificate <https://www.certum.eu/certum/cert,offer_en_open_source_cs.xml>`__:
  173. €86 for a certificate with a smart card and reader, €28 for a new certificate
  174. if you have the hardware. Each certificate is valid for one year.
  175. This is only for open source software.
  176. * Many companies resell Comodo code signing certificates at prices lower than
  177. Comodo themselves, especially if you pay for 3–4 years up front.
  178. `CodeSignCert <https://codesigncert.com/comodocodesigning>`__ ($59–75 per year),
  179. `K Software <http://codesigning.ksoftware.net/>`__ ($67–$84 per year) and
  180. `Cheap SSL Security <https://cheapsslsecurity.co.uk/comodo/codesigningcertificate.html>`__ (UK, £54–£64 per year)
  181. are a few examples; a search will turn up many more like them.
  182. I haven't used any of these companies, so I'm not making a recommendation.
  183. Please do your own research before buying from them.
  184. If you find another good way to get a code signing certificate, please make a
  185. pull request to add it!
  186. Alternatives
  187. ------------
  188. Other ways to distribute applications to users without Python installed include
  189. freeze tools, like `cx_Freeze <http://cx-freeze.sourceforge.net/>`_ and
  190. `PyInstaller <http://www.pyinstaller.org/>`_, and Python compilers like
  191. `Nuitka <http://nuitka.net/>`_.
  192. pynsist has some advantages:
  193. * Python code often does things—like using ``__file__`` to find its
  194. location on disk, or :data:`sys.executable` to launch Python processes—which
  195. don't work when it's run from a frozen exe. pynsist just installs Python files,
  196. so it avoids all these problems.
  197. * It's quite easy to make Windows installers on other platforms, which is
  198. difficult with other tools.
  199. * The tool itself is simpler to understand, and less likely to need updating for
  200. new Python versions.
  201. And some disadvantages:
  202. * Installers tend to be bigger because you're bundling the whole Python standard
  203. library.
  204. * You don't get an exe for your application, just a start menu shortcut to launch
  205. it.
  206. * pynsist only makes Windows installers.
  207. Popular freeze tools also try to automatically detect what packages you're using.
  208. Pynsist could do the same thing, but in my experience, this detection is complex and often
  209. misses things, so for now it expects an explicit list of the packages
  210. your application needs.
  211. Another alternative is `conda constructor <https://github.com/conda/constructor>`__,
  212. which builds an installer out of conda packages. Conda packages are more
  213. flexible than PyPI packages, and many libraries are already packaged, but you
  214. have to make a conda package of your own code as well before using conda
  215. constructor to make an installer.
  216. Conda constructor can also make Linux and Mac installers, but unlike Pynsist, it
  217. can't make a Windows installer from Linux or Mac.