ini2spectacle 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  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 ini2yaml
  18. (1) ini2yaml reads the ini file and divides it into segments.
  19. (2) Parses the 'header' segment. Write out the key,value pairs
  20. (3) Expand the 'Files' if found
  21. (4) Parse the sub-packages segments if found
  22. """
  23. import os
  24. import sys
  25. import re
  26. import shutil
  27. import optparse
  28. from ConfigParser import RawConfigParser
  29. # spectacle modules
  30. from spectacle.convertor import *
  31. from spectacle.dumper import *
  32. from spectacle import logger
  33. class SBConvertor(Convertor):
  34. """ Convertor for SpecBuild ini files """
  35. def __init__(self):
  36. sb_cv_table = {
  37. 'BuildRequires': 'PkgBR',
  38. 'PkgConfig': 'PkgConfigBR',
  39. 'Extras': 'ExtraSources',
  40. 'Extra': 'extra',
  41. }
  42. Convertor.__init__(self, sb_cv_table)
  43. class SBConfigParser(RawConfigParser):
  44. """ SpecBuild ini specific parser """
  45. # keys whose value need to split to list
  46. multi_keys = ('Sources',
  47. 'Patches',
  48. 'Extras',
  49. 'LocaleOptions',
  50. )
  51. # keys whose value may have ver numbers
  52. reqs_keys = ('BuildRequires',
  53. 'PreRequires',
  54. 'Requires',
  55. 'PostRequires',
  56. 'PkgConfig',
  57. 'Provides',
  58. 'Obsoletes',
  59. 'Conflicts',
  60. )
  61. # keys whose value need to be expand(reading extra file)
  62. expand_keys = ('Files',
  63. 'Description',
  64. 'PostMakeExtras',
  65. 'PostMakeInstallExtras',
  66. )
  67. # boolean keys
  68. bool_keys = ('UseAsNeeded',
  69. 'NoAutoReq',
  70. 'NoAutoProv',
  71. 'AddCheck',
  72. )
  73. # keys need to be placed to 'extra'
  74. extra_keys = ['Files',
  75. 'PostMakeExtras',
  76. 'PostMakeInstallExtras',
  77. ]
  78. # must have keys
  79. must_keys = {'Release': '1',
  80. 'Configure': 'configure',
  81. }
  82. def __init__(self, include_files):
  83. RawConfigParser.__init__(self)
  84. if include_files:
  85. self.extra_keys.remove('Files')
  86. self.record_used_files = []
  87. self._check_Makefile()
  88. def _check_Makefile(self):
  89. """ Check whether Makefile in working dir should be moved """
  90. if os.path.exists('Makefile'):
  91. try:
  92. cont = file('Makefile').read()
  93. if re.search('\s+spec-builder\s+', cont):
  94. # The Makefile is for spec-builder
  95. self.record_used_files.append('Makefile')
  96. except:
  97. pass
  98. def read(self, filenames):
  99. """ override super read to record input files """
  100. if isinstance(filenames, basestring):
  101. filenames = [filenames]
  102. self.record_used_files.extend(filenames)
  103. return RawConfigParser.read(self, filenames)
  104. def optionxform(self, option):
  105. # Capitalize the first char
  106. lead = option[0]
  107. if lead.upper() != lead:
  108. return lead.upper() + option[1:]
  109. else:
  110. return option
  111. def _expand_single(self, filename):
  112. """ Helper function to expand *.desc """
  113. if os.path.exists(filename):
  114. self.record_used_files.append(filename)
  115. return file(filename).read().strip()
  116. else:
  117. logger.warning("Warning: missing expected file %s" % filename)
  118. return None
  119. def _cook_config(self):
  120. """ Helper function to update fields for spec-builder specific ones
  121. """
  122. all_items = {}
  123. main_extra = {} # for extra keys of main pkg
  124. for section in self.sections():
  125. items = self._sections[section]
  126. # Convert space seperated string to list
  127. for key in self.multi_keys:
  128. if key in items:
  129. self.set(section, key, map(str.strip, items[key].split()))
  130. # Convert dependent like entry to list
  131. for key in self.reqs_keys:
  132. if key in items:
  133. reqs = []
  134. for entry in re.findall('\S+\s+[<>=]+\s+\S+|\S+', items[key]):
  135. reqs.append(entry.split(',')[0])
  136. self.set(section, key, reqs)
  137. # special cases for 'ConfigOptions'
  138. if 'ConfigOptions' in items:
  139. self.set(section, 'ConfigOptions',
  140. map(lambda s: '--'+s,
  141. [opt for opt in map(str.strip, items['ConfigOptions'].split('--')) if opt]))
  142. # special cases for 'LocaleOptions'
  143. if 'LocaleOptions' in items and 'LocaleName' not in items:
  144. self.set(section, 'LocaleName', '%{name}')
  145. # Convert boolean keys
  146. for key in self.bool_keys:
  147. if key in items:
  148. if items[key].upper() in ('NO', 'FALSE', '0'):
  149. del items[key]
  150. else:
  151. self.set(section, key, 'yes')
  152. # Convert keys which need expanding from external file
  153. for key in self.expand_keys:
  154. if key in items:
  155. content = self._expand_single(items[key])
  156. if content:
  157. if key == 'Files':
  158. # 'Files' need split to list
  159. self.set(section, key, content.split('\n'))
  160. else:
  161. self.set(section, key, content)
  162. else:
  163. # if file empty or file not exists, remove the empty key
  164. del items[key]
  165. # move extra keys to 'extra' key
  166. extra = {}
  167. for key in self.extra_keys:
  168. if key in items:
  169. extra[key] = items[key]
  170. del items[key]
  171. if extra:
  172. if section not in ('header', 'configuration'):
  173. self.set(section, 'extra', extra)
  174. else:
  175. main_extra.update(extra)
  176. for section in self.sections():
  177. if section in ('header', 'configuration'):
  178. all_items.update(dict(self.items(section)))
  179. if main_extra:
  180. all_items['extra'] = main_extra
  181. # Checking must-have keys
  182. for key, default in self.must_keys.iteritems():
  183. if key not in all_items:
  184. all_items[key] = default
  185. # Re-structure sub packages to inner level
  186. if 'SubPackages' in all_items:
  187. subpkg_list = all_items['SubPackages'].split()
  188. all_items['SubPackages'] = []
  189. for subpkg in subpkg_list:
  190. try:
  191. all_items['SubPackages'].append(dict(self.items(subpkg)))
  192. all_items['SubPackages'][-1].update({'Name': subpkg})
  193. except NoSectionError, e:
  194. logger.warning('Needed section for sub-package %s not found' % subpkg)
  195. raise e
  196. return all_items
  197. def cooked_items(self):
  198. """ return all items, cooked to the input of convertor """
  199. return self._cook_config()
  200. def parse_options(args):
  201. import spectacle.__version__
  202. usage = "Usage: %prog [options] [ini-path]"
  203. parser = optparse.OptionParser(usage, version=spectacle.__version__.VERSION)
  204. parser.add_option("-o", "--output", type="string",
  205. dest="outfile_path", default=None,
  206. help="Path of output yaml file")
  207. parser.add_option("-f", "--include-files", action="store_true",
  208. dest="include_files", default=False,
  209. help="To store files list in YAML file")
  210. return parser.parse_args()
  211. if __name__ == '__main__':
  212. """ Main Function """
  213. (options, args) = parse_options(sys.argv[1:])
  214. if not args:
  215. # no ini-path specified, search in CWD
  216. import glob
  217. inils = glob.glob('*.ini')
  218. if not inils:
  219. logger.error('Cannot find valid spec-builder file(*.ini) in current directory, please specify one.')
  220. elif len(inils) > 1:
  221. logger.error('Find multiple spec-builder files(*.ini) in current directory, please specify one.')
  222. ini_fpath = inils[0]
  223. else:
  224. ini_fpath = args[0]
  225. # Check if the input file exists
  226. if not os.path.exists(ini_fpath):
  227. # input file does not exist
  228. logger.error("%s: File does not exist" % ini_fpath)
  229. # check the working path
  230. if ini_fpath.find('/') != -1 and os.path.dirname(ini_fpath) != os.path.curdir:
  231. wdir = os.path.dirname(ini_fpath)
  232. logger.info('Changing to working dir: %s' % wdir)
  233. os.chdir(wdir)
  234. ini_fname = os.path.basename(ini_fpath)
  235. if options.outfile_path:
  236. out_fpath = options.outfile_path
  237. else:
  238. if ini_fname.endswith('.ini'):
  239. out_fpath = ini_fname[:-3] + 'yaml'
  240. else:
  241. out_fpath = ini_fname + '.yaml'
  242. """Read the input file"""
  243. config = SBConfigParser(include_files = options.include_files)
  244. config.read(ini_fname)
  245. convertor = SBConvertor()
  246. """Dump them to spectacle file"""
  247. dumper = SpectacleDumper(format='yaml', opath = out_fpath)
  248. spec_fpath = dumper.dump(convertor.convert(config.cooked_items()))
  249. logger.info('Yaml file %s created' % out_fpath)
  250. if spec_fpath:
  251. logger.info('Spec file %s was created with extra data' % spec_fpath)
  252. # move old spec-builder files to backup dir
  253. backDir = 'spec-builder.backup'
  254. try:
  255. os.mkdir(backDir)
  256. except:
  257. pass
  258. if os.path.exists(backDir) and not os.path.isfile(backDir):
  259. logger.info('The following used spec-builder files are moved to dir: %s' % backDir)
  260. for file in config.record_used_files:
  261. logger.info('\t' + os.path.basename(file))
  262. shutil.move(file, backDir)