{"action":"created","comment":{"url":"https://api.github.com/repos/Semmle/ql/pulls/comments/235637103","pull_request_review_id":177546179,"id":235637103,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIzNTYzNzEwMw==","diff_hunk":"@@ -113,46 +119,75 @@ public Autobuilder(IBuildActions actions, AutobuildOptions options)\n             Actions = actions;\n             Options = options;\n \n-            pathsLazy = new Lazy<IEnumerable<string>>(() =>\n+            pathsLazy = new Lazy<IEnumerable<(string, int)>>(() =>\n             {\n-                var files = new List<string>();\n-                FindFiles(options.RootDirectory, options.SearchDepth, files);\n-                return files.\n-                    OrderBy(s => s.Count(c => c == Path.DirectorySeparatorChar)).\n-                    ThenBy(s => Path.GetFileName(s).Length).\n-                    ToArray();\n+                var files = new List<(string, int)>();\n+                FindFiles(options.RootDirectory, 0, options.SearchDepth, files);\n+                return files.OrderBy(f => f.Item2).ToArray();\n             });\n \n-            solutionsToBuildLazy = new Lazy<IList<ISolution>>(() =>\n+            projectsOrSolutionsToBuildLazy = new Lazy<IList<IProjectOrSolution>>(() =>\n             {\n                 if (options.Solution.Any())\n                 {\n-                    var ret = new List<ISolution>();\n+                    var ret = new List<IProjectOrSolution>();\n                     foreach (var solution in options.Solution)\n                     {\n                         if (actions.FileExists(solution))\n                             ret.Add(new Solution(this, solution));\n                         else\n-                            Log(Severity.Error, \"The specified solution file {0} was not found\", solution);\n+                            Log(Severity.Error, $\"The specified solution file {solution} was not found\");\n                     }\n                     return ret;\n                 }\n \n-                var solutions = GetExtensions(\".sln\").\n-                        Select(s => new Solution(this, s)).\n-                        Where(s => s.ProjectCount > 0).\n-                        OrderByDescending(s => s.ProjectCount).\n-                        ThenBy(s => s.Path.Length).\n+                bool FindFiles(string extension, Func<string, ProjectOrSolution> create, out IEnumerable<IProjectOrSolution> files)\n+                {\n+                    var allFiles = GetExtensions(extension).\n+                        Select(p => (ProjectOrSolution: create(p.Item1), DistanceFromRoot: p.Item2)).\n+                        Where(p => p.ProjectOrSolution.HasLanguage(this.Options.Language)).\n                         ToArray();\n \n-                foreach (var sln in solutions)\n-                {\n-                    Log(Severity.Info, $\"Found {sln.Path} with {sln.ProjectCount} {this.Options.Language} projects, version {sln.ToolsVersion}, config {string.Join(\" \", sln.Configurations.Select(c => c.FullName))}\");\n+                    if (allFiles.Length == 0)\n+                    {\n+                        files = null;\n+                        return false;\n+                    }\n+\n+                    if (options.AllSolutions)\n+                    {\n+                        files = allFiles.Select(p => p.ProjectOrSolution);\n+                        return true;\n+                    }\n+\n+                    var firstIsClosest = allFiles.Length > 1 && allFiles[0].DistanceFromRoot < allFiles[1].DistanceFromRoot;\n+                    if (allFiles.Length == 1 || firstIsClosest)\n+                    {\n+                        files = allFiles.Select(p => p.ProjectOrSolution).Take(1);\n+                        return true;\n+                    }\n+\n+                    var candidates = allFiles.\n+                        Where(f => f.DistanceFromRoot == allFiles[0].DistanceFromRoot).\n+                        Select(f => f.ProjectOrSolution);\n+                    Log(Severity.Info, $\"Found multiple '{extension}' files, giving up: {string.Join(\", \", candidates)}.\");\n+                    files = new IProjectOrSolution[0];\n+                    return true;","path":"csharp/autobuilder/Semmle.Autobuild/Autobuilder.cs","position":155,"original_position":155,"commit_id":"1afc529e63a8682a3d8e14efbab4f544db71c158","original_commit_id":"1afc529e63a8682a3d8e14efbab4f544db71c158","user":{"login":"hvitved","id":3667920,"node_id":"MDQ6VXNlcjM2Njc5MjA=","avatar_url":"https://avatars2.githubusercontent.com/u/3667920?v=4","gravatar_id":"","url":"https://api.github.com/users/hvitved","html_url":"https://github.com/hvitved","followers_url":"https://api.github.com/users/hvitved/followers","following_url":"https://api.github.com/users/hvitved/following{/other_user}","gists_url":"https://api.github.com/users/hvitved/gists{/gist_id}","starred_url":"https://api.github.com/users/hvitved/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hvitved/subscriptions","organizations_url":"https://api.github.com/users/hvitved/orgs","repos_url":"https://api.github.com/users/hvitved/repos","events_url":"https://api.github.com/users/hvitved/events{/privacy}","received_events_url":"https://api.github.com/users/hvitved/received_events","type":"User","site_admin":false},"body":"The return value behaves like e.g. the return value of `int.TryParse(string s, out int i)`; the output parameter contains valid data iff the return value is `true`.","created_at":"2018-11-22T08:34:06Z","updated_at":"2018-11-22T08:34:06Z","html_url":"https://github.com/Semmle/ql/pull/512#discussion_r235637103","pull_request_url":"https://api.github.com/repos/Semmle/ql/pulls/512","author_association":"MEMBER","_links":{"self":{"href":"https://api.github.com/repos/Semmle/ql/pulls/comments/235637103"},"html":{"href":"https://github.com/Semmle/ql/pull/512#discussion_r235637103"},"pull_request":{"href":"https://api.github.com/repos/Semmle/ql/pulls/512"}},"in_reply_to_id":235515668},"pull_request":{"url":"https://api.github.com/repos/Semmle/ql/pulls/512","id":232670709,"node_id":"MDExOlB1bGxSZXF1ZXN0MjMyNjcwNzA5","html_url":"https://github.com/Semmle/ql/pull/512","diff_url":"https://github.com/Semmle/ql/pull/512.diff","patch_url":"https://github.com/Semmle/ql/pull/512.patch","issue_url":"https://api.github.com/repos/Semmle/ql/issues/512","number":512,"state":"open","locked":false,"title":" C#: Recognize `.proj` files in autobuilder","user":{"login":"hvitved","id":3667920,"node_id":"MDQ6VXNlcjM2Njc5MjA=","avatar_url":"https://avatars2.githubusercontent.com/u/3667920?v=4","gravatar_id":"","url":"https://api.github.com/users/hvitved","html_url":"https://github.com/hvitved","followers_url":"https://api.github.com/users/hvitved/followers","following_url":"https://api.github.com/users/hvitved/following{/other_user}","gists_url":"https://api.github.com/users/hvitved/gists{/gist_id}","starred_url":"https://api.github.com/users/hvitved/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hvitved/subscriptions","organizations_url":"https://api.github.com/users/hvitved/orgs","repos_url":"https://api.github.com/users/hvitved/repos","events_url":"https://api.github.com/users/hvitved/events{/privacy}","received_events_url":"https://api.github.com/users/hvitved/received_events","type":"User","site_admin":false},"body":"When determining the target of `msbuild` or `dotnet build`, first look for `.proj` files, then `.sln` files, and finally `.csproj`/`.vcxproj` files. In all three cases, choose the project/solution file closest to the root.\r\n\r\n@calumgrant @jf205 ","created_at":"2018-11-21T13:51:15Z","updated_at":"2018-11-22T08:34:06Z","closed_at":null,"merged_at":null,"merge_commit_sha":"db329c0ac2e11ba9f42680b734ca781e5af662e3","assignee":null,"assignees":[],"requested_reviewers":[{"login":"jf205","id":42464962,"node_id":"MDQ6VXNlcjQyNDY0OTYy","avatar_url":"https://avatars2.githubusercontent.com/u/42464962?v=4","gravatar_id":"","url":"https://api.github.com/users/jf205","html_url":"https://github.com/jf205","followers_url":"https://api.github.com/users/jf205/followers","following_url":"https://api.github.com/users/jf205/following{/other_user}","gists_url":"https://api.github.com/users/jf205/gists{/gist_id}","starred_url":"https://api.github.com/users/jf205/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jf205/subscriptions","organizations_url":"https://api.github.com/users/jf205/orgs","repos_url":"https://api.github.com/users/jf205/repos","events_url":"https://api.github.com/users/jf205/events{/privacy}","received_events_url":"https://api.github.com/users/jf205/received_events","type":"User","site_admin":false}],"requested_teams":[],"labels":[{"id":1013652574,"node_id":"MDU6TGFiZWwxMDEzNjUyNTc0","url":"https://api.github.com/repos/Semmle/ql/labels/C%23","name":"C#","color":"75eada","default":false},{"id":1019698289,"node_id":"MDU6TGFiZWwxMDE5Njk4Mjg5","url":"https://api.github.com/repos/Semmle/ql/labels/depends%20on%20internal%20PR","name":"depends on internal PR","color":"f9e69f","default":false}],"milestone":null,"commits_url":"https://api.github.com/repos/Semmle/ql/pulls/512/commits","review_comments_url":"https://api.github.com/repos/Semmle/ql/pulls/512/comments","review_comment_url":"https://api.github.com/repos/Semmle/ql/pulls/comments{/number}","comments_url":"https://api.github.com/repos/Semmle/ql/issues/512/comments","statuses_url":"https://api.github.com/repos/Semmle/ql/statuses/1afc529e63a8682a3d8e14efbab4f544db71c158","head":{"label":"hvitved:csharp/autobuilder/dirs-proj","ref":"csharp/autobuilder/dirs-proj","sha":"1afc529e63a8682a3d8e14efbab4f544db71c158","user":{"login":"hvitved","id":3667920,"node_id":"MDQ6VXNlcjM2Njc5MjA=","avatar_url":"https://avatars2.githubusercontent.com/u/3667920?v=4","gravatar_id":"","url":"https://api.github.com/users/hvitved","html_url":"https://github.com/hvitved","followers_url":"https://api.github.com/users/hvitved/followers","following_url":"https://api.github.com/users/hvitved/following{/other_user}","gists_url":"https://api.github.com/users/hvitved/gists{/gist_id}","starred_url":"https://api.github.com/users/hvitved/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hvitved/subscriptions","organizations_url":"https://api.github.com/users/hvitved/orgs","repos_url":"https://api.github.com/users/hvitved/repos","events_url":"https://api.github.com/users/hvitved/events{/privacy}","received_events_url":"https://api.github.com/users/hvitved/received_events","type":"User","site_admin":false},"repo":{"id":143420270,"node_id":"MDEwOlJlcG9zaXRvcnkxNDM0MjAyNzA=","name":"ql","full_name":"hvitved/ql","private":false,"owner":{"login":"hvitved","id":3667920,"node_id":"MDQ6VXNlcjM2Njc5MjA=","avatar_url":"https://avatars2.githubusercontent.com/u/3667920?v=4","gravatar_id":"","url":"https://api.github.com/users/hvitved","html_url":"https://github.com/hvitved","followers_url":"https://api.github.com/users/hvitved/followers","following_url":"https://api.github.com/users/hvitved/following{/other_user}","gists_url":"https://api.github.com/users/hvitved/gists{/gist_id}","starred_url":"https://api.github.com/users/hvitved/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hvitved/subscriptions","organizations_url":"https://api.github.com/users/hvitved/orgs","repos_url":"https://api.github.com/users/hvitved/repos","events_url":"https://api.github.com/users/hvitved/events{/privacy}","received_events_url":"https://api.github.com/users/hvitved/received_events","type":"User","site_admin":false},"html_url":"https://github.com/hvitved/ql","description":null,"fork":true,"url":"https://api.github.com/repos/hvitved/ql","forks_url":"https://api.github.com/repos/hvitved/ql/forks","keys_url":"https://api.github.com/repos/hvitved/ql/keys{/key_id}","collaborators_url":"https://api.github.com/repos/hvitved/ql/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/hvitved/ql/teams","hooks_url":"https://api.github.com/repos/hvitved/ql/hooks","issue_events_url":"https://api.github.com/repos/hvitved/ql/issues/events{/number}","events_url":"https://api.github.com/repos/hvitved/ql/events","assignees_url":"https://api.github.com/repos/hvitved/ql/assignees{/user}","branches_url":"https://api.github.com/repos/hvitved/ql/branches{/branch}","tags_url":"https://api.github.com/repos/hvitved/ql/tags","blobs_url":"https://api.github.com/repos/hvitved/ql/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/hvitved/ql/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/hvitved/ql/git/refs{/sha}","trees_url":"https://api.github.com/repos/hvitved/ql/git/trees{/sha}","statuses_url":"https://api.github.com/repos/hvitved/ql/statuses/{sha}","languages_url":"https://api.github.com/repos/hvitved/ql/languages","stargazers_url":"https://api.github.com/repos/hvitved/ql/stargazers","contributors_url":"https://api.github.com/repos/hvitved/ql/contributors","subscribers_url":"https://api.github.com/repos/hvitved/ql/subscribers","subscription_url":"https://api.github.com/repos/hvitved/ql/subscription","commits_url":"https://api.github.com/repos/hvitved/ql/commits{/sha}","git_commits_url":"https://api.github.com/repos/hvitved/ql/git/commits{/sha}","comments_url":"https://api.github.com/repos/hvitved/ql/comments{/number}","issue_comment_url":"https://api.github.com/repos/hvitved/ql/issues/comments{/number}","contents_url":"https://api.github.com/repos/hvitved/ql/contents/{+path}","compare_url":"https://api.github.com/repos/hvitved/ql/compare/{base}...{head}","merges_url":"https://api.github.com/repos/hvitved/ql/merges","archive_url":"https://api.github.com/repos/hvitved/ql/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/hvitved/ql/downloads","issues_url":"https://api.github.com/repos/hvitved/ql/issues{/number}","pulls_url":"https://api.github.com/repos/hvitved/ql/pulls{/number}","milestones_url":"https://api.github.com/repos/hvitved/ql/milestones{/number}","notifications_url":"https://api.github.com/repos/hvitved/ql/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/hvitved/ql/labels{/name}","releases_url":"https://api.github.com/repos/hvitved/ql/releases{/id}","deployments_url":"https://api.github.com/repos/hvitved/ql/deployments","created_at":"2018-08-03T11:40:27Z","updated_at":"2018-08-07T00:46:53Z","pushed_at":"2018-11-21T13:47:49Z","git_url":"git://github.com/hvitved/ql.git","ssh_url":"git@github.com:hvitved/ql.git","clone_url":"https://github.com/hvitved/ql.git","svn_url":"https://github.com/hvitved/ql","homepage":null,"size":10951,"stargazers_count":0,"watchers_count":0,"language":"JavaScript","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"other","name":"Other","spdx_id":"NOASSERTION","url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"Semmle:master","ref":"master","sha":"4e2d40aad8d18c3279adfe069927710f5682b9bc","user":{"login":"Semmle","id":1101495,"node_id":"MDEyOk9yZ2FuaXphdGlvbjExMDE0OTU=","avatar_url":"https://avatars0.githubusercontent.com/u/1101495?v=4","gravatar_id":"","url":"https://api.github.com/users/Semmle","html_url":"https://github.com/Semmle","followers_url":"https://api.github.com/users/Semmle/followers","following_url":"https://api.github.com/users/Semmle/following{/other_user}","gists_url":"https://api.github.com/users/Semmle/gists{/gist_id}","starred_url":"https://api.github.com/users/Semmle/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Semmle/subscriptions","organizations_url":"https://api.github.com/users/Semmle/orgs","repos_url":"https://api.github.com/users/Semmle/repos","events_url":"https://api.github.com/users/Semmle/events{/privacy}","received_events_url":"https://api.github.com/users/Semmle/received_events","type":"Organization","site_admin":false},"repo":{"id":143040428,"node_id":"MDEwOlJlcG9zaXRvcnkxNDMwNDA0Mjg=","name":"ql","full_name":"Semmle/ql","private":false,"owner":{"login":"Semmle","id":1101495,"node_id":"MDEyOk9yZ2FuaXphdGlvbjExMDE0OTU=","avatar_url":"https://avatars0.githubusercontent.com/u/1101495?v=4","gravatar_id":"","url":"https://api.github.com/users/Semmle","html_url":"https://github.com/Semmle","followers_url":"https://api.github.com/users/Semmle/followers","following_url":"https://api.github.com/users/Semmle/following{/other_user}","gists_url":"https://api.github.com/users/Semmle/gists{/gist_id}","starred_url":"https://api.github.com/users/Semmle/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Semmle/subscriptions","organizations_url":"https://api.github.com/users/Semmle/orgs","repos_url":"https://api.github.com/users/Semmle/repos","events_url":"https://api.github.com/users/Semmle/events{/privacy}","received_events_url":"https://api.github.com/users/Semmle/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/Semmle/ql","description":"The standard QL libraries and queries that power LGTM.com and other Semmle Products","fork":false,"url":"https://api.github.com/repos/Semmle/ql","forks_url":"https://api.github.com/repos/Semmle/ql/forks","keys_url":"https://api.github.com/repos/Semmle/ql/keys{/key_id}","collaborators_url":"https://api.github.com/repos/Semmle/ql/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/Semmle/ql/teams","hooks_url":"https://api.github.com/repos/Semmle/ql/hooks","issue_events_url":"https://api.github.com/repos/Semmle/ql/issues/events{/number}","events_url":"https://api.github.com/repos/Semmle/ql/events","assignees_url":"https://api.github.com/repos/Semmle/ql/assignees{/user}","branches_url":"https://api.github.com/repos/Semmle/ql/branches{/branch}","tags_url":"https://api.github.com/repos/Semmle/ql/tags","blobs_url":"https://api.github.com/repos/Semmle/ql/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/Semmle/ql/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/Semmle/ql/git/refs{/sha}","trees_url":"https://api.github.com/repos/Semmle/ql/git/trees{/sha}","statuses_url":"https://api.github.com/repos/Semmle/ql/statuses/{sha}","languages_url":"https://api.github.com/repos/Semmle/ql/languages","stargazers_url":"https://api.github.com/repos/Semmle/ql/stargazers","contributors_url":"https://api.github.com/repos/Semmle/ql/contributors","subscribers_url":"https://api.github.com/repos/Semmle/ql/subscribers","subscription_url":"https://api.github.com/repos/Semmle/ql/subscription","commits_url":"https://api.github.com/repos/Semmle/ql/commits{/sha}","git_commits_url":"https://api.github.com/repos/Semmle/ql/git/commits{/sha}","comments_url":"https://api.github.com/repos/Semmle/ql/comments{/number}","issue_comment_url":"https://api.github.com/repos/Semmle/ql/issues/comments{/number}","contents_url":"https://api.github.com/repos/Semmle/ql/contents/{+path}","compare_url":"https://api.github.com/repos/Semmle/ql/compare/{base}...{head}","merges_url":"https://api.github.com/repos/Semmle/ql/merges","archive_url":"https://api.github.com/repos/Semmle/ql/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/Semmle/ql/downloads","issues_url":"https://api.github.com/repos/Semmle/ql/issues{/number}","pulls_url":"https://api.github.com/repos/Semmle/ql/pulls{/number}","milestones_url":"https://api.github.com/repos/Semmle/ql/milestones{/number}","notifications_url":"https://api.github.com/repos/Semmle/ql/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/Semmle/ql/labels{/name}","releases_url":"https://api.github.com/repos/Semmle/ql/releases{/id}","deployments_url":"https://api.github.com/repos/Semmle/ql/deployments","created_at":"2018-07-31T16:35:51Z","updated_at":"2018-11-22T07:30:26Z","pushed_at":"2018-11-22T07:47:02Z","git_url":"git://github.com/Semmle/ql.git","ssh_url":"git@github.com:Semmle/ql.git","clone_url":"https://github.com/Semmle/ql.git","svn_url":"https://github.com/Semmle/ql","homepage":"https://semmle.com/ql","size":12056,"stargazers_count":65,"watchers_count":65,"language":"JavaScript","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":43,"mirror_url":null,"archived":false,"open_issues_count":27,"license":{"key":"other","name":"Other","spdx_id":"NOASSERTION","url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":43,"open_issues":27,"watchers":65,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/Semmle/ql/pulls/512"},"html":{"href":"https://github.com/Semmle/ql/pull/512"},"issue":{"href":"https://api.github.com/repos/Semmle/ql/issues/512"},"comments":{"href":"https://api.github.com/repos/Semmle/ql/issues/512/comments"},"review_comments":{"href":"https://api.github.com/repos/Semmle/ql/pulls/512/comments"},"review_comment":{"href":"https://api.github.com/repos/Semmle/ql/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/Semmle/ql/pulls/512/commits"},"statuses":{"href":"https://api.github.com/repos/Semmle/ql/statuses/1afc529e63a8682a3d8e14efbab4f544db71c158"}},"author_association":"MEMBER"}}
{"actor":{"display_login":"hvitved"}}