-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Generate shim in build #2413
Generate shim in build #2413
Conversation
|
||
namespace Microsoft.NET.Build.Tasks | ||
{ | ||
public sealed class GetEmbeddedApphostPaths : TaskBase |
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.
These code is copied out of GenerateShim, However, there is only around 10 lines are duplicated. And there is no critical code that will need to keep in sync. If we want to share these code, the overhead will be more than 10 lines.
--> | ||
|
||
<Target Name="GenerateShimsAssets" | ||
AfterTargets="ResolveReferences" |
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.
Using AfterTargets is a good way to integrate into build? I need to be before IncrementalClean
, so the file can be "registered"
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.
AfterTargets
is fine, with the caveat that you shouldn't do AfterTargets="Build"
because of dotnet/msbuild#3345. But I generally prefer to use BeforeTargets
--can you BeforeTargets="IncrementalClean"
?
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.
@rainersigwald thanks, although the task itself depends on ResolveReferences
. Should I just dependson =ResolveReferences
. and BeforeTargets="IncrementalClean"
. But I think use dependson with before/after target is kind of strange.
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.
It's reasonable to have both BeforeTargets="IncrementalClean" DependsOnTargets="ResolveReferences"
. I'd go with that.
It is funny looking, but they're different relationships: BeforeTargets means "before you run that, run me first", while DependsOn means "before I run, run these first". Both can be true at once.
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 would expect the logic for this target to be the same as GenerateBuildDependencyFile. We went through a few iterations on that and ended up against beforetargets=incrementalclean IIRC. It would be good to have one pattern for extra files we create.
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 see, I read the comment. It makes sense. Now the problem is, how to set the timing(before or after what?) I think BeforeTargets="CopyFilesToOutputDirectory"
is reasonable.
Changed to the following, I will push later, I don't want to hide the thread.
<Target Name="GenerateShimsAssets"
BeforeTargets="CopyFilesToOutputDirectory"
DependsOnTargets="ResolveReferences"
Condition="'$(PackAsTool)' == 'true'" >
<ItemGroup>
<_PackAsToolShimRuntimeIdentifiers Include="$(PackAsToolShimRuntimeIdentifiers)"/>
</ItemGroup>
<PropertyGroup>
<PackagedShimOutputRootDirectory Condition=" '$(PackagedShimOutputRootDirectory)' == '' ">$(OutDir)</PackagedShimOutputRootDirectory>
</PropertyGroup>
<GenerateShims
DotNetAppHostExecutableNameWithoutExtension="$(_DotNetAppHostExecutableNameWithoutExtension)"
PackagedShimOutputDirectory="$(PackagedShimOutputRootDirectory)/shims/$(TargetFramework)"
PackageId="$(PackageId)"
PackageVersion="$(Version)"
ProjectAssetsFile="$(ProjectAssetsFile)"
ProjectPath="$(MSBuildProjectFullPath)"
ShimRuntimeIdentifiers="@(_PackAsToolShimRuntimeIdentifiers)"
TargetFrameworkMoniker="$(NuGetTargetMoniker)"
ToolCommandName="$(ToolCommandName)"
ToolEntryPoint="$(ToolEntryPoint)"
>
<Output TaskParameter="EmbeddedApphostPaths" ItemName="_EmbeddedApphostPaths" />
</GenerateShims>
<ItemGroup>
<!-- Do this in an ItemGroup instead of as an output parameter so that it still gets added to the item set
during incremental builds when the task is skipped -->
<FileWrites Include="@(_EmbeddedApphostPaths)"/>
</ItemGroup>
</Target>
/// <summary> | ||
/// Change "dir/file.exe" to "dir" | ||
/// </summary> | ||
internal static string GetDirectoryPathInRelativePath(string publishRelativePath) |
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.
It is a simply task, but I find the most reliable way is to copy paste from corefx. Any suggestions?
See dotnet/corefx#4208
I tried to directly use a. Resolve and add these assets during ResolvePackageAssets. And write them into cache.(or even just output an extra itemgroup). It should be best for perf, but it will introduce implicit coupling of the 2 tasks. b. Add logic to detect "should rewrite shim". The complex part is, we need to detect the input params change. We needs another file to record what we write into shim last time (due to we cannot read back from shim). We could do a and b both |
04ee4bf
to
63bb7e2
Compare
@nguerrera what do you think of the asset file problem |
|
@nguerrera ping review |
@@ -61,6 +56,8 @@ public static class AppHost | |||
SearchAndReplace(accessor, _bytesToSearch, bytesToWrite, appHostSourceFilePath); | |||
} | |||
} | |||
|
|||
File.SetLastWriteTimeUtc(appHostDestinationFilePath, DateTime.UtcNow); |
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 deserves a comment. Memory-mapped write is not updating last write time?
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.
Added comment. And yes, from the test for incremental build. The last write time is not set at all without this line.
{ | ||
public sealed class ExecutableExtension | ||
{ | ||
public static string GetExecutableExtensionAccordingToRuntimeIdentifier(string runtimeIdentifier) |
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.
It's cumbersome to have "ExecutableExtension" in class and method. Maybe ExecutableExtension.ForRuntimeIdentifier
?
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 wonder if we can avoid this altogether by just preserving the apphost extension (or lack thereof). In other words, if we find apphost.exe, we make mytool.exe. If we find apphost, we make mytool. (And more theoretically, if we find apphost.future_os_extension, we make mytool.future_os_extension,)
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.
Plus the comment below. It has problem with GetEmbeddedApphostPaths(a computer only, get where to look for these files). In this we don't require template apphost path. I think it is over kill to depend on template apphost path just to get the extension.
I renamed it to ForRuntimeIdentifier
{ | ||
public static string GetExecutableExtensionAccordingToRuntimeIdentifier(string runtimeIdentifier) | ||
{ | ||
if (runtimeIdentifier.StartsWith("win")) |
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 forget what we decided was correct fo RIDs: case-insensitive or not? Pass StringComparison.Ordinal if case-sensitive and StringComparison.OrdinalIgnoreCase if case-insensitive.
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.
fixed
insensitive. I added OrdinalIgnoreCase
if (runtimeIdentifier.StartsWith("win")) | ||
{ | ||
apphostName += ".exe"; | ||
} |
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.
Why doesn't this use your helper?
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 see now that we search for the full name, not the name without extension and the approach I thought about earlier where we just preserve the extension could be prone to breaking if there's ever an apphost.something_else in the package.
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.
fixed, changed to use helper.
<WriteLinesToFile Lines="$(_GenerateShimsAssetsInputCacheHash)" File="$(_ShimInputCacheFile)" Overwrite="True" WriteOnlyWhenDifferent="True" /> | ||
|
||
<ItemGroup> | ||
<FileWrites Include="$(_ShimInputCacheFile)" Condition="Exists('$(_ShimInputCacheFile)')" /> |
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.
Why is there a condition here?
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.
Removed
// Copyright (c) .NET Foundation and contributors. All rights reserved. | ||
// Licensed under the MIT license. See LICENSE file in the project root for full license information. | ||
|
||
|
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.
nit: extra newline
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.
fixed
public ITaskItem[] ResolvedFileToPublishWithPackagePath { get; private set; } | ||
|
||
private const char DirectorySeparatorChar = '\\'; | ||
private const char AltDirectorySeparatorChar = '/'; |
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.
These are confusing me since they seem like they might just be using static Path
when reading the code. These names are generally OS-specific. How about rename to ForwardSeparatorChar and BackwardSeparatorChar. Or WindowsSeparatorChar and UnixSeparatorChar?
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.
fixed, renamed to ForwardSeparatorChar and BackwardSeparatorChar
<!-- Do this in an ItemGroup instead of as an output parameter so that it still gets added to the item set | ||
during incremental builds when the task is skipped --> | ||
<FileWrites Include="@(_EmbeddedApphostPaths)" Condition="Exists('@(_EmbeddedApphostPaths)')"/> | ||
<FileWrites Include="$(_ShimCreatedSentinelFile)" Condition="Exists('$(_ShimCreatedSentinelFile)')"/> |
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 didn't know that Exists works on items. What does it return if one exists, but another doesn't.
I don't think it's necessary to condition adds to filewrites
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.
removed condition
LastWriteTime in shims are not accurate. And _ShimInputCacheFile will have later timestamp than generated shims. | ||
Use a created file to "record" LastWriteTime. And only use it in "Outputs" field for timestamp comparison. | ||
--> | ||
<WriteLinesToFile Lines="This file's LastWriteTime is used in incremental build" File="$(_ShimCreatedSentinelFile)" Overwrite="True" /> |
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.
Consider using Touch task for this, which is more idiomatic in msbuild for this purpose. You'd lose this comment in the file, but I don't think we need 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.
Touch uses the same corefx api that is having the bug. Once it is fixed, we can remove all this.
https://github.com/Microsoft/msbuild/blob/4972ebc6d2b81c5e967cd3260943a95c8e5eb4be/src/Tasks/Touch.cs#L133
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 change here
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 you just add a comment that Touch uses the same API and therefore has the same problem.
/// 1. Publish PublishRelativePath is a relative <strong>file</strong> path. This need to convert to relative path. | ||
/// 2. Due to DotnetTools package format, "tools/{TargetFramework}/any/" is needed in addition to the relative path. | ||
/// </summary> | ||
public sealed class PublishRelativePathToPackPackagePath : TaskBase |
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 name sounds like it's going to publish something and also doesn't make it very clear that it's specific to getting the paths for a DotnetTools package
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.
fixed, Renamed to RelativePathFormatInPublishToFormatInPackPackage
- Add comment for setting SetLastWriteTimeUtc - Rename and ignore rid casing - Use ExecutableExtension.ForRuntimeIdentifier - Rename and remove condition on include - Rename to RelativePathFormatInPublishToFormatInPackPackage
<ItemGroup> | ||
<_GeneratedFiles Include="$(PublishDepsFilePath)"/> | ||
<_GeneratedFiles Include="$(PublishRuntimeConfigFilePath)"/> | ||
<_GeneratedFiles Include="$(_ToolsSettingsFilePath)"/> | ||
</ItemGroup> | ||
|
||
<RelativePathFormatInPublishToFormatInPackPackage |
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 name is still confusing me. Can it be something like ResolveToolPackagePaths
?
LastWriteTime in shims are not accurate. And _ShimInputCacheFile will have later timestamp than generated shims. | ||
Use a created file to "record" LastWriteTime. And only use it in "Outputs" field for timestamp comparison. | ||
--> | ||
<WriteLinesToFile Lines="This file's LastWriteTime is used in incremental build" File="$(_ShimCreatedSentinelFile)" Overwrite="True" /> |
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 you just add a comment that Touch uses the same API and therefore has the same problem.
Continue of #2366
problem i know so far
It supports Clean target. However, it will not remove empty folder. Since different RID's shims have the same name. I need to separate them by folder. But msbuild will not remove that dotnet/msbuild#2408