diff --git a/.github/actions/cache-restore/action.yml b/.github/actions/cache-restore/action.yml index d4cc26ee801..a1eadc8d572 100644 --- a/.github/actions/cache-restore/action.yml +++ b/.github/actions/cache-restore/action.yml @@ -46,12 +46,18 @@ outputs: toolchain_prebuilt_primary_key: description: 'Primary key for the toolchain cache' value: ${{ steps.toolchain-prebuilt.outputs.cache-primary-key }} + jakt_prebuilt_primary_key: + description: 'Primary key for the jakt toolchain cache' + value: ${{ steps.jakt-prebuilt.outputs.cache-primary-key }} toolchain_prebuilt_path: description: 'Path to the prebuilt toolchain' value: ${{ steps.toolchain-stamps.outputs.toolchain_path }} toolchain_prebuilt_hit: description: 'Whether the toolchain prebuilt cache was hit' value: ${{ steps.toolchain-prebuilt.outputs.cache-hit }} + jakt_prebuilt_hit: + description: 'Whether the jakt prebuilt cache was hit' + value: ${{ steps.jakt-prebuilt.outputs.cache-hit }} runs: using: "composite" @@ -93,6 +99,13 @@ runs: path: ${{ steps.toolchain-stamps.outputs.toolchain_path }} key: '"toolchain" | "${{ inputs.arch }}" | "${{ inputs.toolchain }}" | "${{ steps.toolchain-stamps.outputs.toolchain_stamp }}"' + - name: 'Jakt Toolchain Prebuilt Cache' + uses: actions/cache/restore@v4 + id: 'jakt-prebuilt' + with: + path: ${{ github.workspace }}/Toolchain/Local/jakt + key: toolchain-${{ runner.os }}"-jakt-toolchain-${{ matrix.arch }}-${{ steps.toolchain-stamps.outputs.toolchain_stamp }}" + - name: 'Toolchain Compiler Cache' uses: actions/cache/restore@v4 id: 'toolchain-ccache' diff --git a/.github/actions/cache-save/action.yml b/.github/actions/cache-save/action.yml index b06b1779c40..fdfd642c890 100644 --- a/.github/actions/cache-save/action.yml +++ b/.github/actions/cache-save/action.yml @@ -35,6 +35,14 @@ inputs: description: 'Whether the toolchain prebuilt cache was hit' required: false default: false + jakt_prebuilt_primary_key: + description: 'Primary key for the jakt prebuilt cache' + required: false + default: '' + jakt_prebuilt_hit: + description: 'Whether the jakt prebuilt cache was hit' + required: false + default: false runs: using: "composite" @@ -48,6 +56,13 @@ runs: path: ${{ inputs.toolchain_prebuilt_path }} key: ${{ inputs.toolchain_prebuilt_primary_key }} + - name: 'Jakt Toolchain Prebuilt Cache' + uses: actions/cache/save@v4 + if: ${{ github.event_name != 'pull_request' && !inputs.jakt_prebuilt_hit }} + with: + path: ${{ inputs.jakt_prebuilt_path }} + key: ${{ inputs.jakt_prebuilt_primary_key }} + - name: 'Toolchain Compiler Cache' uses: actions/cache/save@v4 if: ${{ github.event_name != 'pull_request' && inputs.toolchain_ccache_path != '' }} diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index bc56ebaff90..e3291a465bc 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -29,7 +29,10 @@ runs: sudo add-apt-repository ppa:ubuntu-toolchain-r/test sudo apt-get update - sudo apt-get install ccache clang-18 clang++-18 clang-format-18 lld-18 gcc-13 g++-13 libstdc++-13-dev ninja-build unzip qt6-base-dev qt6-tools-dev-tools libqt6svg6-dev qt6-multimedia-dev libgl1-mesa-dev libpulse-dev libssl-dev libegl1-mesa-dev + sudo apt-get install ccache clang-18 clang++-18 clang-format-18 lld-18 gcc-13 g++-13 libstdc++-13-dev ninja-build unzip qt6-base-dev qt6-tools-dev-tools libqt6svg6-dev qt6-multimedia-dev libgl1-mesa-dev libpulse-dev libssl-dev libegl1-mesa-dev libclang-18-dev + + sudo apt-get remove clang-{13,14,15} clang++-{13,14,15} libclang-{13,14,15}-dev llvm-{13,14,15} + sudo apt-get install clang-18 clang++-18 llvm-18 llvm-18-dev sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-18 100 sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-18 100 @@ -65,7 +68,9 @@ runs: sudo add-apt-repository ppa:ubuntu-toolchain-r/test sudo apt-get update + sudo apt-get remove clang-{13,14,15} clang++-{13,14,15} libclang-{13,14,15}-dev llvm-{13,14,15} sudo apt-get install clang-format-18 ccache e2fsprogs gcc-13 g++-13 libstdc++-13-dev libmpfr-dev libmpc-dev ninja-build optipng qemu-utils qemu-system-i386 unzip generate-ninja libegl1-mesa-dev + sudo apt-get install clang-18 clang++-18 llvm-18 llvm-18-dev - name: Enable KVM group perms if: ${{ inputs.os == 'Serenity' }} diff --git a/.github/workflows/serenity-template.yml b/.github/workflows/serenity-template.yml index 28b0f78aca4..a8f629566a5 100644 --- a/.github/workflows/serenity-template.yml +++ b/.github/workflows/serenity-template.yml @@ -84,6 +84,10 @@ jobs: ARCH: ${{ inputs.arch}} CCACHE_DIR: ${{ env.TOOLCHAIN_CCACHE_DIR }} + - name: Build jakt toolchain + if: ${{ !steps.cache-restore.output.jakt_prebuilt_hit }} + run: ARCH="${{ matrix.arch }}" CXX=g++-13 ${{ github.workspace }}/Toolchain/BuildJakt.sh + - name: Create Build Environment if: ${{ inputs.debug_options == 'ALL_DEBUG' }} # Build the entire project with all available debug options turned on, to prevent code rot. @@ -142,6 +146,8 @@ jobs: toolchain_prebuilt_path: ${{ steps.cache-restore.outputs.toolchain_prebuilt_path }} toolchain_prebuilt_primary_key: ${{ steps.cache-restore.outputs.toolchain_prebuilt_primary_key }} toolchain_prebuilt_hit: ${{ steps.cache-restore.outputs.toolchain_prebuilt_hit }} + jakt_prebuilt_primary_key: ${{ steps.cache-restore.outputs.jakt_prebuilt_primary_key }} + jakt_prebuilt_hit: ${{ steps.cache-restore.outputs.jakt_prebuilt_hit }} # === TEST === diff --git a/CMakeLists.txt b/CMakeLists.txt index a24dfc21375..c4ebe91c21f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,13 +49,6 @@ if (NOT HACKSTUDIO_BUILD) # Host tools, required to generate files for the build find_package(Lagom CONFIG REQUIRED) - - if (ENABLE_JAKT) - find_package(Jakt CONFIG REQUIRED - HINTS "${Lagom_DIR}/.." - ) - endif() - endif() # Meta target to run all code-gen steps in the build. @@ -190,7 +183,8 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR}/Userland/Services) include_directories(${CMAKE_CURRENT_BINARY_DIR}/Userland/Libraries) include_directories(${CMAKE_CURRENT_BINARY_DIR}/Userland) -add_custom_target(components ALL) +include(jakt) + option(BUILD_EVERYTHING "Build all optional components" ON) include(utils) diff --git a/Meta/CMake/common_options.cmake b/Meta/CMake/common_options.cmake index 858741c793f..c35f4cd08bf 100644 --- a/Meta/CMake/common_options.cmake +++ b/Meta/CMake/common_options.cmake @@ -26,8 +26,7 @@ serenity_option(ENABLE_ACCELERATED_GRAPHICS ON CACHE BOOL "Enable use of acceler serenity_option(HACKSTUDIO_BUILD OFF CACHE BOOL "Automatically enabled when building from HackStudio") -serenity_option(ENABLE_JAKT OFF CACHE BOOL "Enable building jakt files") -serenity_option(JAKT_SOURCE_DIR "" CACHE STRING "Pre-existing jakt language source directory") +serenity_option(ENABLE_JAKT ON CACHE BOOL "Enable building jakt files") serenity_option(SERENITY_CACHE_DIR "${PROJECT_BINARY_DIR}/../caches" CACHE PATH "Location of shared cache of downloaded files") serenity_option(ENABLE_NETWORK_DOWNLOADS ON CACHE BOOL "Allow downloads of required files. If OFF, required files must already be present in SERENITY_CACHE_DIR") diff --git a/Meta/CMake/jakt.cmake b/Meta/CMake/jakt.cmake index 2f813f278fe..d5b938828fb 100644 --- a/Meta/CMake/jakt.cmake +++ b/Meta/CMake/jakt.cmake @@ -1,20 +1,158 @@ -# -# Builds the jakt bootstrap compiler as a host tool for Lagom to compile files written in jakt -# - -include(FetchContent) - -FetchContent_Declare(jakt - GIT_REPOSITORY https://github.com/SerenityOS/jakt.git - GIT_TAG main - GIT_SHALLOW TRUE -) - -# Allow developers to skip download/update steps with local checkout -if (JAKT_SOURCE_DIR) - set(FETCHCONTENT_SOURCE_DIR_JAKT ${JAKT_SOURCE_DIR} CACHE PATH "Developer's pre-existing jakt source directory" FORCE) - message(STATUS "Using pre-existing JAKT_SOURCE_DIR: ${JAKT_SOURCE_DIR}") +if (NOT ENABLE_JAKT) + return() endif() -set(JAKT_BUILD_TESTING OFF) -FetchContent_MakeAvailable(jakt) +set(arch ${CMAKE_SYSTEM_PROCESSOR}) +if (arch STREQUAL "x86") + set(arch "i686") +endif() + +if (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") + set(vendor "apple") +else() + set(vendor "pc") +endif() + +if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows") + set(os "win32") +elseif (${CMAKE_SYSTEM_NAME} STREQUAL "SerenityOS") + set(os "serenity") +else() + string(TOLOWER ${CMAKE_SYSTEM_NAME} os) +endif() + +set(JAKT_COMPILER "${SerenityOS_SOURCE_DIR}/Toolchain/Local/jakt/bin/jakt") +set(JAKT_TARGET_TRIPLE ${arch}-${vendor}-${os}-unknown) +set(JAKT_LIBRARY_BASE "${CMAKE_SYSROOT}/usr/local/lib") +set(JAKT_LIBRARY_DIR "${JAKT_LIBRARY_BASE}/${JAKT_TARGET_TRIPLE}") +set(JAKT_INCLUDE_DIR "${CMAKE_SYSROOT}/usr/local/include/runtime") +cmake_host_system_information(RESULT JAKT_PROCESSOR_COUNT QUERY NUMBER_OF_PHYSICAL_CORES) +set_property(GLOBAL PROPERTY JOB_POOLS jakt_pool=1) + +message(STATUS "Using jakt compiler at ${JAKT_COMPILER}") +message(STATUS "Using jakt target triple ${JAKT_TARGET_TRIPLE}") +message(STATUS "Using jakt library directory ${JAKT_LIBRARY_DIR}") + +function(add_jakt_executable target source) + cmake_parse_arguments(PARSE_ARGV 2 JAKT_EXECUTABLE "" "" "INCLUDES;CONFIGS;LINK_LIBRARIES;EXTRA_CPP_SOURCES") + set(configs) + set(includes) + set(extra_cpp_sources) + + foreach(config IN LISTS JAKT_EXECUTABLE_CONFIGS) + list(APPEND configs "--config" "${config}") + endforeach() + foreach(include IN LISTS JAKT_EXECUTABLE_INCLUDES) + list(APPEND includes "-I" "${include}") + endforeach() + foreach(source IN LISTS JAKT_EXECUTABLE_EXTRA_CPP_SOURCES) + get_filename_component(source ${source} ABSOLUTE) + list(APPEND extra_cpp_sources "-X" "${source}") + endforeach() + + set(JAKT_EXECUTABLE_NAME ${target}) + set(EXECUTABLE_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${JAKT_EXECUTABLE_NAME}.dir") + set(EMPTY_SOURCE_FILE "${EXECUTABLE_BINARY_DIR}/empty_cmake_hack.cpp") + set(main_output_name "${JAKT_EXECUTABLE_NAME}.a") + set(main_output "${EXECUTABLE_BINARY_DIR}/build/${main_output_name}") + set(depfile "${EXECUTABLE_BINARY_DIR}/build/${main_output_name}.M") + + cmake_path(ABSOLUTE_PATH source + BASE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + OUTPUT_VARIABLE source_abs + ) + + add_custom_command( + OUTPUT ${main_output} + COMMAND ${JAKT_COMPILER} + -d -O -J ${JAKT_PROCESSOR_COUNT} + --ak-is-my-only-stdlib + -T ${JAKT_TARGET_TRIPLE} + --binary-dir "${EXECUTABLE_BINARY_DIR}/build" + --exports-dir "${CMAKE_CURRENT_BINARY_DIR}" + -C ${CMAKE_CXX_COMPILER} + --archiver ${CMAKE_AR} + --link-archive ${main_output_name} + --runtime-library-path ${JAKT_LIBRARY_BASE} + --dep-file ${depfile} + --extra-cpp-flag --sysroot=${CMAKE_SYSROOT} + ${configs} + ${includes} + ${extra_cpp_sources} + "${source_abs}" + DEPFILE ${depfile} + DEPENDS + ${JAKT_EXECUTABLE_EXTRA_CPP_SOURCES} + COMMENT "Jakt: Compile ${source}" + JOB_POOL jakt_pool + ) + add_custom_target(${JAKT_EXECUTABLE_NAME}.t + DEPENDS ${main_output} + ) + + add_custom_command( + OUTPUT ${EMPTY_SOURCE_FILE} + COMMAND ${CMAKE_COMMAND} -E touch ${EMPTY_SOURCE_FILE} + ) + + add_executable(${JAKT_EXECUTABLE_NAME} ${EMPTY_SOURCE_FILE}) + add_dependencies(${JAKT_EXECUTABLE_NAME} ${JAKT_EXECUTABLE_NAME}.t) + + add_library(${JAKT_EXECUTABLE_NAME}.lib STATIC IMPORTED) + set_property(TARGET ${JAKT_EXECUTABLE_NAME}.lib PROPERTY IMPORTED_LOCATION ${main_output}) + + target_link_libraries(${JAKT_EXECUTABLE_NAME} + PRIVATE + ${JAKT_LIBRARY_DIR}/libjakt_main_${JAKT_TARGET_TRIPLE}.a + ${JAKT_EXECUTABLE_NAME}.lib + ${JAKT_LIBRARY_DIR}/libjakt_runtime_${JAKT_TARGET_TRIPLE}.a + ${JAKT_EXECUTABLE_LINK_LIBRARIES} + ) +endfunction() + +function(serenity_jakt_app target_name source) + cmake_parse_arguments(PARSE_ARGV 2 SERENITY_JAKT_EXECUTABLE "" "" "INCLUDES;CONFIGS;LINK_LIBRARIES") + add_jakt_executable(${target_name} ${source} + INCLUDES + ${INCLUDE_DIRECTORIES} + ${PROJECT_SOURCE_DIR} + ${PROJECT_SOURCE_DIR}/Userland/Libraries + ${PROJECT_SOURCE_DIR}/Userland/Libraries/LibCrypt + ${PROJECT_SOURCE_DIR}/Userland/Libraries/LibSystem + ${PROJECT_SOURCE_DIR}/Userland/Services + ${PROJECT_SOURCE_DIR}/Userland + ${PROJECT_BINARY_DIR} + ${PROJECT_BINARY_DIR}/Userland/Services + ${PROJECT_BINARY_DIR}/Userland/Libraries + ${PROJECT_BINARY_DIR}/Userland + ${SERENITY_JAKT_EXECUTABLE_INCLUDES} + CONFIGS ${SERENITY_JAKT_EXECUTABLE_CONFIGS} + LINK_LIBRARIES ${SERENITY_JAKT_EXECUTABLE_LINK_LIBRARIES} + ) + set_target_properties(${target_name} PROPERTIES EXCLUDE_FROM_ALL TRUE) + install(TARGETS ${target_name} RUNTIME DESTINATION bin OPTIONAL) + serenity_generated_sources(${target_name}) + serenity_set_implicit_links(${target_name}) +endfunction(serenity_jakt_app) + +function(serenity_jakt_executable target_name) + cmake_parse_arguments(PARSE_ARGV 1 SERENITY_JAKT_EXECUTABLE "" "" "MAIN_SOURCE;CPP_SOURCES;CONFIGS;LINK_LIBRARIES") + add_jakt_executable(${target_name} ${SERENITY_JAKT_EXECUTABLE_MAIN_SOURCE} + INCLUDES + ${INCLUDE_DIRECTORIES} + ${PROJECT_SOURCE_DIR} + ${PROJECT_SOURCE_DIR}/Userland/Libraries + ${PROJECT_SOURCE_DIR}/Userland/Libraries/LibCrypt + ${PROJECT_SOURCE_DIR}/Userland/Libraries/LibSystem + ${PROJECT_SOURCE_DIR}/Userland/Services + ${PROJECT_SOURCE_DIR}/Userland + ${PROJECT_BINARY_DIR} + ${PROJECT_BINARY_DIR}/Userland/Services + ${PROJECT_BINARY_DIR}/Userland/Libraries + ${PROJECT_BINARY_DIR}/Userland + ${SERENITY_JAKT_EXECUTABLE_INCLUDES} + CONFIGS ${SERENITY_JAKT_EXECUTABLE_CONFIGS} + LINK_LIBRARIES ${SERENITY_JAKT_EXECUTABLE_LINK_LIBRARIES} + EXTRA_CPP_SOURCES ${SERENITY_JAKT_EXECUTABLE_CPP_SOURCES} + ) +endfunction() diff --git a/Meta/CMake/serenity_components.cmake b/Meta/CMake/serenity_components.cmake index d7056aacbee..af6605425eb 100644 --- a/Meta/CMake/serenity_components.cmake +++ b/Meta/CMake/serenity_components.cmake @@ -7,6 +7,7 @@ define_property(TARGET PROPERTY SERENITY_COMPONENT_DESCRIPTION BRIEF_DOCS "Seren define_property(TARGET PROPERTY SERENITY_COMPONENT_RECOMMENDED BRIEF_DOCS "SerenityOS component recommended (flag)" FULL_DOCS "-") define_property(TARGET PROPERTY SERENITY_COMPONENT_REQUIRED BRIEF_DOCS "SerenityOS component required (flag)" FULL_DOCS "-") define_property(TARGET PROPERTY SERENITY_COMPONENT_DEPENDS BRIEF_DOCS "SerenityOS component dependencies" FULL_DOCS "-") +define_property(TARGET PROPERTY SERENITY_SKIP_BUILD BRIEF_DOCS "SerenityOS component target should skip build (flag)" FULL_DOCS "-") function(serenity_component name) cmake_parse_arguments(PARSE_ARGV 1 SERENITY_COMPONENT "RECOMMENDED;REQUIRED" "DESCRIPTION" "TARGETS;DEPENDS") @@ -21,14 +22,19 @@ function(serenity_component name) SERENITY_COMPONENT_REQUIRED "${SERENITY_COMPONENT_REQUIRED}" SERENITY_COMPONENT_DEPENDS "${SERENITY_COMPONENT_DEPENDS}" ) + set(static_should_disable_target TRUE) + if(BUILD_EVERYTHING OR "${BUILD_${NAME_UPPER}}" OR SERENITY_COMPONENT_REQUIRED) + set(static_should_disable_target FALSE) + endif() + + set(should_disable_target $) if(NOT "${SERENITY_COMPONENT_TARGETS}" STREQUAL "") # note: one component is /bin/false, which makes this tricky foreach(target IN LISTS SERENITY_COMPONENT_TARGETS) add_dependencies("Component${name}" "${target}") + set(should_disable_target $>>) endforeach() endif() - if(BUILD_EVERYTHING OR "${BUILD_${NAME_UPPER}}" OR SERENITY_COMPONENT_REQUIRED) - add_dependencies(components "Component${name}") - endif() + set_target_properties("Component${name}" PROPERTIES EXCLUDE_FROM_ALL ${should_disable_target}) foreach(dependency IN LISTS SERENITY_COMPONENT_DEPENDS) add_dependencies("Component${name}" "Component${dependency}") endforeach() diff --git a/Meta/CMake/utils.cmake b/Meta/CMake/utils.cmake index cc224bd3681..97b91fe5371 100644 --- a/Meta/CMake/utils.cmake +++ b/Meta/CMake/utils.cmake @@ -77,10 +77,44 @@ function(serenity_libc target_name fs_name) serenity_generated_sources(${target_name}) endfunction() +if (NOT COMMAND serenity_add_executable) + function(serenity_add_executable name) + # if any of the sources are .jakt files, switch to using serenity_jakt_executable() + # and pass the non-jakt sources as extra cpp sources. + set(jakt_source) + set(cpp_sources) + foreach(source ${ARGN}) + if (${source} MATCHES "\\.jakt$") + if (jakt_source) + message(FATAL_ERROR "Multiple main jakt files in executable ${name}") + else() + set(jakt_source ${source}) + endif() + else() + list(APPEND cpp_sources ${source}) + endif() + endforeach() + + if (jakt_source) + if (ENABLE_JAKT) + serenity_jakt_executable(${name} MAIN_SOURCE ${jakt_source} CPP_SOURCES ${cpp_sources}) + else() + message(STATUS "Skipping ${name} because jakt is disabled") + # Make an empty c++ file: + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/${name}.cpp" "// This file was generated by CMake to skip a jakt executable\n") + add_executable(${name} "${CMAKE_CURRENT_BINARY_DIR}/${name}.cpp") + set_target_properties(${name} PROPERTIES SERENITY_SKIP_BUILD TRUE) + endif() + else() + add_executable(${name} ${cpp_sources}) + endif() + endfunction() +endif() + if (NOT COMMAND serenity_bin) function(serenity_bin target_name) serenity_install_sources() - add_executable(${target_name} ${SOURCES}) + serenity_add_executable(${target_name} ${SOURCES}) target_link_libraries(${target_name} PUBLIC GenericClangPlugin) set_target_properties(${target_name} PROPERTIES EXCLUDE_FROM_ALL TRUE) install(TARGETS ${target_name} RUNTIME DESTINATION bin OPTIONAL) @@ -126,10 +160,11 @@ endfunction() function(serenity_app target_name) cmake_parse_arguments(PARSE_ARGV 1 SERENITY_APP "" "ICON" "") - serenity_bin("${target_name}") set(small_icon "${SerenityOS_SOURCE_DIR}/Base/res/icons/16x16/${SERENITY_APP_ICON}.png") set(medium_icon "${SerenityOS_SOURCE_DIR}/Base/res/icons/32x32/${SERENITY_APP_ICON}.png") + serenity_bin("${target_name}") + if (EXISTS "${small_icon}") embed_resource("${target_name}" serenity_icon_s "${small_icon}") else() @@ -160,7 +195,19 @@ function(embed_resource target section file) DEPENDS "${input_file}" "${SerenityOS_SOURCE_DIR}/Meta/generate-embedded-resource-assembly.sh" COMMENT "Generating ${asm_file}" ) - target_sources("${target}" PRIVATE "${asm_file}") + + add_library( + ${target}-resources-${section} + OBJECT + "${asm_file}" + ) + + target_link_libraries( + ${target} PRIVATE + -Wl,--whole-archive + ${target}-resources-${section} + -Wl,--no-whole-archive + ) endfunction() function(remove_path_if_version_changed version version_file cache_path) diff --git a/Meta/Lagom/CMakeLists.txt b/Meta/Lagom/CMakeLists.txt index 2745b583a65..bbdecd5985c 100644 --- a/Meta/Lagom/CMakeLists.txt +++ b/Meta/Lagom/CMakeLists.txt @@ -312,7 +312,6 @@ macro(add_serenity_subdirectory path) add_subdirectory("${SERENITY_PROJECT_ROOT}/${path}" "${CMAKE_CURRENT_BINARY_DIR}/${path}") endmacro() -add_custom_target(components ALL) option(BUILD_EVERYTHING "Build all optional components" ON) if (NOT TARGET all_generated) diff --git a/Meta/Lagom/Tools/CMakeLists.txt b/Meta/Lagom/Tools/CMakeLists.txt index 11016d41144..1c2f9b193fd 100644 --- a/Meta/Lagom/Tools/CMakeLists.txt +++ b/Meta/Lagom/Tools/CMakeLists.txt @@ -15,6 +15,4 @@ add_subdirectory(CodeGenerators) add_subdirectory(ConfigureComponents) add_subdirectory(IPCMagicLinter) -if (ENABLE_JAKT) - include(jakt) -endif() +include(jakt) diff --git a/Meta/serenity.sh b/Meta/serenity.sh index 3ebe5cfcc29..7160d5035d9 100755 --- a/Meta/serenity.sh +++ b/Meta/serenity.sh @@ -169,7 +169,9 @@ cmd_with_target() { else TOOLCHAIN_DIR="$SERENITY_SOURCE_DIR/Toolchain/Local/$TARGET_TOOLCHAIN/$TARGET" fi + JAKT_TOOLCHAIN_DIR="$SERENITY_SOURCE_DIR/Toolchain/Local/jakt" SUPER_BUILD_DIR="$SERENITY_SOURCE_DIR/Build/superbuild-$TARGET$TARGET_TOOLCHAIN" + JAKT_LIB_DIR="$BUILD_DIR/Root/usr/local/lib/$TARGET-pc-serenity-unknown" else SUPER_BUILD_DIR="$BUILD_DIR" CMAKE_ARGS+=("-DCMAKE_INSTALL_PREFIX=$SERENITY_SOURCE_DIR/Build/lagom-install") @@ -250,6 +252,18 @@ build_toolchain() { fi } +build_jakt() { + [ -z "$JAKT_TOOLCHAIN_DIR" ] && return + echo "build_jakt: $JAKT_TOOLCHAIN_DIR" + ( cd "$SERENITY_SOURCE_DIR/Toolchain" && ./BuildJakt.sh ) +} + +ensure_jakt() { + if ! [ -d "$JAKT_TOOLCHAIN_DIR" ] || ! [ -d "$JAKT_LIB_DIR" ]; then + build_jakt + fi +} + ensure_toolchain() { if [ "$(cmake -P "$SERENITY_SOURCE_DIR"/Meta/CMake/cmake-version.cmake)" -ne 1 ]; then build_cmake @@ -269,6 +283,7 @@ ensure_toolchain() { fi fi + ensure_jakt } confirm_rebuild_if_toolchain_exists() { diff --git a/Toolchain/BuildJakt.sh b/Toolchain/BuildJakt.sh new file mode 100755 index 00000000000..b7eb86ecdf0 --- /dev/null +++ b/Toolchain/BuildJakt.sh @@ -0,0 +1,232 @@ +#!/usr/bin/env bash +set -eo pipefail +# This file will need to be run in bash, for now. + +# === CONFIGURATION AND SETUP === + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +# shellcheck source=/dev/null +. "${DIR}/../Meta/shell_include.sh" + +exit_if_running_as_root "Do not run BuildJakt.sh as root, your Build directory will become root-owned" + +echo "$DIR" + +ARCHES=("x86_64" "aarch64" "riscv64") +PREFIX="$DIR/Local/jakt" + +VALID_TOOLCHAINS=() + +declare -A CXX_GNU=() +declare -A CXX_CLANG=() +declare -A RANLIB_GNU=() +declare -A RANLIB_CLANG=() +declare -A BUILD_GNU=() +declare -A BUILD_CLANG=() +: "${BUILD_GNU[@]}" "${BUILD_CLANG[@]}" # make shellcheck understand that these might be used. + +for ARCH in "${ARCHES[@]}"; do + TARGET="$ARCH-pc-serenity" + + BUILD_GNU["$ARCH"]="$DIR/../Build/$ARCH" + BUILD_CLANG["$ARCH"]="$DIR/../Build/${ARCH}clang" + + CXX_GNU["$ARCH"]="$DIR/Local/$ARCH/bin/$TARGET-g++" + CXX_CLANG["$ARCH"]="$DIR/Local/clang/bin/$TARGET-clang++" + + # Indirectly accessed below. + # shellcheck disable=SC2034 + RANLIB_GNU["$ARCH"]="$DIR/Local/$ARCH/bin/$TARGET-ranlib" + # shellcheck disable=SC2034 + RANLIB_CLANG["$ARCH"]="$DIR/Local/clang/bin/llvm-ranlib" + + if [ -x "${CXX_GNU["$ARCH"]}" ]; then + VALID_TOOLCHAINS+=("GNU;${ARCH}") + fi + + if [ -x "${CXX_CLANG[${ARCH}]}" ]; then + VALID_TOOLCHAINS+=("CLANG;${ARCH}") + fi +done + +if [ "${#VALID_TOOLCHAINS}" -eq 0 ]; then + die "Need to build at least one C++ toolchain (either GNU or Clang) before BuildJakt.sh can succeed" +fi + +REALPATH="realpath" +SYSTEM_NAME="$(uname -s)" + +NPROC=$(get_number_of_processing_units) + +if [ "$SYSTEM_NAME" = "OpenBSD" ]; then + REALPATH="readlink -f" + export CXX=eg++ +fi + +if command -v ginstall &>/dev/null; then + INSTALL=ginstall +else + INSTALL=install +fi + +buildstep() { + NAME=$1 + shift + "$@" 2>&1 | sed $'s|^|\x1b[34m['"${NAME}"$']\x1b[39m |' +} + +buildstep_ninja() { + # When ninja writes to a pipe, it strips ANSI escape codes and prints one line per buildstep. + # Instead, use NINJA_STATUS so that we get colored output from LLVM's build and fewer lines of output when running in a tty. + # ANSI escape codes in NINJA_STATUS are slightly janky (ninja thinks that "\e[34m" needs 5 output characters instead of 5, so + # its middle elision is slightly off; also it would happily elide the "\e39m" which messes up the output if the terminal is too + # narrow), but it's still working better than the alternative. + NAME=$1 + shift + env NINJA_STATUS=$'\e[34m['"${NAME}"$']\e[39m [%f/%t] ' "$@" +} + +echo PREFIX is "$PREFIX" + +mkdir -p "$DIR/Tarballs" + +JAKT_COMMIT_HASH="714b634cf1de81e38a99608c3310157688cd9125" +JAKT_NAME="jakt-${JAKT_COMMIT_HASH}" +JAKT_TARBALL="${JAKT_NAME}.tar.gz" +JAKT_GIT_URL="https://github.com/serenityos/jakt" + +# === DEPENDENCIES === +buildstep dependencies echo "Checking whether 'ninja' is available..." +if ! command -v "${NINJA:-ninja}" >/dev/null; then + buildstep dependencies echo "Please make sure to install Ninja (for the '${NINJA:-ninja}' tool)." + exit 1 +fi + +buildstep dependencies echo "Checking whether 'cmake' is available..." +if ! command -v cmake >/dev/null; then + buildstep dependencies echo "Please make sure to install CMake (for the 'cmake' tool)." + exit 1 +fi + +buildstep dependencies echo "Checking whether your C++ compiler works..." +if ! ${CXX:-c++} -o /dev/null -xc++ - >/dev/null <<'PROGRAM' +int main() {} +PROGRAM +then + buildstep dependencies echo "Please make sure to install a working C++ compiler." + exit 1 +fi + +# === DOWNLOAD AND PATCH === +pushd "$DIR/Tarballs" + if [ ! -d "${JAKT_NAME}" ]; then + echo "Downloading jakt..." + rm -f "$JAKT_TARBALL" + curl -L --output "$JAKT_TARBALL" "$JAKT_GIT_URL/archive/$JAKT_COMMIT_HASH.tar.gz" + + echo "Extracting jakt..." + tar -xzf "${JAKT_TARBALL}" + else + echo "Using existing Jakt source directory" + fi +popd + +# === COMPILE AND INSTALL === +rm -rf "$PREFIX" +mkdir -p "$PREFIX" + +rm -rf "$DIR/Build/jakt" +mkdir -p "$DIR/Build/jakt" +pushd "$DIR/Build/jakt" + echo "XXX configure jakt" + buildstep "jakt/configure" cmake -S "$DIR/Tarballs/${JAKT_NAME}" -B . \ + -DSERENITY_SOURCE_DIR="$DIR/.." \ + -DCMAKE_INSTALL_PREFIX="$PREFIX" \ + -DCMAKE_BUILD_TYPE=Release \ + -GNinja \ + || exit 1 + + echo "XXX build jakt" + buildstep_ninja "jakt/build" ninja jakt_stage1 + echo "XXX install jakt" + buildstep_ninja "jakt/install" ninja install +popd + +build_for() { + TOOLCHAIN="$1" + ARCH="$2" + + TARGET="$ARCH-pc-serenity" + JAKT_TARGET="$TARGET-unknown" + + current_build="BUILD_${TOOLCHAIN}[${ARCH}]" + current_cxx="CXX_${TOOLCHAIN}[${ARCH}]" + current_ranlib="RANLIB_${TOOLCHAIN}[${ARCH}]" + + BUILD="${!current_build}" + TARGET_CXX="${!current_cxx}" + TARGET_RANLIB="${!current_ranlib}" + + # On at least OpenBSD, the path must exist to call realpath(3) on it + if [ ! -d "$BUILD" ]; then + mkdir -p "$BUILD" + fi + BUILD=$($REALPATH "$BUILD") + echo "XXX building jakt support libs in $BUILD with $TARGET_CXX" + + SYSROOT="$BUILD/Root" + echo SYSROOT is "$SYSROOT" + + rm -rf "$DIR/Build/jakt-$TOOLCHAIN-$ARCH" + mkdir -p "$DIR/Build/jakt-$TOOLCHAIN-$ARCH" + pushd "$DIR/Build/jakt-$TOOLCHAIN-$ARCH" + echo "XXX serenity libc headers" + mkdir -p "$BUILD" + pushd "$BUILD" + mkdir -p Root/usr/include/ + SRC_ROOT=$($REALPATH "$DIR"/..) + FILES=$(find \ + "$SRC_ROOT"/AK \ + "$SRC_ROOT"/Kernel/API \ + "$SRC_ROOT"/Kernel/Arch \ + "$SRC_ROOT"/Userland/Libraries/LibC \ + "$SRC_ROOT"/Userland/Libraries/LibELF/ELFABI.h \ + "$SRC_ROOT"/Userland/Libraries/LibRegex/RegexDefs.h \ + -name '*.h' -print) + for header in $FILES; do + target=$(echo "$header" | sed \ + -e "s|$SRC_ROOT/AK/|AK/|" \ + -e "s|$SRC_ROOT/Userland/Libraries/LibC||" \ + -e "s|$SRC_ROOT/Kernel/|Kernel/|" \ + -e "s|$SRC_ROOT/Userland/Libraries/LibELF/|LibELF/|" \ + -e "s|$SRC_ROOT/Userland/Libraries/LibRegex/|LibRegex/|") + buildstep "system_headers" mkdir -p "$(dirname "Root/usr/include/$target")" + buildstep "system_headers" "$INSTALL" "$header" "Root/usr/include/$target" + done + unset SRC_ROOT + popd + + echo "XXX build jakt support libs" + rm -fr "$SYSROOT/usr/local/lib/$JAKT_TARGET" + + buildstep "jakt/support/build/$TOOLCHAIN" "$PREFIX/bin/jakt" cross \ + --only-support-libs \ + --install-root "$SYSROOT/usr/local" \ + --target-triple "$JAKT_TARGET" \ + --target-links-ak \ + -C "$TARGET_CXX" \ + -O \ + -J "$NPROC" + + buildstep "jakt/support/build/$TOOLCHAIN/ranlib" "$TARGET_RANLIB" "$SYSROOT/usr/local/lib/$JAKT_TARGET/libjakt_runtime_$JAKT_TARGET.a" + buildstep "jakt/support/build/$TOOLCHAIN/ranlib" "$TARGET_RANLIB" "$SYSROOT/usr/local/lib/$JAKT_TARGET/libjakt_main_$JAKT_TARGET.a" + popd +} + +for TOOLCHAIN_AND_ARCH in "${VALID_TOOLCHAINS[@]}"; do + IFS=';' read -r TOOLCHAIN ARCH <<< "$TOOLCHAIN_AND_ARCH" + buildstep "build/$TOOLCHAIN/$ARCH" build_for "$TOOLCHAIN" "$ARCH" +done + +echo "Done creating jakt toolchain for targets " "${VALID_TOOLCHAINS[@]}" diff --git a/Userland/Utilities/CMakeLists.txt b/Userland/Utilities/CMakeLists.txt index 74029d48040..8d6b00cc8bc 100644 --- a/Userland/Utilities/CMakeLists.txt +++ b/Userland/Utilities/CMakeLists.txt @@ -1,4 +1,11 @@ -file(GLOB CMD_SOURCES CONFIGURE_DEPENDS "*.cpp") +file(GLOB CMD_SOURCES_CPP CONFIGURE_DEPENDS "*.cpp") +file(GLOB CMD_SOURCES_JAKT CONFIGURE_DEPENDS "*.jakt") +if (ENABLE_JAKT) + list(APPEND CMD_SOURCES ${CMD_SOURCES_CPP} ${CMD_SOURCES_JAKT}) +else() + list(APPEND CMD_SOURCES ${CMD_SOURCES_CPP}) +endif() + list(APPEND SPECIAL_TARGETS test install) list(APPEND REQUIRED_TARGETS arp base64 basename cat chmod chown clear comm cp cut date dd df diff dirname dmesg du echo env expr false @@ -15,6 +22,7 @@ list(APPEND RECOMMENDED_TARGETS foreach(CMD_SRC ${CMD_SOURCES}) get_filename_component(CMD_NAME ${CMD_SRC} NAME_WLE) + get_filename_component(CMD_EXT ${CMD_SRC} LAST_EXT) if (CMD_NAME IN_LIST SPECIAL_TARGETS) set(TARGET_NAME "${CMD_NAME}-bin") else() @@ -39,10 +47,14 @@ foreach(CMD_SRC ${CMD_SOURCES}) ) endif() - add_executable(${TARGET_NAME} ${CMD_SRC}) - target_link_libraries(${TARGET_NAME} PRIVATE LibCore LibMain) + serenity_add_executable(${TARGET_NAME} ${CMD_SRC}) serenity_set_implicit_links(${TARGET_NAME}) set_target_properties(${TARGET_NAME} PROPERTIES EXCLUDE_FROM_ALL TRUE) + target_link_libraries(${TARGET_NAME} PRIVATE LibCore) + if(NOT ${CMD_EXT} EQUAL jakt) + target_link_libraries(${TARGET_NAME} PRIVATE LibMain) + endif() + install(TARGETS ${TARGET_NAME} RUNTIME DESTINATION bin OPTIONAL) if (CMD_NAME IN_LIST SPECIAL_TARGETS) @@ -53,20 +65,6 @@ foreach(CMD_SRC ${CMD_SOURCES}) endif() endforeach() -if (ENABLE_JAKT) - add_jakt_executable(hello-jakt - MAIN_SOURCE hello-world.jakt - ) - target_link_libraries(hello-jakt PRIVATE LibC) - set_target_properties(hello-jakt PROPERTIES EXCLUDE_FROM_ALL TRUE) - install(TARGETS hello-jakt RUNTIME DESTINATION bin OPTIONAL) - serenity_component( - hello-jakt - RECOMMENDED - TARGETS hello-jakt - ) -endif() - install(CODE "file(CREATE_LINK grep ${CMAKE_INSTALL_PREFIX}/bin/egrep SYMBOLIC)") install(CODE "file(CREATE_LINK grep ${CMAKE_INSTALL_PREFIX}/bin/fgrep SYMBOLIC)") install(CODE "file(CREATE_LINK grep ${CMAKE_INSTALL_PREFIX}/bin/rgrep SYMBOLIC)") diff --git a/Userland/Utilities/hello-world.jakt b/Userland/Utilities/hello-world.jakt index f020e37a3b3..d0e0041fd4b 100644 --- a/Userland/Utilities/hello-world.jakt +++ b/Userland/Utilities/hello-world.jakt @@ -1,3 +1,3 @@ -function main() { +fn main() { println("Hello, World, from jakt!"); }