Hdf5 package: build on Windows (#31141)

* Enable hdf5 build (including +mpi) on Windows
* This includes updates to hdf5 dependencies openssl (minor edit) and
  bzip2 (more-extensive edits)
* Add binary-based installation of msmpi (this is currently the only
  supported MPI implementation in Spack for Windows). Note that this
  does not install to the Spack-specified prefix. This implementation
  will be replaced with a source-based implementation

Co-authored-by: John Parent <john.parent@kitware.com>
This commit is contained in:
Jared Popelar 2022-11-17 11:40:53 -07:00 committed by GitHub
parent 6811651a0f
commit 381bedf369
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 94 additions and 21 deletions

View file

@ -1704,9 +1704,11 @@ dependencies or incompatible build tools like autoconf. Here are several
packages known to work on Windows: packages known to work on Windows:
* abseil-cpp * abseil-cpp
* bzip2
* clingo * clingo
* cpuinfo * cpuinfo
* cmake * cmake
* hdf5
* glm * glm
* nasm * nasm
* netlib-lapack (requires Intel Fortran) * netlib-lapack (requires Intel Fortran)

View file

@ -1089,7 +1089,7 @@ def _libs_default_handler(descriptor, spec, cls):
home = getattr(spec.package, "home") home = getattr(spec.package, "home")
# Avoid double 'lib' for packages whose names already start with lib # Avoid double 'lib' for packages whose names already start with lib
if not name.startswith("lib"): if not name.startswith("lib") and not spec.satisfies("platform=windows"):
name = "lib" + name name = "lib" + name
# If '+shared' search only for shared library; if '~shared' search only for # If '+shared' search only for shared library; if '~shared' search only for

View file

@ -4,6 +4,7 @@
# SPDX-License-Identifier: (Apache-2.0 OR MIT) # SPDX-License-Identifier: (Apache-2.0 OR MIT)
import re import re
import sys
from spack.package import * from spack.package import *
@ -24,11 +25,23 @@ class Bzip2(Package, SourcewarePackage):
version("1.0.7", sha256="e768a87c5b1a79511499beb41500bcc4caf203726fff46a6f5f9ad27fe08ab2b") version("1.0.7", sha256="e768a87c5b1a79511499beb41500bcc4caf203726fff46a6f5f9ad27fe08ab2b")
version("1.0.6", sha256="a2848f34fcd5d6cf47def00461fcb528a0484d8edef8208d6d2e2909dc61d9cd") version("1.0.6", sha256="a2848f34fcd5d6cf47def00461fcb528a0484d8edef8208d6d2e2909dc61d9cd")
variant("shared", default=True, description="Enables the build of shared libraries.") variant(
"shared",
default=(sys.platform != "win32"),
description="Enables the build of shared libraries.",
)
variant("pic", default=False, description="Build static libraries with PIC") variant("pic", default=False, description="Build static libraries with PIC")
variant("debug", default=False, description="Enable debug symbols and disable optimization") variant("debug", default=False, description="Enable debug symbols and disable optimization")
depends_on("diffutils", type="build") # makefile.msc doesn't provide a shared recipe
conflicts(
"+shared",
when="platform=windows",
msg="Windows makefile has no recipe for shared builds, use ~shared.",
)
if sys.platform != "win32":
depends_on("diffutils", type="build")
@classmethod @classmethod
def determine_version(cls, exe): def determine_version(cls, exe):
@ -52,9 +65,10 @@ def flag_handler(self, name, flags):
def patch(self): def patch(self):
if self.spec.satisfies("+debug"): if self.spec.satisfies("+debug"):
for makefile in ["Makefile", "Makefile-libbz2_so"]: for makefile in ["Makefile", "Makefile-libbz2_so", "makefile.msc"]:
filter_file(r"-O ", "-O0 ", makefile) filter_file(r"-O ", "-O0 ", makefile)
filter_file(r"-O2 ", "-O0 ", makefile) filter_file(r"-O2 ", "-O0 ", makefile)
filter_file(r"-Ox ", "-O0 ", makefile)
# bzip2 comes with two separate Makefiles for static and dynamic builds # bzip2 comes with two separate Makefiles for static and dynamic builds
# Tell both to use Spack's compiler wrapper instead of GCC # Tell both to use Spack's compiler wrapper instead of GCC
@ -82,13 +96,13 @@ def patch(self):
"$(CC) -dynamiclib -Wl,-install_name -Wl,@rpath/libbz2.{0}.dylib " "$(CC) -dynamiclib -Wl,-install_name -Wl,@rpath/libbz2.{0}.dylib "
"-current_version {1} -compatibility_version {2} -o libbz2.{3}.dylib $(OBJS)" "-current_version {1} -compatibility_version {2} -o libbz2.{3}.dylib $(OBJS)"
).format(v1, v2, v3, v3), ).format(v1, v2, v3, v3),
**kwargs **kwargs,
) )
mf.filter( mf.filter(
"$(CC) $(CFLAGS) -o bzip2-shared bzip2.c libbz2.so.{0}".format(v3), "$(CC) $(CFLAGS) -o bzip2-shared bzip2.c libbz2.so.{0}".format(v3),
"$(CC) $(CFLAGS) -o bzip2-shared bzip2.c libbz2.{0}.dylib".format(v3), "$(CC) $(CFLAGS) -o bzip2-shared bzip2.c libbz2.{0}.dylib".format(v3),
**kwargs **kwargs,
) )
mf.filter( mf.filter(
"rm -f libbz2.so.{0}".format(v2), "rm -f libbz2.{0}.dylib".format(v2), **kwargs "rm -f libbz2.so.{0}".format(v2), "rm -f libbz2.{0}.dylib".format(v2), **kwargs
@ -96,7 +110,7 @@ def patch(self):
mf.filter( mf.filter(
"ln -s libbz2.so.{0} libbz2.so.{1}".format(v3, v2), "ln -s libbz2.so.{0} libbz2.so.{1}".format(v3, v2),
"ln -s libbz2.{0}.dylib libbz2.{1}.dylib".format(v3, v2), "ln -s libbz2.{0}.dylib libbz2.{1}.dylib".format(v3, v2),
**kwargs **kwargs,
) )
def install(self, spec, prefix): def install(self, spec, prefix):
@ -105,8 +119,23 @@ def install(self, spec, prefix):
make("-f", "Makefile-libbz2_so") make("-f", "Makefile-libbz2_so")
# Build the static library and everything else # Build the static library and everything else
make() if self.spec.satisfies("platform=windows"):
make("install", "PREFIX={0}".format(prefix)) # Build step
nmake = Executable("nmake.exe")
nmake("-f", "makefile.msc")
# Install step
mkdirp(self.prefix.include)
mkdirp(self.prefix.lib)
mkdirp(self.prefix.bin)
mkdirp(self.prefix.man)
mkdirp(self.prefix.man.man1)
install("*.h", self.prefix.include)
install("*.lib", self.prefix.lib)
install("*.exe", self.prefix.bin)
install("*.1", self.prefix.man.man1)
else:
make()
make("install", "PREFIX={0}".format(prefix))
if "+shared" in spec: if "+shared" in spec:
install("bzip2-shared", join_path(prefix.bin, "bzip2")) install("bzip2-shared", join_path(prefix.bin, "bzip2"))
@ -124,7 +153,9 @@ def install(self, spec, prefix):
for libname in (lib, lib1, lib2): for libname in (lib, lib1, lib2):
symlink(lib3, libname) symlink(lib3, libname)
with working_dir(prefix.bin): # These files won't be in a Windows installation
force_remove("bunzip2", "bzcat") if not self.spec.satisfies("platform=windows"):
symlink("bzip2", "bunzip2") with working_dir(prefix.bin):
symlink("bzip2", "bzcat") force_remove("bunzip2", "bzcat")
symlink("bzip2", "bunzip2")
symlink("bzip2", "bzcat")

View file

@ -196,13 +196,16 @@ class Hdf5(CMakePackage):
depends_on("cmake@3.12:", type="build") depends_on("cmake@3.12:", type="build")
depends_on("msmpi", when="+mpi platform=windows")
depends_on("mpi", when="+mpi") depends_on("mpi", when="+mpi")
depends_on("java", type=("build", "run"), when="+java") depends_on("java", type=("build", "run"), when="+java")
depends_on("szip", when="+szip") depends_on("szip", when="+szip")
depends_on("zlib@1.1.2:") depends_on("zlib@1.1.2:")
# The compiler wrappers (h5cc, h5fc, etc.) run 'pkg-config'. # The compiler wrappers (h5cc, h5fc, etc.) run 'pkg-config'.
depends_on("pkgconfig", type="run") # Skip this on Windows since pkgconfig is autotools
for plat in ["cray", "darwin", "linux"]:
depends_on("pkgconfig", when="platform=%s" % plat, type="run")
conflicts("api=v114", when="@1.6:1.12", msg="v114 is not compatible with this release") conflicts("api=v114", when="@1.6:1.12", msg="v114 is not compatible with this release")
conflicts("api=v112", when="@1.6:1.10", msg="v112 is not compatible with this release") conflicts("api=v112", when="@1.6:1.10", msg="v112 is not compatible with this release")
@ -498,7 +501,7 @@ def cmake_args(self):
if api != "default": if api != "default":
args.append(self.define("DEFAULT_API_VERSION", api)) args.append(self.define("DEFAULT_API_VERSION", api))
if "+mpi" in spec: if "+mpi" in spec and "platform=windows" not in spec:
args.append(self.define("CMAKE_C_COMPILER", spec["mpi"].mpicc)) args.append(self.define("CMAKE_C_COMPILER", spec["mpi"].mpicc))
if "+cxx" in self.spec: if "+cxx" in self.spec:
@ -567,7 +570,7 @@ def fix_package_config(self):
r"(Requires(?:\.private)?:.*)(hdf5[^\s,]*)(?:-[^\s,]*)(.*)", r"(Requires(?:\.private)?:.*)(hdf5[^\s,]*)(?:-[^\s,]*)(.*)",
r"\1\2\3", r"\1\2\3",
*pc_files, *pc_files,
backup=False backup=False,
) )
# Create non-versioned symlinks to the versioned pkg-config files: # Create non-versioned symlinks to the versioned pkg-config files:

