<div dir="ltr">It looks like this is responsible for some autobuilder failures where bitbake decides to use hob instead of knotty2 and aborts on start up with:<br><br>FATAL: Hob requires Gtk+ 2.20.0 or higher, PyGtk 2.21.0 or higher (No module named gobject).<div><br></div><div>Ross</div></div><div class="gmail_extra"><br><div class="gmail_quote">On 17 August 2015 at 12:12, Paul Eggleton <span dir="ltr">&lt;<a href="mailto:paul.eggleton@linux.intel.com" target="_blank">paul.eggleton@linux.intel.com</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Provide us with a means of showing the list of UIs / server choices for<br>
the command line help, and do the processing in one place for both.<br>
<br>
Signed-off-by: Paul Eggleton &lt;<a href="mailto:paul.eggleton@linux.intel.com">paul.eggleton@linux.intel.com</a>&gt;<br>
---<br>
 lib/bb/main.py | 78 ++++++++++++++++++++++++++++++++++++----------------------<br>
 1 file changed, 48 insertions(+), 30 deletions(-)<br>
<br>
diff --git a/lib/bb/main.py b/lib/bb/main.py<br>
index 4d77408..2b720aa 100755<br>
--- a/lib/bb/main.py<br>
+++ b/lib/bb/main.py<br>
@@ -41,22 +41,45 @@ logger = logging.getLogger(&quot;BitBake&quot;)<br>
 class BBMainException(Exception):<br>
     pass<br>
