Set up build/packaging infra for web builds #1

Merged
lisyarus merged 9 commits from yulya3102/psemek:web into master 2026-07-03 08:40:27 +00:00
15 changed files with 162 additions and 10 deletions

View file

@ -1,3 +1,20 @@
if(CMAKE_SYSTEM_NAME STREQUAL Emscripten)
set(BOOST_INCLUDE_LIBRARIES random stacktrace math)
set(BOOST_ENABLE_COMPATIBILITY_TARGETS 1)
add_compile_options(-fdeclspec) # math doesn't build otherwise
# Use out-of-tree sources for boost, since we can't use pre-built
# libraries for the web build
if(BOOST_SOURCE_ROOT)
add_subdirectory(${BOOST_SOURCE_ROOT} boost EXCLUDE_FROM_ALL)
else()
message(FATAL_ERROR "For web builds BOOST_SOURCE_ROOT must be provided")
endif()
# TODO: make this available via find_package so we wouldn't have to
# special-case it in every library
endif()
file(GLOB_RECURSE RAPIDJSON_SOURCES "${CMAKE_CURRENT_LIST_DIR}/rapidjson/include/*")
add_library(rapidjson INTERFACE EXCLUDE_FROM_ALL "${RAPIDJSON_SOURCES}")
target_include_directories(rapidjson INTERFACE "${CMAKE_CURRENT_LIST_DIR}/rapidjson/include")

View file

@ -50,6 +50,18 @@ if(PSEMEK_BACKEND STREQUAL "ANDROID")
set(PSEMEK_GL_LIBRARIES GLESv3 EGL)
set(PSEMEK_PACKAGE_AS_LIBRARY ON CACHE INTERNAL "Build applications as shared libraries")
list(APPEND PSEMEK_CXX_FLAGS -fPIC)
elseif(CMAKE_SYSTEM_NAME STREQUAL Emscripten)
set(PSEMEK_GL_API gles32)
set(PSEMEK_GL_LIBRARIES EGL)
set(PSEMEK_PACKAGE_AS_LIBRARY OFF CACHE INTERNAL "Build applications as shared libraries")
list(APPEND PSEMEK_DEFINITIONS "-DPSEMEK_GLES=1")
list(APPEND PSEMEK_CXX_FLAGS -sUSE_SDL=2 -fexperimental-library)
# by default emcc doesn't produce catch clauses, but psemek is designed around exceptions
list(APPEND PSEMEK_CXX_FLAGS -sDISABLE_EXCEPTION_CATCHING=0)
if(CMAKE_BUILD_TYPE STREQUAL Debug)
list(APPEND PSEMEK_CXX_FLAGS -g)
endif()
set(CMAKE_EXECUTABLE_SUFFIX ".html" PARENT_SCOPE) # we want to build html, not js
else()
find_package(Threads REQUIRED)
set(OpenGL_GL_PREFERENCE LEGACY)

View file

@ -11,7 +11,7 @@
#include <stdio.h>
#elif defined(_WIN32)
#include <windows.h>
#elif defined(__ANDROID__)
#elif defined(__ANDROID__) || defined(__EMSCRIPTEN__)
#include <EGL/egl.h>
#else
#include <GL/glx.h>
@ -80,7 +80,7 @@ namespace gl
return reinterpret_cast<void*>(GetProcAddress(image, reinterpret_cast<LPCSTR>(name)));
}
#elif defined(__ANDROID__)
#elif defined(__ANDROID__) || defined(__EMSCRIPTEN__)
static void * get_proc_address(const char *func)
{

View file

@ -8,8 +8,12 @@ namespace psemek::journal
std::string current_time()
{
#ifndef __EMSCRIPTEN__
const auto now = std::chrono::system_clock::now();
return std::format("{:%FT%TZ}", now);
#else
return "";
#endif
}
}

View file

@ -148,9 +148,11 @@ namespace psemek::log
#if __APPLE__
::pthread_setname_np(name.data());
#else
#if !(defined __EMSCRIPTEN__) || (defined __EMSCRIPTEN_PTHREADS__)
::pthread_setname_np(::pthread_self(), name.data());
#endif
#endif
#endif
#endif
auto id = std::this_thread::get_id();

