option(PSEMEK_PACKAGE_MODE "Enable packaging mode" OFF) set(PSEMEK_PACKAGE_OUTPUT_PATH "" CACHE STRING "Packaging output path") set(PSEMEK_PACKAGE_VERSION_SUFFIX "" CACHE STRING "Packaging version suffix") if(PSEMEK_PACKAGE_MODE) set(PSEMEK_PACKAGE_SUFFIX ${PSEMEK_PLATFORM} CACHE INTERNAL "Packaging suffix" FORCE) set(PSEMEK_PACKAGE_LINK_FLAGS_RAW) if(PSEMEK_PLATFORM_WEB) list(APPEND PSEMEK_PACKAGE_LINK_FLAGS_RAW -sUSE_SDL=2 -sUSE_FREETYPE=1 -sMIN_WEBGL_VERSION=2 -sNO_DISABLE_EXCEPTION_CATCHING -sFULL_ES3 -sWASMFS -sALLOW_MEMORY_GROWTH -sGROWABLE_ARRAYBUFFERS=0 -sASYNCIFY) if(PSEMEK_USE_FREETYPE) list(APPEND PSEMEK_PACKAGE_LINK_FLAGS_RAW -sUSE_FREETYPE=1) endif() list(APPEND PSEMEK_PACKAGE_LINK_FLAGS_RAW "-sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE='$autoResumeAudioContext','$dynCall'") if(CMAKE_BUILD_TYPE STREQUAL Debug) list(APPEND PSEMEK_PACKAGE_LINK_FLAGS_RAW "-g") endif() elseif((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")) list(APPEND PSEMEK_PACKAGE_LINK_FLAGS_RAW "-static-libgcc" "-static-libstdc++") endif() set(PSEMEK_PACKAGE_LINK_FLAGS ${PSEMEK_PACKAGE_LINK_FLAGS_RAW} CACHE INTERNAL "Packaging CXX flags" FORCE) set(PSEMEK_COPY_FILES_RAW) endif() function(psemek_package_output_path target outvar) if(NOT PSEMEK_PACKAGE_MODE) message(FATAL "psemek_package_output_path must only be called during packaging") endif() if(PSEMEK_PLATFORM_ANDROID) set(_PACKAGE_EXTENSION apk) else() set(_PACKAGE_EXTENSION zip) endif() set(${outvar} "${CMAKE_CURRENT_LIST_DIR}/${PSEMEK_PACKAGE_OUTPUT_PATH}/${target}${PSEMEK_PACKAGE_VERSION_SUFFIX}-${PSEMEK_PACKAGE_SUFFIX}.${_PACKAGE_EXTENSION}" PARENT_SCOPE) endfunction() function(psemek_add_executable_impl target is_application) if((NOT PSEMEK_PACKAGE_MODE) OR PSEMEK_PACKAGE_TARGET) if(${PSEMEK_PACKAGE_AS_LIBRARY} AND ${is_application}) add_library(${target} SHARED ${ARGN}) else() add_executable(${target} ${ARGN}) endif() target_link_libraries(${target} PUBLIC psemek) if(${is_application}) if(PSEMEK_PLATFORM_ANDROID) target_link_libraries(${target} PUBLIC "-Wl,--whole-archive $ -Wl,--no-whole-archive" ${PSEMEK_BACKEND_LIBRARY}) else() target_link_libraries(${target} PUBLIC ${PSEMEK_BACKEND_LIBRARY}) endif() if(PSEMEK_PLATFORM_WEB) target_link_options(${target} PRIVATE "--shell-file=${CMAKE_CURRENT_FUNCTION_LIST_DIR}/web/template.html" ) set_target_properties(${target} PROPERTIES SUFFIX ".html") endif() endif() install(TARGETS ${target}) if(PSEMEK_PACKAGE_MODE) target_link_options(${target} PUBLIC ${PSEMEK_PACKAGE_LINK_FLAGS}) target_compile_definitions(${target} PUBLIC "-DPSEMEK_PACKAGE_MODE") if(PSEMEK_PLATFORM_LINUX) set_target_properties(${target} PROPERTIES BUILD_RPATH "." ) endif() if(PSEMEK_PLATFORM_LINUX) add_custom_command(TARGET ${target} POST_BUILD COMMAND chrpath -r . $ ) endif() psemek_package_output_path(${target} _OUTPUT_PATH) if(NOT PSEMEK_PLATFORM_ANDROID) get_filename_component(_OUTPUT_DIRECTORY "${_OUTPUT_PATH}" DIRECTORY) set(_TARGET_FILE $) set(_TARGET_RUNTIME) if(PSEMEK_PLATFORM_WEB) set(_TARGET_PARENT $) set(_TARGET_STEM $) set(_TARGET_NOEXT "${_TARGET_PARENT}/${_TARGET_STEM}") list(APPEND _TARGET_RUNTIME ${_TARGET_NOEXT}.js) list(APPEND _TARGET_RUNTIME ${_TARGET_NOEXT}.wasm) list(APPEND _TARGET_RUNTIME ${_TARGET_NOEXT}.data) endif() set(_COPY_FILES ${_TARGET_FILE} ${_TARGET_RUNTIME} ${PSEMEK_PACKAGE_COPY_FILES}) if(PSEMEK_GRAPHICS_API STREQUAL WEBGPU) list(APPEND _COPY_FILES ${PSEMEK_PACKAGE_COPY_FILES_WGPU}) endif() if(PSEMEK_USE_FREETYPE) list(APPEND _COPY_FILES ${PSEMEK_PACKAGE_COPY_FILES_FREETYPE}) endif() add_custom_command(TARGET ${target} POST_BUILD COMMAND echo Packaging target ${target} into ${_OUTPUT_PATH} COMMAND mkdir -pv "${_OUTPUT_DIRECTORY}" COMMAND rm -f "${_OUTPUT_PATH}" COMMAND zip -v "${_OUTPUT_PATH}" -j ${_COPY_FILES} COMMAND echo Packaged target ${target} into ${_OUTPUT_PATH} ) else() if("${PSEMEK_APPLICATION_NAME}" STREQUAL "") message(FATAL_ERROR "PSEMEK_APPLICATION_NAME cannot be empty when building for android") endif() add_custom_command(TARGET ${target} POST_BUILD COMMAND ${PSEMEK_PACKAGE_HELPER} "${target}" "${PSEMEK_APPLICATION_NAME}" "${_OUTPUT_PATH}" ) endif() endif() endif() endfunction() function(psemek_add_executable target) psemek_add_executable_impl(${target} FALSE ${ARGN}) endfunction() function(psemek_add_application target) psemek_add_executable_impl(${target} TRUE ${ARGN}) endfunction() function(psemek_add_dev_application target) if(NOT PSEMEK_PACKAGE_MODE) psemek_add_application(${target} ${ARGN}) endif() endfunction() function(psemek_add_build_tool target link_psemek) if((NOT PSEMEK_PACKAGE_MODE) OR PSEMEK_PACKAGE_HOST) add_executable(${target} ${ARGN}) if(${link_psemek}) target_link_libraries(${target} PUBLIC psemek) endif() if(PSEMEK_PACKAGE_MODE) target_compile_definitions(${target} PUBLIC "-DPSEMEK_PACKAGE_MODE") install(TARGETS ${target} RUNTIME DESTINATION bin) endif() else() find_file(_LOCATION ${target} PATHS "${PSEMEK_PACKAGE_TOOLS_PATH}/bin" NO_CACHE) message(STATUS "Found ${target}: ${_LOCATION}") if(NOT _LOCATION) message(FATAL_ERROR "Could not locate ${target} build tool in ${PSEMEK_PACKAGE_TOOLS_PATH}") endif() add_executable(${target} IMPORTED GLOBAL) set_target_properties(${target} PROPERTIES IMPORTED_LOCATION ${_LOCATION}) endif() endfunction() function(psemek_add_dev_tool target link_psemek) if(NOT PSEMEK_PACKAGE_MODE) add_executable(${target} ${ARGN}) if(${link_psemek}) target_link_libraries(${target} PUBLIC psemek) endif() endif() endfunction() function(psemek_add_library target) add_library(${target} ${ARGN}) set(_GUARD_SOURCE_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/nosource_library_guard.cpp") target_sources(${target} PRIVATE ${_GUARD_SOURCE_LOCATION}) if(NOT (EXISTS "${_GUARD_SOURCE_LOCATION}")) file(TOUCH "${_GUARD_SOURCE_LOCATION}") endif() if(PSEMEK_PACKAGE_MODE) target_compile_definitions(${target} PUBLIC "-DPSEMEK_PACKAGE_MODE") endif() endfunction() function(psemek_add_winrc target path) if(PSEMEK_PLATFORM_WINDOWS AND (TARGET ${target})) get_target_property(OUT_DIR ${target} BINARY_DIR) set(_IN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/${path}") file(MAKE_DIRECTORY "${OUT_DIR}/winrc") set(_OUT_FILE "${OUT_DIR}/winrc/res.o") add_custom_command(OUTPUT "${_OUT_FILE}" COMMAND ${CMAKE_RC_COMPILER} "${_IN_FILE}" "${_OUT_FILE}" DEPENDS psemek-resource-compiler "${_IN_FILE}" WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") target_sources(${target} PRIVATE "${_OUT_FILE}") endif() endfunction() function(psemek_package_files_impl target as_resources) if(PSEMEK_PACKAGE_MODE) if(PSEMEK_PACKAGE_TARGET) set(_FILES) foreach(_PATTERN IN LISTS ARGN) if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/${_PATTERN}") list(APPEND _FILES "${_PATTERN}") else() file(GLOB_RECURSE _TEMP_FILES RELATIVE "${CMAKE_CURRENT_LIST_DIR}" "${_PATTERN}") list(APPEND _FILES ${_TEMP_FILES}) endif() endforeach() if(PSEMEK_PLATFORM_ANDROID AND as_resources) add_custom_command(TARGET ${target} POST_BUILD WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} COMMAND ${PSEMEK_COPY_FILES} ${_FILES} ) elseif(PSEMEK_PLATFORM_WEB AND as_resources) foreach(_FILE IN LISTS _FILES) message(STATUS "Preloading ${CMAKE_CURRENT_LIST_DIR}/${_FILE} as resources/${_FILE}") target_link_options(${target} PRIVATE "--preload-file=${CMAKE_CURRENT_LIST_DIR}/${_FILE}@/resources/${_FILE}" ) endforeach() else() psemek_package_output_path(${target} _OUTPUT_PATH) add_custom_command(TARGET ${target} POST_BUILD WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} COMMAND zip -v "${_OUTPUT_PATH}" -r ${_FILES} ) endif() endif() else() foreach(_FILE ${ARGN}) get_filename_component(_DIR ${_FILE} DIRECTORY) add_custom_command(TARGET ${target} POST_BUILD WORKING_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}" COMMAND "mkdir" "-pv" "${CMAKE_CURRENT_BINARY_DIR}/${_DIR}" ) add_custom_command(TARGET ${target} POST_BUILD WORKING_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}" COMMAND "cp" "-rv" ${_FILE} "${CMAKE_CURRENT_BINARY_DIR}/${_DIR}" ) endforeach() endif() endfunction() function(psemek_package_files target) psemek_package_files_impl(${target} FALSE ${ARGN}) endfunction() function(psemek_package_resources target) psemek_package_files_impl(${target} TRUE ${ARGN}) endfunction()