| # This Source Code Form is subject to the terms of the Mozilla Public |
| # License, v. 2.0. If a copy of the MPL was not distributed with this |
| # file, You can obtain one at http://mozilla.org/MPL/2.0/. |
| |
| from __future__ import print_function |
| |
| r'''This module contains code for managing clobbering of the tree.''' |
| |
| import os |
| import sys |
| |
| from mozfile.mozfile import rmtree |
| from textwrap import TextWrapper |
| |
| |
| CLOBBER_MESSAGE = ''.join([TextWrapper().fill(line) + '\n' for line in |
| ''' |
| The CLOBBER file has been updated, indicating that an incremental build since \ |
| your last build will probably not work. A full/clobber build is required. |
| |
| The reason for the clobber is: |
| |
| {clobber_reason} |
| |
| Clobbering can be performed automatically. However, we didn't automatically \ |
| clobber this time because: |
| |
| {no_reason} |
| |
| The easiest and fastest way to clobber is to run: |
| |
| $ mach clobber |
| |
| If you know this clobber doesn't apply to you or you're feeling lucky \ |
| - well do ya? - you can ignore this clobber requirement by running: |
| |
| $ touch {clobber_file} |
| '''.splitlines()]) |
| |
| class Clobberer(object): |
| def __init__(self, topsrcdir, topobjdir): |
| """Create a new object to manage clobbering the tree. |
| |
| It is bound to a top source directory and to a specific object |
| directory. |
| """ |
| assert os.path.isabs(topsrcdir) |
| assert os.path.isabs(topobjdir) |
| |
| self.topsrcdir = os.path.normpath(topsrcdir) |
| self.topobjdir = os.path.normpath(topobjdir) |
| self.src_clobber = os.path.join(topsrcdir, 'CLOBBER') |
| self.obj_clobber = os.path.join(topobjdir, 'CLOBBER') |
| |
| assert os.path.isfile(self.src_clobber) |
| |
| def clobber_needed(self): |
| """Returns a bool indicating whether a tree clobber is required.""" |
| |
| # No object directory clobber file means we're good. |
| if not os.path.exists(self.obj_clobber): |
| return False |
| |
| # Object directory clobber older than current is fine. |
| if os.path.getmtime(self.src_clobber) <= \ |
| os.path.getmtime(self.obj_clobber): |
| |
| return False |
| |
| return True |
| |
| def clobber_cause(self): |
| """Obtain the cause why a clobber is required. |
| |
| This reads the cause from the CLOBBER file. |
| |
| This returns a list of lines describing why the clobber was required. |
| Each line is stripped of leading and trailing whitespace. |
| """ |
| with open(self.src_clobber, 'rt') as fh: |
| lines = [l.strip() for l in fh.readlines()] |
| return [l for l in lines if l and not l.startswith('#')] |
| |
| def ensure_objdir_state(self): |
| """Ensure the CLOBBER file in the objdir exists. |
| |
| This is called as part of the build to ensure the clobber information |
| is configured properly for the objdir. |
| """ |
| if not os.path.exists(self.topobjdir): |
| os.makedirs(self.topobjdir) |
| |
| if not os.path.exists(self.obj_clobber): |
| # Simply touch the file. |
| with open(self.obj_clobber, 'a'): |
| pass |
| |
| def maybe_do_clobber(self, cwd, allow_auto=False, fh=sys.stderr): |
| """Perform a clobber if it is required. Maybe. |
| |
| This is the API the build system invokes to determine if a clobber |
| is needed and to automatically perform that clobber if we can. |
| |
| This returns a tuple of (bool, bool, str). The elements are: |
| |
| - Whether a clobber was/is required. |
| - Whether a clobber was performed. |
| - The reason why the clobber failed or could not be performed. This |
| will be None if no clobber is required or if we clobbered without |
| error. |
| """ |
| assert cwd |
| cwd = os.path.normpath(cwd) |
| |
| if not self.clobber_needed(): |
| print('Clobber not needed.', file=fh) |
| self.ensure_objdir_state() |
| return False, False, None |
| |
| # So a clobber is needed. We only perform a clobber if we are |
| # allowed to perform an automatic clobber (off by default) and if the |
| # current directory is not under the object directory. The latter is |
| # because operating systems, filesystems, and shell can throw fits |
| # if the current working directory is deleted from under you. While it |
| # can work in some scenarios, we take the conservative approach and |
| # never try. |
| if not allow_auto: |
| return True, False, \ |
| self._message('Automatic clobbering is not enabled\n' |
| ' (add "mk_add_options AUTOCLOBBER=1" to your ' |
| 'mozconfig).') |
| |
| if cwd.startswith(self.topobjdir) and cwd != self.topobjdir: |
| return True, False, self._message( |
| 'Cannot clobber while the shell is inside the object directory.') |
| |
| print('Automatically clobbering %s' % self.topobjdir, file=fh) |
| try: |
| if cwd == self.topobjdir: |
| for entry in os.listdir(self.topobjdir): |
| full = os.path.join(self.topobjdir, entry) |
| |
| if os.path.isdir(full): |
| rmtree(full) |
| else: |
| os.unlink(full) |
| |
| else: |
| rmtree(self.topobjdir) |
| |
| self.ensure_objdir_state() |
| print('Successfully completed auto clobber.', file=fh) |
| return True, True, None |
| except (IOError) as error: |
| return True, False, self._message( |
| 'Error when automatically clobbering: ' + str(error)) |
| |
| def _message(self, reason): |
| lines = [' ' + line for line in self.clobber_cause()] |
| |
| return CLOBBER_MESSAGE.format(clobber_reason='\n'.join(lines), |
| no_reason=' ' + reason, clobber_file=self.obj_clobber) |
| |
| |
| def main(args, env, cwd, fh=sys.stderr): |
| if len(args) != 2: |
| print('Usage: clobber.py topsrcdir topobjdir', file=fh) |
| return 1 |
| |
| topsrcdir, topobjdir = args |
| |
| if not os.path.isabs(topsrcdir): |
| topsrcdir = os.path.abspath(topsrcdir) |
| |
| if not os.path.isabs(topobjdir): |
| topobjdir = os.path.abspath(topobjdir) |
| |
| auto = True if env.get('AUTOCLOBBER', False) else False |
| clobber = Clobberer(topsrcdir, topobjdir) |
| required, performed, message = clobber.maybe_do_clobber(cwd, auto, fh) |
| |
| if not required or performed: |
| return 0 |
| |
| print(message, file=fh) |
| return 1 |
| |
| |
| if __name__ == '__main__': |
| sys.exit(main(sys.argv[1:], os.environ, os.getcwd(), sys.stdout)) |
| |