diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 44d25611..f774663f 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -62,17 +62,17 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - - name: Setup llvm binary - if: matrix.os == 'ubuntu-24.04' - run: | - mkdir -p ./.llvm - curl -L "https://github.com/clice-project/llvm-binary/releases/download/20.0.0/x86_64-linux-gnu-debug.tar.xz" | tar -xJ -C ./.llvm - - name: Setup llvm binary if: matrix.os == 'windows-2025' run: | curl -O -L "https://github.com/clice-project/llvm-binary/releases/download/20.0.0/x64-windows-msvc-release.7z" 7z x x64-windows-msvc-release.7z "-o.llvm" + + - name: Setup llvm binary + if: matrix.os == 'ubuntu-24.04' + run: | + mkdir -p ./.llvm + curl -L "https://github.com/clice-project/llvm-binary/releases/download/20.0.0/x86_64-linux-gnu-debug.tar.xz" | tar -xJ -C ./.llvm - name: Setup llvm binary if: matrix.os == 'macos-15' @@ -84,6 +84,12 @@ jobs: if: matrix.os == 'windows-2025' uses: ilammy/msvc-dev-cmd@v1 + - name: Build clice (release) + if: matrix.os == 'windows-2025' + run: | + cmake --preset release + cmake --build --preset release + - name: Build clice (debug) if: matrix.os == 'ubuntu-24.04' run: | @@ -97,16 +103,17 @@ jobs: cmake --preset debug cmake --build --preset debug -v - - name: Build clice (release) - if: matrix.os == 'windows-2025' - run: | - cmake --preset release - cmake --build --preset release - - name: Install Python dependencies run: pip install pytest pytest-asyncio pytest-xdist - name: Run tests + if: matrix.os == 'windows-2025' + run: | + ./build/bin/unit_tests.exe --test-dir="./tests/data" --resource-dir="./.llvm/lib/clang/20" + pytest -s tests/integration --executable=./build/bin/clice.exe + + - name: Run tests + if: matrix.os == 'ubuntu-24.04' || matrix.os == 'macos-15' run: | ./build/bin/unit_tests --test-dir="./tests/data" --resource-dir="./.llvm/lib/clang/20" pytest -s tests/integration --executable=./build/bin/clice diff --git a/.gitignore b/.gitignore index cf2e1f8b..02ef84ea 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,7 @@ temp/ *build*/ .cache/ +.clice/ .llvm*/ .xmake/ .vscode/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 06b5bec3..d69ac43c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.20) -project(CLICE_PROJECT LANGUAGES CXX) +project(CLICE_PROJECT LANGUAGES C CXX) set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED ON) diff --git a/src/Server/Scheduler.cpp b/src/Server/Scheduler.cpp index 1efbe47d..96fee34a 100644 --- a/src/Server/Scheduler.cpp +++ b/src/Server/Scheduler.cpp @@ -109,6 +109,14 @@ async::Task<> Scheduler::buildPCH(std::string path, std::string content) { std::string content) -> async::Task<> { CompilationParams params; params.arguments = scheduler.database.get_command(path, true).arguments; + if(!fs::exists(config::cache.dir)) { + auto error = fs::create_directories(config::cache.dir); + if(error) { + log::warn("Fail to create directory for PCH building: {}", config::cache.dir); + co_return; + } + } + params.outPath = path::join(config::cache.dir, path::filename(path) + ".pch"); params.add_remapped_file(path, content, bound); diff --git a/tests/conftest.py b/tests/conftest.py index 938a5faa..4eb45f30 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -13,8 +13,24 @@ def pytest_addoption(parser): @pytest.fixture(scope="session") def executable(request): - path = request.config.getoption("--executable") - return Path(path).resolve() + executable = request.config.getoption("--executable") + if executable is None: + pytest.exit( + "Error: You must specify the 'clice' executable path using " + "'--executable=' in pytest arguments, " + "or configure it in your pytest.ini/conftest.py.", + returncode=64 + ) + + path = Path(executable) + if not path.exists(): + pytest.exit( + f"Error: 'clice' executable not found at '{executable}'. " + "Please ensure the path is correct and the file exists.", + returncode=64 + ) + + return path.resolve() @pytest.fixture(scope="session") diff --git a/tests/fixtures/client.py b/tests/fixtures/client.py index c03ae44b..36e235f6 100644 --- a/tests/fixtures/client.py +++ b/tests/fixtures/client.py @@ -5,6 +5,8 @@ from .transport import LSPTransport class LSPClient(LSPTransport): def __init__(self, commands, mode="stdio", host="127.0.0.1", port=2087): super().__init__(commands, mode, host, port) + self.workspace = "" + self.opening_files: dict[Path, str] = {} async def initialize(self, workspace: str): self.workspace = workspace @@ -18,3 +20,29 @@ class LSPClient(LSPTransport): async def exit(self): await self.send_notification("exit") await self.stop() + + def get_abs_path(self, relative_path): + return Path(self.workspace, relative_path) + + async def did_open(self, relative_path: str): + path = self.get_abs_path(relative_path) + + content = "" + with open(path, encoding="utf-8") as file: + content = file.read() + + if path in self.opening_files: + raise f"Cannot open same file multiple times: {path}" + + self.opening_files[path] = content + + params = { + "textDocument": { + "uri": path.as_uri(), + "languageId": "cpp", + "version": 0, + "text": content, + } + } + + await self.send_notification("textDocument/didOpen", params) diff --git a/tests/fixtures/transport.py b/tests/fixtures/transport.py index 934d0662..39cf5b91 100644 --- a/tests/fixtures/transport.py +++ b/tests/fixtures/transport.py @@ -11,7 +11,6 @@ class LSPTransport: self.mode = mode self.host = host self.port = port - self.workspace = "" self.process: asyncio.subprocess.Process = None self.reader: asyncio.StreamReader = None diff --git a/tests/integration/test_file_operation.py b/tests/integration/test_file_operation.py new file mode 100644 index 00000000..4090afc6 --- /dev/null +++ b/tests/integration/test_file_operation.py @@ -0,0 +1,17 @@ +import sys +import pytest +import asyncio +from ..fixtures.client import LSPClient + + +@pytest.mark.asyncio +async def test_did_open(executable, test_data_dir): + client = LSPClient([ + executable, "--mode=pipe" + ]) + await client.start() + + await client.initialize(test_data_dir / "hello_world") + await client.did_open("main.cpp") + await asyncio.sleep(3) + await client.exit() diff --git a/tests/integration/test_simple.py b/tests/integration/test_lifecycle.py similarity index 100% rename from tests/integration/test_simple.py rename to tests/integration/test_lifecycle.py