MSBuild targets file doesn't cause outputs to be built, even though inputs are newer - visual-studio-2010

I am trying to create a msbuild/VS2010 .targets file for flex, so that I can use .l files in Visual Studio 2010. So far, I have produced this:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<LFiles Include="$(MSBuildProjectDirectory)\**\*.l"/>
</ItemGroup>
<Target Name="Flex"
BeforeTargets="ClCompile"
Inputs="#(LFiles)"
Outputs="#(LFiles->'%(RootDir)%(Directory)%(Filename).c')"
Returns="#(LFiles->'%(RootDir)%(Directory)%(Filename).c')">
<Exec Command="flex.exe "-o%(LFiles.RootDir)%(LFiles.Directory)%(LFiles.Filename).c" "%(LFiles.FullPath)" 2>&1 | sed -e "s/. line \([0-9]\+\)/(\1)/" | sed -e s/\"//g"/>
</Target>
</Project>
I added this file to my project, using the Build Customizations... dialog. I then added my .l file, scan.l, and built the project - scan.l was created in the expected place, suggesting that the file name transformations are working. Next, I added scan.c to the project, and built the project again. The generated .c file compiled correctly, and the project linked. So it seems that things are basically working.
My expectation was that Visual Studio would then invoke flex if the .l file changes. The target's Inputs attribute includes the .l file, and its Outputs attribute includes the .c file, and Visual Studio can check whether the input is newer than the output. But in fact, this doesn't happen. If I just change the .l file and rebuild, Visual Studio tells me everything is up to date.
If I delete the .c file and build the project, the .c file is regenerated, just as I'd expect. But if I just change the .l file and build the project, nothing happens. In fact, in this case, msbuild doesn't even seem to run! If I delete the build log before building, Visual Studio tells me that everything is up to date, and no new build log is produced. This makes it rather hard for me to work out what might be going on.
This is my first go with msbuild, so I am probably doing something wrong. But what?
(N.B. my targets file might fall over with multiple .l files, or suffer from some other flaw(s) unrelated to the dependency checking issue - I am not bothered about any of this at this stage.)

To figure out the fix, I realised I could create a Visual Studio 2008 project with a .rules file, load it into Visual Studio 2010, let Visual Studio 2010 convert it automatically, and examine the result. So I did that.
To fix the problem, add an XML file with the same base name as the targets file. This one will do for this example:
<?xml version="1.0" encoding="utf-8"?>
<ProjectSchemaDefinitions xmlns="clr-namespace:Microsoft.Build.Framework.XamlTypes;assembly=Microsoft.Build.Framework" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:transformCallback="Microsoft.Cpp.Dev10.ConvertPropertyCallback">
<ItemType
Name="Flex"
DisplayName="Flex" />
<FileExtension
Name="*.l"
ContentType="Flex" />
<ContentType
Name="Flex"
DisplayName="Flex"
ItemType="Flex" />
</ProjectSchemaDefinitions>
Next, refer to this file in the ItemGroup section. The .targets file is pretty small so here's the new version in its entirety:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PropertyPageSchema Include="$(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml"></PropertyPageSchema>
<LFiles Include="$(MSBuildProjectDirectory)\**\*.l"/>
</ItemGroup>
<Target Name="Flex"
BeforeTargets="ClCompile"
Inputs="#(LFiles)"
Outputs="#(LFiles->'%(RootDir)%(Directory)%(Filename).c')"
Returns="#(LFiles->'%(RootDir)%(Directory)%(Filename).c')">
<Exec Command="flex.exe "-o%(LFiles.RootDir)%(LFiles.Directory)%(LFiles.Filename).c" "%(LFiles.FullPath)" 2>&1 | sed -e "s/. line \([0-9]\+\)/(\1)/" | sed -e s/\"//g"/>
</Target>
</Project>
Now reload the project, and visit the properties for the .l file. Select the new Flex option from the Item Type dropdown. This seems to prod Visual Studio into taking a little more care over the file.

Related

Stop Visual Studio 2017 From Autogenerating Output Directory

So I have a solution with 70ish projects that I updated to use a Directory.build.props file to use a single bin folder to make our CI process cleaner. Works great and now everything is in 1 locaction
BUT the problem now is when I open Visual Studio 2017 it creates a bunch of extra project folders now that NEVER get used. Anyway to disable this? It's just confusing to people and clutters up everyone's dev repo.
Example:
%sourceroot%\bin\release\ (this is where all the projects get happily binplaced)
%sourceroot%\bin\project1Neverused\ (unwanted folders that just clutter my dev box up)
%sourceroot%\bin\project2neverUsed\
%sourceroot%\bin\project1Neverused\
%sourceroot%\bin\project2neverUsed\
Here's my Directory.build.props file:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<SolutionDir>$(MSBuildThisFileDirectory)</SolutionDir>
<OutputPath>$(SolutionDir)bin\$(Configuration)\$(MSBuildProjectName)</OutputPath>
<OutDir>$(OutputPath)</OutDir>
</PropertyGroup>
</Project>
Now open the sln file in Visual studio and if you look VS will have auto generated those folders even though you haven't built anything yet. I want that to be disabled because it's just generating junk folders that aren't used. Since we use a props file for msbuild to binplace we didn't update each project file from the default of "bin\debug"
Yes, Visual Studio will create those bin/obj folder by default when you create a new project/solution. It seems that there is no direct setting to prevent Visual Studio from generating these folders.
As a workaround, you can try to add a delete task in Directory.build.props file to delete the those folder:
<Target Name="CleanFolder" AfterTargets="Build">
<RemoveDir Directories="$(BaseIntermediateOutputPath)" />
<RemoveDir Directories="$(ProjectDir)bin" />
</Target>
Hope this helps.

