Problems using MsBuild using command line for Publish Click Once - msbuild

I have Windows application in csproj in my solution, and I want generate Publish using command line (bat, cmd).
My script is (I put \r\n for better reading):
SET MSBUILD="%SystemRoot%\Microsoft.NET\Framework\v3.5\MSBuild.exe"
SET CARWIN="..\..\Security.CarWin.csproj"
rem msbuild para publish
%MSBUILD% /target:rebuild;publish %CARWIN%
/p:ApplicationVersion="1.0.0.0"
/p:Configuration=release
/p:PublishUrl="C:\ClickOnce\CarWin.WebInstall\Publicacion\"
/p:InstallUrl="http://desserver/carwinclickonce/Publicacion/"
/p:PublishDir="C:\ClickOnce\CarWin.WebInstall\Publicacion\"
note: I'll try too using /target:publish
But in path PublishDir or PublishUrl (C:\ClickOnce\CarWin.WebInstall\Publicacion) not generates any files.
I have seen many posts in this site and google but I not found any solution.

Use PublishDir instead of PublishUrl when running from command line.
msbuild /target:publish /p:Configuration=Release;PublishDir=c:\playground\
You can also change version, like ApplicationRevision=666;MinimumRequiredVersion=1.1

Take a look at this Stack Overflow question. Basically the PublishUrl property is ignored when running ClickOnce from the command line. But you can easily add the behaviour with an additional MSBuild-task.
I've created an additional MSBuild-File, for example a build.csproj. This contains a publish-task. This task first invokes the regular MS-Build of the target-project. Afterwards it copies the result to the publish-directory. Now I invoke the 'build.csproj' instead of the reguar project-file from the command-line:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Publish" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<!-- project name-->
<ProjectName>MyExampleProject</ProjectName>
<!--properties for the project-build-->
<DefaultBuildProperties>Configuration=Release</DefaultBuildProperties>
<!-- location of the click-once stuff, relative to the project -->
<ProjectPublishLocation>.\bin\Release\app.publish</ProjectPublishLocation>
<!-- Location you want to copy the click-once-deployment. Here an windows-share-->
<ProjectClickOnceFolder>\\TargetServer\deployments</ProjectClickOnceFolder>
</PropertyGroup>
<Target Name="Publish" DependsOnTargets="Clean">
<Message Text="Publish-Build started for build no $(ApplicationRevision)" />
<!-- run the original build of the project -->
<MSBuild Projects="./$(ProjectName).csproj"
Properties="$(DefaultBuildProperties)"
Targets="Publish"/>
<!-- define the files required for click-once-->
<ItemGroup>
<SetupFiles Include="$(ProjectPublishLocation)\*.*"/>
<UpdateFiles Include="$(ProjectPublishLocation)\Application Files\**\*.*"/>
</ItemGroup>
<!-- and copy them -->
<Copy
SourceFiles="#(SetupFiles)"
DestinationFolder="$(ProjectClickOnceFolder)\"/>
<Copy
SourceFiles="#(UpdateFiles)"
DestinationFolder="$(ProjectClickOnceFolder)\Application Files\%(RecursiveDir)"/>
</Target>
<Target Name="Clean">
<Message Text="Clean project" />
<MSBuild Projects="./$(ProjectName).csproj"
Properties="$(DefaultBuildProperties)"
Targets="Clean"/>
</Target>
</Project>

I don't know if this is a problem, but I noticed that you pass the /target parameter twice?
you could you use a semi-colon delimited example:
/target:rebuild;publish
MSDN Documentation on command line parameters and MSBuild
If that also does not work you could perhaps try to debug it by passing
/verbosity:diag

Related

MC.exe in msbuild

