Skip to content

Commit

Permalink
Refactored code generation to use custom attribute for generation.
Browse files Browse the repository at this point in the history
Definition files are no longer required to be added as AdditionalFiles
  • Loading branch information
almostchristian committed Oct 16, 2023
1 parent d620ed8 commit 88836ee
Show file tree
Hide file tree
Showing 20 changed files with 829 additions and 272 deletions.
8 changes: 8 additions & 0 deletions SampleSourceGen/Education.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using Microsoft.Health.Fhir.CodeGeneration;

namespace SampleSourceGen;

[GeneratedFhir("Education.StructureDefinition.json")]
public partial class Education
{
}
8 changes: 8 additions & 0 deletions SampleSourceGen/Models/Patient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using Microsoft.Health.Fhir.CodeGeneration;

namespace SampleSourceGen.Models;

[GeneratedFhir("Models/Patient.StructureDefinition.json")]
public partial class Patient
{
}
2 changes: 1 addition & 1 deletion SampleSourceGen/Models/Pokemon.StructureDefinition.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"version": "0.1",
"name": "Pokemon",
"status": "draft",
"fhirVersion": "3.0.1",
"fhirVersion": "4.3.0",
"kind": "resource",
"abstract": false,
"type": "Pokemon",
Expand Down
10 changes: 10 additions & 0 deletions SampleSourceGen/Models/Pokemon.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Microsoft.Health.Fhir.CodeGeneration;

namespace SampleSourceGen.Models;

