cmake_minimum_required(VERSION 3.20) project(ksOSKernel LANGUAGES ASM C) set(KERNEL_MODULE_NAME "Kernel") # Where to emit compile_commands.json for SourceKit-LSP. Empty: Kernel/ # (ancestor of SWIFT_SOURCES). Override: -DKERNEL_COMPILE_COMMANDS_DIR=/path # or env KERNEL_COMPILE_COMMANDS_DIR. set(KERNEL_COMPILE_COMMANDS_DIR "" CACHE PATH "Directory for Swift compile_commands.json (SourceKit-LSP)") # --- Locate Swift toolchain with Embedded Swift stdlib --- # Priority: cmake var > env var > auto-detect if(NOT SWIFT_TOOLCHAIN AND DEFINED ENV{SWIFT_TOOLCHAIN}) set(SWIFT_TOOLCHAIN "$ENV{SWIFT_TOOLCHAIN}") endif() if(SWIFT_TOOLCHAIN) set(SWIFTC "${SWIFT_TOOLCHAIN}/usr/bin/swiftc") set(SWIFT_RESOURCE_DIR "${SWIFT_TOOLCHAIN}/usr/lib/swift") elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin") # Scan for a swift.org toolchain that ships the embedded stdlib. # APPLE is false here (target is Generic), so we check the HOST OS. file(GLOB _tc_candidates "$ENV{HOME}/Library/Developer/Toolchains/*.xctoolchain" "/Library/Developer/Toolchains/*.xctoolchain" ) foreach(_tc ${_tc_candidates}) if(EXISTS "${_tc}/usr/lib/swift/embedded") set(SWIFTC "${_tc}/usr/bin/swiftc") set(SWIFT_RESOURCE_DIR "${_tc}/usr/lib/swift") break() endif() endforeach() else() # Linux: find swiftc in PATH find_program(_SWIFTC_EXE swiftc) if(_SWIFTC_EXE) get_filename_component(_SWIFTC_BIN "${_SWIFTC_EXE}" DIRECTORY) get_filename_component(_SWIFTC_USR "${_SWIFTC_BIN}" DIRECTORY) if(EXISTS "${_SWIFTC_USR}/lib/swift/embedded") set(SWIFTC "${_SWIFTC_EXE}") set(SWIFT_RESOURCE_DIR "${_SWIFTC_USR}/lib/swift") elseif(EXISTS "${_SWIFTC_USR}/lib/swift/lib/swift/embedded") set(SWIFTC "${_SWIFTC_EXE}") set(SWIFT_RESOURCE_DIR "${_SWIFTC_USR}/lib/swift/lib/swift") endif() endif() endif() if(NOT SWIFTC OR NOT EXISTS "${SWIFT_RESOURCE_DIR}/embedded") message(FATAL_ERROR "Swift toolchain with Embedded Swift not found.\n" "Install a swift.org toolchain and pass -DSWIFT_TOOLCHAIN= " "or set the SWIFT_TOOLCHAIN env var.") endif() message(STATUS "Swift: ${SWIFTC}") message(STATUS "Swift resource dir: ${SWIFT_RESOURCE_DIR}") # Hint for SourceKit: VSCode must use this toolchain (same as compile_commands), # not Xcode's default; otherwise: "Loading the standard library failed" with Embedded. get_filename_component(_SWIFT_IDE_PATH "${SWIFTC}" DIRECTORY) set(_ide_hint "# If SourceKit reports \"Loading the standard library failed\", set the Swift # extension's swift.path to the directory on the line below and restart # \"Swift: Restart SourceKit LSP\". (Embedded Swift — must match this toolchain, not Xcode.) # Regenerated on each CMake configure. ") string(APPEND _ide_hint "${_SWIFT_IDE_PATH}\n") file(WRITE "${CMAKE_CURRENT_SOURCE_DIR}/../ide-swift-toolchain.txt" "${_ide_hint}") # --- Build --- add_compile_options(-ffreestanding -nostdlib -O0 -g) set(LINKER_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/linker.ld) add_link_options( -fuse-ld=lld -nostdlib -static -Wl,-T,${LINKER_SCRIPT} ) set(SWIFT_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/Source/Kernel.swift ${CMAKE_CURRENT_SOURCE_DIR}/Source/Arch/DTB.swift ${CMAKE_CURRENT_SOURCE_DIR}/Source/IO/UART.swift ${CMAKE_CURRENT_SOURCE_DIR}/Source/Arch/ExceptionContext.swift ${CMAKE_CURRENT_SOURCE_DIR}/Source/OS/panic.swift # idfk why but sourcekit-lsp dont see Panic.swift but see panic.swift perfectly ) set(SWIFT_OBJ ${CMAKE_CURRENT_BINARY_DIR}/kernel_swift.o) add_custom_command( OUTPUT ${SWIFT_OBJ} COMMAND ${SWIFTC} -target aarch64-none-none-elf -enable-experimental-feature Embedded -parse-as-library -wmo -O -Xcc -fno-stack-protector -Xcc -I${CMAKE_CURRENT_SOURCE_DIR}/../Common -import-bridging-header ${CMAKE_CURRENT_SOURCE_DIR}/Source/Support/BridgingHeader.h -resource-dir ${SWIFT_RESOURCE_DIR} -c ${SWIFT_SOURCES} -o ${SWIFT_OBJ} COMMAND ${LLVM_OBJCOPY} --remove-section=.swift_modhash ${SWIFT_OBJ} DEPENDS ${SWIFT_SOURCES} COMMENT "Compiling Swift kernel" ) set_source_files_properties(${SWIFT_OBJ} PROPERTIES EXTERNAL_OBJECT TRUE GENERATED TRUE ) add_executable(kernel.elf Source/Arch/entry.S Source/Support/stubs.c Source/Arch/vectors.S ${SWIFT_OBJ}) add_custom_command(TARGET kernel.elf POST_BUILD COMMAND ${LLVM_OBJCOPY} -O binary kernel.elf kernel.bin ) # --- SourceKit-LSP: generate compile_commands.json for Swift (Dynamic) --- set(_BRIDGING_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/Source/Support/BridgingHeader.h") set(SWIFT_ARGS "\"${SWIFTC}\"" "\"-target\"" "\"aarch64-none-none-elf\"" "\"-enable-experimental-feature\"" "\"Embedded\"" "\"-module-name\"" "\"${KERNEL_MODULE_NAME}\"" "\"-parse-as-library\"" "\"-wmo\"" "\"-import-bridging-header\"" "\"${_BRIDGING_HEADER}\"" "\"-Xcc\"" "\"-I${CMAKE_CURRENT_SOURCE_DIR}/../Common\"" "\"-Xcc\"" "\"-fno-stack-protector\"" "\"-resource-dir\"" "\"${SWIFT_RESOURCE_DIR}\"" ) foreach(_src IN LISTS SWIFT_SOURCES) list(APPEND SWIFT_ARGS "\"${_src}\"") endforeach() string(JOIN ", " SWIFT_ARGS_JSON ${SWIFT_ARGS}) set(COMPDB_ENTRIES "") list(LENGTH SWIFT_SOURCES _src_count) math(EXPR _last_idx "${_src_count} - 1") set(_idx 0) foreach(_src IN LISTS SWIFT_SOURCES) set(_entry " {\n \"file\": \"${_src}\",\n \"directory\": \"${CMAKE_CURRENT_BINARY_DIR}\",\n \"arguments\": [${SWIFT_ARGS_JSON}]\n }") if(_idx LESS _last_idx) string(APPEND _entry ",\n") else() string(APPEND _entry "\n") endif() string(APPEND COMPDB_ENTRIES "${_entry}") math(EXPR _idx "${_idx} + 1") endforeach() # Precedence: CMake -D > env > Kernel/ (ancestor of all SWIFT_SOURCES). if(NOT KERNEL_COMPILE_COMMANDS_DIR STREQUAL "") set(COMPDB_OUTPUT_DIR "${KERNEL_COMPILE_COMMANDS_DIR}") elseif(DEFINED ENV{KERNEL_COMPILE_COMMANDS_DIR} AND NOT "$ENV{KERNEL_COMPILE_COMMANDS_DIR}" STREQUAL "") set(COMPDB_OUTPUT_DIR "$ENV{KERNEL_COMPILE_COMMANDS_DIR}") else() set(COMPDB_OUTPUT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") endif() # Same JSON twice: (1) optional Kernel/ or KERNEL_COMPILE_COMMANDS_DIR, (2) repo # root. SourceKit/Cursor/VSCode Swift extension resolve compile_commands at the # workspace root first; a DB only under Kernel/ often yields no language service # and broken cross-file resolution (WMO) even though the build works. set(KERNEL_COMPDB_CONTENT "[\n${COMPDB_ENTRIES}]\n") file(GENERATE OUTPUT "${COMPDB_OUTPUT_DIR}/compile_commands.json" CONTENT "${KERNEL_COMPDB_CONTENT}" ) set(_REPO_COMPDB "${CMAKE_CURRENT_SOURCE_DIR}/../compile_commands.json") file(GENERATE OUTPUT "${_REPO_COMPDB}" CONTENT "${KERNEL_COMPDB_CONTENT}" )