View file

@ -1,4 +1,6 @@
find_package(Boost COMPONENTS random REQUIRED CONFIG)
if(NOT (CMAKE_SYSTEM_NAME STREQUAL Emscripten))
find_package(Boost COMPONENTS random REQUIRED CONFIG)
endif()
file(GLOB_RECURSE PSEMEK_RANDOM_HEADERS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "include/*.hpp")
file(GLOB_RECURSE PSEMEK_RANDOM_SOURCES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "source/*.cpp")

View file

@ -1,4 +1,7 @@
# On web SDL is provided by emsdk itself
if(NOT CMAKE_SYSTEM_NAME STREQUAL Emscripten)
find_package(SDL2 REQUIRED)
endif()
file(GLOB_RECURSE PSEMEK_SDL2_HEADERS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "include/*.hpp")
file(GLOB_RECURSE PSEMEK_SDL2_SOURCES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "source/*.cpp")

View file

@ -1,6 +1,8 @@
# Boost.Math is header-only
# Required for statistics.hpp
find_package(Boost REQUIRED CONFIG)
if(NOT (CMAKE_SYSTEM_NAME STREQUAL Emscripten))
# Boost.Math is header-only
# Required for statistics.hpp
find_package(Boost REQUIRED CONFIG)
endif()
file(GLOB_RECURSE PSEMEK_UTIL_HEADERS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "include/*.hpp")
file(GLOB_RECURSE PSEMEK_UTIL_SOURCES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "source/*.cpp")

View file

@ -10,14 +10,24 @@ if(PSEMEK_PACKAGE_MODE)
elseif(ANDROID)
set(PSEMEK_PACKAGE_SUFFIX_RAW android)
elseif(UNIX)
set(PSEMEK_PACKAGE_SUFFIX_RAW linux)
if (CMAKE_SYSTEM_NAME STREQUAL Emscripten)
set(PSEMEK_PACKAGE_SUFFIX_RAW web)
else()
set(PSEMEK_PACKAGE_SUFFIX_RAW linux)
endif()
else()
message(FATAL "Uknown system for packaging")
endif()
set(PSEMEK_PACKAGE_SUFFIX ${PSEMEK_PACKAGE_SUFFIX_RAW} CACHE INTERNAL "Packaging suffix" FORCE)
set(PSEMEK_PACKAGE_LINK_FLAGS_RAW)
if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (CMAKE_CXX_COMPILER_ID STREQUAL "Clang"))
if(CMAKE_SYSTEM_NAME STREQUAL Emscripten)
list(APPEND PSEMEK_PACKAGE_LINK_FLAGS_RAW "-sMIN_WEBGL_VERSION=2" "-sNO_DISABLE_EXCEPTION_CATCHING")
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)
@ -71,7 +81,7 @@ function(psemek_add_executable_impl target is_application)
)
endif()
if(UNIX AND (NOT APPLE) AND (NOT ANDROID))
if(UNIX AND (NOT APPLE) AND (NOT ANDROID) AND (NOT (CMAKE_SYSTEM_NAME STREQUAL Emscripten)))
add_custom_command(TARGET ${target} POST_BUILD
COMMAND chrpath -r . $<TARGET_FILE:${target}>
)
@ -82,7 +92,19 @@ function(psemek_add_executable_impl target is_application)
if(NOT ANDROID)
get_filename_component(_OUTPUT_DIRECTORY "${_OUTPUT_PATH}" DIRECTORY)
set(_COPY_FILES $<TARGET_FILE:${target}> ${PSEMEK_PACKAGE_COPY_FILES})
set(_TARGET_FILE $<TARGET_FILE:${target}>)
set(_TARGET_RUNTIME)
if(CMAKE_SYSTEM_NAME STREQUAL Emscripten)
set(_TARGET_PARENT $<PATH:GET_PARENT_PATH,${_TARGET_FILE}>)
set(_TARGET_STEM $<PATH:GET_STEM,${_TARGET_FILE}>)
set(_TARGET_NOEXT "${_TARGET_PARENT}/${_TARGET_STEM}")
list(APPEND _TARGET_RUNTIME ${_TARGET_NOEXT}.js)
list(APPEND _TARGET_RUNTIME ${_TARGET_NOEXT}.wasm)
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})

