how to update File Name in Visual Studio Project Template based on user input - visual-studio-2022

I am trying to create a Visual Studio 2022 Project Template. I am using a custom Windows form, where I accept multiple user inputs and have a class that implements IWizard, where I add it to the replacementsDictionary so that file contents are updated to replace the place holder template variables.
public void RunStarted(object automationObject, Dictionary<string, string> replacementsDictionary, WizardRunKind runKind, object[] customParams)
{
replacementsDictionary.Add("$MyTemplateInput1$", UserInputForm.SomeField.Text);
}
However, I am not able to use the user input value to specify a target file name.
Within MyTemplate.vstemplate file, I have tried the below options.
<TemplateContent>
<Project TargetFileName="$projectname$.csproj" File="MyCustom.Templates.csproj" ReplaceParameters="true">
<Folder Name="Properties" TargetFolderName="Properties">
<ProjectItem ReplaceParameters="true" TargetFileName="launchSettings.json">launchSettings.json</ProjectItem>
</Folder>
<Folder Name="manifests" TargetFolderName="manifests">
<ProjectItem ReplaceParameters="true" TargetFileName="$manifestFilePrefix$-trace-off.yaml">app-mytemplate-trace-off.yaml</ProjectItem>
<ProjectItem ReplaceParameters="true" TargetFileName="app-$MyTemplateInput1$-trace-on.yaml">app-mytemplate-trace-on.yaml</ProjectItem>
</Folder>
<ProjectItem ReplaceParameters="true" TargetFileName="$projectname$_Local.csproj">MyCustom.Templates_Local.csproj</ProjectItem>
<ProjectItem ReplaceParameters="true" TargetFileName="Program.cs">Program.cs</ProjectItem>
<ProjectItem ReplaceParameters="true" TargetFileName="Startup.cs">Startup.cs</ProjectItem>
<ProjectItem ReplaceParameters="true" TargetFileName="web.config">web.config</ProjectItem>
<CustomParameters>
<CustomParameter Name="$manifestFilePrefix$" Value="app-$MyTemplateInput1$"/>
</CustomParameters>
</Project>
</TemplateContent>
While $projectname$ gets replaced in target file name properly, neither the use of custom parameter approach works nor the direct use of replacement variable. In both cases, I am getting the file name as manifestFilePrefix$-trace-off.yaml and app-$MyTemplateInput1$-trace-on.yaml.
Any idea on how to get the file name populated dynamically based on user input.
Thanks!

Related

Ajax Minifier to Deploy Package .zip

I'm using AjaxMin to minify my .js and .css files this is configured within my Release.pubxml file and it works fine as when I navigate to the Release/Package/PackageTemp folder the .js and .css have been minified correctly.
However when I navigate to the actual Package .zip file the .js and .css files have not been minified they are just the original files. I assume that the files in my PackageTemp are not replacing the Package deploy files and I'm not sure how to achieve this from within my .pubxml file. My .pubxml file currently looks this this:
<?xml version="1.0" encoding="utf-8" ?>
<!--
This file is used by the publish/package process of your Web project. You can customize the behavior of this process
by editing this MSBuild file. In order to learn more about this please visit http://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<WebPublishMethod>Package</WebPublishMethod>
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<SiteUrlToLaunchAfterPublish />
<LaunchSiteAfterPublish>True</LaunchSiteAfterPublish>
<ExcludeApp_Data>True</ExcludeApp_Data>
<DesktopBuildPackageLocation>C:\Documents\TestDeploy</DesktopBuildPackageLocation>
<PackageAsSingleFile>true</PackageAsSingleFile>
<DeployIisAppPath />
<PublishDatabaseSettings>
<Objects xmlns="" />
</PublishDatabaseSettings>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\Microsoft\MicrosoftAjax\AjaxMin.targets" />
<PropertyGroup>
<OnAfterPackageUsingManifest>
$(OnAfterPackageUsingManifest); Minify;
</OnAfterPackageUsingManifest>
</PropertyGroup>
<Target Name="Minify" AfterTargets="CopyAllFilesToSingleFolderForPackage">
<ItemGroup>
<JS Include="$(_PackageTempDir)\**\*.js" Exclude="$(_PackageTempDir)\**\*.min.js;" />
<CSS Include="$(_PackageTempDir)\**\*.css" Exclude="$(_PackageTempDir)\**\*.min.css" />
</ItemGroup>
<Message Text="**************MINIFYING**************" Importance="high"></Message>
<AjaxMin JsSourceFiles="#(JS)" JsSourceExtensionPattern="\.js$" JsTargetExtension=".js" CssSourceFiles="#(CSS)" CssSourceExtensionPattern="\.css$" CssTargetExtension=".css" />
</Target>
<PropertyGroup>
<ExcludeFoldersFromDeployment>bin</ExcludeFoldersFromDeployment>
</PropertyGroup>
</Project>
Please could someone shed some light on how to replace the files in my deploy .zip file with the PackageTemp Files via my .pubxml file
Thanks
My error i just noticed i was using the OnAfterPackageUsingManifest option. For anyone that also faces this issue try using the OnBeforePackageUsingManifest option instead.

