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.
Related
I'm trying to run WebDriver + NUnit test from .txt file via .NET Framework console app, here is my demo code:
class Program
{
static void Main(string[] args)
{
string source = File.ReadAllText(#"C:\Users\user\Desktop\SomeTest.txt");
Dictionary<string, string> providerOptions = new Dictionary<string, string>
{
{"CompilerVersion", "v4.0"}
};
CSharpCodeProvider provider = new CSharpCodeProvider(providerOptions);
CompilerParameters compilerParams = new CompilerParameters
{
GenerateInMemory = false,
GenerateExecutable = false,
};
compilerParams.ReferencedAssemblies.Add("nunit.framework.dll");
compilerParams.ReferencedAssemblies.Add("System.dll");
compilerParams.ReferencedAssemblies.Add("WebDriver.dll");
compilerParams.ReferencedAssemblies.Add("nunit.core.interfaces.dll");
compilerParams.ReferencedAssemblies.Add("nunit.core.dll");
compilerParams.OutputAssembly = #"C:\Users\user\Desktop\test.dll";
CompilerResults compileResults = provider.CompileAssemblyFromSource(compilerParams, source);
var assembly = compileResults.CompiledAssembly;
CoreExtensions.Host.InitializeService();
SimpleTestRunner runner = new SimpleTestRunner();
TestPackage package = new TestPackage(assembly.Location);
if (runner.Load(package))
{
TestResult res = runner.Run(new NullListener(),
TestFilter.Empty, false, LoggingThreshold.Off);
}
}
and I'm ending up with this error:
Unhandled Exception: System.IO.FileNotFoundException: Could not load file or assembly 'nunit.core, Version=2.6.4.14350, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77' or one of its dependencies. The system cannot find the file specified.
Here are my references:
Does anyone know what is the issue here?
I found the issue, Copy Local property of these references must be set to true. It works now
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.
Maybe simple question however I'm stuck. I try to configure cake script. I'm using the following script
Task("Build")
.Description("Builds the Solution")
.Does(() =>
{
MSBuild(SOLUTION_FILE, CreateSettings());
});
MSBuildSettings CreateSettings()
{
var settings = new MSBuildSettings { Verbosity = Verbosity.Minimal, Configuration = "Debug" };
settings.WithProperty("DebugType", "pdbonly");
settings.ToolVersion = MSBuildToolVersion.VS2017;
return settings;
}
When I execute it. I have the following issue:
========================================
Build
========================================
An error occurred when executing task 'Build'.
I have added MsBuild path to PATH environment but still not working.
How can i set custom MsBuild path ?
Thanks
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.
I have a Visual Studio extensions that use Roslyn to get a project in current opened solution, compile it and run methods from it. The project can be modified by the programmer.
I have successfully compiled a project in a Visual Studio extension from the current VisualStudioWorkspace.
private static Assembly CompileAndLoad(Compilation compilation)
{
using (MemoryStream dllStream = new MemoryStream())
using (MemoryStream pdbStream = new MemoryStream())
{
EmitResult result = compilation.Emit(dllStream, pdbStream);
if (!result.Success)
{
IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic =>
diagnostic.IsWarningAsError ||
diagnostic.Severity == DiagnosticSeverity.Error);
string failuresException = "Failed to compile code generation project : \r\n";
foreach (Diagnostic diagnostic in failures)
{
failuresException += $"{diagnostic.Id} : {diagnostic.GetMessage()}\r\n";
}
throw new Exception(failuresException);
}
else
{
dllStream.Seek(0, SeekOrigin.Begin);
return AppDomain.CurrentDomain.Load(dllStream.ToArray(), pdbStream.ToArray());
}
}
}
Then I can load the assembly in current domain, get it's types and invoke methods.
The problem is that I need to allow the programmer to put breakpoints if the current configuration of the loaded solution is debug.
I need to run some code in current Visual Studio Host from an extension and allow it to be debugged in the current Visual Studio instance.
Seems like this is actually impossible.
The current visual studio instance cannot debug itself.
I've tried creating a Console Application with roslyn, attaching it to the debugger and then run the generation code from it. But the VisualStudioWorkspace is only available inside VisualStudio (Not serializable and not avalaible througt DTE com interface). So the only solution left was using MBBuildWorkspace. Since it does not have that same behavior as Visual studio workspace, I've abandoned the project.
Here's my code for further references :
Process vsProcess = Process.GetCurrentProcess();
string solutionPath = CurrentWorkspace.CurrentSolution.FilePath;
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText($#"
using System;
using System.Threading.Tasks;
namespace CodeGenApplication
{{
public class Program
{{
public static void Main(string[] args)
{{
Console.ReadLine();
int vsProcessId = Int32.Parse(args[0]);
CodeGenApp.Test(""{solutionPath.Replace(#"\", #"\\")}"", ""{projectName}"", ""{_codeGenProjectName}"");
Console.ReadLine();
}}
}}
}}");
string assemblyName = Path.GetRandomFileName();
Project codeGenProject = CurrentWorkspace.CurrentSolution.Projects.Where(x => x.Name == _codeGenProjectName).FirstOrDefault();
List<MetadataReference> references = codeGenProject.MetadataReferences.ToList();
CSharpCompilation compilation = CSharpCompilation.Create(
assemblyName,
syntaxTrees: new[] { syntaxTree },
references: references,
options: new CSharpCompilationOptions(OutputKind.ConsoleApplication));
// Emit assembly to streams.
EmitResult result = compilation.Emit("CodeGenApplication.exe", "CodeGenApplication.pdb");
if (!result.Success)
{
IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic =>
diagnostic.IsWarningAsError ||
diagnostic.Severity == DiagnosticSeverity.Error);
}
else
{
Process codeGenProcess = new Process();
codeGenProcess.StartInfo.FileName = "CodeGenApplication.exe";
codeGenProcess.StartInfo.Arguments = vsProcess.Id.ToString();
codeGenProcess.StartInfo.UseShellExecute = false;
codeGenProcess.StartInfo.CreateNoWindow = true;
codeGenProcess.StartInfo.LoadUserProfile = true;
codeGenProcess.StartInfo.RedirectStandardError = true;
codeGenProcess.StartInfo.RedirectStandardInput = true;
codeGenProcess.StartInfo.RedirectStandardOutput = false;
codeGenProcess.Start();
foreach (EnvDTE.Process dteProcess in _dte.Debugger.LocalProcesses)
{
if (dteProcess.ProcessID == codeGenProcess.Id)
{
dteProcess.Attach();
}
}
codeGenProcess.StandardInput.WriteLine("Start");
}
You have to attach the debugger to the currently running visual studio host. To do that you need to:
Get hold of the DTE object,
find the (current) process in DTE.Debugger.LocalProcesses
Attach the debugger - example
run method from the compiled assembly
You'd probably want a separate command/button for this, don't just switch on the current build configuration. That's how everybody else does it (e.g. test runners and even Visual Studio). Also, load the compiled Assembly in a new AppDomain, otherwise it'll stick around for ever.
There's also the more "creative" solution of injecting a System.Diagnostics.Debugger.Launch() call to the method you're about to run (modify the correct Compilation.SyntaxTrees before calling Emit) - but seriously, don't do this.