View file

@ -0,0 +1,42 @@
# 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)
from spack.package import *
class Msmpi(Package):
"""A Windows-specced build of MPICH provided directly by
Microsoft Support Team
"""
homepage = "https://www.microsoft.com/en-us/download/default.aspx"
maintainers = ["jpopelar"]
executable = ["mpiexec.exe"]
version(
"10.0",
sha256="7dae13797627726f67fab9c1d251aec2df9ecd25939984645ec05748bdffd396",
extension="exe",
expand=False,
)
provides("mpi")
conflicts("platform=linux")
conflicts("platform=darwin")
conflicts("platform=cray")
def url_for_version(self, version):
return "https://download.microsoft.com/download/A/E/0/AE002626-9D9D-448D-8197-1EA510E297CE/msmpisetup.exe"
def determine_version(self, exe):
output = Executable("mpiexec.exe")
ver_str = re.search("[Version ([0-9.]+)]", output)
return Version(ver_str.group(0)) if ver_str else None
def install(self, spec, prefix):
installer = Executable("msmpisetup.exe")
installer("-unattend")

View file

@ -420,11 +420,6 @@ def install(self, spec, prefix):
# (e.g. gcc) will not accept them. # (e.g. gcc) will not accept them.
filter_file(r"-arch x86_64", "", "Makefile") filter_file(r"-arch x86_64", "", "Makefile")
if spec.satisfies("+dynamic"):
# This variant only makes sense for Windows
if spec.satisfies("platform=windows"):
filter_file(r"MT", "MD", "makefile")
if spec.satisfies("platform=windows"): if spec.satisfies("platform=windows"):
host_make = nmake host_make = nmake
else: else: