Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@
<ItemGroup>
<CompilerVisibleProperty Include="ResourcePackerOutputFileName" />
<CompilerVisibleProperty Include="ResourcePackerDirectory" />
<CompilerVisibleProperty Include="RootNamespace" />
</ItemGroup>
</Project>
15 changes: 8 additions & 7 deletions LuYao.ResourcePacker.SourceGenerator/ResourcePackageGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ private static void Execute(SourceProductionContext context, Compilation compila
var className = "R";

// Get root namespace from compilation options or default to assembly name
var rootNamespace = GetRootNamespace(compilation);
var rootNamespace = GetRootNamespace(compilation, configOptions);

// Visibility is always internal
var visibility = "internal";
Expand All @@ -82,14 +82,15 @@ private static void Execute(SourceProductionContext context, Compilation compila
context.AddSource($"{className}.g.cs", SourceText.From(source, Encoding.UTF8));
}

private static string GetRootNamespace(Compilation compilation)
private static string GetRootNamespace(Compilation compilation, AnalyzerConfigOptionsProvider configOptions)
{
// Try to get RootNamespace from compilation options
// First, check if there's a global namespace option
if (compilation.Options is CSharpCompilationOptions csharpOptions)
// Try to get RootNamespace from analyzer config (MSBuild property)
if (configOptions.GlobalOptions.TryGetValue("build_property.RootNamespace", out var rootNamespace))
{
// Look for MSBuild properties that might contain the root namespace
// The RootNamespace is typically passed through analyzer config
if (!string.IsNullOrWhiteSpace(rootNamespace))
{
return rootNamespace;
}
}

// Default to assembly name if no explicit root namespace is found
Expand Down
3 changes: 3 additions & 0 deletions LuYao.ResourcePacker.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
<Project Path="examples/Lib2/Lib2.csproj" />
<Project Path="examples/Lib3/Lib3.csproj" />
</Folder>
<Folder Name="/samples/">
<Project Path="samples/RootNamespaceTest/RootNamespaceTest.csproj" />
</Folder>
<Project Path="LuYao.ResourcePacker.MSBuild/LuYao.ResourcePacker.MSBuild.csproj" />
<Project Path="LuYao.ResourcePacker.SourceGenerator/LuYao.ResourcePacker.SourceGenerator.csproj" />
<Project Path="LuYao.ResourcePacker.Tests/LuYao.ResourcePacker.Tests.csproj" />
Expand Down
49 changes: 49 additions & 0 deletions samples/RootNamespaceTest/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System;
using System.Threading.Tasks;
using LuYao.ResourcePacker;

namespace Popcorn.Toolkit
{
/// <summary>
/// Test program to verify that the generated R class respects the RootNamespace property.
/// When RootNamespace is set to "Popcorn.Toolkit", the R class should be in this namespace.
/// </summary>
public class Program
{
public static async Task Main(string[] args)
{
Console.WriteLine("RootNamespace Test - Verifying generated R class namespace");
Console.WriteLine("=============================================================");
Console.WriteLine();

// This test verifies that R class is in Popcorn.Toolkit namespace
// If RootNamespace property is not respected, this won't compile
Console.WriteLine($"R class is accessible in namespace: {typeof(R).Namespace}");
Console.WriteLine($"Expected namespace: Popcorn.Toolkit");
Console.WriteLine();

// List available resource keys
Console.WriteLine("Available resource keys:");
Console.WriteLine($" - R.Keys.sample: {R.Keys.sample}");
Console.WriteLine($" - R.Keys.config: {R.Keys.config}");
Console.WriteLine();

// Test reading resources
try
{
var sampleText = await R.ReadSampleAsyncAsString();
Console.WriteLine($"Sample resource content: {sampleText}");

var configJson = await R.ReadConfigAsyncAsString();
Console.WriteLine($"Config resource content: {configJson}");

Console.WriteLine();
Console.WriteLine("✓ Test PASSED: R class is in the correct namespace (Popcorn.Toolkit)");
}
catch (Exception ex)
{
Console.WriteLine($"✗ Error reading resources: {ex.Message}");
}
}
}
}
61 changes: 61 additions & 0 deletions samples/RootNamespaceTest/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# RootNamespace Test Sample

This sample project demonstrates and validates that the source generator correctly respects the `RootNamespace` property in the .csproj file.

## Purpose

When a project sets `<RootNamespace>Popcorn.Toolkit</RootNamespace>` in its .csproj file, the generated `R` class should be placed in the `Popcorn.Toolkit` namespace rather than defaulting to the assembly name.

## Project Configuration

The project is configured with:
- **AssemblyName**: RootNamespaceTest
- **RootNamespace**: Popcorn.Toolkit
- **Resources**: Contains sample.txt and config.json

## Expected Behavior

The source generator should:
1. Read the `RootNamespace` property from MSBuild
2. Generate the `R` class in the `Popcorn.Toolkit` namespace
3. The Program.cs can access `R` class directly since it's in the same namespace

## Running the Test

From the repository root:
```bash
dotnet run --project samples/RootNamespaceTest/RootNamespaceTest.csproj
```

Or from this directory:
```bash
dotnet run
```

Expected output:
```
RootNamespace Test - Verifying generated R class namespace
=============================================================

R class is accessible in namespace: Popcorn.Toolkit
Expected namespace: Popcorn.Toolkit

Available resource keys:
- R.Keys.sample: sample
- R.Keys.config: config

Sample resource content: This is a sample resource file for testing RootNamespace property.
Config resource content: {
"message": "Hello from RootNamespace test!",
"version": "1.0"
}

✓ Test PASSED: R class is in the correct namespace (Popcorn.Toolkit)
```

## Verification

The test program verifies the fix by:
1. Accessing the `R` class without namespace qualification (proving it's in the same `Popcorn.Toolkit` namespace as Program.cs)
2. Using reflection to verify the namespace is exactly `Popcorn.Toolkit`
3. Reading resources to ensure the generated code functions correctly
4 changes: 4 additions & 0 deletions samples/RootNamespaceTest/Resources/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"message": "Hello from RootNamespace test!",
"version": "1.0"
}
1 change: 1 addition & 0 deletions samples/RootNamespaceTest/Resources/sample.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This is a sample resource file for testing RootNamespace property.
58 changes: 58 additions & 0 deletions samples/RootNamespaceTest/RootNamespaceTest.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<RootNamespace>Popcorn.Toolkit</RootNamespace>
<ResourcePackerEnabled>true</ResourcePackerEnabled>
<ResourcePackerDirectory>Resources</ResourcePackerDirectory>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\LuYao.ResourcePacker\LuYao.ResourcePacker.csproj" />
<ProjectReference Include="..\..\LuYao.ResourcePacker.MSBuild\LuYao.ResourcePacker.MSBuild.csproj" />
<ProjectReference Include="..\..\LuYao.ResourcePacker.SourceGenerator\LuYao.ResourcePacker.SourceGenerator.csproj"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false" />
</ItemGroup>

<ItemGroup>
<None Include="Resources\**\*" CopyToOutputDirectory="Never" />
</ItemGroup>

<!-- Add resource files as AdditionalFiles for source generator -->
<ItemGroup>
<AdditionalFiles Include="Resources\**\*" Visible="false" />
</ItemGroup>

<!-- For development with project references, manually import and override assembly path -->
<UsingTask TaskName="LuYao.ResourcePacker.MSBuild.ResourcePackerTask"
AssemblyFile="..\..\LuYao.ResourcePacker.MSBuild\bin\$(Configuration)\netstandard2.0\LuYao.ResourcePacker.MSBuild.dll" />

<Import Project="..\..\LuYao.ResourcePacker.MSBuild\build\LuYao.ResourcePacker.MSBuild.props" />

<!-- Override the targets to use local source generator for development -->
<Target Name="PackResources" BeforeTargets="AssignTargetPaths" Condition="'$(ResourcePackerEnabled)' == 'true'">
<PropertyGroup>
<ResourcePackerOutputFileName Condition="'$(ResourcePackerOutputFileName)' == ''">$(AssemblyName).dat</ResourcePackerOutputFileName>
</PropertyGroup>
<ResourcePackerTask
ProjectDir="$(ProjectDir)"
OutputPath="$(OutputPath)"
AssemblyName="$(AssemblyName)"
ResourceDirectory="$(ResourcePackerDirectory)"
OutputFileName="$(ResourcePackerOutputFileName)" />

<!-- Add .dat file to None so it gets copied to referencing projects -->
<ItemGroup>
<None Include="$(OutputPath)$(ResourcePackerOutputFileName)">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Link>$(ResourcePackerOutputFileName)</Link>
</None>
</ItemGroup>
</Target>

</Project>