#!/bin/sh
#
# Copyright (C) 2020, 2021  Etersoft
# Copyright (C) 2020, 2021  Vitaly Lipatov <lav@etersoft.ru>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#

load_helper epm-sh-altlinux
load_helper epm-assure

# file prefix suffix
__epm_restore_print_comment()
{
    echo "#$2 generated by 'epm restore --dry-run' from $(basename $(dirname $(realpath "$1")))/$(basename "$1")$3"
}

# FIXME: python modules are packaged into python packages, but we have only module names and rpm package names instead of python packages
# enable python3dest(PEP-503 normalized name) provides
# https://bugzilla.altlinux.org/show_bug.cgi?id=39003
__epm_filter_pip_to_rpm()
{
    tr "A-Z" "a-z" | sed -e "s|_|-|g" -e "s|^python[-_]||" -e "s|python$||" \
        -e "s|bs4|beautifulsoup4|" \
        -e "s|pillow|Pillow|" \
        -e "s|sqlalchemy|SQLAlchemy|" \
        -e "s|flask-SQLAlchemy|flask_sqlalchemy|" \
        -e "s|redis|redis-py|" \
        -e "s|pyjwt|jwt|" \
        -e "s|pymonetdb|monetdb|" \
        -e "s|pyyaml|yaml|" \
        -e "s|flask-migrate|Flask-Migrate|" \
        -e "s|twisted|twisted-core|" \
        -e "s|pymacaroons|pymacaroons-pynacl|" \
        -e "s|pygments|Pygments|" \
        -e "s|memcached|memcache|" \
        -e "s|pyinstaller||" \
        -e "s|pyopenssl|OpenSSL|"
}

# TODO: remove me
fill_sign()
{
    local sign="$1"
    echo "$2" | grep -E -- "$sign[[:space:]]*[0-9.]+?" | sed -E -e "s|.*$sign[[:space:]]*([0-9.]+?).*|\1|"
}


# Whether the current PMTYPE actually enforces version constraints passed to "install".
# apt-rpm/apt-dpkg/apk parse and respect "name>=version" / "name==version".
# dnf/yum CLI does NOT honor range operators (ansible/ansible#78295) and rejects the glued
# "name>=version" form; only exact "name-version" matches, which is a different semantics.
# For everything that doesn't enforce, restore drops version constraints from the install args.
__epm_pmtype_enforces_install_version()
{
    case $PMTYPE in
        apt-rpm|apm-rpm|apt-dpkg|aptitude-dpkg|apk) return 0 ;;
        *) return 1 ;;
    esac
}

# Convert dry-run rpm-notation "name >= version" lines into epm install args.
# On version-enforcing PMTYPEs: glue spaces ("name >= version" -> "name>=version") — rpm names
# and versions never contain whitespace, so this is safe.
# Elsewhere: drop the constraint and keep only the package name.
__epm_restore_glue_version()
{
    if __epm_pmtype_enforces_install_version ; then
        sed -e 's| ||g'
    else
        cut -d' ' -f 1
    fi
}

# Emit one dependency line in the current mode.
# In dry-run: print "<macro> <name> [<vspec>]" (spec notation, version always shown).
# In install: print the install arg — "<name><vspec-glued>" when the current PMTYPE
# enforces range constraints, else just "<name>". (stdout carries the install args;
# callers capture them with $().)
# $1 = rpm macro ("Requires:" / "BuildRequires:" / ""), $2 = name, $3 = vspec (">= 1.5") or empty.
__epm_restore_emit()
{
    local macro="$1" name="$2" vspec="$3"
    if [ -n "$dryrun" ] ; then
        echo "$macro $name${vspec:+ $vspec}"
    elif [ -n "$vspec" ] && __epm_pmtype_enforces_install_version ; then
        echo "$name$(printf '%s' "$vspec" | tr -d ' ')"
    else
        echo "$name"
    fi
}

# macro pkg caseline
__epm_pi_sign_to_rpm()
{
    local t="$1"
    local l="$2"
    local equal="$3"
    [ -n "$equal" ] || equal=">="

    local pi=''
    local sign ll
    for sign in "<=" "<" ">=" ">" "==" "!=" "~="; do
        ll=$(fill_sign "$sign" "$l")
        [ -n "$ll" ] || continue
        [ "$sign" = "==" ] && sign="$equal"
        [ "$sign" = "~=" ] && sign="$equal"
        [ "$sign" = "!=" ] && sign=">="
        [ -n "$pi" ] && pi="$pi
"
        pi="$pi$t $sign $ll"
    done
    [ -n "$pi" ] || pi="$t"
    echo "$pi"
}

