1
0

mingen.ck 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455
  1. /*++
  2. Copyright (c) 2017 Minoca Corp.
  3. This file is licensed under the terms of the GNU General Public License
  4. version 3. Alternative licensing terms are available. Contact
  5. info@minocacorp.com for details. See the LICENSE file at the root of this
  6. project for complete licensing information.
  7. Module Name:
  8. mingen.ck
  9. Abstract:
  10. This module implements the Minoca build generator application, which can
  11. transform a build specification into a Makefile or Ninja file.
  12. Author:
  13. Evan Green 30-Jan-2017
  14. Environment:
  15. Chalk
  16. --*/
  17. //
  18. // ------------------------------------------------------------------- Includes
  19. //
  20. from app import argv;
  21. from make import buildMakefile;
  22. from ninja import buildNinja;
  23. from getopt import gnuGetopt;
  24. import os;
  25. from os import getcwd;
  26. //
  27. // --------------------------------------------------------------------- Macros
  28. //
  29. //
  30. // ---------------------------------------------------------------- Definitions
  31. //
  32. var VERSION_MAJOR = 2;
  33. var VERSION_MINOR = 0;
  34. //
  35. // ------------------------------------------------------ Data Type Definitions
  36. //
  37. //
  38. // ----------------------------------------------- Internal Function Prototypes
  39. //
  40. function
  41. _loadProjectRoot (
  42. );
  43. function
  44. _processEntries (
  45. );
  46. function
  47. _selectTargets (
  48. );
  49. function
  50. _loadModule (
  51. name
  52. );
  53. function
  54. _validateEntries (
  55. moduleName,
  56. entries
  57. );
  58. function
  59. _validateTargetEntry (
  60. moduleName,
  61. entry
  62. );
  63. function
  64. _validateToolEntry (
  65. moduleName,
  66. entry
  67. );
  68. function
  69. _validatePoolEntry (
  70. moduleName,
  71. entry
  72. );
  73. function
  74. _processTarget (
  75. target
  76. );
  77. function
  78. _createInputsList (
  79. target,
  80. inputs
  81. );
  82. function
  83. _processInput (
  84. target,
  85. input
  86. );
  87. function
  88. _raiseModuleError (
  89. moduleName,
  90. entry,
  91. description
  92. );
  93. function
  94. _canonicalizeLabel (
  95. moduleName,
  96. label
  97. );
  98. function
  99. _isPathAbsolute (
  100. path
  101. );
  102. function
  103. _findAllTargets (
  104. module
  105. );
  106. function
  107. _markTargetActive (
  108. target
  109. );
  110. function
  111. _printAllEntries (
  112. );
  113. function
  114. _printTool (
  115. tool
  116. );
  117. function
  118. _printPool (
  119. pool
  120. );
  121. function
  122. _printTarget (
  123. target
  124. );
  125. //
  126. // -------------------------------------------------------------------- Globals
  127. //
  128. var shortOptions = "B:De:F:ghi:no:O:uvV";
  129. var longOptions = [
  130. "build-os=",
  131. "expr=",
  132. "debug",
  133. "format=",
  134. "no-generator",
  135. "input=",
  136. "dry-run",
  137. "output=",
  138. "output-file=",
  139. "help",
  140. "unanchored",
  141. "verbose",
  142. "version"
  143. ];
  144. var usage =
  145. "usage: mingen [options] [targets...]\n"
  146. "The Minoca Build Generator creates Ninja files describing the build at \n"
  147. "the current directory. If specific targets are specified, then a build \n"
  148. "file for only those targets will be built. Otherwise, the build file \n"
  149. "is created for the whole project. Options are:\n"
  150. " -B, --build-os=os,machine -- Set the build OS and build machine.\n"
  151. " -e, --expr=var=val -- Set a custom build option.\n"
  152. " This can be specified multiple times.\n"
  153. " -D, --debug -- Print lots of information during execution.\n"
  154. " -f, --format=fmt -- Specify the output format as make or ninja. The \n"
  155. " default is make.\n"
  156. " -g, --no-generator -- Don't include a re-generate rule in the output.\n"
  157. " -n, --dry-run -- Do all the processing, but do not actually create \n"
  158. " any output files.\n"
  159. " -i, --input=dir -- Sets the top level directory of the source. The \n"
  160. " default is to use the current working directory.\n"
  161. " -o, --output=build_dir -- Set the given directory as the build \n"
  162. " output directory.\n"
  163. " -O, --output-file=file -- Set the output file name.\n"
  164. " -u, --unanchored -- Leave the input and output directories blank in \n"
  165. " the final build file. They must be specified manually later.\n"
  166. " -v, --verbose -- Print more information during processing.\n"
  167. " --help -- Show this help text and exit.\n"
  168. " --version -- Print the application version information and exit.\n\n";
  169. var config = {
  170. "build_os": os.system,
  171. "build_machine": os.machine,
  172. "debug": false,
  173. "format": null,
  174. "generator": true,
  175. "input": null,
  176. "output": null,
  177. "output_file": null,
  178. "unanchored": false,
  179. "verbose": false,
  180. "build_module_name": "build",
  181. "default_target": ":",
  182. "targets": [],
  183. "input_variable": "S",
  184. "output_variable": "O",
  185. "vars": {},
  186. "cmdvars": {}
  187. };
  188. var modules = {};
  189. var targets = {};
  190. var targetsList = [];
  191. var tools = {};
  192. var pools = {};
  193. var buildDirectories = {};
  194. var scripts = {};
  195. //
  196. // ------------------------------------------------------------------ Functions
  197. //
  198. function
  199. main (
  200. )
  201. /*++
  202. Routine Description:
  203. This routine implements the application entry point for the mingen
  204. application.
  205. Arguments:
  206. None.
  207. Return Value:
  208. 0 on success.
  209. 1 on failure.
  210. --*/
  211. {
  212. var appOptions = gnuGetopt(argv[1...-1], shortOptions, longOptions);
  213. var args = appOptions[1];
  214. var currentDirectory = getcwd().replace("\\", "/", -1);
  215. var entries = {};
  216. var name;
  217. var value;
  218. //
  219. // The return from getopt is [config, remainingArgs]. Get the config now.
  220. //
  221. appOptions = appOptions[0];
  222. for (option in appOptions) {
  223. name = option[0];
  224. value = option[1];
  225. if ((name == "-B") || (name == "--build-os")) {
  226. value = value.split(",", 1);
  227. config.build_os = value[0];
  228. if (value.length() == 2) {
  229. config.build_machine = value[1];
  230. }
  231. } else if ((name == "-e") || (name == "--expr")) {
  232. value = value.split("=", 1);
  233. if (value.length() == 2) {
  234. config.cmdvars[value[0]] = value[1];
  235. } else {
  236. Core.raise(ValueError("Invalid expression '%s'" % value));
  237. }
  238. } else if ((name == "-D") || (name == "--debug")) {
  239. config.debug = true;
  240. config.verbose = true;
  241. } else if ((name == "f") || (name == "--format")) {
  242. if (!config.format) {
  243. config.format = value;
  244. if (!["make", "ninja", "none"].contains(value)) {
  245. Core.raise(ValueError("Invalid format '%s'" % value));
  246. }
  247. }
  248. } else if ((name == "-g") || (name == "--no-generator")) {
  249. config.generator = false;
  250. } else if ((name == "-n") || (name == "--dry-run")) {
  251. config.format = "none";
  252. } else if ((name == "-i") || (name == "--input")) {
  253. if (!config.input) {
  254. config.input = value;
  255. }
  256. } else if ((name == "-o") || (name == "--output")) {
  257. if (!config.output) {
  258. config.output = value;
  259. }
  260. } else if ((name == "-O") || (name == "--output-file")) {
  261. config.output_file = value;
  262. } else if ((name == "-u") || (name == "--unanchored")) {
  263. config.unanchored = true;
  264. } else if ((name == "-v") || (name == "--verbose")) {
  265. config.verbose = true;
  266. } else if ((name == "-h") || (name == "--help")) {
  267. Core.print(usage);
  268. return 1;
  269. } else if ((name == "-V") || (name == "--version")) {
  270. Core.print("mingen version %d.%d" % [VERSION_MAJOR, VERSION_MINOR]);
  271. return 1;
  272. } else {
  273. Core.raise(ValueError("Invalid option '%s'" % name));
  274. }
  275. }
  276. config.targets = args;
  277. config.argv = argv;
  278. if (!config.input) {
  279. config.input = currentDirectory;
  280. }
  281. Core.setModulePath([config.input] + Core.modulePath());
  282. if (config.debug) {
  283. Core.print("Module search path: " + Core.modulePath().__str());
  284. }
  285. _loadProjectRoot();
  286. config.format ?= "make";
  287. _processEntries();
  288. _selectTargets();
  289. if (config.verbose) {
  290. _printAllEntries();
  291. }
  292. entries.targets = targets;
  293. entries.targetsList = targetsList;
  294. entries.tools = tools;
  295. entries.pools = pools;
  296. entries.buildDirectories = buildDirectories;
  297. entries.scripts = scripts;
  298. if (config.format == "make") {
  299. buildMakefile(config, entries);
  300. } else if (config.format == "ninja") {
  301. buildNinja(config, entries);
  302. }
  303. if (config.verbose) {
  304. Core.print("Done");
  305. }
  306. return 0;
  307. }
  308. //
  309. // --------------------------------------------------------- Internal Functions
  310. //
  311. function
  312. _loadProjectRoot (
  313. )
  314. /*++
  315. Routine Description:
  316. This routine loads the project root script.
  317. Arguments:
  318. None.
  319. Return Value:
  320. None.
  321. --*/
  322. {
  323. var build;
  324. var entries;
  325. var module = Core.importModule(config.build_module_name);
  326. module.run();
  327. modules[""] = module;
  328. scripts[config.build_module_name + ".ck"] = true;
  329. if (config.debug) {
  330. Core.print("Initial Config:");
  331. for (key in config) {
  332. Core.print(" %s: %s" % [key, config[key].__str()]);
  333. }
  334. Core.print("");
  335. }
  336. build = module.build;
  337. entries = build();
  338. _validateEntries("", entries);
  339. if (!config.output) {
  340. config.output = config.input;
  341. }
  342. return;
  343. }
  344. function
  345. _processEntries (
  346. )
  347. /*++
  348. Routine Description:
  349. This routine processes all target entries, adding in dependencies as needed.
  350. Arguments:
  351. None.
  352. Return Value:
  353. None.
  354. --*/
  355. {
  356. var index;
  357. var target;
  358. //
  359. // Iterate carefully as the list of targets may be growing as it's being
  360. // processed. Entries only get added to the end.
  361. //
  362. for (index = 0; index < targetsList.length(); index += 1) {
  363. target = targetsList[index];
  364. _processTarget(target);
  365. }
  366. return;
  367. }
  368. function
  369. _selectTargets (
  370. )
  371. /*++
  372. Routine Description:
  373. This routine marks the requested targets, tools, and pool active, as well
  374. as their dependencies.
  375. Arguments:
  376. None.
  377. Return Value:
  378. None.
  379. --*/
  380. {
  381. var first = true;
  382. var label;
  383. var moduleTargets;
  384. var target;
  385. for (entry in config.targets) {
  386. label = entry.rsplit(":", 1);
  387. if (label.length() == 0) {
  388. Core.raise(ValueError("Invalid target '%s' requested" % entry));
  389. }
  390. if ((label.length() == 2) && (label[1] == "")) {
  391. moduleTargets = _findAllTargets(label[0]);
  392. for (target in moduleTargets) {
  393. _markTargetActive(target);
  394. }
  395. } else {
  396. target = targets.get(entry);
  397. if (!target) {
  398. Core.raise(ValueError("Unknown target '%s' requested" % entry));
  399. }
  400. if (first) {
  401. target.default = true;
  402. first = false;
  403. }
  404. _markTargetActive(target);
  405. }
  406. }
  407. return;
  408. }
  409. function
  410. _loadModule (
  411. name
  412. )
  413. /*++
  414. Routine Description:
  415. This routine loads the given build file. If it has not yet been run, it
  416. runs it and adds the entries to the global list.
  417. Arguments:
  418. name - Supplies the name of the module to load, not including the build.ck
  419. portion.
  420. Return Value:
  421. None.
  422. --*/
  423. {
  424. var build;
  425. var entries;
  426. var fullName;
  427. var module;
  428. var scriptFile;
  429. module = modules.get(name);
  430. if (module) {
  431. return module;
  432. }
  433. fullName = name.replace("/", ".", -1) + "." + config.build_module_name;
  434. module = Core.importModule(fullName);
  435. module.run();
  436. scriptFile = fullName.replace(".", "/", -1) + ".ck";
  437. scripts[scriptFile] = true;
  438. modules[name] = module;
  439. build = module.build;
  440. entries = build();
  441. _validateEntries(name, entries);
  442. return module;
  443. }
  444. function
  445. _validateEntries (
  446. moduleName,
  447. entries
  448. )
  449. /*++
  450. Routine Description:
  451. This routine processes the entries returned from loading a new module.
  452. Arguments:
  453. moduleName - Supplies the name of the module these entries are associated
  454. with.
  455. entries - Supplies the new build entries.
  456. Return Value:
  457. None.
  458. --*/
  459. {
  460. var entryType;
  461. if (!(entries is List)) {
  462. Core.raise(TypeError("In %s: Return a list from build(), not a %s" %
  463. [moduleName,
  464. entries.type().name()]));
  465. }
  466. for (entry in entries) {
  467. if (!(entry is Dict)) {
  468. Core.raise(TypeError("In %s: Expected a dict, got a %s: %s" %
  469. [moduleName,
  470. entry.type().name(),
  471. entry.__str()]));
  472. }
  473. try {
  474. entryType = entry.type;
  475. } except KeyError {
  476. Core.print("In entry:");
  477. for (key in entry) {
  478. Core.print(" %s: %s" % [key, entry[key].__str()]);
  479. }
  480. Core.raise(ValueError("In %s: Dict must have a 'type' member" %
  481. moduleName));
  482. }
  483. if (entryType == "target") {
  484. _validateTargetEntry(moduleName, entry);
  485. } else if (entryType == "tool") {
  486. _validateToolEntry(moduleName, entry);
  487. } else if (entryType == "pool") {
  488. _validatePoolEntry(moduleName, entry);
  489. } else if (entryType != "ignore") {
  490. _raiseModuleError(moduleName,
  491. entry,
  492. "Invalid entry type '%s'" % entryType);
  493. }
  494. }
  495. if (config.verbose) {
  496. Core.print("Processed module " + moduleName);
  497. }
  498. return;
  499. }
  500. function
  501. _validateTargetEntry (
  502. moduleName,
  503. entry
  504. )
  505. /*++
  506. Routine Description:
  507. This routine validates a new target entry.
  508. Arguments:
  509. moduleName - Supplies the name of the module this entry is associated with.
  510. entry - Supplies the new target entry.
  511. Return Value:
  512. None.
  513. --*/
  514. {
  515. var label;
  516. //
  517. // Set the label or output to each other if both are not specified. At
  518. // least one of those two must be specified.
  519. //
  520. if (!entry.get("label")) {
  521. if (!entry.get("output")) {
  522. _raiseModuleError(moduleName,
  523. entry,
  524. "Either label or output must be specified");
  525. }
  526. entry.label = entry.output;
  527. } else if (!entry.get("output")) {
  528. entry.output = entry.label;
  529. }
  530. if (!entry.get("tool")) {
  531. _raiseModuleError(moduleName, entry, "'tool' must be specified");
  532. }
  533. //
  534. // Canonicalize the label and output members so they're fully specified.
  535. //
  536. entry.module = moduleName;
  537. entry.label = _canonicalizeLabel(moduleName, entry.label);
  538. if (!_isPathAbsolute(entry.output)) {
  539. if (moduleName != "") {
  540. entry.output = moduleName + "/" + entry.output;
  541. }
  542. if (entry.tool != "phony") {
  543. entry.output = ("$%s/" % config.output_variable) + entry.output;
  544. }
  545. }
  546. //
  547. // This target had better be unique.
  548. //
  549. if (targets.get(entry.label)) {
  550. _raiseModuleError(moduleName, entry, "Target label must be unique");
  551. }
  552. //
  553. // If no specific targets are requested, then all targets are active.
  554. //
  555. entry.active = false;
  556. if (config.targets.length() == 0) {
  557. entry.active = true;
  558. }
  559. if (!entry.get("config")) {
  560. entry.config = {};
  561. }
  562. if (!entry.get("inputs")) {
  563. entry.inputs = [];
  564. }
  565. if (!entry.get("implicit")) {
  566. entry.implicit = [];
  567. }
  568. if (!entry.get("orderonly")) {
  569. entry.orderonly = [];
  570. }
  571. targets[entry.label] = entry;
  572. targetsList.append(entry);
  573. return;
  574. }
  575. function
  576. _validateToolEntry (
  577. moduleName,
  578. entry
  579. )
  580. /*++
  581. Routine Description:
  582. This routine validates a new tool entry.
  583. Arguments:
  584. moduleName - Supplies the name of the module this entry is associated with.
  585. entry - Supplies the new tool entry.
  586. Return Value:
  587. None.
  588. --*/
  589. {
  590. if ((!entry.get("name")) || (!entry.get("command"))) {
  591. _raiseModuleError(moduleName,
  592. entry,
  593. "'name' and 'command' are required for tools");
  594. }
  595. if (tools.get(entry.name)) {
  596. _raiseModuleError(moduleName, entry, "Tool name must be unique");
  597. }
  598. //
  599. // If no specific targets are requested, then all tools are active.
  600. //
  601. entry.active = false;
  602. if (config.targets.length() == 0) {
  603. entry.active = true;
  604. }
  605. tools[entry.name] = entry;
  606. return;
  607. }
  608. function
  609. _validatePoolEntry (
  610. moduleName,
  611. entry
  612. )
  613. /*++
  614. Routine Description:
  615. This routine validates a new pool entry.
  616. Arguments:
  617. moduleName - Supplies the name of the module this entry is associated with.
  618. entry - Supplies the new pool entry.
  619. Return Value:
  620. None.
  621. --*/
  622. {
  623. if ((!entry.get("name")) || (!entry.get("depth"))) {
  624. _raiseModuleError(moduleName,
  625. entry,
  626. "'name' and 'depth' are required for pools");
  627. }
  628. if (pools.get(entry.name)) {
  629. _raiseModuleError(moduleName, entry, "Pool name must be unique");
  630. }
  631. //
  632. // If no specific targets are requested, then all tools are active.
  633. //
  634. entry.active = false;
  635. if (config.targets.length() == 0) {
  636. entry.active = true;
  637. }
  638. pools[entry.name] = entry;
  639. return;
  640. }
  641. function
  642. _processTarget (
  643. target
  644. )
  645. /*++
  646. Routine Description:
  647. This routine processes a target entry.
  648. Arguments:
  649. target - Supplies the target to process.
  650. Return Value:
  651. None.
  652. --*/
  653. {
  654. var directory;
  655. if (config.debug) {
  656. Core.print("Processing %s" % target.label);
  657. }
  658. //
  659. // Add the target as a build directory if no selective targets are
  660. // requested.
  661. //
  662. if (config.targets.length() == 0) {
  663. if ((!target.get("tool")) || (target.tool != "phony")) {
  664. directory = target.output.rsplit("/", 1)[0];
  665. buildDirectories[directory] = true;
  666. }
  667. }
  668. //
  669. // If this is the default entry, set it as such.
  670. //
  671. if (target.get("default")) {
  672. config.default_target = target.label;
  673. }
  674. //
  675. // Convert the inputs to an array of either sources or other targets.
  676. //
  677. target.inputs = _createInputsList(target, target.inputs);
  678. target.implicit = _createInputsList(target, target.implicit);
  679. target.orderonly = _createInputsList(target, target.orderonly);
  680. return;
  681. }
  682. function
  683. _createInputsList (
  684. target,
  685. inputs
  686. )
  687. /*++
  688. Routine Description:
  689. This routine creates a list that is a combination of sources and other
  690. targets.
  691. Arguments:
  692. target - Supplies the target being processed.
  693. inputs - Supplies the raw list of input strings.
  694. Return Value:
  695. Returns the resulting list, where references to other targets will be
  696. replaced with those targets.
  697. --*/
  698. {
  699. var element;
  700. var result = [];
  701. for (input in inputs) {
  702. element = _processInput(target, input);
  703. if (element is List) {
  704. result += element;
  705. } else {
  706. result.append(element);
  707. }
  708. }
  709. return result;
  710. }
  711. function
  712. _processInput (
  713. target,
  714. input
  715. )
  716. /*++
  717. Routine Description:
  718. This routine processes an input for a target.
  719. Arguments:
  720. target - Supplies the target being processed.
  721. input - Supplies the input string, which is either a direct source file or
  722. a reference to another target by label
  723. Return Value:
  724. Returns the input string if it is a raw source.
  725. Returns a target or a list of targets if the given input refers to another
  726. output.
  727. --*/
  728. {
  729. var inputTarget;
  730. var label;
  731. var module;
  732. //
  733. // If there's no colon, it's a direct file path.
  734. //
  735. if (input.indexOf(":") < 0) {
  736. if (_isPathAbsolute(input)) {
  737. return input;
  738. }
  739. return ("$%s/" % config.input_variable) + target.module + "/" + input;
  740. }
  741. input = _canonicalizeLabel(target.module, input);
  742. inputTarget = targets.get(input);
  743. if (!inputTarget) {
  744. module = input.rsplit(":", 1);
  745. label = module[1];
  746. module = module[0];
  747. try {
  748. _loadModule(module);
  749. } except ImportError as e {
  750. _raiseModuleError(target.module,
  751. target,
  752. "Failed to import '%s'" % module);
  753. }
  754. if (label == "") {
  755. inputTarget = _findAllTargets(module);
  756. } else {
  757. inputTarget = targets.get(input);
  758. if (!inputTarget) {
  759. _raiseModuleError(target.module,
  760. target,
  761. "Failed to find input '%s'" % input);
  762. }
  763. }
  764. }
  765. return inputTarget;
  766. }
  767. function
  768. _raiseModuleError (
  769. moduleName,
  770. entry,
  771. description
  772. )
  773. /*++
  774. Routine Description:
  775. This routine raises an error with one of the module values.
  776. Arguments:
  777. moduleName - Supplies the name of the module this entry is associated with.
  778. entry - Supplies the new target entry.
  779. Return Value:
  780. None.
  781. --*/
  782. {
  783. var name = entry.get("label");
  784. if (moduleName == "") {
  785. moduleName = "<root>";
  786. }
  787. if (!name) {
  788. name = entry.get("output");
  789. if (!name) {
  790. name = "<unknown>";
  791. }
  792. }
  793. description = "%s: %s: %s" % [moduleName, name, description];
  794. Core.raise(ValueError(description));
  795. return;
  796. }
  797. function
  798. _canonicalizeLabel (
  799. moduleName,
  800. label
  801. )
  802. /*++
  803. Routine Description:
  804. This routine canonicalizes a label into its full form: <path>:<label>.
  805. Arguments:
  806. moduleName - Supplies the name of the module the label is defined in.
  807. label - Supplies the initial label string.
  808. Return Value:
  809. Returns the canonical label form.
  810. --*/
  811. {
  812. if (label.indexOf(":") < 0) {
  813. label = ":" + label;
  814. }
  815. if (label[0] == ":") {
  816. return moduleName + label;
  817. }
  818. return label;
  819. }
  820. function
  821. _isPathAbsolute (
  822. path
  823. )
  824. /*++
  825. Routine Description:
  826. This routine determines if the given path is fully specified or relative to
  827. a directory.
  828. Arguments:
  829. path - Supplies the path to test.
  830. Return Value:
  831. Returns true if the path is absolute.
  832. Returns false if the path is relative.
  833. --*/
  834. {
  835. if ((path.startsWith("/")) ||
  836. (path.startsWith("$%s" % config.input_variable)) ||
  837. (path.startsWith("${%s}" % config.input_variable)) ||
  838. (path.startsWith("$%s" % config.output_variable)) ||
  839. (path.startsWith("${%s}" % config.output_variable))) {
  840. return true;
  841. }
  842. return false;
  843. }
  844. function
  845. _findAllTargets (
  846. module
  847. )
  848. /*++
  849. Routine Description:
  850. This routine finds all targets in the given module.
  851. Arguments:
  852. module - Supplies the module name.
  853. Return Value:
  854. Returns a list of all labels in the given module.
  855. --*/
  856. {
  857. var result = [];
  858. module = module + ":";
  859. for (key in targets) {
  860. if (key.startsWith(module)) {
  861. result.append(targets[key]);
  862. }
  863. }
  864. return result;
  865. }
  866. function
  867. _markTargetActive (
  868. target
  869. )
  870. /*++
  871. Routine Description:
  872. This routine marks the given target and all of its dependencies as active.
  873. Arguments:
  874. target - Supplies the target being marked active.
  875. Return Value:
  876. None.
  877. --*/
  878. {
  879. var directory;
  880. var inputs;
  881. var pool;
  882. var tool;
  883. if (target.active) {
  884. return;
  885. }
  886. target.active = true;
  887. tool = target.get("tool");
  888. if ((tool) && (tool != "phony")) {
  889. tools[tool].active = true;
  890. }
  891. pool = target.get("pool");
  892. if (pool) {
  893. pools[pool].active = true;
  894. }
  895. //
  896. // Add the target to the list of build directories.
  897. //
  898. if ((!target.get("tool")) || (target.tool != "phony")) {
  899. directory = target.output.rsplit("/", 1)[0];
  900. buildDirectories[directory] = true;
  901. }
  902. //
  903. // Recursively mark all the input targets as active as well.
  904. //
  905. inputs = target.inputs;
  906. for (input in inputs) {
  907. if (input is Dict) {
  908. _markTargetActive(input);
  909. }
  910. }
  911. inputs = target.implicit;
  912. for (input in inputs) {
  913. if (input is Dict) {
  914. _markTargetActive(input);
  915. }
  916. }
  917. inputs = target.orderonly;
  918. for (input in inputs) {
  919. if (input is Dict) {
  920. _markTargetActive(input);
  921. }
  922. }
  923. return;
  924. }
  925. function
  926. _printAllEntries (
  927. )
  928. /*++
  929. Routine Description:
  930. This routine prints out all targets, tools, and pools for verbose mode.
  931. Arguments:
  932. None.
  933. Return Value:
  934. None.
  935. --*/
  936. {
  937. for (key in tools) {
  938. _printTool(tools[key]);
  939. }
  940. for (key in pools) {
  941. _printPool(pools[key]);
  942. }
  943. for (key in targets) {
  944. _printTarget(targets[key]);
  945. }
  946. Core.print("Final Config:");
  947. for (key in config) {
  948. Core.print(" %s: %s" % [key, config[key].__str()]);
  949. }
  950. Core.print("");
  951. return;
  952. }
  953. function
  954. _printTool (
  955. tool
  956. )
  957. /*++
  958. Routine Description:
  959. This routine prints out the given tool.
  960. Arguments:
  961. tool - Supplies the tool to print.
  962. Return Value:
  963. None.
  964. --*/
  965. {
  966. var depFile = tool.get("depfile");
  967. var depsFormat = tool.get("depsformat");
  968. var description = tool.get("description");
  969. description ?= "";
  970. Core.print("Tool '%s': '%s'" % [tool.name, description]);
  971. Core.print("\t%s" % tool.command);
  972. if (depFile || depsFormat) {
  973. Core.print("\tDepfile: %s DepsFormat: %s" % [depFile, depsFormat]);
  974. }
  975. Core.print("");
  976. return;
  977. }
  978. function
  979. _printPool (
  980. pool
  981. )
  982. /*++
  983. Routine Description:
  984. This routine prints out the given pool.
  985. Arguments:
  986. pool - Supplies the pool to print.
  987. Return Value:
  988. None.
  989. --*/
  990. {
  991. Core.print("Pool %s Depth: %d" % [pool.name, pool.depth]);
  992. return;
  993. }
  994. function
  995. _printTarget (
  996. target
  997. )
  998. /*++
  999. Routine Description:
  1000. This routine prints out the given target.
  1001. Arguments:
  1002. target - Supplies the target to print.
  1003. Return Value:
  1004. None.
  1005. --*/
  1006. {
  1007. var config;
  1008. var inputs;
  1009. var label;
  1010. label = target.label.rsplit(":", 1)[1];
  1011. Core.print("%s (%s): %s" % [target.output, label, target.tool]);
  1012. inputs = target.inputs;
  1013. for (input in inputs) {
  1014. if (input is Dict) {
  1015. input = input.label;
  1016. }
  1017. Core.print("\t" + input);
  1018. }
  1019. inputs = target.implicit;
  1020. for (input in inputs) {
  1021. if (input is Dict) {
  1022. input = input.label;
  1023. }
  1024. Core.print("\t| " + input);
  1025. }
  1026. inputs = target.orderonly;
  1027. for (input in inputs) {
  1028. if (input is Dict) {
  1029. input = input.label;
  1030. }
  1031. Core.print("\t|| " + input);
  1032. }
  1033. config = target.config;
  1034. if (config.length()) {
  1035. for (key in config) {
  1036. Core.print("\t\t%s: %s" % [key, config[key]]);
  1037. }
  1038. }
  1039. Core.print("");
  1040. return;
  1041. }