How can I include different packages in different Configuration/Platform combinations? - msbuild

I have a library that is designed to work with Sap Business One's SDK. The SDK for V10 is different to the one for V9.3, I also have x86/x64 and SQL/HANA builds, this gives me 8 permutations and therefore 8 packages.
The projects that consume these packages will also have 8 builds. I would like to set up the project file and targets so that a specific package is selected for a specific Configuration & Platform. I am trying to work this out, but it makes absolutely no sense.
Currently I have the following in my project file:
<ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'B1v93SQL|x64' ">
<PackageReference Include="OchALCommon.v93SQLx64" Version="1.0.*" />
</ItemGroup>
<ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'B1v93SQL|x86' ">
<PackageReference Include="OchALCommon.v93SQLx86" Version="1.0.*" />
</ItemGroup>
<ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'B1v93HANA|x64' ">
<PackageReference Include="OchALCommon.v93HANAx64" Version="1.0.*" />
</ItemGroup>
<ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'B1v93HANA|x86' ">
<PackageReference Include="OchALCommon.v93HANAx86" Version="1.0.*" />
</ItemGroup>
<ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'B1v10SQL|x64' ">
<PackageReference Include="OchALCommon.v10SQLx64" Version="1.0.*" />
</ItemGroup>
<ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'B1v10SQL|x86' ">
<PackageReference Include="OchALCommon.v10SQLx86" Version="1.0.*" />
</ItemGroup>
<ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'B1v10HANA|x64' ">
<PackageReference Include="OchALCommon.v10HANAx64" Version="1.0.*" />
</ItemGroup>
<ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'B1v10HANA|x86' ">
<PackageReference Include="OchALCommon.v10HANAx86" Version="1.0.*" />
</ItemGroup>
Visual studio indicates all 8 packages and a build process the dependencies of each one - this seems wrong. I also tried this code within a "Directory.Build.targets" file, which initially seemed to work, but then Visual Studio stopped reponding to changes in the targets file (even after a reboot).
We have always used this kind of referencing in the past with Assembly references,and it seems to work, I have no idea how to make PackageReference function. Does anybody know how best to package this library in my scenario?
In an ideal world, I'd want to somehow store my 64 bit and 32 bit build plus some appropriate targets in a single nuget package so that the consuming project gets the right bitness and the right sub project references. Currently I can't work out how to do this, nor get any other workable scenario going.
Again, does anybody know how to do anything like this?
Thanks.

Thus far I have been able to solve the issue by using separate files (.targets) for each ProjectReference, and then ensuring that only those files containing the reference information are included in the project by referencing specific .targets files.
I have then put .targets files into an umbrella nupkg to select which of the actual payloads I want to use. The entire arrangement looks as follows:
I published 8 payload files as follows:
MyLibrary.v93HANAx64
MyLibrary.v93HANAx86
MyLibrary.v93SQLx64
MyLibrary.v93SQLx86
MyLibrary.v10HANAx64
MyLibrary.v10HANAx86
MyLibrary.v10SQLx64
MyLibrary.v10SQLx86
Each payload file simply contains a standard lib/net40 folder with libraries as specified in the https://learn.microsoft.com/en-us/nuget/create-packages/creating-a-package page.
My consuming project has 4 Configurations:
B1v93HANA
B1v93SQL
B1v10HANA
B1v10SQL
I then have an umbrella project "MyLibrary.Targets" containing content as follows:
##build/net40/MyLibrary.Targets.targets
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
<Import Project="MyLibrary/$(Configuration).targets" />
</Project>
##build/net40/MyLibrary/B1v10HANA.targets
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
<ItemGroup>
<PackageReference Include="MyLibrary.v10HANA$(Platform)" Version="1.0.*" />
</ItemGroup>
</Project>
##build/net40/MyLibrary/B1v10SQL.targets
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
<ItemGroup>
<PackageReference Include="MyLibrary.v10SQL$(Platform)" Version="1.0.*" />
</ItemGroup>
</Project>
##build/net40/MyLibrary/B1v93HANA.targets
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
<ItemGroup>
<PackageReference Include="MyLibrary.v93HANA$(Platform)" Version="1.0.*" />
</ItemGroup>
</Project>
##build/net40/MyLibrary/B1v93SQL.targets
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
<ItemGroup>
<PackageReference Include="MyLibrary.v93SQL$(Platform)" Version="1.0.*" />
</ItemGroup>
</Project>
I also have targets files in this folder, "Debug.targets" and "Release.targets" which use my preferred default library in those cases.
.nupec files for all of the packages are pretty standard as per the above linked Microsoft guide. A default nuspec file can be created with the 'nuget spec' command and can then be edited.
My functional package nuspec files have content in 'package/metadata/dependencies' which identify required packages:
<dependencies>
<group targetFramework=".NETFramework4.0">
<dependency id="CryptLib" version="*" />
<dependency id="SAPBusinessOneSDK.HANA" version="10.0.*" />
</group>
</dependencies>
My Selector package "MyLibrary.targets" does not have dependencies, but does have a files section 'package/files':
<files>
<file src="readme.txt" target="" />
<file src="build\**" target="build" />
</files>
Hopefully this saves somebody some time.

