diff --git a/internal/pkg/mpich/mpich.go b/internal/pkg/mpich/mpich.go index ac3ce6d..147154f 100644 --- a/internal/pkg/mpich/mpich.go +++ b/internal/pkg/mpich/mpich.go @@ -5,7 +5,14 @@ package mpich -import "fmt" +import ( + "fmt" + "path/filepath" + "strings" + + "github.com/gvallee/go_exec/pkg/advexec" + "github.com/gvallee/go_util/pkg/util" +) const ( // VersionTag is the tag used to refer to the MPI version in MPICH template(s) @@ -30,7 +37,48 @@ func GetConfigureExtraArgs() []string { return extraArgs } +func parseMPICHInfoOutputForVersion(output string) (string, error) { + targetLineIdx := 1 + lines := strings.Split(output, "\n") + if !strings.Contains(lines[targetLineIdx], "Version:") { + return "", fmt.Errorf("invalid output format") + } + tokens := strings.Split(lines[targetLineIdx], "Version:") + if len(tokens) != 2 { + return "", fmt.Errorf("invalid format: %s", lines[targetLineIdx]) + } + version := strings.TrimPrefix(tokens[1], "Version:") + version = strings.ReplaceAll(version, " ", "") + version = strings.ReplaceAll(version, "\t", "") + version = strings.TrimRight(version, "\n") + return version, nil +} + // DetectFromDir tries to figure out which version of MPICH is installed in a given directory func DetectFromDir(dir string, env []string) (string, string, error) { - return "", "", fmt.Errorf("not implemented") + targetBin := filepath.Join(dir, "bin", "mpirun") + if !util.FileExists(targetBin) { + return "", "", fmt.Errorf("%s does not exist, not an MPICH implementation", targetBin) + } + + var versionCmd advexec.Advcmd + versionCmd.BinPath = targetBin + versionCmd.CmdArgs = append(versionCmd.CmdArgs, "--version") + versionCmd.Env = env + if env == nil { + newLDPath := filepath.Join(dir, "lib") + ":$LD_LIBRARY_PATH" + newPath := filepath.Join(dir, "bin") + ":$PATH" + versionCmd.Env = append(versionCmd.Env, "LD_LIBRARY_PATH="+newLDPath) + versionCmd.Env = append(versionCmd.Env, "PATH="+newPath) + } + res := versionCmd.Run() + if res.Err != nil { + return "", "", fmt.Errorf("unable to execute %s --version: %w", targetBin, res.Err) + } + version, err := parseMPICHInfoOutputForVersion(res.Stdout) + if err != nil { + return "", "", fmt.Errorf("parseOmpiInfoOutputForVersion() failed - %w", err) + } + + return ID, version, nil } diff --git a/internal/pkg/mpich/mpich_test.go b/internal/pkg/mpich/mpich_test.go new file mode 100644 index 0000000..5bc87e8 --- /dev/null +++ b/internal/pkg/mpich/mpich_test.go @@ -0,0 +1,55 @@ +// Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. +// This software is licensed under a 3-clause BSD license. Please consult the +// LICENSE.md file distributed with the sources of this project regarding your +// rights to use or distribute this software. + +package mpich + +import "testing" + +func TestParseMPICHInfoOutputForVersion(t *testing.T) { + tests := []struct { + name string + input string + expectedOutput string + }{ + { + name: "v3.4.2", + input: `HYDRA build details: + Version: 3.4.2 + Release Date: Wed May 26 15:51:40 CDT 2021 + CC: gcc + Configure options: '--disable-option-checking' '--prefix=/home/gvallee/install/mpich-3.4.2' 'FCFLAGS=-fallow-argument-mismatch -O2' 'FFLAGS=-fallow-argument-mismatch -O2' '--with-ucx=/home/gvallee/install/ucx-1.9.0' '--cache-file=/dev/null' '--srcdir=.' 'CC=gcc' 'CFLAGS= -O2' 'LDFLAGS= -L/home/gvallee/install/ucx-1.9.0/lib' 'LIBS=' 'CPPFLAGS= -I/home/gvallee/install/ucx-1.9.0/include -DNETMOD_INLINE=__netmod_inline_ucx__ -I/home/gvallee/src/mpich-3.4.2/src/mpl/include -I/home/gvallee/src/mpich-3.4.2/src/mpl/include -I/home/gvallee/src/mpich-3.4.2/modules/yaksa/src/frontend/include -I/home/gvallee/src/mpich-3.4.2/modules/yaksa/src/frontend/include -I/home/gvallee/src/mpich-3.4.2/modules/json-c -I/home/gvallee/src/mpich-3.4.2/modules/json-c -D_REENTRANT -I/home/gvallee/src/mpich-3.4.2/src/mpi/romio/include' 'MPLLIBNAME=mpl' + Process Manager: pmi + Launchers available: ssh rsh fork slurm ll lsf sge manual persist + Topology libraries available: hwloc + Resource management kernels available: user slurm ll lsf sge pbs cobalt + Demux engines available: poll select`, + expectedOutput: "3.4.2", + }, + { + name: "4.0b1", + input: `HYDRA build details: + Version: 4.0b1 + Release Date: Mon Nov 15 10:22:52 CST 2021 + CC: gcc + Configure options: '--disable-option-checking' '--prefix=/home/gvallee/install/mpich-4.0b1' 'FCFLAGS=-fallow-argument-mismatch -O2' 'FFLAGS=-fallow-argument-mismatch -O2' '--cache-file=/dev/null' '--srcdir=.' 'CC=gcc' 'CFLAGS= -O2' 'LDFLAGS=' 'LIBS=' 'CPPFLAGS=-DNETMOD_INLINE=__netmod_inline_ofi__ -D__HIP_PLATFORM_AMD__ -I/home/gvallee/src/mpich-4.0b1/src/mpl/include -I/home/gvallee/src/mpich-4.0b1/modules/json-c -I/home/gvallee/src/mpich-4.0b1/modules/hwloc/include -D_REENTRANT -I/home/gvallee/src/mpich-4.0b1/src/mpi/romio/include -I/home/gvallee/src/mpich-4.0b1/modules/yaksa/src/frontend/include -I/home/gvallee/src/mpich-4.0b1/modules/libfabric/include' + Process Manager: pmi + Launchers available: ssh rsh fork slurm ll lsf sge manual persist + Topology libraries available: hwloc + Resource management kernels available: user slurm ll lsf sge pbs cobalt + Demux engines available: poll select`, + expectedOutput: "4.0b1", + }, + } + + for _, tt := range tests { + version, err := parseMPICHInfoOutputForVersion(tt.input) + if err != nil { + t.Fatalf("parseOmpiInfoOutputForVersion() failed: %s", err) + } + if version != tt.expectedOutput { + t.Fatalf("parseOmpiInfoOutputForVersion() returned %s instead of %s", version, tt.expectedOutput) + } + } +}