Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make MsBuild Task More Managable #31

Merged
merged 8 commits into from
Jan 19, 2024
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
5 changes: 3 additions & 2 deletions .github/workflows/CleanArchitecture.Blazored.Deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ jobs:
uses: actions/setup-dotnet@v4.0.0
with:
dotnet-version: 8.x
- name: Restore dependencies
run: dotnet restore src/CleanArchitecture.Blazored.csproj
- name: Build Generated Project
shell: pwsh
run: .\CleanArchitecture.Blazored.restore.ps1
- name: Build Project
run: dotnet build --no-restore src/CleanArchitecture.Blazored.csproj -c Release
- name: Get latest release tag
Expand Down
16 changes: 9 additions & 7 deletions .github/workflows/CleanArchitecture.Blazored.Dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,12 @@ jobs:
with:
dotnet-version: 8.x

- name: Create Generated Project
run: dotnet restore src/CleanArchitecture.Blazored.csproj
- name: Build Generated Project
shell: pwsh
run: .\CleanArchitecture.Blazored.restore.ps1

- name: Restore dependencies
run: dotnet restore CleanArchitecture.Blazored.Dev.sln
run: dotnet restore CleanArchitecture.Blazored.Dev.sln --configfile ./nuget.config

- name: Build solution
run: dotnet build --no-restore CleanArchitecture.Blazored.Dev.sln -c Release
Expand Down Expand Up @@ -87,12 +88,13 @@ jobs:
uses: actions/setup-dotnet@v4.0.0
with:
dotnet-version: 8.x

- name: Create Generated Project
run: dotnet restore src/CleanArchitecture.Blazored.csproj

- name: Build Generated Project
shell: pwsh
run: .\CleanArchitecture.Blazored.restore.ps1

- name: Restore dependencies
run: dotnet restore CleanArchitecture.Blazored.Dev.sln
run: dotnet restore CleanArchitecture.Blazored.Dev.sln --configfile ./nuget.config

- name: Build solution
run: dotnet build --no-restore CleanArchitecture.Blazored.Dev.sln -c Release
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/CleanArchitecture.Blazored.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ jobs:
uses: actions/setup-dotnet@v4.0.0
with:
dotnet-version: 8.x
- name: Build Generated Project
shell: pwsh
run: .\CleanArchitecture.Blazored.restore.ps1
- name: Create Generated Project
run: dotnet pack src/CleanArchitecture.Blazored.csproj
- name: Install Project Template
Expand Down
5 changes: 4 additions & 1 deletion .github/workflows/CleanArchitecture.MudBlazored.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v4.0.0
with:
dotnet-version: 8.x
dotnet-version: 8.x
- name: Build Generated Project
shell: pwsh
run: .\CleanArchitecture.Blazored.restore.ps1
- name: Create Generated Project
run: dotnet pack src/CleanArchitecture.Blazored.csproj
- name: Install Project Template
Expand Down
6 changes: 4 additions & 2 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ jobs:
}

if(RegExp(isChecked.source + "Template Enhancement").test(body)) {
labels.push("breaking change");
console.log("PR type: breaking change.");
labels.push("enhancement");
labels.push("management");
console.log("PR type: enhancement.");
console.log("PR type: management.");
}

if(!pr.draft) {
Expand Down
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ BenchmarkDotNet.Artifacts/
# .NET
project.lock.json
project.fragment.lock.json
artifacts/
#artifacts/

# Tye
.tye/
Expand Down Expand Up @@ -448,4 +448,5 @@ $RECYCLE.BIN/
## Visual Studio Code
##
.vscode/*
*.g.md
*.g.md
CleanArchitecture.Blazored.MsBuild.props
9 changes: 9 additions & 0 deletions CleanArchitecture.Blazored.Dev.sln
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "content", "content", "{CEF1
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CleanArchitecture.Blazored.MsBuild", "src\CleanArchitecture.Blazored.MsBuild\CleanArchitecture.Blazored.MsBuild.csproj", "{5292A97C-C670-40AB-9554-E3C62C4A914C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{37B02AEB-075D-458B-B846-0CA70062B8F2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CleanArchitecture.Blazored.MsBuild.Tests", "tests\CleanArchitecture.Blazored.MsBuild.Tests\CleanArchitecture.Blazored.MsBuild.Tests.csproj", "{FD294DC5-2137-4293-AF0E-8A90759213E8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -189,6 +193,10 @@ Global
{5292A97C-C670-40AB-9554-E3C62C4A914C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5292A97C-C670-40AB-9554-E3C62C4A914C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5292A97C-C670-40AB-9554-E3C62C4A914C}.Release|Any CPU.Build.0 = Release|Any CPU
{FD294DC5-2137-4293-AF0E-8A90759213E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FD294DC5-2137-4293-AF0E-8A90759213E8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FD294DC5-2137-4293-AF0E-8A90759213E8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FD294DC5-2137-4293-AF0E-8A90759213E8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -229,6 +237,7 @@ Global
{FF0A9EF0-5016-49D5-A185-A927E4599B9D} = {CEF17048-3644-48CA-B1BF-9362FCD75FB4}
{2B0B5A20-5D73-435D-B4D1-4B313A80D322} = {CEF17048-3644-48CA-B1BF-9362FCD75FB4}
{5292A97C-C670-40AB-9554-E3C62C4A914C} = {888F0530-A4BE-41CA-A040-82C4A693EFCD}
{FD294DC5-2137-4293-AF0E-8A90759213E8} = {37B02AEB-075D-458B-B846-0CA70062B8F2}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E21648D4-C532-447C-9233-32E2BE40946A}
Expand Down
5 changes: 5 additions & 0 deletions CleanArchitecture.Blazored.restore.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
dotnet pack ./src/CleanArchitecture.Blazored.MsBuild/ -o ./artifacts
dotnet nuget update source CleanArchitecture.Blazored.Dev --configfile ./nuget.config
dotnet restore ./src/CleanArchitecture.Blazored.csproj --configfile ./nuget.config -f --no-cache
dotnet build ./src/CleanArchitecture.Blazored.csproj --no-restore -c Debug
dotnet build ./src/CleanArchitecture.Blazored.csproj --no-restore -c Release
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ Refer documentation from each template

## Templates
<!-- include (TemplatesSection.g.md) -->
* [CleanArchitecture.Blazored](src/content/CleanArchitecture.Blazored/README.md)
* [CleanArchitecture.MudBlazored](src/content/CleanArchitecture.MudBlazored/README.md)
* [Clean Architecture Blazored](src/content/CleanArchitecture.Blazored/README.md)
* [Clean Architecture Mud Blazored](src/content/CleanArchitecture.MudBlazored/README.md)
<!-- /include -->

## Support
Expand Down
Empty file added artifacts/.gitkeep
Empty file.
9 changes: 9 additions & 0 deletions nuget.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<!--To inherit the global NuGet package sources remove the <clear/> line below -->
<clear />
<add key="nuget" value="https://api.nuget.org/v3/index.json" />
<add key="CleanArchitecture.Blazored.Dev" value="./artifacts" />
</packageSources>
</configuration>
56 changes: 56 additions & 0 deletions src/CleanArchitecture.Blazored.MsBuild/AddCustomTaskToTarget.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using System.CodeDom.Compiler;
using Microsoft.Build.Framework;
using Task = Microsoft.Build.Utilities.Task;

namespace CleanArchitecture.Blazored.MsBuild;

public class AddCustomTaskToTarget : Task
{
private const string FileName = "build/CleanArchitecture.Blazored.MsBuild.props";
[Required] public required ITaskItem[] Files { get; set; }

[Required] public required string Namespace { get; set; }

[Required] public required string TaskAssembly { get; set; }

public override bool Execute()
{
var tasks = GetAllTask(Files);
GenerateUsingTask(tasks);
return true;
}

private IEnumerable<string> GetAllTask(ITaskItem[] files)
{
return files.Where(item => item.GetMetadata("Extension") == ".cs")
.Select(item => item.GetMetadata("Filename"))
.Select(fileName => "$" + $"(MSBuildThisFileName).{fileName}");
}

private void GenerateUsingTask(IEnumerable<string> tasks)
{
var baseTextWriter = new StringWriter();
var indentWriter = new IndentedTextWriter(baseTextWriter);
indentWriter.Indent = 0;
foreach (var task in tasks)
{
indentWriter.WriteLine($$"""
<UsingTask TaskName="{{task}}"
AssemblyFile="{{"$"+"(CustomTasksAssembly)"}}" />
""");
}

var template = File.ReadAllText("build/UsingTasks.props.template");

var replacingText = $$"""
<!--Register our custom task Starts-->

{{baseTextWriter}}
<!--Register our custom task Ends-->
""";

var newContent = template.Replace("<!--Register our custom task-->", replacingText);

File.WriteAllText(FileName, newContent);
}
}
183 changes: 183 additions & 0 deletions src/CleanArchitecture.Blazored.MsBuild/AppSettingStronglyTyped.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
using System.Text;
using Microsoft.Build.Framework;
using Task = Microsoft.Build.Utilities.Task;

namespace CleanArchitecture.Blazored.MsBuild;

public class AppSettingStronglyTyped : Task
{
//The name of the class which is going to be generated
[Required] public required string SettingClassName { get; set; } = string.Empty;

//The name of the namespace where the class is going to be generated
[Required] public required string SettingNamespaceName { get; set; } = string.Empty;

//List of files which we need to read with the defined format: 'propertyName:type:defaultValue' per line
[Required] public required ITaskItem[] SettingFiles { get; set; } = default!;

//The filename where the class was generated
[Output]
public string ClassNameFile { get; set; } = string.Empty;

public override bool Execute()
{
//Read the input files and return a IDictionary<string, object> with the properties to be created.
//Any format error it will return not succeed and Log.LogError properly
var (success, settings) = ReadProjectSettingFiles();
if (!success)
{
return !Log.HasLoggedErrors;
}
//Create the class based on the Dictionary
_ = CreateSettingClass(settings);

return !Log.HasLoggedErrors;
}

private (bool, IDictionary<string, object>) ReadProjectSettingFiles()
{
var values = new Dictionary<string, object>();
foreach (var item in SettingFiles)
{
var lineNumber = 0;

var settingFile = item.GetMetadata("FullPath");
foreach (var line in File.ReadLines(settingFile))
{
lineNumber++;

var lineParse = line.Split(':');
if (lineParse.Length != 3)
{
Log.LogError(subcategory: null,
errorCode: "APPS0001",
helpKeyword: null,
file: settingFile,
lineNumber: lineNumber,
columnNumber: 0,
endLineNumber: 0,
endColumnNumber: 0,
message: "Incorrect line format. Valid format prop:type:defaultvalue");
return (false, null)!;
}
var value = GetValue(lineParse[1], lineParse[2]);
if (!value.Item1)
{
return (value.Item1, null)!;
}

values[lineParse[0]] = value.Item2;
}
}
return (true, values);
}

private (bool, object) GetValue(string type, string value)
{
try
{
// So far only few types are supported values.
if ("string".Equals(type))
{
return (true, value);
}
if ("int".Equals(type))
{
return (true, int.Parse(value));
}
if ("long".Equals(type))
{
return (true, long.Parse(value));
}
if ("guid".Equals(type))
{
return (true, Guid.Parse(value));
}
if ("bool".Equals(type))
{
return (true, bool.Parse(value));
}
Log.LogError($"Type not supported -> {type}");
return (false, null)!;
}
catch
{
Log.LogError($"It is not possible parse some value based on the type -> {type} - {value}");
return (false, null)!;
}
}

private bool CreateSettingClass(IDictionary<string, object> settings)
{
try
{
ClassNameFile = $"{SettingClassName}.generated.cs";
File.Delete(ClassNameFile);
var settingsClass = new StringBuilder(1024);
// open namespace
settingsClass.Append($@" using System;
namespace {SettingNamespaceName} {{

public class {SettingClassName} {{
");
//For each element in the dictionary create a static property
foreach (var keyValuePair in settings)
{
var typeName = GetTypeString(keyValuePair.Value.GetType().Name);
settingsClass.Append($" public readonly static {typeName} {keyValuePair.Key} = {GetValueString(keyValuePair, typeName)};\r\n");
}
// close namespace and class
settingsClass.Append(@" }

}");
File.WriteAllText(ClassNameFile, settingsClass.ToString());

}
catch (Exception ex)
{
//This logging helper method is designed to capture and display information from arbitrary exceptions in a standard way.
Log.LogErrorFromException(ex, showStackTrace: true);
return false;
}
return true;
}

private string GetTypeString(string typeName)
{
if ("String".Equals(typeName))
{
return "string";
}
if ("Boolean".Equals(typeName))
{
return "bool";
}
if ("Int32".Equals(typeName))
{
return "int";
}
if ("Int64".Equals(typeName))
{
return "long";
}
return typeName;
}

private static object GetValueString(KeyValuePair<string, object> keyValuePair, string typeName)
{
if ("Guid".Equals(typeName))
{
return $"Guid.Parse(\"{keyValuePair.Value}\")";
}
if ("string".Equals(typeName))
{
return $"\"{keyValuePair.Value}\"";
}
if ("bool".Equals(typeName))
{
return $"{keyValuePair.Value.ToString()?.ToLower()}";
}

return keyValuePair.Value;
}
}
Loading
Loading