# -*-eselect-*- vim: ft=eselect # Copyright 2022 Gentoo Authors # Distributed under the terms of the GNU GPL version 2 or later DESCRIPTION="Manage active Wine slots and variants" MAINTAINER="wine@gentoo.org" VERSION="2.0.0" # Setup env and run $@ in ${EROOT}${WINEETC} to simplify path # handling (all other wine-* functions must be ran from this). # Passing '-r' skips the write sanity check. wine-run() { local WINEETC=/etc/eselect/wine local WINEREL=../../.. [[ -d ${EROOT}${WINEETC} ]] || mkdir -p -- "${EROOT}${WINEETC}" pushd -- "${EROOT}${WINEETC}" >/dev/null || die -q "could not change directory to '${EROOT}${WINEETC}'" [[ ${1} == -r ]] && shift || [[ -w . ]] || die -q "No write access to '${PWD}'" local shopt_reset shopt_reset=$(shopt -p nullglob) shopt -s nullglob local -A WINEMAP wine-winemap "${@}" # hack wrt update given PATH is not updated in pkg_postinst (noisy) [[ ${PATH} == @(|*:)"${EPREFIX}${WINEETC}/bin"*(/)@(|:*) || \ ${1} == wine-update ]] || write_warning_msg "'${EPREFIX}${WINEETC}/bin' missing from PATH, may need to run '. ${EROOT}/etc/profile'" ${shopt_reset} popd >/dev/null || die } # Run $@ for each valid wine slot, and set: # dir_bin,inc,shr,win: relative path to bin, include, share and wine lib dir, # dir_wrp: relative path to wine wrappers location (/usr/bin) # slot: wine-- # suffix: - # variant: # is_last: true if last slot, false otherwise # is_last_variant: true if last of a variant, false otherwise # Returns 1 if no valid slots were found. wine-foreach_slot() { # try non-POSIX -V to roughly sort by version, keep glob order if can't local fe_sorted fe_dirs=("${WINEREL}"/usr/lib/wine-*-[0-9]*) mapfile -t fe_sorted < <(printf "%s\n" "${fe_dirs[@]}" | sort -V 2>/dev/null) (( ${#fe_sorted[@]} == ${#fe_dirs[@]} )) || fe_sorted=("${fe_dirs[@]}") local slot variant dir_bin dir_inc dir_shr dir_win local is_last=false is_last_variant=false fe_run=false fe_valid=false local fe_dir fe_newslot fe_newvariant fe_newsuffix for fe_dir in "${fe_sorted[@]}"; do [[ ${fe_dir} =~ /(wine-(([^-]+)-[0-9][^/]*))$ ]] || continue fe_newslot=${BASH_REMATCH[1]} fe_newsuffix=${BASH_REMATCH[2]} fe_newvariant=${BASH_REMATCH[3]} if ${fe_run}; then fe_run=false [[ ${fe_newvariant} != "${variant}" ]] && is_last_variant=true "${@}" is_last_variant=false fi slot=${fe_newslot} suffix=${fe_newsuffix} variant=${fe_newvariant} dir_bin=${fe_dir}/bin dir_inc=${WINEREL}/usr/include/${slot} dir_shr=${WINEREL}/usr/share/${slot} dir_win=${fe_dir}/wine dir_wrp=${WINEREL}/usr/bin [[ -e ${dir_bin}/wine && -d ${dir_inc} && -d ${dir_shr} && \ -d ${dir_win} && -d ${dir_wrp} ]] || continue wine-legacy_checks || continue fe_run=true fe_valid=true done is_last=true is_last_variant=true ${fe_run} && "${@}" ${fe_valid} } # Remove all known files we can create wine-reset() { local p rm=() rmdir=() for p in bin/* bin include share wine wine.conf; do # only wildcard delete links + empty dirs if [[ -L ${p} || ( ${p} == @(bin/wine|wine.conf) && -e ${p} ) ]]; then rm+=("${p}") elif [[ -d ${p} ]]; then rmdir+=("${p}") fi done (( ! ${#rm[@]} )) || rm -- "${rm[@]}" && (( ! ${#rmdir[@]} )) || rmdir -- "${rmdir[@]}" || die -q "failed to remove some files from '${PWD}', please cleanup" } wine-reset-feedback() { wine-reset write_warning_msg "Removed all known files from '${PWD}'" } # Set WINEMAP associative array for current slot selections, # with 'main' being what you get if call 'wine' without suffix. # e.g. ${WINEMAP[vanilla]} == wine-vanilla-7.0 wine-winemap() { WINEMAP=() if [[ -f wine.conf ]]; then local wine while read -r wine; do if [[ ${wine} =~ ^wine-([^-]+)-[0-9].* ]]; then WINEMAP[${BASH_REMATCH[1]}]=${wine} [[ -v WINEMAP[main] ]] || WINEMAP[main]=${wine} fi done < wine.conf || die else wine-legacy_winemap fi } # Store WINEMAP to wine.conf wine-winemap_save() { ( echo "# auto-generated by eselect-wine-${VERSION}, do not edit" [[ -v WINEMAP[main] ]] && echo "${WINEMAP[main]}" unset 'WINEMAP[main]' (( ${#WINEMAP[@]} )) && printf '%s\n' "${WINEMAP[@]}" : ) > wine.conf || die } ### eselect commands describe_list() { echo "List available Wine slots and variants"; } describe_list_parameters() { echo "[variant]"; } describe_list_options() { echo "variant: Variant to list (all if ommitted)"; } do_list() { wine-run -r wine-list "${@}"; } wine-list() { (( ${#} < 2 )) || die -q "Too many arguments" local target local -i count=0 write_list_start "Available Wine slots${1:+ for ${1}}:" wine-list_foreach() { count+=1 [[ ${1} == all || ${1} == "${variant}" ]] || return 0 target=${slot} [[ -v 'WINEMAP[${variant}]' && ${WINEMAP[${variant}]} == "${slot}" ]] && target=$(highlight_marker "${target}") [[ -v WINEMAP[main] && ${WINEMAP[main]} == "${slot}" ]] && ! is_output_mode brief && target+=" ($(highlight main))" write_numbered_list_entry ${count} "${target}" ${is_last_variant} && ! ${is_last} && [[ ${1} == all ]] && ! is_output_mode brief && echo } wine-foreach_slot wine-list_foreach "${1:-all}" [[ -v target ]] || write_numbered_list -m "(none found)" } describe_update() { echo "Auto-select highest slot for each Wine variants"; } describe_update_parameters() { echo "[--if-unset] [variant]"; } describe_update_options() { echo "--if-unset: Only update if selected slots are invalid" echo "variant: Only update the given variant, unless invalid" } do_update() { wine-run wine-update "${@}"; } wine-update() { local ifunset variant_only while (( ${#} )); do case ${1} in --if-unset) ifunset=1;; --reset) wine-reset-feedback; return;; # private option --all|--any|--d3d9|--proton|--staging|--vanilla);; # legacy --*) die -q "Unrecognized option '${1}'";; *) [[ ! -v variant_only ]] || die -q "Too many arguments" if [[ ${1} != all ]]; then ifunset=1 variant_only=${1} fi ;; esac shift done wine-reset local bin display wrap local -A is_done=() wine-update_foreach() { if ${is_last_variant} || [[ -v ifunset && ${variant_only} != "${variant}" && \ ${WINEMAP[${variant}]} == "${slot}" ]] && [[ ! -v 'is_done[${variant}]' ]] then [[ -d bin ]] || mkdir bin || die # wine does not react well to renames, so use variant wrappers for bin in "${dir_bin}"/*; do wrap=${dir_wrp}/${bin##*/}-${suffix} [[ ! -e ${wrap} ]] || ln -s -- "../${wrap}" "bin/${bin##*/}-${variant}" || die done display= [[ ${WINEMAP[${variant}]} != "${slot}" ]] && display="${slot} is set as ${variant} provider (auto)" if ${is_last} || { ${is_last_variant} && [[ ${WINEMAP[main]} == "${WINEMAP[${variant}]}" ]]; } || [[ -v ifunset && ${variant_only} != "${variant}" && \ ${WINEMAP[main]} == "${slot}" ]] && [[ ! -v is_done[main] ]] then # could skip wrappers here, but use for consistency for bin in "${dir_bin}"/*; do wrap=${dir_wrp}/${bin##*/}-${suffix} [[ ! -e ${wrap} ]] || ln -s -- "../${wrap}" "bin/${bin##*/}" || die done ln -s -- "${dir_inc}" include || die ln -s -- "${dir_shr}" share || die ln -s -- "${dir_win}" wine || die [[ ${WINEMAP[main]} != "${slot}" ]] && display="${slot} is set as the main provider (auto)" is_done[main]=1 WINEMAP[main]=${slot} fi [[ ${display} ]] && echo "${display}" is_done[${variant}]=1 WINEMAP[${variant}]=${slot} fi } if wine-foreach_slot wine-update_foreach; then # cleanup unused before saving local variant for variant in "${!WINEMAP[@]}"; do [[ -v 'is_done[${variant}]' ]] || unset "WINEMAP[${variant}]" done wine-winemap_save else # no wine slots, keep placeholders to avoid broken references mkdir -p bin include share wine printf "#!/usr/bin/env sh\necho 'Error: Wine is not setup (eselect-wine)' >&2\nexit 1\n" \ > bin/wine chmod +x bin/wine fi } describe_show() { echo "Show the active Wine slot"; } describe_show_parameters() { echo "[variant]"; } describe_show_options() { echo "variant: Variant to show, e.g. vanilla (main if ommitted)"; } do_show() { wine-run -r wine-show "${@}"; } wine-show() { (( ${#} < 2 )) || die -q "Too many arguments" if [[ -v 'WINEMAP[${1:-main}]' ]]; then echo "${WINEMAP[${1:-main}]}" else write_warning_msg "no slot is selected${1:+ for ${1}}" fi } describe_set() { echo "Set active Wine slots and variants"; } describe_set_parameters() { echo "[--only] "; } describe_set_options() { echo "--only: Only set the variant's slot" echo "target: Target name or number (from 'list' action)" } do_set() { wine-run wine-set "${@}"; } wine-set() { local chosen_slot only while (( ${#} )); do case ${1} in --only) only=1;; --*) die -q "Unrecognized option '${1}'";; *) [[ ! -v chosen_slot ]] || die -q "Too many arguments" chosen_slot=${1} ;; esac shift done [[ -v chosen_slot ]] || die -q "Please specify a target to activate" local -i chosen_num count=1 printf -v chosen_num %u "${chosen_slot}" 2>/dev/null && unset chosen_slot local display wine-set_foreach() { if (( chosen_num == count++ )) || [[ ${chosen_slot} == "${slot}" ]]; then display="${slot} is set the ${variant} provider" [[ ${WINEMAP[${variant}]} == "${slot}" ]] && display="${slot} is already set as the ${variant} provider" WINEMAP[${variant}]=${slot} if [[ ! -v only ]]; then display="${slot} is set as the main provider" [[ ${WINEMAP[main]} == "${slot}" ]] && display="${slot} is already set as the main provider" WINEMAP[main]=${slot} fi fi } wine-foreach_slot wine-set_foreach [[ -v display ]] || die -q "Target '${chosen_slot:-${chosen_num}}' was not found (select from 'list' action)" wine-update --if-unset echo "${display}" } ### legacy junk beyond here waiting for cleanup (~2025?) wine-legacy_checks() { # need to be ran in postrm, not pre.. so we need a way to know we # should ignore the slot that's about to be removed (not the end # of the world if this fails so rely on dodgy methods, dead links # will be fixed on the next `eselect wine update`) if [[ ${EBUILD_PHASE_FUNC} == pkg_prerm && ${P} == "${slot}" ]]; then return 1 fi if [[ -d ${WINEREL}/usr/lib64/${slot} ]]; then write_warning_msg "ignored legacy installation at ${EROOT}/usr/lib64/${slot}" [[ -e ${WINEREL}/usr/bin/${slot} ]] && write_warning_msg "should still be usable by calling versioned ${EROOT}/usr/bin/${slot}" return 1 fi } inherit config wine-legacy_winemap() { # this is created by the eselect-wine ebuild to keep same slots if [[ -f eselect-wine-migration ]]; then local active variant for variant in proton staging vanilla wine; do active=$(load_config eselect-wine-migration ${variant}) [[ ${variant} == wine ]] && variant=main [[ ${active} ]] && WINEMAP[${variant}]=${active} done fi } # no eselect method to hide options, so describe as deprecated describe_deregister() { echo "(deprecated)"; } do_deregister() { :; } describe_register() { echo "(deprecated)"; } do_register() { write_warning_msg "'eselect wine register' is deprecated, please migrate to:" echo ' IDEPEND=">=app-eselect/eselect-wine-2" pkg_postinst() { eselect wine update --if-unset || die } pkg_postrm() { # /not/ prerm eselect wine update --if-unset || die } ' >&2 }