Bug 924977 - More proper current working directory handling in mach; r=ahal
authorGregory Szorc <gps@mozilla.com>
Fri, 11 Oct 2013 12:44:15 -0700
changeset 150541 c1c61b305ce0b58450ec0092457f98cd7119d4ee
parent 150540 348066ccaa45ae741f6b6268ed9a86c9e6418b62
child 150542 b784c6dafa7da581970d474fe8f35685a140c5a1
push id3006
push userkwierso@gmail.com
push dateSat, 12 Oct 2013 02:00:44 +0000
treeherderfx-team@3ff6c5ec4dc5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersahal
bugs924977
milestone27.0a1
Bug 924977 - More proper current working directory handling in mach; r=ahal
build/mach_bootstrap.py
mach
python/mach/mach/base.py
python/mach/mach/main.py
python/mozbuild/mozbuild/base.py
python/mozbuild/mozbuild/mach_commands.py
python/mozbuild/mozbuild/test/test_base.py
--- a/build/mach_bootstrap.py
+++ b/build/mach_bootstrap.py
@@ -167,18 +167,19 @@ def bootstrap(topsrcdir, mozilla_dir=Non
     try:
         import mach.main
     except ImportError:
         sys.path[0:0] = [os.path.join(mozilla_dir, path) for path in SEARCH_PATHS]
         import mach.main
 
     def populate_context(context):
         context.state_dir = state_dir
+        context.topdir = topsrcdir
 
-    mach = mach.main.Mach(topsrcdir)
+    mach = mach.main.Mach(os.getcwd())
     mach.populate_context_handler = populate_context
 
     for category, meta in CATEGORIES.items():
         mach.define_category(category, meta['short'], meta['long'],
             meta['priority'])
 
     for path in MACH_MODULES:
         mach.load_commands_from_file(os.path.join(mozilla_dir, path))
--- a/mach
+++ b/mach
@@ -15,36 +15,41 @@ def ancestors(path):
         if child == "":
             break
 
 def load_mach(topsrcdir):
     sys.path[0:0] = [os.path.join(topsrcdir, "build")]
     import mach_bootstrap
     return mach_bootstrap.bootstrap(topsrcdir)
 
-# Check whether the current directory is within a mach src or obj dir.
-for dir_path in ancestors(os.getcwd()):
-    # If we find a "mozinfo.json" file, we are in the objdir.
-    mozinfo_path = os.path.join(dir_path, "mozinfo.json")
-    if os.path.isfile(mozinfo_path):
-        import json
-        info = json.load(open(mozinfo_path))
-        if "mozconfig" in info and "MOZCONFIG" not in os.environ:
-            # If the MOZCONFIG environment variable is not already set, set it
-            # to the value from mozinfo.json.  This will tell the build system
-            # to look for a config file at the path in $MOZCONFIG rather than
-            # its default locations.
-            #
-            # Note: subprocess requires native strings in os.environ on Windows
-            os.environ[b"MOZCONFIG"] = str(info["mozconfig"])
+def main(args):
+    # Check whether the current directory is within a mach src or obj dir.
+    for dir_path in ancestors(os.getcwd()):
+        # If we find a "mozinfo.json" file, we are in the objdir.
+        mozinfo_path = os.path.join(dir_path, 'mozinfo.json')
+        if os.path.isfile(mozinfo_path):
+            import json
+            info = json.load(open(mozinfo_path))
+            if 'mozconfig' in info and 'MOZCONFIG' not in os.environ:
+                # If the MOZCONFIG environment variable is not already set, set it
+                # to the value from mozinfo.json.  This will tell the build system
+                # to look for a config file at the path in $MOZCONFIG rather than
+                # its default locations.
+                #
+                # Note: subprocess requires native strings in os.environ on Windows
+                os.environ[b'MOZCONFIG'] = str(info['mozconfig'])
 
-        if "topsrcdir" in info:
-            # Continue searching for mach_bootstrap in the source directory.
-            dir_path = info["topsrcdir"]
+            if 'topsrcdir' in info:
+                # Continue searching for mach_bootstrap in the source directory.
+                dir_path = info['topsrcdir']
 
-    # If we find the mach bootstrap module, we are in the srcdir.
-    mach_path = os.path.join(dir_path, "build/mach_bootstrap.py")
-    if os.path.isfile(mach_path):
-        mach = load_mach(dir_path)
-        sys.exit(mach.run(sys.argv[1:]))
+        # If we find the mach bootstrap module, we are in the srcdir.
+        mach_path = os.path.join(dir_path, 'build/mach_bootstrap.py')
+        if os.path.isfile(mach_path):
+            mach = load_mach(dir_path)
+            sys.exit(mach.run(args[1:]))
 
-print("Could not run mach: No mach source directory found")
-sys.exit(1)
+    print('Could not run mach: No mach source directory found.')
+    sys.exit(1)
+
+
+if __name__ == '__main__':
+    main(sys.argv)
--- a/python/mach/mach/base.py
+++ b/python/mach/mach/base.py
@@ -2,19 +2,18 @@
 # 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 unicode_literals
 
 
 class CommandContext(object):
     """Holds run-time state so it can easily be passed to command providers."""
-    def __init__(self, topdir=None, cwd=None, settings=None, log_manager=None,
+    def __init__(self, cwd=None, settings=None, log_manager=None,
         commands=None):
-        self.topdir = topdir
         self.cwd = cwd
         self.settings = settings
         self.log_manager = log_manager
         self.commands = commands
 
 
 class MachError(Exception):
     """Base class for all errors raised by mach itself."""
--- a/python/mach/mach/main.py
+++ b/python/mach/mach/main.py
@@ -284,17 +284,17 @@ To see more help for a specific command,
             return 1
 
         finally:
             sys.stdin = orig_stdin
             sys.stdout = orig_stdout
             sys.stderr = orig_stderr
 
     def _run(self, argv):
-        context = CommandContext(topdir=self.cwd, cwd=self.cwd,
+        context = CommandContext(cwd=self.cwd,
             settings=self.settings, log_manager=self.log_manager,
             commands=Registrar)
 
         if self.populate_context_handler:
             self.populate_context_handler(context)
 
         parser = self.get_argument_parser(context)
 
--- a/python/mozbuild/mozbuild/base.py
+++ b/python/mozbuild/mozbuild/base.py
@@ -167,21 +167,23 @@ class MozbuildObject(ProcessExecutionMix
         config_topobjdir = MozbuildObject.resolve_mozconfig_topobjdir(
             topsrcdir, config)
 
         # If we're inside a objdir and the found mozconfig resolves to
         # another objdir, we abort. The reasoning here is that if you are
         # inside an objdir you probably want to perform actions on that objdir,
         # not another one. This prevents accidental usage of the wrong objdir
         # when the current objdir is ambiguous.
-        if topobjdir and config_topobjdir \
-            and not samepath(topobjdir, config_topobjdir) \
-            and not samepath(topobjdir, os.path.join(config_topobjdir, "mozilla")):
+        if topobjdir and config_topobjdir:
+            mozilla_dir = os.path.join(config_topobjdir, 'mozilla')
+            if not samepath(topobjdir, config_topobjdir) \
+                and (os.path.exists(mozilla_dir) and not samepath(topobjdir,
+                mozilla_dir)):
 
-            raise ObjdirMismatchException(topobjdir, config_topobjdir)
+                raise ObjdirMismatchException(topobjdir, config_topobjdir)
 
         topobjdir = topobjdir or config_topobjdir
         if topobjdir:
             topobjdir = os.path.normpath(topobjdir)
 
         # If we can't resolve topobjdir, oh well. The constructor will figure
         # it out via config.guess.
         return cls(topsrcdir, None, None, topobjdir=topobjdir)
--- a/python/mozbuild/mozbuild/mach_commands.py
+++ b/python/mozbuild/mozbuild/mach_commands.py
@@ -18,16 +18,17 @@ from mach.decorators import (
 
 from mach.mixin.logging import LoggingMixin
 
 from mozbuild.base import (
     MachCommandBase,
     MozbuildObject,
     MozconfigFindException,
     MozconfigLoadException,
+    ObjdirMismatchException,
 )
 
 
 BUILD_WHAT_HELP = '''
 What to build. Can be a top-level make target or a relative directory. If
 multiple options are provided, they will be built serially. Takes dependency
 information from `topsrcdir/build/dumbmake-dependencies` to build additional
 targets as needed. BUILDING ONLY PARTS OF THE TREE CAN RESULT IN BAD TREE
@@ -829,36 +830,42 @@ class Makefiles(MachCommandBase):
 
     def _makefile_ins(self):
         for root, dirs, files in os.walk(self.topsrcdir):
             for f in files:
                 if f == 'Makefile.in':
                     yield os.path.join(root, f)
 
 @CommandProvider
-class MachDebug(object):
-    def __init__(self, context):
-        self.context = context
-
+class MachDebug(MachCommandBase):
     @Command('environment', category='build-dev',
         description='Show info about the mach and build environment.')
     @CommandArgument('--verbose', '-v', action='store_true',
         help='Print verbose output.')
     def environment(self, verbose=False):
         import platform
         print('platform:\n\t%s' % platform.platform())
         print('python version:\n\t%s' % sys.version)
         print('python prefix:\n\t%s' % sys.prefix)
-        print('mach cwd:\n\t%s' % self.context.cwd)
+        print('mach cwd:\n\t%s' % self._mach_context.cwd)
         print('os cwd:\n\t%s' % os.getcwd())
-        print('mach directory:\n\t%s' % self.context.topdir)
-        print('state directory:\n\t%s' % self.context.state_dir)
+        print('mach directory:\n\t%s' % self._mach_context.topdir)
+        print('state directory:\n\t%s' % self._mach_context.state_dir)
 
-        mb = MozbuildObject(self.context.topdir, self.context.settings,
-            self.context.log_manager)
+        try:
+            mb = MozbuildObject.from_environment(cwd=self._mach_context.cwd)
+        except ObjdirMismatchException as e:
+            print('Ambiguous object directory detected. We detected that '
+                'both %s and %s could be object directories. This is '
+                'typically caused by having a mozconfig pointing to a '
+                'different object directory from the current working '
+                'directory. To solve this problem, ensure you do not have a '
+                'default mozconfig in searched paths.' % (e.objdir1,
+                    e.objdir2))
+            return 1
 
         mozconfig = None
 
         try:
             mozconfig = mb.mozconfig
             print('mozconfig path:\n\t%s' % mozconfig['path'])
         except MozconfigFindException as e:
             print('Unable to find mozconfig: %s' % e.message)
--- a/python/mozbuild/mozbuild/test/test_base.py
+++ b/python/mozbuild/mozbuild/test/test_base.py
@@ -195,17 +195,17 @@ class TestMozbuildObject(unittest.TestCa
 
             os.chdir(topobjdir)
 
             class MockMachContext(object):
                 pass
 
             context = MockMachContext()
             context.cwd = topobjdir
-            context.topdir = topobjdir
+            context.topdir = topsrcdir
             context.settings = None
             context.log_manager = None
 
             o = MachCommandBase(context)
 
             self.assertEqual(o.topobjdir, topobjdir)
             self.assertEqual(o.topsrcdir, topsrcdir)