hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

deps.ha (4372B)


      1 // SPDX-License-Identifier: GPL-3.0-only
      2 // (c) Hare authors <https://harelang.org>
      3 
      4 use fmt;
      5 use getopt;
      6 use hare::ast;
      7 use hare::module;
      8 use hare::parse;
      9 use os;
     10 use path;
     11 use sort;
     12 use sort::cmp;
     13 
     14 type deps_fmt = enum {
     15 	DOT,
     16 	TERM,
     17 	TEXT,
     18 };
     19 
     20 type link = struct {
     21 	depth: uint,
     22 	child: size,
     23 	final: bool,
     24 };
     25 
     26 fn deps(name: str, cmd: *getopt::command) (void | error) = {
     27 	let tags = default_tags();
     28 	defer free(tags);
     29 
     30 	let build_dir: str = "";
     31 	let goal = deps_fmt::TERM;
     32 	let recursive = true;
     33 	let submodules = false;
     34 
     35 	for (let opt .. cmd.opts) {
     36 		switch (opt.0) {
     37 		case 'D' =>
     38 			recursive = false;
     39 		case 'd' =>
     40 			goal = deps_fmt::DOT;
     41 		case 'T' =>
     42 			merge_tags(&tags, opt.1)?;
     43 		case 's' =>
     44 			submodules = true;
     45 		case 't' =>
     46 			goal = deps_fmt::TEXT;
     47 		case =>
     48 			abort();
     49 		};
     50 	};
     51 
     52 	if (len(cmd.args) > 1) {
     53 		getopt::printusage(os::stderr, name, cmd.help)!;
     54 		os::exit(os::status::FAILURE);
     55 	};
     56 
     57 	const input = if (len(cmd.args) == 0) os::getcwd() else cmd.args[0];
     58 
     59 	let ctx = module::context {
     60 		harepath = harepath(),
     61 		harecache = harecache(),
     62 		tags = tags,
     63 	};
     64 	let mods: []module::module = [];
     65 	defer module::free_slice(mods);
     66 
     67 	let mod = match (parse::identstr(input)) {
     68 	case let id: ast::ident =>
     69 		yield id;
     70 	case parse::error =>
     71 		static let p = path::path { ... };
     72 		path::set(&p, os::realpath(input)?)?;
     73 		yield &p;
     74 	};
     75 
     76 	if (submodules) {
     77 		module::gather_submodules(&ctx, &mods, mod, recursive)?;
     78 	};
     79 
     80 	match (module::gather(&ctx, &mods, mod, recursive)) {
     81 	case let err: module::error =>
     82 		if (!(module::unwrap_error(err) is module::not_found) ||
     83 				len(mods) == 0) {
     84 			return err;
     85 		};
     86 	case =>
     87 		void;
     88 	};
     89 
     90 	switch (goal) {
     91 	case deps_fmt::TERM =>
     92 		deps_graph(&mods);
     93 	case deps_fmt::DOT =>
     94 		fmt::println("strict digraph deps {")!;
     95 		for (let mod .. mods) {
     96 			if (module::gathered(&mod) && len(mod.deps) == 0) {
     97 				fmt::printfln("\t\"{}\"", mod.name)!;
     98 			} else for (let dep .. mod.deps) {
     99 				const child = mods[dep.0];
    100 				fmt::printfln("\t\"{}\" -> \"{}\";",
    101 					mod.name, child.name)!;
    102 			};
    103 		};
    104 		fmt::println("}")!;
    105 	case deps_fmt::TEXT =>
    106 		for (let mod .. mods) {
    107 			if (module::gathered(&mod) && len(mod.deps) == 0) {
    108 				fmt::printfln("{} -", mod.name)!;
    109 			} else for (let dep .. mod.deps) {
    110 				const child = mods[dep.0];
    111 				fmt::printfln("{} {}", mod.name, child.name)!;
    112 			};
    113 		};
    114 	};
    115 };
    116 
    117 fn deps_graph(mods: *[]module::module) void = {
    118 	if (len(mods) == 1 && len(mods[0].deps) == 0) {
    119 		fmt::println(mods[0].name, "has no dependencies")!;
    120 		return;
    121 	};
    122 
    123 	let links: []link = [];
    124 	defer free(links);
    125 	let depth: []uint = alloc([0...], len(mods))!;
    126 	// traverse in reverse because reverse-topo-sort
    127 	for (let i = len(mods) - 1; i < len(mods); i -= 1) {
    128 		// reverse-sort deps so that we know the last in the list is the
    129 		// "final" child during show_deps
    130 		sort::sort(mods[i].deps, size((size, ast::ident)), &revsort);
    131 
    132 		for (let j = 0z; j < len(links); j += 1) {
    133 			if (i < links[j].child) {
    134 				continue;
    135 			};
    136 			if (depth[i] <= links[j].depth) {
    137 				depth[i] = links[j].depth + 1;
    138 			};
    139 		};
    140 
    141 		// print in-between row
    142 		for (let d = 0u; d < depth[i]; d += 1) {
    143 			let passing = false;
    144 			for (let j = 0z; j < len(links); j += 1) {
    145 				if (i < links[j].child) {
    146 					continue;
    147 				};
    148 				if (d == links[j].depth) {
    149 					passing = true;
    150 				};
    151 			};
    152 			fmt::print(if (passing) "│  " else "   ")!;
    153 		};
    154 		if (i < len(mods) - 1) {
    155 			fmt::println()!;
    156 		};
    157 
    158 		// print row itself
    159 		let on_path = false;
    160 		for (let d = 0u; d < depth[i]; d += 1) {
    161 			let connected = false;
    162 			let passing = false;
    163 			let final = false;
    164 			for (let j = 0z; j < len(links); j += 1) {
    165 				if (i < links[j].child) {
    166 					continue;
    167 				};
    168 				if (d == links[j].depth) {
    169 					passing = true;
    170 					if (i == links[j].child) {
    171 						connected = true;
    172 						on_path = true;
    173 						if (links[j].final) {
    174 							final = true;
    175 						};
    176 					};
    177 				};
    178 			};
    179 			fmt::print(
    180 				if (final) "└──"
    181 				else if (connected) "├──"
    182 				else if (on_path) "───"
    183 				else if (passing) "│  "
    184 				else "   "
    185 			)!;
    186 		};
    187 		fmt::println(mods[i].name)!;
    188 		for (let j = 0z; j < len(mods[i].deps); j += 1) {
    189 			append(links, link{
    190 				depth = depth[i],
    191 				child = mods[i].deps[j].0,
    192 				final = len(mods[i].deps) == j + 1,
    193 			})!;
    194 		};
    195 	};
    196 };
    197 
    198 // sorts in reverse
    199 fn revsort(a: const *opaque, b: const *opaque) int = -cmp::sizes(a, b);