Loading

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方法就可以了

 

posted @ 2019-06-25 13:28  1900Yin  阅读(2512)  评论(7编辑  收藏  举报