diff --git a/build/azure-pipelines/linux/snap-build-linux.yml b/build/azure-pipelines/linux/snap-build-linux.yml index 12829334956..d1c24a33be3 100644 --- a/build/azure-pipelines/linux/snap-build-linux.yml +++ b/build/azure-pipelines/linux/snap-build-linux.yml @@ -45,7 +45,7 @@ steps: x64) SNAPCRAFT_TARGET_ARGS="" ;; *) SNAPCRAFT_TARGET_ARGS="--target-arch $(VSCODE_ARCH)" ;; esac - (cd $SNAP_ROOT/code-* && sudo --preserve-env snapcraft prime $SNAPCRAFT_TARGET_ARGS && snap pack prime --compression=lzo --filename="$SNAP_PATH") + (cd $SNAP_ROOT/code-* && sudo --preserve-env snapcraft snap $SNAPCRAFT_TARGET_ARGS --output "$SNAP_PATH") # Export SNAP_PATH echo "##vso[task.setvariable variable=SNAP_PATH]$SNAP_PATH" diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index 3c4611edab2..f5f207e5203 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -164,7 +164,8 @@ resources: endpoint: VSCodeHub options: --user 0:0 --cap-add SYS_ADMIN - container: snapcraft - image: snapcore/snapcraft:stable + image: vscodehub.azurecr.io/vscode-linux-build-agent:snapcraft-x64 + endpoint: VSCodeHub stages: - stage: Compile diff --git a/resources/linux/snap/electron-launch b/resources/linux/snap/electron-launch index 87011928b49..e90e7a7f610 100755 --- a/resources/linux/snap/electron-launch +++ b/resources/linux/snap/electron-launch @@ -4,6 +4,64 @@ # We need to handle that case and reset $SNAP SNAP=$(echo "$SNAP" | sed -e "s|/var/lib/snapd||g") +# +# Exports are based on https://github.com/snapcore/snapcraft/blob/master/extensions/desktop/common/desktop-exports +# + +# ensure_dir_exists calls `mkdir -p` if the given path is not a directory. +# This speeds up execution time by avoiding unnecessary calls to mkdir. +# +# Usage: ensure_dir_exists []... +# +function ensure_dir_exists() { + [ -d "$1" ] || mkdir -p "$@" +} + +declare -A PIDS +function async_exec() { + "$@" & + PIDS[$!]=$* +} +function wait_for_async_execs() { + for pid in "${!PIDS[@]}" + do + wait "$pid" && continue || echo "ERROR: ${PIDS[$pid]} exited abnormally with status $?" + done +} + +function prepend_dir() { + local -n var="$1" + local dir="$2" + # We can't check if the dir exists when the dir contains variables + if [[ "$dir" == *"\$"* || -d "$dir" ]]; then + export "${!var}=${dir}${var:+:$var}" + fi +} + +function append_dir() { + local -n var="$1" + local dir="$2" + # We can't check if the dir exists when the dir contains variables + if [[ "$dir" == *"\$"* || -d "$dir" ]]; then + export "${!var}=${var:+$var:}${dir}" + fi +} + +# shellcheck source=/dev/null +source "$SNAP_USER_DATA/.last_revision" 2>/dev/null || true +if [ "$SNAP_DESKTOP_LAST_REVISION" = "$SNAP_VERSION" ]; then + needs_update=false +else + needs_update=true +fi + +# Set $REALHOME to the users real home directory +REALHOME=$(getent passwd $UID | cut -d ':' -f 6) + +# Set config folder to local path +ensure_dir_exists "$SNAP_USER_DATA/.config" +chmod 700 "$SNAP_USER_DATA/.config" + if [ "$SNAP_ARCH" == "amd64" ]; then ARCH="x86_64-linux-gnu" elif [ "$SNAP_ARCH" == "armhf" ]; then @@ -14,21 +72,165 @@ else ARCH="$SNAP_ARCH-linux-gnu" fi -GDK_CACHE_DIR="$SNAP_USER_COMMON/.cache" -if [[ -d "$SNAP_USER_DATA/.cache" && ! -e "$GDK_CACHE_DIR" ]]; then +export SNAP_LAUNCHER_ARCH_TRIPLET="$ARCH" + +function is_subpath() { + dir="$(realpath "$1")" + parent="$(realpath "$2")" + [ "${dir##"${parent}"/}" != "${dir}" ] && return 0 || return 1 +} + +function can_open_file() { + [ -f "$1" ] && [ -r "$1" ] +} + +# XDG Config +prepend_dir XDG_CONFIG_DIRS "$SNAP/etc/xdg" + +# Define snaps' own data dir +prepend_dir XDG_DATA_DIRS "$SNAP/usr/share" +prepend_dir XDG_DATA_DIRS "$SNAP/share" +prepend_dir XDG_DATA_DIRS "$SNAP/data-dir" +prepend_dir XDG_DATA_DIRS "$SNAP_USER_DATA" + +# Set XDG_DATA_HOME to local path +ensure_dir_exists "$SNAP_USER_DATA/.local/share" + +# Workaround for GLib < 2.53.2 not searching for schemas in $XDG_DATA_HOME: +# https://bugzilla.gnome.org/show_bug.cgi?id=741335 +prepend_dir XDG_DATA_DIRS "$SNAP_USER_DATA/.local/share" + +# Set cache folder to local path +if [[ -d "$SNAP_USER_DATA/.cache" && ! -e "$SNAP_USER_COMMON/.cache" ]]; then # the .cache directory used to be stored under $SNAP_USER_DATA, migrate it mv "$SNAP_USER_DATA/.cache" "$SNAP_USER_COMMON/" fi -[ ! -d "$GDK_CACHE_DIR" ] && mkdir -p "$GDK_CACHE_DIR" +ensure_dir_exists "$SNAP_USER_COMMON/.cache" -# Gdk-pixbuf loaders -export GDK_PIXBUF_MODULE_FILE="$GDK_CACHE_DIR/gdk-pixbuf-loaders.cache" -export GDK_PIXBUF_MODULEDIR="$SNAP/usr/lib/$ARCH/gdk-pixbuf-2.0/2.10.0/loaders" -if [ -f "$SNAP/usr/lib/$ARCH/gdk-pixbuf-2.0/gdk-pixbuf-query-loaders" ]; then - "$SNAP/usr/lib/$ARCH/gdk-pixbuf-2.0/gdk-pixbuf-query-loaders" > "$GDK_PIXBUF_MODULE_FILE" +# Create $XDG_RUNTIME_DIR if not exists (to be removed when LP: #1656340 is fixed) +# shellcheck disable=SC2174 +ensure_dir_exists "$XDG_RUNTIME_DIR" -m 700 + +# Ensure the app finds locale definitions (requires locales-all to be installed) +append_dir LOCPATH "$SNAP/usr/lib/locale" + +# If detect wayland server socket, then set environment so applications prefer +# wayland, and setup compat symlink (until we use user mounts. Remember, +# XDG_RUNTIME_DIR is /run/user//snap.$SNAP so look in the parent directory +# for the socket. For details: +# https://forum.snapcraft.io/t/wayland-dconf-and-xdg-runtime-dir/186/10 +# Applications that don't support wayland natively may define DISABLE_WAYLAND +# (to any non-empty value) to skip that logic entirely. +wayland_available=false +if [[ -n "$XDG_RUNTIME_DIR" && -z "$DISABLE_WAYLAND" ]]; then + wdisplay="wayland-0" + if [ -n "$WAYLAND_DISPLAY" ]; then + wdisplay="$WAYLAND_DISPLAY" + fi + wayland_sockpath="$XDG_RUNTIME_DIR/../$wdisplay" + wayland_snappath="$XDG_RUNTIME_DIR/$wdisplay" + if [ -S "$wayland_sockpath" ]; then + # if running under wayland, use it + #export WAYLAND_DEBUG=1 + # shellcheck disable=SC2034 + wayland_available=true + # create the compat symlink for now + if [ ! -e "$wayland_snappath" ]; then + ln -s "$wayland_sockpath" "$wayland_snappath" + fi + fi fi -# Create $XDG_RUNTIME_DIR if not exists (to be removed when https://pad.lv/1656340 is fixed) -[ -n "$XDG_RUNTIME_DIR" ] && mkdir -p -m 700 "$XDG_RUNTIME_DIR" +# Keep an array of data dirs, for looping through them +IFS=':' read -r -a data_dirs_array <<< "$XDG_DATA_DIRS" + +# Build mime.cache +# needed for gtk and qt icon +if [ "$needs_update" = true ]; then + rm -rf "$SNAP_USER_DATA/.local/share/mime" + if [ ! -f "$SNAP/usr/share/mime/mime.cache" ]; then + if command -v update-mime-database >/dev/null; then + cp --preserve=timestamps -dR "$SNAP/usr/share/mime" "$SNAP_USER_DATA/.local/share" + async_exec update-mime-database "$SNAP_USER_DATA/.local/share/mime" + fi + fi +fi + +# Gio modules and cache (including gsettings module) +export GIO_MODULE_DIR="$SNAP_USER_COMMON/.cache/gio-modules" +function compile_giomodules { + if [ -f "$1/glib-2.0/gio-querymodules" ]; then + rm -rf "$GIO_MODULE_DIR" + ensure_dir_exists "$GIO_MODULE_DIR" + ln -s "$1"/gio/modules/*.so "$GIO_MODULE_DIR" + "$1/glib-2.0/gio-querymodules" "$GIO_MODULE_DIR" + fi +} +if [ "$needs_update" = true ]; then + async_exec compile_giomodules "$SNAP/usr/lib/$ARCH" +fi + +# Setup compiled gsettings schema +GS_SCHEMA_DIR="$SNAP_USER_DATA/.local/share/glib-2.0/schemas" +function compile_schemas { + if [ -f "$1" ]; then + rm -rf "$GS_SCHEMA_DIR" + ensure_dir_exists "$GS_SCHEMA_DIR" + for ((i = 0; i < ${#data_dirs_array[@]}; i++)); do + schema_dir="${data_dirs_array[$i]}/glib-2.0/schemas" + if [ -f "$schema_dir/gschemas.compiled" ]; then + # This directory already has compiled schemas + continue + fi + if [ -n "$(ls -A "$schema_dir"/*.xml 2>/dev/null)" ]; then + ln -s "$schema_dir"/*.xml "$GS_SCHEMA_DIR" + fi + if [ -n "$(ls -A "$schema_dir"/*.override 2>/dev/null)" ]; then + ln -s "$schema_dir"/*.override "$GS_SCHEMA_DIR" + fi + done + # Only compile schemas if we copied anything + if [ -n "$(ls -A "$GS_SCHEMA_DIR"/*.xml "$GS_SCHEMA_DIR"/*.override 2>/dev/null)" ]; then + "$1" "$GS_SCHEMA_DIR" + fi + fi +} +if [ "$needs_update" = true ]; then + async_exec compile_schemas "$SNAP/usr/lib/$ARCH/glib-2.0/glib-compile-schemas" +fi + +# Gdk-pixbuf loaders +export GDK_PIXBUF_MODULE_FILE="$SNAP_USER_COMMON/.cache/gdk-pixbuf-loaders.cache" +export GDK_PIXBUF_MODULEDIR="$SNAP/usr/lib/$ARCH/gdk-pixbuf-2.0/2.10.0/loaders" +if [ "$needs_update" = true ] || [ ! -f "$GDK_PIXBUF_MODULE_FILE" ]; then + rm -f "$GDK_PIXBUF_MODULE_FILE" + if [ -f "$SNAP/usr/lib/$ARCH/gdk-pixbuf-2.0/gdk-pixbuf-query-loaders" ]; then + async_exec "$SNAP/usr/lib/$ARCH/gdk-pixbuf-2.0/gdk-pixbuf-query-loaders" > "$GDK_PIXBUF_MODULE_FILE" + fi +fi + +# shellcheck disable=SC2154 +if [ "$wayland_available" = true ]; then + export GDK_BACKEND="wayland" +fi + +append_dir GTK_PATH "$SNAP/usr/lib/$ARCH/gtk-3.0" +append_dir GTK_PATH "$SNAP/usr/lib/gtk-3.0" + +# ibus and fcitx integration +GTK_IM_MODULE_DIR="$SNAP_USER_COMMON/.cache/immodules" +export GTK_IM_MODULE_FILE="$GTK_IM_MODULE_DIR/immodules.cache" +# shellcheck disable=SC2154 +if [ "$needs_update" = true ]; then + rm -rf "$GTK_IM_MODULE_DIR" + ensure_dir_exists "$GTK_IM_MODULE_DIR" + ln -s "$SNAP"/usr/lib/"$ARCH"/gtk-3.0/3.0.0/immodules/*.so "$GTK_IM_MODULE_DIR" + async_exec "$SNAP/usr/lib/$ARCH/libgtk-3-0/gtk-query-immodules-3.0" > "$GTK_IM_MODULE_FILE" +fi + +# shellcheck disable=SC2154 +[ "$needs_update" = true ] && echo "SNAP_DESKTOP_LAST_REVISION=$SNAP_VERSION" > "$SNAP_USER_DATA/.last_revision" + +wait_for_async_execs exec "$@" diff --git a/resources/linux/snap/snapcraft.yaml b/resources/linux/snap/snapcraft.yaml index fc775b6554f..1a2e52c5f25 100644 --- a/resources/linux/snap/snapcraft.yaml +++ b/resources/linux/snap/snapcraft.yaml @@ -12,55 +12,73 @@ architectures: grade: stable confinement: classic +base: core20 +compression: lzo parts: - gnome: - plugin: nil - build-packages: - - software-properties-common - override-pull: | - add-apt-repository -y ppa:ubuntu-desktop/gnome-3-26 - apt -y update - code: - after: - - gnome plugin: dump source: . stage-packages: - - ibus-gtk3 - - fcitx-frontend-gtk3 - - gvfs-libs + - ca-certificates - libasound2 - - libgconf-2-4 - - libglib2.0-bin - - libgnome-keyring0 + - libatk-bridge2.0-0 + - libatk1.0-0 + - libatspi2.0-0 + - libcairo2 + - libcanberra-gtk3-module + - libcurl3-gnutls + - libcurl3-nss + - libcurl4 + - libdrm2 - libgbm1 + - libgl1 + - libglib2.0-0 - libgtk-3-0 - - libnotify4 - - libnspr4 - libnss3 - - libpcre3 - - libpulse0 + - libpango-1.0-0 - libsecret-1-0 + - libxcomposite1 + - libxdamage1 + - libxfixes3 + - libxkbcommon0 + - libxkbfile1 + - libxrandr2 - libxss1 - - libxtst6 - - zlib1g + - locales-all + - packagekit-gtk3-module + - xdg-utils prime: - -usr/share/doc - -usr/share/fonts - -usr/share/icons - -usr/share/lintian - -usr/share/man + override-build: | + snapcraftctl build + patchelf --force-rpath --set-rpath '$ORIGIN/../../lib/x86_64-linux-gnu:$ORIGIN:/snap/core20/current/lib/x86_64-linux-gnu' $SNAPCRAFT_PART_INSTALL/usr/share/@@NAME@@/chrome_crashpad_handler + cleanup: + after: + - code + plugin: nil + build-snaps: + - core20 + override-prime: | + set -eux + for snap in "core20"; do + cd "/snap/$snap/current" && find . -type f,l -exec rm -f "$SNAPCRAFT_PRIME/{}" \; + done + patchelf --print-rpath $SNAPCRAFT_PRIME/usr/share/@@NAME@@/chrome_crashpad_handler + apps: @@NAME@@: command: electron-launch $SNAP/usr/share/@@NAME@@/bin/@@NAME@@ --no-sandbox common-id: @@NAME@@.desktop environment: - GSETTINGS_SCHEMA_DIR: $SNAP/usr/share/glib-2.0/schemas + GTK_USE_PORTAL: 1 url-handler: command: electron-launch $SNAP/usr/share/@@NAME@@/bin/@@NAME@@ --open-url --no-sandbox environment: - GSETTINGS_SCHEMA_DIR: $SNAP/usr/share/glib-2.0/schemas + GTK_USE_PORTAL: 1 diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index 70781f7fa86..e55d3df16a5 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -451,14 +451,23 @@ export class NativeHostMainService extends Disposable implements INativeHostMain // Remove some environment variables before opening to avoid issues... const gdkPixbufModuleFile = process.env['GDK_PIXBUF_MODULE_FILE']; const gdkPixbufModuleDir = process.env['GDK_PIXBUF_MODULEDIR']; + const gtkIMModuleFile = process.env['GTK_IM_MODULE_FILE']; + const gdkBackend = process.env['GDK_BACKEND']; + const gioModuleDir = process.env['GIO_MODULE_DIR']; delete process.env['GDK_PIXBUF_MODULE_FILE']; delete process.env['GDK_PIXBUF_MODULEDIR']; + delete process.env['GTK_IM_MODULE_FILE']; + delete process.env['GDK_BACKEND']; + delete process.env['GIO_MODULE_DIR']; shell.openExternal(url); // ...but restore them after process.env['GDK_PIXBUF_MODULE_FILE'] = gdkPixbufModuleFile; process.env['GDK_PIXBUF_MODULEDIR'] = gdkPixbufModuleDir; + process.env['GTK_IM_MODULE_FILE'] = gtkIMModuleFile; + process.env['GDK_BACKEND'] = gdkBackend; + process.env['GIO_MODULE_DIR'] = gioModuleDir; } moveItemToTrash(windowId: number | undefined, fullPath: string): Promise {