summaryrefslogtreecommitdiff
path: root/lib/spack/docs/build_systems/makefilepackage.rst
blob: 2555ac163ae2c960712114d46b83efe00b0d3904 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
.. Copyright 2013-2024 Lawrence Livermore National Security, LLC and other
   Spack Project Developers. See the top-level COPYRIGHT file for details.

   SPDX-License-Identifier: (Apache-2.0 OR MIT)

.. _makefilepackage:

--------
Makefile
--------

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 ``MakefileBuilder`` and ``MakefilePackage`` base classes come 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",
           f"BLASLIB={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(r"^\s*CC\s*=.*",  f"CC = {spack_cc}")
       makefile.filter(r"^\s*CXX\s*=.*", f"CXX = {spack_cxx}")
       makefile.filter(r"^\s*F77\s*=.*", f"F77 = {spack_f77}")
       makefile.filter(r"^\s*FC\s*=.*",  f"FC = {spack_fc}")


`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 spec.satisfies("+blas"):
           config["BLAS_LIBS"] = spec["blas"].libs.joined()

       with open("make.inc", "w") as inc:
           for key in config:
               inc.write(f"{key} = {config[key]}\n")


`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 = [
           f"INSTALL_DIR = {prefix}",
           "INCLUDE_DIR = $(INSTALL_DIR)/include",
           "LIBRARY_DIR = $(INSTALL_DIR)/lib",
       ]

       with open("make.inc", "w") as inc:
           for var in config:
               inc.write(f"{var}\n")


`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