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 }