SolutionDir set to *Undefined* in post-build xcopy event - msbuild

I have a project that has a post-build event that xcopies a DLLs to a certain directory:
xcopy "$(TargetDir)$(TargetName).dll" "$(SolutionDir)..\UdpLocationService\bin\Plugins\" /d /y
xcopy "$(TargetDir)$(TargetName).pdb" "$(SolutionDir)..\UdpLocationService\bin\Plugins\" /d /y
However, I have CruiseControl.NET set up as a build server and MSBuild is failing on building that project due to this xcopy post-build event:
MSB3073: The command "xcopy "C:\Build\Services\Windows\VehicleServer\Plugins\Payload\bin\Debug\Payload.dll" "*Undefined*..\UdpLocationService\bin\Plugins\" /d /y xcopy "C:\Build\Services\Windows\VehicleServer\Plugins\Payload\bin\Debug\Payload.pdb" "*Undefined*..\UdpLocationService\bin\Plugins\" /d /y" exited with code 4. in Microsoft.Common.targets(3397, 13)
Any suggestions to get this fixed?

I just ran into the same problem with TeamCity.
The issue here is the $(SolutionDir) property in your build file. You haven't defined it in your call to MsBuild (this is why you see the word undefined in your output).
Call msbuild with the property set, like this:
msbuild myproject.csproj /property:SolutionDir="solution directory"\
Where "solution directory" is the directory containing your solution file. Note the trailing slash, you'll need that to make sure the path is correctly formed.

I fixed this for problems with the Microsoft.SqlServer.Compact nuget package (which adds a similar post-build script), by adding:
<SolutionDir Condition="'$(SolutionDir)'=='' or '$(SolutionDir)'=='*Undefined*'">..\</SolutionDir>
right above the <PostBuildEvent>. You'll want to adjust the relative path to match your project layout.

Follow these steps:
Unload your project file (e.g. *.csproj)
Open your project file for editing
Find the AfterBuild target
Separate out the two invocations of XCopy into two distinct Exec tasks
Save your changes and Reload your project file

Related

Run MSBuild from Command Line task in Azure Devops

