Commit 44e839e9 authored by Jim Fulton's avatar Jim Fulton

Added a script-generation option to generate relative paths for eggs

in scripts when both the script and the eggs have a common base directory.
parent e2c38253
......@@ -893,7 +893,8 @@ def develop(setup, dest,
if is_jython:
assert subprocess.Popen([_safe_arg(executable)] + args).wait() == 0
else:
assert os.spawnl(os.P_WAIT, executable, _safe_arg (executable), *args) == 0
assert os.spawnl(os.P_WAIT, executable, _safe_arg(executable),
*args) == 0
return _copyeggs(tmp3, dest, '.egg-link', undo)
......@@ -911,11 +912,13 @@ def scripts(reqs, working_set, executable, dest,
arguments='',
interpreter=None,
initialization='',
relative_paths=False,
):
path = [dist.location for dist in working_set]
path.extend(extra_paths)
path = repr(path)[1:-1].replace(', ', ',\n ')
path = map(realpath, path)
generated = []
if isinstance(reqs, str):
......@@ -948,19 +951,87 @@ def scripts(reqs, working_set, executable, dest,
sname = name
sname = os.path.join(dest, sname)
spath, rpsetup = _relative_path_and_setup(sname, path, relative_paths)
generated.extend(
_script(module_name, attrs, path, sname, executable, arguments,
initialization)
_script(module_name, attrs, spath, sname, executable, arguments,
initialization, rpsetup)
)
if interpreter:
sname = os.path.join(dest, interpreter)
generated.extend(_pyscript(path, sname, executable))
spath, rpsetup = _relative_path_and_setup(sname, path, relative_paths)
generated.extend(_pyscript(spath, sname, executable, rpsetup))
return generated
def _relative_path_and_setup(sname, path, relative_paths):
if relative_paths:
sname = os.path.abspath(sname)
spath = ',\n '.join(
[_relativitize(path_item, sname, relative_paths)
for path_item in path]
)
rpsetup = relative_paths_setup
else:
spath = repr(path)[1:-1].replace(', ', ',\n ')
rpsetup = ''
return spath, rpsetup
def _relative_depth(common, path):
n = 0
while 1:
dirname = os.path.dirname(path)
if dirname == path:
raise AssertionError("dirname of %s is the same" % dirname)
if dirname == common:
break
n += 1
path = dirname
return n
def _relative_path(common, path):
r = []
while 1:
dirname, basename = os.path.split(path)
r.append(basename)
if dirname == common:
break
if dirname == path:
raise AssertionError("dirname of %s is the same" % dirname)
path = dirname
r.reverse()
return os.path.join(*r)
def _relativitize(path, script, relative_paths):
if path == script:
raise AssertionError("path == script")
common = os.path.dirname(os.path.commonprefix([path, script]))
if (common == relative_paths or
common.startswith(os.path.join(relative_paths, ''))
):
return "join(dirname(%s, __file__), %r)" % (
_relative_depth(common, script), _relative_path(common, path)
)
else:
return repr(path)
relative_paths_setup = """
import os
def dirname(n, path):
while n >= 0:
n -= 1
path = os.path.dirname(path)
return path
join = os.path.join
"""
def _script(module_name, attrs, path, dest, executable, arguments,
initialization):
initialization, rsetup):
generated = []
script = dest
if is_win32:
......@@ -973,6 +1044,7 @@ def _script(module_name, attrs, path, dest, executable, arguments,
attrs = attrs,
arguments = arguments,
initialization = initialization,
relative_paths_setup = rsetup,
)
changed = not (os.path.exists(dest) and open(dest).read() == contents)
......@@ -1002,9 +1074,10 @@ if is_jython and jython_os_name == 'linux':
else:
script_header = '#!%(python)s'
script_template = script_header + '''\
script_template = script_header + '''\
%(relative_paths_setup)s
import sys
sys.path[0:0] = [
%(path)s,
......@@ -1017,7 +1090,7 @@ if __name__ == '__main__':
'''
def _pyscript(path, dest, executable):
def _pyscript(path, dest, executable, rsetup):
generated = []
script = dest
if is_win32:
......@@ -1026,6 +1099,7 @@ def _pyscript(path, dest, executable):
contents = py_script_template % dict(
python = _safe_arg(executable),
path = path,
relative_paths_setup = rsetup,
)
changed = not (os.path.exists(dest) and open(dest).read() == contents)
......@@ -1051,6 +1125,7 @@ def _pyscript(path, dest, executable):
py_script_template = script_header + '''\
%(relative_paths_setup)s
import sys
sys.path[0:0] = [
......
......@@ -89,6 +89,11 @@ use_dependency_links
for using dependency_links in preference to other
locations. Defaults to true.
relative_paths
Adjust egg paths so they are relative to the script path. This
allows scripts to work when scripts and eggs are moved, as long as
they are both moved in the same way.
The install method returns a working set containing the distributions
needed to meet the given requirements.
......@@ -781,6 +786,107 @@ You can also pass script initialization code:
if __name__ == '__main__':
eggrecipedemo.main(1, 2)
Relative paths
--------------
Sometimes, you want to be able to move a buildout directory around and
have scripts still work without having to rebuild them. We can
control this using the relative_paths option to install. You need
to pass a common base directory of the scripts and eggs:
>>> bo = tmpdir('bo')
>>> mkdir(bo, 'eggs')
>>> mkdir(bo, 'bin')
>>> mkdir(bo, 'other')
>>> ws = zc.buildout.easy_install.install(
... ['demo'], join(bo, 'eggs'), links=[link_server],
... index=link_server+'index/')
>>> scripts = zc.buildout.easy_install.scripts(
... ['demo'], ws, sys.executable, join(bo, 'bin'), dict(demo='run'),
... extra_paths=[os.path.sep+'foo', join(bo, 'bar')],
... interpreter='py',
... relative_paths=bo)
>>> cat(bo, 'bin', 'run')
#!/usr/local/bin/python2.4
<BLANKLINE>
import os
<BLANKLINE>
def dirname(n, path):
while n >= 0:
n -= 1
path = os.path.dirname(path)
return path
<BLANKLINE>
join = os.path.join
<BLANKLINE>
import sys
sys.path[0:0] = [
join(dirname(1, __file__), 'eggs/demo-0.3-py2.4.egg'),
join(dirname(1, __file__), 'eggs/demoneeded-1.1-py2.4.egg'),
'/foo',
join(dirname(1, __file__), 'bar'),
]
<BLANKLINE>
import eggrecipedemo
<BLANKLINE>
if __name__ == '__main__':
eggrecipedemo.main()
Note that the extra path we specified that was outside the directory
passed as relative_paths wasn't converted to a relative path.
Of course, running the script works:
>>> print system(join(bo, 'bin', 'run')),
3 1
We specified an interpreter and its paths are adjusted too:
>>> cat(bo, 'bin', 'py')
#!/usr/local/bin/python2.4
<BLANKLINE>
<BLANKLINE>
import os
<BLANKLINE>
def dirname(n, path):
while n >= 0:
n -= 1
path = os.path.dirname(path)
return path
<BLANKLINE>
join = os.path.join
<BLANKLINE>
import sys
<BLANKLINE>
sys.path[0:0] = [
join(dirname(1, __file__), 'eggs/demo-0.3-py2.4.egg'),
join(dirname(1, __file__), 'eggs/demoneeded-1.1-py2.4.egg'),
'/foo',
join(dirname(1, __file__), 'bar'),
]
<BLANKLINE>
_interactive = True
if len(sys.argv) > 1:
import getopt
_options, _args = getopt.getopt(sys.argv[1:], 'ic:')
_interactive = False
for (_opt, _val) in _options:
if _opt == '-i':
_interactive = True
elif _opt == '-c':
exec _val
<BLANKLINE>
if _args:
sys.argv[:] = _args
execfile(sys.argv[0])
<BLANKLINE>
if _interactive:
import code
code.interact(banner="", local=globals())
Handling custom build options for extensions provided in source distributions
-----------------------------------------------------------------------------
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment