diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index e0dd54fe..bf8f4325 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -9,7 +9,6 @@ on: - "src/**" - "tests/**" - "CMakeLists.txt" - pull_request: branches: [main] paths: @@ -23,39 +22,48 @@ on: jobs: build: strategy: + fail-fast: false matrix: - os: [ubuntu-24.04, windows-2025, macos-15] + 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 ninja - if: matrix.os == 'windows-2025' + - name: Setup dependencies (Windows) + if: runner.os == 'Windows' uses: MinoruSekine/setup-scoop@v4.0.1 with: buckets: main apps: ninja - - name: Setup llvm & libstdc++ & cmake & ninja - if: matrix.os == 'ubuntu-24.04' + - 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 llvm@20 and lld - if: matrix.os == 'macos-15' + - name: Setup dependencies (MacOS) + if: runner.os == 'macOS' run: | brew install llvm@20 lld@20 @@ -63,40 +71,35 @@ jobs: uses: actions/checkout@v4 - name: Setup msvc sysroot for cmake - if: matrix.os == 'windows-2025' + if: runner.os == 'Windows' uses: ilammy/msvc-dev-cmd@v1 - - name: Build clice (release, windows) - if: matrix.os == 'windows-2025' + - name: Build clice + shell: bash run: | - cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCLICE_ENABLE_TEST=ON -DCLICE_CI_ENVIRONMENT=ON - cmake --build build + if [[ "${{ runner.os }}" == "macOS" ]]; then + export PATH="/opt/homebrew/opt/llvm@20/bin:/opt/homebrew/opt/lld@20/bin:$PATH" + fi - - name: Build clice (debug, linux) - if: matrix.os == 'ubuntu-24.04' - run: | - cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER=clang-20 -DCMAKE_CXX_COMPILER=clang++-20 -DCLICE_ENABLE_TEST=ON -DCLICE_CI_ENVIRONMENT=ON - cmake --build build + 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 - - name: Build clice (debug, macos) - if: matrix.os == 'macos-15' - run: | - export PATH="/opt/homebrew/opt/llvm@20/bin:/opt/homebrew/opt/lld@20/bin:$PATH" - cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -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 - if: matrix.os == 'windows-2025' - run: | - ./build/bin/unit_tests.exe --test-dir="./tests/data" - uv run pytest -s --log-cli-level=INFO tests/integration --executable=./build/bin/clice.exe shell: bash - - - name: Run tests - if: matrix.os == 'ubuntu-24.04' || matrix.os == 'macos-15' run: | - ./build/bin/unit_tests --test-dir="./tests/data" - uv run pytest -s --log-cli-level=INFO tests/integration --executable=./build/bin/clice + 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} diff --git a/.github/workflows/xmake.yml b/.github/workflows/xmake.yml index 204617f9..62687b07 100644 --- a/.github/workflows/xmake.yml +++ b/.github/workflows/xmake.yml @@ -9,7 +9,6 @@ on: - "src/**" - "tests/**" - "xmake.lua" - pull_request: branches: [main] paths: @@ -21,52 +20,21 @@ on: - "xmake.lua" jobs: - windows: + build: strategy: + fail-fast: false matrix: - os: [windows-2025] + 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.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: Xmake configure - run: | - xmake config --yes --ci=y --toolchain=clang - - - name: Build clice - run: | - xmake build --verbose --diagnosis --all - - - name: Install uv for integration tests - uses: astral-sh/setup-uv@v6 - - - name: Run tests - run: xmake test --verbose - - linux: - strategy: - matrix: - os: [ubuntu-24.04] - build_type: [release, debug] - - runs-on: ${{ matrix.os }} - - steps: - - name: Setup llvm & libstdc++ & cmake & ninja + - name: Setup dependencies (Linux) + if: runner.os == 'Linux' run: | sudo apt update sudo apt install -y gcc-14 g++-14 libstdc++-14-dev @@ -74,13 +42,16 @@ jobs: 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' + run: | + brew install llvm@20 lld@20 + - name: Checkout repository uses: actions/checkout@v4 @@ -95,9 +66,16 @@ jobs: build-cache: true build-cache-key: ${{ matrix.os }}-${{ matrix.build_type }} - - name: Xmake configure + - name: XMake configure + shell: bash run: | - xmake config --yes --ci=y --mode=${{ matrix.build_type }} --toolchain=clang-20 + 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 + xmake config --yes --ci=y --mode=${{ matrix.build_type }} --toolchain=clang --sdk=/opt/homebrew/opt/llvm@20 + fi - name: Build clice run: | @@ -107,52 +85,15 @@ jobs: uses: astral-sh/setup-uv@v6 - name: Run tests - run: xmake test --verbose - - # Workaround `no space left on device` - - name: Remove llvm package - run: rm -rf /home/runner/.xmake/packages/c/clice-llvm - - macos: - strategy: - matrix: - os: [macos-15] - build_type: [release, debug] - - runs-on: ${{ matrix.os }} - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Setup llvm + shell: bash run: | - brew install llvm@20 + # Workaround for macOS + if [[ "${{ runner.os }}" == "macOS" ]]; then + export PATH="/opt/homebrew/opt/llvm@20/bin:/opt/homebrew/opt/lld@20/bin:$PATH" + fi - - 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: Xmake configure - run: | - xmake config --yes --ci=y --mode=${{ matrix.build_type }} --toolchain=clang --sdk=/opt/homebrew/opt/llvm@20 - - - name: Build clice - run: | - xmake build --verbose --diagnosis --all - - - name: Install uv for integration tests - uses: astral-sh/setup-uv@v6 - - - name: Run tests - run: | - # Workaround for MacOS - export PATH="/opt/homebrew/opt/llvm@20/bin:/opt/homebrew/opt/lld@20/bin:$PATH" xmake test --verbose + + - name: Remove llvm package (Linux) + if: runner.os == 'Linux' + run: rm -rf /home/runner/.xmake/packages/c/clice-llvm diff --git a/CMakeLists.txt b/CMakeLists.txt index cc7eafd2..2e907723 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,6 @@ cmake_minimum_required(VERSION 3.20) project(CLICE_PROJECT LANGUAGES C CXX) - set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) @@ -34,9 +33,20 @@ endif() if(WIN32) set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreadedDLL") - target_link_options(clice_options INTERFACE -fuse-ld=lld-link) -else() - target_link_options(clice_options INTERFACE -fuse-ld=lld) + 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) @@ -51,6 +61,8 @@ else() -fno-exceptions -Wno-deprecated-declarations -Wno-undefined-inline + -ffunction-sections + -fdata-sections ) endif() diff --git a/cmake/llvm_setup.cmake b/cmake/llvm_setup.cmake index f2aae0a6..cd522a21 100644 --- a/cmake/llvm_setup.cmake +++ b/cmake/llvm_setup.cmake @@ -146,7 +146,7 @@ function(install_prebuilt_llvm LLVM_VERSION) # 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/llvm-binary/releases/download/${LLVM_VERSION}/${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 diff --git a/cmake/package.cmake b/cmake/package.cmake index e0078e30..3df35f47 100644 --- a/cmake/package.cmake +++ b/cmake/package.cmake @@ -13,7 +13,6 @@ endif() # set llvm include and lib path add_library(llvm-libs INTERFACE IMPORTED) -message(STATUS "LLVM include path: ${LLVM_INSTALL_PATH}/include") # add to include directories target_include_directories(llvm-libs INTERFACE "${LLVM_INSTALL_PATH}/include") @@ -27,11 +26,9 @@ if(CMAKE_BUILD_TYPE STREQUAL "Debug") clangAST clangASTMatchers clangBasic - clangDependencyScanning clangDriver clangFormat clangFrontend - clangIndex clangLex clangSema clangSerialization @@ -70,11 +67,10 @@ if(CMAKE_BUILD_TYPE STREQUAL "Debug") 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_compile_definitions(llvm-libs INTERFACE "CLANG_BUILD_STATIC") target_link_libraries(llvm-libs INTERFACE version ntdll) endif() diff --git a/config/prebuilt-llvm.json b/config/prebuilt-llvm.json index 41981e58..57bf72ba 100644 --- a/config/prebuilt-llvm.json +++ b/config/prebuilt-llvm.json @@ -5,7 +5,7 @@ "is_lto": false, "filename": "x64-windows-msvc-releasedbg.7z", "version": "21.1.4", - "sha256": "841aab279cae175b18992b50104166e9f5f3735162907accd9217ebb26d1ecaf" + "sha256": "02634ff12194994d93e9c76902866c03516c836ab7b55952933fd9ebcf039664" }, { "platform": "Windows", @@ -13,7 +13,7 @@ "is_lto": true, "filename": "x64-windows-msvc-release-lto.7z", "version": "21.1.4", - "sha256": "d39c8975638ac34dfd514286d5535872f18dcad98351cc72b941337d6daa7ca0" + "sha256": "7792cfd1e2d9240b49e3db81a6a04f33cbc44afa91e9a637c0490c28d4eee95c" }, { "platform": "Linux", @@ -21,7 +21,7 @@ "is_lto": false, "filename": "x86_64-linux-gnu-debug.tar.xz", "version": "21.1.4", - "sha256": "4aaa9ad0f389386d5ba78480185e787f8b643f880e07df854da26367fcd1cccc" + "sha256": "9c7b98e198ce1c5611e153c3602fb1dc03912f230bded99baaaa56ac1ea21cb4" }, { "platform": "Linux", @@ -29,7 +29,7 @@ "is_lto": false, "filename": "x86_64-linux-gnu-releasedbg.tar.xz", "version": "21.1.4", - "sha256": "f2cd5c0482e18e0593a8309f630cd261e1d8e19f0b37680b3b6b94292f3f502c" + "sha256": "99269acd2d9c5debf30062bf57d7bfd41154f2d0baee94abdb56b421c8e5b92c" }, { "platform": "Linux", @@ -37,7 +37,7 @@ "is_lto": true, "filename": "x86_64-linux-gnu-release-lto.tar.xz", "version": "21.1.4", - "sha256": "902d0fbdebcd022914106a5ae1af05d16177f41c8de34b0701eac070d5e0b364" + "sha256": "e5a6c567e30cbe51e4b98151f52071d0013839e7b5eabe7a2a2767c8234d06b2" }, { "platform": "macosx", @@ -53,14 +53,14 @@ "is_lto": false, "filename": "arm64-macosx-apple-releasedbg.tar.xz", "version": "21.1.4", - "sha256": "993cc2000b3ce008f47e8dd2fc0f4679149d9696dbe197ebc8ed1f3f7306b54b" + "sha256": "dc308b0057f472e82c2eb14b300db6ee58fb4160f3428948b5a9dcae931a3378" }, { "platform": "macosx", "build_type": "Release", "is_lto": true, "filename": "arm64-macosx-apple-release-lto.tar.xz", - "version": "20.1.5", - "sha256": "57a58adcc0a033acd66dbf8ed1f6bcf4f334074149e37bf803fc6bf022d419d2" + "version": "21.1.4", + "sha256": "c92d0323ff83e678fec7496c8b024a64b1731c3840752be9a4ade3b99dfd52ff" } ] diff --git a/docs/en/dev/build.md b/docs/en/dev/build.md index 2f1c11e6..5853464d 100644 --- a/docs/en/dev/build.md +++ b/docs/en/dev/build.md @@ -6,165 +6,77 @@ - Linux - MacOS -## Prerequisites +## Prerequisite -This section introduces the prerequisites for compiling clice. - -### Toolchain - -- clang >= 19 -- c++23 compatible standard library +- cmake/xmake +- clang, lld >= 20 +- c++23 **compatible** standard library - MSVC STL >= 19.44(VS 2022 17.4) - GCC libstdc++ >= 14 - Clang libc++ >= 20 -clice uses C++23 as the language standard. Please ensure you have an available clang 19 or above compiler, as well as a standard library compatible with C++23. +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). -> clice can currently only be compiled with clang. In the future, we will improve this to allow compilation with gcc and msvc. +> 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. -### LLVM Libs +## CMake -- 20.1.5 <= llvm libs < 21 +Use the following commands to build clice -Due to the complexity of C++ syntax, writing a new parser from scratch is unrealistic. clice calls clang's API to parse C++ source files and obtain AST, which means it needs to link llvm/clang libs. Additionally, since clice uses clang's private headers, these private headers are not available in llvm's binary release, so you cannot directly use the system's llvm package. +```shell +cmake -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ +cmake --build build +``` -If you can find the llvm commit corresponding to your system's llvm package, copy the following three files from that commit: +Optional build options: -- `clang/lib/Sema/CoroutineStmtBuilder.h` -- `clang/lib/Sema/TypeLocBuilder.h` -- `clang/lib/Sema/TreeTransform.h` +| 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. | -Copy them to `LLVM_INSTALL_PATH/include/clang/Sema/`. +## XMake -Besides this method, there are two other ways to obtain the llvm libs required by clice: - -1. Use our precompiled version +Use the following commands to build clice ```bash -# .github/workflows/cmake.yml - -# Linux precompiled binary require glibc 2.35 (build on ubuntu 22.04) -$ mkdir -p ./.llvm -$ curl -L "https://github.com/clice-io/llvm-binary/releases/download/20.1.5/x86_64-linux-gnu-release.tar.xz" | tar -xJ -C ./.llvm - -# MacOS precompiled binary require macos15+ -$ mkdir -p ./.llvm -$ curl -L "https://github.com/clice-io/llvm-binary/releases/download/20.1.5/arm64-macosx-apple-release.tar.xz" | tar -xJ -C ./.llvm - -# Windows precompiled binary only MD runtime support -$ curl -O -L "https://github.com/clice-io/llvm-binary/releases/download/20.1.5/x64-windows-msvc-release.7z" -$ 7z x x64-windows-msvc-release.7z "-o.llvm" +xmake f -c --mode=releasedbg --toolchain=clang +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` | + +## A Note on LLVM Libs + +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. + +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. + > [!IMPORTANT] > -> For debug versions of llvm libs, we enabled address sanitizer during build, and address sanitizer depends on compiler rt, which is very sensitive to compiler versions. So if using debug versions, please ensure your clang's compiler rt version is **strictly consistent** with what we used during build. +> For debug builds of llvm libs, we enable address sanitizer. Address sanitizer depends on compiler-rt, which is highly sensitive to the compiler version. > -> - Windows currently has no debug build of llvm libs because it doesn't support building clang as a dynamic library. Related progress can be found [here](https://discourse.llvm.org/t/llvm-is-buildable-as-a-windows-dll/87748) +> 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, definitely don't use apple clang +> - 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. -2. Compile llvm/clang from scratch - -This is the most recommended approach, ensuring environment consistency and avoiding crash issues caused by ABI inconsistencies. We provide a script for building the llvm libs required by clice: [build-llvm-libs.py](https://github.com/clice-io/clice/blob/main/scripts/build-llvm-libs.py). +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 /scripts/build-llvm-libs.py debug +cd llvm-project +python3 /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). - -### GCC Toolchain - -clice requires GCC libstdc++ >= 14. You could use a different GCC toolchain and also link statically against its libstdc++: - -```bash -cmake .. -DCMAKE_C_FLAGS="--gcc-toolchain=/usr/local/gcc-14.3.0/" \ - -DCMAKE_CXX_FLAGS="--gcc-toolchain=/usr/local/gcc-14.3.0/" \ - -DCMAKE_EXE_LINKER_FLAGS="-static-libgcc -static-libstdc++" -``` - -## Building - -After handling the prerequisites, you can start building clice. We provide two build methods: cmake/xmake. - -### CMake - -Below are the cmake parameters supported by clice: - -- `LLVM_INSTALL_PATH` specifies the installation path of llvm libs -- `CLICE_ENABLE_TEST` whether to build clice's unit tests - -For example: - -```bash -$ cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DLLVM_INSTALL_PATH="./.llvm" -DCLICE_ENABLE_TEST=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -$ cmake --build build -``` - -### Xmake - -Use the following command to build clice: - -```bash -$ xmake f -c --dev=true --mode=debug --toolchain=clang --llvm="./.llvm" --enable_test=true -$ xmake build --all -``` - -> --llvm is optional. If not specified, xmake will automatically download our precompiled binary - -## Building Docker Image - -Use the following command to build docker image: - -```bash -$ docker build -t clice . -``` - -Run docker image by running the following command: - -```bash -$ docker run --rm -it clice --help -OVERVIEW: clice is a new generation of language server for C/C++ -... -``` - -The directory structure of the docker image is as follows: - -``` -/opt/clice -├── bin -│ ├── clice -> /usr/local/bin/clice -├── include -├── lib -├── LICENSE -├── README.md -``` - -Hint: launch clice in the docker container by running the following command: - -```bash -$ docker run --rm -it --entrypoint bash clice -``` - -## Development Container - -We provide Docker images as a pre-configured environment to streamline the setup process. You can use the following scripts to manage the development container. These scripts can be run from the project root directory. - -```bash -# Build the development image -./docker/linux/build.sh - -# Run the container with the clang toolchain -./docker/linux/run.sh --compiler clang - -# Run the container with the gcc toolchain -./docker/linux/run.sh --compiler gcc - -# Reset the container (stops and removes the existing one) -./docker/linux/run.sh --reset -``` - -> [!NOTE] -> This feature is currently in a preview stage and only supports Linux. Windows support will be provided in the future, and the functionality may be subject to change. +You can also refer to LLVM's official build tutorial: [Building LLVM with CMake](https://llvm.org/docs/CMake.html). diff --git a/docs/zh/dev/build.md b/docs/zh/dev/build.md index bdaf48d9..f1ff411a 100644 --- a/docs/zh/dev/build.md +++ b/docs/zh/dev/build.md @@ -8,159 +8,74 @@ ## Prerequisite -本小节介绍编译 clice 的前置依赖。 - -### Toolchain - -- clang >= 19 -- c++23 compitable standard library +- cmake/xmake +- clang, lld >= 20 +- c++23 **compatible** standard library - MSVC STL >= 19.44(VS 2022 17.4) - GCC libstdc++ >= 14 - Clang libc++ >= 20 -clice 使用 C++23 作为语言标准 ,请确保有可用的 clang 19 以及以上的编译器,以及兼容 C++23 的标准库。 +clice 使用 C++23 作为语言标准,请确保有可用的 clang 20 以及以上的编译器,以及兼容 C++23 的标准库。clice 依赖 lld 作为链接器。请确保你的 clang 工具链可以找到它(通常 clang 发行版会自带 lld,或者你需要单独安装 lld-20 包)。 -> clice 暂时只能使用 clang 编译,在未来我们会改进这一点,使其能使用 gcc 和 msvc 编译。 +> clice 目前只保证能使用 clang 编译(CI 测试保证)。对于 gcc 和 msvc 的兼容,我们尽力而为,但不会在 CI 中添加对应的测试。如果遇到任何问题,欢迎贡献。 -### LLVM Libs +## CMake -- 20.1.5 <= llvm libs < 21 +使用如下的命令构建 clice -由于 C++ 的语法太过复杂,自己编写一个新的 parser 是不现实的。clice 调用 clang 的 API 来 parse C++ 源文件获取 AST,这意味它需要链接 llvm/clang libs。另外由于 clice 使用了 clang 的私有头文件,这些私有头文件在 llvm 发布的 binary release 中是没有的,所以不能直接使用系统的 llvm package。 +```shell +cmake -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ +cmake --build build +``` -如果你能找到系统的 llvm package 对应的 llvm commit,将该 commit 下的如下三个文件 +可选的构建选项: -- `clang/lib/Sema/CoroutineStmtBuilder.h` -- `clang/lib/Sema/TypeLocBuilder.h` -- `clang/lib/Sema/TreeTransform.h` +| 选项 | 默认值 | 效果 | +| -------------------- | ------ | ----------------------------------------------------------------------------------------------------- | +| 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/include/clang/Sema/` 中即可。 +## XMake -除了这种方法以外,还有两种办法获取 clice 所需的 llvm libs: - -1. 使用我们提供的预编译版本 +使用如下的命令即可构建 clice ```bash -# .github/workflows/cmake.yml - -# Linux precompiled binary require glibc 2.35 (build on ubuntu 22.04) -$ mkdir -p ./.llvm -$ curl -L "https://github.com/clice-io/llvm-binary/releases/download/20.1.5/x86_64-linux-gnu-release.tar.xz" | tar -xJ -C ./.llvm - -# MacOS precompiled binary require macos15+ -$ mkdir -p ./.llvm -$ curl -L "https://github.com/clice-io/llvm-binary/releases/download/20.1.5/arm64-macosx-apple-release.tar.xz" | tar -xJ -C ./.llvm - -# Windows precompiled binary only MD runtime support -$ curl -O -L "https://github.com/clice-io/llvm-binary/releases/download/20.1.5/x64-windows-msvc-release.7z" -$ 7z x x64-windows-msvc-release.7z "-o.llvm" +xmake f -c --mode=releasedbg --toolchain=clang +xmake build --all ``` +可选的构建选项: + +| 选项 | 默认值 | 效果 | +| ------------- | ------ | --------------------------------------- | +| --llvm | "" | 使用自定义路径的 llvm libs 来构建 clice | +| --enable_test | false | 是否构建 clice 的单元测试 | +| --ci | false | 是否打开 `CLICE_CI_ENVIRONMENT` | + + +## A Note on LLVM Libs + +由于 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 然后使用, + > [!IMPORTANT] > > 对于 debug 版本的 llvm libs,构建的时候我们开启了 address sanitizer,而 address sanitizer 依赖于 compiler rt,它对编译器版本十分敏感。所以如果使用 debug 版本,请确保你的 clang 的 compiler rt 版本和我们构建的时候**严格一致**。 > -> - Windows 暂时无 debug 构建的 llvm libs,因为它不支持将 clang 构建为动态库,相关的进展可以在 [这里](https://discourse.llvm.org/t/llvm-is-buildable-as-a-windows-dll/87748) 找到 +> - Windows 暂时没有 debug 构建的 llvm libs,因为它不支持将 clang 构建为动态库,相关的进展在 [这里](https://github.com/clice-io/clice/issues/42) 跟踪 > - Linux 使用 clang20 -> - MacOS 使用 homebrew llvm@20,一定不要使用 apple clang +> - 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 的环境保持严格一致。 -2. 自己从头编译 llvm/clang - -这是最推荐的方式,可以保证环境一致性,避免因为 ABI 不一致而导致的崩溃问题。我们提供了一个脚本,用于构建 clice 所需要的 llvm libs:[build-llvm-libs.py](https://github.com/clice-io/clice/blob/main/scripts/build-llvm-libs.py)。 +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 /scripts/build-llvm-libs.py debug +cd llvm-project +python3 /scripts/build-llvm-libs.py debug ``` 也可以参考 llvm 的官方构建教程 [Building LLVM with CMake](https://llvm.org/docs/CMake.html)。 - -### GCC Toolchain - -clice 要求 GCC libstdc++ >= 14。以下命令使用不同的 GCC 工具链并静态链接其 libstdc++: - -```bash -cmake .. -DCMAKE_C_FLAGS="--gcc-toolchain=/usr/local/gcc-14.3.0/" \ - -DCMAKE_CXX_FLAGS="--gcc-toolchain=/usr/local/gcc-14.3.0/" \ - -DCMAKE_EXE_LINKER_FLAGS="-static-libgcc -static-libstdc++" -``` - -## Building - -在处理好前置依赖之后,可以开始构建 clice 了,我们提供 cmake/xmake 两种构建方式。 - -### CMake - -下面是 clice 支持的 cmake 参数 - -- `LLVM_INSTALL_PATH` 用于指定 llvm libs 的安装路径 -- `CLICE_ENABLE_TEST` 是否构建 clice 的单元测试 - -例如 - -```bach -$ cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DLLVM_INSTALL_PATH="./.llvm" -DCLICE_ENABLE_TEST=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -$ cmake --build build -``` - -### Xmake - -使用如下的命令即可构建 clice - -```bash -$ xmake f -c --dev=true --mode=debug --toolchain=clang --llvm="./.llvm" --enable_test=true -$ xmake build --all -``` - -> --llvm 是可选的,如果不指定的话,xmake 会自动下载我们编译好的预编译二进制 - -## Dev Container - -我们提供了 docker 镜像作为预装环境解决方案,可以有效地解决环境配置问题,可通过下列命令使用(不限脚本调用路径,可以直接运行 ./build.sh): - -```bash -# construct container -docker/linux/build.sh -# run clang container -docker/linux/run.sh --compiler clang -# run gcc container -docker/linux/run.sh --compiler gcc -# reset container(delete exist container and reset) -docker/linux/run.sh --reset -``` - -> [!NOTE] -> 当前该功能仍处于 Preview 阶段,仅支持 Linux,后续会提供 Windows 平台版本,并可能存在功能改动 - -## Building Docker Image - -使用以下命令构建 docker 镜像: - -```bash -$ docker build -t clice . -``` - -运行 docker 镜像: - -```bash -$ docker run --rm -it clice --help -OVERVIEW: clice is a new generation of language server for C/C++ -... -``` - -docker 镜像的目录结构如下: - -``` -/opt/clice -├── bin -│ ├── clice -> /usr/local/bin/clice -├── include -├── lib -├── LICENSE -├── README.md -``` - -提示:可以使用以下命令进入 clice 容器: - -```bash -$ docker run --rm -it --entrypoint bash clice diff --git a/xmake.lua b/xmake.lua index a0ecbd37..34088130 100644 --- a/xmake.lua +++ b/xmake.lua @@ -16,7 +16,7 @@ if has_config("dev") then set_runtimes("MD") if is_mode("debug") then print("clice does not support build in debug mode with pre-compiled llvm binary on windows.\n" - .."See https://github.com/clice-io/clice/issues/42 for more information.") + .. "See https://github.com/clice-io/clice/issues/42 for more information.") os.raise() end elseif is_mode("debug") and is_plat("linux", "macosx") then @@ -40,7 +40,7 @@ if has_config("release") then end add_defines("TOML_EXCEPTIONS=0") -add_requires("spdlog", {system=false, version="1.15.3", configs = {header_only = false, std_format = true, noexcept = true}}) +add_requires("spdlog", {system = false, version = "1.15.3", configs = {header_only = false, std_format = true, noexcept = true}}) add_requires(libuv_require, "toml++", "croaring", "flatbuffers") add_requires("clice-llvm", {alias = "llvm"}) @@ -68,11 +68,9 @@ target("clice-core") "clangAST", "clangASTMatchers", "clangBasic", - "clangDependencyScanning", "clangDriver", "clangFormat", "clangFrontend", - "clangIndex", "clangLex", "clangSema", "clangSerialization", @@ -107,14 +105,14 @@ target("clice-core") "clangToolingInclusions", "clangToolingInclusionsStdlib", "clangToolingSyntax", - }}) - on_config(function (target) + } + }) + on_config(function(target) local llvm_dynlib_dir = path.join(target:pkg("llvm"):installdir(), "lib") target:add("rpathdirs", llvm_dynlib_dir) end) elseif is_mode("release", "releasedbg") then add_packages("llvm", {public = true}) - add_ldflags("-Wl,--gc-sections") end target("clice") @@ -123,12 +121,12 @@ target("clice") add_deps("clice-core") - on_config(function (target) + on_config(function(target) local llvm_dir = target:dep("clice-core"):pkg("llvm"):installdir() target:add("installfiles", path.join(llvm_dir, "lib/clang/(**)"), {prefixdir = "lib/clang"}) end) - after_build(function (target) + after_build(function(target) local res_dir = path.join(target:targetdir(), "../lib/clang") if not os.exists(res_dir) then local llvm_dir = target:dep("clice-core"):pkg("llvm"):installdir() @@ -146,7 +144,7 @@ target("unit_tests") add_tests("default") - after_load(function (target) + after_load(function(target) target:set("runargs", "--test-dir=" .. path.absolute("tests/data") ) @@ -161,7 +159,7 @@ target("integration_tests") add_tests("default") - on_test(function (target, opt) + on_test(function(target, opt) import("lib.detect.find_tool") local uv = assert(find_tool("uv"), "uv not found!") @@ -178,7 +176,7 @@ target("integration_tests") end) rule("clice_clang_tidy_config") - on_load(function (target) + on_load(function(target) import("core.project.depend") local autogendir = path.join(target:autogendir(), "rules/clice_clang_tidy_config") @@ -195,15 +193,10 @@ rule("clice_clang_tidy_config") end) rule("clice_build_config") - on_load(function (target) - target:add("cxflags", "-fno-rtti", {tools = {"clang", "clangxx", "gcc", "gxx"}}) - target:add("cxflags", "/GR-", {tools = {"clang_cl", "cl"}}) - -- Fix MSVC Non-standard preprocessor caused error C1189 - -- While compiling Command.cpp, MSVC won't expand Options macro correctly - -- Output: D:\Desktop\code\clice\build\.packages\l\llvm\20.1.5\cc2aa9f1d09a4b71b6fa3bf0011f6387\include\clang/Driver/Options.inc(3590): error C2365: “clang::driver::options::OPT_”: redefinition; previous definition was 'enumerator' - target:add("cxflags", "cl::/Zc:preprocessor") - + on_load(function(target) target:set("exceptions", "no-cxx") + target:add("cxflags", "-fno-rtti", "-Wno-undefined-inline", {tools = {"clang", "clangxx", "gcc", "gxx"}}) + target:add("cxflags", "/GR-", "/Zc:preprocessor", {tools = {"clang_cl", "cl"}}) if target:is_plat("windows") and not target:toolchain("msvc") then target:set("toolset", "ar", "llvm-ar") @@ -214,9 +207,11 @@ rule("clice_build_config") target:add("ldflags", "-fuse-ld=lld-link") end elseif target:is_plat("linux") then - -- gnu ld need to fix link order - target:add("ldflags", "-fuse-ld=lld") + target:add("ldflags", "-fuse-ld=lld", "-Wl,--gc-sections") + elseif target:is_plat("macosx") then + target:add("ldflags", "-fuse-ld=lld", "-Wl,-dead_strip") end + if has_config("ci") then target:add("cxxflags", "-DCLICE_CI_ENVIRONMENT") end @@ -225,7 +220,7 @@ rule("clice_build_config") rule("flatbuffers.schema.gen") set_extensions(".fbs") - on_prepare_files(function (target, jobgraph, sourcebatch, opt) + on_prepare_files(function(target, jobgraph, sourcebatch, opt) import("lib.detect.find_tool") import("core.project.depend") import("utils.progress") @@ -242,7 +237,7 @@ rule("flatbuffers.schema.gen") local generate_dir = path.normalize(path.join(autogendir, path.directory(sourcefile))) target:add("includedirs", generate_dir, {public = true}) os.mkdir(generate_dir) - jobgraph:add(job, function (index, total, opt) + jobgraph:add(job, function(index, total, opt) local argv = { "--cpp", "-o", generate_dir, @@ -266,16 +261,17 @@ package("clice-llvm") if has_config("llvm") then set_sourcedir(get_config("llvm")) else - on_source(function (package) + on_source(function(package) import("core.base.json") local info = json.loadfile("./config/prebuilt-llvm.json") for _, info in ipairs(info) do if info.platform:lower() == get_config("plat") - and (info.build_type:lower() == get_config("mode") - or info.build_type:lower() == "release" and get_config("mode") == "releasedbg") - and (info.is_lto == has_config("release")) then - package:add("urls", format("https://github.com/clice-io/llvm-binary/releases/download/%s/%s", info.version, info.filename)) + and (info.build_type:lower() == get_config("mode") + or info.build_type:lower() == "release" and get_config("mode") == "releasedbg") + and (info.is_lto == has_config("release")) + then + package:add("urls", format("https://github.com/clice-io/clice-llvm/releases/download/%s/%s", info.version, info.filename)) package:add("versions", info.version, info.sha256) end end @@ -292,7 +288,7 @@ package("clice-llvm") add_syslinks("version", "ntdll") end - on_install(function (package) + on_install(function(package) if not package:config("shared") then package:add("defines", "CLANG_BUILD_STATIC") end @@ -317,7 +313,7 @@ if has_config("release") then add_targets("clice") add_installfiles(path.join(os.projectdir(), "docs/clice.toml")) - on_load(function (package) + on_load(function(package) local llvm_dir = package:target("clice"):dep("clice-core"):pkg("llvm"):installdir() package:add("installfiles", path.join(llvm_dir, "lib/clang/(**)"), {prefixdir = "lib/clang"}) end)