Skip to content

Commit 50b70b9

Browse files
committed
Fixed generating sources.
Signed-off-by: AraHaan <seandhunt_7@yahoo.com>
1 parent ef7c9e9 commit 50b70b9

8 files changed

+242
-59
lines changed

GenerationFailedException.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,4 @@ protected GenerationFailedException(SerializationInfo info, StreamingContext con
2525
{
2626
}
2727
}
28-
}
28+
}

GeneratorOptions.cs

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,13 @@
11
namespace GitBuildInfo.SourceGenerator
22
{
3-
/// <summary>
4-
/// Describes the options that feed into code generation.
5-
/// </summary>
3+
using System.Text.Json.Serialization;
4+
65
public record GeneratorOptions
76
{
8-
/// <summary>
9-
/// Gets the type to use to apply the attribute to embed the git information in that is within the assembly it is being applied to.
10-
/// </summary>
11-
/// <value>The type to use to apply the attribute to embed the git information in that is within the assembly it is being applied to.</value>
7+
[JsonPropertyName("AssemblyType")]
128
public string? AssemblyType { get; set; }
139

14-
/// <summary>
15-
/// Gets if the type specified in AssemblyType is a generic type, by default this is set to false to indicate that the type is not a generic type.
16-
/// </summary>
17-
/// <value>If the type specified in AssemblyType is a generic type, by default this is set to false to indicate that the type is not a generic type.</value>
10+
[JsonPropertyName("IsGeneric")]
1811
public bool IsGeneric { get; set; }
1912
}
2013
}

GitBuildInfo.SourceGenerator.csproj

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
<TargetFramework>netstandard2.0</TargetFramework>
66
<LangVersion>latest</LangVersion>
77
<Nullable>enable</Nullable>
8-
<Version>1.0.3</Version>
9-
<PackageReleaseNotes>Replaced property inits with setters on GeneratorOptions.</PackageReleaseNotes>
8+
<Version>1.0.4</Version>
9+
<PackageReleaseNotes>Fixed generating sources.</PackageReleaseNotes>
1010
<Company>Els_kom org.</Company>
1111
<Authors>Els_kom org.</Authors>
1212
<Copyright>Copyright (c) 2021</Copyright>
@@ -39,9 +39,7 @@
3939
<ItemGroup>
4040
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.10.0-1.final" />
4141
<PackageReference Include="Nullable" Version="1.3.0" />
42-
<PackageReference Include="System.Reflection.Metadata" Version="6.0.0-preview.2.21154.6" />
4342
<PackageReference Include="System.Text.Json" Version="6.0.0-preview.2.21154.6" />
44-
<PackageReference Include="System.Memory" Version="4.5.4" PrivateAssets="none" />
4543
</ItemGroup>
4644

4745
<Import Project="$(MSBuildProjectName).targets" />

GitBuildInfo.SourceGenerator.nuspec

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,7 @@
1414
<copyright>Copyright (c) 2021</copyright>
1515
<repository type="git" url="https://github.com/Elskom/GitBuildInfo.SourceGenerator.git" />
1616
<dependencies>
17-
<group targetFramework=".NETStandard2.0">
18-
<dependency id="System.Memory" version="4.5.4" include="All" />
19-
</group>
17+
<group targetFramework=".NETStandard2.0" />
2018
</dependencies>
2119
</metadata>
2220
<files>

GitInfo.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
namespace GitBuildInfo.SourceGenerator
2+
{
3+
using System.Text.Json.Serialization;
4+
5+
public record GitInfo
6+
{
7+
[JsonPropertyName("GitHead")]
8+
public string? GitHead { get; set; }
9+
10+
[JsonPropertyName("CommitHash")]
11+
public string? CommitHash { get; set; }
12+
13+
[JsonPropertyName("GitBranch")]
14+
public string? GitBranch { get; set; }
15+
}
16+
}

SourceGenerator.cs