Related

How to create custom project file that works with fast-up-to-date (and avoids other problems)?

I am trying to create a project file that performs few custom steps (specifically, it "wraps" existing Angular CLI project).
Here is my best attempt (myproject.csproj):
<Project ToolsVersion="Current" DefaultTargets="Build">
<PropertyGroup>
<ProjectGuid>{...some-guid...}</ProjectGuid>
<!-- do not include files by default -->
<EnableDefaultItems>false</EnableDefaultItems>
<!-- this removes 'Publish...' menu in VS -->
<OutputType>Library</OutputType>
<!-- output directory name -->
<AngularProject>MyWebFiles</AngularProject>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<PlatformTarget>x64</PlatformTarget>
<OutputPath>bin\Debug\</OutputPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<PlatformTarget>x64</PlatformTarget>
<OutputPath>bin\Release\</OutputPath>
</PropertyGroup>
<ItemGroup>
<AngularFile Include="**" Exclude="node_modules\**" />
</ItemGroup>
<Target Name="Build" Inputs="#(AngularFile)" Outputs="$(OutputPath)$(AngularProject)\index.html">
<Exec Command="ng build --no-progress --output-path $(OutputPath)$(AngularProject)\" Condition="'$(Configuration)'=='Debug'" />
<Exec Command="ng build --no-progress --output-path $(OutputPath)$(AngularProject)\ --prod" Condition="'$(Configuration)'=='Release'" />
</Target>
<Target Name="Clean">
<RemoveDir Directories="$(OutputPath)$(AngularProject)\" />
</Target>
<Target Name="Rebuild" DependsOnTargets="Clean;Build" />
</Project>
Everything works fine, I can add this project to VS2019 solution, compile, etc. But it has problems:
Fast up-to-date check doesn't work. Related logging produces this:
Build started...
1>Project 'myproject' is not up to date. Error (0x8000FFFF).
I've tried specifying fast up-to-date files manually (via UpToDateCheckInput, etc), but it didn't work (presumably because it relies on additional definitions pulled in when you specify Sdk attribute of Project tag).
VS configuration manager has empty 'Platform' combo box. I'd like to be able to have x64 in it:
it is rather obvious that PlatformTarget is getting ignored by VS.
Opening project in VS results in creation of obj\x64\Debug\TempPE\ directory (if current Configuration is Debug). Nothing ever gets generated in it -- would be nice to avoid it being created.
Is it possible to fix these 3 problems? I suspect relates subsystems expect certain values/properties to be generated, I've tried digging in .props/.targets that come with VS in attempt to locate them, but quickly got lost.
Here is how to do it:
<Project Sdk="Microsoft.Build.NoTargets/3.2.14">
<ItemGroup>
<PackageReference Include="Microsoft.Build.NoTargets" Version="3.2.14" />
</ItemGroup>
<PropertyGroup>
<!-- Any target framework you want as long as its compatible with your referenced NuGet packages -->
<TargetFramework>net462</TargetFramework>
<Platforms>x64</Platforms>
<!-- Do not add TargetFramework to OutputPath -->
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<!-- Do not expect pdb files to be generated (this is for fast up-to-date check) -->
<DebugType>None</DebugType>
<!-- Do not include files by default -->
<EnableDefaultItems>false</EnableDefaultItems>
<!-- Output subdir name -->
<AngularProject>MyWebFiles</AngularProject>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<OutputPath>..\..\Bin\Debug\</OutputPath>
<BuildCommand>ng build --no-progress --output-path $(OutputPath)$(AngularProject)\</BuildCommand>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<OutputPath>..\..\Bin\Release\</OutputPath>
<BuildCommand>ng build --no-progress --output-path $(OutputPath)$(AngularProject)\ --prod</BuildCommand>
</PropertyGroup>
<ItemGroup>
<None Include="**" Exclude="node_modules\**;$(BaseIntermediateOutputPath)\**;$(MSBuildProjectFile)" />
<!-- This deals with fast up-to-date checks -->
<UpToDateCheckBuilt Original="package-lock.json" Include="node_modules/.build" />
<UpToDateCheckInput Include="#(None);$(MSBuildProjectFile)" Set="AngularFiles" />
<UpToDateCheckOutput Include="$(OutputPath)$(AngularProject)\index.html" Set="AngularFiles" />
</ItemGroup>
<Target Name="InitModules" Inputs="package-lock.json" Outputs="node_modules/.build">
<Exec Command="npm ci --no-progress --no-color" YieldDuringToolExecution="true" />
<Exec Command="cd . > node_modules/.build" />
</Target>
<Target Name="BuildAngular" BeforeTargets="AfterBuild" Inputs="#(None);$(MSBuildProjectFile)" Outputs="$(OutputPath)$(AngularProject)\index.html" DependsOnTargets="InitModules">
<Exec Command="$(BuildCommand)" YieldDuringToolExecution="true" />
</Target>
<Target Name="CleanAngular" BeforeTargets="AfterClean">
<RemoveDir Directories="$(OutputPath)$(AngularProject)\" />
</Target>
</Project>
Notes:
it will still generate additional local directory (obj), but it can be moved away by overriding IntermediateOutputPath

