auto
This commit is contained in:
@@ -0,0 +1,219 @@
|
||||
/**
|
||||
* C 代码编译运行后端服务
|
||||
* 接收 C 代码 → 编译 → 运行 → 返回输出
|
||||
*/
|
||||
const express = require('express')
|
||||
const cors = require('cors')
|
||||
const { execSync } = require('child_process')
|
||||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
|
||||
const app = express()
|
||||
const PORT = 3001
|
||||
|
||||
app.use(cors())
|
||||
app.use(express.json({ limit: '1mb' }))
|
||||
|
||||
// 查找可用的 C 编译器
|
||||
// w64devkit GCC 路径
|
||||
const W64DEVKIT_DIR = path.join(__dirname, 'bin', 'w64devkit', 'w64devkit')
|
||||
const W64DEVKIT_GCC = path.join(W64DEVKIT_DIR, 'bin', 'gcc.exe')
|
||||
const W64DEVKIT_PREFIX = path.join(W64DEVKIT_DIR, 'lib', 'gcc')
|
||||
|
||||
function findCompiler() {
|
||||
const candidates = [
|
||||
// MinGW / MSYS2 (PATH)
|
||||
'gcc.exe',
|
||||
'x86_64-w64-mingw32-gcc.exe',
|
||||
'i686-w64-mingw32-gcc.exe',
|
||||
// TCC (Tiny C Compiler)
|
||||
'tcc.exe',
|
||||
// Clang
|
||||
'clang.exe',
|
||||
// MSVC
|
||||
'cl.exe',
|
||||
// 本项目的便携编译器
|
||||
path.join(__dirname, 'bin', 'tcc.exe'),
|
||||
path.join(__dirname, 'bin', 'gcc.exe'),
|
||||
path.join(__dirname, '..', 'tools', 'tcc', 'tcc.exe'),
|
||||
]
|
||||
|
||||
// 先在 PATH 中查找
|
||||
for (const name of ['gcc.exe', 'tcc.exe', 'clang.exe', 'cl.exe']) {
|
||||
try {
|
||||
execSync(`where ${name}`, { stdio: 'pipe', timeout: 3000 })
|
||||
return { path: name, name }
|
||||
} catch {}
|
||||
}
|
||||
|
||||
// 检查 w64devkit GCC(需设置 GCC_EXEC_PREFIX)
|
||||
if (fs.existsSync(W64DEVKIT_GCC)) {
|
||||
return { path: W64DEVKIT_GCC, name: 'gcc.exe', execPrefix: W64DEVKIT_PREFIX }
|
||||
}
|
||||
|
||||
// 再检查其他具体路径
|
||||
for (const candidate of candidates) {
|
||||
try {
|
||||
const absPath = path.resolve(candidate)
|
||||
if (fs.existsSync(absPath)) {
|
||||
return { path: absPath, name: path.basename(absPath) }
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
// 搜索 Visual Studio MSVC 安装目录
|
||||
try {
|
||||
const vsPaths = [
|
||||
'C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC',
|
||||
'C:\\Program Files\\Microsoft Visual Studio\\2022\\Professional\\VC\\Tools\\MSVC',
|
||||
'C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\VC\\Tools\\MSVC',
|
||||
'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Tools\\MSVC',
|
||||
'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools\\VC\\Tools\\MSVC',
|
||||
]
|
||||
for (const vsPath of vsPaths) {
|
||||
if (fs.existsSync(vsPath)) {
|
||||
const versions = fs.readdirSync(vsPath).sort().reverse()
|
||||
for (const ver of versions) {
|
||||
const clPath = path.join(vsPath, ver, 'bin', 'Hostx64', 'x64', 'cl.exe')
|
||||
if (fs.existsSync(clPath)) {
|
||||
return { path: clPath, name: 'cl.exe' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
// 编译并运行 C 代码
|
||||
app.post('/api/run-c', (req, res) => {
|
||||
const { code } = req.body
|
||||
if (!code) {
|
||||
return res.status(400).json({ error: '请提供 C 代码' })
|
||||
}
|
||||
|
||||
const compiler = findCompiler()
|
||||
if (!compiler) {
|
||||
return res.status(400).json({
|
||||
error: '未找到 C 编译器',
|
||||
hint: '请安装 MinGW-w64 GCC 编译器',
|
||||
installGuide: [
|
||||
'--- 安装 MinGW-w64 ---',
|
||||
'方法一(推荐):下载 MinGW-w64 GCC',
|
||||
' 1. 访问 https://winlibs.com/ 下载 GCC (MinGW-w64)',
|
||||
' 2. 解压并将 bin/ 目录添加到系统 PATH 环境变量',
|
||||
' 3. 重启终端后即可使用',
|
||||
'',
|
||||
'方法二(轻量):下载 TCC (Tiny C Compiler)',
|
||||
' 将 tcc.exe 放入 server/bin/ 目录',
|
||||
'',
|
||||
'安装后重新启动: npm run server',
|
||||
].join('\n')
|
||||
})
|
||||
}
|
||||
|
||||
const tmpDir = path.join(__dirname, '..', 'tmp')
|
||||
if (!fs.existsSync(tmpDir)) {
|
||||
fs.mkdirSync(tmpDir, { recursive: true })
|
||||
}
|
||||
|
||||
const srcFile = path.join(tmpDir, `code_${Date.now()}.c`)
|
||||
const outFile = path.join(tmpDir, `code_${Date.now()}.exe`)
|
||||
|
||||
try {
|
||||
// 写入源码
|
||||
fs.writeFileSync(srcFile, code, 'utf-8')
|
||||
|
||||
let compileCmd
|
||||
const execOptions = {
|
||||
timeout: 15000,
|
||||
cwd: tmpDir,
|
||||
encoding: 'utf-8',
|
||||
env: { ...process.env }
|
||||
}
|
||||
|
||||
if (compiler.name.includes('tcc')) {
|
||||
// TCC: 编译并运行一步到位(支持 -run)
|
||||
compileCmd = `"${compiler.path}" -run "${srcFile}"`
|
||||
} else if (compiler.execPrefix) {
|
||||
// w64devkit GCC: 需要设置 GCC_EXEC_PREFIX 并加入 PATH
|
||||
const w64binDir = path.dirname(compiler.path)
|
||||
execOptions.env.GCC_EXEC_PREFIX = compiler.execPrefix + path.sep
|
||||
execOptions.env.PATH = w64binDir + path.delimiter + (execOptions.env.PATH || '')
|
||||
compileCmd = `"${compiler.path}" "${srcFile}" -o "${outFile}" -Wall -std=c99`
|
||||
} else {
|
||||
// GCC/Clang: 先编译再运行
|
||||
compileCmd = `"${compiler.path}" "${srcFile}" -o "${outFile}" -Wall -std=c99 2>&1`
|
||||
}
|
||||
|
||||
// 编译
|
||||
const compileOutput = execSync(compileCmd, execOptions)
|
||||
|
||||
let runOutput = compileOutput
|
||||
|
||||
// GCC/Clang: 编译后还需运行 exe
|
||||
if (!compiler.name.includes('tcc') && fs.existsSync(outFile)) {
|
||||
try {
|
||||
runOutput = execSync(`"${outFile}"`, {
|
||||
timeout: 10000,
|
||||
cwd: tmpDir,
|
||||
encoding: 'utf-8',
|
||||
env: execOptions.env
|
||||
})
|
||||
} catch (runErr) {
|
||||
runOutput = runErr.stdout || runErr.message
|
||||
}
|
||||
}
|
||||
|
||||
// 清理临时文件
|
||||
try {
|
||||
if (fs.existsSync(srcFile)) fs.unlinkSync(srcFile)
|
||||
if (fs.existsSync(outFile)) fs.unlinkSync(outFile)
|
||||
} catch {}
|
||||
|
||||
res.json({ output: runOutput })
|
||||
} catch (compileErr) {
|
||||
// 编译错误
|
||||
res.json({
|
||||
output: compileErr.stdout || compileErr.message,
|
||||
isError: true
|
||||
})
|
||||
} finally {
|
||||
// 确保清理
|
||||
try {
|
||||
if (fs.existsSync(srcFile)) fs.unlinkSync(srcFile)
|
||||
if (fs.existsSync(outFile)) fs.unlinkSync(outFile)
|
||||
} catch {}
|
||||
}
|
||||
})
|
||||
|
||||
// 健康检查
|
||||
app.get('/api/status', (req, res) => {
|
||||
const compiler = findCompiler()
|
||||
res.json({
|
||||
status: 'ok',
|
||||
compiler: compiler ? `${compiler.name} (${compiler.path})` : '未安装'
|
||||
})
|
||||
})
|
||||
|
||||
// 下载 TCC 脚本
|
||||
app.get('/api/setup-compiler', (req, res) => {
|
||||
res.json({
|
||||
message: '请手动下载 TCC:',
|
||||
url: 'https://raw.githubusercontent.com/FooBarWidget/tinycc/master/tcc.exe',
|
||||
instructions: `将 tcc.exe 放入 ${path.join(__dirname, 'bin')} 目录`
|
||||
})
|
||||
})
|
||||
|
||||
app.listen(PORT, () => {
|
||||
console.log(`✅ C 代码运行服务已启动: http://localhost:${PORT}`)
|
||||
const compiler = findCompiler()
|
||||
if (compiler) {
|
||||
console.log(` 检测到编译器: ${compiler.name}`)
|
||||
} else {
|
||||
console.log(` ⚠️ 未检测到 C 编译器,请安装后使用`)
|
||||
console.log(` 下载 TCC: https://raw.githubusercontent.com/FooBarWidget/tinycc/master/tcc.exe`)
|
||||
console.log(` 放置路径: ${path.join(__dirname, 'bin', 'tcc.exe')}`)
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user