想做一個能夠在線編譯代碼運行的平台,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方法就可以了