diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml new file mode 100644 index 0000000000..581da05ac8 --- /dev/null +++ b/.github/workflows/wasm.yml @@ -0,0 +1,85 @@ +name: Webassembly +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + schedule: + - cron: '10 12 * * 0' + +jobs: + build: + name: ${{ matrix.type }}-build + runs-on: ubuntu-latest + strategy: + matrix: + type: [client] + fail-fast: false + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 2 + + - name: Install dependencies + run: | + sudo apt-get -qq update + sudo apt-get install -y p7zip-full zip + + - name: Cache dependencies + uses: actions/cache@v2.1.3 + with: + path: | + 3rdParty/buildCache + !3rdParty/buildCache/wasm/vcpkgcache/ + key: wasm-${{ matrix.type }}-${{ hashFiles('wasm/*.sh') }} + restore-keys: wasm-${{ matrix.type }}- + + - name: Check if build is running from origin repo + if: ${{ success() && env.AWS_ACCESS_KEY_ID != 0 && env.AWS_SECRET_ACCESS_KEY != 0 }} + run: | + echo "VCPKG_BINARY_SOURCES=clear;x-aws,s3://vcpkg.cache.boinc/,readwrite" >> $GITHUB_ENV + + - name: Check if build is running from fork + if: ${{ success() && (env.AWS_ACCESS_KEY_ID == 0 || env.AWS_SECRET_ACCESS_KEY == 0) }} + run: | + echo "VCPKG_BINARY_SOURCES=clear;x-aws-config,no-sign-request;x-aws,s3://vcpkg.cache.boinc/,read" >> $GITHUB_ENV + + - name: Automake + if: success() + run: ./_autosetup + + - name: Configure client as webassembly + if: success() && matrix.type == 'client' + run: wasm/ci_configure_client.sh + + - name: Make + if: success() + run: wasm/ci_make.sh + + - name: Prepare logs on failure + if: ${{ failure() }} + uses: edgarrc/action-7z@v1.0.4 + with: + args: 7z a -t7z -mx=9 deploy/logs.7z config.log -r0 3rdParty/wasm/vcpkg/buildtrees/*.log + + - name: Upload logs on failure + if: ${{ failure() }} + uses: actions/upload-artifact@v2 + with: + name: wasm_logs_${{ matrix.type }}_${{ github.event.pull_request.head.sha }} + path: deploy/logs.7z + + - name: Prepare artifacts for deploy + if: success() && ! contains(matrix.type, 'libs') + env: + PULL_REQUEST: ${{ github.event.number }} + PULL_REQUEST_SHA: ${{ github.event.pull_request.head.sha }} + run: python ./deploy/prepare_deployment.py wasm_${{ matrix.type }} + + - name: Upload artifacts + uses: actions/upload-artifact@v2 + if: ${{ ! contains(matrix.type, 'libs') }} + with: + name: wasm_${{ matrix.type }}_${{ github.event.pull_request.head.sha }} + path: deploy/wasm_${{ matrix.type }}.7z + diff --git a/.gitignore b/.gitignore index 94673773da..d72012afab 100644 --- a/.gitignore +++ b/.gitignore @@ -74,6 +74,7 @@ compile config.guess config.sub configure +configure~ depcomp install-sh ltmain.sh @@ -227,3 +228,10 @@ vda/vdad build/ build-*/ +# WASM +*.wasm +a.out.js +boinc.js +boinc_client.js +boinccmd.js +switcher.js diff --git a/3rdParty/vcpkg_ports/configs/client/wasm/vcpkg.json b/3rdParty/vcpkg_ports/configs/client/wasm/vcpkg.json new file mode 100644 index 0000000000..ae14d997b1 --- /dev/null +++ b/3rdParty/vcpkg_ports/configs/client/wasm/vcpkg.json @@ -0,0 +1,13 @@ +{ + "name": "boinc-client", + "version-string": "7.21.0", + "dependencies": + [ + { + "name": "curl", + "features": ["openssl"], + "default-features": false + }, + "rappture" + ] +} diff --git a/3rdParty/vcpkg_ports/triplets/ci/wasm32-emscripten.cmake b/3rdParty/vcpkg_ports/triplets/ci/wasm32-emscripten.cmake new file mode 100644 index 0000000000..0a6e4444a7 --- /dev/null +++ b/3rdParty/vcpkg_ports/triplets/ci/wasm32-emscripten.cmake @@ -0,0 +1,25 @@ +set(VCPKG_BUILD_TYPE release) +set(VCPKG_ENV_PASSTHROUGH_UNTRACKED EMSCRIPTEN_ROOT EMSDK PATH) + +if(NOT DEFINED ENV{EMSCRIPTEN_ROOT}) + find_path(EMSCRIPTEN_ROOT "emcc") +else() + set(EMSCRIPTEN_ROOT "$ENV{EMSCRIPTEN_ROOT}") +endif() + +if(NOT EMSCRIPTEN_ROOT) + if(NOT DEFINED ENV{EMSDK}) + message(FATAL_ERROR "The emcc compiler not found in PATH") + endif() + set(EMSCRIPTEN_ROOT "$ENV{EMSDK}/upstream/emscripten") +endif() + +if(NOT EXISTS "${EMSCRIPTEN_ROOT}/cmake/Modules/Platform/Emscripten.cmake") + message(FATAL_ERROR "Emscripten.cmake toolchain file not found") +endif() + +set(VCPKG_TARGET_ARCHITECTURE wasm32) +set(VCPKG_CRT_LINKAGE dynamic) +set(VCPKG_LIBRARY_LINKAGE static) +set(VCPKG_CMAKE_SYSTEM_NAME Emscripten) +set(VCPKG_CHAINLOAD_TOOLCHAIN_FILE "${EMSCRIPTEN_ROOT}/cmake/Modules/Platform/Emscripten.cmake") diff --git a/client/hostinfo_unix.cpp b/client/hostinfo_unix.cpp index ff0b596221..5bb9e44436 100644 --- a/client/hostinfo_unix.cpp +++ b/client/hostinfo_unix.cpp @@ -181,6 +181,16 @@ extern "C" { #define LINUX_LIKE_SYSTEM 1 #endif +#if WASM + #include +#endif + +#if WASM + EM_JS(FILE*, popen, (const char* command, const char* mode), { + //TODO: add javascript code + }); +#endif + // Returns the offset between LOCAL STANDARD TIME and UTC. // LOCAL_STANDARD_TIME = UTC_TIME + get_timezone(). // @@ -1238,6 +1248,9 @@ int HOST_INFO::get_cpu_info() { strlcpy( p_model, cpuInfo.name.fromID, sizeof(p_model)); #elif defined(__HAIKU__) get_cpu_info_haiku(*this); +#elif WASM + strlcpy( p_vendor, "WASM", sizeof(p_vendor)); + strlcpy( p_model, "WASM", sizeof(p_model)); #elif HAVE_SYS_SYSCTL_H int mib[2]; size_t len; diff --git a/client/hostinfo_unix_test.cpp b/client/hostinfo_unix_test.cpp index d5e32ae941..11176a255f 100644 --- a/client/hostinfo_unix_test.cpp +++ b/client/hostinfo_unix_test.cpp @@ -51,6 +51,16 @@ using std::string; #define safe_strcat(x, y) strlcat(x, y, sizeof(x)) #define LINUX_LIKE_SYSTEM (defined(__linux__) || defined(__GNU__) || defined(__GLIBC__)) && !defined(__HAIKU__) +#if WASM + #include +#endif + +#if WASM + EM_JS(FILE*, popen, (const char* command, const char* mode), { + //TODO: add javascript code + }); +#endif + enum LINUX_OS_INFO_PARSER { lsbrelease, osrelease, diff --git a/configure.ac b/configure.ac index 0749ef5aa5..a03b658466 100644 --- a/configure.ac +++ b/configure.ac @@ -149,6 +149,12 @@ AC_ARG_ENABLE(apps-gui, [enable_apps_gui=${enableval}], [enable_apps_gui=no]) +AC_ARG_ENABLE(wasm, + AS_HELP_STRING([--enable-wasm], + [enable building the boinc wasm]), + [enable_wasm=${enableval}], + [enable_wasm=no]) + AC_ARG_ENABLE(unit-tests, AS_HELP_STRING([--enable-unit-tests], [enable building the boinc unit tests]), @@ -372,6 +378,10 @@ dnl the manpages only if docbook2x-man is available. AC_PATH_PROG(DOCBOOK2X_MAN, docbook2x-man) AM_CONDITIONAL(HAVE_DOCBOOK2X_MAN, [test -n "${DOCBOOK2X_MAN}"]) +if test "${enable_wasm}" = yes ; then + EXEEXT=.js +fi + AC_SUBST([CLIENT_BIN_FILENAME],[boinc${EXEEXT}]) AC_SUBST([CLIENT_CMD_BIN_FILENAME],[boinccmd${EXEEXT}]) AC_SUBST([CLIENT_GUI_BIN_FILENAME],[boincmgr${EXEEXT}]) @@ -707,7 +717,11 @@ AC_TYPE_SIGNAL if test "${isWIN32}" = "yes" ; then AC_CHECK_HEADERS(winsock2.h winsock.h windows.h ws2tcpip.h winternl.h crtdbg.h) fi -AC_CHECK_HEADERS([sys/types.h sys/un.h arpa/inet.h dirent.h grp.h fcntl.h inttypes.h stdint.h memory.h netdb.h netinet/in.h netinet/tcp.h netinet/ether.h net/if.h net/if_arp.h signal.h strings.h sys/auxv.h sys/file.h sys/fcntl.h sys/ipc.h sys/ioctl.h sys/msg.h sys/param.h sys/resource.h sys/select.h sys/sem.h sys/shm.h sys/sockio.h sys/socket.h sys/stat.h sys/statvfs.h sys/statfs.h sys/systeminfo.h sys/time.h sys/types.h sys/utsname.h sys/vmmeter.h sys/wait.h unistd.h utmp.h errno.h procfs.h ieeefp.h setjmp.h float.h sal.h execinfo.h xlocale.h]) +AC_CHECK_HEADERS(sys/types.h sys/un.h arpa/inet.h dirent.h grp.h fcntl.h inttypes.h stdint.h memory.h netdb.h netinet/in.h netinet/tcp.h netinet/ether.h net/if.h net/if_arp.h signal.h strings.h sys/auxv.h sys/file.h sys/fcntl.h sys/ipc.h sys/ioctl.h sys/msg.h sys/param.h sys/resource.h sys/select.h sys/sem.h sys/sockio.h sys/socket.h sys/stat.h sys/statvfs.h sys/statfs.h sys/systeminfo.h sys/time.h sys/types.h sys/utsname.h sys/vmmeter.h sys/wait.h unistd.h utmp.h errno.h procfs.h ieeefp.h setjmp.h float.h sal.h execinfo.h xlocale.h) + +if test "${enable_wasm}" != yes ; then + AC_CHECK_HEADERS(sys/shm.h) +fi save_cxxflags="${CXXFLAGS}" save_cppflags="${CPPFLAGS}" @@ -1103,10 +1117,16 @@ AM_CONDITIONAL(OS_OS2, [echo $host_os | grep '^os2' > /dev/null]) AM_CONDITIONAL(OS_ARM_LINUX, [echo $host_alias | grep '^arm-linux' > /dev/null]) AM_CONDITIONAL(OS_ARMV6_LINUX, [echo $host_alias | grep '^armv6-linux' > /dev/null]) AM_CONDITIONAL(ANDROID, [test x"${ANDROID}" = xyes]) + AM_CONDITIONAL(BUILD_WITH_VCPKG, [test "${enable_apps_vcpkg}" = yes]) AM_CONDITIONAL(BUILD_WITH_VBOX, [test "${enable_apps_vbox}" = yes]) AM_CONDITIONAL(BUILD_WITH_MINGW, [test "${enable_apps_mingw}" = yes]) AM_CONDITIONAL(BUILD_WITH_GUI, [test "${enable_apps_gui}" = yes]) +AM_CONDITIONAL(BUILD_WITH_WASM, [test "${enable_wasm}" = yes]) +if test "${enable_wasm}" = yes ; then + AC_DEFINE([WASM], [1], [build boinc wasm]) +fi + dnl Whether to build fcgi components AM_CONDITIONAL(ENABLE_FCGI,[test "${enable_fcgi}" = yes]) diff --git a/deploy/prepare_deployment.py b/deploy/prepare_deployment.py index 821f746d17..e2f3ed82b9 100644 --- a/deploy/prepare_deployment.py +++ b/deploy/prepare_deployment.py @@ -184,6 +184,12 @@ windows_manager_list = [ './win_build/Build/ARM64/Release/boincmgr.exe' ] +wasm_client_list = [ + './client/boinc_client.wasm', + './client/boinc.js', + './samples/wasm/index.html', +] + def prepare_7z_archive(archive_name, target_directory, files_list): os.makedirs(target_directory, exist_ok=True) archive_path = os.path.join(target_directory, archive_name + '.7z') @@ -246,6 +252,9 @@ def prepare_win_client(target_directory): def prepare_win_manager(target_directory): prepare_7z_archive('win_manager', target_directory, windows_manager_list) +def prepare_wasm_client(target_directory): + prepare_7z_archive('wasm_client', target_directory, wasm_client_list) + boinc_types = { 'linux_client': prepare_linux_client, 'linux_client-vcpkg': prepare_linux_client_vcpkg, @@ -262,14 +271,14 @@ boinc_types = { 'android_apps-vcpkg': prepare_android_apps_vcpkg, 'win_apps': prepare_win_apps, 'win_client': prepare_win_client, - 'win_manager': prepare_win_manager + 'win_manager': prepare_win_manager, + 'wasm_client': prepare_wasm_client } if (len(sys.argv) != 2): help() sys.exit(1) - boinc_type = sys.argv[1] target_dir = 'deploy' diff --git a/lib/Makefile.am b/lib/Makefile.am index 4777aa359f..6aa46527b0 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -131,6 +131,9 @@ mac_headers= endif endif +if BUILD_WITH_WASM +generic_sources += wasm.cpp +endif if INSTALL_HEADERS pkginclude_HEADERS = \ diff --git a/lib/wasm.cpp b/lib/wasm.cpp new file mode 100644 index 0000000000..7910d0800e --- /dev/null +++ b/lib/wasm.cpp @@ -0,0 +1,27 @@ +// This file is part of BOINC. +// http://boinc.berkeley.edu +// Copyright (C) 2022 University of California +// +// BOINC is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License +// as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// BOINC is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with BOINC. If not, see . + +#include +#include + +key_t ftok(const char *path, int id) +{ + struct stat st; + if (stat(path, &st) < 0) return -1; + + return ((st.st_ino & 0xffff) | ((st.st_dev & 0xff) << 16) | ((id & 0xff) << 24)); +} diff --git a/samples/wasm/index.html b/samples/wasm/index.html new file mode 100644 index 0000000000..08f7002bbe --- /dev/null +++ b/samples/wasm/index.html @@ -0,0 +1,6 @@ + + +