Nuget add custom variables into .nuspec file compatible to XSD scheme

NuGet obtains packages from our own gallery server. A script then creates a CMAKE script with global variables for each package location. I want to add package specific variables like BOOST_INCLUDEDIR or BOOST_LIBRARYPATH with package relative pathes into the .nuspec file of the package. However, all variables shall be usable in CMAKE later.
The .nuspec xsd schema does not allow additional properties. Is there another solution ?
Here is an example of what I need:
<?xml version="1.0"?>
<package>
<metadata>
<id>boost_x86_src</id>
<version>1.55.0</version>
<authors>Fabian Stern</authors>
<owners>Fabian Stern</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Test Package</description>
<dependencies />
<frameworkAssemblies />
<references>
<reference file="signature.sig" />
</references>
<properties>
<add key="BOOST_INCLUDEDIR" value="include/win32" />
<add key="BOOST_LIBRARYPATH" value="libs/win32" />
</properties>
</metadata>
</package>

How do I use my .targets file in Visual Studio with custom build actions?

I am a beginner with MSBuild. So far I have been able to create a custom task called 'MakeTextFile' which creates a text file in C:\ based on the contents property you pass it. This works running from a command line prompt.
I have also included this in my .targets file (under the project tag):
<ItemGroup>
<AvailableItemName Include="CreateTextFileAction" />
</ItemGroup>
When I use the Import tag on my client applications .csproj I can now set items build actions to 'CreateTextFileAction', however the action never triggers (as no text file on C:\ is created)
How do I get all the file paths of items that were marked with my build action 'CreateTextFileAction' and pass them onto my custom task?
For reference, my .targets file:
<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<AvailableItemName Include="CreateTextFileAction" />
</ItemGroup>
<UsingTask AssemblyFile="CustomMSBuildTask.dll" TaskName="CustomMSBuildTask.MakeTextFile" />
<Target Name="MyTarget">
<MakeTextFile Contents="TODO HOW DO I GRAB MARKED FILES?" />
</Target>
</Project>
A csproj file has a defined set of targets. The three main entry points are Build, Rebuild and Clean. These targets each have a set of dependencies. If you write your own targets to be part of the standard csproj build you need to find a suitable injection point within these dependencies.
For ease of use there are two standard targets for you to override called BeforeBuild and AfterBuild. If you define this in the csproj file (after the import of the csharp targets file) and call your custom task in there then it should work (or at least move further along).
<Target Name="BeforeBuild">
<MakeTextFile Contents="TODO HOW DO I GRAB MARKED FILES?" />
</Target>

Where to place dlls for unmanaged libraries?

