From 54f90132a44fee37129d2b816be04a1dbedd82d1 Mon Sep 17 00:00:00 2001 From: actboy168 Date: Sun, 28 Apr 2024 17:44:58 +0800 Subject: [PATCH] cleanup --- scripts/env/find_msvc.lua | 278 ------------------------------------ scripts/env/msvc.lua | 292 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 278 insertions(+), 292 deletions(-) delete mode 100644 scripts/env/find_msvc.lua diff --git a/scripts/env/find_msvc.lua b/scripts/env/find_msvc.lua deleted file mode 100644 index 1fb89df..0000000 --- a/scripts/env/find_msvc.lua +++ /dev/null @@ -1,278 +0,0 @@ -local sp = require "bee.subprocess" -local fs = require "bee.filesystem" -local log = require "log" - -local function Is64BitWindows() - -- https://docs.microsoft.com/en-us/archive/blogs/david.wang/howto-detect-process-bitness - return os.getenv "PROCESSOR_ARCHITECTURE" == "AMD64" or os.getenv "PROCESSOR_ARCHITEW6432" == "AMD64" -end - -local ProgramFiles = Is64BitWindows() and "ProgramFiles(x86)" or "ProgramFiles" -local vswhere = os.getenv(ProgramFiles).."/Microsoft Visual Studio/Installer/vswhere.exe" -local need = { LIB = true, LIBPATH = true, PATH = true, INCLUDE = true } - -local function writeall(filename, content) - local f = assert(io.open(filename, "w")) - if content then - f:write(content) - end -end - -local function readall(filename) - local f = assert(io.open(filename, "r")) - return f:read "a" -end - -local function strtrim(str) - return str:gsub("^%s*(.-)%s*$", "%1") -end - -local InstallDir -local function installpath() - if InstallDir then - return InstallDir - end - local process = assert(sp.spawn { - vswhere, - "-nologo", - "-latest", - "-prerelease", - "-utf8", - "-products", "*", - "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", - "-property", "installationPath", - stdout = true, - stderr = "stdout", - }) - local result = strtrim(process.stdout:read "a") - process.stdout:close() - if process:wait() ~= 0 then - log.fastfail("[vswhere] %s", result) - end - if result == "" then - log.fastfail("[vswhere] VisualStudio not found. %s", result) - end - InstallDir = result - return InstallDir -end - -local function parse_env(str) - local pos = str:find("=") - if not pos then - return - end - return strtrim(str:sub(1, pos - 1)), strtrim(str:sub(pos + 1)) -end - -local function findwinsdk() - local function query(command) - local f = io.popen(command, "r") - if f then - for l in f:lines() do - local r = l:match "^ [^%s]+ [^%s]+ (.*)$" - if r then - f:close() - return r - end - end - end - end - local function find(dir) - local max - for file in fs.pairs(dir.."/include") do - if fs.exists(file / "um" / "winsdkver.h") then - local version = file:filename():string() - if version:sub(1, 3) == "10." then - if max then - if max < version then - max = version - end - else - max = version - end - end - end - end - return max - end - for _, v in ipairs { - [[HKLM\SOFTWARE\Wow6432Node]], - [[HKCU\SOFTWARE\Wow6432Node]], - [[HKLM\SOFTWARE]], - [[HKCU\SOFTWARE]] - } do - local WindowsSdkDir = query(([[reg query "%s\Microsoft\Microsoft SDKs\Windows\v10.0" /v "InstallationFolder"]]):format(v)) - if WindowsSdkDir then - local WindowSdkVersion = find(WindowsSdkDir) - if WindowSdkVersion then - return WindowSdkVersion - end - end - end -end - -local function vsdevcmd(winsdk, arch, f) - local vsvars32 = installpath().."/Common7/Tools/VsDevCmd.bat" - local args = { vsvars32 } - if arch then - args[#args+1] = ("-arch=%s"):format(arch) - end - if winsdk then - args[#args+1] = ("-winsdk=%s"):format(winsdk) - end - local process = assert(sp.spawn { - args, "&&", "set", - stderr = true, - stdout = true, - searchPath = true, - env = { - VSCMD_SKIP_SENDTELEMETRY = "1" - } - }) - for line in process.stdout:lines() do - local name, value = parse_env(line) - if name and value then - f(name, value) - end - end - local err = process.stderr:read "a" - process.stdout:close() - process.stderr:close() - if process:wait() ~= 0 then - log.fastfail("Call `VsDevCmd.bat` error:\n%s", err) - end -end - -local function environment(winsdk, arch) - local env = {} - vsdevcmd(winsdk, arch, function (name, value) - name = name:upper() - if need[name] then - env[name] = value - end - end) - return env -end - -local function prefix(env) - local testdir = os.tmpname() - fs.create_directories(testdir) - writeall(testdir.."/test.c", "#include ") - writeall(testdir.."/build.ninja", [[ -rule showIncludes - command = cl /nologo /showIncludes -c $in -build test: showIncludes test.c -]]) - local process = assert(sp.spawn { - "cmd", "/c", "ninja", - searchPath = true, - env = env, - cwd = testdir, - stdout = true, - stderr = "stdout", - }) - local data = process.stdout:read "a" - if process:wait() ~= 0 then - log.fastfail("ninja failed: %s", data) - end - fs.remove_all(testdir) - - for line in data:gmatch "[^\n\r]+" do - local m = line:match("[^:]+:[^:]+:") - if m then - return m - end - end - log.fastfail("parse msvc_deps_prefix failed:\n%s", data) -end - -local function toolspath() - local ToolsVersion = (function () - local verfile = installpath().."/VC/Auxiliary/Build/Microsoft.VCToolsVersion.default.txt" - local r = readall(verfile) - return strtrim(r) - end)() - return installpath().."/VC/Tools/MSVC/"..ToolsVersion -end - -local function binpath(arch) - local host = Is64BitWindows() and "Hostx64" or "Hostx86" - return toolspath().."/bin/"..host.."/"..arch -end - -local function vcrtpath(arch, optimize) - local RedistVersion = (function () - local verfile = installpath().."/VC/Auxiliary/Build/Microsoft.VCRedistVersion.default.txt" - local r = readall(verfile) - return strtrim(r) - end)() - local ToolsetVersion = (function () - local verfile = toolspath().."/include/yvals_core.h" - local r = readall(verfile) - return r:match "#define%s+_MSVC_STL_VERSION%s+(%d+)" - end)() - local path = installpath().."/VC/Redist/MSVC/"..RedistVersion - if optimize == "off" then - return path.."/debug_nonredist/"..arch.."/Microsoft.VC"..ToolsetVersion..".DebugCRT" - end - return path.."/"..arch.."/Microsoft.VC"..ToolsetVersion..".CRT" -end - -local function ucrtpath(arch, optimize) - local UniversalCRTSdkDir - vsdevcmd(findwinsdk(), arch, function (name, value) - if name == "UniversalCRTSdkDir" then - UniversalCRTSdkDir = value - end - end) - if not UniversalCRTSdkDir then - return - end - local path = UniversalCRTSdkDir.."/Redist" - local redist, ver - local function accept(p, version) - local ucrt = p.."/ucrt/DLLs/"..arch - if fs.exists(ucrt) then - if not ver or ver < version then - redist, ver = ucrt, version - end - end - end - accept(path, 0) - for p in fs.pairs(path) do - local version = p:filename():string():gsub("10%.0%.([0-9]+)%.0", "%1") - version = tonumber(version) - accept(p:string(), version) - end - if not redist then - return - end - if optimize == "off" then - if ver == 0 then - --TODO 不一定合理,但至少比0好 - ver = 17134 - end - return redist, UniversalCRTSdkDir.."/bin/10.0."..ver..".0/"..arch.."/ucrt" - end - return redist -end - -local function llvmpath() - local path = installpath().."/VC/Tools/Llvm/x64/lib/clang/" - for p in fs.pairs(path) do - local version = p:filename():string() - return path .. version .."/lib/windows/" - end -end - -return { - installpath = installpath, - toolspath = toolspath, - environment = environment, - prefix = prefix, - binpath = binpath, - vcrtpath = vcrtpath, - ucrtpath = ucrtpath, - findwinsdk = findwinsdk, - llvmpath = llvmpath, -} diff --git a/scripts/env/msvc.lua b/scripts/env/msvc.lua index 97e92a2..2c0de93 100644 --- a/scripts/env/msvc.lua +++ b/scripts/env/msvc.lua @@ -1,11 +1,275 @@ -local find_msvc = require "env.find_msvc" local fs = require "bee.filesystem" +local sp = require "bee.subprocess" local fsutil = require "fsutil" local globals = require "globals" +local log = require "log" + +local function Is64BitWindows() + -- https://docs.microsoft.com/en-us/archive/blogs/david.wang/howto-detect-process-bitness + return os.getenv "PROCESSOR_ARCHITECTURE" == "AMD64" or os.getenv "PROCESSOR_ARCHITEW6432" == "AMD64" +end + +local ProgramFiles = Is64BitWindows() and "ProgramFiles(x86)" or "ProgramFiles" +local vswhere = os.getenv(ProgramFiles).."/Microsoft Visual Studio/Installer/vswhere.exe" +local need = { LIB = true, LIBPATH = true, PATH = true, INCLUDE = true } + +local function writeall(filename, content) + local f = assert(io.open(filename, "w")) + if content then + f:write(content) + end +end + +local function readall(filename) + local f = assert(io.open(filename, "r")) + return f:read "a" +end + +local function strtrim(str) + return str:gsub("^%s*(.-)%s*$", "%1") +end + +local InstallDir +local function installpath() + if InstallDir then + return InstallDir + end + local process = assert(sp.spawn { + vswhere, + "-nologo", + "-latest", + "-prerelease", + "-utf8", + "-products", "*", + "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", + "-property", "installationPath", + stdout = true, + stderr = "stdout", + }) + local result = strtrim(process.stdout:read "a") + process.stdout:close() + if process:wait() ~= 0 then + log.fastfail("[vswhere] %s", result) + end + if result == "" then + log.fastfail("[vswhere] VisualStudio not found. %s", result) + end + InstallDir = result + return InstallDir +end + +local function parse_env(str) + local pos = str:find("=") + if not pos then + return + end + return strtrim(str:sub(1, pos - 1)), strtrim(str:sub(pos + 1)) +end + +local function findwinsdk() + local function query(command) + local f = io.popen(command, "r") + if f then + for l in f:lines() do + local r = l:match "^ [^%s]+ [^%s]+ (.*)$" + if r then + f:close() + return r + end + end + end + end + local function find(dir) + local max + for file in fs.pairs(dir.."/include") do + if fs.exists(file / "um" / "winsdkver.h") then + local version = file:filename():string() + if version:sub(1, 3) == "10." then + if max then + if max < version then + max = version + end + else + max = version + end + end + end + end + return max + end + for _, v in ipairs { + [[HKLM\SOFTWARE\Wow6432Node]], + [[HKCU\SOFTWARE\Wow6432Node]], + [[HKLM\SOFTWARE]], + [[HKCU\SOFTWARE]] + } do + local WindowsSdkDir = query(([[reg query "%s\Microsoft\Microsoft SDKs\Windows\v10.0" /v "InstallationFolder"]]):format(v)) + if WindowsSdkDir then + local WindowSdkVersion = find(WindowsSdkDir) + if WindowSdkVersion then + return WindowSdkVersion + end + end + end +end + +local function vsdevcmd(winsdk, arch, f) + local vsvars32 = installpath().."/Common7/Tools/VsDevCmd.bat" + local args = { vsvars32 } + if arch then + args[#args+1] = ("-arch=%s"):format(arch) + end + if winsdk then + args[#args+1] = ("-winsdk=%s"):format(winsdk) + end + local process = assert(sp.spawn { + args, "&&", "set", + stderr = true, + stdout = true, + searchPath = true, + env = { + VSCMD_SKIP_SENDTELEMETRY = "1" + } + }) + for line in process.stdout:lines() do + local name, value = parse_env(line) + if name and value then + f(name, value) + end + end + local err = process.stderr:read "a" + process.stdout:close() + process.stderr:close() + if process:wait() ~= 0 then + log.fastfail("Call `VsDevCmd.bat` error:\n%s", err) + end +end + +local function environment(winsdk, arch) + local env = {} + vsdevcmd(winsdk, arch, function (name, value) + name = name:upper() + if need[name] then + env[name] = value + end + end) + return env +end + +local function getMsvcDepsPrefix(env) + local testdir = os.tmpname() + fs.create_directories(testdir) + writeall(testdir.."/test.c", "#include ") + writeall(testdir.."/build.ninja", [[ +rule showIncludes + command = cl /nologo /showIncludes -c $in +build test: showIncludes test.c +]]) + local process = assert(sp.spawn { + "cmd", "/c", "ninja", + searchPath = true, + env = env, + cwd = testdir, + stdout = true, + stderr = "stdout", + }) + local data = process.stdout:read "a" + if process:wait() ~= 0 then + log.fastfail("ninja failed: %s", data) + end + fs.remove_all(testdir) + + for line in data:gmatch "[^\n\r]+" do + local m = line:match("[^:]+:[^:]+:") + if m then + return m + end + end + log.fastfail("parse msvc_deps_prefix failed:\n%s", data) +end + +local function toolspath() + local ToolsVersion = (function () + local verfile = installpath().."/VC/Auxiliary/Build/Microsoft.VCToolsVersion.default.txt" + local r = readall(verfile) + return strtrim(r) + end)() + return installpath().."/VC/Tools/MSVC/"..ToolsVersion +end + +local function binpath(arch) + local host = Is64BitWindows() and "Hostx64" or "Hostx86" + return toolspath().."/bin/"..host.."/"..arch +end + +local function vcrtpath(arch, optimize) + local RedistVersion = (function () + local verfile = installpath().."/VC/Auxiliary/Build/Microsoft.VCRedistVersion.default.txt" + local r = readall(verfile) + return strtrim(r) + end)() + local ToolsetVersion = (function () + local verfile = toolspath().."/include/yvals_core.h" + local r = readall(verfile) + return r:match "#define%s+_MSVC_STL_VERSION%s+(%d+)" + end)() + local path = installpath().."/VC/Redist/MSVC/"..RedistVersion + if optimize == "off" then + return path.."/debug_nonredist/"..arch.."/Microsoft.VC"..ToolsetVersion..".DebugCRT" + end + return path.."/"..arch.."/Microsoft.VC"..ToolsetVersion..".CRT" +end + +local function ucrtpath(arch, optimize) + local UniversalCRTSdkDir + vsdevcmd(findwinsdk(), arch, function (name, value) + if name == "UniversalCRTSdkDir" then + UniversalCRTSdkDir = value + end + end) + if not UniversalCRTSdkDir then + return + end + local path = UniversalCRTSdkDir.."/Redist" + local redist, ver + local function accept(p, version) + local ucrt = p.."/ucrt/DLLs/"..arch + if fs.exists(ucrt) then + if not ver or ver < version then + redist, ver = ucrt, version + end + end + end + accept(path, 0) + for p in fs.pairs(path) do + local version = p:filename():string():gsub("10%.0%.([0-9]+)%.0", "%1") + version = tonumber(version) + accept(p:string(), version) + end + if not redist then + return + end + if optimize == "off" then + if ver == 0 then + --TODO 不一定合理,但至少比0好 + ver = 17134 + end + return redist, UniversalCRTSdkDir.."/bin/10.0."..ver..".0/"..arch.."/ucrt" + end + return redist +end + +local function llvmpath() + local path = installpath().."/VC/Tools/Llvm/x64/lib/clang/" + for p in fs.pairs(path) do + local version = p:filename():string() + return path .. version .."/lib/windows/" + end +end local m = {} local env -local prefix +local msvc_deps_prefix local ArchAlias = { x86_64 = "x64", @@ -39,7 +303,7 @@ local function updateEnvConfig() if not env then local config = readEnvConfig() env = config.env - prefix = config.prefix + msvc_deps_prefix = config.prefix end end @@ -62,23 +326,23 @@ function m.createEnvConfig(arch, rebuild) and config.toolspath and fs.exists(config.toolspath) then env = config.env - prefix = config.prefix + msvc_deps_prefix = config.prefix return end end - local winsdk = find_msvc.findwinsdk() - env = find_msvc.environment(winsdk, ArchAlias[arch]) - prefix = find_msvc.prefix(env) + local winsdk = findwinsdk() + env = environment(winsdk, ArchAlias[arch]) + msvc_deps_prefix = getMsvcDepsPrefix(env) local s = {} s[#s+1] = "return {" s[#s+1] = ("arch=%q,"):format(arch) - s[#s+1] = ("toolspath=%q,"):format(find_msvc.toolspath()) + s[#s+1] = ("toolspath=%q,"):format(toolspath()) s[#s+1] = ("console_cp=%q,"):format(console_cp) if winsdk then s[#s+1] = ("winsdk=%q,"):format(winsdk) end - s[#s+1] = ("prefix=%q,"):format(prefix) + s[#s+1] = ("prefix=%q,"):format(msvc_deps_prefix) s[#s+1] = "env={" for name, value in pairs(env) do s[#s+1] = ("%s=%q,"):format(name, value) @@ -96,16 +360,16 @@ end function m.getPrefix() updateEnvConfig() - return prefix + return msvc_deps_prefix end function m.archAlias(arch) return ArchAlias[arch] end -m.binpath = find_msvc.binpath -m.vcrtpath = find_msvc.vcrtpath -m.ucrtpath = find_msvc.ucrtpath -m.llvmpath = find_msvc.llvmpath +m.binpath = binpath +m.vcrtpath = vcrtpath +m.ucrtpath = ucrtpath +m.llvmpath = llvmpath return m