How do I compile with mc.exe in the correct way. Currently I have a build step which runs the relevant command but looking at developer network
There seems to be a better way.
I am not a expert with msbuild so please excuse how easy this question is. Googling has revealed no help
<Project
DefaultTargets="Build"
ToolsVersion="14.0"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Message Source Files">
<Extensions>mc;</Extensions>
<UniqueIdentifier>{B796B525-44D3-4260-8C76-705DBADA1043}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<MessageCompile Include="a.mc">
<GenerateBaselineResource>true</GenerateBaselineResource>
</MessageCompile>
</ItemGroup>
<Target Name="Build">
<DontKnowWhatGoesHere Sources="#(MessageCompile)"/>
</Target>
</Project>
MSBuild build are usually extended via .targets files, that have to be included in the project, and they extend the existing build proces. The WDK tasks for MSBuild page confirms this:
These command-line tools need to be exposed to MSBuild as tasks (contained in targets) so that they can be run during the build process.
The WDK MSDN page also has a help page on Windows driver targets:
The WindowsDriver.Common.targets, WindowsDriver.masm.targets, and WindowsDriver.arm.targets files provide the targets that are necessary to build a driver.
A quick grep in my C:\Program Files (x86)\Windows Kits\10\build directory showed that the MessageCompile target (the step that actually processes the MessageCompile items) is defined in the build\WindowsDriver.Common.targets file.
After importing the targets in your project you can do one of the following:
<Import
Project="C:\Program Files (x86)\Windows Kits\10\build\build\WindowsDriver.Common.targets" />
<!-- Option A: -->
<Target Name="Build" DependsOnTargets="MessageCompile">
<!-- no need to do anything, the dependency target should do the work -->
</Target>
<!-- Option B: -->
<Target Name="Build" DependsOnTargets="MessageCompile">
<!-- Use the Mc task which is the actual wrapper around the .exe,
see the .common.targets file for the list of all parameters -->
<Mc
Sources ="#(MessageCompile)"
ToolExe ="$(MessageCompileToolExe)"
ToolPath ="$(MessageCompileToolPath)"
Generated
/>
</Target>

Call MSBuild passing it a single task to run

We can define a target in a csproj file and then specify that target when we call msbuild from the command line. That looks like this:
my.csproj
<Target Name="CopyFiles">
<Copy
SourceFiles="#(MySourceFiles)"
DestinationFolder="c:\MyProject\Destination" />
</Target>
msbuild
msbuild my.csproj /t:CopyFiles
The CopyFiles targets asks msbuild to run the Copy task.
What if we don't want to edit the csproj file. How can we define a target just from the command line? Alternatively, using only the command line, how can we ask msbuild to run just one or maybe two tasks?
Pseudo-Code
msbuild my.csproj /t:"Copy SourceFiles=#(MySourceFiles) DestinationFolder=..."
Based on the MSBuild Command-Line Reference, this isn't possible exactly as you describe it, i.e. MSBuild will not take Target definitions from the command-line input. They have to be defined in a file somewhere.
But, you can have a script (maybe even a .bat?) that does something like:
Create a new, essentially empty, project file.
Import the .csproj, with <Import Project="foo.csproj" />
Add the targets
Call MSBuild with the new project file with the targets you want. Multiple targets can be specified by multiple /t: switches. Properties can be specified with /p:
The final, programmatically/script generated project file might look something like:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="foo.csproj" />
<Target Name="CopyFiles">
<Copy
SourceFiles="#(MySourceFiles)"
DestinationFolder="$(Destination)" />
</Target>
</Project>
The actual MSBuild command called might then be:
msbuild temp.proj /t:CopyFiles /p:"Destination=c:\MyProject\Destination"

Change working directory of msbuild.exe

