Would anyone happen to know a trick that will keep this MSBuild task from blocking? I really just want the explorer to open and the build script to keep on going. Currently it blocks at the Exec task until the explorer window is closed.
<Target Name="OpenExplorer">
<Exec Command='explorer.exe "$(DestinationDir)"' IgnoreExitCode="true" />
</Target>
Thanks!
Edit: I was hoping to avoid creating a custom task for this. Perhaps some command line magic exists that could be placed inline for the Command?
Here is an easy way to execute processes asynchronously by only using msbuild and inline tasks. This is only for MSBuild V4.0 and up (God bless the MSBuild guys for adding this feature!). You don't need the any external extension packs.
Effectively, we are taking the code suggested above and putting it in an inline task. Feel free to fiddle with the code to suite your needs.
The point of this solution is that it lets you achieve the result without the headache of creating a separate dll for the custom task. The implementation in the extension pack is definitely more solid but this works as a quick 'n dirty way of solving this issue. You can also customise exactly how you want it to run.
<!--Launch a Process in Parallel-->
<UsingTask TaskName="ExecAsync" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<ParameterGroup>
<!--The file path is the full path to the executable file to run-->
<FilePath ParameterType="System.String" Required="true" />
<!--The arguments should contain all the command line arguments that need to be sent to the application-->
<Arguments ParameterType="System.String" Required="true" />
</ParameterGroup>
<Task>
<Code Type="Fragment" Language="cs">
<![CDATA[
string name = System.IO.Path.GetFileNameWithoutExtension(FilePath);
Log.LogMessage("Starting {0}...", name);
System.Diagnostics.ProcessStartInfo processStartInfo = new System.Diagnostics.ProcessStartInfo(FilePath, Arguments);
processStartInfo.UseShellExecute = true;
System.Diagnostics.Process.Start(processStartInfo);
Log.LogMessage("Finished running process {0}.", name);
]]>
</Code>
</Task>
</UsingTask>
You can then call the ExecAsync task from within your normal script in the following fashion. Note: My script below is used to gather code coverage for an application.
<!--Start listening for coverage data:-->
<Message Text="Starting to listen for coverage..."/>
<ExecAsync FilePath='$(VSPerfCmdExePath)' Arguments='/start:coverage /output:"$(CoverageFilePath)"' ContinueOnError='true'/>
<Message Text="Listening for coverage..."/>
<!--Start App with Coverage:-->
<Message Text="Starting App..."/>
<Exec Command='"$(AppCoverageLatestExePath)"' ContinueOnError='true' WorkingDirectory='$(AppCoverageLatestFolder)'/>
<Message Text="App shut down."/>
<!--Stop gathering coverage results:-->
<Message Text="Stopping listening for coverage..."/>
<Exec Command='"$(VSPerfCmdExePath)" /shutdown'/>
<Message Text="Coverage shut down."/>
Here is a description of what is happening there:
First I kick the performance tool off so that it listens for
coverage. I do this using our AsyncExec task because normally the
tool blocks when running in MSBuild (see here).
Next, we start our program that we want to gather coverage on.
Then we shut down the coverage tool once we are done.
You can't do it with the native Exec. But you can write your own that fires asynchronously, as in this example:
public class AsyncExec : Exec {
protected override int ExecuteTool(string pathToTool,
string responseFileCommands,
string commandLineCommands) {
Process process = new Process();
process.StartInfo = GetProcessStartInfo(pathToTool, commandLineCommands);
process.Start();
return 0;
}
protected virtual ProcessStartInfo GetProcessStartInfo(string executable,
string arguments) {
if (arguments.Length > 0x7d00) {
this.Log.LogWarningWithCodeFromResources("ToolTask.CommandTooLong", new object[] { base.GetType().Name });
}
ProcessStartInfo startInfo = new ProcessStartInfo(executable, arguments);
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = true;
string workingDirectory = this.GetWorkingDirectory();
if (workingDirectory != null) {
startInfo.WorkingDirectory = workingDirectory;
}
StringDictionary environmentOverride = this.EnvironmentOverride;
if (environmentOverride != null) {
foreach (DictionaryEntry entry in environmentOverride) {
startInfo.EnvironmentVariables.Remove(entry.Key.ToString());
startInfo.EnvironmentVariables.Add(entry.Key.ToString(), entry.Value.ToString());
}
}
return startInfo;
}
}
which you can then run with:
<AsyncExec Command="..." />
Answered at Starting a program with MSBuild/Web Deployment Project and not waiting for it
<Exec Command="..." Timeout="2000"></Exec>
Try AsyncExec in MSBuild Extension Pack.
The Command in Exec is placed in a batch file and executed. So you can use the "start" keyword in the Command just the same as in a console window. That will do the trick.
Related
So I have this code to send email. The code tries to find the .cshtml for the email template.
This is my folder hierarchy:
Project
-- Email
---- EmailConfirmation.cshtml
---- ResetPassword.cshtml
This is the code to find the template:
public static string GetEmailTemplate(string templateName)
{
string path = Path.Combine(Config.Env.ContentRootPath, "Email", templateName);
string content = "";
// This text is added only once to the file.
if (File.Exists(path))
{
// Create a file to write to.
content = File.ReadAllText(path);
}
else
{
throw new Exception("The email template could not be found");
}
return content;
}
In Debug, this runs perfectly, but in production, the code cannot find the template.
How can I include the template in publish package?
I tried this in my .csproj:
<ItemGroup>
<Folder Include="ClientApp\" />
<Folder Include="Migrations\" />
<Views Include="Email\**" />
</ItemGroup>
It is not working.
Edit
I tried to set this:
But still not working.
So my problem was the Email folder was not copied to the published package. Add this code to .csproj:
<ItemGroup>
<None Include="Email\EmailConfirmation.cshtml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Email\ResetPassword.cshtml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
Or you can set it in the project explorer like this:
For some reason, the build action has to be set to None, because .NetCore will treat Content differently and the folder will not be copied to the publishing package (maybe the content will be merge into .dll, I do not know).
Could you try this ?
string workingDirectory = Environment.CurrentDirectory;
string projectDirectory=Directory.GetParent(workingDirectory).Parent.Parent.FullName;
It will give you the Path to the project directory. Then you complete with the subdirectory which leads to your file.
RaceConditionTest.fs
namespace FConsole
module RaceConditionTest =
let test x =
...
Program.fs
open System
open FConsole
[<EntryPoint>]
let main argv =
RaceConditionTest.test 1000
0 // return an integer exit code
Then I run my console app (linux)
$ dotnet run
error FS0039: The namespace or module 'FConsole' is not defined.
There is only one test method in RaceConditionTest.fs
Is the order of files the problem? if so, How do I indicate the order of *.fs files?
as #boran suggested in his comments there is this FConsoleProject.fsproj
I just added my file before Program.fs
<ItemGroup>
<Compile Include="RaceConditionTest.fs" />
<Compile Include="Program.fs" />
</ItemGroup>
I have a custom MSBuild Task that generates a file based on files that have no build action. The generated files need to be embedded into the final assembly. The task looks something like this:
public class MyTask : Task
{
public string OutputDirectory { get; set; }
public string[] NoneIncluded { get; set; }
private IEnumerable<ITaskItem> _generatedFiles;
[Output]
public ITaskItem[] GeneratedFiles => _generatedFiles.ToArray();
public override bool Execute()
{
_generatedCssFiles = new List<ITaskItem>();
foreach(var item in NoneIncluded)
{
if(someCondition)
{
var contents = DoFoo(item);
var outputPath = Path.Combine(OutputDirectory, $"{item}.txt");
File.WriteAllText(outputPath, contents);
_generatedFiles.Add(new TaskItem(ProjectCollection.Escape(outputFile)));
}
}
}
}
In my targets file, I then have a target defined like the following:
<PropertyGroup>
<CoreCompileDependsOn>MyTarget;$(CoreCompileDependsOn);</CoreCompileDependsOn>
</PropertyGroup>
<Target Name="MyTarget"
BeforeTargets="CoreCompile;Build">
<MyTask OutputDirectory="$(IntermediateOutputPath)"
NoneIncluded="#(None)">
<Output ItemName="FilesWrite"
TaskParameter="GeneratedFiles"/>
<Output ItemName="EmbeddedResource"
TaskParameter="GeneratedFiles" />
</MyTask>
</Target>
I ultimately end up with two issues that I can't seem to resolve:
Although the files are generated prior to the compile task, it isn't embedded into the assembly, unless I rebuild the project without cleaning the outputs. On the subsequent build the file is embedded.
If I generate the files in the IntermediateOutputPath, the embedded resource id includes that path. So instead of MyProject.SomeResource.txt I get MyProject.obj.netstandard2._0.SomeResource.txt
Note:
- If I replace the Path.Combine and simply generate the output file in the project, it fixes the issue of the resource id, but not the first issue with it not being embedded on the first compile.
How can I ensure the my generated files are embedded on the first compilation, and that I can generate them in the IntermediateOutputPath rather than the Project directory with a resource id as if it were in the project.
You need to run your target earlier in the pipeline. CoreCompile is too late.
You can add the following to your .csproj file. This target will run before ResolveReferences and add Image.png as an embedded resource.
<Target Name="IncludeDynamicResources" BeforeTargets="ResolveReferences">
<ItemGroup>
<EmbeddedResource Include="Image.png">
<Type>Non-Resx</Type>
</EmbeddedResource>
</ItemGroup>
</Target>
Your second issue can be solved by simply setting LogicalName metadata to the created task item.
TaskItem taskItem = new TaskItem(ProjectCollection.Escape(outputFile));
taskItem.SetMetadata("LogicalName", $"{AssemblyName}.{item}.txt");
_generatedFiles.Add(taskItem);
Now to access the assembly name inside your task just add AssemblyName string property to MyTask and AssemblyName="$(AssemblyName)" to your targets. This should produce the desired result.
My code is as follows:
...
propertyGroup1.AddProperty("DeployOnBuild", "true");
propertyGroup1.AddProperty("DeployTarget", "MSDeployPublish");
propertyGroup1.AddProperty("MSDeployServiceUrl", "localhost");
propertyGroup1.AddProperty("DeployIisAppPath", "local.projects.com");
propertyGroup1.AddProperty("MSDeployPublishMethod", "InProc");
propertyGroup1.AddProperty("AllowUntrustedCertificate", "true");
...
cSharpProject.ProjectCollection.RegisterLogger(cSharpLogger);
try {
buildResult = cSharpProject.Build();
...
buildResult is set to false. However, it does not raise any error to the logger. And the project is not deployed.
I'm running my exe in Administrator mode.
Any help is apprecited.
Thanks
Chris
hint: CSAutoParameterize.parameters and other subfolders have not been created.
<Target Name="WebPublish">
<MsBuild Projects="MyProject.csproj" Properties="DeployOnBuild=true;DeployTarget=MSDeployPublish;MSDeployServiceUrl=localhost;DeployIisAppPath=local.website.com;MSDeployPublishMethod=RemoteAgent;AllowUntrustedCertificate=true;Username=someuser;password=somepassword" />
</Target>
I was able to add this target to the project and call just that specific target.
Is there a way to automatically increment the "minimum required version" fields in a ClickOnce deployment to always equal the current build number? Basically, I always want my deployment to be automatically updated at launch.
I suspect I'm going to need a some pre-/post-build events, but I hope there's an easier way.
I may be a little late with answering this one but I found it difficult to find the solution on google but eventually figured it out so thought I would share.
With MSBuild version 4 (VS2010 and VS2012) this can be achieved by inserting the following target:
<Target Name="AutoSetMinimumRequiredVersion" BeforeTargets="GenerateDeploymentManifest">
<FormatVersion Version="$(ApplicationVersion)" Revision="$(ApplicationRevision)">
<Output PropertyName="MinimumRequiredVersion" TaskParameter="OutputVersion" />
</FormatVersion>
<FormatVersion Version="$(ApplicationVersion)" Revision="$(ApplicationRevision)">
<Output PropertyName="_DeploymentBuiltMinimumRequiredVersion" TaskParameter="OutputVersion" />
</FormatVersion>
</Target>
The $(ApplicationVersion) is the same setting that you can set manually in the project's Publish window in the VS IDE, with the revision part set to an asterisk. The $(ApplicationRevision) is the actual revision being used for the published version. The FormatVersion task is a built-in MSBuild task that formats the two into a single full version number.
This will set the 'Minimum Required Version' to be the same as the 'Publish Version' therefore ensuring that the new deployment will always be installed by users, ie no option to Skip the update.
Of course, if you don't want to set the minimum required version to the publish version and want to use a different source property then it is straight-forward to amend the target, but the principle is the same.
I ended up actually rolling an AddIn to VS that synchronizes all the version numbers, and then builds and publishes with a single click. It was pretty easy.
Public Sub Publish()
Try
Dim startProjName As String = Nothing
Dim targetProj As Project = Nothing
Dim soln As Solution2 = TryCast(Me._applicationObject.DTE.Solution, Solution2)
If soln IsNot Nothing Then
For Each prop As [Property] In soln.Properties
If prop.Name = "StartupProject" Then
startProjName = prop.Value.ToString()
Exit For
End If
Next
If startProjName IsNot Nothing Then
For Each proj As Project In soln.Projects
If proj.Name = startProjName Then
targetProj = proj
Exit For
End If
Next
If targetProj IsNot Nothing Then
Dim currAssemVersionString As String = targetProj.Properties.Item("AssemblyVersion").Value.ToString
Dim currAssemVer As New Version(currAssemVersionString)
Dim newAssemVer As New Version(currAssemVer.Major, currAssemVer.Minor, currAssemVer.Build, currAssemVer.Revision + 1)
targetProj.Properties.Item("AssemblyVersion").Value = newAssemVer.ToString()
targetProj.Properties.Item("AssemblyFileVersion").Value = newAssemVer.ToString()
Dim publishProps As Properties = TryCast(targetProj.Properties.Item("Publish").Value, Properties)
Dim shouldPublish As Boolean = False
If publishProps IsNot Nothing Then
shouldPublish = CBool(publishProps.Item("Install").Value)
If shouldPublish Then
targetProj.Properties.Item("GenerateManifests").Value = "true"
publishProps.Item("ApplicationVersion").Value = newAssemVer.ToString()
publishProps.Item("MinimumRequiredVersion").Value = newAssemVer.ToString()
publishProps.Item("ApplicationRevision").Value = newAssemVer.Revision.ToString()
End If
End If
targetProj.Save()
Dim build As SolutionBuild2 = TryCast(soln.SolutionBuild, SolutionBuild2)
If build IsNot Nothing Then
build.Clean(True)
build.Build(True)
If shouldPublish Then
If build.LastBuildInfo = 0 Then
build.Publish(True)
End If
End If
End If
End If
End If
End If
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Sub
Out of the box, I don't belive there is a way. It's not too much effort to spin your own however.
The approach I use is as follows:
1) create a Version.Properties file
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Util-VersionMajor>1</Util-VersionMajor>
<Util-VersionMinor>11</Util-VersionMinor>
<Util-VersionBuild>25</Util-VersionBuild>
<Util-VersionRevision>0</Util-VersionRevision>
<Util-VersionDots>$(Util-VersionMajor).$(Util-VersionMinor).$(Util-VersionBuild).$(Util-VersionRevision)</Util-VersionDots>
<Util-VersionUnders>$(Util-VersionMajor)_$(Util-VersionMinor)_$(Util-VersionBuild)_$(Util-VersionRevision)</Util-VersionUnders>
<MinimumRequiredVersion>$(Util-VersionDots)</MinimumRequiredVersion>
<ApplicationVersion>$(Util-VersionDots)</ApplicationVersion>
<ApplicationRevision>$(Util-VersionRevision)</ApplicationRevision>
</PropertyGroup>
</Project>
2) Import the Version.Properties file into your project files
3) Create a task to increment the version on Build. Here's mine
<Target Name="IncrementVersion" DependsOnTargets="Build" Condition="'$(BuildingInsideVisualStudio)'==''">
<ItemGroup>
<Util-VersionProjectFileItem Include="$(Util-VersionProjectFile)" />
</ItemGroup>
<PropertyGroup>
<Util-VersionProjectFileFullPath>#(Util-VersionProjectFileItem->'%(FullPath)')</Util-VersionProjectFileFullPath>
</PropertyGroup>
<Exec Command=""$(TfCommand)" get /overwrite /force /noprompt "$(Util-VersionProjectFileFullPath)"" Outputs="" />
<Exec Command=""$(TfCommand)" checkout /lock:checkout "$(Util-VersionProjectFileFullPath)"" Outputs="" />
<Version Major="$(Util-VersionMajor)" Minor="$(Util-VersionMinor)" Build="$(Util-VersionBuild)" Revision="$(Util-VersionRevision)" RevisionType="None" BuildType="Increment">
<Output TaskParameter="Major" PropertyName="Util-VersionMajor" />
<Output TaskParameter="Minor" PropertyName="Util-VersionMinor" />
<Output TaskParameter="Build" PropertyName="Util-VersionBuild" />
<Output TaskParameter="Revision" PropertyName="Util-VersionRevision" />
</Version>
<XmlUpdate Prefix="msb" Namespace="http://schemas.microsoft.com/developer/msbuild/2003" XPath="/msb:Project/msb:PropertyGroup/msb:Util-VersionMajor" XmlFileName="$(Util-VersionProjectFile)" Value="$(Util-VersionMajor)" />
<XmlUpdate Prefix="msb" Namespace="http://schemas.microsoft.com/developer/msbuild/2003" XPath="/msb:Project/msb:PropertyGroup/msb:Util-VersionMinor" XmlFileName="$(Util-VersionProjectFile)" Value="$(Util-VersionMinor)" />
<XmlUpdate Prefix="msb" Namespace="http://schemas.microsoft.com/developer/msbuild/2003" XPath="/msb:Project/msb:PropertyGroup/msb:Util-VersionBuild" XmlFileName="$(Util-VersionProjectFile)" Value="$(Util-VersionBuild)" />
<XmlUpdate Prefix="msb" Namespace="http://schemas.microsoft.com/developer/msbuild/2003" XPath="/msb:Project/msb:PropertyGroup/msb:Util-VersionRevision" XmlFileName="$(Util-VersionProjectFile)" Value="$(Util-VersionRevision)" />
<Exec Command=""$(TfCommand)" checkin /override:AutoBuildIncrement /comment:***NO_CI*** "$(Util-VersionProjectFileFullPath)"" />
<Exec Command=""$(TfCommand)" get /overwrite /force /noprompt "$(Util-AssemblyInfoFile)"" Outputs="" />
<Exec Command=""$(TfCommand)" checkout /lock:checkout "$(Util-AssemblyInfoFile)"" Outputs="" />
<AssemblyInfo CodeLanguage="CS" OutputFile="$(Util-AssemblyInfoFile)" AssemblyConfiguration="$(Configuration)" AssemblyVersion="$(Util-VersionMajor).$(Util-VersionMinor).$(Util-VersionBuild).$(Util-VersionRevision)" AssemblyFileVersion="$(Util-VersionMajor).$(Util-VersionMinor).$(Util-VersionBuild).$(Util-VersionRevision)" />
<Exec Command=""$(TfCommand)" checkin /override:AutoBuildIncrement /comment:***NO_CI*** "$(Util-AssemblyInfoFile)"" />
</Target>
Some additional clickonce tricks here http://weblogs.asp.net/sweinstein/archive/2008/08/24/top-5-secrets-of-net-desktop-deployment-wizards.aspx
Here's how I handled this one. First I created a custom task that wraps string replacement:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Build.Utilities;
using Microsoft.Build.Framework;
namespace SynchBuild
{
public class RemoveAsterisk : Task
{
private string myVersion;
[Required]
public string Version
{
set{myVersion = value;}
}
[Output]
public string ReturnValue
{
get { return myVersion.Replace("*", ""); }
}
public override bool Execute()
{
return true;
}
}
}
So that gets built into SynchBuild.dll which you see referenced in the UsingTask below. Now I tried just overwritting the MinimumRequiredVersion property, but it didn't seem to get picked up, so I just overwrote the GenerateApplicationManifest target by adding the following lines to the end of my csproj file:
<UsingTask AssemblyFile="$(MSBuildExtensionsPath)\WegmansBuildTasks\SynchBuild.dll" TaskName="SynchBuild.RemoveAsterisk" />
<Target Name="GenerateDeploymentManifest" DependsOnTargets="GenerateApplicationManifest" Inputs="
$(MSBuildAllProjects);
#(ApplicationManifest)
" Outputs="#(DeployManifest)">
<RemoveAsterisk Version="$(ApplicationVersion)$(ApplicationRevision)">
<Output TaskParameter="ReturnValue" PropertyName="MinimumRequiredVersion" />
</RemoveAsterisk>
<GenerateDeploymentManifest MinimumRequiredVersion="$(MinimumRequiredVersion)" AssemblyName="$(_DeploymentDeployManifestIdentity)" AssemblyVersion="$(_DeploymentManifestVersion)" CreateDesktopShortcut="$(CreateDesktopShortcut)" DeploymentUrl="$(_DeploymentFormattedDeploymentUrl)" Description="$(Description)" DisallowUrlActivation="$(DisallowUrlActivation)" EntryPoint="#(_DeploymentResolvedDeploymentManifestEntryPoint)" ErrorReportUrl="$(_DeploymentFormattedErrorReportUrl)" Install="$(Install)" MapFileExtensions="$(MapFileExtensions)" MaxTargetPath="$(MaxTargetPath)" OutputManifest="#(DeployManifest)" Platform="$(PlatformTarget)" Product="$(ProductName)" Publisher="$(PublisherName)" SuiteName="$(SuiteName)" SupportUrl="$(_DeploymentFormattedSupportUrl)" TargetCulture="$(TargetCulture)" TargetFrameworkVersion="$(TargetFrameworkVersion)" TrustUrlParameters="$(TrustUrlParameters)" UpdateEnabled="$(UpdateEnabled)" UpdateInterval="$(_DeploymentBuiltUpdateInterval)" UpdateMode="$(UpdateMode)" UpdateUnit="$(_DeploymentBuiltUpdateIntervalUnits)" Condition="'$(GenerateClickOnceManifests)'=='true'">
<Output TaskParameter="OutputManifest" ItemName="FileWrites" />
</GenerateDeploymentManifest>
</Target>
The end result is we take the app version and revision, combine them, remove the asterisk, then set the minimum required version. I have the auto increment app version in my publish properties set so that's how incrementing takes place, then I'm just setting the minimumrequiredversion to always match.I don't use team build, this is just designed so that a developer using visual studio can make all clickonce deployments required. Hope this helps.
If you are publishing your ClickOnce application from Visual Studio then just install the AutoUpdateProjectsMinimumRequiredClickOnceVersion NuGet Package in your project and you're good to go.
If you are publishing from a build server or other script, then you can use the Set-ProjectFilesClickOnceVersion PowerShell script. My blog describes in more detail how to setup your build server to accommodate publishing ClickOnce applications.
Are you looking for Application Updates?
Right clicking on the project in the Solution Explorer and then clicking Publish... is the wrong way to get Application Updates. You have to right-click your project and the click Properties, then click the Publish tab. Click the Updates... button and then check the "The application should check for updates" check box. There you can also specify a minimum required version for the application. (I haven't used that functionality but the Updates functionality is the core reason I use ClickOnce and it works great.)