LWN.net Logo

RFC: kernel config: new dependency syntax

From:  Peter Samuelson <peter@cadcamlab.org>
To:  kbuild-devel@lists.sourceforge.net
Subject:  [kbuild-devel] RFC: kernel config: new dependency syntax
Date:  Wed, 14 Aug 2002 21:10:46 -0500


[I wrote]
> Mutating the language, long-term, so that it looks less like sh and
> more like a specialised language, is IMO a worthy goal.  And I think
> it can be done.  The main thing to deal with is adding an
> alternative syntax for 'if' statements which doesn't look like
> test(1).  More about that in a separate message.

I've come up with syntax I think I'm happy with.  It supports most of
the current [ ] based if statement semantics, can be implemented in
shell, and (most importantly for me) drops those $ signs.  This lays
the groundwork for stuff like better error checking and auto
(EXPERIMENTAL) tags.

I made a proof-of-concept patch for Menuconfig.  The Configure patch
would be very similar; God only knows what xconfig or mconfig would
require.

I've reused the syntax for a dependency line (the tail end of a
dep_bool / dep_mbool / dep_tristate), like so:

  if_dep <i>dependency line</i>
     ...
  endif

If the dependency line evaluates to 'm' or 'y', the body is executed.

Now with our current dep_* syntax, this isn't very powerful.  I've
made a few extensions which are, I believe, almost enough[*] to
displace the current use of test(1)-based 'if' statements.  Some of
this syntax is not really needed for dep_* lines, but I *really* like
having a single backend function for all of dep_bool, dep_tristate and
if_dep.

[*] "almost enough" because I haven't implemented an 'else'
    directive.  It would be trivial, but I'm not sure what to call it.
    'else' itself is a shell primitive, so the shell-based parsers
    (Configure, Menuconfig) wouldn't like it.

* '$CONFIG_FOO' can and should be replaced with 'CONFIG_FOO' without
  the $.  My current patch accepts both; eventually we might drop
  backwards compatibility.

* !CONFIG_FOO negates sense: !y==n, !n==y, !==y, !m==m.  The last
  !m==m is due to Roman's observation that it is useful to exclude two
  things from both being Y while allowing them to both be M.

* 'or' placed between dependencies functions as a logical OR, and
  takes very low precedence.  This complements the implicit AND
  performed between every pair of dependencies.

    x or x -> x, for any x
    n or m == m or n  -> m
    n or y == y or n  -> y
    m or y == y or m  -> y

* A=B evaluates to either Y or N, depending on whether A is logically
  equivalent to B.  It has higher precedence than the ! operator.
  Thus:

  CONFIG_FOO=m   evaluates to 'y' if CONFIG_FOO is m, 'n' otherwise
  CONFIG_BAR=n   evaluates to 'y' if CONFIG_BAR is n or empty, 'n' otherwise
  !CONFIG_BAZ=y  evaluates to 'n' if CONFIG_BAZ is y, 'y' otherwise


This syntax is fully backward-compatible.  Examples of use:

  if_dep CONFIG_X86 or CONFIG_X86_64 or CONFIG_IA64
     bool 'ACPI support' CONFIG_ACPI
  endif

  if_dep CONFIG_SOUND !CONFIG_SOUND_ALSA
     source sound/oss/Config.in
  endif

  dep_tristate 'Adaptec (new driver)' CONFIG_AIC7XXX_NEW !CONFIG_AIC7XXX_OLD
  dep_tristate 'Adaptec (old driver)' CONFIG_AIC7XXX_OLD !CONFIG_AIC7XXX_NEW


The one thing I wanted to specify but didn't is an 'else' statement.
The problem is that I can't think what to call it - can't use 'else'
because the shell-based parsers (Configure, Menuconfig) will choke on
it.  Any ideas?

Any other comments?  Am I going in totally the wrong direction?

Peter


