diff --git a/.gitignore b/.gitignore index b4501d4..3e1b0a5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,14 @@ build/ +build-cmake*/ *.iso *.tar limine/ + +# CMake generated files +CMakeCache.txt +CMakeFiles/ +cmake_install.cmake +compile_commands.json +CTestTestfile.cmake +Testing/ +.cmake/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..1b7012f --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,573 @@ +cmake_minimum_required(VERSION 3.20) +project(CLeonOS NONE) + +# User-configurable toolchain and tool variables (Makefile-compatible defaults). +set(NO_COLOR 0 CACHE STRING "Disable colored log output when set to 1") +include("${CMAKE_SOURCE_DIR}/cmake/log.cmake") +if(NO_COLOR) + set(BUILD_LOG_STEP_PREFIX "[STEP]") + set(BUILD_LOG_INFO_PREFIX "[INFO]") +else() + string(ASCII 27 BUILD_LOG_ESC) + set(BUILD_LOG_STEP_PREFIX "${BUILD_LOG_ESC}[1;35m[STEP]${BUILD_LOG_ESC}[0m") + set(BUILD_LOG_INFO_PREFIX "${BUILD_LOG_ESC}[1;36m[INFO]${BUILD_LOG_ESC}[0m") +endif() + +set(CC "x86_64-elf-gcc" CACHE STRING "Kernel C compiler") +set(LD "x86_64-elf-ld" CACHE STRING "Kernel linker") +set(USER_CC "cc" CACHE STRING "User-space C compiler") +set(USER_LD "ld" CACHE STRING "User-space linker") +set(RUSTC "rustc" CACHE STRING "Rust compiler") + +set(XORRISO "xorriso" CACHE STRING "xorriso executable") +set(TAR "tar" CACHE STRING "tar executable") +set(GIT_TOOL "git" CACHE STRING "git executable") +set(MAKE_TOOL "make" CACHE STRING "make executable") +set(SH_TOOL "sh" CACHE STRING "POSIX shell executable") + +set(OBJCOPY_FOR_TARGET "llvm-objcopy" CACHE STRING "objcopy tool for limine configure") +set(OBJDUMP_FOR_TARGET "llvm-objdump" CACHE STRING "objdump tool for limine configure") +set(READELF_FOR_TARGET "llvm-readelf" CACHE STRING "readelf tool for limine configure") + +set(QEMU_X86_64 "qemu-system-x86_64" CACHE STRING "QEMU executable") + +set(LIMINE_DIR "limine" CACHE STRING "Limine source directory") +set(LIMINE_REPO "https://gh-proxy.com/https://github.com/limine-bootloader/limine.git" CACHE STRING "Limine git repository") +set(LIMINE_REF "" CACHE STRING "Optional Limine branch or tag") +set(LIMINE_BIN_DIR "${LIMINE_DIR}/bin" CACHE STRING "Limine bin directory") +set(LIMINE_CONFIGURE_FLAGS "--enable-bios-cd --enable-uefi-cd --enable-uefi-x86-64" CACHE STRING "Limine configure flags") +set(LIMINE_SKIP_CONFIGURE 0 CACHE BOOL "Skip Limine configure step") + +function(resolve_path INPUT_PATH OUTPUT_VAR) + if(IS_ABSOLUTE "${INPUT_PATH}") + set(${OUTPUT_VAR} "${INPUT_PATH}" PARENT_SCOPE) + else() + set(${OUTPUT_VAR} "${CMAKE_SOURCE_DIR}/${INPUT_PATH}" PARENT_SCOPE) + endif() +endfunction() + +function(resolve_tool_with_fallback VAR_NAME) + set(_fallbacks ${ARGN}) + set(_requested "${${VAR_NAME}}") + + if("${_requested}" STREQUAL "") + message(FATAL_ERROR "[ERROR] empty tool variable: ${VAR_NAME}") + endif() + + if(IS_ABSOLUTE "${_requested}") + if(NOT EXISTS "${_requested}") + message(FATAL_ERROR "[ERROR] ${VAR_NAME} not found: ${_requested}") + endif() + set(_resolved "${_requested}") + else() + find_program(_requested_path NAMES "${_requested}") + if(_requested_path) + set(_resolved "${_requested}") + else() + set(_resolved "") + foreach(_cand IN LISTS _fallbacks) + find_program(_cand_path NAMES "${_cand}") + if(_cand_path) + cl_log_warn("${VAR_NAME} '${_requested}' not found; fallback to '${_cand}'") + set(_resolved "${_cand}") + break() + endif() + endforeach() + if("${_resolved}" STREQUAL "") + message(FATAL_ERROR "[ERROR] ${VAR_NAME} tool not found: '${_requested}', fallbacks='${_fallbacks}'") + endif() + endif() + endif() + + get_property(_help CACHE "${VAR_NAME}" PROPERTY HELPSTRING) + if("${_help}" STREQUAL "") + set(_help "tool path/name") + endif() + set(${VAR_NAME} "${_resolved}" CACHE STRING "${_help}" FORCE) + set(${VAR_NAME} "${_resolved}" PARENT_SCOPE) +endfunction() + +set(BUILD_ROOT "${CMAKE_SOURCE_DIR}/build/x86_64") +set(OBJ_ROOT "${BUILD_ROOT}/obj") +set(ISO_ROOT "${BUILD_ROOT}/iso_root") +set(RAMDISK_ROOT "${BUILD_ROOT}/ramdisk_root") +set(KERNEL_ELF "${BUILD_ROOT}/clks_kernel.elf") +set(RAMDISK_IMAGE "${BUILD_ROOT}/cleonos_ramdisk.tar") +set(ISO_IMAGE "${CMAKE_SOURCE_DIR}/build/CLeonOS-x86_64.iso") + +set(USER_BUILD_ROOT "${BUILD_ROOT}/user") +set(USER_OBJ_ROOT "${USER_BUILD_ROOT}/obj") +set(USER_APP_DIR "${USER_BUILD_ROOT}/apps") +set(USER_LIB_DIR "${USER_BUILD_ROOT}/lib") +set(USER_RUST_LIB "${USER_LIB_DIR}/libcleonos_user_rust.a") + +resolve_tool_with_fallback(CC gcc cc clang) +resolve_tool_with_fallback(LD ld.lld ld) +resolve_tool_with_fallback(USER_CC cc gcc clang) +resolve_tool_with_fallback(USER_LD ld.lld ld) +resolve_tool_with_fallback(OBJCOPY_FOR_TARGET llvm-objcopy x86_64-linux-gnu-objcopy objcopy) +resolve_tool_with_fallback(OBJDUMP_FOR_TARGET llvm-objdump x86_64-linux-gnu-objdump objdump) +resolve_tool_with_fallback(READELF_FOR_TARGET llvm-readelf x86_64-linux-gnu-readelf readelf) + +resolve_path("${LIMINE_DIR}" LIMINE_DIR_ABS) +resolve_path("${LIMINE_BIN_DIR}" LIMINE_BIN_DIR_ABS) +set(LIMINE_SETUP_STAMP "${LIMINE_DIR_ABS}/.cleonos-limine-setup.stamp") +set(LIMINE_BUILD_STAMP "${LIMINE_DIR_ABS}/.cleonos-limine-build.stamp") + +set(CLKS_ARCH "x86_64" CACHE STRING "Target CLKS arch") +set(LINKER_SCRIPT "${CMAKE_SOURCE_DIR}/clks/arch/${CLKS_ARCH}/linker.ld") +set(USER_LINKER_SCRIPT "${CMAKE_SOURCE_DIR}/cleonos/c/user.ld") +set(KELF_LINKER_SCRIPT "${CMAKE_SOURCE_DIR}/cleonos/c/kelf.ld") + +set(CFLAGS_COMMON + -std=c11 + -ffreestanding + -fno-stack-protector + -fno-builtin + -Wall + -Wextra + -Werror + "-I${CMAKE_SOURCE_DIR}/clks/include" +) + +set(ARCH_CFLAGS + -DCLKS_ARCH_X86_64=1 + -m64 + -mno-red-zone + -mcmodel=kernel + -fno-pic + -fno-pie +) + +set(ASFLAGS_COMMON + -ffreestanding + "-I${CMAKE_SOURCE_DIR}/clks/include" +) + +set(LDFLAGS_COMMON + -nostdlib + -z + max-page-size=0x1000 +) + +set(USER_CFLAGS + -std=c11 + -ffreestanding + -fno-stack-protector + -fno-builtin + -Wall + -Wextra + -Werror + "-I${CMAKE_SOURCE_DIR}/cleonos/c/include" +) + +set(USER_LDFLAGS + -nostdlib + -z + max-page-size=0x1000 + -T + "${USER_LINKER_SCRIPT}" +) + +set(KELF_LDFLAGS + -nostdlib + -z + max-page-size=0x1000 + -T + "${KELF_LINKER_SCRIPT}" +) + +if(NOT CLKS_ARCH STREQUAL "x86_64") + message(FATAL_ERROR "[ERROR] CLKS_ARCH=${CLKS_ARCH} is not supported by this CMake build yet; use x86_64") +endif() + +set(KERNEL_SOURCE_DIRS + "${CMAKE_SOURCE_DIR}/clks/kernel" + "${CMAKE_SOURCE_DIR}/clks/lib" + "${CMAKE_SOURCE_DIR}/clks/drivers" + "${CMAKE_SOURCE_DIR}/clks/arch/${CLKS_ARCH}" +) + +set(C_SOURCES) +set(ASM_SOURCES) +foreach(_src_dir IN LISTS KERNEL_SOURCE_DIRS) + if(EXISTS "${_src_dir}") + file(GLOB_RECURSE _c_abs CONFIGURE_DEPENDS "${_src_dir}/*.c") + file(GLOB_RECURSE _s_abs CONFIGURE_DEPENDS "${_src_dir}/*.S") + foreach(_abs IN LISTS _c_abs) + file(RELATIVE_PATH _rel "${CMAKE_SOURCE_DIR}" "${_abs}") + list(APPEND C_SOURCES "${_rel}") + endforeach() + foreach(_abs IN LISTS _s_abs) + file(RELATIVE_PATH _rel "${CMAKE_SOURCE_DIR}" "${_abs}") + list(APPEND ASM_SOURCES "${_rel}") + endforeach() + endif() +endforeach() +list(REMOVE_DUPLICATES C_SOURCES) +list(REMOVE_DUPLICATES ASM_SOURCES) +list(SORT C_SOURCES) +list(SORT ASM_SOURCES) + +if(NOT C_SOURCES) + message(FATAL_ERROR "[ERROR] no kernel C sources found in clks/") +endif() +if(NOT EXISTS "${LINKER_SCRIPT}") + message(FATAL_ERROR "[ERROR] missing linker script: ${LINKER_SCRIPT}") +endif() + +file(GLOB_RECURSE USER_COMMON_SOURCES_ABS CONFIGURE_DEPENDS "${CMAKE_SOURCE_DIR}/cleonos/c/src/*.c") +set(USER_COMMON_SOURCES) +foreach(_abs IN LISTS USER_COMMON_SOURCES_ABS) + file(RELATIVE_PATH _rel "${CMAKE_SOURCE_DIR}" "${_abs}") + list(APPEND USER_COMMON_SOURCES "${_rel}") +endforeach() +list(SORT USER_COMMON_SOURCES) + +file(GLOB USER_APP_MAIN_SOURCES_ABS CONFIGURE_DEPENDS "${CMAKE_SOURCE_DIR}/cleonos/c/apps/*_main.c") +file(GLOB USER_APP_KMAIN_SOURCES_ABS CONFIGURE_DEPENDS "${CMAKE_SOURCE_DIR}/cleonos/c/apps/*_kmain.c") + +set(USER_APP_MAIN_SOURCES) +set(USER_APP_KMAIN_SOURCES) +foreach(_abs IN LISTS USER_APP_MAIN_SOURCES_ABS) + file(RELATIVE_PATH _rel "${CMAKE_SOURCE_DIR}" "${_abs}") + list(APPEND USER_APP_MAIN_SOURCES "${_rel}") +endforeach() +foreach(_abs IN LISTS USER_APP_KMAIN_SOURCES_ABS) + file(RELATIVE_PATH _rel "${CMAKE_SOURCE_DIR}" "${_abs}") + list(APPEND USER_APP_KMAIN_SOURCES "${_rel}") +endforeach() +list(SORT USER_APP_MAIN_SOURCES) +list(SORT USER_APP_KMAIN_SOURCES) + +if(NOT USER_COMMON_SOURCES) + message(FATAL_ERROR "[ERROR] no user common sources found in cleonos/c/src/") +endif() +if(NOT USER_APP_MAIN_SOURCES) + message(FATAL_ERROR "[ERROR] no user app *_main.c found in cleonos/c/apps/") +endif() +if(NOT USER_APP_KMAIN_SOURCES) + message(FATAL_ERROR "[ERROR] no kernel app *_kmain.c found in cleonos/c/apps/") +endif() + +function(add_kernel_c_object SRC OUTPUT_LIST_VAR) + string(REGEX REPLACE "\\.c$" ".o" OBJ_REL "${SRC}") + set(OBJ_PATH "${OBJ_ROOT}/${OBJ_REL}") + get_filename_component(OBJ_DIR "${OBJ_PATH}" DIRECTORY) + + add_custom_command( + OUTPUT "${OBJ_PATH}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${OBJ_DIR}" + COMMAND ${CC} ${CFLAGS_COMMON} ${ARCH_CFLAGS} -c "${CMAKE_SOURCE_DIR}/${SRC}" -o "${OBJ_PATH}" + DEPENDS "${CMAKE_SOURCE_DIR}/${SRC}" + VERBATIM + ) + + set(${OUTPUT_LIST_VAR} ${${OUTPUT_LIST_VAR}} "${OBJ_PATH}" PARENT_SCOPE) +endfunction() + +function(add_kernel_asm_object SRC OUTPUT_LIST_VAR) + string(REGEX REPLACE "\\.S$" ".o" OBJ_REL "${SRC}") + set(OBJ_PATH "${OBJ_ROOT}/${OBJ_REL}") + get_filename_component(OBJ_DIR "${OBJ_PATH}" DIRECTORY) + + add_custom_command( + OUTPUT "${OBJ_PATH}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${OBJ_DIR}" + COMMAND ${CMAKE_COMMAND} -E echo "${BUILD_LOG_STEP_PREFIX} assembling ${SRC}" + COMMAND ${CC} ${ASFLAGS_COMMON} ${ARCH_CFLAGS} -c "${CMAKE_SOURCE_DIR}/${SRC}" -o "${OBJ_PATH}" + DEPENDS "${CMAKE_SOURCE_DIR}/${SRC}" + VERBATIM + ) + + set(${OUTPUT_LIST_VAR} ${${OUTPUT_LIST_VAR}} "${OBJ_PATH}" PARENT_SCOPE) +endfunction() + +function(add_user_c_object SRC OUTPUT_VAR) + string(REGEX REPLACE "\\.c$" ".o" OBJ_REL "${SRC}") + set(OBJ_PATH "${USER_OBJ_ROOT}/${OBJ_REL}") + get_filename_component(OBJ_DIR "${OBJ_PATH}" DIRECTORY) + + add_custom_command( + OUTPUT "${OBJ_PATH}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${OBJ_DIR}" + COMMAND ${USER_CC} ${USER_CFLAGS} -c "${CMAKE_SOURCE_DIR}/${SRC}" -o "${OBJ_PATH}" + DEPENDS "${CMAKE_SOURCE_DIR}/${SRC}" + VERBATIM + ) + + set(${OUTPUT_VAR} "${OBJ_PATH}" PARENT_SCOPE) +endfunction() + +set(KERNEL_OBJECTS) +foreach(SRC IN LISTS C_SOURCES) + add_kernel_c_object("${SRC}" KERNEL_OBJECTS) +endforeach() +foreach(SRC IN LISTS ASM_SOURCES) + add_kernel_asm_object("${SRC}" KERNEL_OBJECTS) +endforeach() + +add_custom_command( + OUTPUT "${KERNEL_ELF}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${BUILD_ROOT}" + COMMAND ${LD} ${LDFLAGS_COMMON} -T "${LINKER_SCRIPT}" -o "${KERNEL_ELF}" ${KERNEL_OBJECTS} + DEPENDS ${KERNEL_OBJECTS} "${LINKER_SCRIPT}" + VERBATIM +) + +add_custom_target(kernel DEPENDS "${KERNEL_ELF}") + +set(USER_COMMON_OBJECTS) +foreach(SRC IN LISTS USER_COMMON_SOURCES) + add_user_c_object("${SRC}" OBJ_OUT) + list(APPEND USER_COMMON_OBJECTS "${OBJ_OUT}") +endforeach() + +add_custom_command( + OUTPUT "${USER_RUST_LIB}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${USER_LIB_DIR}" + COMMAND ${CMAKE_COMMAND} -E echo "${BUILD_LOG_STEP_PREFIX} building rust user lib" + COMMAND ${RUSTC} --crate-type staticlib -C panic=abort -O "${CMAKE_SOURCE_DIR}/cleonos/rust/src/lib.rs" -o "${USER_RUST_LIB}" + DEPENDS "${CMAKE_SOURCE_DIR}/cleonos/rust/src/lib.rs" + VERBATIM +) + +set(USER_KELF_APP_NAMES) +foreach(SRC IN LISTS USER_APP_KMAIN_SOURCES) + get_filename_component(_stem "${SRC}" NAME_WE) + string(REGEX REPLACE "_kmain$" "" _app_name "${_stem}") + list(APPEND USER_KELF_APP_NAMES "${_app_name}") +endforeach() +list(REMOVE_DUPLICATES USER_KELF_APP_NAMES) + +set(USER_APP_OUTPUTS) +set(USER_APP_NAMES) +set(RAMDISK_SHELL_APPS) +set(RAMDISK_DRIVER_APPS) +set(RAMDISK_SYSTEM_APPS) + +foreach(SRC IN LISTS USER_APP_MAIN_SOURCES) + get_filename_component(_stem "${SRC}" NAME_WE) + string(REGEX REPLACE "_main$" "" _app_name "${_stem}") + + list(FIND USER_KELF_APP_NAMES "${_app_name}" _shadowed_by_kmain) + if(NOT _shadowed_by_kmain EQUAL -1) + cl_log_info("skip ${SRC} because ${_app_name}_kmain.c exists") + continue() + endif() + + list(FIND USER_APP_NAMES "${_app_name}" _dup_name_idx) + if(NOT _dup_name_idx EQUAL -1) + message(FATAL_ERROR "[ERROR] duplicate user app name: ${_app_name}") + endif() + list(APPEND USER_APP_NAMES "${_app_name}") + + add_user_c_object("${SRC}" _user_obj) + set(_app_out "${USER_APP_DIR}/${_app_name}.elf") + add_custom_command( + OUTPUT "${_app_out}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${USER_APP_DIR}" + COMMAND ${USER_LD} ${USER_LDFLAGS} -o "${_app_out}" ${USER_COMMON_OBJECTS} "${_user_obj}" "${USER_RUST_LIB}" + DEPENDS ${USER_COMMON_OBJECTS} "${_user_obj}" "${USER_RUST_LIB}" "${USER_LINKER_SCRIPT}" + VERBATIM + ) + + list(APPEND USER_APP_OUTPUTS "${_app_out}") + if(_app_name MATCHES ".*drv$") + list(APPEND RAMDISK_DRIVER_APPS "${_app_out}") + else() + list(APPEND RAMDISK_SHELL_APPS "${_app_out}") + endif() +endforeach() + +foreach(SRC IN LISTS USER_APP_KMAIN_SOURCES) + get_filename_component(_stem "${SRC}" NAME_WE) + string(REGEX REPLACE "_kmain$" "" _app_name "${_stem}") + + list(FIND USER_APP_NAMES "${_app_name}" _dup_name_idx) + if(NOT _dup_name_idx EQUAL -1) + message(FATAL_ERROR "[ERROR] duplicate user app name: ${_app_name}") + endif() + list(APPEND USER_APP_NAMES "${_app_name}") + + add_user_c_object("${SRC}" _kelf_obj) + set(_app_out "${USER_APP_DIR}/${_app_name}.elf") + add_custom_command( + OUTPUT "${_app_out}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${USER_APP_DIR}" + COMMAND ${USER_LD} ${KELF_LDFLAGS} -o "${_app_out}" "${_kelf_obj}" + DEPENDS "${_kelf_obj}" "${KELF_LINKER_SCRIPT}" + VERBATIM + ) + + list(APPEND USER_APP_OUTPUTS "${_app_out}") + list(APPEND RAMDISK_SYSTEM_APPS "${_app_out}") +endforeach() + +if(NOT USER_APP_OUTPUTS) + message(FATAL_ERROR "[ERROR] no user ELF apps were discovered") +endif() + +add_custom_target(userapps DEPENDS ${USER_APP_OUTPUTS}) + +add_custom_command( + TARGET userapps + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E echo "${BUILD_LOG_INFO_PREFIX} user elf apps ready" +) + +file(GLOB_RECURSE RAMDISK_FILES CONFIGURE_DEPENDS "${CMAKE_SOURCE_DIR}/ramdisk/*") +set(RAMDISK_ROOT_STAMP "${BUILD_ROOT}/.ramdisk_root.stamp") +set(RAMDISK_COPY_COMMANDS) +foreach(_app IN LISTS RAMDISK_SHELL_APPS) + get_filename_component(_app_file "${_app}" NAME) + list(APPEND RAMDISK_COPY_COMMANDS COMMAND ${CMAKE_COMMAND} -E copy "${_app}" "${RAMDISK_ROOT}/shell/${_app_file}") +endforeach() +foreach(_app IN LISTS RAMDISK_DRIVER_APPS) + get_filename_component(_app_file "${_app}" NAME) + list(APPEND RAMDISK_COPY_COMMANDS COMMAND ${CMAKE_COMMAND} -E copy "${_app}" "${RAMDISK_ROOT}/driver/${_app_file}") +endforeach() +foreach(_app IN LISTS RAMDISK_SYSTEM_APPS) + get_filename_component(_app_file "${_app}" NAME) + list(APPEND RAMDISK_COPY_COMMANDS COMMAND ${CMAKE_COMMAND} -E copy "${_app}" "${RAMDISK_ROOT}/system/${_app_file}") +endforeach() + +add_custom_command( + OUTPUT "${RAMDISK_ROOT_STAMP}" + COMMAND ${CMAKE_COMMAND} -E echo "${BUILD_LOG_STEP_PREFIX} staging ramdisk root -> ${RAMDISK_ROOT}" + COMMAND ${CMAKE_COMMAND} -E rm -rf "${RAMDISK_ROOT}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${RAMDISK_ROOT}" + COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_SOURCE_DIR}/ramdisk" "${RAMDISK_ROOT}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${RAMDISK_ROOT}/system" "${RAMDISK_ROOT}/shell" "${RAMDISK_ROOT}/driver" + ${RAMDISK_COPY_COMMANDS} + COMMAND ${CMAKE_COMMAND} -E touch "${RAMDISK_ROOT_STAMP}" + DEPENDS ${RAMDISK_FILES} ${USER_APP_OUTPUTS} + VERBATIM +) + +add_custom_target(ramdisk-root DEPENDS "${RAMDISK_ROOT_STAMP}") +add_dependencies(ramdisk-root userapps) + +add_custom_command( + OUTPUT "${RAMDISK_IMAGE}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${BUILD_ROOT}" + COMMAND ${CMAKE_COMMAND} -E echo "${BUILD_LOG_STEP_PREFIX} packing ramdisk -> ${RAMDISK_IMAGE}" + COMMAND ${TAR} -C "${RAMDISK_ROOT}" -cf "${RAMDISK_IMAGE}" . + DEPENDS "${RAMDISK_ROOT_STAMP}" + VERBATIM +) + +add_custom_target(ramdisk DEPENDS "${RAMDISK_IMAGE}") + +add_custom_target(setup-tools + COMMAND ${CMAKE_COMMAND} + "-DGIT_TOOL=${GIT_TOOL}" + "-DTAR_TOOL=${TAR}" + "-DXORRISO_TOOL=${XORRISO}" + "-DNO_COLOR=${NO_COLOR}" + "-DCC_TOOL=${CC}" + "-DLD_TOOL=${LD}" + "-DOBJCOPY_TOOL=${OBJCOPY_FOR_TARGET}" + "-DOBJDUMP_TOOL=${OBJDUMP_FOR_TARGET}" + "-DREADELF_TOOL=${READELF_FOR_TARGET}" + "-DUSER_CC_TOOL=${USER_CC}" + "-DUSER_LD_TOOL=${USER_LD}" + "-DRUSTC_TOOL=${RUSTC}" + "-DMAKE_TOOL=${MAKE_TOOL}" + "-DSH_TOOL=${SH_TOOL}" + -P "${CMAKE_SOURCE_DIR}/cmake/check_tools.cmake" +) + +add_custom_target(setup-limine + COMMAND ${CMAKE_COMMAND} + "-DGIT_TOOL=${GIT_TOOL}" + "-DMAKE_TOOL=${MAKE_TOOL}" + "-DSH_TOOL=${SH_TOOL}" + "-DNO_COLOR=${NO_COLOR}" + "-DLIMINE_DIR=${LIMINE_DIR_ABS}" + "-DLIMINE_REPO=${LIMINE_REPO}" + "-DLIMINE_REF=${LIMINE_REF}" + "-DLIMINE_BIN_DIR=${LIMINE_BIN_DIR_ABS}" + "-DLIMINE_SETUP_STAMP=${LIMINE_SETUP_STAMP}" + "-DLIMINE_BUILD_STAMP=${LIMINE_BUILD_STAMP}" + "-DLIMINE_CONFIGURE_FLAGS=${LIMINE_CONFIGURE_FLAGS}" + "-DLIMINE_SKIP_CONFIGURE=${LIMINE_SKIP_CONFIGURE}" + "-DOBJCOPY_FOR_TARGET=${OBJCOPY_FOR_TARGET}" + "-DOBJDUMP_FOR_TARGET=${OBJDUMP_FOR_TARGET}" + "-DREADELF_FOR_TARGET=${READELF_FOR_TARGET}" + -P "${CMAKE_SOURCE_DIR}/cmake/setup_limine.cmake" +) +add_dependencies(setup-limine setup-tools) + +add_custom_target(setup DEPENDS setup-tools setup-limine) +add_custom_command(TARGET setup POST_BUILD COMMAND ${CMAKE_COMMAND} -E echo "${BUILD_LOG_INFO_PREFIX} environment ready") + +add_custom_command( + OUTPUT "${ISO_IMAGE}" + COMMAND ${CMAKE_COMMAND} -E echo "${BUILD_LOG_STEP_PREFIX} assembling iso root" + COMMAND ${CMAKE_COMMAND} -E rm -rf "${ISO_ROOT}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${ISO_ROOT}/boot/limine" + COMMAND ${CMAKE_COMMAND} -E copy "${KERNEL_ELF}" "${ISO_ROOT}/boot/clks_kernel.elf" + COMMAND ${CMAKE_COMMAND} -E copy "${RAMDISK_IMAGE}" "${ISO_ROOT}/boot/cleonos_ramdisk.tar" + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_SOURCE_DIR}/configs/limine.conf" "${ISO_ROOT}/boot/limine/limine.conf" + COMMAND ${CMAKE_COMMAND} -E copy "${LIMINE_BIN_DIR_ABS}/limine-bios.sys" "${ISO_ROOT}/boot/limine/limine-bios.sys" + COMMAND ${CMAKE_COMMAND} -E copy "${LIMINE_BIN_DIR_ABS}/limine-bios-cd.bin" "${ISO_ROOT}/boot/limine/limine-bios-cd.bin" + COMMAND ${CMAKE_COMMAND} -E copy "${LIMINE_BIN_DIR_ABS}/limine-uefi-cd.bin" "${ISO_ROOT}/boot/limine/limine-uefi-cd.bin" + COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_SOURCE_DIR}/build" + COMMAND ${CMAKE_COMMAND} -E echo "${BUILD_LOG_STEP_PREFIX} building iso -> ${ISO_IMAGE}" + COMMAND ${XORRISO} -as mkisofs + -b boot/limine/limine-bios-cd.bin + -no-emul-boot + -boot-load-size 4 + -boot-info-table + --efi-boot boot/limine/limine-uefi-cd.bin + -efi-boot-part + --efi-boot-image + --protective-msdos-label + "${ISO_ROOT}" + -o "${ISO_IMAGE}" + COMMAND ${CMAKE_COMMAND} -E echo "${BUILD_LOG_STEP_PREFIX} installing limine boot sectors" + COMMAND "${LIMINE_BIN_DIR_ABS}/limine" bios-install "${ISO_IMAGE}" + COMMAND ${CMAKE_COMMAND} -E echo "${BUILD_LOG_INFO_PREFIX} iso ready: ${ISO_IMAGE}" + DEPENDS "${KERNEL_ELF}" "${RAMDISK_IMAGE}" "${CMAKE_SOURCE_DIR}/configs/limine.conf" + VERBATIM +) + +add_custom_target(iso ALL DEPENDS "${ISO_IMAGE}") +add_dependencies(iso setup-tools setup-limine kernel ramdisk) + +add_custom_target(run + COMMAND ${CMAKE_COMMAND} -E echo "${BUILD_LOG_STEP_PREFIX} launching qemu run" + COMMAND ${QEMU_X86_64} -M q35 -m 1024M -cdrom "${ISO_IMAGE}" -serial stdio + DEPENDS iso + USES_TERMINAL +) + +add_custom_target(debug + COMMAND ${CMAKE_COMMAND} -E echo "${BUILD_LOG_STEP_PREFIX} launching qemu debug (-s -S)" + COMMAND ${QEMU_X86_64} -M q35 -m 1024M -cdrom "${ISO_IMAGE}" -serial stdio -s -S + DEPENDS iso + USES_TERMINAL +) + +add_custom_target(clean-x86 + COMMAND ${CMAKE_COMMAND} -E echo "${BUILD_LOG_STEP_PREFIX} cleaning ${BUILD_ROOT}" + COMMAND ${CMAKE_COMMAND} -E rm -rf "${BUILD_ROOT}" + COMMAND ${CMAKE_COMMAND} -E echo "${BUILD_LOG_INFO_PREFIX} clean done" +) + +add_custom_target(clean-all + COMMAND ${CMAKE_COMMAND} -E echo "${BUILD_LOG_STEP_PREFIX} cleaning build" + COMMAND ${CMAKE_COMMAND} -E rm -rf "${CMAKE_SOURCE_DIR}/build" + COMMAND ${CMAKE_COMMAND} -E echo "${BUILD_LOG_INFO_PREFIX} clean-all done" +) + +add_custom_target(cleonos-help + COMMAND ${CMAKE_COMMAND} -E echo "CLeonOS CMake build system (x86_64 only)" + COMMAND ${CMAKE_COMMAND} -E echo " cmake -S . -B build-cmake" + COMMAND ${CMAKE_COMMAND} -E echo " cmake --build build-cmake --target setup" + COMMAND ${CMAKE_COMMAND} -E echo " cmake --build build-cmake --target userapps" + COMMAND ${CMAKE_COMMAND} -E echo " cmake --build build-cmake --target iso" + COMMAND ${CMAKE_COMMAND} -E echo " cmake --build build-cmake --target run" + COMMAND ${CMAKE_COMMAND} -E echo " cmake --build build-cmake --target debug" +) diff --git a/Makefile b/Makefile index c011301..601abf7 100644 --- a/Makefile +++ b/Makefile @@ -1,332 +1,121 @@ .RECIPEPREFIX := > +MAKEFLAGS += --no-print-directory +CMAKE ?= cmake +CMAKE_BUILD_DIR ?= build-cmake +CMAKE_BUILD_TYPE ?= Release +CMAKE_GENERATOR ?= +CMAKE_EXTRA_ARGS ?= NO_COLOR ?= 0 - -BUILD_ROOT := build/x86_64 -OBJ_ROOT := $(BUILD_ROOT)/obj -ISO_ROOT := $(BUILD_ROOT)/iso_root -RAMDISK_ROOT := $(BUILD_ROOT)/ramdisk_root -KERNEL_ELF := $(BUILD_ROOT)/clks_kernel.elf -RAMDISK_IMAGE := $(BUILD_ROOT)/cleonos_ramdisk.tar -ISO_IMAGE := build/CLeonOS-x86_64.iso - -USER_BUILD_ROOT := $(BUILD_ROOT)/user -USER_OBJ_ROOT := $(USER_BUILD_ROOT)/obj -USER_APP_DIR := $(USER_BUILD_ROOT)/apps -USER_LIB_DIR := $(USER_BUILD_ROOT)/lib - -LIMINE_DIR ?= limine -LIMINE_REPO ?= https://gh-proxy.com/https://github.com/limine-bootloader/limine.git +LIMINE_SKIP_CONFIGURE ?= LIMINE_REF ?= -LIMINE_BIN_DIR ?= $(LIMINE_DIR)/bin -LIMINE_SETUP_STAMP := $(LIMINE_DIR)/.cleonos-limine-setup.stamp -LIMINE_BUILD_STAMP := $(LIMINE_DIR)/.cleonos-limine-build.stamp -LIMINE_CONFIGURE_FLAGS ?= --enable-bios-cd --enable-uefi-cd --enable-uefi-x86-64 -LIMINE_SKIP_CONFIGURE ?= 0 -OBJCOPY_FOR_TARGET ?= llvm-objcopy -OBJDUMP_FOR_TARGET ?= llvm-objdump -READELF_FOR_TARGET ?= llvm-readelf +LIMINE_REPO ?= +LIMINE_DIR ?= +LIMINE_BIN_DIR ?= +OBJCOPY_FOR_TARGET ?= +OBJDUMP_FOR_TARGET ?= +READELF_FOR_TARGET ?= -XORRISO ?= xorriso -TAR ?= tar - -QEMU_X86_64 ?= qemu-system-x86_64 - -CC ?= x86_64-elf-gcc -LD ?= x86_64-elf-ld -ARCH_CFLAGS := -DCLKS_ARCH_X86_64=1 -m64 -mno-red-zone -mcmodel=kernel -fno-pic -fno-pie -LINKER_SCRIPT := clks/arch/x86_64/linker.ld -RUN_COMMAND := $(QEMU_X86_64) -M q35 -m 1024M -cdrom $(ISO_IMAGE) -serial stdio -DEBUG_COMMAND := $(QEMU_X86_64) -M q35 -m 1024M -cdrom $(ISO_IMAGE) -serial stdio -s -S - -USER_CC ?= cc -USER_LD ?= ld -RUSTC ?= rustc -USER_LINKER_SCRIPT := cleonos/c/user.ld -KELF_LINKER_SCRIPT := cleonos/c/kelf.ld -USER_CFLAGS := -std=c11 -ffreestanding -fno-stack-protector -fno-builtin -Wall -Wextra -Werror -Icleonos/c/include -USER_LDFLAGS := -nostdlib -z max-page-size=0x1000 -T $(USER_LINKER_SCRIPT) -KELF_LDFLAGS := -nostdlib -z max-page-size=0x1000 -T $(KELF_LINKER_SCRIPT) - -ifeq ($(NO_COLOR),1) -COLOR_RESET := -COLOR_INFO := -COLOR_WARN := -COLOR_ERROR := -COLOR_STEP := +ifeq ($(strip $(CMAKE_GENERATOR)),) +GEN_ARG := else -COLOR_RESET := \033[0m -COLOR_INFO := \033[1;36m -COLOR_WARN := \033[1;33m -COLOR_ERROR := \033[1;31m -COLOR_STEP := \033[1;35m +GEN_ARG := -G "$(CMAKE_GENERATOR)" endif -define log_info -@printf '%b\n' "$(COLOR_INFO)[INFO]$(COLOR_RESET) $(1)" -endef +CMAKE_PASSTHROUGH_ARGS := -define log_warn -@printf '%b\n' "$(COLOR_WARN)[WARN]$(COLOR_RESET) $(1)" -endef +ifneq ($(strip $(LIMINE_SKIP_CONFIGURE)),) +CMAKE_PASSTHROUGH_ARGS += -DLIMINE_SKIP_CONFIGURE=$(LIMINE_SKIP_CONFIGURE) +endif +ifneq ($(strip $(LIMINE_REF)),) +CMAKE_PASSTHROUGH_ARGS += -DLIMINE_REF=$(LIMINE_REF) +endif +ifneq ($(strip $(LIMINE_REPO)),) +CMAKE_PASSTHROUGH_ARGS += -DLIMINE_REPO=$(LIMINE_REPO) +endif +ifneq ($(strip $(LIMINE_DIR)),) +CMAKE_PASSTHROUGH_ARGS += -DLIMINE_DIR=$(LIMINE_DIR) +endif +ifneq ($(strip $(LIMINE_BIN_DIR)),) +CMAKE_PASSTHROUGH_ARGS += -DLIMINE_BIN_DIR=$(LIMINE_BIN_DIR) +endif +ifneq ($(strip $(OBJCOPY_FOR_TARGET)),) +CMAKE_PASSTHROUGH_ARGS += -DOBJCOPY_FOR_TARGET=$(OBJCOPY_FOR_TARGET) +endif +ifneq ($(strip $(OBJDUMP_FOR_TARGET)),) +CMAKE_PASSTHROUGH_ARGS += -DOBJDUMP_FOR_TARGET=$(OBJDUMP_FOR_TARGET) +endif +ifneq ($(strip $(READELF_FOR_TARGET)),) +CMAKE_PASSTHROUGH_ARGS += -DREADELF_FOR_TARGET=$(READELF_FOR_TARGET) +endif -define log_error -@printf '%b\n' "$(COLOR_ERROR)[ERROR]$(COLOR_RESET) $(1)" -endef - -define log_step -@printf '%b\n' "$(COLOR_STEP)[STEP]$(COLOR_RESET) $(1)" -endef - -C_SOURCES := \ - clks/kernel/kmain.c \ - clks/kernel/log.c \ - clks/kernel/limine_requests.c \ - clks/kernel/tty.c \ - clks/kernel/pmm.c \ - clks/kernel/heap.c \ - clks/kernel/keyboard.c \ - clks/kernel/interrupts.c \ - clks/kernel/scheduler.c \ - clks/kernel/elf64.c \ - clks/kernel/elfrunner.c \ - clks/kernel/syscall.c \ - clks/kernel/ramdisk.c \ - clks/kernel/fs.c \ - clks/kernel/userland.c \ - clks/kernel/driver.c \ - clks/kernel/service.c \ - clks/kernel/kelf.c \ - clks/kernel/exec.c \ - clks/lib/string.c \ - clks/drivers/serial/serial.c \ - clks/drivers/video/framebuffer.c \ - clks/drivers/video/font8x8.c \ - clks/arch/x86_64/boot.c - -ASM_SOURCES := \ - clks/arch/x86_64/interrupt_stubs.S - -C_OBJECTS := $(patsubst %.c,$(OBJ_ROOT)/%.o,$(C_SOURCES)) -ASM_OBJECTS := $(patsubst %.S,$(OBJ_ROOT)/%.o,$(ASM_SOURCES)) -OBJECTS := $(C_OBJECTS) $(ASM_OBJECTS) - -USER_COMMON_SOURCES := \ - cleonos/c/src/runtime.c \ - cleonos/c/src/syscall.c - -USER_COMMON_OBJECTS := $(patsubst %.c,$(USER_OBJ_ROOT)/%.o,$(USER_COMMON_SOURCES)) -USER_SHELL_OBJECT := $(USER_OBJ_ROOT)/cleonos/c/apps/shell_main.o -USER_TTYDRV_OBJECT := $(USER_OBJ_ROOT)/cleonos/c/apps/ttydrv_main.o -USER_ELFRUNNER_KOBJ := $(USER_OBJ_ROOT)/cleonos/c/apps/elfrunner_kmain.o -USER_MEMC_KOBJ := $(USER_OBJ_ROOT)/cleonos/c/apps/memc_kmain.o -USER_RUST_LIB := $(USER_LIB_DIR)/libcleonos_user_rust.a - -APP_SHELL := $(USER_APP_DIR)/shell.elf -APP_TTYDRV := $(USER_APP_DIR)/ttydrv.elf -APP_ELFRUNNER := $(USER_APP_DIR)/elfrunner.elf -APP_MEMC := $(USER_APP_DIR)/memc.elf -USER_APPS := $(APP_SHELL) $(APP_TTYDRV) $(APP_ELFRUNNER) $(APP_MEMC) - -CFLAGS_COMMON := -std=c11 -ffreestanding -fno-stack-protector -fno-builtin -Wall -Wextra -Werror -Iclks/include -ASFLAGS_COMMON := -ffreestanding -Iclks/include -LDFLAGS_COMMON := -nostdlib -z max-page-size=0x1000 - -.PHONY: all setup setup-tools setup-limine kernel userapps ramdisk-root ramdisk iso run debug clean clean-all help +.PHONY: all configure reconfigure setup setup-tools setup-limine kernel userapps ramdisk-root ramdisk iso run debug clean clean-all help all: iso -setup: setup-tools setup-limine -> $(call log_info,environment ready) +configure: +> @$(CMAKE) -S . -B $(CMAKE_BUILD_DIR) $(GEN_ARG) -DCMAKE_BUILD_TYPE=$(CMAKE_BUILD_TYPE) -DNO_COLOR=$(NO_COLOR) $(CMAKE_EXTRA_ARGS) $(CMAKE_PASSTHROUGH_ARGS) -setup-tools: -> $(call log_step,checking host tools) -> @command -v git >/dev/null 2>&1 || (printf '%b\n' "$(COLOR_ERROR)[ERROR]$(COLOR_RESET) missing tool: git" && exit 1) -> @command -v $(TAR) >/dev/null 2>&1 || (printf '%b\n' "$(COLOR_ERROR)[ERROR]$(COLOR_RESET) missing tool: $(TAR)" && exit 1) -> @command -v $(XORRISO) >/dev/null 2>&1 || (printf '%b\n' "$(COLOR_ERROR)[ERROR]$(COLOR_RESET) missing tool: $(XORRISO)" && exit 1) -> @command -v clang >/dev/null 2>&1 || (printf '%b\n' "$(COLOR_ERROR)[ERROR]$(COLOR_RESET) missing tool: clang" && exit 1) -> @command -v ld.lld >/dev/null 2>&1 || (printf '%b\n' "$(COLOR_ERROR)[ERROR]$(COLOR_RESET) missing tool: ld.lld" && exit 1) -> @command -v $(OBJCOPY_FOR_TARGET) >/dev/null 2>&1 || (printf '%b\n' "$(COLOR_ERROR)[ERROR]$(COLOR_RESET) missing tool: $(OBJCOPY_FOR_TARGET)" && exit 1) -> @command -v $(OBJDUMP_FOR_TARGET) >/dev/null 2>&1 || (printf '%b\n' "$(COLOR_ERROR)[ERROR]$(COLOR_RESET) missing tool: $(OBJDUMP_FOR_TARGET)" && exit 1) -> @command -v $(READELF_FOR_TARGET) >/dev/null 2>&1 || (printf '%b\n' "$(COLOR_ERROR)[ERROR]$(COLOR_RESET) missing tool: $(READELF_FOR_TARGET)" && exit 1) -> @command -v $(USER_CC) >/dev/null 2>&1 || (printf '%b\n' "$(COLOR_ERROR)[ERROR]$(COLOR_RESET) missing tool: $(USER_CC)" && exit 1) -> @command -v $(USER_LD) >/dev/null 2>&1 || (printf '%b\n' "$(COLOR_ERROR)[ERROR]$(COLOR_RESET) missing tool: $(USER_LD)" && exit 1) -> @command -v $(RUSTC) >/dev/null 2>&1 || (printf '%b\n' "$(COLOR_ERROR)[ERROR]$(COLOR_RESET) missing tool: $(RUSTC)" && exit 1) -> $(call log_info,required tools are available) +reconfigure: +> @rm -rf $(CMAKE_BUILD_DIR) +> @$(MAKE) configure CMAKE_BUILD_TYPE=$(CMAKE_BUILD_TYPE) CMAKE_GENERATOR="$(CMAKE_GENERATOR)" CMAKE_EXTRA_ARGS="$(CMAKE_EXTRA_ARGS)" NO_COLOR="$(NO_COLOR)" LIMINE_SKIP_CONFIGURE="$(LIMINE_SKIP_CONFIGURE)" LIMINE_REF="$(LIMINE_REF)" LIMINE_REPO="$(LIMINE_REPO)" LIMINE_DIR="$(LIMINE_DIR)" LIMINE_BIN_DIR="$(LIMINE_BIN_DIR)" OBJCOPY_FOR_TARGET="$(OBJCOPY_FOR_TARGET)" OBJDUMP_FOR_TARGET="$(OBJDUMP_FOR_TARGET)" READELF_FOR_TARGET="$(READELF_FOR_TARGET)" -setup-limine: -> $(call log_step,preparing limine) -> @if [ ! -d "$(LIMINE_DIR)" ]; then \ -> if [ -n "$(LIMINE_REF)" ]; then \ -> printf '%b\n' "$(COLOR_INFO)[INFO]$(COLOR_RESET) cloning limine ($(LIMINE_REF)) into $(LIMINE_DIR)"; \ -> git clone --branch "$(LIMINE_REF)" --depth 1 "$(LIMINE_REPO)" "$(LIMINE_DIR)"; \ -> else \ -> printf '%b\n' "$(COLOR_INFO)[INFO]$(COLOR_RESET) cloning limine (default branch) into $(LIMINE_DIR)"; \ -> git clone --depth 1 "$(LIMINE_REPO)" "$(LIMINE_DIR)"; \ -> fi; \ -> fi -> @if [ "$(LIMINE_SKIP_CONFIGURE)" = "1" ]; then \ -> test -f "$(LIMINE_DIR)/Makefile" || (printf '%b\n' "$(COLOR_ERROR)[ERROR]$(COLOR_RESET) LIMINE_SKIP_CONFIGURE=1 but $(LIMINE_DIR)/Makefile is missing" && exit 1); \ -> printf '%b\n' "$(COLOR_WARN)[WARN]$(COLOR_RESET) skipping limine Makefile generation (LIMINE_SKIP_CONFIGURE=1)"; \ -> else \ -> cfg_fingerprint="FLAGS=$(LIMINE_CONFIGURE_FLAGS);OBJCOPY=$(OBJCOPY_FOR_TARGET);OBJDUMP=$(OBJDUMP_FOR_TARGET);READELF=$(READELF_FOR_TARGET)"; \ -> need_configure=0; \ -> if [ ! -f "$(LIMINE_DIR)/Makefile" ]; then need_configure=1; fi; \ -> if [ ! -f "$(LIMINE_SETUP_STAMP)" ]; then need_configure=1; fi; \ -> if [ -f "$(LIMINE_SETUP_STAMP)" ] && ! grep -qx "$$cfg_fingerprint" "$(LIMINE_SETUP_STAMP)"; then need_configure=1; fi; \ -> if [ "$$need_configure" -eq 1 ]; then \ -> printf '%b\n' "$(COLOR_STEP)[STEP]$(COLOR_RESET) generating/reconfiguring limine Makefile"; \ -> if [ -x "$(LIMINE_DIR)/bootstrap" ]; then \ -> (cd "$(LIMINE_DIR)" && ./bootstrap); \ -> fi; \ -> test -x "$(LIMINE_DIR)/configure" || (printf '%b\n' "$(COLOR_ERROR)[ERROR]$(COLOR_RESET) limine configure script missing" && exit 1); \ -> (cd "$(LIMINE_DIR)" && OBJCOPY_FOR_TARGET="$(OBJCOPY_FOR_TARGET)" OBJDUMP_FOR_TARGET="$(OBJDUMP_FOR_TARGET)" READELF_FOR_TARGET="$(READELF_FOR_TARGET)" ./configure $(LIMINE_CONFIGURE_FLAGS)); \ -> printf '%s\n' "$$cfg_fingerprint" > "$(LIMINE_SETUP_STAMP)"; \ -> rm -f "$(LIMINE_BUILD_STAMP)"; \ -> else \ -> printf '%b\n' "$(COLOR_INFO)[INFO]$(COLOR_RESET) limine configure state unchanged"; \ -> fi; \ -> fi -> @need_build=0; \ -> if [ ! -f "$(LIMINE_BUILD_STAMP)" ]; then need_build=1; fi; \ -> for f in limine limine-bios.sys limine-bios-cd.bin limine-uefi-cd.bin; do \ -> if [ ! -f "$(LIMINE_BIN_DIR)/$$f" ]; then need_build=1; fi; \ -> done; \ -> if [ "$$need_build" -eq 1 ]; then \ -> printf '%b\n' "$(COLOR_INFO)[INFO]$(COLOR_RESET) building limine"; \ -> $(MAKE) -C "$(LIMINE_DIR)"; \ -> touch "$(LIMINE_BUILD_STAMP)"; \ -> else \ -> printf '%b\n' "$(COLOR_INFO)[INFO]$(COLOR_RESET) limine already built, skipping compile"; \ -> fi -> @test -f "$(LIMINE_BIN_DIR)/limine" || (printf '%b\n' "$(COLOR_ERROR)[ERROR]$(COLOR_RESET) limine build failed" && exit 1) -> @test -f "$(LIMINE_BIN_DIR)/limine-bios.sys" || (printf '%b\n' "$(COLOR_ERROR)[ERROR]$(COLOR_RESET) limine-bios.sys missing" && exit 1) -> @test -f "$(LIMINE_BIN_DIR)/limine-bios-cd.bin" || (printf '%b\n' "$(COLOR_ERROR)[ERROR]$(COLOR_RESET) limine-bios-cd.bin missing" && exit 1) -> @test -f "$(LIMINE_BIN_DIR)/limine-uefi-cd.bin" || (printf '%b\n' "$(COLOR_ERROR)[ERROR]$(COLOR_RESET) limine-uefi-cd.bin missing" && exit 1) -> $(call log_info,limine artifacts ready) +setup: configure +> @$(CMAKE) --build $(CMAKE_BUILD_DIR) --target setup -kernel: $(KERNEL_ELF) +setup-tools: configure +> @$(CMAKE) --build $(CMAKE_BUILD_DIR) --target setup-tools -userapps: $(USER_APPS) -> $(call log_info,user elf apps ready) +setup-limine: configure +> @$(CMAKE) --build $(CMAKE_BUILD_DIR) --target setup-limine -ramdisk-root: userapps -> $(call log_step,staging ramdisk root -> $(RAMDISK_ROOT)) -> @rm -rf $(RAMDISK_ROOT) -> @mkdir -p $(RAMDISK_ROOT) -> @cp -a ramdisk/. $(RAMDISK_ROOT)/ -> @mkdir -p $(RAMDISK_ROOT)/system $(RAMDISK_ROOT)/shell $(RAMDISK_ROOT)/driver -> @cp $(APP_SHELL) $(RAMDISK_ROOT)/shell/shell.elf -> @cp $(APP_ELFRUNNER) $(RAMDISK_ROOT)/system/elfrunner.elf -> @cp $(APP_MEMC) $(RAMDISK_ROOT)/system/memc.elf -> @cp $(APP_TTYDRV) $(RAMDISK_ROOT)/driver/ttydrv.elf +kernel: configure +> @$(CMAKE) --build $(CMAKE_BUILD_DIR) --target kernel -ramdisk: $(RAMDISK_IMAGE) +userapps: configure +> @$(CMAKE) --build $(CMAKE_BUILD_DIR) --target userapps -$(KERNEL_ELF): $(OBJECTS) $(LINKER_SCRIPT) Makefile -> $(call log_step,linking kernel -> $(KERNEL_ELF)) -> @mkdir -p $(dir $@) -> @$(LD) $(LDFLAGS_COMMON) -T $(LINKER_SCRIPT) -o $@ $(OBJECTS) +ramdisk-root: configure +> @$(CMAKE) --build $(CMAKE_BUILD_DIR) --target ramdisk-root -$(OBJ_ROOT)/%.o: %.c Makefile -> $(call log_step,compiling $<) -> @mkdir -p $(dir $@) -> @$(CC) $(CFLAGS_COMMON) $(ARCH_CFLAGS) -c $< -o $@ +ramdisk: configure +> @$(CMAKE) --build $(CMAKE_BUILD_DIR) --target ramdisk -$(OBJ_ROOT)/%.o: %.S Makefile -> $(call log_step,assembling $<) -> @mkdir -p $(dir $@) -> @$(CC) $(ASFLAGS_COMMON) $(ARCH_CFLAGS) -c $< -o $@ +iso: configure +> @$(CMAKE) --build $(CMAKE_BUILD_DIR) --target iso -$(USER_OBJ_ROOT)/%.o: %.c Makefile -> $(call log_step,compiling user $<) -> @mkdir -p $(dir $@) -> @$(USER_CC) $(USER_CFLAGS) -c $< -o $@ +run: configure +> @$(CMAKE) --build $(CMAKE_BUILD_DIR) --target run -$(USER_RUST_LIB): cleonos/rust/src/lib.rs Makefile -> $(call log_step,building rust user lib) -> @mkdir -p $(dir $@) -> @$(RUSTC) --crate-type staticlib -C panic=abort -O $< -o $@ - -$(APP_SHELL): $(USER_COMMON_OBJECTS) $(USER_SHELL_OBJECT) $(USER_RUST_LIB) $(USER_LINKER_SCRIPT) -> $(call log_step,linking user shell.elf) -> @mkdir -p $(dir $@) -> @$(USER_LD) $(USER_LDFLAGS) -o $@ $(USER_COMMON_OBJECTS) $(USER_SHELL_OBJECT) $(USER_RUST_LIB) - -$(APP_TTYDRV): $(USER_COMMON_OBJECTS) $(USER_TTYDRV_OBJECT) $(USER_LINKER_SCRIPT) -> $(call log_step,linking user ttydrv.elf) -> @mkdir -p $(dir $@) -> @$(USER_LD) $(USER_LDFLAGS) -o $@ $(USER_COMMON_OBJECTS) $(USER_TTYDRV_OBJECT) - -$(APP_ELFRUNNER): $(USER_ELFRUNNER_KOBJ) $(KELF_LINKER_SCRIPT) -> $(call log_step,linking kernel-elf elfrunner.elf) -> @mkdir -p $(dir $@) -> @$(USER_LD) $(KELF_LDFLAGS) -o $@ $(USER_ELFRUNNER_KOBJ) - -$(APP_MEMC): $(USER_MEMC_KOBJ) $(KELF_LINKER_SCRIPT) -> $(call log_step,linking kernel-elf memc.elf) -> @mkdir -p $(dir $@) -> @$(USER_LD) $(KELF_LDFLAGS) -o $@ $(USER_MEMC_KOBJ) - -$(RAMDISK_IMAGE): ramdisk-root Makefile -> $(call log_step,packing ramdisk -> $(RAMDISK_IMAGE)) -> @mkdir -p $(dir $@) -> @$(TAR) -C $(RAMDISK_ROOT) -cf $@ . - -iso: setup-tools setup-limine $(KERNEL_ELF) $(RAMDISK_IMAGE) configs/limine.conf -> $(call log_step,assembling iso root) -> @rm -rf $(ISO_ROOT) -> @mkdir -p $(ISO_ROOT)/boot/limine -> @cp $(KERNEL_ELF) $(ISO_ROOT)/boot/clks_kernel.elf -> @cp $(RAMDISK_IMAGE) $(ISO_ROOT)/boot/cleonos_ramdisk.tar -> @cp configs/limine.conf $(ISO_ROOT)/boot/limine/limine.conf -> @cp $(LIMINE_BIN_DIR)/limine-bios.sys $(ISO_ROOT)/boot/limine/ -> @cp $(LIMINE_BIN_DIR)/limine-bios-cd.bin $(ISO_ROOT)/boot/limine/ -> @cp $(LIMINE_BIN_DIR)/limine-uefi-cd.bin $(ISO_ROOT)/boot/limine/ -> @mkdir -p $(dir $(ISO_IMAGE)) -> $(call log_step,building iso -> $(ISO_IMAGE)) -> @$(XORRISO) -as mkisofs \ -> -b boot/limine/limine-bios-cd.bin \ -> -no-emul-boot \ -> -boot-load-size 4 \ -> -boot-info-table \ -> --efi-boot boot/limine/limine-uefi-cd.bin \ -> -efi-boot-part \ -> --efi-boot-image \ -> --protective-msdos-label \ -> $(ISO_ROOT) \ -> -o $(ISO_IMAGE) -> $(call log_step,installing limine boot sectors) -> @$(LIMINE_BIN_DIR)/limine bios-install $(ISO_IMAGE) -> $(call log_info,iso ready: $(ISO_IMAGE)) - -run: iso -> $(call log_step,launching qemu run) -> @$(RUN_COMMAND) - -debug: iso -> $(call log_step,launching qemu debug (-s -S)) -> @$(DEBUG_COMMAND) +debug: configure +> @$(CMAKE) --build $(CMAKE_BUILD_DIR) --target debug clean: -> $(call log_step,cleaning $(BUILD_ROOT)) -> @rm -rf $(BUILD_ROOT) -> $(call log_info,clean done) +> @if [ -d "$(CMAKE_BUILD_DIR)" ]; then \ +> $(CMAKE) --build $(CMAKE_BUILD_DIR) --target clean-x86; \ +> else \ +> rm -rf build/x86_64; \ +> fi clean-all: -> $(call log_step,cleaning build) -> @rm -rf build -> $(call log_info,clean-all done) +> @if [ -d "$(CMAKE_BUILD_DIR)" ]; then \ +> $(CMAKE) --build $(CMAKE_BUILD_DIR) --target clean-all; \ +> else \ +> rm -rf build build-cmake; \ +> fi help: -> @echo "CLeonOS build system (x86_64 only)" +> @echo "CLeonOS (CMake-backed wrapper)" +> @echo " make configure" > @echo " make setup" -> @echo " make setup LIMINE_REF=" -> @echo " make setup LIMINE_SKIP_CONFIGURE=1" -> @echo " make setup LIMINE_CONFIGURE_FLAGS='--enable-bios-cd --enable-uefi-cd --enable-uefi-x86-64'" -> @echo " make setup OBJCOPY_FOR_TARGET=llvm-objcopy OBJDUMP_FOR_TARGET=llvm-objdump READELF_FOR_TARGET=llvm-readelf" > @echo " make userapps" > @echo " make iso" > @echo " make run" > @echo " make debug" -> @echo " make NO_COLOR=1 " +> @echo " make clean" +> @echo " make clean-all" +> @echo "" +> @echo "Pass custom CMake cache args via:" +> @echo " make configure CMAKE_EXTRA_ARGS='-DLIMINE_SKIP_CONFIGURE=1 -DOBJCOPY_FOR_TARGET=objcopy'" +> @echo "Direct passthrough is also supported:" +> @echo " make run LIMINE_SKIP_CONFIGURE=1" diff --git a/cmake/check_tools.cmake b/cmake/check_tools.cmake new file mode 100644 index 0000000..b59f155 --- /dev/null +++ b/cmake/check_tools.cmake @@ -0,0 +1,38 @@ +cmake_minimum_required(VERSION 3.20) +include("${CMAKE_CURRENT_LIST_DIR}/log.cmake") + +function(require_tool TOOL_VALUE) + if("${TOOL_VALUE}" STREQUAL "") + cl_log_error("missing tool name") + endif() + + if(IS_ABSOLUTE "${TOOL_VALUE}") + if(NOT EXISTS "${TOOL_VALUE}") + cl_log_error("missing tool: ${TOOL_VALUE}") + endif() + return() + endif() + + find_program(_tool_path NAMES "${TOOL_VALUE}") + if(NOT _tool_path) + cl_log_error("missing tool: ${TOOL_VALUE}") + endif() +endfunction() + +cl_log_step("checking host tools") + +require_tool("${GIT_TOOL}") +require_tool("${TAR_TOOL}") +require_tool("${XORRISO_TOOL}") +require_tool("${CC_TOOL}") +require_tool("${LD_TOOL}") +require_tool("${OBJCOPY_TOOL}") +require_tool("${OBJDUMP_TOOL}") +require_tool("${READELF_TOOL}") +require_tool("${USER_CC_TOOL}") +require_tool("${USER_LD_TOOL}") +require_tool("${RUSTC_TOOL}") +require_tool("${MAKE_TOOL}") +require_tool("${SH_TOOL}") + +cl_log_info("required tools are available") \ No newline at end of file diff --git a/cmake/log.cmake b/cmake/log.cmake new file mode 100644 index 0000000..8313b3c --- /dev/null +++ b/cmake/log.cmake @@ -0,0 +1,36 @@ +cmake_minimum_required(VERSION 3.20) + +if(NOT DEFINED NO_COLOR) + set(NO_COLOR 0) +endif() + +if(NO_COLOR) + set(CL_LOG_RESET "") + set(CL_LOG_INFO "") + set(CL_LOG_WARN "") + set(CL_LOG_ERROR "") + set(CL_LOG_STEP "") +else() + string(ASCII 27 CL_LOG_ESC) + set(CL_LOG_RESET "${CL_LOG_ESC}[0m") + set(CL_LOG_INFO "${CL_LOG_ESC}[1;36m") + set(CL_LOG_WARN "${CL_LOG_ESC}[1;33m") + set(CL_LOG_ERROR "${CL_LOG_ESC}[1;31m") + set(CL_LOG_STEP "${CL_LOG_ESC}[1;35m") +endif() + +function(cl_log_step TEXT) + message(NOTICE "${CL_LOG_STEP}[STEP]${CL_LOG_RESET} ${TEXT}") +endfunction() + +function(cl_log_info TEXT) + message(NOTICE "${CL_LOG_INFO}[INFO]${CL_LOG_RESET} ${TEXT}") +endfunction() + +function(cl_log_warn TEXT) + message(NOTICE "${CL_LOG_WARN}[WARN]${CL_LOG_RESET} ${TEXT}") +endfunction() + +function(cl_log_error TEXT) + message(FATAL_ERROR "${CL_LOG_ERROR}[ERROR]${CL_LOG_RESET} ${TEXT}") +endfunction() \ No newline at end of file diff --git a/cmake/setup_limine.cmake b/cmake/setup_limine.cmake new file mode 100644 index 0000000..8018f60 --- /dev/null +++ b/cmake/setup_limine.cmake @@ -0,0 +1,118 @@ +cmake_minimum_required(VERSION 3.20) +include("${CMAKE_CURRENT_LIST_DIR}/log.cmake") + +function(run_cmd) + set(options) + set(oneValueArgs WORKING_DIRECTORY) + set(multiValueArgs COMMAND) + cmake_parse_arguments(RUN "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if("${RUN_WORKING_DIRECTORY}" STREQUAL "") + execute_process(COMMAND ${RUN_COMMAND} RESULT_VARIABLE _rv) + else() + execute_process(COMMAND ${RUN_COMMAND} WORKING_DIRECTORY "${RUN_WORKING_DIRECTORY}" RESULT_VARIABLE _rv) + endif() + + if(NOT _rv EQUAL 0) + string(REPLACE ";" " " _cmd "${RUN_COMMAND}") + cl_log_error("command failed (${_rv}): ${_cmd}") + endif() +endfunction() + +cl_log_step("preparing limine") +set(_limine_makefile_missing FALSE) + +if(NOT EXISTS "${LIMINE_DIR}") + if("${LIMINE_REF}" STREQUAL "") + cl_log_info("cloning limine (default branch) into ${LIMINE_DIR}") + run_cmd(COMMAND "${GIT_TOOL}" clone --depth 1 "${LIMINE_REPO}" "${LIMINE_DIR}") + else() + cl_log_info("cloning limine (${LIMINE_REF}) into ${LIMINE_DIR}") + run_cmd(COMMAND "${GIT_TOOL}" clone --branch "${LIMINE_REF}" --depth 1 "${LIMINE_REPO}" "${LIMINE_DIR}") + endif() +endif() + +if(LIMINE_SKIP_CONFIGURE) + if(NOT EXISTS "${LIMINE_DIR}/Makefile") + set(_limine_makefile_missing TRUE) + cl_log_warn("LIMINE_SKIP_CONFIGURE=1 but ${LIMINE_DIR}/Makefile is missing; continue with existing limine binaries") + endif() + cl_log_warn("skipping limine Makefile generation (LIMINE_SKIP_CONFIGURE=1)") +else() + set(cfg_fingerprint "FLAGS=${LIMINE_CONFIGURE_FLAGS};OBJCOPY=${OBJCOPY_FOR_TARGET};OBJDUMP=${OBJDUMP_FOR_TARGET};READELF=${READELF_FOR_TARGET}") + + set(need_configure FALSE) + if(NOT EXISTS "${LIMINE_DIR}/Makefile") + set(need_configure TRUE) + endif() + if(NOT EXISTS "${LIMINE_SETUP_STAMP}") + set(need_configure TRUE) + endif() + + if(EXISTS "${LIMINE_SETUP_STAMP}") + file(READ "${LIMINE_SETUP_STAMP}" _stamp_content) + string(STRIP "${_stamp_content}" _stamp_content) + if(NOT _stamp_content STREQUAL cfg_fingerprint) + set(need_configure TRUE) + endif() + endif() + + if(need_configure) + cl_log_step("generating/reconfiguring limine Makefile") + + if(EXISTS "${LIMINE_DIR}/bootstrap") + run_cmd(COMMAND "${SH_TOOL}" "./bootstrap" WORKING_DIRECTORY "${LIMINE_DIR}") + endif() + + if(NOT EXISTS "${LIMINE_DIR}/configure") + cl_log_error("limine configure script missing") + endif() + + separate_arguments(_cfg_flags UNIX_COMMAND "${LIMINE_CONFIGURE_FLAGS}") + run_cmd( + COMMAND + "${CMAKE_COMMAND}" -E env + "OBJCOPY_FOR_TARGET=${OBJCOPY_FOR_TARGET}" + "OBJDUMP_FOR_TARGET=${OBJDUMP_FOR_TARGET}" + "READELF_FOR_TARGET=${READELF_FOR_TARGET}" + "${SH_TOOL}" "./configure" ${_cfg_flags} + WORKING_DIRECTORY "${LIMINE_DIR}" + ) + + file(WRITE "${LIMINE_SETUP_STAMP}" "${cfg_fingerprint}\n") + file(REMOVE "${LIMINE_BUILD_STAMP}") + else() + cl_log_info("limine configure state unchanged") + endif() +endif() + +set(need_build FALSE) +if(NOT EXISTS "${LIMINE_BUILD_STAMP}") + set(need_build TRUE) +endif() + +foreach(_bin limine limine-bios.sys limine-bios-cd.bin limine-uefi-cd.bin) + if(NOT EXISTS "${LIMINE_BIN_DIR}/${_bin}") + set(need_build TRUE) + endif() +endforeach() + +if(need_build) + if(_limine_makefile_missing) + cl_log_warn("limine Makefile missing, skip limine build and use existing artifacts only") + else() + cl_log_info("building limine") + run_cmd(COMMAND "${MAKE_TOOL}" -C "${LIMINE_DIR}") + file(WRITE "${LIMINE_BUILD_STAMP}" "built\n") + endif() +else() + cl_log_info("limine already built, skipping compile") +endif() + +foreach(_required limine limine-bios.sys limine-bios-cd.bin limine-uefi-cd.bin) + if(NOT EXISTS "${LIMINE_BIN_DIR}/${_required}") + cl_log_error("${_required} missing in ${LIMINE_BIN_DIR}") + endif() +endforeach() + +cl_log_info("limine artifacts ready") \ No newline at end of file