Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Globbing with DependentUpon #169

Closed
aienabled opened this issue Feb 6, 2017 · 16 comments
Closed

Globbing with DependentUpon #169

aienabled opened this issue Feb 6, 2017 · 16 comments
Assignees

Comments

@aienabled
Copy link

aienabled commented Feb 6, 2017

I've read this page about globbing in VS 2017 RC /~https://github.com/Microsoft/VSProjectSystem/blob/master/doc/overview/globbing_behavior.md and very happy with how everything works. But...

Are there any way of using DependentUpon with globbing?

Currently I have these rules:

    <Page Include="**\*.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>

    <Compile Include="**\*.xaml.cs">
      <DependentUpon>*.xaml</DependentUpon>
    </Compile>

But it seems DependentUpon is not working this way - solution explorer displays Foo.xaml and Foo.xaml.cs as the separate files, but I need Foo.xaml.cs to be nested in Foo.xaml.
There is a warning generated:
The parent file, '*.xaml', for file 'Foo.xaml.cs' cannot be found in the project file.

Regards!

@aienabled
Copy link
Author

By the way, DependentUpon is applied to my XAML rule (and worked fine in VS2015) /~https://github.com/Microsoft/VSProjectSystem/blob/b70104c4781749369c995a48f83e64607a0c6594/doc/extensibility/automatic_DependentUpon_wireup.md but it doesn't work with globbing.
Here are the rules I'm using:

    <ContentType
        Name="PageXaml"
        DisplayName="XAML Page"
        ItemType="Page">
        <NameValuePair Name="DependentFileExtensions" Value=".cs" />
        <NameValuePair Name="DefaultMetadata_SubType" Value="Designer" />
        <NameValuePair Name="DefaultMetadata_Generator" Value="MSBuild:Compile" />
    </ContentType>

    <ItemType Name="Page" DisplayName="XAML Page" />

    <FileExtension Name=".xaml" ContentType="PageXaml" />

@aienabled
Copy link
Author

Ok, I've found a good workaround - added this to the CSPROJ:

    <Compile Update="**\*.xaml.cs">
      <DependentUpon>%(Filename)</DependentUpon>
      <SubType>Code</SubType>
    </Compile>

@clairernovotny
Copy link

clairernovotny commented Feb 7, 2017

This pattern may also be useful for T4 templates and their generated output. What does %(Filename) expand to here? foo.xaml, foo.xaml.cs? does it need to be dependent on the one w/o the cs extension? 

@aienabled
Copy link
Author

aienabled commented Feb 7, 2017

For those who're also using ReSharper it might be helpful - it seems I've found a bug which is affects only ReSharper - it reports for every named XAML element that the field is already declared - because it looks into the obj folder and sees .xaml.g.cs files. That's strange because I explicitly removed this folder from the included compile items:

<-- this not works for ReSharper for some reason -->
<Compile Include="**\*.cs"  />
<Compile Remove="obj\**"  />

Use this instead:

<Compile Include="**\*.cs" Exclude="obj\**" />

@aienabled
Copy link
Author

aienabled commented Feb 7, 2017

The resulting files globbing code for including XAML files with according .XAML.CS code-behind files in my case:

    <Compile Include="**\*.cs" Exclude="obj\**" />    
    <Compile Update="**\*.xaml.cs">
        <DependentUpon>%(Filename)</DependentUpon>      
        <SubType>Code</SubType>
    </Compile>
			
    <Page Include="**\*.xaml">
        <Generator>MSBuild:Compile</Generator>
        <SubType>Designer</SubType>      
    </Page>

Please note that you need to define the Page rule to properly process XAML files with CPS. It was already discussed at issue #99.

@adrianvmsft
Copy link
Member

In order to support Depend Upon items in combination with globs, we added a new feature - dynamic depend upon items - the dependency between files is calculated dynamically when project is loaded and doesn't get persisted in the project file.

Documentation is pending, so here is a quick overview:

You can use the existing implementation of IDependentFilesProvider that uses metadata defined in the project items schema to identify dependent files:

