Search code examples
msbuildless

How to get MSBuild target to run whenever an input file changes?


Hi I'm pretty new to MSBuild so any feedback at all really helps. I'm trying to make a build step that compiles any *.less files to work with Blazor. I previously used WebCompiler but it doesn't work on non-windows machines.

I've created a less build task as follows:

<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>

I then use it in my project file like so:

    <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" />

I find that with this I have to rebuild every time I'd like my less to recompile. Is there a way I can get the compiler to run for any input file changes, without requiring a build?


Solution

  • Whenever MSBuild is called to build a project, it does so and exits when it's done. Although an instance may remain in the background for caching, it is no longer acquainted with the project and will not react to any changes unless another build is requested. Therefore, MSBuild doesn't support watching files then rebuilding out of the box.

    I suggest using existing tooling or writing a short script which watches the files and invokes MSBuild when they change. If possible, add a cooldown because builds are usually not free and could slow down your IDE if they're requested too often.