I'm trying to execute msbuild on Azure Devops. Because of that I cannot use the MSBuild task provided.
When I use a Command Line task the command is not recognised. On my local machine I load vcvarsall.bat before I use msbuild. But I've not been unable to work out how to obtain that path in Azure Devops. Doesn't appear to be a Develop Command Prompt task for Azue Devops either.
Any ideas on how I can use msbuild from a Command Line task or Batch Script task? Using their Hosted VS agent.
The best way to do this in a supported way is to use vswhere. The following bit of script will install vswhere (using chocolatey) and then query the installer registry where msbuild can be found. Replace -latest with a more specific version if you need that:
choco install vswhere
for /f "tokens=*" %%i in ('vswhere -latest -requires Microsoft.Component.MSBuild -find MSBuild\**\Bin\MSBuild.exe -nologo') do set msbuildpath="%%i"
echo "##vso[task.setvariable variable=msbuildpath]%msbuildpath%"
This will save the path to msbuild to the environment variable %msbuildpath% as well as the pipeline variable (for this stage) $(msbuildpath).
You can then either use a second run commandline task and pass in $(msbuildpath) or you can simply call MsBuild from the same piece of script mentioned above by calling:
%msbuildpath%
This will make sure your script will remain working, even if Microsoft upgrades their images and moves some things around (which does happen).
You can also get vswhere using wget or invoke-webrequest -outfile from the following location:
https://github.com/Microsoft/vswhere/releases/latest/download/vswhere.exe
Other samples for vswhere syntax can be found on the project wiki, including the syntax for PowerShell.
If you use Hosted Agent 2017 you can run the msbuild.exe from the Command Line task in this way:
Command Line version 1:
Command Line version 2:
Results:
If you are interested in seeing how the built-in Microsoft task resolves the path, all the Azure Devops tasks are provided open-source. These are the path functions you probably care to review.
Here is the solution I came up with using only built-in pipeline tasks which makes the MSBuild bin directory available on the path environment variable.
Create a PowerShell task to generate an MSBuild project to capture and output to a file the variables you are interested in (ex. MSBuildBinPath)
PowerShell script
"<Project DefaultTargets=`"DetectMsBuild`">
<ItemGroup>
<OutFile Include=`"`$(MsBuildDetectionFile)`" />
<OutFile Condition=`"'`$(OutFile)' == ''`" Include=`"msbuildInfo.json`" />
</ItemGroup>
<Target Name=`"DetectMsBuild`">
<PropertyGroup>
<MsBuildPaths>
[{
`"Name`": `"BinPath`",
`"Value`": `"`$(MSBuildBinPath.Replace('\', '\\'))`"
}]
</MsBuildPaths>
</PropertyGroup>
<WriteLinesToFile
File=`"#(Outfile)`"
Lines=`"`$(MsBuildPaths)`"
Overwrite=`"true`"
Encoding=`"UTF-8`" />
</Target>
</Project>" | Out-File -FilePath "msbuilddetect.proj" -Encoding utf8
Set the working directory and any variables accordingly.
PowerShell task settings screenshot:
Create an MSBuild task to run the project file generated by the previous task. Ensure the MSBuild version is set to the version you want to use.
MSBuild task settings screenshot:
Last, create another PowerShell task that will parse the outputted JSON file of the extracted variables and sets environment variables accordingly.
PowerShell script
Write-Host "Current path: $($env.Path)`n`n"
$msBuildVariables = Get-Content -Path msbuildInfo.json | ConvertFrom-Json
$Path = "$($msBuildVariables[0].Value);$($env:Path)"
Write-Host "##vso[task.setvariable variable=Path;]$Path"
PowerShell task settings screenshot:
Here is a screenshot of the task order in the build pipeline.

MsBuild publish website without using publish profile

A publish profile to publish a Visual Studio Website can be used from both the Visual Studio 2013 publish dialog, and from the command line MsBuild as explained in this question Using msbuild to execute a File System Publish Profile
C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe
./ProjectRoot/MyProject.csproj /p:DeployOnBuild=true /p:PublishProfile=FileSystemDebug
However, I want to get rid of the publish profile completely and do everything from the command line - because I do not want the publish path to be hard-coded in the PublishProfile xml file. How can I specify the same options directly in the command line arguments? I have tried using OutDir instead, but that results in a different behavior than the path specified in the PublishProfile (an extra _PublishedWebsites is appended to my path).
You can actually override the PublishProfile settings from the command line. You would want this parameter:
/p:publishUrl=pathToPublishHere
If it's local filesystem this should work just fine.
publish.bat
SET PROJECT="D:\Github\MyWebSite.csproj"
SET MSBUILD_PATH="C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin"
SET PUBLISH_DIRECTORY="C:\MyWebsitePublished"
cd /d %MSBUILD_PATH%
MSBuild %PROJECT% /p:DeployOnBuild=True /p:DeployDefaultTarget=WebPublish /p:WebPublishMethod=FileSystem /p:DeleteExistingFiles=True /p:publishUrl=%PUBLISH_DIRECTORY%
needs a Visual Studio to be installed on server.

MSBUILD : error MSB1001: Unknown switch Switch: /Y

I'm using team city to make an automatic deploy and MSBuild won't work...
In the build step the the command line parameters look like this:
ProjectName.deploy.cmd /y /M:https://[WebDeployUrl:8172]/MsDeploy.axd /u:username /p:password –allowUntrusted /A:basic
this works fine from my machine, but the build server fails with the following response:
[MSBuild output] MSBUILD : error MSB1001: Unknown switch.
[MSBuild output] Switch: /Y
Anyone has an idea about this?
This is a very old question, and the asker likely sorted out their issue long ago, but here goes anyways:
MSBuild tasks in TeamCity require a commandline that pertain to MSBuild.exe specifically, IIRC.
That is, TeamCity is executing MSBuild.exe with the parameters you've given it like so:
msbuild.exe ProjectName.deploy.cmd /y /M:https://[WebDeployURL]:8172]/MsDeploy.axd /u:username /p:password -allowUntrusted /A:basic
Of course, MSbuild has no idea what those switches are, nor would it be able to process 'ProjectName.deploy.cmd' as an MSBuild file.

Using Code Contracts in library code built with MSBuild

I've started using Code Contracts in all new code I'm writing, such as in a framework library I'm building to help bootstrap IoC, O/RM, etc., in an ASP.NET MVC application. I've written a simple build script for this framework library that looks like the following:
#echo off
echo.
echo Cleaning build output (removing 'obj' and 'bin' folders)...
for /f "tokens=*" %%G in ('dir /b /ad /s bin') do rmdir /s /q "%%G"
for /f "tokens=*" %%G in ('dir /b /ad /s obj') do rmdir /s /q "%%G"
rmdir /s /q build
echo.
echo Starting the build...
call "%VS100COMNTOOLS%\vsvars32.bat"
msbuild Integration.build /target:Build
echo.
echo Done!
pause
This doesn't work. What I end up with in my build folder if I run this is, for whatever reason, assemblies that aren't fully rewritten by ccrewrite alongside .pdb.original, .rewritten and .csproj.FileListAbsolute.txt files that litter the output directory.
What does work is first building the solution in Visual Studio 2010, commenting out line 3 through 7 in the batch file and running it again. I then end up with properly rewritten assemblies and no .pdb.original nor .rewritten files.
What I've deduced from this is that Visual Studio 2010 somehow triggers the Code Contract rewriter properly so the resulting assemblies from the Visual Studio 2010 build is re-used by the command-line MSBuild call, so what my batch script basically does is just copying files to the build directory. Rather useless, in other words.
I've read this, but Jon's problem seems different from mine since ccrewrite is obviously doing something, but it's just not completing the rewriting for whatever reason. The Integration.build file builds the correct configuration (that has Code Contracts enabled in the .csproj files) and everything else looks right, it just doesn't work properly.
So, I'm wondering: How do I run MSBuild the way Visual Studio 2010 is where ccrewrite does what it's supposed to and doesn't litter my output directory with .rewritten and .pdb.original files? Does anyone have a perfect example of how an MSBuild file doing proper Code Contracts rewriting looks like?
The answer is in the script. All Visual Studio is ever going to do is run MSBuild tasks that will invoke others. One thing you can do is go to Tools|Options|Build... and turn on logging so you can see in detail which bit is doing what to generate the artifacts.
How would one do such a complex and involved thing? Read a guide to MSBuild such as Hashimi p1 and Part 2.
Then dig into the source for the build in e.g.:
C:\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets
The best way to get there is to open your .csproj and look what it includes and go via that (the .CSharp.targets is the first one - the one I cited comes further down the stack).
(That and wait for someone to pop in with an actual answer!)
I've played a little with Code Contract's static analysis and it is pretty cool.
Now trying to set up TeamCity build ...
Here is msbuild integration info from Microsoft Research (see page 44)

How to get MSBuild Exec to run a java program?

I am trying to run a command line action in my Team Build (MSBuild).
When I run it on the command line of the build machine it works fine. But when run in the build script I get a "exited with code 3".
This is command that I am running:
C:\Program Files\Wavelink\Avalanche\PackageBuilder>.\jresdk\bin\java -classpath
"WLUtil.jar;WLPackageBuilder.jar" com.wavelink.buildpkg.AvalanchePackageBuilder
/build PackageName
This command only works when run from the above directory (I have tried running it from c:\ with the full path at it fails).
When I try to run it using ms build this is my statement:
<PropertyGroup>
<!--Working directory of the Package Builder Call-->
<PkgBldWorkingDir>"C:\Program Files\Wavelink\Avalanche\PackageBuilder"</PkgBldWorkingDir>
<!--Command line to run to make Package builder "go"-->
<PkgBldRun>.\jresdk\bin\java" -classpath "WLUtil.jar;WLPackageBuilder.jar" com.wavelink.buildpkg.AvalanchePackageBuilder</PkgBldRun>
</PropertyGroup>
<!--Run package builder command line to update the Ava File.-->
<Exec ContinueOnError="true" WorkingDirectory="$(PackageBuilderWorkingDir)"
Command="$(PkgBldRun) /build PackageName"/>
As I said above this "exits with code 3". This is the full output:
Task "Exec"
Command:
.\jresdk\bin\java -classpath "WLUtil.jar;WLPackageBuilder.jar" com.wavelink.buildpkg.AvalanchePackageBuilder /build PackageName
The system cannot find the path specified.
MSBUILD : warning MSB3073: The command ".\jresdk\bin\java -classpath "WLUtil.jar;WLPackageBuilder.jar" com.wavelink.buildpkg.AvalanchePackageBuilder /build PackageName" exited with code 3.
The previous error was converted to a warning because the task was called with ContinueOnError=true.
Build continuing because "ContinueOnError" on the task "Exec" is set to "true".
Done executing task "Exec" -- FAILED.
It says it can't find the file (who knows what file).
Any ideas on how to make this run a command line action in MS Build?
So... here is the answer.
First my code had a error in it. I defined my working directory as PkgBldWorkingDir but used it as PackageBuilderWorkingDir. That was my first error.
The second one was putting quotes in my working dir property (PkgBldWorkingDir). The " messes that up. Don't pass them in when setting up a working directory. It can handle spaces in a path with out the quotes.