├── .github ├── dependabot.yml └── workflows │ └── continuous-integration.yml ├── .gitignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── Chisel.sln ├── LICENSE ├── MAINTENANCE.md ├── NuGet.config ├── README.md ├── global.json ├── resources ├── ChiselGraphAlias.png ├── icon.png ├── icon.sh └── icon.svg ├── samples ├── Directory.Build.props ├── Microsoft.Identity.Client │ ├── Microsoft.Identity.Client.csproj │ └── Stubs.cs ├── MongoDbSample │ ├── MongoDbSample.csproj │ ├── Program.cs │ └── Properties │ │ └── launchSettings.json └── SqlClientSample │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ └── SqlClientSample.csproj ├── src └── Chisel │ ├── Chisel.cs │ ├── Chisel.csproj │ ├── DependencyGraph.cs │ ├── GraphDirection.cs │ ├── GraphOptions.cs │ ├── GraphWriter.Graphviz.cs │ ├── GraphWriter.Mermaid.cs │ ├── GraphWriter.cs │ ├── LockFileExtensions.cs │ ├── Package.cs │ ├── PackageState.cs │ ├── SdkAssemblyResolver.cs │ ├── build │ ├── Chisel.props │ └── Chisel.targets │ └── packages.lock.json └── tests ├── Chisel.Tests ├── Chisel.Tests.csproj ├── ChiseledAppTests.RunTestApp_non-windows.verified.mermaid ├── ChiseledAppTests.RunTestApp_windows.verified.mermaid ├── ChiseledAppTests.cs ├── DependencyGraphTest.MongoDbGraph_writeIgnoredPackages=False_format=graphviz.verified.gv ├── DependencyGraphTest.MongoDbGraph_writeIgnoredPackages=False_format=mermaid.verified.mmd ├── DependencyGraphTest.MongoDbGraph_writeIgnoredPackages=True_format=graphviz.verified.gv ├── DependencyGraphTest.MongoDbGraph_writeIgnoredPackages=True_format=mermaid.verified.mmd ├── DependencyGraphTest.PollyGraphIgnoreGlob_graphviz.verified.gv ├── DependencyGraphTest.PollyGraphIgnoreGlob_mermaid.verified.mmd ├── DependencyGraphTest.SqlClientGraph_graphviz.verified.gv ├── DependencyGraphTest.SqlClientGraph_mermaid.verified.mmd ├── DependencyGraphTest.cs ├── ProjectAssets │ ├── MongoDbGraph.json │ ├── PollyGraph.json │ ├── SqlClientGraph-InvalidProjectVersion.json │ └── SqlClientGraph.json ├── SdkAssemblyResolverTest.cs ├── Support │ ├── DirectoryInfoExtensions.cs │ ├── PublishMode.cs │ └── TestApp.cs └── xunit.runner.json └── TestApp ├── Program.cs ├── TestApp.csproj └── nuget.config /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 2 | 3 | version: 2 4 | updates: 5 | - package-ecosystem: "nuget" 6 | directory: "/" 7 | schedule: 8 | interval: "daily" 9 | -------------------------------------------------------------------------------- /.github/workflows/continuous-integration.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | 3 | on: push 4 | 5 | env: 6 | Configuration: Release 7 | ContinuousIntegrationBuild: true 8 | DOTNET_CLI_TELEMETRY_OPTOUT: true 9 | DOTNET_NOLOGO: true 10 | DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION: true 11 | TERM: xterm 12 | 13 | jobs: 14 | package: 15 | strategy: 16 | matrix: 17 | os: [ macos-latest, ubuntu-latest, windows-latest ] 18 | fail-fast: false 19 | runs-on: ${{ matrix.os }} 20 | permissions: 21 | checks: write 22 | name: 🛠 Build, test and pack 23 | steps: 24 | - name: 🧑‍💻 Checkout git repository 25 | uses: actions/checkout@v4 26 | with: 27 | fetch-depth: 0 28 | - name: 🏎 Optimize Windows runner 29 | if: matrix.os == 'windows-latest' 30 | run: | 31 | echo "DOTNET_INSTALL_DIR=D:\dotnet" >> $env:GITHUB_ENV 32 | echo "NUGET_PACKAGES=D:\nuget" >> $env:GITHUB_ENV 33 | - name: 🧑‍🔧 Install .NET SDK 34 | uses: actions/setup-dotnet@v4 35 | - name: ℹ️ Show .NET info 36 | run: dotnet --info 37 | - name: 💾 Retrieve cached NuGet packages 38 | uses: actions/cache@v4 39 | with: 40 | path: ~/.nuget/packages 41 | key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} 42 | restore-keys: | 43 | ${{ runner.os }}-nuget- 44 | - name: ⚙️ Restore NuGet packages 45 | run: dotnet restore 46 | - name: 🏗 Build solution 47 | run: dotnet build --no-restore 48 | - name: 🧪 Run tests 49 | run: dotnet test --no-build 50 | - name: 📤 Upload received files from failing tests 51 | uses: actions/upload-artifact@v4 52 | if: failure() 53 | with: 54 | name: Received-${{ runner.os }} 55 | path: "**/*.received.*" 56 | - name: 📊 Test Report 57 | uses: dorny/test-reporter@v1 58 | if: always() 59 | with: 60 | name: Test Results (${{ runner.os }}) 61 | path: TestResults-*.trx 62 | reporter: dotnet-trx 63 | - name: 📤 Upload NuGet package artifact 64 | if: matrix.os == 'ubuntu-latest' 65 | uses: actions/upload-artifact@v4 66 | with: 67 | name: NuGet package 68 | path: "*.nupkg" 69 | - name: 📝 Retrieve release notes from tag 70 | if: matrix.os == 'ubuntu-latest' && startsWith(github.ref, 'refs/tags/') 71 | run: | 72 | git fetch --tags --force 73 | git tag --list ${{ github.ref_name }} --format='%(contents)' > ReleaseNotes.md 74 | - name: 📤 Upload release notes 75 | if: matrix.os == 'ubuntu-latest' && startsWith(github.ref, 'refs/tags/') 76 | uses: actions/upload-artifact@v4 77 | with: 78 | name: Release Notes 79 | path: ReleaseNotes.md 80 | publish: 81 | runs-on: macos-latest 82 | needs: package 83 | if: startsWith(github.ref, 'refs/tags/') 84 | permissions: 85 | contents: write 86 | name: 🐿 Publish 87 | steps: 88 | - name: 📥 Download NuGet package artifact 89 | uses: actions/download-artifact@v4 90 | with: 91 | name: NuGet package 92 | - name: 📥 Download release notes artifact 93 | uses: actions/download-artifact@v4 94 | with: 95 | name: Release Notes 96 | - name: 🚢 Create GitHub Release 97 | uses: softprops/action-gh-release@v2 98 | with: 99 | name: Version ${{ github.ref_name }} 100 | body_path: ReleaseNotes.md 101 | prerelease: ${{ contains(github.ref_name, '-') }} 102 | files: "*.nupkg" 103 | - name: 🚀 Publish NuGet package on nuget.org 104 | run: dotnet nuget push "*.nupkg" --source https://api.nuget.org/v3/index.json --api-key "${{ secrets.NUGET_API_KEY }}" 105 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.user 2 | bin/ 3 | obj/ 4 | src/Chisel/tasks/ 5 | .idea/ 6 | .vs/ 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [1.1.2][1.1.2] - 2025-02-17 8 | 9 | * Chisel is now disabled by default during [design-time builds](https://github.com/dotnet/project-system/blob/main/docs/design-time-builds.md). 10 | * Fixed a bug where Chisel would fail with a `MissingMethodException` when using the .NET SDK 9.0.200. 11 | ``` 12 | Method not found: 'System.Collections.Generic.IList`1 NuGet.ProjectModel.TargetFrameworkInformation.get_Dependencies()'. 13 | at Chisel.LockFileExtensions.ReadPackages(LockFile lockFile, String tfm, String rid, Predicate`1 filter) 14 | at Chisel.Chisel.ProcessGraph() in /_/src/Chisel/Chisel.cs:line 178 15 | at Chisel.Chisel.Execute() in /_/src/Chisel/Chisel.cs:line 137 16 | ``` 17 | 18 | ## [1.1.1][1.1.1] - 2024-11-07 19 | 20 | * Fixed a bug where Chisel could fail in Visual Studio with a `MissingMethodException`. 21 | ``` 22 | Method not found: 'NuGet.Frameworks.NuGetFramework NuGet.ProjectModel.TargetFrameworkInformation.get_FrameworkName()'. 23 | at Chisel.LockFileExtensions.ReadPackages(LockFile lockFile, String tfm, String rid, Predicate`1 filter) 24 | at Chisel.Chisel.ProcessGraph() in /_/src/Chisel/Chisel.cs:line 161 25 | at Chisel.Chisel.Execute() in /_/src/Chisel/Chisel.cs:line 140 26 | ``` 27 | 28 | This issue was only affecting Visual Studio. Rider and the `dotnet` command line interface were not affected. 29 | 30 | ## [1.1.0][1.1.0] - 2024-09-13 31 | 32 | * The `ChiselGraphIgnore` items now support simple globbing. For example to ignore all packages starting with `System.` in the graph, use the following syntax: 33 | 34 | ```xml 35 | 36 | 37 | 38 | ``` 39 | 40 | * The dependency graph roots (i.e. direct package and project references) are now identified with an hexagon shape and stronger borders. 41 | 42 | ```mermaid 43 | graph 44 | 45 | classDef root stroke-width:4px 46 | classDef default fill:aquamarine,stroke:#009061,color:#333333 47 | classDef removed fill:lightcoral,stroke:#A42A2A 48 | 49 | Azure.Identity --> Azure.Core 50 | Microsoft.Data.SqlClient{{Microsoft.Data.SqlClient}} --> Azure.Identity 51 | 52 | class Azure.Core removed 53 | class Azure.Identity removed 54 | class Microsoft.Data.SqlClient root 55 | class Microsoft.Data.SqlClient default 56 | ``` 57 | 58 | ## [1.0.0][1.0.0] - 2024-04-12 59 | 60 | * Fix a crash when MSBuild is running on the desktop .NET Framework 61 | 62 | ## [1.0.0-rc.2][1.0.0-rc.2] - 2024-03-16 63 | 64 | * The wording of some warnings has been improved 65 | * The README has a paragraph on removing the Azure SDK from `Microsoft.Data.SqlClient` 66 | * Readability of Mermaid graphs in dark mode has been improved 67 | 68 | ## [1.0.0-rc.1][1.0.0-rc.1] - 2024-03-15 69 | 70 | Initial release on NuGet 71 | 72 | [1.1.2]: https://github.com/0xced/Chisel/compare/1.1.1...1.1.2 73 | [1.1.1]: https://github.com/0xced/Chisel/compare/1.1.0...1.1.1 74 | [1.1.0]: https://github.com/0xced/Chisel/compare/1.0.0...1.1.0 75 | [1.0.0]: https://github.com/0xced/Chisel/compare/1.0.0-rc.2...1.0.0 76 | [1.0.0-rc.2]: https://github.com/0xced/Chisel/compare/1.0.0-rc.1...1.0.0-rc.2 77 | [1.0.0-rc.1]: https://github.com/0xced/Chisel/releases/tag/1.0.0-rc.1 78 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | cedric.luthi@gmail.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | For answers to common questions about this code of conduct, see the FAQ at 125 | https://www.contributor-covenant.org/faq. Translations are available at 126 | https://www.contributor-covenant.org/translations. 127 | 128 | [homepage]: https://www.contributor-covenant.org 129 | -------------------------------------------------------------------------------- /Chisel.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "meta", "meta", "{13D33453-8FDA-4FBC-863A-24383232D951}" 4 | ProjectSection(SolutionItems) = preProject 5 | CHANGELOG.md = CHANGELOG.md 6 | CODE_OF_CONDUCT.md = CODE_OF_CONDUCT.md 7 | .github\workflows\continuous-integration.yml = .github\workflows\continuous-integration.yml 8 | .github\dependabot.yml = .github\dependabot.yml 9 | global.json = global.json 10 | LICENSE = LICENSE 11 | MAINTENANCE.md = MAINTENANCE.md 12 | NuGet.config = NuGet.config 13 | src\Chisel\packages.lock.json = src\Chisel\packages.lock.json 14 | README.md = README.md 15 | EndProjectSection 16 | EndProject 17 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chisel", "src\Chisel\Chisel.csproj", "{F4CAEC64-3B0C-4ACD-BF87-760A838A5D86}" 18 | EndProject 19 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chisel.Tests", "tests\Chisel.Tests\Chisel.Tests.csproj", "{EC3CCB92-1AA1-4C33-B296-D7111EEF84E4}" 20 | EndProject 21 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{0CC84E67-19D2-480B-B36A-6BB15A9109E7}" 22 | EndProject 23 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MongoDbSample", "samples\MongoDbSample\MongoDbSample.csproj", "{845EDA2A-5207-4C6D-ABE9-9635F4630D90}" 24 | EndProject 25 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{89268D80-B21D-4C76-AF7F-796AAD1E00D9}" 26 | EndProject 27 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{AC8C6685-EDF9-443A-BAF6-A5E7CF777B2A}" 28 | EndProject 29 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestApp", "tests\TestApp\TestApp.csproj", "{8B1B3D6A-7100-4DFB-97C9-CF5ACF1A3B08}" 30 | EndProject 31 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SqlClientSample", "samples\SqlClientSample\SqlClientSample.csproj", "{611D4DE0-F729-48A6-A496-2EA3B5DF8EC6}" 32 | EndProject 33 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Identity.Client", "samples\Microsoft.Identity.Client\Microsoft.Identity.Client.csproj", "{F40FA01A-EB18-4785-9A3C-379F2E7A7A02}" 34 | EndProject 35 | Global 36 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 37 | Debug|Any CPU = Debug|Any CPU 38 | Release|Any CPU = Release|Any CPU 39 | EndGlobalSection 40 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 41 | {F4CAEC64-3B0C-4ACD-BF87-760A838A5D86}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 42 | {F4CAEC64-3B0C-4ACD-BF87-760A838A5D86}.Debug|Any CPU.Build.0 = Debug|Any CPU 43 | {F4CAEC64-3B0C-4ACD-BF87-760A838A5D86}.Release|Any CPU.ActiveCfg = Release|Any CPU 44 | {F4CAEC64-3B0C-4ACD-BF87-760A838A5D86}.Release|Any CPU.Build.0 = Release|Any CPU 45 | {EC3CCB92-1AA1-4C33-B296-D7111EEF84E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 46 | {EC3CCB92-1AA1-4C33-B296-D7111EEF84E4}.Debug|Any CPU.Build.0 = Debug|Any CPU 47 | {EC3CCB92-1AA1-4C33-B296-D7111EEF84E4}.Release|Any CPU.ActiveCfg = Release|Any CPU 48 | {EC3CCB92-1AA1-4C33-B296-D7111EEF84E4}.Release|Any CPU.Build.0 = Release|Any CPU 49 | {845EDA2A-5207-4C6D-ABE9-9635F4630D90}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 50 | {845EDA2A-5207-4C6D-ABE9-9635F4630D90}.Debug|Any CPU.Build.0 = Debug|Any CPU 51 | {845EDA2A-5207-4C6D-ABE9-9635F4630D90}.Release|Any CPU.ActiveCfg = Release|Any CPU 52 | {845EDA2A-5207-4C6D-ABE9-9635F4630D90}.Release|Any CPU.Build.0 = Release|Any CPU 53 | {8B1B3D6A-7100-4DFB-97C9-CF5ACF1A3B08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 54 | {8B1B3D6A-7100-4DFB-97C9-CF5ACF1A3B08}.Release|Any CPU.ActiveCfg = Release|Any CPU 55 | {611D4DE0-F729-48A6-A496-2EA3B5DF8EC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 56 | {611D4DE0-F729-48A6-A496-2EA3B5DF8EC6}.Debug|Any CPU.Build.0 = Debug|Any CPU 57 | {611D4DE0-F729-48A6-A496-2EA3B5DF8EC6}.Release|Any CPU.ActiveCfg = Release|Any CPU 58 | {611D4DE0-F729-48A6-A496-2EA3B5DF8EC6}.Release|Any CPU.Build.0 = Release|Any CPU 59 | {F40FA01A-EB18-4785-9A3C-379F2E7A7A02}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 60 | {F40FA01A-EB18-4785-9A3C-379F2E7A7A02}.Debug|Any CPU.Build.0 = Debug|Any CPU 61 | {F40FA01A-EB18-4785-9A3C-379F2E7A7A02}.Release|Any CPU.ActiveCfg = Release|Any CPU 62 | {F40FA01A-EB18-4785-9A3C-379F2E7A7A02}.Release|Any CPU.Build.0 = Release|Any CPU 63 | EndGlobalSection 64 | GlobalSection(NestedProjects) = preSolution 65 | {845EDA2A-5207-4C6D-ABE9-9635F4630D90} = {0CC84E67-19D2-480B-B36A-6BB15A9109E7} 66 | {F4CAEC64-3B0C-4ACD-BF87-760A838A5D86} = {89268D80-B21D-4C76-AF7F-796AAD1E00D9} 67 | {EC3CCB92-1AA1-4C33-B296-D7111EEF84E4} = {AC8C6685-EDF9-443A-BAF6-A5E7CF777B2A} 68 | {8B1B3D6A-7100-4DFB-97C9-CF5ACF1A3B08} = {AC8C6685-EDF9-443A-BAF6-A5E7CF777B2A} 69 | {611D4DE0-F729-48A6-A496-2EA3B5DF8EC6} = {0CC84E67-19D2-480B-B36A-6BB15A9109E7} 70 | {F40FA01A-EB18-4785-9A3C-379F2E7A7A02} = {0CC84E67-19D2-480B-B36A-6BB15A9109E7} 71 | EndGlobalSection 72 | EndGlobal 73 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Cédric Luthi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MAINTENANCE.md: -------------------------------------------------------------------------------- 1 | ## Creating a release 2 | 3 | **Chisel** uses [MinVer](https://github.com/adamralph/minver) for its versioning, so a tag must exist with the chosen semantic version number in order to create an official release. 4 | 5 | 1. Update the [CHANGELOG](CHANGELOG.md) *Unreleased* section to the chosen version and copy the release notes for step #2. 6 | 7 | - Add the release date 8 | - Update the link from `HEAD` to the chosen version 9 | 10 | 2. Create an **[annotated](https://stackoverflow.com/questions/11514075/what-is-the-difference-between-an-annotated-and-unannotated-tag/25996877#25996877)** tag, the (multi-line) message of the annotated tag will be the content of the GitHub release. Markdown (copied from step #1) should be used. 11 | 12 | `git tag --annotate 1.0.0` 13 | 14 | 3. Push the `main` branch and ensure that the [build is successful](https://github.com/0xced/Chisel/actions). 15 | 16 | `git push` 17 | 18 | 4. [Push the tag](https://stackoverflow.com/questions/5195859/how-do-you-push-a-tag-to-a-remote-repository-using-git/26438076#26438076) 19 | 20 | `git push --follow-tags` 21 | 22 | Once pushed, the GitHub [Continuous Integration](https://github.com/0xced/Chisel/blob/main/.github/workflows/continuous-integration.yml) workflow takes care of building, running the tests, creating the NuGet package, creating the GitHub release and finally publishing the produced NuGet package. -------------------------------------------------------------------------------- /NuGet.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Chisel icon 3 |

