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.

main.cc 7.7KB

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