一、编译服务框架
二、具体实现
首先是编译模块,封装Compiler类,提供Compile函数接口,具体编译流程如下:
- 根据传入文件名,对其进行编译,这里需要实现一个工具类
- 工具类,对文件名进行拼接,待编译的文件放在compile_server/temp文件下,例如test.cpp
- 拼接目标有源文件拼接:test->./temp/test.cpp
- 可执行程序拼接:test->./temp/test.exe
- 错误信息文件拼接:test->./temp/test.stderr
- 正式逻辑开始,首先创建子进程,子进程进行编译服务
- 通过程序替换:execlp函数,执行g++编译源文件,生成text.exe
- g++编译可能成功,可能失败,如果失败,会通过stderr打印,这里对stderr进行重定向到指定文件(dup2函数):./temp/test.stderr
- 父进程对子进程编译结果进行判断,这里只需要判断./temp/test.exe函数是否存在,存在就是编译成功
- 在工具类中FileUtil类提供接口IsFileExists判断文件是否存在,其中调用stat函数
1、compiler.hpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| #pragma once
#include <iostream> #include <string> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #include <fcntl.h>
#include "../common/util.hpp" #include "../common/log.hpp"
namespace ns_compiler { using namespace ns_util; using namespace ns_log; class Compiler { public: Compiler(){}; ~Compiler(){}
static bool Compile(const std::string& file_name) { pid_t pid = fork(); if(pid<0) { LOG(ERROR)<<"内部错误,创建子进程失败"<<"\n"; return false; } else if(pid == 0) { umask(0); int _stderr = open(PathUtil::Stderr(file_name).c_str(), O_CREAT | O_WRONLY, 0644); if(_stderr<0) { LOG(WARNING)<<"没有成功形成stderr文件"<<"\n"; exit(1); } dup2(_stderr, 2); execlp("g++","g++", "-o", PathUtil::Exe(file_name).c_str(),PathUtil::Src(file_name).c_str(),"-std=c++11", NULL); LOG(ERROR)<<"g++编译失败,可能是参数错误"<<"\n"; exit(1); } else { waitpid(pid, nullptr,0); if(FileUtil::IsFileExists(PathUtil::Exe(file_name).c_str())) { LOG(INFO)<<PathUtil::Src(file_name)<<"编译成功"<<"\n"; return true; } LOG(ERROR)<<"编译失败,没有形成可执行程序"<<"\n"; return false; } } }; }
|
2、util.hpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| #pragma once #include <iostream> #include <string> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <sys/time.h>
namespace ns_util { class TimeUtil { public: static std::string GetTimeStamp() { struct timeval _time; gettimeofday(&_time, nullptr); return std::to_string(_time.tv_sec); } };
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+file_name; path_name+=suffix; return path_name; } 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& file_name) { struct stat buf; if(stat(file_name.c_str(), &buf)==0) { return true; } return false; } };
}
|
3、log.hpp
为了更方便调试和打印信息,封装一个日志函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| #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+="]";
std::cout<<message; return std::cout; }
#define LOG(level) Log(#level, __FILE__, __LINE__) }
|