summaryrefslogtreecommitdiff
blob: cb86425b1e515f92b7a77ecd3677d474a8f5a26b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
<!DOCTYPE html>

<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />

    <title>Common helper functions &#8212; Gentoo Python Guide  documentation</title>
    <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
    <link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
    <script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
    <script src="_static/jquery.js"></script>
    <script src="_static/underscore.js"></script>
    <script src="_static/doctools.js"></script>
    <link rel="index" title="Index" href="genindex.html" />
    <link rel="search" title="Search" href="search.html" />
    <link rel="next" title="Advanced dependencies" href="depend.html" />
    <link rel="prev" title="distutils-r1 legacy concepts" href="distutils-legacy.html" />
   
  <link rel="stylesheet" href="_static/custom.css" type="text/css" />
  
  
  <meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />

  </head><body>
  

    <div class="document">
      <div class="documentwrapper">
        <div class="bodywrapper">
          

          <div class="body" role="main">
            
  <section id="common-helper-functions">
<h1>Common helper functions<a class="headerlink" href="#common-helper-functions" title="Permalink to this headline"></a></h1>
<p>The functions described in this chapter are common to all three basic
eclasses.  To facilitate code reuse, they are declared
in <code class="docutils literal notranslate"><span class="pre">python-utils-r1.eclass</span></code>.  However, you should not inherit this
eclass directly and instead assume the functions are provided as part
of the API of other eclasses.</p>
<p>Eclass reference: <a class="reference external" href="https://devmanual.gentoo.org/eclass-reference/python-utils-r1.eclass/index.html">python-utils-r1.eclass(5)</a></p>
<span class="target" id="index-0"></span><span class="target" id="index-1"></span><span class="target" id="index-2"></span><span class="target" id="index-3"></span><span class="target" id="index-4"></span><span class="target" id="index-5"></span><span class="target" id="index-6"></span><section id="install-helpers">
<span id="index-7"></span><h2>Install helpers<a class="headerlink" href="#install-helpers" title="Permalink to this headline"></a></h2>
<p>The install helpers are provided commonly for <code class="docutils literal notranslate"><span class="pre">python-single-r1</span></code>
and <code class="docutils literal notranslate"><span class="pre">python-r1</span></code> eclasses.  Their main purpose is to facilitate
installing Python scripts, modules and extensions whenever the package
lacks a build system or the build system is not suited for installing
them.</p>
<p>The API is consistent with the standard <code class="docutils literal notranslate"><span class="pre">do*</span></code>, <code class="docutils literal notranslate"><span class="pre">new*</span></code> and <code class="docutils literal notranslate"><span class="pre">*into</span></code>
helpers.  There are four kinds of functions provided:</p>
<ol class="arabic simple">
<li><p><code class="docutils literal notranslate"><span class="pre">python_doexe</span></code> and <code class="docutils literal notranslate"><span class="pre">python_newexe</span></code> that install executables
wrapping them via python-exec,</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">python_doscript</span></code> and <code class="docutils literal notranslate"><span class="pre">python_newscript</span></code> that install Python
scripts, updating the shebangs and wrapping them via python-exec,</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">python_domodule</span></code> that installs Python modules, or recursively
installs packages (directories),</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">python_doheader</span></code> that installs header files to Python-specific
include directory.</p></li>
</ol>
<p>The install path for executables and scripts (1. and 2.) can be adjusted
by calling <code class="docutils literal notranslate"><span class="pre">python_scriptinto</span></code>.  Note that this actually affects only
the wrapper symlink install path; the actual scripts will be installed
in the standard python-exec script directories.  This also implies that
no two executables can have the same name, even if final directory is
different.  The default install path is <code class="docutils literal notranslate"><span class="pre">/usr/bin</span></code>.</p>
<p>The install path for modules and packages (3.) can be adjusted
by calling <code class="docutils literal notranslate"><span class="pre">python_moduleinto</span></code>.  This function accepts either absolute
path or Python parent module name that causes modules to be installed
in an appropriate subdirectory of the site-packages directory.
The default install path is top-level site-packages (equivalent
to <code class="docutils literal notranslate"><span class="pre">python_moduleinto</span> <span class="pre">.</span></code>).</p>
<p>The install path for headers (4.) cannot be adjusted.</p>
<p><code class="docutils literal notranslate"><span class="pre">python_doexe</span></code> is generally used to install executables that reference
Python but are not Python scripts.  This could be e.g. a bash script
that calls Python:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">make_wrapper</span> <span class="s2">&quot;$</span><span class="si">{PN}</span><span class="s2">.tmp&quot;</span> <span class="s2">&quot;$</span><span class="si">{EPYTHON}</span><span class="s2"> $(python_get_sitedir)/$</span><span class="si">{PN}</span><span class="s2">/cropgtk.py&quot;</span>
<span class="n">python_newexe</span> <span class="s2">&quot;${ED%/}/usr/bin/$</span><span class="si">{PN}</span><span class="s2">.tmp&quot;</span> <span class="s2">&quot;$</span><span class="si">{PN}</span><span class="s2">&quot;</span>
<span class="n">rm</span> <span class="s2">&quot;${ED%/}/usr/bin/$</span><span class="si">{PN}</span><span class="s2">.tmp&quot;</span> <span class="o">||</span> <span class="n">die</span>
</pre></div>
</div>
<p>Note that you need to ensure that the executable calls correct Python
interpreter itself.</p>
<p><code class="docutils literal notranslate"><span class="pre">python_doscript</span></code> is generally used to install Python scripts
to binary directories:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">python_scriptinto</span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">sbin</span>
<span class="n">python_newscript</span> <span class="n">pynslcd</span><span class="o">.</span><span class="n">py</span> <span class="n">pynslcd</span>
</pre></div>
</div>
<p>It takes care of updating the shebang for you.</p>
<p><code class="docutils literal notranslate"><span class="pre">python_domodule</span></code> is used to install Python modules, extensions,
packages, data files and in general anything that lands in site-packages
directory:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>python_moduleinto ${PN}
python_domodule images application ${MY_PN}.py \
    AUTHORS CHANGES COPYING DEPENDS TODO __init__.py
