Can Exec task in msbuild fail if exitCode = 0 but there is logging to standard error? - msbuild

I am curious about:
https://learn.microsoft.com/en-us/visualstudio/msbuild/exec-task?view=vs-2019
ExitCode Optional Int32 output read-only parameter.
Specifies the exit code that is provided by the executed command, except that if the task logged any errors, but the process had an exit code of 0 (success), ExitCode is set to -1.
Can msbuild fail on Exec even if process has an exit code of 0 but it logs to standard error?
I tried to make it fail with the following code:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp3
{
class Program
{
static int Main( string[] args )
{
using ( var stdErr = Console.Error )
{
for ( int i = 0; i <= 100; i++ )
{
stdErr.WriteLine( $"Failed to copy attempt {i}." );
System.Threading.Thread.Sleep( 50 );
}
// Close redirected error stream.
Console.Error.Close();
}
return 0;
}
}
}
It is being called as part of a standard props file.
<Target
Name = "IvaraSign"
AfterTargets="CoreBuild"
BeforeTargets="CopyFilesToOutputDirectory" >
<!-- Update version on main output -->
<!-- this works when the solution being compiled is in the dotnet folder. -->
<Exec Command="C:\Users\Derek.Morin\source\repos\ConsoleApp3\bin\Debug\ConsoleApp3.exe"/>
</Target>
When I build in msbuild I can see the error text in the build output but the compiling passes.

