How to call MSBuild tasks from FinalBuilder? - msbuild

The only way I see now is to create xml file for MSBuild containing needed tasks invocations and then run MSBuild directly by calling "Execute Program" action. Is there any standard way of doing this using FinalBuilder?

In FinalBuilder 6 you can use a MSBuild Task. However to be honest in our build script I found that using a batch file and the 'Execute Program' to be a better solution.
Edit: Quickly doing some reading on this topic I now remember why I used a batch file. The FB6 MSBuild action is a little counterintuitive as not all the properties are accessible from the 'default view' and you need to change to the 'property grid'.
Update: From your comment; if you want to run an individual MSBuild task and not use the 'Execute Program' action then you will need to create your own FB action. I have never created an custom action myself but apparently they are really simple.
This is the batch file that I used:
#ECHO off
SET Action=%1
SET Configuration=%2
SET Platform=x86
SET CommonTools=%VS90COMNTOOLS%
SET SourceDir=%CD%\..\..
SET SolutionFilename=Solution.sln
SET MSBuild=C:\Windows\Microsoft.NET\Framework\v3.5\MSBuild.exe
IF "%Action%" == "" SET Action=Rebuild
IF "%Configuration%" == "" SET Configuration=Release
:BUILD
%MSBuild% "%SourceDir%\%SolutionFilename%" /v:m /t:%Action% /p:Configuration=%Configuration% /p:DenEnvDir="%CommonTools%..\IDE\" /p:SolutionDir="%SourceDir%" /p:Platform=%Platform%
:END
ECHO.
ECHO ErrorLevel: %ERRORLEVEL%
EXIT /B %ERRORLEVEL%

You cannot just run an msbuild task from within another application without instantiating the msbuild environment on which it depends. You will need an msbuild project file at the very least.

Related

How can I conditionally include large scripts in my ssdt post deployment script?

In our SSDT project we have a script that is huge and contains a lot of INSERT statements for importing data from an old system. Using sqlcmd variables, I'd like to be able to conditionally include the file into the post deployment script.
We're currently using the :r syntax which includes the script inline:
IF '$(ImportData)' = 'true'
BEGIN
:r .\Import\OldSystem.sql
END
This is a problem because the script is being included inline regardless of whether $(ImportData) is true or false and the file is so big that it's slowing the build down by about 15 minutes.
Is there another way to conditionally include this script file so it doesn't slow down the build?
Rather than muddy up my prior answer with another. There is a special case with a VERY simple option.
Create separate SQLCMD input files for each execution possibility.
The key here is to name the execution input files using the value of your control variable.
So, for example, your publish script defines variable 'Config' which may have one of these values: 'Dev','QA', or 'Prod'.
Create 3 post deployment scripts named 'DevPostDeploy.sql', 'QAPostDeploy.sql' and 'ProdPostDeploy.sql'.
Code your actual post deploy file like this:
:r ."\"$(Config)PostDeploy.sql
This is very much like the build event mechanism where you overwrite scripts with appropriate ones except you don't need a build event. But you are dependent upon naming your scripts very specifically.
The scripts referenced using :r are always included. You have a couple of options but I would first verify that if you take the script out it improves the performance to where you want it to get to.
The simplest approach is to just keep it outside of the whole build process and change your deploy process so it becomes a two step thing (deploy DAC then deploy script). The positives of this are you can do things outside of the ssdt process but the negatives are you don't get things like auto disabling of constraints on tables changing in the deployment.
The second way is to not include the script in the deploy when you build but create an AfterBuild msbuild task that adds the script as a post deploy script in the dacpac. The dacpac is a zip file so you can use the .net packaging Api to add a part called postdeploy.sql which will then be included in the deployment process.
Both of these ways mean you lose verification so you might want to keep it in a separate ssdt project which has a "same database" reference to your main project, it will slow down the build when it changes but should be quick the rest of the time.
Here is the way I had to do it.
1) Create a dummy post-deploy script.
2) Create build configurations in your project for each deploy scenario.
3) Use a pre-build event to determine which post deploy configuration to use.
You can either create separate scripts for each configuration or dynamically build the post-deploy script in your pre-build event. Either way you base what you do on the value of $(configuration) which always exists in a build event.
If you use separate static scripts, your build event only needs to copy the appropriate static file, overwriting the dummy post-deploy with whichever script is useful in that deploy scenario.
In my case I had to use dynamic generation because the decision about which scripts to include required knowing the current state of the database being deployed to. So I used the configuration variable to tell me which environment was being deployed to and then used an SQLCMD script with :OUT set to my Post-Deploy script location. Thus my pre-build script would then write the post-deploy script dynamically.
Either way, once build completed and the normal deploy process started the Post-Deploy script contained exactly the :r commands that I wanted.
Here's an example of the SQLCMD script I invoke in pre-build.
:OUT .\Script.DynamicPostDeployment.sql
PRINT ' /*';
PRINT ' DO NOT MANUALLY MODIFY THIS SCRIPT. ';
PRINT ' ';
PRINT ' It is overwritten during build. ';
PRINT ' Content IS based on the Configuration variable (Debug, Dev, Sit, UAT, Release...) ';
PRINT ' ';
PRINT ' Modify Script.PostDeployment.sql to effect changes in executable content. ';
PRINT ' */';
PRINT 'PRINT ''PostDeployment script starting at''+CAST(GETDATE() AS nvarchar)+'' with Configuration = $(Configuration)'';';
PRINT 'GO';
IF '$(Configuration)' IN ('Debug','Dev','Sit')
BEGIN
IF (SELECT IsNeeded FROM rESxStage.StageRebuildNeeded)=1
BEGIN
-- These get a GO statement after every file because most are really HUGE
PRINT 'PRINT ''ETL data was needed and started at''+CAST(GETDATE() AS nvarchar);';
PRINT ' ';
PRINT 'EXEC iESxETL.DeleteAllSchemaData ''pExternalETL'';';
PRINT 'GO';
PRINT ':r .\PopulateExternalData.sql ';
....
I ended up using a mixture of our build tool (Jenkins) and SSDT to accomplish this. This is what I did:
Added a build step to each environment-specific Jenkins job that writes to a text file. I either write a SQLCMD command that includes the import file or else I leave it blank depending on the build parameters the user chooses.
Include the new text file in the Post Deployment script via :r.
That's it! I also use this same approach to choose which pre and post deploy scripts to include in the project based on the application version, except that I grab the version number from the code and write it to the file using a pre-build event in VS instead of in the build tool. (I also added the text file name to .gitignore so it doesn't get committed)