<ContentType
  Name="aaaSourceFile"
  DisplayName="aaa source file"
  ItemType="aaaCompile">
    <ContentType.Metadata>
      <NameValuePair Name="DependentExtensions" Value=".aaa.cs;.aaa.txt" />
    </ContentType.Metadata>
</ContentType>

This snippet will make .aaa.cs and .aaa.txt files to be displayed as dependent on .aaa files

For more advanced relationships you can implement a new IDependentFilesProvider.

@aienabled
Copy link
Author

aienabled commented Feb 14, 2017

@adrianvmsft, thanks! I just checked and it doesn't work for me in the latest VS2017 RC - dependent .xaml.cs files are displayed separately. I'm using this XAML rule:

<ContentType
        Name="PageXaml"
        DisplayName="XAML Page"
        ItemType="Page">
		<ContentType.Metadata>
			<NameValuePair Name="DependentExtensions" Value=".xaml.cs" />
		</ContentType.Metadata>
</ContentType>

@adrianvmsft
Copy link
Member

You're right, I forgot to mention that you also need to define the DynamicDependentFile capability. Sorry about that.

Could you try adding the following to your project (e.g. in the .targets file):

<ProjectCapability Include="DynamicDependentFile"/>

@aienabled
Copy link
Author

@adrianvmsft, thanks! It works now as expected.
It would be great if you also update the automatic_DependentUpon_wireup.md page to mention about this new feature.

Regards!

@adrianvmsft adrianvmsft self-assigned this Feb 15, 2017
@adrianvmsft
Copy link
Member

Great! I am glad that it worked!
Updating that page is already on my todo list - I actually pretty much did half of the work replying here :). Once I do that, I will close this issue.
Thanks!

@adrianvmsft
Copy link
Member

Here is the PR with the documentation update, in case you would like to take a look: #187

@bdovaz
Copy link

bdovaz commented Sep 4, 2017

Anyone got this working? I'm able to compile it but I lost intellisense in VS 2017.

This is my csproj:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <LanguageTargets>$(MSBuildExtensionsPath)\$(VisualStudioVersion)\Bin\Microsoft.CSharp.targets</LanguageTargets>
    <OutputType>winexe</OutputType>
    <TargetFramework>net462</TargetFramework>
    <DebugType>Full</DebugType>
    <ApplicationIcon>res\ico\icon.ico</ApplicationIcon>
    <OutputTypeEx>winexe</OutputTypeEx>
    <StartupObject />
  </PropertyGroup>

  <PropertyGroup>
    <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">15.0</VisualStudioVersion>
    <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
    <TransformOnBuild>True</TransformOnBuild>
    <TransformOutOfDateOnly>false</TransformOutOfDateOnly>
  </PropertyGroup>

  <Import Project="$(VSToolsPath)\TextTemplating\Microsoft.TextTemplating.targets" />

  <ItemGroup>
    <!-- App.xaml -->
    <ApplicationDefinition Include="App.xaml" SubType="Designer" Generator="MSBuild:UpdateDesignTimeXaml" />

    <!-- XAML elements -->
    <Page Include="**\*.xaml" SubType="Designer" Generator="MSBuild:UpdateDesignTimeXaml" Exclude="App.xaml" />
    <Compile Update="**\*.xaml.cs" SubType="Designer" DependentUpon="%(Filename)" />
    <Compile Include="$(IntermediateOutputPath)**\*.g.cs" Visible="false" />
    <None Include="$(ProjectDir)obj" Visible="false" />

    <!-- Resources -->
    <EmbeddedResource Update="Properties\Resources.resx" Generator="ResXFileCodeGenerator" LastGenOutput="Resources.Designer.cs" />
    <Compile Update="Properties\Resources.Designer.cs" AutoGen="True" DependentUpon="Resources.resx" DesignTime="True" />

    <!-- Settings -->
    <None Update="Properties\Settings.settings" Generator="SettingsSingleFileGenerator" LastGenOutput="Settings.Designer.cs" />
    <Compile Update="Properties\Settings.Designer.cs" AutoGen="True" DependentUpon="Settings.settings" />

    <None Update="App.config">
      <TransformOnBuild>true</TransformOnBuild>
    </None>
    <None Update="App.Debug.config">
      <IsTransformFile>True</IsTransformFile>
    </None>
    <None Update="App.Release.config">
      <IsTransformFile>True</IsTransformFile>
    </None>
  </ItemGroup>

  <ItemGroup>
    <Reference Include="PresentationCore" />
    <Reference Include="PresentationFramework" />
    <Reference Include="System.Xaml" />
    <Reference Include="WindowsBase" />
  </ItemGroup>

  <ItemGroup>
    <Compile Remove="Publish\**" />
    <EmbeddedResource Remove="Publish\**" />
    <None Remove="Publish\**" />
    <Page Remove="Publish\**" />
  </ItemGroup>

  <PropertyGroup>
    <AssemblySearchPaths>$(AssemblySearchPaths);{GAC}</AssemblySearchPaths>
  </PropertyGroup>

  <Import Project="$(MSBuildSDKExtrasTargets)" Condition="Exists('$(MSBuildSDKExtrasTargets)')" />
