aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xbinary/all_in_one12
-rw-r--r--install.bat2
-rw-r--r--spec/util/test_env.lua4
-rw-r--r--src/luarocks/core/cfg.lua70
-rw-r--r--src/luarocks/core/sysdetect.lua407
5 files changed, 447 insertions, 48 deletions
diff --git a/binary/all_in_one b/binary/all_in_one
index 01758758..43a18a3b 100755
--- a/binary/all_in_one
+++ b/binary/all_in_one
@@ -50,6 +50,7 @@ local path = require("luarocks.path")
50local manif = require("luarocks.manif") 50local manif = require("luarocks.manif")
51local queries = require("luarocks.queries") 51local queries = require("luarocks.queries")
52local persist = require("luarocks.persist") 52local persist = require("luarocks.persist")
53local sysdetect = require("luarocks.core.sysdetect")
53 54
54-------------------------------------------------------------------------------- 55--------------------------------------------------------------------------------
55 56
@@ -144,16 +145,7 @@ local function write_hardcoded_module(dir)
144 local processor = if_platform("windows", "x86") 145 local processor = if_platform("windows", "x86")
145 146
146 if if_platform("unix", true) then 147 if if_platform("unix", true) then
147 system = util.popen_read("uname -s") 148 system, processor = sysdetect.detect()
148 processor = util.popen_read("uname -m")
149
150 if processor:match("i[%d]86") then
151 processor = "x86"
152 elseif processor:match("amd64") or processor:match("x86_64") then
153 processor = "x86_64"
154 elseif processor:match("Power Macintosh") then
155 processor = "powerpc"
156 end
157 end 149 end
158 150
159 local hardcoded = { 151 local hardcoded = {
diff --git a/install.bat b/install.bat
index 444a6847..4120db49 100644
--- a/install.bat
+++ b/install.bat
@@ -1015,7 +1015,7 @@ local hardcoded_lua = S[[$LUADIR\luarocks\core\hardcoded.lua]]
1015 1015
1016os.remove(hardcoded_lua) 1016os.remove(hardcoded_lua)
1017 1017
1018vars.SYSTEM = USE_MINGW and "MINGW" or "WindowsNT" 1018vars.SYSTEM = USE_MINGW and "mingw" or "windows"
1019 1019
1020local f = io.open(hardcoded_lua, "w") 1020local f = io.open(hardcoded_lua, "w")
1021f:write(S[=[ 1021f:write(S[=[
diff --git a/spec/util/test_env.lua b/spec/util/test_env.lua
index f4cd3f60..6850214f 100644
--- a/spec/util/test_env.lua
+++ b/spec/util/test_env.lua
@@ -807,9 +807,9 @@ local function setup_luarocks()
807 807
808 if test_env.TEST_TARGET_OS == "windows" then 808 if test_env.TEST_TARGET_OS == "windows" then
809 if test_env.MINGW then 809 if test_env.MINGW then
810 table.insert(lines, [[SYSTEM = "MINGW",]]) 810 table.insert(lines, [[SYSTEM = "mingw",]])
811 else 811 else
812 table.insert(lines, [[SYSTEM = "WindowsNT",]]) 812 table.insert(lines, [[SYSTEM = "windows",]])
813 end 813 end
814 table.insert(lines, ("WIN_TOOLS = %q,"):format(testing_paths.win_tools)) 814 table.insert(lines, ("WIN_TOOLS = %q,"):format(testing_paths.win_tools))
815 end 815 end
diff --git a/src/luarocks/core/cfg.lua b/src/luarocks/core/cfg.lua
index 9be573ce..bbcc72ac 100644
--- a/src/luarocks/core/cfg.lua
+++ b/src/luarocks/core/cfg.lua
@@ -15,6 +15,7 @@ local next, table, pairs, require, os, pcall, ipairs, package, tonumber, type, a
15 15
16local util = require("luarocks.core.util") 16local util = require("luarocks.core.util")
17local persist = require("luarocks.core.persist") 17local persist = require("luarocks.core.persist")
18local sysdetect = require("luarocks.core.sysdetect")
18 19
19-------------------------------------------------------------------------------- 20--------------------------------------------------------------------------------
20 21
@@ -151,22 +152,23 @@ do
151 end 152 end
152end 153end
153 154
155local platform_sets = {
156 freebsd = { unix = true, bsd = true, freebsd = true },
157 openbsd = { unix = true, bsd = true, openbsd = true },
158 solaris = { unix = true, solaris = true },
159 windows = { windows = true, win32 = true },
160 cygwin = { unix = true, cygwin = true },
161 macosx = { unix = true, bsd = true, macosx = true, macos = true },
162 netbsd = { unix = true, bsd = true, netbsd = true },
163 haiku = { unix = true, haiku = true },
164 linux = { unix = true, linux = true },
165 mingw = { windows = true, win32 = true, mingw32 = true, mingw = true },
166 msys = { unix = true, cygwin = true, msys = true },
167}
168
154local function make_platforms(system) 169local function make_platforms(system)
155 if system then 170 -- fallback to Unix in unknown systems
156 if system == "Linux" then return { unix = true, linux = true } 171 return platform_sets[system] or { unix = true }
157 elseif system == "FreeBSD" then return { unix = true, bsd = true, freebsd = true }
158 elseif system == "OpenBSD" then return { unix = true, bsd = true, openbsd = true }
159 elseif system == "NetBSD" then return { unix = true, bsd = true, netbsd = true }
160 elseif system == "Darwin" then return { unix = true, bsd = true, macosx = true, macos = true }
161 elseif system == "SunOS" then return { unix = true, solaris = true }
162 elseif system == "Haiku" then return { unix = true, haiku = true }
163 elseif system:match("^CYGWIN") then return { unix = true, cygwin = true }
164 elseif system:match("^MSYS") then return { unix = true, cygwin = true, msys = true }
165 elseif system:match("^Windows") then return { windows = true, win32 = true }
166 elseif system:match("^MINGW") then return { windows = true, win32 = true, mingw32 = true, mingw = true }
167 end
168 end
169 return { unix = true } -- fallback to Unix in unknown systems
170end 172end
171 173
172-------------------------------------------------------------------------------- 174--------------------------------------------------------------------------------
@@ -593,29 +595,27 @@ function cfg.init(lua_data, project_dir, warning)
593 -- A proper build of LuaRocks will hardcode the system 595 -- A proper build of LuaRocks will hardcode the system
594 -- and proc values with hardcoded.SYSTEM and hardcoded.PROCESSOR. 596 -- and proc values with hardcoded.SYSTEM and hardcoded.PROCESSOR.
595 -- If that is not available, we try to identify the system. 597 -- If that is not available, we try to identify the system.
596 local system = hardcoded.SYSTEM 598 local system, processor = sysdetect.detect()
597 local processor = hardcoded.PROCESSOR 599 if hardcoded.SYSTEM then
598 if is_windows then 600 system = hardcoded.SYSTEM
599 if not system then 601 end
600 if os.getenv("VCINSTALLDIR") then 602 if hardcoded.PROCESSOR then
601 -- running from the Development Command prompt for VS 2017 603 processor = hardcoded.PROCESSOR
602 system = "Windows" 604 end
605
606 if system == "windows" then
607 if os.getenv("VCINSTALLDIR") then
608 -- running from the Development Command prompt for VS 2017
609 system = "windows"
610 else
611 local fd = io.open("/bin/sh", "r")
612 if fd then
613 fd:close()
614 system = "msys"
603 else 615 else
604 system = "MINGW" 616 system = "mingw"
605 end
606 end
607 if not processor then
608 local pe_parser = require("luarocks.fs.win32.pe-parser")
609 local err
610 local lua_exe = lua_bindir .. "\\" .. lua_interpreter
611 processor, err = pe_parser.get_architecture(lua_exe)
612 if err then
613 processor = "x86"
614 end 617 end
615 end 618 end
616 else
617 system = system or util.popen_read("uname -s")
618 processor = processor or util.popen_read("uname -m")
619 end 619 end
620 620
621 cfg.target_cpu = processor 621 cfg.target_cpu = processor
diff --git a/src/luarocks/core/sysdetect.lua b/src/luarocks/core/sysdetect.lua
new file mode 100644
index 00000000..bd5139b7
--- /dev/null
+++ b/src/luarocks/core/sysdetect.lua
@@ -0,0 +1,407 @@
1-- Detect the operating system and architecture without forking a subprocess.
2--
3-- We are not going for exhaustive list of every historical system here,
4-- but aiming to cover every platform where LuaRocks is known to run.
5-- If your system is not detected, patches are welcome!
6
7local sysdetect = {}
8
9local function hex(s)
10 return s:gsub("$(..)", function(x)
11 return string.char(tonumber(x, 16))
12 end)
13end
14
15local function read_int8(fd)
16 if io.type(fd) == "closed file" then
17 return nil
18 end
19 local s = fd:read(1)
20 if not s then
21 fd:close()
22 return nil
23 end
24 return s:byte()
25end
26
27local LITTLE = 1
28-- local BIG = 2
29
30local function bytes2number(s, endian)
31 local r = 0
32 if endian == LITTLE then
33 for i = #s, 1, -1 do
34 r = r*256 + s:byte(i,i)
35 end
36 else
37 for i = 1, #s do
38 r = r*256 + s:byte(i,i)
39 end
40 end
41 return r
42end
43
44local function read(fd, bytes, endian)
45 if io.type(fd) == "closed file" then
46 return nil
47 end
48 local s = fd:read(bytes)
49 if not s
50 then fd:close()
51 return nil
52 end
53 return bytes2number(s, endian)
54end
55
56local function read_int32le(fd)
57 return read(fd, 4, LITTLE)
58end
59
60--------------------------------------------------------------------------------
61-- @section ELF
62--------------------------------------------------------------------------------
63
64local e_osabi = {
65 [0x00] = "sysv",
66 [0x01] = "hpux",
67 [0x02] = "netbsd",
68 [0x03] = "linux",
69 [0x04] = "hurd",
70 [0x06] = "solaris",
71 [0x07] = "aix",
72 [0x08] = "irix",
73 [0x09] = "freebsd",
74 [0x0c] = "openbsd",
75}
76
77local e_machines = {
78 [0x02] = "sparc",
79 [0x03] = "x86",
80 [0x08] = "mips",
81 [0x0f] = "hppa",
82 [0x12] = "sparcv8",
83 [0x14] = "ppc",
84 [0x15] = "ppc64",
85 [0x16] = "s390",
86 [0x28] = "arm",
87 [0x2a] = "superh",
88 [0x2b] = "sparcv9",
89 [0x32] = "ia_64",
90 [0x3E] = "x86_64",
91 [0xB6] = "alpha",
92 [0xB7] = "aarch64",
93 [0xF3] = "riscv64",
94 [0x9026] = "alpha",
95}
96
97local SHT_NOTE = 7
98
99local function read_elf_section_headers(fd, hdr)
100 local endian = hdr.endian
101 local word = hdr.word
102
103 local strtab_offset
104 local sections = {}
105 for i = 0, hdr.e_shnum - 1 do
106 fd:seek("set", hdr.e_shoff + (i * hdr.e_shentsize))
107 local section = {}
108 section.sh_name_off = read(fd, 4, endian)
109 section.sh_type = read(fd, 4, endian)
110 section.sh_flags = read(fd, word, endian)
111 section.sh_addr = read(fd, word, endian)
112 section.sh_offset = read(fd, word, endian)
113 section.sh_size = read(fd, word, endian)
114 section.sh_link = read(fd, 4, endian)
115 section.sh_info = read(fd, 4, endian)
116 if section.sh_type == SHT_NOTE then
117 fd:seek("set", section.sh_offset)
118 section.namesz = read(fd, 4, endian)
119 section.descsz = read(fd, 4, endian)
120 section.type = read(fd, 4, endian)
121 section.namedata = fd:read(section.namesz):gsub("%z.*", "")
122 section.descdata = fd:read(section.descsz)
123 elseif i == hdr.e_shstrndx then
124 strtab_offset = section.sh_offset
125 end
126 table.insert(sections, section)
127 end
128 if strtab_offset then
129 for _, section in ipairs(sections) do
130 fd:seek("set", strtab_offset + section.sh_name_off)
131 section.name = fd:read(32):gsub("%z.*", "")
132 sections[section.name] = section
133 end
134 end
135 return sections
136end
137
138local function detect_elf_system(fd, hdr, sections)
139 local system = e_osabi[hdr.osabi]
140 local endian = hdr.endian
141
142 if system == "sysv" then
143 local abitag = sections[".note.ABI-tag"]
144 if abitag then
145 if abitag.namedata == "GNU" and abitag.type == 1
146 and abitag.descdata:sub(0, 4) == "\0\0\0\0" then
147 return "linux"
148 end
149 elseif sections[".SUNW_version"]
150 or sections[".SUNW_signature"] then
151 return "solaris"
152 elseif sections[".note.netbsd.ident"] then
153 return "netbsd"
154 elseif sections[".note.openbsd.ident"] then
155 return "openbsd"
156 end
157
158 local gnu_version_r = sections[".gnu.version_r"]
159 if gnu_version_r then
160
161 local dynstr = sections[".dynstr"].sh_offset
162
163 local idx = 0
164 for _ = 0, gnu_version_r.sh_info - 1 do
165 fd:seek("set", gnu_version_r.sh_offset + idx)
166 assert(read(fd, 2, endian)) -- vn_version
167 local vn_cnt = read(fd, 2, endian)
168 local vn_file = read(fd, 4, endian)
169 local vn_next = read(fd, 2, endian)
170
171 fd:seek("set", dynstr + vn_file)
172 local libname = fd:read(64):gsub("%z.*", "")
173
174 if hdr.e_type == 0x03 and libname == "libroot.so" then
175 return "haiku"
176 elseif libname:match("linux") then
177 return "linux"
178 end
179
180 idx = idx + (vn_next * (vn_cnt + 1))
181 end
182 end
183 end
184
185 return system
186end
187
188local function read_elf_header(fd)
189 local hdr = {}
190
191 hdr.bits = read_int8(fd)
192 hdr.endian = read_int8(fd)
193 hdr.elf_version = read_int8(fd)
194 if hdr.elf_version ~= 1 then
195 return nil
196 end
197 hdr.osabi = read_int8(fd)
198 if not hdr.osabi then
199 return nil
200 end
201
202 local endian = hdr.endian
203 fd:seek("set", 0x10)
204 hdr.e_type = read(fd, 2, endian)
205 local machine = read(fd, 2, endian)
206 local processor = e_machines[machine] or "unknown"
207 if endian == 1 and processor == "ppc64" then
208 processor = "ppc64le"
209 end
210
211 local elfversion = read(fd, 4, endian)
212 if elfversion ~= 1 then
213 return nil
214 end
215
216 local word = (hdr.bits == 1) and 4 or 8
217 hdr.word = word
218
219 hdr.e_entry = read(fd, word, endian)
220 hdr.e_phoff = read(fd, word, endian)
221 hdr.e_shoff = read(fd, word, endian)
222 hdr.e_flags = read(fd, 4, endian)
223 hdr.e_ehsize = read(fd, 2, endian)
224 hdr.e_phentsize = read(fd, 2, endian)
225 hdr.e_phnum = read(fd, 2, endian)
226 hdr.e_shentsize = read(fd, 2, endian)
227 hdr.e_shnum = read(fd, 2, endian)
228 hdr.e_shstrndx = read(fd, 2, endian)
229
230 return hdr, processor
231end
232
233local function detect_elf(fd)
234 local hdr, processor = read_elf_header(fd)
235 if not hdr then
236 return nil
237 end
238 local sections = read_elf_section_headers(fd, hdr)
239 local system = detect_elf_system(fd, hdr, sections)
240 return system, processor
241end
242
243--------------------------------------------------------------------------------
244-- @section Mach Objects (Apple)
245--------------------------------------------------------------------------------
246
247local mach_l64 = {
248 [7] = "x86_64",
249 [12] = "aarch64",
250}
251
252local mach_b64 = {
253 [0] = "ppc64",
254}
255
256local mach_l32 = {
257 [7] = "x86",
258 [12] = "arm",
259}
260
261local mach_b32 = {
262 [0] = "ppc",
263}
264
265local function detect_mach(magic, fd)
266 if not magic then
267 return nil
268 end
269
270 if magic == hex("$CA$FE$BA$BE") then
271 -- fat binary, go for the first one
272 fd:seek("set", 0x12)
273 local offs = read_int8(fd)
274 if not offs then
275 return nil
276 end
277 fd:seek("set", offs * 256)
278 magic = fd:read(4)
279 return detect_mach(magic, fd)
280 end
281
282 local cputype = read_int8(fd)
283
284 if magic == hex("$CF$FA$ED$FE") then
285 return "macosx", mach_l64[cputype] or "unknown"
286 elseif magic == hex("$FE$ED$CF$FA") then
287 return "macosx", mach_b64[cputype] or "unknown"
288 elseif magic == hex("$CE$FA$ED$FE") then
289 return "macosx", mach_l32[cputype] or "unknown"
290 elseif magic == hex("$FE$ED$FA$CE") then
291 return "macosx", mach_b32[cputype] or "unknown"
292 end
293end
294
295--------------------------------------------------------------------------------
296-- @section PE (Windows)
297--------------------------------------------------------------------------------
298
299local pe_machine = {
300 [0x8664] = "x86_64",
301 [0x01c0] = "arm",
302 [0x01c4] = "armv7l",
303 [0xaa64] = "arm64",
304 [0x014c] = "x86",
305}
306
307local function detect_pe(fd)
308 fd:seek("set", 60) -- position of PE header position
309 local peoffset = read_int32le(fd) -- read position of PE header
310 if not peoffset then
311 return nil
312 end
313 local system = "windows"
314 fd:seek("set", peoffset + 4) -- move to position of Machine section
315 local machine = read(fd, 2, LITTLE)
316 local processor = pe_machine[machine]
317
318 local rdata_pos = fd:read(736):match(".rdata%z%z............(....)")
319 if rdata_pos then
320 rdata_pos = bytes2number(rdata_pos, LITTLE)
321 fd:seek("set", rdata_pos)
322 local data = fd:read(512)
323 if data:match("cyggcc") then
324 system = "cygwin"
325 end
326 end
327
328 return system, processor or "unknown"
329end
330
331--------------------------------------------------------------------------------
332-- @section API
333--------------------------------------------------------------------------------
334
335function sysdetect.detect_file(file)
336 assert(type(file) == "string")
337 local fd = io.open(file, "rb")
338 if not fd then
339 return nil
340 end
341 local magic = fd:read(4)
342 if magic == hex("$7FELF") then
343 return detect_elf(fd)
344 end
345 if magic == hex("MZ$90$00") then
346 return detect_pe(fd)
347 end
348 return detect_mach(magic, fd)
349end
350
351local cache_system
352local cache_processor
353
354function sysdetect.detect(input_file)
355 local dirsep = package.config:sub(1,1)
356 local files
357
358 if input_file then
359 files = { input_file }
360 else
361 if cache_system then
362 return cache_system, cache_processor
363 end
364
365 local PATHsep
366 local interp = arg and arg[-1]
367 if dirsep == "/" then
368 -- Unix
369 files = {
370 "/bin/sh", -- Unix: well-known POSIX path
371 "/proc/self/exe", -- Linux: this should always have a working binary
372 }
373 PATHsep = ":"
374 else
375 -- Windows
376 local systemroot = os.getenv("SystemRoot")
377 files = {
378 systemroot .. "\\system32\\notepad.exe", -- well-known Windows path
379 systemroot .. "\\explorer.exe", -- well-known Windows path
380 }
381 if interp and not interp:lower():match("exe$") then
382 interp = interp .. ".exe"
383 end
384 PATHsep = ";"
385 end
386 if interp then
387 if interp:match(dirsep) then
388 -- interpreter path is absolute
389 table.insert(files, interp)
390 else
391 for d in os.getenv("PATH"):gmatch("[^"..PATHsep.."]+") do
392 table.insert(files, d .. dirsep .. interp)
393 end
394 end
395 end
396 end
397 for _, f in ipairs(files) do
398 local system, processor = sysdetect.detect_file(f)
399 if system then
400 cache_system = system
401 cache_processor = processor
402 return system, processor
403 end
404 end
405end
406
407return sysdetect