Files
helium/devutils/setup_adblock.py

174 lines
6.3 KiB
Python
Executable File

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (c) 2026 The Helium Authors
# You can use, redistribute, and/or modify this source code under
# the terms of the GPL-3.0 license that can be found in the LICENSE file.
"""Configure and materialize Helium's native adblock Rust workspace."""
import argparse
import json
import os
import re
import shutil
import subprocess
import sys
from pathlib import Path
_GN_ARG_PATTERN = re.compile(r'^\s*enable_native_adblock\s*=')
def _adblock_enabled():
value = os.environ.get('ENABLE_ADBLOCK', 'false')
if value in ('1', 'true'):
return True
if value in ('0', 'false'):
return False
raise ValueError('ENABLE_ADBLOCK must be 0, 1, false, or true')
def _set_gn_arg(args_gn, enabled):
lines = args_gn.read_text(encoding='utf-8').splitlines()
lines = [line for line in lines if not _GN_ARG_PATTERN.match(line)]
lines.append(f'enable_native_adblock = {str(enabled).lower()}')
args_gn.write_text('\n'.join(lines) + '\n', encoding='utf-8')
def _copy_workspace_inputs(source, destination, chromium_workspace):
(destination / '.cargo').mkdir(parents=True, exist_ok=True)
(destination / 'src').mkdir(parents=True, exist_ok=True)
for relative_path in ('Cargo.toml', 'Cargo.lock', 'gnrt_config.toml',
'src/lib.rs'):
shutil.copyfile(source / relative_path, destination / relative_path)
shutil.copyfile(chromium_workspace / '.cargo/config.toml',
destination / '.cargo/config.toml')
shutil.copyfile(chromium_workspace / 'README.chromium.hbs',
destination / 'README.chromium.hbs')
shutil.copyfile(chromium_workspace / 'BUILD.gn.hbs',
destination / 'BUILD.gn.hbs')
def _crate_epoch(version):
major, minor, patch = version.split('.', 2)
if major != '0':
return f'v{major}'
if minor != '0':
return f'v0_{minor}'
return f"v0_0_{patch.split('-', 1)[0]}"
def _cargo_metadata(src, workspace, environment):
command = [
sys.executable,
str(src / 'tools/crates/run_cargo.py'),
'metadata',
'--locked',
'--offline',
'--format-version',
'1',
'--manifest-path',
str(workspace / 'Cargo.toml'),
]
result = subprocess.run(command,
cwd=workspace,
env=environment,
check=True,
capture_output=True,
text=True)
return json.loads(result.stdout)
def _local_packages(metadata):
workspace_members = set(metadata['workspace_members'])
return [package for package in metadata['packages']
if package['source'] is None and
package['id'] not in workspace_members]
def _generate_forwarders(src, metadata):
for package in _local_packages(metadata):
name = package['name']
version = package['version']
target_name = name.replace('-', '_')
epoch = _crate_epoch(version)
upstream = src / 'third_party/rust' / target_name / epoch
destination = src / 'third_party/rust/helium' / target_name / epoch
if not (upstream / 'BUILD.gn').is_file():
raise FileNotFoundError(
f'missing Chromium GN target for {target_name} {epoch}')
destination.mkdir(parents=True, exist_ok=True)
(destination / 'BUILD.gn').write_text(
'# Copyright 2026 The Helium Authors\n'
'# Use of this source code is governed by a BSD-style license that can be\n'
'# found in the LICENSE file.\n\n'
'group("lib") {\n'
f' public_deps = [ "//third_party/rust/{target_name}/{epoch}:lib" ]\n'
'}\n',
encoding='utf-8')
readme = upstream / 'README.chromium'
if readme.is_file():
shutil.copyfile(readme, destination / 'README.chromium')
def _write_engine_version(workspace, metadata):
versions = {package['version'] for package in metadata['packages']
if package['name'] == 'adblock'}
if len(versions) != 1:
raise ValueError('cargo metadata did not resolve exactly one adblock crate')
version = versions.pop()
(workspace / 'adblock_engine_version.h').write_text(
'// Copyright 2026 The Helium Authors\n'
'// Use of this source code is governed by a BSD-style license that can be\n'
'// found in the LICENSE file.\n\n'
'#ifndef THIRD_PARTY_RUST_HELIUM_CHROMIUM_CRATES_IO_ADBLOCK_ENGINE_VERSION_H_\n'
'#define THIRD_PARTY_RUST_HELIUM_CHROMIUM_CRATES_IO_ADBLOCK_ENGINE_VERSION_H_\n\n'
'namespace helium_adblock {\n\n'
f'inline constexpr char kEngineVersion[] = "adblock-{version}";\n\n'
'} // namespace helium_adblock\n\n'
'#endif // THIRD_PARTY_RUST_HELIUM_CHROMIUM_CRATES_IO_ADBLOCK_ENGINE_VERSION_H_\n',
encoding='utf-8')
def _materialize_workspace(src, gn_dir):
workspace_source = src / 'components/helium_adblock/rs/crates'
workspace = src / 'third_party/rust/helium/chromium_crates_io'
chromium_workspace = src / 'third_party/rust/chromium_crates_io'
_copy_workspace_inputs(workspace_source, workspace, chromium_workspace)
environment = os.environ.copy()
if gn_dir:
environment['PATH'] = (str(gn_dir.resolve()) + os.pathsep +
environment.get('PATH', ''))
command = [sys.executable, 'tools/crates/run_gnrt.py',
'--third-party-workspace', 'helium']
subprocess.run(command + ['vendor'], cwd=src, env=environment, check=True)
metadata = _cargo_metadata(src, workspace, environment)
subprocess.run(command + ['gen'], cwd=src, env=environment, check=True)
_generate_forwarders(src, metadata)
_write_engine_version(workspace, metadata)
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('chromium_src', type=Path)
parser.add_argument('args_gn', type=Path)
parser.add_argument('--gn-dir', type=Path)
args = parser.parse_args()
enabled = _adblock_enabled()
_set_gn_arg(args.args_gn.resolve(), enabled)
if enabled:
_materialize_workspace(args.chromium_src.resolve(), args.gn_dir)
if __name__ == '__main__':
try:
main()
except ValueError as exc:
print(f'error: {exc}', file=sys.stderr)
sys.exit(1)