Modifying WMAppManifest.xml based on Build Configuration - visual-studio

I would like to release to distinct flavours of my app and would like to indicate this in the application name displayed on the phone. As far as I know for Silverlight Phone Apps the name is solely determined by WMAppManifest.xml. Therefore I would like to modify the application title at build time based on my Build Configuration. Any suggestions?

You can do this with a bit of T4 templating and code generation (see http://msdn.microsoft.com/en-us/library/bb126445.aspx if you don't know about this.)
The following steps will allow you to use a different application title if you are using the debug or release configuration.
Take a copy of WMAppManifest.xml and rename it to WMAppManifest-base.tt
Change the content of WMAppManifest-base.tt to be
<## template language="C#" #><## output extension=".xml" #><?xml version="1.0"?>
<Deployment xmlns="http://schemas.microsoft.com/windowsphone/2009/deployment" AppPlatformVersion="7.0">
<App xmlns="" ProductID="{4c5315b6-4030-46c5-b5ea-17284d6af0c6}" Title="<#= this.ConfiguredAppTitle #>" RuntimeType="Silverlight" Version="1.0.0.0" Genre="apps.normal" Author="WindowsPhoneApplication8 author" Description="Sample description" Publisher="WindowsPhoneApplication8">
<IconPath IsRelative="true" IsResource="false">ApplicationIcon.png</IconPath>
<Capabilities>
<Capability Name="ID_CAP_IDENTITY_DEVICE"/>
<Capability Name="ID_CAP_NETWORKING"/>
</Capabilities>
<Tasks>
<DefaultTask Name ="_default" NavigationPage="MainPage.xaml"/>
</Tasks>
<Tokens>
<PrimaryToken TokenID="WindowsPhoneApplication8Token" TaskName="_default">
<TemplateType5>
<BackgroundImageURI IsRelative="true" IsResource="false">Background.png</BackgroundImageURI>
<Count>0</Count>
<Title><#= this.ConfiguredAppTitle #></Title>
</TemplateType5>
</PrimaryToken>
</Tokens>
</App>
</Deployment>
<#+
string ConfiguredAppTitle = "MyPhoneApp";
#>
(Adjust capabilities, etc. as appropriate.)
In the same folder as WMAppManifest-base.tt create a file called Debug.WMAppManifest.tt with the following contents:
<#
ConfiguredAppTitle = "MyDebugApp";
#><## include file="WMAppManifest-base.tt" #>
Now create a file called Release.WMAppManifest.tt with the following contents:
<#
ConfiguredAppTitle = "MyReleaseApp";
#><## include file="WMAppManifest-base.tt" #>
Create a file called copyifnewer.bat in the root of the project. Give it the following contents:
echo Comparing: %1 with %2
if not exist %1 goto File1NotFound
if not exist %2 goto File2NotFound
fc %1 %2
if %ERRORLEVEL%==0 GOTO NoCopy
echo Files are not the same. Copying %1 over %2
copy %1 %2 /y & goto END
:NoCopy
echo Files are the same. Did nothing
goto END
:File1NotFound
echo %1 not found.
goto END
:File2NotFound
copy %1 %2 /y
goto END
:END
In the project properties add this PRE-build command:
"$(ProjectDir)\copyifnewer.bat" "$(ProjectDir)properties\$(ConfigurationName).WMAppManifest.xml" "$(ProjectDir)properties\WMAppManifest.xml"
Now you can adjust the values in the debug & release files to alter the titles as you wish.
If you have other configurations just create appropriately named files (with the same contents as the debug.*.tt) and they'll be picked up automatically.
Note that when testing, if you install the app with one name (in the emulator or phone) you'll have to uninstall it to see a name change reflected in the application list.
Note to self: must blog about this. (It's really powerful but hard to work out how to do the first time.)

You can use the pre-build step (Project Properties -> Build Events -> Pre-Build event command line) in the project properties with conditional command-lines to achieve this.
Having files for each version and then copying over the default to replace the data there. You can also set up your icons to use this same system! :)
if $(ConfigurationName) == Phone_Free_Debug (
copy /Y $(ProjectDir)Properties\WMAppManifest_Free.xml $(ProjectDir)Properties\WMAppManifest.xml
copy /Y $(ProjectDir)173x173icon_Free.png $(ProjectDir)173x173icon.png
copy /Y $(ProjectDir)200x200icon_Free.png $(ProjectDir)200x200icon.png
)
if $(ConfigurationName) == Phone_Free_Release (
copy /Y $(ProjectDir)Properties\WMAppManifest_Free.xml $(ProjectDir)Properties\WMAppManifest.xml
copy /Y $(ProjectDir)173x173icon_Free.png $(ProjectDir)173x173icon.png
copy /Y $(ProjectDir)200x200icon_Free.png $(ProjectDir)200x200icon.png
)
if $(ConfigurationName) == Phone_Debug (
copy /Y $(ProjectDir)Properties\WMAppManifest_Paid.xml $(ProjectDir)Properties\WMAppManifest.xml
copy /Y $(ProjectDir)173x173icon_Paid.png $(ProjectDir)173x173icon.png
copy /Y $(ProjectDir)200x200icon_Paid.png $(ProjectDir)200x200icon.png
)
if $(ConfigurationName) == Phone_Release (
copy /Y $(ProjectDir)Properties\WMAppManifest_Paid.xml $(ProjectDir)Properties\WMAppManifest.xml
copy /Y $(ProjectDir)173x173icon_Paid.png $(ProjectDir)173x173icon.png
copy /Y $(ProjectDir)200x200icon_Paid.png $(ProjectDir)200x200icon.png
)

I know this is an old thread now, but came across it as I was looking to do something similar and the accepted answer was spot on for me. I just wanted to add in a suggestion for a modification to the batch file part of it, in case it helps anyone else. I would have commented on the accepted answer but lack the reputation to so far, so hope no-one minds me adding it as a separate answer..!
If you're version-controlling your solution (e.g. in TFS) your WMAppManifest might be write-protected, unless you specifically remembered to check it out prior to build. If it is, the batch file won't be able to overwrite it, but you won't get any notification of it and the build will proceed, meaning you may fail to notice it didn't update per your build configuration. To address this, add the following to the end of the batch file (after :END):
if %errorlevel% neq 0 exit /b %errorlevel%
If the batch file fails, the build will stop, alerting you to the issue.
Also, don't create the batch file in Visual Studio! It'll save in UTF-8 encoding (I think) and you may get errors when Windows tries to shell it. Create it in notepad to ensure it saves in ASCII. If you save it with the wrong encoding and it won't run, the above amend will catch that as well.

Related

Change Directory of Batch File to the batch file's location

Weekly, I have to update our computers with publications for our aircraft. I have streamlined the process by creating a batch file that deletes the old publications automatically and opens the folder where they are stored so I don't have to dig for it. The location where the publications are stored each week is the same. My batch file looks like below...
del /s /f "C:\Users\Public\Desktop\PubFolder\Pub.pdf"
So basically all I have to do is copy the PubFolder from my USB drive to the Public Desktop after I have ran the batch file to delete all of the publications.
If all of our computers were exactly the same, this would be an easy fix because I could write in the batch file
xcopy /y D:\PubFolder" "C:\Users\Public\Desktop\PubFolder"
and the script would do all of the work for me. My issue is, a lot of the computers have different software installed on them which require partitions in the HDD. Some of them have 3, some 2, some 1.
Basically what I'm needing is when I run the batch file from the USB, it uses the location of the batch file as the directory so I can copy from there.
A cmd script (.bat file) can reference its own directory with %~dp0
See the details using FOR /?
While you are at it, some additional error checking might make some situations easier to recover from.
SETLOCAL
SET "EXITCODE=0"
SET "USERDIR=%PUBLIC%\Desktop\PubFolder"
IF NOT EXIST "%USERDIR%" (
ECHO ERROR: The user publication directory "%USERDIR%" does not exist.
SET EXITCODE=4
GOTO TheEnd
)
IF EXIST "%USERDIR%\Pub.pdf" (DEL "%USERDIR%\Pub.pdf")
xcopy /y %~dp0PubFolder" "%USERDIR%"
SET "EXITCODE=%ERRORLEVEL%"
IF %EXITCODE% NEQ 0 (
ECHO ERROR: Failed to copy from "%~dp0" to "%USERDIR%"
SET EXITCODE=5
GOTO TheEnd
)
:TheEnd
EXIT /B %EXITCODE%
#setlocal
REM If you have pre-Vista machines, uncomment the folowing line:
REM #if not defined PUBLIC set PUBLIC=%SYSTEMDRIVE%\Users\Public%
#xcopy /y %~d0\PubFolder %PUBLIC%\Desktop\PubFolder

spaces in dotfuscator in/out paths

I have a very annoying problem regarding dotfuscator and paths with spaces. I have a .bat file that is called via post-build event with the following syntax:
call "$(SolutionDir)..\Build\dotf.bat" $(SolutionDir) $(ConfigurationName) $(TargetPath) $(TargetDir).
The .bat file looks like this:
SET DOTFPATH="%1..\Tools\DotfuscatorProEdition4.13.0\dotfuscator.exe"
SET DEBUG=%2
SET TARGETFILE=%3
SET OUTPUT=%4
if "%DEBUG%" == "Release" (
%DOTFPATH% /q /in:"%TARGETFILE%" /out:"%OUTPUT%"
) ELSE (
echo DOTFUSCATOR: Skipped due to debugmode
)
The .bat file recieves the arguments and generates a command like this:
"C:\some\path\to\dotfuscator.exe" /q /in:C:\a\path\toproject\Mydll.dll /out:C:\a\path\toproject\
the .bat file works great locally. But on the buildserver the solution and project paths have whitespaces, like this:
"C:\some\path\to\dotfuscator.exe" /q /in:C:\a\path\to project\Mydll.dll /out:C:\a\path\to project\
which ofcourse messes up the arguments. So, i tried to enclose the paths with quotationmarks like this:
"C:\some\path\to\dotfuscator.exe" /q /in:"C:\a\path\to project\Mydll.dll" /out:"C:\a\path\to project\"
This fixes the .bat file and the argument parsing, but dotfuscator itself is now failing miserably!
Dotfuscator now complains about "Illegal characters in path". How do i fix this?
If the path contains spaces then the parameters passed to the bat file should already be protected with quotes. So you're probably creating /in:""c:\some path"" which won't work. First try removing the quotes on your dotfuscator line in the bat file.
Alternatively, try changing to the directory and back instead of trying to work out dotfuscator. You'd need to change the 3rd parameter also from targetpath to targetfile
SET DOTFPATH="%1..\Tools\DotfuscatorProEdition4.13.0\dotfuscator.exe"
SET DEBUG=%2
SET TARGETFILE=%3
SET OUTPUT=%4
if "%DEBUG%" == "Release" (
pushd %OUTPUT%
%DOTFPATH% /q /in:%TARGETFILE% /out:.
popd
) ELSE (
echo DOTFUSCATOR: Skipped due to debugmode
)

Why would a post-build step (xcopy) occasionally exit with code 2 in a TeamCity build?

A few projects in my client's solution have a post-build event: xcopy the build output to a specific folder. This works fine when building locally. However, in TeamCity, I occasionally get
xcopy [...] exited with code 2
If I use regular copy, it exits with code 1. I expect this has something to do with file locks, although the specific files being copied are not the same, so perhaps just locking on the shared destination directory. I use /y to not prompt on overwriting files.
Why this fails in TeamCity but not locally?
Even if you provide the /Y switch with xcopy, you'll still get an error when xcopy doesn't know if the thing you are copying is a file or a directory. This error will appear as "exited with code 2". When you run the same xcopy at a command prompt, you'll see that xcopy is asking for a response of file or directory.
To resolve this issue with an automated build, you can echo in a pre-defined response with a pipe.
To say the thing you are copying is a file, echo in F:
echo F|xcopy /y ...
To say the thing you are copying is a directory, echo in D:
echo D|xcopy /y ...
Sometimes the above can be resolved by simply using a copy command instead of xcopy:
copy /y ...
However, if there are non-existent directories leading up to the final file destination, then an "exited with code 1" will occur.
Remember: use the /C switch and xcopy with caution.
I fixed the error code 2 by adding a \ at the end of my path, without it, xcopy will think that it is a file instead of a folder.
If you are using xcopy in a post build event use the /Y switch in addition to the /C.
/C Continues copying even if errors occur.
/Y Suppresses prompting to confirm you want to overwrite an existing file.
My fix for this issue was to go into the target bin folder, and ensure that the proper subfolder exists there. Once that subfolder was manually created, the build process completed successfully.
copy fixed it for me. xcopy with /c /y did not work. I was getting an exit 4 so I went with xcopy, but turned out I needed quotes around ($TargetPath).
My script:
if $(ConfigurationName) == Debug copy "$(TargetPath)" "$(SolutionDir)\Folder\bin\Debug\$(TargetFileName)"
Probably you using TeamCity with git. If yes, check that folders you want to copy are exists in git repository. Usually git aviod adding empty project folders to repository, so xcopy fails to find it and generates a error.
You can add some empty text file to empty folder, commit and see folder appears in repository.

Is there a way of checking file availability in a DOS script?

Background: I have a post-build process that copies a file to another location. It looks like this:
copy $(TargetPath) "%programfiles%\mypath"
This step can fail if the another process is using the file. The step is not critical, so if possible I would like to ignore the failure. To do this I need the script to check to determine if the file is being used by another process.
Question: Is there a way of testing a file in a DOS script to determine if it is being used by another process?
You can see if the file exists, then rename a .dll/.exe even if it is being executed. Might want to do .pdb files, too.
IF EXIST $(TargetName).deleted del $(TargetName).deleted
IF EXIST $(TargetName).pdb.deleted del $(TargetName).pdb.deleted
IF EXIST "%programfiles%\mypath\$(TargetName)$(TargetExt)" REN "%programfiles%\mypath\$(TargetName)$(TargetExt)" $(TargetName).deleted
IF EXIST "%programfiles%\mypath\$(TargetName)$(TargetExt)" REN "%programfiles%\mypath\$(TargetName)$.pdb" $(TargetName).pdb.deleted
copy $(TargetPath) "%programfiles%\mypath"
Ok, so I needed to check the errorlevel after performing the copy, so that I could handle the exit properly. The solution is below:
copy $(TargetPath) "%programfiles%\mypath"
if errorlevel 1 goto BuildProcessFailed
goto BuildProcessOK
:BuildProcessFailed
echo BUILDPROCESS FAILED FOR PROJECT $(ProjectName)
goto ExitBuildProcess
:BuildProcessOK
echo BUILDPROCESS OK FOR PROJECT $(ProjectName)
:ExitBuildProcess

Visual Studio incremental build: XML documentation file is created too late

I have a DLL project for Visual Studio 2005 that has "XML documetation file" turned on.
Whenever I do an incremental build, during post-build event execution there is no XML documentation file in the output directory.
If I pause the build during post-build event (using sleep utility from GnuWin32 CoreUtils), I can see the file in the output directory with a name like vs5BB5.tmp. But this file is not renamed to MyLib.xml until post-build event (and "AfterBuild" target, as I have some customizations there) are finished.
For a clean build in Studio and for MSBuild started from a command line everything works as expected - XML documentation file is created before post-build events.
Why this happens, and how do I fix incremental builds?
Was just having the same issue. This is a known problem with Visual Studio and incremental builds. See this post on microsoft connect.
I solved it with a conditional xcopy like the one below:
if exist "$(TargetDir)$(TargetName).xml" xcopy $(TargetDir)$(TargetName).xml $(ProjectDir)......\bin\ /C /I /R /Y
SF
Just having this problem myself....
what I found is that the xml file is named a .tmp file, so you can copy this tmp file to where you want, its just a bit of a "messy" work around.
I'm also quite tempted to write myself a command line tool thats called something like :-
WaitForThenCopy <source path> <target path> <milliseconds to wait>
only problem is it would have to be non blocking and you wouldn't know if it worked or not.
I'm using a simple batch file to do the copying instead of the default copy command that detects the tmp file and copies/renames this instead.
REM There is a bug in VS where the xml documentation is written to a tmp file
REM during incremental builds, preventing access during post-build events.
REM See http://connect.microsoft.com/VisualStudio/feedback/details/470485/strange-file-not-found-error-xml-documentation-file-renamed-during-incremental-build
REM As a work around for following script tries to catch this situation and copys/remanes
REM this tmp-file instead.
REM .SYNOPSIS
REM CopyXmlDocumentation "X:\path\to\source.xml" "Y:\target\dir"
if exist "%~1%" (
REM if the file exists, copy it as-is
copy /Y "%~1" "%~2"
) else (
REM else we try to copy the .tmp file and rename it to the desired target name
REM we assume that the tmp file is named "vsXXXX.tmp" where XXXX is an arbitrary string
copy /Y "%~d1\%~p1\vs*.tmp" "%~2\%~n1%~x1"
)

Resources