Files
2026-06-16 09:35:51 +08:00

274 lines
8.0 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 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 {}
}
})
// 运行 Python 代码
app.post('/api/run-py', (req, res) => {
const { code } = req.body
if (!code) {
return res.status(400).json({ error: '请提供 Python 代码' })
}
// 查找 python3 或 python
let pythonCmd = 'python3'
try {
execSync(`${pythonCmd} --version`, { stdio: 'pipe', timeout: 3000 })
} catch {
pythonCmd = 'python'
try {
execSync(`${pythonCmd} --version`, { stdio: 'pipe', timeout: 3000 })
} catch {
return res.status(400).json({
error: '未找到 Python 解释器',
hint: '请安装 Python 3 并确保在 PATH 环境变量中',
installGuide: [
'--- 安装 Python ---',
'1. 访问 https://www.python.org/downloads/ 下载并安装 Python 3',
'2. 安装时勾选 "Add Python to PATH"',
'3. 安装后重启终端',
].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()}.py`)
try {
fs.writeFileSync(srcFile, code, 'utf-8')
const output = execSync(`"${pythonCmd}" "${srcFile}"`, {
timeout: 15000,
cwd: tmpDir,
encoding: 'utf-8'
})
res.json({ output })
} catch (runErr) {
res.json({
output: runErr.stdout || runErr.message,
isError: true
})
} finally {
try { if (fs.existsSync(srcFile)) fs.unlinkSync(srcFile) } 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')}`)
}
})