Set Shell Variable from ConsoleApplication in VB.Net

I have a batchfile that run a vb.net Console App.
I need to change some var's e.g. "vbrun" from the current commandline to use it in other batch files, they run after the Console App
as example test.bat
#echo off
set vbrun=true
myVBTool.exe
echo %vbrun%
In windows, the environment variables are stored inside a memory area called the environment block. This memory is not shared, each process has its own copy. When a parent process starts a child process, the parent customize the environemtn block of the child or give to it a copy of its own environment block.
But you need the inverse. You want to change the environment of the parent process (cmd.exe) from a child process (myVBTool.exe).
In short, no, this can not be done.
But, of course, a process can change its own environment. In your case, you need a way for your tool to indicate to its parent process that the variable needs to be changed.
You can set a return value for your application that will be stored as errorlevel and depending on it set your variable
rem ....
myVBtool.exe
if errorlevel 1 set "vbrun=false"
rem ....
Or your application can write to stdout and the batch file can process this output
for /f "delims=" %%a in ('myVBTool.exe') do set "vbrun=%%a"

how to make batch file for simple msbuild commands

I have below a ms build command which want to execute as a batch file so that i can automate it further.
So can any one tell me how to make a batch file (*.bat) which will execute the command bellow with one click of a button ?
Commands:
set msBuildDir=%WINDIR%\Microsoft.NET\Framework\v4.0.30319
call %msBuildDir%\msbuild.exe "D:\tmc600\WIX_Related_files\RIO600_ConnPack_Ver.1.5_WIX\tmc_SetupProject.wixproj" /p:Platform="x86" /p:configuration="Release"
Try running your set command as written and replace "call" with "start" in the call command.
#echo off
Your set command
start "" "%msBuildDir%\msbuild.exe" "D:\tmc600\WIX_Related_files\RIO600_ConnPack_Ver.1.5_WIX\tmc_SetupProject.wixproj" /p:Platform="x86" /p:configuration="Release"
pause
Save as name.bat. You may need to work on the parameters in the Start. There's a good explanation here: http://ss64.com/nt/start.html

Is it possible to prevent MSBuild printing colourized output

