Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
cmake_minimum_required(VERSION 3.30)

project(CLICE_PROJECT LANGUAGES C CXX)
# Base version; the released version string comes from git describe (see
# cmake/generate_version.cmake) and this value is the tarball fallback.
project(CLICE_PROJECT VERSION 0.1.0 LANGUAGES C CXX)

set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
Expand Down Expand Up @@ -143,10 +145,24 @@ add_custom_command(

add_custom_target(generate_flatbuffers_schema DEPENDS "${GENERATED_HEADER}")

# Version header, regenerated on every build so the embedded git describe
# tracks the checked-out commit (content-unchanged writes are elided).
set(CLICE_VERSION_HEADER "${PROJECT_BINARY_DIR}/generated/version.h")
add_custom_target(generate_version_header
COMMAND ${CMAKE_COMMAND}
-DSOURCE_DIR=${PROJECT_SOURCE_DIR}
-DFALLBACK_VERSION=${PROJECT_VERSION}
-DTEMPLATE=${PROJECT_SOURCE_DIR}/cmake/version.h.in
-DOUTPUT_FILE=${CLICE_VERSION_HEADER}
-P "${PROJECT_SOURCE_DIR}/cmake/generate_version.cmake"
BYPRODUCTS "${CLICE_VERSION_HEADER}"
COMMENT "Generating version header"
)

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)
add_dependencies(clice-core generate_flatbuffers_schema generate_version_header)

target_include_directories(clice-core PUBLIC
"${PROJECT_SOURCE_DIR}/src"
Expand All @@ -166,6 +182,7 @@ target_link_libraries(clice-core PUBLIC

add_executable(clice "${PROJECT_SOURCE_DIR}/src/clice.cc")
target_link_libraries(clice PRIVATE clice::core kota::deco)
add_dependencies(clice generate_version_header)
install(TARGETS clice RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})

add_custom_target(copy_clang_resource ALL
Expand Down Expand Up @@ -198,6 +215,10 @@ if(CLICE_ENABLE_TEST)
"${PROJECT_SOURCE_DIR}/tests/unit"
)
target_link_libraries(unit_tests PRIVATE clice::core kota::zest kota::deco)
# The generated/ include dir propagates through clice::core, but the
# generation order does not — depend explicitly so a test including
# version.h cannot race the generator on a clean parallel build.
add_dependencies(unit_tests generate_version_header)
endif()

if(CLICE_ENABLE_BENCHMARK)
Expand Down
50 changes: 50 additions & 0 deletions cmake/generate_version.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Build-time script: regenerate the version header from git describe.
#
# Runs on every build so the embedded version tracks the checked-out
# commit, not the last configure. configure_file only rewrites the output
# when the content changed, so dependents do not rebuild spuriously.
#
# Inputs: SOURCE_DIR, FALLBACK_VERSION, TEMPLATE, OUTPUT_FILE.

# Guard against git's parent-directory repository discovery: a source
# tarball unpacked inside some other checkout must take the tarball
# fallback, not describe the surrounding repository. Trust git only when
# the discovered top-level is the source directory itself.
execute_process(
COMMAND git -C "${SOURCE_DIR}" rev-parse --show-toplevel
OUTPUT_VARIABLE CLICE_GIT_TOPLEVEL
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
RESULT_VARIABLE CLICE_TOPLEVEL_RESULT
)

set(CLICE_GIT_RESULT 1)
if(CLICE_TOPLEVEL_RESULT EQUAL 0 AND NOT CLICE_GIT_TOPLEVEL STREQUAL "")
file(REAL_PATH "${CLICE_GIT_TOPLEVEL}" CLICE_GIT_TOPLEVEL)
file(REAL_PATH "${SOURCE_DIR}" CLICE_SOURCE_REAL)
if(CLICE_GIT_TOPLEVEL STREQUAL CLICE_SOURCE_REAL)
execute_process(
COMMAND git -C "${SOURCE_DIR}" describe --tags --always --dirty
OUTPUT_VARIABLE CLICE_GIT_DESCRIBE
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
RESULT_VARIABLE CLICE_GIT_RESULT
)
endif()
endif()

if(NOT CLICE_GIT_RESULT EQUAL 0 OR CLICE_GIT_DESCRIBE STREQUAL "")
# Not a git checkout of clice (e.g. a source tarball): base version only.
set(CLICE_VERSION_STRING "${FALLBACK_VERSION}")
elseif(CLICE_GIT_DESCRIBE MATCHES "^[0-9a-f]+(-dirty)?$")
# No tag reachable: describe degrades to a bare commit hash.
set(CLICE_VERSION_STRING "${FALLBACK_VERSION}+g${CLICE_GIT_DESCRIBE}")
elseif(CLICE_GIT_DESCRIBE MATCHES "^v")
# Tag-relative describe; tags are named vX.Y.Z, strip the prefix.
string(SUBSTRING "${CLICE_GIT_DESCRIBE}" 1 -1 CLICE_VERSION_STRING)
else()
# A tag without the v prefix: use the describe output verbatim.
set(CLICE_VERSION_STRING "${CLICE_GIT_DESCRIBE}")
endif()

configure_file("${TEMPLATE}" "${OUTPUT_FILE}" @ONLY)
14 changes: 14 additions & 0 deletions cmake/version.h.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#pragma once

#include <string_view>

namespace clice {

/// Version string baked at build time: `git describe` relative to the
/// latest tag when building from a repository (a tag-exact build prints
/// the tag itself; without a reachable tag the base version plus the
/// commit hash), the project's base version for non-git builds.
/// Generated from cmake/version.h.in — do not edit.
inline constexpr std::string_view version = "@CLICE_VERSION_STRING@";

} // namespace clice
3 changes: 2 additions & 1 deletion src/clice.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <sstream>
#include <string>

#include "version.h"
#include "server/transport/agentic.h"
#include "server/transport/master_server.h"
#include "server/worker/stateful_worker.h"
Expand Down Expand Up @@ -191,7 +192,7 @@ int main(int argc, const char** argv) {
});

