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

Using Apphost for post netcoreapp2.0 standalone activation #978

Merged
merged 2 commits into from
Mar 20, 2017

Conversation

ramarag
Copy link
Member

@ramarag ramarag commented Mar 11, 2017

namespace Microsoft.NET.Build.Tasks
{
/// <summary>
/// Embeds the App Name into the Nuetered host
Copy link
Member

Choose a reason for hiding this comment

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

Nuetered should probably not be used here.

Copy link
Contributor

Choose a reason for hiding this comment

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

Definitely not. (And not just because it's misspelled)

var _modifiedAppHostPath = Path.Combine(destinationDirectory, $"{appbaseName}{hostExtension}");
_outputAppHostPath= new TaskItem(_modifiedAppHostPath);

if ( File.Exists(_modifiedAppHostPath))
Copy link
Member

Choose a reason for hiding this comment

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

?? Shouldn't we overwrite it if it already exists? What if it doesn't contain the correct information?

Copy link
Member Author

Choose a reason for hiding this comment

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

The info inside is the name of the app, so if we have placed a file here- there is not much to do.
Do you think a comment will make it more clearer


var appbaseName = Path.GetFileNameWithoutExtension(AppName);
var destinationDirectory = Path.GetFullPath(AppHostDestinationDirectoryPath);
var _modifiedAppHostPath = Path.Combine(destinationDirectory, $"{appbaseName}{hostExtension}");
Copy link
Member

Choose a reason for hiding this comment

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

local variables should not start with _. Private fields should.

@@ -525,15 +525,21 @@ Copyright (c) .NET Foundation. All rights reserved.
-->

<Target Name="RenamePublishedHost"
DependsOnTargets="_ComputeNETCoreBuildOutputFiles"
Copy link
Member

Choose a reason for hiding this comment

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

Why? That target is for "build", this is for "publish".

Copy link
Member Author

Choose a reason for hiding this comment

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

publish pipes in from build, the notion is largely artificial

@@ -37,4 +37,8 @@ Copyright (c) .NET Foundation. All rights reserved.
<PackageReference Include="Microsoft.NETCore.App" Version="$(RuntimeFrameworkVersion)" IsImplicitlyDefined="true" />
</ItemGroup>

<ItemGroup Condition=" '$(DisableImplicitFrameworkReferences)' != 'true' and '$(TargetFrameworkIdentifier)' == '.NETCoreApp' and !$(RuntimeFrameworkVersion.StartsWith('1.'))">
<PackageReference Include="Microsoft.NETCore.DotNetAppHost" Version="$(RuntimeFrameworkVersion)" IsImplicitlyDefined="true" />
Copy link
Member

Choose a reason for hiding this comment

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

Won't this result in always referencing this package? Even if I'm a portable app?

Copy link
Member Author

Choose a reason for hiding this comment

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

That is the meta package, it will not resolved to any assets

Copy link
Contributor

@nguerrera nguerrera Mar 11, 2017

Choose a reason for hiding this comment

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

It's going to bleed in to pack, solution explorer, etc. etc. cc @davidfowl

Copy link
Member

Choose a reason for hiding this comment

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

Errr, what is this thing...

Copy link
Member

Choose a reason for hiding this comment

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

What is the point of having the Microsoft.NETCore.DotNetHost package anymore? We will never use the dotnet executable from it, since standalone apps always use DotNetAppHost.

So instead of doing it this way, why don't we just put the apphost executable into the DotNetHost package?

Alternatively, change Microsoft.NETCore.App to reference Microsoft.NETCore.DotNetAppHost and remove the reference to Microsoft.NETCore.DotNetHost.

Copy link
Member

Choose a reason for hiding this comment

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

https://github.com/dotnet/core-setup/issues/1722

but that does not affect the change here in SDK

It does affect the change here in the SDK. If we configure the packages efficiently, the tooling doesn't need all this logic in it. See the issue above. In this change we are doing things like

  • Delete the dotnet.exe file from the published files.
  • First look for apphost.exe, if that isn't found fall back to dotnet.exe.

The tooling logic can be a lot simpler.

Copy link
Member

Choose a reason for hiding this comment

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

I agree it can be made simpler once we have figured the correct changes for Core-Setup. However, that should not block enabling this feature. @ramarag please context to this discussion in the issue above so that, once the feature is enabled, you should visit cleaning up Core-Setup, followed by the SDK.

Copy link
Member Author

Choose a reason for hiding this comment

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

@eerhardt i have already done what you are suggesting.
@gkhanna79 the latest M.N.App package indeed refers the Apphost package, but the one sdk currently refers does not, will update it to the latest version and remove this reference

Copy link
Member Author

Choose a reason for hiding this comment

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

Actually spoke too soon, i will have to update M.N.App to get apphost.exe in it

Copy link
Member Author

Choose a reason for hiding this comment

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

Getting this done by dotnet/core-setup#1728

<ResolvedFileToRemove Include ="%(ResolvedFileToPublish.Identity)" Condition="'%(ResolvedFileToPublish.RelativePath)' == '$(_DotNetHostExecutableName)'"/>
<ResolvedFileToPublish Remove ="%(ResolvedFileToRemove.Identity)"/>

<ResolvedFileToAdd Include="%(NativeAppHostNETCore.Identity)">
Copy link
Member

Choose a reason for hiding this comment

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

Why use 2 items here? Why not just Include this into <ResolvedFileToPublish>?

Copy link
Member Author

Choose a reason for hiding this comment

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

Because i need to set the metadata on the item

Copy link
Member

Choose a reason for hiding this comment

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

You can set metadata on the item even without using a separate item type here:

      <ResolvedFileToPublish Include="%(NativeAppHostNETCore.Identity)">
        <RelativePath>$(AssemblyName)%(Extension)</RelativePath>
      </ResolvedFileToPublish>

Works and is simpler and more performant.

Copy link
Member Author

Choose a reason for hiding this comment

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

This sets the metadata on all ResolvedFileToPublish, not what we actually want

Copy link
Member

Choose a reason for hiding this comment

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

No it doesn't. Just like:

<Content Include="File1">
</Content>

<Content Include="File2">
  <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>

In that scenario, how many files get copied to the output directory? Only File2. Adding a new Item and setting metadata on the item doesn't set the metadata on all the existing files in that item.

@eerhardt
Copy link
Member

                                    RenamePublishedHost

This name and comment should be updated. This is no longer what this target does.


Refers to: src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Publish.targets:521 in 0203e6f. [](commit_id = 0203e6f, deletion_comment = False)

<data name="FileNameIsTooLong" xml:space="preserve">
<value>Given File Name '{0}' is longer than 1024 bytes</value>
</data>
<data name="HostIsNotNuetered" xml:space="preserve">
Copy link
Member

Choose a reason for hiding this comment

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

Again, please don't use Nuetered.

var rid = EnvironmentInfo.GetCompatibleRid(targetFramework);


TestProject referencerProject = new TestProject()
Copy link
Member

Choose a reason for hiding this comment

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

Why is this named referencer?

Copy link
Member Author

Choose a reason for hiding this comment

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

oops copy paste error

IsSdkProject = true,
TargetFrameworks = targetFramework,
RuntimeFrameworkVersion = RepoInfo.NetCoreApp20Version,
// Need to use a self-contained app for now because we don't use a CLI that has a "2.0" shared framework
Copy link
Member

Choose a reason for hiding this comment

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

this comment doesn't belong. We use a self-contained app because thatis what we are testing.

Copy link
Member Author

Choose a reason for hiding this comment

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

oops copy paste error

<EmbedAppNameInHost AppHostSourcePath="@(NativeRestoredAppHostNETCore)"
AppHostDestinationDirectoryPath="$(AppHostDestinationDirectoryPath)"
AppName="$(AssemblyName).dll"
StringToReplace="foobar"
Copy link
Member

Choose a reason for hiding this comment

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

Really?

Copy link
Contributor

Choose a reason for hiding this comment

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

Foobar is among terms that are not to be used in products. Also, the blind search and replace strategy carries risk that the mere 5 bytes occur elsewhere in the exe with completely different meaning.

Copy link
Member Author

Choose a reason for hiding this comment

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

blame @schellap for this, does it make more sense to move this string into C# or just hard code the sha value ?


<EmbedAppNameInHost AppHostSourcePath="@(NativeRestoredAppHostNETCore)"
AppHostDestinationDirectoryPath="$(AppHostDestinationDirectoryPath)"
AppName="$(AssemblyName).dll"
Copy link
Member

Choose a reason for hiding this comment

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

Probably shouldn't be hard-coding .dll here. If you really need the extension, use $(TargetExt). Why do we use the extension in the AppName?

Copy link
Member Author

Choose a reason for hiding this comment

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

This is the extension of the managed assembly, i believe it will always be a ".dll"

Copy link
Member

Choose a reason for hiding this comment

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

If it would always be a .dll, then we wouldn't have the $(TargetExt) property. We would just hard-code .dll everywhere.

Use the property.

Copy link
Member

Choose a reason for hiding this comment

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

But again, why do we put the extension in the AppName? MyApp isn't "named" MyApp.dll.

Copy link
Member

Choose a reason for hiding this comment

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

@eerhardt Please see my first comment on the review of this PR - I think it should not be called AppName but rather AppBinaryName.

return;
}

if (AppName.Length > 1024)
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't this be checked against utf8-encoded byte length?

Copy link
Member Author

Choose a reason for hiding this comment

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

is it not the same ?

Copy link
Contributor

Choose a reason for hiding this comment

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

It is not. If AppName has characters outside ASCII, it will take up more than AppName bytes.


protected override void ExecuteCore()
{
var array = File.ReadAllBytes(AppHostSourcePath);
Copy link
Contributor

Choose a reason for hiding this comment

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

How big is apphost? This code seems very likely to be slow.

Copy link
Member Author

Choose a reason for hiding this comment

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

This is a one time operation for every project. We have made the design decision to replace the byte stream in the host, i suppose there is no way out

On win7-x64 it is 74 kb

Copy link
Contributor

Choose a reason for hiding this comment

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

Surely we can do better than copying everything in to 74 kb managed array and doing LINQ (!) over it. See @schellap's sample. Maybe it doesn't have to be quite that sophisticated, but I think the naive approach here is pushing it.

Copy link
Member

Choose a reason for hiding this comment

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

You should move the byte[] read to be after the checks below so that it is not performed in vain.

<EmbedAppNameInHost AppHostSourcePath="@(NativeRestoredAppHostNETCore)"
AppHostDestinationDirectoryPath="$(AppHostDestinationDirectoryPath)"
AppName="$(AssemblyName).dll"
StringToReplace="foobar"
Copy link
Contributor

Choose a reason for hiding this comment

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

Foobar is among terms that are not to be used in products. Also, the blind search and replace strategy carries risk that the mere 5 bytes occur elsewhere in the exe with completely different meaning.

throw new BuildErrorException(Strings.FileNameIsTooLong, AppName);
}

var byteArray = SHA256.Create().ComputeHash(Encoding.UTF8.GetBytes(StringToReplace));
Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, I missed that we're replacing the sha of foobar, not foobar. So disregard 5 byte risk part of earlier comment. Still why are we wasting cpu resources computing this fixed value every time?

<value>Given File Name '{0}' is longer than 1024 bytes</value>
</data>
<data name="HostIsNotNuetered" xml:space="preserve">
<value>The App host does not have the embedded hash value of string '{0}'</value>
Copy link
Contributor

Choose a reason for hiding this comment

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

As of now, this will report 'foobar' to user which would be a world ready tenet blocker and really embarrassing if we ever have to explain this error.

What is the point of using a hash for this marker. Can't we just generate a guid?

I think the user facing message for this should not go in to the implementation details of the replacement. This is basically an invalid (corrupted?) apphost, right?

@schellap
Copy link

schellap commented Mar 11, 2017 via email

@schellap
Copy link

schellap commented Mar 11, 2017 via email

}

