Skip to content

Conversation

@RikkiGibson
Copy link
Member

@RikkiGibson RikkiGibson commented Oct 20, 2025

This package will be used to support dotnet/roslyn#80575.

Some things I haven't figured out yet:

  • GracefulException probably won't work in the source package.
    • Adding a compilation symbol which simply declares this type when we are not in context of dotnet cli
  • JSON generator error in RunApiCommand.cs.
  • Failing to include MSBuildUtilities.cs in the source package. See Microsoft.DotNet.FileBasedPrograms.projitems. It seems like a .cs file which is "out of tree" doesn't make it into the package. I'm unsure if that is expected. @tmat may have some suggestion for how to address this part.
    • Working around this by simply making another copy.

@jjonescz would be interested to know any thoughts you have on the above bullets or the TODOs in source.

}

// TODO: getting the following error, which is not suppressible with pragma.
// error SYSLIB1225: The type 'Encoding' includes the ref like property, field or constructor parameter 'Preamble'. No source code will be generated for the property, field or constructor. (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib1225)
Copy link
Member Author

@RikkiGibson RikkiGibson Oct 21, 2025

Choose a reason for hiding this comment

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

It looks like it is complaining about System.Text.Encoding.Preamble. But AFAIK, I only changed where some of the types involved are declared, not what their members are.

I managed to "debug" this by commenting out some of the attributes and then members on various types.

It looks like this is ultimately coming through via:

  • [JsonSerializable(typeof(RunApiOutput))] to
  • class Project : RunApiOutput to
  • ImmutableArray<SimpleDiagnostic> Diagnostics to
  • Position SimpleDiagnostic.Location to
  • SourceFile SimpleDiagnostic.SourceFile to
  • SourceText SourceFile.Text to
  • Encoding SourceText.Encoding

... and, it looks like the release/10.0.1xx branch version of SimpleDiagnostic does not have SourceFile on it. So, I think moving to that "version" of the declaration would probably make the generator error go away.

It looks like I copied the implementation from the wrong branch here. @jjonescz is release/10.0.1xx the correct branch to be using as a starting point for changes here? I assume we can't get this source package work into there as it is taking servicing fixes only. Is there some step we neglected to take in order to make sure the changes going into the release branch make their way into main?

Copy link
Member

Choose a reason for hiding this comment

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

release/10.0.2xx should be open to normal changes and up to date. I'm using that one for run-file development now. main haven't received updates from release/10.0.1xx since September 20.

Copy link
Member

Choose a reason for hiding this comment

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

This is the correct mindset for navigating the SDK repo 👍

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks! I will plan on updating this based on release/10.0.2xx branch, and changing target to the same. Before I open for “full” review, I will try to set up the history so that the first commit is a pure move.

internal static class AppDirectiveHelpers
{

#pragma warning disable RSEXPERIMENTAL003 // 'SyntaxTokenParser' is experimental
Copy link
Member

Choose a reason for hiding this comment

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

This suppression shouldn't be needed. I'm removing it in #51410


namespace Microsoft.DotNet.FileBasedPrograms;

internal static class AppDirectiveHelpers
Copy link
Member

Choose a reason for hiding this comment

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

I think the official term might be "file-level directives", not "app directives" as the latter seems too generic, although in the context of namespace "FileBasedPrograms" it's fine

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes I think changing term to match the spec will be best here. I can make the same change on the editor features side. https://github.com/dotnet/sdk/blob/main/documentation/general/dotnet-run-file.md#directives-for-project-metadata

namespace Microsoft.DotNet.FileBasedPrograms;

/// <summary>
/// When targeting netstandard2.0, the user of the source package must "implement" certain methods by declaring members in this type.
Copy link
Member

Choose a reason for hiding this comment

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

Missing </summary> closing tag

Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure why copying this file is needed. Does adding <Compile> to the package project not work? I see you are saying "It seems like a .cs file which is "out of tree" doesn't make it into the package." but I've recently created a source package where all the files come from a different directory and it works fine:

https://github.com/dotnet/roslyn/blob/main/src/NuGet/Microsoft.CodeAnalysis.BuildClient.Package/Microsoft.CodeAnalysis.BuildClient.Package.csproj

Although I wasn't using shproj and projitems, perhaps that's the problem?

Copy link
Member Author

Choose a reason for hiding this comment

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

In my case I tried adding a Compile item to the .projitems. I'll take a look at the build for the source project you linked and try to work out what the difference is.

{
}

public GracefulException(string? message, string? todo2) : base(message)
Copy link
Member

Choose a reason for hiding this comment

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

The todo2 parameter is unused. Can we just remove this overload?

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 overload is being called in the implementation. Perhaps when compiling against the proper version, the GracefulException(string, params string[]) overload is used. But I think it would be necessary to do more work to get rid of the usages, in order to delete this overload.

Copy link
Member Author

Choose a reason for hiding this comment

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

I think the work to drop usage of this type in the source package might be worthwhile, I am just trying to push it out until after I can get the end-to-end working. I would also be interested in your thoughts on that.

Copy link
Member

Choose a reason for hiding this comment

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

I guess we can keep the overload and just make sure the second parameter isn't dropped; e.g., using string.Format in the implementation?

}