Lines changed: 117 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
namespace GitBuildInfo.SourceGenerator
22
{
33
using System;
4-
using System.ComponentModel;
5-
using System.Diagnostics;
6-
using System.Globalization;
74
using System.IO;
85
using System.Linq;
96
using System.Text;
107
using System.Text.Json;
118
using Microsoft.CodeAnalysis;
129
using Microsoft.CodeAnalysis.CSharp;
10+
using Microsoft.CodeAnalysis.CSharp.Syntax;
11+
using Microsoft.CodeAnalysis.Text;
1312

1413
/// <summary>
1514
/// Source Generator for dumping git build information into a assembly level attribute on the compilation.
@@ -30,15 +29,28 @@ public void Execute(GeneratorExecutionContext context)
3029
return;
3130
}
3231

32+
_ = context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.projectdir", out var projectdir);
3333
var gitBuildInfoJsonFile = context.AdditionalFiles
3434
.FirstOrDefault(af => string.Equals(Path.GetFileName(af.Path), "GitBuildInfo.json", StringComparison.OrdinalIgnoreCase));
35-
if (gitBuildInfoJsonFile is null)
35+
var gitInfoJsonFile = context.AdditionalFiles
36+
.FirstOrDefault(af => string.Equals(Path.GetFileName(af.Path), "GitInfo.json", StringComparison.OrdinalIgnoreCase));
37+
if (gitBuildInfoJsonFile is null || gitInfoJsonFile is null)
3638
{
3739
return;
3840
}
39-
41+
42+
var jsonStr = gitBuildInfoJsonFile.GetText(context.CancellationToken)!.ToString();
4043
var options = JsonSerializer.Deserialize<GeneratorOptions>(
41-
gitBuildInfoJsonFile.GetText(context.CancellationToken)!.ToString(),
44+
jsonStr,
45+
new JsonSerializerOptions
46+
{
47+
AllowTrailingCommas = true,
48+
ReadCommentHandling = JsonCommentHandling.Skip,
49+
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
50+
});
51+
var jsonStr2 = gitInfoJsonFile.GetText(context.CancellationToken)!.ToString();
52+
var gitInfo = JsonSerializer.Deserialize<GitInfo>(
53+
jsonStr2,
4254
new JsonSerializerOptions
4355
{
4456
AllowTrailingCommas = true,
@@ -51,6 +63,7 @@ public void Execute(GeneratorExecutionContext context)
5163
}
5264

5365
var splitted = options!.AssemblyType!.Contains(".") ? options!.AssemblyType.Split('.') : new string[] { };
66+
var splitted2 = new Span<string>(splitted, 0, splitted.Length - 1);
5467
var splittedLen = splitted.Length;
5568
var usingStr = new StringBuilder();
5669
var gitinformationNamespace = "Elskom.Generic.Libs";
@@ -59,50 +72,114 @@ public void Execute(GeneratorExecutionContext context)
5972
// skip the last value.
6073
if (value != splitted[splittedLen - 1])
6174
{
62-
_ = usingStr.Append(value != splitted[0] ? "." : value);
75+
_ = usingStr.Append(value != splitted[splittedLen - 2] ? $"{value}." : value);
6376
}
6477
}
6578

