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 5.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. #include "sys.h"
  2. #include <libgen.h>
  3. #include <sys/types.h>
  4. #include <sys/stat.h>
  5. #include <unistd.h>
  6. #include <dirent.h>
  7. #include <string.h>
  8. #include <errno.h>
  9. #include <sys/types.h>
  10. #include <sys/wait.h>
  11. #include <stdexcept>
  12. #include <system_error>
  13. #include <unordered_set>
  14. #include "logger.h"
  15. #include "globals.h"
  16. namespace sys {
  17. FileInfo fileInfo(const std::string &path) {
  18. FileInfo finfo;
  19. struct stat st;
  20. if (stat(path.c_str(), &st) < 0) {
  21. throw std::runtime_error("stat '" + path + "': " + strerror(errno));
  22. }
  23. finfo.mTimeSec = st.st_mtim.tv_sec;
  24. finfo.mTimeNsec = st.st_mtim.tv_nsec;
  25. finfo.isDir = S_ISDIR(st.st_mode);
  26. return finfo;
  27. }
  28. bool fileExists(const std::string &path) {
  29. struct stat st;
  30. if (stat(path.c_str(), &st) < 0) {
  31. if (errno == ENOENT) {
  32. return false;
  33. } else {
  34. throw std::runtime_error("stat '" + path + "': " + strerror(errno));
  35. }
  36. }
  37. return true;
  38. }
  39. void mkdirp(const std::string &path) {
  40. static thread_local std::unordered_set<std::string> set;
  41. auto res = set.emplace(path);
  42. if (!res.second)
  43. return;
  44. // TODO: Implement this in C++ instead
  45. std::vector<std::string> argv;
  46. argv.push_back("mkdir");
  47. argv.push_back("-p");
  48. argv.push_back("--");
  49. argv.push_back(path);
  50. execute(argv, nullptr, global::verbose >= 2);
  51. }
  52. void rmrf(const std::string &path) {
  53. // TODO: Implement this in C++ instead
  54. std::vector<std::string> argv;
  55. argv.push_back("rm");
  56. argv.push_back("-rf");
  57. argv.push_back("--");
  58. argv.push_back(path);
  59. execute(argv, nullptr, global::verbose >= 2);
  60. }
  61. void execute(const std::vector<std::string> &args, std::string *output, bool print) {
  62. if (print) {
  63. std::string str;
  64. for (size_t i = 0; i < args.size(); ++i) {
  65. if (i != 0) {
  66. str += " ";
  67. }
  68. str += args[i];
  69. }
  70. logger::log(str);
  71. }
  72. // argv[0] should be interpreted as a shell command, because being able to run
  73. // CC='gcc --sysroot=/blah' is used by some systems.
  74. std::string command = std::string(args[0]) + " \"$@\"";
  75. static thread_local std::vector<const char *> argv;
  76. argv.clear();
  77. argv.push_back("/bin/sh"); // TODO: Use $SHELL?
  78. argv.push_back("-c");
  79. argv.push_back(command.c_str());
  80. argv.push_back("--");
  81. for (size_t i = 1; i < args.size(); ++i) {
  82. argv.push_back(args[i].c_str());
  83. }
  84. argv.push_back(nullptr);
  85. int fds[2];
  86. if (pipe(fds) < 0) {
  87. throw std::runtime_error(std::string("fork: ") + strerror(errno));
  88. }
  89. pid_t child = fork();
  90. if (child == 0) {
  91. close(fds[0]);
  92. dup2(fds[1], 1);
  93. // So, from what I've read, execvp should never modify its argv; so this should be fine?
  94. if (execvp(argv[0], (char *const *)argv.data()) < 0) {
  95. perror(argv[0]);
  96. abort();
  97. }
  98. // This shouldn't happen
  99. exit(0);
  100. } else if (child < 0) {
  101. throw std::runtime_error(std::string("fork: ") + strerror(errno));
  102. }
  103. close(fds[1]);
  104. if (output == nullptr) {
  105. close(fds[0]);
  106. } else {
  107. char buf[1025];
  108. while (true) {
  109. ssize_t num = read(fds[0], buf, sizeof(buf) - 1);
  110. if (num < 0 && errno != EAGAIN) {
  111. close(fds[0]);
  112. throw std::runtime_error(
  113. std::string("read: ") + strerror(errno) +
  114. " (fd: " + std::to_string(fds[0]) + ")");
  115. } else if (num < 0) {
  116. continue;
  117. }
  118. if (num == 0) {
  119. close(fds[0]);
  120. break;
  121. }
  122. buf[num] = '\0';
  123. *output += buf;
  124. }
  125. }
  126. int wstatus;
  127. waitpid(child, &wstatus, 0);
  128. if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) != 0) {
  129. throw std::runtime_error(std::string(args[0]) +
  130. " exited with code " + std::to_string(WEXITSTATUS(wstatus)));
  131. }
  132. if (WTERMSIG(wstatus)) {
  133. throw std::runtime_error(std::string(args[0]) +
  134. " terminated due to " + strsignal(WTERMSIG(wstatus)));
  135. }
  136. if (output != nullptr && output->back() == '\n') {
  137. output->pop_back();
  138. }
  139. }
  140. void readDir(const std::string &path, std::vector<std::string> &files) {
  141. DIR *d = opendir(path.c_str());
  142. if (d == NULL) {
  143. throw std::runtime_error("opendir '" + path + "': " + strerror(errno));
  144. }
  145. struct dirent *ent;
  146. while ((ent = readdir(d)) != NULL) {
  147. if (ent->d_name[0] == '.')
  148. continue;
  149. files.emplace_back(ent->d_name);
  150. }
  151. closedir(d);
  152. }
  153. void chdir(const std::string &path) {
  154. if (::chdir(path.c_str()) < 0) {
  155. throw std::runtime_error("chdir '" + path + "': " + strerror(errno));
  156. }
  157. }
  158. std::string cwd() {
  159. std::vector<char> buf;
  160. buf.reserve(128);
  161. while (1) {
  162. char *res = getcwd(buf.data(), buf.capacity());
  163. if (res == NULL) {
  164. if (errno == ERANGE) {
  165. buf.reserve(buf.capacity() * 2);
  166. } else {
  167. throw std::system_error(EFAULT, std::generic_category());
  168. }
  169. } else {
  170. std::string str(buf.data());
  171. return str;
  172. }
  173. }
  174. }
  175. void symlink(const std::string &from, const std::string &to) {
  176. // TODO: Implement this in C++ instead
  177. std::vector<std::string> argv;
  178. argv.push_back("ln");
  179. argv.push_back("-s");
  180. argv.push_back("-f");
  181. argv.push_back("--");
  182. argv.push_back(from);
  183. argv.push_back(to);
  184. sys::execute(argv, nullptr, global::verbose > 2);
  185. }
  186. std::string dirname(const std::string &path) {
  187. std::vector<char> buf(path.begin(), path.end());
  188. buf.push_back('\0');
  189. char *dir = ::dirname(buf.data());
  190. return std::string(dir);
  191. }
  192. std::ofstream ofstream(const std::string &path) {
  193. std::ofstream f;
  194. f.exceptions(std::ofstream::badbit);
  195. f.open(path);
  196. if (!f.good()) {
  197. throw std::system_error(errno, std::generic_category(), path);
  198. }
  199. return f;
  200. }
  201. std::ifstream ifstream(const std::string &path) {
  202. std::ifstream f;
  203. f.exceptions(std::ifstream::badbit);
  204. f.open(path);
  205. if (!f.good()) {
  206. throw std::system_error(errno, std::generic_category(), path);
  207. }
  208. return f;
  209. }
  210. }