diff --git a/.gitignore b/.gitignore index a50662fe..c5a1b2de 100644 --- a/.gitignore +++ b/.gitignore @@ -202,4 +202,5 @@ FakesAssemblies/ tools/ build/ .nuget/ -.dotnet/ \ No newline at end of file +.dotnet/ +.idea \ No newline at end of file diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 94bd7748..c05f4cef 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,38 +1,2 @@ -### 0.9.8 January 31 2018 #### -**Maintenance release for Hyperion v0.9.*** -This small patch conists of the following bug fixes to Hyperion v0.9.* branch: - -* [Support for FSharpSet](https://github.com/akkadotnet/Hyperion/pull/92) - -### 0.9.7 January 18 2018 #### -**Maintenance release for Hyperion v0.9.*** - -This patch mostly contains bugfixes and enhancements to the existing Hyperion v0.9.* branch. No major API changes have been made. - -* [EnumerableSerializeFactory fixes](https://github.com/akkadotnet/Hyperion/pull/81) -* [Fix: ObjectDisposedException cannot be deserialized](https://github.com/akkadotnet/Hyperion/issues/64) -* [Added support for DateTimeOffset as a primitive](https://github.com/akkadotnet/Hyperion/pull/79) - -You can [see the full set of changes for Hyperion v0.9.7 here](https://github.com/akkadotnet/Hyperion/milestone/5). - -### 0.9.6 August 17 2017 - -Resolving issues with conflicting binary being downloaded via NuGet with version bump. - -### 0.9.5 August 11 2017 - -Provides support for .NET Standard 1.6. - -See the full set of changes here: [Hyperion 0.9.5](https://github.com/akkadotnet/Hyperion/milestone/3) - -### 0.9.2 January 05 2017 -Includes bug fixes for immutable data structures and lists. - -See the full set of changes here: [Hyperion 0.9.2 Milestone](https://github.com/akkadotnet/Hyperion/milestone/2) - -### 0.9.1 January 02 2017 -Includes bug fixes for numerous data structures as well as improved build chain and tools standardization. - -See the full set of changes here: [Hyperion 0.9.1 Milestone](https://github.com/akkadotnet/Hyperion/milestone/1) - -### New in 0.9.0 (Released 2016/11/24) +### 0.9.9 October 10 2019 #### +Hyperion now supports .NET Core 3.0. \ No newline at end of file diff --git a/build-system/README.md b/build-system/README.md new file mode 100644 index 00000000..17b4b40a --- /dev/null +++ b/build-system/README.md @@ -0,0 +1,7 @@ +# Azure Pipelines Build Files +These `.yaml` files are used by Windows Azure DevOps Pipelines to help execute the following types of builds: + +- Pull request validation on Linux (Mono / .NET Core) +- Pull request validation on Windows (.NET Framework / .NET Core) +- NuGet releases with automatic release notes posted to a Github Release repository. + diff --git a/build-system/azure-pipeline.template.yaml b/build-system/azure-pipeline.template.yaml new file mode 100644 index 00000000..feba5487 --- /dev/null +++ b/build-system/azure-pipeline.template.yaml @@ -0,0 +1,55 @@ +parameters: + name: '' + vmImage: '' + scriptFileName: '' + scriptArgs: 'all' + timeoutInMinutes: 120 + outputDirectory: 'bin/nuget' + +jobs: + - job: ${{ parameters.name }} + timeoutInMinutes: ${{ parameters.timeoutInMinutes }} + pool: + vmImage: ${{ parameters.vmImage }} + steps: + - checkout: self # self represents the repo where the initial Pipelines YAML file was found + clean: false # whether to fetch clean each time + submodules: recursive # set to 'true' for a single level of submodules or 'recursive' to get submodules of submodules + persistCredentials: true + # Linux or macOS + - task: Bash@3 + displayName: Linux / OSX Build + inputs: + filePath: ${{ parameters.scriptFileName }} + arguments: ${{ parameters.scriptArgs }} + continueOnError: true + condition: in( variables['Agent.OS'], 'Linux', 'Darwin' ) + # Windows + - task: BatchScript@1 + displayName: Windows Build + inputs: + filename: ${{ parameters.scriptFileName }} + arguments: ${{ parameters.scriptArgs }} + continueOnError: true + condition: eq( variables['Agent.OS'], 'Windows_NT' ) + - task: PublishTestResults@2 + inputs: + testRunner: VSTest + testResultsFiles: '**/*.trx' #TestResults folder usually + testRunTitle: ${{ parameters.name }} + mergeTestResults: true + - task: CopyFiles@2 + displayName: 'Copy Build Output' + inputs: + sourceFolder: ${{ parameters.outputDirectory }} + contents: '**\*' + targetFolder: $(Build.ArtifactStagingDirectory) + continueOnError: boolean # 'true' if future steps should run even if this step fails; defaults to 'false' + - task: PublishBuildArtifacts@1 + inputs: + pathtoPublish: '$(Build.ArtifactStagingDirectory)' + artifactName: ${{ parameters.name }} + - script: 'echo 1>&2' + failOnStderr: true + displayName: 'If above is partially succeeded, then fail' + condition: eq(variables['Agent.JobStatus'], 'SucceededWithIssues') \ No newline at end of file diff --git a/build-system/linux-pr-validation.yaml b/build-system/linux-pr-validation.yaml new file mode 100644 index 00000000..61f748e4 --- /dev/null +++ b/build-system/linux-pr-validation.yaml @@ -0,0 +1,22 @@ +# Pull request validation for Linux against the `dev` and `master` branches +# See https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema for reference +trigger: + branches: + include: + - dev + - master + +name: $(Year:yyyy).$(Month).$(DayOfMonth)$(Rev:.r) + +pr: + autoCancel: true # indicates whether additional pushes to a PR should cancel in-progress runs for the same PR. Defaults to true + branches: + include: [ dev, master ] # branch names which will trigger a build + +jobs: +- template: azure-pipeline.template.yaml + parameters: + name: Ubuntu + vmImage: 'ubuntu-16.04' + scriptFileName: ./build.sh + scriptArgs: all \ No newline at end of file diff --git a/build-system/nightly-builds.yaml b/build-system/nightly-builds.yaml new file mode 100644 index 00000000..c8e91b27 --- /dev/null +++ b/build-system/nightly-builds.yaml @@ -0,0 +1,26 @@ +# Release task for PbLib projects +# See https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema for reference + +pool: + vmImage: vs2017-win2016 + demands: Cmd + +trigger: none +pr: none + +schedules: +- cron: "0 0 * * *" + displayName: Daily midnight build + branches: + include: + - dev + +variables: + - group: nugetKeys #create this group with SECRET variables `nugetKey` + +steps: +- task: BatchScript@1 + displayName: 'FAKE Build' + inputs: + filename: build.cmd + arguments: 'Nuget nugetprerelease=dev nugetpublishurl=$(nightlyUrl) nugetkey=$(nightlyKey)' \ No newline at end of file diff --git a/build-system/pr-validation.yaml b/build-system/pr-validation.yaml new file mode 100644 index 00000000..99a80dfb --- /dev/null +++ b/build-system/pr-validation.yaml @@ -0,0 +1,30 @@ +# Pull request validation for Windows against the `dev` and `master` branches +# See https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema for reference +trigger: + branches: + include: + - dev + - master + +pr: + autoCancel: true # indicates whether additional pushes to a PR should cancel in-progress runs for the same PR. Defaults to true + branches: + include: [ dev, master ] # branch names which will trigger a build + +name: $(Year:yyyy).$(Month).$(DayOfMonth)$(Rev:.r) + +jobs: + - template: azure-pipeline.template.yaml + parameters: + name: 'windows_pr' + displayName: 'Windows PR Validation' + vmImage: 'vs2017-win2016' + scriptFileName: build.cmd + scriptArgs: all + - template: azure-pipeline.template.yaml + parameters: + name: 'linux_pr' + displayName: 'Linux PR Validation' + vmImage: 'ubuntu-16.04' + scriptFileName: ./build.sh + scriptArgs: all \ No newline at end of file diff --git a/build-system/windows-pr-validation.yaml b/build-system/windows-pr-validation.yaml new file mode 100644 index 00000000..47f6ea3b --- /dev/null +++ b/build-system/windows-pr-validation.yaml @@ -0,0 +1,22 @@ +# Pull request validation for Windows against the `dev` and `master` branches +# See https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema for reference +trigger: + branches: + include: + - dev + - master + +pr: + autoCancel: true # indicates whether additional pushes to a PR should cancel in-progress runs for the same PR. Defaults to true + branches: + include: [ dev, master ] # branch names which will trigger a build + +name: $(Year:yyyy).$(Month).$(DayOfMonth)$(Rev:.r) + +jobs: +- template: azure-pipeline.template.yaml + parameters: + name: Windows + vmImage: 'vs2017-win2016' + scriptFileName: build.cmd + scriptArgs: all \ No newline at end of file diff --git a/build-system/windows-release.yaml b/build-system/windows-release.yaml new file mode 100644 index 00000000..6e3b17e4 --- /dev/null +++ b/build-system/windows-release.yaml @@ -0,0 +1,38 @@ +# Release task for PbLib projects +# See https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema for reference + +pool: + vmImage: vs2017-win2016 + demands: Cmd + +trigger: + branches: + include: + - refs/tags/* +pr: none + +variables: + - group: nugetKeys #create this group with SECRET variables `nugetKey` + - name: githubConnectionName + value: AkkaDotNet_Releases #replace this + - name: projectName + value: Hyperion #replace this + - name: githubRepositoryName + value: akkadotnet/Hyperion #replace this + +steps: +- task: BatchScript@1 + displayName: 'FAKE Build' + inputs: + filename: build.cmd + arguments: 'All SignClientUser=$(signingUsername) SignClientSecret=$(signingPassword) nugetpublishurl=https://www.nuget.org/api/v2/package nugetkey=$(hyperionKey)' + +- task: GitHubRelease@0 + displayName: 'GitHub release (create)' + inputs: + gitHubConnection: $(githubConnectionName) + repositoryName: $(githubRepositoryName) + title: '$(projectName) v$(Build.SourceBranchName)' + releaseNotesFile: 'RELEASE_NOTES.md' + assets: | + bin\nuget\*.nupkg \ No newline at end of file diff --git a/build.fsx b/build.fsx index 3a3c1bc2..33ba702c 100644 --- a/build.fsx +++ b/build.fsx @@ -8,15 +8,22 @@ open System.Text open Fake open Fake.DotNetCli open Fake.DocFxHelper -open Fake.FileHelper // Information about the project for Nuget and Assembly info files +let product = "Hyperion" let configuration = "Release" +// Metadata used when signing packages and DLLs +let signingName = "Hyperion" +let signingDescription = "A high performance polymorphic serializer for the .NET framework" +let signingUrl = "" + + // Read release notes and version let solutionFile = FindFirstMatchingFile "*.sln" __SOURCE_DIRECTORY__ // dynamically look up the solution let buildNumber = environVarOrDefault "BUILD_NUMBER" "0" -let preReleaseVersionSuffix = (if (not (buildNumber = "0")) then (buildNumber) else "") + "-beta" +let hasTeamCity = (not (buildNumber = "0")) // check if we have the TeamCity environment variable for build # set +let preReleaseVersionSuffix = "beta" + (if (not (buildNumber = "0")) then (buildNumber) else DateTime.UtcNow.Ticks.ToString()) let versionSuffix = match (getBuildParam "nugetprerelease") with | "dev" -> preReleaseVersionSuffix @@ -33,19 +40,14 @@ let outputTests = __SOURCE_DIRECTORY__ @@ "TestResults" let outputPerfTests = __SOURCE_DIRECTORY__ @@ "PerfResults" let outputNuGet = output @@ "nuget" -// Copied from original NugetCreate target -let nugetDir = output @@ "nuget" -let workingDir = output @@ "build" -let nugetExe = FullName @"./tools/nuget.exe" - Target "Clean" (fun _ -> + ActivateFinalTarget "KillCreatedProcesses" + CleanDir output CleanDir outputTests CleanDir outputPerfTests CleanDir outputNuGet CleanDir "docs/_site" - CleanDirs !! "./**/bin" - CleanDirs !! "./**/obj" ) Target "AssemblyInfo" (fun _ -> @@ -53,29 +55,14 @@ Target "AssemblyInfo" (fun _ -> XmlPokeInnerText "./src/common.props" "//Project/PropertyGroup/PackageReleaseNotes" (releaseNotes.Notes |> String.concat "\n") ) -Target "RestorePackages" (fun _ -> - DotNetCli.Restore +Target "Build" (fun _ -> + DotNetCli.Build (fun p -> { p with Project = solutionFile - NoCache = false}) + Configuration = configuration }) // "Rebuild" ) -Target "Build" (fun _ -> - let additionalArgs = if versionSuffix.Length > 0 then [sprintf "/p:VersionSuffix=%s" versionSuffix;"--no-incremental"] else ["--no-incremental"] - - let runSingleProject project = - DotNetCli.Build - (fun p -> - { p with - Project = project - Configuration = configuration - AdditionalArgs = additionalArgs}) // "Rebuild" - - let assemblies = !! "./src/**/*.*sproj" - - assemblies |> Seq.iter (runSingleProject) -) //-------------------------------------------------------------------------------- // Tests targets @@ -105,12 +92,17 @@ Target "RunTests" (fun _ -> | _ -> !! "./src/**/*.Tests.csproj" // if you need to filter specs for Linux vs. Windows, do it here let runSingleProject project = + let arguments = + match (hasTeamCity) with + | true -> (sprintf "test -c Release --no-build --logger:trx --logger:\"console;verbosity=normal\" --results-directory %s -- -parallel none -teamcity" (outputTests)) + | false -> (sprintf "test -c Release --no-build --logger:trx --logger:\"console;verbosity=normal\" --results-directory %s -- -parallel none" (outputTests)) + let result = ExecProcess(fun info -> info.FileName <- "dotnet" info.WorkingDirectory <- (Directory.GetParent project).FullName - info.Arguments <- (sprintf "xunit -c Release -nobuild -parallel none -teamcity -xml %s_xunit.xml" (outputTests @@ fileNameWithoutExt project))) (TimeSpan.FromMinutes 30.) + info.Arguments <- arguments) (TimeSpan.FromMinutes 30.0) - ResultHandling.failBuildIfXUnitReportedError TestRunnerErrorLevel.DontFailBuild result + ResultHandling.failBuildIfXUnitReportedError TestRunnerErrorLevel.Error result projects |> Seq.iter (log) projects |> Seq.iter (runSingleProject) @@ -120,7 +112,7 @@ Target "NBench" <| fun _ -> let nbenchTestPath = findToolInSubPath "NBench.Runner.exe" (toolsDir @@ "NBench.Runner*") printfn "Using NBench.Runner: %s" nbenchTestPath - let nbenchTestAssemblies = !! "./src/**/*Tests.Performance.csproj" + let nbenchTestAssemblies = !! "./src/**/bin/**/*Tests.Performance.dll" // doesn't support .NET Core at the moment let runNBench assembly = let includes = getBuildParam "include" @@ -146,11 +138,60 @@ Target "NBench" <| fun _ -> info.FileName <- nbenchTestPath info.WorkingDirectory <- (Path.GetDirectoryName (FullName nbenchTestPath)) info.Arguments <- args) (System.TimeSpan.FromMinutes 45.0) (* Reasonably long-running task. *) + if result <> 0 then failwithf "NBench.Runner failed. %s %s" nbenchTestPath args nbenchTestAssemblies |> Seq.iter runNBench +//-------------------------------------------------------------------------------- +// Code signing targets +//-------------------------------------------------------------------------------- +Target "SignPackages" (fun _ -> + let canSign = hasBuildParam "SignClientSecret" && hasBuildParam "SignClientUser" + if(false) then + log "Signing information is available." + + let assemblies = !! (outputNuGet @@ "*.nupkg") + + let signPath = + let globalTool = tryFindFileOnPath "SignClient.exe" + match globalTool with + | Some t -> t + | None -> if isWindows then findToolInSubPath "SignClient.exe" "tools/signclient" + elif isMacOS then findToolInSubPath "SignClient" "tools/signclient" + else findToolInSubPath "SignClient" "tools/signclient" + + let signAssembly assembly = + let args = StringBuilder() + |> append "sign" + |> append "--config" + |> append (__SOURCE_DIRECTORY__ @@ "appsettings.json") + |> append "-i" + |> append assembly + |> append "-r" + |> append (getBuildParam "SignClientUser") + |> append "-s" + |> append (getBuildParam "SignClientSecret") + |> append "-n" + |> append signingName + |> append "-d" + |> append signingDescription + |> append "-u" + |> append signingUrl + |> toText + + let result = ExecProcess(fun info -> + info.FileName <- signPath + info.WorkingDirectory <- __SOURCE_DIRECTORY__ + info.Arguments <- args) (System.TimeSpan.FromMinutes 5.0) (* Reasonably long-running task. *) + if result <> 0 then failwithf "SignClient failed.%s" args + + assemblies |> Seq.iter (signAssembly) + else + log "SignClientSecret not available. Skipping signing" +) + //-------------------------------------------------------------------------------- // Nuget targets //-------------------------------------------------------------------------------- @@ -162,7 +203,6 @@ Target "CreateNuget" (fun _ -> let projects = !! "src/**/*.csproj" -- "src/**/*Tests.csproj" // Don't publish unit tests -- "src/**/*Tests*.csproj" - -- "src/**/*.Demo.csproj" // Don't publish demo apps let runSingleProject project = DotNetCli.Pack @@ -170,7 +210,7 @@ Target "CreateNuget" (fun _ -> { p with Project = project Configuration = configuration - AdditionalArgs = ["--include-symbols"] + AdditionalArgs = ["--include-symbols --no-build"] VersionSuffix = overrideVersionSuffix project OutputPath = outputNuGet }) @@ -220,6 +260,19 @@ Target "DocFx" (fun _ -> DocFxJson = docsPath @@ "docfx.json" }) ) +//-------------------------------------------------------------------------------- +// Cleanup +//-------------------------------------------------------------------------------- + +FinalTarget "KillCreatedProcesses" (fun _ -> + log "Shutting down dotnet build-server" + let result = ExecProcess(fun info -> + info.FileName <- "dotnet" + info.WorkingDirectory <- __SOURCE_DIRECTORY__ + info.Arguments <- "build-server shutdown") (System.TimeSpan.FromMinutes 2.0) + if result <> 0 then failwithf "dotnet build-server shutdown failed" +) + //-------------------------------------------------------------------------------- // Help //-------------------------------------------------------------------------------- @@ -230,11 +283,12 @@ Target "Help" <| fun _ -> "./build.ps1 [target]" "" " Targets for building:" - " * Build Builds" - " * Nuget Create and optionally publish nugets packages" - " * RunTests Runs tests" - " * All Builds, run tests, creates and optionally publish nuget packages" - " * DocFx Creates a DocFx-based website for this solution" + " * Build Builds" + " * Nuget Create and optionally publish nugets packages" + " * SignPackages Signs all NuGet packages, provided that the following arguments are passed into the script: SignClientSecret={secret} and SignClientUser={username}" + " * RunTests Runs tests" + " * All Builds, run tests, creates and optionally publish nuget packages" + " * DocFx Creates a DocFx-based website for this solution" "" " Other Targets" " * Help Display this help" @@ -249,21 +303,22 @@ Target "All" DoNothing Target "Nuget" DoNothing // build dependencies -"Clean" ==> "RestorePackages" ==> "AssemblyInfo" ==> "Build" ==> "BuildRelease" +"Clean" ==> "AssemblyInfo" ==> "Build" ==> "BuildRelease" // tests dependencies +"Build" ==> "RunTests" // nuget dependencies -"Clean" ==> "RestorePackages" ==> "Build" ==> "CreateNuget" -"CreateNuget" ==> "PublishNuget" ==> "Nuget" +"Clean" ==> "Build" ==> "CreateNuget" +"CreateNuget" ==> "SignPackages" ==> "PublishNuget" ==> "Nuget" // docs -"BuildRelease" ==> "Docfx" +"Clean" ==> "BuildRelease" ==> "Docfx" // all "BuildRelease" ==> "All" "RunTests" ==> "All" -//"NBench" ==> "All" +"NBench" ==> "All" "Nuget" ==> "All" RunTargetOrDefault "Help" \ No newline at end of file diff --git a/build.ps1 b/build.ps1 index f2180fc3..d2581ca4 100644 --- a/build.ps1 +++ b/build.ps1 @@ -30,14 +30,13 @@ Param( ) $FakeVersion = "4.61.2" -$NBenchVersion = "1.0.1" $DotNetChannel = "LTS"; -$DotNetVersion = "2.0.0"; -$DotNetInstallerUri = "https://raw.githubusercontent.com/dotnet/cli/v$DotNetVersion/scripts/obtain/dotnet-install.ps1"; +$DotNetVersion = "3.0.100"; +$DotNetInstallerUri = "https://dot.net/v1/dotnet-install.ps1"; $NugetVersion = "4.1.0"; $NugetUrl = "https://dist.nuget.org/win-x86-commandline/v$NugetVersion/nuget.exe" $ProtobufVersion = "3.2.0" -$DocfxVersion = "2.21.1" +$DocfxVersion = "2.40.5" # Make sure tools folder exists $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent @@ -88,8 +87,10 @@ if($FoundDotNetCliVersion -ne $DotNetVersion) { $env:PATH = "$InstallPath;$env:PATH" $env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 $env:DOTNET_CLI_TELEMETRY_OPTOUT=1 + $env:DOTNET_ROOT=$InstallPath } + ########################################################################### # INSTALL NUGET ########################################################################### @@ -115,20 +116,6 @@ if (!(Test-Path $FakeExePath)) { } } -########################################################################### -# INSTALL NBench Runner -########################################################################### - -# Make sure NBench Runner has been installed. -$NBenchDllPath = Join-Path $ToolPath "NBench.Runner/lib/net45/NBench.Runner.exe" -if (!(Test-Path $NBenchDllPath)) { - Write-Host "Installing NBench..." - Invoke-Expression "&`"$NugetPath`" install NBench.Runner -ExcludeVersion -Version $NBenchVersion -OutputDirectory `"$ToolPath`"" | Out-Null; - if ($LASTEXITCODE -ne 0) { - Throw "An error occured while restoring NBench.Runner from NuGet." - } -} - ########################################################################### # Docfx ########################################################################### diff --git a/build.sh b/build.sh old mode 100755 new mode 100644 index 90e7d528..230cfe96 --- a/build.sh +++ b/build.sh @@ -10,8 +10,11 @@ NUGET_EXE=$TOOLS_DIR/nuget.exe NUGET_URL=https://dist.nuget.org/win-x86-commandline/v4.1.0/nuget.exe FAKE_VERSION=4.61.2 FAKE_EXE=$TOOLS_DIR/FAKE/tools/FAKE.exe -DOTNET_VERSION=2.0.3 -DOTNET_INSTALLER_URL=https://raw.githubusercontent.com/dotnet/cli/v$DOTNET_VERSION/scripts/obtain/dotnet-install.sh +DOTNET_VERSION=3.0.100 +DOTNET_INSTALLER_URL=https://dot.net/v1/dotnet-install.sh +DOTNET_CHANNEL=LTS; +DOCFX_VERSION=2.40.5 +DOCFX_EXE=$TOOLS_DIR/docfx.console/tools/docfx.exe # Define default arguments. TARGET="Default" @@ -47,13 +50,14 @@ if [ ! -d "$SCRIPT_DIR/.dotnet" ]; then mkdir "$SCRIPT_DIR/.dotnet" fi curl -Lsfo "$SCRIPT_DIR/.dotnet/dotnet-install.sh" $DOTNET_INSTALLER_URL -bash "$SCRIPT_DIR/.dotnet/dotnet-install.sh" --version $DOTNET_VERSION --install-dir .dotnet --no-path +bash "$SCRIPT_DIR/.dotnet/dotnet-install.sh" --version $DOTNET_VERSION --channel $DOTNET_CHANNEL --install-dir .dotnet --no-path export PATH="$SCRIPT_DIR/.dotnet":$PATH export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 export DOTNET_CLI_TELEMETRY_OPTOUT=1 chmod -R 0755 ".dotnet" "$SCRIPT_DIR/.dotnet/dotnet" --info + ########################################################################### # INSTALL NUGET ########################################################################### @@ -86,6 +90,23 @@ if [ ! -f "$FAKE_EXE" ]; then exit 1 fi +########################################################################### +# INSTALL DOCFX +########################################################################### +if [ ! -f "$DOCFX_EXE" ]; then + mono "$NUGET_EXE" install docfx.console -ExcludeVersion -Version $DOCFX_VERSION -OutputDirectory "$TOOLS_DIR" + if [ $? -ne 0 ]; then + echo "An error occured while installing DocFx." + exit 1 + fi +fi + +# Make sure that DocFx has been installed. +if [ ! -f "$DOCFX_EXE" ]; then + echo "Could not find docfx.exe at '$DOCFX_EXE'." + exit 1 +fi + ########################################################################### # WORKAROUND FOR MONO ########################################################################### diff --git a/src/Hyperion.Benchmarks/Hyperion.Benchmarks.csproj b/src/Hyperion.Benchmarks/Hyperion.Benchmarks.csproj index 35efa975..2c907735 100644 --- a/src/Hyperion.Benchmarks/Hyperion.Benchmarks.csproj +++ b/src/Hyperion.Benchmarks/Hyperion.Benchmarks.csproj @@ -7,7 +7,7 @@ - + diff --git a/src/Hyperion.Tests.FSharpData/Hyperion.Tests.FSharpData.fsproj b/src/Hyperion.Tests.FSharpData/Hyperion.Tests.FSharpData.fsproj index a50bae24..ae47fb53 100644 --- a/src/Hyperion.Tests.FSharpData/Hyperion.Tests.FSharpData.fsproj +++ b/src/Hyperion.Tests.FSharpData/Hyperion.Tests.FSharpData.fsproj @@ -1,7 +1,7 @@  - netstandard1.6 + netstandard2.0 diff --git a/src/Hyperion.Tests/Bugs.cs b/src/Hyperion.Tests/Bugs.cs index 12e7551c..1be64854 100644 --- a/src/Hyperion.Tests/Bugs.cs +++ b/src/Hyperion.Tests/Bugs.cs @@ -126,6 +126,21 @@ public void CanSerializeImmutableGenericInterfaces() } } + [Fact] + public void CanSerializeUri() + { + var stream = new MemoryStream(); + var msg = new Uri("http://localhost:9202/", UriKind.RelativeOrAbsolute); + var serializer = new Serializer(new SerializerOptions(preserveObjectReferences: true, versionTolerance: true)); + serializer.Serialize(msg, stream); + stream.Position = 0; + var res = serializer.Deserialize(stream); + + Assert.Equal(msg, res); + Assert.Equal(stream.Length, stream.Position); + } + + public class SnapshotSelectionCriteria { public static SnapshotSelectionCriteria Latest { get; set; } = new SnapshotSelectionCriteria() diff --git a/src/Hyperion.Tests/CollectionTests.cs b/src/Hyperion.Tests/CollectionTests.cs index 12389be2..b316801a 100644 --- a/src/Hyperion.Tests/CollectionTests.cs +++ b/src/Hyperion.Tests/CollectionTests.cs @@ -316,6 +316,59 @@ public void CanSerializeStack() Assert.Equal(expected.ToList(), actual.ToList()); } + [Fact] + public void CanSerializeArray2DOfInt() + { + var expected = new int[,] { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 } }; // OK + Serialize(expected); + Reset(); + var actual = Deserialize(); + for (int i = expected.GetLowerBound(0); i < expected.GetUpperBound(0); i++) + { + for (int j = expected.GetLowerBound(1); j < expected.GetUpperBound(1); j++) + { + Assert.Equal(expected[i, j], actual[i, j]); + } + } + } + + [Fact] + public void CanSerializeArray2DOfObj() + { + var expected = new object[,] { { "Header1", 2 }, { "Header2", 4 }}; // OK + Serialize(expected); + Reset(); + var actual = Deserialize(); + for (int i = expected.GetLowerBound(0); i < expected.GetUpperBound(0); i++) + { + for (int j = expected.GetLowerBound(1); j < expected.GetUpperBound(1); j++) + { + Assert.Equal(expected[i, j], actual[i, j]); + } + } + } + + [Fact] + public void CanSerializeArray3DOfInt() + { + int[,,] expected = new int[,,] { { { 1, 2, 3 }, { 4, 5, 6 } }, + { { 7, 8, 9 }, { 10, 11, 12 } } }; + Serialize(expected); + Reset(); + var actual = Deserialize(); + for (int i = expected.GetLowerBound(0); i < expected.GetUpperBound(0); i++) + { + for (int j = expected.GetLowerBound(1); j < expected.GetUpperBound(1); j++) + { + for (int m = expected.GetLowerBound(2); j < expected.GetUpperBound(2); j++) + { + Assert.Equal(expected[i, j, m], actual[i, j, m]); + } + } + } + } + + [Fact] public void Issue18() { diff --git a/src/Hyperion.Tests/ExpressionTests.cs b/src/Hyperion.Tests/ExpressionTests.cs index 58db7693..cc693634 100644 --- a/src/Hyperion.Tests/ExpressionTests.cs +++ b/src/Hyperion.Tests/ExpressionTests.cs @@ -184,7 +184,7 @@ public void CanSerializeIndexExpression() Assert.Equal(expr.NodeType, deserialized.NodeType); Assert.Equal(expr.Type, deserialized.Type); Assert.Equal(expr.Indexer, deserialized.Indexer); - Assert.Equal(1, deserialized.Arguments.Count); + Assert.Single(deserialized.Arguments); Assert.Equal(expr.Arguments[0].ConstantValue(), deserialized.Arguments[0].ConstantValue()); var actual = (int[])deserialized.Object.ConstantValue(); Assert.Equal(value[0], actual[0]); @@ -301,7 +301,7 @@ public void CanSerializeMethodCallExpression() Assert.Equal(expr.NodeType, deserialized.NodeType); Assert.Equal(expr.Method, deserialized.Method); Assert.Equal(expr.Object.ConstantValue(), deserialized.Object.ConstantValue()); - Assert.Equal(1, deserialized.Arguments.Count); + Assert.Single(deserialized.Arguments); Assert.Equal(expr.Arguments[0].ConstantValue(), deserialized.Arguments[0].ConstantValue()); } } @@ -523,7 +523,7 @@ public void CanSerializeElementInit() stream.Position = 0; var deserialized = serializer.Deserialize(stream); Assert.Equal(expr.AddMethod, deserialized.AddMethod); - Assert.Equal(1, deserialized.Arguments.Count); + Assert.Single(deserialized.Arguments); Assert.Equal(expr.Arguments[0].ConstantValue(), deserialized.Arguments[0].ConstantValue()); } } diff --git a/src/Hyperion.Tests/Hyperion.Tests.csproj b/src/Hyperion.Tests/Hyperion.Tests.csproj index 7c291918..f97c9736 100644 --- a/src/Hyperion.Tests/Hyperion.Tests.csproj +++ b/src/Hyperion.Tests/Hyperion.Tests.csproj @@ -1,17 +1,15 @@ - + - net461;netcoreapp2.0 - 2.0.3 + net461;netcoreapp2.1;netcoreapp3.0 - + - diff --git a/src/Hyperion/Extensions/ReflectionEx.cs b/src/Hyperion/Extensions/ReflectionEx.cs index 4d445d75..12f5a42b 100644 --- a/src/Hyperion/Extensions/ReflectionEx.cs +++ b/src/Hyperion/Extensions/ReflectionEx.cs @@ -12,10 +12,6 @@ using System.Linq; using System.Reflection; -#if SERIALIZATION - -#endif - namespace Hyperion.Extensions { internal static class BindingFlagsEx diff --git a/src/Hyperion/Extensions/TypeEx.cs b/src/Hyperion/Extensions/TypeEx.cs index 7767e761..a2aa56f7 100644 --- a/src/Hyperion/Extensions/TypeEx.cs +++ b/src/Hyperion/Extensions/TypeEx.cs @@ -13,9 +13,7 @@ using System.IO; using System.Linq; using System.Reflection; -#if SERIALIZATION -using System.Runtime.Serialization; -#endif +using System.Text.RegularExpressions; namespace Hyperion.Extensions { @@ -68,7 +66,7 @@ public static bool IsHyperionPrimitive(this Type type) //add TypeSerializer with null support } -#if !SERIALIZATION +#if NETSTANDARD16 //HACK: the GetUnitializedObject actually exists in .NET Core, its just not public private static readonly Func getUninitializedObjectDelegate = (Func) typeof(string) @@ -84,10 +82,7 @@ public static object GetEmptyObject(this Type type) return getUninitializedObjectDelegate(type); } #else - public static object GetEmptyObject(this Type type) - { - return FormatterServices.GetUninitializedObject(type); - } + public static object GetEmptyObject(this Type type) => System.Runtime.Serialization.FormatterServices.GetUninitializedObject(type); #endif public static bool IsOneDimensionalArray(this Type type) @@ -203,6 +198,10 @@ public static int GetTypeSize(this Type type) private static readonly string CoreAssemblyName = GetCoreAssemblyName(); + private static readonly Regex cleanAssemblyVersionRegex = new Regex( + "(, Version=([\\d\\.]+))?(, Culture=[^,\\] \\t]+)?(, PublicKeyToken=(null|[\\da-f]+))?", + RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); + private static string GetCoreAssemblyName() { var name = 1.GetType().AssemblyQualifiedName; @@ -214,9 +213,7 @@ public static string GetShortAssemblyQualifiedName(this Type self) { var name = self.AssemblyQualifiedName; name = name.Replace(CoreAssemblyName, ",%core%"); - name = name.Replace(", Culture=neutral", ""); - name = name.Replace(", PublicKeyToken=null", ""); - name = name.Replace(", Version=1.0.0.0", ""); //TODO: regex or whatever... + name = cleanAssemblyVersionRegex.Replace(name, string.Empty); return name; } diff --git a/src/Hyperion/Hyperion.csproj b/src/Hyperion/Hyperion.csproj index 608c63da..45761062 100644 --- a/src/Hyperion/Hyperion.csproj +++ b/src/Hyperion/Hyperion.csproj @@ -4,23 +4,36 @@ Hyperion Hyperion, fast binary POCO serializer - netstandard1.6;net45 + netstandard1.6;netstandard2.0;net452 true serialization;poco - 1.6.1 - + + + + + + - + - + + + $(DefineConstants);NETSTANDARD16 + + + + $(DefineConstants);NETSTANDARD20 + + + $(DefineConstants);SERIALIZATION;UNSAFE;NET45 diff --git a/src/Hyperion/SerializerFactories/ExceptionSerializerFactory.cs b/src/Hyperion/SerializerFactories/ExceptionSerializerFactory.cs index 724f4a63..f7ac7b63 100644 --- a/src/Hyperion/SerializerFactories/ExceptionSerializerFactory.cs +++ b/src/Hyperion/SerializerFactories/ExceptionSerializerFactory.cs @@ -37,19 +37,23 @@ public ExceptionSerializerFactory() public override bool CanDeserialize(Serializer serializer, Type type) => CanSerialize(serializer, type); +#if NETSTANDARD16 // Workaround for CoreCLR where FormatterServices.GetUninitializedObject is not public private static readonly Func GetUninitializedObject = (Func) typeof(string).GetTypeInfo().Assembly.GetType("System.Runtime.Serialization.FormatterServices") .GetMethod("GetUninitializedObject", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static) .CreateDelegate(typeof(Func)); +#else + private static readonly Func GetUninitializedObject = System.Runtime.Serialization.FormatterServices.GetUninitializedObject; +#endif public override ValueSerializer BuildSerializer(Serializer serializer, Type type, ConcurrentDictionary typeMapping) { var exceptionSerializer = new ObjectSerializer(type); var hasDefaultConstructor = type.GetTypeInfo().GetConstructor(new Type[0]) != null; - var createInstance = hasDefaultConstructor ? Activator.CreateInstance : GetUninitializedObject; + var createInstance = hasDefaultConstructor ? Activator.CreateInstance : GetUninitializedObject; exceptionSerializer.Initialize((stream, session) => { @@ -60,15 +64,23 @@ public override ValueSerializer BuildSerializer(Serializer serializer, Type type var stackTraceString = stream.ReadString(session); var innerException = stream.ReadObject(session); - _className.SetValue(exception,className); +#if NETSTANDARD20 + _className?.SetValue(exception, className); +#else + _className.SetValue(exception, className); +#endif _message.SetValue(exception, message); _remoteStackTraceString.SetValue(exception, remoteStackTraceString); _stackTraceString.SetValue(exception, stackTraceString); - _innerException.SetValue(exception,innerException); + _innerException.SetValue(exception, innerException); return exception; }, (stream, exception, session) => { +#if NETSTANDARD20 + var className = (string)_className?.GetValue(exception); +#else var className = (string)_className.GetValue(exception); +#endif var message = (string)_message.GetValue(exception); var remoteStackTraceString = (string)_remoteStackTraceString.GetValue(exception); var stackTraceString = (string)_stackTraceString.GetValue(exception); diff --git a/src/Hyperion/SerializerFactories/MultipleDimensionalArraySerialzierFactory.cs b/src/Hyperion/SerializerFactories/MultipleDimensionalArraySerialzierFactory.cs new file mode 100644 index 00000000..cdfab7a2 --- /dev/null +++ b/src/Hyperion/SerializerFactories/MultipleDimensionalArraySerialzierFactory.cs @@ -0,0 +1,151 @@ +#region copyright +// ----------------------------------------------------------------------- +// +// Copyright (C) 2015-2016 AsynkronIT +// Copyright (C) 2016-2016 Akka.NET Team +// +// ----------------------------------------------------------------------- +#endregion + +using System; +using System.Collections.Concurrent; +using System.IO; +using System.Linq; +using System.Reflection; +using Hyperion.Extensions; +using Hyperion.ValueSerializers; + +namespace Hyperion.SerializerFactories +{ + /// we don't support 4 dimensional array now + internal sealed class MultipleDimensionalArraySerialzierFactory : ValueSerializerFactory + { + public override bool CanSerialize(Serializer serializer, Type type) => + /// wo don't support 4 dimensional array now + type.IsArray && type.GetArrayRank() > 1 && type.GetArrayRank() < 4; + + public override bool CanDeserialize(Serializer serializer, Type type) => CanSerialize(serializer, type); + + private static void WriteValues(Array array, Stream stream, Type elementType, ValueSerializer elementSerializer, SerializerSession session) + { + for (var i = 0; i < array.Rank; i ++ ) + { + Int32Serializer.WriteValueImpl(stream, array.GetLength(i), session); + } + var preserveObjectReferences = session.Serializer.Options.PreserveObjectReferences; + foreach (var value in array) + { + stream.WriteObject(value, elementType, elementSerializer, preserveObjectReferences, session); + } + } + + + private static Array ReadValues2D(Stream stream, DeserializerSession session, Array array) + { + for (var i = array.GetLowerBound(0); i <= array.GetUpperBound(0); i++) + { + for (var j = array.GetLowerBound(1); j <= array.GetUpperBound(1); j++) + { + var value = stream.ReadObject(session); + array.SetValue(value, i, j); + } + } + return array; + } + + private static Array ReadValues3D(Stream stream, DeserializerSession session, Array array) + { + for (var i = array.GetLowerBound(0); i <= array.GetUpperBound(0); i++) + { + for (var j = array.GetLowerBound(1); j <= array.GetUpperBound(1); j++) + { + for (var m = array.GetLowerBound(2); m <= array.GetUpperBound(2); m++) + { + var value = stream.ReadObject(session); + array.SetValue(value, i, j, m); + } + + } + } + return array; + } + + + private static ObjectReader CreateReader(bool preserveObjectReferences,int arrayRank, Type elementType) + { + if (arrayRank == 2) + { + ObjectReader reader = (stream, session) => + { + var length1 = stream.ReadInt32(session); + var length2 = stream.ReadInt32(session); + var array = Array.CreateInstance(elementType, length1, length2); + if (preserveObjectReferences) + { + session.TrackDeserializedObject(array); + } + return ReadValues2D(stream, session, array); + }; + return reader; + } + if (arrayRank == 3) + { + ObjectReader reader = (stream, session) => + { + var length1 = stream.ReadInt32(session); + var length2 = stream.ReadInt32(session); + var length3 = stream.ReadInt32(session); + var array = Array.CreateInstance(elementType, length1, length2, length3); + if (preserveObjectReferences) + { + session.TrackDeserializedObject(array); + } + return ReadValues3D(stream, session, array); + }; + return reader; + } + + else + { + throw new UnsupportedTypeException(elementType, "we don't support 4 dimensional array now"); + } + } + + + public override ValueSerializer BuildSerializer(Serializer serializer, Type type, + ConcurrentDictionary typeMapping) + { + var arraySerializer = new ObjectSerializer(type); + + var elementType = + type.GetTypeInfo() + .GetMethods() + .Where(methodInfo => methodInfo.Name == "Get") + .Select(methodInfo => methodInfo.ReturnType) + .FirstOrDefault(); + + var elementSerializer = serializer.GetSerializerByType(elementType); + var preserveObjectReferences = serializer.Options.PreserveObjectReferences; + + var arrayRank = type.GetArrayRank(); + + //TODO: code gen this part + ObjectReader reader = CreateReader(preserveObjectReferences, arrayRank, elementType); + + ObjectWriter writer = (stream, arr, session) => + { + if (preserveObjectReferences) + { + session.TrackSerializedObject(arr); + } + + WriteValues((Array)arr, stream, elementType, elementSerializer, session); + }; + arraySerializer.Initialize(reader, writer); + typeMapping.TryAdd(type, arraySerializer); + return arraySerializer; + } + + + } +} \ No newline at end of file diff --git a/src/Hyperion/SerializerOptions.cs b/src/Hyperion/SerializerOptions.cs index 1422bfc3..31a0c620 100644 --- a/src/Hyperion/SerializerOptions.cs +++ b/src/Hyperion/SerializerOptions.cs @@ -38,6 +38,7 @@ public class SerializerOptions new DefaultDictionarySerializerFactory(), new DictionarySerializerFactory(), new ArraySerializerFactory(), + new MultipleDimensionalArraySerialzierFactory(), #if SERIALIZATION new ISerializableSerializerFactory(), //TODO: this will mess up the indexes in the serializer payload #endif diff --git a/src/Hyperion/SerializerSession.cs b/src/Hyperion/SerializerSession.cs index c395204c..fd4f6a8b 100644 --- a/src/Hyperion/SerializerSession.cs +++ b/src/Hyperion/SerializerSession.cs @@ -12,6 +12,26 @@ namespace Hyperion { + internal class TypedEqualityComparer : IEqualityComparer + { + public static readonly TypedEqualityComparer Instance = new TypedEqualityComparer(); + public new bool Equals(object x, object y) + { + if (EqualityComparer.Default.Equals(x, y)) + { + if (x != null && y != null) + return x.GetType().Equals(y.GetType()); + return true; + } + return false; + } + + public int GetHashCode(object obj) + { + return EqualityComparer.Default.GetHashCode(obj); + } + } + public class SerializerSession { public const int MinBufferSize = 9; @@ -28,9 +48,9 @@ public SerializerSession(Serializer serializer) Serializer = serializer; if (serializer.Options.PreserveObjectReferences) { - _objects = new Dictionary(); + _objects = new Dictionary(TypedEqualityComparer.Instance); } - _nextTypeId = (ushort)(serializer.Options.KnownTypes.Length ); + _nextTypeId = (ushort)(serializer.Options.KnownTypes.Length); } public void TrackSerializedObject(object obj) @@ -60,7 +80,7 @@ public byte[] GetBuffer(int length) if (length <= _buffer.Length) return _buffer; - length = Math.Max(length, _buffer.Length*2); + length = Math.Max(length, _buffer.Length * 2); _buffer = new byte[length]; diff --git a/src/common.props b/src/common.props index ee5ef433..87d141de 100644 --- a/src/common.props +++ b/src/common.props @@ -2,17 +2,16 @@ Copyright © 2016-2017 Akka.NET Team Akka.NET Team - 0.9.8 - Maintenance release for Hyperion v0.9.*** -This small patch conists of the following bug fixes to Hyperion v0.9.*: -[Support for FSharpSet<T>](https://github.com/akkadotnet/Hyperion/pull/92) + 0.9.9 + Hyperion now supports .NET Core 3.0. http://getakka.net/images/akkalogo.png https://github.com/akkadotnet/Hyperion https://github.com/akkadotnet/Hyperion/blob/master/LICENSE $(NoWarn);CS1591 - 2.3.0 - 15.3.0 + 2.4.1 + 16.3.0 + 1.2.2 \ No newline at end of file