I am trying to create a Nuget package for a library that depends on ghostscript and therefore references gsdll32.dll - an unmanaged library. I can't just included that a standard dll reference. Where do I put this in the nuget directory structure?
Add a build folder to the package and, if the package for example has the id MyPackage, add a MSBuild target file called MyPackage.targets to this folder. It is important that the .targets file has the same name as the .nuspec file. In the .nuspec file you must have a section like this:
<files>
<file src="lib\*.*" target="lib" />
<file src="build\MyPackage.targets" target="build" />
</files>
This will add an MSBuild element in the project file pointing to the .targets file.
Furthermore, to only register the managed dlls, add a section like this:
<references>
<reference file="MyManaged.dll" />
</references>
The .targets file should look something like this:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="CopyMyPackageFiles" AfterTargets="AfterBuild">
<ItemGroup>
<MyPackageFiles Include="$(MSBuildThisFileDirectory)..\lib\*.*"/>
</ItemGroup>
<Copy SourceFiles="#(MyPackageFiles)" DestinationFolder="$(OutputPath)" >
</Copy>
</Target>
</Project>
Now, all files - including unmanaged files - will be copied to the project output folder (e.g. \bin\debug) after the build.
The above reference can work, but it actually modifies your post build event to push files over, which may not actually fix your issue if you have the situation we did.
The issue we were having was a dependent DLL could not be registered, but had to exist side by side with another DLL which needed to be registered by nuget so it needed to exist in the lib directory but not be registered.
The nuspec reference now allows you to specify which DLLs in the lib directory get explicitly registered in the visual studio project now, you simply need to add into your nuspec file in the metadata area an explicit references list (if this does not exist the default behavior of nuget is to attempt to register everything under lib).
Here is an example nuspec file of what I mean:
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>SomePackageID</id>
<version>1.0.1</version>
<title>Some Package Title</title>
<authors>Some Authors</authors>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Blah blah blah.</description>
<references>
<reference file="ceTe.DynamicPDF.Rasterizer.20.x86.dll" />
</references>
</metadata>
<files>
<file src="\\SomeNetworkLocation\ceTe.DynamicPDF.Rasterizer.20.x86.dll" target="lib\ceTe.DynamicPDF.Rasterizer.20.x86.dll" />
<file src="\\SomeNetworkLocation\DPDFRast.x86.dll" target="lib\DPDFRast.x86.dll" />
</files>
</package>
As you can see, ceTe.DynamicPDF.Rasterizer.20.x86.dll needs to be registered, but DPDFRast.x86.dll simply needs to exist in that directory to support the other DLL and won't be registered but through some dynamic referencing magic will ultimately be copied over into the destination bin directory anyway because visual studio sees that the first DLL is dependent upon the second.
Here is the original nuspec reference.
Response on the Nuget forum: http://nuget.codeplex.com/discussions/352689
pranavkm:
The SQLCE package has a similar issue that we handle via PS
scripts. Checkout out the scripts at
https://bitbucket.org/davidebbo/nugetpackages/src/1cba18b864f7/SqlServerCompact/Tools.
I largely got this to work using Lars Michael's method, but one thing I needed to add comes from James Eby's answer. Visual Studio was trying to register all the dll's in my lib directory, so I added a references element to the metadata in the nuspec file to tell it to only register the managed dll:
<references>
<reference file="FANNCSharp.dll" />
</references>
Also in
<MyPackageFiles Include="$(MSBuildProjectDirectory)\..\Packages\MyPackage\lib\*.*"/>
I first tried the id of my package FANNCSharp-x64, but it needed the full package name: FANNCSharp-x64.0.1.4.
One problem I had was that the packages path wasn't always in the same place relative to the project file. The following worked for me:
Within the NuGet package, place your unmanaged DLLs in the lib\native folder.
Add the following script to the tools folder:
install.ps1
#This script creates or updates a PackagesPath property in the project file
param($installPath, $toolsPath, $package, $project)
$project.Save()
#Load the csproj file into an xml object
[xml] $xml = Get-Content -path $project.FullName
#grab the namespace from the project element
$nsmgr = New-Object System.Xml.XmlNamespaceManager -ArgumentList $xml.NameTable
$nsmgr.AddNamespace('a',$xml.Project.GetAttribute("xmlns"))
#find or create the property
$property = $xml.Project.SelectSingleNode("//a:PropertyGroup//a:PackagesPath", $nsmgr)
if (!$property)
{
$property = $xml.CreateElement("PackagesPath", $xml.Project.GetAttribute("xmlns"))
$propertyGroup = $xml.CreateElement("PropertyGroup", $xml.Project.GetAttribute("xmlns"))
$propertyGroup.AppendChild($property)
$xml.Project.InsertBefore($propertyGroup, $xml.Project.ItemGroup[0])
}
#find the relative path to the packages folder
$absolutePackagesPath = (get-item $installPath).parent.FullName
push-location (split-path $project.FullName)
$relativePackagesPath = Resolve-Path -Relative $absolutePackagesPath
pop-location
#set the property value
$property.InnerText = $relativePackagesPath
#save the changes.
$xml.Save($project.FullName)
Add a targets file to the build folder. (Change "MyPackage" to the name of your package). Using a unique name for the target, like "CopyMyPackage", avoids conflicts with other packages trying to define the "AfterBuild" target. This targets file makes use of the $(PackagesPath) property defined by the above script.
MyPackage.targets
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="CopyMyPackage" AfterTargets="AfterBuild">
<ItemGroup>
<MyPackageSourceFiles Include="$(PackagesPath)\MyPackage.*\lib\native\*.*"/>
</ItemGroup>
<Copy SourceFiles="#(MyPackageSourceFiles)" DestinationFolder="$(OutputPath)" >
</Copy>
</Target>
</Project>
Finally, add a "MyPackageReadMe.txt" to the Content folder. This will enable the package to install.
See also: http://alski.net/post/2013/05/23/Using-NuGet-25-to-deliver-unmanaged-dlls.aspx
For .NET Core this is pretty straightforward if you know what runtime platform your native code targets. You might notice a folder called "runtimes" in the .NET Core build folder under the bin tree when you build. It looks something like this:
These folders are designed to hold any platform specific stuff, including unmanaged/native DLLs.
In your NuGet package add a the following under the "Files" section:
<file src="[source path for file in package]" target="runtimes\[platform]\native\[file name]" />
When executing the application, the runtime environment will look for unmanaged dlls in the corresponding platform directory.
If you want to target multiple platforms, just add another file entry for each platform.

