.net-coremsbuildhuskymsbuild-targetdotnet-tool

Running props commands ONCE when solution is opened


I have a nuget package with a props file, and in the props file I have these commands:

    <Target Name="_myRunCommands" BeforeTargets="Rebuild;Build">
        <Message Text="$(MyMessagePrefix) Tools install started" Importance="high" />
        <!-- restore will install / restore the packages if missing, but not update them -->
        <Exec Command="dotnet tool restore" StandardOutputImportance="High" StandardErrorImportance="High" />
        <Exec Command="dotnet tool update husky" StandardOutputImportance="High" StandardErrorImportance="High" />
        <Exec Command="dotnet tool update csharpier" StandardOutputImportance="High" StandardErrorImportance="High" />
        <Exec Command="dotnet husky install"  WorkingDirectory="$(MySolutionDir)" />
    </Target>

However, I would like for these commands to only be run ONCE when the solution is opened or changed. Not every time the build is run, or multiple times for the same solution.

QUESTION: Is there a way to run these commands just once when the solution is opened? I looked all through the targets and could find nothing that would work. Does anyone have a solution for this?

CLARIFICATION: I don't care what target I run against, if the target is run multiple times, I would like my target to only execute once.


Solution

  • First note that MSBuild is for specifying build steps. Running a target when a file is opened (by Visual Studio? VSCode? Rider? Notepad?) is outside of MSBuild. But I would suggest that your target is fine as a build step.

    Also note that a solution can build projects but projects by design have extremely limited information about the solution. More to the point, you can't inject a target into the solution's build of projects from within a project.

    A project may have a ProjectReference to your package. That ProjectReference is specific to that project. The .props file in the package will be imported into the project and the _myRunCommands target will be run. Each project that references the package will run the _myRunCommands target. This is great for sharing/reusing custom targets but not at all the behavior you are looking for.

    Custom Solution

    There is a mechanism for customizing the solution.

    For MySolution.sln, if there are files named before.MySolution.sln.targets and/or after.MySolution.sln.targets, they will be auto-imported and used by the solution. However, this only works for command line builds. When building from Visual Studio these files will be ignored.

    (That command line builds and Visual Studio builds have differences is awful but that is a whole other subject.)

    Utility Project

    If you need parity between cli and VStudio builds, another approach is to create a utility project. Within a given solution build, the utility project, like any project, will be built once.

    You can use the Microsoft.Build.NoTargets Project SDK for the utility project.

    Be sure to make the utility project a dependency of projects that need the utility project built first. Dependencies can be set in the solution file. In Visual Studio in the solution explorer, you can right-click on the solution and choose "Project Dependencies".

    Run When the Solution is Changed

    To limit the _myRunCommands target of the utility project from building on every solution build, you can specify inputs and outputs.

    As part of the _myRunCommands target, use the Touch task to create a file or update the timestamp if the file exists. You can name the file whatever you like, e.g. lastrun.txt. This file is your "output". Your .sln file is your "input". If the .sln file has changed and has a newer timestamp then lastrun.txt, the target will be run to update the lastrun.txt file. Otherwise the target won't be run.