Subject: classes of mutually exclusive options
To: None <tech-pkg@NetBSD.org>
From: Dieter Baron <dillo@danbala.tuwien.ac.at>
List: tech-pkg
Date: 05/31/2005 15:46:26
--R3G7APHDIzY6R/pk
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

hi,

the current options framework does not handle classes of mutually
exclusive options well: The pacakge author has to check that only one
of them is set, and the user has to select the one he wants and
deselect all others.

  The attached patch adds support for option classes to the options
framework, which takes care of only selecting one option from each
class: the last option specified (from generic to specific, just as
with regular options) that is not negated with a -:

PKG_OPTIONS_REQUIRED_CLASSES=database
PKG_OPTIONS_CLASS.database=  mysql pgsql
PKG_SUGGESTED_OPTIONS=       mysql

PKG_DEFAULT_OPTIONS=         pgsql
PKG_OPTIONS.foo=             -pgsql

will result in mysql being selected.

  As an example, chat/jabberd2 has been converted to use the classes
support.

  Suggestions, comments?

						yours,
						dillo

--R3G7APHDIzY6R/pk
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="classes.diff"

Index: mk/bsd.options.mk
===================================================================
RCS file: /cvsroot/pkgsrc/mk/bsd.options.mk,v
retrieving revision 1.27
diff -u -r1.27 bsd.options.mk
--- mk/bsd.options.mk	31 May 2005 12:10:07 -0000	1.27
+++ mk/bsd.options.mk	31 May 2005 13:34:22 -0000
@@ -15,6 +15,20 @@
 #               The variable the user can set to enable or disable
 #		options specifically for this package.
 #
+#	PKG_OPTIONS_OPTIONAL_CLASSES
+#		This is a list of classes of mutually exclusive
+#		options.  The options in each class are listed in
+#		PKG_OPTIONS_CLASS.<classname>.  The most specific
+#		setting of any option from the class takes precedence
+#               over all other options in the class.  Options from
+#		the classes will be automatically added to
+#		PKG_SUPPORTED_OPTOINS.
+#
+#	PKG_OPTIONS_REQUIRED_CLASSES
+#		Like PKG_OPTIONS_OPTIONAL_CLASSES, but building
+#		the packages will fail if no option from the class
+#		is selected.
+#
 #	PKG_SUGGESTED_OPTIONS (defaults to empty)
 #		This is a list of build options which are enabled by default.
 #
@@ -54,6 +68,8 @@
 # -------------8<-------------8<-------------8<-------------8<-------------
 # PKG_OPTIONS_VAR=		PKG_OPTIONS.wibble
 # PKG_SUPPORTED_OPTIONS=	wibble-foo ldap sasl
+# PKG_OPTIONAL_CLASSES=		database
+# PKG_CLASS.database=		mysql pgsql
 # PKG_SUGGESTED_OPTIONS=	wibble-foo
 # PKG_OPTIONS_LEGACY_VARS+=	WIBBLE_USE_OPENLDAP:ldap
 # PKG_OPTIONS_LEGACY_VARS+=	WIBBLE_USE_SASL2:sasl
@@ -85,6 +101,16 @@
 # .  include "../../security/cyrus-sasl2/buildlink3.mk"
 # CONFIGURE_ARGS+=	--enable-sasl=${BUILDLINK_PREFIX.sasl}
 # .endif
+#
+# ###
+# ### database support
+# ###
+# .if !empty(PKG_OPTIONS:Mmysql)
+# .  include "../../mk/mysql.buildlink3.mk"
+# .endif
+# .if !empty(PKG_OPTIONS:Mpgsql)
+# .  include "../../mk/pgsql.buildlink3.mk"
+# .endif
 # -------------8<-------------8<-------------8<-------------8<-------------
 
 .include "../../mk/bsd.prefs.mk"
@@ -103,6 +129,20 @@
 # include deprecated variable to options mapping
 .include "${.CURDIR}/../../mk/defaults/obsolete.mk"
 
