Automate the process of choosing action when there are circular dependencies.

This has to be tested, but hopefully, it should allow to find a coherent
build order when there are not too many circular dependencies
This commit is contained in:
Pierre Labastie 2015-11-17 20:22:04 +00:00
parent 5fb012da9a
commit 7dc8595523
3 changed files with 123 additions and 60 deletions

View file

@ -90,11 +90,13 @@ generate_deps() { #
local -i index local -i index
local DepDir=$1 local DepDir=$1
rm -f $DepDir/*.dep rm -f $DepDir/*.{o,}dep
echo 1 > $DepDir/root.dep
for (( index=0 ; index < ${#TARGET[*]} ; index ++ )); do for (( index=0 ; index < ${#TARGET[*]} ; index ++ )); do
echo ${TARGET[${index}]} >> $DepDir/root.dep echo 1 ${TARGET[${index}]} >> $DepDir/root.odep
done done
echo 1 > $DepDir/root.dep
echo 1 >> $DepDir/root.dep
cat $DepDir/root.odep >> $DepDir/root.dep
} }
# #
@ -145,7 +147,7 @@ mkdir $DepDir
generate_deps $DepDir generate_deps $DepDir
pushd $DepDir > /dev/null pushd $DepDir > /dev/null
set +e set +e
generate_dependency_tree root.dep generate_dependency_tree root.dep 1
echo echo
LIST="$(tree_browse root.dep)" LIST="$(tree_browse root.dep)"
set -e set -e

View file

@ -8,19 +8,33 @@
# tree. Everything would be "simple" without circular dependencies. We # # tree. Everything would be "simple" without circular dependencies. We #
# would just have to build the tree using the packages.xml file, and to # # would just have to build the tree using the packages.xml file, and to #
# provide a function for browsing it. But we need to be able to detect # # provide a function for browsing it. But we need to be able to detect #
# circular dependencies and to possibly change the tree depending on the # # circular dependencies and to possibly change the tree depending on #
# user # decision. This is why we keep with each node a record of the path # # priorities. This is why we keep with each node a record of the path #
# from the root to the node, which we call *link*. # # from the root to the node, which we call *link* and a record of the #
# successive priorities which we call *priolink*. #
# #
# Layout of the tree: # # Layout of the tree: #
# Each node is a file <nodeName>.dep, which contains the names of the # # #
# child nodes, one per line, except the first line is the *link*: # # A node of the tree is represented by a file <nodeName>.dep. We keep all #
# the link is a series of numbers (n1 n2 ... nN), describing the path from # # those files in the same directory. The root node file is "root.dep". #
# the root to <nodeName>: n1 is the position of the first node of the path # # Files <nodeName>.dep have the following layout: #
# in root.dep, n2 is the position of the second node of the path in # # - the first line is the link: the link is an array of numbers #
# <node1>.dep and so on. The link is not needed for normal tree operations # # (n1 n2 ... nN), describing the path from the root to <nodeName>: n1 #
# (building a subtree or browsing the tree), but it allows to # # is the position of the first node of the path in root.dep, n2 is the #
# check whether a dependency is circular, and to find its parent. # # position of the second node of the path in <node1>.dep and so on. The #
# link is not needed for normal tree operations (building a subtree or #
# browsing the tree), but it allows to check whether a dependency is #
# circular, and to find its parent. #
# - the second line is an array of priorities (p1 p2 ... pN), giving the #
# priority (1=required, 2=recommended, 3=optional) of each dependency #
# in the link. #
# - each subsequent line is of the form "p <depname>", where p is the #
# priority as above, and <depname> is the name of the dependency. The #
# position which is recorded in the link is the number of the line #
# minus 2. #
# #
# Circular dependencies: # # Circular dependencies: #
# #
# In case we find a cirdular dependency, it has the form : # # In case we find a cirdular dependency, it has the form : #
# parent->dependency_0->...->dependency_n->dependency_0 # # parent->dependency_0->...->dependency_n->dependency_0 #
# If we want to build dependency_n before dependency_0, no problem: # # If we want to build dependency_n before dependency_0, no problem: #
@ -37,10 +51,9 @@
# Global variables: # Global variables:
# A string of spaces for indenting: # A string of spaces for indenting:
declare -a spaceSTR=" " declare -a spaceSTR=" "
declare -a exchange_triplet
# When we are backing up from a circular dependency, `exchange_triplet' # When we are backing up from a circular dependency, `exchange_triplet'
# shall contain (parent dependency_0 dependency_n) # shall contain (parent dependency_0 dependency_n):
declare -a exchange_triplet
#----------------------------# #----------------------------#
generate_dependency_tree() { # generate_dependency_tree() { #
@ -50,8 +63,11 @@ generate_dependency_tree() { #
(recursive function) (recursive function)
input vars: $1 : file with a list of targets (child nodes) input vars: $1 : file with a list of targets (child nodes)
the first line of the file is the link the first line of the file is the link
externals: vars: BLFS_XML $2 : priority (1=req, 2=rec, 3=opt)
DEP_LEVEL externals: vars: DEP_LEVEL contains the 1 if we want to build the
tree only for required dependencies,
2 if we want also recommended ones,
and 3 if we want optional ones too.
returns: 0 if the tree has been successfully created returns: 0 if the tree has been successfully created
1 if we are backing up to the parent of a circular dep 1 if we are backing up to the parent of a circular dep
modifies: vars: exchange_triplet contains the triplet when return is 1 modifies: vars: exchange_triplet contains the triplet when return is 1
@ -62,7 +78,9 @@ generate_dependency_tree() { #
inline_doc inline_doc
local DepFile=$1 local DepFile=$1
local priority=$2
local -a rootlink local -a rootlink
local -a priolink
local -a otherlink local -a otherlink
local -i depth local -i depth
local -i count=0 local -i count=0
@ -71,19 +89,33 @@ local parent
local lines_to_remove= local lines_to_remove=
local srootlink local srootlink
local dep_level local dep_level
local priostring
local dpriostring
local i
{ {
# We use fd number 6 for input from DepFile, because we need 0 for user input # We use fd number 6 for input from DepFile, because we need 0 for user input
read -u6 -a rootlink read -u6 -a rootlink
depth=${#rootlink[*]} depth=${#rootlink[*]}
read -u6 -a priolink
dep_level=$DEP_LEVEL dep_level=$DEP_LEVEL
# For now, process optional deps only for the root packages. # For now, process optional deps only for the root packages.
if (( $DEP_LEVEL > 2 )) && (( $depth > 1 )); then dep_level=2; fi if (( $DEP_LEVEL > 2 )) && (( $depth > 1 )); then dep_level=2; fi
srootlink="${rootlink[*]} " srootlink="${rootlink[*]} "
case $priority in
1) priostring=required ;;
2) priostring=recommended ;;
3) priostring=optional ;;
esac
# start of DepFile # start of DepFile
echo -en "\nNode: $depth${spaceSTR:0:$depth}${RED}$DepFile${OFF}" echo -en "\nNode: $depth${spaceSTR:0:$depth}${RED}$DepFile${OFF} $priostring"
while read -u6 id_of_dep; do while read -u6 prio_of_dep id_of_dep; do
case $prio_of_dep in
1) dpriostring=required ;;
2) dpriostring=recommended ;;
3) dpriostring=optional ;;
esac
# count entries in file # count entries in file
(( count++ )) (( count++ ))
# Has this entry already been seen? # Has this entry already been seen?
@ -97,31 +129,27 @@ while read -u6 id_of_dep; do
# Do not use "${rootlink[*]}" =~ "${otherlink[*]}": case rootlink=(1 11) # Do not use "${rootlink[*]}" =~ "${otherlink[*]}": case rootlink=(1 11)
# and otherlink=(1 1) # and otherlink=(1 1)
if [[ ${srootlink#"${otherlink[*]} "} != ${srootlink} ]]; then # cir. dep if [[ ${srootlink#"${otherlink[*]} "} != ${srootlink} ]]; then # cir. dep
echo -en "\nCirc: $((depth+1))${spaceSTR:0:$((depth+1))}${YELLOW}${id_of_dep}${OFF} $dpriostring"
# First look for the other parent of this dependency. # First look for the other parent of this dependency.
# The parent has the same link without the last entry. # The parent has the same link without the last entry.
# We do not need otherlink anymore so just destroy the last element # We do not need otherlink anymore so just destroy the last element
unset otherlink[${#otherlink[*]}-1] unset otherlink[-1]
parent=$(grep ^"${otherlink[*]}"\$ -l *) parent=$(grep ^"${otherlink[*]}"\$ -l *)
parent=${parent%.dep} parent=${parent%.dep}
echo -en "\nCirc: $depth${spaceSTR:0:$depth}${BLUE}Circular dependency detected:${OFF}" # Find lowest priority in branch from parent to DepFile:
echo -en "\nCirc: $depth${spaceSTR:0:$depth}${BOLD}${id_of_dep}${OFF} is a dependency \ p2=0
of ${BOLD}${parent}${OFF}" for (( i=${#otherlink[*]}; i < $depth ; i++ )) ; do
echo -en "\nCirc: $depth${spaceSTR:0:$depth}${BOLD}${DepFile%.dep}${OFF} is a dependency \ if (( ${priolink[i]} > $p2 )); then p2=${priolink[i]}; fi
of ${BOLD}${id_of_dep}${OFF}" done
echo -en "\nCirc: $depth${spaceSTR:0:$depth}${BOLD}${id_of_dep}${OFF} is a dependency \ if (( $prio_of_dep >= $p2 )); then # prune
of ${BOLD}${DepFile%.dep}${OFF}"
# we propose exchange always
echo -en "\nCirc: $depth${spaceSTR:0:$depth}Do you want to build ${id_of_dep} first? yes/no (no):"
read ANSWER
if [ x$ANSWER = "xyes" ] ; then # exchange:
# set triplet and return 1
exchange_triplet=($parent $id_of_dep ${DepFile%.dep})
return 1
else # no exchange: prune and remove the line(s) from odep...
lines_to_remove="$lines_to_remove $id_of_dep" lines_to_remove="$lines_to_remove $id_of_dep"
sed -i "/$id_of_dep/d" ${DepFile/.dep/.odep} sed -i "/$id_of_dep/d" ${DepFile/.dep/.odep}
else #backup with prio priority
exchange_triplet=($parent $id_of_dep ${DepFile%.dep})
return $priority
fi fi
else # not circular: prune else # not circular: prune tree (but not .odep, since it may happen that
# the tree is destroyed and rebuilt in another order
lines_to_remove="$lines_to_remove $id_of_dep" lines_to_remove="$lines_to_remove $id_of_dep"
fi # circular or not fi # circular or not
continue # this dependency has already been seen, and the associated continue # this dependency has already been seen, and the associated
@ -143,16 +171,25 @@ of ${BOLD}${DepFile%.dep}${OFF}"
# Use -s, because it may happen that after removing lines, .odep exists # Use -s, because it may happen that after removing lines, .odep exists
# but is empty. # but is empty.
if [[ -s ${id_of_dep}.odep ]]; then # this dependency has dependencies if [[ -s ${id_of_dep}.odep ]]; then # this dependency has dependencies
sed "1i${rootlink[*]} $count" < ${id_of_dep}.odep \ sed "1i${rootlink[*]} $count\\
> ${id_of_dep}.dep # add the link ${priolink[*]} $prio_of_dep" < ${id_of_dep}.odep \
generate_dependency_tree ${id_of_dep}.dep > ${id_of_dep}.dep # add link and priolink
generate_dependency_tree ${id_of_dep}.dep $prio_of_dep
# Test return value, in case we exchange dependencies # Test return value, in case we exchange dependencies
case $? in p2=$?
case $p2 in
0) # Normal return 0) # Normal return
;; ;;
1) # We are backing up to parent [123]) # We are backing up to parent
if [[ ${exchange_triplet} == ${DepFile%.dep} ]] # we are the parent if [[ ${exchange_triplet} == ${DepFile%.dep} ]] ; then
then tree_erase ${id_of_dep}.dep # We are the parent!
# First, we have to find the parent of our new direct dep, and remove
# the new direct dep from the parent.odep:
otherlink=($(head -n1 ${exchange_triplet[2]}.dep))
unset otherlink[-1]
parent=$(grep -l ^"${otherlink[*]}"\$ *.dep)
sed -i /[[:digit:]]\ ${exchange_triplet[2]}\$/d ${parent/.dep/.odep}
tree_erase ${id_of_dep}.dep
# We want that our direct dep be ${exchange_triplet[2]} and that id_of_dep # We want that our direct dep be ${exchange_triplet[2]} and that id_of_dep
# be pulled in as an indirect dep, so exchange. # be pulled in as an indirect dep, so exchange.
# Just doing a sed -i "s@${id_of_dep}@${exchange_triplet[2]}@" $DepFile # Just doing a sed -i "s@${id_of_dep}@${exchange_triplet[2]}@" $DepFile
@ -166,16 +203,23 @@ of ${BOLD}${DepFile%.dep}${OFF}"
sed -i "${lineno}s@${id_of_dep}@${exchange_triplet[2]}@" $OdepFile sed -i "${lineno}s@${id_of_dep}@${exchange_triplet[2]}@" $OdepFile
id_of_dep=${exchange_triplet[2]} id_of_dep=${exchange_triplet[2]}
flag=true # we have to regenerate the tree for the new dependency flag=true # we have to regenerate the tree for the new dependency
else # we are not the parent. let's just back up one step else
# echo backing up to ${exchange_triplet} at ${DepFile%.dep} # We are not the parent. If our priority is greater than p2
return 1 # we have to change the triplet and return priority, else return current p2.
# echo (DEBUG) backing up to ${exchange_triplet} at ${DepFile%.dep}
if (( $priority > $p2 )); then
exchange_triplet[2]=${DepFile%.dep}
return $priority
else
return $p2
fi
fi fi
;; ;;
esac esac
else # id_of_dep has no dependencies, just record the link in a file else # id_of_dep has no dependencies, just record the link in a file
# and print # and print
echo "${rootlink[*]} $count" > ${id_of_dep}.dep echo "${rootlink[*]} $count" > ${id_of_dep}.dep
echo -en "\nLeaf: $(($depth+1))${spaceSTR:0:$(($depth+1))}${CYAN}${id_of_dep}${OFF}" echo -en "\nLeaf: $(($depth+1))${spaceSTR:0:$(($depth+1))}${CYAN}${id_of_dep}${OFF} $dpriostring"
fi fi
done done
done done
@ -190,7 +234,7 @@ echo -en "\n End: $depth${spaceSTR:0:$depth}${GREEN}$DepFile${OFF}"
# so first get the position of last line and then delete # so first get the position of last line and then delete
# that line # that line
for line in $lines_to_remove for line in $lines_to_remove
do lineno=$(sed -n /^$line\$/= $DepFile | tail -n1) do lineno=$(sed -n /^[[:digit:]]\ $line\$/= $DepFile | tail -n1)
sed -i ${lineno}d $DepFile sed -i ${lineno}d $DepFile
done done
return 0 return 0
@ -203,7 +247,7 @@ local file=$1
local f local f
#echo file=$file #echo file=$file
for f in $(grep '[^0-9 ]' $file); do for f in $(grep '[^0-9 ]' $file | sed 's/.* //'); do
# echo f=$f # echo f=$f
if grep -q '[^0-9 ]' ${f}.dep ; then if grep -q '[^0-9 ]' ${f}.dep ; then
tree_browse ${f}.dep tree_browse ${f}.dep
@ -222,7 +266,7 @@ local -a rootlink2
#echo file=$file #echo file=$file
rootlink=($(head -n1 $file)) rootlink=($(head -n1 $file))
for f in $(grep '[^0-9 ]' $file); do for f in $(grep '[^0-9 ]' $file | sed 's/.* //'); do
# echo " f"=$f # echo " f"=$f
if [ -f ${f}.dep ]; then if [ -f ${f}.dep ]; then
rootlink2=($(head -n1 ${f}.dep)) rootlink2=($(head -n1 ${f}.dep))

View file

@ -20,26 +20,39 @@
</xsl:template> </xsl:template>
<xsl:template match="package"> <xsl:template match="package">
<xsl:apply-templates select="./dependency[@status='required']"/> <xsl:apply-templates select="./dependency[@status='required']">
<xsl:with-param name="priority" select="1"/>
</xsl:apply-templates>
<xsl:if test="$dependencies &gt; '1'"> <xsl:if test="$dependencies &gt; '1'">
<xsl:apply-templates select="./dependency[@status='recommended']"/> <xsl:apply-templates select="./dependency[@status='recommended']">
<xsl:with-param name="priority" select="2"/>
</xsl:apply-templates>
<xsl:if test="$dependencies = '3'"> <xsl:if test="$dependencies = '3'">
<xsl:apply-templates select="./dependency[@status='optional']"/> <xsl:apply-templates select="./dependency[@status='optional']">
<xsl:with-param name="priority" select="3"/>
</xsl:apply-templates>
</xsl:if> </xsl:if>
</xsl:if> </xsl:if>
</xsl:template> </xsl:template>
<xsl:template match="module"> <xsl:template match="module">
<xsl:apply-templates select="./dependency[@status='required']"/> <xsl:apply-templates select="./dependency[@status='required']">
<xsl:with-param name="priority" select="1"/>
</xsl:apply-templates>
<xsl:if test="$dependencies &gt; '1'"> <xsl:if test="$dependencies &gt; '1'">
<xsl:apply-templates select="./dependency[@status='recommended']"/> <xsl:apply-templates select="./dependency[@status='recommended']">
<xsl:with-param name="priority" select="2"/>
</xsl:apply-templates>
<xsl:if test="$dependencies = '3'"> <xsl:if test="$dependencies = '3'">
<xsl:apply-templates select="./dependency[@status='optional']"/> <xsl:apply-templates select="./dependency[@status='optional']">
<xsl:with-param name="priority" select="3"/>
</xsl:apply-templates>
</xsl:if> </xsl:if>
</xsl:if> </xsl:if>
</xsl:template> </xsl:template>
<xsl:template match="dependency"> <xsl:template match="dependency">
<xsl:param name="priority"/>
<xsl:variable name="depname"> <xsl:variable name="depname">
<xsl:choose> <xsl:choose>
<xsl:when test="@name='xorg-env'"/> <xsl:when test="@name='xorg-env'"/>
@ -67,8 +80,12 @@
</xsl:otherwise> </xsl:otherwise>
</xsl:choose> </xsl:choose>
</xsl:variable> </xsl:variable>
<xsl:apply-templates select="dependency"/> <xsl:apply-templates select="dependency">
<xsl:with-param name="priority" select="1"/>
</xsl:apply-templates>
<xsl:if test="number($install_it)"> <xsl:if test="number($install_it)">
<xsl:value-of select="$priority"/>
<xsl:text> </xsl:text>
<xsl:value-of select="$depname"/> <xsl:value-of select="$depname"/>
<xsl:text>&#xA;</xsl:text> <xsl:text>&#xA;</xsl:text>
</xsl:if> </xsl:if>