var placeHolder = "c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2"; //hash value embedded in apphost.exe
placeHolder = placeHolder.Replace("-", "").ToLower();
Copy link
Contributor

Choose a reason for hiding this comment

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

There are no hyphens to replace in placeholder

Copy link
Contributor

Choose a reason for hiding this comment

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

Nor anything to ToLower()

@nguerrera
Copy link
Contributor

nguerrera commented Mar 11, 2017

(Btw, it is some opaque SHA -- called out as foobar just for the unittest and comments just in case you wanted to regenerate.)

Why would I ever want to regenerate something opaque or why would unit test care to check that is in fact generated in any particular way? I think it will only serve to confuse folks that this is at all linked to foobar. You could have just generated 32 securely random bytes. Can we still change the marker? Policheck flags every occurrence of foobar in source code and this is just inviting folks to use it and waste cycles.

@nguerrera
Copy link
Contributor

nguerrera commented Mar 11, 2017

As far as perf, two questions:

  1. Why not ship a tiny data file in the package that indicates the offset of the marker? No matter how clever, it seems wasteful to go hunting for it on every standalone build.

  2. Why do we need to write the app name in to the apphost? What was wrong with it just detecting the name from whatever it gets renamed to?

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.

See above

@schellap
Copy link

schellap commented Mar 11, 2017 via email