</Project>

@aienabled
Copy link
Author

aienabled commented Sep 5, 2017

@bdovaz I responded there dotnet/project-system#1467 (comment)

@bradphelan
Copy link

bradphelan commented Oct 6, 2017

Here is a fun example of bad behaviour of globbing and the project system. I have

  <ItemGroup>
    <Compile Update="Properties\*.resx.cs">
      <DesignTime>True</DesignTime>
      <AutoGen>True</AutoGen>
      <DependentUpon>%(Filename)</DependentUpon>
    </Compile>
  </ItemGroup>

  <ItemGroup>
    <EmbeddedResource Update="Properties\*.resx">
      <Generator>I18NReactive</Generator>
      <LastGenOutput>%(Filename).resx.cs</LastGenOutput>
    </EmbeddedResource>
  </ItemGroup>

where I18NReactive is a custom single file generator I wrote. The globbing and dependencies work at first glance.

image

Then I right click and select run custom tool to regen the resx.cs file. And not only does it update the file it also updates my csproj file. 👎 so now it is

  <ItemGroup>
    <Compile Update="Properties\*.resx.cs">
      <DesignTime>True</DesignTime>
      <AutoGen>True</AutoGen>
      <DependentUpon>%(Filename)</DependentUpon>
    </Compile>
  </ItemGroup>

  <ItemGroup>
    <EmbeddedResource Update="Properties\*.resx">
      <Generator>I18NReactive</Generator>
      <LastGenOutput>%(Filename).resx.cs</LastGenOutput>
    </EmbeddedResource>
  </ItemGroup>

  <ItemGroup>
    <Compile Update="Properties\lang.resx.cs">
      <DesignTime>True</DesignTime>
      <AutoGen>True</AutoGen>
      <DependentUpon>lang.resx</DependentUpon>
    </Compile>
  </ItemGroup>

  <ItemGroup>
    <EmbeddedResource Update="Properties\lang.resx">
      <Generator>I18NReactive</Generator>
      <LastGenOutput>lang.resx.cs</LastGenOutput>
    </EmbeddedResource>
  </ItemGroup>

that's kinda wierd no?

I also tried

 <ItemGroup>
    <Compile Remove="Properties\*.resx.cs" />
    <Compile Include="Properties\*.resx.cs">
      <DesignTime>True</DesignTime>
      <AutoGen>True</AutoGen>
      <DependentUpon>%(Filename)</DependentUpon>
    </Compile>
  </ItemGroup>

  <ItemGroup>
    <EmbeddedResource Remove="Properties\*.resx" />
    <EmbeddedResource Include="Properties\*.resx">
      <Generator>I18NReactive</Generator>
      <LastGenOutput>%(Filename).resx.cs</LastGenOutput>
    </EmbeddedResource>
  </ItemGroup>

@bradphelan
Copy link

I made the above comment a new issue at dotnet/project-system#2873

@andriysavin
Copy link

@adrianvmsft Hello, I'm wondering why this feature looks so xaml-centered? At the documentation page there is nothing about how to make this work with other file types. For example, I'd like to link common appsettings json files which are not linked automatically (e.g. appsettingsCommon.json and appsettingsCommon.Development.json ). How do I accomplish this with this feature?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants