220 lines
6.6 KiB
JavaScript
220 lines
6.6 KiB
JavaScript
/**
|
||
* 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')}`)
|
||
}
|
||
})
|