View file

@ -8,6 +8,7 @@ if [ "$#" -ne 2 ]; then
echo " linux"
echo " windows"
echo " android"
echo " web"
exit 0
fi
@ -15,6 +16,7 @@ case ${1} in
linux) ;;
windows) ;;
android) ;;
web) ;;
*)
echo "Unknown platform: ${1}"
exit -1

20
package/web/Dockerfile Normal file
View file

@ -0,0 +1,20 @@
FROM lisyarus/psemek:package-linux
# Install emsdk - the version the build was tested at
WORKDIR /home
RUN git clone --branch 3.1.46 https://github.com/emscripten-core/emsdk.git
WORKDIR /home/emsdk
RUN ./emsdk install 3.1.46
RUN ./emsdk activate 3.1.46
WORKDIR /home
COPY env-helper.sh .
# Source emsdk env variables when running the container
ENTRYPOINT ["/home/env-helper.sh"]
# Install Boost sources
WORKDIR /home
RUN git clone --recurse-submodules --branch boost-1.87.0 https://github.com/boostorg/boost.git
# Install toolchain file & main packaging script
COPY toolchain-host.cmake .
COPY package.sh .

7
package/web/build.sh Executable file
View file

@ -0,0 +1,7 @@
#!/usr/bin/env bash
set -e
cd "$(dirname "$0")"
docker build -t lisyarus/psemek:package-web .

6
package/web/env-helper.sh Executable file
View file

@ -0,0 +1,6 @@
#!/bin/bash
# emsdk provides some environment variables that need to be sourced from a file
source /home/emsdk/emsdk_env.sh
exec "$@"

27
package/web/package.sh Executable file
View file

@ -0,0 +1,27 @@
#!/usr/bin/env bash
set -e
mkdir -p /home/build-host
cd /home/build-host
cmake ../source -DCMAKE_TOOLCHAIN_FILE=/home/toolchain-host.cmake -DCMAKE_INSTALL_PREFIX=/home/tools
cmake --build . -t install -j
mkdir -p /home/build-target
cd /home/build-target
rm -rf ./*
# Use emsdk wrapper for cmake - it sets some env variables to select the
# right toolchain.
emcmake cmake \
-DBOOST_SOURCE_ROOT=$(realpath ../source)/psemek/3rdparty/boost \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DPSEMEK_PACKAGE_MODE=ON \
-DPSEMEK_PACKAGE_HOST=OFF \
-DPSEMEK_PACKAGE_TARGET=ON \
-DPSEMEK_STACKTRACE=OFF \
-DBOOST_SOURCE_ROOT=/home/boost \
-DPSEMEK_PACKAGE_TOOLS_PATH=/home/tools \
-DPSEMEK_PACKAGE_COPY_FILES= \
-DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=BOTH \
../source
cmake --build . -j

View file

@ -0,0 +1,26 @@
set(CMAKE_C_COMPILER gcc)
set(CMAKE_CXX_COMPILER g++)
set(CMAKE_BUILD_TYPE Release)
set(PSEMEK_PACKAGE_MODE ON)
set(PSEMEK_PACKAGE_ROOT "${CMAKE_CURRENT_LIST_DIR}")
set(PSEMEK_PACKAGE_HOST ON)
set(PSEMEK_PACKAGE_TARGET OFF)
set(PSEMEK_PACKAGE_TOOLS_PATH "${PSEMEK_PACKAGE_ROOT}/tools")
set(PSEMEK_STACKTRACE OFF)
set(PSEMEK_PACKAGE_COPY_FILES
${PSEMEK_PACKAGE_ROOT}/sdl2/install/lib/libSDL2-2.0.so.0
)
set(PSEMEK_PACKAGE_COPY_FILES_FREETYPE
${PSEMEK_PACKAGE_ROOT}/libfreetype.so.6
)
list(APPEND CMAKE_PREFIX_PATH
"${PSEMEK_PACKAGE_ROOT}/sdl2/install"
)
set(WGPU_NATIVE_ROOT
"${PSEMEK_PACKAGE_ROOT}/wgpu-native/install"
)