ASP .Net Core 3.1 build to customer folder with custom name not to netcoreapp3.1

I have developed a class library for existing project solutions. And It builds successfully.
It's a nop commerce plugin and I need to build it into a specific folder with my project name then plugin manager searching my plugin by name and load it into a page to install it..
When I build it then it builds to a folder named 'netcoreapp3.1'. But I need to build it into a custom folder.
This is my .proj file
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<Copyright>Copyright © Company, Ltd</Copyright>
<Company>Company, Ltd</Company>
<Authors>Isanka Thalagala</Authors>
<PackageLicenseUrl></PackageLicenseUrl>
<PackageProjectUrl>http://www.nopcommerce.com/</PackageProjectUrl>
<RepositoryUrl>https://github.com/nopSolutions/nopCommerce</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<OutputPath>..\..\Presentation\Nop.Web\Plugins\Image.Upload.Azure</OutputPath>
<OutDir>$(OutputPath)</OutDir>
<!--Set this parameter to true to get the dlls copied from the NuGet cache to the output of your project.
You need to set this parameter to true if your plugin has a nuget package
to ensure that the dlls copied from the NuGet cache to the output of your project-->
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
</PropertyGroup>
<!-- This target execute after "Build" target -->
<Target Name="NopTarget" AfterTargets="Build">
<!-- Delete unnecessary libraries from plugins path -->
<MSBuild Projects="#(ClearPluginAssemblies)" Properties="PluginPath=$(MSBuildProjectDirectory)\$(OutDir)" Targets="NopClear" />
</Target>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<OutputPath>D:\LabFriend\JohnMorrisCore\API\Presentation\Nop.Web\Plugins\Image.Upload.Azure</OutputPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<OutputPath>D:\LabFriend\JohnMorrisCore\API\Presentation\Nop.Web\Plugins\Image.Upload.Azure</OutputPath>
</PropertyGroup>
<ItemGroup>
<None Remove="plugin.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="ImageResizer" Version="4.2.5" />
<PackageReference Include="Microsoft.Azure.Storage.Blob" Version="11.1.3" />
<PackageReference Include="WindowsAzure.Storage" Version="9.3.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Presentation\Nop.Web.Framework\Nop.Web.Framework.csproj" />
<ProjectReference Include="..\JohnMorris.Plugin.Core\JohnMorris.Plugin.Core.csproj" />
</ItemGroup>
<ItemGroup>
<Content Include="logo.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="plugin.json" />
<Content Update="plugin.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>
This is the setting screen
You can set the following in your .csproj to disable this behavior.
<PropertyGroup>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
</PropertyGroup>
I downgrade it to .net core 2.2. and now it's allowed to build with custom name

