summaryrefslogtreecommitdiff
path: root/var/spack/repos/builtin/packages/netcdf-fortran/package.py
blob: 63c8c016b2a63fa5f72648026228eb7c436c0a7f (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
# Copyright 2013-2022 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)

import glob
import os
from shutil import Error, copyfile

from spack.package import *


class NetcdfFortran(AutotoolsPackage):
    """NetCDF (network Common Data Form) is a set of software libraries and
    machine-independent data formats that support the creation, access, and
    sharing of array-oriented scientific data. This is the Fortran
    distribution."""

    homepage = "https://www.unidata.ucar.edu/software/netcdf"
    url = "https://downloads.unidata.ucar.edu/netcdf-fortran/4.5.4/netcdf-fortran-4.5.4.tar.gz"

    maintainers = ["skosukhin", "WardF"]

    version("4.5.4", sha256="0a19b26a2b6e29fab5d29d7d7e08c24e87712d09a5cafeea90e16e0a2ab86b81")
    version("4.5.3", sha256="123a5c6184336891e62cf2936b9f2d1c54e8dee299cfd9d2c1a1eb05dd668a74")
    version("4.5.2", sha256="b959937d7d9045184e9d2040a915d94a7f4d0185f4a9dceb8f08c94b0c3304aa")
    version("4.4.5", sha256="2467536ce29daea348c736476aa8e684c075d2f6cab12f3361885cb6905717b8")
    version("4.4.4", sha256="b2d395175f8d283e68c8be516e231a96b191ade67ad0caafaf7fa01b1e6b5d75")
    version("4.4.3", sha256="330373aa163d5931e475b5e83da5c1ad041e855185f24e6a8b85d73b48d6cda9")

    variant("pic", default=True, description="Produce position-independent code (for shared libs)")
    variant("shared", default=True, description="Enable shared library")
    variant("doc", default=False, description="Enable building docs")

    depends_on("netcdf-c")
    depends_on("netcdf-c@4.7.4:", when="@4.5.3:")  # nc_def_var_szip required
    depends_on("doxygen", when="+doc", type="build")

    # The default libtool.m4 is too old to handle NAG compiler properly:
    # https://github.com/Unidata/netcdf-fortran/issues/94
    # Moreover, Libtool can't handle '-pthread' flag coming from libcurl,
    # doesn't inject convenience libraries into the shared ones, and is unable
    # to detect NAG when it is called with an MPI wrapper.
    patch("nag_libtool_2.4.2.patch", when="@:4.4.4%nag")
    patch("nag_libtool_2.4.6.patch", when="@4.4.5:%nag")

    # Enable 'make check' for NAG, which is too strict.
    patch("nag_testing.patch", when="@4.4.5%nag")

    # File fortran/nf_logging.F90 is compiled without -DLOGGING, which leads
    # to missing symbols in the library. Additionally, the patch enables
    # building with NAG, which refuses to compile empty source files (see also
    # comments in the patch):
    patch("logging.patch", when="@:4.4.5")

    # Prevent excessive linking to system libraries. Without this patch the
    # library might get linked to the system installation of libcurl. See
    # https://github.com/Unidata/netcdf-fortran/commit/0a11f580faebbc1c4dce68bf5135709d1c7c7cc1#diff-67e997bcfdac55191033d57a16d1408a
    patch("excessive_linking.patch", when="@4.4.5")

    # Parallel builds do not work in the fortran directory. This patch is
    # derived from https://github.com/Unidata/netcdf-fortran/pull/211
    patch("no_parallel_build.patch", when="@4.5.2")

    def flag_handler(self, name, flags):
        if name == "cflags":
            if "+pic" in self.spec:
                flags.append(self.compiler.cc_pic_flag)
        elif name == "fflags":
            if "+pic" in self.spec:
                flags.append(self.compiler.f77_pic_flag)
            if self.spec.satisfies("%gcc@10:"):
                # https://github.com/Unidata/netcdf-fortran/issues/212
                flags.append("-fallow-argument-mismatch")
            elif self.compiler.name == "cce":
                # Cray compiler generates module files with uppercase names by
                # default, which is not handled by the makefiles of
                # NetCDF-Fortran:
                # https://github.com/Unidata/netcdf-fortran/pull/221.
                # The following flag forces the compiler to produce module
                # files with lowercase names.
                flags.append("-ef")

        # Note that cflags and fflags should be added by the compiler wrapper
        # and not on the command line to avoid overriding the default
        # compilation flags set by the configure script:
        return flags, None, None

    @property
    def libs(self):
        libraries = ["libnetcdff"]

        query_parameters = self.spec.last_query.extra_parameters

        if "shared" in query_parameters:
            shared = True
        elif "static" in query_parameters:
            shared = False
        else:
            shared = "+shared" in self.spec

        libs = find_libraries(libraries, root=self.prefix, shared=shared, recursive=True)

        if libs:
            return libs

        msg = "Unable to recursively locate {0} {1} libraries in {2}"
        raise spack.error.NoLibrariesError(
            msg.format("shared" if shared else "static", self.spec.name, self.spec.prefix)
        )

    def configure_args(self):
        config_args = ["--enable-static"]
        config_args += self.enable_or_disable("shared")
        config_args += self.enable_or_disable("doxygen", variant="doc")

        netcdf_c_spec = self.spec["netcdf-c"]
        if "+mpi" in netcdf_c_spec or "+parallel-netcdf" in netcdf_c_spec:
            # Prefixing with 'mpiexec -n 4' is not necessarily the correct way
            # to launch MPI programs on a particular machine (e.g. 'srun -n 4'
            # with additional arguments might be the right one). Therefore, we
            # make sure the parallel tests are not launched at all (although it
            # is the default behaviour currently):
            config_args.append("--disable-parallel-tests")
            if self.spec.satisfies("@4.5.0:4.5.2"):
                # Versions from 4.5.0 to 4.5.2 check whether the Fortran MPI
                # interface is available and fail the configuration if it is
                # not. However, the interface is needed for a subset of the test
                # programs only (the library itself does not need it), which are
                # not run by default and explicitly disabled above. To avoid the
                # configuration failure, we set the following cache variable:
                config_args.append("ac_cv_func_MPI_File_open=yes")

        return config_args

    @run_after("configure")
    def patch_libtool(self):
        """AOCC support for NETCDF-F"""
        if "%aocc" in self.spec:
            # Libtool does not fully support the compiler toolchain, therefore
            # we have to patch the script. The C compiler normally gets
            # configured correctly, the variables of interest in the
            # 'BEGIN LIBTOOL CONFIG' section are set to non-empty values and,
            # therefore, are not affected by the replacements below. A more
            # robust solution would be to extend the filter_file function with
            # an additional argument start_at and perform the replacements
            # between the '# ### BEGIN LIBTOOL TAG CONFIG: FC' and
            # '# ### END LIBTOOL TAG CONFIG: FC' markers for the Fortran
            # compiler, and between the '# ### BEGIN LIBTOOL TAG CONFIG: F77'
            # and '# ### END LIBTOOL TAG CONFIG: F77' markers for the Fortran 77
            # compiler.

            # How to pass a linker flag through the compiler:
            filter_file(r'^wl=""$', 'wl="{0}"'.format(self.compiler.linker_arg), "libtool")

            # Additional compiler flags for building library objects (we need
            # this to enable shared libraries when building with ~pic). Note
            # that the following will set fc_pic_flag for both FC and F77, which
            # in the case of AOCC, should not be a problem. If it is, the
            # aforementioned modification of the filter_file function could be
            # a solution.
            filter_file(
                r'^pic_flag=""$', 'pic_flag=" {0}"'.format(self.compiler.fc_pic_flag), "libtool"
            )

            # The following is supposed to tell the compiler to use the GNU
            # linker. However, the replacement does not happen (at least for
            # NetCDF-Fortran 4.5.3) because the replaced substring (i.e. the
            # first argument passed to the filter_file function) is not present
            # in the file. The flag should probably be added to 'ldflags' in the
            # flag_handler method above (another option is to add the flag to
            # 'ldflags' in compilers.yaml automatically as it was done for other
            # flags in https://github.com/spack/spack/pull/22219).
            filter_file(
                r"\${wl}-soname \$wl\$soname",
                r"-fuse-ld=ld -Wl,-soname,\$soname",
                "libtool",
                string=True,
            )

        # TODO: resolve the NAG-related issues in a similar way: remove the
        #  respective patch files and tune the generated libtool script instead.

    @when("@:4.4.5")
    def check(self):
        with working_dir(self.build_directory):
            make("check", parallel=False)

    @run_after("install")
    def cray_module_filenames(self):
        # Cray compiler searches for module files with uppercase names by
        # default and with lowercase names when the '-ef' flag is specified.
        # To avoid warning messages when compiler user applications in both
        # cases, we create copies of all '*.mod' files in the prefix/include
        # with names in upper- and lowercase.
        if self.spec.compiler.name != "cce":
            return

        with working_dir(self.spec.prefix.include):
            for f in glob.glob("*.mod"):
                name, ext = os.path.splitext(f)
                try:
                    # Create a copy with uppercase name:
                    copyfile(f, name.upper() + ext)
                except Error:
                    # Assume that the exception tells us that the file with
                    # uppercase name already exists. Try to create a file with
                    # lowercase name then:
                    try:
                        copyfile(f, name.lower() + ext)
                    except Error:
                        pass