[gobject-introspection] scanner: Add --identifier-filter-cmd



commit a882381f83f0acc6aaf7bfa03e1faa1c41a7ba00
Author: Simon Feltman <sfeltman src gnome org>
Date:   Sun Dec 29 05:29:24 2013 -0800

    scanner: Add --identifier-filter-cmd
    
    Add the command line flag --identifier-filter-cmd to g-ir-scanner which
    allows running identifier names through a filtering shell command. The
    identifier is sent as stdin to the filter command and expects a filtered
    result written to stdout.
    
    https://bugzilla.gnome.org/show_bug.cgi?706898

 giscanner/scannermain.py                   |    8 ++++-
 giscanner/transformer.py                   |   15 ++++++++-
 tests/scanner/Identfilter-1.0-expected.gir |   46 +++++++++++++++++++++++++
 tests/scanner/Makefile.am                  |   16 ++++++++-
 tests/scanner/identfilter.h                |   11 ++++++
 tests/scanner/identfilter.py               |   50 ++++++++++++++++++++++++++++
 tests/scanner/test_transformer.py          |   30 ++++++++++++++++
 7 files changed, 173 insertions(+), 3 deletions(-)
---
diff --git a/giscanner/scannermain.py b/giscanner/scannermain.py
index 98d5687..ac34030 100755
--- a/giscanner/scannermain.py
+++ b/giscanner/scannermain.py
@@ -156,6 +156,11 @@ and --symbol-prefix.""")
                       help="""Remove this prefix from C identifiers (structure typedefs, etc.).
 May be specified multiple times.  This is also used as the default for --symbol-prefix if
 the latter is not specified.""")
+    parser.add_option("", "--identifier-filter-cmd",
+                      action="store", dest="identifier_filter_cmd", default='',
+                      help='Filter identifiers (struct and union typedefs) through the given '
+                           'shell command which will receive the identifier name as input '
+                           'to stdin and is expected to output the filtered results to stdout.')
     parser.add_option("", "--symbol-prefix",
                       action="append", dest="symbol_prefixes", default=[],
                       help="Remove this prefix from C symbols (function names)")
@@ -334,7 +339,8 @@ see --identifier-prefix and --symbol-prefix."""
 
 def create_transformer(namespace, options):
     transformer = Transformer(namespace,
-                              accept_unprefixed=options.accept_unprefixed)
+                              accept_unprefixed=options.accept_unprefixed,
+                              identifier_filter_cmd=options.identifier_filter_cmd)
     transformer.set_include_paths(options.include_paths)
     if options.passthrough_gir:
         transformer.disable_cache()
diff --git a/giscanner/transformer.py b/giscanner/transformer.py
index 80265dd..8c5e908 100644
--- a/giscanner/transformer.py
+++ b/giscanner/transformer.py
@@ -20,6 +20,7 @@
 
 import os
 import sys
+import subprocess
 
 from . import ast
 from . import message
@@ -49,7 +50,7 @@ if os.name != 'nt':
 class Transformer(object):
     namespace = property(lambda self: self._namespace)
 
-    def __init__(self, namespace, accept_unprefixed=False):
+    def __init__(self, namespace, accept_unprefixed=False, identifier_filter_cmd=''):
         self._cachestore = CacheStore()
         self._accept_unprefixed = accept_unprefixed
         self._namespace = namespace
@@ -58,6 +59,7 @@ class Transformer(object):
         self._parsed_includes = {}  # <string namespace -> Namespace>
         self._includepaths = []
         self._passthrough_mode = False
+        self._identifier_filter_cmd = identifier_filter_cmd
 
         # Cache a list of struct/unions in C's "tag namespace". This helps
         # manage various orderings of typedefs and structs. See:
