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, ...@@ -893,7 +893,8 @@ def develop(setup, dest,
if is_jython: if is_jython:
assert subprocess.Popen([_safe_arg(executable)] + args).wait() == 0 assert subprocess.Popen([_safe_arg(executable)] + args).wait() == 0
else: 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) return _copyeggs(tmp3, dest, '.egg-link', undo)
...@@ -911,11 +912,13 @@ def scripts(reqs, working_set, executable, dest, ...@@ -911,11 +912,13 @@ def scripts(reqs, working_set, executable, dest,
arguments='', arguments='',
interpreter=None, interpreter=None,
initialization='', initialization='',
relative_paths=False,
): ):
path = [dist.location for dist in working_set] path = [dist.location for dist in working_set]
path.extend(extra_paths) path.extend(extra_paths)
path = repr(path)[1:-1].replace(', ', ',\n ') path = map(realpath, path)
generated = [] generated = []
if isinstance(reqs, str): if isinstance(reqs, str):
...@@ -948,19 +951,87 @@ def scripts(reqs, working_set, executable, dest, ...@@ -948,19 +951,87 @@ def scripts(reqs, working_set, executable, dest,
sname = name sname = name
sname = os.path.join(dest, sname) sname = os.path.join(dest, sname)
spath, rpsetup = _relative_path_and_setup(sname, path, relative_paths)
generated.extend( generated.extend(
_script(module_name, attrs, path, sname, executable, arguments, _script(module_name, attrs, spath, sname, executable, arguments,
initialization) initialization, rpsetup)
) )
if interpreter: if interpreter:
sname = os.path.join(dest, 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 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, def _script(module_name, attrs, path, dest, executable, arguments,
initialization): initialization, rsetup):
generated = [] generated = []
script = dest script = dest
if is_win32: if is_win32:
...@@ -973,6 +1044,7 @@ def _script(module_name, attrs, path, dest, executable, arguments, ...@@ -973,6 +1044,7 @@ def _script(module_name, attrs, path, dest, executable, arguments,
attrs = attrs, attrs = attrs,
arguments = arguments, arguments = arguments,
initialization = initialization, initialization = initialization,
relative_paths_setup = rsetup,
) )
changed = not (os.path.exists(dest) and open(dest).read() == contents) changed = not (os.path.exists(dest) and open(dest).read() == contents)
...@@ -1002,9 +1074,10 @@ if is_jython and jython_os_name == 'linux': ...@@ -1002,9 +1074,10 @@ if is_jython and jython_os_name == 'linux':
else: else:
script_header = '#!%(python)s' script_header = '#!%(python)s'
script_template = script_header + '''\
script_template = script_header + '''\
%(relative_paths_setup)s
import sys import sys
sys.path[0:0] = [ sys.path[0:0] = [
%(path)s, %(path)s,
...@@ -1017,7 +1090,7 @@ if __name__ == '__main__': ...@@ -1017,7 +1090,7 @@ if __name__ == '__main__':
''' '''
def _pyscript(path, dest, executable): def _pyscript(path, dest, executable, rsetup):
generated = [] generated = []
script = dest script = dest
if is_win32: if is_win32:
...@@ -1026,6 +1099,7 @@ def _pyscript(path, dest, executable): ...@@ -1026,6 +1099,7 @@ def _pyscript(path, dest, executable):
contents = py_script_template % dict( contents = py_script_template % dict(
python = _safe_arg(executable), python = _safe_arg(executable),
path = path, path = path,
relative_paths_setup = rsetup,
) )
changed = not (os.path.exists(dest) and open(dest).read() == contents) changed = not (os.path.exists(dest) and open(dest).read() == contents)
...@@ -1051,6 +1125,7 @@ def _pyscript(path, dest, executable): ...@@ -1051,6 +1125,7 @@ def _pyscript(path, dest, executable):
py_script_template = script_header + '''\ py_script_template = script_header + '''\
%(relative_paths_setup)s
import sys import sys
sys.path[0:0] = [ sys.path[0:0] = [
......
...@@ -89,6 +89,11 @@ use_dependency_links ...@@ -89,6 +89,11 @@ use_dependency_links
for using dependency_links in preference to other for using dependency_links in preference to other
locations. Defaults to true. 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 The install method returns a working set containing the distributions
needed to meet the given requirements. needed to meet the given requirements.
...@@ -781,6 +786,107 @@ You can also pass script initialization code: ...@@ -781,6 +786,107 @@ You can also pass script initialization code:
if __name__ == '__main__': if __name__ == '__main__':
eggrecipedemo.main(1, 2) 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 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