You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让MSBuild目标在输入文件变更时自动运行?

问题描述

刚接触MSBuild,想实现Blazor项目中*.less文件的编译步骤,之前用WebCompiler但它无法在非Windows机器上运行。

已创建如下Less构建任务(LessCompile.targets):

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

    <Target Name="RazorLessCompile" BeforeTargets="ResolveScopedCssInputs" Inputs="@(RazorLessFile)" Outputs="@(RazorLessFile->'%(RelativeDir)%(Filename).css')">

        <Exec ContinueOnError="true" IgnoreExitCode="true" Command='npx lessc %(RazorLessFile.Identity) %(RazorLessFile.RelativeDir)%(RazorLessFile.Filename).css --autoprefix="defaults"' />

        <!--
        This is required for compiled css to be picked up by Razor.ScopedCss.targets 
        Without this, we will get the build error 'Could not find a part of the path '**\Debug\net5.0\scopedcss\projectbundle'.'
        -->
        <ItemGroup>
            <None Remove="%(RazorLessFile.RelativeDir)%(RazorLessFile.Filename).css" />
            <None Include="%(RazorLessFile.RelativeDir)%(RazorLessFile.Filename).css" />
        </ItemGroup>
    </Target>

    <Target Name="LessCompile" BeforeTargets="ResolveStaticWebAssetsConfiguration;ResolveScopedCssInputs" Inputs="@(LessFile)" Outputs="@(LessFile->'%(CompileTo)')">
        <Exec ContinueOnError="true" IgnoreExitCode="true" Command='npx lessc %(LessFile.Identity) %(CompileTo) --autoprefix="defaults"' />
    </Target>

    <Target Name="AddStaticAssets" DependsOnTargets="LessCompile" BeforeTargets="ResolveStaticWebAssetsConfiguration;ResolveScopedCssInputs">
        <ItemGroup>
            <Content Remove="wwwroot/**" />
            <Content Include="wwwroot/**" />
        </ItemGroup>
    </Target>
</Project>

在项目文件中引用的方式:

<ItemGroup>
    <RazorLessFile Include="**/*.razor.less" />     
    <LessFile Include="Styles/app.less">
        <CompileTo>wwwroot/css/app.css</CompileTo>
    </LessFile>
    <LessFile Include="Styles/breezer-overrides.less">
        <CompileTo>wwwroot/css/breezer-overrides.css</CompileTo>
    </LessFile>
</ItemGroup>

<Import Project="..\..\LessCompile.targets" />

现在遇到的问题:每次修改Less文件后,必须执行重建操作才会重新编译,希望实现输入文件变更时自动触发编译,无需手动执行重建。


解决方案

要实现MSBuild的增量构建(文件变更时自动触发编译),可以从以下几个方面调整配置:

1. 完善输入输出的关联追踪

当前的编译目标虽指定了Inputs和Outputs,但可以添加Returns属性让MSBuild更清晰地追踪输出文件状态,同时确保每个输入文件对应唯一输出:

<Target Name="RazorLessCompile" 
        BeforeTargets="ResolveScopedCssInputs" 
        Inputs="@(RazorLessFile)" 
        Outputs="@(RazorLessFile->'%(RelativeDir)%(Filename).css')"
        Returns="@(RazorLessFile->'%(RelativeDir)%(Filename).css')">
    <!-- 原编译逻辑不变 -->
</Target>

<Target Name="LessCompile" 
        BeforeTargets="ResolveStaticWebAssetsConfiguration;ResolveScopedCssInputs" 
        Inputs="@(LessFile)" 
        Outputs="@(LessFile->'%(CompileTo)')"
        Returns="@(LessFile->'%(CompileTo)')">
    <!-- 原编译逻辑不变 -->
</Target>

2. 处理Less文件的导入依赖(关键)

如果你的Less文件用@import引用了其他.less文件,MSBuild默认不会识别这些依赖,导致修改导入文件时不会触发编译。需要添加步骤收集依赖:

