setupbase.py
377 lines
| 13.3 KiB
| text/x-python
|
PythonLexer
Brian E Granger
|
r1237 | # encoding: utf-8 | ||
Brian E Granger
|
r1239 | """ | ||
This module defines the things that are used in setup.py for building IPython | ||||
This includes: | ||||
* The basic arguments to setup | ||||
* Functions for finding things like packages, package data, etc. | ||||
* A function for checking dependencies. | ||||
""" | ||||
Fernando Perez
|
r3198 | from __future__ import print_function | ||
Brian E Granger
|
r1237 | |||
#------------------------------------------------------------------------------- | ||||
# Copyright (C) 2008 The IPython Development Team | ||||
# | ||||
# Distributed under the terms of the BSD License. The full license is in | ||||
# the file COPYING, distributed as part of this software. | ||||
#------------------------------------------------------------------------------- | ||||
#------------------------------------------------------------------------------- | ||||
# Imports | ||||
#------------------------------------------------------------------------------- | ||||
Fernando Perez
|
r3198 | import os | ||
import sys | ||||
Brian E Granger
|
r1237 | |||
Thomas Kluyver
|
r4750 | try: | ||
from configparser import ConfigParser | ||||
except: | ||||
from ConfigParser import ConfigParser | ||||
Fernando Perez
|
r3198 | from distutils.command.build_py import build_py | ||
Brian E Granger
|
r1237 | from glob import glob | ||
from setupext import install_data_ext | ||||
#------------------------------------------------------------------------------- | ||||
# Useful globals and utility functions | ||||
#------------------------------------------------------------------------------- | ||||
# A few handy globals | ||||
isfile = os.path.isfile | ||||
pjoin = os.path.join | ||||
def oscmd(s): | ||||
Fernando Perez
|
r3198 | print(">", s) | ||
Brian E Granger
|
r1237 | os.system(s) | ||
Bernardo B. Marques
|
r4872 | |||
Thomas Kluyver
|
r4750 | try: | ||
execfile | ||||
except NameError: | ||||
def execfile(fname, globs, locs=None): | ||||
locs = locs or globs | ||||
exec(compile(open(fname).read(), fname, "exec"), globs, locs) | ||||
Brian E Granger
|
r1237 | |||
# A little utility we'll need below, since glob() does NOT allow you to do | ||||
# exclusion on multiple endings! | ||||
def file_doesnt_endwith(test,endings): | ||||
"""Return true if test is a file and its name does NOT end with any | ||||
of the strings listed in endings.""" | ||||
if not isfile(test): | ||||
return False | ||||
for e in endings: | ||||
if test.endswith(e): | ||||
return False | ||||
return True | ||||
#--------------------------------------------------------------------------- | ||||
# Basic project information | ||||
#--------------------------------------------------------------------------- | ||||
Brian Granger
|
r2146 | # release.py contains version, authors, license, url, keywords, etc. | ||
Thomas Kluyver
|
r4750 | execfile(pjoin('IPython','core','release.py'), globals()) | ||
Brian E Granger
|
r1237 | |||
# Create a dict with the basic information | ||||
# This dict is eventually passed to setup after additional keys are added. | ||||
setup_args = dict( | ||||
name = name, | ||||
version = version, | ||||
description = description, | ||||
long_description = long_description, | ||||
author = author, | ||||
author_email = author_email, | ||||
url = url, | ||||
download_url = download_url, | ||||
license = license, | ||||
platforms = platforms, | ||||
keywords = keywords, | ||||
Thomas Kluyver
|
r4771 | classifiers = classifiers, | ||
Brian E Granger
|
r1237 | cmdclass = {'install_data': install_data_ext}, | ||
) | ||||
#--------------------------------------------------------------------------- | ||||
# Find packages | ||||
#--------------------------------------------------------------------------- | ||||
def find_packages(): | ||||
Brian E Granger
|
r1239 | """ | ||
Find all of IPython's packages. | ||||
""" | ||||
MinRK
|
r4466 | excludes = ['deathrow'] | ||
packages = [] | ||||
for dir,subdirs,files in os.walk('IPython'): | ||||
package = dir.replace(os.path.sep, '.') | ||||
if any([ package.startswith('IPython.'+exc) for exc in excludes ]): | ||||
# package is to be excluded (e.g. deathrow) | ||||
continue | ||||
if '__init__.py' not in files: | ||||
# not a package | ||||
continue | ||||
packages.append(package) | ||||
Brian E Granger
|
r1237 | return packages | ||
#--------------------------------------------------------------------------- | ||||
# Find package data | ||||
#--------------------------------------------------------------------------- | ||||
def find_package_data(): | ||||
Brian E Granger
|
r1239 | """ | ||
Find IPython's package_data. | ||||
""" | ||||
Brian E Granger
|
r1237 | # This is not enough for these things to appear in an sdist. | ||
# We need to muck with the MANIFEST to get this to work | ||||
Bernardo B. Marques
|
r4872 | |||
MinRK
|
r4322 | # walk notebook resources: | ||
cwd = os.getcwd() | ||||
os.chdir(os.path.join('IPython', 'frontend', 'html', 'notebook')) | ||||
static_walk = list(os.walk('static')) | ||||
os.chdir(cwd) | ||||
static_data = [] | ||||
for parent, dirs, files in static_walk: | ||||
for f in files: | ||||
static_data.append(os.path.join(parent, f)) | ||||
Bernardo B. Marques
|
r4872 | |||
Brian E Granger
|
r1317 | package_data = { | ||
MinRK
|
r3954 | 'IPython.config.profile' : ['README', '*/*.py'], | ||
'IPython.testing' : ['*.txt'], | ||||
MinRK
|
r4322 | 'IPython.frontend.html.notebook' : ['templates/*']+static_data | ||
Brian E Granger
|
r1317 | } | ||
Brian E Granger
|
r1237 | return package_data | ||
#--------------------------------------------------------------------------- | ||||
# Find data files | ||||
#--------------------------------------------------------------------------- | ||||
Fernando Perez
|
r1525 | def make_dir_struct(tag,base,out_base): | ||
"""Make the directory structure of all files below a starting dir. | ||||
This is just a convenience routine to help build a nested directory | ||||
hierarchy because distutils is too stupid to do this by itself. | ||||
XXX - this needs a proper docstring! | ||||
""" | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r1525 | # we'll use these a lot below | ||
lbase = len(base) | ||||
pathsep = os.path.sep | ||||
lpathsep = len(pathsep) | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r1525 | out = [] | ||
for (dirpath,dirnames,filenames) in os.walk(base): | ||||
# we need to strip out the dirpath from the base to map it to the | ||||
# output (installation) path. This requires possibly stripping the | ||||
# path separator, because otherwise pjoin will not work correctly | ||||
# (pjoin('foo/','/bar') returns '/bar'). | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r1525 | dp_eff = dirpath[lbase:] | ||
if dp_eff.startswith(pathsep): | ||||
dp_eff = dp_eff[lpathsep:] | ||||
Bernardo B. Marques
|
r4872 | # The output path must be anchored at the out_base marker | ||
Fernando Perez
|
r1525 | out_path = pjoin(out_base,dp_eff) | ||
# Now we can generate the final filenames. Since os.walk only produces | ||||
# filenames, we must join back with the dirpath to get full valid file | ||||
# paths: | ||||
pfiles = [pjoin(dirpath,f) for f in filenames] | ||||
Fernando Perez
|
r3205 | # Finally, generate the entry we need, which is a pari of (output | ||
Fernando Perez
|
r1525 | # path, files) for use as a data_files parameter in install_data. | ||
Fernando Perez
|
r3205 | out.append((out_path, pfiles)) | ||
Fernando Perez
|
r1525 | |||
return out | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r1525 | |||
Brian E Granger
|
r1237 | def find_data_files(): | ||
Brian E Granger
|
r1239 | """ | ||
Find IPython's data_files. | ||||
Fernando Perez
|
r1525 | |||
Most of these are docs. | ||||
Brian E Granger
|
r1239 | """ | ||
Bernardo B. Marques
|
r4872 | |||
Brian Granger
|
r2058 | docdirbase = pjoin('share', 'doc', 'ipython') | ||
manpagebase = pjoin('share', 'man', 'man1') | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r1525 | # Simple file lists can be made by hand | ||
Brian Granger
|
r2058 | manpages = filter(isfile, glob(pjoin('docs','man','*.1.gz'))) | ||
Fernando Perez
|
r4420 | if not manpages: | ||
# When running from a source tree, the manpages aren't gzipped | ||||
manpages = filter(isfile, glob(pjoin('docs','man','*.1'))) | ||||
Fernando Perez
|
r3205 | igridhelpfiles = filter(isfile, | ||
glob(pjoin('IPython','extensions','igrid_help.*'))) | ||||
Fernando Perez
|
r1525 | |||
# For nested structures, use the utility above | ||||
Brian Granger
|
r2058 | example_files = make_dir_struct( | ||
'data', | ||||
pjoin('docs','examples'), | ||||
pjoin(docdirbase,'examples') | ||||
) | ||||
manual_files = make_dir_struct( | ||||
'data', | ||||
Fernando Perez
|
r4453 | pjoin('docs','html'), | ||
Brian Granger
|
r2058 | pjoin(docdirbase,'manual') | ||
) | ||||
Fernando Perez
|
r1525 | |||
# And assemble the entire output list | ||||
Fernando Perez
|
r3205 | data_files = [ (manpagebase, manpages), | ||
(pjoin(docdirbase, 'extensions'), igridhelpfiles), | ||||
Fernando Perez
|
r1525 | ] + manual_files + example_files | ||
Brian Granger
|
r3280 | |||
Fernando Perez
|
r1522 | return data_files | ||
Brian E Granger
|
r1237 | |||
Fernando Perez
|
r2100 | |||
def make_man_update_target(manpage): | ||||
"""Return a target_update-compliant tuple for the given manpage. | ||||
Parameters | ||||
---------- | ||||
manpage : string | ||||
Name of the manpage, must include the section number (trailing number). | ||||
Example | ||||
------- | ||||
>>> make_man_update_target('ipython.1') #doctest: +NORMALIZE_WHITESPACE | ||||
('docs/man/ipython.1.gz', | ||||
['docs/man/ipython.1'], | ||||
'cd docs/man && gzip -9c ipython.1 > ipython.1.gz') | ||||
""" | ||||
man_dir = pjoin('docs', 'man') | ||||
manpage_gz = manpage + '.gz' | ||||
manpath = pjoin(man_dir, manpage) | ||||
manpath_gz = pjoin(man_dir, manpage_gz) | ||||
gz_cmd = ( "cd %(man_dir)s && gzip -9c %(manpage)s > %(manpage_gz)s" % | ||||
locals() ) | ||||
return (manpath_gz, [manpath], gz_cmd) | ||||
Bernardo B. Marques
|
r4872 | |||
Brian E Granger
|
r1237 | #--------------------------------------------------------------------------- | ||
# Find scripts | ||||
#--------------------------------------------------------------------------- | ||||
Thomas Kluyver
|
r4765 | def find_scripts(entry_points=False, suffix=''): | ||
MinRK
|
r3681 | """Find IPython's scripts. | ||
Bernardo B. Marques
|
r4872 | |||
MinRK
|
r3681 | if entry_points is True: | ||
return setuptools entry_point-style definitions | ||||
else: | ||||
return file paths of plain scripts [default] | ||||
Bernardo B. Marques
|
r4872 | |||
Thomas Kluyver
|
r4765 | suffix is appended to script names if entry_points is True, so that the | ||
Python 3 scripts get named "ipython3" etc. | ||||
Brian E Granger
|
r1239 | """ | ||
MinRK
|
r3681 | if entry_points: | ||
Thomas Kluyver
|
r4765 | console_scripts = [s % suffix for s in [ | ||
'ipython%s = IPython.frontend.terminal.ipapp:launch_new_instance', | ||||
'pycolor%s = IPython.utils.PyColorize:main', | ||||
'ipcontroller%s = IPython.parallel.apps.ipcontrollerapp:launch_new_instance', | ||||
'ipengine%s = IPython.parallel.apps.ipengineapp:launch_new_instance', | ||||
'iplogger%s = IPython.parallel.apps.iploggerapp:launch_new_instance', | ||||
'ipcluster%s = IPython.parallel.apps.ipclusterapp:launch_new_instance', | ||||
'iptest%s = IPython.testing.iptest:main', | ||||
'irunner%s = IPython.lib.irunner:main' | ||||
]] | ||||
gui_scripts = [s % suffix for s in [ | ||||
'ipython%s-qtconsole = IPython.frontend.qt.console.qtconsoleapp:main', | ||||
]] | ||||
epatters
|
r3839 | scripts = dict(console_scripts=console_scripts, gui_scripts=gui_scripts) | ||
MinRK
|
r3681 | else: | ||
parallel_scripts = pjoin('IPython','parallel','scripts') | ||||
main_scripts = pjoin('IPython','scripts') | ||||
scripts = [ | ||||
pjoin(parallel_scripts, 'ipengine'), | ||||
pjoin(parallel_scripts, 'ipcontroller'), | ||||
pjoin(parallel_scripts, 'ipcluster'), | ||||
pjoin(parallel_scripts, 'iplogger'), | ||||
pjoin(main_scripts, 'ipython'), | ||||
pjoin(main_scripts, 'pycolor'), | ||||
pjoin(main_scripts, 'irunner'), | ||||
pjoin(main_scripts, 'iptest') | ||||
Evan Patterson
|
r3838 | ] | ||
return scripts | ||||
Brian E Granger
|
r1237 | #--------------------------------------------------------------------------- | ||
Fernando Perez
|
r1525 | # Verify all dependencies | ||
Brian E Granger
|
r1237 | #--------------------------------------------------------------------------- | ||
def check_for_dependencies(): | ||||
Brian E Granger
|
r1239 | """Check for IPython's dependencies. | ||
Bernardo B. Marques
|
r4872 | |||
Brian E Granger
|
r1239 | This function should NOT be called if running under setuptools! | ||
""" | ||||
Brian E Granger
|
r1237 | from setupext.setupext import ( | ||
Fernando Perez
|
r3198 | print_line, print_raw, print_status, | ||
Brian E Granger
|
r1237 | check_for_sphinx, check_for_pygments, | ||
MinRK
|
r3634 | check_for_nose, check_for_pexpect, | ||
MinRK
|
r3699 | check_for_pyzmq, check_for_readline | ||
Brian E Granger
|
r1237 | ) | ||
print_line() | ||||
print_raw("BUILDING IPYTHON") | ||||
print_status('python', sys.version) | ||||
print_status('platform', sys.platform) | ||||
if sys.platform == 'win32': | ||||
print_status('Windows version', sys.getwindowsversion()) | ||||
Bernardo B. Marques
|
r4872 | |||
Brian E Granger
|
r1237 | print_raw("") | ||
print_raw("OPTIONAL DEPENDENCIES") | ||||
check_for_sphinx() | ||||
check_for_pygments() | ||||
check_for_nose() | ||||
gvaroquaux
|
r1486 | check_for_pexpect() | ||
MinRK
|
r3634 | check_for_pyzmq() | ||
MinRK
|
r3699 | check_for_readline() | ||
Fernando Perez
|
r3198 | |||
def record_commit_info(pkg_dir, build_cmd=build_py): | ||||
""" Return extended build command class for recording commit | ||||
The extended command tries to run git to find the current commit, getting | ||||
the empty string if it fails. It then writes the commit hash into a file | ||||
in the `pkg_dir` path, named ``.git_commit_info.ini``. | ||||
In due course this information can be used by the package after it is | ||||
installed, to tell you what commit it was installed from if known. | ||||
To make use of this system, you need a package with a .git_commit_info.ini | ||||
file - e.g. ``myproject/.git_commit_info.ini`` - that might well look like | ||||
this:: | ||||
# This is an ini file that may contain information about the code state | ||||
[commit hash] | ||||
# The line below may contain a valid hash if it has been substituted | ||||
# during 'git archive' | ||||
archive_subst_hash=$Format:%h$ | ||||
# This line may be modified by the install process | ||||
install_hash= | ||||
The .git_commit_info file above is also designed to be used with git | ||||
substitution - so you probably also want a ``.gitattributes`` file in the | ||||
root directory of your working tree that contains something like this:: | ||||
myproject/.git_commit_info.ini export-subst | ||||
That will cause the ``.git_commit_info.ini`` file to get filled in by ``git | ||||
archive`` - useful in case someone makes such an archive - for example with | ||||
via the github 'download source' button. | ||||
Although all the above will work as is, you might consider having something | ||||
like a ``get_info()`` function in your package to display the commit | ||||
information at the terminal. See the ``pkg_info.py`` module in the nipy | ||||
package for an example. | ||||
""" | ||||
class MyBuildPy(build_cmd): | ||||
''' Subclass to write commit data into installation tree ''' | ||||
def run(self): | ||||
build_py.run(self) | ||||
import subprocess | ||||
proc = subprocess.Popen('git rev-parse --short HEAD', | ||||
stdout=subprocess.PIPE, | ||||
stderr=subprocess.PIPE, | ||||
shell=True) | ||||
repo_commit, _ = proc.communicate() | ||||
# We write the installation commit even if it's empty | ||||
cfg_parser = ConfigParser() | ||||
cfg_parser.read(pjoin(pkg_dir, '.git_commit_info.ini')) | ||||
cfg_parser.set('commit hash', 'install_hash', repo_commit) | ||||
out_pth = pjoin(self.build_lib, pkg_dir, '.git_commit_info.ini') | ||||
out_file = open(out_pth, 'wt') | ||||
cfg_parser.write(out_file) | ||||
out_file.close() | ||||
return MyBuildPy | ||||