--- 2.5.31/scripts/Menuconfig	2002-06-09 00:27:32.000000000 -0500
+++ 2.5.31w/scripts/Menuconfig	2002-08-14 21:00:27.000000000 -0500
@@ -73,7 +73,10 @@
 # - Support for multiple conditions in dep_tristate().
 # - Implemented new functions: define_tristate(), define_int(), define_hex(),
 #   define_string(), dep_bool().
-# 
+#
+# 14 Aug 2002, Peter Samuelson <peter@cadcamlab.org>
+# - add lots of new syntax for dependencies in 'dep_*' functions
+# - also use this syntax for a new 'if_dep' / 'endif' primitive
 
 
 #
@@ -112,6 +115,67 @@
     eval info="\$INFO_$1"
 }
 
+
+# Reduce a dependency line down to a single char [ymn].
+# Terms are implicitly ANDed together: 'y m' = 'm y' = m, '... n' = 'n ...' = n
+# Each term can be:
+#   'y', 'm', 'n'	interpreted as-is
+#   'CONFIG_FOO'	reduces to value of CONFIG_FOO, or 'n' if unset
+#   'or'		OR operator, lower precedence than the implicit AND
+#	specifics:	'y or ...' = '... or y' = y
+#	if no 'y':	'm or ...' = '... or m' = m
+#   term=value		tested for equality, replaced with 'y' or 'n'
+#   !term		"negate" value of term: y->n, n->y, m->m, ''->y
+# Note that there is no grouping construct.
+# Use boolean transforms, or nest conditionals.
+function dep_calc () {
+	local neg arg ordep
+	if [ "$nest_ifdep" = n ]; then
+		cur_dep=n;
+		return 1;
+	fi
+	ordep=n
+	cur_dep=y	# return value
+	for arg; do
+	  neg=;
+	  case "$arg" in
+	    or)
+	      case $ordep in
+		n) ordep=$cur_dep ;;
+		m) [ $cur_dep = y ] && ordep=y ;;
+	      esac
+	      cur_dep=y; continue ;;
+	    !*)
+	      neg=N; arg=${arg#?} ;;
+	  esac
+	  case "$arg" in
+	    'CONFIG_'*) eval arg=\$$arg ;;
+	  esac
+	  case "$arg" in
+	    y=y | m=m | n=n | n= | =n) arg=y ;;
+	    *=*) arg=n ;;
+	  esac
+	  case "$cur_dep/$neg/$arg" in
+	    y//m | y/N/m) cur_dep=m ;;
+	    *//n | */N/y) cur_dep=n ;;
+	  esac
+	done
+	case "$cur_dep/$ordep" in
+	  n/* | m/y) cur_dep=$ordep ;;
+	esac
+}
+
+function if_dep () {
+	dep_calc "$@"
+	nest_stack="$nest_ifdep $nest_stack"
+	nest_ifdep=$cur_dep
+}
+
+function endif () {
+	nest_ifdep=${nest_stack%%' '*}
+	nest_stack=${nest_stack#*' '}
+}
+
 #
 # Load the functions used by the config.in files.
 #
@@ -127,6 +191,7 @@
 # Additional comments
 #
 function comment () {
+	dep_calc || return
 	comment_ctr=$[ comment_ctr + 1 ]
 	echo -ne "': $comment_ctr' '--- $1' " >>MCmenu
 }
@@ -135,22 +200,27 @@
 # Define a boolean to a specific value.
 #
 function define_bool () {
+	dep_calc || return
 	eval $1=$2
 }
 
 function define_tristate () {
+	dep_calc || return
 	eval $1=$2
 }
 
 function define_hex () {
+	dep_calc || return
 	eval $1=$2
 }
 
 function define_int () {
+	dep_calc || return
 	eval $1=$2
 }
 
 function define_string () {
+	dep_calc || return
 	eval $1="$2"
 }
 
@@ -159,6 +229,7 @@
 # which calls our local bool function.
 #
 function bool () {
+	dep_calc || return
 	set_x_info "$2" "n"
 
 	case $x in
@@ -178,6 +249,7 @@
 # Collapses to a boolean (Yes/No) if module support is disabled.
 #
 function tristate () {
+	dep_calc || return
 	if [ "$CONFIG_MODULES" != "y" ]
 	then
 		bool "$1" "$2"
@@ -210,28 +282,13 @@
 #       else in the kernel.
 #
 function dep_tristate () {
-	ques="$1"
-	var="$2"
-	dep=y
-	shift 2
-	while [ $# -gt 0 ]; do
-		if   [ "$1" = y ]; then
-			shift
-		elif [ "$1" = m ]; then
-			dep=m
-			shift
-		else
-			dep=n
-			shift $#
-		fi
-	done
-	if [ "$dep" = y ]; then
-	    tristate "$ques" "$var"
-	elif [ "$dep" = m ]; then
-	    mod_bool "$ques" "$var"
-	else 
-	    define_tristate "$var" n
-	fi
+	ques=$1  var=$2;  shift 2
+	dep_calc "$@" || return
+	case $cur_dep in
+	  y) tristate "$ques" "$var" ;;
+	  m) mod_bool "$ques" "$var" ;;
+	  n) define_tristate "$var" n ;;
+	esac
 }
 
 #
@@ -239,49 +296,28 @@
 #   (i.e. third and next arguments).
 #
 function dep_bool () {
-	ques="$1"
-	var="$2"
-	dep=y
-	shift 2
-	while [ $# -gt 0 ]; do
-		if [ "$1" = y ]; then
-			shift
-		else
-			dep=n
-			shift $#
-		fi
-	done
-	if [ "$dep" = y ]; then
-	    bool "$ques" "$var"
-	else 
-	    define_bool "$var" n
-	fi
+	ques=$1  var=$2;  shift 2
+	dep_calc "$@" || return
+	case $cur_dep in
+	  y) bool "$ques" "$var" ;;
+	  *) define_bool "$var" n ;;
+	esac
 }
 
 function dep_mbool () {
-	ques="$1"
-	var="$2"
-	dep=y
-	shift 2
-	while [ $# -gt 0 ]; do
-		if [ "$1" = y -o "$1" = m ]; then
-			shift
-		else
-			dep=n
-			shift $#
-		fi
-	done
-	if [ "$dep" = y ]; then
-	    bool "$ques" "$var"
-	else 
-	    define_bool "$var" n
-	fi
+	ques=$1  var=$2;  shift 2
+	dep_calc "$@" || return
+	case $cur_dep in
+	  y|m) bool "$ques" "$var" ;;
+	  n) define_bool "$var" n ;;
+	esac
 }
 
 #
 # Add a menu item which will call our local int function.
 # 
 function int () {
+	dep_calc || return
 	set_x_info "$2" "$3"
 
 	echo -ne "'$2' '($x) $1$info' " >>MCmenu
@@ -293,6 +329,7 @@
 # Add a menu item which will call our local hex function.
 # 
 function hex () {
+	dep_calc || return
 	set_x_info "$2" "$3"
 	x=${x##*[x,X]}
 
@@ -305,6 +342,7 @@
 # Add a menu item which will call our local string function.
 # 
 function string () {
+	dep_calc || return
 	set_x_info "$2" "$3"
 
 	echo -ne "'$2' '     $1: \"$x\"$info' " >>MCmenu
@@ -316,6 +354,7 @@
 # Add a menu item which will call our local One-of-Many choice list.
 #
 function choice () {
+	dep_calc || return
 	#
 	# Need to remember params cause they're gonna get reset.
 	#
@@ -1066,90 +1105,81 @@
 	# Nested function definitions, YIPEE!
 	#
 	function bool () {
+		dep_calc || return
 		set_x_info "$2" "n"
 		eval define_bool "$2" "$x"
 	}
 
 	function tristate () {
+		dep_calc || return
 		set_x_info "$2" "n"
 		eval define_tristate "$2" "$x"
 	}
 
 	function dep_tristate () {
-		set_x_info "$2" "n"
-		var="$2"
-		shift 2
-		while [ $# -gt 0 ]; do
-			if   [ "$1" = y ]; then
-				shift
-			elif [ "$1" = m -a "$x" != n ]; then
-				x=m; shift
-			else 
-				x=n; shift $#
-			fi
-		done
+		var=$2;  shift 2
+		dep_calc "$@" || return
+		set_x_info "$var" "n"
+		case $cur_dep$x in
+			my) x=m ;;
+			n*) x=n ;;
+		esac
 		define_tristate "$var" "$x"
 	}
 
 	function dep_bool () {
-		set_x_info "$2" "n"
-		var="$2"
-		shift 2
-		while [ $# -gt 0 ]; do
-			if   [ "$1" = y ]; then
-				shift
-			else 
-				x=n; shift $#
-			fi
-		done
+		var=$2;  shift 2
+		dep_calc "$@" || return
+		set_x_info "$var" "n"
+		[ $cur_dep = y ] || x=n
 		define_bool "$var" "$x"
 	}
 
 	function dep_mbool () {
+		var=$2;  shift 2
+		dep_calc "$@" || return
 		set_x_info "$2" "n"
-		var="$2"
-		shift 2
-		while [ $# -gt 0 ]; do
-			if   [ "$1" = y -o "$1" = m ]; then
-				shift
-			else 
-				x=n; shift $#
-			fi
-		done
+		[ $cur_dep = n ] && x=n
 		define_bool "$var" "$x"
 	}
 
 	function int () {
+		dep_calc || return
 		set_x_info "$2" "$3"
 		echo "$2=$x" 		>>$CONFIG
 		echo "#define $2 ($x)"	>>$CONFIG_H
 	}
 
 	function hex () {
+		dep_calc || return
 		set_x_info "$2" "$3"
 		echo "$2=$x" 			 >>$CONFIG
 		echo "#define $2 0x${x##*[x,X]}" >>$CONFIG_H
 	}
 
 	function string () {
+		dep_calc || return
 		set_x_info "$2" "$3"
 		echo "$2=\"$x\"" 			 >>$CONFIG
 		echo "#define $2 \"$x\""	>>$CONFIG_H
 	}
 
 	function define_hex () {
+		dep_calc || return
 		eval $1="$2"
                	echo "$1=$2"			>>$CONFIG
 		echo "#define $1 0x${2##*[x,X]}"	>>$CONFIG_H
 	}
 
 	function define_int () {
+		dep_calc || return
 		eval $1="$2"
 		echo "$1=$2" 			>>$CONFIG
 		echo "#define $1 ($2)"		>>$CONFIG_H
 	}
 
 	function define_string () {
+		dep_calc || return
 		eval $1="$2"
 		echo "$1=\"$2\""		>>$CONFIG
 		echo "#define $1 \"$2\""	>>$CONFIG_H
@@ -1160,6 +1190,7 @@
 	}
 
 	function define_tristate () {
+		dep_calc || return
 		eval $1="$2"
 
    		case "$2" in
@@ -1188,6 +1219,7 @@
 	}
 
 	function choice () {
+		dep_calc || return
 		#
 		# Find the first choice that's already set to 'y'
 		#
@@ -1236,6 +1268,7 @@
 	}
 
 	function mainmenu_option () {
+		dep_calc || return
 		comment_is_option=TRUE
 	}
 
@@ -1244,6 +1277,7 @@
 	}
 
 	function comment () {
+		dep_calc || return
 		if [ "$comment_is_option" ]
 		then
 			comment_is_option=


-------------------------------------------------------
This sf.net email is sponsored by: Dice - The leading online job board
for high-tech professionals. Search and apply for tech jobs today!
http://seeker.dice.com/seeker.epl?rel_code=31
_______________________________________________
kbuild-devel mailing list
kbuild-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/kbuild-devel

Copyright © 2002, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds
Powered by Rackspace Managed Hosting.