Is there a way to output the Build order to a text file via command line?
To explain: We use multiple branches of source and have large solutions of 100+ projects on each branch. I need to write build scripts to build these solutions from command line. We can then tailor the solutions on the branches to only have project references for the projects that team are working on. This should greatly increase solution load time and ease the frustration of the Developers and me, I hope :)
I'm going to keep looking and maybe look at using C# and the APIs provided with VS. We are using 2012 update 1.
This is a good candidate for a Visual Studio Plugin project.
Create a new Visual Studio Add-in project.
In the project creation wizard make sure you choose the following configuration in the Choose Add-in Options step (the other steps are not important, I'm assuming you'll use C#):
In the Connect.cs file, add the following fields:
private BuildEvents _buildEvents;
private Events _events;
private bool buildEventConnected = false;
And add / modify these methods accordingly:
public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
{
_applicationObject = (DTE2)application;
_addInInstance = (AddIn)addInInst;
_events = _applicationObject.Events;
_buildEvents = _events.BuildEvents;
if (connectMode != ext_ConnectMode.ext_cm_UISetup && !buildEventConnected)
{
_buildEvents.OnBuildDone +=
new _dispBuildEvents_OnBuildDoneEventHandler(BuildEvents_OnBuildDone);
buildEventConnected = true;
}
}
private void BuildEvents_OnBuildDone(vsBuildScope Scope, vsBuildAction Action)
{
const string BUILD_OUTPUT_PANE_GUID = "{1BD8A850-02D1-11D1-BEE7-00A0C913D1F8}";
TextDocument txtOutput = default(TextDocument);
TextSelection txtSelection = default(TextSelection);
Window vsWindow = default(Window);
vsWindow = _applicationObject.Windows.Item(EnvDTE.Constants.vsWindowKindOutput);
OutputWindow vsOutputWindow = default(OutputWindow);
OutputWindowPane objBuildOutputWindowPane = default(OutputWindowPane);
vsOutputWindow = (OutputWindow)vsWindow.Object;
foreach (OutputWindowPane objOutputWindowPane in vsOutputWindow.OutputWindowPanes)
{
if (objOutputWindowPane.Guid.ToUpper() == BUILD_OUTPUT_PANE_GUID)
{
objBuildOutputWindowPane = objOutputWindowPane;
break;
}
}
txtOutput = objBuildOutputWindowPane.TextDocument;
txtSelection = txtOutput.Selection;
txtSelection.StartOfDocument(false);
txtSelection.EndOfDocument(true);
objBuildOutputWindowPane.OutputString(System.DateTime.Now.ToString());
txtSelection = txtOutput.Selection;
var solutionDir = System.IO.Path.GetDirectoryName(_applicationObject.Solution.FullName);
System.IO.File.WriteAllText(solutionDir + "\\build_output.log", txtSelection.Text);
}
public void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom)
{
if (buildEventConnected)
{
_buildEvents.OnBuildDone -= new _dispBuildEvents_OnBuildDoneEventHandler(BuildEvents_OnBuildDone);
buildEventConnected = false;
}
}
That's it, on every build you'll have the output sent to the build_output.log file in your solution's folder.
Quick way is to do a "Clean Solution" so that you can see the inverted order in build log.
Related
Good Morning,
Introduction:
My problem is about C# dll and VB6. And it refers to the reason of why I can't see the values of the C# properties, but I can see the same values of those properties in my VB6 project.
Explanation:
I created a dll file and a tlb file from a Windows Form Application with VS19 (Visual Studio 2019).
I moved into Properties -> Application and I changed the "Output type" from "Windows application" to "Class Library". After, I clicked in "Assembly information" and I checked the COM visible voice at the bottom of the window that appeared when I clicked.
After that I moved in "Compilation" and checked the "Interop COM" checkbox at the bottom.
In the end I compile the solution and moved the two files in a Virtual Machine (Win XP 32bit SP3), created with Virtual Box. where there's my application developed in VB6.
In my VB6 project I can set and use properties and method of the .NET dll and I see the values (EOL and EOLs are my .NET classes).
Like this:
Vin_car_11 = Mid$(frmInput.txtVinCode.Text, 11, 1)
Select Case Vin_car_11
Case "2"
EOL.XMLPrdWsUrl = "URL" 'Brescia
Case "5"
EOL.XMLPrdWsUrl = "URL" 'Suzzara
Case "9"
EOL.XMLPrdWsUrl = "URL" 'Bolzano
End Select
EOL.Vin = frmInput.txtVinCode.Text
EOL.Van = frmInput.txtVanCode.Text
returnCode = EOLs.GetXMLFile()
Here the problem:
Why I see the value of "EOL.vin" in VB6, but I can't see the same value in C#?
Here the code of my EOL class:
public class cEOL
{
private string vin;
public string Vin
{
get
{
return this.vin;
}
set
{
this.vin = value;
}
}
}
Here the code of my EOLs class:
public class cEOLs
{
#region DICHIARAZIONI
Vehicle.AuthHeader auth;
cEOL EOL;
string result;
string errorDescr;
#endregion
#region COSTRUTTORE
public cEOLs()
{
EOL = new cEOL();
auth = new Vehicle.AuthHeader();
result = "";
errorDescr = "";
}
#endregion
#region METODO
public int GetXMLFile()
{
//chiave di autenticazione server rilasciato da EHSA (IVECO)
auth.AuthKey = "Key";
var client = new Vehicle.EOLClientsAPI4EXT { Url = EOL.XMLPrdWsUrl };
EOL.ReturnCode = client.GetProductionXML(EOL.Vin, EOL.Van, out result, out errorDescr);
EOL.Result = result;
EOL.ErrorDescr = errorDescr;
return EOL.ReturnCode;
}
#endregion
THANKS IN ADVANCE! I HOPE I WAS CLEAR IN THE EXPLANATION!
I resolved it by myself by simply creating a method that returns a string.
I recall this method in my VB6 project for setting the value of the property.
Here the method:
public string ImpostaVIN(string vin)
{
EOL.Vin = vin;
return EOL.Vin;
}
Here the call in VB6:
EOL.Vin = EOLs.ImpostaVIN(string)
I would like to create tags in the Visual Studio editor to insert all sorts of glyphs, adornments, text hightlightings, etc., based on line/column/length locations in the code.
I have been carefully reading the documentation walkthrough pages (https://learn.microsoft.com/en-us/visualstudio/extensibility/walkthrough-creating-a-margin-glyph?view=vs-2017 and related pages). Although a bit complex and hard to understand, it seems like the API is very much oriented on giving the means to analyse the code: it is able to give your code split into spans, with classifications, etc.
However, I have the "opposite" need: I already have the analysis done by my external analysis engine. And I already have a set of results to be displayed in the editor with line/column/length for each one. Like:
function "foo", located at line 345, column 1, length 3, and other fields containing information to be displayed,
variable "my_var", located at line 349, column 13, length 6, and other fields containing information to be displayed,
Is it possible to create tags in the Visual Studio editor directly based on their line/column/length location? Any hint, any pointer to more detailed documentation or tutorial would be greatly appreciated.
Lance's link was quite helpful to understand another way to create tags different from the MS documentation example.
Indeed, I don't analyse the text contained into the spans, the analysis is already done outside. I get some list of "defects" locations.
I get them into a defectsLocation dictionary (defectsLocation[filename][line] = location data (...)
Here is was I did:
internal class MyDefectTagger : ITagger<MyDefectTag>
{
private IClassifier m_classifier;
private ITextBuffer m_buffer;
internal MyDefectTagger(IClassifier classifier, ITextBuffer buffer)
{
m_classifier = classifier;
m_buffer = buffer;
}
IEnumerable<ITagSpan<MyDefectTag>>
ITagger<MyDefectTag>.GetTags(NormalizedSnapshotSpanCollection spans)
{
if (MyModel.Instance == null || MyModel.Instance.defectsLocation == null)
{
yield return null;
}
var filename = GetFileName(m_buffer);
if (!MyModel.Instance.defectsLocation.ContainsKey(filename))
{
yield return null;
}
foreach (SnapshotSpan span in spans)
{
ITextSnapshot textSnapshot = span.Snapshot;
foreach (ITextSnapshotLine textSnapshotLine in textSnapshot.Lines)
{
var line = textSnapshotLine.LineNumber + 1; // Lines start at 1 in VS Editor
if (MyModel.Instance.defectsLocation[filename].ContainsKey(line) &&
!MyModel.Instance.defectsLocation[filename][line].rendered)
{
var rendered = MyModel.Instance.defectsLocation[filename][line].rendered;
yield return new TagSpan<MyDefectTag>(
new SnapshotSpan(textSnapshotLine.Start, 0),
new MyDefectTag()
);
}
}
}
}
}
I need to show popup use TextViewAdornment, it's require IWpfTextView.
There is old code to that:
private IWpfTextView GetWpfTextView(IVsTextView vTextView)
{
IWpfTextView view = null;
IVsUserData userData = vTextView as IVsUserData;
if (null != userData)
{
IWpfTextViewHost viewHost;
object holder;
Guid guidViewHost = DefGuidList.guidIWpfTextViewHost;
userData.GetData(ref guidViewHost, out holder);
viewHost = (IWpfTextViewHost)holder;
view = viewHost.TextView;
}
return view;
}
but when go to Visual studio 2017 Extension DefGuidList.guidIWpfTextViewHost is missing. So I cannot get IWpfTextView anymore.
Please help me.
Thank you everyone.
After Sergey Vlasov answer I found a solution:
private IWpfTextView GetWpfView()
{
var textManager = (IVsTextManager)ServiceProvider.GetService(typeof(SVsTextManager));
var componentModel = (IComponentModel)this.ServiceProvider.GetService(typeof(SComponentModel));
var editor = componentModel.GetService<IVsEditorAdaptersFactoryService>();
textManager.GetActiveView(1, null, out IVsTextView textViewCurrent);
return editor.GetWpfTextView(textViewCurrent);
}
You must add some reference manual by Add Reference -> Assemblies -> Extensions. Then choose:
Microsoft.VisualStudio.ComponentModelHost
Microsoft.VisualStudio.Editor
I work in a research group and I've been tasked with adding in scripting functionality to a data acquisition program. Ideally, I want to have the ability to write scripts while the data acquisition software is running (and save those scripts as files on the go). A command line might also be nice.
I'm not very experienced at all with C#, but I do have quite a bit of coding experience in other language (Objective-C, Python). I saw this link https://blogs.msdn.microsoft.com/cdndevs/2015/12/01/adding-c-scripting-to-your-development-arsenal-part-1/ which details the "Roselyn Scripting Package" but I'm not sure if that's my best option.
Can anybody suggest the easiest way of getting full scripting functionality? (I'm trying to avoid losing months of my life here =p). Links to start at/advice is much appreciated.
Thanks!
You posted an interesting link for me because I just prototyped something like that some weeks ago, but have not implemented yet. My goal is to create a C# "immediate" console on a webpage.
Have some minor issues regarding loading some assemblies programatically and have to reference them explicitly.
Here is the code behind, please later post your solution, I would be interested to know.
This alows to write c# code at runtime and also get a String return.
protected void getImmediateResult_Click(object sender, EventArgs e)
{
//building the code
string source = #"using System;
class MyType{
public static String Evaluate(){
<!expression!>
}}";
string expression = this.txtimmediate.Text;
string finalSource = source.Replace("<!expression!>", expression);
textcodeCheck.Text = finalSource;
var compileUnit = new CodeSnippetCompileUnit(finalSource);
//preparing compilation
CodeDomProvider provider = new Microsoft.CSharp.CSharpCodeProvider();
// Create the optional compiler parameters
//this correctly references the application but no System.Web etc
string[] refArray = new string[2];
UriBuilder uri = new UriBuilder(Assembly.GetExecutingAssembly().CodeBase);
refArray[0] = uri.Path;
//this works
refArray[1] = "System.Web" + ".dll";
////NOT WORKING for non microsoft assemblies
//var allRefs = Assembly.GetExecutingAssembly().GetReferencedAssemblies();
//string[] refArray = new string[allRefs.Length + 1];
//int i = 1;
//foreach (AssemblyName refer in allRefs)
//{
// refArray[i] = refer.Name + ".dll";
// i++;
//}
var compilerParameters = new CompilerParameters(refArray);
CompilerResults compilerResults = provider.CompileAssemblyFromDom(compilerParameters, compileUnit);
if (compilerResults.Errors.Count > 0)
{
//1st error
this.txtResult.Text = compilerResults.Errors[0].ErrorText;
return;
}
//running it
Type type = compilerResults.CompiledAssembly.GetType("MyType");
MethodInfo method = type.GetMethod("Evaluate");
String result = (String)method.Invoke(null, null);
this.txtResult.Text = result;
}
If you're willing to use IronPython, you can execute scripts directly in C#:
using IronPython.Hosting;
using Microsoft.Scripting.Hosting;
private static void doPython()
{
ScriptEngine engine = Python.CreateEngine();
engine.ExecuteFile(#"test.py");
}
Get IronPython here.
I'm working on a project i SharePoint 2010 where I have several under sites. each under site contain a list with news and I want to attach an Event Receiver to those lists.
The under sites and lists are created programmatically but I cannot attach the Event Receiver I have in my VS2010 Solution.
I've tried with this:
SPList list = new SPSite(siteURL).OpenWeb().Lists[listName];
SPEventReceiverDefinitionCollection eventReceivers = list.EventReceivers;
SPEventReceiverDefinition eventReceiver = eventReceivers.Add();
eventReceiver.Name = receiverName;
eventReceiver.Synchronization = SPEventReceiverSynchronization.Synchronous;
eventReceiver.Type = SPEventReceiverType.ItemAdded;
eventReceiver.SequenceNumber = sequenceNumber;
eventReceiver.Assembly = assemblyFullName;
eventReceiver.Class = assemblyClassName;
eventReceiver.Data = receiverData;
eventReceiver.Update();
But it does not work.
The error message is "Could not load file or assembly 'Projekt_Test1\, \, Version\=1.0.1777.23493\, Culture\=neutral\, PublicKeyToken\=49c7547d535382ab' or one of its dependencies. The given assembly name or codebase was invalid. (Exception from HRESULT: 0x80131047)"
Thanks for help.
I end up creating a List Extension method for this:
public static void EnsureEventReceiver(this SPList list,IEnumerable<SPEventReceiverType> receiverTypes, Type eventHander, SPEventReceiverSynchronization synchronization, int sequenceNumber)
{
foreach (SPEventReceiverType spEventReceiverType in receiverTypes)
{
string name = list.Title + spEventReceiverType.ToString();
if (list.EventReceivers.Cast<SPEventReceiverDefinition>().All(i => i.Name != name))
{
SPEventReceiverDefinition eventReceiver = list.EventReceivers.Add();
eventReceiver.Name = name;
eventReceiver.Type = spEventReceiverType;
eventReceiver.Assembly = eventHander.Assembly.FullName;
eventReceiver.Class = eventHander.FullName;
eventReceiver.SequenceNumber = sequenceNumber;
eventReceiver.Synchronization = synchronization;
eventReceiver.Update();
}
}
}
Caveats, Limitations of this method:
Only one event per List, since this is good enough for me, if you need more then you need to pass the name as a parameter
Event handler methods are in the same class
You can use it like this:
list.EnsureEventReceiver(
new[] { SPEventReceiverType.ItemAdded, SPEventReceiverType.ItemUpdated },
typeof(NewsItemsHandler),
SPEventReceiverSynchronization.Synchronous,
10000);
I have never succeeded with this version of eventReceivers.Add() you are using.
Here is a powershell framgent I'm using, it would be very similar in C#
$ev = $currentList.EventReceivers.Add([Microsoft.SharePoint.SPEventReceiverType]::ItemAdded, $assemblyName, $className);
Couple of things to look at:
Your assembly version is listed as 1.0.1777.23493. That looks like it is being auto-incremented. You will want to set a fixed assembly version or it will update with every build, breaking your code.
You're setting eventReceiver.Synchronization = SPEventReceiverSynchronization.Synchronous, but the ItemAdded is an asynchronous event.
Make sure that your event receiver assembly has been deployed to the GAC on the SharePoint server, and that you have recycled the SharePoint application pools in IIS before you run your code.