spec2spectacle 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772
  1. #!/usr/bin/python -tt
  2. # vim: ai ts=4 sts=4 et sw=4
  3. # Copyright (c) 2009 Intel Corporation
  4. #
  5. # This program is free software; you can redistribute it and/or modify it
  6. # under the terms of the GNU General Public License as published by the Free
  7. # Software Foundation; version 2 of the License
  8. #
  9. # This program is distributed in the hope that it will be useful, but
  10. # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  11. # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  12. # for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License along
  15. # with this program; if not, write to the Free Software Foundation, Inc., 59
  16. # Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  17. """Overview of spec2spectacle
  18. """
  19. import os
  20. import sys
  21. import re
  22. import glob
  23. import optparse
  24. # spectacle modules
  25. from spectacle.convertor import *
  26. from spectacle.dumper import *
  27. from spectacle import logger
  28. class SpecError(Exception):
  29. def __ini__(self, cur_state, cur_pkg, cur_line):
  30. self.cur_state = cur_state
  31. self.cur_pkg = cur_pkg
  32. self.cur_line = cur_line
  33. def __repr__(self):
  34. return self.cur_state + self.cur_pkg + self.cur_line
  35. class SpecFormatError(SpecError):
  36. pass
  37. class SpecUnknowLineError(SpecError):
  38. pass
  39. class SpecUnknowHeaderError(SpecError):
  40. pass
  41. HEADERS = ( 'package',
  42. 'description',
  43. 'prep',
  44. 'build',
  45. 'install',
  46. 'clean',
  47. 'check',
  48. 'preun',
  49. 'pre',
  50. 'postun',
  51. 'post',
  52. 'files',
  53. 'changelog' )
  54. SINGLES = ( 'Summary',
  55. 'Name',
  56. 'Version',
  57. 'Release',
  58. 'Epoch',
  59. 'URL',
  60. 'Url',
  61. 'Group',
  62. 'BuildArch',
  63. 'AutoReq',
  64. 'AutoProv',
  65. 'AutoReqProv',
  66. 'Autoreq',
  67. 'Autoprov',
  68. 'Autoreqprov',
  69. 'ExclusiveArch',
  70. 'Prefix',
  71. 'License' )
  72. REQUIRES = ('BuildRequires',
  73. 'Requires',
  74. 'Requires(post)',
  75. 'Requires(postun)',
  76. 'Requires(pre)',
  77. 'PreRequires', 'PreReq', 'Prereq', # alias in old spec
  78. 'Requires(preun)',
  79. 'Provides',
  80. 'Obsoletes',
  81. 'Conflicts',
  82. 'BuildConflicts',
  83. )
  84. SKIPS = ( 'BuildRoot',)
  85. # must have keys for 'main' package
  86. MUSTHAVE = {'Release': '1',
  87. }
  88. # state definition of parser
  89. (
  90. ST_DEFINE,
  91. ST_MAIN,
  92. ST_INLINE,
  93. ST_SUBPKG,
  94. ) = range(4)
  95. class SpecConvertor(Convertor):
  96. """ Convertor for SpecBuild ini files """
  97. def __init__(self):
  98. sb_cv_table = {
  99. 'BuildRequires': 'PkgBR',
  100. 'pre': 'install-pre',
  101. 'description': 'Description',
  102. 'Requires(post)': 'RequiresPost',
  103. 'Requires(postun)': 'RequiresPostUn',
  104. 'Requires(pre)': 'RequiresPre',
  105. 'PreRequires': 'RequiresPre',
  106. 'PreReq': 'RequiresPre',
  107. 'Prereq': 'RequiresPre',
  108. 'Requires(preun)': 'RequiresPreUn',
  109. 'Url': 'URL',
  110. 'Autoreq': 'AutoReq',
  111. 'Autoprov': 'AutoProv',
  112. 'Autoreqprov': 'AutoReqProv',
  113. }
  114. Convertor.__init__(self, sb_cv_table)
  115. class SpecParser(object):
  116. """ Parser of SPEC file of rpm package """
  117. def __init__(self, replace_macros, builder_parsing, include_files):
  118. # runtime variables
  119. self.items = {}
  120. self.table = {}
  121. self.cur_pkg = 'main'
  122. self.include_files = include_files
  123. self.builder_parsing = builder_parsing
  124. self._Builder = None
  125. self._Configure = None
  126. self.replace_macros = replace_macros
  127. self.macros = []
  128. def _switch_subpkg(self, subpkg, create=False):
  129. # whether '-n subpkg'?
  130. wholename = False
  131. ls = subpkg.split()
  132. if '-n' in ls:
  133. try:
  134. subpkg = ls[ls.index('-n')+1]
  135. wholename = True
  136. except IndexError:
  137. raise SpecFormatError(subpkg)
  138. else:
  139. subpkg = ls[0]
  140. subpkg_new = 'SubPackages' not in self.items or subpkg not in self.items['SubPackages']
  141. if subpkg_new and not create:
  142. logger.warning('<spec2spectacle> un-declared subpkg %s found in spec' % subpkg)
  143. return None
  144. if subpkg_new:
  145. if 'SubPackages' not in self.items:
  146. self.items['SubPackages'] = {}
  147. if subpkg not in self.items['SubPackages']:
  148. self.items['SubPackages'][subpkg] = {}
  149. if wholename:
  150. self.items['SubPackages'][subpkg]['AsWholeName'] = True
  151. # switch
  152. self.cur_pkg = subpkg
  153. return self.items['SubPackages'][subpkg]
  154. def _do_package(self, items, pkg, h, v):
  155. # skip
  156. pass
  157. def _do_prep(self, items, pkg, h, v):
  158. logger.info('<spec2spectacle> the following is the content of PREP in original spec, please compare them with the generated carefully: \n%s' % v)
  159. def _do_build(self, items, pkg, h, v):
  160. """ to handle build script:
  161. trying to find out the most of the generic cases
  162. """
  163. def _save_raw_in_post(items, lines):
  164. # make sure no 'make' generated auto
  165. items['Configure'] = 'none'
  166. items['Builder'] = 'none'
  167. # put all script to 'post install'
  168. items['extra']['PostMakeExtras'] = lines
  169. ### Sub START
  170. lines = v.splitlines()
  171. if lines[0].startswith('-'):
  172. lines.pop(0)
  173. if not self.builder_parsing:
  174. _save_raw_in_post(items, lines)
  175. return
  176. # parts of build script
  177. (PRE, POST_CFG, POST_BUILD) = range(3)
  178. parts = { 'pre': [], 'post': [] }
  179. cur_part = PRE
  180. cont_line = False
  181. for line in lines:
  182. if cont_line:
  183. whole_line = whole_line + ' ' + line.strip()
  184. else:
  185. if not line.strip() or line.startswith('#'):
  186. # empty line or comment line, skip
  187. continue
  188. whole_line = line.strip()
  189. if line[-1:] == '\\':
  190. cont_line = True
  191. whole_line = whole_line[:-1].strip()
  192. continue
  193. else:
  194. cont_line = False
  195. found_cfgr = False
  196. found_bldr = False
  197. # find configure in current line
  198. for cfgr in ('configure', 'reconfigure', 'autogen'):
  199. pieces = whole_line.split()
  200. if cfgr in pieces or '%'+cfgr in pieces:
  201. found_cfgr = True
  202. self._Configure = cfgr
  203. cfgr_line = whole_line[len(cfgr)+1:].strip()
  204. break
  205. # find builder in current line
  206. if re.match('^(%\{__)?make', whole_line):
  207. found_bldr = True
  208. self._Builder = 'make'
  209. elif re.search('python\W+setup.py\W+build', whole_line):
  210. found_bldr = True
  211. self._Builder = 'python'
  212. if cur_part == PRE:
  213. if found_cfgr:
  214. cur_part = POST_CFG
  215. elif found_bldr:
  216. cur_part = POST_BUILD
  217. else:
  218. parts['pre'].append(whole_line)
  219. elif cur_part == POST_CFG:
  220. if found_cfgr:
  221. # more 'configr', wrong
  222. cur_part = PRE
  223. break
  224. elif found_bldr:
  225. cur_part = POST_BUILD
  226. else:
  227. # another line(s) between configr and buildr, wrong
  228. # or no supported buildr found, wrong
  229. cur_part = PRE
  230. break
  231. elif cur_part == POST_BUILD:
  232. parts['post'].append(whole_line)
  233. if cur_part == PRE:
  234. # means match failed
  235. _save_raw_in_post(items, lines)
  236. else:
  237. if self._Configure:
  238. items['Configure'] = self._Configure
  239. # parse the configure options
  240. opts = map(lambda s: '--'+s,
  241. [opt for opt in map(str.strip, cfgr_line.split('--')) if opt])
  242. if opts:
  243. items['ConfigOptions'] = opts
  244. else:
  245. # no configure found, only need builder
  246. items['Configure'] = 'none'
  247. if self._Builder:
  248. items['Builder'] = self._Builder
  249. if parts['pre']:
  250. items['extra']['PreMakeExtras'] = parts['pre']
  251. if parts['post']:
  252. items['extra']['PostMakeExtras'] = parts['post']
  253. def _do_install(self, items, pkg, h, v):
  254. """ to handle install script:
  255. trying to find out the most of the generic cases
  256. """
  257. lines = v.splitlines()
  258. if lines[0].startswith('-'):
  259. lines.pop(0)
  260. # try to search %find_lang
  261. filter_lines = []
  262. for line in lines:
  263. if line.startswith('%find_lang'):
  264. m = re.compile('^%find_lang\s+(.*)\s*').match(line)
  265. if m:
  266. parts = m.group(1).split()
  267. if '||' in parts:
  268. parts = parts[:parts.index('||')]
  269. items['LocaleName'] = parts.pop(0)
  270. if parts:
  271. items['LocaleOptions'] = parts
  272. continue
  273. filter_lines.append(line)
  274. lines = filter_lines
  275. parts = { 'pre': [], 'post': [] }
  276. if self._Builder == 'make':
  277. re_installer = re.compile('make[_ \t]*install')
  278. elif self._Builder == 'python':
  279. re_installer = re.compile('python\W+setup.py\W+install')
  280. re_cleanup = re.compile('(rm|\%\{__rm\})\W+-rf\W+(\$RPM_BUILD_ROOT|\%\{buildroot\})')
  281. if self._Builder:
  282. # have found 'Builder' in build scripts
  283. found_insaller = False
  284. for line in lines:
  285. if not found_insaller:
  286. if re_installer.search(line):
  287. found_insaller = True
  288. elif re_cleanup.search(line):
  289. # skip cleanup line
  290. pass
  291. else:
  292. parts['pre'].append(line)
  293. else:
  294. parts['post'].append(line)
  295. if found_insaller:
  296. if parts['pre']:
  297. items['extra']['PreMakeInstallExtras'] = parts['pre']
  298. if parts['post']:
  299. items['extra']['PostMakeInstallExtras'] = parts['post']
  300. return
  301. # false safe case
  302. items['extra']['PostMakeInstallExtras'] = lines
  303. def _do_clean(self, items, pkg, h, v):
  304. # skip
  305. pass
  306. def _do_check(self, items, pkg, h, v):
  307. items['extra']['check'] = v.strip()
  308. items['Check'] = True
  309. def _parse_prog_in_opt(self, header):
  310. ls = header.split()
  311. if '-p' in ls:
  312. try:
  313. return ls[ls.index('-p')+1]
  314. except IndexError:
  315. raise SpecFormatError(h)
  316. else:
  317. return ''
  318. def _do_extra_scripts(self, items, h, v):
  319. section = h.split()[0][1:]
  320. items['extra'][section] = v.strip().splitlines()
  321. inline_prog = self._parse_prog_in_opt(h)
  322. if inline_prog:
  323. items['extra'][section].insert(0, inline_prog)
  324. def _do_pre(self, items, pkg, h, v):
  325. self._do_extra_scripts(items, h, v)
  326. def _do_preun(self, items, pkg, h, v):
  327. self._do_extra_scripts(items, h, v)
  328. def _do_post(self, items, pkg, h, v):
  329. self._do_extra_scripts(items, h, v)
  330. def _do_postun(self, items, pkg, h, v):
  331. self._do_extra_scripts(items, h, v)
  332. def _do_changelog(self, items, pkg, h, v):
  333. logger.warning('<spec2spectacle> Please move changelog in %changelog to *.changes file.')
  334. def _remove_attrs(self, files):
  335. # try to remove duplicate '%defattr' in files list
  336. dup = '%defattr(-,root,root,-)'
  337. dup2 = '%defattr(-,root,root)'
  338. if dup in files:
  339. files.remove(dup)
  340. if dup2 in files:
  341. files.remove(dup2)
  342. def _do_files(self, items, pkg, h, v):
  343. files = map(str.strip, v.strip().splitlines())
  344. if files:
  345. # skip option line
  346. if files[0].startswith('-'):
  347. files.pop(0)
  348. self._remove_attrs(files)
  349. if self.include_files:
  350. items['Files'] = files
  351. else:
  352. items['extra']['Files'] = files
  353. def _do_description(self, items, pkg, h, v):
  354. items['Description'] = v.strip()
  355. def read(self, filename):
  356. """ read in all recognized directives and headers """
  357. comment = re.compile('^#.*')
  358. directive = re.compile('^([\w()]+):[ \t]*(.*)')
  359. define_re = re.compile('^%(define|global)\s+(\w+)\s+(.*)')
  360. header_re = re.compile('^%(' + '|'.join(HEADERS) + ')\s*(.*)')
  361. state = ST_DEFINE
  362. items = self.items
  363. for line in file(filename):
  364. if state == ST_DEFINE:
  365. line = line.strip()
  366. if not line or comment.match(line):
  367. # skip comment line and empty line
  368. continue
  369. m = define_re.match(line)
  370. if m:
  371. if self.replace_macros:
  372. self.table[m.group(2)] = m.group(3)
  373. else:
  374. self.macros.append(line)
  375. continue #short-cut
  376. else:
  377. state = ST_MAIN
  378. # fall through
  379. if state == ST_INLINE:
  380. if header_re.match(line):
  381. state = ST_MAIN
  382. # fall through
  383. else:
  384. if items:
  385. items[cur_block] += line
  386. continue
  387. if state == ST_MAIN:
  388. line = line.strip()
  389. if not line or comment.match(line):
  390. # skip comment line and empty line
  391. continue
  392. dm = directive.match(line)
  393. if not dm:
  394. hm = header_re.match(line)
  395. if dm:
  396. key = dm.group(1)
  397. val = dm.group(2)
  398. # special case for Source and Patch
  399. if key.startswith('Source'):
  400. key = 'Sources'
  401. elif key.startswith('Patch'):
  402. key = 'Patches'
  403. if key not in items:
  404. items[key] = [val]
  405. else:
  406. items[key].append(val)
  407. elif hm:
  408. header = hm.group(1)
  409. opt = hm.group(2)
  410. if header not in HEADERS:
  411. raise SpecUnknowHeaderError(state, self.cur_pkg, header)
  412. if header == 'package':
  413. if not opt:
  414. raise SpecFormatError(line)
  415. items = self._switch_subpkg(opt, True)
  416. else:
  417. # inline sections of other headers
  418. state = ST_INLINE
  419. if opt and (not opt.startswith('-') or '-n' in opt):
  420. # section with sub-pkg specified
  421. items = self._switch_subpkg(opt)
  422. else:
  423. # for 'main' package
  424. items = self.items
  425. if items:
  426. cur_block = header
  427. if cur_block not in items:
  428. items[cur_block] = line+'\n'
  429. """
  430. # options in header line as the first line
  431. if ' -' in line:
  432. items[cur_block] = line[line.index(' -'):] + '\n'
  433. """
  434. else:
  435. # unparsed line
  436. known_skips = ('python_sitelib', 'python_sitearch')
  437. msg = True
  438. for skip in known_skips:
  439. if skip in line:
  440. msg = False
  441. break
  442. if msg:
  443. logger.warning('<spec2spectacle> un-parsed spec line skipped: %s' % line)
  444. def cooked_items(self):
  445. """ return all items, cooked to the input of convertor """
  446. return self._cook_items('main', self.items)
  447. def _cook_items(self, pkg_name, items):
  448. """ helper function to transfer data structure
  449. <recursive>
  450. """
  451. # pattern of macros
  452. macro_re = re.compile('%{(\w+)}')
  453. ck_items = {'extra': {}}
  454. if pkg_name != 'main':
  455. ck_items['Name'] = pkg_name
  456. for k, v in items.iteritems():
  457. if k in SKIPS or k in HEADERS or k == 'SubPackages':
  458. continue
  459. if self.table:
  460. # macro replacing
  461. nv = []
  462. for vi in v:
  463. while macro_re.search(vi):
  464. nvi = vi
  465. for m in macro_re.finditer(vi):
  466. macro, name = m.group(0, 1)
  467. if name in self.table:
  468. nvi = nvi.replace(macro, self.table[name])
  469. if vi == nvi:
  470. break # break to exit 'while' loop
  471. vi = nvi
  472. # now nvi is the replaced string
  473. nv.append(vi)
  474. v = nv
  475. if k in SINGLES:
  476. # special case for Release
  477. if k == 'Release':
  478. m = re.match('(\S+)%{\?dist}', v[0])
  479. if m: ck_items[k] = m.group(1)
  480. else: ck_items[k] = v[0]
  481. else:
  482. ck_items[k] = v[0]
  483. elif k in REQUIRES:
  484. nbr = [] # new 'PkgConfigBR' list
  485. nv = []
  486. for vi in v:
  487. if 'perl' in vi:
  488. nv.append(vi)
  489. elif ',' in vi:
  490. reqs = []
  491. for entry in re.findall('\S+\s+[<>=]+\s+[^,\s]+|[^,\s]+', vi):
  492. reqs.append(entry)
  493. nv += reqs
  494. elif ' ' in vi:
  495. reqs = []
  496. for entry in re.findall('\S+\s+[<>=]+\s+\S+|\S+', vi):
  497. reqs.append(entry)
  498. nv += reqs
  499. else:
  500. nv.append(vi)
  501. if 'pkgconfig' in vi and k == 'BuildRequires':
  502. for nvi in nv:
  503. pkgbr = re.sub(r'pkgconfig\s*\(\s*([^\)]*)\s*\)', r'\1', nvi)
  504. if pkgbr != nvi:
  505. nbr.append(pkgbr)
  506. nv.remove(nvi)
  507. ck_items[k] = nv
  508. if nbr:
  509. ck_items['PkgConfigBR'] = nbr
  510. else:
  511. ck_items[k] = v
  512. # handle all sectinos with header, IN-ORDER
  513. for hdr in HEADERS:
  514. if hdr in items:
  515. routine = getattr(self, '_do_' + hdr)
  516. hdr_line, Drop, content = items[hdr].partition('\n')
  517. routine(ck_items, pkg_name, hdr_line, content)
  518. if pkg_name != 'main':
  519. # shortcut for subpkg
  520. return ck_items
  521. # handle subpackages
  522. if 'SubPackages' in items:
  523. ck_items['SubPackages'] = []
  524. for sub, sub_items in items['SubPackages'].iteritems():
  525. ck_items['SubPackages'].append(self._cook_items(sub, sub_items))
  526. # check must-have keys
  527. for key, default in MUSTHAVE.iteritems():
  528. if key not in ck_items:
  529. ck_items[key] = default
  530. # check for global macros
  531. if self.macros:
  532. ck_items['extra']['macros'] = self.macros
  533. return ck_items
  534. def parse_options(args):
  535. import spectacle.__version__
  536. usage = "Usage: %prog [options] [spec-path]"
  537. parser = optparse.OptionParser(usage, version=spectacle.__version__.VERSION)
  538. parser.add_option("-o", "--output", type="string",
  539. dest="outfile_path", default=None,
  540. help="Path of output yaml file")
  541. parser.add_option("-r", "--replace-macros", action="store_true",
  542. dest="replace_macros", default=False,
  543. help="To replace self-defined macros in spec file")
  544. parser.add_option("", "--no-builder-parsing", action="store_false",
  545. dest="builder_parsing", default=True,
  546. help="Do NOT try to parse build/install scripts")
  547. parser.add_option("-f", "--include-files", action="store_true",
  548. dest="include_files", default=False,
  549. help="To store files list in YAML file")
  550. return parser.parse_args()
  551. def check_yaml_file(spec_fpath):
  552. specDir = os.path.dirname(spec_fpath)
  553. if not specDir:
  554. specDir = os.path.curdir
  555. yaml_s = glob.glob('*.yaml')
  556. if yaml_s:
  557. answer = logger.ask(""""*.yaml" file(s) exists in working dir: %s
  558. Maybe this package has been converted to spectacle enabled one.
  559. Continue?""" % ' '.join(yaml_s), False)
  560. if not answer:
  561. sys.exit(1)
  562. def check_ini_file(spec_fpath):
  563. specDir = os.path.dirname(spec_fpath)
  564. if not specDir:
  565. specDir = os.path.curdir
  566. ini_s = glob.glob('*.ini')
  567. if ini_s:
  568. answer = logger.ask(""""*.ini" file(s) exists in working dir: %s
  569. If being spec-builder file(s), please use ini2spectacle to convert.
  570. Continue?""" % ' '.join(ini_s), False)
  571. if not answer:
  572. sys.exit(1)
  573. def check_spec_file(spec_fpath):
  574. heads = """#
  575. # Do not Edit! Generated by:
  576. # spectacle version """
  577. new_heads = """#
  578. # Do NOT Edit the Auto-generated Part!
  579. # Generated by: spectacle version """
  580. fcont = file(spec_fpath).read()
  581. if fcont.startswith(heads) or fcont.startswith(new_heads):
  582. logger.error('<spec2spectacle> Input spec file is a spectacle generated one, do NOT convert it again.')
  583. if __name__ == '__main__':
  584. """ Main Function """
  585. (options, args) = parse_options(sys.argv[1:])
  586. if not args:
  587. # no spec-path specified, search in CWD
  588. specls = glob.glob('*.spec')
  589. if not specls:
  590. logger.error('<spec2spectacle> Cannot find valid spec file in current directory, please specify one.')
  591. elif len(specls) > 1:
  592. logger.error('<spec2spectacle> Find multiple spec files in current directory, please specify one.')
  593. spec_fpath = specls[0]
  594. else:
  595. spec_fpath = args[0]
  596. # Check if YAML file exists
  597. check_yaml_file(spec_fpath)
  598. # Check if spec-build's INI file exists
  599. check_ini_file(spec_fpath)
  600. # Check if the input file exists
  601. if not os.path.exists(spec_fpath):
  602. # input file does not exist
  603. logger.error("<spec2spectacle> %s: File does not exist" % spec_fpath)
  604. # Check if spec file is spectacle generated one
  605. check_spec_file(spec_fpath)
  606. # check the working path
  607. if spec_fpath.find('/') != -1 and os.path.dirname(spec_fpath) != os.path.curdir:
  608. wdir = os.path.dirname(spec_fpath)
  609. logger.info('<spec2spectacle> Changing to working dir: %s' % wdir)
  610. os.chdir(wdir)
  611. spec_fname = os.path.basename(spec_fpath)
  612. if options.outfile_path:
  613. out_fpath = options.outfile_path
  614. else:
  615. if spec_fname.endswith('.spec'):
  616. out_fpath = spec_fname[:-4] + 'yaml'
  617. else:
  618. out_fpath = spec_fname + '.yaml'
  619. """Read the input file"""
  620. spec_parser = SpecParser(replace_macros = options.replace_macros,
  621. builder_parsing = options.builder_parsing,
  622. include_files = options.include_files
  623. )
  624. try:
  625. spec_parser.read(spec_fname)
  626. except SpecFormatError, e:
  627. logger.warning('<spec2spectacle> Spec syntax error: %s' % str(e))
  628. except SpecUnknowHeaderError, e:
  629. logger.warning('<spec2spectacle> Unknown spec header: %s' % str(e))
  630. convertor = SpecConvertor()
  631. """Dump them to spectacle file"""
  632. dumper = SpectacleDumper(format='yaml', opath = out_fpath)
  633. newspec_fpath = dumper.dump(convertor.convert(spec_parser.cooked_items()))
  634. logger.info('<spec2spectacle> Yaml file %s created' % out_fpath)
  635. if newspec_fpath:
  636. bak_spec_fpath = os.path.join('spec.backup', newspec_fpath)
  637. logger.info('<spec2spectacle> New spec file %s was generated by new yaml file,' % newspec_fpath)
  638. logger.info('<spec2spectacle> and orignal spec file was saved as %s' % bak_spec_fpath)