DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.

# cbindgen is needed by the style system build and webrender.
cbindgen_is_needed = depends(build_project)(lambda build_project: build_project != 'js')

option(env='CBINDGEN', nargs=1, when=cbindgen_is_needed,
       help='Path to cbindgen')


@imports(_from='textwrap', _import='dedent')
def check_cbindgen_version(cbindgen, fatal=False):
    log.debug("trying cbindgen: %s" % cbindgen)

    cbindgen_min_version = Version('0.8.7')

    # cbindgen x.y.z
    version = Version(check_cmd_output(cbindgen, '--version').strip().split(" ")[1])
    log.debug("%s has version %s" % (cbindgen, version))
    if version >= cbindgen_min_version:
        return True
    if not fatal:
        return False

    die(dedent('''\
    cbindgen version {} is too old. At least version {} is required.

    Please update using 'cargo install cbindgen --force' or running
    './mach bootstrap', after removing the existing executable located at
    {}.
    '''.format(version, cbindgen_min_version, cbindgen)))


@depends_if('CBINDGEN', toolchain_search_path, when=cbindgen_is_needed)
@checking('for cbindgen')
@imports(_from='textwrap', _import='dedent')
def cbindgen(cbindgen_override, toolchain_search_path):
    if cbindgen_override:
        check_cbindgen_version(cbindgen_override[0], fatal=True)
        return cbindgen_override[0]

    candidates = []
    for path in toolchain_search_path:
        candidate = find_program('cbindgen', [path])
        if not candidate:
            continue
        if check_cbindgen_version(candidate):
            return candidate
        candidates.append(candidate)

    if not candidates:
        raise FatalCheckError(dedent('''\
        Cannot find cbindgen. Please run `mach bootstrap`,
        `cargo install cbindgen`, ensure that `cbindgen` is on your PATH,
        or point at an executable with `CBINDGEN`.
        '''))
    check_cbindgen_version(candidates[0], fatal=True)

set_config('CBINDGEN', cbindgen)

# Bindgen can use rustfmt to format Rust file, but it's not required.
js_option(env='RUSTFMT', nargs=1, help='Path to the rustfmt program')

rustfmt = check_prog('RUSTFMT', ['rustfmt'], paths=toolchain_search_path,
                     input='RUSTFMT', allow_missing=True)


# We support setting up the appropriate options for bindgen via setting
# LLVM_CONFIG or by providing explicit configure options.  The Windows
# installer of LLVM/Clang doesn't provide llvm-config, so we need both
# methods to support all of our tier-1 platforms.
@depends(host)
@imports('os')
def llvm_config_paths(host):
    llvm_supported_versions = ['6.0', '5.0', '4.0', '3.9']
    llvm_config_progs = []
    for version in llvm_supported_versions:
        llvm_config_progs += [
            'llvm-config-%s' % version,
            'llvm-config-mp-%s' % version,    # MacPorts' chosen naming scheme.
            'llvm-config%s' % version.replace('.', ''),
        ]
    llvm_config_progs.append('llvm-config')

    # Homebrew on macOS doesn't make clang available on PATH, so we have to
    # look for it in non-standard places.
    if host.kernel == 'Darwin':
        brew = find_program('brew')
        if brew:
            brew_config = check_cmd_output(brew, 'config').strip()

            for line in brew_config.splitlines():
                if line.startswith('HOMEBREW_PREFIX'):
                    fields = line.split(None, 2)
                    prefix = fields[1] if len(fields) == 2 else ''
                    path = ['opt', 'llvm', 'bin', 'llvm-config']
                    llvm_config_progs.append(os.path.join(prefix, *path))
                    break

    # Also add in the location to which `mach bootstrap` or
    # `mach artifact toolchain` installs clang.
    mozbuild_state_dir = os.environ.get('MOZBUILD_STATE_PATH',
                                        os.path.expanduser(os.path.join('~', '.mozbuild')))
    bootstrap_llvm_config = os.path.join(mozbuild_state_dir, 'clang', 'bin', 'llvm-config')

    llvm_config_progs.append(bootstrap_llvm_config)

    return llvm_config_progs

js_option(env='LLVM_CONFIG', nargs=1, help='Path to llvm-config')

llvm_config = check_prog('LLVM_CONFIG', llvm_config_paths, input='LLVM_CONFIG',
                         what='llvm-config', allow_missing=True)

js_option('--with-libclang-path', nargs=1,
          help='Absolute path to a directory containing Clang/LLVM libraries for bindgen (version 3.9.x or above)')
js_option('--with-clang-path', nargs=1,
          help='Absolute path to a Clang binary for bindgen (version 3.9.x or above)')