Does the MSBuild Exec task search STDOUT for the string "error"?
I was able to duplicate making exec file fail with the following
static int Main( string[] args )
{
using ( var stdErr = Console.Error )
{
stdErr.WriteLine( $#"signtool.exe : error information: ""Error: Store IsDiskFile() failed."" (-2147024864/0x80070020) [d:\work\1\s\common\MAFPlugins\MAFScripting\Contracts\Contracts.csproj]" );
}
Console.WriteLine( "but I'm still going to return 0" );
return 0;
}
The workaround is to use IgnoreStandardErrorWarningFormat="true".
<Exec Command="C:\ConsoleApp3\ConsoleApp3.exe" IgnoreStandardErrorWarningFormat="true" />

Related

How to load .NET 6 project in a .NET 6 console application

I am attempting to load a .NET 6 project (SDK Style) in a .NET 6 console application. My entire project is fairly simple - it actually attempts to load its own .csproj file when it runs from the default output folder:
using Microsoft.Build.Evaluation;
namespace ProjLoading
{
internal class Program
{
static void Main(string[] args)
{
var projectLocation = "../../../ProjLoading.csproj";
var project = new Project(projectLocation, null, null, new ProjectCollection());
}
}
}
I am using the following nuget packages:
Microsoft.Build (17.2.0)
Microsoft.Build.Utilities.Core (17.2.0)
When I run the code, I get the following exception:
Microsoft.Build.Exceptions.InvalidProjectFileException: 'The SDK 'Microsoft.NET.Sdk' specified could not be found. C:\Users\vchel\source\repos\ProjLoading\ProjLoading\ProjLoading.csproj'
This exception was originally thrown at this call stack:
Microsoft.Build.Shared.ProjectErrorUtilities.ThrowInvalidProject(string, Microsoft.Build.Shared.IElementLocation, string, object[])
Microsoft.Build.Evaluation.Evaluator<P, I, M, D>.ExpandAndLoadImportsFromUnescapedImportExpressionConditioned(string, Microsoft.Build.Construction.ProjectImportElement, out System.Collections.Generic.List<Microsoft.Build.Construction.ProjectRootElement>, out Microsoft.Build.BackEnd.SdkResolution.SdkResult, bool)
Microsoft.Build.Evaluation.Evaluator<P, I, M, D>.ExpandAndLoadImports(string, Microsoft.Build.Construction.ProjectImportElement, out Microsoft.Build.BackEnd.SdkResolution.SdkResult)
Microsoft.Build.Evaluation.Evaluator<P, I, M, D>.EvaluateImportElement(string, Microsoft.Build.Construction.ProjectImportElement)
Microsoft.Build.Evaluation.Evaluator<P, I, M, D>.PerformDepthFirstPass(Microsoft.Build.Construction.ProjectRootElement)
Microsoft.Build.Evaluation.Evaluator<P, I, M, D>.Evaluate()
Microsoft.Build.Evaluation.Project.ProjectImpl.Reevaluate(Microsoft.Build.BackEnd.Logging.ILoggingService, Microsoft.Build.Evaluation.ProjectLoadSettings, Microsoft.Build.Evaluation.Context.EvaluationContext)
Microsoft.Build.Evaluation.Project.ProjectImpl.ReevaluateIfNecessary(Microsoft.Build.BackEnd.Logging.ILoggingService, Microsoft.Build.Evaluation.ProjectLoadSettings, Microsoft.Build.Evaluation.Context.EvaluationContext)
Microsoft.Build.Evaluation.Project.ProjectImpl.Initialize(System.Collections.Generic.IDictionary<string, string>, string, string, Microsoft.Build.Evaluation.ProjectLoadSettings, Microsoft.Build.Evaluation.Context.EvaluationContext)
Microsoft.Build.Evaluation.Project.Project(string, System.Collections.Generic.IDictionary<string, string>, string, string, Microsoft.Build.Evaluation.ProjectCollection, Microsoft.Build.Evaluation.ProjectLoadSettings, Microsoft.Build.Evaluation.Context.EvaluationContext, Microsoft.Build.FileSystem.IDirectoryCacheFactory)
...
[Call Stack Truncated]
I am building/running this console application from Visual Studio 2022 (17.2.2).
How can I solve this problem?
I don't understand why, but I ran across this solution and it has solved the problem:
https://blog.rsuter.com/missing-sdk-when-using-the-microsoft-build-package-in-net-core/
In case the link dies in the future, my full project now sets the environment variable MSBUILD_EXE_PATH to the latest version of msbuild as shown in the following code:
using Microsoft.Build.Evaluation;
using System.Diagnostics;
using System.Text.RegularExpressions;
namespace ProjLoading
{
internal class Program
{
static void Main(string[] args)
{
var startInfo = new ProcessStartInfo("dotnet", "--list-sdks")
{
RedirectStandardOutput = true
};
var process = Process.Start(startInfo);
process.WaitForExit(1000);
var output = process.StandardOutput.ReadToEnd();
var sdkPaths = Regex.Matches(output, "([0-9]+.[0-9]+.[0-9]+) \\[(.*)\\]")
.OfType<Match>()
.Select(m => System.IO.Path.Combine(m.Groups[2].Value, m.Groups[1].Value, "MSBuild.dll"));
var sdkPath = sdkPaths.Last();
Environment.SetEnvironmentVariable("MSBUILD_EXE_PATH", sdkPath);
var projectLocation = "../../../ProjLoading.csproj";
var project = new Project(projectLocation, null, null, new ProjectCollection());
}
}
}

Visual Studio 2019 .NET Core 3 gRpc not generating service files

I created a simple gRpc server application using the template in Visual Studio 2019. I then created a protobuf file for the server but I cannot get it to generate the corresponding service files.
The protobuf file is:
syntax = "proto3";
option csharp_namespace = "RpcApi";
import "google/protobuf/timestamp.proto";
package API;
service Conferences {
rpc GetAll (GetAllConferencesRequest) returns (GetAllConferencesResponse) {}
rpc Add (AddConferenceRequest) returns (AddConferenceResponse) {}
}
message GetAllConferencesRequest {}
message GetAllConferencesResponse {
repeated Conference Conferences = 1;
}
message AddConferenceRequest {
Conference conferernce = 1;
}
message AddConferenceResponse {
Conference conference = 1;
}
message Conference {
int32 Id = 1;
string Name = 2;
google.protobuf.Timestamp Start = 3;
string Location = 4;
int32 AttendeeTotal = 5;
}
service Proposals {
rpc GetAll (GetAllProposalsRequest) returns (GetAllProposalsResponse) {}
rpc Add (AddProposalRequest) returns (AddProposalResponse) {}
rpc Approve (ApproveRequest) returns (ApproveResponse) {}
}
message GetAllProposalsRequest {
int32 ConferenceId = 1;
}
message GetAllProposalsResponse {
repeated Proposal Proposals = 1;
}
message AddProposalRequest {
Proposal Proposal = 1;
}
message AddProposalResponse {
Proposal Proposal = 1;
}
message ApproveRequest {
int32 Id = 1;
}
message ApproveResponse {
Proposal Proposal = 1;
}
message Proposal {
int32 Id = 1;
int32 ConferenceId = 2;
string Speaker = 3;
string Title = 4;
bool Approved = 5;
}
And the project file is correctly defined:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Protobuf Include="Protos\rpcapi.proto" GrpcServices="Server" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Grpc.AspNetCore" Version="2.23.1" />
</ItemGroup>
<ItemGroup>
<Folder Include="Services\" />
</ItemGroup>
</Project>
Its supposed to generate server-side files whenever the proto file changes but it never generates those files. What is wrong?
It actually works. The files are generated in the obj directory and not the Services directory. If I do Show All Files, I can see them.

Read 'hidden' input for CLI Dart app

What's the best way to receive 'hidden' input from a command-line Dart application? For example, in Bash, this is accomplished with:
read -s SOME_VAR
Set io.stdin.echoMode to false:
import 'dart:io' as io;
void main() {
io.stdin.echoMode = false;
String input = io.stdin.readLineSync();
// or
var input;
while(input != 32) {
input = io.stdin.readByteSync();
if(input != 10) print(input);
}
// restore echoMode
io.stdin.echoMode = true;
}
This is a slightly extended version, key differences are that it uses a finally block to ensure the mode is reset if an exception is thrown whilst the code is executing.
The code also uses a waitFor call (only available in dart cli apps) to turn this code into a synchronous call. Given this is a cli command there is no need for the complications that futures bring to the table.
The code also does the classic output of '*' as you type.
If you are doing much cli work the below code is from the dart package I'm working on called dcli. Have a look at the 'ask' method.
https://pub.dev/packages/dcli
String readHidden() {
var line = <int>[];
try {
stdin.echoMode = false;
stdin.lineMode = false;
int char;
do {
char = stdin.readByteSync();
if (char != 10) {
stdout.write('*');
// we must wait for flush as only one flush can be outstanding at a time.
waitFor<void>(stdout.flush());
line.add(char);
}
} while (char != 10);
} finally {
stdin.echoMode = true;
stdin.lineMode = true;
}
// output a newline as we have suppressed it.
print('');
return Encoding.getByName('utf-8').decode(line);
}

How to have MSBuild quiet output but with error/warning summary

I'm simply trying to get no output from a build except the error/warning summary at the end. Not exactly a demanding task.
The command line:
msbuild.exe /nologo /verbosity:quiet /consoleloggerparameters:summary project.sln
As described here: http://msdn.microsoft.com/en-us/library/ms164311.aspx
It appears MSBuild isn't working as it should - there is no output at all. with /verbosity:normal there is tonnes of output and a useful error/warning summary at the end, is there any way of just not seeing the noise?
MSBuild reports version 12.0.21005.1 as distributed with Studio Express 2013.
late to the party, but MSBuild has the option /verbosity:quiet now and it doesn't print out anything beside error and warnings.
You can specify the following verbosity levels:
q[uiet], m[inimal], n[ormal], d[etailed], and diag[nostic]
Documentation source: https://msdn.microsoft.com/en-us/library/ms164311.aspx
Use /consoleloggerparameters:ErrorsOnly or clp:ErrorsOnly to solve your problem.
I don't think there is a set of options thet matches what you want exactly. But since you're on the commandline anyway, using findstr/grep/tail and the likes is always a good option. Here's an example using powershell to display the summary and what comes after it
powershell -Command "msbuild.exe /nologo project.sln |
Select-String 'Build succeeded|failed' -Context 0, 100"
Another possibility is to use a custom logger, which is not hard as it sounds at first and there are tons of examples on the net. Plus it has the benefit you can get any custom output you want. Here's code to replicate the summary:
using System;
using Microsoft.Build.Utilities;
using Microsoft.Build.Framework;
public class CustomLogger : Logger
{
private int warnings = 0;
private int errors = 0;
public override void Initialize( IEventSource eventSource )
{
eventSource.WarningRaised += ( s, e ) => ++warnings;
eventSource.ErrorRaised += ( s, e ) => ++errors;
eventSource.BuildFinished += ( s, e ) =>
{
Console.WriteLine( errors == 0 ? "Build succeeded." : "Build failed." );
Console.WriteLine( String.Format( " {0} Warning(s)", warnings ) );
Console.WriteLine( String.Format( " {0} Error(s)", errors ) );
};
}
}
Put this in a file CustomLogger.cs and compile it:
csc /t:library CustomLogger.cs /reference:Microsoft.Build.Utilities.v4.0.dll;Microsoft.Build.Framework.dll
which creates a CustomLogger dll file. Now use it like this:
msbuild /nologo /logger:CustomLogger.dll /noconsolelogger project.sln
Use /verbosity:minimal instead. This prints much less, but not nothing.

How to flush invalid SPWebConfigModifications

How do I flush out invalid SPWebConfigModifications?
I tried to execute some invalid modifications as part of a solution and now I cannt get rid of them, everytime I run ApplyWebConfigModifications it tries to execute the invalid modifications.
How do flush them out of the system?
For future reference (after banging my head on the wall for 3 days):
You can use this tool:
http://ianankers.wordpress.com/2011/07/14/web-config-modification-manager-for-sharepoint-2010/
It will list all the mods for every WebApp installed in your farm, you can add new ones and remove old ones.
The tool will only list modifications at webapp level, if you installed mods at the farm level you need to run a script like:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Navigation;
using Microsoft.SharePoint.Administration;
namespace ModTool
{
class Program
{
static void Main(string[] args)
{
SPSite site = new SPSite(args[0]);
SPWebService service = site.WebApplication.Farm.Services.GetValue<SPWebService>();
if (args.Length == 1 || string.IsNullOrEmpty(args[1]))
{
Console.Out.WriteLine("Listing all Mods and Owners");
foreach (SPWebConfigModification mod in service.WebConfigModifications)
{
Console.Out.WriteLine("Mod:" + mod.Name + ", Owner:" + mod.Owner);
}
}
else
{
Console.Out.WriteLine("Removing all mods owner:" + args[1] + ", reference site:" + args[0]);
List<SPWebConfigModification> toDelete = new List<SPWebConfigModification>();
foreach (SPWebConfigModification mod in service.WebConfigModifications)
{
if (mod.Owner == args[1])
{
toDelete.Add(mod);
}
}
Console.Out.WriteLine("Found " + toDelete.Count + "Mods");
foreach (SPWebConfigModification mod in toDelete)
{
service.WebConfigModifications.Remove(mod);
}
service.Update();
SPWebService.ContentService.ApplyWebConfigModifications();
Console.Out.WriteLine("Done!!");
}
}
}
}
Usage:
ModTool http://site - List all the mods for the farm, site is just an entry point
ModTool http://site owner -Deletes all the mods for the far wich owner is "owner"