I am executing MSBuild from a batch file. The MSBuild script is in a different directory than the directory I want MSBuild to consider the working directory when running the script. When invoking MSBuild.exe, how do I change its working directory?
Edit: More details
Let's say I have an MSBuild script located on some other server. I want to run a command thusly:
msbuild.exe \\my_server\c$\My\Path\To\Scripts\TestScript.msbuild
I run that command with my command prompt at c:\temp. Let's say my TestScript.msbuild has a task to create a file. The file has no path just a filename. I would expect that the file gets created inside c:\temp. But it doesn't it gets created next to the msbuild file that is sitting on the server. This is the behavior I want to change.
Edit #2
Here is the script I'm using in my test:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Files Include="HelloWorld.txt" />
</ItemGroup>
<Target Name="TouchFiles">
<Touch Files="#(Files)" AlwaysCreate="True" />
</Target>
</Project>
I am going into a command shell CDing into c:\temp and then executing the script. With or without the /p:OutDir switch that #Nick Nieslanik mentions, the HelloWorld.txt file appears in the folder where the *.msbuild file is and not c:\temp.
I ran across this while looking for a solution to my problem. Here's my solution (build script):
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Default">
<Exec Command="build.bat" WorkingDirectory="..\[your dir]\" />
</Target>
</Project>
I believe that's more what you were originally looking for?
My problem was that my batch file called another that it expected to be in the same directory, but since my ms build script was being run elsewhere, the batch file failed to find the second batch file.
#jkohlhepp - I see now. You are doing the opposite of what I described in my comment to some degree.
MSBuild common targets use the MSBuildProjectDirectory to determine the output folder unless you override that. So in your case, you could run
msbuild.exe \\my_server\c$\My\Pat\To\Scripts\TestScript.msbuild /p:OutDir=c:\temp
to force the output to be dropped in that location.
EDIT:
Given the project file above, you'd need to edit it to do something like the following for this to work:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<OutDir Condition=" '$(OutDir)' == '' ">bin\debug\</OutDir>
</PropertyGroup>
<ItemGroup>
<!-- Without prefacing files with paths, they are assumed relative to the proj file -->
<FilesToCreate Include="$(OutDir)HelloWorld.txt" />
</ItemGroup>
<Target Name="TouchFiles">
<Touch Files="#(FilesToCreate)" AlwaysCreate="True" />
</Target>
</Project>
In current versions of MSBuild the well-known property MSBuildStartupDirectory can be used in the msbuild file to retrieve the absolute path of the folder where MSBuild is called.
https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-reserved-and-well-known-properties?view=vs-2019
This option perhaps did not exist in msbuild around the time when the question was asked. I didn't want to spend too much time investigating it.

msbuild exec task call msbuild