__epm_get_array_name()
{
    echo "$*" | grep "=" | head -n1 | sed -e 's| *=.*||'
}

__epm_lineprint_python_array()
{
    local a="$*"
    [ -n "$a" ] || return
    local name="$(__epm_get_array_name "$a")"
    (echo "$a" | sed -E -e 's@(\]|\)).*@\1@' ; echo "print('\n'.join($name))" ) | ( a= python3 - || a= python - )
}

# translate pip requirement lines to rpm notation
# (compare signs and package names)
__epm_restore_convert_to_rpm_notation()
{
    local equal="$1"
    local l
    while read l ; do
        if echo "$l" | grep -q 'platform_python_implementation != "PyPy"' ; then
            [ -n "$verbose" ] && warning '    $t is not PyPi requirement, skipped'
            continue
        fi
        if echo "$l" | grep -q 'sys_platform == "darwin"' ; then
            [ -n "$verbose" ] && warning '    $t is darwin only requirement, skipped'
            continue
        fi
        if echo "$l" | grep -q 'sys_platform == "win32"' ; then
            [ -n "$verbose" ] && warning '    $t is win32 only requirement, skipped'
            continue
        fi
        if echo "$l" | grep -q "; *python_version *< *['\"]3" ; then
            [ -n "$verbose" ] && warning '    $t is python2 only requirement, skipped'
            continue
        fi
        if echo "$l" | grep -q "; *python_version *<= *['\"]2\." ; then
            [ -n "$verbose" ] && warning '    $t is python2 only requirement, skipped'
            continue
        fi
        # drop various "python_version > '3.5'"
        l="$(echo "$l" | sed -e "s| *;.*||")"
        if echo "$l" | grep -qE "^ *#" || [ -z "$l" ] ; then
            continue
        fi
        local t="$(echo "$l" | sed -E -e "s|[[:space:]]*[<>!=~]+.*||" -e "s| *#.*||" | __epm_filter_pip_to_rpm)"
        [ -n "$t" ] || continue
        # until new section
        if echo "$l" | grep -qE "^\[" ; then
            break
        fi
        # if dependency_links URLs, use egg name
        if echo "$l" | grep -qE "://" ; then
            if echo "$l" | grep -q "#egg=" ; then
                t="$(echo "$l" | sed -e "s|.*#egg=||" -e "s|\[.*||" | __epm_filter_pip_to_rpm)"
            else
                warning "    skipping URL $l ..."
                continue
            fi
        fi

        __epm_pi_sign_to_rpm "$t" "$l" "$equal"
    done
}

__epm_restore_pip()
{
    local req_file="$1"
    local reqmacro
    local ilist

    if [ -n "$dryrun" ] ; then
        reqmacro="%py3_use"
        basename "$req_file" | grep -E -q "(dev|test|coverage)" && reqmacro="%py3_buildrequires"
        echo
        __epm_restore_print_comment "$req_file"
        cat $req_file | __epm_restore_convert_to_rpm_notation | sed -e "s|^|$reqmacro |"
        return
    else
        info 'Install requirements from $req_file ...'
        ilist="$(cat $req_file | __epm_restore_convert_to_rpm_notation | __epm_restore_glue_version | sed -e "s|^|python3-module-|")"
    fi

    ilist="$(estrlist list $ilist)"
    docmd epm install $ilist
}

__epm_restore_print_toml()
{
    local lt
    lt=$(mktemp) || fatal
    remove_on_exit $lt
cat <<EOF >$lt
#!//usr/bin/env python3

# https://realpython.com/python-toml/
import sys
import toml

if len(sys.argv) < 2:
	raise Exception('Run me with a file')

pyproject = sys.argv[1]

c = toml.load(pyproject)
try:
	n = c["tool"]["poetry"]["dependencies"]
	for key, value in n.items():
		if isinstance(value, dict):
			print('\n' + key + ' ' , value["version"])
		else:
			print('\n' + key + ' ' + value)
except:
	n = c["project"]["dependencies"]
	for t in n:
		print('\n' + t)
EOF
    a= python3 $lt "$1"
}

