github.com/ActiveState/cli@v0.0.0-20240508170324-6801f60cd051/pkg/sysinfo/sysinfo.go (about)

     1  package sysinfo
     2  
     3  import (
     4  	"fmt"
     5  	"os/exec"
     6  	"regexp"
     7  	"strconv"
     8  
     9  	"github.com/patrickmn/go-cache"
    10  )
    11  
    12  var sysinfoCache *cache.Cache = cache.New(cache.NoExpiration, cache.NoExpiration)
    13  
    14  const VersionOverrideEnvVar = "ACTIVESTATE_CLI_OSVERSION_OVERRIDE"
    15  
    16  // Cache keys used for storing/retrieving computed system information.
    17  const (
    18  	osVersionInfoCacheKey     = "osVersionInfo"
    19  	libcInfoCacheKey          = "libcInfo"
    20  	requestedLibcInfoCacheKey = "requestedLibcInfo"
    21  	compilersCacheKey         = "compilers"
    22  )
    23  
    24  var versionRegex = regexp.MustCompile(`^(\d+)\D(\d+)(?:\D(\d+))?`)
    25  
    26  // OsInfo represents an OS returned by OS().
    27  type OsInfo int
    28  
    29  const (
    30  	// Linux represents the Linux operating system.
    31  	Linux OsInfo = iota
    32  	// Windows represents the Windows operating system.
    33  	Windows
    34  	// Mac represents the Macintosh operating system.
    35  	Mac
    36  	// UnknownOs represents an unknown operating system.
    37  	UnknownOs
    38  )
    39  
    40  func (i OsInfo) String() string {
    41  	switch i {
    42  	case Linux:
    43  		return "Linux"
    44  	case Windows:
    45  		return "Windows"
    46  	case Mac:
    47  		return "MacOS"
    48  	default:
    49  		return "Unknown"
    50  	}
    51  }
    52  
    53  type VersionInfo struct {
    54  	Version string // raw version string
    55  	Major   int    // major version number
    56  	Minor   int    // minor version number
    57  	Micro   int    // micro version number
    58  }
    59  
    60  // OSVersionInfo represents an OS version returned by OSVersion().
    61  type OSVersionInfo struct {
    62  	*VersionInfo
    63  	Name string // free-form name string (varies by OS)
    64  }
    65  
    66  // ArchInfo represents an architecture returned by Architecture().
    67  type ArchInfo int
    68  
    69  const (
    70  	// I386 represents the Intel x86 (32-bit) architecture.
    71  	I386 ArchInfo = iota
    72  	// Amd64 represents the x86_64 (64-bit) architecture.
    73  	Amd64
    74  	// Arm represents the ARM architecture.
    75  	Arm
    76  	// UnknownArch represents an unknown architecture.
    77  	UnknownArch
    78  )
    79  
    80  func (i ArchInfo) String() string {
    81  	switch i {
    82  	case I386:
    83  		return "i386"
    84  	case Amd64:
    85  		return "x86_64"
    86  	case Arm:
    87  		return "ARM"
    88  	default:
    89  		return "Unknown"
    90  	}
    91  }
    92  
    93  // LibcNameInfo represents a C library name.
    94  type LibcNameInfo int
    95  
    96  const (
    97  	// Glibc represents the GNU C library.
    98  	Glibc LibcNameInfo = iota
    99  	// Msvcrt represents the Microsoft Visual C++ runtime library.
   100  	Msvcrt
   101  	// BsdLibc represents the BSD C library.
   102  	BsdLibc
   103  	// UnknownLibc represents an unknown C library.
   104  	UnknownLibc
   105  )
   106  
   107  func (i LibcNameInfo) String() string {
   108  	switch i {
   109  	case Glibc:
   110  		return "glibc"
   111  	case Msvcrt:
   112  		return "msvcrt"
   113  	case BsdLibc:
   114  		return "libc"
   115  	default:
   116  		return "Unknown"
   117  	}
   118  }
   119  
   120  // LibcInfo represents a LibC returned by Libc().
   121  type LibcInfo struct {
   122  	Name  LibcNameInfo // C library name
   123  	Major int          // major version number
   124  	Minor int          // minor version number
   125  }
   126  
   127  func (l LibcInfo) Version() string {
   128  	return fmt.Sprintf("%d.%d", l.Major, l.Minor)
   129  }
   130  
   131  // CompilerNameInfo reprents a compiler toolchain name.
   132  type CompilerNameInfo int
   133  
   134  const (
   135  	// Gcc represents the GNU C Compiler toolchain.
   136  	Gcc CompilerNameInfo = iota
   137  	// Msvc represents the Microsoft Visual C++ toolchain.
   138  	Msvc
   139  	// Mingw represents the Minimalist GNU for Windows toolchain.
   140  	Mingw
   141  	// Clang represents the LLVM/Clang toolchain.
   142  	Clang
   143  )
   144  
   145  func (i CompilerNameInfo) String() string {
   146  	switch i {
   147  	case Gcc:
   148  		return "GCC"
   149  	case Msvc:
   150  		return "MSVC"
   151  	case Mingw:
   152  		return "MinGW"
   153  	case Clang:
   154  		return "clang"
   155  	default:
   156  		return "Unknown"
   157  	}
   158  }
   159  
   160  // CompilerInfo represents a compiler toolchain returned by Compiler().
   161  type CompilerInfo struct {
   162  	Name  CompilerNameInfo // C compiler name
   163  	Major int              // major version number
   164  	Minor int              // minor version number
   165  }
   166  
   167  // Checks whether or not the given compiler exists and returns its major and
   168  // minor version numbers. A major return of 0 indicates the compiler does not
   169  // exist, or that an error occurred.
   170  func getCompilerVersion(args []string) (int, int, error) {
   171  	cc, err := exec.Command(args[0], args[1:]...).CombinedOutput()
   172  	if err != nil {
   173  		return 0, 0, nil
   174  	}
   175  	regex := regexp.MustCompile(`(\d+)\D(\d+)\D\d+`)
   176  	parts := regex.FindStringSubmatch(string(cc))
   177  	if len(parts) != 3 {
   178  		return 0, 0, fmt.Errorf("Unable to parse version string '%s'", cc)
   179  	}
   180  	for i := 1; i < len(parts); i++ {
   181  		if _, err := strconv.Atoi(parts[i]); err != nil {
   182  			return 0, 0, fmt.Errorf("Unable to parse part '%s' of version string '%s'", parts[i], cc)
   183  		}
   184  	}
   185  	major, _ := strconv.Atoi(parts[1])
   186  	minor, _ := strconv.Atoi(parts[2])
   187  	return major, minor, nil
   188  }
   189  
   190  func parseVersionInfo(v string) (*VersionInfo, error) {
   191  	parts := versionRegex.FindStringSubmatch(v)
   192  	if len(parts) == 0 {
   193  		return nil, fmt.Errorf("Unable to parse version string '%s'", v)
   194  	}
   195  
   196  	major, err := strconv.Atoi(parts[1])
   197  	if err != nil {
   198  		return nil, fmt.Errorf("Unable to parse part '%s' of version string '%s'", parts[1], v)
   199  	}
   200  
   201  	minor, err := strconv.Atoi(parts[2])
   202  	if err != nil {
   203  		return nil, fmt.Errorf("Unable to parse part '%s' of version string '%s'", parts[2], v)
   204  	}
   205  
   206  	var micro int = 0
   207  	if parts[3] != "" {
   208  		micro, err = strconv.Atoi(parts[3])
   209  		if err != nil {
   210  			return nil, fmt.Errorf("Unable to parse part '%s' of version string '%s'", parts[3], v)
   211  		}
   212  	}
   213  
   214  	return &VersionInfo{v, major, minor, micro}, nil
   215  }