I've got my windows shell configured as displaying black text on a white background. This makes it almost impossible to read the default msbuild output due to the very pale colours (especially the yellow warnings).
Therefore I'd like to try one of the following, but I can't work out if it is possible.
I'd like to set a global setting to permanently turn off colourized output in msbuild; or
If (1) isn't possible is it possible to turn this output per-invocation (e.g. with command line arguments).
Does anyone know how to do one of the above?
In MSBuild 4.0 this is possible using the /consoleloggerparameters or /clp switch:
msbuild C:\some_path\your.sln /clp:disableconsolecolor
Alternatively, for previous MSBuild engines, this is possible using PowerShell:
Out-Host will display the default color:
Powershell -Command "msbuild C:\some_path\your.sln | out-host"
Write-Host will let you customize the colors:
Powershell -Command "msbuild C:\some_path\your.sln | write-host -foreground "white""
To completely disable colors use the /clp:disableconsolecolor option when invoking MSBuild.exe (for more information on the /clp option run MSBuild.exe /?).
Update as #KMoraz has commented, and updated his answer to, this only works with MSBuild 4.0 onwards.
If you want to disable color output you can also use the following (which will not work with MSBuild 4.0):
MSBuild.exe arguments > CON 2>&1
This got me curious ;-) so here is one more option that should work with all versions of MSBuild.exe and doesn't rely on CON redirection:
MSBuild.exe arguments 2>&1| findstr /r ".*"
Basically, what happens is that all lines of output are piped through findstr.exe since that uses a pattern to match "everything", all lines are simply output again, but loosing their attributes (color) information. In my tests the 2>&1 (redirect stderr to stdout) was not really necessary, as it looks MSBuild doesn't output any (colored) messages to stderr, but I added it for good measure.

Conditional logic in PostDeployment.sql script using SQLCMD

