嵌入字体会强制Silverlight项目始终重建

时间:2010-09-22 00:09:48

标签: silverlight fonts

我的Silverlight 4项目遇到了一个奇怪的问题,尽管这是我之前见过的。基本上,嵌入字体会强制我的Silverlight应用程序始终重建,即使所有内容都是最新的。由于字体嵌入任务占用大量内存并最终会导致VS崩溃,这种情况很糟糕。我希望能够从命令行构建项目,但无论我的本地项目是什么都已过时,因此“运行”命令会强制进行另一次重建。我试图从我的msbuild日志中删除一些相关的日志信息。

Project "D:\Projects\Test\Test.Web\Test.Web.csproj" (10) is building "D:\Projects\Test\Test.SL\Test.SL.csproj" (2:4) on node 1 (default targets).
Building with tools version "4.0".
// Build operation starts normally (well, the dependency set on the server project is forcing the SL application to build). 
...
Target "ResolveReferences" skipped. Previously built successfully.
// A bunch of tasks are skipped (like this one)
...
Target "SubsetFontsSilverlight" in file "C:\Program Files\MSBuild\Microsoft\Expression\Blend\Silverlight\v4.0\SubsetFontSilverlight.targets" from project "D:\Projects\Test\Test.SL\Test.SL.csproj" (target "PrepareResources" depends on it):
Using "SubsetFontsSilverlight" task from assembly "C:\Program Files\MSBuild\Microsoft\Expression\Blend\Silverlight\v4.0\SubsetFontTask.dll".
Task "SubsetFontsSilverlight"
Done executing task "SubsetFontsSilverlight".
Done building target "SubsetFontsSilverlight" in project "Test.SL.csproj".
// this task never gets skipped
...
Target "MainResourcesGeneration" in file "C:\Program Files\MSBuild\Microsoft\Silverlight\v4.0\Microsoft.Silverlight.Common.targets" from project "D:\Projects\Test\Test.SL\Test.SL.csproj" (target "PrepareResources" depends on it):
Building target "MainResourcesGeneration" completely.
Input file "obj\Debug\Fonts\Fonts.zip" is newer than output file "obj\Debug\Test.SL.g.resources".
// note that the Fonts.zip file now makes the resources file out of date
...
Target "CoreCompile" in file "C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\Microsoft.CSharp.Targets" from project "D:\Projects\Test\Test.SL\Test.SL.csproj" (target "Compile" depends on it):
Building target "CoreCompile" completely.
Input file "obj\Debug\Test.SL.g.resources" is newer than output file "obj\Debug\Test.SL.pdb".
// and the full recompile begins...

无论如何,如果字体文件没有改变,有没有办法阻止字体任务运行?因为不断的重建真的很烦人。

更新:项目文件中的示例字体。

<BlendEmbeddedFont Include="Fonts\MyriadPro-BoldIt.otf">
  <IsSystemFont>True</IsSystemFont>
  <All>True</All>
  <AutoFill>True</AutoFill>
  <Characters>
  </Characters>
  <Uppercase>True</Uppercase>
  <Lowercase>True</Lowercase>
  <Numbers>True</Numbers>
  <Punctuation>True</Punctuation>
</BlendEmbeddedFont>

更新2:

我想发布一个小的repro项目,但我不知道在哪里折腾zip文件。无论如何,可以在几秒钟内将其掀起。以下是步骤:

  1. 创建Sillverlight应用程序(混合或VS,但无论如何你需要Blend才能工作)

  2. 使用Blend中的字体管理器(工具 - &gt;字体管理器),您可能必须打开MainPage.xaml才能启用它。嵌入“Tahoma”字体。

  3. 现在,每次点击“Rebuild”,您都可以验证csc.exe每次都运行,无论是否有任何更改。

2 个答案:

答案 0 :(得分:2)

我通过乱砍我的SubsetFontSilverlight.target文件解决了这个问题。这不是一个完整的修复,它不一定适用于每个人,但它适用于我。

目标文件可以在C:\ Program Files \ MSBuild \ Microsoft \ Expression \ Blend \ Silverlight \ v4.0中找到,至少在我的机器上。

这里的主要思想是使用MSBUILD目标输入/输出属性来查看是否需要更新中间Font.zip文件。我简单地硬编码了输出文件路径,因为我总是使用默认的Blend字体位置 - 但是可以用各个字体文件构建它。

我设置的条件是,如果Font.zip文件比嵌入字体文件和项目文件更新,则应跳过该任务。这样,如果您添加/减去一个字体或替换其中一个字体,字体将重新嵌入,但如果没有更改(或只是代码更改),font.zip将保持不变。

如果没有嵌入字体,那么手动将fonts.zip添加到资源集合中非常重要 - 如果代码需要重新编译但字体可能单独存在,那么msbuild会记住将字体文件包含在资源中。这就是为什么我在主嵌入任务之后添加了一个新任务。

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <UsingTask AssemblyFile="SubsetFontTask.dll" TaskName="Microsoft.Expression.SubsetFontTask.SubsetFontsSilverlight" />
    <UsingTask AssemblyFile="SubsetFontTask.dll" TaskName="Microsoft.Expression.SubsetFontTask.CleanSubsetFonts" />

    <ItemGroup Condition="'$(BuildingInsideVisualStudio)'=='true'">
        <AvailableItemName Include="BlendEmbeddedFont"/>
    </ItemGroup>

  <ItemGroup>
    <!-- Files that we specify as target inputs for msbuild target timestamp check -->
    <FontEmbedRelatedFile Include="$(MSBuildProjectFile)" />
    <FontEmbedRelatedFile Include="@(BlendEmbeddedFont)" />

    <!-- Anticipated font file locations -->
    <FontOutputFile Include="$(IntermediateOutputPath)\Fonts\Fonts.zip" />
  </ItemGroup>

  <!-- 
    this task runs after the main subset task - basically it checks if the task got skipped, and if so
    it adds the projected font output files to the resources collection
   -->
  <Target Name='AfterFontEmbed' AfterTargets="SubsetFontsSilverlight"
    Condition="'@(BlendEmbeddedFont)' != '' AND '@(BlendSubsettedFont)' == ''">
    <Message Text="Adding font files to the resource collection because Font embed was skipped" Importance="normal" />
    <ItemGroup>
      <Resource Include="@(FontOutputFile)" />
    </ItemGroup>
  </Target>

    <Target Name='SubsetFontsSilverlight'  
    Inputs="@(FontEmbedRelatedFile)" Outputs="@(FontOutputFile)"
    DependsOnTargets="$(SubsetFontsDependsOn)"  
    Condition="'@(BlendEmbeddedFont)' != ''">
    <Message Text="Embedding font subsets" Importance="normal" />
        <SubsetFontsSilverlight 
            Fonts="@(BlendEmbeddedFont)" XamlFiles="@(Page)" Resources="@(Resources)"
            IntermediateFilesDirectory="$(IntermediateOutputPath)"
            >
            <Output TaskParameter="SubsettedFonts" ItemName="Resource"/>
            <!-- save our list of embedded font files for later use -->
            <Output TaskParameter="SubsettedFonts" ItemName="BlendSubsettedFont"/>
        </SubsetFontsSilverlight>
    </Target>

    <Target Name='CleanSubsetFonts' DependsOnTargets="$(CleanSubsetFontsDependsOn)" Condition="'@(BlendEmbeddedFont)' != ''">
        <CleanSubsetFonts 
            Fonts="@(BlendEmbeddedFont)"
            IntermediateFilesDirectory="$(IntermediateOutputPath)"
            />
    </Target>

    <PropertyGroup>
        <PrepareResourcesDependsOn>
            SubsetFontsSilverlight;
            $(PrepareResourcesDependsOn);
        </PrepareResourcesDependsOn>
    </PropertyGroup>

    <PropertyGroup>
        <CleanDependsOn>
            $(CleanDependsOn);
            CleanSubsetFonts;
        </CleanDependsOn>
    </PropertyGroup>

</Project>

Visual Studio仍然不是很注意项目是最新的,但是当它构建它们时,核心编译任务不会每次都运行,并且dll文件不会被不必要地覆盖。这意味着您可以在命令行上运行编译器,在调试时将VS设置为“Never Build”,并且仍然加载了正确的调试符号。

我在大多数我能想到的情况(干净之前/之后,缺少文件,仅代码更改,项目更改等)测试了这一点,它似乎做得很好 - 意味着字体在那里,一切都没有永远不会重新编译。

答案 1 :(得分:1)

我一直在使用Egor的答案,这确实有助于减少我们的构建时间,特别是在重建时。但是,最近的更改破坏了一些东西 - 我们将字体从项目文件夹中的\ Fonts移动到\ Assets \ Fonts。目标文件试图对obj \ Debug \ Fonts \ Fonts.zip进行最新检查,但任务是创建obj \ Debug \ Assets \ Fonts \ Fonts.zip。

我修改了目标文件,以检查中间输出路径中相应相关目录下的Fonts.zip文件。此目标文件将与包含在不同文件夹中的多种字体一起使用。

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <!-- The Blend 4 SDK will always rebuild a xap that has font embedding, rather than
       playing nice and doing up-to-date checks. This targets file replaces the default
       target from the Blend SDK with one that does.
       It checks the input font file and the project file against the fonts.zip file.
       The project file is checked so that any added or removed fonts are picked up -->

  <ItemGroup>
    <!-- Files that we specify as target inputs for msbuild target timestamp check -->
    <FontEmbedRelatedFile Include="$(MSBuildProjectFile)" />
    <FontEmbedRelatedFile Include="@(BlendEmbeddedFont)" />

    <FontOutputFiles Include="@(BlendEmbeddedFont -> '$(IntermediateOutputPath)%(RelativeDir)Fonts.zip')" />
  </ItemGroup>

  <!-- This task runs after the main subset task - basically it checks if the task got skipped, and if so
       it adds the projected font output files to the resources collection -->
  <Target Name='AfterFontEmbed' AfterTargets="SubsetFontsSilverlight"
          Condition="'@(BlendEmbeddedFont)' != '' AND '@(_BlendSubsettedFont)' == ''">
    <ItemGroup>
      <Resource Include="@(FontOutputFiles)" />
    </ItemGroup>
  </Target>

  <Target Name='SubsetFontsSilverlight'
          Inputs="@(FontEmbedRelatedFile)" Outputs="@(FontOutputFiles -> Distinct())"
          DependsOnTargets="$(SubsetFontsDependsOn)" Condition="'@(BlendEmbeddedFont)' != ''">
    <SubsetFontsSilverlight Fonts="@(BlendEmbeddedFont)" 
                            XamlFiles="@(Page)" Resources="@(Resources)" 
                            IntermediateFilesDirectory="$(IntermediateOutputPath)">
      <Output TaskParameter="SubsettedFonts" ItemName="Resource"/>
      <Output TaskParameter="SubsettedFonts" ItemName="_BlendSubsettedFont"/>
    </SubsetFontsSilverlight>
  </Target>
</Project>