44using System . Linq ;
55using System . Text ;
66using BenchmarkDotNet . Characteristics ;
7+ using BenchmarkDotNet . Environments ;
78using BenchmarkDotNet . Extensions ;
89using BenchmarkDotNet . Jobs ;
910using BenchmarkDotNet . Loggers ;
@@ -32,6 +33,9 @@ public class DotNetCliCommand
3233
3334 [ PublicAPI ] public bool LogOutput { get ; }
3435
36+ // Whether to use ArtifactsPath or IntermediateOutputPath. ArtifactsPath is only supported in dotnet sdk 8+.
37+ private readonly bool _useArtifactsPath ;
38+
3539 public DotNetCliCommand ( string cliPath , string arguments , GenerateResult generateResult , ILogger logger ,
3640 BuildPartition buildPartition , IReadOnlyList < EnvironmentVariable > environmentVariables , TimeSpan timeout , bool logOutput = false )
3741 {
@@ -43,6 +47,8 @@ public DotNetCliCommand(string cliPath, string arguments, GenerateResult generat
4347 EnvironmentVariables = environmentVariables ;
4448 Timeout = timeout ;
4549 LogOutput = logOutput || ( buildPartition is not null && buildPartition . LogBuildOutput ) ;
50+
51+ _useArtifactsPath = DotNetCliCommandExecutor . DotNetSdkSupportsArtifactsPath ( cliPath ) ;
4652 }
4753
4854 public DotNetCliCommand WithArguments ( string arguments )
@@ -71,12 +77,12 @@ public BuildResult RestoreThenBuild()
7177 if ( BuildPartition . ForcedNoDependenciesForIntegrationTests )
7278 {
7379 var restoreResult = DotNetCliCommandExecutor . Execute ( WithArguments (
74- GetRestoreCommand ( GenerateResult . ArtifactsPaths , BuildPartition , $ "{ Arguments } --no-dependencies", "restore-no-deps" , excludeOutput : true ) ) ) ;
80+ GetRestoreCommand ( GenerateResult . ArtifactsPaths , BuildPartition , _useArtifactsPath , $ "{ Arguments } --no-dependencies", "restore-no-deps" , excludeOutput : true ) ) ) ;
7581 if ( ! restoreResult . IsSuccess )
7682 return BuildResult . Failure ( GenerateResult , restoreResult . AllInformation ) ;
7783
7884 return DotNetCliCommandExecutor . Execute ( WithArguments (
79- GetBuildCommand ( GenerateResult . ArtifactsPaths , BuildPartition , $ "{ Arguments } --no-restore --no-dependencies", "build-no-restore-no-deps" , excludeOutput : true ) ) )
85+ GetBuildCommand ( GenerateResult . ArtifactsPaths , BuildPartition , _useArtifactsPath , $ "{ Arguments } --no-restore --no-dependencies", "build-no-restore-no-deps" , excludeOutput : true ) ) )
8086 . ToBuildResult ( GenerateResult ) ;
8187 }
8288 else
@@ -130,59 +136,62 @@ public DotNetCliCommandResult AddPackages()
130136
131137 public DotNetCliCommandResult Restore ( )
132138 => DotNetCliCommandExecutor . Execute ( WithArguments (
133- GetRestoreCommand ( GenerateResult . ArtifactsPaths , BuildPartition , Arguments , "restore" ) ) ) ;
139+ GetRestoreCommand ( GenerateResult . ArtifactsPaths , BuildPartition , _useArtifactsPath , Arguments , "restore" ) ) ) ;
134140
135141 public DotNetCliCommandResult Build ( )
136142 => DotNetCliCommandExecutor . Execute ( WithArguments (
137- GetBuildCommand ( GenerateResult . ArtifactsPaths , BuildPartition , Arguments , "build" ) ) ) ;
143+ GetBuildCommand ( GenerateResult . ArtifactsPaths , BuildPartition , _useArtifactsPath , Arguments , "build" ) ) ) ;
138144
139145 public DotNetCliCommandResult BuildNoRestore ( )
140146 => DotNetCliCommandExecutor . Execute ( WithArguments (
141- GetBuildCommand ( GenerateResult . ArtifactsPaths , BuildPartition , $ "{ Arguments } --no-restore", "build-no-restore" ) ) ) ;
147+ GetBuildCommand ( GenerateResult . ArtifactsPaths , BuildPartition , _useArtifactsPath , $ "{ Arguments } --no-restore", "build-no-restore" ) ) ) ;
142148
143149 public DotNetCliCommandResult Publish ( )
144150 => DotNetCliCommandExecutor . Execute ( WithArguments (
145- GetPublishCommand ( GenerateResult . ArtifactsPaths , BuildPartition , Arguments , "publish" ) ) ) ;
151+ GetPublishCommand ( GenerateResult . ArtifactsPaths , BuildPartition , _useArtifactsPath , Arguments , "publish" ) ) ) ;
146152
147153 // PublishNoBuildAndNoRestore was removed because we set --output in the build step. We use the implicit build included in the publish command.
148154 public DotNetCliCommandResult PublishNoRestore ( )
149155 => DotNetCliCommandExecutor . Execute ( WithArguments (
150- GetPublishCommand ( GenerateResult . ArtifactsPaths , BuildPartition , $ "{ Arguments } --no-restore", "publish-no-restore" ) ) ) ;
156+ GetPublishCommand ( GenerateResult . ArtifactsPaths , BuildPartition , _useArtifactsPath , $ "{ Arguments } --no-restore", "publish-no-restore" ) ) ) ;
151157
152158 internal static IEnumerable < string > GetAddPackagesCommands ( BuildPartition buildPartition )
153159 => GetNuGetAddPackageCommands ( buildPartition . RepresentativeBenchmarkCase , buildPartition . Resolver ) ;
154160
155- internal static string GetRestoreCommand ( ArtifactsPaths artifactsPaths , BuildPartition buildPartition , string ? extraArguments = null , string ? binLogSuffix = null , bool excludeOutput = false )
161+ internal static string GetRestoreCommand ( ArtifactsPaths artifactsPaths , BuildPartition buildPartition ,
162+ bool useArtifactsPath , string ? extraArguments = null , string ? binLogSuffix = null , bool excludeOutput = false )
156163 => new StringBuilder ( )
157164 . AppendArgument ( "restore" )
158165 . AppendArgument ( string . IsNullOrEmpty ( artifactsPaths . PackagesDirectoryName ) ? string . Empty : $ "--packages \" { artifactsPaths . PackagesDirectoryName } \" ")
159166 . AppendArgument ( GetCustomMsBuildArguments ( buildPartition . RepresentativeBenchmarkCase , buildPartition . Resolver ) )
160167 . AppendArgument ( extraArguments )
161168 . AppendArgument ( GetMandatoryMsBuildSettings ( buildPartition . BuildConfiguration ) )
162169 . AppendArgument ( GetMsBuildBinLogArgument ( buildPartition , binLogSuffix ) )
163- . MaybeAppendOutputPaths ( artifactsPaths , true , excludeOutput )
170+ . MaybeAppendOutputPaths ( artifactsPaths , useArtifactsPath , true , excludeOutput )
164171 . ToString ( ) ;
165172
166- internal static string GetBuildCommand ( ArtifactsPaths artifactsPaths , BuildPartition buildPartition , string ? extraArguments = null , string ? binLogSuffix = null , bool excludeOutput = false )
173+ internal static string GetBuildCommand ( ArtifactsPaths artifactsPaths , BuildPartition buildPartition ,
174+ bool useArtifactsPath , string ? extraArguments = null , string ? binLogSuffix = null , bool excludeOutput = false )
167175 => new StringBuilder ( )
168176 . AppendArgument ( $ "build -c { buildPartition . BuildConfiguration } ") // we don't need to specify TFM, our auto-generated project contains always single one
169177 . AppendArgument ( GetCustomMsBuildArguments ( buildPartition . RepresentativeBenchmarkCase , buildPartition . Resolver ) )
170178 . AppendArgument ( extraArguments )
171179 . AppendArgument ( GetMandatoryMsBuildSettings ( buildPartition . BuildConfiguration ) )
172180 . AppendArgument ( string . IsNullOrEmpty ( artifactsPaths . PackagesDirectoryName ) ? string . Empty : $ "/p:NuGetPackageRoot=\" { artifactsPaths . PackagesDirectoryName } \" ")
173181 . AppendArgument ( GetMsBuildBinLogArgument ( buildPartition , binLogSuffix ) )
174- . MaybeAppendOutputPaths ( artifactsPaths , excludeOutput : excludeOutput )
182+ . MaybeAppendOutputPaths ( artifactsPaths , useArtifactsPath , excludeOutput : excludeOutput )
175183 . ToString ( ) ;
176184
177- internal static string GetPublishCommand ( ArtifactsPaths artifactsPaths , BuildPartition buildPartition , string ? extraArguments = null , string ? binLogSuffix = null )
185+ internal static string GetPublishCommand ( ArtifactsPaths artifactsPaths , BuildPartition buildPartition ,
186+ bool useArtifactsPath , string ? extraArguments = null , string ? binLogSuffix = null )
178187 => new StringBuilder ( )
179188 . AppendArgument ( $ "publish -c { buildPartition . BuildConfiguration } ") // we don't need to specify TFM, our auto-generated project contains always single one
180189 . AppendArgument ( GetCustomMsBuildArguments ( buildPartition . RepresentativeBenchmarkCase , buildPartition . Resolver ) )
181190 . AppendArgument ( extraArguments )
182191 . AppendArgument ( GetMandatoryMsBuildSettings ( buildPartition . BuildConfiguration ) )
183192 . AppendArgument ( string . IsNullOrEmpty ( artifactsPaths . PackagesDirectoryName ) ? string . Empty : $ "/p:NuGetPackageRoot=\" { artifactsPaths . PackagesDirectoryName } \" ")
184193 . AppendArgument ( GetMsBuildBinLogArgument ( buildPartition , binLogSuffix ) )
185- . MaybeAppendOutputPaths ( artifactsPaths )
194+ . MaybeAppendOutputPaths ( artifactsPaths , useArtifactsPath )
186195 . ToString ( ) ;
187196
188197 private static string GetMsBuildBinLogArgument ( BuildPartition buildPartition , string suffix )
@@ -257,15 +266,22 @@ internal static class DotNetCliCommandExtensions
257266 // We force the project to output binaries to a new directory.
258267 // Specifying --output and --no-dependencies breaks the build (because the previous build was not done using the custom output path),
259268 // so we don't include it if we're building no-deps (only supported for integration tests).
260- internal static StringBuilder MaybeAppendOutputPaths ( this StringBuilder stringBuilder , ArtifactsPaths artifactsPaths , bool isRestore = false , bool excludeOutput = false )
269+ internal static StringBuilder MaybeAppendOutputPaths ( this StringBuilder stringBuilder , ArtifactsPaths artifactsPaths , bool useArtifactsPath , bool isRestore = false , bool excludeOutput = false )
261270 => excludeOutput
262271 ? stringBuilder
263272 : stringBuilder
264273 // Use AltDirectorySeparatorChar so it's not interpreted as an escaped quote `\"`.
265- . AppendArgument ( $ "/p:IntermediateOutputPath=\" { artifactsPaths . IntermediateDirectoryPath } { Path . AltDirectorySeparatorChar } \" ")
274+ . AppendArgument ( useArtifactsPath
275+ // We set ArtifactsPath for dotnet sdk 8+, fallback to IntermediateOutputPath for older sdks.
276+ ? $ "/p:ArtifactsPath=\" { artifactsPaths . BuildArtifactsDirectoryPath } { Path . AltDirectorySeparatorChar } \" "
277+ // This is technically incorrect (#2664, #2425), but it's the best we can do for older sdks.
278+ // MSBuild does not support setting BaseIntermediateOutputPath from command line. https://github.com/dotnet/sdk/issues/2003#issuecomment-369408964
279+ : $ "/p:IntermediateOutputPath=\" { artifactsPaths . IntermediateDirectoryPath } { Path . AltDirectorySeparatorChar } \" "
280+ )
266281 . AppendArgument ( $ "/p:OutDir=\" { artifactsPaths . BinariesDirectoryPath } { Path . AltDirectorySeparatorChar } \" ")
267282 // OutputPath is legacy, per-project version of OutDir. We set both just in case. https://github.com/dotnet/msbuild/issues/87
268283 . AppendArgument ( $ "/p:OutputPath=\" { artifactsPaths . BinariesDirectoryPath } { Path . AltDirectorySeparatorChar } \" ")
284+ . AppendArgument ( $ "/p:PublishDir=\" { artifactsPaths . PublishDirectoryPath } { Path . AltDirectorySeparatorChar } \" ")
269285 . AppendArgument ( isRestore ? string . Empty : $ "--output \" { artifactsPaths . BinariesDirectoryPath } { Path . AltDirectorySeparatorChar } \" ") ;
270286 }
271287}
0 commit comments