glib2.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. #! /usr/bin/env python
  2. # encoding: utf-8
  3. # Thomas Nagy, 2006-2018 (ita)
  4. """
  5. Support for GLib2 tools:
  6. * marshal
  7. * enums
  8. * gsettings
  9. * gresource
  10. """
  11. import os
  12. import functools
  13. from waflib import Context, Task, Utils, Options, Errors, Logs
  14. from waflib.TaskGen import taskgen_method, before_method, feature, extension
  15. from waflib.Configure import conf
  16. ################## marshal files
  17. @taskgen_method
  18. def add_marshal_file(self, filename, prefix):
  19. """
  20. Adds a file to the list of marshal files to process. Store them in the attribute *marshal_list*.
  21. :param filename: xml file to compile
  22. :type filename: string
  23. :param prefix: marshal prefix (--prefix=prefix)
  24. :type prefix: string
  25. """
  26. if not hasattr(self, 'marshal_list'):
  27. self.marshal_list = []
  28. self.meths.append('process_marshal')
  29. self.marshal_list.append((filename, prefix))
  30. @before_method('process_source')
  31. def process_marshal(self):
  32. """
  33. Processes the marshal files stored in the attribute *marshal_list* to create :py:class:`waflib.Tools.glib2.glib_genmarshal` instances.
  34. Adds the c file created to the list of source to process.
  35. """
  36. for f, prefix in getattr(self, 'marshal_list', []):
  37. node = self.path.find_resource(f)
  38. if not node:
  39. raise Errors.WafError('file not found %r' % f)
  40. h_node = node.change_ext('.h')
  41. c_node = node.change_ext('.c')
  42. task = self.create_task('glib_genmarshal', node, [h_node, c_node])
  43. task.env.GLIB_GENMARSHAL_PREFIX = prefix
  44. self.source = self.to_nodes(getattr(self, 'source', []))
  45. self.source.append(c_node)
  46. class glib_genmarshal(Task.Task):
  47. vars = ['GLIB_GENMARSHAL_PREFIX', 'GLIB_GENMARSHAL']
  48. color = 'BLUE'
  49. ext_out = ['.h']
  50. def run(self):
  51. bld = self.generator.bld
  52. get = self.env.get_flat
  53. cmd1 = "%s %s --prefix=%s --header > %s" % (
  54. get('GLIB_GENMARSHAL'),
  55. self.inputs[0].srcpath(),
  56. get('GLIB_GENMARSHAL_PREFIX'),
  57. self.outputs[0].abspath()
  58. )
  59. ret = bld.exec_command(cmd1)
  60. if ret:
  61. return ret
  62. #print self.outputs[1].abspath()
  63. c = '''#include "%s"\n''' % self.outputs[0].name
  64. self.outputs[1].write(c)
  65. cmd2 = "%s %s --prefix=%s --body >> %s" % (
  66. get('GLIB_GENMARSHAL'),
  67. self.inputs[0].srcpath(),
  68. get('GLIB_GENMARSHAL_PREFIX'),
  69. self.outputs[1].abspath()
  70. )
  71. return bld.exec_command(cmd2)
  72. ########################## glib-mkenums
  73. @taskgen_method
  74. def add_enums_from_template(self, source='', target='', template='', comments=''):
  75. """
  76. Adds a file to the list of enum files to process. Stores them in the attribute *enums_list*.
  77. :param source: enum file to process
  78. :type source: string
  79. :param target: target file
  80. :type target: string
  81. :param template: template file
  82. :type template: string
  83. :param comments: comments
  84. :type comments: string
  85. """
  86. if not hasattr(self, 'enums_list'):
  87. self.enums_list = []
  88. self.meths.append('process_enums')
  89. self.enums_list.append({'source': source,
  90. 'target': target,
  91. 'template': template,
  92. 'file-head': '',
  93. 'file-prod': '',
  94. 'file-tail': '',
  95. 'enum-prod': '',
  96. 'value-head': '',
  97. 'value-prod': '',
  98. 'value-tail': '',
  99. 'comments': comments})
  100. @taskgen_method
  101. def add_enums(self, source='', target='',
  102. file_head='', file_prod='', file_tail='', enum_prod='',
  103. value_head='', value_prod='', value_tail='', comments=''):
  104. """
  105. Adds a file to the list of enum files to process. Stores them in the attribute *enums_list*.
  106. :param source: enum file to process
  107. :type source: string
  108. :param target: target file
  109. :type target: string
  110. :param file_head: unused
  111. :param file_prod: unused
  112. :param file_tail: unused
  113. :param enum_prod: unused
  114. :param value_head: unused
  115. :param value_prod: unused
  116. :param value_tail: unused
  117. :param comments: comments
  118. :type comments: string
  119. """
  120. if not hasattr(self, 'enums_list'):
  121. self.enums_list = []
  122. self.meths.append('process_enums')
  123. self.enums_list.append({'source': source,
  124. 'template': '',
  125. 'target': target,
  126. 'file-head': file_head,
  127. 'file-prod': file_prod,
  128. 'file-tail': file_tail,
  129. 'enum-prod': enum_prod,
  130. 'value-head': value_head,
  131. 'value-prod': value_prod,
  132. 'value-tail': value_tail,
  133. 'comments': comments})
  134. @before_method('process_source')
  135. def process_enums(self):
  136. """
  137. Processes the enum files stored in the attribute *enum_list* to create :py:class:`waflib.Tools.glib2.glib_mkenums` instances.
  138. """
  139. for enum in getattr(self, 'enums_list', []):
  140. task = self.create_task('glib_mkenums')
  141. env = task.env
  142. inputs = []
  143. # process the source
  144. source_list = self.to_list(enum['source'])
  145. if not source_list:
  146. raise Errors.WafError('missing source ' + str(enum))
  147. source_list = [self.path.find_resource(k) for k in source_list]
  148. inputs += source_list
  149. env.GLIB_MKENUMS_SOURCE = [k.abspath() for k in source_list]
  150. # find the target
  151. if not enum['target']:
  152. raise Errors.WafError('missing target ' + str(enum))
  153. tgt_node = self.path.find_or_declare(enum['target'])
  154. if tgt_node.name.endswith('.c'):
  155. self.source.append(tgt_node)
  156. env.GLIB_MKENUMS_TARGET = tgt_node.abspath()
  157. options = []
  158. if enum['template']: # template, if provided
  159. template_node = self.path.find_resource(enum['template'])
  160. options.append('--template %s' % (template_node.abspath()))
  161. inputs.append(template_node)
  162. params = {'file-head' : '--fhead',
  163. 'file-prod' : '--fprod',
  164. 'file-tail' : '--ftail',
  165. 'enum-prod' : '--eprod',
  166. 'value-head' : '--vhead',
  167. 'value-prod' : '--vprod',
  168. 'value-tail' : '--vtail',
  169. 'comments': '--comments'}
  170. for param, option in params.items():
  171. if enum[param]:
  172. options.append('%s %r' % (option, enum[param]))
  173. env.GLIB_MKENUMS_OPTIONS = ' '.join(options)
  174. # update the task instance
  175. task.set_inputs(inputs)
  176. task.set_outputs(tgt_node)
  177. class glib_mkenums(Task.Task):
  178. """
  179. Processes enum files
  180. """
  181. run_str = '${GLIB_MKENUMS} ${GLIB_MKENUMS_OPTIONS} ${GLIB_MKENUMS_SOURCE} > ${GLIB_MKENUMS_TARGET}'
  182. color = 'PINK'
  183. ext_out = ['.h']
  184. ######################################### gsettings
  185. @taskgen_method
  186. def add_settings_schemas(self, filename_list):
  187. """
  188. Adds settings files to process to *settings_schema_files*
  189. :param filename_list: files
  190. :type filename_list: list of string
  191. """
  192. if not hasattr(self, 'settings_schema_files'):
  193. self.settings_schema_files = []
  194. if not isinstance(filename_list, list):
  195. filename_list = [filename_list]
  196. self.settings_schema_files.extend(filename_list)
  197. @taskgen_method
  198. def add_settings_enums(self, namespace, filename_list):
  199. """
  200. Called only once by task generator to set the enums namespace.
  201. :param namespace: namespace
  202. :type namespace: string
  203. :param filename_list: enum files to process
  204. :type filename_list: file list
  205. """
  206. if hasattr(self, 'settings_enum_namespace'):
  207. raise Errors.WafError("Tried to add gsettings enums to %r more than once" % self.name)
  208. self.settings_enum_namespace = namespace
  209. if not isinstance(filename_list, list):
  210. filename_list = [filename_list]
  211. self.settings_enum_files = filename_list
  212. @feature('glib2')
  213. def process_settings(self):
  214. """
  215. Processes the schema files in *settings_schema_files* to create :py:class:`waflib.Tools.glib2.glib_mkenums` instances. The
  216. same files are validated through :py:class:`waflib.Tools.glib2.glib_validate_schema` tasks.
  217. """
  218. enums_tgt_node = []
  219. install_files = []
  220. settings_schema_files = getattr(self, 'settings_schema_files', [])
  221. if settings_schema_files and not self.env.GLIB_COMPILE_SCHEMAS:
  222. raise Errors.WafError ("Unable to process GSettings schemas - glib-compile-schemas was not found during configure")
  223. # 1. process gsettings_enum_files (generate .enums.xml)
  224. #
  225. if hasattr(self, 'settings_enum_files'):
  226. enums_task = self.create_task('glib_mkenums')
  227. source_list = self.settings_enum_files
  228. source_list = [self.path.find_resource(k) for k in source_list]
  229. enums_task.set_inputs(source_list)
  230. enums_task.env.GLIB_MKENUMS_SOURCE = [k.abspath() for k in source_list]
  231. target = self.settings_enum_namespace + '.enums.xml'
  232. tgt_node = self.path.find_or_declare(target)
  233. enums_task.set_outputs(tgt_node)
  234. enums_task.env.GLIB_MKENUMS_TARGET = tgt_node.abspath()
  235. enums_tgt_node = [tgt_node]
  236. install_files.append(tgt_node)
  237. options = '--comments "<!-- @comment@ -->" --fhead "<schemalist>" --vhead " <@type@ id=\\"%s.@EnumName@\\">" --vprod " <value nick=\\"@valuenick@\\" value=\\"@valuenum@\\"/>" --vtail " </@type@>" --ftail "</schemalist>" ' % (self.settings_enum_namespace)
  238. enums_task.env.GLIB_MKENUMS_OPTIONS = options
  239. # 2. process gsettings_schema_files (validate .gschema.xml files)
  240. #
  241. for schema in settings_schema_files:
  242. schema_task = self.create_task ('glib_validate_schema')
  243. schema_node = self.path.find_resource(schema)
  244. if not schema_node:
  245. raise Errors.WafError("Cannot find the schema file %r" % schema)
  246. install_files.append(schema_node)
  247. source_list = enums_tgt_node + [schema_node]
  248. schema_task.set_inputs (source_list)
  249. schema_task.env.GLIB_COMPILE_SCHEMAS_OPTIONS = [("--schema-file=" + k.abspath()) for k in source_list]
  250. target_node = schema_node.change_ext('.xml.valid')
  251. schema_task.set_outputs (target_node)
  252. schema_task.env.GLIB_VALIDATE_SCHEMA_OUTPUT = target_node.abspath()
  253. # 3. schemas install task
  254. def compile_schemas_callback(bld):
  255. if not bld.is_install:
  256. return
  257. compile_schemas = Utils.to_list(bld.env.GLIB_COMPILE_SCHEMAS)
  258. destdir = Options.options.destdir
  259. paths = bld._compile_schemas_registered
  260. if destdir:
  261. paths = (os.path.join(destdir, path.lstrip(os.sep)) for path in paths)
  262. for path in paths:
  263. Logs.pprint('YELLOW', 'Updating GSettings schema cache %r' % path)
  264. if self.bld.exec_command(compile_schemas + [path]):
  265. Logs.warn('Could not update GSettings schema cache %r' % path)
  266. if self.bld.is_install:
  267. schemadir = self.env.GSETTINGSSCHEMADIR
  268. if not schemadir:
  269. raise Errors.WafError ('GSETTINGSSCHEMADIR not defined (should have been set up automatically during configure)')
  270. if install_files:
  271. self.add_install_files(install_to=schemadir, install_from=install_files)
  272. registered_schemas = getattr(self.bld, '_compile_schemas_registered', None)
  273. if not registered_schemas:
  274. registered_schemas = self.bld._compile_schemas_registered = set()
  275. self.bld.add_post_fun(compile_schemas_callback)
  276. registered_schemas.add(schemadir)
  277. class glib_validate_schema(Task.Task):
  278. """
  279. Validates schema files
  280. """
  281. run_str = 'rm -f ${GLIB_VALIDATE_SCHEMA_OUTPUT} && ${GLIB_COMPILE_SCHEMAS} --dry-run ${GLIB_COMPILE_SCHEMAS_OPTIONS} && touch ${GLIB_VALIDATE_SCHEMA_OUTPUT}'
  282. color = 'PINK'
  283. ################## gresource
  284. @extension('.gresource.xml')
  285. def process_gresource_source(self, node):
  286. """
  287. Creates tasks that turn ``.gresource.xml`` files to C code
  288. """
  289. if not self.env.GLIB_COMPILE_RESOURCES:
  290. raise Errors.WafError ("Unable to process GResource file - glib-compile-resources was not found during configure")
  291. if 'gresource' in self.features:
  292. return
  293. h_node = node.change_ext('_xml.h')
  294. c_node = node.change_ext('_xml.c')
  295. self.create_task('glib_gresource_source', node, [h_node, c_node])
  296. self.source.append(c_node)
  297. @feature('gresource')
  298. def process_gresource_bundle(self):
  299. """
  300. Creates tasks to turn ``.gresource`` files from ``.gresource.xml`` files::
  301. def build(bld):
  302. bld(
  303. features='gresource',
  304. source=['resources1.gresource.xml', 'resources2.gresource.xml'],
  305. install_path='${LIBDIR}/${PACKAGE}'
  306. )
  307. :param source: XML files to process
  308. :type source: list of string
  309. :param install_path: installation path
  310. :type install_path: string
  311. """
  312. for i in self.to_list(self.source):
  313. node = self.path.find_resource(i)
  314. task = self.create_task('glib_gresource_bundle', node, node.change_ext(''))
  315. inst_to = getattr(self, 'install_path', None)
  316. if inst_to:
  317. self.add_install_files(install_to=inst_to, install_from=task.outputs)
  318. class glib_gresource_base(Task.Task):
  319. """
  320. Base class for gresource based tasks
  321. """
  322. color = 'BLUE'
  323. base_cmd = '${GLIB_COMPILE_RESOURCES} --sourcedir=${SRC[0].parent.srcpath()} --sourcedir=${SRC[0].bld_dir()}'
  324. def scan(self):
  325. """
  326. Scans gresource dependencies through ``glib-compile-resources --generate-dependencies command``
  327. """
  328. bld = self.generator.bld
  329. kw = {}
  330. kw['cwd'] = self.get_cwd()
  331. kw['quiet'] = Context.BOTH
  332. cmd = Utils.subst_vars('${GLIB_COMPILE_RESOURCES} --sourcedir=%s --sourcedir=%s --generate-dependencies %s' % (
  333. self.inputs[0].parent.srcpath(),
  334. self.inputs[0].bld_dir(),
  335. self.inputs[0].bldpath()
  336. ), self.env)
  337. output = bld.cmd_and_log(cmd, **kw)
  338. nodes = []
  339. names = []
  340. for dep in output.splitlines():
  341. if dep:
  342. node = bld.bldnode.find_node(dep)
  343. if node:
  344. nodes.append(node)
  345. else:
  346. names.append(dep)
  347. return (nodes, names)
  348. class glib_gresource_source(glib_gresource_base):
  349. """
  350. Task to generate C source code (.h and .c files) from a gresource.xml file
  351. """
  352. vars = ['GLIB_COMPILE_RESOURCES']
  353. fun_h = Task.compile_fun_shell(glib_gresource_base.base_cmd + ' --target=${TGT[0].abspath()} --generate-header ${SRC}')
  354. fun_c = Task.compile_fun_shell(glib_gresource_base.base_cmd + ' --target=${TGT[1].abspath()} --generate-source ${SRC}')
  355. ext_out = ['.h']
  356. def run(self):
  357. return self.fun_h[0](self) or self.fun_c[0](self)
  358. class glib_gresource_bundle(glib_gresource_base):
  359. """
  360. Task to generate a .gresource binary file from a gresource.xml file
  361. """
  362. run_str = glib_gresource_base.base_cmd + ' --target=${TGT} ${SRC}'
  363. shell = True # temporary workaround for #795
  364. @conf
  365. def find_glib_genmarshal(conf):
  366. conf.find_program('glib-genmarshal', var='GLIB_GENMARSHAL')
  367. @conf
  368. def find_glib_mkenums(conf):
  369. if not conf.env.PERL:
  370. conf.find_program('perl', var='PERL')
  371. conf.find_program('glib-mkenums', interpreter='PERL', var='GLIB_MKENUMS')
  372. @conf
  373. def find_glib_compile_schemas(conf):
  374. # when cross-compiling, gsettings.m4 locates the program with the following:
  375. # pkg-config --variable glib_compile_schemas gio-2.0
  376. conf.find_program('glib-compile-schemas', var='GLIB_COMPILE_SCHEMAS')
  377. def getstr(varname):
  378. return getattr(Options.options, varname, getattr(conf.env,varname, ''))
  379. gsettingsschemadir = getstr('GSETTINGSSCHEMADIR')
  380. if not gsettingsschemadir:
  381. datadir = getstr('DATADIR')
  382. if not datadir:
  383. prefix = conf.env.PREFIX
  384. datadir = os.path.join(prefix, 'share')
  385. gsettingsschemadir = os.path.join(datadir, 'glib-2.0', 'schemas')
  386. conf.env.GSETTINGSSCHEMADIR = gsettingsschemadir
  387. @conf
  388. def find_glib_compile_resources(conf):
  389. conf.find_program('glib-compile-resources', var='GLIB_COMPILE_RESOURCES')
  390. def configure(conf):
  391. """
  392. Finds the following programs:
  393. * *glib-genmarshal* and set *GLIB_GENMARSHAL*
  394. * *glib-mkenums* and set *GLIB_MKENUMS*
  395. * *glib-compile-schemas* and set *GLIB_COMPILE_SCHEMAS* (not mandatory)
  396. * *glib-compile-resources* and set *GLIB_COMPILE_RESOURCES* (not mandatory)
  397. """
  398. conf.find_glib_genmarshal()
  399. conf.find_glib_mkenums()
  400. conf.find_glib_compile_schemas(mandatory=False)
  401. conf.find_glib_compile_resources(mandatory=False)
  402. def options(opt):
  403. """
  404. Adds the ``--gsettingsschemadir`` command-line option
  405. """
  406. gr = opt.add_option_group('Installation directories')
  407. gr.add_option('--gsettingsschemadir', help='GSettings schema location [DATADIR/glib-2.0/schemas]', default='', dest='GSETTINGSSCHEMADIR')