diff --git a/Cython/Build/Dependencies.py b/Cython/Build/Dependencies.py
index 9c015d1c3c2d5900ed4f717f7eb2e1c76760dd7c..e22f8caac6122c1e5a2ac9a3f58a90d8b20bb90d 100644
--- a/Cython/Build/Dependencies.py
+++ b/Cython/Build/Dependencies.py
@@ -621,6 +621,7 @@ def create_extension_list(patterns, exclude=[], ctx=None, aliases=None, quiet=Fa
         to_exclude.update(map(os.path.abspath, extended_iglob(pattern)))
 
     module_list = []
+    module_metadata = {}
     for pattern in patterns:
         if isinstance(pattern, str):
             filepattern = pattern
@@ -677,7 +678,10 @@ def create_extension_list(patterns, exclude=[], ctx=None, aliases=None, quiet=Fa
                         source = encode_filename_in_py2(source)
                         if source not in sources:
                             sources.append(source)
+                    extra_sources = kwds['sources']
                     del kwds['sources']
+                else:
+                    extra_sources = None
                 if 'depends' in kwds:
                     depends = resolve_depends(kwds['depends'], (kwds.get('include_dirs') or []) + [find_root_package_dir(file)])
                     if template is not None:
@@ -692,9 +696,12 @@ def create_extension_list(patterns, exclude=[], ctx=None, aliases=None, quiet=Fa
                         name=module_name,
                         sources=sources,
                         **kwds))
+                if extra_sources:
+                    kwds['sources'] = extra_sources
+                module_metadata[module_name] = {'distutils': kwds}
                 m = module_list[-1]
                 seen.add(name)
-    return module_list
+    return module_list, module_metadata
 
 
 # This is the user-exposed entry point.
@@ -737,7 +744,7 @@ def cythonize(module_list, exclude=[], nthreads=0, aliases=None, quiet=False, fo
     cpp_options = CompilationOptions(**options); cpp_options.cplus = True
     ctx = c_options.create_context()
     options = c_options
-    module_list = create_extension_list(
+    module_list, module_metadata = create_extension_list(
         module_list,
         exclude=exclude,
         ctx=ctx,
@@ -809,7 +816,7 @@ def cythonize(module_list, exclude=[], nthreads=0, aliases=None, quiet=False, fo
                     else:
                         fingerprint = None
                     to_compile.append((priority, source, c_file, fingerprint, quiet,
-                                       options, not exclude_failures))
+                                       options, not exclude_failures, module_metadata.get(m.name)))
                 new_sources.append(c_file)
                 if c_file not in modules_by_cfile:
                     modules_by_cfile[c_file] = [m]
@@ -920,7 +927,7 @@ else:
 
 # TODO: Share context? Issue: pyx processing leaks into pxd module
 @record_results
-def cythonize_one(pyx_file, c_file, fingerprint, quiet, options=None, raise_on_failure=True):
+def cythonize_one(pyx_file, c_file, fingerprint, quiet, options=None, raise_on_failure=True, embedded_metadata=None):
     from ..Compiler.Main import compile, default_options
     from ..Compiler.Errors import CompileError, PyrexError
 
@@ -954,6 +961,7 @@ def cythonize_one(pyx_file, c_file, fingerprint, quiet, options=None, raise_on_f
     if options is None:
         options = CompilationOptions(default_options)
     options.output_file = c_file
+    options.embedded_metadata = embedded_metadata
 
     any_failures = 0
     try:
diff --git a/Cython/Compiler/Main.py b/Cython/Compiler/Main.py
index 4ee84cd9bb4efb439317a691931095382a402d5d..29756bc80bf57ee013ff37a82cdc2a89f5d1441c 100644
--- a/Cython/Compiler/Main.py
+++ b/Cython/Compiler/Main.py
@@ -400,6 +400,7 @@ def create_default_resultobj(compilation_source, options):
         else:
             c_suffix = ".c"
         result.c_file = Utils.replace_suffix(source_desc.filename, c_suffix)
+    result.embedded_metadata = options.embedded_metadata
     return result
 
 def run_pipeline(source, options, full_module_name=None, context=None):
@@ -479,7 +480,8 @@ class CompilationOptions(object):
                                 header files.
     timestamps        boolean   Only compile changed source files.
     verbose           boolean   Always print source names being compiled
-    compiler_directives  dict      Overrides for pragma options (see Options.py)
+    compiler_directives  dict   Overrides for pragma options (see Options.py)
+    embedded_metadata    dict   Metadata to embed in the C file as json.
     evaluate_tree_assertions boolean  Test support: evaluate parse tree assertions
     language_level    integer   The Python language level: 2 or 3
     formal_grammar    boolean  Parse the file with the formal grammar
@@ -690,6 +692,7 @@ default_options = dict(
     verbose = 0,
     quiet = 0,
     compiler_directives = {},
+    embedded_metadata = {},
     evaluate_tree_assertions = False,
     emit_linenums = False,
     relative_path_in_code_position_comments = True,
diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py
index 3cbdbd9931c2cac5f78afe6a7b63a6eaf6efe487..94d60724c1f7d735955a8de5bfb94cb69a80d308 100644
--- a/Cython/Compiler/ModuleNode.py
+++ b/Cython/Compiler/ModuleNode.py
@@ -9,6 +9,7 @@ cython.declare(Naming=object, Options=object, PyrexTypes=object, TypeSlots=objec
                error=object, warning=object, py_object_type=object, UtilityCode=object,
                EncodedString=object)
 
+import json
 import os
 import operator
 from .PyrexTypes import CPtrType
@@ -308,7 +309,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
         globalstate.initialize_main_c_code()
         h_code = globalstate['h_code']
 
-        self.generate_module_preamble(env, modules, h_code)
+        self.generate_module_preamble(env, modules, result.embedded_metadata, h_code)
 
         globalstate.module_pos = self.pos
         globalstate.directives = self.directives
@@ -547,9 +548,15 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
             self.generate_cvariable_declarations(module, modulecode, defined_here)
             self.generate_cfunction_declarations(module, modulecode, defined_here)
 
-    def generate_module_preamble(self, env, cimported_modules, code):
+    def generate_module_preamble(self, env, cimported_modules, metadata, code):
         code.putln("/* Generated by Cython %s */" % Version.watermark)
         code.putln("")
+        if metadata:
+            code.putln("/* Cython Metadata */")
+            code.putln("/*")
+            code.putln(json.dumps(metadata, indent=4))
+            code.putln("*/")
+            code.putln("")
         code.putln("#define PY_SSIZE_T_CLEAN")
 
         # sizeof(PyLongObject.ob_digit[0]) may have been determined dynamically