Commit 57f3dd73 authored by Julien Muchembled's avatar Julien Muchembled

Write .installed.cfg only once, in safe way and only if there's any change

Previous attempt to fix possible corruption was reverted because it had severe
performance issues (both CPU & storage).

Also, updating a part does not put it anymore at the end of the list of
installed parts. It was breaking uninstall dependencies.
parent bf58aa81
......@@ -26,6 +26,7 @@ from cStringIO import StringIO
import copy
import distutils.errors
import glob
import errno
import itertools
import logging
import os
......@@ -182,6 +183,12 @@ def _print_annotate(data):
line = ' '
print
def _remove_ignore_missing(path):
try:
os.remove(path)
except OSError, e:
if e.errno != errno.ENOENT:
raise
def _unannotate_section(section):
for key in section:
......@@ -651,8 +658,9 @@ class Buildout(UserDict.DictMixin):
self._maybe_upgrade()
# load installed data
(installed_part_options, installed_exists
)= self._read_installed_part_options()
installed_part_options = self._read_installed_part_options()
installed_parts = installed_part_options['buildout']['parts']
installed_parts = installed_parts and installed_parts.split() or []
# Remove old develop eggs
self._uninstall(
......@@ -665,22 +673,13 @@ class Buildout(UserDict.DictMixin):
installed_part_options['buildout']['installed_develop_eggs'
] = installed_develop_eggs
if installed_exists:
self._update_installed(
installed_develop_eggs=installed_develop_eggs)
# get configured and installed part lists
conf_parts = self['buildout']['parts']
conf_parts = conf_parts and conf_parts.split() or []
installed_parts = installed_part_options['buildout']['parts']
installed_parts = installed_parts and installed_parts.split() or []
try:
if install_args:
install_parts = install_args
uninstall_missing = False
else:
install_parts = conf_parts
install_parts = self['buildout']['parts']
install_parts = install_parts and install_parts.split() or ()
uninstall_missing = True
# load and initialize recipes
......@@ -697,7 +696,6 @@ class Buildout(UserDict.DictMixin):
_save_options(section, self[section], sys.stdout)
print
# compute new part recipe signatures
self._compute_part_signatures(install_parts)
......@@ -741,9 +739,6 @@ class Buildout(UserDict.DictMixin):
self._uninstall_part(part, installed_part_options)
installed_parts = [p for p in installed_parts if p != part]
if installed_exists:
self._update_installed(parts=' '.join(installed_parts))
# Check for unused buildout options:
_check_for_unused_options_in_section(self, 'buildout')
......@@ -753,7 +748,6 @@ class Buildout(UserDict.DictMixin):
saved_options = self[part].copy()
recipe = self[part].recipe
if part in installed_parts: # update
need_to_save_installed = False
__doing__ = 'Updating %s.', part
self._logger.info(*__doing__)
old_options = installed_part_options[part]
......@@ -770,33 +764,15 @@ class Buildout(UserDict.DictMixin):
try:
installed_files = self[part]._call(update)
except:
except Exception:
installed_parts.remove(part)
self._uninstall(old_installed_files)
if installed_exists:
self._update_installed(
parts=' '.join(installed_parts))
raise
old_installed_files = old_installed_files.split('\n')
if installed_files is None:
installed_files = old_installed_files
else:
if isinstance(installed_files, str):
installed_files = [installed_files]
else:
installed_files = list(installed_files)
need_to_save_installed = [
p for p in installed_files
if p not in old_installed_files]
if need_to_save_installed:
installed_files = (old_installed_files
+ need_to_save_installed)
else: # install
need_to_save_installed = True
__doing__ = 'Installing %s.', part
self._logger.info(*__doing__)
installed_files = self[part]._call(recipe.install)
......@@ -805,49 +781,51 @@ class Buildout(UserDict.DictMixin):
"The %s install returned None. A path or "
"iterable of paths should be returned.",
part)
installed_files = ()
elif isinstance(installed_files, str):
installed_files = [installed_files]
else:
installed_files = list(installed_files)
installed_files = ""
installed_part_options[part] = saved_options
saved_options['__buildout_installed__'
] = '\n'.join(installed_files)
if not isinstance(installed_files, str):
installed_files = '\n'.join(installed_files)
saved_options['__buildout_installed__'] = installed_files
saved_options['__buildout_signature__'] = signature
installed_part_options[part] = saved_options
part in installed_parts or installed_parts.append(part)
installed_parts = [p for p in installed_parts if p != part]
installed_parts.append(part)
_check_for_unused_options_in_section(self, part)
if need_to_save_installed:
installed_part_options['buildout']['parts'] = (
' '.join(installed_parts))
self._save_installed_options(installed_part_options)
installed_exists = True
else:
assert installed_exists # nothing to tell the user here
self._update_installed(parts=' '.join(installed_parts))
finally:
pass
if installed_develop_eggs:
if not installed_exists:
self._save_installed_options(installed_part_options)
elif (not installed_parts) and installed_exists:
os.remove(self['buildout']['installed'])
installed_path = self['buildout']['installed']
if installed_parts:
new = StringIO()
buildout = installed_part_options['buildout']
buildout['parts'] = ' '.join(installed_parts)
_save_options('buildout', buildout, new)
for part in installed_parts:
print >>new
_save_options(part, installed_part_options[part], new)
new = new.getvalue()
try:
with open(installed_path) as f:
save = f.read(1+len(new)) != new
except IOError, e:
if e.errno != errno.ENOENT:
raise
save = True
if save:
self._logger.info("Writing %s", installed_path)
installed_tmp = installed_path + ".tmp"
try:
with open(installed_tmp, "w") as f:
f.write(new)
f.flush()
os.fsync(f.fileno())
os.rename(installed_tmp, installed_path)
finally:
_remove_ignore_missing(installed_tmp)
else:
_remove_ignore_missing(installed_path)
self._unload_extensions()
def _update_installed(self, **buildout_options):
installed = self['buildout']['installed']
f = open(installed, 'a')
f.write('\n[buildout]\n')
for option, value in buildout_options.items():
_save_option(option, value, f)
f.close()
def _uninstall_part(self, part, installed_part_options):
# uninstall part
__doing__ = 'Uninstalling %s.', part
......@@ -966,11 +944,9 @@ class Buildout(UserDict.DictMixin):
options[option] = value
result[section] = Options(self, section, options)
return result, True
return result
else:
return ({'buildout': Options(self, 'buildout', {'parts': ''})},
False,
)
return {'buildout': Options(self, 'buildout', {'parts': ''})}
def _uninstall(self, installed):
for f in installed.split('\n'):
......@@ -1011,17 +987,6 @@ class Buildout(UserDict.DictMixin):
return ' '.join(installed)
def _save_installed_options(self, installed_options):
installed = self['buildout']['installed']
if not installed:
return
f = open(installed, 'w')
_save_options('buildout', installed_options['buildout'], f)
for part in installed_options['buildout']['parts'].split():
print >>f
_save_options(part, installed_options[part], f)
f.close()
def _error(self, message, *args):
raise zc.buildout.UserError(message % args)
......
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