I'm trying to use a build script to run the dotless.compiler.exe to compile my .less files into .min.css on build:
<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/MsBuild/2003">
<!--
This MSBuild Script will compile all [*.less] files in the /CSS folder to their [*.min.css] counterparts.
-->
<ItemGroup>
<LessFiles Include="Styles\*.less" />
</ItemGroup>
<Target Name="CompileDotlessCss" AfterTargets="AfterBuild">
<ItemGroup>
<Binaries Include="*.dll;*.exe"/>
</ItemGroup>
<!-- Compile dotLess CSS into minified full CSS -->
<Exec Command="[MSBuild]\dotless.compiler.exe -m %(LessFiles.FullPath) $([System.String]::Copy('%(LessFiles.FullPath)').Replace('.less','.min.css'))" />
</Target>
</Project>
But when I build I get:
The command "[MSBuild]\dotless.compiler.exe -m C:\Source Control\MyProject\MyProject.Web\Styles\main.less C:\Source Control\MyProject\MyProject.Web\Styles\main.min.css" exited with code -1.
I suspect it has to do either with my project being under source control or simply the fact that the file path has a space in the "Source Control" folder.
How can I wrap the path in quotes (since the command itself is in quote)?
If it's the source control factor and it fails because the files are locked (I tried building with the file checked in).. how do I deal with this? I obviously want to keep my project under source control.
You can use "" and '' interchangeably in MSBuild files, inc. for paths with spaces, worst cases might require XML escapes like ". For better output beyond exit code try to up verbosity to detailed or diagnostic, may be it's something to do with [MSBuild], shouldn't it be a $() property?
Related
At the moment we are switching from VS2015 to VS2017. One of our upgrade steps is to switch from stylecop to the new Stylecop.Analyzer package. The new Stylecop is using 2 files. The stylecop.json and the Stylecop.ruleset.
The target: I want to provide the stylecop files as a custom nuget package. But I dont know how to create the needed .csproj entries.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
...
<CodeAnalysisRuleSet>packages\My.StyleCop.1.0.0-pre15\RuleSet\My.StyleCop.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
...
<ItemGroup>
<AdditionalFiles Include="packages\My.StyleCop.1.0.0-pre15\Config\stylecop.json">
<Link>stylecop.json</Link>
</AdditionalFiles>
</ItemGroup>
</Project>
In the past, there was the possibility to use a install.ps1 script to do this stuff. But with NuGet 3. (or 4.) the install scripts are obsolete and will be ignored.
I already tried to use My.StyleCop.targets:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<AdditionalFiles Include="packages\My.StyleCop.1.0.0-pre17\Config\stylecop.json">
<Link>stylecop.json</Link>
</AdditionalFiles>
</ItemGroup>
</Project>
But here I have some issues, too. Since NuGet 3. (or 4.) there is no solution wide package folder and I dont know any variable or placeholder I can use here to get a absolute or relative path to my package.
You can add .props or .targets files to the build folder in your packages and they will be imported to the projects.
On the .props file, you can use the MSBuildThisFileDirectory MSBuild variable that represents the folder where that file is located.
Thanks to Paulo.
How I did it:
This is the structure of my NuGet package.
The solution is quiet easy. You need to create to files. A .props and a .targets file named like the NuGet package and place them in the build folder of your package.
In these MSBuild files you can use the $(MSBuildThisFileDirectory) variable to get the path of your NuGet package.
MSBuildThisFileDirectory = C:\Users\UserName\.nuget\packages\sig.stylecop\1.0.0-pre23\build\
My SIG.StyleCop.props file:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)\..\RuleSet\SIG.combiLink.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
</Project>
My SIG.StyleCop.targets file:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<AdditionalFiles Include="$(MSBuildThisFileDirectory)\..\Config\stylecop.json">
<Link>stylecop.json</Link>
</AdditionalFiles>
</ItemGroup>
</Project>
Cause of the structure of my package i need to navigate (..) into the Config and into the RuleSet folder.
The variable $(MSBuildThisFileDirectory) already includes the backslash at the end. It is important to omit the backslash when you reference the ruleset and the stylecop.json file:
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)..\RuleSet\SIG.combiLink.ruleset</CodeAnalysisRuleSet>
<AdditionalFiles Include="$(MSBuildThisFileDirectory)..\Config\stylecop.json">
With the double backslash I experienced two strange problems in Visual Studio 2017:
Unit tests rebuild the code each time I start them, even without any code change
The IDE shows many StyleCop errors in the Error List window and shows red marks in the scroll bar even for rules that are explicitly disabled in the rule set.
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.
I'm a make(1) guy by nature. What I want to do is the equivalent of this make fragment:
FILES = generated.cs
app.exe : $(FILES)
csc -out:$# $(FILES)
generated.cs :
echo "// generated file" > $#
That is, $(FILES) contains a list of files, some of which may be generated by other targets within the Makefile. This Just Works.
I would like to do the ~same thing with MSBuild. Unfortunately, my attempt has failed:
<Target Name="BuildGenerated"
Outputs="Generated.cs"
>
<WriteLinesToFile
File="Generated.cs"
Lines="// generated file"
Overwrite="True"
/>
</Target>
<ItemGroup>
<Compile Include="Generated.cs" />
</ItemGroup>
That is, use <Compile/> to include a generated file and have MSBuild deduce that since Generated.cs doesn't exist, it should find some <Target/> which will generate that file and then execute it.
It looks like the only way to do something like this is to add pre-build steps, but that seems like a hack. Are pre-build steps the only way to do this? Or is there some way to make MSBuild act like it has a brain?
Update 1: For reference, this would be the pre-build incantation needed to make it work, and (again) this is something I'd rather avoid if possible:
<PropertyGroup>
<CompileDependsOn>
BuildGenerated;$(CompileDependsOn)
</CompileDependsOn>
</PropertyGroup>
This would need to occur after the <Import Project="..." /> element(s) defining <CompileDependsOn/>.
The problem is that your file is a generated one and MsBuild parses the item and property group only once (at the beginning of your build). Thus, when MsBuild tries to include the generated.cs file, it is not created yet and MsBuild does include nothing at all.
A correct way of doing it would be to do the include part inside the BuildGenerated target which would cause it to be dynamically evaluated.
<Target Name="BuildGenerated"
Outputs="Generated.cs"
>
<WriteLinesToFile
File="Generated.cs"
Lines="// generated file"
Overwrite="True"
/>
<ItemGroup>
<Compile Include="Generated.cs" />
</ItemGroup>
</Target>
More information in my answer to this question : Bin folder not being copied with MSBuild, Teamcity
EDIT
Sorry, I did not read correctly your question. in your makefile, in the app.exe target (the default one) you explicitely call your generated.cs target. You can do the same with MsBuild.
By default, MsBuild search for the Build target (the 'all' in makefile), if your main target is "app.exe" you have to call BuildGenerated target within with one of the following option :
<Target Name="app.exe" DependsOnTargets="BuildGenerated>
<!-- Stuff here -->
</Target>
or
<Target Name="app.exe">
<CallTarget Targets="BuildGenerated"/>
<!-- Stuff here -->
</Target>
If you don't set a default target, you can do it either via commandline with the /t parameter or in the project declaration :
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="app.exe">
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
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\\"