手写g++编译命令行工具笔记

基本想法

为什么要写 CPPRUN:

  1. 如果要开警告开关,敲完整的编译代码还挺麻烦的
  2. 想要编译与运行一次性完成
  3. Windows 的控制台本来是 cmd,后来有了 Powershell,但是后者不能用 < 指定输入文件,比较麻烦

所以可以直接用 C++ 写一个类似于脚本的命令行程序。

功能

  1. 编译单个源代码。
  2. 默认情况下若编译成功,则运行程序,可以指定输入输出文件。
  3. 毫秒级计算实际运行时间。

基本用法:
.\run <源程序文件名,不包括.cpp扩展> <其他指令>
指令列表:

  • \i <文件名> 指定输入文件
  • \o <文件名> 指定输出文件
  • \f <文件名,不带扩展名> 指定exe文件名,默认与源文件相同
  • \std <c++/c++11/c++14> 设置C++版本,默认 C++14
  • \u 取消警告,默认开启 -Wall -Wextra -Wconversion -Wshadow
  • \cmd 回显编译命令,默认不开启
  • \comp 只编译不运行,默认编译后立即运行

代码实现

命令行程序添加参数

以 CPPRUN 为例,.\run <...> 中的 <...> 就是参数。在 C++ 中 main 函数其实是有两个参数的(只是竞赛的时候从来不会用到,也可以不写),即

// 参数随便命名,这里举个例子
int main(int argc, char** argv) {
}

其中 argc 是 ”参数数量“ + 1,注意命令行中不同参数用若干空格隔开;argv[1 ~ argc - 1] 是字符串形式的参数。

在程序中形成编译命令并执行

C风格字符串的拼接还挺麻烦,可以直接用 iostream 中的 string,通过 +, += 可以完成字符串与字符、字符串与字符串的拼接。

执行需要用到 system 函数,但是 system 的参数是C风格字符串,string 类型需要用到内置的 c_str() 转化。另外一提,在 system 中是可以使用 <, > 指定输入输出文件的,因为其本质是在 cmd 中执行了指令。相当于免去了在 Powershell 中打开 cmd 执行指令再关闭 cmd 的这种繁琐的操作。

system 函数是有返回值的,就是执行的该条指令的返回值。这样可以获取程序的返回值判断程序是否正常结束,也可以通过 g++ 的返回值判断是否有编译错误。

Tab. g++ 返回 0 是编译成功(包括有编译警告),其余返回值是有编译错误。

其他的小细节

如果想要在命令行中输出中文,一定保证你的源文件是在 GBK(或者其他中文) 的编码下编写并保存的!

源代码

/* Lucky_Glass */
// 2022-6-21 modified
#include <windows.h>
#include <ctime>
#include <cstdio>
#include <cstring>
#include <iostream>

int getOption(char *_str) {
  std::string str(_str);
  if (str == "\\i") return 1;
  if (str == "\\o") return 2;
  if (str == "\\f") return 3;
  if (str == "\\std") return 4;
  if (str == "\\u") return 5;
  if (str == "\\cmd") return 6;
  if (str == "\\comp") return 7;
  return 0;
}
void somethingError() {
  std::cerr << "<fail> CPPRUN发生了未知错误" << std::endl;
  exit(1);
}

bool isopt[50];
int main(int num, char *opt[]) {
  std::string cmd("g++ ");
  if (num == 1) {
    std::cerr << "<fail> 请指定编译文件" << std::endl;
    return 0;
  }
  std::string fil(opt[1]), inp, oup, gver("c++14");
  cmd += fil, cmd += ".cpp";
  for (int i = 2; i < num; ++i) {
    int optid = getOption(opt[i]);
    isopt[optid] = true;
    if (optid <= 4) {
      std::string ext(opt[++i]);
      switch (optid) {
        case 1: inp = ext; break;
        case 2: oup = ext; break;
        case 3: fil = ext; break;
        case 4: gver = ext; break;
        default: somethingError();
      }
    }
  }

  cmd += " -o ", cmd += fil;
  if (gver == "c++14") cmd += " -std=c++14";
  else if (gver == "c++11") cmd += " -std=c++11";
  else if (gver != "c++") {
    std::cerr << "<fail> C++版本只能为 c++/c++11/c++14" << std::endl;
    return 0;
  }

  if (!isopt[5])
    cmd += " -Wall -Wextra -Wconversion -Wshadow";
  
  if (isopt[6])
    std::cerr << cmd << std::endl;

  if(!system(cmd.c_str()))
    std::cerr << "<success> 编译完成" << std::endl;
  else {
    std::cerr << "<fail> 编译失败"  << std::endl;
    return 0;
  }

  if (!isopt[7]) {
    cmd = ".\\", cmd += fil, cmd += ".exe";
    if (isopt[1]) cmd += " < ", cmd += inp;
    if (isopt[2]) cmd += " > ", cmd += oup;
    DWORD stt, edt;
    stt = GetTickCount();
    int ret = system(cmd.c_str());
    edt = GetTickCount();
    std::cerr.precision(3);
    std::cerr << "<success> 运行结束,返回值 " << ret
              << ",运行时间 " << (edt - stt) / 1000.0
              << std::endl;
  }
  return 0;
}

THE END

posted @ 2022-06-23 23:13  Lucky_Glass  阅读(416)  评论(4编辑  收藏  举报
TOP BOTTOM