XPath in MSBuild (SDC) and WIX

Fresh Asking of this Question->
I have a WIX file that I need to modify using MSBuild. It starts like this:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:iis="http://schemas.microsoft.com/wix/IIsExtension">
<?--... Various Removed Params ...-->
<Product Id='$(var.ProductCode)'
UpgradeCode='$(var.UpgradeCode)'
Name='$(var.AppName)' Language="1033" Version='$(var.ProductVersion)'
Manufacturer='$(var.Manufacturer)'>
<Package Id='$(var.PackageCode)' InstallerVersion="200"
Compressed="yes" />
<?--... More of the WIX XML file ...-->
<iis:WebApplication Id='STWebApp' Name='MyWebSite' Isolation='medium' />
<?--... Rest of the WIX XML file ...-->
My problem is the SDC tasks can't seem to reference any of the xml nodes that are WIX related. For example:
<XmlFile.SetAttribute Path="$(MSBuildProjectDirectory)\TestProduct.wxs"
XPath="//iis:WebApplication" Namespaces="#(Namespaces)"
Name="Name" Value="$(VersionTag)"/>
works just fine because it does not use any Wix nodes (just an iis one), but if I use the full XPath path to it (/Wix/Product/iis:WebApplication) the task returns:
Could not find resource string No matches found for XPath expression
This is not a problem till I want to reference a Directory node (/Wix/Product/Directory/Directory/Directory/Directory[#Id='STWebSiteDir'])
I have tried using the full XPath and the shorter //Directory[#Id='STWebSiteDir']. I have tried single quotes and double quotes, I have tried adding the WIX namespace to the call (with no prefix).
<ItemGroup>
<Namespaces Include="http://schemas.microsoft.com/wix/IIsExtension">
<Prefix>iis</Prefix>
<Uri>http://schemas.microsoft.com/wix/IIsExtension</Uri>
</Namespaces>
<Namespaces Include="http://schemas.microsoft.com/wix/2006/wi">
<Prefix></Prefix>
<Uri>http://schemas.microsoft.com/wix/2006/wi</Uri>
</Namespaces>
</ItemGroup>
I have even tried to just get a reference to /Wix/Product and even that fails:
<XmlFile.SetAttribute Path="$(MSBuildProjectDirectory)\TestProduct.wxs"
XPath="/Wix/Product" Namespaces="#(Namespaces)"
Name="Name" Value="MODIFIED"/>
I am clearly missing something. Anyone with a hint on where to go to get this to work?
Vaccano
Can you just define the variables on the command line to the preprocessor?
candle -dVariableName=ValueForVariable
That might be much easier.
Have you included the Wix default namespace in #(Namespaces)?
<ItemGroup>
<Namespaces Include="http://schemas.microsoft.com/wix/IIsExtension">
<Prefix>iis</Prefix>
<Uri>http://schemas.microsoft.com/wix/IIsExtension</Uri>
</Namespaces>
<Namespaces Include="http://schemas.microsoft.com/wix/2006/wi">
<Prefix>wis</Prefix>
<Uri>http://schemas.microsoft.com/wix/2006/wi</Uri>
</Namespaces>
</ItemGroup>
you should add a prefix for wi namespace too,after that it can ok,i have test it.
OK, so here is the answer:
The namespace prefix needed to be missing for the wix part, not just left empty
<ItemGroup>
<Namespaces Include="http://schemas.microsoft.com/wix/IIsExtension">
<Prefix>iis</Prefix>
<Uri>http://schemas.microsoft.com/wix/IIsExtension</Uri>
</Namespaces>
<Namespaces Include="http://schemas.microsoft.com/wix/2006/wi">
<Uri>http://schemas.microsoft.com/wix/2006/wi</Uri>
</Namespaces>
</ItemGroup>
And then you need to add a prefix value to the wix namespace in the file. I used tst.
Vaccano