Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / tools / checker / MonoChecker.cs
1 using System;
2 using System.Collections.Generic;
3 using System.IO;
4 using System.Linq;
5 using System.Text.RegularExpressions;
6 using CppSharp.AST;
7 using CppSharp.AST.Extensions;
8 using CppSharp.Parser;
9 using Newtonsoft.Json;
10
11 namespace CppSharp
12 {
13     /**
14      * This tool reads the AST of a Mono source checkout.
15      */
16     static class MonoChecker
17     {
18         static string CompilationDatabasePath = @"";
19
20         public static void Main(string[] args)
21         {
22             ParseCommandLineArgs(args);
23
24             Console.WriteLine();
25             Console.WriteLine("Parsing Mono's source code...");
26
27             var options = new DriverOptions();
28
29             var log = new TextDiagnosticPrinter();
30             var driver = new Driver(options, log);
31
32             Setup(driver);
33             driver.Setup();
34
35             BuildParseOptions(driver);
36             if (!driver.ParseCode())
37                 return;
38
39             Check(driver.ASTContext);
40         }
41
42         static void ParseCommandLineArgs(string[] args)
43         {
44             var needsArgs = string.IsNullOrWhiteSpace(CompilationDatabasePath);
45
46             if (!needsArgs)
47                 return;
48
49             if (args.Length >= 1)
50                 CompilationDatabasePath = Path.GetFullPath(args[0]);
51             else
52                 CompilationDatabasePath = "compile_commands.json";
53
54             if (!File.Exists(CompilationDatabasePath)) {
55                 Console.WriteLine("Could not find JSON compilation database '{0}'",
56                     CompilationDatabasePath);
57                 Environment.Exit(0);
58             }
59         }
60
61         static string GetXcodeToolchainPath()
62         {
63             var toolchains = Directory.EnumerateDirectories("/Applications", "Xcode*")
64                 .ToList();
65             toolchains.Sort();
66
67             var toolchainPath = toolchains.LastOrDefault();
68             if (toolchainPath == null)
69                 throw new Exception("Could not find a valid Xcode SDK");
70
71             return toolchainPath;
72         }
73
74         static string GetXcodeBuiltinIncludesFolder()
75         {
76             var toolchainPath = GetXcodeToolchainPath();
77
78             var toolchains = Directory.EnumerateDirectories(Path.Combine(toolchainPath,
79                 "Contents/Developer/Toolchains")).ToList();
80             toolchains.Sort();
81
82             toolchainPath = toolchains.LastOrDefault();
83             if (toolchainPath == null)
84                 throw new Exception("Could not find a valid Xcode toolchain");
85
86             var includePaths = Directory.EnumerateDirectories(Path.Combine(toolchainPath,
87                 "usr/lib/clang")).ToList();
88             var includePath = includePaths.LastOrDefault();
89
90             if (includePath == null)
91                 throw new Exception("Could not find a valid Clang include folder");
92
93             return Path.Combine(includePath, "include");
94         }
95
96         static void SetupXcode(Driver driver)
97         {
98             var options = driver.Options;
99
100             var builtinsPath = GetXcodeBuiltinIncludesFolder();
101             options.addSystemIncludeDirs(builtinsPath);
102
103             var includePath = "/usr/include";
104             options.addSystemIncludeDirs(includePath);
105
106             options.NoBuiltinIncludes = true;
107             options.NoStandardIncludes = true;
108         }        
109
110         static void Setup(Driver driver)
111         {
112             var options = driver.Options;
113             options.DryRun = true;
114             options.Verbose = false;
115             options.LibraryName = "Mono";
116             options.MicrosoftMode = false;
117             options.addArguments("-xc");
118             options.addArguments("-std=gnu99");
119
120             SetupXcode(driver);
121         }
122
123         struct CompileUnit
124         {
125             public string directory;
126             public string command;
127             public string file;
128         }
129
130         static List<CompileUnit> CleanCompileUnits(List<CompileUnit> database)
131         {
132             // The compilation database we get from Bear has duplicated entries
133             // for the same files, so clean it up before passing it down to
134             // further processing.
135             var units = new List<CompileUnit>();
136
137             foreach (var unit in database) {
138                 // Ignore compile units compiled with PIC (Position-independent code)
139                 if (unit.command.EndsWith("-fPIC -DPIC"))
140                     continue;
141
142                 // Ignore compile units that are compiled with gcc since in OSX
143                 //  it's a wrapper for the real compiler (clang) for which there'll
144                 //  be another entry.
145                 if (unit.command.Contains("gcc"))
146                     continue;
147
148                 // Ignore the static runtime build.
149                 if (unit.command.Contains("_static_la"))
150                     continue;
151
152                 // Ignore the Boehm runtime build.
153                 if (unit.command.Contains("libmonoruntime_la"))
154                     continue;                    
155
156                 units.Add(unit);
157             }
158
159             return units;
160         }
161
162         static void BuildParseOptions(Driver driver)
163         {
164             var json = File.ReadAllText(CompilationDatabasePath);
165             var compileUnits = JsonConvert.DeserializeObject<List<CompileUnit>>(json);
166
167             compileUnits = CleanCompileUnits(compileUnits);
168             compileUnits = compileUnits.OrderBy(unit => unit.file).ToList();
169
170             foreach (var unit in compileUnits) {
171                 var source = driver.Project.AddFile(unit.file);
172                 source.Options = driver.BuildParseOptions(source);
173
174                 var args = unit.command.Split(new char[] {' '}).Skip(1);
175                 foreach (var arg in args) {
176                     // Skip some arguments that Clang complains about...
177                     var arguments = new List<string> {
178                         "-no-cpp-precomp",
179                         "-Qunused-arguments",
180                         "-fno-strict-aliasing",
181                         "-Qunused-arguments",
182                         "-MD",
183                         "-MF",
184                         "-c"
185                     };
186
187                     if (arguments.Contains(arg))
188                         continue;
189
190                     source.Options.addArguments(arg);
191                 }
192             }
193         }
194
195         static void Check(ASTContext context)
196         {
197             // TODO: Implement checking here
198         }
199     }
200 }