From f40643123d00088af743285b9fd41dd7b5a16da4 Mon Sep 17 00:00:00 2001
From: Robert Bradshaw <robertwb@math.washington.edu>
Date: Sat, 13 Sep 2008 12:48:45 -0700
Subject: [PATCH] Option to emit #line directives, ticket #53

---
 Cython/Compiler/CmdLine.py    |  4 +++-
 Cython/Compiler/Code.py       | 22 +++++++++++++++++-----
 Cython/Compiler/Main.py       |  3 ++-
 Cython/Compiler/ModuleNode.py |  3 +--
 4 files changed, 23 insertions(+), 9 deletions(-)

diff --git a/Cython/Compiler/CmdLine.py b/Cython/Compiler/CmdLine.py
index 34e891e6a..a423d2acd 100644
--- a/Cython/Compiler/CmdLine.py
+++ b/Cython/Compiler/CmdLine.py
@@ -36,7 +36,7 @@ Options:
 
   -D, --no-docstrings            Remove docstrings.
   -a, --annotate                 Produce a colorized HTML version of the source.
-  --convert-range                Convert for loops using range() function to for...from loops. 
+  --line-directives              Produce #line directives pointing to the .pyx source
   --cplus                        Output a c++ rather than c file.
   -X, --directive <name>=<value>[,<name=value,...] Overrides a compiler directive
 """
@@ -114,6 +114,8 @@ def parse_command_line(args):
                 Options.annotate = True
             elif option == "--convert-range":
                 Options.convert_range = True
+            elif option == "--line-directives":
+                options.emit_linenums = True
             elif option in ("-X", "--directive"):
                 try:
                     options.pragma_overrides = Options.parse_option_list(pop_arg())
diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py
index 9010f4fbf..e0897b4d4 100644
--- a/Cython/Compiler/Code.py
+++ b/Cython/Compiler/Code.py
@@ -161,13 +161,14 @@ class GlobalState(object):
     # interned_nums
     # cached_builtins
 
-    def __init__(self, rootwriter):
+    def __init__(self, rootwriter, emit_linenums=False):
         self.filename_table = {}
         self.filename_list = []
         self.input_file_contents = {}
         self.used_utility_code = set()
         self.declared_cnames = {}
         self.pystring_table_needed = False
+        self.emit_linenums = emit_linenums
 
     def initwriters(self, rootwriter):
         self.utilprotowriter = rootwriter.new_writer()
@@ -378,6 +379,8 @@ class GlobalState(object):
         writer.insert(self.utilprotowriter)
 
     def put_utility_code_defs(self, writer):
+        if self.emit_linenums:
+            writer.write('\n#line 1 "cython_utility"\n')
         writer.insert(self.utildefwriter)
 
 
@@ -403,7 +406,7 @@ class CCodeWriter(object):
     - marker: Not copied to insertion point
     - filename_table, filename_list, input_file_contents: All codewriters
       coming from the same root share the same instances simultaneously.
-    """ 
+    """
     
     # f                file            output file
     # buffer           StringIOTree
@@ -415,19 +418,21 @@ class CCodeWriter(object):
     #                                  generation (labels and temps state etc.)
     # globalstate      GlobalState     contains state global for a C file (input file info,
     #                                  utility code, declared constants etc.)
+    # emit_linenums    boolean         whether or not to write #line pragmas 
    
-    def __init__(self, create_from=None, buffer=None, copy_formatting=False):
+    def __init__(self, create_from=None, buffer=None, copy_formatting=False, emit_linenums=None):
         if buffer is None: buffer = StringIOTree()
         self.buffer = buffer
         self.marker = None
         self.last_marker_line = 0
+        self.source_desc = ""
         
         self.funcstate = None
         self.level = 0
         self.bol = 1
         if create_from is None:
             # Root CCodeWriter
-            self.globalstate = GlobalState(self)
+            self.globalstate = GlobalState(self, emit_linenums=emit_linenums)
             self.globalstate.initwriters(self)
             # ^^^ need seperate step because this will reference self.globalstate
         else:
@@ -437,6 +442,10 @@ class CCodeWriter(object):
             if copy_formatting:
                 self.level = create_from.level
                 self.bol = create_from.bol
+        if emit_linenums is None:
+            self.emit_linenums = self.globalstate.emit_linenums
+        else:
+            self.emit_linenums = emit_linenums
 
     def create_new(self, create_from, buffer, copy_formatting):
         # polymorphic constructor -- very slightly more versatile
@@ -504,6 +513,8 @@ class CCodeWriter(object):
     def putln(self, code = ""):
         if self.marker and self.bol:
             self.emit_marker()
+        if self.emit_linenums and self.last_marker_line != 0:
+            self.write('\n#line %s "%s"\n' % (self.last_marker_line, self.source_desc))
         if code:
             self.put(code)
         self.write("\n");
@@ -580,7 +591,8 @@ class CCodeWriter(object):
         marker = u'"%s":%d\n%s\n' % (
             source_desc.get_escaped_description(), line, u'\n'.join(lines))
         self.marker = (line, marker)
-
+        if self.emit_linenums:
+            self.source_desc = source_desc.get_escaped_description()
         
     def put_label(self, lbl):
         if lbl in self.funcstate.labels_used:
diff --git a/Cython/Compiler/Main.py b/Cython/Compiler/Main.py
index 8a9139d02..53af007d7 100644
--- a/Cython/Compiler/Main.py
+++ b/Cython/Compiler/Main.py
@@ -727,7 +727,8 @@ default_options = dict(
     timestamps = None,
     verbose = 0,
     quiet = 0,
-    pragma_overrides = {}
+    pragma_overrides = {},
+    emit_linenums = False,
 )
 if sys.platform == "mac":
     from Cython.Mac.MacSystem import c_compile, c_link, CCompilerError
diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py
index 23cb11455..88db1af7c 100644
--- a/Cython/Compiler/ModuleNode.py
+++ b/Cython/Compiler/ModuleNode.py
@@ -3,7 +3,6 @@
 #
 
 import os, time
-from cStringIO import StringIO
 from PyrexTypes import CPtrType
 import Future
 
@@ -240,7 +239,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
         if Options.annotate or options.annotate:
             code = Annotate.AnnotationCCodeWriter()
         else:
-            code = Code.CCodeWriter()
+            code = Code.CCodeWriter(emit_linenums=options.emit_linenums)
         h_code = code.insertion_point()
         self.generate_module_preamble(env, modules, h_code)
 
-- 
2.30.9