Compare commits
40 Commits
v0.1.0-alp
...
openspec-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eddb34e34e | ||
|
|
94bc872cdb | ||
|
|
e43bb14998 | ||
|
|
21a969af27 | ||
|
|
084f3b2d22 | ||
|
|
eb0a6b35ee | ||
|
|
bc04845293 | ||
|
|
0a891d8b4a | ||
|
|
6d3b6acc82 | ||
|
|
7ed558c1e7 | ||
|
|
a536865fca | ||
|
|
f8a39147a7 | ||
|
|
46ba1e4db6 | ||
|
|
498c975042 | ||
|
|
848065265c | ||
|
|
f7a8d104ce | ||
|
|
020c2cb3cc | ||
|
|
73afcfbb58 | ||
|
|
ce2f355988 | ||
|
|
5b016b1317 | ||
|
|
c0ffd2369b | ||
|
|
d6733dd43d | ||
|
|
53689f2256 | ||
|
|
f30f68f573 | ||
|
|
dd8f0dd90d | ||
|
|
dee5e136b7 | ||
|
|
4d16cf7b0a | ||
|
|
c6d87cccf3 | ||
|
|
aa3e5111de | ||
|
|
7a29560065 | ||
|
|
7105e36803 | ||
|
|
bc523b0681 | ||
|
|
b8da7e79db | ||
|
|
1da34574c9 | ||
|
|
cec13ec29b | ||
|
|
2214d53ea5 | ||
|
|
8f74adf2b9 | ||
|
|
2c11be9365 | ||
|
|
caf9a172d6 | ||
|
|
8aff090a08 |
@@ -1,5 +1,4 @@
|
||||
# clang-format configuration
|
||||
# compatible with clang-format 18
|
||||
|
||||
UseTab: Never
|
||||
ColumnLimit: 100
|
||||
@@ -73,7 +72,7 @@ SpaceBeforeParensOptions:
|
||||
AfterFunctionDeclarationName: false
|
||||
AfterFunctionDefinitionName: false
|
||||
AfterIfMacros: false
|
||||
AfterOverloadedOperator: true
|
||||
AfterOverloadedOperator: false
|
||||
AfterRequiresInClause: true
|
||||
AfterRequiresInExpression: false
|
||||
BeforeNonEmptyParentheses: false
|
||||
@@ -93,11 +92,62 @@ SpacesInParensOptions:
|
||||
|
||||
SpacesInSquareBrackets: false
|
||||
|
||||
WrapNamespaceBodyWithEmptyLines: Always
|
||||
# Order
|
||||
QualifierAlignment: Custom
|
||||
QualifierOrder: ["constexpr", "const", "inline", "static", "type"]
|
||||
SortIncludes: Never
|
||||
SortIncludes: true
|
||||
SortUsingDeclarations: Never
|
||||
IncludeBlocks: Merge
|
||||
IncludeBlocks: Regroup
|
||||
IncludeCategories:
|
||||
- Regex: '^["<](spdlog|toml\+\+|coraing|cpptrace|flatbuffers)/'
|
||||
Priority: 30
|
||||
SortPriority: 31
|
||||
|
||||
- Regex: '^["<](llvm)/'
|
||||
Priority: 30
|
||||
SortPriority: 32
|
||||
|
||||
- Regex: '^["<](clang)/'
|
||||
Priority: 30
|
||||
SortPriority: 33
|
||||
|
||||
- Regex: '^["<](clang-tidy)/'
|
||||
Priority: 30
|
||||
SortPriority: 34
|
||||
|
||||
- Regex: '^["<](Test)/'
|
||||
Priority: 20
|
||||
SortPriority: 22
|
||||
|
||||
- Regex: "^<.*"
|
||||
Priority: 10
|
||||
SortPriority: 10
|
||||
|
||||
- Regex: '^".*/.*"'
|
||||
Priority: 20
|
||||
SortPriority: 23
|
||||
|
||||
- Regex: ".*"
|
||||
Priority: 20
|
||||
SortPriority: 21
|
||||
|
||||
ForEachMacros: ["REFLECTABLE_RECORD"]
|
||||
NamespaceMacros: [TEST_SUITE]
|
||||
|
||||
KeepEmptyLines:
|
||||
AtEndOfFile: false
|
||||
AtStartOfBlock: false
|
||||
AtStartOfFile: false
|
||||
|
||||
StatementMacros:
|
||||
- DECO_CFG_START
|
||||
- DECO_CFG
|
||||
- DECO_CFG_END
|
||||
- DecoKV
|
||||
- DecoFlag
|
||||
- DecoComma
|
||||
- DecoInput
|
||||
- DecoPack
|
||||
- DecoKVStyled
|
||||
- DecoMulti
|
||||
|
||||
2
.coderabbit.yaml
Normal file
2
.coderabbit.yaml
Normal file
@@ -0,0 +1,2 @@
|
||||
chat:
|
||||
auto_reply: false
|
||||
@@ -1,9 +0,0 @@
|
||||
build/
|
||||
out/
|
||||
.cache
|
||||
|
||||
.clice/
|
||||
.llvm*/
|
||||
.xmake/
|
||||
.vscode/
|
||||
.vs/
|
||||
9
.gitattributes
vendored
Normal file
9
.gitattributes
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
# SCM syntax highlighting & preventing 3-way merges
|
||||
pixi.lock merge=binary linguist-language=YAML linguist-generated=true -diff
|
||||
|
||||
# Force LF line endings for test data so that byte offsets from clang
|
||||
# (which reads from disk) match the content sent by didOpen in tests.
|
||||
tests/data/** text eol=lf
|
||||
|
||||
# Treat trace files as binary to suppress text diffs
|
||||
tests/smoke/*.jsonl linguist-generated=true binary
|
||||
24
.github/ISSUE_TEMPLATE/bug_report.md
vendored
24
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,10 +1,9 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
title: ""
|
||||
labels: ""
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
@@ -12,6 +11,7 @@ A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
@@ -24,15 +24,17 @@ A clear and concise description of what you expected to happen.
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
|
||||
7
.github/ISSUE_TEMPLATE/feature_request.md
vendored
7
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,10 +1,9 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
title: ""
|
||||
labels: ""
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
|
||||
20
.github/actions/setup-pixi/action.yml
vendored
Normal file
20
.github/actions/setup-pixi/action.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
name: "Setup Pixi"
|
||||
description: "setup pixi"
|
||||
|
||||
inputs:
|
||||
environments:
|
||||
description: "The pixi environments to install (e.g. default, docs, test)"
|
||||
required: false
|
||||
default: "default"
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Setup Pixi
|
||||
uses: prefix-dev/setup-pixi@v0.9.3
|
||||
with:
|
||||
pixi-version: v0.62.0
|
||||
environments: ${{ inputs.environments }}
|
||||
activate-environment: true
|
||||
cache: true
|
||||
locked: true
|
||||
45
.github/workflows/benchmark.yml
vendored
Normal file
45
.github/workflows/benchmark.yml
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
name: benchmark
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
benchmark:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-24.04, macos-15, windows-2025]
|
||||
runs-on: ${{ matrix.os }}
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- uses: ./.github/actions/setup-pixi
|
||||
|
||||
- name: Build scan_benchmark
|
||||
run: |
|
||||
pixi run cmake-config RelWithDebInfo ON "-DCLICE_ENABLE_BENCHMARK=ON"
|
||||
cmake --build build/RelWithDebInfo --target scan_benchmark
|
||||
|
||||
- name: Clone LLVM
|
||||
run: git clone --depth 1 https://github.com/llvm/llvm-project.git
|
||||
|
||||
- name: Generate CDB
|
||||
run: |
|
||||
cmake -B llvm-build -G Ninja \
|
||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
|
||||
-DCMAKE_TOOLCHAIN_FILE="$(pwd)/cmake/toolchain.cmake" \
|
||||
-DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra;lld;lldb;mlir;polly;flang;bolt" \
|
||||
-DLLVM_ENABLE_RUNTIMES="compiler-rt;libcxx;libcxxabi;libunwind" \
|
||||
llvm-project/llvm
|
||||
|
||||
- name: Run benchmark
|
||||
run: ./build/RelWithDebInfo/bin/scan_benchmark --runs 20 llvm-build/compile_commands.json
|
||||
|
||||
- name: Stop sccache server
|
||||
if: runner.os == 'Windows'
|
||||
run: pixi run -- sccache --stop-server || true
|
||||
191
.github/workflows/build-llvm.yml
vendored
Normal file
191
.github/workflows/build-llvm.yml
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
name: build llvm
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
# if you want to run this workflow, change the branch name to main,
|
||||
# if you want to turn off it, change it to non existent branch.
|
||||
branches: [main-turn-off]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: windows-2025
|
||||
llvm_mode: Debug
|
||||
lto: OFF
|
||||
- os: windows-2025
|
||||
llvm_mode: RelWithDebInfo
|
||||
lto: OFF
|
||||
- os: windows-2025
|
||||
llvm_mode: RelWithDebInfo
|
||||
lto: ON
|
||||
- os: ubuntu-24.04
|
||||
llvm_mode: Debug
|
||||
lto: OFF
|
||||
- os: ubuntu-24.04
|
||||
llvm_mode: RelWithDebInfo
|
||||
lto: OFF
|
||||
- os: ubuntu-24.04
|
||||
llvm_mode: RelWithDebInfo
|
||||
lto: ON
|
||||
- os: macos-15
|
||||
llvm_mode: Debug
|
||||
lto: OFF
|
||||
- os: macos-15
|
||||
llvm_mode: RelWithDebInfo
|
||||
lto: OFF
|
||||
- os: macos-15
|
||||
llvm_mode: RelWithDebInfo
|
||||
lto: ON
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Free Disk Space
|
||||
if: runner.os == 'Linux'
|
||||
uses: jlumbroso/free-disk-space@main
|
||||
|
||||
- name: Increase Swap Space
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
echo "===== Initial Status ====="
|
||||
sudo swapon --show
|
||||
free -h
|
||||
|
||||
echo "===== Creating Swap File ====="
|
||||
sudo swapoff -a
|
||||
sudo fallocate -l 16G /mnt/swapfile
|
||||
sudo chmod 600 /mnt/swapfile
|
||||
sudo mkswap /mnt/swapfile
|
||||
sudo swapon /mnt/swapfile
|
||||
|
||||
echo "===== Final Status ====="
|
||||
sudo swapon --show
|
||||
free -h
|
||||
df -h
|
||||
|
||||
- name: Setup Pixi
|
||||
uses: prefix-dev/setup-pixi@v0.9.3
|
||||
with:
|
||||
pixi-version: v0.59.0
|
||||
environments: package
|
||||
activate-environment: true
|
||||
cache: true
|
||||
locked: true
|
||||
|
||||
- name: Clone llvm-project (21.1.4)
|
||||
shell: bash
|
||||
run: |
|
||||
git clone --branch llvmorg-21.1.4 --depth 1 https://github.com/llvm/llvm-project.git .llvm
|
||||
|
||||
- name: Build LLVM (install-distribution)
|
||||
shell: bash
|
||||
run: |
|
||||
pixi run build-llvm --llvm-src=.llvm --mode="${{ matrix.llvm_mode }}" --lto="${{ matrix.lto }}" --build-dir=build
|
||||
|
||||
- name: Build clice using installed LLVM
|
||||
shell: bash
|
||||
run: |
|
||||
cmake -B build -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=${{ matrix.llvm_mode }} \
|
||||
-DCMAKE_TOOLCHAIN_FILE=cmake/toolchain.cmake \
|
||||
-DCLICE_ENABLE_TEST=ON \
|
||||
-DCLICE_CI_ENVIRONMENT=ON \
|
||||
-DCLICE_ENABLE_LTO=${{ matrix.lto }} \
|
||||
-DLLVM_INSTALL_PATH=".llvm/build-install"
|
||||
cmake --build build
|
||||
|
||||
- name: Run tests
|
||||
shell: bash
|
||||
run: |
|
||||
EXE_EXT=""
|
||||
if [[ "${{ runner.os }}" == "Windows" ]]; then
|
||||
EXE_EXT=".exe"
|
||||
fi
|
||||
./build/bin/unit_tests${EXE_EXT} --test-dir="./tests/data"
|
||||
uv run --project tests pytest -s --log-cli-level=INFO tests/integration --executable=./build/bin/clice${EXE_EXT}
|
||||
|
||||
- name: Prune LLVM static libraries (Debug/RelWithDebInfo no LTO)
|
||||
if: matrix.llvm_mode == 'Debug' || (matrix.llvm_mode == 'RelWithDebInfo' && matrix.lto == 'OFF')
|
||||
shell: bash
|
||||
run: |
|
||||
MANIFEST="pruned-libs-${{ matrix.os }}.json"
|
||||
echo "LLVM_PRUNED_MANIFEST=${MANIFEST}" >> "${GITHUB_ENV}"
|
||||
python3 scripts/prune-llvm-bin.py \
|
||||
--action discover \
|
||||
--install-dir ".llvm/build-install/lib" \
|
||||
--build-dir "build" \
|
||||
--max-attempts 60 \
|
||||
--sleep-seconds 60 \
|
||||
--manifest "${MANIFEST}"
|
||||
|
||||
- name: Upload pruned-libs manifest
|
||||
if: matrix.llvm_mode == 'RelWithDebInfo' && matrix.lto == 'OFF'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: llvm-pruned-libs-${{ matrix.os }}
|
||||
path: ${{ env.LLVM_PRUNED_MANIFEST }}
|
||||
if-no-files-found: error
|
||||
compression-level: 0
|
||||
|
||||
- name: Apply pruned-libs manifest (RelWithDebInfo + LTO)
|
||||
if: matrix.llvm_mode == 'RelWithDebInfo' && matrix.lto == 'ON'
|
||||
shell: bash
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
MANIFEST="pruned-libs-${{ matrix.os }}.json"
|
||||
python3 scripts/prune-llvm-bin.py \
|
||||
--action apply \
|
||||
--manifest "${MANIFEST}" \
|
||||
--install-dir ".llvm/build-install/lib" \
|
||||
--build-dir "build" \
|
||||
--gh-run-id "${{ github.run_id }}" \
|
||||
--gh-artifact "llvm-pruned-libs-${{ matrix.os }}" \
|
||||
--gh-download-dir "artifacts" \
|
||||
--max-attempts 60 \
|
||||
--sleep-seconds 60
|
||||
|
||||
- name: Package LLVM install directory
|
||||
shell: bash
|
||||
run: |
|
||||
MODE_TAG="releasedbg"
|
||||
if [[ "${{ matrix.llvm_mode }}" == "Debug" ]]; then
|
||||
MODE_TAG="debug"
|
||||
fi
|
||||
|
||||
ARCH="x64"
|
||||
PLATFORM="linux"
|
||||
TOOLCHAIN="gnu"
|
||||
if [[ "${{ matrix.os }}" == windows-* ]]; then
|
||||
PLATFORM="windows"
|
||||
TOOLCHAIN="msvc"
|
||||
elif [[ "${{ matrix.os }}" == macos-* ]]; then
|
||||
ARCH="arm64"
|
||||
PLATFORM="macos"
|
||||
TOOLCHAIN="clang"
|
||||
fi
|
||||
|
||||
SUFFIX=""
|
||||
if [[ "${{ matrix.lto }}" == "ON" ]]; then
|
||||
SUFFIX="-lto"
|
||||
fi
|
||||
if [[ "${{ matrix.llvm_mode }}" == "Debug" ]]; then
|
||||
SUFFIX="${SUFFIX}-asan"
|
||||
fi
|
||||
|
||||
ARCHIVE="${ARCH}-${PLATFORM}-${TOOLCHAIN}-${MODE_TAG}${SUFFIX}.tar.xz"
|
||||
|
||||
set -eo pipefail
|
||||
tar -C .llvm -cf - build-install | xz -T0 -9 -c > "${ARCHIVE}"
|
||||
echo "LLVM_INSTALL_ARCHIVE=${ARCHIVE}" >> "${GITHUB_ENV}"
|
||||
|
||||
- name: Upload LLVM install artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ env.LLVM_INSTALL_ARCHIVE }}
|
||||
path: ${{ env.LLVM_INSTALL_ARCHIVE }}
|
||||
if-no-files-found: error
|
||||
50
.github/workflows/check-format.yml
vendored
50
.github/workflows/check-format.yml
vendored
@@ -1,48 +1,34 @@
|
||||
name: format
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
pull_request:
|
||||
branches: [main]
|
||||
workflow_call:
|
||||
|
||||
jobs:
|
||||
check:
|
||||
check-format:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
- uses: ./.github/actions/setup-pixi
|
||||
with:
|
||||
environments: format
|
||||
|
||||
- name: Run check
|
||||
id: precommit
|
||||
run: |
|
||||
uv venv
|
||||
uv pip install pre-commit
|
||||
uv run pre-commit run --all-files
|
||||
- name: Run formatter
|
||||
run: pixi run format
|
||||
continue-on-error: true
|
||||
|
||||
- name: Show diff on failure
|
||||
if: steps.precommit.outcome == 'failure'
|
||||
run: |
|
||||
echo "❌ pre-commit found issues that it auto-fixed."
|
||||
echo " Please run 'pre-commit run --all-files' locally,"
|
||||
echo " commit the changes, and push again."
|
||||
echo ""
|
||||
echo "👇 The required changes are shown below:"
|
||||
git diff
|
||||
|
||||
- name: Fail the job if pre-commit failed
|
||||
if: steps.precommit.outcome == 'failure'
|
||||
run: |
|
||||
echo "pre-commit checks failed."
|
||||
exit 1
|
||||
|
||||
- name: Auto correct
|
||||
uses: huacnlee/autocorrect-action@main
|
||||
uses: huacnlee/autocorrect-action@v2
|
||||
with:
|
||||
args: --lint ./docs
|
||||
continue-on-error: true
|
||||
|
||||
- name: Check diff
|
||||
run: |
|
||||
if ! git diff --quiet; then
|
||||
echo "::error::Formatting changes detected. Please run 'pixi run format' and commit the result."
|
||||
git --no-pager diff --stat
|
||||
git --no-pager diff
|
||||
exit 1
|
||||
fi
|
||||
|
||||
107
.github/workflows/cmake.yml
vendored
107
.github/workflows/cmake.yml
vendored
@@ -1,107 +0,0 @@
|
||||
name: cmake
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- ".github/workflows/cmake.yml"
|
||||
- "include/**"
|
||||
- "src/**"
|
||||
- "tests/**"
|
||||
- "CMakeLists.txt"
|
||||
pull_request:
|
||||
branches: [main]
|
||||
paths:
|
||||
- ".github/workflows/cmake.yml"
|
||||
- "include/**"
|
||||
- "src/**"
|
||||
- "tests/**"
|
||||
- "config/**"
|
||||
- "CMakeLists.txt"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: windows-2025
|
||||
build_type: RelWithDebInfo
|
||||
cc: clang
|
||||
cxx: clang++
|
||||
- os: ubuntu-24.04
|
||||
build_type: Debug
|
||||
cc: clang-20
|
||||
cxx: clang++-20
|
||||
- os: macos-15
|
||||
build_type: Debug
|
||||
cc: clang
|
||||
cxx: clang++
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- name: Setup dependencies (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
uses: MinoruSekine/setup-scoop@v4.0.1
|
||||
with:
|
||||
buckets: main
|
||||
apps: ninja
|
||||
|
||||
- name: Setup dependencies (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install -y gcc-14 g++-14 libstdc++-14-dev
|
||||
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 100
|
||||
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-14 100
|
||||
sudo update-alternatives --set gcc /usr/bin/gcc-14
|
||||
sudo update-alternatives --set g++ /usr/bin/g++-14
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
chmod +x llvm.sh
|
||||
sudo ./llvm.sh 20 all
|
||||
sudo apt install -y cmake ninja-build
|
||||
|
||||
- name: Setup dependencies (MacOS)
|
||||
if: runner.os == 'macOS'
|
||||
env:
|
||||
HOMEBREW_NO_AUTO_UPDATE: 1
|
||||
run: |
|
||||
brew install llvm@20 lld@20
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup msvc sysroot for cmake
|
||||
if: runner.os == 'Windows'
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
|
||||
- name: Build clice
|
||||
shell: bash
|
||||
run: |
|
||||
if [[ "${{ runner.os }}" == "macOS" ]]; then
|
||||
export PATH="/opt/homebrew/opt/llvm@20/bin:/opt/homebrew/opt/lld@20/bin:$PATH"
|
||||
fi
|
||||
|
||||
cmake -B build -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \
|
||||
-DCMAKE_C_COMPILER=${{ matrix.cc }} \
|
||||
-DCMAKE_CXX_COMPILER=${{ matrix.cxx }} \
|
||||
-DCLICE_ENABLE_TEST=ON \
|
||||
-DCLICE_CI_ENVIRONMENT=ON
|
||||
|
||||
cmake --build build
|
||||
|
||||
- name: Install uv for integration tests
|
||||
uses: astral-sh/setup-uv@v6
|
||||
|
||||
- name: Run tests
|
||||
shell: bash
|
||||
run: |
|
||||
EXE_EXT=""
|
||||
if [[ "${{ runner.os }}" == "Windows" ]]; then
|
||||
EXE_EXT=".exe"
|
||||
fi
|
||||
|
||||
./build/bin/unit_tests${EXE_EXT} --test-dir="./tests/data"
|
||||
uv run pytest -s --log-cli-level=INFO tests/integration --executable=./build/bin/clice${EXE_EXT}
|
||||
30
.github/workflows/deploy-docs.yml
vendored
Normal file
30
.github/workflows/deploy-docs.yml
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
name: deploy
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
jobs:
|
||||
deploy-docs:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- uses: ./.github/actions/setup-pixi
|
||||
with:
|
||||
environments: node
|
||||
|
||||
- name: Build docs
|
||||
run: pixi run build-docs
|
||||
|
||||
- name: Deploy to GitHub Pages
|
||||
uses: peaceiris/actions-gh-pages@v4
|
||||
if: github.ref == 'refs/heads/main'
|
||||
with:
|
||||
personal_token: ${{ secrets.PUBLISH_DOCS }}
|
||||
external_repository: clice-io/docs
|
||||
publish_dir: ./docs/.vitepress/dist
|
||||
destination_dir: clice
|
||||
keep_files: true
|
||||
43
.github/workflows/deploy.yml
vendored
43
.github/workflows/deploy.yml
vendored
@@ -1,43 +0,0 @@
|
||||
name: deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- "docs/**"
|
||||
- ".github/workflows/deploy.yml"
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: "npm"
|
||||
cache-dependency-path: docs/package-lock.json
|
||||
|
||||
- name: Install dependencies in docs
|
||||
run: |
|
||||
cd docs
|
||||
npm install
|
||||
|
||||
- name: Build docs
|
||||
run: |
|
||||
cd docs
|
||||
npm run docs:build
|
||||
|
||||
- name: Deploy to GitHub Pages
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: docs/.vitepress/dist
|
||||
cname: clice.io
|
||||
54
.github/workflows/docker.yml
vendored
54
.github/workflows/docker.yml
vendored
@@ -1,54 +0,0 @@
|
||||
name: docker
|
||||
on:
|
||||
release:
|
||||
types: [created]
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
PLATFORMS: linux/amd64,darwin/arm64
|
||||
|
||||
jobs:
|
||||
build-and-publish:
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup Docker buildx
|
||||
uses: docker/setup-buildx-action@v2.5.0
|
||||
with:
|
||||
platforms: ${{ env.PLATFORMS }}
|
||||
|
||||
- name: Log into registry ${{ env.REGISTRY }}
|
||||
uses: docker/login-action@v2.1.0
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract Docker metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4.3.0
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
|
||||
# https://stackoverflow.com/questions/71157844/how-can-i-copy-git-directory-to-the-container-with-github-actions
|
||||
- name: Build and push Docker image
|
||||
id: build-and-push
|
||||
uses: docker/build-push-action@v4.0.0
|
||||
with:
|
||||
push: true
|
||||
context: .
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
platforms: ${{ env.PLATFORMS }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
119
.github/workflows/main.yml
vendored
Normal file
119
.github/workflows/main.yml
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
name: main
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
tags: ["v*"]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
changes:
|
||||
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: read
|
||||
outputs:
|
||||
format: ${{ steps.filter.outputs.format }}
|
||||
docs: ${{ steps.filter.outputs.docs }}
|
||||
clice: ${{ steps.filter.outputs.clice }}
|
||||
vscode: ${{ steps.filter.outputs.vscode }}
|
||||
cmake: ${{ steps.filter.outputs.cmake }}
|
||||
xmake: ${{ steps.filter.outputs.xmake }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dorny/paths-filter@v3
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
format:
|
||||
- '**/*.{h,c,cpp,hpp,ts,js,lua,md,yml,yaml}'
|
||||
docs:
|
||||
- 'docs/**'
|
||||
- '.github/workflows/deploy-docs.yml'
|
||||
clice:
|
||||
- 'src/**'
|
||||
- 'include/**'
|
||||
- 'CMakeLists.txt'
|
||||
- '.github/workflows/publish-clice.yml'
|
||||
vscode:
|
||||
- 'editors/vscode/**'
|
||||
- '.github/workflows/publish-vscode.yml'
|
||||
cmake:
|
||||
- 'CMakeLists.txt'
|
||||
- 'src/**'
|
||||
- 'include/**'
|
||||
- 'tests/**'
|
||||
- 'config/**'
|
||||
- '.github/workflows/test-cmake.yml'
|
||||
xmake:
|
||||
- 'xmake.lua'
|
||||
- 'src/**'
|
||||
- 'include/**'
|
||||
- 'tests/**'
|
||||
- 'config/**'
|
||||
- '.github/workflows/test-xmake.yml'
|
||||
|
||||
format:
|
||||
needs: changes
|
||||
if: ${{ needs.changes.outputs.format == 'true' }}
|
||||
uses: ./.github/workflows/check-format.yml
|
||||
|
||||
deploy:
|
||||
needs: changes
|
||||
if: ${{ needs.changes.outputs.docs == 'true' }}
|
||||
permissions:
|
||||
contents: write
|
||||
uses: ./.github/workflows/deploy-docs.yml
|
||||
secrets: inherit
|
||||
|
||||
# clice:
|
||||
# needs: changes
|
||||
# if: ${{ needs.changes.outputs.clice == 'true' }}
|
||||
# uses: ./.github/workflows/publish-clice.yml
|
||||
|
||||
vscode:
|
||||
needs: changes
|
||||
if: ${{ needs.changes.outputs.vscode == 'true' }}
|
||||
uses: ./.github/workflows/publish-vscode.yml
|
||||
|
||||
cmake:
|
||||
needs: changes
|
||||
if: ${{ needs.changes.outputs.cmake == 'true' }}
|
||||
uses: ./.github/workflows/test-cmake.yml
|
||||
|
||||
# xmake:
|
||||
# needs: changes
|
||||
# if: ${{ needs.changes.outputs.xmake == 'true' }}
|
||||
# uses: ./.github/workflows/test-xmake.yml
|
||||
|
||||
release-clice:
|
||||
permissions:
|
||||
contents: write
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: ./.github/workflows/publish-clice.yml
|
||||
secrets: inherit
|
||||
|
||||
release-vscode:
|
||||
permissions:
|
||||
contents: write
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: ./.github/workflows/publish-vscode.yml
|
||||
secrets: inherit
|
||||
|
||||
checks-passed:
|
||||
if: ${{ always() && !startsWith(github.ref, 'refs/tags/') }}
|
||||
needs:
|
||||
- format
|
||||
- deploy
|
||||
# - clice
|
||||
- vscode
|
||||
- cmake
|
||||
# - xmake
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check results
|
||||
uses: re-actors/alls-green@release/v1
|
||||
with:
|
||||
allowed-skips: format,deploy,clice,vscode,cmake,xmake
|
||||
jobs: ${{ toJSON(needs) }}
|
||||
137
.github/workflows/package.yml
vendored
137
.github/workflows/package.yml
vendored
@@ -1,137 +0,0 @@
|
||||
name: package
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
pull_request:
|
||||
branches: [main]
|
||||
paths:
|
||||
- ".github/workflows/package.yml"
|
||||
|
||||
jobs:
|
||||
package:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: windows-2025
|
||||
artifact_name: clice.zip
|
||||
asset_name: clice-x64-windows-msvc.zip
|
||||
symbol_artifact_name: clice-symbol.zip
|
||||
symbol_asset_name: clice-x64-windows-msvc-symbol.zip
|
||||
toolchain: clang-cl
|
||||
|
||||
- os: ubuntu-24.04
|
||||
artifact_name: clice.tar.gz
|
||||
asset_name: clice-x86_64-linux-gnu.tar.gz
|
||||
symbol_artifact_name: clice-symbol.tar.gz
|
||||
symbol_asset_name: clice-x86_64-linux-gnu-symbol.tar.gz
|
||||
toolchain: clang-20
|
||||
|
||||
- os: macos-15
|
||||
artifact_name: clice.tar.gz
|
||||
asset_name: clice-arm64-macos-darwin.tar.gz
|
||||
symbol_artifact_name: clice-symbol.tar.gz
|
||||
symbol_asset_name: clice-arm64-macos-darwin-symbol.tar.gz
|
||||
toolchain: clang
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- name: Free disk space (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
uses: jlumbroso/free-disk-space@main
|
||||
|
||||
- name: Increase swap file size (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
echo "===== Initial Status ====="
|
||||
sudo swapon --show
|
||||
free -h
|
||||
|
||||
echo "===== Creating Swap File ====="
|
||||
sudo swapoff -a
|
||||
sudo fallocate -l 16G /mnt/swapfile
|
||||
sudo chmod 600 /mnt/swapfile
|
||||
sudo mkswap /mnt/swapfile
|
||||
sudo swapon /mnt/swapfile
|
||||
|
||||
echo "===== Final Status ====="
|
||||
sudo swapon --show
|
||||
free -h
|
||||
df -h
|
||||
|
||||
- name: Setup dependencies (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install -y gcc-14 g++-14 libstdc++-14-dev cmake ninja-build
|
||||
|
||||
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 100
|
||||
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-14 100
|
||||
sudo update-alternatives --set gcc /usr/bin/gcc-14
|
||||
sudo update-alternatives --set g++ /usr/bin/g++-14
|
||||
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
chmod +x llvm.sh
|
||||
sudo ./llvm.sh 20 all
|
||||
|
||||
- name: Setup dependencies (MacOS)
|
||||
if: runner.os == 'macOS'
|
||||
env:
|
||||
HOMEBREW_NO_AUTO_UPDATE: 1
|
||||
run: |
|
||||
brew install llvm@20 lld@20
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup xmake
|
||||
uses: xmake-io/github-action-setup-xmake@v1
|
||||
with:
|
||||
xmake-version: 3.0.4
|
||||
actions-cache-folder: ".xmake-cache"
|
||||
actions-cache-key: ${{ matrix.os }}
|
||||
package-cache: true
|
||||
package-cache-key: ${{ matrix.os }}-pkg-release-v1
|
||||
build-cache: true
|
||||
build-cache-key: ${{ matrix.os }}-build-release-v1
|
||||
|
||||
- name: Configure and Package
|
||||
shell: bash
|
||||
run: |
|
||||
if [[ "${{ runner.os }}" == "Windows" ]]; then
|
||||
xmake config --yes --enable_test=n --dev=n --release=y --mode=releasedbg --toolchain=${{ matrix.toolchain }} -p windows
|
||||
elif [[ "${{ runner.os }}" == "Linux" ]]; then
|
||||
xmake config --yes --enable_test=n --dev=n --release=y --mode=releasedbg --toolchain=${{ matrix.toolchain }}
|
||||
elif [[ "${{ runner.os }}" == "macOS" ]]; then
|
||||
export PATH="/opt/homebrew/opt/llvm@20/bin:/opt/homebrew/opt/lld@20/bin:$PATH"
|
||||
xmake config --yes --enable_test=n --dev=n --release=y --mode=releasedbg --toolchain=${{ matrix.toolchain }} --sdk=/opt/homebrew/opt/llvm@20
|
||||
fi
|
||||
|
||||
xmake pack -v
|
||||
|
||||
- name: Upload Main Package to Release
|
||||
if: github.event_name == 'push'
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: build/xpack/clice/${{ matrix.artifact_name }}
|
||||
asset_name: ${{ matrix.asset_name }}
|
||||
tag: ${{ github.ref }}
|
||||
overwrite: true
|
||||
|
||||
- name: Upload Symbol Package to Release
|
||||
if: github.event_name == 'push'
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: build/xpack/clice/${{ matrix.symbol_artifact_name }}
|
||||
asset_name: ${{ matrix.symbol_asset_name }}
|
||||
tag: ${{ github.ref }}
|
||||
overwrite: true
|
||||
79
.github/workflows/publish-clice.yml
vendored
Normal file
79
.github/workflows/publish-clice.yml
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
name: clice
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
jobs:
|
||||
publish-clice:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: windows-2025
|
||||
artifact_name: clice.zip
|
||||
asset_name: clice-x64-windows-msvc.zip
|
||||
symbol_artifact_name: clice-symbol.zip
|
||||
symbol_asset_name: clice-x64-windows-msvc-symbol.zip
|
||||
|
||||
- os: ubuntu-24.04
|
||||
artifact_name: clice.tar.gz
|
||||
asset_name: clice-x86_64-linux-gnu.tar.gz
|
||||
symbol_artifact_name: clice-symbol.tar.gz
|
||||
symbol_asset_name: clice-x86_64-linux-gnu-symbol.tar.gz
|
||||
|
||||
- os: macos-15
|
||||
artifact_name: clice.tar.gz
|
||||
asset_name: clice-arm64-macos-darwin.tar.gz
|
||||
symbol_artifact_name: clice-symbol.tar.gz
|
||||
symbol_asset_name: clice-arm64-macos-darwin-symbol.tar.gz
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup xmake
|
||||
uses: xmake-io/github-action-setup-xmake@v1
|
||||
with:
|
||||
xmake-version: 3.0.5
|
||||
actions-cache-folder: ".xmake-cache"
|
||||
actions-cache-key: ${{ matrix.os }}
|
||||
package-cache: true
|
||||
package-cache-key: ${{ matrix.os }}-pkg-release-v1
|
||||
build-cache: true
|
||||
build-cache-key: ${{ matrix.os }}-build-release-v1
|
||||
|
||||
- uses: ./.github/actions/setup-pixi
|
||||
with:
|
||||
environments: package
|
||||
|
||||
- name: Remove ci llvm toolchain on Windows
|
||||
if: runner.os == 'Windows'
|
||||
run: |
|
||||
# @see https://github.com/xmake-io/xmake/issues/7158
|
||||
xmake lua os.rmdir "C:/Program Files/Microsoft Visual Studio/2022/Enterprise/VC/Tools/Llvm"
|
||||
xmake lua os.rmdir "C:/Program Files/LLVM"
|
||||
|
||||
- name: Package
|
||||
run: pixi run package
|
||||
|
||||
- name: Upload Main Package to Release
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: build/xpack/clice/${{ matrix.artifact_name }}
|
||||
asset_name: ${{ matrix.asset_name }}
|
||||
tag: ${{ github.ref }}
|
||||
overwrite: true
|
||||
|
||||
- name: Upload Symbol Package to Release
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: build/xpack/clice/${{ matrix.symbol_artifact_name }}
|
||||
asset_name: ${{ matrix.symbol_asset_name }}
|
||||
tag: ${{ github.ref }}
|
||||
overwrite: true
|
||||
38
.github/workflows/publish-vscode.yml
vendored
Normal file
38
.github/workflows/publish-vscode.yml
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
name: vscode
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
jobs:
|
||||
publish-vscode:
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: editors/vscode
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- uses: ./.github/actions/setup-pixi
|
||||
with:
|
||||
environments: node
|
||||
|
||||
- name: Publish and Package to Marketplace
|
||||
env:
|
||||
VSCE_PAT: ${{ secrets.VSCE_PAT }}
|
||||
run: |
|
||||
FLAG="${{ contains(github.ref_name, '-') && '--pre-release' || '' }}"
|
||||
pixi run build-vscode $FLAG
|
||||
|
||||
if [[ "${{ github.ref }}" == "refs/tags/"* ]]; then
|
||||
pixi run publish-vscode -p "$VSCE_PAT" $FLAG
|
||||
fi
|
||||
|
||||
- name: Upload .vsix to Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
with:
|
||||
files: "editors/vscode/*.vsix"
|
||||
tag_name: ${{ github.ref }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
69
.github/workflows/test-cmake.yml
vendored
Normal file
69
.github/workflows/test-cmake.yml
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
name: cmake
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
env:
|
||||
CCACHE_DIR: ${{ github.workspace }}/.cache/ccache
|
||||
SCCACHE_DIR: ${{ github.workspace }}/.cache/sccache
|
||||
CCACHE_BASEDIR: ${{ github.workspace }}
|
||||
SCCACHE_BASEDIRS: ${{ github.workspace }}
|
||||
CCACHE_COMPILERCHECK: content
|
||||
CCACHE_MAXSIZE: 2G
|
||||
SCCACHE_CACHE_SIZE: 2G
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [windows-2025, ubuntu-24.04, macos-15]
|
||||
build_type: [Debug, RelWithDebInfo]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- uses: ./.github/actions/setup-pixi
|
||||
|
||||
- name: Restore compiler cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ runner.os == 'Windows' && '.cache/sccache' || '.cache/ccache' }}
|
||||
key: ${{ runner.os }}-${{ matrix.build_type }}-ccache-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ matrix.build_type }}-ccache-
|
||||
|
||||
- name: Zero cache stats
|
||||
run: |
|
||||
if [ "$RUNNER_OS" = "Windows" ]; then
|
||||
pixi run -- sccache --stop-server || true
|
||||
pixi run -- sccache --zero-stats || true
|
||||
else
|
||||
pixi run -- ccache --zero-stats || true
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- name: Build
|
||||
run: pixi run build ${{ matrix.build_type }} ON
|
||||
|
||||
- name: Unit Test
|
||||
run: pixi run unit-test ${{ matrix.build_type }}
|
||||
|
||||
- name: Integration Test
|
||||
run: pixi run integration-test ${{ matrix.build_type }}
|
||||
|
||||
- name: Smoke Test
|
||||
if: success() || failure()
|
||||
run: pixi run smoke-test ${{ matrix.build_type }}
|
||||
|
||||
- name: Print cache stats and stop server
|
||||
if: always()
|
||||
run: |
|
||||
if [ "$RUNNER_OS" = "Windows" ]; then
|
||||
pixi run -- sccache --show-stats
|
||||
pixi run -- sccache --stop-server || true
|
||||
else
|
||||
pixi run -- ccache --show-stats
|
||||
fi
|
||||
shell: bash
|
||||
42
.github/workflows/test-xmake.yml
vendored
Normal file
42
.github/workflows/test-xmake.yml
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
name: xmake
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [windows-2025, ubuntu-24.04, macos-15]
|
||||
build_type: [debug, releasedbg]
|
||||
exclude:
|
||||
- os: windows-2025
|
||||
build_type: debug
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup xmake
|
||||
uses: xmake-io/github-action-setup-xmake@v1
|
||||
with:
|
||||
xmake-version: 3.0.5
|
||||
actions-cache-folder: ".xmake-cache"
|
||||
actions-cache-key: ${{ matrix.os }}
|
||||
package-cache: true
|
||||
package-cache-key: ${{ matrix.os }}-pixi
|
||||
build-cache: true
|
||||
build-cache-key: ${{ matrix.os }}-${{ matrix.build_type }}
|
||||
|
||||
- uses: ./.github/actions/setup-pixi
|
||||
|
||||
- name: Build
|
||||
run: pixi run xmake ${{ matrix.build_type }}
|
||||
|
||||
- name: Test
|
||||
run: pixi run xmake-test
|
||||
|
||||
- name: Remove llvm package (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
run: xmake require --uninstall clice-llvm
|
||||
38
.github/workflows/upload-llvm.yml
vendored
Normal file
38
.github/workflows/upload-llvm.yml
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
name: upload-llvm
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
# if you want to run this workflow, change the branch name to main,
|
||||
# if you want to turn off it, change it to non existent branch.
|
||||
branches: [main-turn-off]
|
||||
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
workflow_id:
|
||||
description: "Workflow run ID to pull artifacts from"
|
||||
required: true
|
||||
type: string
|
||||
version:
|
||||
description: "Release version/tag to publish (e.g., v1.2.3)"
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
upload:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Download artifacts from workflow
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: scripts/download-llvm.sh "${{ inputs.workflow_id }}"
|
||||
|
||||
- name: Recreate release with artifacts
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.UPLOAD_LLVM }}
|
||||
TARGET_REPO: clice-io/clice-llvm
|
||||
run: python3 scripts/upload-llvm.py "${{ inputs.version }}" "${TARGET_REPO}" "${{ inputs.workflow_id }}"
|
||||
100
.github/workflows/xmake.yml
vendored
100
.github/workflows/xmake.yml
vendored
@@ -1,100 +0,0 @@
|
||||
name: xmake
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- ".github/workflows/xmake.yml"
|
||||
- "include/**"
|
||||
- "src/**"
|
||||
- "tests/**"
|
||||
- "xmake.lua"
|
||||
pull_request:
|
||||
branches: [main]
|
||||
paths:
|
||||
- ".github/workflows/xmake.yml"
|
||||
- "include/**"
|
||||
- "src/**"
|
||||
- "tests/**"
|
||||
- "config/**"
|
||||
- "xmake.lua"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [windows-2025, ubuntu-24.04, macos-15]
|
||||
build_type: [debug, releasedbg]
|
||||
exclude:
|
||||
- os: windows-2025
|
||||
build_type: debug
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- name: Setup dependencies (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install -y gcc-14 g++-14 libstdc++-14-dev
|
||||
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 100
|
||||
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-14 100
|
||||
sudo update-alternatives --set gcc /usr/bin/gcc-14
|
||||
sudo update-alternatives --set g++ /usr/bin/g++-14
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
chmod +x llvm.sh
|
||||
sudo ./llvm.sh 20 all
|
||||
sudo apt install -y cmake ninja-build
|
||||
|
||||
- name: Setup dependencies (MacOS)
|
||||
if: runner.os == 'macOS'
|
||||
env:
|
||||
HOMEBREW_NO_AUTO_UPDATE: 1
|
||||
run: |
|
||||
brew install llvm@20 lld@20
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup xmake
|
||||
uses: xmake-io/github-action-setup-xmake@v1
|
||||
with:
|
||||
xmake-version: 3.0.4
|
||||
actions-cache-folder: ".xmake-cache"
|
||||
actions-cache-key: ${{ matrix.os }}
|
||||
package-cache: true
|
||||
package-cache-key: ${{ matrix.os }}
|
||||
build-cache: true
|
||||
build-cache-key: ${{ matrix.os }}-${{ matrix.build_type }}
|
||||
|
||||
- name: Build clice
|
||||
shell: bash
|
||||
run: |
|
||||
if [[ "${{ runner.os }}" == "Windows" ]]; then
|
||||
xmake config --yes --ci=y --mode=${{ matrix.build_type }} --toolchain=clang -p windows
|
||||
elif [[ "${{ runner.os }}" == "Linux" ]]; then
|
||||
xmake config --yes --ci=y --mode=${{ matrix.build_type }} --toolchain=clang-20
|
||||
elif [[ "${{ runner.os }}" == "macOS" ]]; then
|
||||
export PATH="/opt/homebrew/opt/llvm@20/bin:/opt/homebrew/opt/lld@20/bin:$PATH"
|
||||
xmake config --yes --ci=y --mode=${{ matrix.build_type }} --toolchain=clang --sdk=/opt/homebrew/opt/llvm@20
|
||||
fi
|
||||
|
||||
xmake build --verbose --diagnosis --all
|
||||
|
||||
- name: Install uv for integration tests
|
||||
uses: astral-sh/setup-uv@v6
|
||||
|
||||
- name: Run tests
|
||||
shell: bash
|
||||
run: |
|
||||
# Workaround for macOS
|
||||
if [[ "${{ runner.os }}" == "macOS" ]]; then
|
||||
export PATH="/opt/homebrew/opt/llvm@20/bin:/opt/homebrew/opt/lld@20/bin:$PATH"
|
||||
fi
|
||||
|
||||
xmake test --verbose
|
||||
|
||||
- name: Remove llvm package (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
run: rm -rf /home/runner/.xmake/packages/c/clice-llvm
|
||||
56
.gitignore
vendored
56
.gitignore
vendored
@@ -16,10 +16,6 @@
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Fortran module files
|
||||
*.mod
|
||||
*.smod
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
@@ -31,28 +27,46 @@
|
||||
*.out
|
||||
*.app
|
||||
|
||||
# Evil things
|
||||
.claude
|
||||
# Fortran module files
|
||||
*.mod
|
||||
*.smod
|
||||
|
||||
# LSP & IDE
|
||||
compile_commands.json
|
||||
.vscode/
|
||||
.vs/
|
||||
.idea/
|
||||
|
||||
temp/
|
||||
# Build & Toolchain
|
||||
*build*/
|
||||
temp/
|
||||
.cache/
|
||||
.clice/
|
||||
.llvm*/
|
||||
.xmake/
|
||||
__pycache__/
|
||||
.pytest_cache/
|
||||
tests/unit/Local/
|
||||
.llvm*/
|
||||
.clice/
|
||||
compile_commands.json
|
||||
perf.data
|
||||
flamegraph.svg
|
||||
|
||||
# Nodejs & Web
|
||||
*.vsix
|
||||
dist/
|
||||
out/
|
||||
cache/
|
||||
node_modules/
|
||||
.vscode-test/
|
||||
|
||||
**/node_modules
|
||||
docs/.vitepress/dist
|
||||
docs/.vitepress/cache
|
||||
# Python & Testing
|
||||
__pycache__/
|
||||
.pytest_cache/
|
||||
tests/unit/Local/
|
||||
|
||||
# IDEs & Editors
|
||||
/.vscode/
|
||||
.vs/
|
||||
.idea/
|
||||
.claude
|
||||
.clangd
|
||||
|
||||
# pixi environments
|
||||
.env
|
||||
.pixi/*
|
||||
!.pixi/config.toml
|
||||
|
||||
.codex/
|
||||
.claude/
|
||||
openspec/
|
||||
@@ -1,25 +0,0 @@
|
||||
# .pre-commit-config.yaml
|
||||
exclude: "\\.patch$"
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v6.0.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: check-yaml
|
||||
- id: check-added-large-files
|
||||
- id: check-merge-conflict
|
||||
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.12.12
|
||||
hooks:
|
||||
- id: ruff
|
||||
args: [--fix]
|
||||
- id: ruff-format
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||
rev: v21.1.0
|
||||
hooks:
|
||||
- id: clang-format
|
||||
types_or: [c++, c]
|
||||
23
.prettierrc.yaml
Normal file
23
.prettierrc.yaml
Normal file
@@ -0,0 +1,23 @@
|
||||
# .prettierrc.yaml
|
||||
|
||||
printWidth: 100
|
||||
tabWidth: 4
|
||||
useTabs: false
|
||||
semi: true
|
||||
singleQuote: false
|
||||
jsxSingleQuote: false
|
||||
quoteProps: "as-needed"
|
||||
trailingComma: "all"
|
||||
bracketSpacing: true
|
||||
arrowParens: "always"
|
||||
endOfLine: "lf"
|
||||
|
||||
overrides:
|
||||
- files: "*.md"
|
||||
options:
|
||||
proseWrap: "preserve"
|
||||
tabWidth: 2
|
||||
|
||||
- files: ["*.json", "*.yaml", "*.yml", ".clang-format", ".clang-tidy"]
|
||||
options:
|
||||
tabWidth: 2
|
||||
@@ -1 +0,0 @@
|
||||
3.13
|
||||
238
CMakeLists.txt
238
CMakeLists.txt
@@ -1,8 +1,10 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
cmake_minimum_required(VERSION 3.30)
|
||||
|
||||
project(CLICE_PROJECT LANGUAGES C CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_SCAN_FOR_MODULES OFF)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
@@ -10,127 +12,159 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib")
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib")
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin")
|
||||
|
||||
# Make sure all third libraries are affected by ABI related options
|
||||
option(CLICE_ENABLE_LTO "Enable ThinLTO for all targets" OFF)
|
||||
option(CLICE_USE_LIBCXX "Use libc++ instead of libstdc++" OFF)
|
||||
option(CLICE_OFFLINE_BUILD "Disable network downloads during configuration" OFF)
|
||||
option(CLICE_ENABLE_TEST "Build unit tests" OFF)
|
||||
option(CLICE_CI_ENVIRONMENT "Enable CI-specific configuration" OFF)
|
||||
option(CLICE_ENABLE_BENCHMARK "Build benchmarks" OFF)
|
||||
option(CLICE_RELEASE "Enable release packaging (LTO + strip + pack)" OFF)
|
||||
|
||||
# Global flags that apply to all targets (including FetchContent dependencies).
|
||||
if(NOT MSVC)
|
||||
add_compile_options(-ffunction-sections -fdata-sections)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
# https://conda-forge.org/docs/maintainer/knowledge_base/#newer-c-features-with-old-sdk
|
||||
add_compile_definitions(_LIBCPP_DISABLE_AVAILABILITY=1)
|
||||
endif()
|
||||
|
||||
if(MSVC OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND
|
||||
CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC"))
|
||||
string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,/OPT:REF")
|
||||
string(APPEND CMAKE_SHARED_LINKER_FLAGS " -Wl,/OPT:REF")
|
||||
elseif(APPLE)
|
||||
string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-dead_strip")
|
||||
string(APPEND CMAKE_SHARED_LINKER_FLAGS " -Wl,-dead_strip")
|
||||
else()
|
||||
string(APPEND CMAKE_EXE_LINKER_FLAGS " -static-libstdc++ -static-libgcc -Wl,--gc-sections")
|
||||
string(APPEND CMAKE_SHARED_LINKER_FLAGS " -Wl,--gc-sections")
|
||||
endif()
|
||||
|
||||
if(CLICE_USE_LIBCXX)
|
||||
string(APPEND CMAKE_CXX_FLAGS " -stdlib=libc++")
|
||||
string(APPEND CMAKE_EXE_LINKER_FLAGS " -stdlib=libc++")
|
||||
string(APPEND CMAKE_SHARED_LINKER_FLAGS " -stdlib=libc++")
|
||||
endif()
|
||||
|
||||
if(NOT WIN32 AND CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
string(APPEND CMAKE_CXX_FLAGS " -fsanitize=address")
|
||||
string(APPEND CMAKE_EXE_LINKER_FLAGS " -fsanitize=address")
|
||||
string(APPEND CMAKE_SHARED_LINKER_FLAGS " -fsanitize=address")
|
||||
if(CLICE_RELEASE)
|
||||
set(CLICE_ENABLE_LTO ON)
|
||||
endif()
|
||||
|
||||
if(CLICE_ENABLE_LTO)
|
||||
string(APPEND CMAKE_C_FLAGS " -flto=thin")
|
||||
string(APPEND CMAKE_CXX_FLAGS " -flto=thin")
|
||||
string(APPEND CMAKE_EXE_LINKER_FLAGS " -flto=thin")
|
||||
string(APPEND CMAKE_SHARED_LINKER_FLAGS " -flto=thin")
|
||||
string(APPEND CMAKE_MODULE_LINKER_FLAGS " -flto=thin")
|
||||
endif()
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
add_compile_options(-fsanitize=address)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
|
||||
# clang-cl (MSVC frontend): manually link ASan runtime since clang-cl
|
||||
# doesn't handle -fsanitize=address linking automatically.
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_CXX_COMPILER} --print-resource-dir
|
||||
OUTPUT_VARIABLE CLANG_RESOURCE_DIR
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
set(ASAN_LIB_PATH "${CLANG_RESOURCE_DIR}/lib/windows")
|
||||
link_directories(${ASAN_LIB_PATH})
|
||||
|
||||
set(ASAN_LINK_FLAGS "")
|
||||
list(APPEND ASAN_LINK_FLAGS "clang_rt.asan_dynamic-x86_64.lib")
|
||||
list(APPEND ASAN_LINK_FLAGS "/wholearchive:clang_rt.asan_dynamic_runtime_thunk-x86_64.lib")
|
||||
|
||||
foreach(flag ${ASAN_LINK_FLAGS})
|
||||
string(APPEND CMAKE_EXE_LINKER_FLAGS " ${flag}")
|
||||
string(APPEND CMAKE_SHARED_LINKER_FLAGS " ${flag}")
|
||||
string(APPEND CMAKE_MODULE_LINKER_FLAGS " ${flag}")
|
||||
endforeach()
|
||||
else()
|
||||
string(APPEND CMAKE_EXE_LINKER_FLAGS " -fsanitize=address")
|
||||
string(APPEND CMAKE_SHARED_LINKER_FLAGS " -fsanitize=address")
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
# Disable Identical COMDAT Folding in Debug to avoid ASan ODR false positives.
|
||||
string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,/OPT:NOICF")
|
||||
string(APPEND CMAKE_SHARED_LINKER_FLAGS " -Wl,/OPT:NOICF")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include("${PROJECT_SOURCE_DIR}/cmake/package.cmake")
|
||||
|
||||
# Project-specific options (not applied to third-party deps).
|
||||
add_library(clice_options INTERFACE)
|
||||
|
||||
if(CLICE_CI_ENVIRONMENT)
|
||||
target_compile_definitions(clice_options INTERFACE CLICE_CI_ENVIRONMENT)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreadedDLL")
|
||||
target_link_options(clice_options INTERFACE
|
||||
-fuse-ld=lld-link
|
||||
-Wl,/OPT:REF
|
||||
)
|
||||
elseif(APPLE)
|
||||
target_link_options(clice_options INTERFACE
|
||||
-fuse-ld=lld
|
||||
-Wl,-dead_strip
|
||||
)
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
target_link_options(clice_options INTERFACE
|
||||
-fuse-ld=lld
|
||||
-Wl,--gc-sections
|
||||
)
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
target_compile_options(clice_options INTERFACE
|
||||
/GR-
|
||||
/EHsc-
|
||||
/Zc:preprocessor
|
||||
)
|
||||
if(MSVC OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND
|
||||
CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC"))
|
||||
target_compile_options(clice_options INTERFACE /GR- /EHs-c- /Zc:preprocessor)
|
||||
else()
|
||||
target_compile_options(clice_options INTERFACE
|
||||
-fno-rtti
|
||||
-fno-exceptions
|
||||
-Wno-deprecated-declarations
|
||||
-Wno-undefined-inline
|
||||
-ffunction-sections
|
||||
-fdata-sections
|
||||
$<$<COMPILE_LANG_AND_ID:CXX,Clang,AppleClang>:-Wno-undefined-inline>
|
||||
)
|
||||
endif()
|
||||
|
||||
set(FBS_SCHEMA_FILE "${CMAKE_CURRENT_SOURCE_DIR}/include/Index/schema.fbs")
|
||||
set(GENERATED_HEADER "${CMAKE_CURRENT_BINARY_DIR}/generated/schema_generated.h")
|
||||
if(WIN32)
|
||||
target_link_libraries(clice_options INTERFACE version ntdll)
|
||||
endif()
|
||||
|
||||
if(CLICE_ENABLE_TEST)
|
||||
target_compile_definitions(clice_options INTERFACE CLICE_ENABLE_TEST=1)
|
||||
endif()
|
||||
|
||||
if(CLICE_CI_ENVIRONMENT)
|
||||
target_compile_definitions(clice_options INTERFACE CLICE_CI_ENVIRONMENT=1)
|
||||
endif()
|
||||
|
||||
set(FBS_SCHEMA_FILE "${PROJECT_SOURCE_DIR}/src/index/schema.fbs")
|
||||
set(GENERATED_HEADER "${PROJECT_BINARY_DIR}/generated/schema_generated.h")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${GENERATED_HEADER}
|
||||
COMMAND $<TARGET_FILE:flatc> --cpp -o ${CMAKE_CURRENT_BINARY_DIR}/generated ${FBS_SCHEMA_FILE}
|
||||
DEPENDS ${FBS_SCHEMA_FILE}
|
||||
OUTPUT "${GENERATED_HEADER}"
|
||||
COMMAND $<TARGET_FILE:flatc> --cpp -o "${PROJECT_BINARY_DIR}/generated" "${FBS_SCHEMA_FILE}"
|
||||
DEPENDS "${FBS_SCHEMA_FILE}"
|
||||
COMMENT "Generating C++ header from ${FBS_SCHEMA_FILE}"
|
||||
)
|
||||
|
||||
add_custom_target(
|
||||
generate_flatbuffers_schema
|
||||
DEPENDS ${GENERATED_HEADER}
|
||||
)
|
||||
add_custom_target(generate_flatbuffers_schema DEPENDS "${GENERATED_HEADER}")
|
||||
|
||||
set(CONFIG_SOURCE_FILE "${CMAKE_CURRENT_SOURCE_DIR}/config/clang-tidy-config.h")
|
||||
set(CONFIG_GENERATED_FILE "${CMAKE_CURRENT_BINARY_DIR}/generated/clang-tidy-config.h")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${CONFIG_GENERATED_FILE}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CONFIG_SOURCE_FILE} ${CONFIG_GENERATED_FILE}
|
||||
DEPENDS ${CONFIG_SOURCE_FILE}
|
||||
COMMENT "Generating C++ header from ${CONFIG_SOURCE_FILE}"
|
||||
)
|
||||
|
||||
add_custom_target(
|
||||
generate_config
|
||||
DEPENDS ${CONFIG_GENERATED_FILE}
|
||||
)
|
||||
|
||||
file(GLOB_RECURSE CLICE_SOURCES CONFIGURE_DEPENDS
|
||||
"${PROJECT_SOURCE_DIR}/src/AST/*.cpp"
|
||||
"${PROJECT_SOURCE_DIR}/src/Async/*.cpp"
|
||||
"${PROJECT_SOURCE_DIR}/src/Basic/*.cpp"
|
||||
"${PROJECT_SOURCE_DIR}/src/Compiler/*.cpp"
|
||||
"${PROJECT_SOURCE_DIR}/src/Index/*.cpp"
|
||||
"${PROJECT_SOURCE_DIR}/src/Feature/*.cpp"
|
||||
"${PROJECT_SOURCE_DIR}/src/Server/*.cpp"
|
||||
"${PROJECT_SOURCE_DIR}/src/Support/*.cpp"
|
||||
)
|
||||
add_library(clice-core STATIC "${CLICE_SOURCES}")
|
||||
add_dependencies(clice-core generate_flatbuffers_schema generate_config)
|
||||
file(GLOB_RECURSE CLICE_CORE_SOURCES CONFIGURE_DEPENDS "${PROJECT_SOURCE_DIR}/src/*.cpp")
|
||||
add_library(clice-core STATIC ${CLICE_CORE_SOURCES})
|
||||
add_library(clice::core ALIAS clice-core)
|
||||
add_dependencies(clice-core generate_flatbuffers_schema)
|
||||
|
||||
target_include_directories(clice-core PUBLIC
|
||||
"${PROJECT_SOURCE_DIR}/include"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/generated"
|
||||
"${PROJECT_SOURCE_DIR}/src"
|
||||
"${PROJECT_BINARY_DIR}/generated"
|
||||
)
|
||||
target_link_libraries(clice-core PUBLIC
|
||||
clice_options
|
||||
libuv::libuv
|
||||
llvm-libs
|
||||
spdlog::spdlog
|
||||
tomlplusplus::tomlplusplus
|
||||
roaring::roaring
|
||||
flatbuffers
|
||||
llvm-libs
|
||||
eventide::ipc::lsp
|
||||
eventide::serde::toml
|
||||
simdjson::simdjson
|
||||
)
|
||||
|
||||
add_executable(clice "${PROJECT_SOURCE_DIR}/bin/clice.cc")
|
||||
target_link_libraries(clice PRIVATE clice-core)
|
||||
add_executable(clice "${PROJECT_SOURCE_DIR}/src/clice.cc")
|
||||
target_link_libraries(clice PRIVATE clice::core eventide::deco)
|
||||
install(TARGETS clice RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
message(STATUS "Copying resource directory for development build")
|
||||
file(
|
||||
COPY "${LLVM_INSTALL_PATH}/lib/clang"
|
||||
DESTINATION "${PROJECT_BINARY_DIR}/lib"
|
||||
add_custom_target(copy_clang_resource ALL
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
"${LLVM_INSTALL_PATH}/lib/clang"
|
||||
"${PROJECT_BINARY_DIR}/lib/clang"
|
||||
COMMENT "Copying clang resource directory"
|
||||
)
|
||||
install(
|
||||
DIRECTORY "${LLVM_INSTALL_PATH}/lib/clang"
|
||||
@@ -139,11 +173,35 @@ install(
|
||||
|
||||
if(CLICE_ENABLE_TEST)
|
||||
file(GLOB_RECURSE CLICE_TEST_SOURCES CONFIGURE_DEPENDS
|
||||
"${PROJECT_SOURCE_DIR}/tests/unit/*/*.cpp")
|
||||
add_executable(unit_tests
|
||||
"${CLICE_TEST_SOURCES}"
|
||||
"${PROJECT_SOURCE_DIR}/bin/unit_tests.cc"
|
||||
"${PROJECT_SOURCE_DIR}/tests/unit/*/*_tests.cpp"
|
||||
)
|
||||
target_include_directories(unit_tests PUBLIC "${PROJECT_SOURCE_DIR}")
|
||||
target_link_libraries(unit_tests PRIVATE clice-core)
|
||||
set(CLICE_TEST_SUPPORT_SOURCES
|
||||
"${PROJECT_SOURCE_DIR}/tests/unit/test/annotation.cpp"
|
||||
"${PROJECT_SOURCE_DIR}/tests/unit/test/tester.cpp"
|
||||
)
|
||||
|
||||
add_executable(unit_tests
|
||||
"${PROJECT_SOURCE_DIR}/tests/unit/unit_tests.cc"
|
||||
${CLICE_TEST_SOURCES}
|
||||
${CLICE_TEST_SUPPORT_SOURCES}
|
||||
)
|
||||
target_include_directories(unit_tests PRIVATE
|
||||
"${PROJECT_SOURCE_DIR}/src"
|
||||
"${PROJECT_SOURCE_DIR}/tests/unit"
|
||||
)
|
||||
target_link_libraries(unit_tests PRIVATE clice::core eventide::zest eventide::deco)
|
||||
endif()
|
||||
|
||||
if(CLICE_ENABLE_BENCHMARK)
|
||||
add_executable(scan_benchmark
|
||||
"${PROJECT_SOURCE_DIR}/benchmarks/scan_benchmark.cpp"
|
||||
)
|
||||
target_include_directories(scan_benchmark PRIVATE
|
||||
"${PROJECT_SOURCE_DIR}/src"
|
||||
)
|
||||
target_link_libraries(scan_benchmark PRIVATE clice::core eventide::deco)
|
||||
endif()
|
||||
|
||||
if(CLICE_RELEASE)
|
||||
include("${PROJECT_SOURCE_DIR}/cmake/release.cmake")
|
||||
endif()
|
||||
|
||||
39
Dockerfile
39
Dockerfile
@@ -1,39 +0,0 @@
|
||||
|
||||
FROM debian:13 AS builder
|
||||
|
||||
# Installs System Dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
ninja-build cmake build-essential curl gcc-14 g++-14 git
|
||||
|
||||
# Installs LLVM 20
|
||||
RUN curl https://apt.llvm.org/llvm-snapshot.gpg.key | tee /usr/share/keyrings/llvm-snapshot.gpg.key && \
|
||||
echo "deb [signed-by=/usr/share/keyrings/llvm-snapshot.gpg.key] http://apt.llvm.org/trixie/ llvm-toolchain-trixie main" >> /etc/apt/sources.list && \
|
||||
echo "deb [signed-by=/usr/share/keyrings/llvm-snapshot.gpg.key] http://apt.llvm.org/trixie/ llvm-toolchain-trixie-20 main" >> /etc/apt/sources.list && \
|
||||
apt-get update && apt-get install -y \
|
||||
clang-20 lld-20
|
||||
RUN ln -s /usr/bin/lld-20 /usr/bin/lld
|
||||
|
||||
# Adds source code
|
||||
COPY include /app/include
|
||||
COPY cmake /app/cmake
|
||||
COPY src /app/src
|
||||
COPY tests /app/tests
|
||||
COPY CMakeLists.txt /app/CMakeLists.txt
|
||||
COPY scripts /app/scripts
|
||||
|
||||
WORKDIR /app
|
||||
# Initializes and installs dependencies
|
||||
RUN cmake -B build -G Ninja -DCMAKE_C_COMPILER=clang-20 -DCMAKE_CXX_COMPILER=clang++-20 -DCMAKE_BUILD_TYPE=Release -DCLICE_ENABLE_TEST=ON
|
||||
# Builds clice
|
||||
RUN cmake --build build -j && \
|
||||
cmake --install build --prefix=/opt/clice
|
||||
|
||||
FROM debian:13
|
||||
|
||||
COPY --from=builder /opt/clice /opt/clice/
|
||||
COPY LICENSE /opt/clice/LICENSE
|
||||
COPY README.md /opt/clice/README.md
|
||||
|
||||
RUN ln -s /opt/clice/bin/clice /usr/local/bin/clice
|
||||
|
||||
ENTRYPOINT ["clice"]
|
||||
12
README.md
12
README.md
@@ -5,7 +5,7 @@
|
||||

|
||||
[](https://github.com/clice-io/clice/blob/main/LICENSE)
|
||||
[](https://github.com/clice-io/clice/actions)
|
||||
[](https://clice.io)
|
||||
[](https://docs.clice.io/clice/)
|
||||
[](https://deepwiki.com/clice-io/clice)
|
||||
[](https://discord.gg/PA3UxW2VA3)
|
||||
|
||||
@@ -22,11 +22,11 @@ Download the latest `clice` binary from the [releases page](https://github.com/c
|
||||
|
||||
```jsonc
|
||||
{
|
||||
// Optional: Set this to an empty string to turn off the clangd.
|
||||
"clangd.path": "",
|
||||
// Optional: Set this to an empty string to turn off the clangd.
|
||||
"clangd.path": "",
|
||||
|
||||
// Point this to the clice binary you downloaded.
|
||||
"clice.executable": "/path/to/your/clice/executable",
|
||||
// Point this to the clice binary you downloaded.
|
||||
"clice.executable": "/path/to/your/clice/executable",
|
||||
}
|
||||
```
|
||||
|
||||
@@ -35,4 +35,4 @@ Download the latest `clice` binary from the [releases page](https://github.com/c
|
||||
|
||||
## Documentation
|
||||
|
||||
To learn more about building, installing, and configuring clice, or to dive deep into its features and architecture, please visit our official documentation at [**clice.io**](https://clice.io/).
|
||||
To learn more about building, installing, and configuring clice, or to dive deep into its features and architecture, please visit our official documentation at [**clice.io**](https://docs.clice.io/clice/).
|
||||
|
||||
413
benchmarks/scan_benchmark.cpp
Normal file
413
benchmarks/scan_benchmark.cpp
Normal file
@@ -0,0 +1,413 @@
|
||||
/// Benchmark for scan_dependency_graph on a real compilation database.
|
||||
///
|
||||
/// Usage:
|
||||
/// scan_benchmark [OPTIONS] <compile_commands.json>
|
||||
///
|
||||
/// Example:
|
||||
/// ./build/RelWithDebInfo/bin/scan_benchmark \
|
||||
/// /home/ykiko/C++/clice/.llvm/build-debug/compile_commands.json
|
||||
///
|
||||
/// ./build/RelWithDebInfo/bin/scan_benchmark --log-level info --export graph.json \
|
||||
/// /home/ykiko/C++/clice/.llvm/build-debug/compile_commands.json
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <numeric>
|
||||
#include <print>
|
||||
#include <set>
|
||||
#include <thread>
|
||||
|
||||
#include "command/command.h"
|
||||
#include "eventide/deco/deco.h"
|
||||
#include "eventide/serde/json/serializer.h"
|
||||
#include "support/filesystem.h"
|
||||
#include "support/logging.h"
|
||||
#include "support/path_pool.h"
|
||||
#include "syntax/dependency_graph.h"
|
||||
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
|
||||
namespace et = eventide;
|
||||
|
||||
using namespace clice;
|
||||
|
||||
struct BenchmarkOptions {
|
||||
DecoKV(names = {"--log-level"}; help = "Log level: trace, debug, info, warn, error, off";
|
||||
required = false;)
|
||||
<std::string> log_level = "off";
|
||||
|
||||
DecoKV(names = {"--export"}; help = "Export dependency graph as JSON to this path";
|
||||
required = false;)
|
||||
<std::string> export_path;
|
||||
|
||||
DecoKV(names = {"--runs"}; help = "Number of cold start iterations"; required = false;)
|
||||
<int> runs = 20;
|
||||
|
||||
DecoFlag(names = {"-h", "--help"}; help = "Show help message"; required = false;)
|
||||
help;
|
||||
|
||||
DecoInput(meta_var = "CDB"; help = "Path to compile_commands.json"; required = false;)
|
||||
<std::string> cdb_path;
|
||||
};
|
||||
|
||||
struct FileNode {
|
||||
std::string path;
|
||||
std::string module_name;
|
||||
std::vector<std::string> includes;
|
||||
};
|
||||
|
||||
struct GraphExport {
|
||||
std::vector<FileNode> files;
|
||||
};
|
||||
|
||||
void export_graph_json(const PathPool& path_pool,
|
||||
const DependencyGraph& graph,
|
||||
llvm::StringRef output_path) {
|
||||
// Build reverse module map: path_id -> module_name.
|
||||
llvm::DenseMap<std::uint32_t, llvm::StringRef> path_to_module;
|
||||
for(auto& [name, path_ids]: graph.modules()) {
|
||||
for(auto path_id: path_ids) {
|
||||
path_to_module[path_id] = name;
|
||||
}
|
||||
}
|
||||
|
||||
GraphExport export_data;
|
||||
for(std::uint32_t id = 0; id < path_pool.paths.size(); id++) {
|
||||
auto inc_ids = graph.get_all_includes(id);
|
||||
if(inc_ids.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
FileNode node;
|
||||
node.path = path_pool.paths[id].str();
|
||||
|
||||
auto mod_it = path_to_module.find(id);
|
||||
if(mod_it != path_to_module.end()) {
|
||||
node.module_name = mod_it->second.str();
|
||||
}
|
||||
|
||||
for(auto flagged_id: inc_ids) {
|
||||
auto raw_id = flagged_id & DependencyGraph::PATH_ID_MASK;
|
||||
node.includes.push_back(path_pool.paths[raw_id].str());
|
||||
}
|
||||
|
||||
export_data.files.push_back(std::move(node));
|
||||
}
|
||||
|
||||
auto json = et::serde::json::to_json(export_data);
|
||||
if(!json) {
|
||||
std::println(stderr, "Failed to serialize dependency graph");
|
||||
return;
|
||||
}
|
||||
|
||||
std::ofstream out(output_path.str());
|
||||
if(!out) {
|
||||
std::println(stderr, "Failed to open output file: {}", output_path);
|
||||
return;
|
||||
}
|
||||
out << *json;
|
||||
std::println("Graph exported to {} ({} files)", output_path, export_data.files.size());
|
||||
}
|
||||
|
||||
void print_report(const ScanReport& report) {
|
||||
std::println("===============================================================");
|
||||
std::println(" Dependency Scan Report");
|
||||
std::println("===============================================================");
|
||||
|
||||
// Timing.
|
||||
std::println("");
|
||||
std::println(" Time: {}ms", report.elapsed_ms);
|
||||
std::println(" Waves: {}", report.waves);
|
||||
|
||||
// File counts.
|
||||
std::println("");
|
||||
std::println(" Files");
|
||||
std::println(" Source files (from CDB): {}", report.source_files);
|
||||
std::println(" Header files (discovered): {}", report.header_files);
|
||||
std::println(" Total: {}", report.total_files);
|
||||
std::println(" Modules: {}", report.modules);
|
||||
|
||||
// Include edges.
|
||||
std::println("");
|
||||
std::println(" Include Edges");
|
||||
std::println(" Total: {}", report.total_edges);
|
||||
std::println(" Unconditional: {}", report.unconditional_edges);
|
||||
std::println(" Conditional: {} (inside #if/#ifdef)", report.conditional_edges);
|
||||
|
||||
// Resolution accuracy.
|
||||
std::println("");
|
||||
std::println(" Resolution");
|
||||
std::println(" #include directives: {}", report.includes_found);
|
||||
std::println(" Resolved: {}", report.includes_resolved);
|
||||
auto unresolved_count = report.includes_found - report.includes_resolved;
|
||||
std::println(" Unresolved: {}", unresolved_count);
|
||||
if(report.includes_found > 0) {
|
||||
double rate = 100.0 * static_cast<double>(report.includes_resolved) /
|
||||
static_cast<double>(report.includes_found);
|
||||
std::println(" Accuracy: {:.1f}%", rate);
|
||||
}
|
||||
|
||||
// Wall-clock phase breakdown.
|
||||
std::println("");
|
||||
std::println(" Phase Breakdown (wall-clock)");
|
||||
std::println(" Config extraction: {}ms (prewarm={}ms, loop={}ms)",
|
||||
report.config_ms,
|
||||
report.prewarm_ms,
|
||||
report.config_loop_ms);
|
||||
std::println(" Dir cache pre-pop: {}ms (overlapped with Phase 1)", report.dir_cache_ms);
|
||||
std::println(" Phase 1 (read+scan, parallel): {}ms", report.phase1_ms);
|
||||
std::println(" Phase 2 (include resolve): {}ms", report.phase2_ms);
|
||||
std::println(" Phase 3 (graph build): {}ms", report.phase3_ms);
|
||||
|
||||
// Per-wave breakdown.
|
||||
if(!report.wave_stats.empty()) {
|
||||
std::println("");
|
||||
std::println(" Per-Wave Breakdown");
|
||||
std::println(" {:>5s} {:>8s} {:>8s} {:>8s} {:>8s} {:>8s} {:>10s} {:>10s}",
|
||||
"Wave",
|
||||
"Files",
|
||||
"P1(ms)",
|
||||
"P2(ms)",
|
||||
"Next",
|
||||
"Prefetch",
|
||||
"DirList",
|
||||
"DirHits");
|
||||
for(std::size_t i = 0; i < report.wave_stats.size(); i++) {
|
||||
auto& ws = report.wave_stats[i];
|
||||
std::println(" {:>5} {:>8} {:>8} {:>8} {:>8} {:>8} {:>10} {:>10}",
|
||||
i,
|
||||
ws.files,
|
||||
ws.phase1_ms,
|
||||
ws.phase2_ms,
|
||||
ws.next_files,
|
||||
ws.prefetch_count,
|
||||
ws.dir_listings,
|
||||
ws.dir_hits);
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 2 breakdown.
|
||||
if(report.p2_resolve_us > 0) {
|
||||
auto other_us = report.phase2_ms * 1000 - report.p2_resolve_us;
|
||||
std::println("");
|
||||
std::println(" Phase 2 Breakdown (single-threaded)");
|
||||
std::println(" resolve_include: {:.1f}ms", report.p2_resolve_us / 1000.0);
|
||||
std::println(" Other (cache lookup, intern, graph): {:.1f}ms", other_us / 1000.0);
|
||||
}
|
||||
|
||||
// Cumulative I/O statistics.
|
||||
std::println("");
|
||||
std::println(" I/O Statistics (cumulative across threads)");
|
||||
std::println(" File read: {:.1f}ms (sum of all threads)", report.read_us / 1000.0);
|
||||
std::println(" Lexer scan: {:.1f}ms (sum of all threads)", report.scan_us / 1000.0);
|
||||
std::println(" Filesystem: {:.1f}ms ({} readdir calls, {} dir cache hits)",
|
||||
report.fs_us / 1000.0,
|
||||
report.dir_listings,
|
||||
report.dir_hits);
|
||||
std::println(" File lookups: {}", report.fs_lookups);
|
||||
std::println(" Include cache hits: {}", report.include_cache_hits);
|
||||
std::println(" Scan result cache hits: {}", report.scan_cache_hits);
|
||||
if(report.dir_listings + report.dir_hits > 0) {
|
||||
double hit_rate = 100.0 * static_cast<double>(report.dir_hits) /
|
||||
static_cast<double>(report.dir_listings + report.dir_hits);
|
||||
std::println(" Dir cache hit rate: {:.1f}%", hit_rate);
|
||||
}
|
||||
|
||||
std::println("");
|
||||
std::println("===============================================================");
|
||||
}
|
||||
|
||||
int main(int argc, const char** argv) {
|
||||
auto args = deco::util::argvify(argc, argv);
|
||||
auto result = deco::cli::parse<BenchmarkOptions>(args);
|
||||
|
||||
if(!result.has_value()) {
|
||||
std::println(stderr, "Error: {}", result.error().message);
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto& opts = result->options;
|
||||
|
||||
if(opts.help.value_or(false) || !opts.cdb_path.has_value()) {
|
||||
std::ostringstream oss;
|
||||
deco::cli::write_usage_for<BenchmarkOptions>(oss, "scan_benchmark [OPTIONS] <cdb>");
|
||||
std::print("{}", oss.str());
|
||||
return opts.help.value_or(false) ? 0 : 1;
|
||||
}
|
||||
|
||||
// Configure logging.
|
||||
auto level = spdlog::level::from_str(*opts.log_level);
|
||||
clice::logging::options.level = level;
|
||||
clice::logging::stderr_logger("scan_benchmark", clice::logging::options);
|
||||
|
||||
// resource_dir() is self-initializing (lazy static) — no setup needed.
|
||||
|
||||
auto& cdb_path = *opts.cdb_path;
|
||||
auto hw_threads = std::thread::hardware_concurrency();
|
||||
auto runs = *opts.runs;
|
||||
if(runs <= 0) {
|
||||
std::println(stderr, "Error: --runs must be positive (got {})", runs);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Set UV_THREADPOOL_SIZE if not already set.
|
||||
// Use at least libuv's default (4) so low-core CI runners don't regress.
|
||||
if(!std::getenv("UV_THREADPOOL_SIZE")) {
|
||||
auto pool_size = std::max(hw_threads, 4u);
|
||||
static std::string env = "UV_THREADPOOL_SIZE=" + std::to_string(pool_size);
|
||||
putenv(env.data());
|
||||
}
|
||||
|
||||
std::println("Hardware threads: {}", hw_threads);
|
||||
std::println("UV_THREADPOOL_SIZE: {}", std::getenv("UV_THREADPOOL_SIZE"));
|
||||
std::println("Log level: {}", *opts.log_level);
|
||||
std::println("CDB: {}", cdb_path);
|
||||
std::println("");
|
||||
|
||||
// Load compilation database.
|
||||
auto t0 = std::chrono::steady_clock::now();
|
||||
|
||||
CompilationDatabase cdb;
|
||||
auto count = cdb.load(cdb_path);
|
||||
|
||||
auto t1 = std::chrono::steady_clock::now();
|
||||
auto load_ms = std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0).count();
|
||||
|
||||
std::println("CDB loaded: {} entries in {}ms", count, load_ms);
|
||||
|
||||
{
|
||||
std::set<const CompilationInfo*> unique_contexts;
|
||||
std::set<const CanonicalCommand*> unique_canonicals;
|
||||
std::map<const CanonicalCommand*, int> canonical_hist;
|
||||
for(auto& entry: cdb.get_entries()) {
|
||||
unique_contexts.insert(entry.info.ptr);
|
||||
unique_canonicals.insert(entry.info->canonical.ptr);
|
||||
canonical_hist[entry.info->canonical.ptr]++;
|
||||
}
|
||||
double dedup_ratio =
|
||||
unique_contexts.empty() ? 0.0 : static_cast<double>(count) / unique_contexts.size();
|
||||
std::println(
|
||||
"Context dedup: {} files -> {} unique contexts ({:.1f}x), {} unique canonicals",
|
||||
count,
|
||||
unique_contexts.size(),
|
||||
dedup_ratio,
|
||||
unique_canonicals.size());
|
||||
|
||||
// If canonical dedup is poor, dump diagnostics.
|
||||
if(unique_canonicals.size() > 200) {
|
||||
// Sort canonicals by frequency (descending).
|
||||
std::vector<std::pair<int, const CanonicalCommand*>> sorted;
|
||||
for(auto& [ptr, cnt]: canonical_hist)
|
||||
sorted.push_back({cnt, ptr});
|
||||
std::ranges::sort(sorted,
|
||||
std::greater{},
|
||||
&std::pair<int, const CanonicalCommand*>::first);
|
||||
|
||||
// Show top-5 canonical commands.
|
||||
for(int i = 0; i < std::min(5, (int)sorted.size()); i++) {
|
||||
auto [cnt, cmd] = sorted[i];
|
||||
std::println(" canonical[{}] ({} files, {} args):", i, cnt, cmd->arguments.size());
|
||||
for(auto arg: cmd->arguments)
|
||||
std::println(" {}", arg);
|
||||
}
|
||||
|
||||
// Show a singleton canonical (count==1) to see what per-file arg leaks in.
|
||||
for(auto& [cnt, cmd]: sorted) {
|
||||
if(cnt == 1) {
|
||||
std::println(" singleton canonical ({} args):", cmd->arguments.size());
|
||||
for(auto arg: cmd->arguments)
|
||||
std::println(" {}", arg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Find two canonicals that differ by only a few args.
|
||||
if(sorted.size() >= 2) {
|
||||
auto* a = sorted[0].second;
|
||||
auto* b = sorted[1].second;
|
||||
std::println(" --- Canonical diff (top-1 vs top-2) ---");
|
||||
auto max_len = std::max(a->arguments.size(), b->arguments.size());
|
||||
for(std::size_t i = 0; i < max_len; i++) {
|
||||
llvm::StringRef av = i < a->arguments.size() ? a->arguments[i] : "<missing>";
|
||||
llvm::StringRef bv = i < b->arguments.size() ? b->arguments[i] : "<missing>";
|
||||
if(av != bv)
|
||||
std::println(" DIFF[{}]: '{}' vs '{}'", i, av, bv);
|
||||
else
|
||||
std::println(" SAME[{}]: '{}'", i, av);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::println("\nRunning {} cold start scan(s)...\n", runs);
|
||||
|
||||
PathPool path_pool;
|
||||
DependencyGraph graph;
|
||||
std::vector<std::int64_t> elapsed_times;
|
||||
std::vector<std::int64_t> config_times;
|
||||
std::vector<std::int64_t> phase1_times;
|
||||
std::vector<std::int64_t> phase2_times;
|
||||
elapsed_times.reserve(runs);
|
||||
config_times.reserve(runs);
|
||||
phase1_times.reserve(runs);
|
||||
phase2_times.reserve(runs);
|
||||
|
||||
for(int i = 0; i < runs; i++) {
|
||||
// True cold start: rebuild CDB (clears toolchain & config caches),
|
||||
// reset PathPool and DependencyGraph.
|
||||
cdb = CompilationDatabase{};
|
||||
cdb.load(cdb_path);
|
||||
path_pool = PathPool{};
|
||||
graph = DependencyGraph{};
|
||||
|
||||
auto report = scan_dependency_graph(cdb, path_pool, graph);
|
||||
|
||||
elapsed_times.push_back(report.elapsed_ms);
|
||||
config_times.push_back(report.config_ms);
|
||||
phase1_times.push_back(report.phase1_ms);
|
||||
phase2_times.push_back(report.phase2_ms);
|
||||
|
||||
std::println("[run {:2}] {}ms | config={}ms phase1={}ms phase2={}ms | files={}",
|
||||
i + 1,
|
||||
report.elapsed_ms,
|
||||
report.config_ms,
|
||||
report.phase1_ms,
|
||||
report.phase2_ms,
|
||||
report.total_files);
|
||||
|
||||
// Print detailed report for the first run only.
|
||||
if(i == 0) {
|
||||
std::println("");
|
||||
print_report(report);
|
||||
}
|
||||
}
|
||||
|
||||
// Summary statistics.
|
||||
if(runs > 1) {
|
||||
auto stats = [](std::vector<std::int64_t>& v) {
|
||||
std::ranges::sort(v);
|
||||
auto sum = std::accumulate(v.begin(), v.end(), std::int64_t{0});
|
||||
return std::tuple{v.front(), sum / static_cast<std::int64_t>(v.size()), v.back()};
|
||||
};
|
||||
auto [e_min, e_avg, e_max] = stats(elapsed_times);
|
||||
auto [c_min, c_avg, c_max] = stats(config_times);
|
||||
auto [p1_min, p1_avg, p1_max] = stats(phase1_times);
|
||||
auto [p2_min, p2_avg, p2_max] = stats(phase2_times);
|
||||
|
||||
std::println("\n Summary ({} runs) min avg max", runs);
|
||||
std::println(" Total: {:>7} {:>6} {:>6}", e_min, e_avg, e_max);
|
||||
std::println(" Config extraction: {:>7} {:>6} {:>6}", c_min, c_avg, c_max);
|
||||
std::println(" Phase 1 (read+scan):{:>7} {:>6} {:>6}", p1_min, p1_avg, p1_max);
|
||||
std::println(" Phase 2 (resolve): {:>7} {:>6} {:>6}", p2_min, p2_avg, p2_max);
|
||||
}
|
||||
|
||||
// Export dependency graph as JSON if requested.
|
||||
if(opts.export_path.has_value()) {
|
||||
export_graph_json(path_pool, graph, *opts.export_path);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
136
bin/clice.cc
136
bin/clice.cc
@@ -1,136 +0,0 @@
|
||||
#include "Server/Version.h"
|
||||
#include "Server/Server.h"
|
||||
#include "Support/Logging.h"
|
||||
#include "Support/Format.h"
|
||||
|
||||
#include "llvm/Support/InitLLVM.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/Process.h"
|
||||
#include "llvm/ADT/StringSwitch.h"
|
||||
|
||||
namespace cl = llvm::cl;
|
||||
using namespace clice;
|
||||
|
||||
namespace {
|
||||
|
||||
static cl::OptionCategory category("clice options");
|
||||
|
||||
enum class Mode {
|
||||
Pipe,
|
||||
Socket,
|
||||
Indexer,
|
||||
};
|
||||
|
||||
cl::opt<Mode> mode{
|
||||
"mode",
|
||||
cl::cat(category),
|
||||
cl::value_desc("string"),
|
||||
cl::init(Mode::Pipe),
|
||||
cl::values(clEnumValN(Mode::Pipe, "pipe", "pipe mode, clice will listen on stdio"),
|
||||
clEnumValN(Mode::Socket, "socket", "socket mode, clice will listen on host:port")),
|
||||
/// clEnumValN(Mode::Indexer, "indexer", "indexer mode, to implement")
|
||||
cl::desc("The mode of clice, default is pipe, socket is usually used for debugging"),
|
||||
};
|
||||
|
||||
cl::opt<std::string> host{
|
||||
"host",
|
||||
cl::cat(category),
|
||||
cl::value_desc("string"),
|
||||
cl::init("127.0.0.1"),
|
||||
cl::desc("The host to connect to (default: 127.0.0.1)"),
|
||||
};
|
||||
|
||||
cl::opt<unsigned int> port{
|
||||
"port",
|
||||
cl::cat(category),
|
||||
cl::value_desc("unsigned int"),
|
||||
cl::init(50051),
|
||||
cl::desc("The port to connect to"),
|
||||
};
|
||||
|
||||
cl::opt<logging::ColorMode> log_color{
|
||||
"log-color",
|
||||
cl::cat(category),
|
||||
cl::value_desc("always|auto|never"),
|
||||
cl::init(logging::ColorMode::automatic),
|
||||
cl::values(clEnumValN(logging::ColorMode::automatic, "auto", ""),
|
||||
clEnumValN(logging::ColorMode::always, "always", ""),
|
||||
clEnumValN(logging::ColorMode::never, "never", "")),
|
||||
cl::desc("When to use terminal colors, default is auto"),
|
||||
};
|
||||
|
||||
cl::opt<logging::Level> log_level{
|
||||
"log-level",
|
||||
cl::cat(category),
|
||||
cl::value_desc("trace|debug|info|warn|error"),
|
||||
cl::init(logging::Level::info),
|
||||
cl::values(clEnumValN(logging::Level::trace, "trace", ""),
|
||||
clEnumValN(logging::Level::debug, "debug", ""),
|
||||
clEnumValN(logging::Level::info, "info", ""),
|
||||
clEnumValN(logging::Level::warn, "warn", ""),
|
||||
clEnumValN(logging::Level::err, "error", ""),
|
||||
clEnumValN(logging::Level::off, "off", "")),
|
||||
cl::desc("The log level, default is info"),
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, const char** argv) {
|
||||
llvm::InitLLVM guard(argc, argv);
|
||||
llvm::setBugReportMsg(
|
||||
"Please report bugs to https://github.com/clice-io/clice/issues and include the crash backtrace");
|
||||
cl::SetVersionPrinter([](llvm::raw_ostream& os) {
|
||||
os << std::format("clice version: {}\nllvm version: {}\n",
|
||||
clice::config::version,
|
||||
clice::config::llvm_version);
|
||||
});
|
||||
cl::HideUnrelatedOptions(category);
|
||||
cl::ParseCommandLineOptions(argc,
|
||||
argv,
|
||||
"clice is a new generation of language server for C/C++");
|
||||
|
||||
logging::options.color = log_color;
|
||||
logging::options.level = log_level;
|
||||
logging::stderr_logger("clice", logging::options);
|
||||
|
||||
if(auto result = fs::init_resource_dir(argv[0]); !result) {
|
||||
LOGGING_FATAL("Cannot find default resource directory, because {}", result.error());
|
||||
}
|
||||
|
||||
for(int i = 0; i < argc; ++i) {
|
||||
LOGGING_INFO("argv[{}] = {}", i, argv[i]);
|
||||
}
|
||||
|
||||
async::init();
|
||||
|
||||
/// The global server instance.
|
||||
static Server instance;
|
||||
auto loop = [&](json::Value value) -> async::Task<> {
|
||||
co_await instance.on_receive(value);
|
||||
};
|
||||
|
||||
switch(mode) {
|
||||
case Mode::Pipe: {
|
||||
async::net::listen(loop);
|
||||
LOGGING_INFO("Server starts listening on stdin/stdout");
|
||||
break;
|
||||
}
|
||||
|
||||
case Mode::Socket: {
|
||||
async::net::listen(host.c_str(), port, loop);
|
||||
LOGGING_INFO("Server starts listening on {}:{}", host.getValue(), port.getValue());
|
||||
break;
|
||||
}
|
||||
|
||||
case Mode::Indexer: {
|
||||
/// TODO:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
async::run();
|
||||
|
||||
LOGGING_INFO("clice exit normally!");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,202 +0,0 @@
|
||||
#include "Test/Test.h"
|
||||
#include "Support/Logging.h"
|
||||
#include "Support/GlobPattern.h"
|
||||
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/Signals.h"
|
||||
|
||||
using namespace clice;
|
||||
using namespace clice::testing;
|
||||
|
||||
constexpr static std::string_view GREEN = "\033[32m";
|
||||
constexpr static std::string_view YELLOW = "\033[33m";
|
||||
constexpr static std::string_view RED = "\033[31m";
|
||||
constexpr static std::string_view CLEAR = "\033[0m";
|
||||
|
||||
namespace {
|
||||
|
||||
namespace cl = llvm::cl;
|
||||
|
||||
cl::OptionCategory unittest_category("clice Unittest Options");
|
||||
|
||||
cl::opt<std::string> test_dir{
|
||||
"test-dir",
|
||||
cl::desc("Specify the test source directory path"),
|
||||
cl::value_desc("path"),
|
||||
cl::Required,
|
||||
cl::cat(unittest_category),
|
||||
};
|
||||
|
||||
cl::opt<std::string> test_filter{
|
||||
"test-filter",
|
||||
cl::desc("A glob pattern to run subset of tests"),
|
||||
cl::cat(unittest_category),
|
||||
};
|
||||
|
||||
cl::opt<bool> enable_example{
|
||||
"enable-example",
|
||||
cl::init(false),
|
||||
cl::cat(unittest_category),
|
||||
};
|
||||
|
||||
std::optional<GlobPattern> pattern;
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace clice::testing {
|
||||
|
||||
Runner& Runner::instance() {
|
||||
static Runner runner;
|
||||
return runner;
|
||||
}
|
||||
|
||||
void Runner::add_suite(std::string_view name, Suite suite) {
|
||||
suites[name].emplace_back(suite);
|
||||
}
|
||||
|
||||
void Runner::on_test(std::string_view name, Test test, bool skipped) {
|
||||
std::string full_name = std::format("{}.{}", curr_suite_name, name);
|
||||
|
||||
/// If this test if filter, directly return.
|
||||
if(pattern && !pattern->match(full_name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/// If there is any test in the suite, we print the suite start info.
|
||||
if(all_skipped) {
|
||||
std::println("{}[----------] tests from {}{}", GREEN, curr_suite_name, CLEAR);
|
||||
all_skipped = false;
|
||||
}
|
||||
|
||||
if(skipped) {
|
||||
/// If this test is marked as skipped, only print skip information.
|
||||
std::println("{}[ SKIPPED ] {}{}", YELLOW, full_name, CLEAR);
|
||||
return;
|
||||
}
|
||||
|
||||
/// Reset whether this test is failed or fatal.
|
||||
curr_failed = false;
|
||||
curr_fatal = false;
|
||||
|
||||
using namespace std::chrono;
|
||||
|
||||
std::println("{}[ RUN ] {}.{}{}", GREEN, curr_suite_name, name, CLEAR);
|
||||
auto begin = system_clock::now();
|
||||
|
||||
test();
|
||||
|
||||
auto duration = duration_cast<milliseconds>(system_clock::now() - begin);
|
||||
std::println("{}[ {} ] {} ({} ms){}",
|
||||
curr_failed ? RED : GREEN,
|
||||
curr_failed ? "FAILED" : " OK",
|
||||
full_name,
|
||||
duration.count(),
|
||||
CLEAR);
|
||||
|
||||
/// Update test information.
|
||||
curr_tests_count += 1;
|
||||
total_tests_count += 1;
|
||||
|
||||
curr_test_duration += duration;
|
||||
total_test_duration += duration;
|
||||
|
||||
if(curr_failed) {
|
||||
curr_failed_tests_count += 1;
|
||||
total_failed_tests_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
void Runner::fail(const may_failure& failure) {
|
||||
if(failure.failed) {
|
||||
curr_failed = true;
|
||||
std::println("{}Failure at {}:{}:{}! [{}]{}",
|
||||
RED,
|
||||
failure.location.file_name(),
|
||||
failure.location.line(),
|
||||
failure.location.column(),
|
||||
failure.expression,
|
||||
CLEAR);
|
||||
std::println("{}", failure.message);
|
||||
}
|
||||
|
||||
if(failure.fatal) {
|
||||
curr_fatal = true;
|
||||
std::println("{}--> Test stopped due to fatal error.{}", RED, CLEAR);
|
||||
std::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
int Runner::run_tests() {
|
||||
/// Register all tests.
|
||||
std::println("{}[----------] Global test environment set-up.{}", GREEN, CLEAR);
|
||||
|
||||
for(auto& [suite_name, suite]: suites) {
|
||||
if(!enable_example && suite_name == "TEST.Example") {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!test_filter.empty()) {
|
||||
auto pos = test_filter.find_first_of('.');
|
||||
if(pos != std::string::npos && test_filter.substr(0, pos) != suite_name) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
curr_fatal = false;
|
||||
all_skipped = true;
|
||||
curr_suite_name = suite_name;
|
||||
curr_tests_count = 0;
|
||||
curr_failed_tests_count = 0;
|
||||
curr_test_duration = std::chrono::milliseconds();
|
||||
|
||||
for(auto& callback: suite) {
|
||||
callback();
|
||||
}
|
||||
|
||||
/// If there is any test in the suite, we print the suite info.
|
||||
if(!all_skipped) {
|
||||
total_suites_count += 1;
|
||||
std::println("{}[----------] {} tests from {} ({} ms total)\n{}",
|
||||
GREEN,
|
||||
curr_tests_count,
|
||||
suite_name,
|
||||
total_test_duration.count(),
|
||||
CLEAR);
|
||||
}
|
||||
}
|
||||
|
||||
std::println("{}[----------] Global test environment tear-down{}", GREEN, CLEAR);
|
||||
std::println("{}[==========] {} tests from {} test suites ran. ({} ms total){}",
|
||||
GREEN,
|
||||
total_tests_count,
|
||||
total_suites_count,
|
||||
total_test_duration.count(),
|
||||
CLEAR);
|
||||
|
||||
return total_failed_tests_count != 0;
|
||||
}
|
||||
|
||||
} // namespace clice::testing
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
|
||||
llvm::cl::HideUnrelatedOptions(unittest_category);
|
||||
llvm::cl::ParseCommandLineOptions(argc, argv, "clice test\n");
|
||||
|
||||
logging::stderr_logger("clice", logging::options);
|
||||
|
||||
if(!test_filter.empty()) {
|
||||
if(auto result = GlobPattern::create(test_filter)) {
|
||||
pattern.emplace(std::move(*result));
|
||||
}
|
||||
}
|
||||
|
||||
if(auto result = fs::init_resource_dir(argv[0]); !result) {
|
||||
std::println("Failed to get resource directory, because {}", result.error());
|
||||
return 1;
|
||||
}
|
||||
|
||||
using namespace clice::testing;
|
||||
return Runner::instance().run_tests();
|
||||
}
|
||||
17
cmake/archive.cmake
Normal file
17
cmake/archive.cmake
Normal file
@@ -0,0 +1,17 @@
|
||||
if(OUTPUT MATCHES "\\.tar\\.gz$")
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_COMMAND} -E tar czf "${OUTPUT}" .
|
||||
WORKING_DIRECTORY "${WORK_DIR}"
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
)
|
||||
elseif(OUTPUT MATCHES "\\.zip$")
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_COMMAND} -E tar cf "${OUTPUT}" --format=zip .
|
||||
WORKING_DIRECTORY "${WORK_DIR}"
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
)
|
||||
else()
|
||||
message(FATAL_ERROR "Unsupported archive format: ${OUTPUT}")
|
||||
endif()
|
||||
|
||||
message(STATUS "Created: ${OUTPUT}")
|
||||
@@ -1,57 +0,0 @@
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/utils.cmake)
|
||||
|
||||
# Look up a Git tag's corresponding commit SHA from a GitHub repository
|
||||
function(github_lookup_tag_commit REPO_OWNER REPO_NAME TAG_NAME OUTPUT_VAR)
|
||||
set(GITHUB_API_URL "https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/git/ref/tags/${TAG_NAME}")
|
||||
|
||||
message(STATUS "Fetching tag info from: ${GITHUB_API_URL}")
|
||||
|
||||
# Download tag info directly into a string
|
||||
download_to_string(${GITHUB_API_URL} TAG_JSON DOWNLOAD_RESULT)
|
||||
if(NOT DOWNLOAD_RESULT EQUAL "0")
|
||||
message(WARNING "Failed to fetch tag info. Result: ${DOWNLOAD_RESULT}")
|
||||
set(${OUTPUT_VAR} "NOTFOUND" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Parse the JSON to get object type and SHA
|
||||
parse_json_field_from_string("${TAG_JSON}" object "type" OBJECT_TYPE)
|
||||
parse_json_field_from_string("${TAG_JSON}" object "sha" OBJECT_SHA)
|
||||
|
||||
if(OBJECT_TYPE STREQUAL "NOTFOUND" OR OBJECT_SHA STREQUAL "NOTFOUND")
|
||||
message(WARNING "Could not find object type or SHA in the JSON response.")
|
||||
set(${OUTPUT_VAR} "NOTFOUND" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(COMMIT_SHA "NOTFOUND")
|
||||
|
||||
if(OBJECT_TYPE STREQUAL "commit")
|
||||
# Direct commit reference
|
||||
set(COMMIT_SHA ${OBJECT_SHA})
|
||||
elseif(OBJECT_TYPE STREQUAL "tag")
|
||||
# Annotated tag - need to fetch the actual commit
|
||||
set(TAG_API_URL "https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/git/tags/${OBJECT_SHA}")
|
||||
|
||||
message(STATUS "Fetching annotated tag info from: ${TAG_API_URL}")
|
||||
|
||||
# Download annotated tag info directly into a string
|
||||
download_to_string(${TAG_API_URL} ANNOTATED_TAG_JSON TAG_DOWNLOAD_RESULT)
|
||||
if(NOT TAG_DOWNLOAD_RESULT EQUAL "0")
|
||||
message(WARNING "Failed to fetch annotated tag info. Result: ${TAG_DOWNLOAD_RESULT}")
|
||||
set(${OUTPUT_VAR} "NOTFOUND" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Parse the annotated tag JSON to get the commit SHA
|
||||
parse_json_field_from_string("${ANNOTATED_TAG_JSON}" object "sha" COMMIT_SHA_FROM_TAG)
|
||||
|
||||
if(NOT COMMIT_SHA_FROM_TAG STREQUAL "NOTFOUND")
|
||||
set(COMMIT_SHA ${COMMIT_SHA_FROM_TAG})
|
||||
endif()
|
||||
else()
|
||||
message(WARNING "Unknown object type: ${OBJECT_TYPE}")
|
||||
endif()
|
||||
|
||||
set(${OUTPUT_VAR} ${COMMIT_SHA} PARENT_SCOPE)
|
||||
endfunction()
|
||||
108
cmake/llvm.cmake
Normal file
108
cmake/llvm.cmake
Normal file
@@ -0,0 +1,108 @@
|
||||
include_guard()
|
||||
|
||||
function(setup_llvm LLVM_VERSION)
|
||||
find_package(Python3 COMPONENTS Interpreter REQUIRED)
|
||||
|
||||
set(LLVM_SETUP_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/.llvm/setup-llvm.json")
|
||||
set(LLVM_SETUP_SCRIPT "${PROJECT_SOURCE_DIR}/scripts/setup-llvm.py")
|
||||
set(LLVM_SETUP_ARGS
|
||||
"--version" "${LLVM_VERSION}"
|
||||
"--build-type" "${CMAKE_BUILD_TYPE}"
|
||||
"--binary-dir" "${CMAKE_CURRENT_BINARY_DIR}"
|
||||
"--manifest" "${PROJECT_SOURCE_DIR}/config/llvm-manifest.json"
|
||||
"--output" "${LLVM_SETUP_OUTPUT}"
|
||||
)
|
||||
|
||||
if(CLICE_ENABLE_LTO)
|
||||
list(APPEND LLVM_SETUP_ARGS "--enable-lto")
|
||||
endif()
|
||||
|
||||
if(DEFINED LLVM_INSTALL_PATH AND NOT LLVM_INSTALL_PATH STREQUAL "")
|
||||
list(APPEND LLVM_SETUP_ARGS "--install-path" "${LLVM_INSTALL_PATH}")
|
||||
endif()
|
||||
|
||||
if(DEFINED CLICE_OFFLINE_BUILD AND CLICE_OFFLINE_BUILD)
|
||||
list(APPEND LLVM_SETUP_ARGS "--offline")
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND "${Python3_EXECUTABLE}" "${LLVM_SETUP_SCRIPT}" ${LLVM_SETUP_ARGS}
|
||||
RESULT_VARIABLE LLVM_SETUP_RESULT
|
||||
OUTPUT_VARIABLE LLVM_SETUP_STDOUT
|
||||
ERROR_VARIABLE LLVM_SETUP_STDERR
|
||||
ECHO_OUTPUT_VARIABLE
|
||||
ECHO_ERROR_VARIABLE
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
)
|
||||
|
||||
file(READ "${LLVM_SETUP_OUTPUT}" LLVM_SETUP_JSON)
|
||||
string(JSON LLVM_INSTALL_PATH GET "${LLVM_SETUP_JSON}" install_path)
|
||||
string(JSON LLVM_CMAKE_DIR GET "${LLVM_SETUP_JSON}" cmake_dir)
|
||||
set(LLVM_INSTALL_PATH "${LLVM_INSTALL_PATH}" CACHE PATH "Path to LLVM installation" FORCE)
|
||||
set(LLVM_CMAKE_DIR "${LLVM_CMAKE_DIR}" CACHE PATH "Path to LLVM CMake files" FORCE)
|
||||
|
||||
get_filename_component(LLVM_INSTALL_PATH "${LLVM_INSTALL_PATH}" ABSOLUTE)
|
||||
|
||||
if(NOT EXISTS "${LLVM_INSTALL_PATH}")
|
||||
message(FATAL_ERROR "Error: The specified LLVM_INSTALL_PATH does not exist: ${LLVM_INSTALL_PATH}")
|
||||
endif()
|
||||
|
||||
# set llvm include and lib path
|
||||
add_library(llvm-libs INTERFACE IMPORTED)
|
||||
|
||||
# add to include directories
|
||||
target_include_directories(llvm-libs INTERFACE "${LLVM_INSTALL_PATH}/include")
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug" AND NOT WIN32)
|
||||
target_link_directories(llvm-libs INTERFACE "${LLVM_INSTALL_PATH}/lib")
|
||||
target_link_libraries(llvm-libs INTERFACE
|
||||
LLVMSupport
|
||||
LLVMFrontendOpenMP
|
||||
LLVMOption
|
||||
LLVMTargetParser
|
||||
clangAST
|
||||
clangASTMatchers
|
||||
clangBasic
|
||||
clangDriver
|
||||
clangFormat
|
||||
clangFrontend
|
||||
clangLex
|
||||
clangSema
|
||||
clangSerialization
|
||||
clangTidy
|
||||
clangTidyUtils
|
||||
clangTidyAndroidModule
|
||||
clangTidyAbseilModule
|
||||
clangTidyAlteraModule
|
||||
clangTidyBoostModule
|
||||
clangTidyBugproneModule
|
||||
clangTidyCERTModule
|
||||
clangTidyConcurrencyModule
|
||||
clangTidyCppCoreGuidelinesModule
|
||||
clangTidyDarwinModule
|
||||
clangTidyFuchsiaModule
|
||||
clangTidyGoogleModule
|
||||
clangTidyHICPPModule
|
||||
clangTidyLinuxKernelModule
|
||||
clangTidyLLVMModule
|
||||
clangTidyLLVMLibcModule
|
||||
clangTidyMiscModule
|
||||
clangTidyModernizeModule
|
||||
clangTidyObjCModule
|
||||
clangTidyOpenMPModule
|
||||
clangTidyPerformanceModule
|
||||
clangTidyPortabilityModule
|
||||
clangTidyReadabilityModule
|
||||
clangTidyZirconModule
|
||||
clangTooling
|
||||
clangToolingCore
|
||||
clangToolingInclusions
|
||||
clangToolingInclusionsStdlib
|
||||
clangToolingSyntax
|
||||
)
|
||||
else()
|
||||
file(GLOB LLVM_LIBRARIES CONFIGURE_DEPENDS "${LLVM_INSTALL_PATH}/lib/*${CMAKE_STATIC_LIBRARY_SUFFIX}")
|
||||
target_link_libraries(llvm-libs INTERFACE ${LLVM_LIBRARIES})
|
||||
target_compile_definitions(llvm-libs INTERFACE CLANG_BUILD_STATIC=1)
|
||||
endif()
|
||||
endfunction()
|
||||
@@ -1,217 +0,0 @@
|
||||
include_guard()
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/github.cmake)
|
||||
|
||||
# Check if LLVM version is supported
|
||||
function(check_llvm_version LOCAL_LLVM_VERSION LLVM_VERSION OUTPUT_VAR)
|
||||
if((NOT DEFINED LOCAL_LLVM_VERSION) OR (LOCAL_LLVM_VERSION STREQUAL ""))
|
||||
message(WARNING "LLVM version is not set.")
|
||||
set(${OUTPUT_VAR} FALSE PARENT_SCOPE)
|
||||
elseif(NOT (LOCAL_LLVM_VERSION VERSION_GREATER_EQUAL LLVM_VERSION))
|
||||
message(WARNING "Unsupported LLVM version: ${LOCAL_LLVM_VERSION}. Only LLVM ${LLVM_VERSION} is supported.")
|
||||
set(${OUTPUT_VAR} FALSE PARENT_SCOPE)
|
||||
else()
|
||||
set(${OUTPUT_VAR} TRUE PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Look up LLVM version's corresponding commit SHA
|
||||
function(lookup_commit LLVM_VERSION OUTPUT_VAR)
|
||||
set(LLVM_TAG "llvmorg-${LLVM_VERSION}")
|
||||
github_lookup_tag_commit("llvm" "llvm-project" ${LLVM_TAG} COMMIT_SHA)
|
||||
set(${OUTPUT_VAR} ${COMMIT_SHA} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Fetch private Clang header files from LLVM source
|
||||
function(fetch_private_clang_files LLVM_VERSION)
|
||||
set(PRIVATE_CLANG_FILE_LIST
|
||||
"Sema/CoroutineStmtBuilder.h"
|
||||
"Sema/TypeLocBuilder.h"
|
||||
"Sema/TreeTransform.h"
|
||||
)
|
||||
|
||||
# Check if all files already exist
|
||||
set(PRIVATE_FILE_EXISTS TRUE)
|
||||
foreach(FILE ${PRIVATE_CLANG_FILE_LIST})
|
||||
if(EXISTS "${LLVM_INSTALL_PATH}/include/clang/${FILE}")
|
||||
message(STATUS "Private clang file found in LLVM installation: ${FILE}")
|
||||
elseif(EXISTS "${CMAKE_CURRENT_BINARY_DIR}/include/clang/${FILE}")
|
||||
message(STATUS "Private clang file already exists: ${FILE}")
|
||||
else()
|
||||
set(PRIVATE_FILE_EXISTS FALSE)
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(PRIVATE_FILE_EXISTS)
|
||||
message(STATUS "All required private clang files already exist.")
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Skip download in offline build mode
|
||||
if(CLICE_OFFLINE_BUILD)
|
||||
message(WARNING "CLICE_OFFLINE_BUILD is enabled, skipping private clang files download")
|
||||
message(WARNING "Build may fail if required private headers are missing")
|
||||
return()
|
||||
endif()
|
||||
|
||||
message(WARNING "Required private clang files incomplete, fetching from llvm-project source...")
|
||||
|
||||
# Get the commit SHA for this LLVM version
|
||||
lookup_commit(${LLVM_VERSION} LLVM_COMMIT)
|
||||
if(LLVM_COMMIT STREQUAL "NOTFOUND")
|
||||
message(WARNING "Failed to lookup commit for LLVM ${LLVM_VERSION}, skipping private clang files download")
|
||||
return()
|
||||
endif()
|
||||
|
||||
message(STATUS "LLVM ${LLVM_VERSION} corresponds to commit ${LLVM_COMMIT}")
|
||||
|
||||
# Download missing files
|
||||
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/include/clang")
|
||||
foreach(FILE ${PRIVATE_CLANG_FILE_LIST})
|
||||
set(FILE_PATH "${CMAKE_CURRENT_BINARY_DIR}/include/clang/${FILE}")
|
||||
if(NOT EXISTS "${FILE_PATH}")
|
||||
message(STATUS "Downloading ${FILE}...")
|
||||
file(DOWNLOAD "https://raw.githubusercontent.com/llvm/llvm-project/${LLVM_COMMIT}/clang/lib/${FILE}"
|
||||
"${FILE_PATH}"
|
||||
STATUS DOWNLOAD_STATUS
|
||||
TLS_VERIFY ON)
|
||||
list(GET DOWNLOAD_STATUS 0 STATUS_CODE)
|
||||
if(NOT STATUS_CODE EQUAL 0)
|
||||
message(FATAL_ERROR "Failed to download private clang file: ${FILE}")
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
# Detect system-installed LLVM using llvm-config
|
||||
function(detect_llvm OUTPUT_VAR)
|
||||
find_program(LLVM_CONFIG_EXEC llvm-config)
|
||||
|
||||
if(NOT LLVM_CONFIG_EXEC)
|
||||
set(${OUTPUT_VAR} "" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Get LLVM version and paths
|
||||
execute_process(
|
||||
COMMAND "${LLVM_CONFIG_EXEC}" --version
|
||||
OUTPUT_VARIABLE LLVM_VERSION
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
execute_process(
|
||||
COMMAND "${LLVM_CONFIG_EXEC}" --prefix
|
||||
OUTPUT_VARIABLE LLVM_INSTALL_PATH_DETECTED
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
execute_process(
|
||||
COMMAND "${LLVM_CONFIG_EXEC}" --cmakedir
|
||||
OUTPUT_VARIABLE LLVM_CMAKE_DIR_DETECTED
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
# Set cache variables
|
||||
set(LLVM_INSTALL_PATH "${LLVM_INSTALL_PATH_DETECTED}" CACHE PATH "Path to LLVM installation" FORCE)
|
||||
set(LLVM_CMAKE_DIR "${LLVM_CMAKE_DIR_DETECTED}" CACHE PATH "Path to LLVM CMake files" FORCE)
|
||||
set(${OUTPUT_VAR} ${LLVM_VERSION} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Download and install prebuilt LLVM binaries with error checking
|
||||
function(install_prebuilt_llvm LLVM_VERSION)
|
||||
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.llvm")
|
||||
|
||||
# Determine platform-specific package name
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(LLVM_BUILD_TYPE "debug")
|
||||
elseif(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
|
||||
set(LLVM_BUILD_TYPE "releasedbg")
|
||||
else()
|
||||
set(LLVM_BUILD_TYPE "release-lto")
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
set(LLVM_PACKAGE "x64-windows-msvc-${LLVM_BUILD_TYPE}.7z")
|
||||
elseif(APPLE)
|
||||
set(LLVM_PACKAGE "arm64-macosx-apple-${LLVM_BUILD_TYPE}.tar.xz")
|
||||
elseif(UNIX)
|
||||
set(LLVM_PACKAGE "x86_64-linux-gnu-${LLVM_BUILD_TYPE}.tar.xz")
|
||||
endif()
|
||||
|
||||
if(NOT LLVM_PACKAGE)
|
||||
message(FATAL_ERROR "Unsupported platform or build type for prebuilt LLVM.")
|
||||
endif()
|
||||
|
||||
set(DOWNLOAD_PATH "${CMAKE_CURRENT_BINARY_DIR}/${LLVM_PACKAGE}")
|
||||
|
||||
# Download if file does not exist
|
||||
if(NOT EXISTS "${DOWNLOAD_PATH}")
|
||||
message(STATUS "Downloading prebuilt LLVM package: ${LLVM_PACKAGE}")
|
||||
set(DOWNLOAD_URL "https://github.com/clice-io/clice-llvm/releases/download/${LLVM_VERSION}/${LLVM_PACKAGE}")
|
||||
file(DOWNLOAD "${DOWNLOAD_URL}"
|
||||
"${DOWNLOAD_PATH}"
|
||||
STATUS DOWNLOAD_STATUS
|
||||
SHOW_PROGRESS
|
||||
TLS_VERIFY ON)
|
||||
list(GET DOWNLOAD_STATUS 0 STATUS_CODE)
|
||||
list(GET DOWNLOAD_STATUS 1 ERROR_MESSAGE)
|
||||
|
||||
if(NOT STATUS_CODE EQUAL 0)
|
||||
# Download failed, remove the incomplete file to force a fresh download next time
|
||||
if(EXISTS "${DOWNLOAD_PATH}")
|
||||
file(REMOVE "${DOWNLOAD_PATH}")
|
||||
message(STATUS "Removed incomplete file: ${DOWNLOAD_PATH}")
|
||||
endif()
|
||||
message(FATAL_ERROR "Failed to download prebuilt LLVM package from ${DOWNLOAD_URL}.\nError: ${ERROR_MESSAGE}")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "Prebuilt LLVM package already exists, skipping download.")
|
||||
endif()
|
||||
|
||||
message(STATUS "Extracting LLVM package: ${LLVM_PACKAGE}")
|
||||
execute_process(
|
||||
COMMAND "${CMAKE_COMMAND}" -E tar xvf "${DOWNLOAD_PATH}"
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.llvm"
|
||||
RESULT_VARIABLE TAR_RESULT
|
||||
OUTPUT_QUIET
|
||||
ERROR_QUIET
|
||||
)
|
||||
|
||||
if(NOT TAR_RESULT EQUAL "0")
|
||||
message(FATAL_ERROR "Failed to extract archive. The file may be corrupted or the tool is missing.")
|
||||
endif()
|
||||
|
||||
# Set installation paths
|
||||
set(LLVM_INSTALL_PATH "${CMAKE_CURRENT_BINARY_DIR}/.llvm" CACHE PATH "Path to LLVM installation" FORCE)
|
||||
set(LLVM_CMAKE_DIR "${LLVM_INSTALL_PATH}/lib/cmake/llvm" CACHE PATH "Path to LLVM CMake files" FORCE)
|
||||
message(STATUS "LLVM installation path set to: ${LLVM_INSTALL_PATH}")
|
||||
endfunction()
|
||||
|
||||
# Main function to set up LLVM for the project
|
||||
function(setup_llvm LLVM_VERSION)
|
||||
if(NOT DEFINED LLVM_VERSION OR LLVM_VERSION STREQUAL "")
|
||||
message(FATAL_ERROR "setup_llvm() requires a LLVM_VERSION argument (e.g., '21.1.4').")
|
||||
endif()
|
||||
|
||||
# Use existing LLVM installation if path is already set
|
||||
if(DEFINED LLVM_INSTALL_PATH AND NOT LLVM_INSTALL_PATH STREQUAL "")
|
||||
message(STATUS "LLVM_INSTALL_PATH is set to ${LLVM_INSTALL_PATH}, using it directly.")
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(LLVM_VERSION_OK false)
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||
# Try to detect system LLVM
|
||||
detect_llvm(LOCAL_LLVM_VERSION)
|
||||
check_llvm_version("${LOCAL_LLVM_VERSION}" "${LLVM_VERSION}" LLVM_VERSION_OK)
|
||||
endif()
|
||||
|
||||
# Download prebuilt LLVM if system version is not suitable
|
||||
if(NOT LLVM_VERSION_OK)
|
||||
message(WARNING "System LLVM not found, version mismatch or incompatible build type.")
|
||||
message(WARNING "Downloading prebuilt LLVM ${LLVM_VERSION} ...")
|
||||
install_prebuilt_llvm("${LLVM_VERSION}")
|
||||
endif()
|
||||
|
||||
# Fetch required private Clang headers
|
||||
fetch_private_clang_files("${LLVM_VERSION}")
|
||||
endfunction()
|
||||
@@ -1,150 +1,56 @@
|
||||
include_guard()
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/llvm_setup.cmake)
|
||||
|
||||
setup_llvm("21.1.4")
|
||||
|
||||
get_filename_component(LLVM_INSTALL_PATH "${LLVM_INSTALL_PATH}" ABSOLUTE)
|
||||
|
||||
if(NOT EXISTS "${LLVM_INSTALL_PATH}")
|
||||
message(FATAL_ERROR "Error: The specified LLVM_INSTALL_PATH does not exist: ${LLVM_INSTALL_PATH}")
|
||||
endif()
|
||||
|
||||
# set llvm include and lib path
|
||||
add_library(llvm-libs INTERFACE IMPORTED)
|
||||
|
||||
# add to include directories
|
||||
target_include_directories(llvm-libs INTERFACE "${LLVM_INSTALL_PATH}/include")
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
target_link_directories(llvm-libs INTERFACE "${LLVM_INSTALL_PATH}/lib")
|
||||
target_link_libraries(llvm-libs INTERFACE
|
||||
LLVMSupport
|
||||
LLVMFrontendOpenMP
|
||||
LLVMOption
|
||||
LLVMTargetParser
|
||||
clangAST
|
||||
clangASTMatchers
|
||||
clangBasic
|
||||
clangDriver
|
||||
clangFormat
|
||||
clangFrontend
|
||||
clangLex
|
||||
clangSema
|
||||
clangSerialization
|
||||
clangTidy
|
||||
clangTidyUtils
|
||||
# ALL_CLANG_TIDY_CHECKS
|
||||
clangTidyAndroidModule
|
||||
clangTidyAbseilModule
|
||||
clangTidyAlteraModule
|
||||
clangTidyBoostModule
|
||||
clangTidyBugproneModule
|
||||
clangTidyCERTModule
|
||||
clangTidyConcurrencyModule
|
||||
clangTidyCppCoreGuidelinesModule
|
||||
clangTidyDarwinModule
|
||||
clangTidyFuchsiaModule
|
||||
clangTidyGoogleModule
|
||||
clangTidyHICPPModule
|
||||
clangTidyLinuxKernelModule
|
||||
clangTidyLLVMModule
|
||||
clangTidyLLVMLibcModule
|
||||
clangTidyMiscModule
|
||||
clangTidyModernizeModule
|
||||
clangTidyObjCModule
|
||||
clangTidyOpenMPModule
|
||||
clangTidyPerformanceModule
|
||||
clangTidyPortabilityModule
|
||||
clangTidyReadabilityModule
|
||||
clangTidyZirconModule
|
||||
clangTooling
|
||||
clangToolingCore
|
||||
clangToolingInclusions
|
||||
clangToolingInclusionsStdlib
|
||||
clangToolingSyntax
|
||||
)
|
||||
else()
|
||||
file(GLOB LLVM_LIBRARIES CONFIGURE_DEPENDS "${LLVM_INSTALL_PATH}/lib/*${CMAKE_STATIC_LIBRARY_SUFFIX}")
|
||||
target_link_libraries(llvm-libs INTERFACE ${LLVM_LIBRARIES})
|
||||
target_compile_definitions(llvm-libs INTERFACE CLANG_BUILD_STATIC=1)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
target_link_libraries(llvm-libs INTERFACE version ntdll)
|
||||
endif()
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/llvm.cmake)
|
||||
setup_llvm("21.1.4+r1")
|
||||
|
||||
# install dependencies
|
||||
include(FetchContent)
|
||||
|
||||
if(WIN32)
|
||||
set(NULL_DEVICE NUL)
|
||||
else()
|
||||
set(NULL_DEVICE /dev/null)
|
||||
endif()
|
||||
|
||||
# libuv
|
||||
FetchContent_Declare(
|
||||
libuv
|
||||
GIT_REPOSITORY https://github.com/libuv/libuv.git
|
||||
GIT_TAG v1.x
|
||||
)
|
||||
|
||||
if(NOT WIN32 AND CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(ASAN ON CACHE BOOL "Enable AddressSanitizer for libuv" FORCE)
|
||||
endif()
|
||||
set(LIBUV_BUILD_SHARED OFF CACHE BOOL "" FORCE)
|
||||
set(LIBUV_BUILD_TESTS OFF CACHE BOOL "" FORCE)
|
||||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
|
||||
set(FETCHCONTENT_UPDATES_DISCONNECTED ON)
|
||||
|
||||
# spdlog
|
||||
FetchContent_Declare(
|
||||
spdlog
|
||||
GIT_REPOSITORY https://github.com/gabime/spdlog.git
|
||||
GIT_TAG v1.15.3
|
||||
GIT_SHALLOW TRUE
|
||||
)
|
||||
|
||||
# tomlplusplus
|
||||
FetchContent_Declare(
|
||||
tomlplusplus
|
||||
GIT_REPOSITORY https://github.com/marzer/tomlplusplus.git
|
||||
)
|
||||
set(SPDLOG_USE_STD_FORMAT ON CACHE BOOL "" FORCE)
|
||||
set(SPDLOG_NO_EXCEPTIONS ON CACHE BOOL "" FORCE)
|
||||
|
||||
# croaring
|
||||
FetchContent_Declare(
|
||||
croaring
|
||||
GIT_REPOSITORY https://github.com/RoaringBitmap/CRoaring.git
|
||||
GIT_TAG v4.4.2
|
||||
GIT_TAG v4.4.2
|
||||
GIT_SHALLOW TRUE
|
||||
)
|
||||
set(ENABLE_ROARING_TESTS OFF CACHE INTERNAL "" FORCE)
|
||||
set(ENABLE_ROARING_MICROBENCHMARKS OFF CACHE INTERNAL "" FORCE)
|
||||
|
||||
# flatbuffers
|
||||
FetchContent_Declare(
|
||||
flatbuffers
|
||||
GIT_REPOSITORY https://github.com/google/flatbuffers.git
|
||||
GIT_TAG v25.9.23
|
||||
GIT_TAG v25.9.23
|
||||
GIT_SHALLOW TRUE
|
||||
)
|
||||
set(FLATBUFFERS_BUILD_GRPC OFF CACHE BOOL "" FORCE)
|
||||
set(FLATBUFFERS_BUILD_TESTS OFF CACHE BOOL "" FORCE)
|
||||
set(FLATBUFFERS_BUILD_FLATHASH OFF CACHE BOOL "" FORCE)
|
||||
|
||||
FetchContent_MakeAvailable(libuv spdlog tomlplusplus croaring flatbuffers)
|
||||
|
||||
if(WIN32)
|
||||
target_compile_definitions(uv_a PRIVATE _CRT_SECURE_NO_WARNINGS)
|
||||
endif()
|
||||
|
||||
if(NOT MSVC AND TARGET uv_a)
|
||||
target_compile_options(uv_a PRIVATE
|
||||
"-Wno-unused-function"
|
||||
"-Wno-unused-variable"
|
||||
"-Wno-unused-but-set-variable"
|
||||
"-Wno-deprecated-declarations"
|
||||
"-Wno-missing-braces"
|
||||
)
|
||||
endif()
|
||||
|
||||
target_compile_definitions(spdlog PUBLIC
|
||||
SPDLOG_USE_STD_FORMAT=1
|
||||
SPDLOG_NO_EXCEPTIONS=1
|
||||
FetchContent_Declare(
|
||||
eventide
|
||||
GIT_REPOSITORY https://github.com/clice-io/eventide
|
||||
GIT_TAG main
|
||||
GIT_SHALLOW TRUE
|
||||
)
|
||||
|
||||
set(ETD_ENABLE_ZEST ON)
|
||||
set(ETD_ENABLE_TEST OFF)
|
||||
set(ETD_SERDE_ENABLE_SIMDJSON ON)
|
||||
set(ETD_SERDE_ENABLE_YYJSON ON)
|
||||
set(ETD_SERDE_ENABLE_TOML ON)
|
||||
set(ETD_ENABLE_EXCEPTIONS OFF)
|
||||
set(ETD_ENABLE_RTTI OFF)
|
||||
|
||||
FetchContent_MakeAvailable(eventide spdlog croaring flatbuffers)
|
||||
|
||||
78
cmake/release.cmake
Normal file
78
cmake/release.cmake
Normal file
@@ -0,0 +1,78 @@
|
||||
include_guard()
|
||||
|
||||
set(CLICE_PACK_DIR "${PROJECT_BINARY_DIR}/pack")
|
||||
set(CLICE_SYMBOL_DIR "${PROJECT_BINARY_DIR}/pack-symbol")
|
||||
|
||||
if(WIN32)
|
||||
set(CLICE_ARCHIVE_EXT ".zip")
|
||||
set(CLICE_SYMBOL_NAME "clice.pdb")
|
||||
else()
|
||||
set(CLICE_ARCHIVE_EXT ".tar.gz")
|
||||
if(APPLE)
|
||||
set(CLICE_SYMBOL_NAME "clice.dSYM")
|
||||
else()
|
||||
set(CLICE_SYMBOL_NAME "clice.debug")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
add_custom_target(clice-strip ALL
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${CLICE_SYMBOL_DIR}"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"$<TARGET_PDB_FILE:clice>"
|
||||
"${CLICE_SYMBOL_DIR}/${CLICE_SYMBOL_NAME}"
|
||||
DEPENDS clice
|
||||
COMMENT "Collecting PDB for clice"
|
||||
)
|
||||
elseif(APPLE)
|
||||
add_custom_target(clice-strip ALL
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${CLICE_SYMBOL_DIR}"
|
||||
COMMAND dsymutil "$<TARGET_FILE:clice>" -o "${CLICE_SYMBOL_DIR}/${CLICE_SYMBOL_NAME}"
|
||||
COMMAND strip -x "$<TARGET_FILE:clice>"
|
||||
DEPENDS clice
|
||||
COMMENT "Extracting dSYM and stripping clice"
|
||||
)
|
||||
else()
|
||||
add_custom_target(clice-strip ALL
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${CLICE_SYMBOL_DIR}"
|
||||
COMMAND ${CMAKE_OBJCOPY} --only-keep-debug "$<TARGET_FILE:clice>" "${CLICE_SYMBOL_DIR}/${CLICE_SYMBOL_NAME}"
|
||||
COMMAND ${CMAKE_STRIP} --strip-debug --strip-unneeded "$<TARGET_FILE:clice>"
|
||||
COMMAND ${CMAKE_OBJCOPY} --add-gnu-debuglink="${CLICE_SYMBOL_DIR}/${CLICE_SYMBOL_NAME}" "$<TARGET_FILE:clice>"
|
||||
DEPENDS clice
|
||||
COMMENT "Extracting debug symbols and stripping clice"
|
||||
)
|
||||
endif()
|
||||
|
||||
add_custom_target(clice-pack ALL
|
||||
DEPENDS clice-strip copy_clang_resource
|
||||
COMMAND ${CMAKE_COMMAND} -E rm -rf "${CLICE_PACK_DIR}"
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${CLICE_PACK_DIR}/clice/bin"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "$<TARGET_FILE:clice>" "${CLICE_PACK_DIR}/clice/bin/"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory "${LLVM_INSTALL_PATH}/lib/clang" "${CLICE_PACK_DIR}/clice/lib/clang"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${PROJECT_SOURCE_DIR}/docs/clice.toml" "${CLICE_PACK_DIR}/clice/"
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-DOUTPUT="${PROJECT_BINARY_DIR}/clice${CLICE_ARCHIVE_EXT}"
|
||||
-DWORK_DIR="${CLICE_PACK_DIR}"
|
||||
-P "${PROJECT_SOURCE_DIR}/cmake/archive.cmake"
|
||||
COMMENT "Packaging clice distribution"
|
||||
)
|
||||
|
||||
if(APPLE)
|
||||
set(CLICE_COPY_SYMBOL_CMD ${CMAKE_COMMAND} -E copy_directory
|
||||
"${CLICE_SYMBOL_DIR}/${CLICE_SYMBOL_NAME}" "${CLICE_SYMBOL_DIR}/pack/${CLICE_SYMBOL_NAME}")
|
||||
else()
|
||||
set(CLICE_COPY_SYMBOL_CMD ${CMAKE_COMMAND} -E copy
|
||||
"${CLICE_SYMBOL_DIR}/${CLICE_SYMBOL_NAME}" "${CLICE_SYMBOL_DIR}/pack/")
|
||||
endif()
|
||||
|
||||
add_custom_target(clice-pack-symbol ALL
|
||||
DEPENDS clice-strip
|
||||
COMMAND ${CMAKE_COMMAND} -E rm -rf "${CLICE_SYMBOL_DIR}/pack"
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${CLICE_SYMBOL_DIR}/pack"
|
||||
COMMAND ${CLICE_COPY_SYMBOL_CMD}
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-DOUTPUT="${PROJECT_BINARY_DIR}/clice-symbol${CLICE_ARCHIVE_EXT}"
|
||||
-DWORK_DIR="${CLICE_SYMBOL_DIR}/pack"
|
||||
-P "${PROJECT_SOURCE_DIR}/cmake/archive.cmake"
|
||||
COMMENT "Packaging clice debug symbols"
|
||||
)
|
||||
49
cmake/toolchain.cmake
Normal file
49
cmake/toolchain.cmake
Normal file
@@ -0,0 +1,49 @@
|
||||
cmake_minimum_required(VERSION 3.30)
|
||||
|
||||
set(CMAKE_C_COMPILER clang CACHE STRING "")
|
||||
set(CMAKE_CXX_COMPILER clang++ CACHE STRING "")
|
||||
|
||||
find_program(LLVM_AR_PATH "llvm-ar")
|
||||
if(LLVM_AR_PATH)
|
||||
set(CMAKE_AR "${LLVM_AR_PATH}" CACHE FILEPATH "")
|
||||
set(CMAKE_C_COMPILER_AR "${LLVM_AR_PATH}" CACHE FILEPATH "")
|
||||
set(CMAKE_CXX_COMPILER_AR "${LLVM_AR_PATH}" CACHE FILEPATH "")
|
||||
endif()
|
||||
|
||||
find_program(LLVM_RANLIB_PATH "llvm-ranlib")
|
||||
if(LLVM_RANLIB_PATH)
|
||||
set(CMAKE_RANLIB "${LLVM_RANLIB_PATH}" CACHE FILEPATH "")
|
||||
set(CMAKE_C_COMPILER_RANLIB "${LLVM_RANLIB_PATH}" CACHE FILEPATH "")
|
||||
set(CMAKE_CXX_COMPILER_RANLIB "${LLVM_RANLIB_PATH}" CACHE FILEPATH "")
|
||||
endif()
|
||||
|
||||
find_program(LLVM_NM_PATH "llvm-nm")
|
||||
if(LLVM_NM_PATH)
|
||||
set(CMAKE_NM "${LLVM_NM_PATH}" CACHE FILEPATH "")
|
||||
endif()
|
||||
|
||||
find_program(LLVM_RC_PATH "llvm-rc")
|
||||
if(LLVM_RC_PATH)
|
||||
set(CMAKE_RC_COMPILER "${LLVM_RC_PATH}" CACHE FILEPATH "")
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
find_program(SCCACHE_PATH "sccache")
|
||||
if(SCCACHE_PATH)
|
||||
set(CMAKE_C_COMPILER_LAUNCHER "${SCCACHE_PATH}" CACHE FILEPATH "")
|
||||
set(CMAKE_CXX_COMPILER_LAUNCHER "${SCCACHE_PATH}" CACHE FILEPATH "")
|
||||
endif()
|
||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreadedDLL" CACHE STRING "")
|
||||
set(CMAKE_EXE_LINKER_FLAGS_INIT "-fuse-ld=lld-link")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_INIT "-fuse-ld=lld-link")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS_INIT "-fuse-ld=lld-link")
|
||||
else()
|
||||
find_program(CCACHE_PATH "ccache")
|
||||
if(CCACHE_PATH)
|
||||
set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_PATH}" CACHE FILEPATH "")
|
||||
set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PATH}" CACHE FILEPATH "")
|
||||
endif()
|
||||
set(CMAKE_EXE_LINKER_FLAGS_INIT "-fuse-ld=lld")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_INIT "-fuse-ld=lld")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS_INIT "-fuse-ld=lld")
|
||||
endif()
|
||||
@@ -1,55 +0,0 @@
|
||||
include_guard()
|
||||
|
||||
# Download content from URL directly into a string.
|
||||
# Usage: download_to_string(URL OUTPUT_VAR RESULT_VAR)
|
||||
# - URL: The URL to download from.
|
||||
# - OUTPUT_VAR: The variable to store the downloaded content.
|
||||
# - RESULT_VAR: The variable to store the status code (0 on success).
|
||||
function(download_to_string URL OUTPUT_VAR RESULT_VAR)
|
||||
file(DOWNLOAD
|
||||
${URL}
|
||||
${CMAKE_CURRENT_BINARY_DIR}/temp_download_file
|
||||
STATUS DOWNLOAD_STATUS
|
||||
TLS_VERIFY ON
|
||||
TIMEOUT 10 # Set a reasonable timeout
|
||||
)
|
||||
|
||||
list(GET DOWNLOAD_STATUS 0 STATUS_CODE)
|
||||
|
||||
if(NOT STATUS_CODE EQUAL 0)
|
||||
list(GET DOWNLOAD_STATUS 1 ERROR_MSG)
|
||||
message(WARNING "Failed to download from ${URL}: ${ERROR_MSG}")
|
||||
set(${RESULT_VAR} ${STATUS_CODE} PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
file(READ ${CMAKE_CURRENT_BINARY_DIR}/temp_download_file DOWNLOADED_CONTENT)
|
||||
file(REMOVE ${CMAKE_CURRENT_BINARY_DIR}/temp_download_file)
|
||||
|
||||
set(${OUTPUT_VAR} ${DOWNLOADED_CONTENT} PARENT_SCOPE)
|
||||
set(${RESULT_VAR} 0 PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Parse a nested JSON field from a string.
|
||||
# Usage: parse_json_field_from_string(JSON_STRING PARENT_KEY FIELD_NAME OUTPUT_VAR)
|
||||
# - JSON_STRING: A variable containing the JSON content as a string.
|
||||
# - PARENT_KEY: A key string to get from the JSON content.
|
||||
# - FIELD_NAME: The field name to extract.
|
||||
# - OUTPUT_VAR: The variable to store the extracted value.
|
||||
function(parse_json_field_from_string JSON_CONTENT)
|
||||
# Get the last argument as the output variable name
|
||||
set(ARGS "${ARGN}")
|
||||
list(GET ARGS -1 OUTPUT_VAR)
|
||||
list(REMOVE_AT ARGS -1)
|
||||
|
||||
string(JSON FIELD_VALUE GET "${JSON_CONTENT}" ${ARGS})
|
||||
|
||||
if(NOT FIELD_VALUE)
|
||||
string(JOIN " " FIELD_PATH_STR ${ARGS})
|
||||
message(WARNING "Could not parse field '${FIELD_PATH_STR}' from JSON.")
|
||||
set(${OUTPUT_VAR} "NOTFOUND" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(${OUTPUT_VAR} ${FIELD_VALUE} PARENT_SCOPE)
|
||||
endfunction()
|
||||
@@ -1,6 +0,0 @@
|
||||
xmake,3.0.2
|
||||
cmake,3.31.8
|
||||
python,3.13
|
||||
gcc,14
|
||||
clang,20
|
||||
msvc
|
||||
83
config/llvm-manifest.json
Normal file
83
config/llvm-manifest.json
Normal file
@@ -0,0 +1,83 @@
|
||||
[
|
||||
{
|
||||
"version": "21.1.4+r1",
|
||||
"filename": "arm64-macos-clang-debug-asan.tar.xz",
|
||||
"sha256": "7da4b7d63edefecaf11773e7e701c575140d1a07329bbbb038673b6ee4516ff5",
|
||||
"lto": false,
|
||||
"asan": true,
|
||||
"platform": "macosx",
|
||||
"build_type": "Debug"
|
||||
},
|
||||
{
|
||||
"version": "21.1.4+r1",
|
||||
"filename": "arm64-macos-clang-releasedbg-lto.tar.xz",
|
||||
"sha256": "300455b169448f9f01ae95e3bc269f489558a4ca3955e3032171cc75feca0e30",
|
||||
"lto": true,
|
||||
"asan": false,
|
||||
"platform": "macosx",
|
||||
"build_type": "RelWithDebInfo"
|
||||
},
|
||||
{
|
||||
"version": "21.1.4+r1",
|
||||
"filename": "arm64-macos-clang-releasedbg.tar.xz",
|
||||
"sha256": "9abfc6cd65b957d734ffb97610a634fb4a66d3fbe0fcfb5a1c9124ef693c1495",
|
||||
"lto": false,
|
||||
"asan": false,
|
||||
"platform": "macosx",
|
||||
"build_type": "RelWithDebInfo"
|
||||
},
|
||||
{
|
||||
"version": "21.1.4+r1",
|
||||
"filename": "x64-linux-gnu-debug-asan.tar.xz",
|
||||
"sha256": "c1ad3ec476911596a842ac67dd9c9c9475ce9f0a77b81101d3c801840292e7bc",
|
||||
"lto": false,
|
||||
"asan": true,
|
||||
"platform": "linux",
|
||||
"build_type": "Debug"
|
||||
},
|
||||
{
|
||||
"version": "21.1.4+r1",
|
||||
"filename": "x64-linux-gnu-releasedbg-lto.tar.xz",
|
||||
"sha256": "8a869c2184d139dbba704e2d712e7a68336458ad2d70622b3eb906c3e3511e54",
|
||||
"lto": true,
|
||||
"asan": false,
|
||||
"platform": "linux",
|
||||
"build_type": "RelWithDebInfo"
|
||||
},
|
||||
{
|
||||
"version": "21.1.4+r1",
|
||||
"filename": "x64-linux-gnu-releasedbg.tar.xz",
|
||||
"sha256": "552bab86f715d4f2c027f07eaaf5b3d6b8e430af0b74b470142f3f00da4feec6",
|
||||
"lto": false,
|
||||
"asan": false,
|
||||
"platform": "linux",
|
||||
"build_type": "RelWithDebInfo"
|
||||
},
|
||||
{
|
||||
"version": "21.1.4+r1",
|
||||
"filename": "x64-windows-msvc-debug-asan.tar.xz",
|
||||
"sha256": "093667a493d336c22ff3c604c5f1fea2a7d2c927c1179cec44e9a03726906ac1",
|
||||
"lto": false,
|
||||
"asan": true,
|
||||
"platform": "windows",
|
||||
"build_type": "Debug"
|
||||
},
|
||||
{
|
||||
"version": "21.1.4+r1",
|
||||
"filename": "x64-windows-msvc-releasedbg-lto.tar.xz",
|
||||
"sha256": "010539e85621dc3c6ecf359d899feb4075aeca5d0bba6625cdbec0e570e79129",
|
||||
"lto": true,
|
||||
"asan": false,
|
||||
"platform": "windows",
|
||||
"build_type": "RelWithDebInfo"
|
||||
},
|
||||
{
|
||||
"version": "21.1.4+r1",
|
||||
"filename": "x64-windows-msvc-releasedbg.tar.xz",
|
||||
"sha256": "f473c09fbea10053fac00be409d75dc228d4a38bcbc5e4aeb58b56a4b0dde78e",
|
||||
"lto": false,
|
||||
"asan": false,
|
||||
"platform": "windows",
|
||||
"build_type": "RelWithDebInfo"
|
||||
}
|
||||
]
|
||||
@@ -1,66 +0,0 @@
|
||||
[
|
||||
{
|
||||
"platform": "Windows",
|
||||
"build_type": "Release",
|
||||
"is_lto": false,
|
||||
"filename": "x64-windows-msvc-releasedbg.7z",
|
||||
"version": "21.1.4",
|
||||
"sha256": "02634ff12194994d93e9c76902866c03516c836ab7b55952933fd9ebcf039664"
|
||||
},
|
||||
{
|
||||
"platform": "Windows",
|
||||
"build_type": "Release",
|
||||
"is_lto": true,
|
||||
"filename": "x64-windows-msvc-releasedbg-lto.7z",
|
||||
"version": "21.1.4",
|
||||
"sha256": "7792cfd1e2d9240b49e3db81a6a04f33cbc44afa91e9a637c0490c28d4eee95c"
|
||||
},
|
||||
{
|
||||
"platform": "Linux",
|
||||
"build_type": "Debug",
|
||||
"is_lto": false,
|
||||
"filename": "x86_64-linux-gnu-debug.tar.xz",
|
||||
"version": "21.1.4",
|
||||
"sha256": "9c7b98e198ce1c5611e153c3602fb1dc03912f230bded99baaaa56ac1ea21cb4"
|
||||
},
|
||||
{
|
||||
"platform": "Linux",
|
||||
"build_type": "Release",
|
||||
"is_lto": false,
|
||||
"filename": "x86_64-linux-gnu-releasedbg.tar.xz",
|
||||
"version": "21.1.4",
|
||||
"sha256": "99269acd2d9c5debf30062bf57d7bfd41154f2d0baee94abdb56b421c8e5b92c"
|
||||
},
|
||||
{
|
||||
"platform": "Linux",
|
||||
"build_type": "Release",
|
||||
"is_lto": true,
|
||||
"filename": "x86_64-linux-gnu-releasedbg-lto.tar.xz",
|
||||
"version": "21.1.4",
|
||||
"sha256": "e5a6c567e30cbe51e4b98151f52071d0013839e7b5eabe7a2a2767c8234d06b2"
|
||||
},
|
||||
{
|
||||
"platform": "macosx",
|
||||
"build_type": "Debug",
|
||||
"is_lto": false,
|
||||
"filename": "arm64-macosx-apple-debug.tar.xz",
|
||||
"version": "21.1.4",
|
||||
"sha256": "ae5509d8d1cfc7441c34f6ee4fcac6eb31aae30d8b54a8f9ba3e9948a22fbc8e"
|
||||
},
|
||||
{
|
||||
"platform": "macosx",
|
||||
"build_type": "Release",
|
||||
"is_lto": false,
|
||||
"filename": "arm64-macosx-apple-releasedbg.tar.xz",
|
||||
"version": "21.1.4",
|
||||
"sha256": "dc308b0057f472e82c2eb14b300db6ee58fb4160f3428948b5a9dcae931a3378"
|
||||
},
|
||||
{
|
||||
"platform": "macosx",
|
||||
"build_type": "Release",
|
||||
"is_lto": true,
|
||||
"filename": "arm64-macosx-apple-releasedbg-lto.tar.xz",
|
||||
"version": "21.1.4",
|
||||
"sha256": "c92d0323ff83e678fec7496c8b024a64b1731c3840752be9a4ade3b99dfd52ff"
|
||||
}
|
||||
]
|
||||
@@ -1,265 +0,0 @@
|
||||
# build with multi-stage for cache efficiency
|
||||
FROM ubuntu:24.04 AS basic-tools
|
||||
|
||||
# allow build script to bind-mount project source into build container (host path)
|
||||
ARG BUILD_SRC
|
||||
|
||||
# set non-interactive frontend to avoid prompts
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
# ensure user-local bin is on PATH for non-apt installs (xmake, uv, python)
|
||||
ENV PATH="/root/.local/bin:${PATH}"
|
||||
|
||||
# install basic tools
|
||||
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||||
--mount=type=cache,target=/var/lib/apt,sharing=locked \
|
||||
# TODO: support more cache for python, xmake installation
|
||||
# TODO: check why cache doesn't work after add-apt-repository, may we change it to cache?
|
||||
bash -eux - <<'BASH'
|
||||
|
||||
set -e
|
||||
apt update
|
||||
# first install minimal apt prerequisites
|
||||
# software-properties-common for add-apt-repository
|
||||
# gnupg for gpg to verify cmake installer
|
||||
# curl, git for downloading sources
|
||||
# xz-utils, unzip for extracting archives
|
||||
# make for xmake installation
|
||||
apt install -y --no-install-recommends \
|
||||
software-properties-common \
|
||||
curl \
|
||||
gnupg \
|
||||
git \
|
||||
xz-utils \
|
||||
unzip \
|
||||
make
|
||||
|
||||
# gcc, llvm PPA
|
||||
add-apt-repository -y ppa:ubuntu-toolchain-r/ppa
|
||||
apt update
|
||||
BASH
|
||||
|
||||
# Compiler stage
|
||||
FROM basic-tools AS compiler-stage
|
||||
|
||||
# passed from build arg
|
||||
ARG COMPILER
|
||||
|
||||
# copy instead of bind-mount, to avoid docker build cache invalidation
|
||||
COPY config/default-toolchain-version /clice/config/default-toolchain-version
|
||||
|
||||
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||||
--mount=type=cache,target=/var/lib/apt,sharing=locked \
|
||||
bash -eux - <<'BASH'
|
||||
set -e
|
||||
|
||||
# Always install libstdc++ development files, required for both gcc and clang to link against libstdc++
|
||||
GCC_VERSION=$(grep -E '^gcc,' /clice/config/default-toolchain-version | cut -d',' -f2)
|
||||
apt install -y --no-install-recommends "libstdc++-${GCC_VERSION}-dev"
|
||||
|
||||
if [ "$COMPILER" = "gcc" ]; then
|
||||
apt install -y --no-install-recommends "gcc-${GCC_VERSION}" "g++-${GCC_VERSION}"
|
||||
update-alternatives --install /usr/bin/cc cc "/usr/bin/gcc-${GCC_VERSION}" 100
|
||||
update-alternatives --install /usr/bin/gcc gcc "/usr/bin/gcc-${GCC_VERSION}" 100
|
||||
update-alternatives --install /usr/bin/c++ c++ "/usr/bin/g++-${GCC_VERSION}" 100
|
||||
update-alternatives --install /usr/bin/g++ g++ "/usr/bin/g++-${GCC_VERSION}" 100
|
||||
elif [ "$COMPILER" = "clang" ]; then
|
||||
CLANG_VERSION=$(grep -E '^clang,' /clice/config/default-toolchain-version | cut -d',' -f2)
|
||||
# install clang toolchain, libstdc++ is already installed
|
||||
apt install -y --no-install-recommends "clang-${CLANG_VERSION}" "clang-tools-${CLANG_VERSION}" "lld-${CLANG_VERSION}" "libclang-rt-${CLANG_VERSION}-dev"
|
||||
update-alternatives --install /usr/bin/clang clang "/usr/bin/clang-${CLANG_VERSION}" 100
|
||||
update-alternatives --install /usr/bin/clang++ clang++ "/usr/bin/clang++-${CLANG_VERSION}" 100
|
||||
update-alternatives --install /usr/bin/c++ c++ "/usr/bin/clang++-${CLANG_VERSION}" 100
|
||||
update-alternatives --install /usr/bin/cc cc "/usr/bin/clang-${CLANG_VERSION}" 100
|
||||
update-alternatives --install /usr/bin/ld ld "/usr/bin/lld-${CLANG_VERSION}" 100
|
||||
else
|
||||
echo "Error: Unsupported compiler '$COMPILER'. Use 'gcc' or 'clang'." >&2; exit 1
|
||||
fi
|
||||
BASH
|
||||
|
||||
FROM compiler-stage AS build-tool-stage
|
||||
|
||||
ARG XMAKE_CACHE_DIR="/docker-build-cache/xmake"
|
||||
ARG CMAKE_CACHE_DIR="/docker-build-cache/cmake"
|
||||
ARG UV_CACHE_DIR="/var/cache/uv"
|
||||
|
||||
ENV XMAKE_CACHE_DIR=${XMAKE_CACHE_DIR}
|
||||
ENV CMAKE_CACHE_DIR=${CMAKE_CACHE_DIR}
|
||||
ENV UV_CACHE_DIR=${UV_CACHE_DIR}
|
||||
|
||||
COPY ./pyproject.toml /clice/pyproject.toml
|
||||
|
||||
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||||
--mount=type=cache,target=/var/lib/apt,sharing=locked \
|
||||
--mount=type=cache,target=${XMAKE_CACHE_DIR},sharing=locked \
|
||||
--mount=type=cache,target=${CMAKE_CACHE_DIR},sharing=locked \
|
||||
--mount=type=cache,target=${UV_CACHE_DIR},sharing=locked \
|
||||
bash -eux - <<'BASH'
|
||||
|
||||
install_xmake() {
|
||||
set -e
|
||||
|
||||
XMAKE_VERSION=$(grep -E '^xmake,' /clice/config/default-toolchain-version | cut -d',' -f2)
|
||||
XMAKE_BASE_URL="https://github.com/xmake-io/xmake/releases/download/v${XMAKE_VERSION}"
|
||||
XMAKE_FILENAME="xmake-bundle-v${XMAKE_VERSION}.linux.x86_64"
|
||||
XMAKE_CACHED_FILE="${XMAKE_CACHE_DIR}/${XMAKE_FILENAME}"
|
||||
|
||||
if [ ! -f "${XMAKE_CACHED_FILE}" ] ; then
|
||||
rm -f "${XMAKE_CACHE_DIR}/*"
|
||||
curl -fsSL --retry 3 -o "${XMAKE_CACHED_FILE}" "${XMAKE_BASE_URL}/${XMAKE_FILENAME}"
|
||||
fi
|
||||
|
||||
XMAKE_INSTALL_DIR="/usr/bin"
|
||||
XMAKE_INSTALLED_FILE="${XMAKE_INSTALL_DIR}/${XMAKE_FILENAME}"
|
||||
|
||||
cp "${XMAKE_CACHED_FILE}" "${XMAKE_INSTALLED_FILE}"
|
||||
chmod +x "${XMAKE_INSTALLED_FILE}"
|
||||
|
||||
update-alternatives --install /usr/bin/xmake xmake "${XMAKE_INSTALLED_FILE}" 100
|
||||
|
||||
echo "export XMAKE_ROOT=y" >> ~/.bashrc
|
||||
}
|
||||
|
||||
# Attention: DO NOT install cmake via PPA with apt, which would have to install required build-essential compiler tool chain
|
||||
# We SHOULD NOT install another compiler toolchain, which could cause a lot trouble
|
||||
# And we should not install compiler toolchain away from compiler stage
|
||||
# So we install cmake from official installer script, and cache the downloaded files
|
||||
install_cmake() {
|
||||
set -e
|
||||
|
||||
# cached downloads live under /docker-build-cache/cmake (BuildKit cache mount)
|
||||
CMAKE_VERSION=$(grep -E '^cmake,' /clice/config/default-toolchain-version | cut -d',' -f2)
|
||||
ARCH="x86_64"
|
||||
|
||||
BASE_URL="https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}"
|
||||
INSTALLER_FILENAME="cmake-${CMAKE_VERSION}-linux-${ARCH}.sh"
|
||||
SHA_FILENAME="cmake-${CMAKE_VERSION}-SHA-256.txt"
|
||||
ASC_FILENAME="${SHA_FILENAME}.asc"
|
||||
|
||||
INSTALLER_PATH="${CMAKE_CACHE_DIR}/${INSTALLER_FILENAME}"
|
||||
SHA_PATH="${CMAKE_CACHE_DIR}/${SHA_FILENAME}"
|
||||
ASC_PATH="${CMAKE_CACHE_DIR}/${ASC_FILENAME}"
|
||||
|
||||
verify_cmake_installer() {
|
||||
if ! gpg --verify "${ASC_PATH}" "${SHA_PATH}"; then
|
||||
echo "Signature verification failed for ${SHA_FILENAME}." >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local expected_hash
|
||||
expected_hash=$(grep "${INSTALLER_FILENAME}" "${SHA_PATH}" | awk '{print $1}')
|
||||
|
||||
local actual_hash
|
||||
actual_hash=$(sha256sum "${INSTALLER_PATH}" | awk '{print $1}')
|
||||
if [ "${expected_hash}" != "${actual_hash}" ]; then
|
||||
echo "Checksum mismatch for ${INSTALLER_FILENAME}." >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "Checksum for ${INSTALLER_FILENAME} is valid."
|
||||
return 0
|
||||
}
|
||||
|
||||
gpg --keyserver keys.openpgp.org --recv-keys C6C265324BBEBDC350B513D02D2CEF1034921684
|
||||
|
||||
if [ ! -f "${INSTALLER_PATH}" ] || ! verify_cmake_installer; then
|
||||
rm -f "${CMAKE_CACHE_DIR}/*"
|
||||
|
||||
curl -fsSL --retry 3 -o "${INSTALLER_PATH}" "${BASE_URL}/${INSTALLER_FILENAME}"
|
||||
curl -fsSL --retry 3 -o "${SHA_PATH}" "${BASE_URL}/${SHA_FILENAME}"
|
||||
curl -fsSL --retry 3 -o "${ASC_PATH}" "${BASE_URL}/${ASC_FILENAME}"
|
||||
|
||||
if ! verify_cmake_installer; then
|
||||
echo "Verification of the downloaded installer failed. Cleaning cache." >&2
|
||||
rm -f "${CMAKE_CACHE_DIR}/*"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
sh "${INSTALLER_PATH}" --skip-license --prefix=/usr/local
|
||||
}
|
||||
|
||||
install_python() {
|
||||
set -e
|
||||
PYTHON_VERSION=$(grep -E '^python,' /clice/config/default-toolchain-version | cut -d',' -f2)
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
uv python install "${PYTHON_VERSION}"
|
||||
uv sync
|
||||
}
|
||||
|
||||
do_install() {
|
||||
set -e
|
||||
|
||||
cd /clice
|
||||
|
||||
export PATH="/root/.local/bin:${PATH}"
|
||||
echo "export XMAKE_ROOT=y" >> ~/.bashrc
|
||||
|
||||
install_cmake &
|
||||
install_xmake &
|
||||
install_python &
|
||||
|
||||
for job in $(jobs -p); do
|
||||
wait $job || exit 1
|
||||
done
|
||||
}
|
||||
|
||||
do_install
|
||||
|
||||
BASH
|
||||
|
||||
# download compile dependencies
|
||||
FROM build-tool-stage AS dependency-cache-stage
|
||||
|
||||
# passed from build arg
|
||||
# "lto" or "non_lto"
|
||||
ARG BUILD_SRC
|
||||
# ARG LTO_TYPE=""
|
||||
|
||||
RUN --mount=type=bind,src=./,target=/clice,rw \
|
||||
bash -eux - <<'BASH'
|
||||
|
||||
# cache_xmake_packages() {
|
||||
# set -e
|
||||
|
||||
# export PATH="/root/.local/bin:${PATH}"
|
||||
# export XMAKE_ROOT=y
|
||||
|
||||
# LTO_FLAG=""
|
||||
# if [ "$LTO_TYPE" = "lto" ]; then
|
||||
# LTO_FLAG="--release"
|
||||
# fi
|
||||
|
||||
# xmake f -y -v --mode=release ${LTO_FLAG}
|
||||
# xmake f -y -v --mode=debug ${LTO_FLAG}
|
||||
# }
|
||||
|
||||
do_prepare_dependency() {
|
||||
set -e
|
||||
|
||||
cd /clice
|
||||
|
||||
# cache_xmake_packages &
|
||||
|
||||
for job in $(jobs -p); do
|
||||
wait $job || exit 1
|
||||
done
|
||||
}
|
||||
|
||||
do_prepare_dependency
|
||||
|
||||
BASH
|
||||
|
||||
FROM dependency-cache-stage AS final
|
||||
|
||||
RUN bash -eux - <<'BASH'
|
||||
set -e
|
||||
# clice is mounted here, so remove everything to reduce image size
|
||||
rm -rf /clice
|
||||
|
||||
# disable git exception in cmake build when Fetch-Content
|
||||
git config --global --add safe.directory '*'
|
||||
BASH
|
||||
|
||||
WORKDIR /clice
|
||||
|
||||
CMD ["/bin/bash"]
|
||||
@@ -1,54 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Save original working directory and switch to project root
|
||||
ORIG_PWD="$(pwd)"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
echo "${SCRIPT_DIR}"
|
||||
cd "${SCRIPT_DIR}/../.."
|
||||
PROJECT_ROOT="$(pwd)"
|
||||
|
||||
trap 'cd "${ORIG_PWD}"' EXIT
|
||||
|
||||
# default configurations
|
||||
COMPILER="clang"
|
||||
DOCKERFILE_PATH="docker/linux/Dockerfile"
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: $0 [--compiler <gcc|clang>]
|
||||
|
||||
Defaults:
|
||||
--compiler ${COMPILER}
|
||||
EOF
|
||||
}
|
||||
|
||||
# parse command line arguments
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case "$1" in
|
||||
--compiler)
|
||||
COMPILER="$2"; shift 2;;
|
||||
-h|--help)
|
||||
usage; exit 0;;
|
||||
*)
|
||||
echo "Unknown parameter: $1" >&2; usage; exit 1;;
|
||||
esac
|
||||
done
|
||||
|
||||
IMAGE_TAG="linux-${COMPILER}"
|
||||
IMAGE_NAME="clice-io/clice-dev:${IMAGE_TAG}"
|
||||
|
||||
echo "==========================================="
|
||||
echo "Building image: ${IMAGE_NAME}"
|
||||
echo "Compiler: ${COMPILER}"
|
||||
echo "Dockerfile: ${DOCKERFILE_PATH}"
|
||||
echo "==========================================="
|
||||
|
||||
# build the docker image with specified arguments
|
||||
# must run in clice root dir, so that we can mount the project in docker file to acquire essential files
|
||||
docker buildx build --progress=plain -t "${IMAGE_NAME}" \
|
||||
--build-arg COMPILER="${COMPILER}" \
|
||||
--build-arg BUILD_SRC="${PROJECT_ROOT}" \
|
||||
-f "${DOCKERFILE_PATH}" .
|
||||
|
||||
echo "Build complete. Image:${IMAGE_NAME}"
|
||||
@@ -1,87 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Save original working directory and switch to project root
|
||||
ORIG_PWD="$(pwd)"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
echo "${SCRIPT_DIR}"
|
||||
cd "${SCRIPT_DIR}/../.."
|
||||
PROJECT_ROOT="$(pwd)"
|
||||
|
||||
trap 'cd "${ORIG_PWD}"' EXIT
|
||||
|
||||
# default configurations
|
||||
COMPILER="clang"
|
||||
RESET="false"
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: $0 [--compiler <gcc|clang>] [--reset]
|
||||
|
||||
Defaults:
|
||||
--compiler ${COMPILER}
|
||||
--reset (re-create the container)
|
||||
EOF
|
||||
}
|
||||
|
||||
# parse command line arguments
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case "$1" in
|
||||
--compiler)
|
||||
COMPILER="$2"; shift 2;;
|
||||
--reset)
|
||||
RESET="true"; shift 1;;
|
||||
-h|--help)
|
||||
usage; exit 0;;
|
||||
*) echo "Unknown parameter: $1"; usage; exit 1;;
|
||||
esac
|
||||
done
|
||||
|
||||
IMAGE_TAG="linux-${COMPILER}"
|
||||
IMAGE_NAME="clice-io/clice-dev:${IMAGE_TAG}"
|
||||
CONTAINER_NAME="clice-dev-linux-${COMPILER}"
|
||||
|
||||
# If the image doesn't exist, build it automatically by invoking build.sh
|
||||
if ! docker image inspect "${IMAGE_NAME}" >/dev/null 2>&1; then
|
||||
echo "Image ${IMAGE_NAME} not found, invoking build.sh to create it..."
|
||||
./docker/linux/build.sh --compiler "${COMPILER}"
|
||||
fi
|
||||
|
||||
# Handle --reset: remove the existing container if it exists
|
||||
if [ "${RESET}" = "true" ]; then
|
||||
if docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
|
||||
echo "Resetting container: stopping and removing existing container ${CONTAINER_NAME}..."
|
||||
docker stop "${CONTAINER_NAME}" >/dev/null 2>&1 || true
|
||||
docker rm "${CONTAINER_NAME}" >/dev/null 2>&1
|
||||
echo "Container ${CONTAINER_NAME} has been removed."
|
||||
else
|
||||
echo "Container ${CONTAINER_NAME} does not exist, no need to reset."
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
CONTAINER_WORKDIR="/clice"
|
||||
|
||||
# Check if the container exists
|
||||
if docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
|
||||
echo "==========================================="
|
||||
echo "Attaching to existing container: ${CONTAINER_NAME}"
|
||||
echo "From image: ${IMAGE_NAME}"
|
||||
echo "Project mount: ${PROJECT_ROOT} -> ${CONTAINER_WORKDIR}"
|
||||
echo "==========================================="
|
||||
docker start "${CONTAINER_NAME}" >/dev/null
|
||||
docker exec -it -w "${CONTAINER_WORKDIR}" "${CONTAINER_NAME}" /bin/bash
|
||||
exit 0
|
||||
fi
|
||||
|
||||
DOCKER_RUN_ARGS=(-it -w "${CONTAINER_WORKDIR}")
|
||||
DOCKER_RUN_ARGS+=(--name "${CONTAINER_NAME}")
|
||||
DOCKER_RUN_ARGS+=(--mount "type=bind,src=${PROJECT_ROOT},target=${CONTAINER_WORKDIR}")
|
||||
|
||||
echo "==========================================="
|
||||
echo "Creating and running new container: ${CONTAINER_NAME}"
|
||||
echo "From image: ${IMAGE_NAME}"
|
||||
echo "Project mount: ${PROJECT_ROOT} -> ${CONTAINER_WORKDIR}"
|
||||
echo "==========================================="
|
||||
|
||||
docker run "${DOCKER_RUN_ARGS[@]}" "${IMAGE_NAME}"
|
||||
@@ -1,40 +1,38 @@
|
||||
import { defineConfig } from 'vitepress'
|
||||
import { genSidebar } from './theme/sidebar'
|
||||
import { defineConfig } from "vitepress";
|
||||
import { genSidebar } from "./theme/sidebar";
|
||||
|
||||
// https://vitepress.dev/reference/site-config
|
||||
export default defineConfig({
|
||||
title: "clice",
|
||||
description: "a powerful and modern C++ language server",
|
||||
cleanUrls: true,
|
||||
base: '/',
|
||||
rewrites: {
|
||||
'en/:rest*': ':rest*',
|
||||
},
|
||||
locales: {
|
||||
root: { label: 'English' },
|
||||
zh: { label: '简体中文' },
|
||||
},
|
||||
themeConfig: {
|
||||
// https://vitepress.dev/reference/default-theme-config
|
||||
nav: [
|
||||
{ text: 'Home', link: '/' },
|
||||
],
|
||||
sidebar: {
|
||||
"/zh/": [
|
||||
genSidebar('zh', 'design', { title: 'Design' }),
|
||||
genSidebar('zh', 'dev', { title: 'Development' }),
|
||||
genSidebar('zh', 'guide', { title: 'Guide' }),
|
||||
],
|
||||
"/": [
|
||||
genSidebar('en', 'design', { title: 'Design' }),
|
||||
genSidebar('en', 'dev', { title: 'Development' }),
|
||||
genSidebar('en', 'guide', { title: 'Guide' }),
|
||||
],
|
||||
title: "clice",
|
||||
description: "a powerful and modern C++ language server",
|
||||
cleanUrls: true,
|
||||
base: "/clice/",
|
||||
rewrites: {
|
||||
"en/:rest*": ":rest*",
|
||||
},
|
||||
socialLinks: [
|
||||
{ icon: 'discord', link: 'https://discord.gg/PA3UxW2VA3' },
|
||||
{ icon: 'github', link: 'https://github.com/clice-io/clice' },
|
||||
],
|
||||
outline: 'deep',
|
||||
},
|
||||
})
|
||||
locales: {
|
||||
root: { label: "English" },
|
||||
zh: { label: "简体中文" },
|
||||
},
|
||||
themeConfig: {
|
||||
// https://vitepress.dev/reference/default-theme-config
|
||||
nav: [{ text: "Home", link: "/" }],
|
||||
sidebar: {
|
||||
"/zh/": [
|
||||
genSidebar("zh", "design", { title: "Design" }),
|
||||
genSidebar("zh", "dev", { title: "Development" }),
|
||||
genSidebar("zh", "guide", { title: "Guide" }),
|
||||
],
|
||||
"/": [
|
||||
genSidebar("en", "design", { title: "Design" }),
|
||||
genSidebar("en", "dev", { title: "Development" }),
|
||||
genSidebar("en", "guide", { title: "Guide" }),
|
||||
],
|
||||
},
|
||||
socialLinks: [
|
||||
{ icon: "discord", link: "https://discord.gg/PA3UxW2VA3" },
|
||||
{ icon: "github", link: "https://github.com/clice-io/clice" },
|
||||
],
|
||||
outline: "deep",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
// https://vitepress.dev/guide/custom-theme
|
||||
import { h } from 'vue'
|
||||
import type { Theme } from 'vitepress'
|
||||
import DefaultTheme from 'vitepress/theme'
|
||||
import './style.css'
|
||||
import { h } from "vue";
|
||||
import type { Theme } from "vitepress";
|
||||
import DefaultTheme from "vitepress/theme";
|
||||
import "./style.css";
|
||||
|
||||
export default {
|
||||
extends: DefaultTheme,
|
||||
Layout: () => {
|
||||
return h(DefaultTheme.Layout, null, {
|
||||
// https://vitepress.dev/guide/extending-default-theme#layout-slots
|
||||
})
|
||||
},
|
||||
enhanceApp({ app, router, siteData }) {
|
||||
// ...
|
||||
}
|
||||
} satisfies Theme
|
||||
extends: DefaultTheme,
|
||||
Layout: () => {
|
||||
return h(DefaultTheme.Layout, null, {
|
||||
// https://vitepress.dev/guide/extending-default-theme#layout-slots
|
||||
});
|
||||
},
|
||||
enhanceApp({ app, router, siteData }) {
|
||||
// ...
|
||||
},
|
||||
} satisfies Theme;
|
||||
|
||||
@@ -1,45 +1,45 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { DefaultTheme } from 'vitepress'
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { DefaultTheme } from "vitepress";
|
||||
|
||||
export const genSidebar = (
|
||||
lang: string,
|
||||
dirPath: string,
|
||||
options: {
|
||||
title: string
|
||||
collapsible?: boolean
|
||||
ignore?: string[]
|
||||
}
|
||||
title: string;
|
||||
collapsible?: boolean;
|
||||
ignore?: string[];
|
||||
},
|
||||
): DefaultTheme.SidebarItem => {
|
||||
const sidebarPath = path.resolve(process.cwd(), lang, dirPath)
|
||||
const ignore = options.ignore || ['index.md']
|
||||
const sidebarPath = path.resolve(process.cwd(), lang, dirPath);
|
||||
const ignore = options.ignore || ["index.md"];
|
||||
|
||||
const files = fs
|
||||
.readdirSync(sidebarPath)
|
||||
.filter((file) => file.endsWith('.md') && !ignore.includes(file))
|
||||
.filter((file) => file.endsWith(".md") && !ignore.includes(file));
|
||||
|
||||
const items = files.map((file) => {
|
||||
const content = fs.readFileSync(path.resolve(sidebarPath, file), 'utf-8')
|
||||
const match = content.match(/^#\s+(.*)/)
|
||||
const title = match ? match[1] : file.replace('.md', '')
|
||||
const content = fs.readFileSync(path.resolve(sidebarPath, file), "utf-8");
|
||||
const match = content.match(/^#\s+(.*)/);
|
||||
const title = match ? match[1] : file.replace(".md", "");
|
||||
|
||||
let prefix = '/';
|
||||
if (lang != 'en') {
|
||||
let prefix = "/";
|
||||
if (lang != "en") {
|
||||
prefix += lang;
|
||||
prefix += '/';
|
||||
prefix += "/";
|
||||
}
|
||||
|
||||
return {
|
||||
text: title,
|
||||
/// Make sure link for en is actually root, beacuse Github Pages
|
||||
/// doesn't support redirect url.
|
||||
link: `${prefix}${dirPath}/${file.replace('.md', '')}`
|
||||
}
|
||||
})
|
||||
link: `${prefix}${dirPath}/${file.replace(".md", "")}`,
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
text: options.title,
|
||||
collapsed: options.collapsible || false,
|
||||
items
|
||||
}
|
||||
}
|
||||
items,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -44,30 +44,30 @@
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
:root {
|
||||
--vp-c-default-1: var(--vp-c-gray-1);
|
||||
--vp-c-default-2: var(--vp-c-gray-2);
|
||||
--vp-c-default-3: var(--vp-c-gray-3);
|
||||
--vp-c-default-soft: var(--vp-c-gray-soft);
|
||||
--vp-c-default-1: var(--vp-c-gray-1);
|
||||
--vp-c-default-2: var(--vp-c-gray-2);
|
||||
--vp-c-default-3: var(--vp-c-gray-3);
|
||||
--vp-c-default-soft: var(--vp-c-gray-soft);
|
||||
|
||||
--vp-c-brand-1: var(--vp-c-indigo-1);
|
||||
--vp-c-brand-2: var(--vp-c-indigo-2);
|
||||
--vp-c-brand-3: var(--vp-c-indigo-3);
|
||||
--vp-c-brand-soft: var(--vp-c-indigo-soft);
|
||||
--vp-c-brand-1: var(--vp-c-indigo-1);
|
||||
--vp-c-brand-2: var(--vp-c-indigo-2);
|
||||
--vp-c-brand-3: var(--vp-c-indigo-3);
|
||||
--vp-c-brand-soft: var(--vp-c-indigo-soft);
|
||||
|
||||
--vp-c-tip-1: var(--vp-c-brand-1);
|
||||
--vp-c-tip-2: var(--vp-c-brand-2);
|
||||
--vp-c-tip-3: var(--vp-c-brand-3);
|
||||
--vp-c-tip-soft: var(--vp-c-brand-soft);
|
||||
--vp-c-tip-1: var(--vp-c-brand-1);
|
||||
--vp-c-tip-2: var(--vp-c-brand-2);
|
||||
--vp-c-tip-3: var(--vp-c-brand-3);
|
||||
--vp-c-tip-soft: var(--vp-c-brand-soft);
|
||||
|
||||
--vp-c-warning-1: var(--vp-c-yellow-1);
|
||||
--vp-c-warning-2: var(--vp-c-yellow-2);
|
||||
--vp-c-warning-3: var(--vp-c-yellow-3);
|
||||
--vp-c-warning-soft: var(--vp-c-yellow-soft);
|
||||
--vp-c-warning-1: var(--vp-c-yellow-1);
|
||||
--vp-c-warning-2: var(--vp-c-yellow-2);
|
||||
--vp-c-warning-3: var(--vp-c-yellow-3);
|
||||
--vp-c-warning-soft: var(--vp-c-yellow-soft);
|
||||
|
||||
--vp-c-danger-1: var(--vp-c-red-1);
|
||||
--vp-c-danger-2: var(--vp-c-red-2);
|
||||
--vp-c-danger-3: var(--vp-c-red-3);
|
||||
--vp-c-danger-soft: var(--vp-c-red-soft);
|
||||
--vp-c-danger-1: var(--vp-c-red-1);
|
||||
--vp-c-danger-2: var(--vp-c-red-2);
|
||||
--vp-c-danger-3: var(--vp-c-red-3);
|
||||
--vp-c-danger-soft: var(--vp-c-red-soft);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,15 +75,15 @@
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
:root {
|
||||
--vp-button-brand-border: transparent;
|
||||
--vp-button-brand-text: var(--vp-c-white);
|
||||
--vp-button-brand-bg: var(--vp-c-brand-3);
|
||||
--vp-button-brand-hover-border: transparent;
|
||||
--vp-button-brand-hover-text: var(--vp-c-white);
|
||||
--vp-button-brand-hover-bg: var(--vp-c-brand-2);
|
||||
--vp-button-brand-active-border: transparent;
|
||||
--vp-button-brand-active-text: var(--vp-c-white);
|
||||
--vp-button-brand-active-bg: var(--vp-c-brand-1);
|
||||
--vp-button-brand-border: transparent;
|
||||
--vp-button-brand-text: var(--vp-c-white);
|
||||
--vp-button-brand-bg: var(--vp-c-brand-3);
|
||||
--vp-button-brand-hover-border: transparent;
|
||||
--vp-button-brand-hover-text: var(--vp-c-white);
|
||||
--vp-button-brand-hover-bg: var(--vp-c-brand-2);
|
||||
--vp-button-brand-active-border: transparent;
|
||||
--vp-button-brand-active-text: var(--vp-c-white);
|
||||
--vp-button-brand-active-bg: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -91,31 +91,23 @@
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
:root {
|
||||
--vp-home-hero-name-color: transparent;
|
||||
--vp-home-hero-name-background: -webkit-linear-gradient(
|
||||
120deg,
|
||||
#bd34fe 30%,
|
||||
#41d1ff
|
||||
);
|
||||
--vp-home-hero-name-color: transparent;
|
||||
--vp-home-hero-name-background: -webkit-linear-gradient(120deg, #bd34fe 30%, #41d1ff);
|
||||
|
||||
--vp-home-hero-image-background-image: linear-gradient(
|
||||
-45deg,
|
||||
#bd34fe 50%,
|
||||
#47caff 50%
|
||||
);
|
||||
--vp-home-hero-image-filter: blur(44px);
|
||||
--vp-home-hero-image-background-image: linear-gradient(-45deg, #bd34fe 50%, #47caff 50%);
|
||||
--vp-home-hero-image-filter: blur(44px);
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
:root {
|
||||
--vp-home-hero-image-filter: blur(56px);
|
||||
}
|
||||
:root {
|
||||
--vp-home-hero-image-filter: blur(56px);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
:root {
|
||||
--vp-home-hero-image-filter: blur(68px);
|
||||
}
|
||||
:root {
|
||||
--vp-home-hero-image-filter: blur(68px);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -123,10 +115,10 @@
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
:root {
|
||||
--vp-custom-block-tip-border: transparent;
|
||||
--vp-custom-block-tip-text: var(--vp-c-text-1);
|
||||
--vp-custom-block-tip-bg: var(--vp-c-brand-soft);
|
||||
--vp-custom-block-tip-code-bg: var(--vp-c-brand-soft);
|
||||
--vp-custom-block-tip-border: transparent;
|
||||
--vp-custom-block-tip-text: var(--vp-c-text-1);
|
||||
--vp-custom-block-tip-bg: var(--vp-c-brand-soft);
|
||||
--vp-custom-block-tip-code-bg: var(--vp-c-brand-soft);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -134,5 +126,5 @@
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
.DocSearch {
|
||||
--docsearch-primary-color: var(--vp-c-brand-1) !important;
|
||||
--docsearch-primary-color: var(--vp-c-brand-1) !important;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
### clice configuration
|
||||
## # clice configuration
|
||||
|
||||
# This section outlines the supported built-in variables for clice.
|
||||
# These variables can be referenced in strings using the syntax `${var}`.
|
||||
@@ -9,47 +9,39 @@
|
||||
# - `${workspace}`: The workspace directory provided by the client.
|
||||
|
||||
[project]
|
||||
# Enable experimental clang-tidy diagnostics.
|
||||
# This feature is tracked in https://github.com/clice-project/clice/issues/90.
|
||||
clang_tidy = false
|
||||
|
||||
# Maximum number of active files to keep in memory. If the number of active files
|
||||
# exceeds this limit, the least recently used files will be removed.
|
||||
# The default value is 8. Whatever the number you set, the minimum is 1, the maximum is 512.
|
||||
max_active_file = 8
|
||||
|
||||
# Directory for storing PCH and PCM files.
|
||||
cache_dir = "${workspace}/.clice/cache"
|
||||
|
||||
# Directory for storing index files.
|
||||
index_dir = "${workspace}/.clice/index"
|
||||
|
||||
logging_dir = "${workspace}/.clice/logging"
|
||||
|
||||
# Compile commands directories to search for compile_commands.json files.
|
||||
compile_commands_dirs = ["${workspace}/build"]
|
||||
|
||||
# Enable experimental clang-tidy diagnostics.
|
||||
# This feature is tracked in https://github.com/clice-project/clice/issues/90.
|
||||
clang_tidy = false
|
||||
# Maximum number of active files to keep in memory. If the number of active files
|
||||
# exceeds this limit, the least recently used files will be removed.
|
||||
# The default value is 8. Whatever the number you set, the minimum is 1, the maximum is 512.
|
||||
max_active_file = 8
|
||||
# Directory for storing PCH and PCM files.
|
||||
cache_dir = "${workspace}/.clice/cache"
|
||||
# Directory for storing index files.
|
||||
index_dir = "${workspace}/.clice/index"
|
||||
logging_dir = "${workspace}/.clice/logging"
|
||||
# Compile commands files or directories to search for compile_commands.json files.
|
||||
compile_commands_paths = ["${workspace}/build"]
|
||||
|
||||
# Control the behavior for specific files. Note that Clice matches rules
|
||||
# in order. If you want to add your own rules, either delete this rule
|
||||
# or insert your rule before it.
|
||||
[[rules]]
|
||||
# Files matching the specified pattern will have this rule applied.
|
||||
#
|
||||
# Patterns can use the following syntax:
|
||||
# - `*`: Matches one or more characters in a path segment.
|
||||
# - `?`: Matches a single character in a path segment.
|
||||
# - `**`: Matches any number of path segments, including none.
|
||||
# - `{}`: Groups conditions (e.g., `**/*.{ts,js}` matches all TypeScript
|
||||
# and JavaScript files).
|
||||
# - `[]`: Declares a range of characters to match in a path segment
|
||||
# (e.g., `example.[0-9]` matches `example.0`, `example.1`, etc.).
|
||||
# - `[!...]`: Negates a range of characters to match in a path segment
|
||||
# (e.g., `example.[!0-9]` matches `example.a`, `example.b`, but not `example.0`).
|
||||
patterns = ["**/*"]
|
||||
|
||||
# Commands to append to the original command list (e.g., ["-std=c++17"]).
|
||||
append = []
|
||||
|
||||
# Commands to remove from the original command list.
|
||||
remove = []
|
||||
# Files matching the specified pattern will have this rule applied.
|
||||
#
|
||||
# Patterns can use the following syntax:
|
||||
# - `*`: Matches one or more characters in a path segment.
|
||||
# - `?`: Matches a single character in a path segment.
|
||||
# - `**`: Matches any number of path segments, including none.
|
||||
# - `{}`: Groups conditions (e.g., `**/*.{ts,js}` matches all TypeScript
|
||||
# and JavaScript files).
|
||||
# - `[]`: Declares a range of characters to match in a path segment
|
||||
# (e.g., `example.[0-9]` matches `example.0`, `example.1`, etc.).
|
||||
# - `[!...]`: Negates a range of characters to match in a path segment
|
||||
# (e.g., `example.[!0-9]` matches `example.a`, `example.b`, but not `example.0`).
|
||||
patterns = ["**/*"]
|
||||
# Commands to append to the original command list (e.g., ["-std=c++17"]).
|
||||
append = []
|
||||
# Commands to remove from the original command list.
|
||||
remove = []
|
||||
|
||||
183
docs/en/architecture.md
Normal file
183
docs/en/architecture.md
Normal file
@@ -0,0 +1,183 @@
|
||||
# Server Architecture
|
||||
|
||||
clice uses a **multi-process architecture** where a single **Master Server** coordinates multiple **Worker** processes. This design isolates Clang AST operations (which are memory-heavy and may crash) from the main LSP event loop.
|
||||
|
||||
## Overview
|
||||
|
||||
```
|
||||
┌──────────────┐ JSON/LSP ┌────────────────┐ Bincode/IPC ┌──────────────────┐
|
||||
│ LSP Client │ ◄──────────► │ Master Server │ ◄─────────────► │ Stateful Workers │
|
||||
│ (Editor) │ (stdio) │ │ (stdio) │ (AST cache) │
|
||||
└──────────────┘ │ - Lifecycle │ └──────────────────┘
|
||||
│ - Documents │
|
||||
│ - CDB │ Bincode/IPC ┌──────────────────┐
|
||||
│ - Build drain │ ◄─────────────► │ Stateless Workers│
|
||||
│ - Indexing │ (stdio) │ (one-shot tasks)│
|
||||
└────────────────┘ └──────────────────┘
|
||||
```
|
||||
|
||||
## Master Server
|
||||
|
||||
The master server (`src/server/master_server.cpp`) is the central coordinator. It runs a single-threaded async event loop and never touches Clang directly. Its responsibilities:
|
||||
|
||||
### LSP Lifecycle
|
||||
|
||||
The server progresses through these states:
|
||||
|
||||
1. **Uninitialized** — waiting for `initialize` request
|
||||
2. **Initialized** — capabilities exchanged, waiting for `initialized` notification
|
||||
3. **Ready** — workers spawned, workspace loaded, accepting requests
|
||||
4. **ShuttingDown** — `shutdown` received, draining work
|
||||
5. **Exited** — `exit` received, stopping the event loop
|
||||
|
||||
On `initialized`, the master:
|
||||
|
||||
- Loads configuration from `clice.toml` (or uses defaults)
|
||||
- Starts the worker pool (spawns stateful + stateless processes)
|
||||
- Loads `compile_commands.json` and builds an include graph
|
||||
- Starts the background indexer coroutine (if enabled)
|
||||
|
||||
### Document Management
|
||||
|
||||
Each open document is tracked in a `DocumentState` with:
|
||||
|
||||
- Current `version` and `text` (kept in sync via `didOpen`/`didChange`)
|
||||
- A `generation` counter to detect stale compile results
|
||||
- Build state flags (`build_running`, `build_requested`, `drain_scheduled`)
|
||||
|
||||
When a document is opened or changed:
|
||||
|
||||
1. The include graph is re-scanned (via dependency directives)
|
||||
2. The compile unit is registered/updated in the `CompileGraph`
|
||||
3. A debounced build is scheduled
|
||||
|
||||
### Build Drain
|
||||
|
||||
The `run_build_drain` coroutine implements debounced compilation:
|
||||
|
||||
1. Wait for the debounce timer (default 200ms) to expire
|
||||
2. Ensure PCH/PCM dependencies are ready via `CompileGraph`
|
||||
3. Send a `compile` request to the assigned stateful worker
|
||||
4. Publish diagnostics from the result (or clear them on failure)
|
||||
5. If more edits arrived during compilation (`build_requested`), loop back to step 2
|
||||
|
||||
This ensures rapid typing doesn't trigger a compile per keystroke.
|
||||
|
||||
### Request Routing
|
||||
|
||||
Feature requests are split between two worker types:
|
||||
|
||||
**Stateful workers** (affinity-routed by file path):
|
||||
|
||||
- `textDocument/hover`
|
||||
- `textDocument/semanticTokens/full`
|
||||
- `textDocument/inlayHint`
|
||||
- `textDocument/foldingRange`
|
||||
- `textDocument/documentSymbol`
|
||||
- `textDocument/documentLink`
|
||||
- `textDocument/codeAction`
|
||||
- `textDocument/definition`
|
||||
|
||||
**Stateless workers** (round-robin):
|
||||
|
||||
- `textDocument/completion`
|
||||
- `textDocument/signatureHelp`
|
||||
|
||||
All feature responses use `RawValue` passthrough — the worker serializes the LSP result to JSON, and the master forwards the raw JSON bytes to the client without deserializing. This avoids bincode↔JSON conversion overhead and serde annotation conflicts.
|
||||
|
||||
## Worker Pool
|
||||
|
||||
The worker pool (`src/server/worker_pool.cpp`) manages spawning and communicating with worker processes. Each worker is a child process of the same `clice` binary, launched with `--mode stateful-worker` or `--mode stateless-worker`.
|
||||
|
||||
### Communication
|
||||
|
||||
Workers communicate with the master via **stdio pipes** using a **bincode** serialization format (via `eventide::ipc::BincodePeer`). This is more compact and faster than JSON for internal IPC, while the master handles JSON for the external LSP protocol.
|
||||
|
||||
### Stateful Worker Routing
|
||||
|
||||
Stateful workers use **affinity routing**: each file is consistently assigned to the same worker so that the worker retains the cached AST. Assignment uses a **least-loaded** strategy for new files, with **LRU tracking** to manage ownership.
|
||||
|
||||
When a worker exceeds its document capacity (currently hardcoded at 16 documents), it evicts the least-recently-used document and notifies the master via an `evicted` notification.
|
||||
|
||||
### Stateless Worker Routing
|
||||
|
||||
Stateless workers use simple **round-robin** dispatch. Each request includes the full source text and compilation arguments, so any worker can handle it independently.
|
||||
|
||||
## Stateful Worker
|
||||
|
||||
The stateful worker (`src/server/stateful_worker.cpp`) caches compiled ASTs in memory. Key behavior:
|
||||
|
||||
- **Compile**: Parses source code into a `CompilationUnit`, caches the AST, and returns diagnostics as a `RawValue` (JSON bytes)
|
||||
- **Feature queries**: Look up the cached AST and invoke the corresponding `feature::*` function (hover, semantic tokens, etc.), serializing the result to JSON
|
||||
- **Document updates**: Received as notifications — the worker updates the stored text and marks the document as `dirty`, causing feature queries to return `null` until recompilation
|
||||
- **Eviction**: LRU-based; evicts the oldest document when capacity is exceeded, notifying the master
|
||||
- **Concurrency**: Each document has a per-document `et::mutex` (strand) to serialize compilation and feature queries. Heavy work (compilation, feature extraction) runs on a thread pool via `et::queue`.
|
||||
|
||||
## Stateless Worker
|
||||
|
||||
The stateless worker (`src/server/stateless_worker.cpp`) handles one-shot requests that don't benefit from cached ASTs:
|
||||
|
||||
- **Completion**: Creates a fresh compilation with `CompilationKind::Completion` and invokes `feature::code_complete`
|
||||
- **Signature help**: Similar to completion, using `feature::signature_help`
|
||||
- **Build PCH**: Compiles a precompiled header to a temporary file
|
||||
- **Build PCM**: Compiles a C++20 module interface to a temporary file
|
||||
- **Index**: Compiles a file for indexing (TUIndex generation — currently a stub)
|
||||
|
||||
All requests are dispatched to a thread pool via `et::queue`.
|
||||
|
||||
## Compile Graph
|
||||
|
||||
The compile graph (`src/server/compile_graph.cpp`) tracks compilation unit dependencies as a DAG. It handles:
|
||||
|
||||
- **Registration**: Each file registers its included dependencies
|
||||
- **Cascade invalidation**: When a file changes, all transitive dependents are marked dirty and their ongoing compilations are cancelled
|
||||
- **Dependency compilation**: Before compiling a file, `compile_deps` ensures all dependencies (PCH, PCMs) are built first
|
||||
- **Cancellation**: Uses `et::cancellation_source` to abort in-flight compilations when files are invalidated
|
||||
|
||||
## Configuration
|
||||
|
||||
The server reads configuration from `clice.toml` (or `.clice/config.toml`) in the workspace root. If no config file exists, sensible defaults are computed from system resources:
|
||||
|
||||
| Setting | Default | Description |
|
||||
| ------------------------ | --------------------- | ------------------------------------------- |
|
||||
| `stateful_worker_count` | CPU cores / 4 | Number of stateful worker processes |
|
||||
| `stateless_worker_count` | CPU cores / 4 | Number of stateless worker processes |
|
||||
| `worker_memory_limit` | 4 GB | Memory limit per stateful worker |
|
||||
| `compile_commands_path` | auto-detect | Path to `compile_commands.json` |
|
||||
| `cache_dir` | `<workspace>/.clice/` | Cache directory for PCH/PCM files |
|
||||
| `debounce_ms` | 200 | Debounce interval for recompilation |
|
||||
| `enable_indexing` | true | Enable background indexing |
|
||||
| `idle_timeout_ms` | 3000 | Idle time before background indexing starts |
|
||||
|
||||
String values support `${workspace}` substitution.
|
||||
|
||||
## IPC Protocol
|
||||
|
||||
The master and workers communicate using custom RPC messages defined in `src/server/protocol.h`. Each message type has a `RequestTraits` or `NotificationTraits` specialization that defines the method name and result type.
|
||||
|
||||
### Stateful Worker Messages
|
||||
|
||||
| Method | Direction | Purpose |
|
||||
| ----------------------------- | ------------ | ------------------------------------- |
|
||||
| `clice/worker/compile` | Request | Compile source and return diagnostics |
|
||||
| `clice/worker/hover` | Request | Get hover info at position |
|
||||
| `clice/worker/semanticTokens` | Request | Get semantic tokens for file |
|
||||
| `clice/worker/inlayHints` | Request | Get inlay hints for range |
|
||||
| `clice/worker/foldingRange` | Request | Get folding ranges |
|
||||
| `clice/worker/documentSymbol` | Request | Get document symbols |
|
||||
| `clice/worker/documentLink` | Request | Get document links |
|
||||
| `clice/worker/codeAction` | Request | Get code actions for range |
|
||||
| `clice/worker/goToDefinition` | Request | Go to definition at position |
|
||||
| `clice/worker/documentUpdate` | Notification | Update document text (marks dirty) |
|
||||
| `clice/worker/evict` | Notification | Master → Worker: evict a document |
|
||||
| `clice/worker/evicted` | Notification | Worker → Master: document was evicted |
|
||||
|
||||
### Stateless Worker Messages
|
||||
|
||||
| Method | Direction | Purpose |
|
||||
| ---------------------------- | --------- | ---------------------------- |
|
||||
| `clice/worker/completion` | Request | Code completion at position |
|
||||
| `clice/worker/signatureHelp` | Request | Signature help at position |
|
||||
| `clice/worker/buildPCH` | Request | Build precompiled header |
|
||||
| `clice/worker/buildPCM` | Request | Build C++20 module interface |
|
||||
| `clice/worker/index` | Request | Index a translation unit |
|
||||
@@ -1,45 +1,78 @@
|
||||
# Build from Source
|
||||
|
||||
## Supported Platforms
|
||||
clice depends on C++23 features and requires a modern C++ toolchain. We also need to link against LLVM/Clang to parse ASTs. To speed up builds, the default configuration downloads our published [clice-llvm](https://github.com/clice-io/clice-llvm) prebuilt package. This assumes your local environment matches the prebuilt environment closely (especially when enabling Address Sanitizer or LTO).
|
||||
|
||||
- Windows
|
||||
- Linux
|
||||
- MacOS
|
||||
To simplify setup and keep builds reproducible, we **strongly recommend** [pixi](https://pixi.prefix.dev/latest) to manage the development environment. Dependency versions are pinned in `pixi.toml`.
|
||||
|
||||
## Prerequisite
|
||||
If you prefer not to use pixi, see [Manual Build](#manual-build) below.
|
||||
|
||||
- cmake/xmake
|
||||
- clang, lld >= 20
|
||||
- c++23 **compatible** standard library
|
||||
- MSVC STL >= 19.44(VS 2022 17.4)
|
||||
- GCC libstdc++ >= 14
|
||||
- Clang libc++ >= 20
|
||||
## Quick Start
|
||||
|
||||
clice uses C++23 as its language standard. Please ensure you have a clang 20 (or higher) compiler and a C++23 compatible standard library available. clice depends on lld as its linker. Please ensure your clang toolchain can find it (clang distributions usually bundle lld, or you may need to install the lld-20 package separately).
|
||||
Install pixi following the [official guide](https://pixi.prefix.dev/latest/installation).
|
||||
|
||||
> clice is currently only guaranteed to compile with clang (as ensured by CI testing). We do our best to maintain compatibility with gcc and msvc, but we do not add corresponding tests in CI. Contributions are welcome if you encounter any issues.
|
||||
|
||||
## CMake
|
||||
|
||||
Use the following commands to build clice
|
||||
We ship several tasks; the commands below configure, build, and run tests:
|
||||
|
||||
```shell
|
||||
cmake -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
|
||||
cmake --build build
|
||||
# configure && build (default RelWithDebInfo)
|
||||
pixi run build
|
||||
|
||||
# unit && integration
|
||||
pixi run test
|
||||
```
|
||||
|
||||
For finer-grained tasks (first argument sets the build type):
|
||||
|
||||
```shell
|
||||
pixi run cmake-config Debug
|
||||
pixi run cmake-build Debug
|
||||
pixi run unit-test Debug
|
||||
pixi run integration-test Debug
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> If you want to develop directly with `cmake`, `ninja`, `clang++`, etc., run `pixi shell` to enter a shell with all env vars configured.
|
||||
|
||||
### XMake
|
||||
|
||||
We also support building with XMake:
|
||||
|
||||
```shell
|
||||
# config & build (default releasedbg)
|
||||
pixi run xmake
|
||||
|
||||
# unit & integration
|
||||
pixi run xmake-test
|
||||
```
|
||||
|
||||
## Manual Build
|
||||
|
||||
If you plan to build manually, first ensure your toolchain matches the versions defined in `pixi.toml`.
|
||||
|
||||
> Compatibility: In theory clice does not rely on compiler-specific extensions, so mainstream compilers (GCC/Clang/MSVC) should work. However, CI only guarantees specific versions of Clang. Other compilers or versions are supported on a **best-effort** basis. Please open an issue or PR if you hit problems.
|
||||
|
||||
### CMake
|
||||
|
||||
```shell
|
||||
cmake -B build -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_TOOLCHAIN_FILE=cmake/toolchain.cmake \
|
||||
-DCLICE_ENABLE_TEST=ON
|
||||
```
|
||||
|
||||
> Note: `CMAKE_TOOLCHAIN_FILE` is optional. If your toolchain exactly matches ours, you can use the predefined `cmake/toolchain.cmake`; otherwise remove that flag.
|
||||
|
||||
Optional build options:
|
||||
|
||||
| Option | Default | Description |
|
||||
| :------------------- | :------ | :----------------------------------------------------------------------------------------------------------------------------- |
|
||||
| LLVM_INSTALL_PATH | "" | Build clice using llvm libs from a custom path |
|
||||
| CLICE_ENABLE_TEST | OFF | Whether to build clice's unit tests |
|
||||
| CLICE_USE_LIBCXX | OFF | Whether to build clice with libc++ (adds `-std=libc++`). If enabled, ensure that the llvm libs were also compiled with libc++. |
|
||||
| CLICE_CI_ENVIRONMENT | OFF | Whether to enable the `CLICE_CI_ENVIRONMENT` macro. Some tests only run in a CI environment. |
|
||||
| Option | Default | Effect |
|
||||
| -------------------- | ------- | --------------------------------------------------------------------------------------------------------- |
|
||||
| LLVM_INSTALL_PATH | "" | Build clice with LLVM from a custom path |
|
||||
| CLICE_ENABLE_TEST | OFF | Build clice unit tests |
|
||||
| CLICE_USE_LIBCXX | OFF | Build clice with libc++ (adds `-std=libc++`); if enabled, ensure the LLVM libs are also built with libc++ |
|
||||
| CLICE_CI_ENVIRONMENT | OFF | Enable the `CLICE_CI_ENVIRONMENT` macro; some tests only run in CI |
|
||||
|
||||
## XMake
|
||||
### XMake
|
||||
|
||||
Use the following commands to build clice
|
||||
Build clice with:
|
||||
|
||||
```bash
|
||||
xmake f -c --mode=releasedbg --toolchain=clang
|
||||
@@ -48,35 +81,22 @@ xmake build --all
|
||||
|
||||
Optional build options:
|
||||
|
||||
| Option | Default | Description |
|
||||
| :------------ | :------ | :--------------------------------------------- |
|
||||
| --llvm | "" | Build clice using llvm libs from a custom path |
|
||||
| --enable_test | false | Whether to build clice's unit tests |
|
||||
| --ci | false | Whether to enable `CLICE_CI_ENVIRONMENT` |
|
||||
| Option | Default | Effect |
|
||||
| ------------- | ------- | ---------------------------------------- |
|
||||
| --llvm | "" | Build clice with LLVM from a custom path |
|
||||
| --enable_test | false | Build clice unit tests |
|
||||
| --ci | false | Enable `CLICE_CI_ENVIRONMENT` |
|
||||
|
||||
## A Note on LLVM Libs
|
||||
## About LLVM
|
||||
|
||||
Due to the complexity of C++ syntax, writing a new parser from scratch is unrealistic. clice calls clang's APIs to parse C++ source files and obtain the AST, which means it needs to link against llvm/clang libs. Because clice uses clang's private headers, which are not included in the binary releases published by LLVM, you cannot use the system's llvm package directly.
|
||||
clice calls Clang APIs to parse C++ code, so it must link against LLVM/Clang. Because clice uses Clang's private headers (usually absent from distro packages), the system LLVM package cannot be used directly.
|
||||
|
||||
1. We publish pre-compiled binaries for the LLVM version we use on [clice-llvm](https://github.com/clice-io/clice-llvm/releases), which are used for CI or release builds. By default, cmake and xmake will download and use the llvm libs from here during the build.
|
||||
Two ways to satisfy this dependency:
|
||||
|
||||
1. We publish prebuilt binaries of the LLVM version we use at [clice-llvm](https://github.com/clice-io/clice-llvm/releases) for CI and release builds. During builds, cmake and xmake download these LLVM libs by default.
|
||||
|
||||
> [!IMPORTANT]
|
||||
>
|
||||
> For debug builds of llvm libs, we enable address sanitizer. Address sanitizer depends on compiler-rt, which is highly sensitive to the compiler version.
|
||||
>
|
||||
> Therefore, if you use a debug build, please ensure your clang's compiler-rt version is **strictly identical** to the one used in our build.
|
||||
>
|
||||
> - Windows does not currently have debug builds for llvm libs, as it does not support building clang as a dynamic library. Related progress is tracked [here](https://github.com/clice-io/clice/issues/42).
|
||||
> - Linux uses clang20
|
||||
> - MacOS uses homebrew llvm@20. **Do not use apple clang**.
|
||||
>
|
||||
> You can refer to the [cmake](https://github.com/clice-io/clice/blob/main/.github/workflows/cmake.yml) and [xmake](https://github.com/clice-io/clice/blob/main/.github/workflows/xmake.yml) files in our CI as a reference, as they maintain an environment strictly consistent with the pre-compiled llvm libs.
|
||||
> For debug LLVM builds, we enable address sanitizer, which depends on compiler-rt and is very sensitive to compiler version. If you use a debug build, ensure your clang compiler-rt version matches the one defined in `pixi.toml`.
|
||||
|
||||
2. Build llvm/clang yourself to match your current environment. If the default pre-compiled binaries (Method 1) fail to run on your system due to ABI or library version (e.g., glibc) incompatibility, or if you need a custom Debug build, we recommend you use this method to compile llvm libs from scratch. We provide a script to build the llvm libs required by clice: [build-llvm-libs.py](https://github.com/clice-io/clice/blob/main/scripts/build-llvm-libs.py).
|
||||
|
||||
```bash
|
||||
cd llvm-project
|
||||
python3 <clice>/scripts/build-llvm-libs.py debug
|
||||
```
|
||||
|
||||
You can also refer to LLVM's official build tutorial: [Building LLVM with CMake](https://llvm.org/docs/CMake.html).
|
||||
2. Build LLVM/Clang yourself to match your environment. If the default prebuilt binaries fail due to ABI or library version mismatches, or you need a custom debug build, use this approach. We provide `scripts/build-llvm.py` to build the required LLVM libs, or refer to LLVM's official guide [Building LLVM with CMake](https://llvm.org/docs/CMake.html).
|
||||
|
||||
@@ -7,5 +7,6 @@ Please refer to [build](./build.md) to build clice, refer to [test and debug](./
|
||||
## Code Style
|
||||
|
||||
Naming:
|
||||
|
||||
- Variable names: lowercase with underscores
|
||||
- Class names, enum names: PascalCase
|
||||
|
||||
70
docs/en/dev/extension.md
Normal file
70
docs/en/dev/extension.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# Extension
|
||||
|
||||
This section covers development and release workflows for the editor extensions (VSCode / Neovim / Zed).
|
||||
|
||||
## VSCode
|
||||
|
||||
The VSCode extension uses the Node/PNPM/VSCE toolchain. Work inside the pixi `node` environment for consistent versions.
|
||||
|
||||
```shell
|
||||
# prepare environment (install pixi first)
|
||||
pixi shell -e node
|
||||
|
||||
# install deps (uses pnpm-lock)
|
||||
pixi run install-vscode
|
||||
|
||||
# package the extension; outputs editors/vscode/*.vsix
|
||||
pixi run build-vscode
|
||||
```
|
||||
|
||||
Publish to the VSCode Marketplace (`VSCE_PAT` env var required):
|
||||
|
||||
```shell
|
||||
pixi run publish-vscode
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> If clice is already built locally, set `clice.executable` in VSCode settings to point the extension to your custom binary.
|
||||
|
||||
Develop and debug:
|
||||
|
||||
1. `pixi shell -e node`
|
||||
2. In `editors/vscode`, run `pnpm run watch` for incremental builds
|
||||
3. In VSCode, use the “Run Extension/Launch Extension” configs, or run `code --extensionDevelopmentPath=$(pwd)/editors/vscode`
|
||||
|
||||
Common scripts (inside `pixi shell -e node`):
|
||||
|
||||
```bash
|
||||
pnpm run package # same as pixi run build-vscode
|
||||
pnpm run publish # same as pixi run publish-vscode
|
||||
```
|
||||
|
||||
If you skip pixi, install node.js >= 20 and pnpm yourself, then in `editors/vscode` run:
|
||||
|
||||
```bash
|
||||
pnpm install
|
||||
pnpm run package
|
||||
```
|
||||
|
||||
## Neovim
|
||||
|
||||
The Neovim extension lives in `editors/nvim` and is written in Lua. It is still evolving.
|
||||
|
||||
- Add the repo path to `runtimepath`, e.g. `set rtp+=/path/to/clice/editors/nvim`
|
||||
- Or create a local symlink: `~/.config/nvim/pack/clice/start/clice` -> `<repo>/editors/nvim`
|
||||
- Ensure the `clice` executable is discoverable in `$PATH`
|
||||
|
||||
Dev tips: the codebase is small—load it directly in Neovim and watch `:messages`/LSP logs; format with `stylua` (config included).
|
||||
|
||||
## Zed
|
||||
|
||||
The Zed extension lives in `editors/zed` and uses Rust plus `zed_extension_api`.
|
||||
|
||||
Suggested local verification:
|
||||
|
||||
```bash
|
||||
cd editors/zed
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
Then load the local extension per Zed's official guide (Zed CLI required). Make sure `clice` is on `PATH` before launching. Follow the Zed extension publishing flow when releasing.
|
||||
@@ -53,24 +53,24 @@ You can also connect to a running clice service by configuring the clice-vscode
|
||||
|
||||
```jsonc
|
||||
{
|
||||
// Point this to the clice binary you downloaded.
|
||||
"clice.executable": "/path/to/your/clice/executable",
|
||||
// Point this to the clice binary you downloaded.
|
||||
"clice.executable": "/path/to/your/clice/executable",
|
||||
|
||||
// Enable socket mode.
|
||||
"clice.mode": "socket",
|
||||
"clice.port": 50051,
|
||||
// Enable socket mode.
|
||||
"clice.mode": "socket",
|
||||
"clice.port": 50051,
|
||||
|
||||
// Optional: Set this to an empty string to turn off the clangd.
|
||||
"clangd.path": "",
|
||||
// Optional: Set this to an empty string to turn off the clangd.
|
||||
"clangd.path": "",
|
||||
}
|
||||
```
|
||||
|
||||
3. Reload Window: After modifying the configuration, execute the `Developer: Reload Window` command in VS Code for the settings to take effect. The extension will automatically connect to the clice instance listening on port 50051.
|
||||
|
||||
|
||||
If you need to modify or debug the clice-vscode extension itself, follow these steps:
|
||||
|
||||
1. Clone and install dependencies:
|
||||
|
||||
```shell
|
||||
$ git clone https://github.com/clice-io/clice-vscode
|
||||
$ cd clice-vscode
|
||||
|
||||
@@ -35,7 +35,7 @@ Glob patterns for matching file paths, following LSP's [standard](https://micros
|
||||
- `{}`: Used for grouping conditions (e.g., `**/*.{ts,js}` matches all TypeScript and JavaScript files).
|
||||
- `[]`: Declares a character range to match in a path segment (e.g., `example.[0-9]` matches `example.0`, `example.1`, etc.).
|
||||
- `[!...]`: Excludes a character range to match in a path segment (e.g., `example.[!0-9]` matches `example.a`, `example.b`, but not `example.0`).
|
||||
<br>
|
||||
<br>
|
||||
|
||||
| Name | Type | Default |
|
||||
| ---------------- | ------------------- | ------- |
|
||||
|
||||
@@ -16,8 +16,8 @@ hero:
|
||||
text: Contribution
|
||||
link: /dev/contribution
|
||||
image:
|
||||
src: /image.png
|
||||
alt: clice
|
||||
src: /image.png
|
||||
alt: clice
|
||||
|
||||
features:
|
||||
- icon: T
|
||||
|
||||
2239
docs/package-lock.json
generated
2239
docs/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.1.0",
|
||||
"@types/node": "^24.10.4",
|
||||
"vitepress": "^1.6.4"
|
||||
},
|
||||
"scripts": {
|
||||
@@ -11,9 +11,12 @@
|
||||
"docs:build": "vitepress build",
|
||||
"docs:preview": "vitepress preview"
|
||||
},
|
||||
"overrides": {
|
||||
"vite": {
|
||||
"esbuild": "0.25.9"
|
||||
}
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"esbuild": "~0.25.0"
|
||||
},
|
||||
"ignoredBuiltDependencies": [
|
||||
"esbuild"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
1629
docs/pnpm-lock.yaml
generated
Normal file
1629
docs/pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -58,4 +58,5 @@ struct Widget {
|
||||
|
||||
template <typename T>
|
||||
Widget(T) -> Widget<typename T::value_type>;
|
||||
|
||||
} // namespace foo
|
||||
|
||||
@@ -16,5 +16,4 @@ int main () {
|
||||
|
||||
`iostream` 这个头文件大概有 2w 行代码,clice 会先把 `#include <iostream>` 这一行代码构建成 PCH 文件,在完成之后在使用这个 PCH 文件来解析后面的代码。这样的话后续重新解析的代码量就只剩 5 行了,而不是原本的 2w 行,速度会变得非常快。除非你修改了 preamble 部分的代码,导致需要构建新的 preamble。
|
||||
|
||||
|
||||
## Cancel Compilation
|
||||
|
||||
@@ -28,5 +28,4 @@ void foo(std::vector<std::vector<T>> vec2) {
|
||||
2. 它只进行名称查找而不进行模板实例化,就算找到了最后的结果,也没法把它和最初的模板参数映射起来
|
||||
3. 不考虑默认模板参数,无法处理由默认模板参数导致的依赖名
|
||||
|
||||
|
||||
尽管我们可以对标准库的类型开洞来提供相关的支持,但是我希望用户的代码能和标准库的代码有相同的地位,那么我们就需要一种通用的算法来处理依赖类型。为了解决这个问题,我编写了一个伪实例化器(pseudo instantiator)。它能在没有具体类型的前提下对依赖类型进行实例化,从而达到化简的目的。比如上面这个例子里面的`std::vector<std::vector<T>>::reference`就能被化简为`std::vector<T>&`,进一步就能为用户提供代码补全选项。
|
||||
|
||||
@@ -1,45 +1,78 @@
|
||||
# Build from Source
|
||||
|
||||
## Supported Platforms
|
||||
clice 依赖 C++23 特性,需要使用高版本的 C++ 编译器。同时,我们需要链接 LLVM/Clang 库来解析 AST。为了加快构建速度,默认配置会下载我们发布的 [clice-llvm](https://github.com/clice-io/clice-llvm) 预编译包。这要求你的本地环境与预编译环境保持较高的一致性(尤其是开启 Address Sanitizer 或 LTO 时)。
|
||||
|
||||
- Windows
|
||||
- Linux
|
||||
- MacOS
|
||||
为了简化环境设置并保证可复现性,我们**强烈推荐**使用 [pixi](https://pixi.prefix.dev/latest) 来管理开发环境。所有的依赖版本均严格定义在 `pixi.toml` 中。
|
||||
|
||||
## Prerequisite
|
||||
如果你不想使用 pixi,请参考下方的 [Manual Build](#manual-build) 章节。
|
||||
|
||||
- cmake/xmake
|
||||
- clang, lld >= 20
|
||||
- c++23 **compatible** standard library
|
||||
- MSVC STL >= 19.44(VS 2022 17.4)
|
||||
- GCC libstdc++ >= 14
|
||||
- Clang libc++ >= 20
|
||||
## Quick Start
|
||||
|
||||
clice 使用 C++23 作为语言标准,请确保有可用的 clang 20 以及以上的编译器,以及兼容 C++23 的标准库。clice 依赖 lld 作为链接器。请确保你的 clang 工具链可以找到它(通常 clang 发行版会自带 lld,或者你需要单独安装 lld-20 包)。
|
||||
请参考 [pixi](https://pixi.prefix.dev/latest/installation) 官方指南安装 pixi。
|
||||
|
||||
> clice 目前只保证能使用 clang 编译(CI 测试保证)。对于 gcc 和 msvc 的兼容,我们尽力而为,但不会在 CI 中添加对应的测试。如果遇到任何问题,欢迎贡献。
|
||||
|
||||
## CMake
|
||||
|
||||
使用如下的命令构建 clice
|
||||
我们内置了一系列任务,以下命令可直接完成编译并运行测试:
|
||||
|
||||
```shell
|
||||
cmake -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
|
||||
cmake --build build
|
||||
# configure && build (default RelWithDebInfo)
|
||||
pixi run build
|
||||
|
||||
# unit && integration
|
||||
pixi run test
|
||||
```
|
||||
|
||||
细粒度任务:上述命令由多个子任务组成,你也可以单独运行它们,并支持通过第一个参数指定构建类型:
|
||||
|
||||
```shell
|
||||
pixi run cmake-config Debug
|
||||
pixi run cmake-build Debug
|
||||
pixi run unit-test Debug
|
||||
pixi run integration-test Debug
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> 如果你想直接使用 `cmake`, `ninja`, `clang++` 等命令进行开发,请运行 `pixi shell` 进入已配置好环境变量的终端
|
||||
|
||||
### XMake
|
||||
|
||||
我们同样支持使用 XMake 构建:
|
||||
|
||||
```shell
|
||||
# config & build (default releasedbg)
|
||||
pixi run xmake
|
||||
|
||||
# unit & integration
|
||||
pixi run xmake-test
|
||||
```
|
||||
|
||||
## Manual Build
|
||||
|
||||
如果你打算手动构建,请务必先确认你的工具链满足 pixi.toml 中定义的版本要求。
|
||||
|
||||
> 兼容性说明:理论上 clice 不依赖特定编译器的扩展,可以使用主流编译器(GCC/Clang/MSVC)编译。但我们仅在 CI 中保证特定版本的 Clang 能通过测试。对于其他编译器或版本,我们提供**尽力而为 (Best Effort)** 的支持。如果遇到问题,欢迎提交 Issue 或 PR
|
||||
|
||||
### CMake
|
||||
|
||||
```shell
|
||||
cmake -B build -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_TOOLCHAIN_FILE=cmake/toolchain.cmake \
|
||||
-DCLICE_ENABLE_TEST=ON
|
||||
```
|
||||
|
||||
> 注意:`CMAKE_TOOLCHAIN_FILE` 是可选的。如果你使用的工具链与我们完全一致,可以使用预定义的 `cmake/toolchain.cmake`,否则请移除该选项
|
||||
|
||||
可选的构建选项:
|
||||
|
||||
| 选项 | 默认值 | 效果 |
|
||||
| -------------------- | ------ | ----------------------------------------------------------------------------------------------------- |
|
||||
| LLVM_INSTALL_PATH | "" | 使用自定义路径的 llvm libs 来构建 clice |
|
||||
| CLICE_ENABLE_TEST | OFF | 是否构建 clice 的单元测试 |
|
||||
| CLICE_USE_LIBCXX | OFF | 是否使用 libc++ 来构建 clice(添加 `-std=libc++`),如果开启,请确保 llvm libs 也是使用 libc++ 编译的 |
|
||||
| CLICE_CI_ENVIRONMENT | OFF | 是否打开 `CLICE_CI_ENVIRONMENT` 这个宏,有些测试在 CI 环境才会执行 |
|
||||
| 选项 | 默认值 | 效果 |
|
||||
| -------------------- | ------ | -------------------------------------------------------------------------------------------------- |
|
||||
| LLVM_INSTALL_PATH | "" | 使用自定义路径的 LLVM 库来构建 clice |
|
||||
| CLICE_ENABLE_TEST | OFF | 是否构建 clice 的单元测试 |
|
||||
| CLICE_USE_LIBCXX | OFF | 是否使用 libc++ 来构建 clice(添加 `-std=libc++`),如果开启,请确保 LLVM 库也是使用 libc++ 编译的 |
|
||||
| CLICE_CI_ENVIRONMENT | OFF | 是否打开 `CLICE_CI_ENVIRONMENT` 这个宏,有些测试在 CI 环境才会执行 |
|
||||
|
||||
## XMake
|
||||
### XMake
|
||||
|
||||
使用如下的命令即可构建 clice
|
||||
使用如下命令即可构建 clice:
|
||||
|
||||
```bash
|
||||
xmake f -c --mode=releasedbg --toolchain=clang
|
||||
@@ -48,34 +81,22 @@ xmake build --all
|
||||
|
||||
可选的构建选项:
|
||||
|
||||
| 选项 | 默认值 | 效果 |
|
||||
| ------------- | ------ | --------------------------------------- |
|
||||
| --llvm | "" | 使用自定义路径的 llvm libs 来构建 clice |
|
||||
| --enable_test | false | 是否构建 clice 的单元测试 |
|
||||
| --ci | false | 是否打开 `CLICE_CI_ENVIRONMENT` |
|
||||
| 选项 | 默认值 | 效果 |
|
||||
| ------------- | ------ | ------------------------------------ |
|
||||
| --llvm | "" | 使用自定义路径的 LLVM 库来构建 clice |
|
||||
| --enable_test | false | 是否构建 clice 的单元测试 |
|
||||
| --ci | false | 是否打开 `CLICE_CI_ENVIRONMENT` |
|
||||
|
||||
## About LLVM
|
||||
|
||||
## A Note on LLVM Libs
|
||||
clice 调用 Clang API 来解析 C++ 代码,因此必须链接 LLVM/Clang 库。由于 clice 使用了 Clang 的私有头文件(这些文件通常不包含在发行版中),不能直接使用系统安装的 LLVM 包。
|
||||
|
||||
由于 C++ 的语法太过复杂,自己编写一个新的 parser 是不现实的。clice 调用 clang 的 API 来 parse C++ 源文件获取 AST,这意味它需要链接 llvm/clang libs。由于 clice 使用了 clang 的私有头文件,这些私有头文件在 llvm 发布的 binary release 中是没有的,所以不能直接使用系统的 llvm package。
|
||||
主要有两种方式解决这个依赖问题:
|
||||
|
||||
1. 我们在 [clice-llvm](https://github.com/clice-io/clice-llvm/releases) 上会发布使用的 llvm 版本的预编译二进制,用于 CI 或者 release 构建。在构建时 cmake 和 xmake 默认会从此处下载 llvm libs 然后使用,
|
||||
1. 我们在 [clice-llvm](https://github.com/clice-io/clice-llvm/releases) 上会发布使用的 LLVM 版本的预编译二进制,用于 CI 或者 release 构建。在构建时 cmake 和 xmake 默认会从此处下载 LLVM 库然后使用。
|
||||
|
||||
> [!IMPORTANT]
|
||||
>
|
||||
> 对于 debug 版本的 llvm libs,构建的时候我们开启了 address sanitizer,而 address sanitizer 依赖于 compiler rt,它对编译器版本十分敏感。所以如果使用 debug 版本,请确保你的 clang 的 compiler rt 版本和我们构建的时候**严格一致**。
|
||||
>
|
||||
> - Windows 暂时没有 debug 构建的 llvm libs,因为它不支持将 clang 构建为动态库,相关的进展在 [这里](https://github.com/clice-io/clice/issues/42) 跟踪
|
||||
> - Linux 使用 clang20
|
||||
> - MacOS 使用 homebrew llvm@20,**不要使用 apple clang**
|
||||
>
|
||||
> 可以参考 CI 中的 [cmake](https://github.com/clice-io/clice/blob/main/.github/workflows/cmake.yml) 和 [xmake](https://github.com/clice-io/clice/blob/main/.github/workflows/xmake.yml) 文件作为参考,它们与预编译 llvm libs 的环境保持严格一致。
|
||||
> 对于 debug 版本的 LLVM 库,构建的时候我们开启了 address sanitizer,而 address sanitizer 依赖于 compiler rt,它对编译器版本十分敏感。所以如果使用 debug 版本,请确保你的 clang 的 compiler rt 版本与 `pixi.toml` 中的定义严格一致。
|
||||
|
||||
2. 自己重新一个与当前环境一致的 llvm/clang。如果默认的预编译二进制文件(方法 1)在你的系统上因 ABI 或库版本(如 glibc)不兼容而运行失败,或者你需要一个自定义的 Debug 版本,那么我们推荐你使用此方法从头编译 llvm libs。我们提供了一个脚本,用于构建 clice 所需要的 llvm libs:[build-llvm-libs.py](https://github.com/clice-io/clice/blob/main/scripts/build-llvm-libs.py)。
|
||||
|
||||
```bash
|
||||
cd llvm-project
|
||||
python3 <clice>/scripts/build-llvm-libs.py debug
|
||||
```
|
||||
|
||||
也可以参考 llvm 的官方构建教程 [Building LLVM with CMake](https://llvm.org/docs/CMake.html)。
|
||||
2. 自行构建一套与当前环境一致的 LLVM/Clang。如果默认的预编译二进制文件在你的系统上因 ABI 或库版本不兼容而运行失败,或者你需要一个自定义的 Debug 版本,那么我们推荐你使用此方法从头编译 LLVM 库。我们提供了一个脚本 `scripts/build-llvm.py` 用于构建所需要的 LLVM 库,也可以参考 LLVM 的官方构建教程 [Building LLVM with CMake](https://llvm.org/docs/CMake.html)。
|
||||
|
||||
@@ -7,5 +7,6 @@
|
||||
## Code Style
|
||||
|
||||
命名
|
||||
|
||||
- 变量名:小写下换线
|
||||
- 类名,枚举名:大驼峰
|
||||
|
||||
70
docs/zh/dev/extension.md
Normal file
70
docs/zh/dev/extension.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# Extension
|
||||
|
||||
本节汇总各编辑器插件的开发与发布流程。目前包含 VSCode / Neovim / Zed。
|
||||
|
||||
## VSCode
|
||||
|
||||
VSCode 插件使用 Node/PNPM/VSCE 链路。推荐在 pixi 的 `node` 环境下操作以获得一致的工具链版本。
|
||||
|
||||
```shell
|
||||
# 准备环境(先安装 pixi)
|
||||
pixi shell -e node
|
||||
|
||||
# 安装依赖(基于 pnpm-lock)
|
||||
pixi run install-vscode
|
||||
|
||||
# 打包扩展,产物位于 editors/vscode/*.vsix
|
||||
pixi run build-vscode
|
||||
```
|
||||
|
||||
发布到 VSCode Marketplace(需要 `VSCE_PAT` 环境变量):
|
||||
|
||||
```shell
|
||||
pixi run publish-vscode
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> 若已编译 clice,本地调试时可在 VSCode 设置中填写 `clice.executable`,使扩展指向你的自定义构建。
|
||||
|
||||
开发与调试:
|
||||
|
||||
1. `pixi shell -e node`
|
||||
2. 在 `editors/vscode` 下运行 `pnpm run watch`(增量构建)
|
||||
3. VSCode 中使用 “Run Extension/Launch Extension” 调试配置,或执行 `code --extensionDevelopmentPath=$(pwd)/editors/vscode`
|
||||
|
||||
常用脚本(在 `pixi shell -e node` 下):
|
||||
|
||||
```bash
|
||||
pnpm run package # 等价于 pixi run build-vscode
|
||||
pnpm run publish # 等价于 pixi run publish-vscode
|
||||
```
|
||||
|
||||
如果不使用 pixi,请自行准备 node.js >= 20、pnpm,然后在 `editors/vscode` 目录执行:
|
||||
|
||||
```bash
|
||||
pnpm install
|
||||
pnpm run package
|
||||
```
|
||||
|
||||
## Neovim
|
||||
|
||||
Neovim 插件位于 `editors/nvim`,使用 Lua 编写。目前功能仍在演进中。
|
||||
|
||||
- 将仓库路径加入 `runtimepath`,例如:`set rtp+=/path/to/clice/editors/nvim`
|
||||
- 或在本地创建软链接:`~/.config/nvim/pack/clice/start/clice` -> `<repo>/editors/nvim`
|
||||
- 需要 `clice` 可执行文件可在 `$PATH` 中被找到
|
||||
|
||||
开发提示:代码量较小,可直接在 Neovim 中加载并通过 `:messages`/LSP 日志观察效果;格式化可使用 `stylua`(仓库中已提供 `stylua.toml`)。
|
||||
|
||||
## Zed
|
||||
|
||||
Zed 插件位于 `editors/zed`,使用 Rust 和 `zed_extension_api`。
|
||||
|
||||
建议的本地验证流程:
|
||||
|
||||
```bash
|
||||
cd editors/zed
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
随后按 Zed 官方指南加载本地扩展(需安装 Zed CLI),在启动前确保 `clice` 已在 PATH 中。发布时同样遵循 Zed 扩展发布流程。
|
||||
@@ -51,31 +51,31 @@ $ pytest -s --log-cli-level=INFO tests/integration/test_file_operation.py::test_
|
||||
|
||||
2. 配置 `settings.json`: 在你的项目根目录下创建 `.vscode/settings.json` 文件,并填入以下内容:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
// Point this to the clice binary you downloaded.
|
||||
"clice.executable": "/path/to/your/clice/executable",
|
||||
```jsonc
|
||||
{
|
||||
// Point this to the clice binary you downloaded.
|
||||
"clice.executable": "/path/to/your/clice/executable",
|
||||
|
||||
// Enable socket mode.
|
||||
"clice.mode": "socket",
|
||||
"clice.port": 50051,
|
||||
// Enable socket mode.
|
||||
"clice.mode": "socket",
|
||||
"clice.port": 50051,
|
||||
|
||||
// Optional: Set this to an empty string to turn off the clangd.
|
||||
"clangd.path": "",
|
||||
}
|
||||
```
|
||||
// Optional: Set this to an empty string to turn off the clangd.
|
||||
"clangd.path": "",
|
||||
}
|
||||
```
|
||||
|
||||
3. 重新加载窗口:修改配置后,在 vscode 中执行 Developer: Reload Window 命令使配置生效。插件会自动连接到正在 50051 端口监听的 clice。
|
||||
|
||||
|
||||
如果你需要修改或调试 clice-vscode 插件本身,可以按以下步骤操作:
|
||||
|
||||
1. 克隆并安装依赖:
|
||||
```shell
|
||||
$ git clone https://github.com/clice-io/clice-vscode
|
||||
$ cd clice-vscode
|
||||
$ npm install
|
||||
```
|
||||
|
||||
```shell
|
||||
$ git clone https://github.com/clice-io/clice-vscode
|
||||
$ cd clice-vscode
|
||||
$ npm install
|
||||
```
|
||||
|
||||
2. 使用 vscode 打开插件项目:用一个新的 vscode 窗口打开 clice-vscode 文件夹
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
- `{}`: 用于分组条件 (例如,`**/*.{ts,js}` 匹配所有 TypeScript 和 JavaScript 文件)。
|
||||
- `[]`: 声明要匹配的路径段中的字符范围 (例如,`example.[0-9]` 匹配 `example.0`, `example.1` 等)。
|
||||
- `[!...]`: 排除要匹配的路径段中的字符范围 (例如,`example.[!0-9]` 匹配 `example.a`, `example.b`,但不匹配 `example.0`)。
|
||||
<br>
|
||||
<br>
|
||||
|
||||
| 名称 | 类型 | 默认值 |
|
||||
| ---------------- | ------------------- | ------ |
|
||||
|
||||
@@ -26,7 +26,6 @@ clice 实现了 [Language Server Protocol](https://microsoft.github.io/language-
|
||||
|
||||
自己从源码编译 clice,具体的步骤参考 [build](../dev/build.md)。
|
||||
|
||||
|
||||
## Project Setup
|
||||
|
||||
为了让 clice 能正确理解你的代码(例如找到头文件的位置),需要为 clice 提供一份 `compile_commands.json` 文件,也就说所谓的 [编译数据库](https://clang.llvm.org/docs/JSONCompilationDatabase.html)。编译数据库中提供了每个源文件的编译选项。
|
||||
|
||||
@@ -16,8 +16,8 @@ hero:
|
||||
text: 参与贡献
|
||||
link: /zh/dev/contribution
|
||||
image:
|
||||
src: /image.png
|
||||
alt: clice
|
||||
src: /image.png
|
||||
alt: clice
|
||||
|
||||
features:
|
||||
- icon: T
|
||||
|
||||
3
editors/nvim/README.md
Normal file
3
editors/nvim/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# clice-nvim
|
||||
|
||||
Provide extended functionality for [clice](https://github.com/clice-io/clice)
|
||||
32
editors/nvim/doc/clice.lua
Normal file
32
editors/nvim/doc/clice.lua
Normal file
@@ -0,0 +1,32 @@
|
||||
-- Should be placed under config_dir/lsp/
|
||||
-- Lsp configuration for nvim >= 0.11
|
||||
|
||||
local clice = {
|
||||
filetypes = { 'c', 'cpp' },
|
||||
|
||||
root_markers = {
|
||||
'.git/',
|
||||
'clice.toml',
|
||||
'.clang-tidy',
|
||||
'.clang-format',
|
||||
'compile_commands.json',
|
||||
'compile_flags.txt',
|
||||
'configure.ac', -- AutoTools
|
||||
},
|
||||
|
||||
capabilities = {
|
||||
textDocument = {
|
||||
completion = {
|
||||
editsNearCursor = true,
|
||||
},
|
||||
},
|
||||
offsetEncoding = { 'utf-8' },
|
||||
},
|
||||
|
||||
cmd = {
|
||||
'clice',
|
||||
'--mode=pipe',
|
||||
},
|
||||
}
|
||||
|
||||
return clice
|
||||
3
editors/nvim/plugin/clice-nvim.lua
Normal file
3
editors/nvim/plugin/clice-nvim.lua
Normal file
@@ -0,0 +1,3 @@
|
||||
local augroup = vim.api.nvim_create_augroup('LspClice', { clear = true })
|
||||
|
||||
-- TODO: CallHierarchy, TypeHierarchy, LspDump?, ShowHeaderContext?
|
||||
6
editors/nvim/stylua.toml
Normal file
6
editors/nvim/stylua.toml
Normal file
@@ -0,0 +1,6 @@
|
||||
column_width = 160
|
||||
line_endings = "Unix"
|
||||
indent_type = "Spaces"
|
||||
indent_width = 4
|
||||
quote_style = "AutoPreferSingle"
|
||||
call_parentheses = "None"
|
||||
5
editors/vscode/.vscode-test.mjs
Normal file
5
editors/vscode/.vscode-test.mjs
Normal file
@@ -0,0 +1,5 @@
|
||||
import { defineConfig } from '@vscode/test-cli';
|
||||
|
||||
export default defineConfig({
|
||||
files: 'out/test/**/*.test.js',
|
||||
});
|
||||
9
editors/vscode/.vscode/extensions.json
vendored
Normal file
9
editors/vscode/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"amodio.tsl-problem-matcher",
|
||||
"ms-vscode.extension-test-runner"
|
||||
]
|
||||
}
|
||||
23
editors/vscode/.vscode/launch.json
vendored
Normal file
23
editors/vscode/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Run Extension (socket)",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"args": ["--disable-extensions", "--extensionDevelopmentPath=${workspaceFolder}"],
|
||||
"env": { "CLICE_MODE": "socket" },
|
||||
"outFiles": ["${workspaceFolder}/dist/**/*.js"],
|
||||
"preLaunchTask": "${defaultBuildTask}"
|
||||
},
|
||||
{
|
||||
"name": "Run Extension (pipe)",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"args": ["--disable-extensions", "--extensionDevelopmentPath=${workspaceFolder}"],
|
||||
"env": { "CLICE_MODE": "pipe" },
|
||||
"outFiles": ["${workspaceFolder}/dist/**/*.js"],
|
||||
"preLaunchTask": "${defaultBuildTask}"
|
||||
}
|
||||
]
|
||||
}
|
||||
13
editors/vscode/.vscode/settings.json
vendored
Normal file
13
editors/vscode/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
// Place your settings in this file to overwrite default and user settings.
|
||||
{
|
||||
"files.exclude": {
|
||||
"out": false, // set this to true to hide the "out" folder with the compiled JS files
|
||||
"dist": false // set this to true to hide the "dist" folder with the compiled JS files
|
||||
},
|
||||
"search.exclude": {
|
||||
"out": true, // set this to false to include "out" folder in search results
|
||||
"dist": true // set this to false to include "dist" folder in search results
|
||||
},
|
||||
// Turn off tsc task auto detection since we have the necessary tasks as npm scripts
|
||||
"typescript.tsc.autoDetect": "off"
|
||||
}
|
||||
37
editors/vscode/.vscode/tasks.json
vendored
Normal file
37
editors/vscode/.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "watch",
|
||||
"problemMatcher": "$ts-webpack-watch",
|
||||
"isBackground": true,
|
||||
"presentation": {
|
||||
"reveal": "never",
|
||||
"group": "watchers"
|
||||
},
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "watch-tests",
|
||||
"problemMatcher": "$tsc-watch",
|
||||
"isBackground": true,
|
||||
"presentation": {
|
||||
"reveal": "never",
|
||||
"group": "watchers"
|
||||
},
|
||||
"group": "build"
|
||||
},
|
||||
{
|
||||
"label": "tasks: watch-tests",
|
||||
"dependsOn": ["npm: watch", "npm: watch-tests"],
|
||||
"problemMatcher": []
|
||||
}
|
||||
]
|
||||
}
|
||||
15
editors/vscode/.vscodeignore
Normal file
15
editors/vscode/.vscodeignore
Normal file
@@ -0,0 +1,15 @@
|
||||
.github/
|
||||
.vscode/**
|
||||
.vscode-test/**
|
||||
out/**
|
||||
node_modules/**
|
||||
src/**
|
||||
.gitignore
|
||||
.yarnrc
|
||||
webpack.config.js
|
||||
vsc-extension-quickstart.md
|
||||
**/tsconfig.json
|
||||
**/.eslintrc.json
|
||||
**/*.map
|
||||
**/*.ts
|
||||
**/.vscode-test.*
|
||||
201
editors/vscode/LICENSE
Normal file
201
editors/vscode/LICENSE
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
19
editors/vscode/README.md
Normal file
19
editors/vscode/README.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# vscode-clice
|
||||
|
||||
This is the vscode extension for [clice](https://github.com/clice-project/clice).
|
||||
|
||||
## develop
|
||||
|
||||
- install dependencies
|
||||
|
||||
```shell
|
||||
git clone https://github.com/clice-io/clice.git
|
||||
cd clice/editors/vscode
|
||||
npm install
|
||||
```
|
||||
|
||||
- package
|
||||
|
||||
```shell
|
||||
npm run package
|
||||
```
|
||||
BIN
editors/vscode/clice.png
Normal file
BIN
editors/vscode/clice.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 62 KiB |
182
editors/vscode/package.json
Normal file
182
editors/vscode/package.json
Normal file
@@ -0,0 +1,182 @@
|
||||
{
|
||||
"name": "clice-vscode",
|
||||
"displayName": "clice",
|
||||
"description": "VSCode extension for clice",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/clice-project/clice-vscode"
|
||||
},
|
||||
"version": "0.1.4",
|
||||
"publisher": "ykiko",
|
||||
"icon": "clice.png",
|
||||
"engines": {
|
||||
"vscode": "^1.80.0"
|
||||
},
|
||||
"categories": [
|
||||
"Programming Languages",
|
||||
"Linters",
|
||||
"Formatters"
|
||||
],
|
||||
"keywords": [
|
||||
"C",
|
||||
"C++",
|
||||
"cuda",
|
||||
"clang"
|
||||
],
|
||||
"activationEvents": [
|
||||
"onLanguage:c",
|
||||
"onLanguage:cpp",
|
||||
"onLanguage:cuda-cpp"
|
||||
],
|
||||
"contributes": {
|
||||
"configuration": {
|
||||
"type": "object",
|
||||
"title": "C/C++ Language Client",
|
||||
"properties": {
|
||||
"clice-client.trace.server": {
|
||||
"default": "verbose"
|
||||
},
|
||||
"clice.executable": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "The path of clice executable."
|
||||
},
|
||||
"clice.mode": {
|
||||
"type": "string",
|
||||
"default": "pipe",
|
||||
"enum": [
|
||||
"pipe",
|
||||
"socket"
|
||||
],
|
||||
"description": "How to communicate with clice. pipe or socket. For daily use please use pipe."
|
||||
},
|
||||
"clice.host": {
|
||||
"type": "string",
|
||||
"default": "127.0.0.1",
|
||||
"description": "The host to connect to (default: 127.0.0.1)"
|
||||
},
|
||||
"clice.port": {
|
||||
"type": "number",
|
||||
"default": 50051,
|
||||
"description": "The port to connect to"
|
||||
}
|
||||
}
|
||||
},
|
||||
"commands": [
|
||||
{
|
||||
"command": "clice.restart",
|
||||
"title": "Clice: Restart Language Server"
|
||||
}
|
||||
],
|
||||
"semanticTokenTypes": [
|
||||
{
|
||||
"id": "character",
|
||||
"description": "C/C++ character literal (e.g., 'a')",
|
||||
"superType": "string"
|
||||
},
|
||||
{
|
||||
"id": "directive",
|
||||
"description": "C/C++ preprocessor directive (e.g., #include)",
|
||||
"superType": "keyword"
|
||||
},
|
||||
{
|
||||
"id": "header",
|
||||
"description": "C/C++ header name (e.g., <iostream>)",
|
||||
"superType": "string"
|
||||
},
|
||||
{
|
||||
"id": "module",
|
||||
"description": "C++20 module name",
|
||||
"superType": "namespace"
|
||||
},
|
||||
{
|
||||
"id": "macroParameter",
|
||||
"description": "C/C++ macro parameter",
|
||||
"superType": "parameter"
|
||||
},
|
||||
{
|
||||
"id": "union",
|
||||
"description": "C/C++ union type",
|
||||
"superType": "struct"
|
||||
},
|
||||
{
|
||||
"id": "field",
|
||||
"description": "C/C++ field (member variable)",
|
||||
"superType": "variable"
|
||||
},
|
||||
{
|
||||
"id": "label",
|
||||
"description": "C/C++ label (for goto)",
|
||||
"superType": "variable"
|
||||
},
|
||||
{
|
||||
"id": "concept",
|
||||
"description": "C++20 concept",
|
||||
"superType": "type"
|
||||
},
|
||||
{
|
||||
"id": "attribute",
|
||||
"description": "GNU/MSVC/C++11/C23 attribute",
|
||||
"superType": "macro"
|
||||
},
|
||||
{
|
||||
"id": "paren",
|
||||
"description": "Parentheses `()`",
|
||||
"superType": "operator"
|
||||
},
|
||||
{
|
||||
"id": "bracket",
|
||||
"description": "Brackets `[]`",
|
||||
"superType": "operator"
|
||||
},
|
||||
{
|
||||
"id": "brace",
|
||||
"description": "Braces `{}`",
|
||||
"superType": "operator"
|
||||
},
|
||||
{
|
||||
"id": "angle",
|
||||
"description": "Angle brackets `<>`",
|
||||
"superType": "operator"
|
||||
}
|
||||
]
|
||||
},
|
||||
"main": "./dist/extension.js",
|
||||
"scripts": {
|
||||
"compile": "webpack",
|
||||
"watch": "webpack --watch",
|
||||
"vscode:prepublish": "webpack --mode production --devtool hidden-source-map",
|
||||
"package": "vsce package --no-dependencies --baseImagesUrl https://raw.githubusercontent.com/clice-io/clice/main/",
|
||||
"publish": "vsce publish --no-dependencies --baseImagesUrl https://raw.githubusercontent.com/clice-io/clice/main/",
|
||||
"pretest": "pnpm run compile",
|
||||
"test": "vscode-test",
|
||||
"release:patch": "pnpm version patch -m \"release: v%s\" && git push --follow-tags",
|
||||
"release:minor": "pnpm version minor -m \"release: v%s\" && git push --follow-tags"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/decompress": "^4.2.7",
|
||||
"@types/mocha": "^10.0.10",
|
||||
"@types/node": "~25.0.3",
|
||||
"@types/vscode": "^1.80.0",
|
||||
"@vscode/test-cli": "^0.0.12",
|
||||
"@vscode/test-electron": "^2.5.2",
|
||||
"@vscode/vsce": "^3.7.1",
|
||||
"ts-loader": "^9.5.4",
|
||||
"typescript": "~5.5.4",
|
||||
"webpack": "^5.104.1",
|
||||
"webpack-cli": "^6.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"decompress": "^4.2.1",
|
||||
"vscode-languageclient": "^9.0.1"
|
||||
},
|
||||
"overrides": {
|
||||
"glob": "^11.1.0"
|
||||
},
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
"@vscode/vsce-sign",
|
||||
"keytar"
|
||||
]
|
||||
}
|
||||
}
|
||||
4373
editors/vscode/pnpm-lock.yaml
generated
Normal file
4373
editors/vscode/pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
247
editors/vscode/src/download.ts
Normal file
247
editors/vscode/src/download.ts
Normal file
@@ -0,0 +1,247 @@
|
||||
import * as vscode from "vscode";
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
import * as https from "https";
|
||||
import * as os from "os";
|
||||
// @ts-ignore
|
||||
import decompress = require("decompress");
|
||||
|
||||
interface GitHubRelease {
|
||||
tag_name: string;
|
||||
assets: {
|
||||
name: string;
|
||||
browser_download_url: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
export async function ensureServerBinary(
|
||||
context: vscode.ExtensionContext,
|
||||
channel: vscode.OutputChannel,
|
||||
): Promise<string | undefined> {
|
||||
const storagePath = context.globalStorageUri.fsPath;
|
||||
const platform = os.platform();
|
||||
const arch = os.arch();
|
||||
|
||||
channel.appendLine(`[Download] Initializing clice downloader...`);
|
||||
channel.appendLine(`[Download] Platform: ${platform}, Arch: ${arch}, Storage: ${storagePath}`);
|
||||
|
||||
let platformKeyword = "";
|
||||
let archKeyword = "";
|
||||
let binaryName = "clice";
|
||||
|
||||
if (platform === "win32") {
|
||||
platformKeyword = "windows";
|
||||
archKeyword = "x64";
|
||||
binaryName = "clice.exe";
|
||||
} else if (platform === "darwin") {
|
||||
platformKeyword = "macos";
|
||||
archKeyword = arch;
|
||||
} else if (platform === "linux") {
|
||||
platformKeyword = "linux";
|
||||
archKeyword = arch === "x64" ? "x86_64" : arch;
|
||||
} else {
|
||||
const msg = `Unsupported platform: ${platform}`;
|
||||
channel.appendLine(`[Download] Error: ${msg}`);
|
||||
vscode.window.showErrorMessage(msg);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const executablePath = path.join(storagePath, "bin", binaryName);
|
||||
|
||||
if (fs.existsSync(executablePath)) {
|
||||
channel.appendLine(`[Download] Found existing binary at: ${executablePath}`);
|
||||
// TODO: check tag update
|
||||
return executablePath;
|
||||
}
|
||||
|
||||
if (!fs.existsSync(storagePath)) {
|
||||
channel.appendLine(`[Download] Creating storage directory: ${storagePath}`);
|
||||
fs.mkdirSync(storagePath, { recursive: true });
|
||||
}
|
||||
|
||||
const statusItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100);
|
||||
|
||||
try {
|
||||
statusItem.text = "$(sync~spin) Checking clice updates...";
|
||||
statusItem.show();
|
||||
|
||||
channel.appendLine(`[Download] Fetching latest release from GitHub...`);
|
||||
const release = await fetchReleaseInfo(channel);
|
||||
channel.appendLine(`[Download] Latest tag: ${release.tag_name}`);
|
||||
|
||||
const asset = release.assets.find((a) => {
|
||||
const name = a.name.toLowerCase();
|
||||
return (
|
||||
name.includes(platformKeyword) &&
|
||||
name.includes(archKeyword) &&
|
||||
!name.includes("symbol")
|
||||
);
|
||||
});
|
||||
|
||||
if (!asset) {
|
||||
throw new Error(
|
||||
`No compatible asset found for ${platform}-${archKeyword} in release ${release.tag_name}`,
|
||||
);
|
||||
}
|
||||
|
||||
channel.appendLine(`[Download] Found asset: ${asset.name}`);
|
||||
channel.appendLine(`[Download] Download URL: ${asset.browser_download_url}`);
|
||||
|
||||
const tempArchiveName = asset.name;
|
||||
const tempArchivePath = path.join(storagePath, tempArchiveName);
|
||||
|
||||
statusItem.text = `$(cloud-download) Downloading clice...`;
|
||||
await downloadFile(asset.browser_download_url, tempArchivePath, channel);
|
||||
|
||||
statusItem.text = `$(file-zip) Extracting...`;
|
||||
channel.appendLine(`[Download] Extracting ${tempArchivePath} to ${storagePath}...`);
|
||||
|
||||
await decompress(tempArchivePath, storagePath);
|
||||
channel.appendLine(`[Download] Extraction complete.`);
|
||||
|
||||
fs.unlinkSync(tempArchivePath);
|
||||
|
||||
if (!fs.existsSync(executablePath)) {
|
||||
throw new Error(`Executable not found at ${executablePath} after extraction.`);
|
||||
}
|
||||
|
||||
if (platform !== "win32") {
|
||||
channel.appendLine(`[Download] Setting executable permissions (chmod 755)...`);
|
||||
fs.chmodSync(executablePath, "755");
|
||||
}
|
||||
|
||||
channel.appendLine(`[Download] Setup successful. Binary ready at: ${executablePath}`);
|
||||
vscode.window.showInformationMessage(
|
||||
`Clice language server updated to ${release.tag_name}`,
|
||||
);
|
||||
return executablePath;
|
||||
} catch (error) {
|
||||
channel.appendLine(`[Download] CRITICAL ERROR during setup:`);
|
||||
if (error instanceof Error) {
|
||||
channel.appendLine(`[Download] Message: ${error.message}`);
|
||||
if (error.stack) {
|
||||
channel.appendLine(`[Download] Stack: ${error.stack}`);
|
||||
}
|
||||
} else {
|
||||
channel.appendLine(`[Download] Unknown error: ${JSON.stringify(error)}`);
|
||||
}
|
||||
|
||||
vscode.window
|
||||
.showErrorMessage(
|
||||
`Failed to download clice server. Check "clice" output channel for details.`,
|
||||
"Open Output",
|
||||
)
|
||||
.then((selection) => {
|
||||
if (selection === "Open Output") {
|
||||
channel.show();
|
||||
}
|
||||
});
|
||||
|
||||
return undefined;
|
||||
} finally {
|
||||
statusItem.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
function downloadFile(
|
||||
url: string,
|
||||
destPath: string,
|
||||
channel: vscode.OutputChannel,
|
||||
maxRedirects = 5,
|
||||
): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (maxRedirects <= 0) {
|
||||
reject(new Error("Too many redirects"));
|
||||
return;
|
||||
}
|
||||
|
||||
const file = fs.createWriteStream(destPath);
|
||||
channel.appendLine(`[Download] Start downloading to ${destPath}`);
|
||||
|
||||
https
|
||||
.get(url, { headers: { "User-Agent": "VSCode-Extension" } }, (response) => {
|
||||
if (response.statusCode === 302 || response.statusCode === 301) {
|
||||
channel.appendLine(`[Download] Redirecting to ${response.headers.location}`);
|
||||
file.close();
|
||||
downloadFile(response.headers.location!, destPath, channel, maxRedirects - 1)
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
return;
|
||||
}
|
||||
if (response.statusCode !== 200) {
|
||||
reject(new Error(`Download failed with status code ${response.statusCode}`));
|
||||
return;
|
||||
}
|
||||
|
||||
response.pipe(file);
|
||||
file.on("finish", () => {
|
||||
file.close();
|
||||
channel.appendLine(`[Download] Download finished.`);
|
||||
resolve();
|
||||
});
|
||||
})
|
||||
.on("error", (err) => {
|
||||
file.close();
|
||||
fs.unlink(destPath, () => {});
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function fetchReleaseInfo(channel: vscode.OutputChannel): Promise<GitHubRelease> {
|
||||
try {
|
||||
channel.appendLine("[Download] Attempting to fetch latest stable release...");
|
||||
const release = await fetchJson<GitHubRelease>("/repos/clice-io/clice/releases/latest");
|
||||
channel.appendLine(`[Download] Found stable release: ${release.tag_name}`);
|
||||
return release;
|
||||
} catch (error: any) {
|
||||
if (error.message && error.message.includes("404")) {
|
||||
channel.appendLine(
|
||||
"[Download] Latest stable release not found (404). Checking for pre-releases...",
|
||||
);
|
||||
|
||||
const releases = await fetchJson<GitHubRelease[]>(
|
||||
"/repos/clice-io/clice/releases?per_page=1",
|
||||
);
|
||||
|
||||
if (Array.isArray(releases) && releases.length > 0) {
|
||||
const latestPre = releases[0];
|
||||
channel.appendLine(`[Download] Found pre-release: ${latestPre.tag_name}`);
|
||||
return latestPre;
|
||||
} else {
|
||||
throw new Error("No releases found in repository.");
|
||||
}
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
function fetchJson<T>(apiPath: string): Promise<T> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const options = {
|
||||
hostname: "api.github.com",
|
||||
path: apiPath,
|
||||
headers: { "User-Agent": "VSCode-Extension" },
|
||||
};
|
||||
|
||||
https
|
||||
.get(options, (res) => {
|
||||
let data = "";
|
||||
if (res.statusCode && (res.statusCode < 200 || res.statusCode >= 300)) {
|
||||
res.resume();
|
||||
reject(new Error(`GitHub API returned ${res.statusCode} for ${apiPath}`));
|
||||
return;
|
||||
}
|
||||
|
||||
res.on("data", (chunk) => (data += chunk));
|
||||
res.on("end", () => {
|
||||
try {
|
||||
resolve(JSON.parse(data));
|
||||
} catch (e) {
|
||||
reject(new Error(`Failed to parse GitHub API response: ${e}`));
|
||||
}
|
||||
});
|
||||
})
|
||||
.on("error", reject);
|
||||
});
|
||||
}
|
||||
95
editors/vscode/src/extension.ts
Normal file
95
editors/vscode/src/extension.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import * as net from "net";
|
||||
import * as vscode from "vscode";
|
||||
import { workspace, window, ExtensionContext } from "vscode";
|
||||
import {
|
||||
LanguageClient,
|
||||
LanguageClientOptions,
|
||||
ServerOptions,
|
||||
StreamInfo,
|
||||
} from "vscode-languageclient/node";
|
||||
import { getSetting } from "./setting";
|
||||
import { ensureServerBinary } from "./download";
|
||||
|
||||
let client: LanguageClient;
|
||||
|
||||
export async function registerCommands(client: LanguageClient, context: ExtensionContext) {
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand("clice.restart", async () => {
|
||||
await client.restart();
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export async function activate(context: ExtensionContext) {
|
||||
console.log('Congratulations, your extension "clice" is now active!');
|
||||
|
||||
const channel = window.createOutputChannel("clice");
|
||||
const verboseChannel = window.createOutputChannel("clice-verbose");
|
||||
|
||||
const setting = getSetting();
|
||||
if (!setting) {
|
||||
return;
|
||||
}
|
||||
|
||||
let executable = setting.executable;
|
||||
let serverOptions: ServerOptions | (() => Promise<StreamInfo>);
|
||||
|
||||
if (setting.mode === "pipe") {
|
||||
if (!executable || executable === "") {
|
||||
const downloadedPath = await ensureServerBinary(context, channel);
|
||||
if (downloadedPath) {
|
||||
executable = downloadedPath;
|
||||
} else {
|
||||
window.showErrorMessage("Could not find or download clice executable.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let args = ["--mode=pipe"];
|
||||
serverOptions = {
|
||||
run: { command: executable, args: args },
|
||||
debug: { command: executable, args: args },
|
||||
};
|
||||
} else if (setting.mode === "socket") {
|
||||
serverOptions = (): Promise<StreamInfo> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const client = new net.Socket();
|
||||
client.connect(setting.port, setting.host, () => {
|
||||
resolve({
|
||||
reader: client,
|
||||
writer: client,
|
||||
});
|
||||
});
|
||||
client.on("error", (error) => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
};
|
||||
} else {
|
||||
vscode.window.showErrorMessage("Invalid mode, please set the mode to 'pipe' or 'socket'.");
|
||||
return;
|
||||
}
|
||||
|
||||
const clientOptions: LanguageClientOptions = {
|
||||
documentSelector: [{ scheme: "file", language: "cpp" }],
|
||||
outputChannel: channel,
|
||||
traceOutputChannel: verboseChannel,
|
||||
synchronize: {
|
||||
fileEvents: workspace.createFileSystemWatcher("**/.clientrc"),
|
||||
},
|
||||
};
|
||||
|
||||
client = new LanguageClient("clice", "clice", serverOptions, clientOptions);
|
||||
|
||||
await registerCommands(client, context);
|
||||
|
||||
await client.start();
|
||||
}
|
||||
|
||||
export function deactivate(): Thenable<void> | undefined {
|
||||
if (!client) {
|
||||
return undefined;
|
||||
}
|
||||
let ret = client.stop();
|
||||
return ret;
|
||||
}
|
||||
72
editors/vscode/src/feature/header.ts
Normal file
72
editors/vscode/src/feature/header.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import * as vscode from "vscode";
|
||||
import { DocumentUri } from "vscode-languageclient/node";
|
||||
|
||||
let provider: HeaderContextProvider | undefined = undefined;
|
||||
|
||||
export type HeaderContext = {
|
||||
file: string;
|
||||
index: number;
|
||||
include: number;
|
||||
};
|
||||
|
||||
export type HeaderContextSwitchParams = {
|
||||
header: DocumentUri;
|
||||
context: HeaderContext;
|
||||
};
|
||||
|
||||
export type IncludeLocation = {
|
||||
line: number;
|
||||
filename: string;
|
||||
};
|
||||
|
||||
export class TreeItem extends vscode.TreeItem {
|
||||
children: Array<TreeItem> = [];
|
||||
context: HeaderContext | undefined = undefined;
|
||||
}
|
||||
|
||||
export class HeaderContextProvider implements vscode.TreeDataProvider<TreeItem> {
|
||||
private _onDidChangeTreeData: vscode.EventEmitter<TreeItem | undefined | void> =
|
||||
new vscode.EventEmitter<TreeItem | undefined | void>();
|
||||
readonly onDidChangeTreeData: vscode.Event<TreeItem | undefined | void> =
|
||||
this._onDidChangeTreeData.event;
|
||||
|
||||
header: string = "";
|
||||
items: Array<TreeItem> = [];
|
||||
|
||||
update(contexts: Array<Array<HeaderContext>>) {
|
||||
/// Create groups
|
||||
this.items = contexts.map((contexts) => {
|
||||
let item = new TreeItem("", vscode.TreeItemCollapsibleState.Expanded);
|
||||
item.children = contexts.map((context) => {
|
||||
const uri = vscode.Uri.file(context.file);
|
||||
let item = new TreeItem(uri, vscode.TreeItemCollapsibleState.None);
|
||||
item.context = context;
|
||||
item.iconPath = vscode.ThemeIcon.File;
|
||||
item.contextValue = "header-context";
|
||||
return item;
|
||||
});
|
||||
return item;
|
||||
});
|
||||
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
refresh(): void {
|
||||
this._onDidChangeTreeData.fire();
|
||||
}
|
||||
|
||||
getTreeItem(element: TreeItem) {
|
||||
return element;
|
||||
}
|
||||
|
||||
getChildren(element?: TreeItem) {
|
||||
return element ? element.children : this.items;
|
||||
}
|
||||
}
|
||||
|
||||
export function registerHeaderContextView() {
|
||||
provider = new HeaderContextProvider();
|
||||
let treeView = vscode.window.createTreeView("header-contexts", {
|
||||
treeDataProvider: provider,
|
||||
});
|
||||
}
|
||||
63
editors/vscode/src/feature/highlight.ts
Normal file
63
editors/vscode/src/feature/highlight.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import * as vscode from "vscode";
|
||||
|
||||
const rainbowColors = ["#56B6C2", "#61AFEF", "#C678DD", "#E06C75", "#98C379", "#D19A66", "#E5C07B"];
|
||||
|
||||
const textEditorDecorationTypes = rainbowColors.map((color) => {
|
||||
return vscode.window.createTextEditorDecorationType({
|
||||
color: color,
|
||||
});
|
||||
});
|
||||
|
||||
export function highlightDocument(
|
||||
document: vscode.TextDocument,
|
||||
legend: vscode.SemanticTokensLegend,
|
||||
semanticTokens: vscode.SemanticTokens,
|
||||
) {
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
if (!editor || editor.document !== document) {
|
||||
return;
|
||||
}
|
||||
const angleIndex = legend?.tokenTypes.indexOf("angle");
|
||||
const leftIndex = legend?.tokenModifiers.indexOf("left");
|
||||
const rightIndex = legend?.tokenModifiers.indexOf("right");
|
||||
if (leftIndex === undefined || rightIndex === undefined || angleIndex === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const decorations = new Map<number, vscode.Range[]>();
|
||||
let level = 0;
|
||||
|
||||
let lastLine = 0;
|
||||
let lastStart = 0;
|
||||
|
||||
// [line, startCharacter, length, tokenType, tokenModifiers]
|
||||
for (let i = 0; i < semanticTokens.data.length; i += 5) {
|
||||
const [lineDelta, startDelta, length, tokenType, tokenModifiers] =
|
||||
semanticTokens.data.slice(i, i + 5);
|
||||
|
||||
lastLine += lineDelta;
|
||||
lastStart = lineDelta === 0 ? lastStart + startDelta : startDelta;
|
||||
|
||||
const range = new vscode.Range(lastLine, lastStart, lastLine, lastStart + length);
|
||||
|
||||
if (tokenType === angleIndex) {
|
||||
if (tokenModifiers & (1 << rightIndex)) {
|
||||
level -= 1;
|
||||
}
|
||||
|
||||
if (decorations.has(level % rainbowColors.length)) {
|
||||
decorations.get(level % rainbowColors.length)?.push(range);
|
||||
} else {
|
||||
decorations.set(level % rainbowColors.length, [range]);
|
||||
}
|
||||
|
||||
if (tokenModifiers & (1 << leftIndex)) {
|
||||
level += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const [level, ranges] of decorations) {
|
||||
editor.setDecorations(textEditorDecorationTypes[level], ranges);
|
||||
}
|
||||
}
|
||||
34
editors/vscode/src/setting.ts
Normal file
34
editors/vscode/src/setting.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import * as vscode from "vscode";
|
||||
|
||||
interface Setting {
|
||||
executable: string | undefined;
|
||||
mode: string;
|
||||
host: string;
|
||||
port: number;
|
||||
}
|
||||
|
||||
export function getSetting(): Setting | undefined {
|
||||
const setting = vscode.workspace.getConfiguration("clice");
|
||||
const executable = setting.get<string>("executable");
|
||||
const mode = process.env.CLICE_MODE || setting.get<string>("mode");
|
||||
|
||||
if (mode !== "pipe" && mode !== "socket") {
|
||||
vscode.window.showErrorMessage(`Unexpected mode: ${mode}`);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const host = setting.get<string>("host")!;
|
||||
const port = setting.get<number>("port")!;
|
||||
|
||||
if (mode === "socket" && (!host || !port)) {
|
||||
vscode.window.showErrorMessage("Socket mode requires both host and port to be configured.");
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
executable,
|
||||
mode,
|
||||
host,
|
||||
port,
|
||||
};
|
||||
}
|
||||
15
editors/vscode/src/test/extension.test.ts
Normal file
15
editors/vscode/src/test/extension.test.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import * as assert from "assert";
|
||||
|
||||
// You can import and use all API from the 'vscode' module
|
||||
// as well as import your extension to test it
|
||||
import * as vscode from "vscode";
|
||||
// import * as myExtension from '../../extension';
|
||||
|
||||
suite("Extension Test Suite", () => {
|
||||
vscode.window.showInformationMessage("Start all tests.");
|
||||
|
||||
test("Sample test", () => {
|
||||
assert.strictEqual(-1, [1, 2, 3].indexOf(5));
|
||||
assert.strictEqual(-1, [1, 2, 3].indexOf(0));
|
||||
});
|
||||
});
|
||||
14
editors/vscode/tsconfig.json
Normal file
14
editors/vscode/tsconfig.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "Node16",
|
||||
"target": "ES2022",
|
||||
"lib": ["ES2022"],
|
||||
"sourceMap": true,
|
||||
"rootDir": "src",
|
||||
"strict": true /* enable all strict type-checking options */
|
||||
/* Additional Checks */
|
||||
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||
}
|
||||
}
|
||||
48
editors/vscode/webpack.config.js
Normal file
48
editors/vscode/webpack.config.js
Normal file
@@ -0,0 +1,48 @@
|
||||
//@ts-check
|
||||
|
||||
"use strict";
|
||||
|
||||
const path = require("path");
|
||||
|
||||
//@ts-check
|
||||
/** @typedef {import('webpack').Configuration} WebpackConfig **/
|
||||
|
||||
/** @type WebpackConfig */
|
||||
const extensionConfig = {
|
||||
target: "node", // VS Code extensions run in a Node.js-context 📖 -> https://webpack.js.org/configuration/node/
|
||||
mode: "none", // this leaves the source code as close as possible to the original (when packaging we set this to 'production')
|
||||
|
||||
entry: "./src/extension.ts", // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/
|
||||
output: {
|
||||
// the bundle is stored in the 'dist' folder (check package.json), 📖 -> https://webpack.js.org/configuration/output/
|
||||
path: path.resolve(__dirname, "dist"),
|
||||
filename: "extension.js",
|
||||
libraryTarget: "commonjs2",
|
||||
},
|
||||
externals: {
|
||||
vscode: "commonjs vscode", // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed, 📖 -> https://webpack.js.org/configuration/externals/
|
||||
// modules added here also need to be added in the .vscodeignore file
|
||||
},
|
||||
resolve: {
|
||||
// support reading TypeScript and JavaScript files, 📖 -> https://github.com/TypeStrong/ts-loader
|
||||
extensions: [".ts", ".js"],
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.ts$/,
|
||||
exclude: /node_modules/,
|
||||
use: [
|
||||
{
|
||||
loader: "ts-loader",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
devtool: "nosources-source-map",
|
||||
infrastructureLogging: {
|
||||
level: "log", // enables logging required for problem matchers
|
||||
},
|
||||
};
|
||||
module.exports = [extensionConfig];
|
||||
1
editors/zed/.gitignore
vendored
Normal file
1
editors/zed/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/target
|
||||
329
editors/zed/Cargo.lock
generated
Normal file
329
editors/zed/Cargo.lock
generated
Normal file
@@ -0,0 +1,329 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "id-arena"
|
||||
version = "2.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||
|
||||
[[package]]
|
||||
name = "leb128"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.143"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
|
||||
[[package]]
|
||||
name = "spdx"
|
||||
version = "0.10.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3e17e880bafaeb362a7b751ec46bdc5b61445a188f80e0606e68167cd540fa3"
|
||||
dependencies = [
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-encoder"
|
||||
version = "0.201.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9c7d2731df60006819b013f64ccc2019691deccf6e11a1804bc850cd6748f1a"
|
||||
dependencies = [
|
||||
"leb128",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-metadata"
|
||||
version = "0.201.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fd83062c17b9f4985d438603cde0a5e8c5c8198201a6937f778b607924c7da2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"spdx",
|
||||
"wasm-encoder",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.201.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84e5df6dba6c0d7fafc63a450f1738451ed7a0b52295d83e868218fa286bf708"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"indexmap",
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "288f992ea30e6b5c531b52cdd5f3be81c148554b09ea416f058d16556ba92c27"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"wit-bindgen-rt",
|
||||
"wit-bindgen-rust-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-core"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e85e72719ffbccf279359ad071497e47eb0675fe22106dea4ed2d8a7fcb60ba4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"wit-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcb8738270f32a2d6739973cbbb7c1b6dd8959ce515578a6e19165853272ee64"
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rust"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8a39a15d1ae2077688213611209849cad40e9e5cccf6e61951a425850677ff3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
"indexmap",
|
||||
"wasm-metadata",
|
||||
"wit-bindgen-core",
|
||||
"wit-component",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rust-macro"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d376d3ae5850526dfd00d937faea0d81a06fa18f7ac1e26f386d760f241a8f4b"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wit-bindgen-core",
|
||||
"wit-bindgen-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-component"
|
||||
version = "0.201.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "421c0c848a0660a8c22e2fd217929a0191f14476b68962afd2af89fd22e39825"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags",
|
||||
"indexmap",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"wasm-encoder",
|
||||
"wasm-metadata",
|
||||
"wasmparser",
|
||||
"wit-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-parser"
|
||||
version = "0.201.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "196d3ecfc4b759a8573bf86a9b3f8996b304b3732e4c7de81655f875f6efdca6"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"id-arena",
|
||||
"indexmap",
|
||||
"log",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"unicode-xid",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zed_clice"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"zed_extension_api",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zed_extension_api"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "594fd10dd0f2f853eb243e2425e7c95938cef49adb81d9602921d002c5e6d9d9"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wit-bindgen",
|
||||
]
|
||||
13
editors/zed/Cargo.toml
Normal file
13
editors/zed/Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "zed_clice"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
path = "src/clice.rs"
|
||||
|
||||
[dependencies]
|
||||
zed_extension_api = "0.1.0"
|
||||
21
editors/zed/LICENSE
Normal file
21
editors/zed/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 clice-project
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user