TLDR: dotnet mytarget could trigger dotnet msbuild /t:mytarget
Let's say I have a custom target in my project file: <Target Name="mytarget" ...
To execute this I can run dotnet msbuild /t:mytarget which is rather verbose.
Now that MSBuild is an integral part of the dotnet CLI, why not have a command resolver that checks for custom targets? We could add this to the end of the current list of resolvers so as not to break existing resolution strategies.
This would make it possible to type dotnet mytarget instead of dotnet msbuild /t:mytarget and any arguments I pass could be forwarded to msbuild just like dotnet build does.
This would simplify tool development to just needing a NuGet package with MSBuild targets and getting dotnet mytarget for free. We'd no longer need a console app that wraps the call to dotnet msbuild. This is a pattern discussed in the extensibility docs:
However, if you want to provide a better user experience, you can combine per-project tools and custom targets. In this scenario, the per-project tool would essentially just accept whatever needed parameters and would translate that into the required dotnet msbuild invocation that would execute the target. You can see a sample of this kind of synergy on the MVP Summit 2016 Hackathon samples repo in the dotnet-packer project.
-- https://github.com/dotnet/docs/commit/049bc39d50f149b5ffb173f157ff6ff47182477b#diff-f3b0783a35a3fc75fe4e57558a221194R154
This could be opt-in/out perhaps by setting an attribute on the custom target(s). Especially if at some point there will be a command to list custom tools. i.e.: <Target Name="MyTool" ExtendDotNetCli="true" />
Perhaps also alias the sub command while we're at it: <Target Name=MyTool" ExtendDotNetCliAlias="mytoolalias" /> which would map the target to dotnet mytoolalias instead of dotnet mytool.
I really like the idea, writing a cli tool that only invokes an msbuild target seems a lot of unnecessary work (and fining the right dotnet/msbuild version in the tool isn't straightforward either). (Related: this SO question)
I don't think MSBuild allows for arbitrary attributes on <Target> elements, but maybe a simple convention could help here:
<Target Name="FancyDotNetCliVerbTarget">...</Target>
=> dotnet fancy
Additional arguments could be made available through a property like $(DotNetCliVerbArguments), maybe some "well-known" arguments (--configuration, --framework) could be parsed as well.
We could mirror this after the behavior of yarn run https://yarnpkg.com/en/docs/cli/run.
yarn run lists scriptsdotnet targets or dotnet target list which by default lists only custom command targets based on whatever filter we derive. We could even add an --all flag to list all targets. Maybe this should be a part of dotnet msbuild? I don't mind how this is implemented so long as the functionality exists. This will simplify looking through *.proj files and PackageReferences to discover custom command targets.dotnet commands or dotnet command listdotnet help with --commands or --targets or otherwise.yarn run X and yarn X both run X, unless X is a reserved sub-command in which case you can only use yarn run Xyarn X where it doesn't collide with built-in and/or reserved sub-commands. This would map to dotnet X as described above. dotnet msbuild /t:X that provides a slightly more verbose version of yarn run X which is sufficient if a custom target collides with a reserved sub-command. In terms of discoverability in NuGet packages, maybe we need a dedicated packageType for calling out that a package has custom command targets? Much like DotnetCliTool we could have DotnetTargetTool or whatever.
while this is good to have, i'll add some questions:
dotnet fancy work in subdirecotry of same repo? or like .net cli tools just in same dir of repo?/p:Version=1.2.3 is really strange, more so when mixed dotnet pack -c Release /p:Version=1.2.3how pass cli arguments? force to msbuild args sintax like now /p:Version=1.2.3 is really strange, more so when mixed dotnet pack -c Release /p:Version=1.2.3
I'd say mimic npm run behavior and use -- to indicate that the rest of the arguments should be passed along and not parsed by the dotnet CLI, or we could just pass all arguments to the command and/or map to MSBuild properties as that makes sense. Skys the limit, I believe. I agree mixing syntax is confusing and should be avoided.
what msbuild project to load? if multiple, like now using .net cli tools, just fail?
can dotnet fancy work in subdirecotry of same repo? or like .net cli tools just in same dir of repo?
I assume we can preserve existing behavior for running custom commands.
Perhaps we could add something on the solution level to run commands across projects?
how restore deps? a target is nice, but without task is not that useful. and put lot of logic in .target is difficult
when restore deps? you cannot use same restored targets in the current execution of msbuild, because targets are already imported.
I assume you are talking about dependencies of a custom command? If so, restoring packages could work the same way as when DotnetCliToolReferences is used with a wrapper command to just invoke an MSBuild target. It all happens transparently as a part of dotnet restore so that again this isn't the concern of a person that just wants to use dotnet whatever ... and have it invoke an MSBuild target. Really I think this is all about a different syntax for invoking msbuild targets as we can already call dotnet msbuild directly, but that's rather verbose and as you pointed out, mixing arguments is confusing.
If you add a target from a PackageReference, then of course any dependencies of that package would implicitly be restored. As happens already with custom targets.
a target is nice, but without task is not that useful
I beg to differ on that.
Granted, getting msbuild tasks to work correctly with dependencies on core and desktop msbuild is quite complicated. If you need to distribute complex logic, you are probably better off creating a standard cli tool.
But for everything I can do purely in msbuild, creating an additional cli tool that just calls it feels like a lot of overhead.
For example, I have a PublishAll target that invokes publish for all target frameworks and runtime identifiers specified in the project. If there was a simple way to access it via dotnet publish-all vs. dotnet msbuild /t:PublishAll that would be cool.
Similarly, a ci build script that publishes multiple configurations to a directory hierarchy by creating publish profiles on the fly. (though this one was built for classic web csprojs, I see no problem in doing something similar for asp.net core projects).
The added benefit is that the target is also directly available in msbuild so I can invoke it from ci scripts. Otherwise, this would have to be done via <Exec Command="dotnet foo" /> and praying that the version of dotnet this resolves to works. (there current host / resolved sdk isn't yet available as msbuild property).
Most helpful comment
I really like the idea, writing a cli tool that only invokes an msbuild target seems a lot of unnecessary work (and fining the right dotnet/msbuild version in the tool isn't straightforward either). (Related: this SO question)
I don't think MSBuild allows for arbitrary attributes on
<Target>elements, but maybe a simple convention could help here:=>
dotnet fancyAdditional arguments could be made available through a property like
$(DotNetCliVerbArguments), maybe some "well-known" arguments (--configuration,--framework) could be parsed as well.