#if FILE_BASED_PROGRAMS_SOURCE_PACKAGE_GRACEFUL_EXCEPTION
internal class GracefulException : Exception
Copy link
Member

@tmat tmat Oct 22, 2025

Choose a reason for hiding this comment

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

Can we remove all the exception throwing? We already report diagnostics, right?
The calling code can convert diagnostics to an exception if needed.

Copy link
Member Author

Choose a reason for hiding this comment

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

I think it will be best to remove usage of this type from the source package. But, it will involve a decent chunk of refactoring.

// Also normalize blackslashes to forward slashes to ensure the directive works on all platforms.
var sourceDirectory = Path.GetDirectoryName(context.SourceFile.Path) ?? ".";
var resolvedProjectPath = Path.Combine(sourceDirectory, directiveText.Replace('\\', '/'));
if (Directory.Exists(resolvedProjectPath))
Copy link
Member

Choose a reason for hiding this comment

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

Seems like this may depend on the process current directory. Should we abstract that? Depending on process state makes it hard to test and reuse the code in different environments

Copy link
Member Author

Choose a reason for hiding this comment

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

Are you suggesting adding some way to pass in a base directory for resolution here? Perhaps using the compilation’s SourceReferenceResolver if it exists?

Copy link
Member

Choose a reason for hiding this comment

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

Perhaps pass additional defaultDirectory parameter or something similar.

@RikkiGibson RikkiGibson changed the base branch from main to release/10.0.2xx October 23, 2025 19:03
@RikkiGibson
Copy link
Member Author

When attempting to use the package in Roslyn, I get the following exception in the analyzer.

[System.Resources.MissingManifestResourceException thrown]	
[External Code]	
FileBasedProgramsResources.GetResourceString(string resourceKey, string defaultValue) Line 15	C#
FileBasedProgramsResources.CouldNotFindProjectOrDirectory.get() Line 19	C#
CSharpDirective.Project.Parse(CSharpDirective.ParseContext context) Line 492	C#
CSharpDirective.Parse(CSharpDirective.ParseContext context) Line 315	C#
AppDirectiveHelpers.FindLeadingDirectives(SourceFile sourceFile, SyntaxTriviaList triviaList, DiagnosticBag diagnostics, ImmutableArray<CSharpDirective>.Builder builder) Line 165	C#

It looks like the .resources file is not getting packed, I will need to figure out how to do that.

tree output in the source package:

F:\.nuget\packages\microsoft.dotnet.filebasedprograms\10.0.200-dev
│   .nupkg.metadata
│   Icon.png
│   microsoft.dotnet.filebasedprograms.10.0.200-dev.nupkg
│   microsoft.dotnet.filebasedprograms.10.0.200-dev.nupkg.sha512
│   microsoft.dotnet.filebasedprograms.nuspec
│
├───build
│       Microsoft.DotNet.FileBasedPrograms.targets
│
└───contentFiles
    └───cs
        ├───net9.0
        │   │   .editorconfig
        │   │   AppDirectiveHelpers.cs
        │   │   ExternalHelpers.cs
        │   │   MSBuildUtilities.cs
        │   │
        │   └───FileBasedProgramsResources.resx
        │           Microsoft.DotNet.FileBasedPrograms.FileBasedProgramsResources.cs
        │
        └───netstandard2.0
            │   .editorconfig
            │   AppDirectiveHelpers.cs
            │   ExternalHelpers.cs
            │   MSBuildUtilities.cs
            │
            └───FileBasedProgramsResources.resx
                    Microsoft.DotNet.FileBasedPrograms.FileBasedProgramsResources.cs