if(!args.empty() && (args[0] == "--version" || args[0] == "-v")) {
std::println("clice version 0.1.0");
std::println("clice version {}", clice::version);
return 0;
}

Expand Down
3 changes: 2 additions & 1 deletion src/server/transport/lsp_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <type_traits>
#include <variant>

#include "version.h"
#include "command/argument_parser.h"
#include "semantic/symbol_kind.h"
#include "server/compiler/context_resolver.h"
Expand Down Expand Up @@ -173,7 +174,7 @@ void LSPClient::register_lifecycle() {

protocol::ServerInfo info;
info.name = "clice";
info.version = "0.1.0";
info.version = std::string(clice::version);
result.server_info = std::move(info);

co_return result;
Expand Down
14 changes: 12 additions & 2 deletions tests/integration/features/test_server.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Integration tests for the clice MasterServer using pygls."""

import asyncio
import subprocess

import pytest
from lsprotocol.types import (
Expand All @@ -15,9 +16,18 @@


@pytest.mark.workspace("hello_world")
async def test_server_info(client, workspace):
async def test_server_info(client, workspace, executable):
assert client.init_result.server_info.name == "clice"
assert client.init_result.server_info.version == "0.1.0"
# The version is injected at build time (git describe or the base
# version); instead of pinning a value, pin that the LSP handshake and
# the --version CLI report the same thing.
result = subprocess.run(
[str(executable), "--version"], capture_output=True, text=True, timeout=10
)
assert result.returncode == 0
cli_version = result.stdout.strip().removeprefix("clice version ")
assert cli_version and cli_version[0].isdigit()
assert client.init_result.server_info.version == cli_version


@pytest.mark.workspace("hello_world")
Expand Down