diff --git a/.gitignore b/.gitignore index 70c5e46..4761a42 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,10 @@ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## -## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore - -## Custom -appsettings.ABT-PC.json - - +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore # User-specific files +*.rsuser *.suo *.user *.userosscache @@ -17,6 +13,9 @@ appsettings.ABT-PC.json # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs +# Mono auto generated files +mono_crash.* + # Build results [Dd]ebug/ [Dd]ebugPublic/ @@ -24,15 +23,17 @@ appsettings.ABT-PC.json [Rr]eleases/ x64/ x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ +[Ll]ogs/ # Visual Studio 2015/2017 cache/options directory .vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ # Visual Studio 2017 auto generated files Generated\ Files/ @@ -41,9 +42,10 @@ Generated\ Files/ [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* -# NUNIT +# NUnit *.VisualState.xml TestResult.xml +nunit-*.xml # Build Results of an ATL Project [Dd]ebugPS/ @@ -53,11 +55,16 @@ dlldata.c # Benchmark Results BenchmarkDotNet.Artifacts/ -# .NET Core +# .NET project.lock.json project.fragment.lock.json artifacts/ -##**/Properties/launchSettings.json + +# Tye +.tye/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt # StyleCop StyleCopReport.xml @@ -65,12 +72,14 @@ StyleCopReport.xml # Files built by Visual Studio *_i.c *_p.c -*_i.h +*_h.h *.ilk *.meta *.obj +*.iobj *.pch *.pdb +*.ipdb *.pgc *.pgd *.rsp @@ -80,7 +89,9 @@ StyleCopReport.xml *.tlh *.tmp *.tmp_proj +*_wpftmp.csproj *.log +*.tlog *.vspscc *.vssscc .builds @@ -88,9 +99,6 @@ StyleCopReport.xml *.svclog *.scc -# Chutzpah Test files -_Chutzpah* - # Visual C++ cache files ipch/ *.aps @@ -111,215 +119,66 @@ ipch/ # Visual Studio Trace Files *.e2e -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in +# ReSharper _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user -# JustCode is a .NET coding add-in -.JustCode - -# TeamCity is a build add-in +# TeamCity _TeamCity* -# DotCover is a Code Coverage Tool +# DotCover *.dotCover -# AxoCover is a Code Coverage Tool +# AxoCover .axoCover/* !.axoCover/settings.json +# Coverlet +coverage*.json +coverage*.xml +coverage*.info + # Visual Studio code coverage results *.coverage *.coveragexml -# NCrunch -_NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# Note: Comment the next line if you want to checkin your web deploy settings, -# but database connection strings (with potential passwords) will be unencrypted -*.pubxml -*.publishproj - -# Microsoft Azure Web App publish settings. Comment the next line if you want to -# checkin your Azure Web App publish settings, but sensitive information contained -# in these scripts will be unencrypted -PublishScripts/ - # NuGet Packages *.nupkg +*.snupkg +# NuGet Symbol Packages +*.symbols.nupkg + # The packages folder can be ignored because of Package Restore **/[Pp]ackages/* + # except build/, which is used as an MSBuild target. !**/[Pp]ackages/build/ -# Uncomment if necessary however generally it will be regenerated when needed + +# Uncomment if necessary; generally it will be regenerated when needed #!**/[Pp]ackages/repositories.config + # NuGet v3's project.json files produces more ignorable files *.nuget.props *.nuget.targets -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Windows Store app package directories and files -AppPackages/ -BundleArtifacts/ -Package.StoreAssociation.xml -_pkginfo.txt -*.appx - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.jfm -*.pfx -*.publishsettings -orleans.codegen.cs - -# Including strong name files can present a security risk -# (https://github.com/github/gitignore/pull/2483#issue-259490424) -#*.snk - -# Since there are multiple workflows, uncomment next line to ignore bower_components -# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) -#bower_components/ - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm -ServiceFabricBackup/ - -# SQL Server files -*.mdf -*.ldf -*.ndf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat -node_modules/ - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) -*.vbw - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -.paket/paket.exe -paket-files/ - -# FAKE - F# Make -.fake/ - -# JetBrains Rider -.idea/ -*.sln.iml - -# CodeRush -.cr/ - -# Python Tools for Visual Studio (PTVS) -__pycache__/ -*.pyc - -# Cake - Uncomment if you are using it -# tools/** -# !tools/packages.config - -# Tabs Studio -*.tss - -# Telerik's JustMock configuration file -*.jmconfig - -# BizTalk build output -*.btp.cs -*.btm.cs -*.odx.cs -*.xsd.cs +# Chutzpah Test files +_Chutzpah* -# OpenCover UI analysis results -OpenCover/ +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb -# Azure Stream Analytics local run output -ASALocalRun/ +# node +**/node_modules/ -# MSBuild Binary and Structured Log -*.binlog +# Python +__pycache__ +*.pyc diff --git a/readme.md b/readme.md index 0db9851..a8028b5 100644 --- a/readme.md +++ b/readme.md @@ -33,7 +33,6 @@ Zero runtime overhead. No reflection. No configuration required. - [Target Frameworks](#target-frameworks) - [Building & Testing](#building--testing) - [Project Structure](#project-structure) -- [Contributing](#contributing) - [License](#license) --- @@ -242,221 +241,6 @@ host process. The consuming project can target any framework supported by Roslyn | .NET 8 | ✅ | | .NET Standard 2.0+ | ✅ | | .NET Framework 4.6.2+ | ✅ | ---- - -## License - -[MIT](LICENSE) © [BEN ABT](https://benjamin-abt.com/) - -Please consider donating to institutions of your choice such as child cancer aid, -children's hospices, or similar charitable causes. Thank you! - - -[![AssemblyMetadata](https://img.shields.io/nuget/v/AssemblyMetadata.svg?logo=nuget&label=AssemblyMetadata)](https://www.nuget.org/packages/AssemblyMetadata) -[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) - -A **Roslyn incremental source generator** that embeds build-time metadata - timestamp, date and time components - as **compile-time constants** directly into your assembly. -Zero runtime overhead. No reflection. No configuration required. - ---- - -## Table of Contents - -- [Why AssemblyMetadata?](#why-assemblymetadata) -- [Installation](#installation) -- [Quick Start](#quick-start) -- [API Reference](#api-reference) - - [BuildInfo](#buildinfo) -- [Usage Examples](#usage-examples) - - [Display Build Timestamp](#display-build-timestamp) - - [Parse into DateTimeOffset](#parse-into-datetimeoffset) - - [Use Individual Components](#use-individual-components) - - [Build Age Check](#build-age-check) -- [How It Works](#how-it-works) -- [Target Frameworks](#target-frameworks) -- [Building & Testing](#building--testing) -- [Project Structure](#project-structure) -- [License](#license) - ---- - -## Why AssemblyMetadata? - -Knowing *when* an assembly was built is useful for diagnostics, "About" screens, deployment -validation, and telemetry. The traditional approach - reading `Assembly.GetExecutingAssembly().GetName().Version` -or an embedded resource - requires reflection at runtime or extra tooling. - -AssemblyMetadata solves this differently: - -- **Compile-time constants** - values are `const`, so the JIT can inline and dead-code-eliminate them -- **Zero-cost access** - reading the timestamp costs nothing beyond a field load -- **No dependencies at runtime** - the NuGet package ships as a source generator analyzer; nothing is added to your runtime dependency graph -- **Incremental generator** - uses the modern Roslyn `IIncrementalGenerator` API, meaning the generator only runs when the compilation changes, keeping build times fast - ---- - -## Installation - -Add the package to **any project** that needs build metadata: - -```xml - -``` - -> The `OutputItemType="Analyzer"` and `ReferenceOutputAssembly="false"` attributes are required. -> They instruct MSBuild to load the package as a Roslyn analyzer (source generator) rather than a -> regular assembly reference, so it produces no runtime dependency. - ---- - -## Quick Start - -After adding the package, the generated class `AssemblyMetadataInfo` is immediately available -anywhere in your project - no `using` statement needed from the `BenjaminAbt.AssemblyMetadata` -namespace if you are already inside a child namespace of it. - -```csharp -using BenjaminAbt.AssemblyMetadata; - -// ISO 8601 UTC timestamp of the build -string timestamp = AssemblyMetadataInfo.BuildInfo.BuildTimestamp; -// e.g. "2026-03-02T14:35:07.1234567+00:00" - -// Individual date/time components (UTC) -Console.WriteLine($"Built on {AssemblyMetadataInfo.BuildInfo.BuildDateYear}-" - + $"{AssemblyMetadataInfo.BuildInfo.BuildDateMonth:D2}-" - + $"{AssemblyMetadataInfo.BuildInfo.BuildDateDay:D2} " - + $"at {AssemblyMetadataInfo.BuildInfo.BuildTimeHour:D2}:" - + $"{AssemblyMetadataInfo.BuildInfo.BuildTimeMinute:D2}:" - + $"{AssemblyMetadataInfo.BuildInfo.BuildTimeSecond:D2} UTC"); -``` - ---- - -## API Reference - -The generator produces a single internal static class in the `BenjaminAbt.AssemblyMetadata` -namespace. - -### BuildInfo - -`AssemblyMetadataInfo.BuildInfo` - all members are `public const`. - -| Member | Type | Description | -|---|---|---| -| `BuildTimestamp` | `string` | Build time as a UTC ISO 8601 round-trip string (`"o"` format) | -| `BuildFileTimeUtc` | `long` | Build time as a Windows FileTime (100-nanosecond intervals since 1601-01-01 UTC) | -| `BuildDateYear` | `int` | Year component of the UTC build date | -| `BuildDateMonth` | `int` | Month component of the UTC build date (1–12) | -| `BuildDateDay` | `int` | Day component of the UTC build date (1–31) | -| `BuildTimeHour` | `int` | Hour component of the UTC build time (0–23) | -| `BuildTimeMinute` | `int` | Minute component of the UTC build time (0–59) | -| `BuildTimeSecond` | `int` | Second component of the UTC build time (0–59) | - ---- - -## Usage Examples - -### Display Build Timestamp - -```csharp -using BenjaminAbt.AssemblyMetadata; - -Console.WriteLine(AssemblyMetadataInfo.BuildInfo.BuildTimestamp); -// Output: 2026-03-02T14:35:07.1234567+00:00 -``` - -### Parse into DateTimeOffset - -Use the `"o"` round-trip format specifier to parse the stored constant back into a -`DateTimeOffset`: - -```csharp -using System; -using BenjaminAbt.AssemblyMetadata; - -DateTimeOffset buildOn = DateTimeOffset.ParseExact( - AssemblyMetadataInfo.BuildInfo.BuildTimestamp, "o", null); - -Console.WriteLine($"Built {(DateTimeOffset.UtcNow - buildOn).Days} days ago"); -``` - -Or reconstruct from the FileTime for maximum performance (no string parsing): - -```csharp -DateTimeOffset buildOn = DateTimeOffset.FromFileTime( - AssemblyMetadataInfo.BuildInfo.BuildFileTimeUtc); -``` - -### Use Individual Components - -The integer components allow zero-allocation formatting and direct comparison: - -```csharp -using BenjaminAbt.AssemblyMetadata; - -string buildDate = string.Create(null, - stackalloc char[10], - $"{AssemblyMetadataInfo.BuildInfo.BuildDateYear}-" - + $"{AssemblyMetadataInfo.BuildInfo.BuildDateMonth:D2}-" - + $"{AssemblyMetadataInfo.BuildInfo.BuildDateDay:D2}"); -``` - -### Build Age Check - -```csharp -using System; -using BenjaminAbt.AssemblyMetadata; - -bool isOlderThan30Days = - (DateTimeOffset.UtcNow - DateTimeOffset.FromFileTime( - AssemblyMetadataInfo.BuildInfo.BuildFileTimeUtc)).TotalDays > 30; - -if (isOlderThan30Days) - Console.WriteLine("Warning: this build is more than 30 days old."); -``` - ---- - -## How It Works - -AssemblyMetadata uses the modern **Roslyn incremental source generator** API -(`IIncrementalGenerator`). The generator is registered against the compilation provider, so it -fires on every new compilation: - -``` -Build triggered - └─ Roslyn compilation created - └─ AssemblyMetadataGenerator.Execute() - └─ DateTimeOffset.UtcNow captured - └─ AssemblyMetadataInfo.gen.cs emitted - └─ Compiled into the consuming assembly as internal constants -``` - -The generator emits a file named `AssemblyMetadataInfo.gen.cs` directly into the consuming -project's compilation. Because all members are `const`, the C# compiler inlines them at every -call site - no method call, no property access, no object allocation. - -> **Note:** The generated file never appears on disk; it lives only in the in-memory compilation. -> When Visual Studio or `dotnet build` invokes the generator, the output is automatically available. - ---- - -## Target Frameworks - -The source generator itself targets `netstandard2.0` because it runs inside the Roslyn/MSBuild -host process. The consuming project can target any framework supported by Roslyn source generators: - -| Framework | Supported | -|---|---| -| .NET 8 | ✅ | -| .NET 9 | ✅ | -| .NET 10 | ✅ | -| .NET Standard 2.0+ | ✅ | -| .NET Framework 4.6.2+ | ✅ | --- @@ -502,6 +286,7 @@ tests/ AssemblyMetadata.UnitTests/ # xUnit v3 unit tests (net10.0) AssemblyMetadata.UnitTests.csproj AssemblyMetadataInfoTests.cs + AssemblyMetadataGeneratorTests.cs sample/ AssemblyMetadata.SampleApp/ # Console sample demonstrating the generated API (net10.0) @@ -527,4 +312,3 @@ NuGet.config # NuGet feed configuration Please donate - if possible - to institutions of your choice such as child cancer aid, children's hospices, etc. Thanks! -