</pre></div>
</div>
<p>It is roughly equivalent to <code class="docutils literal notranslate"><span class="pre">dodir</span> <span class="pre">-r</span></code>, except that it byte-compiles
all Python modules found inside it.</p>
<p><code class="docutils literal notranslate"><span class="pre">python_doheader</span></code> is used in the very rare cases when Python packages
install additional header files that are used to compile other
extensions:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">python_doheader</span> <span class="n">src</span><span class="o">/</span><span class="n">libImaging</span><span class="o">/*.</span><span class="n">h</span>
</pre></div>
</div>
</section>
<section id="fixing-shebangs-on-installed-scripts">
<span id="index-8"></span><h2>Fixing shebangs on installed scripts<a class="headerlink" href="#fixing-shebangs-on-installed-scripts" title="Permalink to this headline"></a></h2>
<p>If upstream build system installs Python scripts, it should also update
their shebangs to match the interpreter used for install.  Otherwise,
the scripts could end up being run via another implementation, one
that possible does not have the necessary dependencies installed.
An example of correct shebang is:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="ch">#!/usr/bin/env python3.8</span>
</pre></div>
</div>
<p>However, if the build system installs a script with <code class="docutils literal notranslate"><span class="pre">python3</span></code> or even
<code class="docutils literal notranslate"><span class="pre">python</span></code> shebang, it needs to be updated.  The <code class="docutils literal notranslate"><span class="pre">python_fix_shebang</span></code>
function is provided precisely for that purpose.  It can be used to
update the shebang on an installed file:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">src_install</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">default</span>
    <span class="n">python_fix_shebang</span> <span class="s2">&quot;$</span><span class="si">{D}</span><span class="s2">&quot;</span><span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="nb">bin</span><span class="o">/</span><span class="n">sphinxtrain</span>
<span class="p">}</span>
</pre></div>
</div>
<p>It can also be used in working directory to update a script that’s used
at build time or before it is installed:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">src_prepare</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">default</span>
    <span class="n">python_fix_shebang</span> <span class="n">openvpn</span><span class="o">-</span><span class="n">vulnkey</span>
