matplotlib dies on "c++: fatal error: Killed signal terminated program cc1plus". "Killed" is a SIGKILL: the kernel's OOM killer, and nothing in the message names memory. A single matplotlib source file asks cc1plus for up to 2.5 GiB. Two causes compound. The VM is small -- the catalogue starts at 1 or 2 GiB -- and every build backend launches nproc compilations in parallel, each with its own cc1plus. Six cores exhaust 8 GiB. Hence two answers. A swap file tops memory up to 8 GiB, never taking more than half the free disk nor touching fstab -- a declared and missing swap degrades boot. And parallelism is bounded by actual memory, 2 GiB per task. ninja has no parallelism environment variable, but meson-python reads "NINJA", the executable PATH -- verified in mesonpy 0.20. We point it at a wrapper that adds the -j. That is what saves matplotlib; MAKEFLAGS and CMAKE_BUILD_PARALLEL_LEVEL cover the rest. --- FR --- matplotlib s'arrête sur « c++: fatal error: Killed signal terminated program cc1plus ». « Killed » est un SIGKILL : c'est le tueur du noyau, et rien dans le message ne nomme la mémoire. Un seul fichier de matplotlib demande jusqu'à 2,5 Gio à cc1plus. Deux causes se cumulent. La VM est petite — le catalogue démarre à 1 ou 2 Gio — et chaque moteur de build lance nproc compilations en parallèle, chacune avec son cc1plus. Six cœurs épuisent 8 Gio. D'où deux réponses. Un fichier d'échange complète la mémoire jusqu'à 8 Gio, sans jamais prendre plus de la moitié du disque libre ni toucher à fstab — un swap déclaré et disparu dégrade le démarrage. Et le parallélisme est borné d'après la mémoire réelle, 2 Gio par tâche. ninja n'a aucune variable de parallélisme, mais meson-python lit « NINJA », le CHEMIN de l'exécutable — vérifié dans mesonpy 0.20. On y met une enveloppe qui ajoute le -j. C'est ce qui sauve matplotlib ; MAKEFLAGS et CMAKE_BUILD_PARALLEL_LEVEL couvrent les autres. Assisted-by: Claude Opus 5
370 lines
16 KiB
Bash
Executable file
370 lines
16 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# © 2021-2026 TechnoLibre (http://www.technolibre.ca)
|
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
|
|
#
|
|
# Dépendances système ERPLibre pour la famille SUSE (zypper). Équivalent SUSE
|
|
# de install_debian_dependency.sh et install_fedora_dependency.sh.
|
|
#
|
|
# Visé : openSUSE Leap 16 (le défaut, numéroté et stable) et Tumbleweed (la
|
|
# rolling, gardée comme banc d'essai). Les deux livrent un qpdf au-dessus du
|
|
# seuil de pikepdf 10 — la compilation de qpdf, une demi-heure sous émulation
|
|
# s390x, ne s'y déclenche pas.
|
|
#
|
|
# Ce qui les sépare dans ce script tient en deux points, tous deux marqués par
|
|
# SUSE_ROLLING : le chemin des dépôts, et « dup » contre « up ».
|
|
|
|
. ./env_var.sh
|
|
. ./script/install/lib_qpdf.sh
|
|
. ./script/install/lib_lowmem.sh
|
|
|
|
EL_USER=${USER}
|
|
|
|
# « --non-interactive » est une option GLOBALE de zypper, elle précède la
|
|
# sous-commande. Les deux autres appartiennent à « install » et doivent la
|
|
# SUIVRE : placées avant, zypper répond « The flag
|
|
# --auto-agree-with-licenses is not known » et s'arrête.
|
|
#
|
|
# On demande à zypper quelles options il accepte, plutôt que de le supposer :
|
|
# la famille SUSE n'est pas testée ici, et un drapeau inconnu fait échouer la
|
|
# commande entière au lieu du seul paquet visé.
|
|
# --auto-agree-with-licenses : sinon zypper attend l'acceptation d'une
|
|
# licence, réponse que personne ne donnera dans une installation détachée.
|
|
# --ignore-unknown : saute un nom de paquet absent au lieu de refuser tout
|
|
# le lot, l'équivalent de « --skip-unavailable » côté dnf.
|
|
ZYP="sudo zypper --non-interactive"
|
|
ZYP_HELP="$(zypper install --help 2>&1 || true)"
|
|
ZYP_LIC=""
|
|
ZYP_IGN=""
|
|
case "${ZYP_HELP}" in
|
|
*--auto-agree-with-licenses*) ZYP_LIC="--auto-agree-with-licenses" ;;
|
|
esac
|
|
case "${ZYP_HELP}" in
|
|
*--ignore-unknown*) ZYP_IGN="--ignore-unknown" ;;
|
|
esac
|
|
echo "zypper : options retenues = ${ZYP_LIC} ${ZYP_IGN}"
|
|
# Lot obligatoire : un paquet manquant doit se voir.
|
|
ZYP_IN="${ZYP} install ${ZYP_LIC}"
|
|
# Lot best-effort : un nom absent est sauté.
|
|
ZYP_SOFT="${ZYP_IN} ${ZYP_IGN}"
|
|
|
|
# openSUSE remplace des paquets par des variantes « compat » qui les FOURNISSENT
|
|
# sans en porter le nom : sur Tumbleweed, zlib-ng-compat-devel fournit
|
|
# zlib-devel et se trouve posé d'office. Demander « zlib-devel » par son nom
|
|
# force alors un échange, zypper soulève un conflit — et --non-interactive n'a
|
|
# aucune solution par défaut : il énumère les choix, puis ABANDONNE le lot
|
|
# entier. C'est ce qui a arrêté l'installation sur amd64, en emportant les huit
|
|
# autres dépendances de pyenv qui, elles, ne posaient aucun problème.
|
|
# « --ignore-unknown » ne couvre que les noms absents, jamais les conflits.
|
|
#
|
|
# On ne demande donc que ce que rien ne fournit déjà. rpm interroge les
|
|
# « Provides », pas seulement les noms : c'est la seule autorité sur la
|
|
# question. Effet de bord bienvenu, une relance ne redemande plus rien.
|
|
zyp_filter() {
|
|
local p
|
|
for p in "$@"; do
|
|
rpm -q --whatprovides "${p}" > /dev/null 2>&1 || printf '%s\n' "${p}"
|
|
done
|
|
}
|
|
|
|
# Un telechargement tronque par le miroir fait echouer le LOT ENTIER, et
|
|
# zypper ne reessaie pas. Vecu : « libLLVM22 ... OpenSSL SSL_read: unexpected
|
|
# eof while reading » a emporte « rust cargo », et l'absence de cargo n'est
|
|
# apparue qu'une heure plus tard, dans la compilation de bcrypt. Une seconde
|
|
# tentative suffit dans la quasi-totalite des cas.
|
|
zyp_retry() {
|
|
local cmd=("$@")
|
|
"${cmd[@]}" && return 0
|
|
echo " zypper a echoue (reseau ?), seconde tentative..." >&2
|
|
"${cmd[@]}"
|
|
}
|
|
|
|
zyp_in() {
|
|
local todo
|
|
mapfile -t todo < <(zyp_filter "$@")
|
|
if [ "${#todo[@]}" -eq 0 ]; then
|
|
echo " deja fourni : $*"
|
|
return 0
|
|
fi
|
|
zyp_retry ${ZYP_IN} "${todo[@]}"
|
|
}
|
|
|
|
zyp_soft() {
|
|
local todo
|
|
mapfile -t todo < <(zyp_filter "$@")
|
|
if [ "${#todo[@]}" -eq 0 ]; then
|
|
echo " deja fourni : $*"
|
|
return 0
|
|
fi
|
|
zyp_retry ${ZYP_SOFT} "${todo[@]}"
|
|
}
|
|
|
|
#--------------------------------------------------
|
|
# Miroir des dépôts
|
|
#--------------------------------------------------
|
|
# Le redirecteur officiel d'openSUSE n'est PAS géographique : mesuré depuis
|
|
# Montréal sur les métadonnées oss s390x (15 Mo), download.opensuse.org met
|
|
# 23,8 s — il sert depuis l'Europe — contre 2,7 s pour mirrors.rit.edu.
|
|
#
|
|
# Chaque miroir est SONDÉ sur le chemin de l'architecture courante : tous ne
|
|
# répliquent pas les architectures secondaires, et rit.edu sert justement
|
|
# zsystems mais pas x86_64. Aucun sondage concluant : on garde les dépôts de
|
|
# l'image, donc le comportement d'avant.
|
|
# Plusieurs miroirs, sondés dans l'ordre sur le chemin exact du produit et
|
|
# de l'architecture : aucun ne réplique tout. Relevé le 2026-08-12 —
|
|
# csclub sert Leap mais pas Tumbleweed, leaseweb sert Tumbleweed x86_64
|
|
# mais pas les ports zsystems, et rit.edu — seule entrée jusqu'ici — était
|
|
# injoignable ce jour-là, ce qui renvoyait tout le monde en Europe.
|
|
ZYP_MIRRORS="https://mirror.csclub.uwaterloo.ca/opensuse https://mirrors.rit.edu/opensuse https://mirror.us.leaseweb.net/opensuse"
|
|
# Leap et Tumbleweed sont deux PRODUITS, pas deux versions du même : arbres de
|
|
# dépôts différents, et surtout une mise à jour de nature différente (plus bas).
|
|
SUSE_ID="$([ -r /etc/os-release ] && . /etc/os-release && echo "${ID}")"
|
|
SUSE_VER="$([ -r /etc/os-release ] && . /etc/os-release && echo "${VERSION_ID}")"
|
|
case "${SUSE_ID}" in
|
|
*tumbleweed*) SUSE_ROLLING=1 ;;
|
|
*) SUSE_ROLLING=0 ;;
|
|
esac
|
|
if [ "${SUSE_ROLLING}" = 1 ]; then
|
|
# Tumbleweed isole les architectures secondaires sous /ports/.
|
|
zp=tumbleweed
|
|
[ "$(uname -m)" = s390x ] && zp=ports/zsystems/tumbleweed
|
|
else
|
|
# Leap 16 unifie tout : un seul arbre, avec s390x dedans. Les chemins
|
|
# /ports/ équivalents rendent 404 — relevé, pas déduit.
|
|
zp="distribution/leap/${SUSE_VER}"
|
|
fi
|
|
for zm in ${ZYP_MIRRORS}; do
|
|
if curl -fsS --max-time 20 -o /dev/null \
|
|
"${zm}/${zp}/repo/oss/repodata/repomd.xml"; then
|
|
sudo sed -i "s|https\?://download\.opensuse\.org|${zm}|g" \
|
|
/etc/zypp/repos.d/*.repo 2> /dev/null || true
|
|
echo "miroir openSUSE : ${zm}"
|
|
break
|
|
fi
|
|
done
|
|
|
|
echo -e "\n---- Rafraichissement des depots ----"
|
|
# Tumbleweed est ROLLING : installer sans rafraîchir mène à des paquets
|
|
# introuvables (l'index local pointe des versions déjà retirées du miroir).
|
|
sudo zypper --non-interactive refresh || true
|
|
|
|
# Mise à jour COMPLÈTE, obligatoire pour la même raison qu'Arch : une rolling
|
|
# release ne supporte PAS les mises à jour partielles. L'image cloud est un
|
|
# instantané figé dont les dépôts ont avancé — vécu sur s390x, git 2.54
|
|
# réclamait un perl-Git bâti contre un perl-base plus ancien que celui de
|
|
# l'image. zypper proposait alors trois solutions et ATTENDAIT un choix ;
|
|
# « --non-interactive » prend le défaut, « c » = annuler, et tout s'arrêtait.
|
|
if [ "${SUSE_ROLLING}" = 1 ]; then
|
|
echo -e "\n---- Mise a jour complete (dup) ----"
|
|
${ZYP} dup ${ZYP_LIC} --allow-vendor-change || true
|
|
else
|
|
# Sur Leap, « dup » sert à CHANGER de version, pas à mettre à jour : l'y
|
|
# appeler alignerait la machine sur ce que les dépôts contiennent, ce qui
|
|
# n'est pas ce qu'on veut d'une distribution justement choisie pour ne pas
|
|
# bouger. « up » suffit, l'image et ses dépôts étant de la même version.
|
|
echo -e "\n---- Mise a jour (up) ----"
|
|
${ZYP} up ${ZYP_LIC} || true
|
|
fi
|
|
|
|
#--------------------------------------------------
|
|
# Outils de compilation (build Python via pyenv, extensions Python)
|
|
#--------------------------------------------------
|
|
echo -e "\n---- Outils de developpement ----"
|
|
# Les compilateurs sont installés EXPLICITEMENT, jamais par le seul motif :
|
|
# un « pattern » absent ou incomplet laisserait le système sans g++, et
|
|
# l'échec n'apparaîtrait qu'à la compilation de numpy — la leçon d'EL, où le
|
|
# groupe « development-tools » n'existe pas et où le repli ne partait jamais.
|
|
# Les noms non versionnés existent bien dans Tumbleweed (relevé dans le
|
|
# primary.xml du dépôt oss) : « gcc » y est un méta-paquet qui tire gcc15.
|
|
zyp_in gcc gcc-c++ make automake patch
|
|
retVal=$?
|
|
if [[ ${retVal} -ne 0 ]]; then
|
|
echo "zypper install compilers error."
|
|
exit 1
|
|
fi
|
|
# Le motif ensuite, en complément et sans jamais bloquer.
|
|
${ZYP_IN} -t pattern devel_basis > /dev/null 2>&1 || true
|
|
|
|
# Python appelle son compilateur par le nom gravé dans sysconfig, souvent
|
|
# « cc ». Sur openSUSE ce lien n'est garanti par aucune déclaration de
|
|
# paquet — vécu sur amd64 : « cc -pthread … error: [Errno 2] No such file or
|
|
# directory » à la compilation de python-ldap, alors que gcc était installé.
|
|
if ! command -v cc > /dev/null 2>&1 && command -v gcc > /dev/null 2>&1; then
|
|
sudo ln -sf "$(command -v gcc)" /usr/local/bin/cc
|
|
echo "cc -> $(command -v gcc)"
|
|
fi
|
|
|
|
#--------------------------------------------------
|
|
# PostgreSQL
|
|
#--------------------------------------------------
|
|
echo -e "\n---- Install PostgreSQL Server ----"
|
|
zyp_in postgresql-server postgresql-contrib postgresql-server-devel
|
|
retVal=$?
|
|
if [[ ${retVal} -ne 0 ]]; then
|
|
echo "zypper install postgresql installation error."
|
|
exit 1
|
|
fi
|
|
# openSUSE n'initialise pas le cluster : le service le fait au 1er démarrage,
|
|
# mais seulement si le répertoire de données est vide.
|
|
sudo systemctl enable --now postgresql 2> /dev/null || true
|
|
# PostGIS : absent des dépôts Tumbleweed s390x. Optionnel, ne bloque pas.
|
|
zyp_soft postgis || echo "PostGIS non installe (optionnel)."
|
|
|
|
echo -e "\n---- Creating the ERPLibre PostgreSQL User ----"
|
|
sudo su - postgres -c "createuser -s ${EL_USER}" 2> /dev/null || true
|
|
|
|
#--------------------------------------------------
|
|
# Dépendances de build (extensions Python, Odoo)
|
|
#--------------------------------------------------
|
|
echo -e "\n--- Installing suse dependency --"
|
|
# git-daemon : comme sur Fedora, « git daemon » n'est pas dans le paquet git de
|
|
# base. ERPLibre sert son manifeste par git://127.0.0.1:9418/ pendant
|
|
# « repo sync » — sans lui, « Connection refused » et synchro impossible.
|
|
# Rust n'est nécessaire QUE là où les roues manquent : bcrypt et cryptography
|
|
# portent une extension Rust, et sur amd64 leurs roues masquent le besoin. Sur
|
|
# s390x tout compile, et bcrypt s'arrête sur « error: can't find Rust
|
|
# compiler ». Tumbleweed livre 1.94, bien au-dessus du 1.78 qu'exige le
|
|
# Cargo.lock v4 de cryptography. On évite ainsi ~200 Mo inutiles sur amd64.
|
|
if [ "$(uname -m)" = "s390x" ]; then
|
|
# La mémoire est la ressource qui manque en premier ici : cc1plus
|
|
# demande jusqu'à 2,5 Gio pour un seul fichier de matplotlib, et le
|
|
# tueur du noyau abrège sans jamais nommer la mémoire (« Killed signal
|
|
# terminated program cc1plus »). On complète par du swap avant d'en
|
|
# arriver là.
|
|
el_swap_ensure
|
|
zyp_soft rust cargo
|
|
if ! command -v cargo > /dev/null 2>&1; then
|
|
# FATAL, et non plus un simple avertissement : sur s390x, bcrypt et
|
|
# cryptography n'ont aucune roue et portent une extension Rust. Sans
|
|
# cargo l'installation est perdue d'avance — mais elle ne le montrait
|
|
# qu'une heure plus tard, au fond d'une trace pip. Mieux vaut s'arreter
|
|
# ici, ou la cause est encore lisible.
|
|
echo "cargo introuvable apres deux tentatives." >&2
|
|
echo " bcrypt et cryptography ne peuvent pas se compiler sans lui." >&2
|
|
echo " Verifiez le miroir zypper, puis relancez." >&2
|
|
exit 1
|
|
fi
|
|
# Sans roue s390x, TOUT se compile contre les en-têtes de la distribution.
|
|
# Cette liste reprend donc celles d'apt et de dnf, aux noms d'openSUSE près
|
|
# — relevés dans l'index oss de zsystems, jamais devinés. La tenir alignée
|
|
# ici plutôt que d'attendre : trois fois de suite, le même manque a été
|
|
# découvert d'abord par Debian, puis retrouvé une distribution à la fois.
|
|
# tbb-devel manifold3d refuse de configurer sans lui, et le dit dans un
|
|
# message de CMake, pas de compilateur : « Parallel mode
|
|
# enabled, but tbb was not found ».
|
|
# clang-devel pymupdf charge libclang.so par ctypes (lien juste après).
|
|
# cmake, pkgconf-pkg-config : nécessaires à el_qpdf_ensure, et ils
|
|
# n'arrivent que plus bas dans le script.
|
|
#
|
|
# « pkgconf-pkg-config » et non « pkg-config » : aucun RPM ne porte ce
|
|
# dernier nom dans Tumbleweed, il n'existe plus que comme capacité fournie
|
|
# par le premier. La capacité résout aujourd'hui, le nom réel ne dépend
|
|
# de rien.
|
|
#
|
|
# GeographicLib est absent : openSUSE n'empaquette que le binding Python,
|
|
# pas la bibliothèque C++. Rien à poser, contrairement à apt et dnf.
|
|
zyp_soft qpdf-devel libjpeg8-devel cmake pkgconf-pkg-config \
|
|
tbb-devel geos-devel proj-devel krb5-devel ninja clang-devel llvm-devel
|
|
# Tumbleweed livre qpdf 12.3.2 : l'appel ne fait que le constater. Il est là
|
|
# pour que les trois familles suivent la même règle.
|
|
el_qpdf_ensure
|
|
# pymupdf charge « libclang.so » par son nom nu, via ctypes ; le paquet ne
|
|
# livre qu'un nom versionné. Il ne manque que le lien — même correctif que
|
|
# côté apt et dnf.
|
|
for d in /usr/lib64 /usr/lib; do
|
|
if [ -d "${d}" ] && [ ! -e "${d}/libclang.so" ]; then
|
|
so="$(ls -1 "${d}"/libclang.so.* 2> /dev/null | sort -V | tail -1)"
|
|
if [ -n "${so}" ]; then
|
|
sudo ln -s "${so}" "${d}/libclang.so" && sudo ldconfig
|
|
echo "libclang.so -> ${so}"
|
|
fi
|
|
fi
|
|
done
|
|
fi
|
|
|
|
zyp_soft \
|
|
git git-daemon wget libxslt-devel libzip-devel openldap2-devel \
|
|
cyrus-sasl-devel libffi-devel libbz2-devel gnu_parallel swig cmake \
|
|
portaudio-devel cups-devel xmlsec1 xmlsec1-openssl-devel \
|
|
libmariadb-devel freetds-devel
|
|
retVal=$?
|
|
if [[ ${retVal} -ne 0 ]]; then
|
|
echo "zypper suse tool installation error."
|
|
exit 1
|
|
fi
|
|
|
|
# Dépendances de build pour pyenv (compilation de CPython) — CRITIQUE.
|
|
# Python de la DISTRIBUTION d'abord : Tumbleweed livre python312 en 3.12.13,
|
|
# qui satisfait « >=3.12.10,<3.13 ». lib_python_provider.sh le prefere alors a
|
|
# toute compilation — decisif sur s390x, ou gcc 15.2 s'arrete sur une erreur
|
|
# interne en batissant Parser/parser.c de CPython 3.12.10.
|
|
# Le nom suit la version demandee : python312 pour 3.12.x.
|
|
PY_WANT="$(cat .python-odoo-version 2>/dev/null | xargs)"
|
|
PY_PKG="python$(echo "${PY_WANT}" | awk -F. '{print $1 $2}')"
|
|
if [ -n "${PY_WANT}" ]; then
|
|
echo -e "\n---- Python ${PY_WANT} depuis la distribution (${PY_PKG}) ----"
|
|
zyp_soft "${PY_PKG}" "${PY_PKG}-devel" "${PY_PKG}-pip"
|
|
fi
|
|
|
|
echo -e "\n---- Dependances pyenv (compilation Python) ----"
|
|
zyp_in \
|
|
make gcc zlib-devel libbz2-devel readline-devel sqlite3-devel \
|
|
libopenssl-devel tk-devel libffi-devel xz-devel patch findutils
|
|
retVal=$?
|
|
if [[ ${retVal} -ne 0 ]]; then
|
|
echo "zypper pyenv dependencies installation error."
|
|
exit 1
|
|
fi
|
|
|
|
# Dépendances selenium / bindings.
|
|
zyp_soft \
|
|
cairo-devel python3-devel pkgconf-pkg-config gobject-introspection-devel \
|
|
libXt-devel || echo "Dependances selenium partielles (optionnel)."
|
|
|
|
#--------------------------------------------------
|
|
# Node.js + npm (rtlcss, less)
|
|
#--------------------------------------------------
|
|
echo -e "\n---- Installing nodeJS NPM and rtlcss ----"
|
|
# openSUSE versionne les paquets node (nodejs22, nodejs24…) et ne fournit pas
|
|
# toujours de méta-paquet « nodejs ». On prend le plus récent disponible.
|
|
NODE_OK=0
|
|
for pkg in nodejs24 nodejs22 nodejs20 nodejs; do
|
|
if ${ZYP_IN} "${pkg}" "${pkg/nodejs/npm}" 2> /dev/null; then
|
|
NODE_OK=1
|
|
echo "node fourni par ${pkg}"
|
|
break
|
|
fi
|
|
done
|
|
if [[ ${NODE_OK} -ne 1 ]] || ! command -v npm > /dev/null 2>&1; then
|
|
echo "zypper nodejs installation error."
|
|
exit 1
|
|
fi
|
|
sudo npm install -g rtlcss less || echo "npm rtlcss/less: erreur (optionnel)."
|
|
|
|
echo -e "\n---- Test tool ----"
|
|
npm install || echo "npm install (prettier/plugin-xml): erreur (optionnel)."
|
|
sudo ln -fs /usr/local/bin/lessc /usr/bin/lessc 2> /dev/null || true
|
|
|
|
#--------------------------------------------------
|
|
# nginx (optionnel)
|
|
#--------------------------------------------------
|
|
if [ "${EL_INSTALL_NGINX}" = "True" ]; then
|
|
echo -e "\n---- Installing nginx ----"
|
|
zyp_soft nginx || echo "nginx: erreur (optionnel)."
|
|
fi
|
|
|
|
#--------------------------------------------------
|
|
# wkhtmltopdf (optionnel)
|
|
#--------------------------------------------------
|
|
if [ "${EL_INSTALL_WKHTMLTOPDF}" = "True" ]; then
|
|
if ! command -v wkhtmltopdf > /dev/null 2>&1; then
|
|
echo -e "\n---- Installing wkhtml (best-effort) ----"
|
|
# wkhtmltopdf ne publie AUCUN paquet openSUSE. Le dépôt de la distribution
|
|
# fournit « wkhtmltopdf » quand il existe pour l'architecture ; sinon on
|
|
# renonce, Odoo sait imprimer sans lui (rendu dégradé des PDF).
|
|
zyp_soft wkhtmltopdf || echo "wkhtmltopdf non installe (optionnel)."
|
|
else
|
|
echo -e "\n---- Already installed wkhtml ----"
|
|
fi
|
|
fi
|
|
|
|
echo -e "\n---- SUSE dependency installation done ----"
|