source: BLFS/libs/func_dependencies@ c650f9bf

new_features
Last change on this file since c650f9bf was 99ba6d8, checked in by Pierre Labastie <pierre@…>, 9 years ago

Merge trunk up to r3857

  • Property mode set to 100644
File size: 12.3 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
[e91a15d]74 ../packages.xml: File containing packages id
[7ac11f0]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 ;;
[e91a15d]114 4) priostring=external ;;
[7dc8595]115esac
[d7c07e0]116# start of DepFile
[7dc8595]117echo -en "\nNode: $depth${spaceSTR:0:$depth}${RED}$DepFile${OFF} $priostring"
[e576789]118
[7dc8595]119while read -u6 prio_of_dep id_of_dep; do
120case $prio_of_dep in
121 1) dpriostring=required ;;
122 2) dpriostring=recommended ;;
123 3) dpriostring=optional ;;
[e91a15d]124 4) dpriostring=external ;;
[7dc8595]125esac
[e576789]126# count entries in file
127 (( count++ ))
128# Has this entry already been seen?
129 if [ -f ${id_of_dep}.dep ]; then # found ${id_of_dep}.dep already in tree
130 otherlink=($(head -n 1 ${id_of_dep}.dep))
131 if [ -z "${otherlink[*]}" ]
132 then echo otherlink empty for $id_of_dep.dep
133 echo This should not happen, but happens to happen...
134 exit 1
135 fi
136# Do not use "${rootlink[*]}" =~ "${otherlink[*]}": case rootlink=(1 11)
137# and otherlink=(1 1)
[e4b1293]138 if [[ ${srootlink#"${otherlink[*]} "} != ${srootlink} ]]; then # cir. dep
[7dc8595]139 echo -en "\nCirc: $((depth+1))${spaceSTR:0:$((depth+1))}${YELLOW}${id_of_dep}${OFF} $dpriostring"
[e576789]140# First look for the other parent of this dependency.
141# The parent has the same link without the last entry.
142# We do not need otherlink anymore so just destroy the last element
[7dc8595]143 unset otherlink[-1]
[e576789]144 parent=$(grep ^"${otherlink[*]}"\$ -l *)
145 parent=${parent%.dep}
[7dc8595]146# Find lowest priority in branch from parent to DepFile:
147 p2=0
148 for (( i=${#otherlink[*]}; i < $depth ; i++ )) ; do
149 if (( ${priolink[i]} > $p2 )); then p2=${priolink[i]}; fi
150 done
151 if (( $prio_of_dep >= $p2 )); then # prune
[e4b1293]152 lines_to_remove="$lines_to_remove $id_of_dep"
[d7c07e0]153 sed -i "/$id_of_dep/d" ${DepFile/.dep/.odep}
[7dc8595]154 else #backup with prio priority
155 exchange_triplet=($parent $id_of_dep ${DepFile%.dep})
156 return $priority
[e4b1293]157 fi
[7dc8595]158 else # not circular: prune tree (but not .odep, since it may happen that
[7ac11f0]159 # the tree is destroyed and rebuilt in another order)
[e576789]160 lines_to_remove="$lines_to_remove $id_of_dep"
[e4b1293]161 fi # circular or not
162 continue # this dependency has already been seen, and the associated
163 # subtree computed. We are done
164 fi # Has this entry already been seen?
[e91a15d]165# So, this entry has not already been seen.
166# If this is an external dep, just display it and go to next dep:
167 if [ "$prio_of_dep" -eq 4 ]; then
168 echo "${rootlink[*]} $count" > ${id_of_dep}.dep
169 echo -en "\nLeaf: $(($depth+1))${spaceSTR:0:$(($depth+1))}${CYAN}${id_of_dep}${OFF} $dpriostring"
170 continue
171 fi
172# Otherwise, let's build the corresponding
[e4b1293]173# subtree. Since decisions about circular deps can lead us to start again
174# dependencies, we restart until the flag is false.
[e576789]175 flag=true
176 while [ $flag = true ]; do
177 flag=false
[d7c07e0]178 if [ ! -f "${id_of_dep}.odep" ]; then
179 xsltproc --stringparam dependencies ${dep_level} \
[e576789]180 --stringparam idofdep $id_of_dep \
[7ac11f0]181 --stringparam MTA $MAIL_SERVER \
[d7c07e0]182 -o ${id_of_dep}.odep \
[e576789]183 ../xsl/dependencies.xsl ../packages.xml
[d7c07e0]184 fi
[e576789]185
[d7c07e0]186# Use -s, because it may happen that after removing lines, .odep exists
187# but is empty.
188 if [[ -s ${id_of_dep}.odep ]]; then # this dependency has dependencies
[7dc8595]189 sed "1i${rootlink[*]} $count\\
190${priolink[*]} $prio_of_dep" < ${id_of_dep}.odep \
191 > ${id_of_dep}.dep # add link and priolink
192 generate_dependency_tree ${id_of_dep}.dep $prio_of_dep
[e576789]193# Test return value, in case we exchange dependencies
[7dc8595]194 p2=$?
195 case $p2 in
[e576789]196 0) # Normal return
197 ;;
[7dc8595]198 [123]) # We are backing up to parent
199 if [[ ${exchange_triplet} == ${DepFile%.dep} ]] ; then
200# We are the parent!
201# First, we have to find the parent of our new direct dep, and remove
202# the new direct dep from the parent.odep:
203 otherlink=($(head -n1 ${exchange_triplet[2]}.dep))
204 unset otherlink[-1]
205 parent=$(grep -l ^"${otherlink[*]}"\$ *.dep)
206 sed -i /[[:digit:]]\ ${exchange_triplet[2]}\$/d ${parent/.dep/.odep}
207 tree_erase ${id_of_dep}.dep
[e4b1293]208# We want that our direct dep be ${exchange_triplet[2]} and that id_of_dep
209# be pulled in as an indirect dep, so exchange.
[e576789]210# Just doing a sed -i "s@${id_of_dep}@${exchange_triplet[2]}@" $DepFile
211# is not good if $DepFile contains several times the same line
212# so first find the first line and then sed
213 lineno=$(sed -n /${id_of_dep}/= $DepFile | head -n1)
214 sed -i "${lineno}s@${id_of_dep}@${exchange_triplet[2]}@" $DepFile
[d7c07e0]215# Do the same for the odep file:
216 local OdepFile=${DepFile/.dep/.odep}
217 lineno=$(sed -n /${id_of_dep}/= $OdepFile | head -n1)
218 sed -i "${lineno}s@${id_of_dep}@${exchange_triplet[2]}@" $OdepFile
[e576789]219 id_of_dep=${exchange_triplet[2]}
[e4b1293]220 flag=true # we have to regenerate the tree for the new dependency
[7dc8595]221 else
222# We are not the parent. If our priority is greater than p2
223# we have to change the triplet and return priority, else return current p2.
224# echo (DEBUG) backing up to ${exchange_triplet} at ${DepFile%.dep}
225 if (( $priority > $p2 )); then
226 exchange_triplet[2]=${DepFile%.dep}
227 return $priority
228 else
229 return $p2
230 fi
[e576789]231 fi
232 ;;
[3c10176]233 esac
[e4b1293]234 else # id_of_dep has no dependencies, just record the link in a file
235 # and print
[e576789]236 echo "${rootlink[*]} $count" > ${id_of_dep}.dep
[7dc8595]237 echo -en "\nLeaf: $(($depth+1))${spaceSTR:0:$(($depth+1))}${CYAN}${id_of_dep}${OFF} $dpriostring"
[3c10176]238 fi
239 done
[e576789]240done
241echo -en "\n End: $depth${spaceSTR:0:$depth}${GREEN}$DepFile${OFF}"
242} 6<$DepFile
243# It may happen that a file is created with several times
244# the same line. Normally, all those lines but one
245# would be flagged to be removed (or all of them if
[e4b1293]246# the dependency appeared before). A simple sed /$line/d
[e576789]247# destroys all the lines. We should instead remove
248# only one for each appearance of it in lines_to_remove.
[e4b1293]249# so first get the position of last line and then delete
[e576789]250# that line
251for line in $lines_to_remove
[7dc8595]252 do lineno=$(sed -n /^[[:digit:]]\ $line\$/= $DepFile | tail -n1)
[e576789]253 sed -i ${lineno}d $DepFile
254done
255return 0
256}
[3c10176]257
[e576789]258#---------------#
259tree_browse() { #
260#---------------#
261local file=$1
262local f
263
264#echo file=$file
[7dc8595]265for f in $(grep '[^0-9 ]' $file | sed 's/.* //'); do
[e576789]266# echo f=$f
267 if grep -q '[^0-9 ]' ${f}.dep ; then
268 tree_browse ${f}.dep
[3c10176]269 fi
[e576789]270 echo $f
271done
272}
[3c10176]273
[e576789]274#--------------#
275tree_erase() { #
276#--------------#
277local file=$1
278local f
279local -a rootlink
[99ba6d8]280local rootlink2
[e576789]281
282#echo file=$file
283rootlink=($(head -n1 $file))
[7dc8595]284for f in $(grep '[^0-9 ]' $file | sed 's/.* //'); do
[e576789]285# echo " f"=$f
286 if [ -f ${f}.dep ]; then
[99ba6d8]287 rootlink2="$(head -n1 ${f}.dep) "
288# See comment above about srootlink
289 if [[ ${rootlink2#"${rootlink[*]} "} != ${rootlink2} ]] ; then
[e576789]290 tree_erase ${f}.dep
291 fi
292 fi
293done
294rm -f $file
[3c10176]295}
Note: See TracBrowser for help on using the repository browser.