__epm_restore_print_pyproject()
{
    local req_file="$1"
    __epm_restore_print_toml "$req_file" | __epm_restore_convert_to_rpm_notation | sed -e 's|\*||' -e 's|\^|>= |'
}

__epm_restore_pyproject()
{
    local req_file="$1"
    local reqmacro
    local ilist

    if [ -n "$dryrun" ] ; then
        reqmacro="%py3_use"
        echo
        __epm_restore_print_comment "$req_file"
        __epm_restore_print_pyproject "$req_file" | sed -e "s|^|$reqmacro |"
        return
    else
        info "Install requirements from $req_file ..."
        ilist="$(__epm_restore_print_pyproject "$req_file" | __epm_restore_glue_version | sed -e "s|^|python3-module-|")"
    fi

    ilist="$(estrlist list $ilist)"
    docmd epm install $ilist
}

__eresection()
{
    rhas "$1" "[[:space:]]*$2[[:space:]]*=[[:space:]]*[\[(]"
}

__epm_restore_setup_py()
{
    local req_file="$1"
    if [ -z "$dryrun" ] ; then
        info "Install requirements from $req_file ..."
    fi

    local ar=''
    local ilist=''
    local reqmacro
    local section=''
    while read l ; do
        if rhas "$l" "^ *#" ; then
            continue
        fi
        # start of section
        if __eresection "$l" "REQUIREMENTS" ; then
            reqmacro="%py3_use"
            section="$l"
        fi
        if __eresection "$l" "install_requires" ; then
            reqmacro="%py3_use"
            section="$l"
        fi
        if __eresection "$l" "setup_requires" ; then
            reqmacro="%py3_buildrequires"
            section="$l"
        fi
        if __eresection "$l" "tests_require" ; then
            reqmacro="%py3_buildrequires"
            section="$l"
        fi
        if [ -n "$section" ] ; then
            ar="$ar
$l"
        fi

        # not end of section
        if [ -z "$section" ] || ! rhas "$l" "(\]|\)),*" ; then
            continue
        fi

        if [ -n "$dryrun" ] ; then
            echo
            __epm_restore_print_comment "$req_file" "" " $(__epm_get_array_name "$section")"
            __epm_lineprint_python_array "$ar" | __epm_restore_convert_to_rpm_notation ">=" | sed -e "s|^|$reqmacro |"
        else
            ilist="$ilist $(__epm_lineprint_python_array "$ar" | __epm_restore_convert_to_rpm_notation ">=" | __epm_restore_glue_version | sed -e "s|^|python3-module-|")"
        fi
        section=''
        ar=''
    done < $req_file

    if [ -n "$dryrun" ] ; then
        return
    fi

    ilist="$(estrlist list $ilist)"
    docmd epm install $ilist
}

__epm_print_npm_list()
{
    local reqmacro="$1"
    local req_file="$2"
    local l name ver vspec cleanver
    while read l ; do
        # "tap": "^14.10.7"
        echo "$l" | grep -q '"\(.*\)": "\(.*\)"' || continue
        name="$(echo "$l" | sed -e 's|.*"\(.*\)": ".*|\1|')"
        [ -z "$name" ] && continue
        ver="$(echo "$l" | sed -e 's|.*"\(.*\)": "\(.*\)".*|\2|')" #'
        # npm versions: ^X.Y.Z (caret), ~X.Y.Z (tilde), bare X.Y.Z → lower bound ">= X.Y.Z".
        vspec=''
        if echo "$ver" | grep -qE "^[\^~]?[0-9]" ; then
            cleanver="$(echo "$ver" | sed -e 's|^[\^~]||')"
            vspec=">= $cleanver"
        fi
        __epm_restore_emit "$reqmacro" "node-$name" "$vspec"
    done < $req_file
}


__epm_print_perl_list()
{
    local reqmacro="$1"
    local req_file="$2"
    local l name ver vspec
    for l in $(cat) ; do
        # perl(Class::ErrorHandler)>=0
        echo "$l" | grep -q '^perl(' || continue
        name="$(echo "$l" | sed -e 's|>=.*||' -e 's|::|/|g' -e 's|)|.pm)|')"
        [ -z "$name" ] && continue
        [ "$name" = "perl(perl.pm)" ] && continue
        ver="$(echo "$l" | sed -e 's|.*>=||')"
        vspec=''
        [ "$ver" = "0" ] || vspec=">= $ver"
        __epm_restore_emit "$reqmacro" "$name" "$vspec"
    done < $req_file
}


