Skip to content

Commit 4562f5a

Browse files
xperiandriclaude
andcommitted
build: let FAKE write the assembly information as MSBuild properties
The release step of the build script wrote the assembly attributes into AssemblyInfo.fs, and Directory.Build.props set GenerateAssemblyInfo to false so that the SDK did not generate them a second time. Every other assembly attribute then needed a source file, or a property and a target. The GenerateAssemblyInfo target now writes src/AssemblyInfo.Build.props with Product, AssemblyVersion, FileVersion and InformationalVersion, and the AssemblyMetadata items ReleaseDate, ReleaseChannel and GitHash. src/Directory.Build.props imports it, GenerateAssemblyInfo is no longer false, and the SDK generates the attributes. A project sets what is specific to it (its title, for example) in its own project file. The SDK appends no source revision to the informational version, because the git hash is in the metadata. AssemblyInfo.fs keeps the module AssemblyVersionInformation with the literals, but no attributes. The release commit stages both files. The package Fake.DotNet.AssemblyInfoFile is not needed any more. The values in the checked-in files are those of the last release, as before. The assembly has the attributes of before and the four that the SDK generates by default: AssemblyCompany, AssemblyConfiguration, AssemblyDescription and the AssemblyMetadata RepositoryUrl. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
1 parent 7a7c00f commit 4562f5a

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)