Using a ** wildcard in an F# project causes Visual Studio to refuse to load the project - visual-studio

I've got an F# project with several additional files stored in many nested folders, creating a complex folder structure. The compiler doesn't need to know about them: all I need to do is to have them copied into the output directory at the end of the build process.
I tried adding my files using the ** wildcard:
<ItemGroup>
    <FilesToCopyToOutput Include="additionalData\**\*.*"/>
</ItemGroup>
It works fine when running MSBuild manually from PowerShell. However, when I attempt to open my project in Visual Studio (v. 2015), I get the following error message:
Cannot open F# project . This project uses
wildcards in the item specification. Wildcards in F# projects are not
currently supported.
Similarly, a C# project with the wildcards works just fine. I guess this has something to do with the fact that the order of files in an F# project matters and using the wildcards causes a problem if a user wants to reorder the files.
However, I'm wondering if there's anything that can be done in my particular case: I don't care if the files are available in VS or not: I just want them to be copied to the output folder.

Using a ** wildcard in an F# project causes Visual Studio to refuse to load the project
Indeed, Visual Studio 2017 is a good choose. I have test the ** wildcard in the Visual Studio 2015 and 2017, it works fine in the Visual Studio 2017 but not in the Visual Studio 2015, got the same error as you.
Since you could not change the tech stack to use Visual Studio 2017, I would like provide a workaround for this issue, you can check if it works for you.
Since you do not care if the files are available in VS or not, you just want them to be copied to the output folder, you can use a power shell script to copy the additionalData folder to the output folder, like:
Copy-Item -Path "ThePathForAdditionalData\additionalData" -Destination "ThePathForProject\bin\Debug" -recurse -Force
Then execute this power shell script with MSBuild task after build:
<Target Name="CopyMyFiles" AfterTargets="Build">
<Message Text="Copying files..."/>
<Exec Command="C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -NonInteractive -executionpolicy Unrestricted -command "& { .\YourCopyFiles.ps1 } "" ></Exec>
</Target>
Hope this helps.

I assume FilesToCopyToOutput is the name you gave to an item collection, and the question is how to stop the old F# compiler from thinking it should handle it?
I suspect what you want to do though is to treat an entire folder as content items and copy them to the output folder :
<Content Include="additionalData\**\*.*">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
Copy Task
If you want to copy items with an MSBuild task you can use Copy. You should be able to write :
<ItemGroup>
<MyAdditionalData Include="additionalData\**\*.*" />
</ItemGroup>
<Target Name="CopyFiles">
<Copy
SourceFiles="#(MyAdditionalData)"
DestinationFolder="$(OutputPath)"
/>
</Target>
The Include attribute allows selecting multiple files that can be used as arguments to a task. Exclude is used to exclude files. You can specify a Condition attribute too, eg to run the task only for Release or Debug configuration. $(OutputPath) is one of the MSBuild properties that can be used in paths.
How to: Exclude Files from the Build shows how you can combine all those attributes to process specific items in a directory, eg:
<JPGFile
Include="Images\**\*.jpg"
Exclude = "Images\**\Version2\*.jpg"/>
This will include all files in the Images directory except those in Version2
Using Post-build events
Most people though didn't use MSBuild until the latest simplified format.
The most common way to copy files after build is to use a post-build event from the project's property pages, eg :
xcopy $(ProjectDir)additionalData\*.* $(TargetDir) /s /e
$(ProjectDir) and $(TargetDir) are replacement macros that point to the projects' folder and output folder. The list of all macros is available in Pre-build Event/Post-build Event Command Line Dialog Box.

Related

Visual studio add project reference with lib file

It looks like there has been some work done to add support for visual studio c++ project to project references.
https://developercommunity.visualstudio.com/t/MsBuild-to-resolve-and-copy-ProjectRefer/668834?space=62
If I setup a reference from GoogleTestSample to oqkern I am able to get a bunch of files copied locally.
oqkern.net.dll
oqkern.net.runtimeconfig.json
and also a bunch of other dlls
Is there a way I can get the oqkern.net.lib file to be copied locally as well?
I seem to need that to be able to compile without getting linker errors.
I think the property below takes care of being able to include source files from the referenced project:
I have tried to use the "content file" approach but it didn't seem to work.
<ItemGroup>
<Library Include="..\..\src\oqkern\bin\$(Platform)\$(Configuration)\oqkern.net.lib">
<DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</DeploymentContent>
<DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</DeploymentContent>
</Library>
</ItemGroup>
I'm not sure if the content file approach happens soon enough, I need the file copied before the build so things compile.
A pre-build event works:
xcopy /y /d "$(SolutionDir)src\oqkern\bin\$(Platform)\$(Configuration)\oqkern.net.lib" "$(OutDir)"

