Building Project Programmatically Fails Using Microsoft.Build - c#

I installed the three following packages into my console application:
Microsoft.Build
Microsoft.Build.Framework
Microsoft.Build.Tasks.Core
Microsoft.Build.Utilities.Core
And I tried to use the following method to build a project:
static void Build(string projectPath)
{
var logger = new ConsoleLogger(LoggerVerbosity.Normal);
logger.ShowSummary = true;
var manager = BuildManager.DefaultBuildManager;
var projectInstance = new ProjectInstance(projectPath);
var result = manager.Build(
new BuildParameters()
{
DetailedSummary = true,
Loggers = new List<ILogger>() { logger }
},
new BuildRequestData(projectInstance, new string[] { "Build" }));
var buildResult = result.ResultsByTarget["Build"];
var buildResultItems = buildResult.Items;
}
However, after I ran the code, I got the error that described in the following image:
Why is this happening and how can I fix it?

I think you're not using tht right MSBuild version. Try to set the variable explicitly in your .proj :
<MSBuildExtensionsPath>C:\Program Files (x86)\MSBuild</MSBuildExtensionsPath>

It seems the best solution is to use MSBuild command line in Process class. A working sample is as follows:
var buildOutput = new List<string>();
var buildError = new List<string>();
var buildProcess = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Enterprise\\MSBuild\\15.0\\Bin\\MSBuild.exe",
Arguments = projectPath + " /t:Rebuild /p:Configuration=Debug",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
}
};
buildProcess.Start();
while (!buildProcess.StandardOutput.EndOfStream)
{
buildOutput.Add(buildProcess.StandardOutput.ReadLine());
}
while (!buildProcess.StandardError.EndOfStream)
{
buildError.Add(buildProcess.StandardError.ReadLine());
}
And then you could use the output to determine whether the build was successful or not. The important note is that you have to find the correct path of MSBuild.exe file as there are several versions of this file and in my case (VS 2017) the correct path is the one in the sample code.

One of BuildRequestData constructor overloads supports a parameter called "toolVersion". Since you are using Visual Studio 2017, set it as "15.0".
EDIT: I quitted using the .Net Framework provided MSBuild version (the one located here):
System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory()
C:\Windows\Microsoft.NET\Framework\v4.0.30319
Instead, I'm using the one located here:
C:\Program Files (x86)\MSBuild\{version}\Bin
This version provide extra parameters as LangVersion or DeployOnBuild.

Related

How to programmatically compile .net core 3.1 application?

