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'))):
34 path = os.path.join(basedir, 'lib', 'python%d.%d' % sys.version_info[:2],
36 sys.path.insert(0, path)
38 from giscanner.glibtransformer import GLibTransformer
39 from giscanner.sourcescanner import SourceScanner
40 from giscanner.transformer import Transformer
41 from giscanner.ast import Include
42 from giscanner.minixpath import myxpath, xpath_assert
45 def _get_option_parser():
46 parser = optparse.OptionParser('%prog [options] sources')
47 parser.add_option("", "--format",
48 action="store", dest="format",
50 help="format to use, one of gidl, gir")
51 parser.add_option("-i", "--include",
52 action="append", dest="includes", default=[],
53 help="include types for other gidls")
54 parser.add_option("--add-include-path",
55 action="append", dest="include_paths", default=[],
56 help="include paths for other GIR files")
57 parser.add_option("-l", "--library",
58 action="append", dest="libraries", default=[],
59 help="libraries of this unit")
60 parser.add_option("-L", "--library-path",
61 action="append", dest="library_paths", default=[],
62 help="directories to search for libraries")
63 parser.add_option("-n", "--namespace",
64 action="store", dest="namespace_name",
65 help="name of namespace for this unit, also used as --strip-prefix default")
66 parser.add_option("", "--nsversion",
67 action="store", dest="namespace_version",
68 help="version of namespace for this unit")
69 parser.add_option("", "--strip-prefix",
70 action="store", dest="strip_prefix", default=None,
71 help="remove this prefix from objects and functions")
72 parser.add_option("-o", "--output",
73 action="store", dest="output",
74 help="output to writeout, defaults to stdout")
75 parser.add_option("", "--pkg",
76 action="append", dest="packages", default=[],
77 help="pkg-config packages to get cflags from")
78 parser.add_option("-v", "--verbose",
79 action="store_true", dest="verbose",
81 parser.add_option("", "--noclosure",
82 action="store_true", dest="noclosure",
83 help="do not delete unknown types")
84 parser.add_option("", "--typelib-xml",
85 action="store_true", dest="typelib_xml",
86 help="Just convert GIR to typelib XML")
87 parser.add_option("", "--inject",
88 action="store_true", dest="inject",
89 help="Inject additional components into GIR XML")
90 parser.add_option("", "--xpath-assertions",
91 action="store", dest="xpath_assertions",
92 help="Use given file to create assertions on GIR content")
94 group = optparse.OptionGroup(parser, "Preprocessor options")
95 group.add_option("-I", help="Pre-processor include file",
96 action="append", dest="cpp_includes",
98 group.add_option("-D", help="Pre-processor define",
99 action="append", dest="cpp_defines",
101 group.add_option("-U", help="Pre-processor undefine",
102 action="append", dest="cpp_undefines",
104 group.add_option("-p", dest="", help="Ignored")
105 parser.add_option_group(group)
111 raise SystemExit('ERROR: %s' % (msg, ))
113 def typelib_xml_strip(path):
114 from giscanner.girparser import GIRParser
115 from giscanner.girwriter import GIRWriter
116 from giscanner.girparser import C_NS
117 c_ns_key = '{%s}' % (C_NS, )
119 parser = GIRParser(path, initial_parse=False)
120 doc = parser.get_doc()
121 for node in doc.getiterator():
122 for attrib in list(node.attrib):
123 if attrib.startswith(c_ns_key):
124 del node.attrib[attrib]
126 writer = GIRWriter(parser.get_namespace(),
127 parser.get_shared_libraries(),
128 parser.get_includes())
129 sys.stdout.write(writer.get_xml())
132 def inject(path, additions, outpath):
133 from giscanner.girparser import GIRParser
134 from giscanner.girwriter import GIRWriter
135 from xml.etree.cElementTree import parse
137 parser = GIRParser(path, initial_parse=False)
138 root = parser.get_doc().getroot()
139 injectDoc = parse(open(additions))
140 for node in injectDoc.getroot():
141 injectPath = node.attrib['path']
142 target = myxpath(root, injectPath)
144 raise ValueError("Couldn't find path %r" % (injectPath, ))
148 writer = GIRWriter(parser.get_namespace(),
149 parser.get_shared_libraries(),
150 parser.get_includes())
151 outf = open(outpath, 'w')
152 outf.write(writer.get_xml())
156 def validate(assertions, path):
157 from xml.etree.cElementTree import parse
158 doc = parse(open(path))
161 assertions_list = f.readlines()
162 print "=== CHECKING %s (%d assertions) ===" % (assertions,
163 len(assertions_list))
164 for assertion in assertions_list:
165 assertion = assertion.strip()
166 xpath_assert(root, assertion)
168 print "=== PASSED %s ===" % (assertions, )
172 parser = _get_option_parser()
173 (options, args) = parser.parse_args(args)
176 _error('Need at least one filename')
178 if options.typelib_xml:
179 return typelib_xml_strip(args[1])
183 _error('Need three filenames; e.g. g-ir-scanner --inject Source.gir Additions.xml SourceOut.gir')
184 return inject(*args[1:4])
186 if options.xpath_assertions:
187 return validate(options.xpath_assertions, args[1])
189 if not options.namespace_name:
190 _error('Namespace name missing')
192 if options.format == 'gir':
193 from giscanner.girwriter import GIRWriter as Writer
195 _error("Unknown format: %s" % (options.format, ))
197 if not options.libraries:
198 _error("Must specify --library at least one primary library")
199 libraries = options.libraries
201 for package in options.packages:
202 output = subprocess.Popen(['pkg-config', '--cflags', package],
203 stdout=subprocess.PIPE).communicate()[0]
204 # Some pkg-config files on Windows have options we don't understand,
205 # so we explicitly filter to only the ones we need.
206 options_whitelist = ['-I', '-D', '-U', '-l', '-L']
207 def filter_option(opt):
208 for optstart in options_whitelist:
209 if opt.startswith(optstart):
212 output = output.split()
213 filtered_output = filter(filter_option, output)
214 pkg_options, unused = parser.parse_args(filtered_output)
215 options.cpp_includes.extend(pkg_options.cpp_includes)
216 options.cpp_defines.extend(pkg_options.cpp_defines)
217 options.cpp_undefines.extend(pkg_options.cpp_undefines)
219 output = subprocess.Popen(['pkg-config', '--libs-only-L', package],
220 stdout=subprocess.PIPE).communicate()[0]
221 output = output.split()
222 filtered_output = filter(filter_option, output)
223 pkg_options, unused = parser.parse_args(filtered_output)
224 options.library_paths.extend(pkg_options.library_paths)
226 # FIXME: using LPATH is definitely not portable enough. Using Python's
227 # find_library for finding our shared libraries is not a portable enough
228 # anyway as it behaves differently depending on the OS
229 lpath = os.environ.get('LPATH')
230 library_path = ':'.join(options.library_paths)
232 os.environ['LPATH'] = ':'.join([lpath, library_path])
234 os.environ['LPATH'] = library_path
238 if (arg.endswith('.c') or
240 if not os.path.exists(arg):
241 _error('%s: no such a file or directory' % (arg, ))
242 filenames.append(arg)
244 # Run the preprocessor, tokenize and construct simple
245 # objects representing the raw C symbols
247 ss.set_cpp_options(options.cpp_includes,
249 options.cpp_undefines)
250 ss.parse_files(filenames)
251 ss.parse_macros(filenames)
253 # Transform the C symbols into AST nodes
254 transformer = Transformer(ss, options.namespace_name, options.namespace_version)
255 if options.strip_prefix:
256 transformer.set_strip_prefix(options.strip_prefix)
258 transformer.set_strip_prefix(options.namespace_name)
259 transformer.set_include_paths(options.include_paths)
260 shown_include_warning = False
261 for include in options.includes:
262 if os.sep in include:
263 raise ValueError("Invalid include path %r" % (include, ))
264 include_obj = Include.from_string(include)
265 transformer.register_include(include_obj)
267 # Transform the C AST nodes into higher level
269 glibtransformer = GLibTransformer(transformer, noclosure=options.noclosure)
270 if options.libraries:
271 for library in options.libraries:
272 glibtransformer.add_library(library)
274 namespace = glibtransformer.parse()
277 writer = Writer(namespace, libraries, transformer.get_includes())
278 data = writer.get_xml()
280 fd = open(options.output, "w")
287 sys.exit(main(sys.argv))