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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  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 void printCompileCommands(
  136. const std::vector<SourceFile> &sources, const std::string &outDir,
  137. std::ostream &os) {
  138. std::string cwd = sys::cwd();
  139. os << "[\n\t";
  140. bool first = true;
  141. for (auto &source: sources) {
  142. std::string command;
  143. for (auto arg: source.compileCommand(outDir)) {
  144. command += arg + ' ';
  145. }
  146. command.pop_back();
  147. if (!first) {
  148. os << ", ";
  149. }
  150. os << "{\n"
  151. << "\t\t\"directory\":\"" << cwd << "\"\n"
  152. << "\t\t\"command\":\"" << command << "\"\n"
  153. << "\t\t\"file\":\"" << source.path() << "\"\n"
  154. << "\t}";
  155. first = false;
  156. }
  157. os << "\n]\n";
  158. }
  159. static std::string findTargetName(
  160. const BBParser::Variables &vars,
  161. const std::vector<SourceFile> &sources) {
  162. auto it = vars.find("target");
  163. if (it != vars.end() && it->second.size() != 0) {
  164. return it->second[0];
  165. }
  166. for (auto &source: sources) {
  167. const std::vector<std::string> *ptr = source.variable("target");
  168. if (ptr != nullptr && ptr->size() != 0) {
  169. return (*ptr)[0];
  170. }
  171. }
  172. return "target";
  173. }
  174. int main(int argc, char **argv) {
  175. logger::LogContext logCtx = logger::init();
  176. std::string outDir = "bbbuild";
  177. int jobs = parallel::coreCount();
  178. std::string workDir = "";
  179. std::string target = "";
  180. enum class Action {
  181. BUILD, PRINT_COMPILE_COMMANDS, PRINT_STATE,
  182. };
  183. Action action = Action::BUILD;
  184. const char *shortopts = "hvo:j:C:cp";
  185. const struct option opts[] = {
  186. { "help", no_argument, NULL, 'h' },
  187. { "verbose", no_argument, NULL, 'v' },
  188. { "output", required_argument, NULL, 'o' },
  189. { "jobs", required_argument, NULL, 'j' },
  190. { "directory", required_argument, NULL, 'C' },
  191. { "target", required_argument, NULL, 't' },
  192. { "compile-commands", no_argument, NULL, 'c' },
  193. { "print-state", no_argument, NULL, 'p' },
  194. {},
  195. };
  196. const char usage[] =
  197. "Usage: bbbuild [options...] [sources]\n"
  198. "\n"
  199. " -h, --help "
  200. "Show this help text.\n"
  201. " -v, --verbose "
  202. "Show every command as it's executing.\n"
  203. " -o, --output <dir> "
  204. "Set output directory. Default: bbbuild\n"
  205. " -j, --jobs <count> "
  206. "Set the number of jobs run simultaneously. "
  207. "Default: the number of cores in the machine.\n"
  208. " -C, --directory <dir> "
  209. "Change directory before doing anything else.\n"
  210. " -c, --compile-commands "
  211. "Write compile commands to stdout.\n"
  212. " -p, --print-state "
  213. "Print the state instead of building.\n";
  214. // Parse options from argv
  215. while (1) {
  216. int optidx;
  217. int c = getopt_long(argc, argv, shortopts, opts, &optidx);
  218. if (c < 0) {
  219. break;
  220. }
  221. switch (c) {
  222. case 'h':
  223. puts(usage);
  224. return 0;
  225. case 'v':
  226. global::verbose += 1;
  227. break;
  228. case 'o':
  229. outDir = optarg;
  230. break;
  231. case 'j':
  232. jobs = atoi(optarg);
  233. if (jobs <= 0) {
  234. fprintf(stderr, "Can't run %i jobs.\n", jobs);
  235. return 1;
  236. }
  237. break;
  238. case 'C':
  239. workDir = optarg;
  240. break;
  241. case 'c':
  242. action = Action::PRINT_COMPILE_COMMANDS;
  243. break;
  244. case 'p':
  245. action = Action::PRINT_STATE;
  246. break;
  247. default:
  248. printf("Unknown option: '%c'.\n%s", (char)c, usage);
  249. return 1;
  250. }
  251. }
  252. // Change directory?
  253. if (workDir.size() > 0) {
  254. fprintf(stderr, "Entering directory '%s'\n", workDir.c_str());
  255. sys::chdir(workDir);
  256. }
  257. // Read config from file
  258. BBParser::Variables variables;
  259. if (sys::fileExists("build.bb")) {
  260. std::ifstream stream("build.bb");
  261. BBParser parser(stream, BBParser::FLAG_NONE);
  262. parser.parse(variables);
  263. }
  264. // Read variables from argv
  265. while (optind < argc) {
  266. std::istringstream stream(argv[optind++]);
  267. BBParser parser(stream, BBParser::FLAG_ONE_LINE);
  268. parser.parse(variables);
  269. }
  270. // Find source dirs
  271. std::vector<std::string> sourceDirs;
  272. {
  273. auto it = variables.find("files");
  274. if (it == variables.end()) {
  275. sys::readDir(".", sourceDirs);
  276. } else {
  277. sourceDirs = it->second;
  278. }
  279. }
  280. // Read configs from source dirs
  281. std::vector<SourceFile> sources;
  282. for (std::string &dir: sourceDirs) {
  283. findSources(".", dir, sources, variables);
  284. }
  285. // Find target name
  286. if (target.size() == 0) {
  287. target = findTargetName(variables, sources);
  288. }
  289. switch (action) {
  290. case Action::BUILD:
  291. // TODO: Support more types than BINARY
  292. if (!compileAndLink(sources, outDir, jobs, target, toolchain::TargetType::BINARY)) {
  293. logger::log("Nothing to do.");
  294. }
  295. break;
  296. case Action::PRINT_COMPILE_COMMANDS:
  297. printCompileCommands(sources, outDir, std::cout);
  298. break;
  299. case Action::PRINT_STATE:
  300. printState(sources);
  301. break;
  302. }
  303. }