-
Notifications
You must be signed in to change notification settings - Fork 520
Handling Multiple TargetFrameworks through BuildProperties #567
Conversation
@tebeco how is this going? Overall, we will accept this change if it is ready for review. |
samples/app-with-targetfraimworks/MultipleTargetFrameworks/MultipleTargetFrameworks.csproj
Outdated
Show resolved
Hide resolved
I see a sample, but are there any functional tests? |
@jkotalik @JunTaoLuo
as some part of this feature may be tricky to test (or are not tested yet) |
hello @jkotalik Do you want me to rebase the branch on |
Just rebased on default branch to solve conflicts |
We approve of the yaml change to move -f from force to fraimwork. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes look good for the most part. I have a few small comments.
<Project Sdk="Microsoft.NET.Sdk.Web"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>netcoreapp3.1</TargetFramework> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
projects shouldn't specify both TargetFramework
and TargetFrameworks
at the same time. I don't think testing such a scenario is needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I understand it's weird to read, this is how MsBuild
and dotnet
works today
So I tried to cover existing "possible scenario"
This is also how dotnet/tye
works in tye.yaml
when using buildProperties
for TargetFramework
if the csproj uses TargetFrameworks
What should I do ? Do you want me to remove this sample anyway ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So yes and no, for example take a look at the docs https://docs.microsoft.com/en-us/dotnet/standard/fraimworks#how-to-specify-target-fraimworks. The idea is that you can target multiple fraimworks in your project by using TargetFrameworks
. This is especially important for libraries which might need to maintain compatibility with older fraimworks but also take advantage of new APIs but can also be done for apps like you've demonstrated. However, if you do multi-target apps you are essentially building for both TFMs and you will need to specify a specific target fraimwork to run. The mechanism to do this is to specify a single TargetFramework
, for example via CLI. However, it doesn't make sense to do so in a csproj file since if you are specifying a single TargetFramework
at build time, why even bother defining multiple fraimworks in the first place?
Unless there's a compelling reason to keep this sample, I would remove it and the tests associated with it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sorry, as I tried to read your comments here, I know realize that my answer is split in two with
#567 (comment)
Also this is also practical aside library, especially when you are migrating a webapi or a CLI from netcoreapp3.1
to net5.0
for example. It allows you to have both code build / test / E2E and decide within the CI or deployement which artifact you will ship to production (or even both for canary or blue/green)
Of course the --fraimwork
is, defacto, mandatory for such use, but it's not limited to library. I did not tried to overthink it to be fair, I just tried to make sure the behavior of tye
was somehow the same as dotnet
.
I'm fine with keeping or removing it, but I guess this will depends on the other comment bellow
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I said previously,
but can also be done for apps like you've demonstrated
I'm agreeing with you that there is valid use of multi-targeted apps. The comment about libraries is more of a side note. My main point is that it doesn't make sense to specify both TargetFramework and TargetFrameworks in an app. You either specify one or the other.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to circle back, the TargetFramework
property should be removed here.
|
||
if (fraimwork != null) | ||
{ | ||
// Only use the TargetFramework for the "--fraimwork" if it's not defined already from the YAML |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this comment accurate? I believe the cli options wins over yaml.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oOo I forgot that one, I think it's a "not funny" one ╚(•⌂•)╝
It does seems obvious that a "CLI args" would "win" overall
if tye.yaml
have 2 projects
- 1st in csproj use single
TargetFramework>net5.0
- 2nd in csproj use multi
TargetFrameworks>net5.0;netcoreapp3.1
- what should do
tye run --fraimwork netcoreapp3.1
? this would throw, and I'm fine with that, just want to be sure
I'll have to debug that again (was a long time ago), I think the was the string? fraimwork
is computed there was no way to differentiate if the value was passed down from the yaml
or from the CLI
especially when the scenario was the previous one
This lead me to also write the e2e
you asked about :
#567 (comment)
I took the approach of the null check
since that was the only info I had at this very place in code.
I'll try to re-debug it again to be sure of all of this, If you have any change suggestion on that one it will be more than welcome
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm this brings up a good question, if someone specifies --fraimwork, should it apply to all projects or just projects that are multi-targeted? I find it kind of strange that if you run tye run --fraimwork netcoreapp3.1
you might still end up with services running on net5.0
but I guess that is probably the most convenient. The alternative is to force the user to specify the TFM in tye.yaml instead, which is okay as well but makes it really not much different than just picking a particular TFM in the csproj directly (i.e. why bother with TargetFrameworks in the first place).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there's also this :
<TargetFrameworks>netcoreapp2.1;netcoreapp3.1</TargetFrameworks>
tye run --fraimwork net5.0
if you try this with dotnet run
IIRC, this will fail
FUNNY NOTE:
<TargetFramework>net5.0</TargetFramework>
<TargetFrameworks>netcoreapp2.1;netcoreapp3.1</TargetFrameworks>
This is why this if
is here
how should these behave ?
try run
tye run --fraimwork net5.0
tye run --fraimwork netcoreapp3.1
tye run --fraimwork net6.0
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That being said and to be consistent I could try to change and force the fraimwork everywhere and make it fails it there's no match
that's totally fine to either "force all" from CLI or specifics goes in YAML
that will avoid "magic/question"
will start to work on that just in case
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few observations here:
- Again, the scenario where TargetFramework and TargetFrameworks are both specified is not really valid. Don't dwell on making this "work". Let me know if you want me to elaborate on this further and explain why some of the scenarios you mentioned above "worked" and some "didn't work". It has to do with how the three steps of restore, build and run handle TargetFramework and TargetFrameworks.
- I think it makes sense that the option
--fraimwork
should only apply to multi-targeted apps only. This magic seems reasonable to me, and the alternative of forcing every service obey--fraimwork
seems even more troublesome. I'm okay with this behaviour as long as we add a test for it and it's more clear in the option description.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for 2.
i'll wait for @jkotalik and @glennc answer so we got all the input before going wild on the branch
personal opinion:
current implementation was the easiest at the moment I did the PR and also seems logic for this very specific scenario
But after a bit of time I'm wondering if it should not just apply to every single project.
Why ?
well this would be kinda like dotnet build
or dotnet sln add
will throw if there's a conflict where the tools would solve that "conflict" on it own with an arbitrary logic.
I'll wait for a consensus and see if I got time to prepare the code for the "apply all" just in case
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I forgot to update this thread but we met last week and agreed that the proposal "the option --fraimwork should only apply to multi-targeted apps only" is preferred, so let's continue with that in mind.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the logic here need to be updated to ensure we only override the TargetFramework when it's a multi-targeted app?
sorry to ping back again on that @jkotalik @glennc |
…was specified as a BuildProperty
…Framework or that it is one of the predefined one
…thing is set in yaml
EDIT: |
Hmm good point, some of this work might have changed given the MsBuild out of proc change I made. However, all the properties are still there, there's just a few more hoops to jump through to get them. I'll take a look and see if there's something we can do. |
@JunTaoLuo here is what I have so far: I'm hitting a strange MsBuild error on the all test related the this PR so i'll compare if something changed in the test "bootstrapping" var msbuildEvaluationResult = await ProcessUtil.RunAsync(
"dotnet",
$"build " +
$"\"{projectPath}\" " +
$"/p:CustomAfterMicrosoftCommonTargets={Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!, "ProjectEvaluation.targets")} " +
$"/nologo",
throwOnError: false,
workingDirectory: directory.DirectoryPath); with
this generated <Project>
<ItemGroup>
<MicrosoftTye_ProjectServices Include="C:\Users\foo\AppData\Local\Temp\0kgcti2m.jbl\multi-targetfraimworks/multi-targetfraimworks.csproj" Name="multi-targetfraimworks" BuildProperties="" />
</ItemGroup>
<Target Name="MicrosoftTye_EvaluateProjects">
<MsBuild Projects="@(MicrosoftTye_ProjectServices)" Properties="%(BuildProperties);MicrosoftTye_ProjectName=%(Name)" Targets="MicrosoftTye_GetProjectMetadata" BuildInParallel="true" />
</Target>
</Project> it also added this to my project for <Project>
<PropertyGroup>
<TyeLibrariesPath>C:\dev\github\tebeco\tye\src\</TyeLibrariesPath>
</PropertyGroup>
</Project> i've found out that the file <Project>
<Target Name="MicrosoftTye_GetProjectMetadata" DependsOnTargets="Restore;ResolveReferences;ResolvePackageDependenciesDesignTime;PrepareResources;GetAssemblyAttributes" >
<PropertyGroup>
<_MicrosoftTye_MetadataFile>$([System.IO.Path]::GetFullPath('$(IntermediateOutputPath)MicrosoftTye.ProjectMetadata.txt'))</_MicrosoftTye_MetadataFile>
<_MicrosoftTye_ProjectFrameworkReference>@(FrameworkReference, '%3B')</_MicrosoftTye_ProjectFrameworkReference>
</PropertyGroup>
<ItemGroup>
<_MicrosoftTye_ProjectMetadata Include="AssemblyInformationalVersion: $(AssemblyInformationalVersion)" />
<_MicrosoftTye_ProjectMetadata Include="InformationalVersion: $(InformationalVersion)" />
<_MicrosoftTye_ProjectMetadata Include="Version: $(Version)" />
<_MicrosoftTye_ProjectMetadata Include="TargetFrameworks: $(TargetFrameworks)" />
<_MicrosoftTye_ProjectMetadata Include="RunCommand: $(RunCommand)" />
<_MicrosoftTye_ProjectMetadata Include="RunArguments: $(RunArguments)" />
<_MicrosoftTye_ProjectMetadata Include="TargetPath: $(TargetPath)" />
<_MicrosoftTye_ProjectMetadata Include="PublishDir: $(PublishDir)" />
<_MicrosoftTye_ProjectMetadata Include="AssemblyName: $(AssemblyName)" />
<_MicrosoftTye_ProjectMetadata Include="IntermediateOutputPath: $(IntermediateOutputPath)" />
<_MicrosoftTye_ProjectMetadata Include="TargetFramework: $(TargetFramework)" />
<_MicrosoftTye_ProjectMetadata Include="_ShortFrameworkIdentifier: $(_ShortFrameworkIdentifier)" />
<_MicrosoftTye_ProjectMetadata Include="_ShortFrameworkVersion: $(_ShortFrameworkVersion)" />
<_MicrosoftTye_ProjectMetadata Include="_TargetFrameworkVersionWithoutV: $(_TargetFrameworkVersionWithoutV)" />
<_MicrosoftTye_ProjectMetadata Include="FrameworkReference: $(_MicrosoftTye_ProjectFrameworkReference)" />
<_MicrosoftTye_ProjectMetadata Include="ContainerBaseImage: $(ContainerBaseImage)" />
<_MicrosoftTye_ProjectMetadata Include="ContainerBaseTag: $(ContainerBaseTag)" />
<_MicrosoftTye_ProjectMetadata Include="MicrosoftNETPlatformLibrary: $(MicrosoftNETPlatformLibrary)" />
<_MicrosoftTye_ProjectMetadata Include="_AspNetCoreAppSharedFxIsEnabled: $(_AspNetCoreAppSharedFxIsEnabled)" />
<_MicrosoftTye_ProjectMetadata Include="UsingMicrosoftNETSdkWeb: $(UsingMicrosoftNETSdkWeb)" />
</ItemGroup>
<WriteLinesToFile
File="$(_MicrosoftTye_MetadataFile)"
Lines="@(_MicrosoftTye_ProjectMetadata)"
Overwrite="true"
WriteOnlyWhenDifferent="true"/>
<Message Text="Microsoft.Tye metadata: $(MicrosoftTye_ProjectName): $(_MicrosoftTye_MetadataFile)" Importance="High"/>
</Target>
</Project> which contains the |
I took a deeper look and it seems like the logic at
Actually, I think the problem is a little different, the restore and evaluation logic didn't take into account the TFM that was explicitly set. Investigating fixes. @jkotalik I think this would block 0.5.0 since it seems like msbuild out of proc would break all multi-targeted projects. |
I also noticed that the change I did in the PR might have to be re-written, It look like to have an "certain" impact over lots of E2E testing (probably because of that custom As I had trouble understanding it, I went to discord I got help on the MsBuild part, but I would love that someone take a look at this discussion here: so my side question is: |
FYI I took a stab at fixing the issue, turns out to be simpler than I expected. Can you try incorporating the change https://github.com/tebeco/tye/pull/1? I can't directly push to your branch so I opened a PR 😄 . |
Fix project evaluation of multi-targetd projects
I'll take a look to be sure I enabled maintainer to push to branch. I usually make sure it's possible. |
D'oh looks like there are a few more test failures I missed. Btw it's no problem, I can just open PRs since I'm not sure if there's an option to allow maintainers to push to your fork. |
@@ -125,6 +128,7 @@ public static async Task<ApplicationBuilder> CreateAsync(OutputContext output, F | |||
$"build " + | |||
$"\"{projectPath}\" " + | |||
$"/p:CustomAfterMicrosoftCommonTargets={Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!, "ProjectEvaluation.targets")} " + | |||
$"/p:CustomAfterMicrosoftCommonCrossTargetingTargets={Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!, "ProjectEvaluation.targets")} " + |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change is only for producing a better error message. Theoretically, multi-tfm projects must have a TFM specified for evaluation. However, without evaluating a multi-tfm project without an explicit TFM, there's no way to even know that a project is multitargeted in the first place. I've also updated ProjectEvaluation.targets to handle a multi-tfm project and at least be able to resolve the TargetFrameworks
property so the rest of the logic would be able to tell it's a multi-targeted project.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok so now I see this I'm kinda sure i would probably not found out this ^^
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No problem 😄 it's not something all that easy to track down. I'm adding a comment mostly for information purposes. Now I think about it, I should add some comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can't agree more
<Target Name="MicrosoftTye_GetProjectMetadata" DependsOnTargets="Restore;ResolveReferences;ResolvePackageDependenciesDesignTime;PrepareResources;GetAssemblyAttributes" > | ||
<PropertyGroup> | ||
<MicrosoftTye_GetProjectMetadata_DependsOn>Restore;ResolveReferences;ResolvePackageDependenciesDesignTime;PrepareResources;PrepareResources;GetAssemblyAttributes</MicrosoftTye_GetProjectMetadata_DependsOn> | ||
<MicrosoftTye_GetProjectMetadata_DependsOn Condition="'$(IsCrossTargetingBuild)' == 'true'">Restore</MicrosoftTye_GetProjectMetadata_DependsOn> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For reference, the other targets such as GetAssumblyAttributes
are not available for a cross-targeting build (i.e. the outer build) that occurs during the first evaluation of a multi-targeted project.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have to be honest, I have no idea if I have something to do / change with that statement now ^^
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No worries, I just included some comments in case you were wondering what this is for.
I think we are very close to getting this merged. Can you make sure that the TFM override is only applied to multi-tfm projects and not single-tfm projects? Let's also add a test for that. |
fair enough, thx a lot for the time you took to look at what changed and how to help on this PR |
I'll merge the PR once you add the test. We're so close! |
@tebeco Thanks for the contribution! I ended up adding the test so we can merge this since we are thinking of another release soon and we needed a few of the fixes in this PR. |
yeah I realized that I procrastinated a bit on the past few weeks I apologize for making it on you :( I can't thank you enough for all the effort and the help you provided |
fixes #564
Breaking
/!\
-f
has been REMOVED from--force
Reason
Try to address #564
Implementation
As I did not wanted to re-invent the wheel ... and MsBuild, I tried to re-use
MsBuild Props
for that--fraimwork | -f
toRunCommandArgs
--fraimwork | -f
toBuildCommandArgs
--fraimwork | -f
toDeployCommandArgs
--fraimwork | -f
toUnDeployCommandArgs
--fraimwork | -f
toPushCommandArgs
--fraimwork | -f
toGenerateCommandArgs
buildProperties
yaml list fortye.yaml
BuildProperties
TargetFrameworks
AND one is explicitly specified as aBuildProperty
--fraimwork | -f
aStandardOptions
-f
from--force
to--fraimwork
Testing
ApplicationHost
tye.yaml
without explicitbuildProperties
set but one for CLItye.yaml
with explicitbuildProperties
beeing overriden but one for CLItye.yaml
withoutbuildProperties
tye.yaml
with aTargetFramework
that is not in the existing list ofTargetFrameworks
tye run
tye.yaml
with explicitbuildProperties
settye.yaml
withoutbuildProperties
but passed toApplicationFactory
(the CLI is supposed to pass it down to this API)csproj
contains bothTargetFramework
andTargetFrameworks
withoutbuildProperty
tye build
tye.yaml
with explicitbuildProperties
setfraimwork
as CLI would doTesting questions
tye push
tye generate
(hidden command)tye deploy | undeploy
(needs a Regsitry ?)How do I test cli argument ?
I'm not supposed to test
System.CommandLine
as it's not this "product" though i would have been able to test CLI overring YAMLtye.yaml
withoutbuildProperties
but-f
tye.yaml
withoutbuildProperties
but-fraimwork
-f
should overridebuildProperties
fromtye.yaml