diff --git a/Directory.Build.props b/Directory.Build.props index db8bbd23..6e84452a 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -29,7 +29,6 @@ See https://learn.microsoft.com/en-us/visualstudio/msbuild/customize-by-director enable true true - false diff --git a/Directory.Packages.props b/Directory.Packages.props index 3660945f..def08789 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -19,7 +19,6 @@ - diff --git a/README.md b/README.md index a10138fc..950057e1 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ or - `GenerateCoverageReport` - Code coverage is run during `DotnetTest` and this generates a report via [ReportGenerator](https://github.com/danielpalme/ReportGenerator). - `ShowCoverageReport` - Shows the report generated in `GenerateCoverageReport`. - `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. -- `GenerateAssemblyInfo` - Generates [AssemblyInfo](https://docs.microsoft.com/en-us/dotnet/api/microsoft.visualbasic.applicationservices.assemblyinfo?view=netframework-4.8) for libraries. +- `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. - `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). - `SourceLinkTest` - Runs a Source Link test tool to verify Source Links were properly generated. - `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`. diff --git a/build/build.fs b/build/build.fs index 004a55b3..d9536a28 100644 --- a/build/build.fs +++ b/build/build.fs @@ -1,4 +1,5 @@ open System +open System.Xml.Linq open Fake.Core open Fake.DotNet open Fake.Tools @@ -28,6 +29,9 @@ let rootDirectory = __SOURCE_DIRECTORY__ ".." let productName = "FSharp.Collections.Immutable" +/// The assembly information of the release. `src/Directory.Build.props` imports it, so that the SDK generates the attributes. +let assemblyInfoPropsPath = rootDirectory "src" "AssemblyInfo.Build.props" + let sln = rootDirectory "FSharp.Collections.Immutable.slnf" let srcCodeGlob = @@ -432,47 +436,90 @@ let watchTests _ = cancelEvent.Cancel <- true +/// Writes the assembly information of the latest changelog entry in two places. `src/AssemblyInfo.Build.props` holds +/// the properties and the `AssemblyMetadata` items from which the SDK generates the assembly attributes, and +/// `src/Directory.Build.props` imports it, so a project sets only what is specific to it in its own project file. +/// `AssemblyInfo.fs` of each F# project in `src` holds the literals of `AssemblyVersionInformation`, which the code can +/// use at compile time. It has no attributes, because the SDK generates them. let generateAssemblyInfo _ = - let (|Fsproj|Csproj|Vbproj|) (projFileName : string) = - match projFileName with - | f when f.EndsWith ("fsproj") -> Fsproj - | f when f.EndsWith ("csproj") -> Csproj - | f when f.EndsWith ("vbproj") -> Vbproj - | _ -> failwithf "Project file %s not supported. Unknown project type." projFileName - let releaseChannel = match latestEntry.SemVer.PreRelease with | Some pr -> pr.Name | _ -> "release" - let getAssemblyInfoAttributes projectName = [ - AssemblyInfo.Title (projectName) - AssemblyInfo.Product productName - AssemblyInfo.Version latestEntry.AssemblyVersion - // The date is optional in a changelog entry - match latestEntry.Date with - | Some releaseDate -> AssemblyInfo.Metadata ("ReleaseDate", releaseDate.ToString ("o")) + // The date is optional in a changelog entry + let releaseDate = latestEntry.Date |> Option.map _.ToString("o") + let gitHash = Git.Information.getCurrentSHA1 (null) + + let metadata = [ + match releaseDate with + | Some date -> "ReleaseDate", date | None -> () - AssemblyInfo.FileVersion latestEntry.AssemblyVersion - AssemblyInfo.InformationalVersion latestEntry.AssemblyVersion - AssemblyInfo.Metadata ("ReleaseChannel", releaseChannel) - AssemblyInfo.Metadata ("GitHash", Git.Information.getCurrentSHA1 (null)) + "ReleaseChannel", releaseChannel + "GitHash", gitHash ] - let getProjectDetails (projectPath : string) = - let projectName = IO.Path.GetFileNameWithoutExtension (projectPath) + let properties = + XElement ( + "PropertyGroup", + XElement ("Product", productName), + XElement ("AssemblyVersion", latestEntry.AssemblyVersion), + XElement ("FileVersion", latestEntry.AssemblyVersion), + XElement ("InformationalVersion", latestEntry.AssemblyVersion), + // The git hash is in the metadata, so the SDK must not append the source revision to the informational version + XElement ("IncludeSourceRevisionInInformationalVersion", "false") + ) - (projectPath, projectName, IO.Path.GetDirectoryName (projectPath), (getAssemblyInfoAttributes projectName)) + let items = + XElement ( + "ItemGroup", + metadata + |> List.map (fun (key, value) -> + XElement ("AssemblyMetadata", XAttribute ("Include", key), XAttribute ("Value", value)) + ) + ) - !!srcGlob - |> Seq.map getProjectDetails - |> Seq.iter (fun (projFileName, _, folderName, attributes) -> - match projFileName with - | Fsproj -> AssemblyInfoFile.createFSharp (folderName "AssemblyInfo.fs") attributes - | Csproj -> AssemblyInfoFile.createCSharp ((folderName "Properties") "AssemblyInfo.cs") attributes - | Vbproj -> AssemblyInfoFile.createVisualBasic ((folderName "My Project") "AssemblyInfo.vb") attributes - ) + let props = + XDocument (XComment " Auto-Generated by FAKE; do not edit ", XElement ("Project", properties, items)) + + // XDocument breaks lines as the operating system does, and the other generated file has "\n" everywhere + let propsText = props.ToString().ReplaceLineEndings "\n" + + File.writeString false assemblyInfoPropsPath $"%s{propsText}\n" + + let versionInformation projectName = + let literals = [ + "AssemblyTitle", projectName + "AssemblyProduct", productName + "AssemblyVersion", latestEntry.AssemblyVersion + match releaseDate with + | Some date -> "AssemblyMetadata_ReleaseDate", date + | None -> () + "AssemblyFileVersion", latestEntry.AssemblyVersion + "AssemblyInformationalVersion", latestEntry.AssemblyVersion + "AssemblyMetadata_ReleaseChannel", releaseChannel + "AssemblyMetadata_GitHash", gitHash + ] + + [ + "// Auto-Generated by FAKE; do not edit" + "namespace System" + "" + "module internal AssemblyVersionInformation =" + yield! + literals + |> List.map (fun (name, value) -> $" let [] %s{name} = \"%s{value}\"") + ] + |> String.concat "\n" + + let writeVersionInformation (projectPath : string) = + // The project path is not null, so neither is the name + let projectName = IO.Path.GetFileNameWithoutExtension projectPath |> nonNull + File.writeString false (Path.getDirectory projectPath "AssemblyInfo.fs") $"%s{versionInformation projectName}\n" + + !!(rootDirectory "src/**/*.fsproj") + |> Seq.iter writeVersionInformation let dotnetPack ctx = // Get release notes with properly-linked version number @@ -534,6 +581,7 @@ let gitRelease _ = !!(rootDirectory "src/**/AssemblyInfo.fs") ++ (rootDirectory "tests/**/AssemblyInfo.fs") + ++ assemblyInfoPropsPath |> Seq.iter (Git.Staging.stageFile "" >> ignore) let msg = $"release: bump version to `%s{latestEntry.NuGetVersion}`\n\n%s{releaseNotesGitCommitFormat}" diff --git a/build/build.fsproj b/build/build.fsproj index 95ccdb3c..26c77382 100644 --- a/build/build.fsproj +++ b/build/build.fsproj @@ -25,7 +25,6 @@ - diff --git a/src/AssemblyInfo.Build.props b/src/AssemblyInfo.Build.props new file mode 100644 index 00000000..924e6532 --- /dev/null +++ b/src/AssemblyInfo.Build.props @@ -0,0 +1,15 @@ + + + + FSharp.Collections.Immutable + 1.0.0 + 1.0.0 + 1.0.0 + false + + + + + + + diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 5ea09a4f..59621c56 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,5 +1,7 @@ + + true true diff --git a/src/FSharp.Collections.Immutable/AssemblyInfo.fs b/src/FSharp.Collections.Immutable/AssemblyInfo.fs index db10dac4..48935162 100644 --- a/src/FSharp.Collections.Immutable/AssemblyInfo.fs +++ b/src/FSharp.Collections.Immutable/AssemblyInfo.fs @@ -1,16 +1,5 @@ // Auto-Generated by FAKE; do not edit namespace System -open System.Reflection - -[] -[] -[] -[] -[] -[] -[] -[] -do () module internal AssemblyVersionInformation = let [] AssemblyTitle = "FSharp.Collections.Immutable"