Skip to content

Commit

Permalink
pw_ide: clangd wrapper generators
Browse files Browse the repository at this point in the history
clangd must be run within the activated Pigweed environment to pick up
the right paths to the Pigweed toolchains. We point clangd language
servers to these wrapper scripts instead of the bare executable.

We can't just store platform-specific scripts as files in this repo,
because most editors don't let us specify a different clangd location
for different OS's (e.g. `clangd.bat` on Windows and `clangd.sh`
elsewhere). Also, the location of the Pigweed environment can vary. So
we generate wrapper scripts for the user that are appropriate to their
OS and project configuration.

Change-Id: I70a8d3536678329a802507ca950ca79a274aa36f
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/110254
Reviewed-by: Anthony DiGirolamo <tonymd@google.com>
Commit-Queue: Chad Norvell <chadnorvell@google.com>
  • Loading branch information
chadnorvell authored and CQ Bot Account committed Sep 17, 2022
1 parent ca32950 commit e76c4cf
Showing 1 changed file with 88 additions and 1 deletion.
89 changes: 88 additions & 1 deletion pw_ide/py/pw_ide/cpp.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,19 @@
from collections import defaultdict
from dataclasses import dataclass, field
from io import TextIOBase
from inspect import cleandoc
import json
import os
from pathlib import Path
import platform
import re
import stat
from typing import (cast, Dict, Generator, List, Optional, Tuple, TypedDict,
Union)

from pw_ide.exceptions import (BadCompDbException, InvalidTargetException,
MissingCompDbException)
MissingCompDbException,
UnsupportedPlatformException)

from pw_ide.settings import IdeSettings
from pw_ide.symlinks import set_symlink
Expand Down Expand Up @@ -437,3 +441,86 @@ def write_compilation_databases(compdbs: Dict[str, CppCompilationDatabase],
for target, compdb in compdbs.items():
compdb.to_file(settings.working_dir /
compdb_generate_file_path(target))


def make_clangd_script(system: str = platform.system()) -> str:
"""Create a clangd wrapper script appropriate to the platform this is
running on."""

boilerplate = """
# A wrapper around clangd that ensures it is run in the activated Pigweed
# environment, which in turn ensures that the correct toolchain paths are used.
# THIS FILE IS AUTOMATICALLY GENERATED AND SHOULDN'T BE MODIFIED!"""

posix_script = cleandoc(f"""
#!/bin/bash
{boilerplate}
CWD="$(pwd)"
if [ ! -f "$CWD/activate.sh" ]; then
echo "clangd: must be run from workspace root (currently in $CWD)" 1>&2
exit 1
fi
if [ ! -d "$CWD/environment" ]; then
echo "clangd: Pigweed must be bootstrapped" 1>&2
exit 1
fi
[[ -z "$PW_ROOT" ]] && source "$CWD/activate.sh" >/dev/null 2>&1
exec "$PW_PIGWEED_CIPD_INSTALL_DIR/bin/clangd" --query-driver="$PW_PIGWEED_CIPD_INSTALL_DIR/bin/*,$PW_ARM_CIPD_INSTALL_DIR/bin/*" "$@"
""")

windows_script = cleandoc(f"""
:<<"::WINDOWS_ONLY"
@echo off
:<<"::WINDOWS_ONLY"
{boilerplate.replace('#', '::')}
if not exist "%cd%\\activate.sh" {{
echo clangd: must be run from workspace root (currently in %cd%) 1>&2
exit /b 2
}}
if not exist "%cd%\\environment" {{
echo clangd: Pigweed must be bootstrapped 1>&2
exit /b 2
}}
if "%PW_ROOT%" == "" {{
%cd%\\activate.bat >nul 2>&1
}}
start "%PW_PIGWEED_CIPD_INSTALL_DIR%\\bin\\clangd" --query-driver="%PW_PIGWEED_CIPD_INSTALL_DIR%\\bin\\*,%PW_ARM_CIPD_INSTALL_DIR%\\bin\\*" "%*"
::WINDOWS_ONLY
""")

if system == '':
raise UnsupportedPlatformException()

if system == 'Windows':
# On Windows, use a batch script.
return windows_script

# If it's not Windows, assume a POSIX shell script will work.
return posix_script


def write_clangd_wrapper_script(script: str, working_dir: Path) -> None:
"""Write a clangd wrapper script to file and make it executable.
clangd needs to run in the activated environment to detect toolchains
correctly, so we wrap it in this script instead of calling it directly.
This also allows us to abstract over the actual environment directory.
"""

wrapper_path = working_dir / CLANGD_WRAPPER_FILE_NAME

with wrapper_path.open('w') as wrapper_file:
wrapper_file.write(script)

current_stat = wrapper_path.stat()
# This is `chmod +x`.
wrapper_path.chmod(current_stat.st_mode | stat.S_IEXEC)

0 comments on commit e76c4cf

Please sign in to comment.