I need to call exec and build a wix setup project.
Currently I have the following in my TFSbuild.proj
<PropertyGroup>
<WebRoot>$(DropLocation)\Latest\x86\Release\_PublishedWebsites\Web</WebRoot>
<DBRoot>$(DropLocation)\Latest\x86\Release\Database</DBRoot>
</PropertyGroup>
<PropertyGroup>
<Msbuildexe>"msbuild"</Msbuildexe>
<Configuration>"/p:Configuration:"Release""</Configuration>
<DefineConstants>" /p:DefineConstants:"WebRoot=$(WebRoot);DBRoot=$(DBRoot)""</DefineConstants>
<WixSolution>"$(MSBuildProjectDirectory)\Setup\Setup.sln"</WixSolution>
</PropertyGroup>
<Message Text="Bulding setup solution" />
<Message Text="$(Msbuildexe) $(Configuration) $(DefineConstants) $(WixSolution)" />
<Exec Command="$(Msbuildexe) $(Configuration) $(DefineConstants) $(WixSolution)" />
I've tried to simply as much as possible so I don't get confused where the " are meant to be. When I run this the debug message (2nd last command) outputs
"msbuild"
"/p:Configuration:"Release"" "
/p:DefineConstants:"WebRoot=\server\drops\app\Installer Build\Latest\x86\Release_PublishedWebsites\Web;DBRoot=\server\drops\app\Installer Build\Latest\x86\Release\Database""
"f:\builds\app\Installer Build\BuildType\Setup\Setup.sln"
And I get the following error in the log
'"msbuild"' is not recognized as an
internal or external command,
operable program or batch file.
f:\builds\app\Installer
Build\BuildType\TFSBuild.proj(538,5):
error MSB3073: The command ""msbuild"
"/p:Configuration:"Release"" "
/p:DefineConstants:"WebRoot=\server\drops\app\Installer Build\Latest\x86\Release_PublishedWebsites\Web;DBRoot=\server\drops\app\Installer Build\Latest\x86\Release\Database""
"f:\builds\app\Installer
Build\BuildType\Setup\Setup.sln""
exited with code 9009.
I'm not sure if this is being caused by not being able to call the msbuild command from the command line or a " issue. If it is because I can't call msbuild from the command line like this how would I go about referencing it, is there a property that points to it?
To start with, you don't need most of the quotes, especially if the paths you are using don't contain spaces, but I'd trim it down to this, allowing for spaces in the paths for $(WebRoot), $(DbRoot) and $(MSBuildProjectDirectory):
<PropertyGroup>
<WebRoot>$(DropLocation)\Latest\x86\Release\_PublishedWebsites\Web</WebRoot>
<DBRoot>$(DropLocation)\Latest\x86\Release\Database</DBRoot>
</PropertyGroup>
<PropertyGroup>
<MsbuildExe>{still-needs-a-path-to}\msbuild</MsbuildExe>
<Configuration>/p:Configuration:Release</Configuration>
<DefineConstants>/p:DefineConstants:"WebRoot=$(WebRoot);DBRoot=$(DBRoot)"</DefineConstants>
<WixSolution>"$(MSBuildProjectDirectory)\Setup\Setup.sln"</WixSolution>
</PropertyGroup>
<Message
Text="Bulding setup solution"
/>
<Message
Text="$(MsbuildExe) $(Configuration) $(DefineConstants) $(WixSolution)"
/>
<Exec
Command="$(MsbuildExe) $(Configuration) $(DefineConstants) $(WixSolution)"
/>
However, you still won't be able to execute MSBuild with this, since the path to MSBuild isn't specified. It is typically found in the $(WINDIR)\Framework\Microsoft.Net\v4.0.30319 folder. There are a few ways to get this, either encode it directly, rely on an environment variable (that has to be set up somehow), use the predefined $(MSBuildBinPath), or extract it from the registry using the MSBuild registry syntax, which would look like this:
$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSBuild\ToolsVersions\4.0\MSBuildToolsPath)
However, it isn't clear why you are running MSBuild using Exec rather than just using the MSBuild task. Change the line with Exec to this:
<MSBuild
Project="$(WixSolution)"
Properties="$(DefineConstants)"
/>
removing your declaration for <Configuration> and changing <DefineConstants> to this:
<DefineConstants>Configuration=$(Configuration);WebRoot=$(WebRoot);DBRoot=$(DBRoot)</DefineConstants>
Following up on my comment I'd suggest you try using the MSBuild Task instead of Exec:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="BuildWiXSolution">
<!-- Include the custom build targets installed with WiX -->
<Import Project="$(MSBuildExtensionsPath)\Wix\Wix.targets"/>
<PropertyGroup>
<WebRoot>$(DropLocation)\Latest\x86\Release\_PublishedWebsites\Web</WebRoot>
<DBRoot>$(DropLocation)\Latest\x86\Release\Database</DBRoot>
</PropertyGroup>
<ItemGroup>
<WiXSolution Include="$(MSBuildProjectDirectory)\Setup\Setup.sln">
<Properties>Configuration=Release</Properties>
<AdditionalProperties>WebRoot=$(WebRoot);DBRoot=$(DBRoot)</AdditionalProperties>
</WiXSolution>
</ItemGroup>
<Target Name="BuildWiXSolution">
<MSBuild Projects="#(WiXSolution)" />
</Target>
</Project>
It allows you to keep configuration properties and additional properties together with your Wix solution.

MSBuild doesn't respect PublishUrl property for my ClickOnce app

