1
0
Fork 0
mirror of https://github.com/LadybirdBrowser/ladybird.git synced 2025-06-07 21:17:07 +09:00

Meta: Add build command to ladybird.py

This commit is contained in:
ayeteadoe 2025-05-15 07:04:01 -07:00 committed by Andrew Kaster
parent 85842c1739
commit c9624d5118
Notes: github-actions[bot] 2025-05-21 17:37:14 +00:00
5 changed files with 309 additions and 0 deletions

2
.gitignore vendored
View file

@ -59,3 +59,5 @@ Meta/CMake/vcpkg/user-variables.cmake
# Keep last to ensure .DS_Store is never tracked, even if otherwise allowed by any exception above.
.DS_Store
__pycache__

View file

@ -55,14 +55,18 @@
{
"hidden": true,
"name": "windows_ci",
"binaryDir": "${fileDir}/Build/release",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "RelWithDebInfo",
"ENABLE_WINDOWS_CI": "ON"
}
},
{
"hidden": true,
"name": "windows_dev",
"binaryDir": "${fileDir}/Build/debug",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"ENABLE_WINDOWS_CI": "OFF"
}
},

1
Meta/__init__.py Normal file
View file

@ -0,0 +1 @@
# NOTE: Provided so ladybird.py can import the BuildVcpkg script as a module

301
Meta/ladybird.py Executable file
View file