throw new BuildErrorException(Strings.FileNameIsTooLong, AppName);
}

var placeHolder = "c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2"; //hash value embedded in apphost.exe
Copy link
Member

Choose a reason for hiding this comment

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

It will be good to add more comments around this and pointers to areas in Core-Setup to which this is connect. It will help the reader with more context.

public string AppHostDestinationDirectoryPath { get; set; }

[Required]
public string AppName { get; set; }
Copy link
Member

Choose a reason for hiding this comment

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

It is not really AppName but AppBinaryName, right? Can we reflect it in the field naming as well?

var placeHolder = "c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2"; //hash value embedded in apphost.exe
placeHolder = placeHolder.Replace("-", "").ToLower();

var bytesToWrite = Encoding.UTF8.GetBytes(AppName);
Copy link
Member

@gkhanna79 gkhanna79 Mar 13, 2017

Choose a reason for hiding this comment

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

I would also suggest writing a comment describing what is happening in the next set of statements for the reader to get context.

@@ -25,7 +25,7 @@ Copyright (c) .NET Foundation. All rights reserved.
<_GetChildProjectCopyToPublishDirectoryItems Condition="'$(_GetChildProjectCopyToPublishDirectoryItems)' == ''">true</_GetChildProjectCopyToPublishDirectoryItems>

