mach
author Mike Hommey <mh+mozilla@glandium.org>
Thu, 24 Jul 2014 13:43:56 +0900
changeset 195822 34235900c3a0e300afb1911489fa6e0c8d67df76
parent 195343 ade872c15c024a3266722c096d40e52ed70d202e
child 198583 65bcc7b2a8a6f4e5475be2b8c7949f5bbae08369
permissions -rwxr-xr-x
Bug 1036286 - Make sure libraries loaded by faulty.lib use its own sigaction. r=nfroyd Android L added a libsigchain library it LD_PRELOADs. That library exposes a different sigaction than libc's. It's used for ART. faulty.lib gets its sigaction from libsigchain, but after bug 874708, the libraries it loads simply use libc's sigaction, assuming it would be the hooked one. In turn, this means libraries loaded by faulty.lib may override faulty.lib's handler, which is definitely not the intent. This essentially restores some of the code that bug 874708 removed. An alternative fix would be to add support for LD_PRELOAD, but that has more implications and feels more risky. This could be done, if necessary, as a followup.

#!/bin/sh
# 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/.

# The beginning of this script is both valid shell and valid python,
# such that the script starts with the shell and is reexecuted with
# the right python.
'''which' python2.7 > /dev/null && exec python2.7 "$0" "$@" || exec python "$0" "$@"
'''

from __future__ import print_function, unicode_literals

import os
import sys

def ancestors(path):
    while path:
        yield path
        (path, child) = os.path.split(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)


def check_and_run_mach(dir_path, args):
    # 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))


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']

        check_and_run_mach(dir_path, args)

    # If we didn't find a source path by scanning for a mozinfo.json, check
    # whether the directory containing this script is a source directory.
    check_and_run_mach(os.path.dirname(__file__), args)

    print('Could not run mach: No mach source directory found.')
    sys.exit(1)


if __name__ == '__main__':
    if sys.platform == 'win32':
        # This is a complete hack to work around the fact that Windows
        # multiprocessing needs to import the original module (ie: this
        # file), but only works if it has a .py extension.
        #
        # We do this by a sort of two-level function interposing. The first
        # level interposes forking.get_command_line() with our version defined
        # in my_get_command_line(). Our version of get_command_line will
        # replace the command string with the contents of the fork_interpose()
        # function to be used in the subprocess.
        #
        # The subprocess then gets an interposed imp.find_module(), which we
        # hack up to find 'mach' without the .py extension, since we already
        # know where it is (it's us!). If we're not looking for 'mach', then
        # the original find_module will suffice.
        #
        # See also: http://bugs.python.org/issue19946
        # And: https://bugzilla.mozilla.org/show_bug.cgi?id=914563
        import inspect
        from multiprocessing import forking
        global orig_command_line

        def fork_interpose():
            import imp
            import os
            orig_find_module = imp.find_module
            def my_find_module(name, dirs):
                if name == 'mach':
                    path = os.path.join(dirs[0], 'mach')
                    f = open(path)
                    return (f, path, ('', 'r', imp.PY_SOURCE))
                return orig_find_module(name, dirs)

            imp.find_module = my_find_module
            from multiprocessing.forking import main; main()

        def my_get_command_line():
            fork_code, lineno = inspect.getsourcelines(fork_interpose)
            # Remove the first line (for 'def fork_interpose():') and the three
            # levels of indentation (12 spaces).
            fork_string = ''.join(x[12:] for x in fork_code[1:])
            cmdline = orig_command_line()
            cmdline[2] = fork_string
            return cmdline
        orig_command_line = forking.get_command_line
        forking.get_command_line = my_get_command_line

    main(sys.argv[1:])