@ -0,0 +1,301 @@
#!/usr/bin/env python3
import argparse
import os
import re
import sys
import shutil
import multiprocessing
from pathlib import Path
import subprocess
from enum import IntEnum
class HostArchitecture(IntEnum):
Unsupported = 0
x86_64 = 1
AArch64 = 2
class HostSystem(IntEnum):
Unsupported = 0
Linux = 1
macOS = 2
Windows = 3
class Platform:
def __init__(self):
import platform
self.system = platform.system()
if self.system == 'Windows':
self.host_system = HostSystem.Windows
elif self.system == 'Darwin':
self.host_system = HostSystem.macOS
elif self.system == 'Linux':
self.host_system = HostSystem.Linux
else:
self.host_system = HostSystem.Unsupported
self.architecture = platform.machine().lower()
if self.architecture in ('x86_64', 'amd64'):
self.host_architecture = HostArchitecture.x86_64
elif self.architecture in ('aarch64', 'arm64'):
self.host_architecture = HostArchitecture.AArch64
else:
self.host_architecture = HostArchitecture.Unsupported
def main(platform):
if platform.host_system == HostSystem.Unsupported:
print(f'Unsupported host system {platform.system}', file=sys.stderr)
sys.exit(1)
if platform.host_architecture == HostArchitecture.Unsupported:
print(f'Unsupported host architecture {platform.architecture}', file=sys.stderr)
sys.exit(1)
parser = argparse.ArgumentParser(description='Ladybird')
subparsers = parser.add_subparsers(dest='command')
preset_parser = argparse.ArgumentParser(add_help=False)
preset_parser.add_argument(
'--preset',
required=False,
default=os.environ.get('BUILD_PRESET',
'windows_dev_ninja' if platform.host_system == HostSystem.Windows else 'default'),
)
# FIXME: Validate that the cc/cxx combination is compatible (e.g. don't allow CC=gcc and CXX=clang++)
# FIXME: Migrate find_compiler.sh for more explicit compiler validation
compiler_parser = argparse.ArgumentParser(add_help=False)
compiler_parser.add_argument(
'--cc',
required=False,
default=os.environ.get('CC', 'clang-cl' if platform.host_system == HostSystem.Windows else 'cc'))
compiler_parser.add_argument(
'--cxx',
required=False,
default=os.environ.get('CXX', 'clang-cl' if platform.host_system == HostSystem.Windows else 'c++')
)
target_parser = argparse.ArgumentParser(add_help=False)
target_parser.add_argument('--target', required=False, default='Ladybird')
build_parser = subparsers.add_parser('build', help='Compiles the target binaries',
parents=[preset_parser, compiler_parser, target_parser])
build_parser.add_argument('args', nargs=argparse.REMAINDER,
help='Additional arguments passed through to the build system')
args = parser.parse_args()
kwargs = vars(args)
command = kwargs.pop('command', None)
if not command:
parser.print_help()
sys.exit(1)
if platform.host_system != HostSystem.Windows and os.geteuid() == 0:
print('Do not run ladybird.py as root, your Build directory will become root-owned', file=sys.stderr)
sys.exit(1)
elif platform.host_system == HostSystem.Windows and 'VCINSTALLDIR' not in os.environ:
print('ladybird.py must be run from a Visual Studio enabled environment', file=sys.stderr)
sys.exit(1)
if command == 'build':
build_dir = _configure_main(platform, **kwargs)
_build_main(build_dir, **kwargs)
def _configure_main(platform, **kwargs):
cmake_args = []
host_system = platform.host_system
if host_system == HostSystem.Linux and platform.host_architecture == HostArchitecture.AArch64:
cmake_args.extend(_configure_skia_jemalloc())
lb_source_dir, build_preset_dir, build_env_cmake_args = _configure_build_env(**kwargs)
if build_preset_dir.joinpath('build.ninja').exists() or build_preset_dir.joinpath('ladybird.sln').exists():
return build_preset_dir
_build_vcpkg()
_validate_cmake_version()
cmake_args.extend(build_env_cmake_args)
config_args = [
'cmake',
'--preset',
kwargs.get('preset'),
'-S',
lb_source_dir,
'-B',
build_preset_dir,
]
config_args.extend(cmake_args)
# FIXME: Improve error reporting for vcpkg install failures
# https://github.com/LadybirdBrowser/ladybird/blob/master/Documentation/BuildInstructionsLadybird.md#unable-to-find-a-build-program-corresponding-to-ninja
try:
subprocess.check_call(config_args)
except subprocess.CalledProcessError as e:
_print_process_stderr(e, 'Unable to configure ladybird project')
sys.exit(1)
return build_preset_dir
def _configure_skia_jemalloc():
import resource
page_size = resource.getpagesize()
gn = shutil.which('gn') or None
# https://github.com/LadybirdBrowser/ladybird/issues/261
if page_size != 4096 and gn is None:
print('GN not found! Please build GN from source and put it in $PATH', file=sys.stderr)
sys.exit(1)
pkg_config = shutil.which('pkg-config') or None
cmake_args = []
if pkg_config:
cmake_args.append(f'-DPKG_CONFIG_EXECUTABLE={pkg_config}')
user_vars_cmake_module = Path('Meta/CMake/vcpkg/user-variables.cmake')
user_vars_cmake_module.parent.mkdir(parents=True, exist_ok=True)
with open(user_vars_cmake_module, 'w') as f:
f.writelines([
f'set(PKGCONFIG {pkg_config})',
f'set(GN {gn})',
'',
])
return cmake_args
def _configure_build_env(**kwargs):
preset = kwargs.get('preset')
cc = kwargs.get('cc')
cxx = kwargs.get('cxx')
cmake_args = []
cmake_args.extend([
f'-DCMAKE_C_COMPILER={cc}',
f'-DCMAKE_CXX_COMPILER={cxx}',
])
_ensure_ladybird_source_dir()
lb_source_dir = Path(os.environ.get('LADYBIRD_SOURCE_DIR'))
build_root_dir = lb_source_dir / 'Build'
known_presets = {
'default': build_root_dir / 'release',
'windows_ci_ninja': build_root_dir / 'release',
'windows_dev_ninja': build_root_dir / 'debug',
'windows_dev_msbuild': build_root_dir / 'debug',
'Debug': build_root_dir / 'debug',
'Sanitizer': build_root_dir / 'sanitizers',
'Distribution': build_root_dir / 'distribution',
}
if preset not in known_presets:
print(f'Unknown build preset "{preset}"', file=sys.stderr)
sys.exit(1)
build_preset_dir = known_presets.get(preset)
vcpkg_root = str(build_root_dir / 'vcpkg')
os.environ['VCPKG_ROOT'] = vcpkg_root
os.environ['PATH'] += os.pathsep + str(lb_source_dir.joinpath('Toolchain', 'Local', 'cmake', 'bin'))
os.environ['PATH'] += os.pathsep + str(vcpkg_root)
return lb_source_dir, build_preset_dir, cmake_args
def _build_vcpkg():
sys.path.append(str(Path(__file__).parent.joinpath('..', 'Toolchain')))
# FIXME: Rename main() in BuildVcpkg.py to build_vcpkg() and call that from the scripts __main__
from BuildVcpkg import main as build_vcpkg
build_vcpkg()
def _validate_cmake_version():
# FIXME: This 3.25+ CMake version check may not be needed anymore due to vcpkg downloading a newer version
cmake_pls_install_msg = 'Please install CMake version 3.25 or newer.'
try:
cmake_version_output = subprocess.check_output(
[
'cmake',
'--version',
],
text=True
).strip()
version_match = re.search(r'version\s+(\d+)\.(\d+)\.(\d+)?', cmake_version_output)
if version_match:
major = int(version_match.group(1))
minor = int(version_match.group(2))
patch = int(version_match.group(3))
if major < 3 or (major == 3 and minor < 25):
print(f'CMake version {major}.{minor}.{patch} is too old. {cmake_pls_install_msg}', file=sys.stderr)
sys.exit(1)
else:
print(f'Unable to determine CMake version. {cmake_pls_install_msg}', file=sys.stderr)
sys.exit(1)
except subprocess.CalledProcessError as e:
_print_process_stderr(e, f'CMake not found. {cmake_pls_install_msg}\n')
sys.exit(1)
def _ensure_ladybird_source_dir():
ladybird_source_dir = os.environ.get('LADYBIRD_SOURCE_DIR', None)
ladybird_source_dir = Path(ladybird_source_dir) if ladybird_source_dir else None
if not ladybird_source_dir or not ladybird_source_dir.is_dir():
try:
top_dir = subprocess.check_output(
[
'git',
'rev-parse',
'--show-toplevel',
],
text=True
).strip()
ladybird_source_dir = Path(top_dir)
os.environ['LADYBIRD_SOURCE_DIR'] = str(ladybird_source_dir)
except subprocess.CalledProcessError as e:
_print_process_stderr(e, 'Unable to determine LADYBIRD_SOURCE_DIR:')
sys.exit(1)
return ladybird_source_dir
def _build_main(build_dir, **kwargs):
build_args = [
'cmake',
'--build',
str(build_dir),
'--parallel',
os.environ.get('MAKEJOBS', str(multiprocessing.cpu_count())),
]
build_target = kwargs.get('target', '')
if build_target:
build_args.extend([
'--target',
build_target,
])
build_system_args = kwargs.get('args', [])
if build_system_args:
build_args.append('--')
build_args.extend(build_system_args)
try:
subprocess.check_call(build_args)
except subprocess.CalledProcessError as e:
msg = 'Unable to build ladybird '
if build_target:
msg += f'target "{build_target}"'
else:
msg += 'project'
_print_process_stderr(e, msg)
sys.exit(1)
def _print_process_stderr(e, msg):
err_details = f': {e.stderr}' if e.stderr else ''
print(f'{msg}{err_details}', file=sys.stderr)
if __name__ == '__main__':
main(Platform())

1
Toolchain/__init__.py Normal file
View file

@ -0,0 +1 @@
# NOTE: Provided so ladybird.py can import the BuildVcpkg script as a module