blob: 114527331a6c3fb3298d546bc528567a86bd2fbf [file] [log] [blame]
#!/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:]))