How to avoid that Visual Studio incremental build does not run when files outside <Compile> and <EmbeddedResource> are changed?

I have a VS2017 csharp project and the .csproj file looks like the following:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<MyItem Include="file.dat" />
</ItemGroup>
<PropertyGroup>
<PrepareResourcesDependsOn>
$(PrepareResourcesDependsOn);
MyCompileTarget
</PrepareResourcesDependsOn>
<CoreCompileDependsOn>
$(CoreCompileDependsOn);
MyCompileTarget
</CoreCompileDependsOn>
</PropertyGroup>
<Target Name="MyCompileTarget" Inputs="#(MyItem)" Outputs="#(MyItem->'%(FileName).out')">
...
</Target>
</Project>
Where MyCompileTarget is a target that generates the file.out from file.dat (in the actual code the incremental build target and properties are in a target file automatically included via a NuGet package).
The issue is that if I change file.dat and press on Build, no target is executed at all, (but MyTarget is correctly executed with Rebuild or when running with msbuild). I would expect the MyCompileTarget to be executed so that the file.out file is updated.
The same issue occurs if I use BeforeBuild or AfterBuild instead of PrepareResourcesDependsOn etc.
It seems that Visual Studio incremental build won't start unless some file in #(Compile) or #(EmbeddedResource) is changed. Indeed, if I add the following
<EmbeddedResource>file.dat</EmbeddedResource>
the incremental build works as expected (but clearly I do not want to embeed the file.dat into the generated assembly).
Is it possible to force Visual Studio to enable incremental build if file.dat is modified, and if the corresponding generated file is older than file.dat or it does not exist?
Note: the same issue occurs using VS2015, with .NET CORE or .NET FRAMEWORK.
Also, incremental build will be triggered if I change a csharp file, and it will therefore trigger MyTask, but only if file.dat is newer than the generated file (as expected).
Thanks in advance,
Fabio.
Is it possible to force Visual Studio to enable incremental build if file.dat is modified
You can set the property DisableFastUpToDateCheck to true in the project file to disable FastUpToDateCheck for Visual Studio build manager:
<PropertyGroup>
<DisableFastUpToDateCheck>True</DisableFastUpToDateCheck>
</PropertyGroup>
Check MSDN about DisableFastUpToDateCheck:
A boolean value that applies to Visual Studio only. The Visual Studio
build manager uses a process called FastUpToDateCheck to determine
whether a project must be rebuilt to be up to date. This process is
faster than using MSBuild to determine this. Setting the
DisableFastUpToDateCheck property to true lets you bypass the Visual
Studio build manager and force it to use MSBuild to determine whether
the project is up to date
Update:
Also, we can set the UpToDateCheckInput to the item:
<UpToDateCheckInput Include="file.dat" />
Disabling the VS fast up-to-date check will make your builds much slower. Don't do it!
Instead, make sure the up-to-date check knows about the items in your project and how they relate to build. There are two kinds of item you can add to your project for this:
UpToDateCheckInput for inputs
UpToDateCheckBuilt for outputs
In your case you need the second option as there is both an input and an output. You need to ensure that if you delete the output, it is rebuilt.
<PropertyGroup>
<UpToDateCheckBuilt Original="#(MyItem)" Include="#(MyItem->'%(FileName).out')">
</PropertyGroup>
For more information, see the documentation:
https://github.com/dotnet/project-system/blob/main/docs/up-to-date-check.md

What is the default location for MSBuild logs?