__epm_print_nupkg_list()
{
    local req_file="$1"
    local n name req other vspec
    a= dotnet list "$req_file" package | grep "^   > " | while read n name req other ; do
        vspec=''
        [ -n "$req" ] && vspec=">= $req"
        __epm_restore_emit "BuildRequires:" "nupkg($name)" "$vspec"
    done
}

__epm_restore_nupkg()
{
    local req_file="$1"
    if [ -n "$dryrun" ] ; then
        echo "# generated via dotnet list $(basename $(dirname $(realpath "$req_file")))/$(basename "$req_file") package"
        __epm_print_nupkg_list $req_file
        return
    fi

    info "Install requirements from $req_file ..."
    ilist=$(__epm_print_nupkg_list $req_file)
    ilist="$(estrlist list $ilist)"
    docmd epm install $ilist
}

__epm_print_meson_list()
{
    local reqmacro="$1"
    local req_file="$2"
    local name sign ver other vspec
    while read name sign ver other ; do
        # gtk4-wayland
        # gtk4 >= 4.6
        [ -n "$other" ] && continue
        vspec=''
        [ -n "$sign" ] && [ -n "$ver" ] && vspec="$sign $ver"
        __epm_restore_emit "$reqmacro" "pkgconfig($name)" "$vspec"
    done < $req_file
}

__epm_restore_meson()
{
    local req_file="$1"
# gtk_dep = dependency('gtk4', version: '>= 4.6')
# gtk_wayland_dep = dependency('gtk4-wayland', required: false)
# packagekit_dep = dependency('packagekit-glib2', version: '>= 1.2', required: get_option('packagekit'))
    if [ -n "$dryrun" ] ; then
        local lt
        lt=$(mktemp) || fatal
        remove_on_exit $lt
        echo
        __epm_restore_print_comment "$req_file" " dependency"
        grep "dependency(" $req_file | sed -e 's|.*dependency(||' -e 's|).*||' -e 's|, required.*||' -e 's|, version:||' -e "s|'||g" >$lt
        __epm_print_meson_list "BuildRequires:" $lt
        rm -f $lt
        return
    fi

    info "Install requirements from $req_file ..."
    local lt
    lt=$(mktemp) || fatal
    remove_on_exit $lt
    grep "dependency(" $req_file | sed -e 's|.*dependency(||' -e 's|).*||' -e 's|, required.*||' -e 's|, version:||' -e "s|'||g" >$lt
    ilist="$ilist $(__epm_print_meson_list "" $lt)"

    rm -f $lt
    docmd epm install $ilist

}


__epm_restore_npm()
{
    local req_file="$1"

    assure_exists jq

    if [ -n "$dryrun" ] ; then
        local lt
        lt=$(mktemp) || fatal
        remove_on_exit $lt
        a= jq .dependencies <$req_file >$lt
        echo
        __epm_restore_print_comment "$req_file"
        __epm_print_npm_list "Requires:" $lt

        echo
        __epm_restore_print_comment "$req_file" " devDependencies"
        a= jq .devDependencies <$req_file >$lt
        __epm_print_npm_list "BuildRequires:" $lt
        rm -f $lt
        return
    fi

    info "Install requirements from $req_file ..."
    local lt
    lt=$(mktemp) || fatal
    remove_on_exit $lt
    a= jq .dependencies <$req_file >$lt
    ilist="$(__epm_print_npm_list "" $lt)"
    a= jq .devDependencies <$req_file >$lt
    ilist="$ilist $(__epm_print_npm_list "" $lt)"
    rm -f $lt
    docmd epm install $ilist
}

# TODO: check 
__epm_restore_perl()
{
    local req_file="$1"

    if [ -n "$dryrun" ] ; then
        local lt
        lt=$(mktemp) || fatal
        remove_on_exit $lt
        a= /usr/bin/perl $req_file PRINT_PREREQ=1 >$lt
        # all requirements will autodetected during packing, put it to the buildreq
        echo
        __epm_restore_print_comment "$req_file"
        __epm_print_perl_list "BuildRequires:" $lt
        rm -f $lt
        return
    fi

    info "Install requirements from $req_file ..."
    local lt
    lt=$(mktemp) || exit
    remove_on_exit $lt
    a= /usr/bin/perl $req_file PRINT_PREREQ=1 >$lt
    ilist="$(__epm_print_perl_list "" $lt)"
    rm -f $lt
    docmd epm install $ilist
}

