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 ...@@ -26,6 +26,7 @@ from cStringIO import StringIO
import copy import copy
import distutils.errors import distutils.errors
import glob import glob
import errno
import itertools import itertools
import logging import logging
import os import os
...@@ -182,6 +183,12 @@ def _print_annotate(data): ...@@ -182,6 +183,12 @@ def _print_annotate(data):
line = ' ' line = ' '
print print
def _remove_ignore_missing(path):
try:
os.remove(path)
except OSError, e:
if e.errno != errno.ENOENT:
raise
def _unannotate_section(section): def _unannotate_section(section):
for key in section: for key in section:
...@@ -651,8 +658,9 @@ class Buildout(UserDict.DictMixin): ...@@ -651,8 +658,9 @@ class Buildout(UserDict.DictMixin):
self._maybe_upgrade() self._maybe_upgrade()
# load installed data # load installed data
(installed_part_options, installed_exists installed_part_options = self._read_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 # Remove old develop eggs
self._uninstall( self._uninstall(
...@@ -665,22 +673,13 @@ class Buildout(UserDict.DictMixin): ...@@ -665,22 +673,13 @@ class Buildout(UserDict.DictMixin):
installed_part_options['buildout']['installed_develop_eggs' installed_part_options['buildout']['installed_develop_eggs'
] = 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: try:
if install_args: if install_args:
install_parts = install_args install_parts = install_args
uninstall_missing = False uninstall_missing = False
else: else:
install_parts = conf_parts install_parts = self['buildout']['parts']
install_parts = install_parts and install_parts.split() or ()
uninstall_missing = True uninstall_missing = True
# load and initialize recipes # load and initialize recipes
...@@ -697,7 +696,6 @@ class Buildout(UserDict.DictMixin): ...@@ -697,7 +696,6 @@ class Buildout(UserDict.DictMixin):
_save_options(section, self[section], sys.stdout) _save_options(section, self[section], sys.stdout)
print print
# compute new part recipe signatures # compute new part recipe signatures
self._compute_part_signatures(install_parts) self._compute_part_signatures(install_parts)
...@@ -741,9 +739,6 @@ class Buildout(UserDict.DictMixin): ...@@ -741,9 +739,6 @@ class Buildout(UserDict.DictMixin):
self._uninstall_part(part, installed_part_options) self._uninstall_part(part, installed_part_options)
installed_parts = [p for p in installed_parts if p != part] 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 buildout options:
_check_for_unused_options_in_section(self, 'buildout') _check_for_unused_options_in_section(self, 'buildout')
...@@ -753,7 +748,6 @@ class Buildout(UserDict.DictMixin): ...@@ -753,7 +748,6 @@ class Buildout(UserDict.DictMixin):
saved_options = self[part].copy() saved_options = self[part].copy()
recipe = self[part].recipe recipe = self[part].recipe
if part in installed_parts: # update if part in installed_parts: # update
need_to_save_installed = False
__doing__ = 'Updating %s.', part __doing__ = 'Updating %s.', part
self._logger.info(*__doing__) self._logger.info(*__doing__)
old_options = installed_part_options[part] old_options = installed_part_options[part]
...@@ -770,33 +764,15 @@ class Buildout(UserDict.DictMixin): ...@@ -770,33 +764,15 @@ class Buildout(UserDict.DictMixin):
try: try:
installed_files = self[part]._call(update) installed_files = self[part]._call(update)
except: except Exception:
installed_parts.remove(part) installed_parts.remove(part)
self._uninstall(old_installed_files) self._uninstall(old_installed_files)
if installed_exists:
self._update_installed(
parts=' '.join(installed_parts))
raise raise
old_installed_files = old_installed_files.split('\n')
if installed_files is None: if installed_files is None:
installed_files = old_installed_files 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 else: # install
need_to_save_installed = True
__doing__ = 'Installing %s.', part __doing__ = 'Installing %s.', part
self._logger.info(*__doing__) self._logger.info(*__doing__)
installed_files = self[part]._call(recipe.install) installed_files = self[part]._call(recipe.install)
...@@ -805,49 +781,51 @@ class Buildout(UserDict.DictMixin): ...@@ -805,49 +781,51 @@ class Buildout(UserDict.DictMixin):
"The %s install returned None. A path or " "The %s install returned None. A path or "
"iterable of paths should be returned.", "iterable of paths should be returned.",
part) part)
installed_files = () installed_files = ""
elif isinstance(installed_files, str):
installed_files = [installed_files]
else:
installed_files = list(installed_files)
installed_part_options[part] = saved_options if not isinstance(installed_files, str):
saved_options['__buildout_installed__' installed_files = '\n'.join(installed_files)
] = '\n'.join(installed_files) saved_options['__buildout_installed__'] = installed_files
saved_options['__buildout_signature__'] = signature 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) _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: finally:
pass installed_path = self['buildout']['installed']
if installed_parts:
if installed_develop_eggs: new = StringIO()
if not installed_exists: buildout = installed_part_options['buildout']
self._save_installed_options(installed_part_options) buildout['parts'] = ' '.join(installed_parts)
elif (not installed_parts) and installed_exists: _save_options('buildout', buildout, new)
os.remove(self['buildout']['installed']) 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() 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): def _uninstall_part(self, part, installed_part_options):
# uninstall part # uninstall part
__doing__ = 'Uninstalling %s.', part __doing__ = 'Uninstalling %s.', part
...@@ -966,11 +944,9 @@ class Buildout(UserDict.DictMixin): ...@@ -966,11 +944,9 @@ class Buildout(UserDict.DictMixin):
options[option] = value options[option] = value
result[section] = Options(self, section, options) result[section] = Options(self, section, options)
return result, True return result
else: else:
return ({'buildout': Options(self, 'buildout', {'parts': ''})}, return {'buildout': Options(self, 'buildout', {'parts': ''})}
False,
)
def _uninstall(self, installed): def _uninstall(self, installed):
for f in installed.split('\n'): for f in installed.split('\n'):
...@@ -1011,17 +987,6 @@ class Buildout(UserDict.DictMixin): ...@@ -1011,17 +987,6 @@ class Buildout(UserDict.DictMixin):
return ' '.join(installed) 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): def _error(self, message, *args):
raise zc.buildout.UserError(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