49

All of the documentation for PyInstaller talks about including individual files.

Is it possible to include a directory, or should I write a function to create the include array by traversing my include directory?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Simon Knight
  • 600
  • 1
  • 6
  • 12

5 Answers5

36

There is also the official supported option using Tree():

Pyinstaller: generate -exe file + folder (in --onefile mode)

The TOC and Tree Classes

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
denfromufa
  • 5,610
  • 13
  • 81
  • 138
  • 12
    Thank you for sharing, using [the Tree class](https://pythonhosted.org/PyInstaller/advanced-topics.html#the-tree-class) solved it for me in an elegant way. Now I just need this one line: `a.datas += Tree('./dir_to_include', prefix='dir_to_include')` – sunyata Sep 21 '17 at 01:23
  • yep, I think it is just not documented well-enough, but this open-source, so PRs are welcome :) – denfromufa Sep 21 '17 at 14:23
23

Paste the following after a = Analysis() in the spec file to traverse a directory recursively and add all the files in it to the distribution.

##### include mydir in distribution #######
def extra_datas(mydir):
    def rec_glob(p, files):
        import os
        import glob
        for d in glob.glob(p):
            if os.path.isfile(d):
                files.append(d)
            rec_glob("%s/*" % d, files)
    files = []
    rec_glob("%s/*" % mydir, files)
    extra_datas = []
    for f in files:
        extra_datas.append((f, f, 'DATA'))

    return extra_datas
###########################################

# append the 'data' dir
a.datas += extra_datas('data')
styts
  • 1,003
  • 1
  • 8
  • 19
  • 1
    Excuse me, but I don't really get it. I have a directory called `~/Scripts`. My data is stored in `~/Scripts/Data`. Should I substitute `a.datas += extra_datas('data')` by `a.datas += extra_datas('Data')`? – Exeleration-G Apr 21 '13 at 10:32
  • If your pyInstaller script is also in `Scripts` and you call it with `python mybuildscript.py` from within `Scripts` then yes, you should substitute with `Data`, otherwise use `.` and `..` to navigate the directory tree. – styts Apr 25 '13 at 09:36
17

The problem is easier than you can imagine. Try this:

--add-data="path/to/folder/*;."

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
leminhnguyen
  • 1,518
  • 2
  • 13
  • 19
  • 17
    This will include all of the files in path/to/folder/, and all files in sub-folders, in the root of your pyinstaller output. However, any sub-folder structure won't be kept - all files will be flattened to the root. If you want to include the files in their same directory structure (but it still won't keep sub-folder structure), you can use `--add-data="path/to/folder/*;path/to/folder/"` – wags1999 May 05 '21 at 14:47
10

Yes, you can just add directories to the Analysis object and they get copied across.

a = Analysis(['main.py'],
             datas = [('test/dir', 'test/dir')],
             ...)
wedesoft
  • 2,781
  • 28
  • 25
  • 1
    At least for pyinstaller 4.2 the datas field need to be added as a tuple. If you want to add multiple folders it needs to be something like: `... datas = [('test/dir', 'test/dir'),('test2/dir', 'test2/dir')] ...` – Joao Antunes Feb 03 '21 at 18:59
8

Just use glob:

from glob import glob

datas = []
datas += glob('/path/to/filedir/*')
datas += glob('/path/to/textdir/*.txt')

# ...

a.datas = datas
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
jdi
  • 90,542
  • 19
  • 167
  • 203