1 // Copyright Mario Kröplin 2020. 2 // Distributed under the Boost Software License, Version 1.0. 3 // (See accompanying file LICENSE_1_0.txt or copy at 4 // https://www.boost.org/LICENSE_1_0.txt) 5 module main; 6 7 import core.stdc.stdlib; 8 import deps; 9 import graph; 10 import imports; 11 import model; 12 import settings : readSettings = read; 13 import std.algorithm; 14 import std.array; 15 import std.exception; 16 import std.range; 17 import std.stdio; 18 import std.typecons; 19 import uml; 20 21 void main(string[] args) 22 { 23 const settings = readSettings(args); 24 25 with (settings) 26 { 27 auto readDependencies(File file) 28 { 29 import std.regex : matchFirst, Regex; 30 31 if (pattern != Regex!char()) 32 { 33 bool matches(T)(T dependency) 34 { 35 with (dependency) 36 return client.path.matchFirst(pattern) 37 && supplier.path.matchFirst(pattern); 38 } 39 40 return moduleDependencies!(dependency => matches(dependency))(file).array; 41 } 42 else 43 { 44 bool matches(T)(T dependency) 45 { 46 with (dependency) 47 return unrecognizedArgs.canFind(client.path) 48 && unrecognizedArgs.canFind(supplier.path); 49 } 50 51 return moduleDependencies!(dependency => matches(dependency))(file).array; 52 } 53 } 54 55 Dependency[] actualDependencies; 56 57 if (compiler.empty) 58 { 59 actualDependencies = mutualDependencies(unrecognizedArgs).array; 60 } 61 else 62 { 63 import std.process : pipeProcess, Redirect, wait; 64 65 const args_ = [compiler, "-deps", "-o-"] ~ unrecognizedArgs; 66 auto pipes = pipeProcess(args_, Redirect.stdout); 67 68 scope (exit) 69 { 70 auto status = wait(pipes.pid); 71 72 if (status != 0) 73 exit(EXIT_FAILURE); 74 } 75 76 actualDependencies = readDependencies(pipes.stdout); 77 } 78 actualDependencies ~= depsFiles 79 .map!(depsFile => readDependencies(File(depsFile))) 80 .join; 81 actualDependencies ~= umlFiles 82 .map!(umlFile => read(File(umlFile).byLine)) 83 .join; 84 actualDependencies = actualDependencies.sort.uniq.array; 85 if (!targetFiles.empty) 86 { 87 import check : Checker; 88 import uml : read; 89 90 bool success = true; 91 Dependency[] targetDependencies = null; 92 93 foreach (targetFile; targetFiles) 94 targetDependencies ~= read(File(targetFile).byLine); 95 96 if (!transitive) 97 targetDependencies.transitiveClosure; 98 99 auto checker = Checker(targetDependencies, simplify); 100 101 foreach (dependency; actualDependencies) 102 { 103 auto client = dependency.client; 104 auto supplier = dependency.supplier; 105 106 if (detail) 107 dependency = Dependency(client, supplier); 108 else 109 { 110 dependency = Dependency(client.packages, supplier.packages); 111 if (dependency.client.names.empty || dependency.supplier.names.empty 112 || dependency.client == dependency.supplier) 113 continue; 114 } 115 if (!checker.allows(dependency)) 116 { 117 stderr.writefln("error: unintended dependency %s -> %s", client, supplier); 118 success = false; 119 } 120 } 121 if (!success) 122 exit(EXIT_FAILURE); 123 } 124 if (dot || targetFiles.empty) 125 { 126 Dependency[] dependencies_ = null; 127 128 if (detail) 129 dependencies_ = actualDependencies; 130 else 131 { 132 foreach (dependency; actualDependencies) 133 { 134 const client = dependency.client.packages; 135 const supplier = dependency.supplier.packages; 136 137 if (!client.empty && !supplier.empty && client != supplier) 138 dependencies_.add(Dependency(client, supplier)); 139 } 140 } 141 if (!transitive) 142 { 143 auto cyclicDependencies = transitiveReduction(dependencies_); 144 145 if (!cyclicDependencies.empty) 146 { 147 stderr.writeln("warning: cyclic dependencies"); 148 foreach (dependency; cyclicDependencies.sort) 149 stderr.writefln!"%s -> %s"(dependency.client, dependency.supplier); 150 } 151 } 152 if (dot) 153 { 154 import graph : write; 155 import std.stdio : stdout; 156 157 stdout.lockingTextWriter.write(dependencies_); 158 } 159 else 160 { 161 import uml : write; 162 import std.stdio : stdout; 163 164 stdout.lockingTextWriter.write(dependencies_); 165 } 166 } 167 } 168 }