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 }