i´m having a problem with MSBuild and Powershell. There is a PS-script that i want to execute within the MSBuild exec-Task.
The Problem: Running the Script direct from CMD works, but running the script within MSBuild I get an error.
Here the MSBuild script:
<Import Project="$(MSBuildExtensionsPath)\ExtensionPack\4.0\MSBuild.ExtensionPack.tasks"/>
<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets"/>
<PropertyGroup>
<PathToSvnClient>C:\Program Files (x86)\CollabNet\Subversion Client</PathToSvnClient>
</PropertyGroup>
<ItemGroup>
<!-- set Folder to Svn Repository for svn info command-->
<SvnFolder Include="$(MSBuildProjectDirectory)\.."/>
</ItemGroup>
<Target Name="SvnInfo">
<!-- get SVN Revision and Repository Path -->
<SvnInfo LocalPath="%(SvnFolder.FullPath)" ToolPath="$(PathToSvnClient)">
<Output TaskParameter="Revision" PropertyName="Revision" />
<Output TaskParameter="RepositoryPath" PropertyName="RepositoryPath" />
</SvnInfo>
</Target>
<Target Name="SetProductVersion" DependsOnTargets="SvnInfo">
<Exec Command="powershell -file "Scripts\SetSth.ps1" -PARAM "$(PathToSth)" -SVNID $(Revision) -SVNFOLDER "$(RepositoryPath)"" LogStandardErrorAsError="true" ContinueOnError="false"/>
</Target>
The Command is executed exactly the same way as on CMD, but i get an exception from the Powershell Script for the SVNFOLDER param.
The Command that is executed looks like this:
powershell -file "Scripts\SetSth.ps1" -PARAM "C:\abc\cde" -SVNID 1234
-SVNFOLDER "https://domain/svn/rep/branches/xy%20(Build%2012)"
So from CMD it works, from within MSBuild not. I have no idea why. I hope you got an idea.
What about this approach playing with double and singles quotes:
<Target Name="SetProductVersion" DependsOnTargets="SvnInfo">
<Exec Command="powershell -command "& {Scripts\SetSth.ps1 -PARAM '$(PathToSth)' -SVNID '$(Revision)' -SVNFOLDER '$(RepositoryPath)'}"" LogStandardErrorAsError="true" ContinueOnError="false"/>
</Target>
Double check your paths.
Remember, powershell invoked in this way runs as Msbuild.exe under whatever user is executing the build. To msbuild.exe, a straight call to cmd.exe is going to start in the working directory where msbuild lives.
Assume -file "Scripts\SetSth.ps1" references C:\users\yourusername\Scripts\SetSth.ps1
So for you, calling cmd.exe and running that may work just fine, b/c your working directory is going to match C:\users\yourusername
For msbuild.exe, its likely unable to find that file, as its starting in something like *C:\Windows\Microsoft.NET\Framework\v4.0*
So it's looking for C:\Windows\Microsoft.NET\Framework\v4.0\Scripts\SetSth.ps1
I would try making that file path fully qualified. If that still doesn't work, have cmd.exe dump its results into a property and have msbuild log it. Then you can review the paths.
Related
I have added a code piece in my MS build file to run a powershell script. But while building it gives me a error error MSB4067: The element "Exec" beneath element "Project" is unrecognized.
Below is the code:
<PropertyGroup>
<PowerShellExe Condition=" '$(PowerShellExe)'=='' ">
%SYSTEMROOT%\System32\WindowsPowerShell\v1.0\powershell.exe
</PowerShellExe>
<ScriptLocation Condition=" '$(ScriptLocation)'=='' ">
D:\Synopsis\SynopsysDetect.ps1
</ScriptLocation>
</PropertyGroup>
<Exec Command="%SYSTEMROOT%\System32\WindowsPowerShell\v1.0\powershell.exe -NonInteractive -ExecutionPolicy Unrestricted -command ^"^& {^&'D:\Synopsis\SynopsysDetect.ps1'} ^"" />
Not sure if the issue is with code or have I placed the code piece at the wrong place. Any help would be appreciated. Thanks.
Moving the above piece of code completely into the "Target" tag fixed the issue.
MSB4067 is "UnrecognizedChildElement". It means that the element containing this line doesn't "know" the exec command. Since the tag </PropertyGroup> is above your line, I have no idea how your structure look like. Please share more of the file blurring confidential parts.
Also see that TFS has a "Run powershell step" that you can use.
I've noticed that when MSBuild fails, the value of the $LastExitCode variable is always 0. I'm on Windows 7, with MSBuild v4.0 and PowerShell 2.0. This is my MSBuild scritpt:
<?xml version="1.0" encoding="UTF-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0" DefaultTargets="Fail">
<Target Name="Fail">
<Error />
</Target>
</Project>
When I run:
msbuild.exe MyProject.csproj
I can see in the output that MSBuild fails, but when I check $LastExitCode, it has a value of 0. Anyone know what might be going on?
I've tried setting $(ErrorActionPreference) to Stop, but that didn't work. I re-opened a new PowerShell window, that didn't work either.
I ran into a problem recently. It turned out some code in my profile that was updating $lastexitcode- the code was a custom prompt generator. Try running powershell without your profile with "powershell -noprofile" to see if the problem could be code in your profile.
Consider checking the value $?
Its false if $lastexitcode is nonzero... It works for me even when $lastexitcode did not.
$LASTEXITCODE is for win32 executables and $? is for PS commands. What is the value of %errorlevel% when you run the msbuild.exe from cmd?
Please read http://techibee.com/powershell/what-is-lastexitcode-and-in-powershell/1847 if you want to know more about difference between these two special variables.
I'm encountering an issue with the <exec> task on batch files in my NAnt project files. When running on Windows XP SP 3 (but not Windows Vista or Windows Server 2008) and using NAnt 0.85 or 0.91alpha2, the <exec> task will always succeed (returning an error code of 0) regardless of what the executed script returned.
As an example, I wrote the following NAnt target:
<target name="build">
<exec program="fail.bat"
failonerror="false" resultproperty="makeall.result">
</exec>
<echo message="Makeall task returned result ${makeall.result}"/>
<fail if="${int::parse(makeall.result) != 0}">Encountered ${makeall.result} errors.</fail>
</target>
which calls the following batch file:
exit /b 1
Under normal operation (Windows Vista), the result of running NAnt is:
build:
[exec] C:\Users\Will\Code>exit /b 1
[exec] C:\Users\Will\Code\fail.build(6,4):
[exec] External Program Failed: C:\Users\Will\Code\fail.bat (return code was 1)
[echo] Makeall task returned result 1
BUILD FAILED - 1 non-fatal error(s), 0 warning(s)
But on two different Windows XP SP3 machines, the result of running NAnt is:
build:
[exec] C:\Documents and Settings\Will\My Documents\My Code>exit /b 1
[echo] Makeall task returned result 0
BUILD SUCCEEDED
Although I'm not discounting the possibility that this is a bug, I find it much more likely that I'm forgetting some crucial configuration setting on either Windows or NAnt that is causing this behavior. Has anyone else encountered this? Is there a reasonably elegant workaround?
Nant contains 2 functions that might be useful to you:
environment::get-operating-system
operating-system::get-version
With these you might be able to write a target to generate an expected build result property. Then it should be possible to compare the expected result to the actual result returned. This way you can handle OS specific scenarios.
As for the 'root-cause' of the difference between the two results, I don't know why they would be different between versions of Windows.
This is the format that we use, it always seems to work for us. the dexbuild .bat file contains
---------2.0 ------------- "C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\devenv" C:\YourProject\YourSoultion.sln /Rebuild release
----------3.5-------------- "C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\devenv" C:\YourProject\YourSoultion.sln /Rebuild release
similar logic for 4.0
ccnet.config
<tasks>
<nant>
<executable>C:\Nant\Nant0.86\bin\nant.exe</executable>
<baseDirectory>.</baseDirectory>
<buildFile>C:\NANT_SCRIPTS\build.xml</buildFile>
<targetList>
<target>DexWeb</target>
</targetList>
<buildTimeoutSeconds>2000</buildTimeoutSeconds>
</nant>
</tasks>
build.xml file
<target name="DexWeb">
<exec program="C:\NANT_SCRIPTS\continous\dexbuild.bat" />
</target>
I was able to get this working using comments on this blog post, see comments from Scott.
at start of batch file I set SETLOCAL enabledelayedexpansion
then using following SQLCMD
FOR /R %SCRIPTDIR% %%G IN (*.sql) DO (
sqlcmd -S%SERVER% -d %DB% -E -h-1 -w255 -i "%%G" -b -m 1 -r
echo %%G
IF !ERRORLEVEL! NEQ 0 GOTO ERROR
)
and finally labels in batch file
:ERROR
SET ERRORLEVEL=!ERRORLEVEL!
GOTO ERRORExit
:ResultCode
EXIT /B %1
:ERRORExit
ENDLOCAL & CALL :ResultCode %ERRORLEVEL%
Please note return value is still not being passed back to nant but build fails OK. I have tried various combinations of resultproperty and failonerror to no success.
It works on WinXPsp3 with NANT0.90.
SC
We're in the process of evaluating MSBuild and Nant for deploys. We may roll our own tool. One thing that a different business unit -- let's call it DeptA -- would really like to have (as in it better have it) is the ability for someone in DeptA to read the script and see what it will do. Currently we do this with .bat files. We hates the bat files. Nasty nasty bat files's. But if we ask DeptA to learn a new script language (nant, msbuild) they may be resistant.
Ideally the build tool of choice would be able kick out a list of actions without doing anything else. Along the lines of:
Stop service ABC on server Z
Stop service DEF on server Z
Copy all files from
\server\dirA\dirB to \server2\dirC
Start service ABC on server Z
Start service DEF on server Z
Run all scripts in dir
\server\dirA\dirC
Is it possible to do this with MSBuild? NAnt? Without me re-learning XSL?
If I was you I would actually blend MSBuild and MSDeploy. You should have your MSBuild script perform the actions like start/stop service etc. Then let MSDeploy to the file copy. With MSDeploy you can use the -whatif switch to indicate that you only want a report of the actions to be executed instead of actually executing it. MSBuild unfortunately doesn't offer such an option out of the box you will have to "build" that into your scripts. You can do this with properties and conditions. For example it might look something like this:
<Project ...>
<PropertyGroup>
<!--
Create the property to use, and declare a default value.
Here I've defaulted this to true because it is better to force the
caller to explicitly specify when to perform the action instead of it
being the default.
-->
<SimulateExecution Condition= '$(SimulateExecution)'==''>true</SimulateExecution>
</PropertyGroup>
<Target Name="Deploy">
<Message Text="Deploy started" />
<Message Text="Stop service ABC on server Z"/>
<WindowsService ... Condition=" '$(SimulateExecution)'=='false' "/>
<Message Text="Stop service DEF on server Z"/>
<WindowsService ... Condition=" '$(SimulateExecution)'=='false' "/>
<!-- Call MSDeploy with the Exec task. -->
<PropertyGroup>
<MSDeployCommand>...\msdeploy.exe YOUR_OPTIONS_HERE</MSDeployCommand>
<!-- Append the -whatif to the command if this is just a simulation -->
<MSDeployCommand Condition= '$(SimulateExecution)'=='false' ">$(MSDeployCommand) -whatif</MSDeployCommand>
</PropertyGroup>
<Exec Command="$(MSDeployCommand)" />
... More things here
</Target>
</Project>
For the service actions you can use the WindowsService task from the MSBuild Extension Pack. You will have to fill in the blanks there.
When you call MSDeploy you should just use the Exec task to invoke msdeploy.exe with your parameters. If you pass the -whatif it will not actually perform the actions, just report what it would have done. These will be logged to the msbuild log. So if you invoke msbuild.exe with /fl you will get those actions written out to a file. The only issue that I've seen when taking this approach is that for msdeploy.exe you many times have to use full paths (those without ..) which can sometimes be tricky so be wary of such paths.
We use NAnt extensively for our build system. Recently, I've written a couple PowerShell Cmdlets to perform a few database related things. At first, the intent of these Cmdlets was not to necessarily run within our build process. However, this has recently become a need and we would like to run a few of these Cmdlets from our NAnt based build process.
These Cmdlets are written in C# and we have a SnapIn for them (if that matters at all).
A few ideas:
Use the exec task to call PowerShell? (not sure how this would work though)
Write a custom NAnt task that references and uses the Cmdlet?
What might be a good way to do this?
You can use the below exec task in your nant script to call your ps cmdlets.
<exec program="powershell" workingdir="${BuildManagementDir}" verbose="true">
<arg value="-noprofile"/>
<arg value="-nologo"/>
<arg value="-noninteractive"/>
<arg value="-command"/>
<arg value=".\xyz.ps1"/>
</exec>
You could certainly use the exec task, setting the program attribute to powershell.exe and passing in the command line something like "-Command { }".
Alternatively, you could create a custom NAnt task that internally uses the powershell hosting APIs to execute your cmdlets or scripts. There's a simple example of this (using the PS v1 APIs) here.
Based on JiBe's answer its the other way around, here is the working solution. When running powershell that takes arguments you need to run the powershell script then the arguments.
PS yourscript.ps1 -arg1 value1 -arg2 value2
In NAnt:
<exec program="powershell" workingdir="${powershell_dir}" verbose="true">
<arg value=".\yourscript.ps1"/>
<arg value="-arg1 ${value1}"/>
<arg value="-arg2 ${value2}"/>
</exec>
The best way I think is to define the arguments in PS for NAnt is like
$value1=$args[0]
$value2=$args[1]
So in command line you will use:
PS yourscript.ps1 some_value1 some_value2
Then this translates in NAnt like:
<property name="Value1" value="some_Value1" />
<property name="Value2" value="some_Value2" />
<exec program="powershell" workingdir="${powershell_dir}" verbose="true">
<arg value=".\yourscript.ps1"/>
<arg value="${value1}"/>
<arg value="${value2}"/>
</exec>
The best way is to use similar methods as one would use in task scheduler. That means run powershell with the -command argument and begin the command with &.
For example:
<exec program="powershell" workingdir="${ifscriptrequires}" verbose="true">
<arg line="-Command" />
<arg line="$amp; C:\scripts\somescript.ps1 -SwitchParam -someargument 'somevalue' 'somepositionalparameter'" />
</exec>