Java后台調用gcc編譯C語言代碼


想做一個能夠在線編譯代碼運行的平台,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(" ","&nbsp;");
                else {
                    //編譯出錯,找到error的位置,返回error及其后的信息
                    int errorIndex = compileResult.indexOf("error");
                    return compileResult.substring(errorIndex).replaceAll("\n","<br/>").replaceAll(" ","&nbsp;");
                }
            }
        });
        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方法就可以了

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM