11using Semmle . Extraction . CSharp ;
22using Semmle . Util . Logging ;
33using Semmle . Autobuild . Shared ;
4+ using Semmle . Util ;
5+ using System . Linq ;
46
57namespace Semmle . Autobuild . CSharp
68{
@@ -29,25 +31,16 @@ public CSharpAutobuildOptions(IBuildActions actions) : base(actions)
2931
3032 public class CSharpAutobuilder : Autobuilder < CSharpAutobuildOptions >
3133 {
32- public CSharpAutobuilder ( IBuildActions actions , CSharpAutobuildOptions options ) : base ( actions , options ) { }
34+ private const string buildCommandDocsUrl =
35+ "https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-the-codeql-workflow-for-compiled-languages" ;
3336
34- public override BuildScript GetBuildScript ( )
35- {
36- /// <summary>
37- /// A script that checks that the C# extractor has been executed.
38- /// </summary>
39- BuildScript CheckExtractorRun ( bool warnOnFailure ) =>
40- BuildScript . Create ( actions =>
41- {
42- if ( actions . FileExists ( Extractor . GetCSharpLogPath ( ) ) )
43- return 0 ;
37+ private readonly AutoBuildRule autoBuildRule ;
4438
45- if ( warnOnFailure )
46- Log ( Severity . Error , "No C# code detected during build." ) ;
47-
48- return 1 ;
49- } ) ;
39+ public CSharpAutobuilder ( IBuildActions actions , CSharpAutobuildOptions options ) : base ( actions , options , new CSharpDiagnosticClassifier ( ) ) =>
40+ this . autoBuildRule = new AutoBuildRule ( this ) ;
5041
42+ public override BuildScript GetBuildScript ( )
43+ {
5144 var attempt = BuildScript . Failure ;
5245 switch ( GetCSharpBuildStrategy ( ) )
5346 {
@@ -65,47 +58,9 @@ BuildScript CheckExtractorRun(bool warnOnFailure) =>
6558 attempt = new DotNetRule ( ) . Analyse ( this , false ) & CheckExtractorRun ( true ) ;
6659 break ;
6760 case CSharpBuildStrategy . Auto :
68- var cleanTrapFolder =
69- BuildScript . DeleteDirectory ( TrapDir ) ;
70- var cleanSourceArchive =
71- BuildScript . DeleteDirectory ( SourceArchiveDir ) ;
72- var tryCleanExtractorArgsLogs =
73- BuildScript . Create ( actions =>
74- {
75- foreach ( var file in Extractor . GetCSharpArgsLogs ( ) )
76- {
77- try
78- {
79- actions . FileDelete ( file ) ;
80- }
81- catch // lgtm[cs/catch-of-all-exceptions] lgtm[cs/empty-catch-block]
82- { }
83- }
84-
85- return 0 ;
86- } ) ;
87- var attemptExtractorCleanup =
88- BuildScript . Try ( cleanTrapFolder ) &
89- BuildScript . Try ( cleanSourceArchive ) &
90- tryCleanExtractorArgsLogs &
91- BuildScript . DeleteFile ( Extractor . GetCSharpLogPath ( ) ) ;
92-
93- /// <summary>
94- /// Execute script `s` and check that the C# extractor has been executed.
95- /// If either fails, attempt to cleanup any artifacts produced by the extractor,
96- /// and exit with code 1, in order to proceed to the next attempt.
97- /// </summary>
98- BuildScript IntermediateAttempt ( BuildScript s ) =>
99- ( s & CheckExtractorRun ( false ) ) |
100- ( attemptExtractorCleanup & BuildScript . Failure ) ;
101-
10261 attempt =
103- // First try .NET Core
104- IntermediateAttempt ( new DotNetRule ( ) . Analyse ( this , true ) ) |
105- // Then MSBuild
106- ( ( ) => IntermediateAttempt ( new MsBuildRule ( ) . Analyse ( this , true ) ) ) |
107- // And finally look for a script that might be a build script
108- ( ( ) => new BuildCommandAutoRule ( DotNetRule . WithDotNet ) . Analyse ( this , true ) & CheckExtractorRun ( true ) ) |
62+ // Attempt a few different build strategies to see if one works
63+ this . autoBuildRule . Analyse ( this , true ) |
10964 // All attempts failed: print message
11065 AutobuildFailure ( ) ;
11166 break ;
@@ -114,6 +69,127 @@ BuildScript IntermediateAttempt(BuildScript s) =>
11469 return attempt ;
11570 }
11671
72+ /// <summary>
73+ /// A script that checks that the C# extractor has been executed.
74+ /// </summary>
75+ public BuildScript CheckExtractorRun ( bool warnOnFailure ) =>
76+ BuildScript . Create ( actions =>
77+ {
78+ if ( actions . FileExists ( Extractor . GetCSharpLogPath ( ) ) )
79+ return 0 ;
80+
81+ if ( warnOnFailure )
82+ Log ( Severity . Error , "No C# code detected during build." ) ;
83+
84+ return 1 ;
85+ } ) ;
86+
87+ protected override void AutobuildFailureDiagnostic ( )
88+ {
89+ // if `ScriptPath` is not null here, the `BuildCommandAuto` rule was
90+ // run and found at least one script to execute
91+ if ( this . autoBuildRule . BuildCommandAutoRule . ScriptPath is not null )
92+ {
93+ var relScriptPath = this . MakeRelative ( autoBuildRule . BuildCommandAutoRule . ScriptPath ) ;
94+
95+ // if we found multiple build scripts in the project directory, then we can say
96+ // as much to indicate that we may have picked the wrong one;
97+ // otherwise, we just report that the one script we found didn't work
98+ DiagnosticMessage message =
99+ this . autoBuildRule . BuildCommandAutoRule . CandidatePaths . Count ( ) > 1 ?
100+ new (
101+ this . Options . Language ,
102+ "multiple-build-scripts" ,
103+ "There are multiple potential build scripts" ,
104+ markdownMessage :
105+ "CodeQL found multiple potential build scripts for your project and " +
106+ $ "attempted to run `{ relScriptPath } `, which failed. " +
107+ "This may not be the right build script for your project. " +
108+ $ "Set up a [manual build command]({ buildCommandDocsUrl } )."
109+ ) :
110+ new (
111+ this . Options . Language ,
112+ "script-failure" ,
113+ "Unable to build project using build script" ,
114+ markdownMessage :
115+ "CodeQL attempted to build your project using a script located at " +
116+ $ "`{ relScriptPath } `, which failed. " +
117+ $ "Set up a [manual build command]({ buildCommandDocsUrl } )."
118+ ) ;
119+
120+ AddDiagnostic ( message ) ;
121+ }
122+
123+ // project files which don't exist get marked as not .NET core projects, but we don't want
124+ // to show an error for this if the files don't exist
125+ var foundNotDotNetProjects = autoBuildRule . DotNetRule . NotDotNetProjects . Where (
126+ proj => this . Actions . FileExists ( proj . FullPath )
127+ ) ;
128+
129+ // both dotnet and msbuild builds require project or solution files; if we haven't found any
130+ // then neither of those rules would've worked
131+ if ( this . ProjectsOrSolutionsToBuild . Count == 0 )
132+ {
133+ this . AddDiagnostic ( new (
134+ this . Options . Language ,
135+ "no-projects-or-solutions" ,
136+ "No project or solutions files found" ,
137+ markdownMessage :
138+ "CodeQL could not find any project or solution files in your repository. " +
139+ $ "Set up a [manual build command]({ buildCommandDocsUrl } )."
140+ ) ) ;
141+ }
142+ // show a warning if there are projects which are not compatible with .NET Core, in case that is unintentional
143+ else if ( foundNotDotNetProjects . Any ( ) )
144+ {
145+ this . AddDiagnostic ( new (
146+ this . Options . Language ,
147+ "dotnet-incompatible-projects" ,
148+ "Some projects are incompatible with .NET Core" ,
149+ severity : DiagnosticMessage . TspSeverity . Warning ,
150+ markdownMessage : $ """
151+ CodeQL found some projects which cannot be built with .NET Core:
152+
153+ { autoBuildRule . DotNetRule . NotDotNetProjects . Select ( p => this . MakeRelative ( p . FullPath ) ) . ToMarkdownList ( MarkdownUtil . CodeFormatter , 5 ) }
154+ """
155+ ) ) ;
156+ }
157+
158+ // report any projects that failed to build with .NET Core
159+ if ( autoBuildRule . DotNetRule . FailedProjectsOrSolutions . Any ( ) )
160+ {
161+ this . AddDiagnostic ( new (
162+ this . Options . Language ,
163+ "dotnet-build-failure" ,
164+ "Some projects or solutions failed to build using .NET Core" ,
165+ markdownMessage : $ """
166+ CodeQL was unable to build the following projects using .NET Core:
167+
168+ { autoBuildRule . DotNetRule . FailedProjectsOrSolutions . Select ( p => this . MakeRelative ( p . FullPath ) ) . ToMarkdownList ( MarkdownUtil . CodeFormatter , 10 ) }
169+
170+ Set up a [manual build command]({ buildCommandDocsUrl } ).
171+ """
172+ ) ) ;
173+ }
174+
175+ // report any projects that failed to build with MSBuild
176+ if ( autoBuildRule . MsBuildRule . FailedProjectsOrSolutions . Any ( ) )
177+ {
178+ this . AddDiagnostic ( new (
179+ this . Options . Language ,
180+ "msbuild-build-failure" ,
181+ "Some projects or solutions failed to build using MSBuild" ,
182+ markdownMessage : $ """
183+ CodeQL was unable to build the following projects using MSBuild:
184+
185+ { autoBuildRule . MsBuildRule . FailedProjectsOrSolutions . Select ( p => this . MakeRelative ( p . FullPath ) ) . ToMarkdownList ( MarkdownUtil . CodeFormatter , 10 ) }
186+
187+ Set up a [manual build command]({ buildCommandDocsUrl } ).
188+ """
189+ ) ) ;
190+ }
191+ }
192+
117193 /// <summary>
118194 /// Gets the build strategy that the autobuilder should apply, based on the
119195 /// options in the `lgtm.yml` file.
0 commit comments