I am using a SQL 2008 database project (in visual studio) to manage the schema and initial test data for my project. The atabase project uses a post deployment which includes a number of other scripts using SQLCMD's ":r " syntax.
I would like to be able to conditionally include certain files based on a SQLCMD variable. This will allow me to run the project several times with our nightly build to setup various version of the database with different configurations of the data (for a multi-tenant system).
I have tried the following:
IF ('$(ConfigSetting)' = 'Configuration1')
BEGIN
print 'inserting specific configuration'
:r .\Configuration1\Data.sql
END
ELSE
BEGIN
print 'inserting generic data'
:r .\GenericConfiguration\Data.sql
END
But I get a compilation error:
SQL01260: A fatal parser error occurred: Script.PostDeployment.sql
Has anyone seen this error or managed to configure their postdeployment script to be flexible in this way? Or am I going about this in the wrong way completely?
Thanks,
Rob
P.S. I've also tried changing this around so that the path to the file is a variable, similar to this post. But this gives me an error saying that the path is incorrect.
UPDATE
I've now discovered that the if/else syntax above doesn't work for me because some of my linked scripts require a GO statement. Essentially the :r just imports the scripts inline, so this becomes invalid sytax.
If you need a GO statement in the linked scripts (as I do) then there isn't any easy way around this, I ended up creating several post deployment scripts and then changing my project to overwrite the main post depeployment script at build time depending on the build configuration. This is now doing what I need, but it seems like there should be an easier way!
For anyone needing the same thing - I found this post useful
So in my project I have the following post deployment files:
Script.PostDeployment.sql (empty file which will be replaced)
Default.Script.PostDeployment.sql (links to scripts needed for standard data config)
Configuration1.Script.PostDeployment.sql (links to scripts needed for a specific data config)
I then added the following to the end of the project file (right click to unload and then right click edit):
<Target Name="BeforeBuild">
<Message Text="Copy files task running for configuration: $(Configuration)" Importance="high" />
<Copy Condition=" '$(Configuration)' == 'Release' " SourceFiles="Scripts\Post-Deployment\Default.Script.PostDeployment.sql" DestinationFiles="Scripts\Post-Deployment\Script.PostDeployment.sql" OverwriteReadOnlyFiles="true" />
<Copy Condition=" '$(Configuration)' == 'Debug' " SourceFiles="Scripts\Post-Deployment\Default.Script.PostDeployment.sql" DestinationFiles="Scripts\Post-Deployment\Script.PostDeployment.sql" OverwriteReadOnlyFiles="true" />
<Copy Condition=" '$(Configuration)' == 'Configuration1' " SourceFiles="Scripts\Post-Deployment\Configuration1.Script.PostDeployment.sql" DestinationFiles="Scripts\Post-Deployment\Script.PostDeployment.sql" OverwriteReadOnlyFiles="true" />
</Target>
Finally, you will need to setup matching build configurations in the solution.
Also, for anyone trying other work arounds, I also tried the following without any luck:
Creating a post build event to copy the files instead of having to hack the project file XML. i couldn't get this to work because I couldn't form the correct path to the post deployment script file. This connect issue describes the problem
Using variables for the script path to pass to the :r command. But I came across several errors with this approach.
I managed to work around the problem using the noexec method.
So, instead of this:
IF ('$(ConfigSetting)' = 'Configuration1')
BEGIN
print 'inserting specific configuration'
:r .\Configuration1\Data.sql
END
I reversed the conditional and set NOEXEC ON to skip over the imported statement(s) thusly:
IF ('$(ConfigSetting)' <> 'Configuration1')
SET NOEXEC ON
:r .\Configuration1\Data.sql
SET NOEXEC OFF
Make sure you turn it back off if you want to execute any subsequent statements.
Here's how I am handling conditional deployment within the post deployment process to deploy test data for the Debug but not Release configuration.
First, in solution explorer, open the project properties folder, and right-click to add a new SqlCmd.variables file.
Name the file Debug.sqlcmdvars.
Within the file, add your custom variables, and then add a final variable called $(BuildConfiguration), and set the value to Debug.
Repeat the process to create a Release.sqlcmdvars, setting the $(BuildConfiguration) to Release.
Now, configure your configurations:
Open up the project properties page to the Deploy tab.
On the top dropdown, set the configuration to be Debug.
On the bottom dropdown, (Sql command variables), set the file to Properties\Debug.sqlcmdvars.
Repeat for Release as:
On the top dropdown, set the configuration to be Release.
On the bottom dropdown, (Sql command variables), set the file to Properties\Release.sqlcmdvars.
Now, within your Script.PostDeployment.sql file, you can specify conditional logic such as:
IF 'Debug' = '$(BuildConfiguration)'
BEGIN
PRINT '***** Creating Test Data for Debug configuration *****';
:r .\TestData\TestData.sql
END
In solution explorer, right click on the top level solution and open Configuration Manager. You can specify which configuration is active for your build.
You can also specify the configuration on the MSBUILD.EXE command line.
There you go- now your developer builds have test data, but not your release build!
As Rob worked out, GO statements aren't allowed in the linked SQL scripts as this would nest it within the BEGIN/END statements.
However, I have a different solution to his - if possible, remove any GO statements from the referenced scripts, and put a single one after the END statement:
IF '$(DeployTestData)' = 'True'
BEGIN
:r .\TestData\Data.sql
END
GO -- moved from Data.sql
Note that I've also created a new variable in my sqlcmdvars file called $(DeployTestData) which allows me to turn on/off test script deployment.
I found a hack from an MSDN blog which worked fairly well. The trick is to write the commands to a temp script file and then execute that script instead. Basically the equivalent of dynamic SQL for SQLCMD.
-- Helper newline variable
:setvar CRLF "CHAR(13) + CHAR(10)"
GO
-- Redirect output to the TempScript.sql file
:OUT $(TEMP)\TempScript.sql
IF ('$(ConfigSetting)' = 'Configuration1')
BEGIN
PRINT 'print ''inserting specific configuration'';' + $(CRLF)
PRINT ':r .\Configuration1\Data.sql' + $(CRLF)
END
ELSE
BEGIN
PRINT 'print ''inserting generic data'';' + $(CRLF)
PRINT ':r .\GenericConfiguration\Data.sql' + $(CRLF)
END
GO
-- Change output to stdout
:OUT stdout
-- Now execute the generated script
:r $(TEMP)\TempScript.sql
GO
The TempScript.sql file will then contain either:
print 'inserting specific configuration';
:r .\Configuration1\Data.sql
or
print 'inserting generic data';
:r .\GenericConfiguration\Data.sql
depending on the value of $(ConfigSetting) and there will be no problems with GO statements etc. when it is executed.
I was inspired by Rob Bird's solution. However, I am simply using the Build Events to replace the post deployment scripts based on the selected build configuration.
I have one empty "dummy" post deployment script.
I set up a pre-build event to replace this "dummy" file based on the selected build configuration (see attached picture).
I set up a post-build event to place the "dummy" file back after the build has finished (see attached picture). The reason is that I do not want to generate changes in the change control after the build.