4 | 5 | **Chisel** provides a way to remove unwanted dependencies from your dotnet projects. 6 | 7 | [![NuGet](https://img.shields.io/nuget/v/Chisel.svg?label=NuGet&logo=NuGet)](https://www.nuget.org/packages/Chisel/) [![Continuous Integration](https://img.shields.io/github/actions/workflow/status/0xced/Chisel/continuous-integration.yml?branch=main&label=Continuous%20Integration&logo=GitHub)](https://github.com/0xced/Chisel/actions/workflows/continuous-integration.yml) 8 | 9 | Chisel was born because some database drivers can't resist taking dependencies on cloud libraries. The [MongoDB driver](https://www.nuget.org/packages/MongoDB.Driver) (version 2.*) depends on the ASW SDK for authentication with Identity and Access Management (IAM) and [Microsoft's SQL Server driver](https://www.nuget.org/packages/Microsoft.Data.SqlClient) depends on the Azure SDK for authentication with the Microsoft identity platform (formerly Azure AD). 10 | 11 | Users have asked for separate NuGet packages for both MongoDB ([issue #4635](https://jira.mongodb.org/browse/CSHARP-4635)) and SqlClient ([issue #1108](https://github.com/dotnet/SqlClient/issues/1108)) but as of `MongoDB.Driver` 2.* and `Microsoft.Data.SqlClient` 6.* the cloud dependencies are unavoidable, even if MongoDB or SQL Server is used on premises (where cloud authentication is obviously not needed). 12 | 13 | > [!NOTE] 14 | > Chisel is no longer needed starting with version 3.0.0 of the `MongoDB.Driver` package since AWS authentication has been moved into a new optional [MongoDB.Driver.Authentication.AWS](https://www.nuget.org/packages/MongoDB.Driver.Authentication.AWS) package. See [Version 3.0 Breaking Changes](https://www.mongodb.com/docs/drivers/csharp/current/upgrade/v3/#version-3.0-breaking-changes) for more information. 15 | 16 | Enter Chisel to remove those dependencies and save some precious megabytes. 17 | 18 | ## Getting started 19 | 20 | Add the [Chisel](https://www.nuget.org/packages/Chisel/) NuGet package to your project using the NuGet Package Manager or run the following command: 21 | 22 | ```sh 23 | dotnet add package Chisel 24 | ``` 25 | 26 | While Chisel's main purpose is removing unwanted dependencies from existing NuGet packages, it provides another great feature: a graph of your project's dependencies. After adding Chisel, a [Mermaid](https://mermaid.js.org) (or [Graphviz](https://graphviz.org)) graph is written in the intermediate output path (aka the `obj` directory). 27 | 28 | For a project referencing `Hangfire.PostgreSql` and `Npgsql.EntityFrameworkCore.PostgreSQL` the graph would look like this. 29 | 30 | ```mermaid 31 | graph LR 32 | 33 | classDef root stroke-width:4px 34 | classDef default fill:aquamarine,stroke:#009061,color:#333333 35 | 36 | Hangfire.Core --> Newtonsoft.Json 37 | Hangfire.PostgreSql{{Hangfire.PostgreSql}} --> Dapper 38 | Hangfire.PostgreSql{{Hangfire.PostgreSql}} --> Hangfire.Core 39 | Hangfire.PostgreSql{{Hangfire.PostgreSql}} --> Npgsql 40 | Microsoft.EntityFrameworkCore --> Microsoft.EntityFrameworkCore.Abstractions 41 | Microsoft.EntityFrameworkCore --> Microsoft.Extensions.Caching.Memory 42 | Microsoft.EntityFrameworkCore --> Microsoft.Extensions.Logging 43 | Microsoft.EntityFrameworkCore.Relational --> Microsoft.EntityFrameworkCore 44 | Microsoft.EntityFrameworkCore.Relational --> Microsoft.Extensions.Configuration.Abstractions 45 | Microsoft.Extensions.Caching.Abstractions --> Microsoft.Extensions.Primitives 46 | Microsoft.Extensions.Caching.Memory --> Microsoft.Extensions.Caching.Abstractions 47 | Microsoft.Extensions.Caching.Memory --> Microsoft.Extensions.DependencyInjection.Abstractions 48 | Microsoft.Extensions.Caching.Memory --> Microsoft.Extensions.Logging.Abstractions 49 | Microsoft.Extensions.Caching.Memory --> Microsoft.Extensions.Options 50 | Microsoft.Extensions.Caching.Memory --> Microsoft.Extensions.Primitives 51 | Microsoft.Extensions.Configuration.Abstractions --> Microsoft.Extensions.Primitives 52 | Microsoft.Extensions.DependencyInjection --> Microsoft.Extensions.DependencyInjection.Abstractions 53 | Microsoft.Extensions.Logging --> Microsoft.Extensions.DependencyInjection 54 | Microsoft.Extensions.Logging --> Microsoft.Extensions.Logging.Abstractions 55 | Microsoft.Extensions.Logging --> Microsoft.Extensions.Options 56 | Microsoft.Extensions.Logging.Abstractions --> Microsoft.Extensions.DependencyInjection.Abstractions 57 | Microsoft.Extensions.Options --> Microsoft.Extensions.DependencyInjection.Abstractions 58 | Microsoft.Extensions.Options --> Microsoft.Extensions.Primitives 59 | Npgsql --> Microsoft.Extensions.Logging.Abstractions 60 | Npgsql.EntityFrameworkCore.PostgreSQL{{Npgsql.EntityFrameworkCore.PostgreSQL}} --> Microsoft.EntityFrameworkCore 61 | Npgsql.EntityFrameworkCore.PostgreSQL{{Npgsql.EntityFrameworkCore.PostgreSQL}} --> Microsoft.EntityFrameworkCore.Abstractions 62 | Npgsql.EntityFrameworkCore.PostgreSQL{{Npgsql.EntityFrameworkCore.PostgreSQL}} --> Microsoft.EntityFrameworkCore.Relational 63 | Npgsql.EntityFrameworkCore.PostgreSQL{{Npgsql.EntityFrameworkCore.PostgreSQL}} --> Npgsql 64 | 65 | class Hangfire.PostgreSql root 66 | class Npgsql.EntityFrameworkCore.PostgreSQL root 67 | ``` 68 | 69 | Mermaid graphs can be used directly in [markdown files on GitHub](https://github.blog/2022-02-14-include-diagrams-markdown-files-mermaid/) and are rendered as graphs, just like the one just above. Or they can also be edited, previewed and shared with the [Mermaid live editor](https://mermaid.live/). 70 | 71 | Graphviz (DOT) files can be written instead by setting the `ChiselGraphName` property to a name that ends with `.gv`: 72 | 73 | ```xml 74 | 75 | $(MSBuildProjectName).Chisel.gv 76 | 77 | ``` 78 | 79 | Graphviz files can be visualized and shared online with [Edotor](https://edotor.net) or locally with the excellent [Graphviz Interactive Preview](https://marketplace.visualstudio.com/items?itemName=tintinweb.graphviz-interactive-preview) extension for Visual Studio Code. 80 | 81 | > [!WARNING] 82 | > While this technique has been sucessfully tested with the MongDB driver and the SQL Server driver, removing dependencies from a package might lead to exceptions at runtime. Make sure to properly test your application. 83 | 84 | ## Removing the AWS SDK from `MongoDB.Driver` version 2.* 85 | 86 | After adding the `Chisel` package to your project, tell it to remove the `AWSSDK.SecurityToken` dependency with the `ChiselPackage` property. 87 | 88 | ```xml 89 | 90 | AWSSDK.SecurityToken 91 | 92 | ``` 93 | 94 | Specifying the _direct_ dependencies is enough. Looking at the produced graph confirms that Chisel figured out the transitive dependencies by itself (there's only `AWSSDK.Core` in this scenario). 95 | 96 | ```mermaid 97 | graph LR 98 | 99 | classDef root stroke-width:4px 100 | classDef default fill:aquamarine,stroke:#009061,color:#333333 101 | classDef removed fill:lightcoral,stroke:#A42A2A 102 | 103 | AWSSDK.SecurityToken/3.7.100.14 --> AWSSDK.Core/3.7.100.14 104 | DnsClient/1.6.1 --> Microsoft.Win32.Registry/5.0.0 105 | Microsoft.Win32.Registry/5.0.0 --> System.Security.AccessControl/5.0.0 106 | Microsoft.Win32.Registry/5.0.0 --> System.Security.Principal.Windows/5.0.0 107 | MongoDB.Bson/2.30.0 --> System.Runtime.CompilerServices.Unsafe/5.0.0 108 | MongoDB.Driver/2.30.0{{MongoDB.Driver/2.30.0}} --> Microsoft.Extensions.Logging.Abstractions/6.0.4 109 | MongoDB.Driver/2.30.0{{MongoDB.Driver/2.30.0}} --> MongoDB.Bson/2.30.0 110 | MongoDB.Driver/2.30.0{{MongoDB.Driver/2.30.0}} --> MongoDB.Driver.Core/2.30.0 111 | MongoDB.Driver/2.30.0{{MongoDB.Driver/2.30.0}} --> MongoDB.Libmongocrypt/1.12.0 112 | MongoDB.Driver.Core/2.30.0 --> AWSSDK.SecurityToken/3.7.100.14 113 | MongoDB.Driver.Core/2.30.0 --> DnsClient/1.6.1 114 | MongoDB.Driver.Core/2.30.0 --> Microsoft.Extensions.Logging.Abstractions/6.0.4 115 | MongoDB.Driver.Core/2.30.0 --> MongoDB.Bson/2.30.0 116 | MongoDB.Driver.Core/2.30.0 --> MongoDB.Libmongocrypt/1.12.0 117 | MongoDB.Driver.Core/2.30.0 --> SharpCompress/0.30.1 118 | MongoDB.Driver.Core/2.30.0 --> Snappier/1.0.0 119 | MongoDB.Driver.Core/2.30.0 --> ZstdSharp.Port/0.7.3 120 | System.Security.AccessControl/5.0.0 --> System.Security.Principal.Windows/5.0.0 121 | 122 | class AWSSDK.Core/3.7.100.14 removed 123 | class AWSSDK.SecurityToken/3.7.100.14 removed 124 | class DnsClient/1.6.1 default 125 | class Microsoft.Extensions.Logging.Abstractions/6.0.4 default 126 | class Microsoft.Win32.Registry/5.0.0 default 127 | class MongoDB.Bson/2.30.0 default 128 | class MongoDB.Driver/2.30.0 root 129 | class MongoDB.Driver/2.30.0 default 130 | class MongoDB.Driver.Core/2.30.0 default 131 | class MongoDB.Libmongocrypt/1.12.0 default 132 | class SharpCompress/0.30.1 default 133 | class Snappier/1.0.0 default 134 | class System.Runtime.CompilerServices.Unsafe/5.0.0 default 135 | class System.Security.AccessControl/5.0.0 default 136 | class System.Security.Principal.Windows/5.0.0 default 137 | class ZstdSharp.Port/0.7.3 default 138 | ``` 139 | 140 | Now, both `AWSSDK.Core.dll` and `AWSSDK.SecurityToken.dll` have disappeared from the build output. 141 | 142 | As long as the [MONGODB-AWS authentication mechanism](https://www.mongodb.com/docs/drivers/csharp/current/fundamentals/authentication/#std-label-csharp-mongodb-aws) is not used everything will work fine. See the `MongoDbSample` project in the `samples` directory. 143 | 144 | ## Removing the Azure SDK from `Microsoft.Data.SqlClient` version 6.* 145 | 146 | Getting rid of the Azure/Microsoft Identity bits requires defining three packages to remove. In the previous example, `` was used as an MSBuild property. Here, it's used as an MSBuild item (i.e. with the `Include` attribute) to specify multiple packages. 147 | 148 | ```xml 149 | 150 | 151 | 152 | 153 | 154 | ``` 155 | 156 | As with the MongoDB driver, specifying the three _direct_ dependencies is enough. We can see in the produced graph that the `Microsoft.Identity*` libraries have many transitive dependencies which are also removed (in red). 157 | 158 | ```mermaid 159 | graph LR 160 | 161 | classDef root stroke-width:4px 162 | classDef default fill:aquamarine,stroke:#009061,color:#333333 163 | classDef removed fill:lightcoral,stroke:#A42A2A 164 | 165 | Azure.Core --> Microsoft.Bcl.AsyncInterfaces 166 | Azure.Core --> System.ClientModel 167 | Azure.Core --> System.Diagnostics.DiagnosticSource 168 | Azure.Core --> System.Memory.Data 169 | Azure.Core --> System.Text.Encodings.Web 170 | Azure.Core --> System.Text.Json 171 | Azure.Identity --> Azure.Core 172 | Azure.Identity --> Microsoft.Identity.Client 173 | Azure.Identity --> Microsoft.Identity.Client.Extensions.Msal 174 | Azure.Identity --> System.Security.Cryptography.ProtectedData 175 | Azure.Identity --> System.Text.Json 176 | Microsoft.Data.SqlClient{{Microsoft.Data.SqlClient}} --> Azure.Identity 177 | Microsoft.Data.SqlClient{{Microsoft.Data.SqlClient}} --> Microsoft.Bcl.Cryptography 178 | Microsoft.Data.SqlClient{{Microsoft.Data.SqlClient}} --> Microsoft.Extensions.Caching.Memory 179 | Microsoft.Data.SqlClient{{Microsoft.Data.SqlClient}} --> Microsoft.IdentityModel.JsonWebTokens 180 | Microsoft.Data.SqlClient{{Microsoft.Data.SqlClient}} --> Microsoft.IdentityModel.Protocols.OpenIdConnect 181 | Microsoft.Data.SqlClient{{Microsoft.Data.SqlClient}} --> Microsoft.SqlServer.Server 182 | Microsoft.Data.SqlClient{{Microsoft.Data.SqlClient}} --> System.Configuration.ConfigurationManager 183 | Microsoft.Data.SqlClient{{Microsoft.Data.SqlClient}} --> System.Security.Cryptography.Pkcs 184 | Microsoft.Extensions.Caching.Abstractions --> Microsoft.Extensions.Primitives 185 | Microsoft.Extensions.Caching.Memory --> Microsoft.Extensions.Caching.Abstractions 186 | Microsoft.Extensions.Caching.Memory --> Microsoft.Extensions.DependencyInjection.Abstractions 187 | Microsoft.Extensions.Caching.Memory --> Microsoft.Extensions.Logging.Abstractions 188 | Microsoft.Extensions.Caching.Memory --> Microsoft.Extensions.Options 189 | Microsoft.Extensions.Caching.Memory --> Microsoft.Extensions.Primitives 190 | Microsoft.Extensions.Logging.Abstractions --> Microsoft.Extensions.DependencyInjection.Abstractions 191 | Microsoft.Extensions.Options --> Microsoft.Extensions.DependencyInjection.Abstractions 192 | Microsoft.Extensions.Options --> Microsoft.Extensions.Primitives 193 | Microsoft.Identity.Client --> Microsoft.IdentityModel.Abstractions 194 | Microsoft.Identity.Client --> System.Diagnostics.DiagnosticSource 195 | Microsoft.Identity.Client.Extensions.Msal --> Microsoft.Identity.Client 196 | Microsoft.Identity.Client.Extensions.Msal --> System.Security.Cryptography.ProtectedData 197 | Microsoft.IdentityModel.JsonWebTokens --> Microsoft.IdentityModel.Tokens 198 | Microsoft.IdentityModel.Logging --> Microsoft.IdentityModel.Abstractions 199 | Microsoft.IdentityModel.Protocols --> Microsoft.IdentityModel.Tokens 200 | Microsoft.IdentityModel.Protocols.OpenIdConnect --> Microsoft.IdentityModel.Protocols 201 | Microsoft.IdentityModel.Protocols.OpenIdConnect --> System.IdentityModel.Tokens.Jwt 202 | Microsoft.IdentityModel.Tokens --> Microsoft.IdentityModel.Logging 203 | System.ClientModel --> System.Memory.Data 204 | System.ClientModel --> System.Text.Json 205 | System.Configuration.ConfigurationManager --> System.Diagnostics.EventLog 206 | System.Configuration.ConfigurationManager --> System.Security.Cryptography.ProtectedData 207 | System.Diagnostics.DiagnosticSource --> System.Runtime.CompilerServices.Unsafe 208 | System.IdentityModel.Tokens.Jwt --> Microsoft.IdentityModel.JsonWebTokens 209 | System.IdentityModel.Tokens.Jwt --> Microsoft.IdentityModel.Tokens 210 | System.Memory.Data --> System.Text.Encodings.Web 211 | System.Memory.Data --> System.Text.Json 212 | 213 | class Azure.Core removed 214 | class Azure.Identity removed 215 | class Microsoft.Bcl.AsyncInterfaces removed 216 | class Microsoft.Bcl.Cryptography default 217 | class Microsoft.Data.SqlClient root 218 | class Microsoft.Data.SqlClient default 219 | class Microsoft.Extensions.Caching.Abstractions default 220 | class Microsoft.Extensions.Caching.Memory default 221 | class Microsoft.Extensions.DependencyInjection.Abstractions default 222 | class Microsoft.Extensions.Logging.Abstractions default 223 | class Microsoft.Extensions.Options default 224 | class Microsoft.Extensions.Primitives default 225 | class Microsoft.Identity.Client removed 226 | class Microsoft.Identity.Client.Extensions.Msal removed 227 | class Microsoft.IdentityModel.Abstractions removed 228 | class Microsoft.IdentityModel.JsonWebTokens removed 229 | class Microsoft.IdentityModel.Logging removed 230 | class Microsoft.IdentityModel.Protocols removed 231 | class Microsoft.IdentityModel.Protocols.OpenIdConnect removed 232 | class Microsoft.IdentityModel.Tokens removed 233 | class Microsoft.SqlServer.Server default 234 | class System.ClientModel removed 235 | class System.Configuration.ConfigurationManager default 236 | class System.Diagnostics.DiagnosticSource removed 237 | class System.Diagnostics.EventLog default 238 | class System.IdentityModel.Tokens.Jwt removed 239 | class System.Memory.Data removed 240 | class System.Runtime.CompilerServices.Unsafe removed 241 | class System.Security.Cryptography.Pkcs default 242 | class System.Security.Cryptography.ProtectedData default 243 | class System.Text.Encodings.Web removed 244 | class System.Text.Json removed 245 | ``` 246 | 247 | For `Microsoft.Data.SqlClient` prior to version 6, an additional trick is required to get rid of the `Microsoft.Identity.Client` dependency. See a [previous version](https://github.com/0xced/Chisel/blob/1.1.1/README.md#removing-the-azure-sdk-from-microsoftdatasqlclient) of the README for more information. 248 | 249 | On macOS, removing the Azure authentication libraries and its dependencies reduces the size of the dlls from 5.6 MiB down to 3.1 MiB. In other words, the Azure librarires are responsible fore almost 50% of the size of the `Microsoft.Data.SqlClient` (6.0.1) package. 250 | 251 | ```mermaid 252 | %%{ init: { 'theme': 'base', 'themeVariables': { 'pie1': 'lightcoral', 'pie2': 'aquamarine' } } }%% 253 | pie title Microsoft.Data.SqlClient composition 254 | "Azure/authentication libraries" : 2544 255 | "SqlClient core libraries" : 3170 256 | ``` 257 | 258 | Have a look at the `SqlClientSample` project in the `samples` directory for a concrete example. 259 | 260 | ## Advanced configuration 261 | 262 | Here are all the MSBuild properties and items supported by Chisel. 263 | 264 | ### `ChiselEnabled` 265 | 266 | defaults to `true` for standard builds and `false` for [design-time builds](https://github.com/dotnet/project-system/blob/main/docs/design-time-builds.md) 267 | 268 | In order to completely disable Chisel, set the `ChiselEnabled` property to `false`. This can be useful for building on the command line with `dotnet build -p:ChiselEnabled=false` for example. 269 | 270 | ### `ChiselGraphName` 271 | 272 | defaults to `$(MSBuildProjectName).Chisel.mermaid` 273 | 274 | This is the name of the dependency graph file. A Mermaid file will be written if it ends with either `.mmd` or `.mermaid`, otherwise a Graphviz (DOT) file will be written. To completely disable the graph feature, use `none`. 275 | 276 | ```xml 277 | 278 | none 279 | 280 | ``` 281 | 282 | Note that the file name must not include a path separator. 283 | 284 | ### `ChiselGraphAlias` 285 | 286 | no default value 287 | 288 | Setting the `ChiselGraphAlias` property adds an alias (link) under the project. This is useful to see the graph directly into the IDE. A very good combination with the Rider [Mermaid plugin](https://plugins.jetbrains.com/plugin/20146-mermaid). 289 | 290 | ![Screenshot of the SqlClientSample project with the Mermaid graph](resources/ChiselGraphAlias.png) 291 | 292 | ```xml 293 | 294 | Chisel\SqlClientSample.mermaid 295 | 296 | ``` 297 | 298 | ### `ChiselGraphDirection` 299 | 300 | defaults to `LeftToRight` 301 | 302 | This defines how the dependency graph is laid out. Possible values are `LeftToRight` and `TopToBottom`. Except for shallow graphs, left to right usually produces a more readable graph. 303 | 304 | ### `ChiselGraphIncludeVersions` 305 | 306 | defaults to `false` 307 | 308 | Controls whether the dependency graph nodes are named `{package}` or `{package}/{version}`. 309 | 310 | Example with `ChiselGraphIncludeVersions` set to `false` 311 | 312 | ```mermaid 313 | graph LR 314 | Serilog.Sinks.File --> Serilog 315 | ``` 316 | 317 | Example with `ChiselGraphIncludeVersions` set to `true` 318 | 319 | ```mermaid 320 | graph LR 321 | Serilog.Sinks.File/5.0.0 --> Serilog/2.10.0 322 | ``` 323 | 324 | ### `ChiselGraphIgnore` 325 | 326 | On real projects, the dependency graph may become huge. Adding packages to `ChiselGraphIgnore` will remove them from the dependency graph, thus reducing the overall size and increasing readability in other areas. 327 | 328 | ```xml 329 | 330 | 331 | 332 | 333 | 334 | ``` 335 | 336 | Globbing is supported since version 1.1.0. For example to ignore all packages starting with `System.` in the graph, use the following syntax: 337 | 338 | ```xml 339 | 340 | 341 | 342 | ``` -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/global", 3 | "sdk": { 4 | "version": "8.0.100", 5 | "allowPrerelease": false, 6 | "rollForward": "latestMajor" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /resources/ChiselGraphAlias.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xced/Chisel/b3053c25c85f311a6e76918f4f8f477ee80575ea/resources/ChiselGraphAlias.png -------------------------------------------------------------------------------- /resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xced/Chisel/b3053c25c85f311a6e76918f4f8f477ee80575ea/resources/icon.png -------------------------------------------------------------------------------- /resources/icon.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -xeo pipefail 2 | 3 | # Bought https://thenounproject.com/icon/chisel-1211331/ by Creative Mahira for $2.99 4 | 5 | SCRIPT_DIR=$(dirname "$BASH_SOURCE") 6 | 7 | rsvg-convert --version > /dev/null 2>/dev/null || (echo "Please install librsvg with \`brew install librsvg\` and try again." && exit 1) 8 | 9 | rsvg-convert --width 256 --output "${SCRIPT_DIR}/icon.png" "${SCRIPT_DIR}/icon.svg" 10 | -------------------------------------------------------------------------------- /resources/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /samples/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | false 4 | 5 | 6 | -------------------------------------------------------------------------------- /samples/Microsoft.Identity.Client/Microsoft.Identity.Client.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | none 6 | 4.61.3 7 | true 8 | $(BaseIntermediateOutputPath)MSAL.snk 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /samples/Microsoft.Identity.Client/Stubs.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Identity.Client 2 | { 3 | public class DeviceCodeResult 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /samples/MongoDbSample/MongoDbSample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | true 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | AWSSDK.SecurityToken 19 | 20 | 21 | 22 | Testcontainers.MongoDb 23 | MongoDBSample.mermaid 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /samples/MongoDbSample/Program.cs: -------------------------------------------------------------------------------- 1 | using MongoDB.Bson; 2 | using MongoDB.Driver; 3 | using Testcontainers.MongoDb; 4 | 5 | /* 6 | * This program demonstrates that a MongoDB connection can be successfully established when authenticating with a user and a password. 7 | * 8 | * Passing the --aws argument changes the authentication mechanism to [MONGODB-AWS][1] which actually requires the removed AWS package. 9 | * Since the package was removed with AWSSDK.SecurityToken a `FileNotFoundException` will be thrown: 10 | * > Could not load file or assembly 'AWSSDK.Core, Version=3.3.0.0, Culture=neutral, PublicKeyToken=885c28607f98e604' 11 | * 12 | * [1]: https://www.mongodb.com/docs/drivers/csharp/current/fundamentals/authentication/#std-label-csharp-mongodb-aws 13 | */ 14 | 15 | await using var mongoContainer = new MongoDbBuilder().Build(); 16 | try 17 | { 18 | await mongoContainer.StartAsync(); 19 | var mongoSettings = MongoClientSettings.FromConnectionString(mongoContainer.GetConnectionString()); 20 | if (args.Contains("--aws")) 21 | { 22 | mongoSettings.Credential = new MongoCredential("MONGODB-AWS", new MongoExternalIdentity("username"), new PasswordEvidence("password")); 23 | } 24 | var mongoClient = new MongoClient(mongoSettings); 25 | var database = mongoClient.GetDatabase("admin"); 26 | var buildInfo = await database.RunCommandAsync(new BsonDocumentCommand(new BsonDocument { ["buildInfo"] = true })); 27 | Console.WriteLine($"✅ {buildInfo}"); 28 | return 0; 29 | } 30 | catch (Exception exception) 31 | { 32 | Console.Error.WriteLine($"❌ {exception}"); 33 | return 1; 34 | } 35 | -------------------------------------------------------------------------------- /samples/MongoDbSample/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "profiles": { 4 | "MongoDbSample (works without AWS authentication)": { 5 | "commandName": "Project" 6 | }, 7 | "MongoDbSample (crashes when using AWS authentication)": { 8 | "commandName": "Project", 9 | "commandLineArgs": "--aws" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /samples/SqlClientSample/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Data.SqlClient; 2 | 3 | /* 4 | * This program demonstrates that an SQL connection can be successfully established when authenticating with a user and a password. 5 | * 6 | * Passing the --azure argument changes the authentication method to [managed identity][1] which actually requires the removed Azure package. 7 | * Since the package was transitively removed with a `FileNotFoundException` will be thrown (when not using the Microsoft.Identity.Client stub) 8 | * > Could not load file or assembly 'Azure.Core, Version=1.35.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8' 9 | * 10 | * [1]: https://learn.microsoft.com/en-us/sql/connect/ado-net/sql/azure-active-directory-authentication#using-managed-identity-authentication 11 | */ 12 | 13 | try 14 | { 15 | var builder = new SqlConnectionStringBuilder { DataSource = "sqlprosample.database.windows.net", InitialCatalog = "sqlprosample" }; 16 | if (args.Contains("--azure")) 17 | { 18 | builder.Authentication = SqlAuthenticationMethod.ActiveDirectoryManagedIdentity; 19 | } 20 | else 21 | { 22 | builder.UserID = "sqlproro"; 23 | builder.Password = "nh{Zd?*8ZU@Y}Bb#"; 24 | } 25 | 26 | using var connection = new SqlConnection(builder.ConnectionString); 27 | connection.Open(); 28 | using var command = new SqlCommand("Select @@version", connection); 29 | var result = command.ExecuteScalar(); 30 | 31 | if (BinaryData.Empty.IsEmpty) 32 | { 33 | Console.WriteLine($"✅ {result}"); 34 | } 35 | else 36 | { 37 | throw new InvalidOperationException("BinaryData.Empty.IsEmpty returned false"); 38 | } 39 | return 0; 40 | } 41 | catch (Exception exception) 42 | { 43 | Console.Error.WriteLine($"❌ {exception}"); 44 | return 1; 45 | } 46 | -------------------------------------------------------------------------------- /samples/SqlClientSample/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "profiles": { 4 | "SqlClientSample (works without Azure authentication)": { 5 | "commandName": "Project" 6 | }, 7 | "SqlClientSample (crashes when using Azure authentication)": { 8 | "commandName": "Project", 9 | "commandLineArgs": "--azure" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /samples/SqlClientSample/SqlClientSample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | net8.0-windows 7 | embedded 8 | true 9 | enable 10 | true 11 | en 12 | 6.0.2 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | SqlClientSample.mermaid 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/Chisel/Chisel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using Microsoft.Build.Framework; 8 | using Microsoft.Build.Utilities; 9 | 10 | namespace Chisel; 11 | 12 | /// 13 | /// Task that determines which package to remove from the build. 14 | /// 15 | [SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Global", Justification = "For MSBuild")] 16 | [SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Global", Justification = "For MSBuild")] 17 | [SuppressMessage("ReSharper", "MemberCanBePrivate.Global", Justification = "For MSBuild")] 18 | [SuppressMessage("ReSharper", "UnusedType.Global", Justification = "For MSBuild")] 19 | public class Chisel : Task 20 | { 21 | /// 22 | /// The project assets file path (project.assets.json). 23 | /// 24 | [Required] 25 | public string ProjectAssetsFile { get; set; } = ""; 26 | 27 | /// 28 | /// The target framework. 29 | /// 30 | [Required] 31 | public string TargetFramework { get; set; } = ""; 32 | 33 | /// 34 | /// The runtime identifier (rid). 35 | /// 36 | public string RuntimeIdentifier { get; set; } = ""; 37 | 38 | /// 39 | /// The intermediate output path where the is saved. 40 | /// 41 | public string IntermediateOutputPath { get; set; } = ""; 42 | 43 | /// 44 | /// The name of the project referencing Chisel. Used to produce a high quality warnings. 45 | /// 46 | public string ProjectName { get; set; } = ""; 47 | 48 | /// 49 | /// The output type of the project referencing Chisel. Used to ensure Chisel is not used on a class library. 50 | /// 51 | public string OutputType { get; set; } = ""; 52 | 53 | /// 54 | /// The list of resolved runtime assemblies (RuntimeCopyLocalItems). 55 | /// 56 | [Required] 57 | public ITaskItem[] RuntimeAssemblies { get; set; } = []; 58 | 59 | /// 60 | /// The list of resolved native libraries (NativeCopyLocalItems). 61 | /// 62 | [Required] 63 | public ITaskItem[] NativeLibraries { get; set; } = []; 64 | 65 | /// 66 | /// The optional dependency graph file name. 67 | /// If the file name ends with .mmd or .mermaid then a Mermaid graph is written. 68 | /// Otherwise, a Graphviz dot file is written. 69 | /// Use none to disable writing the dependency graph file. 70 | /// 71 | public string GraphName { get; set; } = ""; 72 | 73 | /// 74 | /// The dependency graph direction. Allowed values are LeftToRight and TopToBottom. 75 | /// 76 | public string GraphDirection { get; set; } = nameof(global::Chisel.GraphDirection.LeftToRight); 77 | 78 | /// 79 | /// Include the version numbers in the generated dependency graph file. 80 | /// 81 | public bool GraphIncludeVersions { get; set; } 82 | 83 | /// 84 | /// Writes ignored packages (ChiselGraphIgnore) to the dependency graph file in gray. Used for debugging. 85 | /// 86 | public bool GraphWriteIgnoredPackages { get; set; } 87 | 88 | /// 89 | /// The package references to ignore when building the dependency graph. 90 | /// 91 | public ITaskItem[] GraphIgnores { get; set; } = []; 92 | 93 | /// 94 | /// The package references to remove from the build. 95 | /// 96 | public ITaskItem[] ChiselPackages { get; set; } = []; 97 | 98 | /// 99 | /// The RuntimeCopyLocalItems to remove from the build. 100 | /// 101 | [Output] 102 | public ITaskItem[] RemoveRuntimeAssemblies { get; private set; } = []; 103 | 104 | /// 105 | /// The NativeCopyLocalItems to remove from the build. 106 | /// 107 | [Output] 108 | public ITaskItem[] RemoveNativeLibraries { get; private set; } = []; 109 | 110 | /// 111 | /// The path where the dependency graph was written or an empty array none was written. 112 | /// 113 | [Output] 114 | public ITaskItem[] Graph { get; private set; } = []; 115 | 116 | /// 117 | /// The total number of bytes from all the runtime assemblies and native libraries that were removed by Chisel. 118 | /// 119 | [Output] 120 | public long? BytesSaved { get; private set; } 121 | 122 | static Chisel() => AppDomain.CurrentDomain.AssemblyResolve += SdkAssemblyResolver.ResolveAssembly; 123 | 124 | /// 125 | public override bool Execute() 126 | { 127 | if (string.Equals(OutputType, "library", StringComparison.OrdinalIgnoreCase)) 128 | { 129 | Log.LogError($"Chisel can't be used on {ProjectName} because it's a class library (OutputType = {OutputType})"); 130 | return false; 131 | } 132 | 133 | try 134 | { 135 | LogNuGetAssemblies(); 136 | 137 | var graph = ProcessGraph(); 138 | 139 | foreach (var (project, dependent, dependency) in graph.EnumerateUnsatisfiedProjectDependencies()) 140 | { 141 | LogWarning("CHISEL007", $"{dependent.Name}/{dependent.Version} requires {dependency.Id} to satisfy {dependency.VersionRange} but {project.Version} does not"); 142 | } 143 | 144 | WriteGraph(graph); 145 | return true; 146 | } 147 | catch (Exception exception) 148 | { 149 | Log.LogErrorFromException(exception, showStackTrace: true, showDetail: true, null); 150 | return false; 151 | } 152 | } 153 | 154 | private void LogNuGetAssemblies() 155 | { 156 | var assemblyDescriptions = new[] 157 | { 158 | ("NuGet.ProjectModel", typeof(NuGet.ProjectModel.LockFile)), 159 | ("NuGet.LibraryModel", typeof(NuGet.LibraryModel.Library)), 160 | ("NuGet.Versioning", typeof(NuGet.Versioning.NuGetVersion)), 161 | }; 162 | foreach (var (assemblyName, assemblyType) in assemblyDescriptions) 163 | { 164 | var message = $"Chisel is using {assemblyName} from {assemblyType.Assembly.Location}"; 165 | SdkAssemblyResolver.DebugLog(message); 166 | if (Environment.GetEnvironmentVariable("CHISEL_DEBUG_FILE") != null) 167 | { 168 | LogWarning("CHISEL000", message); 169 | } 170 | Log.LogMessage(MessageImportance.Low, message); 171 | } 172 | } 173 | 174 | private DependencyGraph ProcessGraph() 175 | { 176 | var lockFile = new NuGet.ProjectModel.LockFileFormat().Read(ProjectAssetsFile); 177 | var copyLocalPackages = new HashSet(RuntimeAssemblies.Select(NuGetPackageId).Concat(NativeLibraries.Select(NuGetPackageId))); 178 | var (packages, roots) = lockFile.ReadPackages(TargetFramework, RuntimeIdentifier, package => package.IsProjectReference || copyLocalPackages.Contains(package.Name)); 179 | var graph = new DependencyGraph(packages, roots, GraphIgnores.Select(e => e.ItemSpec)); 180 | var (removed, notFound, removedRoots) = graph.Remove(ChiselPackages.Select(e => e.ItemSpec)); 181 | 182 | foreach (var packageName in notFound) 183 | { 184 | LogWarning("CHISEL002", $"The package {packageName} (defined in ChiselPackage) was not found in the dependency graph of {ProjectName}"); 185 | } 186 | 187 | foreach (var packageName in removedRoots) 188 | { 189 | LogWarning("CHISEL003", $"The package {packageName} (defined in ChiselPackage) can't be removed because it's a direct dependency of {ProjectName}"); 190 | } 191 | 192 | RemoveRuntimeAssemblies = RuntimeAssemblies.Where(item => removed.Contains(NuGetPackageId(item))).ToArray(); 193 | RemoveNativeLibraries = NativeLibraries.Where(item => removed.Contains(NuGetPackageId(item))).ToArray(); 194 | 195 | try 196 | { 197 | BytesSaved = RemoveRuntimeAssemblies.Concat(RemoveNativeLibraries).Sum(e => new FileInfo(e.ItemSpec).Length); 198 | Log.LogMessage($"Chisel saved {BytesSaved / (1024.0 * 1024):F1} MiB"); 199 | } 200 | catch (Exception exception) 201 | { 202 | Log.LogWarningFromException(exception, showStackTrace: true); 203 | } 204 | 205 | return graph; 206 | } 207 | 208 | private void WriteGraph(DependencyGraph graph) 209 | { 210 | if (string.IsNullOrEmpty(GraphName) || GraphName.Equals("none", StringComparison.OrdinalIgnoreCase)) 211 | { 212 | return; 213 | } 214 | 215 | if (GraphName != Path.GetFileName(GraphName)) 216 | { 217 | LogWarning("CHISEL020", $"The ChiselGraph property ({GraphName}) must be a file name that does not include a directory"); 218 | return; 219 | } 220 | 221 | if (!Directory.Exists(IntermediateOutputPath)) 222 | { 223 | LogWarning("CHISEL021", $"The IntermediateOutputPath property ({IntermediateOutputPath}) must point to an existing directory"); 224 | return; 225 | } 226 | 227 | var graphPath = Path.Combine(IntermediateOutputPath, GraphName); 228 | try 229 | { 230 | using var graphStream = new FileStream(graphPath, FileMode.Create); 231 | using var writer = new StreamWriter(graphStream, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); 232 | var isMermaid = Path.GetExtension(GraphName) is ".mmd" or ".mermaid"; 233 | var graphWriter = isMermaid ? GraphWriter.Mermaid(writer) : GraphWriter.Graphviz(writer); 234 | graphWriter.Write(graph, GetGraphOptions()); 235 | var graphItem = new TaskItem(graphPath); 236 | graphItem.SetMetadata("Format", graphWriter.FormatName); 237 | Graph = [ graphItem ]; 238 | } 239 | catch (Exception exception) 240 | { 241 | Log.LogWarningFromException(exception, showStackTrace: true); 242 | try 243 | { 244 | File.Delete(graphPath); 245 | } 246 | catch (Exception deleteException) 247 | { 248 | Log.LogWarningFromException(deleteException, showStackTrace: true); 249 | } 250 | } 251 | } 252 | 253 | private GraphOptions GetGraphOptions() 254 | { 255 | if (!Enum.TryParse(GraphDirection, ignoreCase: true, out var direction)) 256 | { 257 | LogWarning("CHISEL022", $"The ChiselGraphDirection property ({GraphDirection}) must be either {nameof(global::Chisel.GraphDirection.LeftToRight)} or {nameof(global::Chisel.GraphDirection.TopToBottom)}"); 258 | } 259 | 260 | return new GraphOptions 261 | { 262 | Direction = direction, 263 | IncludeVersions = GraphIncludeVersions, 264 | WriteIgnoredPackages = GraphWriteIgnoredPackages, 265 | }; 266 | } 267 | 268 | private string NuGetPackageId(ITaskItem item) 269 | { 270 | var packageId = item.GetMetadata("NuGetPackageId"); 271 | if (string.IsNullOrEmpty(packageId)) 272 | { 273 | var metadataNames = string.Join(", ", item.MetadataNames.OfType().Select(e => $"\"{e}\"")); 274 | LogWarning("CHISEL001", $"\"{item.ItemSpec}\" should contain \"NuGetPackageId\" metadata but contains {metadataNames}"); 275 | } 276 | return packageId; 277 | } 278 | 279 | private void LogWarning(string warningCode, string message) 280 | { 281 | Log.LogWarning( 282 | subcategory: default, 283 | warningCode: warningCode, 284 | helpKeyword: default, 285 | file: default, 286 | lineNumber: default, 287 | columnNumber: default, 288 | endLineNumber: default, 289 | endColumnNumber: default, 290 | message: message 291 | ); 292 | } 293 | } -------------------------------------------------------------------------------- /src/Chisel/Chisel.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | latest 6 | 7 | 8 | 9 | enable 10 | All 11 | true 12 | 13 | 14 | 15 | 16 | <_Parameter1>true 17 | 18 | 19 | 20 | 21 | embedded 22 | true 23 | true 24 | 25 | tasks 26 | $(BuildOutputTargetFolder) 27 | NU5100 28 | true 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | Cédric Luthi 37 | Copyright © Cédric Luthi 38 | Remove unwanted dependencies from your dotnet projects 39 | icon.png 40 | MIT 41 | nuget;package;dependencies 42 | https://github.com/0xced/Chisel 43 | https://github.com/0xced/Chisel/blob/main/CHANGELOG.md 44 | true 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 1.0 53 | 54 | 55 | 56 | true 57 | 58 | true 59 | 60 | 61 | 62 | 63 | 1 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | GraphWriter.cs 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /src/Chisel/DependencyGraph.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Chisel; 6 | 7 | internal sealed class DependencyGraph 8 | { 9 | private readonly IReadOnlyCollection _roots; 10 | /* 11 | * ┌───────────────────┐ 12 | * │ Azure.Identity │───────────────────────────┐ 13 | * └───────────────────┘ ▼ 14 | * │ ┌───────────────────────┐ 15 | * │ ┌────────────▶│ System.Text.Json │ 16 | * │ │ └───────────────────────┘ 17 | * │ │ ▲ 18 | * │ │ │ 19 | * │ ┌───────────────────┐ ┌───────────────────────┐ 20 | * └▶│ Azure.Core │──▶│ System.Memory.Data │ 21 | * └───────────────────┘ └───────────────────────┘ 22 | * 23 | * _graph: Key = Azure.Identity, Values = [ Azure.Core, System.Text.Json ] 24 | * _reverseGraph: Key = System.Text.Json, Values = [ Azure.Identity, Azure.Core, System.Memory.Data ] 25 | */ 26 | private readonly Dictionary> _graph = new(); 27 | private readonly Dictionary> _reverseGraph = new(); 28 | 29 | public DependencyGraph(IReadOnlyDictionary packages, IReadOnlyCollection roots, IEnumerable ignores) 30 | { 31 | foreach (var package in packages.Values) 32 | { 33 | var dependencies = new HashSet(package.Dependencies.Where(e => packages.ContainsKey(e.Id)).Select(e => packages[e.Id])); 34 | 35 | if (dependencies.Count > 0) 36 | { 37 | _graph.Add(package, dependencies); 38 | } 39 | 40 | foreach (var dependency in dependencies) 41 | { 42 | if (_reverseGraph.TryGetValue(dependency, out var reverseDependencies)) 43 | { 44 | reverseDependencies.Add(package); 45 | } 46 | else 47 | { 48 | _reverseGraph[dependency] = [package]; 49 | } 50 | } 51 | } 52 | 53 | _roots = roots; 54 | 55 | foreach (var root in _roots) 56 | { 57 | if (_reverseGraph.TryGetValue(root, out var reverseDependencies)) 58 | { 59 | reverseDependencies.Add(root); 60 | } 61 | else 62 | { 63 | _reverseGraph[root] = [root]; 64 | } 65 | } 66 | 67 | Ignore(ignores); 68 | } 69 | 70 | public (HashSet Removed, HashSet NotFound, HashSet RemovedRoots) Remove(IEnumerable packageNames) 71 | { 72 | var notFound = new HashSet(StringComparer.OrdinalIgnoreCase); 73 | var removedRoots = new HashSet(StringComparer.OrdinalIgnoreCase); 74 | var packages = new HashSet(); 75 | foreach (var packageName in packageNames.Distinct()) 76 | { 77 | var package = _reverseGraph.Keys.SingleOrDefault(e => e.Name.Equals(packageName, StringComparison.OrdinalIgnoreCase)); 78 | if (package == null) 79 | { 80 | notFound.Add(packageName); 81 | } 82 | else 83 | { 84 | if (_roots.Contains(package)) 85 | { 86 | removedRoots.Add(package.Name); 87 | } 88 | else 89 | { 90 | packages.Add(package); 91 | } 92 | } 93 | } 94 | 95 | foreach (var package in packages) 96 | { 97 | Remove(package); 98 | Restore(package, packages); 99 | } 100 | 101 | return ([.._reverseGraph.Keys.Where(e => e.State == PackageState.Remove).Select(e => e.Name)], notFound, removedRoots); 102 | } 103 | 104 | public IEnumerable<(Package Project, Package Dependent, Dependency Dependency)> EnumerateUnsatisfiedProjectDependencies() 105 | { 106 | foreach (var (project, dependents) in _reverseGraph.Where(e => e.Key.IsProjectReference).Select(e => (e.Key, e.Value))) 107 | { 108 | foreach (var dependent in dependents) 109 | { 110 | foreach (var dependency in dependent.Dependencies.Where(e => e.Id == project.Name)) 111 | { 112 | if (!dependency.VersionRange.Satisfies(project.Version)) 113 | { 114 | yield return (project, dependent, dependency); 115 | } 116 | } 117 | } 118 | } 119 | } 120 | 121 | private void Ignore(IEnumerable packageNames) 122 | { 123 | var packages = new HashSet(); 124 | 125 | foreach (var packageName in packageNames) 126 | { 127 | if (packageName.EndsWith("*", StringComparison.OrdinalIgnoreCase)) 128 | { 129 | var packagePrefix = packageName[..^1]; 130 | foreach (var package in _reverseGraph.Keys.Where(e => e.Name.StartsWith(packagePrefix, StringComparison.OrdinalIgnoreCase))) 131 | { 132 | packages.Add(package); 133 | } 134 | } 135 | else 136 | { 137 | foreach (var package in _reverseGraph.Keys.Where(e => e.Name.Equals(packageName, StringComparison.OrdinalIgnoreCase))) 138 | { 139 | packages.Add(package); 140 | } 141 | } 142 | } 143 | 144 | foreach (var package in packages) 145 | { 146 | Ignore(package); 147 | Restore(package, packages); 148 | } 149 | } 150 | 151 | private void Remove(Package package) => UpdateDependencies(package, PackageState.Remove); 152 | private void Ignore(Package package) => UpdateDependencies(package, PackageState.Ignore); 153 | 154 | private void UpdateDependencies(Package package, PackageState state) 155 | { 156 | package.State = state; 157 | if (_graph.TryGetValue(package, out var dependencies)) 158 | { 159 | foreach (var dependency in dependencies) 160 | { 161 | UpdateDependencies(dependency, state); 162 | } 163 | } 164 | } 165 | 166 | private void Restore(Package package, HashSet excludePackages) 167 | { 168 | if ((_reverseGraph[package].Any(e => e.State == PackageState.Keep) && !excludePackages.Contains(package)) || _roots.Except(excludePackages).Contains(package)) 169 | { 170 | package.State = PackageState.Keep; 171 | } 172 | 173 | if (_graph.TryGetValue(package, out var dependencies)) 174 | { 175 | foreach (var dependency in dependencies) 176 | { 177 | Restore(dependency, excludePackages); 178 | } 179 | } 180 | } 181 | 182 | public IEnumerable Packages => _reverseGraph.Keys; 183 | 184 | public IReadOnlyDictionary> Dependencies 185 | { 186 | get 187 | { 188 | var graph = new Dictionary>(_graph); 189 | foreach (var root in _roots.Where(root => !graph.ContainsKey(root))) 190 | { 191 | graph.Add(root, []); 192 | } 193 | return graph; 194 | } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /src/Chisel/GraphDirection.cs: -------------------------------------------------------------------------------- 1 | namespace Chisel; 2 | 3 | internal enum GraphDirection 4 | { 5 | LeftToRight, 6 | TopToBottom, 7 | } -------------------------------------------------------------------------------- /src/Chisel/GraphOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Chisel; 2 | 3 | internal struct GraphOptions 4 | { 5 | public GraphOptions() 6 | { 7 | Color = new Colors 8 | { 9 | Default = new Color { Fill = "aquamarine", Stroke = "#009061", Text = "#333333" }, 10 | Project = new Color { Fill = "skyblue", Stroke = "#05587C" }, 11 | Removed = new Color { Fill = "lightcoral", Stroke = "#A42A2A" }, 12 | Ignored = new Color { Fill = "lightgray", Stroke = "#7A7A7A" }, 13 | }; 14 | } 15 | 16 | public required GraphDirection Direction { get; init; } 17 | public required bool IncludeVersions { get; init; } 18 | public required bool WriteIgnoredPackages { get; init; } 19 | public Colors Color { get; } 20 | } 21 | 22 | internal struct Colors 23 | { 24 | public required Color Default { get; init; } 25 | public required Color Project { get; init; } 26 | public required Color Removed { get; init; } 27 | public required Color Ignored { get; init; } 28 | } 29 | 30 | internal struct Color 31 | { 32 | public required string Fill { get; init; } 33 | public required string Stroke { get; init; } 34 | public string? Text { get; init; } 35 | } -------------------------------------------------------------------------------- /src/Chisel/GraphWriter.Graphviz.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace Chisel; 4 | 5 | internal sealed class GraphvizWriter(TextWriter writer) : GraphWriter(writer) 6 | { 7 | public override string FormatName => "Graphviz"; 8 | 9 | protected override void WriteHeader(bool hasProject, bool hasIgnored, bool hasRemoved, GraphOptions options) 10 | { 11 | Writer.WriteLine("# Generated by https://github.com/0xced/Chisel"); 12 | Writer.WriteLine(); 13 | Writer.WriteLine("digraph"); 14 | Writer.WriteLine("{"); 15 | 16 | if (options.Direction == GraphDirection.LeftToRight) 17 | Writer.WriteLine(" rankdir=LR"); 18 | else if (options.Direction == GraphDirection.TopToBottom) 19 | Writer.WriteLine(" rankdir=TB"); 20 | 21 | Writer.WriteLine($" node [ fontname = \"Segoe UI, sans-serif\", shape = box, style = filled, {Color(options.Color.Default)} ]"); 22 | Writer.WriteLine(); 23 | } 24 | 25 | protected override void WriteFooter() 26 | { 27 | Writer.WriteLine("}"); 28 | } 29 | 30 | protected override void WriteRoot(Package package, GraphOptions options) 31 | { 32 | } 33 | 34 | protected override void WriteNode(Package package, GraphOptions options) 35 | { 36 | Writer.Write($" \"{GetPackageId(package, options)}\""); 37 | var color = package.State switch 38 | { 39 | PackageState.Ignore => options.Color.Ignored, 40 | PackageState.Remove => options.Color.Removed, 41 | _ => package.IsProjectReference ? options.Color.Project : (Color?)null, 42 | }; 43 | 44 | if (package.IsRoot || color.HasValue) 45 | { 46 | Writer.Write(" ["); 47 | if (package.IsRoot) 48 | { 49 | Writer.Write(" shape = hexagon, penwidth = 4"); 50 | } 51 | if (color.HasValue) 52 | { 53 | if (package.IsRoot) 54 | { 55 | Writer.Write(','); 56 | } 57 | Writer.Write($" {Color(color.Value)}"); 58 | } 59 | Writer.Write(" ]"); 60 | } 61 | 62 | Writer.WriteLine(); 63 | } 64 | 65 | protected override void WriteEdge(Package package, Package dependency, GraphOptions options) 66 | { 67 | Writer.WriteLine($" \"{GetPackageId(package, options)}\" -> \"{GetPackageId(dependency, options)}\""); 68 | } 69 | 70 | private static string Color(Color color) 71 | { 72 | var colorDefinition = $"fillcolor = {Fill(color)}, color = {Stroke(color)}"; 73 | return string.IsNullOrEmpty(color.Text) ? colorDefinition : colorDefinition + $", fontcolor = {Text(color)}"; 74 | } 75 | 76 | private static string Fill(Color color) => color.Fill.StartsWith("#") ? $"\"{color.Fill}\"" : color.Fill; 77 | 78 | private static string Stroke(Color color) => color.Stroke.StartsWith("#") ? $"\"{color.Stroke}\"" : color.Stroke; 79 | 80 | private static string Text(Color color) => color.Text?.StartsWith("#") == true ? $"\"{color.Text}\"" : color.Stroke; 81 | } -------------------------------------------------------------------------------- /src/Chisel/GraphWriter.Mermaid.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace Chisel; 4 | 5 | internal sealed class MermaidWriter(TextWriter writer) : GraphWriter(writer) 6 | { 7 | public override string FormatName => "Mermaid"; 8 | 9 | private static string ClassDef(string name, Color color) 10 | { 11 | var classDef = $"classDef {name} fill:{color.Fill},stroke:{color.Stroke}"; 12 | return string.IsNullOrEmpty(color.Text) ? classDef : classDef + ",color:" + color.Text; 13 | } 14 | 15 | protected override void WriteHeader(bool hasProject, bool hasIgnored, bool hasRemoved, GraphOptions options) 16 | { 17 | Writer.WriteLine("%% Generated by https://github.com/0xced/Chisel"); 18 | Writer.WriteLine(); 19 | Writer.Write("graph"); 20 | if (options.Direction == GraphDirection.LeftToRight) 21 | Writer.Write(" LR"); 22 | else if (options.Direction == GraphDirection.TopToBottom) 23 | Writer.Write(" TB"); 24 | Writer.WriteLine(); 25 | 26 | Writer.WriteLine(); 27 | Writer.WriteLine("classDef root stroke-width:4px"); 28 | Writer.WriteLine(ClassDef("default", options.Color.Default)); 29 | if (hasProject) 30 | Writer.WriteLine(ClassDef("project", options.Color.Project)); 31 | if (hasIgnored) 32 | Writer.WriteLine(ClassDef("ignored", options.Color.Ignored)); 33 | if (hasRemoved) 34 | Writer.WriteLine(ClassDef("removed", options.Color.Removed)); 35 | Writer.WriteLine(); 36 | } 37 | 38 | protected override void WriteFooter() 39 | { 40 | } 41 | 42 | protected override void WriteRoot(Package package, GraphOptions options) 43 | { 44 | var packageId = GetPackageId(package, options); 45 | Writer.WriteLine($"{packageId}{{{{{packageId}}}}}"); 46 | } 47 | 48 | protected override void WriteNode(Package package, GraphOptions options) 49 | { 50 | if (package.IsRoot) 51 | { 52 | Writer.WriteLine($"class {GetPackageId(package, options)} root"); 53 | } 54 | var className = package.State switch 55 | { 56 | PackageState.Ignore => "ignored", 57 | PackageState.Remove => "removed", 58 | _ => package.IsProjectReference ? "project" : "default", 59 | }; 60 | Writer.WriteLine($"class {GetPackageId(package, options)} {className}"); 61 | } 62 | 63 | protected override void WriteEdge(Package package, Package dependency, GraphOptions options) 64 | { 65 | var packageId = GetPackageId(package, options); 66 | var source = package.IsRoot ? $"{packageId}{{{{{packageId}}}}}" : packageId; 67 | Writer.WriteLine($"{source} --> {GetPackageId(dependency, options)}"); 68 | } 69 | } -------------------------------------------------------------------------------- /src/Chisel/GraphWriter.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Linq; 3 | 4 | namespace Chisel; 5 | 6 | internal abstract class GraphWriter(TextWriter writer) 7 | { 8 | public static GraphWriter Graphviz(TextWriter writer) => new GraphvizWriter(writer); 9 | 10 | public static GraphWriter Mermaid(TextWriter writer) => new MermaidWriter(writer); 11 | 12 | protected readonly TextWriter Writer = writer; 13 | 14 | public void Write(DependencyGraph graph, GraphOptions options) 15 | { 16 | var hasProject = graph.Packages.Any(e => e.IsProjectReference); 17 | var hasIgnored = graph.Packages.Any(e => e.State == PackageState.Ignore) && options.WriteIgnoredPackages; 18 | var hasRemoved = graph.Packages.Any(e => e.State == PackageState.Remove); 19 | WriteHeader(hasProject: hasProject, hasIgnored: hasIgnored, hasRemoved: hasRemoved, options); 20 | WriteEdges(graph, options); 21 | Writer.WriteLine(); 22 | WriteNodes(graph, options); 23 | WriteFooter(); 24 | } 25 | 26 | public abstract string FormatName { get; } 27 | protected abstract void WriteHeader(bool hasProject, bool hasIgnored, bool hasRemoved, GraphOptions options); 28 | protected abstract void WriteFooter(); 29 | protected abstract void WriteRoot(Package package, GraphOptions options); 30 | protected abstract void WriteNode(Package package, GraphOptions options); 31 | protected abstract void WriteEdge(Package package, Package dependency, GraphOptions options); 32 | 33 | protected static string GetPackageId(Package package, GraphOptions options) => options.IncludeVersions ? $"{package.Name}/{package.Version}" : package.Name; 34 | 35 | private static bool FilterIgnored(Package package, GraphOptions options) => options.WriteIgnoredPackages || package.State != PackageState.Ignore; 36 | 37 | private void WriteNodes(DependencyGraph graph, GraphOptions options) 38 | { 39 | foreach (var package in graph.Packages.Where(e => FilterIgnored(e, options)).OrderBy(e => e.Name)) 40 | { 41 | WriteNode(package, options); 42 | } 43 | } 44 | 45 | private void WriteEdges(DependencyGraph graph, GraphOptions options) 46 | { 47 | foreach (var (package, dependencies) in graph.Dependencies.Select(e => (e.Key, e.Value)).Where(e => FilterIgnored(e.Key, options)).OrderBy(e => e.Key.Name)) 48 | { 49 | if (dependencies.Count == 0) 50 | { 51 | WriteRoot(package, options); 52 | } 53 | foreach (var dependency in dependencies.Where(e => FilterIgnored(e, options)).OrderBy(e => e.Name)) 54 | { 55 | WriteEdge(package, dependency, options); 56 | } 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /src/Chisel/LockFileExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using NuGet.LibraryModel; 5 | using NuGet.ProjectModel; 6 | 7 | namespace Chisel; 8 | 9 | internal static class LockFileExtensions 10 | { 11 | public static (IReadOnlyDictionary Packages, IReadOnlyCollection Roots) ReadPackages(this LockFile lockFile, string tfm, string? rid, Predicate? filter = null) 12 | { 13 | var frameworks = lockFile.PackageSpec?.TargetFrameworks?.Where(e => e.TargetAlias == tfm).ToList() ?? []; 14 | var framework = frameworks.Count switch 15 | { 16 | 0 => throw new ArgumentException($"Target framework \"{tfm}\" is not available in assets at \"{lockFile.Path}\" (JSON path: project.frameworks.*.targetAlias)", nameof(tfm)), 17 | 1 => frameworks[0], 18 | _ => throw new ArgumentException($"Multiple target frameworks are matching \"{tfm}\" in assets at \"{lockFile.Path}\" (JSON path: project.frameworks.*.targetAlias)", nameof(tfm)), 19 | }; 20 | var targets = lockFile.Targets.Where(e => e.TargetFramework == framework.FrameworkName && (string.IsNullOrEmpty(rid) || e.RuntimeIdentifier == rid)).ToList(); 21 | // https://github.com/NuGet/NuGet.Client/blob/6.10.0.52/src/NuGet.Core/NuGet.ProjectModel/LockFile/LockFileTarget.cs#L17 22 | var targetId = framework.FrameworkName + (string.IsNullOrEmpty(rid) ? "" : $"/{rid}"); 23 | var target = targets.Count switch 24 | { 25 | 0 => throw new ArgumentException($"Target \"{targetId}\" is not available in assets at \"{lockFile.Path}\" (JSON path: targets)", nameof(rid)), 26 | 1 => targets[0], 27 | _ => throw new ArgumentException($"Multiple targets are matching \"{targetId}\" in assets at \"{lockFile.Path}\" (JSON path: targets)", nameof(rid)), 28 | }; 29 | var packages = target.Libraries.Where(e => e.Name != null && e.Version != null).Select(CreatePackage).Where(e => filter == null || filter(e)).ToDictionary(e => e.Name, StringComparer.OrdinalIgnoreCase); 30 | var frameworkName = framework.FrameworkName.GetShortFolderName(); 31 | var projectDependencies = lockFile.ProjectFileDependencyGroups.Where(e => e.FrameworkName == frameworkName).SelectMany(e => e.Dependencies).Select(ParseProjectFileDependency); 32 | var packageDependencies = framework.GetDependencies().Select(e => e.Name); 33 | var roots = new HashSet(projectDependencies.Concat(packageDependencies).Where(e => packages.ContainsKey(e)).Select(e => packages[e])); 34 | foreach (var root in roots) 35 | { 36 | root.IsRoot = true; 37 | } 38 | return (packages, roots); 39 | } 40 | 41 | private static Package CreatePackage(LockFileTargetLibrary library) 42 | { 43 | var name = library.Name ?? throw new ArgumentException("The library must have a name", nameof(library)); 44 | var version = library.Version ?? throw new ArgumentException($"The library \"{name}\" must have a version", nameof(library)); 45 | // https://github.com/dotnet/sdk/blob/v8.0.202/documentation/specs/runtime-configuration-file.md#libraries-section-depsjson 46 | // > `type` - the type of the library. `package` for NuGet packages. `project` for a project reference. Can be other things as well. 47 | var isProjectReference = library.Type == LibraryType.Project; 48 | var dependencies = library.Dependencies.Select(e => new Dependency(e.Id, e.VersionRange)).ToList(); 49 | return new Package(name, version, isProjectReference, dependencies); 50 | } 51 | 52 | private static string ParseProjectFileDependency(string dependency) 53 | { 54 | // Extract the dependency name by "reversing" NuGet.LibraryModel.LibraryRange.ToLockFileDependencyGroupString() 55 | // See https://github.com/NuGet/NuGet.Client/blob/6.9.1.3/src/NuGet.Core/NuGet.LibraryModel/LibraryRange.cs#L76-L115 56 | var spaceIndex = dependency.IndexOf(' '); 57 | return spaceIndex != -1 ? dependency[..spaceIndex] : dependency; 58 | } 59 | 60 | private static IEnumerable GetDependencies(this TargetFrameworkInformation targetFrameworkInformation) 61 | { 62 | // Can't use targetFrameworkInformation.Dependencies because NuGet.ProjectModel 6.13.1 changed the `Dependencies` property type 63 | // from IList to ImmutableArray, leading to this exception (use the .NET SDK 9.0.200 with Chisel 1.1.1 to reproduce) 64 | // > Chisel.targets(12,5): Error : MissingMethodException: Method not found: 'System.Collections.Generic.IList`1 NuGet.ProjectModel.TargetFrameworkInformation.get_Dependencies()'. 65 | // > at Chisel.LockFileExtensions.ReadPackages(LockFile lockFile, String tfm, String rid, Predicate`1 filter) 66 | // > at Chisel.Chisel.ProcessGraph() in /_/src/Chisel/Chisel.cs:line 178 67 | // > at Chisel.Chisel.Execute() in /_/src/Chisel/Chisel.cs:line 137 68 | var type = targetFrameworkInformation.GetType(); 69 | var dependenciesProperty = type.GetProperty(nameof(targetFrameworkInformation.Dependencies)) ?? throw new MissingMemberException(type.FullName, nameof(targetFrameworkInformation.Dependencies)); 70 | return (IEnumerable)(dependenciesProperty.GetValue(targetFrameworkInformation) ?? (IEnumerable)[]); 71 | } 72 | } -------------------------------------------------------------------------------- /src/Chisel/Package.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using NuGet.Versioning; 5 | 6 | namespace Chisel; 7 | 8 | [DebuggerDisplay("{Name}/{Version}")] 9 | internal sealed class Package(string name, NuGetVersion version, bool isProjectReference, IReadOnlyCollection dependencies) : IEquatable 10 | { 11 | public string Name { get; } = name; 12 | public NuGetVersion Version { get; } = version; 13 | public bool IsProjectReference { get; } = isProjectReference; 14 | public IReadOnlyCollection Dependencies { get; } = dependencies; 15 | 16 | public bool IsRoot { get; set; } 17 | 18 | public PackageState State { get; set; } = PackageState.Keep; 19 | 20 | public override string ToString() => Name; 21 | 22 | public bool Equals(Package? other) 23 | { 24 | if (ReferenceEquals(null, other)) return false; 25 | if (ReferenceEquals(this, other)) return true; 26 | return Name.Equals(other.Name, StringComparison.OrdinalIgnoreCase); 27 | } 28 | 29 | public override bool Equals(object? obj) => ReferenceEquals(this, obj) || obj is Package other && Equals(other); 30 | 31 | public override int GetHashCode() => Name.GetHashCode(); 32 | } 33 | 34 | internal record Dependency(string Id, VersionRange VersionRange); -------------------------------------------------------------------------------- /src/Chisel/PackageState.cs: -------------------------------------------------------------------------------- 1 | namespace Chisel; 2 | 3 | internal enum PackageState 4 | { 5 | Keep, 6 | Remove, 7 | Ignore, 8 | } -------------------------------------------------------------------------------- /src/Chisel/SdkAssemblyResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text.RegularExpressions; 8 | 9 | namespace Chisel; 10 | 11 | /// 12 | /// Resolves assemblies by looking in the right place. 13 | /// So that there's no need to package NuGet.ProjectModel.dll, NuGet.LibraryModel.dll and NuGet.Versioning.dll inside Chisel. 14 | /// 15 | /// 16 | /// To understand when this is required, go to Rider settings -> Build, Execution, Deployment -> Toolset and Build then choose 17 | /// C:\Program Files\Microsoft Visual Studio\2022\Professional\MSBuild\Current\Bin\MSBuild.exe instead of the auto detected C:\Program Files\dotnet\sdk\8.0.200\MSBuild.dll 18 | /// 19 | internal static class SdkAssemblyResolver 20 | { 21 | public static Assembly? ResolveAssembly(object sender, ResolveEventArgs args) 22 | { 23 | var dotnet = "dotnet"; 24 | var log = DebugLog; 25 | 26 | try 27 | { 28 | if (sender is ValueTuple> tuple) 29 | { 30 | dotnet = tuple.Item1; 31 | log = tuple.Item2; 32 | } 33 | 34 | var assemblyName = new AssemblyName(args.Name); 35 | var assemblyFileName = $"{assemblyName.Name}.dll"; 36 | log($"ResolveAssembly({assemblyName})"); 37 | 38 | var directories = GetNuGetDirectories(sender as AppDomain, assemblyFileName, dotnet, log); 39 | foreach (var directory in directories) 40 | { 41 | var assemblyFile = new FileInfo(Path.Combine(directory, assemblyFileName)); 42 | if (assemblyFile.Exists) 43 | { 44 | log($"Loading {assemblyFile.FullName}"); 45 | try 46 | { 47 | var assembly = Assembly.LoadFile(assemblyFile.FullName); 48 | log($"Loaded {assembly}"); 49 | return assembly; 50 | } 51 | catch (Exception exception) 52 | { 53 | log($"Failed to load {assemblyFile.FullName}: {exception}"); 54 | } 55 | } 56 | 57 | log($"Assembly {assemblyFile.FullName} not found"); 58 | } 59 | 60 | return null; 61 | } 62 | catch (Exception exception) 63 | { 64 | log($"Unexpected exception: {exception}"); 65 | return null; 66 | } 67 | } 68 | 69 | private static IEnumerable GetNuGetDirectories(AppDomain? appDomain, string assemblyFileName, string dotnet, Action log) 70 | { 71 | var nugetDirectories = new HashSet(); 72 | 73 | var loadedAssemblies = appDomain?.GetAssemblies().Where(e => e.GetName().Name.StartsWith("NuGet.")).ToList() ?? []; 74 | foreach (var (assembly, i) in loadedAssemblies.Select((e, i) => (e, i + 1))) 75 | { 76 | log($"Already loaded NuGet assembly from \"{appDomain?.FriendlyName}\" ({i}/{loadedAssemblies.Count}): {assembly} @ {assembly.Location}"); 77 | } 78 | 79 | var loadedDirectories = loadedAssemblies.OrderBy(e => e.GetName().Name == "NuGet.ProjectModel" ? 0 : 1).Select(e => Path.GetDirectoryName(e.Location)).Distinct().ToList(); 80 | foreach (var (directory, i) in loadedDirectories.Select((e, i) => (e, i + 1))) 81 | { 82 | nugetDirectories.Add(directory); 83 | log($"NuGet directory from already loaded assembly ({i}/{loadedDirectories.Count}): {directory}"); 84 | } 85 | 86 | if (nugetDirectories.Count > 0) 87 | { 88 | return nugetDirectories; 89 | } 90 | 91 | var dotnetSdkDirectory = GetDotnetSdkDirectory(dotnet, log); 92 | if (dotnetSdkDirectory?.Exists == true) 93 | { 94 | var toolsDirectory = Path.Combine(dotnetSdkDirectory.FullName, "Sdks", "Microsoft.NET.Sdk", "tools"); 95 | if (Directory.Exists(toolsDirectory)) 96 | { 97 | log($"Searching for {assemblyFileName} in {toolsDirectory} (recursively)"); 98 | foreach (var assemblyFilePath in Directory.EnumerateFiles(toolsDirectory, assemblyFileName, SearchOption.AllDirectories)) 99 | { 100 | var nugetDirectory = Path.GetDirectoryName(assemblyFilePath); 101 | nugetDirectories.Add(nugetDirectory); 102 | } 103 | } 104 | else 105 | { 106 | log($"{toolsDirectory} not found"); 107 | } 108 | } 109 | 110 | foreach (var (directory, i) in nugetDirectories.Select((e, i) => (e, i + 1))) 111 | { 112 | log($"NuGet directory from SDK ({i}/{nugetDirectories.Count}): {directory}"); 113 | } 114 | 115 | if (nugetDirectories.Count == 0) 116 | { 117 | log("NuGet directory not found"); 118 | } 119 | 120 | return nugetDirectories; 121 | } 122 | 123 | private static readonly Regex SdkRegex = new(@"(.*) \[(.*)\]", RegexOptions.Compiled); 124 | 125 | private static DirectoryInfo? GetDotnetSdkDirectory(string dotnet, Action log) 126 | { 127 | var listSdks = new Process 128 | { 129 | StartInfo = new ProcessStartInfo(dotnet) 130 | { 131 | Arguments = "--list-sdks", 132 | UseShellExecute = false, 133 | CreateNoWindow = true, 134 | RedirectStandardOutput = true, 135 | }, 136 | }; 137 | log($"▶️ {listSdks.StartInfo.FileName} {listSdks.StartInfo.Arguments}"); 138 | listSdks.Start(); 139 | 140 | DirectoryInfo? dotnetSdkDirectory = null; 141 | 142 | while (listSdks.StandardOutput.ReadLine() is {} line) 143 | { 144 | var match = SdkRegex.Match(line); 145 | if (match.Success) 146 | { 147 | dotnetSdkDirectory = new DirectoryInfo(Path.Combine(match.Groups[2].Value, match.Groups[1].Value)); 148 | var symbol = dotnetSdkDirectory.Exists ? "📁" : "❓"; 149 | log($"{symbol} {dotnetSdkDirectory}"); 150 | } 151 | else 152 | { 153 | log($"{line}{Environment.NewLine} does not match {SdkRegex}"); 154 | } 155 | } 156 | 157 | listSdks.WaitForExit(2000); 158 | 159 | return dotnetSdkDirectory; 160 | } 161 | 162 | internal static void DebugLog(string message) 163 | { 164 | var debugFile = Environment.GetEnvironmentVariable("CHISEL_DEBUG_FILE"); 165 | if (debugFile != null) 166 | { 167 | using var stream = new FileStream(debugFile, FileMode.Append); 168 | using var writer = new StreamWriter(stream); 169 | writer.WriteLine($"[{DateTime.Now:O}] {message}"); 170 | } 171 | } 172 | } -------------------------------------------------------------------------------- /src/Chisel/build/Chisel.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | false 5 | true 6 | $(MSBuildProjectName).Chisel.mermaid 7 | LeftToRight 8 | false 9 | 10 | 11 | 12 | 13 | $(ChiselGraphAlias) 14 | Never 15 | Never 16 | 17 | 18 | 19 | 20 | 21 | false 22 | 23 | 24 | 25 | 26 | 27 | false 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/Chisel/build/Chisel.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/Chisel/packages.lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "dependencies": { 4 | ".NETStandard,Version=v2.0": { 5 | "Microsoft.Build.Utilities.Core": { 6 | "type": "Direct", 7 | "requested": "[17.13.9, )", 8 | "resolved": "17.13.9", 9 | "contentHash": "8l/iUywD8ARu+9P5J2AUihLF2WAJRhZm3dbphKHBKZgwcJfHpfeVzIbh/4ZT7hnj5D0p6gVdoODs/X5b9aM9Qw==", 10 | "dependencies": { 11 | "Microsoft.Build.Framework": "17.13.9", 12 | "Microsoft.NET.StringTools": "17.13.9", 13 | "Microsoft.Win32.Registry": "5.0.0", 14 | "System.Collections.Immutable": "8.0.0", 15 | "System.Configuration.ConfigurationManager": "8.0.0", 16 | "System.Memory": "4.5.5", 17 | "System.Runtime.CompilerServices.Unsafe": "6.0.0", 18 | "System.Security.Principal.Windows": "5.0.0", 19 | "System.Text.Encoding.CodePages": "7.0.0" 20 | } 21 | }, 22 | "MinVer": { 23 | "type": "Direct", 24 | "requested": "[6.0.0, )", 25 | "resolved": "6.0.0", 26 | "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g==" 27 | }, 28 | "NETStandard.Library": { 29 | "type": "Direct", 30 | "requested": "[2.0.3, )", 31 | "resolved": "2.0.3", 32 | "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", 33 | "dependencies": { 34 | "Microsoft.NETCore.Platforms": "1.1.0" 35 | } 36 | }, 37 | "NuGet.ProjectModel": { 38 | "type": "Direct", 39 | "requested": "[6.11.1, )", 40 | "resolved": "6.11.1", 41 | "contentHash": "/91qJLUq3njYsRL4E2r0+cS6xKuePpt9vzweAH7cm2lKBIYKxRhzx6elzRm3jsGMwlcABn0bYl6PXx81Y0SMGg==", 42 | "dependencies": { 43 | "NuGet.DependencyResolver.Core": "6.11.1" 44 | } 45 | }, 46 | "PolySharp": { 47 | "type": "Direct", 48 | "requested": "[1.15.0, )", 49 | "resolved": "1.15.0", 50 | "contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g==" 51 | }, 52 | "Microsoft.Bcl.AsyncInterfaces": { 53 | "type": "Transitive", 54 | "resolved": "7.0.0", 55 | "contentHash": "3aeMZ1N0lJoSyzqiP03hqemtb1BijhsJADdobn/4nsMJ8V1H+CrpuduUe4hlRdx+ikBQju1VGjMD1GJ3Sk05Eg==", 56 | "dependencies": { 57 | "System.Threading.Tasks.Extensions": "4.5.4" 58 | } 59 | }, 60 | "Microsoft.Build.Framework": { 61 | "type": "Transitive", 62 | "resolved": "17.13.9", 63 | "contentHash": "Yc9R6+YdvB7KStBN2SiojR5bsFf+/xklQFhlgRog1wp+2V6rpfOyJftP6WdMwpvRAdy7kWSrvlrbllw62OgtlA==", 64 | "dependencies": { 65 | "Microsoft.Win32.Registry": "5.0.0", 66 | "System.Memory": "4.5.5", 67 | "System.Runtime.CompilerServices.Unsafe": "6.0.0", 68 | "System.Security.Principal.Windows": "5.0.0" 69 | } 70 | }, 71 | "Microsoft.NET.StringTools": { 72 | "type": "Transitive", 73 | "resolved": "17.13.9", 74 | "contentHash": "GjB0YvVu4gamPBYr+iKokahqZIBWds5hVNSFAYUsxk1KZLI5oubs52xmG3sMDTKy1yR7MBxBhi/k9YY6/qw53A==", 75 | "dependencies": { 76 | "System.Memory": "4.5.5", 77 | "System.Runtime.CompilerServices.Unsafe": "6.0.0" 78 | } 79 | }, 80 | "Microsoft.NETCore.Platforms": { 81 | "type": "Transitive", 82 | "resolved": "1.1.0", 83 | "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" 84 | }, 85 | "Microsoft.Win32.Registry": { 86 | "type": "Transitive", 87 | "resolved": "5.0.0", 88 | "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", 89 | "dependencies": { 90 | "System.Buffers": "4.5.1", 91 | "System.Memory": "4.5.4", 92 | "System.Security.AccessControl": "5.0.0", 93 | "System.Security.Principal.Windows": "5.0.0" 94 | } 95 | }, 96 | "Newtonsoft.Json": { 97 | "type": "Transitive", 98 | "resolved": "13.0.3", 99 | "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" 100 | }, 101 | "NuGet.Common": { 102 | "type": "Transitive", 103 | "resolved": "6.11.1", 104 | "contentHash": "6ouw0UC3TGaFHNJyoEK2/Q5jSryRHzcbKGv9C0+t/TPnTP8PoLqnyFxO1dwmQUmJkWuKAUo6Vu0kIXHY/8LyKQ==", 105 | "dependencies": { 106 | "NuGet.Frameworks": "6.11.1" 107 | } 108 | }, 109 | "NuGet.Configuration": { 110 | "type": "Transitive", 111 | "resolved": "6.11.1", 112 | "contentHash": "vbEe2acKrI2QmNx9U94ewhgUt6cLpwBeQfMtrie6NMz+GkJcX/4qIkKsX3SeBO4gFgCf8eeTyURl5hxPtiUctw==", 113 | "dependencies": { 114 | "NuGet.Common": "6.11.1", 115 | "System.Security.Cryptography.ProtectedData": "4.4.0" 116 | } 117 | }, 118 | "NuGet.DependencyResolver.Core": { 119 | "type": "Transitive", 120 | "resolved": "6.11.1", 121 | "contentHash": "vw4uYwXc1zarWBbC/eZToQIqOYtGqGOBIadfk2p2VKbJQk3eTeEbOa5Qsxl8J3gAIXb2SVSE0MOFydbpgEvL6g==", 122 | "dependencies": { 123 | "NuGet.Configuration": "6.11.1", 124 | "NuGet.LibraryModel": "6.11.1", 125 | "NuGet.Protocol": "6.11.1" 126 | } 127 | }, 128 | "NuGet.Frameworks": { 129 | "type": "Transitive", 130 | "resolved": "6.11.1", 131 | "contentHash": "plTZ3ariSWQVsFn2mk83SsdmSg1VpgIMTSZpP/eSE/NNQF02p+M9ItxAYeUZBMX+cQ2nFkSwxQRJ0/fkaV9Hbg==" 132 | }, 133 | "NuGet.LibraryModel": { 134 | "type": "Transitive", 135 | "resolved": "6.11.1", 136 | "contentHash": "An4JAcY8FuYF2Sq8X/9QPQRC7veD28z1FZAV6n57fJpTYYDd32aD7MuxPsXKGIyA6iVMcJ00pSpoGELQecXZLQ==", 137 | "dependencies": { 138 | "NuGet.Common": "6.11.1", 139 | "NuGet.Versioning": "6.11.1" 140 | } 141 | }, 142 | "NuGet.Packaging": { 143 | "type": "Transitive", 144 | "resolved": "6.11.1", 145 | "contentHash": "wiofIUUr7khwuaGXiOibMb7+dEkF97EVsAmzlaNc188HV9ujjqweQMuVCoAK2/MqXdhnrKjvicUfKo9CPsNnfg==", 146 | "dependencies": { 147 | "Newtonsoft.Json": "13.0.3", 148 | "NuGet.Configuration": "6.11.1", 149 | "NuGet.Versioning": "6.11.1", 150 | "System.Security.Cryptography.Pkcs": "6.0.4" 151 | } 152 | }, 153 | "NuGet.Protocol": { 154 | "type": "Transitive", 155 | "resolved": "6.11.1", 156 | "contentHash": "WkYlSuNHNt/j1tbHp/xjvwk2EsIdSM3raEjk3EfIFd62ER1+x4eC8/J1VKqnve6cTupF4LsuwD3Z4YCumnfCXw==", 157 | "dependencies": { 158 | "NuGet.Packaging": "6.11.1", 159 | "System.Text.Json": "7.0.3" 160 | } 161 | }, 162 | "NuGet.Versioning": { 163 | "type": "Transitive", 164 | "resolved": "6.11.1", 165 | "contentHash": "YNn3BB71F+guJW42TbAhGcMh3gpyqFMZcPVD9pm5vcvGivTALtRely/VCPWQQ6JQ5PfwIrjPaJMO7VnqyeK3rg==" 166 | }, 167 | "System.Buffers": { 168 | "type": "Transitive", 169 | "resolved": "4.5.1", 170 | "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" 171 | }, 172 | "System.Collections.Immutable": { 173 | "type": "Transitive", 174 | "resolved": "8.0.0", 175 | "contentHash": "AurL6Y5BA1WotzlEvVaIDpqzpIPvYnnldxru8oXJU2yFxFUy3+pNXjXd1ymO+RA0rq0+590Q8gaz2l3Sr7fmqg==", 176 | "dependencies": { 177 | "System.Memory": "4.5.5", 178 | "System.Runtime.CompilerServices.Unsafe": "6.0.0" 179 | } 180 | }, 181 | "System.Configuration.ConfigurationManager": { 182 | "type": "Transitive", 183 | "resolved": "8.0.0", 184 | "contentHash": "JlYi9XVvIREURRUlGMr1F6vOFLk7YSY4p1vHo4kX3tQ0AGrjqlRWHDi66ImHhy6qwXBG3BJ6Y1QlYQ+Qz6Xgww==", 185 | "dependencies": { 186 | "System.Security.Cryptography.ProtectedData": "8.0.0" 187 | } 188 | }, 189 | "System.Formats.Asn1": { 190 | "type": "Transitive", 191 | "resolved": "6.0.0", 192 | "contentHash": "T6fD00dQ3NTbPDy31m4eQUwKW84s03z0N2C8HpOklyeaDgaJPa/TexP4/SkORMSOwc7WhKifnA6Ya33AkzmafA==", 193 | "dependencies": { 194 | "System.Buffers": "4.5.1", 195 | "System.Memory": "4.5.4" 196 | } 197 | }, 198 | "System.Memory": { 199 | "type": "Transitive", 200 | "resolved": "4.5.5", 201 | "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", 202 | "dependencies": { 203 | "System.Buffers": "4.5.1", 204 | "System.Numerics.Vectors": "4.4.0", 205 | "System.Runtime.CompilerServices.Unsafe": "4.5.3" 206 | } 207 | }, 208 | "System.Numerics.Vectors": { 209 | "type": "Transitive", 210 | "resolved": "4.5.0", 211 | "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" 212 | }, 213 | "System.Runtime.CompilerServices.Unsafe": { 214 | "type": "Transitive", 215 | "resolved": "6.0.0", 216 | "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" 217 | }, 218 | "System.Security.AccessControl": { 219 | "type": "Transitive", 220 | "resolved": "5.0.0", 221 | "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", 222 | "dependencies": { 223 | "System.Security.Principal.Windows": "5.0.0" 224 | } 225 | }, 226 | "System.Security.Cryptography.Cng": { 227 | "type": "Transitive", 228 | "resolved": "5.0.0", 229 | "contentHash": "jIMXsKn94T9JY7PvPq/tMfqa6GAaHpElRDpmG+SuL+D3+sTw2M8VhnibKnN8Tq+4JqbPJ/f+BwtLeDMEnzAvRg==" 230 | }, 231 | "System.Security.Cryptography.Pkcs": { 232 | "type": "Transitive", 233 | "resolved": "6.0.4", 234 | "contentHash": "LGbXi1oUJ9QgCNGXRO9ndzBL/GZgANcsURpMhNR8uO+rca47SZmciS3RSQUvlQRwK3QHZSHNOXzoMUASKA+Anw==", 235 | "dependencies": { 236 | "System.Buffers": "4.5.1", 237 | "System.Formats.Asn1": "6.0.0", 238 | "System.Memory": "4.5.4", 239 | "System.Security.Cryptography.Cng": "5.0.0" 240 | } 241 | }, 242 | "System.Security.Cryptography.ProtectedData": { 243 | "type": "Transitive", 244 | "resolved": "8.0.0", 245 | "contentHash": "+TUFINV2q2ifyXauQXRwy4CiBhqvDEDZeVJU7qfxya4aRYOKzVBpN+4acx25VcPB9ywUN6C0n8drWl110PhZEg==", 246 | "dependencies": { 247 | "System.Memory": "4.5.5" 248 | } 249 | }, 250 | "System.Security.Principal.Windows": { 251 | "type": "Transitive", 252 | "resolved": "5.0.0", 253 | "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" 254 | }, 255 | "System.Text.Encoding.CodePages": { 256 | "type": "Transitive", 257 | "resolved": "7.0.0", 258 | "contentHash": "LSyCblMpvOe0N3E+8e0skHcrIhgV2huaNcjUUEa8hRtgEAm36aGkRoC8Jxlb6Ra6GSfF29ftduPNywin8XolzQ==", 259 | "dependencies": { 260 | "System.Memory": "4.5.5", 261 | "System.Runtime.CompilerServices.Unsafe": "6.0.0" 262 | } 263 | }, 264 | "System.Text.Encodings.Web": { 265 | "type": "Transitive", 266 | "resolved": "7.0.0", 267 | "contentHash": "OP6umVGxc0Z0MvZQBVigj4/U31Pw72ITihDWP9WiWDm+q5aoe0GaJivsfYGq53o6dxH7DcXWiCTl7+0o2CGdmg==", 268 | "dependencies": { 269 | "System.Buffers": "4.5.1", 270 | "System.Memory": "4.5.5", 271 | "System.Runtime.CompilerServices.Unsafe": "6.0.0" 272 | } 273 | }, 274 | "System.Text.Json": { 275 | "type": "Transitive", 276 | "resolved": "7.0.3", 277 | "contentHash": "AyjhwXN1zTFeIibHimfJn6eAsZ7rTBib79JQpzg8WAuR/HKDu9JGNHTuu3nbbXQ/bgI+U4z6HtZmCHNXB1QXrQ==", 278 | "dependencies": { 279 | "Microsoft.Bcl.AsyncInterfaces": "7.0.0", 280 | "System.Buffers": "4.5.1", 281 | "System.Memory": "4.5.5", 282 | "System.Numerics.Vectors": "4.5.0", 283 | "System.Runtime.CompilerServices.Unsafe": "6.0.0", 284 | "System.Text.Encodings.Web": "7.0.0", 285 | "System.Threading.Tasks.Extensions": "4.5.4" 286 | } 287 | }, 288 | "System.Threading.Tasks.Extensions": { 289 | "type": "Transitive", 290 | "resolved": "4.5.4", 291 | "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", 292 | "dependencies": { 293 | "System.Runtime.CompilerServices.Unsafe": "4.5.3" 294 | } 295 | } 296 | } 297 | } 298 | } -------------------------------------------------------------------------------- /tests/Chisel.Tests/Chisel.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | exe 6 | enable 7 | true 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | --report-trx --report-trx-filename TestResults-$(TargetFramework).trx --results-directory $([MSBuild]::NormalizePath('$(MSBuildProjectDirectory)', '..', '..')) 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /tests/Chisel.Tests/ChiseledAppTests.RunTestApp_non-windows.verified.mermaid: -------------------------------------------------------------------------------- 1 | %% Generated by https://github.com/0xced/Chisel 2 | 3 | graph LR 4 | 5 | classDef root stroke-width:4px 6 | classDef default fill:aquamarine,stroke:#009061,color:#333333 7 | classDef removed fill:lightcoral,stroke:#A42A2A 8 | 9 | Azure.Core/1.38.0 --> Microsoft.Bcl.AsyncInterfaces/1.1.1 10 | Azure.Core/1.38.0 --> System.ClientModel/1.0.0 11 | Azure.Core/1.38.0 --> System.Diagnostics.DiagnosticSource/6.0.1 12 | Azure.Core/1.38.0 --> System.Memory.Data/1.0.2 13 | Azure.Core/1.38.0 --> System.Text.Encodings.Web/9.0.5 14 | Azure.Core/1.38.0 --> System.Text.Json/9.0.5 15 | Azure.Identity/1.11.4 --> Azure.Core/1.38.0 16 | Azure.Identity/1.11.4 --> Microsoft.Identity.Client/4.61.3 17 | Azure.Identity/1.11.4 --> Microsoft.Identity.Client.Extensions.Msal/4.61.3 18 | Azure.Identity/1.11.4 --> System.Security.Cryptography.ProtectedData/8.0.0 19 | Azure.Identity/1.11.4 --> System.Text.Json/9.0.5 20 | Microsoft.Data.SqlClient/6.0.2{{Microsoft.Data.SqlClient/6.0.2}} --> Azure.Identity/1.11.4 21 | Microsoft.Data.SqlClient/6.0.2{{Microsoft.Data.SqlClient/6.0.2}} --> Microsoft.Bcl.Cryptography/8.0.0 22 | Microsoft.Data.SqlClient/6.0.2{{Microsoft.Data.SqlClient/6.0.2}} --> Microsoft.Extensions.Caching.Memory/8.0.1 23 | Microsoft.Data.SqlClient/6.0.2{{Microsoft.Data.SqlClient/6.0.2}} --> Microsoft.IdentityModel.JsonWebTokens/7.5.0 24 | Microsoft.Data.SqlClient/6.0.2{{Microsoft.Data.SqlClient/6.0.2}} --> Microsoft.IdentityModel.Protocols.OpenIdConnect/7.5.0 25 | Microsoft.Data.SqlClient/6.0.2{{Microsoft.Data.SqlClient/6.0.2}} --> Microsoft.SqlServer.Server/1.0.0 26 | Microsoft.Data.SqlClient/6.0.2{{Microsoft.Data.SqlClient/6.0.2}} --> System.Configuration.ConfigurationManager/8.0.1 27 | Microsoft.Data.SqlClient/6.0.2{{Microsoft.Data.SqlClient/6.0.2}} --> System.Security.Cryptography.Pkcs/8.0.1 28 | Microsoft.Extensions.Caching.Abstractions/8.0.0 --> Microsoft.Extensions.Primitives/8.0.0 29 | Microsoft.Extensions.Caching.Memory/8.0.1 --> Microsoft.Extensions.Caching.Abstractions/8.0.0 30 | Microsoft.Extensions.Caching.Memory/8.0.1 --> Microsoft.Extensions.DependencyInjection.Abstractions/8.0.2 31 | Microsoft.Extensions.Caching.Memory/8.0.1 --> Microsoft.Extensions.Logging.Abstractions/8.0.2 32 | Microsoft.Extensions.Caching.Memory/8.0.1 --> Microsoft.Extensions.Options/8.0.2 33 | Microsoft.Extensions.Caching.Memory/8.0.1 --> Microsoft.Extensions.Primitives/8.0.0 34 | Microsoft.Extensions.DependencyModel/9.0.5{{Microsoft.Extensions.DependencyModel/9.0.5}} --> System.Text.Encodings.Web/9.0.5 35 | Microsoft.Extensions.DependencyModel/9.0.5{{Microsoft.Extensions.DependencyModel/9.0.5}} --> System.Text.Json/9.0.5 36 | Microsoft.Extensions.Logging.Abstractions/8.0.2 --> Microsoft.Extensions.DependencyInjection.Abstractions/8.0.2 37 | Microsoft.Extensions.Options/8.0.2 --> Microsoft.Extensions.DependencyInjection.Abstractions/8.0.2 38 | Microsoft.Extensions.Options/8.0.2 --> Microsoft.Extensions.Primitives/8.0.0 39 | Microsoft.Identity.Client/4.61.3 --> Microsoft.IdentityModel.Abstractions/7.5.0 40 | Microsoft.Identity.Client/4.61.3 --> System.Diagnostics.DiagnosticSource/6.0.1 41 | Microsoft.Identity.Client.Extensions.Msal/4.61.3 --> Microsoft.Identity.Client/4.61.3 42 | Microsoft.Identity.Client.Extensions.Msal/4.61.3 --> System.Security.Cryptography.ProtectedData/8.0.0 43 | Microsoft.IdentityModel.JsonWebTokens/7.5.0 --> Microsoft.IdentityModel.Tokens/7.5.0 44 | Microsoft.IdentityModel.Logging/7.5.0 --> Microsoft.IdentityModel.Abstractions/7.5.0 45 | Microsoft.IdentityModel.Protocols/7.5.0 --> Microsoft.IdentityModel.Tokens/7.5.0 46 | Microsoft.IdentityModel.Protocols.OpenIdConnect/7.5.0 --> Microsoft.IdentityModel.Protocols/7.5.0 47 | Microsoft.IdentityModel.Protocols.OpenIdConnect/7.5.0 --> System.IdentityModel.Tokens.Jwt/7.5.0 48 | Microsoft.IdentityModel.Tokens/7.5.0 --> Microsoft.IdentityModel.Logging/7.5.0 49 | System.ClientModel/1.0.0 --> System.Memory.Data/1.0.2 50 | System.ClientModel/1.0.0 --> System.Text.Json/9.0.5 51 | System.Configuration.ConfigurationManager/8.0.1 --> System.Diagnostics.EventLog/8.0.1 52 | System.Configuration.ConfigurationManager/8.0.1 --> System.Security.Cryptography.ProtectedData/8.0.0 53 | System.Diagnostics.DiagnosticSource/6.0.1 --> System.Runtime.CompilerServices.Unsafe/6.0.0 54 | System.IdentityModel.Tokens.Jwt/7.5.0 --> Microsoft.IdentityModel.JsonWebTokens/7.5.0 55 | System.IdentityModel.Tokens.Jwt/7.5.0 --> Microsoft.IdentityModel.Tokens/7.5.0 56 | System.Memory.Data/1.0.2 --> System.Text.Encodings.Web/9.0.5 57 | System.Memory.Data/1.0.2 --> System.Text.Json/9.0.5 58 | System.Text.Json/9.0.5 --> System.IO.Pipelines/9.0.5 59 | System.Text.Json/9.0.5 --> System.Text.Encodings.Web/9.0.5 60 | 61 | class Azure.Core/1.38.0 removed 62 | class Azure.Identity/1.11.4 removed 63 | class Microsoft.Bcl.AsyncInterfaces/1.1.1 removed 64 | class Microsoft.Bcl.Cryptography/8.0.0 default 65 | class Microsoft.Data.SqlClient/6.0.2 root 66 | class Microsoft.Data.SqlClient/6.0.2 default 67 | class Microsoft.Extensions.Caching.Abstractions/8.0.0 default 68 | class Microsoft.Extensions.Caching.Memory/8.0.1 default 69 | class Microsoft.Extensions.DependencyInjection.Abstractions/8.0.2 default 70 | class Microsoft.Extensions.DependencyModel/9.0.5 root 71 | class Microsoft.Extensions.DependencyModel/9.0.5 default 72 | class Microsoft.Extensions.Logging.Abstractions/8.0.2 default 73 | class Microsoft.Extensions.Options/8.0.2 default 74 | class Microsoft.Extensions.Primitives/8.0.0 default 75 | class Microsoft.Identity.Client/4.61.3 removed 76 | class Microsoft.Identity.Client.Extensions.Msal/4.61.3 removed 77 | class Microsoft.IdentityModel.Abstractions/7.5.0 removed 78 | class Microsoft.IdentityModel.JsonWebTokens/7.5.0 removed 79 | class Microsoft.IdentityModel.Logging/7.5.0 removed 80 | class Microsoft.IdentityModel.Protocols/7.5.0 removed 81 | class Microsoft.IdentityModel.Protocols.OpenIdConnect/7.5.0 removed 82 | class Microsoft.IdentityModel.Tokens/7.5.0 removed 83 | class Microsoft.SqlServer.Server/1.0.0 default 84 | class System.ClientModel/1.0.0 removed 85 | class System.Configuration.ConfigurationManager/8.0.1 default 86 | class System.Diagnostics.DiagnosticSource/6.0.1 removed 87 | class System.Diagnostics.EventLog/8.0.1 default 88 | class System.IdentityModel.Tokens.Jwt/7.5.0 removed 89 | class System.IO.Pipelines/9.0.5 default 90 | class System.Memory.Data/1.0.2 removed 91 | class System.Runtime.CompilerServices.Unsafe/6.0.0 removed 92 | class System.Security.Cryptography.Pkcs/8.0.1 default 93 | class System.Security.Cryptography.ProtectedData/8.0.0 default 94 | class System.Text.Encodings.Web/9.0.5 default 95 | class System.Text.Json/9.0.5 default 96 | -------------------------------------------------------------------------------- /tests/Chisel.Tests/ChiseledAppTests.RunTestApp_windows.verified.mermaid: -------------------------------------------------------------------------------- 1 | %% Generated by https://github.com/0xced/Chisel 2 | 3 | graph LR 4 | 5 | classDef root stroke-width:4px 6 | classDef default fill:aquamarine,stroke:#009061,color:#333333 7 | classDef removed fill:lightcoral,stroke:#A42A2A 8 | 9 | Azure.Core/1.38.0 --> Microsoft.Bcl.AsyncInterfaces/1.1.1 10 | Azure.Core/1.38.0 --> System.ClientModel/1.0.0 11 | Azure.Core/1.38.0 --> System.Diagnostics.DiagnosticSource/6.0.1 12 | Azure.Core/1.38.0 --> System.Memory.Data/1.0.2 13 | Azure.Core/1.38.0 --> System.Text.Encodings.Web/9.0.5 14 | Azure.Core/1.38.0 --> System.Text.Json/9.0.5 15 | Azure.Identity/1.11.4 --> Azure.Core/1.38.0 16 | Azure.Identity/1.11.4 --> Microsoft.Identity.Client/4.61.3 17 | Azure.Identity/1.11.4 --> Microsoft.Identity.Client.Extensions.Msal/4.61.3 18 | Azure.Identity/1.11.4 --> System.Security.Cryptography.ProtectedData/8.0.0 19 | Azure.Identity/1.11.4 --> System.Text.Json/9.0.5 20 | Microsoft.Data.SqlClient/6.0.2{{Microsoft.Data.SqlClient/6.0.2}} --> Azure.Identity/1.11.4 21 | Microsoft.Data.SqlClient/6.0.2{{Microsoft.Data.SqlClient/6.0.2}} --> Microsoft.Bcl.Cryptography/8.0.0 22 | Microsoft.Data.SqlClient/6.0.2{{Microsoft.Data.SqlClient/6.0.2}} --> Microsoft.Data.SqlClient.SNI.runtime/6.0.2 23 | Microsoft.Data.SqlClient/6.0.2{{Microsoft.Data.SqlClient/6.0.2}} --> Microsoft.Extensions.Caching.Memory/8.0.1 24 | Microsoft.Data.SqlClient/6.0.2{{Microsoft.Data.SqlClient/6.0.2}} --> Microsoft.IdentityModel.JsonWebTokens/7.5.0 25 | Microsoft.Data.SqlClient/6.0.2{{Microsoft.Data.SqlClient/6.0.2}} --> Microsoft.IdentityModel.Protocols.OpenIdConnect/7.5.0 26 | Microsoft.Data.SqlClient/6.0.2{{Microsoft.Data.SqlClient/6.0.2}} --> Microsoft.SqlServer.Server/1.0.0 27 | Microsoft.Data.SqlClient/6.0.2{{Microsoft.Data.SqlClient/6.0.2}} --> System.Configuration.ConfigurationManager/8.0.1 28 | Microsoft.Data.SqlClient/6.0.2{{Microsoft.Data.SqlClient/6.0.2}} --> System.Security.Cryptography.Pkcs/8.0.1 29 | Microsoft.Extensions.Caching.Abstractions/8.0.0 --> Microsoft.Extensions.Primitives/8.0.0 30 | Microsoft.Extensions.Caching.Memory/8.0.1 --> Microsoft.Extensions.Caching.Abstractions/8.0.0 31 | Microsoft.Extensions.Caching.Memory/8.0.1 --> Microsoft.Extensions.DependencyInjection.Abstractions/8.0.2 32 | Microsoft.Extensions.Caching.Memory/8.0.1 --> Microsoft.Extensions.Logging.Abstractions/8.0.2 33 | Microsoft.Extensions.Caching.Memory/8.0.1 --> Microsoft.Extensions.Options/8.0.2 34 | Microsoft.Extensions.Caching.Memory/8.0.1 --> Microsoft.Extensions.Primitives/8.0.0 35 | Microsoft.Extensions.DependencyModel/9.0.5{{Microsoft.Extensions.DependencyModel/9.0.5}} --> System.Text.Encodings.Web/9.0.5 36 | Microsoft.Extensions.DependencyModel/9.0.5{{Microsoft.Extensions.DependencyModel/9.0.5}} --> System.Text.Json/9.0.5 37 | Microsoft.Extensions.Logging.Abstractions/8.0.2 --> Microsoft.Extensions.DependencyInjection.Abstractions/8.0.2 38 | Microsoft.Extensions.Options/8.0.2 --> Microsoft.Extensions.DependencyInjection.Abstractions/8.0.2 39 | Microsoft.Extensions.Options/8.0.2 --> Microsoft.Extensions.Primitives/8.0.0 40 | Microsoft.Identity.Client/4.61.3 --> Microsoft.IdentityModel.Abstractions/7.5.0 41 | Microsoft.Identity.Client/4.61.3 --> System.Diagnostics.DiagnosticSource/6.0.1 42 | Microsoft.Identity.Client.Extensions.Msal/4.61.3 --> Microsoft.Identity.Client/4.61.3 43 | Microsoft.Identity.Client.Extensions.Msal/4.61.3 --> System.Security.Cryptography.ProtectedData/8.0.0 44 | Microsoft.IdentityModel.JsonWebTokens/7.5.0 --> Microsoft.IdentityModel.Tokens/7.5.0 45 | Microsoft.IdentityModel.Logging/7.5.0 --> Microsoft.IdentityModel.Abstractions/7.5.0 46 | Microsoft.IdentityModel.Protocols/7.5.0 --> Microsoft.IdentityModel.Tokens/7.5.0 47 | Microsoft.IdentityModel.Protocols.OpenIdConnect/7.5.0 --> Microsoft.IdentityModel.Protocols/7.5.0 48 | Microsoft.IdentityModel.Protocols.OpenIdConnect/7.5.0 --> System.IdentityModel.Tokens.Jwt/7.5.0 49 | Microsoft.IdentityModel.Tokens/7.5.0 --> Microsoft.IdentityModel.Logging/7.5.0 50 | System.ClientModel/1.0.0 --> System.Memory.Data/1.0.2 51 | System.ClientModel/1.0.0 --> System.Text.Json/9.0.5 52 | System.Configuration.ConfigurationManager/8.0.1 --> System.Diagnostics.EventLog/8.0.1 53 | System.Configuration.ConfigurationManager/8.0.1 --> System.Security.Cryptography.ProtectedData/8.0.0 54 | System.Diagnostics.DiagnosticSource/6.0.1 --> System.Runtime.CompilerServices.Unsafe/6.0.0 55 | System.IdentityModel.Tokens.Jwt/7.5.0 --> Microsoft.IdentityModel.JsonWebTokens/7.5.0 56 | System.IdentityModel.Tokens.Jwt/7.5.0 --> Microsoft.IdentityModel.Tokens/7.5.0 57 | System.Memory.Data/1.0.2 --> System.Text.Encodings.Web/9.0.5 58 | System.Memory.Data/1.0.2 --> System.Text.Json/9.0.5 59 | System.Text.Json/9.0.5 --> System.IO.Pipelines/9.0.5 60 | System.Text.Json/9.0.5 --> System.Text.Encodings.Web/9.0.5 61 | 62 | class Azure.Core/1.38.0 removed 63 | class Azure.Identity/1.11.4 removed 64 | class Microsoft.Bcl.AsyncInterfaces/1.1.1 removed 65 | class Microsoft.Bcl.Cryptography/8.0.0 default 66 | class Microsoft.Data.SqlClient/6.0.2 root 67 | class Microsoft.Data.SqlClient/6.0.2 default 68 | class Microsoft.Data.SqlClient.SNI.runtime/6.0.2 default 69 | class Microsoft.Extensions.Caching.Abstractions/8.0.0 default 70 | class Microsoft.Extensions.Caching.Memory/8.0.1 default 71 | class Microsoft.Extensions.DependencyInjection.Abstractions/8.0.2 default 72 | class Microsoft.Extensions.DependencyModel/9.0.5 root 73 | class Microsoft.Extensions.DependencyModel/9.0.5 default 74 | class Microsoft.Extensions.Logging.Abstractions/8.0.2 default 75 | class Microsoft.Extensions.Options/8.0.2 default 76 | class Microsoft.Extensions.Primitives/8.0.0 default 77 | class Microsoft.Identity.Client/4.61.3 removed 78 | class Microsoft.Identity.Client.Extensions.Msal/4.61.3 removed 79 | class Microsoft.IdentityModel.Abstractions/7.5.0 removed 80 | class Microsoft.IdentityModel.JsonWebTokens/7.5.0 removed 81 | class Microsoft.IdentityModel.Logging/7.5.0 removed 82 | class Microsoft.IdentityModel.Protocols/7.5.0 removed 83 | class Microsoft.IdentityModel.Protocols.OpenIdConnect/7.5.0 removed 84 | class Microsoft.IdentityModel.Tokens/7.5.0 removed 85 | class Microsoft.SqlServer.Server/1.0.0 default 86 | class System.ClientModel/1.0.0 removed 87 | class System.Configuration.ConfigurationManager/8.0.1 default 88 | class System.Diagnostics.DiagnosticSource/6.0.1 removed 89 | class System.Diagnostics.EventLog/8.0.1 default 90 | class System.IdentityModel.Tokens.Jwt/7.5.0 removed 91 | class System.IO.Pipelines/9.0.5 default 92 | class System.Memory.Data/1.0.2 removed 93 | class System.Runtime.CompilerServices.Unsafe/6.0.0 removed 94 | class System.Security.Cryptography.Pkcs/8.0.1 default 95 | class System.Security.Cryptography.ProtectedData/8.0.0 default 96 | class System.Text.Encodings.Web/9.0.5 default 97 | class System.Text.Json/9.0.5 default 98 | -------------------------------------------------------------------------------- /tests/Chisel.Tests/ChiseledAppTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using AwesomeAssertions; 7 | using AwesomeAssertions.Execution; 8 | using CliWrap; 9 | using CliWrap.Exceptions; 10 | using VerifyXunit; 11 | using Xunit; 12 | 13 | namespace Chisel.Tests; 14 | 15 | [Trait("Category", "Integration")] 16 | public sealed class ChiseledAppTests(ITestOutputHelper outputHelper, TestApp testApp) : IDisposable, IClassFixture 17 | { 18 | private readonly AssertionScope _scope = new(); 19 | 20 | public void Dispose() => _scope.Dispose(); 21 | 22 | public static readonly TheoryData PublishModeData = new(Enum.GetValues()); 23 | 24 | [Theory] 25 | [MemberData(nameof(PublishModeData))] 26 | public async Task RunTestApp(PublishMode publishMode) 27 | { 28 | var (stdOut, stdErr) = await RunTestAppAsync(publishMode); 29 | var actualDlls = stdOut.Split(Environment.NewLine).Where(e => e.EndsWith(".dll")).ToHashSet(); 30 | var platformDlls = OperatingSystem.IsWindows() ? ["Microsoft.Data.SqlClient.SNI.dll", "System.Diagnostics.EventLog.Messages.dll"] : Array.Empty(); 31 | var expectedDlls = platformDlls.Concat( 32 | [ 33 | "Microsoft.Bcl.Cryptography.dll", 34 | "Microsoft.Data.SqlClient.dll", 35 | "Microsoft.Extensions.Caching.Abstractions.dll", 36 | "Microsoft.Extensions.Caching.Memory.dll", 37 | "Microsoft.Extensions.DependencyInjection.Abstractions.dll", 38 | "Microsoft.Extensions.DependencyModel.dll", 39 | "Microsoft.Extensions.Logging.Abstractions.dll", 40 | "Microsoft.Extensions.Options.dll", 41 | "Microsoft.Extensions.Primitives.dll", 42 | "Microsoft.SqlServer.Server.dll", 43 | "System.Configuration.ConfigurationManager.dll", 44 | "System.Diagnostics.EventLog.dll", 45 | "System.IO.Pipelines.dll", 46 | "System.Security.Cryptography.Pkcs.dll", 47 | "System.Security.Cryptography.ProtectedData.dll", 48 | "System.Text.Encodings.Web.dll", 49 | "System.Text.Json.dll", 50 | "TestApp.dll", 51 | ]).ToHashSet(); 52 | actualDlls.Except(expectedDlls).Should().BeEmpty(); 53 | expectedDlls.Except(actualDlls).Should().BeEmpty(); 54 | stdOut.Should().Contain("[OK] "); 55 | stdErr.Should().BeEmpty(); 56 | 57 | await Verifier.VerifyFile(testApp.IntermediateOutputPath.File("TestApp.Chisel.mermaid")) 58 | .UseTextForParameters(OperatingSystem.IsWindows() ? "windows" : "non-windows") 59 | .DisableRequireUniquePrefix(); 60 | } 61 | 62 | private async Task<(string StdOut, string StdErr)> RunTestAppAsync(PublishMode publishMode, params string[] args) 63 | { 64 | var stdOutBuilder = new StringBuilder(); 65 | var stdErrBuilder = new StringBuilder(); 66 | 67 | var command = Cli.Wrap(testApp.GetExecutablePath(publishMode)) 68 | .WithArguments(args) 69 | .WithValidation(CommandResultValidation.None) 70 | .WithStandardOutputPipe(PipeTarget.ToStringBuilder(stdOutBuilder)) 71 | .WithStandardErrorPipe(PipeTarget.ToStringBuilder(stdErrBuilder)); 72 | 73 | outputHelper.WriteLine(command.ToString()); 74 | 75 | var stopwatch = Stopwatch.StartNew(); 76 | var result = await command.ExecuteAsync(); 77 | var executionTime = stopwatch.ElapsedMilliseconds; 78 | 79 | var stdOut = stdOutBuilder.ToString().Trim(); 80 | var stdErr = stdErrBuilder.ToString().Trim(); 81 | 82 | outputHelper.WriteLine($"⌚ Executed in {executionTime} ms"); 83 | outputHelper.WriteLine(stdOut); 84 | 85 | if (result.ExitCode != 0) 86 | { 87 | throw new CommandExecutionException(command, result.ExitCode, $"An unexpected exception has occurred while running {command}{Environment.NewLine}{stdErr}".Trim()); 88 | } 89 | 90 | return (stdOut, stdErr); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /tests/Chisel.Tests/DependencyGraphTest.MongoDbGraph_writeIgnoredPackages=False_format=graphviz.verified.gv: -------------------------------------------------------------------------------- 1 | # Generated by https://github.com/0xced/Chisel 2 | 3 | digraph 4 | { 5 | rankdir=LR 6 | node [ fontname = "Segoe UI, sans-serif", shape = box, style = filled, fillcolor = aquamarine, color = "#009061", fontcolor = "#333333" ] 7 | 8 | "AWSSDK.SecurityToken" -> "AWSSDK.Core" 9 | "DnsClient" -> "Microsoft.Win32.Registry" 10 | "Microsoft.Extensions.Logging.Abstractions" -> "Microsoft.Extensions.DependencyInjection.Abstractions" 11 | "Microsoft.Win32.Registry" -> "System.Security.AccessControl" 12 | "Microsoft.Win32.Registry" -> "System.Security.Principal.Windows" 13 | "MongoDB.Bson" -> "System.Runtime.CompilerServices.Unsafe" 14 | "MongoDB.Driver" -> "Microsoft.Extensions.Logging.Abstractions" 15 | "MongoDB.Driver" -> "MongoDB.Bson" 16 | "MongoDB.Driver" -> "MongoDB.Driver.Core" 17 | "MongoDB.Driver" -> "MongoDB.Libmongocrypt" 18 | "MongoDB.Driver.Core" -> "AWSSDK.SecurityToken" 19 | "MongoDB.Driver.Core" -> "DnsClient" 20 | "MongoDB.Driver.Core" -> "Microsoft.Extensions.Logging.Abstractions" 21 | "MongoDB.Driver.Core" -> "MongoDB.Bson" 22 | "MongoDB.Driver.Core" -> "MongoDB.Libmongocrypt" 23 | "MongoDB.Driver.Core" -> "SharpCompress" 24 | "MongoDB.Driver.Core" -> "Snappier" 25 | "MongoDB.Driver.Core" -> "ZstdSharp.Port" 26 | "System.Security.AccessControl" -> "System.Security.Principal.Windows" 27 | 28 | "AWSSDK.Core" [ fillcolor = lightcoral, color = "#A42A2A" ] 29 | "AWSSDK.SecurityToken" [ fillcolor = lightcoral, color = "#A42A2A" ] 30 | "ByteSize" [ shape = hexagon, penwidth = 4 ] 31 | "DnsClient" 32 | "Microsoft.Extensions.DependencyInjection.Abstractions" 33 | "Microsoft.Extensions.Logging.Abstractions" [ shape = hexagon, penwidth = 4 ] 34 | "Microsoft.Win32.Registry" 35 | "MongoDB.Bson" 36 | "MongoDB.Driver" [ shape = hexagon, penwidth = 4 ] 37 | "MongoDB.Driver.Core" 38 | "MongoDB.Libmongocrypt" 39 | "SharpCompress" 40 | "Snappier" 41 | "System.Runtime.CompilerServices.Unsafe" 42 | "System.Security.AccessControl" 43 | "System.Security.Principal.Windows" 44 | "ZstdSharp.Port" 45 | } 46 | -------------------------------------------------------------------------------- /tests/Chisel.Tests/DependencyGraphTest.MongoDbGraph_writeIgnoredPackages=False_format=mermaid.verified.mmd: -------------------------------------------------------------------------------- 1 | %% Generated by https://github.com/0xced/Chisel 2 | 3 | graph LR 4 | 5 | classDef root stroke-width:4px 6 | classDef default fill:aquamarine,stroke:#009061,color:#333333 7 | classDef removed fill:lightcoral,stroke:#A42A2A 8 | 9 | AWSSDK.SecurityToken --> AWSSDK.Core 10 | ByteSize{{ByteSize}} 11 | DnsClient --> Microsoft.Win32.Registry 12 | Microsoft.Extensions.Logging.Abstractions{{Microsoft.Extensions.Logging.Abstractions}} --> Microsoft.Extensions.DependencyInjection.Abstractions 13 | Microsoft.Win32.Registry --> System.Security.AccessControl 14 | Microsoft.Win32.Registry --> System.Security.Principal.Windows 15 | MongoDB.Bson --> System.Runtime.CompilerServices.Unsafe 16 | MongoDB.Driver{{MongoDB.Driver}} --> Microsoft.Extensions.Logging.Abstractions 17 | MongoDB.Driver{{MongoDB.Driver}} --> MongoDB.Bson 18 | MongoDB.Driver{{MongoDB.Driver}} --> MongoDB.Driver.Core 19 | MongoDB.Driver{{MongoDB.Driver}} --> MongoDB.Libmongocrypt 20 | MongoDB.Driver.Core --> AWSSDK.SecurityToken 21 | MongoDB.Driver.Core --> DnsClient 22 | MongoDB.Driver.Core --> Microsoft.Extensions.Logging.Abstractions 23 | MongoDB.Driver.Core --> MongoDB.Bson 24 | MongoDB.Driver.Core --> MongoDB.Libmongocrypt 25 | MongoDB.Driver.Core --> SharpCompress 26 | MongoDB.Driver.Core --> Snappier 27 | MongoDB.Driver.Core --> ZstdSharp.Port 28 | System.Security.AccessControl --> System.Security.Principal.Windows 29 | 30 | class AWSSDK.Core removed 31 | class AWSSDK.SecurityToken removed 32 | class ByteSize root 33 | class ByteSize default 34 | class DnsClient default 35 | class Microsoft.Extensions.DependencyInjection.Abstractions default 36 | class Microsoft.Extensions.Logging.Abstractions root 37 | class Microsoft.Extensions.Logging.Abstractions default 38 | class Microsoft.Win32.Registry default 39 | class MongoDB.Bson default 40 | class MongoDB.Driver root 41 | class MongoDB.Driver default 42 | class MongoDB.Driver.Core default 43 | class MongoDB.Libmongocrypt default 44 | class SharpCompress default 45 | class Snappier default 46 | class System.Runtime.CompilerServices.Unsafe default 47 | class System.Security.AccessControl default 48 | class System.Security.Principal.Windows default 49 | class ZstdSharp.Port default 50 | -------------------------------------------------------------------------------- /tests/Chisel.Tests/DependencyGraphTest.MongoDbGraph_writeIgnoredPackages=True_format=graphviz.verified.gv: -------------------------------------------------------------------------------- 1 | # Generated by https://github.com/0xced/Chisel 2 | 3 | digraph 4 | { 5 | rankdir=LR 6 | node [ fontname = "Segoe UI, sans-serif", shape = box, style = filled, fillcolor = aquamarine, color = "#009061", fontcolor = "#333333" ] 7 | 8 | "AWSSDK.SecurityToken" -> "AWSSDK.Core" 9 | "DnsClient" -> "Microsoft.Win32.Registry" 10 | "Docker.DotNet" -> "Newtonsoft.Json" 11 | "Docker.DotNet.X509" -> "Docker.DotNet" 12 | "Microsoft.Extensions.Logging.Abstractions" -> "Microsoft.Extensions.DependencyInjection.Abstractions" 13 | "Microsoft.Win32.Registry" -> "System.Security.AccessControl" 14 | "Microsoft.Win32.Registry" -> "System.Security.Principal.Windows" 15 | "MongoDB.Bson" -> "System.Runtime.CompilerServices.Unsafe" 16 | "MongoDB.Driver" -> "Microsoft.Extensions.Logging.Abstractions" 17 | "MongoDB.Driver" -> "MongoDB.Bson" 18 | "MongoDB.Driver" -> "MongoDB.Driver.Core" 19 | "MongoDB.Driver" -> "MongoDB.Libmongocrypt" 20 | "MongoDB.Driver.Core" -> "AWSSDK.SecurityToken" 21 | "MongoDB.Driver.Core" -> "DnsClient" 22 | "MongoDB.Driver.Core" -> "Microsoft.Extensions.Logging.Abstractions" 23 | "MongoDB.Driver.Core" -> "MongoDB.Bson" 24 | "MongoDB.Driver.Core" -> "MongoDB.Libmongocrypt" 25 | "MongoDB.Driver.Core" -> "SharpCompress" 26 | "MongoDB.Driver.Core" -> "Snappier" 27 | "MongoDB.Driver.Core" -> "ZstdSharp.Port" 28 | "SSH.NET" -> "SshNet.Security.Cryptography" 29 | "System.Security.AccessControl" -> "System.Security.Principal.Windows" 30 | "System.Text.Encodings.Web" -> "System.Runtime.CompilerServices.Unsafe" 31 | "System.Text.Json" -> "System.Runtime.CompilerServices.Unsafe" 32 | "System.Text.Json" -> "System.Text.Encodings.Web" 33 | "Testcontainers" -> "BouncyCastle.Cryptography" 34 | "Testcontainers" -> "Docker.DotNet" 35 | "Testcontainers" -> "Docker.DotNet.X509" 36 | "Testcontainers" -> "Microsoft.Bcl.AsyncInterfaces" 37 | "Testcontainers" -> "Microsoft.Extensions.Logging.Abstractions" 38 | "Testcontainers" -> "SharpZipLib" 39 | "Testcontainers" -> "SSH.NET" 40 | "Testcontainers" -> "System.Text.Json" 41 | "Testcontainers.MongoDb" -> "Testcontainers" 42 | 43 | "AWSSDK.Core" [ fillcolor = lightcoral, color = "#A42A2A" ] 44 | "AWSSDK.SecurityToken" [ fillcolor = lightcoral, color = "#A42A2A" ] 45 | "BouncyCastle.Cryptography" [ fillcolor = lightgray, color = "#7A7A7A" ] 46 | "ByteSize" [ shape = hexagon, penwidth = 4 ] 47 | "DnsClient" 48 | "Docker.DotNet" [ fillcolor = lightgray, color = "#7A7A7A" ] 49 | "Docker.DotNet.X509" [ fillcolor = lightgray, color = "#7A7A7A" ] 50 | "Microsoft.Bcl.AsyncInterfaces" [ fillcolor = lightgray, color = "#7A7A7A" ] 51 | "Microsoft.Extensions.DependencyInjection.Abstractions" 52 | "Microsoft.Extensions.Logging.Abstractions" [ shape = hexagon, penwidth = 4 ] 53 | "Microsoft.Win32.Registry" 54 | "MongoDB.Bson" 55 | "MongoDB.Driver" [ shape = hexagon, penwidth = 4 ] 56 | "MongoDB.Driver.Core" 57 | "MongoDB.Libmongocrypt" 58 | "Newtonsoft.Json" [ fillcolor = lightgray, color = "#7A7A7A" ] 59 | "SharpCompress" 60 | "SharpZipLib" [ fillcolor = lightgray, color = "#7A7A7A" ] 61 | "Snappier" 62 | "SSH.NET" [ fillcolor = lightgray, color = "#7A7A7A" ] 63 | "SshNet.Security.Cryptography" [ fillcolor = lightgray, color = "#7A7A7A" ] 64 | "System.Runtime.CompilerServices.Unsafe" 65 | "System.Security.AccessControl" 66 | "System.Security.Principal.Windows" 67 | "System.Text.Encodings.Web" [ fillcolor = lightgray, color = "#7A7A7A" ] 68 | "System.Text.Json" [ fillcolor = lightgray, color = "#7A7A7A" ] 69 | "Testcontainers" [ fillcolor = lightgray, color = "#7A7A7A" ] 70 | "Testcontainers.MongoDb" [ shape = hexagon, penwidth = 4, fillcolor = lightgray, color = "#7A7A7A" ] 71 | "ZstdSharp.Port" 72 | } 73 | -------------------------------------------------------------------------------- /tests/Chisel.Tests/DependencyGraphTest.MongoDbGraph_writeIgnoredPackages=True_format=mermaid.verified.mmd: -------------------------------------------------------------------------------- 1 | %% Generated by https://github.com/0xced/Chisel 2 | 3 | graph LR 4 | 5 | classDef root stroke-width:4px 6 | classDef default fill:aquamarine,stroke:#009061,color:#333333 7 | classDef ignored fill:lightgray,stroke:#7A7A7A 8 | classDef removed fill:lightcoral,stroke:#A42A2A 9 | 10 | AWSSDK.SecurityToken --> AWSSDK.Core 11 | ByteSize{{ByteSize}} 12 | DnsClient --> Microsoft.Win32.Registry 13 | Docker.DotNet --> Newtonsoft.Json 14 | Docker.DotNet.X509 --> Docker.DotNet 15 | Microsoft.Extensions.Logging.Abstractions{{Microsoft.Extensions.Logging.Abstractions}} --> Microsoft.Extensions.DependencyInjection.Abstractions 16 | Microsoft.Win32.Registry --> System.Security.AccessControl 17 | Microsoft.Win32.Registry --> System.Security.Principal.Windows 18 | MongoDB.Bson --> System.Runtime.CompilerServices.Unsafe 19 | MongoDB.Driver{{MongoDB.Driver}} --> Microsoft.Extensions.Logging.Abstractions 20 | MongoDB.Driver{{MongoDB.Driver}} --> MongoDB.Bson 21 | MongoDB.Driver{{MongoDB.Driver}} --> MongoDB.Driver.Core 22 | MongoDB.Driver{{MongoDB.Driver}} --> MongoDB.Libmongocrypt 23 | MongoDB.Driver.Core --> AWSSDK.SecurityToken 24 | MongoDB.Driver.Core --> DnsClient 25 | MongoDB.Driver.Core --> Microsoft.Extensions.Logging.Abstractions 26 | MongoDB.Driver.Core --> MongoDB.Bson 27 | MongoDB.Driver.Core --> MongoDB.Libmongocrypt 28 | MongoDB.Driver.Core --> SharpCompress 29 | MongoDB.Driver.Core --> Snappier 30 | MongoDB.Driver.Core --> ZstdSharp.Port 31 | SSH.NET --> SshNet.Security.Cryptography 32 | System.Security.AccessControl --> System.Security.Principal.Windows 33 | System.Text.Encodings.Web --> System.Runtime.CompilerServices.Unsafe 34 | System.Text.Json --> System.Runtime.CompilerServices.Unsafe 35 | System.Text.Json --> System.Text.Encodings.Web 36 | Testcontainers --> BouncyCastle.Cryptography 37 | Testcontainers --> Docker.DotNet 38 | Testcontainers --> Docker.DotNet.X509 39 | Testcontainers --> Microsoft.Bcl.AsyncInterfaces 40 | Testcontainers --> Microsoft.Extensions.Logging.Abstractions 41 | Testcontainers --> SharpZipLib 42 | Testcontainers --> SSH.NET 43 | Testcontainers --> System.Text.Json 44 | Testcontainers.MongoDb{{Testcontainers.MongoDb}} --> Testcontainers 45 | 46 | class AWSSDK.Core removed 47 | class AWSSDK.SecurityToken removed 48 | class BouncyCastle.Cryptography ignored 49 | class ByteSize root 50 | class ByteSize default 51 | class DnsClient default 52 | class Docker.DotNet ignored 53 | class Docker.DotNet.X509 ignored 54 | class Microsoft.Bcl.AsyncInterfaces ignored 55 | class Microsoft.Extensions.DependencyInjection.Abstractions default 56 | class Microsoft.Extensions.Logging.Abstractions root 57 | class Microsoft.Extensions.Logging.Abstractions default 58 | class Microsoft.Win32.Registry default 59 | class MongoDB.Bson default 60 | class MongoDB.Driver root 61 | class MongoDB.Driver default 62 | class MongoDB.Driver.Core default 63 | class MongoDB.Libmongocrypt default 64 | class Newtonsoft.Json ignored 65 | class SharpCompress default 66 | class SharpZipLib ignored 67 | class Snappier default 68 | class SSH.NET ignored 69 | class SshNet.Security.Cryptography ignored 70 | class System.Runtime.CompilerServices.Unsafe default 71 | class System.Security.AccessControl default 72 | class System.Security.Principal.Windows default 73 | class System.Text.Encodings.Web ignored 74 | class System.Text.Json ignored 75 | class Testcontainers ignored 76 | class Testcontainers.MongoDb root 77 | class Testcontainers.MongoDb ignored 78 | class ZstdSharp.Port default 79 | -------------------------------------------------------------------------------- /tests/Chisel.Tests/DependencyGraphTest.PollyGraphIgnoreGlob_graphviz.verified.gv: -------------------------------------------------------------------------------- 1 | # Generated by https://github.com/0xced/Chisel 2 | 3 | digraph 4 | { 5 | rankdir=LR 6 | node [ fontname = "Segoe UI, sans-serif", shape = box, style = filled, fillcolor = aquamarine, color = "#009061", fontcolor = "#333333" ] 7 | 8 | "Microsoft.Bcl.TimeProvider/8.0.0" -> "Microsoft.Bcl.AsyncInterfaces/6.0.0" 9 | "NETStandard.Library/2.0.3" -> "Microsoft.NETCore.Platforms/1.1.0" 10 | "Polly/8.4.0" -> "Polly.Core/8.4.0" 11 | "Polly.Core/8.4.0" -> "Microsoft.Bcl.AsyncInterfaces/6.0.0" 12 | "Polly.Core/8.4.0" -> "Microsoft.Bcl.TimeProvider/8.0.0" 13 | 14 | "Microsoft.Bcl.AsyncInterfaces/6.0.0" 15 | "Microsoft.Bcl.TimeProvider/8.0.0" 16 | "Microsoft.NETCore.Platforms/1.1.0" 17 | "NETStandard.Library/2.0.3" [ shape = hexagon, penwidth = 4 ] 18 | "Polly/8.4.0" [ shape = hexagon, penwidth = 4 ] 19 | "Polly.Core/8.4.0" 20 | } 21 | -------------------------------------------------------------------------------- /tests/Chisel.Tests/DependencyGraphTest.PollyGraphIgnoreGlob_mermaid.verified.mmd: -------------------------------------------------------------------------------- 1 | %% Generated by https://github.com/0xced/Chisel 2 | 3 | graph LR 4 | 5 | classDef root stroke-width:4px 6 | classDef default fill:aquamarine,stroke:#009061,color:#333333 7 | 8 | Microsoft.Bcl.TimeProvider/8.0.0 --> Microsoft.Bcl.AsyncInterfaces/6.0.0 9 | NETStandard.Library/2.0.3{{NETStandard.Library/2.0.3}} --> Microsoft.NETCore.Platforms/1.1.0 10 | Polly/8.4.0{{Polly/8.4.0}} --> Polly.Core/8.4.0 11 | Polly.Core/8.4.0 --> Microsoft.Bcl.AsyncInterfaces/6.0.0 12 | Polly.Core/8.4.0 --> Microsoft.Bcl.TimeProvider/8.0.0 13 | 14 | class Microsoft.Bcl.AsyncInterfaces/6.0.0 default 15 | class Microsoft.Bcl.TimeProvider/8.0.0 default 16 | class Microsoft.NETCore.Platforms/1.1.0 default 17 | class NETStandard.Library/2.0.3 root 18 | class NETStandard.Library/2.0.3 default 19 | class Polly/8.4.0 root 20 | class Polly/8.4.0 default 21 | class Polly.Core/8.4.0 default 22 | -------------------------------------------------------------------------------- /tests/Chisel.Tests/DependencyGraphTest.SqlClientGraph_graphviz.verified.gv: -------------------------------------------------------------------------------- 1 | # Generated by https://github.com/0xced/Chisel 2 | 3 | digraph 4 | { 5 | rankdir=LR 6 | node [ fontname = "Segoe UI, sans-serif", shape = box, style = filled, fillcolor = aquamarine, color = "#009061", fontcolor = "#333333" ] 7 | 8 | "Azure.Core/1.35.0" -> "Microsoft.Bcl.AsyncInterfaces/1.1.1" 9 | "Azure.Core/1.35.0" -> "System.Diagnostics.DiagnosticSource/6.0.1" 10 | "Azure.Core/1.35.0" -> "System.Memory.Data/8.0.0" 11 | "Azure.Core/1.35.0" -> "System.Text.Encodings.Web/8.0.0" 12 | "Azure.Core/1.35.0" -> "System.Text.Json/8.0.0" 13 | "Azure.Identity/1.10.3" -> "Azure.Core/1.35.0" 14 | "Azure.Identity/1.10.3" -> "Microsoft.Identity.Client/4.56.0" 15 | "Azure.Identity/1.10.3" -> "Microsoft.Identity.Client.Extensions.Msal/4.56.0" 16 | "Azure.Identity/1.10.3" -> "System.Security.Cryptography.ProtectedData/8.0.0" 17 | "Azure.Identity/1.10.3" -> "System.Text.Json/8.0.0" 18 | "Microsoft.Data.SqlClient/5.2.0" -> "Azure.Identity/1.10.3" 19 | "Microsoft.Data.SqlClient/5.2.0" -> "Microsoft.Data.SqlClient.SNI.runtime/5.2.0" 20 | "Microsoft.Data.SqlClient/5.2.0" -> "Microsoft.Identity.Client/4.56.0" 21 | "Microsoft.Data.SqlClient/5.2.0" -> "Microsoft.IdentityModel.JsonWebTokens/6.35.0" 22 | "Microsoft.Data.SqlClient/5.2.0" -> "Microsoft.IdentityModel.Protocols.OpenIdConnect/6.35.0" 23 | "Microsoft.Data.SqlClient/5.2.0" -> "Microsoft.SqlServer.Server/1.0.0" 24 | "Microsoft.Data.SqlClient/5.2.0" -> "System.Configuration.ConfigurationManager/8.0.0" 25 | "Microsoft.Data.SqlClient/5.2.0" -> "System.Runtime.Caching/8.0.0" 26 | "Microsoft.Identity.Client.Extensions.Msal/4.56.0" -> "Microsoft.Identity.Client/4.56.0" 27 | "Microsoft.Identity.Client.Extensions.Msal/4.56.0" -> "System.IO.FileSystem.AccessControl/5.0.0" 28 | "Microsoft.Identity.Client.Extensions.Msal/4.56.0" -> "System.Security.Cryptography.ProtectedData/8.0.0" 29 | "Microsoft.IdentityModel.JsonWebTokens/6.35.0" -> "Microsoft.IdentityModel.Tokens/6.35.0" 30 | "Microsoft.IdentityModel.JsonWebTokens/6.35.0" -> "System.Text.Encodings.Web/8.0.0" 31 | "Microsoft.IdentityModel.JsonWebTokens/6.35.0" -> "System.Text.Json/8.0.0" 32 | "Microsoft.IdentityModel.Logging/6.35.0" -> "Microsoft.IdentityModel.Abstractions/6.35.0" 33 | "Microsoft.IdentityModel.Protocols/6.35.0" -> "Microsoft.IdentityModel.Logging/6.35.0" 34 | "Microsoft.IdentityModel.Protocols/6.35.0" -> "Microsoft.IdentityModel.Tokens/6.35.0" 35 | "Microsoft.IdentityModel.Protocols.OpenIdConnect/6.35.0" -> "Microsoft.IdentityModel.Protocols/6.35.0" 36 | "Microsoft.IdentityModel.Protocols.OpenIdConnect/6.35.0" -> "System.IdentityModel.Tokens.Jwt/6.35.0" 37 | "Microsoft.IdentityModel.Tokens/6.35.0" -> "Microsoft.IdentityModel.Logging/6.35.0" 38 | "Microsoft.IdentityModel.Tokens/6.35.0" -> "System.Security.Cryptography.Cng/4.5.0" 39 | "System.Configuration.ConfigurationManager/8.0.0" -> "System.Diagnostics.EventLog/8.0.0" 40 | "System.Configuration.ConfigurationManager/8.0.0" -> "System.Security.Cryptography.ProtectedData/8.0.0" 41 | "System.Diagnostics.DiagnosticSource/6.0.1" -> "System.Runtime.CompilerServices.Unsafe/6.0.0" 42 | "System.IdentityModel.Tokens.Jwt/6.35.0" -> "Microsoft.IdentityModel.JsonWebTokens/6.35.0" 43 | "System.IdentityModel.Tokens.Jwt/6.35.0" -> "Microsoft.IdentityModel.Tokens/6.35.0" 44 | "System.IO.FileSystem.AccessControl/5.0.0" -> "System.Security.AccessControl/5.0.0" 45 | "System.IO.FileSystem.AccessControl/5.0.0" -> "System.Security.Principal.Windows/5.0.0" 46 | "System.Memory.Data/8.0.0" -> "System.Text.Json/8.0.0" 47 | "System.Runtime.Caching/8.0.0" -> "System.Configuration.ConfigurationManager/8.0.0" 48 | "System.Security.AccessControl/5.0.0" -> "System.Security.Principal.Windows/5.0.0" 49 | "System.Text.Json/8.0.0" -> "System.Text.Encodings.Web/8.0.0" 50 | 51 | "Azure.Core/1.35.0" [ fillcolor = lightcoral, color = "#A42A2A" ] 52 | "Azure.Identity/1.10.3" [ fillcolor = lightcoral, color = "#A42A2A" ] 53 | "Microsoft.Bcl.AsyncInterfaces/1.1.1" [ fillcolor = lightcoral, color = "#A42A2A" ] 54 | "Microsoft.Data.SqlClient/5.2.0" [ shape = hexagon, penwidth = 4 ] 55 | "Microsoft.Data.SqlClient.SNI.runtime/5.2.0" 56 | "Microsoft.Identity.Client/4.56.0" [ shape = hexagon, penwidth = 4, fillcolor = skyblue, color = "#05587C" ] 57 | "Microsoft.Identity.Client.Extensions.Msal/4.56.0" [ fillcolor = lightcoral, color = "#A42A2A" ] 58 | "Microsoft.IdentityModel.Abstractions/6.35.0" [ fillcolor = lightcoral, color = "#A42A2A" ] 59 | "Microsoft.IdentityModel.JsonWebTokens/6.35.0" [ fillcolor = lightcoral, color = "#A42A2A" ] 60 | "Microsoft.IdentityModel.Logging/6.35.0" [ fillcolor = lightcoral, color = "#A42A2A" ] 61 | "Microsoft.IdentityModel.Protocols/6.35.0" [ fillcolor = lightcoral, color = "#A42A2A" ] 62 | "Microsoft.IdentityModel.Protocols.OpenIdConnect/6.35.0" [ fillcolor = lightcoral, color = "#A42A2A" ] 63 | "Microsoft.IdentityModel.Tokens/6.35.0" [ fillcolor = lightcoral, color = "#A42A2A" ] 64 | "Microsoft.SqlServer.Server/1.0.0" 65 | "System.Configuration.ConfigurationManager/8.0.0" 66 | "System.Diagnostics.DiagnosticSource/6.0.1" [ fillcolor = lightcoral, color = "#A42A2A" ] 67 | "System.Diagnostics.EventLog/8.0.0" 68 | "System.IdentityModel.Tokens.Jwt/6.35.0" [ fillcolor = lightcoral, color = "#A42A2A" ] 69 | "System.IO.FileSystem.AccessControl/5.0.0" [ fillcolor = lightcoral, color = "#A42A2A" ] 70 | "System.Memory.Data/8.0.0" [ shape = hexagon, penwidth = 4 ] 71 | "System.Runtime.Caching/8.0.0" 72 | "System.Runtime.CompilerServices.Unsafe/6.0.0" [ fillcolor = lightcoral, color = "#A42A2A" ] 73 | "System.Security.AccessControl/5.0.0" [ fillcolor = lightcoral, color = "#A42A2A" ] 74 | "System.Security.Cryptography.Cng/4.5.0" [ fillcolor = lightcoral, color = "#A42A2A" ] 75 | "System.Security.Cryptography.ProtectedData/8.0.0" 76 | "System.Security.Principal.Windows/5.0.0" [ fillcolor = lightcoral, color = "#A42A2A" ] 77 | "System.Text.Encodings.Web/8.0.0" 78 | "System.Text.Json/8.0.0" 79 | } 80 | -------------------------------------------------------------------------------- /tests/Chisel.Tests/DependencyGraphTest.SqlClientGraph_mermaid.verified.mmd: -------------------------------------------------------------------------------- 1 | %% Generated by https://github.com/0xced/Chisel 2 | 3 | graph LR 4 | 5 | classDef root stroke-width:4px 6 | classDef default fill:aquamarine,stroke:#009061,color:#333333 7 | classDef project fill:skyblue,stroke:#05587C 8 | classDef removed fill:lightcoral,stroke:#A42A2A 9 | 10 | Azure.Core/1.35.0 --> Microsoft.Bcl.AsyncInterfaces/1.1.1 11 | Azure.Core/1.35.0 --> System.Diagnostics.DiagnosticSource/6.0.1 12 | Azure.Core/1.35.0 --> System.Memory.Data/8.0.0 13 | Azure.Core/1.35.0 --> System.Text.Encodings.Web/8.0.0 14 | Azure.Core/1.35.0 --> System.Text.Json/8.0.0 15 | Azure.Identity/1.10.3 --> Azure.Core/1.35.0 16 | Azure.Identity/1.10.3 --> Microsoft.Identity.Client/4.56.0 17 | Azure.Identity/1.10.3 --> Microsoft.Identity.Client.Extensions.Msal/4.56.0 18 | Azure.Identity/1.10.3 --> System.Security.Cryptography.ProtectedData/8.0.0 19 | Azure.Identity/1.10.3 --> System.Text.Json/8.0.0 20 | Microsoft.Data.SqlClient/5.2.0{{Microsoft.Data.SqlClient/5.2.0}} --> Azure.Identity/1.10.3 21 | Microsoft.Data.SqlClient/5.2.0{{Microsoft.Data.SqlClient/5.2.0}} --> Microsoft.Data.SqlClient.SNI.runtime/5.2.0 22 | Microsoft.Data.SqlClient/5.2.0{{Microsoft.Data.SqlClient/5.2.0}} --> Microsoft.Identity.Client/4.56.0 23 | Microsoft.Data.SqlClient/5.2.0{{Microsoft.Data.SqlClient/5.2.0}} --> Microsoft.IdentityModel.JsonWebTokens/6.35.0 24 | Microsoft.Data.SqlClient/5.2.0{{Microsoft.Data.SqlClient/5.2.0}} --> Microsoft.IdentityModel.Protocols.OpenIdConnect/6.35.0 25 | Microsoft.Data.SqlClient/5.2.0{{Microsoft.Data.SqlClient/5.2.0}} --> Microsoft.SqlServer.Server/1.0.0 26 | Microsoft.Data.SqlClient/5.2.0{{Microsoft.Data.SqlClient/5.2.0}} --> System.Configuration.ConfigurationManager/8.0.0 27 | Microsoft.Data.SqlClient/5.2.0{{Microsoft.Data.SqlClient/5.2.0}} --> System.Runtime.Caching/8.0.0 28 | Microsoft.Identity.Client/4.56.0{{Microsoft.Identity.Client/4.56.0}} 29 | Microsoft.Identity.Client.Extensions.Msal/4.56.0 --> Microsoft.Identity.Client/4.56.0 30 | Microsoft.Identity.Client.Extensions.Msal/4.56.0 --> System.IO.FileSystem.AccessControl/5.0.0 31 | Microsoft.Identity.Client.Extensions.Msal/4.56.0 --> System.Security.Cryptography.ProtectedData/8.0.0 32 | Microsoft.IdentityModel.JsonWebTokens/6.35.0 --> Microsoft.IdentityModel.Tokens/6.35.0 33 | Microsoft.IdentityModel.JsonWebTokens/6.35.0 --> System.Text.Encodings.Web/8.0.0 34 | Microsoft.IdentityModel.JsonWebTokens/6.35.0 --> System.Text.Json/8.0.0 35 | Microsoft.IdentityModel.Logging/6.35.0 --> Microsoft.IdentityModel.Abstractions/6.35.0 36 | Microsoft.IdentityModel.Protocols/6.35.0 --> Microsoft.IdentityModel.Logging/6.35.0 37 | Microsoft.IdentityModel.Protocols/6.35.0 --> Microsoft.IdentityModel.Tokens/6.35.0 38 | Microsoft.IdentityModel.Protocols.OpenIdConnect/6.35.0 --> Microsoft.IdentityModel.Protocols/6.35.0 39 | Microsoft.IdentityModel.Protocols.OpenIdConnect/6.35.0 --> System.IdentityModel.Tokens.Jwt/6.35.0 40 | Microsoft.IdentityModel.Tokens/6.35.0 --> Microsoft.IdentityModel.Logging/6.35.0 41 | Microsoft.IdentityModel.Tokens/6.35.0 --> System.Security.Cryptography.Cng/4.5.0 42 | System.Configuration.ConfigurationManager/8.0.0 --> System.Diagnostics.EventLog/8.0.0 43 | System.Configuration.ConfigurationManager/8.0.0 --> System.Security.Cryptography.ProtectedData/8.0.0 44 | System.Diagnostics.DiagnosticSource/6.0.1 --> System.Runtime.CompilerServices.Unsafe/6.0.0 45 | System.IdentityModel.Tokens.Jwt/6.35.0 --> Microsoft.IdentityModel.JsonWebTokens/6.35.0 46 | System.IdentityModel.Tokens.Jwt/6.35.0 --> Microsoft.IdentityModel.Tokens/6.35.0 47 | System.IO.FileSystem.AccessControl/5.0.0 --> System.Security.AccessControl/5.0.0 48 | System.IO.FileSystem.AccessControl/5.0.0 --> System.Security.Principal.Windows/5.0.0 49 | System.Memory.Data/8.0.0{{System.Memory.Data/8.0.0}} --> System.Text.Json/8.0.0 50 | System.Runtime.Caching/8.0.0 --> System.Configuration.ConfigurationManager/8.0.0 51 | System.Security.AccessControl/5.0.0 --> System.Security.Principal.Windows/5.0.0 52 | System.Text.Json/8.0.0 --> System.Text.Encodings.Web/8.0.0 53 | 54 | class Azure.Core/1.35.0 removed 55 | class Azure.Identity/1.10.3 removed 56 | class Microsoft.Bcl.AsyncInterfaces/1.1.1 removed 57 | class Microsoft.Data.SqlClient/5.2.0 root 58 | class Microsoft.Data.SqlClient/5.2.0 default 59 | class Microsoft.Data.SqlClient.SNI.runtime/5.2.0 default 60 | class Microsoft.Identity.Client/4.56.0 root 61 | class Microsoft.Identity.Client/4.56.0 project 62 | class Microsoft.Identity.Client.Extensions.Msal/4.56.0 removed 63 | class Microsoft.IdentityModel.Abstractions/6.35.0 removed 64 | class Microsoft.IdentityModel.JsonWebTokens/6.35.0 removed 65 | class Microsoft.IdentityModel.Logging/6.35.0 removed 66 | class Microsoft.IdentityModel.Protocols/6.35.0 removed 67 | class Microsoft.IdentityModel.Protocols.OpenIdConnect/6.35.0 removed 68 | class Microsoft.IdentityModel.Tokens/6.35.0 removed 69 | class Microsoft.SqlServer.Server/1.0.0 default 70 | class System.Configuration.ConfigurationManager/8.0.0 default 71 | class System.Diagnostics.DiagnosticSource/6.0.1 removed 72 | class System.Diagnostics.EventLog/8.0.0 default 73 | class System.IdentityModel.Tokens.Jwt/6.35.0 removed 74 | class System.IO.FileSystem.AccessControl/5.0.0 removed 75 | class System.Memory.Data/8.0.0 root 76 | class System.Memory.Data/8.0.0 default 77 | class System.Runtime.Caching/8.0.0 default 78 | class System.Runtime.CompilerServices.Unsafe/6.0.0 removed 79 | class System.Security.AccessControl/5.0.0 removed 80 | class System.Security.Cryptography.Cng/4.5.0 removed 81 | class System.Security.Cryptography.ProtectedData/8.0.0 default 82 | class System.Security.Principal.Windows/5.0.0 removed 83 | class System.Text.Encodings.Web/8.0.0 default 84 | class System.Text.Json/8.0.0 default 85 | -------------------------------------------------------------------------------- /tests/Chisel.Tests/DependencyGraphTest.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Linq; 3 | using System.Runtime.CompilerServices; 4 | using System.Threading.Tasks; 5 | using AwesomeAssertions; 6 | using NuGet.ProjectModel; 7 | using NuGet.Versioning; 8 | using Xunit; 9 | using static VerifyXunit.Verifier; 10 | 11 | namespace Chisel.Tests; 12 | 13 | public class DependencyGraphTest 14 | { 15 | private static readonly string[] MongoDbCopyLocalPackages = 16 | [ 17 | "AWSSDK.Core", 18 | "AWSSDK.SecurityToken", 19 | "ByteSize", 20 | "BouncyCastle.Cryptography", 21 | "DnsClient", 22 | "Docker.DotNet", 23 | "Docker.DotNet.X509", 24 | "Microsoft.Bcl.AsyncInterfaces", 25 | "Microsoft.Extensions.DependencyInjection.Abstractions", 26 | "Microsoft.Extensions.Logging.Abstractions", 27 | "Microsoft.Win32.Registry", 28 | "MongoDB.Bson", 29 | "MongoDB.Driver.Core", 30 | "MongoDB.Driver", 31 | "MongoDB.Libmongocrypt", 32 | "Newtonsoft.Json", 33 | "SharpCompress", 34 | "SharpZipLib", 35 | "Snappier", 36 | "SSH.NET", 37 | "SshNet.Security.Cryptography", 38 | "System.Runtime.CompilerServices.Unsafe", 39 | "System.Security.AccessControl", 40 | "System.Security.Principal.Windows", 41 | "System.Text.Encodings.Web", 42 | "System.Text.Json", 43 | "Testcontainers", 44 | "Testcontainers.MongoDb", 45 | "ZstdSharp.Port", 46 | ]; 47 | 48 | private static readonly string[] SqlClientCopyLocalPackages = 49 | [ 50 | "Azure.Core", 51 | "Azure.Identity", 52 | "Microsoft.Bcl.AsyncInterfaces", 53 | "Microsoft.Data.SqlClient", 54 | "Microsoft.Data.SqlClient.SNI.runtime", 55 | "Microsoft.Identity.Client.Extensions.Msal", 56 | "Microsoft.IdentityModel.Abstractions", 57 | "Microsoft.IdentityModel.JsonWebTokens", 58 | "Microsoft.IdentityModel.Logging", 59 | "Microsoft.IdentityModel.Protocols.OpenIdConnect", 60 | "Microsoft.IdentityModel.Protocols", 61 | "Microsoft.IdentityModel.Tokens", 62 | "Microsoft.SqlServer.Server", 63 | "System.Configuration.ConfigurationManager", 64 | "System.Diagnostics.DiagnosticSource", 65 | "System.Diagnostics.EventLog", 66 | "System.IdentityModel.Tokens.Jwt", 67 | "System.IO.FileSystem.AccessControl", 68 | "System.Memory.Data", 69 | "System.Runtime.Caching", 70 | "System.Runtime.CompilerServices.Unsafe", 71 | "System.Security.AccessControl", 72 | "System.Security.Cryptography.Cng", 73 | "System.Security.Cryptography.ProtectedData", 74 | "System.Security.Principal.Windows", 75 | "System.Text.Encodings.Web", 76 | "System.Text.Json", 77 | ]; 78 | 79 | [Theory] 80 | [CombinatorialData] 81 | public async Task MongoDbGraph(bool writeIgnoredPackages, [CombinatorialValues("graphviz", "mermaid")] string format) 82 | { 83 | var lockFile = new LockFileFormat().Read(GetAssetsPath("MongoDbGraph.json")); 84 | var (packages, roots) = lockFile.ReadPackages(tfm: "net8.0", rid: null, package => package.IsProjectReference || MongoDbCopyLocalPackages.Contains(package.Name)); 85 | var graph = new DependencyGraph(packages, roots, ignores: [ "Testcontainers.MongoDb" ]); 86 | var (removed, notFound, removedRoots) = graph.Remove([ "MongoDB.Driver", "AWSSDK.SecurityToken", "NonExistentPackage" ]); 87 | await using var writer = new StringWriter(); 88 | var graphWriter = format == "graphviz" ? GraphWriter.Graphviz(writer) : GraphWriter.Mermaid(writer); 89 | graphWriter.Write(graph, new GraphOptions { Direction = GraphDirection.LeftToRight, IncludeVersions = false, WriteIgnoredPackages = writeIgnoredPackages }); 90 | 91 | removed.Should().BeEquivalentTo("AWSSDK.SecurityToken", "AWSSDK.Core"); 92 | notFound.Should().BeEquivalentTo("NonExistentPackage"); 93 | removedRoots.Should().BeEquivalentTo("MongoDB.Driver"); 94 | 95 | await Verify(writer.ToString(), format == "graphviz" ? "gv" : "mmd").UseParameters(writeIgnoredPackages, format); 96 | } 97 | 98 | [Theory] 99 | [InlineData("graphviz")] 100 | [InlineData("mermaid")] 101 | public async Task SqlClientGraph(string format) 102 | { 103 | var lockFile = new LockFileFormat().Read(GetAssetsPath("SqlClientGraph.json")); 104 | var (packages, roots) = lockFile.ReadPackages(tfm: "net8.0-windows", rid: "win-x64", package => package.IsProjectReference || SqlClientCopyLocalPackages.Contains(package.Name)); 105 | var graph = new DependencyGraph(packages, roots, ignores: []); 106 | var (removed, notFound, removedRoots) = graph.Remove([ "Azure.Identity", "Microsoft.IdentityModel.JsonWebTokens", "Microsoft.IdentityModel.Protocols.OpenIdConnect", "System.Memory.Data" ]); 107 | await using var writer = new StringWriter(); 108 | 109 | var graphWriter = format == "graphviz" ? GraphWriter.Graphviz(writer) : GraphWriter.Mermaid(writer); 110 | graphWriter.Write(graph, new GraphOptions { Direction = GraphDirection.LeftToRight, IncludeVersions = true, WriteIgnoredPackages = false }); 111 | 112 | removed.Should().BeEquivalentTo([ 113 | "Azure.Core", 114 | "Azure.Identity", 115 | "Microsoft.Bcl.AsyncInterfaces", 116 | "Microsoft.Identity.Client.Extensions.Msal", 117 | "Microsoft.IdentityModel.Abstractions", 118 | "Microsoft.IdentityModel.JsonWebTokens", 119 | "Microsoft.IdentityModel.Logging", 120 | "Microsoft.IdentityModel.Protocols", 121 | "Microsoft.IdentityModel.Protocols.OpenIdConnect", 122 | "Microsoft.IdentityModel.Tokens", 123 | "System.Diagnostics.DiagnosticSource", 124 | "System.IO.FileSystem.AccessControl", 125 | "System.IdentityModel.Tokens.Jwt", 126 | "System.Runtime.CompilerServices.Unsafe", 127 | "System.Security.AccessControl", 128 | "System.Security.Cryptography.Cng", 129 | "System.Security.Principal.Windows", 130 | ]); 131 | notFound.Should().BeEmpty(); 132 | removedRoots.Should().BeEquivalentTo(["System.Memory.Data"]); 133 | 134 | await Verify(writer.ToString(), format == "graphviz" ? "gv" : "mmd").UseTextForParameters(format); 135 | } 136 | 137 | [Theory] 138 | [InlineData("graphviz")] 139 | [InlineData("mermaid")] 140 | public async Task PollyGraphIgnoreGlob(string format) 141 | { 142 | var lockFile = new LockFileFormat().Read(GetAssetsPath("PollyGraph.json")); 143 | var (packages, roots) = lockFile.ReadPackages(tfm: "netstandard2.0", rid: ""); 144 | var graph = new DependencyGraph(packages, roots, ignores: [ "System.*" ]); 145 | await using var writer = new StringWriter(); 146 | 147 | var graphWriter = format == "graphviz" ? GraphWriter.Graphviz(writer) : GraphWriter.Mermaid(writer); 148 | graphWriter.Write(graph, new GraphOptions { Direction = GraphDirection.LeftToRight, IncludeVersions = true, WriteIgnoredPackages = false }); 149 | 150 | await Verify(writer.ToString(), format == "graphviz" ? "gv" : "mmd").UseTextForParameters(format); 151 | } 152 | 153 | [Fact] 154 | public void ValidProjectVersion() 155 | { 156 | var lockFile = new LockFileFormat().Read(GetAssetsPath("SqlClientGraph.json")); 157 | var (packages, roots) = lockFile.ReadPackages(tfm: "net8.0-windows", rid: "win-x64", package => package.IsProjectReference || SqlClientCopyLocalPackages.Contains(package.Name)); 158 | var graph = new DependencyGraph(packages, roots, ignores: []); 159 | 160 | graph.EnumerateUnsatisfiedProjectDependencies().Should().BeEmpty(); 161 | } 162 | 163 | [Fact] 164 | public void InvalidProjectVersion() 165 | { 166 | var lockFile = new LockFileFormat().Read(GetAssetsPath("SqlClientGraph-InvalidProjectVersion.json")); 167 | var (packages, roots) = lockFile.ReadPackages(tfm: "net8.0-windows", rid: "win-x64", package => package.IsProjectReference || SqlClientCopyLocalPackages.Contains(package.Name)); 168 | var graph = new DependencyGraph(packages, roots, ignores: []); 169 | 170 | var result = graph.EnumerateUnsatisfiedProjectDependencies().ToList(); 171 | 172 | var versionRange = VersionRange.Parse("4.56.0"); 173 | result.Should().BeEquivalentTo([ 174 | (Package("Microsoft.Identity.Client"), Package("Azure.Identity"), new Dependency("Microsoft.Identity.Client", versionRange)), 175 | (Package("Microsoft.Identity.Client"), Package("Microsoft.Data.SqlClient"), new Dependency("Microsoft.Identity.Client", versionRange)), 176 | (Package("Microsoft.Identity.Client"), Package("Microsoft.Identity.Client.Extensions.Msal"), new Dependency("Microsoft.Identity.Client", versionRange)) 177 | ]); 178 | result.Select(e => e.Project).Distinct().Should().ContainSingle().Which.Version.Should().Be(new NuGetVersion(1, 22, 333)); 179 | 180 | // Package objects are compared by name only 181 | static Package Package(string name) => new(name, default!, default, default!); 182 | } 183 | 184 | private static string GetAssetsPath(string file, [CallerFilePath] string path = "") 185 | => Path.GetFullPath(Path.Combine(Path.GetDirectoryName(path)!, "ProjectAssets", file)); 186 | } -------------------------------------------------------------------------------- /tests/Chisel.Tests/ProjectAssets/PollyGraph.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "targets": { 4 | ".NETStandard,Version=v2.0": { 5 | "Microsoft.Bcl.AsyncInterfaces/6.0.0": { 6 | "type": "package", 7 | "dependencies": { 8 | "System.Threading.Tasks.Extensions": "4.5.4" 9 | }, 10 | "compile": { 11 | "lib/netstandard2.0/Microsoft.Bcl.AsyncInterfaces.dll": { 12 | "related": ".xml" 13 | } 14 | }, 15 | "runtime": { 16 | "lib/netstandard2.0/Microsoft.Bcl.AsyncInterfaces.dll": { 17 | "related": ".xml" 18 | } 19 | } 20 | }, 21 | "Microsoft.Bcl.TimeProvider/8.0.0": { 22 | "type": "package", 23 | "dependencies": { 24 | "Microsoft.Bcl.AsyncInterfaces": "6.0.0" 25 | }, 26 | "compile": { 27 | "lib/netstandard2.0/Microsoft.Bcl.TimeProvider.dll": { 28 | "related": ".xml" 29 | } 30 | }, 31 | "runtime": { 32 | "lib/netstandard2.0/Microsoft.Bcl.TimeProvider.dll": { 33 | "related": ".xml" 34 | } 35 | } 36 | }, 37 | "Microsoft.NETCore.Platforms/1.1.0": { 38 | "type": "package", 39 | "compile": { 40 | "lib/netstandard1.0/_._": {} 41 | }, 42 | "runtime": { 43 | "lib/netstandard1.0/_._": {} 44 | } 45 | }, 46 | "NETStandard.Library/2.0.3": { 47 | "type": "package", 48 | "dependencies": { 49 | "Microsoft.NETCore.Platforms": "1.1.0" 50 | }, 51 | "compile": { 52 | "lib/netstandard1.0/_._": {} 53 | }, 54 | "runtime": { 55 | "lib/netstandard1.0/_._": {} 56 | }, 57 | "build": { 58 | "build/netstandard2.0/NETStandard.Library.targets": {} 59 | } 60 | }, 61 | "Polly/8.4.0": { 62 | "type": "package", 63 | "dependencies": { 64 | "Polly.Core": "8.4.0" 65 | }, 66 | "compile": { 67 | "lib/netstandard2.0/Polly.dll": { 68 | "related": ".pdb;.xml" 69 | } 70 | }, 71 | "runtime": { 72 | "lib/netstandard2.0/Polly.dll": { 73 | "related": ".pdb;.xml" 74 | } 75 | } 76 | }, 77 | "Polly.Core/8.4.0": { 78 | "type": "package", 79 | "dependencies": { 80 | "Microsoft.Bcl.AsyncInterfaces": "6.0.0", 81 | "Microsoft.Bcl.TimeProvider": "8.0.0", 82 | "System.ComponentModel.Annotations": "4.5.0", 83 | "System.Threading.Tasks.Extensions": "4.5.4" 84 | }, 85 | "compile": { 86 | "lib/netstandard2.0/Polly.Core.dll": { 87 | "related": ".pdb;.xml" 88 | } 89 | }, 90 | "runtime": { 91 | "lib/netstandard2.0/Polly.Core.dll": { 92 | "related": ".pdb;.xml" 93 | } 94 | } 95 | }, 96 | "System.ComponentModel.Annotations/4.5.0": { 97 | "type": "package", 98 | "compile": { 99 | "ref/netstandard2.0/System.ComponentModel.Annotations.dll": { 100 | "related": ".xml" 101 | } 102 | }, 103 | "runtime": { 104 | "lib/netstandard2.0/System.ComponentModel.Annotations.dll": {} 105 | } 106 | }, 107 | "System.Runtime.CompilerServices.Unsafe/4.5.3": { 108 | "type": "package", 109 | "compile": { 110 | "ref/netstandard2.0/System.Runtime.CompilerServices.Unsafe.dll": { 111 | "related": ".xml" 112 | } 113 | }, 114 | "runtime": { 115 | "lib/netstandard2.0/System.Runtime.CompilerServices.Unsafe.dll": { 116 | "related": ".xml" 117 | } 118 | } 119 | }, 120 | "System.Threading.Tasks.Extensions/4.5.4": { 121 | "type": "package", 122 | "dependencies": { 123 | "System.Runtime.CompilerServices.Unsafe": "4.5.3" 124 | }, 125 | "compile": { 126 | "lib/netstandard2.0/System.Threading.Tasks.Extensions.dll": { 127 | "related": ".xml" 128 | } 129 | }, 130 | "runtime": { 131 | "lib/netstandard2.0/System.Threading.Tasks.Extensions.dll": { 132 | "related": ".xml" 133 | } 134 | } 135 | } 136 | } 137 | }, 138 | "libraries": { 139 | "Microsoft.Bcl.AsyncInterfaces/6.0.0": { 140 | "sha512": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==", 141 | "type": "package", 142 | "path": "microsoft.bcl.asyncinterfaces/6.0.0", 143 | "files": [ 144 | ".nupkg.metadata", 145 | ".signature.p7s", 146 | "Icon.png", 147 | "LICENSE.TXT", 148 | "THIRD-PARTY-NOTICES.TXT", 149 | "lib/net461/Microsoft.Bcl.AsyncInterfaces.dll", 150 | "lib/net461/Microsoft.Bcl.AsyncInterfaces.xml", 151 | "lib/netstandard2.0/Microsoft.Bcl.AsyncInterfaces.dll", 152 | "lib/netstandard2.0/Microsoft.Bcl.AsyncInterfaces.xml", 153 | "lib/netstandard2.1/Microsoft.Bcl.AsyncInterfaces.dll", 154 | "lib/netstandard2.1/Microsoft.Bcl.AsyncInterfaces.xml", 155 | "microsoft.bcl.asyncinterfaces.6.0.0.nupkg.sha512", 156 | "microsoft.bcl.asyncinterfaces.nuspec", 157 | "useSharedDesignerContext.txt" 158 | ] 159 | }, 160 | "Microsoft.Bcl.TimeProvider/8.0.0": { 161 | "sha512": "f5Kr5JepAbiGo7uDmhgvMqhntwxqXNn6/IpTBSSI4cuHhgnJGrLxFRhMjVpRkLPp6zJXO0/G0l3j9p9zSJxa+w==", 162 | "type": "package", 163 | "path": "microsoft.bcl.timeprovider/8.0.0", 164 | "files": [ 165 | ".nupkg.metadata", 166 | ".signature.p7s", 167 | "Icon.png", 168 | "LICENSE.TXT", 169 | "PACKAGE.md", 170 | "THIRD-PARTY-NOTICES.TXT", 171 | "buildTransitive/net461/Microsoft.Bcl.TimeProvider.targets", 172 | "buildTransitive/net462/_._", 173 | "buildTransitive/net6.0/_._", 174 | "buildTransitive/netcoreapp2.0/Microsoft.Bcl.TimeProvider.targets", 175 | "lib/net462/Microsoft.Bcl.TimeProvider.dll", 176 | "lib/net462/Microsoft.Bcl.TimeProvider.xml", 177 | "lib/net8.0/Microsoft.Bcl.TimeProvider.dll", 178 | "lib/net8.0/Microsoft.Bcl.TimeProvider.xml", 179 | "lib/netstandard2.0/Microsoft.Bcl.TimeProvider.dll", 180 | "lib/netstandard2.0/Microsoft.Bcl.TimeProvider.xml", 181 | "microsoft.bcl.timeprovider.8.0.0.nupkg.sha512", 182 | "microsoft.bcl.timeprovider.nuspec", 183 | "useSharedDesignerContext.txt" 184 | ] 185 | }, 186 | "Microsoft.NETCore.Platforms/1.1.0": { 187 | "sha512": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==", 188 | "type": "package", 189 | "path": "microsoft.netcore.platforms/1.1.0", 190 | "files": [ 191 | ".nupkg.metadata", 192 | ".signature.p7s", 193 | "ThirdPartyNotices.txt", 194 | "dotnet_library_license.txt", 195 | "lib/netstandard1.0/_._", 196 | "microsoft.netcore.platforms.1.1.0.nupkg.sha512", 197 | "microsoft.netcore.platforms.nuspec", 198 | "runtime.json" 199 | ] 200 | }, 201 | "NETStandard.Library/2.0.3": { 202 | "sha512": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", 203 | "type": "package", 204 | "path": "netstandard.library/2.0.3", 205 | "files": [ 206 | ".nupkg.metadata", 207 | ".signature.p7s", 208 | "LICENSE.TXT", 209 | "THIRD-PARTY-NOTICES.TXT", 210 | "build/netstandard2.0/NETStandard.Library.targets", 211 | "build/netstandard2.0/ref/Microsoft.Win32.Primitives.dll", 212 | "build/netstandard2.0/ref/System.AppContext.dll", 213 | "build/netstandard2.0/ref/System.Collections.Concurrent.dll", 214 | "build/netstandard2.0/ref/System.Collections.NonGeneric.dll", 215 | "build/netstandard2.0/ref/System.Collections.Specialized.dll", 216 | "build/netstandard2.0/ref/System.Collections.dll", 217 | "build/netstandard2.0/ref/System.ComponentModel.Composition.dll", 218 | "build/netstandard2.0/ref/System.ComponentModel.EventBasedAsync.dll", 219 | "build/netstandard2.0/ref/System.ComponentModel.Primitives.dll", 220 | "build/netstandard2.0/ref/System.ComponentModel.TypeConverter.dll", 221 | "build/netstandard2.0/ref/System.ComponentModel.dll", 222 | "build/netstandard2.0/ref/System.Console.dll", 223 | "build/netstandard2.0/ref/System.Core.dll", 224 | "build/netstandard2.0/ref/System.Data.Common.dll", 225 | "build/netstandard2.0/ref/System.Data.dll", 226 | "build/netstandard2.0/ref/System.Diagnostics.Contracts.dll", 227 | "build/netstandard2.0/ref/System.Diagnostics.Debug.dll", 228 | "build/netstandard2.0/ref/System.Diagnostics.FileVersionInfo.dll", 229 | "build/netstandard2.0/ref/System.Diagnostics.Process.dll", 230 | "build/netstandard2.0/ref/System.Diagnostics.StackTrace.dll", 231 | "build/netstandard2.0/ref/System.Diagnostics.TextWriterTraceListener.dll", 232 | "build/netstandard2.0/ref/System.Diagnostics.Tools.dll", 233 | "build/netstandard2.0/ref/System.Diagnostics.TraceSource.dll", 234 | "build/netstandard2.0/ref/System.Diagnostics.Tracing.dll", 235 | "build/netstandard2.0/ref/System.Drawing.Primitives.dll", 236 | "build/netstandard2.0/ref/System.Drawing.dll", 237 | "build/netstandard2.0/ref/System.Dynamic.Runtime.dll", 238 | "build/netstandard2.0/ref/System.Globalization.Calendars.dll", 239 | "build/netstandard2.0/ref/System.Globalization.Extensions.dll", 240 | "build/netstandard2.0/ref/System.Globalization.dll", 241 | "build/netstandard2.0/ref/System.IO.Compression.FileSystem.dll", 242 | "build/netstandard2.0/ref/System.IO.Compression.ZipFile.dll", 243 | "build/netstandard2.0/ref/System.IO.Compression.dll", 244 | "build/netstandard2.0/ref/System.IO.FileSystem.DriveInfo.dll", 245 | "build/netstandard2.0/ref/System.IO.FileSystem.Primitives.dll", 246 | "build/netstandard2.0/ref/System.IO.FileSystem.Watcher.dll", 247 | "build/netstandard2.0/ref/System.IO.FileSystem.dll", 248 | "build/netstandard2.0/ref/System.IO.IsolatedStorage.dll", 249 | "build/netstandard2.0/ref/System.IO.MemoryMappedFiles.dll", 250 | "build/netstandard2.0/ref/System.IO.Pipes.dll", 251 | "build/netstandard2.0/ref/System.IO.UnmanagedMemoryStream.dll", 252 | "build/netstandard2.0/ref/System.IO.dll", 253 | "build/netstandard2.0/ref/System.Linq.Expressions.dll", 254 | "build/netstandard2.0/ref/System.Linq.Parallel.dll", 255 | "build/netstandard2.0/ref/System.Linq.Queryable.dll", 256 | "build/netstandard2.0/ref/System.Linq.dll", 257 | "build/netstandard2.0/ref/System.Net.Http.dll", 258 | "build/netstandard2.0/ref/System.Net.NameResolution.dll", 259 | "build/netstandard2.0/ref/System.Net.NetworkInformation.dll", 260 | "build/netstandard2.0/ref/System.Net.Ping.dll", 261 | "build/netstandard2.0/ref/System.Net.Primitives.dll", 262 | "build/netstandard2.0/ref/System.Net.Requests.dll", 263 | "build/netstandard2.0/ref/System.Net.Security.dll", 264 | "build/netstandard2.0/ref/System.Net.Sockets.dll", 265 | "build/netstandard2.0/ref/System.Net.WebHeaderCollection.dll", 266 | "build/netstandard2.0/ref/System.Net.WebSockets.Client.dll", 267 | "build/netstandard2.0/ref/System.Net.WebSockets.dll", 268 | "build/netstandard2.0/ref/System.Net.dll", 269 | "build/netstandard2.0/ref/System.Numerics.dll", 270 | "build/netstandard2.0/ref/System.ObjectModel.dll", 271 | "build/netstandard2.0/ref/System.Reflection.Extensions.dll", 272 | "build/netstandard2.0/ref/System.Reflection.Primitives.dll", 273 | "build/netstandard2.0/ref/System.Reflection.dll", 274 | "build/netstandard2.0/ref/System.Resources.Reader.dll", 275 | "build/netstandard2.0/ref/System.Resources.ResourceManager.dll", 276 | "build/netstandard2.0/ref/System.Resources.Writer.dll", 277 | "build/netstandard2.0/ref/System.Runtime.CompilerServices.VisualC.dll", 278 | "build/netstandard2.0/ref/System.Runtime.Extensions.dll", 279 | "build/netstandard2.0/ref/System.Runtime.Handles.dll", 280 | "build/netstandard2.0/ref/System.Runtime.InteropServices.RuntimeInformation.dll", 281 | "build/netstandard2.0/ref/System.Runtime.InteropServices.dll", 282 | "build/netstandard2.0/ref/System.Runtime.Numerics.dll", 283 | "build/netstandard2.0/ref/System.Runtime.Serialization.Formatters.dll", 284 | "build/netstandard2.0/ref/System.Runtime.Serialization.Json.dll", 285 | "build/netstandard2.0/ref/System.Runtime.Serialization.Primitives.dll", 286 | "build/netstandard2.0/ref/System.Runtime.Serialization.Xml.dll", 287 | "build/netstandard2.0/ref/System.Runtime.Serialization.dll", 288 | "build/netstandard2.0/ref/System.Runtime.dll", 289 | "build/netstandard2.0/ref/System.Security.Claims.dll", 290 | "build/netstandard2.0/ref/System.Security.Cryptography.Algorithms.dll", 291 | "build/netstandard2.0/ref/System.Security.Cryptography.Csp.dll", 292 | "build/netstandard2.0/ref/System.Security.Cryptography.Encoding.dll", 293 | "build/netstandard2.0/ref/System.Security.Cryptography.Primitives.dll", 294 | "build/netstandard2.0/ref/System.Security.Cryptography.X509Certificates.dll", 295 | "build/netstandard2.0/ref/System.Security.Principal.dll", 296 | "build/netstandard2.0/ref/System.Security.SecureString.dll", 297 | "build/netstandard2.0/ref/System.ServiceModel.Web.dll", 298 | "build/netstandard2.0/ref/System.Text.Encoding.Extensions.dll", 299 | "build/netstandard2.0/ref/System.Text.Encoding.dll", 300 | "build/netstandard2.0/ref/System.Text.RegularExpressions.dll", 301 | "build/netstandard2.0/ref/System.Threading.Overlapped.dll", 302 | "build/netstandard2.0/ref/System.Threading.Tasks.Parallel.dll", 303 | "build/netstandard2.0/ref/System.Threading.Tasks.dll", 304 | "build/netstandard2.0/ref/System.Threading.Thread.dll", 305 | "build/netstandard2.0/ref/System.Threading.ThreadPool.dll", 306 | "build/netstandard2.0/ref/System.Threading.Timer.dll", 307 | "build/netstandard2.0/ref/System.Threading.dll", 308 | "build/netstandard2.0/ref/System.Transactions.dll", 309 | "build/netstandard2.0/ref/System.ValueTuple.dll", 310 | "build/netstandard2.0/ref/System.Web.dll", 311 | "build/netstandard2.0/ref/System.Windows.dll", 312 | "build/netstandard2.0/ref/System.Xml.Linq.dll", 313 | "build/netstandard2.0/ref/System.Xml.ReaderWriter.dll", 314 | "build/netstandard2.0/ref/System.Xml.Serialization.dll", 315 | "build/netstandard2.0/ref/System.Xml.XDocument.dll", 316 | "build/netstandard2.0/ref/System.Xml.XPath.XDocument.dll", 317 | "build/netstandard2.0/ref/System.Xml.XPath.dll", 318 | "build/netstandard2.0/ref/System.Xml.XmlDocument.dll", 319 | "build/netstandard2.0/ref/System.Xml.XmlSerializer.dll", 320 | "build/netstandard2.0/ref/System.Xml.dll", 321 | "build/netstandard2.0/ref/System.dll", 322 | "build/netstandard2.0/ref/mscorlib.dll", 323 | "build/netstandard2.0/ref/netstandard.dll", 324 | "build/netstandard2.0/ref/netstandard.xml", 325 | "lib/netstandard1.0/_._", 326 | "netstandard.library.2.0.3.nupkg.sha512", 327 | "netstandard.library.nuspec" 328 | ] 329 | }, 330 | "Polly/8.4.0": { 331 | "sha512": "z2EeUutuy49jBQyZ5s2FUuTCGx3GCzJ0cJ2HbjWwks94TsC6bKTtAHKBkMZOa/DyYRl5yIX7MshvMTWl1J6RNg==", 332 | "type": "package", 333 | "path": "polly/8.4.0", 334 | "files": [ 335 | ".nupkg.metadata", 336 | ".signature.p7s", 337 | "lib/net462/Polly.dll", 338 | "lib/net462/Polly.pdb", 339 | "lib/net462/Polly.xml", 340 | "lib/net472/Polly.dll", 341 | "lib/net472/Polly.pdb", 342 | "lib/net472/Polly.xml", 343 | "lib/net6.0/Polly.dll", 344 | "lib/net6.0/Polly.pdb", 345 | "lib/net6.0/Polly.xml", 346 | "lib/netstandard2.0/Polly.dll", 347 | "lib/netstandard2.0/Polly.pdb", 348 | "lib/netstandard2.0/Polly.xml", 349 | "package-icon.png", 350 | "package-readme.md", 351 | "polly.8.4.0.nupkg.sha512", 352 | "polly.nuspec" 353 | ] 354 | }, 355 | "Polly.Core/8.4.0": { 356 | "sha512": "3AZxuP//pxOeBo9tQs7/tz4Z5TTbu4BYfjpaXZD0JYKJo98ngN9TMUz1nybh4k0ykWkMBpGZALV/AmVIE3ew7A==", 357 | "type": "package", 358 | "path": "polly.core/8.4.0", 359 | "files": [ 360 | ".nupkg.metadata", 361 | ".signature.p7s", 362 | "lib/net462/Polly.Core.dll", 363 | "lib/net462/Polly.Core.pdb", 364 | "lib/net462/Polly.Core.xml", 365 | "lib/net472/Polly.Core.dll", 366 | "lib/net472/Polly.Core.pdb", 367 | "lib/net472/Polly.Core.xml", 368 | "lib/net6.0/Polly.Core.dll", 369 | "lib/net6.0/Polly.Core.pdb", 370 | "lib/net6.0/Polly.Core.xml", 371 | "lib/net8.0/Polly.Core.dll", 372 | "lib/net8.0/Polly.Core.pdb", 373 | "lib/net8.0/Polly.Core.xml", 374 | "lib/netstandard2.0/Polly.Core.dll", 375 | "lib/netstandard2.0/Polly.Core.pdb", 376 | "lib/netstandard2.0/Polly.Core.xml", 377 | "package-icon.png", 378 | "package-readme.md", 379 | "polly.core.8.4.0.nupkg.sha512", 380 | "polly.core.nuspec" 381 | ] 382 | }, 383 | "System.ComponentModel.Annotations/4.5.0": { 384 | "sha512": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==", 385 | "type": "package", 386 | "path": "system.componentmodel.annotations/4.5.0", 387 | "files": [ 388 | ".nupkg.metadata", 389 | ".signature.p7s", 390 | "LICENSE.TXT", 391 | "THIRD-PARTY-NOTICES.TXT", 392 | "lib/MonoAndroid10/_._", 393 | "lib/MonoTouch10/_._", 394 | "lib/net45/_._", 395 | "lib/net461/System.ComponentModel.Annotations.dll", 396 | "lib/netcore50/System.ComponentModel.Annotations.dll", 397 | "lib/netcoreapp2.0/_._", 398 | "lib/netstandard1.4/System.ComponentModel.Annotations.dll", 399 | "lib/netstandard2.0/System.ComponentModel.Annotations.dll", 400 | "lib/portable-net45+win8/_._", 401 | "lib/uap10.0.16299/_._", 402 | "lib/win8/_._", 403 | "lib/xamarinios10/_._", 404 | "lib/xamarinmac20/_._", 405 | "lib/xamarintvos10/_._", 406 | "lib/xamarinwatchos10/_._", 407 | "ref/MonoAndroid10/_._", 408 | "ref/MonoTouch10/_._", 409 | "ref/net45/_._", 410 | "ref/net461/System.ComponentModel.Annotations.dll", 411 | "ref/net461/System.ComponentModel.Annotations.xml", 412 | "ref/netcore50/System.ComponentModel.Annotations.dll", 413 | "ref/netcore50/System.ComponentModel.Annotations.xml", 414 | "ref/netcore50/de/System.ComponentModel.Annotations.xml", 415 | "ref/netcore50/es/System.ComponentModel.Annotations.xml", 416 | "ref/netcore50/fr/System.ComponentModel.Annotations.xml", 417 | "ref/netcore50/it/System.ComponentModel.Annotations.xml", 418 | "ref/netcore50/ja/System.ComponentModel.Annotations.xml", 419 | "ref/netcore50/ko/System.ComponentModel.Annotations.xml", 420 | "ref/netcore50/ru/System.ComponentModel.Annotations.xml", 421 | "ref/netcore50/zh-hans/System.ComponentModel.Annotations.xml", 422 | "ref/netcore50/zh-hant/System.ComponentModel.Annotations.xml", 423 | "ref/netcoreapp2.0/_._", 424 | "ref/netstandard1.1/System.ComponentModel.Annotations.dll", 425 | "ref/netstandard1.1/System.ComponentModel.Annotations.xml", 426 | "ref/netstandard1.1/de/System.ComponentModel.Annotations.xml", 427 | "ref/netstandard1.1/es/System.ComponentModel.Annotations.xml", 428 | "ref/netstandard1.1/fr/System.ComponentModel.Annotations.xml", 429 | "ref/netstandard1.1/it/System.ComponentModel.Annotations.xml", 430 | "ref/netstandard1.1/ja/System.ComponentModel.Annotations.xml", 431 | "ref/netstandard1.1/ko/System.ComponentModel.Annotations.xml", 432 | "ref/netstandard1.1/ru/System.ComponentModel.Annotations.xml", 433 | "ref/netstandard1.1/zh-hans/System.ComponentModel.Annotations.xml", 434 | "ref/netstandard1.1/zh-hant/System.ComponentModel.Annotations.xml", 435 | "ref/netstandard1.3/System.ComponentModel.Annotations.dll", 436 | "ref/netstandard1.3/System.ComponentModel.Annotations.xml", 437 | "ref/netstandard1.3/de/System.ComponentModel.Annotations.xml", 438 | "ref/netstandard1.3/es/System.ComponentModel.Annotations.xml", 439 | "ref/netstandard1.3/fr/System.ComponentModel.Annotations.xml", 440 | "ref/netstandard1.3/it/System.ComponentModel.Annotations.xml", 441 | "ref/netstandard1.3/ja/System.ComponentModel.Annotations.xml", 442 | "ref/netstandard1.3/ko/System.ComponentModel.Annotations.xml", 443 | "ref/netstandard1.3/ru/System.ComponentModel.Annotations.xml", 444 | "ref/netstandard1.3/zh-hans/System.ComponentModel.Annotations.xml", 445 | "ref/netstandard1.3/zh-hant/System.ComponentModel.Annotations.xml", 446 | "ref/netstandard1.4/System.ComponentModel.Annotations.dll", 447 | "ref/netstandard1.4/System.ComponentModel.Annotations.xml", 448 | "ref/netstandard1.4/de/System.ComponentModel.Annotations.xml", 449 | "ref/netstandard1.4/es/System.ComponentModel.Annotations.xml", 450 | "ref/netstandard1.4/fr/System.ComponentModel.Annotations.xml", 451 | "ref/netstandard1.4/it/System.ComponentModel.Annotations.xml", 452 | "ref/netstandard1.4/ja/System.ComponentModel.Annotations.xml", 453 | "ref/netstandard1.4/ko/System.ComponentModel.Annotations.xml", 454 | "ref/netstandard1.4/ru/System.ComponentModel.Annotations.xml", 455 | "ref/netstandard1.4/zh-hans/System.ComponentModel.Annotations.xml", 456 | "ref/netstandard1.4/zh-hant/System.ComponentModel.Annotations.xml", 457 | "ref/netstandard2.0/System.ComponentModel.Annotations.dll", 458 | "ref/netstandard2.0/System.ComponentModel.Annotations.xml", 459 | "ref/portable-net45+win8/_._", 460 | "ref/uap10.0.16299/_._", 461 | "ref/win8/_._", 462 | "ref/xamarinios10/_._", 463 | "ref/xamarinmac20/_._", 464 | "ref/xamarintvos10/_._", 465 | "ref/xamarinwatchos10/_._", 466 | "system.componentmodel.annotations.4.5.0.nupkg.sha512", 467 | "system.componentmodel.annotations.nuspec", 468 | "useSharedDesignerContext.txt", 469 | "version.txt" 470 | ] 471 | }, 472 | "System.Runtime.CompilerServices.Unsafe/4.5.3": { 473 | "sha512": "3TIsJhD1EiiT0w2CcDMN/iSSwnNnsrnbzeVHSKkaEgV85txMprmuO+Yq2AdSbeVGcg28pdNDTPK87tJhX7VFHw==", 474 | "type": "package", 475 | "path": "system.runtime.compilerservices.unsafe/4.5.3", 476 | "files": [ 477 | ".nupkg.metadata", 478 | ".signature.p7s", 479 | "LICENSE.TXT", 480 | "THIRD-PARTY-NOTICES.TXT", 481 | "lib/net461/System.Runtime.CompilerServices.Unsafe.dll", 482 | "lib/net461/System.Runtime.CompilerServices.Unsafe.xml", 483 | "lib/netcoreapp2.0/System.Runtime.CompilerServices.Unsafe.dll", 484 | "lib/netcoreapp2.0/System.Runtime.CompilerServices.Unsafe.xml", 485 | "lib/netstandard1.0/System.Runtime.CompilerServices.Unsafe.dll", 486 | "lib/netstandard1.0/System.Runtime.CompilerServices.Unsafe.xml", 487 | "lib/netstandard2.0/System.Runtime.CompilerServices.Unsafe.dll", 488 | "lib/netstandard2.0/System.Runtime.CompilerServices.Unsafe.xml", 489 | "ref/net461/System.Runtime.CompilerServices.Unsafe.dll", 490 | "ref/net461/System.Runtime.CompilerServices.Unsafe.xml", 491 | "ref/netstandard1.0/System.Runtime.CompilerServices.Unsafe.dll", 492 | "ref/netstandard1.0/System.Runtime.CompilerServices.Unsafe.xml", 493 | "ref/netstandard2.0/System.Runtime.CompilerServices.Unsafe.dll", 494 | "ref/netstandard2.0/System.Runtime.CompilerServices.Unsafe.xml", 495 | "system.runtime.compilerservices.unsafe.4.5.3.nupkg.sha512", 496 | "system.runtime.compilerservices.unsafe.nuspec", 497 | "useSharedDesignerContext.txt", 498 | "version.txt" 499 | ] 500 | }, 501 | "System.Threading.Tasks.Extensions/4.5.4": { 502 | "sha512": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", 503 | "type": "package", 504 | "path": "system.threading.tasks.extensions/4.5.4", 505 | "files": [ 506 | ".nupkg.metadata", 507 | ".signature.p7s", 508 | "LICENSE.TXT", 509 | "THIRD-PARTY-NOTICES.TXT", 510 | "lib/MonoAndroid10/_._", 511 | "lib/MonoTouch10/_._", 512 | "lib/net461/System.Threading.Tasks.Extensions.dll", 513 | "lib/net461/System.Threading.Tasks.Extensions.xml", 514 | "lib/netcoreapp2.1/_._", 515 | "lib/netstandard1.0/System.Threading.Tasks.Extensions.dll", 516 | "lib/netstandard1.0/System.Threading.Tasks.Extensions.xml", 517 | "lib/netstandard2.0/System.Threading.Tasks.Extensions.dll", 518 | "lib/netstandard2.0/System.Threading.Tasks.Extensions.xml", 519 | "lib/portable-net45+win8+wp8+wpa81/System.Threading.Tasks.Extensions.dll", 520 | "lib/portable-net45+win8+wp8+wpa81/System.Threading.Tasks.Extensions.xml", 521 | "lib/xamarinios10/_._", 522 | "lib/xamarinmac20/_._", 523 | "lib/xamarintvos10/_._", 524 | "lib/xamarinwatchos10/_._", 525 | "ref/MonoAndroid10/_._", 526 | "ref/MonoTouch10/_._", 527 | "ref/netcoreapp2.1/_._", 528 | "ref/xamarinios10/_._", 529 | "ref/xamarinmac20/_._", 530 | "ref/xamarintvos10/_._", 531 | "ref/xamarinwatchos10/_._", 532 | "system.threading.tasks.extensions.4.5.4.nupkg.sha512", 533 | "system.threading.tasks.extensions.nuspec", 534 | "useSharedDesignerContext.txt", 535 | "version.txt" 536 | ] 537 | } 538 | }, 539 | "projectFileDependencyGroups": { 540 | ".NETStandard,Version=v2.0": [ 541 | "NETStandard.Library >= 2.0.3", 542 | "Polly >= 8.4.0" 543 | ] 544 | }, 545 | "project": { 546 | "version": "1.0.0", 547 | "frameworks": { 548 | "netstandard2.0": { 549 | "targetAlias": "netstandard2.0", 550 | "dependencies": { 551 | "NETStandard.Library": { 552 | "suppressParent": "All", 553 | "target": "Package", 554 | "version": "[2.0.3, )", 555 | "autoReferenced": true 556 | }, 557 | "Polly": { 558 | "target": "Package", 559 | "version": "[8.4.0, )" 560 | } 561 | }, 562 | "imports": [ 563 | "net461", 564 | "net462", 565 | "net47", 566 | "net471", 567 | "net472", 568 | "net48", 569 | "net481" 570 | ], 571 | "assetTargetFallback": true, 572 | "warn": true 573 | } 574 | } 575 | } 576 | } -------------------------------------------------------------------------------- /tests/Chisel.Tests/SdkAssemblyResolverTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using AwesomeAssertions; 3 | using Xunit; 4 | 5 | namespace Chisel.Tests; 6 | 7 | public class SdkAssemblyResolverTest(ITestOutputHelper testOutputHelper) 8 | { 9 | [Fact] 10 | public void LoadNuGetLibraryModel() 11 | { 12 | var sender = ("dotnet", (Action)testOutputHelper.WriteLine); 13 | var assembly = SdkAssemblyResolver.ResolveAssembly(sender, new ResolveEventArgs("NuGet.LibraryModel, Version=6.9.1.3, Culture=neutral, PublicKeyToken=31bf3856ad364e35")); 14 | assembly.Should().NotBeNull(); 15 | } 16 | 17 | [Fact] 18 | public void FailToLoadNuGetLibraryModel() 19 | { 20 | var sender = ("not-an-existing-command", (Action)testOutputHelper.WriteLine); 21 | var assembly = SdkAssemblyResolver.ResolveAssembly(sender, new ResolveEventArgs("NuGet.LibraryModel, Version=6.9.1.3, Culture=neutral, PublicKeyToken=31bf3856ad364e35")); 22 | assembly.Should().BeNull(); 23 | } 24 | } -------------------------------------------------------------------------------- /tests/Chisel.Tests/Support/DirectoryInfoExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Linq; 3 | 4 | namespace Chisel.Tests; 5 | 6 | internal static class DirectoryInfoExtensions 7 | { 8 | public static DirectoryInfo SubDirectory(this DirectoryInfo directory, params string[] paths) 9 | => new(Path.GetFullPath(Path.Combine(paths.Prepend(directory.FullName).ToArray()))); 10 | 11 | public static FileInfo File(this DirectoryInfo directory, params string[] paths) 12 | => new(Path.GetFullPath(Path.Combine(paths.Prepend(directory.FullName).ToArray()))); 13 | } 14 | -------------------------------------------------------------------------------- /tests/Chisel.Tests/Support/PublishMode.cs: -------------------------------------------------------------------------------- 1 | namespace Chisel.Tests; 2 | 3 | /// 4 | /// The possible application publish modes for the TestApp. 5 | /// See also the .NET application publishing overview documentation. 6 | /// 7 | public enum PublishMode 8 | { 9 | /// 10 | /// Standard app publish, all dlls and related files are copied along the main executable. 11 | /// 12 | Standard, 13 | 14 | /// 15 | /// Publish a single file as a framework-dependent binary. 16 | /// 17 | SingleFile, 18 | } 19 | -------------------------------------------------------------------------------- /tests/Chisel.Tests/Support/TestApp.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Runtime.CompilerServices; 7 | using System.Runtime.Versioning; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using AwesomeAssertions; 11 | using CliWrap; 12 | using CliWrap.Exceptions; 13 | using NuGet.Frameworks; 14 | using Xunit; 15 | 16 | namespace Chisel.Tests; 17 | 18 | public sealed class TestApp : IAsyncLifetime 19 | { 20 | private readonly DirectoryInfo _workingDirectory; 21 | private readonly Dictionary _executables; 22 | private string _packageVersion = "N/A"; 23 | 24 | public TestApp() 25 | { 26 | var tfm = NuGetFramework.Parse(typeof(TestApp).Assembly.GetCustomAttribute()?.FrameworkName ?? throw new InvalidOperationException("TargetFrameworkAttribute not found")); 27 | _workingDirectory = GetDirectory("tests", $"TestApp-{tfm}"); 28 | _workingDirectory.Create(); 29 | foreach (var file in GetDirectory("tests", "TestApp").EnumerateFiles()) 30 | { 31 | file.CopyTo(_workingDirectory.File(file.Name).FullName, overwrite: true); 32 | } 33 | _executables = new Dictionary(); 34 | } 35 | 36 | async ValueTask IAsyncLifetime.InitializeAsync() 37 | { 38 | await CreateTestAppAsync(); 39 | } 40 | 41 | ValueTask IAsyncDisposable.DisposeAsync() 42 | { 43 | if (Environment.GetEnvironmentVariable("ContinuousIntegrationBuild") == "true") 44 | { 45 | // The NuGet package was already built as part of the tests (PackAsync), 46 | // so move it to the root of the repository for the "Upload NuGet package artifact" step to pick it. 47 | var packageName = $"Chisel.{_packageVersion}.nupkg"; 48 | var packageFile = _workingDirectory.File(packageName); 49 | packageFile.MoveTo(GetFullPath(packageName), overwrite: false); 50 | } 51 | 52 | try 53 | { 54 | _workingDirectory.Delete(recursive: true); 55 | } 56 | catch (UnauthorizedAccessException exception) 57 | { 58 | // This sometimes happen on the Windows runner in GitHub actions 59 | // > [Test Class Cleanup Failure (Chisel.Tests.ChiseledAppTests)]: System.UnauthorizedAccessException : Access to the path 'TestApp.dll' is denied. 60 | TestContext.Current.SendDiagnosticMessage($"Deleting {_workingDirectory} failed: {exception}"); 61 | } 62 | return ValueTask.CompletedTask; 63 | } 64 | 65 | public string GetExecutablePath(PublishMode publishMode) => _executables[publishMode].FullName; 66 | 67 | public DirectoryInfo IntermediateOutputPath { get; private set; } = new("."); 68 | 69 | private async Task CreateTestAppAsync() 70 | { 71 | // It might be tempting to do pack -> restore -> build --no-restore -> publish --no-build (and parallelize over publish modes) 72 | // But this would fail because of https://github.com/dotnet/sdk/issues/17526 and probably because of other unforeseen bugs 73 | // preventing from running multiple `dotnet publish` commands with different parameters. 74 | 75 | await PackAsync(); 76 | await RestoreAsync(); 77 | foreach (var publishMode in Enum.GetValues()) 78 | { 79 | await PublishAsync(publishMode); 80 | } 81 | } 82 | 83 | private async Task PackAsync() 84 | { 85 | var projectFile = GetFile("src", "Chisel", "Chisel.csproj"); 86 | var packArgs = new[] { 87 | "pack", projectFile.FullName, 88 | "--no-build", 89 | "--output", _workingDirectory.FullName, 90 | "--getProperty:PackageVersion", 91 | }; 92 | var packageVersion = await RunDotnetAsync(_workingDirectory, packArgs); 93 | _packageVersion = packageVersion.TrimEnd(); 94 | } 95 | 96 | private async Task RestoreAsync() 97 | { 98 | // Can't use "--source . --source https://api.nuget.org/v3/index.json" because of https://github.com/dotnet/sdk/issues/27202 => a nuget.config file is used instead. 99 | // It also has the benefit of using settings _only_ from the specified config file, ignoring the global nuget.config where package source mapping could interfere with the local source. 100 | var restoreArgs = new[] { 101 | "restore", 102 | "--configfile", "nuget.config", 103 | $"-p:ChiselPackageVersion={_packageVersion}", 104 | }; 105 | await RunDotnetAsync(_workingDirectory, restoreArgs); 106 | } 107 | 108 | private async Task PublishAsync(PublishMode publishMode) 109 | { 110 | var publishDirectory = _workingDirectory.SubDirectory("publish"); 111 | 112 | var outputDirectory = publishDirectory.SubDirectory(publishMode.ToString()); 113 | 114 | var publishArgs = new[] { 115 | "publish", 116 | "--no-restore", 117 | "--output", outputDirectory.FullName, 118 | $"-p:PublishSingleFile={publishMode is PublishMode.SingleFile}", 119 | "--getProperty:IntermediateOutputPath", 120 | }; 121 | var intermediateOutputPath = await RunDotnetAsync(_workingDirectory, publishArgs); 122 | IntermediateOutputPath = _workingDirectory.SubDirectory(intermediateOutputPath.TrimEnd()); 123 | 124 | var executableFileName = OperatingSystem.IsWindows() ? "TestApp.exe" : "TestApp"; 125 | var executableFile = outputDirectory.File(executableFileName); 126 | executableFile.Exists.Should().BeTrue(); 127 | var dlls = executableFile.Directory!.EnumerateFiles("*.dll"); 128 | if (publishMode == PublishMode.Standard) 129 | { 130 | dlls.Should().NotBeEmpty(because: $"the test app was _not_ published as single-file ({publishMode})"); 131 | } 132 | else 133 | { 134 | dlls.Should().BeEmpty(because: $"the test app was published as single-file ({publishMode})"); 135 | executableFile.Directory.EnumerateFiles().Should().ContainSingle().Which.FullName.Should().Be(executableFile.FullName); 136 | } 137 | 138 | _executables[publishMode] = executableFile; 139 | } 140 | 141 | private async Task RunDotnetAsync(DirectoryInfo workingDirectory, params string[] arguments) 142 | { 143 | var outBuilder = new StringBuilder(); 144 | var errBuilder = new StringBuilder(); 145 | var command = Cli.Wrap("dotnet") 146 | .WithValidation(CommandResultValidation.None) 147 | .WithWorkingDirectory(workingDirectory.FullName) 148 | .WithEnvironmentVariables(env => env.Set("DOTNET_NOLOGO", "1")) 149 | .WithArguments(arguments) 150 | .WithStandardOutputPipe(PipeTarget.ToDelegate(line => 151 | { 152 | outBuilder.AppendLine(line); 153 | TestContext.Current.SendDiagnosticMessage($"==> out: {line}"); 154 | })) 155 | .WithStandardErrorPipe(PipeTarget.ToDelegate(line => 156 | { 157 | errBuilder.AppendLine(line); 158 | TestContext.Current.SendDiagnosticMessage($"==> err: {line}"); 159 | })); 160 | 161 | TestContext.Current.SendDiagnosticMessage($"📁 {workingDirectory.FullName} 🛠️ {command}"); 162 | 163 | var result = await command.ExecuteAsync(); 164 | if (result.ExitCode != 0) 165 | { 166 | throw new CommandExecutionException(command, result.ExitCode, $"An unexpected exception has occurred while running {command}{Environment.NewLine}{errBuilder}{outBuilder}".Trim()); 167 | } 168 | 169 | return outBuilder.ToString(); 170 | } 171 | 172 | private static DirectoryInfo GetDirectory(params string[] paths) => new(GetFullPath(paths)); 173 | 174 | private static FileInfo GetFile(params string[] paths) => new(GetFullPath(paths)); 175 | 176 | private static string GetFullPath(params string[] paths) => Path.GetFullPath(Path.Combine(new[] { GetThisDirectory(), "..", "..", ".." }.Concat(paths).ToArray())); 177 | 178 | private static string GetThisDirectory([CallerFilePath] string path = "") => Path.GetDirectoryName(path)!; 179 | } 180 | -------------------------------------------------------------------------------- /tests/Chisel.Tests/xunit.runner.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", 3 | "diagnosticMessages": true 4 | } 5 | -------------------------------------------------------------------------------- /tests/TestApp/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.IO.MemoryMappedFiles; 6 | using System.Linq; 7 | using System.Runtime.InteropServices; 8 | using System.Text.RegularExpressions; 9 | using Microsoft.Data.SqlClient; 10 | using Microsoft.Extensions.DependencyModel; 11 | 12 | try 13 | { 14 | foreach (var dll in EnumerateDlls(Environment.ProcessPath).Distinct().Order()) 15 | { 16 | Console.WriteLine(dll); 17 | } 18 | 19 | var connectionString = args.Length > 0 && !args[^1].StartsWith("--") ? args[^1] : "Server=sqlprosample.database.windows.net;Database=sqlprosample;user=sqlproro;password=nh{Zd?*8ZU@Y}Bb#"; 20 | await using var dataSource = SqlClientFactory.Instance.CreateDataSource(connectionString); 21 | await using var command = dataSource.CreateCommand("Select @@version"); 22 | var result = await command.ExecuteScalarAsync(); 23 | 24 | Console.WriteLine($"[OK] {result}"); 25 | return 0; 26 | } 27 | catch (Exception exception) 28 | { 29 | Console.Error.WriteLine($"[ERROR] {exception}"); 30 | return 1; 31 | } 32 | 33 | static IEnumerable EnumerateDlls(string appPath) 34 | { 35 | using var depsJsonStream = GetJsonDepsStream(appPath); 36 | using var reader = new DependencyContextJsonReader(); 37 | var dependencyContext = reader.Read(depsJsonStream); 38 | 39 | foreach (var assembly in dependencyContext.GetRuntimeAssemblyNames(RuntimeInformation.RuntimeIdentifier)) 40 | { 41 | yield return $"{assembly.Name}.dll"; 42 | } 43 | 44 | foreach (var file in dependencyContext.GetRuntimeNativeRuntimeFileAssets(RuntimeInformation.RuntimeIdentifier)) 45 | { 46 | yield return Path.GetFileName(file.Path); 47 | } 48 | } 49 | 50 | // See https://github.com/0xced/SingleFileAppDependencyContext 51 | static Stream GetJsonDepsStream(string appPath) 52 | { 53 | var depsJsonRegex = new Regex(@"DepsJson Offset:\[([0-9a-fA-F]+)\] Size\[([0-9a-fA-F]+)\]", RegexOptions.Compiled | RegexOptions.IgnoreCase); 54 | var startInfo = new ProcessStartInfo(appPath) 55 | { 56 | EnvironmentVariables = 57 | { 58 | ["COREHOST_TRACE"] = "1", 59 | ["COREHOST_TRACE_VERBOSITY"] = "3", 60 | ["DOTNET_STARTUP_HOOKS"] = " ", 61 | }, 62 | UseShellExecute = false, 63 | CreateNoWindow = true, 64 | RedirectStandardError = true, 65 | }; 66 | 67 | using var process = new Process { StartInfo = startInfo }; 68 | long? depsJsonOffset = null; 69 | long? depsJsonSize = null; 70 | process.ErrorDataReceived += (sender, args) => 71 | { 72 | var match = depsJsonRegex.Match(args.Data ?? ""); 73 | if (match.Success) 74 | { 75 | var p = (Process)sender; 76 | depsJsonOffset = Convert.ToInt64(match.Groups[1].Value, 16); 77 | depsJsonSize = Convert.ToInt64(match.Groups[2].Value, 16); 78 | p.CancelErrorRead(); 79 | p.Kill(); 80 | } 81 | }; 82 | 83 | process.Start(); 84 | process.BeginErrorReadLine(); 85 | if (!process.WaitForExit(2000)) 86 | { 87 | process.Kill(); 88 | } 89 | 90 | if (depsJsonOffset.HasValue && depsJsonSize.HasValue) 91 | { 92 | using var appHostFile = MemoryMappedFile.CreateFromFile(appPath, FileMode.Open, null, 0, MemoryMappedFileAccess.Read); 93 | return appHostFile.CreateViewStream(depsJsonOffset.Value, depsJsonSize.Value, MemoryMappedFileAccess.Read); 94 | } 95 | 96 | return new FileStream(Path.ChangeExtension(appPath, ".deps.json"), FileMode.Open); 97 | } -------------------------------------------------------------------------------- /tests/TestApp/TestApp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | latest 7 | embedded 8 | true 9 | false 10 | true 11 | en 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | true 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /tests/TestApp/nuget.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | --------------------------------------------------------------------------------