diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 000000000..6ed7f6e71 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,14 @@ +# Refer to https://hub.docker.com/_/microsoft-dotnet-sdk for available versions +FROM mcr.microsoft.com/dotnet/sdk:8.0.300-jammy + +# Installing mono makes `dotnet test` work without errors even for net472. +# But installing it takes a long time, so it's excluded by default. +#RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF +#RUN echo "deb https://download.mono-project.com/repo/ubuntu stable-bionic main" | tee /etc/apt/sources.list.d/mono-official-stable.list +#RUN apt-get update +#RUN DEBIAN_FRONTEND=noninteractive apt-get install -y mono-devel + +# Clear the NUGET_XMLDOC_MODE env var so xml api doc files get unpacked, allowing a rich experience in Intellisense. +# See https://github.com/dotnet/dotnet-docker/issues/2790 for a discussion on this, where the prioritized use case +# was *not* devcontainers, sadly. +ENV NUGET_XMLDOC_MODE= diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..f4e3b31a3 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,20 @@ +{ + "name": "Dev space", + "dockerFile": "Dockerfile", + "settings": { + "terminal.integrated.shell.linux": "/usr/bin/pwsh" + }, + "postCreateCommand": "./init.ps1 -InstallLocality machine", + "extensions": [ + "ms-azure-devops.azure-pipelines", + "ms-dotnettools.csharp", + "k--kato.docomment", + "editorconfig.editorconfig", + "pflannery.vscode-versionlens", + "davidanson.vscode-markdownlint", + "dotjoshjohnson.xml", + "ms-vscode-remote.remote-containers", + "ms-azuretools.vscode-docker", + "ms-vscode.powershell" + ] +} diff --git a/.editorconfig b/.editorconfig index c1f1e3133..c99b5fb73 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,4 @@ -# EditorConfig is awesome:http://EditorConfig.org +# EditorConfig is awesome:http://EditorConfig.org # top-most EditorConfig file root = true @@ -6,8 +6,6 @@ root = true # Don't use tabs for indentation. [*] indent_style = space -insert_final_newline = true -trim_trailing_whitespace = true # (Please don't specify an indent_size here; that has too many unintended consequences.) @@ -18,18 +16,21 @@ indent_style = space # Code files [*.{cs,csx,vb,vbx,h,cpp,idl}] indent_size = 4 +insert_final_newline = true +trim_trailing_whitespace = true -# Xml project files -[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] +# MSBuild project files +[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj,msbuildproj,props,targets}] indent_size = 2 # Xml config files -[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}] +[*.{ruleset,config,nuspec,resx,vsixmanifest,vsct,runsettings}] indent_size = 2 # JSON files [*.json] indent_size = 2 +indent_style = space # Dotnet code style settings: [*.{cs,vb}] @@ -167,3 +168,71 @@ csharp_new_line_before_members_in_anonymous_types = true # Blocks are allowed csharp_prefer_braces = true:silent + +dotnet_diagnostic.RS0041.severity = suggestion + +# SA1130: Use lambda syntax +dotnet_diagnostic.SA1130.severity = silent + +# IDE1006: Naming Styles - StyleCop handles these for us +dotnet_diagnostic.IDE1006.severity = none + +dotnet_diagnostic.DOC100.severity = silent +dotnet_diagnostic.DOC104.severity = warning +dotnet_diagnostic.DOC105.severity = warning +dotnet_diagnostic.DOC106.severity = warning +dotnet_diagnostic.DOC107.severity = warning +dotnet_diagnostic.DOC108.severity = warning +dotnet_diagnostic.DOC200.severity = warning +dotnet_diagnostic.DOC202.severity = warning + +# CS1591: Missing XML comment for publicly visible type or member +dotnet_diagnostic.CS1591.severity = suggestion + +# SA1101: Prefix local calls with this +dotnet_diagnostic.SA1101.severity = suggestion + +# SA120x: Element ordering +dotnet_diagnostic.SA1201.severity = silent +dotnet_diagnostic.SA1202.severity = silent +dotnet_diagnostic.SA1204.severity = silent +dotnet_diagnostic.SA1214.severity = silent + +# SA1121: Use built-in type alias +dotnet_diagnostic.SA1121.severity = suggestion + +# SA1124: Do not use regions +dotnet_diagnostic.SA1124.severity = suggestion + +# SA1133: Each attribute should be placed in its own set of square brackets +dotnet_diagnostic.SA1133.severity = silent + +# SA1134: Each attribute should be placed on its own line of code +dotnet_diagnostic.SA1134.severity = silent + +# SA1139: Use literal syntax instead of casting +dotnet_diagnostic.SA1139.severity = suggestion + +# SA1600: Elements should be documented +dotnet_diagnostic.SA1600.severity = silent + +# SA1601: Partial elements should be documented +dotnet_diagnostic.SA1601.severity = silent + +# SA1602: Enumeration items should be documented +dotnet_diagnostic.SA1602.severity = silent + +# SA1601: Element documentation should have summary +dotnet_diagnostic.SA1604.severity = suggestion + +# SA1615: The documentation for parameter is missing +dotnet_diagnostic.SA1611.severity = suggestion + +# SA1615: Element return value should be documented +dotnet_diagnostic.SA1615.severity = suggestion + +# SA1618: Document type parameters +dotnet_diagnostic.SA1618.severity = suggestion + +[*.sln] +indent_style = tab diff --git a/.gitattributes b/.gitattributes index f84f11f2a..1f35e683d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,7 +2,13 @@ # Set default behavior to automatically normalize line endings. ############################################################################### * text=auto -*.sh text eol=lf + +# Ensure shell scripts use LF line endings (linux only accepts LF) +*.sh eol=lf +*.ps1 eol=lf + +# The macOS codesign tool is extremely picky, and requires LF line endings. +*.plist eol=lf ############################################################################### # Set default behavior for command prompt diff. @@ -18,7 +24,7 @@ # # Merging from the command prompt will add diff markers to the files if there # are conflicts (Merging from VS is not affected by the settings below, in VS -# the diff markers are never inserted). Diff markers may cause the following +# the diff markers are never inserted). Diff markers may cause the following # file extensions to fail to load in VS. An alternative would be to treat # these files as binary and thus will always conflict and require user # intervention with every merge. To do so, just uncomment the entries below @@ -47,9 +53,9 @@ ############################################################################### # diff behavior for common document formats -# +# # Convert binary document formats to text before diffing them. This feature -# is only available from the command line. Turn it on by uncommenting the +# is only available from the command line. Turn it on by uncommenting the # entries below. ############################################################################### #*.doc diff=astextplain @@ -62,4 +68,3 @@ #*.PDF diff=astextplain #*.rtf diff=astextplain #*.RTF diff=astextplain - diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..74a8d180b --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,5 @@ +# These are supported funding model platforms + +github: +- neuecc +- aarnott diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..7150f8e07 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +# Please see the documentation for all configuration options: +# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: +- package-ecosystem: nuget + target-branch: develop + directory: / + schedule: + interval: monthly + ignore: + - dependency-name: MessagePack diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index e625d1164..27e7f9cf0 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -3,6 +3,7 @@ name: Mark stale issues and pull requests on: schedule: - cron: "0 0 * * *" + workflow_dispatch: jobs: stale: @@ -10,14 +11,16 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/stale@v1 + - uses: actions/stale@v5.2.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} days-before-stale: 90 days-before-close: 5 stale-issue-message: 'This issue is stale because it has been open 90 days with no activity. Remove stale label or comment or this will be closed in 5 days.' stale-issue-label: 'no-issue-activity' - exempt-issue-label: 'awaiting-approval' + exempt-issue-labels: awaiting-approval stale-pr-message: 'This pull request is stale because it has been open 90 days with no activity. Remove stale label or comment or this will be closed in 5 days.' stale-pr-label: 'no-pr-activity' - exempt-pr-label: 'awaiting-approval' + exempt-pr-labels: awaiting-approval + close-issue-reason: not_planned + diff --git a/.github/workflows/unity.yml b/.github/workflows/unity.yml new file mode 100644 index 000000000..2bf7bcba2 --- /dev/null +++ b/.github/workflows/unity.yml @@ -0,0 +1,48 @@ +name: unity + +on: + workflow_dispatch: + push: + branches: + - master + - develop + pull_request: + branches: + - master + - develop + +jobs: + unity: + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 # avoid shallow clone so nbgv can do its work. + - uses: actions/setup-dotnet@v3 + with: + global-json-file: global.json + - name: copy assets + run: src/MessagePack.UnityClient/copy_assets.sh + - uses: actions/cache@v3 + with: + path: src/MessagePack.UnityClient/Library + key: MessagePack-ubuntu + - name: build + uses: game-ci/unity-builder@v2 + env: + UNITY_LICENSE: ${{ secrets.UNITY_LICENSE_2021 }} + with: + projectPath: src/MessagePack.UnityClient + unityVersion: 2021.3.11f1 + targetPlatform: StandaloneLinux64 + buildMethod: PackageExporter.Export + customParameters: /headless /ScriptBackend mono + versioning: None + - uses: Cysharp/Actions/.github/actions/check-metas@main # check meta files + with: + directory: src/MessagePack.UnityClient + - uses: actions/upload-artifact@v3 + with: + name: MessagePack.unitypackage + path: bin/*.unitypackage diff --git a/.gitignore b/.gitignore index e2bdc90ad..7209c6cbc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,16 +1,23 @@ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore # User-specific files +*.rsuser *.suo *.user *.userosscache *.sln.docstates +*.lutconfig launchSettings.json # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs +# Mono auto generated files +mono_crash.* + # Build results [Dd]ebug/ [Dd]ebugPublic/ @@ -18,46 +25,66 @@ launchSettings.json [Rr]eleases/ x64/ x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ -# Visual Studio 2015 cache/options directory +# Visual Studio 2015/2017 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ +# Jetbrains Rider cache directory +.idea/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* -# NUNIT +# NUnit *.VisualState.xml TestResult.xml +nunit-*.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio *_i.c *_p.c -*_i.h +*_h.h *.ilk *.obj +*.iobj *.pch *.pdb +*.ipdb *.pgc *.pgd +*.rsp +!Directory.Build.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj +*_wpftmp.csproj *.log -*.binlog *.vspscc *.vssscc .builds @@ -85,6 +112,9 @@ ipch/ *.vspx *.sap +# Visual Studio Trace Files +*.e2e + # TFS 2012 Local Workspace $tf/ @@ -105,6 +135,15 @@ _TeamCity* # DotCover is a Code Coverage Tool *.dotCover +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml +/coveragereport/ + # NCrunch _NCrunch_* .*crunch*.local.xml @@ -136,9 +175,9 @@ publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml -# TODO: Comment the next line if you want to checkin your web deploy settings +# Note: Comment the next line if you want to checkin your web deploy settings, # but database connection strings (with potential passwords) will be unencrypted -#*.pubxml +*.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to @@ -148,13 +187,15 @@ PublishScripts/ # NuGet Packages *.nupkg +# NuGet Symbol Packages +*.snupkg # The packages folder can be ignored because of Package Restore -**/packages/* +**/[Pp]ackages/* # except build/, which is used as an MSBuild target. -!**/packages/build/ +!**/[Pp]ackages/build/ # Uncomment if necessary however generally it will be regenerated when needed -#!**/packages/repositories.config -# NuGet v3's project.json files produces more ignoreable files +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files *.nuget.props *.nuget.targets @@ -171,12 +212,15 @@ AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt +*.appx +*.appxbundle +*.appxupload # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache -!*.[Cc]ache/ +!?*.[Cc]ache/ # Others ClientBin/ @@ -187,9 +231,12 @@ ClientBin/ *.jfm *.pfx *.publishsettings -node_modules/ orleans.codegen.cs +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ @@ -204,15 +251,22 @@ _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak # SQL Server files *.mdf *.ldf +*.ndf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl # Microsoft Fakes FakesAssemblies/ @@ -222,6 +276,7 @@ FakesAssemblies/ # Node.js Tools for Visual Studio .ntvs_analysis.dat +node_modules/ # Visual Studio 6 build log *.plg @@ -229,6 +284,9 @@ FakesAssemblies/ # Visual Studio 6 workspace options file *.opt +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts @@ -244,40 +302,74 @@ paket-files/ # FAKE - F# Make .fake/ -# JetBrains Rider -.idea/ -*.sln.iml - -# CodeRush -.cr/ +# CodeRush personal settings +.cr/personal # Python Tools for Visual Studio (PTVS) __pycache__/ *.pyc +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# dotnet tool local install directory +.store/ + +# mac-created file to track user view preferences for a directory +.DS_Store + +# Analysis results +*.sarif + # Unity src/MessagePack.UnityClient/bin/* src/MessagePack.UnityClient/Library/* src/MessagePack.UnityClient/obj/* src/MessagePack.UnityClient/Temp/* - -# Project Specified - -nuget/mpc.exe -nuget/mpc.exe.config - -nuget/tools/* -nuget/unity/tools/* -nuget/unity* -/nuget/*.unitypackage - -# VSCode -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json +src/MessagePack.UnityClient/UserSettings/* +src/MessagePack.UnityClient/Assets/Packages/ # BenchmarkDotNet results BenchmarkDotNet.Artifacts/ + +src/MessagePack.UnityClient/.vsconfig + +*.lscache diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 000000000..ca3a2aa9d --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,19 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. + // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp + // List of extensions which should be recommended for users of this workspace. + "recommendations": [ + "ms-azure-devops.azure-pipelines", + "ms-dotnettools.csharp", + "k--kato.docomment", + "editorconfig.editorconfig", + "pflannery.vscode-versionlens", + "davidanson.vscode-markdownlint", + "dotjoshjohnson.xml", + "ms-vscode-remote.remote-containers", + "ms-azuretools.vscode-docker", + "tintoy.msbuild-project-tools" + ], + // List of extensions recommended by VS Code that should not be recommended for users of this workspace. + "unwantedRecommendations": [] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..af4bc6a30 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,21 @@ +{ + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md + "version": "0.2.0", + "configurations": [ + { + "name": "PowerShell Launch Current File", + "type": "PowerShell", + "request": "launch", + "script": "${file}", + "cwd": "${cwd}" + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach", + "processId": "${command:pickProcess}" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..3ae1371c6 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "files.trimTrailingWhitespace": true, + "files.insertFinalNewline": true, + "files.trimFinalNewlines": true, + "omnisharp.enableEditorConfigSupport": true, + "omnisharp.enableImportCompletion": true, + "omnisharp.enableRoslynAnalyzers": true +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 000000000..67b06180f --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,17 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + } + ] +} diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 239759454..016f0566e 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -1,2 +1,2 @@ Yoshifumi Kawai -Andrew Arnott +Andrew Arnott diff --git a/Directory.Build.props b/Directory.Build.props index fc7c5f0f7..5c788b006 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,43 +1,70 @@ Debug - $(MSBuildThisFileDirectory)obj\$(MSBuildProjectName)\ - $(MSBuildThisFileDirectory)bin\$(MSBuildProjectName)\ - $(MSBuildThisFileDirectory)bin\Packages\$(Configuration)\NuGet\ - + $(MSBuildThisFileDirectory) + $(RepoRootPath)obj\$([MSBuild]::MakeRelative($(RepoRootPath), $(MSBuildProjectDirectory)))\ + $(RepoRootPath)bin\$(MSBuildProjectName)\ + $(RepoRootPath)bin\Packages\$(Configuration)\ + 10 + latest + true true - 7.3 - $(NoWarn);CS1591 - $(MSBuildThisFileDirectory)MessagePack.ruleset + true - false - false - MIT + + true + + + + false + + + $(MSBuildThisFileDirectory) + + + embedded + + https://github.com/MessagePack-CSharp/MessagePack-CSharp neuecc,aarnott - neuecc,aarnott © Yoshifumi Kawai and contributors. All rights reserved. - https://github.com/neuecc/MessagePack-CSharp - + MIT true true - true + true snupkg + - - - - - - - - - + + + + + + - https://github.com/neuecc/MessagePack-CSharp/releases/tag/v$(Version) + $(PackageProjectUrl)/releases/tag/v$(Version) + + + false + true + + + + + false + false + false + false + diff --git a/Directory.Build.targets b/Directory.Build.targets new file mode 100644 index 000000000..ea7b6e6f8 --- /dev/null +++ b/Directory.Build.targets @@ -0,0 +1,11 @@ + + + + false + + + + + + + diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 000000000..ecd5e9ebc --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,75 @@ + + + + true + true + 0.13.5 + 3.11.0 + 4.4.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/LICENSE b/LICENSE index 8519f42f4..eebeb87e0 100644 --- a/LICENSE +++ b/LICENSE @@ -26,6 +26,8 @@ SOFTWARE. lz4net +BSD 2-Clause "Simplified" License + Copyright (c) 2013-2017, Milosz Krajewski All rights reserved. diff --git a/MessagePack.ruleset b/MessagePack.ruleset deleted file mode 100644 index 48d2d2be7..000000000 --- a/MessagePack.ruleset +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/MessagePack.sln b/MessagePack.sln index 77d762b8a..2b743e8e3 100644 --- a/MessagePack.sln +++ b/MessagePack.sln @@ -1,9 +1,13 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29108.181 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.33201.384 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{86309CF6-0054-4CE3-BFD3-CA0AA7DB17BC}" + ProjectSection(SolutionItems) = preProject + src\Directory.Build.props = src\Directory.Build.props + src\Directory.Build.targets = src\Directory.Build.targets + EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagePack", "src\MessagePack\MessagePack.csproj", "{7ABB33EE-A2F1-492B-8DAF-5DF89F0F0B79}" EndProject @@ -44,9 +48,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestData2", "sandbox\TestDa EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{301F812B-8AEE-4DC2-8009-4510F02294AD}" ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig azure-pipelines.yml = azure-pipelines.yml azure-pipelines\build.yml = azure-pipelines\build.yml Directory.Build.props = Directory.Build.props + Directory.Packages.props = Directory.Packages.props global.json = global.json nuget.config = nuget.config stylecop.json = stylecop.json diff --git a/README.md b/README.md index 27af11f5b..b15a0df32 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# MessagePack for C# (.NET, .NET Core, Unity, Xamarin) +# MessagePack for C# (.NET Framework, .NET 6, Unity, Xamarin) [![NuGet](https://img.shields.io/nuget/v/MessagePack.svg)](https://www.nuget.org/packages/messagepack) [![NuGet](https://img.shields.io/nuget/vpre/MessagePack.svg)](https://www.nuget.org/packages/messagepack) -[![Releases](https://img.shields.io/github/release/neuecc/MessagePack-CSharp.svg)][Releases] +[![Releases](https://img.shields.io/github/release/neuecc/MessagePack-CSharp.svg)](https://github.com/MessagePack-CSharp/MessagePack-CSharp/releases) [![Join the chat at https://gitter.im/MessagePack-CSharp/Lobby](https://badges.gitter.im/MessagePack-CSharp/Lobby.svg)](https://gitter.im/MessagePack-CSharp/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status](https://dev.azure.com/ils0086/MessagePack-CSharp/_apis/build/status/MessagePack-CSharp-CI?branchName=master)](https://dev.azure.com/ils0086/MessagePack-CSharp/_build/latest?definitionId=2&branchName=master) @@ -63,12 +63,13 @@ MessagePack has a compact binary size and a full set of general purpose expressi - [StreamJsonRpc](#streamjsonrpc) - [How to build](#how-to-build) - [Author Info](#author-info) +- [Code of Conduct & .NET Foundation notice](#coc) ## Installation This library is distributed via NuGet. Special [Unity support](#unity) is available, too. -We target .NET Standard 2.0 with special optimizations for .NET Core 2.1+, making it compatible with most reasonably recent .NET runtimes such as Core 2.0 and later, Framework 4.6.1 and later, Mono 5.4 and later and Unity 2018.3 and later. +We target .NET Standard 2.0 with special optimizations for .NET 6+, making it compatible with most reasonably recent .NET runtimes such as Core 2.0 and later, Framework 4.6.1 and later, Mono 5.4 and later and Unity 2018.3 and later. The library code is pure C# (with Just-In-Time IL code generation on some platforms). ### NuGet packages @@ -95,7 +96,7 @@ Install-Package MessagePack.AspNetCoreMvcFormatter ### Unity -For Unity projects, the [releases][Releases] page provides downloadable `.unitypackage` files. When using in Unity IL2CPP or Xamarin AOT environments, please carefully read the [pre-code generation section](#aot). +For Unity projects, the [Releases](https://github.com/MessagePack-CSharp/MessagePack-CSharp/releases) page provides downloadable `.unitypackage` files. When using in Unity IL2CPP or Xamarin AOT environments, please carefully read the [pre-code generation section](#aot). ### Migration notes from v1.x @@ -1531,7 +1532,7 @@ Within the *reserved* ranges, this library defines or implements extensions that Unity lowest supported version is `2018.3`, API Compatibility Level supports both `.NET 4.x` and `.NET Standard 2.0`. -You can install the `unitypackage` from the [releases][Releases] page. +You can install the `unitypackage` from the [Releases](https://github.com/MessagePack-CSharp/MessagePack-CSharp/releases) page. If your build targets .NET Framework 4.x and runs on mono, you can use it as is. But if your build targets IL2CPP, you can not use `Dynamic***Resolver`, so it is required to use pre-code generation. Please see [pre-code generation section](#aot). @@ -1566,7 +1567,7 @@ The `MessagePack.UnityShims` NuGet package is for .NET server-side serialization If you want to share a class between Unity and a server, you can use `SharedProject` or `Reference as Link` or a glob reference (with `LinkBase`), etc. Anyway, you need to share at source-code level. This is a sample project structure using a glob reference (recommended). -- ServerProject(.NET 4.6/.NET Core/.NET Standard) +- ServerProject(.NET Framework 4.6/.NET/.NET Standard) - \[``\] - \[MessagePack\] - \[MessagePack.UnityShims\] @@ -1584,7 +1585,7 @@ For all other Unity targets, AOT is required. If you want to avoid the upfront dynamic generation cost or you need to run on Xamarin or Unity, you need AOT code generation. `mpc` (MessagePackCompiler) is the code generator of MessagePack for C#. mpc uses [Roslyn](https://github.com/dotnet/roslyn) to analyze source code. -First of all, mpc requires [.NET Core 3 Runtime](https://dotnet.microsoft.com/download). The easiest way to acquire and run mpc is as a dotnet tool. +First of all, mpc requires [.NET 6+ Runtime](https://dotnet.microsoft.com/download). The easiest way to acquire and run mpc is as a dotnet tool. ``` dotnet tool install --global MessagePack.Generator @@ -1603,11 +1604,7 @@ Once you have the tool installed, simply invoke using `dotnet mpc` within your r ``` dotnet mpc --help -``` - -Alternatively, you can download mpc from the [releases][Releases] page, that includes platform native binaries (that don't require a separate dotnet runtime). -``` Usage: mpc [options...] Options: @@ -1677,7 +1674,7 @@ In Unity, you can use MessagePack CodeGen windows at `Windows -> MessagePack -> ![](https://user-images.githubusercontent.com/46207/69414381-f14da400-0d55-11ea-9f8d-9af448d347dc.png) -Install the .NET Core runtime, install mpc (as a .NET Core Tool as described above), and execute `dotnet mpc`. Currently this tool is experimental so please tell me your opinion. +Install the .NET runtime, install mpc (as a .NET Tool as described above), and execute `dotnet mpc`. Currently this tool is experimental so please tell me your opinion. In Xamarin, you can install the [the `MessagePack.MSBuild.Tasks` NuGet package](doc/msbuildtask.md) into your projects to pre-compile fast serialization code and run in environments where JIT compilation is not allowed. @@ -1697,16 +1694,11 @@ The StreamJsonRpc library is based on [JSON-RPC](https://www.jsonrpc.org/) and i See our [contributor's guide](CONTRIBUTING.md). -## Author Info +## Code of Conduct -Yoshifumi Kawai (a.k.a. neuecc) is a software developer in Japan. -He is the Director/CTO at Grani, Inc. -Grani is a mobile game developer company in Japan and well known for using C#. -He is awarding Microsoft MVP for Visual C# since 2011. -He is known as the creator of [UniRx](https://github.com/neuecc/UniRx/) (Reactive Extensions for Unity) +This project has adopted the code of conduct defined by the Contributor Covenant to clarify expected behavior in our community. +For more information see the [.NET Foundation Code of Conduct](https://dotnetfoundation.org/code-of-conduct). -* Blog: [https://medium.com/@neuecc](https://medium.com/@neuecc) (English) -* Blog: [http://neue.cc/](http://neue.cc/) (Japanese) -* Twitter: [https://twitter.com/neuecc](https://twitter.com/neuecc) (Japanese) +## .NET Foundation -[Releases]: https://github.com/neuecc/MessagePack-CSharp/releases +This project is supported by the [.NET Foundation](https://dotnetfoundation.org). diff --git a/azure-pipelines.yml b/azure-pipelines.yml index d18997c3b..98cea23dd 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,4 +1,5 @@ trigger: + batch: true branches: include: - master @@ -6,96 +7,32 @@ trigger: - 'v?.*' - 'validate/*' paths: - exclude: ["doc", "*.md"] + exclude: + - doc/ + - '*.md' + - .vscode/ + - .github/ + - azure-pipelines/release.yml + +parameters: +- name: includeMacOS + displayName: Build on macOS + type: boolean + default: false # macOS is often bogged down in Azure Pipelines +- name: RunTests + displayName: Run tests + type: boolean + default: true variables: - TreatWarningsAsErrors: true + MSBuildTreatWarningsAsErrors: true DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true BuildConfiguration: Release - BuildPlatform: Any CPU - -stages: -- stage: Build - jobs: - - job: Windows - pool: - vmImage: windows-2022 - steps: - - checkout: self - clean: true - - template: azure-pipelines/install-dependencies.yml - - - powershell: '& (./azure-pipelines/Get-nbgv.ps1) cloud' - displayName: Set build number - - - template: azure-pipelines/build.yml - - - job: Unity - pool: - name: CustomAgents - demands: - - UNITYHUB_EDITORS_FOLDER_LOCATION - - UNITYVERSION -equals 2019.1 - steps: - - checkout: self - clean: true - - template: azure-pipelines/install-dependencies.yml - - template: azure-pipelines/build_unity.yml - - - job: Linux - pool: - vmImage: ubuntu-20.04 - steps: - - checkout: self - clean: true - - template: azure-pipelines/install-dependencies.yml - - template: azure-pipelines/build_nonWindows.yml - - - job: macOS - pool: - vmImage: macOS-11 - steps: - - checkout: self - clean: true - - template: azure-pipelines/install-dependencies.yml - - template: azure-pipelines/build_nonWindows.yml - -# This job ensures that we're running mpc regularly on the generated code that we check in. -# It also helps exercise mpc so bugs don't go unnoticed. - - job: codegen_diff - pool: - vmImage: ubuntu-20.04 - steps: - - checkout: self - clean: true - - template: azure-pipelines/install-dependencies.yml - - pwsh: sandbox/codegen.ps1 - displayName: Regenerate checked-in code - - bash: | - git add -u . # compare after applying git EOL normalization - git diff --cached --exit-code --stat \ - || (echo "##[error] found changed files after build. please 'azure-pipelines/codegen.ps1'" \ - "and check in all changes" \ - && git diff --cached \ - && exit 1) - displayName: Check for uncommitted changes -- stage: Publish - condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) - jobs: - - job: push - pool: - vmImage: ubuntu-20.04 - steps: - - download: current - artifact: nuget - - task: NuGetToolInstaller@1 - displayName: Use NuGet 5.x - inputs: - versionSpec: 5.x - - task: NuGetCommand@2 - displayName: NuGet push - inputs: - command: push - packagesToPush: $(Pipeline.Workspace)/nuget/*.nupkg - publishVstsFeed: MessagePack-CSharp/MessagePack-CI - continueOnError: true + ci_feed: https://pkgs.dev.azure.com/ils0086/MessagePack-CSharp/_packaging/MessagePack-CI/nuget/v3/index.json + NUGET_PACKAGES: $(Agent.TempDirectory)/.nuget/packages/ + +jobs: +- template: azure-pipelines/build.yml + parameters: + includeMacOS: ${{ parameters.includeMacOS }} + RunTests: ${{ parameters.RunTests }} diff --git a/azure-pipelines/Darwin.runsettings b/azure-pipelines/Darwin.runsettings deleted file mode 100644 index b444e8196..000000000 --- a/azure-pipelines/Darwin.runsettings +++ /dev/null @@ -1 +0,0 @@ - diff --git a/azure-pipelines/Get-ArtifactsStagingDirectory.ps1 b/azure-pipelines/Get-ArtifactsStagingDirectory.ps1 new file mode 100644 index 000000000..391e5713a --- /dev/null +++ b/azure-pipelines/Get-ArtifactsStagingDirectory.ps1 @@ -0,0 +1,15 @@ +Param( + [switch]$CleanIfLocal +) +if ($env:BUILD_ARTIFACTSTAGINGDIRECTORY) { + $ArtifactStagingFolder = $env:BUILD_ARTIFACTSTAGINGDIRECTORY +} elseif ($env:RUNNER_TEMP) { + $ArtifactStagingFolder = "$env:RUNNER_TEMP\_artifacts" +} else { + $ArtifactStagingFolder = [System.IO.Path]::GetFullPath("$PSScriptRoot/../obj/_artifacts") + if ($CleanIfLocal -and (Test-Path $ArtifactStagingFolder)) { + Remove-Item $ArtifactStagingFolder -Recurse -Force + } +} + +$ArtifactStagingFolder diff --git a/azure-pipelines/Get-CodeCovTool.ps1 b/azure-pipelines/Get-CodeCovTool.ps1 new file mode 100644 index 000000000..ca580b4db --- /dev/null +++ b/azure-pipelines/Get-CodeCovTool.ps1 @@ -0,0 +1,86 @@ +<# +.SYNOPSIS + Downloads the CodeCov.io uploader tool and returns the path to it. +.PARAMETER AllowSkipVerify + Allows skipping signature verification of the downloaded tool if gpg is not installed. +#> +[CmdletBinding()] +Param( + [switch]$AllowSkipVerify +) + +if ($IsMacOS) { + $codeCovUrl = "https://uploader.codecov.io/latest/macos/codecov" + $toolName = 'codecov' +} +elseif ($IsLinux) { + $codeCovUrl = "https://uploader.codecov.io/latest/linux/codecov" + $toolName = 'codecov' +} +else { + $codeCovUrl = "https://uploader.codecov.io/latest/windows/codecov.exe" + $toolName = 'codecov.exe' +} + +$shaSuffix = ".SHA256SUM" +$sigSuffix = $shaSuffix + ".sig" + +Function Get-FileFromWeb([Uri]$Uri, $OutDir) { + $OutFile = Join-Path $OutDir $Uri.Segments[-1] + if (!(Test-Path $OutFile)) { + Write-Verbose "Downloading $Uri..." + if (!(Test-Path $OutDir)) { New-Item -ItemType Directory -Path $OutDir | Out-Null } + try { + (New-Object System.Net.WebClient).DownloadFile($Uri, $OutFile) + } finally { + # This try/finally causes the script to abort + } + } + + $OutFile +} + +$toolsPath = & "$PSScriptRoot\Get-TempToolsPath.ps1" +$binaryToolsPath = Join-Path $toolsPath codecov +$testingPath = Join-Path $binaryToolsPath unverified +$finalToolPath = Join-Path $binaryToolsPath $toolName + +if (!(Test-Path $finalToolPath)) { + if (Test-Path $testingPath) { + Remove-Item -Recurse -Force $testingPath # ensure we download all matching files + } + $tool = Get-FileFromWeb $codeCovUrl $testingPath + $sha = Get-FileFromWeb "$codeCovUrl$shaSuffix" $testingPath + $sig = Get-FileFromWeb "$codeCovUrl$sigSuffix" $testingPath + $key = Get-FileFromWeb https://keybase.io/codecovsecurity/pgp_keys.asc $testingPath + + if ((Get-Command gpg -ErrorAction SilentlyContinue)) { + Write-Host "Importing codecov key" -ForegroundColor Yellow + gpg --import $key + Write-Host "Verifying signature on codecov hash" -ForegroundColor Yellow + gpg --verify $sig $sha + } else { + if ($AllowSkipVerify) { + Write-Warning "gpg not found. Unable to verify hash signature." + } else { + throw "gpg not found. Unable to verify hash signature. Install gpg or add -AllowSkipVerify to override." + } + } + + Write-Host "Verifying hash on downloaded tool" -ForegroundColor Yellow + $actualHash = (Get-FileHash -Path $tool -Algorithm SHA256).Hash + $expectedHash = (Get-Content $sha).Split()[0] + if ($actualHash -ne $expectedHash) { + # Validation failed. Delete the tool so we can't execute it. + #Remove-Item $codeCovPath + throw "codecov uploader tool failed signature validation." + } + + Copy-Item $tool $finalToolPath + + if ($IsMacOS -or $IsLinux) { + chmod u+x $finalToolPath + } +} + +return $finalToolPath diff --git a/azure-pipelines/Get-NuGetTool.ps1 b/azure-pipelines/Get-NuGetTool.ps1 index 4431adb91..3097c8736 100644 --- a/azure-pipelines/Get-NuGetTool.ps1 +++ b/azure-pipelines/Get-NuGetTool.ps1 @@ -6,7 +6,7 @@ #> Param( [Parameter()] - [string]$NuGetVersion='5.2.0' + [string]$NuGetVersion='6.4.0' ) $toolsPath = & "$PSScriptRoot\Get-TempToolsPath.ps1" diff --git a/azure-pipelines/Get-SymbolFiles.ps1 b/azure-pipelines/Get-SymbolFiles.ps1 new file mode 100644 index 000000000..0ce229fc2 --- /dev/null +++ b/azure-pipelines/Get-SymbolFiles.ps1 @@ -0,0 +1,61 @@ +<# +.SYNOPSIS + Collect the list of PDBs built in this repo. +.PARAMETER Path + The directory to recursively search for PDBs. +.PARAMETER Tests + A switch indicating to find PDBs only for test binaries instead of only for shipping shipping binaries. +#> +[CmdletBinding()] +param ( + [parameter(Mandatory=$true)] + [string]$Path, + [switch]$Tests +) + +$ActivityName = "Collecting symbols from $Path" +Write-Progress -Activity $ActivityName -CurrentOperation "Discovery PDB files" +$PDBs = Get-ChildItem -rec "$Path/*.pdb" + +# Filter PDBs to product OR test related. +$testregex = "unittest|tests|\.test\." + +Write-Progress -Activity $ActivityName -CurrentOperation "De-duplicating symbols" +$PDBsByHash = @{} +$i = 0 +$PDBs |% { + Write-Progress -Activity $ActivityName -CurrentOperation "De-duplicating symbols" -PercentComplete (100 * $i / $PDBs.Length) + $hash = Get-FileHash $_ + $i++ + Add-Member -InputObject $_ -MemberType NoteProperty -Name Hash -Value $hash.Hash + Write-Output $_ +} | Sort-Object CreationTime |% { + # De-dupe based on hash. Prefer the first match so we take the first built copy. + if (-not $PDBsByHash.ContainsKey($_.Hash)) { + $PDBsByHash.Add($_.Hash, $_.FullName) + Write-Output $_ + } +} |? { + if ($Tests) { + $_.FullName -match $testregex + } else { + $_.FullName -notmatch $testregex + } +} |% { + # Collect the DLLs/EXEs as well. + $dllPath = "$($_.Directory)/$($_.BaseName).dll" + $exePath = "$($_.Directory)/$($_.BaseName).exe" + if (Test-Path $dllPath) { + $BinaryImagePath = $dllPath + } elseif (Test-Path $exePath) { + $BinaryImagePath = $exePath + } else { + Write-Warning "`"$_`" found with no matching binary file." + $BinaryImagePath = $null + } + + if ($BinaryImagePath) { + Write-Output $BinaryImagePath + Write-Output $_.FullName + } +} diff --git a/azure-pipelines/Linux.runsettings b/azure-pipelines/Linux.runsettings deleted file mode 100644 index b444e8196..000000000 --- a/azure-pipelines/Linux.runsettings +++ /dev/null @@ -1 +0,0 @@ - diff --git a/azure-pipelines/Merge-CodeCoverage.ps1 b/azure-pipelines/Merge-CodeCoverage.ps1 new file mode 100644 index 000000000..02ff12b00 --- /dev/null +++ b/azure-pipelines/Merge-CodeCoverage.ps1 @@ -0,0 +1,51 @@ +#!/usr/bin/env pwsh + +<# +.SYNOPSIS + Merges code coverage reports. +.PARAMETER Path + The path(s) to search for Cobertura code coverage reports. +.PARAMETER Format + The format for the merged result. The default is Cobertura +.PARAMETER OutputDir + The directory the merged result will be written to. The default is `coveragereport` in the root of this repo. +#> +[CmdletBinding()] +Param( + [Parameter(Mandatory=$true)] + [string[]]$Path, + [ValidateSet('Badges', 'Clover', 'Cobertura', 'CsvSummary', 'Html', 'Html_Dark', 'Html_Light', 'HtmlChart', 'HtmlInline', 'HtmlInline_AzurePipelines', 'HtmlInline_AzurePipelines_Dark', 'HtmlInline_AzurePipelines_Light', 'HtmlSummary', 'JsonSummary', 'Latex', 'LatexSummary', 'lcov', 'MarkdownSummary', 'MHtml', 'PngChart', 'SonarQube', 'TeamCitySummary', 'TextSummary', 'Xml', 'XmlSummary')] + [string]$Format='Cobertura', + [string]$OutputFile=("$PSScriptRoot/../coveragereport/merged.cobertura.xml") +) + +$RepoRoot = [string](Resolve-Path $PSScriptRoot/..) + +if (!(Test-Path $RepoRoot/obj/dotnet-coverage*)) { + dotnet tool install --tool-path $RepoRoot/obj dotnet-coverage --version 17.4.3 --configfile $PSScriptRoot/justnugetorg.nuget.config +} + +Write-Verbose "Searching $Path for *.cobertura.xml files" +$reports = Get-ChildItem -Recurse $Path -Filter *.cobertura.xml + +if ($reports) { + $reports |% { $_.FullName } |% { + # In addition to replacing {reporoot}, we also normalize on one kind of slash so that the report aggregates data for a file whether data was collected on Windows or not. + $xml = [xml](Get-Content -Path $_) + $xml.coverage.packages.package.classes.class |? { $_.filename} |% { + $_.filename = $_.filename.Replace('{reporoot}', $RepoRoot).Replace([IO.Path]::AltDirectorySeparatorChar, [IO.Path]::DirectorySeparatorChar) + } + + $xml.Save($_) + } + + $Inputs = $reports |% { Resolve-Path -relative $_.FullName } + + if ((Split-Path $OutputFile) -and -not (Test-Path (Split-Path $OutputFile))) { + New-Item -Type Directory -Path (Split-Path $OutputFile) | Out-Null + } + + & "$RepoRoot/obj/dotnet-coverage" merge $Inputs -o $OutputFile -f cobertura +} else { + Write-Error "No reports found to merge." +} diff --git a/azure-pipelines/Windows_NT.runsettings b/azure-pipelines/Windows_NT.runsettings deleted file mode 100644 index 0b43ecb09..000000000 --- a/azure-pipelines/Windows_NT.runsettings +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - %BUILD_ARTIFACTSTAGINGDIRECTORY% - - - - - diff --git a/azure-pipelines/artifacts/Variables.ps1 b/azure-pipelines/artifacts/Variables.ps1 index cfcb7df52..4bc6d2165 100644 --- a/azure-pipelines/artifacts/Variables.ps1 +++ b/azure-pipelines/artifacts/Variables.ps1 @@ -2,18 +2,21 @@ # It "snaps" the values of these variables where we can compute them during the build, # and otherwise captures the scripts to run later during an Azure Pipelines environment release. -$RepoRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..") -$ArtifactBasePath = "$RepoRoot\obj\_artifacts" -$VariablesArtifactPath = "$ArtifactBasePath\variables" +$RepoRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot/../..") +$ArtifactBasePath = "$RepoRoot/obj/_artifacts" +$VariablesArtifactPath = Join-Path $ArtifactBasePath variables if (-not (Test-Path $VariablesArtifactPath)) { New-Item -ItemType Directory -Path $VariablesArtifactPath | Out-Null } # Copy variables, either by value if the value is calculable now, or by script -Get-ChildItem -Path "$PSScriptRoot\..\variables" |% { +Get-ChildItem "$PSScriptRoot/../variables" |% { $value = $null if (-not $_.BaseName.StartsWith('_')) { # Skip trying to interpret special scripts # First check the environment variables in case the variable was set in a queued build - if (Test-Path env:$($_.BaseName)) { - $value = Get-Content "env:$($_.BaseName)" + # Always use all caps for env var access because Azure Pipelines converts variables to upper-case for env vars, + # and on non-Windows env vars are case sensitive. + $envVarName = $_.BaseName.ToUpper() + if (Test-Path env:$envVarName) { + $value = Get-Content "env:$envVarName" } # If that didn't give us anything, try executing the script right now from its original position @@ -32,7 +35,7 @@ Get-ChildItem -Path "$PSScriptRoot\..\variables" |% { $value = Get-Content -Path $_.FullName } - Set-Content -Path "$VariablesArtifactPath\$($_.Name)" -Value $value + Set-Content -Path "$VariablesArtifactPath/$($_.Name)" -Value $value } @{ diff --git a/azure-pipelines/artifacts/_all.ps1 b/azure-pipelines/artifacts/_all.ps1 index efd6bdb3e..9a22a1d08 100755 --- a/azure-pipelines/artifacts/_all.ps1 +++ b/azure-pipelines/artifacts/_all.ps1 @@ -1,18 +1,25 @@ #!/usr/bin/env pwsh -# This script returns all the artifacts that should be collected after a build. -# -# Each powershell artifact is expressed as an object with these properties: -# Source - the full path to the source file -# ArtifactName - the name of the artifact to upload to -# ContainerFolder - the relative path within the artifact in which the file should appear -# -# Each artifact aggregating .ps1 script should return a hashtable: -# Key = path to the directory from which relative paths within the artifact should be calculated -# Value = an array of paths (absolute or relative to the BaseDirectory) to files to include in the artifact. -# FileInfo objects are also allowed. - -$RepoRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..") +<# +.SYNOPSIS + This script returns all the artifacts that should be collected after a build. + Each powershell artifact is expressed as an object with these properties: + Source - the full path to the source file + ArtifactName - the name of the artifact to upload to + ContainerFolder - the relative path within the artifact in which the file should appear + Each artifact aggregating .ps1 script should return a hashtable: + Key = path to the directory from which relative paths within the artifact should be calculated + Value = an array of paths (absolute or relative to the BaseDirectory) to files to include in the artifact. + FileInfo objects are also allowed. +.PARAMETER Force + Executes artifact scripts even if they have already been staged. +#> + +[CmdletBinding(SupportsShouldProcess = $true)] +param ( + [string]$ArtifactNameSuffix, + [switch]$Force +) Function EnsureTrailingSlash($path) { if ($path.length -gt 0 -and !$path.EndsWith('\') -and !$path.EndsWith('/')) { @@ -22,31 +29,44 @@ Function EnsureTrailingSlash($path) { $path.Replace('\', [IO.Path]::DirectorySeparatorChar) } -Get-ChildItem "$PSScriptRoot\*.ps1" -Exclude "_*" -Recurse |% { - $ArtifactName = $_.BaseName +Function Test-ArtifactStaged($artifactName) { + $varName = "ARTIFACTSTAGED_$($artifactName.ToUpper())" + Test-Path "env:$varName" +} - $fileGroups = & $_ - if (!$fileGroups -or $fileGroups.Count -eq 0) { - Write-Warning "No files found for the `"$ArtifactName`" artifact." - } else { - $fileGroups.GetEnumerator() | % { - $BaseDirectory = New-Object Uri ((EnsureTrailingSlash $_.Key.ToString()), [UriKind]::Absolute) - $_.Value | % { - if ($_.GetType() -eq [IO.FileInfo] -or $_.GetType() -eq [IO.DirectoryInfo]) { - $_ = $_.FullName - } +Get-ChildItem "$PSScriptRoot\*.ps1" -Exclude "_*" -Recurse | % { + $ArtifactName = $_.BaseName + if ($Force -or !(Test-ArtifactStaged($ArtifactName + $ArtifactNameSuffix))) { + $totalFileCount = 0 + Write-Verbose "Collecting file list for artifact $($_.BaseName)" + $fileGroups = & $_ + if ($fileGroups) { + $fileGroups.GetEnumerator() | % { + $BaseDirectory = New-Object Uri ((EnsureTrailingSlash $_.Key.ToString()), [UriKind]::Absolute) + $_.Value | ? { $_ } | % { + if ($_.GetType() -eq [IO.FileInfo] -or $_.GetType() -eq [IO.DirectoryInfo]) { + $_ = $_.FullName + } - $artifact = New-Object -TypeName PSObject - Add-Member -InputObject $artifact -MemberType NoteProperty -Name ArtifactName -Value $ArtifactName + $artifact = New-Object -TypeName PSObject + Add-Member -InputObject $artifact -MemberType NoteProperty -Name ArtifactName -Value $ArtifactName - $SourceFullPath = New-Object Uri ($BaseDirectory, $_) - Add-Member -InputObject $artifact -MemberType NoteProperty -Name Source -Value $SourceFullPath.LocalPath + $SourceFullPath = New-Object Uri ($BaseDirectory, $_) + Add-Member -InputObject $artifact -MemberType NoteProperty -Name Source -Value $SourceFullPath.LocalPath - $RelativePath = [Uri]::UnescapeDataString($BaseDirectory.MakeRelative($SourceFullPath)) - Add-Member -InputObject $artifact -MemberType NoteProperty -Name ContainerFolder -Value (Split-Path $RelativePath) + $RelativePath = [Uri]::UnescapeDataString($BaseDirectory.MakeRelative($SourceFullPath)) + Add-Member -InputObject $artifact -MemberType NoteProperty -Name ContainerFolder -Value (Split-Path $RelativePath) - Write-Output $artifact + Write-Output $artifact + $totalFileCount += 1 + } } } + + if ($totalFileCount -eq 0) { + Write-Warning "No files found for the `"$ArtifactName`" artifact." + } + } else { + Write-Host "Skipping $ArtifactName because it has already been staged." -ForegroundColor DarkGray } } diff --git a/azure-pipelines/artifacts/_pipelines.ps1 b/azure-pipelines/artifacts/_pipelines.ps1 index 5bca7c6c1..2d3338b24 100644 --- a/azure-pipelines/artifacts/_pipelines.ps1 +++ b/azure-pipelines/artifacts/_pipelines.ps1 @@ -1,10 +1,44 @@ -# This script translates all the artifacts described by _all.ps1 -# into commands that instruct Azure Pipelines to actually collect those artifacts. +<# +.SYNOPSIS + This script translates all the artifacts described by _all.ps1 + into commands that instruct Azure Pipelines to actually collect those artifacts. +#> +[CmdletBinding()] param ( - [string]$ArtifactNameSuffix + [string]$ArtifactNameSuffix, + [switch]$StageOnly ) +Function Set-PipelineVariable($name, $value) { + if ((Test-Path "Env:\$name") -and (Get-Item "Env:\$name").Value -eq $value) { + return # already set + } + + #New-Item -Path "Env:\$name".ToUpper() -Value $value -Force | Out-Null + Write-Host "##vso[task.setvariable variable=$name]$value" +} + +Function Test-ArtifactUploaded($artifactName) { + $varName = "ARTIFACTUPLOADED_$($artifactName.ToUpper())" + Test-Path "env:$varName" +} + & "$PSScriptRoot/_stage_all.ps1" -ArtifactNameSuffix $ArtifactNameSuffix |% { - Write-Host "##vso[artifact.upload containerfolder=$($_.Name);artifactname=$($_.Name);]$($_.Path)" + # Set a variable which will out-live this script so that a subsequent attempt to collect and upload artifacts + # will skip this one from a check in the _all.ps1 script. + Set-PipelineVariable "ARTIFACTSTAGED_$($_.Name.ToUpper())" 'true' + Write-Host "Staged artifact $($_.Name) to $($_.Path)" + + if (!$StageOnly) { + if (Test-ArtifactUploaded $_.Name) { + Write-Host "Skipping $($_.Name) because it has already been uploaded." -ForegroundColor DarkGray + } else { + Write-Host "##vso[artifact.upload containerfolder=$($_.Name);artifactname=$($_.Name);]$($_.Path)" + + # Set a variable which will out-live this script so that a subsequent attempt to collect and upload artifacts + # will skip this one from a check in the _all.ps1 script. + Set-PipelineVariable "ARTIFACTUPLOADED_$($_.Name.ToUpper())" 'true' + } + } } diff --git a/azure-pipelines/artifacts/_stage_all.ps1 b/azure-pipelines/artifacts/_stage_all.ps1 index 4e6a6dbe0..d81d16d46 100644 --- a/azure-pipelines/artifacts/_stage_all.ps1 +++ b/azure-pipelines/artifacts/_stage_all.ps1 @@ -1,20 +1,16 @@ -# This script links all the artifacts described by _all.ps1 -# into a staging directory, reading for uploading to a cloud build artifact store. -# It returns a sequence of objects with Name and Path properties. - +<# +.SYNOPSIS + This script links all the artifacts described by _all.ps1 + into a staging directory, reading for uploading to a cloud build artifact store. + It returns a sequence of objects with Name and Path properties. +#> + +[CmdletBinding()] param ( [string]$ArtifactNameSuffix ) -$RepoRoot = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot (Join-Path .. ..))) -if ($env:BUILD_ARTIFACTSTAGINGDIRECTORY) { - $ArtifactStagingFolder = $env:BUILD_ARTIFACTSTAGINGDIRECTORY -} else { - $ArtifactStagingFolder = Join-Path $RepoRoot (Join-Path obj _artifacts) - if (Test-Path $ArtifactStagingFolder) { - Remove-Item $ArtifactStagingFolder -Recurse -Force - } -} +$ArtifactStagingFolder = & "$PSScriptRoot/../Get-ArtifactsStagingDirectory.ps1" -CleanIfLocal function Create-SymbolicLink { param ( @@ -29,7 +25,6 @@ function Create-SymbolicLink { if (Test-Path $Link) { Remove-Item $Link } $LinkContainer = Split-Path $Link -Parent if (!(Test-Path $LinkContainer)) { mkdir $LinkContainer } - Write-Verbose "Linking $Link to $Target" if ($IsMacOS -or $IsLinux) { ln $Target $Link | Out-Null } else { @@ -38,9 +33,9 @@ function Create-SymbolicLink { } # Stage all artifacts -$Artifacts = & "$PSScriptRoot\_all.ps1" +$Artifacts = & "$PSScriptRoot\_all.ps1" -ArtifactNameSuffix $ArtifactNameSuffix $Artifacts |% { - $DestinationFolder = (Join-Path (Join-Path $ArtifactStagingFolder "$($_.ArtifactName)$ArtifactNameSuffix") $_.ContainerFolder).TrimEnd('\') + $DestinationFolder = [System.IO.Path]::GetFullPath("$ArtifactStagingFolder/$($_.ArtifactName)$ArtifactNameSuffix/$($_.ContainerFolder)").TrimEnd('\') $Name = "$(Split-Path $_.Source -Leaf)" #Write-Host "$($_.Source) -> $($_.ArtifactName)\$($_.ContainerFolder)" -ForegroundColor Yellow @@ -51,7 +46,13 @@ $Artifacts |% { } } -$Artifacts |% { "$($_.ArtifactName)$ArtifactNameSuffix" } | Get-Unique |% { +$ArtifactNames = $Artifacts |% { "$($_.ArtifactName)$ArtifactNameSuffix" } +$ArtifactNames += Get-ChildItem env:ARTIFACTSTAGED_* |% { + # Return from ALLCAPS to the actual capitalization used for the artifact. + $artifactNameAllCaps = "$($_.Name.Substring('ARTIFACTSTAGED_'.Length))" + (Get-ChildItem $ArtifactStagingFolder\$artifactNameAllCaps* -Filter $artifactNameAllCaps).Name +} +$ArtifactNames | Get-Unique |% { $artifact = New-Object -TypeName PSObject Add-Member -InputObject $artifact -MemberType NoteProperty -Name Name -Value $_ Add-Member -InputObject $artifact -MemberType NoteProperty -Name Path -Value (Join-Path $ArtifactStagingFolder $_) diff --git a/azure-pipelines/artifacts/build_logs.ps1 b/azure-pipelines/artifacts/build_logs.ps1 index b55ba48f3..f05358e03 100644 --- a/azure-pipelines/artifacts/build_logs.ps1 +++ b/azure-pipelines/artifacts/build_logs.ps1 @@ -1,12 +1,7 @@ -if ($env:BUILD_ARTIFACTSTAGINGDIRECTORY) { - $artifactsRoot = $env:BUILD_ARTIFACTSTAGINGDIRECTORY -} else { - $RepoRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..") - $artifactsRoot = "$RepoRoot\bin" -} +$ArtifactStagingFolder = & "$PSScriptRoot/../Get-ArtifactsStagingDirectory.ps1" -if (!(Test-Path $artifactsRoot/build_logs)) { return } +if (!(Test-Path $ArtifactStagingFolder/build_logs)) { return } @{ - "$artifactsRoot/build_logs" = (Get-ChildItem -Recurse "$artifactsRoot/build_logs") + "$ArtifactStagingFolder/build_logs" = (Get-ChildItem -Recurse "$ArtifactStagingFolder/build_logs") } diff --git a/azure-pipelines/artifacts/coverageResults.ps1 b/azure-pipelines/artifacts/coverageResults.ps1 new file mode 100644 index 000000000..d2fee5016 --- /dev/null +++ b/azure-pipelines/artifacts/coverageResults.ps1 @@ -0,0 +1,23 @@ +$RepoRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..") + +$coverageFiles = @(Get-ChildItem "$RepoRoot/tests/*.cobertura.xml" -Recurse | Where {$_.FullName -notlike "*/In/*" -and $_.FullName -notlike "*\In\*" }) + +# Prepare code coverage reports for merging on another machine +if ($env:SYSTEM_DEFAULTWORKINGDIRECTORY) { + Write-Host "Substituting $env:SYSTEM_DEFAULTWORKINGDIRECTORY with `"{reporoot}`"" + $coverageFiles |% { + $content = Get-Content -Path $_ |% { $_ -Replace [regex]::Escape($env:SYSTEM_DEFAULTWORKINGDIRECTORY), "{reporoot}" } + Set-Content -Path $_ -Value $content -Encoding UTF8 + } +} else { + Write-Warning "coverageResults: Azure Pipelines not detected. Machine-neutral token replacement skipped." +} + +if (!((Test-Path $RepoRoot\bin) -and (Test-Path $RepoRoot\obj))) { return } + +@{ + $RepoRoot = ( + $coverageFiles + + (Get-ChildItem "$RepoRoot\obj\*.cs" -Recurse) + ); +} diff --git a/azure-pipelines/artifacts/vsix.ps1 b/azure-pipelines/artifacts/deployables.ps1 similarity index 56% rename from azure-pipelines/artifacts/vsix.ps1 rename to azure-pipelines/artifacts/deployables.ps1 index 963404f91..94c48cdd9 100644 --- a/azure-pipelines/artifacts/vsix.ps1 +++ b/azure-pipelines/artifacts/deployables.ps1 @@ -4,11 +4,10 @@ if (!$BuildConfiguration) { $BuildConfiguration = 'Debug' } -$result = @{} +$PackagesRoot = "$RepoRoot/bin/Packages/$BuildConfiguration" -$VsixRoot = "$RepoRoot/bin/MessagePackAnalyzer.Vsix/$BuildConfiguration" -if (Test-Path $VsixRoot) { - $result[$VsixRoot] = (Get-ChildItem "$VsixRoot/*vsix") -} +if (!(Test-Path $PackagesRoot)) { return } -return $result +@{ + "$PackagesRoot" = (Get-ChildItem $PackagesRoot -Recurse) +} diff --git a/azure-pipelines/artifacts/mpc.ps1 b/azure-pipelines/artifacts/mpc.ps1 deleted file mode 100644 index fa22d29d2..000000000 --- a/azure-pipelines/artifacts/mpc.ps1 +++ /dev/null @@ -1,11 +0,0 @@ -$RepoRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..") -$BuildConfiguration = $env:BUILDCONFIGURATION -if (!$BuildConfiguration) { - $BuildConfiguration = 'Debug' -} - -$mpcArtifactPath = "$RepoRoot/bin/MessagePack.Generator/$BuildConfiguration/netcoreapp3.0" - -@{ - $mpcArtifactPath = (Get-ChildItem $mpcArtifactPath -Recurse); -} diff --git a/azure-pipelines/artifacts/nuget.ps1 b/azure-pipelines/artifacts/nuget.ps1 deleted file mode 100644 index ea4a55a06..000000000 --- a/azure-pipelines/artifacts/nuget.ps1 +++ /dev/null @@ -1,14 +0,0 @@ -$RepoRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..") -$BuildConfiguration = $env:BUILDCONFIGURATION -if (!$BuildConfiguration) { - $BuildConfiguration = 'Debug' -} - -$result = @{} - -$PackagesRoot = "$RepoRoot/bin/Packages/$BuildConfiguration/NuGet" -if (Test-Path $PackagesRoot) { - $result[$PackagesRoot] = (Get-ChildItem $PackagesRoot -Recurse) -} - -return $result diff --git a/azure-pipelines/artifacts/symbols.ps1 b/azure-pipelines/artifacts/symbols.ps1 new file mode 100644 index 000000000..9e2c7bd5b --- /dev/null +++ b/azure-pipelines/artifacts/symbols.ps1 @@ -0,0 +1,7 @@ +$BinPath = [System.IO.Path]::GetFullPath("$PSScriptRoot/../../bin") +if (!(Test-Path $BinPath)) { return } +$symbolfiles = & "$PSScriptRoot/../Get-SymbolFiles.ps1" -Path $BinPath | Get-Unique + +@{ + "$BinPath" = $SymbolFiles; +} diff --git a/azure-pipelines/artifacts/testResults.ps1 b/azure-pipelines/artifacts/testResults.ps1 new file mode 100644 index 000000000..6c042043a --- /dev/null +++ b/azure-pipelines/artifacts/testResults.ps1 @@ -0,0 +1,15 @@ +[CmdletBinding()] +Param( +) + +$result = @{} + +$testRoot = Resolve-Path "$PSScriptRoot\..\..\tests" +$result[$testRoot] = (Get-ChildItem "$testRoot\TestResults" -Recurse -Directory | Get-ChildItem -Recurse -File) + +$testlogsPath = "$env:BUILD_ARTIFACTSTAGINGDIRECTORY\test_logs" +if (Test-Path $testlogsPath) { + $result[$testlogsPath] = Get-ChildItem "$testlogsPath\*"; +} + +$result diff --git a/azure-pipelines/artifacts/test_symbols.ps1 b/azure-pipelines/artifacts/test_symbols.ps1 new file mode 100644 index 000000000..ce2b6481c --- /dev/null +++ b/azure-pipelines/artifacts/test_symbols.ps1 @@ -0,0 +1,7 @@ +$BinPath = [System.IO.Path]::GetFullPath("$PSScriptRoot/../../bin") +if (!(Test-Path $BinPath)) { return } +$symbolfiles = & "$PSScriptRoot/../Get-SymbolFiles.ps1" -Path $BinPath -Tests | Get-Unique + +@{ + "$BinPath" = $SymbolFiles; +} diff --git a/azure-pipelines/artifacts/unity.ps1 b/azure-pipelines/artifacts/unity.ps1 deleted file mode 100644 index b5059b309..000000000 --- a/azure-pipelines/artifacts/unity.ps1 +++ /dev/null @@ -1,14 +0,0 @@ -$RepoRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..") -$BuildConfiguration = $env:BUILDCONFIGURATION -if (!$BuildConfiguration) { - $BuildConfiguration = 'Debug' -} - -$result = @{} - -$UnityPackRoot = "$RepoRoot/bin" -if (Test-Path $UnityPackRoot) { - $result[$UnityPackRoot] = (Get-ChildItem $UnityPackRoot/*.unitypackage) -} - -return $result diff --git a/azure-pipelines/build.yml b/azure-pipelines/build.yml index 99f5524dd..c9f5fc170 100644 --- a/azure-pipelines/build.yml +++ b/azure-pipelines/build.yml @@ -1,30 +1,93 @@ -steps: -- task: DotNetCoreCLI@2 - inputs: - command: build - arguments: --configuration $(BuildConfiguration) /t:build,pack /m /v:m /bl:"$(Build.ArtifactStagingDirectory)/build_logs/msbuild.binlog" - displayName: Build MessagePack.sln +parameters: +- name: windowsPool + type: object + default: + vmImage: windows-2022 +- name: includeMacOS +- name: RunTests + type: boolean + default: true -- task: DotNetCoreCLI@2 - displayName: Run tests - inputs: - command: test - projects: tests/**/*.Tests.csproj - arguments: --configuration $(BuildConfiguration) --no-build -v n --settings "$(Build.Repository.LocalPath)/azure-pipelines/$(Agent.OS).runsettings" - testRunTitle: $(Agent.JobName) +jobs: +- job: Windows + pool: ${{ parameters.windowsPool }} + steps: + - checkout: self + fetchDepth: 0 # avoid shallow clone so nbgv can do its work. + clean: true + - template: install-dependencies.yml -## The rest of these steps are for deployment and skipped for PR builds + - powershell: '& (./azure-pipelines/Get-nbgv.ps1) cloud -c' + displayName: ⚙ Set build number -- task: PowerShell@2 - inputs: - filePath: azure-pipelines/variables/_pipelines.ps1 - failOnStderr: true - displayName: Update pipeline variables based on build outputs - condition: succeededOrFailed() + - template: dotnet.yml + parameters: + RunTests: ${{ parameters.RunTests }} + +- job: Linux + pool: + vmImage: ubuntu-22.04 + steps: + - checkout: self + fetchDepth: 0 # avoid shallow clone so nbgv can do its work. + clean: true + - template: install-dependencies.yml + - template: dotnet.yml + parameters: + RunTests: ${{ parameters.RunTests }} + +- job: macOS + condition: ${{ parameters.includeMacOS }} + pool: + vmImage: macOS-12 + steps: + - checkout: self + fetchDepth: 0 # avoid shallow clone so nbgv can do its work. + clean: true + - template: install-dependencies.yml + - template: dotnet.yml + parameters: + RunTests: ${{ parameters.RunTests }} + +# This job ensures that we're running mpc regularly on the generated code that we check in. +# It also helps exercise mpc so bugs don't go unnoticed. +- job: codegen_diff + pool: + vmImage: ubuntu-22.04 + steps: + - checkout: self + clean: true + - template: install-dependencies.yml + - pwsh: sandbox/codegen.ps1 + displayName: 🏭 Regenerate checked-in code + - bash: | + git add -u . # compare after applying git EOL normalization + git diff --cached --exit-code --stat \ + || (echo "##[error] found changed files after build. Please run 'sandbox/codegen.ps1'" \ + "and check in all changes" \ + && git diff --cached \ + && exit 1) + displayName: 🔍 Check for uncommitted changes -- task: PowerShell@2 - inputs: - filePath: azure-pipelines/artifacts/_pipelines.ps1 - # arguments: -ArtifactNameSuffix "-$(Agent.JobName)" - displayName: Publish artifacts +- job: WrapUp + dependsOn: + - Windows + - Linux + - macOS + pool: ${{ parameters.windowsPool }} # Use Windows agent because PublishSymbols task requires it (https://github.com/microsoft/azure-pipelines-tasks/issues/13821). condition: succeededOrFailed() + steps: + - checkout: self + fetchDepth: 0 # avoid shallow clone so nbgv can do its work. + clean: true + - template: install-dependencies.yml + parameters: + initArgs: -NoRestore + - template: publish-symbols.yml + parameters: + includeMacOS: ${{ parameters.includeMacOS }} + - ${{ if parameters.RunTests }}: + - template: publish-codecoverage.yml + parameters: + includeMacOS: ${{ parameters.includeMacOS }} + - template: publish-deployables.yml diff --git a/azure-pipelines/build_nonWindows.yml b/azure-pipelines/build_nonWindows.yml deleted file mode 100644 index cf3f51d76..000000000 --- a/azure-pipelines/build_nonWindows.yml +++ /dev/null @@ -1,41 +0,0 @@ -steps: -- task: DotNetCoreCLI@2 - displayName: Build MessagePack.sln - inputs: - command: build - arguments: --no-restore -c $(BuildConfiguration) - -- task: DotNetCoreCLI@2 - displayName: Run MessagePack.Tests (netcoreapp3.1) - inputs: - command: test - projects: tests/MessagePack.Tests/MessagePack.Tests.csproj - arguments: --no-build -c $(BuildConfiguration) -f netcoreapp3.1 -v n --settings "$(Build.Repository.LocalPath)/azure-pipelines/$(Agent.OS).runsettings" - testRunTitle: netcoreapp3.1-$(Agent.JobName) - -- task: DotNetCoreCLI@2 - displayName: Run MessagePack.Tests (net6.0) - inputs: - command: test - projects: tests/MessagePack.Tests/MessagePack.Tests.csproj - arguments: --no-build -c $(BuildConfiguration) -f net6.0 -v n --settings "$(Build.Repository.LocalPath)/azure-pipelines/$(Agent.OS).runsettings" - testRunTitle: net6.0-$(Agent.JobName) - -- bash: mono ~/.nuget/packages/xunit.runner.console/2.4.1/tools/net472/xunit.console.exe bin/MessagePack.Tests/$(BuildConfiguration)/net472/MessagePack.Tests.dll -html $(BUILD.ARTIFACTSTAGINGDIRECTORY)/build_logs/mono_testrun.html - displayName: Run MessagePack.Tests (mono) - -- task: DotNetCoreCLI@2 - displayName: Run MessagePackAnalyzer.Tests - inputs: - command: test - projects: tests/MessagePackAnalyzer.Tests/MessagePackAnalyzer.Tests.csproj - arguments: --no-build -c $(BuildConfiguration) -f netcoreapp3.1 -v n --settings "$(Build.Repository.LocalPath)/azure-pipelines/$(Agent.OS).runsettings" - testRunTitle: netcoreapp3.1-$(Agent.JobName)-Analyzers - -- task: DotNetCoreCLI@2 - displayName: Run MessagePack.Generator.Tests - inputs: - command: test - projects: tests/MessagePack.Generator.Tests/MessagePack.Generator.Tests.csproj - arguments: --no-build -c $(BuildConfiguration) -f netcoreapp3.1 -v n --settings "$(Build.Repository.LocalPath)/azure-pipelines/$(Agent.OS).runsettings" - testRunTitle: netcoreapp3.1-$(Agent.JobName)-Generator diff --git a/azure-pipelines/build_unity.yml b/azure-pipelines/build_unity.yml deleted file mode 100644 index 6338d449b..000000000 --- a/azure-pipelines/build_unity.yml +++ /dev/null @@ -1,34 +0,0 @@ -steps: -- powershell: Write-Host "##vso[task.setvariable variable=unity_package_version;]$((& (./azure-pipelines/Get-nbgv.ps1) get-version -f json | ConvertFrom-Json).NuGetPackageVersion)" - displayName: Get unity package version - -- script: dotnet publish src/MessagePack -c $(BuildConfiguration) -f netstandard2.0 - displayName: Build MessagePack - -- script: src/MessagePack.UnityClient/build.sh -l $(Build.ArtifactStagingDirectory)/build_logs/unitypackage.log - displayName: Build Unity package - -- task: PublishBuildArtifacts@1 - inputs: - PathtoPublish: $(Build.ArtifactStagingDirectory)/build_logs - ArtifactName: build_logs - ArtifactType: Container - displayName: Publish build_logs artifacts - condition: succeededOrFailed() - -- task: CopyFiles@1 - inputs: - Contents: | - bin/*.unitypackage - TargetFolder: $(Build.ArtifactStagingDirectory)/unity - flattenFolders: true - displayName: Collecting deployables - condition: succeededOrFailed() - -- task: PublishBuildArtifacts@1 - inputs: - PathtoPublish: $(Build.ArtifactStagingDirectory)/unity - ArtifactName: unity - ArtifactType: Container - displayName: Publish deployables artifacts - condition: succeededOrFailed() diff --git a/azure-pipelines/dotnet-test-cloud.ps1 b/azure-pipelines/dotnet-test-cloud.ps1 new file mode 100644 index 000000000..13f973cd4 --- /dev/null +++ b/azure-pipelines/dotnet-test-cloud.ps1 @@ -0,0 +1,83 @@ +#!/usr/bin/env pwsh + +<# +.SYNOPSIS + Runs tests as they are run in cloud test runs. +.PARAMETER Configuration + The configuration within which to run tests +.PARAMETER Agent + The name of the agent. This is used in preparing test run titles. +.PARAMETER PublishResults + A switch to publish results to Azure Pipelines. +.PARAMETER x86 + A switch to run the tests in an x86 process. +.PARAMETER dotnet32 + The path to a 32-bit dotnet executable to use. +#> +[CmdletBinding()] +Param( + [string]$Configuration='Debug', + [string]$Agent='Local', + [switch]$PublishResults, + [switch]$x86, + [string]$dotnet32 +) + +$RepoRoot = (Resolve-Path "$PSScriptRoot/..").Path +$ArtifactStagingFolder = & "$PSScriptRoot/Get-ArtifactsStagingDirectory.ps1" + +$dotnet = 'dotnet' +if ($x86) { + $x86RunTitleSuffix = ", x86" + if ($dotnet32) { + $dotnet = $dotnet32 + } else { + $dotnet32Possibilities = "$PSScriptRoot\../obj/tools/x86/.dotnet/dotnet.exe", "$env:AGENT_TOOLSDIRECTORY/x86/dotnet/dotnet.exe", "${env:ProgramFiles(x86)}\dotnet\dotnet.exe" + $dotnet32Matches = $dotnet32Possibilities |? { Test-Path $_ } + if ($dotnet32Matches) { + $dotnet = Resolve-Path @($dotnet32Matches)[0] + Write-Host "Running tests using `"$dotnet`"" -ForegroundColor DarkGray + } else { + Write-Error "Unable to find 32-bit dotnet.exe" + return 1 + } + } +} + +& $dotnet test $RepoRoot ` + --no-build ` + -c $Configuration ` + --filter "TestCategory!=FailsInCloudTest" ` + --collect "Code Coverage;Format=cobertura" ` + --settings "$PSScriptRoot/test.runsettings" ` + --blame-hang-timeout 60s ` + --blame-crash ` + -bl:"$ArtifactStagingFolder/build_logs/test.binlog" ` + --diag "$ArtifactStagingFolder/test_logs/diag.log;TraceLevel=info" ` + --logger trx ` + +$unknownCounter = 0 +Get-ChildItem -Recurse -Path $RepoRoot\tests\*.trx |% { + Copy-Item $_ -Destination $ArtifactStagingFolder/test_logs/ + + if ($PublishResults) { + $x = [xml](Get-Content -Path $_) + $runTitle = $null + if ($x.TestRun.TestDefinitions -and $x.TestRun.TestDefinitions.GetElementsByTagName('UnitTest')) { + $storage = $x.TestRun.TestDefinitions.GetElementsByTagName('UnitTest')[0].storage -replace '\\','/' + if ($storage -match '/(?net[^/]+)/(?:(?[^/]+)/)?(?[^/]+)\.dll$') { + if ($matches.rid) { + $runTitle = "$($matches.lib) ($($matches.tfm), $($matches.rid), $Agent)" + } else { + $runTitle = "$($matches.lib) ($($matches.tfm)$x86RunTitleSuffix, $Agent)" + } + } + } + if (!$runTitle) { + $unknownCounter += 1; + $runTitle = "unknown$unknownCounter ($Agent$x86RunTitleSuffix)"; + } + + Write-Host "##vso[results.publish type=VSTest;runTitle=$runTitle;publishRunAttachments=true;resultFiles=$_;failTaskOnFailedTests=true;testRunSystem=VSTS - PTR;]" + } +} diff --git a/azure-pipelines/dotnet.yml b/azure-pipelines/dotnet.yml new file mode 100644 index 000000000..79babd4d4 --- /dev/null +++ b/azure-pipelines/dotnet.yml @@ -0,0 +1,30 @@ +parameters: + RunTests: + +steps: + +- script: dotnet build -t:build,pack --no-restore -c $(BuildConfiguration) /bl:"$(Build.ArtifactStagingDirectory)/build_logs/build.binlog" + displayName: 🛠 dotnet build + +- powershell: azure-pipelines/dotnet-test-cloud.ps1 -Configuration $(BuildConfiguration) -Agent $(Agent.JobName) -PublishResults + displayName: 🧪 dotnet test + condition: and(succeeded(), ${{ parameters.RunTests }}) + +- powershell: azure-pipelines/variables/_pipelines.ps1 + failOnStderr: true + displayName: ⚙ Update pipeline variables based on build outputs + condition: succeededOrFailed() + +- powershell: azure-pipelines/artifacts/_pipelines.ps1 -ArtifactNameSuffix "-$(Agent.JobName)" -Verbose + failOnStderr: true + displayName: 📢 Publish artifacts + condition: succeededOrFailed() + +- ${{ if and(ne(variables['codecov_token'], ''), parameters.RunTests) }}: + - powershell: | + $ArtifactStagingFolder = & "azure-pipelines/Get-ArtifactsStagingDirectory.ps1" + $CoverageResultsFolder = Join-Path $ArtifactStagingFolder "coverageResults-$(Agent.JobName)" + azure-pipelines/publish-CodeCov.ps1 -CodeCovToken "$(codecov_token)" -PathToCodeCoverage "$CoverageResultsFolder" -Name "$(Agent.JobName) Coverage Results" -Flags "$(Agent.JobName)Host,$(BuildConfiguration)" + displayName: 📢 Publish code coverage results to codecov.io + timeoutInMinutes: 3 + continueOnError: true diff --git a/azure-pipelines/install-dependencies.yml b/azure-pipelines/install-dependencies.yml index 7563bc1f8..817826689 100644 --- a/azure-pipelines/install-dependencies.yml +++ b/azure-pipelines/install-dependencies.yml @@ -3,18 +3,23 @@ parameters: steps: +- task: NuGetAuthenticate@1 + displayName: 🔏 Authenticate NuGet feeds + inputs: + forceReinstallCredentialProvider: true + - powershell: | $AccessToken = '$(System.AccessToken)' # Avoid specifying the access token directly on the init.ps1 command line to avoid it showing up in errors - .\init.ps1 -AccessToken $AccessToken ${{ parameters['initArgs'] }} -UpgradePrerequisites + .\init.ps1 -AccessToken $AccessToken ${{ parameters['initArgs'] }} -UpgradePrerequisites -NoNuGetCredProvider dotnet --info # Print mono version if it is present. if (Get-Command mono -ErrorAction SilentlyContinue) { mono --version } - displayName: Install prerequisites + displayName: ⚙ Install prerequisites - powershell: azure-pipelines/variables/_pipelines.ps1 failOnStderr: true - displayName: Set pipeline variables based on source + displayName: ⚙ Set pipeline variables based on source name: SetPipelineVariables diff --git a/azure-pipelines/publish-CodeCov.ps1 b/azure-pipelines/publish-CodeCov.ps1 new file mode 100644 index 000000000..9926f0188 --- /dev/null +++ b/azure-pipelines/publish-CodeCov.ps1 @@ -0,0 +1,30 @@ +<# +.SYNOPSIS + Uploads code coverage to codecov.io +.PARAMETER CodeCovToken + Code coverage token to use +.PARAMETER PathToCodeCoverage + Path to root of code coverage files +.PARAMETER Name + Name to upload with codecoverge +.PARAMETER Flags + Flags to upload with codecoverge +#> +[CmdletBinding()] +Param ( + [Parameter(Mandatory=$true)] + [string]$CodeCovToken, + [Parameter(Mandatory=$true)] + [string]$PathToCodeCoverage, + [string]$Name, + [string]$Flags +) + +$RepoRoot = (Resolve-Path "$PSScriptRoot/..").Path + +Get-ChildItem -Recurse -Path $PathToCodeCoverage -Filter "*.cobertura.xml" | % { + $relativeFilePath = Resolve-Path -relative $_.FullName + + Write-Host "Uploading: $relativeFilePath" -ForegroundColor Yellow + & (& "$PSScriptRoot/Get-CodeCovTool.ps1") -t $CodeCovToken -f $relativeFilePath -R $RepoRoot -F $Flags -n $Name +} diff --git a/azure-pipelines/publish-codecoverage.yml b/azure-pipelines/publish-codecoverage.yml new file mode 100644 index 000000000..fbb6a39a7 --- /dev/null +++ b/azure-pipelines/publish-codecoverage.yml @@ -0,0 +1,25 @@ +parameters: + includeMacOS: + +steps: +- download: current + artifact: coverageResults-Windows + displayName: 🔻 Download Windows code coverage results + continueOnError: true +- download: current + artifact: coverageResults-Linux + displayName: 🔻 Download Linux code coverage results + continueOnError: true +- download: current + artifact: coverageResults-macOS + displayName: 🔻 Download macOS code coverage results + continueOnError: true + condition: and(succeeded(), ${{ parameters.includeMacOS }}) +- powershell: azure-pipelines/Merge-CodeCoverage.ps1 -Path '$(Pipeline.Workspace)' -OutputFile coveragereport/merged.cobertura.xml -Format Cobertura -Verbose + displayName: ⚙ Merge coverage +- task: PublishCodeCoverageResults@1 + displayName: 📢 Publish code coverage results to Azure DevOps + inputs: + codeCoverageTool: cobertura + summaryFileLocation: coveragereport/merged.cobertura.xml + failIfCoverageEmpty: true diff --git a/azure-pipelines/publish-deployables.yml b/azure-pipelines/publish-deployables.yml new file mode 100644 index 000000000..31e80a437 --- /dev/null +++ b/azure-pipelines/publish-deployables.yml @@ -0,0 +1,8 @@ +steps: +- download: current + displayName: 🔻 Download deployables + artifact: deployables-Windows + +- powershell: dotnet nuget push "$(Resolve-Path '$(Pipeline.Workspace)\deployables-Windows\')*.nupkg" -s $(ci_feed) -k azdo --skip-duplicate + displayName: 📦 Push packages to CI feed + condition: and(succeeded(), ne(variables['ci_feed'], ''), ne(variables['Build.Reason'], 'PullRequest')) diff --git a/azure-pipelines/publish-symbols.yml b/azure-pipelines/publish-symbols.yml new file mode 100644 index 000000000..00c188fc3 --- /dev/null +++ b/azure-pipelines/publish-symbols.yml @@ -0,0 +1,59 @@ +parameters: + includeMacOS: + +steps: +- task: DownloadPipelineArtifact@2 + inputs: + artifact: symbols-Windows + path: $(Pipeline.Workspace)/symbols/Windows + displayName: 🔻 Download Windows symbols + continueOnError: true +- task: DownloadPipelineArtifact@2 + inputs: + artifact: symbols-Linux + path: $(Pipeline.Workspace)/symbols/Linux + displayName: 🔻 Download Linux symbols + continueOnError: true +- task: DownloadPipelineArtifact@2 + inputs: + artifact: symbols-macOS + path: $(Pipeline.Workspace)/symbols/macOS + displayName: 🔻 Download macOS symbols + continueOnError: true + condition: ${{ parameters.includeMacOS }} + +- task: DownloadPipelineArtifact@2 + inputs: + artifact: test_symbols-Windows + path: $(Pipeline.Workspace)/test_symbols/Windows + displayName: 🔻 Download Windows test symbols + continueOnError: true +- task: DownloadPipelineArtifact@2 + inputs: + artifact: test_symbols-Linux + path: $(Pipeline.Workspace)/test_symbols/Linux + displayName: 🔻 Download Linux test symbols + continueOnError: true +- task: DownloadPipelineArtifact@2 + inputs: + artifact: test_symbols-macOS + path: $(Pipeline.Workspace)/test_symbols/macOS + displayName: 🔻 Download macOS test symbols + continueOnError: true + condition: ${{ parameters.includeMacOS }} + +- task: PublishSymbols@2 + inputs: + SymbolsFolder: $(Pipeline.Workspace)/symbols + SearchPattern: '**/*.pdb' + IndexSources: false + SymbolServerType: TeamServices + displayName: 📢 Publish symbols + +- task: PublishSymbols@2 + inputs: + SymbolsFolder: $(Pipeline.Workspace)/test_symbols + SearchPattern: '**/*.pdb' + IndexSources: false + SymbolServerType: TeamServices + displayName: 📢 Publish test symbols diff --git a/azure-pipelines/release.yml b/azure-pipelines/release.yml index 83a6ab1ab..be5c4f614 100644 --- a/azure-pipelines/release.yml +++ b/azure-pipelines/release.yml @@ -9,67 +9,54 @@ resources: tags: - auto-release -stages: -- stage: GitHubRelease - displayName: GitHub Release - jobs: - - deployment: create - pool: - vmImage: ubuntu-latest - environment: No-Approval - strategy: - runOnce: - deploy: - steps: - - download: none - - powershell: | - Write-Host "##vso[build.updatebuildnumber]$(resources.pipeline.CI.runName)" - displayName: Set pipeline name - - download: CI - artifact: unity - displayName: 'Downloading artifact: unity' - - task: GitHubRelease@1 - displayName: GitHub release (create) - inputs: - gitHubConnection: AArnott github - repositoryName: $(Build.Repository.Name) - target: $(resources.pipeline.CI.sourceCommit) - tagSource: userSpecifiedTag - tag: v$(resources.pipeline.CI.runName) - title: v$(resources.pipeline.CI.runName) - assets: | - $(Pipeline.Workspace)/CI/unity/*.unitypackage - isDraft: true # After running this step, visit the new draft release, edit, and publish. - changeLogCompareToRelease: lastNonDraftRelease - changeLogType: issueBased - changeLogLabels: | - [ - { "label" : "bug", "displayName" : "Fixes", "state" : "closed" }, - { "label" : "enhancement", "displayName": "Enhancements", "state" : "closed" } - ] +variables: +- group: Publishing secrets -- stage: nuget_org - displayName: nuget.org - dependsOn: GitHubRelease - jobs: - - deployment: push - pool: - vmImage: ubuntu-latest - environment: No-Approval - strategy: - runOnce: - deploy: - steps: - - download: CI - artifact: nuget - - task: NuGetToolInstaller@1 - displayName: Use NuGet 5.x - inputs: - versionSpec: 5.x - - task: NuGetCommand@2 - displayName: NuGet push - inputs: - command: push - packagesToPush: $(Pipeline.Workspace)/CI/nuget/*.nupkg - nuGetFeedType: external - publishFeedCredentials: nuget.org (aarnott) +jobs: +- job: release + pool: + vmImage: ubuntu-latest + steps: + - checkout: none + - powershell: | + Write-Host "##vso[build.updatebuildnumber]$(resources.pipeline.CI.runName)" + if ('$(resources.pipeline.CI.runName)'.Contains('-')) { + Write-Host "##vso[task.setvariable variable=IsPrerelease]true" + } else { + Write-Host "##vso[task.setvariable variable=IsPrerelease]false" + } + displayName: ⚙ Set up pipeline + - task: UseDotNet@2 + displayName: ⚙ Install .NET SDK + inputs: + packageType: sdk + version: 6.x + - download: CI + artifact: deployables-Windows + displayName: 🔻 Download deployables-Windows artifact + patterns: 'deployables-Windows/*' + - task: GitHubRelease@1 + displayName: 📢 GitHub release (create) + inputs: + gitHubConnection: AArnott github + repositoryName: $(Build.Repository.Name) + target: $(resources.pipeline.CI.sourceCommit) + tagSource: userSpecifiedTag + tag: v$(resources.pipeline.CI.runName) + title: v$(resources.pipeline.CI.runName) + isDraft: true # After running this step, visit the new draft release, edit, and publish. + isPreRelease: $(IsPrerelease) + assets: | + $(Pipeline.Workspace)/CI/deployables-Windows/*.nupkg + $(Pipeline.Workspace)/CI/unity/*.unitypackage + changeLogCompareToRelease: lastNonDraftRelease + changeLogType: issueBased + changeLogLabels: | + [ + { "label" : "breaking change", "displayName" : "Breaking changes", "state" : "closed" }, + { "label" : "bug", "displayName" : "Fixes", "state" : "closed" }, + { "label" : "enhancement", "displayName": "Enhancements", "state" : "closed" } + ] + - script: dotnet nuget push $(Pipeline.Workspace)/CI/deployables-Windows/*.nupkg -s https://api.nuget.org/v3/index.json --api-key $(NuGetOrgApiKey) --skip-duplicate + displayName: 📦 Push packages to nuget.org + condition: and(succeeded(), ne(variables['NuGetOrgApiKey'], '')) diff --git a/azure-pipelines/test.runsettings b/azure-pipelines/test.runsettings new file mode 100644 index 000000000..4e24a0a65 --- /dev/null +++ b/azure-pipelines/test.runsettings @@ -0,0 +1,44 @@ + + + + + + + + + \.dll$ + \.exe$ + + + xunit\..* + + + + + ^System\.Diagnostics\.DebuggerHiddenAttribute$ + ^System\.Diagnostics\.DebuggerNonUserCodeAttribute$ + ^System\.CodeDom\.Compiler\.GeneratedCodeAttribute$ + ^System\.Diagnostics\.CodeAnalysis\.ExcludeFromCodeCoverageAttribute$ + + + + + True + + True + + True + + False + + False + + False + + True + + + + + + diff --git a/azure-pipelines/variables/_all.ps1 b/azure-pipelines/variables/_all.ps1 index 0407d307e..cc6e88105 100755 --- a/azure-pipelines/variables/_all.ps1 +++ b/azure-pipelines/variables/_all.ps1 @@ -1,7 +1,14 @@ #!/usr/bin/env pwsh -# This script returns a hashtable of build variables that should be set -# at the start of a build or release definition's execution. +<# +.SYNOPSIS + This script returns a hashtable of build variables that should be set + at the start of a build or release definition's execution. +#> + +[CmdletBinding(SupportsShouldProcess = $true)] +param ( +) $vars = @{} diff --git a/azure-pipelines/variables/_pipelines.ps1 b/azure-pipelines/variables/_pipelines.ps1 index 867b7fc8b..11748b81b 100644 --- a/azure-pipelines/variables/_pipelines.ps1 +++ b/azure-pipelines/variables/_pipelines.ps1 @@ -1,13 +1,20 @@ -# This script translates the variables returned by the _all.ps1 script -# into commands that instruct Azure Pipelines to actually set those variables for other pipeline tasks to consume. +<# +.SYNOPSIS + This script translates the variables returned by the _all.ps1 script + into commands that instruct Azure Pipelines to actually set those variables for other pipeline tasks to consume. -# The build or release definition may have set these variables to override -# what the build would do. So only set them if they have not already been set. + The build or release definition may have set these variables to override + what the build would do. So only set them if they have not already been set. +#> + +[CmdletBinding()] +param ( +) (& "$PSScriptRoot\_all.ps1").GetEnumerator() |% { # Always use ALL CAPS for env var names since Azure Pipelines converts variable names to all caps and on non-Windows OS, env vars are case sensitive. $keyCaps = $_.Key.ToUpper() - if (Test-Path -Path "env:$keyCaps") { + if ((Test-Path "env:$keyCaps") -and (Get-Content "env:$keyCaps")) { Write-Host "Skipping setting $keyCaps because variable is already set to '$(Get-Content env:$keyCaps)'." -ForegroundColor Cyan } else { Write-Host "$keyCaps=$($_.Value)" -ForegroundColor Yellow diff --git a/benchmark/.editorconfig b/benchmark/.editorconfig new file mode 100644 index 000000000..c43f8d7eb --- /dev/null +++ b/benchmark/.editorconfig @@ -0,0 +1,2 @@ +# SA1300: Element should begin with an uppercase letter +dotnet_diagnostic.SA1300.severity = silent diff --git a/benchmark/Directory.Build.props b/benchmark/Directory.Build.props new file mode 100644 index 000000000..68555510b --- /dev/null +++ b/benchmark/Directory.Build.props @@ -0,0 +1,6 @@ + + + + false + + diff --git a/benchmark/ExperimentalBenchmark/ExperimentalBenchmark.csproj b/benchmark/ExperimentalBenchmark/ExperimentalBenchmark.csproj index dbefafaf4..08cce028b 100644 --- a/benchmark/ExperimentalBenchmark/ExperimentalBenchmark.csproj +++ b/benchmark/ExperimentalBenchmark/ExperimentalBenchmark.csproj @@ -2,16 +2,15 @@ Exe - netcoreapp3.1 + net6.0 Benchmark true - True $(NoWarn);MSB3243 - - + + diff --git a/benchmark/ExperimentalBenchmark/Tests.cs b/benchmark/ExperimentalBenchmark/Tests.cs index ffbfe7d64..e51837b1b 100644 --- a/benchmark/ExperimentalBenchmark/Tests.cs +++ b/benchmark/ExperimentalBenchmark/Tests.cs @@ -9,6 +9,7 @@ using System.Runtime.InteropServices; using BenchmarkDotNet.Attributes; +#pragma warning disable SA1402 // File may only contain a single type #pragma warning disable SA1649 // File name should match first type name namespace Benchmark diff --git a/benchmark/SerializerBenchmark/BenchmarkConfig.cs b/benchmark/SerializerBenchmark/BenchmarkConfig.cs index e6e7066c4..750d4f2d2 100644 --- a/benchmark/SerializerBenchmark/BenchmarkConfig.cs +++ b/benchmark/SerializerBenchmark/BenchmarkConfig.cs @@ -42,7 +42,7 @@ public class CustomOrderer : IOrderer { public bool SeparateLogicalGroups => false; - public IEnumerable GetExecutionOrder(ImmutableArray benchmarksCase) + public IEnumerable GetExecutionOrder(ImmutableArray benchmarksCase, IEnumerable order) { return benchmarksCase; } @@ -57,7 +57,7 @@ public string GetLogicalGroupKey(ImmutableArray allBenchmarksCase return null; } - public IEnumerable> GetLogicalGroupOrder(IEnumerable> logicalGroups) + public IEnumerable> GetLogicalGroupOrder(IEnumerable> logicalGroups, IEnumerable order) { return logicalGroups; } diff --git a/benchmark/SerializerBenchmark/Fixture/OtherPrimitiveFixtures.cs b/benchmark/SerializerBenchmark/Fixture/OtherPrimitiveFixtures.cs index b73b10e83..0fb2a100c 100644 --- a/benchmark/SerializerBenchmark/Fixture/OtherPrimitiveFixtures.cs +++ b/benchmark/SerializerBenchmark/Fixture/OtherPrimitiveFixtures.cs @@ -4,6 +4,7 @@ using System; using System.Runtime.CompilerServices; +#pragma warning disable SA1402 // File may only contain a single type #pragma warning disable SA1649 // File name should match first type name namespace Benchmark.Fixture diff --git a/benchmark/SerializerBenchmark/Models/Answer.cs b/benchmark/SerializerBenchmark/Models/Answer.cs index cf421348c..442fe8526 100644 --- a/benchmark/SerializerBenchmark/Models/Answer.cs +++ b/benchmark/SerializerBenchmark/Models/Answer.cs @@ -1,6 +1,7 @@ // Copyright (c) All contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +#pragma warning disable SA1402 // File may only contain a single type #pragma warning disable IDE1006 #pragma warning disable SA1516 diff --git a/benchmark/SerializerBenchmark/Models/Comment.cs b/benchmark/SerializerBenchmark/Models/Comment.cs index 1142d728e..e923671df 100644 --- a/benchmark/SerializerBenchmark/Models/Comment.cs +++ b/benchmark/SerializerBenchmark/Models/Comment.cs @@ -1,6 +1,7 @@ // Copyright (c) All contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +#pragma warning disable SA1402 // File may only contain a single type #pragma warning disable IDE1006 #pragma warning disable SA1516 diff --git a/benchmark/SerializerBenchmark/Models/Feed.cs b/benchmark/SerializerBenchmark/Models/Feed.cs index d18af0361..196b2a4c8 100644 --- a/benchmark/SerializerBenchmark/Models/Feed.cs +++ b/benchmark/SerializerBenchmark/Models/Feed.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using ProtoBuf; +#pragma warning disable SA1402 // File may only contain a single type #pragma warning disable SA1649 // File name should match first type name namespace Benchmark.Models diff --git a/benchmark/SerializerBenchmark/Models/ShallowUser.cs b/benchmark/SerializerBenchmark/Models/ShallowUser.cs index d3faeb43b..1c0b07055 100644 --- a/benchmark/SerializerBenchmark/Models/ShallowUser.cs +++ b/benchmark/SerializerBenchmark/Models/ShallowUser.cs @@ -1,6 +1,7 @@ // Copyright (c) All contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +#pragma warning disable SA1402 // File may only contain a single type #pragma warning disable IDE1006 #pragma warning disable SA1516 diff --git a/benchmark/SerializerBenchmark/Program.cs b/benchmark/SerializerBenchmark/Program.cs index c0d23073b..880af229b 100644 --- a/benchmark/SerializerBenchmark/Program.cs +++ b/benchmark/SerializerBenchmark/Program.cs @@ -10,7 +10,7 @@ internal static class Program private static void Main(string[] args) { #if !DEBUG - //BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); + ////BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); BenchmarkRunner.Run(); #else BenchmarkRunner.Run(new BenchmarkDotNet.Configs.DebugInProcessConfig()); diff --git a/benchmark/SerializerBenchmark/SerializerBenchmark.cs b/benchmark/SerializerBenchmark/SerializerBenchmark.cs index 71a95e208..cd58a1641 100644 --- a/benchmark/SerializerBenchmark/SerializerBenchmark.cs +++ b/benchmark/SerializerBenchmark/SerializerBenchmark.cs @@ -11,6 +11,7 @@ #pragma warning disable SA1300 // Element should begin with upper-case letter #pragma warning disable SA1306 // Field names should begin with lower-case letter #pragma warning disable SA1401 // Fields should be private +#pragma warning disable SA1402 // File may only contain a single type #pragma warning disable SA1649 // File name should match first type name namespace Benchmark @@ -29,8 +30,8 @@ public class AllSerializerBenchmark_BytesInOut new MessagePackLz4_v1(), new MessagePackLz4_v2(), new MsgPack_v2_opt(), - //new MsgPack_v2_string(), - //new MsgPack_v2_str_lz4(), + ////new MsgPack_v2_string(), + ////new MsgPack_v2_str_lz4(), new ProtobufNetSerializer(), new JsonNetSerializer(), new BsonNetSerializer(), @@ -1135,6 +1136,7 @@ public class ShortRun_AllSerializerBenchmark_BytesInOut // models protected static readonly Benchmark.Models.Answer AnswerInput = ExpressionTreeFixture.Create(); + // not same data so does not gurantee correctly. protected static readonly Benchmark.Models.Answer2 Answer2Input = ExpressionTreeFixture.Create(); diff --git a/benchmark/SerializerBenchmark/SerializerBenchmark.csproj b/benchmark/SerializerBenchmark/SerializerBenchmark.csproj index b6be2ea09..a6ea965ee 100644 --- a/benchmark/SerializerBenchmark/SerializerBenchmark.csproj +++ b/benchmark/SerializerBenchmark/SerializerBenchmark.csproj @@ -2,26 +2,25 @@ Exe - netcoreapp3.1 + net7.0 SerializerBenchmark Benchmark true - SerializerBenchmark.ruleset + $(NoWarn);SA1300 - - - - - - - - - - - - + + + + + + + + + + + @@ -41,6 +40,6 @@ false - + diff --git a/benchmark/SerializerBenchmark/SerializerBenchmark.ruleset b/benchmark/SerializerBenchmark/SerializerBenchmark.ruleset deleted file mode 100644 index 46e72a17f..000000000 --- a/benchmark/SerializerBenchmark/SerializerBenchmark.ruleset +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/benchmark/SerializerBenchmark/Serializers/BinaryFormatterSerializer.cs b/benchmark/SerializerBenchmark/Serializers/BinaryFormatterSerializer.cs index c54b5487f..ec3891178 100644 --- a/benchmark/SerializerBenchmark/Serializers/BinaryFormatterSerializer.cs +++ b/benchmark/SerializerBenchmark/Serializers/BinaryFormatterSerializer.cs @@ -6,6 +6,8 @@ namespace Benchmark.Serializers { +#pragma warning disable SYSLIB0011 // Type or member is obsolete + public class BinaryFormatterSerializer : SerializerBase { public override T Deserialize(object input) diff --git a/benchmark/SerializerBenchmark/Serializers/MessagePackSerializer.cs b/benchmark/SerializerBenchmark/Serializers/MessagePackSerializer.cs index 7583e0361..f9438c98c 100644 --- a/benchmark/SerializerBenchmark/Serializers/MessagePackSerializer.cs +++ b/benchmark/SerializerBenchmark/Serializers/MessagePackSerializer.cs @@ -4,6 +4,7 @@ extern alias oldmsgpack; extern alias newmsgpack; +#pragma warning disable SA1402 // File may only contain a single type #pragma warning disable SA1649 // File name should match first type name namespace Benchmark.Serializers diff --git a/global.json b/global.json index 954a92e75..c83e939e8 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "6.0.300", + "version": "8.0.300", "rollForward": "patch", "allowPrerelease": false } diff --git a/init.ps1 b/init.ps1 index e5aad5bfa..ad3b4145c 100755 --- a/init.ps1 +++ b/init.ps1 @@ -20,6 +20,9 @@ Per-machine requires elevation and will download and install all SDKs and runtimes to machine-wide locations so all applications can find it. .PARAMETER NoPrerequisites Skips the installation of prerequisite software (e.g. SDKs, tools). +.PARAMETER NoNuGetCredProvider + Skips the installation of the NuGet credential provider. Useful in pipelines with the `NuGetAuthenticate` task, as a workaround for https://github.com/microsoft/artifacts-credprovider/issues/244. + This switch is ignored and installation is skipped when -NoPrerequisites is specified. .PARAMETER UpgradePrerequisites Takes time to install prerequisites even if they are already present in case they need to be upgraded. No effect if -NoPrerequisites is specified. @@ -27,25 +30,35 @@ Skips the package restore step. .PARAMETER AccessToken An optional access token for authenticating to Azure Artifacts authenticated feeds. +.PARAMETER Interactive + Runs NuGet restore in interactive mode. This can turn authentication failures into authentication challenges. #> -[CmdletBinding(SupportsShouldProcess=$true)] +[CmdletBinding(SupportsShouldProcess = $true)] Param ( - [ValidateSet('repo','user','machine')] - [string]$InstallLocality='user', + [ValidateSet('repo', 'user', 'machine')] + [string]$InstallLocality = 'user', [Parameter()] [switch]$NoPrerequisites, [Parameter()] + [switch]$NoNuGetCredProvider, + [Parameter()] [switch]$UpgradePrerequisites, [Parameter()] [switch]$NoRestore, [Parameter()] - [string]$AccessToken + [string]$AccessToken, + [Parameter()] + [switch]$Interactive ) $EnvVars = @{} +$PrependPath = @() if (!$NoPrerequisites) { - & "$PSScriptRoot\tools\Install-NuGetCredProvider.ps1" -AccessToken $AccessToken -Force:$UpgradePrerequisites + if (!$NoNuGetCredProvider) { + & "$PSScriptRoot\tools\Install-NuGetCredProvider.ps1" -AccessToken $AccessToken -Force:$UpgradePrerequisites + } + & "$PSScriptRoot\tools\Install-DotNetSdk.ps1" -InstallLocality $InstallLocality if ($LASTEXITCODE -eq 3010) { Exit 3010 @@ -59,22 +72,28 @@ if (!$NoPrerequisites) { } # Workaround nuget credential provider bug that causes very unreliable package restores on Azure Pipelines -$env:NUGET_PLUGIN_HANDSHAKE_TIMEOUT_IN_SECONDS=20 -$env:NUGET_PLUGIN_REQUEST_TIMEOUT_IN_SECONDS=20 +$env:NUGET_PLUGIN_HANDSHAKE_TIMEOUT_IN_SECONDS = 20 +$env:NUGET_PLUGIN_REQUEST_TIMEOUT_IN_SECONDS = 20 Push-Location $PSScriptRoot try { $HeaderColor = 'Green' if (!$NoRestore -and $PSCmdlet.ShouldProcess("NuGet packages", "Restore")) { + $RestoreArguments = @() + if ($Interactive) + { + $RestoreArguments += '--interactive' + } + Write-Host "Restoring NuGet packages" -ForegroundColor $HeaderColor - dotnet restore + dotnet restore @RestoreArguments if ($lastexitcode -ne 0) { throw "Failure while restoring packages." } } - & "$PSScriptRoot/tools/Set-EnvVars.ps1" -Variables $EnvVars | Out-Null + & "$PSScriptRoot/tools/Set-EnvVars.ps1" -Variables $EnvVars -PrependPath $PrependPath | Out-Null } catch { Write-Error $error[0] diff --git a/sandbox/Directory.Build.props b/sandbox/Directory.Build.props new file mode 100644 index 000000000..68555510b --- /dev/null +++ b/sandbox/Directory.Build.props @@ -0,0 +1,6 @@ + + + + false + + diff --git a/sandbox/DynamicCodeDumper/DynamicCodeDumper.csproj b/sandbox/DynamicCodeDumper/DynamicCodeDumper.csproj index 9a41bc14d..676074044 100644 --- a/sandbox/DynamicCodeDumper/DynamicCodeDumper.csproj +++ b/sandbox/DynamicCodeDumper/DynamicCodeDumper.csproj @@ -2,6 +2,7 @@ net472 true + enable exe $(DefineConstants);DYNAMICCODEDUMPER @@ -45,6 +46,9 @@ Code\DynamicAssembly.cs + + Code\DynamicAssemblyFactory.cs + Code\ExpressionUtility.cs @@ -69,6 +73,9 @@ Code\ExtensionResult.cs + + Code\MessagePackEventSource.cs + Code\MessagePackSerializerOptions.cs @@ -146,7 +153,7 @@ - - + + diff --git a/sandbox/DynamicCodeDumper/Program.cs b/sandbox/DynamicCodeDumper/Program.cs index 0d74db48d..d9ec30cb4 100644 --- a/sandbox/DynamicCodeDumper/Program.cs +++ b/sandbox/DynamicCodeDumper/Program.cs @@ -1,6 +1,8 @@ // Copyright (c) All contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +#pragma warning disable SA1402 // File may only contain a single type + using System; using System.Buffers; using System.Collections.Generic; @@ -43,7 +45,7 @@ private static void Main(string[] args) ////DynamicObjectResolver.Instance.GetFormatter(); ////DynamicObjectResolver.Instance.GetFormatter(); ////DynamicObjectResolver.Instance.GetFormatter(); - IMessagePackFormatter f = DynamicObjectResolverAllowPrivate.Instance.GetFormatter(); + IMessagePackFormatter f = DynamicObjectResolverAllowPrivate.Instance.GetFormatterWithVerify(); ////IMessagePackFormatter f = DynamicObjectResolver.Instance.GetFormatter(); ////DynamicObjectResolver.Instance.GetFormatter(); ////DynamicObjectResolver.Instance.GetFormatter(); @@ -114,7 +116,7 @@ private class EmptyResolver : IFormatterResolver { internal static readonly MessagePackSerializerOptions Options = new MessagePackSerializerOptions(new EmptyResolver()); - public IMessagePackFormatter GetFormatter() => null; + public IMessagePackFormatter? GetFormatter() => null; } } @@ -127,7 +129,7 @@ public class MyClass [Key(1)] [MessagePackFormatter(typeof(String_x2Formatter))] - public string MyProperty2 { get; set; } + public string? MyProperty2 { get; set; } #pragma warning disable SA1306 // Field names should begin with lower-case letter [Key(2)] @@ -334,7 +336,7 @@ public class Contractless2 { public int MyProperty { get; set; } - public string MyProperty2 { get; set; } + public string? MyProperty2 { get; set; } } public interface IEntity @@ -364,7 +366,7 @@ public Holder(IEntity entity) public abstract class EntityBase { - public string Name { get; } + public string? Name { get; } public EntityBase() { diff --git a/sandbox/MessagePack.Internal/MessagePack.Internal.csproj b/sandbox/MessagePack.Internal/MessagePack.Internal.csproj index 283524803..bc7d5d8d4 100644 --- a/sandbox/MessagePack.Internal/MessagePack.Internal.csproj +++ b/sandbox/MessagePack.Internal/MessagePack.Internal.csproj @@ -1,7 +1,8 @@  - netcoreapp3.1 + net6.0 + enable $(DefineConstants);SPAN_BUILTIN;MESSAGEPACK_INTERNAL true True @@ -14,10 +15,6 @@ - - - - diff --git a/sandbox/PerfBenchmarkDotNet/BenchmarkConfig.cs b/sandbox/PerfBenchmarkDotNet/BenchmarkConfig.cs index 9f29282d0..d07283a16 100644 --- a/sandbox/PerfBenchmarkDotNet/BenchmarkConfig.cs +++ b/sandbox/PerfBenchmarkDotNet/BenchmarkConfig.cs @@ -18,10 +18,10 @@ internal class BenchmarkConfig : ManualConfig { public BenchmarkConfig() { - this.Add(MarkdownExporter.GitHub); - this.Add(MemoryDiagnoser.Default); + this.AddExporter(MarkdownExporter.GitHub); + this.AddDiagnoser(MemoryDiagnoser.Default); - this.Add(Job.ShortRun.With(BenchmarkDotNet.Environments.Platform.X64).WithWarmupCount(1).WithIterationCount(1)); + this.AddJob(Job.ShortRun.WithPlatform(BenchmarkDotNet.Environments.Platform.X64).WithWarmupCount(1).WithIterationCount(1)); ////Add(Job.ShortRun.With(BenchmarkDotNet.Environments.Platform.X64).WithWarmupCount(1).WithIterationCount(1), ////Job.ShortRun.With(BenchmarkDotNet.Environments.Platform.X86).WithWarmupCount(1).WithIterationCount(1)); diff --git a/sandbox/PerfBenchmarkDotNet/GeneratedFormatter.cs b/sandbox/PerfBenchmarkDotNet/GeneratedFormatter.cs index 81f107844..95b69bd53 100644 --- a/sandbox/PerfBenchmarkDotNet/GeneratedFormatter.cs +++ b/sandbox/PerfBenchmarkDotNet/GeneratedFormatter.cs @@ -6,6 +6,7 @@ #pragma warning disable SA1200 // Using directives should be placed correctly #pragma warning disable SA1306 // Field names should begin with lower-case letter #pragma warning disable SA1312 // Variable names should begin with lower-case letter +#pragma warning disable SA1402 // File may only contain a single type #pragma warning disable SA1403 // File may only contain a single namespace #pragma warning disable SA1649 // File name should match first type name @@ -596,4 +597,3 @@ public void Serialize(ref MessagePackWriter writer, global::PerfBenchmarkDotNet. #pragma warning restore SA1200 // Using directives should be placed correctly #pragma warning restore SA1403 // File may only contain a single namespace - diff --git a/sandbox/PerfBenchmarkDotNet/PerfBenchmarkDotNet.csproj b/sandbox/PerfBenchmarkDotNet/PerfBenchmarkDotNet.csproj index 6804253df..4dbab7b25 100644 --- a/sandbox/PerfBenchmarkDotNet/PerfBenchmarkDotNet.csproj +++ b/sandbox/PerfBenchmarkDotNet/PerfBenchmarkDotNet.csproj @@ -1,7 +1,7 @@  Exe - net472;net6.0 + net472;net7.0 true true @@ -13,18 +13,18 @@ - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/sandbox/PerfNetFramework/PerfNetFramework.csproj b/sandbox/PerfNetFramework/PerfNetFramework.csproj index 57cbfd36e..e97936e7e 100644 --- a/sandbox/PerfNetFramework/PerfNetFramework.csproj +++ b/sandbox/PerfNetFramework/PerfNetFramework.csproj @@ -1,15 +1,15 @@  Exe - net472;net6.0 + net472;net7.0 true - - - - - + + + + + diff --git a/sandbox/PerfNetFramework/README.md b/sandbox/PerfNetFramework/README.md index 3c202c2be..f91fa842c 100644 --- a/sandbox/PerfNetFramework/README.md +++ b/sandbox/PerfNetFramework/README.md @@ -10,7 +10,7 @@ When collecting ETL traces, use these settings in the Collect->Run dialog: | Setting | Value | |-------------|-------| -| Command | `dotnet run -c release -p .\sandbox\PerfNetFramework\ -f net461 --no-build` +| Command | `dotnet run -c release -p .\sandbox\PerfNetFramework\ -f net472 --no-build` | Current Dir | `d:\git\messagepack-csharp` (or wherever your enlistment is) | Additional Providers | `*MessagePack-Benchmark` | No V3.X NGen | Checked diff --git a/sandbox/Sandbox/Generated.cs b/sandbox/Sandbox/Generated.cs index bc3bcd7aa..16b349886 100644 --- a/sandbox/Sandbox/Generated.cs +++ b/sandbox/Sandbox/Generated.cs @@ -344,13 +344,13 @@ public void Serialize(ref global::MessagePack.MessagePackWriter writer, global:: switch (keyValuePair.Value) { case 0: - options.Resolver.GetFormatterWithVerify().Serialize(ref writer, (global::TextMessageBody)value, options); + global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Serialize(ref writer, (global::TextMessageBody)value, options); break; case 1: - options.Resolver.GetFormatterWithVerify().Serialize(ref writer, (global::StampMessageBody)value, options); + global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Serialize(ref writer, (global::StampMessageBody)value, options); break; case 2: - options.Resolver.GetFormatterWithVerify().Serialize(ref writer, (global::QuestMessageBody)value, options); + global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Serialize(ref writer, (global::QuestMessageBody)value, options); break; default: break; @@ -386,13 +386,13 @@ public void Serialize(ref global::MessagePack.MessagePackWriter writer, global:: switch (key) { case 0: - result = (global::IMessageBody)options.Resolver.GetFormatterWithVerify().Deserialize(ref reader, options); + result = (global::IMessageBody)global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Deserialize(ref reader, options); break; case 1: - result = (global::IMessageBody)options.Resolver.GetFormatterWithVerify().Deserialize(ref reader, options); + result = (global::IMessageBody)global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Deserialize(ref reader, options); break; case 2: - result = (global::IMessageBody)options.Resolver.GetFormatterWithVerify().Deserialize(ref reader, options); + result = (global::IMessageBody)global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Deserialize(ref reader, options); break; default: reader.Skip(); @@ -457,7 +457,7 @@ public void Serialize(ref global::MessagePack.MessagePackWriter writer, global:: switch (keyValuePair.Value) { case 0: - options.Resolver.GetFormatterWithVerify().Serialize(ref writer, (global::SharedData.MySubUnion1)value, options); + global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Serialize(ref writer, (global::SharedData.MySubUnion1)value, options); break; default: break; @@ -493,7 +493,7 @@ public void Serialize(ref global::MessagePack.MessagePackWriter writer, global:: switch (key) { case 0: - result = (global::SharedData.IIVersioningUnion)options.Resolver.GetFormatterWithVerify().Deserialize(ref reader, options); + result = (global::SharedData.IIVersioningUnion)global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Deserialize(ref reader, options); break; default: reader.Skip(); @@ -538,16 +538,16 @@ public void Serialize(ref global::MessagePack.MessagePackWriter writer, global:: switch (keyValuePair.Value) { case 0: - options.Resolver.GetFormatterWithVerify().Serialize(ref writer, (global::SharedData.MySubUnion1)value, options); + global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Serialize(ref writer, (global::SharedData.MySubUnion1)value, options); break; case 1: - options.Resolver.GetFormatterWithVerify().Serialize(ref writer, (global::SharedData.MySubUnion2)value, options); + global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Serialize(ref writer, (global::SharedData.MySubUnion2)value, options); break; case 2: - options.Resolver.GetFormatterWithVerify().Serialize(ref writer, (global::SharedData.MySubUnion3)value, options); + global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Serialize(ref writer, (global::SharedData.MySubUnion3)value, options); break; case 3: - options.Resolver.GetFormatterWithVerify().Serialize(ref writer, (global::SharedData.MySubUnion4)value, options); + global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Serialize(ref writer, (global::SharedData.MySubUnion4)value, options); break; default: break; @@ -583,16 +583,16 @@ public void Serialize(ref global::MessagePack.MessagePackWriter writer, global:: switch (key) { case 0: - result = (global::SharedData.IUnionChecker)options.Resolver.GetFormatterWithVerify().Deserialize(ref reader, options); + result = (global::SharedData.IUnionChecker)global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Deserialize(ref reader, options); break; case 1: - result = (global::SharedData.IUnionChecker)options.Resolver.GetFormatterWithVerify().Deserialize(ref reader, options); + result = (global::SharedData.IUnionChecker)global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Deserialize(ref reader, options); break; case 2: - result = (global::SharedData.IUnionChecker)options.Resolver.GetFormatterWithVerify().Deserialize(ref reader, options); + result = (global::SharedData.IUnionChecker)global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Deserialize(ref reader, options); break; case 3: - result = (global::SharedData.IUnionChecker)options.Resolver.GetFormatterWithVerify().Deserialize(ref reader, options); + result = (global::SharedData.IUnionChecker)global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Deserialize(ref reader, options); break; default: reader.Skip(); @@ -637,16 +637,16 @@ public void Serialize(ref global::MessagePack.MessagePackWriter writer, global:: switch (keyValuePair.Value) { case 0: - options.Resolver.GetFormatterWithVerify().Serialize(ref writer, (global::SharedData.MySubUnion2)value, options); + global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Serialize(ref writer, (global::SharedData.MySubUnion2)value, options); break; case 1: - options.Resolver.GetFormatterWithVerify().Serialize(ref writer, (global::SharedData.MySubUnion3)value, options); + global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Serialize(ref writer, (global::SharedData.MySubUnion3)value, options); break; case 2: - options.Resolver.GetFormatterWithVerify().Serialize(ref writer, (global::SharedData.MySubUnion4)value, options); + global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Serialize(ref writer, (global::SharedData.MySubUnion4)value, options); break; case 3: - options.Resolver.GetFormatterWithVerify().Serialize(ref writer, (global::SharedData.MySubUnion1)value, options); + global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Serialize(ref writer, (global::SharedData.MySubUnion1)value, options); break; default: break; @@ -682,16 +682,16 @@ public void Serialize(ref global::MessagePack.MessagePackWriter writer, global:: switch (key) { case 0: - result = (global::SharedData.IUnionChecker2)options.Resolver.GetFormatterWithVerify().Deserialize(ref reader, options); + result = (global::SharedData.IUnionChecker2)global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Deserialize(ref reader, options); break; case 1: - result = (global::SharedData.IUnionChecker2)options.Resolver.GetFormatterWithVerify().Deserialize(ref reader, options); + result = (global::SharedData.IUnionChecker2)global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Deserialize(ref reader, options); break; case 2: - result = (global::SharedData.IUnionChecker2)options.Resolver.GetFormatterWithVerify().Deserialize(ref reader, options); + result = (global::SharedData.IUnionChecker2)global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Deserialize(ref reader, options); break; case 3: - result = (global::SharedData.IUnionChecker2)options.Resolver.GetFormatterWithVerify().Deserialize(ref reader, options); + result = (global::SharedData.IUnionChecker2)global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Deserialize(ref reader, options); break; default: reader.Skip(); @@ -732,10 +732,10 @@ public void Serialize(ref global::MessagePack.MessagePackWriter writer, global:: switch (keyValuePair.Value) { case 0: - options.Resolver.GetFormatterWithVerify().Serialize(ref writer, (global::SharedData.FooClass)value, options); + global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Serialize(ref writer, (global::SharedData.FooClass)value, options); break; case 1: - options.Resolver.GetFormatterWithVerify().Serialize(ref writer, (global::SharedData.BarClass)value, options); + global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Serialize(ref writer, (global::SharedData.BarClass)value, options); break; default: break; @@ -771,10 +771,10 @@ public void Serialize(ref global::MessagePack.MessagePackWriter writer, global:: switch (key) { case 0: - result = (global::SharedData.IUnionSample)options.Resolver.GetFormatterWithVerify().Deserialize(ref reader, options); + result = (global::SharedData.IUnionSample)global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Deserialize(ref reader, options); break; case 1: - result = (global::SharedData.IUnionSample)options.Resolver.GetFormatterWithVerify().Deserialize(ref reader, options); + result = (global::SharedData.IUnionSample)global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Deserialize(ref reader, options); break; default: reader.Skip(); @@ -815,10 +815,10 @@ public void Serialize(ref global::MessagePack.MessagePackWriter writer, global:: switch (keyValuePair.Value) { case 0: - options.Resolver.GetFormatterWithVerify().Serialize(ref writer, (global::SharedData.SubUnionType1)value, options); + global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Serialize(ref writer, (global::SharedData.SubUnionType1)value, options); break; case 1: - options.Resolver.GetFormatterWithVerify().Serialize(ref writer, (global::SharedData.SubUnionType2)value, options); + global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Serialize(ref writer, (global::SharedData.SubUnionType2)value, options); break; default: break; @@ -854,10 +854,10 @@ public void Serialize(ref global::MessagePack.MessagePackWriter writer, global:: switch (key) { case 0: - result = (global::SharedData.RootUnionType)options.Resolver.GetFormatterWithVerify().Deserialize(ref reader, options); + result = (global::SharedData.RootUnionType)global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Deserialize(ref reader, options); break; case 1: - result = (global::SharedData.RootUnionType)options.Resolver.GetFormatterWithVerify().Deserialize(ref reader, options); + result = (global::SharedData.RootUnionType)global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Deserialize(ref reader, options); break; default: reader.Skip(); @@ -3093,6 +3093,53 @@ public void Serialize(ref global::MessagePack.MessagePackWriter writer, global:: } } + public sealed class SimpleGenericDataFormatter : global::MessagePack.Formatters.IMessagePackFormatter> + { + + public void Serialize(ref global::MessagePack.MessagePackWriter writer, global::SharedData.SimpleGenericData value, global::MessagePack.MessagePackSerializerOptions options) + { + if (value == null) + { + writer.WriteNil(); + return; + } + + global::MessagePack.IFormatterResolver formatterResolver = options.Resolver; + writer.WriteArrayHeader(1); + global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(formatterResolver).Serialize(ref writer, value.Value, options); + } + + public global::SharedData.SimpleGenericData Deserialize(ref global::MessagePack.MessagePackReader reader, global::MessagePack.MessagePackSerializerOptions options) + { + if (reader.TryReadNil()) + { + return null; + } + + options.Security.DepthStep(ref reader); + global::MessagePack.IFormatterResolver formatterResolver = options.Resolver; + var length = reader.ReadArrayHeader(); + var __Value__ = default(T); + + for (int i = 0; i < length; i++) + { + switch (i) + { + case 0: + __Value__ = global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(formatterResolver).Deserialize(ref reader, options); + break; + default: + reader.Skip(); + break; + } + } + + var ____result = new global::SharedData.SimpleGenericData(__Value__); + reader.Depth--; + return ____result; + } + } + public sealed class SimpleIntKeyDataFormatter : global::MessagePack.Formatters.IMessagePackFormatter { diff --git a/sandbox/Sandbox/Program.cs b/sandbox/Sandbox/Program.cs index d920a907b..9d9ce7e03 100644 --- a/sandbox/Sandbox/Program.cs +++ b/sandbox/Sandbox/Program.cs @@ -24,6 +24,7 @@ #pragma warning disable SA1307 // Accessible fields should begin with upper-case letter #pragma warning disable SA1401 // Fields should be private +#pragma warning disable SA1402 // File may only contain a single type #pragma warning disable SA1649 // File name should match first type name namespace Sandbox @@ -206,7 +207,7 @@ public struct Point [Key(1)] public readonly int Y; - // can't find matched constructor parameter, parameterType mismatch. type:Point parameterIndex:0 paramterType:ValueTuple`2 + // can't find matched constructor parameter, parameterType mismatch. type:Point parameterIndex:0 parameterType:ValueTuple`2 public Point((int, int) p) { this.X = p.Item1; diff --git a/sandbox/Sandbox/Sandbox.csproj b/sandbox/Sandbox/Sandbox.csproj index 17da2db30..5ab57a0ce 100644 --- a/sandbox/Sandbox/Sandbox.csproj +++ b/sandbox/Sandbox/Sandbox.csproj @@ -2,16 +2,16 @@ Exe - netcoreapp3.1 + net6.0 True - - - - - + + + + + diff --git a/sandbox/Sandbox/codegen.ps1 b/sandbox/Sandbox/codegen.ps1 index 279718f6f..d52201290 100644 --- a/sandbox/Sandbox/codegen.ps1 +++ b/sandbox/Sandbox/codegen.ps1 @@ -1 +1 @@ -dotnet run -f "net6.0" --project "$PSScriptRoot/../../src/MessagePack.Generator/MessagePack.Generator.csproj" -- -i "$PSScriptRoot/../SharedData/SharedData.csproj" -o "$PSScriptRoot/Generated.cs" +dotnet run -f net8.0 --project "$PSScriptRoot/../../src/MessagePack.Generator/MessagePack.Generator.csproj" -- -i "$PSScriptRoot/../SharedData/SharedData.csproj" -o "$PSScriptRoot/Generated.cs" diff --git a/sandbox/TestData.InvalidProject/TestData.InvalidProject.csproj b/sandbox/TestData.InvalidProject/TestData.InvalidProject.csproj index 3743f78a0..3b149c7b3 100644 --- a/sandbox/TestData.InvalidProject/TestData.InvalidProject.csproj +++ b/sandbox/TestData.InvalidProject/TestData.InvalidProject.csproj @@ -1,11 +1,11 @@  - net461 + net462 - - - + + + diff --git a/sandbox/TestData.InvalidSyntax/TestData.InvalidSyntax.csproj b/sandbox/TestData.InvalidSyntax/TestData.InvalidSyntax.csproj index d992280d8..26e2e25dc 100644 --- a/sandbox/TestData.InvalidSyntax/TestData.InvalidSyntax.csproj +++ b/sandbox/TestData.InvalidSyntax/TestData.InvalidSyntax.csproj @@ -1,10 +1,10 @@  - net461 + net462 - - - + + + diff --git a/sandbox/TestData2/Generated.cs b/sandbox/TestData2/Generated.cs index 1e65486dc..ac86ae02f 100644 --- a/sandbox/TestData2/Generated.cs +++ b/sandbox/TestData2/Generated.cs @@ -47,22 +47,28 @@ internal static class GeneratedResolverGetFormatterHelper static GeneratedResolverGetFormatterHelper() { - lookup = new global::System.Collections.Generic.Dictionary(14) - { - { typeof(global::System.Collections.Generic.List), 0 }, - { typeof(global::System.Collections.Generic.List), 1 }, - { typeof(global::TestData2.Nest1.Id), 2 }, - { typeof(global::TestData2.Nest2.Id), 3 }, - { typeof(global::TestData2.A), 4 }, - { typeof(global::TestData2.B), 5 }, - { typeof(global::TestData2.C), 6 }, - { typeof(global::TestData2.Nest1), 7 }, - { typeof(global::TestData2.Nest1.IdType), 8 }, - { typeof(global::TestData2.Nest2), 9 }, - { typeof(global::TestData2.Nest2.IdType), 10 }, - { typeof(global::TestData2.PropNameCheck1), 11 }, - { typeof(global::TestData2.PropNameCheck2), 12 }, - { typeof(global::TestData2.Record), 13 }, + lookup = new global::System.Collections.Generic.Dictionary(20) + { + { typeof(global::System.Collections.Generic.List), 0 }, + { typeof(global::System.Collections.Generic.List), 1 }, + { typeof(global::System.Collections.Generic.List), 2 }, + { typeof(global::System.Collections.Generic.List>), 3 }, + { typeof(global::TestData2.NestedGenericTestA), 4 }, + { typeof(global::TestData2.NestedGenericTestB), 5 }, + { typeof(global::TestData2.Nest1.Id), 6 }, + { typeof(global::TestData2.Nest2.Id), 7 }, + { typeof(global::TestData2.A), 8 }, + { typeof(global::TestData2.B), 9 }, + { typeof(global::TestData2.C), 10 }, + { typeof(global::TestData2.Nest1), 11 }, + { typeof(global::TestData2.Nest1.IdType), 12 }, + { typeof(global::TestData2.Nest2), 13 }, + { typeof(global::TestData2.Nest2.IdType), 14 }, + { typeof(global::TestData2.NestedGenericTestC), 15 }, + { typeof(global::TestData2.NullableTest), 16 }, + { typeof(global::TestData2.PropNameCheck1), 17 }, + { typeof(global::TestData2.PropNameCheck2), 18 }, + { typeof(global::TestData2.Record), 19 }, }; } @@ -76,20 +82,26 @@ internal static object GetFormatter(global::System.Type t) switch (key) { - case 0: return new global::MessagePack.Formatters.ListFormatter(); - case 1: return new global::MessagePack.Formatters.ListFormatter(); - case 2: return new MessagePack.Formatters.TestData2.Nest1_IdFormatter(); - case 3: return new MessagePack.Formatters.TestData2.Nest2_IdFormatter(); - case 4: return new MessagePack.Formatters.TestData2.AFormatter(); - case 5: return new MessagePack.Formatters.TestData2.BFormatter(); - case 6: return new MessagePack.Formatters.TestData2.CFormatter(); - case 7: return new MessagePack.Formatters.TestData2.Nest1Formatter(); - case 8: return new MessagePack.Formatters.TestData2.Nest1_IdTypeFormatter(); - case 9: return new MessagePack.Formatters.TestData2.Nest2Formatter(); - case 10: return new MessagePack.Formatters.TestData2.Nest2_IdTypeFormatter(); - case 11: return new MessagePack.Formatters.TestData2.PropNameCheck1Formatter(); - case 12: return new MessagePack.Formatters.TestData2.PropNameCheck2Formatter(); - case 13: return new MessagePack.Formatters.TestData2.RecordFormatter(); + case 0: return new global::MessagePack.Formatters.ListFormatter(); + case 1: return new global::MessagePack.Formatters.ListFormatter(); + case 2: return new global::MessagePack.Formatters.ListFormatter(); + case 3: return new global::MessagePack.Formatters.ListFormatter>(); + case 4: return new MessagePack.Formatters.TestData2.NestedGenericTestAFormatter(); + case 5: return new MessagePack.Formatters.TestData2.NestedGenericTestBFormatter(); + case 6: return new MessagePack.Formatters.TestData2.Nest1_IdFormatter(); + case 7: return new MessagePack.Formatters.TestData2.Nest2_IdFormatter(); + case 8: return new MessagePack.Formatters.TestData2.AFormatter(); + case 9: return new MessagePack.Formatters.TestData2.BFormatter(); + case 10: return new MessagePack.Formatters.TestData2.CFormatter(); + case 11: return new MessagePack.Formatters.TestData2.Nest1Formatter(); + case 12: return new MessagePack.Formatters.TestData2.Nest1_IdTypeFormatter(); + case 13: return new MessagePack.Formatters.TestData2.Nest2Formatter(); + case 14: return new MessagePack.Formatters.TestData2.Nest2_IdTypeFormatter(); + case 15: return new MessagePack.Formatters.TestData2.NestedGenericTestCFormatter(); + case 16: return new MessagePack.Formatters.TestData2.NullableTestFormatter(); + case 17: return new MessagePack.Formatters.TestData2.PropNameCheck1Formatter(); + case 18: return new MessagePack.Formatters.TestData2.PropNameCheck2Formatter(); + case 19: return new MessagePack.Formatters.TestData2.RecordFormatter(); default: return null; } } @@ -564,6 +576,232 @@ public void Serialize(ref global::MessagePack.MessagePackWriter writer, global:: } } + public sealed class NestedGenericTestAFormatter : global::MessagePack.Formatters.IMessagePackFormatter> + { + // Field + private static global::System.ReadOnlySpan GetSpan_Field() => new byte[1 + 5] { 165, 70, 105, 101, 108, 100 }; + + public void Serialize(ref global::MessagePack.MessagePackWriter writer, global::TestData2.NestedGenericTestA value, global::MessagePack.MessagePackSerializerOptions options) + { + if (value is null) + { + writer.WriteNil(); + return; + } + + var formatterResolver = options.Resolver; + writer.WriteMapHeader(1); + writer.WriteRaw(GetSpan_Field()); + global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(formatterResolver).Serialize(ref writer, value.Field, options); + } + + public global::TestData2.NestedGenericTestA Deserialize(ref global::MessagePack.MessagePackReader reader, global::MessagePack.MessagePackSerializerOptions options) + { + if (reader.TryReadNil()) + { + return null; + } + + options.Security.DepthStep(ref reader); + var formatterResolver = options.Resolver; + var length = reader.ReadMapHeader(); + var ____result = new global::TestData2.NestedGenericTestA(); + + for (int i = 0; i < length; i++) + { + var stringKey = global::MessagePack.Internal.CodeGenHelpers.ReadStringSpan(ref reader); + switch (stringKey.Length) + { + default: + FAIL: + reader.Skip(); + continue; + case 5: + if (global::MessagePack.Internal.AutomataKeyGen.GetKey(ref stringKey) != 431315315014UL) { goto FAIL; } + + ____result.Field = global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(formatterResolver).Deserialize(ref reader, options); + continue; + + } + } + + reader.Depth--; + return ____result; + } + } + + public sealed class NestedGenericTestBFormatter : global::MessagePack.Formatters.IMessagePackFormatter> + { + // Field + private static global::System.ReadOnlySpan GetSpan_Field() => new byte[1 + 5] { 165, 70, 105, 101, 108, 100 }; + + public void Serialize(ref global::MessagePack.MessagePackWriter writer, global::TestData2.NestedGenericTestB value, global::MessagePack.MessagePackSerializerOptions options) + { + if (value is null) + { + writer.WriteNil(); + return; + } + + var formatterResolver = options.Resolver; + writer.WriteMapHeader(1); + writer.WriteRaw(GetSpan_Field()); + global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify>>(formatterResolver).Serialize(ref writer, value.Field, options); + } + + public global::TestData2.NestedGenericTestB Deserialize(ref global::MessagePack.MessagePackReader reader, global::MessagePack.MessagePackSerializerOptions options) + { + if (reader.TryReadNil()) + { + return null; + } + + options.Security.DepthStep(ref reader); + var formatterResolver = options.Resolver; + var length = reader.ReadMapHeader(); + var ____result = new global::TestData2.NestedGenericTestB(); + + for (int i = 0; i < length; i++) + { + var stringKey = global::MessagePack.Internal.CodeGenHelpers.ReadStringSpan(ref reader); + switch (stringKey.Length) + { + default: + FAIL: + reader.Skip(); + continue; + case 5: + if (global::MessagePack.Internal.AutomataKeyGen.GetKey(ref stringKey) != 431315315014UL) { goto FAIL; } + + ____result.Field = global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify>>(formatterResolver).Deserialize(ref reader, options); + continue; + + } + } + + reader.Depth--; + return ____result; + } + } + + public sealed class NestedGenericTestCFormatter : global::MessagePack.Formatters.IMessagePackFormatter + { + // Field + private static global::System.ReadOnlySpan GetSpan_Field() => new byte[1 + 5] { 165, 70, 105, 101, 108, 100 }; + + public void Serialize(ref global::MessagePack.MessagePackWriter writer, global::TestData2.NestedGenericTestC value, global::MessagePack.MessagePackSerializerOptions options) + { + if (value is null) + { + writer.WriteNil(); + return; + } + + var formatterResolver = options.Resolver; + writer.WriteMapHeader(1); + writer.WriteRaw(GetSpan_Field()); + global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify>(formatterResolver).Serialize(ref writer, value.Field, options); + } + + public global::TestData2.NestedGenericTestC Deserialize(ref global::MessagePack.MessagePackReader reader, global::MessagePack.MessagePackSerializerOptions options) + { + if (reader.TryReadNil()) + { + return null; + } + + options.Security.DepthStep(ref reader); + var formatterResolver = options.Resolver; + var length = reader.ReadMapHeader(); + var ____result = new global::TestData2.NestedGenericTestC(); + + for (int i = 0; i < length; i++) + { + var stringKey = global::MessagePack.Internal.CodeGenHelpers.ReadStringSpan(ref reader); + switch (stringKey.Length) + { + default: + FAIL: + reader.Skip(); + continue; + case 5: + if (global::MessagePack.Internal.AutomataKeyGen.GetKey(ref stringKey) != 431315315014UL) { goto FAIL; } + + ____result.Field = global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify>(formatterResolver).Deserialize(ref reader, options); + continue; + + } + } + + reader.Depth--; + return ____result; + } + } + + public sealed class NullableTestFormatter : global::MessagePack.Formatters.IMessagePackFormatter + { + // a + private static global::System.ReadOnlySpan GetSpan_a() => new byte[1 + 1] { 161, 97 }; + // b + private static global::System.ReadOnlySpan GetSpan_b() => new byte[1 + 1] { 161, 98 }; + + public void Serialize(ref global::MessagePack.MessagePackWriter writer, global::TestData2.NullableTest value, global::MessagePack.MessagePackSerializerOptions options) + { + if (value is null) + { + writer.WriteNil(); + return; + } + + var formatterResolver = options.Resolver; + writer.WriteMapHeader(2); + writer.WriteRaw(GetSpan_a()); + global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(formatterResolver).Serialize(ref writer, value.a, options); + writer.WriteRaw(GetSpan_b()); + global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify>(formatterResolver).Serialize(ref writer, value.b, options); + } + + public global::TestData2.NullableTest Deserialize(ref global::MessagePack.MessagePackReader reader, global::MessagePack.MessagePackSerializerOptions options) + { + if (reader.TryReadNil()) + { + return null; + } + + options.Security.DepthStep(ref reader); + var formatterResolver = options.Resolver; + var length = reader.ReadMapHeader(); + var ____result = new global::TestData2.NullableTest(); + + for (int i = 0; i < length; i++) + { + var stringKey = global::MessagePack.Internal.CodeGenHelpers.ReadStringSpan(ref reader); + switch (stringKey.Length) + { + default: + FAIL: + reader.Skip(); + continue; + case 1: + switch (global::MessagePack.Internal.AutomataKeyGen.GetKey(ref stringKey)) + { + default: goto FAIL; + case 97UL: + ____result.a = global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(formatterResolver).Deserialize(ref reader, options); + continue; + case 98UL: + ____result.b = global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify>(formatterResolver).Deserialize(ref reader, options); + continue; + } + + } + } + + reader.Depth--; + return ____result; + } + } + public sealed class PropNameCheck1Formatter : global::MessagePack.Formatters.IMessagePackFormatter { // MyProperty1 diff --git a/sandbox/TestData2/NestedGenerics.cs b/sandbox/TestData2/NestedGenerics.cs new file mode 100644 index 000000000..a3e9f549d --- /dev/null +++ b/sandbox/TestData2/NestedGenerics.cs @@ -0,0 +1,28 @@ +// Copyright (c) All contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#pragma warning disable SA1307 // Accessible fields should begin with upper-case letter +#pragma warning disable SA1401 // Fields should be private +#pragma warning disable SA1402 // File may only contain a single type +#pragma warning disable SA1649 // File name should match first type name + +namespace TestData2 +{ + [MessagePackObject(true)] + public class NestedGenericTestA + { + public T Field { get; set; } + } + + [MessagePackObject(true)] + public class NestedGenericTestB + { + public List> Field { get; set; } + } + + [MessagePackObject(true)] + public class NestedGenericTestC + { + public NestedGenericTestB Field { get; set; } + } +} diff --git a/sandbox/TestData2/NestedTypeCheck.cs b/sandbox/TestData2/NestedTypeCheck.cs index 3573fb331..59a6f4745 100644 --- a/sandbox/TestData2/NestedTypeCheck.cs +++ b/sandbox/TestData2/NestedTypeCheck.cs @@ -5,6 +5,7 @@ #pragma warning disable SA1307 // Accessible fields should begin with upper-case letter #pragma warning disable SA1401 // Fields should be private +#pragma warning disable SA1402 // File may only contain a single type #pragma warning disable SA1649 // File name should match first type name namespace TestData2 diff --git a/sandbox/TestData2/NullableTest.cs b/sandbox/TestData2/NullableTest.cs new file mode 100644 index 000000000..64edfbf2f --- /dev/null +++ b/sandbox/TestData2/NullableTest.cs @@ -0,0 +1,17 @@ +// Copyright (c) All contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#pragma warning disable SA1307 // Accessible fields should begin with upper-case letter +#pragma warning disable SA1401 // Fields should be private +#pragma warning disable SA1649 // File name should match first type name + +#nullable enable + +namespace TestData2; + +[MessagePackObject(true)] +public class NullableTest +{ + public int[]? a; + public List b = null!; +} diff --git a/sandbox/TestData2/TestData2.csproj b/sandbox/TestData2/TestData2.csproj index a73130494..3d0134229 100644 --- a/sandbox/TestData2/TestData2.csproj +++ b/sandbox/TestData2/TestData2.csproj @@ -1,13 +1,13 @@  - net461 - 10 + net462 - - - - + + + + + diff --git a/sandbox/TestData2/codegen.ps1 b/sandbox/TestData2/codegen.ps1 index 6781688ed..b964af899 100644 --- a/sandbox/TestData2/codegen.ps1 +++ b/sandbox/TestData2/codegen.ps1 @@ -1 +1 @@ -dotnet run -f "net6.0" --project "$PSScriptRoot/../../src/MessagePack.Generator/MessagePack.Generator.csproj" -- -i "$PSScriptRoot/TestData2.csproj" -o "$PSScriptRoot/Generated.cs" +dotnet run -f net8.0 --project "$PSScriptRoot/../../src/MessagePack.Generator/MessagePack.Generator.csproj" -- -i "$PSScriptRoot/TestData2.csproj" -o "$PSScriptRoot/Generated.cs" diff --git a/src/.editorconfig b/src/.editorconfig new file mode 100644 index 000000000..e69de29bb diff --git a/src/AssemblyInfo.cs b/src/AssemblyInfo.cs new file mode 100644 index 000000000..e083c6a92 --- /dev/null +++ b/src/AssemblyInfo.cs @@ -0,0 +1,6 @@ +// Copyright (c) All contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Runtime.InteropServices; + +[assembly: DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] diff --git a/src/Directory.Build.props b/src/Directory.Build.props new file mode 100644 index 000000000..199d8a013 --- /dev/null +++ b/src/Directory.Build.props @@ -0,0 +1,8 @@ + + + enable + True + $(MSBuildThisFileDirectory)..\opensource.snk + + + diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets index c983d0826..9af35cb00 100644 --- a/src/Directory.Build.targets +++ b/src/Directory.Build.targets @@ -1,12 +1,11 @@ - - - True - $(MSBuildThisFileDirectory)..\opensource.snk - - + + + + + diff --git a/src/MessagePack.Annotations/MessagePack.Annotations.csproj b/src/MessagePack.Annotations/MessagePack.Annotations.csproj index 7561506dc..48e95271a 100644 --- a/src/MessagePack.Annotations/MessagePack.Annotations.csproj +++ b/src/MessagePack.Annotations/MessagePack.Annotations.csproj @@ -5,7 +5,6 @@ $(NoWarn);CS0649 MessagePack - true MessagePack annotations for C# Attributes and interfaces for .NET types serializable with MessagePack. MsgPack;MessagePack;Serialization;Formatter;Serializer;Unity;Xamarin diff --git a/src/MessagePack.AspNetCoreMvcFormatter/MessagePack.AspNetCoreMvcFormatter.csproj b/src/MessagePack.AspNetCoreMvcFormatter/MessagePack.AspNetCoreMvcFormatter.csproj index d68ddacda..6203d8a90 100644 --- a/src/MessagePack.AspNetCoreMvcFormatter/MessagePack.AspNetCoreMvcFormatter.csproj +++ b/src/MessagePack.AspNetCoreMvcFormatter/MessagePack.AspNetCoreMvcFormatter.csproj @@ -1,21 +1,20 @@  - netstandard2.0;netcoreapp3.1 - true + netstandard2.0;net6.0 ASP.NET Core MVC Input/Output MessagePack formatter ASP.NET Core MVC Input/Output MessagePack formatter. MsgPack;MessagePack;Serialization;Formatter;Serializer;aspnetcore;aspnetcoremvc - + - + @@ -23,4 +22,4 @@ - \ No newline at end of file + diff --git a/src/MessagePack.AspNetCoreMvcFormatter/MessagePackInputFormatter.cs b/src/MessagePack.AspNetCoreMvcFormatter/MessagePackInputFormatter.cs index 59c49a998..f84b4bf7a 100644 --- a/src/MessagePack.AspNetCoreMvcFormatter/MessagePackInputFormatter.cs +++ b/src/MessagePack.AspNetCoreMvcFormatter/MessagePackInputFormatter.cs @@ -9,16 +9,17 @@ namespace MessagePack.AspNetCoreMvcFormatter public class MessagePackInputFormatter : InputFormatter { private const string ContentType = "application/x-msgpack"; + private static readonly MessagePackSerializerOptions DefaultOptions = MessagePackSerializerOptions.Standard.WithSecurity(MessagePackSecurity.UntrustedData); private readonly MessagePackSerializerOptions options; public MessagePackInputFormatter() - : this(null) + : this(DefaultOptions) { } - public MessagePackInputFormatter(MessagePackSerializerOptions options) + public MessagePackInputFormatter(MessagePackSerializerOptions? options) { - this.options = options; + this.options = options ?? DefaultOptions; SupportedMediaTypes.Add(ContentType); } diff --git a/src/MessagePack.AspNetCoreMvcFormatter/MessagePackOutputFormatter.cs b/src/MessagePack.AspNetCoreMvcFormatter/MessagePackOutputFormatter.cs index 409981148..fa0f65671 100644 --- a/src/MessagePack.AspNetCoreMvcFormatter/MessagePackOutputFormatter.cs +++ b/src/MessagePack.AspNetCoreMvcFormatter/MessagePackOutputFormatter.cs @@ -9,14 +9,14 @@ namespace MessagePack.AspNetCoreMvcFormatter public class MessagePackOutputFormatter : OutputFormatter { private const string ContentType = "application/x-msgpack"; - private readonly MessagePackSerializerOptions options; + private readonly MessagePackSerializerOptions? options; public MessagePackOutputFormatter() : this(null) { } - public MessagePackOutputFormatter(MessagePackSerializerOptions options) + public MessagePackOutputFormatter(MessagePackSerializerOptions? options) { this.options = options; @@ -25,55 +25,38 @@ public MessagePackOutputFormatter(MessagePackSerializerOptions options) public override Task WriteResponseBodyAsync(OutputFormatterWriteContext context) { - if (context.ObjectType == typeof(object)) + if (context.Object == null) { - if (context.Object == null) - { #if NETSTANDARD2_0 + context.HttpContext.Response.Body.WriteByte(MessagePackCode.Nil); + return Task.CompletedTask; +#else + var writer = context.HttpContext.Response.BodyWriter; + if (writer == null) + { context.HttpContext.Response.Body.WriteByte(MessagePackCode.Nil); return Task.CompletedTask; -#else - var writer = context.HttpContext.Response.BodyWriter; - if (writer == null) - { - context.HttpContext.Response.Body.WriteByte(MessagePackCode.Nil); - return Task.CompletedTask; - } - - var span = writer.GetSpan(1); - span[0] = MessagePackCode.Nil; - writer.Advance(1); - return writer.FlushAsync().AsTask(); -#endif } - else - { -#if NETSTANDARD2_0 - return MessagePackSerializer.SerializeAsync(context.Object.GetType(), context.HttpContext.Response.Body, context.Object, this.options, context.HttpContext.RequestAborted); -#else - var writer = context.HttpContext.Response.BodyWriter; - if (writer == null) - { - return MessagePackSerializer.SerializeAsync(context.Object.GetType(), context.HttpContext.Response.Body, context.Object, this.options, context.HttpContext.RequestAborted); - } - MessagePackSerializer.Serialize(context.Object.GetType(), writer, context.Object, this.options, context.HttpContext.RequestAborted); - return writer.FlushAsync().AsTask(); + var span = writer.GetSpan(1); + span[0] = MessagePackCode.Nil; + writer.Advance(1); + return writer.FlushAsync().AsTask(); #endif - } } else { + var objectType = context.ObjectType == null || context.ObjectType == typeof(object) ? context.Object.GetType() : context.ObjectType; #if NETSTANDARD2_0 - return MessagePackSerializer.SerializeAsync(context.ObjectType, context.HttpContext.Response.Body, context.Object, this.options, context.HttpContext.RequestAborted); + return MessagePackSerializer.SerializeAsync(objectType, context.HttpContext.Response.Body, context.Object, this.options, context.HttpContext.RequestAborted); #else var writer = context.HttpContext.Response.BodyWriter; if (writer == null) { - return MessagePackSerializer.SerializeAsync(context.ObjectType, context.HttpContext.Response.Body, context.Object, this.options, context.HttpContext.RequestAborted); + return MessagePackSerializer.SerializeAsync(objectType, context.HttpContext.Response.Body, context.Object, this.options, context.HttpContext.RequestAborted); } - MessagePackSerializer.Serialize(context.ObjectType, writer, context.Object, this.options, context.HttpContext.RequestAborted); + MessagePackSerializer.Serialize(objectType, writer, context.Object, this.options, context.HttpContext.RequestAborted); return writer.FlushAsync().AsTask(); #endif } diff --git a/src/MessagePack.Experimental/HardwareIntrinsics/FormatterHelpers.cs b/src/MessagePack.Experimental/HardwareIntrinsics/FormatterHelpers.cs index ab013605b..cfa117d3d 100644 --- a/src/MessagePack.Experimental/HardwareIntrinsics/FormatterHelpers.cs +++ b/src/MessagePack.Experimental/HardwareIntrinsics/FormatterHelpers.cs @@ -109,6 +109,7 @@ What is the `shuffle`? using System; +#pragma warning disable SA1402 // File may only contain a single type #pragma warning disable SA1649 // File name should match first type name #pragma warning disable CS0436 // The same name of the primary package diff --git a/src/MessagePack.Experimental/HardwareIntrinsics/FormatterHelpers.tt b/src/MessagePack.Experimental/HardwareIntrinsics/FormatterHelpers.tt index f7767c9db..3ef9693b9 100644 --- a/src/MessagePack.Experimental/HardwareIntrinsics/FormatterHelpers.tt +++ b/src/MessagePack.Experimental/HardwareIntrinsics/FormatterHelpers.tt @@ -127,6 +127,7 @@ int range;#> using System; +#pragma warning disable SA1402 // File may only contain a single type #pragma warning disable SA1649 // File name should match first type name #pragma warning disable CS0436 // The same name of the primary package diff --git a/src/MessagePack.Experimental/HardwareIntrinsics/Formatters.cs b/src/MessagePack.Experimental/HardwareIntrinsics/Formatters.cs index ce7342f45..34b7a143f 100644 --- a/src/MessagePack.Experimental/HardwareIntrinsics/Formatters.cs +++ b/src/MessagePack.Experimental/HardwareIntrinsics/Formatters.cs @@ -6,6 +6,7 @@ using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.X86; +#pragma warning disable SA1402 // File may only contain a single type #pragma warning disable SA1649 // File name should match first type name namespace MessagePack.Formatters @@ -36,6 +37,7 @@ public unsafe void Serialize(ref MessagePackWriter writer, sbyte[]? value, Messa { const int ShiftCount = 4; const int Stride = 1 << ShiftCount; + // We enter the SIMD mode when there are more than the Stride after alignment adjustment. if (inputLength < Stride << 1) { @@ -148,6 +150,7 @@ public unsafe void Serialize(ref MessagePackWriter writer, short[]? value, Messa { // Make InputIterator Aligned var offset = UnsafeMemoryAlignmentUtility.CalculateDifferenceAlign16(inputIterator); + // When offset is times of 2, you can adjust memory address. if ((offset & 1) == 0) { @@ -275,6 +278,7 @@ public unsafe void Serialize(ref MessagePackWriter writer, int[]? value, Message { // Make InputIterator Aligned var offset = UnsafeMemoryAlignmentUtility.CalculateDifferenceAlign16(inputIterator); + // When offset is times of 4, you can adjust memory address. if ((offset & 3) == 0) { @@ -412,6 +416,7 @@ public unsafe void Serialize(ref MessagePackWriter writer, float[]? value, Messa { // new float[] { 1.0, -2.0, 3.5, } is byte[12] { 00, 00, 80, 3f, 00, 00, 00, c0, 00, 00, 60, 40 } in binary expression; var current = Sse2.LoadVector128((byte*)inputIterator); + // Output binary should be byte[15] { ca, 3f, 80, 00, 00, ca, c0, 00, 00, 00, ca, 40, 60, 00, 00 }; Sse2.Store(outputIterator, Sse2.Or(Ssse3.Shuffle(current, vectorShuffle), vectorConstant)); } @@ -477,8 +482,10 @@ public unsafe void Serialize(ref MessagePackWriter writer, double[]? value, Mess { // Fetch 4 doubles. var current = Avx.LoadVector256((byte*)inputIterator); + // Reorder Little Endian bytes to Big Endian. var answer = Avx2.Shuffle(current, vectorShuffle).AsUInt64(); + // Write 4 Big-Endian doubles. *outputIterator++ = MessagePackCode.Float64; *(ulong*)outputIterator = answer.GetElement(0); @@ -595,6 +602,7 @@ public void Serialize(ref MessagePackWriter writer, bool[]? value, MessagePackSe // A value of false for the type bool is 0 for the sbyte representation. var isTrue = Avx2.CompareEqual(current, Vector256.Zero); + // A value of true in the SIMD context is -1 for the sbyte representation. // True is 0xc3 as MessagePackCode and false is 0xc2. // Reinterpreted as sbyte values, they are -61 and -62, respectively. @@ -633,6 +641,7 @@ public void Serialize(ref MessagePackWriter writer, bool[]? value, MessagePackSe // A value of false for the type bool is 0 for the sbyte representation. var isTrue = Sse2.CompareEqual(current, Vector128.Zero); + // A value of true in the SIMD context is -1 for the sbyte representation. // True is 0xc3 as MessagePackCode and false is 0xc2. // Reinterpreted as sbyte values, they are -61 and -62, respectively. diff --git a/src/MessagePack.Experimental/MessagePack.Experimental.csproj b/src/MessagePack.Experimental/MessagePack.Experimental.csproj index 5053878dc..58d6fb688 100644 --- a/src/MessagePack.Experimental/MessagePack.Experimental.csproj +++ b/src/MessagePack.Experimental/MessagePack.Experimental.csproj @@ -1,13 +1,9 @@ - netcoreapp3.1 + net6.0 true - 8.0 - enable - True - true MessagePack for C#, Experimental Plugins Extremely Fast MessagePack Serializer for C#(.NET, .NET Core, Unity, Xamarin). Experimental implementations. MsgPack;MessagePack;Serialization;Formatter;Serializer diff --git a/src/MessagePack.Experimental/UnsafeUnmanagedStructFormatter/UnsafeUnmanagedStructReadOnlySequenceFormatter.cs b/src/MessagePack.Experimental/UnsafeUnmanagedStructFormatter/UnsafeUnmanagedStructReadOnlySequenceFormatter.cs index eaf867b5f..c65220f2b 100644 --- a/src/MessagePack.Experimental/UnsafeUnmanagedStructFormatter/UnsafeUnmanagedStructReadOnlySequenceFormatter.cs +++ b/src/MessagePack.Experimental/UnsafeUnmanagedStructFormatter/UnsafeUnmanagedStructReadOnlySequenceFormatter.cs @@ -42,12 +42,18 @@ public void Serialize(ref MessagePackWriter writer, ReadOnlySequence value, M } else { + var destinationIterator = (byte*)destination; + var destinationCapacity = byteCount; foreach (var sourceMemory in value) { - fixed (void* source = &sourceMemory.Span[0]) + var sourceSpan = sourceMemory.Span; + fixed (void* source = &sourceSpan[0]) { - Buffer.MemoryCopy(source, destination, byteCount, byteCount); + Buffer.MemoryCopy(source, destinationIterator, destinationCapacity, sourceSpan.Length); } + + destinationCapacity -= sourceSpan.Length; + destinationIterator += sourceSpan.Length; } } } diff --git a/src/MessagePack.Generator/MessagePack.Generator.csproj b/src/MessagePack.Generator/MessagePack.Generator.csproj index 42b02f30f..dda450aa6 100644 --- a/src/MessagePack.Generator/MessagePack.Generator.csproj +++ b/src/MessagePack.Generator/MessagePack.Generator.csproj @@ -3,10 +3,7 @@ mpc Exe - netcoreapp3.1;net6.0 - 10 - enable - true + net6.0;net8.0 true mpc Major @@ -19,11 +16,12 @@ - - - - - + + + + + + diff --git a/src/MessagePack.Generator/MessagepackCompiler.cs b/src/MessagePack.Generator/MessagepackCompiler.cs index dfea81961..17e18f107 100644 --- a/src/MessagePack.Generator/MessagepackCompiler.cs +++ b/src/MessagePack.Generator/MessagepackCompiler.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Runtime.Loader; using System.Threading; using System.Threading.Tasks; @@ -90,6 +91,12 @@ public async Task RunAsync( { var logger = new ConsoleLogger(Microsoft.Build.Framework.LoggerVerbosity.Quiet); var project = await workspace.OpenProjectAsync(projectPath, logger, null, cancellationToken); + if (workspace.Diagnostics.Any(x => x.Kind is WorkspaceDiagnosticKind.Failure)) + { + throw new InvalidOperationException("One or more errors occured while opening the project:" + Environment.NewLine + + string.Join(Environment.NewLine, workspace.Diagnostics.Select(x => $"========================================{Environment.NewLine}{x.Message}{Environment.NewLine}========================================"))); + } + var compilation = await project.GetCompilationAsync(cancellationToken); if (compilation is null) { diff --git a/src/MessagePack.GeneratorCore/CodeAnalysis/Definitions.cs b/src/MessagePack.GeneratorCore/CodeAnalysis/Definitions.cs index 30aaedb98..c7282896d 100644 --- a/src/MessagePack.GeneratorCore/CodeAnalysis/Definitions.cs +++ b/src/MessagePack.GeneratorCore/CodeAnalysis/Definitions.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; +#pragma warning disable SA1402 // File may only contain a single type #pragma warning disable SA1649 // File name should match first type name namespace MessagePackCompiler.CodeAnalysis diff --git a/src/MessagePack.GeneratorCore/CodeAnalysis/TypeCollector.cs b/src/MessagePack.GeneratorCore/CodeAnalysis/TypeCollector.cs index eeedbdadc..5f991d514 100644 --- a/src/MessagePack.GeneratorCore/CodeAnalysis/TypeCollector.cs +++ b/src/MessagePack.GeneratorCore/CodeAnalysis/TypeCollector.cs @@ -1,6 +1,7 @@ // Copyright (c) All contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +#pragma warning disable SA1402 // File may only contain a single type #pragma warning disable SA1649 // File name should match first type name using System; @@ -252,12 +253,15 @@ public class TypeCollector private readonly List collectedGenericInfo = new(); private readonly List collectedUnionInfo = new(); + private readonly Compilation compilation; + public TypeCollector(Compilation compilation, bool disallowInternal, bool isForceUseMap, string[]? ignoreTypeNames, Action logger) { this.typeReferences = new ReferenceSymbols(compilation, logger); this.disallowInternal = disallowInternal; this.isForceUseMap = isForceUseMap; this.externalIgnoreTypeNames = new HashSet(ignoreTypeNames ?? Array.Empty()); + this.compilation = compilation; targetTypes = compilation.GetNamedTypeSymbols() .Where(x => @@ -316,7 +320,7 @@ private void CollectCore(ITypeSymbol typeSymbol) return; } - var typeSymbolString = typeSymbol.ToString() ?? throw new InvalidOperationException(); + var typeSymbolString = typeSymbol.WithNullableAnnotation(NullableAnnotation.NotAnnotated).ToString() ?? throw new InvalidOperationException(); if (this.embeddedTypes.Contains(typeSymbolString)) { return; @@ -329,7 +333,7 @@ private void CollectCore(ITypeSymbol typeSymbol) if (typeSymbol is IArrayTypeSymbol arrayTypeSymbol) { - this.CollectArray(arrayTypeSymbol); + this.CollectArray((IArrayTypeSymbol)ToTupleUnderlyingType(arrayTypeSymbol)); return; } @@ -357,13 +361,7 @@ private void CollectCore(ITypeSymbol typeSymbol) if (type.IsGenericType) { - this.CollectGeneric(type); - return; - } - - if (type.TupleUnderlyingType != null) - { - CollectGeneric(type.TupleUnderlyingType); + this.CollectGeneric((INamedTypeSymbol)ToTupleUnderlyingType(type)); return; } @@ -459,12 +457,34 @@ private void CollectArray(IArrayTypeSymbol array) this.collectedGenericInfo.Add(info); } + private ITypeSymbol ToTupleUnderlyingType(ITypeSymbol typeSymbol) + { + if (typeSymbol is IArrayTypeSymbol array) + { + return compilation.CreateArrayTypeSymbol(ToTupleUnderlyingType(array.ElementType), array.Rank); + } + + if (typeSymbol is not INamedTypeSymbol namedType || !namedType.IsGenericType) + { + return typeSymbol; + } + + namedType = namedType.TupleUnderlyingType ?? namedType; + var newTypeArguments = namedType.TypeArguments.Select(ToTupleUnderlyingType).ToArray(); + if (!namedType.TypeArguments.SequenceEqual(newTypeArguments)) + { + return namedType.ConstructedFrom.Construct(newTypeArguments); + } + + return namedType; + } + private void CollectGeneric(INamedTypeSymbol type) { INamedTypeSymbol genericType = type.ConstructUnboundGenericType(); var genericTypeString = genericType.ToDisplayString(); var fullName = type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); - var isOpenGenericType = type.TypeArguments.Any(x => x is ITypeParameterSymbol); + var isOpenGenericType = IsOpenGenericTypeRecursively(type); // special case if (fullName == "global::System.ArraySegment" || fullName == "global::System.ArraySegment?") @@ -615,7 +635,14 @@ private ObjectSerializationInfo GetObjectInfo(INamedTypeSymbol type) } var customFormatterAttr = item.GetAttributes().FirstOrDefault(x => x.AttributeClass.ApproximatelyEqual(this.typeReferences.MessagePackFormatterAttribute))?.ConstructorArguments[0].Value as INamedTypeSymbol; - var member = new MemberSerializationInfo(true, isWritable, isReadable, hiddenIntKey++, item.Name, item.Name, item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), item.Type.ToDisplayString(BinaryWriteFormat), customFormatterAttr?.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)); + var keyAttribute = item.GetAttributes() + .FirstOrDefault(x => x.AttributeClass.ApproximatelyEqual(this.typeReferences.KeyAttribute)); + + var stringKey = keyAttribute?.ConstructorArguments.Length > 0 + ? keyAttribute.ConstructorArguments[0].Value as string ?? item.Name + : item.Name; + + var member = new MemberSerializationInfo(true, isWritable, isReadable, hiddenIntKey++, stringKey, item.Name, item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), item.Type.ToDisplayString(BinaryWriteFormat), customFormatterAttr?.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)); stringMembers.Add(member.StringKey, member); this.CollectCore(item.Type); // recursive collect @@ -641,7 +668,14 @@ private ObjectSerializationInfo GetObjectInfo(INamedTypeSymbol type) } var customFormatterAttr = item.GetAttributes().FirstOrDefault(x => x.AttributeClass.ApproximatelyEqual(this.typeReferences.MessagePackFormatterAttribute))?.ConstructorArguments[0].Value as INamedTypeSymbol; - var member = new MemberSerializationInfo(false, isWritable, isReadable, hiddenIntKey++, item.Name, item.Name, item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), item.Type.ToDisplayString(BinaryWriteFormat), customFormatterAttr?.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)); + var keyAttribute = item.GetAttributes() + .FirstOrDefault(x => x.AttributeClass.ApproximatelyEqual(this.typeReferences.KeyAttribute)); + + var stringKey = keyAttribute?.ConstructorArguments.Length > 0 + ? keyAttribute.ConstructorArguments[0].Value as string ?? item.Name + : item.Name; + + var member = new MemberSerializationInfo(false, isWritable, isReadable, hiddenIntKey++, stringKey, item.Name, item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), item.Type.ToDisplayString(BinaryWriteFormat), customFormatterAttr?.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)); stringMembers.Add(member.StringKey, member); this.CollectCore(item.Type); // recursive collect } @@ -843,7 +877,7 @@ private ObjectSerializationInfo GetObjectInfo(INamedTypeSymbol type) } else { - throw new MessagePackGeneratorResolveFailedException("can't find matched constructor parameter, parameterType mismatch. type:" + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " parameterIndex:" + ctorParamIndex + " paramterType:" + item.Type.Name); + throw new MessagePackGeneratorResolveFailedException("can't find matched constructor parameter, parameterType mismatch. type:" + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " parameterIndex:" + ctorParamIndex + " parameterType:" + item.Type.Name); } } } @@ -864,6 +898,7 @@ private ObjectSerializationInfo GetObjectInfo(INamedTypeSymbol type) { IEnumerable> hasKey = constructorLookupDictionary[item.Name]; using var enumerator = hasKey.GetEnumerator(); + // hasKey.Count() == 0 if (!enumerator.MoveNext()) { @@ -877,12 +912,13 @@ private ObjectSerializationInfo GetObjectInfo(INamedTypeSymbol type) } var first = enumerator.Current.Value; + // hasKey.Count() != 1 if (enumerator.MoveNext()) { if (ctorEnumerator == null) { - throw new MessagePackGeneratorResolveFailedException("duplicate matched constructor parameter name:" + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " parameterName:" + item.Name + " paramterType:" + item.Type.Name); + throw new MessagePackGeneratorResolveFailedException("duplicate matched constructor parameter name:" + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " parameterName:" + item.Name + " parameterType:" + item.Type.Name); } ctor = null; @@ -898,7 +934,7 @@ private ObjectSerializationInfo GetObjectInfo(INamedTypeSymbol type) { if (ctorEnumerator == null) { - throw new MessagePackGeneratorResolveFailedException("can't find matched constructor parameter, parameterType mismatch. type:" + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " parameterName:" + item.Name + " paramterType:" + item.Type.Name); + throw new MessagePackGeneratorResolveFailedException("can't find matched constructor parameter, parameterType mismatch. type:" + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " parameterName:" + item.Name + " parameterType:" + item.Type.Name); } ctor = null; @@ -1026,5 +1062,10 @@ private bool IsAllowAccessibility(ITypeSymbol symbol) return true; } + + private bool IsOpenGenericTypeRecursively(INamedTypeSymbol type) + { + return type.IsGenericType && type.TypeArguments.Any(x => x is ITypeParameterSymbol || (x is INamedTypeSymbol symbol && IsOpenGenericTypeRecursively(symbol))); + } } } diff --git a/src/MessagePack.GeneratorCore/Generator/StringKey/StringKeyFormatterTemplate.cs b/src/MessagePack.GeneratorCore/Generator/StringKey/StringKeyFormatterTemplate.cs index abc89f788..083c69037 100644 --- a/src/MessagePack.GeneratorCore/Generator/StringKey/StringKeyFormatterTemplate.cs +++ b/src/MessagePack.GeneratorCore/Generator/StringKey/StringKeyFormatterTemplate.cs @@ -66,6 +66,17 @@ namespace "); this.Write("\r\n"); } this.Write(" {\r\n"); + foreach (var item in objInfo.Members) { + if (item.CustomFormatterTypeName != null) { + this.Write(" private readonly "); + this.Write(this.ToStringHelper.ToStringWithCulture(item.CustomFormatterTypeName)); + this.Write(" __"); + this.Write(this.ToStringHelper.ToStringWithCulture(item.Name)); + this.Write("CustomFormatter__ = new "); + this.Write(this.ToStringHelper.ToStringWithCulture(item.CustomFormatterTypeName)); + this.Write("();\r\n"); + } + } for (var i = 0; i < list.Count; i++) { var member = list[i].Item1; var binary = list[i].Item2; diff --git a/src/MessagePack.GeneratorCore/Generator/StringKey/StringKeyFormatterTemplate.tt b/src/MessagePack.GeneratorCore/Generator/StringKey/StringKeyFormatterTemplate.tt index 9f0dac873..6a6c3a8e2 100644 --- a/src/MessagePack.GeneratorCore/Generator/StringKey/StringKeyFormatterTemplate.tt +++ b/src/MessagePack.GeneratorCore/Generator/StringKey/StringKeyFormatterTemplate.tt @@ -36,6 +36,11 @@ foreach (var objInfo in ObjectSerializationInfos) { where <#= typeArg.Name #> : <#= typeArg.Constraints #> <# }#> { +<# foreach (var item in objInfo.Members) { #> +<# if (item.CustomFormatterTypeName != null) { #> + private readonly <#= item.CustomFormatterTypeName #> __<#= item.Name #>CustomFormatter__ = new <#= item.CustomFormatterTypeName #>(); +<# } #> +<# } #> <# for (var i = 0; i < list.Count; i++) { var member = list[i].Item1; var binary = list[i].Item2; #> diff --git a/src/MessagePack.GeneratorCore/Generator/TemplatePartials.cs b/src/MessagePack.GeneratorCore/Generator/TemplatePartials.cs index bca5c5876..9e04131b9 100644 --- a/src/MessagePack.GeneratorCore/Generator/TemplatePartials.cs +++ b/src/MessagePack.GeneratorCore/Generator/TemplatePartials.cs @@ -1,6 +1,8 @@ // Copyright (c) All contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +#pragma warning disable SA1402 // File may only contain a single type + using MessagePackCompiler.CodeAnalysis; namespace MessagePackCompiler.Generator diff --git a/src/MessagePack.GeneratorCore/Generator/UnionTemplate.cs b/src/MessagePack.GeneratorCore/Generator/UnionTemplate.cs index b7a639bf7..12e2abb8f 100644 --- a/src/MessagePack.GeneratorCore/Generator/UnionTemplate.cs +++ b/src/MessagePack.GeneratorCore/Generator/UnionTemplate.cs @@ -95,9 +95,10 @@ namespace "); for(var i = 0; i < info.SubTypes.Length; i++) { var item = info.SubTypes[i]; this.Write(" case "); this.Write(this.ToStringHelper.ToStringWithCulture(i)); - this.Write(":\r\n options.Resolver.GetFormatterWithVerify<"); + this.Write(":\r\n global::MessagePack.FormatterResolverExtensions.GetFor" + + "matterWithVerify<"); this.Write(this.ToStringHelper.ToStringWithCulture(item.Type)); - this.Write(">().Serialize(ref writer, ("); + this.Write(">(options.Resolver).Serialize(ref writer, ("); this.Write(this.ToStringHelper.ToStringWithCulture(item.Type)); this.Write(")value, options);\r\n break;\r\n"); } @@ -127,9 +128,10 @@ namespace "); this.Write(this.ToStringHelper.ToStringWithCulture(i)); this.Write(":\r\n result = ("); this.Write(this.ToStringHelper.ToStringWithCulture(info.FullName)); - this.Write(")options.Resolver.GetFormatterWithVerify<"); + this.Write(")global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify<"); this.Write(this.ToStringHelper.ToStringWithCulture(item.Type)); - this.Write(">().Deserialize(ref reader, options);\r\n break;\r\n"); + this.Write(">(options.Resolver).Deserialize(ref reader, options);\r\n break;" + + "\r\n"); } this.Write(" default:\r\n reader.Skip();\r\n " + " break;\r\n }\r\n\r\n reader.Depth--;\r\n return result" + diff --git a/src/MessagePack.GeneratorCore/Generator/UnionTemplate.tt b/src/MessagePack.GeneratorCore/Generator/UnionTemplate.tt index eb9272d02..a3ecb671d 100644 --- a/src/MessagePack.GeneratorCore/Generator/UnionTemplate.tt +++ b/src/MessagePack.GeneratorCore/Generator/UnionTemplate.tt @@ -51,7 +51,7 @@ namespace <#= Namespace #> { <# for(var i = 0; i < info.SubTypes.Length; i++) { var item = info.SubTypes[i]; #> case <#= i #>: - options.Resolver.GetFormatterWithVerify<<#= item.Type #>>().Serialize(ref writer, (<#= item.Type #>)value, options); + global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify<<#= item.Type #>>(options.Resolver).Serialize(ref writer, (<#= item.Type #>)value, options); break; <# } #> default: @@ -89,7 +89,7 @@ namespace <#= Namespace #> { <# for(var i = 0; i < info.SubTypes.Length; i++) { var item = info.SubTypes[i]; #> case <#= i #>: - result = (<#= info.FullName #>)options.Resolver.GetFormatterWithVerify<<#= item.Type #>>().Deserialize(ref reader, options); + result = (<#= info.FullName #>)global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify<<#= item.Type #>>(options.Resolver).Deserialize(ref reader, options); break; <# } #> default: diff --git a/src/MessagePack.GeneratorCore/MessagePack.GeneratorCore.csproj b/src/MessagePack.GeneratorCore/MessagePack.GeneratorCore.csproj index df4755e25..2714c70f3 100644 --- a/src/MessagePack.GeneratorCore/MessagePack.GeneratorCore.csproj +++ b/src/MessagePack.GeneratorCore/MessagePack.GeneratorCore.csproj @@ -3,16 +3,12 @@ netstandard2.0 MessagePackCompiler - - True - ..\..\opensource.snk - 9 - enable + false - - + + diff --git a/src/MessagePack.GeneratorCore/MessagePack.UniversalCodeGenerator.ruleset b/src/MessagePack.GeneratorCore/MessagePack.UniversalCodeGenerator.ruleset deleted file mode 100644 index 75c353d79..000000000 --- a/src/MessagePack.GeneratorCore/MessagePack.UniversalCodeGenerator.ruleset +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/MessagePack.ImmutableCollection/MessagePack.ImmutableCollection.csproj b/src/MessagePack.ImmutableCollection/MessagePack.ImmutableCollection.csproj index 3a53f9e27..5e579b218 100644 --- a/src/MessagePack.ImmutableCollection/MessagePack.ImmutableCollection.csproj +++ b/src/MessagePack.ImmutableCollection/MessagePack.ImmutableCollection.csproj @@ -3,7 +3,6 @@ netstandard2.0 - true MessagePack for C# Extension Support for ImmutableCollection Deprecated package to add System.Collections.Immutable support to MessagePack. This is now built into the main package. MsgPack;MessagePack;Serialization;Formatter;Serializer diff --git a/src/MessagePack.MSBuild.Tasks/MessagePack.MSBuild.Tasks.csproj b/src/MessagePack.MSBuild.Tasks/MessagePack.MSBuild.Tasks.csproj index 4eb0b7fc0..6f2e11c86 100644 --- a/src/MessagePack.MSBuild.Tasks/MessagePack.MSBuild.Tasks.csproj +++ b/src/MessagePack.MSBuild.Tasks/MessagePack.MSBuild.Tasks.csproj @@ -2,8 +2,6 @@ netstandard2.0 - 8 - enable true true @@ -11,7 +9,6 @@ $(TargetsForTfmSpecificContentInPackage);AddBuildOutputAndDependencies true true - true MessagePack CodeGenerator Tasks MSBuild Tasks of MessagePack for C#. MsgPack;MessagePack;Serialization;Formatter;Serializer;Unity;Xamarin @@ -26,7 +23,7 @@ - + diff --git a/src/MessagePack.MSBuild.Tasks/MessagePackGenerator.cs b/src/MessagePack.MSBuild.Tasks/MessagePackGenerator.cs index a3766024a..69d8f23e5 100644 --- a/src/MessagePack.MSBuild.Tasks/MessagePackGenerator.cs +++ b/src/MessagePack.MSBuild.Tasks/MessagePackGenerator.cs @@ -20,15 +20,13 @@ namespace MessagePack.MSBuild.Tasks { public class MessagePackGenerator : Microsoft.Build.Utilities.Task, ICancelableTask { - private const string GeneratedFileName = "mpc_generated.cs"; - private readonly CancellationTokenSource cts = new CancellationTokenSource(); [Required] public ITaskItem[] Compile { get; set; } = null!; [Required] - public string IntermediateOutputPath { get; set; } = null!; + public string GeneratedOutputPath { get; set; } = null!; [Required] public ITaskItem[] ReferencePath { get; set; } = null!; @@ -44,9 +42,6 @@ public class MessagePackGenerator : Microsoft.Build.Utilities.Task, ICancelableT public string[]? ExternalIgnoreTypeNames { get; set; } - [Output] - public string? GeneratedOutputPath { get; set; } - internal CancellationToken CancellationToken => this.cts.Token; public void Cancel() => this.cts.Cancel(); @@ -59,8 +54,6 @@ public override bool Execute() return false; } - this.GeneratedOutputPath = Path.Combine(this.IntermediateOutputPath, GeneratedFileName); - try { var compilation = this.CreateCompilation(); diff --git a/src/MessagePack.MSBuild.Tasks/build/MessagePack.MSBuild.Tasks.targets b/src/MessagePack.MSBuild.Tasks/build/MessagePack.MSBuild.Tasks.targets index 554bc7f42..e95cf9e90 100644 --- a/src/MessagePack.MSBuild.Tasks/build/MessagePack.MSBuild.Tasks.targets +++ b/src/MessagePack.MSBuild.Tasks/build/MessagePack.MSBuild.Tasks.targets @@ -1,21 +1,28 @@  + + $(IntermediateOutputPath)mpc_generated.cs + + + + + + + DependsOnTargets="ResolveReferences" + Inputs="@(Compile)" + Outputs="$(GeneratedMessagePackFile)"> - - - + /> diff --git a/src/MessagePack.ReactiveProperty/Formatters.cs b/src/MessagePack.ReactiveProperty/Formatters.cs index 6374a63b3..6a3d4ec28 100644 --- a/src/MessagePack.ReactiveProperty/Formatters.cs +++ b/src/MessagePack.ReactiveProperty/Formatters.cs @@ -8,6 +8,7 @@ using MessagePack.Formatters; using Reactive.Bindings; +#pragma warning disable SA1402 // File may only contain a single type #pragma warning disable SA1649 // File name should match first type name namespace MessagePack.ReactivePropertyExtension @@ -109,9 +110,9 @@ internal static int ToReactivePropertySlimModeInt(global::Reactive.Bindings.R } // [Mode, SchedulerId, Value] : length should be three. - public class ReactivePropertyFormatter : IMessagePackFormatter> + public class ReactivePropertyFormatter : IMessagePackFormatter?> { - public void Serialize(ref MessagePackWriter writer, ReactiveProperty value, MessagePackSerializerOptions options) + public void Serialize(ref MessagePackWriter writer, ReactiveProperty? value, MessagePackSerializerOptions options) { if (value == null) { @@ -127,7 +128,7 @@ public void Serialize(ref MessagePackWriter writer, ReactiveProperty value, M } } - public ReactiveProperty Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) + public ReactiveProperty? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) { if (reader.TryReadNil()) { @@ -332,9 +333,9 @@ public void Serialize(ref MessagePackWriter writer, Unit? value, MessagePackSeri } // [Mode, Value] - public class ReactivePropertySlimFormatter : IMessagePackFormatter> + public class ReactivePropertySlimFormatter : IMessagePackFormatter?> { - public void Serialize(ref MessagePackWriter writer, ReactivePropertySlim value, MessagePackSerializerOptions options) + public void Serialize(ref MessagePackWriter writer, ReactivePropertySlim? value, MessagePackSerializerOptions options) { if (value == null) { @@ -349,7 +350,7 @@ public void Serialize(ref MessagePackWriter writer, ReactivePropertySlim valu } } - public ReactivePropertySlim Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) + public ReactivePropertySlim? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) { if (reader.TryReadNil()) { diff --git a/src/MessagePack.ReactiveProperty/MessagePack.ReactiveProperty.csproj b/src/MessagePack.ReactiveProperty/MessagePack.ReactiveProperty.csproj index f6b54590d..c0ffb1ae9 100644 --- a/src/MessagePack.ReactiveProperty/MessagePack.ReactiveProperty.csproj +++ b/src/MessagePack.ReactiveProperty/MessagePack.ReactiveProperty.csproj @@ -3,7 +3,6 @@ netstandard2.0 - true MessagePack for C# Extension Support for ReactiveProperty Extremely Fast MessagePack Serializer for C#(.NET, .NET Core, Unity, Xamarin). Extension support for ReactiveProperty. MsgPack;MessagePack;Serialization;Formatter;Serializer;ReactiveProperty @@ -15,8 +14,7 @@ - - + diff --git a/src/MessagePack.ReactiveProperty/ReactivePropertyResolver.cs b/src/MessagePack.ReactiveProperty/ReactivePropertyResolver.cs index 51e30e742..7dd40cf2b 100644 --- a/src/MessagePack.ReactiveProperty/ReactivePropertyResolver.cs +++ b/src/MessagePack.ReactiveProperty/ReactivePropertyResolver.cs @@ -1,6 +1,8 @@ // Copyright (c) All contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +#pragma warning disable SA1402 // File may only contain a single type + using System; using System.Collections.Generic; using System.Reactive; @@ -18,18 +20,18 @@ private ReactivePropertyResolver() { } - public IMessagePackFormatter GetFormatter() + public IMessagePackFormatter? GetFormatter() { return FormatterCache.Formatter; } private static class FormatterCache { - internal static readonly IMessagePackFormatter Formatter; + internal static readonly IMessagePackFormatter? Formatter; static FormatterCache() { - Formatter = (IMessagePackFormatter)ReactivePropertyResolverGetFormatterHelper.GetFormatter(typeof(T)); + Formatter = (IMessagePackFormatter?)ReactivePropertyResolverGetFormatterHelper.GetFormatter(typeof(T)); } } } @@ -45,7 +47,7 @@ internal static class ReactivePropertyResolverGetFormatterHelper { typeof(ReactivePropertySlim<>), typeof(ReactivePropertySlimFormatter<>) }, }; - internal static object GetFormatter(Type t) + internal static object? GetFormatter(Type t) { if (t == typeof(Unit)) { @@ -71,7 +73,7 @@ internal static object GetFormatter(Type t) return null; } - private static object CreateInstance(Type genericType, Type[] genericTypeArguments, params object[] arguments) + private static object? CreateInstance(Type genericType, Type[] genericTypeArguments, params object[] arguments) { return Activator.CreateInstance(genericType.MakeGenericType(genericTypeArguments), arguments); } diff --git a/src/MessagePack.UnityClient/.gitignore b/src/MessagePack.UnityClient/.gitignore index 7de09ed87..357eef0a7 100644 --- a/src/MessagePack.UnityClient/.gitignore +++ b/src/MessagePack.UnityClient/.gitignore @@ -5,3 +5,6 @@ # Unity wants to see binaries inside its Assets directory. # We link them in with make_unity_symlink.bat, but we don't want to actually commit them into git. Assets/*.dll + +# Unity requires .meta files to be committed - unignore the setting from above +!*.meta \ No newline at end of file diff --git a/src/MessagePack.UnityClient/.vsconfig b/src/MessagePack.UnityClient/.vsconfig new file mode 100644 index 000000000..d70cd98b7 --- /dev/null +++ b/src/MessagePack.UnityClient/.vsconfig @@ -0,0 +1,6 @@ +{ + "version": "1.0", + "components": [ + "Microsoft.VisualStudio.Workload.ManagedGame" + ] +} diff --git a/src/MessagePack.UnityClient/Assets/Resources.meta b/src/MessagePack.UnityClient/Assets/Resources.meta new file mode 100644 index 000000000..73088aba8 --- /dev/null +++ b/src/MessagePack.UnityClient/Assets/Resources.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b1de8635c42f5144f9c77b537e3a9616 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/MessagePack.UnityClient/Assets/Resources/BillingMode.json b/src/MessagePack.UnityClient/Assets/Resources/BillingMode.json new file mode 100644 index 000000000..6f4bfb710 --- /dev/null +++ b/src/MessagePack.UnityClient/Assets/Resources/BillingMode.json @@ -0,0 +1 @@ +{"androidStore":"GooglePlay"} \ No newline at end of file diff --git a/src/MessagePack.UnityClient/Assets/Resources/BillingMode.json.meta b/src/MessagePack.UnityClient/Assets/Resources/BillingMode.json.meta new file mode 100644 index 000000000..7a0108031 --- /dev/null +++ b/src/MessagePack.UnityClient/Assets/Resources/BillingMode.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: ae6ce7e0a0dd7ca45b060f370d748bd3 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor.meta b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor.meta index 049ee1978..2e4c0c54e 100644 --- a/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor.meta +++ b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: e64ff5fb973b7104f9c76c166c46f872 +guid: bc6ad6aaa548c204f9866a24b01d4507 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/HierarchyTreeBuilder.cs.meta b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/HierarchyTreeBuilder.cs.meta index 102152e98..82c8e9bb4 100644 --- a/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/HierarchyTreeBuilder.cs.meta +++ b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/HierarchyTreeBuilder.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 50688156d1ed48e4d8e835a295e75699 +guid: 8760bbbab905a534eb6fb7b61b736926 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.MenuItems.cs b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.MenuItems.cs index 9ec1c5453..94508b51c 100644 --- a/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.MenuItems.cs +++ b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.MenuItems.cs @@ -169,6 +169,8 @@ static void BuildTargetStandaloneWindows64() SaveSettings(settings); } +#if !UNITY_2019_2_OR_NEWER + [MenuItem("Test/Settings/BuildTarget/StandaloneLinux", validate = true, priority = 3)] static bool ValidateBuildTargetStandaloneLinux() { @@ -185,6 +187,8 @@ static void BuildTargetStandaloneLinux() SaveSettings(settings); } +#endif + [MenuItem("Test/Settings/BuildTarget/StandaloneLinux64", validate = true, priority = 4)] static bool ValidateBuildTargetStandaloneLinux64() { diff --git a/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.MenuItems.cs.meta b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.MenuItems.cs.meta index 5aef7c9ce..7c9917290 100644 --- a/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.MenuItems.cs.meta +++ b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.MenuItems.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 8b7df7a947db56a4389c26cf3022840f +guid: 12bdad0556e999f4aa82da29415d361f MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.cs b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.cs index 46da861f4..9d7ddeb16 100644 --- a/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.cs +++ b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.cs @@ -4,6 +4,7 @@ using RuntimeUnitTestToolkit.Editor; using System; using UnityEditor; +using UnityEditor.Build; using UnityEditor.Build.Reporting; using UnityEditor.SceneManagement; using UnityEngine; @@ -118,7 +119,7 @@ public static void BuildUnitTest() if (buildPath == null) { - buildPath = $"bin/UnitTest/{settings.BuildTarget}_{settings.ScriptBackend}/test" + (IsWindows(settings.BuildTarget) ? ".exe" : ""); + buildPath = $"bin/UnitTest/{settings.BuildTarget}_{settings.ScriptBackend}/test" + GetExtensionForBuildTarget(settings.BuildTarget); } var originalScene = SceneManager.GetActiveScene().path; @@ -397,7 +398,9 @@ static void Build(string sceneName, string buildPath, RuntimeUnitTestSettings se } if (settings.Headless) { +#pragma warning disable CS0618 options |= BuildOptions.EnableHeadlessMode; +#pragma warning restore CS0618 } var targetGroup = ToBuildTargetGroup(settings.BuildTarget); @@ -408,6 +411,9 @@ static void Build(string sceneName, string buildPath, RuntimeUnitTestSettings se PlayerSettings.SetScriptingBackend(targetGroup, settings.ScriptBackend); } + // MemoryPack changed: + EditorUserBuildSettings.il2CppCodeGeneration = Il2CppCodeGeneration.OptimizeSpeed; //runtime + var buildOptions = new BuildPlayerOptions { target = settings.BuildTarget, @@ -457,20 +463,42 @@ static bool IsWindows(BuildTarget buildTarget) } } + static string GetExtensionForBuildTarget(BuildTarget buildTarget) + { + switch (buildTarget) + { + case BuildTarget.StandaloneWindows: + case BuildTarget.StandaloneWindows64: + case BuildTarget.WSAPlayer: + return ".exe"; + case BuildTarget.StandaloneOSX: + return ".app"; + case BuildTarget.Android: + return ".apk"; + default: + return ""; + } + } + static BuildTargetGroup ToBuildTargetGroup(BuildTarget buildTarget) { #pragma warning disable CS0618 switch (buildTarget) { +#if UNITY_2017_3_OR_NEWER case BuildTarget.StandaloneOSX: - case (BuildTarget)3: +#else case BuildTarget.StandaloneOSXIntel: case BuildTarget.StandaloneOSXIntel64: + case BuildTarget.StandaloneOSXUniversal: +#endif // UNITY_2017_3_OR_NEWER case BuildTarget.StandaloneWindows: case BuildTarget.StandaloneWindows64: - case BuildTarget.StandaloneLinux: case BuildTarget.StandaloneLinux64: +#if !UNITY_2019_2_OR_NEWER + case BuildTarget.StandaloneLinux: case BuildTarget.StandaloneLinuxUniversal: +#endif // !UNITY_2019_2_OR_NEWER return BuildTargetGroup.Standalone; case (BuildTarget)6: case (BuildTarget)7: diff --git a/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.cs.meta b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.cs.meta index 7a1cd450b..bf66c58a9 100644 --- a/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.cs.meta +++ b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.cs.meta @@ -1,7 +1,8 @@ fileFormatVersion: 2 -guid: 4a1edf0e809243a4b95687c2fc05134c +guid: 3518da33b6245d341a0ef3670ee9268b +timeCreated: 1488689723 +licenseType: Pro MonoImporter: - externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 diff --git a/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/RuntimeUnitTestToolkit.asmdef.meta b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/RuntimeUnitTestToolkit.asmdef.meta index d96593114..5ad198be0 100644 --- a/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/RuntimeUnitTestToolkit.asmdef.meta +++ b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/RuntimeUnitTestToolkit.asmdef.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: e1ad5407f3f9cec45a06a114f01e5d25 +guid: 14c4fea4b238088479114ba2ffe195f9 AssemblyDefinitionImporter: externalObjects: {} userData: diff --git a/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/UnitTestData.cs b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/UnitTestData.cs new file mode 100644 index 000000000..2e31d3af6 --- /dev/null +++ b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/UnitTestData.cs @@ -0,0 +1,391 @@ +using NUnit.Framework; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using UnityEngine; +using UnityEngine.TestTools; + +namespace RuntimeUnitTestToolkit +{ + public class UnitTestData + { + // [SetupFixture][OneTimeSetup] + readonly List globalSetups = new List(); + // [SetupFixture][OneTimeTearDown] + readonly List globalTearDowns = new List(); + + // Key:Type.FullName + readonly Dictionary testGroups = new Dictionary(); + + public IEnumerable GlobalSetups => globalSetups; + public IEnumerable GlobalTearDowns => globalTearDowns; + public IEnumerable> TestGroups => testGroups; + + static IEnumerable GetTestTargetTypes() + { + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) + { + var n = assembly.FullName; + if (n.StartsWith("UnityEngine")) continue; + if (n.StartsWith("mscorlib")) continue; + if (n.StartsWith("System")) continue; + + foreach (var item in assembly.GetTypes()) + { + SetUpFixtureAttribute setupFixture; + try + { + setupFixture = item.GetCustomAttribute(true); + } + catch (Exception ex) + { + Debug.Log("TestAttribute Load Fail, Assembly:" + assembly.FullName); + Debug.LogException(ex); + goto NEXT_ASSEMBLY; + } + if (setupFixture != null) + { + yield return item; + continue; + } + + foreach (var method in item.GetMethods()) + { + TestAttribute t1 = null; + try + { + t1 = method.GetCustomAttribute(true); + } + catch (Exception ex) + { + Debug.Log("TestAttribute Load Fail, Assembly:" + assembly.FullName); + Debug.LogException(ex); + goto NEXT_ASSEMBLY; + } + if (t1 != null) + { + yield return item; + break; + } + + UnityTestAttribute t2 = null; + try + { + t2 = method.GetCustomAttribute(true); + } + catch (Exception ex) + { + Debug.Log("UnityTestAttribute Load Fail, Assembly:" + assembly.FullName); + Debug.LogException(ex); + goto NEXT_ASSEMBLY; + } + if (t2 != null) + { + yield return item; + break; + } + } + } + + NEXT_ASSEMBLY: + continue; + } + } + + static void RegisterAttributeAction(MethodInfo info, object instance, List list) + where T : Attribute + { + var attr = info.GetCustomAttribute(true); + if (attr != null) + { + list.Add((Action)Delegate.CreateDelegate(typeof(Action), instance, info)); + } + } + + public static UnitTestData CreateFromAllAssemblies() + { + return new UnitTestData(); + } + + UnitTestData() + { + foreach (var testType in GetTestTargetTypes()) + { + var setupFixture = testType.GetCustomAttribute(true); + if (setupFixture != null) + { + var instance = Activator.CreateInstance(testType); + + var methods = testType.GetMethods(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); + foreach (var item in methods) + { + RegisterAttributeAction(item, instance, globalSetups); + RegisterAttributeAction(item, instance, globalTearDowns); + } + continue; + } + else + { + var group = new TestGroup(testType); + testGroups[testType.FullName] = group; + } + } + } + } + + public class TestGroup + { + // [Test] + List<(string name, Action test)> syncTests = new List<(string name, Action test)>(); + + // [UnityTest] + List<(string name, Func test)> asyncTests = new List<(string name, Func test)>(); + + // [SetUp] + List setups = new List(); + + // [OneTimeSetUp] + List onetimeSetups = new List(); + + // [TearDown] + List tearDowns = new List(); + + // [OneTimeTearDown] + List oneTimeTearDowns = new List(); + + // [UnitySetUp] + List> unitySetUps = new List>(); + + // [UnityTearDown] + List> unityTearDowns = new List>(); + + public string Name { get; private set; } + public IEnumerable<(string name, Action test)> SyncTests => syncTests; + public IEnumerable<(string name, Func test)> AsyncTests => asyncTests; + public IEnumerable Setups => setups; + public IEnumerable OnetimeSetups => onetimeSetups; + public IEnumerable TearDowns => tearDowns; + public IEnumerable OneTimeTearDowns => oneTimeTearDowns; + public IEnumerable> UnitySetUps => unitySetUps; + public IEnumerable> UnityTearDowns => unityTearDowns; + + public IEnumerable<(string name, object test)> Tests + { + get + { + foreach (var item in syncTests) yield return (item.name, item.test); + foreach (var item in asyncTests) yield return (item.name, item.test); + } + } + + static void RegisterAttributeAction(MethodInfo info, object instance, List list) + where T : Attribute + { + var attr = info.GetCustomAttribute(true); + if (attr != null) + { + list.Add((Action)Delegate.CreateDelegate(typeof(Action), instance, info)); + } + } + + static void RegisterAttributeAction(MethodInfo info, object instance, List> list) + where T : Attribute + { + var attr = info.GetCustomAttribute(true); + if (attr != null) + { + list.Add((Func)Delegate.CreateDelegate(typeof(Func), instance, info)); + } + } + + public TestGroup(Type testType) + { + this.Name = testType.Name; // not FullName + + var test = Activator.CreateInstance(testType); + var methods = testType.GetMethods(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); + + foreach (var item in methods) + { + try + { + RegisterAttributeAction(item, test, setups); + RegisterAttributeAction(item, test, tearDowns); + RegisterAttributeAction(item, test, onetimeSetups); + RegisterAttributeAction(item, test, oneTimeTearDowns); + RegisterAttributeAction(item, test, unitySetUps); + RegisterAttributeAction(item, test, unityTearDowns); + } + catch (Exception e) + { + UnityEngine.Debug.LogError(testType.Name + "." + item.Name + " failed to register setup/teardown method, exception: " + e.ToString()); + } + + try + { + var iteratorTest = item.GetCustomAttribute(true); + if (iteratorTest != null) + { + if (item.GetParameters().Length == 0 && item.ReturnType == typeof(IEnumerator)) + { + var factory = (Func)Delegate.CreateDelegate(typeof(Func), test, item); + asyncTests.Add((factory.Method.Name, factory)); + } + else + { + var testData = GetTestData(item); + if (testData.Count != 0) + { + foreach (var item2 in testData) + { + Func factory; + if (item.IsGenericMethod) + { + var method2 = InferGenericType(item, item2); + factory = () => (IEnumerator)method2.Invoke(test, item2); + } + else + { + factory = () => (IEnumerator)item.Invoke(test, item2); + } + var name = item.Name + "(" + string.Join(", ", item2.Select(x => x?.ToString() ?? "null")) + ")"; + name = name.Replace(Char.MinValue, ' ').Replace(Char.MaxValue, ' ').Replace("<", "[").Replace(">", "]"); + asyncTests.Add((name, factory)); + } + } + else + { + UnityEngine.Debug.Log(testType.Name + "." + item.Name + " currently does not supported in RuntumeUnitTestToolkit(multiple parameter without TestCase or return type is invalid)."); + } + } + } + + var standardTest = item.GetCustomAttribute(true); + if (standardTest != null) + { + if (item.GetParameters().Length == 0 && item.ReturnType == typeof(void)) + { + var invoke = (Action)Delegate.CreateDelegate(typeof(Action), test, item); + syncTests.Add((invoke.Method.Name, invoke)); + } + else + { + var testData = GetTestData(item); + if (testData.Count != 0) + { + foreach (var item2 in testData) + { + Action invoke = null; + if (item.IsGenericMethod) + { + var method2 = InferGenericType(item, item2); + invoke = () => method2.Invoke(test, item2); + } + else + { + invoke = () => item.Invoke(test, item2); + } + var name = item.Name + "(" + string.Join(", ", item2.Select(x => x?.ToString() ?? "null")) + ")"; + name = name.Replace(Char.MinValue, ' ').Replace(Char.MaxValue, ' ').Replace("<", "[").Replace(">", "]"); + syncTests.Add((name, invoke)); + } + } + else + { + UnityEngine.Debug.Log(testType.Name + "." + item.Name + " currently does not supported in RuntumeUnitTestToolkit(multiple parameter without TestCase or return type is invalid)."); + } + } + } + } + catch (Exception e) + { + UnityEngine.Debug.LogError(testType.Name + "." + item.Name + " failed to register method, exception: " + e.ToString()); + } + } + } + + List GetTestData(MethodInfo methodInfo) + { + List testCases = new List(); + + var inlineData = methodInfo.GetCustomAttributes(true); + foreach (var item in inlineData) + { + testCases.Add(item.Arguments); + } + + var sourceData = methodInfo.GetCustomAttributes(true); + foreach (var item in sourceData) + { + var enumerator = GetTestCaseSource(methodInfo, item.SourceType, item.SourceName, item.MethodParams); + foreach (var item2 in enumerator) + { + var item3 = item2 as IEnumerable; // object[][] + if (item3 != null) + { + var l = new List(); + foreach (var item4 in item3) + { + l.Add(item4); + } + testCases.Add(l.ToArray()); + } + } + } + + return testCases; + } + + IEnumerable GetTestCaseSource(MethodInfo method, Type sourceType, string sourceName, object[] methodParams) + { + Type type = sourceType ?? method.DeclaringType; + + MemberInfo[] member = type.GetMember(sourceName, BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); + if (member.Length == 1) + { + MemberInfo memberInfo = member[0]; + FieldInfo fieldInfo = memberInfo as FieldInfo; + if ((object)fieldInfo != null) + { + return (!fieldInfo.IsStatic) ? ReturnErrorAsParameter("The sourceName specified on a TestCaseSourceAttribute must refer to a static field, property or method.") : ((methodParams == null) ? ((IEnumerable)fieldInfo.GetValue(null)) : ReturnErrorAsParameter("You have specified a data source field but also given a set of parameters. Fields cannot take parameters, please revise the 3rd parameter passed to the TestCaseSourceAttribute and either remove it or specify a method.")); + } + PropertyInfo propertyInfo = memberInfo as PropertyInfo; + if ((object)propertyInfo != null) + { + return (!propertyInfo.GetGetMethod(nonPublic: true).IsStatic) ? ReturnErrorAsParameter("The sourceName specified on a TestCaseSourceAttribute must refer to a static field, property or method.") : ((methodParams == null) ? ((IEnumerable)propertyInfo.GetValue(null, null)) : ReturnErrorAsParameter("You have specified a data source property but also given a set of parameters. Properties cannot take parameters, please revise the 3rd parameter passed to the TestCaseSource attribute and either remove it or specify a method.")); + } + MethodInfo methodInfo = memberInfo as MethodInfo; + if ((object)methodInfo != null) + { + return (!methodInfo.IsStatic) ? ReturnErrorAsParameter("The sourceName specified on a TestCaseSourceAttribute must refer to a static field, property or method.") : ((methodParams == null || methodInfo.GetParameters().Length == methodParams.Length) ? ((IEnumerable)methodInfo.Invoke(null, methodParams)) : ReturnErrorAsParameter("You have given the wrong number of arguments to the method in the TestCaseSourceAttribute, please check the number of parameters passed in the object is correct in the 3rd parameter for the TestCaseSourceAttribute and this matches the number of parameters in the target method and try again.")); + } + } + return null; + } + + MethodInfo InferGenericType(MethodInfo methodInfo, object[] parameters) + { + var set = new HashSet(); + List genericParameters = new List(); + foreach (var item in methodInfo.GetParameters() + .Select((x, i) => new { x.ParameterType, i }) + .Where(x => x.ParameterType.IsGenericParameter) + .OrderBy(x => x.ParameterType.GenericParameterPosition)) + { + if (set.Add(item.ParameterType)) // DistinctBy + { + genericParameters.Add(parameters[item.i].GetType()); + } + } + + return methodInfo.MakeGenericMethod(genericParameters.ToArray()); + } + + IEnumerable ReturnErrorAsParameter(string name) + { + throw new Exception(name); + } + } +} \ No newline at end of file diff --git a/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/UnitTestData.cs.meta b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/UnitTestData.cs.meta new file mode 100644 index 000000000..dc968a32b --- /dev/null +++ b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/UnitTestData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9658eff528f61734a84e348d7954e502 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/UnitTestRunner.cs b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/UnitTestRunner.cs index 8637876cd..e05b45c0e 100644 --- a/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/UnitTestRunner.cs +++ b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/UnitTestRunner.cs @@ -13,9 +13,7 @@ namespace RuntimeUnitTestToolkit { public class UnitTestRunner : MonoBehaviour { - // object is IEnumerator or Func - Dictionary> tests = new Dictionary>(); - + UnitTestData testData; List additionalActionsOnFirst = new List(); public Button clearButton; @@ -50,21 +48,18 @@ void Start() }; // register all test types - foreach (var item in GetTestTargetTypes()) - { - RegisterAllMethods(item); - } + testData = UnitTestData.CreateFromAllAssemblies(); var executeAll = new List>(); - foreach (var ___item in tests) + foreach (var ___item in testData.TestGroups) { var actionList = ___item; // be careful, capture in lambda - executeAll.Add(() => StartCoroutine(RunTestInCoroutine(actionList))); - Add(actionList.Key, () => StartCoroutine(RunTestInCoroutine(actionList))); + executeAll.Add(() => StartCoroutine(RunTestInCoroutine(actionList.Key, actionList.Value, true))); + Add(actionList.Key, actionList.Value.Name, () => StartCoroutine(RunTestInCoroutine(actionList.Key, actionList.Value, false))); } - var executeAllButton = Add("Run All Tests", () => StartCoroutine(ExecuteAllInCoroutine(executeAll))); + var executeAllButton = Add("$<>__AllTest", "Run All Tests", () => StartCoroutine(ExecuteAllInCoroutine(executeAll))); clearButton.gameObject.GetComponent().color = new Color(170 / 255f, 170 / 255f, 170 / 255f, 1); executeAllButton.gameObject.GetComponent().color = new Color(250 / 255f, 150 / 255f, 150 / 255f, 1); @@ -117,10 +112,10 @@ void Start() } } - Button Add(string title, UnityAction test) + Button Add(string key, string title, UnityAction test) { var newButton = GameObject.Instantiate(clearButton); - newButton.name = title; + newButton.name = key; newButton.onClick.RemoveAllListeners(); newButton.GetComponentInChildren().text = title; newButton.onClick.AddListener(test); @@ -178,249 +173,16 @@ static IEnumerable GetTestTargetTypes() } } -NEXT_ASSEMBLY: + NEXT_ASSEMBLY: continue; } } - public void AddTest(string group, string title, Action test, List setups, List teardowns) - { - List list; - if (!tests.TryGetValue(group, out list)) - { - list = new List(); - tests[group] = list; - } - - list.Add(new TestKeyValuePair(title, test, setups, teardowns)); - } - - public void AddAsyncTest(string group, string title, Func asyncTestCoroutine, List setups, List teardowns) - { - List list; - if (!tests.TryGetValue(group, out list)) - { - list = new List(); - tests[group] = list; - } - - list.Add(new TestKeyValuePair(title, asyncTestCoroutine, setups, teardowns)); - } - public void AddCutomAction(string name, UnityAction action) { additionalActionsOnFirst.Add(new Pair { Name = name, Action = action }); } - - public void RegisterAllMethods() - where T : new() - { - RegisterAllMethods(typeof(T)); - } - - public void RegisterAllMethods(Type testType) - { - try - { - var test = Activator.CreateInstance(testType); - - var methods = testType.GetMethods(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); - List setups = new List(); - List teardowns = new List(); - foreach (var item in methods) - { - try - { - var setup = item.GetCustomAttribute(true); - if (setup != null) - { - setups.Add((Action)Delegate.CreateDelegate(typeof(Action), test, item)); - } - var teardown = item.GetCustomAttribute(true); - if (teardown != null) - { - teardowns.Add((Action)Delegate.CreateDelegate(typeof(Action), test, item)); - } - } - catch (Exception e) - { - UnityEngine.Debug.LogError(testType.Name + "." + item.Name + " failed to register setup/teardown method, exception: " + e.ToString()); - } - } - - foreach (var item in methods) - { - try - { - var iteratorTest = item.GetCustomAttribute(true); - if (iteratorTest != null) - { - if (item.GetParameters().Length == 0 && item.ReturnType == typeof(IEnumerator)) - { - var factory = (Func)Delegate.CreateDelegate(typeof(Func), test, item); - AddAsyncTest(factory.Target.GetType().Name, factory.Method.Name, factory, setups, teardowns); - } - else - { - var testData = GetTestData(item); - if (testData.Count != 0) - { - foreach (var item2 in testData) - { - Func factory; - if (item.IsGenericMethod) - { - var method2 = InferGenericType(item, item2); - factory = () => (IEnumerator)method2.Invoke(test, item2); - } - else - { - factory = () => (IEnumerator)item.Invoke(test, item2); - } - var name = item.Name + "(" + string.Join(", ", item2.Select(x => x?.ToString() ?? "null")) + ")"; - name = name.Replace(Char.MinValue, ' ').Replace(Char.MaxValue, ' ').Replace("<", "[").Replace(">", "]"); - AddAsyncTest(test.GetType().Name, name, factory, setups, teardowns); - } - } - else - { - UnityEngine.Debug.Log(testType.Name + "." + item.Name + " currently does not supported in RuntumeUnitTestToolkit(multiple parameter without TestCase or return type is invalid)."); - } - } - } - - var standardTest = item.GetCustomAttribute(true); - if (standardTest != null) - { - if (item.GetParameters().Length == 0 && item.ReturnType == typeof(void)) - { - var invoke = (Action)Delegate.CreateDelegate(typeof(Action), test, item); - AddTest(invoke.Target.GetType().Name, invoke.Method.Name, invoke, setups, teardowns); - } - else - { - var testData = GetTestData(item); - if (testData.Count != 0) - { - foreach (var item2 in testData) - { - Action invoke = null; - if (item.IsGenericMethod) - { - var method2 = InferGenericType(item, item2); - invoke = () => method2.Invoke(test, item2); - } - else - { - invoke = () => item.Invoke(test, item2); - } - var name = item.Name + "(" + string.Join(", ", item2.Select(x => x?.ToString() ?? "null")) + ")"; - name = name.Replace(Char.MinValue, ' ').Replace(Char.MaxValue, ' ').Replace("<", "[").Replace(">", "]"); - AddTest(test.GetType().Name, name, invoke, setups, teardowns); - } - } - else - { - UnityEngine.Debug.Log(testType.Name + "." + item.Name + " currently does not supported in RuntumeUnitTestToolkit(multiple parameter without TestCase or return type is invalid)."); - } - } - } - } - catch (Exception e) - { - UnityEngine.Debug.LogError(testType.Name + "." + item.Name + " failed to register method, exception: " + e.ToString()); - } - } - } - catch (Exception ex) - { - Debug.LogException(ex); - } - } - - List GetTestData(MethodInfo methodInfo) - { - List testCases = new List(); - - var inlineData = methodInfo.GetCustomAttributes(true); - foreach (var item in inlineData) - { - testCases.Add(item.Arguments); - } - - var sourceData = methodInfo.GetCustomAttributes(true); - foreach (var item in sourceData) - { - var enumerator = GetTestCaseSource(methodInfo, item.SourceType, item.SourceName, item.MethodParams); - foreach (var item2 in enumerator) - { - var item3 = item2 as IEnumerable; // object[][] - if (item3 != null) - { - var l = new List(); - foreach (var item4 in item3) - { - l.Add(item4); - } - testCases.Add(l.ToArray()); - } - } - } - - return testCases; - } - - IEnumerable GetTestCaseSource(MethodInfo method, Type sourceType, string sourceName, object[] methodParams) - { - Type type = sourceType ?? method.DeclaringType; - - MemberInfo[] member = type.GetMember(sourceName, BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); - if (member.Length == 1) - { - MemberInfo memberInfo = member[0]; - FieldInfo fieldInfo = memberInfo as FieldInfo; - if ((object)fieldInfo != null) - { - return (!fieldInfo.IsStatic) ? ReturnErrorAsParameter("The sourceName specified on a TestCaseSourceAttribute must refer to a static field, property or method.") : ((methodParams == null) ? ((IEnumerable)fieldInfo.GetValue(null)) : ReturnErrorAsParameter("You have specified a data source field but also given a set of parameters. Fields cannot take parameters, please revise the 3rd parameter passed to the TestCaseSourceAttribute and either remove it or specify a method.")); - } - PropertyInfo propertyInfo = memberInfo as PropertyInfo; - if ((object)propertyInfo != null) - { - return (!propertyInfo.GetGetMethod(nonPublic: true).IsStatic) ? ReturnErrorAsParameter("The sourceName specified on a TestCaseSourceAttribute must refer to a static field, property or method.") : ((methodParams == null) ? ((IEnumerable)propertyInfo.GetValue(null, null)) : ReturnErrorAsParameter("You have specified a data source property but also given a set of parameters. Properties cannot take parameters, please revise the 3rd parameter passed to the TestCaseSource attribute and either remove it or specify a method.")); - } - MethodInfo methodInfo = memberInfo as MethodInfo; - if ((object)methodInfo != null) - { - return (!methodInfo.IsStatic) ? ReturnErrorAsParameter("The sourceName specified on a TestCaseSourceAttribute must refer to a static field, property or method.") : ((methodParams == null || methodInfo.GetParameters().Length == methodParams.Length) ? ((IEnumerable)methodInfo.Invoke(null, methodParams)) : ReturnErrorAsParameter("You have given the wrong number of arguments to the method in the TestCaseSourceAttribute, please check the number of parameters passed in the object is correct in the 3rd parameter for the TestCaseSourceAttribute and this matches the number of parameters in the target method and try again.")); - } - } - return null; - } - - MethodInfo InferGenericType(MethodInfo methodInfo, object[] parameters) - { - var set = new HashSet(); - List genericParameters = new List(); - foreach (var item in methodInfo.GetParameters() - .Select((x, i) => new { x.ParameterType, i }) - .Where(x => x.ParameterType.IsGenericParameter) - .OrderBy(x => x.ParameterType.GenericParameterPosition)) - { - if (set.Add(item.ParameterType)) // DistinctBy - { - genericParameters.Add(parameters[item.i].GetType()); - } - } - - return methodInfo.MakeGenericMethod(genericParameters.ToArray()); - } - - IEnumerable ReturnErrorAsParameter(string name) - { - throw new Exception(name); - } - System.Collections.IEnumerator ScrollLogToEndNextFrame() { yield return null; @@ -428,13 +190,13 @@ System.Collections.IEnumerator ScrollLogToEndNextFrame() logScrollBar.value = 0; } - IEnumerator RunTestInCoroutine(KeyValuePair> actionList) + IEnumerator RunTestInCoroutine(string key, TestGroup testGroup, bool withoutGlobalSetup) { Button self = null; foreach (var btn in list.GetComponentsInChildren