66-
context.AddSource("GitAssemblyInfo.g.cs",
67-
string.Format(
68-
CultureInfo.InvariantCulture,
69-
$"// <autogenerated/>{0}{1}{0}{0}[assembly: GitInformationAttribute(\"{2}\", \"{3}\", \"{4}\", typeof({5}{6}))]{0}",
70-
Environment.NewLine,
71-
splittedLen > 0 && !string.Equals(
72-
gitinformationNamespace,
73-
usingStr.ToString(),
74-
StringComparison.Ordinal) ? $"using {usingStr};{Environment.NewLine}using {gitinformationNamespace};" : $"using {gitinformationNamespace};",
75-
this.RunGit("describe --all --always --dirty", Directory.GetParent(gitBuildInfoJsonFile.Path).FullName),
76-
this.RunGit("rev-parse --short HEAD", Directory.GetParent(gitBuildInfoJsonFile.Path).FullName),
77-
this.RunGit("name-rev --name-only HEAD", Directory.GetParent(gitBuildInfoJsonFile.Path).FullName),
78-
splittedLen > 0 ? splitted[splittedLen - 1] : options!.AssemblyType,
79-
options!.IsGeneric ? "<>" : string.Empty));
79+
var generated = GenerateCode(
80+
options,
81+
splitted2.ToArray(),
82+
gitinformationNamespace,
83+
gitInfo!.GitHead!,
84+
gitInfo.CommitHash!,
85+
gitInfo.GitBranch!,
86+
splittedLen > 0 ? splitted[splittedLen - 1] : options!.AssemblyType);
87+
context.AddSource("GitAssemblyInfo.g.cs", SourceText.From(generated.ToFullString(), Encoding.UTF8));
8088
}
8189