+#
+# create map of option to class and add class options to PKG_SUPPORTED_OPTOINS
+#
+.for _cls_ in ${PKG_OPTIONS_OPTIONAL_CLASSES} ${PKG_OPTIONS_REQUIRED_CLASSES}
+_PKG_OPTIONS_CLASS_STACK.${_cls_}:=#empty
+.  for _opt_ in ${PKG_OPTIONS_CLASS.${_cls_}}
+PKG_SUPPORTED_OPTIONS+= ${_opt_}
+_PKG_OPTIONS_CLASS_MAP.${_opt_}=${_cls_}
+.  endfor
+.endfor
+
+#
+# place options imlied by legacy variables in _PKG_LEGACY_OPTIONS
+#
 .for _m_ in ${PKG_OPTIONS_LEGACY_VARS}
 _var_:=	${_m_:C/:.*//}
 _opt_:=	${_m_:C/.*://}
@@ -118,6 +158,9 @@
 .undef _opt_
 .undef _popt_
 
+#
+# create map of old option name to new option name for legacy options
+#
 .for _m_ in ${PKG_OPTIONS_LEGACY_OPTS}
 _old_:= ${_m_:C/:.*//}
 _new_:= ${_m_:C/.*://}
@@ -164,18 +207,47 @@
 .  if empty(PKG_SUPPORTED_OPTIONS:M${_popt_})
 _OPTIONS_UNSUPPORTED:=${_OPTIONS_UNSUPPORTED} ${_opt_}
 .  else
-.    if !empty(_opt_:M-*)
-PKG_OPTIONS:=	${PKG_OPTIONS:N${_popt_}}
+.    if defined(_PKG_OPTIONS_CLASS_MAP.${_popt_})
+_cls_:= ${_PKG_OPTIONS_CLASS_MAP.${_popt_}}
+_stk_:=	_PKG_OPTIONS_CLASS_STACK.${_cls_}
+_cnt_:=	${${_stk_}}
+.      if !empty(_opt_:M-*)
+${_stk_}:=	${_cnt_:N${_popt_}}
+.      else
+${_stk_}:=	${_cnt_} ${_popt_}
+.      endif
 .    else
+.      if !empty(_opt_:M-*)
+PKG_OPTIONS:=	${PKG_OPTIONS:N${_popt_}}
+.      else
 PKG_OPTIONS:=	${PKG_OPTIONS} ${_popt_}
+.      endif
 .    endif
 .  endif
 .endfor
 .undef _opt_
 .undef _popt_
+.undef _stk_
+
+.for _cls_ in ${PKG_OPTIONS_REQUIRED_CLASSES}
+.  if empty(_PKG_OPTIONS_CLASS_STACK.${_cls_})
+PKG_FAIL_REASON:=One of the following options must be selected: ${PKG_OPTIONS_CLASS.${_cls_}:O:u}
+.  endif
+.endfor
+
+.for _cls_ in ${PKG_OPTIONS_REQUIRED_CLASSES} ${PKG_OPTIONS_OPTIONAL_CLASSES}
+.undef _opt_
+.  for _o_ in ${_PKG_OPTIONS_CLASS_STACK.${_cls_}}
+_opt_:=		${_o_}
+.  endfor
+.  if defined(_opt_)
+PKG_OPTIONS:=	${PKG_OPTIONS} ${_opt_}
+.  endif
+.endfor
+.undef _opt_
 
 .if !empty(_OPTIONS_UNSUPPORTED)
-PKG_FAIL_REASON:=The following selected options are not supported: ${_OPTIONS_UNSUPPORTED:O:u:Q}
+PKG_FAIL_REASON:=The following selected options are not supported: ${_OPTIONS_UNSUPPORTED:O:u}
 .endif
 
 .undef _OPTIONS_UNSUPPORTED
Index: regress/pkg-options/Makefile
===================================================================
RCS file: /cvsroot/pkgsrc/regress/pkg-options/Makefile,v
retrieving revision 1.4
diff -u -r1.4 Makefile
--- regress/pkg-options/Makefile	31 May 2005 11:05:31 -0000	1.4
+++ regress/pkg-options/Makefile	31 May 2005 13:34:22 -0000
@@ -8,7 +8,7 @@
 MAINTAINER=	rillig@NetBSD.org
 COMMENT=	Test bsd.options.mk framework
 
-REGRESS_TESTS=	all legacy-opt order simple unsupported
+REGRESS_TESTS=	all class-required classes legacy-opt order simple unsupported
 
 do-test:
 .for t in ${REGRESS_TESTS}
Index: regress/pkg-options/files/class-required.mk
===================================================================
RCS file: regress/pkg-options/files/class-required.mk
diff -N regress/pkg-options/files/class-required.mk
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ regress/pkg-options/files/class-required.mk	31 May 2005 13:34:22 -0000
@@ -0,0 +1,18 @@
+# $NetBSD: unsupported.mk,v 1.1 2005/05/28 12:16:43 dillo Exp $
+#
+# This file tests that selecting no option from a requried class
+# causes the package build to fail.
+#
+
+MAKECONF=		/dev/null
+
+PKG_OPTIONS_VAR=	PKG_OPTIONS.unused
+PKG_SUPPORTED_OPTIONS=	single
+PKG_OPTIONS_REQUIRED_CLASSES=	req
+PKG_OPTIONS_CLASS.req=	a b
+
+.include "../../mk/bsd.options.mk"
+
+.PHONY: test
+test:
+	echo ${PKG_FAIL_REASON:Q}
Index: regress/pkg-options/files/class-required.out
===================================================================
RCS file: regress/pkg-options/files/class-required.out
diff -N regress/pkg-options/files/class-required.out
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ regress/pkg-options/files/class-required.out	31 May 2005 13:34:22 -0000
@@ -0,0 +1 @@
+One of the following options must be selected: a b
Index: regress/pkg-options/files/classes.mk
===================================================================
RCS file: regress/pkg-options/files/classes.mk
diff -N regress/pkg-options/files/classes.mk
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ regress/pkg-options/files/classes.mk	31 May 2005 13:34:22 -0000
@@ -0,0 +1,25 @@
+# $NetBSD: legacy-opt.mk,v 1.1 2005/05/31 11:05:31 dillo Exp $
+#
+# This file tests option classes (PKG_OPTIONS_REQUIRED_CLASSES and
+# PKG_OPTIONS_OPTIONAL_CLASSES).
+#
+
+MAKECONF=			/dev/null
+
+PKG_OPTIONS_VAR=		PKG_OPTIONS.foo
+PKG_SUPPORTED_OPTIONS=		single
+PKG_OPTIONS_REQUIRED_CLASSES=	abc def
+PKG_OPTIONS_OPTIONAL_CLASSES=	ghi
+PKG_OPTIONS_CLASS.abc=		a b c
+PKG_OPTIONS_CLASS.def=		d e f
+PKG_OPTIONS_CLASS.ghi=		g h i
+
+PKG_OPTIONS.foo=	a b -a
+PKG_OPTIONS.foo+=	d e f
+PKG_OPTIONS.foo+=	g h i -i -h
+
+.include "../../mk/bsd.options.mk"
+
+.PHONY: test
+test:
+	echo ${PKG_OPTIONS:M*:Q}
Index: regress/pkg-options/files/classes.out
===================================================================
RCS file: regress/pkg-options/files/classes.out
diff -N regress/pkg-options/files/classes.out
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ regress/pkg-options/files/classes.out	31 May 2005 13:34:22 -0000
@@ -0,0 +1 @@
+b f g
Index: chat/jabberd2/options.mk
===================================================================
RCS file: /cvsroot/pkgsrc/chat/jabberd2/options.mk,v
retrieving revision 1.9
diff -u -r1.9 options.mk
--- chat/jabberd2/options.mk	31 May 2005 11:24:32 -0000	1.9
+++ chat/jabberd2/options.mk	31 May 2005 13:34:22 -0000
@@ -2,15 +2,13 @@
 #
 
 PKG_OPTIONS_VAR=	PKG_OPTIONS.jabberd2
-PKG_SUPPORTED_OPTIONS=	db mysql pgsql ldap pam
+PKG_SUPPORTED_OPTIONS=	db ldap pam
+PKG_OPTIONS_OPTIONAL_CLASSES=	database
+PKG_OPTIONS_CLASS.database=	mysql pgsql
 PKG_SUGGESTED_OPTIONS=	mysql
 
 .include "../../mk/bsd.options.mk"
 
-.if !empty(PKG_OPTIONS:Mmysql) && !empty(PKG_OPTIONS:Mpgsql)
-PKG_FAIL_REASON+=	"Cannot use mysql and pgsql, use one of them."
-.endif
-
 .if !empty(PKG_OPTIONS:Mdb)
 BUILD_DEFS+=		JABBERD_DBDIR
 JABBERD_DBDIR?=         ${VARBASE}/db/jabberd

--R3G7APHDIzY6R/pk--