Build tool
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

sys.cc 3.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. #include "sys.h"
  2. #include <sys/types.h>
  3. #include <sys/stat.h>
  4. #include <unistd.h>
  5. #include <string.h>
  6. #include <errno.h>
  7. #include <sys/types.h>
  8. #include <sys/wait.h>
  9. #include <stdexcept>
  10. #include "globals.h"
  11. namespace sys {
  12. FileInfo fileInfo(const std::string &path) {
  13. FileInfo finfo;
  14. struct stat st;
  15. if (stat(path.c_str(), &st) < 0) {
  16. throw std::runtime_error("stat '" + path + "': " + strerror(errno));
  17. }
  18. finfo.mTimeSec = st.st_mtim.tv_sec;
  19. finfo.mTimeNsec = st.st_mtim.tv_nsec;
  20. finfo.isDir = S_ISDIR(st.st_mode);
  21. return finfo;
  22. }
  23. bool fileExists(const std::string &path) {
  24. struct stat st;
  25. if (stat(path.c_str(), &st) < 0) {
  26. if (errno == ENOENT) {
  27. return false;
  28. } else {
  29. throw std::runtime_error("stat '" + path + "': " + strerror(errno));
  30. }
  31. }
  32. return true;
  33. }
  34. void mkdirp(const std::string &path) {
  35. // TODO: Implement this in C++ instead
  36. std::vector<const char *> argv;
  37. argv.push_back("mkdir");
  38. argv.push_back("-p");
  39. argv.push_back(path.c_str());
  40. execute(argv, nullptr);
  41. }
  42. void execute(std::vector<const char *> &args, std::string *output) {
  43. if (global::verbose) {
  44. for (size_t i = 0; i < args.size(); ++i) {
  45. if (i == 0) {
  46. fprintf(stderr, "%s", args[i]);
  47. } else {
  48. fprintf(stderr, " %s", args[i]);
  49. }
  50. }
  51. fprintf(stderr, "\n");
  52. }
  53. // argv[0] should be interpreted as a shell command, because being able to run
  54. // CC='gcc --sysroot=/blah' is used by some systems.
  55. std::string command = std::string(args[0]) + " \"$@\"";
  56. std::vector<const char *> argv;
  57. argv.push_back("/bin/sh"); // TODO: Use $SHELL?
  58. argv.push_back("-c");
  59. argv.push_back(command.c_str());
  60. argv.push_back("--");
  61. for (size_t i = 1; i < args.size(); ++i) {
  62. argv.push_back(args[i]);
  63. }
  64. argv.push_back(nullptr);
  65. int fds[2];
  66. if (pipe(fds) < 0) {
  67. throw std::runtime_error(std::string("fork: ") + strerror(errno));
  68. }
  69. pid_t child = fork();
  70. if (child == 0) {
  71. close(fds[0]);
  72. dup2(fds[1], 1);
  73. // So, from what I've read, execvp should never modify its argv; so this should be fine?
  74. if (execvp(argv[0], (char *const *)argv.data()) < 0) {
  75. perror(argv[0]);
  76. abort();
  77. }
  78. } else if (child < 0) {
  79. throw std::runtime_error(std::string("fork: ") + strerror(errno));
  80. }
  81. close(fds[1]);
  82. if (output == nullptr) {
  83. close(fds[0]);
  84. } else {
  85. char buf[1025];
  86. while (true) {
  87. ssize_t num = read(fds[0], buf, sizeof(buf) - 1);
  88. if (num < 0 && errno != EAGAIN) {
  89. close(fds[0]);
  90. throw std::runtime_error(std::string("read: ") + strerror(errno));
  91. } else if (num < 0) {
  92. continue;
  93. }
  94. if (num == 0) {
  95. close(fds[1]);
  96. break;
  97. }
  98. buf[num] = '\0';
  99. *output += buf;
  100. }
  101. }
  102. int wstatus;
  103. waitpid(child, &wstatus, 0);
  104. if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) != 0) {
  105. throw std::runtime_error(std::string(args[0]) +
  106. " exited with code " + std::to_string(WEXITSTATUS(wstatus)));
  107. }
  108. if (WTERMSIG(wstatus)) {
  109. throw std::runtime_error(std::string(args[0]) +
  110. " terminated due to " + strsignal(WTERMSIG(wstatus)));
  111. }
  112. if (output != nullptr && output->back() == '\n') {
  113. output->pop_back();
  114. }
  115. }
  116. }