#!/usr/bin/env vpython3 # # Copyright 2022 The ANGLE Project Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # # mesa_build.py: # Helper script for building Mesa in an ANGLE checkout. import argparse import json import logging import os import shutil import subprocess import sys SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) ANGLE_DIR = os.path.dirname(os.path.dirname(SCRIPT_DIR)) DEFAULT_LOG_LEVEL = 'info' EXIT_SUCCESS = 0 EXIT_FAILURE = 1 MESON = os.path.join(ANGLE_DIR, 'third_party', 'meson', 'meson.py') MESA_SOURCE_DIR = os.path.join(ANGLE_DIR, 'third_party', 'mesa', 'src') LIBDRM_SOURCE_DIR = os.path.join(ANGLE_DIR, 'third_party', 'libdrm') LIBDRM_BUILD_DIR = os.path.join(ANGLE_DIR, 'out', 'libdrm-build') MESA_STAMP = 'mesa.stamp' LIBDRM_STAMP = 'libdrm.stamp' MESA_OPTIONS = [ '-Dzstd=disabled', '-Dplatforms=x11', '-Dgallium-drivers=zink', '-Dvulkan-drivers=', '-Dvalgrind=disabled', ] LIBDRM_OPTIONS = [ '-Dtests=false', '-Dintel=disabled', '-Dnouveau=disabled', '-Damdgpu=disabled', '-Dradeon=disabled', '-Dvmwgfx=disabled', '-Dvalgrind=disabled', '-Dman-pages=disabled', ] def main(raw_args): parser = argparse.ArgumentParser() parser.add_argument( '-l', '--log', '--log-level', help='Logging level. Default is %s.' % DEFAULT_LOG_LEVEL, default=DEFAULT_LOG_LEVEL) subparser = parser.add_subparsers(dest='command') mesa = subparser.add_parser('mesa') mesa.add_argument('build_dir', help='Target build directory.') mesa.add_argument('-j', '--jobs', help='Compile jobs.') compile_parser = subparser.add_parser('compile') compile_parser.add_argument('-j', '--jobs', help='Compile jobs.') compile_parser.add_argument('build_dir', help='Target build directory.') gni = subparser.add_parser('gni') gni.add_argument('output', help='Output location for gni file.') gni.add_argument('mesa_build_dir', help='Target Mesa build directory.') gni.add_argument('libdrm_build_dir', help='Target libdrm build directory.') libdrm = subparser.add_parser('libdrm') runhook_parser = subparser.add_parser('runhook') runhook_parser.add_argument( '-o', '--output', help='Output location for stamp sha1 file.', default=MESA_STAMP) setup_parser = subparser.add_parser('setup') setup_parser.add_argument('target', help='Project: mesa or libdrm.') setup_parser.add_argument('build_dir', help='Target build directory.') setup_parser.add_argument('-w', '--wipe', help='Wipe output directory.', action='store_true') args, extra_args = parser.parse_known_args(raw_args) logging.basicConfig(level=args.log.upper()) assert os.path.exists(MESON), 'Could not find meson.py: %s' % MESON if args.command == 'mesa': SetupBuild(args.build_dir, MESA_SOURCE_DIR, MESA_OPTIONS) Compile(args, args.build_dir) elif args.command == 'compile': Compile(args, args.build_dir) elif args.command == 'gni': GenerateGni(args) elif args.command == 'libdrm': SetupBuild(args.build_dir, LIBDRM_SOURCE_DIR, LIBDRM_OPTIONS) Compile(args, args.build_dir) elif args.command == 'runhook': RunHook(args) elif args.command == 'setup': LazySetup(args, args.build_dir) return EXIT_SUCCESS def SetupBuild(build_dir, source_dir, options, pkg_config_paths=[]): if not os.path.exists(build_dir): os.mkdir(build_dir) sysroot_dir = os.path.join(ANGLE_DIR, 'build', 'linux', 'debian_bullseye_amd64-sysroot') cflags = ' '.join([ '--sysroot=%s' % sysroot_dir, '-Wno-constant-conversion', '-Wno-deprecated-builtins', '-Wno-deprecated-declarations', '-Wno-deprecated-non-prototype', '-Wno-enum-compare-conditional', '-Wno-enum-conversion', '-Wno-implicit-const-int-float-conversion', '-Wno-implicit-function-declaration', '-Wno-initializer-overrides', '-Wno-sometimes-uninitialized', '-Wno-unused-but-set-variable', '-Wno-unused-function', ]) pkg_config_paths += [ '%s/usr/share/pkgconfig' % sysroot_dir, '%s/usr/lib/pkgconfig' % sysroot_dir ] extra_env = { 'CC': 'clang', 'CC_LD': 'lld', 'CXX': 'clang++', 'CXX_LD': 'lld', 'CFLAGS': cflags, 'CXXFLAGS': cflags, 'PKG_CONFIG_PATH': ':'.join(pkg_config_paths), } args = [source_dir, build_dir, '--cross-file', os.path.join(SCRIPT_DIR, 'angle_cross.ini')] + options if os.path.isdir(os.path.join(build_dir, 'meson-info')): args += ['--wipe'] return Meson(build_dir, 'setup', args, extra_env) def Compile(args, build_dir): return Meson(build_dir, 'compile', ['-C', build_dir]) def MakeEnv(): clang_dir = os.path.join(ANGLE_DIR, 'third_party', 'llvm-build', 'Release+Asserts', 'bin') flex_bison_dir = os.path.join(ANGLE_DIR, 'tools', 'flex-bison') # TODO: Windows flex_bison_platform = 'linux' flex_bison_bin_dir = os.path.join(flex_bison_dir, flex_bison_platform) depot_tools_dir = os.path.join(ANGLE_DIR, 'third_party', 'depot_tools') env = os.environ.copy() paths = [clang_dir, flex_bison_bin_dir, depot_tools_dir, env['PATH']] env['PATH'] = ':'.join(paths) env['BISON_PKGDATADIR'] = os.path.join(flex_bison_dir, 'third_party') return env GNI_TEMPLATE = """\ # GENERATED FILE - DO NOT EDIT. # Generated by {script_name} # # Copyright 2022 The ANGLE Project Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # # {filename}: ANGLE build information for Mesa. angle_mesa_outputs = [ {angle_mesa_outputs}] angle_mesa_sources = [ {angle_mesa_sources}] angle_libdrm_outputs = [ {angle_libdrm_outputs}] angle_libdrm_sources = [ {angle_libdrm_sources}] """ def GenerateGni(args): mesa_sources_filter = lambda target: target['type'] != 'shared library' mesa_outputs_filter = lambda target: target['type'] == 'shared library' mesa_sources, mesa_outputs = GetMesonSourcesAndOutputs(args.mesa_build_dir, mesa_sources_filter, mesa_outputs_filter) libdrm_sources_filter = lambda target: True libdrm_outputs_filter = lambda target: target['type'] == 'shared library' libdrm_sources, libdrm_outputs = GetMesonSourcesAndOutputs(args.libdrm_build_dir, libdrm_sources_filter, libdrm_outputs_filter) fmt_list = lambda l, rp: ''.join( sorted(list(set([' "%s",\n' % os.path.relpath(li, rp) for li in l])))) format_args = { 'script_name': os.path.basename(__file__), 'filename': os.path.basename(args.output), 'angle_mesa_outputs': fmt_list(mesa_outputs, args.mesa_build_dir), 'angle_mesa_sources': fmt_list(mesa_sources, MESA_SOURCE_DIR), 'angle_libdrm_outputs': fmt_list(libdrm_outputs, args.libdrm_build_dir), 'angle_libdrm_sources': fmt_list(libdrm_sources, LIBDRM_SOURCE_DIR), } gni_text = GNI_TEMPLATE.format(**format_args) with open(args.output, 'w') as outf: outf.write(gni_text) outf.close() logging.info('Saved GNI data to %s' % args.output) def GetMesonSourcesAndOutputs(build_dir, sources_filter, output_filter): text_data = Meson(build_dir, 'introspect', [build_dir, '--targets'], stdout=subprocess.PIPE) json_data = json.loads(text_data) outputs = [] all_sources = [] generated = [] for target in json_data: generated += target['filename'] if output_filter(target): outputs += target['filename'] if sources_filter(target): for target_source in target['target_sources']: all_sources += target_source['sources'] sources = list(filter(lambda s: (s not in generated), all_sources)) for source in sources: assert os.path.exists(source), '%s does not exist' % source return sources, outputs def Meson(build_dir, command, args, extra_env={}, stdout=None): meson_cmd = [MESON, command] + args env = MakeEnv() for k, v in extra_env.items(): env[k] = v # TODO: Remove when crbug.com/1373441 is fixed. env['VPYTHON_DEFAULT_SPEC'] = os.path.join(ANGLE_DIR, '.vpython3') logging.info(' '.join(['%s=%s' % (k, v) for (k, v) in extra_env.items()] + meson_cmd)) completed = subprocess.run(meson_cmd, env=env, stdout=stdout) if completed.returncode != EXIT_SUCCESS: logging.fatal('Got error from meson:') with open(os.path.join(build_dir, 'meson-logs', 'meson-log.txt')) as logf: lines = logf.readlines() for line in lines[-10:]: logging.fatal(' %s' % line.strip()) sys.exit(EXIT_FAILURE) if stdout: return completed.stdout def RunHook(args): output = os.path.join(SCRIPT_DIR, args.output) Stamp(args, MESA_SOURCE_DIR, output) libdrm_out = os.path.join(SCRIPT_DIR, LIBDRM_STAMP) Stamp(args, LIBDRM_SOURCE_DIR, libdrm_out) def Stamp(args, source_dir, output): commit_id = GrabOutput('git rev-parse HEAD', source_dir) with open(output, 'w') as outf: outf.write(commit_id) outf.close() logging.info('Saved git hash data to %s' % output) def GrabOutput(command, cwd): return subprocess.Popen( command, stdout=subprocess.PIPE, shell=True, cwd=cwd).communicate()[0].strip().decode() def LazySetup(args, build_dir): stamp = args.target + '.stamp' in_stamp = os.path.join(SCRIPT_DIR, stamp) out_stamp = os.path.join(build_dir, args.target, stamp) if not args.wipe and SameStamps(in_stamp, out_stamp): logging.info('%s setup up-to-date.' % args.target) sys.exit(EXIT_SUCCESS) if args.target == 'mesa': source_dir = MESA_SOURCE_DIR options = MESA_OPTIONS pkg_config_paths = [os.path.join(build_dir, 'libdrm', 'meson-uninstalled')] else: assert (args.target == 'libdrm') source_dir = LIBDRM_SOURCE_DIR options = LIBDRM_OPTIONS pkg_config_paths = [] SetupBuild(os.path.join(build_dir, args.target), source_dir, options, pkg_config_paths) shutil.copyfile(in_stamp, out_stamp) logging.info('Finished setup and updated %s.' % out_stamp) def SameStamps(in_stamp, out_stamp): assert os.path.exists(in_stamp) if not os.path.exists(out_stamp): return False in_data = ReadFile(in_stamp) out_data = ReadFile(out_stamp) return in_data == out_data def ReadFile(path): with open(path, 'rt') as inf: all_data = inf.read() inf.close() return all_data if __name__ == "__main__": sys.exit(main(sys.argv[1:]))