# Print cpanfile "requires" as "<phase>\t<Module>\t<spec>" lines (tab-separated).
# recommends/suggests and the perl core requirement are skipped here.
# cpanfile is a Perl DSL (with ternary version specs and on/feature blocks), so it
# cannot be parsed with regexps — Module::CPANfile evals it safely.
__epm_print_cpanfile_specs()
{
    # $1 = cpanfile
    a= /usr/bin/perl -MModule::CPANfile -e '
        my $f = shift;
        my $prereqs = Module::CPANfile->load($f)->prereqs;
        for my $phase (qw(runtime build configure test develop)) {
            my $req = eval { $prereqs->requirements_for($phase, "requires") };
            next unless $req;
            my $h = $req->as_string_hash;          # { Mod => ">= 1.85" | "4.00" | "0" }
            for my $mod (sort keys %$h) {
                next if $mod eq "perl";
                print "$phase\t$mod\t$h->{$mod}\n";
            }
        }
    ' "$1"
}

# Translate a cpanfile version spec (CPAN::Meta::Requirements string) into an rpm
# Requires constraint, or print nothing for "any version".
# Several constraints are joined by ", ". rpm Requires cannot express "!=" and cannot
# hold more than one operator per line, so drop "!=" and keep the first surviving
# constraint (the lower bound). Bare versions ("1.85") and "==" mean ">=".
__epm_cpanfile_spec_to_rpm()
{
    local spec="$1"
    [ -n "$spec" ] || return
    [ "$spec" = "0" ] && return

    local part saved ver
    local OLDIFS="$IFS"
    IFS=','
    for part in $spec ; do
        IFS="$OLDIFS"
        part="${part# }"; part="${part% }"
        [ -n "$part" ] || continue
        case "$part" in "!="*) continue ;; esac
        [ -n "$saved" ] && continue
        case "$part" in
            ">="*|">"*|"<="*|"<"*) saved="$part" ;;
            "=="*) ver="${part#??}"; ver="${ver# }"; saved=">= $ver" ;;
            "="*)  ver="${part#?}";  ver="${ver# }"; saved=">= $ver" ;;
            *)     saved=">= $part" ;;
        esac
    done
    IFS="$OLDIFS"
    [ -n "$saved" ] || return
    echo "$saved"
}

# Read "<phase>\t<Module>\t<spec>" lines (already sorted runtime-first).
# runtime requires -> Requires:, the rest (build/configure/test/develop) -> BuildRequires:.
# Emits each dependency via __epm_restore_emit (spec line in dry-run, install arg otherwise).
__epm_print_cpanfile_list()
{
    local req_file="$1"
    local phase mod spec name vspec macro
    while IFS="$(printf '\t')" read phase mod spec ; do
        case "$phase" in
            runtime) macro="Requires:" ;;
            *)       macro="BuildRequires:" ;;
        esac
        name="perl($(printf '%s' "$mod" | sed -e 's|::|/|g').pm)"
        vspec="$(__epm_cpanfile_spec_to_rpm "$spec")"
        __epm_restore_emit "$macro" "$name" "$vspec"
    done < $req_file
}

__epm_restore_cpanfile()
{
    local req_file="$1"

    # Module::CPANfile (ALT package perl-Module-CPANfile) is the canonical cpanfile parser
    if ! a= /usr/bin/perl -MModule::CPANfile -e 'exit 0' 2>/dev/null ; then
        info "Installing perl-Module-CPANfile to parse cpanfile ..."
        docmd epm install perl-Module-CPANfile || fatal "Can't install perl-Module-CPANfile"
    fi

    local lt
    lt=$(mktemp) || fatal
    remove_on_exit $lt
    __epm_print_cpanfile_specs "$req_file" >$lt

    if [ -n "$dryrun" ] ; then
        echo
        __epm_restore_print_comment "$req_file"
        __epm_print_cpanfile_list $lt
        rm -f $lt
        return
    fi

    info "Install requirements from $req_file ..."
    ilist="$(__epm_print_cpanfile_list $lt)"
    rm -f $lt
    ilist="$(estrlist list $ilist)"
    docmd epm install $ilist
}

