What's the correct wrong with my project template - templates

I'm trying to write some project and file templates for MonoDevelop (Android, Console and iOS) and can't seem to get the mpack files to install, I keep getting this error when installing the mpack file the monodevelop addin manager:
The add-in MonoDevelop.MonoAndroid.2.8.1.2 is trying to extend /MonoDevelop/Ide/About, but there isn't any add-in defining this extension point.
And here is my template, if you see anything silly, please let me know.
Thanks,
Kenny.
<Runtime>
<Import file="__PreviewImage.png"/>
<Import file="__TemplateIcon.png"/>
</Runtime>
<Dependencies>
<Addin id="::MonoDevelop.Ide" version="2.8"/>
</Dependencies>
<Extension path="/MonoDevelop/Ide/ProjectTemplates">
<ProjectTemplate id="iFactrCrossPlatformAndroid" file="iFactrCrossPlatformAndroid.xpt.xml" />
<ProjectTemplate id="iFactrCrossPlatformTouch" file="iFactrCrossPlatformTouch.xpt.xml" />
<ProjectTemplate id="iFactrAndroid" file="iFactrAndroid.xpt.xml" />
</Extension>

I'm assuming you have the correct attributes on your root element. If so, the error would seem to indicate that you have an old copy of MonoDevelop.MonoAndroid.dll somewhere, and it's confusing the addin engine for some reason.
Note also that you need to import all the addin's files (except the manifest) to get them included in the mpack.
<Runtime>
<Import file="__PreviewImage.png"/>
<Import file="__TemplateIcon.png"/>
<Import file="iFactrCrossPlatformAndroid.xpt.xml"/>
<Import file="iFactrCrossPlatformTouch.xpt.xml"/>
<Import file="iFactrAndroid.xpt.xml"/>
</Runtime>

Related

TextTemplating target in a .Net Core project