I am using Visual Studio Express 2012. Where is the location of the log file? I have searched in the folder where my solution and projects are stored, but cannot find any .log file.
This is the configuration for logging:
Log file from Visual Studio is only supported for C++ projects. You just have to work with the output window for others.
See this similar thread: VS2010: minimal build log in output and detailed log in log file
And in case you happen to do this for a C++ project, the file is at:
... build log in the intermediate files directory
... The path and name of the build log is represented by the MSBuild macro
expression, $(IntDir)\$(MSBuildProjectName).log.
Use build output instead of logging to file. Instead of copy/paste, simply click somewhere in the output and press CTRL + S to save. Visual Studio will prompt you for a location (tested with Visual Studio 2017, but I'm assuming this works in earlier versions too).
The msdn documentation is pretty clear about this (And you ain't gonna like it!):
https://msdn.microsoft.com/en-us/library/jj651643.aspx
Where it says:
To create a build log file for a managed-code project On the menu bar,
choose Build, Build Solution.
In the Output window, highlight the
information from the build, and then copy it to the Clipboard.
Open a
text editor, such as Notepad, paste the information into the file, and
then save it.
While it's true that VS doesn't allow this directly, it is still possible to build with MSBuild "inside" VS2015 and get both the build window output and the log file, as follows: (Arguably this is a bit of a hack.)
In your VS Managed solution, add a new project (Let's call it 'Make').
a. The project type you want is Visual C++/NMake project.
Define the MSBuild commands you need on the command line (see below).
Change the solution configuration to build the NMake project instead of the normal managed projects.
This will create a project that has Build, Rebuild, and Clean command lines where you can execute MSBuild directly. For example:
Rebuild: MSBuild.exe /ds /v:diag /property:Configuration=Debug ..\BuildTest\BuildTest.csproj /t:Clean,Build
Build: MSBuild.exe /ds /v:diag /property:Configuration=Debug ..\BuildTest\BuildTest.csproj /t:Build
Clean: MSBuild.exe /ds /v:diag /property:Configuration=Debug ..\BuildTest\BuildTest.csproj /t:Clean
You can also specify multiple MSBuild.EXE command lines in order to build multiple projects. For the usual build-the-entire-solution outcome you can target only the final end assemblies and let the dependency graph generate the individual targets.
This will produce a .log file, where NAME is the name of the NMake project you used. In the example above, the log would be make.log.
A working example is available on GitHub:
https://github.com/bitblitz/VS_MsbuildExample
(Tested with VS2015)
Note that building individual projects directly will still build with the normal VS behavior, but you can build the full solution inside VS and get the build logs.

Run web.config transformation from command-line

Good day!
I want to have ability to build ASP.NET MVC 2 project using VS2010 Publish dialog and from command-line.
For command-line I get the following to work:
C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe .\SolutionFolder\MyProject.csproj /p:Configuration=Release;DeployOnBuild=True;PackageAsSingleFile=False;outdir=c:\_OutputFolder\
The only problem I have that Web.config transformation are not applied (but added to WebDeploy package). I don't use WebDeploy. Is there any way to apply Web.config transformations?
Thanks!
You can also try using the XDT Transformation Tool:
http://ctt.codeplex.com was moved to Github
https://github.com/greenfinch/ctt
I'm using this instead of messing with obscure msbuild targets.
Here is another approach, which uses msbuild to transform Web.config file:
http://sedodream.com/2010/04/26/ConfigTransformationsOutsideOfWebAppBuilds.aspx
In my tests the results were better. Basically, you create a project file to perform only a TransformXML task:
<Project ToolsVersion="4.0" DefaultTargets="Demo" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask TaskName="TransformXml"
AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll"/>
<Target Name="Demo">
<TransformXml Source="app.config"
Transform="Transform.xml"
Destination="app.prod.config"/>
</Target>
</Project>
Save the project file and then apply the transformation, running the following command:
msbuild trans.proj /t:Demo
Where trans.proj is the name of the project file and Demo is the name of task target.
I think it's worth to mention that you can also use with PowerShell the DLL that Visual Studio is using: Microsoft.Web.XmlTransform.dll
PowerShell script, see: Web.Config transforms outside of Microsoft MSBuild?
To load the DLL instead of copying around, I do like this (so you see where to find this DLL, at least in my scenario at work we had to look-up these locations):
if (Test-Path "C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v14.0\Web\Microsoft.Web.XmlTransform.dll") {
Add-Type -LiteralPath "C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v14.0\Web\Microsoft.Web.XmlTransform.dll"
} elseif (Test-Path "C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v12.0\Web\Microsoft.Web.XmlTransform.dll") {
Add-Type -LiteralPath "C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v12.0\Web\Microsoft.Web.XmlTransform.dll"
} else {
throw [System.IO.FileNotFoundException] "Microsoft.Web.XmlTransform.dll not found."
}

How to make Visual Studio copy a DLL file to the output directory?

I have a Visual Studio C++ project that relies on an external DLL file. How can I make Visual Studio copy this DLL file automatically into the output directory (debug/release) when I build the project?
Use a post-build action in your project, and add the commands to copy the offending DLL. The post-build action are written as a batch script.
The output directory can be referenced as $(OutDir). The project directory is available as $(ProjDir). Try to use relative pathes where applicable, so that you can copy or move your project folder without breaking the post-build action.
$(OutDir) turned out to be a relative path in VS2013, so I had to combine it with $(ProjectDir) to achieve the desired effect:
xcopy /y /d "$(ProjectDir)External\*.dll" "$(ProjectDir)$(OutDir)"
BTW, you can easily debug the scripts by adding 'echo ' at the beginning and observe the expanded text in the build output window.
The details in the comments section above did not work for me (VS 2013) when trying to copy the output dll from one C++ project to the release and debug folder of another C# project within the same solution.
I had to add the following post build-action (right click on the project that has a .dll output) then properties -> configuration properties -> build events -> post-build event -> command line
now I added these two lines to copy the output dll into the two folders:
xcopy /y $(TargetPath) $(SolutionDir)aeiscontroller\bin\Release
xcopy /y $(TargetPath) $(SolutionDir)aeiscontroller\bin\Debug
To do it with the GUI, first add the file(s) to the project: right-click the project, select "Add...", then "Existing Item", then browse to the file or files you want to add and click "Add". Next, tell Visual Studio to copy the file when you build: right-click the file you want to copy, select "Properties". You'll see a list of properties, including "Item Type". Change the "Item Type" to "Copy File". Hit OK and you're done.
Here's the file properties dialog:
Looking in the *.vcxproj file, the steps above add something like this:
<ItemGroup>
<CopyFileToFolders Include="libs\a.dll" />
<CopyFileToFolders Include="libs\a.dll" />
</ItemGroup>
I couldn't find any official documentation for <CopyFileToFolders>, but clearly it's supported or the GUI wouldn't use it. But, if you're doing it by hand and an undocumented item type makes you uncomfortable you can always use the well known but slightly more verbose <Content> type:
<ItemGroup>
<Content Include="libs\a.dll" >
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="libs\b.dll" >
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
Add builtin COPY in project.csproj file:
<Project>
...
<Target Name="AfterBuild">
<Copy SourceFiles="$(ProjectDir)..\..\Lib\*.dll" DestinationFolder="$(OutDir)Debug\bin" SkipUnchangedFiles="false" />
<Copy SourceFiles="$(ProjectDir)..\..\Lib\*.dll" DestinationFolder="$(OutDir)Release\bin" SkipUnchangedFiles="false" />
</Target>
</Project>
(This answer only applies to C# not C++, sorry I misread the original question)
I've got through DLL hell like this before. My final solution was to store the unmanaged DLLs in the managed DLL as binary resources, and extract them to a temporary folder when the program launches and delete them when it gets disposed.
This should be part of the .NET or pinvoke infrastructure, since it is so useful.... It makes your managed DLL easy to manage, both using Xcopy or as a Project reference in a bigger Visual Studio solution. Once you do this, you don't have to worry about post-build events.
UPDATE:
I posted code here in another answer https://stackoverflow.com/a/11038376/364818
xcopy /y /d "$(ProjectDir)External\*.dll" "$(TargetDir)"
You can also refer to a relative path, the next example will find the DLL in a folder located one level above the project folder. If you have multiple projects that use the DLL in a single solution, this places the source of the DLL in a common area reachable when you set any of them as the Startup Project.
xcopy /y /d "$(ProjectDir)..\External\*.dll" "$(TargetDir)"
The /y option copies without confirmation.
The /d option checks to see if a file exists in the target and if it does only copies if the source has a newer timestamp than the target.
I found that in at least newer versions of Visual Studio, such as VS2109, $(ProjDir) is undefined and had to use $(ProjectDir) instead.
Leaving out a target folder in xcopy should default to the output directory. That is important to understand reason $(OutDir) alone is not helpful.
$(OutDir), at least in recent versions of Visual Studio, is defined as a relative path to the output folder, such as bin/x86/Debug. Using it alone as the target will create a new set of folders starting from the project output folder. Ex: … bin/x86/Debug/bin/x86/Debug.
Combining it with the project folder should get you to the proper place. Ex: $(ProjectDir)$(OutDir).
However $(TargetDir) will provide the output directory in one step.
Microsoft's list of MSBuild macros for current and previous versions of Visual Studio
I had a similar question. In my project, there were couple of external DLLs. So I created a new folder in the project called "lib" and copied all the external dlls to this folder.
Add a reference to these DLLs.
Go to Project References>dll properties and change the following properties
enter image description here

Resources