We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Please support SOURCE_DATE_EPOCH specification for build time stamps
Copied from http://bugs.debian.org/824266:

> Hi,
> 
> mkdocs integrates build time stamps into the documentation it is
> generating. This makes at least unburden-home-dir no more reproducibly
> building because it now uses mkdocs to generate HTML documentation at
> build time.
> 
> See https://tests.reproducible-builds.org/rb-pkg/unstable/amd64/unburden-home-dir.html and
> https://tests.reproducible-builds.org/dbd/unstable/amd64/unburden-home-dir_0.4.diffoscope.html
> 
> Other cases of this issue are the source package djangorestframework (see
> https://tests.reproducible-builds.org/rb-pkg/testing/i386/djangorestframework.html and
> https://tests.reproducible-builds.org/dbd/testing/i386/djangorestframework_3.3.2-2.diffoscope.html)
> and of course the source package python-mkdocs itself, see
> https://tests.reproducible-builds.org/rb-pkg/unstable/amd64/python-mkdocs.html and
> https://tests.reproducible-builds.org/dbd/unstable/amd64/python-mkdocs_0.15.3-3.diffoscope.html
> 
> At https://reproducible-builds.org/specs/source-date-epoch/ you can find
> a bound to be widely adopted specification for how to solve such kind of
> issues generally: If the environment variable SOURCE_DATE_EPOCH exists
> use the Unix time stamp (seconds since 1st of January 1970, 00:00:00
> UTC) it contains to preseed all build time stamps with that time stamp
> instead of the current time.
> 
> See https://wiki.debian.org/ReproducibleBuilds/TimestampsProposal#Examples
> for some examples on how to implement support for SOURCE_DATE_EPOCH,
> including an example for Python.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `mkdocs/commands/build.py`
Content:
```
1 # coding: utf-8
2 
3 from __future__ import unicode_literals
4 from datetime import datetime
5 import io
6 import logging
7 import os
8 
9 from jinja2.exceptions import TemplateNotFound
10 import jinja2
11 import json
12 
13 from mkdocs import nav, search, utils
14 from mkdocs.utils import filters
15 from mkdocs.relative_path_ext import RelativePathExtension
16 import mkdocs
17 
18 log = logging.getLogger(__name__)
19 
20 
21 def convert_markdown(markdown_source, config, site_navigation=None):
22     """
23     Convert the Markdown source file to HTML as per the config and
24     site_navigation. Return a tuple of the HTML as a string, the parsed table
25     of contents, and a dictionary of any metadata that was specified in the
26     Markdown file.
27     """
28 
29     extensions = [
30         RelativePathExtension(site_navigation, config['strict'])
31     ] + config['markdown_extensions']
32 
33     return utils.convert_markdown(
34         markdown_source=markdown_source,
35         extensions=extensions,
36         extension_configs=config['mdx_configs']
37     )
38 
39 
40 def get_global_context(nav, config):
41     """
42     Given the SiteNavigation and config, generate the context which is relevant
43     to app pages.
44     """
45 
46     site_name = config['site_name']
47 
48     if config['site_favicon']:
49         site_favicon = nav.url_context.make_relative('/' + config['site_favicon'])
50     else:
51         site_favicon = None
52 
53     page_description = config['site_description']
54 
55     extra_javascript = utils.create_media_urls(nav, config['extra_javascript'])
56 
57     extra_css = utils.create_media_urls(nav, config['extra_css'])
58 
59     return {
60         'site_name': site_name,
61         'site_author': config['site_author'],
62         'favicon': site_favicon,
63         'page_description': page_description,
64 
65         # Note that there's intentionally repetition here. Rather than simply
66         # provide the config dictionary we instead pass everything explicitly.
67         #
68         # This helps ensure that we can throughly document the context that
69         # gets passed to themes.
70         'repo_url': config['repo_url'],
71         'repo_name': config['repo_name'],
72         'nav': nav,
73         'base_url': nav.url_context.make_relative('/'),
74         'homepage_url': nav.homepage.url,
75         'site_url': config['site_url'],
76 
77         'extra_css': extra_css,
78         'extra_javascript': extra_javascript,
79 
80         'include_nav': config['include_nav'],
81         'include_next_prev': config['include_next_prev'],
82 
83         'copyright': config['copyright'],
84         'google_analytics': config['google_analytics'],
85 
86         'mkdocs_version': mkdocs.__version__,
87         'build_date_utc': datetime.utcnow(),
88 
89         'config': config
90     }
91 
92 
93 def get_page_context(page, content, toc, meta, config):
94     """
95     Generate the page context by extending the global context and adding page
96     specific variables.
97     """
98     if config['site_url']:
99         page.set_canonical_url(config['site_url'])
100 
101     page.content = content
102     page.toc = toc
103     page.meta = meta
104 
105     # TODO: remove the rest in version 1.0 as they are deprecated
106 
107     if page.is_homepage or page.title is None:
108         page_title = None
109     else:
110         page_title = page.title
111 
112     if page.is_homepage:
113         page_description = config['site_description']
114     else:
115         page_description = None
116 
117     return {
118         'page': page,
119         # TODO: remove the rest in version 1.0 as they are deprecated
120         'page_title': page_title,
121         'page_description': page_description,
122 
123         'content': content,
124         'toc': toc,
125         'meta': meta,
126 
127         'canonical_url': page.canonical_url,
128 
129         'current_page': page,
130         'previous_page': page.previous_page,
131         'next_page': page.next_page
132     }
133 
134 
135 def build_template(template_name, env, config, site_navigation=None):
136 
137     log.debug("Building template: %s", template_name)
138 
139     try:
140         template = env.get_template(template_name)
141     except TemplateNotFound:
142         return False
143 
144     context = {'page': None}
145     if site_navigation is not None:
146         context.update(get_global_context(site_navigation, config))
147 
148     output_content = template.render(context)
149     output_path = os.path.join(config['site_dir'], template_name)
150     utils.write_file(output_content.encode('utf-8'), output_path)
151     return True
152 
153 
154 def _build_page(page, config, site_navigation, env, dump_json):
155 
156     # Read the input file
157     input_path = os.path.join(config['docs_dir'], page.input_path)
158 
159     try:
160         input_content = io.open(input_path, 'r', encoding='utf-8').read()
161     except IOError:
162         log.error('file not found: %s', input_path)
163         raise
164 
165     # Process the markdown text
166     html_content, table_of_contents, meta = convert_markdown(
167         markdown_source=input_content,
168         config=config,
169         site_navigation=site_navigation
170     )
171 
172     context = get_global_context(site_navigation, config)
173     context.update(get_page_context(
174         page, html_content, table_of_contents, meta, config
175     ))
176 
177     # Allow 'template:' override in md source files.
178     if 'template' in meta:
179         template = env.get_template(meta['template'][0])
180     else:
181         template = env.get_template('base.html')
182 
183     # Render the template.
184     output_content = template.render(context)
185 
186     # Write the output file.
187     output_path = os.path.join(config['site_dir'], page.output_path)
188     if dump_json:
189         json_context = {
190             'content': context['content'],
191             'title': context['current_page'].title,
192             'url': context['current_page'].abs_url,
193             'language': 'en',
194         }
195         json_output = json.dumps(json_context, indent=4).encode('utf-8')
196         utils.write_file(json_output, output_path.replace('.html', '.json'))
197     else:
198         utils.write_file(output_content.encode('utf-8'), output_path)
199 
200     return html_content, table_of_contents, meta
201 
202 
203 def build_extra_templates(extra_templates, config, site_navigation=None):
204 
205     log.debug("Building extra_templates page")
206 
207     for extra_template in extra_templates:
208 
209         input_path = os.path.join(config['docs_dir'], extra_template)
210 
211         with io.open(input_path, 'r', encoding='utf-8') as template_file:
212             template = jinja2.Template(template_file.read())
213 
214         context = {'page': None}
215         if site_navigation is not None:
216             context.update(get_global_context(site_navigation, config))
217 
218         output_content = template.render(context)
219         output_path = os.path.join(config['site_dir'], extra_template)
220         utils.write_file(output_content.encode('utf-8'), output_path)
221 
222 
223 def build_pages(config, dump_json=False):
224     """
225     Builds all the pages and writes them into the build directory.
226     """
227     site_navigation = nav.SiteNavigation(config['pages'], config['use_directory_urls'])
228     loader = jinja2.FileSystemLoader(config['theme_dir'] + [config['mkdocs_templates'], ])
229     env = jinja2.Environment(loader=loader)
230 
231     # TODO: remove DeprecationContext in v1.0 when all deprecated vars have been removed
232     from jinja2.runtime import Context
233     deprecated_vars = [
234         'page_title',
235         'content',
236         'toc',
237         'meta',
238         'current_page',
239         'canonical_url',
240         'previous_page',
241         'next_page'
242     ]
243 
244     class DeprecationContext(Context):
245         def resolve(self, key):
246             """ Log a warning when acessing any deprecated variable name. """
247             if key in deprecated_vars:
248                 replacement = "page" if key == 'current_page' else "page.{0}".format(key)
249                 log.warn(
250                     "Template variable warning: '{0}' is being deprecated and will not be "
251                     "available in a future version. Use '{1}' instead.".format(key, replacement)
252                 )
253             return super(DeprecationContext, self).resolve(key)
254 
255     env.context_class = DeprecationContext
256     # TODO: end remove DeprecationContext
257 
258     env.filters['tojson'] = filters.tojson
259     search_index = search.SearchIndex()
260 
261     build_template('404.html', env, config, site_navigation)
262 
263     if not build_template('search.html', env, config, site_navigation):
264         log.debug("Search is enabled but the theme doesn't contain a "
265                   "search.html file. Assuming the theme implements search "
266                   "within a modal.")
267 
268     build_template('sitemap.xml', env, config, site_navigation)
269 
270     build_extra_templates(config['extra_templates'], config, site_navigation)
271 
272     for page in site_navigation.walk_pages():
273 
274         try:
275             log.debug("Building page %s", page.input_path)
276             build_result = _build_page(page, config, site_navigation, env,
277                                        dump_json)
278             html_content, table_of_contents, _ = build_result
279             search_index.add_entry_from_context(
280                 page, html_content, table_of_contents)
281         except Exception:
282             log.error("Error building page %s", page.input_path)
283             raise
284 
285     search_index = search_index.generate_search_index()
286     json_output_path = os.path.join(config['site_dir'], 'mkdocs', 'search_index.json')
287     utils.write_file(search_index.encode('utf-8'), json_output_path)
288 
289 
290 def build(config, live_server=False, dump_json=False, clean_site_dir=False):
291     """
292     Perform a full site build.
293     """
294     if clean_site_dir:
295         log.info("Cleaning site directory")
296         utils.clean_directory(config['site_dir'])
297     if not live_server:
298         log.info("Building documentation to directory: %s", config['site_dir'])
299         if not clean_site_dir and site_directory_contains_stale_files(config['site_dir']):
300             log.info("The directory contains stale files. Use --clean to remove them.")
301 
302     if dump_json:
303         build_pages(config, dump_json=True)
304         return
305 
306     # Reversed as we want to take the media files from the builtin theme
307     # and then from the custom theme_dir so that the custom versions take
308     # precedence.
309     for theme_dir in reversed(config['theme_dir']):
310         log.debug("Copying static assets from theme: %s", theme_dir)
311         utils.copy_media_files(
312             theme_dir, config['site_dir'], exclude=['*.py', '*.pyc', '*.html']
313         )
314 
315     log.debug("Copying static assets from the docs dir.")
316     utils.copy_media_files(config['docs_dir'], config['site_dir'])
317 
318     log.debug("Building markdown pages.")
319     build_pages(config)
320 
321 
322 def site_directory_contains_stale_files(site_directory):
323     """
324     Check if the site directory contains stale files from a previous build.
325     Right now the check returns true if the directory is not empty.
326     A more sophisticated approach should be found to trigger only if there are
327     files that won't be overwritten anyway.
328     """
329     if os.path.exists(site_directory):
330         if os.listdir(site_directory):
331             return True
332     return False
333
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/mkdocs/commands/build.py b/mkdocs/commands/build.py
--- a/mkdocs/commands/build.py
+++ b/mkdocs/commands/build.py
@@ -2,6 +2,7 @@
 
 from __future__ import unicode_literals
 from datetime import datetime
+from calendar import timegm
 import io
 import logging
 import os
@@ -56,6 +57,10 @@
 
     extra_css = utils.create_media_urls(nav, config['extra_css'])
 
+    # Support SOURCE_DATE_EPOCH environment variable for "reproducible" builds.
+    # See https://reproducible-builds.org/specs/source-date-epoch/
+    timestamp = int(os.environ.get('SOURCE_DATE_EPOCH', timegm(datetime.utcnow().utctimetuple())))
+
     return {
         'site_name': site_name,
         'site_author': config['site_author'],
@@ -84,7 +89,7 @@
         'google_analytics': config['google_analytics'],
 
         'mkdocs_version': mkdocs.__version__,
-        'build_date_utc': datetime.utcnow(),
+        'build_date_utc': datetime.utcfromtimestamp(timestamp),
 
         'config': config
     }

{"golden_diff": "diff --git a/mkdocs/commands/build.py b/mkdocs/commands/build.py\n--- a/mkdocs/commands/build.py\n+++ b/mkdocs/commands/build.py\n@@ -2,6 +2,7 @@\n \n from __future__ import unicode_literals\n from datetime import datetime\n+from calendar import timegm\n import io\n import logging\n import os\n@@ -56,6 +57,10 @@\n \n     extra_css = utils.create_media_urls(nav, config['extra_css'])\n \n+    # Support SOURCE_DATE_EPOCH environment variable for \"reproducible\" builds.\n+    # See https://reproducible-builds.org/specs/source-date-epoch/\n+    timestamp = int(os.environ.get('SOURCE_DATE_EPOCH', timegm(datetime.utcnow().utctimetuple())))\n+\n     return {\n         'site_name': site_name,\n         'site_author': config['site_author'],\n@@ -84,7 +89,7 @@\n         'google_analytics': config['google_analytics'],\n \n         'mkdocs_version': mkdocs.__version__,\n-        'build_date_utc': datetime.utcnow(),\n+        'build_date_utc': datetime.utcfromtimestamp(timestamp),\n \n         'config': config\n     }\n", "issue": "Please support SOURCE_DATE_EPOCH specification for build time stamps\nCopied from http://bugs.debian.org/824266:\n\n> Hi,\n> \n> mkdocs integrates build time stamps into the documentation it is\n> generating. This makes at least unburden-home-dir no more reproducibly\n> building because it now uses mkdocs to generate HTML documentation at\n> build time.\n> \n> See https://tests.reproducible-builds.org/rb-pkg/unstable/amd64/unburden-home-dir.html and\n> https://tests.reproducible-builds.org/dbd/unstable/amd64/unburden-home-dir_0.4.diffoscope.html\n> \n> Other cases of this issue are the source package djangorestframework (see\n> https://tests.reproducible-builds.org/rb-pkg/testing/i386/djangorestframework.html and\n> https://tests.reproducible-builds.org/dbd/testing/i386/djangorestframework_3.3.2-2.diffoscope.html)\n> and of course the source package python-mkdocs itself, see\n> https://tests.reproducible-builds.org/rb-pkg/unstable/amd64/python-mkdocs.html and\n> https://tests.reproducible-builds.org/dbd/unstable/amd64/python-mkdocs_0.15.3-3.diffoscope.html\n> \n> At https://reproducible-builds.org/specs/source-date-epoch/ you can find\n> a bound to be widely adopted specification for how to solve such kind of\n> issues generally: If the environment variable SOURCE_DATE_EPOCH exists\n> use the Unix time stamp (seconds since 1st of January 1970, 00:00:00\n> UTC) it contains to preseed all build time stamps with that time stamp\n> instead of the current time.\n> \n> See https://wiki.debian.org/ReproducibleBuilds/TimestampsProposal#Examples\n> for some examples on how to implement support for SOURCE_DATE_EPOCH,\n> including an example for Python.\n\n", "before_files": [{"content": "# coding: utf-8\n\nfrom __future__ import unicode_literals\nfrom datetime import datetime\nimport io\nimport logging\nimport os\n\nfrom jinja2.exceptions import TemplateNotFound\nimport jinja2\nimport json\n\nfrom mkdocs import nav, search, utils\nfrom mkdocs.utils import filters\nfrom mkdocs.relative_path_ext import RelativePathExtension\nimport mkdocs\n\nlog = logging.getLogger(__name__)\n\n\ndef convert_markdown(markdown_source, config, site_navigation=None):\n    \"\"\"\n    Convert the Markdown source file to HTML as per the config and\n    site_navigation. Return a tuple of the HTML as a string, the parsed table\n    of contents, and a dictionary of any metadata that was specified in the\n    Markdown file.\n    \"\"\"\n\n    extensions = [\n        RelativePathExtension(site_navigation, config['strict'])\n    ] + config['markdown_extensions']\n\n    return utils.convert_markdown(\n        markdown_source=markdown_source,\n        extensions=extensions,\n        extension_configs=config['mdx_configs']\n    )\n\n\ndef get_global_context(nav, config):\n    \"\"\"\n    Given the SiteNavigation and config, generate the context which is relevant\n    to app pages.\n    \"\"\"\n\n    site_name = config['site_name']\n\n    if config['site_favicon']:\n        site_favicon = nav.url_context.make_relative('/' + config['site_favicon'])\n    else:\n        site_favicon = None\n\n    page_description = config['site_description']\n\n    extra_javascript = utils.create_media_urls(nav, config['extra_javascript'])\n\n    extra_css = utils.create_media_urls(nav, config['extra_css'])\n\n    return {\n        'site_name': site_name,\n        'site_author': config['site_author'],\n        'favicon': site_favicon,\n        'page_description': page_description,\n\n        # Note that there's intentionally repetition here. Rather than simply\n        # provide the config dictionary we instead pass everything explicitly.\n        #\n        # This helps ensure that we can throughly document the context that\n        # gets passed to themes.\n        'repo_url': config['repo_url'],\n        'repo_name': config['repo_name'],\n        'nav': nav,\n        'base_url': nav.url_context.make_relative('/'),\n        'homepage_url': nav.homepage.url,\n        'site_url': config['site_url'],\n\n        'extra_css': extra_css,\n        'extra_javascript': extra_javascript,\n\n        'include_nav': config['include_nav'],\n        'include_next_prev': config['include_next_prev'],\n\n        'copyright': config['copyright'],\n        'google_analytics': config['google_analytics'],\n\n        'mkdocs_version': mkdocs.__version__,\n        'build_date_utc': datetime.utcnow(),\n\n        'config': config\n    }\n\n\ndef get_page_context(page, content, toc, meta, config):\n    \"\"\"\n    Generate the page context by extending the global context and adding page\n    specific variables.\n    \"\"\"\n    if config['site_url']:\n        page.set_canonical_url(config['site_url'])\n\n    page.content = content\n    page.toc = toc\n    page.meta = meta\n\n    # TODO: remove the rest in version 1.0 as they are deprecated\n\n    if page.is_homepage or page.title is None:\n        page_title = None\n    else:\n        page_title = page.title\n\n    if page.is_homepage:\n        page_description = config['site_description']\n    else:\n        page_description = None\n\n    return {\n        'page': page,\n        # TODO: remove the rest in version 1.0 as they are deprecated\n        'page_title': page_title,\n        'page_description': page_description,\n\n        'content': content,\n        'toc': toc,\n        'meta': meta,\n\n        'canonical_url': page.canonical_url,\n\n        'current_page': page,\n        'previous_page': page.previous_page,\n        'next_page': page.next_page\n    }\n\n\ndef build_template(template_name, env, config, site_navigation=None):\n\n    log.debug(\"Building template: %s\", template_name)\n\n    try:\n        template = env.get_template(template_name)\n    except TemplateNotFound:\n        return False\n\n    context = {'page': None}\n    if site_navigation is not None:\n        context.update(get_global_context(site_navigation, config))\n\n    output_content = template.render(context)\n    output_path = os.path.join(config['site_dir'], template_name)\n    utils.write_file(output_content.encode('utf-8'), output_path)\n    return True\n\n\ndef _build_page(page, config, site_navigation, env, dump_json):\n\n    # Read the input file\n    input_path = os.path.join(config['docs_dir'], page.input_path)\n\n    try:\n        input_content = io.open(input_path, 'r', encoding='utf-8').read()\n    except IOError:\n        log.error('file not found: %s', input_path)\n        raise\n\n    # Process the markdown text\n    html_content, table_of_contents, meta = convert_markdown(\n        markdown_source=input_content,\n        config=config,\n        site_navigation=site_navigation\n    )\n\n    context = get_global_context(site_navigation, config)\n    context.update(get_page_context(\n        page, html_content, table_of_contents, meta, config\n    ))\n\n    # Allow 'template:' override in md source files.\n    if 'template' in meta:\n        template = env.get_template(meta['template'][0])\n    else:\n        template = env.get_template('base.html')\n\n    # Render the template.\n    output_content = template.render(context)\n\n    # Write the output file.\n    output_path = os.path.join(config['site_dir'], page.output_path)\n    if dump_json:\n        json_context = {\n            'content': context['content'],\n            'title': context['current_page'].title,\n            'url': context['current_page'].abs_url,\n            'language': 'en',\n        }\n        json_output = json.dumps(json_context, indent=4).encode('utf-8')\n        utils.write_file(json_output, output_path.replace('.html', '.json'))\n    else:\n        utils.write_file(output_content.encode('utf-8'), output_path)\n\n    return html_content, table_of_contents, meta\n\n\ndef build_extra_templates(extra_templates, config, site_navigation=None):\n\n    log.debug(\"Building extra_templates page\")\n\n    for extra_template in extra_templates:\n\n        input_path = os.path.join(config['docs_dir'], extra_template)\n\n        with io.open(input_path, 'r', encoding='utf-8') as template_file:\n            template = jinja2.Template(template_file.read())\n\n        context = {'page': None}\n        if site_navigation is not None:\n            context.update(get_global_context(site_navigation, config))\n\n        output_content = template.render(context)\n        output_path = os.path.join(config['site_dir'], extra_template)\n        utils.write_file(output_content.encode('utf-8'), output_path)\n\n\ndef build_pages(config, dump_json=False):\n    \"\"\"\n    Builds all the pages and writes them into the build directory.\n    \"\"\"\n    site_navigation = nav.SiteNavigation(config['pages'], config['use_directory_urls'])\n    loader = jinja2.FileSystemLoader(config['theme_dir'] + [config['mkdocs_templates'], ])\n    env = jinja2.Environment(loader=loader)\n\n    # TODO: remove DeprecationContext in v1.0 when all deprecated vars have been removed\n    from jinja2.runtime import Context\n    deprecated_vars = [\n        'page_title',\n        'content',\n        'toc',\n        'meta',\n        'current_page',\n        'canonical_url',\n        'previous_page',\n        'next_page'\n    ]\n\n    class DeprecationContext(Context):\n        def resolve(self, key):\n            \"\"\" Log a warning when acessing any deprecated variable name. \"\"\"\n            if key in deprecated_vars:\n                replacement = \"page\" if key == 'current_page' else \"page.{0}\".format(key)\n                log.warn(\n                    \"Template variable warning: '{0}' is being deprecated and will not be \"\n                    \"available in a future version. Use '{1}' instead.\".format(key, replacement)\n                )\n            return super(DeprecationContext, self).resolve(key)\n\n    env.context_class = DeprecationContext\n    # TODO: end remove DeprecationContext\n\n    env.filters['tojson'] = filters.tojson\n    search_index = search.SearchIndex()\n\n    build_template('404.html', env, config, site_navigation)\n\n    if not build_template('search.html', env, config, site_navigation):\n        log.debug(\"Search is enabled but the theme doesn't contain a \"\n                  \"search.html file. Assuming the theme implements search \"\n                  \"within a modal.\")\n\n    build_template('sitemap.xml', env, config, site_navigation)\n\n    build_extra_templates(config['extra_templates'], config, site_navigation)\n\n    for page in site_navigation.walk_pages():\n\n        try:\n            log.debug(\"Building page %s\", page.input_path)\n            build_result = _build_page(page, config, site_navigation, env,\n                                       dump_json)\n            html_content, table_of_contents, _ = build_result\n            search_index.add_entry_from_context(\n                page, html_content, table_of_contents)\n        except Exception:\n            log.error(\"Error building page %s\", page.input_path)\n            raise\n\n    search_index = search_index.generate_search_index()\n    json_output_path = os.path.join(config['site_dir'], 'mkdocs', 'search_index.json')\n    utils.write_file(search_index.encode('utf-8'), json_output_path)\n\n\ndef build(config, live_server=False, dump_json=False, clean_site_dir=False):\n    \"\"\"\n    Perform a full site build.\n    \"\"\"\n    if clean_site_dir:\n        log.info(\"Cleaning site directory\")\n        utils.clean_directory(config['site_dir'])\n    if not live_server:\n        log.info(\"Building documentation to directory: %s\", config['site_dir'])\n        if not clean_site_dir and site_directory_contains_stale_files(config['site_dir']):\n            log.info(\"The directory contains stale files. Use --clean to remove them.\")\n\n    if dump_json:\n        build_pages(config, dump_json=True)\n        return\n\n    # Reversed as we want to take the media files from the builtin theme\n    # and then from the custom theme_dir so that the custom versions take\n    # precedence.\n    for theme_dir in reversed(config['theme_dir']):\n        log.debug(\"Copying static assets from theme: %s\", theme_dir)\n        utils.copy_media_files(\n            theme_dir, config['site_dir'], exclude=['*.py', '*.pyc', '*.html']\n        )\n\n    log.debug(\"Copying static assets from the docs dir.\")\n    utils.copy_media_files(config['docs_dir'], config['site_dir'])\n\n    log.debug(\"Building markdown pages.\")\n    build_pages(config)\n\n\ndef site_directory_contains_stale_files(site_directory):\n    \"\"\"\n    Check if the site directory contains stale files from a previous build.\n    Right now the check returns true if the directory is not empty.\n    A more sophisticated approach should be found to trigger only if there are\n    files that won't be overwritten anyway.\n    \"\"\"\n    if os.path.exists(site_directory):\n        if os.listdir(site_directory):\n            return True\n    return False\n", "path": "mkdocs/commands/build.py"}], "after_files": [{"content": "# coding: utf-8\n\nfrom __future__ import unicode_literals\nfrom datetime import datetime\nfrom calendar import timegm\nimport io\nimport logging\nimport os\n\nfrom jinja2.exceptions import TemplateNotFound\nimport jinja2\nimport json\n\nfrom mkdocs import nav, search, utils\nfrom mkdocs.utils import filters\nfrom mkdocs.relative_path_ext import RelativePathExtension\nimport mkdocs\n\nlog = logging.getLogger(__name__)\n\n\ndef convert_markdown(markdown_source, config, site_navigation=None):\n    \"\"\"\n    Convert the Markdown source file to HTML as per the config and\n    site_navigation. Return a tuple of the HTML as a string, the parsed table\n    of contents, and a dictionary of any metadata that was specified in the\n    Markdown file.\n    \"\"\"\n\n    extensions = [\n        RelativePathExtension(site_navigation, config['strict'])\n    ] + config['markdown_extensions']\n\n    return utils.convert_markdown(\n        markdown_source=markdown_source,\n        extensions=extensions,\n        extension_configs=config['mdx_configs']\n    )\n\n\ndef get_global_context(nav, config):\n    \"\"\"\n    Given the SiteNavigation and config, generate the context which is relevant\n    to app pages.\n    \"\"\"\n\n    site_name = config['site_name']\n\n    if config['site_favicon']:\n        site_favicon = nav.url_context.make_relative('/' + config['site_favicon'])\n    else:\n        site_favicon = None\n\n    page_description = config['site_description']\n\n    extra_javascript = utils.create_media_urls(nav, config['extra_javascript'])\n\n    extra_css = utils.create_media_urls(nav, config['extra_css'])\n\n    # Support SOURCE_DATE_EPOCH environment variable for \"reproducible\" builds.\n    # See https://reproducible-builds.org/specs/source-date-epoch/\n    timestamp = int(os.environ.get('SOURCE_DATE_EPOCH', timegm(datetime.utcnow().utctimetuple())))\n\n    return {\n        'site_name': site_name,\n        'site_author': config['site_author'],\n        'favicon': site_favicon,\n        'page_description': page_description,\n\n        # Note that there's intentionally repetition here. Rather than simply\n        # provide the config dictionary we instead pass everything explicitly.\n        #\n        # This helps ensure that we can throughly document the context that\n        # gets passed to themes.\n        'repo_url': config['repo_url'],\n        'repo_name': config['repo_name'],\n        'nav': nav,\n        'base_url': nav.url_context.make_relative('/'),\n        'homepage_url': nav.homepage.url,\n        'site_url': config['site_url'],\n\n        'extra_css': extra_css,\n        'extra_javascript': extra_javascript,\n\n        'include_nav': config['include_nav'],\n        'include_next_prev': config['include_next_prev'],\n\n        'copyright': config['copyright'],\n        'google_analytics': config['google_analytics'],\n\n        'mkdocs_version': mkdocs.__version__,\n        'build_date_utc': datetime.utcfromtimestamp(timestamp),\n\n        'config': config\n    }\n\n\ndef get_page_context(page, content, toc, meta, config):\n    \"\"\"\n    Generate the page context by extending the global context and adding page\n    specific variables.\n    \"\"\"\n    if config['site_url']:\n        page.set_canonical_url(config['site_url'])\n\n    page.content = content\n    page.toc = toc\n    page.meta = meta\n\n    # TODO: remove the rest in version 1.0 as they are deprecated\n\n    if page.is_homepage or page.title is None:\n        page_title = None\n    else:\n        page_title = page.title\n\n    if page.is_homepage:\n        page_description = config['site_description']\n    else:\n        page_description = None\n\n    return {\n        'page': page,\n        # TODO: remove the rest in version 1.0 as they are deprecated\n        'page_title': page_title,\n        'page_description': page_description,\n\n        'content': content,\n        'toc': toc,\n        'meta': meta,\n\n        'canonical_url': page.canonical_url,\n\n        'current_page': page,\n        'previous_page': page.previous_page,\n        'next_page': page.next_page\n    }\n\n\ndef build_template(template_name, env, config, site_navigation=None):\n\n    log.debug(\"Building template: %s\", template_name)\n\n    try:\n        template = env.get_template(template_name)\n    except TemplateNotFound:\n        return False\n\n    context = {'page': None}\n    if site_navigation is not None:\n        context.update(get_global_context(site_navigation, config))\n\n    output_content = template.render(context)\n    output_path = os.path.join(config['site_dir'], template_name)\n    utils.write_file(output_content.encode('utf-8'), output_path)\n    return True\n\n\ndef _build_page(page, config, site_navigation, env, dump_json):\n\n    # Read the input file\n    input_path = os.path.join(config['docs_dir'], page.input_path)\n\n    try:\n        input_content = io.open(input_path, 'r', encoding='utf-8').read()\n    except IOError:\n        log.error('file not found: %s', input_path)\n        raise\n\n    # Process the markdown text\n    html_content, table_of_contents, meta = convert_markdown(\n        markdown_source=input_content,\n        config=config,\n        site_navigation=site_navigation\n    )\n\n    context = get_global_context(site_navigation, config)\n    context.update(get_page_context(\n        page, html_content, table_of_contents, meta, config\n    ))\n\n    # Allow 'template:' override in md source files.\n    if 'template' in meta:\n        template = env.get_template(meta['template'][0])\n    else:\n        template = env.get_template('base.html')\n\n    # Render the template.\n    output_content = template.render(context)\n\n    # Write the output file.\n    output_path = os.path.join(config['site_dir'], page.output_path)\n    if dump_json:\n        json_context = {\n            'content': context['content'],\n            'title': context['current_page'].title,\n            'url': context['current_page'].abs_url,\n            'language': 'en',\n        }\n        json_output = json.dumps(json_context, indent=4).encode('utf-8')\n        utils.write_file(json_output, output_path.replace('.html', '.json'))\n    else:\n        utils.write_file(output_content.encode('utf-8'), output_path)\n\n    return html_content, table_of_contents, meta\n\n\ndef build_extra_templates(extra_templates, config, site_navigation=None):\n\n    log.debug(\"Building extra_templates page\")\n\n    for extra_template in extra_templates:\n\n        input_path = os.path.join(config['docs_dir'], extra_template)\n\n        with io.open(input_path, 'r', encoding='utf-8') as template_file:\n            template = jinja2.Template(template_file.read())\n\n        context = {'page': None}\n        if site_navigation is not None:\n            context.update(get_global_context(site_navigation, config))\n\n        output_content = template.render(context)\n        output_path = os.path.join(config['site_dir'], extra_template)\n        utils.write_file(output_content.encode('utf-8'), output_path)\n\n\ndef build_pages(config, dump_json=False):\n    \"\"\"\n    Builds all the pages and writes them into the build directory.\n    \"\"\"\n    site_navigation = nav.SiteNavigation(config['pages'], config['use_directory_urls'])\n    loader = jinja2.FileSystemLoader(config['theme_dir'] + [config['mkdocs_templates'], ])\n    env = jinja2.Environment(loader=loader)\n\n    # TODO: remove DeprecationContext in v1.0 when all deprecated vars have been removed\n    from jinja2.runtime import Context\n    deprecated_vars = [\n        'page_title',\n        'content',\n        'toc',\n        'meta',\n        'current_page',\n        'canonical_url',\n        'previous_page',\n        'next_page'\n    ]\n\n    class DeprecationContext(Context):\n        def resolve(self, key):\n            \"\"\" Log a warning when acessing any deprecated variable name. \"\"\"\n            if key in deprecated_vars:\n                replacement = \"page\" if key == 'current_page' else \"page.{0}\".format(key)\n                log.warn(\n                    \"Template variable warning: '{0}' is being deprecated and will not be \"\n                    \"available in a future version. Use '{1}' instead.\".format(key, replacement)\n                )\n            return super(DeprecationContext, self).resolve(key)\n\n    env.context_class = DeprecationContext\n    # TODO: end remove DeprecationContext\n\n    env.filters['tojson'] = filters.tojson\n    search_index = search.SearchIndex()\n\n    build_template('404.html', env, config, site_navigation)\n\n    if not build_template('search.html', env, config, site_navigation):\n        log.debug(\"Search is enabled but the theme doesn't contain a \"\n                  \"search.html file. Assuming the theme implements search \"\n                  \"within a modal.\")\n\n    build_template('sitemap.xml', env, config, site_navigation)\n\n    build_extra_templates(config['extra_templates'], config, site_navigation)\n\n    for page in site_navigation.walk_pages():\n\n        try:\n            log.debug(\"Building page %s\", page.input_path)\n            build_result = _build_page(page, config, site_navigation, env,\n                                       dump_json)\n            html_content, table_of_contents, _ = build_result\n            search_index.add_entry_from_context(\n                page, html_content, table_of_contents)\n        except Exception:\n            log.error(\"Error building page %s\", page.input_path)\n            raise\n\n    search_index = search_index.generate_search_index()\n    json_output_path = os.path.join(config['site_dir'], 'mkdocs', 'search_index.json')\n    utils.write_file(search_index.encode('utf-8'), json_output_path)\n\n\ndef build(config, live_server=False, dump_json=False, clean_site_dir=False):\n    \"\"\"\n    Perform a full site build.\n    \"\"\"\n    if clean_site_dir:\n        log.info(\"Cleaning site directory\")\n        utils.clean_directory(config['site_dir'])\n    if not live_server:\n        log.info(\"Building documentation to directory: %s\", config['site_dir'])\n        if not clean_site_dir and site_directory_contains_stale_files(config['site_dir']):\n            log.info(\"The directory contains stale files. Use --clean to remove them.\")\n\n    if dump_json:\n        build_pages(config, dump_json=True)\n        return\n\n    # Reversed as we want to take the media files from the builtin theme\n    # and then from the custom theme_dir so that the custom versions take\n    # precedence.\n    for theme_dir in reversed(config['theme_dir']):\n        log.debug(\"Copying static assets from theme: %s\", theme_dir)\n        utils.copy_media_files(\n            theme_dir, config['site_dir'], exclude=['*.py', '*.pyc', '*.html']\n        )\n\n    log.debug(\"Copying static assets from the docs dir.\")\n    utils.copy_media_files(config['docs_dir'], config['site_dir'])\n\n    log.debug(\"Building markdown pages.\")\n    build_pages(config)\n\n\ndef site_directory_contains_stale_files(site_directory):\n    \"\"\"\n    Check if the site directory contains stale files from a previous build.\n    Right now the check returns true if the directory is not empty.\n    A more sophisticated approach should be found to trigger only if there are\n    files that won't be overwritten anyway.\n    \"\"\"\n    if os.path.exists(site_directory):\n        if os.listdir(site_directory):\n            return True\n    return False\n", "path": "mkdocs/commands/build.py"}]}