在LessCompile.targets中新增收集依赖的目标:

<Target Name="CollectLessDependencies" BeforeTargets="RazorLessCompile;LessCompile">
    <!-- 收集RazorLessFile的依赖 -->
    <Exec Command='npx lessc --list-dependencies %(RazorLessFile.Identity)' 
          Outputs="%(RazorLessFile.Identity).deps"
          WriteLinesToFile="%(RazorLessFile.Identity).deps"
          Lines="$(_ExecCommandOutput)" />
    <ReadLinesFromFile File="%(RazorLessFile.Identity).deps">
        <Output TaskParameter="Lines" ItemName="RazorLessDependency" />
    </ReadLinesFromFile>
    <ItemGroup>
        <RazorLessFile Remove="%(RazorLessFile.Identity)" />
        <RazorLessFile Include="%(RazorLessFile.Identity)">
            <Dependencies>@(RazorLessDependency)</Dependencies>
        </RazorLessFile>
    </ItemGroup>

    <!-- 收集LessFile的依赖 -->
    <Exec Command='npx lessc --list-dependencies %(LessFile.Identity)' 
          Outputs="%(LessFile.Identity).deps"
          WriteLinesToFile="%(LessFile.Identity).deps"
          Lines="$(_ExecCommandOutput)" />
    <ReadLinesFromFile File="%(LessFile.Identity).deps">
        <Output TaskParameter="Lines" ItemName="LessDependency" />
    </ReadLinesFromFile>
    <ItemGroup>
        <LessFile Remove="%(LessFile.Identity)" />
        <LessFile Include="%(LessFile.Identity)">
            <Dependencies>@(LessDependency)</Dependencies>
        </LessFile>
    </ItemGroup>
</Target>

然后修改两个编译目标的Inputs,把依赖文件加入监控范围:

<Target Name="RazorLessCompile" 
        BeforeTargets="ResolveScopedCssInputs" 
        Inputs="@(RazorLessFile);@(RazorLessFile->'%(Dependencies)')" 
        Outputs="@(RazorLessFile->'%(RelativeDir)%(Filename).css')">
    <!-- 原编译逻辑不变 -->
</Target>

<Target Name="LessCompile" 
        BeforeTargets="ResolveStaticWebAssetsConfiguration;ResolveScopedCssInputs" 
        Inputs="@(LessFile);@(LessFile->'%(Dependencies)')" 
        Outputs="@(LessFile->'%(CompileTo)')">
    <!-- 原编译逻辑不变 -->
</Target>

3. 移除错误忽略配置

当前Exec任务设置了ContinueOnError="true"和IgnoreExitCode="true",会导致编译失败时MSBuild仍认为任务成功,无法及时反馈问题。建议移除这两个属性:

<Exec Command='npx lessc %(RazorLessFile.Identity) %(RazorLessFile.RelativeDir)%(RazorLessFile.Filename).css --autoprefix="defaults"' />

4. 优化静态资源添加逻辑

原AddStaticAssets目标每次都会重新包含wwwroot/**,会干扰增量构建。修改为只添加编译生成的CSS文件:

<Target Name="AddStaticAssets" DependsOnTargets="LessCompile;RazorLessCompile" BeforeTargets="ResolveStaticWebAssetsConfiguration;ResolveScopedCssInputs">
    <ItemGroup>
        <!-- 添加编译后的全局CSS -->
        <Content Include="@(LessFile->'%(CompileTo)')" />
        <!-- 添加编译后的Razor作用域CSS -->
        <Content Include="@(RazorLessFile->'%(RelativeDir)%(Filename).css')" />
    </ItemGroup>
</Target>

完成以上调整后,修改任意Less文件(包括导入的依赖文件),执行普通构建而非重建,MSBuild会自动触发编译。


内容的提问来源于stack exchange,提问作者Alexander Blyth

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 21:05:06