3 # GObject-Introspection - a framework for introspecting GObject libraries
4 # Copyright (C) 2008 Johan Dahlin
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
27 # This only works on unix systems
28 currentdir = os.path.dirname(os.path.abspath(sys.argv[0]))
29 basedir = os.path.abspath(os.path.join(currentdir, '..'))
30 if (os.path.exists(os.path.join(basedir, '.svn')) or
31 os.path.exists(os.path.join(basedir, '.git'))):
40 path = os.path.join(basedir, libdir, 'python%d.%d' % sys.version_info[:2],
42 sys.path.insert(0, path)
44 from giscanner.ast import Include
45 from giscanner.cachestore import CacheStore
46 from giscanner.dumper import compile_introspection_binary
47 from giscanner.glibtransformer import GLibTransformer, IntrospectionBinary
48 from giscanner.minixpath import myxpath, xpath_assert
49 from giscanner.sourcescanner import SourceScanner
50 from giscanner.transformer import Transformer
52 def _get_option_parser():
53 parser = optparse.OptionParser('%prog [options] sources')
54 parser.add_option("", "--format",
55 action="store", dest="format",
57 help="format to use, one of gidl, gir")
58 parser.add_option("-i", "--include",
59 action="append", dest="includes", default=[],
60 help="include types for other gidls")
61 parser.add_option("", "--add-include-path",
62 action="append", dest="include_paths", default=[],
63 help="include paths for other GIR files")
64 parser.add_option("", "--program",
65 action="store", dest="program", default=None,
66 help="program to execute")
67 parser.add_option("", "--program-arg",
68 action="append", dest="program_args", default=[],
69 help="extra arguments to program")
70 parser.add_option("", "--no-libtool",
71 action="store_true", dest="nolibtool", default=False,
73 parser.add_option("-l", "--library",
74 action="append", dest="libraries", default=[],
75 help="libraries of this unit")
76 parser.add_option("-L", "--library-path",
77 action="append", dest="library_paths", default=[],
78 help="directories to search for libraries")
79 parser.add_option("-n", "--namespace",
80 action="store", dest="namespace_name",
81 help=("name of namespace for this unit, also "
82 "used as --strip-prefix default"))
83 parser.add_option("", "--nsversion",
84 action="store", dest="namespace_version",
85 help="version of namespace for this unit")
86 parser.add_option("", "--strip-prefix",
87 action="store", dest="strip_prefix", default=None,
88 help="remove this prefix from objects and functions")
89 parser.add_option("-o", "--output",
90 action="store", dest="output",
91 help="output to writeout, defaults to stdout")
92 parser.add_option("", "--pkg",
93 action="append", dest="packages", default=[],
94 help="pkg-config packages to get cflags from")
95 parser.add_option("-v", "--verbose",
96 action="store_true", dest="verbose",
98 parser.add_option("", "--noclosure",
99 action="store_true", dest="noclosure",
100 help="do not delete unknown types")
101 parser.add_option("", "--typelib-xml",
102 action="store_true", dest="typelib_xml",
103 help="Just convert GIR to typelib XML")
104 parser.add_option("", "--inject",
105 action="store_true", dest="inject",
106 help="Inject additional components into GIR XML")
107 parser.add_option("", "--xpath-assertions",
108 action="store", dest="xpath_assertions",
109 help="Use given file to create assertions on GIR content")
111 group = optparse.OptionGroup(parser, "Preprocessor options")
112 group.add_option("-I", help="Pre-processor include file",
113 action="append", dest="cpp_includes",
115 group.add_option("-D", help="Pre-processor define",
116 action="append", dest="cpp_defines",
118 group.add_option("-U", help="Pre-processor undefine",
119 action="append", dest="cpp_undefines",
121 group.add_option("-p", dest="", help="Ignored")
122 parser.add_option_group(group)
128 raise SystemExit('ERROR: %s' % (msg, ))
130 def typelib_xml_strip(path):
131 from giscanner.girparser import GIRParser
132 from giscanner.girwriter import GIRWriter
133 from giscanner.girparser import C_NS
134 from xml.etree.cElementTree import parse
136 c_ns_key = '{%s}' % (C_NS, )
139 root = tree.getroot()
140 for node in root.getiterator():
141 for attrib in list(node.attrib):
142 if attrib.startswith(c_ns_key):
143 del node.attrib[attrib]
145 parser.parse_tree(tree)
147 writer = GIRWriter(parser.get_namespace(),
148 parser.get_shared_libraries(),
149 parser.get_includes())
150 sys.stdout.write(writer.get_xml())
153 def inject(path, additions, outpath):
154 from giscanner.girparser import GIRParser
155 from giscanner.girwriter import GIRWriter
156 from xml.etree.cElementTree import parse
159 root = tree.getroot()
160 injectDoc = parse(open(additions))
161 for node in injectDoc.getroot():
162 injectPath = node.attrib['path']
163 target = myxpath(root, injectPath)
165 raise ValueError("Couldn't find path %r" % (injectPath, ))
170 parser.parse_tree(tree)
171 writer = GIRWriter(parser.get_namespace(),
172 parser.get_shared_libraries(),
173 parser.get_includes())
174 outf = open(outpath, 'w')
175 outf.write(writer.get_xml())
179 def validate(assertions, path):
180 from xml.etree.cElementTree import parse
181 doc = parse(open(path))
184 assertions_list = f.readlines()
185 print "=== CHECKING %s (%d assertions) ===" % (assertions,
186 len(assertions_list))
187 for assertion in assertions_list:
188 assertion = assertion.strip()
189 xpath_assert(root, assertion)
191 print "=== PASSED %s ===" % (assertions, )
195 parser = _get_option_parser()
196 (options, args) = parser.parse_args(args)
199 _error('Need at least one filename')
201 if options.typelib_xml:
202 return typelib_xml_strip(args[1])
206 _error('Need three filenames; e.g. g-ir-scanner '
207 '--inject Source.gir Additions.xml SourceOut.gir')
208 return inject(*args[1:4])
210 if options.xpath_assertions:
211 return validate(options.xpath_assertions, args[1])
213 if not options.namespace_name:
214 _error('Namespace name missing')
216 if options.format == 'gir':
217 from giscanner.girwriter import GIRWriter as Writer
219 _error("Unknown format: %s" % (options.format, ))
221 if not (options.libraries or options.program):
222 _error("Must specify --program or --library")
223 libraries = options.libraries
225 for package in options.packages:
226 output = subprocess.Popen(['pkg-config', '--cflags', package],
227 stdout=subprocess.PIPE).communicate()[0]
228 # Some pkg-config files on Windows have options we don't understand,
229 # so we explicitly filter to only the ones we need.
230 options_whitelist = ['-I', '-D', '-U', '-l', '-L']
231 def filter_option(opt):
232 for optstart in options_whitelist:
233 if opt.startswith(optstart):
236 output = output.split()
237 filtered_output = filter(filter_option, output)
238 pkg_options, unused = parser.parse_args(filtered_output)
239 options.cpp_includes.extend(pkg_options.cpp_includes)
240 options.cpp_defines.extend(pkg_options.cpp_defines)
241 options.cpp_undefines.extend(pkg_options.cpp_undefines)
243 output = subprocess.Popen(['pkg-config', '--libs-only-L', package],
244 stdout=subprocess.PIPE).communicate()[0]
245 output = output.split()
246 filtered_output = filter(filter_option, output)
247 pkg_options, unused = parser.parse_args(filtered_output)
248 options.library_paths.extend(pkg_options.library_paths)
250 # FIXME: using LPATH is definitely not portable enough. Using Python's
251 # find_library for finding our shared libraries is not a portable enough
252 # anyway as it behaves differently depending on the OS
253 lpath = os.environ.get('LPATH')
254 library_path = ':'.join(options.library_paths)
256 ld_library_path = os.environ.get('LD_LIBRARY_PATH')
258 library_path = ':'.join([ld_library_path, library_path])
261 os.environ['LPATH'] = ':'.join([lpath, library_path])
263 os.environ['LPATH'] = library_path
266 if (arg.endswith('.c') or
268 if not os.path.exists(arg):
269 _error('%s: no such a file or directory' % (arg, ))
270 filenames.append(arg)
272 cachestore = CacheStore()
273 # Run the preprocessor, tokenize and construct simple
274 # objects representing the raw C symbols
276 ss.set_cpp_options(options.cpp_includes,
278 options.cpp_undefines)
279 ss.parse_files(filenames)
280 ss.parse_macros(filenames)
282 # Transform the C symbols into AST nodes
283 transformer = Transformer(cachestore, ss,
284 options.namespace_name,
285 options.namespace_version)
286 if options.strip_prefix:
287 transformer.set_strip_prefix(options.strip_prefix)
289 transformer.set_strip_prefix(options.namespace_name)
290 transformer.set_include_paths(options.include_paths)
291 shown_include_warning = False
292 for include in options.includes:
293 if os.sep in include:
294 raise ValueError("Invalid include path %r" % (include, ))
295 include_obj = Include.from_string(include)
296 transformer.register_include(include_obj)
299 args=[options.program]
300 args.extend(options.program_args)
301 binary = IntrospectionBinary(args)
303 binary = compile_introspection_binary(options)
305 # Transform the C AST nodes into higher level
307 glibtransformer = GLibTransformer(transformer,
308 noclosure=options.noclosure,
309 nolibtool=options.nolibtool)
310 glibtransformer.set_introspection_binary(binary)
312 namespace = glibtransformer.parse()
315 writer = Writer(namespace, libraries, transformer.get_includes())
316 data = writer.get_xml()
318 fd = open(options.output, "w")
325 sys.exit(main(sys.argv))