Skip to content
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

Publish to Single-File #3132

Merged
merged 6 commits into from
Apr 19, 2019
Merged

Publish to Single-File #3132

merged 6 commits into from
Apr 19, 2019

Conversation

swaroop-sridhar
Copy link
Contributor

This change implements support for publishing apps to a single file.

  • dotnet publish /p:PublishSingleFile=true causes the contents of the "original" publish directory to a single file in the actual publish directory
  • Files marked with the meta-data <ExcludeFromSingleFile>false<ExcludeFromSingleFile> are left in the publish directory unbundled. This includes PDB files by default
  • PDB files can be bundled into the single file by setting /p:IncludeSymbolsInSingleFile=true

Publishing to a single file requires publishing wrt a RID using an apphost, because the generated file is the platform-specific AppHost executable with embedded dependencies.

@swaroop-sridhar
Copy link
Contributor Author

@nguerrera @fadimounir @sbomer please review.
CC: @ericstj

@swaroop-sridhar
Copy link
Contributor Author

@dotnet-bot test this please

@swaroop-sridhar swaroop-sridhar force-pushed the bundle-lib branch 3 times, most recently from d84a6a1 to 3bd01b7 Compare April 17, 2019 21:07
@@ -85,6 +86,9 @@ Copyright (c) .NET Foundation. All rights reserved.
-->
<Target Name="PrepareForPublish">

<NETSdkError Condition="'$(_IsExecutable)' != 'true'"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be checking PublishSingleFile as well?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh you already fixed it. :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @sbomer; as you know, I saw the feedback over your shoulder :)

@swaroop-sridhar
Copy link
Contributor Author

Thanks @sbomer. Once the ILLinker change (#3125) goes through, I'll add another test for Illink + R2R + Bundled app.

<comment>{StrBegin="NETSDK1098: "}</comment>
</data>
<data name="CannotHaveSingleFileWithoutExecutable" xml:space="preserve">
<value>NETSDK1099: Publishing to a single-file is currently only supported for executable applications.</value>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

currently [](start = 54, length = 9)

I feel we should remove "currently". If we support this in the future, we can delete this error message

<PropertyGroup>
<!-- Handle the case where IncludeSymbolsInSingleFile may not be defined -->
<EmbedPDBs Condition="'$(IncludeSymbolsInSingleFile)' == 'true'">true</EmbedPDBs>
<EmbedPDBs Condition="'$(IncludeSymbolsInSingleFile)' != 'true'">false</EmbedPDBs>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also handle the .pdb/.map files created by crossgen here. We can do this later (I want to make some changes to the R2R related stuff first). Please file an issue to track this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change implements support for publishing apps to a [single file](https://github.com/dotnet/designs/blob/master/accepted/single-file/design.md).

* ``dotnet publish /p:PublishSingleFile=true`` causes the contents of the "original" publish directory to a single file in the actual publish directory
* Files marked with the meta-data ``<ExcludeFromSingleFile>false<ExcludeFromSingleFile>`` are left in the publish directory unbundled. This includes PDB files by default
* PDB files can be bundled into the single file by setting ``/p:IncludeSymbolsInSingleFile=true``

Publishing to a single file requires publishing wrt a RID using an apphost, because the generated file is the platform-specific AppHost executable with embedded dependencies.
The property to trigger R2R compilation was changed from "Ready2Run" to "PublishReadyToRun".
Update this in a test case.
@swaroop-sridhar
Copy link
Contributor Author

In this change 1cc9352 @fadimounir I updated a test case to use the new name PublishReadyToRun for triggering R2R compilation, now that #3134 is checked in.

Copy link
Contributor

@nguerrera nguerrera left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good overall. Some minor feedback.

foreach(var item in FilesToBundle)
{
fileSpec.Add(new FileSpec(sourcePath: item.ItemSpec,
bundleRelativePath: item.GetMetadata("RelativePath")));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
bundleRelativePath: item.GetMetadata("RelativePath")));
bundleRelativePath: item.GetMetadata(MetadataKeys.RelativePath)));

Not sure if I got that exactly right, but there's a class with constants for metadata. Use that.

Bundler bundler = new Bundler(AppHostName, OutputDir, EmbedPDBs, ShowDisgnosticOutput);
var fileSpec = new List<FileSpec>(FilesToBundle.Length);

foreach(var item in FilesToBundle)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
foreach(var item in FilesToBundle)
foreach (var item in FilesToBundle)

nit: spacing

