raptor_make.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932
  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_make module
  16. # This module contains the classes that write and call Makefile wrappers.
  17. #
  18. import hashlib
  19. import os
  20. import pickle
  21. import random
  22. import raptor
  23. import raptor_timing
  24. import raptor_utilities
  25. import raptor_version
  26. import raptor_data
  27. import re
  28. import subprocess
  29. import time
  30. from raptor_makefile import *
  31. import traceback
  32. import sys
  33. from xml.sax.saxutils import escape
  34. from xml.sax.saxutils import unescape
  35. class BadMakeEngineException(Exception):
  36. pass
  37. class CannotWriteMakefileException(Exception):
  38. pass
  39. def string_following(prefix, str):
  40. """If str starts with prefix then return the rest of str, otherwise None"""
  41. if str.startswith(prefix):
  42. return str[len(prefix):]
  43. else:
  44. return None
  45. def XMLEscapeLog(stream):
  46. """ A generator that reads a raptor log from a stream and performs an XML escape
  47. on all text between tags, which is usually make output that could contain
  48. illegal characters that upset XML-based log parsers.
  49. This function yields "xml-safe" output line by line.
  50. """
  51. inRecipe = False
  52. for line in stream:
  53. if line.startswith("<recipe"):
  54. inRecipe = True
  55. elif line.startswith("</recipe"):
  56. inRecipe = False
  57. # unless we are inside a "recipe", any line not starting
  58. # with "<" is free text that must be escaped.
  59. if inRecipe or line.startswith("<"):
  60. yield line
  61. else:
  62. yield escape(line)
  63. def AnnoFileParseOutput(annofile):
  64. """ A generator that extracts log output from an emake annotation file,
  65. perform an XML-unescape on it and "yields" it line by line. """
  66. if isinstance(annofile,str):
  67. af = open(annofile, "r")
  68. else:
  69. af = annofile
  70. inOutput = False
  71. buildid = ""
  72. duration = "unknown"
  73. availability = "unknown"
  74. for line in af:
  75. line = line.rstrip("\n\r")
  76. if not inOutput:
  77. o = string_following("<output>", line)
  78. if not o:
  79. o = string_following('<output src="prog">', line)
  80. if o:
  81. inOutput = True
  82. yield unescape(o)+'\n'
  83. continue
  84. o = string_following('<build id="',line)
  85. if o:
  86. buildid = o[:o.find('"')]
  87. yield "Starting build: "+buildid+"\n"
  88. continue
  89. o = string_following('<metric name="duration">', line)
  90. if o:
  91. total_secs = int(float(o[:o.find('<')]))
  92. if total_secs != 0:
  93. duration = "{mins:.0f}:{secs}".format(mins = total_secs/60, secs = total_secs % 60)
  94. else:
  95. duration = "0:0"
  96. continue
  97. o = string_following('<metric name="clusterAvailability">', line)
  98. if o:
  99. availability = o[:o.find('<')]
  100. continue
  101. else:
  102. end_output = line.find("</output>")
  103. if end_output != -1:
  104. line = line[:end_output]
  105. inOutput = False
  106. if line != "":
  107. yield unescape(line)+'\n'
  108. yield "Finished build: {0} Duration: {1} (m:s) Cluster availability: {2}%\n".format(buildid, duration, availability)
  109. af.close()
  110. def run_make(make_process):
  111. """ A function to run make command. This is in a standalone function so
  112. Raptor could easily use multiprocess to run multiple make programs
  113. """
  114. makeenv = os.environ.copy()
  115. makeenv['TALON_RECIPEATTRIBUTES'] = make_process.talon_recipeattributes
  116. makeenv['TALON_SHELL'] = make_process.talon_shell
  117. makeenv['TALON_BUILDID'] = make_process.talon_buildid
  118. makeenv['TALON_TIMEOUT'] = make_process.talon_timeout
  119. if make_process.filesystem == "unix":
  120. p = subprocess.Popen(
  121. args = [make_process.command],
  122. bufsize = 65535,
  123. stdout = subprocess.PIPE,
  124. stderr = subprocess.STDOUT,
  125. close_fds = True,
  126. shell = True,
  127. env = makeenv)
  128. else:
  129. p = subprocess.Popen(
  130. args = [make_process.shell, '-c', make_process.command],
  131. bufsize = 65535,
  132. stdout = subprocess.PIPE,
  133. stderr = subprocess.STDOUT,
  134. shell = False,
  135. universal_newlines = True,
  136. env = makeenv)
  137. # When using an [emake] annotation file all the output from make is redirected
  138. # to .stdout and .stderr files. Those files are read when the make process exits
  139. # and only then is the output passed on to the filters. We still read from the
  140. # subprocess pipe here in case there is any unexpected output from the shell
  141. # that might block the subprocess.
  142. #
  143. # Without an annotation file, make errors are still redirected to a .stderr
  144. # file since we don't really care about seeing them immediately. But standard
  145. # output is not redirected, it is read from the subprocess pipe and passed
  146. # to the filters immediately so that users can see the build progressing.
  147. stream = p.stdout
  148. for line in XMLEscapeLog(stream):
  149. if not make_process.copyLogFromAnnoFile:
  150. make_process.logstream.write(line)
  151. make_process.returncode = p.wait()
  152. make_process.hasrun = True
  153. return make_process.returncode
  154. def log_output(make_process):
  155. """ A function to send the output from a previously run make command to the
  156. log filters. This is needed for safely injecting the stderr output by
  157. escaping it and making sure it isn't interleaved with stdout. Also, for
  158. emake builds this is where we copy the stdout from the annotation file.
  159. """
  160. if not make_process.hasrun:
  161. return
  162. if make_process.copyLogFromAnnoFile:
  163. annofilename = make_process.annoFileName
  164. make_process.logstream.write("<info>copylogfromannofile: Copying log from annotation file {0} to work around a potential problem with the console output</info>\n".format(annofilename))
  165. try:
  166. for l in XMLEscapeLog(AnnoFileParseOutput(annofilename)):
  167. make_process.logstream.write(l)
  168. except Exception,e:
  169. sys.stderr.write("Couldn't complete stdout output from annofile {0} for {1} - '{2}'\n".format(annofilename, make_process.command, str(e)))
  170. # Take all the stderr output that went into the .stderr file
  171. # and put it back into the log, but safely so it can't mess up
  172. # xml parsers.
  173. try:
  174. errfile = open(make_process.stderrfilename, "r")
  175. for line in errfile:
  176. make_process.logstream.write(escape(line))
  177. errfile.close()
  178. except Exception,e:
  179. sys.stderr.write("Couldn't complete stderr output for {0} - '{1}'\n".format(make_process.command, str(e)))
  180. class MakeProcess(object):
  181. """ A process of make program """
  182. def __init__(self, command):
  183. self.command = command
  184. self.hasrun = False # Has this process been executed
  185. self.returncode = 255 # default to error
  186. # raptor_make module classes
  187. class MakeEngine(object):
  188. def __init__(self, Raptor, engine="make_engine"):
  189. self.raptor = Raptor
  190. self.valid = True
  191. self.descrambler = None
  192. self.descrambler_started = False
  193. self.global_make_variables = {}
  194. # look for an alias first as this gives end-users a chance to modify
  195. # the shipped variant rather than completely replacing it.
  196. if engine in Raptor.cache.aliases:
  197. avar = Raptor.cache.FindNamedAlias(engine)
  198. elif engine in Raptor.cache.variants:
  199. avar = Raptor.cache.FindNamedVariant(engine)
  200. else:
  201. raise BadMakeEngineException("'{0}' does not appear to be a make engine - no settings found for it".format(engine))
  202. if not avar.isDerivedFrom("make_engine", Raptor.cache):
  203. raise BadMakeEngineException("'{0}' is not a build engine (it's a variant but it does not extend 'make_engine')".format(engine))
  204. # find the variant and extract the values
  205. try:
  206. units = avar.GenerateBuildUnits(Raptor.cache)
  207. evaluator = Raptor.GetEvaluator( None, units[0] , gathertools=True)
  208. # shell
  209. self.shellpath = evaluator.Get("DEFAULT_SHELL")
  210. self.talonshell = str(evaluator.Get("TALON_SHELL"))
  211. self.talontimeout = str(evaluator.Get("TALON_TIMEOUT"))
  212. self.talonretries = str(evaluator.Get("TALON_RETRIES"))
  213. # work around for RVCT 2.2 failed compiles
  214. delete_on_failed_compile_s = evaluator.Get("DELETE_ON_FAILED_COMPILE")
  215. self.delete_on_failed_compile = ""
  216. if delete_on_failed_compile_s is not None and delete_on_failed_compile_s != "":
  217. self.delete_on_failed_compile = "1"
  218. # commands
  219. self.initCommand = evaluator.Get("initialise")
  220. self.buildCommand = evaluator.Get("build")
  221. self.shutdownCommand = evaluator.Get("shutdown")
  222. # options
  223. self.makefileOption = evaluator.Get("makefile")
  224. self.keepGoingOption = evaluator.Get("keep_going")
  225. self.jobsOption = evaluator.Get("jobs")
  226. self.defaultMakeOptions = evaluator.Get("defaultoptions")
  227. # Logging
  228. # copylogfromannofile means, for emake, that we should ignore
  229. # emake's console output and instead extract output from its annotation
  230. # file. This is a workaround for a problem where some emake
  231. # console output is lost. The annotation file has a copy of this
  232. # output in the "parse" job and it turns out to be uncorrupted.
  233. self.copyLogFromAnnoFile = (evaluator.Get("copylogfromannofile") == "true")
  234. self.emakeCm = (len([opt for opt in self.raptor.makeOptions if opt.startswith("--emake-cm")]) > 0)
  235. self.annoFileName = None # store the anno file name
  236. if self.copyLogFromAnnoFile:
  237. try:
  238. self.annoFileName = string_following("--emake-annofile=", [opt for opt in self.raptor.makeOptions if opt.startswith("--emake-annofile")][0])
  239. self.raptor.Info("annofile: " + self.annoFileName)
  240. except IndexError, bad_index:
  241. cannot_use_anno_msg = "Cannot copy log from annotation file as no annotation filename was specified via the option --mo=--emake-annofile=<filename>"
  242. if self.emakeCm:
  243. self.raptor.Error(cannot_use_anno_msg) # Only an error if requested use of cm
  244. else:
  245. self.raptor.Info(cannot_use_anno_msg)
  246. self.copyLogFromAnnoFile = False
  247. # buffering
  248. self.scrambled = (evaluator.Get("scrambled") == "true")
  249. # check tool versions
  250. Raptor.CheckToolset(evaluator, avar.name)
  251. # default targets (can vary per-invocation)
  252. self.defaultTargets = Raptor.defaultTargets
  253. # work out how to split up makefiles
  254. try:
  255. selectorNames = [ x.strip() for x in evaluator.Get("selectors").split(',') if x.strip() != "" ]
  256. self.selectors = []
  257. if len(selectorNames) > 0:
  258. for name in selectorNames:
  259. pattern = evaluator.Get(name.strip() + ".selector.iface")
  260. target = evaluator.Get(name.strip() + ".selector.target")
  261. ignoretargets = evaluator.Get(name.strip() + ".selector.ignoretargets")
  262. self.selectors.append(MakefileSelector(name,pattern,target,ignoretargets))
  263. except KeyError:
  264. Raptor.Error("{0}.selector.iface, {0}.selector.target not found in make engine configuration".format(name))
  265. self.selectors = []
  266. except KeyError:
  267. self.valid = False
  268. raise BadMakeEngineException("Bad '{0}' configuration found.".format(engine))
  269. # there must at least be a build command...
  270. if not self.buildCommand:
  271. self.valid = False
  272. raise BadMakeEngineException("No build command for '{0}'".format(engine))
  273. def Write(self, toplevel, specs, configs):
  274. """Generate a set of makefiles, or one big Makefile."""
  275. if not self.valid:
  276. return None
  277. talon_settings="""
  278. TALON_SHELL:={0}
  279. TALON_TIMEOUT:={1}
  280. TALON_RECIPEATTRIBUTES:=\
  281. name='$$RECIPE'\
  282. target='$$TARGET'\
  283. host='$$HOSTNAME'\
  284. layer='$$COMPONENT_LAYER'\
  285. component='$$COMPONENT_NAME'\
  286. bldinf='$$COMPONENT_META' mmp='$$PROJECT_META'\
  287. config='$$SBS_CONFIGURATION' platform='$$PLATFORM'\
  288. phase='$$MAKEFILE_GROUP' source='$$SOURCE'
  289. export TALON_RECIPEATTRIBUTES TALON_SHELL TALON_TIMEOUT
  290. USE_TALON:={2}
  291. """.format(self.talonshell, self.talontimeout, "1")
  292. timing_start = "$(info " + \
  293. raptor_timing.Timing.custom_string(tag = "start",
  294. object_type = "makefile", task = "parse",
  295. key = "$(THIS_FILENAME)",
  296. time="$(shell date +%s.%N)").rstrip("\n") + ")"
  297. timing_end = "$(info " + \
  298. raptor_timing.Timing.custom_string(tag = "end",
  299. object_type = "makefile", task = "parse",
  300. key = "$(THIS_FILENAME)",
  301. time="$(shell date +%s.%N)").rstrip("\n") + ")"
  302. # Debugging on or off for make:
  303. # We need it at the very top level so that it can be used
  304. # to determine what extra info to put in recipe tags
  305. try:
  306. flmdebug_setting = os.environ["FLMDEBUG"]
  307. except KeyError:
  308. flmdebug_setting = ""
  309. # global variables are set at the top of each makefile
  310. self.global_make_variables['HOSTPLATFORM'] = " ".join(raptor.hostplatform)
  311. self.global_make_variables['HOSTPLATFORM_DIR'] = raptor.hostplatform_dir
  312. self.global_make_variables['HOSTPLATFORM32_DIR'] = raptor.hostplatform32_dir
  313. self.global_make_variables['OSTYPE'] = self.raptor.filesystem
  314. self.global_make_variables['FLMHOME'] = str(self.raptor.systemFLM)
  315. self.global_make_variables['SHELL'] = self.shellpath
  316. self.global_make_variables['DELETE_ON_FAILED_COMPILE'] = raptor_utilities.make_bool_string(self.delete_on_failed_compile)
  317. self.global_make_variables['NO_DEPEND_GENERATE'] = raptor_utilities.make_bool_string(self.raptor.noDependGenerate)
  318. self.makefile_prologue = """
  319. # generated by {0} {1}
  320. HOSTPLATFORM:={2}
  321. HOSTPLATFORM_DIR:={3}
  322. HOSTPLATFORM32_DIR:={4}
  323. OSTYPE:={5}
  324. FLMHOME:={6}
  325. SHELL:={7}
  326. THIS_FILENAME:=$(firstword $(MAKEFILE_LIST))
  327. DELETE_ON_FAILED_COMPILE:={8}
  328. {9}
  329. FLMDEBUG:={10}
  330. include {11}
  331. """ .format( raptor.name, raptor_version.fullversion(),
  332. self.global_make_variables['HOSTPLATFORM'],
  333. self.global_make_variables['HOSTPLATFORM_DIR'],
  334. self.global_make_variables['HOSTPLATFORM32_DIR'],
  335. self.global_make_variables['OSTYPE'],
  336. self.global_make_variables['FLMHOME'],
  337. self.global_make_variables['SHELL'],
  338. self.global_make_variables['DELETE_ON_FAILED_COMPILE'],
  339. talon_settings,
  340. flmdebug_setting,
  341. self.raptor.systemFLM.Append('globals.mk') )
  342. # Unless dependency processing has been eschewed via the CLI, use a .DEFAULT target to
  343. # trap missing dependencies (ignoring user config files that we know are usually absent)
  344. if not (self.raptor.noDependGenerate or self.raptor.noDependInclude):
  345. self.makefile_prologue += """
  346. $(FLMHOME)/user/final.mk:
  347. $(FLMHOME)/user/default.flm:
  348. $(FLMHOME)/user/globals.mk:
  349. .DEFAULT::
  350. @echo "<warning>Missing dependency detected: $@</warning>"
  351. """
  352. # Only output timings if requested on CLI
  353. if self.raptor.timing:
  354. self.makefile_prologue += "\n# Print Start-time of Makefile parsing\n" \
  355. + timing_start + "\n\n"
  356. self.makefile_epilogue = "\n\n# Print End-time of Makefile parsing\n" \
  357. + timing_end + "\n"
  358. else:
  359. self.makefile_epilogue = ""
  360. self.makefile_epilogue += """
  361. include {0}
  362. """.format(self.raptor.systemFLM.Append('final.mk') )
  363. self.raptor.Debug("Writing Makefile '{0}'".format(str(toplevel)))
  364. self.toplevel = toplevel
  365. # create the top-level makefiles
  366. makefileset = None
  367. try:
  368. makefileset = MakefileSet(directory = str(toplevel.Dir()),
  369. selectors = self.selectors,
  370. filenamebase = str(toplevel.File()),
  371. prologue = self.makefile_prologue,
  372. epilogue = self.makefile_epilogue,
  373. defaulttargets = self.defaultTargets)
  374. # are we pruning duplicates?
  375. self.prune = self.raptor.pruneDuplicateMakefiles
  376. self.hashes = set()
  377. # are we writing one Makefile or lots?
  378. self.many = not self.raptor.writeSingleMakefile
  379. # add a makefile for each spec under each config
  380. config_makefileset = makefileset
  381. for c in configs:
  382. if self.many:
  383. config_makefileset = makefileset.createChild(c.name)
  384. # make sure the config_wide spec item is put out first so that it
  385. # can affect everything.
  386. ordered_specs=[]
  387. config_wide_spec = None
  388. for s in specs:
  389. if s.name == "config_wide":
  390. config_wide_spec = s
  391. else:
  392. ordered_specs.append(s)
  393. if config_wide_spec is not None:
  394. config_wide_spec.Configure(c, cache = self.raptor.cache)
  395. self.WriteConfiguredSpec(config_makefileset, config_wide_spec, c, True)
  396. for s in ordered_specs:
  397. s.Configure(c, cache = self.raptor.cache)
  398. self.WriteConfiguredSpec(config_makefileset, s, c, False)
  399. makefileset.close()
  400. except Exception,e:
  401. tb = traceback.format_exc()
  402. if not self.raptor.debugOutput:
  403. tb=""
  404. self.raptor.Error("Failed to write makefile '{0}': {1} : {2}".format(str(toplevel),str(e),tb))
  405. raise CannotWriteMakefileException(str(e))
  406. return makefileset
  407. def WriteConfiguredSpec(self, parentMakefileSet, spec, config, useAllInterfaces):
  408. # ignore this spec if it is empty
  409. hasInterface = spec.HasInterface()
  410. childSpecs = spec.GetChildSpecs()
  411. if not hasInterface and not childSpecs:
  412. return
  413. parameters = []
  414. dupe = True
  415. pickled = False
  416. iface = None
  417. guard = None
  418. if hasInterface:
  419. # find the Interface (it may be a ref)
  420. try:
  421. iface = spec.GetInterface(self.raptor.cache)
  422. except raptor_data.MissingInterfaceError, e:
  423. self.raptor.Error("No interface for '{0}'".format(spec.name))
  424. return
  425. if iface.abstract:
  426. self.raptor.Error("Abstract interface '{0}' for '{1}'".format(
  427. iface.name, spec.name))
  428. return
  429. # we need to guard the FLM call with a hash based on all the
  430. # parameter values so that duplicate calls cannot be made.
  431. # So we need to find all the values before we can write
  432. # anything out.
  433. md5hash = hashlib.md5()
  434. md5hash.update(iface.name)
  435. # we need an Evaluator to get parameter values for this
  436. # Specification in the context of this Configuration
  437. evaluator = self.raptor.GetEvaluator(spec, config)
  438. def addparam(k, value, default):
  439. if value == None:
  440. if p.default != None:
  441. value = p.default
  442. else:
  443. self.raptor.Error("{0} undefined for '{1}'".format(
  444. k, spec.name))
  445. value = ""
  446. parameters.append((k, value))
  447. md5hash.update(value)
  448. # parameters required by the interface
  449. for p in iface.GetParams(self.raptor.cache):
  450. val = evaluator.Resolve(p.name)
  451. addparam(p.name,val,p.default)
  452. # Use Patterns to fetch a group of parameters
  453. for g in iface.GetParamGroups(self.raptor.cache):
  454. for k,v in evaluator.ResolveMatching(g.patternre):
  455. addparam(k,v,g.default)
  456. hash = md5hash.hexdigest()
  457. dupe = hash in self.hashes
  458. self.hashes.add(hash)
  459. # pickled interfaces need the bld.inf output directory even
  460. # if it is not an FLM parameter (and it normally isn't)
  461. pickled = iface.isPickled(self.raptor.cache)
  462. if pickled:
  463. bldinfop = evaluator.Resolve("BLDINF_OUTPUTPATH")
  464. if not bldinfop:
  465. self.raptor.Error("BLDINF_OUTPUTPATH is required in {0} for pickled interfaces".format(config.name))
  466. # we only create a Makefile if we have a new FLM call to contribute,
  467. # OR we are not pruning duplicates (guarding instead)
  468. # OR we have some child specs that need something to include them.
  469. if dupe and self.prune and not childSpecs:
  470. return
  471. makefileset = parentMakefileSet
  472. # Create a new layer of makefiles?
  473. if self.many:
  474. makefileset = makefileset.createChild(spec.name)
  475. if not (self.prune and dupe):
  476. if self.prune:
  477. guard = ""
  478. else:
  479. guard = "guard_" + hash
  480. # generate the call to the FLM
  481. if iface is not None and not dupe:
  482. # pickled interfaces save the parameters in a separate file
  483. # and add a parameter which points at that file's location.
  484. if pickled:
  485. self.pickleParameters(parameters, hash, bldinfop)
  486. # add the FLM call to the selected makefiles
  487. makefileset.addCall(spec.name, config.name, iface.name, useAllInterfaces, iface.GetFLMIncludePath(self.raptor.cache), parameters, guard)
  488. # recursive includes
  489. for child in childSpecs:
  490. self.WriteConfiguredSpec(makefileset, child, config, useAllInterfaces)
  491. if self.many:
  492. makefileset.close() # close child set of makefiles as we'll never see them again.
  493. def pickleParameters(self, parameters, hash, directory):
  494. """write a pickle of the parameter dictionary to directory/hash/pickle."""
  495. if not parameters or not hash or not directory:
  496. return
  497. dictionary = dict(parameters)
  498. # create a combined hash of the FLM parameters and the global variables
  499. # as we add the globals to the parameter dictionary we just made.
  500. md5hash = hashlib.md5()
  501. md5hash.update(hash)
  502. for k in sorted(self.global_make_variables.keys()):
  503. value = self.global_make_variables[k]
  504. md5hash.update(k + value)
  505. dictionary[k] = value
  506. planbdir = directory + "/" + md5hash.hexdigest()
  507. if not os.path.isdir(planbdir):
  508. try:
  509. os.makedirs(planbdir)
  510. except:
  511. self.raptor.Error("could not create directory " + planbdir)
  512. return
  513. filename = os.path.join(planbdir, "pickle")
  514. # if the file already exists then it is automatically up to date
  515. # because the name contains a hash of the contents.
  516. if not os.path.isfile(filename):
  517. try:
  518. file = open(filename, "wb")
  519. pickle.dump(dictionary, file, protocol=2)
  520. file.close()
  521. except:
  522. self.raptor.Error("could not create file " + filename)
  523. parameters.append(("PLANBDIR", planbdir))
  524. def Make(self, makefileset, build_zero_flmcall_makefiles = False):
  525. "run the make command"
  526. if not self.valid:
  527. return False
  528. # Always use Talon since it does the XML not
  529. # just descrambling
  530. if not self.StartTalon() and not self.raptor.keepGoing:
  531. self.Tidy()
  532. return False
  533. # run any initialisation script
  534. if self.initCommand:
  535. self.raptor.Info("Running {0}".format(self.initCommand))
  536. if os.system(self.initCommand) != 0:
  537. self.raptor.Error("Failed in {0}".format(self.initCommand))
  538. self.Tidy()
  539. return False
  540. # Save file names to a list, to allow the order to be reversed
  541. if build_zero_flmcall_makefiles:
  542. makefile_sequence = makefileset.makefiles
  543. self.raptor.Debug ("Makefiles to build: {0}".format(str([f.filename for f in makefile_sequence])))
  544. else:
  545. makefile_sequence = makefileset.nonempty_makefiles()
  546. self.raptor.Debug ("Makefiles with non-zero flm call counts: {0}".format(str([f.filename for f in makefile_sequence])))
  547. # Iterate through args passed to raptor, searching for CLEAN or REALLYCLEAN
  548. clean_flag = False
  549. for arg in self.raptor.args:
  550. clean_flag = ("CLEAN" in self.raptor.args) or \
  551. ("REALLYCLEAN" in self.raptor.args)
  552. # Files should be deleted in the opposite order to the order
  553. # they were built. So reverse file order if cleaning.
  554. # For len() etc to work we need to create a list
  555. # - not just an iterator which is what reversed() returns.
  556. if clean_flag:
  557. makefile_sequence = [m for m in reversed(makefile_sequence)]
  558. # Report number of makefiles to be built
  559. self.raptor.InfoDiscovery(object_type = "makefile", count = len(makefile_sequence))
  560. # Stores all the make processes that were executed:
  561. make_processes = []
  562. return_state = True
  563. # Process each file in turn
  564. for makefile in makefile_sequence:
  565. makefilename = str(makefile.filename)
  566. if not os.path.exists(makefilename):
  567. self.raptor.Info("Skipping makefile {0}".format(makefilename))
  568. continue
  569. self.raptor.Info("Making {0}".format(makefilename))
  570. # assemble the build command line
  571. command = self.buildCommand
  572. if self.makefileOption:
  573. command += ' {0} "{1}" '.format(self.makefileOption, makefilename)
  574. if self.raptor.keepGoing and self.keepGoingOption:
  575. command += " " + self.keepGoingOption
  576. if self.raptor.jobs > 1 and self.jobsOption:
  577. command += " {0} {1}".format(self.jobsOption,str(self.raptor.jobs))
  578. # Set default options first so that they can be overridden by
  579. # ones set by the --mo option on the raptor commandline:
  580. command += " " + self.defaultMakeOptions
  581. # Can supply options on the commandline to override default settings.
  582. if len(self.raptor.makeOptions) > 0:
  583. for o in self.raptor.makeOptions:
  584. if o.find(";") != -1 or o.find("\\") != -1:
  585. command += " '{0}'".format(o)
  586. else:
  587. command += " {0}".format(o)
  588. # Switch off dependency file including?
  589. if self.raptor.noDependInclude or self.raptor.noDependGenerate:
  590. command += " NO_DEPEND_INCLUDE=1"
  591. # Switch off dependency file generation (and, implicitly, inclusion)?
  592. if self.raptor.noDependGenerate:
  593. command += " NO_DEPEND_GENERATE=1"
  594. command += ' TALON_DESCRAMBLE='
  595. if self.scrambled:
  596. command += '1 '
  597. else:
  598. command += '0 '
  599. # use the retry mechanism if requested
  600. if self.raptor.tries > 1:
  601. command += ' RECIPETRIES={0}'.format(self.raptor.tries)
  602. command += ' TALON_RETRIES={0}'.format(self.raptor.tries - 1)
  603. # targets go at the end, if the makefile supports them
  604. addTargets = self.raptor.targets[:]
  605. ignoreTargets = makefile.ignoretargets
  606. if addTargets and ignoreTargets:
  607. for target in self.raptor.targets:
  608. if re.match(ignoreTargets, target):
  609. addTargets.remove(target)
  610. if addTargets:
  611. command += " " + " ".join(addTargets)
  612. # Send stderr to a file so that it can't mess up the log (e.g.
  613. # clock skew messages from some build engines scatter their
  614. # output across our xml.
  615. stderrfilename = makefilename+'.stderr'
  616. stdoutfilename = makefilename+'.stdout'
  617. command += " 2>'{0}' ".format(stderrfilename)
  618. # Keep a copy of the stdout too in the case of using the
  619. # annofile - so that we can trap the problem that
  620. # makes the copy-log-from-annofile workaround necessary
  621. # and perhaps determine when we can remove it.
  622. if self.copyLogFromAnnoFile:
  623. command += " >'{0}' ".format(stdoutfilename)
  624. # Substitute the makefile name for any occurrence of #MAKEFILE#
  625. command = command.replace("#MAKEFILE#", makefilename)
  626. # Substitute the makefile stage for any occurrence of #STAGE#
  627. # e.g. --history=ncp_#STAGE#.hist might become
  628. # --history=ncp_export.hist
  629. command = command.replace("#STAGE#", makefile.name)
  630. self.raptor.Info("Executing '{0}'".format(command))
  631. # Create a process of make program
  632. mproc = MakeProcess(command)
  633. mproc.makefile = str(makefilename)
  634. mproc.talon_recipeattributes = "none"
  635. mproc.talon_shell = self.talonshell
  636. mproc.talon_buildid = str(self.buildID)
  637. mproc.talon_timeout = str(self.talontimeout)
  638. mproc.filesystem = self.raptor.filesystem
  639. mproc.logstream = self.raptor.out
  640. mproc.copyLogFromAnnoFile = self.copyLogFromAnnoFile
  641. mproc.stderrfilename = stderrfilename
  642. mproc.stdoutfilename = stdoutfilename
  643. mproc.shell = raptor_data.ToolSet.shell
  644. make_processes.append(mproc)
  645. if self.copyLogFromAnnoFile:
  646. mproc.annoFileName = self.annoFileName.replace("#MAKEFILE#", makefilename)
  647. # execute the build.
  648. # the actual call differs between Windows and Unix.
  649. # bufsize=1 means "line buffered"
  650. try:
  651. # Time the build
  652. self.raptor.InfoStartTime(object_type = "makefile",
  653. task = "build", key = str(makefilename))
  654. run_make(mproc)
  655. log_output(mproc)
  656. if mproc.returncode != 0:
  657. return_state = False
  658. if not self.raptor.keepGoing:
  659. break
  660. except Exception,e:
  661. self.raptor.Error("Exception '{0}' during '{1}'".format(str(e), command))
  662. break
  663. finally:
  664. # Still report end-time of the build
  665. self.raptor.InfoEndTime(object_type = "makefile", task = "build",
  666. key = str(makefilename))
  667. # run any shutdown script
  668. if self.shutdownCommand != None and self.shutdownCommand != "":
  669. self.raptor.Info("Running {0}".format(self.shutdownCommand))
  670. if os.system(self.shutdownCommand) != 0:
  671. self.raptor.Error("Failed in {0}".format(self.shutdownCommand))
  672. return_state = False
  673. self.Tidy()
  674. return return_state
  675. def Tidy(self):
  676. "clean up after the make command"
  677. self.StopTalon()
  678. def StartTalon(self):
  679. # the talon command
  680. beginning = raptor.hostplatform_dir + "/bin"
  681. if "win" in raptor.hostplatform:
  682. end = ".exe"
  683. else:
  684. end = ""
  685. self.talonctl = str(self.raptor.home.Append(beginning, "talonctl"+end))
  686. # generate a unique build number
  687. random.seed()
  688. looking = True
  689. tries = 0
  690. while looking and tries < 100:
  691. self.buildID = raptor.name + str(random.getrandbits(32))
  692. command = self.talonctl + " start"
  693. os.environ["TALON_BUILDID"] = self.buildID
  694. self.raptor.Info("Running {0}".format(command))
  695. looking = (os.system(command) != 0)
  696. tries += 1
  697. if looking:
  698. self.raptor.Error("Failed to initialise the talon shell for this build")
  699. self.talonctl = ""
  700. return False
  701. return True
  702. def StopTalon(self):
  703. if self.talonctl:
  704. command = self.talonctl + " stop"
  705. self.talonctl = ""
  706. self.raptor.Info("Running {0}".format(command))
  707. if os.system(command) != 0:
  708. self.raptor.Error("Failed in {0}".format(command))
  709. return False
  710. return True
  711. def StartDescrambler(self):
  712. # the descrambler command
  713. beginning = raptor.hostplatform_dir + "/bin"
  714. if "win" in raptor.hostplatform:
  715. end = ".exe"
  716. else:
  717. end = ""
  718. self.descrambler = str(self.raptor.home.Append(beginning, "sbs_descramble"+end))
  719. # generate a unique build number
  720. random.seed()
  721. looking = True
  722. tries = 0
  723. while looking and tries < 100:
  724. buildID = raptor.name + str(random.getrandbits(32))
  725. command = self.descrambler + " " + buildID + " start"
  726. self.raptor.Info("Running {0}".format(command))
  727. looking = (os.system(command) != 0)
  728. tries += 1
  729. if looking:
  730. self.raptor.Error("Failed to start the log descrambler")
  731. self.descrambler_started = True
  732. return False
  733. self.descrambler_started = True
  734. self.descrambler += " " + buildID
  735. return True
  736. def StopDescrambler(self):
  737. if self.descrambler_started:
  738. command = self.descrambler + " stop"
  739. self.descrambler = ""
  740. self.raptor.Info("Running {0}".format(command))
  741. if os.system(command) != 0:
  742. self.raptor.Error("Failed in {0}".format(command))
  743. return False
  744. return True
  745. # end of the raptor_make module