Build tool
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. #include <unistd.h>
  2. #include <getopt.h>
  3. #include <dirent.h>
  4. #include <stdexcept>
  5. #include <string.h>
  6. #include <errno.h>
  7. #include <fstream>
  8. #include <iostream>
  9. #include <unordered_set>
  10. #include <mutex>
  11. #include "SourceFile.h"
  12. #include "BBBParser.h"
  13. #include "parallel.h"
  14. #include "toolchain.h"
  15. #include "globals.h"
  16. static void readDir(std::string dir, std::vector<SourceFile> &sources,
  17. BBBParser::Variables vars);
  18. static void readPath(std::string dir, std::string name,
  19. std::vector<SourceFile> &sources, BBBParser::Variables vars) {
  20. std::string path = dir + "/" + name;
  21. sys::FileInfo finfo = sys::fileInfo(path);
  22. if (finfo.isDir) {
  23. // We don't want to send 'files'
  24. BBBParser::Variables subvars = vars;
  25. subvars.erase("files");
  26. readDir(path, sources, std::move(subvars));
  27. } else {
  28. SourceFile::FileType type = SourceFile::fileTypeFrom(name);
  29. if (type != SourceFile::FileType::UNKNOWN) {
  30. sources.emplace_back(dir, name, type, vars);
  31. }
  32. }
  33. }
  34. static void readDir(std::string dir, std::vector<SourceFile> &sources,
  35. BBBParser::Variables vars) {
  36. std::ifstream buildFile(dir + "/build.bbb");
  37. std::vector<std::string> files;
  38. bool hasFiles = false;
  39. // Parse $dir/build.bbb, see if it specifies a 'files' value.
  40. if (buildFile.good()) {
  41. BBBParser parser(buildFile, BBBParser::FLAG_NONE);
  42. parser.parse(vars);
  43. auto it = vars.find("files");
  44. if (it != vars.end()) {
  45. files = it->second;
  46. hasFiles = true;
  47. }
  48. }
  49. // If build.bbb didn't specify 'files', we have to readdir
  50. if (!hasFiles) {
  51. DIR *d = opendir(dir.c_str());
  52. if (d == NULL) {
  53. throw std::runtime_error("opendir '" + dir + "': " + strerror(errno));
  54. return;
  55. }
  56. struct dirent *ent;
  57. while ((ent = readdir(d)) != NULL) {
  58. if (ent->d_name[0] == '.')
  59. continue;
  60. files.emplace_back(ent->d_name);
  61. }
  62. closedir(d);
  63. }
  64. // Go through files
  65. for (auto &ent: files) {
  66. std::string path = dir + "/" + ent;
  67. BBBParser::Variables subvars = vars;
  68. readPath(dir, ent, sources, vars);
  69. }
  70. }
  71. static void printState(const std::vector<SourceFile> &sources) {
  72. for (auto &source: sources) {
  73. std::cout << source.dir() << '/' << source.name() << ":\n";
  74. for (auto &kv: source.vars()) {
  75. std::cout << " " << kv.first << ":\n";
  76. for (auto &val: kv.second) {
  77. std::cout << " " << val << '\n';
  78. }
  79. }
  80. }
  81. }
  82. static bool compile(const std::vector<SourceFile> &sources, const std::string &outDir, int jobs) {
  83. bool compiled = false;
  84. std::mutex mut;
  85. parallel::parallel(jobs, sources, [&](const SourceFile &source) {
  86. if (source.needsRecompile(outDir)) {
  87. source.compile(outDir);
  88. mut.lock();
  89. compiled = true;
  90. mut.unlock();
  91. }
  92. });
  93. return compiled;
  94. }
  95. static void link(
  96. const std::vector<SourceFile> &sources,
  97. const std::string &outDir, const std::string &name,
  98. toolchain::TargetType targetType) {
  99. std::cerr << "Link " << outDir << '/' << name << '\n';
  100. std::vector<std::string> ldFlags;
  101. std::unordered_set<std::string> ldFlagsSet;
  102. std::vector<std::string> ldLibs;
  103. std::unordered_set<std::string> ldLibsSet;
  104. std::vector<std::string> objects;
  105. SourceFile::FileType type = SourceFile::FileType::C;
  106. // Gather ldflags, ldlibs and objects from sources
  107. std::vector<std::string> flags;
  108. for (auto &source: sources) {
  109. if (source.type() != SourceFile::FileType::C) {
  110. type = source.type();
  111. }
  112. flags = source.ldFlags();
  113. for (auto &flag: flags) {
  114. if (flag[0] == '-' && flag[1] == 'l') {
  115. std::cerr << "Warning: -l flags should go in ldlibs "
  116. << "(at " << source.path() << ": " << flag << ")\n";
  117. }
  118. auto it = ldFlagsSet.find(flag);
  119. if (it == ldFlagsSet.end()) {
  120. ldFlagsSet.emplace(flag);
  121. ldFlags.push_back(std::move(flag));
  122. }
  123. }
  124. flags = source.ldLibs();
  125. for (auto &flag: flags) {
  126. auto it = ldLibsSet.find(flag);
  127. if (it == ldLibsSet.end()) {
  128. ldLibsSet.emplace(flag);
  129. ldLibs.push_back(std::move(flag));
  130. }
  131. }
  132. objects.push_back(std::move(source.objectPath(outDir)));
  133. }
  134. flags.clear();
  135. toolchain::link(name, ldFlags, ldLibs, type, targetType, objects, outDir);
  136. }
  137. static bool compileAndLink(
  138. const std::vector<SourceFile> &sources, const std::string &outDir,
  139. int jobs, const std::string &target, toolchain::TargetType targetType) {
  140. std::string targetPath = toolchain::targetFilePath(targetType, target, outDir);
  141. if (compile(sources, outDir, jobs) || !sys::fileExists(targetPath)) {
  142. link(sources, outDir, target, targetType);
  143. return true;
  144. }
  145. return false;
  146. }
  147. int main(int argc, char **argv) {
  148. std::vector<std::string> srcDirs;
  149. std::string outDir = "bbbuild";
  150. int jobs = parallel::coreCount();
  151. std::string workDir = "";
  152. std::string target = "";
  153. enum class Action {
  154. BUILD, PRINT_STATE,
  155. };
  156. Action action = Action::BUILD;
  157. const char *shortopts = "hvo:j:C:t:p";
  158. const struct option opts[] = {
  159. { "help", no_argument, NULL, 'h' },
  160. { "verbose", no_argument, NULL, 'v' },
  161. { "output", required_argument, NULL, 'o' },
  162. { "jobs", required_argument, NULL, 'j' },
  163. { "directory", required_argument, NULL, 'C' },
  164. { "target", required_argument, NULL, 't' },
  165. { "print-state", no_argument, NULL, 'p' },
  166. {},
  167. };
  168. const char usage[] =
  169. "Usage: bbbuild [options...] [sources]\n"
  170. "\n"
  171. " -h, --help "
  172. "Show this help text.\n"
  173. " -v, --verbose "
  174. "Show every command as it's executing."
  175. " -o, --output <dir> "
  176. "Set output directory. Default: bbbuild\n"
  177. " -j, --jobs <count> "
  178. "Set the number of jobs run simultaneously. "
  179. "Default: the number of cores in the machine.\n"
  180. " -C, --directory <dir> "
  181. "Change directory before doing anything else.\n"
  182. " -t, --target <name> "
  183. "Set the name of the executable.\n"
  184. " -p, --print-state "
  185. "Print the state instead of building.\n";
  186. // Parse options from argv
  187. while (1) {
  188. int optidx;
  189. int c = getopt_long(argc, argv, shortopts, opts, &optidx);
  190. if (c < 0) {
  191. break;
  192. }
  193. switch (c) {
  194. case 'h':
  195. puts(usage);
  196. return 0;
  197. case 'v':
  198. global::verbose = true;
  199. break;
  200. case 'o':
  201. outDir = optarg;
  202. break;
  203. case 'j':
  204. jobs = atoi(optarg);
  205. if (jobs <= 0) {
  206. fprintf(stderr, "Can't run %i jobs.\n", jobs);
  207. return 1;
  208. }
  209. break;
  210. case 'C':
  211. workDir = optarg;
  212. break;
  213. case 't':
  214. target = optarg;
  215. break;
  216. case 'p':
  217. action = Action::PRINT_STATE;
  218. break;
  219. default:
  220. printf("Unknown option: '%c'.\n%s", (char)c, usage);
  221. return 1;
  222. }
  223. }
  224. // Change directory?
  225. if (workDir.size() > 0) {
  226. if (global::verbose) {
  227. fprintf(stderr, "Entering directory '%s'\n", workDir.c_str());
  228. }
  229. if (chdir(workDir.c_str()) < 0) {
  230. perror(workDir.c_str());
  231. return 1;
  232. }
  233. }
  234. // TODO: Read this from build.bbb
  235. if (target.size() == 0) {
  236. target = "target";
  237. }
  238. // Parse non-opt argv as source dirs
  239. if (optind < argc) {
  240. while (optind < argc) {
  241. srcDirs.push_back(argv[optind++]);
  242. }
  243. } else {
  244. srcDirs.push_back(".");
  245. }
  246. // Read configs from source dirs
  247. std::vector<SourceFile> sources;
  248. for (std::string &dir: srcDirs) {
  249. readDir(dir, sources, {});
  250. }
  251. switch (action) {
  252. case Action::BUILD:
  253. // TODO: Support more types than BINARY
  254. if (!compileAndLink(sources, outDir, jobs, target, toolchain::TargetType::BINARY)) {
  255. std::cerr << "Nothing to do.\n";
  256. }
  257. break;
  258. case Action::PRINT_STATE:
  259. printState(sources);
  260. break;
  261. }
  262. }