compiler服务设计
提供的服务:编译并运行代码,得到格式化的相关的结果
compiler.hpp:提供编译服务
runner.hpp:提供运行服务
compile_run.hpp:整合功能,既调用compiler又调用runner
compile_server.cc:基于网络请求的编译并运行服务
makefile
编写makefile
compile_server:compile_server.cc
g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
rm -f compile_server
编写util.hpp
创建一个temp路径来保存临时文件
在comm文件夹创建一个util.hpp,提供一个工具类
util.hpp
#pragma once
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/unistd.h>
namespace ns_util
{
const std::string temp_path = "./temp/";
class PathUtil
{
public:
static std::string AddSuffix(const std::string &file_name, const std::string &suffix)
{
std::string path_name = temp_path;
path_name += file_name;
path_name += suffix;
return path_name;
}
//构建源文件路径+后缀的完整文件名
//1234 -> ./temp/1234.cpp
static std::string Src(const std::string &file_name)
{
return AddSuffix(file_name, ".cpp");
}
//构建可执行程序的完整路径+后缀名
static std::string Exe(const std::string &file_name)
{
return AddSuffix(file_name, ".exe");
}
//构建该程序对应的标准错误完整路径+后缀名
static std::string Stderr(const std::string &file_name)
{
return AddSuffix(file_name, ".stderr");
}
};
class FileUtil {
public:
static bool IsFileExists(const std::string &path_name)
{
struct stat st;
if(stat(path_name.c_str(), &st) == 0){
//获取属性成功,文件已经存在
return true;
}
return false;
}
};
}
编写compiler.hpp
compiler.hpp
#pragma once
#include <iostream>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "../comm/util.hpp"
// 只负责进行代码的编译
namespace ns_compiler
{
//引入路径拼接功能
using namespace ns_util;
class Compiler{
public:
Compiler()
{}
~Compiler()
{}
//返回值:编译成功:true,否则:false
//输入参数:编译的文件名
//1234 -> ./temp/1234.cpp源文件
//1234 -> ./temp/1234.exe可执行程序文件名
//1234 -> ./temp/1234.stderr标准错误文件名
static bool Compile(const std::string &file_name)
{
pid_t pid = fork();
if (pid < 0)
{
return false;
}
else if (pid == 0)
{
int _stderr = open(PathUtil::Stderr(file_name).c_str(), O_CREAT | O_WRONLY, 0644);
if(_stderr < 0){
exit(1);
}
//重定向标准错误到_stderr
dup2(_stderr, 2);
//程序替换,并不影响进程的文件描述符表
// 子进程:调用编译器,完成对代码的编译工作
//g++ -o target src -std=c++11
execlp("g++", "-o", PathUtil::Exe(file_name).c_str(), \
PathUtil::Src(file_name).c_str(), "-std=c++11", nullptr/*不要忘记*/);
exit(2);
}
else{
waitpid(pid, nullptr, 0);
//编译是否成功,就看有没有形成对应的可执行程序
if(FileUtil::IsFileExists(PathUtil::Exe(file_name))){
return true;
}
}
return false;
}
};
}
日志功能
在comm新建一个log.hpp
log.hpp
#pragma once
#include <iostream>
#include <string>
#include "util.hpp"
namespace ns_log
{
using namespace ns_util;
//日志等级
enum {
INFO,
DEBUG,
WARNING,
ERROR,
FATAL
};
inline std::ostream &Log(const std::string &level, const std::string &file_name, int line)
{
// 添加日志等级
std::string message = "[";
message += level;
message += "]";
// 添加报错文件名称
message += "[";
message += file_name;
message += "]";
// 添加报错行
message += "[";
message += std::to_string(line);
message += "]";
// 日志时间戳
message += "[";
message += TimeUtil::GetTimeStamp();
message += "]";
// cout 本质 内部是包含缓冲区的
std::cout << message; //不要endl进行刷新
return std::cout;
}
// LOG(INFo) << "message" << "\n";
// 开放式日志
#define LOG(level) Log(#level, __FILE__, __LINE__)
}
util.hpp,添加TimeUtil类
class TimeUtil
{
public:
static std::string GetTimeStamp()
{
struct timeval _time;
gettimeofday(&_time, nullptr);
return std::to_string(_time.tv_sec);
}
};
日志功能调用
#pragma once
#include <iostream>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "../comm/util.hpp"
#include "../comm/log.hpp"
// 只负责进行代码的编译
namespace ns_compiler
{
//引入路径拼接功能
using namespace ns_util;
using namespace ns_log;
class Compiler{
public:
Compiler()
{}
~Compiler()
{}
//返回值:编译成功:true,否则:false
//输入参数:编译的文件名
//1234 -> ./temp/1234.cpp源文件
//1234 -> ./temp/1234.exe可执行程序文件名
//1234 -> ./temp/1234.stderr标准错误文件名
static bool Compile(const std::string &file_name)
{
pid_t pid = fork();
if (pid < 0)
{
LOG(ERROR) << "内部错误,创建子进程失败" << "\n";
return false;
}
else if (pid == 0)
{
int _stderr = open(PathUtil::Stderr(file_name).c_str(), O_CREAT | O_WRONLY, 0644);
if(_stderr < 0){
LOG(WARNING) << "没有成功形成stderr文件" << "\n";
exit(1);
}
//重定向标准错误到_stderr
dup2(_stderr, 2);
//程序替换,并不影响进程的文件描述符表
// 子进程:调用编译器,完成对代码的编译工作
//g++ -o target src -std=c++11
execlp("g++", "g++", "-o", PathUtil::Exe(file_name).c_str(), \
PathUtil::Src(file_name).c_str(), "-std=c++11", nullptr/*不要忘记*/);
LOG(ERROR) << "启动编译器g++失败,可能是参数错误" << "\n";
exit(2);
}
else{
waitpid(pid, nullptr, 0);
//编译是否成功,就看有没有形成对应的可执行程序
if(FileUtil::IsFileExists(PathUtil::Exe(file_name))){
LOG(INFO) << PathUtil::Src(file_name) << "编译成功" << "\n";
return true;
}
}
LOG(ERROR) << "编译失败,没有形成可执行程序" << "\n";
return false;
}
};
}
测试编译模块
在temp文件夹里添加一个code.cpp作为测试代码
#include <iostream>
int main()
{
std::cout << "hello world!" << std::endl;
return 0;
}
编写compile_server.cc
#include "compiler.hpp"
using namespace ns_compiler;
int main()
{
std::string code = "code";
Compiler::Compile(code);
return 0;
}
编译运行compile_server
编译成功
在temp有code执行文件