build.py 55 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778
  1. #
  2. # Copyright (c) 2006-2011 Nokia Corporation and/or its subsidiary(-ies).
  3. # All rights reserved.
  4. # This component and the accompanying materials are made available
  5. # under the terms of the License "Eclipse Public License v1.0"
  6. # which accompanies this distribution, and is available
  7. # at the URL "http://www.eclipse.org/legal/epl-v10.html".
  8. #
  9. # Initial Contributors:
  10. # Nokia Corporation - initial contribution.
  11. #
  12. # Contributors:
  13. #
  14. # Description:
  15. # raptor module
  16. # This module represents the running Raptor program. Raptor is started
  17. # either by calling the Main() function, which creates an instance of
  18. # the Raptor class and calls its methods to perform a build based
  19. # on command-line parameters, or by explicitly creating a Raptor
  20. # instance and calling its methods to set-up and perform a build.
  21. #
  22. name = "sbs" # the public name for the raptor build tool
  23. env = "SBS_HOME" # the environment variable that locates us
  24. xml = "sbs_init.xml" # the primary initialisation file
  25. env2 = "HOME" # the environment variable that locates the user
  26. xml2 = ".sbs_init.xml" # the override initialisation file
  27. import sys
  28. import os
  29. import stat
  30. import time
  31. import traceback
  32. import subprocess
  33. import types
  34. import json
  35. from xml.sax.saxutils import escape
  36. from raptor import generic_path
  37. import raptor.cache
  38. import raptor.cli
  39. import raptor.data
  40. import raptor.make
  41. import raptor.meta
  42. import raptor.utilities
  43. import raptor.version
  44. import raptor.timing as timing
  45. from raptor.makefile import MakefileSet
  46. from raptor.sysdef import SystemModel
  47. from raptor.filter_list import FilterList
  48. from raptor.pluginbox import PluginBox
  49. from raptor.buildrecord import BuildRecord
  50. if not "HOSTPLATFORM" in os.environ or not "HOSTPLATFORM_DIR" in os.environ or not "HOSTPLATFORM32_DIR" in os.environ:
  51. sys.stderr.write("Error: HOSTPLATFORM, HOSTPLATFORM_DIR and HOSTPLATFORM32_DIR must be set in the environment (this is usually done automatically by the startup script).\n")
  52. sys.exit(1)
  53. hostplatform = set(os.environ["HOSTPLATFORM"].split(" "))
  54. unixplatforms = set(['linux','freebsd','darwin','sunos'])
  55. isunix = not hostplatform.isdisjoint(unixplatforms)
  56. hostplatform_dir = os.environ["HOSTPLATFORM_DIR"]
  57. hostplatform32_dir = os.environ["HOSTPLATFORM32_DIR"]
  58. # defaults can use EPOCROOT
  59. if "EPOCROOT" in os.environ:
  60. incoming_epocroot = os.environ["EPOCROOT"]
  61. epocroot = incoming_epocroot.replace("\\","/")
  62. else:
  63. if 'win' in hostplatform:
  64. incoming_epocroot = os.sep
  65. epocroot = "/"
  66. os.environ["EPOCROOT"] = os.sep
  67. else:
  68. epocroot=os.environ['HOME'] + os.sep + "epocroot"
  69. os.environ["EPOCROOT"] = epocroot
  70. incoming_epocroot = epocroot
  71. if "SBS_BUILD_DIR" in os.environ:
  72. sbs_build_dir = os.environ["SBS_BUILD_DIR"]
  73. else:
  74. sbs_build_dir = (epocroot + "/epoc32/build").replace("//","/")
  75. # only use default XML from the epoc32 tree if it exists
  76. defaultSystemConfig = "lib/config"
  77. epoc32UserConfigDir = generic_path.Join(epocroot, "epoc32/sbs_config")
  78. if epoc32UserConfigDir.isDir():
  79. defaultSystemConfig = str(epoc32UserConfigDir) + os.pathsep + defaultSystemConfig
  80. # parameters that can be overriden by the sbs_init.xml file
  81. # or by the command-line.
  82. defaults = {
  83. "allowCommandLineOverrides" : True,
  84. "CLI" : "raptor_cli",
  85. "buildInformation" : generic_path.Path("bld.inf"),
  86. "defaultConfig" : "default",
  87. "jobs": 4,
  88. "keepGoing": False,
  89. "logFileName" : generic_path.Join(sbs_build_dir,"Makefile.%TIME.log"),
  90. "makeEngine" : "make",
  91. "preferBuildInfoToSystemDefinition" : False,
  92. "pruneDuplicateMakefiles": True,
  93. "quiet" : False,
  94. "systemConfig" : defaultSystemConfig,
  95. "systemDefinition" : generic_path.Path("System_Definition.xml"),
  96. "systemDefinitionBase" : generic_path.Path("."),
  97. "systemFLM" : generic_path.Path("lib/flm"),
  98. "systemPlugins" : generic_path.Path("raptor/plugins"),
  99. "topMakefile" : generic_path.Join(sbs_build_dir,"Makefile.%TIME"),
  100. "tries": 1,
  101. "writeSingleMakefile": True,
  102. "ignoreOsDetection": False,
  103. "toolcheck": "on",
  104. "incremental_parsing": False,
  105. "no_metadata_depend": False,
  106. "ignore_zero_flmcall_makefiles": False,
  107. "filterList": "filterterminal,filterlogfile"
  108. }
  109. class ModelNode(object):
  110. """ Represents any node in a a tree of build information
  111. e.g. a tree of bld.infs, mmps and finally things like resource blocks and string table blocks.
  112. This is before they are produced into "build" specs.
  113. """
  114. def __init__(self, id, parent = None):
  115. self.id = id
  116. self.type = type
  117. self.specs = []
  118. self.deps = [] # files that this node depends on
  119. self.depfiles = [] # files with dependencies listed in them in GNU make format
  120. self.children = set()
  121. self.unfurled = False
  122. self.parent = parent
  123. # Allow one to make a set
  124. def __hash__(self):
  125. return hash(self.id)
  126. def __eq__(self,other):
  127. return self.id == other.id
  128. def __iter__(self):
  129. return iter(self.children)
  130. def __getitem__(self,x):
  131. if isinstance(x, slice):
  132. return self.children[x.start:x.stop]
  133. return self.children[x]
  134. def __setitem__(self,k, v):
  135. self.children[k] = v
  136. def __len__(self):
  137. return len(self.children)
  138. def add(self, item):
  139. return self.children.add(item)
  140. def isunfurled(self, c):
  141. return self.unfurled == False
  142. def unique_filename(self):
  143. """ returns a string that is short enough to add to a filename that is believed to be unique
  144. within a single build. This really should be overridden by children """
  145. # not a great default but we're not supposed to know what self.id contains so we can't e.g. process it like a filename
  146. return raptor.utilities.sanitise(hash(id))
  147. def alldeps(self):
  148. """ All the known simple dependencies of the object represented by this Node.
  149. This doesn't include dependencies that are generated by tools like CPP - alldepfiles() does that"""
  150. for d in self.deps:
  151. yield d
  152. for c in self.children:
  153. for d in c.alldeps():
  154. yield d
  155. def alldepfiles(self):
  156. """Dependencies are sometimes discovered by tools like CPP and then stored in files"""
  157. for d in self.depfiles:
  158. yield d
  159. for c in self.children:
  160. for d in c.alldepfiles():
  161. yield d
  162. def unfurl(self, build):
  163. """Find any children of this node by processing it, produces specs"""
  164. pass
  165. def unfurl_all(self, build):
  166. """Unfurl self and all children - preparatory e.g for realisation"""
  167. if not self.unfurled:
  168. self.unfurl(build)
  169. self.realise_exports(build) # permit communication of dependencies between children
  170. for c in self.children:
  171. c.unfurl_all(build)
  172. def realise_exports(self, build):
  173. """Do the things that are needed such that we can fully unfurl all
  174. sibling nodes. i.e. this step is here to "take care" of the dependencies
  175. between siblings.
  176. """
  177. pass
  178. def makefile_name(self, build):
  179. """use data from the build object to determine the makefilename for this node"""
  180. postfix = "_" + self.unique_filename()
  181. makefilename_base = build.topMakefile.Absolute()
  182. return str(makefilename_base) + postfix
  183. def realise_makefile(self, build, specs):
  184. makefile = self.makefile_name(build)
  185. metadepsfilename = makefile + ".metadeps"
  186. try:
  187. os.makedirs(str(generic_path.Path(makefile).Dir()))
  188. except OSError as e:
  189. pass # if the dir is already there
  190. build.Debug("Layer Deps: {0} with {1} children depfile {2}".format(self.id, len(self.children),metadepsfilename))
  191. build.InfoDiscovery(object_type = "layers", count = 1)
  192. build.InfoStartTime(object_type = "layer", task = "parse",
  193. key = makefile)
  194. makefileset = build.maker.Write(generic_path.Path(makefile), specs, build.buildUnitsToBuild)
  195. makefileset.write_metadeps(self.alldeps(), self.alldepfiles()) # ensure that this makefileset's dependency file has been written out
  196. build.InfoEndTime(object_type = "layer", task = "parse",
  197. key = makefile)
  198. # Tell the buildrecord about this set of makefiles
  199. build.build_record.record_makefileset(makefileset)
  200. return makefileset
  201. def realise(self, build):
  202. """Give the spec trees to the make engine and actually
  203. "build" the product represented by this model node"""
  204. # Must ensure that all children are unfurled at this point
  205. self.unfurl_all(build)
  206. sp = self.specs
  207. build.AssertBuildOK()
  208. makefileset = self.realise_makefile(build, sp)
  209. build.InfoStartTime(object_type = "layer", task = "build",
  210. key = (str(makefileset.directory) + "/" + str(makefileset.filenamebase)))
  211. result = build.Make(makefileset)
  212. build.InfoEndTime(object_type = "layer", task = "build",
  213. key = (str(makefileset.directory) + "/" + str(makefileset.filenamebase)))
  214. return result
  215. class Project(ModelNode):
  216. """A project or, in symbian-speak, an MMP
  217. """
  218. def __init__(self, filename, parent = None):
  219. super(Project,self).__init__(filename, parent = parent)
  220. # Assume that components are specified in mmp files for now
  221. # One day that tyranny might end.
  222. self.mmp_name = str(generic_path.Path.Absolute(filename))
  223. self.id = self.mmp_name
  224. self.unfurled = False
  225. self.deps.append(self.mmp_name)
  226. def makefile(self, makefilename_base, engine, named = False):
  227. """Makefiles for individual mmps not feasible at the moment"""
  228. pass
  229. # Cannot, currently, "unfurl an mmp" directly but do want
  230. # to be able to simulate the overall recursive unfurling of a build.
  231. class Component(ModelNode):
  232. """ An Abstract class for group of projects (where projects are
  233. usually things that represent one program or libary) """
  234. def __init__(self, filename, layername="", componentname=""):
  235. super(Component,self).__init__(filename)
  236. self.filename = filename
  237. self.id = str(filename)
  238. self.exportspecs = []
  239. self.depfiles = []
  240. self.unfurled = False # We can parse this
  241. self.deps.append(str(self.filename))
  242. # Extra metadata optionally supplied with system definition file gathered components
  243. self.layername = layername
  244. self.name = componentname
  245. def render_bldinf(self, build):
  246. raise Exception("Can't render a bld.inf from component {0} - don't know how".format(self.filename))
  247. class BldinfComponent(Component):
  248. """A group of projects or, in symbian-speak, a bld.inf.
  249. """
  250. def __init__(self, filename, layername="", componentname=""):
  251. super(BldinfComponent,self).__init__(filename, layername = layername, componentname=componentname)
  252. # Assume that components are specified in bld.inf files for now
  253. # One day that tyranny might end.
  254. self.bldinf = None # Slot for a bldinf object if we spot one later
  255. self.bldinf_filename = generic_path.Path(str(filename)).Absolute()
  256. def AddMMP(self, filename):
  257. self.children.add(Project(filename))
  258. def render_bldinf(self, build):
  259. return self
  260. class QmakeErrorException(Exception):
  261. def __init__(self, text, output = "", errorcode=1,command=""):
  262. self.output = output
  263. self.errorcode = errorcode
  264. self.command=command
  265. def __str__(self):
  266. return "{0} - while running: {1}".format(self.output,self.command)
  267. class QtProComponent(BldinfComponent):
  268. """ represents a component that is specified in a .pro file. The intention is that it should
  269. be possible to translate it into a bld.inf as we don't intend to parse Qt files directly which
  270. would be an immense and pointless effort."""
  271. def __init__(self, filename, layername="", componentname=""):
  272. self.qtpro_filename = generic_path.Path(filename).Absolute()
  273. super(QtProComponent,self).__init__(filename,layername=layername, componentname=componentname)
  274. # automatically determine the related bld.inf name by putting it in the same dir as the qt file.
  275. self.bldinf_filename = generic_path.Join(self.qtpro_filename.Dir(), "bld.inf")
  276. self.bldinf_produced = False
  277. def render_bldinf(self, build):
  278. self.bldinf_produced = True
  279. qmake = build.metaeval.Get("QMAKE")
  280. moc = build.metaeval.Get("MOC")
  281. uic = build.metaeval.Get("UIC")
  282. rcc = build.metaeval.Get("RCC")
  283. # run qmake and produce the bld.inf immediately.
  284. shell = "/bin/sh" # only needed on linux.
  285. # should really get qmake(.exe)'s absolute location from somewhere
  286. global epocroot
  287. spec = build.metaeval.Get("QMAKESPEC")
  288. incdir = build.metaeval.Get("QMAKE_INCDIR_QT")
  289. headers = build.metaeval.Get("QT_HEADERS")
  290. makeenv = os.environ.copy()
  291. if isunix:
  292. command = [qmake, "-spec", spec, str(self.qtpro_filename), "-o",str(self.bldinf_filename),
  293. "QMAKE_INCDIR_QT=" + headers,
  294. "QMAKE_MOC=" + moc,
  295. "QMAKE_UIC=" + uic,
  296. "QMAKE_RCC=" + rcc]
  297. build.Debug("qmake command: {0}".format(command))
  298. p = subprocess.Popen(
  299. args = command,
  300. bufsize = 65535,
  301. stdout = subprocess.PIPE,
  302. stderr = subprocess.STDOUT,
  303. shell = False,
  304. universal_newlines = True,
  305. env = makeenv)
  306. else:
  307. command = "{0} -spec {1} {2} -o {3} QMAKE_INCDIR_QT={4} QMAKE_MOC={5} QMAKE_UIC={6} QMAKE_RCC={7}".format(qmake, spec, self.qtpro_filename, self.bldinf_filename, headers, moc,uic,rcc)
  308. build.Debug("qmake command: {0}".format(command))
  309. p = subprocess.Popen(
  310. args = command,
  311. bufsize = 65535,
  312. stdout = subprocess.PIPE,
  313. stderr = subprocess.STDOUT,
  314. shell = False,
  315. env = makeenv)
  316. stream = p.stdout
  317. self.qmake_output = []
  318. for l in stream:
  319. self.qmake_output.append(l)
  320. returncode = p.wait()
  321. if returncode != 0:
  322. e = QmakeErrorException("{0} failed for '{1}'".format(qmake, self.qtpro_filename), output = "\n".join(self.qmake_output), errorcode = returncode, command = command)
  323. raise e
  324. return self
  325. class Layer(ModelNode):
  326. """ Some components that should be built togther
  327. e.g. a Layer in the system definition.
  328. Components that come from system definition files can
  329. have extra surrounding metadata that we need to pass
  330. on for use in log output.
  331. """
  332. def __init__(self, name, componentlist=[]):
  333. """ componentlist may be a list of items of type Component xor type sysdef.SystemModelComponent
  334. @componentlist must be a list of objects that are derived from the Component class.
  335. """
  336. super(Layer,self).__init__(name)
  337. self.name = name
  338. for c in componentlist:
  339. # this is a component from the plain old command-line and we expect it to be of type "Component" already.
  340. self.children.add(c)
  341. def unique_filename(self):
  342. """ returns a string that is short enough to add to a filename that is believed to be unique
  343. within a single build. """
  344. # the layer name should be unique
  345. return raptor.utilities.sanitise(self.name)
  346. @classmethod
  347. def from_system_model(cls, name, sysmodel_componentlist):
  348. """ A factory method to build a layer from a sysdef.SystemModelComponent
  349. this eases the process of working with a "system_definition.xml" file. """
  350. l = cls(name) # Call our class' constructor
  351. for c in sysmodel_componentlist:
  352. l.children.add(BldinfComponent(c, c.GetLayerName(), c.GetContainerName("component")))
  353. return l
  354. def unfurl(self, build):
  355. """Discover the children of this layer. This involves parsing the component MetaData (bld.infs, mmps).
  356. Takes a raptor object as a parameter (build), together with a list of Configurations.
  357. We currently have parsers that work on collections of components/bld.infs and that cannot
  358. parse at a "finer" level. So one can't 'unfurl' an mmp at the moment.
  359. Returns True if the object was successfully unfurled.
  360. """
  361. # setup all our components
  362. for c in self.children:
  363. c.specs = []
  364. self.configs = build.buildUnitsToBuild
  365. # render the components down to bld.inf form (if possible)
  366. # since we don't understand any other component format
  367. components = []
  368. for c in self.children:
  369. try:
  370. components.append(c.render_bldinf(build))
  371. except QmakeErrorException as e:
  372. build.Error(str(e))
  373. if len(components) > 0:
  374. try:
  375. # create a MetaReader that is aware of the list of
  376. # configurations that we are trying to build.
  377. metaReader = raptor.meta.MetaReader(build, build.buildUnitsToBuild)
  378. # convert the list of bld.inf files into a specification
  379. # hierarchy suitable for all the configurations we are using.
  380. self.specs = list(build.generic_specs)
  381. self.specs.extend(metaReader.ReadBldInfFiles(components, doexport = build.doExport, dobuild = not build.doExportOnly))
  382. except raptor.meta.MetaDataError as e:
  383. build.Error(e.Text)
  384. self.unfurled = True
  385. def _split_into_blocks(self, build):
  386. """ Split layer's components into blocks for parallel parsing """
  387. nc = len(self.children)
  388. number_blocks = build.jobs
  389. block_size = int((nc / number_blocks) + 1)
  390. component_blocks = [] # list of mini-layers, split up for parallel parsing
  391. b = 0
  392. childlist = list(self.children)
  393. while b < nc:
  394. l = Layer(self.name, childlist[b:b+block_size])
  395. component_blocks.append(l)
  396. b += block_size
  397. while len(component_blocks[-1].children) <= 0:
  398. component_blocks.pop()
  399. number_blocks -= 1
  400. build.Info("Parallel Parsing: bld.infs split into {0} blocks\n".format(number_blocks))
  401. return component_blocks
  402. def meta_realise(self, build):
  403. """Generate specs that can be used to "take care of" finding out more
  404. about this metaunit - i.e. one doesn't want to parse it immediately
  405. but to create a makefile that will parse it.
  406. In this case it allows bld.infs to be parsed in parallel by make."""
  407. # insert the start time into the Makefile name?
  408. self.configs = build.buildUnitsToBuild
  409. # Pass certain CLI flags through to the makefile-generating sbs calls
  410. cli_options = ""
  411. if build.debugOutput == True:
  412. cli_options += " -d"
  413. if build.ignoreOsDetection == True:
  414. cli_options += " -i"
  415. if build.keepGoing == True:
  416. cli_options += " -k"
  417. if build.quiet == True:
  418. cli_options += " -q"
  419. if build.noDependInclude == True:
  420. cli_options += " --no-depend-include"
  421. if build.noDependGenerate == True:
  422. cli_options += " --no-depend-generate"
  423. tm = build.topMakefile.Absolute()
  424. # List of all confgurations apart from "build" which
  425. # is used internally for generating makefiles for
  426. # parallel parsing
  427. configList = " ".join([c.name for c in self.configs if c.name != "build" ])
  428. # Cause the binding makefiles to have the toplevel makefile's
  429. # name. The bindee's have __pp appended.
  430. binding_makefiles = MakefileSet(str(tm.Dir()), build.maker.selectors, makefiles=None, filenamebase=str(tm.File()))
  431. build.topMakefile = generic_path.Path(str(tm) + "_pp")
  432. component_blocks = self._split_into_blocks(build)
  433. spec_nodes = []
  434. loop_number = 0
  435. for block in component_blocks:
  436. loop_number += 1
  437. specNode = raptor.data.Specification("metadata_" + self.name)
  438. # root path for generated sysdef files and their partnering makefiles
  439. makefile_path = str(build.topMakefile) + "_" + str(loop_number)
  440. try:
  441. os.unlink(makefile_path)
  442. except Exception:
  443. pass
  444. pp_system_definition = makefile_path + ".sysdef.xml"
  445. try:
  446. sys_def_writer = SystemModel(build, aDoRead=False)
  447. sys_def_writer.AddLayer(block)
  448. sys_def_writer.Write(pp_system_definition)
  449. build.Debug("Wrote intermediate parallel-parsing system definition file " + pp_system_definition)
  450. except Exception as e:
  451. build.Error("Failed to write intermediate parallel-parsing system definition file: {0} {1}".format(pp_system_definition,str(e)))
  452. raise e
  453. configList = " ".join([c.name for c in self.configs if c.name != "build" ])
  454. # add some basic data in a component-wide variant
  455. var = raptor.data.Variant()
  456. var.AddOperation(raptor.data.Set("PP_SYSTEM_DEFINITION", pp_system_definition))
  457. var.AddOperation(raptor.data.Set("MAKEFILE_PATH", makefile_path))
  458. var.AddOperation(raptor.data.Set("CONFIGS", configList))
  459. var.AddOperation(raptor.data.Set("CLI_OPTIONS", cli_options))
  460. # Allow the flm to skip exports. Note: this parameter
  461. doexport_str = '1'
  462. if not build.doExport:
  463. doexport_str = ''
  464. var.AddOperation(raptor.data.Set("DOEXPORT", doexport_str ))
  465. # Pass on '-n' (if specified) to the makefile-generating sbs calls
  466. if build.noBuild:
  467. var.AddOperation(raptor.data.Set("NO_BUILD", "1"))
  468. specNode.AddVariant(var)
  469. try:
  470. interface = build.cache.FindNamedInterface("build.makefiles")
  471. specNode.SetInterface(interface)
  472. except KeyError:
  473. build.Error("Can't find flm interface 'build.makefiles' ")
  474. spec_nodes.append(specNode)
  475. binding_makefiles.addInclude(str(makefile_path)+"_all")
  476. build.InfoDiscovery(object_type = "layers", count = 1)
  477. build.InfoStartTime(object_type = "layer", task = "parse",
  478. key = str(build.topMakefile))
  479. # Generate the makefileset and build it
  480. mset = self.realise_makefile(build, spec_nodes)
  481. mset.close()
  482. gen_result = build.Make(mset)
  483. build.InfoEndTime(object_type = "layer", task = "parse",
  484. key = str(build.topMakefile))
  485. build.InfoStartTime(object_type = "layer", task = "build",
  486. key = str(build.topMakefile))
  487. build.Debug("Binding Makefile base name is {0} ".format(binding_makefiles.filenamebase))
  488. binding_makefiles.close()
  489. b = build.Make(binding_makefiles)
  490. build.InfoEndTime(object_type = "layer", task = "build",
  491. key = str(build.topMakefile))
  492. return b
  493. class BuildCannotProgressException(Exception):
  494. pass
  495. # raptor module classes
  496. class Raptor(object):
  497. """An instance of a running Raptor program.
  498. When operated from the command-line there is a single Raptor object
  499. created by the Main function. When operated by an IDE several Raptor
  500. objects may be created and operated at the same time.
  501. """
  502. # mission enumeration
  503. M_BUILD = 1
  504. M_QUERY = 2
  505. M_VERSION = 3
  506. def __init__(self, home = None, commandline = [], dotargets = True, logger = None, load_defaults = True):
  507. """
  508. Keyword Arguments:
  509. home - where to load xml settings from (default None indicated the current working directory)
  510. commandline - potentially a commandline full of options to set (default empty list)
  511. targets - when processing a commandline this ensures
  512. that correct make targets are added e.g. "WHAT" if doing --what. (Default True)
  513. logger - a class that provides Debug, Info, Warning and Error
  514. functions ( default is an internal logger )"""
  515. self.args = commandline
  516. self.dotargets = dotargets
  517. self.logger = logger
  518. if self.logger is not None:
  519. # Patching functions out here rather than putting
  520. # an if statement in each function seems
  521. # a little more efficient although it may
  522. # be over optimisation.
  523. self.Info = self.logger.Info
  524. self.Debug = self.logger.Debug
  525. self.Warn = self.logger.Warn
  526. self.load_defaults = load_defaults
  527. self._default_setup(home)
  528. # Load up the all the other XML configuration data:
  529. self.configPath = generic_path.NormalisePathList(self.systemConfig.split(os.pathsep))
  530. # If there are any commandline arguments then apply them
  531. if len(commandline) > 0:
  532. # remember the arguments for the log
  533. self.args = commandline
  534. # assuming self.CLI = "cli"
  535. if not raptor.cli.GetArgs(self, self.args):
  536. # raise exception quietly since the error
  537. # messages are already printed out from GetArgs
  538. raise BuildCannotProgressException("")
  539. # With incremental parsing which is itself an experimental feature
  540. # it is relatively safe to switch on a feature that doesn't execute
  541. # Makefiles if they have no flm calls in them
  542. if self.incremental_parsing:
  543. self.ignore_zero_flmcall_makefiles = True
  544. # Validate our current state
  545. if self.incremental_parsing and self.doParallelParsing:
  546. raise BuildCannotProgressException("The parallel parsing (--pp=on) and incremental parsing (--ip=on) options cannot be used together at the moment")
  547. if (self.dotargets):
  548. self._check_and_set_build_targets()
  549. def _default_setup(self, home = None):
  550. """the default set-up state"""
  551. self.errorCode = 0
  552. self.skipAll = False
  553. self.summary = True
  554. self.out = sys.stdout # Just until filters get started.
  555. # Create a bootstrap output system.
  556. self.out = FilterList()
  557. if home == None:
  558. try:
  559. home = os.environ[env]
  560. except KeyError:
  561. home = os.getcwd()
  562. # make sure the home directory exists
  563. self.home = generic_path.Path(home).Absolute()
  564. if not self.home.isDir():
  565. self.Error("{0} '{1}' is not a directory".format(env, self.home))
  566. raise BuildCannotProgressException("{0} '{1}' is not a directory".format(env, str(self.home)))
  567. # the set-up file location.
  568. # use the override "env2/xml2" if it exists
  569. # else use the primary "env/xml" if it exists
  570. # else keep the hard-coded defaults.
  571. self.raptorXML = self.home.Append(xml)
  572. if env2 in os.environ:
  573. sbs_init = generic_path.Join(os.environ[env2], xml2)
  574. if sbs_init.isFile():
  575. self.raptorXML = sbs_init
  576. # things that can be overridden by the set-up file
  577. for key, value in defaults.items():
  578. self.__dict__[key] = value
  579. # things to initialise
  580. self.args = []
  581. self.layers = []
  582. self.orderLayers = False
  583. self.commandline_layer = Layer('commandline')
  584. self.systemModel = None
  585. self.systemDefinitionFile = None
  586. self.systemDefinitionRequestedLayers = []
  587. self.systemDefinitionOrderLayers = False
  588. self.specGroups = {}
  589. self.configNames = []
  590. self.configsToBuild = set()
  591. self.makeOptions = []
  592. self.maker = None
  593. self.debugOutput = False
  594. self.doExportOnly = False
  595. self.doExport = True
  596. self.noBuild = False
  597. self.noDependInclude = False
  598. self.noDependGenerate = False
  599. self.projects = set()
  600. self.queries = []
  601. self.cache = raptor.cache.Cache(self)
  602. self.override = {env: str(self.home)}
  603. self.targets = []
  604. self.defaultTargets = []
  605. self.doCheck = False
  606. self.doWhat = False
  607. self.doParallelParsing = False
  608. self.doCaseFolding_rsg = False
  609. self.mission = Raptor.M_BUILD
  610. # what platform and filesystem are we running on?
  611. self.filesystem = raptor.utilities.getOSFileSystem()
  612. self.timing = True # Needed by filters such as copy_file to monitor progress
  613. self.toolset = None
  614. self.starttime = time.time()
  615. # Create a unique time id that combines millisecond part of start time
  616. # and id of the process which Raptor python is running on
  617. timeid = str(round(self.starttime, 3)).split('.')[1] + '-' + str(os.getpid())
  618. self.timestring = time.strftime("%Y-%m-%d-%H-%M-%S.") + timeid
  619. self.fatalErrorState = False
  620. if self.load_defaults:
  621. # Load up the raptor defaults from XML (formerly from the ConfigFile function)
  622. if self.raptorXML.isFile():
  623. self.cache.Load(self.raptorXML)
  624. # find the 'defaults.raptor' variant and extract the values
  625. try:
  626. var = self.cache.FindNamedVariant("defaults.init")
  627. evaluator = self.GetEvaluator( None, raptor.data.BuildUnit(var.name,[var]) )
  628. for key, value in defaults.items():
  629. newValue = evaluator.Resolve(key)
  630. if newValue != None:
  631. # got a string for the value
  632. if type(value) == bool:
  633. newValue = (newValue.lower() != "false")
  634. elif type(value) == int:
  635. newValue = int(newValue)
  636. elif isinstance(value, generic_path.Path):
  637. newValue = generic_path.Path(newValue)
  638. self.__dict__[key] = newValue
  639. except KeyError:
  640. # it is OK to not have this but useful to say it wasn't there
  641. self.Info("No 'defaults.init' configuration found in " + str(self.raptorXML))
  642. def _load_cache(self):
  643. """Before initiating any action like a build or query, we should load up all
  644. xml configuration. This function is not intended for use except by other
  645. members of the Raptor class.
  646. """
  647. self.InfoStartTime(object_type = "config_cache", task = "load", key="cache")
  648. def mkAbsolute(aGenericPath):
  649. """ internal function to make a generic_path.Path
  650. absolute if required"""
  651. if not aGenericPath.isAbsolute():
  652. return self.home.Append(aGenericPath)
  653. else:
  654. return aGenericPath
  655. # make generic paths absolute (if required)
  656. self.configPath = [mkAbsolute(p) for p in self.configPath]
  657. self.cache.Load(self.configPath)
  658. if not self.systemFLM.isAbsolute():
  659. self.systemFLM = self.home.Append(self.systemFLM)
  660. self.cache.Load(self.systemFLM)
  661. # Make it possible to ask this instance about default tools locations without
  662. # doing the evaluator creation repeatedly for no reason
  663. self.metavariant = self.cache.FindNamedVariant("meta")
  664. self.metaeval = self.GetEvaluator(None, raptor.data.BuildUnit(self.metavariant.name, [self.metavariant]) )
  665. self.InfoEndTime(object_type = "config_cache", task = "load", key="cache")
  666. def AddConfigList(self, configPathList):
  667. # this function converts cmd line option into a list
  668. # and prepends it to default config.
  669. self.configPath = generic_path.NormalisePathList(configPathList.split(os.pathsep)) + self.configPath
  670. return True
  671. def AddConfigName(self, name):
  672. if name == "build":
  673. traceback.print_stack((sys.stdout))
  674. sys.exit(1)
  675. self.configNames.append(name)
  676. return True
  677. def RunQuietly(self, TrueOrFalse):
  678. self.quiet = TrueOrFalse
  679. return True
  680. def SetCheck(self, TrueOrFalse):
  681. self.doCheck = TrueOrFalse
  682. return True
  683. def SetWhat(self, TrueOrFalse):
  684. self.doWhat = TrueOrFalse
  685. return True
  686. def SetEnv(self, name, value):
  687. self.override[name] = value
  688. def AddTarget(self, target):
  689. if self.doCheck or self.doWhat:
  690. self.Warn("ignoring target {0} because --what or --check is specified.\n".format(target))
  691. else:
  692. self.targets.append(target)
  693. def AddSourceTarget(self, filename):
  694. # source targets are sanitised and then added as if they were a "normal" makefile target
  695. # in addition they have a default, empty, top-level target assigned in order that they can
  696. # be presented to any generated makefile without error
  697. sourceTarget = generic_path.Path(filename).Absolute()
  698. sourceTarget = 'SOURCETARGET_' + raptor.utilities.sanitise(str(sourceTarget))
  699. self.AddTarget(sourceTarget)
  700. self.defaultTargets.append(sourceTarget)
  701. return True
  702. def SetSysDefFile(self, filename):
  703. self.systemDefinitionFile = generic_path.Path(filename)
  704. return True
  705. def SetSysDefBase(self, path):
  706. self.systemDefinitionBase = generic_path.Path(path)
  707. return True
  708. def AddSysDefLayer(self, layer):
  709. self.systemDefinitionRequestedLayers.append(layer)
  710. return True
  711. def SetSysDefOrderLayers(self, TrueOrFalse):
  712. self.systemDefinitionOrderLayers = TrueOrFalse
  713. return True
  714. def AddBuildInfoFile(self, filename):
  715. bldinf = str(generic_path.Path(filename).Absolute())
  716. self.commandline_layer.add(BldinfComponent(bldinf))
  717. return True
  718. def AddQtProFile(self, filename):
  719. qt_pro_file = str(generic_path.Path(filename).Absolute())
  720. self.commandline_layer.add(QtProComponent(qt_pro_file))
  721. return True
  722. def SetTopMakefile(self, filename):
  723. self.topMakefile = generic_path.Path(filename)
  724. return True
  725. def SetDebugOutput(self, TrueOrFalse):
  726. self.debugOutput = TrueOrFalse
  727. return True
  728. def SetExportOnly(self, TrueOrFalse):
  729. self.doExportOnly = TrueOrFalse
  730. if not self.doExport:
  731. self.Error("The --noexport and --export-only options are incompatible - won't to do anything useful")
  732. return False
  733. return True
  734. def SetNoExport(self, TrueOrFalse):
  735. self.doExport = not TrueOrFalse
  736. if self.doExportOnly:
  737. self.Error("The --noexport and --export-only options are incompatible - won't to do anything useful")
  738. return False
  739. return True
  740. def SetNoBuild(self, TrueOrFalse):
  741. self.noBuild = TrueOrFalse
  742. return True
  743. def SetNoDependInclude(self, TrueOrFalse):
  744. self.noDependInclude = TrueOrFalse
  745. return True
  746. def SetNoDependGenerate(self, TrueOrFalse):
  747. self.noDependGenerate = TrueOrFalse
  748. return True
  749. def SetKeepGoing(self, TrueOrFalse):
  750. self.keepGoing = TrueOrFalse
  751. return True
  752. def SetLogFileName(self, logfile):
  753. if logfile == "-":
  754. self.logFileName = None # stdout
  755. else:
  756. self.logFileName = generic_path.Path(logfile)
  757. return True
  758. def SetMakeEngine(self, makeEngine):
  759. self.makeEngine = makeEngine
  760. return True
  761. def AddMakeOption(self, makeOption):
  762. self.makeOptions.append(makeOption)
  763. return True
  764. def SetJobs(self, numberOfJobs):
  765. try:
  766. self.jobs = int(numberOfJobs)
  767. except ValueError:
  768. self.jobs = 0
  769. if self.jobs < 1:
  770. self.Warn("The number of jobs ({0}) must be a positive integer\n".format(numberOfJobs))
  771. self.jobs = 1
  772. return False
  773. return True
  774. def SetTries(self, numberOfTries):
  775. try:
  776. self.tries = int(numberOfTries)
  777. except ValueError:
  778. self.tries = 0
  779. if self.tries < 1:
  780. self.Warn("The number of tries ({0}) must be a positive integer\n".format(numberOfTries))
  781. self.tries = 1
  782. return False
  783. return True
  784. def SetToolCheck(self, type):
  785. type = type.lower()
  786. toolcheck_types= [ "forced", "on", "off" ]
  787. if type in toolcheck_types:
  788. self.toolcheck=type
  789. else:
  790. self.Warn("toolcheck option must be one of: {0}".format(toolcheck_types))
  791. return False
  792. return True
  793. def SetTiming(self, TrueOrFalse):
  794. self.Info("--timing switch no longer has any effect - build timing is now permanently on")
  795. return True
  796. def SetParallelParsing(self, type):
  797. type = type.lower()
  798. if type == "on":
  799. self.doParallelParsing = True
  800. elif type == "slave":
  801. self.isParallelParsingSlave = True
  802. elif type == "off":
  803. self.doParallelParsing = False
  804. else:
  805. self.Warn(" parallel parsing option must be either 'on' or 'off' (was {0})".format(type))
  806. return False
  807. return True
  808. def SetRsgCaseFolding(self, TrueOrFalse):
  809. self.doCaseFolding_rsg = TrueOrFalse
  810. return True
  811. def SetIncrementalParsing(self, type):
  812. if type == "on":
  813. self.incremental_parsing = True
  814. elif type == "off":
  815. self.incremental_parsing = False
  816. else:
  817. self.Warn(" incremental parsing option must be either 'on' or 'off' (was {0})".format(type))
  818. return False
  819. return True
  820. def SetNoMetadataDepend(self, TrueOrFalse):
  821. self.no_metadata_depend = TrueOrFalse
  822. return True
  823. def AddProject(self, projectName):
  824. self.projects.add(projectName.lower())
  825. return True
  826. def AddQuery(self, q):
  827. self.queries.append(q)
  828. self.mission = Raptor.M_QUERY
  829. return True
  830. def FilterList(self, value):
  831. self.filterList = value
  832. return True
  833. def IgnoreOsDetection(self, value):
  834. self.ignoreOsDetection = value
  835. return True
  836. def PrintVersion(self,dummy):
  837. global name
  838. print("{0} version {1}".format(name, raptor.version.fullversion()))
  839. self.mission = Raptor.M_VERSION
  840. return True
  841. # worker methods
  842. def Introduction(self):
  843. """Print a header of useful information about Raptor"""
  844. self.Info("{0}: version {1}".format(name, raptor.version.fullversion()))
  845. self.Info("{0} {1}".format(env, str(self.home)))
  846. self.Info("Set-up {0}".format(str(self.raptorXML)))
  847. self.Info("Command-line-arguments {0}".format(" ".join(self.args)))
  848. self.Info("Current working directory {0}".format(os.getcwd()))
  849. # the inherited environment
  850. for e, value in sorted( os.environ.items() ):
  851. self.Info("Environment {0}={1}".format(e, value.replace("]]>", "]]&gt;")))
  852. # and some general debug stuff
  853. self.Debug("Platform {0}".format("-".join(hostplatform)))
  854. self.Debug("Filesystem {0}".format(self.filesystem))
  855. self.Debug("Python {0}.{1}.{2}".format(*sys.version_info[:3]))
  856. self.Debug("Command-line-parser {0}".format(self.CLI))
  857. self.Debug("Currently selected filters: {0}".format(self.filterList))
  858. for e,value in self.override.items():
  859. self.Debug("Override {0} = {1}".format(e, value))
  860. for t in self.targets:
  861. self.Debug("Target {0}".format(t))
  862. def _check_and_set_build_targets(self):
  863. # resolve inter-argument dependencies.
  864. targets = [x.lower() for x in self.targets]
  865. # --what or --check implies the WHAT target and FilterWhat Filter
  866. if self.doWhat or self.doCheck:
  867. self.targets = ["WHAT"]
  868. self.filterList = "filterwhat"
  869. else:
  870. # 1. CLEAN/CLEANEXPORT/REALLYCLEAN needs the FilterClean filter.
  871. # 2. Targets that clean should not be combined with other targets.
  872. CL = "clean"
  873. CE = "cleanexport"
  874. RC = "reallyclean"
  875. EX = "export"
  876. is_clean = 0
  877. is_suspicious_clean = 0
  878. if CL in targets and CE in targets:
  879. is_clean = 1
  880. if len(targets) > 2:
  881. is_suspicious_clean = 1
  882. elif RC in targets or CL in targets or CE in targets:
  883. is_clean = 1
  884. if len(targets) > 1:
  885. is_suspicious_clean = 1
  886. if is_clean:
  887. self.filterList += ",filterclean"
  888. if is_suspicious_clean:
  889. self.Warn('CLEAN, CLEANEXPORT and a REALLYCLEAN should not be combined with other targets as the result is unpredictable.')
  890. else:
  891. # Copyfile implements the <copy> tag which is primarily useful with cluster builds.
  892. # It allows file copying to occur on the primary build host rather than on the cluster.
  893. # This is more efficient.
  894. if EX in targets: # sbs EXPORT implies sbs --export-only
  895. self.SetExportOnly(True)
  896. elif self.doExportOnly: # sbs --export-only implies sbs EXPORT
  897. self.targets.append(EX)
  898. else:
  899. self.filterList += ",filtercopyfile"
  900. def GetBuildUnitsToBuild(self, configNames):
  901. """Return a list of the configuration objects that correspond to the
  902. list of configuration names in the configNames parameter.
  903. GetBuildUnitsToBuild(["armv5", "winscw"])
  904. >>> [ config1, config2, ... , configN ]
  905. """
  906. if len(configNames) == 0:
  907. # use default config
  908. if len(self.defaultConfig) == 0:
  909. self.Warn("No default configuration name")
  910. else:
  911. configNames.append(self.defaultConfig)
  912. buildUnitsToBuild = raptor.data.GetBuildUnits(configNames, self.cache, self)
  913. for b in buildUnitsToBuild:
  914. self.Info("Buildable configuration '{0}'".format(b.name))
  915. if len(buildUnitsToBuild) == 0:
  916. self.Error("No build configurations given")
  917. return buildUnitsToBuild
  918. def CheckToolset(self, evaluator, configname):
  919. """Check the toolset for a particular config, allow other objects access
  920. to the toolset for this build (e.g. the make class)."""
  921. if self.toolset is None:
  922. if self.toolcheck == 'on':
  923. self.toolset = raptor.data.ToolSet(log=self)
  924. elif self.toolcheck == 'forced' :
  925. self.toolset = raptor.data.ToolSet(log=self, forced=True)
  926. else:
  927. return True
  928. return self.toolset.check(evaluator, configname)
  929. def CheckConfigs(self, configs):
  930. """ Tool checking for all the buildable configurations
  931. NB. We are allowed to use different tool versions for different
  932. configurations."""
  933. tools_ok = True
  934. tool_problems = []
  935. for b in configs:
  936. self.Debug("Tool check for {0}".format(b.name))
  937. config_ok = False #default
  938. try:
  939. evaluator = self.GetEvaluator(None, b, gathertools=True)
  940. config_ok = self.CheckToolset(evaluator, b.name)
  941. except raptor.data.UninitialisedVariableException as e:
  942. tool_problems.append(b.name)
  943. self.Error("{0} is a bad configuration: {1}".format(b.name,str(e)))
  944. tools_ok = tools_ok and config_ok
  945. if len(tool_problems) > 0:
  946. self.FatalError("Build stopped because the following requested configurations are incomplete or invalid: {0}".format(", ".join(tool_problems)))
  947. return tools_ok
  948. def GatherSysModelLayers(self, systemModel, systemDefinitionRequestedLayers):
  949. """Return a list of lists of components to be built.
  950. components = GatherSysModelLayers(self, configurations)
  951. >>> set("abc/group/bld.inf","def/group/bld.inf, ....")
  952. """
  953. layersToBuild = []
  954. if systemModel:
  955. # We either process all available layers in the system model, or a subset of
  956. # layers specified on the command line. In both cases, the processing is the same,
  957. # and can be subject to ordering if explicitly requested.
  958. systemModel.DumpInfo()
  959. if systemDefinitionRequestedLayers:
  960. layersToProcess = systemDefinitionRequestedLayers
  961. else:
  962. layersToProcess = systemModel.GetLayerNames()
  963. for layer in layersToProcess:
  964. systemModel.DumpLayerInfo(layer)
  965. if systemModel.IsLayerBuildable(layer):
  966. layersToBuild.append(Layer.from_system_model(layer,
  967. systemModel.GetLayerComponents(layer)))
  968. return layersToBuild
  969. # Add bld.inf or system definition xml to command line layers (depending on preference)
  970. def FindSysDefIn(self, aDir = None):
  971. # Find a system definition file
  972. if aDir is None:
  973. dir = generic_path.CurrentDir()
  974. else:
  975. dir = generic_path.Path(aDir)
  976. sysDef = dir.Append(self.systemDefinition)
  977. if not sysDef.isFile():
  978. return None
  979. return sysDef
  980. def FindComponentIn(self, aDir = None):
  981. # look for a bld.inf
  982. if aDir is None:
  983. dir = generic_path.CurrentDir()
  984. else:
  985. dir = generic_path.Path(aDir)
  986. bldInf = dir.Append(self.buildInformation)
  987. if bldInf.isFile():
  988. return BldinfComponent(bldInf)
  989. return None
  990. def GenerateGenericSpecs(self, configsToBuild):
  991. # if a Configuration has any config-wide interfaces
  992. # then add a Specification node to call each of them.
  993. configWide = {}
  994. genericSpecs = []
  995. for c in configsToBuild:
  996. evaluator = self.GetEvaluator(None, c)
  997. iface = evaluator.Get("INTERFACE.config")
  998. if iface:
  999. if iface in configWide:
  1000. # seen it already, so reuse the node
  1001. filter = configWide[iface]
  1002. filter.AddConfigCondition(c.name)
  1003. else:
  1004. # create a new node
  1005. filter = raptor.data.Filter(name = "config_wide")
  1006. filter.AddConfigCondition(c.name)
  1007. for i in iface.split():
  1008. spec = raptor.data.Specification(i)
  1009. spec.SetInterface(i)
  1010. filter.AddChildSpecification(spec)
  1011. # remember it, use it
  1012. configWide[iface] = filter
  1013. genericSpecs.append(filter)
  1014. return genericSpecs
  1015. def GetEvaluator(self, specification, configuration, gathertools=False):
  1016. """ this will perform some caching later """
  1017. return raptor.data.Evaluator(specification, configuration, gathertools=gathertools, cache = self.cache)
  1018. def Make(self, makefileset):
  1019. if not self.noBuild and makefileset is not None:
  1020. if self.maker.Make(makefileset, not self.ignore_zero_flmcall_makefiles):
  1021. self.Info("The make-engine exited successfully.")
  1022. return True
  1023. else:
  1024. self.Error("The make-engine exited with errors.")
  1025. return False
  1026. else:
  1027. self.Info("No build performed")
  1028. def Report(self):
  1029. if self.quiet:
  1030. return
  1031. # Allow filters access to the build duration or "run time"
  1032. self.raptor_params.runtime = int(0.5 + self.build_record.timing['runtime'])
  1033. self.Info("Run time {0} seconds".format(self.raptor_params.runtime))
  1034. def AssertBuildOK(self):
  1035. """Raise a BuildCannotProgressException if no further processing is required
  1036. """
  1037. if self.Skip():
  1038. raise BuildCannotProgressException("")
  1039. return True
  1040. def Skip(self):
  1041. """Indicate not to perform operation if:
  1042. fatalErrorState is set
  1043. an error code is set but we're not in keepgoing mode
  1044. """
  1045. return self.fatalErrorState or ((self.errorCode != 0) and (not self.keepGoing))
  1046. def InitPlugins(self):
  1047. """ Find all the raptor plugins and put them into a pluginbox. """
  1048. if not self.systemPlugins.isAbsolute():
  1049. self.systemPlugins = self.home.Append(self.systemPlugins)
  1050. self.pbox = PluginBox(str(self.systemPlugins))
  1051. # Try to load up plugins from the era before raptor-as-a-package
  1052. oldplugins = self.home.Append("python/plugins")
  1053. oldraptor_dir = str(self.home.Append("python"))
  1054. sys.path.insert(0,oldraptor_dir)
  1055. self.pbox.add_plugins(str(oldplugins))
  1056. sys.path[0:0] = []
  1057. # log file open/close
  1058. def OpenLog(self):
  1059. """Open a log file for the various I/O methods to write to."""
  1060. try:
  1061. self.InitPlugins()
  1062. self.raptor_params = BuildStats(self)
  1063. # Open the requested plugins using the pluginbox
  1064. self.out.open(self.raptor_params, self.filterList, self.pbox)
  1065. # log header
  1066. self.out.write("<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\n")
  1067. namespace = "http://symbian.com/xml/build/log"
  1068. progress_namespace = "http://symbian.com/xml/build/log/progress"
  1069. schema = "http://symbian.com/xml/build/log/1_0.xsd"
  1070. self.out.write("<buildlog sbs_version=\"{0}\" xmlns=\"{1}\" xmlns:progress=\"{2}\""
  1071. " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"{3} {4}\">\n".format(
  1072. raptor.version.fullversion(), namespace, progress_namespace, namespace, schema))
  1073. self.logOpen = True
  1074. except Exception as e:
  1075. self.out = sys.stdout # make sure that we can actually get errors out.
  1076. self.logOpen = False
  1077. self.FatalError("Unable to open the output logs: {0}".format(str(e)))
  1078. def CloseLog(self):
  1079. if self.logOpen:
  1080. self.out.summary()
  1081. self.out.write("</buildlog>\n")
  1082. if not self.out.close():
  1083. self.errorCode = 1
  1084. def Cleanup(self):
  1085. # ensure that the toolset cache is flushed.
  1086. if self.toolset is not None:
  1087. self.toolset.write()
  1088. # I/O methods
  1089. @staticmethod
  1090. def attributeString(dictionary):
  1091. """Turn a dictionary into a string of XML attributes"""
  1092. return "".join([" {0}='{1}'".format(a,v) for a,v in dictionary.items()])
  1093. def Info(self, msg, **attributes):
  1094. """Send an information message to the configured channel
  1095. (XML control characters will be escaped)
  1096. """
  1097. self.out.write("<info{0}>{1}</info>\n".format(self.attributeString(attributes), escape(msg)))
  1098. def InfoDiscovery(self, object_type, count):
  1099. if self.timing:
  1100. try:
  1101. self.out.write(timing.Timing.discovery_string(object_type = object_type,
  1102. count = count))
  1103. except Exception as exception:
  1104. self.Error(exception.Text, function = "InfoDiscoveryTime")
  1105. def InfoStartTime(self, object_type, task, key):
  1106. if self.timing:
  1107. try:
  1108. self.out.write(timing.Timing.start_string(object_type = object_type,
  1109. task = task, key = key))
  1110. except Exception as exception:
  1111. self.Error(exception.message, function = "InfoStartTime")
  1112. def InfoEndTime(self, object_type, task, key):
  1113. if self.timing:
  1114. try:
  1115. self.out.write(timing.Timing.end_string(object_type = object_type,
  1116. task = task, key = key))
  1117. except Exception as exception:
  1118. self.Error(exception.Text, function = "InfoEndTime")
  1119. def Debug(self, msg, **attributes):
  1120. "Send a debugging message to the configured channel"
  1121. # the debug text is out of our control so wrap it in a CDATA
  1122. # in case it contains characters special to XML... like <>
  1123. if self.debugOutput:
  1124. self.out.write("<debug{0}><![CDATA[\n{1}\n]]></debug>\n".format(self.attributeString(attributes),escape(msg)))
  1125. def Warn(self, msg, **attributes):
  1126. """Send a warning message to the configured channel
  1127. (XML control characters will be escaped)
  1128. """
  1129. self.out.write("<warning{0}>{1}</warning>\n".format(self.attributeString(attributes), escape(msg)))
  1130. def FatalError(self, msg, **attributes):
  1131. """Send an error message to the configured channel. This implies such a serious
  1132. error that the entire build must be shut down asap whilst still finishing off
  1133. correctly whatever housekeeping is possible e.g. producing error reports.
  1134. Remains quiet if the raptor object is already in a fatal state since there
  1135. further errors are probably triggered by the first.
  1136. """
  1137. if not self.fatalErrorState:
  1138. self.out.write("<error{0}>{1}</error>\n".format(self.attributeString(attributes), escape(msg)))
  1139. self.errorCode = 1
  1140. self.fatalErrorState = True
  1141. def Error(self, msg, **attributes):
  1142. """Send an error message to the configured channel
  1143. (XML control characters will be escaped)
  1144. """
  1145. self.out.write("<error{0}>{1}</error>\n".format(self.attributeString(attributes), escape(msg)))
  1146. self.errorCode = 1
  1147. def PrintXML(self, format, *extras):
  1148. "Print to configured channel (no newline is added) (assumes valid xml)"
  1149. if format:
  1150. self.out.write(format % extras)
  1151. def GetLayersFromCLI(self):
  1152. """Returns the list of layers as specified by the
  1153. commandline interface to Raptor e.g. parameters
  1154. or the current directory"""
  1155. layers=[]
  1156. # Look for bld.infs or sysdefs in the current dir if none were specified
  1157. if self.systemDefinitionFile == None and len(self.commandline_layer) == 0:
  1158. cwd = os.getcwd()
  1159. if not self.preferBuildInfoToSystemDefinition:
  1160. self.systemDefinitionFile = self.FindSysDefIn(cwd)
  1161. if self.systemDefinitionFile == None:
  1162. aComponent = self.FindComponentIn(cwd)
  1163. if aComponent is not None:
  1164. layers.append(Layer('default',[aComponent]))
  1165. else:
  1166. aComponent = self.FindComponentIn(cwd)
  1167. if aComponent is None:
  1168. self.systemDefinitionFile = self.FindSysDefIn(cwd)
  1169. else:
  1170. layers.append(Layer('default',[aComponent]))
  1171. if len(layers) <= 0 and self.systemDefinitionFile == None:
  1172. self.Warn("No default bld.inf or system definition file found in current directory ({0})".format(cwd))
  1173. # If we now have a System Definition to parse then get the layers of components
  1174. if self.systemDefinitionFile != None:
  1175. systemModel = SystemModel(self, self.systemDefinitionFile, self.systemDefinitionBase)
  1176. layers = self.GatherSysModelLayers(systemModel, self.systemDefinitionRequestedLayers)
  1177. # Now get components specified on a commandline - build them after any
  1178. # layers in the system definition.
  1179. if len(self.commandline_layer) > 0:
  1180. layers.append(self.commandline_layer)
  1181. # If we aren't building components in order then flatten down
  1182. # the groups
  1183. if not self.systemDefinitionOrderLayers:
  1184. # Flatten the layers into one group of components if
  1185. # we are not required to build them in order.
  1186. newcg = Layer("all")
  1187. for cg in layers:
  1188. for c in cg:
  1189. newcg.add(c)
  1190. layers = [newcg]
  1191. return layers
  1192. def Query(self):
  1193. "process command-line queries."
  1194. if self.mission != Raptor.M_QUERY:
  1195. return 0
  1196. # establish an object cache based on the current settings
  1197. self._load_cache()
  1198. # our "self" is a valid object for initialising an API Context
  1199. import raptor.api
  1200. context = raptor.api.Context(self)
  1201. # intialise the plugs in case they are needed for the query
  1202. self.InitPlugins()
  1203. print("<sbs version='{0}'>".format(raptor.version.numericversion()))
  1204. for q in self.queries:
  1205. try:
  1206. print(context.stringquery(q))
  1207. except Exception as e:
  1208. self.Error("exception '{0}' with query '{1}'".format(str(e), q))
  1209. print("</sbs>")
  1210. return self.errorCode
  1211. def Build(self):
  1212. if self.mission != Raptor.M_BUILD: # help or version requested instead.
  1213. return 0
  1214. # open the log file
  1215. self.OpenLog()
  1216. try:
  1217. self.AssertBuildOK()
  1218. # show the command and platform info
  1219. self.Introduction()
  1220. # load the cache of configurations etc
  1221. self._load_cache()
  1222. # establish an object cache
  1223. self.AssertBuildOK()
  1224. # find out what configurations to build
  1225. buildUnitsToBuild = self.GetBuildUnitsToBuild(self.configNames)
  1226. if len(buildUnitsToBuild) == 0:
  1227. raise BuildCannotProgressException("No configurations to build.")
  1228. self.buildUnitsToBuild = buildUnitsToBuild
  1229. self.AssertBuildOK()
  1230. ###### insert the start time into the Makefile name
  1231. makefile = self.topMakefile.Absolute()
  1232. makefile.path = makefile.path.replace("%TIME", self.timestring)
  1233. self.topMakefile = makefile
  1234. ######
  1235. # Create a build record as non-parallel parsed builds can take advantage of incremental
  1236. # makefile generation with some level of safety:
  1237. tm_dir = str(makefile.Dir())
  1238. tm_file = str(makefile.File())
  1239. environment = ";".join(["{0}={1}".format(k,os.environ[k]) for k in BuildRecord.sensed_environment_variables])
  1240. must_create_makefiles = True
  1241. if self.incremental_parsing:
  1242. self.build_record = BuildRecord.from_old(adir = str(self.topMakefile.Dir()), commandline=" ".join(self.args), environment = environment, topmakefile = str(makefile), logfilename = str(self.logFileName))
  1243. must_create_makefiles = not self.build_record.reused
  1244. else:
  1245. self.build_record = BuildRecord(commandline=" ".join(self.args), environment = environment, topmakefilename = str(makefile), logfilename=str(self.logFileName), makefilesets=[])
  1246. if must_create_makefiles:
  1247. if self.incremental_parsing:
  1248. self.Info("incremental makefile generation: cannot reuse any pre-existing makefiles")
  1249. if len(self.build_record.new_metadata) > 0:
  1250. self.Info("incremental makefile generation: metadata changed: {0}".format(" ".join(self.build_record.new_metadata)))
  1251. else:
  1252. self.Info("incremental makefile generation: no metadata was newer than the makefiles")
  1253. # find out what components to build, and in what way
  1254. layers = []
  1255. self.AssertBuildOK()
  1256. if len(buildUnitsToBuild) >= 0:
  1257. layers = self.GetLayersFromCLI()
  1258. componentCount = sum([len(cg) for cg in layers])
  1259. if not componentCount > 0:
  1260. raise BuildCannotProgressException("No components to build.")
  1261. # check the configurations (tools versions)
  1262. self.AssertBuildOK()
  1263. if self.toolcheck != 'off':
  1264. self.CheckConfigs(buildUnitsToBuild)
  1265. else:
  1266. self.Info("Not Checking Tool Versions")
  1267. self.AssertBuildOK()
  1268. else:
  1269. # Don't use the makefile name specified on the commmandline - use the one from the build record.
  1270. self.Info("incremental makefile generation: pre-existing makefiles will be reused: {0}".format(self.build_record.topmakefilename))
  1271. # Setup a make engine.
  1272. if not self.maker:
  1273. try:
  1274. self.maker = raptor.make.MakeEngine(self, self.makeEngine)
  1275. except raptor.make.BadMakeEngineException as e:
  1276. self.Error("Unable to use make engine: {0} ".format(str(e)))
  1277. self.AssertBuildOK()
  1278. # if self.doParallelParsing and not (len(layers) == 1 and len(layers[0]) == 1):
  1279. # rebuilding and parallel parsing are not compatible at the moment. There is
  1280. # a validation check for that earlier.
  1281. if self.doParallelParsing:
  1282. # Create a Makefile to parse components in parallel and build them
  1283. self.build_record = BuildRecord(commandline=" ".join(self.args), environment=environment, topmakefilename=str(makefile), logfilename=str(self.logFileName), makefilesets=[])
  1284. for l in layers:
  1285. l.meta_realise(self)
  1286. elif must_create_makefiles:
  1287. # Parse components serially, creating one set of makefiles
  1288. # create non-component specs
  1289. self.generic_specs = self.GenerateGenericSpecs(buildUnitsToBuild)
  1290. self.AssertBuildOK()
  1291. self.build_record = BuildRecord(commandline=" ".join(self.args), environment=environment, topmakefilename=str(makefile), logfilename=str(self.logFileName), makefilesets=[])
  1292. self.build_record.build_start(self.starttime)
  1293. for l in layers:
  1294. # create specs for a specific group of components
  1295. try:
  1296. l.realise(self)
  1297. except raptor.make.CannotWriteMakefileException as e:
  1298. pass # make will report these errors itself
  1299. # Record the end of the build
  1300. self.build_record.build_end()
  1301. try:
  1302. self.build_record.to_file()
  1303. except Exception as e:
  1304. self.Info("Couldn't write build record file: {0}".format(str(e)))
  1305. else:
  1306. self.build_record.build_start(self.starttime)
  1307. # Reusing old makefiles
  1308. for makefileset in self.build_record.makefilesets:
  1309. self.InfoStartTime(object_type = "makefileset", task = "build",
  1310. key = makefileset.metadepsfilename)
  1311. result = self.Make(makefileset)
  1312. self.InfoEndTime(object_type = "makefileset", task = "build",
  1313. key = makefileset.metadepsfilename)
  1314. self.build_record.build_end()
  1315. except BuildCannotProgressException as b:
  1316. if str(b) != "":
  1317. self.Info(str(b))
  1318. # final report
  1319. if not self.fatalErrorState:
  1320. self.Report()
  1321. self.Cleanup()
  1322. # close the log file
  1323. self.CloseLog()
  1324. return self.errorCode
  1325. @classmethod
  1326. def CreateCommandlineAnalysis(cls, argv):
  1327. """ Perform an analysis run where a build is not performed.
  1328. Don't parse command line targets - they don't make any sense if you're not doing a build"""
  1329. build = Raptor(commandline=argv, dotargets = False)
  1330. return build
  1331. # Class for passing constricted parameters to filters
  1332. class BuildStats(object):
  1333. def __init__(self, raptor_instance):
  1334. self.incoming_epocroot = incoming_epocroot
  1335. self.epocroot = epocroot
  1336. self.logFileName = raptor_instance.logFileName
  1337. self.configPath = raptor_instance.configPath
  1338. self.home = raptor_instance.home
  1339. self.quiet = raptor_instance.quiet
  1340. self.doCheck = raptor_instance.doCheck
  1341. self.doWhat = raptor_instance.doWhat
  1342. self.platform = hostplatform
  1343. self.skipAll = raptor_instance.fatalErrorState
  1344. self.timestring = raptor_instance.timestring
  1345. self.targets = raptor_instance.targets
  1346. self.runtime = 0
  1347. self.name = raptor.version.name
  1348. self.topMakefile = raptor_instance.topMakefile
  1349. # raptor module functions
  1350. def Main(argv):
  1351. """The main entry point for Raptor.
  1352. argv is a list of command-line parameters,
  1353. NOT including the name of the calling script.
  1354. The return value is zero for success and non-zero for failure."""
  1355. DisplayBanner()
  1356. try:
  1357. # object which represents a build
  1358. b = Raptor(commandline=argv)
  1359. if b.mission == Raptor.M_QUERY:
  1360. return b.Query()
  1361. return b.Build()
  1362. except BuildCannotProgressException as e:
  1363. t = str(e)
  1364. if t != "":
  1365. sys.stderr.write("sbs error: {0:s}\n".format(t))
  1366. return 1
  1367. def DisplayBanner():
  1368. """Stuff that needs printing out for every command."""
  1369. pass
  1370. # end of the raptor module