============================================================
-->
<Target Name="_CopyResolvedFilesToPublishPreserveNewest"
DependsOnTargets="_ComputeResolvedFilesToPublishTypes"
Inputs="@(_ResolvedFileToPublishPreserveNewest)"
Outputs="@(_ResolvedFileToPublishPreserveNewest->'$(PublishDir)%(RelativePath)')">

<ItemGroup>
<_ResolvedUnBundledFileToPublishPreserveNewest
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<_ResolvedUnBundledFileToPublishPreserveNewest
<_ResolvedUnbundledFileToPublishPreserveNewest

nit on casing. Un is not a word on its own.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Obviously, rename all references to it too.)

<PropertyGroup>
<!-- Handle the case where IncludeSymbolsInSingleFile may not be defined -->
<EmbedPDBs Condition="'$(IncludeSymbolsInSingleFile)' == 'true'">true</EmbedPDBs>
<EmbedPDBs Condition="'$(IncludeSymbolsInSingleFile)' != 'true'">false</EmbedPDBs>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EmbedPDBs is a pretty generic name since there is also a concept of embeddign PDB in dll (DebugType=Embedded). You can avoid having a new name, by defaulting IncludeSymbolsInSingleFile to false when empty. This is very idiomatic in msbuild, you will see it all over the place.

<PropertyGroup>
   <IncludeSymbolsInSingleFile Condition="'$(IncludeSymbolsInSingleFile)' == ''">false</IncludeSymbolsInSingleFile>
</PropertyGroup>

I don't even think you need the comment if you do that, it is pretty obvious what this handles.

Then pass EmbedPdbs="$(IncludeSymbolsInSingleFile)

[Required]
public string AppHostName { get; set; }
[Required]
public bool EmbedPDBs { get; set; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public bool EmbedPDBs { get; set; }
public bool IncludeSymbols { get; set; }

I think it is worth using the same terminology throughout, and the public facing name is symbols for this feature.


protected override void ExecuteCore()
{
Bundler bundler = new Bundler(AppHostName, OutputDir, EmbedPDBs, ShowDisgnosticOutput);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Bundler bundler = new Bundler(AppHostName, OutputDir, EmbedPDBs, ShowDisgnosticOutput);
var bundler = new Bundler(AppHostName, OutputDir, EmbedPDBs, ShowDisgnosticOutput);

nit: type is obvious

Condition="'$(PublishSingleFile)' == 'true'"
DependsOnTargets="_ComputeFilesToBundle"
Inputs="@(_FilesToBundle)"
Outputs="$(PublishDir)\$(AssemblyName)$(_NativeExecutableExtension)">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Outputs="$(PublishDir)\$(AssemblyName)$(_NativeExecutableExtension)">
Outputs="$(PublishDir)$(AssemblyName)$(_NativeExecutableExtension)">

We ensure trailing slash and you can see this is how PublishDir is used elsewhere. This is another common idiom in msbuild, it prevents accidental writing to root of disk if something is undefined. I once deleted my entire hard drive this way so I stick to this now :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Funny story, a similar issue caused CoreFx build perf to tank once, the cause $(SomeProperty)\**\*.extension. Someone renamed SomeProperty and CI passed...

@swaroop-sridhar
Copy link
Contributor Author

@nguerrera: I've made the corrections you suggested in this commit: 9246cad
Thanks.

var bundler = new Bundler(AppHostName, OutputDir, IncludeSymbols, ShowDisgnosticOutput);
var fileSpec = new List<FileSpec>(FilesToBundle.Length);

Array.ForEach(FilesToBundle,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you change this to Array.ForEach? I liked the foreach loop better.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK @nguerrera, I pushed 25c2b42 which revers back to the foreach() loop.

[Required]
public bool ShowDisgnosticOutput { get; set; }

protected override void ExecuteCore()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love how delightfully simple this task ended up being. :)

[Required]
public string OutputDir { get; set; }
[Required]
public bool ShowDisgnosticOutput { get; set; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public bool ShowDisgnosticOutput { get; set; }
public bool ShowDiagnosticOutput { get; set; }

Just spotted a typo

Copy link
Contributor Author

@swaroop-sridhar swaroop-sridhar Apr 19, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, fixed it in 671de2d

@swaroop-sridhar
Copy link
Contributor Author

@dotnet-bot test dotnet-sdk-public-ci

Copy link
Member

@ericstj ericstj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

@nguerrera nguerrera merged commit aacf124 into dotnet:master Apr 19, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants