1
1
using Semmle . Extraction . CSharp ;
2
2
using Semmle . Util . Logging ;
3
3
using Semmle . Autobuild . Shared ;
4
+ using Semmle . Util ;
5
+ using System . Linq ;
4
6
5
7
namespace Semmle . Autobuild . CSharp
6
8
{
@@ -29,25 +31,16 @@ public CSharpAutobuildOptions(IBuildActions actions) : base(actions)
29
31
30
32
public class CSharpAutobuilder : Autobuilder < CSharpAutobuildOptions >
31
33
{
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" ;
33
36
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 ;
44
38
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 ) ;
50
41
42
+ public override BuildScript GetBuildScript ( )
43
+ {
51
44
var attempt = BuildScript . Failure ;
52
45
switch ( GetCSharpBuildStrategy ( ) )
53
46
{
@@ -65,47 +58,9 @@ BuildScript CheckExtractorRun(bool warnOnFailure) =>
65
58
attempt = new DotNetRule ( ) . Analyse ( this , false ) & CheckExtractorRun ( true ) ;
66
59
break ;
67
60
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
-
102
61
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 ) |
109
64
// All attempts failed: print message
110
65
AutobuildFailure ( ) ;
111
66
break ;
@@ -114,6 +69,127 @@ BuildScript IntermediateAttempt(BuildScript s) =>
114
69
return attempt ;
115
70
}
116
71
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
+
117
193
/// <summary>
118
194
/// Gets the build strategy that the autobuilder should apply, based on the
119
195
/// options in the `lgtm.yml` file.
0 commit comments