[GeneratedFhir(
"Models/Pokemon.StructureDefinition.json",
TerminologyResources = new[] { "Models/PokemonType.CodeSystem.json", "Models/PokemonType.ValueSet.json" })]
public partial class Pokemon
{
}
1 change: 1 addition & 0 deletions SampleSourceGen/Models/PokemonType.CodeSystem.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"status": "active",
"date": "2023-08-01",
"publisher": "The Pokemon Company",
"content": "complete",
"concept": [
{
"code": "normal",
Expand Down
4 changes: 3 additions & 1 deletion SampleSourceGen/SampleSourceGen.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<OutputType>Exe</OutputType>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GeneratedFiles</CompilerGeneratedFilesOutputPath>
</PropertyGroup>

<ItemGroup>
Expand All @@ -17,7 +19,7 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\src\fhirCsR2\fhirCsR2.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" Visible="false" />
<ProjectReference Include="..\src\Microsoft.Health.Fhir.CodeGeneration\Microsoft.Health.Fhir.CodeGeneration.csproj" />
<ProjectReference Include="..\src\Microsoft.Health.Fhir.CodeGenCommon\Microsoft.Health.Fhir.CodeGenCommon.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" Visible="false" />
<ProjectReference Include="..\src\Microsoft.Health.Fhir.SourceGenerator\Microsoft.Health.Fhir.SourceGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
<ProjectReference Include="..\src\Microsoft.Health.Fhir.SpecManager\Microsoft.Health.Fhir.SpecManager.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" Visible="false" />
Expand Down
22 changes: 4 additions & 18 deletions TestSourceGen/Program.cs
Original file line number Diff line number Diff line change
@@ -1,26 +1,12 @@
// See https://aka.ms/new-console-template for more information

using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.Health.Fhir.SourceGenerator;
using Microsoft.Health.Fhir.SpecManager.Converters;
using Microsoft.Health.Fhir.SpecManager.Language;
using Microsoft.Health.Fhir.SpecManager.Manager;
using Microsoft.Health.Fhir.SourceGenerator.Parsing;

var generator = new FhirSourceGenerator();
var resourceClass = new ResourcePartialClass(typeof(Program).Namespace!, "Patient", "Patient.StructureDefinition.json", Array.Empty<string>());

var fhirConverter = ConverterHelper.ConverterForVersion(FhirPackageCommon.FhirSequenceEnum.R4B);
var emitter = new Emitter(resourceClass, diag => Console.Error.WriteLine(diag.GetMessage()));

var fhirInfo = new FhirVersionInfo(FhirPackageCommon.FhirSequenceEnum.R4B);

var complex = generator.ProcessFile("Patient.StructureDefinition.json", fhirInfo, fhirConverter, m => m.Resources, out var fileName, out var canonical, out var artifactClass);

ILanguage language = LanguageHelper.GetLanguages("CSharpFirely2")[0];
using var memoryStream = new MemoryStream(short.MaxValue);
language.Export(fhirInfo, complex, memoryStream);

memoryStream.Seek(0, SeekOrigin.Begin);
StreamReader reader = new StreamReader(memoryStream, Encoding.UTF8);
var code = await reader.ReadToEndAsync();
var code = emitter.Emit();

Console.WriteLine(code);
9 changes: 8 additions & 1 deletion fhir-codegen.sln
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.editorconfig = .editorconfig
.gitignore = .gitignore
CODE_OF_CONDUCT.md = CODE_OF_CONDUCT.md
src\Directory.Build.props = src\Directory.Build.props
.github\workflows\docs.yaml = .github\workflows\docs.yaml
GeoPol.xml = GeoPol.xml
LICENSE = LICENSE
Expand Down Expand Up @@ -36,7 +37,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleSourceGen", "SampleSo
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.Fhir.SourceGenerator", "src\Microsoft.Health.Fhir.SourceGenerator\Microsoft.Health.Fhir.SourceGenerator.csproj", "{213EE0DD-ECE8-47AF-8301-42BEAE4EFE54}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestSourceGen", "TestSourceGen\TestSourceGen.csproj", "{956E4954-BBC8-4471-B53C-9424F1FE681A}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestSourceGen", "TestSourceGen\TestSourceGen.csproj", "{956E4954-BBC8-4471-B53C-9424F1FE681A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Health.Fhir.CodeGeneration", "src\Microsoft.Health.Fhir.CodeGeneration\Microsoft.Health.Fhir.CodeGeneration.csproj", "{37BFB554-48B3-4BBA-83ED-415956898C48}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -80,6 +83,10 @@ Global
{956E4954-BBC8-4471-B53C-9424F1FE681A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{956E4954-BBC8-4471-B53C-9424F1FE681A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{956E4954-BBC8-4471-B53C-9424F1FE681A}.Release|Any CPU.Build.0 = Release|Any CPU
{37BFB554-48B3-4BBA-83ED-415956898C48}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{37BFB554-48B3-4BBA-83ED-415956898C48}.Debug|Any CPU.Build.0 = Debug|Any CPU
{37BFB554-48B3-4BBA-83ED-415956898C48}.Release|Any CPU.ActiveCfg = Release|Any CPU
{37BFB554-48B3-4BBA-83ED-415956898C48}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
41 changes: 41 additions & 0 deletions src/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<Project>

<Target Name="IncludeAnalyzersInPackage"
Condition="'@(ProjectReference)' != '' and @(ProjectReference->AnyHaveMetadataValue('PackAsAnalyzer', 'true'))">
<MSBuild Projects="@(ProjectReference->WithMetadataValue('PackAsAnalyzer', 'true'))"
Targets="GetAnalyzerPackFiles"
RemoveProperties="SetTargetFramework">
<Output TaskParameter="TargetOutputs" ItemName="_AnalyzerFile" />
</MSBuild>

<ItemGroup>
<Content Include="@(_AnalyzerFile)" Pack="True" Condition="!%(_AnalyzerFile.IsSymbol)" />
<!-- Symbols don't honor PackagePath. By default they are placed in lib/%(TargetFramework).
Pack does honor TargetPath and does Path.Combine("lib/%(TargetFramework)", "%(TargetPath)"),
so a rooted path value for TargetPath will override lib.
https://github.com/NuGet/Home/issues/10860 -->
<_TargetPathsToSymbols Include="@(_AnalyzerFile)" TargetPath="/%(_AnalyzerFile.PackagePath)" Condition="%(_AnalyzerFile.IsSymbol)" />
</ItemGroup>
</Target>

<Target Name="GetAnalyzerPackFiles"
DependsOnTargets="$(GenerateNuspecDependsOn)"
Returns="@(_AnalyzerPackFile)">
<PropertyGroup>
<_analyzerPath>analyzers/dotnet/cs</_analyzerPath>
<_analyzerPath Condition="'$(AnalyzerRoslynVersion)' != ''">$(_analyzerPath)/roslyn$(AnalyzerRoslynVersion)</_analyzerPath>
<_analyzerPath Condition="'$(AnalyzerLanguage)' != ''">$(_analyzerPath)/$(AnalyzerLanguage)</_analyzerPath>
</PropertyGroup>

<!-- Filter on netstandard2.0 so that generator projects can multi-target for the purpose of enabling nullable reference type compiler checks. -->
<ItemGroup>
<_AnalyzerPackFile Include="@(_BuildOutputInPackage->WithMetadataValue('TargetFramework', 'netstandard2.0'))" IsSymbol="false" />
<_AnalyzerPackFile Include="@(_TargetPathsToSymbols->WithMetadataValue('TargetFramework', 'netstandard2.0'))" IsSymbol="true" />
<_AnalyzerPackFile PackagePath="$(_analyzerPath)/%(TargetPath)" />
</ItemGroup>

<Error Text="Analyzers must target netstandard2.0 since they run in the compiler which targets netstandard2.0. $(MSBuildProjectFullPath) targets '$([MSBuild]::ValueOrDefault('$(TargetFrameworks)', '$(TargetFramework)'))' instead."
Condition="'@(_AnalyzerPackFile)' == ''" />
</Target>

</Project>
25 changes: 25 additions & 0 deletions src/Microsoft.Health.Fhir.CodeGeneration/GeneratedFhirAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;

namespace Microsoft.Health.Fhir.CodeGeneration;

[AttributeUsage(AttributeTargets.Class)]
public class GeneratedFhirAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="GeneratedFhirAttribute"/> class.
/// </summary>
public GeneratedFhirAttribute(string structureDefinitionPath)
{
StructureDefinitionPath = structureDefinitionPath;
}

/// <summary>
/// The relative path to the structure definition resource json file.
/// </summary>
public string StructureDefinitionPath { get; }

/// <summary>
/// The relative paths to the terminology resource (CodeSystem/ValueSet) json files.
/// </summary>
public string[] TerminologyResources { get; set; } = Array.Empty<string>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>10.0</LangVersion>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<DevelopmentDependency>true</DevelopmentDependency>
<PackOnBuild>true</PackOnBuild>
<PackFolder>analyzers\cs</PackFolder>
<Version>0.1.0-beta.2</Version>
</PropertyGroup>

<PropertyGroup>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<BeforePack>$(BeforePack);IncludeAnalyzersInPackage;</BeforePack>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="7.0.0" PrivateAssets="all" GeneratePathProperty="true" />
<PackageReference Include="System.Text.Json" Version="7.0.2" PrivateAssets="all" GeneratePathProperty="true" />
<PackageReference Include="System.Text.Encodings.Web" Version="7.0.0" PrivateAssets="all" GeneratePathProperty="true" />
<PackageReference Include="DynamicLanguageRuntime" Version="1.3.3" PrivateAssets="all" GeneratePathProperty="true" />
</ItemGroup>

<ItemGroup>
<None Include="$(PKGSystem_Text_Json)\lib\netstandard2.0\System.Text.Json.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
<None Include="$(PKGMicrosoft_Bcl_AsyncInterfaces)\lib\netstandard2.0\Microsoft.Bcl.AsyncInterfaces.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
<None Include="$(PKGSystem_Text_Encodings_Web)\lib\netstandard2.0\System.Text.Encodings.Web.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
<None Include="$(PKGDynamicLanguageRuntime)\lib\netstandard2.0\Microsoft.Dynamic.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
<None Include="$(PKGDynamicLanguageRuntime)\lib\netstandard2.0\Microsoft.Scripting.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
<None Include="$(PKGDynamicLanguageRuntime)\lib\netstandard2.0\Microsoft.Scripting.Metadata.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
</ItemGroup>

<ItemGroup>
<ProjectReference
Include="..\Microsoft.Health.Fhir.SourceGenerator\Microsoft.Health.Fhir.SourceGenerator.csproj"
ReferenceOutputAssembly="false"
OutputItemType="Analyzer"
PackAsAnalyzer="true" />
<ProjectReference Include="..\fhirCsR2\fhirCsR2.csproj"
ReferenceOutputAssembly="false"
OutputItemType="Analyzer"
PackAsAnalyzer="true" />
<ProjectReference Include="..\Microsoft.Health.Fhir.CodeGenCommon\Microsoft.Health.Fhir.CodeGenCommon.csproj"
ReferenceOutputAssembly="false"
OutputItemType="Analyzer"
PackAsAnalyzer="true" />
<ProjectReference Include="..\Microsoft.Health.Fhir.SpecManager\Microsoft.Health.Fhir.SpecManager.csproj"
ReferenceOutputAssembly="false"
OutputItemType="Analyzer"
PackAsAnalyzer="true" />
</ItemGroup>
</Project>
46 changes: 46 additions & 0 deletions src/Microsoft.Health.Fhir.SourceGenerator/DiagnosticDescriptors.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using Microsoft.CodeAnalysis;

namespace Microsoft.Health.Fhir.SourceGenerator;

internal static class DiagnosticDescriptors
{
public static readonly DiagnosticDescriptor FailedArtifactDef = new(
"FHIRGEN001",
"Invalid artifact file",
"Unable to parse {1} file '{0}'. StructureDefinition files must have a snapshot.",
"FhirSourceGenerator",
DiagnosticSeverity.Warning,
true);

public static readonly DiagnosticDescriptor TypeLoaderException = new(
"FHIRGEN002",
"Fatal FhirCode Generation error",
"Type loader exception '{0}'.",
"FhirSourceGenerator",
DiagnosticSeverity.Error,
true);

public static readonly DiagnosticDescriptor UnhandledException = new(
"FHIRGEN003",
"Fatal FhirCode Generation error",
"Unhandled exception '{0}'.",
"FhirSourceGenerator",
DiagnosticSeverity.Error,
true);

public static readonly DiagnosticDescriptor FailedToGenerate = new(
"FHIRGEN004",
"Invalid .StructureDefinition.json file",
"Failed to generate code for json file [{0}]{1}.",
"FhirSourceGenerator",
DiagnosticSeverity.Error,
true);

public static readonly DiagnosticDescriptor ProcessSuccess = new(
"FHIRGEN005",
"Parsing success",
"JsonPath: {0} Canonical: {1}, Artifact Type: {2}, ResourceCount: {3}",
"FhirSourceGenerator",
DiagnosticSeverity.Info,
true);
}
Loading

0 comments on commit 88836ee

Please sign in to comment.