diff --git a/.github/actions/get-dotnet-channel/action.yml b/.github/actions/get-dotnet-channel/action.yml
new file mode 100644
index 000000000..84bab509d
--- /dev/null
+++ b/.github/actions/get-dotnet-channel/action.yml
@@ -0,0 +1,43 @@
+name: Convert Target Framework to Channel
+description: |
+ Convert Target Framework to Channel. A channel is either LTS/STS or a version number.
+ Use `target_framework` for a single conversion with `channel` as output.
+ Use `target_framework_array` for a single conversion with `channels_multiline` as output.
+
+inputs:
+ target_framework:
+ description: 'The target framework to use'
+ required: false
+ target_framework_array:
+ description: 'The target framework array to use'
+ required: false
+
+outputs:
+ channel:
+ description: 'The converted Channel variable'
+ value: ${{steps.set_output.outputs.channel}}
+ channels_multiline:
+ description: 'The converted Channels multiline variable'
+ value: ${{steps.set_output.outputs.channels_multiline}}
+
+runs:
+ using: "composite"
+ steps:
+ - name: Set Channels
+ id: set_output
+ run: |
+ $target_framework = "${{inputs.target_framework}}";
+ if ($target_framework -ne '') {
+ $channel = $target_framework.Replace('coreapp', '').Replace('net', '');
+ "channel=$channel" | Out-File -FilePath $env:GITHUB_OUTPUT -Append;
+ }
+ $target_frameworks = "${{inputs.target_framework_array}}";
+ if ($target_frameworks -ne '') {
+ $EOF = -join (1..15 | ForEach {[char]((48..57)+(65..90)+(97..122) | Get-Random)});
+ "channels_multiline<<$EOF" | Out-File -FilePath $env:GITHUB_OUTPUT -Append;
+ foreach ($target_framework in ConvertFrom-Json "${{inputs.target_framework_array}}") {
+ $target_framework.Replace('coreapp', '').Replace('net', '') | Out-File -FilePath $env:GITHUB_OUTPUT -Append;
+ }
+ "$EOF" | Out-File -FilePath $env:GITHUB_OUTPUT -Append;
+ }
+ shell: pwsh
\ No newline at end of file
diff --git a/.github/actions/get-dotnet-path/action.yml b/.github/actions/get-dotnet-path/action.yml
new file mode 100644
index 000000000..1967f844f
--- /dev/null
+++ b/.github/actions/get-dotnet-path/action.yml
@@ -0,0 +1,51 @@
+name: Get .NET Path
+description: Get .NET path for the architecture
+
+inputs:
+ architecture:
+ description: 'The architecture to use'
+ required: true
+
+outputs:
+ path:
+ description: 'The .NET path for the architecture'
+ value: ${{ steps.set-path.outputs.path }}
+
+runs:
+ using: "composite"
+ steps:
+ - name: Get Program Files path for x86
+ if: ${{runner.os == 'Windows' && inputs.architecture == 'x86'}}
+ uses: ./.github/actions/get-program-files
+ id: get-program-files-x86
+ with:
+ architecture: x86
+
+ - name: Get Program Files path for x64
+ if: ${{runner.os == 'Windows' && (inputs.architecture == 'x86' || inputs.architecture == 'x64')}}
+ uses: ./.github/actions/get-program-files
+ id: get-program-files-x64
+ with:
+ architecture: x64
+
+ - name: Set .NET path for ${{inputs.architecture}}
+ id: set-path
+ run: |
+ if ('${{runner.os == 'Windows'}}' -eq 'true') {
+ if ('${{inputs.architecture == 'x86'}}' -eq 'true') {
+ $dotnet = "${{steps.get-program-files-x86.outputs.path}}/dotnet/dotnet.exe";
+ } else {
+ $dotnet = "${{steps.get-program-files-x64.outputs.path}}/dotnet/dotnet.exe";
+ }
+ } elseif ('${{inputs.image == 'macos-14'}}' -eq 'true') {
+ if ('${{inputs.architecture == 'x64'}}' -eq 'true') {
+ $dotnet = "/Users/runner/.dotnet/x64/dotnet";
+ } else {
+ $dotnet = "/Users/runner/.dotnet/dotnet";
+ }
+ } else {
+ # Ubuntu only has x64
+ $dotnet = 'dotnet';
+ }
+ "path=$dotnet" | Out-File -FilePath $env:GITHUB_OUTPUT -Append;
+ shell: pwsh
diff --git a/.github/actions/get-mono-path/action.yml b/.github/actions/get-mono-path/action.yml
new file mode 100644
index 000000000..30183462a
--- /dev/null
+++ b/.github/actions/get-mono-path/action.yml
@@ -0,0 +1,44 @@
+name: Get Mono Path
+description: Get Mono path for the architecture
+
+inputs:
+ architecture:
+ description: 'The architecture to use'
+ required: true
+
+outputs:
+ path:
+ description: 'The Mono path for the architecture'
+ value: ${{ steps.set-path.outputs.path }}
+
+runs:
+ using: "composite"
+ steps:
+ - name: Get Program Files path for x86
+ if: ${{runner.os == 'Windows' && inputs.architecture == 'x86'}}
+ uses: ./.github/actions/get-program-files
+ id: get-program-files-x86
+ with:
+ architecture: x86
+
+ - name: Get Program Files path for x64
+ if: ${{runner.os == 'Windows' && (inputs.architecture == 'x86' || inputs.architecture == 'x64')}}
+ uses: ./.github/actions/get-program-files
+ id: get-program-files-x64
+ with:
+ architecture: x64
+
+ - name: Set Mono path for ${{inputs.architecture}}
+ id: set-path
+ run: |
+ if ('${{runner.os == 'Windows'}}' -eq 'true') {
+ if ('${{inputs.architecture == 'x86'}}' -eq 'true') {
+ $mono = "${{steps.get-program-files-x86.outputs.path}}/Mono/bin/mono.exe";
+ } else {
+ $mono = "${{steps.get-program-files-x64.outputs.path}}/Mono/bin/mono.exe";
+ }
+ } else {
+ $mono = 'mono';
+ }
+ "path=$mono" | Out-File -FilePath $env:GITHUB_OUTPUT -Append;
+ shell: pwsh
diff --git a/.github/actions/get-program-files/action.yml b/.github/actions/get-program-files/action.yml
new file mode 100644
index 000000000..95afca37d
--- /dev/null
+++ b/.github/actions/get-program-files/action.yml
@@ -0,0 +1,28 @@
+name: Get ProgramFiles Path
+description: Get ProgramFiles path for the architecture
+
+inputs:
+ architecture:
+ description: 'The architecture to use'
+ required: true
+
+outputs:
+ path:
+ description: 'The ProgramFiles path for the architecture'
+ value: ${{ steps.set-path.outputs.path }}
+
+runs:
+ using: "composite"
+ steps:
+ - name: Set Program Files path for ${{inputs.architecture}}
+ id: set-path
+ run: |
+ if ('${{ inputs.architecture == 'x86'}}' -eq 'true') {
+ $path = [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::ProgramFilesX86);
+ "path=$path" | Out-File -FilePath $env:GITHUB_OUTPUT -Append;
+ }
+ if ('${{ inputs.architecture == 'x64'}}' -eq 'true') {
+ $path = [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::ProgramFiles);
+ "path=$path" | Out-File -FilePath $env:GITHUB_OUTPUT -Append;
+ }
+ shell: pwsh
diff --git a/.github/actions/setup-dotnet-macos-rosetta/action.yml b/.github/actions/setup-dotnet-macos-rosetta/action.yml
new file mode 100644
index 000000000..9eff5f041
--- /dev/null
+++ b/.github/actions/setup-dotnet-macos-rosetta/action.yml
@@ -0,0 +1,37 @@
+name: Setup .NET
+description: Setup .NET using the provided target framework and architecture
+
+inputs:
+ target_framework:
+ description: 'The .NET target framework to setup'
+ required: true
+ target_framework_array:
+ description: 'The .NET target frameworks to setup'
+ required: true
+
+runs:
+ using: "composite"
+ steps:
+ - name: Get .NET Channels
+ uses: ./.github/actions/get-dotnet-channel
+ id: get_channels
+ with:
+ target_framework: ${{inputs.target_framework}}
+ target_framework_array: ${{inputs.target_framework_array}}
+
+ - name: Setup .NET x64 ${{steps.get_channels.outputs.channels_multiline}}
+ uses: dlemstra/setup-dotnet@add-architecture-option
+ with:
+ dotnet-architecture: x64
+ dotnet-version: |
+ ${{steps.get_channels.outputs.channel}}
+ ${{steps.get_channels.outputs.channels_multiline}}
+ env:
+ DOTNET_INSTALL_DIR: /Users/runner/.dotnet/x64
+
+ - name: Setup .NET ${{steps.get_channels.outputs.channels_multiline}}
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: |
+ ${{steps.get_channels.outputs.channel}}
+ ${{steps.get_channels.outputs.channels_multiline}}
diff --git a/.github/actions/setup-dotnet-unix/action.yml b/.github/actions/setup-dotnet-unix/action.yml
new file mode 100644
index 000000000..5eb827076
--- /dev/null
+++ b/.github/actions/setup-dotnet-unix/action.yml
@@ -0,0 +1,30 @@
+name: Setup .NET
+description: Setup .NET using the provided target framework and architecture
+
+inputs:
+ target_framework:
+ description: 'The .NET target framework to setup'
+ required: true
+ target_framework_array:
+ description: 'The .NET target frameworks to setup'
+ required: true
+
+runs:
+ using: "composite"
+ steps:
+ - name: Setup .NET Sdk
+ uses: actions/setup-dotnet@v4
+
+ - name: Get .NET Channels
+ uses: ./.github/actions/get-dotnet-channel
+ id: get_channels
+ with:
+ target_framework: ${{inputs.target_framework}}
+ target_framework_array: ${{inputs.target_framework_array}}
+
+ - name: Setup .NET ${{steps.get_channels.outputs.channels_multiline}}
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: |
+ ${{steps.get_channels.outputs.channel}}
+ ${{steps.get_channels.outputs.channels_multiline}}
diff --git a/.github/actions/setup-dotnet-windows/action.yml b/.github/actions/setup-dotnet-windows/action.yml
new file mode 100644
index 000000000..e7fbc620a
--- /dev/null
+++ b/.github/actions/setup-dotnet-windows/action.yml
@@ -0,0 +1,48 @@
+name: Setup .NET Windows
+description: Setup .NET for Windows using the provided target framework and architecture
+
+inputs:
+ architecture:
+ description: 'The .NET architecture to setup'
+ required: true
+ target_framework:
+ description: 'The .NET target framework to setup'
+ required: true
+ target_framework_array:
+ description: 'The .NET target frameworks to setup'
+ required: true
+
+runs:
+ using: "composite"
+ steps:
+ - name: Get .NET Channel for ${{inputs.target_framework}}
+ uses: ./.github/actions/get-dotnet-channel
+ id: get_channels
+ with:
+ target_framework: ${{inputs.target_framework}}
+ target_framework_array: ${{inputs.target_framework_array}}
+
+ - name: Get Program Files path for x86
+ uses: ./.github/actions/get-program-files
+ if: ${{inputs.architecture == 'x86'}}
+ id: get-program-files-x86
+ with:
+ architecture: x86
+
+ - name: Setup .NET x86 ${{steps.get_channels.outputs.channels_multiline}}
+ uses: dlemstra/setup-dotnet@add-architecture-option
+ if: ${{inputs.architecture == 'x86'}}
+ with:
+ dotnet-architecture: x86
+ dotnet-version: |
+ ${{steps.get_channels.outputs.channel}}
+ ${{steps.get_channels.outputs.channels_multiline}}
+ env:
+ DOTNET_INSTALL_DIR: ${{steps.get-program-files-x86.outputs.path}}/dotnet
+
+ - name: Setup .NET ${{inputs.architecture}} ${{steps.get_channels.outputs.channels_multiline}}
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: |
+ ${{steps.get_channels.outputs.channel}}
+ ${{steps.get_channels.outputs.channels_multiline}}
diff --git a/.github/actions/setup-mono-windows/action.yml b/.github/actions/setup-mono-windows/action.yml
new file mode 100644
index 000000000..342224f82
--- /dev/null
+++ b/.github/actions/setup-mono-windows/action.yml
@@ -0,0 +1,28 @@
+name: Setup Mono Windows
+description: Setup Mono for Windows using the latest version
+
+inputs:
+ architecture:
+ description: 'The architecture to setup Mono for'
+ required: true
+ version:
+ description: 'The Mono version to install'
+ required: false
+
+outputs:
+ path:
+ description: 'The Mono path for the architecture'
+ value: '${{steps.get-program-files.outputs.path}}/Mono/bin/mono.exe'
+
+runs:
+ using: "composite"
+ steps:
+ - name: Setup Mono
+ run: choco install mono --yes --no-progress --${{inputs.architecture}} ${{(inputs.version != '' && '--version=') || ''}}${{inputs.version}} --ignore-checksums
+ shell: pwsh
+
+ - name: Get Program Files path for ${{inputs.architecture}}
+ uses: ./.github/actions/get-program-files
+ id: get-program-files
+ with:
+ architecture: ${{inputs.architecture}}
diff --git a/.github/actions/setup-wine-macos/action.yml b/.github/actions/setup-wine-macos/action.yml
new file mode 100644
index 000000000..ecea2e128
--- /dev/null
+++ b/.github/actions/setup-wine-macos/action.yml
@@ -0,0 +1,24 @@
+name: Install Wine on OS X
+description: Install Wine on OS X
+
+runs:
+ using: "composite"
+ steps:
+ # wine@staging v9.17
+ - run: |
+ curl -L ${{env.WINE_CASK}} > wine@staging.rb;
+ brew install --cask wine@staging.rb;
+ rm wine@staging.rb;
+ shell: bash
+ env:
+ WINE_CASK: https://raw.githubusercontent.com/Homebrew/homebrew-cask/2f29727c9961e8159df6dc19db315bc7783a4b99/Casks/w/wine%40staging.rb
+
+ # We can't use /usr/share/wine/mono because it's read-only
+ # We can't use /opt/wine/mono because it's read-only
+ - run: |
+ mkdir -p ~/.wine/share/wine/mono
+ curl -L -o mono.tar.xz ${{env.WINE_MONO}}
+ tar -xf mono.tar.xz -C ~/.wine/share/wine/mono
+ shell: bash
+ env:
+ WINE_MONO: https://github.com/madewokherd/wine-mono/releases/download/wine-mono-9.3.0/wine-mono-9.3.0-x86.tar.xz
\ No newline at end of file
diff --git a/.github/actions/setup-wine-ubuntu/action.yml b/.github/actions/setup-wine-ubuntu/action.yml
new file mode 100644
index 000000000..8226b03f7
--- /dev/null
+++ b/.github/actions/setup-wine-ubuntu/action.yml
@@ -0,0 +1,35 @@
+name: Install Wine on Ubuntu
+description: Install Wine on Ubuntu
+
+runs:
+ using: "composite"
+ steps:
+ - run: sudo dpkg --add-architecture i386
+ shell: bash
+
+ - run: |
+ sudo mkdir -pm755 /etc/apt/keyrings;
+ sudo wget -O /etc/apt/keyrings/winehq-archive.key https://dl.winehq.org/wine-builds/winehq.key;
+ sudo wget -NP /etc/apt/sources.list.d/ https://dl.winehq.org/wine-builds/ubuntu/dists/jammy/winehq-jammy.sources
+ shell: bash
+
+ - run: sudo apt-get update
+ shell: bash
+
+ - run: |
+ sudo apt install --install-recommends \
+ winehq-staging=${{env.WINE_VERSION}} \
+ wine-staging=${{env.WINE_VERSION}} \
+ wine-staging-i386=${{env.WINE_VERSION}} \
+ wine-staging-amd64=${{env.WINE_VERSION}}
+ shell: bash
+ env:
+ WINE_VERSION: 9.17~jammy-1
+
+ - run: |
+ mkdir -p /usr/share/wine/mono
+ curl -L -o mono.tar.xz ${{env.WINE_MONO}}
+ tar -xf mono.tar.xz -C /usr/share/wine/mono
+ shell: bash
+ env:
+ WINE_MONO: https://github.com/madewokherd/wine-mono/releases/download/wine-mono-9.3.0/wine-mono-9.3.0-x86.tar.xz
diff --git a/.github/actions/test-build-artifacts-upload/action.yml b/.github/actions/test-build-artifacts-upload/action.yml
new file mode 100644
index 000000000..2548ab4c6
--- /dev/null
+++ b/.github/actions/test-build-artifacts-upload/action.yml
@@ -0,0 +1,23 @@
+name: Upload Build Artifacts
+description: Upload the build artifacts for the specified image and build configuration
+
+inputs:
+ image:
+ description: 'The image to use'
+ required: true
+ architecture:
+ description: 'The architecture to use'
+ required: true
+ build_configuration:
+ description: 'The build configuration to use'
+ required: true
+
+runs:
+ using: "composite"
+ steps:
+ - name: Upload Build Artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: artifacts-${{inputs.image}}-${{inputs.architecture}}-${{inputs.build_configuration}}
+ path: artifacts/
+ retention-days: 7
\ No newline at end of file
diff --git a/.github/actions/test-build-cache-download/action.yml b/.github/actions/test-build-cache-download/action.yml
new file mode 100644
index 000000000..6aa92fbcf
--- /dev/null
+++ b/.github/actions/test-build-cache-download/action.yml
@@ -0,0 +1,22 @@
+name: Download Test Build Cache
+description: Download the build cache for the specified image and build configuration
+
+inputs:
+ image:
+ description: 'The image to use'
+ required: true
+ architecture:
+ description: 'The architecture to use'
+ required: true
+ build_configuration:
+ description: 'The build configuration to use'
+ required: true
+
+runs:
+ using: "composite"
+ steps:
+ - name: Download Build Cache
+ uses: actions/download-artifact@v4
+ with:
+ name: build-cache-${{inputs.image}}-${{inputs.architecture}}-${{inputs.build_configuration}}
+ path: artifacts/
\ No newline at end of file
diff --git a/.github/actions/test-build-cache-upload/action.yml b/.github/actions/test-build-cache-upload/action.yml
new file mode 100644
index 000000000..f01065cf5
--- /dev/null
+++ b/.github/actions/test-build-cache-upload/action.yml
@@ -0,0 +1,23 @@
+name: Upload Test Build Cache
+description: Upload the build cache for the specified image and build configuration
+
+inputs:
+ image:
+ description: 'The image to use'
+ required: true
+ architecture:
+ description: 'The architecture to use'
+ required: true
+ build_configuration:
+ description: 'The build configuration to use'
+ required: true
+
+runs:
+ using: "composite"
+ steps:
+ - name: Upload Artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: build-cache-${{inputs.image}}-${{inputs.architecture}}-${{inputs.build_configuration}}
+ path: artifacts/
+ retention-days: 1
diff --git a/.github/actions/test-results-download-publish/action.yml b/.github/actions/test-results-download-publish/action.yml
new file mode 100644
index 000000000..7f2d8b437
--- /dev/null
+++ b/.github/actions/test-results-download-publish/action.yml
@@ -0,0 +1,47 @@
+name: Download and Publish Test Results
+description: Download the test results for the specified operating system and build configuration
+
+inputs:
+ workflow_run:
+ required: true
+ description: 'The workflow run Id to download the test results from'
+ experimental:
+ required: false
+ default: "false"
+ description: 'Whether the tests are mandatory for the build to pass'
+
+runs:
+ using: "composite"
+ steps:
+ - name: Download Event File
+ uses: actions/download-artifact@v4
+ with:
+ run-id: ${{ fromJson(inputs.workflow_run).id }}
+ github-token: ${{ github.token }}
+ name: test-event-file
+ merge-multiple: false
+
+ - name: Download Test Results
+ uses: actions/download-artifact@v4
+ with:
+ run-id: ${{ fromJson(inputs.workflow_run).id }}
+ github-token: ${{ github.token }}
+ pattern: ${{(inputs.experimental == 'true' && 'experimental-') || ''}}test-results-*
+ path: test-results
+ merge-multiple: false
+
+ - name: Publish Test Results
+ uses: nike4613/actions-test-results@v3
+ with:
+ check_name: Test Results ${{(inputs.experimental == 'true' && '(Experimental)') || ''}}
+ files: test-results/**/*.trx
+ fail_on: ${{(inputs.experimental == 'true' && 'nothing') || 'test failures'}}
+ comment_mode: ${{(inputs.experimental == 'true' && 'off') || 'always'}}
+ use_emojis: true
+
+ commit: ${{ fromJson(inputs.workflow_run).head_sha }}
+ event_file: event.json
+ event_name: ${{ fromJson(inputs.workflow_run).event }}
+ #gist_token: ${{ secrets.gist_token }}
+
+ comment_on_commit: true
diff --git a/.github/actions/test-results-upload/action.yml b/.github/actions/test-results-upload/action.yml
new file mode 100644
index 000000000..477ea3332
--- /dev/null
+++ b/.github/actions/test-results-upload/action.yml
@@ -0,0 +1,28 @@
+name: Upload Test Results
+description: Upload the test results for the specified image and build configuration
+
+inputs:
+ osimage:
+ description: 'The image to use'
+ required: true
+ architecture:
+ description: 'The architecture to use'
+ required: true
+ build_configuration:
+ description: 'The build configuration to use'
+ required: true
+ experimental:
+ description: 'Whether the tests are mandatory for the build to pass'
+ required: true
+
+runs:
+ using: "composite"
+ steps:
+ - name: Upload Test Results
+ uses: actions/upload-artifact@v4
+ if: ${{success() || failure()}}
+ with:
+ name: ${{(inputs.experimental == 'true' && 'experimental-') || ''}}test-results-${{inputs.image}}-${{inputs.architecture}}-${{inputs.build_configuration}}
+ path: '**/*.trx'
+ if-no-files-found: ${{(inputs.experimental == 'true' && 'ignore') || 'warn'}}
+ retention-days: 1
diff --git a/.github/workflows/reusable-build.yml b/.github/workflows/reusable-build.yml
new file mode 100644
index 000000000..280861f89
--- /dev/null
+++ b/.github/workflows/reusable-build.yml
@@ -0,0 +1,97 @@
+name: 🔒Reusable Workflow Build for Tests
+
+on:
+ workflow_call:
+ inputs:
+ image:
+ required: true
+ type: string
+ description: 'The image to use'
+ architecture:
+ required: true
+ type: string
+ description: 'The architecture to use'
+ build_configuration:
+ required: true
+ type: string
+ description: 'The build configuration to use'
+
+env:
+ # Disable the .NET logo in the console output.
+ DOTNET_NOLOGO: true
+ # Disable the .NET first time experience to skip caching NuGet packages and speed up the build.
+ DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
+ # Disable sending .NET CLI telemetry to Microsoft.
+ DOTNET_CLI_TELEMETRY_OPTOUT: true
+
+jobs:
+ get-version:
+ name: Calculating Version Suffix
+ runs-on: ubuntu-latest
+ outputs:
+ version_suffix: ${{ steps.set-vars.outputs.version_suffix }}
+ steps:
+ - uses: actions/checkout@v4
+
+ - id: git-vars
+ name: Get git branch information
+ shell: bash
+ run: |
+ echo "##[set-output name=git_branch;]$(echo $GITHUB_REF)"
+ echo "::set-output name=git_hash::$(git rev-parse --short HEAD)"
+
+ - id: set-vars
+ uses: actions/github-script@v7
+ with:
+ script: |
+ let runNumber = "${{ github.run_number }}";
+ let gitHash = "${{ steps.git-vars.outputs.git_hash }}";
+ let rawGitRef = "${{ steps.git-vars.outputs.git_branch }}";
+ console.log("rawGitRef: " + rawGitRef);
+ let gitRef = rawGitRef.replace(/^refs\/heads\//, "").replace(/^refs\/heads\//, "").replace(/[_//!@#$%&]/g, "-");
+ if(gitRef.indexOf("refs/pull/") === 0) {
+ gitRef = "pr-" + gitRef.substring(10, gitRef.lastIndexOf("/"));
+ }
+ var versSuffix = `${gitRef}.${runNumber}+${gitHash}`;
+ console.log(versSuffix);
+ core.setOutput("version_suffix", versSuffix);
+
+ build:
+ name: Upload Test Build Output Cache
+ runs-on: ${{inputs.image}}
+ needs: [get-version]
+ timeout-minutes: 10
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Add msbuild to PATH
+ uses: microsoft/setup-msbuild@v2
+
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v4
+
+ - name: Build on master branch
+ if: ${{github.ref == 'refs/heads/master'}}
+ run: |
+ dotnet restore AsmResolver.sln;
+ msbuild AsmResolver.sln -property:Configuration=${{inputs.build_configuration}} -property:Platform=${{inputs.architecture}};
+
+ - name: Build on non-master branch
+ if: ${{github.ref != 'refs/heads/master'}}
+ run: |
+ dotnet restore AsmResolver.sln /p:VersionSuffix=${{needs.get-version.outputs.version_suffix}};
+ msbuild AsmResolver.sln -property:Configuration=${{inputs.build_configuration}} -property:Platform=${{inputs.architecture}} -property:VersionSuffix=${{needs.get-version.outputs.version_suffix}};
+
+ - name: Upload Build Artifacts
+ uses: ./.github/actions/test-build-artifacts-upload
+ with:
+ image: ${{inputs.image}}
+ architecture: ${{inputs.architecture}}
+ build_configuration: ${{inputs.build_configuration}}
+
+ - name: Upload Build Cache
+ uses: ./.github/actions/test-build-cache-upload
+ with:
+ image: ${{inputs.image}}
+ architecture: ${{inputs.architecture}}
+ build_configuration: ${{inputs.build_configuration}}
diff --git a/.github/workflows/reusable-publish.yml b/.github/workflows/reusable-publish.yml
new file mode 100644
index 000000000..607cbf157
--- /dev/null
+++ b/.github/workflows/reusable-publish.yml
@@ -0,0 +1,60 @@
+name: 🔒Reusable Workflow Publish
+
+on:
+ workflow_call:
+ inputs:
+ image:
+ required: true
+ type: string
+ description: 'The image to use'
+ architecture:
+ required: true
+ type: string
+ description: 'The architecture to use'
+ build_configuration:
+ required: true
+ type: string
+ description: 'The build configuration to use'
+ secrets:
+ NUGET_API_KEY:
+ required: true
+ NIGHTLY_NUGET_API_KEY:
+ required: true
+ NIGHTLY_NUGET_SOURCE:
+ required: true
+
+env:
+ # Disable the .NET logo in the console output.
+ DOTNET_NOLOGO: true
+ # Disable the .NET first time experience to skip caching NuGet packages and speed up the build.
+ DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
+ # Disable sending .NET CLI telemetry to Microsoft.
+ DOTNET_CLI_TELEMETRY_OPTOUT: true
+
+jobs:
+ publish:
+ name: Publish NuGet Packages from Build Output Cache
+ runs-on: ubuntu-latest
+ timeout-minutes: 10
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v4
+
+ - name: Download Build Cache
+ uses: ./.github/actions/test-build-cache-download
+ with:
+ image: ${{inputs.image}}
+ architecture: ${{inputs.architecture}}
+ build_configuration: ${{inputs.build_configuration}}
+
+ - name: Push to NuGet
+ if: ${{github.ref == 'refs/heads/master'}}
+ run: dotnet nuget push "./artifacts/**/*.nupkg" -k ${{secrets.NUGET_API_KEY}} --skip-duplicate
+ shell: pwsh
+
+ - name: Push to NuGet Nightly
+ if: ${{github.ref == 'refs/heads/development'}}
+ run: dotnet nuget push "./artifacts/**/*.nupkg" -k ${{secrets.NIGHTLY_NUGET_API_KEY}} -s ${{secrets.NIGHTLY_NUGET_SOURCE}} --skip-duplicate
+ shell: pwsh
diff --git a/.github/workflows/reusable-test.yml b/.github/workflows/reusable-test.yml
new file mode 100644
index 000000000..687372510
--- /dev/null
+++ b/.github/workflows/reusable-test.yml
@@ -0,0 +1,157 @@
+name: 🔒Reusable Workflow Test
+# We build the binaries for all os and configurations
+# So we don't have to do that on each test run
+
+on:
+ workflow_call:
+ inputs:
+ image:
+ required: true
+ type: string
+ description: 'The image to use'
+ architecture:
+ required: true
+ type: string
+ description: 'The architecture to use'
+ dotnet_target_frameworks:
+ required: true
+ type: string
+ description: 'The target frameworks to use'
+ build_configuration:
+ required: true
+ type: string
+ description: 'The build configuration to use'
+ is_experimental:
+ required: false
+ type: boolean
+ default: false
+ description: 'Whether the tests are mandatory for the build to pass'
+ skip_experimental:
+ required: false
+ type: boolean
+ default: false
+ description: 'Whether to skip the experimental tests'
+
+env:
+ # Disable the .NET logo in the console output.
+ DOTNET_NOLOGO: true
+ # Disable the .NET first time experience to skip caching NuGet packages and speed up the build.
+ DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
+ # Disable sending .NET CLI telemetry to Microsoft.
+ DOTNET_CLI_TELEMETRY_OPTOUT: true
+
+jobs:
+ test:
+ name: Execute Tests
+ runs-on: ${{inputs.image}}
+ continue-on-error: ${{inputs.is_experimental}}
+ timeout-minutes: 10
+ if: ${{ !inputs.is_experimental || (inputs.is_experimental && !inputs.skip_experimental) }}
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install Rosetta 2 on OS X
+ if: ${{inputs.image == 'macos-14' && inputs.architecture == 'x64'}}
+ run: /usr/sbin/softwareupdate --install-rosetta --agree-to-license
+
+ # Wine
+ - name: Setup Wine on Ubuntu
+ if: ${{runner.os == 'Linux'}}
+ uses: ./.github/actions/setup-wine-ubuntu
+
+ - name: Setup Wine on OS X
+ if: ${{runner.os == 'macOS'}}
+ uses: ./.github/actions/setup-wine-macos
+
+ - name: Get Installed Wine Information
+ if: ${{runner.os == 'Linux' || runner.os == 'macOS'}}
+ run: wine --version
+
+ - name: Get Installed Wine Programs Information
+ if: ${{runner.os == 'Linux' || runner.os == 'macOS'}}
+ run: wine uninstaller --list
+ # Wine
+
+ # .NET
+ - name: Setup .NET on Ubuntu and OS X (Intel/Arm)
+ if: ${{runner.os == 'Linux' || (inputs.image == 'macos-13' || (inputs.image == 'macos-14' && inputs.architecture == 'arm64'))}}
+ uses: ./.github/actions/setup-dotnet-unix
+ with:
+ target_framework: ''
+ target_framework_array: ${{inputs.dotnet_target_frameworks}}
+
+ - name: Setup .NET on OS X (Arm) Rosetta 2
+ if: ${{inputs.image == 'macos-14' && inputs.architecture == 'x64'}}
+ uses: ./.github/actions/setup-dotnet-macos-rosetta
+ with:
+ target_framework: ''
+ target_framework_array: ${{inputs.dotnet_target_frameworks}}
+
+ - name: Setup .NET on Windows
+ if: ${{runner.os == 'Windows'}}
+ uses: ./.github/actions/setup-dotnet-windows
+ with:
+ architecture: ${{inputs.architecture}}
+ target_framework: ''
+ target_framework_array: ${{inputs.dotnet_target_frameworks}}
+
+ - name: Get .NET path for ${{inputs.architecture}}
+ uses: ./.github/actions/get-dotnet-path
+ id: get-dotnet-path
+ with:
+ architecture: ${{inputs.architecture}}
+
+ - name: Get Installed .NET Information for ${{inputs.architecture}}
+ run: '& "${{steps.get-dotnet-path.outputs.path}}" --info'
+ shell: pwsh
+ # .NET
+
+ # Mono
+ - name: Setup Mono on Windows
+ if: ${{runner.os == 'Windows'}}
+ uses: ./.github/actions/setup-mono-windows
+ with:
+ architecture: ${{inputs.architecture}}
+
+ - name: Get Mono path for ${{inputs.architecture}}
+ uses: ./.github/actions/get-mono-path
+ id: get-mono-path
+ with:
+ architecture: ${{inputs.architecture}}
+
+ - name: Get Installed Mono Information
+ run: '& "${{steps.get-mono-path.outputs.path}}" --version'
+ shell: pwsh
+ # Mono
+
+ - name: Download Build Cache
+ uses: ./.github/actions/test-build-cache-download
+ with:
+ #image: ${{inputs.image}}
+ image: 'windows-latest'
+ architecture: ${{(inputs.architecture == 'arm64' && 'x64') || inputs.architecture}}
+ build_configuration: ${{inputs.build_configuration}}
+
+ # TODO: Cover Mono on Windows
+ - name: Test
+ run: |
+ $files = Get-ChildItem -Path ./test -Recurse -Filter *.csproj | ? { $_.FullName -inotmatch 'TestBinaries' };
+ $hasError = $false;
+ foreach ($file in $files) {
+ & "${{steps.get-dotnet-path.outputs.path}}" test $file.FullName -c ${{inputs.build_configuration}} -l trx -l 'console;verbosity=normal';
+ Write-Host "Exit code for $($file.FullName): $exitCode"
+ $hasError = $hasError -or $LASTEXITCODE -ne 0;
+ }
+ if ($hasError) {
+ throw 'Tests failed';
+ }
+ shell: pwsh
+
+ - name: Upload Test Results
+ if: always()
+ uses: ./.github/actions/test-results-upload
+ with:
+ image: ${{inputs.image}}
+ architecture: ${{inputs.architecture}}
+ build_configuration: ${{inputs.build_configuration}}
+ experimental: ${{inputs.is_experimental}}
diff --git a/.github/workflows/test-and-publish.yml b/.github/workflows/test-and-publish.yml
new file mode 100644
index 000000000..7f23b7363
--- /dev/null
+++ b/.github/workflows/test-and-publish.yml
@@ -0,0 +1,124 @@
+name: Test and Publish NuGet
+
+on:
+ push:
+ branches:
+ - master
+ - development
+ - simple
+ - simple-test2
+ paths-ignore:
+ - '.github/workflows/docs.yml'
+ - 'docs/**'
+ pull_request:
+ branches:
+ - master
+ - development
+ paths-ignore:
+ - '.github/workflows/docs.yml'
+ - 'docs/*'
+ workflow_dispatch:
+
+concurrency:
+ group: ${{github.workflow}}-${{github.event.pull_request.number || github.ref}}
+ cancel-in-progress: true
+
+env:
+ # Right now we can't run experimental tests that can fail, since it will affect the PR
+ # By default we only run it on master/development branches
+ # Unless the variable will be true
+ EXPERIMENTAL: false
+
+ # You are interested in changing these values
+ BUILD_CONFIGURATIONS: "['Release']"
+ DOTNET_TARGET_FRAMEWORKS: "['netcoreapp3.1', 'net5.0', 'net6.0', 'net7.0', 'net8.0']"
+ DOTNET_TARGET_FRAMEWORKS_MACOS_ARM64: "['net6.0', 'net7.0', 'net8.0']"
+ FRAMEWORK_TARGET_FRAMEWORKS: "['net35', 'net48']"
+
+jobs:
+
+ # This technique allows us to run `test-results-publish.yml` with the owner's secrets
+ # without comprimizing the security
+ upload-event-file:
+ name: Upload Event File
+ runs-on: ubuntu-latest
+ steps:
+ - name: Upload event file
+ uses: actions/upload-artifact@v4
+ with:
+ name: test-event-file
+ path: ${{ github.event_path }}
+ retention-days: 1
+
+ # https://stackoverflow.com/a/77549656
+ variables:
+ name: Variable Accessibility Workaround for Jobs
+ runs-on: ubuntu-latest
+ outputs:
+ EXPERIMENTAL: ${{ env.EXPERIMENTAL == 'true' || (github.event == 'push' && github.ref == 'refs/heads/master' || github.ref == 'refs/heads/development') }}
+ BUILD_CONFIGURATIONS: ${{env.BUILD_CONFIGURATIONS}}
+ DOTNET_TARGET_FRAMEWORKS: ${{env.DOTNET_TARGET_FRAMEWORKS}}
+ DOTNET_TARGET_FRAMEWORKS_MACOS_ARM64: ${{env.DOTNET_TARGET_FRAMEWORKS_MACOS_ARM64}}
+ FRAMEWORK_TARGET_FRAMEWORKS: ${{env.FRAMEWORK_TARGET_FRAMEWORKS}}
+ steps:
+ - name: Compute outputs
+ run: |
+ echo "EXPERIMENTAL=${{env.EXPERIMENTAL}}" >> $GITHUB_OUTPUT
+ echo "BUILD_CONFIGURATIONS=${{env.BUILD_CONFIGURATIONS}}" >> $GITHUB_OUTPUT
+ echo "DOTNET_TARGET_FRAMEWORKS=${{env.DOTNET_TARGET_FRAMEWORKS}}" >> $GITHUB_OUTPUT
+ echo "DOTNET_TARGET_FRAMEWORKS_MACOS_ARM64=${{env.DOTNET_TARGET_FRAMEWORKS_MACOS_ARM64}}" >> $GITHUB_OUTPUT
+ echo "FRAMEWORK_TARGET_FRAMEWORKS=${{env.FRAMEWORK_TARGET_FRAMEWORKS}}" >> $GITHUB_OUTPUT
+
+ build:
+ name: Perform Build
+ needs: [variables]
+ strategy:
+ matrix:
+ runner: [
+ { image: 'windows-latest', arch: 'x64' },
+ { image: 'windows-latest', arch: 'x86' },
+ ]
+ build_configuration: ${{fromJson(needs.variables.outputs.BUILD_CONFIGURATIONS)}}
+ uses: ./.github/workflows/reusable-build.yml
+ with:
+ image: ${{matrix.runner.image}}
+ architecture: ${{matrix.runner.arch}}
+ build_configuration: ${{matrix.build_configuration}}
+
+ test:
+ name: Execute Tests
+ needs: [variables, build]
+ strategy:
+ fail-fast: false
+ matrix:
+ runner: [
+ { image: 'windows-latest', arch: 'x64' },
+ { image: 'windows-latest', arch: 'x86' },
+ { image: 'ubuntu-latest', arch: 'x64' },
+ { image: 'macos-13', arch: 'x64' },
+ { image: 'macos-14', arch: 'arm64' },
+ { image: 'macos-14', arch: 'x64' },
+ ]
+ build_configuration: ${{fromJson(needs.variables.outputs.BUILD_CONFIGURATIONS)}}
+ uses: ./.github/workflows/reusable-test.yml
+ with:
+ image: ${{matrix.runner.image}}
+ architecture: ${{matrix.runner.arch}}
+ dotnet_target_frameworks: ${{needs.variables.outputs.DOTNET_TARGET_FRAMEWORKS}}
+ build_configuration: ${{matrix.build_configuration}}
+ #ignore non-windows tests since support is yet lacking
+ is_experimental: ${{matrix.runner.image != 'windows-latest' && matrix.runner.image != 'ubuntu-latest'}}
+ skip_experimental: ${{ needs.variables.outputs.EXPERIMENTAL != 'true' }}
+
+ publish:
+ name: Publish NuGet Packages
+ needs: [variables, test]
+ uses: ./.github/workflows/reusable-publish.yml
+ with:
+ image: 'windows-latest'
+ architecture: 'x64'
+ build_configuration: 'Release'
+ secrets:
+ NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
+ NIGHTLY_NUGET_API_KEY: ${{ secrets.NIGHTLY_NUGET_API_KEY }}
+ NIGHTLY_NUGET_SOURCE: ${{ secrets.NIGHTLY_NUGET_SOURCE }}
diff --git a/.github/workflows/test-results-publish.yml b/.github/workflows/test-results-publish.yml
new file mode 100644
index 000000000..ea19a5cf2
--- /dev/null
+++ b/.github/workflows/test-results-publish.yml
@@ -0,0 +1,37 @@
+
+name: Publish Test Results
+
+on:
+ workflow_run:
+ workflows:
+ - Test and Publish NuGet
+ types: [completed]
+permissions: {}
+
+jobs:
+ publish-test-results:
+ if: github.event.workflow_run.conclusion != 'skipped' && github.event.workflow_run.conclusion != 'cancelled'
+ runs-on: ubuntu-latest
+ name: Publish Test Results
+
+ permissions:
+ checks: write
+ pull-requests: write
+ contents: write
+ issues: read
+ actions: read
+ statuses: read
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Download and Publish Test Results
+ uses: ./.github/actions/test-results-download-publish
+ with:
+ workflow_run: ${{toJSON(github.event.workflow_run)}}
+
+ - name: Download and Publish Test Results (Experimental)
+ uses: ./.github/actions/test-results-download-publish
+ with:
+ workflow_run: ${{toJSON(github.event.workflow_run)}}
+ experimental: true
\ No newline at end of file
diff --git a/AsmResolver.sln b/AsmResolver.sln
index 492017f8f..ebf8bcb55 100644
--- a/AsmResolver.sln
+++ b/AsmResolver.sln
@@ -70,6 +70,9 @@ EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AsmResolver.Benchmarks", "test\AsmResolver.Benchmarks\AsmResolver.Benchmarks.csproj", "{CB766958-8D07-4E2D-8A38-C21617B1C45D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{0D04B5DF-8D16-488F-8767-E49803D3AF46}"
+ ProjectSection(SolutionItems) = preProject
+ tools\Directory.Build.props = tools\Directory.Build.props
+ EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AsmResolver.PE.Exports.OrdinalMapper", "tools\AsmResolver.PE.Exports.OrdinalMapper\AsmResolver.PE.Exports.OrdinalMapper.csproj", "{3E4B6896-269F-4129-9746-965338779C31}"
EndProject
@@ -118,6 +121,10 @@ Global
{4EA3F800-1A1B-4465-931F-4E9949C3373B}.Release|Any CPU.Build.0 = Release|Any CPU
{4EA3F800-1A1B-4465-931F-4E9949C3373B}.Release|x64.ActiveCfg = Release|Any CPU
{4EA3F800-1A1B-4465-931F-4E9949C3373B}.Release|x86.ActiveCfg = Release|Any CPU
+ {4EA3F800-1A1B-4465-931F-4E9949C3373B}.Release|x64.Build.0 = Release|Any CPU
+ {4EA3F800-1A1B-4465-931F-4E9949C3373B}.Release|x86.Build.0 = Release|Any CPU
+ {4EA3F800-1A1B-4465-931F-4E9949C3373B}.Debug|x86.Build.0 = Debug|Any CPU
+ {4EA3F800-1A1B-4465-931F-4E9949C3373B}.Debug|x64.Build.0 = Debug|Any CPU
{7609C0D1-B65C-4C12-B520-EAF611B0E68F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7609C0D1-B65C-4C12-B520-EAF611B0E68F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7609C0D1-B65C-4C12-B520-EAF611B0E68F}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -138,6 +145,10 @@ Global
{89F2A014-E503-4328-9601-6D93B9E221D8}.Release|Any CPU.Build.0 = Release|Any CPU
{89F2A014-E503-4328-9601-6D93B9E221D8}.Release|x64.ActiveCfg = Release|Any CPU
{89F2A014-E503-4328-9601-6D93B9E221D8}.Release|x86.ActiveCfg = Release|Any CPU
+ {89F2A014-E503-4328-9601-6D93B9E221D8}.Release|x64.Build.0 = Release|Any CPU
+ {89F2A014-E503-4328-9601-6D93B9E221D8}.Release|x86.Build.0 = Release|Any CPU
+ {89F2A014-E503-4328-9601-6D93B9E221D8}.Debug|x86.Build.0 = Debug|Any CPU
+ {89F2A014-E503-4328-9601-6D93B9E221D8}.Debug|x64.Build.0 = Debug|Any CPU
{239BD2AF-D7CF-44C5-BF56-09913166710E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{239BD2AF-D7CF-44C5-BF56-09913166710E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{239BD2AF-D7CF-44C5-BF56-09913166710E}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -158,6 +169,10 @@ Global
{856EEF27-8430-4360-9E4C-F923AF142D23}.Release|Any CPU.Build.0 = Release|Any CPU
{856EEF27-8430-4360-9E4C-F923AF142D23}.Release|x64.ActiveCfg = Release|Any CPU
{856EEF27-8430-4360-9E4C-F923AF142D23}.Release|x86.ActiveCfg = Release|Any CPU
+ {856EEF27-8430-4360-9E4C-F923AF142D23}.Release|x64.Build.0 = Release|Any CPU
+ {856EEF27-8430-4360-9E4C-F923AF142D23}.Release|x86.Build.0 = Release|Any CPU
+ {856EEF27-8430-4360-9E4C-F923AF142D23}.Debug|x86.Build.0 = Debug|Any CPU
+ {856EEF27-8430-4360-9E4C-F923AF142D23}.Debug|x64.Build.0 = Debug|Any CPU
{4BCE8E05-9A72-4C7C-8A4C-130133F4FC56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4BCE8E05-9A72-4C7C-8A4C-130133F4FC56}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4BCE8E05-9A72-4C7C-8A4C-130133F4FC56}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -166,6 +181,10 @@ Global
{4BCE8E05-9A72-4C7C-8A4C-130133F4FC56}.Release|Any CPU.Build.0 = Release|Any CPU
{4BCE8E05-9A72-4C7C-8A4C-130133F4FC56}.Release|x64.ActiveCfg = Release|Any CPU
{4BCE8E05-9A72-4C7C-8A4C-130133F4FC56}.Release|x86.ActiveCfg = Release|Any CPU
+ {4BCE8E05-9A72-4C7C-8A4C-130133F4FC56}.Release|x64.Build.0 = Release|Any CPU
+ {4BCE8E05-9A72-4C7C-8A4C-130133F4FC56}.Release|x86.Build.0 = Release|Any CPU
+ {4BCE8E05-9A72-4C7C-8A4C-130133F4FC56}.Debug|x86.Build.0 = Debug|Any CPU
+ {4BCE8E05-9A72-4C7C-8A4C-130133F4FC56}.Debug|x64.Build.0 = Debug|Any CPU
{6F5D18B2-8536-452E-83C4-4FE594E5A37F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6F5D18B2-8536-452E-83C4-4FE594E5A37F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6F5D18B2-8536-452E-83C4-4FE594E5A37F}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -403,13 +422,11 @@ Global
{E7D186F7-B2BC-451D-98CC-427866CABE54}.Release|x86.ActiveCfg = Release|Win32
{E7D186F7-B2BC-451D-98CC-427866CABE54}.Release|x86.Build.0 = Release|Win32
{40483E28-C703-4933-BA5B-9512EF6E6A21}.Debug|Any CPU.ActiveCfg = Debug|x64
- {40483E28-C703-4933-BA5B-9512EF6E6A21}.Debug|Any CPU.Build.0 = Debug|x64
{40483E28-C703-4933-BA5B-9512EF6E6A21}.Debug|x64.ActiveCfg = Debug|x64
{40483E28-C703-4933-BA5B-9512EF6E6A21}.Debug|x64.Build.0 = Debug|x64
{40483E28-C703-4933-BA5B-9512EF6E6A21}.Debug|x86.ActiveCfg = Debug|Win32
{40483E28-C703-4933-BA5B-9512EF6E6A21}.Debug|x86.Build.0 = Debug|Win32
{40483E28-C703-4933-BA5B-9512EF6E6A21}.Release|Any CPU.ActiveCfg = Release|x64
- {40483E28-C703-4933-BA5B-9512EF6E6A21}.Release|Any CPU.Build.0 = Release|x64
{40483E28-C703-4933-BA5B-9512EF6E6A21}.Release|x64.ActiveCfg = Release|x64
{40483E28-C703-4933-BA5B-9512EF6E6A21}.Release|x64.Build.0 = Release|x64
{40483E28-C703-4933-BA5B-9512EF6E6A21}.Release|x86.ActiveCfg = Release|Win32
diff --git a/Directory.Build.props b/Directory.Build.props
index d2dca83f1..91039a58c 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -2,14 +2,19 @@
Copyright © Washi 2016-2024
- https://github.com/Washi1337/AsmResolver
https://github.com/Washi1337/AsmResolver/LICENSE.md
- https://github.com/Washi1337/AsmResolver
- git
+ true
+ true
+ $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb
10
- 6.0.0-beta.1
+ 6.0.0
+ beta.1
true
true
+
+ true
+
+
diff --git a/appveyor.yml b/appveyor.yml
index 69dc8beed..1d11d753d 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -11,6 +11,21 @@
files:
- docs/*
+ #---------------------------------#
+ # Install .NET #
+ #---------------------------------#
+ install:
+ - ps: $env:DOTNET_INSTALL_DIR = "$pwd\.dotnetsdk"
+ - ps: mkdir $env:DOTNET_INSTALL_DIR -Force | Out-Null
+ - ps: Invoke-WebRequest -Uri "https://dot.net/v1/dotnet-install.ps1" -OutFile "$($env:DOTNET_INSTALL_DIR)/dotnet-install.ps1"
+ - ps: '& "$($env:DOTNET_INSTALL_DIR)/dotnet-install.ps1" -Channel 3.1 -InstallDir $env:DOTNET_INSTALL_DIR'
+ - ps: '& "$($env:DOTNET_INSTALL_DIR)/dotnet-install.ps1" -Channel 5.0 -InstallDir $env:DOTNET_INSTALL_DIR'
+ - ps: '& "$($env:DOTNET_INSTALL_DIR)/dotnet-install.ps1" -Channel 6.0 -InstallDir $env:DOTNET_INSTALL_DIR'
+ - ps: '& "$($env:DOTNET_INSTALL_DIR)/dotnet-install.ps1" -Channel 7.0 -InstallDir $env:DOTNET_INSTALL_DIR'
+ - ps: '& "$($env:DOTNET_INSTALL_DIR)/dotnet-install.ps1" -Channel 8.0 -InstallDir $env:DOTNET_INSTALL_DIR'
+ - ps: $env:Path = "$env:DOTNET_INSTALL_DIR;$env:Path"
+ - ps: dotnet --info
+
before_build:
- dotnet restore
@@ -40,6 +55,21 @@
files:
- docs/*
+ #---------------------------------#
+ # Install .NET #
+ #---------------------------------#
+ install:
+ - ps: $env:DOTNET_INSTALL_DIR = "$pwd\.dotnetsdk"
+ - ps: mkdir $env:DOTNET_INSTALL_DIR -Force | Out-Null
+ - ps: Invoke-WebRequest -Uri "https://dot.net/v1/dotnet-install.ps1" -OutFile "$($env:DOTNET_INSTALL_DIR)/dotnet-install.ps1"
+ - ps: '& "$($env:DOTNET_INSTALL_DIR)/dotnet-install.ps1" -Channel 3.1 -InstallDir $env:DOTNET_INSTALL_DIR'
+ - ps: '& "$($env:DOTNET_INSTALL_DIR)/dotnet-install.ps1" -Channel 5.0 -InstallDir $env:DOTNET_INSTALL_DIR'
+ - ps: '& "$($env:DOTNET_INSTALL_DIR)/dotnet-install.ps1" -Channel 6.0 -InstallDir $env:DOTNET_INSTALL_DIR'
+ - ps: '& "$($env:DOTNET_INSTALL_DIR)/dotnet-install.ps1" -Channel 7.0 -InstallDir $env:DOTNET_INSTALL_DIR'
+ - ps: '& "$($env:DOTNET_INSTALL_DIR)/dotnet-install.ps1" -Channel 8.0 -InstallDir $env:DOTNET_INSTALL_DIR'
+ - ps: $env:Path = "$env:DOTNET_INSTALL_DIR;$env:Path"
+ - ps: dotnet --info
+
before_build:
- dotnet restore
diff --git a/src/AsmResolver.DotNet.Dynamic/AsmResolver.DotNet.Dynamic.csproj b/src/AsmResolver.DotNet.Dynamic/AsmResolver.DotNet.Dynamic.csproj
index 6db621977..a3ab5f7fd 100644
--- a/src/AsmResolver.DotNet.Dynamic/AsmResolver.DotNet.Dynamic.csproj
+++ b/src/AsmResolver.DotNet.Dynamic/AsmResolver.DotNet.Dynamic.csproj
@@ -1,33 +1,22 @@
-
- AsmResolver.DotNet.Dynamic
- Dynamic method support for the AsmResolver executable file inspection toolsuite.
- exe pe directories imports exports resources dotnet cil inspection manipulation assembly disassembly dynamic
- 1701;1702;NU5105
-
+
+ AsmResolver.DotNet.Dynamic
+ Dynamic method support for the AsmResolver executable file inspection toolsuite.
+ exe pe directories imports exports resources dotnet cil inspection manipulation assembly disassembly dynamic
+ false
+
-
- bin\Debug\AsmResolver.DotNet.Dynamic.xml
-
+
+ bin\Debug\AsmResolver.DotNet.Dynamic.xml
+
-
- bin\Release\AsmResolver.DotNet.Dynamic.xml
-
+
+ bin\Release\AsmResolver.DotNet.Dynamic.xml
+
-
-
-
-
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
+
+
+
diff --git a/src/AsmResolver.DotNet/AsmResolver.DotNet.csproj b/src/AsmResolver.DotNet/AsmResolver.DotNet.csproj
index 9de9ad8f8..59a99df5a 100644
--- a/src/AsmResolver.DotNet/AsmResolver.DotNet.csproj
+++ b/src/AsmResolver.DotNet/AsmResolver.DotNet.csproj
@@ -1,33 +1,21 @@
-
- AsmResolver.DotNet
- High level .NET image models for the AsmResolver executable file inspection toolsuite.
- exe pe directories imports exports resources dotnet cil inspection manipulation assembly disassembly
- 1701;1702;NU5105
- true
-
+
+ AsmResolver.DotNet
+ High level .NET image models for the AsmResolver executable file inspection toolsuite.
+ exe pe directories imports exports resources dotnet cil inspection manipulation assembly disassembly
+
-
- bin\Debug\AsmResolver.DotNet.xml
-
+
+ bin\Debug\AsmResolver.DotNet.xml
+
-
- bin\Release\AsmResolver.DotNet.xml
-
+
+ bin\Release\AsmResolver.DotNet.xml
+
-
-
-
+
+
+
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
diff --git a/src/AsmResolver.PE.File/AsmResolver.PE.File.csproj b/src/AsmResolver.PE.File/AsmResolver.PE.File.csproj
index fb00775e4..f03ccca79 100644
--- a/src/AsmResolver.PE.File/AsmResolver.PE.File.csproj
+++ b/src/AsmResolver.PE.File/AsmResolver.PE.File.csproj
@@ -1,35 +1,23 @@
-
- AsmResolver.PE.File
- Raw PE file models for the AsmResolver executable file inspection toolsuite.
- exe pe headers sections inspection manipulation assembly disassembly
- 1701;1702;NU5105
- true
- true
-
+
+ AsmResolver.PE.File
+ Raw PE file models for the AsmResolver executable file inspection toolsuite.
+ exe pe headers sections inspection manipulation assembly disassembly
+ 1701;1702;NU5105
+ true
+
-
- bin\Debug\AsmResolver.PE.File.xml
-
+
+ bin\Debug\AsmResolver.PE.File.xml
+
-
- bin\Release\AsmResolver.PE.File.xml
-
+
+ bin\Release\AsmResolver.PE.File.xml
+
-
-
-
-
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
+
+
+
diff --git a/src/AsmResolver.PE.File/OptionalHeader.cs b/src/AsmResolver.PE.File/OptionalHeader.cs
index 196b33a64..d9821cb55 100644
--- a/src/AsmResolver.PE.File/OptionalHeader.cs
+++ b/src/AsmResolver.PE.File/OptionalHeader.cs
@@ -414,8 +414,10 @@ public void SetDataDirectory(DataDirectoryIndex index, DataDirectory directory)
/// Maps a segment to a data directory.
///
/// The index.
- /// The contents of the data directory.
- public void SetDataDirectory(DataDirectoryIndex index, ISegment contents)
+ ///
+ /// The contents of the data directory, or null to indicate an absence of the directory.
+ ///
+ public void SetDataDirectory(DataDirectoryIndex index, ISegment? contents)
{
SetDataDirectory(index, DataDirectory.CreateForSegment(contents));
}
diff --git a/src/AsmResolver.PE.Win32Resources/AsmResolver.PE.Win32Resources.csproj b/src/AsmResolver.PE.Win32Resources/AsmResolver.PE.Win32Resources.csproj
index 6af1f8737..96725bd6e 100644
--- a/src/AsmResolver.PE.Win32Resources/AsmResolver.PE.Win32Resources.csproj
+++ b/src/AsmResolver.PE.Win32Resources/AsmResolver.PE.Win32Resources.csproj
@@ -1,35 +1,22 @@
-
- AsmResolver.PE.Win32Resources
- exe pe directories imports exports resources dotnet cil inspection manipulation assembly disassembly
- 1701;1702;NU5105
- true
- 12
-
+
+ AsmResolver.PE.Win32Resources
+ exe pe directories imports exports resources dotnet cil inspection manipulation assembly disassembly
+ 12
+
-
- bin\Debug\AsmResolver.PE.Win32Resources.xml
-
+
+ bin\Debug\AsmResolver.PE.Win32Resources.xml
+
-
- bin\Release\AsmResolver.PE.Win32Resources.xml
-
+
+ bin\Release\AsmResolver.PE.Win32Resources.xml
+
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
-
-
-
-
+
+
+
+
diff --git a/src/AsmResolver.PE/AsmResolver.PE.csproj b/src/AsmResolver.PE/AsmResolver.PE.csproj
index 699fbbd7a..2cafd88fb 100644
--- a/src/AsmResolver.PE/AsmResolver.PE.csproj
+++ b/src/AsmResolver.PE/AsmResolver.PE.csproj
@@ -1,34 +1,21 @@
-
- AsmResolver.PE
- PE image models for the AsmResolver executable file inspection toolsuite.
- exe pe directories imports exports resources dotnet cil inspection manipulation assembly disassembly
- 1701;1702;NU5105
- true
-
+
+ AsmResolver.PE
+ PE image models for the AsmResolver executable file inspection toolsuite.
+ exe pe directories imports exports resources dotnet cil inspection manipulation assembly disassembly
+
-
- bin\Debug\AsmResolver.PE.xml
-
+
+ bin\Debug\AsmResolver.PE.xml
+
-
- bin\Release\AsmResolver.PE.xml
-
+
+ bin\Release\AsmResolver.PE.xml
+
-
-
-
-
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
+
+
+
diff --git a/src/AsmResolver.PE/Builder/UnmanagedPEFileBuilder.cs b/src/AsmResolver.PE/Builder/UnmanagedPEFileBuilder.cs
index 7af2d90b8..bc816699a 100644
--- a/src/AsmResolver.PE/Builder/UnmanagedPEFileBuilder.cs
+++ b/src/AsmResolver.PE/Builder/UnmanagedPEFileBuilder.cs
@@ -188,29 +188,26 @@ protected override void AssignDataDirectories(BuilderContext context, PEFile out
header.EnsureDataDirectoryCount(OptionalHeader.DefaultNumberOfRvasAndSizes);
+ // NOTE: We need to explicitly check if imports.count == 0 because the buffer may not be populated if
+ // IAT trampolining (and thus IAT rebuilding) is disabled. In such a case, we want to preserve the existing IAT
+ // rather than remove it.
if (!context.ImportDirectory.IsEmpty)
{
header.SetDataDirectory(DataDirectoryIndex.ImportDirectory, context.ImportDirectory);
header.SetDataDirectory(DataDirectoryIndex.IatDirectory, context.ImportDirectory.ImportAddressDirectory);
}
+ else if (context.Image.Imports.Count == 0)
+ {
+ header.SetDataDirectory(DataDirectoryIndex.ImportDirectory, null);
+ header.SetDataDirectory(DataDirectoryIndex.IatDirectory, null);
+ }
- if (!context.ExportDirectory.IsEmpty)
- header.SetDataDirectory(DataDirectoryIndex.ExportDirectory, context.ExportDirectory);
-
- if (!context.DebugDirectory.IsEmpty)
- header.SetDataDirectory(DataDirectoryIndex.DebugDirectory, context.DebugDirectory);
-
- if (!context.ResourceDirectory.IsEmpty)
- header.SetDataDirectory(DataDirectoryIndex.ResourceDirectory, context.ResourceDirectory);
-
- if (context.Image.DotNetDirectory is not null)
- header.SetDataDirectory(DataDirectoryIndex.ClrDirectory, context.Image.DotNetDirectory);
-
- if (context.Image.TlsDirectory is not null)
- header.SetDataDirectory(DataDirectoryIndex.TlsDirectory, context.Image.TlsDirectory);
-
- if (!context.RelocationsDirectory.IsEmpty)
- header.SetDataDirectory(DataDirectoryIndex.BaseRelocationDirectory, context.RelocationsDirectory);
+ header.SetDataDirectory(DataDirectoryIndex.ExportDirectory, !context.ExportDirectory.IsEmpty ? context.ExportDirectory : null);
+ header.SetDataDirectory(DataDirectoryIndex.DebugDirectory, !context.DebugDirectory.IsEmpty ? context.DebugDirectory : null);
+ header.SetDataDirectory(DataDirectoryIndex.ResourceDirectory, !context.ResourceDirectory.IsEmpty ? context.ResourceDirectory : null);
+ header.SetDataDirectory(DataDirectoryIndex.ClrDirectory, context.Image.DotNetDirectory);
+ header.SetDataDirectory(DataDirectoryIndex.TlsDirectory, context.Image.TlsDirectory);
+ header.SetDataDirectory(DataDirectoryIndex.BaseRelocationDirectory, !context.RelocationsDirectory.IsEmpty ? context.RelocationsDirectory : null);
}
///
@@ -234,10 +231,6 @@ protected override void AssignDataDirectories(BuilderContext context, PEFile out
if (TrampolineImports && !context.ImportDirectory.IsEmpty)
contents.Add(context.ImportDirectory);
- // Reconstructed exports.
- if (!context.ExportDirectory.IsEmpty)
- contents.Add(context.ExportDirectory);
-
// Reconstructed debug directory.
if (!context.DebugDirectory.IsEmpty)
{
@@ -258,7 +251,7 @@ protected override void AssignDataDirectories(BuilderContext context, PEFile out
context.ImportTrampolines.ApplyPatches(context.ClonedSections);
}
- // Code for newly added exports.
+ // Add newly added exports code.
if (context.Image.Exports is { Entries: { Count: > 0 } entries })
{
for (int i = 0; i < entries.Count; i++)
@@ -350,6 +343,13 @@ void AddOrPatch(ISegment? segment, ISegment? originalSegment)
AddOrPatch(directory.CallbackFunctions, originalDirectory?.CallbackFunctions);
}
+ // Add export directory.
+ if (image.Exports is { Entries.Count: > 0 })
+ {
+ if (!TryPatchDataDirectory(context, context.ExportDirectory, DataDirectoryIndex.ExportDirectory))
+ contents.Add(context.ExportDirectory, (uint) context.Platform.PointerSize);
+ }
+
if (contents.Count == 0)
return null;
@@ -386,10 +386,6 @@ void AddOrPatch(ISegment? newSegment, ISegment? originalSegment)
contents.Add(fixups[i].Tokens, (uint) context.Platform.PointerSize);
}
- // Add export directory.
- if (image.Exports is { Entries.Count: > 0 })
- contents.Add(context.ExportDirectory, (uint) context.Platform.PointerSize);
-
if (image.TlsDirectory is { } directory)
{
// Add TLS index segment.
diff --git a/src/AsmResolver.PE/Debug/Builder/DebugDirectoryBuffer.cs b/src/AsmResolver.PE/Debug/Builder/DebugDirectoryBuffer.cs
index 2cc85880d..c3a0dc300 100644
--- a/src/AsmResolver.PE/Debug/Builder/DebugDirectoryBuffer.cs
+++ b/src/AsmResolver.PE/Debug/Builder/DebugDirectoryBuffer.cs
@@ -27,7 +27,7 @@ public class DebugDirectoryBuffer : ISegment
public void AddEntry(DebugDataEntry entry)
{
_headers.Add(entry);
- if (entry.Contents != null)
+ if (entry.Contents is not null and not EmptyDebugDataSegment)
_streamsTable.Add(entry.Contents, 4);
}
diff --git a/src/AsmResolver.PE/Debug/EmptyDebugDataSegment.cs b/src/AsmResolver.PE/Debug/EmptyDebugDataSegment.cs
new file mode 100644
index 000000000..402079a8e
--- /dev/null
+++ b/src/AsmResolver.PE/Debug/EmptyDebugDataSegment.cs
@@ -0,0 +1,43 @@
+using System.Diagnostics;
+using AsmResolver.IO;
+
+namespace AsmResolver.PE.Debug;
+
+///
+/// Represents the contents of an empty debug data entry.
+///
+public sealed class EmptyDebugDataSegment : IDebugDataSegment
+{
+ ///
+ /// Creates a new empty debug data segment for the provided debug data content.
+ ///
+ /// The content type.
+ public EmptyDebugDataSegment(DebugDataType type)
+ {
+ Type = type;
+ }
+
+ ///
+ public DebugDataType Type { get; }
+
+ bool ISegment.CanUpdateOffsets => false;
+
+ ulong IOffsetProvider.Offset => 0;
+
+ uint IOffsetProvider.Rva => 0;
+
+ ///
+ void ISegment.UpdateOffsets(in RelocationParameters parameters)
+ {
+ }
+
+ ///
+ uint IWritable.GetPhysicalSize() => 0;
+
+ ///
+ uint ISegment.GetVirtualSize() => 0;
+
+ void IWritable.Write(BinaryStreamWriter writer)
+ {
+ }
+}
diff --git a/src/AsmResolver.PE/Debug/RsdsDataSegment.cs b/src/AsmResolver.PE/Debug/RsdsDataSegment.cs
index e26e19c33..77450a1b2 100644
--- a/src/AsmResolver.PE/Debug/RsdsDataSegment.cs
+++ b/src/AsmResolver.PE/Debug/RsdsDataSegment.cs
@@ -38,7 +38,7 @@ public class RsdsDataSegment : CodeViewDataSegment
reader.ReadBytes(buffer, 0, 16);
result.Guid = new Guid(buffer);
result.Age = reader.ReadUInt32();
- result.Path = Encoding.UTF8.GetString(reader.ReadBytesUntil(0x00));
+ result.Path = Encoding.UTF8.GetString(reader.ReadBytesUntil(0x00, false));
return result;
}
diff --git a/src/AsmResolver.PE/Debug/SerializedDebugDataEntry.cs b/src/AsmResolver.PE/Debug/SerializedDebugDataEntry.cs
index 4711a6063..311adfeaf 100644
--- a/src/AsmResolver.PE/Debug/SerializedDebugDataEntry.cs
+++ b/src/AsmResolver.PE/Debug/SerializedDebugDataEntry.cs
@@ -43,17 +43,25 @@ public SerializedDebugDataEntry(
///
protected override IDebugDataSegment? GetContents()
{
+ BinaryStreamReader reader;
+
if (_sizeOfData == 0)
- return null;
+ return new EmptyDebugDataSegment(_type);
- var reference = _context.File.GetReferenceToRva(_addressOfRawData);
- if (!reference.CanRead)
+ if (_addressOfRawData == 0 || _context.File.MappingMode == File.PEMappingMode.Unmapped)
+ {
+ if (!_context.File.TryCreateReaderAtFileOffset(_pointerToRawData, out reader))
+ {
+ _context.BadImage("Debug data entry contains an invalid file offset.");
+ return null;
+ }
+ }
+ else if (!_context.File.TryCreateReaderAtRva(_addressOfRawData, out reader))
{
_context.BadImage("Debug data entry contains an invalid RVA.");
return null;
}
- var reader = reference.CreateReader();
if (_sizeOfData > reader.Length)
{
_context.BadImage("Debug data entry contains a too large size.");
diff --git a/src/AsmResolver.PE/Relocations/Builder/RelocationBlock.cs b/src/AsmResolver.PE/Relocations/Builder/RelocationBlock.cs
index 53a36a08e..f1fceac48 100644
--- a/src/AsmResolver.PE/Relocations/Builder/RelocationBlock.cs
+++ b/src/AsmResolver.PE/Relocations/Builder/RelocationBlock.cs
@@ -1,5 +1,4 @@
using System.Collections.Generic;
-using System.Threading;
using AsmResolver.IO;
namespace AsmResolver.PE.Relocations.Builder
@@ -36,18 +35,27 @@ public IList Entries
}
///
- public override uint GetPhysicalSize() => (uint) (Entries.Count + 1) * sizeof(ushort) + 2 * sizeof(uint);
+ public override uint GetPhysicalSize()
+ {
+ // Ensure the ending zero entry is included in the size.
+ int totalCount = Entries.Count + (Entries[Entries.Count - 1].IsEmpty ? 0 : 1);
+ return (uint) totalCount * sizeof(ushort) + 2 * sizeof(uint);
+ }
///
public override void Write(BinaryStreamWriter writer)
{
+ // Block header.
writer.WriteUInt32(PageRva);
writer.WriteUInt32(GetPhysicalSize());
+ // Write all entries in block.
for (int i = 0; i < Entries.Count; i++)
Entries[i].Write(writer);
- default(RelocationEntry).Write(writer);
+ // Ensure block ends with zero entry.
+ if (!Entries[Entries.Count - 1].IsEmpty)
+ default(RelocationEntry).Write(writer);
}
}
diff --git a/src/AsmResolver.PE/Relocations/Builder/RelocationEntry.cs b/src/AsmResolver.PE/Relocations/Builder/RelocationEntry.cs
index 6cc559611..eb1a792e7 100644
--- a/src/AsmResolver.PE/Relocations/Builder/RelocationEntry.cs
+++ b/src/AsmResolver.PE/Relocations/Builder/RelocationEntry.cs
@@ -47,6 +47,11 @@ public RelocationEntry(RelocationType type, int offset)
///
public int Offset => _value & 0xFFF;
+ ///
+ /// Gets a value indicating the entry is a zero entry.
+ ///
+ public bool IsEmpty => _value == 0;
+
///
public uint GetPhysicalSize() => sizeof(ushort);
diff --git a/src/AsmResolver.Symbols.Pdb/AsmResolver.Symbols.Pdb.csproj b/src/AsmResolver.Symbols.Pdb/AsmResolver.Symbols.Pdb.csproj
index 61bad1f34..74a5401c0 100644
--- a/src/AsmResolver.Symbols.Pdb/AsmResolver.Symbols.Pdb.csproj
+++ b/src/AsmResolver.Symbols.Pdb/AsmResolver.Symbols.Pdb.csproj
@@ -1,36 +1,23 @@
-
- AsmResolver
- Windows PDB models for the AsmResolver executable file inspection toolsuite.
- true
- true
-
+
+ AsmResolver
+ Windows PDB models for the AsmResolver executable file inspection toolsuite.
+
-
- true
- bin\Debug\netstandard2.0\AsmResolver.Symbols.Pdb.xml
-
+
+ true
+ bin\Debug\netstandard2.0\AsmResolver.Symbols.Pdb.xml
+
-
- true
- bin\Release\netstandard2.0\AsmResolver.Symbols.Pdb.xml
-
+
+ true
+ bin\Release\netstandard2.0\AsmResolver.Symbols.Pdb.xml
+
-
-
-
-
-
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
+
+
+
+
diff --git a/src/AsmResolver/AsmResolver.csproj b/src/AsmResolver/AsmResolver.csproj
index ddaa59dfb..804809d85 100644
--- a/src/AsmResolver/AsmResolver.csproj
+++ b/src/AsmResolver/AsmResolver.csproj
@@ -1,36 +1,23 @@
-
- AsmResolver
- The base library for the AsmResolver executable file inspection toolsuite.
- exe pe dotnet cil inspection manipulation assembly disassembly
- 1701;1702;NU5105
- true
-
+
+ AsmResolver
+ The base library for the AsmResolver executable file inspection toolsuite.
+ exe pe dotnet cil inspection manipulation assembly disassembly
+
-
- true
- bin\Debug\netstandard2.0\AsmResolver.xml
-
+
+ true
+ bin\Debug\netstandard2.0\AsmResolver.xml
+
-
- true
- bin\Release\netstandard2.0\AsmResolver.xml
-
+
+ true
+ bin\Release\netstandard2.0\AsmResolver.xml
+
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
-
-
-
+
+
+
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 59bb459dd..cf799acc9 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -1,10 +1,25 @@
-
+
+
net8.0;net6.0;net35;netcoreapp3.1;netstandard2.0;netstandard2.1
true
enable
- $(MSBuildThisFileDirectory)..\artifacts
+ true
+ 1701;1702;NU5105
+ $(MSBuildThisFileDirectory)..\artifacts\src
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
diff --git a/test/AsmResolver.DotNet.Tests/Bundles/BundleManifestTest.cs b/test/AsmResolver.DotNet.Tests/Bundles/BundleManifestTest.cs
index aa32bde45..e343fdf8f 100644
--- a/test/AsmResolver.DotNet.Tests/Bundles/BundleManifestTest.cs
+++ b/test/AsmResolver.DotNet.Tests/Bundles/BundleManifestTest.cs
@@ -307,9 +307,8 @@ private void AssertPatchAndRepackageChangesOutput(
"HelloWorld.exe",
bundleStream.ToArray(),
null,
- 5000,
- className,
- methodName);
+ testClass: className,
+ testMethod: methodName);
Assert.Equal("Hello, Mars!\n", output);
}
@@ -337,9 +336,8 @@ private void AssertWriteManifestWindowsPreservesOutput(
Path.ChangeExtension(fileName, ".exe"),
stream.ToArray(),
null,
- 5000,
- className,
- methodName);
+ testClass: className,
+ testMethod: methodName);
Assert.Equal(expectedOutput.Replace("\r\n", "\n"), output);
}
diff --git a/test/AsmResolver.DotNet.Tests/TestUtils.cs b/test/AsmResolver.DotNet.Tests/TestUtils.cs
index 4a3b7e23f..ad33bd7d0 100644
--- a/test/AsmResolver.DotNet.Tests/TestUtils.cs
+++ b/test/AsmResolver.DotNet.Tests/TestUtils.cs
@@ -12,7 +12,7 @@ public static class TestUtils
// We want unit tests to always throw reader errors as opposed to ignore them.
public static readonly ModuleReaderParameters TestReaderParameters = new(ThrowErrorListener.Instance);
- public static void RebuildAndRun(this PERunner runner, ModuleDefinition module, string fileName, string expectedOutput, int timeout = 5000,
+ public static void RebuildAndRun(this PERunner runner, ModuleDefinition module, string fileName, string expectedOutput, int timeout = 30000,
[CallerFilePath] string testClass = "File",
[CallerMemberName] string testMethod = "Test")
{
diff --git a/test/AsmResolver.PE.Tests/Builder/UnmanagedPEFileBuilderTest.cs b/test/AsmResolver.PE.Tests/Builder/UnmanagedPEFileBuilderTest.cs
index 249ef6b2e..303b2b593 100644
--- a/test/AsmResolver.PE.Tests/Builder/UnmanagedPEFileBuilderTest.cs
+++ b/test/AsmResolver.PE.Tests/Builder/UnmanagedPEFileBuilderTest.cs
@@ -3,6 +3,7 @@
using System.Linq;
using AsmResolver.PE.Builder;
using AsmResolver.PE.DotNet.Metadata;
+using AsmResolver.PE.Exports;
using AsmResolver.PE.File;
using AsmResolver.PE.Imports;
using AsmResolver.Shims;
@@ -233,4 +234,19 @@ public void AddMetadataToMixedModeAssembly()
expectedOutput
);
}
+
+ [Fact]
+ public void AddExportToExistingDirectory()
+ {
+ var image = PEImage.FromBytes(Properties.Resources.SimpleDll_Exports, TestReaderParameters);
+ image.Exports!.Entries.Add(new ExportedSymbol(new VirtualAddress(0x13371337), "MySymbol"));
+
+ var file = image.ToPEFile(new UnmanagedPEFileBuilder());
+ using var stream = new MemoryStream();
+ file.Write(stream);
+
+ var newImage = PEImage.FromBytes(stream.ToArray(), TestReaderParameters);
+ Assert.NotNull(newImage.Exports);
+ Assert.Equal(image.Exports.Entries.Select(x => x.Name), newImage.Exports.Entries.Select(x => x.Name));
+ }
}
diff --git a/test/AsmResolver.Tests/Runners/PERunner.cs b/test/AsmResolver.Tests/Runners/PERunner.cs
index 4f6761cb4..3769ae6a3 100644
--- a/test/AsmResolver.Tests/Runners/PERunner.cs
+++ b/test/AsmResolver.Tests/Runners/PERunner.cs
@@ -25,7 +25,7 @@ protected abstract string ExecutableExtension
get;
}
- public void RebuildAndRun(PEFile peFile, string fileName, string expectedOutput, int timeout = 5000,
+ public void RebuildAndRun(PEFile peFile, string fileName, string expectedOutput, int timeout = 30000,
[CallerFilePath] string testClass = "File",
[CallerMemberName] string testMethod = "Test")
{
@@ -59,7 +59,7 @@ public string Rebuild(PEFile peFile, string fileName, string testClass, string t
}
public string RunAndCaptureOutput(string fileName, byte[] contents, string[]? arguments = null,
- int timeout = 5000,
+ int timeout = 30000,
[CallerFilePath] string testClass = "File",
[CallerMemberName] string testMethod = "Test")
{
@@ -69,7 +69,7 @@ public string RunAndCaptureOutput(string fileName, byte[] contents, string[]? ar
return RunAndCaptureOutput(testExecutablePath, arguments, timeout);
}
- public string RunAndCaptureOutput(string filePath, string[]? arguments = null, int timeout = 5000)
+ public string RunAndCaptureOutput(string filePath, string[]? arguments = null, int timeout = 30000)
{
var info = GetStartInfo(filePath, arguments);
info.RedirectStandardError = true;
diff --git a/test/Directory.Build.props b/test/Directory.Build.props
index 171c6ac9d..8bd4deeda 100644
--- a/test/Directory.Build.props
+++ b/test/Directory.Build.props
@@ -1,5 +1,6 @@
-
+
+
$(MSBuildThisFileDirectory)..\artifacts\test
diff --git a/test/TestBinaries/DotNet/HelloWorld/HelloWorld.csproj b/test/TestBinaries/DotNet/HelloWorld/HelloWorld.csproj
index 90588ca1e..af0779346 100644
--- a/test/TestBinaries/DotNet/HelloWorld/HelloWorld.csproj
+++ b/test/TestBinaries/DotNet/HelloWorld/HelloWorld.csproj
@@ -2,7 +2,7 @@
Exe
- net47;netcoreapp3.1;net6.0;net8.0
+ netcoreapp3.1;net6.0;net8.0
Resources\Icon.ico
true
diff --git a/test/TestBinaries/DotNet/TheAnswer/TheAnswer.csproj b/test/TestBinaries/DotNet/TheAnswer/TheAnswer.csproj
index 02d35264b..e15f8ab2e 100644
--- a/test/TestBinaries/DotNet/TheAnswer/TheAnswer.csproj
+++ b/test/TestBinaries/DotNet/TheAnswer/TheAnswer.csproj
@@ -2,7 +2,7 @@
Exe
- net47;netcoreapp3.1
+ net472;netcoreapp3.1
diff --git a/tools/Directory.Build.props b/tools/Directory.Build.props
new file mode 100644
index 000000000..f28236508
--- /dev/null
+++ b/tools/Directory.Build.props
@@ -0,0 +1,10 @@
+
+
+
+
+
+ $(MSBuildThisFileDirectory)..\artifacts\tools
+ false
+
+
+