mirror of
https://bitbucket.org/chromiumembedded/cef
synced 2025-06-05 21:39:12 +02:00
Add tooling to set up a Visual Studio Code development environment for CEF. See script output for usage. Run: python3 tools/setup_vscode.py
293 lines
10 KiB
Python
293 lines
10 KiB
Python
# Copyright (c) 2025 The Chromium Embedded Framework Authors. All rights
|
|
# reserved. Use of this source code is governed by a BSD-style license that
|
|
# can be found in the LICENSE file.
|
|
|
|
from __future__ import absolute_import
|
|
from __future__ import print_function
|
|
from exec_util import exec_cmd
|
|
from file_util import backup_file, copy_file, make_dir, read_file, write_file
|
|
import os
|
|
import platform as python_platform
|
|
import sys
|
|
|
|
COMPILE_COMMANDS_JSON = 'compile_commands.json'
|
|
LLDBINIT = '.lldbinit'
|
|
|
|
|
|
def UpdateCompileCommandsJSON(src_dir, out_dir, create):
|
|
out_path = os.path.join(src_dir, COMPILE_COMMANDS_JSON)
|
|
if not create and not os.path.isfile(out_path):
|
|
return True
|
|
|
|
print(f'Writing {COMPILE_COMMANDS_JSON} for clangd...')
|
|
with open(out_path, 'w') as f:
|
|
result = exec_cmd(
|
|
sys.executable +
|
|
f' tools/clang/scripts/generate_compdb.py -p {out_dir}',
|
|
src_dir,
|
|
output_file=f)
|
|
if result['err']:
|
|
print('ERROR: generate_compdb.py failed: %s' % result['err'])
|
|
return False
|
|
return True
|
|
|
|
|
|
def GetPreferredOutputDirectory(all_dirs):
|
|
if sys.platform == 'win32':
|
|
# Windows machines report 'ARM64' or 'AMD64'.
|
|
machine = 'arm64' if python_platform.machine() == 'ARM64' else 'x64'
|
|
elif sys.platform == 'darwin':
|
|
# Mac machines report 'arm64' or 'x86_64'.
|
|
machine = 'arm64' if python_platform.machine() == 'arm64' else 'x64'
|
|
elif sys.platform.startswith('linux'):
|
|
# Linux machines report 'aarch64', 'armv7l', 'x86_64', 'i386', etc.
|
|
machine = 'arm64' if python_platform.machine() == 'aarch64' else 'x64'
|
|
|
|
# Return the preferred directory that matches the host architecture.
|
|
for dir in (f'Debug_GN_{machine}', f'Release_GN_{machine}'):
|
|
if dir in all_dirs:
|
|
return dir
|
|
|
|
return None
|
|
|
|
|
|
if __name__ == "__main__":
|
|
from optparse import OptionParser
|
|
|
|
desc = """
|
|
This utility sets up Visual Studio Code (VSCode) integration for CEF.
|
|
"""
|
|
|
|
epilog = """
|
|
This utility sets up Visual Studio Code (VSCode) integration for an existing
|
|
CEF/Chromium development environment. See
|
|
https://bitbucket.org/chromiumembedded/cef/wiki/MasterBuildQuickStart.md for
|
|
prerequisite environment setup instructions.
|
|
|
|
The VSCode application and recommended extensions should be installed manually
|
|
upon completion of this setup. Instructions for this will be provided.
|
|
|
|
This setup is an automation and customization of the Chromium setup documented
|
|
at https://chromium.googlesource.com/chromium/src/+/main/docs/vscode.md. After
|
|
completion of this setup the VSCode configuration can be further customized by
|
|
modifying JSON files in the src/.vscode directory (either directly or via the
|
|
VSCode interface).
|
|
|
|
This setup includes configuration of clangd for improved C/C++ IntelliSense.
|
|
This functionality depends on a src/compile_commands.json file that will
|
|
be created by this utility and then regenerated each time cef_create_projects
|
|
is called in the future. Delete this json file manually if you do not wish to
|
|
utilize clangd with VSCode.
|
|
"""
|
|
|
|
class CustomParser(OptionParser):
|
|
|
|
def format_epilog(self, formatter):
|
|
return self.epilog
|
|
|
|
parser = CustomParser(description=desc, epilog=epilog + '\n')
|
|
parser.add_option(
|
|
'-v',
|
|
'--verbose',
|
|
action='store_true',
|
|
dest='verbose',
|
|
default=False,
|
|
help='output detailed status information')
|
|
parser.add_option(
|
|
'--headless',
|
|
action='store_true',
|
|
dest='headless',
|
|
default=False,
|
|
help='run without requiring user interaction')
|
|
parser.add_option(
|
|
'--force-update',
|
|
action='store_true',
|
|
dest='force_update',
|
|
default=False,
|
|
help='force update all JSON configuration files')
|
|
(options, args) = parser.parse_args()
|
|
|
|
print(epilog)
|
|
|
|
if not options.headless:
|
|
input("Press Enter to proceed with setup...")
|
|
|
|
quiet = not options.verbose
|
|
|
|
script_dir = os.path.dirname(__file__)
|
|
cef_dir = os.path.abspath(os.path.join(script_dir, os.pardir))
|
|
src_dir = os.path.abspath(os.path.join(cef_dir, os.pardir))
|
|
chromium_dir = os.path.abspath(os.path.join(src_dir, os.pardir))
|
|
in_dir = os.path.join(cef_dir, 'tools', 'vscode')
|
|
|
|
print('Running setup for VSCode environment...\n')
|
|
|
|
# Determine the platform and architecture.
|
|
# Chromium development only supports ARM64 and x64 host systems. All other
|
|
# configurations will be cross-compiled.
|
|
if sys.platform == 'win32':
|
|
platform = 'windows'
|
|
# Windows machines report 'ARM64' or 'AMD64'.
|
|
machine = 'arm64' if python_platform.machine() == 'ARM64' else 'x64'
|
|
sampleapp = 'cefclient.exe'
|
|
testsapp = 'ceftests.exe'
|
|
elif sys.platform == 'darwin':
|
|
platform = 'mac'
|
|
# Mac machines report 'arm64' or 'x86_64'.
|
|
machine = 'arm64' if python_platform.machine() == 'arm64' else 'x64'
|
|
sampleapp = 'cefclient.app/Contents/MacOS/cefclient'
|
|
testsapp = 'ceftests.app/Contents/MacOS/ceftests'
|
|
elif sys.platform.startswith('linux'):
|
|
platform = 'linux'
|
|
# Linux machines report 'aarch64', 'armv7l', 'x86_64', 'i386', etc.
|
|
machine = 'arm64' if python_platform.machine() == 'aarch64' else 'x64'
|
|
sampleapp = 'cefclient'
|
|
testsapp = 'ceftests'
|
|
else:
|
|
print('ERROR: Unknown operating system platform.')
|
|
sys.exit(1)
|
|
|
|
debug_out_dir = os.path.join(src_dir, 'out', 'Debug_GN_' + machine)
|
|
debug_out_dir_exists = os.path.isdir(debug_out_dir)
|
|
release_out_dir = os.path.join(src_dir, 'out', 'Release_GN_' + machine)
|
|
release_out_dir_exists = os.path.isdir(release_out_dir)
|
|
|
|
if not debug_out_dir_exists and not release_out_dir_exists:
|
|
print(
|
|
f'ERROR: Output directories matching your host architecture ({machine}) do not exist.\n'
|
|
'Check your GN_OUT_CONFIGS environment variable and re-run cef_create_projects before proceeding.'
|
|
)
|
|
sys.exit(1)
|
|
|
|
out_dir = debug_out_dir if debug_out_dir_exists else release_out_dir
|
|
|
|
print(
|
|
f'Configuring VSCode project files for your host architecture ({machine})...\n'
|
|
)
|
|
|
|
variables = {
|
|
'ARCH': machine,
|
|
'DEFAULT': 'Debug' if debug_out_dir_exists else 'Release',
|
|
'DEBUGGER': 'cppvsdbg' if platform == 'windows' else 'cppdbg',
|
|
'EXEEXT': '.exe' if platform == 'windows' else '',
|
|
'SAMPLEAPP': sampleapp,
|
|
'TESTSAPP': testsapp,
|
|
}
|
|
|
|
vscode_dir = os.path.join(src_dir, '.vscode')
|
|
if not os.path.isdir(vscode_dir):
|
|
make_dir(vscode_dir, quiet)
|
|
|
|
change_ct = 0
|
|
|
|
# Update JSON files if necessary.
|
|
for json_name in ('cpp.code-snippets', 'extensions.json', 'keybindings.json',
|
|
'settings.json', 'launch.json', 'tasks.json'):
|
|
out_path = os.path.join(vscode_dir, json_name)
|
|
if os.path.isfile(out_path):
|
|
if not options.force_update:
|
|
print(f'Skipping existing file {json_name}')
|
|
continue
|
|
else:
|
|
print(f'Backing up existing file {json_name}')
|
|
backup_file(out_path)
|
|
|
|
in_path = os.path.join(in_dir, json_name)
|
|
if os.path.isfile(in_path):
|
|
# Copying a CEF file as-is.
|
|
copy_file(in_path, out_path, quiet)
|
|
change_ct += 1
|
|
continue
|
|
|
|
in_path += '.in'
|
|
if os.path.isfile(in_path):
|
|
# Copying a CEF file with variable substitution.
|
|
content = read_file(in_path)
|
|
for name, val in variables.items():
|
|
content = content.replace('{{' + name + '}}', val)
|
|
write_file(out_path, content, quiet=quiet)
|
|
change_ct += 1
|
|
continue
|
|
|
|
in_path = os.path.join(src_dir, 'tools', 'vscode', json_name)
|
|
if os.path.isfile(in_path):
|
|
# Copying a Chromium file as-is.
|
|
copy_file(in_path, out_path, quiet)
|
|
change_ct += 1
|
|
continue
|
|
|
|
print(f'ERROR: Required input file {json_name} does not exist.')
|
|
sys.exit(1)
|
|
|
|
gclient_path = os.path.join(chromium_dir, '.gclient')
|
|
if not os.path.isfile(gclient_path):
|
|
print(f'ERROR: Required input file {gclient_path} does not exist.')
|
|
sys.exit(1)
|
|
|
|
# Setup for clangd.
|
|
# https://chromium.googlesource.com/chromium/src/+/main/docs/clangd.md
|
|
content = read_file(gclient_path)
|
|
if content.find('checkout_clangd') < 0:
|
|
insert = "'custom_vars': {"
|
|
content = content.replace(insert, insert + "'checkout_clangd': True, ")
|
|
write_file(gclient_path, content, quiet=quiet)
|
|
change_ct += 1
|
|
|
|
print('Downloading clangd...')
|
|
result = exec_cmd('gclient sync --with_branch_heads --nohooks', src_dir)
|
|
if len(result['err']) > 0:
|
|
print('ERROR: gclient sync failed: %s' % result['err'])
|
|
sys.exit(1)
|
|
|
|
if not os.path.isfile(os.path.join(src_dir, COMPILE_COMMANDS_JSON)):
|
|
if UpdateCompileCommandsJSON(src_dir, out_dir, create=True):
|
|
change_ct += 1
|
|
else:
|
|
sys.exit(1)
|
|
|
|
if platform == 'mac':
|
|
# Setup for lldb.
|
|
# https://chromium.googlesource.com/chromium/src/+/main/docs/lldbinit.md
|
|
lldbinit_path = os.path.join(src_dir, LLDBINIT)
|
|
if os.path.isfile(lldbinit_path):
|
|
if not options.force_update:
|
|
print(f'Skipping existing file {LLDBINIT}')
|
|
else:
|
|
print(f'Backing up existing file {LLDBINIT}')
|
|
backup_file(lldbinit_path)
|
|
|
|
if not os.path.isfile(lldbinit_path):
|
|
content = "# So that lldbinit.py takes precedence.\n" \
|
|
f"script sys.path[:0] = ['{src_dir}/tools/lldb']\n" \
|
|
"script import lldbinit"
|
|
write_file(lldbinit_path, content, quiet=quiet)
|
|
change_ct += 1
|
|
|
|
if change_ct == 0:
|
|
print('No work performed.')
|
|
else:
|
|
print(f'Updated {change_ct} files.')
|
|
|
|
missing_dirs = []
|
|
if not debug_out_dir_exists:
|
|
missing_dirs.append('Debug')
|
|
if not release_out_dir_exists:
|
|
missing_dirs.append('Release')
|
|
|
|
for dir in missing_dirs:
|
|
print(
|
|
f'\nWARNING: A {dir} output directory matching your host architecture ({machine}) does\n'
|
|
f'not exist. You will not be able to build or run {dir} builds with VSCode.'
|
|
)
|
|
|
|
print(
|
|
'\nFIRST TIME USAGE INSTRUCTIONS\n\n'
|
|
'1. Install VSCode (including command-line integration) by following the instructions at'
|
|
'\n https://code.visualstudio.com/docs/setup/setup-overview'
|
|
'\n\n2. Launch VSCode with the following console commands:\n\n'
|
|
f' $ cd {src_dir}\n'
|
|
' $ code .\n'
|
|
'\n3. Install recommended VSCode extensions when prompted or by following the instructions at'
|
|
'\n https://chromium.googlesource.com/chromium/src/+/main/docs/vscode.md#Install-Recommended-Extensions\n'
|
|
)
|