Boinc

+ + + diff --git a/wasm/ci_configure_client.sh b/wasm/ci_configure_client.sh new file mode 100755 index 0000000000..707f653181 --- /dev/null +++ b/wasm/ci_configure_client.sh @@ -0,0 +1,20 @@ +#!/bin/bash +set -e + +if [ ! -d "wasm" ]; then + echo "start this script in the source root directory" + exit 1 +fi + +CACHE_DIR="$PWD/3rdParty/buildCache/wasm" +BUILD_DIR="$PWD/3rdParty/wasm" +VCPKG_ROOT="$BUILD_DIR/vcpkg" +EMSDK_ROOT="$BUILD_DIR/emsdk" +export VCPKG_DIR="$VCPKG_ROOT/installed/wasm32-emscripten" + +wasm/update_emsdk.sh +source $EMSDK_ROOT/emsdk_env.sh +wasm/update_emsdk_vcpkg.sh + +export _libcurl_pc="$VCPKG_DIR/lib/pkgconfig/libcurl.pc" +emconfigure ./configure --enable-wasm --enable-vcpkg --with-libcurl=$VCPKG_DIR --with-ssl=$VCPKG_DIR --disable-server --enable-client --disable-manager diff --git a/wasm/ci_make.sh b/wasm/ci_make.sh new file mode 100755 index 0000000000..e1b9c84dea --- /dev/null +++ b/wasm/ci_make.sh @@ -0,0 +1,13 @@ +#!/bin/bash +set -e + +if [ ! -d "wasm" ]; then + echo "start this script in the source root directory" + exit 1 +fi + +BUILD_DIR="$PWD/3rdParty/wasm" +EMSDK_ROOT="$BUILD_DIR/emsdk" + +source $EMSDK_ROOT/emsdk_env.sh +emmake make diff --git a/wasm/update_emsdk.sh b/wasm/update_emsdk.sh new file mode 100755 index 0000000000..953d6f68d7 --- /dev/null +++ b/wasm/update_emsdk.sh @@ -0,0 +1,21 @@ +#!/bin/sh +set -e + +if [ ! -d "wasm" ]; then + echo "start this script in the source root directory" + exit 1 +fi + +CACHE_DIR="$PWD/3rdParty/buildCache/wasm" +BUILD_DIR="$PWD/3rdParty/wasm" +EMSDK_ROOT="$BUILD_DIR/emsdk" + + +if [ ! -d $EMSDK_ROOT ]; then + mkdir -p $BUILD_DIR + git -C $BUILD_DIR clone https://github.com/emscripten-core/emsdk +fi + +git -C $EMSDK_ROOT pull +$EMSDK_ROOT/emsdk install latest +$EMSDK_ROOT/emsdk activate latest diff --git a/wasm/update_emsdk_vcpkg.sh b/wasm/update_emsdk_vcpkg.sh new file mode 100755 index 0000000000..654a75d84e --- /dev/null +++ b/wasm/update_emsdk_vcpkg.sh @@ -0,0 +1,21 @@ +#!/bin/sh +set -e + +if [ ! -d "wasm" ]; then + echo "start this script in the source root directory" + exit 1 +fi + + +BUILD_DIR="$PWD/3rdParty/wasm" +VCPKG_PORTS="$PWD/3rdParty/vcpkg_ports" +VCPKG_ROOT="$BUILD_DIR/vcpkg" + +if [ ! -d $VCPKG_ROOT ]; then + mkdir -p $BUILD_DIR + git -C $BUILD_DIR clone https://github.com/microsoft/vcpkg +fi + +git -C $VCPKG_ROOT pull +$VCPKG_ROOT/bootstrap-vcpkg.sh +$VCPKG_ROOT/vcpkg install --x-manifest-root=3rdParty/vcpkg_ports/configs/client/wasm --x-install-root=$VCPKG_ROOT/installed/ --overlay-ports=$VCPKG_PORTS/ports --overlay-triplets=$VCPKG_PORTS/triplets/ci --triplet=wasm32-emscripten --clean-after-build