__epm_restore_by()
{
    local req_file="$1"
    [ -n "$verbose" ] && info 'Checking for $req_file ...'
    [ -s "$req_file" ] || return
    if file $req_file | grep -q "ELF [3264]*-bit LSB executable" ; then
        assure_exists ldd-requires
        showcmd ldd-requires $req_file
        local TOINSTALL="$(a= ldd-requires $req_file | grep "^apt-get install" | sed -e "s|^apt-get install ||")"
        if [ -n "$dryrun" ] ; then
            estrlist list $TOINSTALL
            return
        fi
        [ -n "$TOINSTALL" ] || { info 'There are no missed packages is found for $req_file binary.' ; return ; }
        docmd epm install $TOINSTALL
        return
    fi

    case $req_file in
        requirements/default.txt|requirements/dev.txt|requirements/test.txt|requirements/coverage.txt)
            [ -s "$req_file" ] && __epm_restore_pip "$req_file" && return
            ;;
    esac

    case $(basename $req_file) in
        requirements.txt|dev-requirements.txt|requirements-dev.txt|requirements_dev.txt|requirements_test.txt|requirements-test.txt|test-requirements.txt|requires.txt)
            [ -s "$req_file" ] && __epm_restore_pip "$req_file"
            ;;
        setup.py|python_dependencies.py)
            [ -s "$req_file" ] && __epm_restore_setup_py "$req_file"
            ;;
        pyproject.toml)
            [ -s "$req_file" ] && __epm_restore_pyproject "$req_file"
            ;;
        package.json)
            [ -s "$req_file" ] && __epm_restore_npm "$req_file"
            ;;
        meson.build)
            [ -s "$req_file" ] && __epm_restore_meson "$req_file"
            ;;
#        META.yml)
#            [ -s "$req_file" ] && __epm_restore_perl "$req_file"
#            ;;
        Makefile.PL)
            # cpanfile is the canonical modern source (Makefile.PL is often generated
            # from it); if both are present, prefer cpanfile to avoid a second, less
            # precise dependency block.
            [ -s "$(dirname "$req_file")/cpanfile" ] && return
            [ -s "$req_file" ] && __epm_restore_perl "$req_file"
            ;;
        cpanfile)
            [ -s "$req_file" ] && __epm_restore_cpanfile "$req_file"
            ;;
        *.sln|*.csproj)
            local PROJ="$(echo $req_file)"
            [ -s "$PROJ" ] && __epm_restore_nupkg "$PROJ"
            ;;
        Gemfile|package.json)
            info "$req_file support is not implemented yet"
            ;;
    esac
}

epm_restore()
{
    # Requirement files or URLs given explicitly on the command line.
    if [ $# -gt 0 ] ; then
        local arg fn
        for arg in "$@" ; do
            if is_url "$arg" ; then
                fn="$(basename "$arg")"
                #assure eget
                [ -r "$fn" ] && fatal "File $fn is already exists in $(pwd)"
                info "Downloading '$fn' from '$arg' ..."
                eget "$arg"
                [ -s "$fn" ] || fatal "Can't download $fn from '$arg'"
                __epm_restore_by "$fn"
            elif [ -s "$arg" ] ; then
                __epm_restore_by "$arg"
            else
                fatal "Can't read requirements file '$arg'"
            fi
        done
        return
    fi

# TODO: nowhere works: python3 setup.py --requires

    # if run with empty args — auto-search known requirement files
    local i
    for i in requirements.txt requirements/default.txt requirements_dev.txt requirements-dev.txt requirements/dev.txt dev-requirements.txt \
             requirements-test.txt requirements_test.txt requirements/test.txt test-requirements.txt requirements/coverage.txt \
             Gemfile requires.txt package.json setup.py python_dependencies.py cpanfile Makefile.PL meson.build pyproject.toml \
             *.sln *.csproj ; do
        __epm_restore_by $i
    done

    # auto-search is best-effort; ignore the status of the last glob probe
    # (otherwise 'epm restore --dry-run' with no matching *.sln/*.csproj exits 1)
    return 0
}
