dotnet / msbuild

The Microsoft Build Engine (MSBuild) is the build platform for .NET and Visual Studio.
https://docs.microsoft.com/visualstudio/msbuild/msbuild
MIT License
5.17k stars 1.34k forks source link

Add scoping and information hiding for msbuild variables (properties and items) #2480

Open richardszalay opened 6 years ago

richardszalay commented 6 years ago

I'd like to start a conversation around the potential for PropertyGroups / ItemGroups that are scoped to a local target.

MSBuild scripts, written by Microsoft or not, end up using overly descriptive names for properties and items to avoid clashing with those created by other targets and introducing "undefined behavior".

The proposed feature would introduce the ability to define a property/item group as "Scope=Target". Values defined in these blocks would be added to a scope that is local to the target. Variable resolution would always prefer the target scope, if it is available.

<PropertyGroup Scope="Target">
</PropertyGroup>

This scope would be truly local - target dependency chains wouldn't be able to see each others' target-scoped variables.

Disclaimer: While I'm reasonably familiar with consuming MSBuild as a developer, as well as the web publishing tasks developed by Microsoft, I'm not at all familiar with the MSBuild source code. There might be some very good architectural reasons why this proposal isn't possible or feasible.

Outstanding questions:

erwinbonsma commented 4 years ago

I am in favour of such a feature. In our custom MSBuild scripts we use poor man's scoping for item groups. In particular:

This, however, is far from ideal. The explicit remove statements:

Scoping would nicely address this. A second-best solution would be a convenient syntax for emptying an item group, but the latter is obviously inferior.

cdmihai commented 4 years ago

I see three potential privacy scopes:

If we ever do this, it would good to actually formalize it in terms of established scoping theory (lexical vs dynamic, etc).

rainersigwald commented 4 years ago

A problem with "project level" is the common pattern of having related .props and .targets files. It might be reasonable to want to have an access level that would cover both.

LostTime76 commented 4 months ago

I don't find this whole msbuild properties are basically all global thing to be very sane especially since it came and smacked me in the face just now. I am trying to author multiple build task libraries in different repos with .targets and .props files. Multiple .props files would have the same property names, otherwise I would have to somehow make them unique across the world. I had thought that the properties would get evaluated then and there and be useable in the UsingTask declarations; however, UsingTask declarations are evaluated in a subsequent pass after all properties are evaluated. That means whatever .props file set the property last, is the one that wins. That obviously screws up other .props files trying to declare UsingTask statements.

What exactly do you expect me to do here? Hardcode 'magic constants' into each UsingTask statement so that properties are not used? That means if those constants change, I have to update a billion UsingTasks. I had considered this as a necessary evil; however, I actually have several things that must be properties, because they are built based on the path of the .props file and then used in the UsingTask decalarations. I can't hardcode constants there.

It would not be the end of the world if UsingTasks got evaluated as they were declared after the properties as the properties would just get used and then subsequent .props files could just redefine the properties.

Is it honestly expected for me to declare all my property values as ThisVeryLongNamespace_AssemblyTasksPath to try and make them unique?

rainersigwald commented 4 months ago

Is it honestly expected for me to declare all my property values as ThisVeryLongNamespace_AssemblyTasksPath to try and make them unique?

Yes, that's the best option available today. It doesn't usually have to be that long, most prefixes are pretty short. For example, NuGet uses $(RestoreTaskAssemblyFile) for the purpose you're describing.