Build tool
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

sys.cc 5.0KB

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