@@ -293,6 +295,17 @@ raise ValueError."""
         return matches[-1]
 
     def strip_identifier(self, ident):
+        if self._identifier_filter_cmd:
+            proc = subprocess.Popen(self._identifier_filter_cmd,
+                                    stdin=subprocess.PIPE,
+                                    stdout=subprocess.PIPE,
+                                    stderr=subprocess.PIPE,
+                                    shell=True)
+            ident, err = proc.communicate(ident)
+            if proc.returncode:
+                raise ValueError('filter: "%s" exited: %d with error: %s' %
+                                 (self._identifier_filter_cmd, proc.returncode, err))
+
         hidden = ident.startswith('_')
         if hidden:
             ident = ident[1:]
diff --git a/tests/scanner/Identfilter-1.0-expected.gir b/tests/scanner/Identfilter-1.0-expected.gir
new file mode 100644
index 0000000..5c4590e
--- /dev/null
+++ b/tests/scanner/Identfilter-1.0-expected.gir
@@ -0,0 +1,46 @@
+<?xml version="1.0"?>
+<!-- This file was automatically generated from C sources - DO NOT EDIT!
+To affect the contents of this file, edit the original C definitions,
+and/or use gtk-doc annotations.  -->
+<repository version="1.2"
+            xmlns="http://www.gtk.org/introspection/core/1.0";
+            xmlns:c="http://www.gtk.org/introspection/c/1.0";
+            xmlns:glib="http://www.gtk.org/introspection/glib/1.0";>
+  <namespace name="Identfilter"
+             version="1.0"
+             shared-library=""
+             c:identifier-prefixes="Identfilter"
+             c:symbol-prefixes="identfilter">
+    <record name="Context" c:type="identfilter_t" disguised="1">
+    </record>
+    <record name="Object" c:type="identfilter_object_t" disguised="1">
+      <method name="foo_method" c:identifier="identfilter_object_foo_method">
+        <return-value transfer-ownership="none">
+          <type name="none" c:type="void"/>
+        </return-value>
+        <parameters>
+          <instance-parameter name="self" transfer-ownership="none">
+            <type name="Object" c:type="identfilter_object_t*"/>
+          </instance-parameter>
+        </parameters>
+      </method>
+      <method name="free" c:identifier="identfilter_object_free">
+        <return-value transfer-ownership="none">
+          <type name="none" c:type="void"/>
+        </return-value>
+        <parameters>
+          <instance-parameter name="self" transfer-ownership="none">
+            <type name="Object" c:type="identfilter_object_t*"/>
+          </instance-parameter>
+        </parameters>
+      </method>
+      <function name="new"
+                c:identifier="identfilter_object_new"
+                introspectable="0">
+        <return-value>
+          <type name="Object" c:type="identfilter_object_t*"/>
+        </return-value>
+      </function>
+    </record>
+  </namespace>
+</repository>
diff --git a/tests/scanner/Makefile.am b/tests/scanner/Makefile.am
index f697b4b..abdaed8 100644
--- a/tests/scanner/Makefile.am
+++ b/tests/scanner/Makefile.am
@@ -161,10 +161,24 @@ EXTRA_DIST += \
        headeronly.h \
        Headeronly-1.0-expected.gir
 CLEANFILES += Headeronly-1.0.gir
+CHECKGIRS += Headeronly-1.0.gir
 
 Headeronly-1.0.gir: headeronly.h
        $(AM_V_GEN) $(INTROSPECTION_SCANNER) $(INTROSPECTION_SCANNER_ARGS) --warn-all --warn-error 
--reparse-validate --namespace=Headeronly --nsversion=1.0 --header-only --output=$@ $<
 
+EXTRA_DIST += \
+       identfilter.h \
+       Identifilter-1.0-expected.gir
+CLEANFILES += Identfilter-1.0.gir
+CHECKGIRS += Identfilter-1.0.gir
+
+Identfilter-1.0.gir: identfilter.h
+       $(AM_V_GEN) $(INTROSPECTION_SCANNER) $(INTROSPECTION_SCANNER_ARGS) \
+               --warn-all --reparse-validate \
+               --namespace=Identfilter --accept-unprefixed --nsversion=1.0 --header-only \
+               --identifier-filter-cmd="$(PYTHON) $(srcdir)/identfilter.py" \
+               --output=$@ $<
+
 if BUILD_DOCTOOL
 DOCGIRS = Regress-1.0.gir
 CHECKDOCS = $(DOCGIRS:.gir=-C) $(DOCGIRS:.gir=-Python) $(DOCGIRS:.gir=-Gjs) $(DOCGIRS:.gir=-sections.txt)
@@ -196,7 +210,7 @@ PYTESTS = \
        test_sourcescanner.py \
        test_transformer.py
 
-TESTS = Headeronly-1.0.gir $(CHECKGIRS) $(CHECKDOCS) $(TYPELIBS) $(PYTESTS)
+TESTS = $(CHECKGIRS) $(CHECKDOCS) $(TYPELIBS) $(PYTESTS)
 TESTS_ENVIRONMENT = srcdir=$(srcdir) top_srcdir=$(top_srcdir) builddir=$(builddir) 
top_builddir=$(top_builddir) \
        PYTHON=$(PYTHON) UNINSTALLED_INTROSPECTION_SRCDIR=$(top_srcdir)
 LOG_COMPILER = $(top_srcdir)/tests/gi-tester
diff --git a/tests/scanner/identfilter.h b/tests/scanner/identfilter.h
new file mode 100644
index 0000000..43ad509
--- /dev/null
+++ b/tests/scanner/identfilter.h
@@ -0,0 +1,11 @@
+#ifndef __IDENTFILTER_H__
+#define __IDENTFILTER_H__
+
+typedef struct _identfilter identfilter_t;
+typedef struct _identfilter_object identfilter_object_t;
+
+identfilter_object_t *identfilter_object_new (void);
+void identfilter_object_foo_method (identfilter_object_t *self);
+void identfilter_object_free (identfilter_object_t *self);
+
+#endif
diff --git a/tests/scanner/identfilter.py b/tests/scanner/identfilter.py
new file mode 100644
index 0000000..1138112
--- /dev/null
+++ b/tests/scanner/identfilter.py
@@ -0,0 +1,50 @@
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2014 Simon Feltman <sfeltman gnome org>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+# USA
+
+"""
+Script which reads identifiers in the form of "foo_bar_t" from stdin and
+translates them to title case like "FooBar", stripping the "_t" suffix.
+This also adds a special case where a context like identifier with the same
+name as the library is translated into "Context" in GI (useful in situations
+like cairo_t -> Context.
+"""
+
+import sys
+
+
+def ensure_title_case(text):
+    # Special case identfilter_t which starts with the same name as the GI
+    # namespace we are trying to achieve.
+    if text == 'identfilter_t':
+        return 'IdentfilterContext'
+
+    # Strip off "_t" suffix.
+    if text.endswith('_t'):
+        text = text[:-2]
+
+    # Split text on underscores and re-join title casing each part
+    text = ''.join(part.title() for part in text.split('_'))
+
+    return text
+
+
+if __name__ == '__main__':
+    text = ensure_title_case(sys.stdin.read())
+    sys.stdout.write(text)
diff --git a/tests/scanner/test_transformer.py b/tests/scanner/test_transformer.py
index 39c54a4..37dbce9 100644
--- a/tests/scanner/test_transformer.py
+++ b/tests/scanner/test_transformer.py
@@ -36,6 +36,36 @@ def load_namespace_from_source_string(namespace, source):
     xformer.parse(ss.get_symbols())
 
 
+class TestIdentifierFilter(unittest.TestCase):
+    def test_underscore_t_sed_filter(self):
+        cmd = r"sed " \
+              r"-e 's/^test_t$/TestContext/' " \
+              r"-e 's/\(.*\)_t$/\1/' " \
+              r"-e 's/^test_/Test_/' " \
+              r"-e 's/_\([a-z]\)/\u\1/g'"
+
+        namespace = ast.Namespace('Test', '1.0')
+        xformer = Transformer(namespace, identifier_filter_cmd=cmd)
+
+        self.assertEqual(xformer.strip_identifier('test_t'), 'Context')
+        self.assertEqual(xformer.strip_identifier('test_foo_t'), 'Foo')
+        self.assertEqual(xformer.strip_identifier('test_foot'), 'Foot')
+        self.assertEqual(xformer.strip_identifier('test_foo_bart'), 'FooBart')
+        self.assertEqual(xformer.strip_identifier('test_foo_tart'), 'FooTart')
+
+    def test_invalid_command(self):
+        cmd = r'this-is-not-a-real-command'
+        namespace = ast.Namespace('Test', '1.0')
+        xformer = Transformer(namespace, identifier_filter_cmd=cmd)
+        self.assertRaises(ValueError, xformer.strip_identifier, 'test_t')
+
+    def test_invalid_argument(self):
+        cmd = r'sed --not-a-valid-argument'
+        namespace = ast.Namespace('Test', '1.0')
+        xformer = Transformer(namespace, identifier_filter_cmd=cmd)
+        self.assertRaises(ValueError, xformer.strip_identifier, 'test_t')
+
+
 class TestStructTypedefs(unittest.TestCase):
     def setUp(self):
         # Hack to set logging singleton


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]