<!-- publishing self-contained apps should publish the native host as $(AssemblyName).exe -->
<RenamePublishedHost Condition=" '$(RenamePublishedHost)' == '' and '$(OutputType)' == 'Exe' and '$(RuntimeIdentifier)' != ''">true</RenamePublishedHost>
<DeployAppHost Condition=" '$(DeployAppHost)' == '' and '$(OutputType)' == 'Exe' and '$(RuntimeIdentifier)' != ''">true</DeployAppHost>
Copy link
Member

Choose a reason for hiding this comment

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

Presumable, the RID check implies that this is a standalone app - is that correct @eerhardt? If so, @ramarag please add a comment around it.

Copy link
Member

Choose a reason for hiding this comment

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

Correct, here and other places in the code '$(RuntimeIdentifier)' != '' means it is a standalone app.


Renames the dotnet native host to match the application's name.
Deploys the host to run the app and ensures it matches the app name
Copy link
Member

Choose a reason for hiding this comment

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

We should update this to reflect that this applies to standalone apps only.


referencerAsset.Restore(testProject.Name);
var publishCommand = new PublishCommand(Stage0MSBuild, Path.Combine(referencerAsset.TestRoot, testProject.Name));
var publishResult = publishCommand.Execute($"/p:RuntimeIdentifier={rid}");
Copy link
Member

Choose a reason for hiding this comment

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

You don't need to pass /p:RuntimeIdentifier={rid} to publish. That value is already hard-coded into the .csproj up above. You can just say .Execute().

"Hello.pdb",
"Hello.deps.json",
"Hello.runtimeconfig.json",
$"{libPrefix}coreclr{Constants.DynamicLibSuffix}",
Copy link
Member

Choose a reason for hiding this comment

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

Why do we use Constants.DynamicLibSuffix, but we hard-code our own prefix?

Copy link
Member Author

Choose a reason for hiding this comment

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

There is no prefix in that class

.Pass()
.And
.HaveStdOutContaining("Hello from a netcoreapp2.0.!");
}
Copy link
Member

Choose a reason for hiding this comment

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

Do you also want to ensure that the executable has the app name embedded in it correctly?

Copy link
Member Author

Choose a reason for hiding this comment

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