I have recently migrated a test project to .NET Core 2.0. That test project used text templates to generate some repetitive code. The previous project had a build target to generate all T4-templates before build. Therefore, the generated code is also not checked in into the VCS.
I had used this snippet in the project to ensure that templates are built:
<PropertyGroup>
<!-- Default VisualStudioVersion to 15 (VS2017) -->
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">15.0</VisualStudioVersion>
<!-- Determinate VSToolsPath -->
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<!-- Run T4 generation if there are outdated files -->
<TransformOnBuild>True</TransformOnBuild>
<TransformOutOfDateOnly>True</TransformOutOfDateOnly>
</PropertyGroup>
<!-- Import TextTemplating target -->
<Import Project="$(VSToolsPath)\TextTemplating\Microsoft.TextTemplating.targets" />
My first approach was to keep this fragment and copy it to the new .NET Core project file.
Inside Visual Studio, this works because apparently, VSToolsPath is set correctly. However, when I run the .NET Core SDK tools, as for example dotnet test (as I do on the build server), VSToolsPath maps to Program Files\dotnet\sdk\2.0.3 and there, the text templating targets cannot be found.
Because that did not work, I also tried to simply install the Microsoft.VisualStudio.TextTemplating package from Nuget but that has two problems:
it does not officially support .NET Core and installs for .NET 4.6.1 and
Nuget does not seem to install anything, so I cannot adjust the paths in the project file.
To support building T4 templates while building dotnet build you need to use Custom Text Template Host, which already exists for .NET Core (https://github.com/atifaziz/t5). To include it, add to your project in any ItemGroup this element:
<DotNetCliToolReference Include="T5.TextTransform.Tool" Version="1.1.0-*" />.
As Visual Studio already has it's own Text Template Host implementation, your added element should be conditioned only for .NET Core. For example:
<ItemGroup Condition="'$(MSBuildRuntimeType)'=='Core'">
<DotNetCliToolReference Include="T5.TextTransform.Tool" Version="1.1.0-*" />
</ItemGroup>
And at the same time you should condition out of .NET Core your settings for Visual Studio's Text Template Host, like this: Condition="'$(MSBuildRuntimeType)'=='Full'".
You should also add <Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" Condition="'$(MSBuildRuntimeType)'=='Full'" /> before importing Microsoft.TextTemplating.targets to make everything work correctly with .NET Core csproj in Visual Studio.
If you need to be able to clean up all generated code, you should rename your templates from *.tt to *.Generated.tt, all the code will be generated under *.Generated.cs and it will be possible to filter these file out at dotnet clean action.
The complete example of what it will look like in your csproj:
<!-- T4 build support for .NET Core (Begin) -->
<ItemGroup Condition="'$(MSBuildRuntimeType)'=='Core'">
<DotNetCliToolReference Include="T5.TextTransform.Tool" Version="1.1.0-*" />
<TextTemplate Include="**\*.Generated.tt" />
<Generated Include="**\*.Generated.cs" />
</ItemGroup>
<Target Name="TextTemplateTransform" BeforeTargets="BeforeBuild" Condition="'$(MSBuildRuntimeType)'=='Core'">
<ItemGroup>
<Compile Remove="**\*.cs" />
</ItemGroup>
<Exec WorkingDirectory="$(ProjectDir)" Command="dotnet tt %(TextTemplate.Identity)" />
<ItemGroup>
<Compile Include="**\*.cs" />
</ItemGroup>
</Target>
<Target Name="TextTemplateClean" AfterTargets="Clean">
<Delete Files="#(Generated)" />
</Target>
<!-- T4 build support for .NET Core (End) -->
<!-- T4 build support for Visual Studio (Begin) -->
<PropertyGroup Condition="'$(MSBuildRuntimeType)'=='Full'">
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<!-- This is what will cause the templates to be transformed when the project is built (default is false) -->
<TransformOnBuild>true</TransformOnBuild>
<!-- Set to true to force overwriting of read-only output files, e.g. if they're not checked out (default is false) -->
<OverwriteReadOnlyOutputFiles>true</OverwriteReadOnlyOutputFiles>
<!-- Set to false to transform files even if the output appears to be up-to-date (default is true) -->
<TransformOutOfDateOnly>false</TransformOutOfDateOnly>
</PropertyGroup>
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" Condition="'$(MSBuildRuntimeType)'=='Full'" />
<Import Project="$(VSToolsPath)\TextTemplating\Microsoft.TextTemplating.targets" Condition="'$(MSBuildRuntimeType)'=='Full'" />
<!-- T4 build support for Visual Studio (End) -->
If you don't want to rename your template files and you don't need to clean up them, then replace:
<TextTemplate Include="**\*.Generated.tt" />
<Generated Include="**\*.Generated.cs" />
with:
<TextTemplate Include="**\*.tt" />
And delete:
<Target Name="TextTemplateClean" AfterTargets="Clean">
<Delete Files="#(Generated)" />
</Target>
For more information see:
How to set up code generation on dotnet build:
https://notquitepure.info/2018/12/12/T4-Templates-at-Build-Time-With-Dotnet-Core/
How to set up code generation on build for Visual Studio and .NET Core csproj:
https://thomaslevesque.com/2017/11/13/transform-t4-templates-as-part-of-the-build-and-pass-variables-from-the-project/
The full example of the generation of multiple files from a single T4 template:
https://github.com/Konard/T4GenericsExample
Update:
GitHub.com/Mono/T4 is even better.
You are at the mercy of someone writing a port for dotnet core.
This is old: http://www.bricelam.net/2015/03/12/t4-on-aspnet5.html
Or use a different templating tool entirely, though of course I understand if that’s not an option.
Or just use T4Executer. You can set which templates to execute before build, after build or ignore specific templates. Works good with VS2017-19
To expand on Konard's comment of "Update: https://github.com/mono/t4 is even better."
Install Mono/T4 (dotnet-t4) as a tool
If you are running an Azure devops pipeline, you can add it as a step - see the first part of https://stackoverflow.com/a/60667867/1901857.
If you are building in a linux dockerfile, add this before you build (we were using .net 6 on alpine, but it should be fine with other distros and versions):
# you will see a warning if this folder is not on PATH
ENV PATH="${PATH}:/root/.dotnet/tools"
RUN dotnet tool install -g dotnet-t4
If you want to use the tool on your dev machine, instead of VS TextTemplating, run a one-off install from powershell (but every dev in your team will need to do this):
dotnet tool install -g dotnet-t4
Run t4 on build - dev machine and pipeline
dotnet-t4 is setup very similarly to t5 in Konard's answer
Option 1 - dotnet-t4 installed on your dev machine
Add this to your csproj file. No conditional settings required.
<!-- T4 build support for .NET Core (Begin) -->
<ItemGroup>
<TextTemplate Include="**\*.tt" />
</ItemGroup>
<Target Name="TextTemplateTransform" BeforeTargets="BeforeBuild">
<ItemGroup>
<Compile Remove="**\*.cs" />
</ItemGroup>
<Exec WorkingDirectory="$(ProjectDir)" Command="t4 %(TextTemplate.Identity)" />
<ItemGroup>
<Compile Include="**\*.cs" />
</ItemGroup>
</Target>
<!-- T4 build support for .NET Core (End) -->
Option 2 - use VS templating on your dev machine
If you don't want everyone to install the tool locally, you can still add conditional build steps to the project, as per Konard's answer. Note that Visual Studio is now 64-bit, so you can use MSBuildExtensionsPath instead of MSBuildExtensionsPath32:
<!-- T4 build support for .NET Core (Begin) -->
<ItemGroup Condition="'$(MSBuildRuntimeType)'=='Core'">
<TextTemplate Include="**\*.tt" />
</ItemGroup>
<Target Name="TextTemplateTransform" BeforeTargets="BeforeBuild" Condition="'$(MSBuildRuntimeType)'=='Core'">
<ItemGroup>
<Compile Remove="**\*.cs" />
</ItemGroup>
<Exec WorkingDirectory="$(ProjectDir)" Command="t4 %(TextTemplate.Identity)" />
<ItemGroup>
<Compile Include="**\*.cs" />
</ItemGroup>
</Target>
<!-- T4 build support for .NET Core (End) -->
<!-- T4 build support for Visual Studio (Begin) -->
<PropertyGroup Condition="'$(MSBuildRuntimeType)'=='Full'">
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<TransformOnBuild>true</TransformOnBuild>
<!--Other properties can be inserted here-->
<!--Set to true to force overwriting of read-only output files, e.g. if they're not checked out (default is false)-->
<OverwriteReadOnlyOutputFiles>true</OverwriteReadOnlyOutputFiles>
<!--Set to false to transform files even if the output appears to be up-to-date (default is true)-->
<TransformOutOfDateOnly>false</TransformOutOfDateOnly>
</PropertyGroup>
<Import Project="$(VSToolsPath)\TextTemplating\Microsoft.TextTemplating.targets" Condition="'$(MSBuildRuntimeType)'=='Full'" />
<!-- T4 build support for Visual Studio (End) -->

How to use the Web Publishing Pipeline and Web Deploy (MSDEPLOY) to Publish a Console Application?

I would like to use web deploy to publish a Visual Studio "Console" application to a folder on the target system.
I have had some luck, and have been able to produce something similar to what I need, but not quite.
I've added the following to the console .csproj:
added the following projectName.wpp.targets file
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" />
and I've added the following projectName.wpp.targets:
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<PropertyGroup>
<DeployAsIisApp>false</DeployAsIisApp>
<IncludeSetAclProviderOnDestination>false</IncludeSetAclProviderOnDestination>
</PropertyGroup>
<ItemGroup>
<FilesForPackagingFromProject Include="$(IntermediateOutputPath)$(TargetFileName).config">
<DestinationRelativePath>bin\%(RecursiveDir)%(FileName)%(Extension)</DestinationRelativePath>
<FromTarget>projectName.wpp.targets</FromTarget>
</FilesForPackagingFromProject>
</ItemGroup>
</Project>
I then edit the .SetParameters.xml file as follows:
<parameters>
<setParameter name="IIS Web Application Name" value="c:\company\project" />
</parameters>
When I then deploy using the generated .cmd file, I get all the files deployed to C:\company\project\bin.
That's not bad, but I'd like to do better. In particular, I'd like to omit the "bin" folder and put all files in the "C:\company\project" folder, and I'd like to be able to specify the ACLs
Has anybody been able to work around these problems?
Ok, so here's the way how to omit the 'bin' folder.
First of all, I'd like to emphasize that all this msdeploy-related stuff is for web apps deployment, and 'bin' folder seems for me to be almost hardcoded deeply inside. So if you want to get rid of it - you have to do some dirty things. Which I did.
We'll have to change $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets project a little bit, so it's better to change not it, but it's copy.
Steps:
1.Backup $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets(alternatively, you could install MSBuild.Microsoft.VisualStudio.Web.targets package, redirect your csproj file to Microsoft.WebApplication.targets file obtained from package and work with it).
2. In the $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplicaton.targets find the xml node which looks like <CopyPipelineFiles PipelineItems="#(FilesForPackagingFromProject)"(there are several ones of them, take the one from the line ~2570).
3. Comment the node out, replace with the custom one, so eventually it will look like:
<!--
<CopyPipelineFiles PipelineItems="#(FilesForPackagingFromProject)"
SourceDirectory="$(WebPublishPipelineProjectDirectory)"
TargetDirectory="$(WPPAllFilesInSingleFolder)"
SkipMetadataExcludeTrueItems="True"
UpdateItemSpec="True"
DeleteItemsMarkAsExcludeTrue ="True"
Condition="'#(FilesForPackagingFromProject)' != ''">
<Output TaskParameter="ResultPipelineItems" ItemName="_FilesForPackagingFromProjectTempory"/>
</CopyPipelineFiles>-->
<!-- Copying files to package folder in 'custom'(dirty) way -->
<CreateItem Include="$(OutputPath)\**\*.*">
<Output TaskParameter="Include" ItemName="YourFilesToCopy" />
</CreateItem>
<Copy SourceFiles="#(YourFilesToCopy)"
DestinationFiles="#(YourFilesToCopy->'$(WPPAllFilesInSingleFolder)\%(RecursiveDir)%(Filename)%(Extension)')" />
Then
4. Your projectName.wpp.targets don't have to have FilesForPackagingFromProject, so it will look like:
<!-- targets -->
<PropertyGroup>
<DeployAsIisApp>false</DeployAsIisApp>
<IncludeSetAclProviderOnDestination>false</IncludeSetAclProviderOnDestination>
</PropertyGroup>
<ItemGroup>
<!-- intentionally left blank -->
</ItemGroup>
</Project>
That's it. Worked for me(tm), tested. Let me be honest, I don't like this approach, but that was the only way I made it working in the needed way. It's up to you whether you'll use it in your project or not.
My opinion is not to use msdeploy here - it was not for you task.
Better to write msbuild-scripts from scratch or accept the 'bin' folder, and fight against the framework again once next customization is required.

Include AWSSDK refrence to the nant build file

I'm making a build file to build a website written in C#.net using NANT,
but i'm Unable to include the AWSSDK assembly to the file. it give me error
error CS0234: The type or namespace name 'S3' does not exist in the namespace 'Amazon'
the AWSSDK is present in the c:/windows/assembly folder
and this is what i tried
<references>
<include name="/WINDOWS/assembly/AWSSDK.dll" />
</references>
Note :-
<Reference Include="AWSSDK, Version=1.3.19.0, Culture=neutral,
PublicKeyToken=cd2d24cd2bace800, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>/WINDOWS/assembly/</HintPath>
</Reference>
Doesn't work i guess Reference Include=is not the part of the NANT
Make sure you're importing the correct namespace.
<imports>
<import namespace="Amazon.S3" />
</imports>

Build with nant and reference libraries in subdirectories

I'm creating bild-file for a project containing several 3rd-party libraries located inside a lib-folder. So my build-script looks like this:
<csc target="library" ....>
<sources>
<include name="**/*.cs" />
<!-- common assembly-level attributes -->
<include name="../../src/CommonAssemblyInfo.cs" />
<exclude name="Properties/AssemblyInfo.cs" />
</sources>
<references>
<include name="${build.dir}/bin/lib/Should.Fluent.dll" />
</references>
</csc>
The compilation runs fine, however, runtime doesn't work, saying it can't find the library Should.Fluent.dll. How can I make the program find it?
The library has to be present either in GAC or in the same directory that the referencing assembly is in. You can copy it manually to check if this fixes the problem - if yes, then add a <copy> task that makes sure you references are present in your output problem.

Copy all files and folders using msbuild

Just wondering if someone could help me with some msbuild scripts that I am trying to write. What I would like to do is copy all the files and sub folders from a folder to another folder using msbuild.
{ProjectName}
|----->Source
|----->Tools
|----->Viewer
|-----{about 5 sub dirs}
What I need to be able to do is copy all the files and sub folders from the tools folder into the debug folder for the application. This is the code that I have so far.
<ItemGroup>
<Viewer Include="..\$(ApplicationDirectory)\Tools\viewer\**\*.*" />
</ItemGroup>
<Target Name="BeforeBuild">
<Copy SourceFiles="#(Viewer)" DestinationFolder="#(Viewer->'$(OutputPath)\\Tools')" />
</Target>
The build script runs but doesn't copy any of the files or folders.
Thanks
I was searching help on this too. It took me a while, but here is what I did that worked really well.
<Target Name="AfterBuild">
<ItemGroup>
<ANTLR Include="..\Data\antlrcs\**\*.*" />
</ItemGroup>
<Copy SourceFiles="#(ANTLR)" DestinationFolder="$(TargetDir)\%(RecursiveDir)" SkipUnchangedFiles="true" />
</Target>
This recursively copied the contents of the folder named antlrcs to the $(TargetDir).
I think the problem might be in how you're creating your ItemGroup and calling the Copy task. See if this makes sense:
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
<PropertyGroup>
<YourDestinationDirectory>..\SomeDestinationDirectory</YourDestinationDirectory>
<YourSourceDirectory>..\SomeSourceDirectory</YourSourceDirectory>
</PropertyGroup>
<Target Name="BeforeBuild">
<CreateItem Include="$(YourSourceDirectory)\**\*.*">
<Output TaskParameter="Include" ItemName="YourFilesToCopy" />
</CreateItem>
<Copy SourceFiles="#(YourFilesToCopy)"
DestinationFiles="#(YourFilesToCopy->'$(YourDestinationDirectory)\%(RecursiveDir)%(Filename)%(Extension)')" />
</Target>
</Project>
I'm kinda new to MSBuild but I find the EXEC Task handy for situation like these. I came across the same challenge in my project and this worked for me and was much simpler. Someone please let me know if it's not a good practice.
<Target Name="CopyToDeployFolder" DependsOnTargets="CompileWebSite">
<Exec Command="xcopy.exe $(OutputDirectory) $(DeploymentDirectory) /e" WorkingDirectory="C:\Windows\" />
</Target>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
<PropertyGroup>
<YourDestinationDirectory>..\SomeDestinationDirectory</YourDestinationDirectory>
<YourSourceDirectory>..\SomeSourceDirectory</YourSourceDirectory>
</PropertyGroup>
<Target Name="BeforeBuild">
<CreateItem Include="$(YourSourceDirectory)\**\*.*">
<Output TaskParameter="Include" ItemName="YourFilesToCopy" />
</CreateItem>
<Copy SourceFiles="#(YourFilesToCopy)"
DestinationFiles="$(YourFilesToCopy)\%(RecursiveDir)" />
</Target>
</Project>
\**\*.* help to get files from all the folder.
RecursiveDir help to put all the file in the respective folder...
This is the example that worked:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<MySourceFiles Include="c:\MySourceTree\**\*.*"/>
</ItemGroup>
<Target Name="CopyFiles">
<Copy
SourceFiles="#(MySourceFiles)"
DestinationFiles="#(MySourceFiles->'c:\MyDestinationTree\%(RecursiveDir)%(Filename)%(Extension)')"
/>
</Target>
</Project>
source: https://msdn.microsoft.com/en-us/library/3e54c37h.aspx
This is copy task i used in my own project, it was working perfectly for me that copies folder with sub folders to destination successfully:
<ItemGroup >
<MyProjectSource Include="$(OutputRoot)/MySource/**/*.*" />
</ItemGroup>
<Target Name="AfterCopy" AfterTargets="WebPublish">
<Copy SourceFiles="#(MyProjectSource)"
OverwriteReadOnlyFiles="true" DestinationFolder="$(PublishFolder)api/% (RecursiveDir)"/>
In my case i copied a project's publish folder to another destination folder, i think it is similiar with your case.
Did you try to specify concrete destination directory instead of
DestinationFolder="#(Viewer->'$(OutputPath)\\Tools')" ?
I'm not very proficient with advanced MSBuild syntax, but
#(Viewer->'$(OutputPath)\\Tools')
looks weird to me. Script looks good, so the problem might be in values of $(ApplicationDirectory) and $(OutputPath)
Here is a blog post that might be useful:
How To: Recursively Copy Files Using the <Copy> Task
Personally I have made use of CopyFolder which is part of the SDC Tasks Library.
http://sdctasks.codeplex.com/
The best way to recursively copy files from one directory to another using MSBuild is using Copy task with SourceFiles and DestinationFiles as parameters. For example - To copy all files from build directory to back up directory will be
<PropertyGroup>
<BuildDirectory Condition="'$(BuildDirectory)' == ''">Build</BuildDirectory>
<BackupDirectory Condition="'$(BackupDiretory)' == ''">Backup</BackupDirectory>
</PropertyGroup>
<ItemGroup>
<AllFiles Include="$(MSBuildProjectDirectory)/$(BuildDirectory)/**/*.*" />
</ItemGroup>
<Target Name="Backup">
<Exec Command="if not exist $(BackupDirectory) md $(BackupDirectory)" />
<Copy SourceFiles="#(AllFiles)" DestinationFiles="#(AllFiles->
'$(MSBuildProjectDirectory)/$(BackupDirectory)/%(RecursiveDir)/%(Filename)%
(Extension)')" />
</Target>
Now in above Copy command all source directories are traversed and files are copied to destination directory.
If you are working with typical C++ toolchain, another way to go is to add your files into standard CopyFileToFolders list
<ItemGroup>
<CopyFileToFolders Include="materials\**\*">
<DestinationFolders>$(MainOutputDirectory)\Resources\materials\%(RecursiveDir)</DestinationFolders>
</CopyFileToFolders>
</ItemGroup>
Besides being simple, this is a nice way to go because CopyFilesToFolders task will generate appropriate inputs, outputs and even TLog files therefore making sure that copy operations will run only when one of the input files has changed or one of the output files is missing. With TLog, Visual Studio will also properly recognize project as "up to date" or not (it uses a separate U2DCheck mechanism for that).