想做一个能够在线编译代码运行的平台,Java和SQL已经支持了,因为是用Java写的后台,所以Java和SQL挺容易就实现了,做到支持C的时候就卡住了,网上搜了一下这种帖子好像很少。
我采取的办法是就是刚开始学C语言的教的调用GCC来编译.C文件的文件,首先将前端传过来的C代码写入到特定的路径下,然后利用Java的API调用CMD来执行gcc命令编译这个文件,这好像有点MakeFile文件的意思。。编译之后继续调用CMD执行生成的exe,同时获取CMD的输出,至此整个过程完成,但是最后需要做好一项善后工作,那就是继续调用CMD将这个exe的进程杀掉,否则会出现问题,因为exe还没停止,继续编译写入会报权限问题。
封装了整个编译运行的Servivce:
package com.mine.ide.service.implement; import com.mine.ide.util.CLangUtil; import org.apache.log4j.Logger; import org.springframework.stereotype.Service; import java.io.*; import java.util.concurrent.*; /** * @author yintianhao * @createTime 20190625 11:14 * @description ExecuteCLang */ @Service public class ExecuteCLangService { private static final Logger log = Logger.getLogger(ExecuteCLangService.class); //代码存放路径 public static final String CODE_PATH = "D:\\springboot2\\code\\"; /** * @param content C代码 * */ private boolean generateCFile(String content){ BufferedWriter out = null; try{ //写入 out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(CODE_PATH+"test.c", false))); out.write(content); }catch (Exception e){ log.error(e.getCause().getMessage()); return false; }finally { try { out.close(); }catch (IOException e){ log.error(e.getCause().getMessage()); return false; } } return true; } /** * @param sourceCode 源代码 * */ public String runCLangCode(String sourceCode){ //先生成文件 generateCFile(sourceCode); Executor executor = Executors.newSingleThreadExecutor(); FutureTask<String> futureTask = new FutureTask<String>(new Callable<String>() { @Override public String call() throws Exception { //编译C文件 String compileResult = execCmd("gcc -o "+CODE_PATH+"test "+CODE_PATH+"test.c",null); if (compileResult.equals("")) //编译不出错的情况,replaceAll将\n换成HTML的换行,空格换成HTML的空格 return execCmd(CODE_PATH+"test.exe",null).replaceAll("\n","<br/>").replaceAll(" "," "); else { //编译出错,找到error的位置,返回error及其后的信息 int errorIndex = compileResult.indexOf("error"); return compileResult.substring(errorIndex).replaceAll("\n","<br/>").replaceAll(" "," "); } } }); executor.execute(futureTask); try { //编译运行完毕将text.exe的进程kill execCmd("taskkill /f /im test.exe",null); log.info("killed test.exe"); }catch (Exception e){ e.printStackTrace(); } String result = ""; try{ //设置超时时间 result=futureTask.get(10, TimeUnit.SECONDS); }catch (InterruptedException e) { log.info("Interrupt"); result = "程序中断,请检查是否有内存冲突等错误"; // future.cancel(true); }catch (ExecutionException e) { result = "程序执行错误"; futureTask.cancel(true); }catch (TimeoutException e) { result = "时间超限,请检查是否存在无限循环等程序无法自动结束的情况"; } log.info("result - "+result); return result.equals("")?"没有输出":result; } /** * 执行系统命令, 返回执行结果 * @param cmd 需要执行的命令 * @param dir 执行命令的子进程的工作目录, null 表示和当前主进程工作目录相同 */ private String execCmd(String cmd, File dir) throws Exception { StringBuilder result = new StringBuilder(); Process process = null; BufferedReader bufrIn = null; BufferedReader bufrError = null; try { // 执行命令, 返回一个子进程对象(命令在子进程中执行) process = Runtime.getRuntime().exec(cmd, null, dir); // 方法阻塞, 等待命令执行完成(成功会返回0) process.waitFor(); // 获取命令执行结果, 有两个结果: 正常的输出 和 错误的输出(PS: 子进程的输出就是主进程的输入) bufrIn = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8")); bufrError = new BufferedReader(new InputStreamReader(process.getErrorStream(), "UTF-8")); // 读取输出 String line = null; while ((line = bufrIn.readLine()) != null) { result.append(line).append('\n'); } while ((line = bufrError.readLine()) != null) { result.append(line).append('\n'); } } finally { closeStream(bufrIn); closeStream(bufrError); // 销毁子进程 if (process != null) { process.destroy(); } } // 返回执行结果 return result.toString(); } private void closeStream(Closeable stream) { if (stream != null) { try { stream.close(); } catch (Exception e) { // nothing } } } }
至此写完,之后只要调用runCLangCode方法就可以了