Commit ef294676 authored by Julien Muchembled's avatar Julien Muchembled Committed by Kazuhiko Shiozaki

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

Also, updating a part does not put it anymore at the end of the list of
installed parts. It was breaking uninstall dependencies.
parent e7d42b35
...@@ -33,6 +33,7 @@ import copy ...@@ -33,6 +33,7 @@ import copy
import datetime import datetime
import distutils.errors import distutils.errors
import glob import glob
import errno
import itertools import itertools
import logging import logging
import os import os
...@@ -173,6 +174,12 @@ def _print_annotate(data): ...@@ -173,6 +174,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:
...@@ -628,8 +635,9 @@ class Buildout(DictMixin): ...@@ -628,8 +635,9 @@ class Buildout(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(
...@@ -642,22 +650,13 @@ class Buildout(DictMixin): ...@@ -642,22 +650,13 @@ class Buildout(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
...@@ -674,7 +673,6 @@ class Buildout(DictMixin): ...@@ -674,7 +673,6 @@ class Buildout(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)
...@@ -718,9 +716,6 @@ class Buildout(DictMixin): ...@@ -718,9 +716,6 @@ class Buildout(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')
...@@ -730,7 +725,6 @@ class Buildout(DictMixin): ...@@ -730,7 +725,6 @@ class Buildout(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]
...@@ -747,33 +741,15 @@ class Buildout(DictMixin): ...@@ -747,33 +741,15 @@ class Buildout(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)
...@@ -782,50 +758,53 @@ class Buildout(DictMixin): ...@@ -782,50 +758,53 @@ class Buildout(DictMixin):
"The %s install returned None. A path or " "The %s install returned None. A path or "
"iterable os paths should be returned.", "iterable os 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, basestring):
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
self._update_installed(parts=' '.join(installed_parts))
finally: finally:
pass installed_path = self['buildout']['installed']
buildout = installed_part_options['buildout']
if installed_develop_eggs: if installed_parts or buildout['installed_develop_eggs']:
if not installed_exists: new = StringIO()
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.debug("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)
if self.show_picked_versions or self.update_versions_file: if self.show_picked_versions or self.update_versions_file:
self._print_picked_versions() self._print_picked_versions()
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 list(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
...@@ -943,11 +922,9 @@ class Buildout(DictMixin): ...@@ -943,11 +922,9 @@ class Buildout(DictMixin):
options[option] = value options[option] = value
result[section] = self.Options(self, section, options) result[section] = self.Options(self, section, options)
return result, True return result
else: else:
return ({'buildout': self.Options(self, 'buildout', {'parts': ''})}, return {'buildout': self.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'):
...@@ -988,17 +965,6 @@ class Buildout(DictMixin): ...@@ -988,17 +965,6 @@ class Buildout(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_(file=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