def invoke_llvm_config(llvm_config, *options):
    '''Invoke llvm_config with the given options and return the first line of
    output.'''
    lines = check_cmd_output(llvm_config, *options).splitlines()
    return lines[0]

@imports(_from='textwrap', _import='dedent')
def check_minimum_llvm_config_version(llvm_config):
    version = Version(invoke_llvm_config(llvm_config, '--version'))
    min_version = Version('3.9.0')
    if version < min_version:
        die(dedent('''\
        llvm installation {} is incompatible with bindgen.

        Please install version {} or greater of Clang + LLVM
        and ensure that the 'llvm-config' from that
        installation is first on your path.

        You can verify this by typing 'llvm-config --version'.
        '''.format(version, min_version)))

@depends(llvm_config, '--with-libclang-path', '--with-clang-path',
         host_library_name_info, host, build_project)
@imports('os.path')
@imports('glob')
@imports(_from='textwrap', _import='dedent')
def bindgen_config_paths(llvm_config, libclang_path, clang_path,
                         library_name_info, host, build_project):
    def search_for_libclang(path):
        # Try to ensure that the clang shared library that bindgen is going
        # to look for is actually present.  The files that we search for
        # mirror the logic in clang-sys/build.rs.
        libclang_choices = []
        if host.os == 'WINNT':
            libclang_choices.append('libclang.dll')
        libclang_choices.append('%sclang%s' % (library_name_info.dll.prefix,
                                               library_name_info.dll.suffix))
        if host.kernel == 'Linux':
            libclang_choices.append('libclang.so.1')

        if host.os == 'OpenBSD':
            libclang_choices = glob.glob(path + '/libclang.so.*.*')

        # At least one of the choices must be found.
        for choice in libclang_choices:
            libclang = os.path.join(path, choice)
            if os.path.exists(libclang):
                return (libclang, None)
        else:
            return (None, list(set(libclang_choices)))

    # XXX: we want this code to be run for both Gecko and JS, but we don't
    # necessarily want to force a bindgen/Rust dependency on JS just yet.
    # Actually, we don't want to force an error if we're not building the
    # browser generally.  We therefore whitelist the projects that require
    # bindgen facilities at this point and leave it at that.
    bindgen_projects = ('browser', 'mobile/android')

    if not libclang_path and not clang_path:
        # We must have LLVM_CONFIG in this case.
        if not llvm_config:
            if build_project not in bindgen_projects:
                return namespace()

            die(dedent('''\
            Could not find LLVM/Clang installation for compiling bindgen.
            Please specify the 'LLVM_CONFIG' environment variable
            (recommended), pass the '--with-libclang-path' and '--with-clang-path'
            options to configure, or put 'llvm-config' in your PATH.  Altering your
            PATH may expose 'clang' as well, potentially altering your compiler,
            which may not be what you intended.'''))

        check_minimum_llvm_config_version(llvm_config)
        libclang_arg = '--bindir' if host.os == 'WINNT' else '--libdir'
        libclang_path = invoke_llvm_config(llvm_config, libclang_arg)
        clang_path = os.path.join(invoke_llvm_config(llvm_config, '--bindir'),
                                  'clang')
        libclang_path = normsep(libclang_path)
        clang_path = normsep(clang_path)

        # Debian-based distros, at least, can have llvm-config installed
        # but not have other packages installed.  Since the user is trying
        # to use their system packages, we can't be more specific about what
        # they need.
        clang_resolved = find_program(clang_path)
        if not clang_resolved:
            die(dedent('''\
            The file {} returned by `llvm-config {}` does not exist.
            clang is required to build Firefox.  Please install the
            necessary packages, or run `mach bootstrap`.
            '''.format(clang_path, '--bindir')))

        if not os.path.exists(libclang_path):
            die(dedent('''\
            The directory {} returned by `llvm-config {}` does not exist.
            clang is required to build Firefox.  Please install the
            necessary packages, or run `mach bootstrap`.
            '''.format(libclang_path, libclang_arg)))

        (libclang, searched) = search_for_libclang(libclang_path)
        if not libclang:
            die(dedent('''\
            Could not find the clang shared library in the path {}
            returned by `llvm-config {}` (searched for files {}).
            clang is required to build Firefox.  Please install the
            necessary packages, or run `mach bootstrap`.
            '''.format(libclang_path, libclang_arg, searched)))

        return namespace(
            libclang=libclang,
            libclang_path=libclang_path,
            clang_path=clang_resolved,
        )

    if (not libclang_path and clang_path) or \
       (libclang_path and not clang_path):
        if build_project not in bindgen_projects:
            return namespace()

        die(dedent('''\
        You must provide both of --with-libclang-path and --with-clang-path
        or neither of them.'''))

    libclang_path = libclang_path[0]
    clang_path = clang_path[0]

    if not os.path.exists(libclang_path) or \
       not os.path.isdir(libclang_path):
        die(dedent('''\
        The argument to --with-libclang-path is not a directory: {}
        '''.format(libclang_path)))

    (libclang, searched) = search_for_libclang(libclang_path)
    if not libclang:
        die(dedent('''\
        Could not find the clang shared library in the path {}
        specified by --with-libclang-path (searched for files {}).
        '''.format(libclang_path, searched)))

    clang_resolved = find_program(clang_path)
    if not clang_resolved:
        die(dedent('''\
        The argument to --with-clang-path is not a file: {}
        '''.format(clang_path)))

    return namespace(
        libclang=libclang,
        libclang_path=libclang_path,
        clang_path=clang_resolved,
    )