<span class="p">}</span>
</pre></div>
</div>
<p>Finally, it can also be used on a directory to recursively update
shebangs in all Python scripts found inside it:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">src_install</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">insinto</span> <span class="o">/</span><span class="n">usr</span>
    <span class="n">doins</span> <span class="o">-</span><span class="n">r</span> <span class="n">linux</span><span class="o">-</span><span class="n">package</span><span class="o">/*</span>
    <span class="n">dobin</span> <span class="n">linux</span><span class="o">-</span><span class="n">package</span><span class="o">/</span><span class="nb">bin</span><span class="o">/</span><span class="n">kitty</span>
    <span class="n">python_fix_shebang</span> <span class="s2">&quot;$</span><span class="si">{ED}</span><span class="s2">&quot;</span>
<span class="p">}</span>
</pre></div>
</div>
<p>Normally, <code class="docutils literal notranslate"><span class="pre">python_fix_shebang</span></code> errors out when the target interpreter
is not compatible with the original shebang, e.g. when you are trying
to install a script with <code class="docutils literal notranslate"><span class="pre">python2</span></code> shebang for Python 3.  <code class="docutils literal notranslate"><span class="pre">-f</span></code>
(force) switch can be used to override that:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">src_prepare</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">default</span>
    <span class="n">python_fix_shebang</span> <span class="o">-</span><span class="n">f</span> <span class="s2">&quot;$</span><span class="si">{PN}</span><span class="s2">.py&quot;</span>
<span class="p">}</span>
</pre></div>
</div>
</section>
<section id="byte-compiling-python-modules">
<span id="index-9"></span><h2>Byte-compiling Python modules<a class="headerlink" href="#byte-compiling-python-modules" title="Permalink to this headline"></a></h2>
<p>Python modules are byte compiled in order to speed up their loading.
Byte-compilation is normally done by the build system when the modules
are installed.  However, sometimes packages fail to compile them
entirely, or byte-compile them only partially.  Nowadays, QA checks
detect and report that:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>* This package installs one or more Python modules that are not byte-compiled.
* The following files are missing:
*
*   /usr/lib/pypy2.7/site-packages/_feedparser_sgmllib.pyc
*   /usr/lib64/python2.7/site-packages/_feedparser_sgmllib.pyc
*   /usr/lib64/python2.7/site-packages/_feedparser_sgmllib.pyo
*
* Please either fix the upstream build system to byte-compile Python modules
* correctly, or call python_optimize after installing them.  For more
* information, see:
* https://wiki.gentoo.org/wiki/Project:Python/Byte_compiling
</pre></div>
</div>
<p>The eclass provides a <code class="docutils literal notranslate"><span class="pre">python_optimize</span></code> function to byte-compile
modules.  The most common way of using it is to call it after installing
the package to byte-compile all modules installed into site-packages:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">src_install</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">cmake_src_install</span>
    <span class="n">python_optimize</span>
<span class="p">}</span>
</pre></div>
</div>
<p>If Python scripts are installed to a non-standard directory, the path
to them can be passed to the function:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">src_install</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">cd</span> <span class="s2">&quot;$</span><span class="si">{S}</span><span class="s2">&quot;</span><span class="o">/</span><span class="n">client</span> <span class="o">||</span> <span class="n">die</span>
    <span class="n">emake</span> <span class="n">DESTDIR</span><span class="o">=</span><span class="s2">&quot;$</span><span class="si">{D}</span><span class="s2">&quot;</span> <span class="n">LIBDIR</span><span class="o">=</span><span class="s2">&quot;usr/lib&quot;</span> <span class="n">install</span>
    <span class="n">python_optimize</span> <span class="s2">&quot;$</span><span class="si">{D}</span><span class="s2">/usr/lib/entropy/client&quot;</span>
<span class="p">}</span>
</pre></div>
</div>
<span class="target" id="index-10"></span><span class="target" id="index-11"></span><span class="target" id="index-12"></span><span class="target" id="index-13"></span><span class="target" id="index-14"></span><span class="target" id="index-15"></span></section>
<section id="querying-the-implementation-information">
<span id="index-16"></span><h2>Querying the implementation information<a class="headerlink" href="#querying-the-implementation-information" title="Permalink to this headline"></a></h2>
<p>Most of the time, various build systems manage to detect and query
the Python implementation correctly for necessary build details.
Ocassionally, you need to provide those values or override bad detection
results.  For this purpose, the eclasses provide a series of <em>getters</em>.</p>
<p>The following generic getters are provided:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">python_get_sitedir</span></code> that outputs the absolute path to the target’s
site-packages directory (where Python modules are installed).</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">python_get_includedir</span></code> that outputs the absolute path
to the target-specific header directory.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">python_get_scriptdir</span></code> that outputs the absolute path
to the python-exec script directory for the implementation.</p></li>
</ul>
<p>The following getters are provided only for CPython targets:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">python_get_library_path</span></code> that outputs the absolute path
to the <code class="docutils literal notranslate"><span class="pre">python</span></code> library.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">python_get_CFLAGS</span></code> that outputs the C preprocessor flags
for linking against the Python library (equivalent to <code class="docutils literal notranslate"><span class="pre">pkg-config</span>
<span class="pre">--cflags</span> <span class="pre">...</span></code>).</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">python_get_LIBS</span></code> that outputs the linker flags for linking
against the Python library (equivalent to <code class="docutils literal notranslate"><span class="pre">pkg-config</span> <span class="pre">--libs</span> <span class="pre">...</span></code>).</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">python_get_PYTHON_CONFIG</span></code> that outputs the absolute path
to the <code class="docutils literal notranslate"><span class="pre">python-config</span></code> executable.</p></li>
</ul>
<p>Note that all paths provided by getters include the offset-prefix
(<code class="docutils literal notranslate"><span class="pre">${EPREFIX}</span></code>) already and they are not suitable to passing
to <code class="docutils literal notranslate"><span class="pre">*into</span></code> helpers.  If you need to install something, use <a class="reference internal" href="#install-helpers">install
helpers</a> instead.</p>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>src_configure<span class="o">()</span> <span class="o">{</span>
    <span class="nb">local</span> <span class="nv">mycmakeargs</span><span class="o">=(</span>
        ...
    <span class="o">)</span>
    use python <span class="o">&amp;&amp;</span> <span class="nv">mycmakeargs</span><span class="o">+=(</span>
        -DPYTHON_DEST<span class="o">=</span><span class="s2">&quot;</span><span class="k">$(</span>python_get_sitedir<span class="k">)</span><span class="s2">&quot;</span>
        -DPYTHON_EXECUTABLE<span class="o">=</span><span class="s2">&quot;</span><span class="si">${</span><span class="nv">PYTHON</span><span class="si">}</span><span class="s2">&quot;</span>
        -DPYTHON_INCLUDE_DIR<span class="o">=</span><span class="s2">&quot;</span><span class="k">$(</span>python_get_includedir<span class="k">)</span><span class="s2">&quot;</span>
        -DPYTHON_LIBRARY<span class="o">=</span><span class="s2">&quot;</span><span class="k">$(</span>python_get_library_path<span class="k">)</span><span class="s2">&quot;</span>
    <span class="o">)</span>

    cmake_src_configure
<span class="o">}</span>
</pre></div>
</div>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>python_test<span class="o">()</span> <span class="o">{</span>
    <span class="c1"># prepare embedded executable</span>
    emake <span class="se">\</span>
        <span class="nv">CC</span><span class="o">=</span><span class="s2">&quot;</span><span class="k">$(</span>tc-getCC<span class="k">)</span><span class="s2">&quot;</span> <span class="se">\</span>
        <span class="nv">PYINC</span><span class="o">=</span><span class="s2">&quot;</span><span class="k">$(</span>python_get_CFLAGS<span class="k">)</span><span class="s2">&quot;</span> <span class="se">\</span>
        <span class="nv">PYLIB</span><span class="o">=</span><span class="s2">&quot;</span><span class="k">$(</span>python_get_LIBS<span class="k">)</span><span class="s2">&quot;</span> <span class="se">\</span>
        check
<span class="o">}</span>
</pre></div>
</div>
</section>
</section>


          </div>
          
        </div>
      </div>
      <div class="sphinxsidebar" role="navigation" aria-label="main navigation">
        <div class="sphinxsidebarwrapper">
<h1 class="logo"><a href="index.html">Gentoo Python Guide</a></h1>








<h3>Navigation</h3>
<p class="caption" role="heading"><span class="caption-text">Contents:</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="preface.html">Preface</a></li>
<li class="toctree-l1"><a class="reference internal" href="interpreter.html">Python interpreters</a></li>
<li class="toctree-l1"><a class="reference internal" href="eclass.html">Choosing between Python eclasses</a></li>
<li class="toctree-l1"><a class="reference internal" href="basic.html">Common basics</a></li>
<li class="toctree-l1"><a class="reference internal" href="any.html">python-any-r1 — build-time dependency</a></li>
<li class="toctree-l1"><a class="reference internal" href="single.html">python-single-r1 — single-impl packages</a></li>
<li class="toctree-l1"><a class="reference internal" href="multi.html">python-r1 — multi-impl packages</a></li>
<li class="toctree-l1"><a class="reference internal" href="distutils.html">distutils-r1 — standard Python build systems</a></li>
<li class="toctree-l1"><a class="reference internal" href="distutils-legacy.html">distutils-r1 legacy concepts</a></li>
<li class="toctree-l1 current"><a class="current reference internal" href="#">Common helper functions</a><ul>
<li class="toctree-l2"><a class="reference internal" href="#install-helpers">Install helpers</a></li>
<li class="toctree-l2"><a class="reference internal" href="#fixing-shebangs-on-installed-scripts">Fixing shebangs on installed scripts</a></li>
<li class="toctree-l2"><a class="reference internal" href="#byte-compiling-python-modules">Byte-compiling Python modules</a></li>
<li class="toctree-l2"><a class="reference internal" href="#querying-the-implementation-information">Querying the implementation information</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="depend.html">Advanced dependencies</a></li>
<li class="toctree-l1"><a class="reference internal" href="test.html">Resolving test suite problems</a></li>
<li class="toctree-l1"><a class="reference internal" href="pytest.html">pytest recipes</a></li>
<li class="toctree-l1"><a class="reference internal" href="concept.html">Advanced concepts</a></li>
<li class="toctree-l1"><a class="reference internal" href="expert-multi.html">Expert python-r1 usage</a></li>
<li class="toctree-l1"><a class="reference internal" href="buildsys.html">Integration with build systems written in Python</a></li>
<li class="toctree-l1"><a class="reference internal" href="porting.html">Porting tips</a></li>
<li class="toctree-l1"><a class="reference internal" href="migration.html">Migration guides</a></li>
<li class="toctree-l1"><a class="reference internal" href="package-maintenance.html">Python package maintenance</a></li>
<li class="toctree-l1"><a class="reference internal" href="interpreter-maintenance.html">Maintenance of Python implementations</a></li>
</ul>

<div class="relations">
<h3>Related Topics</h3>
<ul>
  <li><a href="index.html">Documentation overview</a><ul>
      <li>Previous: <a href="distutils-legacy.html" title="previous chapter">distutils-r1 legacy concepts</a></li>
      <li>Next: <a href="depend.html" title="next chapter">Advanced dependencies</a></li>
  </ul></li>
</ul>
</div>
<div id="searchbox" style="display: none" role="search">
  <h3 id="searchlabel">Quick search</h3>
    <div class="searchformwrapper">
    <form class="search" action="search.html" method="get">
      <input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
      <input type="submit" value="Go" />
    </form>
    </div>
</div>
<script>$('#searchbox').show(0);</script>








        </div>
      </div>
      <div class="clearer"></div>
    </div>
    <div class="footer">
      &copy;2020, Michał Górny, license: CC BY 4.0.
      
      |
      Powered by <a href="http://sphinx-doc.org/">Sphinx 4.5.0</a>
      &amp; <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
      
      |
      <a href="_sources/helper.rst.txt"
          rel="nofollow">Page source</a>
    </div>

    

    
  </body>
</html>