Keep documentation xml file while running publish with -c Release

I'm trying to build a project that includes swashbuckle and for troubleshooting I want to include swagger into a release build.
I've narrowed it down to this now:
When I run
dotnet publish -o ./out
the xml file ProjectName.xml is generated in the out folder and when I run
dotnet publish -o ./out -c Release
the xml file is not generated.
The csproj file looks like this:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<OutputType>Exe</OutputType>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<OutputPath>bin\Debug\</OutputPath>
<DocumentationFile>obj\Debug\ProjectName.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<Folder Include="somefolder\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.4" />
...
<PackageReference Include="Swashbuckle.AspNetCore" Version="3.0.0" />
...
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.1.3" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="3.0.0" />
</ItemGroup>
</Project>
and I'm pretty certain I can narrow it down even a bit further given time but I'm fairly new with dotnet and I also guess someone sees the problem already.
I'm looking for either a way to get this working or an explanation for why this is too wrong to be doable.
PS: Running on linux so no Visual Studio. Otherwise this could have been a possible solution for what I know: Swashbuckle + XmlComments work locally, but fail swagger generation on server
Add this to your csproj file instead of setting OutputPath or DocumentationFile:
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
Add in your .csproj :
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<OutputPath>bin\Release\</OutputPath>
<DocumentationFile>obj\Release\ProjectName.xml</DocumentationFile>
</PropertyGroup>
Or remove the condition :
<PropertyGroup>
<OutputPath>bin\$(Configuration)\</OutputPath>
<DocumentationFile>obj\$(Configuration)\ProjectName.xml</DocumentationFile>
</PropertyGroup>

Using FsLex/Yacc in Vs2013