@depends(bindgen_config_paths.libclang)
@checking('that libclang is new enough', lambda s: 'yes' if s else 'no')
@imports(_from='ctypes', _import='CDLL')
@imports(_from='textwrap', _import='dedent')
def min_libclang_version(libclang):
    try:
        lib = CDLL(libclang.encode('utf-8'))
        # We want at least 4.0. The API we test below is enough for that.
        # Just accessing it should throw if not found.
        fun = lib.clang_EvalResult_getAsLongLong
        return True
    except:
        die(dedent('''\
        The libclang located at {} is too old (need at least 4.0).

        Please make sure to update it or point to a newer libclang using
        --with-libclang-path.
        '''.format(libclang)))
        return False


set_config('MOZ_LIBCLANG_PATH', bindgen_config_paths.libclang_path)
set_config('MOZ_CLANG_PATH', bindgen_config_paths.clang_path)


@depends(target, target_is_unix, cxx_compiler, bindgen_cflags_android,
         bindgen_config_paths.clang_path, macos_sdk)
def basic_bindgen_cflags(target, is_unix, compiler_info, android_cflags,
                         clang_path, macos_sdk):
    args = [
        '-x', 'c++', '-fno-sized-deallocation',
        '-DTRACING=1', '-DIMPL_LIBXUL', '-DMOZILLA_INTERNAL_API',
        '-DRUST_BINDGEN'
    ]

    if is_unix:
        args += ['-DOS_POSIX=1']

    if target.os == 'Android':
        args += android_cflags

    args += {
        'Android': ['-DOS_ANDROID=1'],
        'DragonFly': ['-DOS_BSD=1', '-DOS_DRAGONFLY=1'],
        'FreeBSD': ['-DOS_BSD=1', '-DOS_FREEBSD=1'],
        'GNU': ['-DOS_LINUX=1'],
        'NetBSD': ['-DOS_BSD=1', '-DOS_NETBSD=1'],
        'OpenBSD': ['-DOS_BSD=1', '-DOS_OPENBSD=1'],
        'OSX': ['-DOS_MACOSX=1', '-stdlib=libc++'],
        'SunOS': ['-DOS_SOLARIS=1'],
        'WINNT': [
            '-DOS_WIN=1',
            '-DWIN32=1',
        ],
    }.get(target.os, [])

    if compiler_info.type == 'clang-cl':
        args += [
            # To enable the builtin __builtin_offsetof so that CRT wouldn't
            # use reinterpret_cast in offsetof() which is not allowed inside
            # static_assert().
            '-D_CRT_USE_BUILTIN_OFFSETOF',
            # Enable hidden attribute (which is not supported by MSVC and
            # thus not enabled by default with a MSVC-compatibile build)
            # to exclude hidden symbols from the generated file.
            '-DHAVE_VISIBILITY_HIDDEN_ATTRIBUTE=1',
        ]

    # We want to pass the same base flags as we'd pass clang.
    # check_compiler from toolchain.configure gives us that.
    # XXX: We should actually use the compiler from toolchain.configure.
    # See bug 1526857.
    info = check_compiler([clang_path], 'C++', target)

    args += info.flags

    # XXX We wouldn't have to do this if we were using the compiler from
    # toolchain.configure, rather than just `check_compiler`.
    if macos_sdk and target.os == 'OSX':
        args += ['-isysroot', macos_sdk]

    return args


js_option(env='BINDGEN_CFLAGS',
          nargs=1,
          help='Options bindgen should pass to the C/C++ parser')


@depends(basic_bindgen_cflags, 'BINDGEN_CFLAGS')
@checking('bindgen cflags', lambda s: s if s else 'no')
def bindgen_cflags(base_flags, extra_flags):
    flags = base_flags
    if extra_flags and len(extra_flags):
        flags += extra_flags[0].split()
    return ' '.join(flags)


add_old_configure_assignment('_BINDGEN_CFLAGS', bindgen_cflags)