@RikkiGibson
Copy link
Member Author

I am unsure whether it is better to include the outputs of compiling the .resx/.xlf files, and add some kind of targets to ensure the resulting .resources get embedded into the resource assemblies of the consuming project, or, to actually embed the original .resx/.xlf as items and have the consuming project compile them.

I didn't see any examples of a source-only package which includes resource files. Do we have any examples of that in our org? Tagging @tmat @baronfel in case you happen to know (or know where to look.)

@tmat
Copy link
Member

tmat commented Oct 24, 2025

I am unsure whether it is better to include the outputs of compiling the .resx/.xlf files, and add some kind of targets to ensure the resulting .resources get embedded into the resource assemblies of the consuming project, or, to actually embed the original .resx/.xlf as items and have the consuming project compile them.

I didn't see any examples of a source-only package which includes resource files. Do we have any examples of that in our org? Tagging @tmat @baronfel in case you happen to know (or know where to look.)

Resources in source packages are PITA. Is it possible to avoid?

@RikkiGibson
Copy link
Member Author

RikkiGibson commented Oct 24, 2025

The resources are basically the error messages for bad #:property etc directives. It would be possible to require the consumer of the source package to supply the resource strings, and copy paste them into the .resx of each repo essentially, but it seems unfortunate.

This is the content that would be copied (and possibly translations performed again, unless, perhaps with comments we give the translators a heads up that there is another copy elsewhere to use as a reference.)

<data name="CouldNotFindAnyProjectInDirectory" xml:space="preserve">
<value>Could not find any project in `{0}`.</value>
</data>
<data name="CouldNotFindProjectOrDirectory" xml:space="preserve">
<value>Could not find project or directory `{0}`.</value>
</data>
<data name="MoreThanOneProjectInDirectory" xml:space="preserve">
<value>Found more than one project in `{0}`. Specify which one to use.</value>
</data>
<data name="PropertyDirectiveInvalidName" xml:space="preserve">
<value>Invalid property name: {0}</value>
<comment>{0} is an inner exception message.</comment>
</data>
<data name="PropertyDirectiveMissingParts" xml:space="preserve">
<value>The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'.</value>
<comment>{Locked="#:property"}</comment>
</data>
<data name="StaticGraphRestoreNotSupported" xml:space="preserve">
<value>Static graph restore is not supported for file-based apps. Remove the '#:property'.</value>
<comment>{Locked="#:property"}</comment>
</data>
<data name="DirectiveError" xml:space="preserve">
<value>error</value>
<comment>Used when reporting directive errors like "file(location): error: message".</comment>
</data>
<data name="InvalidDirectiveName" xml:space="preserve">
<value>The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'.</value>
<comment>{0} is the directive type like 'package' or 'sdk'. {1} is the expected separator like '@' or '='.</comment>
</data>
<data name="CannotConvertDirective" xml:space="preserve">
<value>Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway.</value>
<comment>{Locked="--force"}</comment>
</data>
<data name="DuplicateDirective" xml:space="preserve">
<value>Duplicate directives are not supported: {0}</value>
<comment>{0} is the directive type and name.</comment>
</data>
<data name="QuoteInDirective" xml:space="preserve">
<value>Directives currently cannot contain double quotes (").</value>
</data>
<data name="InvalidProjectDirective" xml:space="preserve">
<value>The '#:project' directive is invalid: {0}</value>
<comment>{0} is the inner error message.</comment>
</data>
<data name="MissingDirectiveName" xml:space="preserve">
<value>Missing name of '{0}'.</value>
<comment>{0} is the directive name like 'package' or 'sdk'.</comment>
</data>
<data name="UnrecognizedDirective" xml:space="preserve">
<value>Unrecognized directive '{0}'.</value>
<comment>{0} is the directive name like 'package' or 'sdk'.</comment>
</data>

@jjonescz
Copy link
Member

to actually embed the original .resx/.xlf as items and have the consuming project compile them.

I would do this and on the consuming side something like this could work?

<PackageReference Include="microsoft.dotnet.filebasedprograms" GeneratePathProperty="true" />

<EmbeddedResource Include="$(PkgMicrosoft_DotNet_FileBasedPrograms)contentfiles\cs\netstandard2.0\FileBasedProgramResources.resx" />

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants