diff --git a/repos/spack_repo/builtin/packages/chrono/package.py b/repos/spack_repo/builtin/packages/chrono/package.py new file mode 100644 index 00000000000..6a515f63052 --- /dev/null +++ b/repos/spack_repo/builtin/packages/chrono/package.py @@ -0,0 +1,149 @@ +# Copyright Spack Project Developers. See COPYRIGHT file for details. +# +# SPDX-License-Identifier: (Apache-2.0 OR MIT) + +import os + +from spack_repo.builtin.build_systems.cmake import CMakePackage + +from spack.package import * + + +class Chrono(CMakePackage): + """Project Chrono is an open-source multi-physics simulation engine for + rigid and flexible multibody dynamics, collision detection, granular + material, fluid-solid interaction and vehicle dynamics. + + The ``+python`` variant additionally builds ``pychrono``, the SWIG-generated + Python module, which Chrono builds from the same tree rather than shipping + as a separate project. + """ + + homepage = "https://projectchrono.org/" + url = "https://github.com/projectchrono/chrono/archive/refs/tags/10.0.0.tar.gz" + git = "https://github.com/projectchrono/chrono.git" + + maintainers("cekees") + + license("BSD-3-Clause") + + version("main", branch="main") + version("10.0.0", sha256="806e5e24a06f26bbd42344dd1f13d75e3214c9eb29901553574b9c87217d8722") + + variant("python", default=False, description="Build the pychrono Python module") + variant("openmp", default=True, description="Enable OpenMP parallelism") + variant("shared", default=True, description="Build shared libraries") + variant("simd", default=False, description="Enable SIMD vectorization") + + # Chrono compiles C as well as C++ (e.g. chrono_thirdparty/libstl/stlfile.c), + # so both must be declared -- omitting "c" fails at cmake time with + # "[spack cc]: Error: SPACK_CC_* variables not set". + depends_on("c", type="build") + depends_on("cxx", type="build") + depends_on("cmake@3.18:", type="build") + + # Chrono's core headers include Eigen directly, so it is a link-time + # dependency of every consumer, not just a build-time one. + depends_on("eigen@3.3.0:") + + # The Python module is generated with SWIG at build time + # (src/chrono_swig/chrono_python/CMakeLists.txt does find_package(SWIG)). + with when("+python"): + # NOT extends("python"): Chrono installs pychrono into + # share/chrono/python, never into site-packages -- verified on an actual + # install, whose prefix contains only include/ lib/ share/ + # importer_blender/. extends() would promise spack a layout that does not + # exist. PYTHONPATH is set for this package and its dependents below, + # which is the same thing PETSc's own proteus build does + # (PYTHONPATH="$PREFIX/share/chrono/python"). + depends_on("python@3.8:", type=("build", "run")) + depends_on("swig@4:", type="build") + + depends_on("llvm-openmp", when="+openmp platform=darwin") + + # ------------------------------------------------------------------ + # ChClassFactory.h uses std::enable_if, std::is_polymorphic and + # std::is_abstract but includes only + # , relying on one of those to pull in + # transitively. libstdc++ and Apple's libc++ still do; + # newer libc++ (e.g. conda-forge's) does not, and Chrono_core then fails + # with 14 errors of the form + # + # ChClassFactory.h:48:53: error: no member named 'is_polymorphic' in + # namespace 'std' + # + # Belongs upstream in projectchrono/chrono; done here as a filter_file + # rather than a patch so it stays a no-op once upstream adds the include. + # ------------------------------------------------------------------ + @run_before("cmake") + def add_missing_type_traits_include(self): + header = join_path(self.stage.source_path, "src", "chrono", "core", "ChClassFactory.h") + if not os.path.exists(header): + return + with open(header) as f: + if "#include " in f.read(): + return + filter_file(r"^#include $", "#include \n#include ", header) + + def cmake_args(self): + args = [ + self.define_from_variant("BUILD_SHARED_LIBS", "shared"), + self.define("BUILD_TESTING", False), + self.define("BUILD_DEMOS", False), + # Optional modules, each pulling dependencies Chrono does not need + # for multibody dynamics. Option names carry the CH_ prefix as of + # 10.0.0 (confirmed against the tag's own CMakeLists.txt). + self.define("CH_ENABLE_MODULE_CASCADE", False), + self.define("CH_ENABLE_MODULE_IRRLICHT", False), + self.define("CH_ENABLE_MODULE_MATLAB", False), + self.define("CH_ENABLE_MODULE_POSTPROCESS", False), + self.define("CH_ENABLE_MODULE_ROS", False), + self.define("CH_ENABLE_MODULE_VEHICLE", False), + self.define_from_variant("CH_ENABLE_MODULE_PYTHON", "python"), + self.define_from_variant("CH_ENABLE_OPENMP", "openmp"), + self.define_from_variant("CH_USE_SIMD", "simd"), + # Chrono appends CH_DEBUG_POSTFIX ("_d") to every library name in a + # Debug build, but consumers link the unsuffixed name. Force it + # empty so a Debug build keeps -g without renaming the libraries. + self.define("CH_DEBUG_POSTFIX", ""), + ] + + if self.spec.satisfies("+python"): + # Chrono's find_package(Python3 ... COMPONENTS Development) has been + # observed to populate only Python3_EXECUTABLE, after which the SWIG + # module links without libpython and fails on every CPython symbol. + # Point CMake at the library and headers directly. + python = self.spec["python"] + args += [ + self.define("Python3_EXECUTABLE", python.command.path), + self.define("Python3_INCLUDE_DIR", python.headers.directories[0]), + self.define("Python3_LIBRARY", python.libs[0]), + ] + if self.spec.satisfies("platform=darwin"): + # chrono_python's _core module target neither links -lpython nor + # passes -undefined dynamic_lookup. Unresolved symbols in a + # shared object are fine on Linux and resolve at dlopen time, + # but are a hard link error for a macOS bundle. + args.append(self.define("CMAKE_MODULE_LINKER_FLAGS", "-undefined dynamic_lookup")) + + return args + + # pychrono lives in share/chrono/python, so nothing finds it by default. + # Setting CH_INSTALL_PYTHON does not move it (tried against 10.0.0: the + # module still installed to share/chrono/python), so expose it explicitly -- + # for this package's own run environment and for anything depending on it. + @property + def _pychrono_dir(self): + return join_path(self.prefix, "share", "chrono", "python") + + def setup_run_environment(self, env): + if self.spec.satisfies("+python"): + env.prepend_path("PYTHONPATH", self._pychrono_dir) + + def setup_dependent_build_environment(self, env, dependent_spec): + if self.spec.satisfies("+python"): + env.prepend_path("PYTHONPATH", self._pychrono_dir) + + def setup_dependent_run_environment(self, env, dependent_spec): + if self.spec.satisfies("+python"): + env.prepend_path("PYTHONPATH", self._pychrono_dir) diff --git a/repos/spack_repo/builtin/packages/openblas/package.py b/repos/spack_repo/builtin/packages/openblas/package.py index 36a62ad3245..9ad1693fdd6 100644 --- a/repos/spack_repo/builtin/packages/openblas/package.py +++ b/repos/spack_repo/builtin/packages/openblas/package.py @@ -647,7 +647,26 @@ def build(self, pkg: MakefilePackage, spec: Spec, prefix: Prefix) -> None: with working_dir(self.build_directory): # Due to the verbosity of the command line and number of object # files created, we suppress makefile command echoing via `-s`. - make("-s", *self.make_defs) + # Build the library targets only, ONE AT A TIME. A bare "make" is + # OpenBLAS's default "all" -- "libs netlib $(RELA) tests shared" -- so + # the test suite is built AND RUN by an ordinary install. That defeats + # the check_build/check_install hooks below, which are guarded by + # @on_package_attributes(run_tests=True) and are the intended opt-in, + # and it turns any test failure into a hard build failure: on macOS + # x86_64 (TARGET=SKYLAKEX NO_AVX512=1 DYNAMIC_ARCH=1) "TEST 1/125 + # min:smin_negative" dies with "Bus error: 10" though the library + # itself builds cleanly. + # + # The invocations must stay SEPARATE. Passing "libs netlib shared" as + # three goals to one make lets them run concurrently, which races: + # "ar: libopenblas-r0.3.34.a: error reading zlatm5.o: file truncated" + # on linux-alderlake/gcc. That is the race this method's docstring is + # about, and the reason it overrides 'make all' in the first place. + # + # TEMPORARY: carried on this branch pending an upstream fix; drop it + # once spack/spack-packages takes the change. + for target in ("libs", "netlib", "shared"): + make("-s", target, *self.make_defs) def edit(self, pkg, spec, prefix): # https://github.com/spack/spack-packages/pull/5883#issuecomment-5189054355 diff --git a/repos/spack_repo/builtin/packages/petsc/package.py b/repos/spack_repo/builtin/packages/petsc/package.py index 3645fa1a469..254d7a78388 100644 --- a/repos/spack_repo/builtin/packages/petsc/package.py +++ b/repos/spack_repo/builtin/packages/petsc/package.py @@ -136,6 +136,7 @@ class Petsc(Package, CudaPackage, ROCmPackage): when="+fortran", description="Activates support for superlu-dist (only parallel)", ) + variant("superlu", default=False, description="Activates support for superlu (sequential)") variant("strumpack", default=False, description="Activates support for Strumpack") variant( "scalapack", default=False, when="+fortran", description="Activates support for Scalapack" @@ -366,6 +367,11 @@ class Petsc(Package, CudaPackage, ROCmPackage): depends_on("mkl", when="+mkl-pardiso") depends_on("fftw+mpi", when="+fftw+mpi") depends_on("suite-sparse", when="+suite-sparse") + depends_on("superlu", when="+superlu") + conflicts( + "+superlu+int64", + msg="SuperLU has no support for 64-bit integers, use superlu-dist instead", + ) depends_on("libx11", when="+X") depends_on("mpfr", when="+mpfr") depends_on("gmp", when="+mpfr") @@ -549,6 +555,7 @@ def configure_options(self): ("kokkos", "kokkos", False, False), ("kokkos-kernels", "kokkos-kernels", False, False), ("superlu-dist", "superlu_dist", True, True), + "superlu", ("scotch", "ptscotch", True, True), ( "suite-sparse:umfpack,klu,cholmod,btf,ccolamd,colamd,camd,amd," diff --git a/repos/spack_repo/builtin/packages/pumi/package.py b/repos/spack_repo/builtin/packages/pumi/package.py index aa630a18aeb..a8ddf81af89 100644 --- a/repos/spack_repo/builtin/packages/pumi/package.py +++ b/repos/spack_repo/builtin/packages/pumi/package.py @@ -33,6 +33,12 @@ class Pumi(CMakePackage): # scorec/core develop branch and we prefer not to expose spack users # to the added instability. version("master", submodules=True, branch="master") + version( + "4.2.1", submodules=True, commit="a40922de30f09af63a0251a0d53f95c6cadd9199" + ) # tag 4.2.1 (same commit as 4.2.0 -- retag only, includes the apfMDS.cc int/long fix) + version( + "4.1.0", submodules=True, commit="a8e3aef58bfe86790782c4ae5e5c1bb5f232ff30" + ) # tag 4.1.0 version( "2.2.9", submodules=True, commit="f87525cae7597322edfb2ccf1c7d4437402d9481" ) # tag 2.2.9 diff --git a/repos/spack_repo/builtin/packages/py_proteus/package.py b/repos/spack_repo/builtin/packages/py_proteus/package.py new file mode 100644 index 00000000000..5ce950bd118 --- /dev/null +++ b/repos/spack_repo/builtin/packages/py_proteus/package.py @@ -0,0 +1,99 @@ +# Copyright Spack Project Developers. See COPYRIGHT file for details. +# +# SPDX-License-Identifier: (Apache-2.0 OR MIT) + +import glob +import os + +from spack_repo.builtin.build_systems.python import PythonPackage + +from spack.package import * + + +class PyProteus(PythonPackage): + """Proteus: Computational Methods and Simulation Toolkit. Python tools + for rapidly developing computer models and numerical methods""" + + homepage = "http://proteustoolkit.org" + git = "https://github.com/cekees/proteus.git" + + maintainers("cekees") + + license("MIT") + + version("main", branch="main") + + variant("pumi", default=True, description="Enable PUMI mesh adaptation support") + variant("chrono", default=True, description="Enable Chrono modeling support") + + depends_on("c", type="build") + depends_on("cxx", type="build") + depends_on("fortran", type="build") # remove in future not a direct proteus dep + depends_on("python@3.9:", type=("build", "run")) + depends_on("py-setuptools@61:", type="build") + depends_on("py-cython@3:", type="build") + depends_on("py-pybind11@2.11:2", type="build") # xtensor@0.27.1 *= overload issue + depends_on("py-numpy@1.25:", type=("build", "run")) + depends_on("py-scipy", type=("build", "run")) + depends_on("py-mpi4py", type=("build", "run")) + depends_on("py-petsc4py", type=("build", "run")) + depends_on("py-h5py+mpi", type=("build", "run")) + depends_on("mpi") + depends_on("petsc+mpi+hypre+superlu-dist+superlu") + depends_on("hdf5+mpi+hl") + depends_on("openblas") # should relax to generic blas in future + depends_on("metis") # through several dependencies + depends_on("superlu") + depends_on("triangle") # shells out but has linked in past + depends_on("tetgen") # shells out + depends_on("gmsh~fltk~med") # shells out to the gmsh, no fltk/med needed + depends_on("ncurses") # Fenton waves as text gui, generally not used + # run type matters: chrono exports PYTHONPATH for share/chrono/python + # (pychrono is not installed into site-packages) via + # setup_dependent_run_environment, which spack only applies to run deps. + depends_on("chrono+python", when="+chrono", type=("build", "link", "run")) + depends_on("pumi@4.2.1:+zoltan+shared", when="+pumi") # <4.2.1 requires patch + depends_on("zoltan+parmetis~fortran", when="+pumi") + depends_on("parmetis") + depends_on("eigen@3.4") # xtensor dep + depends_on("xtensor@0.26.0") + depends_on("xtensor-python@0.28.0:") + depends_on("xtl") + + def _mpi_dir(self): + # try to find the right mpi.h as it's not always in self.spec["mpi"] + mpi_prefix = self.spec["mpi"].prefix + if os.path.isfile(os.path.join(mpi_prefix, "include", "mpi.h")): + return mpi_prefix + for candidate in glob.glob(os.path.join(mpi_prefix, "lib", "*", "mpich")): + if os.path.isfile(os.path.join(candidate, "include", "mpi.h")): + return candidate + for candidate in glob.glob(os.path.join(mpi_prefix, "lib", "*", "openmpi")): + if os.path.isfile(os.path.join(candidate, "include", "mpi.h")): + return candidate + return mpi_prefix + + def setup_build_environment(self, env): + if self.spec.satisfies("~pumi"): + env.set("PROTEUS_SKIP_PUMI", "1") + if self.spec.satisfies("~chrono"): + env.set("PROTEUS_SKIP_CHRONO", "1") + env.set("PETSC_DIR", self.spec["petsc"].prefix) + env.set("MPI_DIR", self._mpi_dir()) + env.set("HDF5_DIR", self.spec["hdf5"].prefix) + env.set("BLAS_DIR", self.spec["openblas"].prefix) + env.set("LAPACK_DIR", self.spec["openblas"].prefix) + env.set("SUPERLU_DIR", self.spec["superlu"].prefix) + env.set("NCURSES_DIR", self.spec["ncurses"].prefix) + env.set("METIS_DIR", self.spec["metis"].prefix) + + if self.spec.satisfies("+pumi"): + env.set("SCOREC_DIR", self.spec["pumi"].prefix) + env.set("ZOLTAN_DIR", self.spec["zoltan"].prefix) + env.set("PARMETIS_DIR", self.spec["parmetis"].prefix) + + if self.spec.satisfies("+chrono"): + env.set("CHRONO_DIR", self.spec["chrono"].prefix) + + for dep in ("eigen", "xtensor", "xtensor-python", "xtl"): + env.prepend_path("CPATH", self.spec[dep].prefix.include) diff --git a/repos/spack_repo/builtin/packages/triangle/package.py b/repos/spack_repo/builtin/packages/triangle/package.py index d4a9bb43f06..261d4899bcc 100644 --- a/repos/spack_repo/builtin/packages/triangle/package.py +++ b/repos/spack_repo/builtin/packages/triangle/package.py @@ -23,11 +23,17 @@ class Triangle(Package): version("1.6", sha256="1766327add038495fa3499e9b7cc642179229750f7201b94f8e1b7bee76f8480") - depends_on("libx11", type="link") + depends_on("libx11", type=("build", "link")) + depends_on("xproto", type="build") depends_on("gmake", type="build") def install(self, spec, prefix): - make() + x11 = spec["libx11"].prefix + xproto = spec["xproto"].prefix + cswitches = "-O -std=gnu17 -I{0} -I{1} -L{2}".format(x11.include, xproto.include, x11.lib) + if not spec.satisfies("platform=darwin"): + cswitches = "-DLINUX " + cswitches + make("CSWITCHES=" + cswitches) mkdirp(prefix.bin) install("triangle", prefix.bin) diff --git a/repos/spack_repo/builtin/packages/zoltan/package.py b/repos/spack_repo/builtin/packages/zoltan/package.py index 05fd4eeda55..6fda8e5511d 100644 --- a/repos/spack_repo/builtin/packages/zoltan/package.py +++ b/repos/spack_repo/builtin/packages/zoltan/package.py @@ -2,6 +2,7 @@ # # SPDX-License-Identifier: (Apache-2.0 OR MIT) +import os import re from spack_repo.builtin.build_systems.autotools import AutotoolsPackage @@ -119,6 +120,11 @@ def configure_args(self): # Although adding to config_libs _should_ suffice, it does not # Add to ldflags as well config_ldflags.append("-lgfortran") + gfortran_lib = Executable(self.compiler.fc)( + "-print-file-name=libgfortran." + dso_suffix, output=str + ).strip() + if gfortran_lib and gfortran_lib != "libgfortran." + dso_suffix: + config_ldflags.append("-L" + os.path.dirname(gfortran_lib)) if spec.satisfies("%intel") or spec.satisfies("%oneapi"): config_libs.append("-lifcore") @@ -211,3 +217,10 @@ def solib_install(self): for lib_path in find(self.spec.prefix.lib, "lib*.a"): lib_shared_name = re.sub(r"\.a$", f".{dso_suffix}", lib_path) move(lib_path, lib_shared_name) + if dso_suffix == "dylib": + install_name_tool = which("install_name_tool", required=True) + install_name_tool( + "-id", + "@rpath/" + os.path.basename(lib_shared_name), + lib_shared_name, + )