<br>
-def get_ui(config):<br>
-    if not config.ui:<br>
-        # modify &#39;ui&#39; attribute because it is also read by cooker<br>
-        config.ui = os.environ.get(&#39;BITBAKE_UI&#39;, &#39;knotty&#39;)<br>
-<br>
-    interface = config.ui<br>
-<br>
+def list_extension_modules(pkg, checkattr):<br>
+    &quot;&quot;&quot;<br>
+    Lists extension modules in a specific Python package<br>
+    (e.g. UIs, servers)<br>
+    Parameters:<br>
+        pkg: previously imported Python package to list<br>
+        checkattr: attribute to look for in module to determine if it&#39;s valid<br>
+            as the type of extension you are looking for<br>
+    &quot;&quot;&quot;<br>
+    import pkgutil<br>
+    pkgdir = os.path.dirname(pkg.__file__)<br>
+<br>
+    modules = []<br>
+    for _, modulename, _ in pkgutil.iter_modules([pkgdir]):<br>
+        if os.path.isdir(os.path.join(pkgdir, modulename)):<br>
+            # ignore directories<br>
+            continue<br>
+        try:<br>
+            module = __import__(pkg.__name__, fromlist=[modulename])<br>
+        except ImportError:<br>
+            # If we can&#39;t import it, it&#39;s not valid<br>
+            continue<br>
+        module_if = getattr(module, modulename)<br>
+        if getattr(module_if, &#39;hidden_extension&#39;, False):<br>
+            continue<br>
+        if not checkattr or hasattr(module_if, checkattr):<br>
+            modules.append(modulename)<br>
+    return modules<br>
+<br>
+def import_extension_module(pkg, modulename):<br>
     try:<br>
         # Dynamically load the UI based on the ui name. Although we<br>
         # suggest a fixed set this allows you to have flexibility in which<br>
         # ones are available.<br>
-        module = __import__(&quot;bb.ui&quot;, fromlist = [interface])<br>
-        return getattr(module, interface)<br>
+        module = __import__(pkg.__name__, fromlist = [modulename])<br>
+        return getattr(module, modulename)<br>
     except AttributeError:<br>
-        raise BBMainException(&quot;FATAL: Invalid user interface &#39;%s&#39; specified.\n&quot;<br>
-                 &quot;Valid interfaces: depexp, goggle, ncurses, hob, knotty [default].&quot; % interface)<br>
+        raise BBMainException(&quot;FATAL: Unable to import extension module %s from %s&quot; % (modulename, pkg.__name__))<br>
+<br>
<br>
<br>
 # Display bitbake/OE warnings via the BitBake.Warnings logger, ignoring others&quot;&quot;&quot;<br>
@@ -146,11 +169,19 @@ class BitBakeConfigParameters(cookerdata.ConfigParameters):<br>
         parser.add_option(&quot;-P&quot;, &quot;--profile&quot;, help = &quot;Profile the command and save reports.&quot;,<br>
                    action = &quot;store_true&quot;, dest = &quot;profile&quot;, default = False)<br>
<br>
-        parser.add_option(&quot;-u&quot;, &quot;--ui&quot;, help = &quot;The user interface to use (e.g. knotty, hob, depexp).&quot;,<br>
-                   action = &quot;store&quot;, dest = &quot;ui&quot;)<br>
+        env_ui = os.environ.get(&#39;BITBAKE_UI&#39;, None)<br>
+        valid_uis = list_extension_modules(bb.ui, &#39;main&#39;)<br>
+        default_ui = env_ui or &#39;knotty&#39;<br>
+        if env_ui and not env_ui in valid_uis:<br>
+            raise BBMainException(&#39;Invalid UI &quot;%s&quot; specified in BITBAKE_UI environment variable - valid choices: %s&#39; % (env_ui, &#39;, &#39;.join(valid_uis)))<br>
+        elif not default_ui in valid_uis:<br>
+            raise BBMainException(&#39;Default UI &quot;%s&quot; could not be found&#39;)<br>
+        parser.add_option(&quot;-u&quot;, &quot;--ui&quot;, help = &quot;The user interface to use (%s).&quot; % &#39;, &#39;.join(valid_uis),<br>
+                   action=&quot;store&quot;, dest=&quot;ui&quot;, type=&quot;choice&quot;, choices=valid_uis, default=default_ui)<br>
<br>
-        parser.add_option(&quot;-t&quot;, &quot;--servertype&quot;, help = &quot;Choose which server to use, process or xmlrpc.&quot;,<br>
-                   action = &quot;store&quot;, dest = &quot;servertype&quot;)<br>
+        valid_server_types = list_extension_modules(bb.server, &#39;BitBakeServer&#39;)<br>
+        parser.add_option(&quot;-t&quot;, &quot;--servertype&quot;, help = &quot;Choose which server type to use (%s).&quot; % &#39;, &#39;.join(valid_server_types),<br>
+                   action = &quot;store&quot;, dest = &quot;servertype&quot;, default = &quot;process&quot;)<br>
<br>
         parser.add_option(&quot;&quot;, &quot;--token&quot;, help = &quot;Specify the connection token to be used when connecting to a remote server.&quot;,<br>
                    action = &quot;store&quot;, dest = &quot;xmlrpctoken&quot;)<br>
@@ -279,21 +310,8 @@ def bitbake_main(configParams, configuration):<br>
<br>
     configuration.setConfigParameters(configParams)<br>
<br>
-    ui_module = get_ui(configParams)<br>
-<br>
-    # Server type can be xmlrpc or process currently, if nothing is specified,<br>
-    # the default server is process<br>
-    if configParams.servertype:<br>
-        server_type = configParams.servertype<br>
-    else:<br>
-        server_type = &#39;process&#39;<br>
-<br>
-    try:<br>
-        module = __import__(&quot;bb.server&quot;, fromlist = [server_type])<br>
-        servermodule = getattr(module, server_type)<br>
-    except AttributeError:<br>
-        raise BBMainException(&quot;FATAL: Invalid server type &#39;%s&#39; specified.\n&quot;<br>
-                              &quot;Valid interfaces: xmlrpc, process [default].&quot; % server_type)<br>
+    ui_module = import_extension_module(bb.ui, configParams.ui)<br>
+    servermodule = import_extension_module(bb.server, configParams.servertype)<br>
<br>
     if configParams.server_only:<br>
         if configParams.servertype != &quot;xmlrpc&quot;:<br>
<span class="HOEnZb"><font color="#888888">--<br>
2.1.0<br>
<br>
--<br>
_______________________________________________<br>
bitbake-devel mailing list<br>
<a href="mailto:bitbake-devel@lists.openembedded.org">bitbake-devel@lists.openembedded.org</a><br>
<a href="http://lists.openembedded.org/mailman/listinfo/bitbake-devel" rel="noreferrer" target="_blank">http://lists.openembedded.org/mailman/listinfo/bitbake-devel</a><br>
</font></span></blockquote></div><br></div>