├── .wkdev-sdk-root ├── images └── wkdev_sdk │ ├── cross │ └── armhf │ │ ├── 02-jsconly-dependencies.lst │ │ ├── 01-toolchain.lst │ │ ├── toolchainfile-gcc.cmake │ │ └── toolchainfile-clang.cmake │ ├── required_system_packages │ ├── 06-devtools-opt.lst │ ├── 02-gcc.lst │ ├── 05-armv7-jhbuild.lst │ ├── 03-clang.lst │ ├── 01-base.lst │ └── 04-devtools.lst │ ├── rootfs │ ├── usr │ │ └── bin │ │ │ └── podman-host │ └── etc │ │ ├── ccache.conf │ │ └── apt │ │ └── sources.list.d │ │ └── llvm.list │ ├── user_home_directory_defaults │ ├── dot-zlogin │ ├── dot-bash_login │ ├── dot-gdbinit │ ├── dot-zprofile │ ├── fish-config │ ├── dot-bash_profile │ └── dot-zshrc │ ├── jhbuild │ ├── jhbuildrc │ ├── README.txt │ └── webkit-sdk-deps.modules │ └── Containerfile ├── .gitignore ├── docs ├── 05-Setup-and-use-vscode.md ├── 10-Running-inside-LXC.md ├── 03-Tagged-versions.md ├── 01-Building-the-SDK.md ├── 04-Cross-compilation-inside-the-SDK.md ├── 02-Building-third-party-libraries.md ├── 07-Using-sysprof.md ├── 06-A-gdb-debugging-example.md └── 00-Introduction.md ├── register-sdk-on-host.fish ├── utilities ├── timer.sh ├── README.md ├── host-setup-tasks.sh ├── kernel-parameters.sh ├── resources.sh ├── prerequisites.sh ├── debian-packages.sh ├── nvidia-gpu.sh ├── settings.sh ├── cpu-profiling.sh ├── podman.sh ├── application.sh └── bash-argsparse │ └── argsparse-completion.sh ├── register-sdk-on-host.sh ├── scripts ├── .wkdev-completion.sh ├── helpers │ └── create-clang-symlinks ├── wkdev-start-profiling ├── wkdev-stop-profiling ├── wkdev ├── container-only │ ├── wkdev-setup-clang │ ├── wkdev-setup-devhelp │ ├── wkdev-setup-sccache │ ├── wkdev-setup-vscode │ ├── wkdev-sdk-show-welcome-message │ ├── .wkdev-sync-runtime-state │ ├── wkdev-setup-remote-ccache │ ├── wkdev-test-host-integration │ ├── wkdev-setup-cluster-administration-tools │ └── .wkdev-init └── host-only │ ├── wkdev-setup-nvidia-gpu-for-container │ ├── wkdev-sdk-bakery │ ├── wkdev-update │ ├── wkdev-enter │ └── wkdev-create ├── LICENSE ├── test-sdk-build-and-run.sh ├── README.md └── .github └── workflows └── wkdev-sdk.yml /.wkdev-sdk-root: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /images/wkdev_sdk/cross/armhf/02-jsconly-dependencies.lst: -------------------------------------------------------------------------------- 1 | libicu-dev:armhf 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .build-done_wkdev-webkit-dependencies 2 | .sysctl_setting_perf_event_paranoid 3 | -------------------------------------------------------------------------------- /images/wkdev_sdk/cross/armhf/01-toolchain.lst: -------------------------------------------------------------------------------- 1 | gcc-arm-linux-gnueabihf 2 | g++-arm-linux-gnueabihf 3 | -------------------------------------------------------------------------------- /images/wkdev_sdk/required_system_packages/06-devtools-opt.lst: -------------------------------------------------------------------------------- 1 | # These are not available on all platforms 2 | 3 | rr 4 | -------------------------------------------------------------------------------- /images/wkdev_sdk/required_system_packages/02-gcc.lst: -------------------------------------------------------------------------------- 1 | # GNU GCC Toolchain (current version: 14) 2 | gcc-14 g++-14 gdb libc6-dev 3 | -------------------------------------------------------------------------------- /images/wkdev_sdk/required_system_packages/05-armv7-jhbuild.lst: -------------------------------------------------------------------------------- 1 | # Needed for jhbuild on armv7 2 | libyaml-dev desktop-file-utils 3 | -------------------------------------------------------------------------------- /images/wkdev_sdk/rootfs/usr/bin/podman-host: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | exec /usr/bin/podman --remote --url "unix:${HOST_PODMAN_SOCKET}" "${@}" 3 | -------------------------------------------------------------------------------- /images/wkdev_sdk/user_home_directory_defaults/dot-zlogin: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | "${WKDEV_SDK}/scripts/container-only/wkdev-sdk-show-welcome-message" 3 | -------------------------------------------------------------------------------- /images/wkdev_sdk/user_home_directory_defaults/dot-bash_login: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | "${WKDEV_SDK}/scripts/container-only/wkdev-sdk-show-welcome-message" 3 | -------------------------------------------------------------------------------- /images/wkdev_sdk/rootfs/etc/ccache.conf: -------------------------------------------------------------------------------- 1 | # By default ccache uses the mtime of the compiler to know if a cache 2 | # is still valid, however we constantly re-create our containers so 3 | # it is better to rely on the hash of the compiler instead. 4 | compiler_check = content -------------------------------------------------------------------------------- /images/wkdev_sdk/user_home_directory_defaults/dot-gdbinit: -------------------------------------------------------------------------------- 1 | set history save 2 | set verbose off 3 | set print pretty on 4 | set print array off 5 | set print array-indexes on 6 | set python print-stack full 7 | set debuginfod enabled on 8 | set sysroot / 9 | handle SIGUSR1 nostop noprint 10 | -------------------------------------------------------------------------------- /docs/05-Setup-and-use-vscode.md: -------------------------------------------------------------------------------- 1 | ## Setting up vscode 2 | 3 | There's a script for it, `wkdev-setup-vscode`, which can be executed from 4 | inside a container: 5 | 6 | ```bash 7 | wkdev-setup-vscode 8 | ``` 9 | 10 | ## Using vscode 11 | After installation, vscode can be started with `code`. 12 | -------------------------------------------------------------------------------- /register-sdk-on-host.fish: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Igalia S.L. 2 | # SPDX-License: MIT 3 | 4 | # To be sourced from your e.g. ~/.config/fish/config.fish to integrate wkdev-sdk with your host OS. 5 | set --export WKDEV_SDK (dirname (readlink -m (status --current-filename))) 6 | fish_add_path --global --path "$WKDEV_SDK/scripts" 7 | fish_add_path --global --path "$WKDEV_SDK/scripts/host-only" 8 | -------------------------------------------------------------------------------- /images/wkdev_sdk/user_home_directory_defaults/dot-zprofile: -------------------------------------------------------------------------------- 1 | export DEBUGINFOD_URLS="https://debuginfod.ubuntu.com" 2 | 3 | # Otherwise one has to pass --break-system-packages to python, when installing custom packages. 4 | export PIP_BREAK_SYSTEM_PACKAGES=1 5 | 6 | # wkdev-sdk integration 7 | export WKDEV_SDK=/wkdev-sdk 8 | export PATH="${WKDEV_SDK}/scripts:${WKDEV_SDK}/scripts/container-only:$(python3 -m site --user-base)/bin:${PATH}" 9 | -------------------------------------------------------------------------------- /images/wkdev_sdk/cross/armhf/toolchainfile-gcc.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_SYSTEM_NAME Linux) 2 | set(CMAKE_SYSTEM_PROCESSOR arm) 3 | 4 | set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc) 5 | set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++) 6 | 7 | set(CMAKE_FIND_ROOT_PATH /usr/arm-linux-gnueabihf;/usr/lib/arm-linux-gnueabihf;) 8 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 9 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 10 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH) 11 | -------------------------------------------------------------------------------- /images/wkdev_sdk/required_system_packages/03-clang.lst: -------------------------------------------------------------------------------- 1 | # Clang toolchain (current version: 18) 2 | clang-18 clangd-18 clang-format-18 clang-tidy-18 lld-18 3 | 4 | # Add most recent clang version (current version 21) -- it's not set as default (see Containerfile) 5 | # Ubuntu provided lldb-18 and LLVM-provided lldb-21 are in conflict - however we can just pull in lldb-21, which works with clang-18 too. 6 | clang-21 clangd-21 clang-format-21 clang-tidy-21 lld-21 lldb-21 7 | -------------------------------------------------------------------------------- /utilities/timer.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | [ -z "${application_ready}" ] && { echo "[FATAL] You need to source 'utilities/application.sh' before sourcing this script."; return 1; } 4 | source "${WKDEV_SDK}/utilities/prerequisites.sh" 5 | 6 | start_time=0 7 | 8 | timer_start() { start_time=${SECONDS}; } 9 | 10 | timer_stop() { 11 | 12 | elapsed=$(( SECONDS - start_time )) 13 | eval "echo Elapsed time: $(date -ud "@$elapsed" +'$((%s/3600/24)) days %H hr %M min %S sec')" 14 | } 15 | -------------------------------------------------------------------------------- /register-sdk-on-host.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2024 Igalia S.L. 3 | # SPDX-License: MIT 4 | 5 | # To be sourced from your e.g. ~/.bashrc / ~.zprofile / ... to integrate wkdev-sdk with your host OS. 6 | my_relpath="${BASH_SOURCE[0]}" 7 | test -z "${my_relpath}" && my_relpath="${0}" 8 | wkdev_sdk_directory="$(readlink -f $(dirname ${my_relpath}))" 9 | 10 | export WKDEV_SDK="${wkdev_sdk_directory}" 11 | export PATH="${WKDEV_SDK}/scripts:${WKDEV_SDK}/scripts/host-only:$(python3 -m site --user-base)/bin:${PATH}" 12 | -------------------------------------------------------------------------------- /images/wkdev_sdk/cross/armhf/toolchainfile-clang.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_SYSTEM_NAME Linux) 2 | set(CMAKE_SYSTEM_PROCESSOR arm) 3 | 4 | set(CMAKE_C_COMPILER clang) 5 | set(CMAKE_CXX_COMPILER clang++) 6 | set(CMAKE_C_COMPILER_TARGET arm-linux-gnueabihf) 7 | set(CMAKE_CXX_COMPILER_TARGET arm-linux-gnueabihf) 8 | set(CMAKE_LINKER_TYPE LLD) 9 | 10 | set(CMAKE_FIND_ROOT_PATH /usr/arm-linux-gnueabihf;/usr/lib/arm-linux-gnueabihf;) 11 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 12 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 13 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH) 14 | -------------------------------------------------------------------------------- /docs/10-Running-inside-LXC.md: -------------------------------------------------------------------------------- 1 | # Running the SDK inside an LXC container 2 | 3 | For running this inside an LXC container the following setup works: 4 | 5 | 1. Use a LXC privileged container. 6 | 7 | 8 | 2. Configure the LXC container config with the following lines 9 | 10 | ``` 11 | # tun/tap for podman 12 | lxc.cgroup.devices.allow = c 10:200 rwm 13 | lxc.cgroup2.devices.allow = c 10:200 rwm 14 | lxc.mount.entry = /dev/net/tun dev/net/tun none bind,create=file 15 | 16 | # Allow /dev/fuse 17 | lxc.cgroup.devices.allow = c 10:229 rwm 18 | lxc.mount.entry = /dev/fuse dev/fuse none bind,create=file 0 0 19 | ``` 20 | -------------------------------------------------------------------------------- /images/wkdev_sdk/user_home_directory_defaults/fish-config: -------------------------------------------------------------------------------- 1 | set --export DEBUGINFOD_URLS "https://debuginfod.ubuntu.com" 2 | 3 | # Otherwise one has to pass --break-system-packages to python, when installing custom packages. 4 | set --export PIP_BREAK_SYSTEM_PACKAGES 1 5 | 6 | fish_add_path "$(python3 -m site --user-base)/bin" 7 | 8 | # wkdev-sdk integration 9 | set --export WKDEV_SDK /wkdev-sdk 10 | set --global --export --append --path PATH "$WKDEV_SDK/scripts" 11 | set --global --export --append --path PATH "$WKDEV_SDK/scripts/container-only" 12 | 13 | "$WKDEV_SDK/scripts/container-only/wkdev-sdk-show-welcome-message" -------------------------------------------------------------------------------- /images/wkdev_sdk/rootfs/etc/apt/sources.list.d/llvm.list: -------------------------------------------------------------------------------- 1 | # From https://apt.llvm.org/ 2 | deb http://apt.llvm.org/noble/ llvm-toolchain-noble main 3 | deb-src http://apt.llvm.org/noble/ llvm-toolchain-noble main 4 | # 19 5 | deb http://apt.llvm.org/noble/ llvm-toolchain-noble-19 main 6 | deb-src http://apt.llvm.org/noble/ llvm-toolchain-noble-19 main 7 | # 20 8 | deb http://apt.llvm.org/noble/ llvm-toolchain-noble-20 main 9 | deb-src http://apt.llvm.org/noble/ llvm-toolchain-noble-20 main 10 | # 21 11 | deb http://apt.llvm.org/noble/ llvm-toolchain-noble-21 main 12 | deb-src http://apt.llvm.org/noble/ llvm-toolchain-noble-21 main 13 | -------------------------------------------------------------------------------- /scripts/.wkdev-completion.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2024 Igalia S.L. 3 | # SPDX-License: MIT 4 | 5 | _wkdev_completions() 6 | { 7 | local dir 8 | local words=() 9 | 10 | # Create array of wkdev- words. 11 | dir=$(dirname $(which wkdev)) 12 | while read each; do 13 | each=$(basename "$each") 14 | if [[ "$each" == wkdev-* ]]; then 15 | words+=(${each:6}) 16 | fi 17 | done <<< $(find $dir -type f -print) 18 | 19 | # Expand array to string. 20 | words=${words[@]} 21 | # Set autocomplete words for wkdev. 22 | COMPREPLY=($(compgen -W "${words}" "${COMP_WORDS[1]}")) 23 | } 24 | complete -F _wkdev_completions wkdev 25 | -------------------------------------------------------------------------------- /utilities/README.md: -------------------------------------------------------------------------------- 1 | This directory contains various helper shell script fragments, to share code between the various 2 | scripts in this repository. 3 | 4 | Naming convention 5 | ----------------- 6 | 7 | 1. "is_" / "does_" methods 8 | 9 | All methods starting with "is_" return an exit code, that can be checked. 10 | Typical idioms: ```is_socket_available || _abort_ "No socket available" 11 | 12 | 2. "get_" methods 13 | 14 | All methods starting with "get_" print the resulting string/numeric value 15 | to stdout, from which it can be captured in local variables by the callee, 16 | e.g. ```my_default="$(get_some_default_value)"``` 17 | 18 | 3. "verify_" methods 19 | 20 | Ensures a condition is fulfiled -- aborts if not. 21 | -------------------------------------------------------------------------------- /images/wkdev_sdk/required_system_packages/01-base.lst: -------------------------------------------------------------------------------- 1 | # Daemon providing dynamic access to debug symbols / sources for system packages 2 | debuginfod 3 | 4 | # Container/capabilities support tools 5 | iptables libcap2-bin libyajl2 uidmap 6 | 7 | # NVIDIA/EGL/Wayland integration 8 | libnvidia-egl-wayland1 9 | 10 | # Pipewire support 11 | wireplumber 12 | 13 | # General utilities 14 | apt-file aptly atop at-spi2-core bind9-dnsutils emacs git htop iotop iputils-ping kmod less libportal-dev libportal-gtk4-dev libxcb-cursor-dev locate man-db nano openssh-client python3-virtualenv strace sudo zip unzip vim-gtk3 wget 15 | 16 | # Make python a symbolic link to python3 (and related binaries) 17 | python-is-python3 18 | 19 | # Multimedia 20 | libx264-dev 21 | libx265-dev 22 | 23 | # Vulkan 24 | mesa-vulkan-drivers vulkan-tools 25 | -------------------------------------------------------------------------------- /utilities/host-setup-tasks.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | [ -z "${application_ready}" ] && { echo "[FATAL] You need to source 'utilities/application.sh' before sourcing this script."; return 1; } 4 | 5 | host_setup_try_enable_localuser_authorization_for_x11() { 6 | 7 | _log_ "" 8 | 9 | local host_user_name=$(id --user --name) 10 | 11 | if type xhost >/dev/null 2>&1; then 12 | if xhost | grep --quiet "localuser:${host_user_name}"; then 13 | _log_ "-> The X11 localuser authorization for user '${host_user_name}' on host system is already enabled." 14 | else 15 | _log_ "-> Enable X11 localuser authorization for user '${host_user_name}' on host system..." 16 | xhost +"si:localuser:${host_user_name}" 17 | fi 18 | fi 19 | } 20 | 21 | host_setup_prerun_tasks() { 22 | 23 | host_setup_try_enable_localuser_authorization_for_x11 24 | } -------------------------------------------------------------------------------- /utilities/kernel-parameters.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | [ -z "${application_ready}" ] && { echo "[FATAL] You need to source 'utilities/application.sh' before sourcing this script."; return 1; } 4 | source "${WKDEV_SDK}/utilities/prerequisites.sh" 5 | 6 | # Some distros still have a separate /sbin and sysctl may be in it. 7 | if [ -d /sbin ]; then 8 | PATH="/sbin:${PATH}" 9 | fi 10 | 11 | verify_executables_exist sudo sysctl 12 | 13 | # Helper function to read from /proc/sys/ via 'sysctl'. 14 | read_kernel_parameter() { 15 | 16 | local parameter_name="${1}" 17 | sysctl --values "kernel.${parameter_name}" 2>/dev/null 18 | } 19 | 20 | # Helper function to write to /proc/sys/ via 'sysctl'. 21 | write_kernel_parameter() { 22 | 23 | local parameter_name="${1}" 24 | local value="${2}" 25 | sudo sh -c "sysctl --quiet --write 'kernel.${parameter_name}=${value}' 2>/dev/null" 26 | } 27 | -------------------------------------------------------------------------------- /utilities/resources.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | [ -z "${application_ready}" ] && { echo "[FATAL] You need to source 'utilities/application.sh' before sourcing this script."; return 1; } 4 | 5 | # Internal helpers 6 | _get_sdk_images_directory() { 7 | local default="${WKDEV_SDK}/images"; 8 | echo "${WKDEV_SDK_IMAGES_DIRECTORY:-"${default}"}"; 9 | } 10 | 11 | # Get absolute path to '/images/' directory, given an image name. 12 | # The subdirectory name is equal to the image name, except that hyphens transform to 13 | # underscores (image name: wkdev-sdk -> directory name: wkdev_sdk). 14 | get_image_directory_by_name() { 15 | 16 | local image_name="${1}" 17 | local image_directory="$(_get_sdk_images_directory)/${image_name//-/_}" 18 | [ ! -d "${image_directory}" ] && _abort_ "Invalid image directory '${image_directory}' for image name '${image_name}'" 19 | echo "${image_directory}" 20 | } 21 | -------------------------------------------------------------------------------- /images/wkdev_sdk/required_system_packages/04-devtools.lst: -------------------------------------------------------------------------------- 1 | # Build systems 2 | build-essential cmake ninja-build 3 | 4 | # Build tools 5 | ccache 6 | 7 | # Debugging / profiling / tracing 8 | valgrind perf-tools-unstable systemd-coredump 9 | 10 | # Documentation 11 | asciidoc doxygen doxygen-latex doxygen-doxyparse graphviz python3-sphinx devhelp libglib2.0-doc libgtk-4-doc libsoup-3.0-doc 12 | 13 | # For WebKit scripts such as git-webkit 14 | python3-pip python3-cffi 15 | 16 | # For WebKit scripts such as cross-toolchain-helper 17 | chrpath cpio diffstat lz4 zstd 18 | 19 | # For podman-host 20 | podman 21 | 22 | # For libadwaita 23 | libyaml-dev 24 | libappstream-dev 25 | 26 | # post-install for Epiphany 27 | desktop-file-utils 28 | 29 | # JSON commandline processing 30 | jq 31 | 32 | # Port knocking (remote ccache support) 33 | knockd 34 | 35 | # For OpenXR tests 36 | libeigen3-dev glslang-tools libudev-dev libv4l-dev libvulkan-dev 37 | -------------------------------------------------------------------------------- /images/wkdev_sdk/jhbuild/jhbuildrc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import sys 3 | import os 4 | 5 | # Configure jhbuild to be self-contained to /jhbuild 6 | buildroot = '/jhbuild/build' 7 | prefix = '/jhbuild/install' 8 | tarballdir = '/jhbuild/downloads' 9 | modulesets_dir = '/jhbuild' 10 | 11 | if 'WKDEV_IN_IMAGE_BUILD' in os.environ: 12 | # In our image build we put these in a cached directory. 13 | checkoutroot = '/jhbuild/checkout' 14 | buildroot = '/var/tmp/jhbuild/build' 15 | tarballdir = '/var/tmp/jhbuild/downloads' 16 | shallow_clone = True 17 | else: 18 | checkoutroot = os.path.expanduser('~/checkout') 19 | 20 | # Whether to use a local copy of modulesets 21 | use_local_modulesets = True 22 | # Assume we have sysdeps installed 23 | check_sysdeps = False 24 | # List of modulesets to use 25 | moduleset = [ '/jhbuild/webkit-sdk-deps.modules' ] 26 | # A list of the modules to build. 27 | modules = [ 'webkit-sdk-deps' ] 28 | -------------------------------------------------------------------------------- /scripts/helpers/create-clang-symlinks: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2024 Igalia S.L. 3 | # SPDX-License: MIT 4 | 5 | # Standalone helper script to create symlinks for clang tools. 6 | # Used by both the Containerfile and wkdev-setup-clang. 7 | 8 | set -euo pipefail 9 | 10 | usage() { 11 | echo "Usage: $(basename "$0") [output_path]" 12 | echo " version - clang version number (e.g., 18)" 13 | echo " output_path - directory for symlinks (default: /usr/local/bin)" 14 | exit 1 15 | } 16 | 17 | if [ $# -lt 1 ]; then 18 | usage 19 | fi 20 | 21 | version="$1" 22 | output_path="${2:-/usr/local/bin}" 23 | 24 | if [ ! -f "/usr/bin/clang-${version}" ]; then 25 | echo "Error: clang-${version} is not installed" >&2 26 | exit 1 27 | fi 28 | 29 | mkdir -p "${output_path}" 30 | for binary in /usr/bin/*-"${version}"; do 31 | binary_name="$(basename "${binary}")" 32 | ln --symbolic --force "${binary}" "${output_path}/${binary_name::-3}" 33 | done 34 | -------------------------------------------------------------------------------- /docs/03-Tagged-versions.md: -------------------------------------------------------------------------------- 1 | # Tagged versions 2 | 3 | Over time the SDK will introduce major changes that will break behavior; 4 | In order to easily use older versions we support tagged versions. 5 | 6 | To use a specific tag the `wkdev-create` and `wkdev-sdk-bakery` commands take a `--tag` 7 | argument. You can also set the `WKDEV_SDK_TAG` environment variable. 8 | 9 | To get a list of available tags you can pass `--list-tags` to `wkdev-create`. 10 | 11 | For example using an older image when creating a container: 12 | 13 | ```sh 14 | wkdev-create --name='example-name' --tag='23.04' 15 | ``` 16 | 17 | As the scripts diverge from older images this *may* fail but for now should work fine. 18 | However to use older scripts you can simply checkout the branch for that tag: `git checkout tag/23.04`. 19 | This will use the `23.04` image by default but you may override as mentioned above. 20 | 21 | ## Creating a tag 22 | 23 | The entire process of creating a tag is automated just create a branch named `tag/${tag_name}` 24 | and CI will publish an image under that name. 25 | 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024 Igalia S.L. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /images/wkdev_sdk/jhbuild/README.txt: -------------------------------------------------------------------------------- 1 | In `Containerfile` we copy `jhbuildrc` and our modulesets to the image. 2 | 3 | Then we run: 4 | 5 | ``` 6 | export JHBUILD_RUN_AS_ROOT=1 WKDEV_IN_IMAGE_BUILD=1 7 | jhbuild --no-interact build 8 | ``` 9 | 10 | The build state is cached in `/var/tmp/jhbuild` during image generation 11 | but not included in the final image. 12 | 13 | If you want to hack on this, simply modify the moduleset and rebuild 14 | the SDK. 15 | 16 | Tip: Enable verbose mode when building. Example: 17 | wkdev-sdk-bakery --verbose --mode build 18 | 19 | If there is some issue building the jhbuild and you want to debug it 20 | then the easiest way is to start the container on the step previous 21 | to executing the deploy script and run it manually. 22 | 23 | For example, if you have this: 24 | 25 | STEP 41/46: WORKDIR /jhbuild 26 | --> 2fabea45a33f 27 | STEP 42/46: RUN git clone https://gitlab.gnome.org/GNOME/jhbuild.git ... 28 | 29 | Then you can debug step 27 with: 30 | 31 | $ podman run -it --rm 2fabea45a33f /bin/bash 32 | root@2fabea45a33f:~# env JHBUILD_RUN_AS_ROOT=1 WKDEV_IN_IMAGE_BUILD=1 jhbuild build 33 | -------------------------------------------------------------------------------- /utilities/prerequisites.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | [ -z "${application_ready}" ] && { echo "[FATAL] You need to source 'utilities/application.sh' before sourcing this script."; return 1; } 4 | 5 | does_executable_exist() { command -v "${1}" >/dev/null; } 6 | 7 | verify_executable_exists() { 8 | 9 | local executable="${1}" 10 | does_executable_exist "${executable}" || _abort_ "Cannot find required '${executable}' executable" 11 | } 12 | 13 | is_version_greater_or_equal() { [ "$(printf '%s\n' "${@}" | sort -V | head -n 1)" != "${1}" ]; } 14 | 15 | verify_podman_is_acceptable() { 16 | 17 | local executable="${1}" 18 | verify_executable_exists "${executable}" 19 | 20 | local -r required_version="4.0.0" 21 | local podman_version 22 | podman_version=$("${executable}" --version | awk '{print $3}') 23 | is_version_greater_or_equal "${podman_version}" "${required_version}" || _abort_ "'${executable}' version '${podman_version}' is older than required '${required_version}'" 24 | } 25 | 26 | verify_executables_exist() { 27 | 28 | local executables="${@}" 29 | for executable in ${executables}; do 30 | verify_executable_exists "${executable}" 31 | done 32 | } 33 | -------------------------------------------------------------------------------- /images/wkdev_sdk/user_home_directory_defaults/dot-bash_profile: -------------------------------------------------------------------------------- 1 | export DEBUGINFOD_URLS="https://debuginfod.ubuntu.com" 2 | 3 | # Otherwise one has to pass --break-system-packages to python, when installing custom packages. 4 | export PIP_BREAK_SYSTEM_PACKAGES=1 5 | 6 | # wkdev-sdk integration 7 | export WKDEV_SDK=/wkdev-sdk 8 | export PATH="${WKDEV_SDK}/scripts:${WKDEV_SDK}/scripts/container-only:$(python3 -m site --user-base)/bin:${PATH}" 9 | 10 | # The shell-specific profile file (.bash_profile) overrides the generic one (.profile). 11 | # If we have a .profile, source it as well here. 12 | if [ -f "$HOME/.profile" ]; then 13 | . "$HOME/.profile" 14 | fi 15 | 16 | # If this login shell is interactive, we should source .bashrc and 17 | # .bash_login as well, in order to setup the right environment and 18 | # make sure a welcome message is shown when entering the container. 19 | if [ "${PS1-}" ]; then 20 | # Note that ~/.profile (if it exists) may or may not have also 21 | # sourced .bashrc. Ideally .bashrc scripts should be idempotent so 22 | # as to this to not cause any problems. 23 | if [ -f "$HOME/.bashrc" ]; then 24 | . "$HOME/.bashrc" 25 | fi 26 | 27 | # Bash won't load .bash_login if .bash_profile has already been 28 | # found, so we have to make sure we load it from here too. 29 | if [ -f "$HOME/.bash_login" ]; then 30 | . "$HOME/.bash_login" 31 | fi 32 | fi 33 | -------------------------------------------------------------------------------- /scripts/wkdev-start-profiling: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2024 Igalia S.L. 3 | # SPDX-License: MIT 4 | 5 | [ -f "${WKDEV_SDK}/.wkdev-sdk-root" ] && source "${WKDEV_SDK}/utilities/application.sh" || { echo "Please set \${WKDEV_SDK} to point to the root of the wkdev-sdk checkout."; exit 1; } 6 | init_application "${0}" "Modify 'perf' related host kernel parameters to enable CPU profiling for unprivileged users." host-and-container 7 | 8 | # Source utility script fragments 9 | source "${WKDEV_SDK}/utilities/cpu-profiling.sh" 10 | 11 | argsparse_use_option trace "Enable 'xtrace' mode for this script" 12 | 13 | argsparse_usage_description="$(cat <> 15 | 16 | $(get_perf_settings_help_message) 17 | 18 | \`${application_name}\` remembers the current setting, and switches to '$(get_desired_perf_event_kernel_setting_value_for_profiling)'. Use \`wkdev-stop-profiling\`, the 19 | companion tool, to restore the original 'kernel.$(get_perf_event_kernel_setting)' setting. Both tools will ask for superuser privileges. 20 | EOF 21 | )" 22 | 23 | process_command_line_arguments() { 24 | 25 | # Allow empty command line 26 | argsparse_allow_no_argument yes 27 | 28 | argsparse_parse_options "${@}" 29 | argsparse_is_option_set "trace" && set -o xtrace 30 | } 31 | 32 | # Main functionality 33 | run() { 34 | 35 | process_command_line_arguments "${@}" 36 | enable_perf_settings_for_cpu_profiling 37 | } 38 | 39 | run "${@}" 40 | -------------------------------------------------------------------------------- /images/wkdev_sdk/user_home_directory_defaults/dot-zshrc: -------------------------------------------------------------------------------- 1 | # Keep 1000 lines of history within the shell and save it to ~/.zsh_history: 2 | HISTSIZE=1000 3 | SAVEHIST=1000 4 | HISTFILE=~/.zsh_history 5 | 6 | # Additional options 7 | setopt PROMPT_SUBST 8 | setopt extendedglob 9 | setopt nonomatch 10 | setopt autopushd pushdminus pushdsilent pushdtohome 11 | setopt autocd 12 | setopt hist_ignore_dups 13 | setopt share_history 14 | setopt append_history 15 | setopt correct 16 | 17 | # Colors 18 | autoload -U colors && colors 19 | 20 | # Completion 21 | autoload -U compinit && compinit 22 | 23 | # VCs 24 | autoload -Uz vcs_info 25 | autoload -Uz run-help-git 26 | precmd() { vcs_info } 27 | zstyle ':vcs_info:*' enable git cvs 28 | zstyle ':vcs_info:*' check-for-changes true 29 | zstyle ':vcs_info:*' get-revision true 30 | zstyle ':vcs_info:*' stagedstr "%F{green}+%f" 31 | zstyle ':vcs_info:*' unstagedstr "%F{red}*%f" 32 | zstyle ':vcs_info:*' formats "[%F{cyan}%b%f%u%c]" 33 | zstyle ':vcs_info:*' actionformats "%u%c[%F{red}%a%f]" 34 | 35 | # Word style 36 | autoload -U select-word-style 37 | select-word-style bash 38 | 39 | # Setup prompt 40 | PROMPT='📦 %{$fg[white]%}%B%n@%m%b%{$reset_color%}:%{$fg[blue]%}%B%~/%b%{$reset_color%}%(!.#.$) ' 41 | RPROMPT='${vcs_info_msg_0_}%{$fg[red]%}%(?.. [%?])%{$reset_color%} [%*]' 42 | 43 | # enable color support for grep/ls 44 | alias grep='grep --color=auto' 45 | alias ls='ls --color=auto' 46 | 47 | # rm, cp and mv aliases 48 | alias rm='rm -i' 49 | alias cp='cp -i -v' 50 | alias mv='mv -i -v' 51 | -------------------------------------------------------------------------------- /scripts/wkdev-stop-profiling: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2024 Igalia S.L. 3 | # SPDX-License: MIT 4 | 5 | [ -f "${WKDEV_SDK}/.wkdev-sdk-root" ] && source "${WKDEV_SDK}/utilities/application.sh" || { echo "Please set \${WKDEV_SDK} to point to the root of the wkdev-sdk checkout."; exit 1; } 6 | init_application "${0}" "Modify 'perf' related host kernel parameters to disable CPU profiling for unprivileged users." host-and-container 7 | 8 | # Source utility script fragments 9 | source "${WKDEV_SDK}/utilities/cpu-profiling.sh" 10 | 11 | argsparse_use_option trace "Enable 'xtrace' mode for this script" 12 | 13 | argsparse_usage_description="$(cat <> 15 | 16 | $(get_perf_settings_help_message) 17 | 18 | \`${application_name}\` restores the original 'kernel.$(get_perf_event_kernel_setting)' setting to the value before 19 | \`wkdev-start-profiling\` switched the value to '$(get_desired_perf_event_kernel_setting_value_for_profiling)'. 20 | 21 | NOTE: It is not recommended to leave the 'kernel.perf*' parameters at '$(get_desired_perf_event_kernel_setting_value_for_profiling)' all the time, due to security concerns! 22 | EOF 23 | )" 24 | 25 | process_command_line_arguments() { 26 | 27 | # Allow empty command line 28 | argsparse_allow_no_argument yes 29 | 30 | argsparse_parse_options "${@}" 31 | argsparse_is_option_set "trace" && set -o xtrace 32 | } 33 | 34 | # Main functionality 35 | run() { 36 | 37 | process_command_line_arguments "${@}" 38 | disable_perf_settings_for_cpu_profiling 39 | } 40 | 41 | run "${@}" 42 | -------------------------------------------------------------------------------- /docs/01-Building-the-SDK.md: -------------------------------------------------------------------------------- 1 | ## Building the SDK 2 | 3 | `wkdev-sdk-bakery` encapsulates the whole process of building the SDK including all images. Just try it: 4 | 5 | ```sh 6 | wkdev-sdk-bakery --mode build 7 | ``` 8 | 9 | ### Adding patched projects to the SDK 10 | 11 | If you want to build a patched version of a library to the SDK the easiest method is using JHBuild. 12 | 13 | The [modulesets](https://gnome.pages.gitlab.gnome.org/jhbuild/moduleset-syntax.html) we use are 14 | located in `images/wkdev_sdk/jhbuild/webkit-sdk-deps.modules` and you can add a new module there. 15 | 16 | A simple example of a project with a patch would be: 17 | 18 | ```xml 19 | 20 | 25 | 26 | 27 | 28 | 29 | ``` 30 | 31 | You can use a local file also but you will have to `COPY` it in `images/wkdev_sdk/Containerfile`. 32 | 33 | This only works for tarballs, if you have a git source you can specify a different branch/commit with `tag`: 34 | 35 | ```xml 36 | 37 | 40 | 41 | ``` 42 | 43 | If you want it to be in the SDK you must then modify the `` element in 44 | `webkit-sdk-deps.modules`, otherwise it will only be built when a user explicitly runs `jhbuild build ${module}`. 45 | -------------------------------------------------------------------------------- /utilities/debian-packages.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | [ -z "${application_ready}" ] && { echo "[FATAL] You need to source 'utilities/application.sh' before sourcing this script."; return 1; } 4 | source "${WKDEV_SDK}/utilities/prerequisites.sh" 5 | 6 | verify_executables_exist apt-get dpkg-query 7 | 8 | update_packages() { apt-get update; } 9 | 10 | add_ppa() { 11 | 12 | local ppa_repo="${1}" # e.g. project/name 13 | local name="${2}" 14 | local signing_key="${3}" 15 | local keyring="/etc/apt/keyrings/${name}.gpg" 16 | 17 | mkdir "${HOME}/.gnupg" 18 | dirmngr --daemon 19 | gpg --no-default-keyring --keyring="/etc/apt/keyrings/${name}.gpg" --keyserver=keyserver.ubuntu.com --recv-keys "${signing_key}" || _abort_ "Failed to import key for ${ppa_repo}" 20 | dirmngr --shutdown 21 | rm -r "${HOME}/.gnupg" 22 | 23 | echo "deb [signed-by=${keyring}] http://ppa.launchpad.net/${ppa_repo}/ubuntu noble main 24 | deb-src [signed-by=${keyring}] http://ppa.launchpad.net/${ppa_repo}/ubuntu noble main" > "/etc/apt/sources.list.d/${name}.list" 25 | } 26 | 27 | is_package_installed() { 28 | 29 | local package="${1}" 30 | [ $(dpkg-query --show --showformat='${Status}' "${package}" 2>/dev/null | grep --count "ok installed") -eq 0 ] && return 1 31 | } 32 | 33 | ensure_package_installed() { 34 | 35 | local package="${1}" 36 | is_package_installed "${package}" && return 0 37 | apt-get --assume-yes install "${package}" || _abort_ "Cannot install package '${package}': executing 'apt-get install' failed" 38 | } 39 | 40 | upgrade_packages() { 41 | 42 | local packages="${@}" 43 | 44 | apt-get upgrade -y "${packages}" || _abort_ "Cannot upgrade packages '${packages}': executing 'apt-get upgrade' failed" 45 | } 46 | -------------------------------------------------------------------------------- /utilities/nvidia-gpu.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | [ -z "${application_ready}" ] && { echo "[FATAL] You need to source 'utilities/application.sh' before sourcing this script."; return 1; } 4 | source "${WKDEV_SDK}/utilities/prerequisites.sh" 5 | 6 | verify_executable_exists lsmod 7 | 8 | # Checks if kernel modules named 'nvidia....' are loaded. 9 | is_nvidia_kernel_module_loaded() { 10 | 11 | # Check kernel modules named 'nvidia...' are loaded. 12 | local nvidia_modules=$(lsmod | grep --count "^nvidia") 13 | [ ${nvidia_modules} -gt 0 ] || return 1 14 | } 15 | 16 | # Checks if an NVIDIA GPU is available for use (on the host!) 17 | is_nvidia_gpu_installed() { 18 | 19 | is_nvidia_kernel_module_loaded || return 1 20 | 21 | # Check NVIDIA tools are available. 22 | does_executable_exist nvidia-smi || return 1 23 | 24 | # Check nvidia-smi returns an zero exit code. 25 | run_command_silent nvidia-smi || return 1 26 | } 27 | 28 | get_nvidia_ubuntu_distribution() { echo "stable/deb"; } 29 | get_nvidia_host_package() { echo "nvidia-container-toolkit-base"; } # to be installed on host to create CDI specs 30 | get_nvidia_container_package() { echo "nvidia-utils-550"; } # to be installed in the container to have 'nvidia-smi' available 31 | 32 | get_nvidia_repository_name() { echo "libnvidia-container"; } 33 | get_nvidia_repository_url() { echo "https://nvidia.github.io/$(get_nvidia_repository_name)"; } 34 | get_nvidia_apt_source_file() { echo "/etc/apt/sources.list.d/$(get_nvidia_repository_name).list"; } 35 | get_nvidia_gpg_key_file() { echo "/etc/apt/trusted.gpg.d/$(get_nvidia_repository_name).gpg"; } 36 | get_nvidia_cdi_config_file() { echo "/etc/cdi/nvidia.yaml"; } 37 | get_nvidia_gpu_profiling_conf_file() { echo "/etc/modprobe.d/nvidia-gpu-profiling.conf"; } 38 | -------------------------------------------------------------------------------- /docs/04-Cross-compilation-inside-the-SDK.md: -------------------------------------------------------------------------------- 1 | ## Cross-compiling inside the SDK 2 | 3 | To cross-compile WebKit for different machines, WebKit provides a yocto based environment. 4 | See /path/to/WebKit/Tools/yocto/README.md for details. You do not need to cross-compile from 5 | within the SDK, but if you want to do it, here are some instructions: 6 | 7 | First be sure to setup the environment, so that yocto caches downloads/sstate information: 8 | ``` 9 | export DL_DIR="${HOME}/.cache/yocto/downloads" 10 | export SSTATE_DIR="${HOME}/.cache/yocto/sstate" 11 | export BB_ENV_PASSTHROUGH_ADDITIONS="${BB_ENV_PASSTHROUGH_ADDITIONS} DL_DIR SSTATE_DIR" 12 | ``` 13 | 14 | Then proceed with the compilation: 15 | 16 | ``` 17 | unset LD_LIBRARY_PATH 18 | export NUMBER_OF_PROCESSORS=12 # Adapt to your machine. 19 | export WEBKIT_USE_SCCACHE=0 20 | Tools/Scripts/cross-toolchain-helper --cross-target rpi3-32bits-mesa --build-image 21 | Tools/Scripts/cross-toolchain-helper --cross-target rpi3-32bits-mesa --build-toolchain 22 | Tools/Scripts/cross-toolchain-helper --cross-target rpi3-32bits-mesa --cross-toolchain-run-cmd CFLAGS="" CPPFLAGS="" WebKitBuild/CrossToolChains/rpi3-32bits-mesa/build/toolchain/sysroots/x86_64-pokysdk-linux/post-relocate-setup.d/meson-setup.py 23 | Tools/Scripts/cross-toolchain-helper --cross-target rpi3-32bits-mesa --cross-toolchain-run-cmd Tools/Scripts/build-webkit --wpe --release 24 | ``` 25 | 26 | It is important to unset the `LD_LIBRARY_PATH` otherwise `cross-toolchain-helper` will partly fail, 27 | but `build-webkit` continues, leading to an inconsistent build environment, that will fail to produce binaries. 28 | 29 | sccache is also not supported in that mode, and will interfere with yocto -- disable it. 30 | 31 | Follow the instructions in Tools/yocto/README.md to flash the image onto your target machine. 32 | -------------------------------------------------------------------------------- /utilities/settings.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | [ -z "${application_ready}" ] && { echo "[FATAL] You need to source 'utilities/application.sh' before sourcing this script."; return 1; } 4 | 5 | ##### SDK maintainer information 6 | get_sdk_maintainer_name() { echo "Igalia"; } 7 | get_sdk_maintainer_email() { echo "webkit-gtk@lists.webkit.org"; } 8 | 9 | ##### 10 | ##### Host/container environment detection 11 | ##### 12 | get_init_done_file() { echo "/run/.wkdev-init-done"; } 13 | is_running_in_container() { [ -f "/run/.containerenv" ]; } 14 | is_running_in_wkdev_sdk_container() { is_running_in_container && [ -f "/usr/bin/podman-host" ]; } 15 | 16 | ##### 17 | ##### Container registry 18 | ##### 19 | get_default_container_registry() { echo "${WKDEV_SDK_CONTAINER_REGISTRY:-ghcr.io}"; } 20 | get_default_container_registry_user_name() { echo "${WKDEV_SDK_CONTAINER_REGISTRY_USER_NAME:-igalia}"; } 21 | 22 | ##### 23 | ##### Container naming/versioning 24 | ##### 25 | get_default_container_tag() { 26 | local default='latest' 27 | 28 | if [[ "$(git -C "${WKDEV_SDK}" rev-parse --abbrev-ref HEAD)" =~ tag/(.*) ]]; then 29 | default="${BASH_REMATCH[1]}" 30 | fi 31 | 32 | echo "${WKDEV_SDK_TAG:-"${default}"}"; 33 | } 34 | 35 | # Given an image name, return the qualified image name "//" 36 | get_qualified_name() { 37 | 38 | local image_name="${1}" 39 | echo "$(get_default_container_registry)/$(get_default_container_registry_user_name)/${image_name}" 40 | } 41 | 42 | 43 | # Get absolute path to 'user_home_directory_defaults' directory in the wkdev-sdk. 44 | get_container_home_defaults_directory_name() { echo "${WKDEV_SDK}/images/wkdev_sdk/user_home_directory_defaults"; } 45 | 46 | ##### wkdev-sdk definitions 47 | get_sdk_image_name() { echo "wkdev-sdk"; } 48 | get_sdk_qualified_name() { get_qualified_name "$(get_sdk_image_name)"; } 49 | -------------------------------------------------------------------------------- /docs/02-Building-third-party-libraries.md: -------------------------------------------------------------------------------- 1 | ## Building third-party libraries 2 | 3 | As the container is a normal Ubuntu installation there are many ways to install custom libraries 4 | however we have a solution to make this easier. [JHBuild](https://gnome.pages.gitlab.gnome.org/jhbuild/index.html) 5 | is a tool that automates downloading, building, and installing projects and is provided in the SDK. 6 | 7 | #### Hacking on glib with JHBuild 8 | 9 | 1. Build glib master 10 | 11 | ```sh 12 | jhbuild build glib 13 | ``` 14 | 15 | This will do an initial build of glib and its dependencies. 16 | 17 | 2. Modify and reinstall glib 18 | 19 | All of the sources are located in `~/checkout`. 20 | 21 | ```sh 22 | cd ~/checkout/glib 23 | # Modify as you please 24 | jhbuild make 25 | ``` 26 | 27 | Note that `jhbuild make` only works in the checkout directory. If you have sources 28 | in another location you can make a symlink matching the module name. 29 | 30 | You can remove your modified version with `jhbuild uninstall glib` though do note 31 | that some JHBuild modules such as GStreamer are included by default and should not 32 | be uninstalled. 33 | 34 | #### Hacking on other libraries in JHBuild 35 | 36 | We provide a small list of projects that can be easily hacked on. You can view 37 | the projects JHBuild knows about with `jhbuild list --all-modules`. The process 38 | is identical for all of these projects. 39 | 40 | If you want to add a new project you can make [a moduleset file](https://gnome.pages.gitlab.gnome.org/jhbuild/moduleset-syntax.html) 41 | to use the same workflow. Examples can be found in `/jhbuild/webkit-sdk-deps.modules` 42 | (our default modules) and `/jhbuild/jhbuild/modulesets/`. You can then build a 43 | custom moduleset with `jhbuild -m ~/myproject.modules build myproject` for example. 44 | 45 | It is also possible to directly build any project like so: 46 | 47 | ```sh 48 | jhbuild shell 49 | # For CMake 50 | cmake -DCMAKE_INSTALL_PREFIX=$JHBUILD_PREFIX -DCMAKE_INSTALL_LIBDIR=$JHBUILD_LIBDIR ... 51 | # For Meson 52 | meson setup --prefix=$JHBUILD_PREFIX --libdir=$JHBUILD_LIBDIR ... 53 | ``` -------------------------------------------------------------------------------- /docs/07-Using-sysprof.md: -------------------------------------------------------------------------------- 1 | # Using sysprof with webkit-container-sdk 2 | 3 | This guide explains how to profile applications using `sysprof-cli` from within the webkit-container-sdk container and how to configure passwordless profiling on the host system. 4 | 5 | ## Overview 6 | 7 | `sysprof-cli` is a system profiling tool that captures performance data from running applications. When used from within a container, it communicates with the host's `sysprofd` daemon via D-Bus, which requires authentication through polkit by default. 8 | 9 | ## Basic Usage 10 | 11 | To profile an application with sysprof-cli: 12 | 13 | ```bash 14 | sysprof-cli -f capture.syscap -- your-application [arguments] 15 | ``` 16 | 17 | For example, to profile MiniBrowser: 18 | ```bash 19 | sysprof-cli -f profile.syscap -- run-minibrowser --wpe -- --fullscreen https://example.com 20 | ``` 21 | 22 | ## Configuring Passwordless Profiling 23 | 24 | By default, sysprof requires authentication each time you start profiling. To avoid this, you can create a polkit rule on the host system. 25 | 26 | ### Step 1: Create a polkit rule 27 | 28 | Create a file with the following content: 29 | 30 | ```bash 31 | sudo tee /etc/polkit-1/rules.d/99-sysprof-noauth.rules > /dev/null << EOF 32 | // Allow specific user to use sysprof without authentication 33 | polkit.addRule(function(action, subject) { 34 | if (action.id == "org.gnome.sysprof3.profile" && 35 | subject.user == "$USERNAME") { 36 | return polkit.Result.YES; 37 | } 38 | }); 39 | EOF 40 | 41 | # Set correct permissions 42 | sudo chmod 644 /etc/polkit-1/rules.d/99-sysprof-noauth.rules 43 | 44 | # Restart polkit to apply changes 45 | sudo systemctl restart polkit 46 | ``` 47 | 48 | ### Step 2: Verify the configuration 49 | 50 | After installing the rule, you should be able to run `sysprof-cli` from within the container without being prompted for authentication. 51 | 52 | ## Viewing Profile Results 53 | 54 | After capturing a profile, you can view it using the sysprof GUI application: 55 | 56 | ```bash 57 | # From the host system 58 | sysprof capture.syscap 59 | ``` 60 | -------------------------------------------------------------------------------- /test-sdk-build-and-run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2024 Igalia S.L. 3 | # SPDX-License: MIT 4 | 5 | [ -f "${WKDEV_SDK}/.wkdev-sdk-root" ] || { echo "Please set \${WKDEV_SDK} to point to the root of the wkdev-sdk checkout."; exit 1; } 6 | 7 | # NOTE: This doesn't make use of utilities/application.sh on purpose. 8 | # This script is intended to be used for CI later and thus needs to 9 | # be revisited anyhow at a later time. 10 | 11 | test_container_name="wkdev-bootstrap" 12 | test_home_directory="/tmp/${test_container_name}-home" 13 | 14 | # This script is trivial enough to justify 'set -o errexit' usage. 15 | set -o errexit # Exit upon command failure 16 | 17 | # Warn about unset variables 18 | set -o nounset 19 | 20 | # 1) Build SDK 21 | echo "[1/6] Building SDK..." 22 | ${WKDEV_SDK}/scripts/host-only/wkdev-sdk-bakery --mode build --verbose 23 | 24 | # 2) Test creation of container 25 | echo "" 26 | echo "[2/6] Creating '${test_container_name}' container with fresh home directory in '${test_home_directory}'..." 27 | ${WKDEV_SDK}/scripts/host-only/wkdev-create --verbose --create-home --home "${test_home_directory}" --name "${test_container_name}" 28 | 29 | # 3) Test executing a command in the container 30 | uptime_command="uptime --pretty" 31 | echo "" 32 | echo "[3/6] Executing '${uptime_command}' in '${test_container_name}' container..." 33 | ${WKDEV_SDK}/scripts/host-only/wkdev-enter --verbose --exec --name "${test_container_name}" -- ${uptime_command} 34 | 35 | # 4) Stop container 36 | echo "" 37 | echo "[4/6] Stopping '${test_container_name}' container..." 38 | podman stop "${test_container_name}" &>/dev/null 39 | 40 | # 5) Delete container 41 | echo "" 42 | echo "[5/6] Deleting '${test_container_name}' container..." 43 | podman rm --volumes "${test_container_name}" &>/dev/null 44 | 45 | # 6) Delete home directory 46 | echo "" 47 | echo "[6/6] Deleting '${test_container_name}' home directory..." 48 | podman unshare rm -rf "${test_home_directory}" &>/dev/null 49 | 50 | # 7) Show instructions how to deploy the new SDK image 51 | echo "" 52 | echo "Ready. If everything went well, use 'scripts/host-only/wkdev-sdk-bakery --mode deploy' to push the new SDK image to the registry, once tested!" 53 | -------------------------------------------------------------------------------- /scripts/wkdev: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2024 Igalia S.L. 3 | # SPDX-License: MIT 4 | 5 | [ -f "${WKDEV_SDK}/.wkdev-sdk-root" ] && source "${WKDEV_SDK}/utilities/application.sh" || { echo "Please set \${WKDEV_SDK} to point to the root of the wkdev-sdk checkout."; exit 1; } 6 | init_application "${0}" "Launcher for wkdev commands" host-and-container 7 | 8 | usage() { 9 | echo "${application_name} COMMAND [ARGS...]" 10 | echo "" 11 | echo "These are common wkdev commands used in various situations:" 12 | wkdev_generic_commands 13 | echo "" 14 | echo "These are wkdev commands intended for execution on the host system:" 15 | wkdev_host_commands 16 | echo "" 17 | echo "These are wkdev commands intended for execution on the container:" 18 | wkdev_container_commands 19 | } 20 | 21 | wkdev_generic_commands() { 22 | for item in "${application_directory}"/wkdev-* ; do 23 | [[ -x $item ]] || continue 24 | echo " - ${item##*/}" 25 | done 26 | } 27 | 28 | wkdev_host_commands() { 29 | for item in "${application_directory}"/host-only/wkdev-* ; do 30 | [[ -x $item ]] || continue 31 | echo " - ${item##*/}" 32 | done 33 | } 34 | 35 | wkdev_container_commands() { 36 | for item in "${application_directory}"/container-only/wkdev-* ; do 37 | [[ -x $item ]] || continue 38 | echo " - ${item##*/}" 39 | done 40 | } 41 | 42 | find_wkdev_command() { 43 | local command="${1}" 44 | 45 | for item in "${application_directory}"/wkdev-* \ 46 | "${application_directory}"/host-only/wkdev-* \ 47 | "${application_directory}"/container-only/wkdev-* 48 | do 49 | [[ -x $item ]] || continue 50 | 51 | if [[ ${item##*/wkdev-} = $command ]] ; then 52 | echo "$item" 53 | return 54 | fi 55 | done 56 | 57 | return 1 58 | } 59 | 60 | # Main. 61 | if [[ ${#} -eq 0 ]]; then 62 | usage 63 | exit 64 | fi 65 | 66 | COMMAND="${1}" 67 | shift 68 | 69 | # Find command in list of all available wkdev- commands. 70 | COMMAND=$(find_wkdev_command "${COMMAND}") 71 | 72 | # Execute command. 73 | if [[ -x "${COMMAND}" ]]; then 74 | ${COMMAND} ${@} 75 | fi 76 | 77 | -------------------------------------------------------------------------------- /scripts/container-only/wkdev-setup-clang: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2024 Igalia S.L. 3 | # SPDX-License: MIT 4 | 5 | if [ -f "${WKDEV_SDK}/.wkdev-sdk-root" ]; then 6 | source "${WKDEV_SDK}/utilities/application.sh" 7 | else 8 | echo "Please set \${WKDEV_SDK} to point to the root of the wkdev-sdk checkout." 9 | exit 1 10 | fi 11 | 12 | min_clang_version=14 13 | max_clang_version=22 14 | 15 | init_application "${0}" "Installs and creates symlinks to set default clang executables" container-only 16 | 17 | argsparse_use_option "=version:" "The clang version between ${min_clang_version}-${max_clang_version}" "type:uint" 18 | argsparse_use_option "install-only" "Only install clang packages, do not create symlinks to set as default" 19 | argsparse_use_option "latest" "Automatically install the latest available clang version" 20 | 21 | argsparse_usage_description="$(cat <> 23 | 24 | Provides an easy way to install and switch between clang toolchains. 25 | 26 | << Examples >> 27 | 28 | $ ${application_name} --version=17 29 | $ ${application_name} --latest 30 | EOF 31 | )" 32 | 33 | run() { 34 | 35 | argsparse_parse_options "${@}" 36 | 37 | _log_ "" 38 | 39 | local version 40 | if argsparse_is_option_set "latest"; then 41 | version="${max_clang_version}" 42 | elif argsparse_is_option_set "version"; then 43 | version="${program_options["version"]}" 44 | # Sanity check versions Ubuntu actually has. 45 | if (( ${version} < ${min_clang_version} )) || (( ${version} > ${max_clang_version})); then 46 | _log_ "${version} is not a valid value (between ${min_clang_version}-${max_clang_version})." 47 | exit 1 48 | fi 49 | else 50 | _log_ "Either --version or --latest must be specified." 51 | exit 1 52 | fi 53 | 54 | if [ ! -f "/usr/bin/clang-${version}" ]; then 55 | _log_ "Installing clang toolchain version ${version}" 56 | _log_ "" 57 | if ! sudo apt-get install "clang-tools-${version}" "clangd-${version}" "clang-format-${version}" "clang-tidy-${version}" "lld-${version}" "lldb-${version}" "llvm-${version}"; then 58 | _log_ "" 59 | _log_ "Failed to install clang toolchain" 60 | exit 1 61 | fi 62 | fi 63 | 64 | if argsparse_is_option_set "install-only"; then 65 | _log_ "Skipping symlink creation (--install-only specified)" 66 | exit 0 67 | fi 68 | 69 | local output_path 70 | if [ "$EUID" -eq 0 ]; then 71 | output_path="/usr/local/bin" 72 | else 73 | output_path="${HOME}/.local/bin" 74 | fi 75 | _log_ "Creating symlinks in ${output_path}" 76 | "${WKDEV_SDK}/scripts/helpers/create-clang-symlinks" "${version}" "${output_path}" 77 | } 78 | 79 | run "${@}" 80 | -------------------------------------------------------------------------------- /docs/06-A-gdb-debugging-example.md: -------------------------------------------------------------------------------- 1 | ## An example of how to debug WebKit GTK with gdb from a WebKit container 2 | 3 | 1. Build WebKit from a container: `./Tools/Scripts/build-webkit --debug --gtk` 4 | 2. Run MiniBrowser, from above container, with some file to debug, e.g.: 5 | ``` 6 | ./Tools/Scripts/run-minibrowser --debug --gtk LayoutTests/imported/w3c/web-platform-tests/css/css-fonts/lang-attribute-affects-rendering.html 7 | ``` 8 | 3. Enter the same container in a different tab. 9 | 4. Find the logged `PID` of `WebKitWebProcess`, e.g. as follows: 10 | ``` 11 | mirko@wkdev:/host/home/mirko/work/code/WebKit$ ps -fA | grep -w "WebKitWebProcess\|PID" 12 | UID PID PPID C STIME TTY TIME CMD 13 | mirko 13460 13424 0 09:42 pts/0 00:00:00 /usr/bin/bwrap --args 34 -- /host/home/mirko/work/code/WebKit/WebKitBuild/GTK/Debug/bin/WebKitWebProcess 13 26 28 14 | mirko 13461 13460 0 09:42 pts/0 00:00:02 /host/home/mirko/work/code/WebKit/WebKitBuild/GTK/Debug/bin/WebKitWebProcess 13 26 28 15 | mirko 15736 13961 0 10:08 pts/1 00:00:00 grep --color=auto -w WebKitWebProcess\|PID 16 | ``` 17 | 5. Attach gdb to the process: 18 | ``` 19 | gdb -p 13461 20 | ``` 21 | and wait a little, this may download some files. That may end with a warning but that won't prevent debugging: 22 | ``` 23 | [...] 24 | Downloading separate debug info for system-supplied DSO at 0x7fff33477000 25 | [Thread debugging using libthread_db enabled] 26 | Using host libthread_db library "/usr/lib/x86_64-linux-gnu/libthread_db.so.1". 27 | Download failed: Invalid argument. Continuing without source file ./io/../sysdeps/unix/sysv/linux/poll.c. 28 | 0x00007b01163094cd in __GI___poll (fds=0x5c19f4ea9150, nfds=2, timeout=28649) at ../sysdeps/unix/sysv/linux/poll.c:29 29 | 30 | warning: 29 ../sysdeps/unix/sysv/linux/poll.c: No such file or directory 31 | ``` 32 | 6. Set a breakpoint at some interesting function which is known to be called and continue, e.g.: 33 | ``` 34 | (gdb) b FontPlatformData::platformDataInit 35 | Breakpoint 1 at 0x7b012a45ec83: file /host/home/mirko/work/code/WebKit/Source/WebCore/platform/graphics/skia/FontPlatformDataSkia.cpp, line 65. 36 | (gdb) c 37 | ``` 38 | 7. In the MiniBrowser, reload the tab, e.g. via CTRL+F5 to not reuse cached content. The`PID` of the tab will stay the same. 39 | 9. Observe that `gdb` hit the breakpoint: 40 | ``` 41 | Thread 1 "WebKitWebProces" hit Breakpoint 1, WebCore::FontPlatformData::platformDataInit (this=0x7fff3340fb10) 42 | at /host/home/mirko/work/code/WebKit/Source/WebCore/platform/graphics/skia/FontPlatformDataSkia.cpp:65 43 | 65 { 44 | (gdb) 45 | 46 | ``` 47 | 10. Examine the call stack, e.g. to learn the control flow of the code. 48 | 49 | If one uses this procedure more frequently, step 4 and 5 can be replaced with an `alias` in `.bashrc`: 50 | ``` 51 | alias gdb-first-WebKitWebProcess='ps -fA | grep -v "bwrap\|grep" | grep -w "WebKitWebProcess\|PID" | grep -o "[0-9]*" | head --lines=1 | xargs --open-tty gdb -p' 52 | ``` 53 | -------------------------------------------------------------------------------- /scripts/container-only/wkdev-setup-devhelp: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2024 Igalia S.L. 3 | # SPDX-License: MIT 4 | 5 | if [ -f "${WKDEV_SDK}/.wkdev-sdk-root" ]; then 6 | source "${WKDEV_SDK}/utilities/application.sh" 7 | else 8 | echo "Please set \${WKDEV_SDK} to point to the root of the wkdev-sdk checkout." 9 | exit 1 10 | fi 11 | 12 | init_application "${0}" "Configures devhelp to use WebKit's built documentation." container-only 13 | 14 | argsparse_use_option "webkit-=directory:" "The path to your WebKit source" "mandatory" "type:directory" 15 | argsparse_use_option "release" "Use the Release build's documentation" "exclude:debug" 16 | argsparse_use_option "wpe" "Use the WPE port's documentation instead of GTK" 17 | argsparse_use_option "debug" "Use the debug build's documentation" "exclude:release" 18 | argsparse_use_option "container-specific" "Install the documentation private to this container" 19 | 20 | argsparse_usage_description="$(cat <> 22 | 23 | Sets up links to the the devhelp documentation for a specific WebKit build. 24 | 25 | By default this will survive recreating the container and you only need to 26 | run this when you want to add new books or change the directory. 27 | 28 | However you can make the documentation private to this container by passing 29 | the '--container-specific' option. 30 | 31 | << Examples >> 32 | 33 | $ ${application_name} --webkit-directory=./ --debug 34 | $ ${application_name} --webkit-directory=\$HOME/WebKit --release 35 | EOF 36 | )" 37 | 38 | run() { 39 | 40 | argsparse_parse_options "${@}" 41 | local webkit_directory="${program_options["webkit-directory"]}" 42 | local build_type="$(argsparse_is_option_set 'release' && echo 'Release' || echo 'Debug')" 43 | local port="$(argsparse_is_option_set 'wpe' && echo 'WPE' || echo 'GTK')" 44 | local docs_dir="${webkit_directory}/WebKitBuild/${port}/${build_type}/Documentation" 45 | 46 | _log_ "" 47 | 48 | if [ ! -d "${webkit_directory}/WebKit.xcworkspace" ]; then 49 | _log_ "Passed directory does not appear to be a WebKit source directory: ${webkit_directory}" 50 | exit 1 51 | fi 52 | 53 | if [ ! -d "${docs_dir}" ]; then 54 | _log_ "WebKit ${build_type} has not been built with documentation." 55 | exit 1 56 | fi 57 | 58 | local books_dir 59 | 60 | if argsparse_is_option_set "container-specific"; then 61 | books_dir="/jhbuild/install/share/devhelp/books" 62 | else 63 | books_dir="${XDG_DATA_HOME-"${HOME}/.local/share"}/devhelp/books" 64 | fi 65 | 66 | mkdir -p "${books_dir}" 67 | 68 | _log_ "$( 69 | echo "Linking documentation:" 70 | printf " Source:\t %s\n" "${docs_dir}" 71 | printf " Destination:\t %s\n" "${books_dir}" 72 | printf " Books:\t" 73 | for dir in "${docs_dir}"/*; do 74 | echo -n " $(basename "${dir}")" 75 | ln --symbolic --force "${dir}" "${books_dir}/$(basename "${dir}")" 76 | done 77 | echo "" 78 | )" 79 | 80 | _log_ "" 81 | _log_ "Simply run 'devhelp' to access the documentation." 82 | } 83 | 84 | run "${@}" 85 | -------------------------------------------------------------------------------- /utilities/cpu-profiling.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | [ -z "${application_ready}" ] && { echo "[FATAL] You need to source 'utilities/application.sh' before sourcing this script."; return 1; } 4 | source "${WKDEV_SDK}/utilities/kernel-parameters.sh" 5 | 6 | get_perf_event_kernel_setting() { echo "perf_event_paranoid"; } 7 | get_perf_event_procfs_path() { echo "/proc/sys/kernel/$(get_perf_event_kernel_setting)"; } 8 | get_preserved_perf_event_kernel_setting_file() { echo "${WKDEV_SDK}/.sysctl_setting_$(get_perf_event_kernel_setting)"; } 9 | get_desired_perf_event_kernel_setting_value_for_profiling() { echo "-1"; } 10 | 11 | # Helper function to explain the purpose of this setting 12 | get_perf_settings_help_message() { 13 | cat < The kernel parameter 'kernel.${perf_event_setting}' needs to be modified to allow for CPU profiling (current value: '${current_value}'), switching to '${desired_value}'..." 29 | _log_ "${current_value}" > "$(get_preserved_perf_event_kernel_setting_file)" 30 | write_kernel_parameter "$(get_perf_event_kernel_setting)" "$(get_desired_perf_event_kernel_setting_value_for_profiling)" 31 | else 32 | _log_ "-> The kernel parameter 'kernel.${perf_event_setting}' is set to the correct value '${current_value}' - no need to modify." 33 | fi 34 | } 35 | 36 | # Helper function to disable CPU profiling related host kernel settings (and switch back to the original values). 37 | disable_perf_settings_for_cpu_profiling() { 38 | 39 | local perf_event_setting="$(get_perf_event_kernel_setting)" 40 | local perf_event_preserved_file="$(get_preserved_perf_event_kernel_setting_file)" 41 | if [ -f "${perf_event_preserved_file}" ]; then 42 | local current_value="$(read_kernel_parameter "${perf_event_setting}")" 43 | local preserved_value="$(cat "${perf_event_preserved_file}")" 44 | _log_ "-> Restoring kernel parameter 'kernel.${perf_event_setting}' (current value: '${current_value}') to its original value '${preserved_value}'..." 45 | write_kernel_parameter "$(get_perf_event_kernel_setting)" "${preserved_value}" 46 | rm -f "${perf_event_preserved_file}" 47 | else 48 | _log_ "-> The kernel parameter 'kernel.${perf_event_setting}' was not modified by the SDK -- no need to restore a value." 49 | fi 50 | } 51 | -------------------------------------------------------------------------------- /scripts/container-only/wkdev-setup-sccache: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2024 Igalia S.L. 3 | # SPDX-License: MIT 4 | 5 | if [ -f "${WKDEV_SDK}/.wkdev-sdk-root" ]; then 6 | source "${WKDEV_SDK}/utilities/application.sh" 7 | else 8 | echo "Please set \${WKDEV_SDK} to point to the root of the wkdev-sdk checkout." 9 | exit 1 10 | fi 11 | source "${WKDEV_SDK}/utilities/prerequisites.sh" 12 | 13 | init_application "${0}" "Configures sccache." container-only 14 | 15 | verify_executables_exist curl 16 | 17 | argsparse_allow_no_argument true 18 | argsparse_use_option =token: "Use a specific sccache token. If none provided it will require Igalia credentials." default: 19 | argsparse_use_option =url: "Use a specific sccache server URL." default:https://sccache.igalia.com 20 | argsparse_use_option =force "Overwrite any previous configuration." 21 | 22 | process_command_line_arguments() { 23 | 24 | argsparse_parse_options "${@}" 25 | } 26 | 27 | __requested_token= 28 | 29 | request_token() { 30 | 31 | local username 32 | local password 33 | local response 34 | 35 | # This is a bit hacky, but we don't want to store any credentials and only 36 | # Igalians should have access to this information. We can scrape the token 37 | # from our mailinglist archive. 38 | 39 | _log_ "" 40 | _log_ "Logging in using Igalia credentials to get the sccache token." 41 | read -rp 'Username: ' username 42 | read -rsp 'Password: ' password 43 | _log_ "" 44 | 45 | response=$(curl --silent --fail --user "${username}:${password}" https://archive.igalia.com/mail/team-webkit/2023/01/msg00005.html) 46 | 47 | if [ -z "${response}" ]; then 48 | _log_ "Failed to authenticate." 49 | exit 1 50 | fi 51 | 52 | local regex='The new token is: (\S+)' 53 | if [[ ! "$response" =~ $regex ]]; then 54 | _log_ "Failed to retrieve token." 55 | exit 1 56 | fi 57 | 58 | __requested_token="${BASH_REMATCH[1]}" 59 | } 60 | 61 | print_how_to_use() { 62 | _log_ "" 63 | _log_ "In order to use sccache, you need to set the following environment variables:" 64 | _log_ "" 65 | _log_ " export WEBKIT_USE_SCCACHE=1" 66 | _log_ " export NUMBER_OF_PROCESSORS=45 # How many jobs to queue at once." 67 | _log_ "" 68 | } 69 | 70 | run() { 71 | 72 | process_command_line_arguments "${@}" 73 | local token=${program_options["token"]} 74 | local url=${program_options["url"]} 75 | 76 | local sccache_config="${XDG_CONFIG_HOME:-${HOME}/.config}/sccache/config" 77 | 78 | if [ -f "${sccache_config}" ] && ! argsparse_is_option_set "force"; then 79 | _log_ "" 80 | _log_ "Already configured, you can overwrite with --force. (${sccache_config})" 81 | print_how_to_use 82 | exit 1 83 | fi 84 | 85 | if [ -z "${token}" ]; then 86 | request_token 87 | token="${__requested_token}" 88 | fi 89 | 90 | mkdir -p "$(dirname "${sccache_config}")" 91 | 92 | _log_ "" 93 | _log_ "Creating ${sccache_config}..." 94 | cat <"${sccache_config}" 95 | [dist] 96 | scheduler_url = "${url}" 97 | toolchains = [] 98 | 99 | [dist.auth] 100 | type = "token" 101 | token = "${token}" 102 | EOF 103 | 104 | print_how_to_use 105 | } 106 | 107 | run "${@}" 108 | -------------------------------------------------------------------------------- /scripts/container-only/wkdev-setup-vscode: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2024 Igalia S.L. 3 | # SPDX-License: MIT 4 | 5 | if [ -f "${WKDEV_SDK}/.wkdev-sdk-root" ]; then 6 | source "${WKDEV_SDK}/utilities/application.sh" 7 | else 8 | echo "Please set \${WKDEV_SDK} to point to the root of the wkdev-sdk checkout." 9 | exit 1 10 | fi 11 | source "${WKDEV_SDK}/utilities/prerequisites.sh" 12 | 13 | init_application "${0}" "Configures Visual Studio Code." container-only 14 | 15 | verify_executables_exist curl 16 | 17 | argsparse_allow_no_argument true 18 | argsparse_use_option "=yes" "Assume yes for all prompts." 19 | argsparse_use_option "no-extensions" "Don't install extensions." 20 | 21 | install_vscode() { 22 | 23 | _log_ "" 24 | _log_ "Installing Visual Studio Code..." 25 | _log_ "" 26 | 27 | if which code > /dev/null; then 28 | _log_ "Visual Studio Code is already installed." 29 | return 30 | fi 31 | 32 | local download_url='https://code.visualstudio.com/sha/download?build=stable&os=linux-deb-x64' 33 | 34 | if [ "$(uname -m)" = 'aarch64' ]; then 35 | download_url='https://code.visualstudio.com/sha/download?build=stable&os=linux-deb-arm64' 36 | fi 37 | 38 | if ! curl --silent --fail --location "${download_url}" -o /tmp/code.deb; then 39 | _log_ "Failed to download Visual Studio Code." 40 | exit 1 41 | fi 42 | 43 | if ! sudo apt install /tmp/code.deb; then 44 | _log_ "Failed to install Visual Studio Code." 45 | rm /tmp/code.deb 46 | exit 1 47 | fi 48 | 49 | rm /tmp/code.deb 50 | _log_ "" 51 | _log_ "Visual Studio Code has been installed." 52 | } 53 | 54 | install_extension() { 55 | 56 | local extension_name="${1}" 57 | local description="${2}" 58 | local ask="${3:-false}" 59 | local response 60 | local installed_extensions 61 | 62 | readarray installed_extensions < <(code --list-extensions) 63 | 64 | if [[ "${installed_extensions[*]}" =~ "${extension_name}" ]]; then 65 | _log_ "VSCode extension already installed: ${extension_name}" 66 | return 67 | fi 68 | 69 | if [ "${ask}" = true ] && ! argsparse_is_option_set "yes"; then 70 | read -r -p "Install VSCode extension: ${extension_name} (${description})? [Y/n] " response 71 | if [[ "$response" =~ ^([nN][oO]|[nN])$ ]]; then 72 | return 73 | fi 74 | else 75 | _log_ "Installing VSCode extension: ${extension_name} (${description})..." 76 | fi 77 | 78 | if ! code --install-extension "${extension_name}" &>/dev/null; then 79 | _log_ "Failed to install VSCode extension: ${extension_name}" 80 | exit 1 81 | fi 82 | 83 | _log_ "Installed" 84 | _log_ "" 85 | } 86 | 87 | install_extensions() { 88 | 89 | _log_ "" 90 | _log_ "Installing extensions..." 91 | _log_ "" 92 | install_extension ms-vscode.cmake-tools "CMake support" 93 | install_extension llvm-vs-code-extensions.vscode-clangd "C/C++ support" 94 | install_extension vadimcn.vscode-lldb "C/C++ debugging" true 95 | install_extension mads-hartmann.bash-ide-vscode "BASH support" true 96 | install_extension ms-python.python "Python support" true 97 | } 98 | 99 | run() { 100 | 101 | argsparse_parse_options "${@}" 102 | 103 | install_vscode 104 | 105 | if ! argsparse_is_option_set "no-extensions"; then 106 | install_extensions 107 | fi 108 | } 109 | 110 | run "${@}" 111 | -------------------------------------------------------------------------------- /utilities/podman.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | [ -z "${application_ready}" ] && { echo "[FATAL] You need to source 'utilities/application.sh' before sourcing this script."; return 1; } 4 | source "${WKDEV_SDK}/utilities/prerequisites.sh" 5 | 6 | podman_executable="/usr/bin/podman" 7 | if is_running_in_wkdev_sdk_container; then 8 | # Requires the presence of /usr/bin/podman-host in the container image. 9 | # It acts as portal to access the host podman instance. 10 | podman_executable="/usr/bin/podman-host" 11 | fi 12 | 13 | # systemctl to check the presence of a 'podman.socket' user service 14 | # podman, to control, well, podman. :-) 15 | verify_executables_exist systemctl 16 | 17 | verify_podman_is_acceptable "${podman_executable}" 18 | 19 | # Uses host podman no matter if executed within container or on host. 20 | run_podman() { run_command "${podman_executable}" "${@}"; } 21 | run_podman_silent() { run_command_silent "${podman_executable}" "${@}"; } 22 | run_podman_silent_unless_verbose() { run_command_silent_unless_verbose "${podman_executable}" "${@}"; } 23 | run_podman_silent_unless_verbose_or_abort() { run_command_silent_unless_verbose_or_abort "${podman_executable}" "${@}"; } 24 | 25 | run_podman_in_background_and_log_to_file() { 26 | 27 | local log_file="${1}" 28 | local command="${2}" 29 | shift 2 30 | 31 | run_podman "${command}" "${@}" &> "${log_file}" & 32 | } 33 | 34 | # Queries the container status - stores result in global 'last_container_status' variable. 35 | get_podman_container_status() { 36 | 37 | local container_name="${1}" 38 | run_podman inspect --type container --format "{{.State.Status}}" "${container_name}" 2>/dev/null 39 | } 40 | 41 | # Get the parameters passed to wkdev-init from an existing container. 42 | get_podman_container_init_arguments() { 43 | 44 | local container_name="${1}" 45 | run_podman inspect --type container --format "{{range .Args}}{{.}} {{end}}" "${container_name}" 2>/dev/null 46 | } 47 | 48 | # Get the location where the home directory for a container is stored on the host. 49 | get_podman_container_home_directory_on_host() { 50 | 51 | local container_name="${1}" 52 | local extract_variable="HOST_CONTAINER_HOME_PATH" 53 | set -o pipefail 54 | run_podman inspect --type container "${container_name}" 2>/dev/null | grep "${extract_variable}" | sed -e "s/.*${extract_variable}=//" | sed -e s'/",//' | head --lines 1 55 | local podman_status=${?} 56 | set +o pipefail 57 | return ${podman_status} 58 | } 59 | 60 | # Get currently used image name given an container name. 61 | get_image_name_by_container_name() { 62 | 63 | local container_name="${1}" 64 | run_podman inspect --type container --format "{{.ImageName}}" "${container_name}" 2>/dev/null 65 | } 66 | 67 | # Get currently used image ID given an container name. 68 | get_image_id_by_container_name() { 69 | 70 | local container_name="${1}" 71 | run_podman inspect --type container --format "{{.Image}}" "${container_name}" 2>/dev/null 72 | } 73 | 74 | # Get most recent known image ID given an image name. 75 | get_image_id_by_image_name() { 76 | 77 | local image_name="${1}" 78 | run_podman inspect --type image --format "{{.Id}}" "${image_name}" 2>/dev/null 79 | } 80 | 81 | # Get list of all containers by name. 82 | get_list_of_containers() { run_podman container list --all --format "{{.Names}}"; } 83 | 84 | # Does the podman user socket exist? 85 | is_podman_user_socket_available() { 86 | 87 | local podman_socket="${XDG_RUNTIME_DIR-}/podman/podman.sock" 88 | 89 | # The socket has to exist... 90 | [ -S "${podman_socket}" ] || return 1 91 | 92 | # ... and it should be controlled by the systemd user session. 93 | systemctl status --user podman.socket &>/dev/null 94 | return ${?} 95 | } 96 | -------------------------------------------------------------------------------- /scripts/container-only/wkdev-sdk-show-welcome-message: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2024 Igalia S.L. 3 | # SPDX-License: MIT 4 | 5 | [ -f "${WKDEV_SDK}/.wkdev-sdk-root" ] && source "${WKDEV_SDK}/utilities/application.sh" || { echo "Please set \${WKDEV_SDK} to point to the root of the wkdev-sdk checkout."; exit 1; } 6 | init_application "${0}" "" container-only 7 | 8 | argsparse_use_option trace "Enable 'xtrace' mode for this script" 9 | 10 | # Source utility script fragments 11 | source "${WKDEV_SDK}/utilities/ansi-code-generator/ansi.sh" 12 | 13 | argsparse_usage_description="$(cat <> 15 | 16 | Shows the welcome message upon container login. 17 | 18 | << Examples >> 19 | 20 | $ ${application_name} 21 | EOF 22 | )" 23 | 24 | process_command_line_arguments() { 25 | 26 | # Allow empty command line 27 | argsparse_allow_no_argument yes 28 | 29 | argsparse_parse_options "${@}" 30 | argsparse_is_option_set "trace" && set -o xtrace 31 | } 32 | 33 | # Runs a single test. 34 | run_test() { 35 | 36 | local description="${1}" 37 | local command="${2}" 38 | shift 2 39 | 40 | 41 | _log_ "" 42 | _log_ "-> ${description}:" 43 | "${command}" "${@}" 44 | } 45 | 46 | # Main functionality 47 | run() { 48 | 49 | process_command_line_arguments "${@}" 50 | 51 | pushd "${WKDEV_SDK}" &>/dev/null 52 | 53 | _log_ "" 54 | _log_ ' _ ______ _______ _______ ______ _ ' 55 | _log_ '|\ /|| \ /\( __ \ ( ____ \|\ /| ( ____ \( __ \ | \ /\' 56 | _log_ '| ) ( || \ / /| ( \ )| ( \/| ) ( | | ( \/| ( \ )| \ / /' 57 | _log_ '| | _ | || (_/ / | | ) || (__ | | | | _____ | (_____ | | ) || (_/ / ' 58 | _log_ '| |( )| || _ ( | | | || __) ( ( ) )(_____)(_____ )| | | || _ ( ' 59 | _log_ '| || || || ( \ \ | | ) || ( \ \_/ / ) || | ) || ( \ \ ' 60 | _log_ '| () () || / \ \| (__/ )| (____/\ \ / /\____) || (__/ )| / \ \' 61 | _log_ '(_______)|_/ \/(______/ (_______/ \_/ \_______)(______/ |_/ \/' 62 | _log_ "" 63 | _log_ "" 64 | _log_ "Git repository 'wkdev-sdk' ($(ansi::bold) $(git branch --show-current) @ $(git rev-parse HEAD) $(ansi::normal))" 65 | _log_ " 🠲 Last commit: $(ansi::bold)$(git log -1 --format=%cd)$(ansi::normal)" 66 | _log_ "" 67 | _log_ "NOTE:" 68 | _log_ " - Be sure to try $(ansi::bold)\`wkdev-test-host-integration\`$(ansi::normal) to verify your container setup behaves as expected." 69 | _log_ "" 70 | _log_ " - The home directory $(ansi::bold)\${HOME}$(ansi::normal)=${HOME} within the container is $(ansi::underline)$(ansi::bold)not the same$(ansi::normal)$(ansi::noUnderline) as your host home directory." 71 | _log_ " You can find your regular host home directory in the container under $(ansi::bold)\${HOST_HOME}$(ansi::normal)=${HOST_HOME}." 72 | _log_ "" 73 | _log_ " - Instructions on how to build / debug / profile WebKit can be found in the SDK documentation, either" 74 | _log_ " locally in $(ansi::underline)\${WKDEV_SDK}/docs$(ansi::noUnderline) or online $(ansi::underline)https://github.com/Igalia/wkdev-sdk/tree/main/docs$(ansi::noUnderline)". 75 | _log_ "" 76 | _log_ " - WebKit builds and tests will happen on a common directory at $(ansi::bold)/sdk/webkit$(ansi::normal) when using the SDK." 77 | _log_ " This allows relocating the build directory and executing tests anywhere without rebuilding," 78 | _log_ " and also sharing ccache/sccache artifacts across directories and between bots and developers." 79 | _log_ " To connect with the remote ccache service used by the bots, run $(ansi::bold)\`wkdev-setup-remote-ccache\`$(ansi::normal)." 80 | _log_ " To opt out of common-directory builds at /sdk/webkit, export the following environment variable" 81 | _log_ " in the container's \${HOME}/.bashrc or equivalent: WEBKIT_CONTAINER_SDK_INSIDE_MOUNT_NAMESPACE=1" 82 | 83 | popd &>/dev/null 84 | } 85 | 86 | run "${@}" 87 | -------------------------------------------------------------------------------- /scripts/container-only/.wkdev-sync-runtime-state: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2024 Igalia S.L. 3 | # SPDX-License: MIT 4 | 5 | source "${WKDEV_SDK}/utilities/application.sh" || { echo "Please set \${WKDEV_SDK} to point to the root of the wkdev-sdk checkout."; exit 1; } 6 | init_application "${0}" "" container-only with-quiet-support 7 | 8 | argsparse_allow_no_argument true 9 | argsparse_use_option trace "Enable 'xtrace' mode for this script" 10 | 11 | argsparse_usage_description="$(cat <> 13 | 14 | ${application_name} is ran on enter for every wkdev-sdk container. 15 | 16 | It takes information from your host environment and ensures that the container 17 | contains all host state such as the WAYLAND_DISPLAY in use. 18 | 19 | NOTE: You do NOT need to call this script manually, it is automatically used during container startup. 20 | EOF 21 | )" 22 | 23 | process_command_line_arguments() { 24 | 25 | argsparse_parse_options "${@}" 26 | argsparse_is_option_set "trace" && set -o xtrace 27 | } 28 | 29 | try_mount_host_wayland() { 30 | local host_wayland_socket="/host/run/${WAYLAND_DISPLAY}" 31 | local container_wayland_socket="${XDG_RUNTIME_DIR}/${WAYLAND_DISPLAY}" 32 | 33 | if [ -L "${container_wayland_socket}" ]; then 34 | # Silently succeed. 35 | return 36 | elif [ -S "${host_wayland_socket}" ]; then 37 | _log_ "" 38 | _log_ "-> Mounting host Wayland socket (${WAYLAND_DISPLAY})." 39 | ln -s "${host_wayland_socket}" "${container_wayland_socket}" 40 | else 41 | _log_ "" 42 | _log_ "-> Skipping host Wayland socket (${WAYLAND_DISPLAY}) - not found." 43 | fi 44 | 45 | if [[ ! -f "${container_wayland_socket}.lock" && -f "${host_wayland_socket}.lock" ]]; then 46 | ln -s "${host_wayland_socket}.lock" "${container_wayland_socket}.lock" 47 | fi 48 | } 49 | 50 | try_mount_host_pipewire() { 51 | 52 | local host_pipewired_socket="/host/run/${PIPEWIRE_REMOTE}" 53 | local container_pipewire_socket="${XDG_RUNTIME_DIR}/${PIPEWIRE_REMOTE}" 54 | 55 | if [ -L "${container_pipewire_socket}" ]; then 56 | # Silently succeed. 57 | return 58 | elif [ -S "${host_pipewired_socket}" ]; then 59 | _log_ "" 60 | _log_ "-> Mounting host Pipewire socket (${PIPEWIRE_REMOTE})." 61 | ln -s "${host_pipewired_socket}" "${container_pipewire_socket}" 62 | else 63 | _log_ "" 64 | _log_ "-> Skipping host Pipewire socket (${PIPEWIRE_REMOTE}) - not found." 65 | fi 66 | 67 | if [[ ! -f "${container_pipewire_socket}.lock" && -f "${host_pipewired_socket}.lock" ]]; then 68 | ln -s "${host_pipewired_socket}.lock" "${container_pipewire_socket}.lock" 69 | fi 70 | } 71 | 72 | mount_host_flatpak_instance_data() { 73 | 74 | # WebKit's sandbox writes to .flatpak for xdg-desktop-portal to read 75 | # our bwrapinfo.json file from to map PIDs. This works as our podman container 76 | # is not in its own pidns. 77 | 78 | # The others are just needed for flatpaks to function in general. 79 | 80 | for dir in '.flatpak' '.flatpak-helper' 'doc'; do 81 | local host_flatpak_instance_dir="/host/run/${dir}" 82 | local container_flatpak_instance_dir="${XDG_RUNTIME_DIR}/${dir}" 83 | 84 | if [ -d "${container_flatpak_instance_dir}" ]; then 85 | rm -r "${container_flatpak_instance_dir}" 86 | fi 87 | 88 | mkdir --mode=0700 --parents "${host_flatpak_instance_dir}" 89 | ln -s "${host_flatpak_instance_dir}" "${container_flatpak_instance_dir}" 90 | done 91 | } 92 | 93 | # Main functionality 94 | run() { 95 | 96 | process_command_line_arguments "${@}" 97 | 98 | WAYLAND_DISPLAY=${WAYLAND_DISPLAY-"wayland-0"} 99 | PIPEWIRE_REMOTE=${PIPEWIRE_REMOTE-"pipewire-0"} 100 | 101 | try_mount_host_wayland 102 | try_mount_host_pipewire 103 | mount_host_flatpak_instance_data 104 | 105 | _log_ "" 106 | } 107 | 108 | run "${@}" 109 | -------------------------------------------------------------------------------- /scripts/container-only/wkdev-setup-remote-ccache: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2025 Igalia S.L. 3 | # SPDX-License: MIT 4 | 5 | if [ -f "${WKDEV_SDK}/.wkdev-sdk-root" ]; then 6 | source "${WKDEV_SDK}/utilities/application.sh" 7 | else 8 | echo "Please set \${WKDEV_SDK} to point to the root of the wkdev-sdk checkout." 9 | exit 1 10 | fi 11 | source "${WKDEV_SDK}/utilities/prerequisites.sh" 12 | 13 | init_application "${0}" "Configures ccache with remote storage backend." container-only 14 | 15 | verify_executables_exist knock 16 | 17 | argsparse_allow_no_argument true 18 | argsparse_use_option =server: "Use a specific ccache server." default:wk-ccache-bazel.igalia.com 19 | argsparse_use_option =port: "Use a specific ccache port." default:25689 20 | 21 | process_command_line_arguments() { 22 | 23 | argsparse_parse_options "${@}" 24 | } 25 | 26 | setup_build_webkit_pre_script_env_var() { 27 | 28 | local ccache_knock_script="${1}" 29 | 30 | # Detect the user's shell 31 | local user_shell=$(basename "${SHELL}") 32 | 33 | local shell_config_file="" 34 | local export_line="export BUILD_WEBKIT_PRE_SCRIPT=\"${ccache_knock_script}\"" 35 | 36 | # Determine which shell configuration file to modify 37 | case "${user_shell}" in 38 | bash) 39 | shell_config_file="${HOME}/.bashrc" 40 | ;; 41 | zsh) 42 | shell_config_file="${HOME}/.zprofile" 43 | ;; 44 | fish) 45 | shell_config_file="${HOME}/.config/fish/config.fish" 46 | export_line="set -x BUILD_WEBKIT_PRE_SCRIPT \"${ccache_knock_script}\"" 47 | ;; 48 | *) 49 | _log_ "Warning: Unknown shell '${user_shell}', skipping BUILD_WEBKIT_PRE_SCRIPT setup." 50 | return 1 51 | ;; 52 | esac 53 | 54 | # Create the shell config file if it doesn't exist 55 | if [ ! -f "${shell_config_file}" ]; then 56 | mkdir -p "$(dirname "${shell_config_file}")" 57 | touch "${shell_config_file}" 58 | fi 59 | 60 | # Check if BUILD_WEBKIT_PRE_SCRIPT is already set in the file 61 | if grep -q "BUILD_WEBKIT_PRE_SCRIPT" "${shell_config_file}" &>/dev/null; then 62 | _log_ "BUILD_WEBKIT_PRE_SCRIPT already exists in ${shell_config_file}, leaving unchanged." 63 | return 0 64 | fi 65 | 66 | # Add the export line to the shell config file 67 | _log_ "Adding BUILD_WEBKIT_PRE_SCRIPT to ${shell_config_file}..." 68 | cat <>"${shell_config_file}" 69 | 70 | # ccache remote storage - knock script to open port 71 | ${export_line} 72 | EOF 73 | 74 | _log_ "Successfully configured BUILD_WEBKIT_PRE_SCRIPT in ${shell_config_file}" 75 | _log_ "NOTE: Be sure to re-login once or re-source ${shell_config_file}!" 76 | } 77 | 78 | run() { 79 | 80 | process_command_line_arguments "${@}" 81 | local server=${program_options["server"]} 82 | local port=${program_options["port"]} 83 | 84 | local ccache_config="${XDG_CONFIG_HOME:-${HOME}/.config}/ccache/ccache.conf" 85 | local ccache_knock_script="${XDG_CONFIG_HOME:-${HOME}/.config}/ccache/ccache-knock.sh" 86 | mkdir -p "$(dirname "${ccache_config}")" 87 | 88 | if grep -q "remote_storage" "${ccache_config}" &>/dev/null; then 89 | _log_ "Already configured ccache remote storage. (${ccache_config})" 90 | exit 1 91 | fi 92 | 93 | _log_ "Adding remote_storage section to ${ccache_config}..." 94 | cat <>"${ccache_config}" 95 | remote_storage = http://${server}:${port}|layout=bazel 96 | reshare = true 97 | EOF 98 | 99 | _log_ "Writing port knocking script to ${ccache_knock_script} (executed every time the container is entered)..." 100 | 101 | cat <>"${ccache_knock_script}" 102 | #!/usr/bin/env bash 103 | if ! curl -s "http://${server}:${port}/status" | grep -q MaxSize; then 104 | knock -d 500 "${server}" 1111 2222 3333 4444 5555 105 | fi 106 | EOF 107 | 108 | chmod +x "${ccache_knock_script}" 109 | 110 | _log_ "" 111 | _log_ "Setting up BUILD_WEBKIT_PRE_SCRIPT environment variable..." 112 | setup_build_webkit_pre_script_env_var "${ccache_knock_script}" 113 | 114 | _log_ "" 115 | _log_ "ccache config file content:" 116 | cat "${ccache_config}" 117 | 118 | _log_ "" 119 | _log_ "knock script file content:" 120 | cat "${ccache_knock_script}" 121 | 122 | _log_ "" 123 | _log_ "Testing access to remote ccache server (you should see non-empty JSON output below):" 124 | "${ccache_knock_script}" 125 | curl "http://${server}:${port}/status" 126 | } 127 | 128 | run "${@}" 129 | -------------------------------------------------------------------------------- /scripts/container-only/wkdev-test-host-integration: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2024 Igalia S.L. 3 | # SPDX-License: MIT 4 | 5 | [ -f "${WKDEV_SDK}/.wkdev-sdk-root" ] && source "${WKDEV_SDK}/utilities/application.sh" || { echo "Please set \${WKDEV_SDK} to point to the root of the wkdev-sdk checkout."; exit 1; } 6 | init_application "${0}" "Test host system integration" container-only 7 | 8 | argsparse_use_option trace "Enable 'xtrace' mode for this script" 9 | 10 | argsparse_usage_description="$(cat <> 12 | 13 | Verifies that the container <-> host system integration behaves as expected. 14 | 15 | This script exercises systemd / dbus intgration, X11/Wayland integration, desktop notifications, 16 | PulseAudio, dconf (via gtk4-demo) and runs glxgears, the glmark2 benchmark, and Epiphany. Each test 17 | denotes an expectation, whether the tested feature is supposed to work in the container or if 18 | access is forbidden. 19 | 20 | << Examples >> 21 | 22 | $ ${application_name} --trace 23 | EOF 24 | )" 25 | 26 | process_command_line_arguments() { 27 | 28 | # Allow empty command line 29 | argsparse_allow_no_argument yes 30 | 31 | argsparse_parse_options "${@}" 32 | argsparse_is_option_set "trace" && set -o xtrace 33 | } 34 | 35 | # Runs a single test. 36 | run_test() { 37 | 38 | local description="${1}" 39 | local command="${2}" 40 | shift 2 41 | 42 | read -p "Press any key to continue.." 43 | 44 | _log_ "" 45 | _log_ "-> ${description}:" 46 | "${command}" "${@}" 47 | } 48 | 49 | # Main functionality 50 | run() { 51 | 52 | process_command_line_arguments "${@}" 53 | 54 | _log_ "-> Install required packages:" 55 | sudo apt-get install --assume-yes epiphany-browser gtk-4-examples glmark2 glmark2-drm glmark2-wayland libnotify-bin mesa-utils x11-apps 56 | 57 | run_test "Ping allowed in rootless container: (expected to work)" \ 58 | ping -c 4 wpewebkit.org 59 | 60 | run_test "Access to system dbus: (expected to work)" \ 61 | busctl --no-pager list 62 | 63 | run_test "Access to session dbus: (expected to work)" \ 64 | busctl --no-pager --user list 65 | 66 | run_test "Access to systemd session: (expected to fail)" \ 67 | systemctl --no-pager status 68 | 69 | run_test "Accesus to systemd user session: (expected to work)" \ 70 | systemctl --no-pager --user status 71 | 72 | run_test "Access to system journal: (expected to work)" \ 73 | journalctl --no-pager --boot=0 --lines=10 74 | 75 | run_test "Access to user journal: (expected to work)" \ 76 | journalctl --no-pager --user --boot=0 --lines=10 77 | 78 | run_test "Test Bubblewrap: (should work if host supports unprivileged user namespaces)" \ 79 | bwrap --ro-bind / / --dev-bind /dev /dev --unshare-all echo 'Success' 80 | 81 | # TODO: We would need to setup a subordinate GID/UID mapping that is well embedded 82 | # in the constraints given bythe host subordinate GID/UID mapping. If we ever want 83 | # that, investigate. The current approach of steering the host podman from within 84 | # the container is reliable, and avoids the problem alltogether, at the expense 85 | # of opening another portal to the host system which exposes many host system 86 | # details that one usually wants to hide from a potentially malicious container. 87 | # In our case that for sure does not apply -- it is a developer SDK. 88 | run_test "Launching podman in podman container: (not expected to work on every host yet)" \ 89 | podman info 90 | 91 | run_test "Launching podman-host in podman container: (expected to work)" \ 92 | podman-host info 93 | 94 | message="Hello from wkdev-sdk!" 95 | run_test "Desktop notifications should appear: (expected to work)" \ 96 | notify-send "${message}" 97 | 98 | run_test "Run X11 application, xeyes: (should work if X11/Xwayland is activate on host)" \ 99 | xeyes 100 | 101 | run_test "Run Gtk/Wayland application, gtk4-demo: (should work if Wayland is activate on host)" \ 102 | gtk4-demo 103 | 104 | run_test "Run glxgears OpenGL application: (should work if it works on the host)" \ 105 | glxgears -info 106 | 107 | run_test "Run glmark2 benchmark: (should work if it works on the host)" \ 108 | glmark2 109 | 110 | run_test "Test PulseAudio: (should work if it works on the host)" \ 111 | pactl info 112 | 113 | # Our self-compiled GStreamer interferes with the system-provided one, avoid that for testing epiphany. 114 | unset GST_PLUGIN_PATH_1_0 115 | unset GST_PLUGIN_SCANNER 116 | unset LD_LIBRARY_PATH 117 | 118 | run_test "Test Epiphany browser: (try youtube.com, CSS 3D demos, WebGL, etc.)" \ 119 | epiphany 120 | } 121 | 122 | run "${@}" 123 | -------------------------------------------------------------------------------- /scripts/host-only/wkdev-setup-nvidia-gpu-for-container: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2024 Igalia S.L. 3 | # SPDX-License: MIT 4 | 5 | [ -f "${WKDEV_SDK}/.wkdev-sdk-root" ] && source "${WKDEV_SDK}/utilities/application.sh" || { _log_ "Please set \${WKDEV_SDK} to point to the root of the wkdev-sdk checkout."; exit 1; } 6 | init_application "${0}" "Install NVIDIA GPU Container Toolkit on host system" host-only 7 | 8 | # Source utility script fragments 9 | source "${WKDEV_SDK}/utilities/nvidia-gpu.sh" 10 | source "${WKDEV_SDK}/utilities/prerequisites.sh" 11 | 12 | argsparse_use_option trace "Enable 'xtrace' mode for this script" 13 | 14 | argsparse_usage_description="$(cat <> 16 | 17 | Installs the NVIDIA Container Toolkit on the host machine and sets up 18 | /etc/cdi/nvidia.yaml to expose NVIDIA GPUs to podman. 19 | 20 | << Examples >> 21 | 22 | $ ${application_name} 23 | EOF 24 | )" 25 | 26 | process_command_line_arguments() { 27 | 28 | # Allow empty command line 29 | argsparse_allow_no_argument yes 30 | 31 | argsparse_parse_options "${@}" 32 | argsparse_is_option_set "trace" && set -o xtrace 33 | } 34 | 35 | # Main functionality 36 | run() { 37 | 38 | process_command_line_arguments "${@}" 39 | 40 | if [ -f "$(get_nvidia_cdi_config_file)" ]; then 41 | _log_ "" 42 | _log_ "-> Configuration file '$(get_nvidia_cdi_config_file)' already exists, skipping creation." 43 | _log_ " NOTE: If your system GPU configuration changed, remove the file and re-run '${application_name}'." 44 | exit 0 45 | fi 46 | 47 | distribution=$(. /etc/os-release; echo $ID; ) 48 | if [ "${distribution}" = "ubuntu" ]; then 49 | if [ ! -f "$(get_nvidia_gpg_key_file)" ]; then 50 | _log_ "-> Download & deploy NVIDIA GPG key to '$(get_nvidia_gpg_key_file)'..." 51 | 52 | set -o pipefail 53 | curl --silent --location "$(get_nvidia_repository_url)/gpgkey" | gpg --dearmor | sudo tee "$(get_nvidia_gpg_key_file)" &>/dev/null 54 | [ ${?} -eq 0 ] || _abort_ "Adding new GPG key failed" 55 | set +o pipefail 56 | else 57 | _log_ "-> No need to download & deploy NVIDIA GPG key, already present in '$(get_nvidia_gpg_key_file)'." 58 | fi 59 | 60 | if [ ! -f "$(get_nvidia_apt_source_file)" ]; then 61 | _log_ "" 62 | _log_ "-> Add '$(get_nvidia_repository_url)' to APT source file '$(get_nvidia_apt_source_file)'..." 63 | 64 | set -o pipefail 65 | curl --silent --location "$(get_nvidia_repository_url)/$(get_nvidia_ubuntu_distribution)/$(get_nvidia_repository_name).list" | sudo tee "$(get_nvidia_apt_source_file)" &>/dev/null 66 | [ ${?} -eq 0 ] || _abort_ "Adding new repository failed" 67 | set +o pipefail 68 | else 69 | _log_ "-> No need to create new APT source file, already present in '$(get_nvidia_apt_source_file)'." 70 | fi 71 | 72 | _log_ "" 73 | _log_ "-> Updating APT repositories..." 74 | sudo apt-get update || _abort_ "'apt-get update' failed" 75 | 76 | _log_ "" 77 | _log_ "-> Installing '$(get_nvidia_host_package)'..." 78 | sudo apt-get install --assume-yes "$(get_nvidia_host_package)" || _abort_ "'apt-get install' failed" 79 | elif [ "${distribution}" = "fedora" ]; then 80 | # local nvidia_distribution="$(. /etc/os-release; echo $ID$VERSION_ID;)" 81 | local nvidia_distribution="centos8" # e.g. fedora7 symlinks no longer work 82 | local yum_repo_file="/etc/yum.repos.d/$(get_nvidia_repository_name).repo" 83 | 84 | if [ ! -f "${yum_repo_file}" ]; then 85 | _log_ "" 86 | _log_ "-> Add '$(get_nvidia_repository_url)' to YUM repo file '${yum_repo_file}'..." 87 | 88 | set -o pipefail 89 | curl --silent --location "$(get_nvidia_repository_url)/${nvidia_distribution}/$(get_nvidia_repository_name).repo" | sudo tee "${yum_repo_file}" &>/dev/null 90 | [ ${?} -eq 0 ] || _abort_ "Adding new repository failed" 91 | set +o pipefail 92 | fi 93 | 94 | _log_ "" 95 | _log_ "-> Updating DNF cache..." 96 | sudo dnf clean expire-cache || _abort_ "'dnf clean expire-cache' failed" 97 | 98 | _log_ "" 99 | _log_ "-> Installing '$(get_nvidia_host_package)'..." 100 | sudo dnf install -y "$(get_nvidia_host_package)" || _abort_ "'dnf install' failed" 101 | fi 102 | 103 | if [ ! -f "$(get_nvidia_cdi_config_file)" ]; then 104 | _log_ "" 105 | _log_ "-> Generating CDI specification '$(get_nvidia_cdi_config_file)'..." 106 | verify_executable_exists nvidia-ctk 107 | sudo nvidia-ctk cdi generate --output "$(get_nvidia_cdi_config_file)" || _abort_ "CDI generation failed" 108 | sudo chmod 644 "$(get_nvidia_cdi_config_file)" 109 | fi 110 | 111 | _log_ "" 112 | _log_ "-> Finished!" 113 | } 114 | 115 | run "${@}" 116 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## WebKit Container SDK 2 | 3 | Welcome to the WebKit Container SDK, the all-in-one SDK for WebKit GTK/WPE port development. 4 | 5 | It provides a fully-equipped container image ready for WebKit development 6 | as well as scripts to run the image using `podman`. 7 | 8 | Once you entered the container, you can navigate to a WebKit checkout 9 | and compile using `./Tools/Scripts/build-webkit [--gtk|--wpe]`, as usual. 10 | 11 | There is extra documentation in the [**docs**](https://github.com/Igalia/webkit-container-sdk/tree/main/docs) 12 | subdirectory for further information the SDK provides but the guide below will cover common usage. 13 | 14 | ### Quickstart guide 15 | 16 | 1. Integrate the SDK with your shell environment. 17 | 18 | Add the following to your shell configuration file (e.g. `~/.bashrc`, `~/.zprofile`, ...) 19 | to ensure that the `${WKDEV_SDK}` environment variable points to the correct location 20 | of your `webkit-container-sdk` Git checkout. It also extends the `${PATH}` to make the `wkdev-*` scripts 21 | provided by this repository accessible without having to specify full paths in the shell. 22 | 23 | ```sh 24 | source /absolute/path/to/your/Git/checkout/of/webkit-container-sdk/register-sdk-on-host.sh 25 | ``` 26 | 27 | Launch a new shell, or `source` your shell configuration files, to verify `${WKDEV_SDK}` 28 | now works as intended -- pointing to your `webkit-container-sdk` checkout. 29 | 30 | 2. Create a new **wkdev** container for WebKit development 31 | 32 | Execute the following command on your host system: 33 | 34 | ```sh 35 | wkdev-create --create-home 36 | ``` 37 | 38 | This will create a container named **wkdev** by default or a custom one with `--name` 39 | and it will create a new home directory for the SDK in `${HOME}/wkdev-home` by default 40 | or a custom one with `--home`. 41 | 42 | Within the container, the `${HOME}` directory is not equal to the host `${HOME}` directory: 43 | `${HOME}/wkdev-home` (from host) is bind-mounted into the container as `/home/${USER}`. 44 | This avoids pollution of files in your host `${HOME}` directory and for convenience 45 | it's still exposed in the container, as `${HOST_HOME}`. 46 | 47 | NOTE: `wkdev-create` will auto-detect the whole environment: X11, Wayland, PulseAudio, etc. 48 | and eventually needs **root** permissions on the *host system* to perform first-time-run-only 49 | initializations (such as allowing GPU profiling, by modifying `root`-owned config files, etc.) 50 | 51 | 3. Enter the new **wkdev** container 52 | 53 | Execute the following command on your host system: 54 | 55 | ``` 56 | wkdev-enter --name wkdev 57 | ``` 58 | 59 | After a few seconds you enter the container shell. 60 | 61 | 4. Verify host system integration is working properly 62 | 63 | You may optionally run the test script in the container, which tests various workloads: 64 | 65 | ```sh 66 | wkdev-test-host-integration 67 | ``` 68 | 69 | 5. Compile WPE WebKit 70 | 71 | ```sh 72 | cd "${HOST_HOME}/path/to/your/WebKit/checkout" 73 | ./Tools/Scripts/build-webkit --wpe --release 74 | ``` 75 | 76 | To run tests / execute MiniBrowser, try; 77 | 78 | ```sh 79 | ./Tools/Scripts/run-webkit-tests --wpe --release fast/css # Full tests take a long time 80 | ./Tools/Scripts/run-minibrowser --wpe https://browserbench.org/MotionMark1.2/ 81 | ``` 82 | 83 | You should expect at least 2 GB of RAM usage per core during the build. 84 | If you have less RAM available, consider limiting the number of cores by setting the `NUMBER_OF_PROCESSORS` environment variable when executing `build-webkit`. 85 | 86 | 6. READY! 87 | 88 | 89 | ### Update guide 90 | 91 | You should check, once in a while, if there is a new upstream version of the `wkdev-sdk` image available. 92 | 93 | 1. Use the `wkdev-update` tool. 94 | 95 | Run `wkdev-update` and following the instructions to selectively update the base images of your local 96 | containers. under the hood it deleted the old containers and re-creates them using the new base image 97 | and your previous settings. **NOTE: Any changes to the container filesystem will VANISH.**. Modifications 98 | to e.g. `/etc` config files in the container, manually installed packages, etc. will disappear. Only the 99 | container home directory will stay untouched. 100 | 101 | 2. READY! 102 | 103 | 104 | ### Firstrun script 105 | 106 | Since you will be regularly re-creating containers there is support for automatically running a script 107 | after each container is created. You can do this by making a `.wkdev-firstrun` script in the directory 108 | you specify as your SDK home (`${HOME}/wkdev-home` by default). This script runs as your user but has 109 | permissions to use `sudo` for tasks such as installing packages. An example script: 110 | 111 | ```bash 112 | #!/usr/bin/env bash 113 | # This example is a bash script but it can be any executable file. 114 | 115 | # Install extra applications like your favorite editor. 116 | sudo apt-get install --yes micro 117 | 118 | # The hostname is set to the name of the container so you could 119 | # for example have a specific one where you always work on a library. 120 | if [[ "$(hostname -s)" == "wkdev-foo" ]]; then 121 | jhbuild --no-interact build glib 122 | fi 123 | ``` 124 | -------------------------------------------------------------------------------- /scripts/container-only/wkdev-setup-cluster-administration-tools: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2025 Igalia S.L. 3 | # SPDX-License: MIT 4 | 5 | if [ -f "${WKDEV_SDK}/.wkdev-sdk-root" ]; then 6 | source "${WKDEV_SDK}/utilities/application.sh" 7 | else 8 | echo "Please set \${WKDEV_SDK} to point to the root of the wkdev-sdk checkout." 9 | exit 1 10 | fi 11 | source "${WKDEV_SDK}/utilities/prerequisites.sh" 12 | 13 | init_application "${0}" "Install webkit-cluster-infrastructure related administration tools." container-only 14 | 15 | verify_executables_exist curl 16 | 17 | argsparse_allow_no_argument true 18 | 19 | install_opentofu() { 20 | 21 | _log_ "" 22 | _log_ "Installing OpenTofu..." 23 | _log_ "" 24 | 25 | if which tofu > /dev/null; then 26 | _log_ "OpenTofu already installed." 27 | return 28 | fi 29 | 30 | local download_url='https://get.opentofu.org/install-opentofu.sh' 31 | 32 | if ! curl --silent --fail --location "${download_url}" -o /tmp/install-opentofu.sh; then 33 | _log_ "Failed to download OpenTofu installation script." 34 | exit 1 35 | fi 36 | 37 | if ! chmod +x /tmp/install-opentofu.sh; then 38 | _log_ "Failed to make OpenTofu installation script executable." 39 | rm /tmp/install-opentofu.sh 40 | exit 1 41 | fi 42 | 43 | if ! /tmp/install-opentofu.sh --install-method deb; then 44 | _log_ "Failed to install OpenTofu." 45 | rm /tmp/install-opentofu.sh 46 | exit 1 47 | fi 48 | 49 | rm /tmp/install-opentofu.sh 50 | _log_ "" 51 | _log_ "OpenTofu has been installed." 52 | } 53 | 54 | install_helm() { 55 | 56 | _log_ "" 57 | _log_ "Installing Helm..." 58 | _log_ "" 59 | 60 | if which helm > /dev/null; then 61 | _log_ "Helm already installed." 62 | return 63 | fi 64 | 65 | local download_url='https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3' 66 | 67 | if ! curl --silent --fail --location "${download_url}" -o /tmp/install-helm.sh; then 68 | _log_ "Failed to download Helm installation script." 69 | exit 1 70 | fi 71 | 72 | if ! chmod +x /tmp/install-helm.sh; then 73 | _log_ "Failed to make Helm installation script executable." 74 | rm /tmp/install-helm.sh 75 | exit 1 76 | fi 77 | 78 | if ! /tmp/install-helm.sh; then 79 | _log_ "Failed to install Helm." 80 | rm /tmp/install-helm.sh 81 | exit 1 82 | fi 83 | 84 | rm /tmp/install-helm.sh 85 | _log_ "" 86 | _log_ "Helm has been installed." 87 | } 88 | 89 | install_packer() { 90 | 91 | _log_ "" 92 | _log_ "Installing Packer..." 93 | _log_ "" 94 | 95 | if which packer > /dev/null; then 96 | _log_ "Packer already installed." 97 | return 98 | fi 99 | 100 | local base_url='https://apt.releases.hashicorp.com' 101 | 102 | if ! curl --silent --fail --location "${base_url}"/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/hashicorp-archive-keyring.gpg > /dev/null; then 103 | _log_ "Failed to add gpg key for Packer." 104 | exit 1 105 | fi 106 | 107 | if ! echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/hashicorp-archive-keyring.gpg] "${base_url}" $(grep -oP '(?<=UBUNTU_CODENAME=).*' /etc/os-release || lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list; then 108 | _log_ "Failed to add apt repository for Packer." 109 | exit 1 110 | fi 111 | 112 | if ! sudo apt update; then 113 | _log_ "Failed to update package list using apt." 114 | exit 1 115 | fi 116 | 117 | if ! sudo apt install packer; then 118 | _log_ "Failed to install Packer." 119 | exit 1 120 | fi 121 | 122 | _log_ "" 123 | _log_ "Packer has been installed." 124 | } 125 | 126 | install_kubectl() { 127 | 128 | _log_ "" 129 | _log_ "Installing kubectl..." 130 | _log_ "" 131 | 132 | if which kubectl > /dev/null; then 133 | _log_ "kubectl already installed." 134 | return 135 | fi 136 | 137 | local base_url='https://pkgs.k8s.io/core:/stable:/v1.31/deb' 138 | 139 | if ! curl --silent --fail --location "${base_url}"/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg > /dev/null; then 140 | _log_ "Failed to add gpg key for kubectl." 141 | exit 1 142 | fi 143 | 144 | if ! echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] "${base_url}"/ /" | sudo tee /etc/apt/sources.list.d/kubernetes.list; then 145 | _log_ "Failed to add apt repository for kubectl." 146 | exit 1 147 | fi 148 | 149 | if ! sudo apt update; then 150 | _log_ "Failed to update package list using apt." 151 | exit 1 152 | fi 153 | 154 | if ! sudo apt install kubectl; then 155 | _log_ "Failed to install kubectl." 156 | exit 1 157 | fi 158 | 159 | _log_ "" 160 | _log_ "kubectl has been installed." 161 | } 162 | 163 | install_hcloud() { 164 | 165 | _log_ "" 166 | _log_ "Installing hcloud..." 167 | _log_ "" 168 | 169 | if which hcloud > /dev/null; then 170 | _log_ "hcloud already installed." 171 | return 172 | fi 173 | 174 | if ! sudo apt update; then 175 | _log_ "Failed to update package list using apt." 176 | exit 1 177 | fi 178 | 179 | if ! sudo apt install hcloud-cli; then 180 | _log_ "Failed to install hcloud." 181 | exit 1 182 | fi 183 | 184 | _log_ "" 185 | _log_ "hcloud has been installed." 186 | } 187 | 188 | run() { 189 | 190 | argsparse_parse_options "${@}" 191 | 192 | install_opentofu 193 | install_helm 194 | install_packer 195 | install_kubectl 196 | install_hcloud 197 | } 198 | 199 | run "${@}" 200 | -------------------------------------------------------------------------------- /docs/00-Introduction.md: -------------------------------------------------------------------------------- 1 | ## Introduction to ``wkdev SDK`` 2 | 3 | NOTE: This documents covers _using_ the SDK. Creating the images is covered in BUILDING.md. 4 | 5 | The ``wkdev SDK`` provides a hassle-free environment to perform WebKit Gtk/WPE development. 6 | It is distributed in form of an **OCI image**, a standardized container format that allows 7 | any OCI-compatible container system, such as **Docker** and **podman**, to run the SDK. 8 | The same image can also be used within the WebKit Early Warning System (EWS) to provide 9 | an environment in which tests can be executed in a reliable & reproducible way. 10 | 11 | By utilizing the ``wkdev SDK``, a vanilla Linux installation can be turned into a fully 12 | functional WebKit development / debugging environment within minutes. After the initial 13 | setup procedure, the ``wkdev SDK`` user (hereafter: the **developer**) can either directly 14 | run commands within the **wkdev** container or launch one or more interactive shell 15 | sessions, in which you can compile WebKit / run tests / etc. 16 | 17 | To run CLI applications within a container, requires no effort: it works out of the box. 18 | Running graphical applications, that utilize e.g. [Wayland](https://wayland.freedesktop.org) 19 | for screen presentation, need [D-Bus](https://freedesktop.org/wiki/Software/dbus) to communicate 20 | with other system components, or use [SystemD](https://freedesktop.org/wiki/Software/systemd) 21 | APIs to query network / power / etc. information, require a substantial amount of configuration 22 | to allow the containerized GUI application to integrate seamlessly within the host desktop 23 | environment. 24 | 25 | To overcome the tedious setup procedure, wrapper tools were created, such as [toolbx](https://containertoolbx.org) 26 | and [distrobox](https://distrobox.privatedns.org), that greatly simplify the setup procedure. 27 | 28 | **distrobox** and **toolbx** both allow you to run GUI applications out of the box, the former supports 29 | both **Docker** and **podman** as backends, where **toolbx** is tied to **podman** only. Both also support 30 | to share the current host user and its **\$HOME directory** with the container, replacing any other 31 | \$HOME directory that might reside in the OCI container image. **toolbx** only supports that operation mode, 32 | whereas **distrobox** allows for fine-grained control about what files/directories to share with the container. 33 | 34 | However for our specific purposes, we used our own set of wrapper scripts such as `wkdev-create`, 35 | `wkdev-enter`, inspired by **distrobox**, to make it it easy as possible to get stated with WebKit 36 | development. 37 | 38 | ### Setup procedure 39 | 40 | On your **host system** ensure that **podman** is installed. 41 | 42 | * [podman](https://podman.io) 43 | * Fedora: [podman](https://packages.fedoraproject.org/pkgs/podman/podman) 44 | * Debian (sid): [podman](https://packages.debian.org/sid/podman) 45 | * Ubuntu (starting from 23.04): [podman](https://packages.ubuntu.com/lunar/podman) 46 | * macOS: [podman](https://formulae.brew.sh/formula/podman) 47 | 48 | That's all you need to install on your host system. Now it's the time to get a fresh WebKit source 49 | checkout, or update/clean an existing one. 50 | 51 | ```sh 52 | $ cd ~/path/to/home/subdirectory/with/git/checkout/of/ 53 | $ git clone https://github.com/WebKit/WebKit.git 54 | ``` 55 | 56 | That can take several hours, depending on your internet connection. 57 | Let it run, and move on to the **Quickstart guide**. 58 | 59 | ### Quickstart guide 60 | 61 | 1. Integrate `wkdev-sdk` with your shell environment. 62 | 63 | Add the following to your shell configuration file (e.g. `~/.bashrc`, `~/.zprofile`, ...) 64 | to ensure that the `${WKDEV_SDK}` environment variable points to the correct location 65 | of your `wkdev-sdk` Git checkout. It also extends the `${PATH}` to make the `wkdev-*` scripts 66 | provided by this repository accessible without having to specify full paths in the shell. 67 | 68 | ```sh 69 | source /absolute/path/to/your/Git/checkout/of/wkdev-sdk/register-sdk-on-host.sh 70 | ``` 71 | 72 | Launch a new shell, or `source` your shell configuration files, to verify `${WKDEV_SDK}` 73 | now works as intended -- pointing to your `webkit-container-sdk` checkout. 74 | 75 | 2. Create a new **wkdev** container for WebKit development 76 | 77 | Execute the following command on your host system: 78 | 79 | ```sh 80 | wkdev-create --name wkdev --create-home --home "${HOME}/wkdev-home" 81 | ``` 82 | 83 | This will create a container named **wkdev**. 84 | 85 | Within the container, the `${HOME}` directory is not equal to the host `${HOME}` directory: 86 | `${HOME}/wkdev-home` (from host) is bind-mounted into the container as `/home/${USER}`. 87 | This avoids pollution of files in your host `${HOME}` directory and for convenience 88 | it's still exposed in the container, as `${HOST_HOME}`. 89 | 90 | The `name` and `home` values above are the defaults so you can omit them in regular usage. 91 | 92 | NOTE: `wkdev-create` will auto-detect the whole environment: X11, Wayland, PulseAudio, etc. 93 | and eventually needs **root** permissions on the *host system* to perform first-time-run-only 94 | initializations (such as allowing GPU profiling, by modifying `root`-owned config files, etc.) 95 | 96 | 3. Enter the new **wkdev** container 97 | 98 | Execute the following command on your host system: 99 | 100 | ``` 101 | wkdev-enter --name wkdev 102 | ``` 103 | 104 | After a few seconds you enter the container shell. 105 | 106 | 4. Verify host system integration is working properly 107 | 108 | You may optionally run the test script in the container, which tests various workloads: 109 | 110 | ```sh 111 | wkdev-test-host-integration 112 | ``` 113 | 114 | 5. Compile WPE WebKit 115 | 116 | ```sh 117 | cd "${HOST_HOME}/path/to/your/WebKit/checkout" 118 | ./Tools/Scripts/build-webkit --wpe --release 119 | ``` 120 | 121 | To run tests / execute MiniBrowser, try; 122 | 123 | ```sh 124 | ./Tools/Scripts/run-webkit-tests --wpe --release fast/css # Full tests take a long time 125 | ./Tools/Scripts/run-minibrowser --wpe https://browserbench.org/MotionMark1.2/ 126 | ``` 127 | 128 | 6. READY! 129 | -------------------------------------------------------------------------------- /scripts/host-only/wkdev-sdk-bakery: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2024 Igalia S.L. 3 | # SPDX-License: MIT 4 | 5 | [ -f "${WKDEV_SDK}/.wkdev-sdk-root" ] && source "${WKDEV_SDK}/utilities/application.sh" || { echo "Please set \${WKDEV_SDK} to point to the root of the wkdev-sdk checkout."; exit 1; } 6 | init_application "${0}" "Build one of the container images that form the SDK" host-only 7 | 8 | # Source utility script fragments 9 | source "${WKDEV_SDK}/utilities/podman.sh" 10 | source "${WKDEV_SDK}/utilities/resources.sh" 11 | source "${WKDEV_SDK}/utilities/timer.sh" 12 | 13 | # nproc is used to determine the number of CPU cores 14 | verify_executables_exist nproc 15 | 16 | argsparse_use_option debug "Enable debug logging for podman (useful to debug container issues)" 17 | argsparse_use_option trace "Enable 'xtrace' mode for this script" 18 | argsparse_use_option =verbose "Increase verbosity of this script" 19 | 20 | argsparse_use_option =name: "Name of container image" mandatory default:wkdev-sdk 21 | argsparse_use_option =env: "Environment variable as string array, e.g. -e KEY=value,FOO=bar" cumulative 22 | argsparse_use_option =mode: "Operation mode: 'build', 'deploy', or 'export'" mandatory 23 | argsparse_use_option idle-cores: "Number of CPU cores to leave idle, when building the image" type:uint default:2 24 | argsparse_use_option =tag: "Tag to use for created image." default:$(get_default_container_tag) 25 | argsparse_use_option =arch: "Container architecture. When building images, we also append this arch to the tag name." 26 | argsparse_use_option multiarch "Assemble all available images of the form tag_ARCH into one multiarch image." 27 | 28 | argsparse_usage_description="$(cat <> 30 | 31 | Builds one of the container images that form the SDK. 32 | 33 | << Examples >> 34 | 35 | $ ${application_name} --name wkdev-sdk --mode build 36 | $ ${application_name} --name wkdev-sdk --mode deploy 37 | EOF 38 | )" 39 | 40 | process_command_line_arguments() { 41 | 42 | argsparse_parse_options "${@}" 43 | argsparse_is_option_set "trace" && set -o xtrace 44 | 45 | container_image_name="${program_options["name"]}" 46 | container_tag="${program_options["tag"]}" 47 | idle_cores=${program_options["idle-cores"]} 48 | 49 | operation_mode="${program_options["mode"]}" 50 | [ "${operation_mode}" != 'build' ] && [ "${operation_mode}" != 'deploy' ] && [ "${operation_mode}" != 'export' ] && _abort_ "Unknown operation mode: '${operation_mode}' (valid choices are 'build', 'deploy', or 'export')" 51 | } 52 | 53 | get_tag_for_build() { echo "$(get_qualified_name "${container_image_name}"):${container_tag}"; } 54 | get_number_of_cores_for_build() { nproc --ignore=${idle_cores}; } 55 | 56 | build_image() { 57 | 58 | tag="$(get_tag_for_build)" 59 | 60 | _log_ "" 61 | _log_ "-> Building container image '${container_image_name}' using tag '${tag}'..." 62 | _log_ "" 63 | 64 | pushd "$(get_image_directory_by_name "${container_image_name}")" &>/dev/null 65 | timer_start 66 | 67 | local podman_argument=("--jobs" "$(get_number_of_cores_for_build)" "--build-context" "scripts=${WKDEV_SDK}/scripts") 68 | 69 | if argsparse_is_option_set "arch"; then 70 | container_arch="${program_options["arch"]}" 71 | echo "Overriding container architecture: ${container_arch}" 72 | podman_argument+=("--arch=${container_arch}") 73 | 74 | tag="${tag}_${container_arch}" 75 | fi 76 | podman_argument+=("--tag" "${tag}") 77 | 78 | for environment_variable in "${cumulated_values_env[@]}" 79 | do 80 | podman_argument+=("--env" "${environment_variable}") 81 | done 82 | 83 | run_podman_silent_unless_verbose build ${podman_argument[@]} . || _abort_ "Container image build failed" 84 | timer_stop 85 | popd &>/dev/null 86 | } 87 | 88 | deploy_image() { 89 | 90 | _log_ "" 91 | _log_ "-> Deploying container image '${container_image_name}' to registry '$(get_default_container_registry)'..." 92 | _log_ "" 93 | 94 | local image_directory="$(get_image_directory_by_name "${container_image_name}")" 95 | pushd "${image_directory}" &>/dev/null || _abort_ "Switching to directory '${image_directory}' failed" 96 | 97 | if argsparse_is_option_set "multiarch"; then 98 | image=$(get_qualified_name "${container_image_name}") 99 | target_tag="${container_tag}" 100 | image_qualified="$(get_tag_for_build)" 101 | echo "Building multiarch image for ${image_qualified}" 102 | input_tags="$(podman image list "${image}" --format "{{.Tag}}" | grep "${target_tag}_")" 103 | input_tags=${input_tags//'\n'/ } 104 | echo "Creating manifest for ${image_qualified}" 105 | run_podman_silent_unless_verbose manifest create "${image_qualified}" || _abort_ "Creating manifest failed" 106 | for input_tag in $input_tags 107 | do 108 | echo "Adding ${image}:${input_tag} to ${image_qualified}" 109 | run_podman_silent_unless_verbose manifest add "${image_qualified}" "containers-storage:${image}:${input_tag}" || _abort_ "Adding to manifest failed" 110 | done 111 | run_podman_silent_unless_verbose manifest push --all "$(get_tag_for_build)" "docker://$(get_tag_for_build)" || _abort_ "Pushing to registry failed" 112 | else 113 | run_podman_silent_unless_verbose push "$(get_tag_for_build)" || _abort_ "Pushing to registry failed" 114 | fi 115 | 116 | popd &>/dev/null 117 | } 118 | 119 | export_image() { 120 | 121 | _log_ "" 122 | _log_ "-> Exporting container image '${container_image_name}' to file ${container_image_name}.tar..." 123 | _log_ "" 124 | 125 | if argsparse_is_option_set "arch"; then 126 | output_file="${container_image_name}-${program_options["arch"]}.tar" 127 | tag="$(get_tag_for_build)_${program_options["arch"]}" 128 | else 129 | output_file="${container_image_name}.tar" 130 | tag="$(get_tag_for_build)" 131 | fi 132 | 133 | run_podman_silent_unless_verbose save --format=oci-archive -o ${output_file} ${tag} 134 | } 135 | 136 | # Main functionality 137 | run() { 138 | 139 | process_command_line_arguments "${@}" 140 | 141 | [ "${operation_mode}" = "build" ] && build_image 142 | [ "${operation_mode}" = "deploy" ] && deploy_image 143 | [ "${operation_mode}" = "export" ] && export_image 144 | 145 | _log_ "" 146 | _log_ "-> Finished!" 147 | } 148 | 149 | run "${@}" 150 | -------------------------------------------------------------------------------- /scripts/host-only/wkdev-update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2024 Igalia S.L. 3 | # SPDX-License: MIT 4 | 5 | [ -f "${WKDEV_SDK}/.wkdev-sdk-root" ] && source "${WKDEV_SDK}/utilities/application.sh" || { echo "Please set \${WKDEV_SDK} to point to the root of the wkdev-sdk checkout."; exit 1; } 6 | init_application "${0}" "Updates the wkdev-sdk Git repository and containers." host-only 7 | 8 | # Source utility script fragments 9 | source "${WKDEV_SDK}/utilities/ansi-code-generator/ansi.sh" 10 | source "${WKDEV_SDK}/utilities/podman.sh" 11 | 12 | argsparse_use_option debug "Enable debug logging for podman (useful to debug container issues)" 13 | argsparse_use_option trace "Enable 'xtrace' mode for this script" 14 | 15 | argsparse_usage_description="$(cat <> 17 | 18 | Updates the Git repository that holds the 'wkdev-sdk' and assists in re-creating containers, if the base image changes. 19 | EOF 20 | )" 21 | 22 | process_command_line_arguments() { 23 | 24 | # Allow empty command line 25 | argsparse_allow_no_argument yes 26 | 27 | argsparse_parse_options "${@}" 28 | argsparse_is_option_set "trace" && set -o xtrace 29 | } 30 | 31 | try_update_sdk_repository() { 32 | 33 | _log_ "" 34 | 35 | if [ "$(git rev-parse --abbrev-ref HEAD)" = "main" ] && [ -z "$(git status --untracked-files=no --porcelain)" ]; then 36 | _log_ "-> Updating Git repository 'wkdev-sdk' located in \${WKDEV_SDK}..." 37 | run_command git pull --rebase || _abort_ "Cannot update wkdev-sdk repository" 38 | else 39 | _log_ "-> Not updating Git repository 'wkdev-sdk' located in \${WKDEV_SDK} - there are local modifications!" 40 | fi 41 | } 42 | 43 | ask_for_confirmation() { 44 | 45 | local prompt="${1}" 46 | 47 | while true; do 48 | local answer="" 49 | read -p "${prompt}" answer 50 | 51 | case "${answer}" in 52 | [Yy]* ) return 0;; 53 | [Nn]* ) return 1;; 54 | * ) _log_ "Please answer 'y'es or 'n'o.";; 55 | esac 56 | done 57 | } 58 | 59 | update_containers_using_image() { 60 | 61 | _log_ "" 62 | local image_name="${1}" 63 | 64 | local latest_image_id="$(get_image_id_by_image_name "${image_name}")" 65 | if [ -z "${latest_image_id}" ]; then 66 | _log_ "-> Skipping update of image "${image_name}" - no containers use this image." 67 | return 68 | fi 69 | 70 | local max_container_name_length=0 71 | local found_containers_using_image=0 72 | for container_name in $(get_list_of_containers); do 73 | local container_image_name_with_tag="$(get_image_name_by_container_name "${container_name}")" 74 | _log_ "${container_image_name_with_tag} = ${image_name}" 75 | [ "${container_image_name_with_tag}" = "${image_name}" ] || continue 76 | found_containers_using_image=$((found_containers_using_image+1)) 77 | [ ${#container_name} -gt ${max_container_name_length} ] && max_container_name_length=${#container_name} 78 | done 79 | 80 | if [ ${found_containers_using_image} -eq 0 ]; then 81 | _log_ "-> No containers use this image, no need to recreate any of them." 82 | return 83 | fi 84 | 85 | _log_ "-> Found ${found_containers_using_image} container(s) using the image '${image_name}', checking status:" 86 | 87 | local -A outdated_container_data=() 88 | for container_name in $(get_list_of_containers); do 89 | local container_image_name_with_tag="$(get_image_name_by_container_name "${container_name}")" 90 | [ "${container_image_name_with_tag}" = "${image_name}" ] || continue 91 | 92 | local existing_image_id="$(get_image_id_by_container_name "${container_name}")" 93 | [ -z "${existing_image_id}" ] && _abort_ "Cannot retrieve image id for container name '${container_name}'" 94 | 95 | local status="OK" 96 | local status_color="green" 97 | if [ "${existing_image_id}" != "${latest_image_id}" ]; then 98 | status="OUTDATED" 99 | status_color="red" 100 | 101 | local recreate_arguments=() 102 | recreate_arguments+=("--home" "$(get_podman_container_home_directory_on_host "${container_name}")") 103 | local init_arguments=($(get_podman_container_init_arguments "${container_name}")) 104 | for arg in "${init_arguments[@]}"; do 105 | # wkdev-create --attach passes the --exit-when-done option to .wkdev-init so that 106 | # podman start --attach doesn't hang forever. 107 | # --exit-when-done is not an option of wkdev-create however, so we need to skip it. 108 | if [[ "${arg}" != "--exit-when-done" ]]; then 109 | recreate_arguments+=("${arg}") 110 | fi 111 | done 112 | outdated_container_data["${container_name}"]=${recreate_arguments[@]} 113 | fi 114 | 115 | printf " Container: %-${max_container_name_length}s ⇾ " "${container_name}" 116 | printf "$(ansi::bold)$(ansi::${status_color})%-8s $(ansi::resetForeground)$(ansi::normal) (image id ${existing_image_id})\n" "${status}" 117 | done 118 | 119 | local outdated_container_names="${!outdated_container_data[@]}" 120 | if [ -z "${outdated_container_names}" ]; then 121 | _log_ "" 122 | _log_ "-> No containers need to be re-created, all use the latest image version." 123 | return 124 | fi 125 | 126 | _log_ "" 127 | _log_ "-> Trying to re-create containers, if desired..." 128 | _log_ " NOTE: Remember that all changes made to your local image are $(ansi::bold)gone$(ansi::normal) if you re-create the container." 129 | _log_ "" 130 | 131 | # Loop over outdated containers and re-create them, if desired. 132 | for key in ${outdated_container_names}; do 133 | local container_name="${key}" 134 | local recreate_arguments="${outdated_container_data["${key}"]}" 135 | ask_for_confirmation " -> Do you want to re-create the $(ansi::bold)'${container_name}'$(ansi::normal) container? [y/n] " || continue 136 | 137 | # TODO: Handle re-creation of home directories, if the default shell configuration files, change? 138 | # At least we have to warn if there is e.g. a new '.bashrc' file... 139 | _log_ "" 140 | _log_ " -> Stopping container..." 141 | run_podman_silent stop "${container_name}" 142 | 143 | _log_ " -> Deleting container..." 144 | run_podman_silent rm "${container_name}" 145 | 146 | _log_ " -> Re-creating container..." 147 | "${WKDEV_SDK}/scripts/host-only/wkdev-create" --verbose --name "${container_name}" ${recreate_arguments[@]} 148 | done 149 | } 150 | 151 | 152 | update_image() { 153 | 154 | local image_name="${1}" 155 | 156 | _log_ "" 157 | _log_ "-> Updating image '${image_name}'..." 158 | run_podman pull "${image_name}" || _abort_ "Cannot update image '${image_name}'" 159 | } 160 | 161 | # Main functionality 162 | run() { 163 | 164 | process_command_line_arguments "${@}" 165 | 166 | pushd "${WKDEV_SDK}" &>/dev/null 167 | try_update_sdk_repository 168 | popd &>/dev/null 169 | 170 | update_image "$(get_sdk_qualified_name):$(get_default_container_tag)" 171 | 172 | update_containers_using_image "$(get_sdk_qualified_name):$(get_default_container_tag)" 173 | 174 | _log_ "" 175 | _log_ "-> Finished." 176 | } 177 | 178 | run "${@}" 179 | -------------------------------------------------------------------------------- /utilities/application.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | [ ! -z "${application_ready}" ] && { echo "[FATAL] You are not allowed to source 'utilities/application.sh' more than once."; return 1; } 4 | 5 | # Exposed global variables. 6 | application_name="" 7 | application_directory="" 8 | application_description="" 9 | 10 | # This script fragment provides the following global variables after sourcing. 11 | # 12 | # 1. ${application_name} - Name of the application - equal to the filename of the script. 13 | # 2. ${application_directory} - Directory name where the application resides. 14 | # 3. ${application_description} - Description of the application - a few words about the purpose. 15 | # 16 | # The 'init_application' method serves as common entry point for all scripts in the wkdev-sdk. 17 | # Is is assumed that all scripts call 'init_application' in their preamble, before executing any logic. 18 | 19 | log_to_stdout_enabled=1 # Disabled by --quiet. 20 | quiet_args=() # Contains --quiet if --quiet is passed in argv, used 21 | # to propagate it to other scripts. 22 | app_argv=("${@}") # init_application requires argv to check for --quiet. 23 | 24 | enable_quiet_support() { 25 | # Register the option for the sake of --help. 26 | argsparse_use_option =quiet "Silence all WebKit container SDK messages (use this when you need \ 27 | to launch a program in the container from the host with clean output)" 28 | 29 | # We do the argument parsing ourselves instead of with argsparse 30 | # as we need to know whether we need to be quiet before printing the 31 | # application description message, before the application has set all 32 | # its own argsparse settings. 33 | for flag in "${app_argv[@]}"; do 34 | case "${flag}" in 35 | --quiet|-q) 36 | log_to_stdout_enabled=0 37 | quiet_args=(--quiet) 38 | ;; 39 | --) 40 | break 41 | ;; 42 | esac 43 | done 44 | } 45 | 46 | # Add log message to stdout. 47 | _log_() { 48 | 49 | local log_message="${1-}" 50 | if [ "${log_to_stdout_enabled}" -eq 1 ]; then 51 | echo "${log_message}" 52 | fi 53 | # Log to journald if available. This can help users troubleshoot issues when 54 | # using `wkdev-enter --quiet` inside scripts. 55 | logger -p local0.info -t "${application_name}" -- "${log_message}" || true 56 | } 57 | 58 | # Aborts the application/script. 59 | # Requires an error message as first parameter, and an optional exit code as second parameter. 60 | _abort_() { 61 | 62 | local error_message="${1-}" 63 | local exit_code="${2-}" 64 | if [ -z "${exit_code}" ]; then 65 | exit_code=1 66 | fi 67 | 68 | printf '\n[FATAL] %s - aborting with exit code %d.\n' \ 69 | "${error_message}" "${exit_code}" >&2 70 | exit "${exit_code}" 71 | } 72 | 73 | # Runs a command. 74 | run_command() { 75 | 76 | local command="${1}" 77 | shift 78 | 79 | "${command}" "${@}" 80 | } 81 | 82 | # Runs a command suppressing both stdout and stderr output. 83 | run_command_silent() { 84 | 85 | local command="${1}" 86 | shift 87 | 88 | "${command}" "${@}" &>/dev/null 89 | } 90 | 91 | # Runs a command suppressing both stdout and stderr output, 92 | # unless '--verbose' was passed as option to the script. 93 | run_command_silent_unless_verbose() { 94 | 95 | local command="${1}" 96 | shift 97 | 98 | if argsparse_is_option_set "verbose"; then 99 | echo "${command}" "${@}" 100 | "${command}" "${@}" 101 | else 102 | "${command}" "${@}" &>/dev/null 103 | fi 104 | } 105 | 106 | # Runs a command, exiting if the command return non-zero. 107 | # If the verbose option is enabled, stdout and stderr are connected to the screen. 108 | # If the verbose option is not enabled, stderr is captured and printed only 109 | # if the subprocess exits with failure. 110 | run_command_silent_unless_verbose_or_abort() { 111 | 112 | local command="${1}" 113 | shift 114 | 115 | local cmd_args=("${command}" "${@}") 116 | 117 | if argsparse_is_option_set "verbose"; then 118 | "${cmd_args[@]}" 119 | local code=$? 120 | else 121 | # We need to split the variable declaration from the variable 122 | # assignation, as otherwise $? will not reflect the exit code of the 123 | # subshell command: https://stackoverflow.com/a/2556122/1777162 124 | local captured_stderr 125 | captured_stderr="$("${cmd_args[@]}" 2>&1 >/dev/null)" 126 | local code=$? 127 | fi 128 | 129 | if [ "${code}" -ne 0 ]; then 130 | if ! argsparse_is_option_set "verbose"; then 131 | echo "${captured_stderr}" >&2 132 | fi 133 | _abort_ "Command failed with code ${code}: $(shjoin "${cmd_args[@]}")" 134 | fi 135 | } 136 | 137 | # Python's shlex.join(), but for Bash: receives any number of arguments 138 | # representing a shell command invocation and echoes in return one single string 139 | # that a user can paste in a terminal to run that command invocation. 140 | # 141 | # Bash provides a very similar mechanism with "${array[*]Q}", but it quotes 142 | # even when unnecessary, which is often undesirable for user output. 143 | shjoin() { 144 | 145 | local is_first=true 146 | for arg in "$@"; do 147 | if [[ "$is_first" == true ]]; then 148 | is_first=false 149 | else 150 | echo -n " " 151 | fi 152 | if [[ "'${arg}'" == "${arg@Q}" ]]; then 153 | printf "%s" "${arg}" 154 | else 155 | printf "%s" "${arg@Q}" 156 | fi 157 | done 158 | } 159 | 160 | init_application() { 161 | 162 | # Bash script recommendations, see https://www.davidpashley.com/articles/writing-robust-shell-scripts/. 163 | 164 | # All wkdev-sdk scripts... 165 | set +o errexit # ... do not auto-abort if any sub-command fails with a non-zero exit code. 166 | # (see http://mywiki.wooledge.org/BashFAQ/105 for pros/cons). 167 | 168 | set -o nounset # ... abort if any unset variable is read (very useful during development). 169 | # (see http://mywiki.wooledge.org/BashFAQ/112 for pros/cons). 170 | 171 | # set -o pipefail is only enabled where necessary. 172 | 173 | # The callee needs to pass its "${0}" value as first parameter to 'init_application' 174 | # and a short description of the application's purpose as second parameter. 175 | local application_path="${0}" 176 | application_name=$(basename "${application_path}") 177 | application_directory=$(cd "$(dirname "${application_path:-${PWD}}")" &>/dev/null && pwd) 178 | application_description="${2}" 179 | 180 | local application_constraints="${3-}" # Either "container-only", "host-only" or "host-and-container". 181 | if [ "${application_constraints}" = "container-only" ]; then 182 | # Prevent application to run on the host. 183 | is_running_in_wkdev_sdk_container || _abort_ "The script '${application_name}' is intended to run from within the wkdev-sdk container only" 184 | elif [ "${application_constraints}" = "host-only" ]; then 185 | # Prevent application to run within the container. 186 | is_running_in_wkdev_sdk_container && _abort_ "The script '${application_name}' is intended to run on the host only" 187 | elif [ "${application_constraints}" = "host-and-container" ]; then 188 | true # no-op 189 | else 190 | _abort_ "Unknown constraints '${application_constraints}' passed as third parameter to 'init_application'" 191 | fi 192 | 193 | local extra_options="${4-}" # Currently either "with-quiet-support" or nothing 194 | if [ "${extra_options}" == "with-quiet-support" ]; then 195 | enable_quiet_support 196 | fi 197 | 198 | [ -z "${application_description}" ] || _log_ "${application_name}: ${application_description}" 199 | } 200 | 201 | # Remember that we've processed and loaded this script fragment - it's safe now to include others. 202 | application_ready=1 203 | source "${WKDEV_SDK}/utilities/bash-argsparse/argsparse.sh" 204 | source "${WKDEV_SDK}/utilities/settings.sh" 205 | -------------------------------------------------------------------------------- /utilities/bash-argsparse/argsparse-completion.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # -*- tab-width: 4; encoding: utf-8; -*- 3 | # 4 | ######### 5 | # License: 6 | # 7 | # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 8 | # Version 2, December 2004 9 | # 10 | # Copyright (C) 2004 Sam Hocevar 11 | # 12 | # Everyone is permitted to copy and distribute verbatim or modified 13 | # copies of this license document, and changing it is allowed as long 14 | # as the name is changed. 15 | # 16 | # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 17 | # TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 18 | # 19 | # 0. You just DO WHAT THE FUCK YOU WANT TO. 20 | # 21 | ######### 22 | # 23 | ## @file 24 | ## @author Damien Nadé 25 | ## @brief Bash completion for scripts using argsparse library. 26 | ## @copyright WTFPLv2 27 | ## @version 1.8 28 | ## @details 29 | ## @par URL 30 | ## https://github.com/Anvil/bash-argsparse @n 31 | ## 32 | ## @par Purpose 33 | ## 34 | ## To automatically enable, for bash-completion users, completion for 35 | ## scripts that use the argsparse library. 36 | ## 37 | ## @par Usage 38 | ## 39 | ## In your ~/.bashrc, add the following lines to enable completion for 40 | ## all your argsparse-written scripts: 41 | ## 42 | ## @code 43 | ## . argsparse-completion.sh 44 | ## complete -F _argsparse_complete [ your scripts names ... ] 45 | ## @endcode 46 | ## 47 | ## @par Required configuration 48 | ## 49 | ## argsparse-completion relies on a few shell settings: 50 | ## 51 | ## @li "sourcepath" shell option must be enabled. This should be 52 | ## enabled by default, but you can enforce it by running: 53 | ## 54 | ## @code 55 | ## shopt -s sourcepath 56 | ## @endcode 57 | ## 58 | ## If correctly enabled, the following command below should return 59 | ## this output. 60 | ## 61 | ## @code 62 | ## $ shopt sourcepath 63 | ## sourcepath on 64 | ## @endcode 65 | ## 66 | ## @par Limitations 67 | ## 68 | ## @li Every time the completion is invoked, the completed script will 69 | ## be sourced, up to either the argsparse_parse_options() function 70 | ## call or any the first return top-level statement. This means that 71 | ## up to this point the script should not have any side effect (like 72 | ## file system alteration, network connections, ...), and should 73 | ## avoid time-consuming tasks up to this point. 74 | ## 75 | ## @li Only a limited set of option types completion are currently 76 | ## implemented. 77 | ## 78 | ## 79 | ## 80 | ## @defgroup ArgsparseCompletion Bash Completion-related functions. 81 | 82 | ## @fn __argsparse_compgen() 83 | ## @private 84 | ## @brief A compgen wrapper. 85 | ## @details This function will just call compgen with given argument, 86 | ## safely adding $cur in the command line. Also if compgen_prefix is 87 | ## set, a -P option will be provided to compgen. 88 | ## @note __argsparse_compgen() makes use of the bash-completion 89 | ## standard variables. 90 | ## @param param... any set of compgen options 91 | ## @return compgen output and return code. 92 | ## @ingroup ArgsparseCompletion 93 | __argsparse_compgen() { 94 | if [[ -v compgen_prefix ]] 95 | then 96 | set -- "$@" -P "$compgen_prefix" 97 | fi 98 | compgen "$@" -- "$cur" 99 | } 100 | 101 | ## @fn __argsparse_complete_value() 102 | ## @brief Complete the value an option. 103 | ## @details Run compgen with values matching given option. If an array 104 | ## "option__values" exists, complete with its values. Else 105 | ## if option has a type, complete values according to type when 106 | ## possible. Else do nothing. 107 | ## @note __argsparse_complete_value() makes use of the bash-completion 108 | ## standard variables. 109 | ## @param option a long option name. 110 | ## @ingroup ArgsparseCompletion 111 | __argsparse_complete_value() { 112 | [[ $# -eq 1 ]] || return 1 113 | local option=$1 114 | local array option_type 115 | local -a values 116 | if array=$(__argsparse_values_array_identifier "$option") 117 | then 118 | # Option accepts an enumeration of values. 119 | values=( "${!array}" ) 120 | __argsparse_compgen -W "${values[*]}" 121 | elif option_type=$(argsparse_has_option_property "$option" type) 122 | then 123 | case "$option_type" in 124 | file|pipe|socket|link) 125 | __argsparse_compgen -A file 126 | ;; 127 | directory|group) 128 | __argsparse_compgen -A "$option_type" 129 | ;; 130 | username) 131 | __argsparse_compgen -A user 132 | ;; 133 | host|hostname) 134 | __argsparse_compgen -A hostname 135 | ;; 136 | hexa) 137 | values=( "$cur"{a..f} ) 138 | ;;& 139 | int|hexa) 140 | values+=( "$cur"{0..9} ) 141 | __argsparse_compgen -W "${values[*]}" 142 | ;; 143 | esac 144 | fi 145 | } 146 | 147 | ## @fn __argsparse_complete_get_long() 148 | ## @brief Find the option we want to complete. 149 | ## @details If given word parameter is a recognized option, print the 150 | ## matching long option name. Also if "$cur" should be this option 151 | ## value, then return 0. 152 | ## @param word any word. 153 | ## @param long... a list of long options. 154 | ## @return the long option matching given parameter. 155 | ## @retval 0 if given word matches an option and if that option 156 | ## accepts a value. 157 | ## @private 158 | ## @ingroup ArgsparseCompletion 159 | __argsparse_complete_get_long() { 160 | [[ $# -ge 1 ]] || return 1 161 | local word=$1 162 | shift 163 | local -a longs=( "$@" ) 164 | local long 165 | if [[ $word = -[!-] ]] 166 | then 167 | long=$(argsparse_short_to_long "${word#-}") || return 168 | elif __argsparse_index_of "$word" "${longs[@]}" >/dev/null 169 | then 170 | long=${word#--} 171 | else 172 | # Unknown option 173 | return 1 174 | fi 175 | printf %s "$long" 176 | argsparse_has_option_property "$long" value 177 | } 178 | 179 | ## @fn __argsparse_complete() 180 | ## @brief Completion for the command stored in ${words[0]}. 181 | ## @details Will load the script to complete, and invoke compgen 182 | ## according to context. 183 | ## @retval non-zero if completed command cannot be sourced. 184 | ## @ingroup ArgsparseCompletion 185 | __argsparse_complete() { 186 | local script=${words[0]} 187 | ( 188 | set +o posix 189 | ARGSPARSE_COMPLETION_MODE=1 190 | shopt -s expand_aliases 191 | unalias -a 192 | . "$script" 2>/dev/null || return 193 | longs=( "${!__argsparse_options_descriptions[@]}" ) 194 | longs=( "${longs[@]/#/--}" ) 195 | option=${prev#--} 196 | if __argsparse_index_of -- "${words[@]:0:${#words[@]}-1}" >/dev/null 197 | then 198 | # We're after the -- parameter, complete positionnal arguments 199 | __argsparse_compgen -A file 200 | elif long=$(__argsparse_complete_get_long "$prev" "${longs[@]}") 201 | then 202 | # We're right after an option that accepts a value, so 203 | # complete a value. 204 | __argsparse_complete_value "$long" 205 | else 206 | # Complete current token 207 | case "$cur" in 208 | --?*=*|-[!-]?*) 209 | # Complete the --foo=something pattern as if 210 | # prev=--foo and cur=something 211 | # Complete -fsomething as if prev=-f and cur=something 212 | if [[ "$cur" =~ ^((--[^=]+)=)(.*)$ || 213 | "$cur" =~ ^((-[^-]))(.*)$ ]] 214 | then 215 | compgen_prefix=${BASH_REMATCH[1]} 216 | option=${BASH_REMATCH[2]} 217 | cur=${BASH_REMATCH[3]} 218 | long=$(__argsparse_complete_get_long \ 219 | "$option" "${longs[@]}") && \ 220 | __argsparse_complete_value "$long" 221 | fi 222 | ;; 223 | ""|-) 224 | shorts=( "${!__argsparse_short_options[@]}" ) 225 | shorts=( "${shorts[@]/#/-}" ) 226 | ;;& 227 | ""|-*) 228 | __argsparse_compgen -W "${shorts[*]} ${longs[*]}" 229 | ;; 230 | *) 231 | # Default non-option completion. 232 | __argsparse_compgen -A file 233 | ;; 234 | esac 235 | fi 236 | ) 237 | } 238 | 239 | ## @fn _argsparse_complete() 240 | ## @brief The argsparse completion function. 241 | ## @details To enable completion on a script that uses the argsparse 242 | ## library, call the "complete" built-in as following: @n 243 | ## @code 244 | ## complete -F _argsparse_complete 245 | ## @endcode 246 | ## @note Technically, this function gets a parameter (the name of the 247 | ## command to complete), but it is ignored. 248 | ## @ingroup ArgsparseCompletion 249 | _argsparse_complete() { 250 | local cur prev words cword split 251 | _init_completion -s || return 252 | COMPREPLY=( $(__argsparse_complete) ) 253 | } 254 | -------------------------------------------------------------------------------- /images/wkdev_sdk/jhbuild/webkit-sdk-deps.modules: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 27 | 29 | 31 | 33 | 35 | 37 | 38 | 39 | 42 | 43 | 44 | 46 | 50 | 51 | 52 | 53 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 69 | 70 | 71 | 72 | 73 | 76 | 77 | 78 | 79 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 91 | 92 | 93 | 94 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 107 | 108 | 109 | 110 | 111 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 135 | 136 | 137 | 138 | 141 | 142 | 143 | 144 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 156 | 157 | 158 | 159 | 165 | 166 | 167 | 168 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | gcr-4.pc 183 | 184 | 185 | 186 | 187 | libarchive.pc 188 | 189 | 190 | 191 | 192 | libportal-gtk4.pc 193 | 194 | 195 | 196 | 197 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 219 | 220 | 221 | 222 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 238 | 239 | 240 | 241 | -------------------------------------------------------------------------------- /scripts/host-only/wkdev-enter: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2024 Igalia S.L. 3 | # SPDX-License: MIT 4 | 5 | [ -f "${WKDEV_SDK}/.wkdev-sdk-root" ] && source "${WKDEV_SDK}/utilities/application.sh" || { echo "Please set \${WKDEV_SDK} to point to the root of the wkdev-sdk checkout."; exit 1; } 6 | init_application "${0}" "Launch a command or spawn an interactive shell in a container built by 'wkdev-create'" host-only with-quiet-support 7 | 8 | # Source utility script fragments 9 | source "${WKDEV_SDK}/utilities/host-setup-tasks.sh" 10 | source "${WKDEV_SDK}/utilities/podman.sh" 11 | 12 | argsparse_use_option debug "Enable debug logging for podman (useful to debug container issues)" 13 | argsparse_use_option trace "Enable 'xtrace' mode for this script" 14 | argsparse_use_option =verbose "Increase verbosity of this script" 15 | 16 | argsparse_use_option =root "Login as root user in container (mapped to $(id --user --name) on host)" 17 | argsparse_use_option no-tty "Disable tty allocation (you need this if you need want stdout and stderr to be different streams)" short:T 18 | argsparse_use_option no-interactive "Disable stdin (useful in scripts, see Caveats)" short:I 19 | 20 | argsparse_use_option =exec "Treat all remaining non-option arguments (or everything after the '--' character sequence) as command to execute in the container instead of spawning a shell" 21 | argsparse_use_option =name: "Name of container" default:wkdev 22 | 23 | argsparse_use_option max-retries: "Maximum number of + cycles" type:uint default:10 24 | argsparse_use_option sleep-duration: "Amount of seconds to sleep before attempting to enter the container (during first-time container run)" type:uint default:20 25 | 26 | argsparse_usage_description="$(cat <> 28 | 29 | Spawns an interactive shell in a running container, or executes a command in the container. 30 | 31 | When a 'wkdev-sdk' container is entered the first time, it has to be adapted according to 32 | the needs of the current host user that enters it. For example the same shell that the 33 | user is currently using to launch these scripts is installed in the container, if not 34 | present. The first-time initialiation takes a while, and therefore entering the container 35 | interactively is delayed. ${application_name} sleeps for a while and retries a configurable 36 | amount of times before it gives up trying to enter the container. The podman logs for the 37 | container allow to check the status and/or debug possible issues with the initialization 38 | procedure. 39 | 40 | << Examples >> 41 | 42 | $ ${application_name} --name 43 | $ ${application_name} --root --name 44 | $ ${application_name} --exec --name -- uptime 45 | 46 | << Caveats >> 47 | 48 | By default, this command uses podman exec --interactive under the hood. 49 | 50 | podman exec --interactive uses sockets to pipe stdin to the contained process, and as a 51 | consequence it will read as much input from stdin as it is available, even if the 52 | contained process doesn't request it. This can lead to hangs or corrupted stdin if the 53 | same stdin is connected to a script that calls wkdev-enter as anything but the last 54 | command. Therefore, when writing scripts, use non-interactive mode where stdin is not 55 | necessary and avoid sharing stdin with several interactive executions. 56 | 57 | EOF 58 | )" 59 | 60 | process_command_line_arguments() { 61 | 62 | argsparse_allow_no_argument "true" 63 | argsparse_parse_options "${@}" 64 | argsparse_is_option_set "trace" && set -o xtrace 65 | 66 | container_name="${program_options["name"]}" 67 | max_retries="${program_options["max-retries"]}" 68 | sleep_duration_in_seconds="${program_options["sleep-duration"]}" 69 | } 70 | 71 | build_podman_arguments() { 72 | 73 | local -n generic_arguments=${1} 74 | 75 | # On the host, the podman user socket can be used as well: 76 | # '--remote' routes podman communication through the socket, 77 | # which is supposed to deliver a CLI usage with less latency. 78 | is_podman_user_socket_available && generic_arguments+=("--remote") 79 | 80 | argsparse_is_option_set "debug" && generic_arguments+=("--log-level debug") 81 | } 82 | 83 | ensure_container_is_running() { 84 | 85 | # Check if container is running, if not - start or create it. 86 | container_status="$(get_podman_container_status "${container_name}")" 87 | argsparse_is_option_set "debug" && _log_ "## Status for '${container_name}' container: '${container_status}'" 88 | 89 | if [[ "${container_status}" == "unknown" ]] || [[ -z "${container_status}" ]]; then 90 | _abort_ "Cannot find container '${container_name}'" 91 | fi 92 | [ "${container_status}" == "running" ] || { 93 | _log_ ""; 94 | _log_ "-> Container '${container_name}' is not yet running. Starting it before attempting to enter..."; 95 | host_setup_prerun_tasks 96 | # There is no flag to prevent `podman start` from writing the id of the 97 | # container to stdout, so we just connect stdout to /dev/null. 98 | run_podman_silent_unless_verbose_or_abort start "${container_name}" >/dev/null 99 | } 100 | } 101 | 102 | propagate_environment_variables_from_host() { 103 | 104 | local -r passthrough_variables=( 105 | 'TERM' 106 | 'WAYLAND_DISPLAY' 107 | 'DISPLAY' 108 | 'PULSE_SERVER' 109 | 'XDG_SESSION_TYPE' 110 | 'PIPEWIRE_REMOTE' 111 | ) 112 | 113 | for env in "${passthrough_variables[@]}"; do 114 | if [ -n "${!env-}" ]; then 115 | podman_exec_arguments+=("--env=${env}=${!env-}") 116 | fi 117 | done 118 | } 119 | 120 | # Main functionality 121 | run() { 122 | 123 | process_command_line_arguments "${@}" 124 | 125 | # 1) Ensure container is running 126 | ensure_container_is_running 127 | 128 | # 2) Check if container is initialized (running is not sufficient). 129 | local podman_arguments=() 130 | build_podman_arguments podman_arguments 131 | 132 | run_podman_silent ${podman_arguments[@]} exec "${container_name}" test -f "$(get_init_done_file)" || { 133 | _log_ "" 134 | _log_ "-> Wait for '${container_name}' to finish initialization..." 135 | 136 | local retries=0 137 | while : ; do 138 | run_podman_silent ${podman_arguments[@]} exec "${container_name}" test -f "$(get_init_done_file)" && break 139 | 140 | retries=$((retries+1)) 141 | [ ${retries} -eq ${max_retries} ] && _abort_ "Container does not start, please investigate using 'podman logs -f ${container_name}'" 142 | 143 | _log_ " Retry ${retries}/${max_retries} in ${sleep_duration_in_seconds} seconds..." 144 | sleep ${sleep_duration_in_seconds} 145 | 146 | _log_ "" 147 | _log_ " Last 5 lines from container log (invoke 'podman logs ${container_name}' to see the full output):" 148 | _log_ " ------------------------------------------------------------------" 149 | run_podman ${podman_arguments[@]} logs --tail 5 "${container_name}" 150 | _log_ " ------------------------------------------------------------------" 151 | _log_ "" 152 | done; 153 | } 154 | 155 | # Prepare to call "podman exec". 156 | if argsparse_is_option_set "verbose"; then 157 | _log_ "" 158 | 159 | if argsparse_is_option_set "exec"; then 160 | _log_ "-> Spawn non-interactive shell session in '${container_name}' container ('--exec' mode enabled)..." 161 | else 162 | _log_ "-> Spawn interactive shell session in '${container_name}' container..." 163 | fi 164 | fi 165 | 166 | local podman_exec_arguments=() 167 | 168 | # Request pseudo-tty allocation unless explicitly disabled 169 | if ! argsparse_is_option_set "no-tty"; then 170 | podman_exec_arguments+=("--tty") 171 | fi 172 | 173 | # Ensure WKDEV_SDK is set. It is done here and not creation to support older containers. 174 | podman_exec_arguments+=("--env" "WKDEV_SDK=/wkdev-sdk") 175 | 176 | # Choose root or regular user. 177 | if argsparse_is_option_set "root"; then 178 | podman_exec_arguments+=("--user" "0:0") 179 | else 180 | podman_exec_arguments+=("--user" "$(id --user --real):$(id --group --real)") 181 | fi 182 | 183 | propagate_environment_variables_from_host 184 | 185 | podman_exec_arguments+=("${container_name}") 186 | 187 | # Mount pipewire, wayland, etc... 188 | run_podman "${podman_arguments[@]}" exec "${podman_exec_arguments[@]}" /wkdev-sdk/scripts/container-only/.wkdev-sync-runtime-state "${quiet_args[@]}" " setting. 13 | ARG NUMBER_OF_PARALLEL_BUILDS=4 14 | ARG CONTAINER_LOCALE=en_US.UTF-8 15 | 16 | # No need to modify these. 17 | ARG APT_UPDATE="apt-get update" 18 | ARG APT_BUILDDEP="apt-get --assume-yes build-dep" 19 | ARG APT_UPGRADE="apt-get --assume-yes upgrade" 20 | ARG APT_INSTALL="apt-get --assume-yes install --no-install-recommends" 21 | ARG APT_AUTOREMOVE="apt-get --assume-yes autoremove" 22 | 23 | # Disable prompt during package configuration 24 | ENV DEBIAN_FRONTEND noninteractive 25 | 26 | # Used by the WebKit tooling to detect if is running inside the SDK 27 | ENV WEBKIT_CONTAINER_SDK "1" 28 | 29 | # Enable debugging in WebKit's sandbox. 30 | ENV WEBKIT_ENABLE_DEBUG_PERMISSIONS_IN_SANDBOX "1" 31 | 32 | # Used in webkitdirs.pm to prefer building against system libraries instead of the Flatpak SDK. 33 | ENV WEBKIT_BUILD_USE_SYSTEM_LIBRARIES "1" 34 | 35 | # Delete the default ubuntu user which has a UID of 1000. 36 | # Podman refuses to map a user from the host if the UID is already in /etc/passwd. 37 | RUN userdel ubuntu 38 | 39 | # NOTE: All RUN commands contain the (autoremove / clean / rm step to ensure that no intermediate layer 40 | # ever contains unncessary stuff that never appears in the final image, only in deeper layers, and 41 | # thus increases the whole image size no gain, except an "easier to read" Dockerfile. 42 | 43 | # Disable sandboxing (dropping privileges to _apt user during apt-get update/install/... fails when using 44 | # podman in podman if both are rootless; since it's no gain in security in the container anyhow, disable it. 45 | RUN echo 'APT::Sandbox::User "root";' > /etc/apt/apt.conf.d/no-sandbox 46 | 47 | # Update package list, upgrade to latest version, install necessary packages for 48 | # early bootstrapping: .deb package configuration + locale generation + curl for downloading GPG keys. 49 | RUN ${APT_UPDATE} && \ 50 | ${APT_INSTALL} apt-utils ca-certificates curl dialog libterm-readline-gnu-perl locales unminimize && \ 51 | ${APT_UPGRADE} && ${APT_AUTOREMOVE} 52 | 53 | # Disable exclusion of locales / translations / documentation (default in Ubuntu images) 54 | RUN yes | /usr/bin/unminimize 55 | 56 | # Switch to fixed locale. 57 | RUN locale-gen ${CONTAINER_LOCALE} 58 | ENV LC_ALL ${CONTAINER_LOCALE} 59 | ENV LANG ${CONTAINER_LOCALE} 60 | RUN dpkg-reconfigure locales 61 | 62 | # Ensure a strong TLS connection is always used when downloading sensitive files. 63 | ARG CURL_DOWNLOAD="curl --proto =https --tlsv1.2 --show-error --silent --fail" 64 | 65 | # Add LLVM repo for clang packages (clang-21 requires apt.llvm.org). 66 | COPY /rootfs/etc/apt/sources.list.d/llvm.list /etc/apt/sources.list.d/llvm.list 67 | RUN ${CURL_DOWNLOAD} https://apt.llvm.org/llvm-snapshot.gpg.key -o /etc/apt/trusted.gpg.d/apt.llvm.org.asc 68 | 69 | # Install all dependencies for WebKit/GStreamer/etc in one pass. 70 | WORKDIR /var/tmp/wkdev-packages 71 | COPY /required_system_packages/*.lst . 72 | 73 | # We unconditionally install the GCC-based cross toolchain for armhf. 74 | COPY /cross . 75 | 76 | # NB: The armhf binary packages can only be found in ports.ubuntu.com. On 77 | # aarch64, things are already set up properly (aarch64 also uses 78 | # ports.ubuntu.com and specifies the architecture). However, on x86_64 the 79 | # default sources point to {archive,securyt}.ubuntu.com. There, we need to 80 | # generate the corresponding ubuntu-armhf.sources as well as rewrite 81 | # ubuntu.sources to only use the default sources for amd64. 82 | RUN sed -i 's/^Types: deb$/Types: deb deb-src/' /etc/apt/sources.list.d/ubuntu.sources && \ 83 | if [ "$(uname -m)" = "x86_64" ]; then \ 84 | sed -r 's@(archive|security).ubuntu.com/ubuntu@ports.ubuntu.com/ubuntu-ports@g' /etc/apt/sources.list.d/ubuntu-armhf.sources; \ 85 | sed -i -r 's/^(Components:.*)/\1\nArchitectures: amd64/' /etc/apt/sources.list.d/ubuntu.sources; \ 86 | sed -i -r 's/^(Components:.*)/\1\nArchitectures: armhf/' /etc/apt/sources.list.d/ubuntu-armhf.sources; \ 87 | fi && \ 88 | dpkg --add-architecture armhf && \ 89 | ${APT_UPDATE} && \ 90 | export LST_FILES=*.lst && \ 91 | if [ "$(uname -m)" = "x86_64" -o "$(uname -m)" = "aarch64" ]; then export LST_FILES="$LST_FILES $(echo armhf/*.lst)"; fi; \ 92 | for list in $LST_FILES; do \ 93 | echo ""; \ 94 | echo "#### Installing required packages from ${list}..."; \ 95 | ${APT_INSTALL} $(sed -e "s/.*#.*//; /^$/d" "${list}" | tr '\n' ' ') || exit 1; \ 96 | done; \ 97 | ${APT_BUILDDEP} gst-libav1.0 gst-plugins-bad1.0 gst-plugins-base1.0 \ 98 | gst-plugins-good1.0 gst-plugins-ugly1.0 && \ 99 | git clone --filter=blob:none --no-checkout --depth=1 https://github.com/WebKit/WebKit.git && \ 100 | cd WebKit && \ 101 | git sparse-checkout set Tools/ && \ 102 | git checkout main && \ 103 | yes | ./Tools/gtk/install-dependencies && \ 104 | yes | ./Tools/wpe/install-dependencies && \ 105 | cd .. && \ 106 | rm -rf WebKit && \ 107 | ${APT_AUTOREMOVE} 108 | 109 | # GStreamer 1.26.x requires at least Meson 1.4. Install the latest version. 110 | RUN pip install meson==1.8.2 --break-system-packages 111 | 112 | # Add Rust environment. 113 | ENV RUSTUP_HOME="/opt/rust" \ 114 | CARGO_HOME="/opt/rust" \ 115 | PATH="/opt/rust/bin:${PATH}" 116 | 117 | RUN ${CURL_DOWNLOAD} https://sh.rustup.rs | sh -s -- -y && \ 118 | rustup default 1.84.1 && \ 119 | rustup component remove rust-docs && \ 120 | cargo install --root /usr/local --version 0.9.1 --locked sccache && \ 121 | cargo install --root /usr/local --version 0.10.9 --locked cargo-c 122 | 123 | RUN git clone https://github.com/ystreet/librice.git && \ 124 | cd librice && \ 125 | git checkout b7435690c0b233427479ab38ece1390e13074e22 && \ 126 | cargo cinstall -p rice-proto --release --prefix=/usr --libdir=lib/$(gcc -dumpmachine) --library-type=cdylib && \ 127 | cargo cinstall -p rice-io --release --prefix=/usr --libdir=lib/$(gcc -dumpmachine) --library-type=cdylib && \ 128 | cd .. && \ 129 | rm -fr librice 130 | 131 | # Copy jhbuild helper files and do the initial build & install 132 | COPY /jhbuild/jhbuildrc /etc/xdg/jhbuildrc 133 | COPY /jhbuild/webkit-sdk-deps.modules /jhbuild/webkit-sdk-deps.modules 134 | 135 | WORKDIR /jhbuild 136 | RUN git clone https://gitlab.gnome.org/GNOME/jhbuild.git && \ 137 | cd jhbuild && \ 138 | ./autogen.sh --prefix=/usr/local && \ 139 | make && \ 140 | make install && \ 141 | export JHBUILD_RUN_AS_ROOT=1 WKDEV_IN_IMAGE_BUILD=1 && \ 142 | jhbuild --exit-on-error --no-interact build && \ 143 | rm -r /var/tmp/jhbuild 144 | 145 | # Register basic JHBuild environment 146 | # TODO: Instead of hardcoding here the values it would be better to 147 | # explore the possibility of generating it dynamically with "jhbuild shell" 148 | # when the user enters into the container (or similar), but that may cause 149 | # issues with the env not exported when someone enter into the 150 | # container via direct command exec rather than by login 151 | ENV LIB "/jhbuild/install/lib" 152 | ENV INCLUDE "/jhbuild/install/include" 153 | ENV LD_LIBRARY_PATH "/jhbuild/install/lib" 154 | ENV GST_PLUGIN_PATH_1_0 "/jhbuild/install/lib/gstreamer-1.0" 155 | ENV PKG_CONFIG_PATH "/jhbuild/install/lib/pkgconfig:/jhbuild/install/share/pkgconfig:/usr/local/lib/pkgconfig:/usr/local/share/pkgconfig:/usr/lib/x86_64-linux-gnu/pkgconfig:/usr/lib/pkgconfig:/usr/share/pkgconfig" 156 | ENV LDFLAGS "-L/jhbuild/install/lib" 157 | ENV C_INCLUDE_PATH "/jhbuild/install/include" 158 | ENV CPLUS_INCLUDE_PATH "/jhbuild/install/include" 159 | ENV GI_TYPELIB_PATH "/jhbuild/install/lib/girepository-1.0" 160 | ENV XDG_DATA_DIRS "/jhbuild/install/share:/usr/local/share:/usr/share" 161 | ENV PATH "/jhbuild/install/bin:$PATH" 162 | 163 | # Podman proxy, connecting to host instance 164 | COPY /rootfs/usr/bin/podman-host /usr/bin/podman-host 165 | 166 | COPY /rootfs/etc/ccache.conf /etc/ccache.conf 167 | 168 | # Convenience symlinks for clang tools, the VSCode extension doesn't find these by default. 169 | COPY --from=scripts /helpers/create-clang-symlinks /tmp/create-clang-symlinks 170 | RUN /tmp/create-clang-symlinks 18 && rm /tmp/create-clang-symlinks 171 | 172 | # Fix Qt6 system packages - missing symlinks in the Ubuntu-provided packages. 173 | RUN export QT_VERSION=$(qmake6 -query QT_VERSION) && \ 174 | for directory in /usr/include/x86_64-linux-gnu/qt6/*; do \ 175 | ln -s ${directory} ${directory}/${QT_VERSION} >/dev/null 2>&1 || true; \ 176 | done 177 | 178 | # Specify the default OpenXR runtime 179 | RUN mkdir -p /etc/xdg/openxr/1 && \ 180 | ln -s /jhbuild/install/share/openxr/1/openxr_monado.json /etc/xdg/openxr/1/active_runtime.json 181 | 182 | # Check GStreamer plugins are installed. 183 | RUN gst-inspect-1.0 audiornnoise && \ 184 | gst-inspect-1.0 cea608tott && \ 185 | gst-inspect-1.0 dav1ddec && \ 186 | gst-inspect-1.0 isofmp4mux && \ 187 | gst-inspect-1.0 rsrtp && \ 188 | gst-inspect-1.0 x264enc && \ 189 | gst-inspect-1.0 x265enc 190 | 191 | # Check locally installed libraries. 192 | RUN pkg-config --modversion rice-io && \ 193 | pkg-config --modversion rice-proto 194 | 195 | # Place the toolchainfiles in a discoverable location. 196 | RUN mkdir -p /usr/share/wkdev-sdk/cross/armhf && \ 197 | cp -r /var/tmp/wkdev-packages/armhf/*.cmake /usr/share/wkdev-sdk/cross/armhf/ 198 | 199 | # Remove systemd services that would startup by default, when spawning 200 | # systemd as PID 1 within the container (usually, we don't spawn systemd 201 | # within the wkdev-sdk container, for interactive usage, but we do so 202 | # when deploying the wkdev-sdk image as bot). 203 | RUN find /etc/systemd/ -type l -name '*.service' -print -exec rm {} \; 204 | 205 | # Create the /sdk common-directory to help with ccache/sccache artifact sharing 206 | RUN mkdir /sdk 207 | 208 | # Change the owner to UID 1000 as a fast path 209 | RUN chown --recursive 1000:1000 ${RUSTUP_HOME} /jhbuild /sdk 210 | 211 | # Switch back to interactive prompt, when using apt. 212 | ENV DEBIAN_FRONTEND dialog 213 | -------------------------------------------------------------------------------- /scripts/container-only/.wkdev-init: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2024 Igalia S.L. 3 | # SPDX-License: MIT 4 | 5 | [ -z "${WKDEV_SDK}" ] && export WKDEV_SDK=/wkdev-sdk 6 | [ -f "${WKDEV_SDK}/.wkdev-sdk-root" ] && source "${WKDEV_SDK}/utilities/application.sh" || { echo "Please set \${WKDEV_SDK} to point to the root of the wkdev-sdk checkout."; exit 1; } 7 | init_application "${0}" "Performs post-container-startup initialization tasks." container-only 8 | 9 | # Source utility script fragments 10 | source "${WKDEV_SDK}/utilities/debian-packages.sh" 11 | source "${WKDEV_SDK}/utilities/podman.sh" 12 | 13 | argsparse_use_option trace "Enable 'xtrace' mode for this script" 14 | 15 | argsparse_use_option =shell: "Specific shell to use for interactive container usage" mandatory 16 | argsparse_use_option =user: "Container account user name" type:username mandatory 17 | argsparse_use_option =group: "Container account group name" type:group mandatory 18 | argsparse_use_option =packages: "Additional packages to install" 19 | argsparse_use_option exit-when-done "Instead of sleeping exit (stopping the container) when done" 20 | 21 | 22 | argsparse_usage_description="$(cat <> 24 | 25 | ${application_name} is the entry point for the wkdev-sdk container. 26 | 27 | It performs various initialization tasks, such as installing the user shell package in the container, 28 | updating the APT cache, setting up sudo support, etc. 29 | 30 | NOTE: You do NOT need to call this script manually, it is only used during container startup. 31 | EOF 32 | )" 33 | 34 | process_command_line_arguments() { 35 | 36 | argsparse_parse_options "${@}" 37 | argsparse_is_option_set "trace" && set -o xtrace 38 | 39 | container_shell="${program_options["shell"]}" 40 | container_user_name="${program_options["user"]}" 41 | container_group_name="${program_options["group"]}" 42 | additional_packages="${program_options["packages"]-}" 43 | } 44 | 45 | try_copy_hosts_file() { 46 | 47 | task_step "Copying /etc/hosts file from host to container..." 48 | if [ -f /host/etc/hosts ]; then 49 | cp /host/etc/hosts /etc/hosts || _abort_ "Cannot copy /etc/hosts file from host to container" 50 | else 51 | echo "No hosts file found, creating one." 52 | cat > /etc/hosts < /etc/subgid contents: $(cat /etc/subgid)" 139 | echo " -> /etc/subuid contents: $(cat /etc/subuid)" 140 | } 141 | 142 | try_setup_journal_dev_log() { 143 | 144 | task_step "Check if /dev/log exists, if not set symbolic link to /run/systemd/journal/dev-log ..." 145 | echo "" 146 | 147 | if [ ! -e /dev/log ]; then 148 | sudo ln -s /run/systemd/journal/dev-log /dev/log 149 | echo " -> /dev/log symbolic link created." 150 | else 151 | echo " -> /dev/log already exists, skipping symbolic link creation." 152 | fi 153 | } 154 | 155 | try_setup_sudoers_file() { 156 | 157 | local sudoers_directory="/etc/sudoers.d" 158 | local sudoers_file="${sudoers_directory}/sudoers" 159 | 160 | task_step "Setup sudoers file '${sudoers_file}' for user '${container_user_name}', if necessary..." 161 | 162 | mkdir --parents "${sudoers_directory}" &>/dev/null 163 | chmod 750 "${sudoers_directory}" 164 | 165 | [ ! -f "${sudoers_file}" ] && touch "${sudoers_file}" 166 | 167 | # Suppress FQDN checks upon sudo invocation 168 | if ! grep --quiet "Defaults !fqdn" "${sudoers_file}"; then 169 | echo "Defaults !fqdn" >> "${sudoers_file}" 170 | fi 171 | 172 | # Ensure passwordless sudo is set up for user 173 | if ! grep --quiet "${container_user_name} ALL = (root) NOPASSWD:ALL" "${sudoers_file}"; then 174 | echo "${container_user_name} ALL = (root) NOPASSWD:ALL" >> "${sudoers_file}" 175 | fi 176 | } 177 | 178 | try_setup_run_user_directory() { 179 | 180 | local container_user_id=$(id --user --real "${container_user_name}") 181 | local current_run_user_directory="/run/user/${container_user_id}" 182 | 183 | task_step "Initialize systemd-style /run/user/ user session directory '${current_run_user_directory}', if necessary..." 184 | 185 | mkdir --parents "${current_run_user_directory}" &>/dev/null 186 | chmod 700 "${current_run_user_directory}" 187 | chown "${container_user_name}:${container_group_name}" "${current_run_user_directory}" &>/dev/null 188 | } 189 | 190 | try_setup_dockerenv_file() { 191 | 192 | task_step "Create /.dockerenv file to make 'bwrap' detection work in legacy Epiphany/cog versions..." 193 | sudo touch /.dockerenv 194 | } 195 | 196 | try_setup_permissions_jhbuild_directory() { 197 | 198 | local jhbuild_directory="/jhbuild" 199 | 200 | task_step "Setup jhbuild '${jhbuild_directory}' directory permissions..." 201 | 202 | if sudo -u "${container_user_name}" test -w "${jhbuild_directory}"; then 203 | task_step "Already writable, skipping." 204 | else 205 | chown --recursive "${container_user_name}:${container_group_name}" "${jhbuild_directory}" &>/dev/null 206 | fi 207 | } 208 | 209 | try_setup_permissions_rust_directory() { 210 | 211 | local rust_directory="/opt/rust" 212 | 213 | task_step "Setup rust '${rust_directory}' directory permissions..." 214 | 215 | if sudo -u "${container_user_name}" test -w "${rust_directory}"; then 216 | task_step "Already writable, skipping." 217 | else 218 | chown --recursive "${container_user_name}:${container_group_name}" "${rust_directory}" &>/dev/null 219 | fi 220 | } 221 | 222 | try_setup_root_sdk_directory() { 223 | 224 | local root_sdk_directory="/sdk" 225 | 226 | task_step "Setup '${root_sdk_directory}' directory permissions..." 227 | 228 | if sudo -u "${container_user_name}" test -w "${root_sdk_directory}"; then 229 | task_step "Already writable, skipping." 230 | else 231 | chown "${container_user_name}:${container_group_name}" "${root_sdk_directory}" 232 | fi 233 | } 234 | 235 | 236 | try_firstrun_script() { 237 | 238 | local user_home 239 | 240 | user_home=$(getent passwd "${container_user_name}" | cut -d: -f6) 241 | 242 | if [ -x "${user_home}/.wkdev-firstrun" ]; then 243 | task_step "Executing ${user_home}/.wkdev-firstrun" 244 | su "${container_user_name}" --group="${container_group_name}" --command="${user_home}/.wkdev-firstrun" 245 | else 246 | task_step "Skipping ${user_home}/.wkdev-firstrun script, no executable found." 247 | fi 248 | } 249 | 250 | sleep_forever() { 251 | 252 | echo "" 253 | echo "Sleeping forever..." 254 | exec sleep infinity 255 | } 256 | 257 | TASKS=( 258 | "try_copy_hosts_file" 259 | "try_update_apt_cache" 260 | "try_install_shell_package" 261 | "try_install_optional_drivers" 262 | "try_install_additional_packages" 263 | "try_switch_shell_for_user" 264 | "try_setup_setgid_subuid_files" 265 | "try_setup_sudoers_file" 266 | "try_setup_journal_dev_log" 267 | "try_setup_run_user_directory" 268 | "try_setup_dockerenv_file" 269 | "try_setup_permissions_jhbuild_directory" 270 | "try_setup_permissions_rust_directory" 271 | "try_setup_root_sdk_directory" 272 | "try_firstrun_script" 273 | ) 274 | 275 | task_step() { 276 | TASK_STEP=${TASK_STEP:-1} 277 | TASK_TOTAL=${TASK_TOTAL:-${#TASKS[@]}} 278 | 279 | echo "" 280 | echo "[${TASK_STEP}/${TASK_TOTAL}] ${1}" 281 | TASK_STEP=$(( TASK_STEP + 1 )) 282 | } 283 | 284 | # Main functionality 285 | run() { 286 | 287 | if [ "$EUID" -ne 0 ]; then 288 | echo "This script should only be ran as root." 289 | exit 1 290 | fi 291 | 292 | process_command_line_arguments "${@}" 293 | 294 | if [ -f "$(get_init_done_file)" ]; then 295 | echo "Initialization already performed, skipping." 296 | sleep_forever 297 | exit 0 298 | fi 299 | 300 | # Set for the whole wkdev-init session. 301 | export DEBIAN_FRONTEND=noninteractive 302 | 303 | echo "-> Performing post-startup initialization tasks in container..." 304 | for task in ${TASKS[@]}; do 305 | $task 306 | done 307 | 308 | # Reset DEBIAN_FRONTEND again. 309 | unset DEBIAN_FRONTEND 310 | 311 | echo "" 312 | echo "Finished initialization" 313 | 314 | touch "$(get_init_done_file)" 315 | 316 | if ! argsparse_is_option_set "exit-when-done"; then 317 | sleep_forever 318 | fi 319 | } 320 | 321 | run "${@}" 322 | -------------------------------------------------------------------------------- /.github/workflows/wkdev-sdk.yml: -------------------------------------------------------------------------------- 1 | name: wkdev-sdk 2 | on: 3 | push: 4 | branches: [ main, tag/** ] 5 | paths-ignore: [ docs/*, README.md] 6 | pull_request: 7 | paths-ignore: [ docs/*, README.md] 8 | create: 9 | defaults: 10 | run: 11 | shell: bash 12 | jobs: 13 | build_amd64: 14 | runs-on: [self-hosted, x64] 15 | permissions: 16 | packages: write 17 | if: | 18 | github.event_name != 'create' || 19 | startsWith(github.ref_name, 'tag/') 20 | steps: 21 | - name: Set tag name 22 | run: | 23 | if [ "${GITHUB_REF_NAME}" = 'main' ]; then 24 | echo "WKDEV_SDK_TAG=latest" >> "${GITHUB_ENV}" 25 | elif [[ "${GITHUB_REF_NAME}" =~ ^tag/(.+)$ ]]; then 26 | echo "WKDEV_SDK_TAG=${BASH_REMATCH[1]}" >> "${GITHUB_ENV}" 27 | else 28 | echo "WKDEV_SDK_TAG=latest" >> "${GITHUB_ENV}" 29 | fi 30 | echo "WKDEV_SDK_CONTAINER_REGISTRY_USER_NAME=$(echo ${GITHUB_REPOSITORY_OWNER} | tr '[:upper:]' '[:lower:]')" >> "${GITHUB_ENV}" 31 | echo "REPO=ghcr.io/$(echo ${GITHUB_REPOSITORY_OWNER} | tr '[:upper:]' '[:lower:]')/wkdev-sdk" >> "${GITHUB_ENV}" 32 | 33 | - name: Install podman 34 | run: sudo apt-get update && sudo apt-get -y install podman fuse-overlayfs 35 | 36 | - name: Prune old containers/images 37 | run: | 38 | echo "Disk space before prune:" 39 | df -h 40 | echo "Pruning containers and images older than 2 weeks..." 41 | podman system prune --all --force --volumes --filter until=336h || true 42 | echo "Disk space after prune:" 43 | df -h 44 | 45 | - name: Checkout repo 46 | uses: actions/checkout@v4 47 | 48 | - name: Clean unrelated images 49 | run: | 50 | podman rmi --ignore --force ${REPO}:${WKDEV_SDK_TAG} 51 | podman manifest rm ${REPO}:${WKDEV_SDK_TAG} || true 52 | podman rmi --ignore --force ${REPO}:${WKDEV_SDK_TAG}_amd64 53 | 54 | - name: Build image 55 | run: | 56 | podman build --jobs $(nproc) --build-context scripts=./scripts -t ${REPO}:${WKDEV_SDK_TAG}_amd64 --arch=amd64 images/wkdev_sdk 57 | podman image list 58 | 59 | - name: Test image 60 | run: | 61 | CONTAINER="wkdev-$(date +%s)" 62 | echo "CONTAINER=${CONTAINER}" >> "${GITHUB_ENV}" 63 | source ./register-sdk-on-host.sh 64 | wkdev-create --create-home --home ${HOME}/${CONTAINER}-home --verbose --attach --no-pull --name ${CONTAINER} --arch amd64 65 | wkdev-enter -n ${CONTAINER} --exec -- git clone --depth=1 https://github.com/WebKit/WebKit.git 66 | wkdev-enter -n ${CONTAINER} --exec -- ./WebKit/Tools/Scripts/build-webkit --wpe --release --generate-project-only 67 | wkdev-enter -n ${CONTAINER} --exec -- ./WebKit/Tools/Scripts/build-webkit --gtk --release --generate-project-only 68 | 69 | - name: Push image 70 | if: github.ref_name == 'main' || startsWith(github.ref_name, 'tag/') 71 | run: | 72 | echo "${{ secrets.GITHUB_TOKEN }}" | podman login ghcr.io --username=${GITHUB_REPOSITORY_OWNER} --password-stdin 73 | podman push ${REPO}:${WKDEV_SDK_TAG}_amd64 74 | 75 | - name: Cleanup test artifacts 76 | if: always() 77 | run: | 78 | # Remove the specific container created by this test run 79 | if [ -n "${CONTAINER:-}" ]; then 80 | podman rm --force ${CONTAINER} 2>/dev/null || true 81 | rm -rf ${HOME}/${CONTAINER}-home || true 82 | fi 83 | # Clean up any leftover containers and home directories from previous failed runs 84 | podman ps -a --filter "name=^wkdev-" --format "{{.Names}}" | xargs -r podman rm --force 2>/dev/null || true 85 | find ${HOME} -maxdepth 1 -type d -name "wkdev-*-home" -exec rm -rf {} + 2>/dev/null || true 86 | 87 | build_arm64: 88 | runs-on: [self-hosted, arch-arm64] 89 | permissions: 90 | packages: write 91 | if: | 92 | github.event_name != 'create' || 93 | startsWith(github.ref_name, 'tag/') 94 | steps: 95 | - name: Set tag name 96 | run: | 97 | if [ "${GITHUB_REF_NAME}" = 'main' ]; then 98 | echo "WKDEV_SDK_TAG=latest" >> "${GITHUB_ENV}" 99 | elif [[ "${GITHUB_REF_NAME}" =~ ^tag/(.+)$ ]]; then 100 | echo "WKDEV_SDK_TAG=${BASH_REMATCH[1]}" >> "${GITHUB_ENV}" 101 | else 102 | echo "WKDEV_SDK_TAG=latest" >> "${GITHUB_ENV}" 103 | fi 104 | echo "WKDEV_SDK_CONTAINER_REGISTRY_USER_NAME=$(echo ${GITHUB_REPOSITORY_OWNER} | tr '[:upper:]' '[:lower:]')" >> "${GITHUB_ENV}" 105 | echo "REPO=ghcr.io/$(echo ${GITHUB_REPOSITORY_OWNER} | tr '[:upper:]' '[:lower:]')/wkdev-sdk" >> "${GITHUB_ENV}" 106 | 107 | - name: Install podman 108 | run: sudo apt-get update && sudo apt-get -y install podman fuse-overlayfs 109 | 110 | - name: Prune old containers/images 111 | run: | 112 | echo "Disk space before prune:" 113 | df -h 114 | echo "Pruning containers and images older than 2 weeks..." 115 | podman system prune --all --force --volumes --filter until=336h || true 116 | echo "Disk space after prune:" 117 | df -h 118 | 119 | - name: Checkout repo 120 | uses: actions/checkout@v4 121 | 122 | - name: Clean unrelated images 123 | run: | 124 | podman rmi --ignore --force ${REPO}:${WKDEV_SDK_TAG} 125 | podman manifest rm ${REPO}:${WKDEV_SDK_TAG} || true 126 | podman rmi --ignore --force ${REPO}:${WKDEV_SDK_TAG}_arm64 127 | 128 | - name: Build image 129 | run: | 130 | podman build --jobs $(nproc) --build-context scripts=./scripts -t ${REPO}:${WKDEV_SDK_TAG}_arm64 --arch=arm64 images/wkdev_sdk 131 | podman image list 132 | 133 | - name: Test image 134 | run: | 135 | CONTAINER="wkdev-$(date +%s)" 136 | echo "CONTAINER=${CONTAINER}" >> "${GITHUB_ENV}" 137 | source ./register-sdk-on-host.sh 138 | wkdev-create --create-home --home ${HOME}/${CONTAINER}-home --verbose --attach --no-pull --name ${CONTAINER} --arch arm64 --shell /bin/bash 139 | wkdev-enter -n ${CONTAINER} --exec -- git clone --depth=1 https://github.com/WebKit/WebKit.git 140 | wkdev-enter -n ${CONTAINER} --exec -- ./WebKit/Tools/Scripts/build-webkit --wpe --release --generate-project-only 141 | wkdev-enter -n ${CONTAINER} --exec -- ./WebKit/Tools/Scripts/build-webkit --gtk --release --generate-project-only 142 | 143 | - name: Push image 144 | if: github.ref_name == 'main' || startsWith(github.ref_name, 'tag/') 145 | run: | 146 | echo "${{ secrets.GITHUB_TOKEN }}" | podman login ghcr.io --username=${GITHUB_REPOSITORY_OWNER} --password-stdin 147 | podman push ${REPO}:${WKDEV_SDK_TAG}_arm64 148 | 149 | - name: Cleanup test artifacts 150 | if: always() 151 | run: | 152 | # Remove the specific container created by this test run 153 | if [ -n "${CONTAINER:-}" ]; then 154 | podman rm --force ${CONTAINER} 2>/dev/null || true 155 | rm -rf ${HOME}/${CONTAINER}-home || true 156 | fi 157 | # Clean up any leftover containers and home directories from previous failed runs 158 | podman ps -a --filter "name=^wkdev-" --format "{{.Names}}" | xargs -r podman rm --force 2>/dev/null || true 159 | find ${HOME} -maxdepth 1 -type d -name "wkdev-*-home" -exec rm -rf {} + 2>/dev/null || true 160 | 161 | deploy: 162 | runs-on: [self-hosted, x64] 163 | permissions: 164 | packages: write 165 | needs: [build_amd64, build_arm64] 166 | if: | 167 | (github.event_name != 'create' || startsWith(github.ref_name, 'tag/')) && 168 | (github.ref_name == 'main' || startsWith(github.ref_name, 'tag/')) 169 | steps: 170 | - name: Set tag name 171 | run: | 172 | if [ "${GITHUB_REF_NAME}" = 'main' ]; then 173 | echo "WKDEV_SDK_TAG=latest" >> "${GITHUB_ENV}" 174 | elif [[ "${GITHUB_REF_NAME}" =~ ^tag/(.+)$ ]]; then 175 | echo "WKDEV_SDK_TAG=${BASH_REMATCH[1]}" >> "${GITHUB_ENV}" 176 | else 177 | echo "WKDEV_SDK_TAG=latest" >> "${GITHUB_ENV}" 178 | fi 179 | echo "WKDEV_SDK_CONTAINER_REGISTRY_USER_NAME=$(echo ${GITHUB_REPOSITORY_OWNER} | tr '[:upper:]' '[:lower:]')" >> "${GITHUB_ENV}" 180 | echo "REPO=ghcr.io/$(echo ${GITHUB_REPOSITORY_OWNER} | tr '[:upper:]' '[:lower:]')/wkdev-sdk" >> "${GITHUB_ENV}" 181 | 182 | - name: Install podman and skopeo 183 | run: sudo apt-get update && sudo apt-get -y install podman fuse-overlayfs skopeo 184 | 185 | - name: Prune old containers/images 186 | run: | 187 | echo "Disk space before prune:" 188 | df -h 189 | echo "Pruning containers and images older than 2 weeks..." 190 | podman system prune --all --force --volumes --filter until=336h || true 191 | echo "Disk space after prune:" 192 | df -h 193 | 194 | - name: Checkout repo 195 | uses: actions/checkout@v4 196 | 197 | - name: Set registry auth file 198 | run: echo "REGISTRY_AUTH_FILE=path" >> $GITHUB_ENV 199 | 200 | - name: Login to registry 201 | run: echo "${{ secrets.GITHUB_TOKEN }}" | skopeo login ghcr.io --username=${GITHUB_REPOSITORY_OWNER} --password-stdin 202 | 203 | - name: Validate all architecture images exist in registry 204 | id: validate 205 | run: | 206 | VALIDATION_FAILED=0 207 | for arch in amd64 arm64 arm; do 208 | if ! skopeo inspect docker://${REPO}:${WKDEV_SDK_TAG}_${arch} > /dev/null 2>&1; then 209 | echo "Error: Image ${REPO}:${WKDEV_SDK_TAG}_${arch} does not exist in registry" 210 | VALIDATION_FAILED=1 211 | else 212 | echo "Validated: ${REPO}:${WKDEV_SDK_TAG}_${arch} exists in registry" 213 | fi 214 | done 215 | if [ $VALIDATION_FAILED -eq 1 ]; then 216 | echo "validation_failed=true" >> $GITHUB_OUTPUT 217 | exit 1 218 | fi 219 | echo "validation_failed=false" >> $GITHUB_OUTPUT 220 | 221 | - name: Clean up arch-specific images on validation failure (amd64) 222 | if: failure() && steps.validate.outputs.validation_failed == 'true' 223 | continue-on-error: true 224 | uses: bots-house/ghcr-delete-image-action@v1.1.0 225 | with: 226 | owner: ${{ github.repository_owner }} 227 | name: wkdev-sdk 228 | token: ${{ secrets.GITHUB_TOKEN }} 229 | tag: ${{ env.WKDEV_SDK_TAG }}_amd64 230 | 231 | - name: Clean up arch-specific images on validation failure (arm64) 232 | if: failure() && steps.validate.outputs.validation_failed == 'true' 233 | continue-on-error: true 234 | uses: bots-house/ghcr-delete-image-action@v1.1.0 235 | with: 236 | owner: ${{ github.repository_owner }} 237 | name: wkdev-sdk 238 | token: ${{ secrets.GITHUB_TOKEN }} 239 | tag: ${{ env.WKDEV_SDK_TAG }}_arm64 240 | 241 | - name: Pull arch-specific images to local storage 242 | run: | 243 | podman pull ${REPO}:${WKDEV_SDK_TAG}_amd64 244 | podman pull ${REPO}:${WKDEV_SDK_TAG}_arm64 245 | 246 | - name: Delete arch-specific tag (amd64) 247 | uses: bots-house/ghcr-delete-image-action@v1.1.0 248 | with: 249 | owner: ${{ github.repository_owner }} 250 | name: wkdev-sdk 251 | token: ${{ secrets.GITHUB_TOKEN }} 252 | tag: ${{ env.WKDEV_SDK_TAG }}_amd64 253 | 254 | - name: Delete arch-specific tag (arm64) 255 | uses: bots-house/ghcr-delete-image-action@v1.1.0 256 | with: 257 | owner: ${{ github.repository_owner }} 258 | name: wkdev-sdk 259 | token: ${{ secrets.GITHUB_TOKEN }} 260 | tag: ${{ env.WKDEV_SDK_TAG }}_arm64 261 | 262 | - name: Create multi-arch manifest from local storage 263 | run: | 264 | podman manifest create ${REPO}:${WKDEV_SDK_TAG} 265 | podman manifest add ${REPO}:${WKDEV_SDK_TAG} containers-storage:${REPO}:${WKDEV_SDK_TAG}_amd64 266 | podman manifest add ${REPO}:${WKDEV_SDK_TAG} containers-storage:${REPO}:${WKDEV_SDK_TAG}_arm64 267 | 268 | - name: Push self-contained multi-arch manifest with all layers 269 | run: | 270 | podman manifest push --all ${REPO}:${WKDEV_SDK_TAG} docker://${REPO}:${WKDEV_SDK_TAG} 271 | -------------------------------------------------------------------------------- /scripts/host-only/wkdev-create: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2024 Igalia S.L. 3 | # SPDX-License: MIT 4 | 5 | [ -f "${WKDEV_SDK}/.wkdev-sdk-root" ] && source "${WKDEV_SDK}/utilities/application.sh" || { echo "Please set \${WKDEV_SDK} to point to the root of the wkdev-sdk checkout."; exit 1; } 6 | init_application "${0}" "Create a new container ready to use for WebKit GTK/WPE development." host-only 7 | 8 | # Source utility script fragments 9 | source "${WKDEV_SDK}/utilities/cpu-profiling.sh" 10 | source "${WKDEV_SDK}/utilities/host-setup-tasks.sh" 11 | source "${WKDEV_SDK}/utilities/nvidia-gpu.sh" 12 | source "${WKDEV_SDK}/utilities/podman.sh" 13 | 14 | argsparse_use_option debug "Enable debug logging for podman (useful to debug container issues)" 15 | argsparse_use_option trace "Enable 'xtrace' mode for this script" 16 | argsparse_use_option =verbose "Increase verbosity of this script" 17 | 18 | argsparse_use_option =user: "Container account user name" type:username default:$(id --user --name) 19 | argsparse_use_option =group: "Container account group name" type:group default:$(id --group --name) 20 | argsparse_use_option =shell: "Specific shell to use for interactive container usage" type:file default:${SHELL} 21 | argsparse_use_option =create-home "Create home directory and add necessary configuration files (shell settings, etc.)" 22 | argsparse_use_option shared-dir: "Path to existing or to-be-created shared directory that will map to the same path on the host" 23 | argsparse_use_option h=ome: "Path to existing or to-be-created container home directory" default:${HOME}/wkdev-home 24 | argsparse_use_option =name: "Name of container" default:wkdev 25 | argsparse_use_option rm "Force removal of container if it already exists." 26 | argsparse_use_option attach "Attach to container as it starts up." 27 | argsparse_use_option no-pull "Do not login or pull images." 28 | argsparse_use_option list-tags "List available image tags." 29 | argsparse_use_option =tag: "Create the container using a specific tag, see-also --list-tags." default:$(get_default_container_tag) 30 | argsparse_use_option =arch: "Container architecture." 31 | 32 | argsparse_usage_description="$(cat <> 34 | 35 | Creates & starts a new podman container that runs the '$(get_sdk_image_name)' image with approriate settings. 36 | 37 | The Podman containers run *unprivileged* and *rootless* -- as hardened as possible, while still 38 | offering tight integration with the host. The host system shares the systemd, d-bus, X11, wayland, 39 | PulseAudio, ... sockets via bind-mounts with the container, allowing for smooth integration of 40 | applications started from within the container with the host users' running desktop session. 41 | 42 | << Examples >> 43 | 44 | $ ${application_name} --create-home --home \${HOME}/wkdev-home --name wkdev 45 | $ ${application_name} --home \${HOME}/wkdev-home --trace --name wkdev2 46 | $ ${application_name} --home \${HOME}/wkdev-home --debug --shell /usr/bin/bash --name wkdev3 47 | $ ${application_name} --home \${HOME}/wkdev-home --shared-dir \${HOME}/wkdev-shared 48 | EOF 49 | )" 50 | 51 | # Hardcoded settings 52 | get_settings_table_first_column_size() { echo 40; } 53 | get_settings_table_second_column_size() { echo 40; } 54 | get_settings_table_print_prefix() { echo " "; } 55 | 56 | process_command_line_arguments() { 57 | 58 | argsparse_allow_no_argument "true" 59 | argsparse_parse_options "${@}" 60 | argsparse_is_option_set "trace" && set -o xtrace 61 | 62 | if argsparse_is_option_set "list-tags"; then 63 | _log_ "" 64 | run_podman search "$(get_sdk_qualified_name)" --list-tags --format='{{ .Tag }}' 65 | exit 0 66 | fi 67 | 68 | container_tag="${program_options["tag"]}" 69 | 70 | container_shell="${program_options["shell"]}" 71 | container_name="${program_options["name"]}" 72 | container_user_home="${program_options["home"]}" 73 | 74 | container_user_name="${program_options["user"]}" 75 | container_user_id=$(id --user --real "${container_user_name}") 76 | 77 | host_user_name=$(id --user --name) 78 | host_user_id=$(id --user --real) 79 | 80 | host_group_name=$(id --group --name) 81 | host_group_id=$(id --group --real) 82 | 83 | container_group_name="${program_options["group"]}" 84 | container_group_id=$(id --group --real "${container_group_name}") 85 | 86 | host_hostname="$(hostnamectl hostname)" 87 | container_hostname="$(basename "${container_name}").${host_hostname}" 88 | 89 | # Verify the container home directory is accessible. 90 | if [ ! -d "${container_user_home}" ]; then 91 | if ! argsparse_is_option_set "create-home"; then 92 | _abort_ "The passed home directory '${container_user_home}' does not exist (pass --create-home, if you want to automatically create them)" 93 | else 94 | _log_ "" 95 | _log_ "-> The passed home directory '${container_user_home}' does not exist, creating..." 96 | _log_ "" 97 | 98 | # Copy shell configuration skeleton files from host. 99 | cp --recursive --verbose /etc/skel "${container_user_home}" 100 | 101 | # Set ownership / permissions 102 | chown --recursive ${container_user_id}:${container_group_id} "${container_user_home}" 103 | chmod 750 "${container_user_home}" 104 | 105 | default_config_directory="$(get_container_home_defaults_directory_name)" 106 | shell_type=$(basename "${SHELL}") 107 | mkdir --parents "${container_user_home}"/.config 108 | if [ "${shell_type}" = "bash" ]; then 109 | cp --verbose "${default_config_directory}"/dot-bash_login "${container_user_home}"/.bash_login 110 | cp --verbose "${default_config_directory}"/dot-bash_profile "${container_user_home}"/.bash_profile 111 | elif [ "${shell_type}" = "zsh" ]; then 112 | cp --verbose "${default_config_directory}"/dot-zlogin "${container_user_home}"/.zlogin 113 | cp --verbose "${default_config_directory}"/dot-zprofile "${container_user_home}"/.zprofile 114 | cp --verbose "${default_config_directory}"/dot-zshrc "${container_user_home}"/.zshrc 115 | elif [ "${shell_type}" = "fish" ]; then 116 | mkdir "${container_user_home}"/.config/fish 117 | cp --verbose "${default_config_directory}"/fish-config "${container_user_home}"/.config/fish/config.fish 118 | else 119 | _log_ "" 120 | _log_ "-> Shell '${shell_type}' auto configuration is unsupported. Please setup the configuration files for your shell " \ 121 | "on your own (see $(get_container_home_defaults_directory_name)/dot-* to examine the default settings for other shells." 122 | fi 123 | 124 | cp --verbose "${default_config_directory}"/dot-gdbinit "${container_user_home}"/.gdbinit 125 | 126 | _log_ "" 127 | fi 128 | else 129 | _log_ "" 130 | _log_ "-> The passed home directory '${container_user_home}' already exists. Skipping configuration." 131 | _log_ "" 132 | fi 133 | 134 | # Verify we should setup a shared folder and it's accessible 135 | if argsparse_is_option_set "shared-dir"; then 136 | container_shared_folder="${program_options["shared-dir"]}" 137 | # Normalize relative paths so we can bind mount them 138 | if [ "${container_shared_folder#/*}" == "$container_shared_folder" ]; then 139 | container_shared_folder=$(realpath "$container_shared_folder") 140 | fi 141 | 142 | if [ ! -d "${container_shared_folder}" ]; then 143 | _log_ "-> Shared folder '${container_shared_folder}' does not exist, creating." 144 | _log_ "" 145 | mkdir --parents "${container_shared_folder}" 146 | else 147 | _log_ "-> Setting up '${container_shared_folder}' as shared folder between host and container" 148 | _log_ "" 149 | fi 150 | else 151 | _log_ "-> Not setting shared folder." 152 | _log_ "" 153 | fi 154 | # Stop & remove existing container if '--rm' is given. 155 | if argsparse_is_option_set "rm"; then 156 | run_podman_silent stop "${container_name}" 157 | run_podman_silent rm "${container_name}" 158 | fi 159 | } 160 | 161 | try_process() { 162 | 163 | local -n arguments=${1} 164 | local operation="${2}" 165 | local test_condition=${3} 166 | shift 3 167 | 168 | local -a key_value_pair=(${@}) 169 | if [ ${test_condition} -eq 1 ]; then 170 | _log_ " [x] ${operation}" 171 | arguments+=(${key_value_pair[@]}) 172 | else 173 | _log_ " [ ] ${operation}" 174 | fi 175 | } 176 | 177 | try_process_user() { 178 | if [[ ${EUID} == 0 ]]; then 179 | return 180 | fi 181 | 182 | # Map host UID/GIDs into container user namespace, unmodified! 183 | # The 'keep-id' mode for user namespaces is mandatory, when the container shall 184 | # be able to communicate with the host dbus session, while staying unprivileged. 185 | # Reference: https://github.com/containers/podman/discussions/16772 186 | local podman_argument=("--userns" "keep-id") 187 | 188 | # root inside the container is mapped to the current host user. We need root access 189 | # only for the initial bootstrapping (executing wkdev-init). 190 | podman_argument+=("--user" "root:root") 191 | 192 | try_process ${1} "Map host user UID/GID unmodified into user namespace" 1 ${podman_argument[@]} 193 | } 194 | 195 | try_process_groups() { 196 | 197 | # Map secondary GIDS into container user namespace as well. 198 | local podman_argument=("--group-add" "keep-groups") 199 | try_process ${1} "Map host user secondary GIDs into user namespace" 1 ${podman_argument[@]} 200 | } 201 | 202 | try_process_home_directory() { 203 | 204 | # Map given home directory path as container user home directory (eventually separated from host home directory). 205 | local podman_argument=("--env" "HOST_HOME=/host/home/${container_user_name}") 206 | podman_argument+=("--env" "HOST_CONTAINER_HOME_PATH=${container_user_home}") 207 | podman_argument+=("--mount" "type=bind,source=${container_user_home},destination=/home/${container_user_name},rslave") 208 | podman_argument+=("--mount" "type=bind,source=${HOME},destination=/host/home/${container_user_name},rslave") 209 | 210 | # systemctl --user is-enabled relies on the accessibility of ~/.config/systemd/user, map it into the container if available. 211 | [ -d "${HOME}/.config/systemd/user" ] && podman_argument+=("--mount" "type=bind,source=${HOME}/.config/systemd/user,destination=/home/${container_user_name}/.config/systemd/user,rslave") 212 | 213 | try_process ${1} "Expose both host & container home directory" 1 ${podman_argument[@]} 214 | } 215 | 216 | try_process_shared_directory() { 217 | local shared_directory_wanted=$(argsparse_is_option_set "shared-dir" && echo 1 || echo 0) 218 | local podman_argument=("--mount" "type=bind,source=${container_shared_folder},destination=${container_shared_folder},rslave") 219 | try_process ${1} "Expose common shared directory (optional)" ${shared_directory_wanted} ${podman_argument[@]} 220 | } 221 | 222 | try_process_coredump_directory() { 223 | 224 | # coredumpctl needs to share the directory when the systemd service stores the coredumps 225 | local coredump_directory="/var/lib/systemd/coredump" 226 | local podman_argument=("--mount" "type=bind,source=${coredump_directory},destination=${coredump_directory},rslave") 227 | local coredump_directory_exists=$([ -d "${coredump_directory}" ] && echo 1 || echo 0) 228 | try_process ${1} "Expose systemd-coredump directory in case it is present" ${coredump_directory_exists} ${podman_argument[@]} 229 | } 230 | 231 | try_process_timezone() { 232 | 233 | local podman_argument=("--tz" "local") 234 | try_process ${1} "Share host timezone settings with container" 1 ${podman_argument[@]} 235 | } 236 | 237 | try_process_ulimit() { 238 | 239 | local podman_argument=("--ulimit" "host") 240 | try_process ${1} "Share host ulimit settings with container" 1 ${podman_argument[@]} 241 | } 242 | 243 | try_process_pids_limit() { 244 | 245 | local podman_argument=("--pids-limit" "-1") 246 | try_process ${1} "Set unlimited pids for container" 1 ${podman_argument[@]} 247 | } 248 | 249 | try_process_journal() { 250 | 251 | local journal_log_directory="/var/log/journal" 252 | local journal_log_directory_exists=$([ -d "${journal_log_directory}" ] && echo 1 || echo 0) 253 | local podman_argument=("--mount" "type=bind,source=${journal_log_directory},destination=${journal_log_directory},ro,rslave") 254 | try_process ${1} "Share system journal log directory with container (read-only)" ${journal_log_directory_exists} ${podman_argument[@]} 255 | 256 | local journal_run_directory="/run/systemd/journal" 257 | local journal_run_directory_exists=$([ -d "${journal_run_directory}" ] && echo 1 || echo 0) 258 | local podman_argument=("--mount" "type=bind,source=${journal_run_directory},destination=${journal_run_directory},ro,rslave") 259 | try_process ${1} "Share system journal run directory with container (read-only)" ${journal_run_directory_exists} ${podman_argument[@]} 260 | } 261 | 262 | try_process_keyring() { 263 | 264 | local keyring_directory="${XDG_RUNTIME_DIR}/keyring" 265 | 266 | # Respect $SSH_AUTH_SOCK (but strip the "unix:path=" prefix) 267 | if [ -v SSH_AUTH_SOCK ]; then 268 | keyring_directory=$(dirname "${SSH_AUTH_SOCK##unix:path=}") 269 | fi 270 | 271 | local keyring_directory_exists=$([ -d "${keyring_directory}" ] && echo 1 || echo 0) 272 | local podman_argument=("--mount" "type=bind,source=${keyring_directory},destination=${keyring_directory},rslave") 273 | podman_argument+=("--env" "SSH_AUTH_SOCK=${keyring_directory}/ssh") 274 | try_process ${1} "Share keyring with container" ${keyring_directory_exists} ${podman_argument[@]} 275 | } 276 | 277 | try_process_system_bus() { 278 | 279 | local dbus_system_socket="/run/dbus/system_bus_socket" 280 | 281 | # Respect $DBUS_SYSTEM_BUS_ADDRESS (but strip the "unix:path=" prefix) 282 | if [ -v DBUS_SYSTEM_BUS_ADDRESS ]; then 283 | dbus_system_socket="${DBUS_SYSTEM_BUS_ADDRESS##unix:path=}" 284 | fi 285 | 286 | local dbus_system_socket_exists=$([ -S "${dbus_system_socket}" ] && echo 1 || echo 0) 287 | local podman_argument=("--mount" "type=bind,source=${dbus_system_socket},destination=${dbus_system_socket},rslave") 288 | try_process ${1} "Share system bus with container" ${dbus_system_socket_exists} ${podman_argument[@]} 289 | } 290 | 291 | try_process_session_bus() { 292 | 293 | local dbus_user_socket="${XDG_RUNTIME_DIR}/bus" 294 | 295 | # Respect $DBUS_SESSION_BUS_ADDRESS (but strip the "unix:path=" prefix) 296 | if [ -v DBUS_SESSION_BUS_ADDRESS ]; then 297 | dbus_user_socket="${DBUS_SESSION_BUS_ADDRESS##unix:path=}" 298 | fi 299 | 300 | local dbus_user_socket_exists=$([ -S "${dbus_user_socket}" ] && echo 1 || echo 0) 301 | local podman_argument=("--mount" "type=bind,source=${dbus_user_socket},destination=${dbus_user_socket},rslave") 302 | podman_argument+=("--env" "DBUS_SESSION_BUS_ADDRESS=unix:path=${dbus_user_socket}") 303 | try_process ${1} "Share session bus with container" ${dbus_user_socket_exists} ${podman_argument[@]} 304 | } 305 | 306 | try_process_dconf() { 307 | 308 | local dconf_directory="${XDG_CONFIG_HOME-"${HOME}/.config"}/dconf" 309 | local dconf_directory_exists=$([ -d "${dconf_directory}" ] && echo 1 || echo 0) 310 | local podman_argument=("--mount" "type=bind,source=${dconf_directory},destination=${dconf_directory},rslave") 311 | try_process ${1} "Share dconf settings with container" ${dconf_directory_exists} ${podman_argument[@]} 312 | } 313 | 314 | try_process_accessibility() { 315 | 316 | local at_spi_directory="${XDG_RUNTIME_DIR}/at-spi" 317 | 318 | # Respect $AT_SPI_BUS_ADDRESS (but strip the "unix:path=" prefix) 319 | if [ -v AT_SPI_BUS_ADDRESS ]; then 320 | at_spi_directory="${AT_SPI_BUS_ADDRESS##unix:path=}" 321 | fi 322 | 323 | local at_spi_directory_exists=$([ -d "${at_spi_directory}" ] && echo 1 || echo 0) 324 | local podman_argument=("--mount" "type=bind,source=${at_spi_directory},destination=${at_spi_directory},rslave") 325 | try_process ${1} "Share accessibility bus with container" ${at_spi_directory_exists} ${podman_argument[@]} 326 | } 327 | 328 | try_process_themes() { 329 | 330 | local themes_directory_exists=$([ -d "/usr/share/themes" ] && echo 1 || echo 0) 331 | local podman_argument=("--mount" "type=bind,source=/usr/share/themes,destination=/usr/local/share/themes,rslave") 332 | try_process ${1} "Share host themes with container" ${themes_directory_exists} ${podman_argument[@]} 333 | } 334 | 335 | try_process_icons() { 336 | 337 | local icons_directory_exists=$([ -d "/usr/share/icons" ] && echo 1 || echo 0) 338 | local podman_argument=("--mount" "type=bind,source=/usr/share/icons,destination=/usr/local/share/icons,rslave") 339 | try_process ${1} "Share host icons with container" ${icons_directory_exists} ${podman_argument[@]} 340 | } 341 | 342 | try_process_fonts() { 343 | 344 | local fonts_directory_exists=$([ -d "/usr/share/fonts" ] && echo 1 || echo 0) 345 | local podman_argument=("--mount" "type=bind,source=/usr/share/fonts,destination=/usr/local/share/fonts,rslave") 346 | try_process ${1} "Share host fonts with container" ${fonts_directory_exists} ${podman_argument[@]} 347 | } 348 | 349 | try_process_dri() { 350 | 351 | local is_dri_device_available=$([ -d "/dev/dri" ] && echo 1 || echo 0) 352 | local podman_argument=("--device" "/dev/dri") 353 | try_process ${1} "Access to host DRI devices" ${is_dri_device_available} ${podman_argument[@]} 354 | } 355 | 356 | try_process_nvidia_gpu() { 357 | 358 | local is_nvidia_gpu_available=$(is_nvidia_gpu_installed && [ -f "$(get_nvidia_cdi_config_file)" ] && echo 1 || echo 0) 359 | local podman_argument=("--device" "nvidia.com/gpu=all") 360 | if [[ "${is_nvidia_gpu_available}" -eq 1 ]]; then 361 | if nvidia-ctk --version | head -n1 | grep -qE 'version 1\.1?[0-4]\.[[:digit:]]+$'; then 362 | _abort_ "Broken version of nvidia-ctk detected. Please install nvidia-container-toolkit>=1.15. See: https://github.com/Igalia/webkit-container-sdk/issues/72" 363 | fi 364 | fi 365 | try_process ${1} "Access to NVIDIA GPU on host" ${is_nvidia_gpu_available} ${podman_argument[@]} 366 | } 367 | 368 | try_process_x11() { 369 | 370 | local x11_directory_exists=$([ -d "/tmp/.X11-unix" ] && echo 1 || echo 0) 371 | podman_argument+=("--mount" "type=bind,source=/tmp/.X11-unix,destination=/tmp/.X11-unix,rslave") 372 | try_process ${1} "Access to X11 on host" ${x11_directory_exists} ${podman_argument[@]} 373 | } 374 | 375 | try_process_pulseaudio() { 376 | 377 | local pulseaudio_directory="${XDG_RUNTIME_DIR}/pulse" 378 | 379 | # Respect $PULSE_SERVER (but strip the "unix:path=" prefix) 380 | if [ -v PULSE_SERVER ]; then 381 | pulseaudio_directory="${PULSE_SERVER##unix:path=}" 382 | fi 383 | 384 | local pulseaudio_directory_exists=$([ -d "${pulseaudio_directory}" ] && echo 1 || echo 0) 385 | local podman_argument=("--mount" "type=bind,source=${pulseaudio_directory},destination=${pulseaudio_directory},rslave") 386 | try_process ${1} "Access to PulseAudio on host" ${pulseaudio_directory_exists} ${podman_argument[@]} 387 | } 388 | 389 | try_process_podman() { 390 | 391 | local podman_socket="${XDG_RUNTIME_DIR}/podman/podman.sock" 392 | local podman_socket_exists=$([ -S "${podman_socket}" ] && echo 1 || echo 0) 393 | local podman_argument=("--env" "HOST_PODMAN_SOCKET=${podman_socket}.host") 394 | podman_argument+=("--mount" "type=bind,source=${podman_socket},destination=${podman_socket}.host,rslave") 395 | try_process ${1} "Access to Podman on host" ${podman_socket_exists} ${podman_argument[@]} 396 | } 397 | 398 | build_podman_create_arguments() { 399 | 400 | local -n arguments=${1} 401 | 402 | # NOTE: Extra capabilities are needed to run podman in podman. We don't need it, 403 | # since we command the host podman to run extra containers for us, when needed. 404 | 405 | # Map our SDK git repository into the container. 406 | arguments+=("--mount" "type=bind,source=${WKDEV_SDK},destination=/wkdev-sdk,rslave") 407 | 408 | # Add 'SYS_PTRACE' support, to be able to use gdb. 409 | arguments+=("--cap-add=SYS_PTRACE") 410 | 411 | # Add 'NET_RAW' support, to be able to use ping 412 | arguments+=("--cap-add=NET_RAW") 413 | 414 | # Add 'SYS_ADMIN' support, to be able to use CPU profiiling. 415 | arguments+=("--cap-add=SYS_ADMIN") 416 | 417 | # Set container name & hostname & workdir. 418 | arguments+=("--name" "${container_name}") 419 | arguments+=("--hostname" "${container_hostname}") 420 | arguments+=("--workdir" "/home/${container_user_name}") 421 | 422 | # Share pid namepace with host -- otherwise 'systemctl --user' won't work. 423 | # 424 | # Long story: 425 | # PID 1 is assumed to be the systemd "system session" and systemctl communicates 426 | # with PID 1 via dbus. Therefore one either has to provide a session within the 427 | # container (use systemd init mechanism) or share the PID namespace with the host, 428 | # and let 'systemctl --user' communicate with the host PID 1 (require systemd host). 429 | arguments+=("--pid" "host") 430 | 431 | # Share IPC namepace with host -- otherwise /dev/shm access (e.g. in glxgears) won't work. 432 | arguments+=("--ipc" "host") 433 | 434 | # Share network namepace with host. 435 | arguments+=("--network" "host") 436 | 437 | # Map /etc/{hosts|localtime|resolv.conf|machine-id} into container. 438 | # Note that /etc/hosts is copied into the container in wkdev-init. 439 | arguments+=("--mount" "type=bind,source=/etc/hosts,destination=/host/etc/hosts,ro") 440 | arguments+=("--mount" "type=bind,source=/etc/localtime,destination=/etc/localtime,ro") 441 | arguments+=("--mount" "type=bind,source=/etc/resolv.conf,destination=/etc/resolv.conf,ro") 442 | arguments+=("--mount" "type=bind,source=/etc/machine-id,destination=/etc/machine-id,ro,rslave") 443 | 444 | # Mount /dev/pts in container (pseudo-terminal support) only if not running inside a LXC container. 445 | [ "$(systemd-detect-virt)" != "lxc" ] && arguments+=("--mount" "type=devpts,destination=/dev/pts") 446 | 447 | # Mount /dev and /run/udev for devices like gamepads 448 | arguments+=("-v" "/dev/:/dev:rslave") 449 | [ -d "/run/udev" ] && arguments+=("-v" "/run/udev:/run/udev") 450 | 451 | # Mount a tmpfs. 452 | arguments+=("--tmpfs" "/tmp") 453 | 454 | # Mount the host runtime directory, used during wkdev-sync-runtime-state 455 | arguments+=("--mount" "type=bind,source=${XDG_RUNTIME_DIR},destination=/host/run,bind-propagation=rslave") 456 | 457 | # Disable SELinux isolation. 458 | arguments+=("--security-opt" "label=disable") 459 | 460 | # Allow for unprivileged user namespaces (bwrap) to work. 461 | arguments+=("--security-opt" "unmask=ALL") 462 | 463 | # Required for rr to work. 464 | arguments+=("--security-opt" "seccomp=unconfined") 465 | 466 | # Always set XDG_RUNTIME_DIR to the same value. 467 | arguments+=("--env" "XDG_RUNTIME_DIR=/run/user/${host_user_id}") 468 | 469 | if argsparse_is_option_set "no-pull"; then 470 | arguments+=("--pull=never") 471 | else 472 | arguments+=("--pull=newer") 473 | fi 474 | 475 | if argsparse_is_option_set "arch"; then 476 | container_arch="${program_options["arch"]}" 477 | echo "Overriding container architecture: ${container_arch}" 478 | arguments+=("--arch=${container_arch}") 479 | 480 | if argsparse_is_option_set "no-pull"; then 481 | if ! podman image exists "$(get_sdk_qualified_name):${container_tag}"; then 482 | echo "Image $(get_sdk_qualified_name):${container_tag} does not exist, trying arch-specific version." 483 | container_tag="${container_tag}_${container_arch}" 484 | fi 485 | fi 486 | fi 487 | 488 | if argsparse_is_option_set "no-pull"; then 489 | if ! podman image exists "$(get_sdk_qualified_name):${container_tag}"; then 490 | echo "Image $(get_sdk_qualified_name):${container_tag} does not exist." 491 | exit 1 492 | fi 493 | fi 494 | echo "Using image $(get_sdk_qualified_name):${container_tag}." 495 | 496 | set +o nounset 497 | try_process_user ${1} 498 | try_process_groups ${1} 499 | try_process_home_directory ${1} 500 | try_process_shared_directory ${1} 501 | try_process_coredump_directory ${1} 502 | try_process_timezone ${1} 503 | try_process_ulimit ${1} 504 | try_process_pids_limit ${1} 505 | try_process_journal ${1} 506 | try_process_keyring ${1} 507 | try_process_system_bus ${1} 508 | try_process_session_bus ${1} 509 | try_process_dconf ${1} 510 | try_process_accessibility ${1} 511 | try_process_themes ${1} 512 | try_process_icons ${1} 513 | try_process_fonts ${1} 514 | try_process_dri ${1} 515 | # We need to use software rendering anyway in this case, but on (e.g. armv7) 516 | # the nvidia packages are not available. 517 | if ! argsparse_is_option_set "arch"; then 518 | try_process_nvidia_gpu ${1} 519 | fi 520 | try_process_x11 ${1} 521 | try_process_pulseaudio ${1} 522 | try_process_podman ${1} 523 | set -o nounset 524 | 525 | arguments+=("$(get_sdk_qualified_name):${container_tag}") 526 | 527 | # Entry point 528 | arguments+=("/wkdev-sdk/scripts/container-only/.wkdev-init") 529 | arguments+=("--shell" "${container_shell}") 530 | arguments+=("--user" "${container_user_name}") 531 | arguments+=("--group" "${container_group_name}") 532 | 533 | if argsparse_is_option_set "attach"; then 534 | arguments+=("--exit-when-done") 535 | fi 536 | } 537 | 538 | build_podman_arguments() { 539 | 540 | local -n generic_arguments=${1} 541 | 542 | argsparse_is_option_set "debug" && generic_arguments+=("--log-level debug") 543 | } 544 | 545 | # Pretty printing tables 546 | print_table_header_border() { 547 | 548 | printf "+%$(expr $(get_settings_table_first_column_size) + 2)s+%$(expr $(get_settings_table_second_column_size) + 2)s+" | tr " " "-" 549 | } 550 | 551 | print_table_header() { 552 | 553 | local column_title_1="${1}" 554 | local column_title_2="${2}" 555 | printf "| %-$(get_settings_table_first_column_size)s | %-$(get_settings_table_second_column_size)s |\n" "${column_title_1}" "${column_title_2}" 556 | } 557 | 558 | print_table_row() { 559 | 560 | local key="${1}" 561 | local value="${2}" 562 | printf "| %-$(get_settings_table_first_column_size)s | %-$(get_settings_table_second_column_size)s |\n" "${key}" "${value}" 563 | } 564 | 565 | print_table() { 566 | 567 | printf "%s%s\n" "$(get_settings_table_print_prefix)" "${1}" 568 | } 569 | 570 | print_host_settings() { 571 | 572 | _log_ "" 573 | _log_ " Host settings:" 574 | _log_ "" 575 | 576 | print_table "$(print_table_header_border)" 577 | print_table "$(print_table_header "Key" "Value")" 578 | print_table "$(print_table_header_border)" 579 | print_table "$(print_table_row "Hostname" "${host_hostname}")" 580 | print_table "$(print_table_row "User name" "$(printf "%s %s" "${host_user_name}" "(UID ${host_user_id})")")" 581 | print_table "$(print_table_row "Group name" "$(printf "%s %s" "${host_group_name}" "(GID ${host_group_id})")")" 582 | print_table "$(print_table_row "\${XDG_RUNTIME_DIR}" "${XDG_RUNTIME_DIR-}")" 583 | print_table "$(print_table_row "$(get_perf_event_procfs_path)" "$(read_kernel_parameter "$(get_perf_event_kernel_setting)")")" 584 | 585 | # Do not attempt to validate the /etc/sub?id files for printing purposes, assume no duplicates are present and 586 | # an entry is present for the current UID or the current user name, but not both. 587 | subuid_settings="MISSING!" 588 | subuid_name_entries=$(cat /etc/subuid | grep "${host_user_name}") 589 | if [ ! -z "${subuid_name_entries}" ]; then 590 | subuid_settings=$(echo "${subuid_name_entries}" | awk -F':' '{ print $3 " UIDs available, first: " $2 }') 591 | else 592 | subuid_id_entries=$(cat /etc/subuid | grep "${host_user_id}") 593 | subuid_settings=$(echo "${subuid_id_entries}" | awk -F':' '{ print $3 " UIDs available, first: " $2 }') 594 | fi 595 | 596 | subgid_settings="MISSING!" 597 | subgid_name_entries=$(cat /etc/subgid | grep "${host_group_name}") 598 | if [ ! -z "${subgid_name_entries}" ]; then 599 | subgid_settings=$(echo "${subgid_name_entries}" | awk -F':' '{ print $3 " GIDs available, first: " $2 }') 600 | else 601 | subgid_id_entries=$(cat /etc/subgid | grep "${host_group_id}") 602 | subgid_settings=$(echo "${subgid_id_entries}" | awk -F':' '{ print $3 " GIDs available, first: " $2 }') 603 | fi 604 | 605 | print_table "$(print_table_row "/etc/subuid" "${subuid_settings}")" 606 | print_table "$(print_table_row "/etc/subgid" "${subgid_settings}")" 607 | print_table "$(print_table_header_border)" 608 | } 609 | 610 | print_container_settings() { 611 | 612 | _log_ "" 613 | _log_ " Container settings:" 614 | _log_ "" 615 | 616 | print_table "$(print_table_header_border)" 617 | print_table "$(print_table_header "Key" "Value")" 618 | print_table "$(print_table_header_border)" 619 | print_table "$(print_table_row "Hostname" "${container_hostname}")" 620 | print_table "$(print_table_row "User name" "$(printf "%s %s" "${container_user_name}" "(UID ${container_user_id})")")" 621 | print_table "$(print_table_row "Group name" "$(printf "%s %s" "${container_group_name}" "(UID ${container_group_id})")")" 622 | print_table "$(print_table_header_border)" 623 | } 624 | 625 | print_user_namespace_settings() { 626 | 627 | _log_ "" 628 | _log_ " Host -> Container UID/GID mapping configuration for rootless mode:" 629 | _log_ "" 630 | 631 | _log_ " $ podman unshare cat /proc/self/uid_map:" 632 | podman unshare cat /proc/self/uid_map 633 | _log_ "" 634 | 635 | _log_ " $ podman unshare cat /proc/self/gid_map:" 636 | podman unshare cat /proc/self/gid_map 637 | _log_ "" 638 | 639 | _log_ " The '/proc/self/{uid|gid}_map' contains N rows with triplets, that describe a mapping of UIDs/GIDs" 640 | _log_ " \" \" ---> [, ..., + ]." 641 | } 642 | 643 | print_settings() { 644 | 645 | print_host_settings 646 | print_container_settings 647 | print_user_namespace_settings 648 | } 649 | 650 | try_enable_lingering_on_host() { 651 | 652 | _log_ "" 653 | 654 | local linger_status=$(loginctl show-user "${host_user_name}" | grep Linger | sed -e s/Linger=//) 655 | if [ "${linger_status}" = "no" ]; then 656 | _log_ "-> Enable lingering for user '${host_user_name}' on host system..." 657 | loginctl enable-linger ${host_user_name} 658 | else 659 | _log_ "-> Lingering for user '${host_user_name}' is already activated." 660 | fi 661 | } 662 | 663 | try_enable_user_podman_socket_service_on_host() { 664 | 665 | _log_ "" 666 | 667 | local service="podman.socket" 668 | local socket_status=$(systemctl --user is-enabled "${service}") 669 | if [ "${socket_status}" = "disabled" ]; then 670 | _log_ "-> Enable '${service}' systemd user service for user '${host_user_name}' on host system..." 671 | systemctl --user enable "${service}" 672 | systemctl --user start "${service}" 673 | else 674 | _log_ "-> The systemd user service '${service}' is already enabled." 675 | fi 676 | 677 | if argsparse_is_option_set "verbose"; then 678 | _log_ "" 679 | _log_ "systemctl --user status ${service}:" 680 | systemctl --user status "${service}" 681 | fi 682 | } 683 | 684 | try_enable_nvidia_container_integration_on_host() { 685 | 686 | _log_ "" 687 | 688 | if [ ! -f "$(get_nvidia_cdi_config_file)" ]; then 689 | _log_ "-> Enable NVIDIA GPU container integration on host system, running 'wkdev-setup-nvidia-gpu-for-container'..." 690 | # Continue gracefully, as long as the setup script is not widely tested. 691 | "${WKDEV_SDK}/scripts/host-only/wkdev-setup-nvidia-gpu-for-container" || _log_ "Script failed, please investigate." 692 | else 693 | _log_ "-> The NVIDIA GPU container integration is already enabled on host system." 694 | fi 695 | } 696 | 697 | try_enable_nvidia_gpu_profiling_settings_on_host() { 698 | 699 | _log_ "" 700 | 701 | if [ -f "$(get_nvidia_gpu_profiling_conf_file)" ]; then 702 | _log_ "-> The NVIDIA kernel module settings were already deployed to $(get_nvidia_gpu_profiling_conf_file) - no need to modify." 703 | else 704 | _log_ "-> Deploying NVIDIA kernel module settings to $(get_nvidia_gpu_profiling_conf_file)..." 705 | echo 'options nvidia "NVreg_RestrictProfilingToAdminUsers=0"' | sudo tee "$(get_nvidia_gpu_profiling_conf_file)" &>/dev/null 706 | 707 | _log_ "-> Updating /boot/initrd.img files..." 708 | if does_executable_exist dracut; then 709 | sudo dracut --regenerate-all --force 710 | elif does_executable_exist update-initramfs; then 711 | update-initramfs -u -k all 712 | else 713 | _abort_ "Cannot update initram - which method to use?" 714 | fi 715 | 716 | _log_ "" 717 | _log_ "NOTE: YOU NEED TO REBOOT THE HOST SYSTEM ONCE!" 718 | fi 719 | } 720 | 721 | # Main functionality 722 | run() { 723 | 724 | process_command_line_arguments "${@}" 725 | 726 | # Ensure some vars are always set to their defaults. 727 | XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR-"/run/user/${host_user_id}"}" 728 | 729 | if [ ! -d "${XDG_RUNTIME_DIR}" ]; then 730 | # This will likely only happen when running as root like CI does. 731 | _log_ "WARNING: ${XDG_RUNTIME_DIR} did not exist, creating it..." 732 | mkdir -p --mode=700 "${XDG_RUNTIME_DIR}" 733 | fi 734 | 735 | _log_ "-> Preparing creation of rootless podman container..." 736 | print_settings 737 | 738 | try_enable_lingering_on_host 739 | try_enable_user_podman_socket_service_on_host 740 | 741 | if is_nvidia_gpu_installed; then 742 | try_enable_nvidia_container_integration_on_host 743 | try_enable_nvidia_gpu_profiling_settings_on_host 744 | fi 745 | 746 | host_setup_prerun_tasks 747 | 748 | _log_ "" 749 | _log_ "-> Host integration features:" 750 | _log_ "" 751 | 752 | local podman_create_arguments=() 753 | build_podman_create_arguments podman_create_arguments 754 | 755 | local podman_arguments=() 756 | build_podman_arguments podman_arguments 757 | 758 | _log_ "" 759 | _log_ "-> Creating container '${container_name}'..." 760 | if argsparse_is_option_set "verbose"; then 761 | _log_ "" 762 | _log_ " $ $(shjoin podman "${podman_arguments[@]}" create "${podman_create_arguments[@]}")" 763 | fi 764 | 765 | container_id=$(run_podman "${podman_arguments[@]}" create "${podman_create_arguments[@]}") 766 | [ -z "${container_id}" ] && _abort_ "Container creation failed - please check the logs and report any issue" 767 | 768 | _log_ "" 769 | _log_ "-> Starting container '${container_name}'..." 770 | local args=() 771 | if argsparse_is_option_set "attach"; then 772 | args=(start --attach "${container_id}") 773 | else 774 | _log_ " NOTE: Use \`podman logs -f ${container_name}\` to follow the initialization." 775 | args=(start "${container_id}") 776 | fi 777 | if argsparse_is_option_set "verbose"; then 778 | _log_ "" 779 | _log_ " $ $(shjoin podman "${args[@]}")" 780 | fi 781 | run_podman_silent_unless_verbose_or_abort "${args[@]}" 782 | 783 | _log_ "" 784 | _log_ "-> Finished creation of container '${container_name}'!" 785 | _log_ " NOTE: Use \`wkdev-enter --name ${container_name}\` to launch an interactive shell." 786 | } 787 | 788 | run "${@}" 789 | --------------------------------------------------------------------------------