cmake_minimum_required(VERSION 3.20)
project(no_core_write LANGUAGES NONE)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

find_program(RUSTC rustc REQUIRED)

set(RUST_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/main.rs")
set(RUST_BINARY "${CMAKE_CURRENT_BINARY_DIR}/no_core_write${CMAKE_EXECUTABLE_SUFFIX}")

# no_core is an unstable rustc feature. RUSTC_BOOTSTRAP lets this deliberately
# minimal example be built with the installed compiler without Cargo or core.
set(RUSTC_ENV "RUSTC_BOOTSTRAP=1")
set(RUSTC_FLAGS -C panic=abort)

# CMAKE_BUILD_TYPE does not automatically affect a custom rustc command.
# Pass the corresponding optimization/debug-info settings explicitly.
if (CMAKE_BUILD_TYPE STREQUAL "Release")
    list(APPEND RUSTC_FLAGS -C opt-level=3 -C debuginfo=0)
elseif (CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
    list(APPEND RUSTC_FLAGS -C opt-level=3 -C debuginfo=2)
else()
    list(APPEND RUSTC_FLAGS -C opt-level=0 -C debuginfo=2)
endif()

# A no_core binary does not get Rust's normal startup/linker setup. Link the
# small platform system/runtime library needed by the stdout implementation.
if (WIN32)
    # Use Win32 directly instead of the C runtime; this keeps the executable
    # small while still writing to the console's stdout handle.
    set(RUSTC_LINK_ARGS
        -l kernel32
        -C
        "link-args=/NODEFAULTLIB /ENTRY:mainCRTStartup /SUBSYSTEM:CONSOLE /OPT:REF /OPT:ICF"
    )
elseif (UNIX)
    set(RUSTC_LINK_ARGS -C "link-args=-lc")
endif()

add_custom_command(
    OUTPUT "${RUST_BINARY}"
    COMMAND "${CMAKE_COMMAND}" -E env "${RUSTC_ENV}" "${RUSTC}"
            "${RUST_SOURCE}"
            --crate-name no_core_write
            --crate-type bin
            -o "${RUST_BINARY}"
            ${RUSTC_FLAGS}
            ${RUSTC_LINK_ARGS}
    DEPENDS "${RUST_SOURCE}"
    VERBATIM
)

add_custom_target(no_core_write ALL DEPENDS "${RUST_BINARY}")
