Skip to content

Emit SDK-derived assembly attributes from the G# SDK - #2817

Merged
DavidObando merged 1 commit into
mainfrom
fix/issue-2814
Jul 27, 2026
Merged

Emit SDK-derived assembly attributes from the G# SDK#2817
DavidObando merged 1 commit into
mainfrom
fix/issue-2814

Conversation

@DavidObando

Copy link
Copy Markdown
Owner

Summary

  • render @(AssemblyAttribute) as file-level G# @assembly: annotations from a new WriteGsharpAssemblyInfoTask
  • generate and fold $(GeneratedAssemblyInfoFile) into @(Compile) before CoreCompile, keyed off the SDK's own inputs cache for incrementality
  • add unit coverage for rendering, G# escaping, positional-gap detection and write-only-when-changed

Problem

Microsoft.NET.GenerateAssemblyInfo.targets:189 gates CoreGenerateAssemblyInfo on '$(Language)'=='VB' or '$(Language)'=='C#'. build/Gsharp.NET.Sdk.props:4 sets <Language>Gsharp</Language>, so the target silently no-ops and every .gsproj shipped an assembly with no AssemblyTitle, AssemblyProduct, AssemblyCompany or AssemblyConfiguration. @(AssemblyAttribute) was already populated correctly by the standard GetAssemblyAttributes target — only the emit step was missing.

This violated ADR-0143 §D, which makes reproducing those attributes the G# SDK's job precisely because cs2gs deliberately drops the SDK-generated AssemblyInfo.cs and preserves only the project properties.

It surfaced in the migrated Oahu app, whose window title and header rendered as "Oahu.Foundation" instead of "Oahu":

App.axaml.gs:33   viewModel.Title = ApplEnv.AssemblyTitle ?? "Oahu"

ApplEnv.cs:27     AssemblyTitle =
                    GetAttribute<AssemblyTitleAttribute>()?.Title           // off EntryAssembly
                    ?? Path.GetFileNameWithoutExtension(ExecutingAssembly.Location);

With no AssemblyTitleAttribute on the entry assembly, ApplEnv fell through to GetExecutingAssembly() — which is Oahu.Foundation.dll, because ApplEnv lives there. The ?? "Oahu" guard never fired because AssemblyTitle was non-null.

Approach

WriteCodeFragment cannot be reused here: it is CodeDom-based and only emits C# and VB. Routing a generated .cs AssemblyInfo through _GsharpPartitionForeignCompile + gsgen back-translation does not work either — GeneratedDocTranslator drops compilation-unit-level [assembly: ...] lists and emits nothing at all for a type-free file (filed as #2815).

So the task renders the attributes as G# directly. It honors the same item metadata conventions WriteCodeFragment defines — positional _Parameter1_ParameterN, any other metadata as a named argument (name: value per ADR-0080), and a <name>_IsLiteral sibling for raw code — so a project that already hand-authors <AssemblyAttribute> items for a C# build keeps working unchanged after migration.

String escaping is G#-specific rather than inherited from C#: every G# interpreted string interpolates $ident/${expr}, so a literal $ is escaped as $$ (docs/lexical.md).

$(GeneratedAssemblyInfoFile) needs no override — the SDK already defaults it to $(IntermediateOutputPath)$(MSBuildProjectName).AssemblyInfo$(DefaultLanguageSourceExtension) and Sdk.props sets that extension to .gs.

Testing

  • dotnet test test/Sdk.Tests/Sdk.Tests.csproj — 57 passed
  • dotnet test tools/cs2gs/Cs2Gs.Tests/Cs2Gs.Tests.csproj — 1765 passed
  • all 10 e2etests/*.sh suites pass (sdk, gsgen, foreign-cs, nuget-pack, projectref, packageref, property, multitarget, multifile, debug-info, templates)

Verified end to end against the reported symptom with a G# app named Oahu referencing the real Oahu.Foundation.dll:

without fix:  ApplEnv.AssemblyTitle = Oahu.Foundation
with fix:     ApplEnv.AssemblyTitle = Oahu

Values round-trip through MetadataLoadContext with escaping intact (Probe $Company\Sub, My Probe "Title"), multi-parameter attributes (AssemblyMetadata) and InternalsVisibleTo are emitted correctly, a no-op rebuild does not rewrite the file, and changing <AssemblyTitle> regenerates it and reaches the emitted assembly.

Follow-ups

Closes #2814

Microsoft.NET.GenerateAssemblyInfo.targets gates CoreGenerateAssemblyInfo on
Language being C# or VB, so a .gsproj silently lost AssemblyTitle,
AssemblyProduct, AssemblyCompany and AssemblyConfiguration. ADR-0143 section D
already assigns reproducing those to the G# SDK; only the emit step was missing.

WriteCodeFragment cannot be reused because it is CodeDom-based and only knows
C# and VB, so render the same @(AssemblyAttribute) item group as file-level G#
@assembly: annotations instead, honoring MSBuild's positional, named and
_IsLiteral metadata conventions.
@DavidObando
DavidObando merged commit 17feb7b into main Jul 27, 2026
10 checks passed
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.

G# SDK never emits the SDK-derived AssemblyInfo attributes (AssemblyTitle/Product/Company/Configuration)

1 participant