Is there a way to compile a WebAPI app from console app in .NET Core 3.1?
I have tried many different approaches, such as:
1.
var collection = ProjectCollection.GlobalProjectCollection;
var project = collection.LoadProject($#"{path}\Project.csproj");
project.SetProperty("Configuration", configuration);
project.Build();
(Path to .sln)
ERROR: MSB4041: The default XML namespace of the project must be the MSBuild XML namespace.
2.
new Microsoft.Build.Execution.ProjectInstance("PathToProject.sln").Build();
(Path to .sln)
ERROR: Microsoft.Build.Exceptions.InvalidProjectFileException: 'The project file could not be loaded. Data at the root level is invalid.
(Path to .csproj)
ERROR: Microsoft.Build.Exceptions.InvalidProjectFileException: 'The SDK 'Microsoft.NET.Sdk.Web' specified could not be found.
3.
ProjectCollection pc = new ProjectCollection();
Dictionary<string, string> GlobalProperty = new Dictionary<string, string>();
GlobalProperty.Add("Configuration", "Debug");
GlobalProperty.Add("Platform", "Any CPU");
GlobalProperty.Add("OutputPath", Directory.GetCurrentDirectory() + "\\MyOutput");
BuildParameters bp = new BuildParameters(pc);
BuildManager.DefaultBuildManager.BeginBuild(bp);
BuildRequestData BuildRequest = new BuildRequestData(projectFilePath, GlobalProperty, null, new string[] { "Build" }, null);
BuildSubmission BuildSubmission = BuildManager.DefaultBuildManager.PendBuildRequest(BuildRequest);
BuildSubmission.Execute();
BuildManager.DefaultBuildManager.EndBuild();
if (BuildSubmission.BuildResult.OverallResult == BuildResultCode.Failure)
{
throw new Exception();
}
(Both path to .sln and .csproj)
ERROR: Build result is a failure without reported exception
However, none of the approaches worked.
Therefore, I am wondering is it even possible to compile the .NET Core 3.1 WebAPI code?
The only way I could get this to work was to use the following code snippet (.NET 6):
var commandText = $"myProject.csproj";
p.StartInfo = new ProcessStartInfo(#"C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\MSBuild\Current\Bin\msbuild.exe", commandText);
p.StartInfo.WorkingDirectory = projectFileLocation;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardOutput = true;
p.Start();
while (!p.StandardOutput.EndOfStream)
{
var line = p.StandardOutput.ReadLine();
sb.Append(line);
}
p.WaitForExit();
I hope this saves somebody a lot more time.

Visual Studio, C# and StanfordCoreNLP problem

My goal is to test this code to make sure that Stanford Core NLP installed properly.
First I installed StanfordCOreNLP package using NuGet package manager and then I downloaded a zip file that contained a jar file that needed to be installed using jar -xf command , and then I ran the code.
At (var pipeline = new StanfordCoreNLP(props);)
I'm getting an error that says:
edu.stanford.nlp.io.RuntimeIOException: Error while loading a tagger model(probably missing model file)"
Inner Exception IOException:Unable to open"edu/stanford/nlp/models/pos-tagger/english-left3words/english-left3words-distsim.tagger" as class path, filename or URL
var jarRoot = #"D:/VisualStudioProjects/C#MachineLearningProjects/Chapter3TwiterSentiment/CoreNLPTest2/CoreNLPTest2/edu/stanford/nlp/models/pos-tagger";
var text = "We're going to test our CoreNLP instalation!!";
Properties props = new Properties();
props.setProperty("annotators", "tokenize, ssplit, pos, lemma, ner, parse, dcoref");
props.setProperty("ner.useSUTime", "0");
var curDir = Environment.CurrentDirectory;
Directory.SetCurrentDirectory(jarRoot);
var pipeline = new StanfordCoreNLP(props);
Directory.SetCurrentDirectory(curDir);
var annotation = new Annotation(text);
pipeline.annotate(annotation);
using (var stream = new ByteArrayOutputStream())
{
pipeline.prettyPrint(annotation, new PrintWriter(stream));
Console.WriteLine(stream.toString());
stream.close();
}
Console.ReadKey();
Please follow the below steps:
Step 1: Download Core NLP
Step 2: Unzip d:\stanford-corenlp-full-2018-10-05
Step 3: Unzip d:\stanford-corenlp-full-2018-10-05\stanford-corenlp-3.9.2-models.jar
Step 4: Change var jarRoot = #"d:/stanford-corenlp-full-2018-10-05/stanford-corenlp-3.9.2-models";
Step 5: Change props.setProperty("ner.useSUTime", "0"); to props.setProperty("sutime.binders", "0")

How to install Nuget Packages using ENVDTE.DTE

I created one console application to create visual studio project pragmatically, here i am not able install Nuget packages, always
var componentModel = (IComponentModel)Package.GetGlobalService(typeof(SComponentModel)); statement returns null values. for your reference i added my code below. Help me to resolve this issue.
static void Main(string[] args)
{
//InstallNuGetPackages.InstallNuGet("");
string ProjectName = "WebAPIProj";
string SolutionName = "EmptyTemplate";
System.Type type = System.Type.GetTypeFromProgID("VisualStudio.DTE.11.0");
Object obj = System.Activator.CreateInstance(type, true);
EnvDTE.DTE dte = (EnvDTE.DTE)obj;
dte.MainWindow.Visible = true; // optional if you want to See VS doing its thing
// create a new solution
dte.Solution.Create("C:\\"+ SolutionName + "\\", SolutionName);
var solution = dte.Solution;
// create a C# WinForms app
solution.AddFromTemplate(#"C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\ProjectTemplatesCache\CSharp\Web\1033\EmptyWebApplicationProject40\EmptyWebApplicationProject40.vstemplate",
#"C:\NewSolution\"+ ProjectName, ProjectName);
InstallNuGetPackages.InstallNuGet(dte);
foreach (var p in dte.Solution.Projects)
{
InstallNuGetPackages.InstallNuGet((Project)p, "Microsoft.AspNet.WebApi version1.1");
}
// save and quit
dte.ExecuteCommand("File.SaveAll");
dte.Quit();
}
Code to install Nuget Packages
public bool InstallNuGetPackage(Project project, string package)
{
bool installedPkg = true;
try
{
var componentModel = (IComponentModel)Package.GetGlobalService(typeof(SComponentModel)); //Always this statement returns null
IVsPackageInstallerServices installerServices = componentModel.GetService();
if (!installerServices.IsPackageInstalled(project, package))
{
var installer = componentModel.GetService();
installer.InstallPackage(null, project, package, (System.Version)null, false);
}
}
catch (Exception ex)
{
installedPkg = false;
}
return installedPkg;
}
(Turned this into an answer for better readability and more room)
created one console application - you only have access to the ServiceProvider from Visual Studio if you run your code inside of it, i.e. from an extension and/or package.
Running this from a console application cannot work. Visual Studio internally does a lot more setup for all the services and general environment than creating an instance of DTE.
To persue your route, although I'm not sure how feasible that is, invoke nuget.exe or NuGet.Core code to achieve similar.

MSBuild threw error: "... task could not be instantiated from the assembly"

EDIT: Never mind, it turns that out the task does not actually implement the Microsoft.Build.Framework.ITask interface. I had made the erroneous assumption that it did. D'oh!
I use Visual Studio 2015.
Relevant code:
string solutionFilePath = args.Single();
var fileLogger = new FileLogger { Verbosity = LoggerVerbosity.Detailed, Parameters = #"logfile=C:\MSBuildResults.txt" };
var projectCollection = new ProjectCollection {DefaultToolsVersion = "14.0"};
var buildParameters = new BuildParameters(projectCollection) { Loggers = new List<ILogger> { fileLogger } };
var globalProperties = new Dictionary<string, string> { { "Configuration", "Debug" }, { "Platform", "Any CPU" } };
var buildRequestData = new BuildRequestData(solutionFilePath,
globalProperties,
targetsToBuild: new[] { "Build" },
toolsVersion: "14.0",
hostServices: null);
BuildResult buildResult = BuildManager.DefaultBuildManager.Build(buildParameters, buildRequestData);
buildResult.OverallResult is BuildResultCode.Failure, with error messages like the following printed to my log file:
C:\Workspaces\SomeProject.csproj(67,5): error MSB4127: The "DummyTask"
task could not be instantiated from the assembly
"\networkDrive\Dummy.dll". Please verify the task assembly has been
built using the same version of the Microsoft.Build.Framework assembly
as the one installed on your computer and that your host application
is not missing a binding redirect for Microsoft.Build.Framework.
Unable to cast object of type 'DummyTask' to type
'Microsoft.Build.Framework.ITask'.
Following the advice here, I added the <bindingRedirect> tag in the .exe.config file of my program. The same error messages were thrown nonetheless.
Instead of using Microsoft.Build libraries, I decided to invoke MSBuild.exe using Process.Start() (which I know is not recommended):
var processStartInfo = new ProcessStartInfo(#"C:\Program Files (x86)\MSBuild\14.0\Bin\MSBuild.exe")
{
Arguments = $"{solutionFilePath} /t:Build",
RedirectStandardOutput = false,
UseShellExecute = true,
CreateNoWindow = false
};
var process = Process.Start(processStartInfo);
process.WaitForExit();
process.Close();
This time, the build was successful and the process ran to completion with exit code 0.
What did I do wrong when using Microsoft.Build libraries to build my solution? What should I do instead to ensure that the build succeeds when I use BuildManager?
Let me know if you need any more information to help me with my issue.

How to install multiple subdirectories under ProgramFiles64Forder with WixSharp?

I have made a WixSharp 64bit installer that should install files under two different directories under "Program Files". Here is a stripped down version of the code:
using System;
using WixSharp;
using File = WixSharp.File;
public class Script {
public static void Main(string[] args) {
var project =
new Project("My Product",
new Dir(#"%ProgramFiles%",
new Dir(#"SubDir1", new File(#"Files\test2.txt")),
new Dir(#"SubDir2", new File(#"Files\test2.txt"))
));
project.Platform = Platform.x64;
project.GUID = new Guid("6f330b47-2577-43ad-9095-1861ba25889b");
Compiler.BuildMsi(project);
}
}
The problem is that the subdirectories will be created under "c:\%ProgramFiles64%\" instead of being under "c:\Program Files\".
If I just install one sub-directory, then the directory will be installed correctly into "c:\Program Files".
If I do the same without specifying the platform as x64 the files will correctly go under "c:\Program Files(x86)".
What am I doing wrong here? How could I get the two directories there.
I first suspected I might be hitting the wrong overload of the Dir constructor, but the behavior is the same when using the following code to ensure it goes into the Dir(string targetPath, params WixEntity[] items) constructor:
new Dir(#"%ProgramFiles%",new WixEntity[] {
new Dir(#"SubDir1", new File(#"Files\test2.txt")),
new Dir(#"SubDir2", new File(#"Files\test2.txt"))
}
I asked the same question at the Wix# projects page and Oleg_s anwered with a workaround and a good explanation of why it did not work. The answer is here:
http://wixsharp.codeplex.com/discussions/648259#post1454338
string strLocationOne = "InstallDirOne";
string strLocationTwo = "InstallDirTwo";
string strAllDeployFilesLocation = #"E:\files_to_deploy\*.*"
var project = new Project("MyApp",
new Dir(#"C:\",
new Dir(strLocationOne, new DirFiles(strAllDeployFilesLocation)),
new Dir(strLocationTwo", new DirFiles(strAllDeployFilesLocation))
));

Categories

Resources