82-
private string RunGit(string arguments, string workingDirectory)
90+
private static CompilationUnitSyntax GenerateCode(GeneratorOptions options, string[] usings, string originalnamespace, string arg1, string arg2, string arg3, string typeName)
91+
=> SyntaxFactory.CompilationUnit().WithUsings(
92+
SyntaxFactory.List(
93+
string.Equals(string.Join(".", usings), originalnamespace, StringComparison.Ordinal)
94+
? new UsingDirectiveSyntax[] {
95+
AddUsing(new string[] { "Elskom", "Generic", "Libs" }, true)
96+
}
97+
: new UsingDirectiveSyntax[] {
98+
AddUsing(new string[] { "Elskom", "Generic", "Libs" }, true),
99+
AddUsing(usings, false)
100+
}))
101+
.WithAttributeLists(
102+
SyntaxFactory.SingletonList(
103+
SyntaxFactory.AttributeList(
104+
SyntaxFactory.SingletonSeparatedList(
105+
SyntaxFactory.Attribute(SyntaxFactory.IdentifierName("GitInformationAttribute"))
106+
.WithArgumentList(
107+
SyntaxFactory.AttributeArgumentList(
108+
SyntaxFactory.SeparatedList<AttributeArgumentSyntax>(
109+
MakeAttributeArgumentList(options, new string[] { arg1, arg2, arg3 }, typeName))))))
110+
.WithOpenBracketToken(
111+
SyntaxFactory.Token(
112+
SyntaxFactory.TriviaList(SyntaxFactory.LineFeed),
113+
SyntaxKind.OpenBracketToken,
114+
SyntaxFactory.TriviaList()))
115+
.WithTarget(
116+
SyntaxFactory.AttributeTargetSpecifier(SyntaxFactory.Token(SyntaxKind.AssemblyKeyword))
117+
.WithColonToken(
118+
SyntaxFactory.Token(
119+
SyntaxFactory.TriviaList(),
120+
SyntaxKind.ColonToken,
121+
SyntaxFactory.TriviaList(SyntaxFactory.Space))))
122+
.WithCloseBracketToken(
123+
SyntaxFactory.Token(
124+
SyntaxFactory.TriviaList(),
125+
SyntaxKind.CloseBracketToken,
126+
SyntaxFactory.TriviaList(SyntaxFactory.LineFeed)))));
127+
128+
private static UsingDirectiveSyntax AddUsing(string[] strings, bool autogeneratedheader)
83129
{
84-
using var pro1 = new Process();
85-
pro1.StartInfo.FileName = "git";
86-
pro1.StartInfo.Arguments = arguments;
87-
pro1.StartInfo.RedirectStandardOutput = true;
88-
pro1.StartInfo.UseShellExecute = false;
89-
pro1.StartInfo.CreateNoWindow = true;
90-
pro1.StartInfo.WorkingDirectory = workingDirectory;
91-
try
130+
NameSyntax? qualifiedName = null;
131+
for (var index = 0; index < strings.Length; index++)
92132
{
93-
_ = pro1.Start();
94-
var git_out = pro1.StandardOutput.ReadToEnd();
95-
pro1.WaitForExit();
96-
97-
// handle all cases of possible endlines.
98-
git_out = git_out.Replace("\r\n", string.Empty);
99-
git_out = git_out.Replace("\n", string.Empty);
100-
return git_out.Replace("\r", string.Empty);
133+
if (index == 0 && strings.Length > 1)
134+
{
135+
qualifiedName = SyntaxFactory.QualifiedName(
136+
SyntaxFactory.IdentifierName(strings[index]),
137+
SyntaxFactory.IdentifierName(strings[index + 1]));
138+
index++;
139+
}
140+
else
141+
{
142+
qualifiedName = strings.Length == 1
143+
? SyntaxFactory.IdentifierName(strings[index])
144+
: SyntaxFactory.QualifiedName(qualifiedName!, SyntaxFactory.IdentifierName(strings[index]));
145+
}
101146
}
102-
catch (Win32Exception)
147+
148+
return SyntaxFactory.UsingDirective(qualifiedName!)
149+
.WithUsingKeyword(SyntaxFactory.Token(
150+
SyntaxFactory.TriviaList(autogeneratedheader ? new[] { SyntaxFactory.Comment("// <autogenerated/>"), SyntaxFactory.LineFeed } : Array.Empty<SyntaxTrivia>()),
151+
SyntaxKind.UsingKeyword,
152+
SyntaxFactory.TriviaList(SyntaxFactory.Space)))
153+
.WithSemicolonToken(SyntaxFactory.Token(
154+
SyntaxFactory.TriviaList(),
155+
SyntaxKind.SemicolonToken,
156+
SyntaxFactory.TriviaList(SyntaxFactory.LineFeed)));
157+
}
158+
159+
private static SyntaxNodeOrToken[] MakeAttributeArgumentList(GeneratorOptions options, string[] args, string typeName)
160+
{
161+
var lst = new SyntaxNodeOrToken[7];
162+
var lstIndex = 0;
163+
foreach (var arg in args)
103164
{
104-
return "Not a git clone or git is not in Path.";
165+
lst[lstIndex] = SyntaxFactory.AttributeArgument(
166+
SyntaxFactory.LiteralExpression(
167+
SyntaxKind.StringLiteralExpression,
168+
SyntaxFactory.Literal(arg)));
169+
lstIndex++;
170+
lst[lstIndex] = SyntaxFactory.Token(
171+
SyntaxFactory.TriviaList(),
172+
SyntaxKind.CommaToken,
173+
SyntaxFactory.TriviaList(SyntaxFactory.Space));
174+
lstIndex++;
105175
}
176+
177+
lst[lstIndex] = SyntaxFactory.AttributeArgument(
178+
SyntaxFactory.TypeOfExpression(
179+
options.IsGeneric
180+
? SyntaxFactory.GenericName(typeName)
181+
: SyntaxFactory.IdentifierName(typeName)));
182+
return lst;
106183
}
107184
}
108185
}

build/GitBuildInfo.SourceGenerator.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22

