Add documentation on build systems (#5015)
Spack provides a number of classes based on commonly-used build systems that users can extend when writing packages; the classes provide functionality to perform the actions relevant to the build system (e.g. running "configure" for an Autotools-based package). This adds documentation for classes supporting the following build systems: * Makefile * Autotools * CMake * QMake * SCons * Waf This includes build systems for managing extensions of the following packages: * Perl * Python * R * Octave This also adds documentation on implementing packages that use a custom build system (e.g. Perl/CMake). Spack also provides extendable classes which aggregate functionality for related sets of packages, e.g. those using CUDA. Documentation is added for CudaPackage.
This commit is contained in:
parent
25062d0bd4
commit
8ce62ba513
83
lib/spack/docs/build_systems.rst
Normal file
83
lib/spack/docs/build_systems.rst
Normal file
@ -0,0 +1,83 @@
|
||||
|
||||
.. _build-systems:
|
||||
|
||||
=============
|
||||
Build Systems
|
||||
=============
|
||||
|
||||
Spack defines a number of classes which understand how to use common
|
||||
`build systems <https://en.wikipedia.org/wiki/List_of_build_automation_software>`_
|
||||
(Makefiles, CMake, etc.). Spack package definitions can inherit these
|
||||
classes in order to streamline their builds.
|
||||
|
||||
This guide provides information specific to each particular build system.
|
||||
It assumes that you've read the :ref:`packaging-guide` and expands
|
||||
on these ideas for each distinct build system that Spack supports:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Make-based
|
||||
|
||||
build_systems/makefilepackage
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Make-incompatible
|
||||
|
||||
build_systems/sconspackage
|
||||
build_systems/wafpackage
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Build-script generation
|
||||
|
||||
build_systems/autotoolspackage
|
||||
build_systems/cmakepackage
|
||||
build_systems/qmakepackage
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Language-specific
|
||||
|
||||
build_systems/octavepackage
|
||||
build_systems/perlpackage
|
||||
build_systems/pythonpackage
|
||||
build_systems/rpackage
|
||||
build_systems/rubypackage
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Other
|
||||
|
||||
build_systems/cudapackage
|
||||
build_systems/intelpackage
|
||||
build_systems/custompackage
|
||||
|
||||
For reference, the :py:mod:`Build System API docs <spack.build_systems>`
|
||||
provide a list of build systems and methods/attributes that can be
|
||||
overridden. If you are curious about the implementation of a particular
|
||||
build system, you can view the source code by running:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ spack edit --build-system autotools
|
||||
|
||||
|
||||
This will open up the ``AutotoolsPackage`` definition in your favorite
|
||||
editor. In addition, if you are working with a less common build system
|
||||
like QMake, SCons, or Waf, it may be useful to see examples of other
|
||||
packages. You can quickly find examples by running:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ cd var/spack/repos/builtin/packages
|
||||
$ grep -l QMakePackage */package.py
|
||||
|
||||
|
||||
You can then view these packages with ``spack edit``.
|
||||
|
||||
This guide is intended to supplement the
|
||||
:py:mod:`Build System API docs <spack.build_systems>` with examples of
|
||||
how to override commonly used methods. It also provides rules of thumb
|
||||
and suggestions for package developers who are unfamiliar with a
|
||||
particular build system.
|
840
lib/spack/docs/build_systems/Autoconf-automake-process.svg
Normal file
840
lib/spack/docs/build_systems/Autoconf-automake-process.svg
Normal file
@ -0,0 +1,840 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generated by graphviz version 2.30.1 (20130303.0813)
|
||||
-->
|
||||
|
||||
<!-- Title: autotools Pages: 1 -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="515pt"
|
||||
height="936pt"
|
||||
viewBox="0.00 0.00 515.00 936.00"
|
||||
id="svg3335"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.4 r9939"
|
||||
sodipodi:docname="Autoconf-automake-process.svg">
|
||||
<metadata
|
||||
id="metadata3645">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs3643" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1309"
|
||||
inkscape:window-height="744"
|
||||
id="namedview3641"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.70163371"
|
||||
inkscape:cx="271.30388"
|
||||
inkscape:cy="758.87622"
|
||||
inkscape:window-x="57"
|
||||
inkscape:window-y="24"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg3335" />
|
||||
<polygon
|
||||
style="fill:#ffffff;stroke:#ffffff"
|
||||
id="polygon3340"
|
||||
points="512,-932 512,5 -4,5 -4,5 -4,-932 "
|
||||
transform="translate(4,932)" />
|
||||
<g
|
||||
class="node"
|
||||
id="node1"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3343">configure.ac</title>
|
||||
<polygon
|
||||
style="fill:#0000f0;stroke:#000000;opacity:1;fill-opacity:0.08627451"
|
||||
id="polygon3345"
|
||||
points="114.75,-818 209.25,-818 209.25,-854 209.25,-854 114.75,-854 " />
|
||||
<text
|
||||
style="font-size:14px;text-anchor:middle;font-family:Liberation Sans"
|
||||
id="text3347"
|
||||
font-size="14.00"
|
||||
y="-832.29999"
|
||||
x="162">configure.ac</text>
|
||||
</g>
|
||||
<g
|
||||
class="node"
|
||||
id="node5"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3350">aclocal</title>
|
||||
<ellipse
|
||||
style="fill:none;stroke:#000000"
|
||||
sodipodi:ry="18"
|
||||
sodipodi:rx="39.469101"
|
||||
sodipodi:cy="-762"
|
||||
sodipodi:cx="66"
|
||||
d="m 105.4691,-762 c 0,9.94113 -17.670917,18 -39.4691,18 -21.798183,0 -39.469101,-8.05887 -39.469101,-18 0,-9.94113 17.670918,-18 39.469101,-18 21.798183,0 39.4691,8.05887 39.4691,18 z"
|
||||
id="ellipse3352"
|
||||
ry="18"
|
||||
rx="39.469101"
|
||||
cy="-762"
|
||||
cx="66" />
|
||||
<text
|
||||
style="font-size:14px;text-anchor:middle;font-family:Liberation Sans"
|
||||
id="text3354"
|
||||
font-size="14.00"
|
||||
y="-758.29999"
|
||||
x="66">aclocal</text>
|
||||
</g>
|
||||
<g
|
||||
class="edge"
|
||||
id="edge3"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3357">configure.ac->aclocal</title>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:12,1;stroke-dashoffset:0"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3359"
|
||||
d="m 139.249,-817.937 c -13.626,10.22 -31.049,23.287 -45.4828,34.112" />
|
||||
<polygon
|
||||
style="fill:#000000;stroke:#000000"
|
||||
id="polygon3361"
|
||||
points="89.6641,-780.748 93.6641,-783.748 93.6641,-783.748 93.6641,-783.748 89.6641,-780.748 90.9641,-787.348 85.6641,-777.748 85.6641,-777.748 85.6641,-777.748 96.3642,-780.148 " />
|
||||
</g>
|
||||
<g
|
||||
class="node"
|
||||
id="node6"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3364">autoconf</title>
|
||||
<ellipse
|
||||
style="fill:none;stroke:#000000"
|
||||
sodipodi:ry="18"
|
||||
sodipodi:rx="46.219101"
|
||||
sodipodi:cy="-614"
|
||||
sodipodi:cx="130"
|
||||
d="m 176.2191,-614 c 0,9.94113 -20.693,18 -46.2191,18 -25.5261,0 -46.219101,-8.05887 -46.219101,-18 0,-9.94113 20.693001,-18 46.219101,-18 25.5261,0 46.2191,8.05887 46.2191,18 z"
|
||||
id="ellipse3366"
|
||||
ry="18"
|
||||
rx="46.219101"
|
||||
cy="-614"
|
||||
cx="130" />
|
||||
<text
|
||||
style="font-size:14px;text-anchor:middle;font-family:Liberation Sans"
|
||||
id="text3368"
|
||||
font-size="14.00"
|
||||
y="-610.29999"
|
||||
x="130">autoconf</text>
|
||||
</g>
|
||||
<g
|
||||
class="edge"
|
||||
id="edge4"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3371">configure.ac->autoconf</title>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:12,1;stroke-dashoffset:0"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3373"
|
||||
d="m 159.489,-817.737 c -5.538,38.073 -18.837,129.506 -25.518,175.436" />
|
||||
<polygon
|
||||
style="fill:#000000;stroke:#000000"
|
||||
id="polygon3375"
|
||||
points="133.219,-637.131 133.939,-642.079 133.939,-642.079 133.939,-642.079 133.219,-637.131 129.486,-642.726 132.499,-632.183 132.499,-632.183 132.499,-632.183 138.392,-641.431 " />
|
||||
</g>
|
||||
<g
|
||||
class="node"
|
||||
id="node7"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3378">autoheader</title>
|
||||
<ellipse
|
||||
style="fill:none;stroke:#000000"
|
||||
sodipodi:ry="18"
|
||||
sodipodi:rx="57.292702"
|
||||
sodipodi:cy="-762"
|
||||
sodipodi:cx="220"
|
||||
d="m 277.2927,-762 c 0,9.94113 -25.65081,18 -57.2927,18 -31.64189,0 -57.2927,-8.05887 -57.2927,-18 0,-9.94113 25.65081,-18 57.2927,-18 31.64189,0 57.2927,8.05887 57.2927,18 z"
|
||||
id="ellipse3380"
|
||||
ry="18"
|
||||
rx="57.292702"
|
||||
cy="-762"
|
||||
cx="220" />
|
||||
<text
|
||||
style="font-size:14px;text-anchor:middle;font-family:Liberation Sans"
|
||||
id="text3382"
|
||||
font-size="14.00"
|
||||
y="-758.29999"
|
||||
x="220">autoheader</text>
|
||||
</g>
|
||||
<g
|
||||
class="edge"
|
||||
id="edge5"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3385">configure.ac->autoheader</title>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:12,1;stroke-dashoffset:0"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3387"
|
||||
d="m 175.745,-817.937 c 7.223,8.967 16.212,20.125 24.197,30.038" />
|
||||
<polygon
|
||||
style="fill:#000000;stroke:#000000"
|
||||
id="polygon3389"
|
||||
points="203.217,-783.834 200.08,-787.728 200.08,-787.728 200.08,-787.728 203.217,-783.834 196.576,-784.905 206.353,-779.941 206.353,-779.941 206.353,-779.941 203.585,-790.551 " />
|
||||
</g>
|
||||
<g
|
||||
class="node"
|
||||
id="node8"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3392">automake</title>
|
||||
<ellipse
|
||||
style="fill:none;stroke:#000000"
|
||||
sodipodi:ry="18"
|
||||
sodipodi:rx="50.542702"
|
||||
sodipodi:cy="-614"
|
||||
sodipodi:cx="309"
|
||||
d="m 359.5427,-614 c 0,9.94113 -22.62874,18 -50.5427,18 -27.91396,0 -50.5427,-8.05887 -50.5427,-18 0,-9.94113 22.62874,-18 50.5427,-18 27.91396,0 50.5427,8.05887 50.5427,18 z"
|
||||
id="ellipse3394"
|
||||
ry="18"
|
||||
rx="50.542702"
|
||||
cy="-614"
|
||||
cx="309" />
|
||||
<text
|
||||
style="font-size:14px;text-anchor:middle;font-family:Liberation Sans"
|
||||
id="text3396"
|
||||
font-size="14.00"
|
||||
y="-610.29999"
|
||||
x="309">automake</text>
|
||||
</g>
|
||||
<g
|
||||
class="edge"
|
||||
id="edge6"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3399">configure.ac->automake</title>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:12,1;stroke-dashoffset:0"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3401"
|
||||
d="m 209.463,-825.823 c 26.664,7.68 58.18,21.469 76.537,45.823 30.292,40.188 30.031,101.971 26.756,137.428" />
|
||||
<polygon
|
||||
style="fill:#000000;stroke:#000000"
|
||||
id="polygon3403"
|
||||
points="312.206,-637.438 312.739,-642.41 312.739,-642.41 312.739,-642.41 312.206,-637.438 308.265,-642.889 311.674,-632.467 311.674,-632.467 311.674,-632.467 317.213,-641.93 " />
|
||||
</g>
|
||||
<g
|
||||
class="node"
|
||||
id="node2"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3406">autoscan</title>
|
||||
<ellipse
|
||||
style="fill:none;stroke:#000000"
|
||||
sodipodi:ry="18"
|
||||
sodipodi:rx="48.1437"
|
||||
sodipodi:cy="-910"
|
||||
sodipodi:cx="162"
|
||||
d="m 210.1437,-910 c 0,9.94113 -21.55467,18 -48.1437,18 -26.58903,0 -48.1437,-8.05887 -48.1437,-18 0,-9.94113 21.55467,-18 48.1437,-18 26.58903,0 48.1437,8.05887 48.1437,18 z"
|
||||
id="ellipse3408"
|
||||
ry="18"
|
||||
rx="48.1437"
|
||||
cy="-910"
|
||||
cx="162" />
|
||||
<text
|
||||
style="font-size:14px;text-anchor:middle;font-family:Liberation Sans"
|
||||
id="text3410"
|
||||
font-size="14.00"
|
||||
y="-906.29999"
|
||||
x="162">autoscan</text>
|
||||
</g>
|
||||
<g
|
||||
class="edge"
|
||||
id="edge1"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3413">autoscan->configure.ac</title>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3415"
|
||||
d="m 162,-891.937 c 0,6.716 0,14.661 0,22.392" />
|
||||
<polygon
|
||||
style="fill:#000000;stroke:#000000"
|
||||
id="polygon3417"
|
||||
points="156.75,-869.441 167.25,-869.441 167.25,-869.441 162,-854.441 " />
|
||||
</g>
|
||||
<g
|
||||
class="node"
|
||||
id="node3"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3420">Makefile.am</title>
|
||||
<polygon
|
||||
style="fill:#0000f0;stroke:#000000;fill-opacity:0.08627451"
|
||||
id="polygon3422"
|
||||
points="333,-670 425,-670 425,-706 425,-706 333,-706 " />
|
||||
<text
|
||||
style="font-size:14px;text-anchor:middle;font-family:Liberation Sans"
|
||||
id="text3424"
|
||||
font-size="14.00"
|
||||
y="-684.29999"
|
||||
x="379">Makefile.am</text>
|
||||
</g>
|
||||
<g
|
||||
class="edge"
|
||||
id="edge10"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3427">Makefile.am->automake</title>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:12,1;stroke-dashoffset:0"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3429"
|
||||
d="m 362.411,-669.937 c -9.021,9.279 -20.323,20.903 -30.204,31.067" />
|
||||
<polygon
|
||||
style="fill:#000000;stroke:#000000"
|
||||
id="polygon3431"
|
||||
points="328.474,-635.03 331.959,-638.615 331.959,-638.615 331.959,-638.615 328.474,-635.03 328.732,-641.752 324.988,-631.445 324.988,-631.445 324.988,-631.445 335.185,-635.478 " />
|
||||
</g>
|
||||
<g
|
||||
class="node"
|
||||
id="node9"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3434">aclocal.m4</title>
|
||||
<polygon
|
||||
style="fill:#0000f0;stroke:#000000;fill-opacity:0.08627451"
|
||||
id="polygon3436"
|
||||
points="21.75,-670 106.25,-670 106.25,-706 106.25,-706 21.75,-706 " />
|
||||
<text
|
||||
style="font-size:14px;text-anchor:middle;font-family:Liberation Sans"
|
||||
id="text3438"
|
||||
font-size="14.00"
|
||||
y="-684.29999"
|
||||
x="64">aclocal.m4</text>
|
||||
</g>
|
||||
<g
|
||||
class="edge"
|
||||
id="edge7"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3441">aclocal->aclocal.m4</title>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3443"
|
||||
d="m 65.526,-743.937 c -0.1865,6.716 -0.4072,14.661 -0.622,22.392" />
|
||||
<polygon
|
||||
style="fill:#000000;stroke:#000000"
|
||||
id="polygon3445"
|
||||
points="59.6531,-721.581 70.149,-721.289 70.149,-721.289 64.4845,-706.441 " />
|
||||
</g>
|
||||
<g
|
||||
class="node"
|
||||
id="node12"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3448">configure</title>
|
||||
<ellipse
|
||||
style="fill:#0000f0;stroke:#000000;fill-opacity:0.08627451"
|
||||
sodipodi:ry="72"
|
||||
sodipodi:rx="72"
|
||||
sodipodi:cy="-486"
|
||||
sodipodi:cx="130"
|
||||
d="m 202,-486 c 0,39.7645 -32.2355,72 -72,72 -39.764502,0 -72,-32.2355 -72,-72 0,-39.7645 32.235498,-72 72,-72 39.7645,0 72,32.2355 72,72 z"
|
||||
id="ellipse3450"
|
||||
ry="72"
|
||||
rx="72"
|
||||
cy="-486"
|
||||
cx="130" />
|
||||
<polyline
|
||||
style="fill:none;stroke:#000000"
|
||||
id="polyline3452"
|
||||
points="177.621,-540 82.3792,-540 " />
|
||||
<polyline
|
||||
style="fill:none;stroke:#000000"
|
||||
id="polyline3454"
|
||||
points="177.621,-432 82.3792,-432 " />
|
||||
<text
|
||||
style="font-size:14px;text-anchor:middle;font-family:Liberation Sans"
|
||||
id="text3456"
|
||||
font-size="14.00"
|
||||
y="-482.29999"
|
||||
x="130">configure</text>
|
||||
</g>
|
||||
<g
|
||||
class="edge"
|
||||
id="edge13"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3459">autoconf->configure</title>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3461"
|
||||
d="m 130,-595.744 c 0,6.397 0,14.16 0,22.563" />
|
||||
<polygon
|
||||
style="fill:#000000;stroke:#000000"
|
||||
id="polygon3463"
|
||||
points="124.75,-573.051 135.25,-573.051 135.25,-573.051 130,-558.051 " />
|
||||
</g>
|
||||
<g
|
||||
class="node"
|
||||
id="node10"
|
||||
transform="translate(7.9661017,932)">
|
||||
<title
|
||||
id="title3466">config.h.in</title>
|
||||
<polygon
|
||||
style="fill:#0000f0;stroke:#000000;fill-opacity:0.08627451"
|
||||
id="polygon3468"
|
||||
points="260.25,-670 260.25,-706 260.25,-706 179.75,-706 179.75,-670 " />
|
||||
<text
|
||||
style="font-size:14px;text-anchor:middle;font-family:Liberation Sans"
|
||||
id="text3470"
|
||||
font-size="14.00"
|
||||
y="-684.29999"
|
||||
x="220">config.h.in</text>
|
||||
</g>
|
||||
<g
|
||||
class="edge"
|
||||
id="edge8"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3473">autoheader->config.h.in</title>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3475"
|
||||
d="m 220,-743.937 c 0,6.716 0,14.661 0,22.392" />
|
||||
<polygon
|
||||
style="fill:#000000;stroke:#000000"
|
||||
id="polygon3477"
|
||||
points="214.75,-721.441 225.25,-721.441 225.25,-721.441 220,-706.441 " />
|
||||
</g>
|
||||
<g
|
||||
class="node"
|
||||
id="node11"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3480">Makefile.in</title>
|
||||
<polygon
|
||||
style="fill:#0000f0;stroke:#000000;fill-opacity:0.08627451"
|
||||
id="polygon3482"
|
||||
points="260.75,-468 343.25,-468 343.25,-504 343.25,-504 260.75,-504 " />
|
||||
<text
|
||||
style="font-size:14px;text-anchor:middle;font-family:Liberation Sans"
|
||||
id="text3484"
|
||||
font-size="14.00"
|
||||
y="-482.29999"
|
||||
x="302">Makefile.in</text>
|
||||
</g>
|
||||
<g
|
||||
class="edge"
|
||||
id="edge11"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3487">automake->Makefile.in</title>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3489"
|
||||
d="m 308.041,-595.744 c -1.089,19.604 -2.89,52.031 -4.243,76.384" />
|
||||
<polygon
|
||||
style="fill:#000000;stroke:#000000"
|
||||
id="polygon3491"
|
||||
points="298.537,-519.315 309.021,-518.733 309.021,-518.733 302.947,-504.047 " />
|
||||
</g>
|
||||
<g
|
||||
class="edge"
|
||||
id="edge12"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3494">aclocal.m4->autoconf</title>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:12,1;stroke-dashoffset:0"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3496"
|
||||
d="m 79.6411,-669.937 c 8.5054,9.279 19.1615,20.903 28.4789,31.067" />
|
||||
<polygon
|
||||
style="fill:#000000;stroke:#000000"
|
||||
id="polygon3498"
|
||||
points="111.547,-635.131 108.168,-638.816 108.168,-638.816 108.168,-638.816 111.547,-635.131 104.851,-635.776 114.925,-631.445 114.925,-631.445 114.925,-631.445 111.485,-641.857 " />
|
||||
</g>
|
||||
<g
|
||||
class="edge"
|
||||
id="edge9"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3501">config.h.in->automake</title>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:12,1;stroke-dashoffset:0"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3503"
|
||||
d="m 241.092,-669.937 c 12.174,9.849 27.617,22.342 40.698,32.925" />
|
||||
<polygon
|
||||
style="fill:#000000;stroke:#000000"
|
||||
id="polygon3505"
|
||||
points="285.693,-633.855 281.806,-637 281.806,-637 281.806,-637 285.693,-633.855 278.975,-633.501 289.58,-630.71 289.58,-630.71 289.58,-630.71 284.636,-640.498 " />
|
||||
</g>
|
||||
<g
|
||||
class="node"
|
||||
id="node15"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3508">config.status</title>
|
||||
<ellipse
|
||||
style="fill:#0000f0;stroke:#000000;fill-opacity:0.08627451"
|
||||
sodipodi:ry="63.088799"
|
||||
sodipodi:rx="63.066601"
|
||||
sodipodi:cy="-313"
|
||||
sodipodi:cx="230"
|
||||
d="m 293.0666,-313 c 0,34.84298 -28.23588,63.0888 -63.0666,63.0888 -34.83072,0 -63.0666,-28.24582 -63.0666,-63.0888 0,-34.84298 28.23588,-63.0888 63.0666,-63.0888 34.83072,0 63.0666,28.24582 63.0666,63.0888 z"
|
||||
id="ellipse3510"
|
||||
ry="63.088799"
|
||||
rx="63.066601"
|
||||
cy="-313"
|
||||
cx="230" />
|
||||
<polyline
|
||||
style="fill:none;stroke:#000000"
|
||||
id="polyline3512"
|
||||
points="271.668,-360.283 188.332,-360.283 " />
|
||||
<polyline
|
||||
style="fill:none;stroke:#000000"
|
||||
id="polyline3514"
|
||||
points="271.668,-265.717 188.332,-265.717 " />
|
||||
<text
|
||||
style="font-size:14px;text-anchor:middle;font-family:Liberation Sans"
|
||||
id="text3516"
|
||||
font-size="14.00"
|
||||
y="-309.29999"
|
||||
x="230">config.status</text>
|
||||
</g>
|
||||
<g
|
||||
class="edge"
|
||||
id="edge14"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3519">config.h.in->config.status</title>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:12,1;stroke-dashoffset:0"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3521"
|
||||
d="m 227.40968,-670.00576 c 1.335,49.807 -1.72568,195.00376 0.65132,283.69476"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<polygon
|
||||
style="fill:#000000;stroke:#000000"
|
||||
id="polygon3523"
|
||||
points="228.202,-381.047 228.068,-386.045 228.068,-386.045 228.068,-386.045 228.202,-381.047 223.57,-385.925 228.336,-376.049 228.336,-376.049 228.336,-376.049 232.567,-386.166 " />
|
||||
</g>
|
||||
<g
|
||||
class="edge"
|
||||
id="edge15"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3526">Makefile.in->config.status</title>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:12,1;stroke-dashoffset:0"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3528"
|
||||
d="m 294.833,-467.979 c -8.579,20.376 -23.463,55.724 -36.807,87.417" />
|
||||
<polygon
|
||||
style="fill:#000000;stroke:#000000"
|
||||
id="polygon3530"
|
||||
points="256.067,-375.909 258.007,-380.517 258.007,-380.517 258.007,-380.517 256.067,-375.909 253.86,-382.264 254.127,-371.301 254.127,-371.301 254.127,-371.301 262.155,-378.771 " />
|
||||
</g>
|
||||
<g
|
||||
class="edge"
|
||||
id="edge16"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3533">configure->config.status</title>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3535"
|
||||
d="m 166.102,-423.265 c 8.055,13.774 16.633,28.443 24.803,42.413" />
|
||||
<polygon
|
||||
style="fill:#000000;stroke:#000000"
|
||||
id="polygon3537"
|
||||
points="186.478,-378.022 195.542,-383.323 195.542,-383.323 198.582,-367.724 " />
|
||||
</g>
|
||||
<g
|
||||
class="node"
|
||||
id="node13"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3540">config.h</title>
|
||||
<polygon
|
||||
style="fill:#0000f0;stroke:#000000;fill-opacity:0.08627451"
|
||||
id="polygon3542"
|
||||
points="110.75,-176 177.25,-176 177.25,-212 177.25,-212 110.75,-212 " />
|
||||
<text
|
||||
style="font-size:14px;text-anchor:middle;font-family:Liberation Sans"
|
||||
id="text3544"
|
||||
font-size="14.00"
|
||||
y="-190.3"
|
||||
x="144">config.h</text>
|
||||
</g>
|
||||
<g
|
||||
class="node"
|
||||
id="node18"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3547">make</title>
|
||||
<ellipse
|
||||
style="fill:none;stroke:#000000"
|
||||
sodipodi:ry="18"
|
||||
sodipodi:rx="33.220901"
|
||||
sodipodi:cy="-106"
|
||||
sodipodi:cx="230"
|
||||
d="m 263.2209,-106 c 0,9.941125 -14.8735,18 -33.2209,18 -18.3474,0 -33.2209,-8.058875 -33.2209,-18 0,-9.94113 14.8735,-18 33.2209,-18 18.3474,0 33.2209,8.05887 33.2209,18 z"
|
||||
id="ellipse3549"
|
||||
ry="18"
|
||||
rx="33.220901"
|
||||
cy="-106"
|
||||
cx="230" />
|
||||
<text
|
||||
style="font-size:14px;text-anchor:middle;font-family:Liberation Sans"
|
||||
id="text3551"
|
||||
font-size="14.00"
|
||||
y="-102.3"
|
||||
x="230">make</text>
|
||||
</g>
|
||||
<g
|
||||
class="edge"
|
||||
id="edge21"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3554">config.h->make</title>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:12,1;stroke-dashoffset:0"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3556"
|
||||
d="m 161.403,-175.597 c 13.375,13.375 31.944,31.944 46.39,46.39" />
|
||||
<polygon
|
||||
style="fill:#000000;stroke:#000000"
|
||||
id="polygon3558"
|
||||
points="211.386,-125.614 207.85,-129.15 207.85,-129.15 207.85,-129.15 211.386,-125.614 204.668,-125.968 214.922,-122.078 214.922,-122.078 214.922,-122.078 211.032,-132.332 " />
|
||||
</g>
|
||||
<g
|
||||
class="node"
|
||||
id="node14"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3561">Makefile</title>
|
||||
<polygon
|
||||
style="fill:#0000f0;stroke:#000000;fill-opacity:0.08627451"
|
||||
id="polygon3563"
|
||||
points="195.75,-176 264.25,-176 264.25,-212 264.25,-212 195.75,-212 " />
|
||||
<text
|
||||
style="font-size:14px;text-anchor:middle;font-family:Liberation Sans"
|
||||
id="text3565"
|
||||
font-size="14.00"
|
||||
y="-190.3"
|
||||
x="230">Makefile</text>
|
||||
</g>
|
||||
<g
|
||||
class="edge"
|
||||
id="edge22"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3568">Makefile->make</title>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:12,1;stroke-dashoffset:0"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3570"
|
||||
d="m 230,-175.597 c 0,11.851 0,27.78 0,41.305" />
|
||||
<polygon
|
||||
style="fill:#000000;stroke:#000000"
|
||||
id="polygon3572"
|
||||
points="230,-129.084 230,-134.084 230,-134.084 230,-134.084 230,-129.084 225.5,-134.084 230,-124.084 230,-124.084 230,-124.084 234.5,-134.084 " />
|
||||
</g>
|
||||
<g
|
||||
class="edge"
|
||||
id="edge18"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3575">config.status->config.h</title>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3577"
|
||||
d="m 193.029,-261.702 c -9.358,12.731 -19.105,25.992 -27.42,37.304" />
|
||||
<polygon
|
||||
style="fill:#000000;stroke:#000000"
|
||||
id="polygon3579"
|
||||
points="161.268,-227.357 169.729,-221.138 169.729,-221.138 156.615,-212.162 " />
|
||||
</g>
|
||||
<g
|
||||
class="edge"
|
||||
id="edge19"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3582">config.status->Makefile</title>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3584"
|
||||
d="m 230,-249.732 c 0,7.628 0,15.171 0,22.106" />
|
||||
<polygon
|
||||
style="fill:#000000;stroke:#000000"
|
||||
id="polygon3586"
|
||||
points="224.75,-227.368 235.25,-227.368 235.25,-227.368 230,-212.368 " />
|
||||
</g>
|
||||
<g
|
||||
class="node"
|
||||
id="node20"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3589">input file</title>
|
||||
<polygon
|
||||
style="fill:#0000f0;stroke:#000000;fill-opacity:0.08627451"
|
||||
id="polygon3591"
|
||||
points="393,-176 461,-176 461,-212 461,-212 393,-212 " />
|
||||
<text
|
||||
style="font-size:14px;font-style:italic;text-anchor:start;font-family:URW Palladio L"
|
||||
id="text3593"
|
||||
font-size="14.00"
|
||||
font-style="italic"
|
||||
y="-191.3"
|
||||
x="401.5">input file</text>
|
||||
</g>
|
||||
<g
|
||||
class="node"
|
||||
id="node19"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3596">executable</title>
|
||||
<polygon
|
||||
style="fill:#00c800;stroke:#000000;fill-opacity:0.19607843"
|
||||
id="polygon3598"
|
||||
points="192.75,0 267.25,0 267.25,-36 267.25,-36 192.75,-36 " />
|
||||
<text
|
||||
style="font-size:14px;font-style:italic;text-anchor:start;font-family:URW Palladio L"
|
||||
id="text3600"
|
||||
font-size="14.00"
|
||||
font-style="italic"
|
||||
y="-15.3"
|
||||
x="201">executable</text>
|
||||
</g>
|
||||
<g
|
||||
class="edge"
|
||||
id="edge24"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3603">make->executable</title>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3605"
|
||||
d="m 230,-87.5966 c 0,10.4427 0,24.0522 0,36.3931" />
|
||||
<polygon
|
||||
style="fill:#000000;stroke:#000000"
|
||||
id="polygon3607"
|
||||
points="224.75,-51.084 235.25,-51.084 235.25,-51.084 230,-36.084 " />
|
||||
</g>
|
||||
<g
|
||||
class="node"
|
||||
id="node21"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3610">process</title>
|
||||
<ellipse
|
||||
style="fill:none;stroke:#000000"
|
||||
sodipodi:ry="18"
|
||||
sodipodi:rx="37.070099"
|
||||
sodipodi:cy="-106"
|
||||
sodipodi:cx="427"
|
||||
d="m 464.0701,-106 c 0,9.941125 -16.59685,18 -37.0701,18 -20.47325,0 -37.0701,-8.058875 -37.0701,-18 0,-9.94113 16.59685,-18 37.0701,-18 20.47325,0 37.0701,8.05887 37.0701,18 z"
|
||||
id="ellipse3612"
|
||||
ry="18"
|
||||
rx="37.070099"
|
||||
cy="-106"
|
||||
cx="427" />
|
||||
<text
|
||||
style="font-size:14px;font-style:italic;text-anchor:start;font-family:URW Palladio L"
|
||||
id="text3614"
|
||||
font-size="14.00"
|
||||
font-style="italic"
|
||||
y="-103.3"
|
||||
x="407">process</text>
|
||||
</g>
|
||||
<g
|
||||
class="edge"
|
||||
id="edge26"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3617">input file->process</title>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:12,1;stroke-dashoffset:0"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3619"
|
||||
d="m 427,-175.597 c 0,11.851 0,27.78 0,41.305" />
|
||||
<polygon
|
||||
style="fill:#000000;stroke:#000000"
|
||||
id="polygon3621"
|
||||
points="427,-129.084 427,-134.084 427,-134.084 427,-134.084 427,-129.084 422.5,-134.084 427,-124.084 427,-124.084 427,-124.084 431.5,-134.084 " />
|
||||
<text
|
||||
style="font-size:14px;text-anchor:middle;font-family:Liberation Sans"
|
||||
id="text3623"
|
||||
font-size="14.00"
|
||||
y="-146.3"
|
||||
x="467"> influences </text>
|
||||
</g>
|
||||
<g
|
||||
class="node"
|
||||
id="node22"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3626">output file</title>
|
||||
<polygon
|
||||
style="fill:#00c800;stroke:#000000;fill-opacity:0.19607843"
|
||||
id="polygon3628"
|
||||
points="389.75,0 464.25,0 464.25,-36 464.25,-36 389.75,-36 " />
|
||||
<text
|
||||
style="font-size:14px;font-style:italic;text-anchor:start;font-family:URW Palladio L"
|
||||
id="text3630"
|
||||
font-size="14.00"
|
||||
font-style="italic"
|
||||
y="-15.3"
|
||||
x="398">output file</text>
|
||||
</g>
|
||||
<g
|
||||
class="edge"
|
||||
id="edge27"
|
||||
transform="translate(4,932)">
|
||||
<title
|
||||
id="title3633">process->output file</title>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3635"
|
||||
d="m 427,-87.5966 c 0,10.4427 0,24.0522 0,36.3931" />
|
||||
<polygon
|
||||
style="fill:#000000;stroke:#000000"
|
||||
id="polygon3637"
|
||||
points="421.75,-51.084 432.25,-51.084 432.25,-51.084 427,-36.084 " />
|
||||
<text
|
||||
style="font-size:14px;text-anchor:middle;font-family:Liberation Sans"
|
||||
id="text3639"
|
||||
font-size="14.00"
|
||||
y="-58.299999"
|
||||
x="458.5"> creates </text>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 27 KiB |
300
lib/spack/docs/build_systems/autotoolspackage.rst
Normal file
300
lib/spack/docs/build_systems/autotoolspackage.rst
Normal file
@ -0,0 +1,300 @@
|
||||
.. _autotoolspackage:
|
||||
|
||||
----------------
|
||||
AutotoolsPackage
|
||||
----------------
|
||||
|
||||
Autotools is a GNU build system that provides a build-script generator.
|
||||
By running the platform-independent ``./configure`` script that comes
|
||||
with the package, you can generate a platform-dependent Makefile.
|
||||
|
||||
^^^^^^
|
||||
Phases
|
||||
^^^^^^
|
||||
|
||||
The ``AutotoolsPackage`` base class comes with the following phases:
|
||||
|
||||
#. ``autoreconf`` - generate the configure script
|
||||
#. ``configure`` - generate the Makefiles
|
||||
#. ``build`` - build the package
|
||||
#. ``install`` - install the package
|
||||
|
||||
Most of the time, the ``autoreconf`` phase will do nothing, but if the
|
||||
package is missing a ``configure`` script, ``autoreconf`` will generate
|
||||
one for you.
|
||||
|
||||
The other phases run:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ ./configure --prefix=/path/to/installation/prefix
|
||||
$ make
|
||||
$ make check # optional
|
||||
$ make install
|
||||
$ make installcheck # optional
|
||||
|
||||
|
||||
Of course, you may need to add a few arguments to the ``./configure``
|
||||
line.
|
||||
|
||||
^^^^^^^^^^^^^^^
|
||||
Important files
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
The most important file for an Autotools-based package is the ``configure``
|
||||
script. This script is automatically generated by Autotools and generates
|
||||
the appropriate Makefile when run.
|
||||
|
||||
.. warning::
|
||||
|
||||
Watch out for fake Autotools packages!
|
||||
|
||||
Autotools is a very popular build system, and many people are used to the
|
||||
classic steps to install a package:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ ./configure
|
||||
$ make
|
||||
$ make install
|
||||
|
||||
|
||||
For this reason, some developers will write their own ``configure``
|
||||
scripts that have nothing to do with Autotools. These packages may
|
||||
not accept the same flags as other Autotools packages, so it is
|
||||
better to use the ``Package`` base class and create a
|
||||
:ref:`custom build system <custompackage>`. You can tell if a package
|
||||
uses Autotools by running ``./configure --help`` and comparing the output
|
||||
to other known Autotools packages. You should also look for files like:
|
||||
|
||||
* ``configure.ac``
|
||||
* ``configure.in``
|
||||
* ``Makefile.am``
|
||||
|
||||
Packages that don't use Autotools aren't likely to have these files.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Build system dependencies
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Whether or not your package requires Autotools to install depends on
|
||||
how the source code is distributed. Most of the time, when developers
|
||||
distribute tarballs, they will already contain the ``configure`` script
|
||||
necessary for installation. If this is the case, your package does not
|
||||
require any Autotools dependencies.
|
||||
|
||||
However, a basic rule of version control systems is to never commit
|
||||
code that can be generated. The source code repository itself likely
|
||||
does not have a ``configure`` script. Developers typically write
|
||||
(or auto-generate) a ``configure.ac`` script that contains configuration
|
||||
preferences and a ``Makefile.am`` script that contains build instructions.
|
||||
Then, ``autoconf`` is used to convert ``configure.ac`` into ``configure``,
|
||||
while ``automake`` is used to convert ``Makefile.am`` into ``Makefile.in``.
|
||||
``Makefile.in`` is used by ``configure`` to generate a platform-dependent
|
||||
``Makefile`` for you. The following diagram provides a high-level overview
|
||||
of the process:
|
||||
|
||||
.. figure:: Autoconf-automake-process.*
|
||||
:target: https://commons.wikimedia.org/w/index.php?curid=15581407
|
||||
|
||||
`GNU autoconf and automake process for generating makefiles <https://commons.wikimedia.org/wiki/File:Autoconf-automake-process.svg>`_
|
||||
by `Jdthood` under `CC BY-SA 3.0 <https://creativecommons.org/licenses/by-sa/3.0/deed.en>`_
|
||||
|
||||
If a ``configure`` script is not present in your tarball, you will
|
||||
need to generate one yourself. Luckily, Spack already has an ``autoreconf``
|
||||
phase to do most of the work for you. By default, the ``autoreconf``
|
||||
phase runs:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ libtoolize
|
||||
$ aclocal
|
||||
$ autoreconf --install --verbose --force
|
||||
|
||||
All you need to do is add a few Autotools dependencies to the package.
|
||||
Most stable releases will come with a ``configure`` script, but if you
|
||||
check out a commit from the ``develop`` branch, you would want to add:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
depends_on('autoconf', type='build', when='@develop')
|
||||
depends_on('automake', type='build', when='@develop')
|
||||
depends_on('libtool', type='build', when='@develop')
|
||||
depends_on('m4', type='build', when='@develop')
|
||||
|
||||
In some cases, developers might need to distribute a patch that modifies
|
||||
one of the files used to generate ``configure`` or ``Makefile.in``.
|
||||
In this case, these scripts will need to be regenerated. It is
|
||||
preferable to regenerate these manually using the patch, and then
|
||||
create a new patch that directly modifies ``configure``. That way,
|
||||
Spack can use the secondary patch and additional build system
|
||||
dependencies aren't necessary.
|
||||
|
||||
""""""""""""""""
|
||||
force_autoreconf
|
||||
""""""""""""""""
|
||||
|
||||
If for whatever reason you really want to add the original patch
|
||||
and tell Spack to regenerate ``configure``, you can do so using the
|
||||
following setting:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
force_autoreconf = True
|
||||
|
||||
This line tells Spack to wipe away the existing ``configure`` script
|
||||
and generate a new one. If you only need to do this for a single
|
||||
version, this can be done like so:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@property
|
||||
def force_autoreconf(self):
|
||||
return self.version == Version('1.2.3'):
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Finding configure flags
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Once you have a ``configure`` script present, the next step is to
|
||||
determine what option flags are available. These flags can be found
|
||||
by running:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ ./configure --help
|
||||
|
||||
``configure`` will display a list of valid flags separated into
|
||||
some or all of the following sections:
|
||||
|
||||
* Configuration
|
||||
* Installation directories
|
||||
* Fine tuning of the installation directories
|
||||
* Program names
|
||||
* X features
|
||||
* System types
|
||||
* **Optional Features**
|
||||
* **Optional Packages**
|
||||
* **Some influential environment variables**
|
||||
|
||||
For the most part, you can ignore all but the last 3 sections.
|
||||
The "Optional Features" sections lists flags that enable/disable
|
||||
features you may be interested in. The "Optional Packages" section
|
||||
often lists dependencies and the flags needed to locate them. The
|
||||
"environment variables" section lists environment variables that the
|
||||
build system uses to pass flags to the compiler and linker.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Addings flags to configure
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
For most of the flags you encounter, you will want a variant to
|
||||
optionally enable/disable them. You can then optionally pass these
|
||||
flags to the ``configure`` call by overriding the ``configure_args``
|
||||
function like so:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def configure_args(self):
|
||||
args = []
|
||||
|
||||
if '+mpi' in self.spec:
|
||||
args.append('--enable-mpi')
|
||||
else:
|
||||
args.append('--disable-mpi')
|
||||
|
||||
return args
|
||||
|
||||
Note that we are explicitly disabling MPI support if it is not
|
||||
requested. This is important, as many Autotools packages will enable
|
||||
options by default if the dependencies are found, and disable them
|
||||
otherwise. We want Spack installations to be as deterministic as possible.
|
||||
If two users install a package with the same variants, the goal is that
|
||||
both installations work the same way. See `here <https://www.linux.com/news/best-practices-autotools>`__
|
||||
and `here <https://wiki.gentoo.org/wiki/Project:Quality_Assurance/Automagic_dependencies>`__
|
||||
for a rationale as to why these so-called "automagic" dependencies
|
||||
are a problem.
|
||||
|
||||
By default, Autotools installs packages to ``/usr``. We don't want this,
|
||||
so Spack automatically adds ``--prefix=/path/to/installation/prefix``
|
||||
to your list of ``configure_args``. You don't need to add this yourself.
|
||||
|
||||
^^^^^^^^^^^^^^^^
|
||||
Helper functions
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
You may have noticed that most of the Autotools flags are of the form
|
||||
``--enable-foo``, ``--disable-bar``, ``--with-baz=<prefix>``, or
|
||||
``--without-baz``. Since these flags are so common, Spack provides a
|
||||
couple of helper functions to make your life easier.
|
||||
|
||||
TODO: document ``with_or_without`` and ``enable_or_disable``.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Configure script in a sub-directory
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Occasionally, developers will hide their source code and ``configure``
|
||||
script in a subdirectory like ``src``. If this happens, Spack won't
|
||||
be able to automatically detect the build system properly when running
|
||||
``spack create``. You will have to manually change the package base
|
||||
class and tell Spack where the ``configure`` script resides. You can
|
||||
do this like so:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
configure_directory = 'src'
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
Building out of source
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Some packages like ``gcc`` recommend building their software in a
|
||||
different directory than the source code to prevent build pollution.
|
||||
This can be done using the ``build_directory`` variable:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
build_directory = 'spack-build'
|
||||
|
||||
By default, Spack will build the package in the same directory that
|
||||
contains the ``configure`` script
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Build and install targets
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
For most Autotools packages, the usual:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ configure
|
||||
$ make
|
||||
$ make install
|
||||
|
||||
is sufficient to install the package. However, if you need to run
|
||||
make with any other targets, for example, to build an optional
|
||||
library or build the documentation, you can add these like so:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
build_targets = ['all', 'docs']
|
||||
install_targets = ['install', 'docs']
|
||||
|
||||
^^^^^^^
|
||||
Testing
|
||||
^^^^^^^
|
||||
|
||||
Autotools-based packages typically provide unit testing via the
|
||||
``check`` and ``installcheck`` targets. If you build your software
|
||||
with ``spack install --test=root``, Spack will check for the presence
|
||||
of a ``check`` or ``test`` target in the Makefile and run
|
||||
``make check`` for you. After installation, it will check for an
|
||||
``installcheck`` target and run ``make installcheck`` if it finds one.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
External documentation
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
For more information on the Autotools build system, see:
|
||||
https://www.gnu.org/software/automake/manual/html_node/Autotools-Introduction.html
|
274
lib/spack/docs/build_systems/cmakepackage.rst
Normal file
274
lib/spack/docs/build_systems/cmakepackage.rst
Normal file
@ -0,0 +1,274 @@
|
||||
.. _cmakepackage:
|
||||
|
||||
------------
|
||||
CMakePackage
|
||||
------------
|
||||
|
||||
Like Autotools, CMake is a widely-used build-script generator. Designed
|
||||
by Kitware, CMake is the most popular build system for new C, C++, and
|
||||
Fortran projects, and many older projects are switching to it as well.
|
||||
|
||||
Unlike Autotools, CMake can generate build scripts for builders other
|
||||
than Make: Ninja, Visual Studio, etc. It is therefore cross-platform,
|
||||
whereas Autotools is Unix-only.
|
||||
|
||||
^^^^^^
|
||||
Phases
|
||||
^^^^^^
|
||||
|
||||
The ``CMakePackage`` base class comes with the following phases:
|
||||
|
||||
#. ``cmake`` - generate the Makefile
|
||||
#. ``build`` - build the package
|
||||
#. ``install`` - install the package
|
||||
|
||||
By default, these phases run:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ mkdir spack-build
|
||||
$ cd spack-build
|
||||
$ cmake .. -DCMAKE_INSTALL_PREFIX=/path/to/installation/prefix
|
||||
$ make
|
||||
$ make test # optional
|
||||
$ make install
|
||||
|
||||
|
||||
A few more flags are passed to ``cmake`` by default, including flags
|
||||
for setting the build type and flags for locating dependencies. Of
|
||||
course, you may need to add a few arguments yourself.
|
||||
|
||||
^^^^^^^^^^^^^^^
|
||||
Important files
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
A CMake-based package can be identified by the presence of a
|
||||
``CMakeLists.txt`` file. This file defines the build flags that can be
|
||||
passed to the cmake invocation, as well as linking instructions. If
|
||||
you are familiar with CMake, it can prove very useful for determining
|
||||
dependencies and dependency version requirements.
|
||||
|
||||
One thing to look for is the ``cmake_minimum_required`` function:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
cmake_minimum_required(VERSION 2.8.12)
|
||||
|
||||
|
||||
This means that CMake 2.8.12 is the earliest release that will work.
|
||||
You should specify this in a ``depends_on`` statement.
|
||||
|
||||
CMake-based packages may also contain ``CMakeLists.txt`` in subdirectories.
|
||||
This modularization helps to manage complex builds in a hierarchical
|
||||
fashion. Sometimes these nested ``CMakeLists.txt`` require additional
|
||||
dependencies not mentioned in the top-level file.
|
||||
|
||||
There's also usually a ``cmake`` or ``CMake`` directory containing
|
||||
additional macros, find scripts, etc. These may prove useful in
|
||||
determining dependency version requirements.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Build system dependencies
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Every package that uses the CMake build system requires a ``cmake``
|
||||
dependency. Since this is always the case, the ``CMakePackage`` base
|
||||
class already contains:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
depends_on('cmake', type='build')
|
||||
|
||||
|
||||
If you need to specify a particular version requirement, you can
|
||||
override this in your package:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
depends_on('cmake@2.8.12:', type='build')
|
||||
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
Finding cmake flags
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
To get a list of valid flags that can be passed to ``cmake``, run the
|
||||
following command in the directory that contains ``CMakeLists.txt``:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ cmake . -LAH
|
||||
|
||||
|
||||
CMake will start by checking for compilers and dependencies. Eventually
|
||||
it will begin to list build options. You'll notice that most of the
|
||||
build options at the top are prefixed with ``CMAKE_``. You can safely
|
||||
ignore most of these options as Spack already sets them for you. This
|
||||
includes flags needed to locate dependencies, RPATH libraries, set the
|
||||
installation directory, and set the build type.
|
||||
|
||||
The rest of the flags are the ones you should consider adding to your
|
||||
package. They often include flags to enable/disable support for certain
|
||||
features and locate specific dependencies. One thing you'll notice that
|
||||
makes CMake different from Autotools is that CMake has an understanding
|
||||
of build flag hierarchy. That is, certain flags will not display unless
|
||||
their parent flag has been selected. For example, flags to specify the
|
||||
``lib`` and ``include`` directories for a package might not appear
|
||||
unless CMake found the dependency it was looking for. You may need to
|
||||
manually specify certain flags to explore the full depth of supported
|
||||
build flags, or check the ``CMakeLists.txt`` yourself.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
Adding flags to cmake
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
To add additional flags to the ``cmake`` call, simply override the
|
||||
``cmake_args`` function:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def cmake_args(self):
|
||||
args = []
|
||||
|
||||
if '+hdf5' in self.spec:
|
||||
args.append('-DDETECT_HDF5=ON')
|
||||
else:
|
||||
args.append('-DDETECT_HDF5=OFF')
|
||||
|
||||
return args
|
||||
|
||||
|
||||
^^^^^^^^^^
|
||||
Generators
|
||||
^^^^^^^^^^
|
||||
|
||||
CMake and Autotools are build-script generation tools; they "generate"
|
||||
the Makefiles that are used to build a software package. CMake actually
|
||||
supports multiple generators, not just Makefiles. Another common
|
||||
generator is Ninja. To switch to the Ninja generator, simply add:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
generator = 'Ninja'
|
||||
|
||||
|
||||
``CMakePackage`` defaults to "Unix Makefiles". If you switch to the
|
||||
Ninja generator, make sure to add:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
depends_on('ninja', type='build')
|
||||
|
||||
to the package as well. Aside from that, you shouldn't need to do
|
||||
anything else. Spack will automatically detect that you are using
|
||||
Ninja and run:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ cmake .. -G Ninja
|
||||
$ ninja
|
||||
$ ninja install
|
||||
|
||||
Spack currently only supports "Unix Makefiles" and "Ninja" as valid
|
||||
generators, but it should be simple to add support for alternative
|
||||
generators. For more information on CMake generators, see:
|
||||
https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html
|
||||
|
||||
^^^^^^^^^^^^^^^^
|
||||
CMAKE_BUILD_TYPE
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
Every CMake-based package accepts a ``-DCMAKE_BUILD_TYPE`` flag to
|
||||
dictate which level of optimization to use. In order to ensure
|
||||
uniformity across packages, the ``CMakePackage`` base class adds
|
||||
a variant to control this:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
variant('build_type', default='RelWithDebInfo',
|
||||
description='CMake build type',
|
||||
values=('Debug', 'Release', 'RelWithDebInfo', 'MinSizeRel'))
|
||||
|
||||
However, not every CMake package accepts all four of these options.
|
||||
Grep the ``CMakeLists.txt`` file to see if the default values are
|
||||
missing or replaced. For example, the
|
||||
`dealii <https://github.com/spack/spack/blob/develop/var/spack/repos/builtin/packages/dealii/package.py>`_
|
||||
package overrides the default variant with:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
variant('build_type', default='DebugRelease',
|
||||
description='The build type to build',
|
||||
values=('Debug', 'Release', 'DebugRelease'))
|
||||
|
||||
For more information on ``CMAKE_BUILD_TYPE``, see:
|
||||
https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
CMakeLists.txt in a sub-directory
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Occasionally, developers will hide their source code and ``CMakeLists.txt``
|
||||
in a subdirectory like ``src``. If this happens, Spack won't
|
||||
be able to automatically detect the build system properly when running
|
||||
``spack create``. You will have to manually change the package base
|
||||
class and tell Spack where ``CMakeLists.txt`` resides. You can do this
|
||||
like so:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
root_cmakelists_dir = 'src'
|
||||
|
||||
|
||||
Note that this path is relative to the root of the extracted tarball,
|
||||
not to the ``build_directory``. It defaults to the current directory.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
Building out of source
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
By default, Spack builds every ``CMakePackage`` in a ``spack-build``
|
||||
sub-directory. If, for whatever reason, you would like to build in a
|
||||
different sub-directory, simply override ``build_directory`` like so:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
build_directory = 'my-build'
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Build and install targets
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
For most CMake packages, the usual:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ cmake
|
||||
$ make
|
||||
$ make install
|
||||
|
||||
is sufficient to install the package. However, if you need to run
|
||||
make with any other targets, for example, to build an optional
|
||||
library or build the documentation, you can add these like so:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
build_targets = ['all', 'docs']
|
||||
install_targets = ['install', 'docs']
|
||||
|
||||
^^^^^^^
|
||||
Testing
|
||||
^^^^^^^
|
||||
|
||||
CMake-based packages typically provide unit testing via the
|
||||
``test`` target. If you build your software with ``--test=root``,
|
||||
Spack will check for the presence of a ``test`` target in the
|
||||
Makefile and run ``make test`` for you. If you want to run a
|
||||
different test instead, simply override the ``check`` method.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
External documentation
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
For more information on the CMake build system, see:
|
||||
https://cmake.org/cmake/help/latest/
|
38
lib/spack/docs/build_systems/cudapackage.rst
Normal file
38
lib/spack/docs/build_systems/cudapackage.rst
Normal file
@ -0,0 +1,38 @@
|
||||
.. _cudapackage:
|
||||
|
||||
-----------
|
||||
CudaPackage
|
||||
-----------
|
||||
|
||||
Different from other packages, ``CudaPackage`` does not represent a build
|
||||
system. Instead its goal is to simplify and unify usage of ``CUDA`` in other
|
||||
packages.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Provided variants and dependencies
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
``CudaPackage`` provides ``cuda`` variant (default to ``off``) to enable/disable
|
||||
``CUDA``, and ``cuda_arch`` variant to optionally specify the architecture.
|
||||
It also declares dependencies on the ``CUDA`` package ``depends_on('cuda@...')``
|
||||
based on the architecture as well as specifies conflicts for certain compiler versions.
|
||||
|
||||
^^^^^
|
||||
Usage
|
||||
^^^^^
|
||||
|
||||
In order to use it, just add another base class to your package, for example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class MyPackage(CMakePackage, CudaPackage):
|
||||
...
|
||||
def cmake_args(self):
|
||||
spec = self.spec
|
||||
if '+cuda' in spec:
|
||||
options.append('-DWITH_CUDA=ON')
|
||||
cuda_arch = spec.variants['cuda_arch'].value
|
||||
if cuda_arch is not None:
|
||||
options.append('-DCUDA_FLAGS=-arch=sm_{0}'.format(cuda_arch[0]))
|
||||
else:
|
||||
options.append('-DWITH_CUDA=OFF')
|
204
lib/spack/docs/build_systems/custompackage.rst
Normal file
204
lib/spack/docs/build_systems/custompackage.rst
Normal file
@ -0,0 +1,204 @@
|
||||
.. _custompackage:
|
||||
|
||||
--------------------
|
||||
Custom Build Systems
|
||||
--------------------
|
||||
|
||||
While the build systems listed above should meet your needs for the
|
||||
vast majority of packages, some packages provide custom build scripts.
|
||||
This guide is intended for the following use cases:
|
||||
|
||||
* Packaging software with its own custom build system
|
||||
* Adding support for new build systems
|
||||
|
||||
If you want to add support for a new build system, a good place to
|
||||
start is to look at the definitions of other build systems. This guide
|
||||
focuses mostly on how Spack's build systems work.
|
||||
|
||||
In this guide, we will be using the
|
||||
`perl <https://github.com/spack/spack/blob/develop/var/spack/repos/builtin/packages/perl/package.py>`_ and
|
||||
`cmake <https://github.com/spack/spack/blob/develop/var/spack/repos/builtin/packages/cmake/package.py>`_
|
||||
packages as examples. ``perl``'s build system is a hand-written
|
||||
``Configure`` shell script, while ``cmake`` bootstraps itself during
|
||||
installation. Both of these packages require custom build systems.
|
||||
|
||||
^^^^^^^^^^
|
||||
Base class
|
||||
^^^^^^^^^^
|
||||
|
||||
If your package does not belong to any of the aforementioned build
|
||||
systems that Spack already supports, you should inherit from the
|
||||
``Package`` base class. ``Package`` is a simple base class with a
|
||||
single phase: ``install``. If your package is simple, you may be able
|
||||
to simply write an ``install`` method that gets the job done. However,
|
||||
if your package is more complex and installation involves multiple
|
||||
steps, you should add separate phases as mentioned in the next section.
|
||||
|
||||
If you are creating a new build system base class, you should inherit
|
||||
from ``PackageBase``. This is the superclass for all build systems in
|
||||
Spack.
|
||||
|
||||
^^^^^^
|
||||
Phases
|
||||
^^^^^^
|
||||
|
||||
The most important concept in Spack's build system support is the idea
|
||||
of phases. Each build system defines a set of phases that are necessary
|
||||
to install the package. They usually follow some sort of "configure",
|
||||
"build", "install" guideline, but any of those phases may be missing
|
||||
or combined with another phase.
|
||||
|
||||
If you look at the ``perl`` package, you'll see:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
phases = ['configure', 'build', 'install']
|
||||
|
||||
Similarly, ``cmake`` defines:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
phases = ['bootstrap', 'build', 'install']
|
||||
|
||||
If we look at the ``cmake`` example, this tells Spack's ``PackageBase``
|
||||
class to run the ``bootstrap``, ``build``, and ``install`` functions
|
||||
in that order. It is now up to you to define these methods.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Phase and phase_args functions
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If we look at ``perl``, we see that it defines a ``configure`` method:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def configure(self, spec, prefix):
|
||||
configure = Executable('./Configure')
|
||||
configure(*self.configure_args())
|
||||
|
||||
There is also a corresponding ``configure_args`` function that handles
|
||||
all of the arguments to pass to ``Configure``, just like in
|
||||
``AutotoolsPackage``. Comparatively, the ``build`` and ``install``
|
||||
phases are pretty simple:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def build(self, spec, prefix):
|
||||
make()
|
||||
|
||||
def install(self, spec, prefix):
|
||||
make('install')
|
||||
|
||||
The ``cmake`` package looks very similar, but with a ``bootstrap``
|
||||
function instead of ``configure``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def bootstrap(self, spec, prefix):
|
||||
bootstrap = Executable('./bootstrap')
|
||||
bootstrap(*self.bootstrap_args())
|
||||
|
||||
def build(self, spec, prefix):
|
||||
make()
|
||||
|
||||
def install(self, spec, prefix):
|
||||
make('install')
|
||||
|
||||
Again, there is a ``boostrap_args`` function that determines the
|
||||
correct bootstrap flags to use.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
run_before/run_after
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Occasionally, you may want to run extra steps either before or after
|
||||
a given phase. This applies not just to custom build systems, but to
|
||||
existing build systems as well. You may need to patch a file that is
|
||||
generated by configure, or install extra files in addition to what
|
||||
``make install`` copies to the installation prefix. This is where
|
||||
``@run_before`` and ``@run_after`` come in.
|
||||
|
||||
These Python decorators allow you to write functions that are called
|
||||
before or after a particular phase. For example, in ``perl``, we see:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@run_after('install')
|
||||
def install_cpanm(self):
|
||||
spec = self.spec
|
||||
|
||||
if '+cpanm' in spec:
|
||||
with working_dir(join_path('cpanm', 'cpanm')):
|
||||
perl = spec['perl'].command
|
||||
perl('Makefile.PL')
|
||||
make()
|
||||
make('install')
|
||||
|
||||
This extra step automatically installs ``cpanm`` in addition to the
|
||||
base Perl installation.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
on_package_attributes
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The ``run_before``/``run_after`` logic discussed above becomes
|
||||
particularly powerful when combined with the ``@on_package_attributes``
|
||||
decorator. This decorator allows you to conditionally run certain
|
||||
functions depending on the attributes of that package. The most
|
||||
common example is conditional testing. Many unit tests are prone to
|
||||
failure, even when there is nothing wrong with the installation.
|
||||
Unfortunately, non-portable unit tests and tests that are
|
||||
"supposed to fail" are more common than we would like. Instead of
|
||||
always running unit tests on installation, Spack lets users
|
||||
conditionally run tests with the ``--test=root`` flag.
|
||||
|
||||
If we wanted to define a function that would conditionally run
|
||||
if and only if this flag is set, we would use the following line:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@on_package_attributes(run_tests=True)
|
||||
|
||||
^^^^^^^
|
||||
Testing
|
||||
^^^^^^^
|
||||
|
||||
Let's put everything together and add unit tests to our package.
|
||||
In the ``perl`` package, we can see:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@run_after('build')
|
||||
@on_package_attributes(run_tests=True)
|
||||
def test(self):
|
||||
make('test')
|
||||
|
||||
As you can guess, this runs ``make test`` *after* building the package,
|
||||
if and only if testing is requested. Again, this is not specific to
|
||||
custom build systems, it can be added to existing build systems as well.
|
||||
|
||||
Ideally, every package in Spack will have some sort of test to ensure
|
||||
that it was built correctly. It is up to the package authors to make
|
||||
sure this happens. If you are adding a package for some software and
|
||||
the developers list commands to test the installation, please add these
|
||||
tests to your ``package.py``.
|
||||
|
||||
.. warning::
|
||||
|
||||
The order of decorators matters. The following ordering:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@run_after('install')
|
||||
@on_package_attributes(run_tests=True)
|
||||
|
||||
works as expected. However, if you reverse the ordering:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@on_package_attributes(run_tests=True)
|
||||
@run_after('install')
|
||||
|
||||
the tests will always be run regardless of whether or not
|
||||
``--test=root`` is requested. See https://github.com/spack/spack/issues/3833
|
||||
for more information
|
13
lib/spack/docs/build_systems/intelpackage.rst
Normal file
13
lib/spack/docs/build_systems/intelpackage.rst
Normal file
@ -0,0 +1,13 @@
|
||||
.. _intelpackage:
|
||||
|
||||
------------
|
||||
IntelPackage
|
||||
------------
|
||||
|
||||
Intel provides many licensed software packages, which all share the
|
||||
same basic steps for configuring and installing, as well as license
|
||||
management.
|
||||
|
||||
This build system is a work-in-progress. See
|
||||
https://github.com/spack/spack/pull/4300 and
|
||||
https://github.com/spack/spack/pull/7469 for more information.
|
304
lib/spack/docs/build_systems/makefilepackage.rst
Normal file
304
lib/spack/docs/build_systems/makefilepackage.rst
Normal file
@ -0,0 +1,304 @@
|
||||
.. _makefilepackage:
|
||||
|
||||
---------------
|
||||
MakefilePackage
|
||||
---------------
|
||||
|
||||
The most primitive build system a package can use is a plain Makefile.
|
||||
Makefiles are simple to write for small projects, but they usually
|
||||
require you to edit the Makefile to set platform and compiler-specific
|
||||
variables.
|
||||
|
||||
^^^^^^
|
||||
Phases
|
||||
^^^^^^
|
||||
|
||||
The ``MakefilePackage`` base class comes with 3 phases:
|
||||
|
||||
#. ``edit`` - edit the Makefile
|
||||
#. ``build`` - build the project
|
||||
#. ``install`` - install the project
|
||||
|
||||
By default, ``edit`` does nothing, but you can override it to replace
|
||||
hard-coded Makefile variables. The ``build`` and ``install`` phases
|
||||
run:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ make
|
||||
$ make install
|
||||
|
||||
|
||||
^^^^^^^^^^^^^^^
|
||||
Important files
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
The main file that matters for a ``MakefilePackage`` is the Makefile.
|
||||
This file will be named one of the following ways:
|
||||
|
||||
* GNUmakefile (only works with GNU Make)
|
||||
* Makefile (most common)
|
||||
* makefile
|
||||
|
||||
Some Makefiles also *include* other configuration files. Check for an
|
||||
``include`` directive in the Makefile.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Build system dependencies
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Spack assumes that the operating system will have a valid ``make`` utility
|
||||
installed already, so you don't need to add a dependency on ``make``.
|
||||
However, if the package uses a ``GNUmakefile`` or the developers recommend
|
||||
using GNU Make, you should add a dependency on ``gmake``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
depends_on('gmake', type='build')
|
||||
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Types of Makefile packages
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Most of the work involved in packaging software that uses Makefiles
|
||||
involves overriding or replacing hard-coded variables. Many packages
|
||||
make the mistake of hard-coding compilers, usually for GCC or Intel.
|
||||
This is fine if you happen to be using that particular compiler, but
|
||||
Spack is designed to work with *any* compiler, and you need to ensure
|
||||
that this is the case.
|
||||
|
||||
Depending on how the Makefile is designed, there are 4 common strategies
|
||||
that can be used to set or override the appropriate variables:
|
||||
|
||||
"""""""""""""""""""""
|
||||
Environment variables
|
||||
"""""""""""""""""""""
|
||||
|
||||
Make has multiple types of
|
||||
`assignment operators <https://www.gnu.org/software/make/manual/make.html#Setting>`_.
|
||||
Some Makefiles use ``=`` to assign variables. The only way to override
|
||||
these variables is to edit the Makefile or override them on the
|
||||
command-line. However, Makefiles that use ``?=`` for assignment honor
|
||||
environment variables. Since Spack already sets ``CC``, ``CXX``, ``F77``,
|
||||
and ``FC``, you won't need to worry about setting these variables. If
|
||||
there are any other variables you need to set, you can do this in the
|
||||
``edit`` method:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def edit(self, spec, prefix):
|
||||
env['PREFIX'] = prefix
|
||||
env['BLASLIB'] = spec['blas'].libs.ld_flags
|
||||
|
||||
|
||||
`cbench <https://github.com/spack/spack/blob/develop/var/spack/repos/builtin/packages/cbench/package.py>`_
|
||||
is a good example of a simple package that does this, while
|
||||
`esmf <https://github.com/spack/spack/blob/develop/var/spack/repos/builtin/packages/esmf/package.py>`_
|
||||
is a good example of a more complex package.
|
||||
|
||||
""""""""""""""""""""""
|
||||
Command-line arguments
|
||||
""""""""""""""""""""""
|
||||
|
||||
If the Makefile ignores environment variables, the next thing to try
|
||||
is command-line arguments. You can do this by overriding the
|
||||
``build_targets`` attribute. If you don't need access to the spec,
|
||||
you can do this like so:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
build_targets = ['CC=cc']
|
||||
|
||||
|
||||
If you do need access to the spec, you can create a property like so:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@property
|
||||
def build_targets(self):
|
||||
spec = self.spec
|
||||
|
||||
return [
|
||||
'CC=cc',
|
||||
'BLASLIB={0}'.format(spec['blas'].libs.ld_flags),
|
||||
]
|
||||
|
||||
|
||||
`cloverleaf <https://github.com/spack/spack/blob/develop/var/spack/repos/builtin/packages/cloverleaf/package.py>`_
|
||||
is a good example of a package that uses this strategy.
|
||||
|
||||
"""""""""""""
|
||||
Edit Makefile
|
||||
"""""""""""""
|
||||
|
||||
Some Makefiles are just plain stubborn and will ignore command-line
|
||||
variables. The only way to ensure that these packages build correctly
|
||||
is to directly edit the Makefile. Spack provides a ``FileFilter`` class
|
||||
and a ``filter_file`` method to help with this. For example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def edit(self, spec, prefix):
|
||||
makefile = FileFilter('Makefile')
|
||||
|
||||
makefile.filter('CC = gcc', 'CC = cc')
|
||||
makefile.filter('CXX = g++', 'CC = c++')
|
||||
|
||||
|
||||
`stream <https://github.com/spack/spack/blob/develop/var/spack/repos/builtin/packages/stream/package.py>`_
|
||||
is a good example of a package that involves editing a Makefile to set
|
||||
the appropriate variables.
|
||||
|
||||
"""""""""""
|
||||
Config file
|
||||
"""""""""""
|
||||
|
||||
More complex packages often involve Makefiles that *include* a
|
||||
configuration file. These configuration files are primarily composed
|
||||
of variables relating to the compiler, platform, and the location of
|
||||
dependencies or names of libraries. Since these config files are
|
||||
dependent on the compiler and platform, you will often see entire
|
||||
directories of examples for common compilers and architectures. Use
|
||||
these examples to help determine what possible values to use.
|
||||
|
||||
If the config file is long and only contains one or two variables
|
||||
that need to be modified, you can use the technique above to edit
|
||||
the config file. However, if you end up needing to modify most of
|
||||
the variables, it may be easier to write a new file from scratch.
|
||||
|
||||
If each variable is independent of each other, a dictionary works
|
||||
well for storing variables:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def edit(self, spec, prefix):
|
||||
config = {
|
||||
'CC': 'cc',
|
||||
'MAKE': 'make',
|
||||
}
|
||||
|
||||
if '+blas' in spec:
|
||||
config['BLAS_LIBS'] = spec['blas'].libs.joined()
|
||||
|
||||
with open('make.inc', 'w') as inc:
|
||||
for key in config:
|
||||
inc.write('{0} = {1}\n'.format(key, config[key]))
|
||||
|
||||
|
||||
`elk <https://github.com/spack/spack/blob/develop/var/spack/repos/builtin/packages/elk/package.py>`_
|
||||
is a good example of a package that uses a dictionary to store
|
||||
configuration variables.
|
||||
|
||||
If the order of variables is important, it may be easier to store
|
||||
them in a list:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def edit(self, spec, prefix):
|
||||
config = [
|
||||
'INSTALL_DIR = {0}'.format(prefix),
|
||||
'INCLUDE_DIR = $(INSTALL_DIR)/include',
|
||||
'LIBRARY_DIR = $(INSTALL_DIR)/lib',
|
||||
]
|
||||
|
||||
with open('make.inc', 'w') as inc:
|
||||
for var in config:
|
||||
inc.write('{0}\n'.format(var))
|
||||
|
||||
|
||||
`hpl <https://github.com/spack/spack/blob/develop/var/spack/repos/builtin/packages/hpl/package.py>`_
|
||||
is a good example of a package that uses a list to store
|
||||
configuration variables.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Variables to watch out for
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The following is a list of common variables to watch out for. The first
|
||||
two sections are
|
||||
`implicit variables <https://www.gnu.org/software/make/manual/html_node/Implicit-Variables.html>`_
|
||||
defined by Make and will always use the same name, while the rest are
|
||||
user-defined variables and may vary from package to package.
|
||||
|
||||
* **Compilers**
|
||||
|
||||
This includes variables such as ``CC``, ``CXX``, ``F77``, ``F90``,
|
||||
and ``FC``, as well as variables related to MPI compiler wrappers,
|
||||
like ``MPICC`` and friends.
|
||||
|
||||
* **Compiler flags**
|
||||
|
||||
This includes variables for specific compilers, like ``CFLAGS``,
|
||||
``CXXFLAGS``, ``F77FLAGS``, ``F90FLAGS``, ``FCFLAGS``, and ``CPPFLAGS``.
|
||||
These variables are often hard-coded to contain flags specific to a
|
||||
certain compiler. If these flags don't work for every compiler,
|
||||
you may want to consider filtering them.
|
||||
|
||||
* **Variables that enable or disable features**
|
||||
|
||||
This includes variables like ``MPI``, ``OPENMP``, ``PIC``, and
|
||||
``DEBUG``. These flags often require you to create a variant
|
||||
so that you can either build with or without MPI support, for
|
||||
example. These flags are often compiler-dependent. You should
|
||||
replace them with the appropriate compiler flags, such as
|
||||
``self.compiler.openmp_flag`` or ``self.compiler.pic_flag``.
|
||||
|
||||
* **Platform flags**
|
||||
|
||||
These flags control the type of architecture that the executable
|
||||
is compiler for. Watch out for variables like ``PLAT`` or ``ARCH``.
|
||||
|
||||
* **Dependencies**
|
||||
|
||||
Look out for variables that sound like they could be used to
|
||||
locate dependencies, such as ``JAVA_HOME``, ``JPEG_ROOT``, or
|
||||
``ZLIBDIR``. Also watch out for variables that control linking,
|
||||
such as ``LIBS``, ``LDFLAGS``, and ``INCLUDES``. These variables
|
||||
need to be set to the installation prefix of a dependency, or
|
||||
to the correct linker flags to link to that dependency.
|
||||
|
||||
* **Installation prefix**
|
||||
|
||||
If your Makefile has an ``install`` target, it needs some way of
|
||||
knowing where to install. By default, many packages install to
|
||||
``/usr`` or ``/usr/local``. Since many Spack users won't have
|
||||
sudo privileges, it is imperative that each package is installed
|
||||
to the proper prefix. Look for variables like ``PREFIX`` or
|
||||
``INSTALL``.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Makefiles in a sub-directory
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Not every package places their Makefile in the root of the package
|
||||
tarball. If the Makefile is in a sub-directory like ``src``, you
|
||||
can tell Spack where to locate it like so:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
build_directory = 'src'
|
||||
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
Manual installation
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Not every Makefile includes an ``install`` target. If this is the
|
||||
case, you can override the default ``install`` method to manually
|
||||
install the package:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def install(self, spec, prefix):
|
||||
mkdir(prefix.bin)
|
||||
install('foo', prefix.bin)
|
||||
install_tree('lib', prefix.lib)
|
||||
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
External documentation
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
For more information on reading and writing Makefiles, see:
|
||||
https://www.gnu.org/software/make/manual/make.html
|
47
lib/spack/docs/build_systems/octavepackage.rst
Normal file
47
lib/spack/docs/build_systems/octavepackage.rst
Normal file
@ -0,0 +1,47 @@
|
||||
.. _octavepackage:
|
||||
|
||||
-------------
|
||||
OctavePackage
|
||||
-------------
|
||||
|
||||
Octave has its own build system for installing packages.
|
||||
|
||||
^^^^^^
|
||||
Phases
|
||||
^^^^^^
|
||||
|
||||
The ``OctavePackage`` base class has a single phase:
|
||||
|
||||
#. ``install`` - install the package
|
||||
|
||||
By default, this phase runs the following command:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ octave '--eval' 'pkg prefix <prefix>; pkg install <archive_file>'
|
||||
|
||||
|
||||
Beware that uninstallation is not implemented at the moment. After uninstalling
|
||||
a package via Spack, you also need to manually uninstall it from Octave via
|
||||
``pkg uninstall <package_name>``.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Finding Octave packages
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Most Octave packages are listed at https://octave.sourceforge.io/packages.php.
|
||||
|
||||
^^^^^^^^^^^^
|
||||
Dependencies
|
||||
^^^^^^^^^^^^
|
||||
|
||||
Usually, the homepage of a package will list dependencies, i.e.
|
||||
``Dependencies: Octave >= 3.6.0 struct >= 1.0.12``. The same information should
|
||||
be available in the ``DESCRIPTION`` file in the root of each archive.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
External Documentation
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
For more information on the Octave build system, see:
|
||||
https://octave.org/doc/v4.4.0/Installing-and-Removing-Packages.html
|
207
lib/spack/docs/build_systems/perlpackage.rst
Normal file
207
lib/spack/docs/build_systems/perlpackage.rst
Normal file
@ -0,0 +1,207 @@
|
||||
.. _perlpackage:
|
||||
|
||||
-----------
|
||||
PerlPackage
|
||||
-----------
|
||||
|
||||
Much like Octave, Perl has its own language-specific
|
||||
build system.
|
||||
|
||||
^^^^^^
|
||||
Phases
|
||||
^^^^^^
|
||||
|
||||
The ``PerlPackage`` base class comes with 3 phases that can be overridden:
|
||||
|
||||
#. ``configure`` - configure the package
|
||||
#. ``build`` - build the package
|
||||
#. ``install`` - install the package
|
||||
|
||||
Perl packages have 2 common modules used for module installation:
|
||||
|
||||
"""""""""""""""""""""""
|
||||
``ExtUtils::MakeMaker``
|
||||
"""""""""""""""""""""""
|
||||
|
||||
The ``ExtUtils::MakeMaker`` module is just what it sounds like, a module
|
||||
designed to generate Makefiles. It can be identified by the presence of
|
||||
a ``Makefile.PL`` file, and has the following installation steps:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ perl Makefile.PL INSTALL_BASE=/path/to/installation/prefix
|
||||
$ make
|
||||
$ make test # optional
|
||||
$ make install
|
||||
|
||||
|
||||
"""""""""""""""""
|
||||
``Module::Build``
|
||||
"""""""""""""""""
|
||||
|
||||
The ``Module::Build`` module is a pure-Perl build system, and can be
|
||||
identified by the presence of a ``Build.PL`` file. It has the following
|
||||
installation steps:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ perl Build.PL --install_base /path/to/installation/prefix
|
||||
$ ./Build
|
||||
$ ./Build test # optional
|
||||
$ ./Build install
|
||||
|
||||
|
||||
If both ``Makefile.PL`` and ``Build.PL`` files exist in the package,
|
||||
Spack will use ``Makefile.PL`` by default. If your package uses a
|
||||
different module, ``PerlPackage`` will need to be extended to support
|
||||
it.
|
||||
|
||||
``PerlPackage`` automatically detects which build steps to use, so there
|
||||
shouldn't be much work on the package developer's side to get things
|
||||
working.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
Finding Perl packages
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Most Perl modules are hosted on CPAN - The Comprehensive Perl Archive
|
||||
Network. If you need to find a package for ``XML::Parser``, for example,
|
||||
you should search for "CPAN XML::Parser".
|
||||
|
||||
Some CPAN pages are versioned. Check for a link to the
|
||||
"Latest Release" to make sure you have the latest version.
|
||||
|
||||
^^^^^^^^^^^^
|
||||
Package name
|
||||
^^^^^^^^^^^^
|
||||
|
||||
When you use ``spack create`` to create a new Perl package, Spack will
|
||||
automatically prepend ``perl-`` to the front of the package name. This
|
||||
helps to keep Perl modules separate from other packages. The same
|
||||
naming scheme is used for other language extensions, like Python and R.
|
||||
|
||||
^^^^^^^^^^^
|
||||
Description
|
||||
^^^^^^^^^^^
|
||||
|
||||
Most CPAN pages have a short description under "NAME" and a longer
|
||||
description under "DESCRIPTION". Use whichever you think is more
|
||||
useful while still being succinct.
|
||||
|
||||
^^^^^^^^
|
||||
Homepage
|
||||
^^^^^^^^
|
||||
|
||||
In the top-right corner of the CPAN page, you'll find a "permalink"
|
||||
for the package. This should be used instead of the current URL, as
|
||||
it doesn't contain the version number and will always link to the
|
||||
latest release.
|
||||
|
||||
^^^
|
||||
URL
|
||||
^^^
|
||||
|
||||
If you haven't found it already, the download URL is on the right
|
||||
side of the page below the permalink. Search for "Download".
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Build system dependencies
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Every ``PerlPackage`` obviously depends on Perl at build and run-time,
|
||||
so ``PerlPackage`` contains:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
extends('perl')
|
||||
|
||||
depends_on('perl', type=('build', 'run'))
|
||||
|
||||
|
||||
If your package requires a specific version of Perl, you should
|
||||
specify this.
|
||||
|
||||
Although newer versions of Perl include ``ExtUtils::MakeMaker`` and
|
||||
``Module::Build`` as "core" modules, you may want to add dependencies
|
||||
on ``perl-extutils-makemaker`` and ``perl-module-build`` anyway. Many
|
||||
people add Perl as an external package, and we want the build to work
|
||||
properly. If your package uses ``Makefile.PL`` to build, add:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
depends_on('perl-extutils-makemaker', type='build')
|
||||
|
||||
|
||||
If your package uses ``Build.PL`` to build, add:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
depends_on('perl-module-build', type='build')
|
||||
|
||||
|
||||
^^^^^^^^^^^^^^^^^
|
||||
Perl dependencies
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
Below the download URL, you will find a "Dependencies" link, which
|
||||
takes you to a page listing all of the dependencies of the package.
|
||||
Packages listed as "Core module" don't need to be added as dependencies,
|
||||
but all direct dependencies should be added. Don't add dependencies of
|
||||
dependencies. These should be added as dependencies to the dependency,
|
||||
not to your package.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Passing arguments to configure
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Packages that have non-Perl dependencies often use command-line
|
||||
variables to specify their installation directory. You can pass
|
||||
arguments to ``Makefile.PL`` or ``Build.PL`` by overriding
|
||||
``configure_args`` like so:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def configure_args(self):
|
||||
expat = self.spec['expat'].prefix
|
||||
|
||||
return [
|
||||
'EXPATLIBPATH={0}'.format(expat.lib),
|
||||
'EXPATINCPATH={0}'.format(expat.include),
|
||||
]
|
||||
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
Alternatives to Spack
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If you need to maintain a stack of Perl modules for a user and don't
|
||||
want to add all of them to Spack, a good alternative is ``cpanm``.
|
||||
If Perl is already installed on your system, it should come with a
|
||||
``cpan`` executable. To install ``cpanm``, run the following command:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ cpan App::cpanminus
|
||||
|
||||
|
||||
Now, you can install any Perl module you want by running:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ cpanm Module::Name
|
||||
|
||||
|
||||
Obviously, these commands can only be run if you have root privileges.
|
||||
Furthermore, ``cpanm`` is not capable of installing non-Perl dependencies.
|
||||
If you need to install to your home directory or need to install a module
|
||||
with non-Perl dependencies, Spack is a better option.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
External documentation
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
You can find more information on installing Perl modules from source
|
||||
at: http://www.perlmonks.org/?node_id=128077
|
||||
|
||||
More generic Perl module installation instructions can be found at:
|
||||
http://www.cpan.org/modules/INSTALL.html
|
742
lib/spack/docs/build_systems/pythonpackage.rst
Normal file
742
lib/spack/docs/build_systems/pythonpackage.rst
Normal file
@ -0,0 +1,742 @@
|
||||
.. _pythonpackage:
|
||||
|
||||
-------------
|
||||
PythonPackage
|
||||
-------------
|
||||
|
||||
Python packages and modules have their own special build system.
|
||||
|
||||
^^^^^^
|
||||
Phases
|
||||
^^^^^^
|
||||
|
||||
The ``PythonPackage`` base class provides the following phases that
|
||||
can be overridden:
|
||||
|
||||
* ``build``
|
||||
* ``build_py``
|
||||
* ``build_ext``
|
||||
* ``build_clib``
|
||||
* ``build_scripts``
|
||||
* ``clean``
|
||||
* ``install``
|
||||
* ``install_lib``
|
||||
* ``install_headers``
|
||||
* ``install_scripts``
|
||||
* ``install_data``
|
||||
* ``sdist``
|
||||
* ``register``
|
||||
* ``bdist``
|
||||
* ``bdist_dumb``
|
||||
* ``bdist_rpm``
|
||||
* ``bdist_wininst``
|
||||
* ``upload``
|
||||
* ``check``
|
||||
|
||||
These are all standard ``setup.py`` commands and can be found by running:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ python setup.py --help-commands
|
||||
|
||||
|
||||
By default, only the ``build`` and ``install`` phases are run:
|
||||
|
||||
#. ``build`` - build everything needed to install
|
||||
#. ``install`` - install everything from build directory
|
||||
|
||||
If for whatever reason you need to run more phases, simply modify your
|
||||
``phases`` list like so:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
phases = ['build_ext', 'install', 'bdist']
|
||||
|
||||
|
||||
Each phase provides a function ``<phase>`` that runs:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ python -s setup.py --no-user-cfg <phase>
|
||||
|
||||
|
||||
Each phase also has a ``<phase_args>`` function that can pass arguments to
|
||||
this call. All of these functions are empty except for the ``install_args``
|
||||
function, which passes ``--prefix=/path/to/installation/prefix``. There is
|
||||
also some additional logic specific to setuptools and eggs.
|
||||
|
||||
If you need to run a phase that is not a standard ``setup.py`` command,
|
||||
you'll need to define a function for it like so:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
phases = ['configure', 'build', 'install']
|
||||
|
||||
def configure(self, spec, prefix):
|
||||
self.setup_py('configure')
|
||||
|
||||
|
||||
^^^^^^^^^^^^^^^
|
||||
Important files
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
Python packages can be identified by the presence of a ``setup.py`` file.
|
||||
This file is used by package managers like ``pip`` to determine a
|
||||
package's dependencies and the version of dependencies required, so if
|
||||
the ``setup.py`` file is not accurate, the package will not build properly.
|
||||
For this reason, the ``setup.py`` file should be fairly reliable. If the
|
||||
documentation and ``setup.py`` disagree on something, the ``setup.py``
|
||||
file should be considered to be the truth. As dependencies are added or
|
||||
removed, the documentation is much more likely to become outdated than
|
||||
the ``setup.py``.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Finding Python packages
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The vast majority of Python packages are hosted on PyPI - The Python
|
||||
Package Index. ``pip`` only supports packages hosted on PyPI, making
|
||||
it the only option for developers who want a simple installation.
|
||||
Search for "PyPI <package-name>" to find the download page. Note that
|
||||
some pages are versioned, and the first result may not be the newest
|
||||
version. Click on the "Latest Version" button to the top right to see
|
||||
if a newer version is available. The download page is usually at:
|
||||
https://pypi.org/project/<package-name>
|
||||
|
||||
^^^^^^^^^^^
|
||||
Description
|
||||
^^^^^^^^^^^
|
||||
|
||||
The top of the PyPI downloads page contains a description of the
|
||||
package. The first line is usually a short description, while there
|
||||
may be a several line "Project Description" that follows. Choose whichever
|
||||
is more useful. You can also get these descriptions on the command-line
|
||||
using:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ python setup.py --description
|
||||
$ python setup.py --long-description
|
||||
|
||||
|
||||
^^^^^^^^
|
||||
Homepage
|
||||
^^^^^^^^
|
||||
|
||||
Package developers use ``setup.py`` to upload new versions to PyPI.
|
||||
The ``setup`` method often passes metadata like ``homepage`` to PyPI.
|
||||
This metadata is displayed on the left side of the download page.
|
||||
Search for the text "Homepage" under "Project links" to find it. You
|
||||
should use this page instead of the PyPI page if they differ. You can
|
||||
also get the homepage on the command-line by running:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ python setup.py --url
|
||||
|
||||
|
||||
^^^
|
||||
URL
|
||||
^^^
|
||||
|
||||
You may have noticed that Spack allows you to add multiple versions of
|
||||
the same package without adding multiple versions of the download URL.
|
||||
It does this by guessing what the version string in the URL is and
|
||||
replacing this with the requested version. Obviously, if Spack cannot
|
||||
guess the version correctly, or if non-version-related things change
|
||||
in the URL, Spack cannot substitute the version properly.
|
||||
|
||||
Once upon a time, PyPI offered nice, simple download URLs like:
|
||||
https://pypi.python.org/packages/source/n/numpy/numpy-1.13.1.zip
|
||||
|
||||
As you can see, the version is 1.13.1. It probably isn't hard to guess
|
||||
what URL to use to download version 1.12.0, and Spack was perfectly
|
||||
capable of performing this calculation.
|
||||
|
||||
However, PyPI switched to a new download URL format:
|
||||
https://pypi.python.org/packages/c0/3a/40967d9f5675fbb097ffec170f59c2ba19fc96373e73ad47c2cae9a30aed/numpy-1.13.1.zip#md5=2c3c0f4edf720c3a7b525dacc825b9ae
|
||||
|
||||
and more recently:
|
||||
https://files.pythonhosted.org/packages/b0/2b/497c2bb7c660b2606d4a96e2035e92554429e139c6c71cdff67af66b58d2/numpy-1.14.3.zip
|
||||
|
||||
As you can imagine, it is impossible for Spack to guess what URL to
|
||||
use to download version 1.12.0 given this URL. There is a solution,
|
||||
however. PyPI offers a new hidden interface for downloading
|
||||
Python packages that does not include a hash in the URL:
|
||||
https://pypi.io/packages/source/n/numpy/numpy-1.13.1.zip
|
||||
|
||||
This URL redirects to the files.pythonhosted.org URL. The general syntax for
|
||||
this pypi.io URL is:
|
||||
https://pypi.io/packages/source/<first-letter-of-name>/<name>/<name>-<version>.<extension>
|
||||
|
||||
Please use the pypi.io URL instead of the pypi.python.org URL. If both
|
||||
``.tar.gz`` and ``.zip`` versions are available, ``.tar.gz`` is preferred.
|
||||
If some releases offer both ``.tar.gz`` and ``.zip`` versions, but some
|
||||
only offer ``.zip`` versions, use ``.zip``.
|
||||
|
||||
"""""""""""""""
|
||||
PyPI vs. GitHub
|
||||
"""""""""""""""
|
||||
|
||||
Many packages are hosted on PyPI, but are developed on GitHub and other
|
||||
version control systems. The tarball can be downloaded from either
|
||||
location, but PyPI is preferred for the following reasons:
|
||||
|
||||
#. PyPI contains the bare minimum of files to install the package.
|
||||
|
||||
You may notice that the tarball you download from PyPI does not
|
||||
have the same checksum as the tarball you download from GitHub.
|
||||
When a developer uploads a new release to PyPI, it doesn't contain
|
||||
every file in the repository, only the files necessary to install
|
||||
the package. PyPI tarballs are therefore smaller.
|
||||
|
||||
#. PyPI is the official source for package managers like ``pip``.
|
||||
|
||||
Let's be honest, ``pip`` is much more popular than Spack. If the
|
||||
GitHub tarball contains a file not present in the PyPI tarball that
|
||||
causes a bug, the developers may not realize this for quite some
|
||||
time. If the bug was in a file contained in the PyPI tarball, users
|
||||
would notice the bug much more quickly.
|
||||
|
||||
#. GitHub release may be a beta version.
|
||||
|
||||
When a developer releases a new version of a package on GitHub,
|
||||
it may not be intended for most users. Until that release also
|
||||
makes its way to PyPI, it should be assumed that the release is
|
||||
not yet ready for general use.
|
||||
|
||||
#. The checksum for a GitHub release may change.
|
||||
|
||||
Unfortunately, some developers have a habit of patching releases
|
||||
without incrementing the version number. This results in a change
|
||||
in tarball checksum. Package managers like Spack that use checksums
|
||||
to verify the integrity of a download tarball grind to a halt when
|
||||
the checksum for a known version changes. Most of the time, the
|
||||
change is intentional, and contains a needed bug fix. However,
|
||||
sometimes the change indicates a download source that has been
|
||||
compromised, and a tarball that contains a virus. If this happens,
|
||||
you must contact the developers to determine which is the case.
|
||||
PyPI is nice because it makes it physically impossible to
|
||||
re-release the same version of a package with a different checksum.
|
||||
|
||||
There are some reasons to prefer downloading from GitHub:
|
||||
|
||||
#. The GitHub tarball may contain unit tests
|
||||
|
||||
As previously mentioned, the PyPI tarball contains the bare minimum
|
||||
of files to install the package. Unless explicitly specified by the
|
||||
developers, it will not contain development files like unit tests.
|
||||
If you desire to run the unit tests during installation, you should
|
||||
use the GitHub tarball instead.
|
||||
|
||||
#. Spack does not yet support ``spack versions`` and ``spack checksum``
|
||||
with PyPI URLs
|
||||
|
||||
These commands work just fine with GitHub URLs. This is a minor
|
||||
annoyance, not a reason to prefer GitHub over PyPI.
|
||||
|
||||
If you really want to run these unit tests, no one will stop you from
|
||||
submitting a PR for a new package that downloads from GitHub.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Build system dependencies
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
There are a few dependencies common to the ``PythonPackage`` build system.
|
||||
|
||||
""""""
|
||||
Python
|
||||
""""""
|
||||
|
||||
Obviously, every ``PythonPackage`` needs Python at build-time to run
|
||||
``python setup.py build && python setup.py install``. Python is also
|
||||
needed at run-time if you want to import the module. Due to backwards
|
||||
incompatible changes between Python 2 and 3, it is very important to
|
||||
specify which versions of Python are supported. If the documentation
|
||||
mentions that Python 3 is required, this can be specified as:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
depends_on('python@3:', type=('build', 'run')
|
||||
|
||||
|
||||
If Python 2 is required, this would look like:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
depends_on('python@:2', type=('build', 'run')
|
||||
|
||||
|
||||
If Python 2.7 is the only version that works, you can use:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
depends_on('python@2.7:2.8', type=('build', 'run')
|
||||
|
||||
|
||||
The documentation may not always specify supported Python versions.
|
||||
Another place to check is in the ``setup.py`` file. Look for a line
|
||||
containing ``python_requires``. An example from
|
||||
`py-numpy <https://github.com/spack/spack/blob/develop/var/spack/repos/builtin/packages/py-numpy/package.py>`_
|
||||
looks like:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
python_requires='>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*'
|
||||
|
||||
|
||||
More commonly, you will find a version check at the top of the file:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
if sys.version_info[:2] < (2, 7) or (3, 0) <= sys.version_info[:2] < (3, 4):
|
||||
raise RuntimeError("Python version 2.7 or >= 3.4 required.")
|
||||
|
||||
|
||||
This can be converted to Spack's spec notation like so:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
depends_on('python@2.7:2.8,3.4:', type=('build', 'run'))
|
||||
|
||||
|
||||
""""""""""
|
||||
setuptools
|
||||
""""""""""
|
||||
|
||||
Originally, the Python language had a single build system called
|
||||
distutils, which is built into Python. Distutils provided a common
|
||||
framework for package authors to describe their project and how it
|
||||
should be built. However, distutils was not without limitations.
|
||||
Most notably, there was no way to list a project's dependencies
|
||||
with distutils. Along came setuptools, a non-builtin build system
|
||||
designed to overcome the limitations of distutils. Both projects
|
||||
use a similar API, making the transition easy while adding much
|
||||
needed functionality. Today, setuptools is used in around 75% of
|
||||
the Python packages in Spack.
|
||||
|
||||
Since setuptools isn't built-in to Python, you need to add it as a
|
||||
dependency. To determine whether or not a package uses setuptools,
|
||||
search the file for an import statement like:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import setuptools
|
||||
|
||||
|
||||
or:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
|
||||
Some packages are designed to work with both setuptools and distutils,
|
||||
so you may find something like:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
try:
|
||||
from setuptools import setup
|
||||
except ImportError:
|
||||
from distutils.core import setup
|
||||
|
||||
|
||||
This uses setuptools if available, and falls back to distutils if not.
|
||||
In this case, you would still want to add a setuptools dependency, as
|
||||
it offers us more control over the installation.
|
||||
|
||||
Unless specified otherwise, setuptools is usually a build-only dependency.
|
||||
That is, it is needed to install the software, but is not needed at
|
||||
run-time. This can be specified as:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
depends_on('py-setuptools', type='build')
|
||||
|
||||
|
||||
""""""
|
||||
cython
|
||||
""""""
|
||||
|
||||
Compared to compiled languages, interpreted languages like Python can
|
||||
be quite a bit slower. To work around this, some Python developers
|
||||
rewrite computationally demanding sections of code in C, a process
|
||||
referred to as "cythonizing". In order to build these package, you
|
||||
need to add a build dependency on cython:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
depends_on('py-cython', type='build')
|
||||
|
||||
|
||||
Look for references to "cython" in the ``setup.py`` to determine
|
||||
whether or not this is necessary. Cython may be optional, but
|
||||
even then you should list it as a required dependency. Spack is
|
||||
designed to compile software, and is meant for HPC facilities
|
||||
where speed is crucial. There is no reason why someone would not
|
||||
want an optimized version of a library instead of the pure-Python
|
||||
version.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
Python dependencies
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
When you install a package with ``pip``, it reads the ``setup.py``
|
||||
file in order to determine the dependencies of the package.
|
||||
If the dependencies are not yet installed, ``pip`` downloads them
|
||||
and installs them for you. This may sound convenient, but Spack
|
||||
cannot rely on this behavior for two reasons:
|
||||
|
||||
#. Spack needs to be able to install packages on air-gapped networks.
|
||||
|
||||
If there is no internet connection, ``pip`` can't download the
|
||||
package dependencies. By explicitly listing every dependency in
|
||||
the ``package.py``, Spack knows what to download ahead of time.
|
||||
|
||||
#. Duplicate installations of the same dependency may occur.
|
||||
|
||||
Spack supports *activation* of Python extensions, which involves
|
||||
symlinking the package installation prefix to the Python installation
|
||||
prefix. If your package is missing a dependency, that dependency
|
||||
will be installed to the installation directory of the same package.
|
||||
If you try to activate the package + dependency, it may cause a
|
||||
problem if that package has already been activated.
|
||||
|
||||
For these reasons, you must always explicitly list all dependencies.
|
||||
Although the documentation may list the package's dependencies,
|
||||
often the developers assume people will use ``pip`` and won't have to
|
||||
worry about it. Always check the ``setup.py`` to find the true
|
||||
dependencies.
|
||||
|
||||
If the package relies on ``distutils``, it may not explicitly list its
|
||||
dependencies. Check for statements like:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
try:
|
||||
import numpy
|
||||
except ImportError:
|
||||
raise ImportError("numpy must be installed prior to installation")
|
||||
|
||||
|
||||
Obviously, this means that ``py-numpy`` is a dependency.
|
||||
|
||||
If the package uses ``setuptools``, check for the following clues:
|
||||
|
||||
* ``install_requires``
|
||||
|
||||
These packages are required for installation.
|
||||
|
||||
* ``extra_requires``
|
||||
|
||||
These packages are optional dependencies that enable additional
|
||||
functionality. You should add a variant that optionally adds these
|
||||
dependencies.
|
||||
|
||||
* ``test_requires``
|
||||
|
||||
These are packages that are required to run the unit tests for the
|
||||
package. These dependencies can be specified using the
|
||||
``type='test'`` dependency type.
|
||||
|
||||
In the root directory of the package, you may notice a
|
||||
``requirements.txt`` file. It may look like this file contains a list
|
||||
of all of the package's dependencies. Don't be fooled. This file is
|
||||
used by tools like Travis to install the pre-requisites for the
|
||||
package... and a whole bunch of other things. It often contains
|
||||
dependencies only needed for unit tests, like:
|
||||
|
||||
* mock
|
||||
* nose
|
||||
* pytest
|
||||
|
||||
It can also contain dependencies for building the documentation, like
|
||||
sphinx. If you can't find any information about the package's
|
||||
dependencies, you can take a look in ``requirements.txt``, but be sure
|
||||
not to add test or documentation dependencies.
|
||||
|
||||
""""""""""
|
||||
setuptools
|
||||
""""""""""
|
||||
|
||||
Setuptools is a bit of a special case. If a package requires setuptools
|
||||
at run-time, how do they express this? They could add it to
|
||||
``install_requires``, but setuptools is imported long before this and
|
||||
needed to read this line. And since you can't install the package
|
||||
without setuptools, the developers assume that setuptools will already
|
||||
be there, so they never mention when it is required. We don't want to
|
||||
add run-time dependencies if they aren't needed, so you need to
|
||||
determine whether or not setuptools is needed. Grep the installation
|
||||
directory for any files containing a reference to ``setuptools`` or
|
||||
``pkg_resources``. Both modules come from ``py-setuptools``.
|
||||
``pkg_resources`` is particularly common in scripts in ``prefix/bin``.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Passing arguments to setup.py
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The default build and install phases should be sufficient to install
|
||||
most packages. However, you may want to pass additional flags to
|
||||
either phase.
|
||||
|
||||
You can view the available options for a particular phase with:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ python setup.py <phase> --help
|
||||
|
||||
|
||||
Each phase provides a ``<phase_args>`` function that can be used to
|
||||
pass arguments to that phase. For example,
|
||||
`py-numpy <https://github.com/spack/spack/blob/develop/var/spack/repos/builtin/packages/py-numpy/package.py>`_
|
||||
adds:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def build_args(self, spec, prefix):
|
||||
args = []
|
||||
|
||||
# From NumPy 1.10.0 on it's possible to do a parallel build.
|
||||
if self.version >= Version('1.10.0'):
|
||||
# But Parallel build in Python 3.5+ is broken. See:
|
||||
# https://github.com/spack/spack/issues/7927
|
||||
# https://github.com/scipy/scipy/issues/7112
|
||||
if spec['python'].version < Version('3.5'):
|
||||
args = ['-j', str(make_jobs)]
|
||||
|
||||
return args
|
||||
|
||||
|
||||
^^^^^^^
|
||||
Testing
|
||||
^^^^^^^
|
||||
|
||||
``PythonPackage`` provides a couple of options for testing packages.
|
||||
|
||||
""""""""""""
|
||||
Import tests
|
||||
""""""""""""
|
||||
|
||||
Just because a package successfully built does not mean that it built
|
||||
correctly. The most reliable test of whether or not the package was
|
||||
correctly installed is to attempt to import all of the modules that
|
||||
get installed. To get a list of modules, run the following command
|
||||
in the source directory:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ python
|
||||
>>> import setuptools
|
||||
>>> setuptools.find_packages()
|
||||
['numpy', 'numpy._build_utils', 'numpy.compat', 'numpy.core', 'numpy.distutils', 'numpy.doc', 'numpy.f2py', 'numpy.fft', 'numpy.lib', 'numpy.linalg', 'numpy.ma', 'numpy.matrixlib', 'numpy.polynomial', 'numpy.random', 'numpy.testing', 'numpy.core.code_generators', 'numpy.distutils.command', 'numpy.distutils.fcompiler']
|
||||
|
||||
|
||||
Large, complex packages like ``numpy`` will return a long list of
|
||||
packages, while other packages like ``six`` will return an empty list.
|
||||
``py-six`` installs a single ``six.py`` file. In Python packaging lingo,
|
||||
a "package" is a directory containing files like:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
foo/__init__.py
|
||||
foo/bar.py
|
||||
foo/baz.py
|
||||
|
||||
|
||||
whereas a "module" is a single Python file. Since ``find_packages``
|
||||
only returns packages, you'll have to determine the correct module
|
||||
names yourself. You can now add these packages and modules to the
|
||||
package like so:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import_modules = ['six']
|
||||
|
||||
|
||||
When you run ``spack install --test=root py-six``, Spack will attempt
|
||||
to import the ``six`` module after installation.
|
||||
|
||||
These tests most often catch missing dependencies and non-RPATHed
|
||||
libraries. Make sure not to add modules/packages containing the word
|
||||
"test", as these likely won't end up in installation directory.
|
||||
|
||||
""""""""""
|
||||
Unit tests
|
||||
""""""""""
|
||||
|
||||
The package you want to install may come with additional unit tests.
|
||||
By default, Spack runs:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ python setup.py test
|
||||
|
||||
|
||||
if it detects that the ``setup.py`` file supports a ``test`` phase.
|
||||
You can add additional build-time or install-time tests by overriding
|
||||
``test`` and ``installtest``, respectively. For example, ``py-numpy``
|
||||
adds:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def install_test(self):
|
||||
with working_dir('..'):
|
||||
python('-c', 'import numpy; numpy.test("full", verbose=2)')
|
||||
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Setup file in a sub-directory
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
In order to be compatible with package managers like ``pip``, the package
|
||||
is required to place its ``setup.py`` in the root of the tarball. However,
|
||||
not every Python package cares about ``pip`` or PyPI. If you are installing
|
||||
a package that is not hosted on PyPI, you may find that it places its
|
||||
``setup.py`` in a sub-directory. To handle this, add the directory containing
|
||||
``setup.py`` to the package like so:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
build_directory = 'source'
|
||||
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Alternate names for setup.py
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
As previously mentioned, packages need to call their setup script ``setup.py``
|
||||
in order to be compatible with package managers like ``pip``. However, some
|
||||
packages like
|
||||
`py-meep <https://github.com/spack/spack/blob/develop/var/spack/repos/builtin/packages/py-meep/package.py>`_ and
|
||||
`py-adios <https://github.com/spack/spack/blob/develop/var/spack/repos/builtin/packages/py-adios/package.py>`_
|
||||
come with multiple setup scripts, one for a serial build and another for a
|
||||
parallel build. You can override the default name to use like so:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def setup_file(self):
|
||||
return 'setup-mpi.py' if '+mpi' in self.spec else 'setup.py'
|
||||
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
PythonPackage vs. packages that use Python
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
There are many packages that make use of Python, but packages that depend
|
||||
on Python are not necessarily ``PythonPackages``.
|
||||
|
||||
"""""""""""""""""""""""
|
||||
Choosing a build system
|
||||
"""""""""""""""""""""""
|
||||
|
||||
First of all, you need to select a build system. ``spack create`` usually
|
||||
does this for you, but if for whatever reason you need to do this manually,
|
||||
choose ``PythonPackage`` if and only if the package contains a ``setup.py``
|
||||
file.
|
||||
|
||||
"""""""""""""""""""""""
|
||||
Choosing a package name
|
||||
"""""""""""""""""""""""
|
||||
|
||||
Selecting the appropriate package name is a little more complicated
|
||||
than choosing the build system. By default, ``spack create`` will
|
||||
prepend ``py-`` to the beginning of the package name if it detects
|
||||
that the package uses the ``PythonPackage`` build system. However, there
|
||||
are occasionally packages that use ``PythonPackage`` that shouldn't
|
||||
start with ``py-``. For example:
|
||||
|
||||
* busco
|
||||
* easybuild
|
||||
* httpie
|
||||
* mercurial
|
||||
* scons
|
||||
* snakemake
|
||||
|
||||
The thing these packages have in common is that they are command-line
|
||||
tools that just so happen to be written in Python. Someone who wants
|
||||
to install ``mercurial`` with Spack isn't going to realize that it is
|
||||
written in Python, and they certainly aren't going to assume the package
|
||||
is called ``py-mercurial``. For this reason, we manually renamed the
|
||||
package to ``mercurial``.
|
||||
|
||||
Likewise, there are occasionally packages that don't use the
|
||||
``PythonPackage`` build system but should still be prepended with ``py-``.
|
||||
For example:
|
||||
|
||||
* py-genders
|
||||
* py-py2cairo
|
||||
* py-pygobject
|
||||
* py-pygtk
|
||||
* py-pyqt
|
||||
* py-pyserial
|
||||
* py-sip
|
||||
* py-xpyb
|
||||
|
||||
These packages are primarily used as Python libraries, not as
|
||||
command-line tools. You may see C/C++ packages that have optional
|
||||
Python language-bindings, such as:
|
||||
|
||||
* antlr
|
||||
* cantera
|
||||
* conduit
|
||||
* pagmo
|
||||
* vtk
|
||||
|
||||
Don't prepend these kind of packages with ``py-``. When in doubt,
|
||||
think about how this package will be used. Is it primarily a Python
|
||||
library that will be imported in other Python scripts? Or is it a
|
||||
command-line tool, or C/C++/Fortran program with optional Python
|
||||
modules? The former should be prepended with ``py-``, while the
|
||||
latter should not.
|
||||
|
||||
""""""""""""""""""""""
|
||||
extends vs. depends_on
|
||||
""""""""""""""""""""""
|
||||
|
||||
This is very similar to the naming dilemma above, with a slight twist.
|
||||
As mentioned in the :ref:`Packaging Guide <packaging_extensions>`,
|
||||
``extends`` and ``depends_on`` are very similar, but ``extends`` adds
|
||||
the ability to *activate* the package. Activation involves symlinking
|
||||
everything in the installation prefix of the package to the installation
|
||||
prefix of Python. This allows the user to import a Python module without
|
||||
having to add that module to ``PYTHONPATH``.
|
||||
|
||||
When deciding between ``extends`` and ``depends_on``, the best rule of
|
||||
thumb is to check the installation prefix. If Python libraries are
|
||||
installed to ``prefix/lib/python2.7/site-packages`` (where 2.7 is the
|
||||
MAJOR.MINOR version of Python you used to install the package), then
|
||||
you should use ``extends``. If Python libraries are installed elsewhere
|
||||
or the only files that get installed reside in ``prefix/bin``, then
|
||||
don't use ``extends``, as symlinking the package wouldn't be useful.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
Alternatives to Spack
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
PyPI has hundreds of thousands of packages that are not yet in Spack,
|
||||
and ``pip`` may be a perfectly valid alternative to using Spack. The
|
||||
main advantage of Spack over ``pip`` is its ability to compile
|
||||
non-Python dependencies. It can also build cythonized versions of a
|
||||
package or link to an optimized BLAS/LAPACK library like MKL,
|
||||
resulting in calculations that run orders of magnitude faster.
|
||||
Spack does not offer a significant advantage to other python-management
|
||||
systems for installing and using tools like flake8 and sphinx.
|
||||
But if you need packages with non-Python dependencies like
|
||||
numpy and scipy, Spack will be very valuable to you.
|
||||
|
||||
Anaconda is another great alternative to Spack, and comes with its own
|
||||
``conda`` package manager. Like Spack, Anaconda is capable of compiling
|
||||
non-Python dependencies. Anaconda contains many Python packages that
|
||||
are not yet in Spack, and Spack contains many Python packages that are
|
||||
not yet in Anaconda. The main advantage of Spack over Anaconda is its
|
||||
ability to choose a specific compiler and BLAS/LAPACK or MPI library.
|
||||
Spack also has better platform support for supercomputers. On the
|
||||
other hand, Anaconda offers Windows support.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
External documentation
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
For more information on Python packaging, see:
|
||||
https://packaging.python.org/
|
111
lib/spack/docs/build_systems/qmakepackage.rst
Normal file
111
lib/spack/docs/build_systems/qmakepackage.rst
Normal file
@ -0,0 +1,111 @@
|
||||
.. _qmakepackage:
|
||||
|
||||
------------
|
||||
QMakePackage
|
||||
------------
|
||||
|
||||
Much like Autotools and CMake, QMake is a build-script generator
|
||||
designed by the developers of Qt. In its simplest form, Spack's
|
||||
``QMakePackage`` runs the following steps:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ qmake
|
||||
$ make
|
||||
$ make check # optional
|
||||
$ make install
|
||||
|
||||
|
||||
QMake does not appear to have a standardized way of specifying
|
||||
the installation directory, so you may have to set environment
|
||||
variables or edit ``*.pro`` files to get things working properly.
|
||||
|
||||
^^^^^^
|
||||
Phases
|
||||
^^^^^^
|
||||
|
||||
The ``QMakePackage`` base class comes with the following phases:
|
||||
|
||||
#. ``qmake`` - generate Makefiles
|
||||
#. ``build`` - build the project
|
||||
#. ``install`` - install the project
|
||||
|
||||
By default, these phases run:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ qmake
|
||||
$ make
|
||||
$ make install
|
||||
|
||||
|
||||
Any of these phases can be overridden in your package as necessary.
|
||||
There is also a ``check`` method that looks for a ``check`` target
|
||||
in the Makefile. If a ``check`` target exists and the user runs:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ spack install --test=root <qmake-package>
|
||||
|
||||
|
||||
Spack will run ``make check`` after the build phase.
|
||||
|
||||
^^^^^^^^^^^^^^^
|
||||
Important files
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
Packages that use the QMake build system can be identified by the
|
||||
presence of a ``<project-name>.pro`` file. This file declares things
|
||||
like build instructions and dependencies.
|
||||
|
||||
One thing to look for is the ``minQtVersion`` function:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
minQtVersion(5, 6, 0)
|
||||
|
||||
|
||||
This means that Qt 5.6.0 is the earliest release that will work.
|
||||
You should specify this in a ``depends_on`` statement.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Build system dependencies
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
At the bare minimum, packages that use the QMake build system need a
|
||||
``qt`` dependency. Since this is always the case, the ``QMakePackage``
|
||||
base class already contains:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
depends_on('qt', type='build')
|
||||
|
||||
|
||||
If you want to specify a particular version requirement, or need to
|
||||
link to the ``qt`` libraries, you can override this in your package:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
depends_on('qt@5.6.0:')
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Passing arguments to qmake
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If you need to pass any arguments to the ``qmake`` call, you can
|
||||
override the ``qmake_args`` method like so:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def qmake_args(self):
|
||||
return ['-recursive']
|
||||
|
||||
|
||||
This method can be used to pass flags as well as variables.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
External documentation
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
For more information on the QMake build system, see:
|
||||
http://doc.qt.io/qt-5/qmake-manual.html
|
341
lib/spack/docs/build_systems/rpackage.rst
Normal file
341
lib/spack/docs/build_systems/rpackage.rst
Normal file
@ -0,0 +1,341 @@
|
||||
.. _rpackage:
|
||||
|
||||
--------
|
||||
RPackage
|
||||
--------
|
||||
|
||||
Like Python, R has its own built-in build system.
|
||||
|
||||
The R build system is remarkably uniform and well-tested.
|
||||
This makes it one of the easiest build systems to create
|
||||
new Spack packages for.
|
||||
|
||||
^^^^^^
|
||||
Phases
|
||||
^^^^^^
|
||||
|
||||
The ``RPackage`` base class has a single phase:
|
||||
|
||||
#. ``install`` - install the package
|
||||
|
||||
By default, this phase runs the following command:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ R CMD INSTALL --library=/path/to/installation/prefix/rlib/R/library .
|
||||
|
||||
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
Finding R packages
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The vast majority of R packages are hosted on CRAN - The Comprehensive
|
||||
R Archive Network. If you are looking for a particular R package, search
|
||||
for "CRAN <package-name>" and you should quickly find what you want.
|
||||
If it isn't on CRAN, try Bioconductor, another common R repository.
|
||||
|
||||
For the purposes of this tutorial, we will be walking through
|
||||
`r-caret <https://github.com/spack/spack/blob/develop/var/spack/repos/builtin/packages/r-caret/package.py>`_
|
||||
as an example. If you search for "CRAN caret", you will quickly find what
|
||||
you are looking for at https://cran.r-project.org/web/packages/caret/index.html.
|
||||
If you search for "Package source", you will find the download URL for
|
||||
the latest release. Use this URL with ``spack create`` to create a new
|
||||
package.
|
||||
|
||||
^^^^^^^^^^^^
|
||||
Package name
|
||||
^^^^^^^^^^^^
|
||||
|
||||
The first thing you'll notice is that Spack prepends ``r-`` to the front
|
||||
of the package name. This is how Spack separates R package extensions
|
||||
from the rest of the packages in Spack. Without this, we would end up
|
||||
with package name collisions more frequently than we would like. For
|
||||
instance, there are already packages for both:
|
||||
|
||||
* ``ape`` and ``r-ape``
|
||||
* ``curl`` and ``r-curl``
|
||||
* ``gmp`` and ``r-gmp``
|
||||
* ``jpeg`` and ``r-jpeg``
|
||||
* ``openssl`` and ``r-openssl``
|
||||
* ``uuid`` and ``r-uuid``
|
||||
* ``xts`` and ``r-xts``
|
||||
|
||||
Many popular programs written in C/C++ are later ported to R as a
|
||||
separate project.
|
||||
|
||||
^^^^^^^^^^^
|
||||
Description
|
||||
^^^^^^^^^^^
|
||||
|
||||
The first thing you'll need to add to your new package is a description.
|
||||
The top of the homepage for ``caret`` lists the following description:
|
||||
|
||||
caret: Classification and Regression Training
|
||||
|
||||
Misc functions for training and plotting classification and regression models.
|
||||
|
||||
You can either use the short description (first line), long description
|
||||
(second line), or both depending on what you feel is most appropriate.
|
||||
|
||||
^^^^^^^^
|
||||
Homepage
|
||||
^^^^^^^^
|
||||
|
||||
If you look at the bottom of the page, you'll see:
|
||||
|
||||
Linking:
|
||||
|
||||
Please use the canonical form https://CRAN.R-project.org/package=caret to link to this page.
|
||||
|
||||
Please uphold the wishes of the CRAN admins and use
|
||||
https://CRAN.R-project.org/package=caret as the homepage instead of
|
||||
https://cran.r-project.org/web/packages/caret/index.html. The latter may
|
||||
change without notice.
|
||||
|
||||
^^^
|
||||
URL
|
||||
^^^
|
||||
|
||||
As previously mentioned, the download URL for the latest release can be
|
||||
found by searching "Package source" on the homepage.
|
||||
|
||||
^^^^^^^^
|
||||
List URL
|
||||
^^^^^^^^
|
||||
|
||||
CRAN maintains a single webpage containing the latest release of every
|
||||
single package: https://cran.r-project.org/src/contrib/
|
||||
|
||||
Of course, as soon as a new release comes out, the version you were using
|
||||
in your package is no longer available at that URL. It is moved to an
|
||||
archive directory. If you search for "Old sources", you will find:
|
||||
https://cran.r-project.org/src/contrib/Archive/caret
|
||||
|
||||
If you only specify the URL for the latest release, your package will
|
||||
no longer be able to fetch that version as soon as a new release comes
|
||||
out. To get around this, add the archive directory as a ``list_url``.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Build system dependencies
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
As an extension of the R ecosystem, your package will obviously depend
|
||||
on R to build and run. Normally, we would use ``depends_on`` to express
|
||||
this, but for R packages, we use ``extends``. ``extends`` is similar to
|
||||
``depends_on``, but adds an additional feature: the ability to "activate"
|
||||
the package by symlinking it to the R installation directory. Since
|
||||
every R package needs this, the ``RPackage`` base class contains:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
extends('r')
|
||||
depends_on('r', type=('build', 'run'))
|
||||
|
||||
|
||||
Take a close look at the homepage for ``caret``. If you look at the
|
||||
"Depends" section, you'll notice that ``caret`` depends on "R (≥ 2.10)".
|
||||
You should add this to your package like so:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
depends_on('r@2.10:', type=('build', 'run'))
|
||||
|
||||
|
||||
^^^^^^^^^^^^^^
|
||||
R dependencies
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
R packages are often small and follow the classic Unix philosophy
|
||||
of doing one thing well. They are modular and usually depend on
|
||||
several other packages. You may find a single package with over a
|
||||
hundred dependencies. Luckily, CRAN packages are well-documented
|
||||
and list all of their dependencies in the following sections:
|
||||
|
||||
* Depends
|
||||
* Imports
|
||||
* LinkingTo
|
||||
|
||||
As far as Spack is concerned, all 3 of these dependency types
|
||||
correspond to ``type=('build', 'run')``, so you don't have to worry
|
||||
about them. If you are curious what they mean,
|
||||
https://github.com/spack/spack/issues/2951 has a pretty good summary:
|
||||
|
||||
``Depends`` is required and will cause those R packages to be *attached*,
|
||||
that is, their APIs are exposed to the user. ``Imports`` *loads* packages
|
||||
so that *the package* importing these packages can access their APIs,
|
||||
while *not* being exposed to the user. When a user calls ``library(foo)``
|
||||
s/he *attaches* package ``foo`` and all of the packages under ``Depends``.
|
||||
Any function in one of these package can be called directly as ``bar()``.
|
||||
If there are conflicts, user can also specify ``pkgA::bar()`` and
|
||||
``pkgB::bar()`` to distinguish between them. Historically, there was only
|
||||
``Depends`` and ``Suggests``, hence the confusing names. Today, maybe
|
||||
``Depends`` would have been named ``Attaches``.
|
||||
|
||||
The ``LinkingTo`` is not perfect and there was recently an extensive
|
||||
discussion about API/ABI among other things on the R-devel mailing
|
||||
list among very skilled R developers:
|
||||
|
||||
* https://stat.ethz.ch/pipermail/r-devel/2016-December/073505.html
|
||||
* https://stat.ethz.ch/pipermail/r-devel/2017-January/073647.html
|
||||
|
||||
Some packages also have a fourth section:
|
||||
|
||||
* Suggests
|
||||
|
||||
These are optional, rarely-used dependencies that a user might find
|
||||
useful. You should **NOT** add these dependencies to your package.
|
||||
R packages already have enough dependencies as it is, and adding
|
||||
optional dependencies can really slow down the concretization
|
||||
process. They can also introduce circular dependencies.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Core, recommended, and non-core packages
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If you look at "Depends", "Imports", and "LinkingTo", you will notice
|
||||
3 different types of packages:
|
||||
|
||||
"""""""""""""
|
||||
Core packages
|
||||
"""""""""""""
|
||||
|
||||
If you look at the ``caret`` homepage, you'll notice a few dependencies
|
||||
that don't have a link to the package, like ``methods``, ``stats``, and
|
||||
``utils``. These packages are part of the core R distribution and are
|
||||
tied to the R version installed. You can basically consider these to be
|
||||
"R itself". These are so essential to R so it would not make sense that
|
||||
they could be updated via CRAN. If so, you would basically get a different
|
||||
version of R. Thus, they're updated when R is updated.
|
||||
|
||||
You can find a list of these core libraries at:
|
||||
https://github.com/wch/r-source/tree/trunk/src/library
|
||||
|
||||
""""""""""""""""""""
|
||||
Recommended packages
|
||||
""""""""""""""""""""
|
||||
|
||||
When you install R, there is an option called ``--with-recommended-packages``.
|
||||
This flag causes the R installation to include a few "Recommended" packages
|
||||
(legacy term). They are for historical reasons quite tied to the core R
|
||||
distribution, developed by the R core team or people closely related to it.
|
||||
The R core distribution "knows" about these package, but they are indeed
|
||||
distributed via CRAN. Because they're distributed via CRAN, they can also be
|
||||
updated between R version releases.
|
||||
|
||||
Spack explicitly adds the ``--without-recommended-packages`` flag to prevent
|
||||
the installation of these packages. Due to the way Spack handles package
|
||||
activation (symlinking packages to the R installation directory),
|
||||
pre-existing recommended packages will cause conflicts for already-existing
|
||||
files. We could either not include these recommended packages in Spack and
|
||||
require them to be installed through ``--with-recommended-packages``, or
|
||||
we could not install them with R and let users choose the version of the
|
||||
package they want to install. We chose the latter.
|
||||
|
||||
Since these packages are so commonly distributed with the R system, many
|
||||
developers may assume these packages exist and fail to list them as
|
||||
dependencies. Watch out for this.
|
||||
|
||||
You can find a list of these recommended packages at:
|
||||
https://github.com/wch/r-source/blob/trunk/share/make/vars.mk
|
||||
|
||||
"""""""""""""""""
|
||||
Non-core packages
|
||||
"""""""""""""""""
|
||||
|
||||
These are packages that are neither "core" nor "recommended". There are more
|
||||
than 10,000 of these packages hosted on CRAN alone.
|
||||
|
||||
For each of these package types, if you see that a specific version is
|
||||
required, for example, "lattice (≥ 0.20)", please add this information to
|
||||
the dependency:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
depends_on('r-lattice@0.20:', type=('build', 'run'))
|
||||
|
||||
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
Non-R dependencies
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Some packages depend on non-R libraries for linking. Check out the
|
||||
`r-stringi <https://github.com/spack/spack/blob/develop/var/spack/repos/builtin/packages/r-stringi/package.py>`_
|
||||
package for an example: https://CRAN.R-project.org/package=stringi.
|
||||
If you search for the text "SystemRequirements", you will see:
|
||||
|
||||
ICU4C (>= 52, optional)
|
||||
|
||||
This is how non-R dependencies are listed. Make sure to add these
|
||||
dependencies. The default dependency type should suffice.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Passing arguments to the installation
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Some R packages provide additional flags that can be passed to
|
||||
``R CMD INSTALL``, often to locate non-R dependencies.
|
||||
`r-rmpi <https://github.com/spack/spack/blob/develop/var/spack/repos/builtin/packages/r-rmpi/package.py>`_
|
||||
is an example of this, and flags for linking to an MPI library. To pass
|
||||
these to the installation command, you can override ``configure_args``
|
||||
like so:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def configure_args(self, spec, prefix):
|
||||
mpi_name = spec['mpi'].name
|
||||
|
||||
# The type of MPI. Supported values are:
|
||||
# OPENMPI, LAM, MPICH, MPICH2, or CRAY
|
||||
if mpi_name == 'openmpi':
|
||||
Rmpi_type = 'OPENMPI'
|
||||
elif mpi_name == 'mpich':
|
||||
Rmpi_type = 'MPICH2'
|
||||
else:
|
||||
raise InstallError('Unsupported MPI type')
|
||||
|
||||
return [
|
||||
'--with-Rmpi-type={0}'.format(Rmpi_type),
|
||||
'--with-mpi={0}'.format(spec['mpi'].prefix),
|
||||
]
|
||||
|
||||
|
||||
There is a similar ``configure_vars`` function that can be overridden
|
||||
to pass variables to the build.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
Alternatives to Spack
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
CRAN hosts over 10,000 R packages, most of which are not in Spack. Many
|
||||
users may not need the advanced features of Spack, and may prefer to
|
||||
install R packages the normal way:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ R
|
||||
> install.packages("ggplot2")
|
||||
|
||||
|
||||
R will search CRAN for the ``ggplot2`` package and install all necessary
|
||||
dependencies for you. If you want to update all installed R packages to
|
||||
the latest release, you can use:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
> update.packages(ask = FALSE)
|
||||
|
||||
|
||||
This works great for users who have internet access, but those on an
|
||||
air-gapped cluster will find it easier to let Spack build a download
|
||||
mirror and install these packages for you.
|
||||
|
||||
Where Spack really shines is its ability to install non-R dependencies
|
||||
and link to them properly, something the R installation mechanism
|
||||
cannot handle.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
External documentation
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
For more information on installing R packages, see:
|
||||
https://stat.ethz.ch/R-manual/R-devel/library/utils/html/INSTALL.html
|
11
lib/spack/docs/build_systems/rubypackage.rst
Normal file
11
lib/spack/docs/build_systems/rubypackage.rst
Normal file
@ -0,0 +1,11 @@
|
||||
.. _rubypackage:
|
||||
|
||||
-----------
|
||||
RubyPackage
|
||||
-----------
|
||||
|
||||
Like Perl, Python, and R, Ruby has its own build system for
|
||||
installing Ruby gems.
|
||||
|
||||
This build system is a work-in-progress. See
|
||||
https://github.com/spack/spack/pull/3127 for more information.
|
301
lib/spack/docs/build_systems/sconspackage.rst
Normal file
301
lib/spack/docs/build_systems/sconspackage.rst
Normal file
@ -0,0 +1,301 @@
|
||||
.. _sconspackage:
|
||||
|
||||
------------
|
||||
SConsPackage
|
||||
------------
|
||||
|
||||
SCons is a general-purpose build system that does not rely on
|
||||
Makefiles to build software. SCons is written in Python, and handles
|
||||
all building and linking itself.
|
||||
|
||||
As far as build systems go, SCons is very non-uniform. It provides a
|
||||
common framework for developers to write build scripts, but the build
|
||||
scripts themselves can vary drastically. Some developers add subcommands
|
||||
like:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ scons clean
|
||||
$ scons build
|
||||
$ scons test
|
||||
$ scons install
|
||||
|
||||
|
||||
Others don't add any subcommands. Some have configuration options that
|
||||
can be specified through variables on the command line. Others don't.
|
||||
|
||||
^^^^^^
|
||||
Phases
|
||||
^^^^^^
|
||||
|
||||
As previously mentioned, SCons allows developers to add subcommands like
|
||||
``build`` and ``install``, but by default, installation usually looks like:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ scons
|
||||
$ scons install
|
||||
|
||||
|
||||
To facilitate this, the ``SConsPackage`` base class provides the
|
||||
following phases:
|
||||
|
||||
#. ``build`` - build the package
|
||||
#. ``install`` - install the package
|
||||
|
||||
Package developers often add unit tests that can be invoked with
|
||||
``scons test`` or ``scons check``. Spack provides a ``test`` method
|
||||
to handle this. Since we don't know which one the package developer
|
||||
chose, the ``test`` method does nothing by default, but can be easily
|
||||
overridden like so:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def test(self):
|
||||
scons('check')
|
||||
|
||||
|
||||
^^^^^^^^^^^^^^^
|
||||
Important files
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
SCons packages can be identified by their ``SConstruct`` files. These
|
||||
files handle everything from setting up subcommands and command-line
|
||||
options to linking and compiling.
|
||||
|
||||
One thing to look for is the ``EnsureSConsVersion`` function:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
EnsureSConsVersion(2, 3, 0)
|
||||
|
||||
|
||||
This means that SCons 2.3.0 is the earliest release that will work.
|
||||
You should specify this in a ``depends_on`` statement.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Build system dependencies
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
At the bare minimum, packages that use the SCons build system need a
|
||||
``scons`` dependency. Since this is always the case, the ``SConsPackage``
|
||||
base class already contains:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
depends_on('scons', type='build')
|
||||
|
||||
|
||||
If you want to specify a particular version requirement, you can override
|
||||
this in your package:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
depends_on('scons@2.3.0:', type='build')
|
||||
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Finding available options
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The first place to start when looking for a list of valid options to
|
||||
build a package is ``scons --help``. Some packages like
|
||||
`kahip <https://github.com/spack/spack/blob/develop/var/spack/repos/builtin/packages/kahip/package.py>`_
|
||||
don't bother overwriting the default SCons help message, so this isn't
|
||||
very useful, but other packages like
|
||||
`serf <https://github.com/spack/spack/blob/develop/var/spack/repos/builtin/packages/serf/package.py>`_
|
||||
print a list of valid command-line variables:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ scons --help
|
||||
scons: Reading SConscript files ...
|
||||
Checking for GNU-compatible C compiler...yes
|
||||
scons: done reading SConscript files.
|
||||
|
||||
PREFIX: Directory to install under ( /path/to/PREFIX )
|
||||
default: /usr/local
|
||||
actual: /usr/local
|
||||
|
||||
LIBDIR: Directory to install architecture dependent libraries under ( /path/to/LIBDIR )
|
||||
default: $PREFIX/lib
|
||||
actual: /usr/local/lib
|
||||
|
||||
APR: Path to apr-1-config, or to APR's install area ( /path/to/APR )
|
||||
default: /usr
|
||||
actual: /usr
|
||||
|
||||
APU: Path to apu-1-config, or to APR's install area ( /path/to/APU )
|
||||
default: /usr
|
||||
actual: /usr
|
||||
|
||||
OPENSSL: Path to OpenSSL's install area ( /path/to/OPENSSL )
|
||||
default: /usr
|
||||
actual: /usr
|
||||
|
||||
ZLIB: Path to zlib's install area ( /path/to/ZLIB )
|
||||
default: /usr
|
||||
actual: /usr
|
||||
|
||||
GSSAPI: Path to GSSAPI's install area ( /path/to/GSSAPI )
|
||||
default: None
|
||||
actual: None
|
||||
|
||||
DEBUG: Enable debugging info and strict compile warnings (yes|no)
|
||||
default: False
|
||||
actual: False
|
||||
|
||||
APR_STATIC: Enable using a static compiled APR (yes|no)
|
||||
default: False
|
||||
actual: False
|
||||
|
||||
CC: Command name or path of the C compiler
|
||||
default: None
|
||||
actual: gcc
|
||||
|
||||
CFLAGS: Extra flags for the C compiler (space-separated)
|
||||
default: None
|
||||
actual:
|
||||
|
||||
LIBS: Extra libraries passed to the linker, e.g. "-l<library1> -l<library2>" (space separated)
|
||||
default: None
|
||||
actual: None
|
||||
|
||||
LINKFLAGS: Extra flags for the linker (space-separated)
|
||||
default: None
|
||||
actual:
|
||||
|
||||
CPPFLAGS: Extra flags for the C preprocessor (space separated)
|
||||
default: None
|
||||
actual: None
|
||||
|
||||
Use scons -H for help about command-line options.
|
||||
|
||||
|
||||
More advanced packages like
|
||||
`cantera <https://github.com/spack/spack/blob/develop/var/spack/repos/builtin/packages/cantera/package.py>`_
|
||||
use ``scons --help`` to print a list of subcommands:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ scons --help
|
||||
scons: Reading SConscript files ...
|
||||
|
||||
SCons build script for Cantera
|
||||
|
||||
Basic usage:
|
||||
'scons help' - print a description of user-specifiable options.
|
||||
|
||||
'scons build' - Compile Cantera and the language interfaces using
|
||||
default options.
|
||||
|
||||
'scons clean' - Delete files created while building Cantera.
|
||||
|
||||
'[sudo] scons install' - Install Cantera.
|
||||
|
||||
'[sudo] scons uninstall' - Uninstall Cantera.
|
||||
|
||||
'scons test' - Run all tests which did not previously pass or for which the
|
||||
results may have changed.
|
||||
|
||||
'scons test-reset' - Reset the passing status of all tests.
|
||||
|
||||
'scons test-clean' - Delete files created while running the tests.
|
||||
|
||||
'scons test-help' - List available tests.
|
||||
|
||||
'scons test-NAME' - Run the test named "NAME".
|
||||
|
||||
'scons <command> dump' - Dump the state of the SCons environment to the
|
||||
screen instead of doing <command>, e.g.
|
||||
'scons build dump'. For debugging purposes.
|
||||
|
||||
'scons samples' - Compile the C++ and Fortran samples.
|
||||
|
||||
'scons msi' - Build a Windows installer (.msi) for Cantera.
|
||||
|
||||
'scons sphinx' - Build the Sphinx documentation
|
||||
|
||||
'scons doxygen' - Build the Doxygen documentation
|
||||
|
||||
|
||||
You'll notice that cantera provides a ``scons help`` subcommand. Running
|
||||
``scons help`` prints a list of valid command-line variables.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Passing arguments to scons
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Now that you know what arguments the project accepts, you can add them to
|
||||
the package build phase. This is done by overriding ``build_args`` like so:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def build_args(self, spec, prefix):
|
||||
args = [
|
||||
'PREFIX={0}'.format(prefix),
|
||||
'ZLIB={0}'.format(spec['zlib'].prefix),
|
||||
]
|
||||
|
||||
if '+debug' in spec:
|
||||
args.append('DEBUG=yes')
|
||||
else:
|
||||
args.append('DEBUG=no')
|
||||
|
||||
return args
|
||||
|
||||
|
||||
``SConsPackage`` also provides an ``install_args`` function that you can
|
||||
override to pass additional arguments to ``scons install``.
|
||||
|
||||
^^^^^^^^^^^^^^^^^
|
||||
Compiler wrappers
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
By default, SCons builds all packages in a separate execution environment,
|
||||
and doesn't pass any environment variables from the user environment.
|
||||
Even changes to ``PATH`` are not propagated unless the package developer
|
||||
does so.
|
||||
|
||||
This is particularly troublesome for Spack's compiler wrappers, which depend
|
||||
on environment variables to manage dependencies and linking flags. In many
|
||||
cases, SCons packages are not compatible with Spack's compiler wrappers,
|
||||
and linking must be done manually.
|
||||
|
||||
First of all, check the list of valid options for anything relating to
|
||||
environment variables. For example, cantera has the following option:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
* env_vars: [ string ]
|
||||
Environment variables to propagate through to SCons. Either the
|
||||
string "all" or a comma separated list of variable names, e.g.
|
||||
'LD_LIBRARY_PATH,HOME'.
|
||||
- default: 'LD_LIBRARY_PATH,PYTHONPATH'
|
||||
|
||||
|
||||
In the case of cantera, using ``env_vars=all`` allows us to use
|
||||
Spack's compiler wrappers. If you don't see an option related to
|
||||
environment variables, try using Spack's compiler wrappers by passing
|
||||
``spack_cc``, ``spack_cxx``, and ``spack_fc`` via the ``CC``, ``CXX``,
|
||||
and ``FC`` arguments, respectively. If you pass them to the build and
|
||||
you see an error message like:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
Spack compiler must be run from Spack! Input 'SPACK_PREFIX' is missing.
|
||||
|
||||
|
||||
you'll know that the package isn't compatible with Spack's compiler
|
||||
wrappers. In this case, you'll have to use the path to the actual
|
||||
compilers, which are stored in ``self.compiler.cc`` and friends.
|
||||
Note that this may involve passing additional flags to the build to
|
||||
locate dependencies, a task normally done by the compiler wrappers.
|
||||
serf is an example of a package with this limitation.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
External documentation
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
For more information on the SCons build system, see:
|
||||
http://scons.org/documentation.html
|
124
lib/spack/docs/build_systems/wafpackage.rst
Normal file
124
lib/spack/docs/build_systems/wafpackage.rst
Normal file
@ -0,0 +1,124 @@
|
||||
.. _wafpackage:
|
||||
|
||||
----------
|
||||
WafPackage
|
||||
----------
|
||||
|
||||
Like SCons, Waf is a general-purpose build system that does not rely
|
||||
on Makefiles to build software.
|
||||
|
||||
^^^^^^
|
||||
Phases
|
||||
^^^^^^
|
||||
|
||||
The ``WafPackage`` base class comes with the following phases:
|
||||
|
||||
#. ``configure`` - configure the project
|
||||
#. ``build`` - build the project
|
||||
#. ``install`` - install the project
|
||||
|
||||
By default, these phases run:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ python waf configure --prefix=/path/to/installation/prefix
|
||||
$ python waf build
|
||||
$ python waf install
|
||||
|
||||
|
||||
Each of these are standard Waf commands and can be found by running:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ python waf --help
|
||||
|
||||
|
||||
Each phase provides a ``<phase>`` function that runs:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ python waf -j<jobs> <phase>
|
||||
|
||||
|
||||
where ``<jobs>`` is the number of parallel jobs to build with. Each phase
|
||||
also has a ``<phase_args>`` function that can pass arguments to this call.
|
||||
All of these functions are empty except for the ``configure_args``
|
||||
function, which passes ``--prefix=/path/to/installation/prefix``.
|
||||
|
||||
^^^^^^^
|
||||
Testing
|
||||
^^^^^^^
|
||||
|
||||
``WafPackage`` also provides ``test`` and ``installtest`` methods,
|
||||
which are run after the ``build`` and ``install`` phases, respectively.
|
||||
By default, these phases do nothing, but you can override them to
|
||||
run package-specific unit tests. For example, the
|
||||
`py-py2cairo <https://github.com/spack/spack/blob/develop/var/spack/repos/builtin/packages/py-py2cairo/package.py>`_
|
||||
package uses:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def installtest(self):
|
||||
with working_dir('test'):
|
||||
pytest = which('py.test')
|
||||
pytest()
|
||||
|
||||
|
||||
^^^^^^^^^^^^^^^
|
||||
Important files
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
Each Waf package comes with a custom ``waf`` build script, written in
|
||||
Python. This script contains instructions to build the project.
|
||||
|
||||
The package also comes with a ``wscript`` file. This file is used to
|
||||
override the default ``configure``, ``build``, and ``install`` phases
|
||||
to customize the Waf project. It also allows developers to override
|
||||
the default ``./waf --help`` message. Check this file to find useful
|
||||
information about dependencies and the minimum versions that are
|
||||
supported.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Build system dependencies
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
``WafPackage`` does not require ``waf`` to build. ``waf`` is only
|
||||
needed to create the ``./waf`` script. Since ``./waf`` is a Python
|
||||
script, Python is needed to build the project. ``WafPackage`` adds
|
||||
the following dependency automatically:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
depends_on('python@2.5:', type='build')
|
||||
|
||||
|
||||
Waf only supports Python 2.5 and up.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Passing arguments to waf
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
As previously mentioned, each phase comes with a ``<phase_args>``
|
||||
function that can be used to pass arguments to that particular
|
||||
phase. For example, if you need to pass arguments to the build
|
||||
phase, you can use:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def build_args(self, spec, prefix):
|
||||
args = []
|
||||
|
||||
if self.run_tests:
|
||||
args.append('--test')
|
||||
|
||||
return args
|
||||
|
||||
|
||||
A list of valid options can be found by running ``./waf --help``.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
External documentation
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
For more information on the Waf build system, see:
|
||||
https://waf.io/book/
|
@ -73,6 +73,7 @@ or refer to the full manual below.
|
||||
|
||||
contribution_guide
|
||||
packaging_guide
|
||||
build_systems
|
||||
developer_guide
|
||||
docker_for_developers
|
||||
Spack API Docs <spack>
|
||||
|
Loading…
Reference in New Issue
Block a user