I've got a nice power-shell driven post-build script that does some magic mumbo-jumbo for pulling all our plugins into the correct location after they're compiled and sorting out thier dependencies with the master-project of the solution.
My problem is, occasionally when I do something stupid, I can end up in a state where my script can't execute it's operation correctly, and throws an exception in powershell with the details of what went wrong.
Is there a way to get these exceptions to pull up to the Visual Studio Error window so that when my post-build fails, vstudio gets a failure notification and nicely formats it in the window alongside all the other warnings/errors?
Edit: This is a clarification to what I'm ideally looking for.
removed dead ImageShack link
Note: If you've landed here looking for how to make custom error messages show up in Visual Studio (when you have control of the error messages) you can see Roy Tinker's Answer to learn how to tailor your messages to show up. If you're interested in catching unexpected errors you can't control, or finding a more complete solution please see the accepted answer.
To create an error, warning, or message that will appear in the Error List window, simply log a message to stdout or stderr in the following format from a script initiated by a pre- or post-build event. Please comment or edit if there is an official spec; this is only what I was able to deduce by trial-and-error and by watching the output of MSBuild. Square brackets denote "optional":
FilePath[(LineNumber[,ColumnNumber])]: MessageType[ MessageCode]: Description
As an example:
E:\Projects\SampleProject\Program.cs(18,5): error CS1519: Invalid token '}' in class, struct, or interface member declaration
For more examples, see the Output window for any error/warning/message that may occur when you run a build in Visual Studio.
Here are two approaches for interrupting the build when a PowerShell script errors.
Use exit() to terminate the PowerShell process
To return a status code from the script which, if non-zero, will show up in the error list, use the following:
exit(45) # or any other non-zero number, for that matter.
This doesn't precisely get the error text onto your error list, but it does terminate the script and get a little something in your error list to indicate which pre- or post-build command failed.
Use a custom MSBuild task to execute the PowerShell script
I spent a little time working out the details of executing a PowerShell script within an MSBuild task. I have a full article with sample code on my blog. Do check it out, as it includes more discussion, and some explanation of how to handle the sample projects. It's probably not a complete or perfect solution, but I got it Working on My MachineTM with a very simple script.
This approach provides line and column precision when reporting PowerShell errors, and even supports the double-click-takes-me-to-the-file behavior we're accustomed to in Visual Studio. If it's lacking, I'm sure you'll be able to extend it to meet your needs. Also, depending on your version of Visual Studio, you may need to massage details like assembly reference versions.
First off, build a custom MSBuild Task in a class library project. The library should reference the following assemblies for MSBuild and PowerShell integration. (Note that this example requires PowerShell 2.0.)
Microsoft.Build.Framework (GAC)
Microsoft.Build.Utilities.v3.5 (GAC)
System.Management.Automation (from C:\Program Files\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0)
Build a task class, and expose a property to specify the path to the PowerShell script, like so:
using System.IO;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
public class PsBuildTask : Task
{
[Required]
public string ScriptPath { get; set; }
public override bool Execute()
{
// ...
}
}
Within the Execute() method, start the PowerShell run time, execute the script, and collect errors. Use the Log property to log the errors. When finished, close the runspace and return true if the script logged no errors.
// create Powershell runspace
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
// create a pipeline and feed it the script text
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(". " + ScriptPath);
// execute the script and extract errors
pipeline.Invoke();
var errors = pipeline.Error;
// log an MSBuild error for each error.
foreach (PSObject error in errors.Read(errors.Count))
{
var invocationInfo = ((ErrorRecord)(error.BaseObject)).InvocationInfo;
Log.LogError(
"Script",
string.Empty,
string.Empty,
new FileInfo(ScriptPath).FullName,
invocationInfo.ScriptLineNumber,
invocationInfo.OffsetInLine,
0,
0,
error.ToString());
}
// close the runspace
runspace.Close();
return !Log.HasLoggedErrors;
And that's it. With this assembly in hand, we can configure another project to consume the MSBuild task.
Consider, for example, a C#-based class library project (.csproj). Integrating the task in a post build event requires just a few things.
First, register the task just inside the <Project> node of the .csproj file like so:
<UsingTask TaskName="PsBuildTask"
AssemblyFile="..\Noc.PsBuild\bin\Debug\Noc.PsBuild.dll" />
TaskName should be the name of the task class, though it would seem the namespace is not required. AssemblyFile is an absolute path to the custom MSBuild task assembly, or relative path with respect to the .csproj file. For assemblies in the GAC, you can use the AssemblyName attribute instead.
Once registered, the task can be used within pre- and post-build events. Configure a build event within the <Project> element of the .csproj file like so:
<Target Name="AfterBuild">
<PsBuildTask ScriptPath=".\script.ps1" />
</Target>
And that's it. When Visual Studio compiles the project, it loads the custom assembly and task object and executes the task. Errors raised by the pipeline are retrieved and reported.
Related
I borrowed a Program using BinaryFormatter serialization.
An important task for me was to change it in XML serialization.
I developed a converter program which reads the via BinaryFormatter serialized file and saves it in XML.
The program works fine when it's laying in its own project and is executed by hand, but when I call it via the Visual Studio Installer Project it breaks with a SerializationException with the message "The Assembly XXX can not be found".
Why does it work when I execute it manually and automated I get this exception?
Can anybody give me a hint how to solve this issue?
Greets
Henrik
Is it possible to execute a target in an msbuild script from within webmatrix and have output parsed for errors in MSBuild format?
The short answer is no. WebMatrix doesn't support running msbuild or feeding errors into the error list.
However, WebMatrix 2.0 introduced an extensibility model, and some of the team members are working on a console extension, this will allow you to run any command line from within WebMatrix (but still not parse and show results).
Another approach could be to write a full extension that will run the msbuild script, there is support to add tabs to the task tabs, where you can create for example a build error tab, and feed the errors there.
Here are some links for reference:
Defining a task tab
, Adding a task tab
Note that to get access to the task tab you will need to import it through MEF in your extension class, something like the following code:
[Import(typeof(IEditorTaskPanelService))]
IEditorTaskPanelService TaskPanelService {get; set;}
As said in the comment to the answer of Yishai I wrote the functionality myself. It is open source and available as a NuGet package. Have a look at http://macawnl.github.com/WebMatrix.Executer/.
Some screenshots:
It is as easy as one initialization call and you can start executing any command or PowerShell script.
Let me know what you think of it!
For some reason in VS2010 I can't embed any file into an assembly. These files have their Build Action property set to Embedded Resource as they should, but when the assembly is executed it founds no resources. For example, the following test code:
string[] list = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceNames();
string msg = (list.Length > 0) ? "Full" : "Empty";
MessageBox.Show(msg);
always shows "Empty" because GetManifestResourceNames() always returns an empty list.
This issue affects a project using nettiers in which is not convenient for that particular case to include the stored procedures into the DB, so they must be taken from the Procedures.xml file, which BTW is automatically set with Build Action= Embedded Resource after the classes generation, and then of course when a function tries to get any SQL script from it, the program fails.
I also created a couple of test projects where I tried to embed an xml and a gif, always with no success.
Update: The problem seems to be only for C#. I reproduced the test project in VB.Net and there the file does get embedded. I've noticed that in the output window, in the C# case, the compiler command line doesn't include the /resource option (which should be there followed with the name of the file to embed) whereas for the VB case it does appear.
Are there any global settings or configuration files anywhere for C# I should check? (The build options for the project have nothing strange there)
Does anyone have an idea of what may be happening and how to fix this?
Finally, I found the cause:
There was a wrong line inside the Microsoft.CSharp.targets file (located inside c:\WINDOWS\Microsoft.NET\Framework\v4.0.30319). There was a tag written as:
<EmbeddedResourceF Include="#(_Temporary)" />
instead of
<EmbeddedResource Include="#(_Temporary)" />
Writing it right restored the ability to embed files
How that F appeared and why that file got altered in first place, I dont know... Despite being a .NET programmer for several years I just became aware today of the ".targets" files after searching the web about this issue, as never have had the need to look about the MSBuild stuff.
I have a solution with several projects in it that executes many custom build steps. Some projects depend on other projects, but most of the build steps are independent of each other.
When building inside the VS 2010 IDE, I am getting errors like this:
error MSB6003: The specified task executable "cmd.exe" could not be run. The process cannot access the file 'C:\full\path\Debug\custombuild.write.1.tlog' because it is being used by another process
However, when I build the solution with MSBuild from the command line, all is well, and the log file writing does not seem to cause the same error.
Is this a known issue? Google has not been much help today...
The answer was hinted at in an MSBuild forum thread.
The custom build rule logs are written into the containing project's $(IntDir). We had multiple projects (with no real output being sent to IntDir) that all inadvertently shared the same IntDir value. Giving each project its own IntDir value eliminated the problem.
Part of the post build on my project is the execution of a program I wrote for testing the main application. Unfortunately, the post build process in visual studio locks up waiting for the executable to exit. So, i'm stuck closing my test program in order to have the post build process complete and my application run. How do I change this so VS does not wait for the program to return before launching? Thanks.
I also found that the start trick didn't work and downloading a separate tool to complete such a simple task seemed excessive. After some additional searching I found this SO post which gave an answer that worked for my needs.
Just replace <actual-command-line-to-run> with your command. Remember to give the full path to the executable and encapsulate it in "quotes" if there's spaces in your path.
powershell start-process <actual-command-line-to-run>
Wow, seems VS is really stubborn about this.
You can use this little tool that can launch apps without showing cmd windows (among other things). In your post build event:
c:\path\to\cmdow /run app.exe
This seems to be a known issue of VS 2010 (e.g. here and here) and it seems it won't be fixed that soon.
Regarding a possible workaround, similar to the one mentioned by #RichieHindle, one of the MS Connect persons suggests:
START /WAIT cmd /c YourPostBuildTool.exe
Running your test program via start might work. Change your post build step from this:
runtest.exe
to this:
start runtest.exe
If you utilize cruise control for your builds, you could place this in the publishers section which allows the build to finish before running the publisher tasks.
Additionally a custom msbuild task is quite trivial to build, if you know how to spin off a process in .net then it's really simple to do in msbuild. To do this you could edit your .csproj file or your .proj build script to use that custom task.
using System;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
namespace MyTasks
{
public class SimpleTask : Task
{
public override bool Execute()
{
//something involving Process.Start
return true;
}
}
}
Then in your build script or csproj file you add the using for the task you created and call it.
I uploaded a patch to MSBuild Extension Pack that implements a custom msbuild task called SmartExec that works around this issue.
http://msbuildextensionpack.codeplex.com/workitem/9053
http://msbuildextensionpack.codeplex.com/SourceControl/list/patches
Patch Id 9878
I spent a considerable amount of time trying to figure this out. What you need to do is place "cmd" as the first line of your post-build event.
An example might look like this:
cmd
xcopy /Y $(ProjectDir)$(OutputDir)* C:\SomePath\*