I'm trying to resurrect an old f# parser project I had working in vs 2008 to work with vs 2013. It uses FsLexYacc.
I got it building ok by using a prebuild step as thus:
fslex --unicode "$(ProjectDir)XpathLexer.fsl"
fsyacc --module XpathParser "$(ProjectDir)XpathParser.fsy"
But this is less than ideal, as it always executes whether or not the inputs have changed.
I then tried just using the old MsBuild actions:
<FsYacc Include="XpathParser.fsy">
<FsLex Include="XpathLexer.fsl">
but these appeared to be completely ignored during the build process. Is that right? Have these build tasks been removed somehow?
I then found some stuff documented under vs C++ that I thought might work:
<CustomBuild Include="XpathParser.fsy">
<Message>Calling FsYacc</Message>
<Command>fsyacc --module XpathParser "$(ProjectDir)XpathParser.fsy"</Command>
<Outputs>$(ProjectDir)XpathParser.fs</Outputs>
</CustomBuild>
and
<PropertyGroup>
<CustomBuildBeforeTargets>CoreCompile</CustomBuildBeforeTargets>
</PropertyGroup>
(I inspected the Microsoft.Fsharp.Targets file to come up with the "CoreCompile" target.)
Alas, still no cigar.
Is anyone able to shine a light on whether it is indeed possible to properly integrate fslex/yacc into a vs 2013 solution, and if so, how?
I don't think the those tools are included by default with the F# compiler that is installed with Visual Studio and so the tasks don't exist. I did the following with a Visual Studio 2012 project, but I expect it would be similar in VS 2013. Here were the steps I had to follow:
Install FSharp.Powerpack from nuget. This has the fslex and fsyacc tools as well as build tasks and targets.
Unload the project and edit the .fsproj file.
Add an import statement for the FSharp.Powerpack.target file. This will add the CallFsLex and CallFsYacc build targets. I added this after the import for Microsoft.FSharp.targets:
<Import Project="$(ProjectDir)\..\packages\FSPowerPack.Community.3.0.0.0\Tools\FSharp.PowerPack.targets" />
Add these three properties to main PropertyGroup at the top of the file:
<FsYaccToolPath>..\packages\FSPowerPack.Community.3.0.0.0\Tools</FsYaccToolPath>
<FsLexToolPath>..\packages\FSPowerPack.Community.3.0.0.0\Tools</FsLexToolPath>
<FsLexUnicode>true</FsLexUnicode> This tells the build tasks where to find the necessary tools and sets the unicode option for fslex.
To use the targets we've imported, you need to define the FsLex and FsYacc item groups with the input files to use. You also need to add Compile items for the output .fs files. You end up with something like this in an ItemGroup section:
<Compile Include="Sql.fs" />
<FsYacc Include="SqlParser.fsp">
<Module>SqlParser</Module>
</FsYacc>
<Compile Include="SqlParser.fsi" />
<Compile Include="SqlParser.fs" />
<FsLex Include="SqlLexer.fsl" />
<Compile Include="SqlLexer.fs" />
You might be able to use the FsLex and FsYacc build tasks directly by referencing the FSharp.Powerpack.Build.Tasks.dll, but for me this was easier to get going.
This is what works for me (Windows 7 x64, Visual Studio 2013 Ultimate RTM):
Get and install "PowerPack for FSharp 3.0 + .NET 4.x + VS2012" from CodePlex (https://fsharppowerpack.codeplex.com/downloads/get/625449)
Create the following Registry key: HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\AssemblyFolders\FSharp.PowerPack-1.9.9.9 (for x64 versions of Windows, omit the Wow6432Node for 32bit versions) and set its (Default) value to the installation directory of the F# PowerPack (e.g. "C:\Program Files (x86)\FSharpPowerPack-4.0.0.0\bin"). [This is related to a long standing/regression bug in src/FSharp.PowerPack/CompilerLocationUtils.fs which basically breaks tool discovery.]
Import the PowerPack targets (AFTER importing the F# targets) in your *.fsproj file: <Import Project="$(MSBuildExtensionsPath32)\FSharp\1.0\FSharp.PowerPack.targets" />
Update your ItemGroup node to something like this (use FsYacc accordingly):
<ItemGroup>
<None Include="App.config" />
<FsLex Include="Lexer.fsl" />
<Compile Include="Lexer.fs">
<Visible>False</Visible>
</Compile>
<Compile Include="Program.fs" />
</ItemGroup>
Include a reference to FSharp.PowerPack.dll and build.
You should end up with a *.fsproj file similar to this:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>8c565f99-d6bc-43a9-ace9-eadfe429c0f7</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>FsYaccTest</RootNamespace>
<AssemblyName>FsYaccTest</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFSharpCoreVersion>4.3.1.0</TargetFSharpCoreVersion>
<Name>FsYaccTest</Name>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<!-- Snip -->
</PropertyGroup>
<ItemGroup>
<Reference Include="FSharp.PowerPack">
<HintPath>C:\Program Files (x86)\FSharpPowerPack-4.0.0.0\bin\FSharp.PowerPack.dll</HintPath>
</Reference>
<Reference Include="mscorlib" />
<Reference Include="FSharp.Core, Version=$(TargetFSharpCoreVersion), Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Numerics" />
</ItemGroup>
<PropertyGroup>
<MinimumVisualStudioVersion Condition="'$(MinimumVisualStudioVersion)' == ''">11</MinimumVisualStudioVersion>
</PropertyGroup>
<Choose>
<When Condition="'$(VisualStudioVersion)' == '11.0'">
<PropertyGroup Condition="Exists('$(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets')">
<FSharpTargetsPath>$(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets</FSharpTargetsPath>
</PropertyGroup>
</When>
<Otherwise>
<PropertyGroup Condition="Exists('$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets')">
<FSharpTargetsPath>$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets</FSharpTargetsPath>
</PropertyGroup>
</Otherwise>
</Choose>
<Import Project="$(FSharpTargetsPath)" />
<Import Project="$(MSBuildExtensionsPath32)\FSharp\1.0\FSharp.PowerPack.targets" />
<PropertyGroup>
<FsLexUnicode>true</FsLexUnicode>
</PropertyGroup>
<ItemGroup>
<None Include="App.config" />
<FsLex Include="Lexer.fsl" />
<Compile Include="Lexer.fs">
<Visible>False</Visible>
</Compile>
<Compile Include="Program.fs" />
</ItemGroup>
</Project>
Note: You can probably omit creating the Registry key if you provide a proper FsYaccToolPath as described in mike z's answer.
This looks like it works - at least, in my experience, if you use the separate FsLexYacc nuget package as detailed here, and then put the following in your fsproj file (extracted from the github example):
Next to all the other imports:
<Import Project="..\packages\FsLexYacc.6.0.4\bin\FsLexYacc.targets" />
etc, etc
and then for the source files:
<FsYacc Include="Parser.fsp">
<OtherFlags>--module SqlParser</OtherFlags>
</FsYacc>
<FsLex Include="Lexer.fsl">
<OtherFlags>--unicode</OtherFlags>
</FsLex>
No need to do anything apart from edit the fsproj file, and install the nuget packages.

using dirPath Provider with WebDeploy

I have a wcf application hosted in iis that i am trying to package using webdeploy. Everything works great with the visual studio tools, but i need to also create a logs folder and set permissions on it. For this i created a ProjectName.wpp.target file in my web project.
The file looks like this
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="CreateLogsDirectory" AfterTargets="AddIisSettingAndFileContentsToSourceManifest">
<!-- This must be declared inside of a target because the property
$(_MSDeployDirPath_FullPath) will not be defined at that time. -->
<ItemGroup>
<MsDeploySourceManifest Include="dirPath">
<Path>$(_MSDeployDirPath_FullPath)\logs</Path>
<enableRule>DoNotDeleteRule</enableRule>
</MsDeploySourceManifest>
</ItemGroup>
</Target>
<Target Name="DeclareCustomParameters" AfterTargets="AddIisAndContentDeclareParametersItems">
<!-- This must be declared inside of a target because the property
$(_EscapeRegEx_MSDeployDirPath) will not be defined at that time. -->
<ItemGroup>
<MsDeployDeclareParameters Include="LogsDirectoryPath">
<Kind>ProviderPath</Kind>
<Scope>dirPath</Scope>
<Match>^$(_EscapeRegEx_MSDeployDirPath)\\logs$</Match>
<Value>$(_DestinationContentPath)/log</Value>
<ExcludeFromSetParameter>True</ExcludeFromSetParameter>
</MsDeployDeclareParameters>
</ItemGroup>
</Target>
</Project>
i can see that dirPath provider is added to the sourcemanifest file, but when i deploy the package it tries to create the source file path. Essentially the LogsDirectoryPAth item is not replacing the path. can someone point out what i need to do ? thanks !
Considering your additional directory is inside your web application, it's not really necessary to include another dirPath provider and doing so would only lead to more headaches (additional parameter declarations, etc).
Here are some helpers I use to help with this kind of thing. Your application specific values can be declared in your wpp.targets file:
<!-- Items specific to your application (these should be in your wpp.targets) -->
<ItemGroup>
<SkipDeleteFiles Include="logs" />
<EmptyDirectoriesToDeploy Include="logs" />
<AdditionalAcls Include="logs">
<AclAccess>Write</AclAccess>
</AdditionalAcls>
</ItemGroup>
And the following convention-based definitions can be either put in you wpp.targets or in a common targets file that can be imported into your wpp.targets:
<!--
Empty directories
-->
<PropertyGroup>
<BeforeAddContentPathToSourceManifest>
$(BeforeAddContentPathToSourceManifest);
CreateEmptyDirectories;
</BeforeAddContentPathToSourceManifest>
</PropertyGroup>
<Target Name="CreateEmptyDirectories">
<MakeDir Directories="$(_MSDeployDirPath_FullPath)\%(EmptyDirectoriesToDeploy.Identity)"
Condition="'#(EmptyDirectoriesToDeploy)' != ''" />
</Target>
<!--
Additional ACLs
-->
<ItemDefinitionGroup>
<AdditionalAcls>
<AclAccess>Write</AclAccess>
<ResourceType>Directory</ResourceType>
</AdditionalAcls>
</ItemDefinitionGroup>
<PropertyGroup>
<AfterAddIisSettingAndFileContentsToSourceManifest>
$(AfterAddIisSettingAndFileContentsToSourceManifest);
AddAdditionalAclsToSourceManifest;
</AfterAddIisSettingAndFileContentsToSourceManifest>
<AfterAddIisAndContentDeclareParametersItems>
$(AfterAddIisAndContentDeclareParametersItems);
AddAdditionalAclsDeclareParameterItems
</AfterAddIisAndContentDeclareParametersItems>
</PropertyGroup>
<Target Name="AddAdditionalAclsToSourceManifest">
<ItemGroup Condition="'#(AdditionalAcls)' != ''">
<MsDeploySourceManifest Include="setAcl">
<Path>$(_MSDeployDirPath_FullPath)\%(AdditionalAcls.Identity)</Path>
<setAclResourceType Condition="'%(AdditionalAcls.ResourceType)' != ''">%(AdditionalAcls.ResourceType)</setAclResourceType>
<setAclAccess>%(AdditionalAcls.AclAccess)</setAclAccess>
<AdditionalProviderSettings>setAclResourceType;setAclAccess</AdditionalProviderSettings>
</MsDeploySourceManifest>
</ItemGroup>
</Target>
<Target Name="AddAdditionalAclsDeclareParameterItems">
<ItemGroup Condition="'#(AdditionalAcls)' != ''">
<MsDeployDeclareParameters Include="Add %(AdditionalAcls.AclAccess) permission to %(AdditionalAcls.Identity) Folder">
<Kind>ProviderPath</Kind>
<Scope>setAcl</Scope>
<Match>^$(_EscapeRegEx_MSDeployDirPath)\\#(AdditionalAcls)$</Match>
<Description>Add %(AdditionalAcls.AclAccess) permission to %(AdditionalAcls.Identity) Folder</Description>
<DefaultValue>{$(_MsDeployParameterNameForContentPath)}/#(AdditionalAcls)</DefaultValue>
<DestinationContentPath>$(_DestinationContentPath)/#(AdditionalAcls)</DestinationContentPath>
<Tags>Hidden</Tags>
<ExcludeFromSetParameter>True</ExcludeFromSetParameter>
<Priority>$(VsSetAclPriority)</Priority>
</MsDeployDeclareParameters>
</ItemGroup>
</Target>
<!--
Skip delete files and directories
-->
<PropertyGroup>
<ImportPublishingParameterValuesDependsOn>
$(ImportPublishingParameterValuesDependsOn);
AddSkipDirectives;
</ImportPublishingParameterValuesDependsOn>
</PropertyGroup>
<ItemGroup>
<SkipDeleteItems Include="#(SkipDeleteFiles)"
Condition="'#(SkipDeleteFiles)' != ''">
<Provider>filePath</Provider>
</SkipDeleteItems>
<SkipDeleteItems Include="#(SkipDeleteDirectories)"
Condition="'#(SkipDeleteDirectories)' != ''">
<Provider>dirPath</Provider>
</SkipDeleteItems>
</ItemGroup>
<!-- Uses MSBuild trickery to add an escaped version of the skip path to as
"EscapedPath" metadata -->
<Target Name="AddRegexEscapedPathMetadata" Outputs="%(SkipDeleteItems.EscapedPath)">
<EscapeTextForRegularExpressions Text="%(SkipDeleteItems.Identity)">
<Output TaskParameter="Result"
PropertyName="_Temp_EscapeRegEx_SkipDeleteItemPath" />
</EscapeTextForRegularExpressions>
<ItemGroup>
<SkipDeleteItems Condition="'%(SkipDeleteItems.Identity)' == '%(Identity)'" >
<EscapedPath>$(_Temp_EscapeRegEx_SkipDeleteItemPath)</EscapedPath>
</SkipDeleteItems>
</ItemGroup>
<PropertyGroup>
<!-- Clear value -->
<_Temp_EscapeRegEx_SkipDeleteItemPath></_Temp_EscapeRegEx_SkipDeleteItemPath>
</PropertyGroup>
</Target>
<Target Name="AddSkipDirectives" DependsOnTargets="AddRegexEscapedPathMetadata">
<ItemGroup>
<MsDeploySkipRules Include="%(SkipDeleteItems.Identity)">
<SkipAction>Delete</SkipAction>
<ObjectName>%(SkipDeleteItems.Provider)</ObjectName>
<AbsolutePath>%(SkipDeleteItems.EscapedPath)</AbsolutePath>
</MsDeploySkipRules>
</ItemGroup>
</Target>
NB If you go to the extra effort to separate your packaging process from your deployment process, then technically your SkipDeleteFiles should be in your pubxml rather than your wpp.targets.