Skip to content

Commit df960c2

Browse files
xperiandriclaude
andauthored
build: let FAKE write the assembly information as MSBuild properties (#42)
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
1 parent 7a7c00f commit df960c2

8 files changed

Lines changed: 95 additions & 44 deletions

File tree

‎Directory.Build.props‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ See https://learn.microsoft.com/en-us/visualstudio/msbuild/customize-by-director
2929
<Nullable>enable</Nullable>
3030
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
3131
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
32-
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
3332
</PropertyGroup>
3433

3534
<ItemGroup>

‎Directory.Packages.props‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
<PackageVersion Include="Fake.Core.Process" Version="6.2.0" />
2020
<PackageVersion Include="Fake.Core.ReleaseNotes" Version="6.2.0" />
2121
<PackageVersion Include="Fake.Core.Target" Version="6.2.0" />
22-
<PackageVersion Include="Fake.DotNet.AssemblyInfoFile" Version="6.2.0" />
2322
<PackageVersion Include="Fake.DotNet.Cli" Version="6.2.0" />
2423
<PackageVersion Include="Fake.DotNet.Paket" Version="6.2.0" />
2524
<PackageVersion Include="Fake.IO.FileSystem" Version="6.2.0" />

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ or
7979
- `GenerateCoverageReport` - Code coverage is run during `DotnetTest` and this generates a report via [ReportGenerator](https://github.com/danielpalme/ReportGenerator).
8080
- `ShowCoverageReport` - Shows the report generated in `GenerateCoverageReport`.
8181
- `WatchTests` - Runs [dotnet watch](https://docs.microsoft.com/en-us/aspnet/core/tutorials/dotnet-watch?view=aspnetcore-3.0) with the test projects. Useful for rapid feedback loops.
82-
- `GenerateAssemblyInfo` - Generates [AssemblyInfo](https://docs.microsoft.com/en-us/dotnet/api/microsoft.visualbasic.applicationservices.assemblyinfo?view=netframework-4.8) for libraries.
82+
- `GenerateAssemblyInfo` - Writes the version, the release date, the release channel and the git hash of the latest changelog entry into `src/AssemblyInfo.Build.props`, which `src/Directory.Build.props` imports, so that the SDK generates the assembly attributes of the libraries from it.
8383
- `DotnetPack` - Runs [dotnet pack](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-pack). This includes running [Source Link](https://github.com/dotnet/sourcelink).
8484
- `SourceLinkTest` - Runs a Source Link test tool to verify Source Links were properly generated.
8585
- `PublishToNuGet` - Publishes the NuGet packages generated in `DotnetPack` to NuGet via [dotnet nuget push](https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-nuget-push). Runs only from `Github Actions`.

‎build/build.fs‎

Lines changed: 77 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
open System
2+
open System.Xml.Linq
23
open Fake.Core
34
open Fake.DotNet
45
open Fake.Tools
@@ -28,6 +29,9 @@ let rootDirectory = __SOURCE_DIRECTORY__ </> ".."
2829

2930
let productName = "FSharp.Collections.Immutable"
3031

32+
/// The assembly information of the release. `src/Directory.Build.props` imports it, so that the SDK generates the attributes.
33+
let assemblyInfoPropsPath = rootDirectory </> "src" </> "AssemblyInfo.Build.props"
34+
3135
let sln = rootDirectory </> "FSharp.Collections.Immutable.slnf"
3236

3337
let srcCodeGlob =
@@ -432,47 +436,90 @@ let watchTests _ =
432436

433437
cancelEvent.Cancel <- true
434438

439+
/// Writes the assembly information of the latest changelog entry in two places. `src/AssemblyInfo.Build.props` holds
440+
/// the properties and the `AssemblyMetadata` items from which the SDK generates the assembly attributes, and
441+
/// `src/Directory.Build.props` imports it, so a project sets only what is specific to it in its own project file.
442+
/// `AssemblyInfo.fs` of each F# project in `src` holds the literals of `AssemblyVersionInformation`, which the code can
443+
/// use at compile time. It has no attributes, because the SDK generates them.
435444
let generateAssemblyInfo _ =
436445

437-
let (|Fsproj|Csproj|Vbproj|) (projFileName : string) =
438-
match projFileName with
439-
| f when f.EndsWith ("fsproj") -> Fsproj
440-
| f when f.EndsWith ("csproj") -> Csproj
441-
| f when f.EndsWith ("vbproj") -> Vbproj
442-
| _ -> failwithf "Project file %s not supported. Unknown project type." projFileName
443-
444446
let releaseChannel =
445447
match latestEntry.SemVer.PreRelease with
446448
| Some pr -> pr.Name
447449
| _ -> "release"
448450

449-
let getAssemblyInfoAttributes projectName = [
450-
AssemblyInfo.Title (projectName)
451-
AssemblyInfo.Product productName
452-
AssemblyInfo.Version latestEntry.AssemblyVersion
453-
// The date is optional in a changelog entry
454-
match latestEntry.Date with
455-
| Some releaseDate -> AssemblyInfo.Metadata ("ReleaseDate", releaseDate.ToString ("o"))
451+
// The date is optional in a changelog entry
452+
let releaseDate = latestEntry.Date |> Option.map _.ToString("o")
453+
let gitHash = Git.Information.getCurrentSHA1 (null)
454+
455+
let metadata = [
456+
match releaseDate with
457+
| Some date -> "ReleaseDate", date
456458
| None -> ()
457-
AssemblyInfo.FileVersion latestEntry.AssemblyVersion
458-
AssemblyInfo.InformationalVersion latestEntry.AssemblyVersion
459-
AssemblyInfo.Metadata ("ReleaseChannel", releaseChannel)
460-
AssemblyInfo.Metadata ("GitHash", Git.Information.getCurrentSHA1 (null))
459+
"ReleaseChannel", releaseChannel
460+
"GitHash", gitHash
461461
]
462462

463-
let getProjectDetails (projectPath : string) =
464-
let projectName = IO.Path.GetFileNameWithoutExtension (projectPath)
463+
let properties =
464+
XElement (
465+
"PropertyGroup",
466+
XElement ("Product", productName),
467+
XElement ("AssemblyVersion", latestEntry.AssemblyVersion),
468+
XElement ("FileVersion", latestEntry.AssemblyVersion),
469+
XElement ("InformationalVersion", latestEntry.AssemblyVersion),
470+
// The git hash is in the metadata, so the SDK must not append the source revision to the informational version
471+
XElement ("IncludeSourceRevisionInInformationalVersion", "false")
472+
)
465473

466-
(projectPath, projectName, IO.Path.GetDirectoryName (projectPath), (getAssemblyInfoAttributes projectName))
474+
let items =
475+
XElement (
476+
"ItemGroup",
477+
metadata
478+
|> List.map (fun (key, value) ->
479+
XElement ("AssemblyMetadata", XAttribute ("Include", key), XAttribute ("Value", value))
480+
)
481+
)
467482

468-
!!srcGlob
469-
|> Seq.map getProjectDetails
470-
|> Seq.iter (fun (projFileName, _, folderName, attributes) ->
471-
match projFileName with
472-
| Fsproj -> AssemblyInfoFile.createFSharp (folderName </> "AssemblyInfo.fs") attributes
473-
| Csproj -> AssemblyInfoFile.createCSharp ((folderName </> "Properties") </> "AssemblyInfo.cs") attributes
474-
| Vbproj -> AssemblyInfoFile.createVisualBasic ((folderName </> "My Project") </> "AssemblyInfo.vb") attributes
475-
)
483+
let props =
484+
XDocument (XComment " Auto-Generated by FAKE; do not edit ", XElement ("Project", properties, items))
485+
486+
// XDocument breaks lines as the operating system does, and the other generated file has "\n" everywhere
487+
let propsText = props.ToString().ReplaceLineEndings "\n"
488+
489+
File.writeString false assemblyInfoPropsPath $"%s{propsText}\n"
490+
491+
let versionInformation projectName =
492+
let literals = [
493+
"AssemblyTitle", projectName
494+
"AssemblyProduct", productName
495+
"AssemblyVersion", latestEntry.AssemblyVersion
496+
match releaseDate with
497+
| Some date -> "AssemblyMetadata_ReleaseDate", date
498+
| None -> ()
499+
"AssemblyFileVersion", latestEntry.AssemblyVersion
500+
"AssemblyInformationalVersion", latestEntry.AssemblyVersion
501+
"AssemblyMetadata_ReleaseChannel", releaseChannel
502+
"AssemblyMetadata_GitHash", gitHash
503+
]
504+
505+
[
506+
"// Auto-Generated by FAKE; do not edit"
507+
"namespace System"
508+
""
509+
"module internal AssemblyVersionInformation ="
510+
yield!
511+
literals
512+
|> List.map (fun (name, value) -> $" let [<Literal>] %s{name} = \"%s{value}\"")
513+
]
514+
|> String.concat "\n"
515+
516+
let writeVersionInformation (projectPath : string) =
517+
// The project path is not null, so neither is the name
518+
let projectName = IO.Path.GetFileNameWithoutExtension projectPath |> nonNull
519+
File.writeString false (Path.getDirectory projectPath </> "AssemblyInfo.fs") $"%s{versionInformation projectName}\n"
520+
521+
!!(rootDirectory </> "src/**/*.fsproj")
522+
|> Seq.iter writeVersionInformation
476523

477524
let dotnetPack ctx =
478525
// Get release notes with properly-linked version number
@@ -534,6 +581,7 @@ let gitRelease _ =
534581

535582
!!(rootDirectory </> "src/**/AssemblyInfo.fs")
536583
++ (rootDirectory </> "tests/**/AssemblyInfo.fs")
584+
++ assemblyInfoPropsPath
537585
|> Seq.iter (Git.Staging.stageFile "" >> ignore)
538586

539587
let msg = $"release: bump version to `%s{latestEntry.NuGetVersion}`\n\n%s{releaseNotesGitCommitFormat}"

‎build/build.fsproj‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
<PackageReference Include="Fake.Core.Process" />
2626
<PackageReference Include="Fake.Core.ReleaseNotes" />
2727
<PackageReference Include="Fake.Core.Target" />
28-
<PackageReference Include="Fake.DotNet.AssemblyInfoFile" />
2928
<PackageReference Include="Fake.DotNet.Cli" />
3029
<PackageReference Include="Fake.DotNet.Paket" />
3130
<PackageReference Include="Fake.IO.FileSystem" />

‎src/AssemblyInfo.Build.props‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<!-- Auto-Generated by FAKE; do not edit -->
2+
<Project>
3+
<PropertyGroup>
4+
<Product>FSharp.Collections.Immutable</Product>
5+
<AssemblyVersion>1.0.0</AssemblyVersion>
6+
<FileVersion>1.0.0</FileVersion>
7+
<InformationalVersion>1.0.0</InformationalVersion>
8+
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
9+
</PropertyGroup>
10+
<ItemGroup>
11+
<AssemblyMetadata Include="ReleaseDate" Value="2020-02-23T00:00:00.0000000" />
12+
<AssemblyMetadata Include="ReleaseChannel" Value="release" />
13+
<AssemblyMetadata Include="GitHash" Value="59d59515d1f3a3ecee1bb208b9bc1378035e4694" />
14+
</ItemGroup>
15+
</Project>

‎src/Directory.Build.props‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<Project>
22
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
3+
<!-- The build script (FAKE) writes the assembly information of the release into this file, and the SDK generates the assembly attributes from it -->
4+
<Import Project="AssemblyInfo.Build.props" Condition="Exists('$(MSBuildThisFileDirectory)AssemblyInfo.Build.props')" />
35
<PropertyGroup>
46
<GenerateDocumentationFile>true</GenerateDocumentationFile>
57
<IsPackable>true</IsPackable>

‎src/FSharp.Collections.Immutable/AssemblyInfo.fs‎

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,5 @@
11
// Auto-Generated by FAKE; do not edit
22
namespace System
3-
open System.Reflection
4-
5-
[<assembly: AssemblyTitleAttribute("FSharp.Collections.Immutable")>]
6-
[<assembly: AssemblyProductAttribute("FSharp.Collections.Immutable")>]
7-
[<assembly: AssemblyVersionAttribute("1.0.0")>]
8-
[<assembly: AssemblyMetadataAttribute("ReleaseDate","2020-02-23T00:00:00.0000000")>]
9-
[<assembly: AssemblyFileVersionAttribute("1.0.0")>]
10-
[<assembly: AssemblyInformationalVersionAttribute("1.0.0")>]
11-
[<assembly: AssemblyMetadataAttribute("ReleaseChannel","release")>]
12-
[<assembly: AssemblyMetadataAttribute("GitHash","59d59515d1f3a3ecee1bb208b9bc1378035e4694")>]
13-
do ()
143

154
module internal AssemblyVersionInformation =
165
let [<Literal>] AssemblyTitle = "FSharp.Collections.Immutable"

0 commit comments

Comments
 (0)