-
Notifications
You must be signed in to change notification settings - Fork 105
Expand file tree
/
Copy pathscanner.lua
More file actions
211 lines (190 loc) · 5.57 KB
/
scanner.lua
File metadata and controls
211 lines (190 loc) · 5.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
local scanner = {}
-- Configuration
scanner.HOME = os.getenv("HOME") or os.getenv("USERPROFILE")
scanner.KITS_FILE = scanner.HOME .. "/.config/cmake-tools/cmake-kits.json"
--Helper functions
-- Simple JSON encoder
function scanner.json_encode(obj, indent)
indent = indent or 0
local spaces = string.rep(" ", indent)
if type(obj) == "table" then
local is_array = #obj > 0
local result = "{\n"
local first = true
for k, v in pairs(obj) do
if not first then
result = result .. ",\n"
end
first = false
if is_array then
result = result .. spaces .. " " .. scanner.json_encode(v, indent + 1)
else
result = result .. spaces .. ' "' .. k .. '": ' .. scanner.json_encode(v, indent + 1)
end
end
return result .. "\n" .. spaces .. "}"
elseif type(obj) == "string" then
return '"' .. obj:gsub("\\", "\\\\"):gsub('"', '\\"') .. '"'
elseif type(obj) == "number" or type(obj) == "boolean" then
return tostring(obj)
elseif obj == nil then
return "null"
end
end
function scanner.execute_command(cmd)
local handle = io.popen(cmd .. " 2>&1")
if handle == nil then
return false, -1, ""
end
local result = handle:read("*a")
if result == nil then
result = ""
end
local success, exit_type, exit_code = handle:close()
-- io.popen's close() returns: true on success, or nil, "exit", code on failure
if success == nil then
return false, exit_code or -1, result
end
return true, 0, result
end
function scanner.file_exists(path)
local file = io.open(path, "r")
if file then
file:close()
return true
else
return false
end
end
function scanner.split_path(path_env)
local paths = {}
local sep = package.config:sub(1, 1) == "\\" and ";" or ":"
for path in string.gmatch(path_env, "([^" .. sep .. "]+)") do
table.insert(paths, path)
end
return paths
end
function scanner.get_gcc_version(gcc_path)
local success, exit_code, output = scanner.execute_command('"' .. gcc_path .. '" --version')
if output == nil then
return nil
end
-- Try multiple patterns to match different gcc output formats
local version = output:match("gcc%s+%(GCC%)%s+([%d%.]+)") -- "gcc (GCC) 15.2.1"
or output:match("gcc version ([%d%.]+)") -- "gcc version 11.4.0"
return version
end
function scanner.get_clang_version(clang_path)
local success, exit_code, output = scanner.execute_command('"' .. clang_path .. '" --version ')
if output == nil then
return nil
end
local version_line = output:match("clang version ([%d%.]+)")
return version_line
end
function scanner.find_compiler_pair(dir, c_compiler)
local base_name = c_compiler:match("([^/\\]+)$")
local cxx_name
if base_name:match("gcc") then
cxx_name = base_name:gsub("gcc", "g++")
elseif base_name:match("clang") then
cxx_name = base_name:gsub("clang", "clang++")
else
return nil
end
local cxx_path = dir .. "/" .. cxx_name
if scanner.file_exists(cxx_path) then
return cxx_path
end
return nil
end
function scanner.find_linker_pair(dir, linker_name)
if not linker_name then
return nil
end
local linker_path = dir .. "/" .. linker_name
if scanner.file_exists(linker_path) then
return linker_path
end
return nil
end
function scanner.get_toolchain_file()
local toolchainFile = os.getenv("CMAKE_TOOLCHAIN_FILE")
if toolchainFile and scanner.file_exists(toolchainFile) then
return toolchainFile
end
return nil
end
function scanner.ensure_directory(path)
local pattern = "(.*/)"
local dir = path:match(pattern)
if dir then
os.execute('mkdir -p "' .. dir .. '"')
end
end
function scanner.save_kits(kits, filepath)
scanner.ensure_directory(filepath)
local file = io.open(filepath, "w")
if not file then
error("Failed to open file for writing: " .. filepath)
return false
end
local json_content = scanner.json_encode(kits)
file:write(json_content)
file:close()
return true
end
-- Main fucntion to scan for kits
function scanner.scan_for_kits()
local kits = {}
local path_env = os.getenv("PATH") or ""
local paths = scanner.split_path(path_env)
for _, dir in ipairs(paths) do
local linker_path = scanner.find_linker_pair(dir, "lld")
if linker_path == nil then
linker_path = scanner.find_linker_pair(dir, "ld")
end
local toolchainFile = scanner.get_toolchain_file()
local gcc_path = dir .. "/gcc"
if scanner.file_exists(gcc_path) then
local gcc_version = scanner.get_gcc_version(gcc_path)
local gxx_path = scanner.find_compiler_pair(dir, gcc_path)
if gxx_path then
local kit = {
name = "GCC-" .. (gcc_version or "unknown"),
compilers = {
C = gcc_path,
CXX = gxx_path,
},
linker = (linker_path or ""),
toolchainFile = (toolchainFile or ""),
}
table.insert(kits, kit)
end
end
local clang_path = dir .. "/clang"
if scanner.file_exists(clang_path) then
local clang_version = scanner.get_clang_version(clang_path)
local clangxx_path = scanner.find_compiler_pair(dir, clang_path)
if clangxx_path then
local kit = {
name = "Clang-" .. (clang_version or "unknown"),
compilers = {
C = clang_path,
CXX = clangxx_path,
},
linker = (linker_path or ""),
toolchainFile = (toolchainFile or ""),
}
table.insert(kits, kit)
end
end
end
if #kits == 0 then
print("No compilers found in PATH.")
return {}
end
scanner.save_kits(kits, scanner.KITS_FILE)
return kits
end
return scanner