I'm trying to make a batch file to publish the few ClickOnce application we have in one click. I'm using msbuild for that, and as an example the below command line shows how I'm doing it:
msbuild
MyApp.sln
/t:Publish
/p:Configuration=Release
/p:PublishUrl="C:\Apps\"
/v:normal > Log.txt
(wrapped for easier reading)
when I run the above command it builds and publish the application in the release directory, i.e. bin\release! Any idea why msbuild doesn't respect PublishUrl property in my example above?
PS: I tried also different combinations including remove 'Configuration', use 'Rebuild' and 'PublishOnly' as targets, and remove the the quotation marks but without any success.
You are setting the wrong property. Try PublishDir instead.
You can pass it into MSBuild as you are or you can set it in the project file (or maybe the sln file too, not sure I always use the project file.) like this
<PropertyGroup>
<PublishDir>C:\Dev\Release\$(BuildEnvironment)\</PublishDir>
</PropertyGroup>
I've just done a few blog posts on MsBuild and ClickOnce stuff, check it out you 'should' find them useful...
Some features are done by Visual-Studio and not by the MSBuild-script. So the click-once-deployment behaves differently when it's executed from the command-line.
The ApplicationRevision isn't increased with every build. This works only when is exectued from Visual Studio
In in somecases, the PublishUrl isn't used. Quote from MSDN:
For example, you could set the PublishURL to an FTP path and set the InstallURL to a Web URL. In this case, the PublishURL is only used in the IDE to transfer the files, but not used in the command-line builds. Finally, you can use UpdateUrl if you want to publish a ClickOnce application that updates itself from a separate location from which it is installed.
I've created a special MSBuild-file which does this things. It runs the publish-target and copies then the files to the right location.
An example of the build-file, as requested by alhambraeidos. It basically runs the regular VisualStudio-build and then copies the click-once data to the real release folder. Note that removed some project-specific stuff, so it's maybe broken. Furthermore it doesn't increase the build-number. Thats done by our Continues-Build-Server:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Publish" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<!-- the folder of the project to build -->
<ProjLocation>..\YourProjectFolder</ProjLocation>
<ProjLocationReleaseDir>$(ProjLocation)\bin\Release</ProjLocationReleaseDir>
<ProjPublishLocation>$(ProjLocationReleaseDir)\app.publish</ProjPublishLocation>
<!-- This is the web-folder, which provides the artefacts for click-once. After this
build the project is actually deployed on the server -->
<DeploymentFolder>D:\server\releases\</DeploymentFolder>
</PropertyGroup>
<Target Name="Publish" DependsOnTargets="Clean">
<Message Text="Publish-Build started for build no $(ApplicationRevision)" />
<MSBuild Projects="$(ProjLocation)/YourProject.csproj" Properties="Configuration=Release" Targets="Publish"/>
<ItemGroup>
<SchoolPlannerSetupFiles Include="$(ProjPublishLocation)\*.*"/>
<SchoolPlannerUpdateFiles Include="$(ProjPublishLocation)\Application Files\**\*.*"/>
</ItemGroup>
<Copy
SourceFiles="#(SchoolPlannerSetupFiles)"
DestinationFolder="$(DeploymentFolder)\"
/>
<Copy
SourceFiles="#(SchoolPlannerUpdateFiles)"
DestinationFolder="$(DeploymentFolder)\Application Files\%(RecursiveDir)"
/>
<CallTarget Targets="RestoreLog"/>
</Target>
<Target Name="Clean">
<Message Text="Clean project:" />
<MSBuild Projects="$(ProjLocation)/YourProject.csproj" Properties="Configuration=Release" Targets="Clean"/>
</Target>
</Project>
I'll put in my 2 cents, this syntax seems to work (right or wrong):
/p:publishUrl="C:\\_\\Projects\\Samples\\artifacts\\Web\\"
For me, the soultion was to escape the path.
Instead of:
/p:PublishUrl="C:\Apps\"
Put:
/p:PublishUrl="C:\\Apps\\"