github.com/LanceLRQ/deer-common@v0.0.9-0.20210319081233-e8222ac018a8/provider/main.go (about)

     1  /* Compiler Provider Base
     2   * (C) 2019 LanceLRQ
     3   *
     4   * This code is licenced under the GPLv3.
     5   */
     6  package provider
     7  
     8  import (
     9  	"context"
    10  	"encoding/json"
    11  	"fmt"
    12  	"github.com/LanceLRQ/deer-common/structs"
    13  	"github.com/LanceLRQ/deer-common/utils"
    14  	"github.com/pkg/errors"
    15  	"github.com/satori/go.uuid"
    16  	"io/ioutil"
    17  	"os"
    18  	"path"
    19  	"strings"
    20  	"time"
    21  )
    22  
    23  type CompileCommandsStruct struct {
    24  	GNUC    string `json:"gcc"`
    25  	GNUCPP  string `json:"g++"`
    26  	Java    string `json:"java"`
    27  	Go      string `json:"golang"`
    28  	NodeJS  string `json:"nodejs"`
    29  	PHP     string `json:"php"`
    30  	Ruby    string `json:"ruby"`
    31  	Python2 string `json:"python2"`
    32  	Python3 string `json:"python3"`
    33  	Rust    string `json:"rust"`
    34  }
    35  
    36  var CompileCommands = CompileCommandsStruct{
    37  	GNUC:    "gcc %s -o %s -ansi -fno-asm -Wall -std=c11 -lm",
    38  	GNUCPP:  "g++ %s -o %s -ansi -fno-asm -Wall -lm -std=c++11",
    39  	Java:    "javac -encoding utf-8 %s -d %s",
    40  	Go:      "go build -o %s %s",
    41  	NodeJS:  "node -c %s",
    42  	PHP:     "php -l -f %s",
    43  	Ruby:    "ruby -c %s",
    44  	Python2: "python -u %s",
    45  	Python3: "python3 -u %s",
    46  	Rust:    "rustc %s -o %s",
    47  }
    48  
    49  type CodeCompileProviderInterface interface {
    50  	// 初始化
    51  	Init(code string, workDir string) error
    52  	// 初始化文件信息
    53  	initFiles(codeExt string, programExt string) error
    54  	// 执行编译
    55  	Compile() (result bool, errmsg string)
    56  	// 清理工作目录
    57  	Clean()
    58  	// 获取程序的运行命令参数组
    59  	GetRunArgs() (args []string)
    60  	// 判断STDERR的输出内容是否存在编译错误信息,通常用于脚本语言的判定,
    61  	IsCompileError(remsg string) bool
    62  	// 是否为实时编译的语言
    63  	IsRealTime() bool
    64  	// 是否已经编译完毕
    65  	IsReady() bool
    66  	// 调用Shell命令并获取运行结果
    67  	shell(commands string) (success bool, errout string)
    68  	// 保存代码到文件
    69  	saveCode() error
    70  	// 检查工作目录是否存在
    71  	checkWorkDir() error
    72  	// 获取名称
    73  	GetName() string
    74  }
    75  
    76  type CodeCompileProvider struct {
    77  	CodeCompileProviderInterface
    78  	Name                             string // 编译器提供程序名称
    79  	codeContent                      string // 代码
    80  	realTime                         bool   // 是否为实时编译的语言
    81  	isReady                          bool   // 是否已经编译完毕
    82  	codeFileName, codeFilePath       string // 目标程序源文件
    83  	programFileName, programFilePath string // 目标程序文件
    84  	workDir                          string // 工作目录
    85  }
    86  
    87  func PlaceCompilerCommands(configFile string) error {
    88  	if configFile != "" {
    89  		_, err := os.Stat(configFile)
    90  		// ignore
    91  		if os.IsNotExist(err) {
    92  			return nil
    93  		}
    94  		cbody, err := ioutil.ReadFile(configFile)
    95  		if err != nil {
    96  			return err
    97  		}
    98  		err = json.Unmarshal(cbody, &CompileCommands)
    99  		if err != nil {
   100  			return err
   101  		}
   102  	}
   103  	return nil
   104  }
   105  
   106  func (prov *CodeCompileProvider) initFiles(codeExt string, programExt string) error {
   107  	prov.codeFileName = fmt.Sprintf("%s%s", uuid.NewV4().String(), codeExt)
   108  	prov.programFileName = fmt.Sprintf("%s%s", uuid.NewV4().String(), programExt)
   109  	prov.codeFilePath = path.Join(prov.workDir, prov.codeFileName)
   110  	prov.programFilePath = path.Join(prov.workDir, prov.programFileName)
   111  
   112  	err := prov.saveCode()
   113  	return err
   114  }
   115  
   116  func (prov *CodeCompileProvider) GetName() string {
   117  	return prov.Name
   118  }
   119  
   120  func (prov *CodeCompileProvider) Clean() {
   121  	_ = os.Remove(prov.codeFilePath)
   122  	_ = os.Remove(prov.programFilePath)
   123  }
   124  
   125  func (prov *CodeCompileProvider) shell(commands string) (success bool, errout string) {
   126  	ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
   127  	cmdArgs := strings.Split(commands, " ")
   128  	if len(cmdArgs) <= 1 {
   129  		return false, "not enough arguments for compiler"
   130  	}
   131  	ret, err := utils.RunUnixShell(&structs.ShellOptions{
   132  		Context:   ctx,
   133  		Name:      cmdArgs[0],
   134  		Args:      cmdArgs[1:],
   135  		StdWriter: nil,
   136  		OnStart:   nil,
   137  	})
   138  	if err != nil {
   139  		return false, err.Error()
   140  	}
   141  	if !ret.Success {
   142  		return false, ret.Stderr
   143  	}
   144  	return true, ""
   145  }
   146  
   147  func (prov *CodeCompileProvider) saveCode() error {
   148  	file, err := os.OpenFile(prov.codeFilePath, os.O_RDWR|os.O_CREATE, 0644)
   149  	if err != nil {
   150  		return err
   151  	}
   152  	defer file.Close()
   153  	_, err = file.WriteString(prov.codeContent)
   154  	return err
   155  }
   156  
   157  func (prov *CodeCompileProvider) checkWorkDir() error {
   158  	_, err := os.Stat(prov.workDir)
   159  	if err != nil {
   160  		if os.IsNotExist(err) {
   161  			return errors.Errorf("work dir not exists")
   162  		}
   163  		return err
   164  	}
   165  	return nil
   166  }
   167  
   168  func (prov *CodeCompileProvider) IsRealTime() bool {
   169  	return prov.realTime
   170  }
   171  
   172  func (prov *CodeCompileProvider) IsReady() bool {
   173  	return prov.isReady
   174  }