source: BLFS/libs/func_dependencies@ 7ac11f0

2.4 ablfs-more legacy new_features trunk
Last change on this file since 7ac11f0 was 7ac11f0, checked in by Pierre Labastie <pierre@…>, 9 years ago

Fix a long standing bug preventing the chosen MTA to be used and improve
some comments

  • Property mode set to 100644
File size: 11.9 KB
RevLine 
[3c10176]1#!/bin/bash
2#
3# $Id$
4#
5
[e4b1293]6#---------------------------------------------------------------------------#
7# This is a set of (recursive) functions for manipulating a dependency #
8# tree. Everything would be "simple" without circular dependencies. We #
9# would just have to build the tree using the packages.xml file, and to #
[7f9fa78]10# provide a function for browsing it. But we need to be able to detect #
[7dc8595]11# circular dependencies and to possibly change the tree depending on #
12# priorities. This is why we keep with each node a record of the path #
13# from the root to the node, which we call *link* and a record of the #
14# successive priorities which we call *priolink*. #
15# #
[e4b1293]16# Layout of the tree: #
[7dc8595]17# #
18# A node of the tree is represented by a file <nodeName>.dep. We keep all #
19# those files in the same directory. The root node file is "root.dep". #
20# Files <nodeName>.dep have the following layout: #
21# - the first line is the link: the link is an array of numbers #
22# (n1 n2 ... nN), describing the path from the root to <nodeName>: n1 #
23# is the position of the first node of the path in root.dep, n2 is the #
24# position of the second node of the path in <node1>.dep and so on. The #
25# link is not needed for normal tree operations (building a subtree or #
26# browsing the tree), but it allows to check whether a dependency is #
27# circular, and to find its parent. #
28# - the second line is an array of priorities (p1 p2 ... pN), giving the #
29# priority (1=required, 2=recommended, 3=optional) of each dependency #
30# in the link. #
31# - each subsequent line is of the form "p <depname>", where p is the #
32# priority as above, and <depname> is the name of the dependency. The #
33# position which is recorded in the link is the number of the line #
34# minus 2. #
35# #
[d7c07e0]36# Circular dependencies: #
[7dc8595]37# #
[d7c07e0]38# In case we find a cirdular dependency, it has the form : #
39# parent->dependency_0->...->dependency_n->dependency_0 #
40# If we want to build dependency_n before dependency_0, no problem: #
41# we just prune the tree at dependency_n. If we want to build first #
42# dependency_0, we need to put dependency_n as a dependency of parent, #
43# then erase and rebuild the subtree from there. Now, we may have met #
44# another circular dependency in the subtree, and erasing the tree makes #
45# us forget the decision which was made. So, after first generating the #
46# list of dependencies from packages.xml, we keep the generated list in #
47# a file <nodeName>.odep, which we modify according to the decision which #
48# was made. #
[e4b1293]49#---------------------------------------------------------------------------#
50
51# Global variables:
[e576789]52# A string of spaces for indenting:
[3c10176]53declare -a spaceSTR=" "
[d7c07e0]54# When we are backing up from a circular dependency, `exchange_triplet'
[7dc8595]55# shall contain (parent dependency_0 dependency_n):
56declare -a exchange_triplet
[3c10176]57
58#----------------------------#
59generate_dependency_tree() { #
60#----------------------------#
61: <<inline_doc
[e576789]62 function: Create a subtree of the dependency tree
63 (recursive function)
[7f9fa78]64 input vars: $1 : file with a list of targets (child nodes)
[e4b1293]65 the first line of the file is the link
[7dc8595]66 $2 : priority (1=req, 2=rec, 3=opt)
[7ac11f0]67 externals: vars: DEP_LEVEL contains 1 if we want to build the
68 tree only for required dependencies,
69 2 if we want also recommended ones,
70 and 3 if we want optional ones too.
71 MAIL_SERVER contains the name of the MTA we want to use.
72 files: ../xsl/dependencies.xsl: stylesheet for creating the
73 .dep files
74 ../packages.xml: Files containing packages id
75 and dependencies
[e4b1293]76 returns: 0 if the tree has been successfully created
77 1 if we are backing up to the parent of a circular dep
[7f9fa78]78 modifies: vars: exchange_triplet contains the triplet when return is 1
[e4b1293]79 output: files: for each <pkg> with dependencies in $1,
80 a file <pkg>.dep and its dependencies
[3c10176]81 on error: nothing
82 on success: nothing
83inline_doc
84
[e576789]85local DepFile=$1
[7dc8595]86local priority=$2
[e576789]87local -a rootlink
[7dc8595]88local -a priolink
[e576789]89local -a otherlink
90local -i depth
91local -i count=0
92local id_of_dep
93local parent
94local lines_to_remove=
95local srootlink
96local dep_level
[7dc8595]97local priostring
98local dpriostring
99local i
[e576789]100
101{
102# We use fd number 6 for input from DepFile, because we need 0 for user input
103read -u6 -a rootlink
104depth=${#rootlink[*]}
[7dc8595]105read -u6 -a priolink
[e576789]106dep_level=$DEP_LEVEL
[1670a20]107# For now, process optional deps only for the root packages.
[e576789]108if (( $DEP_LEVEL > 2 )) && (( $depth > 1 )); then dep_level=2; fi
109srootlink="${rootlink[*]} "
[7dc8595]110case $priority in
111 1) priostring=required ;;
112 2) priostring=recommended ;;
113 3) priostring=optional ;;
114esac
[d7c07e0]115# start of DepFile
[7dc8595]116echo -en "\nNode: $depth${spaceSTR:0:$depth}${RED}$DepFile${OFF} $priostring"
[e576789]117
[7dc8595]118while read -u6 prio_of_dep id_of_dep; do
119case $prio_of_dep in
120 1) dpriostring=required ;;
121 2) dpriostring=recommended ;;
122 3) dpriostring=optional ;;
123esac
[e576789]124# count entries in file
125 (( count++ ))
126# Has this entry already been seen?
127 if [ -f ${id_of_dep}.dep ]; then # found ${id_of_dep}.dep already in tree
128 otherlink=($(head -n 1 ${id_of_dep}.dep))
129 if [ -z "${otherlink[*]}" ]
130 then echo otherlink empty for $id_of_dep.dep
131 echo This should not happen, but happens to happen...
132 exit 1
133 fi
134# Do not use "${rootlink[*]}" =~ "${otherlink[*]}": case rootlink=(1 11)
135# and otherlink=(1 1)
[e4b1293]136 if [[ ${srootlink#"${otherlink[*]} "} != ${srootlink} ]]; then # cir. dep
[7dc8595]137 echo -en "\nCirc: $((depth+1))${spaceSTR:0:$((depth+1))}${YELLOW}${id_of_dep}${OFF} $dpriostring"
[e576789]138# First look for the other parent of this dependency.
139# The parent has the same link without the last entry.
140# We do not need otherlink anymore so just destroy the last element
[7dc8595]141 unset otherlink[-1]
[e576789]142 parent=$(grep ^"${otherlink[*]}"\$ -l *)
143 parent=${parent%.dep}
[7dc8595]144# Find lowest priority in branch from parent to DepFile:
145 p2=0
146 for (( i=${#otherlink[*]}; i < $depth ; i++ )) ; do
147 if (( ${priolink[i]} > $p2 )); then p2=${priolink[i]}; fi
148 done
149 if (( $prio_of_dep >= $p2 )); then # prune
[e4b1293]150 lines_to_remove="$lines_to_remove $id_of_dep"
[d7c07e0]151 sed -i "/$id_of_dep/d" ${DepFile/.dep/.odep}
[7dc8595]152 else #backup with prio priority
153 exchange_triplet=($parent $id_of_dep ${DepFile%.dep})
154 return $priority
[e4b1293]155 fi
[7dc8595]156 else # not circular: prune tree (but not .odep, since it may happen that
[7ac11f0]157 # the tree is destroyed and rebuilt in another order)
[e576789]158 lines_to_remove="$lines_to_remove $id_of_dep"
[e4b1293]159 fi # circular or not
160 continue # this dependency has already been seen, and the associated
161 # subtree computed. We are done
162 fi # Has this entry already been seen?
163# So, this entry has not already been seen. Let's build the corresponding
164# subtree. Since decisions about circular deps can lead us to start again
165# dependencies, we restart until the flag is false.
[e576789]166 flag=true
167 while [ $flag = true ]; do
168 flag=false
[d7c07e0]169 if [ ! -f "${id_of_dep}.odep" ]; then
170 xsltproc --stringparam dependencies ${dep_level} \
[e576789]171 --stringparam idofdep $id_of_dep \
[7ac11f0]172 --stringparam MTA $MAIL_SERVER \
[d7c07e0]173 -o ${id_of_dep}.odep \
[e576789]174 ../xsl/dependencies.xsl ../packages.xml
[d7c07e0]175 fi
[e576789]176
[d7c07e0]177# Use -s, because it may happen that after removing lines, .odep exists
178# but is empty.
179 if [[ -s ${id_of_dep}.odep ]]; then # this dependency has dependencies
[7dc8595]180 sed "1i${rootlink[*]} $count\\
181${priolink[*]} $prio_of_dep" < ${id_of_dep}.odep \
182 > ${id_of_dep}.dep # add link and priolink
183 generate_dependency_tree ${id_of_dep}.dep $prio_of_dep
[e576789]184# Test return value, in case we exchange dependencies
[7dc8595]185 p2=$?
186 case $p2 in
[e576789]187 0) # Normal return
188 ;;
[7dc8595]189 [123]) # We are backing up to parent
190 if [[ ${exchange_triplet} == ${DepFile%.dep} ]] ; then
191# We are the parent!
192# First, we have to find the parent of our new direct dep, and remove
193# the new direct dep from the parent.odep:
194 otherlink=($(head -n1 ${exchange_triplet[2]}.dep))
195 unset otherlink[-1]
196 parent=$(grep -l ^"${otherlink[*]}"\$ *.dep)
197 sed -i /[[:digit:]]\ ${exchange_triplet[2]}\$/d ${parent/.dep/.odep}
198 tree_erase ${id_of_dep}.dep
[e4b1293]199# We want that our direct dep be ${exchange_triplet[2]} and that id_of_dep
200# be pulled in as an indirect dep, so exchange.
[e576789]201# Just doing a sed -i "s@${id_of_dep}@${exchange_triplet[2]}@" $DepFile
202# is not good if $DepFile contains several times the same line
203# so first find the first line and then sed
204 lineno=$(sed -n /${id_of_dep}/= $DepFile | head -n1)
205 sed -i "${lineno}s@${id_of_dep}@${exchange_triplet[2]}@" $DepFile
[d7c07e0]206# Do the same for the odep file:
207 local OdepFile=${DepFile/.dep/.odep}
208 lineno=$(sed -n /${id_of_dep}/= $OdepFile | head -n1)
209 sed -i "${lineno}s@${id_of_dep}@${exchange_triplet[2]}@" $OdepFile
[e576789]210 id_of_dep=${exchange_triplet[2]}
[e4b1293]211 flag=true # we have to regenerate the tree for the new dependency
[7dc8595]212 else
213# We are not the parent. If our priority is greater than p2
214# we have to change the triplet and return priority, else return current p2.
215# echo (DEBUG) backing up to ${exchange_triplet} at ${DepFile%.dep}
216 if (( $priority > $p2 )); then
217 exchange_triplet[2]=${DepFile%.dep}
218 return $priority
219 else
220 return $p2
221 fi
[e576789]222 fi
223 ;;
[3c10176]224 esac
[e4b1293]225 else # id_of_dep has no dependencies, just record the link in a file
226 # and print
[e576789]227 echo "${rootlink[*]} $count" > ${id_of_dep}.dep
[7dc8595]228 echo -en "\nLeaf: $(($depth+1))${spaceSTR:0:$(($depth+1))}${CYAN}${id_of_dep}${OFF} $dpriostring"
[3c10176]229 fi
230 done
[e576789]231done
232echo -en "\n End: $depth${spaceSTR:0:$depth}${GREEN}$DepFile${OFF}"
233} 6<$DepFile
234# It may happen that a file is created with several times
235# the same line. Normally, all those lines but one
236# would be flagged to be removed (or all of them if
[e4b1293]237# the dependency appeared before). A simple sed /$line/d
[e576789]238# destroys all the lines. We should instead remove
239# only one for each appearance of it in lines_to_remove.
[e4b1293]240# so first get the position of last line and then delete
[e576789]241# that line
242for line in $lines_to_remove
[7dc8595]243 do lineno=$(sed -n /^[[:digit:]]\ $line\$/= $DepFile | tail -n1)
[e576789]244 sed -i ${lineno}d $DepFile
245done
246return 0
247}
[3c10176]248
[e576789]249#---------------#
250tree_browse() { #
251#---------------#
252local file=$1
253local f
254
255#echo file=$file
[7dc8595]256for f in $(grep '[^0-9 ]' $file | sed 's/.* //'); do
[e576789]257# echo f=$f
258 if grep -q '[^0-9 ]' ${f}.dep ; then
259 tree_browse ${f}.dep
[3c10176]260 fi
[e576789]261 echo $f
262done
263}
[3c10176]264
[e576789]265#--------------#
266tree_erase() { #
267#--------------#
268local file=$1
269local f
270local -a rootlink
271local -a rootlink2
272
273#echo file=$file
274rootlink=($(head -n1 $file))
[7dc8595]275for f in $(grep '[^0-9 ]' $file | sed 's/.* //'); do
[e576789]276# echo " f"=$f
277 if [ -f ${f}.dep ]; then
278 rootlink2=($(head -n1 ${f}.dep))
279 if [[ "${rootlink2[*]}" =~ "${rootlink[*]}" ]] ; then
280 tree_erase ${f}.dep
281 fi
282 fi
283done
284rm -f $file
[3c10176]285}
Note: See TracBrowser for help on using the repository browser.