mirror of
https://bitbucket.org/chromiumembedded/cef
synced 2025-06-05 21:39:12 +02:00
tools: Add VSCode setup (fixes #3906)
Add tooling to set up a Visual Studio Code development environment for CEF. See script output for usage. Run: python3 tools/setup_vscode.py
This commit is contained in:
292
tools/setup_vscode.py
Normal file
292
tools/setup_vscode.py
Normal file
@ -0,0 +1,292 @@
|
||||
# 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'
|
||||
)
|
Reference in New Issue
Block a user