add --query option wip
authorRichard Taylor <richard.i.taylor@nokia.com>
Tue, 27 Apr 2010 16:46:48 +0100
branchwip
changeset 498 564986768b79
parent 461 0c5ca7f6d8ae
child 499 cad3b96a4fb1
add --query option
sbsv2/raptor/lib/config/make.xml
sbsv2/raptor/python/raptor.py
sbsv2/raptor/python/raptor_api.py
sbsv2/raptor/python/raptor_cli.py
sbsv2/raptor/python/raptor_data.py
sbsv2/raptor/test/config/api.xml
sbsv2/raptor/test/smoke_suite/query_cli.py
sbsv2/raptor/test/smoke_suite/test_resources/bv/config/variants/bv_test.xml
sbsv2/raptor/test/unit_suite/raptor_api_unit.py
sbsv2/raptor/test/unit_suite/raptor_cli_unit.py
--- a/sbsv2/raptor/lib/config/make.xml	Wed Apr 14 14:40:58 2010 +0100
+++ b/sbsv2/raptor/lib/config/make.xml	Tue Apr 27 16:46:48 2010 +0100
@@ -55,7 +55,7 @@
 		<set name="copylogfromannofile" value="false"/>
 	</var>
 	
-	<alias name="make" meaning="make_engine"/>
+	<alias name="make" meaning="make_engine" type="engine"/>
 
 	<!-- use the talon shell -->
 	<var name="make_no_talon_engine" extends="make_engine">
@@ -63,7 +63,7 @@
 	        <set name='USE_TALON' value=''/>
 	</var>
 
-	<alias name="make_no_talon" meaning="make_no_talon_engine"/>
+	<alias name="make_no_talon" meaning="make_no_talon_engine" type="engine"/>
 
 	<!-- other derived versions of GNU make -->
 
@@ -79,7 +79,7 @@
 		<set name="copylogfromannofile" value="true"/>
 	</var>
 
-	<alias name="emake" meaning="emake_engine"/>
+	<alias name="emake" meaning="emake_engine" type="engine"/>
 	
 	<!-- use the talon shell -->
 	<var name="emake_no_talon_engine" extends="emake_engine">
@@ -87,7 +87,7 @@
 	        <set name='USE_TALON' value=''/>
 	</var>
 
-	<alias name="emake_no_talon" meaning="emake_no_talon_engine"/>
+	<alias name="emake_no_talon" meaning="emake_no_talon_engine" type="engine"/>
 	
 	<!-- Raptor make engine for PVMgmake -->
 	<var name="pvmgmake_engine" extends="make_engine">
@@ -98,6 +98,6 @@
 		<set name='DELETE_ON_FAILED_COMPILE' value=''/>
 	</var>
 
-	<alias name="pvmgmake" meaning="pvmgmake_engine"/>
+	<alias name="pvmgmake" meaning="pvmgmake_engine" type="engine"/>
 
 </build>
