diff --git a/plugins/csharp/parser/include/csharpparser/csharpparser.h b/plugins/csharp/parser/include/csharpparser/csharpparser.h index 6435dcf53..61dae01d1 100644 --- a/plugins/csharp/parser/include/csharpparser/csharpparser.h +++ b/plugins/csharp/parser/include/csharpparser/csharpparser.h @@ -32,9 +32,9 @@ class CsharpParser : public AbstractParser bool acceptProjectBuildPath(const std::string& buildDir_); bool parseProjectBuildPath( - const std::vector& path_, - const std::string& buildPath_); - void addSource(const std::string& filepath_, bool error_); + const std::vector& path_ + ); + void addSource(const std::string& filepath_, const std::string& targetDll_, bool error_); }; } // parser diff --git a/plugins/csharp/parser/src/csharpparser.cpp b/plugins/csharp/parser/src/csharpparser.cpp index 490c82f56..28b4b409e 100644 --- a/plugins/csharp/parser/src/csharpparser.cpp +++ b/plugins/csharp/parser/src/csharpparser.cpp @@ -18,6 +18,9 @@ #include +#include +#include + namespace cc { namespace parser @@ -27,11 +30,7 @@ CsharpParser::CsharpParser(ParserContext& ctx_): AbstractParser(ctx_) { _threadNum = _ctx.options["jobs"].as(); } -/* -bool CsharpParser::acceptProjectBuildPath(const std::vector& path_) -{ - return path_.size() >= 2 && fs::is_directory(path_[0]) && fs::is_directory(path_[1]); -}*/ + bool CsharpParser::acceptProjectBuildPath(const std::string& buildPath_) { return fs::is_directory(buildPath_); @@ -42,17 +41,15 @@ bool CsharpParser::parse() bool success = true; std::vector paths = _ctx.options["input"].as>(); - std::string buildPath = _ctx.options["build-dir"].as(); - if (acceptProjectBuildPath(buildPath)) + if (!paths.empty()) { - LOG(debug) << "C# parser parse path: " << paths[0]; - LOG(debug) << "Parsed csharp project build path: " << buildPath; - success = success && parseProjectBuildPath(paths, buildPath); + LOG(debug) << "C# parser parse path: " << paths.size(); + success = success && parseProjectBuildPath(paths); } else { - LOG(error) << "Build path must be a directory!"; + LOG(error) << "No input directories provided for C# parser!"; success = false; } @@ -60,8 +57,8 @@ bool CsharpParser::parse() } bool CsharpParser::parseProjectBuildPath( - const std::vector& paths_, - const std::string& buildPath_) + const std::vector& paths_ //, + ) { namespace ch = std::chrono; std::future log; @@ -80,8 +77,6 @@ bool CsharpParser::parseProjectBuildPath( command.append("'"); command.append(_ctx.options["database"].as()); command.append("' '"); - command.append(buildPath_); - command.append("' '"); command.append(csharp_path.string()); command.append("' "); command.append(std::to_string(_ctx.options["jobs"].as())); @@ -108,22 +103,38 @@ bool CsharpParser::parseProjectBuildPath( std::string line; std::stringstream log_str(log.get()); - //LOG(warning) << log_str.str(); int countFull = 0, countPart = 0; - + while(std::getline(log_str, line, '\n')) { - if (line[0] == '+' || line[0] == '-') + // Skip empty lines or non-JSON lines (like debug info "ParallelRun ...") + if (line.empty() || line[0] != '{') continue; + + try { - addSource(line.substr(1), line[0] == '-'); - if (line[0] == '+') - { - countFull++; - } - else - { - countPart++; - } + std::stringstream jsonStream(line); + boost::property_tree::ptree pt; + boost::property_tree::read_json(jsonStream, pt); + + bool fullyParsed = pt.get("fullyParsed"); + std::string filepath = pt.get("filePath"); + std::string targetDll = pt.get("targetDll"); + + // Check if it's an error based on 'fullyParsed' + bool isError = !fullyParsed; + + addSource(filepath, targetDll, isError); + + if (fullyParsed) { countFull++; } + else { countPart++; } + } + catch (const boost::property_tree::json_parser::json_parser_error& e) + { + LOG(warning) << "Failed to parse JSON output from C# parser: " << e.what() << " | Line: " << line; + } + catch (const boost::property_tree::ptree_error& e) + { + LOG(warning) << "Missing expected JSON field from C# parser: " << e.what() << " | Line: " << line; } } @@ -138,12 +149,12 @@ bool CsharpParser::parseProjectBuildPath( return result == 0; } -void CsharpParser::addSource(const std::string& filepath_, bool error_) +void CsharpParser::addSource(const std::string& filepath_, const std::string& targetDll_, bool error_) { util::OdbTransaction transaction(_ctx.db); model::BuildActionPtr buildAction(new model::BuildAction); - buildAction->command = " "; + buildAction->command = "dotnet build "; buildAction->type = model::BuildAction::Compile; model::BuildSource buildSource; @@ -176,10 +187,6 @@ extern "C" { boost::program_options::options_description description("C# Plugin"); - description.add_options() - ("build-dir,b", po::value()->default_value("Build directory"), - "The build directory of the parsed project."); - return description; } diff --git a/plugins/csharp/parser/src_csharp/Program.cs b/plugins/csharp/parser/src_csharp/Program.cs index a9b2b23a5..aefbe8618 100644 --- a/plugins/csharp/parser/src_csharp/Program.cs +++ b/plugins/csharp/parser/src_csharp/Program.cs @@ -9,14 +9,14 @@ using System.Collections.Generic; using System.Threading.Tasks; using CSharpParser.model; +using System.Xml.Linq; +using System.Text.Json; namespace CSharpParser { class Program { - //private readonly CsharpDbContext _context; private static List _rootDir; - private static string _buildDir = ""; private static string _buildDirBase = ""; private static string _connectionString = ""; @@ -28,11 +28,10 @@ static int Main(string[] args) try { _connectionString = args[0].Replace("'", ""); - _buildDir = args[1].Replace("'", ""); - _buildDirBase = args[2].Replace("'", ""); - threadNum = int.Parse(args[3]); + _buildDirBase = args[1].Replace("'", ""); //indexes + threadNum = int.Parse(args[2]); - for (int i = 4; i < args.Length; ++i) + for (int i = 3; i < args.Length; ++i) { _rootDir.Add(args[i].Replace("'", "")); } @@ -42,44 +41,6 @@ static int Main(string[] args) WriteLine("Error in parsing command!"); return 1; } - /*if (args.Length < 3) - { - WriteLine("Missing command-line arguments in CSharpParser!"); - return 1; - } - else if (args.Length == 3) - { - _connectionString = args[0].Replace("'", ""); - _rootDir = args[1].Replace("'", ""); - _buildDir = args[2].Replace("'", ""); - } - else if (args.Length == 4) - { - _connectionString = args[0].Replace("'", ""); - _rootDir = args[1].Replace("'", ""); - _buildDir = args[2].Replace("'", ""); - bool success = int.TryParse(args[3], out threadNum); - if (!success){ - WriteLine("Invalid threadnumber argument! Multithreaded parsing disabled!"); - } - } - else if (args.Length == 5) - { - _connectionString = args[0].Replace("'", ""); - _rootDir = args[1].Replace("'", ""); - _buildDir = args[2].Replace("'", ""); - _buildDirBase = args[3].Replace("'", ""); - bool success = int.TryParse(args[4], out threadNum); - if (!success) - { - WriteLine("Invalid threadnumber argument! Multithreaded parsing disabled!"); - } - } - else if (args.Length > 5) - { - WriteLine("Too many command-line arguments in CSharpParser!"); - return 1; - }*/ //Converting the connectionstring into entiy framwork style connectionstring string csharpConnectionString = transformConnectionString(); @@ -91,21 +52,35 @@ static int Main(string[] args) CsharpDbContext _context = new CsharpDbContext(options); _context.Database.Migrate(); + List allFiles = new List(); + // This dictionary will remember which file belongs to which DLL + Dictionary fileToTargetDll = new Dictionary(); + foreach (var p in _rootDir) { - Console.WriteLine(p); allFiles.AddRange(GetSourceFilesFromDir(p, ".cs")); } + allFiles = allFiles.Distinct().ToList(); foreach (var f in allFiles) { WriteLine(f); } - IEnumerable assemblies = GetSourceFilesFromDir(_buildDir, ".dll"); - IEnumerable assemblies_base = assemblies; - if (args.Length == 5) - assemblies_base = GetSourceFilesFromDir(_buildDirBase, ".dll"); + + + IEnumerable assemblies_base = GetSourceFilesFromDir(_buildDirBase, ".dll"); //loading basic dlls + + List assemblies = new List(); + foreach (var p in _rootDir) + { + // We search for all .dll files in all input directories + assemblies.AddRange(GetSourceFilesFromDir(p, ".dll")); + } + // Let's keep only one of each DLL based on the file name! + assemblies = assemblies.GroupBy(x => System.IO.Path.GetFileName(x)) + .Select(g => g.First()) + .ToList(); List trees = new List(); foreach (string file in allFiles) @@ -129,14 +104,14 @@ static int Main(string[] args) compilation = compilation.AddReferences(MetadataReference.CreateFromFile(file)); } - var runtask = ParalellRun(csharpConnectionString, threadNum, trees, compilation); + var runtask = ParalellRun(csharpConnectionString, threadNum, trees, compilation, fileToTargetDll); int ret = runtask.Result; return 0; } private static async Task ParalellRun(string csharpConnectionString, int threadNum, - List trees, CSharpCompilation compilation) + List trees, CSharpCompilation compilation, Dictionary fileToTargetDll) { var options = new DbContextOptionsBuilder() .UseNpgsql(csharpConnectionString) @@ -156,7 +131,7 @@ private static async Task ParalellRun(string csharpConnectionString, int th WriteLine(threadNum); for (int i = 0; i < maxThread; i++) { - ParsingTasks.Add(ParseTree(contextList[i],trees[i],compilation,i)); + ParsingTasks.Add(ParseTree(contextList[i],trees[i],compilation,i,fileToTargetDll)); } int nextTreeIndex = maxThread; @@ -169,7 +144,7 @@ private static async Task ParalellRun(string csharpConnectionString, int th if (nextTreeIndex < trees.Count) { ParsingTasks.Add(ParseTree(contextList[nextContextIndex], - trees[nextTreeIndex],compilation,nextContextIndex)); + trees[nextTreeIndex],compilation,nextContextIndex, fileToTargetDll)); ++nextTreeIndex; } } @@ -183,15 +158,28 @@ private static async Task ParalellRun(string csharpConnectionString, int th } private static async Task ParseTree(CsharpDbContext context, - SyntaxTree tree, CSharpCompilation compilation, int index) + SyntaxTree tree, CSharpCompilation compilation, int index, + Dictionary fileToTargetDll) { var ParsingTask = Task.Run(() => { WriteLine("ParallelRun " + tree.FilePath); SemanticModel model = compilation.GetSemanticModel(tree); var visitor = new AstVisitor(context, model, tree); - visitor.Visit(tree.GetCompilationUnitRoot()); - WriteLine((visitor.FullyParsed ? "+" : "-") + tree.FilePath); + visitor.Visit(tree.GetCompilationUnitRoot()); + + // Find the DLL name and append a | to the filename. + string target = fileToTargetDll.ContainsKey(tree.FilePath) ? fileToTargetDll[tree.FilePath] : "Unknown.dll"; + + var resultData = new + { + fullyParsed = visitor.FullyParsed, + filePath = tree.FilePath, + targetDll = target + }; + + WriteLine(JsonSerializer.Serialize(resultData)); + return index; }); return await ParsingTask;