Custom Build Rule fails after converting to VS2013

I need to integrate a legacy VS2008 project into my VS2013 solution. This project uses some custom build rules which initially worked after converting the .vcproj to a .vcxproj. However, when doing a fresh checkout of the project including the .vcxproj, the project file can no longer be opened.
I've tracked it down to this issue:
The project file references a couple of custom build rules like this:
<ImportGroup Label="ExtensionSettings">
<Import Project="..\..\..\tools\build\ms_mc.props" />
(8 similar lines follow)
</ImportGroup>
However, the ms_mc.props file is not present, but there is a ms_mc.rule file. If I convert the VS2008 solution with VS2013 (and assumably also if I opened it in VS2008, which I don't possess), the ms_mc.props file (plus a .targets and a .xml file) is created. However, if I delete that file and open the converted VS2013 project, the file does not get created.
I realized, in the old .vcproj, the corresponding lines are
<ToolFiles>
<ToolFile RelativePath="..\..\..\tools\build\ms_mc.rule" />
(8 similar lines follow)
</ToolFiles>
Why does VS2008 reference the .rule file and VS2013 imports the .props file without specifying the .rule file? And more importantly: How can I make this work again?
The .rule and .props file are added for reference
ms_mc.rule:
<?xml version="1.0" encoding="utf-8"?>
<VisualStudioToolFile
Name="MS MC"
Version="8,00"
>
<Rules>
<CustomBuildRule
Name="MS_MC"
DisplayName="Microsoft Message Catalogue Compiler"
CommandLine="mc [Verbose] [inputs] [RCIncludePath] [CIncludePath]"
Outputs="[$RCIncludePath]\$(InputName).rc;[$RCIncludePath]\$(InputName).h"
FileExtensions="*.mc"
ExecutionDescription="Compiling Message Catalogue $(InputName).mc"
>
<Properties>
<BooleanProperty
Name="Verbose"
DisplayName="Verbose"
Description="Gives verbose output. (-v)"
Switch="-v"
/>
<StringProperty
Name="RCIncludePath"
DisplayName="RC include file path"
Description="Gives the path of where to create the RC include file and the binary message resource files it includes. (-r [pathspec])"
Switch="-r [value]"
DefaultValue=".\"
/>
<StringProperty
Name="CIncludePath"
DisplayName="C include file path"
Description="Gives the path of where to create the include header file. (-h [pathspec])"
Switch="-h [value]"
DefaultValue=".\"
/>
</Properties>
</CustomBuildRule>
</Rules>
</VisualStudioToolFile>
ms_mc.props (after Conversion to VS2013):
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup
Condition="'$(MS_MCBeforeTargets)' == '' and '$(MS_MCAfterTargets)' == '' and '$(ConfigurationType)' != 'Makefile'">
<MS_MCBeforeTargets>Midl</MS_MCBeforeTargets>
<MS_MCAfterTargets>CustomBuild</MS_MCAfterTargets>
</PropertyGroup>
<PropertyGroup>
<MS_MCDependsOn
Condition="'$(ConfigurationType)' != 'Makefile'">_SelectedFiles;$(MS_MCDependsOn)</MS_MCDependsOn>
</PropertyGroup>
<ItemDefinitionGroup>
<MS_MC>
<Verbose>False</Verbose>
<RCIncludePath>.\</RCIncludePath>
<CIncludePath>.\</CIncludePath>
<CommandLineTemplate>mc [Verbose] [inputs] [RCIncludePath] [CIncludePath]</CommandLineTemplate>
<Outputs>%(RCIncludePath)\%(Filename).rc;%(RCIncludePath)\%(Filename).h</Outputs>
<ExecutionDescription>Compiling Message Catalogue %(Filename).mc</ExecutionDescription>
</MS_MC>
</ItemDefinitionGroup>
</Project>
I found this blog post for VS2010 which states the following:
Custom build rule is a build feature introduced in VS2005. It provides the ability for the users to easily Plug-In third party tools to use in their build process. The custom build rule is defined by “.rules” files.
and more importantly
In VS2010, due to the migration to MSBuild, the information in the rules file is represented by three files: .XML, .props and .targets files.
This basically means that the .XML, .props and .targets files are in fact not created by VS2008; instead, they are a replacement of the old .rules file format since VS2010. Using this information, I can now safely check in those new files without breaking the VS2008 solution. I might have to adapt the new files manually in order to make them work as before, as also mentioned in the blog.

Is there a way to add source files to visual studio project from command-line?

I want to use sublime to edit a visual studio project.
I have a custom build:
{
"cmd": ["c:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\MSBuild.exe"],
"working_dir": "${project_path:${folder:${file_path}}}/../Project"
}
But if I add new files I also need to include them in the project.
Is there a way to do this from the command line, maybe at compile-time?
I am working with opengl using c++;
I basically set up a project using one of the examples provided on the opengl website.
Then I opened the project folder in sublime text and successfully compiled it using the custom build system.
However, when I add NEW source files to the project (*.h and *.cpp) I get a linking error.
I get the same error when I build in visual studio.
The error disappeared after I had included the files by manually browsing and adding them to the project.
What I wanted was a way to automatically add all the source files in a folder to the project(via command line, or wildcard or smth else).
This way I can easily work on a vs2010 project in sublime, add new source files and build the project.
Or maybe there already is a better workflow for this?
You could try to modify your .vcxproj file to include any .h and .cpp file in your project folder or folders below.
In case of a c++ VS project you can try to alter your .vcxproj file like this:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- rest of project file untouched -->
<!-- start of modified part -->
<ItemGroup>
<ClInclude Include="**\*.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="**\*.cpp" />
</ItemGroup>
<!-- end of modified part -->
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
Be aware that adding files to your project from inside VS at later point will replace the modification described above!
As an alternative you could also create an external project file holding the same <ItemGroup /> elements described above and include this project file into your .vcxproj.
I'll add an example of this alternative if you're interested.

How do I use an MSBuild file from Visual Studio 2012?

I have a simple MSBuild file that I'm learning with.
Here it is:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Clean" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<ProjectGuid>{D5A16164-962E-4A6D-9382-240F31AB6C50}</ProjectGuid>
</PropertyGroup>
<Target Name="Clean">
<ItemGroup>
<BinFiles Include="bin\*.*" />
<fff Include="f\*.*" />
</ItemGroup>
<Delete Files="#(BinFiles)" />
<Delete Files="#(fff)" />
</Target>
</Project>
Now I want to include this in a Visual Studio solution and be able to run the "clean" target from Visual Studio 2012. I tried naming it testproject.msbuildproj like the internet seems to suggest "works", but it doesn't work. When I run the clean command I just get "unexpected error".
If I rename the project to testproject.csproj, it does some unintuitive things like creating compilation directories, but it does actually run my clean command properly. However, this is undesireable because it creates obj and bin/x86/debug type directories. It also looks goofy in Visual Studio because it still gives the References drop down.
How can I use just a plain vanilla MSBuild project from Visual Studio without random errors or false assumptions?
Note I only am having a problem with this from Visual Studio. Using msbuild from the command line it works perfectly
Visual Studio creates bin / obj folders when it opens csproj file. When you click Build / Rebuild / Clean it just uses appropriate targets from the project file.
You cannot stop VS from creating these folders, but you can ask it to create them in say temp folder by setting appropriate properties - refer this MSDN article for details.
So the steps are to rename your project to csproj, and add the following lines into project:
<PropertyGroup>
<OutputPath>$(Temp)\bin</OutputPath>
<IntermediateOutputPath>$(Temp)\obj</IntermediateOutputPath>
</PropertyGroup>
I usually use a bit different approach to work with MSBUILD files from VS:
I use regular csproj file with removed Import ... CSharp.targets part as pure container for my Build projects.
I add actual build files with targets and logic, and all properties, necessary artifacts like XSLT etc using "Include into project", so I can manage hierarchy and change any file from within VS.Net.
I redefine Build / Rebuild targets in csproj file for whatever I need, for example Build may contain minimum output, and while rebuild diagnostic one.
Like this:
<Target Name="Build">
<Exec Command="%windir%\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe Builds\build.proj /t:Build /v:m" />
</Target>
<Target Name="Rebuild">
<Exec Command="%windir%\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe Builds\build.proj /t:Build /v:d" />
</Target>

Build doesn't work from VisualStudio, but is ok from msbuild

From a brand new console application template in visual studio, I edited the .csproj to build another project like this:
...
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="BeforeBuild">
<MSBuild Projects=".\other.mproj"/>
</Target>
...
Where other.mproj is:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
<Target Name="Build">
<Message Text="kikou" />
</Target>
</Project>
After a while I discovered that modifying the content of other.mproj (for instance, by introducing errors or changing text kikou to something else) would not be taken into account unless unloading/reloading the project in visual studio.
Building from the command line with 'msbuild myproj.csproj' immediatly detect changes in 'other.mproj'. So it all looks like visual studio is working from a cached version of other.mproj file.
Why is visual studio caching this other script (which is even not included to the project), and how can I solve this issue ?
Update: I also tried this UseHostCompilerIfAvailable, it doesn't work.
NB1: I didn't add other.mproj as a project reference in the .csproj because it is not a .NET project at all (it just creates resources files for the .csproj from other inputs before the build)
NB2: I'm using VS2010 (10.0.10219.1SP1Rel + MSBuild 4.0.30319.1)
Visual Studio caches all MSBuild files, this is done for performance reasons. You will not be able to have an MSBuild only way around this. It may be possible to achieve this via a VS add-in but I'm not 100% sure of that.

Resources