--- a/sbsv2/raptor/python/raptor.py	Wed Apr 14 14:40:58 2010 +0100
+++ b/sbsv2/raptor/python/raptor.py	Tue Apr 27 16:46:48 2010 +0100
@@ -448,9 +448,10 @@
 	created by the Main function. When operated by an IDE several Raptor
 	objects may be created and operated at the same time."""
 
-
+	# mission enumeration
 	M_BUILD = 1
-	M_VERSION = 2
+	M_QUERY = 2
+	M_VERSION = 3
 
 	def __init__(self, home = None):
 
@@ -520,7 +521,8 @@
 		self.noDependInclude = False
 		self.noDependGenerate = False
 		self.projects = set()
-
+		self.queries = []
+		
 		self.cache = raptor_cache.Cache(self)
 		self.override = {env: str(self.home)}
 		self.targets = []
@@ -717,6 +719,11 @@
 		self.projects.add(projectName.lower())
 		return True
 
+	def AddQuery(self, q):
+		self.queries.append(q)
+		self.mission = Raptor.M_QUERY
+		return True
+	
 	def FilterList(self, value):
 		self.filterList = value
 		return True
@@ -1219,6 +1226,31 @@
 
 		return layers
 
+	def Query(self):
+		"process command-line queries."
+		
+		if self.mission != Raptor.M_QUERY:
+			return 0
+		
+		# establish an object cache based on the current settings
+		self.LoadCache()
+			
+		# our "self" is a valid object for initialising an API Context
+		import raptor_api
+		api = raptor_api.Context(self)
+		
+		print "<sbs version='%s'>" % raptor_version.fullversion()
+		
+		for q in self.queries:
+			try:
+				print api.StringQuery(q)
+				
+			except Exception, e:
+				self.Error("exception '%s' with query '%s'", str(e), q)
+		
+		print "</sbs>"	
+		return self.errorCode
+	
 	def Build(self):
 
 		if self.mission != Raptor.M_BUILD: # help or version requested instead.
@@ -1356,6 +1388,9 @@
 	# object which represents a build
 	b = Raptor.CreateCommandlineBuild(argv)
 
+	if b.mission == Raptor.M_QUERY:
+		return b.Query()
+	
 	return b.Build()
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sbsv2/raptor/python/raptor_api.py	Tue Apr 27 16:46:48 2010 +0100
@@ -0,0 +1,179 @@
+#
+# Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+# All rights reserved.
+# This component and the accompanying materials are made available
+# under the terms of the License "Eclipse Public License v1.0"
+# which accompanies this distribution, and is available
+# at the URL "http://www.eclipse.org/legal/epl-v10.html".
+#
+# Initial Contributors:
+# Nokia Corporation - initial contribution.
+#
+# Contributors:
+#
+# Description: 
+#
+# raptor_api module
+#
+# Python API for Raptor. External code should interact with Raptor via this
+# module only, as it is the only programatic interface considered public. The
+# command line --query option is also implemented using this module.
+
+class Reply(object):
+	"""object to return values from API calls.
+	"""
+	def __init__(self, text=""):
+		self.reply_text = text
+	
+	def __str__(self):
+		name = type(self).__name__.lower()
+		
+		str = "<" + name
+		end = "/>\n"
+		
+		for attribute,value in self.__dict__.items():
+			if attribute != "reply_text":
+				str += " %s='%s'" % (attribute, value)
+		
+		if self.reply_text:
+			str += ">" + self.reply_text
+			end = "</%s>\n" % name
+			
+		str += end
+		
+		return str
+
+class Alias(Reply):
+	pass
+
+class Config(Reply):
+	pass
+
+class Product(Reply):
+	pass
+
+import generic_path
+import raptor
+import raptor_data
+import re
+
+class Context(object):
+	"""object to contain state information for API calls.
+	
+	For example,
+	
+	api = raptor_api.Context()
+	val = api.get(X)
+	"""
+	def __init__(self, initialiser=None):
+		# this object has a private Raptor object that can either be
+		# passed in or created internally.
+		
+		if initialiser == None:
+			self.__raptor = raptor.Raptor()
+		else:
+			self.__raptor = initialiser
+			
+	def StringQuery(self, query):
+		"""turn a string into an API call and execute it.
+		
+		This is a convenience method for "lazy" callers.
+		
+		The return value is also converted into a string.
+		"""
+		
+		if query == "aliases":
+			aliases = self.GetAliases()
+			return "".join(map(str, aliases)).strip()
+		
+		elif query == "products":
+			variants = self.GetProducts()
+			return "".join(map(str, variants)).strip()
+		
+		elif query.startswith("config"):
+			match = re.match("config\[(.*)\]", query)
+			if match:
+				config = self.GetConfig(match.group(1))
+				return str(config).strip()
+			else:
+				raise BadQuery("syntax error")
+		
+		raise BadQuery("unknown query")
+
+	def GetAliases(self, type=""):
+		"""extract all aliases of a given type.
+		
+		the default type is "".
+		to get all aliases pass type=None
+		"""
+		aliases = []
+		
+		for a in self.__raptor.cache.aliases.values():
+			if a.type == type or type == None:
+				r = Alias()
+				# copy the members we want to expose
+				r.name = a.name
+				r.meaning = a.meaning
+				aliases.append(r)
+			
+		return aliases
+	
+	def GetConfig(self, name):
+		"""extract the values for a given configuration.
+		
+		'name' should be an alias or variant followed optionally by a
+		dot-separated list of variants. For example "armv5_urel" or
+		"armv5_urel.savespace.vasco".
+		"""
+		
+		r = Config()
+		
+		names = name.split(".")
+		if names[0] in self.__raptor.cache.aliases:
+			x = self.__raptor.cache.FindNamedAlias(names[0])
+			
+			if len(names) > 1:
+				r.fullname = x.meaning + "." + ".".join(names[1:])
+			else:
+				r.fullname = x.meaning
+				
+		elif names[0] in self.__raptor.cache.variants:
+			r.fullname = name
+			
+		else:
+			raise BadQuery("'%s' is not an alias or a variant" % names[0])
+		
+		tmp = raptor_data.Alias("tmp")
+		tmp.SetProperty("meaning", r.fullname)
+		
+		units = tmp.GenerateBuildUnits(self.__raptor.cache)
+		evaluator = self.__raptor.GetEvaluator(None, units[0])
+		
+		releasepath = evaluator.Get("RELEASEPATH")
+		fullvariantpath = evaluator.Get("FULLVARIANTPATH")
+		
+		if releasepath and fullvariantpath:
+			r.outputpath = str(generic_path.Join(releasepath, fullvariantpath))
+		else:
+			raise BadQuery("could not get outputpath for config '%s'" % name)
+		
+		return r
+		
+	def GetProducts(self):
+		"""extract all product variants."""
+		
+		variants = []
+		
+		for v in self.__raptor.cache.variants.values():
+			if v.type == "product":
+				r = Product()
+				# copy the members we want to expose
+				r.name = v.name
+				variants.append(r)
+			
+		return variants
+	
+class BadQuery(Exception):
+	pass
+
+# end of the raptor_api module
--- a/sbsv2/raptor/python/raptor_cli.py	Wed Apr 14 14:40:58 2010 +0100
+++ b/sbsv2/raptor/python/raptor_cli.py	Tue Apr 27 16:46:48 2010 +0100
@@ -18,13 +18,8 @@
 # by a raptor.Raptor object.
 #
 
-import re
 import types
 import raptor
-import os
-import sys
-import tempfile
-from raptor_utilities import getOSPlatform
 
 from optparse import OptionParser # for parsing command line parameters
 
@@ -132,6 +127,18 @@
 parser.add_option("-q","--quiet",action="store_true",dest="quiet",
 				help="Run quietly, not generating output messages.")
 
+parser.add_option("--query",action="append",dest="query",
+				help="""Access various build settings and options using a basic API. The current options are:
+				
+				* aliases - return all the values that can be sensibly used with the sbs -c option.
+				
+				* products - return all the values that can be "." appended to an alias to specialise it for a product build.
+				
+				* config[x] - return a set of values that represent the build configuration "x". Typically "x" will be an alias name or an alias followed by "." followed by a product.
+				
+				Multiple --query options can be given.
+				""")
+
 parser.add_option("-s","--sysdef",action="store",dest="sys_def_file",
 				help="System Definition XML filename.")
 
@@ -245,8 +252,7 @@
 	# parse the full set of arguments
 	(options, leftover_args) = parser.parse_args(expanded_args)
 
-	# the leftover_args are either variable assignments of the form a=b
-	# or target names.
+	# the leftover_args are target names.
 	for leftover in leftover_args:
 		Raptor.AddTarget(leftover)
 
@@ -275,6 +281,7 @@
 				 'noDependGenerate': Raptor.SetNoDependGenerate,
 				 'number_of_jobs': Raptor.SetJobs,
 				 'project_name' :  Raptor.AddProject,
+				 'query' : Raptor.AddQuery,
 				 'filter_list' : Raptor.FilterList,
 				 'ignore_os_detection': Raptor.IgnoreOsDetection,
 				 'check' :  Raptor.SetCheck,
--- a/sbsv2/raptor/python/raptor_data.py	Wed Apr 14 14:40:58 2010 +0100
+++ b/sbsv2/raptor/python/raptor_data.py	Tue Apr 27 16:46:48 2010 +0100
@@ -835,6 +835,7 @@
 		Model.__init__(self)
 		Config.__init__(self)
 		self.name = name
+		self.type = ""
 
 		# Operations defined inside this variant.
 		self.ops = []
@@ -855,6 +856,8 @@
 				self.host = value
 		elif name == "extends":
 			self.extends = value
+		elif name == "type":
+			self.type = value
 		else:
 			raise InvalidPropertyError()
 
@@ -948,6 +951,7 @@
 		Config.__init__(self)
 		self.name = name
 		self.meaning = ""
+		self.type = ""
 		self.varRefs = []
 		self.variants = []
 
@@ -962,6 +966,8 @@
 
 			for u in val.split("."):
 				self.varRefs.append( VariantRef(ref = u) )
+		elif key == "type":
+			self.type = val
 		else:
 			raise InvalidPropertyError()
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sbsv2/raptor/test/config/api.xml	Tue Apr 27 16:46:48 2010 +0100
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<build xmlns="http://symbian.com/xml/build"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://symbian.com/xml/build build/2_0.xsd">
+
+	<!-- test data for raptor_api_unit -->
+
+	<alias meaning="a" name="alias_A"/>
+	<alias meaning="a.b" name="alias_B"/>
+	<alias meaning="a.b.c" name="alias_C" type="O"/>
+	<alias meaning="a.b.c.d" name="alias_D" type="X"/>
+ 
+ 	<var name="product_A" type="product"/>
+ 	<var name="product_B"/>
+ 	<var name="product_C" type="product"/>
+ 	<var name="product_D"/>
+ 	
+ 	<var name="buildme">
+ 		<set name="RELEASEPATH" value="/home/raptor"/>
+ 		<set name="FULLVARIANTPATH" value="foo/bar"/>
+ 	</var>
+ 	
+ 	<var name="foo"/>
+ 	<var name="bar"/>
+ 	
+ 	<alias name="s1" meaning="buildme.foo"/>
+ 	<alias name="s2" meaning="buildme.foo.bar"/>
+ 	
+</build>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sbsv2/raptor/test/smoke_suite/query_cli.py	Tue Apr 27 16:46:48 2010 +0100
@@ -0,0 +1,82 @@
+#
+# Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+# All rights reserved.
+# This component and the accompanying materials are made available
+# under the terms of the License "Eclipse Public License v1.0"
+# which accompanies this distribution, and is available
+# at the URL "http://www.eclipse.org/legal/epl-v10.html".
+#
+# Initial Contributors:
+# Nokia Corporation - initial contribution.
+#
+# Contributors:
+#
+# Description: 
+#
+
+from raptor_tests import SmokeTest
+
+def run():
+	
+	t = SmokeTest()
+	t.description = "Test the --query command-line option"
+
+	t.name = "query_cli_alias"
+	t.command = "sbs --query=aliases"
+	t.mustmatch_singleline = [
+		"<sbs version='2.*'>",
+		"<alias.*name='armv5_urel'.*/>",
+		"<alias.*name='armv5_udeb'.*/>",
+		"<alias.*name='winscw_urel'.*/>",
+		"<alias.*name='winscw_udeb'.*/>",
+		"<alias.*name='tools2_rel'.*/>",
+		"<alias.*name='tools2_deb'.*/>",
+		"</sbs>"
+		]
+	t.mustnotmatch_singleline = [
+		"<alias.*name='make'.*/>",
+		"<alias.*name='emake'.*/>"
+		]
+	t.run()
+	
+	t.name = "query_cli_product"
+	t.command = "sbs --query=products --configpath=test/smoke_suite/test_resources/bv"
+	t.mustmatch_singleline = [
+		"<sbs version='2.*'>",
+		"<product.*name='test_bv_1'.*/>",
+		"<product.*name='test_bv_2'.*/>",
+		"<product.*name='test_bv_3'.*/>",
+		"</sbs>"
+		]
+	t.mustnotmatch_singleline = [
+		"<product.*name='arm'.*/>",
+		"<product.*name='root'.*/>"
+		]
+	t.run()
+	
+	t.name = "query_cli_config"
+	t.command = "sbs --query=config[armv5_urel]"
+	t.mustmatch_singleline = [
+		"<sbs version='2.*'>",
+		"fullname='arm\.v5\.urel\.rvct.*'",
+		"outputpath='.*/epoc32/release/armv5/urel'",
+		"</sbs>"
+		]
+	t.mustnotmatch_singleline = []
+	t.run()
+	
+	t.name = "query_cli_bad"
+	t.command = "sbs --query=nonsense"
+	t.mustmatch_singleline = [
+		"<sbs version='2.*'>",
+		"exception 'unknown query' with query 'nonsense'",
+		"</sbs>"
+		]
+	t.mustnotmatch_singleline = []
+	t.errors = 1
+	t.returncode = 1
+	t.run()
+	
+	t.name = "query_cli"
+	t.print_result()
+	return t
--- a/sbsv2/raptor/test/smoke_suite/test_resources/bv/config/variants/bv_test.xml	Wed Apr 14 14:40:58 2010 +0100
+++ b/sbsv2/raptor/test/smoke_suite/test_resources/bv/config/variants/bv_test.xml	Tue Apr 27 16:46:48 2010 +0100
@@ -5,7 +5,7 @@
 			xsi:schemaLocation="http://symbian.com/xml/build build/2_0.xsd">
 
 		<!-- test feature variants -->
-		<var name="test_bv_1">
+		<var name="test_bv_1" type="product">
 			<set name='FEATUREVARIANTNAME' value='.one' />
 			<set name='FEATURELISTFILES'
 					value='$(SBS_HOME)/test/smoke_suite/test_resources/bv/listA.txt
@@ -16,7 +16,7 @@
 					value='$(SBS_HOME)/test/smoke_suite/test_resources/bv/var1
 					$(SBS_HOME)/test/smoke_suite/test_resources/bv/hrh' />   
   		</var>
-		<var name="test_bv_2">
+		<var name="test_bv_2" type="product">
 			<set name='FEATUREVARIANTNAME' value='.two' />
 			<set name='FEATURELISTFILES'
 					value='$(SBS_HOME)/test/smoke_suite/test_resources/bv/listA.txt
@@ -27,7 +27,7 @@
 					value='$(SBS_HOME)/test/smoke_suite/test_resources/bv/var2
 					$(SBS_HOME)/test/smoke_suite/test_resources/bv/hrh' />
 		</var>
-		<var name="test_bv_3">
+		<var name="test_bv_3" type="product">
 			<set name='FEATUREVARIANTNAME' value='.three' />
 			<set name='FEATURELISTFILES'
 					value='$(SBS_HOME)/test/smoke_suite/test_resources/bv/listA.txt
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sbsv2/raptor/test/unit_suite/raptor_api_unit.py	Tue Apr 27 16:46:48 2010 +0100
@@ -0,0 +1,99 @@
+#
+# Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+# All rights reserved.
+# This component and the accompanying materials are made available
+# under the terms of the License "Eclipse Public License v1.0"
+# which accompanies this distribution, and is available
+# at the URL "http://www.eclipse.org/legal/epl-v10.html".
+#
+# Initial Contributors:
+# Nokia Corporation - initial contribution.
+#
+# Contributors:
+#
+# Description: 
+# raptor_api_unit module
+
+import generic_path
+import raptor
+import raptor_api
+import unittest
+
+class TestRaptorApi(unittest.TestCase):
+			
+	def testContext(self):
+		api = raptor_api.Context()
+		
+	def testContextInitialiser(self):
+		r = raptor.Raptor()
+		api = raptor_api.Context(r)
+		
+	def testAliases(self):
+		r = raptor.Raptor()
+		r.cache.Load( generic_path.Join(r.home, "test", "config", "api.xml") )
+
+		api = raptor_api.Context(r)
+	
+		aliases = api.GetAliases() # type == ""
+		self.failUnlessEqual(len(aliases), 4)
+		self.failUnlessEqual(set(["alias_A","alias_B","s1","s2"]),
+							 set(a.name for a in aliases))
+		
+		aliases = api.GetAliases(None) # ignore type
+		self.failUnlessEqual(len(aliases), 6)
+		
+		aliases = api.GetAliases("X") # type == "X"
+		self.failUnlessEqual(len(aliases), 1)
+		self.failUnlessEqual(aliases[0].name, "alias_D")
+		self.failUnlessEqual(aliases[0].meaning, "a.b.c.d")
+	
+	def testConfig(self):
+		r = raptor.Raptor()
+		r.cache.Load( generic_path.Join(r.home, "test", "config", "api.xml") )
+
+		api = raptor_api.Context(r)
+		
+		config = api.GetConfig("buildme")
+		self.failUnlessEqual(config.fullname, "buildme")
+		self.failUnlessEqual(config.outputpath, "/home/raptor/foo/bar")
+		
+		config = api.GetConfig("buildme.foo")
+		self.failUnlessEqual(config.fullname, "buildme.foo")
+		self.failUnlessEqual(config.outputpath, "/home/raptor/foo/bar")
+		
+		config = api.GetConfig("s1")
+		self.failUnlessEqual(config.fullname, "buildme.foo")
+		self.failUnlessEqual(config.outputpath, "/home/raptor/foo/bar")
+		
+		config = api.GetConfig("s2.product_A")
+		self.failUnlessEqual(config.fullname, "buildme.foo.bar.product_A")
+		self.failUnlessEqual(config.outputpath, "/home/raptor/foo/bar")
+		
+	def testProducts(self):
+		r = raptor.Raptor()
+		r.cache.Load( generic_path.Join(r.home, "test", "config", "api.xml") )
+
+		api = raptor_api.Context(r)
+		
+		products = api.GetProducts() # type == "product"
+		self.failUnlessEqual(len(products), 2)
+		self.failUnlessEqual(set(["product_A","product_C"]),
+							 set(p.name for p in products))
+		
+# run all the tests
+
+from raptor_tests import SmokeTest
+
+def run():
+	t = SmokeTest()
+	t.name = "raptor_api_unit"
+
+	tests = unittest.makeSuite(TestRaptorApi)
+	result = unittest.TextTestRunner(verbosity=2).run(tests)
+
+	if result.wasSuccessful():
+		t.result = SmokeTest.PASS
+	else:
+		t.result = SmokeTest.FAIL
+
+	return t
--- a/sbsv2/raptor/test/unit_suite/raptor_cli_unit.py	Wed Apr 14 14:40:58 2010 +0100
+++ b/sbsv2/raptor/test/unit_suite/raptor_cli_unit.py	Tue Apr 27 16:46:48 2010 +0100
@@ -153,6 +153,9 @@
 	def AddProject(self, project):
 		return True
 
+	def AddQuery(self, query):
+		return True
+	
 	def PrintVersion(self):
 		return True