if it is not the apphost will fail. I was wondering should i add a knob to turn off the EmbedAppNameInHost and add a test case to turn of the knob and see the test fail, i decided against it as this is testing the host and we already do that in core-setup

@ramarag
Copy link
Member Author

ramarag commented Mar 18, 2017

@dotnet-bot test this please

@ramarag
Copy link
Member Author

ramarag commented Mar 18, 2017

@eerhardt @gkhanna79 @eerhardt i have addressed the comments

public string AppBinaryName { get; set; }

[Output]
public string ModifiedAppHostPath
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: Can be public string ModifiedAppHostPath { get; private set; }

get { return _modifiedAppHostPath; }
}

private static string placeHolder = "c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2"; //hash value embedded in default apphost executable
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: placeholder is one valid word so use placheholder casing (no capital H)


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.

nit: extra whitespace


SearchAndReplace(array, bytesToSearch, bytesToWrite);


Copy link
Contributor

Choose a reason for hiding this comment

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

nit: extra whitespace

SearchAndReplace(array, bytesToSearch, bytesToWrite);


if ( !Directory.Exists(destinationDirectory))
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: extra whitespace next to paren


}
}

Copy link
Contributor

Choose a reason for hiding this comment

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

nit: extra whitespace

}

}

Copy link
Contributor

Choose a reason for hiding this comment

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

nit: extra whitespace

<value>The App host has been modified, it is missing the default embedded value '{0}'</value>
</data>
<data name="FileNameIsTooLong" xml:space="preserve">
<value>Given File Name '{0}' is longer than 1024 bytes</value>
Copy link
Contributor

Choose a reason for hiding this comment

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

file name (no caps, it's not a proper noun).

}

var array = File.ReadAllBytes(AppHostSourcePath);
var bytesToSearch = Encoding.UTF8.GetBytes(placeHolder);
Copy link
Contributor

Choose a reason for hiding this comment

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

We shouldn't need to re-encode the placeholder every time, why not just have a static readonly byte[] for that.

throw new BuildErrorException(Strings.FileNameIsTooLong, AppBinaryName);
}

var array = File.ReadAllBytes(AppHostSourcePath);
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm still concerned about reading the entire file in to one big managed array every time.

Copy link
Member Author

Choose a reason for hiding this comment

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

This is a one time per project's lifetime. We can revisit it later, will file a bug for it

}

// See: https://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm
private static int[] ComputeKMP_FailureFunction(byte[] pattern)
Copy link
Contributor

Choose a reason for hiding this comment

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

Don't name non-test methods with underscores.

This doesn't need to be re-computed every time either.

Copy link
Member Author

Choose a reason for hiding this comment

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

Will file a bug for it

@@ -249,4 +249,10 @@
<data name="RuntimeIdentifierWasNotSpecified" xml:space="preserve">
<value>Specify a RuntimeIdentifier</value>
</data>
<data name="AppHostHasBeenModified" xml:space="preserve">
<value>The App host has been modified, it is missing the default embedded value '{0}'</value>
Copy link
Contributor

@nguerrera nguerrera Mar 20, 2017

Choose a reason for hiding this comment

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

This will be very rare so I think the message should help identify the problem better. Saying "modified" is a bit misleading as it could also be a bug where we get out of sync with apphost changes.

I suggest something like "Unable to use '{0}' as application host executable as it does not contain the expected placeholder byte sequence '{1}' that would mark where the application name would be written."

{0} would be the full path to the file so we can see package version, etc. if someone actually hits this and asks for support.

@nguerrera
Copy link
Contributor

I'm OK postponing the perf changes, but I'd like an issue to be filed to look at that.

Please clean up the whitespace and the error message before merging.

@ramarag ramarag merged commit 952b5b7 into dotnet:master Mar 20, 2017
mmitche pushed a commit to mmitche/sdk that referenced this pull request Jun 5, 2020
* Update dependencies from https://github.com/dotnet/arcade build 20191001.4

- Microsoft.DotNet.Arcade.Sdk - 5.0.0-beta.19501.4

* Update dependencies from https://github.com/dotnet/arcade build 20191002.11

- Microsoft.DotNet.Arcade.Sdk - 5.0.0-beta.19502.11
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.

7 participants