33
<ItemGroup>
44
<AdditionalFiles Include="GitBuildInfo.json" Condition="Exists('GitBuildInfo.json')" />
5+
<AdditionalFiles Include="GitInfo.json" Condition="Exists('GitInfo.json')" />
56
</ItemGroup>
67
</Project>
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
4+
<UsingTask TaskName="GitBuildInfo.GitInfoTask"
5+
TaskFactory="RoslynCodeTaskFactory"
6+
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
7+
<ParameterGroup>
8+
<OutputPath Required="true" />
9+
</ParameterGroup>
10+
<Task>
11+
<Using Namespace="System" />
12+
<Using Namespace="System.ComponentModel" />
13+
<Using Namespace="System.Diagnostics" />
14+
<Using Namespace="System.IO" />
15+
<Using Namespace="System.Text" />
16+
<Using Namespace="Microsoft.Build.Framework" />
17+
<Using Namespace="Microsoft.Build.Utilities" />
18+
<Code Type="Class" Language="cs">
19+
<![CDATA[
20+
namespace GitBuildInfo
21+
{
22+
using System;
23+
using System.ComponentModel;
24+
using System.Diagnostics;
25+
using System.IO;
26+
using System.Text;
27+
using Microsoft.Build.Framework;
28+
using Microsoft.Build.Utilities;
29+
30+
/// <summary>
31+
/// A MSBuild task that generates the msbuild information for an assembly.
32+
///
33+
/// Note: use in the BeforeBuild target.
34+
/// </summary>
35+
public class GitInfoTask : Task
36+
{
37+
/// <summary>
38+
/// Gets or sets the generated output file path.
39+
/// </summary>
40+
[Required]
41+
public string OutputPath { get; set; }
42+
43+
/// <inheritdoc/>
44+
public override bool Execute()
45+
{
46+
this.RunGit("describe --all --always --dirty", out var git_out1);
47+
this.RunGit("rev-parse --short HEAD", out var git_out2);
48+
this.RunGit("name-rev --name-only HEAD", out var git_out3);
49+
var outputData = $"{{{Environment.NewLine} \"GitHead\": \"{git_out1}\",{Environment.NewLine} \"CommitHash\": \"{git_out2}\",{Environment.NewLine} \"GitBranch\": \"{git_out3}\"{Environment.NewLine}}}";
50+
// patch 112019: only print the getting build info from git message from the initial call to this task.
51+
// all other calls will not print anything to avoid spamming up the build output.
52+
try
53+
{
54+
if (!File.Exists(this.OutputPath) || (File.Exists(this.OutputPath) && !string.Equals(outputData, File.ReadAllText(this.OutputPath), StringComparison.Ordinal)))
55+
{
56+
this.Log.LogMessage(MessageImportance.High, "Getting build info from git");
57+
File.WriteAllText(this.OutputPath, outputData);
58+
}
59+
}
60+
catch (IOException)
61+
{
62+
// catch I/O error from being unable to open the file for checking it's contents.
63+
}
64+
return true;
65+
}
66+
67+
private void RunGit(string arguments, out string git_out)
68+
{
69+
using var pro1 = new Process();
70+
pro1.StartInfo.FileName = "git";
71+
pro1.StartInfo.Arguments = arguments;
72+
pro1.StartInfo.RedirectStandardOutput = true;
73+
pro1.StartInfo.UseShellExecute = false;
74+
pro1.StartInfo.CreateNoWindow = true;
75+
pro1.StartInfo.WorkingDirectory = Path.GetFullPath(this.OutputPath).Replace(Path.GetFileName(this.OutputPath), string.Empty);
76+
try
77+
{
78+
_ = pro1.Start();
79+
git_out = pro1.StandardOutput.ReadToEnd();
80+
pro1.WaitForExit();
81+
// handle all cases of possible endlines.
82+
git_out = git_out.Replace("\r\n", string.Empty);
83+
git_out = git_out.Replace("\n", string.Empty);
84+
git_out = git_out.Replace("\r", string.Empty);
85+
}
86+
catch (Win32Exception)
87+
{
88+
git_out = "Not a git clone or git is not in Path.";
89+
}
90+
}
91+
}
92+
}]]>
93+
</Code>
94+
</Task>
95+
</UsingTask>
96+
97+
<Target Name="BeforeCompile">
98+
<GitInfoTask OutputPath="$(MSBuildProjectDirectory)\GitInfo.json" />
99+
</Target>
100+
</Project>

0 commit comments

Comments
 (0)