|
1 | 1 | open System |
| 2 | +open System.Xml.Linq |
2 | 3 | open Fake.Core |
3 | 4 | open Fake.DotNet |
4 | 5 | open Fake.Tools |
@@ -28,6 +29,9 @@ let rootDirectory = __SOURCE_DIRECTORY__ </> ".." |
28 | 29 |
|
29 | 30 | let productName = "FSharp.Collections.Immutable" |
30 | 31 |
|
| 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 | + |
31 | 35 | let sln = rootDirectory </> "FSharp.Collections.Immutable.slnf" |
32 | 36 |
|
33 | 37 | let srcCodeGlob = |
@@ -432,47 +436,90 @@ let watchTests _ = |
432 | 436 |
|
433 | 437 | cancelEvent.Cancel <- true |
434 | 438 |
|
| 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. |
435 | 444 | let generateAssemblyInfo _ = |
436 | 445 |
|
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 | | - |
444 | 446 | let releaseChannel = |
445 | 447 | match latestEntry.SemVer.PreRelease with |
446 | 448 | | Some pr -> pr.Name |
447 | 449 | | _ -> "release" |
448 | 450 |
|
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 |
456 | 458 | | 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 |
461 | 461 | ] |
462 | 462 |
|
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 | + ) |
465 | 473 |
|
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 | + ) |
467 | 482 |
|
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 |
476 | 523 |
|
477 | 524 | let dotnetPack ctx = |
478 | 525 | // Get release notes with properly-linked version number |
@@ -534,6 +581,7 @@ let gitRelease _ = |
534 | 581 |
|
535 | 582 | !!(rootDirectory </> "src/**/AssemblyInfo.fs") |
536 | 583 | ++ (rootDirectory </> "tests/**/AssemblyInfo.fs") |
| 584 | + ++ assemblyInfoPropsPath |
537 | 585 | |> Seq.iter (Git.Staging.stageFile "" >> ignore) |
538 | 586 |
|
539 | 587 | let msg = $"release: bump version to `%s{latestEntry.NuGetVersion}`\n\n%s{releaseNotesGitCommitFormat}" |
|
0 commit comments