aboutsummaryrefslogtreecommitdiff
path: root/lib/cpt-deps
blob: 1429ae926d913560bd48ab5d463b54be3969479b (plain)
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# Dependeny and ordering functions

pkg_depends() {
    # Resolve all dependencies and generate an ordered list.
    # This does a depth-first search. The deepest dependencies are
    # listed first and then the parents in reverse order.
    contains "$deps" "$1" || {
        # Filter out non-explicit, aleady installed dependencies.
        # Only filter installed if called from 'pkg_build()'.
        [ "$pkg_build" ] && [ -z "$2" ] &&
            (pkg_list "$1" >/dev/null) && return

        while read -r dep type || [ "$dep" ]; do
            # Skip comments and empty lines.
            [ "${dep##\#*}" ] || continue
            # Skip test dependencies unless $CPT_TEST is set to 1.
            #
            # Skip make dependencies on the 'tree' operation for child packages
            # or when the 'first-nomake' argument is given.
            case $type in
                test) [ "$CPT_TEST" = 1 ] || continue ;;
                make) [ "$2" = tree ] && [ -z "${3#first-nomake}" ] && continue
            esac

            # Recurse through the dependencies of the child packages. Forward
            # the 'tree' operation.
            if [ "$2" = tree ]; then
                pkg_depends "$dep" tree
            else
                pkg_depends "$dep"
            fi
        done 2>/dev/null < "$(pkg_find "$1")/depends" ||:

        # After child dependencies are added to the list,
        # add the package which depends on them.
        [ "$2" = explicit ] || [ "$3" ] || deps="$deps $1 "
    }
}

pkg_order() {
    # Order a list of packages based on dependence and
    # take into account pre-built tarballs if this is
    # to be called from 'cpt i'.
    order=; redro=; deps=

    for pkg do case $pkg in
        *.tar.*) deps="$deps $pkg "  ;;
        *)       pkg_depends "$pkg" raw
    esac done

    # Filter the list, only keeping explicit packages.
    # The purpose of these two loops is to order the
    # argument list based on dependence.
    for pkg in $deps; do ! contains "$*" "$pkg" || {
        order="$order $pkg "
        redro=" $pkg $redro"
    } done

    deps=
}

pkg_fix_deps() {
    # Dynamically look for missing runtime dependencies by checking each binary
    # and library with either 'ldd' or 'readelf'. This catches any extra
    # libraries and or dependencies pulled in by the package's build suite.
    log "$1" "Checking for missing dependencies"

    # Go to the directory containing the built package to
    # simplify path building.
    cd "$pkg_dir/$1/$pkg_db/$1"

    # Make a copy of the depends file if it exists to have a
    # reference to 'diff' against.
    if [ -f depends ]; then
        cp -f depends "$mak_dir/d"
        dep_file=$mak_dir/d
    else
        dep_file=/dev/null
    fi

    # Generate a list of all installed manifests.
    pkg_name=$1
    set +f; set -f -- "$sys_db/"*/manifest

    # Get a list of binaries and libraries, false files
    # will be found, however it's faster to get 'ldd' to check
    # them anyway than to filter them out.
    find "$pkg_dir/$pkg_name/" -type f 2>/dev/null |

    while read -r file; do
        case ${elf_prog:-ldd} in
            *readelf) "$elf_prog" -d "$file" 2>/dev/null  ;;
            *)        ldd "$file" 2>/dev/null  ;;
        esac |
        while read -r dep; do
            # Skip lines containing 'ldd'.
            [ "${dep##*ldd*}" ] || continue
            case $dep in *NEEDED*\[*\] | *'=>'*) ;; *) continue; esac

            # readelf output:
            # 0x0000 (NEEDED) Shared library: [libc.so]
            dep=${dep##*\[}
            dep=${dep%%\]*}

            # ldd output:
            # libc.so => /lib/ld-musl-x86_64.so.1
            dep=${dep#* => }
            dep=${dep% *}

            # Figure out which package owns the file. Skip file if it is owned
            # by the current package. This also handles cases where a '*-bin'
            # package exists on the system, so the package manager doesn't think
            # that the package we are building depends on the *-bin version of
            # itself, or any other renamed versions of the same software.
            pkg_owner -l "/${dep#/}\$" "$PWD/manifest" >/dev/null && continue
            pkg_owner -l "/${dep#/}\$" "$@" ||:
        done ||:
    done >> depends

    # Remove duplicate entries from the new depends file.
    # This removes duplicate lines looking *only* at the
    # first column.
    sort -uk1,1 -o depends depends 2>/dev/null ||:

    # Display a diff of the new dependencies against the old ones.
    diff -U 3 "$dep_file" depends 2>/dev/null ||:

    # Remove the depends file if it is empty.
    [ -s depends ] || rm -f depends
}

pkg_get_base() (
    # Print the packages defined in the /etc/cpt-base file.
    # If an argument is given, it prints a space seperated list instead
    # of a list seperated by newlines.

    # cpt-base is an optional file, return with success if it doesn't exist.
    [ -f "$CPT_ROOT/etc/cpt-base" ] || return 0
    nonl=$1; set --

    # Older versions of shellcheck warns us that the variable is changing on the
    # subshell. That is our purpose here, thank you very much.
    # shellcheck disable=SC2030
    while read -r pkgname _; do
        [ "${pkgname##\#*}" ] || continue
        set -- "$@" "$pkgname"
    done < "$CPT_ROOT/etc/cpt-base"
    if [ "$nonl" ]; then printf '%s ' "$@"; else printf '%s\n' "$@"; fi
)

pkg_gentree() (
    # Generate an ordered dependency tree of a package. Useful for testing
    # whether the generated dependency tree is enough to actually building a
    # given package. A second argument can be given as a combination of
    # characters (similar to 'tar(1)' keys) which will be used as an option
    # parser. See the documentation for more information on the keys.
    # shellcheck disable=2030
    deps='' reverse='' nonl='' make_deps=first
    for op in $(sepchar "$2"); do
        case "$op" in
            b) deps="$(pkg_get_base nonl)" ;;
            x) make_deps=first-nomake ;;
            r) reverse=1 ;;
            n) nonl=1 ;;
            *) return 1
        esac
    done
    pkg_depends "$1" tree "${make_deps:+first}"
    eval set -- "$deps"
    pkg_order "$@"
    if [ "$reverse" ]; then eval set -- "$redro"; else eval set -- "$order"; fi
    if [ "$nonl" ];    then printf '%s ' "$@"; else printf '%s\n' "$@"; fi
)

# Local Variables:
# mode: sh
# End: