title: Pyinstaller on a setuptools package
body: <p>I'm attempting to run PyInstaller on a CLI app I am building in Python using the <a href="http://click.pocoo.org/6/" rel="noreferrer">Click</a> library. I'm having trouble building the project using PyInstaller. PyInstaller has a document in their GitHub wiki titled <a href="https://github.com/pyinstaller/pyinstaller/wiki/Recipe-Setuptools-Entry-Point" rel="noreferrer">Recipe Setuptools Entry Point</a>, which gives information about how to use PyInstaller with a <code>setuptools</code> package, which I'm using for this project. However, it seems it cannot find the base module when I run <code>pyinstaller --onefile main.spec</code>.</p>
  
  <p><strong>My question is: Is the problem simply an issue with the folder structure I have? Does the <a href="https://github.com/pyinstaller/pyinstaller/wiki/Recipe-Setuptools-Entry-Point" rel="noreferrer">Recipe Setuptools Entry Point</a> assume a certain file structure?</strong></p>
  
  <p><em>Relevant information</em></p>
  
  <p><strong>Pyinstaller output</strong></p>
  
  <pre><code>184 INFO: PyInstaller: 3.3.1
  184 INFO: Python: 3.6.4
  189 INFO: Platform: Darwin-16.7.0-x86_64-i386-64bit
  193 INFO: UPX is available.
  Traceback (most recent call last):
    File "/usr/local/bin/pyinstaller", line 11, in &lt;module&gt;
      sys.exit(run())
    File "/usr/local/lib/python3.6/site-packages/PyInstaller/__main__.py", line 94, in run
      run_build(pyi_config, spec_file, **vars(args))
    File "/usr/local/lib/python3.6/site-packages/PyInstaller/__main__.py", line 46, in run_build
      PyInstaller.building.build_main.main(pyi_config, spec_file, **kwargs)
    File "/usr/local/lib/python3.6/site-packages/PyInstaller/building/build_main.py", line 791, in main
      build(specfile, kw.get('distpath'), kw.get('workpath'), kw.get('clean_build'))
    File "/usr/local/lib/python3.6/site-packages/PyInstaller/building/build_main.py", line 737, in build
      exec(text, spec_namespace)
    File "&lt;string&gt;", line 40, in &lt;module&gt;
    File "&lt;string&gt;", line 26, in Entrypoint
    File "/usr/local/lib/python3.6/site-packages/pkg_resources/__init__.py", line 582, in get_entry_info
      return get_distribution(dist).get_entry_info(group, name)
    File "/usr/local/lib/python3.6/site-packages/pkg_resources/__init__.py", line 564, in get_distribution
      dist = get_provider(dist)
    File "/usr/local/lib/python3.6/site-packages/pkg_resources/__init__.py", line 436, in get_provider
      return working_set.find(moduleOrReq) or require(str(moduleOrReq))[0]
    File "/usr/local/lib/python3.6/site-packages/pkg_resources/__init__.py", line 984, in require
      needed = self.resolve(parse_requirements(requirements))
    File "/usr/local/lib/python3.6/site-packages/pkg_resources/__init__.py", line 870, in resolve
      raise DistributionNotFound(req, requirers)
  pkg_resources.DistributionNotFound: The 'myapp' distribution was not found and is required by the application
  </code></pre>
  
  <p><strong>The <code>main.spec</code> file for <code>main.py</code>, which is the entrypoint for my CLI app:</strong></p>
  
  <pre><code>block_cipher = None
  
  def Entrypoint(dist, group, name,
                 scripts=None, pathex=None, hiddenimports=None,
                 hookspath=None, excludes=None, runtime_hooks=None):
      import pkg_resources
  
      # get toplevel packages of distribution from metadata
      def get_toplevel(dist):
          distribution = pkg_resources.get_distribution(dist)
          if distribution.has_metadata('top_level.txt'):
              return list(distribution.get_metadata('top_level.txt').split())
          else:
              return []
  
      hiddenimports = hiddenimports or []
      packages = []
      for distribution in hiddenimports:
          packages += get_toplevel(distribution)
  
      scripts = scripts or []
      pathex = pathex or []
      # get the entry point
      ep = pkg_resources.get_entry_info(dist, group, name)
      # insert path of the egg at the verify front of the search path
      pathex = [ep.dist.location] + pathex
      # script name must not be a valid module name to avoid name clashes on import
      script_path = os.path.join(workpath, name + '-script.py')
      print ("creating script for entry point", dist, group, name)
      with open(script_path, 'w') as fh:
          print("import", ep.module_name, file=fh)
          print("%s.%s()" % (ep.module_name, '.'.join(ep.attrs)), file=fh)
          for package in packages:
              print ("import", package, file=fh)
  
      return Analysis([script_path] + scripts, pathex, hiddenimports, hookspath, excludes, runtime_hooks)
  
  a = Entrypoint('myapp', 'console_scripts', 'myapp')
  
  pyz = PYZ(a.pure, a.zipped_data,
               cipher=block_cipher)
  exe = EXE(pyz,
            a.scripts,
            exclude_binaries=True,
            name='main',
            debug=False,
            strip=False,
            upx=True,
            console=True )
  coll = COLLECT(exe,
                 a.binaries,
                 a.zipfiles,
                 a.datas,
                 strip=False,
                 upx=True,
                 name='main')
  </code></pre>
  
  <p><strong>The contents of the <code>myapp</code> script generated when I run <code>pip3 install --editable .</code> in my virtual environment:</strong></p>
  
  <pre><code>#!/some/path/to/myapp-cli/venv/bin/python3.6
  # EASY-INSTALL-ENTRY-SCRIPT: 'myapp','console_scripts','myapp'
  __requires__ = 'myapp'
  import re
  import sys
  from pkg_resources import load_entry_point
  
  if __name__ == '__main__':
      sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
      sys.exit(
          load_entry_point('myapp', 'console_scripts', 'myapp')()
      )
  </code></pre>
  
  <p><strong>And finally, my repository structure:</strong></p>
  
  <pre><code>myapp-cli/
  ├── README.md
  ├── myapp
  │   ├── __init__.py
  │   ├── main.py
  │   ├── main.spec
  │   ├── resources
  │   │   ├── __init__.py
  │   │   └── functions.py
  │   ├── subcommands
  │   │   ├── __init__.py
  │   │   ├── config
  │   │   │   ├── __init__.py
  │   │   │   └── cli.py
  │   │   ├── create
  │   │   │   ├── __init__.py
  │   │   │   └── cli.py
  │   │   ├── destroy
  │   │   │   ├── __init__.py
  │   │   │   └── cli.py
  │   │   └── switch
  │   │       ├── __init__.py
  │   │       └── cli.py
  │   └── variables.py
  ├── requirements.txt
  └── setup.py
  </code></pre>
  
  <p><strong>And my <code>setup.py</code> file:</strong></p>
  
  <pre><code>from setuptools import find_packages
  from setuptools import setup
  import os
  
  base_dir = os.path.dirname(__file__)
  
  setup(
      entry_points = '''
          [console_scripts]
          myapp=myapp.main:entry_point
      ''',
      install_requires = [
          'packageone==1.0',
          'packagetwo==2.0',
      ],
      name = "myapp",
      packages=find_packages(),
      setup_requires="setuptools",
      version = "0.1",
  )
  </code></pre>