Merge branch 'PCSX2:master' into master

This commit is contained in:
Hallkezz 2024-12-19 20:01:32 +03:00 committed by GitHub
commit 46925c15e9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2613 changed files with 1223052 additions and 466123 deletions

View File

@ -18,6 +18,8 @@ body:
Please make an effort to make sure your issue isn't already reported. Please make an effort to make sure your issue isn't already reported.
Do not create issues involving software piracy of BIOS or ISO files, our rules specifically prohibit this and your issue will be closed.
### Please Avoid Issues Pertaining to the Following: ### Please Avoid Issues Pertaining to the Following:
- We are **not** accepting bug reports for **PSX mode** at this time. - We are **not** accepting bug reports for **PSX mode** at this time.
- If you are interested in helping contribute to PSX mode please do so on the forums. Otherwise our recommendation is that you use [Duckstation](https://github.com/stenzek/duckstation/releases/tag/latest). - If you are interested in helping contribute to PSX mode please do so on the forums. Otherwise our recommendation is that you use [Duckstation](https://github.com/stenzek/duckstation/releases/tag/latest).
@ -27,6 +29,9 @@ body:
- If your bug is the result of upscaling please use the forums or discord for assistance with various upscaling workarounds. Additionally, the unofficial PCSX2 [Wiki](https://wiki.pcsx2.net/Main_Page) often lists various fixes for upscaling issues. - If your bug is the result of upscaling please use the forums or discord for assistance with various upscaling workarounds. Additionally, the unofficial PCSX2 [Wiki](https://wiki.pcsx2.net/Main_Page) often lists various fixes for upscaling issues.
- We do **not** accept issues relating to Widescreen/no-interlace patches at this time. - We do **not** accept issues relating to Widescreen/no-interlace patches at this time.
- Any issues pertaining to Widescreen/no-interlace patches please forward them to the [patches repository](https://github.com/PCSX2/pcsx2_patches). - Any issues pertaining to Widescreen/no-interlace patches please forward them to the [patches repository](https://github.com/PCSX2/pcsx2_patches).
- We do **not** accept issues pertaining to Linux builds other than the official AppImage and Flatpak.
- Please contact your packager for support. We have no control over other builds, nor can we investigate any issues, and historically they have been known to be broken.
- This includes pre-configured "distributions" such as EmuDeck, the "AUR", etc.
- type: textarea - type: textarea
id: desc id: desc
@ -65,11 +70,10 @@ body:
- Windows 11 - Windows 11
- Windows 10 (64bit) - Windows 10 (64bit)
- Linux (64bit) - Specify distro below - Linux (64bit) - Specify distro below
- macOS 14 (Sonoma)
- macOS 13 (Ventura) - macOS 13 (Ventura)
- macOS 12 (Monterey) - macOS 12 (Monterey)
- macOS 11 (Big Sur) - macOS 11 (Big Sur)
- macOS 10.15 (Catalina)
- macOS 10.14 (Mojave)
validations: validations:
required: true required: true
- type: input - type: input

View File

@ -18,6 +18,10 @@ body:
Please make an effort to make sure your issue isn't already reported. Please make an effort to make sure your issue isn't already reported.
Please make sure your game is verified using the built-in game verifier.
Do not create issues involving software piracy of BIOS or ISO files, our rules specifically prohibit this and your issue will be closed.
### Please Avoid Issues Pertaining to the Following: ### Please Avoid Issues Pertaining to the Following:
- We are **not** accepting bug reports for **PSX mode** at this time. - We are **not** accepting bug reports for **PSX mode** at this time.
- If you are interested in helping contribute to PSX mode please do so on the forums. Otherwise our recommendation is that you use [Duckstation](https://github.com/stenzek/duckstation/releases/tag/latest). - If you are interested in helping contribute to PSX mode please do so on the forums. Otherwise our recommendation is that you use [Duckstation](https://github.com/stenzek/duckstation/releases/tag/latest).
@ -27,6 +31,9 @@ body:
- If your bug is the result of upscaling please use the forums or discord for assistance with various upscaling workarounds. Additionally, the unofficial PCSX2 [Wiki](https://wiki.pcsx2.net/Main_Page) often lists various fixes for upscaling issues. - If your bug is the result of upscaling please use the forums or discord for assistance with various upscaling workarounds. Additionally, the unofficial PCSX2 [Wiki](https://wiki.pcsx2.net/Main_Page) often lists various fixes for upscaling issues.
- We do **not** accept issues relating to Widescreen/no-interlace patches at this time. - We do **not** accept issues relating to Widescreen/no-interlace patches at this time.
- Any issues pertaining to Widescreen/no-interlace patches please forward them to the [patches repository](https://github.com/PCSX2/pcsx2_patches). - Any issues pertaining to Widescreen/no-interlace patches please forward them to the [patches repository](https://github.com/PCSX2/pcsx2_patches).
- We do **not** accept issues pertaining to Linux builds other than the official AppImage and Flatpak.
- Please contact your packager for support. We have no control over other builds, nor can we investigate any issues, and historically they have been known to be broken.
- This includes pre-configured "distributions" such as EmuDeck, the "AUR", etc.
- type: textarea - type: textarea
id: desc id: desc
@ -80,11 +87,10 @@ body:
- Windows 11 - Windows 11
- Windows 10 (64bit) - Windows 10 (64bit)
- Linux (64bit) - Specify distro below - Linux (64bit) - Specify distro below
- macOS 14 (Sonoma)
- macOS 13 (Ventura) - macOS 13 (Ventura)
- macOS 12 (Monterey) - macOS 12 (Monterey)
- macOS 11 (Big Sur) - macOS 11 (Big Sur)
- macOS 10.15 (Catalina)
- macOS 10.14 (Mojave)
validations: validations:
required: true required: true
- type: input - type: input
@ -122,9 +128,9 @@ body:
description: | description: |
Any non-default core settings. If you don't want to list them out, please provide screenshots of your configuration window. Any non-default core settings. If you don't want to list them out, please provide screenshots of your configuration window.
Please note that the safe preset works for most games. MTVU can have some compatibility issues so please disable it before making a report. Please note that defaults tend to work best.
If you need to modify the settings manually because a game requires you to do so to work, please state that explicitly. If you need to modify the settings manually to fix an issue , please state that explicitly.
validations: validations:
required: false required: false
- type: textarea - type: textarea

View File

@ -2,10 +2,19 @@
name: Feature request name: Feature request
description: Suggest a new feature or improve an existing one description: Suggest a new feature or improve an existing one
title: "[Feature Request]: " title: "[Feature Request]: "
labels: ["Enhancement / Feature Request"] labels: ["Enhancement / Feature Request", "FR: Awaiting Consideration"]
# assignees: # assignees:
# - octocat # - octocat
body: body:
- type: markdown
attributes:
value: |
## Important: Read First
Please make an effort to make sure your issue isn't already reported.
Do not create issues involving software piracy of BIOS or ISO files, our rules specifically prohibit this and your issue will be closed.
- type: textarea - type: textarea
id: desc id: desc
attributes: attributes:

View File

@ -6,3 +6,7 @@ updates:
schedule: schedule:
# Check for updates to GitHub Actions every week # Check for updates to GitHub Actions every week
interval: "weekly" interval: "weekly"
groups:
ci-deps:
patterns:
- "*"

234
.github/labeler.yml vendored
View File

@ -2,127 +2,187 @@
# General Labels # General Labels
'Build | Project System': 'Build | Project System':
- '.github/*' - changed-files:
- '.github/**/*' - any-glob-to-any-file:
- '*.sln' - '.github/*'
- '**/*.sln' - '.github/**/*'
- '*.vcxproj*' - '*.sln'
- '**/*.vcxproj*' - '**/*.sln'
- 'cmake/*' - '*.vcxproj*'
- 'cmake/**/*' - '**/*.vcxproj*'
- 'CMakeLists.txt' - 'cmake/*'
- '**/CMakeLists.txt' - 'cmake/**/*'
- 'build.sh' - 'CMakeLists.txt'
- '**/CMakeLists.txt'
- 'build.sh'
'Dependencies': 'Dependencies':
- '3rdparty/*' - changed-files:
- '3rdparty/**/*' - any-glob-to-any-file:
- '**/3rdpartyDeps.props' - '3rdparty/*'
- '.gitmodules' - '3rdparty/**/*'
- '**/3rdpartyDeps.props'
- '.gitmodules'
'Documentation': 'Documentation':
- '*.md' - changed-files:
- '**/*.md' - any-glob-to-any-file:
- '*.pdf' - '*.md'
- '**/*.pdf' - '**/*.md'
- '*.pdf'
- '**/*.pdf'
'GUI/Qt': 'GUI/Qt':
- 'pcsx2-qt/*' - changed-files:
- 'pcsx2-qt/**/*' - any-glob-to-any-file:
- '3rdparty/Qt/*' - 'pcsx2-qt/*'
- '3rdparty/Qt/**/*' - 'pcsx2-qt/**/*'
- '3rdparty/Qt/*'
- '3rdparty/Qt/**/*'
'GameDB': 'GameDB':
- '**/GameIndex.*' - changed-files:
- any-glob-to-any-file:
- '**/GameIndex.*'
'Installer | Package': 'Installer | Package':
- 'build.sh' - changed-files:
- any-glob-to-any-file:
- 'build.sh'
'Translations': 'Translations':
- 'pcsx2-qt/Translations/*' - changed-files:
- any-glob-to-any-file:
- 'pcsx2-qt/Translations/*'
# Tools / Features # Tools / Features
'Debugger': 'Debugger':
- 'pcsx2/DebugTools/*' - changed-files:
- 'pcsx2/DebugTools/**/*' - any-glob-to-any-file:
- 'pcsx2-qt/Debugger/*' - 'pcsx2/DebugTools/*'
- 'pcsx2-qt/Debugger/**/*' - 'pcsx2/DebugTools/**/*'
- 'pcsx2-qt/Debugger/*'
- 'pcsx2-qt/Debugger/**/*'
'IPC': 'IPC':
- 'pcsx2/IPC*' - changed-files:
- 'pcsx2/**/IPC*' - any-glob-to-any-file:
- 'pcsx2/IPC*'
- 'pcsx2/**/IPC*'
'TAS Functionality': 'TAS Functionality':
- 'pcsx2/Recording/*' - changed-files:
- 'pcsx2/Recording/**/*' - any-glob-to-any-file:
- 'pcsx2/Recording/*'
- 'pcsx2/Recording/**/*'
'RetroAchievements': 'RetroAchievements':
- 'pcsx2/Frontend/Achievements.*' - changed-files:
- 'pcsx2/Achievements.*' - any-glob-to-any-file:
- 'pcsx2/Frontend/Achievements.*'
- 'pcsx2/Achievements.*'
# Emulation Components # Emulation Components
'Counters': 'Counters':
- 'pcsx2/Counters.*' - changed-files:
- any-glob-to-any-file:
- 'pcsx2/Counters.*'
'Vector Units': 'Vector Units':
- 'pcsx2/VU*' - changed-files:
- 'pcsx2/**/VU*' - any-glob-to-any-file:
- 'pcsx2/*VU*' - 'pcsx2/VU*'
- 'pcsx2/**/*VU*' - 'pcsx2/**/VU*'
- 'pcsx2/*VU*'
- 'pcsx2/**/*VU*'
'VIF': 'VIF':
- 'pcsx2/Vif*' - changed-files:
- 'pcsx2/**/Vif*' - any-glob-to-any-file:
- 'pcsx2/VIF*' - 'pcsx2/Vif*'
- 'pcsx2/**/VIF*' - 'pcsx2/**/Vif*'
- 'pcsx2/VIF*'
- 'pcsx2/**/VIF*'
# GS Related Labels # GS Related Labels
'GS': 'GS':
- 'pcsx2/GS/*' - changed-files:
- 'pcsx2/GS/**/*' - any-glob-to-any-file:
- 'bin/resources/shaders/*' - 'pcsx2/GS/*'
- 'bin/resources/shaders/**/*' - 'pcsx2/GS/**/*'
- 'bin/resources/shaders/*'
- 'bin/resources/shaders/**/*'
'GS: Direct3D 11': 'GS: Direct3D 11':
- 'pcsx2/GS/Renderers/DX11/*' - changed-files:
- 'pcsx2/GS/Renderers/DX11/**/*' - any-glob-to-any-file:
- 'bin/resources/shaders/dx11/*' - 'pcsx2/GS/Renderers/DX11/*'
- 'bin/resources/shaders/dx11/**/*' - 'pcsx2/GS/Renderers/DX11/**/*'
- 'bin/resources/shaders/dx11/*'
- 'bin/resources/shaders/dx11/**/*'
'GS: Direct3D 12': 'GS: Direct3D 12':
- 'pcsx2/GS/Renderers/DX12/*' - changed-files:
- 'pcsx2/GS/Renderers/DX12/**/*' - any-glob-to-any-file:
- 'bin/resources/shaders/dx11/*' - 'pcsx2/GS/Renderers/DX12/*'
- 'bin/resources/shaders/dx11/**/*' - 'pcsx2/GS/Renderers/DX12/**/*'
- 'bin/resources/shaders/dx11/*'
- 'bin/resources/shaders/dx11/**/*'
'GS: Hardware': 'GS: Hardware':
- 'pcsx2/GS/Renderers/HW/*' - changed-files:
- 'pcsx2/GS/Renderers/HW/**/*' - any-glob-to-any-file:
- 'pcsx2/GS/Renderers/HW/*'
- 'pcsx2/GS/Renderers/HW/**/*'
'GS: OpenGL': 'GS: OpenGL':
- 'pcsx2/GS/Renderers/OpenGL/*' - changed-files:
- 'pcsx2/GS/Renderers/OpenGL/**/*' - any-glob-to-any-file:
- 'bin/resources/shaders/opengl/*' - 'pcsx2/GS/Renderers/OpenGL/*'
- 'bin/resources/shaders/opengl/**/*' - 'pcsx2/GS/Renderers/OpenGL/**/*'
- 'bin/resources/shaders/opengl/*'
- 'bin/resources/shaders/opengl/**/*'
'GS: Vulkan': 'GS: Vulkan':
- 'pcsx2/GS/Renderers/Vulkan/*' - changed-files:
- 'pcsx2/GS/Renderers/Vulkan/**/*' - any-glob-to-any-file:
- 'bin/resources/shaders/vulkan/*' - 'pcsx2/GS/Renderers/Vulkan/*'
- 'bin/resources/shaders/vulkan/**/*' - 'pcsx2/GS/Renderers/Vulkan/**/*'
- 'bin/resources/shaders/vulkan/*'
- 'bin/resources/shaders/vulkan/**/*'
'GS: Metal': 'GS: Metal':
- 'pcsx2/GS/Renderers/Metal/*' - changed-files:
- 'pcsx2/GS/Renderers/Metal/**/*' - any-glob-to-any-file:
- 'pcsx2/GS/Renderers/Metal/*'
- 'pcsx2/GS/Renderers/Metal/**/*'
'GS: Texture Cache': 'GS: Texture Cache':
- 'pcsx2/GS/Renderers/*TextureCache*.*' - changed-files:
- 'pcsx2/GS/Renderers/**/*TextureCache*.*' - any-glob-to-any-file:
- 'pcsx2/GS/Renderers/*TextureCache*.*'
- 'pcsx2/GS/Renderers/**/*TextureCache*.*'
'GS: Software': 'GS: Software':
- 'pcsx2/GS/Renderers/SW/*' - changed-files:
- 'pcsx2/GS/Renderers/SW/**/*' - any-glob-to-any-file:
- 'pcsx2/GS/Renderers/SW/*'
- 'pcsx2/GS/Renderers/SW/**/*'
# Other Core Components # Other Core Components
'CDVD': 'CDVD':
- 'pcsx2/CDVD/*' - changed-files:
- 'pcsx2/CDVD/**/*' - any-glob-to-any-file:
- 'pcsx2/CDVD/*'
- 'pcsx2/CDVD/**/*'
'DEV9': 'DEV9':
- 'pcsx2/DEV9/*' - changed-files:
- 'pcsx2/DEV9/**/*' - any-glob-to-any-file:
- 'pcsx2/DEV9/*'
- 'pcsx2/DEV9/**/*'
'IPU': 'IPU':
- 'pcsx2/IPU/*' - changed-files:
- 'pcsx2/IPU/**/*' - any-glob-to-any-file:
- 'pcsx2/IPU/*'
- 'pcsx2/IPU/**/*'
'Memory Card': 'Memory Card':
- 'pcsx2/SIO/Memcard/*' - changed-files:
- 'pcsx2/SIO/Memcard/**/*' - any-glob-to-any-file:
- 'pcsx2/SIO/Memcard/*'
- 'pcsx2/SIO/Memcard/**/*'
'PAD': 'PAD':
- 'pcsx2/SIO/Pad/*' - changed-files:
- 'pcsx2/SIO/Pad/**/*' - any-glob-to-any-file:
- 'pcsx2/SIO/Pad/*'
- 'pcsx2/SIO/Pad/**/*'
'SPU2': 'SPU2':
- 'pcsx2/SPU2/*' - changed-files:
- 'pcsx2/SPU2/**/*' - any-glob-to-any-file:
- 'pcsx2/SPU2/*'
- 'pcsx2/SPU2/**/*'
'USB': 'USB':
- 'pcsx2/USB/*' - changed-files:
- 'pcsx2/USB/**/*' - any-glob-to-any-file:
- 'pcsx2/USB/*'
- 'pcsx2/USB/**/*'

View File

@ -23,20 +23,27 @@ jobs:
FLATHUB_RELEASE=$(curl -L -s https://flathub.org/api/v2/appstream/net.pcsx2.PCSX2 | jq -r '.releases | max_by(.version) | .version') FLATHUB_RELEASE=$(curl -L -s https://flathub.org/api/v2/appstream/net.pcsx2.PCSX2 | jq -r '.releases | max_by(.version) | .version')
echo "Latest PCSX2 release is: '${PCSX2_RELEASE}'" echo "Latest PCSX2 release is: '${PCSX2_RELEASE}'"
echo "Latest Flathub release is: '${FLATHUB_RELEASE}'" echo "Latest Flathub release is: '${FLATHUB_RELEASE}'"
PCSX2_RELEASE=$(echo $PCSX2_RELEASE | sed 's/[^0-9]*//g')
FLATHUB_RELEASE=$(echo $FLATHUB_RELEASE | sed 's/[^0-9]*//g')
echo "PCSX2_RELEASE=${PCSX2_RELEASE}" >> "$GITHUB_OUTPUT" echo "PCSX2_RELEASE=${PCSX2_RELEASE}" >> "$GITHUB_OUTPUT"
echo "FLATHUB_RELEASE=${FLATHUB_RELEASE}" >> "$GITHUB_OUTPUT" echo "FLATHUB_RELEASE=${FLATHUB_RELEASE}" >> "$GITHUB_OUTPUT"
build: build:
needs: check needs: check
if: needs.check.outputs.FLATHUB_RELEASE < needs.check.outputs.PCSX2_RELEASE # outputs are automatically compared as strings. This doesn't work in our favour
# Use fromJson() to convert them to proper integers...
# see: https://github.com/github/docs/pull/25870
# and: https://github.com/orgs/community/discussions/57480
#if: fromJson(needs.check.outputs.FLATHUB_RELEASE) < fromJson(needs.check.outputs.PCSX2_RELEASE)
name: "Build and publish Flatpak" name: "Build and publish Flatpak"
uses: ./.github/workflows/linux_build_flatpak.yml uses: ./.github/workflows/linux_build_flatpak.yml
with: with:
jobName: "Qt" jobName: "Qt"
artifactPrefixName: "PCSX2-linux-Qt-x64-flatpak"
compiler: clang compiler: clang
cmakeflags: "" cmakeflags: ""
publish: true publish: true
branch: stable
fetchTags: true fetchTags: true
stableBuild: false
secrets: inherit secrets: inherit

View File

@ -17,10 +17,10 @@ jobs:
run: ./.github/workflows/scripts/common/update_base_translation.sh run: ./.github/workflows/scripts/common/update_base_translation.sh
- name: Create Pull Request - name: Create Pull Request
uses: peter-evans/create-pull-request@v5 uses: peter-evans/create-pull-request@v7
with: with:
title: "Qt: Update Base Translation" title: "Qt: Update Base Translation"
commit-message: "Qt: Update Base Translation" commit-message: "[ci skip] Qt: Update Base Translation."
committer: "PCSX2 Bot <PCSX2Bot@users.noreply.github.com>" committer: "PCSX2 Bot <PCSX2Bot@users.noreply.github.com>"
author: "PCSX2 Bot <PCSX2Bot@users.noreply.github.com>" author: "PCSX2 Bot <PCSX2Bot@users.noreply.github.com>"
body: "Daily update of base translation sources." body: "Daily update of base translation sources."

View File

@ -19,11 +19,12 @@ jobs:
mv ./game_controller_db.txt ${{github.workspace}}/bin/resources/game_controller_db.txt mv ./game_controller_db.txt ${{github.workspace}}/bin/resources/game_controller_db.txt
- name: Create Pull Request - name: Create Pull Request
uses: peter-evans/create-pull-request@v5 uses: peter-evans/create-pull-request@v7
with: with:
title: "PAD: Update to latest controller database" title: "PAD: Update to latest controller database"
commit-message: "PAD: Update to latest controller database." commit-message: "[ci skip] PAD: Update to latest controller database."
committer: "PCSX2 Bot <PCSX2Bot@users.noreply.github.com>" committer: "PCSX2 Bot <PCSX2Bot@users.noreply.github.com>"
author: "PCSX2 Bot <PCSX2Bot@users.noreply.github.com>" author: "PCSX2 Bot <PCSX2Bot@users.noreply.github.com>"
body: "Weekly automatic update of SDL Controller DB" body: "Weekly automatic update of SDL Controller DB."
reviewers: lightningterror branch: update-controller-db
delete-branch: true

View File

@ -20,12 +20,24 @@ jobs:
- name: Checkout Repository - name: Checkout Repository
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Install Packages
run: |
npm install -g ajv-cli prettier
sudo apt-get -y install yamllint
- name: Validate YAML
run: |
yamllint -sd "{extends: relaxed, rules: {line-length: disable}}" ./bin/resources/GameIndex.yaml
- name: Validate Contents - name: Validate Contents
run: | run: |
npm install -g ajv-cli
ajv validate -s ./pcsx2/Docs/gamedb-schema.json --spec=draft2020 -d ./bin/resources/GameIndex.yaml ajv validate -s ./pcsx2/Docs/gamedb-schema.json --spec=draft2020 -d ./bin/resources/GameIndex.yaml
- name: Check Formatting - name: Check Formatting
run: | run: |
npm install -g prettier if ! prettier --check bin/resources/GameIndex.yaml; then
prettier --check ./bin/resources/GameIndex.yaml prettier --write bin/resources/GameIndex.yaml
echo "Prettier failed, diff:"
git diff bin/resources/GameIndex.yaml
exit 1
fi

View File

@ -6,6 +6,9 @@ on:
jobName: jobName:
required: true required: true
type: string type: string
artifactPrefixName:
required: true
type: string
os: os:
required: false required: false
type: string type: string
@ -20,10 +23,6 @@ on:
cmakeflags: cmakeflags:
required: true required: true
type: string type: string
branch:
required: false
type: string
default: "stable"
publish: publish:
required: false required: false
type: boolean type: boolean
@ -36,18 +35,30 @@ on:
required: false required: false
type: boolean type: boolean
default: false default: false
stableBuild:
required: false
type: boolean
default: false
jobs: jobs:
build_linux: build_linux:
name: ${{ inputs.jobName }} name: ${{ inputs.jobName }}
runs-on: ${{ inputs.os }} runs-on: ${{ inputs.os }}
container:
image: ghcr.io/flathub-infra/flatpak-github-actions:kde-6.7
options: --privileged
timeout-minutes: 60 timeout-minutes: 60
steps: steps:
- name: Checkout Repository - name: Checkout Repository
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
submodules: recursive set-safe-directory: ${{ env.GITHUB_WORKSPACE }}
# Work around container ownership issue
- name: Set Safe Directory
shell: bash
run: git config --global --add safe.directory "*"
# Hackity hack. When running the workflow on a schedule, we don't have the tag, # Hackity hack. When running the workflow on a schedule, we don't have the tag,
# it doesn't fetch tags, therefore we don't get a version. So grab them manually. # it doesn't fetch tags, therefore we don't get a version. So grab them manually.
@ -56,66 +67,97 @@ jobs:
if: ${{ inputs.fetchTags }} if: ${{ inputs.fetchTags }}
run: git fetch --tags --no-recurse-submodules run: git fetch --tags --no-recurse-submodules
- name: Add stable release identifier file
if: ${{ inputs.stableBuild == true || inputs.stableBuild == 'true' }}
shell: bash
run: |
echo "#define DEFAULT_UPDATER_CHANNEL \"stable\"" > ./pcsx2-qt/DefaultUpdaterChannel.h
cat ./pcsx2-qt/DefaultUpdaterChannel.h
- name: Prepare Artifact Metadata - name: Prepare Artifact Metadata
id: artifact-metadata id: artifact-metadata
shell: bash shell: bash
env: env:
OS: linux PREFIX: ${{ inputs.artifactPrefixName }}
BUILD_SYSTEM: flatpak
ARCH: ${{ inputs.platform }}
EVENT_NAME: ${{ github.event_name }} EVENT_NAME: ${{ github.event_name }}
PR_TITLE: ${{ github.event.pull_request.title }} PR_TITLE: ${{ github.event.pull_request.title }}
PR_NUM: ${{ github.event.pull_request.number }} PR_NUM: ${{ github.event.pull_request.number }}
PR_SHA: ${{ github.event.pull_request.head.sha }} PR_SHA: ${{ github.event.pull_request.head.sha }}
run: ./.github/workflows/scripts/common/name-artifacts.sh run: ./.github/workflows/scripts/common/name-artifacts.sh
- name: Install Packages
env:
COMPILER: ${{ inputs.compiler }}
run: .github/workflows/scripts/linux/install-packages-flatpak.sh
- name: Download patches - name: Download patches
run: | run: |
cd bin/resources cd bin/resources
aria2c -Z "${{ inputs.patchesUrl }}/patches.zip" wget "${{ inputs.patchesUrl }}/patches.zip"
- name: Generate AppStream XML - name: Validate manifest
run: | run: |
./.github/workflows/scripts/linux/generate-metainfo.sh .github/workflows/scripts/linux/flatpak/net.pcsx2.PCSX2.metainfo.xml flatpak-builder-lint manifest .github/workflows/scripts/linux/flatpak/net.pcsx2.PCSX2.json
cat .github/workflows/scripts/linux/flatpak/net.pcsx2.PCSX2.metainfo.xml
flatpak run org.freedesktop.appstream-glib validate .github/workflows/scripts/linux/flatpak/net.pcsx2.PCSX2.metainfo.xml
- name: Build Flatpak - name: Build Flatpak (beta)
uses: flatpak/flatpak-github-actions/flatpak-builder@v6.2 if: ${{ inputs.stableBuild == false || inputs.stableBuild == 'false' }}
uses: flathub-infra/flatpak-github-actions/flatpak-builder@23796715b3dfa4c86ddf50cf29c3cc8b3c82dca8
with: with:
bundle: ${{ steps.artifact-metadata.outputs.artifact-name }}.flatpak bundle: ${{ steps.artifact-metadata.outputs.artifact-name }}.flatpak
upload-artifact: false
manifest-path: .github/workflows/scripts/linux/flatpak/net.pcsx2.PCSX2.json manifest-path: .github/workflows/scripts/linux/flatpak/net.pcsx2.PCSX2.json
arch: x86_64 arch: x86_64
build-bundle: true build-bundle: true
verbose: true verbose: true
mirror-screenshots-url: https://dl.flathub.org/repo/screenshots mirror-screenshots-url: https://dl.flathub.org/media
branch: ${{ inputs.branch }} branch: beta
cache: true
restore-cache: true
cache-key: ${{ inputs.os }} ${{ inputs.platform }} ${{ inputs.compiler }} flatpak ${{ hashFiles('.github/workflows/scripts/linux/flatpak/**/*.json', '.github/workflows/scripts/common/*.patch') }}
- name: Build Flatpak (stable)
if: ${{ inputs.stableBuild == true || inputs.stableBuild == 'true' }}
uses: flathub-infra/flatpak-github-actions/flatpak-builder@23796715b3dfa4c86ddf50cf29c3cc8b3c82dca8
with:
bundle: ${{ steps.artifact-metadata.outputs.artifact-name }}.flatpak
upload-artifact: false
manifest-path: .github/workflows/scripts/linux/flatpak/net.pcsx2.PCSX2.json
arch: x86_64
build-bundle: true
verbose: true
mirror-screenshots-url: https://dl.flathub.org/media
branch: stable
cache: true cache: true
restore-cache: true restore-cache: true
cache-key: ${{ inputs.os }} ${{ inputs.platform }} ${{ inputs.compiler }} flatpak ${{ hashFiles('.github/workflows/scripts/linux/flatpak/**/*.json') }} cache-key: ${{ inputs.os }} ${{ inputs.platform }} ${{ inputs.compiler }} flatpak ${{ hashFiles('.github/workflows/scripts/linux/flatpak/**/*.json') }}
- name: Commit screenshots to OSTree #- name: Validate build
run: | # run: |
ostree commit --repo=repo --canonical-permissions --branch=screenshots/x86_64 flatpak_app/screenshots # flatpak-builder-lint repo repo
- name: Push to Flathub beta - name: Push to Flathub (beta)
if: inputs.publish == true && inputs.branch == 'beta' if: ${{ inputs.publish == true && (inputs.stableBuild == false || inputs.stableBuild == 'false') }}
uses: flatpak/flatpak-github-actions/flat-manager@v6.2 uses: flathub-infra/flatpak-github-actions/flat-manager@23796715b3dfa4c86ddf50cf29c3cc8b3c82dca8
with: with:
flat-manager-url: https://hub.flathub.org/ flat-manager-url: https://hub.flathub.org/
repository: beta repository: beta
token: ${{ secrets.FLATHUB_BETA_TOKEN }} token: ${{ secrets.FLATHUB_BETA_TOKEN }}
build-log-url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
- name: Push to Flathub stable - name: Push to Flathub (stable)
if: inputs.publish == true && inputs.branch == 'stable' if: ${{ inputs.publish == true && (inputs.stableBuild == true || inputs.stableBuild == 'true') }}
uses: flatpak/flatpak-github-actions/flat-manager@v6.2 uses: flathub-infra/flatpak-github-actions/flat-manager@23796715b3dfa4c86ddf50cf29c3cc8b3c82dca8
with: with:
flat-manager-url: https://hub.flathub.org/ flat-manager-url: https://hub.flathub.org/
repository: stable repository: stable
token: ${{ secrets.FLATHUB_TOKEN }} token: ${{ secrets.FLATHUB_TOKEN }}
build-log-url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
# NOTE - this is done after on purpose so the flatpak file is wherever it needs to be for the previous pushes
- name: Prepare artifacts folder
# NOTE - 'flatpak-builder' dumps the artifact out into the current directory
run: |
mkdir -p "$GITHUB_WORKSPACE"/ci-artifacts/
mv "./${{ steps.artifact-metadata.outputs.artifact-name }}.flatpak" "$GITHUB_WORKSPACE"/ci-artifacts/
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: ${{ steps.artifact-metadata.outputs.artifact-name }}
path: ci-artifacts

View File

@ -15,6 +15,7 @@ jobs:
uses: ./.github/workflows/linux_build_qt.yml uses: ./.github/workflows/linux_build_qt.yml
with: with:
jobName: "AppImage Build" jobName: "AppImage Build"
artifactPrefixName: "PCSX2-linux-Qt-x64-appimage-sse4"
compiler: clang compiler: clang
cmakeflags: "" cmakeflags: ""
buildAppImage: true buildAppImage: true
@ -25,6 +26,7 @@ jobs:
uses: ./.github/workflows/linux_build_flatpak.yml uses: ./.github/workflows/linux_build_flatpak.yml
with: with:
jobName: "Flatpak Build" jobName: "Flatpak Build"
artifactPrefixName: "PCSX2-linux-Qt-x64-flatpak-sse4"
compiler: clang compiler: clang
cmakeflags: "" cmakeflags: ""
publish: false publish: false

View File

@ -6,6 +6,9 @@ on:
jobName: jobName:
required: true required: true
type: string type: string
artifactPrefixName:
required: true
type: string
os: os:
required: false required: false
type: string type: string
@ -36,13 +39,17 @@ on:
required: false required: false
type: boolean type: boolean
default: false default: false
stableBuild:
required: false
type: boolean
default: false
jobs: jobs:
build_linux: build_linux:
name: ${{ inputs.jobName }} name: ${{ inputs.jobName }}
runs-on: ${{ inputs.os }} runs-on: ${{ inputs.os }}
# Set some sort of timeout in the event of run-away builds. We are limited on concurrent jobs so, get rid of them. # Set some sort of timeout in the event of run-away builds. We are limited on concurrent jobs so, get rid of them.
timeout-minutes: 60 timeout-minutes: 90
env: env:
CCACHE_BASEDIR: ${{ github.workspace }} CCACHE_BASEDIR: ${{ github.workspace }}
CCACHE_DIR: ${{ github.workspace }}/.ccache CCACHE_DIR: ${{ github.workspace }}/.ccache
@ -54,19 +61,25 @@ jobs:
- name: Checkout Repository - name: Checkout Repository
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
submodules: recursive fetch-depth: 0
# actions/checkout elides tags, fetch them primarily for releases # actions/checkout elides tags, fetch them primarily for releases
- name: Fetch Tags - name: Fetch Tags
if: ${{ inputs.fetchTags }} if: ${{ inputs.fetchTags }}
run: git fetch --tags --no-recurse-submodules run: git fetch --tags --no-recurse-submodules
- name: Add stable release identifier file
if: ${{ inputs.stableBuild == true || inputs.stableBuild == 'true' }}
shell: bash
run: |
echo "#define DEFAULT_UPDATER_CHANNEL \"stable\"" > ./pcsx2-qt/DefaultUpdaterChannel.h
cat ./pcsx2-qt/DefaultUpdaterChannel.h
- name: Prepare Artifact Metadata - name: Prepare Artifact Metadata
id: artifact-metadata id: artifact-metadata
shell: bash shell: bash
env: env:
OS: linux PREFIX: ${{ inputs.artifactPrefixName }}
ARCH: ${{ inputs.platform }}
EVENT_NAME: ${{ github.event_name }} EVENT_NAME: ${{ github.event_name }}
PR_TITLE: ${{ github.event.pull_request.title }} PR_TITLE: ${{ github.event.pull_request.title }}
PR_NUM: ${{ github.event.pull_request.number }} PR_NUM: ${{ github.event.pull_request.number }}
@ -79,27 +92,36 @@ jobs:
run: echo "timestamp=$(date -u "+%Y-%m-%d-%H;%M;%S")" >> $GITHUB_OUTPUT run: echo "timestamp=$(date -u "+%Y-%m-%d-%H;%M;%S")" >> $GITHUB_OUTPUT
- name: ccache cache files - name: ccache cache files
uses: actions/cache@v3 uses: actions/cache@v4
with: with:
path: .ccache path: .ccache
key: ${{ inputs.os }} ${{ inputs.platform }} ${{ inputs.compiler }} ${{ inputs.detail }} ccache ${{ steps.ccache_cache_timestamp.outputs.timestamp }} key: ${{ inputs.os }} ${{ inputs.platform }} ${{ inputs.compiler }} ${{ inputs.detail }} ccache ${{ steps.ccache_cache_timestamp.outputs.timestamp }}
restore-keys: ${{ inputs.os }} ${{ inputs.platform }} ${{ inputs.compiler }} ${{ inputs.detail }} ccache restore-keys: ${{ inputs.os }} ${{ inputs.platform }} ${{ inputs.compiler }} ${{ inputs.detail }} ccache
- name: Install Packages - name: Install Packages
env: run: |
COMPILER: ${{ inputs.compiler }} tools/retry.sh wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc
run: .github/workflows/scripts/linux/install-packages-qt.sh sudo tools/retry.sh apt-add-repository -n 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-17 main'
sudo tools/retry.sh apt-get update
sudo tools/retry.sh apt-get -y install \
build-essential ccache clang-17 cmake curl extra-cmake-modules git libasound2-dev libaio-dev libavcodec-dev libavformat-dev libavutil-dev \
libcurl4-openssl-dev libdbus-1-dev libdecor-0-dev libegl-dev libevdev-dev libfontconfig-dev libfreetype-dev libfuse2 libgtk-3-dev libgudev-1.0-dev \
libharfbuzz-dev libinput-dev libopengl-dev libpcap-dev libpipewire-0.3-dev libpulse-dev libssl-dev libswresample-dev libswscale-dev libudev-dev \
libwayland-dev libx11-dev libx11-xcb-dev libxcb1-dev libxcb-composite0-dev libxcb-cursor-dev libxcb-damage0-dev libxcb-glx0-dev libxcb-icccm4-dev \
libxcb-image0-dev libxcb-keysyms1-dev libxcb-present-dev libxcb-randr0-dev libxcb-render0-dev libxcb-render-util0-dev libxcb-shape0-dev \
libxcb-shm0-dev libxcb-sync-dev libxcb-util-dev libxcb-xfixes0-dev libxcb-xinput-dev libxcb-xkb-dev libxext-dev libxkbcommon-x11-dev libxrandr-dev \
lld-17 llvm-17 ninja-build patchelf pkg-config zlib1g-dev
- name: Cache Dependencies - name: Cache Dependencies
id: cache-deps id: cache-deps
uses: actions/cache@v3 uses: actions/cache@v4
with: with:
path: ~/deps path: ~/deps
key: ${{ inputs.os }} ${{ inputs.platform }} deps ${{ hashFiles('.github/workflows/scripts/linux/build-dependencies-qt.sh') }} key: ${{ inputs.os }} ${{ inputs.platform }} deps ${{ hashFiles('.github/workflows/scripts/linux/build-dependencies-qt.sh', '.github/workflows/scripts/common/*.patch') }}
- name: Build Dependencies - name: Build Dependencies
if: steps.cache-deps.outputs.cache-hit != 'true' if: steps.cache-deps.outputs.cache-hit != 'true'
run: .github/workflows/scripts/linux/build-dependencies-qt.sh run: .github/workflows/scripts/linux/build-dependencies-qt.sh "$HOME/deps"
- name: Download patches - name: Download patches
run: | run: |
@ -108,16 +130,31 @@ jobs:
- name: Generate CMake - name: Generate CMake
env: env:
COMPILER: ${{ inputs.compiler }}
ADDITIONAL_CMAKE_ARGS: ${{ inputs.cmakeflags }} ADDITIONAL_CMAKE_ARGS: ${{ inputs.cmakeflags }}
CLANG_PATH: /usr/bin/clang-16
CLANGXX_PATH: /usr/bin/clang++-16
run: | run: |
DEPS_PREFIX="$HOME/deps" .github/workflows/scripts/linux/generate-cmake-qt.sh cmake -B build -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON \
-DCMAKE_PREFIX_PATH="$HOME/deps" \
-DCMAKE_C_COMPILER=clang-17 \
-DCMAKE_CXX_COMPILER=clang++-17 \
-DCMAKE_EXE_LINKER_FLAGS_INIT="-fuse-ld=lld" \
-DCMAKE_MODULE_LINKER_FLAGS_INIT="-fuse-ld=lld" \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DENABLE_SETCAP=OFF \
-DDISABLE_ADVANCE_SIMD=TRUE \
$ADDITIONAL_CMAKE_ARGS
- name: Build PCSX2 - name: Build PCSX2
working-directory: build working-directory: build
run: ../.github/workflows/scripts/linux/compile.sh run: |
# Prepare the Cache
ccache -p
ccache -z
# Build
ninja
# Save the Cache
ccache -s
- name: Run Tests - name: Run Tests
working-directory: ./build working-directory: ./build
@ -134,7 +171,7 @@ jobs:
- name: Upload artifact - name: Upload artifact
if: inputs.buildAppImage == true if: inputs.buildAppImage == true
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: ${{ steps.artifact-metadata.outputs.artifact-name }} name: ${{ steps.artifact-metadata.outputs.artifact-name }}
path: ci-artifacts path: ci-artifacts

View File

@ -6,10 +6,13 @@ on:
jobName: jobName:
required: true required: true
type: string type: string
artifactPrefixName:
required: true
type: string
os: os:
required: false required: false
type: string type: string
default: macos-13 default: macos-14
patchesUrl: patchesUrl:
required: false required: false
type: string type: string
@ -18,6 +21,10 @@ on:
required: false required: false
type: boolean type: boolean
default: false default: false
stableBuild:
required: false
type: boolean
default: false
jobs: jobs:
build_macos: build_macos:
@ -36,22 +43,27 @@ jobs:
steps: steps:
- name: Checkout Repository - name: Checkout Repository
uses: actions/checkout@v4 uses: actions/checkout@v4
with:
submodules: recursive
# actions/checkout elides tags, fetch them primarily for releases # actions/checkout elides tags, fetch them primarily for releases
- name: Fetch Tags - name: Fetch Tags
if: ${{ inputs.fetchTags }} if: ${{ inputs.fetchTags }}
run: git fetch --tags --no-recurse-submodules run: git fetch --tags --no-recurse-submodules
- name: Use Xcode 14.3.1 - name: Add stable release identifier file
run: sudo xcode-select -s /Applications/Xcode_14.3.1.app if: ${{ inputs.stableBuild == true || inputs.stableBuild == 'true' }}
shell: bash
run: |
echo "#define DEFAULT_UPDATER_CHANNEL \"stable\"" > ./pcsx2-qt/DefaultUpdaterChannel.h
cat ./pcsx2-qt/DefaultUpdaterChannel.h
- name: Use Xcode 15.2
run: sudo xcode-select -s /Applications/Xcode_15.2.app
- name: Prepare Artifact Metadata - name: Prepare Artifact Metadata
id: artifact-metadata id: artifact-metadata
shell: bash shell: bash
env: env:
OS: macos PREFIX: ${{ inputs.artifactPrefixName }}
EVENT_NAME: ${{ github.event_name }} EVENT_NAME: ${{ github.event_name }}
PR_TITLE: ${{ github.event.pull_request.title }} PR_TITLE: ${{ github.event.pull_request.title }}
PR_NUM: ${{ github.event.pull_request.number }} PR_NUM: ${{ github.event.pull_request.number }}
@ -60,28 +72,25 @@ jobs:
- name: Install Packages - name: Install Packages
env: env:
PLATFORM: "x64"
HOMEBREW_NO_INSTALL_CLEANUP: 1 HOMEBREW_NO_INSTALL_CLEANUP: 1
HOMEBREW_NO_ANALYTICS: 1 HOMEBREW_NO_ANALYTICS: 1
run: | run: |
brew unlink libjpeg || true # Conflicts with our self-built dependencies if ! brew install ccache nasm; then
brew unlink libpng || true
# Unlike other packages, brew's MoltenVK build uses MoltenVK's minimum macOS version of 10.13 so we can use it
if ! brew install molten-vk ccache nasm; then
brew update brew update
brew install molten-vk ccache nasm brew install ccache nasm
fi fi
- name: Cache Dependencies - name: Cache Dependencies
id: cache-deps id: cache-deps
uses: actions/cache@v3 uses: actions/cache@v4
with: with:
path: ~/deps path: ~/deps
key: ${{ inputs.os }} deps ${{ hashFiles('.github/workflows/scripts/macos/build-dependencies.sh') }} key: ${{ inputs.os }} deps ${{ hashFiles('.github/workflows/scripts/macos/build-dependencies.sh', '.github/workflows/scripts/common/*.patch') }}
- name: Build Dependencies - name: Build Dependencies
if: steps.cache-deps.outputs.cache-hit != 'true' if: steps.cache-deps.outputs.cache-hit != 'true'
run: .github/workflows/scripts/macos/build-dependencies.sh run: |
.github/workflows/scripts/macos/build-dependencies.sh "$HOME/deps"
- name: Download patches - name: Download patches
run: | run: |
@ -94,7 +103,7 @@ jobs:
run: echo "timestamp=$(date -u "+%Y-%m-%d-%H;%M;%S")" >> $GITHUB_OUTPUT run: echo "timestamp=$(date -u "+%Y-%m-%d-%H;%M;%S")" >> $GITHUB_OUTPUT
- name: Cache ccache cache - name: Cache ccache cache
uses: actions/cache@v3 uses: actions/cache@v4
with: with:
path: .ccache path: .ccache
key: ${{ inputs.os }} ccache ${{ steps.ccache_cache_timestamp.outputs.timestamp }} key: ${{ inputs.os }} ccache ${{ steps.ccache_cache_timestamp.outputs.timestamp }}
@ -104,12 +113,9 @@ jobs:
run: | run: |
cmake -DCMAKE_PREFIX_PATH="$HOME/deps" \ cmake -DCMAKE_PREFIX_PATH="$HOME/deps" \
-DCMAKE_BUILD_TYPE=Release \ -DCMAKE_BUILD_TYPE=Release \
-DUSE_OPENGL=OFF \ -DCMAKE_OSX_ARCHITECTURES="x86_64" \
-DUSE_VULKAN=OFF \
-DDISABLE_ADVANCE_SIMD=ON \ -DDISABLE_ADVANCE_SIMD=ON \
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON \ -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON \
-DUSE_SYSTEM_LIBS=OFF \
-DUSE_SYSTEM_SDL2=ON \
-DUSE_LINKED_FFMPEG=ON \ -DUSE_LINKED_FFMPEG=ON \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \ -DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
@ -138,7 +144,6 @@ jobs:
- name: Prepare Build Artifacts - name: Prepare Build Artifacts
run: | run: |
cp /usr/local/lib/libMoltenVK.dylib build/pcsx2*/PCSX2.app/Contents/Frameworks/
TAG="$(git tag --points-at HEAD)" TAG="$(git tag --points-at HEAD)"
if [ -z "$TAG" ]; then if [ -z "$TAG" ]; then
APPNAME="${{ steps.artifact-metadata.outputs.artifact-name }}" APPNAME="${{ steps.artifact-metadata.outputs.artifact-name }}"
@ -151,7 +156,7 @@ jobs:
cp "${{ steps.artifact-metadata.outputs.artifact-name }}.tar.xz" ci-artifacts/macOS.tar.xz cp "${{ steps.artifact-metadata.outputs.artifact-name }}.tar.xz" ci-artifacts/macOS.tar.xz
- name: Upload Artifact - name: Upload Artifact
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: ${{ steps.artifact-metadata.outputs.artifact-name }} name: ${{ steps.artifact-metadata.outputs.artifact-name }}
path: "*.tar.xz" path: "*.tar.xz"

View File

@ -15,4 +15,5 @@ jobs:
uses: ./.github/workflows/macos_build.yml uses: ./.github/workflows/macos_build.yml
with: with:
jobName: "MacOS Build" jobName: "MacOS Build"
artifactPrefixName: "PCSX2-macos-Qt"
secrets: inherit secrets: inherit

View File

@ -10,16 +10,19 @@ on:
push: push:
branches: branches:
- master - master
# TODO - future work workflow_dispatch:
# workflow_dispatch: inputs:
# inputs: is_prelease:
# isStable: description: 'Should be a pre-release?'
# description: 'Should it be a stable release?' required: true
# required: true default: 'true'
# default: 'false' type: choice
# versionTag: options:
# description: 'The version to tag with' - 'true'
# required: true - 'false'
tag_value:
description: 'Create a new release from latest master with the given tag, if this is left blank it will bump the patch version. You dont need to include the "v" prefix'
required: false
permissions: permissions:
contents: write contents: write
@ -37,11 +40,13 @@ jobs:
# Docs - https://github.com/mathieudutour/github-tag-action # Docs - https://github.com/mathieudutour/github-tag-action
- name: Bump Version and Push Tag - name: Bump Version and Push Tag
id: tag_version id: tag_version
uses: mathieudutour/github-tag-action@v6.1 uses: mathieudutour/github-tag-action@v6.2
with: with:
github_token: ${{ github.token }} github_token: ${{ github.token }}
tag_prefix: v tag_prefix: v
default_bump: patch default_bump: patch
# if set, it will overwrite the bump settings
custom_tag: ${{ github.event.inputs.tag_value == '' && null || github.event.inputs.tag_value }}
# TODO - we could do this and remove the node.js script, but auto-generated notes only work # TODO - we could do this and remove the node.js script, but auto-generated notes only work
# with PRs -- not commits (determine how much we care). # with PRs -- not commits (determine how much we care).
@ -62,9 +67,18 @@ jobs:
node index.js node index.js
mv ./release-notes.md ${GITHUB_WORKSPACE}/release-notes.md mv ./release-notes.md ${GITHUB_WORKSPACE}/release-notes.md
- name: Create a GitHub Release - name: Create a GitHub Release (Manual)
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v2
if: steps.tag_version.outputs.new_tag if: steps.tag_version.outputs.new_tag && github.event_name == 'workflow_dispatch'
with:
body_path: ./release-notes.md
draft: true
prerelease: ${{ github.event_name != 'workflow_dispatch' || inputs.is_prelease == 'true' }}
tag_name: ${{ steps.tag_version.outputs.new_tag }}
- name: Create a GitHub Release (Push)
uses: softprops/action-gh-release@v2
if: steps.tag_version.outputs.new_tag && github.event_name != 'workflow_dispatch'
with: with:
body_path: ./release-notes.md body_path: ./release-notes.md
draft: true draft: true
@ -81,10 +95,12 @@ jobs:
uses: ./.github/workflows/linux_build_qt.yml uses: ./.github/workflows/linux_build_qt.yml
with: with:
jobName: "AppImage Build" jobName: "AppImage Build"
artifactPrefixName: "PCSX2-linux-Qt-x64-appimage"
compiler: clang compiler: clang
cmakeflags: "" cmakeflags: ""
buildAppImage: true buildAppImage: true
fetchTags: true fetchTags: true
stableBuild: ${{ github.event_name == 'workflow_dispatch' && inputs.is_prelease == 'false' }}
secrets: inherit secrets: inherit
build_linux_flatpak: build_linux_flatpak:
@ -95,11 +111,12 @@ jobs:
uses: ./.github/workflows/linux_build_flatpak.yml uses: ./.github/workflows/linux_build_flatpak.yml
with: with:
jobName: "Flatpak Build" jobName: "Flatpak Build"
artifactPrefixName: "PCSX2-linux-Qt-x64-flatpak"
compiler: clang compiler: clang
cmakeflags: "" cmakeflags: ""
branch: "stable"
publish: false publish: false
fetchTags: true fetchTags: true
stableBuild: ${{ github.event_name == 'workflow_dispatch' && inputs.is_prelease == 'false' }}
secrets: inherit secrets: inherit
# Windows # Windows
@ -111,10 +128,12 @@ jobs:
uses: ./.github/workflows/windows_build_qt.yml uses: ./.github/workflows/windows_build_qt.yml
with: with:
jobName: "Windows Build" jobName: "Windows Build"
artifactPrefixName: "PCSX2-windows-Qt-x64"
configuration: CMake configuration: CMake
buildSystem: cmake buildSystem: cmake
cmakeFlags: -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl cmakeFlags: -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl
fetchTags: true fetchTags: true
stableBuild: ${{ github.event_name == 'workflow_dispatch' && inputs.is_prelease == 'false' }}
secrets: inherit secrets: inherit
# MacOS # MacOS
@ -126,7 +145,9 @@ jobs:
uses: ./.github/workflows/macos_build.yml uses: ./.github/workflows/macos_build.yml
with: with:
jobName: "MacOS Build" jobName: "MacOS Build"
artifactPrefixName: "PCSX2-macos-Qt"
fetchTags: true fetchTags: true
stableBuild: ${{ github.event_name == 'workflow_dispatch' && inputs.is_prelease == 'false' }}
secrets: inherit secrets: inherit
# Upload the Artifacts # Upload the Artifacts
@ -146,7 +167,7 @@ jobs:
- name: Prepare Artifact Folder - name: Prepare Artifact Folder
run: mkdir ./ci-artifacts/ run: mkdir ./ci-artifacts/
- uses: actions/download-artifact@v3 - uses: actions/download-artifact@v4
name: Download all Artifacts name: Download all Artifacts
with: with:
path: ./ci-artifacts/ path: ./ci-artifacts/
@ -159,9 +180,6 @@ jobs:
working-directory: ./ci-artifacts/ working-directory: ./ci-artifacts/
run: for d in *windows*/; do 7z a "${d}asset.7z" ./$d/*; done run: for d in *windows*/; do 7z a "${d}asset.7z" ./$d/*; done
# Artifact Naming:
# MacOS: PCSX2-<tag>-macOS-[additional hyphen seperated tags]
# Windows|Linux: PCSX2-<tag>-<windows|linux>-<32bit|64bit>--[additional hyphen seperated tags]
- name: Name and Upload the Release Assets - name: Name and Upload the Release Assets
env: env:
GITHUB_TOKEN: ${{ github.token }} GITHUB_TOKEN: ${{ github.token }}
@ -184,7 +202,7 @@ jobs:
echo "TAG_VAL=${TAG_VAL}" echo "TAG_VAL=${TAG_VAL}"
gh release edit ${TAG_VAL} --draft=false --repo PCSX2/pcsx2 gh release edit ${TAG_VAL} --draft=false --repo PCSX2/pcsx2
- uses: actions/setup-node@v3 - uses: actions/setup-node@v4
with: with:
node-version: 16 node-version: 16

View File

@ -1,39 +1,26 @@
#!/bin/bash #!/bin/bash
# Artifact Naming Scheme: # Artifact Naming Scheme:
# PCSX2-<OS>-Qt-[ARCH]-[SIMD]-[pr\[PR_NUM\]]-[title|sha\[SHA|PR_TITLE\] # PCSX2-<OS>-Qt-[BUILD_SYSTEM]-[ARCH]-[SIMD]-[pr\[PR_NUM\]]-[title|sha\[SHA|PR_TITLE\]
# -- limited to 200 chars # -- limited to 200 chars
# Outputs: # Outputs:
# - artifact-name # - artifact-name
# Example - PCSX2-linux-Qt-x64-flatpak-sse4-sha[e880a2749]
# Inputs as env-vars # Inputs as env-vars
# OS # PREFIX
# BUILD_SYSTEM
# ARCH
# SIMD
# EVENT_NAME # EVENT_NAME
# PR_TITLE # PR_TITLE
# PR_NUM # PR_NUM
# PR_SHA # PR_SHA
NAME="" if [[ -z "${PREFIX}" ]]; then
echo "PREFIX is not set, can't name artifact without it!"
if [ "${OS}" == "macos" ]; then exit 1
# MacOS has combined binaries for x64 and ARM64.
NAME="PCSX2-${OS}-Qt"
elif [[ ("${OS}" == "windows" && "$BUILD_SYSTEM" != "cmake") ]]; then
NAME="PCSX2-${OS}-Qt-${ARCH}-${SIMD}"
else
NAME="PCSX2-${OS}-Qt-${ARCH}"
fi fi
# Add cmake if used to differentate it from msbuild builds NAME="${PREFIX}"
# Else the two artifacts will have the same name and the files will be merged
if [[ ! -z "${BUILD_SYSTEM}" ]]; then
if [[ "${BUILD_SYSTEM}" == "cmake" ]] || [[ "${BUILD_SYSTEM}" == "flatpak" ]]; then
NAME="${NAME}-${BUILD_SYSTEM}"
fi
fi
# Add PR / Commit Metadata # Add PR / Commit Metadata
if [ "$EVENT_NAME" == "pull_request" ]; then if [ "$EVENT_NAME" == "pull_request" ]; then

View File

@ -0,0 +1,270 @@
diff --git a/CHANGES b/CHANGES
index 5d3dd16..587b612 100644
--- a/CHANGES
+++ b/CHANGES
@@ -4,7 +4,7 @@ v2024.1
- Update dependencies
- Propagate test/install options to Glslang
-v2024.0
+v2024.0 2024-03-09
- Update dependencies
- Utilities:
- Use Python3 explicitly in utility scripts
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ffcb54b..7c1a6d8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -117,6 +117,9 @@ if(MSVC)
endif()
endif(MSVC)
+if(NOT WIN32)
+ add_definitions("-fvisibility=hidden")
+endif()
# Configure subdirectories.
# We depend on these for later projects, so they should come first.
@@ -124,7 +127,6 @@ add_subdirectory(third_party)
add_subdirectory(libshaderc_util)
add_subdirectory(libshaderc)
-add_subdirectory(glslc)
if(${SHADERC_ENABLE_EXAMPLES})
add_subdirectory(examples)
endif()
@@ -158,5 +160,3 @@ function(define_pkg_config_file NAME LIBS)
endfunction()
define_pkg_config_file(shaderc -lshaderc_shared)
-define_pkg_config_file(shaderc_static "-lshaderc ${EXTRA_STATIC_PKGCONFIG_LIBS} -lshaderc_util")
-define_pkg_config_file(shaderc_combined -lshaderc_combined)
diff --git a/libshaderc/CMakeLists.txt b/libshaderc/CMakeLists.txt
index df9a88d..b15e5d7 100644
--- a/libshaderc/CMakeLists.txt
+++ b/libshaderc/CMakeLists.txt
@@ -24,13 +24,6 @@ set(SHADERC_SOURCES
src/shaderc_private.h
)
-add_library(shaderc STATIC ${SHADERC_SOURCES})
-shaderc_default_compile_options(shaderc)
-target_include_directories(shaderc
- PUBLIC include
- PRIVATE ${glslang_SOURCE_DIR}
- ${SPIRV-Headers_SOURCE_DIR}/include)
-
add_library(shaderc_shared SHARED ${SHADERC_SOURCES})
shaderc_default_compile_options(shaderc_shared)
target_include_directories(shaderc_shared
@@ -54,7 +47,7 @@ if(SHADERC_ENABLE_INSTALL)
DESTINATION
${CMAKE_INSTALL_INCLUDEDIR}/shaderc)
- install(TARGETS shaderc shaderc_shared
+ install(TARGETS shaderc_shared
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR}
@@ -69,20 +62,8 @@ set(SHADERC_LIBS
SPIRV-Tools
)
-target_link_libraries(shaderc PRIVATE ${SHADERC_LIBS})
target_link_libraries(shaderc_shared PRIVATE ${SHADERC_LIBS})
-shaderc_add_tests(
- TEST_PREFIX shaderc
- LINK_LIBS shaderc
- INCLUDE_DIRS include ${shaderc_SOURCE_DIR}/libshaderc_util/include ${glslang_SOURCE_DIR}
- ${spirv-tools_SOURCE_DIR}/include
- ${SPIRV-Headers_SOURCE_DIR}/include
- TEST_NAMES
- shaderc
- shaderc_cpp
- shaderc_private)
-
shaderc_add_tests(
TEST_PREFIX shaderc_shared
LINK_LIBS shaderc_shared SPIRV-Tools
@@ -94,22 +75,6 @@ shaderc_add_tests(
shaderc_cpp
shaderc_private)
-shaderc_combine_static_lib(shaderc_combined shaderc)
-
-if(SHADERC_ENABLE_INSTALL)
- install(TARGETS shaderc_combined DESTINATION ${CMAKE_INSTALL_LIBDIR})
-endif(SHADERC_ENABLE_INSTALL)
-
-shaderc_add_tests(
- TEST_PREFIX shaderc_combined
- LINK_LIBS shaderc_combined ${CMAKE_THREAD_LIBS_INIT}
- INCLUDE_DIRS include ${shaderc_SOURCE_DIR}/libshaderc_util/include ${glslang_SOURCE_DIR}
- ${spirv-tools_SOURCE_DIR}/include
- ${SPIRV-Headers_SOURCE_DIR}/include
- TEST_NAMES
- shaderc
- shaderc_cpp)
-
if(${SHADERC_ENABLE_TESTS})
add_executable(shaderc_c_smoke_test ./src/shaderc_c_smoke_test.c)
shaderc_default_c_compile_options(shaderc_c_smoke_test)
diff --git a/libshaderc/include/shaderc/shaderc.h b/libshaderc/include/shaderc/shaderc.h
index 3a3e97d..65d5b77 100644
--- a/libshaderc/include/shaderc/shaderc.h
+++ b/libshaderc/include/shaderc/shaderc.h
@@ -15,6 +15,8 @@
#ifndef SHADERC_SHADERC_H_
#define SHADERC_SHADERC_H_
+#define SHADERC_PCSX2_CUSTOM 1
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -317,7 +317,7 @@ SHADERC_EXPORT void shaderc_compile_options_set_source_language(
// Sets the compiler mode to generate debug information in the output.
SHADERC_EXPORT void shaderc_compile_options_set_generate_debug_info(
- shaderc_compile_options_t options);
+ shaderc_compile_options_t options, bool enabled, bool enable_non_semantic);
// Sets the compiler optimization level to the given level. Only the last one
// takes effect if multiple calls of this function exist.
diff --git a/libshaderc/include/shaderc/shaderc.hpp b/libshaderc/include/shaderc/shaderc.hpp
index 3817af8..5592b49 100644
--- a/libshaderc/include/shaderc/shaderc.hpp
+++ b/libshaderc/include/shaderc/shaderc.hpp
@@ -168,8 +168,9 @@ class CompileOptions {
}
// Sets the compiler mode to generate debug information in the output.
- void SetGenerateDebugInfo() {
- shaderc_compile_options_set_generate_debug_info(options_);
+ void SetGenerateDebugInfo(bool enabled, bool non_semantic_debug_info) {
+ shaderc_compile_options_set_generate_debug_info(options_, enabled,
+ non_sematic_debug_info);
}
// Sets the compiler optimization level to the given level. Only the last one
diff --git a/libshaderc/src/shaderc.cc b/libshaderc/src/shaderc.cc
index 63f1bbc..c1a9b12 100644
--- a/libshaderc/src/shaderc.cc
+++ b/libshaderc/src/shaderc.cc
@@ -418,8 +418,12 @@ void shaderc_compile_options_set_source_language(
}
void shaderc_compile_options_set_generate_debug_info(
- shaderc_compile_options_t options) {
- options->compiler.SetGenerateDebugInfo();
+ shaderc_compile_options_t options, bool enabled, bool enable_non_semantic) {
+ if (enabled) {
+ options->compiler.SetGenerateDebugInfo();
+ if (enable_non_semantic)
+ options->compiler.SetEmitNonSemanticDebugInfo();
+ }
}
void shaderc_compile_options_set_optimization_level(
diff --git a/libshaderc_util/include/libshaderc_util/compiler.h b/libshaderc_util/include/libshaderc_util/compiler.h
index d9d02b9..b076ec8 100644
--- a/libshaderc_util/include/libshaderc_util/compiler.h
+++ b/libshaderc_util/include/libshaderc_util/compiler.h
@@ -195,6 +195,7 @@ class Compiler {
warnings_as_errors_(false),
suppress_warnings_(false),
generate_debug_info_(false),
+ emit_non_semantic_debug_info_(false),
enabled_opt_passes_(),
target_env_(TargetEnv::Vulkan),
target_env_version_(TargetEnvVersion::Default),
@@ -220,6 +221,10 @@ class Compiler {
// such as identifier names and line numbers.
void SetGenerateDebugInfo();
+ // Requests that the compiler emit non-semantic debug information.
+ // Requires VK_KHR_shader_non_semantic_info.
+ void SetEmitNonSemanticDebugInfo();
+
// Sets the optimization level to the given level. Only the last one takes
// effect if multiple calls of this method exist.
void SetOptimizationLevel(OptimizationLevel level);
@@ -486,6 +491,10 @@ class Compiler {
// output.
bool generate_debug_info_;
+ // When true and generate_debug_info_ is also set, generate non-semantic debug
+ // information.
+ bool emit_non_semantic_debug_info_;
+
// Optimization passes to be applied.
std::vector<PassId> enabled_opt_passes_;
diff --git a/libshaderc_util/src/compiler.cc b/libshaderc_util/src/compiler.cc
index e5f5d10..1f9e6a5 100644
--- a/libshaderc_util/src/compiler.cc
+++ b/libshaderc_util/src/compiler.cc
@@ -341,6 +341,11 @@ std::tuple<bool, std::vector<uint32_t>, size_t> Compiler::Compile(
options.generateDebugInfo = generate_debug_info_;
options.disableOptimizer = true;
options.optimizeSize = false;
+ options.emitNonSemanticShaderDebugInfo =
+ generate_debug_info_ && emit_non_semantic_debug_info_;
+ options.emitNonSemanticShaderDebugSource =
+ generate_debug_info_ && emit_non_semantic_debug_info_;
+
// Note the call to GlslangToSpv also populates compilation_output_data.
glslang::GlslangToSpv(*program.getIntermediate(used_shader_stage), spirv,
&options);
@@ -438,6 +443,10 @@ void Compiler::SetGenerateDebugInfo() {
}
}
+void Compiler::SetEmitNonSemanticDebugInfo() {
+ emit_non_semantic_debug_info_ = true;
+}
+
void Compiler::SetOptimizationLevel(Compiler::OptimizationLevel level) {
// Clear previous settings first.
enabled_opt_passes_.clear();
diff --git a/third_party/CMakeLists.txt b/third_party/CMakeLists.txt
index d44f62a..83966b6 100644
--- a/third_party/CMakeLists.txt
+++ b/third_party/CMakeLists.txt
@@ -20,9 +20,9 @@ set(SHADERC_TINT_DIR "${SHADERC_THIRD_PARTY_ROOT_DIR}/tint" CACHE STRING
set(SHADERC_ABSL_DIR "${SHADERC_THIRD_PARTY_ROOT_DIR}/abseil_cpp" CACHE STRING
"Location of re2 source")
-set( SKIP_GLSLANG_INSTALL ${SHADERC_SKIP_INSTALL} )
-set( SKIP_SPIRV_TOOLS_INSTALL ${SHADERC_SKIP_INSTALL} )
-set( SKIP_GOOGLETEST_INSTALL ${SHADERC_SKIP_INSTALL} )
+set( SKIP_GLSLANG_INSTALL ON )
+set( SKIP_SPIRV_TOOLS_INSTALL ON )
+set( SKIP_GOOGLETEST_INSTALL ON )
# Configure third party projects.
if(${SHADERC_ENABLE_TESTS})
@@ -64,7 +64,10 @@ if (NOT TARGET SPIRV-Tools)
add_subdirectory(${SHADERC_RE2_DIR} re2)
add_subdirectory(${SHADERC_EFFCEE_DIR} effcee)
endif()
- add_subdirectory(${SHADERC_SPIRV_TOOLS_DIR} spirv-tools)
+ set(SPIRV_SKIP_EXECUTABLES ON CACHE BOOL "Skip building SPIRV-Tools executables")
+ set(SPIRV_TOOLS_BUILD_STATIC OFF CACHE BOOL "Skip building two SPIRV-Tools libs")
+ set(SPIRV_TOOLS_LIBRARY_TYPE STATIC CACHE STRING "Build static SPIRV-Tools libs")
+ add_subdirectory(${SHADERC_SPIRV_TOOLS_DIR} spirv-tools EXCLUDE_FROM_ALL)
if (NOT "${SPIRV_SKIP_TESTS}")
if (MSVC)
if (${MSVC_VERSION} LESS 1920)
@@ -87,8 +90,8 @@ if (NOT TARGET glslang)
# Glslang tests are off by default. Turn them on if testing Shaderc.
set(GLSLANG_TESTS ON)
endif()
- set(GLSLANG_ENABLE_INSTALL $<NOT:${SKIP_GLSLANG_INSTALL}>)
- add_subdirectory(${SHADERC_GLSLANG_DIR} glslang)
+ set(GLSLANG_ENABLE_INSTALL OFF)
+ add_subdirectory(${SHADERC_GLSLANG_DIR} glslang EXCLUDE_FROM_ALL)
endif()
if (NOT TARGET glslang)
message(FATAL_ERROR "glslang was not found - required for compilation")

View File

@ -1,12 +1,14 @@
#!/bin/bash #!/bin/bash
SCRIPTDIR=$(dirname "${BASH_SOURCE[0]}") SCRIPTDIR=$(dirname "${BASH_SOURCE[0]}")
source "$SCRIPTDIR/../linux/functions.sh"
set -e set -e
# While we use custom Qt builds for our releases, the Qt6 package will be good enough # While we use custom Qt builds for our releases, the Qt6 package will be good enough
# for just updating translations. Saves building it for this action alone. # for just updating translations. Saves building it for this action alone.
retry_command sudo apt-get -y install qt6-l10n-tools "$SCRIPTDIR/../../../../tools/retry.sh" sudo apt-get -y install qt6-l10n-tools python3
PATH=/usr/lib/qt6/bin:$PATH "$SCRIPTDIR/../../../../pcsx2-qt/Translations/update_en_translation.sh" "$SCRIPTDIR/../../../../tools/generate_fullscreen_ui_translation_strings.py"
"$SCRIPTDIR/../../../../pcsx2-qt/Translations/update_glyph_ranges.py"
"$SCRIPTDIR/../../../../tools/generate_update_fa_glyph_ranges.py"
PATH=/usr/lib/qt6/bin:$PATH "$SCRIPTDIR/../../../../pcsx2-qt/Translations/update_base_translation.sh"

View File

@ -26,7 +26,6 @@
# For more information, please refer to <http://unlicense.org/> # For more information, please refer to <http://unlicense.org/>
SCRIPTDIR=$(dirname "${BASH_SOURCE[0]}") SCRIPTDIR=$(dirname "${BASH_SOURCE[0]}")
source "$SCRIPTDIR/functions.sh"
if [ "$#" -ne 4 ]; then if [ "$#" -ne 4 ]; then
echo "Syntax: $0 <path to pcsx2 directory> <path to build directory> <deps prefix> <output name>" echo "Syntax: $0 <path to pcsx2 directory> <path to build directory> <deps prefix> <output name>"
@ -42,6 +41,10 @@ BINARY=pcsx2-qt
APPDIRNAME=PCSX2.AppDir APPDIRNAME=PCSX2.AppDir
STRIP=strip STRIP=strip
declare -a MANUAL_LIBS=(
"libshaderc_shared.so.1"
)
declare -a MANUAL_QT_LIBS=( declare -a MANUAL_QT_LIBS=(
"libQt6WaylandEglClientHwIntegration.so.6" "libQt6WaylandEglClientHwIntegration.so.6"
) )
@ -52,32 +55,58 @@ declare -a MANUAL_QT_PLUGINS=(
"wayland-shell-integration" "wayland-shell-integration"
) )
declare -a REMOVE_LIBS=(
'libwayland-client.so*'
'libwayland-cursor.so*'
'libwayland-egl.so*'
)
set -e set -e
LINUXDEPLOY=./linuxdeploy-x86_64.AppImage LINUXDEPLOY=./linuxdeploy-x86_64
LINUXDEPLOY_PLUGIN_QT=./linuxdeploy-plugin-qt-x86_64.AppImage LINUXDEPLOY_PLUGIN_QT=./linuxdeploy-plugin-qt-x86_64
APPIMAGETOOL=./appimagetool-x86_64.AppImage APPIMAGETOOL=./appimagetool-x86_64
PATCHELF=patchelf PATCHELF=patchelf
if [ ! -f "$LINUXDEPLOY" ]; then if [ ! -f "$LINUXDEPLOY" ]; then
retry_command wget -O "$LINUXDEPLOY" https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage "$PCSX2DIR/tools/retry.sh" wget -O "$LINUXDEPLOY" https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
chmod +x "$LINUXDEPLOY" chmod +x "$LINUXDEPLOY"
fi fi
if [ ! -f "$LINUXDEPLOY_PLUGIN_QT" ]; then if [ ! -f "$LINUXDEPLOY_PLUGIN_QT" ]; then
retry_command wget -O "$LINUXDEPLOY_PLUGIN_QT" https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage "$PCSX2DIR/tools/retry.sh" wget -O "$LINUXDEPLOY_PLUGIN_QT" https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage
chmod +x "$LINUXDEPLOY_PLUGIN_QT" chmod +x "$LINUXDEPLOY_PLUGIN_QT"
fi fi
# Using go-appimage
# Backported from https://github.com/stenzek/duckstation/pull/3251
if [ ! -f "$APPIMAGETOOL" ]; then if [ ! -f "$APPIMAGETOOL" ]; then
retry_command wget -O "$APPIMAGETOOL" https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage APPIMAGETOOLURL=$(wget -q https://api.github.com/repos/probonopd/go-appimage/releases -O - | sed 's/[()",{} ]/\n/g' | grep -o 'https.*continuous.*tool.*86_64.*mage$' | head -1)
"$PCSX2DIR/tools/retry.sh" wget -O "$APPIMAGETOOL" "$APPIMAGETOOLURL"
chmod +x "$APPIMAGETOOL" chmod +x "$APPIMAGETOOL"
fi fi
OUTDIR=$(realpath "./$APPDIRNAME") OUTDIR=$(realpath "./$APPDIRNAME")
SCRIPTDIR=$(dirname "${BASH_SOURCE[0]}")
rm -fr "$OUTDIR" rm -fr "$OUTDIR"
echo "Locating extra libraries..."
EXTRA_LIBS_ARGS=""
for lib in "${MANUAL_LIBS[@]}"; do
srcpath=$(find "$DEPSDIR" -name "$lib")
if [ ! -f "$srcpath" ]; then
echo "Missinge extra library $lib. Exiting."
exit 1
fi
echo "Found $lib at $srcpath."
if [ "$EXTRA_LIBS_ARGS" == "" ]; then
EXTRA_LIBS_ARGS="--library=$srcpath"
else
EXTRA_LIBS_ARGS="$EXTRA_LIBS_ARGS,$srcpath"
fi
done
# Why the nastyness? linuxdeploy strips our main binary, and there's no option to turn it off. # Why the nastyness? linuxdeploy strips our main binary, and there's no option to turn it off.
# It also doesn't strip the Qt libs. We can't strip them after running linuxdeploy, because # It also doesn't strip the Qt libs. We can't strip them after running linuxdeploy, because
# patchelf corrupts the libraries (but they still work), but patchelf+strip makes them crash # patchelf corrupts the libraries (but they still work), but patchelf+strip makes them crash
@ -99,11 +128,12 @@ cp "$PCSX2DIR/.github/workflows/scripts/linux/pcsx2-qt.desktop" "net.pcsx2.PCSX2
cp "$PCSX2DIR/bin/resources/icons/AppIconLarge.png" "PCSX2.png" cp "$PCSX2DIR/bin/resources/icons/AppIconLarge.png" "PCSX2.png"
echo "Running linuxdeploy to create AppDir..." echo "Running linuxdeploy to create AppDir..."
EXTRA_QT_PLUGINS="core;gui;network;svg;waylandclient;widgets;xcbqpa" \ EXTRA_QT_PLUGINS="core;gui;svg;waylandclient;widgets;xcbqpa" \
EXTRA_PLATFORM_PLUGINS="libqwayland-egl.so;libqwayland-generic.so" \ EXTRA_PLATFORM_PLUGINS="libqwayland-egl.so;libqwayland-generic.so" \
DEPLOY_PLATFORM_THEMES="1" \
QMAKE="$DEPSDIR/bin/qmake" \ QMAKE="$DEPSDIR/bin/qmake" \
NO_STRIP="1" \ NO_STRIP="1" \
$LINUXDEPLOY --plugin qt --appdir="$OUTDIR" --executable="$BUILDDIR/bin/pcsx2-qt" \ $LINUXDEPLOY --plugin qt --appdir="$OUTDIR" --executable="$BUILDDIR/bin/pcsx2-qt" $EXTRA_LIBS_ARGS \
--desktop-file="net.pcsx2.PCSX2.desktop" --icon-file="PCSX2.png" --desktop-file="net.pcsx2.PCSX2.desktop" --icon-file="PCSX2.png"
echo "Copying resources into AppDir..." echo "Copying resources into AppDir..."
@ -137,13 +167,22 @@ for GROUP in "${MANUAL_QT_PLUGINS[@]}"; do
done done
done done
# Why do we have to manually remove these libs? Because the linuxdeploy Qt plugin
# copies them, not the "main" linuxdeploy binary, and plugins don't inherit the
# include list...
for lib in "${REMOVE_LIBS[@]}"; do
for libpath in $(find "$OUTDIR/usr/lib" -name "$lib"); do
echo " Removing problematic library ${libpath}."
rm -f "$libpath"
done
done
# Restore unstripped deps (for cache). # Restore unstripped deps (for cache).
rm -fr "$DEPSDIR" rm -fr "$DEPSDIR"
mv "$DEPSDIR.bak" "$DEPSDIR" mv "$DEPSDIR.bak" "$DEPSDIR"
# Fix up translations. # Fix up translations.
rm -fr "$OUTDIR/usr/bin/translations" rm -fr "$OUTDIR/usr/bin/translations" "$OUTDIR/usr/translations"
mv "$OUTDIR/usr/translations" "$OUTDIR/usr/bin"
cp -a "$BUILDDIR/bin/translations" "$OUTDIR/usr/bin" cp -a "$BUILDDIR/bin/translations" "$OUTDIR/usr/bin"
# Generate AppStream meta-info. # Generate AppStream meta-info.
@ -151,7 +190,28 @@ echo "Generating AppStream metainfo..."
mkdir -p "$OUTDIR/usr/share/metainfo" mkdir -p "$OUTDIR/usr/share/metainfo"
"$SCRIPTDIR/generate-metainfo.sh" "$OUTDIR/usr/share/metainfo/net.pcsx2.PCSX2.appdata.xml" "$SCRIPTDIR/generate-metainfo.sh" "$OUTDIR/usr/share/metainfo/net.pcsx2.PCSX2.appdata.xml"
echo "Generating AppImage..." # Copy in AppRun hooks.
rm -f "$NAME.AppImage" # Unfortunately linuxdeploy is a bit lame and doesn't let us provide our own AppRun hooks, instead
$APPIMAGETOOL -v "$OUTDIR" "$NAME.AppImage" # they have to come from plugins.. and screw writing one of those just to disable Wayland.
echo "Copying AppRun hooks..."
mkdir -p "$OUTDIR/apprun-hooks"
for hookpath in "$SCRIPTDIR/apprun-hooks"/*; do
hookname=$(basename "$hookpath")
cp -v "$hookpath" "$OUTDIR/apprun-hooks/$hookname"
sed -i -e 's/exec /source "$this_dir"\/apprun-hooks\/"'"$hookname"'"\nexec /' "$OUTDIR/AppRun"
done
echo "Generating AppImage..."
GIT_VERSION=$(git tag --points-at HEAD)
if [[ "${GIT_VERSION}" == "" ]]; then
# In the odd event that we run this script before the release gets tagged.
GIT_VERSION=$(git describe --tags)
if [[ "${GIT_VERSION}" == "" ]]; then
GIT_VERSION=$(git rev-parse HEAD)
fi
fi
rm -f "$NAME.AppImage"
ARCH=x86_64 VERSION="${GIT_VERSION}" "$APPIMAGETOOL" -s "$OUTDIR" && mv ./*.AppImage "$NAME.AppImage"

View File

@ -0,0 +1,9 @@
if [[ -z "$I_WANT_A_BROKEN_WAYLAND_UI" ]]; then
echo "Forcing X11 instead of Wayland, due to various protocol limitations"
echo "and Qt issues. If you want to use Wayland, launch PCSX2 with"
echo "I_WANT_A_BROKEN_WAYLAND_UI=YES set."
export QT_QPA_PLATFORM=xcb
else
echo "Wayland is not being disabled. Do not complain when things break."
fi

View File

@ -2,52 +2,141 @@
set -e set -e
INSTALLDIR="$HOME/deps" if [ "$#" -ne 1 ]; then
echo "Syntax: $0 <output directory>"
exit 1
fi
SCRIPTDIR=$(realpath $(dirname "${BASH_SOURCE[0]}"))
NPROCS="$(getconf _NPROCESSORS_ONLN)" NPROCS="$(getconf _NPROCESSORS_ONLN)"
SDL=SDL2-2.28.2 INSTALLDIR="$1"
QT=6.5.2 if [ "${INSTALLDIR:0:1}" != "/" ]; then
INSTALLDIR="$PWD/$INSTALLDIR"
fi
LIBBACKTRACE=ad106d5fdd5d960bd33fae1c48a351af567fd075 LIBBACKTRACE=ad106d5fdd5d960bd33fae1c48a351af567fd075
LIBJPEG=9f
LIBPNG=1.6.44
LIBWEBP=1.4.0
LZ4=b8fd2d15309dd4e605070bd4486e26b6ef814e29
SDL=SDL2-2.30.10
QT=6.8.1
ZSTD=1.5.6
SHADERC=2024.1
SHADERC_GLSLANG=142052fa30f9eca191aa9dcf65359fcaed09eeec
SHADERC_SPIRVHEADERS=5e3ad389ee56fca27c9705d093ae5387ce404df4
SHADERC_SPIRVTOOLS=dd4b663e13c07fea4fbb3f70c1c91c86731099f7
mkdir -p deps-build mkdir -p deps-build
cd deps-build cd deps-build
cat > SHASUMS <<EOF cat > SHASUMS <<EOF
64b1102fa22093515b02ef33dd8739dee1ba57e9dbba6a092942b8bbed1a1c5e $SDL.tar.gz
fd6f417fe9e3a071cf1424a5152d926a34c4a3c5070745470be6cf12a404ed79 $LIBBACKTRACE.zip fd6f417fe9e3a071cf1424a5152d926a34c4a3c5070745470be6cf12a404ed79 $LIBBACKTRACE.zip
3db4c729b4d80a9d8fda8dd77128406353baff4755ca619177eda4cddae71269 qtbase-everywhere-src-$QT.tar.xz 04705c110cb2469caa79fb71fba3d7bf834914706e9641a4589485c1f832565b jpegsrc.v$LIBJPEG.tar.gz
aae0c08924c6a5e47f9d57e031673d611ffff7aab2bee2e1cc460471ecac6743 qtimageformats-everywhere-src-$QT.tar.xz 60c4da1d5b7f0aa8d158da48e8f8afa9773c1c8baa5d21974df61f1886b8ce8e libpng-$LIBPNG.tar.xz
48b4cc1093af2e0ab3bea30f60651bddd877a2335d16e7207879a2e9e81963a3 qtsvg-everywhere-src-$QT.tar.xz 61f873ec69e3be1b99535634340d5bde750b2e4447caa1db9f61be3fd49ab1e5 libwebp-$LIBWEBP.tar.gz
551ffb22751d8fd4d88e9ebd55b9131f4ca55341ee497fdbbba4da8d10d94341 qttools-everywhere-src-$QT.tar.xz 0728800155f3ed0a0c87e03addbd30ecbe374f7b080678bbca1506051d50dec3 $LZ4.tar.gz
337c45637e757e754c2f0ea65c20de3e6e53a841dda1253db15baa622515beeb qttranslations-everywhere-src-$QT.tar.xz f59adf36a0fcf4c94198e7d3d776c1b3824211ab7aeebeb31fe19836661196aa $SDL.tar.gz
3020be86fb7fd0abb8509906ca6583cadcaee168159abceaeb5b3e9d42563c9a qtwayland-everywhere-src-$QT.tar.xz 8c29e06cf42aacc1eafc4077ae2ec6c6fcb96a626157e0593d5e82a34fd403c1 zstd-$ZSTD.tar.gz
40b14562ef3bd779bc0e0418ea2ae08fa28235f8ea6e8c0cb3bce1d6ad58dcaf qtbase-everywhere-src-$QT.tar.xz
138cc2909aa98f5ff7283e36eb3936eb5e625d3ca3b4febae2ca21d8903dd237 qtimageformats-everywhere-src-$QT.tar.xz
3d0de73596e36b2daa7c48d77c4426bb091752856912fba720215f756c560dd0 qtsvg-everywhere-src-$QT.tar.xz
9d43d409be08b8681a0155a9c65114b69c9a3fc11aef6487bb7fdc5b283c432d qttools-everywhere-src-$QT.tar.xz
635a6093e99152243b807de51077485ceadd4786d4acb135b9340b2303035a4a qttranslations-everywhere-src-$QT.tar.xz
2226fbde4e2ddd12f8bf4b239c8f38fd706a54e789e63467dfddc77129eca203 qtwayland-everywhere-src-$QT.tar.xz
eb3b5f0c16313d34f208d90c2fa1e588a23283eed63b101edd5422be6165d528 shaderc-$SHADERC.tar.gz
aa27e4454ce631c5a17924ce0624eac736da19fc6f5a2ab15a6c58da7b36950f shaderc-glslang-$SHADERC_GLSLANG.tar.gz
5d866ce34a4b6908e262e5ebfffc0a5e11dd411640b5f24c85a80ad44c0d4697 shaderc-spirv-headers-$SHADERC_SPIRVHEADERS.tar.gz
03ee1a2c06f3b61008478f4abe9423454e53e580b9488b47c8071547c6a9db47 shaderc-spirv-tools-$SHADERC_SPIRVTOOLS.tar.gz
EOF EOF
curl -L \ curl -L \
-O "https://libsdl.org/release/$SDL.tar.gz" \
-O "https://github.com/ianlancetaylor/libbacktrace/archive/$LIBBACKTRACE.zip" \ -O "https://github.com/ianlancetaylor/libbacktrace/archive/$LIBBACKTRACE.zip" \
-O "https://ijg.org/files/jpegsrc.v$LIBJPEG.tar.gz" \
-O "https://downloads.sourceforge.net/project/libpng/libpng16/$LIBPNG/libpng-$LIBPNG.tar.xz" \
-O "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-$LIBWEBP.tar.gz" \
-O "https://github.com/lz4/lz4/archive/$LZ4.tar.gz" \
-O "https://libsdl.org/release/$SDL.tar.gz" \
-O "https://github.com/facebook/zstd/releases/download/v$ZSTD/zstd-$ZSTD.tar.gz" \
-O "https://download.qt.io/official_releases/qt/${QT%.*}/$QT/submodules/qtbase-everywhere-src-$QT.tar.xz" \ -O "https://download.qt.io/official_releases/qt/${QT%.*}/$QT/submodules/qtbase-everywhere-src-$QT.tar.xz" \
-O "https://download.qt.io/official_releases/qt/${QT%.*}/$QT/submodules/qtimageformats-everywhere-src-$QT.tar.xz" \ -O "https://download.qt.io/official_releases/qt/${QT%.*}/$QT/submodules/qtimageformats-everywhere-src-$QT.tar.xz" \
-O "https://download.qt.io/official_releases/qt/${QT%.*}/$QT/submodules/qtsvg-everywhere-src-$QT.tar.xz" \ -O "https://download.qt.io/official_releases/qt/${QT%.*}/$QT/submodules/qtsvg-everywhere-src-$QT.tar.xz" \
-O "https://download.qt.io/official_releases/qt/${QT%.*}/$QT/submodules/qttools-everywhere-src-$QT.tar.xz" \ -O "https://download.qt.io/official_releases/qt/${QT%.*}/$QT/submodules/qttools-everywhere-src-$QT.tar.xz" \
-O "https://download.qt.io/official_releases/qt/${QT%.*}/$QT/submodules/qttranslations-everywhere-src-$QT.tar.xz" \ -O "https://download.qt.io/official_releases/qt/${QT%.*}/$QT/submodules/qttranslations-everywhere-src-$QT.tar.xz" \
-O "https://download.qt.io/official_releases/qt/${QT%.*}/$QT/submodules/qtwayland-everywhere-src-$QT.tar.xz" -O "https://download.qt.io/official_releases/qt/${QT%.*}/$QT/submodules/qtwayland-everywhere-src-$QT.tar.xz" \
-o "shaderc-$SHADERC.tar.gz" "https://github.com/google/shaderc/archive/refs/tags/v$SHADERC.tar.gz" \
-o "shaderc-glslang-$SHADERC_GLSLANG.tar.gz" "https://github.com/KhronosGroup/glslang/archive/$SHADERC_GLSLANG.tar.gz" \
-o "shaderc-spirv-headers-$SHADERC_SPIRVHEADERS.tar.gz" "https://github.com/KhronosGroup/SPIRV-Headers/archive/$SHADERC_SPIRVHEADERS.tar.gz" \
-o "shaderc-spirv-tools-$SHADERC_SPIRVTOOLS.tar.gz" "https://github.com/KhronosGroup/SPIRV-Tools/archive/$SHADERC_SPIRVTOOLS.tar.gz"
shasum -a 256 --check SHASUMS shasum -a 256 --check SHASUMS
echo "Building SDL..." echo "Building libbacktrace..."
tar xf "$SDL.tar.gz" rm -fr "libbacktrace-$LIBBACKTRACE"
cd "$SDL" unzip "$LIBBACKTRACE.zip"
./configure --prefix "$INSTALLDIR" --disable-dbus --without-x --disable-video-opengl --disable-video-opengles --disable-video-vulkan --disable-wayland-shared --disable-ime --disable-oss --disable-alsa --disable-jack --disable-esd --disable-pipewire --disable-pulseaudio --disable-arts --disable-nas --disable-sndio --disable-fusionsound --disable-diskaudio cd "libbacktrace-$LIBBACKTRACE"
make "-j$NPROCS" ./configure --prefix="$INSTALLDIR"
make
make install make install
cd .. cd ..
echo "Building libbacktrace..." echo "Building libpng..."
unzip "$LIBBACKTRACE.zip" rm -fr "libpng-$LIBPNG"
cd "libbacktrace-$LIBBACKTRACE" tar xf "libpng-$LIBPNG.tar.xz"
./configure --prefix="$HOME/deps" cd "libpng-$LIBPNG"
make cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DBUILD_SHARED_LIBS=ON -DBUILD_SHARED_LIBS=ON -DPNG_TESTS=OFF -DPNG_STATIC=OFF -DPNG_SHARED=ON -DPNG_TOOLS=OFF -B build -G Ninja
cmake --build build --parallel
ninja -C build install
cd ..
echo "Building libjpeg..."
rm -fr "jpeg-$LIBJPEG"
tar xf "jpegsrc.v$LIBJPEG.tar.gz"
cd "jpeg-$LIBJPEG"
mkdir build
cd build
../configure --prefix="$INSTALLDIR" --disable-static --enable-shared
make "-j$NPROCS"
make install make install
cd ../..
echo "Building LZ4..."
rm -fr "lz4-$LZ4"
tar xf "$LZ4.tar.gz"
cd "lz4-$LZ4"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DBUILD_SHARED_LIBS=ON -DLZ4_BUILD_CLI=OFF -DLZ4_BUILD_LEGACY_LZ4C=OFF -B build-dir -G Ninja build/cmake
cmake --build build-dir --parallel
ninja -C build-dir install
cd ..
echo "Building Zstandard..."
rm -fr "zstd-$ZSTD"
tar xf "zstd-$ZSTD.tar.gz"
cd "zstd-$ZSTD"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DBUILD_SHARED_LIBS=ON -DZSTD_BUILD_SHARED=ON -DZSTD_BUILD_STATIC=OFF -DZSTD_BUILD_PROGRAMS=OFF -B build -G Ninja build/cmake
cmake --build build --parallel
ninja -C build install
cd ..
echo "Building WebP..."
rm -fr "libwebp-$LIBWEBP"
tar xf "libwebp-$LIBWEBP.tar.gz"
cd "libwebp-$LIBWEBP"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -B build -G Ninja \
-DWEBP_BUILD_ANIM_UTILS=OFF -DWEBP_BUILD_CWEBP=OFF -DWEBP_BUILD_DWEBP=OFF -DWEBP_BUILD_GIF2WEBP=OFF -DWEBP_BUILD_IMG2WEBP=OFF \
-DWEBP_BUILD_VWEBP=OFF -DWEBP_BUILD_WEBPINFO=OFF -DWEBP_BUILD_WEBPMUX=OFF -DWEBP_BUILD_EXTRAS=OFF -DBUILD_SHARED_LIBS=ON
cmake --build build --parallel
ninja -C build install
cd ..
echo "Building SDL..."
rm -fr "$SDL"
tar xf "$SDL.tar.gz"
cd "$SDL"
cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DBUILD_SHARED_LIBS=ON -DSDL_SHARED=ON -DSDL_STATIC=OFF -G Ninja
cmake --build build --parallel
ninja -C build install
cd .. cd ..
# Couple notes: # Couple notes:
@ -56,65 +145,54 @@ cd ..
# ICU avoids pulling in a bunch of large libraries, and hopefully we can get away without it. # ICU avoids pulling in a bunch of large libraries, and hopefully we can get away without it.
# OpenGL is needed to render window decorations in Wayland, apparently. # OpenGL is needed to render window decorations in Wayland, apparently.
echo "Building Qt Base..." echo "Building Qt Base..."
rm -fr "qtbase-everywhere-src-$QT"
tar xf "qtbase-everywhere-src-$QT.tar.xz" tar xf "qtbase-everywhere-src-$QT.tar.xz"
cd "qtbase-everywhere-src-$QT" cd "qtbase-everywhere-src-$QT"
mkdir build mkdir build
cd build cd build
../configure -prefix "$INSTALLDIR" -release -no-dbus -gui -widgets -fontconfig -qt-doubleconversion -ssl -openssl-runtime -opengl desktop -qpa xcb,wayland -xkbcommon -- -DFEATURE_dbus=OFF -DFEATURE_icu=OFF -DFEATURE_printsupport=OFF -DFEATURE_sql=OFF ../configure -prefix "$INSTALLDIR" -release -dbus-linked -gui -widgets -fontconfig -qt-doubleconversion -ssl -openssl-runtime -opengl desktop -qpa xcb,wayland -xkbcommon -xcb -gtk -- -DFEATURE_dbus=ON -DFEATURE_icu=OFF -DFEATURE_printsupport=OFF -DFEATURE_sql=OFF -DFEATURE_system_png=ON -DFEATURE_system_jpeg=ON -DFEATURE_system_zlib=ON -DFEATURE_system_freetype=ON -DFEATURE_system_harfbuzz=ON
cmake --build . --parallel cmake --build . --parallel
cmake --install . ninja install
cd ../../ cd ../../
echo "Building Qt SVG..." echo "Building Qt SVG..."
rm -fr "qtsvg-everywhere-src-$QT"
tar xf "qtsvg-everywhere-src-$QT.tar.xz" tar xf "qtsvg-everywhere-src-$QT.tar.xz"
cd "qtsvg-everywhere-src-$QT" cd "qtsvg-everywhere-src-$QT"
mkdir build mkdir build
cd build cd build
cmake -G Ninja -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DCMAKE_BUILD_TYPE=Release .. "$INSTALLDIR/bin/qt-configure-module" .. -- -DCMAKE_PREFIX_PATH="$INSTALLDIR"
cmake --build . --parallel cmake --build . --parallel
cmake --install . ninja install
cd ../../ cd ../../
echo "Building Qt Image Formats..." echo "Building Qt Image Formats..."
rm -fr "qtimageformats-everywhere-src-$QT"
tar xf "qtimageformats-everywhere-src-$QT.tar.xz" tar xf "qtimageformats-everywhere-src-$QT.tar.xz"
cd "qtimageformats-everywhere-src-$QT" cd "qtimageformats-everywhere-src-$QT"
mkdir build mkdir build
cd build cd build
cmake -G Ninja -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DCMAKE_BUILD_TYPE=Release .. "$INSTALLDIR/bin/qt-configure-module" .. -- -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DFEATURE_system_webp=ON
cmake --build . --parallel cmake --build . --parallel
cmake --install . ninja install
cd ../../ cd ../../
echo "Building Qt Wayland..." echo "Building Qt Wayland..."
rm -fr "qtwayland-everywhere-src-$QT"
tar xf "qtwayland-everywhere-src-$QT.tar.xz" tar xf "qtwayland-everywhere-src-$QT.tar.xz"
cd "qtwayland-everywhere-src-$QT" cd "qtwayland-everywhere-src-$QT"
mkdir build mkdir build
cd build cd build
cmake -G Ninja -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DCMAKE_BUILD_TYPE=Release .. "$INSTALLDIR/bin/qt-configure-module" .. -- -DCMAKE_PREFIX_PATH="$INSTALLDIR"
cmake --build . --parallel cmake --build . --parallel
cmake --install . ninja install
cd ../../ cd ../../
echo "Installing Qt Tools..." echo "Installing Qt Tools..."
rm -fr "qttools-everywhere-src-$QT"
tar xf "qttools-everywhere-src-$QT.tar.xz" tar xf "qttools-everywhere-src-$QT.tar.xz"
cd "qttools-everywhere-src-$QT" cd "qttools-everywhere-src-$QT"
# From Mac build-dependencies.sh: # Force disable clang scanning, it gets very confused.
# Linguist relies on a library in the Designer target, which takes 5-7 minutes to build on the CI
# Avoid it by not building Linguist, since we only need the tools that come with it
patch -u src/linguist/CMakeLists.txt <<EOF
--- src/linguist/CMakeLists.txt
+++ src/linguist/CMakeLists.txt
@@ -14,7 +14,7 @@
add_subdirectory(lrelease-pro)
add_subdirectory(lupdate)
add_subdirectory(lupdate-pro)
-if(QT_FEATURE_process AND QT_FEATURE_pushbutton AND QT_FEATURE_toolbutton AND TARGET Qt::Widgets AND NOT no-png)
+if(QT_FEATURE_process AND QT_FEATURE_pushbutton AND QT_FEATURE_toolbutton AND TARGET Qt::Widgets AND TARGET Qt::PrintSupport AND NOT no-png)
add_subdirectory(linguist)
endif()
EOF
# Also force disable clang scanning, it gets very confused.
patch -u configure.cmake <<EOF patch -u configure.cmake <<EOF
--- configure.cmake --- configure.cmake
+++ configure.cmake +++ configure.cmake
@ -139,21 +217,40 @@ EOF
mkdir build mkdir build
cd build cd build
cmake -G Ninja -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DCMAKE_BUILD_TYPE=Release -DFEATURE_assistant=OFF -DFEATURE_clang=OFF -DFEATURE_designer=OFF -DFEATURE_kmap2qmap=OFF -DFEATURE_pixeltool=OFF -DFEATURE_pkg_config=OFF -DFEATURE_qev=OFF -DFEATURE_qtattributionsscanner=OFF -DFEATURE_qtdiag=OFF -DFEATURE_qtplugininfo=OFF .. "$INSTALLDIR/bin/qt-configure-module" .. -- -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DFEATURE_assistant=OFF -DFEATURE_clang=OFF -DFEATURE_designer=ON -DFEATURE_kmap2qmap=OFF -DFEATURE_pixeltool=OFF -DFEATURE_pkg_config=OFF -DFEATURE_qev=OFF -DFEATURE_qtattributionsscanner=OFF -DFEATURE_qtdiag=OFF -DFEATURE_qtplugininfo=OFF
cmake --build . --parallel cmake --build . --parallel
cmake --install . ninja install
cd ../../ cd ../../
echo "Installing Qt Translations..." echo "Installing Qt Translations..."
rm -fr "qttranslations-everywhere-src-$QT"
tar xf "qttranslations-everywhere-src-$QT.tar.xz" tar xf "qttranslations-everywhere-src-$QT.tar.xz"
cd "qttranslations-everywhere-src-$QT" cd "qttranslations-everywhere-src-$QT"
mkdir build mkdir build
cd build cd build
cmake -G Ninja -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DCMAKE_BUILD_TYPE=Release .. "$INSTALLDIR/bin/qt-configure-module" .. -- -DCMAKE_PREFIX_PATH="$INSTALLDIR"
cmake --build . --parallel cmake --build . --parallel
cmake --install . ninja install
cd ../../ cd ../../
echo "Building shaderc..."
rm -fr "shaderc-$SHADERC"
tar xf "shaderc-$SHADERC.tar.gz"
cd "shaderc-$SHADERC"
cd third_party
tar xf "../../shaderc-glslang-$SHADERC_GLSLANG.tar.gz"
mv "glslang-$SHADERC_GLSLANG" "glslang"
tar xf "../../shaderc-spirv-headers-$SHADERC_SPIRVHEADERS.tar.gz"
mv "SPIRV-Headers-$SHADERC_SPIRVHEADERS" "spirv-headers"
tar xf "../../shaderc-spirv-tools-$SHADERC_SPIRVTOOLS.tar.gz"
mv "SPIRV-Tools-$SHADERC_SPIRVTOOLS" "spirv-tools"
cd ..
patch -p1 < "$SCRIPTDIR/../common/shaderc-changes.patch"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DSHADERC_SKIP_TESTS=ON -DSHADERC_SKIP_EXAMPLES=ON -DSHADERC_SKIP_COPYRIGHT_CHECK=ON -B build -G Ninja
cmake --build build --parallel
ninja -C build install
cd ..
echo "Cleaning up..." echo "Cleaning up..."
cd .. cd ..
rm -r deps-build rm -r deps-build

View File

@ -1,15 +0,0 @@
#!/bin/bash
set -e
if [ -n "${GITHUB_ACTIONS}" ]; then
echo "Warning: Running this script outside of GitHub Actions isn't recommended."
fi
# Prepare the Cache
ccache -p
ccache -z
# Build
ninja
# Save the Cache
ccache -s

View File

@ -8,8 +8,8 @@
{ {
"type": "git", "type": "git",
"url": "https://github.com/the-tcpdump-group/libpcap.git", "url": "https://github.com/the-tcpdump-group/libpcap.git",
"tag": "libpcap-1.10.4", "tag": "libpcap-1.10.5",
"commit": "104271ba4a14de6743e43bcf87536786d8fddea4" "commit": "bbcbc9174df3298a854daee2b3e666a4b6e5383a"
} }
], ],
"cleanup": [ "cleanup": [

View File

@ -1,24 +0,0 @@
{
"name": "libaio",
"no-autogen": true,
"make-install-args": [
"prefix=/app"
],
"build-options": {
"strip": true
},
"sources": [
{
"type": "git",
"url": "https://pagure.io/libaio.git",
"tag": "libaio-0.3.113",
"commit": "1b18bfafc6a2f7b9fa2c6be77a95afed8b7be448"
}
],
"cleanup": [
"/include",
"/lib/*.a",
"/lib/*.la"
]
}

View File

@ -1,26 +1,12 @@
{ {
"name": "sdl2", "name": "sdl2",
"buildsystem": "autotools", "buildsystem": "cmake-ninja",
"no-autogen": true, "builddir": true,
"config-opts": [ "config-opts": [
"--disable-dbus", "-DBUILD_SHARED_LIBS=ON",
"--without-x", "-DSDL_SHARED=ON",
"--disable-video-opengl", "-DSDL_STATIC=OFF",
"--disable-video-opengles", "-DSDL_TESTS=OFF"
"--disable-video-vulkan",
"--disable-wayland-shared",
"--disable-ime",
"--disable-oss",
"--disable-alsa",
"--disable-jack",
"--disable-esd",
"--disable-pipewire",
"--disable-pulseaudio",
"--disable-arts",
"--disable-nas",
"--disable-sndio",
"--disable-fusionsound",
"--disable-diskaudio"
], ],
"build-options": { "build-options": {
"strip": true "strip": true
@ -28,8 +14,8 @@
"sources": [ "sources": [
{ {
"type": "archive", "type": "archive",
"url": "https://libsdl.org/release/SDL2-2.28.2.tar.gz", "url": "https://libsdl.org/release/SDL2-2.30.10.tar.gz",
"sha256": "64b1102fa22093515b02ef33dd8739dee1ba57e9dbba6a092942b8bbed1a1c5e" "sha256": "f59adf36a0fcf4c94198e7d3d776c1b3824211ab7aeebeb31fe19836661196aa"
} }
], ],
"cleanup": [ "cleanup": [
@ -42,4 +28,3 @@
"/share/aclocal" "/share/aclocal"
] ]
} }

View File

@ -0,0 +1,51 @@
{
"name": "shaderc",
"buildsystem": "cmake-ninja",
"builddir": true,
"config-opts": [
"-DCMAKE_BUILD_TYPE=Release",
"-DSHADERC_SKIP_TESTS=ON",
"-DSHADERC_SKIP_EXAMPLES=ON",
"-DSHADERC_SKIP_COPYRIGHT_CHECK=ON"
],
"build-options": {
"strip": true
},
"sources": [
{
"type": "git",
"url": "https://github.com/google/shaderc.git",
"commit": "47a9387ef5b3600d30d84c71ec77a59dc7db46fa"
},
{
"type": "archive",
"url": "https://github.com/KhronosGroup/glslang/archive/142052fa30f9eca191aa9dcf65359fcaed09eeec.tar.gz",
"sha256": "aa27e4454ce631c5a17924ce0624eac736da19fc6f5a2ab15a6c58da7b36950f",
"dest": "third_party/glslang"
},
{
"type": "archive",
"url": "https://github.com/KhronosGroup/SPIRV-Headers/archive/5e3ad389ee56fca27c9705d093ae5387ce404df4.tar.gz",
"sha256": "5d866ce34a4b6908e262e5ebfffc0a5e11dd411640b5f24c85a80ad44c0d4697",
"dest": "third_party/spirv-headers"
},
{
"type": "archive",
"url": "https://github.com/KhronosGroup/SPIRV-Tools/archive/dd4b663e13c07fea4fbb3f70c1c91c86731099f7.tar.gz",
"sha256": "03ee1a2c06f3b61008478f4abe9423454e53e580b9488b47c8071547c6a9db47",
"dest": "third_party/spirv-tools"
},
{
"type": "patch",
"path": "../../../common/shaderc-changes.patch"
}
],
"cleanup": [
"/bin",
"/include",
"/lib/*.a",
"/lib/*.la",
"/lib/cmake",
"/lib/pkgconfig"
]
}

View File

@ -1,15 +1,15 @@
{ {
"app-id": "net.pcsx2.PCSX2", "app-id": "net.pcsx2.PCSX2",
"runtime": "org.kde.Platform", "runtime": "org.kde.Platform",
"runtime-version": "6.5", "runtime-version": "6.8",
"sdk": "org.kde.Sdk", "sdk": "org.kde.Sdk",
"sdk-extensions": [ "sdk-extensions": [
"org.freedesktop.Sdk.Extension.llvm16" "org.freedesktop.Sdk.Extension.llvm18"
], ],
"add-extensions": { "add-extensions": {
"org.freedesktop.Platform.ffmpeg-full": { "org.freedesktop.Platform.ffmpeg-full": {
"directory": "lib/ffmpeg", "directory": "lib/ffmpeg",
"version": "22.08", "version": "24.08",
"add-ld-path": ".", "add-ld-path": ".",
"autodownload": true "autodownload": true
} }
@ -19,30 +19,39 @@
"--device=all", "--device=all",
"--share=network", "--share=network",
"--share=ipc", "--share=ipc",
"--socket=fallback-x11", "--socket=x11",
"--socket=wayland",
"--socket=pulseaudio", "--socket=pulseaudio",
"--filesystem=host:ro", "--talk-name=org.freedesktop.ScreenSaver",
"--talk-name=org.freedesktop.ScreenSaver" "--env=QT_QPA_PLATFORM=xcb"
], ],
"modules": [ "modules": [
"modules/10-libpcap.json", "modules/10-libpcap.json",
"modules/11-libaio.json",
"modules/20-sdl2.json", "modules/20-sdl2.json",
"modules/21-libbacktrace.json", "modules/21-libbacktrace.json",
"modules/22-shaderc.json",
{ {
"name": "pcsx2", "name": "pcsx2",
"buildsystem": "simple", "buildsystem": "cmake-ninja",
"builddir": true,
"no-make-install": true,
"build-options": { "build-options": {
"strip": false, "strip": false,
"no-debuginfo": true, "no-debuginfo": true,
"env": { "cflags": "",
"DEPS_PREFIX": "/app", "cflags-override": true,
"COMPILER": "clang", "cxxflags": "",
"CLANG_PATH": "/usr/lib/sdk/llvm16/bin/clang", "cxxflags-override": true,
"CLANGXX_PATH": "/usr/lib/sdk/llvm16/bin/clang++", "config-opts": [
"ADDITIONAL_CMAKE_ARGS": "-DUSE_LINKED_FFMPEG=ON" "-DCMAKE_BUILD_TYPE=Release",
} "-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON",
"-DCMAKE_C_COMPILER=/usr/lib/sdk/llvm18/bin/clang",
"-DCMAKE_CXX_COMPILER=/usr/lib/sdk/llvm18/bin/clang++",
"-DCMAKE_EXE_LINKER_FLAGS_INIT=-fuse-ld=lld",
"-DCMAKE_MODULE_LINKER_FLAGS_INIT=-fuse-ld=lld",
"-DCMAKE_SHARED_LINKER_FLAGS_INIT=-fuse-ld=lld",
"-DUSE_LINKED_FFMPEG=ON",
"-DDISABLE_ADVANCE_SIMD=TRUE"
]
}, },
"sources": [ "sources": [
{ {
@ -50,17 +59,15 @@
"path": "../../../../.." "path": "../../../../.."
} }
], ],
"build-commands": [
".github/workflows/scripts/linux/generate-cmake-qt.sh",
"cd build && ../.github/workflows/scripts/linux/compile.sh && cd ..",
"cp -a build/bin ${FLATPAK_DEST}",
"cd build && ninja unittests && cd .."
],
"post-install": [ "post-install": [
"install -Dm644 bin/resources/icons/AppIconLarge.png ${FLATPAK_DEST}/share/icons/hicolor/512x512/apps/net.pcsx2.PCSX2.png", "cp -a bin \"${FLATPAK_DEST}\"",
"install -Dm644 .github/workflows/scripts/linux/pcsx2-qt.desktop ${FLATPAK_DEST}/share/applications/net.pcsx2.PCSX2.desktop", "install -Dm644 \"${FLATPAK_BUILDER_BUILDDIR}/bin/resources/icons/AppIconLarge.png\" \"${FLATPAK_DEST}/share/icons/hicolor/512x512/apps/net.pcsx2.PCSX2.png\"",
"desktop-file-edit --set-key=Icon --set-value=net.pcsx2.PCSX2 ${FLATPAK_DEST}/share/applications/net.pcsx2.PCSX2.desktop", "install -Dm644 \"${FLATPAK_BUILDER_BUILDDIR}/.github/workflows/scripts/linux/pcsx2-qt.desktop\" \"${FLATPAK_DEST}/share/applications/net.pcsx2.PCSX2.desktop\"",
"install -Dm644 .github/workflows/scripts/linux/flatpak/net.pcsx2.PCSX2.metainfo.xml ${FLATPAK_DEST}/share/metainfo/net.pcsx2.PCSX2.metainfo.xml" "desktop-file-edit --set-key=Icon --set-value=net.pcsx2.PCSX2 \"${FLATPAK_DEST}/share/applications/net.pcsx2.PCSX2.desktop\"",
"${FLATPAK_BUILDER_BUILDDIR}/.github/workflows/scripts/linux/generate-metainfo.sh \"${FLATPAK_BUILDER_BUILDDIR}/net.pcsx2.PCSX2.metainfo.xml\"",
"cat \"${FLATPAK_BUILDER_BUILDDIR}/net.pcsx2.PCSX2.metainfo.xml\"",
"install -Dm644 \"${FLATPAK_BUILDER_BUILDDIR}/net.pcsx2.PCSX2.metainfo.xml\" \"${FLATPAK_DEST}/share/metainfo/net.pcsx2.PCSX2.metainfo.xml\"",
"mkdir -p \"${FLATPAK_DEST}/lib/ffmpeg\""
] ]
} }
] ]

View File

@ -1,16 +0,0 @@
#!/bin/bash
function retry_command {
# Package servers tend to be unreliable at times..
# Retry a bunch of times.
local RETRIES=10
for i in $(seq 1 "$RETRIES"); do
"$@" && break
if [ "$i" == "$RETRIES" ]; then
echo "Command \"$@\" failed after ${RETRIES} retries."
exit 1
fi
done
}

View File

@ -1,50 +0,0 @@
#!/usr/bin/env bash
set -e
if [[ -z "${DEPS_PREFIX}" ]]; then
echo "DEPS_PREFIX is not set."
exit 1
fi
echo "Using build dependencies from: ${DEPS_PREFIX}"
if [ "${COMPILER}" = "clang" ]; then
if [[ -z "${CLANG_PATH}" ]] || [[ -z "${CLANGXX_PATH}" ]]; then
echo "CLANG_PATH or CLANGXX_PATH is not set."
exit 1
fi
echo "Using clang toolchain"
cat > "clang-toolchain.cmake" << EOF
set(CMAKE_C_COMPILER "${CLANG_PATH}")
set(CMAKE_CXX_COMPILER "${CLANGXX_PATH}")
set(CMAKE_EXE_LINKER_FLAGS_INIT "-fuse-ld=lld")
set(CMAKE_MODULE_LINKER_FLAGS_INIT "-fuse-ld=lld")
set(CMAKE_SHARED_LINKER_FLAGS_INIT "-fuse-ld=lld")
EOF
ADDITIONAL_CMAKE_ARGS="$ADDITIONAL_CMAKE_ARGS -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON -DCMAKE_TOOLCHAIN_FILE=clang-toolchain.cmake"
fi
echo "Additional CMake Args - ${ADDITIONAL_CMAKE_ARGS}"
# Generate CMake into ./build
# DISABLE_ADVANCE_SIMD is needed otherwise we end up doing -march=native.
# shellcheck disable=SC2086
cmake \
-B build \
-G Ninja \
$ADDITIONAL_CMAKE_ARGS \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DCMAKE_BUILD_TYPE=Release \
-DQT_BUILD=ON \
-DCUBEB_API=ON \
-DX11_API=ON \
-DWAYLAND_API=ON \
-DENABLE_SETCAP=OFF \
-DCMAKE_PREFIX_PATH="${DEPS_PREFIX}" \
-DUSE_SYSTEM_SDL2=ON \
-DUSE_SYSTEM_ZSTD=OFF \
-DDISABLE_ADVANCE_SIMD=TRUE

View File

@ -8,7 +8,7 @@ if [[ $# -lt 1 ]]; then
fi fi
OUTFILE=$1 OUTFILE=$1
GIT_DATE=$(git log -1 --pretty=%cd --date=short) GIT_DATE=$(git log -1 --pretty=%cd --date=iso8601)
GIT_VERSION=$(git tag --points-at HEAD) GIT_VERSION=$(git tag --points-at HEAD)
GIT_HASH=$(git rev-parse HEAD) GIT_HASH=$(git rev-parse HEAD)

View File

@ -1,55 +0,0 @@
#!/bin/bash
SCRIPTDIR=$(dirname "${BASH_SOURCE[0]}")
source "$SCRIPTDIR/functions.sh"
set -e
ARCH=x86_64
KDE_BRANCH=6.5
BRANCH=22.08
FLAT_MANAGER_CLIENT_DIR="$HOME/.local/bin"
# Build packages. Mostly needed for flat-manager-client.
declare -a BUILD_PACKAGES=(
"flatpak"
"flatpak-builder"
"appstream-util"
"python3-aiohttp"
"python3-tenacity"
"python3-gi"
"gobject-introspection"
"libappstream-glib8"
"libappstream-glib-dev"
"libappstream-dev"
"gir1.2-ostree-1.0"
)
# Flatpak runtimes and SDKs.
declare -a FLATPAK_PACKAGES=(
"org.kde.Platform/${ARCH}/${KDE_BRANCH}"
"org.kde.Sdk/${ARCH}/${KDE_BRANCH}"
"org.freedesktop.Platform.ffmpeg-full/${ARCH}/${BRANCH}"
"org.freedesktop.Sdk.Extension.llvm16/${ARCH}/${BRANCH}"
"org.freedesktop.appstream-glib/${ARCH}/stable"
)
retry_command sudo apt-get -qq update
# Install packages needed for building
echo "Will install the following packages for building - ${BUILD_PACKAGES[*]}"
retry_command sudo apt-get -y install "${BUILD_PACKAGES[@]}"
sudo flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
# Install packages needed for building
echo "Will install the following packages for building - ${FLATPAK_PACKAGES[*]}"
retry_command sudo flatpak -y install "${FLATPAK_PACKAGES[@]}"
echo "Downloading flat-manager-client"
mkdir -p "$FLAT_MANAGER_CLIENT_DIR"
pushd "$FLAT_MANAGER_CLIENT_DIR"
aria2c -Z "https://raw.githubusercontent.com/flatpak/flat-manager/9401efbdc0d6bd489507d8401c567ba219d735d5/flat-manager-client"
chmod +x flat-manager-client
echo "$FLAT_MANAGER_CLIENT_DIR" >> $GITHUB_PATH
popd

View File

@ -1,83 +0,0 @@
#!/bin/bash
SCRIPTDIR=$(dirname "${BASH_SOURCE[0]}")
source "$SCRIPTDIR/functions.sh"
set -e
# Packages - Build and Qt
declare -a BUILD_PACKAGES=(
"build-essential"
"g++"
"git"
"cmake"
"ccache"
"ninja-build"
"patchelf"
"libfuse2"
"libglib2.0-dev"
"libfontconfig1-dev"
"libharfbuzz-dev"
"libjpeg-dev"
"libpng-dev"
"libfreetype-dev"
"libinput-dev"
"libxcb-*-dev"
"libxkbcommon-dev"
"libxkbcommon-x11-dev"
"libxrender-dev"
"libwayland-dev"
"libgl1-mesa-dev"
"libegl-dev"
"libegl1-mesa-dev"
"libgl1-mesa-dev"
"libssl-dev"
)
# Packages - PCSX2
declare -a PCSX2_PACKAGES=(
"extra-cmake-modules"
"libaio-dev"
"libasound2-dev"
"libbz2-dev"
"libcurl4-openssl-dev"
"libegl1-mesa-dev"
"libgl1-mesa-dev"
"libgtk-3-dev"
"libharfbuzz-dev"
"libjpeg-dev"
"liblzma-dev"
"libpcap0.8-dev"
"libpng-dev"
"libpulse-dev"
"librsvg2-dev"
"libsamplerate0-dev"
"libudev-dev"
"libx11-xcb-dev"
"libavcodec-dev"
"libavformat-dev"
"libavutil-dev"
"libswresample-dev"
"libswscale-dev"
"pkg-config"
"zlib1g-dev"
)
if [ "${COMPILER}" = "clang" ]; then
BUILD_PACKAGES+=("llvm-16" "lld-16" "clang-16")
# Ubuntu 22.04 doesn't ship with LLVM 16, so we need to pull it from the llvm.org repos.
retry_command wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo apt-add-repository -n 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-16 main'
fi
retry_command sudo apt-get -qq update && break
# Install packages needed for building
echo "Will install the following packages for building - ${BUILD_PACKAGES[*]}"
retry_command sudo apt-get -y install "${BUILD_PACKAGES[@]}"
# Install packages needed by pcsx2
PCSX2_PACKAGES=("${PCSX2_PACKAGES[@]}")
echo "Will install the following packages for pcsx2 - ${PCSX2_PACKAGES[*]}"
retry_command sudo apt-get -y install "${PCSX2_PACKAGES[@]}"

View File

@ -1,33 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- Uses the AppStream standard https://www.freedesktop.org/software/appstream/docs/chap-Metadata.html -->
<component type="desktop"> <component type="desktop">
<id>net.pcsx2.PCSX2</id> <id>net.pcsx2.PCSX2</id>
<launchable type="desktop-id">net.pcsx2.PCSX2.desktop</launchable> <launchable type="desktop-id">net.pcsx2.PCSX2.desktop</launchable>
<metadata_license>CC0-1.0</metadata_license> <metadata_license>CC0-1.0</metadata_license>
<project_license>GPL-3.0</project_license> <project_license>GPL-3.0+</project_license>
<name>PCSX2</name> <name>PCSX2</name>
<developer_name>PCSX2</developer_name> <developer_name>PCSX2</developer_name>
<summary>PlayStation 2 Emulator</summary> <summary>PlayStation 2 Emulator</summary>
<description> <description>
<p>PCSX2 is a free and open-source PlayStation 2 (PS2) emulator. Its purpose is to emulate the PS2's hardware, using a combination of MIPS CPU Interpreters, Recompilers and a Virtual Machine which manages hardware states and PS2 system memory. This allows you to play PS2 games on your PC, with many additional features and benefits.</p> <p>PCSX2 is a free and open-source PlayStation 2 (PS2) emulator. Its purpose is to emulate the PS2's hardware, using a combination of MIPS CPU Interpreters, Recompilers, and a Virtual Machine which manages hardware states and PS2 system memory. This allows you to play PS2 games on your PC, with many additional features and benefits.</p>
<p>PlayStation 2 and PS2 are registered trademarks of Sony Interactive Entertainment. This application is not affiliated in any way with Sony Interactive Entertainment.</p> <p>PlayStation 2 and PS2 are registered trademarks of Sony Interactive Entertainment. This application is not affiliated in any way with Sony Interactive Entertainment.</p>
</description> </description>
<url type="homepage">https://pcsx2.net/</url> <url type="homepage">https://pcsx2.net/</url>
<url type="bugtracker">https://github.com/PCSX2/pcsx2/issues</url> <url type="bugtracker">https://github.com/PCSX2/pcsx2/issues</url>
<url type="donation">https://github.com/sponsors/PCSX2</url> <url type="donation">https://github.com/sponsors/PCSX2</url>
<url type="faq">https://pcsx2.net/docs/</url> <url type="faq">https://pcsx2.net/docs/</url>
<url type="help">https://discord.com/invite/TCz3t9k</url> <url type="help">https://pcsx2.net/discord</url>
<url type="translate">https://crowdin.com/project/pcsx2-emulator</url> <url type="translate">https://crowdin.com/project/pcsx2-emulator</url>
<url type="vcs-browser">https://github.com/PCSX2/pcsx2</url> <url type="contact">https://mastodon.social/@PCSX2</url>
<screenshots> <screenshots>
<screenshot type="default"> <screenshot type="default">
<image>https://raw.githubusercontent.com/PCSX2/pcsx2/master/.github/workflows/scripts/linux/flatpak/screenshots/screenshot1.png</image> <image>
https://raw.githubusercontent.com/PCSX2/pcsx2/master/.github/workflows/scripts/linux/flatpak/screenshots/screenshot1.png
</image>
<caption>
The main PCSX2 Qt interface
</caption>
</screenshot> </screenshot>
<screenshot> <screenshot>
<image>https://raw.githubusercontent.com/PCSX2/pcsx2/master/.github/workflows/scripts/linux/flatpak/screenshots/screenshot2.png</image> <image>
https://raw.githubusercontent.com/PCSX2/pcsx2/master/.github/workflows/scripts/linux/flatpak/screenshots/screenshot2.png
</image>
<caption>
PCSX2 running a game
</caption>
</screenshot> </screenshot>
</screenshots> </screenshots>
<content_rating type="oars-1.1"/> <content_rating type="oars-1.1"/>
<update_contact>stenzek_AT_gmail.com</update_contact> <update_contact>pcsx2_AT_pcsx2.net</update_contact>
<releases> <releases>
<release version="@GIT_VERSION@" date="@GIT_DATE@" /> <release version="@GIT_VERSION@" date="@GIT_DATE@" />
</releases> </releases>

View File

@ -0,0 +1,371 @@
#!/bin/bash
set -e
merge_binaries() {
X86DIR=$1
ARMDIR=$2
echo "Merging ARM64 binaries from $ARMDIR into fat binaries at $X86DIR..."
IFS="
"
pushd "$X86DIR"
for X86BIN in $(find . -type f \( -name '*.dylib' -o -name '*.a' -o -perm +111 \)); do
if file "$X86DIR/$X86BIN" | grep "Mach-O " >/dev/null; then
ARMBIN="${ARMDIR}/${X86BIN}"
echo "Merge $ARMBIN to $X86BIN..."
lipo -create "$X86BIN" "$ARMBIN" -o "$X86BIN"
fi
done
popd
}
if [ "$#" -ne 1 ]; then
echo "Syntax: $0 <output directory>"
exit 1
fi
# The bundled ffmpeg has a lot of things disabled to reduce code size.
# Users may want to use system ffmpeg for additional features
: ${BUILD_FFMPEG:=1}
export MACOSX_DEPLOYMENT_TARGET=11.0
NPROCS="$(getconf _NPROCESSORS_ONLN)"
SCRIPTDIR=$(realpath $(dirname "${BASH_SOURCE[0]}"))
INSTALLDIR="$1"
if [ "${INSTALLDIR:0:1}" != "/" ]; then
INSTALLDIR="$PWD/$INSTALLDIR"
fi
FREETYPE=2.13.3
HARFBUZZ=10.0.1
SDL=SDL2-2.30.10
ZSTD=1.5.6
LZ4=b8fd2d15309dd4e605070bd4486e26b6ef814e29
LIBPNG=1.6.44
LIBJPEG=9f
LIBWEBP=1.4.0
FFMPEG=6.0
MOLTENVK=1.2.9
QT=6.7.2
SHADERC=2024.1
SHADERC_GLSLANG=142052fa30f9eca191aa9dcf65359fcaed09eeec
SHADERC_SPIRVHEADERS=5e3ad389ee56fca27c9705d093ae5387ce404df4
SHADERC_SPIRVTOOLS=dd4b663e13c07fea4fbb3f70c1c91c86731099f7
mkdir -p deps-build
cd deps-build
export PKG_CONFIG_PATH="$INSTALLDIR/lib/pkgconfig:$PKG_CONFIG_PATH"
export LDFLAGS="-L$INSTALLDIR/lib $LDFLAGS"
export CFLAGS="-I$INSTALLDIR/include $CFLAGS"
export CXXFLAGS="-I$INSTALLDIR/include $CXXFLAGS"
CMAKE_COMMON=(
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_SHARED_LINKER_FLAGS="-dead_strip -dead_strip_dylibs"
-DCMAKE_PREFIX_PATH="$INSTALLDIR"
-DCMAKE_INSTALL_PREFIX="$INSTALLDIR"
-DCMAKE_INSTALL_NAME_DIR='$<INSTALL_PREFIX>/lib'
)
CMAKE_ARCH_X64=-DCMAKE_OSX_ARCHITECTURES="x86_64"
CMAKE_ARCH_ARM64=-DCMAKE_OSX_ARCHITECTURES="arm64"
CMAKE_ARCH_UNIVERSAL=-DCMAKE_OSX_ARCHITECTURES="x86_64;arm64"
cat > SHASUMS <<EOF
0550350666d427c74daeb85d5ac7bb353acba5f76956395995311a9c6f063289 freetype-$FREETYPE.tar.xz
e7358ea86fe10fb9261931af6f010d4358dac64f7074420ca9bc94aae2bdd542 harfbuzz-$HARFBUZZ.tar.gz
f59adf36a0fcf4c94198e7d3d776c1b3824211ab7aeebeb31fe19836661196aa $SDL.tar.gz
8c29e06cf42aacc1eafc4077ae2ec6c6fcb96a626157e0593d5e82a34fd403c1 zstd-$ZSTD.tar.gz
0728800155f3ed0a0c87e03addbd30ecbe374f7b080678bbca1506051d50dec3 $LZ4.tar.gz
60c4da1d5b7f0aa8d158da48e8f8afa9773c1c8baa5d21974df61f1886b8ce8e libpng-$LIBPNG.tar.xz
61f873ec69e3be1b99535634340d5bde750b2e4447caa1db9f61be3fd49ab1e5 libwebp-$LIBWEBP.tar.gz
04705c110cb2469caa79fb71fba3d7bf834914706e9641a4589485c1f832565b jpegsrc.v$LIBJPEG.tar.gz
57be87c22d9b49c112b6d24bc67d42508660e6b718b3db89c44e47e289137082 ffmpeg-$FFMPEG.tar.xz
f415a09385030c6510a936155ce211f617c31506db5fbc563e804345f1ecf56e v$MOLTENVK.tar.gz
c5f22a5e10fb162895ded7de0963328e7307611c688487b5d152c9ee64767599 qtbase-everywhere-src-$QT.tar.xz
e1a1d8785fae67d16ad0a443b01d5f32663a6b68d275f1806ebab257485ce5d6 qtimageformats-everywhere-src-$QT.tar.xz
fb0d1286a35be3583fee34aeb5843c94719e07193bdf1d4d8b0dc14009caef01 qtsvg-everywhere-src-$QT.tar.xz
58e855ad1b2533094726c8a425766b63a04a0eede2ed85086860e54593aa4b2a qttools-everywhere-src-$QT.tar.xz
9845780b5dc1b7279d57836db51aeaf2e4a1160c42be09750616f39157582ca9 qttranslations-everywhere-src-$QT.tar.xz
eb3b5f0c16313d34f208d90c2fa1e588a23283eed63b101edd5422be6165d528 shaderc-$SHADERC.tar.gz
aa27e4454ce631c5a17924ce0624eac736da19fc6f5a2ab15a6c58da7b36950f shaderc-glslang-$SHADERC_GLSLANG.tar.gz
5d866ce34a4b6908e262e5ebfffc0a5e11dd411640b5f24c85a80ad44c0d4697 shaderc-spirv-headers-$SHADERC_SPIRVHEADERS.tar.gz
03ee1a2c06f3b61008478f4abe9423454e53e580b9488b47c8071547c6a9db47 shaderc-spirv-tools-$SHADERC_SPIRVTOOLS.tar.gz
EOF
curl -C - -L \
-o "freetype-$FREETYPE.tar.xz" "https://sourceforge.net/projects/freetype/files/freetype2/$FREETYPE/freetype-$FREETYPE.tar.xz/download" \
-o "harfbuzz-$HARFBUZZ.tar.gz" "https://github.com/harfbuzz/harfbuzz/archive/refs/tags/$HARFBUZZ.tar.gz" \
-O "https://libsdl.org/release/$SDL.tar.gz" \
-O "https://github.com/facebook/zstd/releases/download/v$ZSTD/zstd-$ZSTD.tar.gz" \
-O "https://github.com/lz4/lz4/archive/$LZ4.tar.gz" \
-O "https://downloads.sourceforge.net/project/libpng/libpng16/$LIBPNG/libpng-$LIBPNG.tar.xz" \
-O "https://ijg.org/files/jpegsrc.v$LIBJPEG.tar.gz" \
-O "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-$LIBWEBP.tar.gz" \
-O "https://ffmpeg.org/releases/ffmpeg-$FFMPEG.tar.xz" \
-O "https://github.com/KhronosGroup/MoltenVK/archive/refs/tags/v$MOLTENVK.tar.gz" \
-O "https://download.qt.io/official_releases/qt/${QT%.*}/$QT/submodules/qtbase-everywhere-src-$QT.tar.xz" \
-O "https://download.qt.io/official_releases/qt/${QT%.*}/$QT/submodules/qtimageformats-everywhere-src-$QT.tar.xz" \
-O "https://download.qt.io/official_releases/qt/${QT%.*}/$QT/submodules/qtsvg-everywhere-src-$QT.tar.xz" \
-O "https://download.qt.io/official_releases/qt/${QT%.*}/$QT/submodules/qttools-everywhere-src-$QT.tar.xz" \
-O "https://download.qt.io/official_releases/qt/${QT%.*}/$QT/submodules/qttranslations-everywhere-src-$QT.tar.xz" \
-o "shaderc-$SHADERC.tar.gz" "https://github.com/google/shaderc/archive/refs/tags/v$SHADERC.tar.gz" \
-o "shaderc-glslang-$SHADERC_GLSLANG.tar.gz" "https://github.com/KhronosGroup/glslang/archive/$SHADERC_GLSLANG.tar.gz" \
-o "shaderc-spirv-headers-$SHADERC_SPIRVHEADERS.tar.gz" "https://github.com/KhronosGroup/SPIRV-Headers/archive/$SHADERC_SPIRVHEADERS.tar.gz" \
-o "shaderc-spirv-tools-$SHADERC_SPIRVTOOLS.tar.gz" "https://github.com/KhronosGroup/SPIRV-Tools/archive/$SHADERC_SPIRVTOOLS.tar.gz"
shasum -a 256 --check SHASUMS
echo "Installing SDL..."
rm -fr "$SDL"
tar xf "$SDL.tar.gz"
cd "$SDL"
cmake -B build "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_UNIVERSAL" -DSDL_X11=OFF -DBUILD_SHARED_LIBS=ON
make -C build "-j$NPROCS"
make -C build install
cd ..
if [ "$BUILD_FFMPEG" -ne 0 ]; then
echo "Installing FFmpeg..."
rm -fr "ffmpeg-$FFMPEG"
tar xf "ffmpeg-$FFMPEG.tar.xz"
cd "ffmpeg-$FFMPEG"
mkdir build
cd build
LDFLAGS="-dead_strip $LDFLAGS" CFLAGS="-Os $CFLAGS" CXXFLAGS="-Os $CXXFLAGS" \
../configure --prefix="$INSTALLDIR" \
--enable-cross-compile --arch=x86_64 --cc='clang -arch x86_64' --cxx='clang++ -arch x86_64' --disable-x86asm \
--disable-all --disable-autodetect --disable-static --enable-shared \
--enable-avcodec --enable-avformat --enable-avutil --enable-swresample --enable-swscale \
--enable-audiotoolbox --enable-videotoolbox \
--enable-encoder=ffv1,qtrle,pcm_s16be,pcm_s16le,*_at,*_videotoolbox \
--enable-muxer=avi,matroska,mov,mp3,mp4,wav \
--enable-protocol=file
make "-j$NPROCS"
cd ..
mkdir build-arm64
cd build-arm64
LDFLAGS="-dead_strip $LDFLAGS" CFLAGS="-Os $CFLAGS" CXXFLAGS="-Os $CXXFLAGS" \
../configure --prefix="$INSTALLDIR" \
--enable-cross-compile --arch=arm64 --cc='clang -arch arm64' --cxx='clang++ -arch arm64' --disable-x86asm \
--disable-all --disable-autodetect --disable-static --enable-shared \
--enable-avcodec --enable-avformat --enable-avutil --enable-swresample --enable-swscale \
--enable-audiotoolbox --enable-videotoolbox \
--enable-encoder=ffv1,qtrle,pcm_s16be,pcm_s16le,*_at,*_videotoolbox \
--enable-muxer=avi,matroska,mov,mp3,mp4,wav \
--enable-protocol=file
make "-j$NPROCS"
cd ..
merge_binaries $(realpath build) $(realpath build-arm64)
cd build
make install
cd ../..
fi
echo "Installing Zstd..."
rm -fr "zstd-$ZSTD"
tar xf "zstd-$ZSTD.tar.gz"
cd "zstd-$ZSTD"
cmake "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_X64" -DBUILD_SHARED_LIBS=ON -DZSTD_BUILD_PROGRAMS=OFF -B build-dir build/cmake
make -C build-dir "-j$NPROCS"
cmake "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_ARM64" -DBUILD_SHARED_LIBS=ON -DZSTD_BUILD_PROGRAMS=OFF -B build-dir-arm64 build/cmake
make -C build-dir-arm64 "-j$NPROCS"
merge_binaries $(realpath build-dir) $(realpath build-dir-arm64)
make -C build-dir install
cd ..
echo "Installing LZ4..."
rm -fr "lz4-$LZ4"
tar xf "$LZ4.tar.gz"
cd "lz4-$LZ4"
cmake "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_X64" -DBUILD_SHARED_LIBS=ON -DLZ4_BUILD_CLI=OFF -DLZ4_BUILD_LEGACY_LZ4C=OFF -B build-dir build/cmake
make -C build-dir "-j$NPROCS"
cmake "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_ARM64" -DBUILD_SHARED_LIBS=ON -DLZ4_BUILD_CLI=OFF -DLZ4_BUILD_LEGACY_LZ4C=OFF -B build-dir-arm64 build/cmake
make -C build-dir-arm64 "-j$NPROCS"
merge_binaries $(realpath build-dir) $(realpath build-dir-arm64)
make -C build-dir install
cd ..
echo "Installing libpng..."
rm -fr "libpng-$LIBPNG"
tar xf "libpng-$LIBPNG.tar.xz"
cd "libpng-$LIBPNG"
cmake "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_X64" -DBUILD_SHARED_LIBS=ON -DPNG_TESTS=OFF -DPNG_FRAMEWORK=OFF -B build
make -C build "-j$NPROCS"
cmake "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_ARM64" -DBUILD_SHARED_LIBS=ON -DPNG_TESTS=OFF -DPNG_FRAMEWORK=OFF -DPNG_ARM_NEON=on -B build-arm64
make -C build-arm64 "-j$NPROCS"
merge_binaries $(realpath build) $(realpath build-arm64)
make -C build install
cd ..
echo "Installing libjpeg..."
rm -fr "jpeg-$LIBJPEG"
tar xf "jpegsrc.v$LIBJPEG.tar.gz"
cd "jpeg-$LIBJPEG"
mkdir build
cd build
../configure --prefix="$INSTALLDIR" --disable-static --enable-shared --host="x86_64-apple-darwin" CFLAGS="-arch x86_64"
make "-j$NPROCS"
cd ..
mkdir build-arm64
cd build-arm64
../configure --prefix="$INSTALLDIR" --disable-static --enable-shared --host="aarch64-apple-darwin" CFLAGS="-arch arm64"
make "-j$NPROCS"
cd ..
merge_binaries $(realpath build) $(realpath build-arm64)
make -C build install
cd ..
echo "Installing WebP..."
rm -fr "libwebp-$LIBWEBP"
tar xf "libwebp-$LIBWEBP.tar.gz"
cd "libwebp-$LIBWEBP"
cmake "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_X64" -B build \
-DWEBP_BUILD_ANIM_UTILS=OFF -DWEBP_BUILD_CWEBP=OFF -DWEBP_BUILD_DWEBP=OFF -DWEBP_BUILD_GIF2WEBP=OFF -DWEBP_BUILD_IMG2WEBP=OFF \
-DWEBP_BUILD_VWEBP=OFF -DWEBP_BUILD_WEBPINFO=OFF -DWEBP_BUILD_WEBPMUX=OFF -DWEBP_BUILD_EXTRAS=OFF -DBUILD_SHARED_LIBS=ON
make -C build "-j$NPROCS"
cmake "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_ARM64" -B build-arm64 \
-DWEBP_BUILD_ANIM_UTILS=OFF -DWEBP_BUILD_CWEBP=OFF -DWEBP_BUILD_DWEBP=OFF -DWEBP_BUILD_GIF2WEBP=OFF -DWEBP_BUILD_IMG2WEBP=OFF \
-DWEBP_BUILD_VWEBP=OFF -DWEBP_BUILD_WEBPINFO=OFF -DWEBP_BUILD_WEBPMUX=OFF -DWEBP_BUILD_EXTRAS=OFF -DBUILD_SHARED_LIBS=ON
make -C build-arm64 "-j$NPROCS"
merge_binaries $(realpath build) $(realpath build-arm64)
make -C build install
cd ..
echo "Building FreeType without HarfBuzz..."
rm -fr "freetype-$FREETYPE"
tar xf "freetype-$FREETYPE.tar.xz"
cd "freetype-$FREETYPE"
cmake "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_UNIVERSAL" -DBUILD_SHARED_LIBS=ON -DFT_REQUIRE_ZLIB=ON -DFT_REQUIRE_PNG=ON -DFT_DISABLE_BZIP2=TRUE -DFT_DISABLE_BROTLI=TRUE -DFT_DISABLE_HARFBUZZ=TRUE -B build
cmake --build build --parallel
cmake --install build
cd ..
echo "Building HarfBuzz..."
rm -fr "harfbuzz-$HARFBUZZ"
tar xf "harfbuzz-$HARFBUZZ.tar.gz"
cd "harfbuzz-$HARFBUZZ"
cmake "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_UNIVERSAL" -DBUILD_SHARED_LIBS=ON -DHB_BUILD_UTILS=OFF -B build
cmake --build build --parallel
cmake --install build
cd ..
echo "Building FreeType with HarfBuzz..."
rm -fr "freetype-$FREETYPE"
tar xf "freetype-$FREETYPE.tar.xz"
cd "freetype-$FREETYPE"
cmake "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_UNIVERSAL" -DBUILD_SHARED_LIBS=ON -DFT_REQUIRE_ZLIB=ON -DFT_REQUIRE_PNG=ON -DFT_DISABLE_BZIP2=TRUE -DFT_DISABLE_BROTLI=TRUE -DFT_REQUIRE_HARFBUZZ=TRUE -B build
cmake --build build --parallel
cmake --install build
cd ..
# MoltenVK already builds universal binaries, nothing special to do here.
echo "Installing MoltenVK..."
rm -fr "MoltenVK-${MOLTENVK}"
tar xf "v$MOLTENVK.tar.gz"
cd "MoltenVK-${MOLTENVK}"
./fetchDependencies --macos
make macos
cp Package/Latest/MoltenVK/dynamic/dylib/macOS/libMoltenVK.dylib "$INSTALLDIR/lib/"
cd ..
echo "Installing Qt Base..."
rm -fr "qtbase-everywhere-src-$QT"
tar xf "qtbase-everywhere-src-$QT.tar.xz"
cd "qtbase-everywhere-src-$QT"
# since we don't have a direct reference to QtSvg, it doesn't deployed directly from the main binary
# (only indirectly from iconengines), and the libqsvg.dylib imageformat plugin does not get deployed.
# We could run macdeployqt twice, but that's even more janky than patching it.
patch -u src/tools/macdeployqt/shared/shared.cpp <<EOF
--- shared.cpp
+++ shared.cpp
@@ -1119,14 +1119,8 @@
addPlugins(QStringLiteral("networkinformation"));
}
- // All image formats (svg if QtSvg is used)
- const bool usesSvg = deploymentInfo.containsModule("Svg", libInfix);
- addPlugins(QStringLiteral("imageformats"), [usesSvg](const QString &lib) {
- if (lib.contains(QStringLiteral("qsvg")) && !usesSvg)
- return false;
- return true;
- });
-
+ // All image formats
+ addPlugins(QStringLiteral("imageformats"));
addPlugins(QStringLiteral("iconengines"));
// Platforminputcontext plugins if QtGui is in use
EOF
cmake -B build "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_UNIVERSAL" -DFEATURE_dbus=OFF -DFEATURE_framework=OFF -DFEATURE_icu=OFF -DFEATURE_opengl=OFF -DFEATURE_printsupport=OFF -DFEATURE_sql=OFF -DFEATURE_gssapi=OFF -DFEATURE_system_png=ON -DFEATURE_system_jpeg=ON -DFEATURE_system_zlib=ON -DFEATURE_system_freetype=ON -DFEATURE_system_harfbuzz=ON
make -C build "-j$NPROCS"
make -C build install
cd ..
echo "Installing Qt SVG..."
rm -fr "qtsvg-everywhere-src-$QT"
tar xf "qtsvg-everywhere-src-$QT.tar.xz"
cd "qtsvg-everywhere-src-$QT"
mkdir build
cd build
"$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_UNIVERSAL"
make "-j$NPROCS"
make install
cd ../..
echo "Installing Qt Image Formats..."
rm -fr "qtimageformats-everywhere-src-$QT"
tar xf "qtimageformats-everywhere-src-$QT.tar.xz"
cd "qtimageformats-everywhere-src-$QT"
mkdir build
cd build
"$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_UNIVERSAL" -DFEATURE_system_webp=ON
make "-j$NPROCS"
make install
cd ../..
echo "Installing Qt Tools..."
rm -fr "qttools-everywhere-src-$QT"
tar xf "qttools-everywhere-src-$QT.tar.xz"
cd "qttools-everywhere-src-$QT"
mkdir build
cd build
"$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_UNIVERSAL" -DFEATURE_assistant=OFF -DFEATURE_clang=OFF -DFEATURE_designer=OFF -DFEATURE_kmap2qmap=OFF -DFEATURE_pixeltool=OFF -DFEATURE_pkg_config=OFF -DFEATURE_qev=OFF -DFEATURE_qtattributionsscanner=OFF -DFEATURE_qtdiag=OFF -DFEATURE_qtplugininfo=OFF
make "-j$NPROCS"
make install
cd ../..
echo "Building shaderc..."
rm -fr "shaderc-$SHADERC"
tar xf "shaderc-$SHADERC.tar.gz"
cd "shaderc-$SHADERC"
cd third_party
tar xf "../../shaderc-glslang-$SHADERC_GLSLANG.tar.gz"
mv "glslang-$SHADERC_GLSLANG" "glslang"
tar xf "../../shaderc-spirv-headers-$SHADERC_SPIRVHEADERS.tar.gz"
mv "SPIRV-Headers-$SHADERC_SPIRVHEADERS" "spirv-headers"
tar xf "../../shaderc-spirv-tools-$SHADERC_SPIRVTOOLS.tar.gz"
mv "SPIRV-Tools-$SHADERC_SPIRVTOOLS" "spirv-tools"
cd ..
patch -p1 < "$SCRIPTDIR/../common/shaderc-changes.patch"
cmake "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_UNIVERSAL" -DSHADERC_SKIP_TESTS=ON -DSHADERC_SKIP_EXAMPLES=ON -DSHADERC_SKIP_COPYRIGHT_CHECK=ON -B build
make -C build "-j$NPROCS"
make -C build install
cd ..
echo "Installing Qt Translations..."
rm -fr "qttranslations-everywhere-src-$QT"
tar xf "qttranslations-everywhere-src-$QT.tar.xz"
cd "qttranslations-everywhere-src-$QT"
mkdir build
cd build
"$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_UNIVERSAL"
make "-j$NPROCS"
make install
cd ../..
echo "Cleaning up..."
cd ..
rm -rf deps-build

View File

@ -2,238 +2,328 @@
set -e set -e
export MACOSX_DEPLOYMENT_TARGET=10.14 if [ "$#" -ne 1 ]; then
echo "Syntax: $0 <output directory>"
exit 1
fi
# The bundled ffmpeg has a lot of things disabled to reduce code size.
# Users may want to use system ffmpeg for additional features
: ${BUILD_FFMPEG:=1}
export MACOSX_DEPLOYMENT_TARGET=11.0
INSTALLDIR="$HOME/deps"
NPROCS="$(getconf _NPROCESSORS_ONLN)" NPROCS="$(getconf _NPROCESSORS_ONLN)"
SDL=SDL2-2.28.2 SCRIPTDIR=$(realpath $(dirname "${BASH_SOURCE[0]}"))
PNG=1.6.37 INSTALLDIR="$1"
JPG=9e if [ "${INSTALLDIR:0:1}" != "/" ]; then
FFMPEG=6.0 INSTALLDIR="$PWD/$INSTALLDIR"
QT=6.4.3 # Currently stuck on Qt 6.4 due to 6.5 requiring macOS 11.0. fi
mkdir deps-build FREETYPE=2.13.3
HARFBUZZ=10.0.1
SDL=SDL2-2.30.10
ZSTD=1.5.6
LZ4=b8fd2d15309dd4e605070bd4486e26b6ef814e29
LIBPNG=1.6.44
LIBJPEG=9f
LIBWEBP=1.4.0
FFMPEG=6.0
MOLTENVK=1.2.9
QT=6.7.2
SHADERC=2024.1
SHADERC_GLSLANG=142052fa30f9eca191aa9dcf65359fcaed09eeec
SHADERC_SPIRVHEADERS=5e3ad389ee56fca27c9705d093ae5387ce404df4
SHADERC_SPIRVTOOLS=dd4b663e13c07fea4fbb3f70c1c91c86731099f7
mkdir -p deps-build
cd deps-build cd deps-build
export PKG_CONFIG_PATH="$INSTALLDIR/lib/pkgconfig:$PKG_CONFIG_PATH" export PKG_CONFIG_PATH="$INSTALLDIR/lib/pkgconfig:$PKG_CONFIG_PATH"
export LDFLAGS="-L$INSTALLDIR/lib -dead_strip $LDFLAGS" export LDFLAGS="-L$INSTALLDIR/lib $LDFLAGS"
export CFLAGS="-I$INSTALLDIR/include -Os $CFLAGS" export CFLAGS="-I$INSTALLDIR/include $CFLAGS"
export CXXFLAGS="-I$INSTALLDIR/include -Os $CXXFLAGS" export CXXFLAGS="-I$INSTALLDIR/include $CXXFLAGS"
CMAKE_COMMON=(
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_SHARED_LINKER_FLAGS="-dead_strip -dead_strip_dylibs"
-DCMAKE_PREFIX_PATH="$INSTALLDIR"
-DCMAKE_INSTALL_PREFIX="$INSTALLDIR"
-DCMAKE_OSX_ARCHITECTURES="x86_64"
-DCMAKE_INSTALL_NAME_DIR='$<INSTALL_PREFIX>/lib'
)
cat > SHASUMS <<EOF cat > SHASUMS <<EOF
64b1102fa22093515b02ef33dd8739dee1ba57e9dbba6a092942b8bbed1a1c5e $SDL.tar.gz 0550350666d427c74daeb85d5ac7bb353acba5f76956395995311a9c6f063289 freetype-$FREETYPE.tar.xz
505e70834d35383537b6491e7ae8641f1a4bed1876dbfe361201fc80868d88ca libpng-$PNG.tar.xz e7358ea86fe10fb9261931af6f010d4358dac64f7074420ca9bc94aae2bdd542 harfbuzz-$HARFBUZZ.tar.gz
4077d6a6a75aeb01884f708919d25934c93305e49f7e3f36db9129320e6f4f3d jpegsrc.v$JPG.tar.gz f59adf36a0fcf4c94198e7d3d776c1b3824211ab7aeebeb31fe19836661196aa $SDL.tar.gz
8c29e06cf42aacc1eafc4077ae2ec6c6fcb96a626157e0593d5e82a34fd403c1 zstd-$ZSTD.tar.gz
0728800155f3ed0a0c87e03addbd30ecbe374f7b080678bbca1506051d50dec3 $LZ4.tar.gz
60c4da1d5b7f0aa8d158da48e8f8afa9773c1c8baa5d21974df61f1886b8ce8e libpng-$LIBPNG.tar.xz
61f873ec69e3be1b99535634340d5bde750b2e4447caa1db9f61be3fd49ab1e5 libwebp-$LIBWEBP.tar.gz
04705c110cb2469caa79fb71fba3d7bf834914706e9641a4589485c1f832565b jpegsrc.v$LIBJPEG.tar.gz
57be87c22d9b49c112b6d24bc67d42508660e6b718b3db89c44e47e289137082 ffmpeg-$FFMPEG.tar.xz 57be87c22d9b49c112b6d24bc67d42508660e6b718b3db89c44e47e289137082 ffmpeg-$FFMPEG.tar.xz
5087c9e5b0165e7bc3c1a4ab176b35d0cd8f52636aea903fa377bdba00891a60 qtbase-everywhere-src-$QT.tar.xz f415a09385030c6510a936155ce211f617c31506db5fbc563e804345f1ecf56e v$MOLTENVK.tar.gz
0aff58062e74b84617c5da8325d8cdad5368d8f4d2a11ceafcd58329fe99b798 qtimageformats-everywhere-src-$QT.tar.xz c5f22a5e10fb162895ded7de0963328e7307611c688487b5d152c9ee64767599 qtbase-everywhere-src-$QT.tar.xz
88315f886cf81898705e487cedba6e6160724359d23c518c92c333c098879a4a qtsvg-everywhere-src-$QT.tar.xz e1a1d8785fae67d16ad0a443b01d5f32663a6b68d275f1806ebab257485ce5d6 qtimageformats-everywhere-src-$QT.tar.xz
867df829cd5cd3ae8efe62e825503123542764b13c96953511e567df70c5a091 qttools-everywhere-src-$QT.tar.xz fb0d1286a35be3583fee34aeb5843c94719e07193bdf1d4d8b0dc14009caef01 qtsvg-everywhere-src-$QT.tar.xz
79e56b7800d49649a8a8010818538c367a829e0b7a09d5f60bd3aecf5abe972c qttranslations-everywhere-src-$QT.tar.xz 58e855ad1b2533094726c8a425766b63a04a0eede2ed85086860e54593aa4b2a qttools-everywhere-src-$QT.tar.xz
9845780b5dc1b7279d57836db51aeaf2e4a1160c42be09750616f39157582ca9 qttranslations-everywhere-src-$QT.tar.xz
eb3b5f0c16313d34f208d90c2fa1e588a23283eed63b101edd5422be6165d528 shaderc-$SHADERC.tar.gz
aa27e4454ce631c5a17924ce0624eac736da19fc6f5a2ab15a6c58da7b36950f shaderc-glslang-$SHADERC_GLSLANG.tar.gz
5d866ce34a4b6908e262e5ebfffc0a5e11dd411640b5f24c85a80ad44c0d4697 shaderc-spirv-headers-$SHADERC_SPIRVHEADERS.tar.gz
03ee1a2c06f3b61008478f4abe9423454e53e580b9488b47c8071547c6a9db47 shaderc-spirv-tools-$SHADERC_SPIRVTOOLS.tar.gz
EOF EOF
curl -L \ curl -L \
-o "freetype-$FREETYPE.tar.xz" "https://sourceforge.net/projects/freetype/files/freetype2/$FREETYPE/freetype-$FREETYPE.tar.xz/download" \
-o "harfbuzz-$HARFBUZZ.tar.gz" "https://github.com/harfbuzz/harfbuzz/archive/refs/tags/$HARFBUZZ.tar.gz" \
-O "https://libsdl.org/release/$SDL.tar.gz" \ -O "https://libsdl.org/release/$SDL.tar.gz" \
-O "https://downloads.sourceforge.net/project/libpng/libpng16/$PNG/libpng-$PNG.tar.xz" \ -O "https://github.com/facebook/zstd/releases/download/v$ZSTD/zstd-$ZSTD.tar.gz" \
-O "https://www.ijg.org/files/jpegsrc.v$JPG.tar.gz" \ -O "https://github.com/lz4/lz4/archive/$LZ4.tar.gz" \
-O "https://downloads.sourceforge.net/project/libpng/libpng16/$LIBPNG/libpng-$LIBPNG.tar.xz" \
-O "https://ijg.org/files/jpegsrc.v$LIBJPEG.tar.gz" \
-O "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-$LIBWEBP.tar.gz" \
-O "https://ffmpeg.org/releases/ffmpeg-$FFMPEG.tar.xz" \ -O "https://ffmpeg.org/releases/ffmpeg-$FFMPEG.tar.xz" \
-O "https://github.com/KhronosGroup/MoltenVK/archive/refs/tags/v$MOLTENVK.tar.gz" \
-O "https://download.qt.io/official_releases/qt/${QT%.*}/$QT/submodules/qtbase-everywhere-src-$QT.tar.xz" \ -O "https://download.qt.io/official_releases/qt/${QT%.*}/$QT/submodules/qtbase-everywhere-src-$QT.tar.xz" \
-O "https://download.qt.io/official_releases/qt/${QT%.*}/$QT/submodules/qtimageformats-everywhere-src-$QT.tar.xz" \ -O "https://download.qt.io/official_releases/qt/${QT%.*}/$QT/submodules/qtimageformats-everywhere-src-$QT.tar.xz" \
-O "https://download.qt.io/official_releases/qt/${QT%.*}/$QT/submodules/qtsvg-everywhere-src-$QT.tar.xz" \ -O "https://download.qt.io/official_releases/qt/${QT%.*}/$QT/submodules/qtsvg-everywhere-src-$QT.tar.xz" \
-O "https://download.qt.io/official_releases/qt/${QT%.*}/$QT/submodules/qttools-everywhere-src-$QT.tar.xz" \ -O "https://download.qt.io/official_releases/qt/${QT%.*}/$QT/submodules/qttools-everywhere-src-$QT.tar.xz" \
-O "https://download.qt.io/official_releases/qt/${QT%.*}/$QT/submodules/qttranslations-everywhere-src-$QT.tar.xz" \ -O "https://download.qt.io/official_releases/qt/${QT%.*}/$QT/submodules/qttranslations-everywhere-src-$QT.tar.xz" \
-o "shaderc-$SHADERC.tar.gz" "https://github.com/google/shaderc/archive/refs/tags/v$SHADERC.tar.gz" \
-o "shaderc-glslang-$SHADERC_GLSLANG.tar.gz" "https://github.com/KhronosGroup/glslang/archive/$SHADERC_GLSLANG.tar.gz" \
-o "shaderc-spirv-headers-$SHADERC_SPIRVHEADERS.tar.gz" "https://github.com/KhronosGroup/SPIRV-Headers/archive/$SHADERC_SPIRVHEADERS.tar.gz" \
-o "shaderc-spirv-tools-$SHADERC_SPIRVTOOLS.tar.gz" "https://github.com/KhronosGroup/SPIRV-Tools/archive/$SHADERC_SPIRVTOOLS.tar.gz"
shasum -a 256 --check SHASUMS shasum -a 256 --check SHASUMS
echo "Installing SDL..." echo "Installing SDL..."
rm -fr "$SDL"
tar xf "$SDL.tar.gz" tar xf "$SDL.tar.gz"
cd "$SDL" cd "$SDL"
cmake -B build "${CMAKE_COMMON[@]}" -DSDL_X11=OFF -DBUILD_SHARED_LIBS=ON
# MFI causes multiple joystick connection events, I'm guessing because both the HIDAPI and MFI interfaces
# race each other, and sometimes both end up getting through. So, just force MFI off.
patch -u CMakeLists.txt <<EOF
--- CMakeLists.txt 2023-08-03 01:33:11
+++ CMakeLists.txt 2023-08-26 12:58:53
@@ -2105,7 +2105,7 @@
#import <Foundation/Foundation.h>
#import <CoreHaptics/CoreHaptics.h>
int main() { return 0; }" HAVE_FRAMEWORK_COREHAPTICS)
- if(HAVE_FRAMEWORK_GAMECONTROLLER AND HAVE_FRAMEWORK_COREHAPTICS)
+ if(HAVE_FRAMEWORK_GAMECONTROLLER AND HAVE_FRAMEWORK_COREHAPTICS AND FALSE)
# Only enable MFI if we also have CoreHaptics to ensure rumble works
set(SDL_JOYSTICK_MFI 1)
set(SDL_FRAMEWORK_GAMECONTROLLER 1)
EOF
cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DSDL_X11=OFF
make -C build "-j$NPROCS" make -C build "-j$NPROCS"
make -C build install make -C build install
cd .. cd ..
if [ "$BUILD_FFMPEG" -ne 0 ]; then
echo "Installing FFmpeg..."
rm -fr "ffmpeg-$FFMPEG"
tar xf "ffmpeg-$FFMPEG.tar.xz"
cd "ffmpeg-$FFMPEG"
LDFLAGS="-dead_strip $LDFLAGS" CFLAGS="-Os $CFLAGS" CXXFLAGS="-Os $CXXFLAGS" \
./configure --prefix="$INSTALLDIR" \
--enable-cross-compile --arch=x86_64 --cc='clang -arch x86_64' --cxx='clang++ -arch x86_64' \
--disable-all --disable-autodetect --disable-static --enable-shared \
--enable-avcodec --enable-avformat --enable-avutil --enable-swresample --enable-swscale \
--enable-audiotoolbox --enable-videotoolbox \
--enable-encoder=ffv1,qtrle,pcm_s16be,pcm_s16le,*_at,*_videotoolbox \
--enable-muxer=avi,matroska,mov,mp3,mp4,wav \
--enable-protocol=file
make "-j$NPROCS"
make install
cd ..
fi
echo "Installing Zstd..."
rm -fr "zstd-$ZSTD"
tar xf "zstd-$ZSTD.tar.gz"
cd "zstd-$ZSTD"
cmake "${CMAKE_COMMON[@]}" -DBUILD_SHARED_LIBS=ON -DZSTD_BUILD_PROGRAMS=OFF -B build-dir build/cmake
make -C build-dir "-j$NPROCS"
make -C build-dir install
cd ..
echo "Installing LZ4..."
rm -fr "lz4-$LZ4"
tar xf "$LZ4.tar.gz"
cd "lz4-$LZ4"
cmake "${CMAKE_COMMON[@]}" -DBUILD_SHARED_LIBS=ON -DLZ4_BUILD_CLI=OFF -DLZ4_BUILD_LEGACY_LZ4C=OFF -B build-dir build/cmake
make -C build-dir "-j$NPROCS"
make -C build-dir install
cd ..
echo "Installing libpng..." echo "Installing libpng..."
tar xf "libpng-$PNG.tar.xz" rm -fr "libpng-$LIBPNG"
cd "libpng-$PNG" tar xf "libpng-$LIBPNG.tar.xz"
./configure --prefix "$INSTALLDIR" --disable-dependency-tracking cd "libpng-$LIBPNG"
make "-j$NPROCS" cmake "${CMAKE_COMMON[@]}" -DBUILD_SHARED_LIBS=ON -DPNG_TESTS=OFF -DPNG_FRAMEWORK=OFF -B build
make install make -C build "-j$NPROCS"
make -C build install
cd .. cd ..
echo "Installing libjpeg..." echo "Installing libjpeg..."
tar xf "jpegsrc.v$JPG.tar.gz" rm -fr "jpeg-$LIBJPEG"
cd "jpeg-$JPG" tar xf "jpegsrc.v$LIBJPEG.tar.gz"
./configure --prefix "$INSTALLDIR" --disable-dependency-tracking cd "jpeg-$LIBJPEG"
mkdir build
cd build
../configure --prefix="$INSTALLDIR" --disable-static --enable-shared --host="x86_64-apple-darwin" CFLAGS="-arch x86_64"
make "-j$NPROCS" make "-j$NPROCS"
make install make install
cd ../..
echo "Installing WebP..."
rm -fr "libwebp-$LIBWEBP"
tar xf "libwebp-$LIBWEBP.tar.gz"
cd "libwebp-$LIBWEBP"
cmake "${CMAKE_COMMON[@]}" -B build \
-DWEBP_BUILD_ANIM_UTILS=OFF -DWEBP_BUILD_CWEBP=OFF -DWEBP_BUILD_DWEBP=OFF -DWEBP_BUILD_GIF2WEBP=OFF -DWEBP_BUILD_IMG2WEBP=OFF \
-DWEBP_BUILD_VWEBP=OFF -DWEBP_BUILD_WEBPINFO=OFF -DWEBP_BUILD_WEBPMUX=OFF -DWEBP_BUILD_EXTRAS=OFF -DBUILD_SHARED_LIBS=ON
make -C build "-j$NPROCS"
make -C build install
cd .. cd ..
echo "Installing FFmpeg..." echo "Building FreeType without HarfBuzz..."
tar xf "ffmpeg-$FFMPEG.tar.xz" rm -fr "freetype-$FREETYPE"
cd "ffmpeg-$FFMPEG" tar xf "freetype-$FREETYPE.tar.xz"
./configure --prefix="$INSTALLDIR" --disable-all --disable-autodetect --disable-static --enable-shared \ cd "freetype-$FREETYPE"
--enable-avcodec --enable-avformat --enable-avutil --enable-swresample --enable-swscale \ cmake "${CMAKE_COMMON[@]}" -DBUILD_SHARED_LIBS=ON -DFT_REQUIRE_ZLIB=ON -DFT_REQUIRE_PNG=ON -DFT_DISABLE_BZIP2=TRUE -DFT_DISABLE_BROTLI=TRUE -DFT_DISABLE_HARFBUZZ=TRUE -B build
--enable-audiotoolbox --enable-videotoolbox \ cmake --build build --parallel
--enable-encoder=ffv1,qtrle,pcm_s16be,pcm_s16le,*_at,*_videotoolbox \ cmake --install build
--enable-muxer=avi,matroska,mov,mp3,mp4,wav \ cd ..
--enable-protocol=file
make "-j$NPROCS" echo "Building HarfBuzz..."
make install rm -fr "harfbuzz-$HARFBUZZ"
tar xf "harfbuzz-$HARFBUZZ.tar.gz"
cd "harfbuzz-$HARFBUZZ"
cmake "${CMAKE_COMMON[@]}" -DBUILD_SHARED_LIBS=ON -DHB_BUILD_UTILS=OFF -B build
cmake --build build --parallel
cmake --install build
cd ..
echo "Building FreeType with HarfBuzz..."
rm -fr "freetype-$FREETYPE"
tar xf "freetype-$FREETYPE.tar.xz"
cd "freetype-$FREETYPE"
cmake "${CMAKE_COMMON[@]}" -DBUILD_SHARED_LIBS=ON -DFT_REQUIRE_ZLIB=ON -DFT_REQUIRE_PNG=ON -DFT_DISABLE_BZIP2=TRUE -DFT_DISABLE_BROTLI=TRUE -DFT_REQUIRE_HARFBUZZ=TRUE -B build
cmake --build build --parallel
cmake --install build
cd ..
# MoltenVK already builds universal binaries, nothing special to do here.
echo "Installing MoltenVK..."
rm -fr "MoltenVK-${MOLTENVK}"
tar xf "v$MOLTENVK.tar.gz"
cd "MoltenVK-${MOLTENVK}"
sed -i '' 's/xcodebuild "$@"/xcodebuild $XCODEBUILD_EXTRA_ARGS "$@"/g' fetchDependencies
sed -i '' 's/XCODEBUILD :=/XCODEBUILD ?=/g' Makefile
XCODEBUILD_EXTRA_ARGS="VALID_ARCHS=x86_64" ./fetchDependencies --macos
XCODEBUILD="set -o pipefail && xcodebuild VALID_ARCHS=x86_64" make macos
cp Package/Latest/MoltenVK/dynamic/dylib/macOS/libMoltenVK.dylib "$INSTALLDIR/lib/"
cd .. cd ..
echo "Installing Qt Base..." echo "Installing Qt Base..."
rm -fr "qtbase-everywhere-src-$QT"
tar xf "qtbase-everywhere-src-$QT.tar.xz" tar xf "qtbase-everywhere-src-$QT.tar.xz"
cd "qtbase-everywhere-src-$QT" cd "qtbase-everywhere-src-$QT"
# Qt's panel:shouldEnableURL: implementation does a whole bunch of things that activate macOS's sandbox permissions dialog
# Since this is called on every file being displayed in the open/save panel, that spams users with permissions dialogs
# Simple solution: Hopefully no one needs any filters that aren't simple file extension filters, remove all other handling
patch -u src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm <<EOF
--- src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm
+++ src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm
@@ -133,7 +133,5 @@
NSURL *url = [NSURL fileURLWithPath:filepath isDirectory:info.isDir()];
- bool selectable = (m_options->acceptMode() == QFileDialogOptions::AcceptSave)
- || [self panel:m_panel shouldEnableURL:url];
m_panel.directoryURL = [NSURL fileURLWithPath:m_currentDirectory]; # since we don't have a direct reference to QtSvg, it doesn't deployed directly from the main binary
- m_panel.nameFieldStringValue = selectable ? info.fileName().toNSString() : @""; # (only indirectly from iconengines), and the libqsvg.dylib imageformat plugin does not get deployed.
+ m_panel.nameFieldStringValue = info.fileName().toNSString(); # We could run macdeployqt twice, but that's even more janky than patching it.
@@ -203,61 +201,2 @@ # https://github.com/qt/qtbase/commit/7b018629c3c3ab23665bf1da00c43c1546042035
return hidden; # The QProcess default wait time of 30s may be too short in e.g. CI environments where processes may be blocked
-} # for a longer time waiting for CPU or IO.
patch -u src/tools/macdeployqt/shared/shared.cpp <<EOF
--- shared.cpp
+++ shared.cpp
@@ -152,7 +152,7 @@
LogDebug() << " inspecting" << binaryPath;
QProcess otool;
otool.start("otool", QStringList() << "-L" << binaryPath);
- otool.waitForFinished();
+ otool.waitForFinished(-1);
if (otool.exitStatus() != QProcess::NormalExit || otool.exitCode() != 0) {
LogError() << otool.readAllStandardError();
@@ -1122,14 +1122,8 @@
addPlugins(QStringLiteral("networkinformation"));
}
- // All image formats (svg if QtSvg is used)
- const bool usesSvg = deploymentInfo.containsModule("Svg", libInfix);
- addPlugins(QStringLiteral("imageformats"), [usesSvg](const QString &lib) {
- if (lib.contains(QStringLiteral("qsvg")) && !usesSvg)
- return false;
- return true;
- });
- -
-- (BOOL)panel:(id)sender shouldEnableURL:(NSURL *)url + // All image formats
-{ + addPlugins(QStringLiteral("imageformats"));
- Q_UNUSED(sender); addPlugins(QStringLiteral("iconengines"));
-
- NSString *filename = url.path; // Platforminputcontext plugins if QtGui is in use
- if (!filename.length)
- return NO;
-
- // Always accept directories regardless of their names (unless it is a bundle):
- NSFileManager *fm = NSFileManager.defaultManager;
- NSDictionary *fileAttrs = [fm attributesOfItemAtPath:filename error:nil];
- if (!fileAttrs)
- return NO; // Error accessing the file means 'no'.
- NSString *fileType = fileAttrs.fileType;
- bool isDir = [fileType isEqualToString:NSFileTypeDirectory];
- if (isDir) {
- if (!m_panel.treatsFilePackagesAsDirectories) {
- if ([NSWorkspace.sharedWorkspace isFilePackageAtPath:filename] == NO)
- return YES;
- }
- }
-
- // Treat symbolic links and aliases to directories like directories
- QFileInfo fileInfo(QString::fromNSString(filename));
- if (fileInfo.isSymLink() && QFileInfo(fileInfo.symLinkTarget()).isDir())
- return YES;
-
- QString qtFileName = fileInfo.fileName();
- // No filter means accept everything
- bool nameMatches = m_selectedNameFilter->isEmpty();
- // Check if the current file name filter accepts the file:
- for (int i = 0; !nameMatches && i < m_selectedNameFilter->size(); ++i) {
- if (QDir::match(m_selectedNameFilter->at(i), qtFileName))
- nameMatches = true;
- }
- if (!nameMatches)
- return NO;
-
- QDir::Filters filter = m_options->filter();
- if ((!(filter & (QDir::Dirs | QDir::AllDirs)) && isDir)
- || (!(filter & QDir::Files) && [fileType isEqualToString:NSFileTypeRegular])
- || ((filter & QDir::NoSymLinks) && [fileType isEqualToString:NSFileTypeSymbolicLink]))
- return NO;
-
- bool filterPermissions = ((filter & QDir::PermissionMask)
- && (filter & QDir::PermissionMask) != QDir::PermissionMask);
- if (filterPermissions) {
- if ((!(filter & QDir::Readable) && [fm isReadableFileAtPath:filename])
- || (!(filter & QDir::Writable) && [fm isWritableFileAtPath:filename])
- || (!(filter & QDir::Executable) && [fm isExecutableFileAtPath:filename]))
- return NO;
- }
- if (!(filter & QDir::Hidden)
- && (qtFileName.startsWith(u'.') || [self isHiddenFileAtURL:url]))
- return NO;
-
- return YES;
}
@@ -406,5 +345,2 @@
{
- if (m_options->acceptMode() != QFileDialogOptions::AcceptSave)
- return nil; // panel:shouldEnableURL: does the file filtering for NSOpenPanel
-
QStringList fileTypes;
EOF EOF
cmake -B build -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DCMAKE_BUILD_TYPE=Release -DFEATURE_optimize_size=ON -DFEATURE_dbus=OFF -DFEATURE_framework=OFF -DFEATURE_icu=OFF -DFEATURE_opengl=OFF -DFEATURE_printsupport=OFF -DFEATURE_sql=OFF -DFEATURE_gssapi=OFF cmake -B build "${CMAKE_COMMON[@]}" -DFEATURE_dbus=OFF -DFEATURE_framework=OFF -DFEATURE_icu=OFF -DFEATURE_opengl=OFF -DFEATURE_sql=OFF -DFEATURE_gssapi=OFF -DFEATURE_system_png=ON -DFEATURE_system_jpeg=ON -DFEATURE_system_zlib=ON -DFEATURE_system_freetype=ON -DFEATURE_system_harfbuzz=ON
make -C build "-j$NPROCS" make -C build "-j$NPROCS"
make -C build install make -C build install
cd .. cd ..
echo "Installing Qt SVG..." echo "Installing Qt SVG..."
rm -fr "qtsvg-everywhere-src-$QT"
tar xf "qtsvg-everywhere-src-$QT.tar.xz" tar xf "qtsvg-everywhere-src-$QT.tar.xz"
cd "qtsvg-everywhere-src-$QT" cd "qtsvg-everywhere-src-$QT"
cmake -B build -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DCMAKE_BUILD_TYPE=MinSizeRel mkdir build
make -C build "-j$NPROCS" cd build
make -C build install "$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}"
cd .. make "-j$NPROCS"
make install
cd ../..
echo "Installing Qt Image Formats..." echo "Installing Qt Image Formats..."
rm -fr "qtimageformats-everywhere-src-$QT"
tar xf "qtimageformats-everywhere-src-$QT.tar.xz" tar xf "qtimageformats-everywhere-src-$QT.tar.xz"
cd "qtimageformats-everywhere-src-$QT" cd "qtimageformats-everywhere-src-$QT"
cmake -B build -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DCMAKE_BUILD_TYPE=MinSizeRel mkdir build
make -C build "-j$NPROCS" cd build
make -C build install "$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}" -DFEATURE_system_webp=ON
cd .. make "-j$NPROCS"
make install
cd ../..
echo "Installing Qt Tools..." echo "Installing Qt Tools..."
rm -fr "qttools-everywhere-src-$QT"
tar xf "qttools-everywhere-src-$QT.tar.xz" tar xf "qttools-everywhere-src-$QT.tar.xz"
cd "qttools-everywhere-src-$QT" cd "qttools-everywhere-src-$QT"
# Linguist relies on a library in the Designer target, which takes 5-7 minutes to build on the CI mkdir build
# Avoid it by not building Linguist, since we only need the tools that come with it cd build
patch -u src/linguist/CMakeLists.txt <<EOF "$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}" -DFEATURE_assistant=OFF -DFEATURE_clang=OFF -DFEATURE_designer=ON -DFEATURE_kmap2qmap=OFF -DFEATURE_pixeltool=OFF -DFEATURE_pkg_config=OFF -DFEATURE_qev=OFF -DFEATURE_qtattributionsscanner=OFF -DFEATURE_qtdiag=OFF -DFEATURE_qtplugininfo=OFF
--- src/linguist/CMakeLists.txt make "-j$NPROCS"
+++ src/linguist/CMakeLists.txt make install
@@ -14,7 +14,7 @@ cd ../..
add_subdirectory(lrelease-pro)
add_subdirectory(lupdate) echo "Building shaderc..."
add_subdirectory(lupdate-pro) rm -fr "shaderc-$SHADERC"
-if(QT_FEATURE_process AND QT_FEATURE_pushbutton AND QT_FEATURE_toolbutton AND TARGET Qt::Widgets AND NOT no-png) tar xf "shaderc-$SHADERC.tar.gz"
+if(QT_FEATURE_process AND QT_FEATURE_pushbutton AND QT_FEATURE_toolbutton AND TARGET Qt::Widgets AND TARGET Qt::PrintSupport AND NOT no-png) cd "shaderc-$SHADERC"
add_subdirectory(linguist) cd third_party
endif() tar xf "../../shaderc-glslang-$SHADERC_GLSLANG.tar.gz"
EOF mv "glslang-$SHADERC_GLSLANG" "glslang"
cmake -B build -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DCMAKE_BUILD_TYPE=Release -DFEATURE_assistant=OFF -DFEATURE_clang=OFF -DFEATURE_designer=OFF -DFEATURE_kmap2qmap=OFF -DFEATURE_pixeltool=OFF -DFEATURE_pkg_config=OFF -DFEATURE_qev=OFF -DFEATURE_qtattributionsscanner=OFF -DFEATURE_qtdiag=OFF -DFEATURE_qtplugininfo=OFF tar xf "../../shaderc-spirv-headers-$SHADERC_SPIRVHEADERS.tar.gz"
mv "SPIRV-Headers-$SHADERC_SPIRVHEADERS" "spirv-headers"
tar xf "../../shaderc-spirv-tools-$SHADERC_SPIRVTOOLS.tar.gz"
mv "SPIRV-Tools-$SHADERC_SPIRVTOOLS" "spirv-tools"
cd ..
patch -p1 < "$SCRIPTDIR/../common/shaderc-changes.patch"
cmake "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_UNIVERSAL" -DSHADERC_SKIP_TESTS=ON -DSHADERC_SKIP_EXAMPLES=ON -DSHADERC_SKIP_COPYRIGHT_CHECK=ON -B build
make -C build "-j$NPROCS" make -C build "-j$NPROCS"
make -C build install make -C build install
cd .. cd ..
echo "Installing Qt Translations..." echo "Installing Qt Translations..."
rm -fr "qttranslations-everywhere-src-$QT"
tar xf "qttranslations-everywhere-src-$QT.tar.xz" tar xf "qttranslations-everywhere-src-$QT.tar.xz"
cd "qttranslations-everywhere-src-$QT" cd "qttranslations-everywhere-src-$QT"
cmake -B build -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DCMAKE_BUILD_TYPE=Release mkdir build
make -C build "-j$NPROCS" cd build
make -C build install "$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}"
cd .. make "-j$NPROCS"
make install
cd ../..
echo "Cleaning up..." echo "Cleaning up..."
cd .. cd ..
rm -r deps-build rm -rf deps-build

View File

@ -461,15 +461,15 @@
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
}, },
"node_modules/ws": { "node_modules/ws": {
"version": "8.2.3", "version": "8.17.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
"integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
"engines": { "engines": {
"node": ">=10.0.0" "node": ">=10.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"bufferutil": "^4.0.1", "bufferutil": "^4.0.1",
"utf-8-validate": "^5.0.2" "utf-8-validate": ">=5.0.2"
}, },
"peerDependenciesMeta": { "peerDependenciesMeta": {
"bufferutil": { "bufferutil": {
@ -842,9 +842,9 @@
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
}, },
"ws": { "ws": {
"version": "8.2.3", "version": "8.17.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
"integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
"requires": {} "requires": {}
} }
} }

View File

@ -0,0 +1,296 @@
@echo off
setlocal enabledelayedexpansion
echo Setting environment...
if exist "%ProgramFiles%\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsamd64_arm64.bat" (
call "%ProgramFiles%\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsamd64_arm64.bat"
) else if exist "%ProgramFiles%\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsamd64_arm64.bat" (
call "%ProgramFiles%\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsamd64_arm64.bat"
) else (
echo Visual Studio 2022 not found.
goto error
)
set SEVENZIP="C:\Program Files\7-Zip\7z.exe"
set PATCH="C:\Program Files\Git\usr\bin\patch.exe"
if defined DEBUG (
echo DEBUG=%DEBUG%
) else (
set DEBUG=1
)
pushd %~dp0
set "SCRIPTDIR=%CD%"
cd ..\..\..\..
mkdir deps-build
cd deps-build || goto error
set "BUILDDIR=%CD%"
cd ..
mkdir deps-arm64
cd deps-arm64 || goto error
set "INSTALLDIR=%CD%"
cd ..
cd deps || goto error
set "X64INSTALLDIR=%CD%"
cd ..
popd
echo SCRIPTDIR=%SCRIPTDIR%
echo BUILDDIR=%BUILDDIR%
echo INSTALLDIR=%INSTALLDIR%
cd "%BUILDDIR%"
set FREETYPE=2.13.3
set HARFBUZZ=10.0.1
set LIBJPEG=9f
set LIBPNG=1643
set LZ4=b8fd2d15309dd4e605070bd4486e26b6ef814e29
set QT=6.8.1
set QTMINOR=6.8
set SDL=SDL2-2.30.10
set WEBP=1.4.0
set ZLIB=1.3.1
set ZLIBSHORT=131
set ZSTD=1.5.6
set SHADERC=2024.1
set SHADERC_GLSLANG=142052fa30f9eca191aa9dcf65359fcaed09eeec
set SHADERC_SPIRVHEADERS=5e3ad389ee56fca27c9705d093ae5387ce404df4
set SHADERC_SPIRVTOOLS=dd4b663e13c07fea4fbb3f70c1c91c86731099f7
call :downloadfile "freetype-%FREETYPE%.tar.gz" https://sourceforge.net/projects/freetype/files/freetype2/%FREETYPE%/freetype-%FREETYPE%.tar.gz/download 5c3a8e78f7b24c20b25b54ee575d6daa40007a5f4eea2845861c3409b3021747 || goto error
call :downloadfile "harfbuzz-%HARFBUZZ%.zip" https://github.com/harfbuzz/harfbuzz/archive/refs/tags/%HARFBUZZ%.zip 8adf9f5a4b6022aa2744f45c89ce347df46fea8403e99f01d650b11c417d0aa8 || goto error
call :downloadfile "lpng%LIBPNG%.zip" https://download.sourceforge.net/libpng/lpng1643.zip fc466a1e638e635d6c66363bdf3f38555b81b0141d0b06ba45b49ccca327436d || goto error
call :downloadfile "jpegsr%LIBJPEG%.zip" https://ijg.org/files/jpegsr%LIBJPEG%.zip 6255da8c89e09d694e6800688c76145eb6870a76ac0d36c74fccd61b3940aafa || goto error
call :downloadfile "libwebp-%WEBP%.tar.gz" "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-%WEBP%.tar.gz" 61f873ec69e3be1b99535634340d5bde750b2e4447caa1db9f61be3fd49ab1e5 || goto error
call :downloadfile "lz4-%LZ4%.zip" "https://github.com/lz4/lz4/archive/%LZ4%.zip" 0c33119688d6b180c7e760b0acd70059222389cfd581632623784bee27e51a31 || goto error
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 14b06b30d3400953875e73b0c4771cad1483488a1ef816803610f22b32300ce8 || goto error
call :downloadfile "qtbase-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtbase-everywhere-src-%QT%.zip" e22d997bd15b795a176c8da62c8c1da0a674eb534e02f7c01ca507bf11bce0c3 || goto error
call :downloadfile "qtimageformats-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtimageformats-everywhere-src-%QT%.zip" 247a0a58039275a5a4fb499a600a90f66dc6e00321bb6f86a9b8d8020344d853 || goto error
call :downloadfile "qtsvg-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtsvg-everywhere-src-%QT%.zip" 57bd332e5550ff70a852560c591b786b6ba587c5e41cb5ef91038d82db137ab9 || goto error
call :downloadfile "qttools-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttools-everywhere-src-%QT%.zip" c65a89140f5d68137ffec67d631ec97002fb37077d9b4eb4ee45cbec39b1c38a || goto error
call :downloadfile "qttranslations-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttranslations-everywhere-src-%QT%.zip" 30a8e7773e1f274557e049a97f158b808f344247da03ae5240e4956c81d51cd5 || goto error
call :downloadfile "zlib%ZLIBSHORT%.zip" "https://zlib.net/zlib%ZLIBSHORT%.zip" 72af66d44fcc14c22013b46b814d5d2514673dda3d115e64b690c1ad636e7b17 || goto error
call :downloadfile "zstd-%ZSTD%.zip" "https://github.com/facebook/zstd/archive/refs/tags/v%ZSTD%.zip" 3b1c3b46e416d36931efd34663122d7f51b550c87f74de2d38249516fe7d8be5 || goto error
call :downloadfile "zstd-fd5f8106a58601a963ee816e6a57aa7c61fafc53.patch" https://github.com/facebook/zstd/commit/fd5f8106a58601a963ee816e6a57aa7c61fafc53.patch 8df152f4969b308546306c074628de761f0b80265de7de534e3822fab22d7535 || goto error
call :downloadfile "shaderc-%SHADERC%.zip" "https://github.com/google/shaderc/archive/refs/tags/v%SHADERC%.zip" 6c9f42ed6bf42750f5369b089909abfdcf0101488b4a1f41116d5159d00af8e7 || goto error
call :downloadfile "shaderc-glslang-%SHADERC_GLSLANG%.zip" "https://github.com/KhronosGroup/glslang/archive/%SHADERC_GLSLANG%.zip" 03ad8a6fa987af4653d0cfe6bdaed41bcf617f1366a151fb1574da75950cd3e8 || goto error
call :downloadfile "shaderc-spirv-headers-%SHADERC_SPIRVHEADERS%.zip" "https://github.com/KhronosGroup/SPIRV-Headers/archive/%SHADERC_SPIRVHEADERS%.zip" fa59a54334feaba5702b9c25724c3f4746123865769b36dd5a28d9ef5e9d39ab || goto error
call :downloadfile "shaderc-spirv-tools-%SHADERC_SPIRVTOOLS%.zip" "https://github.com/KhronosGroup/SPIRV-Tools/archive/%SHADERC_SPIRVTOOLS%.zip" bf385994c20293485b378c27dfdbd77a31b949deabccd9218a977f173eda9f6f || goto error
if %DEBUG%==1 (
echo Building debug and release libraries...
) else (
echo Building release libraries...
)
set FORCEPDB=-DCMAKE_SHARED_LINKER_FLAGS_RELEASE="/DEBUG"
set ARM64TOOLCHAIN=-DCMAKE_TOOLCHAIN_FILE="%SCRIPTDIR%\cmake-toolchain-windows-arm64.cmake"
echo Building Zlib...
rmdir /S /Q "zlib-%ZLIB%"
%SEVENZIP% x "zlib%ZLIBSHORT%.zip" || goto error
cd "zlib-%ZLIB%" || goto error
cmake %ARM64TOOLCHAIN% -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="%INSTALLDIR%" -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DBUILD_SHARED_LIBS=ON -DZLIB_BUILD_EXAMPLES=OFF -B build -G Ninja || goto error
cmake --build build --parallel || goto error
ninja -C build install || goto error
cd .. || goto error
echo Building libpng...
rmdir /S /Q "lpng%LIBPNG%"
%SEVENZIP% x "lpng%LIBPNG%.zip" || goto error
cd "lpng%LIBPNG%" || goto error
cmake %ARM64TOOLCHAIN% -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="%INSTALLDIR%" -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DBUILD_SHARED_LIBS=ON -DBUILD_SHARED_LIBS=ON -DPNG_TESTS=OFF -DPNG_STATIC=OFF -DPNG_SHARED=ON -DPNG_TOOLS=OFF -B build -G Ninja || goto error
cmake --build build --parallel || goto error
ninja -C build install || goto error
cd .. || goto error
echo Building libjpeg...
rmdir /S /Q "jpeg-%LIBJPEG%"
%SEVENZIP% x "jpegsr%LIBJPEG%.zip" || goto error
cd "jpeg-%LIBJPEG%" || goto error
%PATCH% -p1 < "%SCRIPTDIR%\libjpeg-cmake.patch" || goto error
cmake %ARM64TOOLCHAIN% -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="%INSTALLDIR%" -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DBUILD_SHARED_LIBS=ON -DBUILD_STATIC_LIBS=OFF -B build -G Ninja || goto error
cmake --build build --parallel || goto error
ninja -C build install || goto error
cd .. || goto error
echo Building LZ4...
rmdir /S /Q "lz4"
%SEVENZIP% x "lz4-%LZ4%.zip" || goto error
rename "lz4-%LZ4%" "lz4" || goto error
cd "lz4" || goto error
cmake %ARM64TOOLCHAIN% -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="%INSTALLDIR%" -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DBUILD_SHARED_LIBS=ON -DLZ4_BUILD_CLI=OFF -DLZ4_BUILD_LEGACY_LZ4C=OFF -DCMAKE_C_FLAGS="/wd4711 /wd5045" -B build-dir -G Ninja build/cmake || goto error
cmake --build build-dir --parallel || goto error
ninja -C build-dir install || goto error
cd ..
echo Building FreeType without HarfBuzz...
rmdir /S /Q "freetype-%FREETYPE%"
tar -xf "freetype-%FREETYPE%.tar.gz" || goto error
cd "freetype-%FREETYPE%" || goto error
cmake %ARM64TOOLCHAIN% -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="%INSTALLDIR%" -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DBUILD_SHARED_LIBS=ON -DFT_REQUIRE_ZLIB=TRUE -DFT_REQUIRE_PNG=TRUE -DFT_DISABLE_BZIP2=TRUE -DFT_DISABLE_BROTLI=TRUE -DFT_DISABLE_HARFBUZZ=TRUE -B build -G Ninja || goto error
cmake --build build --parallel || goto error
ninja -C build install || goto error
cd .. || goto error
echo Building HarfBuzz...
rmdir /S /Q "harfbuzz-%HARFBUZZ%"
%SEVENZIP% x "-x^!harfbuzz-%HARFBUZZ%\README" "harfbuzz-%HARFBUZZ%.zip" || goto error
cd "harfbuzz-%HARFBUZZ%" || goto error
cmake %ARM64TOOLCHAIN% -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="%INSTALLDIR%" -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DBUILD_SHARED_LIBS=ON -DHB_BUILD_UTILS=OFF -B build -G Ninja || goto error
cmake --build build --parallel || goto error
ninja -C build install || goto error
cd .. || goto error
echo Building FreeType with HarfBuzz...
rmdir /S /Q "freetype-%FREETYPE%"
tar -xf "freetype-%FREETYPE%.tar.gz" || goto error
cd "freetype-%FREETYPE%" || goto error
cmake %ARM64TOOLCHAIN% -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="%INSTALLDIR%" -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DBUILD_SHARED_LIBS=ON -DFT_REQUIRE_ZLIB=TRUE -DFT_REQUIRE_PNG=TRUE -DFT_DISABLE_BZIP2=TRUE -DFT_DISABLE_BROTLI=TRUE -DFT_REQUIRE_HARFBUZZ=TRUE -B build -G Ninja || goto error
cmake --build build --parallel || goto error
ninja -C build install || goto error
cd .. || goto error
echo Building Zstandard...
rmdir /S /Q "zstd-%ZSTD%"
%SEVENZIP% x "-x^!zstd-%ZSTD%\tests\cli-tests\bin" "zstd-%ZSTD%.zip" || goto error
cd "zstd-%ZSTD%"
%PATCH% -p1 < "..\zstd-fd5f8106a58601a963ee816e6a57aa7c61fafc53.patch" || goto error
cmake %ARM64TOOLCHAIN% -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="%INSTALLDIR%" -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DBUILD_SHARED_LIBS=ON -DZSTD_BUILD_SHARED=ON -DZSTD_BUILD_STATIC=OFF -DZSTD_BUILD_PROGRAMS=OFF -B build -G Ninja build/cmake
cmake --build build --parallel || goto error
ninja -C build install || goto error
cd .. || goto error
echo Building WebP...
rmdir /S /Q "libwebp-%WEBP%"
tar -xf "libwebp-%WEBP%.tar.gz" || goto error
cd "libwebp-%WEBP%" || goto error
cmake -B build %ARM64TOOLCHAIN% -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="%INSTALLDIR%" -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DWEBP_BUILD_ANIM_UTILS=OFF -DWEBP_BUILD_CWEBP=OFF -DWEBP_BUILD_DWEBP=OFF -DWEBP_BUILD_GIF2WEBP=OFF -DWEBP_BUILD_IMG2WEBP=OFF -DWEBP_BUILD_VWEBP=OFF -DWEBP_BUILD_WEBPINFO=OFF -DWEBP_BUILD_WEBPMUX=OFF -DWEBP_BUILD_EXTRAS=OFF -DBUILD_SHARED_LIBS=ON -G Ninja || goto error
cmake --build build --parallel || goto error
ninja -C build install || goto error
cd .. || goto error
echo Building SDL...
rmdir /S /Q "%SDL%"
%SEVENZIP% x "%SDL%.zip" || goto error
cd "%SDL%" || goto error
cmake -B build %ARM64TOOLCHAIN% -DCMAKE_BUILD_TYPE=Release %FORCEPDB% -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DBUILD_SHARED_LIBS=ON -DSDL_SHARED=ON -DSDL_STATIC=OFF -G Ninja || goto error
cmake --build build --parallel || goto error
ninja -C build install || goto error
copy build\SDL2.pdb "%INSTALLDIR%\bin" || goto error
cd .. || goto error
if %DEBUG%==1 (
set QTBUILDSPEC=-DCMAKE_CONFIGURATION_TYPES="Release;Debug" -G "Ninja Multi-Config"
) else (
set QTBUILDSPEC=-DCMAKE_BUILD_TYPE=Release -G Ninja
)
echo Building Qt base...
rmdir /S /Q "qtbase-everywhere-src-%QT%"
%SEVENZIP% x "qtbase-everywhere-src-%QT%.zip" || goto error
cd "qtbase-everywhere-src-%QT%" || goto error
cmake -B build %ARM64TOOLCHAIN% -DFEATURE_sql=OFF -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DQT_HOST_PATH="%X64INSTALLDIR%" %FORCEPDB% -DINPUT_gui=yes -DINPUT_widgets=yes -DINPUT_ssl=yes -DINPUT_openssl=no -DINPUT_schannel=yes -DFEATURE_system_png=ON -DFEATURE_system_jpeg=ON -DFEATURE_system_zlib=ON -DFEATURE_system_freetype=ON -DFEATURE_system_harfbuzz=ON %QTBUILDSPEC% || goto error
cmake --build build --parallel || goto error
ninja -C build install || goto error
cd .. || goto error
echo Building Qt SVG...
rmdir /S /Q "qtsvg-everywhere-src-%QT%"
%SEVENZIP% x "qtsvg-everywhere-src-%QT%.zip" || goto error
cd "qtsvg-everywhere-src-%QT%" || goto error
mkdir build || goto error
cd build || goto error
call "%INSTALLDIR%\bin\qt-configure-module.bat" .. -- %FORCEPDB% -DCMAKE_PREFIX_PATH="%INSTALLDIR%" || goto error
cmake --build . --parallel || goto error
ninja install || goto error
cd ..\.. || goto error
echo Building Qt Image Formats...
rmdir /S /Q "qtimageformats-everywhere-src-%QT%"
%SEVENZIP% x "qtimageformats-everywhere-src-%QT%.zip" || goto error
cd "qtimageformats-everywhere-src-%QT%" || goto error
mkdir build || goto error
cd build || goto error
call "%INSTALLDIR%\bin\qt-configure-module.bat" .. -- %FORCEPDB% -DCMAKE_PREFIX_PATH="%INSTALLDIR%" -DFEATURE_system_webp=ON || goto error
cmake --build . --parallel || goto error
ninja install || goto error
cd ..\.. || goto error
echo Building Qt Tools...
rmdir /S /Q "qtimageformats-everywhere-src-%QT%"
%SEVENZIP% x "qttools-everywhere-src-%QT%.zip" || goto error
cd "qttools-everywhere-src-%QT%" || goto error
mkdir build || goto error
cd build || goto error
call "%INSTALLDIR%\bin\qt-configure-module.bat" .. -- %FORCEPDB% -DFEATURE_assistant=OFF -DFEATURE_clang=OFF -DFEATURE_designer=OFF -DFEATURE_kmap2qmap=OFF -DFEATURE_pixeltool=OFF -DFEATURE_pkg_config=OFF -DFEATURE_qev=OFF -DFEATURE_qtattributionsscanner=OFF -DFEATURE_qtdiag=OFF -DFEATURE_qtplugininfo=OFF || goto error
cmake --build . --parallel || goto error
ninja install || goto error
cd ..\.. || goto error
echo Building Qt Translations...
rmdir /S /Q "qttranslations-everywhere-src-%QT%"
%SEVENZIP% x "qttranslations-everywhere-src-%QT%.zip" || goto error
cd "qttranslations-everywhere-src-%QT%" || goto error
mkdir build || goto error
cd build || goto error
call "%INSTALLDIR%\bin\qt-configure-module.bat" .. -- %FORCEPDB% || goto error
cmake --build . --parallel || goto error
ninja install || goto error
cd ..\.. || goto error
echo Building shaderc...
rmdir /S /Q "shaderc-%SHADERC%"
%SEVENZIP% x "shaderc-%SHADERC%.zip" || goto error
cd "shaderc-%SHADERC%" || goto error
cd third_party || goto error
%SEVENZIP% x "..\..\shaderc-glslang-%SHADERC_GLSLANG%.zip" || goto error
rename "glslang-%SHADERC_GLSLANG%" "glslang" || goto error
%SEVENZIP% x "..\..\shaderc-spirv-headers-%SHADERC_SPIRVHEADERS%.zip" || goto error
rename "SPIRV-Headers-%SHADERC_SPIRVHEADERS%" "spirv-headers" || goto error
%SEVENZIP% x "..\..\shaderc-spirv-tools-%SHADERC_SPIRVTOOLS%.zip" || goto error
rename "SPIRV-Tools-%SHADERC_SPIRVTOOLS%" "spirv-tools" || goto error
cd .. || goto error
%PATCH% -p1 < "%SCRIPTDIR%\..\common\shaderc-changes.patch" || goto error
cmake %ARM64TOOLCHAIN% -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="%INSTALLDIR%" -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DSHADERC_SKIP_TESTS=ON -DSHADERC_SKIP_EXAMPLES=ON -DSHADERC_SKIP_COPYRIGHT_CHECK=ON -DSHADERC_ENABLE_SHARED_CRT=ON -B build -G Ninja || goto error
cmake --build build --parallel || goto error
ninja -C build install || goto error
cd .. || goto error
echo Cleaning up...
cd ..
rd /S /Q deps-build
echo Exiting with success.
exit 0
:error
echo Failed with error #%errorlevel%.
pause
exit %errorlevel%
:downloadfile
if not exist "%~1" (
echo Downloading %~1 from %~2...
curl -L -o "%~1" "%~2" || goto error
)
rem based on https://gist.github.com/gsscoder/e22daefaff9b5d8ac16afb070f1a7971
set idx=0
for /f %%F in ('certutil -hashfile "%~1" SHA256') do (
set "out!idx!=%%F"
set /a idx += 1
)
set filechecksum=%out1%
if /i %~3==%filechecksum% (
echo Validated %~1.
exit /B 0
) else (
echo Expected %~3 got %filechecksum%.
exit /B 1
)

View File

@ -12,6 +12,7 @@ if exist "%ProgramFiles%\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Bu
) )
set SEVENZIP="C:\Program Files\7-Zip\7z.exe" set SEVENZIP="C:\Program Files\7-Zip\7z.exe"
set PATCH="C:\Program Files\Git\usr\bin\patch.exe"
if defined DEBUG ( if defined DEBUG (
echo DEBUG=%DEBUG% echo DEBUG=%DEBUG%
@ -35,20 +36,48 @@ echo SCRIPTDIR=%SCRIPTDIR%
echo BUILDDIR=%BUILDDIR% echo BUILDDIR=%BUILDDIR%
echo INSTALLDIR=%INSTALLDIR% echo INSTALLDIR=%INSTALLDIR%
set "PATH=%PATH%;%INSTALLDIR%\bin"
cd "%BUILDDIR%" cd "%BUILDDIR%"
set QT=6.5.2 set FREETYPE=2.13.3
set QTMINOR=6.5 set HARFBUZZ=10.0.1
set SDL=SDL2-2.28.2 set LIBJPEG=9f
set LIBPNG=1643
set LZ4=b8fd2d15309dd4e605070bd4486e26b6ef814e29
set QT=6.8.1
set QTMINOR=6.8
set SDL=SDL2-2.30.10
set WEBP=1.4.0
set ZLIB=1.3.1
set ZLIBSHORT=131
set ZSTD=1.5.6
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 22383a6b242bac072f949d2b3854cf04c6856cae7a87eaa78c60dd733b71e41e || goto error set SHADERC=2024.1
call :downloadfile "qtbase-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtbase-everywhere-src-%QT%.zip" f770a087e350d688441880d08ad2791465e5e3b9a0f8fc2cfbeb5dd305a11d50 || goto error set SHADERC_GLSLANG=142052fa30f9eca191aa9dcf65359fcaed09eeec
call :downloadfile "qtimageformats-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtimageformats-everywhere-src-%QT%.zip" 9757899b00eea4e6b65f81f922c0215c70969661567398d91da6639a50a788e7 || goto error set SHADERC_SPIRVHEADERS=5e3ad389ee56fca27c9705d093ae5387ce404df4
call :downloadfile "qtsvg-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtsvg-everywhere-src-%QT%.zip" 0546a6aa19f5e0188d1ba4a0e0a1423d22b7dc55ce8a614cc4aa65bfac506f74 || goto error set SHADERC_SPIRVTOOLS=dd4b663e13c07fea4fbb3f70c1c91c86731099f7
call :downloadfile "qttools-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttools-everywhere-src-%QT%.zip" 3148f4f263bf9930d89107eb44bc452481a5f8c6178459e26ecbf3c8dca3b5c7 || goto error
call :downloadfile "qttranslations-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttranslations-everywhere-src-%QT%.zip" 8b99046b54c40106d4e310be63b41331b717cfd8b42da4b4fc1c9169604be7fc || goto error
call :downloadfile "4b119f48f5cb5e1499f91a0791150231c47430d4.diff" "https://github.com/qt/qtbase/commit/4b119f48f5cb5e1499f91a0791150231c47430d4.diff" d86bd2bd4ee2aff5f5e97da027aa926178dca250d163427eb21503bb357730a5 || goto error call :downloadfile "freetype-%FREETYPE%.tar.gz" https://sourceforge.net/projects/freetype/files/freetype2/%FREETYPE%/freetype-%FREETYPE%.tar.gz/download 5c3a8e78f7b24c20b25b54ee575d6daa40007a5f4eea2845861c3409b3021747 || goto error
call :downloadfile "harfbuzz-%HARFBUZZ%.zip" https://github.com/harfbuzz/harfbuzz/archive/refs/tags/%HARFBUZZ%.zip 8adf9f5a4b6022aa2744f45c89ce347df46fea8403e99f01d650b11c417d0aa8 || goto error
call :downloadfile "lpng%LIBPNG%.zip" https://download.sourceforge.net/libpng/lpng1643.zip fc466a1e638e635d6c66363bdf3f38555b81b0141d0b06ba45b49ccca327436d || goto error
call :downloadfile "jpegsr%LIBJPEG%.zip" https://ijg.org/files/jpegsr%LIBJPEG%.zip 6255da8c89e09d694e6800688c76145eb6870a76ac0d36c74fccd61b3940aafa || goto error
call :downloadfile "libwebp-%WEBP%.tar.gz" "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-%WEBP%.tar.gz" 61f873ec69e3be1b99535634340d5bde750b2e4447caa1db9f61be3fd49ab1e5 || goto error
call :downloadfile "lz4-%LZ4%.zip" "https://github.com/lz4/lz4/archive/%LZ4%.zip" 0c33119688d6b180c7e760b0acd70059222389cfd581632623784bee27e51a31 || goto error
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 14b06b30d3400953875e73b0c4771cad1483488a1ef816803610f22b32300ce8 || goto error
call :downloadfile "qtbase-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtbase-everywhere-src-%QT%.zip" e22d997bd15b795a176c8da62c8c1da0a674eb534e02f7c01ca507bf11bce0c3 || goto error
call :downloadfile "qtimageformats-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtimageformats-everywhere-src-%QT%.zip" 247a0a58039275a5a4fb499a600a90f66dc6e00321bb6f86a9b8d8020344d853 || goto error
call :downloadfile "qtsvg-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtsvg-everywhere-src-%QT%.zip" 57bd332e5550ff70a852560c591b786b6ba587c5e41cb5ef91038d82db137ab9 || goto error
call :downloadfile "qttools-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttools-everywhere-src-%QT%.zip" c65a89140f5d68137ffec67d631ec97002fb37077d9b4eb4ee45cbec39b1c38a || goto error
call :downloadfile "qttranslations-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttranslations-everywhere-src-%QT%.zip" 30a8e7773e1f274557e049a97f158b808f344247da03ae5240e4956c81d51cd5 || goto error
call :downloadfile "zlib%ZLIBSHORT%.zip" "https://zlib.net/zlib%ZLIBSHORT%.zip" 72af66d44fcc14c22013b46b814d5d2514673dda3d115e64b690c1ad636e7b17 || goto error
call :downloadfile "zstd-%ZSTD%.zip" "https://github.com/facebook/zstd/archive/refs/tags/v%ZSTD%.zip" 3b1c3b46e416d36931efd34663122d7f51b550c87f74de2d38249516fe7d8be5 || goto error
call :downloadfile "zstd-fd5f8106a58601a963ee816e6a57aa7c61fafc53.patch" https://github.com/facebook/zstd/commit/fd5f8106a58601a963ee816e6a57aa7c61fafc53.patch 8df152f4969b308546306c074628de761f0b80265de7de534e3822fab22d7535 || goto error
call :downloadfile "shaderc-%SHADERC%.zip" "https://github.com/google/shaderc/archive/refs/tags/v%SHADERC%.zip" 6c9f42ed6bf42750f5369b089909abfdcf0101488b4a1f41116d5159d00af8e7 || goto error
call :downloadfile "shaderc-glslang-%SHADERC_GLSLANG%.zip" "https://github.com/KhronosGroup/glslang/archive/%SHADERC_GLSLANG%.zip" 03ad8a6fa987af4653d0cfe6bdaed41bcf617f1366a151fb1574da75950cd3e8 || goto error
call :downloadfile "shaderc-spirv-headers-%SHADERC_SPIRVHEADERS%.zip" "https://github.com/KhronosGroup/SPIRV-Headers/archive/%SHADERC_SPIRVHEADERS%.zip" fa59a54334feaba5702b9c25724c3f4746123865769b36dd5a28d9ef5e9d39ab || goto error
call :downloadfile "shaderc-spirv-tools-%SHADERC_SPIRVTOOLS%.zip" "https://github.com/KhronosGroup/SPIRV-Tools/archive/%SHADERC_SPIRVTOOLS%.zip" bf385994c20293485b378c27dfdbd77a31b949deabccd9218a977f173eda9f6f || goto error
if %DEBUG%==1 ( if %DEBUG%==1 (
echo Building debug and release libraries... echo Building debug and release libraries...
@ -56,18 +85,100 @@ if %DEBUG%==1 (
echo Building release libraries... echo Building release libraries...
) )
set FORCEPDB=-DCMAKE_SHARED_LINKER_FLAGS_RELEASE="/DEBUG"
echo Building Zlib...
rmdir /S /Q "zlib-%ZLIB%"
%SEVENZIP% x "zlib%ZLIBSHORT%.zip" || goto error
cd "zlib-%ZLIB%" || goto error
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="%INSTALLDIR%" -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DBUILD_SHARED_LIBS=ON -DZLIB_BUILD_EXAMPLES=OFF -B build -G Ninja || goto error
cmake --build build --parallel || goto error
ninja -C build install || goto error
cd .. || goto error
echo Building libpng...
rmdir /S /Q "lpng%LIBPNG%"
%SEVENZIP% x "lpng%LIBPNG%.zip" || goto error
cd "lpng%LIBPNG%" || goto error
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="%INSTALLDIR%" -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DBUILD_SHARED_LIBS=ON -DBUILD_SHARED_LIBS=ON -DPNG_TESTS=OFF -DPNG_STATIC=OFF -DPNG_SHARED=ON -DPNG_TOOLS=OFF -B build -G Ninja || goto error
cmake --build build --parallel || goto error
ninja -C build install || goto error
cd .. || goto error
echo Building libjpeg...
rmdir /S /Q "jpeg-%LIBJPEG%"
%SEVENZIP% x "jpegsr%LIBJPEG%.zip" || goto error
cd "jpeg-%LIBJPEG%" || goto error
%PATCH% -p1 < "%SCRIPTDIR%\libjpeg-cmake.patch" || goto error
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="%INSTALLDIR%" -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DBUILD_SHARED_LIBS=ON -DBUILD_STATIC_LIBS=OFF -B build -G Ninja || goto error
cmake --build build --parallel || goto error
ninja -C build install || goto error
cd .. || goto error
echo Building LZ4...
rmdir /S /Q "lz4"
%SEVENZIP% x "lz4-%LZ4%.zip" || goto error
rename "lz4-%LZ4%" "lz4" || goto error
cd "lz4" || goto error
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="%INSTALLDIR%" -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DBUILD_SHARED_LIBS=ON -DLZ4_BUILD_CLI=OFF -DLZ4_BUILD_LEGACY_LZ4C=OFF -DCMAKE_C_FLAGS="/wd4711 /wd5045" -B build-dir -G Ninja build/cmake || goto error
cmake --build build-dir --parallel || goto error
ninja -C build-dir install || goto error
cd ..
echo Building FreeType without HarfBuzz...
rmdir /S /Q "freetype-%FREETYPE%"
tar -xf "freetype-%FREETYPE%.tar.gz" || goto error
cd "freetype-%FREETYPE%" || goto error
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="%INSTALLDIR%" -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DBUILD_SHARED_LIBS=ON -DFT_REQUIRE_ZLIB=TRUE -DFT_REQUIRE_PNG=TRUE -DFT_DISABLE_BZIP2=TRUE -DFT_DISABLE_BROTLI=TRUE -DFT_DISABLE_HARFBUZZ=TRUE -B build -G Ninja || goto error
cmake --build build --parallel || goto error
ninja -C build install || goto error
cd .. || goto error
echo Building HarfBuzz...
rmdir /S /Q "harfbuzz-%HARFBUZZ%"
%SEVENZIP% x "-x^!harfbuzz-%HARFBUZZ%\README" "harfbuzz-%HARFBUZZ%.zip" || goto error
cd "harfbuzz-%HARFBUZZ%" || goto error
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="%INSTALLDIR%" -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DBUILD_SHARED_LIBS=ON -DHB_BUILD_UTILS=OFF -B build -G Ninja || goto error
cmake --build build --parallel || goto error
ninja -C build install || goto error
cd .. || goto error
echo Building FreeType with HarfBuzz...
rmdir /S /Q "freetype-%FREETYPE%"
tar -xf "freetype-%FREETYPE%.tar.gz" || goto error
cd "freetype-%FREETYPE%" || goto error
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="%INSTALLDIR%" -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DBUILD_SHARED_LIBS=ON -DFT_REQUIRE_ZLIB=TRUE -DFT_REQUIRE_PNG=TRUE -DFT_DISABLE_BZIP2=TRUE -DFT_DISABLE_BROTLI=TRUE -DFT_REQUIRE_HARFBUZZ=TRUE -B build -G Ninja || goto error
cmake --build build --parallel || goto error
ninja -C build install || goto error
cd .. || goto error
echo Building Zstandard...
rmdir /S /Q "zstd-%ZSTD%"
%SEVENZIP% x "-x^!zstd-%ZSTD%\tests\cli-tests\bin" "zstd-%ZSTD%.zip" || goto error
cd "zstd-%ZSTD%"
%PATCH% -p1 < "..\zstd-fd5f8106a58601a963ee816e6a57aa7c61fafc53.patch" || goto error
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="%INSTALLDIR%" -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DBUILD_SHARED_LIBS=ON -DZSTD_BUILD_SHARED=ON -DZSTD_BUILD_STATIC=OFF -DZSTD_BUILD_PROGRAMS=OFF -B build -G Ninja build/cmake
cmake --build build --parallel || goto error
ninja -C build install || goto error
cd .. || goto error
echo Building WebP...
rmdir /S /Q "libwebp-%WEBP%"
tar -xf "libwebp-%WEBP%.tar.gz" || goto error
cd "libwebp-%WEBP%" || goto error
cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="%INSTALLDIR%" -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DWEBP_BUILD_ANIM_UTILS=OFF -DWEBP_BUILD_CWEBP=OFF -DWEBP_BUILD_DWEBP=OFF -DWEBP_BUILD_GIF2WEBP=OFF -DWEBP_BUILD_IMG2WEBP=OFF -DWEBP_BUILD_VWEBP=OFF -DWEBP_BUILD_WEBPINFO=OFF -DWEBP_BUILD_WEBPMUX=OFF -DWEBP_BUILD_EXTRAS=OFF -DBUILD_SHARED_LIBS=ON -G Ninja || goto error
cmake --build build --parallel || goto error
ninja -C build install || goto error
cd .. || goto error
echo Building SDL... echo Building SDL...
rmdir /S /Q "%SDL%" rmdir /S /Q "%SDL%"
%SEVENZIP% x "%SDL%.zip" || goto error %SEVENZIP% x "%SDL%.zip" || goto error
cd "%SDL%" || goto error cd "%SDL%" || goto error
if %DEBUG%==1 ( cmake -B build -DCMAKE_BUILD_TYPE=Release %FORCEPDB% -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DBUILD_SHARED_LIBS=ON -DSDL_SHARED=ON -DSDL_STATIC=OFF -G Ninja || goto error
cmake -B build-debug -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DBUILD_SHARED_LIBS=ON -DSDL_SHARED=ON -DSDL_STATIC=OFF -G Ninja || goto error
cmake --build build-debug --parallel || goto error
ninja -C build-debug install || goto error
)
cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DBUILD_SHARED_LIBS=ON -DSDL_SHARED=ON -DSDL_STATIC=OFF -G Ninja || goto error
cmake --build build --parallel || goto error cmake --build build --parallel || goto error
ninja -C build install || goto error ninja -C build install || goto error
copy build\SDL2.pdb "%INSTALLDIR%\bin" || goto error
cd .. || goto error cd .. || goto error
if %DEBUG%==1 ( if %DEBUG%==1 (
@ -80,8 +191,14 @@ echo Building Qt base...
rmdir /S /Q "qtbase-everywhere-src-%QT%" rmdir /S /Q "qtbase-everywhere-src-%QT%"
%SEVENZIP% x "qtbase-everywhere-src-%QT%.zip" || goto error %SEVENZIP% x "qtbase-everywhere-src-%QT%.zip" || goto error
cd "qtbase-everywhere-src-%QT%" || goto error cd "qtbase-everywhere-src-%QT%" || goto error
"C:\Program Files\Git\usr\bin\patch" -p1 < ../4b119f48f5cb5e1499f91a0791150231c47430d4.diff || goto error
cmake -B build -DFEATURE_sql=OFF -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DINPUT_gui=yes -DINPUT_widgets=yes -DINPUT_ssl=yes -DINPUT_openssl=no -DINPUT_schannel=yes %QTBUILDSPEC% || goto error rem Disable the PCRE2 JIT, it doesn't properly verify AVX2 support.
%PATCH% -p1 < "%SCRIPTDIR%\qtbase-disable-pcre2-jit.patch" || goto error
rem Hackfix settings icon stretching
%PATCH% -p1 < "%SCRIPTDIR%\qtbase-fix-icon-stretch.patch" || goto error
cmake -B build -DFEATURE_sql=OFF -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" %FORCEPDB% -DINPUT_gui=yes -DINPUT_widgets=yes -DINPUT_ssl=yes -DINPUT_openssl=no -DINPUT_schannel=yes -DFEATURE_system_png=ON -DFEATURE_system_jpeg=ON -DFEATURE_system_zlib=ON -DFEATURE_system_freetype=ON -DFEATURE_system_harfbuzz=ON %QTBUILDSPEC% || goto error
cmake --build build --parallel || goto error cmake --build build --parallel || goto error
ninja -C build install || goto error ninja -C build install || goto error
cd .. || goto error cd .. || goto error
@ -92,7 +209,7 @@ rmdir /S /Q "qtsvg-everywhere-src-%QT%"
cd "qtsvg-everywhere-src-%QT%" || goto error cd "qtsvg-everywhere-src-%QT%" || goto error
mkdir build || goto error mkdir build || goto error
cd build || goto error cd build || goto error
call "%INSTALLDIR%\bin\qt-configure-module.bat" .. || goto error call "%INSTALLDIR%\bin\qt-configure-module.bat" .. -- %FORCEPDB% -DCMAKE_PREFIX_PATH="%INSTALLDIR%" || goto error
cmake --build . --parallel || goto error cmake --build . --parallel || goto error
ninja install || goto error ninja install || goto error
cd ..\.. || goto error cd ..\.. || goto error
@ -103,7 +220,7 @@ rmdir /S /Q "qtimageformats-everywhere-src-%QT%"
cd "qtimageformats-everywhere-src-%QT%" || goto error cd "qtimageformats-everywhere-src-%QT%" || goto error
mkdir build || goto error mkdir build || goto error
cd build || goto error cd build || goto error
call "%INSTALLDIR%\bin\qt-configure-module.bat" .. || goto error call "%INSTALLDIR%\bin\qt-configure-module.bat" .. -- %FORCEPDB% -DCMAKE_PREFIX_PATH="%INSTALLDIR%" -DFEATURE_system_webp=ON || goto error
cmake --build . --parallel || goto error cmake --build . --parallel || goto error
ninja install || goto error ninja install || goto error
cd ..\.. || goto error cd ..\.. || goto error
@ -114,7 +231,7 @@ rmdir /S /Q "qtimageformats-everywhere-src-%QT%"
cd "qttools-everywhere-src-%QT%" || goto error cd "qttools-everywhere-src-%QT%" || goto error
mkdir build || goto error mkdir build || goto error
cd build || goto error cd build || goto error
call "%INSTALLDIR%\bin\qt-configure-module.bat" .. -- -DFEATURE_assistant=OFF -DFEATURE_clang=OFF -DFEATURE_designer=OFF -DFEATURE_kmap2qmap=OFF -DFEATURE_pixeltool=OFF -DFEATURE_pkg_config=OFF -DFEATURE_qev=OFF -DFEATURE_qtattributionsscanner=OFF -DFEATURE_qtdiag=OFF -DFEATURE_qtplugininfo=OFF || goto error call "%INSTALLDIR%\bin\qt-configure-module.bat" .. -- %FORCEPDB% -DFEATURE_assistant=OFF -DFEATURE_clang=OFF -DFEATURE_designer=ON -DFEATURE_kmap2qmap=OFF -DFEATURE_pixeltool=OFF -DFEATURE_pkg_config=OFF -DFEATURE_qev=OFF -DFEATURE_qtattributionsscanner=OFF -DFEATURE_qtdiag=OFF -DFEATURE_qtplugininfo=OFF || goto error
cmake --build . --parallel || goto error cmake --build . --parallel || goto error
ninja install || goto error ninja install || goto error
cd ..\.. || goto error cd ..\.. || goto error
@ -125,11 +242,29 @@ rmdir /S /Q "qttranslations-everywhere-src-%QT%"
cd "qttranslations-everywhere-src-%QT%" || goto error cd "qttranslations-everywhere-src-%QT%" || goto error
mkdir build || goto error mkdir build || goto error
cd build || goto error cd build || goto error
call "%INSTALLDIR%\bin\qt-configure-module.bat" .. || goto error call "%INSTALLDIR%\bin\qt-configure-module.bat" .. -- %FORCEPDB% || goto error
cmake --build . --parallel || goto error cmake --build . --parallel || goto error
ninja install || goto error ninja install || goto error
cd ..\.. || goto error cd ..\.. || goto error
echo Building shaderc...
rmdir /S /Q "shaderc-%SHADERC%"
%SEVENZIP% x "shaderc-%SHADERC%.zip" || goto error
cd "shaderc-%SHADERC%" || goto error
cd third_party || goto error
%SEVENZIP% x "..\..\shaderc-glslang-%SHADERC_GLSLANG%.zip" || goto error
rename "glslang-%SHADERC_GLSLANG%" "glslang" || goto error
%SEVENZIP% x "..\..\shaderc-spirv-headers-%SHADERC_SPIRVHEADERS%.zip" || goto error
rename "SPIRV-Headers-%SHADERC_SPIRVHEADERS%" "spirv-headers" || goto error
%SEVENZIP% x "..\..\shaderc-spirv-tools-%SHADERC_SPIRVTOOLS%.zip" || goto error
rename "SPIRV-Tools-%SHADERC_SPIRVTOOLS%" "spirv-tools" || goto error
cd .. || goto error
%PATCH% -p1 < "%SCRIPTDIR%\..\common\shaderc-changes.patch" || goto error
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="%INSTALLDIR%" -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DSHADERC_SKIP_TESTS=ON -DSHADERC_SKIP_EXAMPLES=ON -DSHADERC_SKIP_COPYRIGHT_CHECK=ON -DSHADERC_ENABLE_SHARED_CRT=ON -B build -G Ninja || goto error
cmake --build build --parallel || goto error
ninja -C build install || goto error
cd .. || goto error
echo Cleaning up... echo Cleaning up...
cd .. cd ..
rd /S /Q deps-build rd /S /Q deps-build

View File

@ -0,0 +1,4 @@
set(CMAKE_CROSSCOMPILING TRUE)
set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_VERSION 10)
set(CMAKE_SYSTEM_PROCESSOR arm64)

View File

@ -0,0 +1,422 @@
diff -ruN jpeg-9f/CMakeLists.txt jpeg-9f-new/CMakeLists.txt
--- jpeg-9f/CMakeLists.txt 1970-01-01 10:00:00.000000000 +1000
+++ jpeg-9f-new/CMakeLists.txt 2024-03-23 21:29:37.969221600 +1000
@@ -0,0 +1,110 @@
+# CMake configuration for IJG libjpeg
+# Modified from https://github.com/csparker247/jpeg-cmake/blob/develop/resources/CMakeLists.txt
+# To install, copy this file and jconfig.h.in into a libjpeg source directory
+# Adapted from LuaDist's CMakeLists
+# https://github.com/LuaDist/libjpeg/blob/master/CMakeLists.txt
+
+cmake_minimum_required(VERSION 3.5)
+
+### Setup the project ###
+file(READ "configure.ac" ac)
+string(REGEX MATCH "AC_INIT\\(\\[libjpeg\\],\ \\[([0-9]*\\.[0-9]*\\.[0-9]*)\\]\\)" _ ${ac})
+set(version ${CMAKE_MATCH_1})
+project(libjpeg VERSION ${version} LANGUAGES C)
+set(C_STANDARD 99)
+
+### Include extra packages ###
+include(CMakeDependentOption)
+include(GNUInstallDirs)
+
+### Options ###
+option(BUILD_SHARED_LIBS "Build shared libraries" ON)
+option(BUILD_STATIC_LIBS "Build static libraries" ON)
+
+# Make sure we build at least one library
+if(NOT(BUILD_SHARED_LIBS OR BUILD_STATIC_LIBS))
+ message(FATAL_ERROR "Both static and shared libraries are disabled. Nothing will be built.")
+endif()
+
+### Configure jconfig.h ###
+include(ConfigureJConfig.cmake)
+
+### Build the object library ###
+set(PUBLIC_HDRS
+ jconfig.h
+ jerror.h
+ jmorecfg.h
+ jpeglib.h
+)
+
+set(SRCS
+ jaricom.c jcapimin.c jcapistd.c jcarith.c jccoefct.c jccolor.c
+ jcdctmgr.c jchuff.c jcinit.c jcmainct.c jcmarker.c jcmaster.c jcomapi.c
+ jcparam.c jcprepct.c jcsample.c jctrans.c jdapimin.c jdapistd.c jdarith.c
+ jdatadst.c jdatasrc.c jdcoefct.c jdcolor.c jddctmgr.c jdhuff.c jdinput.c
+ jdmainct.c jdmarker.c jdmaster.c jdmerge.c jdpostct.c jdsample.c jdtrans.c
+ jerror.c jfdctflt.c jfdctfst.c jfdctint.c jidctflt.c jidctfst.c jidctint.c
+ jmemmgr.c jmemnobs.c jquant1.c jquant2.c jutils.c
+)
+
+### Create static and shared libs ###
+if(BUILD_SHARED_LIBS)
+ add_library(libjpeg SHARED ${SRCS})
+ target_compile_definitions(libjpeg PRIVATE COMPILING_LIBJPEG)
+ target_include_directories(libjpeg
+ PUBLIC
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+ $<INSTALL_INTERFACE:include>
+ )
+ set_target_properties(libjpeg
+ PROPERTIES
+ VERSION ${PROJECT_VERSION_MAJOR}
+ POSITION_INDEPENDENT_CODE ON
+ CLEAN_DIRECT_OUTPUT ON
+ PUBLIC_HEADER "${PUBLIC_HDRS}"
+ )
+ install(TARGETS libjpeg
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
+ )
+endif()
+
+if(BUILD_STATIC_LIBS)
+ add_library(libjpeg_static STATIC $<TARGET_OBJECTS:jpeg_objs>)
+ target_include_directories(libjpeg_static
+ PUBLIC
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+ $<INSTALL_INTERFACE:include>
+ )
+ set_target_properties(libjpeg_static
+ PROPERTIES
+ OUTPUT_NAME jpeg
+ VERSION ${PROJECT_VERSION_MAJOR}
+ POSITION_INDEPENDENT_CODE ON
+ CLEAN_DIRECT_OUTPUT ON
+ PUBLIC_HEADER "${PUBLIC_HDRS}"
+ )
+ install(TARGETS libjpeg_static
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
+ )
+endif()
+
+# Configure and install pkg-config and libtool files
+if(BUILD_STATIC_LIBS OR BUILD_SHARED_LIBS)
+ # Compute the la file's weird version number
+ math(EXPR JPEG_CONF_VER_MAJOR "${PROJECT_VERSION_MAJOR} + ${PROJECT_VERSION_MINOR}")
+ set(JPEG_LIB_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
+ set(JPEG_LIB_VERSION_MINOR ${PROJECT_VERSION_MINOR})
+
+ # Configure and install
+ configure_file(libjpeg.pc.cmakein libjpeg.pc @ONLY)
+ install(FILES
+ ${CMAKE_CURRENT_BINARY_DIR}/libjpeg.pc
+ DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/pkgconfig
+ )
+endif()
+
diff -ruN jpeg-9f/ConfigureJConfig.cmake jpeg-9f-new/ConfigureJConfig.cmake
--- jpeg-9f/ConfigureJConfig.cmake 1970-01-01 10:00:00.000000000 +1000
+++ jpeg-9f-new/ConfigureJConfig.cmake 2024-03-23 21:09:37.223882900 +1000
@@ -0,0 +1,95 @@
+include(CheckIncludeFile)
+include(CheckSymbolExists)
+include(CheckTypeSize)
+
+# Define this if your system has an ANSI-conforming <stddef.h> file.
+check_include_file(stddef.h HAVE_STDDEF_H)
+
+# Define this if your system has an ANSI-conforming <stdlib.h> file.
+check_include_file(stdlib.h HAVE_STDLIB_H)
+
+# Does your compiler support function prototypes?
+# (If not, you also need to use ansi2knr, see install.txt)
+set(HAVE_PROTOTYPES true CACHE BOOL "Does your compiler support function prototypes?")
+
+# Does your compiler support the declaration "unsigned char" ?
+# How about "unsigned short" ?
+check_type_size("unsigned char" UNSIGNED_CHAR LANGUAGE C)
+check_type_size("unsigned short" UNSIGNED_SHORT LANGUAGE C)
+
+# Define "void" as "char" if your compiler doesn't know about type void.
+# NOTE: be sure to define void such that "void *" represents the most general
+# pointer type, e.g., that returned by malloc().
+# NOT IMPLEMENTED: Modify in jconfig.h.in #
+
+# Define "const" as empty if your compiler doesn't know the "const" keyword.
+# NOT IMPLEMENTED: Modify in jconfig.h.in #
+
+# Define this if an ordinary "char" type is unsigned.
+# If you're not sure, leaving it undefined will work at some cost in speed.
+# If you defined HAVE_UNSIGNED_CHAR then the speed difference is minimal.
+set(CHAR_IS_UNSIGNED false CACHE BOOL "char type is unsigned")
+
+# Define this if your system does not have an ANSI/SysV <string.h>,
+# but does have a BSD-style <strings.h>.
+set(NEED_BSD_STRINGS false CACHE BOOL "Use BSD <strings.h>. Use only if system lacks ANSI/SysV <strings.h>")
+
+# Define this if your system does not provide typedef size_t in any of the
+# ANSI-standard places (stddef.h, stdlib.h, or stdio.h), but places it in
+# <sys/types.h> instead.
+set(NEED_SYS_TYPES_H false CACHE BOOL "size_t defined in <sys/types.h>")
+
+# For 80x86 machines, you need to define NEED_FAR_POINTERS,
+# unless you are using a large-data memory model or 80386 flat-memory mode.
+# On less brain-damaged CPUs this symbol must not be defined.
+# (Defining this symbol causes large data structures to be referenced through
+# "far" pointers and to be allocated with a special version of malloc.)
+set(NEED_FAR_POINTERS false CACHE BOOL "Reference large data structures through 'far' pointers allocated with a special version of malloc")
+
+# Define this if your linker needs global names to be unique in less
+# than the first 15 characters.
+set(NEED_SHORT_EXTERNAL_NAMES false CACHE BOOL "Global names must be unique in less than the first 15 characters")
+
+# Although a real ANSI C compiler can deal perfectly well with pointers to
+# unspecified structures (see "incomplete types" in the spec), a few pre-ANSI
+# and pseudo-ANSI compilers get confused. To keep one of these bozos happy,
+# define INCOMPLETE_TYPES_BROKEN. This is not recommended unless you
+# actually get "missing structure definition" warnings or errors while
+# compiling the JPEG code.
+set(INCOMPLETE_TYPES_BROKEN false CACHE BOOL "Disable pointers to unspecified structures")
+
+# Define "boolean" as unsigned char, not enum, on Windows systems.
+# NOT IMPLEMENTED: Modify in jconfig.h.in #
+
+# The following options affect code selection within the JPEG library,
+# but they don't need to be visible to applications using the library.
+# To minimize application namespace pollution, the symbols won't be
+# defined unless JPEG_INTERNALS has been defined.
+#
+
+# Define this if your compiler implements ">>" on signed values as a logical
+# (unsigned) shift; leave it undefined if ">>" is a signed (arithmetic) shift,
+# which is the normal and rational definition.
+set(RIGHT_SHIFT_IS_UNSIGNED false CACHE BOOL "Compiler implements >> on signed values as a logical (unsigned) shift")
+
+# The remaining options do not affect the JPEG library proper,
+# but only the sample applications cjpeg/djpeg (see cjpeg.c, djpeg.c).
+# Other applications can ignore these.
+#
+
+mark_as_advanced(FORCE
+ HAVE_PROTOTYPES
+ HAVE_UNSIGNED_CHAR
+ HAVE_UNSIGNED_SHORT
+ CHAR_IS_UNSIGNED
+ HAVE_STDDEF_H
+ HAVE_STDLIB_H
+ NEED_BSD_STRINGS
+ NEED_SYS_TYPES_H
+ NEED_FAR_POINTERS
+ NEED_SHORT_EXTERNAL_NAMES
+ INCOMPLETE_TYPES_BROKEN
+ RIGHT_SHIFT_IS_UNSIGNED
+)
+
+configure_file(jconfig.h.in ${CMAKE_CURRENT_SOURCE_DIR}/jconfig.h)
diff -ruN jpeg-9f/jconfig.h.in jpeg-9f-new/jconfig.h.in
--- jpeg-9f/jconfig.h.in 1970-01-01 10:00:00.000000000 +1000
+++ jpeg-9f-new/jconfig.h.in 2024-03-23 21:06:05.204994600 +1000
@@ -0,0 +1,173 @@
+/*
+ * jconfig.h.in
+ *
+ * Copyright (C) 1991-1994, Thomas G. Lane.
+ * Modified 2009-2013 by Guido Vollbeding.
+ * This file is part of the Independent JPEG Group's software.
+ * For conditions of distribution and use, see the accompanying README file.
+ *
+ * This file is a modification of jconfig.txt from libjpeg. In addition to
+ * documenting the configuration options that are required to customize the
+ * JPEG software for a particular system, it is used by jpeg-cmake to configure
+ * jconfig.h
+ */
+
+
+/*
+ * These symbols indicate the properties of your machine or compiler.
+ * #define the symbol if yes, #undef it if no.
+ */
+
+/* Does your compiler support function prototypes?
+ * (If not, you also need to use ansi2knr, see install.txt)
+ */
+#cmakedefine HAVE_PROTOTYPES
+
+/* Does your compiler support the declaration "unsigned char" ?
+ * How about "unsigned short" ?
+ */
+#cmakedefine HAVE_UNSIGNED_CHAR
+#cmakedefine HAVE_UNSIGNED_SHORT
+
+/* Define "void" as "char" if your compiler doesn't know about type void.
+ * NOTE: be sure to define void such that "void *" represents the most general
+ * pointer type, e.g., that returned by malloc().
+ */
+/* #define void char */
+
+/* Define "const" as empty if your compiler doesn't know the "const" keyword.
+ */
+/* #define const */
+
+/* Define this if an ordinary "char" type is unsigned.
+ * If you're not sure, leaving it undefined will work at some cost in speed.
+ * If you defined HAVE_UNSIGNED_CHAR then the speed difference is minimal.
+ */
+#cmakedefine CHAR_IS_UNSIGNED
+
+/* Define this if your system has an ANSI-conforming <stddef.h> file.
+ */
+#cmakedefine HAVE_STDDEF_H
+
+/* Define this if your system has an ANSI-conforming <stdlib.h> file.
+ */
+#cmakedefine HAVE_STDLIB_H
+
+/* Define this if your system does not have an ANSI/SysV <string.h>,
+ * but does have a BSD-style <strings.h>.
+ */
+#cmakedefine NEED_BSD_STRINGS
+
+/* Define this if your system does not provide typedef size_t in any of the
+ * ANSI-standard places (stddef.h, stdlib.h, or stdio.h), but places it in
+ * <sys/types.h> instead.
+ */
+#cmakedefine NEED_SYS_TYPES_H
+
+/* For 80x86 machines, you need to define NEED_FAR_POINTERS,
+ * unless you are using a large-data memory model or 80386 flat-memory mode.
+ * On less brain-damaged CPUs this symbol must not be defined.
+ * (Defining this symbol causes large data structures to be referenced through
+ * "far" pointers and to be allocated with a special version of malloc.)
+ */
+#cmakedefine NEED_FAR_POINTERS
+
+/* Define this if your linker needs global names to be unique in less
+ * than the first 15 characters.
+ */
+#cmakedefine NEED_SHORT_EXTERNAL_NAMES
+
+/* Although a real ANSI C compiler can deal perfectly well with pointers to
+ * unspecified structures (see "incomplete types" in the spec), a few pre-ANSI
+ * and pseudo-ANSI compilers get confused. To keep one of these bozos happy,
+ * define INCOMPLETE_TYPES_BROKEN. This is not recommended unless you
+ * actually get "missing structure definition" warnings or errors while
+ * compiling the JPEG code.
+ */
+#cmakedefine INCOMPLETE_TYPES_BROKEN
+
+/* Define "boolean" as unsigned char, not enum, on Windows systems.
+ */
+#ifdef _WIN32
+#ifndef __RPCNDR_H__ /* don't conflict if rpcndr.h already read */
+typedef unsigned char boolean;
+#endif
+#ifndef FALSE /* in case these macros already exist */
+#define FALSE 0 /* values of boolean */
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+#define HAVE_BOOLEAN /* prevent jmorecfg.h from redefining it */
+#endif
+
+
+/*
+ * The following options affect code selection within the JPEG library,
+ * but they don't need to be visible to applications using the library.
+ * To minimize application namespace pollution, the symbols won't be
+ * defined unless JPEG_INTERNALS has been defined.
+ */
+
+#ifdef JPEG_INTERNALS
+
+/* Define this if your compiler implements ">>" on signed values as a logical
+ * (unsigned) shift; leave it undefined if ">>" is a signed (arithmetic) shift,
+ * which is the normal and rational definition.
+ */
+#cmakedefine RIGHT_SHIFT_IS_UNSIGNED
+
+
+#endif /* JPEG_INTERNALS */
+
+
+/*
+ * The remaining options do not affect the JPEG library proper,
+ * but only the sample applications cjpeg/djpeg (see cjpeg.c, djpeg.c).
+ * Other applications can ignore these.
+ */
+
+#ifdef JPEG_CJPEG_DJPEG
+
+/* These defines indicate which image (non-JPEG) file formats are allowed. */
+
+#cmakedefine BMP_SUPPORTED /* BMP image file format */
+#cmakedefine GIF_SUPPORTED /* GIF image file format */
+#cmakedefine PPM_SUPPORTED /* PBMPLUS PPM/PGM image file format */
+#cmakedefine RLE_SUPPORTED /* Utah RLE image file format */
+#cmakedefine TARGA_SUPPORTED /* Targa image file format */
+
+/*
+ * This defines the default output format for djpeg. Must be one of the FMT_*
+ * enums found in djpeg.c or djpegalt.c
+ */
+#cmakedefine DEFAULT_FMT @DEFAULT_FMT@
+
+/* Define this if you want to name both input and output files on the command
+ * line, rather than using stdout and optionally stdin. You MUST do this if
+ * your system can't cope with binary I/O to stdin/stdout. See comments at
+ * head of cjpeg.c or djpeg.c.
+ */
+#cmakedefine TWO_FILE_COMMANDLINE
+
+/* Define this if your system needs explicit cleanup of temporary files.
+ * This is crucial under MS-DOS, where the temporary "files" may be areas
+ * of extended memory; on most other systems it's not as important.
+ */
+#cmakedefine NEED_SIGNAL_CATCHER
+
+/* By default, we open image files with fopen(...,"rb") or fopen(...,"wb").
+ * This is necessary on systems that distinguish text files from binary files,
+ * and is harmless on most systems that don't. If you have one of the rare
+ * systems that complains about the "b" spec, define this symbol.
+ */
+#cmakedefine DONT_USE_B_MODE
+
+/* Define this if you want percent-done progress reports from cjpeg/djpeg.
+ */
+#cmakedefine PROGRESS_REPORT
+
+/* Define this if you *don't* want overwrite confirmation */
+#cmakedefine NO_OVERWRITE_CHECK
+
+#endif /* JPEG_CJPEG_DJPEG */
diff -ruN jpeg-9f/jmorecfg.h jpeg-9f-new/jmorecfg.h
--- jpeg-9f/jmorecfg.h 2022-03-31 19:41:26.000000000 +1000
+++ jpeg-9f-new/jmorecfg.h 2024-03-23 21:20:25.514814400 +1000
@@ -244,8 +244,13 @@
#define LOCAL(type) static type
/* a function referenced thru EXTERNs: */
#define GLOBAL(type) type
+
/* a reference to a GLOBAL function: */
-#define EXTERN(type) extern type
+#ifdef COMPILING_LIBJPEG
+#define EXTERN(type) __declspec(dllexport) extern type
+#else
+#define EXTERN(type) __declspec(dllimport) extern type
+#endif
/* This macro is used to declare a "method", that is, a function pointer.
diff -ruN jpeg-9f/libjpeg.pc.cmakein jpeg-9f-new/libjpeg.pc.cmakein
--- jpeg-9f/libjpeg.pc.cmakein 1970-01-01 10:00:00.000000000 +1000
+++ jpeg-9f-new/libjpeg.pc.cmakein 2024-03-23 21:06:13.922695100 +1000
@@ -0,0 +1,10 @@
+prefix=@CMAKE_INSTALL_PREFIX@
+exec_prefix=${prefix}
+libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@
+includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
+
+Name: libjpeg
+Description: Reads and writes JPEG files
+Version: @JPEG_LIB_VERSION_MAJOR@.@JPEG_LIB_VERSION_MINOR@.0
+Libs: -L${libdir} -ljpeg
+Cflags: -I${includedir}

View File

@ -0,0 +1,35 @@
--- qtbase/src/3rdparty/pcre2/CMakeLists.txt 2024-03-19 08:46:43.000000000 -0700
+++ qtbase/src/3rdparty/pcre2/CMakeLists.txt 2024-06-06 21:52:20.539619500 -0700
@@ -41,6 +41,7 @@
src/pcre2_xclass.c
DEFINES
HAVE_CONFIG_H
+ PCRE2_DISABLE_JIT
PUBLIC_DEFINES
PCRE2_CODE_UNIT_WIDTH=16
PUBLIC_INCLUDE_DIRECTORIES
@@ -52,23 +53,8 @@
## Scopes:
#####################################################################
-qt_internal_extend_target(BundledPcre2 CONDITION QNX OR UIKIT
- DEFINES
- PCRE2_DISABLE_JIT
-)
-
-qt_internal_extend_target(BundledPcre2 CONDITION (TEST_architecture_arch STREQUAL "arm") AND WIN32
- DEFINES
- PCRE2_DISABLE_JIT
-)
-
-qt_internal_extend_target(BundledPcre2 CONDITION (TEST_architecture_arch STREQUAL "arm64") AND WIN32
- DEFINES
- PCRE2_DISABLE_JIT
-)
-
if (APPLE)
- target_compile_options(BundledPcre2 PRIVATE "SHELL:-Xarch_arm64 -DPCRE2_DISABLE_JIT")
+ target_compile_options(BundledPcre2 PRIVATE "SHELL:-Xarch_arm64")
endif()
qt_internal_extend_target(BundledPcre2 CONDITION WIN32

View File

@ -0,0 +1,13 @@
diff --git a/src/plugins/styles/modernwindows/qwindowsvistastyle.cpp b/src/plugins/styles/modernwindows/qwindowsvistastyle.cpp
index 208420d7e8..26ef6f31ef 100644
--- a/src/plugins/styles/modernwindows/qwindowsvistastyle.cpp
+++ b/src/plugins/styles/modernwindows/qwindowsvistastyle.cpp
@@ -4232,8 +4232,6 @@ QRect QWindowsVistaStyle::subElementRect(SubElement element, const QStyleOption
case SE_ItemViewItemDecoration:
rect = QWindowsStyle::subElementRect(element, option, widget);
- if (qstyleoption_cast<const QStyleOptionViewItem *>(option))
- rect.adjust(-2, 0, 2, 0);
break;
case SE_ItemViewItemFocusRect:

View File

@ -8,7 +8,7 @@ jobs:
if: github.repository == 'PCSX2/pcsx2' if: github.repository == 'PCSX2/pcsx2'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/labeler@main - uses: actions/labeler@v5
with: with:
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -27,6 +27,7 @@ jobs:
uses: ./.github/workflows/windows_build_qt.yml uses: ./.github/workflows/windows_build_qt.yml
with: with:
jobName: "MSVC SSE4" jobName: "MSVC SSE4"
artifactPrefixName: "PCSX2-windows-Qt-x64-sse4-msvc"
configuration: Release configuration: Release
simd: "SSE4" simd: "SSE4"
secrets: inherit secrets: inherit
@ -38,6 +39,7 @@ jobs:
uses: ./.github/workflows/windows_build_qt.yml uses: ./.github/workflows/windows_build_qt.yml
with: with:
jobName: "MSVC AVX2" jobName: "MSVC AVX2"
artifactPrefixName: "PCSX2-windows-Qt-x64-avx2-msvc"
configuration: Release AVX2 configuration: Release AVX2
secrets: inherit secrets: inherit
@ -47,6 +49,7 @@ jobs:
uses: ./.github/workflows/windows_build_qt.yml uses: ./.github/workflows/windows_build_qt.yml
with: with:
jobName: "CMake MSVC" jobName: "CMake MSVC"
artifactPrefixName: "PCSX2-windows-Qt-x64-cmake-msvc"
configuration: CMake configuration: CMake
buildSystem: cmake buildSystem: cmake
secrets: inherit secrets: inherit
@ -58,6 +61,7 @@ jobs:
uses: ./.github/workflows/windows_build_qt.yml uses: ./.github/workflows/windows_build_qt.yml
with: with:
jobName: "Clang SSE4" jobName: "Clang SSE4"
artifactPrefixName: "PCSX2-windows-Qt-x64-sse4-clang"
configuration: Release Clang configuration: Release Clang
simd: "SSE4" simd: "SSE4"
secrets: inherit secrets: inherit
@ -69,6 +73,7 @@ jobs:
uses: ./.github/workflows/windows_build_qt.yml uses: ./.github/workflows/windows_build_qt.yml
with: with:
jobName: "Clang AVX2" jobName: "Clang AVX2"
artifactPrefixName: "PCSX2-windows-Qt-x64-avx2-clang"
configuration: Release Clang AVX2 configuration: Release Clang AVX2
secrets: inherit secrets: inherit
@ -78,6 +83,7 @@ jobs:
uses: ./.github/workflows/windows_build_qt.yml uses: ./.github/workflows/windows_build_qt.yml
with: with:
jobName: "CMake Clang" jobName: "CMake Clang"
artifactPrefixName: "PCSX2-windows-Qt-x64-cmake-clang"
configuration: CMake configuration: CMake
buildSystem: cmake buildSystem: cmake
cmakeFlags: -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DPCSX2_EXE_NAME=pcsx2-qt-clang cmakeFlags: -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DPCSX2_EXE_NAME=pcsx2-qt-clang

View File

@ -6,6 +6,9 @@ on:
jobName: jobName:
required: true required: true
type: string type: string
artifactPrefixName:
required: true
type: string
os: os:
required: false required: false
type: string type: string
@ -37,6 +40,10 @@ on:
required: false required: false
type: boolean type: boolean
default: false default: false
stableBuild:
required: false
type: boolean
default: false
jobs: jobs:
build_windows_qt: build_windows_qt:
@ -48,24 +55,30 @@ jobs:
POWERSHELL_TELEMETRY_OPTOUT: 1 POWERSHELL_TELEMETRY_OPTOUT: 1
steps: steps:
- name: Tempfix Clang
if: inputs.configuration == 'CMake'
run: choco uninstall llvm
- name: Checkout Repository - name: Checkout Repository
uses: actions/checkout@v4 uses: actions/checkout@v4
with:
submodules: recursive
# actions/checkout elides tags, fetch them primarily for releases # actions/checkout elides tags, fetch them primarily for releases
- name: Fetch Tags - name: Fetch Tags
if: ${{ inputs.fetchTags }} if: ${{ inputs.fetchTags }}
run: git fetch --tags --no-recurse-submodules run: git fetch --tags --no-recurse-submodules
- name: Add stable release identifier file
if: ${{ inputs.stableBuild == true || inputs.stableBuild == 'true' }}
shell: bash
run: |
echo "#define DEFAULT_UPDATER_CHANNEL \"stable\"" > ./pcsx2-qt/DefaultUpdaterChannel.h
cat ./pcsx2-qt/DefaultUpdaterChannel.h
- name: Prepare Artifact Metadata - name: Prepare Artifact Metadata
id: artifact-metadata id: artifact-metadata
shell: bash shell: bash
env: env:
OS: windows PREFIX: ${{ inputs.artifactPrefixName }}
BUILD_SYSTEM: ${{ inputs.buildSystem }}
ARCH: ${{ inputs.platform }}
SIMD: ${{ inputs.simd }}
EVENT_NAME: ${{ github.event_name }} EVENT_NAME: ${{ github.event_name }}
PR_TITLE: ${{ github.event.pull_request.title }} PR_TITLE: ${{ github.event.pull_request.title }}
PR_NUM: ${{ github.event.pull_request.number }} PR_NUM: ${{ github.event.pull_request.number }}
@ -74,7 +87,7 @@ jobs:
- name: Setup msbuild - name: Setup msbuild
if: inputs.configuration != 'CMake' if: inputs.configuration != 'CMake'
uses: microsoft/setup-msbuild@v1 uses: microsoft/setup-msbuild@v2
- name: Download patches - name: Download patches
shell: cmd shell: cmd
@ -84,10 +97,10 @@ jobs:
- name: Cache Dependencies - name: Cache Dependencies
id: cache-deps id: cache-deps
uses: actions/cache@v3 uses: actions/cache@v4
with: with:
path: deps path: deps
key: ${{ inputs.os }} ${{ inputs.platform }} deps ${{ hashFiles('.github/workflows/scripts/windows/build-dependencies.bat') }} key: ${{ inputs.os }} ${{ inputs.platform }} deps ${{ hashFiles('.github/workflows/scripts/windows/build-dependencies.bat', '.github/workflows/scripts/common/*.patch') }}
- name: Build Dependencies - name: Build Dependencies
if: steps.cache-deps.outputs.cache-hit != 'true' if: steps.cache-deps.outputs.cache-hit != 'true'
@ -119,10 +132,11 @@ jobs:
shell: cmd shell: cmd
run: | run: |
call "%ProgramFiles%\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" call "%ProgramFiles%\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
set PATH=%PATH%;%GITHUB_WORKSPACE%\bin
cmake --build build --config Release --target unittests cmake --build build --config Release --target unittests
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: ${{ steps.artifact-metadata.outputs.artifact-name }} name: ${{ steps.artifact-metadata.outputs.artifact-name }}
path: | path: |
@ -136,7 +150,7 @@ jobs:
!./bin/**/*.lib !./bin/**/*.lib
- name: Upload artifact - with symbols - name: Upload artifact - with symbols
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: ${{ steps.artifact-metadata.outputs.artifact-name }}-symbols name: ${{ steps.artifact-metadata.outputs.artifact-name }}-symbols
path: ./bin/**/*.pdb path: ./bin/**/*.pdb

29
.gitignore vendored
View File

@ -56,6 +56,14 @@ oprofile_data/
/UpgradeLog*.htm /UpgradeLog*.htm
/.vscode* /.vscode*
# Jetbrains Rider
/.idea*
# KDevelop 4 Workspace Configuration Files
*.kdev4
/.kdev4*
# Resources and docs in /bin are tracked
/bin/**/*.dll /bin/**/*.dll
/bin/**/*.dmp /bin/**/*.dmp
/bin/**/*.exp /bin/**/*.exp
@ -67,11 +75,13 @@ oprofile_data/
/bin/bios /bin/bios
/bin/cache /bin/cache
/bin/cheats /bin/cheats
/bin/patches
/bin/covers /bin/covers
/bin/dumps /bin/dumps
/bin/gamesettings /bin/gamesettings
/bin/help /bin/help
/bin/inis /bin/inis
/bin/inis/debuggersettings
/bin/logs /bin/logs
/bin/memcards /bin/memcards
/bin/plugins /bin/plugins
@ -80,7 +90,25 @@ oprofile_data/
/bin/textures /bin/textures
/bin/translations /bin/translations
/bin/inputprofiles /bin/inputprofiles
/bin/videos
/bin/portable.ini
/bin/portable.txt
# Resources and docs copied from /bin
/bin-arm64
# Manually added by user.
/bin/resources/patches.zip
# Resources that are runtime downloaded.
/bin/resources/fonts/NotoSansJP-Regular.ttf
/bin/resources/fonts/NotoSansKR-Regular.ttf
/bin/resources/fonts/NotoSansSC-Regular.ttf
/bin/resources/fonts/NotoSansTC-Regular.ttf
/deps-build
/deps /deps
/deps-arm64
/ipch /ipch
!/3rdparty/libjpeg/change.log !/3rdparty/libjpeg/change.log
@ -91,3 +119,4 @@ oprofile_data/
CMakeSettings.json CMakeSettings.json
/ci-artifacts/ /ci-artifacts/
/out/ /out/
/.cache/

32
.gitmodules vendored
View File

@ -1,32 +0,0 @@
[submodule "3rdparty/xz/xz"]
path = 3rdparty/xz/xz
url = https://github.com/PCSX2/xz.git
[submodule "3rdparty/gtest"]
path = 3rdparty/gtest
url = https://github.com/google/googletest.git
[submodule "3rdparty/fmt/fmt"]
path = 3rdparty/fmt/fmt
url = https://github.com/fmtlib/fmt.git
[submodule "3rdparty/libchdr/libchdr"]
path = 3rdparty/libchdr/libchdr
url = https://github.com/rtissera/libchdr.git
[submodule "3rdparty/wil"]
path = 3rdparty/wil
url = https://github.com/microsoft/wil.git
branch = master
[submodule "3rdparty/rapidyaml/rapidyaml"]
path = 3rdparty/rapidyaml/rapidyaml
url = https://github.com/biojppm/rapidyaml.git
branch = master
[submodule "3rdparty/glslang/glslang"]
path = 3rdparty/glslang/glslang
url = https://github.com/KhronosGroup/glslang.git
[submodule "3rdparty/vulkan-headers"]
path = 3rdparty/vulkan-headers
url = https://github.com/KhronosGroup/Vulkan-Headers.git
[submodule "3rdparty/zstd/zstd"]
path = 3rdparty/zstd/zstd
url = https://github.com/facebook/zstd.git
[submodule "3rdparty/rcheevos/rcheevos"]
path = 3rdparty/rcheevos/rcheevos
url = https://github.com/RetroAchievements/rcheevos.git

View File

@ -16,6 +16,23 @@
<WarningLevel>TurnOffAllWarnings</WarningLevel> <WarningLevel>TurnOffAllWarnings</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<CompileAs>Default</CompileAs> <CompileAs>Default</CompileAs>
<LanguageStandard>stdcpp20</LanguageStandard>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<MinimalRebuild>false</MinimalRebuild>
<ConformanceMode>true</ConformanceMode>
<!-- MSVC automatically adds __AVX__ and __AVX2__ appropriately -->
<PreprocessorDefinitions>_M_X86;__SSE4_1__;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<EnableEnhancedInstructionSet Condition="!$(Configuration.Contains(AVX2)) Or $(Configuration.Contains(Clang))">NotSet</EnableEnhancedInstructionSet>
<EnableEnhancedInstructionSet Condition="$(Configuration.Contains(AVX2)) And !$(Configuration.Contains(Clang))">AdvancedVectorExtensions2</EnableEnhancedInstructionSet>
<AdditionalOptions Condition="'$(Platform)'=='x64' And $(Configuration.Contains(Clang)) And !$(Configuration.Contains(AVX2))"> -march=nehalem %(AdditionalOptions)</AdditionalOptions>
<AdditionalOptions Condition="'$(Platform)'=='x64' And $(Configuration.Contains(Clang)) And $(Configuration.Contains(AVX2))"> -march=haswell %(AdditionalOptions)</AdditionalOptions>
<AdditionalOptions Condition="'$(Platform)'=='ARM64' And $(Configuration.Contains(Clang))"> -march=armv8.4-a %(AdditionalOptions)</AdditionalOptions>
<AdditionalOptions Condition="!$(Configuration.Contains(Clang))">%(AdditionalOptions) /Zc:externConstexpr /Zc:__cplusplus /Zo /utf-8</AdditionalOptions>
<!-- Force ThinLTO for Release builds, MSVC doesn't seem to do it otherwise. -->
<!-- Also due to include order, needs to be set here, rather than in CodeGen_Release.props -->
<AdditionalOptions Condition="$(Configuration.Contains(Clang)) And $(Configuration.Contains(Release))"> -flto=thin %(AdditionalOptions)</AdditionalOptions>
</ClCompile> </ClCompile>
</ItemDefinitionGroup> </ItemDefinitionGroup>
</Project> </Project>

41
3rdparty/ccc/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,41 @@
cmake_minimum_required(VERSION 3.14)
project(ccc)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
add_library(ccc STATIC
src/ccc/ast.cpp
src/ccc/ast.h
src/ccc/elf.cpp
src/ccc/elf.h
src/ccc/elf_symtab.cpp
src/ccc/elf_symtab.h
src/ccc/importer_flags.cpp
src/ccc/importer_flags.h
src/ccc/mdebug_analysis.cpp
src/ccc/mdebug_analysis.h
src/ccc/mdebug_importer.cpp
src/ccc/mdebug_importer.h
src/ccc/mdebug_section.cpp
src/ccc/mdebug_section.h
src/ccc/mdebug_symbols.cpp
src/ccc/mdebug_symbols.h
src/ccc/sndll.cpp
src/ccc/sndll.h
src/ccc/stabs.cpp
src/ccc/stabs.h
src/ccc/stabs_to_ast.cpp
src/ccc/stabs_to_ast.h
src/ccc/symbol_database.cpp
src/ccc/symbol_database.h
src/ccc/symbol_file.cpp
src/ccc/symbol_file.h
src/ccc/symbol_table.cpp
src/ccc/symbol_table.h
src/ccc/util.cpp
src/ccc/util.h
)
target_include_directories(ccc PUBLIC src)

37
3rdparty/ccc/README.md vendored Normal file
View File

@ -0,0 +1,37 @@
# Chaos Compiler Collection
This code was originally developed in the following repository and was copied
into PCSX2 by the author:
- [https://github.com/chaoticgd/ccc](https://github.com/chaoticgd/ccc)
It includes additional resources that are not present in the PCSX2 repository.
## Documentation
### DWARF (.debug) Section
- [DWARF Debugging Information Format](https://dwarfstd.org/doc/dwarf_1_1_0.pdf)
### MIPS Debug (.mdebug) Section
- [Third Eye Software and the MIPS symbol table (Peter Rowell)](http://datahedron.com/mips.html)
- [MIPS Mdebug Debugging Information (David Anderson, 1996)](https://www.prevanders.net/Mdebug.ps)
- MIPS Assembly Language Programmer's Guide, Symbol Table Chapter (Silicon Graphics, 1992)
- Tru64 UNIX Object File and Symbol Table Format Specification, Symbol Table Chapter
- `mdebugread.c` from gdb (reading)
- `ecoff.c` from gas (writing)
- `include/coff/sym.h` from binutils (headers)
### MIPS EABI
- [MIPS EABI](https://sourceware.org/legacy-ml/binutils/2003-06/msg00436.html)
### STABS
- [The "stabs" representation of debugging information (Julia Menapace, Jim Kingdon, and David MacKenzie, 1992-???)](https://sourceware.org/gdb/onlinedocs/stabs.html)
- `stabs.c` from binutils (reading)
- `stabsread.c` from gdb (reading)
- `dbxread.c` from gdb (reading)
- `dbxout.c` from gcc (writing)
- `stab.def` from gcc (symbol codes)

75
3rdparty/ccc/ccc.vcxproj vendored Normal file
View File

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(SolutionDir)common\vsprops\BaseProjectConfig.props" />
<Import Project="$(SolutionDir)common\vsprops\WinSDK.props" />
<PropertyGroup Label="Globals">
<ProjectGuid>{2589F8CE-EA77-4B73-911E-64074569795B}</ProjectGuid>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset Condition="!$(Configuration.Contains(Clang))">$(DefaultPlatformToolset)</PlatformToolset>
<PlatformToolset Condition="$(Configuration.Contains(Clang))">ClangCL</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
<WholeProgramOptimization Condition="$(Configuration.Contains(Release))">true</WholeProgramOptimization>
<UseDebugLibraries Condition="$(Configuration.Contains(Debug))">true</UseDebugLibraries>
<UseDebugLibraries Condition="!$(Configuration.Contains(Debug))">false</UseDebugLibraries>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings" />
<ImportGroup Label="PropertySheets">
<Import Project="..\DefaultProjectRootDir.props" />
<Import Project="..\3rdparty.props" />
<Import Condition="$(Configuration.Contains(Debug))" Project="..\..\common\vsprops\CodeGen_Debug.props" />
<Import Condition="$(Configuration.Contains(Devel))" Project="..\..\common\vsprops\CodeGen_Devel.props" />
<Import Condition="$(Configuration.Contains(Release))" Project="..\..\common\vsprops\CodeGen_Release.props" />
<Import Condition="!$(Configuration.Contains(Release))" Project="..\..\common\vsprops\IncrementalLinking.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<ClInclude Include="src\ccc\ast.h" />
<ClInclude Include="src\ccc\elf.h" />
<ClInclude Include="src\ccc\elf_symtab.h" />
<ClInclude Include="src\ccc\importer_flags.h" />
<ClInclude Include="src\ccc\mdebug_analysis.h" />
<ClInclude Include="src\ccc\mdebug_importer.h" />
<ClInclude Include="src\ccc\mdebug_section.h" />
<ClInclude Include="src\ccc\mdebug_symbols.h" />
<ClInclude Include="src\ccc\sndll.h" />
<ClInclude Include="src\ccc\stabs.h" />
<ClInclude Include="src\ccc\stabs_to_ast.h" />
<ClInclude Include="src\ccc\symbol_database.h" />
<ClInclude Include="src\ccc\symbol_file.h" />
<ClInclude Include="src\ccc\symbol_table.h" />
<ClInclude Include="src\ccc\util.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\ccc\ast.cpp" />
<ClCompile Include="src\ccc\elf.cpp" />
<ClCompile Include="src\ccc\elf_symtab.cpp" />
<ClCompile Include="src\ccc\importer_flags.cpp" />
<ClCompile Include="src\ccc\mdebug_analysis.cpp" />
<ClCompile Include="src\ccc\mdebug_importer.cpp" />
<ClCompile Include="src\ccc\mdebug_section.cpp" />
<ClCompile Include="src\ccc\mdebug_symbols.cpp" />
<ClCompile Include="src\ccc\sndll.cpp" />
<ClCompile Include="src\ccc\stabs.cpp" />
<ClCompile Include="src\ccc\stabs_to_ast.cpp" />
<ClCompile Include="src\ccc\symbol_database.cpp" />
<ClCompile Include="src\ccc\symbol_file.cpp" />
<ClCompile Include="src\ccc\symbol_table.cpp" />
<ClCompile Include="src\ccc\util.cpp" />
</ItemGroup>
<ItemDefinitionGroup>
<ClCompile>
<WarningLevel>TurnOffAllWarnings</WarningLevel>
<AdditionalIncludeDirectories>$(ProjectDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<LanguageStandard>stdcpp20</LanguageStandard>
</ClCompile>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />
</Project>

111
3rdparty/ccc/ccc.vcxproj.filters vendored Normal file
View File

@ -0,0 +1,111 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\ccc\ast.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\ccc\elf.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\ccc\elf_symtab.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\ccc\importer_flags.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\ccc\mdebug_analysis.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\ccc\mdebug_importer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\ccc\mdebug_section.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\ccc\mdebug_symbols.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\ccc\sndll.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\ccc\stabs.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\ccc\stabs_to_ast.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\ccc\symbol_database.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\ccc\symbol_file.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\ccc\symbol_table.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\ccc\util.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\ccc\ast.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\ccc\elf.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\ccc\elf_symtab.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\ccc\importer_flags.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\ccc\mdebug_analysis.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\ccc\mdebug_importer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\ccc\mdebug_section.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\ccc\mdebug_symbols.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\ccc\sndll.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\ccc\stabs.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\ccc\stabs_to_ast.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\ccc\symbol_database.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\ccc\symbol_file.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\ccc\symbol_table.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\ccc\util.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

562
3rdparty/ccc/src/ccc/ast.cpp vendored Normal file
View File

@ -0,0 +1,562 @@
// This file is part of the Chaos Compiler Collection.
// SPDX-License-Identifier: MIT
#include "ast.h"
#include "importer_flags.h"
#include "symbol_database.h"
namespace ccc::ast {
static bool compare_nodes_and_merge(
CompareResult& dest, const Node& node_lhs, const Node& node_rhs, const SymbolDatabase* database);
static bool try_to_match_wobbly_typedefs(
const Node& node_lhs, const Node& node_rhs, const SymbolDatabase& database);
void Node::set_access_specifier(AccessSpecifier specifier, u32 importer_flags)
{
if((importer_flags & NO_ACCESS_SPECIFIERS) == 0) {
access_specifier = specifier;
}
}
std::pair<Node*, DataType*> Node::physical_type(SymbolDatabase& database, s32 max_depth)
{
Node* type = this;
DataType* symbol = nullptr;
for(s32 i = 0; i < max_depth && type->descriptor == TYPE_NAME; i++) {
DataType* data_type = database.data_types.symbol_from_handle(type->as<TypeName>().data_type_handle);
if (!data_type || !data_type->type()) {
break;
}
type = data_type->type();
symbol = data_type;
}
return std::pair(type, symbol);
}
std::pair<const Node*, const DataType*> Node::physical_type(const SymbolDatabase& database, s32 max_depth) const
{
return const_cast<Node*>(this)->physical_type(const_cast<SymbolDatabase&>(database), max_depth);
}
const char* member_function_modifier_to_string(MemberFunctionModifier modifier)
{
switch(modifier) {
case MemberFunctionModifier::NONE: return "none";
case MemberFunctionModifier::STATIC: return "static";
case MemberFunctionModifier::VIRTUAL: return "virtual";
}
return "";
}
bool StructOrUnion::flatten_fields(
std::vector<FlatField>& output,
const DataType* symbol,
const SymbolDatabase& database,
bool skip_statics,
s32 base_offset,
s32 max_fields,
s32 max_depth) const
{
if(max_depth == 0) {
return false;
}
for(const std::unique_ptr<Node>& type_name : base_classes) {
if(type_name->descriptor != TYPE_NAME) {
continue;
}
s32 new_base_offset = base_offset + type_name->offset_bytes;
DataTypeHandle handle = type_name->as<TypeName>().data_type_handle;
const DataType* base_class_symbol = database.data_types.symbol_from_handle(handle);
if(!base_class_symbol || !base_class_symbol->type() || base_class_symbol->type()->descriptor != STRUCT_OR_UNION) {
continue;
}
const StructOrUnion& base_class = base_class_symbol->type()->as<StructOrUnion>();
if(!base_class.flatten_fields(output, base_class_symbol, database, skip_statics, new_base_offset, max_fields, max_depth - 1)) {
return false;
}
}
for(const std::unique_ptr<Node>& field : fields) {
if(skip_statics && field->storage_class == STORAGE_CLASS_STATIC) {
continue;
}
if((s32) output.size() >= max_fields) {
return false;
}
FlatField& flat = output.emplace_back();
flat.node = field.get();
flat.symbol = symbol;
flat.base_offset = base_offset;
}
return true;
}
const char* type_name_source_to_string(TypeNameSource source)
{
switch(source) {
case TypeNameSource::REFERENCE: return "reference";
case TypeNameSource::CROSS_REFERENCE: return "cross_reference";
case TypeNameSource::UNNAMED_THIS: return "this";
}
return "";
}
const char* forward_declared_type_to_string(ForwardDeclaredType type)
{
switch(type) {
case ForwardDeclaredType::STRUCT: return "struct";
case ForwardDeclaredType::UNION: return "union";
case ForwardDeclaredType::ENUM: return "enum";
}
return "";
}
DataTypeHandle TypeName::data_type_handle_unless_forward_declared() const
{
if(!is_forward_declared) {
return data_type_handle;
} else {
return DataTypeHandle();
}
}
CompareResult compare_nodes(
const Node& node_lhs, const Node& node_rhs, const SymbolDatabase* database, bool check_intrusive_fields)
{
CompareResult result = CompareResultType::MATCHES_NO_SWAP;
if(node_lhs.descriptor != node_rhs.descriptor) {
return CompareFailReason::DESCRIPTOR;
}
if(check_intrusive_fields) {
if(node_lhs.storage_class != node_rhs.storage_class) {
// In some cases we can determine that a type was typedef'd for C
// translation units, but not for C++ translation units, so we need
// to add a special case for that here.
if(node_lhs.storage_class == STORAGE_CLASS_TYPEDEF && node_rhs.storage_class == STORAGE_CLASS_NONE) {
result = CompareResultType::MATCHES_FAVOUR_LHS;
} else if(node_lhs.storage_class == STORAGE_CLASS_NONE && node_rhs.storage_class == STORAGE_CLASS_TYPEDEF) {
result = CompareResultType::MATCHES_FAVOUR_RHS;
} else {
return CompareFailReason::STORAGE_CLASS;
}
}
// Vtable pointers and constructors can sometimes contain type numbers
// that are different between translation units, so we don't want to
// compare them.
bool is_vtable_pointer = node_lhs.is_vtable_pointer && node_rhs.is_vtable_pointer;
bool is_numbered_constructor = node_lhs.name.starts_with("$_") && node_rhs.name.starts_with("$_");
if(node_lhs.name != node_rhs.name && !is_vtable_pointer && !is_numbered_constructor) {
return CompareFailReason::NAME;
}
if(node_lhs.offset_bytes != node_rhs.offset_bytes) {
return CompareFailReason::RELATIVE_OFFSET_BYTES;
}
if(node_lhs.size_bits != node_rhs.size_bits) {
return CompareFailReason::SIZE_BITS;
}
if(node_lhs.is_const != node_rhs.is_const) {
return CompareFailReason::CONSTNESS;
}
}
switch(node_lhs.descriptor) {
case ARRAY: {
const auto [lhs, rhs] = Node::as<Array>(node_lhs, node_rhs);
if(compare_nodes_and_merge(result, *lhs.element_type.get(), *rhs.element_type.get(), database)) {
return result;
}
if(lhs.element_count != rhs.element_count) {
return CompareFailReason::ARRAY_ELEMENT_COUNT;
}
break;
}
case BITFIELD: {
const auto [lhs, rhs] = Node::as<BitField>(node_lhs, node_rhs);
if(lhs.bitfield_offset_bits != rhs.bitfield_offset_bits) {
return CompareFailReason::BITFIELD_OFFSET_BITS;
}
if(compare_nodes_and_merge(result, *lhs.underlying_type.get(), *rhs.underlying_type.get(), database)) {
return result;
}
break;
}
case BUILTIN: {
const auto [lhs, rhs] = Node::as<BuiltIn>(node_lhs, node_rhs);
if(lhs.bclass != rhs.bclass) {
return CompareFailReason::BUILTIN_CLASS;
}
break;
}
case ENUM: {
const auto [lhs, rhs] = Node::as<Enum>(node_lhs, node_rhs);
if(lhs.constants != rhs.constants) {
return CompareFailReason::ENUM_CONSTANTS;
}
break;
}
case ERROR_NODE: {
break;
}
case FUNCTION: {
const auto [lhs, rhs] = Node::as<Function>(node_lhs, node_rhs);
if(lhs.return_type.has_value() != rhs.return_type.has_value()) {
return CompareFailReason::FUNCTION_RETURN_TYPE_HAS_VALUE;
}
if(lhs.return_type.has_value()) {
if(compare_nodes_and_merge(result, *lhs.return_type->get(), *rhs.return_type->get(), database)) {
return result;
}
}
if(lhs.parameters.has_value() && rhs.parameters.has_value()) {
if(lhs.parameters->size() != rhs.parameters->size()) {
return CompareFailReason::FUNCTION_PARAMAETER_COUNT;
}
for(size_t i = 0; i < lhs.parameters->size(); i++) {
if(compare_nodes_and_merge(result, *(*lhs.parameters)[i].get(), *(*rhs.parameters)[i].get(), database)) {
return result;
}
}
} else if(lhs.parameters.has_value() != rhs.parameters.has_value()) {
return CompareFailReason::FUNCTION_PARAMETERS_HAS_VALUE;
}
if(lhs.modifier != rhs.modifier) {
return CompareFailReason::FUNCTION_MODIFIER;
}
break;
}
case POINTER_OR_REFERENCE: {
const auto [lhs, rhs] = Node::as<PointerOrReference>(node_lhs, node_rhs);
if(lhs.is_pointer != rhs.is_pointer) {
return CompareFailReason::DESCRIPTOR;
}
if(compare_nodes_and_merge(result, *lhs.value_type.get(), *rhs.value_type.get(), database)) {
return result;
}
break;
}
case POINTER_TO_DATA_MEMBER: {
const auto [lhs, rhs] = Node::as<PointerToDataMember>(node_lhs, node_rhs);
if(compare_nodes_and_merge(result, *lhs.class_type.get(), *rhs.class_type.get(), database)) {
return result;
}
if(compare_nodes_and_merge(result, *lhs.member_type.get(), *rhs.member_type.get(), database)) {
return result;
}
break;
}
case STRUCT_OR_UNION: {
const auto [lhs, rhs] = Node::as<StructOrUnion>(node_lhs, node_rhs);
if(lhs.is_struct != rhs.is_struct) {
return CompareFailReason::DESCRIPTOR;
}
if(lhs.base_classes.size() != rhs.base_classes.size()) {
return CompareFailReason::BASE_CLASS_COUNT;
}
for(size_t i = 0; i < lhs.base_classes.size(); i++) {
if(compare_nodes_and_merge(result, *lhs.base_classes[i].get(), *rhs.base_classes[i].get(), database)) {
return result;
}
}
if(lhs.fields.size() != rhs.fields.size()) {
return CompareFailReason::FIELDS_SIZE;
}
for(size_t i = 0; i < lhs.fields.size(); i++) {
if(compare_nodes_and_merge(result, *lhs.fields[i].get(), *rhs.fields[i].get(), database)) {
return result;
}
}
if(lhs.member_functions.size() != rhs.member_functions.size()) {
return CompareFailReason::MEMBER_FUNCTION_COUNT;
}
for(size_t i = 0; i < lhs.member_functions.size(); i++) {
if(compare_nodes_and_merge(result, *lhs.member_functions[i].get(), *rhs.member_functions[i].get(), database)) {
return result;
}
}
break;
}
case TYPE_NAME: {
const auto [lhs, rhs] = Node::as<TypeName>(node_lhs, node_rhs);
// Don't check the source so that REFERENCE and CROSS_REFERENCE are
// treated as the same.
if(lhs.data_type_handle != rhs.data_type_handle) {
return CompareFailReason::TYPE_NAME;
}
const TypeName::UnresolvedStabs* lhs_unresolved_stabs = lhs.unresolved_stabs.get();
const TypeName::UnresolvedStabs* rhs_unresolved_stabs = rhs.unresolved_stabs.get();
if(lhs_unresolved_stabs && rhs_unresolved_stabs) {
if(lhs_unresolved_stabs->type_name != rhs_unresolved_stabs->type_name) {
return CompareFailReason::TYPE_NAME;
}
} else if(lhs_unresolved_stabs || rhs_unresolved_stabs) {
return CompareFailReason::TYPE_NAME;
}
break;
}
}
return result;
}
static bool compare_nodes_and_merge(
CompareResult& dest, const Node& node_lhs, const Node& node_rhs, const SymbolDatabase* database)
{
CompareResult result = compare_nodes(node_lhs, node_rhs, database, true);
if(database) {
if(result.type == CompareResultType::DIFFERS && try_to_match_wobbly_typedefs(node_lhs, node_rhs, *database)) {
result.type = CompareResultType::MATCHES_FAVOUR_LHS;
} else if(result.type == CompareResultType::DIFFERS && try_to_match_wobbly_typedefs(node_rhs, node_lhs, *database)) {
result.type = CompareResultType::MATCHES_FAVOUR_RHS;
}
}
if(dest.type != result.type) {
if(dest.type == CompareResultType::DIFFERS || result.type == CompareResultType::DIFFERS) {
// If any of the inner types differ, the outer type does too.
dest.type = CompareResultType::DIFFERS;
} else if(dest.type == CompareResultType::MATCHES_CONFUSED || result.type == CompareResultType::MATCHES_CONFUSED) {
// Propagate confusion.
dest.type = CompareResultType::MATCHES_CONFUSED;
} else if(dest.type == CompareResultType::MATCHES_FAVOUR_LHS && result.type == CompareResultType::MATCHES_FAVOUR_RHS) {
// One of the results favours the LHS node and the other favours the
// RHS node so we are confused.
dest.type = CompareResultType::MATCHES_CONFUSED;
} else if(dest.type == CompareResultType::MATCHES_FAVOUR_RHS && result.type == CompareResultType::MATCHES_FAVOUR_LHS) {
// One of the results favours the LHS node and the other favours the
// RHS node so we are confused.
dest.type = CompareResultType::MATCHES_CONFUSED;
} else if(dest.type == CompareResultType::MATCHES_FAVOUR_LHS || result.type == CompareResultType::MATCHES_FAVOUR_LHS) {
// One of the results favours the LHS node and the other is neutral
// so go with the LHS node.
dest.type = CompareResultType::MATCHES_FAVOUR_LHS;
} else if(dest.type == CompareResultType::MATCHES_FAVOUR_RHS || result.type == CompareResultType::MATCHES_FAVOUR_RHS) {
// One of the results favours the RHS node and the other is neutral
// so go with the RHS node.
dest.type = CompareResultType::MATCHES_FAVOUR_RHS;
}
}
if(dest.fail_reason == CompareFailReason::NONE) {
dest.fail_reason = result.fail_reason;
}
return dest.type == CompareResultType::DIFFERS;
}
static bool try_to_match_wobbly_typedefs(
const Node& type_name_node, const Node& raw_node, const SymbolDatabase& database)
{
// Detect if one side has a typedef when the other just has the plain type.
// This was previously a common reason why type deduplication would fail.
if(type_name_node.descriptor != TYPE_NAME) {
return false;
}
const TypeName& type_name = type_name_node.as<TypeName>();
if(const TypeName::UnresolvedStabs* unresolved_stabs = type_name.unresolved_stabs.get()) {
if(unresolved_stabs->referenced_file_handle == (u32) -1 || !unresolved_stabs->stabs_type_number.valid()) {
return false;
}
const SourceFile* source_file =
database.source_files.symbol_from_handle(unresolved_stabs->referenced_file_handle);
CCC_ASSERT(source_file);
auto handle = source_file->stabs_type_number_to_handle.find(unresolved_stabs->stabs_type_number);
if(handle != source_file->stabs_type_number_to_handle.end()) {
const DataType* referenced_type = database.data_types.symbol_from_handle(handle->second);
CCC_ASSERT(referenced_type && referenced_type->type());
// Don't compare 'intrusive' fields e.g. the offset.
CompareResult new_result = compare_nodes(*referenced_type->type(), raw_node, &database, false);
if(new_result.type != CompareResultType::DIFFERS) {
return true;
}
}
}
return false;
}
const char* compare_fail_reason_to_string(CompareFailReason reason)
{
switch(reason) {
case CompareFailReason::NONE: return "error";
case CompareFailReason::DESCRIPTOR: return "descriptor";
case CompareFailReason::STORAGE_CLASS: return "storage class";
case CompareFailReason::NAME: return "name";
case CompareFailReason::RELATIVE_OFFSET_BYTES: return "relative offset";
case CompareFailReason::ABSOLUTE_OFFSET_BYTES: return "absolute offset";
case CompareFailReason::BITFIELD_OFFSET_BITS: return "bitfield offset";
case CompareFailReason::SIZE_BITS: return "size";
case CompareFailReason::CONSTNESS: return "constness";
case CompareFailReason::ARRAY_ELEMENT_COUNT: return "array element count";
case CompareFailReason::BUILTIN_CLASS: return "builtin class";
case CompareFailReason::FUNCTION_RETURN_TYPE_HAS_VALUE: return "function return type has value";
case CompareFailReason::FUNCTION_PARAMAETER_COUNT: return "function paramaeter count";
case CompareFailReason::FUNCTION_PARAMETERS_HAS_VALUE: return "function parameter";
case CompareFailReason::FUNCTION_MODIFIER: return "function modifier";
case CompareFailReason::ENUM_CONSTANTS: return "enum constant";
case CompareFailReason::BASE_CLASS_COUNT: return "base class count";
case CompareFailReason::FIELDS_SIZE: return "fields size";
case CompareFailReason::MEMBER_FUNCTION_COUNT: return "member function count";
case CompareFailReason::VTABLE_GLOBAL: return "vtable global";
case CompareFailReason::TYPE_NAME: return "type name";
case CompareFailReason::VARIABLE_CLASS: return "variable class";
case CompareFailReason::VARIABLE_TYPE: return "variable type";
case CompareFailReason::VARIABLE_STORAGE: return "variable storage";
case CompareFailReason::VARIABLE_BLOCK: return "variable block";
}
return "";
}
const char* node_type_to_string(const Node& node)
{
switch(node.descriptor) {
case ARRAY: return "array";
case BITFIELD: return "bitfield";
case BUILTIN: return "builtin";
case ENUM: return "enum";
case ERROR_NODE: return "error";
case FUNCTION: return "function";
case POINTER_OR_REFERENCE: {
const PointerOrReference& pointer_or_reference = node.as<PointerOrReference>();
if(pointer_or_reference.is_pointer) {
return "pointer";
} else {
return "reference";
}
}
case POINTER_TO_DATA_MEMBER: return "pointer_to_data_member";
case STRUCT_OR_UNION: {
const StructOrUnion& struct_or_union = node.as<StructOrUnion>();
if(struct_or_union.is_struct) {
return "struct";
} else {
return "union";
}
}
case TYPE_NAME: return "type_name";
}
return "";
}
const char* storage_class_to_string(StorageClass storage_class)
{
switch(storage_class) {
case STORAGE_CLASS_NONE: return "none";
case STORAGE_CLASS_TYPEDEF: return "typedef";
case STORAGE_CLASS_EXTERN: return "extern";
case STORAGE_CLASS_STATIC: return "static";
case STORAGE_CLASS_AUTO: return "auto";
case STORAGE_CLASS_REGISTER: return "register";
}
return "";
}
const char* access_specifier_to_string(AccessSpecifier specifier)
{
switch(specifier) {
case AS_PUBLIC: return "public";
case AS_PROTECTED: return "protected";
case AS_PRIVATE: return "private";
}
return "";
}
const char* builtin_class_to_string(BuiltInClass bclass)
{
switch(bclass) {
case BuiltInClass::VOID_TYPE: return "void";
case BuiltInClass::UNSIGNED_8: return "8-bit unsigned integer";
case BuiltInClass::SIGNED_8: return "8-bit signed integer";
case BuiltInClass::UNQUALIFIED_8: return "8-bit integer";
case BuiltInClass::BOOL_8: return "8-bit boolean";
case BuiltInClass::UNSIGNED_16: return "16-bit unsigned integer";
case BuiltInClass::SIGNED_16: return "16-bit signed integer";
case BuiltInClass::UNSIGNED_32: return "32-bit unsigned integer";
case BuiltInClass::SIGNED_32: return "32-bit signed integer";
case BuiltInClass::FLOAT_32: return "32-bit floating point";
case BuiltInClass::UNSIGNED_64: return "64-bit unsigned integer";
case BuiltInClass::SIGNED_64: return "64-bit signed integer";
case BuiltInClass::FLOAT_64: return "64-bit floating point";
case BuiltInClass::UNSIGNED_128: return "128-bit unsigned integer";
case BuiltInClass::SIGNED_128: return "128-bit signed integer";
case BuiltInClass::UNQUALIFIED_128: return "128-bit integer";
case BuiltInClass::FLOAT_128: return "128-bit floating point";
}
return "";
}
s32 builtin_class_size(BuiltInClass bclass)
{
switch(bclass) {
case BuiltInClass::VOID_TYPE: return 0;
case BuiltInClass::UNSIGNED_8: return 1;
case BuiltInClass::SIGNED_8: return 1;
case BuiltInClass::UNQUALIFIED_8: return 1;
case BuiltInClass::BOOL_8: return 1;
case BuiltInClass::UNSIGNED_16: return 2;
case BuiltInClass::SIGNED_16: return 2;
case BuiltInClass::UNSIGNED_32: return 4;
case BuiltInClass::SIGNED_32: return 4;
case BuiltInClass::FLOAT_32: return 4;
case BuiltInClass::UNSIGNED_64: return 8;
case BuiltInClass::SIGNED_64: return 8;
case BuiltInClass::FLOAT_64: return 8;
case BuiltInClass::UNSIGNED_128: return 16;
case BuiltInClass::SIGNED_128: return 16;
case BuiltInClass::UNQUALIFIED_128: return 16;
case BuiltInClass::FLOAT_128: return 16;
}
return 0;
}
}

377
3rdparty/ccc/src/ccc/ast.h vendored Normal file
View File

@ -0,0 +1,377 @@
// This file is part of the Chaos Compiler Collection.
// SPDX-License-Identifier: MIT
#pragma once
#include "symbol_database.h"
namespace ccc::ast {
enum NodeDescriptor : u8 {
ARRAY,
BITFIELD,
BUILTIN,
ENUM,
ERROR_NODE,
FUNCTION,
POINTER_OR_REFERENCE,
POINTER_TO_DATA_MEMBER,
STRUCT_OR_UNION,
TYPE_NAME
};
enum AccessSpecifier {
AS_PUBLIC = 0,
AS_PROTECTED = 1,
AS_PRIVATE = 2
};
// To add a new type of node:
// 1. Add it to the NodeDescriptor enum.
// 2. Create a struct for it.
// 3. Add support for it in for_each_node.
// 4. Add support for it in compute_size_bytes_recursive.
// 5. Add support for it in compare_nodes.
// 6. Add support for it in node_type_to_string.
// 7. Add support for it in CppPrinter::ast_node.
// 8. Add support for it in write_json.
// 9. Add support for it in refine_node.
struct Node {
const NodeDescriptor descriptor;
u8 is_const : 1 = false;
u8 is_volatile : 1 = false;
u8 is_virtual_base_class : 1 = false;
u8 is_vtable_pointer : 1 = false;
u8 is_constructor_or_destructor : 1 = false;
u8 is_special_member_function : 1 = false;
u8 is_operator_member_function : 1 = false;
u8 cannot_compute_size : 1 = false;
u8 storage_class : 4 = STORAGE_CLASS_NONE;
u8 access_specifier : 2 = AS_PUBLIC;
s32 size_bytes = -1;
// If the name isn't populated for a given node, the name from the last
// ancestor to have one should be used i.e. when processing the tree you
// should pass the name down.
std::string name;
s32 offset_bytes = -1; // Offset relative to start of last inline struct/union.
s32 size_bits = -1; // Size stored in the .mdebug symbol table, may not be set.
Node(NodeDescriptor d) : descriptor(d) {}
Node(const Node& rhs) = default;
virtual ~Node() {}
template <typename SubType>
SubType& as() {
CCC_ASSERT(descriptor == SubType::DESCRIPTOR);
return *static_cast<SubType*>(this);
}
template <typename SubType>
const SubType& as() const {
CCC_ASSERT(descriptor == SubType::DESCRIPTOR);
return *static_cast<const SubType*>(this);
}
template <typename SubType>
static std::pair<const SubType&, const SubType&> as(const Node& lhs, const Node& rhs) {
CCC_ASSERT(lhs.descriptor == SubType::DESCRIPTOR && rhs.descriptor == SubType::DESCRIPTOR);
return std::pair<const SubType&, const SubType&>(static_cast<const SubType&>(lhs), static_cast<const SubType&>(rhs));
}
void set_access_specifier(AccessSpecifier specifier, u32 importer_flags);
// If this node is a type name, repeatedly resolve it to the type it's
// referencing, otherwise return (this, nullptr).
std::pair<Node*, DataType*> physical_type(SymbolDatabase& database, s32 max_depth = 100);
std::pair<const Node*, const DataType*> physical_type(const SymbolDatabase& database, s32 max_depth = 100) const;
};
struct Array : Node {
std::unique_ptr<Node> element_type;
s32 element_count = -1;
Array() : Node(DESCRIPTOR) {}
static const constexpr NodeDescriptor DESCRIPTOR = ARRAY;
};
struct BitField : Node {
s32 bitfield_offset_bits = -1; // Offset relative to the last byte (not the position of the underlying type!).
std::unique_ptr<Node> underlying_type;
BitField() : Node(DESCRIPTOR) {}
static const constexpr NodeDescriptor DESCRIPTOR = BITFIELD;
};
enum class BuiltInClass {
VOID_TYPE,
UNSIGNED_8, SIGNED_8, UNQUALIFIED_8, BOOL_8,
UNSIGNED_16, SIGNED_16,
UNSIGNED_32, SIGNED_32, FLOAT_32,
UNSIGNED_64, SIGNED_64, FLOAT_64,
UNSIGNED_128, SIGNED_128, UNQUALIFIED_128, FLOAT_128
};
struct BuiltIn : Node {
BuiltInClass bclass = BuiltInClass::VOID_TYPE;
BuiltIn() : Node(DESCRIPTOR) {}
static const constexpr NodeDescriptor DESCRIPTOR = BUILTIN;
};
struct Enum : Node {
std::vector<std::pair<s32, std::string>> constants;
Enum() : Node(DESCRIPTOR) {}
static const constexpr NodeDescriptor DESCRIPTOR = ENUM;
};
struct Error : Node {
std::string message;
Error() : Node(ERROR_NODE) {}
static const constexpr NodeDescriptor DESCRIPTOR = ERROR_NODE;
};
enum class MemberFunctionModifier {
NONE,
STATIC,
VIRTUAL
};
const char* member_function_modifier_to_string(MemberFunctionModifier modifier);
struct Function : Node {
std::optional<std::unique_ptr<Node>> return_type;
std::optional<std::vector<std::unique_ptr<Node>>> parameters;
MemberFunctionModifier modifier = MemberFunctionModifier::NONE;
s32 vtable_index = -1;
FunctionHandle definition_handle; // Filled in by fill_in_pointers_to_member_function_definitions.
Function() : Node(DESCRIPTOR) {}
static const constexpr NodeDescriptor DESCRIPTOR = FUNCTION;
};
struct PointerOrReference : Node {
bool is_pointer = true;
std::unique_ptr<Node> value_type;
PointerOrReference() : Node(DESCRIPTOR) {}
static const constexpr NodeDescriptor DESCRIPTOR = POINTER_OR_REFERENCE;
};
struct PointerToDataMember : Node {
std::unique_ptr<Node> class_type;
std::unique_ptr<Node> member_type;
PointerToDataMember() : Node(DESCRIPTOR) {}
static const constexpr NodeDescriptor DESCRIPTOR = POINTER_TO_DATA_MEMBER;
};
struct StructOrUnion : Node {
bool is_struct = true;
std::vector<std::unique_ptr<Node>> base_classes;
std::vector<std::unique_ptr<Node>> fields;
std::vector<std::unique_ptr<Node>> member_functions;
StructOrUnion() : Node(DESCRIPTOR) {}
static const constexpr NodeDescriptor DESCRIPTOR = STRUCT_OR_UNION;
struct FlatField {
// The field itself.
const Node* node;
// The symbol that owns the node.
const DataType* symbol;
// Offset of the innermost enclosing base class in the object.
s32 base_offset = 0;
};
// Generate a flat list of all the fields in this class as well as all the
// base classes recursively, but only until the max_fields or max_depth
// limits are reached. Return true if all the fields were enumerated.
bool flatten_fields(
std::vector<FlatField>& output,
const DataType* symbol,
const SymbolDatabase& database,
bool skip_statics,
s32 base_offset = 0,
s32 max_fields = 100000,
s32 max_depth = 100) const;
};
enum class TypeNameSource : u8 {
REFERENCE, // A STABS type reference.
CROSS_REFERENCE, // A STABS cross reference.
UNNAMED_THIS // A this parameter (or return type) referencing an unnamed type.
};
const char* type_name_source_to_string(TypeNameSource source);
enum class ForwardDeclaredType {
STRUCT,
UNION,
ENUM // Should be illegal but STABS supports cross references to enums so it's here.
};
const char* forward_declared_type_to_string(ForwardDeclaredType type);
struct TypeName : Node {
DataTypeHandle data_type_handle;
TypeNameSource source = TypeNameSource::REFERENCE;
bool is_forward_declared = false;
DataTypeHandle data_type_handle_unless_forward_declared() const;
struct UnresolvedStabs {
std::string type_name;
SourceFileHandle referenced_file_handle;
StabsTypeNumber stabs_type_number;
std::optional<ForwardDeclaredType> type;
};
std::unique_ptr<UnresolvedStabs> unresolved_stabs;
TypeName() : Node(DESCRIPTOR) {}
static const constexpr NodeDescriptor DESCRIPTOR = TYPE_NAME;
};
enum class CompareResultType {
MATCHES_NO_SWAP, // Both lhs and rhs are identical.
MATCHES_CONFUSED, // Both lhs and rhs are almost identical, and we don't which is better.
MATCHES_FAVOUR_LHS, // Both lhs and rhs are almost identical, but lhs is better.
MATCHES_FAVOUR_RHS, // Both lhs and rhs are almost identical, but rhs is better.
DIFFERS, // The two nodes differ substantially.
};
enum class CompareFailReason {
NONE,
DESCRIPTOR,
STORAGE_CLASS,
NAME,
RELATIVE_OFFSET_BYTES,
ABSOLUTE_OFFSET_BYTES,
BITFIELD_OFFSET_BITS,
SIZE_BITS,
CONSTNESS,
ARRAY_ELEMENT_COUNT,
BUILTIN_CLASS,
FUNCTION_RETURN_TYPE_HAS_VALUE,
FUNCTION_PARAMAETER_COUNT,
FUNCTION_PARAMETERS_HAS_VALUE,
FUNCTION_MODIFIER,
ENUM_CONSTANTS,
BASE_CLASS_COUNT,
FIELDS_SIZE,
MEMBER_FUNCTION_COUNT,
VTABLE_GLOBAL,
TYPE_NAME,
VARIABLE_CLASS,
VARIABLE_TYPE,
VARIABLE_STORAGE,
VARIABLE_BLOCK
};
struct CompareResult {
CompareResult(CompareResultType type) : type(type), fail_reason(CompareFailReason::NONE) {}
CompareResult(CompareFailReason reason) : type(CompareResultType::DIFFERS), fail_reason(reason) {}
CompareResultType type;
CompareFailReason fail_reason;
};
// Compare two AST nodes and their children recursively. This will only check
// fields that will be equal for two versions of the same type from different
// translation units.
CompareResult compare_nodes(const Node& lhs, const Node& rhs, const SymbolDatabase* database, bool check_intrusive_fields);
const char* compare_fail_reason_to_string(CompareFailReason reason);
const char* node_type_to_string(const Node& node);
const char* storage_class_to_string(StorageClass storage_class);
const char* access_specifier_to_string(AccessSpecifier specifier);
const char* builtin_class_to_string(BuiltInClass bclass);
s32 builtin_class_size(BuiltInClass bclass);
enum TraversalOrder {
PREORDER_TRAVERSAL,
POSTORDER_TRAVERSAL
};
enum ExplorationMode {
EXPLORE_CHILDREN,
DONT_EXPLORE_CHILDREN
};
template <typename ThisNode, typename Callback>
void for_each_node(ThisNode& node, TraversalOrder order, Callback callback)
{
if(order == PREORDER_TRAVERSAL && callback(node) == DONT_EXPLORE_CHILDREN) {
return;
}
switch(node.descriptor) {
case ARRAY: {
auto& array = node.template as<Array>();
for_each_node(*array.element_type.get(), order, callback);
break;
}
case BITFIELD: {
auto& bitfield = node.template as<BitField>();
for_each_node(*bitfield.underlying_type.get(), order, callback);
break;
}
case BUILTIN: {
break;
}
case ENUM: {
break;
}
case ERROR_NODE: {
break;
}
case FUNCTION: {
auto& func = node.template as<Function>();
if(func.return_type.has_value()) {
for_each_node(*func.return_type->get(), order, callback);
}
if(func.parameters.has_value()) {
for(auto& child : *func.parameters) {
for_each_node(*child.get(), order, callback);
}
}
break;
}
case POINTER_OR_REFERENCE: {
auto& pointer_or_reference = node.template as<PointerOrReference>();
for_each_node(*pointer_or_reference.value_type.get(), order, callback);
break;
}
case POINTER_TO_DATA_MEMBER: {
auto& pointer = node.template as<PointerToDataMember>();
for_each_node(*pointer.class_type.get(), order, callback);
for_each_node(*pointer.member_type.get(), order, callback);
break;
}
case STRUCT_OR_UNION: {
auto& struct_or_union = node.template as<StructOrUnion>();
for(auto& child : struct_or_union.base_classes) {
for_each_node(*child.get(), order, callback);
}
for(auto& child : struct_or_union.fields) {
for_each_node(*child.get(), order, callback);
}
for(auto& child : struct_or_union.member_functions) {
for_each_node(*child.get(), order, callback);
}
break;
}
case TYPE_NAME: {
break;
}
}
if(order == POSTORDER_TRAVERSAL) {
callback(node);
}
}
}

128
3rdparty/ccc/src/ccc/elf.cpp vendored Normal file
View File

@ -0,0 +1,128 @@
// This file is part of the Chaos Compiler Collection.
// SPDX-License-Identifier: MIT
#include "elf.h"
namespace ccc {
Result<ElfFile> ElfFile::parse(std::vector<u8> image)
{
ElfFile elf;
elf.image = std::move(image);
const ElfIdentHeader* ident = get_unaligned<ElfIdentHeader>(elf.image, 0);
CCC_CHECK(ident, "ELF ident header out of range.");
CCC_CHECK(ident->magic == CCC_FOURCC("\x7f\x45\x4c\x46"), "Not an ELF file.");
CCC_CHECK(ident->e_class == ElfIdentClass::B32, "Wrong ELF class (not 32 bit).");
const ElfFileHeader* header = get_unaligned<ElfFileHeader>(elf.image, sizeof(ElfIdentHeader));
CCC_CHECK(header, "ELF file header out of range.");
elf.file_header = *header;
const ElfSectionHeader* shstr_section_header =
get_unaligned<ElfSectionHeader>(elf.image, header->shoff + header->shstrndx * sizeof(ElfSectionHeader));
CCC_CHECK(shstr_section_header, "ELF section name header out of range.");
for(u32 i = 0; i < header->shnum; i++) {
u64 header_offset = header->shoff + i * sizeof(ElfSectionHeader);
const ElfSectionHeader* section_header = get_unaligned<ElfSectionHeader>(elf.image, header_offset);
CCC_CHECK(section_header, "ELF section header out of range.");
std::optional<std::string_view> name = get_string(elf.image, shstr_section_header->offset + section_header->name);
CCC_CHECK(name.has_value(), "ELF section name out of range.");
ElfSection& section = elf.sections.emplace_back();
section.name = *name;
section.header = *section_header;
}
for(u32 i = 0; i < header->phnum; i++) {
u64 header_offset = header->phoff + i * sizeof(ElfProgramHeader);
const ElfProgramHeader* program_header = get_unaligned<ElfProgramHeader>(elf.image, header_offset);
CCC_CHECK(program_header, "ELF program header out of range.");
elf.segments.emplace_back(*program_header);
}
return elf;
}
Result<void> ElfFile::create_section_symbols(
SymbolDatabase& database, const SymbolGroup& group) const
{
for(const ElfSection& section : sections) {
Address address = Address::non_zero(section.header.addr);
Result<Section*> symbol = database.sections.create_symbol(
section.name, address, group.source, group.module_symbol);
CCC_RETURN_IF_ERROR(symbol);
(*symbol)->set_size(section.header.size);
}
return Result<void>();
}
const ElfSection* ElfFile::lookup_section(const char* name) const
{
for(const ElfSection& section : sections) {
if(section.name == name) {
return &section;
}
}
return nullptr;
}
std::optional<u32> ElfFile::file_offset_to_virtual_address(u32 file_offset) const
{
for(const ElfProgramHeader& segment : segments) {
if(file_offset >= segment.offset && file_offset < segment.offset + segment.filesz) {
return segment.vaddr + file_offset - segment.offset;
}
}
return std::nullopt;
}
const ElfProgramHeader* ElfFile::entry_point_segment() const
{
const ccc::ElfProgramHeader* entry_segment = nullptr;
for(const ccc::ElfProgramHeader& segment : segments) {
if(file_header.entry >= segment.vaddr && file_header.entry < segment.vaddr + segment.filesz) {
entry_segment = &segment;
}
}
return entry_segment;
}
std::optional<std::span<const u8>> ElfFile::get_virtual(u32 address, u32 size) const
{
u32 end_address = address + size;
if(end_address >= address) {
for(const ElfProgramHeader& segment : segments) {
if(address >= segment.vaddr && end_address <= segment.vaddr + segment.filesz) {
size_t begin_offset = segment.offset + (address - segment.vaddr);
size_t end_offset = begin_offset + size;
if(begin_offset <= image.size() && end_offset <= image.size()) {
return std::span<const u8>(image.data() + begin_offset, image.data() + end_offset);
}
}
}
}
return std::nullopt;
}
bool ElfFile::copy_virtual(u8* dest, u32 address, u32 size) const
{
std::optional<std::span<const u8>> block = get_virtual(address, size);
if(!block.has_value()) {
return false;
}
memcpy(dest, block->data(), size);
return true;
}
}

160
3rdparty/ccc/src/ccc/elf.h vendored Normal file
View File

@ -0,0 +1,160 @@
// This file is part of the Chaos Compiler Collection.
// SPDX-License-Identifier: MIT
#pragma once
#include "symbol_database.h"
namespace ccc {
enum class ElfIdentClass : u8 {
B32 = 0x1,
B64 = 0x2
};
CCC_PACKED_STRUCT(ElfIdentHeader,
/* 0x0 */ u32 magic; // 7f 45 4c 46
/* 0x4 */ ElfIdentClass e_class;
/* 0x5 */ u8 endianess;
/* 0x6 */ u8 version;
/* 0x7 */ u8 os_abi;
/* 0x8 */ u8 abi_version;
/* 0x9 */ u8 pad[7];
)
enum class ElfFileType : u16 {
NONE = 0x00,
REL = 0x01,
EXEC = 0x02,
DYN = 0x03,
CORE = 0x04,
LOOS = 0xfe00,
HIOS = 0xfeff,
LOPROC = 0xff00,
HIPROC = 0xffff
};
enum class ElfMachine : u16 {
MIPS = 0x08
};
CCC_PACKED_STRUCT(ElfFileHeader,
/* 0x10 */ ElfFileType type;
/* 0x12 */ ElfMachine machine;
/* 0x14 */ u32 version;
/* 0x18 */ u32 entry;
/* 0x1c */ u32 phoff;
/* 0x20 */ u32 shoff;
/* 0x24 */ u32 flags;
/* 0x28 */ u16 ehsize;
/* 0x2a */ u16 phentsize;
/* 0x2c */ u16 phnum;
/* 0x2e */ u16 shentsize;
/* 0x30 */ u16 shnum;
/* 0x32 */ u16 shstrndx;
)
enum class ElfSectionType : u32 {
NULL_SECTION = 0x0,
PROGBITS = 0x1,
SYMTAB = 0x2,
STRTAB = 0x3,
RELA = 0x4,
HASH = 0x5,
DYNAMIC = 0x6,
NOTE = 0x7,
NOBITS = 0x8,
REL = 0x9,
SHLIB = 0xa,
DYNSYM = 0xb,
INIT_ARRAY = 0xe,
FINI_ARRAY = 0xf,
PREINIT_ARRAY = 0x10,
GROUP = 0x11,
SYMTAB_SHNDX = 0x12,
NUM = 0x13,
LOOS = 0x60000000,
MIPS_DEBUG = 0x70000005
};
CCC_PACKED_STRUCT(ElfSectionHeader,
/* 0x00 */ u32 name;
/* 0x04 */ ElfSectionType type;
/* 0x08 */ u32 flags;
/* 0x0c */ u32 addr;
/* 0x10 */ u32 offset;
/* 0x14 */ u32 size;
/* 0x18 */ u32 link;
/* 0x1c */ u32 info;
/* 0x20 */ u32 addralign;
/* 0x24 */ u32 entsize;
)
struct ElfSection {
std::string name;
ElfSectionHeader header;
};
CCC_PACKED_STRUCT(ElfProgramHeader,
/* 0x00 */ u32 type;
/* 0x04 */ u32 offset;
/* 0x08 */ u32 vaddr;
/* 0x0c */ u32 paddr;
/* 0x10 */ u32 filesz;
/* 0x14 */ u32 memsz;
/* 0x18 */ u32 flags;
/* 0x1c */ u32 align;
)
struct ElfFile {
ElfFileHeader file_header;
std::vector<u8> image;
std::vector<ElfSection> sections;
std::vector<ElfProgramHeader> segments;
// Parse the ELF file header, section headers and program headers.
static Result<ElfFile> parse(std::vector<u8> image);
// Create a section object for each section header in the ELF file.
Result<void> create_section_symbols(SymbolDatabase& database, const SymbolGroup& group) const;
const ElfSection* lookup_section(const char* name) const;
std::optional<u32> file_offset_to_virtual_address(u32 file_offset) const;
// Find the program header for the segment that contains the entry point.
const ElfProgramHeader* entry_point_segment() const;
// Retrieve a block of data in an ELF file given its address and size.
std::optional<std::span<const u8>> get_virtual(u32 address, u32 size) const;
// Copy a block of data in an ELF file to the destination buffer given its
// address and size.
bool copy_virtual(u8* dest, u32 address, u32 size) const;
// Retrieve an object of type T from an ELF file given its address.
template <typename T>
std::optional<T> get_object_virtual(u32 address) const
{
std::optional<std::span<const u8>> result = get_virtual(address, sizeof(T));
if(!result.has_value()) {
return std::nullopt;
}
return *(T*) result->data();
}
// Retrieve an array of objects of type T from an ELF file given its
// address and element count.
template <typename T>
std::optional<std::span<const T>> get_array_virtual(u32 address, u32 element_count) const
{
std::optional<std::span<const u8>> result = get_virtual(address, element_count * sizeof(T));
if(!result.has_value()) {
return std::nullopt;
}
return std::span<const T>((T*) result->data(), (T*) (result->data() + result->size()));
}
};
}

214
3rdparty/ccc/src/ccc/elf_symtab.cpp vendored Normal file
View File

@ -0,0 +1,214 @@
// This file is part of the Chaos Compiler Collection.
// SPDX-License-Identifier: MIT
#include "elf_symtab.h"
#include "importer_flags.h"
namespace ccc::elf {
enum class SymbolBind : u8 {
LOCAL = 0,
GLOBAL = 1,
WEAK = 2,
NUM = 3,
GNU_UNIQUE = 10
};
enum class SymbolType : u8 {
NOTYPE = 0,
OBJECT = 1,
FUNC = 2,
SECTION = 3,
FILE = 4,
COMMON = 5,
TLS = 6,
NUM = 7,
GNU_IFUNC = 10
};
enum class SymbolVisibility {
DEFAULT = 0,
INTERNAL = 1,
HIDDEN = 2,
PROTECTED = 3
};
CCC_PACKED_STRUCT(Symbol,
/* 0x0 */ u32 name;
/* 0x4 */ u32 value;
/* 0x8 */ u32 size;
/* 0xc */ u8 info;
/* 0xd */ u8 other;
/* 0xe */ u16 shndx;
SymbolType type() const { return (SymbolType) (info & 0xf); }
SymbolBind bind() const { return (SymbolBind) (info >> 4); }
SymbolVisibility visibility() const { return (SymbolVisibility) (other & 0x3); }
)
static const char* symbol_bind_to_string(SymbolBind bind);
static const char* symbol_type_to_string(SymbolType type);
static const char* symbol_visibility_to_string(SymbolVisibility visibility);
Result<void> import_symbols(
SymbolDatabase& database,
const SymbolGroup& group,
std::span<const u8> symtab,
std::span<const u8> strtab,
u32 importer_flags,
DemanglerFunctions demangler)
{
for(u32 i = 0; i < symtab.size() / sizeof(Symbol); i++) {
const Symbol* symbol = get_unaligned<Symbol>(symtab, i * sizeof(Symbol));
CCC_ASSERT(symbol);
Address address;
if(symbol->value != 0) {
address = symbol->value;
}
if(!address.valid() || symbol->visibility() != SymbolVisibility::DEFAULT) {
continue;
}
if(!(importer_flags & DONT_DEDUPLICATE_SYMBOLS)) {
if(database.functions.first_handle_from_starting_address(address).valid()) {
continue;
}
if(database.global_variables.first_handle_from_starting_address(address).valid()) {
continue;
}
if(database.local_variables.first_handle_from_starting_address(address).valid()) {
continue;
}
}
std::optional<std::string_view> string_view = get_string(strtab, symbol->name);
CCC_CHECK(string_view.has_value(), "Symbol string out of range.");
std::string string(*string_view);
switch(symbol->type()) {
case SymbolType::NOTYPE: {
Result<Label*> label = database.labels.create_symbol(
std::move(string), group.source, group.module_symbol, address, importer_flags, demangler);
CCC_RETURN_IF_ERROR(label);
// These symbols get emitted at the same addresses as functions
// and aren't extremely useful, so we want to mark them to
// prevent them from possibly being used as function names.
(*label)->is_junk =
(*label)->name() == "__gnu_compiled_c" ||
(*label)->name() == "__gnu_compiled_cplusplus" ||
(*label)->name() == "gcc2_compiled.";
break;
}
case SymbolType::OBJECT: {
if(symbol->size != 0) {
Result<GlobalVariable*> global_variable = database.global_variables.create_symbol(
std::move(string), group.source, group.module_symbol, address, importer_flags, demangler);
CCC_RETURN_IF_ERROR(global_variable);
if(*global_variable) {
(*global_variable)->set_size(symbol->size);
}
} else {
Result<Label*> label = database.labels.create_symbol(
std::move(string), group.source, group.module_symbol, address, importer_flags, demangler);
CCC_RETURN_IF_ERROR(label);
}
break;
}
case SymbolType::FUNC: {
Result<Function*> function = database.functions.create_symbol(
std::move(string), group.source, group.module_symbol, address, importer_flags, demangler);
CCC_RETURN_IF_ERROR(function);
if(*function) {
(*function)->set_size(symbol->size);
}
break;
}
case SymbolType::FILE: {
Result<SourceFile*> source_file = database.source_files.create_symbol(
std::move(string), group.source, group.module_symbol);
CCC_RETURN_IF_ERROR(source_file);
break;
}
default: {}
}
}
return Result<void>();
}
Result<void> print_symbol_table(FILE* out, std::span<const u8> symtab, std::span<const u8> strtab)
{
fprintf(out, "ELF SYMBOLS:\n");
fprintf(out, " Num: Value Size Type Bind Vis Ndx Name\n");
for(u32 i = 0; i < symtab.size() / sizeof(Symbol); i++) {
const Symbol* symbol = get_unaligned<Symbol>(symtab, i * sizeof(Symbol));
CCC_ASSERT(symbol);
const char* type = symbol_type_to_string(symbol->type());
const char* bind = symbol_bind_to_string(symbol->bind());
const char* visibility = symbol_visibility_to_string(symbol->visibility());
std::optional<std::string_view> string = get_string(strtab, symbol->name);
CCC_CHECK(string.has_value(), "Symbol string out of range.");
fprintf(out, "%6u: %08x %5u %-7s %-7s %-7s %3u %s\n",
i, symbol->value, symbol->size, type, bind, visibility, symbol->shndx, string->data());
}
return Result<void>();
}
static const char* symbol_bind_to_string(SymbolBind bind)
{
switch(bind) {
case SymbolBind::LOCAL: return "LOCAL";
case SymbolBind::GLOBAL: return "GLOBAL";
case SymbolBind::WEAK: return "WEAK";
case SymbolBind::NUM: return "NUM";
case SymbolBind::GNU_UNIQUE: return "GNU_UNIQUE";
}
return "ERROR";
}
static const char* symbol_type_to_string(SymbolType type)
{
switch(type) {
case SymbolType::NOTYPE: return "NOTYPE";
case SymbolType::OBJECT: return "OBJECT";
case SymbolType::FUNC: return "FUNC";
case SymbolType::SECTION: return "SECTION";
case SymbolType::FILE: return "FILE";
case SymbolType::COMMON: return "COMMON";
case SymbolType::TLS: return "TLS";
case SymbolType::NUM: return "NUM";
case SymbolType::GNU_IFUNC: return "GNU_IFUNC";
}
return "ERROR";
}
static const char* symbol_visibility_to_string(SymbolVisibility visibility)
{
switch(visibility) {
case SymbolVisibility::DEFAULT: return "DEFAULT";
case SymbolVisibility::INTERNAL: return "INTERNAL";
case SymbolVisibility::HIDDEN: return "HIDDEN";
case SymbolVisibility::PROTECTED: return "PROTECTED";
}
return "ERROR";
}
}

20
3rdparty/ccc/src/ccc/elf_symtab.h vendored Normal file
View File

@ -0,0 +1,20 @@
// This file is part of the Chaos Compiler Collection.
// SPDX-License-Identifier: MIT
#pragma once
#include "symbol_database.h"
namespace ccc::elf {
Result<void> import_symbols(
SymbolDatabase& database,
const SymbolGroup& group,
std::span<const u8> symtab,
std::span<const u8> strtab,
u32 importer_flags,
DemanglerFunctions demangler);
Result<void> print_symbol_table(FILE* out, std::span<const u8> symtab, std::span<const u8> strtab);
}

95
3rdparty/ccc/src/ccc/importer_flags.cpp vendored Normal file
View File

@ -0,0 +1,95 @@
// This file is part of the Chaos Compiler Collection.
// SPDX-License-Identifier: MIT
#include "importer_flags.h"
namespace ccc {
const std::vector<ImporterFlagInfo> IMPORTER_FLAGS = {
{DEMANGLE_PARAMETERS, "--demangle-parameters", {
"Include parameters in demangled function names."
}},
{DEMANGLE_RETURN_TYPE, "--demangle-return-type", {
"Include return types at the end of demangled",
"function names if they're available."
}},
{DONT_DEDUPLICATE_SYMBOLS, "--dont-deduplicate-symbols", {
"Do not deduplicate matching symbols from",
"different symbol tables. This options has no",
"effect on data types."
}},
{DONT_DEDUPLICATE_TYPES, "--dont-deduplicate-types", {
"Do not deduplicate data types from different",
"translation units."
}},
{DONT_DEMANGLE_NAMES, "--dont-demangle-names", {
"Do not demangle function names, global variable",
"names, or overloaded operator names."
}},
{INCLUDE_GENERATED_MEMBER_FUNCTIONS, "--include-generated-functions", {
"Output member functions that were likely",
"automatically generated by the compiler."
}},
{NO_ACCESS_SPECIFIERS, "--no-access-specifiers", {
"Do not print access specifiers."
}},
{NO_MEMBER_FUNCTIONS, "--no-member-functions", {
"Do not print member functions."
}},
{NO_OPTIMIZED_OUT_FUNCTIONS, "--no-optimized-out-functions", {
"Discard functions that were optimized out."
}},
{STRICT_PARSING, "--strict", {
"Make more types of errors fatal."
}},
{TYPEDEF_ALL_ENUMS, "--typedef-all-enums", {
"Force all emitted C++ enums to be defined using",
"a typedef. With STABS, it is not always possible",
"to determine if an enum was like this in the",
"original source code, so this option should be",
"useful for reverse engineering C projects."
}},
{TYPEDEF_ALL_STRUCTS, "--typedef-all-structs", {
"Force all emitted C++ structure types to be",
"defined using a typedef."
}},
{TYPEDEF_ALL_UNIONS, "--typedef-all-unions", {
"Force all emitted C++ union types to be defined",
"using a typedef."
}},
{UNIQUE_FUNCTIONS, "--unique-functions", {
" If multiple identical .mdebug function symbols",
"are present, find the one that seems to have",
"actually been included in the linked binary, and",
"remove the addresses from all the rest. Using",
"this importer flag in combination with",
"--no-optimized-out-functions will remove these",
"duplicate function symbols entirely."
}}
};
u32 parse_importer_flag(const char* argument)
{
for(const ImporterFlagInfo& flag : IMPORTER_FLAGS) {
if(strcmp(flag.argument, argument) == 0) {
return flag.flag;
}
}
return NO_IMPORTER_FLAGS;
}
void print_importer_flags_help(FILE* out)
{
for(const ImporterFlagInfo& flag : IMPORTER_FLAGS) {
fprintf(out, "\n");
fprintf(out, " %-29s ", flag.argument);
for(size_t i = 0; i < flag.help_text.size(); i++) {
if(i > 0) {
fprintf(out, " ");
}
fprintf(out, "%s\n", flag.help_text[i]);
}
}
}
}

39
3rdparty/ccc/src/ccc/importer_flags.h vendored Normal file
View File

@ -0,0 +1,39 @@
// This file is part of the Chaos Compiler Collection.
// SPDX-License-Identifier: MIT
#pragma once
#include "util.h"
namespace ccc {
enum ImporterFlags {
NO_IMPORTER_FLAGS = 0,
DEMANGLE_PARAMETERS = (1 << 0),
DEMANGLE_RETURN_TYPE = (1 << 1),
DONT_DEDUPLICATE_SYMBOLS = (1 << 2),
DONT_DEDUPLICATE_TYPES = (1 << 3),
DONT_DEMANGLE_NAMES = (1 << 4),
INCLUDE_GENERATED_MEMBER_FUNCTIONS = (1 << 5),
NO_ACCESS_SPECIFIERS = (1 << 6),
NO_MEMBER_FUNCTIONS = (1 << 7),
NO_OPTIMIZED_OUT_FUNCTIONS = (1 << 8),
STRICT_PARSING = (1 << 9),
TYPEDEF_ALL_ENUMS = (1 << 10),
TYPEDEF_ALL_STRUCTS = (1 << 11),
TYPEDEF_ALL_UNIONS = (1 << 12),
UNIQUE_FUNCTIONS = (1 << 13)
};
struct ImporterFlagInfo {
ImporterFlags flag;
const char* argument;
std::vector<const char*> help_text;
};
extern const std::vector<ImporterFlagInfo> IMPORTER_FLAGS;
u32 parse_importer_flag(const char* argument);
void print_importer_flags_help(FILE* out);
}

349
3rdparty/ccc/src/ccc/mdebug_analysis.cpp vendored Normal file
View File

@ -0,0 +1,349 @@
// This file is part of the Chaos Compiler Collection.
// SPDX-License-Identifier: MIT
#include "mdebug_analysis.h"
#include "stabs_to_ast.h"
namespace ccc::mdebug {
Result<void> LocalSymbolTableAnalyser::stab_magic(const char* magic)
{
return Result<void>();
}
Result<void> LocalSymbolTableAnalyser::source_file(const char* path, Address text_address)
{
if(m_next_relative_path.empty()) {
m_next_relative_path = m_source_file.command_line_path;
}
return Result<void>();
}
Result<void> LocalSymbolTableAnalyser::data_type(const ParsedSymbol& symbol)
{
Result<std::unique_ptr<ast::Node>> node = stabs_type_to_ast(
*symbol.name_colon_type.type.get(), nullptr, m_stabs_to_ast_state, 0, false, false);
CCC_RETURN_IF_ERROR(node);
if(symbol.is_typedef && (*node)->descriptor == ast::STRUCT_OR_UNION) {
ast::StructOrUnion& struct_or_union = (*node)->as<ast::StructOrUnion>();
const std::string& name = symbol.name_colon_type.name;
StabsTypeNumber type_number = symbol.name_colon_type.type->type_number;
fix_recursively_emitted_structures(struct_or_union, name, type_number, m_stabs_to_ast_state.file_handle);
}
bool is_struct = (*node)->descriptor == ast::STRUCT_OR_UNION && (*node)->as<ast::StructOrUnion>().is_struct;
bool force_typedef =
((m_context.importer_flags & TYPEDEF_ALL_ENUMS) && (*node)->descriptor == ast::ENUM) ||
((m_context.importer_flags & TYPEDEF_ALL_STRUCTS) && (*node)->descriptor == ast::STRUCT_OR_UNION && is_struct) ||
((m_context.importer_flags & TYPEDEF_ALL_UNIONS) && (*node)->descriptor == ast::STRUCT_OR_UNION && !is_struct);
(*node)->name = (symbol.name_colon_type.name == " ") ? "" : symbol.name_colon_type.name;
if(symbol.is_typedef || force_typedef) {
(*node)->storage_class = STORAGE_CLASS_TYPEDEF;
}
const char* name = (*node)->name.c_str();
StabsTypeNumber number = symbol.name_colon_type.type->type_number;
if(m_context.importer_flags & DONT_DEDUPLICATE_TYPES) {
Result<DataType*> data_type = m_database.data_types.create_symbol(
name, m_context.group.source, m_context.group.module_symbol);
CCC_RETURN_IF_ERROR(data_type);
m_source_file.stabs_type_number_to_handle[number] = (*data_type)->handle();
(*data_type)->set_type(std::move(*node));
(*data_type)->files = {m_source_file.handle()};
} else {
Result<ccc::DataType*> type = m_database.create_data_type_if_unique(
std::move(*node), number, name, m_source_file, m_context.group);
CCC_RETURN_IF_ERROR(type);
}
return Result<void>();
}
Result<void> LocalSymbolTableAnalyser::global_variable(
const char* mangled_name, Address address, const StabsType& type, bool is_static, GlobalStorageLocation location)
{
Result<GlobalVariable*> global = m_database.global_variables.create_symbol(
mangled_name, m_context.group.source, m_context.group.module_symbol, address, m_context.importer_flags, m_context.demangler);
CCC_RETURN_IF_ERROR(global);
CCC_ASSERT(*global);
m_global_variables.emplace_back((*global)->handle());
Result<std::unique_ptr<ast::Node>> node = stabs_type_to_ast(type, nullptr, m_stabs_to_ast_state, 0, true, false);
CCC_RETURN_IF_ERROR(node);
if(is_static) {
(*global)->storage_class = STORAGE_CLASS_STATIC;
}
(*global)->set_type(std::move(*node));
(*global)->storage.location = location;
return Result<void>();
}
Result<void> LocalSymbolTableAnalyser::sub_source_file(const char* path, Address text_address)
{
if(m_current_function && m_state == IN_FUNCTION_BEGINNING) {
Function::SubSourceFile& sub = m_current_function->sub_source_files.emplace_back();
sub.address = text_address;
sub.relative_path = path;
} else {
m_next_relative_path = path;
}
return Result<void>();
}
Result<void> LocalSymbolTableAnalyser::procedure(
const char* mangled_name, Address address, const ProcedureDescriptor* procedure_descriptor, bool is_static)
{
if(!m_current_function || strcmp(mangled_name, m_current_function->mangled_name().c_str()) != 0) {
Result<void> result = create_function(mangled_name, address);
CCC_RETURN_IF_ERROR(result);
}
if(is_static) {
m_current_function->storage_class = STORAGE_CLASS_STATIC;
}
if(procedure_descriptor) {
m_current_function->stack_frame_size = procedure_descriptor->frame_size;
}
return Result<void>();
}
Result<void> LocalSymbolTableAnalyser::label(const char* label, Address address, s32 line_number)
{
if(address.valid() && m_current_function && label[0] == '$') {
Function::LineNumberPair& pair = m_current_function->line_numbers.emplace_back();
pair.address = address;
pair.line_number = line_number;
}
return Result<void>();
}
Result<void> LocalSymbolTableAnalyser::text_end(const char* name, s32 function_size)
{
if(m_state == IN_FUNCTION_BEGINNING) {
CCC_CHECK(m_current_function, "END TEXT symbol outside of function.");
m_current_function->set_size(function_size);
m_state = IN_FUNCTION_END;
}
return Result<void>();
}
Result<void> LocalSymbolTableAnalyser::function(const char* mangled_name, const StabsType& return_type, Address address)
{
if(!m_current_function || strcmp(mangled_name, m_current_function->mangled_name().c_str()) != 0) {
Result<void> result = create_function(mangled_name, address);
CCC_RETURN_IF_ERROR(result);
} else {
// For MTV Music Maker 2, the addresses for static functions stored in
// the PROC symbols are relative to the translation unit, while the
// addresses stored in the FUN symbol are absolute. This is the only
// game I've found that seems to have this problem, but since in all
// other cases it seems all these addresses are all absolute, I may as
// well add in a hack here to deal with it.
bool no_module_base_address = m_context.group.module_symbol && m_context.group.module_symbol->address().get_or_zero() == 0;
bool new_address_greater = address.valid() && address > m_current_function->address();
if(no_module_base_address && new_address_greater) {
m_database.functions.move_symbol(m_current_function->handle(), address);
}
}
Result<std::unique_ptr<ast::Node>> node = stabs_type_to_ast(return_type, nullptr, m_stabs_to_ast_state, 0, true, true);
CCC_RETURN_IF_ERROR(node);
m_current_function->set_type(std::move(*node));
return Result<void>();
}
Result<void> LocalSymbolTableAnalyser::function_end()
{
if(m_current_function) {
m_current_function->set_parameter_variables(std::move(m_current_parameter_variables), m_database);
m_current_function->set_local_variables(std::move(m_current_local_variables), m_database);
}
m_current_function = nullptr;
m_current_parameter_variables = std::vector<ParameterVariableHandle>();
m_current_local_variables = std::vector<LocalVariableHandle>();
m_blocks.clear();
m_pending_local_variables.clear();
m_state = NOT_IN_FUNCTION;
return Result<void>();
}
Result<void> LocalSymbolTableAnalyser::parameter(
const char* name, const StabsType& type, bool is_stack, s32 value, bool is_by_reference)
{
CCC_CHECK(m_current_function, "Parameter symbol before first func/proc symbol.");
Result<ParameterVariable*> parameter_variable = m_database.parameter_variables.create_symbol(
name, m_context.group.source, m_context.group.module_symbol);
CCC_RETURN_IF_ERROR(parameter_variable);
m_current_parameter_variables.emplace_back((*parameter_variable)->handle());
Result<std::unique_ptr<ast::Node>> node = stabs_type_to_ast(type, nullptr, m_stabs_to_ast_state, 0, true, true);
CCC_RETURN_IF_ERROR(node);
(*parameter_variable)->set_type(std::move(*node));
if(is_stack) {
StackStorage& stack_storage = (*parameter_variable)->storage.emplace<StackStorage>();
stack_storage.stack_pointer_offset = value;
} else {
RegisterStorage& register_storage = (*parameter_variable)->storage.emplace<RegisterStorage>();
register_storage.dbx_register_number = value;
register_storage.is_by_reference = is_by_reference;
}
return Result<void>();
}
Result<void> LocalSymbolTableAnalyser::local_variable(
const char* name, const StabsType& type, u32 value, StabsSymbolDescriptor desc, SymbolClass sclass)
{
if(!m_current_function) {
return Result<void>();
}
Address address = (desc == StabsSymbolDescriptor::STATIC_LOCAL_VARIABLE) ? value : Address();
Result<LocalVariable*> local_variable = m_database.local_variables.create_symbol(
name, address, m_context.group.source, m_context.group.module_symbol);
CCC_RETURN_IF_ERROR(local_variable);
m_current_local_variables.emplace_back((*local_variable)->handle());
m_pending_local_variables.emplace_back((*local_variable)->handle());
Result<std::unique_ptr<ast::Node>> node = stabs_type_to_ast(type, nullptr, m_stabs_to_ast_state, 0, true, false);
CCC_RETURN_IF_ERROR(node);
if(desc == StabsSymbolDescriptor::STATIC_LOCAL_VARIABLE) {
GlobalStorage& global_storage = (*local_variable)->storage.emplace<GlobalStorage>();
std::optional<GlobalStorageLocation> location_opt =
symbol_class_to_global_variable_location(sclass);
CCC_CHECK(location_opt.has_value(),
"Invalid static local variable location %s.",
symbol_class(sclass));
global_storage.location = *location_opt;
(*node)->storage_class = STORAGE_CLASS_STATIC;
} else if(desc == StabsSymbolDescriptor::REGISTER_VARIABLE) {
RegisterStorage& register_storage = (*local_variable)->storage.emplace<RegisterStorage>();
register_storage.dbx_register_number = (s32) value;
} else if(desc == StabsSymbolDescriptor::LOCAL_VARIABLE) {
StackStorage& stack_storage = (*local_variable)->storage.emplace<StackStorage>();
stack_storage.stack_pointer_offset = (s32) value;
} else {
return CCC_FAILURE("LocalSymbolTableAnalyser::local_variable() called with bad symbol descriptor.");
}
(*local_variable)->set_type(std::move(*node));
return Result<void>();
}
Result<void> LocalSymbolTableAnalyser::lbrac(s32 begin_offset)
{
for(LocalVariableHandle local_variable_handle : m_pending_local_variables) {
if(LocalVariable* local_variable = m_database.local_variables.symbol_from_handle(local_variable_handle)) {
local_variable->live_range.low = m_source_file.address().value + begin_offset;
}
}
m_blocks.emplace_back(std::move(m_pending_local_variables));
m_pending_local_variables = {};
return Result<void>();
}
Result<void> LocalSymbolTableAnalyser::rbrac(s32 end_offset)
{
CCC_CHECK(!m_blocks.empty(), "RBRAC symbol without a matching LBRAC symbol.");
std::vector<LocalVariableHandle>& variables = m_blocks.back();
for(LocalVariableHandle local_variable_handle : variables) {
if(LocalVariable* local_variable = m_database.local_variables.symbol_from_handle(local_variable_handle)) {
local_variable->live_range.high = m_source_file.address().value + end_offset;
}
}
m_blocks.pop_back();
return Result<void>();
}
Result<void> LocalSymbolTableAnalyser::finish()
{
CCC_CHECK(m_state != IN_FUNCTION_BEGINNING,
"Unexpected end of symbol table for '%s'.", m_source_file.name().c_str());
if(m_current_function) {
Result<void> result = function_end();
CCC_RETURN_IF_ERROR(result);
}
m_source_file.set_functions(std::move(m_functions), m_database);
m_source_file.set_global_variables(std::move(m_global_variables), m_database);
return Result<void>();
}
Result<void> LocalSymbolTableAnalyser::create_function(const char* mangled_name, Address address)
{
if(m_current_function) {
Result<void> result = function_end();
CCC_RETURN_IF_ERROR(result);
}
Result<Function*> function = m_database.functions.create_symbol(
mangled_name, m_context.group.source, m_context.group.module_symbol, address, m_context.importer_flags, m_context.demangler);
CCC_RETURN_IF_ERROR(function);
CCC_ASSERT(*function);
m_current_function = *function;
m_functions.emplace_back(m_current_function->handle());
m_state = IN_FUNCTION_BEGINNING;
if(!m_next_relative_path.empty() && m_current_function->relative_path != m_source_file.command_line_path) {
m_current_function->relative_path = m_next_relative_path;
}
return Result<void>();
}
std::optional<GlobalStorageLocation> symbol_class_to_global_variable_location(SymbolClass symbol_class)
{
std::optional<GlobalStorageLocation> location;
switch(symbol_class) {
case SymbolClass::NIL: location = GlobalStorageLocation::NIL; break;
case SymbolClass::DATA: location = GlobalStorageLocation::DATA; break;
case SymbolClass::BSS: location = GlobalStorageLocation::BSS; break;
case SymbolClass::ABS: location = GlobalStorageLocation::ABS; break;
case SymbolClass::SDATA: location = GlobalStorageLocation::SDATA; break;
case SymbolClass::SBSS: location = GlobalStorageLocation::SBSS; break;
case SymbolClass::RDATA: location = GlobalStorageLocation::RDATA; break;
case SymbolClass::COMMON: location = GlobalStorageLocation::COMMON; break;
case SymbolClass::SCOMMON: location = GlobalStorageLocation::SCOMMON; break;
case SymbolClass::SUNDEFINED: location = GlobalStorageLocation::SUNDEFINED; break;
default: {}
}
return location;
}
}

99
3rdparty/ccc/src/ccc/mdebug_analysis.h vendored Normal file
View File

@ -0,0 +1,99 @@
// This file is part of the Chaos Compiler Collection.
// SPDX-License-Identifier: MIT
#pragma once
#include "importer_flags.h"
#include "mdebug_section.h"
#include "mdebug_symbols.h"
#include "stabs.h"
#include "stabs_to_ast.h"
#include "symbol_database.h"
namespace ccc::mdebug {
struct AnalysisContext {
const mdebug::SymbolTableReader* reader = nullptr;
const std::map<u32, const mdebug::Symbol*>* external_functions = nullptr;
const std::map<std::string, const mdebug::Symbol*>* external_globals = nullptr;
SymbolGroup group;
u32 importer_flags = NO_IMPORTER_FLAGS;
DemanglerFunctions demangler;
};
class LocalSymbolTableAnalyser {
public:
LocalSymbolTableAnalyser(SymbolDatabase& database, const StabsToAstState& stabs_to_ast_state, const AnalysisContext& context, SourceFile& source_file)
: m_database(database)
, m_context(context)
, m_stabs_to_ast_state(stabs_to_ast_state)
, m_source_file(source_file) {}
// Functions for processing individual symbols.
//
// In most cases these symbols will appear in the following order:
// PROC TEXT
// ... line numbers ... ($LM<N>)
// END TEXT
// LABEL TEXT FUN
// ... parameters ...
// ... blocks ... (... local variables ... LBRAC ... subblocks ... RBRAC)
// NIL NIL FUN
//
// For some compiler versions the symbols can appear in this order:
// LABEL TEXT FUN
// ... parameters ...
// first line number ($LM1)
// PROC TEXT
// ... line numbers ... ($LM<N>)
// END TEXT
// ... blocks ... (... local variables ... LBRAC ... subblocks ... RBRAC)
Result<void> stab_magic(const char* magic);
Result<void> source_file(const char* path, Address text_address);
Result<void> data_type(const ParsedSymbol& symbol);
Result<void> global_variable(
const char* mangled_name, Address address, const StabsType& type, bool is_static, GlobalStorageLocation location);
Result<void> sub_source_file(const char* name, Address text_address);
Result<void> procedure(
const char* mangled_name, Address address, const ProcedureDescriptor* procedure_descriptor, bool is_static);
Result<void> label(const char* label, Address address, s32 line_number);
Result<void> text_end(const char* name, s32 function_size);
Result<void> function(const char* mangled_name, const StabsType& return_type, Address address);
Result<void> function_end();
Result<void> parameter(
const char* name, const StabsType& type, bool is_stack, s32 value, bool is_by_reference);
Result<void> local_variable(
const char* name, const StabsType& type, u32 value, StabsSymbolDescriptor desc, SymbolClass sclass);
Result<void> lbrac(s32 begin_offset);
Result<void> rbrac(s32 end_offset);
Result<void> finish();
Result<void> create_function(const char* mangled_name, Address address);
protected:
enum AnalysisState {
NOT_IN_FUNCTION,
IN_FUNCTION_BEGINNING,
IN_FUNCTION_END
};
SymbolDatabase& m_database;
const AnalysisContext& m_context;
const StabsToAstState& m_stabs_to_ast_state;
AnalysisState m_state = NOT_IN_FUNCTION;
SourceFile& m_source_file;
std::vector<FunctionHandle> m_functions;
std::vector<GlobalVariableHandle> m_global_variables;
Function* m_current_function = nullptr;
std::vector<ParameterVariableHandle> m_current_parameter_variables;
std::vector<LocalVariableHandle> m_current_local_variables;
std::vector<std::vector<LocalVariableHandle>> m_blocks;
std::vector<LocalVariableHandle> m_pending_local_variables;
std::string m_next_relative_path;
};
std::optional<GlobalStorageLocation> symbol_class_to_global_variable_location(SymbolClass symbol_class);
};

668
3rdparty/ccc/src/ccc/mdebug_importer.cpp vendored Normal file
View File

@ -0,0 +1,668 @@
// This file is part of the Chaos Compiler Collection.
// SPDX-License-Identifier: MIT
#include "mdebug_importer.h"
namespace ccc::mdebug {
static Result<void> resolve_type_names(
SymbolDatabase& database, const SymbolGroup& group, u32 importer_flags);
static Result<void> resolve_type_name(
ast::TypeName& type_name,
SymbolDatabase& database,
const SymbolGroup& group,
u32 importer_flags);
static void compute_size_bytes(ast::Node& node, SymbolDatabase& database);
static void detect_duplicate_functions(SymbolDatabase& database, const SymbolGroup& group);
static void detect_fake_functions(SymbolDatabase& database, const std::map<u32, const mdebug::Symbol*>& external_functions, const SymbolGroup& group);
static void destroy_optimized_out_functions(
SymbolDatabase& database, const SymbolGroup& group);
Result<void> import_symbol_table(
SymbolDatabase& database,
std::span<const u8> elf,
s32 section_offset,
const SymbolGroup& group,
u32 importer_flags,
const DemanglerFunctions& demangler,
const std::atomic_bool* interrupt)
{
SymbolTableReader reader;
Result<void> reader_result = reader.init(elf, section_offset);
CCC_RETURN_IF_ERROR(reader_result);
Result<std::vector<mdebug::Symbol>> external_symbols = reader.parse_external_symbols();
CCC_RETURN_IF_ERROR(external_symbols);
// The addresses of the global variables aren't present in the local symbol
// table, so here we extract them from the external table. In addition, for
// some games we need to cross reference the function symbols in the local
// symbol table with the entries in the external symbol table.
std::map<u32, const mdebug::Symbol*> external_functions;
std::map<std::string, const mdebug::Symbol*> external_globals;
for(const mdebug::Symbol& external : *external_symbols) {
if(external.symbol_type == mdebug::SymbolType::PROC) {
external_functions[external.value] = &external;
}
if(external.symbol_type == mdebug::SymbolType::GLOBAL
&& (external.symbol_class != mdebug::SymbolClass::UNDEFINED)) {
external_globals[external.string] = &external;
}
}
// Bundle together some unchanging state to pass to import_files.
AnalysisContext context;
context.reader = &reader;
context.external_functions = &external_functions;
context.external_globals = &external_globals;
context.group = group;
context.importer_flags = importer_flags;
context.demangler = demangler;
Result<void> result = import_files(database, context, interrupt);
CCC_RETURN_IF_ERROR(result);
return Result<void>();
}
Result<void> import_files(SymbolDatabase& database, const AnalysisContext& context, const std::atomic_bool* interrupt)
{
Result<s32> file_count = context.reader->file_count();
CCC_RETURN_IF_ERROR(file_count);
for(s32 i = 0; i < *file_count; i++) {
if(interrupt && *interrupt) {
return CCC_FAILURE("Operation interrupted by user.");
}
Result<mdebug::File> file = context.reader->parse_file(i);
CCC_RETURN_IF_ERROR(file);
Result<void> result = import_file(database, *file, context);
CCC_RETURN_IF_ERROR(result);
}
// The files field may be modified by further analysis passes, so we
// need to save this information here.
for(DataType& data_type : database.data_types) {
if(context.group.is_in_group(data_type) && data_type.files.size() == 1) {
data_type.only_defined_in_single_translation_unit = true;
}
}
// Lookup data types and store data type handles in type names.
Result<void> type_name_result = resolve_type_names(database, context.group, context.importer_flags);
CCC_RETURN_IF_ERROR(type_name_result);
// Compute the size in bytes of all the AST nodes.
database.for_each_symbol([&](ccc::Symbol& symbol) {
if(context.group.is_in_group(symbol) && symbol.type()) {
compute_size_bytes(*symbol.type(), database);
}
});
// Propagate the size information to the global variable symbols.
for(GlobalVariable& global_variable : database.global_variables) {
if(global_variable.type() && global_variable.type()->size_bytes > -1) {
global_variable.set_size((u32) global_variable.type()->size_bytes);
}
}
// Propagate the size information to the static local variable symbols.
for(LocalVariable& local_variable : database.local_variables) {
bool is_static_local = std::holds_alternative<GlobalStorage>(local_variable.storage);
if(is_static_local && local_variable.type() && local_variable.type()->size_bytes > -1) {
local_variable.set_size((u32) local_variable.type()->size_bytes);
}
}
// Some games (e.g. Jet X2O) have multiple function symbols across different
// translation units with the same name and address.
if(context.importer_flags & UNIQUE_FUNCTIONS) {
detect_duplicate_functions(database, context.group);
}
// If multiple functions appear at the same address, discard the addresses
// of all of them except the real one.
if(context.external_functions) {
detect_fake_functions(database, *context.external_functions, context.group);
}
// Remove functions with no address. If there are any such functions, this
// will invalidate all pointers to symbols.
if(context.importer_flags & NO_OPTIMIZED_OUT_FUNCTIONS) {
destroy_optimized_out_functions(database, context.group);
}
return Result<void>();
}
Result<void> import_file(SymbolDatabase& database, const mdebug::File& input, const AnalysisContext& context)
{
// Parse the stab strings into a data structure that's vaguely
// one-to-one with the text-based representation.
u32 importer_flags_for_this_file = context.importer_flags;
Result<std::vector<ParsedSymbol>> symbols = parse_symbols(input.symbols, importer_flags_for_this_file);
CCC_RETURN_IF_ERROR(symbols);
// In stabs, types can be referenced by their number from other stabs,
// so here we build a map of type numbers to the parsed types.
std::map<StabsTypeNumber, const StabsType*> stabs_types;
for(const ParsedSymbol& symbol : *symbols) {
if(symbol.type == ParsedSymbolType::NAME_COLON_TYPE) {
symbol.name_colon_type.type->enumerate_numbered_types(stabs_types);
}
}
Result<SourceFile*> source_file = database.source_files.create_symbol(
input.full_path, input.address, context.group.source, context.group.module_symbol);
CCC_RETURN_IF_ERROR(source_file);
(*source_file)->working_dir = input.working_dir;
(*source_file)->command_line_path = input.command_line_path;
// Sometimes the INFO symbols contain information about what toolchain
// version was used for building the executable.
for(const mdebug::Symbol& symbol : input.symbols) {
if(symbol.symbol_class == mdebug::SymbolClass::INFO && strcmp(symbol.string, "@stabs") != 0) {
(*source_file)->toolchain_version_info.emplace(symbol.string);
}
}
StabsToAstState stabs_to_ast_state;
stabs_to_ast_state.file_handle = (*source_file)->handle().value;
stabs_to_ast_state.stabs_types = &stabs_types;
stabs_to_ast_state.importer_flags = importer_flags_for_this_file;
stabs_to_ast_state.demangler = context.demangler;
// Convert the parsed stabs symbols to a more standard C AST.
LocalSymbolTableAnalyser analyser(database, stabs_to_ast_state, context, **source_file);
for(const ParsedSymbol& symbol : *symbols) {
if(symbol.duplicate) {
continue;
}
switch(symbol.type) {
case ParsedSymbolType::NAME_COLON_TYPE: {
switch(symbol.name_colon_type.descriptor) {
case StabsSymbolDescriptor::LOCAL_FUNCTION:
case StabsSymbolDescriptor::GLOBAL_FUNCTION: {
const char* name = symbol.name_colon_type.name.c_str();
const StabsType& type = *symbol.name_colon_type.type.get();
Result<void> result = analyser.function(name, type, symbol.raw->value);
CCC_RETURN_IF_ERROR(result);
break;
}
case StabsSymbolDescriptor::REFERENCE_PARAMETER_A:
case StabsSymbolDescriptor::REGISTER_PARAMETER:
case StabsSymbolDescriptor::VALUE_PARAMETER:
case StabsSymbolDescriptor::REFERENCE_PARAMETER_V: {
const char* name = symbol.name_colon_type.name.c_str();
const StabsType& type = *symbol.name_colon_type.type.get();
bool is_stack_variable = symbol.name_colon_type.descriptor == StabsSymbolDescriptor::VALUE_PARAMETER;
bool is_by_reference = symbol.name_colon_type.descriptor == StabsSymbolDescriptor::REFERENCE_PARAMETER_A
|| symbol.name_colon_type.descriptor == StabsSymbolDescriptor::REFERENCE_PARAMETER_V;
Result<void> result = analyser.parameter(name, type, is_stack_variable, symbol.raw->value, is_by_reference);
CCC_RETURN_IF_ERROR(result);
break;
}
case StabsSymbolDescriptor::REGISTER_VARIABLE:
case StabsSymbolDescriptor::LOCAL_VARIABLE:
case StabsSymbolDescriptor::STATIC_LOCAL_VARIABLE: {
const char* name = symbol.name_colon_type.name.c_str();
const StabsType& type = *symbol.name_colon_type.type.get();
Result<void> result = analyser.local_variable(
name, type, symbol.raw->value, symbol.name_colon_type.descriptor, symbol.raw->symbol_class);
CCC_RETURN_IF_ERROR(result);
break;
}
case StabsSymbolDescriptor::GLOBAL_VARIABLE:
case StabsSymbolDescriptor::STATIC_GLOBAL_VARIABLE: {
const char* name = symbol.name_colon_type.name.c_str();
u32 address = -1;
std::optional<GlobalStorageLocation> location =
symbol_class_to_global_variable_location(symbol.raw->symbol_class);
if(symbol.name_colon_type.descriptor == StabsSymbolDescriptor::GLOBAL_VARIABLE) {
// The address for non-static global variables is
// only stored in the external symbol table (and
// the ELF symbol table), so we pull that
// information in here.
if(context.external_globals) {
auto global_symbol = context.external_globals->find(symbol.name_colon_type.name);
if(global_symbol != context.external_globals->end()) {
address = (u32) global_symbol->second->value;
location = symbol_class_to_global_variable_location(global_symbol->second->symbol_class);
}
}
} else {
// And for static global variables it's just stored
// in the local symbol table.
address = (u32) symbol.raw->value;
}
CCC_CHECK(location.has_value(), "Invalid global variable location.")
const StabsType& type = *symbol.name_colon_type.type.get();
bool is_static = symbol.name_colon_type.descriptor == StabsSymbolDescriptor::STATIC_GLOBAL_VARIABLE;
Result<void> result = analyser.global_variable(name, address, type, is_static, *location);
CCC_RETURN_IF_ERROR(result);
break;
}
case StabsSymbolDescriptor::TYPE_NAME:
case StabsSymbolDescriptor::ENUM_STRUCT_OR_TYPE_TAG: {
Result<void> result = analyser.data_type(symbol);
CCC_RETURN_IF_ERROR(result);
break;
}
}
break;
}
case ParsedSymbolType::SOURCE_FILE: {
Result<void> result = analyser.source_file(symbol.raw->string, symbol.raw->value);
CCC_RETURN_IF_ERROR(result);
break;
}
case ParsedSymbolType::SUB_SOURCE_FILE: {
Result<void> result = analyser.sub_source_file(symbol.raw->string, symbol.raw->value);
CCC_RETURN_IF_ERROR(result);
break;
}
case ParsedSymbolType::LBRAC: {
Result<void> result = analyser.lbrac(symbol.raw->value);
CCC_RETURN_IF_ERROR(result);
break;
}
case ParsedSymbolType::RBRAC: {
Result<void> result = analyser.rbrac(symbol.raw->value);
CCC_RETURN_IF_ERROR(result);
break;
}
case ParsedSymbolType::FUNCTION_END: {
Result<void> result = analyser.function_end();
CCC_RETURN_IF_ERROR(result);
break;
}
case ParsedSymbolType::NON_STABS: {
if(symbol.raw->symbol_class == mdebug::SymbolClass::TEXT) {
if(symbol.raw->symbol_type == mdebug::SymbolType::PROC) {
Result<void> result = analyser.procedure(symbol.raw->string, symbol.raw->value, symbol.raw->procedure_descriptor, false);
CCC_RETURN_IF_ERROR(result);
} else if(symbol.raw->symbol_type == mdebug::SymbolType::STATICPROC) {
Result<void> result = analyser.procedure(symbol.raw->string, symbol.raw->value, symbol.raw->procedure_descriptor, true);
CCC_RETURN_IF_ERROR(result);
} else if(symbol.raw->symbol_type == mdebug::SymbolType::LABEL) {
Result<void> result = analyser.label(symbol.raw->string, symbol.raw->value, symbol.raw->index);
CCC_RETURN_IF_ERROR(result);
} else if(symbol.raw->symbol_type == mdebug::SymbolType::END) {
Result<void> result = analyser.text_end(symbol.raw->string, symbol.raw->value);
CCC_RETURN_IF_ERROR(result);
}
}
break;
}
}
}
Result<void> result = analyser.finish();
CCC_RETURN_IF_ERROR(result);
return Result<void>();
}
static Result<void> resolve_type_names(
SymbolDatabase& database, const SymbolGroup& group, u32 importer_flags)
{
Result<void> result;
database.for_each_symbol([&](ccc::Symbol& symbol) {
if(group.is_in_group(symbol) && symbol.type()) {
ast::for_each_node(*symbol.type(), ast::PREORDER_TRAVERSAL, [&](ast::Node& node) {
if(node.descriptor == ast::TYPE_NAME) {
Result<void> type_name_result = resolve_type_name(node.as<ast::TypeName>(), database, group, importer_flags);
if(!type_name_result.success()) {
result = std::move(type_name_result);
}
}
return ast::EXPLORE_CHILDREN;
});
}
});
return result;
}
static Result<void> resolve_type_name(
ast::TypeName& type_name,
SymbolDatabase& database,
const SymbolGroup& group,
u32 importer_flags)
{
ast::TypeName::UnresolvedStabs* unresolved_stabs = type_name.unresolved_stabs.get();
if(!unresolved_stabs) {
return Result<void>();
}
// Lookup the type by its STABS type number. This path ensures that the
// correct type is found even if multiple types have the same name.
if(unresolved_stabs->referenced_file_handle != (u32) -1 && unresolved_stabs->stabs_type_number.valid()) {
const SourceFile* source_file = database.source_files.symbol_from_handle(unresolved_stabs->referenced_file_handle);
CCC_ASSERT(source_file);
auto handle = source_file->stabs_type_number_to_handle.find(unresolved_stabs->stabs_type_number);
if(handle != source_file->stabs_type_number_to_handle.end()) {
type_name.data_type_handle = handle->second.value;
type_name.is_forward_declared = false;
type_name.unresolved_stabs.reset();
return Result<void>();
}
}
// Looking up the type by its STABS type number failed, so look for it by
// its name instead. This happens when a type is forward declared but not
// defined in a given translation unit.
if(!unresolved_stabs->type_name.empty()) {
for(auto& name_handle : database.data_types.handles_from_name(unresolved_stabs->type_name)) {
DataType* data_type = database.data_types.symbol_from_handle(name_handle.second);
if(data_type && group.is_in_group(*data_type)) {
type_name.data_type_handle = name_handle.second.value;
type_name.is_forward_declared = true;
type_name.unresolved_stabs.reset();
return Result<void>();
}
}
}
// If this branch is taken it means the type name was probably from an
// automatically generated member function of a nested struct trying to
// reference the struct (for the this parameter). We shouldn't create a
// forward declared type in this case.
if(type_name.source == ast::TypeNameSource::UNNAMED_THIS) {
return Result<void>();
}
// Type lookup failed. This happens when a type is forward declared in a
// translation unit with symbols but is not defined in one. We haven't
// already created a forward declared type, so we create one now.
std::unique_ptr<ast::Node> forward_declared_node;
if(unresolved_stabs->type.has_value()) {
switch(*unresolved_stabs->type) {
case ast::ForwardDeclaredType::STRUCT: {
std::unique_ptr<ast::StructOrUnion> node = std::make_unique<ast::StructOrUnion>();
node->is_struct = true;
forward_declared_node = std::move(node);
break;
}
case ast::ForwardDeclaredType::UNION: {
std::unique_ptr<ast::StructOrUnion> node = std::make_unique<ast::StructOrUnion>();
node->is_struct = false;
forward_declared_node = std::move(node);
break;
}
case ast::ForwardDeclaredType::ENUM: {
std::unique_ptr<ast::Enum> node = std::make_unique<ast::Enum>();
forward_declared_node = std::move(node);
break;
}
}
}
if(forward_declared_node) {
Result<DataType*> forward_declared_type = database.data_types.create_symbol(
unresolved_stabs->type_name, group.source, group.module_symbol);
CCC_RETURN_IF_ERROR(forward_declared_type);
(*forward_declared_type)->set_type(std::move(forward_declared_node));
(*forward_declared_type)->not_defined_in_any_translation_unit = true;
type_name.data_type_handle = (*forward_declared_type)->handle().value;
type_name.is_forward_declared = true;
type_name.unresolved_stabs.reset();
return Result<void>();
}
const char* error_message = "Unresolved %s type name '%s' with STABS type number (%d,%d).";
if(importer_flags & STRICT_PARSING) {
return CCC_FAILURE(error_message,
ast::type_name_source_to_string(type_name.source),
type_name.unresolved_stabs->type_name.c_str(),
type_name.unresolved_stabs->stabs_type_number.file,
type_name.unresolved_stabs->stabs_type_number.type);
} else {
CCC_WARN(error_message,
ast::type_name_source_to_string(type_name.source),
type_name.unresolved_stabs->type_name.c_str(),
type_name.unresolved_stabs->stabs_type_number.file,
type_name.unresolved_stabs->stabs_type_number.type);
}
return Result<void>();
}
static void compute_size_bytes(ast::Node& node, SymbolDatabase& database)
{
for_each_node(node, ast::POSTORDER_TRAVERSAL, [&](ast::Node& node) {
// Skip nodes that have already been processed.
if(node.size_bytes > -1 || node.cannot_compute_size) {
return ast::EXPLORE_CHILDREN;
}
// Can't compute size recursively.
node.cannot_compute_size = true;
switch(node.descriptor) {
case ast::ARRAY: {
ast::Array& array = node.as<ast::Array>();
if(array.element_type->size_bytes > -1) {
array.size_bytes = array.element_type->size_bytes * array.element_count;
}
break;
}
case ast::BITFIELD: {
break;
}
case ast::BUILTIN: {
ast::BuiltIn& built_in = node.as<ast::BuiltIn>();
built_in.size_bytes = builtin_class_size(built_in.bclass);
break;
}
case ast::FUNCTION: {
break;
}
case ast::ENUM: {
node.size_bytes = 4;
break;
}
case ast::ERROR_NODE: {
break;
}
case ast::STRUCT_OR_UNION: {
node.size_bytes = node.size_bits / 8;
break;
}
case ast::POINTER_OR_REFERENCE: {
node.size_bytes = 4;
break;
}
case ast::POINTER_TO_DATA_MEMBER: {
break;
}
case ast::TYPE_NAME: {
ast::TypeName& type_name = node.as<ast::TypeName>();
DataType* resolved_type = database.data_types.symbol_from_handle(type_name.data_type_handle_unless_forward_declared());
if(resolved_type) {
ast::Node* resolved_node = resolved_type->type();
CCC_ASSERT(resolved_node);
if(resolved_node->size_bytes < 0 && !resolved_node->cannot_compute_size) {
compute_size_bytes(*resolved_node, database);
}
type_name.size_bytes = resolved_node->size_bytes;
}
break;
}
}
if(node.size_bytes > -1) {
node.cannot_compute_size = false;
}
return ast::EXPLORE_CHILDREN;
});
}
static void detect_duplicate_functions(SymbolDatabase& database, const SymbolGroup& group)
{
std::vector<FunctionHandle> duplicate_functions;
for(Function& test_function : database.functions) {
if(!test_function.address().valid() && !group.is_in_group(test_function)) {
continue;
}
// Find cases where there are two or more functions at the same address.
auto functions_with_same_address = database.functions.handles_from_starting_address(test_function.address());
if(functions_with_same_address.begin() == functions_with_same_address.end()) {
continue;
}
if(++functions_with_same_address.begin() == functions_with_same_address.end()) {
continue;
}
// Try to figure out the address of the translation unit which the
// version of the function that actually ended up in the linked binary
// comes from. We can't just check which source file the symbol comes
// from because it may be present in multiple.
u32 source_file_address = UINT32_MAX;
for(SourceFile& source_file : database.source_files) {
if(source_file.address() < test_function.address()) {
source_file_address = std::min(source_file.address().value, source_file_address);
}
}
if(source_file_address == UINT32_MAX) {
continue;
}
// Remove the addresses from all the matching symbols from other
// translation units.
FunctionHandle best_handle;
u32 best_offset = UINT32_MAX;
for(const auto& [address, handle] : functions_with_same_address) {
ccc::Function* function = database.functions.symbol_from_handle(handle);
if(!function || !group.is_in_group(*function) || function->mangled_name() != test_function.mangled_name()) {
continue;
}
if(address - source_file_address < best_offset) {
if(best_handle.valid()) {
duplicate_functions.emplace_back(best_handle);
}
best_handle = function->handle();
best_offset = address - source_file_address;
} else {
duplicate_functions.emplace_back(function->handle());
}
}
for(FunctionHandle duplicate_function : duplicate_functions) {
database.functions.move_symbol(duplicate_function, Address());
}
duplicate_functions.clear();
}
}
static void detect_fake_functions(SymbolDatabase& database, const std::map<u32, const mdebug::Symbol*>& external_functions, const SymbolGroup& group)
{
// Find cases where multiple fake function symbols were emitted for a given
// address and cross-reference with the external symbol table to try and
// find which one is the real one.
s32 fake_function_count = 0;
for(Function& function : database.functions) {
if(!function.address().valid() || !group.is_in_group(function)) {
continue;
}
// Find cases where there are two or more functions at the same address.
auto functions_with_same_address = database.functions.handles_from_starting_address(function.address());
if(functions_with_same_address.begin() == functions_with_same_address.end()) {
continue;
}
if(++functions_with_same_address.begin() == functions_with_same_address.end()) {
continue;
}
auto external_function = external_functions.find(function.address().value);
if(external_function == external_functions.end() || strcmp(function.mangled_name().c_str(), external_function->second->string) != 0) {
database.functions.move_symbol(function.handle(), Address());
if(fake_function_count < 10) {
CCC_WARN("Discarding address of function symbol '%s' as it is probably incorrect.", function.mangled_name().c_str());
} else if(fake_function_count == 10) {
CCC_WARN("Discarding more addresses of function symbols.");
}
fake_function_count++;
}
}
}
static void destroy_optimized_out_functions(
SymbolDatabase& database, const SymbolGroup& group)
{
bool marked = false;
for(Function& function : database.functions) {
if(group.is_in_group(function) && !function.address().valid()) {
function.mark_for_destruction();
marked = true;
}
}
if(marked) {
// This will invalidate all pointers to symbols in the database.
database.destroy_marked_symbols();
}
}
void fill_in_pointers_to_member_function_definitions(SymbolDatabase& database)
{
// Fill in pointers from member function declaration to corresponding definitions.
for(Function& function : database.functions) {
const std::string& qualified_name = function.name();
std::string::size_type name_separator_pos = qualified_name.find_last_of("::");
if(name_separator_pos == std::string::npos || name_separator_pos < 2) {
continue;
}
std::string function_name = qualified_name.substr(name_separator_pos + 1);
// This won't work for some template types.
std::string::size_type type_separator_pos = qualified_name.find_last_of("::", name_separator_pos - 2);
std::string type_name;
if(type_separator_pos != std::string::npos) {
type_name = qualified_name.substr(type_separator_pos + 1, name_separator_pos - type_separator_pos - 2);
} else {
type_name = qualified_name.substr(0, name_separator_pos - 1);
}
for(const auto& name_handle : database.data_types.handles_from_name(type_name)) {
DataType* data_type = database.data_types.symbol_from_handle(name_handle.second);
if(!data_type || !data_type->type() || data_type->type()->descriptor != ast::STRUCT_OR_UNION) {
continue;
}
ast::StructOrUnion& struct_or_union = data_type->type()->as<ast::StructOrUnion>();
for(std::unique_ptr<ast::Node>& declaration : struct_or_union.member_functions) {
if(declaration->name == function_name) {
declaration->as<ast::Function>().definition_handle = function.handle().value;
function.is_member_function_ish = true;
break;
}
}
if(function.is_member_function_ish) {
break;
}
}
}
}
}

31
3rdparty/ccc/src/ccc/mdebug_importer.h vendored Normal file
View File

@ -0,0 +1,31 @@
// This file is part of the Chaos Compiler Collection.
// SPDX-License-Identifier: MIT
#pragma once
#include <atomic>
#include "mdebug_analysis.h"
#include "mdebug_section.h"
#include "symbol_database.h"
namespace ccc::mdebug {
// Perform all the main analysis passes on the mdebug symbol table and convert
// it to a set of C++ ASTs.
Result<void> import_symbol_table(
SymbolDatabase& database,
std::span<const u8> elf,
s32 section_offset,
const SymbolGroup& group,
u32 importer_flags,
const DemanglerFunctions& demangler,
const std::atomic_bool* interrupt);
Result<void> import_files(SymbolDatabase& database, const AnalysisContext& context, const std::atomic_bool* interrupt);
Result<void> import_file(SymbolDatabase& database, const mdebug::File& input, const AnalysisContext& context);
// Try to add pointers from member function declarations to their definitions
// using a heuristic.
void fill_in_pointers_to_member_function_definitions(SymbolDatabase& database);
}

474
3rdparty/ccc/src/ccc/mdebug_section.cpp vendored Normal file
View File

@ -0,0 +1,474 @@
// This file is part of the Chaos Compiler Collection.
// SPDX-License-Identifier: MIT
#include "mdebug_section.h"
namespace ccc::mdebug {
// MIPS debug symbol table headers.
// See include/coff/sym.h from GNU binutils for more information.
CCC_PACKED_STRUCT(SymbolicHeader,
/* 0x00 */ s16 magic;
/* 0x02 */ s16 version_stamp;
/* 0x04 */ s32 line_number_count;
/* 0x08 */ s32 line_numbers_size_bytes;
/* 0x0c */ s32 line_numbers_offset;
/* 0x10 */ s32 dense_numbers_count;
/* 0x14 */ s32 dense_numbers_offset;
/* 0x18 */ s32 procedure_descriptor_count;
/* 0x1c */ s32 procedure_descriptors_offset;
/* 0x20 */ s32 local_symbol_count;
/* 0x24 */ s32 local_symbols_offset;
/* 0x28 */ s32 optimization_symbols_count;
/* 0x2c */ s32 optimization_symbols_offset;
/* 0x30 */ s32 auxiliary_symbol_count;
/* 0x34 */ s32 auxiliary_symbols_offset;
/* 0x38 */ s32 local_strings_size_bytes;
/* 0x3c */ s32 local_strings_offset;
/* 0x40 */ s32 external_strings_size_bytes;
/* 0x44 */ s32 external_strings_offset;
/* 0x48 */ s32 file_descriptor_count;
/* 0x4c */ s32 file_descriptors_offset;
/* 0x50 */ s32 relative_file_descriptor_count;
/* 0x54 */ s32 relative_file_descriptors_offset;
/* 0x58 */ s32 external_symbols_count;
/* 0x5c */ s32 external_symbols_offset;
)
CCC_PACKED_STRUCT(FileDescriptor,
/* 0x00 */ u32 address;
/* 0x04 */ s32 file_path_string_offset;
/* 0x08 */ s32 strings_offset;
/* 0x0c */ s32 cb_ss;
/* 0x10 */ s32 isym_base;
/* 0x14 */ s32 symbol_count;
/* 0x18 */ s32 line_number_entry_index_base;
/* 0x1c */ s32 cline;
/* 0x20 */ s32 optimization_entry_index_base;
/* 0x24 */ s32 copt;
/* 0x28 */ u16 ipd_first;
/* 0x2a */ u16 procedure_descriptor_count;
/* 0x2c */ s32 iaux_base;
/* 0x30 */ s32 caux;
/* 0x34 */ s32 rfd_base;
/* 0x38 */ s32 crfd;
/* 0x3c */ u32 lang : 5;
/* 0x3c */ u32 f_merge : 1;
/* 0x3c */ u32 f_readin : 1;
/* 0x3c */ u32 f_big_endian : 1;
/* 0x3c */ u32 reserved_1 : 22;
/* 0x40 */ s32 line_number_offset;
/* 0x44 */ s32 cb_line;
)
static_assert(sizeof(FileDescriptor) == 0x48);
CCC_PACKED_STRUCT(SymbolHeader,
/* 0x0 */ u32 iss;
/* 0x4 */ u32 value;
/* 0x8 */ u32 st : 6;
/* 0x8 */ u32 sc : 5;
/* 0x8 */ u32 reserved : 1;
/* 0x8 */ u32 index : 20;
)
static_assert(sizeof(SymbolHeader) == 0xc);
CCC_PACKED_STRUCT(ExternalSymbolHeader,
/* 0x0 */ u16 flags;
/* 0x2 */ s16 ifd;
/* 0x4 */ SymbolHeader symbol;
)
static_assert(sizeof(ExternalSymbolHeader) == 0x10);
static void print_symbol(FILE* out, const Symbol& symbol);
static void print_procedure_descriptor(FILE* out, const ProcedureDescriptor& procedure_descriptor);
static Result<s32> get_corruption_fixing_fudge_offset(s32 section_offset, const SymbolicHeader& hdrr);
static Result<Symbol> get_symbol(const SymbolHeader& header, std::span<const u8> elf, s32 strings_offset);
Result<void> SymbolTableReader::init(std::span<const u8> elf, s32 section_offset)
{
m_elf = elf;
m_section_offset = section_offset;
m_hdrr = get_unaligned<SymbolicHeader>(m_elf, m_section_offset);
CCC_CHECK(m_hdrr != nullptr, "MIPS debug section header out of bounds.");
CCC_CHECK(m_hdrr->magic == 0x7009, "Invalid symbolic header.");
Result<s32> fudge_offset = get_corruption_fixing_fudge_offset(m_section_offset, *m_hdrr);
CCC_RETURN_IF_ERROR(fudge_offset);
m_fudge_offset = *fudge_offset;
m_ready = true;
return Result<void>();
}
s32 SymbolTableReader::file_count() const
{
CCC_ASSERT(m_ready);
return m_hdrr->file_descriptor_count;
}
Result<File> SymbolTableReader::parse_file(s32 index) const
{
CCC_ASSERT(m_ready);
File file;
u64 fd_offset = m_hdrr->file_descriptors_offset + index * sizeof(FileDescriptor);
const FileDescriptor* fd_header = get_unaligned<FileDescriptor>(m_elf, fd_offset + m_fudge_offset);
CCC_CHECK(fd_header != nullptr, "MIPS debug file descriptor out of bounds.");
CCC_CHECK(fd_header->f_big_endian == 0, "Not little endian or bad file descriptor table.");
file.address = fd_header->address;
s32 rel_raw_path_offset = fd_header->strings_offset + fd_header->file_path_string_offset;
s32 raw_path_offset = m_hdrr->local_strings_offset + rel_raw_path_offset + m_fudge_offset;
std::optional<std::string_view> command_line_path = get_string(m_elf, raw_path_offset);
if(command_line_path.has_value()) {
file.command_line_path = *command_line_path;
}
// Parse local symbols.
for(s64 j = 0; j < fd_header->symbol_count; j++) {
u64 rel_symbol_offset = (fd_header->isym_base + j) * sizeof(SymbolHeader);
u64 symbol_offset = m_hdrr->local_symbols_offset + rel_symbol_offset + m_fudge_offset;
const SymbolHeader* symbol_header = get_unaligned<SymbolHeader>(m_elf, symbol_offset);
CCC_CHECK(symbol_header != nullptr, "Symbol header out of bounds.");
s32 strings_offset = m_hdrr->local_strings_offset + fd_header->strings_offset + m_fudge_offset;
Result<Symbol> sym = get_symbol(*symbol_header, m_elf, strings_offset);
CCC_RETURN_IF_ERROR(sym);
bool string_offset_equal = (s32) symbol_header->iss == fd_header->file_path_string_offset;
if(file.working_dir.empty() && string_offset_equal && sym->is_stabs() && sym->code() == N_SO && file.symbols.size() > 2) {
const Symbol& working_dir = file.symbols.back();
if(working_dir.is_stabs() && working_dir.code() == N_SO) {
file.working_dir = working_dir.string;
}
}
file.symbols.emplace_back(std::move(*sym));
}
// Parse procedure descriptors.
for(s64 i = 0; i < fd_header->procedure_descriptor_count; i++) {
u64 rel_procedure_offset = (fd_header->ipd_first + i) * sizeof(ProcedureDescriptor);
u64 procedure_offset = m_hdrr->procedure_descriptors_offset + rel_procedure_offset + m_fudge_offset;
const ProcedureDescriptor* procedure_descriptor = get_unaligned<ProcedureDescriptor>(m_elf, procedure_offset);
CCC_CHECK(procedure_descriptor != nullptr, "Procedure descriptor out of bounds.");
CCC_CHECK(procedure_descriptor->symbol_index < file.symbols.size(), "Symbol index out of bounds.");
file.symbols[procedure_descriptor->symbol_index].procedure_descriptor = procedure_descriptor;
}
file.full_path = merge_paths(file.working_dir, file.command_line_path);
return file;
}
Result<std::vector<Symbol>> SymbolTableReader::parse_external_symbols() const
{
CCC_ASSERT(m_ready);
std::vector<Symbol> external_symbols;
for(s64 i = 0; i < m_hdrr->external_symbols_count; i++) {
u64 sym_offset = m_hdrr->external_symbols_offset + i * sizeof(ExternalSymbolHeader);
const ExternalSymbolHeader* external_header = get_unaligned<ExternalSymbolHeader>(m_elf, sym_offset + m_fudge_offset);
CCC_CHECK(external_header != nullptr, "External header out of bounds.");
Result<Symbol> sym = get_symbol(external_header->symbol, m_elf, m_hdrr->external_strings_offset + m_fudge_offset);
CCC_RETURN_IF_ERROR(sym);
external_symbols.emplace_back(std::move(*sym));
}
return external_symbols;
}
void SymbolTableReader::print_header(FILE* dest) const
{
CCC_ASSERT(m_ready);
fprintf(dest, "Symbolic Header, magic = %hx, vstamp = %hx:\n",
(u16) m_hdrr->magic,
(u16) m_hdrr->version_stamp);
fprintf(dest, "\n");
fprintf(dest, " Offset Size (Bytes) Count\n");
fprintf(dest, " ------ ------------ -----\n");
fprintf(dest, " Line Numbers 0x%-8x " "0x%-8x " "%-8d\n",
(u32) m_hdrr->line_numbers_offset,
(u32) m_hdrr->line_numbers_size_bytes,
m_hdrr->line_number_count);
fprintf(dest, " Dense Numbers 0x%-8x " "0x%-8x " "%-8d\n",
(u32) m_hdrr->dense_numbers_offset,
(u32) m_hdrr->dense_numbers_count * 8,
m_hdrr->dense_numbers_count);
fprintf(dest, " Procedure Descriptors 0x%-8x " "0x%-8x " "%-8d\n",
(u32) m_hdrr->procedure_descriptors_offset,
(u32) m_hdrr->procedure_descriptor_count * (u32) sizeof(ProcedureDescriptor),
m_hdrr->procedure_descriptor_count);
fprintf(dest, " Local Symbols 0x%-8x " "0x%-8x " "%-8d\n",
(u32) m_hdrr->local_symbols_offset,
(u32) m_hdrr->local_symbol_count * (u32) sizeof(SymbolHeader),
m_hdrr->local_symbol_count);
fprintf(dest, " Optimization Symbols 0x%-8x " "- " "%-8d\n",
(u32) m_hdrr->optimization_symbols_offset,
m_hdrr->optimization_symbols_count);
fprintf(dest, " Auxiliary Symbols 0x%-8x " "0x%-8x " "%-8d\n",
(u32) m_hdrr->auxiliary_symbols_offset,
(u32) m_hdrr->auxiliary_symbol_count * 4,
m_hdrr->auxiliary_symbol_count);
fprintf(dest, " Local Strings 0x%-8x " "0x%-8x " "-\n",
(u32) m_hdrr->local_strings_offset,
(u32) m_hdrr->local_strings_size_bytes);
fprintf(dest, " External Strings 0x%-8x " "0x%-8x " "-\n",
(u32) m_hdrr->external_strings_offset,
(u32) m_hdrr->external_strings_size_bytes);
fprintf(dest, " File Descriptors 0x%-8x " "0x%-8x " "%-8d\n",
(u32) m_hdrr->file_descriptors_offset,
(u32) m_hdrr->file_descriptor_count * (u32) sizeof(FileDescriptor),
m_hdrr->file_descriptor_count);
fprintf(dest, " Relative File Descriptors 0x%-8x " "0x%-8x " "%-8d\n",
(u32) m_hdrr->relative_file_descriptors_offset,
(u32) m_hdrr->relative_file_descriptor_count * 4,
m_hdrr->relative_file_descriptor_count);
fprintf(dest, " External Symbols 0x%-8x " "0x%-8x " "%-8d\n",
(u32) m_hdrr->external_symbols_offset,
(u32) m_hdrr->external_symbols_count * 16,
m_hdrr->external_symbols_count);
}
Result<void> SymbolTableReader::print_symbols(FILE* out, bool print_locals, bool print_procedure_descriptors, bool print_externals) const
{
if(print_locals || print_procedure_descriptors) {
s32 count = file_count();
for(s32 i = 0; i < count; i++) {
Result<File> file = parse_file(i);
CCC_RETURN_IF_ERROR(file);
fprintf(out, "FILE %s:\n", file->command_line_path.c_str());
for(const Symbol& symbol : file->symbols) {
if(print_locals || symbol.procedure_descriptor) {
print_symbol(out, symbol);
}
if(print_procedure_descriptors && symbol.procedure_descriptor) {
print_procedure_descriptor(out, *symbol.procedure_descriptor);
}
}
}
}
if(print_externals) {
fprintf(out, "EXTERNAL SYMBOLS:\n");
Result<std::vector<Symbol>> external_symbols = parse_external_symbols();
CCC_RETURN_IF_ERROR(external_symbols);
for(const Symbol& symbol : *external_symbols) {
print_symbol(out, symbol);
}
}
return Result<void>();
}
static void print_symbol(FILE* out, const Symbol& symbol)
{
fprintf(out, " %8x ", symbol.value);
const char* symbol_type_str = symbol_type(symbol.symbol_type);
if(symbol_type_str) {
fprintf(out, "%-11s ", symbol_type_str);
} else {
fprintf(out, "ST(%7u) ", (u32) symbol.symbol_type);
}
const char* symbol_class_str = symbol_class(symbol.symbol_class);
if(symbol_class_str) {
fprintf(out, "%-4s ", symbol_class_str);
} else if ((u32) symbol.symbol_class == 0) {
fprintf(out, " ");
} else {
fprintf(out, "SC(%4u) ", (u32) symbol.symbol_class);
}
if(symbol.is_stabs()) {
fprintf(out, "%-8s ", stabs_code_to_string(symbol.code()));
} else {
fprintf(out, "SI(%4u) ", symbol.index);
}
fprintf(out, "%s\n", symbol.string);
}
static void print_procedure_descriptor(FILE* out, const ProcedureDescriptor& procedure_descriptor)
{
fprintf(out, " Address 0x%08x\n", procedure_descriptor.address);
fprintf(out, " Symbol Index %d\n", procedure_descriptor.symbol_index);
fprintf(out, " Line Number Entry Index %d\n", procedure_descriptor.line_number_entry_index);
fprintf(out, " Saved Register Mask 0x%08x\n", procedure_descriptor.saved_register_mask);
fprintf(out, " Saved Register Offset %d\n", procedure_descriptor.saved_register_offset);
fprintf(out, " Optimization Entry Index %d\n", procedure_descriptor.optimization_entry_index);
fprintf(out, " Saved Float Register Mask 0x%08x\n", procedure_descriptor.saved_float_register_mask);
fprintf(out, " Saved Float Register Offset %d\n", procedure_descriptor.saved_float_register_offset);
fprintf(out, " Frame Size %d\n", procedure_descriptor.frame_size);
fprintf(out, " Frame Pointer Register %hd\n", procedure_descriptor.frame_pointer_register);
fprintf(out, " Return PC Register %hd\n", procedure_descriptor.return_pc_register);
fprintf(out, " Line Number Low %d\n", procedure_descriptor.line_number_low);
fprintf(out, " Line Number High %d\n", procedure_descriptor.line_number_high);
fprintf(out, " Line Number Offset %d\n", procedure_descriptor.line_number_offset);
}
static Result<s32> get_corruption_fixing_fudge_offset(s32 section_offset, const SymbolicHeader& hdrr)
{
// GCC will always put the first part of the symbol table right after the
// header, so if the header says it's somewhere else we know the section has
// probably been moved without updating its contents.
s32 right_after_header = INT32_MAX;
if(hdrr.line_numbers_offset > 0) right_after_header = std::min(hdrr.line_numbers_offset, right_after_header);
if(hdrr.dense_numbers_offset > 0) right_after_header = std::min(hdrr.dense_numbers_offset, right_after_header);
if(hdrr.procedure_descriptors_offset > 0) right_after_header = std::min(hdrr.procedure_descriptors_offset, right_after_header);
if(hdrr.local_symbols_offset > 0) right_after_header = std::min(hdrr.local_symbols_offset, right_after_header);
if(hdrr.optimization_symbols_offset > 0) right_after_header = std::min(hdrr.optimization_symbols_offset, right_after_header);
if(hdrr.auxiliary_symbols_offset > 0) right_after_header = std::min(hdrr.auxiliary_symbols_offset, right_after_header);
if(hdrr.local_strings_offset > 0) right_after_header = std::min(hdrr.local_strings_offset, right_after_header);
if(hdrr.external_strings_offset > 0) right_after_header = std::min(hdrr.external_strings_offset, right_after_header);
if(hdrr.file_descriptors_offset > 0) right_after_header = std::min(hdrr.file_descriptors_offset, right_after_header);
if(hdrr.relative_file_descriptors_offset > 0) right_after_header = std::min(hdrr.relative_file_descriptors_offset, right_after_header);
if(hdrr.external_symbols_offset > 0) right_after_header = std::min(hdrr.external_symbols_offset, right_after_header);
CCC_CHECK(right_after_header >= 0 && right_after_header < INT32_MAX, "Invalid symbolic header.");
// Figure out how much we need to adjust all the file offsets by.
s32 fudge_offset = section_offset - (right_after_header - sizeof(SymbolicHeader));
if(fudge_offset != 0) {
CCC_WARN("The .mdebug section was moved without updating its contents. Adjusting file offsets by %d bytes.", fudge_offset);
}
return fudge_offset;
}
static Result<Symbol> get_symbol(const SymbolHeader& header, std::span<const u8> elf, s32 strings_offset)
{
Symbol symbol;
std::optional<std::string_view> string = get_string(elf, strings_offset + header.iss);
CCC_CHECK(string.has_value(), "Symbol has invalid string.");
symbol.string = string->data();
symbol.value = header.value;
symbol.symbol_type = (SymbolType) header.st;
symbol.symbol_class = (SymbolClass) header.sc;
symbol.index = header.index;
if(symbol.is_stabs()) {
CCC_CHECK(stabs_code_to_string(symbol.code()) != nullptr, "Bad stabs symbol code '%x'.", symbol.code());
}
return symbol;
}
const char* symbol_type(SymbolType type)
{
switch(type) {
case SymbolType::NIL: return "NIL";
case SymbolType::GLOBAL: return "GLOBAL";
case SymbolType::STATIC: return "STATIC";
case SymbolType::PARAM: return "PARAM";
case SymbolType::LOCAL: return "LOCAL";
case SymbolType::LABEL: return "LABEL";
case SymbolType::PROC: return "PROC";
case SymbolType::BLOCK: return "BLOCK";
case SymbolType::END: return "END";
case SymbolType::MEMBER: return "MEMBER";
case SymbolType::TYPEDEF: return "TYPEDEF";
case SymbolType::FILE_SYMBOL: return "FILE";
case SymbolType::STATICPROC: return "STATICPROC";
case SymbolType::CONSTANT: return "CONSTANT";
}
return nullptr;
}
const char* symbol_class(SymbolClass symbol_class)
{
switch(symbol_class) {
case SymbolClass::NIL: return "NIL";
case SymbolClass::TEXT: return "TEXT";
case SymbolClass::DATA: return "DATA";
case SymbolClass::BSS: return "BSS";
case SymbolClass::REGISTER: return "REGISTER";
case SymbolClass::ABS: return "ABS";
case SymbolClass::UNDEFINED: return "UNDEFINED";
case SymbolClass::LOCAL: return "LOCAL";
case SymbolClass::BITS: return "BITS";
case SymbolClass::DBX: return "DBX";
case SymbolClass::REG_IMAGE: return "REG_IMAGE";
case SymbolClass::INFO: return "INFO";
case SymbolClass::USER_STRUCT: return "USER_STRUCT";
case SymbolClass::SDATA: return "SDATA";
case SymbolClass::SBSS: return "SBSS";
case SymbolClass::RDATA: return "RDATA";
case SymbolClass::VAR: return "VAR";
case SymbolClass::COMMON: return "COMMON";
case SymbolClass::SCOMMON: return "SCOMMON";
case SymbolClass::VAR_REGISTER: return "VAR_REGISTER";
case SymbolClass::VARIANT: return "VARIANT";
case SymbolClass::SUNDEFINED: return "SUNDEFINED";
case SymbolClass::INIT: return "INIT";
case SymbolClass::BASED_VAR: return "BASED_VAR";
case SymbolClass::XDATA: return "XDATA";
case SymbolClass::PDATA: return "PDATA";
case SymbolClass::FINI: return "FINI";
case SymbolClass::NONGP: return "NONGP";
}
return nullptr;
}
const char* stabs_code_to_string(StabsCode code)
{
switch(code) {
case STAB: return "STAB";
case N_GSYM: return "GSYM";
case N_FNAME: return "FNAME";
case N_FUN: return "FUN";
case N_STSYM: return "STSYM";
case N_LCSYM: return "LCSYM";
case N_MAIN: return "MAIN";
case N_PC: return "PC";
case N_NSYMS: return "NSYMS";
case N_NOMAP: return "NOMAP";
case N_OBJ: return "OBJ";
case N_OPT: return "OPT";
case N_RSYM: return "RSYM";
case N_M2C: return "M2C";
case N_SLINE: return "SLINE";
case N_DSLINE: return "DSLINE";
case N_BSLINE: return "BSLINE";
case N_EFD: return "EFD";
case N_EHDECL: return "EHDECL";
case N_CATCH: return "CATCH";
case N_SSYM: return "SSYM";
case N_SO: return "SO";
case N_LSYM: return "LSYM";
case N_BINCL: return "BINCL";
case N_SOL: return "SOL";
case N_PSYM: return "PSYM";
case N_EINCL: return "EINCL";
case N_ENTRY: return "ENTRY";
case N_LBRAC: return "LBRAC";
case N_EXCL: return "EXCL";
case N_SCOPE: return "SCOPE";
case N_RBRAC: return "RBRAC";
case N_BCOMM: return "BCOMM";
case N_ECOMM: return "ECOMM";
case N_ECOML: return "ECOML";
case N_NBTEXT: return "NBTEXT";
case N_NBDATA: return "NBDATA";
case N_NBBSS: return "NBBSS";
case N_NBSTS: return "NBSTS";
case N_NBLCS: return "NBLCS";
case N_LENG: return "LENG";
}
return nullptr;
}
}

176
3rdparty/ccc/src/ccc/mdebug_section.h vendored Normal file
View File

@ -0,0 +1,176 @@
// This file is part of the Chaos Compiler Collection.
// SPDX-License-Identifier: MIT
#pragma once
#include "util.h"
namespace ccc::mdebug {
struct SymbolicHeader;
enum class SymbolType : u32 {
NIL = 0,
GLOBAL = 1,
STATIC = 2,
PARAM = 3,
LOCAL = 4,
LABEL = 5,
PROC = 6,
BLOCK = 7,
END = 8,
MEMBER = 9,
TYPEDEF = 10,
FILE_SYMBOL = 11,
STATICPROC = 14,
CONSTANT = 15
};
enum class SymbolClass : u32 {
NIL = 0,
TEXT = 1,
DATA = 2,
BSS = 3,
REGISTER = 4,
ABS = 5,
UNDEFINED = 6,
LOCAL = 7,
BITS = 8,
DBX = 9,
REG_IMAGE = 10,
INFO = 11,
USER_STRUCT = 12,
SDATA = 13,
SBSS = 14,
RDATA = 15,
VAR = 16,
COMMON = 17,
SCOMMON = 18,
VAR_REGISTER = 19,
VARIANT = 20,
SUNDEFINED = 21,
INIT = 22,
BASED_VAR = 23,
XDATA = 24,
PDATA = 25,
FINI = 26,
NONGP = 27
};
// See stab.def from gcc for documentation on what all these are.
enum StabsCode {
STAB = 0x00,
N_GSYM = 0x20,
N_FNAME = 0x22,
N_FUN = 0x24,
N_STSYM = 0x26,
N_LCSYM = 0x28,
N_MAIN = 0x2a,
N_PC = 0x30,
N_NSYMS = 0x32,
N_NOMAP = 0x34,
N_OBJ = 0x38,
N_OPT = 0x3c,
N_RSYM = 0x40,
N_M2C = 0x42,
N_SLINE = 0x44,
N_DSLINE = 0x46,
N_BSLINE = 0x48,
N_EFD = 0x4a,
N_EHDECL = 0x50,
N_CATCH = 0x54,
N_SSYM = 0x60,
N_SO = 0x64,
N_LSYM = 0x80,
N_BINCL = 0x82,
N_SOL = 0x84,
N_PSYM = 0xa0,
N_EINCL = 0xa2,
N_ENTRY = 0xa4,
N_LBRAC = 0xc0,
N_EXCL = 0xc2,
N_SCOPE = 0xc4,
N_RBRAC = 0xe0,
N_BCOMM = 0xe2,
N_ECOMM = 0xe4,
N_ECOML = 0xe8,
N_NBTEXT = 0xf0,
N_NBDATA = 0xf2,
N_NBBSS = 0xf4,
N_NBSTS = 0xf6,
N_NBLCS = 0xf8,
N_LENG = 0xfe
};
CCC_PACKED_STRUCT(ProcedureDescriptor,
/* 0x00 */ u32 address;
/* 0x04 */ u32 symbol_index;
/* 0x08 */ s32 line_number_entry_index;
/* 0x0c */ s32 saved_register_mask;
/* 0x10 */ s32 saved_register_offset;
/* 0x14 */ s32 optimization_entry_index;
/* 0x18 */ s32 saved_float_register_mask;
/* 0x1c */ s32 saved_float_register_offset;
/* 0x20 */ s32 frame_size;
/* 0x24 */ s16 frame_pointer_register;
/* 0x26 */ s16 return_pc_register;
/* 0x28 */ s32 line_number_low;
/* 0x2c */ s32 line_number_high;
/* 0x30 */ u32 line_number_offset;
)
static_assert(sizeof(ProcedureDescriptor) == 0x34);
struct Symbol {
u32 value;
SymbolType symbol_type;
SymbolClass symbol_class;
u32 index;
const char* string;
const ProcedureDescriptor* procedure_descriptor = nullptr;
bool is_stabs() const {
return (index & 0xfff00) == 0x8f300;
}
StabsCode code() const {
return (StabsCode) (index - 0x8f300);
}
};
struct File {
std::vector<Symbol> symbols;
u32 address = 0;
std::string working_dir; // The working directory of gcc.
std::string command_line_path; // The source file path passed on the command line to gcc.
std::string full_path; // The full combined path.
};
class SymbolTableReader {
public:
Result<void> init(std::span<const u8> elf, s32 section_offset);
s32 file_count() const;
Result<File> parse_file(s32 index) const;
Result<std::vector<Symbol>> parse_external_symbols() const;
void print_header(FILE* out) const;
Result<void> print_symbols(FILE* out, bool print_locals, bool print_procedure_descriptors, bool print_externals) const;
protected:
bool m_ready = false;
std::span<const u8> m_elf;
s32 m_section_offset;
// If the .mdebug section was moved without updating its contents all the
// absolute file offsets stored within will be incorrect by a fixed amount.
s32 m_fudge_offset;
const SymbolicHeader* m_hdrr;
};
const char* symbol_type(SymbolType type);
const char* symbol_class(SymbolClass symbol_class);
const char* stabs_code_to_string(StabsCode code);
}

220
3rdparty/ccc/src/ccc/mdebug_symbols.cpp vendored Normal file
View File

@ -0,0 +1,220 @@
// This file is part of the Chaos Compiler Collection.
// SPDX-License-Identifier: MIT
#include "mdebug_symbols.h"
#include "importer_flags.h"
namespace ccc::mdebug {
static void mark_duplicate_symbols(std::vector<ParsedSymbol>& symbols);
Result<std::vector<ParsedSymbol>> parse_symbols(const std::vector<mdebug::Symbol>& input, u32& importer_flags)
{
std::vector<ParsedSymbol> output;
std::string prefix;
for(const mdebug::Symbol& symbol : input) {
if(symbol.is_stabs()) {
switch(symbol.code()) {
case mdebug::N_GSYM: // Global variable
case mdebug::N_FUN: // Function
case mdebug::N_STSYM: // Data section static global variable
case mdebug::N_LCSYM: // BSS section static global variable
case mdebug::N_RSYM: // Register variable
case mdebug::N_LSYM: // Automatic variable or type definition
case mdebug::N_PSYM: { // Parameter variable
// Some STABS symbols are split between multiple strings.
if(symbol.string[0] != '\0') {
if(symbol.string[strlen(symbol.string) - 1] == '\\') {
prefix += std::string(symbol.string, symbol.string + strlen(symbol.string) - 1);
} else {
std::string merged_string;
const char* string;
if(!prefix.empty()) {
merged_string = prefix + symbol.string;
string = merged_string.c_str();
prefix.clear();
} else {
string = symbol.string;
}
const char* input = string;
Result<StabsSymbol> parse_result = parse_stabs_symbol(input);
if(parse_result.success()) {
if(*input != '\0') {
if(importer_flags & STRICT_PARSING) {
return CCC_FAILURE("Unknown data '%s' at the end of the '%s' stab.", input, parse_result->name.c_str());
} else {
CCC_WARN("Unknown data '%s' at the end of the '%s' stab.", input, parse_result->name.c_str());
}
}
ParsedSymbol& parsed = output.emplace_back();
parsed.type = ParsedSymbolType::NAME_COLON_TYPE;
parsed.raw = &symbol;
parsed.name_colon_type = std::move(*parse_result);
} else if(parse_result.error().message == STAB_TRUNCATED_ERROR_MESSAGE) {
// Symbol truncated due to a GCC bug. Report a
// warning and try to tolerate further faults
// caused as a result of this.
CCC_WARN("%s Symbol string: %s", STAB_TRUNCATED_ERROR_MESSAGE, string);
importer_flags &= ~STRICT_PARSING;
} else {
return CCC_FAILURE("%s Symbol string: %s",
parse_result.error().message.c_str(), string);
}
}
} else {
CCC_CHECK(prefix.empty(), "Invalid STABS continuation.");
if(symbol.code() == mdebug::N_FUN) {
ParsedSymbol& func_end = output.emplace_back();
func_end.type = ParsedSymbolType::FUNCTION_END;
func_end.raw = &symbol;
}
}
break;
}
case mdebug::N_SOL: { // Sub-source file
ParsedSymbol& sub = output.emplace_back();
sub.type = ParsedSymbolType::SUB_SOURCE_FILE;
sub.raw = &symbol;
break;
}
case mdebug::N_LBRAC: { // Begin block
ParsedSymbol& begin_block = output.emplace_back();
begin_block.type = ParsedSymbolType::LBRAC;
begin_block.raw = &symbol;
break;
}
case mdebug::N_RBRAC: { // End block
ParsedSymbol& end_block = output.emplace_back();
end_block.type = ParsedSymbolType::RBRAC;
end_block.raw = &symbol;
break;
}
case mdebug::N_SO: { // Source filename
ParsedSymbol& so_symbol = output.emplace_back();
so_symbol.type = ParsedSymbolType::SOURCE_FILE;
so_symbol.raw = &symbol;
break;
}
case mdebug::STAB:
case mdebug::N_OPT:
case mdebug::N_BINCL:
case mdebug::N_EINCL: {
break;
}
case mdebug::N_FNAME:
case mdebug::N_MAIN:
case mdebug::N_PC:
case mdebug::N_NSYMS:
case mdebug::N_NOMAP:
case mdebug::N_OBJ:
case mdebug::N_M2C:
case mdebug::N_SLINE:
case mdebug::N_DSLINE:
case mdebug::N_BSLINE:
case mdebug::N_EFD:
case mdebug::N_EHDECL:
case mdebug::N_CATCH:
case mdebug::N_SSYM:
case mdebug::N_ENTRY:
case mdebug::N_EXCL:
case mdebug::N_SCOPE:
case mdebug::N_BCOMM:
case mdebug::N_ECOMM:
case mdebug::N_ECOML:
case mdebug::N_NBTEXT:
case mdebug::N_NBDATA:
case mdebug::N_NBBSS:
case mdebug::N_NBSTS:
case mdebug::N_NBLCS:
case mdebug::N_LENG: {
CCC_WARN("Unhandled N_%s symbol: %s", mdebug::stabs_code_to_string(symbol.code()), symbol.string);
break;
}
}
} else {
ParsedSymbol& non_stabs_symbol = output.emplace_back();
non_stabs_symbol.type = ParsedSymbolType::NON_STABS;
non_stabs_symbol.raw = &symbol;
}
}
mark_duplicate_symbols(output);
return output;
}
static void mark_duplicate_symbols(std::vector<ParsedSymbol>& symbols)
{
std::map<StabsTypeNumber, size_t> stabs_type_number_to_symbol;
for(size_t i = 0; i < symbols.size(); i++) {
ParsedSymbol& symbol = symbols[i];
if(symbol.type == ParsedSymbolType::NAME_COLON_TYPE) {
StabsType& type = *symbol.name_colon_type.type;
if(type.type_number.valid() && type.descriptor.has_value()) {
stabs_type_number_to_symbol.emplace(type.type_number, i);
}
}
}
for(ParsedSymbol& symbol : symbols) {
symbol.is_typedef =
symbol.type == ParsedSymbolType::NAME_COLON_TYPE &&
symbol.name_colon_type.descriptor == StabsSymbolDescriptor::TYPE_NAME &&
symbol.name_colon_type.type->descriptor != StabsTypeDescriptor::ENUM;
}
for(size_t i = 0; i < symbols.size(); i++) {
ParsedSymbol& symbol = symbols[i];
if(symbol.type != ParsedSymbolType::NAME_COLON_TYPE) {
continue;
}
bool is_type =
symbol.name_colon_type.descriptor == StabsSymbolDescriptor::TYPE_NAME ||
symbol.name_colon_type.descriptor == StabsSymbolDescriptor::ENUM_STRUCT_OR_TYPE_TAG;
if(!is_type) {
continue;
}
StabsType& type = *symbol.name_colon_type.type;
if(!type.descriptor.has_value()) {
auto referenced_index = stabs_type_number_to_symbol.find(type.type_number);
if(referenced_index != stabs_type_number_to_symbol.end()) {
ParsedSymbol& referenced = symbols[referenced_index->second];
if(referenced.name_colon_type.name == symbol.name_colon_type.name) {
// symbol: "Struct:T(1,1)=s1;"
// referenced: "Struct:t(1,1)"
symbol.duplicate = true;
}
}
}
if(type.descriptor.has_value() && type.descriptor == StabsTypeDescriptor::TYPE_REFERENCE) {
auto referenced_index = stabs_type_number_to_symbol.find(type.as<StabsTypeReferenceType>().type->type_number);
if(referenced_index != stabs_type_number_to_symbol.end() && referenced_index->second != i) {
ParsedSymbol& referenced = symbols[referenced_index->second];
if(referenced.name_colon_type.name == " ") {
// referenced: " :T(1,1)=e;"
// symbol: "ErraticEnum:t(1,2)=(1,1)"
referenced.name_colon_type.name = symbol.name_colon_type.name;
referenced.is_typedef = true;
symbol.duplicate = true;
}
if(referenced.name_colon_type.name == symbol.name_colon_type.name) {
// referenced: "NamedTypedefedStruct:T(1,1)=s1;"
// symbol: "NamedTypedefedStruct:t(1,2)=(1,1)"
referenced.is_typedef = true;
symbol.duplicate = true;
}
}
}
}
}
}

32
3rdparty/ccc/src/ccc/mdebug_symbols.h vendored Normal file
View File

@ -0,0 +1,32 @@
// This file is part of the Chaos Compiler Collection.
// SPDX-License-Identifier: MIT
#pragma once
#include "util.h"
#include "stabs.h"
#include "mdebug_section.h"
namespace ccc::mdebug {
enum class ParsedSymbolType {
NAME_COLON_TYPE,
SOURCE_FILE,
SUB_SOURCE_FILE,
LBRAC,
RBRAC,
FUNCTION_END,
NON_STABS
};
struct ParsedSymbol {
ParsedSymbolType type;
const mdebug::Symbol* raw;
StabsSymbol name_colon_type;
bool duplicate = false;
bool is_typedef = false;
};
Result<std::vector<ParsedSymbol>> parse_symbols(const std::vector<mdebug::Symbol>& input, u32& importer_flags);
}

193
3rdparty/ccc/src/ccc/sndll.cpp vendored Normal file
View File

@ -0,0 +1,193 @@
// This file is part of the Chaos Compiler Collection.
// SPDX-License-Identifier: MIT
#include "sndll.h"
#include "importer_flags.h"
namespace ccc {
CCC_PACKED_STRUCT(SNDLLHeaderCommon,
/* 0x00 */ u32 magic;
/* 0x04 */ u32 relocations;
/* 0x08 */ u32 relocation_count;
/* 0x0c */ u32 symbols;
/* 0x10 */ u32 symbol_count;
/* 0x14 */ u32 elf_path;
/* 0x18 */ u32 load_func;
/* 0x1c */ u32 unload_func;
/* 0x20 */ u32 unknown_20;
/* 0x24 */ u32 unknown_24;
/* 0x28 */ u32 unknown_28;
/* 0x2c */ u32 file_size;
/* 0x30 */ u32 unknown_30;
)
CCC_PACKED_STRUCT(SNDLLHeaderV1,
/* 0x00 */ SNDLLHeaderCommon common;
)
CCC_PACKED_STRUCT(SNDLLHeaderV2,
/* 0x00 */ SNDLLHeaderCommon common;
/* 0x34 */ u32 unknown_34;
/* 0x38 */ u32 unknown_38;
)
CCC_PACKED_STRUCT(SNDLLRelocation,
/* 0x0 */ u32 unknown_0;
/* 0x4 */ u32 unknown_4;
/* 0x8 */ u32 unknown_8;
)
CCC_PACKED_STRUCT(SNDLLSymbolHeader,
/* 0x0 */ u32 string;
/* 0x4 */ u32 value;
/* 0x8 */ u8 unknown_8;
/* 0x9 */ u8 unknown_9;
/* 0xa */ SNDLLSymbolType type;
/* 0xb */ u8 processed;
)
static Result<SNDLLFile> parse_sndll_common(
std::span<const u8> image, Address address, SNDLLType type, const SNDLLHeaderCommon& common, SNDLLVersion version);
static const char* sndll_symbol_type_to_string(SNDLLSymbolType type);
Result<SNDLLFile> parse_sndll_file(std::span<const u8> image, Address address, SNDLLType type)
{
std::optional<u32> magic = copy_unaligned<u32>(image, 0);
CCC_CHECK(magic.has_value(), "Failed to read SNDLL header.");
CCC_CHECK((*magic & 0xffffff) == CCC_FOURCC("SNR\00"), "Not a SNDLL %s.", address.valid() ? "section" : "file");
char version = *magic >> 24;
switch(version) {
case '1': {
const SNDLLHeaderV1* header = get_unaligned<SNDLLHeaderV1>(image, 0);
CCC_CHECK(header, "File too small to contain SNDLL V1 header.");
return parse_sndll_common(image, address, type, header->common, SNDLL_V1);
}
case '2': {
const SNDLLHeaderV2* header = get_unaligned<SNDLLHeaderV2>(image, 0);
CCC_CHECK(header, "File too small to contain SNDLL V2 header.");
return parse_sndll_common(image, address, type, header->common, SNDLL_V2);
}
}
return CCC_FAILURE("Unknown SNDLL version '%c'.", version);
}
static Result<SNDLLFile> parse_sndll_common(
std::span<const u8> image, Address address, SNDLLType type, const SNDLLHeaderCommon& common, SNDLLVersion version)
{
SNDLLFile sndll;
sndll.address = address;
sndll.type = type;
sndll.version = version;
if(common.elf_path) {
std::optional<std::string_view> elf_path = get_string(image, common.elf_path);
CCC_CHECK(elf_path.has_value(), "SNDLL header has invalid ELF path field.");
sndll.elf_path = *elf_path;
}
CCC_CHECK(common.symbol_count < (32 * 1024 * 1024) / sizeof(SNDLLSymbol), "SNDLL symbol count is too high.");
sndll.symbols.reserve(common.symbol_count);
for(u32 i = 0; i < common.symbol_count; i++) {
u32 symbol_offset = common.symbols - address.get_or_zero() + i * sizeof(SNDLLSymbolHeader);
const SNDLLSymbolHeader* symbol_header = get_unaligned<SNDLLSymbolHeader>(image, symbol_offset);
CCC_CHECK(symbol_header, "SNDLL symbol out of range.");
std::optional<std::string_view> string;
if(symbol_header->string) {
string = get_string(image, symbol_header->string - address.get_or_zero());
}
SNDLLSymbol& symbol = sndll.symbols.emplace_back();
symbol.type = symbol_header->type;
symbol.value = symbol_header->value;
if(string.has_value()) {
symbol.string = *string;
}
}
return sndll;
}
Result<void> import_sndll_symbols(
SymbolDatabase& database,
const SNDLLFile& sndll,
const SymbolGroup& group,
u32 importer_flags,
DemanglerFunctions demangler)
{
for(const SNDLLSymbol& symbol : sndll.symbols) {
if(symbol.value == 0 || symbol.string.empty()) {
continue;
}
u32 address = symbol.value;
if(symbol.type != SNDLL_ABSOLUTE && sndll.type == SNDLLType::DYNAMIC_LIBRARY) {
address += sndll.address.get_or_zero();
}
if(!(importer_flags & DONT_DEDUPLICATE_SYMBOLS)) {
if(database.functions.first_handle_from_starting_address(address).valid()) {
continue;
}
if(database.global_variables.first_handle_from_starting_address(address).valid()) {
continue;
}
if(database.local_variables.first_handle_from_starting_address(address).valid()) {
continue;
}
}
const Section* section = database.sections.symbol_overlapping_address(address);
if(section) {
if(section->contains_code()) {
Result<Function*> function = database.functions.create_symbol(
symbol.string, group.source, group.module_symbol, address, importer_flags, demangler);
CCC_RETURN_IF_ERROR(function);
continue;
} else if(section->contains_data()) {
Result<GlobalVariable*> global_variable = database.global_variables.create_symbol(
symbol.string, group.source, group.module_symbol, address, importer_flags, demangler);
CCC_RETURN_IF_ERROR(global_variable);
continue;
}
}
Result<Label*> label = database.labels.create_symbol(
symbol.string, group.source, group.module_symbol, address, importer_flags, demangler);
CCC_RETURN_IF_ERROR(label);
}
return Result<void>();
}
void print_sndll_symbols(FILE* out, const SNDLLFile& sndll)
{
fprintf(out, "SNDLL SYMBOLS:\n");
for(const SNDLLSymbol& symbol : sndll.symbols) {
const char* type = sndll_symbol_type_to_string(symbol.type);
const char* string = !symbol.string.empty() ? symbol.string.c_str() : "(no string)";
fprintf(out, "%8s %08x %s\n", type, symbol.value, string);
}
}
static const char* sndll_symbol_type_to_string(SNDLLSymbolType type)
{
switch(type) {
case SNDLL_NIL: return "NIL";
case SNDLL_EXTERNAL: return "EXTERNAL";
case SNDLL_RELATIVE: return "RELATIVE";
case SNDLL_WEAK: return "WEAK";
case SNDLL_ABSOLUTE: return "ABSOLUTE";
}
return "invalid";
}
}

55
3rdparty/ccc/src/ccc/sndll.h vendored Normal file
View File

@ -0,0 +1,55 @@
// This file is part of the Chaos Compiler Collection.
// SPDX-License-Identifier: MIT
#pragma once
#include "symbol_database.h"
namespace ccc {
enum class SNDLLType {
SNDATA_SECTION,
DYNAMIC_LIBRARY
};
enum SNDLLVersion {
SNDLL_V1,
SNDLL_V2
};
enum SNDLLSymbolType : u8 {
SNDLL_NIL = 0, // I think this is just so that the first real symbol has an index of 1.
SNDLL_EXTERNAL = 1, // Symbol with an empty value, to be filled in from another module.
SNDLL_RELATIVE = 2, // Global symbol, value is relative to the start of the SNDLL file.
SNDLL_WEAK = 3, // Weak symbol, value is relative to the start of the SNDLL file.
SNDLL_ABSOLUTE = 4 // Global symbol, value is an absolute address.
};
struct SNDLLSymbol {
SNDLLSymbolType type = SNDLL_NIL;
u32 value = 0;
std::string string;
};
struct SNDLLFile {
Address address;
SNDLLType type;
SNDLLVersion version;
std::string elf_path;
std::vector<SNDLLSymbol> symbols;
};
// If a valid address is passed, the pointers in the header will be treated as
// addresses, otherwise they will be treated as file offsets.
Result<SNDLLFile> parse_sndll_file(std::span<const u8> image, Address address, SNDLLType type);
Result<void> import_sndll_symbols(
SymbolDatabase& database,
const SNDLLFile& sndll,
const SymbolGroup& group,
u32 importer_flags,
DemanglerFunctions demangler);
void print_sndll_symbols(FILE* out, const SNDLLFile& sndll);
}

835
3rdparty/ccc/src/ccc/stabs.cpp vendored Normal file
View File

@ -0,0 +1,835 @@
// This file is part of the Chaos Compiler Collection.
// SPDX-License-Identifier: MIT
#include "stabs.h"
namespace ccc {
#define STABS_DEBUG(...) //__VA_ARGS__
#define STABS_DEBUG_PRINTF(...) STABS_DEBUG(printf(__VA_ARGS__);)
static bool validate_symbol_descriptor(StabsSymbolDescriptor descriptor);
static Result<std::unique_ptr<StabsType>> parse_stabs_type(const char*& input);
static Result<std::vector<StabsStructOrUnionType::Field>> parse_field_list(const char*& input);
static Result<std::vector<StabsStructOrUnionType::MemberFunctionSet>> parse_member_functions(const char*& input);
static Result<StabsStructOrUnionType::Visibility> parse_visibility_character(const char*& input);
STABS_DEBUG(static void print_field(const StabsStructOrUnionType::Field& field);)
const char* STAB_TRUNCATED_ERROR_MESSAGE =
"STABS symbol truncated. This was probably caused by a GCC bug. "
"Other symbols from the same translation unit may also be invalid.";
Result<StabsSymbol> parse_stabs_symbol(const char*& input)
{
STABS_DEBUG_PRINTF("PARSING %s\n", input);
StabsSymbol symbol;
Result<std::string> name = parse_dodgy_stabs_identifier(input, ':');
CCC_RETURN_IF_ERROR(name);
symbol.name = *name;
CCC_EXPECT_CHAR(input, ':', "identifier");
CCC_CHECK(*input != '\0', "Unexpected end of input.");
if((*input >= '0' && *input <= '9') || *input == '(') {
symbol.descriptor = StabsSymbolDescriptor::LOCAL_VARIABLE;
} else {
char symbol_descriptor = *(input++);
CCC_CHECK(symbol_descriptor != '\0', "Failed to parse symbol descriptor.");
symbol.descriptor = (StabsSymbolDescriptor) symbol_descriptor;
}
CCC_CHECK(validate_symbol_descriptor(symbol.descriptor),
"Invalid symbol descriptor '%c'.",
(char) symbol.descriptor);
CCC_CHECK(*input != '\0', "Unexpected end of input.");
if(*input == 't') {
input++;
}
auto type = parse_top_level_stabs_type(input);
CCC_RETURN_IF_ERROR(type);
// Handle nested functions.
bool is_function =
symbol.descriptor == StabsSymbolDescriptor::LOCAL_FUNCTION ||
symbol.descriptor == StabsSymbolDescriptor::GLOBAL_FUNCTION;
if(is_function && input[0] == ',') {
input++;
while(*input != ',' && *input != '\0') input++; // enclosing function
CCC_EXPECT_CHAR(input, ',', "nested function suffix");
while(*input != ',' && *input != '\0') input++; // function
}
symbol.type = std::move(*type);
// Make sure that variable names aren't used as type names e.g. the STABS
// symbol "somevar:P123=*456" may be referenced by the type number 123, but
// the type name is not "somevar".
bool is_type = symbol.descriptor == StabsSymbolDescriptor::TYPE_NAME
|| symbol.descriptor == StabsSymbolDescriptor::ENUM_STRUCT_OR_TYPE_TAG;
if(is_type) {
symbol.type->name = symbol.name;
}
symbol.type->is_typedef = symbol.descriptor == StabsSymbolDescriptor::TYPE_NAME;
symbol.type->is_root = true;
return symbol;
}
static bool validate_symbol_descriptor(StabsSymbolDescriptor descriptor)
{
bool valid;
switch(descriptor) {
case StabsSymbolDescriptor::LOCAL_VARIABLE:
case StabsSymbolDescriptor::REFERENCE_PARAMETER_A:
case StabsSymbolDescriptor::LOCAL_FUNCTION:
case StabsSymbolDescriptor::GLOBAL_FUNCTION:
case StabsSymbolDescriptor::GLOBAL_VARIABLE:
case StabsSymbolDescriptor::REGISTER_PARAMETER:
case StabsSymbolDescriptor::VALUE_PARAMETER:
case StabsSymbolDescriptor::REGISTER_VARIABLE:
case StabsSymbolDescriptor::STATIC_GLOBAL_VARIABLE:
case StabsSymbolDescriptor::TYPE_NAME:
case StabsSymbolDescriptor::ENUM_STRUCT_OR_TYPE_TAG:
case StabsSymbolDescriptor::STATIC_LOCAL_VARIABLE:
case StabsSymbolDescriptor::REFERENCE_PARAMETER_V:
valid = true;
break;
default:
valid = false;
break;
}
return valid;
}
Result<std::unique_ptr<StabsType>> parse_top_level_stabs_type(const char*& input)
{
Result<std::unique_ptr<StabsType>> type = parse_stabs_type(input);
CCC_RETURN_IF_ERROR(type);
// Handle first base class suffixes.
if((*type)->descriptor == StabsTypeDescriptor::STRUCT && input[0] == '~' && input[1] == '%') {
input += 2;
Result<std::unique_ptr<StabsType>> first_base_class = parse_stabs_type(input);
CCC_RETURN_IF_ERROR(first_base_class);
(*type)->as<StabsStructType>().first_base_class = std::move(*first_base_class);
CCC_EXPECT_CHAR(input, ';', "first base class suffix");
}
// Handle extra live range information.
if(input[0] == ';' && input[1] == 'l') {
input += 2;
CCC_EXPECT_CHAR(input, '(', "live range suffix");
CCC_EXPECT_CHAR(input, '#', "live range suffix");
std::optional<s32> start = parse_number_s32(input);
CCC_CHECK(start.has_value(), "Failed to parse live range suffix.");
CCC_EXPECT_CHAR(input, ',', "live range suffix");
CCC_EXPECT_CHAR(input, '#', "live range suffix");
std::optional<s32> end = parse_number_s32(input);
CCC_CHECK(end.has_value(), "Failed to parse live range suffix.");
CCC_EXPECT_CHAR(input, ')', "live range suffix");
}
return type;
}
static Result<std::unique_ptr<StabsType>> parse_stabs_type(const char*& input)
{
StabsTypeNumber type_number;
CCC_CHECK(*input != '\0', "Unexpected end of input.");
if(*input == '(') {
// This file has type numbers made up of two pieces: an include file
// index and a type number.
input++;
std::optional<s32> file_index = parse_number_s32(input);
CCC_CHECK(file_index.has_value(), "Failed to parse type number (file index).");
CCC_EXPECT_CHAR(input, ',', "type number");
std::optional<s32> type_index = parse_number_s32(input);
CCC_CHECK(type_index.has_value(), "Failed to parse type number (type index).");
CCC_EXPECT_CHAR(input, ')', "type number");
type_number.file = *file_index;
type_number.type = *type_index;
if(*input != '=') {
return std::make_unique<StabsType>(type_number);
}
input++;
} else if(*input >= '0' && *input <= '9') {
// This file has type numbers which are just a single number. This is
// the more common case for games.
std::optional<s32> type_index = parse_number_s32(input);
CCC_CHECK(type_index.has_value(), "Failed to parse type number.");
type_number.type = *type_index;
if(*input != '=') {
return std::make_unique<StabsType>(type_number);
}
input++;
}
CCC_CHECK(*input != '\0', "Unexpected end of input.");
StabsTypeDescriptor descriptor;
if((*input >= '0' && *input <= '9') || *input == '(') {
descriptor = StabsTypeDescriptor::TYPE_REFERENCE;
} else {
char descriptor_char = *(input++);
CCC_CHECK(descriptor_char != '\0', "Failed to parse type descriptor.");
descriptor = (StabsTypeDescriptor) descriptor_char;
}
std::unique_ptr<StabsType> out_type;
switch(descriptor) {
case StabsTypeDescriptor::TYPE_REFERENCE: { // 0..9
auto type_reference = std::make_unique<StabsTypeReferenceType>(type_number);
auto type = parse_stabs_type(input);
CCC_RETURN_IF_ERROR(type);
type_reference->type = std::move(*type);
out_type = std::move(type_reference);
break;
}
case StabsTypeDescriptor::ARRAY: { // a
auto array = std::make_unique<StabsArrayType>(type_number);
auto index_type = parse_stabs_type(input);
CCC_RETURN_IF_ERROR(index_type);
array->index_type = std::move(*index_type);
auto element_type = parse_stabs_type(input);
CCC_RETURN_IF_ERROR(element_type);
array->element_type = std::move(*element_type);
out_type = std::move(array);
break;
}
case StabsTypeDescriptor::ENUM: { // e
auto enum_type = std::make_unique<StabsEnumType>(type_number);
STABS_DEBUG_PRINTF("enum {\n");
while(*input != ';') {
std::optional<std::string> name = parse_stabs_identifier(input, ':');
CCC_CHECK(name.has_value(), "Failed to parse enum field name.");
CCC_EXPECT_CHAR(input, ':', "enum");
std::optional<s32> value = parse_number_s32(input);
CCC_CHECK(value.has_value(), "Failed to parse enum value.");
enum_type->fields.emplace_back(*value, std::move(*name));
CCC_EXPECT_CHAR(input, ',', "enum");
}
input++;
STABS_DEBUG_PRINTF("}\n");
out_type = std::move(enum_type);
break;
}
case StabsTypeDescriptor::FUNCTION: { // f
auto function = std::make_unique<StabsFunctionType>(type_number);
auto return_type = parse_stabs_type(input);
CCC_RETURN_IF_ERROR(return_type);
function->return_type = std::move(*return_type);
out_type = std::move(function);
break;
}
case StabsTypeDescriptor::VOLATILE_QUALIFIER: { // B
auto volatile_qualifier = std::make_unique<StabsVolatileQualifierType>(type_number);
auto type = parse_stabs_type(input);
CCC_RETURN_IF_ERROR(type);
volatile_qualifier->type = std::move(*type);
out_type = std::move(volatile_qualifier);
break;
}
case StabsTypeDescriptor::CONST_QUALIFIER: { // k
auto const_qualifier = std::make_unique<StabsConstQualifierType>(type_number);
auto type = parse_stabs_type(input);
CCC_RETURN_IF_ERROR(type);
const_qualifier->type = std::move(*type);
out_type = std::move(const_qualifier);
break;
}
case StabsTypeDescriptor::RANGE: { // r
auto range = std::make_unique<StabsRangeType>(type_number);
auto type = parse_stabs_type(input);
CCC_RETURN_IF_ERROR(type);
range->type = std::move(*type);
CCC_EXPECT_CHAR(input, ';', "range type descriptor");
std::optional<std::string> low = parse_stabs_identifier(input, ';');
CCC_CHECK(low.has_value(), "Failed to parse low part of range.");
CCC_EXPECT_CHAR(input, ';', "low range value");
std::optional<std::string> high = parse_stabs_identifier(input, ';');
CCC_CHECK(high.has_value(), "Failed to parse high part of range.");
CCC_EXPECT_CHAR(input, ';', "high range value");
range->low = std::move(*low);
range->high = std::move(*high);
out_type = std::move(range);
break;
}
case StabsTypeDescriptor::STRUCT: { // s
auto struct_type = std::make_unique<StabsStructType>(type_number);
STABS_DEBUG_PRINTF("struct {\n");
std::optional<s64> struct_size = parse_number_s64(input);
CCC_CHECK(struct_size.has_value(), "Failed to parse struct size.");
struct_type->size = *struct_size;
if(*input == '!') {
input++;
std::optional<s32> base_class_count = parse_number_s32(input);
CCC_CHECK(base_class_count.has_value(), "Failed to parse base class count.");
CCC_EXPECT_CHAR(input, ',', "base class section");
for(s64 i = 0; i < *base_class_count; i++) {
StabsStructOrUnionType::BaseClass base_class;
char is_virtual = *(input++);
switch(is_virtual) {
case '0': base_class.is_virtual = false; break;
case '1': base_class.is_virtual = true; break;
default: return CCC_FAILURE("Failed to parse base class (virtual character).");
}
Result<StabsStructOrUnionType::Visibility> visibility = parse_visibility_character(input);
CCC_RETURN_IF_ERROR(visibility);
base_class.visibility = *visibility;
std::optional<s32> offset = parse_number_s32(input);
CCC_CHECK(offset.has_value(), "Failed to parse base class offset.");
base_class.offset = (s32) *offset;
CCC_EXPECT_CHAR(input, ',', "base class section");
auto base_class_type = parse_stabs_type(input);
CCC_RETURN_IF_ERROR(base_class_type);
base_class.type = std::move(*base_class_type);
CCC_EXPECT_CHAR(input, ';', "base class section");
struct_type->base_classes.emplace_back(std::move(base_class));
}
}
auto fields = parse_field_list(input);
CCC_RETURN_IF_ERROR(fields);
struct_type->fields = std::move(*fields);
auto member_functions = parse_member_functions(input);
CCC_RETURN_IF_ERROR(member_functions);
struct_type->member_functions = std::move(*member_functions);
STABS_DEBUG_PRINTF("}\n");
out_type = std::move(struct_type);
break;
}
case StabsTypeDescriptor::UNION: { // u
auto union_type = std::make_unique<StabsUnionType>(type_number);
STABS_DEBUG_PRINTF("union {\n");
std::optional<s64> union_size = parse_number_s64(input);
CCC_CHECK(union_size.has_value(), "Failed to parse struct size.");
union_type->size = *union_size;
auto fields = parse_field_list(input);
CCC_RETURN_IF_ERROR(fields);
union_type->fields = std::move(*fields);
auto member_functions = parse_member_functions(input);
CCC_RETURN_IF_ERROR(member_functions);
union_type->member_functions = std::move(*member_functions);
STABS_DEBUG_PRINTF("}\n");
out_type = std::move(union_type);
break;
}
case StabsTypeDescriptor::CROSS_REFERENCE: { // x
auto cross_reference = std::make_unique<StabsCrossReferenceType>(type_number);
char cross_reference_type = *(input++);
CCC_CHECK(cross_reference_type != '\0', "Failed to parse cross reference type.");
switch(cross_reference_type) {
case 'e': cross_reference->type = ast::ForwardDeclaredType::ENUM; break;
case 's': cross_reference->type = ast::ForwardDeclaredType::STRUCT; break;
case 'u': cross_reference->type = ast::ForwardDeclaredType::UNION; break;
default:
return CCC_FAILURE("Invalid cross reference type '%c'.", cross_reference->type);
}
Result<std::string> identifier = parse_dodgy_stabs_identifier(input, ':');
CCC_RETURN_IF_ERROR(identifier);
cross_reference->identifier = std::move(*identifier);
cross_reference->name = cross_reference->identifier;
CCC_EXPECT_CHAR(input, ':', "cross reference");
out_type = std::move(cross_reference);
break;
}
case StabsTypeDescriptor::FLOATING_POINT_BUILTIN: { // R
auto fp_builtin = std::make_unique<StabsFloatingPointBuiltInType>(type_number);
std::optional<s32> fpclass = parse_number_s32(input);
CCC_CHECK(fpclass.has_value(), "Failed to parse floating point built-in class.");
fp_builtin->fpclass = *fpclass;
CCC_EXPECT_CHAR(input, ';', "floating point builtin");
std::optional<s32> bytes = parse_number_s32(input);
CCC_CHECK(bytes.has_value(), "Failed to parse floating point built-in.");
fp_builtin->bytes = *bytes;
CCC_EXPECT_CHAR(input, ';', "floating point builtin");
std::optional<s32> value = parse_number_s32(input);
CCC_CHECK(value.has_value(), "Failed to parse floating point built-in.");
CCC_EXPECT_CHAR(input, ';', "floating point builtin");
out_type = std::move(fp_builtin);
break;
}
case StabsTypeDescriptor::METHOD: { // #
auto method = std::make_unique<StabsMethodType>(type_number);
if(*input == '#') {
input++;
auto return_type = parse_stabs_type(input);
CCC_RETURN_IF_ERROR(return_type);
method->return_type = std::move(*return_type);
if(*input == ';') {
input++;
}
} else {
auto class_type = parse_stabs_type(input);
CCC_RETURN_IF_ERROR(class_type);
method->class_type = std::move(*class_type);
CCC_EXPECT_CHAR(input, ',', "method");
auto return_type = parse_stabs_type(input);
CCC_RETURN_IF_ERROR(return_type);
method->return_type = std::move(*return_type);
while(*input != '\0') {
if(*input == ';') {
input++;
break;
}
CCC_EXPECT_CHAR(input, ',', "method");
auto parameter_type = parse_stabs_type(input);
CCC_RETURN_IF_ERROR(parameter_type);
method->parameter_types.emplace_back(std::move(*parameter_type));
}
}
out_type = std::move(method);
break;
}
case StabsTypeDescriptor::REFERENCE: { // &
auto reference = std::make_unique<StabsReferenceType>(type_number);
auto value_type = parse_stabs_type(input);
CCC_RETURN_IF_ERROR(value_type);
reference->value_type = std::move(*value_type);
out_type = std::move(reference);
break;
}
case StabsTypeDescriptor::POINTER: { // *
auto pointer = std::make_unique<StabsPointerType>(type_number);
auto value_type = parse_stabs_type(input);
CCC_RETURN_IF_ERROR(value_type);
pointer->value_type = std::move(*value_type);
out_type = std::move(pointer);
break;
}
case StabsTypeDescriptor::TYPE_ATTRIBUTE: { // @
if((*input >= '0' && *input <= '9') || *input == '(') {
auto member_pointer = std::make_unique<StabsPointerToDataMemberType>(type_number);
auto class_type = parse_stabs_type(input);
CCC_RETURN_IF_ERROR(class_type);
member_pointer->class_type = std::move(*class_type);
CCC_EXPECT_CHAR(input, ',', "pointer to non-static data member");
auto member_type = parse_stabs_type(input);
CCC_RETURN_IF_ERROR(member_type);
member_pointer->member_type = std::move(*member_type);
out_type = std::move(member_pointer);
} else {
auto type_attribute = std::make_unique<StabsSizeTypeAttributeType>(type_number);
CCC_CHECK(*input == 's', "Weird value following '@' type descriptor.");
input++;
std::optional<s64> size_bits = parse_number_s64(input);
CCC_CHECK(size_bits.has_value(), "Failed to parse type attribute.")
type_attribute->size_bits = *size_bits;
CCC_EXPECT_CHAR(input, ';', "type attribute");
auto type = parse_stabs_type(input);
CCC_RETURN_IF_ERROR(type);
type_attribute->type = std::move(*type);
out_type = std::move(type_attribute);
}
break;
}
case StabsTypeDescriptor::BUILTIN: { // -
auto built_in = std::make_unique<StabsBuiltInType>(type_number);
std::optional<s64> type_id = parse_number_s64(input);
CCC_CHECK(type_id.has_value(), "Failed to parse built-in.");
built_in->type_id = *type_id;
CCC_EXPECT_CHAR(input, ';', "builtin");
out_type = std::move(built_in);
break;
}
default: {
return CCC_FAILURE(
"Invalid type descriptor '%c' (%02x).",
(u32) descriptor, (u32) descriptor);
}
}
return out_type;
}
static Result<std::vector<StabsStructOrUnionType::Field>> parse_field_list(const char*& input)
{
std::vector<StabsStructOrUnionType::Field> fields;
while(*input != '\0') {
if(*input == ';') {
input++;
break;
}
const char* before_field = input;
StabsStructOrUnionType::Field field;
Result<std::string> name = parse_dodgy_stabs_identifier(input, ':');
CCC_RETURN_IF_ERROR(name);
field.name = std::move(*name);
CCC_EXPECT_CHAR(input, ':', "identifier");
if(*input == '/') {
input++;
Result<StabsStructOrUnionType::Visibility> visibility = parse_visibility_character(input);
CCC_RETURN_IF_ERROR(visibility);
field.visibility = *visibility;
}
if(*input == ':') {
input = before_field;
break;
}
auto type = parse_stabs_type(input);
CCC_RETURN_IF_ERROR(type);
field.type = std::move(*type);
if(field.name.size() >= 1 && field.name[0] == '$') {
// Virtual function table pointers and virtual base class pointers.
CCC_EXPECT_CHAR(input, ',', "field type");
std::optional<s32> offset_bits = parse_number_s32(input);
CCC_CHECK(offset_bits.has_value(), "Failed to parse field offset.");
field.offset_bits = *offset_bits;
CCC_EXPECT_CHAR(input, ';', "field offset");
} else if(*input == ':') {
// Static fields.
input++;
field.is_static = true;
std::optional<std::string> type_name = parse_stabs_identifier(input, ';');
CCC_CHECK(type_name.has_value(), "Failed to parse static field type name.");
field.type_name = std::move(*type_name);
CCC_EXPECT_CHAR(input, ';', "identifier");
} else if(*input == ',') {
// Normal fields.
input++;
std::optional<s32> offset_bits = parse_number_s32(input);
CCC_CHECK(offset_bits.has_value(), "Failed to parse field offset.");
field.offset_bits = *offset_bits;
CCC_EXPECT_CHAR(input, ',', "field offset");
std::optional<s32> size_bits = parse_number_s32(input);
CCC_CHECK(size_bits.has_value(), "Failed to parse field size.");
field.size_bits = *size_bits;
CCC_EXPECT_CHAR(input, ';', "field size");
} else {
return CCC_FAILURE("Expected ':' or ',', got '%c' (%hhx).", *input, *input);
}
STABS_DEBUG(print_field(field);)
fields.emplace_back(std::move(field));
}
return fields;
}
static Result<std::vector<StabsStructOrUnionType::MemberFunctionSet>> parse_member_functions(const char*& input)
{
// Check for if the next character is from an enclosing field list. If this
// is the case, the next character will be ',' for normal fields and ':' for
// static fields (see above).
if(*input == ',' || *input == ':') {
return std::vector<StabsStructOrUnionType::MemberFunctionSet>();
}
std::vector<StabsStructOrUnionType::MemberFunctionSet> member_functions;
while(*input != '\0') {
if(*input == ';') {
input++;
break;
}
StabsStructOrUnionType::MemberFunctionSet member_function_set;
std::optional<std::string> name = parse_stabs_identifier(input, ':');
CCC_CHECK(name.has_value(), "Failed to parse member function name.");
member_function_set.name = std::move(*name);
CCC_EXPECT_CHAR(input, ':', "member function");
CCC_EXPECT_CHAR(input, ':', "member function");
while(*input != '\0') {
if(*input == ';') {
input++;
break;
}
StabsStructOrUnionType::MemberFunction function;
auto type = parse_stabs_type(input);
CCC_RETURN_IF_ERROR(type);
function.type = std::move(*type);
CCC_EXPECT_CHAR(input, ':', "member function");
std::optional<std::string> identifier = parse_stabs_identifier(input, ';');
CCC_CHECK(identifier.has_value(), "Invalid member function identifier.");
CCC_EXPECT_CHAR(input, ';', "member function");
Result<StabsStructOrUnionType::Visibility> visibility = parse_visibility_character(input);
CCC_RETURN_IF_ERROR(visibility);
function.visibility = *visibility;
char modifiers = *(input++);
CCC_CHECK(modifiers != '\0', "Failed to parse member function modifiers.");
switch(modifiers) {
case 'A':
function.is_const = false;
function.is_volatile = false;
break;
case 'B':
function.is_const = true;
function.is_volatile = false;
break;
case 'C':
function.is_const = false;
function.is_volatile = true;
break;
case 'D':
function.is_const = true;
function.is_volatile = true;
break;
case '?':
case '.':
break;
default:
return CCC_FAILURE("Invalid member function modifiers.");
}
char flag = *(input++);
CCC_CHECK(flag != '\0', "Failed to parse member function type.");
switch(flag) {
case '.': { // normal member function
function.modifier = ast::MemberFunctionModifier::NONE;
break;
}
case '?': { // static member function
function.modifier = ast::MemberFunctionModifier::STATIC;
break;
}
case '*': { // virtual member function
std::optional<s32> vtable_index = parse_number_s32(input);
CCC_CHECK(vtable_index.has_value(), "Failed to parse vtable index.");
function.vtable_index = *vtable_index;
CCC_EXPECT_CHAR(input, ';', "virtual member function");
auto virtual_type = parse_stabs_type(input);
CCC_RETURN_IF_ERROR(virtual_type);
function.virtual_type = std::move(*virtual_type);
CCC_EXPECT_CHAR(input, ';', "virtual member function");
function.modifier = ast::MemberFunctionModifier::VIRTUAL;
break;
}
default:
return CCC_FAILURE("Invalid member function type.");
}
member_function_set.overloads.emplace_back(std::move(function));
}
STABS_DEBUG_PRINTF("member func: %s\n", member_function_set.name.c_str());
member_functions.emplace_back(std::move(member_function_set));
}
return member_functions;
}
static Result<StabsStructOrUnionType::Visibility> parse_visibility_character(const char*& input)
{
char visibility = *(input++);
switch(visibility) {
case '0': return StabsStructOrUnionType::Visibility::PRIVATE;
case '1': return StabsStructOrUnionType::Visibility::PROTECTED;
case '2': return StabsStructOrUnionType::Visibility::PUBLIC;
case '9': return StabsStructOrUnionType::Visibility::PUBLIC_OPTIMIZED_OUT;
default: break;
}
return CCC_FAILURE("Failed to parse visibility character.");
}
std::optional<s32> parse_number_s32(const char*& input)
{
char* end;
s64 value = strtoll(input, &end, 10);
if(end == input) {
return std::nullopt;
}
input = end;
return (s32) value;
}
std::optional<s64> parse_number_s64(const char*& input)
{
char* end;
s64 value = strtoll(input, &end, 10);
if(end == input) {
return std::nullopt;
}
input = end;
return value;
}
std::optional<std::string> parse_stabs_identifier(const char*& input, char terminator)
{
const char* begin = input;
for(; *input != '\0'; input++) {
if(*input == terminator) {
return std::string(begin, input);
}
}
return std::nullopt;
}
// The complexity here is because the input may contain an unescaped namespace
// separator '::' even if the field terminator is supposed to be a colon, as
// well as the raw contents of character literals. See test/ccc/stabs_tests.cpp
// for some examples.
Result<std::string> parse_dodgy_stabs_identifier(const char*& input, char terminator)
{
const char* begin = input;
s32 template_depth = 0;
for(; *input != '\0'; input++) {
// Skip past character literals.
if(*input == '\'') {
input++;
if(*input == '\'') {
input++; // Handle character literals containing a single quote.
}
while(*input != '\'' && *input != '\0') {
input++;
}
if(*input == '\0') {
break;
}
input++;
}
// Keep track of the template depth so we know when to expect the
// terminator character.
if(*input == '<') {
template_depth++;
}
if(*input == '>') {
template_depth--;
}
if(*input == terminator && template_depth == 0) {
return std::string(begin, input);
}
}
return CCC_FAILURE(STAB_TRUNCATED_ERROR_MESSAGE);
}
STABS_DEBUG(
static void print_field(const StabsStructOrUnionType::Field& field)
{
printf("\t%04x %04x %04x %04x %s\n", field.offset_bits / 8, field.size_bits / 8, field.offset_bits, field.size_bits, field.name.c_str());
}
)
const char* stabs_field_visibility_to_string(StabsStructOrUnionType::Visibility visibility)
{
switch(visibility) {
case StabsStructOrUnionType::Visibility::PRIVATE: return "private";
case StabsStructOrUnionType::Visibility::PROTECTED: return "protected";
case StabsStructOrUnionType::Visibility::PUBLIC: return "public";
case StabsStructOrUnionType::Visibility::PUBLIC_OPTIMIZED_OUT: return "public_optimizedout";
default: return "none";
}
return "";
}
}

379
3rdparty/ccc/src/ccc/stabs.h vendored Normal file
View File

@ -0,0 +1,379 @@
// This file is part of the Chaos Compiler Collection.
// SPDX-License-Identifier: MIT
#pragma once
#include "ast.h"
#include "util.h"
namespace ccc {
enum class StabsSymbolDescriptor : u8 {
LOCAL_VARIABLE = '_',
REFERENCE_PARAMETER_A = 'a',
LOCAL_FUNCTION = 'f',
GLOBAL_FUNCTION = 'F',
GLOBAL_VARIABLE = 'G',
REGISTER_PARAMETER = 'P',
VALUE_PARAMETER = 'p',
REGISTER_VARIABLE = 'r',
STATIC_GLOBAL_VARIABLE = 'S',
TYPE_NAME = 't',
ENUM_STRUCT_OR_TYPE_TAG = 'T',
STATIC_LOCAL_VARIABLE = 'V',
REFERENCE_PARAMETER_V = 'v'
};
struct StabsType;
struct StabsSymbol {
StabsSymbolDescriptor descriptor;
std::string name;
std::unique_ptr<StabsType> type;
};
Result<StabsSymbol> parse_stabs_symbol(const char*& input);
enum class StabsTypeDescriptor : u8 {
TYPE_REFERENCE = 0xef, // '0'..'9','('
ARRAY = 'a',
ENUM = 'e',
FUNCTION = 'f',
CONST_QUALIFIER = 'k',
RANGE = 'r',
STRUCT = 's',
UNION = 'u',
CROSS_REFERENCE = 'x',
VOLATILE_QUALIFIER = 'B',
FLOATING_POINT_BUILTIN = 'R',
METHOD = '#',
REFERENCE = '&',
POINTER = '*',
TYPE_ATTRIBUTE = '@',
POINTER_TO_DATA_MEMBER = 0xee, // also '@'
BUILTIN = '-'
};
struct StabsBaseClass;
struct StabsField;
struct StabsMemberFunctionSet;
// e.g. for "123=*456" 123 would be the type_number, the type descriptor would
// be of type POINTER and StabsPointerType::value_type would point to a type
// with type_number = 456.
struct StabsType {
StabsTypeNumber type_number;
// The name field is only populated for root types and cross references.
std::optional<std::string> name;
bool is_typedef = false;
bool is_root = false;
std::optional<StabsTypeDescriptor> descriptor;
StabsType(StabsTypeNumber n) : type_number(n) {}
StabsType(StabsTypeDescriptor d) : descriptor(d) {}
StabsType(StabsTypeNumber n, StabsTypeDescriptor d) : type_number(n), descriptor(d) {}
virtual ~StabsType() {}
template <typename SubType>
SubType& as()
{
CCC_ASSERT(descriptor == SubType::DESCRIPTOR);
return *static_cast<SubType*>(this);
}
template <typename SubType>
const SubType& as() const
{
CCC_ASSERT(descriptor == SubType::DESCRIPTOR);
return *static_cast<const SubType*>(this);
}
virtual void enumerate_numbered_types(std::map<StabsTypeNumber, const StabsType*>& output) const
{
if(type_number.valid() && descriptor.has_value()) {
output.emplace(type_number, this);
}
}
};
struct StabsTypeReferenceType : StabsType {
std::unique_ptr<StabsType> type;
StabsTypeReferenceType(StabsTypeNumber n) : StabsType(n, DESCRIPTOR) {}
static const constexpr StabsTypeDescriptor DESCRIPTOR = StabsTypeDescriptor::TYPE_REFERENCE;
void enumerate_numbered_types(std::map<StabsTypeNumber, const StabsType*>& output) const override
{
StabsType::enumerate_numbered_types(output);
type->enumerate_numbered_types(output);
}
};
struct StabsArrayType : StabsType {
std::unique_ptr<StabsType> index_type;
std::unique_ptr<StabsType> element_type;
StabsArrayType(StabsTypeNumber n) : StabsType(n, DESCRIPTOR) {}
static const constexpr StabsTypeDescriptor DESCRIPTOR = StabsTypeDescriptor::ARRAY;
void enumerate_numbered_types(std::map<StabsTypeNumber, const StabsType*>& output) const override
{
StabsType::enumerate_numbered_types(output);
index_type->enumerate_numbered_types(output);
element_type->enumerate_numbered_types(output);
}
};
struct StabsEnumType : StabsType {
std::vector<std::pair<s32, std::string>> fields;
StabsEnumType(StabsTypeNumber n) : StabsType(n, DESCRIPTOR) {}
static const constexpr StabsTypeDescriptor DESCRIPTOR = StabsTypeDescriptor::ENUM;
};
struct StabsFunctionType : StabsType {
std::unique_ptr<StabsType> return_type;
StabsFunctionType(StabsTypeNumber n) : StabsType(n, DESCRIPTOR) {}
static const constexpr StabsTypeDescriptor DESCRIPTOR = StabsTypeDescriptor::FUNCTION;
void enumerate_numbered_types(std::map<StabsTypeNumber, const StabsType*>& output) const override
{
StabsType::enumerate_numbered_types(output);
return_type->enumerate_numbered_types(output);
}
};
struct StabsVolatileQualifierType : StabsType {
std::unique_ptr<StabsType> type;
StabsVolatileQualifierType(StabsTypeNumber n) : StabsType(n, DESCRIPTOR) {}
static const constexpr StabsTypeDescriptor DESCRIPTOR = StabsTypeDescriptor::VOLATILE_QUALIFIER;
void enumerate_numbered_types(std::map<StabsTypeNumber, const StabsType*>& output) const override
{
StabsType::enumerate_numbered_types(output);
type->enumerate_numbered_types(output);
}
};
struct StabsConstQualifierType : StabsType {
std::unique_ptr<StabsType> type;
StabsConstQualifierType(StabsTypeNumber n) : StabsType(n, DESCRIPTOR) {}
static const constexpr StabsTypeDescriptor DESCRIPTOR = StabsTypeDescriptor::CONST_QUALIFIER;
void enumerate_numbered_types(std::map<StabsTypeNumber, const StabsType*>& output) const override
{
StabsType::enumerate_numbered_types(output);
type->enumerate_numbered_types(output);
}
};
struct StabsRangeType : StabsType {
std::unique_ptr<StabsType> type;
std::string low;
std::string high; // Some compilers wrote out a wrapped around value here for zero (or variable?) length arrays.
StabsRangeType(StabsTypeNumber n) : StabsType(n, DESCRIPTOR) {}
static const constexpr StabsTypeDescriptor DESCRIPTOR = StabsTypeDescriptor::RANGE;
void enumerate_numbered_types(std::map<StabsTypeNumber, const StabsType*>& output) const override
{
StabsType::enumerate_numbered_types(output);
type->enumerate_numbered_types(output);
}
};
struct StabsStructOrUnionType : StabsType {
enum class Visibility : u8 {
NONE,
PRIVATE,
PROTECTED,
PUBLIC,
PUBLIC_OPTIMIZED_OUT
};
struct BaseClass {
bool is_virtual;
Visibility visibility;
s32 offset = -1;
std::unique_ptr<StabsType> type;
};
struct Field {
std::string name;
Visibility visibility = Visibility::NONE;
std::unique_ptr<StabsType> type;
bool is_static = false;
s32 offset_bits = 0;
s32 size_bits = 0;
std::string type_name;
};
struct MemberFunction {
std::unique_ptr<StabsType> type;
std::unique_ptr<StabsType> virtual_type;
Visibility visibility;
bool is_const = false;
bool is_volatile = false;
ast::MemberFunctionModifier modifier = ast::MemberFunctionModifier::NONE;
s32 vtable_index = -1;
};
struct MemberFunctionSet {
std::string name;
std::vector<MemberFunction> overloads;
};
s64 size = -1;
std::vector<BaseClass> base_classes;
std::vector<Field> fields;
std::vector<MemberFunctionSet> member_functions;
std::unique_ptr<StabsType> first_base_class;
StabsStructOrUnionType(StabsTypeNumber n, StabsTypeDescriptor d) : StabsType(n, d) {}
void enumerate_numbered_types(std::map<StabsTypeNumber, const StabsType*>& output) const override
{
StabsType::enumerate_numbered_types(output);
for(const BaseClass& base_class : base_classes) {
base_class.type->enumerate_numbered_types(output);
}
for(const Field& field : fields) {
field.type->enumerate_numbered_types(output);
}
for(const MemberFunctionSet& member_function_set : member_functions) {
for(const MemberFunction& member_function : member_function_set.overloads) {
member_function.type->enumerate_numbered_types(output);
if(member_function.virtual_type.get()) {
member_function.virtual_type->enumerate_numbered_types(output);
}
}
}
if(first_base_class.get()) {
first_base_class->enumerate_numbered_types(output);
}
}
};
struct StabsStructType : StabsStructOrUnionType {
StabsStructType(StabsTypeNumber n) : StabsStructOrUnionType(n, DESCRIPTOR) {}
static const constexpr StabsTypeDescriptor DESCRIPTOR = StabsTypeDescriptor::STRUCT;
};
struct StabsUnionType : StabsStructOrUnionType {
StabsUnionType(StabsTypeNumber n) : StabsStructOrUnionType(n, DESCRIPTOR) {}
static const constexpr StabsTypeDescriptor DESCRIPTOR = StabsTypeDescriptor::UNION;
};
struct StabsCrossReferenceType : StabsType {
ast::ForwardDeclaredType type;
std::string identifier;
StabsCrossReferenceType(StabsTypeNumber n) : StabsType(n, DESCRIPTOR) {}
static const constexpr StabsTypeDescriptor DESCRIPTOR = StabsTypeDescriptor::CROSS_REFERENCE;
};
struct StabsFloatingPointBuiltInType : StabsType {
s32 fpclass = -1;
s32 bytes = -1;
StabsFloatingPointBuiltInType(StabsTypeNumber n) : StabsType(n, DESCRIPTOR) {}
static const constexpr StabsTypeDescriptor DESCRIPTOR = StabsTypeDescriptor::FLOATING_POINT_BUILTIN;
};
struct StabsMethodType : StabsType {
std::unique_ptr<StabsType> return_type;
std::optional<std::unique_ptr<StabsType>> class_type;
std::vector<std::unique_ptr<StabsType>> parameter_types;
StabsMethodType(StabsTypeNumber n) : StabsType(n, DESCRIPTOR) {}
static const constexpr StabsTypeDescriptor DESCRIPTOR = StabsTypeDescriptor::METHOD;
void enumerate_numbered_types(std::map<StabsTypeNumber, const StabsType*>& output) const override
{
StabsType::enumerate_numbered_types(output);
return_type->enumerate_numbered_types(output);
if(class_type.has_value()) {
(*class_type)->enumerate_numbered_types(output);
}
for(const std::unique_ptr<StabsType>& parameter_type : parameter_types) {
parameter_type->enumerate_numbered_types(output);
}
}
};
struct StabsReferenceType : StabsType {
std::unique_ptr<StabsType> value_type;
StabsReferenceType(StabsTypeNumber n) : StabsType(n, DESCRIPTOR) {}
static const constexpr StabsTypeDescriptor DESCRIPTOR = StabsTypeDescriptor::REFERENCE;
void enumerate_numbered_types(std::map<StabsTypeNumber, const StabsType*>& output) const override
{
StabsType::enumerate_numbered_types(output);
value_type->enumerate_numbered_types(output);
}
};
struct StabsPointerType : StabsType {
std::unique_ptr<StabsType> value_type;
StabsPointerType(StabsTypeNumber n) : StabsType(n, DESCRIPTOR) {}
static const constexpr StabsTypeDescriptor DESCRIPTOR = StabsTypeDescriptor::POINTER;
void enumerate_numbered_types(std::map<StabsTypeNumber, const StabsType*>& output) const override
{
StabsType::enumerate_numbered_types(output);
value_type->enumerate_numbered_types(output);
}
};
struct StabsSizeTypeAttributeType : StabsType {
s64 size_bits = -1;
std::unique_ptr<StabsType> type;
StabsSizeTypeAttributeType(StabsTypeNumber n) : StabsType(n, DESCRIPTOR) {}
static const constexpr StabsTypeDescriptor DESCRIPTOR = StabsTypeDescriptor::TYPE_ATTRIBUTE;
void enumerate_numbered_types(std::map<StabsTypeNumber, const StabsType*>& output) const override
{
StabsType::enumerate_numbered_types(output);
type->enumerate_numbered_types(output);
}
};
struct StabsPointerToDataMemberType : StabsType {
std::unique_ptr<StabsType> class_type;
std::unique_ptr<StabsType> member_type;
StabsPointerToDataMemberType(StabsTypeNumber n) : StabsType(n, DESCRIPTOR) {}
static const constexpr StabsTypeDescriptor DESCRIPTOR = StabsTypeDescriptor::POINTER_TO_DATA_MEMBER;
void enumerate_numbered_types(std::map<StabsTypeNumber, const StabsType*>& output) const override
{
StabsType::enumerate_numbered_types(output);
class_type->enumerate_numbered_types(output);
member_type->enumerate_numbered_types(output);
}
};
struct StabsBuiltInType : StabsType {
s64 type_id = -1;
StabsBuiltInType(StabsTypeNumber n) : StabsType(n, DESCRIPTOR) {}
static const constexpr StabsTypeDescriptor DESCRIPTOR = StabsTypeDescriptor::BUILTIN;
};
extern const char* STAB_TRUNCATED_ERROR_MESSAGE;
Result<std::unique_ptr<StabsType>> parse_top_level_stabs_type(const char*& input);
std::optional<s32> parse_number_s32(const char*& input);
std::optional<s64> parse_number_s64(const char*& input);
std::optional<std::string> parse_stabs_identifier(const char*& input, char terminator);
Result<std::string> parse_dodgy_stabs_identifier(const char*& input, char terminator);
const char* stabs_field_visibility_to_string(StabsStructOrUnionType::Visibility visibility);
}

834
3rdparty/ccc/src/ccc/stabs_to_ast.cpp vendored Normal file
View File

@ -0,0 +1,834 @@
// This file is part of the Chaos Compiler Collection.
// SPDX-License-Identifier: MIT
#include "stabs_to_ast.h"
#include "importer_flags.h"
#define AST_DEBUG(...) //__VA_ARGS__
#define AST_DEBUG_PRINTF(...) AST_DEBUG(printf(__VA_ARGS__);)
namespace ccc {
struct MemberFunctionInfo {
std::string name;
bool is_constructor_or_destructor = false;
bool is_special_member_function = false;
bool is_operator_member_function = false;
};
static bool is_void_like(const StabsType& type);
static Result<ast::BuiltInClass> classify_range(const StabsRangeType& type);
static Result<std::unique_ptr<ast::Node>> field_to_ast(
const StabsStructOrUnionType::Field& field,
const StabsType& enclosing_struct,
const StabsToAstState& state,
s32 depth);
static Result<bool> detect_bitfield(const StabsStructOrUnionType::Field& field, const StabsToAstState& state);
static Result<std::vector<std::unique_ptr<ast::Node>>> member_functions_to_ast(
const StabsStructOrUnionType& type, const StabsToAstState& state, s32 depth);
static MemberFunctionInfo check_member_function(
const std::string& mangled_name,
std::string_view type_name_no_template_args,
const DemanglerFunctions& demangler,
u32 importer_flags);
Result<std::unique_ptr<ast::Node>> stabs_type_to_ast(
const StabsType& type,
const StabsType* enclosing_struct,
const StabsToAstState& state,
s32 depth,
bool substitute_type_name,
bool force_substitute)
{
AST_DEBUG_PRINTF("%-*stype desc=%hhx '%c' num=(%d,%d) name=%s\n",
depth * 4, "",
type.descriptor.has_value() ? (u8) *type.descriptor : 'X',
(type.descriptor.has_value() && isprint((u8) *type.descriptor)) ? (u8) *type.descriptor : '!',
type.type_number.file, type.type_number.type,
type.name.has_value() ? type.name->c_str() : "");
if(depth > 200) {
const char* error_message = "Call depth greater than 200 in stabs_type_to_ast, probably infinite recursion.";
if(state.importer_flags & STRICT_PARSING) {
return CCC_FAILURE(error_message);
} else {
CCC_WARN(error_message);
auto error = std::make_unique<ast::Error>();
error->message = error_message;
return std::unique_ptr<ast::Node>(std::move(error));
}
}
// This makes sure that types are replaced with their type name in cases
// where that would be more appropriate.
if(type.name.has_value()) {
bool try_substitute = depth > 0 && (type.is_root
|| type.descriptor == StabsTypeDescriptor::RANGE
|| type.descriptor == StabsTypeDescriptor::BUILTIN);
// GCC emits anonymous enums with a name of " " since apparently some
// debuggers can't handle zero-length names.
bool is_name_empty = type.name == "" || type.name == " ";
// Cross references will be handled below.
bool is_cross_reference = type.descriptor == StabsTypeDescriptor::CROSS_REFERENCE;
bool is_void = is_void_like(type);
if((substitute_type_name || try_substitute) && !is_name_empty && !is_cross_reference && !is_void) {
auto type_name = std::make_unique<ast::TypeName>();
type_name->source = ast::TypeNameSource::REFERENCE;
type_name->unresolved_stabs = std::make_unique<ast::TypeName::UnresolvedStabs>();
type_name->unresolved_stabs->type_name = *type.name;
type_name->unresolved_stabs->referenced_file_handle = state.file_handle;
type_name->unresolved_stabs->stabs_type_number = type.type_number;
return std::unique_ptr<ast::Node>(std::move(type_name));
}
}
// This prevents infinite recursion when an automatically generated member
// function references an unnamed type.
bool can_compare_type_numbers = type.type_number.valid() && enclosing_struct && enclosing_struct->type_number.valid();
if(force_substitute && can_compare_type_numbers && type.type_number == enclosing_struct->type_number) {
// It's probably a this parameter (or return type) for an unnamed type.
auto type_name = std::make_unique<ast::TypeName>();
type_name->source = ast::TypeNameSource::UNNAMED_THIS;
type_name->unresolved_stabs = std::make_unique<ast::TypeName::UnresolvedStabs>();
type_name->unresolved_stabs->type_name = enclosing_struct->name.has_value() ? *enclosing_struct->name : "";
type_name->unresolved_stabs->referenced_file_handle = state.file_handle;
type_name->unresolved_stabs->stabs_type_number = type.type_number;
return std::unique_ptr<ast::Node>(std::move(type_name));
}
if(!type.descriptor.has_value()) {
// The definition of the type has been defined previously, so we have to
// look it up by its type number.
CCC_CHECK(type.type_number.valid(), "Cannot lookup type (type is anonymous).");
auto stabs_type = state.stabs_types->find(type.type_number);
if(stabs_type == state.stabs_types->end()) {
std::string error_message = "Failed to lookup STABS type by its type number ("
+ std::to_string(type.type_number.file) + "," + std::to_string(type.type_number.type) + ").";
if(state.importer_flags & STRICT_PARSING) {
return CCC_FAILURE("%s", error_message.c_str());
} else {
CCC_WARN("%s", error_message.c_str());
std::unique_ptr<ast::Error> error = std::make_unique<ast::Error>();
error->message = std::move(error_message);
return std::unique_ptr<ast::Node>(std::move(error));
}
}
return stabs_type_to_ast(
*stabs_type->second,
enclosing_struct,
state,
depth + 1,
substitute_type_name,
force_substitute);
}
std::unique_ptr<ast::Node> result;
switch(*type.descriptor) {
case StabsTypeDescriptor::TYPE_REFERENCE: {
const auto& stabs_type_ref = type.as<StabsTypeReferenceType>();
if(!type.type_number.valid() || !stabs_type_ref.type->type_number.valid() || stabs_type_ref.type->type_number != type.type_number) {
auto node = stabs_type_to_ast(
*stabs_type_ref.type,
enclosing_struct,
state,
depth + 1,
substitute_type_name,
force_substitute);
CCC_RETURN_IF_ERROR(node);
result = std::move(*node);
} else {
// I still don't know why in STABS void is a reference to
// itself, maybe because I'm not a philosopher.
auto builtin = std::make_unique<ast::BuiltIn>();
builtin->bclass = ast::BuiltInClass::VOID_TYPE;
result = std::move(builtin);
}
break;
}
case StabsTypeDescriptor::ARRAY: {
auto array = std::make_unique<ast::Array>();
const auto& stabs_array = type.as<StabsArrayType>();
auto element_node = stabs_type_to_ast(
*stabs_array.element_type,
enclosing_struct,
state,
depth + 1,
true,
force_substitute);
CCC_RETURN_IF_ERROR(element_node);
array->element_type = std::move(*element_node);
const StabsRangeType& index = stabs_array.index_type->as<StabsRangeType>();
char* end = nullptr;
const char* low = index.low.c_str();
s64 low_value = strtoll(low, &end, 10);
CCC_CHECK(end != low, "Failed to parse low part of range as integer.");
CCC_CHECK(low_value == 0, "Invalid index type for array.");
const char* high = index.high.c_str();
s64 high_value = strtoll(high, &end, 10);
CCC_CHECK(end != high, "Failed to parse low part of range as integer.");
if(high_value == 4294967295) {
// Some compilers wrote out a wrapped around value here.
array->element_count = 0;
} else {
array->element_count = (s32) high_value + 1;
}
result = std::move(array);
break;
}
case StabsTypeDescriptor::ENUM: {
auto inline_enum = std::make_unique<ast::Enum>();
const auto& stabs_enum = type.as<StabsEnumType>();
inline_enum->constants = stabs_enum.fields;
result = std::move(inline_enum);
break;
}
case StabsTypeDescriptor::FUNCTION: {
auto function = std::make_unique<ast::Function>();
auto node = stabs_type_to_ast(
*type.as<StabsFunctionType>().return_type,
enclosing_struct,
state,
depth + 1,
true,
force_substitute);
CCC_RETURN_IF_ERROR(node);
function->return_type = std::move(*node);
result = std::move(function);
break;
}
case StabsTypeDescriptor::VOLATILE_QUALIFIER: {
const auto& volatile_qualifier = type.as<StabsVolatileQualifierType>();
auto node = stabs_type_to_ast(
*volatile_qualifier.type.get(),
enclosing_struct,
state,
depth + 1,
substitute_type_name,
force_substitute);
CCC_RETURN_IF_ERROR(node);
result = std::move(*node);
result->is_volatile = true;
break;
}
case StabsTypeDescriptor::CONST_QUALIFIER: {
const auto& const_qualifier = type.as<StabsConstQualifierType>();
auto node = stabs_type_to_ast(
*const_qualifier.type.get(),
enclosing_struct,
state,
depth + 1,
substitute_type_name,
force_substitute);
result = std::move(*node);
result->is_const = true;
break;
}
case StabsTypeDescriptor::RANGE: {
auto builtin = std::make_unique<ast::BuiltIn>();
Result<ast::BuiltInClass> bclass = classify_range(type.as<StabsRangeType>());
CCC_RETURN_IF_ERROR(bclass);
builtin->bclass = *bclass;
result = std::move(builtin);
break;
}
case StabsTypeDescriptor::STRUCT:
case StabsTypeDescriptor::UNION: {
const StabsStructOrUnionType* stabs_struct_or_union;
if(type.descriptor == StabsTypeDescriptor::STRUCT) {
stabs_struct_or_union = &type.as<StabsStructType>();
} else {
stabs_struct_or_union = &type.as<StabsUnionType>();
}
auto struct_or_union = std::make_unique<ast::StructOrUnion>();
struct_or_union->is_struct = type.descriptor == StabsTypeDescriptor::STRUCT;
struct_or_union->size_bits = (s32) stabs_struct_or_union->size * 8;
for(const StabsStructOrUnionType::BaseClass& stabs_base_class : stabs_struct_or_union->base_classes) {
auto base_class = stabs_type_to_ast(
*stabs_base_class.type,
&type,
state,
depth + 1,
true,
force_substitute);
CCC_RETURN_IF_ERROR(base_class);
(*base_class)->offset_bytes = stabs_base_class.offset;
(*base_class)->set_access_specifier(stabs_field_visibility_to_access_specifier(stabs_base_class.visibility), state.importer_flags);
if(stabs_base_class.is_virtual) {
(*base_class)->is_virtual_base_class = true;
}
struct_or_union->base_classes.emplace_back(std::move(*base_class));
}
AST_DEBUG_PRINTF("%-*s beginfields\n", depth * 4, "");
for(const StabsStructOrUnionType::Field& field : stabs_struct_or_union->fields) {
auto node = field_to_ast(field, type, state, depth);
CCC_RETURN_IF_ERROR(node);
struct_or_union->fields.emplace_back(std::move(*node));
}
AST_DEBUG_PRINTF("%-*s endfields\n", depth * 4, "");
AST_DEBUG_PRINTF("%-*s beginmemberfuncs\n", depth * 4, "");
Result<std::vector<std::unique_ptr<ast::Node>>> member_functions =
member_functions_to_ast(*stabs_struct_or_union, state, depth);
CCC_RETURN_IF_ERROR(member_functions);
struct_or_union->member_functions = std::move(*member_functions);
AST_DEBUG_PRINTF("%-*s endmemberfuncs\n", depth * 4, "");
result = std::move(struct_or_union);
break;
}
case StabsTypeDescriptor::CROSS_REFERENCE: {
const auto& cross_reference = type.as<StabsCrossReferenceType>();
auto type_name = std::make_unique<ast::TypeName>();
type_name->source = ast::TypeNameSource::CROSS_REFERENCE;
type_name->unresolved_stabs = std::make_unique<ast::TypeName::UnresolvedStabs>();
type_name->unresolved_stabs->type_name = cross_reference.identifier;
type_name->unresolved_stabs->type = cross_reference.type;
result = std::move(type_name);
break;
}
case ccc::StabsTypeDescriptor::FLOATING_POINT_BUILTIN: {
const auto& fp_builtin = type.as<StabsFloatingPointBuiltInType>();
auto builtin = std::make_unique<ast::BuiltIn>();
switch(fp_builtin.bytes) {
case 1: builtin->bclass = ast::BuiltInClass::UNSIGNED_8; break;
case 2: builtin->bclass = ast::BuiltInClass::UNSIGNED_16; break;
case 4: builtin->bclass = ast::BuiltInClass::UNSIGNED_32; break;
case 8: builtin->bclass = ast::BuiltInClass::UNSIGNED_64; break;
case 16: builtin->bclass = ast::BuiltInClass::UNSIGNED_128; break;
default: builtin->bclass = ast::BuiltInClass::UNSIGNED_8; break;
}
result = std::move(builtin);
break;
}
case StabsTypeDescriptor::METHOD: {
const auto& stabs_method = type.as<StabsMethodType>();
auto function = std::make_unique<ast::Function>();
auto return_node = stabs_type_to_ast(
*stabs_method.return_type.get(),
enclosing_struct,
state,
depth + 1,
true,
true);
CCC_RETURN_IF_ERROR(return_node);
function->return_type = std::move(*return_node);
function->parameters.emplace();
for(const std::unique_ptr<StabsType>& parameter_type : stabs_method.parameter_types) {
auto parameter_node = stabs_type_to_ast(
*parameter_type,
enclosing_struct,
state,
depth + 1,
true,
true);
CCC_RETURN_IF_ERROR(parameter_node);
function->parameters->emplace_back(std::move(*parameter_node));
}
result = std::move(function);
break;
}
case StabsTypeDescriptor::POINTER: {
auto pointer = std::make_unique<ast::PointerOrReference>();
pointer->is_pointer = true;
auto value_node = stabs_type_to_ast(
*type.as<StabsPointerType>().value_type,
enclosing_struct,
state,
depth + 1,
true,
force_substitute);
CCC_RETURN_IF_ERROR(value_node);
pointer->value_type = std::move(*value_node);
result = std::move(pointer);
break;
}
case StabsTypeDescriptor::REFERENCE: {
auto reference = std::make_unique<ast::PointerOrReference>();
reference->is_pointer = false;
auto value_node = stabs_type_to_ast(
*type.as<StabsReferenceType>().value_type,
enclosing_struct,
state,
depth + 1,
true,
force_substitute);
CCC_RETURN_IF_ERROR(value_node);
reference->value_type = std::move(*value_node);
result = std::move(reference);
break;
}
case StabsTypeDescriptor::TYPE_ATTRIBUTE: {
const auto& stabs_type_attribute = type.as<StabsSizeTypeAttributeType>();
auto node = stabs_type_to_ast(
*stabs_type_attribute.type,
enclosing_struct,
state,
depth + 1,
substitute_type_name,
force_substitute);
CCC_RETURN_IF_ERROR(node);
result = std::move(*node);
result->size_bits = (s32) stabs_type_attribute.size_bits;
break;
}
case StabsTypeDescriptor::POINTER_TO_DATA_MEMBER: {
const auto& stabs_member_pointer = type.as<StabsPointerToDataMemberType>();
auto member_pointer = std::make_unique<ast::PointerToDataMember>();
auto class_node = stabs_type_to_ast(
*stabs_member_pointer.class_type.get(),
enclosing_struct,
state,
depth + 1,
true,
true);
CCC_RETURN_IF_ERROR(class_node);
member_pointer->class_type = std::move(*class_node);
auto member_node = stabs_type_to_ast(
*stabs_member_pointer.member_type.get(),
enclosing_struct,
state,
depth + 1,
true,
true);
CCC_RETURN_IF_ERROR(member_node);
member_pointer->member_type = std::move(*member_node);
result = std::move(member_pointer);
break;
}
case StabsTypeDescriptor::BUILTIN: {
CCC_CHECK(type.as<StabsBuiltInType>().type_id == 16,
"Unknown built-in type!");
auto builtin = std::make_unique<ast::BuiltIn>();
builtin->bclass = ast::BuiltInClass::BOOL_8;
result = std::move(builtin);
break;
}
}
CCC_CHECK(result, "Result of stabs_type_to_ast call is nullptr.");
return result;
}
static bool is_void_like(const StabsType& type)
{
// Unfortunately, a common case seems to be that various types (most
// commonly __builtin_va_list) are indistinguishable from void or void*, so
// we have to output them as a void built-in.
if(type.descriptor.has_value()) {
switch(*type.descriptor) {
case StabsTypeDescriptor::POINTER: {
return is_void_like(*type.as<StabsPointerType>().value_type.get());
}
case StabsTypeDescriptor::TYPE_REFERENCE: {
return type.as<StabsTypeReferenceType>().type->type_number == type.type_number;
}
default: {
break;
}
}
}
return false;
}
static Result<ast::BuiltInClass> classify_range(const StabsRangeType& type)
{
const char* low = type.low.c_str();
const char* high = type.high.c_str();
// Handle some special cases and values that are too large to easily store
// in a 64-bit integer.
static const struct { const char* low; const char* high; ast::BuiltInClass classification; } strings[] = {
{"4", "0", ast::BuiltInClass::FLOAT_32},
{"000000000000000000000000", "001777777777777777777777", ast::BuiltInClass::UNSIGNED_64},
{"00000000000000000000000000000000000000000000", "00000000000000000000001777777777777777777777", ast::BuiltInClass::UNSIGNED_64},
{"0000000000000", "01777777777777777777777", ast::BuiltInClass::UNSIGNED_64}, // IOP
{"0", "18446744073709551615", ast::BuiltInClass::UNSIGNED_64},
{"001000000000000000000000", "000777777777777777777777", ast::BuiltInClass::SIGNED_64},
{"00000000000000000000001000000000000000000000", "00000000000000000000000777777777777777777777", ast::BuiltInClass::SIGNED_64},
{"01000000000000000000000", "0777777777777777777777", ast::BuiltInClass::SIGNED_64}, // IOP
{"-9223372036854775808", "9223372036854775807", ast::BuiltInClass::SIGNED_64},
{"8", "0", ast::BuiltInClass::FLOAT_64},
{"00000000000000000000000000000000000000000000", "03777777777777777777777777777777777777777777", ast::BuiltInClass::UNSIGNED_128},
{"02000000000000000000000000000000000000000000", "01777777777777777777777777777777777777777777", ast::BuiltInClass::SIGNED_128},
{"000000000000000000000000", "0377777777777777777777777777777777", ast::BuiltInClass::UNQUALIFIED_128},
{"16", "0", ast::BuiltInClass::FLOAT_128},
{"0", "-1", ast::BuiltInClass::UNQUALIFIED_128} // Old homebrew toolchain
};
for(const auto& range : strings) {
if(strcmp(range.low, low) == 0 && strcmp(range.high, high) == 0) {
return range.classification;
}
}
// For smaller values we actually parse the bounds as integers.
char* end = nullptr;
s64 low_value = strtoll(type.low.c_str(), &end, low[0] == '0' ? 8 : 10);
CCC_CHECK(end != low, "Failed to parse low part of range as integer.");
s64 high_value = strtoll(type.high.c_str(), &end, high[0] == '0' ? 8 : 10);
CCC_CHECK(end != high, "Failed to parse high part of range as integer.");
static const struct { s64 low; s64 high; ast::BuiltInClass classification; } integers[] = {
{0, 255, ast::BuiltInClass::UNSIGNED_8},
{-128, 127, ast::BuiltInClass::SIGNED_8},
{0, 127, ast::BuiltInClass::UNQUALIFIED_8},
{0, 65535, ast::BuiltInClass::UNSIGNED_16},
{-32768, 32767, ast::BuiltInClass::SIGNED_16},
{0, 4294967295, ast::BuiltInClass::UNSIGNED_32},
{-2147483648, 2147483647, ast::BuiltInClass::SIGNED_32},
};
for(const auto& range : integers) {
if((range.low == low_value || range.low == -low_value) && range.high == high_value) {
return range.classification;
}
}
return CCC_FAILURE("Failed to classify range.");
}
static Result<std::unique_ptr<ast::Node>> field_to_ast(
const StabsStructOrUnionType::Field& field,
const StabsType& enclosing_struct,
const StabsToAstState& state,
s32 depth)
{
AST_DEBUG_PRINTF("%-*s field %s\n", depth * 4, "", field.name.c_str());
Result<bool> is_bitfield = detect_bitfield(field, state);
CCC_RETURN_IF_ERROR(is_bitfield);
if(*is_bitfield) {
// Process bitfields.
auto bitfield_node = stabs_type_to_ast(
*field.type,
&enclosing_struct,
state,
depth + 1,
true,
false);
CCC_RETURN_IF_ERROR(bitfield_node);
std::unique_ptr<ast::BitField> bitfield = std::make_unique<ast::BitField>();
bitfield->name = (field.name == " ") ? "" : field.name;
bitfield->offset_bytes = field.offset_bits / 8;
bitfield->size_bits = field.size_bits;
bitfield->underlying_type = std::move(*bitfield_node);
bitfield->bitfield_offset_bits = field.offset_bits % 8;
bitfield->set_access_specifier(stabs_field_visibility_to_access_specifier(field.visibility), state.importer_flags);
return std::unique_ptr<ast::Node>(std::move(bitfield));
} else {
// Process a normal field.
Result<std::unique_ptr<ast::Node>> node = stabs_type_to_ast(
*field.type,
&enclosing_struct,
state,
depth + 1,
true,
false);
CCC_RETURN_IF_ERROR(node);
(*node)->name = field.name;
(*node)->offset_bytes = field.offset_bits / 8;
(*node)->size_bits = field.size_bits;
(*node)->set_access_specifier(stabs_field_visibility_to_access_specifier(field.visibility), state.importer_flags);
if(field.name.starts_with("$vf") || field.name.starts_with("_vptr$") || field.name.starts_with("_vptr.")) {
(*node)->is_vtable_pointer = true;
}
if(field.is_static) {
(*node)->storage_class = STORAGE_CLASS_STATIC;
}
return node;
}
}
static Result<bool> detect_bitfield(const StabsStructOrUnionType::Field& field, const StabsToAstState& state)
{
// Static fields can't be bitfields.
if(field.is_static) {
return false;
}
// Resolve type references.
const StabsType* type = field.type.get();
for(s32 i = 0; i < 50; i++) {
if(!type->descriptor.has_value()) {
if(!type->type_number.valid()) {
return false;
}
auto next_type = state.stabs_types->find(type->type_number);
if(next_type == state.stabs_types->end() || next_type->second == type) {
return false;
}
type = next_type->second;
} else if(type->descriptor == StabsTypeDescriptor::TYPE_REFERENCE) {
type = type->as<StabsTypeReferenceType>().type.get();
} else if(type->descriptor == StabsTypeDescriptor::CONST_QUALIFIER) {
type = type->as<StabsConstQualifierType>().type.get();
} else if(type->descriptor == StabsTypeDescriptor::VOLATILE_QUALIFIER) {
type = type->as<StabsVolatileQualifierType>().type.get();
} else {
break;
}
// Prevent an infinite loop if there's a cycle (fatal frame).
if(i == 49) {
return false;
}
}
// Determine the size of the underlying type.
s32 underlying_type_size_bits = 0;
switch(*type->descriptor) {
case ccc::StabsTypeDescriptor::RANGE: {
Result<ast::BuiltInClass> bclass = classify_range(type->as<StabsRangeType>());
CCC_RETURN_IF_ERROR(bclass);
underlying_type_size_bits = builtin_class_size(*bclass) * 8;
break;
}
case ccc::StabsTypeDescriptor::CROSS_REFERENCE: {
if(type->as<StabsCrossReferenceType>().type == ast::ForwardDeclaredType::ENUM) {
underlying_type_size_bits = 32;
} else {
return false;
}
break;
}
case ccc::StabsTypeDescriptor::TYPE_ATTRIBUTE: {
underlying_type_size_bits = (s32) type->as<StabsSizeTypeAttributeType>().size_bits;
break;
}
case ccc::StabsTypeDescriptor::BUILTIN: {
underlying_type_size_bits = 8; // bool
break;
}
default: {
return false;
}
}
if(underlying_type_size_bits == 0) {
return false;
}
return field.size_bits != underlying_type_size_bits;
}
static Result<std::vector<std::unique_ptr<ast::Node>>> member_functions_to_ast(
const StabsStructOrUnionType& type, const StabsToAstState& state, s32 depth)
{
if(state.importer_flags & NO_MEMBER_FUNCTIONS) {
return std::vector<std::unique_ptr<ast::Node>>();
}
std::string_view type_name_no_template_args;
if(type.name.has_value()) {
type_name_no_template_args =
std::string_view(*type.name).substr(0, type.name->find("<"));
}
std::vector<std::unique_ptr<ast::Node>> member_functions;
bool only_special_functions = true;
for(const StabsStructOrUnionType::MemberFunctionSet& function_set : type.member_functions) {
MemberFunctionInfo info = check_member_function(
function_set.name, type_name_no_template_args, state.demangler, state.importer_flags);
if(!info.is_special_member_function) {
only_special_functions = false;
}
for(const StabsStructOrUnionType::MemberFunction& stabs_func : function_set.overloads) {
auto node = stabs_type_to_ast(
*stabs_func.type,
&type,
state,
depth + 1,
true,
true);
CCC_RETURN_IF_ERROR(node);
(*node)->is_constructor_or_destructor = info.is_constructor_or_destructor;
(*node)->is_special_member_function = info.is_special_member_function;
(*node)->is_operator_member_function = info.is_operator_member_function;
(*node)->name = info.name;
(*node)->set_access_specifier(stabs_field_visibility_to_access_specifier(stabs_func.visibility), state.importer_flags);
if((*node)->descriptor == ast::FUNCTION) {
ast::Function& function = (*node)->as<ast::Function>();
function.modifier = stabs_func.modifier;
function.vtable_index = stabs_func.vtable_index;
}
member_functions.emplace_back(std::move(*node));
}
}
if(only_special_functions && (state.importer_flags & INCLUDE_GENERATED_MEMBER_FUNCTIONS) == 0) {
return std::vector<std::unique_ptr<ast::Node>>();
}
return member_functions;
}
static MemberFunctionInfo check_member_function(
const std::string& mangled_name,
std::string_view type_name_no_template_args,
const DemanglerFunctions& demangler,
u32 importer_flags)
{
MemberFunctionInfo info;
// Some compiler versions output gcc opnames for overloaded operators
// instead of their proper names.
if((importer_flags & DONT_DEMANGLE_NAMES) == 0 && demangler.cplus_demangle_opname) {
char* demangled_name = demangler.cplus_demangle_opname(mangled_name.c_str(), 0);
if(demangled_name) {
info.name = demangled_name;
free(reinterpret_cast<void*>(demangled_name));
}
}
if(info.name.empty()) {
info.name = mangled_name;
}
bool is_constructor =
info.name == "__ct" || // Takes a parameter to decide whether or not to construct virtual base classes.
info.name == "__comp_ctor" || // Constructs virtual base classes.
info.name == "__base_ctor"; // Does not construct virtual base classes.
if(!is_constructor && !type_name_no_template_args.empty()) {
is_constructor |= info.name == type_name_no_template_args; // Named constructor.
}
bool is_destructor =
info.name == "__dt" || // Takes parameters to decide whether or not to construct virtual base classes and/or delete the object.
info.name == "__comp_dtor" || // Destructs virtual base classes.
info.name == "__base_dtor" || // Does not construct virtual base classes.
info.name == "__deleting_dtor"; // Destructs virtual base clases then deletes the entire object.
if(!is_destructor && !info.name.empty()) {
is_destructor |= info.name[0] == '~' && std::string_view(info.name).substr(1) == type_name_no_template_args; // Named destructor.
}
info.is_constructor_or_destructor = is_constructor || is_destructor || info.name.starts_with("$_");
info.is_special_member_function = info.is_constructor_or_destructor || info.name == "operator=";
return info;
}
void fix_recursively_emitted_structures(
ast::StructOrUnion& outer_struct, const std::string& name, StabsTypeNumber type_number, SourceFileHandle file_handle)
{
// This is a rather peculiar case. For some compiler versions, when a struct
// or a union defined using a typedef is being emitted and it needs to
// reference itself from a member function parameter, it will emit its
// entire definition again in the middle of the first definition, although
// thankfully it won't recurse more than once.
//
// The game Sega Soccer Slam is affected by this. See the PeculiarParameter
// test case in mdebug_importer_tests.cpp for a bare bones example.
for(std::unique_ptr<ast::Node>& node : outer_struct.member_functions) {
if(node->descriptor != ast::FUNCTION) {
continue;
}
ast::Function& function = node->as<ast::Function>();
if(!function.parameters.has_value()) {
continue;
}
for(std::unique_ptr<ast::Node>& parameter : *function.parameters) {
if(parameter->descriptor != ast::POINTER_OR_REFERENCE) {
continue;
}
ast::PointerOrReference& pointer_or_reference = parameter->as<ast::PointerOrReference>();
if(pointer_or_reference.value_type->descriptor != ast::STRUCT_OR_UNION) {
continue;
}
ast::StructOrUnion& inner_struct = pointer_or_reference.value_type->as<ast::StructOrUnion>();
// Since C++ doesn't allow struct definitions in function parameter
// lists normally, and most of the time the member function
// parameters aren't even filled in by GCC, this is a really rare
// case, so here we only bother to do some very basic checks to
// verify that the inner struct is similar to the outer struct.
if(inner_struct.base_classes.size() != outer_struct.base_classes.size()) {
continue;
}
if(inner_struct.fields.size() != outer_struct.fields.size()) {
continue;
}
if(inner_struct.member_functions.size() != outer_struct.member_functions.size()) {
continue;
}
auto type_name = std::make_unique<ast::TypeName>();
type_name->source = ast::TypeNameSource::REFERENCE;
type_name->unresolved_stabs = std::make_unique<ast::TypeName::UnresolvedStabs>();
type_name->unresolved_stabs->type_name = name;
type_name->unresolved_stabs->referenced_file_handle = file_handle;
type_name->unresolved_stabs->stabs_type_number = type_number;
pointer_or_reference.value_type = std::move(type_name);
}
}
}
ast::AccessSpecifier stabs_field_visibility_to_access_specifier(StabsStructOrUnionType::Visibility visibility)
{
ast::AccessSpecifier access_specifier = ast::AS_PUBLIC;
switch(visibility) {
case StabsStructOrUnionType::Visibility::NONE: access_specifier = ast::AS_PUBLIC; break;
case StabsStructOrUnionType::Visibility::PUBLIC: access_specifier = ast::AS_PUBLIC; break;
case StabsStructOrUnionType::Visibility::PROTECTED: access_specifier = ast::AS_PROTECTED; break;
case StabsStructOrUnionType::Visibility::PRIVATE: access_specifier = ast::AS_PRIVATE; break;
case StabsStructOrUnionType::Visibility::PUBLIC_OPTIMIZED_OUT: access_specifier = ast::AS_PUBLIC; break;
}
return access_specifier;
}
}

29
3rdparty/ccc/src/ccc/stabs_to_ast.h vendored Normal file
View File

@ -0,0 +1,29 @@
// This file is part of the Chaos Compiler Collection.
// SPDX-License-Identifier: MIT
#pragma once
#include "ast.h"
#include "stabs.h"
namespace ccc {
struct StabsToAstState {
u32 file_handle;
std::map<StabsTypeNumber, const StabsType*>* stabs_types;
u32 importer_flags;
DemanglerFunctions demangler;
};
Result<std::unique_ptr<ast::Node>> stabs_type_to_ast(
const StabsType& type,
const StabsType* enclosing_struct,
const StabsToAstState& state,
s32 depth,
bool substitute_type_name,
bool force_substitute);
void fix_recursively_emitted_structures(
ast::StructOrUnion& outer_struct, const std::string& name, StabsTypeNumber type_number, SourceFileHandle file_handle);
ast::AccessSpecifier stabs_field_visibility_to_access_specifier(StabsStructOrUnionType::Visibility visibility);
}

1204
3rdparty/ccc/src/ccc/symbol_database.cpp vendored Normal file

File diff suppressed because it is too large Load Diff

721
3rdparty/ccc/src/ccc/symbol_database.h vendored Normal file
View File

@ -0,0 +1,721 @@
// This file is part of the Chaos Compiler Collection.
// SPDX-License-Identifier: MIT
#pragma once
#include <map>
#include <atomic>
#include <variant>
#include "util.h"
namespace ccc {
// An X macro for all the symbol types.
#define CCC_FOR_EACH_SYMBOL_TYPE_DO_X \
CCC_X(DataType, data_types) \
CCC_X(Function, functions) \
CCC_X(GlobalVariable, global_variables) \
CCC_X(Label, labels) \
CCC_X(LocalVariable, local_variables) \
CCC_X(Module, modules) \
CCC_X(ParameterVariable, parameter_variables) \
CCC_X(Section, sections) \
CCC_X(SourceFile, source_files) \
CCC_X(SymbolSource, symbol_sources)
// An enum for all the symbol types.
enum SymbolDescriptor {
DATA_TYPE = 1 << 0,
FUNCTION = 1 << 1,
GLOBAL_VARIABLE = 1 << 2,
LABEL = 1 << 3,
LOCAL_VARIABLE = 1 << 4,
MODULE = 1 << 5,
PARAMETER_VARIABLE = 1 << 6,
SECTION = 1 << 7,
SOURCE_FILE = 1 << 8,
SYMBOL_SOURCE = 1 << 9
};
enum {
ALL_SYMBOL_TYPES = 0xffff
};
// Forward declare all the different types of symbol objects.
#define CCC_X(SymbolType, symbol_list) class SymbolType;
CCC_FOR_EACH_SYMBOL_TYPE_DO_X
#undef CCC_X
class SymbolDatabase;
// Strongly typed handles for all of the symbol objects. These are here to solve
// the problem of dangling references to symbols.
template <typename SymbolType>
struct SymbolHandle {
u32 value = (u32) -1;
SymbolHandle() {}
SymbolHandle(u32 v) : value(v) {}
SymbolHandle(const SymbolType* symbol)
: value(symbol ? symbol->handle().value : (u32) -1) {}
// Check if this symbol handle has been initialised. Note that this doesn't
// determine whether or not the symbol it points to has been deleted!
bool valid() const { return value != (u32) -1; }
friend auto operator<=>(const SymbolHandle& lhs, const SymbolHandle& rhs) = default;
};
#define CCC_X(SymbolType, symbol_list) using SymbolType##Handle = SymbolHandle<SymbolType>;
CCC_FOR_EACH_SYMBOL_TYPE_DO_X
#undef CCC_X
enum SymbolFlag {
NO_SYMBOL_FLAGS = 0,
WITH_ADDRESS_MAP = 1 << 0,
WITH_NAME_MAP = 1 << 1,
NAME_NEEDS_DEMANGLING = 1 << 2
};
// A container class for symbols of a given type that maintains maps of their
// names and addresses depending on the value of SymbolType::FLAGS.
template <typename SymbolType>
class SymbolList {
public:
// Lookup symbols from their handles using binary search.
SymbolType* symbol_from_handle(SymbolHandle<SymbolType> handle);
const SymbolType* symbol_from_handle(SymbolHandle<SymbolType> handle) const;
// Lookup multiple symbols from their handles using binary search.
std::vector<SymbolType*> symbols_from_handles(const std::vector<SymbolHandle<SymbolType>>& handles);
std::vector<const SymbolType*> symbols_from_handles(const std::vector<SymbolHandle<SymbolType>>& handles) const;
std::vector<SymbolType*> optional_symbols_from_handles(const std::optional<std::vector<SymbolHandle<SymbolType>>>& handles);
std::vector<const SymbolType*> optional_symbols_from_handles(const std::optional<std::vector<SymbolHandle<SymbolType>>>& handles) const;
using Iterator = typename std::vector<SymbolType>::iterator;
using ConstIterator = typename std::vector<SymbolType>::const_iterator;
// For iterating over all the symbols.
Iterator begin();
ConstIterator begin() const;
Iterator end();
ConstIterator end() const;
using AddressToHandleMap = std::multimap<u32, SymbolHandle<SymbolType>>;
using NameToHandleMap = std::multimap<std::string, SymbolHandle<SymbolType>>;
template <typename Iterator>
class Iterators {
public:
Iterators(Iterator b, Iterator e)
: m_begin(b), m_end(e) {}
Iterator begin() const { return m_begin; }
Iterator end() const { return m_end; }
protected:
Iterator m_begin;
Iterator m_end;
};
using AddressToHandleMapIterators = Iterators<typename AddressToHandleMap::const_iterator>;
using NameToHandleMapIterators = Iterators<typename NameToHandleMap::const_iterator>;
// Lookup symbols by their address.
AddressToHandleMapIterators handles_from_starting_address(Address address) const;
AddressToHandleMapIterators handles_from_address_range(AddressRange range) const;
SymbolHandle<SymbolType> first_handle_from_starting_address(Address address) const;
SymbolHandle<SymbolType> first_handle_after_address(Address address) const;
// Lookup symbols by their name.
NameToHandleMapIterators handles_from_name(const std::string& name) const;
SymbolHandle<SymbolType> first_handle_from_name(const std::string& name) const;
// Find a symbol with an address range that contains the provided address.
// For example, to find which function an instruction belongs to.
SymbolType* symbol_overlapping_address(Address address);
const SymbolType* symbol_overlapping_address(Address address) const;
// Convert handles to underlying array indices.
s32 index_from_handle(SymbolHandle<SymbolType> handle) const;
// Index into the underlying array.
SymbolType& symbol_from_index(s32 index);
const SymbolType& symbol_from_index(s32 index) const;
// Determine if any symbols are being stored.
bool empty() const;
// Retrieve the number of symbols stored.
s32 size() const;
// Create a new symbol. If it's a SymbolSource symbol, source can be left
// empty, otherwise it has to be valid.
Result<SymbolType*> create_symbol(
std::string name, Address address, SymbolSourceHandle source, const Module* module_symbol = nullptr);
// Create a new symbol. Similar to above, but for symbols without addresses.
Result<SymbolType*> create_symbol(
std::string name, SymbolSourceHandle source, const Module* module_symbol = nullptr);
// Create a new symbol. Similar to above, but unless DONT_DEMANGLE_NAMES is
// set, the name of the symbol will be demangled.
Result<SymbolType*> create_symbol(
std::string name,
SymbolSourceHandle source,
const Module* module_symbol,
Address address,
u32 importer_flags,
DemanglerFunctions demangler);
// Update the address of a symbol without changing its handle.
bool move_symbol(SymbolHandle<SymbolType> handle, Address new_address);
// Update the name of a symbol without changing its handle.
bool rename_symbol(SymbolHandle<SymbolType> handle, std::string new_name);
// Move all the symbols from the passed list into this list.
void merge_from(SymbolList<SymbolType>& list);
// Mark a symbol for destruction. If the correct symbol database pointer is
// passed, all descendants will also be marked. For example, marking a
// function will also mark its parameters and local variables.
bool mark_symbol_for_destruction(SymbolHandle<SymbolType> handle, SymbolDatabase* database);
// Mark all the symbols from a given symbol source for destruction. For
// example you can use this to free a symbol table without destroying
// user-defined symbols. The behaviour for marking descendants is the same
// as destroy_symbol.
void mark_symbols_from_source_for_destruction(SymbolSourceHandle source, SymbolDatabase* database);
// Mark all the symbols from a given module for destruction. The behaviour
// for marking descendants is the same as destroy_symbol.
void mark_symbols_from_module_for_destruction(ModuleHandle module_handle, SymbolDatabase* database);
// Destroy all symbols that have previously been marked for destruction.
// This invalidates all pointers to symbols in this list.
void destroy_marked_symbols();
// Destroy all symbols, but don't reset m_next_handle so we don't have to
// worry about dangling handles.
void clear();
protected:
// Do a binary search for a handle, and return either its index, or the
// index where it could be inserted.
size_t binary_search(SymbolHandle<SymbolType> handle) const;
// Keep the address map in sync with the symbol list.
void link_address_map(SymbolType& symbol);
void unlink_address_map(SymbolType& symbol);
// Keep the name map in sync with the symbol list.
void link_name_map(SymbolType& symbol);
void unlink_name_map(SymbolType& symbol);
std::vector<SymbolType> m_symbols;
AddressToHandleMap m_address_to_handle;
NameToHandleMap m_name_to_handle;
// We share this between symbol lists of the same type so that we can merge
// them without having to rewrite all the handles.
static std::atomic<u32> m_next_handle;
};
// Base class for all the symbols.
class Symbol {
template <typename SymbolType>
friend class SymbolList;
public:
const std::string& name() const { return m_name; }
u32 raw_handle() const { return m_handle; }
SymbolSourceHandle source() const { return m_source; }
ModuleHandle module_handle() const { return m_module; }
Address address() const { return m_address; }
u32 size() const { return m_size; }
void set_size(u32 size) { m_size = size; }
AddressRange address_range() const { return AddressRange(m_address, m_address.get_or_zero() + m_size); }
ast::Node* type() { return m_type.get(); }
const ast::Node* type() const { return m_type.get(); }
void set_type(std::unique_ptr<ast::Node> type);
u32 generation() const { return m_generation; }
// This MUST be called after any AST nodes have been created/deleted/moved.
// For the set_type function this is done for you.
void invalidate_node_handles() { m_generation++; }
// Mark a single symbol for destruction, not including its descendants.
void mark_for_destruction() { m_marked_for_destruction = true; }
bool is_marked_for_destruction() { return m_marked_for_destruction; }
protected:
void on_create() {}
void on_destroy(SymbolDatabase* database) {}
u32 m_handle = (u32) -1;
SymbolSourceHandle m_source;
Address m_address;
u32 m_size = 0;
std::string m_name;
std::unique_ptr<ast::Node> m_type;
u32 m_generation : 31 = 0;
u32 m_marked_for_destruction : 1 = false;
ModuleHandle m_module;
};
// Variable storage types. This is different to whether the variable is a
// global, local or parameter. For example local variables can have global
// storage (static locals).
enum GlobalStorageLocation {
NIL,
DATA,
BSS,
ABS,
SDATA,
SBSS,
RDATA,
COMMON,
SCOMMON,
SUNDEFINED
};
const char* global_storage_location_to_string(GlobalStorageLocation location);
struct GlobalStorage {
GlobalStorageLocation location = GlobalStorageLocation::NIL;
GlobalStorage() {}
friend auto operator<=>(const GlobalStorage& lhs, const GlobalStorage& rhs) = default;
};
struct RegisterStorage {
s32 dbx_register_number = -1;
bool is_by_reference;
RegisterStorage() {}
friend auto operator<=>(const RegisterStorage& lhs, const RegisterStorage& rhs) = default;
};
struct StackStorage {
s32 stack_pointer_offset = -1;
StackStorage() {}
friend auto operator<=>(const StackStorage& lhs, const StackStorage& rhs) = default;
};
// The hashing algorithm for functions. If you change this algorithm make sure
// to bump the version number for the JSON format so we can know if a hash was
// generated using the new algorithm or not.
class FunctionHash {
public:
void update(u32 instruction)
{
// Separate out the opcode so that the hash remains the same regardless
// of if relocations are applied or not.
u32 opcode = instruction >> 26;
m_hash = m_hash * 31 + opcode;
}
u32 get() const
{
return m_hash;
}
protected:
u32 m_hash = 0;
};
// All the different types of symbol objects.
// A C/C++ data type.
class DataType : public Symbol {
friend SourceFile;
public:
static constexpr const SymbolDescriptor DESCRIPTOR = DATA_TYPE;
static constexpr const char* NAME = "Data Type";
static constexpr const u32 FLAGS = WITH_NAME_MAP;
DataTypeHandle handle() const { return m_handle; }
std::vector<SourceFileHandle> files; // List of files for which a given top-level type is present.
const char* compare_fail_reason = nullptr;
bool not_defined_in_any_translation_unit : 1 = false;
bool only_defined_in_single_translation_unit : 1 = false;
};
// A function. The type stored is the return type.
class Function : public Symbol {
friend SourceFile;
friend SymbolList<Function>;
public:
static constexpr const SymbolDescriptor DESCRIPTOR = FUNCTION;
static constexpr const char* NAME = "Function";
static constexpr const u32 FLAGS = WITH_ADDRESS_MAP | WITH_NAME_MAP | NAME_NEEDS_DEMANGLING;
FunctionHandle handle() const { return m_handle; }
SourceFileHandle source_file() const { return m_source_file; }
const std::optional<std::vector<ParameterVariableHandle>>& parameter_variables() const;
void set_parameter_variables(std::optional<std::vector<ParameterVariableHandle>> parameter_variables, SymbolDatabase& database);
const std::optional<std::vector<LocalVariableHandle>>& local_variables() const;
void set_local_variables(std::optional<std::vector<LocalVariableHandle>> local_variables, SymbolDatabase& database);
const std::string& mangled_name() const;
void set_mangled_name(std::string mangled);
// A hash of all the opcodes in the function, read from file.
u32 original_hash() const;
void set_original_hash(u32 hash);
// A hash of all the opcodes in the function, read from memory.
u32 current_hash() const;
void set_current_hash(FunctionHash hash);
struct LineNumberPair {
Address address;
s32 line_number;
};
struct SubSourceFile {
Address address;
std::string relative_path;
};
std::string relative_path;
StorageClass storage_class;
s32 stack_frame_size = -1;
std::vector<LineNumberPair> line_numbers;
std::vector<SubSourceFile> sub_source_files;
bool is_member_function_ish = false; // Filled in by fill_in_pointers_to_member_function_definitions.
bool is_no_return = false;
protected:
void on_destroy(SymbolDatabase* database);
SourceFileHandle m_source_file;
std::optional<std::vector<ParameterVariableHandle>> m_parameter_variables;
std::optional<std::vector<LocalVariableHandle>> m_local_variables;
std::string m_mangled_name;
u32 m_original_hash = 0;
u32 m_current_hash = 0;
};
// A global variable.
class GlobalVariable : public Symbol {
friend SourceFile;
public:
static constexpr const SymbolDescriptor DESCRIPTOR = GLOBAL_VARIABLE;
static constexpr const char* NAME = "Global Variable";
static constexpr u32 FLAGS = WITH_ADDRESS_MAP | WITH_NAME_MAP | NAME_NEEDS_DEMANGLING;
GlobalVariableHandle handle() const { return m_handle; }
SourceFileHandle source_file() const { return m_source_file; };
const std::string& mangled_name() const;
void set_mangled_name(std::string mangled);
GlobalStorage storage;
StorageClass storage_class;
protected:
SourceFileHandle m_source_file;
std::string m_mangled_name;
};
// A label. This could be a label defined in assembly, C/C++, or just a symbol
// that we can't automatically determine the type of (e.g. SNDLL symbols).
class Label : public Symbol {
public:
static constexpr const SymbolDescriptor DESCRIPTOR = LABEL;
static constexpr const char* NAME = "Label";
static constexpr u32 FLAGS = WITH_ADDRESS_MAP | WITH_NAME_MAP;
LabelHandle handle() const { return m_handle; }
// Indicates that this label should not be used as a function name.
bool is_junk = false;
};
// A local variable. This includes static local variables which have global
// storage.
class LocalVariable : public Symbol {
friend Function;
public:
static constexpr const SymbolDescriptor DESCRIPTOR = LOCAL_VARIABLE;
static constexpr const char* NAME = "Local Variable";
static constexpr u32 FLAGS = WITH_ADDRESS_MAP;
LocalVariableHandle handle() const { return m_handle; }
FunctionHandle function() const { return m_function; };
std::variant<GlobalStorage, RegisterStorage, StackStorage> storage;
AddressRange live_range;
protected:
FunctionHandle m_function;
};
// A program module e.g. an ELF file or an SNDLL file. Every symbol has a module
// field indicating what module the symbol belongs to. This can be used to
// delete all the symbols associated with a given module. Additionally, when a
// valid module pointer is passed to SymbolList<>::create_symbol, the address of
// the symbol will be added to the address of the new symbol.
class Module : public Symbol {
friend SymbolList<Module>;
public:
static constexpr const SymbolDescriptor DESCRIPTOR = MODULE;
static constexpr const char* NAME = "Module";
static constexpr u32 FLAGS = WITH_NAME_MAP;
ModuleHandle handle() const { return m_handle; }
// These are used for IRX modules.
bool is_irx = false;
s32 version_major = -1;
s32 version_minor = -1;
protected:
void on_create();
};
// A parameter variable.
class ParameterVariable : public Symbol {
friend Function;
public:
static constexpr const SymbolDescriptor DESCRIPTOR = PARAMETER_VARIABLE;
static constexpr const char* NAME = "Parameter Variable";
static constexpr u32 FLAGS = NO_SYMBOL_FLAGS;
ParameterVariableHandle handle() const { return m_handle; }
FunctionHandle function() const { return m_function; };
std::variant<RegisterStorage, StackStorage> storage;
protected:
FunctionHandle m_function;
};
// An ELF section. These are created from the ELF section headers.
class Section : public Symbol {
public:
static constexpr const SymbolDescriptor DESCRIPTOR = SECTION;
static constexpr const char* NAME = "Section";
static constexpr u32 FLAGS = WITH_ADDRESS_MAP | WITH_NAME_MAP;
SectionHandle handle() const { return m_handle; }
// Check if the section name is ".text".
bool contains_code() const;
// Check for known data section names.
bool contains_data() const;
};
// A source file (.c or .cpp file). One of these will be created for every
// translation unit in the program (but only if debugging symbols are present).
class SourceFile : public Symbol {
friend SymbolList<SourceFile>;
public:
static constexpr const SymbolDescriptor DESCRIPTOR = SOURCE_FILE;
static constexpr const char* NAME = "Source File";
static constexpr u32 FLAGS = WITH_ADDRESS_MAP | WITH_NAME_MAP;
SourceFileHandle handle() const { return m_handle; }
const std::string& full_path() const { return name(); }
const std::vector<FunctionHandle>& functions() const;
void set_functions(std::vector<FunctionHandle> functions, SymbolDatabase& database);
const std::vector<GlobalVariableHandle>& global_variables() const;
void set_global_variables(std::vector<GlobalVariableHandle> global_variables, SymbolDatabase& database);
// Check whether at least half of the functions associated with the source
// file match their original hash (meaning they haven't been overwritten).
bool functions_match() const;
void check_functions_match(const SymbolDatabase& database);
std::string working_dir;
std::string command_line_path;
std::map<StabsTypeNumber, DataTypeHandle> stabs_type_number_to_handle;
std::set<std::string> toolchain_version_info;
protected:
void on_destroy(SymbolDatabase* database);
std::vector<FunctionHandle> m_functions;
std::vector<GlobalVariableHandle> m_global_variables;
bool m_functions_match = true;
};
// A symbol source. Every symbol has a symbol source field indicating how the
// symbol was created. For example, the symbol table importers will each create
// one of these (if it doesn't already exist).
class SymbolSource : public Symbol {
friend SymbolList<SymbolSource>;
public:
static constexpr const SymbolDescriptor DESCRIPTOR = SYMBOL_SOURCE;
static constexpr const char* NAME = "Symbol Source";
static constexpr u32 FLAGS = WITH_NAME_MAP;
SymbolSourceHandle handle() const { return m_handle; }
protected:
void on_create();
};
// Bundles together all the information needed to identify if a symbol came from
// a specific symbol table import operation. For example, this is used to make
// sure that we don't reference symbols from another symbol table during the
// import process.
struct SymbolGroup {
SymbolSourceHandle source;
Module* module_symbol = nullptr;
bool is_in_group(const Symbol& symbol) const;
};
// The symbol database itself. This owns all the symbols.
class SymbolDatabase {
public:
SymbolList<DataType> data_types;
SymbolList<Function> functions;
SymbolList<GlobalVariable> global_variables;
SymbolList<Label> labels;
SymbolList<LocalVariable> local_variables;
SymbolList<Module> modules;
SymbolList<ParameterVariable> parameter_variables;
SymbolList<Section> sections;
SymbolList<SourceFile> source_files;
SymbolList<SymbolSource> symbol_sources;
// Sum up the symbol counts for each symbol list.
s32 symbol_count() const;
// Find a symbol of any of the specified types given an address. Symbols of
// the types specified higher up in the CCC_FOR_EACH_SYMBOL_TYPE_DO_X macro
// are checked for first.
const Symbol* symbol_starting_at_address(
Address address, u32 descriptors = ALL_SYMBOL_TYPES, SymbolDescriptor* descriptor_out = nullptr) const;
const Symbol* symbol_after_address(
Address address, u32 descriptors = ALL_SYMBOL_TYPES, SymbolDescriptor* descriptor_out = nullptr) const;
const Symbol* symbol_overlapping_address(
Address address, u32 descriptors = ALL_SYMBOL_TYPES, SymbolDescriptor* descriptor_out = nullptr) const;
// Find a symbol of any of the specified types given its name. Symbols of
// the types specified higher up in the CCC_FOR_EACH_SYMBOL_TYPE_DO_X macro
// are checked for first.
const Symbol* symbol_with_name(
const std::string& name, u32 descriptors = ALL_SYMBOL_TYPES, SymbolDescriptor* descriptor_out = nullptr) const;
// Finds a symbol source object with the given name or creates one if it
// doesn't already exist.
Result<SymbolSourceHandle> get_symbol_source(const std::string& name);
// Deduplicate matching data types with the same name. May replace the
// existing data type with the new one if the new one is better.
Result<DataType*> create_data_type_if_unique(
std::unique_ptr<ast::Node> node,
StabsTypeNumber number,
const char* name,
SourceFile& source_file,
const SymbolGroup& group);
// Move all the symbols in the passed database into this database.
void merge_from(SymbolDatabase& database);
// Destroy all the symbols from a given symbol source. For example you can
// use this to free a symbol table without destroying user-defined symbols.
void destroy_symbols_from_source(SymbolSourceHandle source, bool destroy_descendants);
// Destroy all the symbols from a given module.
void destroy_symbols_from_module(ModuleHandle module_handle, bool destroy_descendants);
// Destroy all the symbols that have previously been marked for destruction.
// This invalidates all pointers to symbols in this database.
void destroy_marked_symbols();
// Destroy all the symbols in the symbol database.
void clear();
template <typename Callback>
void for_each_symbol(Callback callback) {
// Use indices here to avoid iterator invalidation.
#define CCC_X(SymbolType, symbol_list) \
for(s32 i = 0; i < symbol_list.size(); i++) { \
callback(symbol_list.symbol_from_index(i)); \
}
CCC_FOR_EACH_SYMBOL_TYPE_DO_X
#undef CCC_X
}
};
// A handle to a symbol of any type.
class MultiSymbolHandle {
public:
// Create an empty multi symbol handle.
MultiSymbolHandle();
// Create a multi symbol handle of the specified type.
template <typename SymbolType>
MultiSymbolHandle(const SymbolType& symbol);
MultiSymbolHandle(SymbolDescriptor descriptor, u32 handle);
bool valid() const;
SymbolDescriptor descriptor() const;
u32 handle() const;
Symbol* lookup_symbol(SymbolDatabase& database);
const Symbol* lookup_symbol(const SymbolDatabase& database) const;
bool is_flag_set(SymbolFlag flag) const;
bool move_symbol(Address new_address, SymbolDatabase& database) const;
bool rename_symbol(std::string new_name, SymbolDatabase& database) const;
bool destroy_symbol(SymbolDatabase& database, bool destroy_descendants) const;
friend auto operator<=>(const MultiSymbolHandle& lhs, const MultiSymbolHandle& rhs) = default;
protected:
SymbolDescriptor m_descriptor = DATA_TYPE;
u32 m_handle = (u32) -1;
};
// A handle to an AST node.
class NodeHandle {
friend SymbolDatabase;
public:
// Create an empty node handle.
NodeHandle();
// Create a node handle that will always allow accesses to its node. You
// should only use this if you know the lifetime of the handle is a subset
// of the lifetime of the node.
NodeHandle(const ast::Node* node);
// Create a node handle pointing to an AST node from a given symbol that
// will prevent accesses to the node if the symbol is deleted.
template <typename SymbolType>
NodeHandle(const SymbolType& symbol, const ast::Node* node);
NodeHandle(SymbolDescriptor descriptor, const Symbol& symbol, const ast::Node* node);
bool valid() const;
const MultiSymbolHandle& symbol() const;
const ast::Node* lookup_node(const SymbolDatabase& database) const;
NodeHandle handle_for_child(const ast::Node* child_node) const;
friend auto operator<=>(const NodeHandle& lhs, const NodeHandle& rhs) = default;
protected:
MultiSymbolHandle m_symbol;
const ast::Node* m_node = nullptr;
u32 m_generation = 0;
};
}

114
3rdparty/ccc/src/ccc/symbol_file.cpp vendored Normal file
View File

@ -0,0 +1,114 @@
// This file is part of the Chaos Compiler Collection.
// SPDX-License-Identifier: MIT
#include "symbol_file.h"
namespace ccc {
Result<std::unique_ptr<SymbolFile>> parse_symbol_file(std::vector<u8> image, std::string file_name)
{
const std::optional<u32> magic = copy_unaligned<u32>(image, 0);
CCC_CHECK(magic.has_value(), "File too small.");
std::unique_ptr<SymbolFile> symbol_file;
switch(*magic) {
case CCC_FOURCC("\x7f""ELF"): {
Result<ElfFile> elf = ElfFile::parse(std::move(image));
CCC_RETURN_IF_ERROR(elf);
symbol_file = std::make_unique<ElfSymbolFile>(std::move(*elf), std::move(file_name));
break;
}
case CCC_FOURCC("SNR1"):
case CCC_FOURCC("SNR2"): {
Result<SNDLLFile> sndll = parse_sndll_file(image, Address(), SNDLLType::DYNAMIC_LIBRARY);
CCC_RETURN_IF_ERROR(sndll);
symbol_file = std::make_unique<SNDLLSymbolFile>(std::make_shared<SNDLLFile>(std::move(*sndll)));
break;
}
default: {
return CCC_FAILURE("Unknown file type.");
}
}
return symbol_file;
}
ElfSymbolFile::ElfSymbolFile(ElfFile elf, std::string elf_name)
: m_elf(std::move(elf)), m_name(std::move(elf_name)) {}
std::string ElfSymbolFile::name() const
{
return m_name;
}
Result<std::vector<std::unique_ptr<SymbolTable>>> ElfSymbolFile::get_all_symbol_tables() const
{
std::vector<std::unique_ptr<SymbolTable>> symbol_tables;
symbol_tables.emplace_back(std::make_unique<ElfSectionHeadersSymbolTable>(m_elf));
for(size_t i = 0; i < SYMBOL_TABLE_FORMATS.size(); i++) {
const SymbolTableFormatInfo& info = SYMBOL_TABLE_FORMATS[i];
const ElfSection* section = m_elf.lookup_section(info.section_name);
if(section) {
Result<std::unique_ptr<SymbolTable>> symbol_table = create_elf_symbol_table(*section, m_elf, info.format);
CCC_RETURN_IF_ERROR(symbol_table);
if(*symbol_table) {
symbol_tables.emplace_back(std::move(*symbol_table));
}
}
}
return symbol_tables;
}
Result<std::vector<std::unique_ptr<SymbolTable>>> ElfSymbolFile::get_symbol_tables_from_sections(
const std::vector<SymbolTableLocation>& sections) const
{
std::vector<std::unique_ptr<SymbolTable>> symbol_tables;
for(const SymbolTableLocation& location : sections) {
const ElfSection* section = m_elf.lookup_section(location.section_name.c_str());
CCC_CHECK(section, "No '%s' section.", location.section_name.c_str());
Result<std::unique_ptr<SymbolTable>> symbol_table = create_elf_symbol_table(*section, m_elf, location.format);
CCC_RETURN_IF_ERROR(symbol_table);
if(*symbol_table) {
symbol_tables.emplace_back(std::move(*symbol_table));
}
}
return symbol_tables;
}
const ElfFile& ElfSymbolFile::elf() const
{
return m_elf;
}
SNDLLSymbolFile::SNDLLSymbolFile(std::shared_ptr<SNDLLFile> sndll)
: m_sndll(std::move(sndll)) {}
std::string SNDLLSymbolFile::name() const
{
return m_sndll->elf_path;
}
Result<std::vector<std::unique_ptr<SymbolTable>>> SNDLLSymbolFile::get_all_symbol_tables() const
{
std::vector<std::unique_ptr<SymbolTable>> symbol_tables;
symbol_tables.emplace_back(std::make_unique<SNDLLSymbolTable>(m_sndll));
return symbol_tables;
}
Result<std::vector<std::unique_ptr<SymbolTable>>> SNDLLSymbolFile::get_symbol_tables_from_sections(
const std::vector<SymbolTableLocation>& sections) const
{
return CCC_FAILURE("An SNDLL file is not composed of sections.");
}
}

62
3rdparty/ccc/src/ccc/symbol_file.h vendored Normal file
View File

@ -0,0 +1,62 @@
// This file is part of the Chaos Compiler Collection.
// SPDX-License-Identifier: MIT
#pragma once
#include "elf.h"
#include "sndll.h"
#include "symbol_table.h"
namespace ccc {
struct SymbolTableLocation {
std::string section_name;
SymbolTableFormat format;
};
class SymbolFile {
public:
virtual ~SymbolFile() {}
virtual std::string name() const = 0;
virtual Result<std::vector<std::unique_ptr<SymbolTable>>> get_all_symbol_tables() const = 0;
virtual Result<std::vector<std::unique_ptr<SymbolTable>>> get_symbol_tables_from_sections(
const std::vector<SymbolTableLocation>& sections) const = 0;
};
// Determine the type of the input file and parse it.
Result<std::unique_ptr<SymbolFile>> parse_symbol_file(std::vector<u8> image, std::string file_name);
class ElfSymbolFile : public SymbolFile {
public:
ElfSymbolFile(ElfFile elf, std::string elf_name);
std::string name() const override;
Result<std::vector<std::unique_ptr<SymbolTable>>> get_all_symbol_tables() const override;
Result<std::vector<std::unique_ptr<SymbolTable>>> get_symbol_tables_from_sections(
const std::vector<SymbolTableLocation>& sections) const override;
const ElfFile& elf() const;
protected:
ElfFile m_elf;
std::string m_name;
};
class SNDLLSymbolFile : public SymbolFile {
public:
SNDLLSymbolFile(std::shared_ptr<SNDLLFile> sndll);
std::string name() const override;
Result<std::vector<std::unique_ptr<SymbolTable>>> get_all_symbol_tables() const override;
Result<std::vector<std::unique_ptr<SymbolTable>>> get_symbol_tables_from_sections(
const std::vector<SymbolTableLocation>& sections) const override;
protected:
std::shared_ptr<SNDLLFile> m_sndll;
};
}

285
3rdparty/ccc/src/ccc/symbol_table.cpp vendored Normal file
View File

@ -0,0 +1,285 @@
// This file is part of the Chaos Compiler Collection.
// SPDX-License-Identifier: MIT
#include "symbol_table.h"
#include "elf.h"
#include "elf_symtab.h"
#include "mdebug_importer.h"
#include "mdebug_section.h"
#include "sndll.h"
namespace ccc {
const std::vector<SymbolTableFormatInfo> SYMBOL_TABLE_FORMATS = {
{MDEBUG, "mdebug", ".mdebug"}, // The infamous Third Eye symbol table.
{SYMTAB, "symtab", ".symtab"}, // The standard ELF symbol table.
{SNDLL, "sndll", ".sndata"} // The SNDLL symbol table.
};
const SymbolTableFormatInfo* symbol_table_format_from_enum(SymbolTableFormat format)
{
for(size_t i = 0; i < SYMBOL_TABLE_FORMATS.size(); i++) {
if(SYMBOL_TABLE_FORMATS[i].format == format) {
return &SYMBOL_TABLE_FORMATS[i];
}
}
return nullptr;
}
const SymbolTableFormatInfo* symbol_table_format_from_name(const char* format_name)
{
for(size_t i = 0; i < SYMBOL_TABLE_FORMATS.size(); i++) {
if(strcmp(SYMBOL_TABLE_FORMATS[i].format_name, format_name) == 0) {
return &SYMBOL_TABLE_FORMATS[i];
}
}
return nullptr;
}
const SymbolTableFormatInfo* symbol_table_format_from_section(const char* section_name)
{
for(size_t i = 0; i < SYMBOL_TABLE_FORMATS.size(); i++) {
if(strcmp(SYMBOL_TABLE_FORMATS[i].section_name, section_name) == 0) {
return &SYMBOL_TABLE_FORMATS[i];
}
}
return nullptr;
}
// *****************************************************************************
Result<std::unique_ptr<SymbolTable>> create_elf_symbol_table(
const ElfSection& section, const ElfFile& elf, SymbolTableFormat format)
{
std::unique_ptr<SymbolTable> symbol_table;
switch(format) {
case MDEBUG: {
symbol_table = std::make_unique<MdebugSymbolTable>(elf.image, (s32) section.header.offset);
break;
}
case SYMTAB: {
CCC_CHECK(section.header.offset + section.header.size <= elf.image.size(),
"Section '%s' out of range.", section.name.c_str());
std::span<const u8> data = std::span(elf.image).subspan(section.header.offset, section.header.size);
CCC_CHECK(section.header.link != 0, "Section '%s' has no linked string table.", section.name.c_str());
CCC_CHECK(section.header.link < elf.sections.size(),
"Section '%s' has out of range link field.", section.name.c_str());
const ElfSection& linked_section = elf.sections[section.header.link];
CCC_CHECK(linked_section.header.offset + linked_section.header.size <= elf.image.size(),
"Linked section '%s' out of range.", linked_section.name.c_str());
std::span<const u8> linked_data = std::span(elf.image).subspan(
linked_section.header.offset, linked_section.header.size);
symbol_table = std::make_unique<SymtabSymbolTable>(data, linked_data);
break;
}
case SNDLL: {
CCC_CHECK(section.header.offset + section.header.size <= elf.image.size(),
"Section '%s' out of range.", section.name.c_str());
std::span<const u8> data = std::span(elf.image).subspan(section.header.offset, section.header.size);
if(data.size() >= 4 && data[0] != '\0') {
Result<SNDLLFile> file = parse_sndll_file(data, Address::non_zero(section.header.addr), SNDLLType::SNDATA_SECTION);
CCC_RETURN_IF_ERROR(file);
symbol_table = std::make_unique<SNDLLSymbolTable>(std::make_shared<SNDLLFile>(std::move(*file)));
} else {
CCC_WARN("Invalid SNDLL section.");
}
break;
}
}
return symbol_table;
}
Result<ModuleHandle> import_symbol_tables(
SymbolDatabase& database,
const std::vector<std::unique_ptr<SymbolTable>>& symbol_tables,
std::string module_name,
Address base_address,
u32 importer_flags,
DemanglerFunctions demangler,
const std::atomic_bool* interrupt)
{
Result<SymbolSourceHandle> module_source = database.get_symbol_source("Symbol Table Importer");
CCC_RETURN_IF_ERROR(module_source);
Result<Module*> module_symbol = database.modules.create_symbol(
std::move(module_name), base_address, *module_source, nullptr);
CCC_RETURN_IF_ERROR(module_symbol);
ModuleHandle module_handle = (*module_symbol)->handle();
for(const std::unique_ptr<SymbolTable>& symbol_table : symbol_tables) {
// Find a symbol source object with the right name, or create one if one
// doesn't already exist.
Result<SymbolSourceHandle> source = database.get_symbol_source(symbol_table->name());
if(!source.success()) {
database.destroy_symbols_from_module(module_handle, false);
return source;
}
// Import the symbol table.
SymbolGroup group;
group.source = *source;
group.module_symbol = database.modules.symbol_from_handle(module_handle);
Result<void> result = symbol_table->import(
database, group, importer_flags, demangler, interrupt);
if(!result.success()) {
database.destroy_symbols_from_module(module_handle, false);
return result;
}
}
return module_handle;
}
// *****************************************************************************
MdebugSymbolTable::MdebugSymbolTable(std::span<const u8> image, s32 section_offset)
: m_image(image), m_section_offset(section_offset) {}
const char* MdebugSymbolTable::name() const
{
return "MIPS Debug Symbol Table";
}
Result<void> MdebugSymbolTable::import(
SymbolDatabase& database,
const SymbolGroup& group,
u32 importer_flags,
DemanglerFunctions demangler,
const std::atomic_bool* interrupt) const
{
return mdebug::import_symbol_table(
database, m_image, m_section_offset, group, importer_flags, demangler, interrupt);
}
Result<void> MdebugSymbolTable::print_headers(FILE* out) const
{
mdebug::SymbolTableReader reader;
Result<void> reader_result = reader.init(m_image, m_section_offset);
CCC_RETURN_IF_ERROR(reader_result);
reader.print_header(out);
return Result<void>();
}
Result<void> MdebugSymbolTable::print_symbols(FILE* out, u32 flags) const
{
mdebug::SymbolTableReader reader;
Result<void> reader_result = reader.init(m_image, m_section_offset);
CCC_RETURN_IF_ERROR(reader_result);
Result<void> print_result = reader.print_symbols(
out, flags & PRINT_LOCALS, flags & PRINT_PROCEDURE_DESCRIPTORS, flags & PRINT_EXTERNALS);
CCC_RETURN_IF_ERROR(print_result);
return Result<void>();
}
// *****************************************************************************
SymtabSymbolTable::SymtabSymbolTable(std::span<const u8> symtab, std::span<const u8> strtab)
: m_symtab(symtab), m_strtab(strtab) {}
const char* SymtabSymbolTable::name() const
{
return "ELF Symbol Table";
}
Result<void> SymtabSymbolTable::import(
SymbolDatabase& database,
const SymbolGroup& group,
u32 importer_flags,
DemanglerFunctions demangler,
const std::atomic_bool* interrupt) const
{
return elf::import_symbols(database, group, m_symtab, m_strtab, importer_flags, demangler);
}
Result<void> SymtabSymbolTable::print_headers(FILE* out) const
{
return Result<void>();
}
Result<void> SymtabSymbolTable::print_symbols(FILE* out, u32 flags) const
{
Result<void> symbtab_result = elf::print_symbol_table(out, m_symtab, m_strtab);
CCC_RETURN_IF_ERROR(symbtab_result);
return Result<void>();
}
// *****************************************************************************
SNDLLSymbolTable::SNDLLSymbolTable(std::shared_ptr<SNDLLFile> sndll)
: m_sndll(std::move(sndll)) {}
const char* SNDLLSymbolTable::name() const
{
return "SNDLL Symbol Table";
}
Result<void> SNDLLSymbolTable::import(
SymbolDatabase& database,
const SymbolGroup& group,
u32 importer_flags,
DemanglerFunctions demangler,
const std::atomic_bool* interrupt) const
{
return import_sndll_symbols(database, *m_sndll, group, importer_flags, demangler);
}
Result<void> SNDLLSymbolTable::print_headers(FILE* out) const
{
return Result<void>();
}
Result<void> SNDLLSymbolTable::print_symbols(FILE* out, u32 flags) const
{
print_sndll_symbols(out, *m_sndll);
return Result<void>();
}
// *****************************************************************************
ElfSectionHeadersSymbolTable::ElfSectionHeadersSymbolTable(const ElfFile& elf)
: m_elf(elf) {}
const char* ElfSectionHeadersSymbolTable::name() const
{
return "ELF Section Headers";
}
Result<void> ElfSectionHeadersSymbolTable::import(
SymbolDatabase& database,
const SymbolGroup& group,
u32 importer_flags,
DemanglerFunctions demangler,
const std::atomic_bool* interrupt) const
{
return m_elf.create_section_symbols(database, group);
}
Result<void> ElfSectionHeadersSymbolTable::print_headers(FILE* out) const
{
return Result<void>();
}
Result<void> ElfSectionHeadersSymbolTable::print_symbols(FILE* out, u32 flags) const
{
return Result<void>();
}
}

164
3rdparty/ccc/src/ccc/symbol_table.h vendored Normal file
View File

@ -0,0 +1,164 @@
// This file is part of the Chaos Compiler Collection.
// SPDX-License-Identifier: MIT
#pragma once
#include <atomic>
#include "symbol_database.h"
namespace ccc {
// Determine which symbol tables are present in a given file.
enum SymbolTableFormat {
MDEBUG = 0, // The infamous Third Eye symbol table
SYMTAB = 1, // Standard ELF symbol table
SNDLL = 2 // SNDLL section
};
struct SymbolTableFormatInfo {
SymbolTableFormat format;
const char* format_name;
const char* section_name;
};
// All the supported symbol table formats, sorted from best to worst.
extern const std::vector<SymbolTableFormatInfo> SYMBOL_TABLE_FORMATS;
const SymbolTableFormatInfo* symbol_table_format_from_enum(SymbolTableFormat format);
const SymbolTableFormatInfo* symbol_table_format_from_name(const char* format_name);
const SymbolTableFormatInfo* symbol_table_format_from_section(const char* section_name);
enum SymbolPrintFlags {
PRINT_LOCALS = 1 << 0,
PRINT_PROCEDURE_DESCRIPTORS = 1 << 1,
PRINT_EXTERNALS = 1 << 2
};
class SymbolTable {
public:
virtual ~SymbolTable() {}
virtual const char* name() const = 0;
// Imports this symbol table into the passed database.
virtual Result<void> import(
SymbolDatabase& database,
const SymbolGroup& group,
u32 importer_flags,
DemanglerFunctions demangler,
const std::atomic_bool* interrupt) const = 0;
// Print out all the field in the header structure if one exists.
virtual Result<void> print_headers(FILE* out) const = 0;
// Print out all the symbols in the symbol table. For .mdebug symbol tables
// the symbols are split between those that are local to a specific
// translation unit and those that are external, which is what the
// print_locals and print_externals parameters control.
virtual Result<void> print_symbols(FILE* out, u32 flags) const = 0;
};
struct ElfSection;
struct ElfFile;
// Create a symbol table from an ELF section. The return value may be null.
Result<std::unique_ptr<SymbolTable>> create_elf_symbol_table(
const ElfSection& section, const ElfFile& elf, SymbolTableFormat format);
// Utility function to call import_symbol_table on all the passed symbol tables
// and to generate a module handle.
Result<ModuleHandle> import_symbol_tables(
SymbolDatabase& database,
const std::vector<std::unique_ptr<SymbolTable>>& symbol_tables,
std::string module_name,
Address base_address,
u32 importer_flags,
DemanglerFunctions demangler,
const std::atomic_bool* interrupt);
class MdebugSymbolTable : public SymbolTable {
public:
MdebugSymbolTable(std::span<const u8> image, s32 section_offset);
const char* name() const override;
Result<void> import(
SymbolDatabase& database,
const SymbolGroup& group,
u32 importer_flags,
DemanglerFunctions demangler,
const std::atomic_bool* interrupt) const override;
Result<void> print_headers(FILE* out) const override;
Result<void> print_symbols(FILE* out, u32 flags) const override;
protected:
std::span<const u8> m_image;
s32 m_section_offset;
};
class SymtabSymbolTable : public SymbolTable {
public:
SymtabSymbolTable(std::span<const u8> symtab, std::span<const u8> strtab);
const char* name() const override;
Result<void> import(
SymbolDatabase& database,
const SymbolGroup& group,
u32 importer_flags,
DemanglerFunctions demangler,
const std::atomic_bool* interrupt) const override;
Result<void> print_headers(FILE* out) const override;
Result<void> print_symbols(FILE* out, u32 flags) const override;
protected:
std::span<const u8> m_symtab;
std::span<const u8> m_strtab;
};
struct SNDLLFile;
class SNDLLSymbolTable : public SymbolTable {
public:
SNDLLSymbolTable(std::shared_ptr<SNDLLFile> sndll);
const char* name() const override;
Result<void> import(
SymbolDatabase& database,
const SymbolGroup& group,
u32 importer_flags,
DemanglerFunctions demangler,
const std::atomic_bool* interrupt) const override;
Result<void> print_headers(FILE* out) const override;
Result<void> print_symbols(FILE* out, u32 flags) const override;
protected:
std::shared_ptr<SNDLLFile> m_sndll;
};
class ElfSectionHeadersSymbolTable : public SymbolTable {
public:
ElfSectionHeadersSymbolTable(const ElfFile& elf);
const char* name() const override;
Result<void> import(
SymbolDatabase& database,
const SymbolGroup& group,
u32 importer_flags,
DemanglerFunctions demangler,
const std::atomic_bool* interrupt) const override;
Result<void> print_headers(FILE* out) const override;
Result<void> print_symbols(FILE* out, u32 flags) const override;
protected:
const ElfFile& m_elf;
};
}

176
3rdparty/ccc/src/ccc/util.cpp vendored Normal file
View File

@ -0,0 +1,176 @@
// This file is part of the Chaos Compiler Collection.
// SPDX-License-Identifier: MIT
#include "util.h"
namespace ccc {
static CustomErrorCallback custom_error_callback = nullptr;
Error format_error(const char* source_file, int source_line, const char* format, ...)
{
va_list args;
va_start(args, format);
char message[4096];
if(vsnprintf(message, sizeof(message), format, args) < 0) {
strncpy(message, "Failed to generate error message.", sizeof(message));
}
Error error;
error.message = message;
error.source_file = source_file;
error.source_line = source_line;
va_end(args);
return error;
}
void report_error(const Error& error)
{
if(custom_error_callback) {
custom_error_callback(error, ERROR_LEVEL_ERROR);
} else {
fprintf(stderr, "[%s:%d] " CCC_ANSI_COLOUR_RED "error:" CCC_ANSI_COLOUR_OFF " %s\n",
error.source_file, error.source_line, error.message.c_str());
}
}
void report_warning(const Error& warning)
{
if(custom_error_callback) {
custom_error_callback(warning, ERROR_LEVEL_WARNING);
} else {
fprintf(stderr, "[%s:%d] " CCC_ANSI_COLOUR_MAGENTA "warning:" CCC_ANSI_COLOUR_OFF " %s\n",
warning.source_file, warning.source_line, warning.message.c_str());
}
}
void set_custom_error_callback(CustomErrorCallback callback)
{
custom_error_callback = callback;
}
std::optional<std::string_view> get_string(std::span<const u8> bytes, u64 offset)
{
for(u64 i = offset; i < bytes.size(); i++) {
if(bytes[i] == '\0') {
return std::string_view(
reinterpret_cast<const char*>(&bytes[offset]),
reinterpret_cast<const char*>(&bytes[i]));
}
}
return std::nullopt;
}
std::string merge_paths(const std::string& base, const std::string& path)
{
// Try to figure out if we're dealing with a Windows path of a UNIX path.
bool is_windows_path = false;
if(base.empty()) {
is_windows_path = guess_is_windows_path(path.c_str());
} else {
is_windows_path = guess_is_windows_path(base.c_str());
}
// Actually merge the paths. If path is the entire path, we don't need to
// append base onto the front, so check for that now.
bool is_absolute_unix = (path.size() >= 1) && (path[0] == '/' || path[0] == '\\');
bool is_absolute_windows = (path.size() >= 3) && path[1] == ':' && (path[2] == '/' || path[2] == '\\');
if(base.empty() || is_absolute_unix || is_absolute_windows) {
return normalise_path(path.c_str(), is_windows_path);
}
return normalise_path((base + "/" + path).c_str(), is_windows_path);
}
std::string normalise_path(const char* input, bool use_backslashes_as_path_separators)
{
bool is_absolute = false;
std::optional<char> drive_letter;
std::vector<std::string> parts;
// Parse the beginning of the path.
if(*input == '/' || *input == '\\') { // UNIX path, drive relative Windows path or UNC Windows path.
is_absolute = true;
} else if(isalpha(*input) && input[1] == ':' && (input[2] == '/' || input[2] == '\\')) { // Absolute Windows path.
is_absolute = true;
drive_letter = toupper(*input);
input += 2;
} else {
parts.emplace_back();
}
// Parse the rest of the path.
while(*input != 0) {
if(*input == '/' || *input == '\\') {
while(*input == '/' || *input == '\\') input++;
parts.emplace_back();
} else {
parts.back() += *(input++);
}
}
// Remove "." and ".." parts.
for(s32 i = 0; i < (s32) parts.size(); i++) {
if(parts[i] == ".") {
parts.erase(parts.begin() + i);
i--;
} else if(parts[i] == ".." && i > 0 && parts[i - 1] != "..") {
parts.erase(parts.begin() + i);
parts.erase(parts.begin() + i - 1);
i -= 2;
}
}
// Output the path in a normal form.
std::string output;
if(is_absolute) {
if(drive_letter.has_value()) {
output += *drive_letter;
output += ":";
}
output += use_backslashes_as_path_separators ? '\\' : '/';
}
for(size_t i = 0; i < parts.size(); i++) {
output += parts[i];
if(i != parts.size() - 1) {
output += use_backslashes_as_path_separators ? '\\' : '/';
}
}
return output;
}
bool guess_is_windows_path(const char* path)
{
for(const char* ptr = path; *ptr != 0; ptr++) {
if(*ptr == '\\') {
return true;
} else if(*ptr == '/') {
return false;
}
}
return false;
}
std::string extract_file_name(const std::string& path)
{
std::string::size_type forward_pos = path.find_last_of('/');
std::string::size_type backward_pos = path.find_last_of('\\');
std::string::size_type pos;
if(forward_pos == std::string::npos) {
pos = backward_pos;
} else if(backward_pos == std::string::npos) {
pos = forward_pos;
} else {
pos = std::max(forward_pos, backward_pos);
}
if(pos + 1 != path.size() && pos != std::string::npos) {
return path.substr(pos + 1);
} else {
return path;
}
}
}

341
3rdparty/ccc/src/ccc/util.h vendored Normal file
View File

@ -0,0 +1,341 @@
// This file is part of the Chaos Compiler Collection.
// SPDX-License-Identifier: MIT
#pragma once
#include <set>
#include <span>
#include <cstdio>
#include <vector>
#include <memory>
#include <string>
#include <cstdint>
#include <cstdarg>
#include <cstdlib>
#include <cstring>
#include <optional>
namespace ccc {
using u8 = unsigned char;
using u16 = uint16_t;
using u32 = uint32_t;
using u64 = uint64_t;
using s8 = signed char;
using s16 = int16_t;
using s32 = int32_t;
using s64 = int64_t;
#ifdef _WIN32
#define CCC_ANSI_COLOUR_OFF ""
#define CCC_ANSI_COLOUR_RED ""
#define CCC_ANSI_COLOUR_MAGENTA ""
#define CCC_ANSI_COLOUR_GRAY ""
#else
#define CCC_ANSI_COLOUR_OFF "\033[0m"
#define CCC_ANSI_COLOUR_RED "\033[31m"
#define CCC_ANSI_COLOUR_MAGENTA "\033[35m"
#define CCC_ANSI_COLOUR_GRAY "\033[90m"
#endif
struct Error {
std::string message;
const char* source_file;
s32 source_line;
};
enum ErrorLevel {
ERROR_LEVEL_ERROR,
ERROR_LEVEL_WARNING
};
typedef void (*CustomErrorCallback)(const Error& error, ErrorLevel level);
Error format_error(const char* source_file, int source_line, const char* format, ...);
void report_error(const Error& error);
void report_warning(const Error& warning);
void set_custom_error_callback(CustomErrorCallback callback);
#define CCC_FATAL(...) \
{ \
ccc::Error error = ccc::format_error(__FILE__, __LINE__, __VA_ARGS__); \
ccc::report_error(error); \
exit(1); \
}
#define CCC_CHECK_FATAL(condition, ...) \
if(!(condition)) { \
ccc::Error error = ccc::format_error(__FILE__, __LINE__, __VA_ARGS__); \
ccc::report_error(error); \
exit(1); \
}
#define CCC_ABORT_IF_FALSE(condition, ...) \
if(!(condition)) { \
ccc::Error error = ccc::format_error(__FILE__, __LINE__, __VA_ARGS__); \
ccc::report_error(error); \
abort(); \
}
#define CCC_ASSERT(condition) \
CCC_ABORT_IF_FALSE(condition, #condition)
// The main error handling construct in CCC. This class is used to bundle
// together a return value and a pointer to error information, so that errors
// can be propagated up the stack.
template <typename Value>
class [[nodiscard]] Result {
template <typename OtherValue>
friend class Result;
protected:
Value m_value;
std::unique_ptr<Error> m_error;
Result() {}
public:
Result(Value value) : m_value(std::move(value)), m_error(nullptr) {}
// Used to propagate errors up the call stack.
template <typename OtherValue>
Result(Result<OtherValue>&& rhs)
{
CCC_ASSERT(rhs.m_error != nullptr);
m_error = std::move(rhs.m_error);
}
static Result<Value> failure(Error error)
{
Result<Value> result;
result.m_error = std::make_unique<Error>(std::move(error));
return result;
}
bool success() const
{
return m_error == nullptr;
}
const Error& error() const
{
CCC_ASSERT(m_error != nullptr);
return *m_error;
}
Value& operator*()
{
CCC_ASSERT(m_error == nullptr);
return m_value;
}
const Value& operator*() const
{
CCC_ASSERT(m_error == nullptr);
return m_value;
}
Value* operator->()
{
CCC_ASSERT(m_error == nullptr);
return &m_value;
}
const Value* operator->() const
{
CCC_ASSERT(m_error == nullptr);
return &m_value;
}
};
template <>
class [[nodiscard]] Result<void> : public Result<int> {
public:
Result() : Result<int>(0) {}
// Used to propagate errors up the call stack.
template <typename OtherValue>
Result(Result<OtherValue>&& rhs)
{
CCC_ASSERT(rhs.m_error != nullptr);
m_error = std::move(rhs.m_error);
}
};
#define CCC_FAILURE(...) ccc::Result<int>::failure(ccc::format_error(__FILE__, __LINE__, __VA_ARGS__))
#define CCC_CHECK(condition, ...) \
if(!(condition)) { \
return CCC_FAILURE(__VA_ARGS__); \
}
#define CCC_EXPECT_CHAR(input, c, context) \
CCC_CHECK(*(input++) == c, \
"Expected '%c' in %s, got '%c' (%02hhx)", \
c, context, *(input - 1), *(input - 1))
#define CCC_RETURN_IF_ERROR(result) \
if(!(result).success()) { \
return (result); \
}
#define CCC_EXIT_IF_ERROR(result) \
if(!(result).success()) { \
ccc::report_error((result).error()); \
exit(1); \
}
#define CCC_GTEST_FAIL_IF_ERROR(result) \
if(!(result).success()) { \
FAIL() << (result).error().message; \
}
template <typename... Args>
void warn_impl(const char* source_file, int source_line, const char* format, Args... args)
{
Error warning = format_error(source_file, source_line, format, args...);
report_warning(warning);
}
#define CCC_WARN(...) \
ccc::warn_impl(__FILE__, __LINE__, __VA_ARGS__)
#ifdef _MSC_VER
#define CCC_PACKED_STRUCT(name, ...) \
__pragma(pack(push, 1)) struct name { __VA_ARGS__ } __pragma(pack(pop));
#else
#define CCC_PACKED_STRUCT(name, ...) \
struct __attribute__((__packed__)) name { __VA_ARGS__ };
#endif
template <typename T>
const T* get_aligned(std::span<const u8> bytes, u64 offset)
{
if(offset > bytes.size() || bytes.size() - offset < sizeof(T) || offset % alignof(T) != 0) {
return nullptr;
}
return reinterpret_cast<const T*>(&bytes[offset]);
}
template <typename T>
const T* get_unaligned(std::span<const u8> bytes, u64 offset)
{
if(offset > bytes.size() || bytes.size() - offset < sizeof(T)) {
return nullptr;
}
return reinterpret_cast<const T*>(&bytes[offset]);
}
template <typename T>
std::optional<T> copy_unaligned(std::span<const u8> bytes, u64 offset)
{
if(offset > bytes.size() || bytes.size() - offset < sizeof(T)) {
return std::nullopt;
}
T value;
memcpy(&value, &bytes[offset], sizeof(T));
return value;
}
std::optional<std::string_view> get_string(std::span<const u8> bytes, u64 offset);
#define CCC_BEGIN_END(x) (x).begin(), (x).end()
#define CCC_ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#define CCC_FOURCC(string) ((string)[0] | (string)[1] << 8 | (string)[2] << 16 | (string)[3] << 24)
struct Address {
u32 value = (u32) -1;
Address() {}
Address(u32 v) : value(v) {}
bool valid() const
{
return value != (u32) -1;
}
u32 get_or_zero() const
{
if(valid()) {
return value;
} else {
return 0;
}
}
Address add_base_address(Address base_address) const
{
if(valid()) {
return base_address.get_or_zero() + value;
} else {
return Address();
}
}
static Address non_zero(u32 address)
{
Address result;
if(address != 0) {
result = address;
}
return result;
}
friend auto operator<=>(const Address& lhs, const Address& rhs) = default;
};
struct AddressRange {
Address low;
Address high;
AddressRange() {}
AddressRange(Address address) : low(address), high(address) {}
AddressRange(Address l, Address h) : low(l), high(h) {}
friend auto operator<=>(const AddressRange& lhs, const AddressRange& rhs) = default;
};
// These functions are to be used only for source file paths present in the
// symbol table, since we want them to be handled consistently across different
// platforms, which with std::filesystem::path doesn't seem to be possible.
std::string merge_paths(const std::string& base, const std::string& path);
std::string normalise_path(const char* input, bool use_backslashes_as_path_separators);
bool guess_is_windows_path(const char* path);
std::string extract_file_name(const std::string& path);
namespace ast { struct Node; }
// These are used to reference STABS types from other types within a single
// translation unit. For most games these will just be a single number, the type
// number. In some cases, for example with the homebrew SDK, type numbers are a
// pair of two numbers surrounded by round brackets e.g. (1,23) where the first
// number is the index of the include file to use (includes are listed for each
// translation unit separately), and the second number is the type number.
struct StabsTypeNumber {
s32 file = -1;
s32 type = -1;
friend auto operator<=>(const StabsTypeNumber& lhs, const StabsTypeNumber& rhs) = default;
bool valid() const { return type > -1; }
};
enum StorageClass {
STORAGE_CLASS_NONE = 0,
STORAGE_CLASS_TYPEDEF = 1,
STORAGE_CLASS_EXTERN = 2,
STORAGE_CLASS_STATIC = 3,
STORAGE_CLASS_AUTO = 4,
STORAGE_CLASS_REGISTER = 5
};
// Function pointers for the GNU demangler functions, so we can build CCC as a
// library without linking against the demangler.
struct DemanglerFunctions {
char* (*cplus_demangle)(const char *mangled, int options) = nullptr;
char* (*cplus_demangle_opname)(const char *opname, int options) = nullptr;
};
}

View File

@ -3,7 +3,7 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.5 FATAL_ERROR)
# ---[ Setup project # ---[ Setup project
PROJECT( PROJECT(
cpuinfo cpuinfo
LANGUAGES C CXX LANGUAGES C
) )
# ---[ Options. # ---[ Options.
@ -13,6 +13,26 @@ SET(CPUINFO_RUNTIME_TYPE "default" CACHE STRING "Type of runtime library (shared
SET_PROPERTY(CACHE CPUINFO_RUNTIME_TYPE PROPERTY STRINGS default static shared) SET_PROPERTY(CACHE CPUINFO_RUNTIME_TYPE PROPERTY STRINGS default static shared)
SET(CPUINFO_LOG_LEVEL "default" CACHE STRING "Minimum logging level (info with lower severity will be ignored)") SET(CPUINFO_LOG_LEVEL "default" CACHE STRING "Minimum logging level (info with lower severity will be ignored)")
SET_PROPERTY(CACHE CPUINFO_LOG_LEVEL PROPERTY STRINGS default debug info warning error fatal none) SET_PROPERTY(CACHE CPUINFO_LOG_LEVEL PROPERTY STRINGS default debug info warning error fatal none)
IF(ANDROID)
OPTION(CPUINFO_LOG_TO_STDIO "Log errors, warnings, and information to stdout/stderr" OFF)
ELSE()
OPTION(CPUINFO_LOG_TO_STDIO "Log errors, warnings, and information to stdout/stderr" ON)
ENDIF()
OPTION(CPUINFO_BUILD_TOOLS "Build command-line tools" OFF)
OPTION(CPUINFO_BUILD_UNIT_TESTS "Build cpuinfo unit tests" OFF)
OPTION(CPUINFO_BUILD_MOCK_TESTS "Build cpuinfo mock tests" OFF)
OPTION(CPUINFO_BUILD_BENCHMARKS "Build cpuinfo micro-benchmarks" OFF)
OPTION(CPUINFO_BUILD_PKG_CONFIG "Build pkg-config manifest" OFF)
OPTION(USE_SYSTEM_LIBS "Use system libraries instead of downloading and building them" OFF)
OPTION(USE_SYSTEM_GOOGLEBENCHMARK "Use system Google Benchmark library instead of downloading and building it" ${USE_SYSTEM_LIBS})
OPTION(USE_SYSTEM_GOOGLETEST "Use system Google Test library instead of downloading and building it" ${USE_SYSTEM_LIBS})
# ---[ CMake options
INCLUDE(GNUInstallDirs)
IF(CPUINFO_BUILD_UNIT_TESTS OR CPUINFO_BUILD_MOCK_TESTS)
ENABLE_TESTING()
ENDIF()
MACRO(CPUINFO_TARGET_ENABLE_C99 target) MACRO(CPUINFO_TARGET_ENABLE_C99 target)
SET_TARGET_PROPERTIES(${target} PROPERTIES SET_TARGET_PROPERTIES(${target} PROPERTIES
@ -22,7 +42,7 @@ ENDMACRO()
MACRO(CPUINFO_TARGET_ENABLE_CXX11 target) MACRO(CPUINFO_TARGET_ENABLE_CXX11 target)
SET_TARGET_PROPERTIES(${target} PROPERTIES SET_TARGET_PROPERTIES(${target} PROPERTIES
CXX_STANDARD 11 CXX_STANDARD 14
CXX_EXTENSIONS NO) CXX_EXTENSIONS NO)
ENDMACRO() ENDMACRO()
@ -38,10 +58,32 @@ MACRO(CPUINFO_TARGET_RUNTIME_LIBRARY target)
ENDIF() ENDIF()
ENDMACRO() ENDMACRO()
# -- [ Determine whether building for Apple's desktop or mobile OSes
IF(CMAKE_SYSTEM_NAME MATCHES "^(Darwin|iOS|tvOS|watchOS)$")
SET(IS_APPLE_OS TRUE)
ELSE()
SET(IS_APPLE_OS FALSE)
ENDIF()
# -- [ Determine target processor # -- [ Determine target processor
SET(CPUINFO_TARGET_PROCESSOR "${CMAKE_SYSTEM_PROCESSOR}") SET(CPUINFO_TARGET_PROCESSOR "${CMAKE_SYSTEM_PROCESSOR}")
IF(CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_OSX_ARCHITECTURES MATCHES "^(x86_64|arm64)$") IF(CMAKE_SYSTEM_NAME MATCHES "FreeBSD" AND CPUINFO_TARGET_PROCESSOR STREQUAL "amd64")
SET(CPUINFO_TARGET_PROCESSOR "AMD64")
ENDIF()
IF(IS_APPLE_OS AND CMAKE_OSX_ARCHITECTURES MATCHES "^(x86_64|arm64.*)$")
SET(CPUINFO_TARGET_PROCESSOR "${CMAKE_OSX_ARCHITECTURES}") SET(CPUINFO_TARGET_PROCESSOR "${CMAKE_OSX_ARCHITECTURES}")
ELSEIF(CMAKE_GENERATOR MATCHES "^Visual Studio " AND CMAKE_VS_PLATFORM_NAME)
IF(CMAKE_VS_PLATFORM_NAME STREQUAL "Win32")
SET(CPUINFO_TARGET_PROCESSOR "x86")
ELSEIF(CMAKE_VS_PLATFORM_NAME STREQUAL "x64")
SET(CPUINFO_TARGET_PROCESSOR "x86_64")
ELSEIF(CMAKE_VS_PLATFORM_NAME STREQUAL "ARM64")
SET(CPUINFO_TARGET_PROCESSOR "arm64")
ELSEIF(CMAKE_VS_PLATFORM_NAME MATCHES "^(ARM64EC|arm64ec|ARM64E|arm64e)")
SET(CPUINFO_TARGET_PROCESSOR "arm64")
ELSE()
MESSAGE(FATAL_ERROR "Unsupported Visual Studio architecture \"${CMAKE_VS_PLATFORM_NAME}\"")
ENDIF()
ENDIF() ENDIF()
# ---[ Build flags # ---[ Build flags
@ -53,20 +95,21 @@ IF(NOT CMAKE_SYSTEM_PROCESSOR)
"cpuinfo will compile, but cpuinfo_initialize() will always fail.") "cpuinfo will compile, but cpuinfo_initialize() will always fail.")
SET(CPUINFO_SUPPORTED_PLATFORM FALSE) SET(CPUINFO_SUPPORTED_PLATFORM FALSE)
ENDIF() ENDIF()
ELSEIF(NOT CPUINFO_TARGET_PROCESSOR MATCHES "^(i[3-6]86|AMD64|x86(_64)?|armv[5-8].*|aarch64|arm64)$") ELSEIF(NOT CPUINFO_TARGET_PROCESSOR MATCHES "^(i[3-6]86|AMD64|x86(_64)?|armv[5-8].*|aarch64|arm64.*|ARM64.*|riscv(32|64))$")
MESSAGE(WARNING MESSAGE(WARNING
"Target processor architecture \"${CPUINFO_TARGET_PROCESSOR}\" is not supported in cpuinfo. " "Target processor architecture \"${CPUINFO_TARGET_PROCESSOR}\" is not supported in cpuinfo. "
"cpuinfo will compile, but cpuinfo_initialize() will always fail.") "cpuinfo will compile, but cpuinfo_initialize() will always fail.")
SET(CPUINFO_SUPPORTED_PLATFORM FALSE) SET(CPUINFO_SUPPORTED_PLATFORM FALSE)
ENDIF() ENDIF()
IF(NOT CMAKE_SYSTEM_NAME) IF(NOT CMAKE_SYSTEM_NAME)
MESSAGE(WARNING MESSAGE(WARNING
"Target operating system is not specified. " "Target operating system is not specified. "
"cpuinfo will compile, but cpuinfo_initialize() will always fail.") "cpuinfo will compile, but cpuinfo_initialize() will always fail.")
SET(CPUINFO_SUPPORTED_PLATFORM FALSE) SET(CPUINFO_SUPPORTED_PLATFORM FALSE)
ELSEIF(NOT CMAKE_SYSTEM_NAME MATCHES "^(Windows|CYGWIN|MSYS|Darwin|Linux|Android)$") ELSEIF(NOT CMAKE_SYSTEM_NAME MATCHES "^(Windows|WindowsStore|CYGWIN|MSYS|Darwin|Linux|Android|FreeBSD)$")
IF(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.14" AND NOT CMAKE_SYSTEM_NAME STREQUAL "iOS") IF(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.14" AND NOT IS_APPLE_OS)
MESSAGE(WARNING MESSAGE(WARNING
"Target operating system \"${CMAKE_SYSTEM_NAME}\" is not supported in cpuinfo. " "Target operating system \"${CMAKE_SYSTEM_NAME}\" is not supported in cpuinfo. "
"cpuinfo will compile, but cpuinfo_initialize() will always fail.") "cpuinfo will compile, but cpuinfo_initialize() will always fail.")
@ -74,11 +117,48 @@ ELSEIF(NOT CMAKE_SYSTEM_NAME MATCHES "^(Windows|CYGWIN|MSYS|Darwin|Linux|Android
ENDIF() ENDIF()
ENDIF() ENDIF()
IF(CPUINFO_SUPPORTED_PLATFORM)
IF(CPUINFO_BUILD_MOCK_TESTS OR CPUINFO_BUILD_UNIT_TESTS OR CPUINFO_BUILD_BENCHMARKS)
ENABLE_LANGUAGE(CXX)
ENDIF()
ENDIF()
# ---[ Download deps
SET(CONFU_DEPENDENCIES_SOURCE_DIR ${CMAKE_SOURCE_DIR}/deps
CACHE PATH "Confu-style dependencies source directory")
SET(CONFU_DEPENDENCIES_BINARY_DIR ${CMAKE_BINARY_DIR}/deps
CACHE PATH "Confu-style dependencies binary directory")
IF(CPUINFO_SUPPORTED_PLATFORM AND (CPUINFO_BUILD_MOCK_TESTS OR CPUINFO_BUILD_UNIT_TESTS))
IF(USE_SYSTEM_GOOGLETEST)
FIND_PACKAGE(GTest REQUIRED)
ELSEIF(NOT DEFINED GOOGLETEST_SOURCE_DIR)
MESSAGE(STATUS "Downloading Google Test to ${CONFU_DEPENDENCIES_SOURCE_DIR}/googletest (define GOOGLETEST_SOURCE_DIR to avoid it)")
CONFIGURE_FILE(cmake/DownloadGoogleTest.cmake "${CONFU_DEPENDENCIES_BINARY_DIR}/googletest-download/CMakeLists.txt")
EXECUTE_PROCESS(COMMAND "${CMAKE_COMMAND}" -G "${CMAKE_GENERATOR}" .
WORKING_DIRECTORY "${CONFU_DEPENDENCIES_BINARY_DIR}/googletest-download")
EXECUTE_PROCESS(COMMAND "${CMAKE_COMMAND}" --build .
WORKING_DIRECTORY "${CONFU_DEPENDENCIES_BINARY_DIR}/googletest-download")
SET(GOOGLETEST_SOURCE_DIR "${CONFU_DEPENDENCIES_SOURCE_DIR}/googletest" CACHE STRING "Google Test source directory")
ENDIF()
ENDIF()
IF(CPUINFO_SUPPORTED_PLATFORM AND CPUINFO_BUILD_BENCHMARKS)
IF(USE_SYSTEM_GOOGLEBENCHMARK)
FIND_PACKAGE(benchmark REQUIRED)
ELSEIF(NOT DEFINED GOOGLEBENCHMARK_SOURCE_DIR)
MESSAGE(STATUS "Downloading Google Benchmark to ${CONFU_DEPENDENCIES_SOURCE_DIR}/googlebenchmark (define GOOGLEBENCHMARK_SOURCE_DIR to avoid it)")
CONFIGURE_FILE(cmake/DownloadGoogleBenchmark.cmake "${CONFU_DEPENDENCIES_BINARY_DIR}/googlebenchmark-download/CMakeLists.txt")
EXECUTE_PROCESS(COMMAND "${CMAKE_COMMAND}" -G "${CMAKE_GENERATOR}" .
WORKING_DIRECTORY "${CONFU_DEPENDENCIES_BINARY_DIR}/googlebenchmark-download")
EXECUTE_PROCESS(COMMAND "${CMAKE_COMMAND}" --build .
WORKING_DIRECTORY "${CONFU_DEPENDENCIES_BINARY_DIR}/googlebenchmark-download")
SET(GOOGLEBENCHMARK_SOURCE_DIR "${CONFU_DEPENDENCIES_SOURCE_DIR}/googlebenchmark" CACHE STRING "Google Benchmark source directory")
ENDIF()
ENDIF()
# ---[ cpuinfo library # ---[ cpuinfo library
SET(CPUINFO_SRCS SET(CPUINFO_SRCS src/api.c src/cache.c src/init.c src/log.c)
src/init.c
src/api.c
src/cache.c)
IF(CPUINFO_SUPPORTED_PLATFORM) IF(CPUINFO_SUPPORTED_PLATFORM)
IF(NOT CMAKE_SYSTEM_NAME STREQUAL "Emscripten" AND (CPUINFO_TARGET_PROCESSOR MATCHES "^(i[3-6]86|AMD64|x86(_64)?)$" OR IOS_ARCH MATCHES "^(i386|x86_64)$")) IF(NOT CMAKE_SYSTEM_NAME STREQUAL "Emscripten" AND (CPUINFO_TARGET_PROCESSOR MATCHES "^(i[3-6]86|AMD64|x86(_64)?)$" OR IOS_ARCH MATCHES "^(i386|x86_64)$"))
@ -97,12 +177,18 @@ IF(CPUINFO_SUPPORTED_PLATFORM)
LIST(APPEND CPUINFO_SRCS LIST(APPEND CPUINFO_SRCS
src/x86/linux/init.c src/x86/linux/init.c
src/x86/linux/cpuinfo.c) src/x86/linux/cpuinfo.c)
ELSEIF(CMAKE_SYSTEM_NAME STREQUAL "Darwin" OR CMAKE_SYSTEM_NAME STREQUAL "iOS") ELSEIF(IS_APPLE_OS)
LIST(APPEND CPUINFO_SRCS src/x86/mach/init.c) LIST(APPEND CPUINFO_SRCS src/x86/mach/init.c)
ELSEIF(CMAKE_SYSTEM_NAME MATCHES "^(Windows|CYGWIN|MSYS)$") ELSEIF(CMAKE_SYSTEM_NAME MATCHES "^(Windows|WindowsStore|CYGWIN|MSYS)$")
LIST(APPEND CPUINFO_SRCS src/x86/windows/init.c) LIST(APPEND CPUINFO_SRCS src/x86/windows/init.c)
ELSEIF(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
LIST(APPEND CPUINFO_SRCS src/x86/freebsd/init.c)
ENDIF() ENDIF()
ELSEIF(CPUINFO_TARGET_PROCESSOR MATCHES "^(armv[5-8].*|aarch64|arm64)$" OR IOS_ARCH MATCHES "^(armv7.*|arm64.*)$") ELSEIF(CMAKE_SYSTEM_NAME MATCHES "^Windows" AND CPUINFO_TARGET_PROCESSOR MATCHES "^(ARM64|arm64)$")
LIST(APPEND CPUINFO_SRCS
src/arm/windows/init-by-logical-sys-info.c
src/arm/windows/init.c)
ELSEIF(CPUINFO_TARGET_PROCESSOR MATCHES "^(armv[5-8].*|aarch64|arm64.*)$" OR IOS_ARCH MATCHES "^(armv7.*|arm64.*)$")
LIST(APPEND CPUINFO_SRCS LIST(APPEND CPUINFO_SRCS
src/arm/uarch.c src/arm/uarch.c
src/arm/cache.c) src/arm/cache.c)
@ -122,13 +208,22 @@ IF(CPUINFO_SUPPORTED_PLATFORM)
ELSEIF(CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64|arm64)$") ELSEIF(CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64|arm64)$")
LIST(APPEND CPUINFO_SRCS src/arm/linux/aarch64-isa.c) LIST(APPEND CPUINFO_SRCS src/arm/linux/aarch64-isa.c)
ENDIF() ENDIF()
ELSEIF(IOS OR (CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CPUINFO_TARGET_PROCESSOR STREQUAL "arm64")) ELSEIF(IS_APPLE_OS AND CPUINFO_TARGET_PROCESSOR MATCHES "arm64.*")
LIST(APPEND CPUINFO_SRCS src/arm/mach/init.c) LIST(APPEND CPUINFO_SRCS src/arm/mach/init.c)
ENDIF() ENDIF()
IF(CMAKE_SYSTEM_NAME STREQUAL "Android") IF(CMAKE_SYSTEM_NAME STREQUAL "Android")
LIST(APPEND CPUINFO_SRCS LIST(APPEND CPUINFO_SRCS
src/arm/android/properties.c) src/arm/android/properties.c)
ENDIF() ENDIF()
ELSEIF(CPUINFO_TARGET_PROCESSOR MATCHES "^(riscv(32|64))$")
LIST(APPEND CPUINFO_SRCS
src/riscv/uarch.c)
IF(CMAKE_SYSTEM_NAME STREQUAL "Linux")
LIST(APPEND CPUINFO_SRCS
src/riscv/linux/init.c
src/riscv/linux/riscv-hw.c
src/riscv/linux/riscv-isa.c)
ENDIF()
ENDIF() ENDIF()
IF(CMAKE_SYSTEM_NAME STREQUAL "Emscripten") IF(CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
@ -142,11 +237,13 @@ IF(CPUINFO_SUPPORTED_PLATFORM)
src/linux/multiline.c src/linux/multiline.c
src/linux/cpulist.c src/linux/cpulist.c
src/linux/processors.c) src/linux/processors.c)
ELSEIF(CMAKE_SYSTEM_NAME STREQUAL "Darwin" OR CMAKE_SYSTEM_NAME STREQUAL "iOS") ELSEIF(IS_APPLE_OS)
LIST(APPEND CPUINFO_SRCS src/mach/topology.c) LIST(APPEND CPUINFO_SRCS src/mach/topology.c)
ELSEIF(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
LIST(APPEND CPUINFO_SRCS src/freebsd/topology.c)
ENDIF() ENDIF()
IF(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "Android") IF(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "Android" OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
SET(CMAKE_THREAD_PREFER_PTHREAD TRUE) SET(CMAKE_THREAD_PREFER_PTHREAD TRUE)
SET(THREADS_PREFER_PTHREAD_FLAG TRUE) SET(THREADS_PREFER_PTHREAD_FLAG TRUE)
FIND_PACKAGE(Threads REQUIRED) FIND_PACKAGE(Threads REQUIRED)
@ -166,34 +263,43 @@ ADD_LIBRARY(cpuinfo_internals STATIC ${CPUINFO_SRCS})
CPUINFO_TARGET_ENABLE_C99(cpuinfo) CPUINFO_TARGET_ENABLE_C99(cpuinfo)
CPUINFO_TARGET_ENABLE_C99(cpuinfo_internals) CPUINFO_TARGET_ENABLE_C99(cpuinfo_internals)
CPUINFO_TARGET_RUNTIME_LIBRARY(cpuinfo) CPUINFO_TARGET_RUNTIME_LIBRARY(cpuinfo)
IF(CMAKE_SYSTEM_NAME MATCHES "^(Windows|CYGWIN|MSYS)$") IF(CMAKE_SYSTEM_NAME MATCHES "^(Windows|WindowsStore|CYGWIN|MSYS)$")
# Target Windows 7+ API # Target Windows 7+ API
TARGET_COMPILE_DEFINITIONS(cpuinfo PRIVATE _WIN32_WINNT=0x0601) TARGET_COMPILE_DEFINITIONS(cpuinfo PRIVATE _WIN32_WINNT=0x0601 _CRT_SECURE_NO_WARNINGS)
TARGET_COMPILE_DEFINITIONS(cpuinfo_internals PRIVATE _WIN32_WINNT=0x0601) TARGET_COMPILE_DEFINITIONS(cpuinfo_internals PRIVATE _WIN32_WINNT=0x0601 _CRT_SECURE_NO_WARNINGS)
# Explicitly link Kernel32 for UWP build
if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
TARGET_LINK_LIBRARIES(cpuinfo PUBLIC Kernel32)
endif()
ENDIF()
IF(ANDROID AND NOT CPUINFO_LOG_TO_STDIO)
TARGET_LINK_LIBRARIES(cpuinfo PRIVATE "log")
ENDIF() ENDIF()
SET_TARGET_PROPERTIES(cpuinfo PROPERTIES PUBLIC_HEADER include/cpuinfo.h) SET_TARGET_PROPERTIES(cpuinfo PROPERTIES PUBLIC_HEADER include/cpuinfo.h)
TARGET_INCLUDE_DIRECTORIES(cpuinfo BEFORE PUBLIC include) TARGET_INCLUDE_DIRECTORIES(cpuinfo BEFORE PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
TARGET_INCLUDE_DIRECTORIES(cpuinfo BEFORE PRIVATE src) TARGET_INCLUDE_DIRECTORIES(cpuinfo BEFORE PRIVATE src)
TARGET_INCLUDE_DIRECTORIES(cpuinfo_internals BEFORE PUBLIC include src) TARGET_INCLUDE_DIRECTORIES(cpuinfo_internals BEFORE PUBLIC include src)
TARGET_COMPILE_DEFINITIONS(cpuinfo PRIVATE "CPUINFO_LOG_TO_STDIO=$<BOOL:${CPUINFO_LOG_TO_STDIO}>")
IF(CPUINFO_LOG_LEVEL STREQUAL "default") IF(CPUINFO_LOG_LEVEL STREQUAL "default")
# default logging level: error (subject to change) # default logging level: error (subject to change)
TARGET_COMPILE_DEFINITIONS(cpuinfo PRIVATE CPUINFO_LOG_LEVEL=2) TARGET_COMPILE_DEFINITIONS(cpuinfo PRIVATE "CPUINFO_LOG_LEVEL=2")
ELSEIF(CPUINFO_LOG_LEVEL STREQUAL "debug") ELSEIF(CPUINFO_LOG_LEVEL STREQUAL "debug")
TARGET_COMPILE_DEFINITIONS(cpuinfo PRIVATE CPUINFO_LOG_LEVEL=5) TARGET_COMPILE_DEFINITIONS(cpuinfo PRIVATE "CPUINFO_LOG_LEVEL=5")
ELSEIF(CPUINFO_LOG_LEVEL STREQUAL "info") ELSEIF(CPUINFO_LOG_LEVEL STREQUAL "info")
TARGET_COMPILE_DEFINITIONS(cpuinfo PRIVATE CPUINFO_LOG_LEVEL=4) TARGET_COMPILE_DEFINITIONS(cpuinfo PRIVATE "CPUINFO_LOG_LEVEL=4")
ELSEIF(CPUINFO_LOG_LEVEL STREQUAL "warning") ELSEIF(CPUINFO_LOG_LEVEL STREQUAL "warning")
TARGET_COMPILE_DEFINITIONS(cpuinfo PRIVATE CPUINFO_LOG_LEVEL=3) TARGET_COMPILE_DEFINITIONS(cpuinfo PRIVATE "CPUINFO_LOG_LEVEL=3")
ELSEIF(CPUINFO_LOG_LEVEL STREQUAL "error") ELSEIF(CPUINFO_LOG_LEVEL STREQUAL "error")
TARGET_COMPILE_DEFINITIONS(cpuinfo PRIVATE CPUINFO_LOG_LEVEL=2) TARGET_COMPILE_DEFINITIONS(cpuinfo PRIVATE "CPUINFO_LOG_LEVEL=2")
ELSEIF(CPUINFO_LOG_LEVEL STREQUAL "fatal") ELSEIF(CPUINFO_LOG_LEVEL STREQUAL "fatal")
TARGET_COMPILE_DEFINITIONS(cpuinfo PRIVATE CPUINFO_LOG_LEVEL=1) TARGET_COMPILE_DEFINITIONS(cpuinfo PRIVATE "CPUINFO_LOG_LEVEL=1")
ELSEIF(CPUINFO_LOG_LEVEL STREQUAL "none") ELSEIF(CPUINFO_LOG_LEVEL STREQUAL "none")
TARGET_COMPILE_DEFINITIONS(cpuinfo PRIVATE CPUINFO_LOG_LEVEL=0) TARGET_COMPILE_DEFINITIONS(cpuinfo PRIVATE "CPUINFO_LOG_LEVEL=0")
ELSE() ELSE()
MESSAGE(FATAL_ERROR "Unsupported logging level ${CPUINFO_LOG_LEVEL}") MESSAGE(FATAL_ERROR "Unsupported logging level ${CPUINFO_LOG_LEVEL}")
ENDIF() ENDIF()
TARGET_COMPILE_DEFINITIONS(cpuinfo_internals PRIVATE CPUINFO_LOG_LEVEL=0) TARGET_COMPILE_DEFINITIONS(cpuinfo_internals PRIVATE "CPUINFO_LOG_LEVEL=0")
TARGET_COMPILE_DEFINITIONS(cpuinfo_internals PRIVATE "CPUINFO_LOG_TO_STDIO=1")
IF(CPUINFO_SUPPORTED_PLATFORM) IF(CPUINFO_SUPPORTED_PLATFORM)
TARGET_COMPILE_DEFINITIONS(cpuinfo INTERFACE CPUINFO_SUPPORTED_PLATFORM=1) TARGET_COMPILE_DEFINITIONS(cpuinfo INTERFACE CPUINFO_SUPPORTED_PLATFORM=1)
@ -202,22 +308,618 @@ IF(CPUINFO_SUPPORTED_PLATFORM)
TARGET_LINK_LIBRARIES(cpuinfo_internals PUBLIC ${CMAKE_THREAD_LIBS_INIT}) TARGET_LINK_LIBRARIES(cpuinfo_internals PUBLIC ${CMAKE_THREAD_LIBS_INIT})
TARGET_COMPILE_DEFINITIONS(cpuinfo PRIVATE _GNU_SOURCE=1) TARGET_COMPILE_DEFINITIONS(cpuinfo PRIVATE _GNU_SOURCE=1)
TARGET_COMPILE_DEFINITIONS(cpuinfo_internals PRIVATE _GNU_SOURCE=1) TARGET_COMPILE_DEFINITIONS(cpuinfo_internals PRIVATE _GNU_SOURCE=1)
ELSEIF(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
TARGET_LINK_LIBRARIES(cpuinfo PUBLIC ${CMAKE_THREAD_LIBS_INIT})
TARGET_LINK_LIBRARIES(cpuinfo_internals PUBLIC ${CMAKE_THREAD_LIBS_INIT})
ENDIF() ENDIF()
ELSE() ELSE()
TARGET_COMPILE_DEFINITIONS(cpuinfo INTERFACE CPUINFO_SUPPORTED_PLATFORM=0) TARGET_COMPILE_DEFINITIONS(cpuinfo INTERFACE CPUINFO_SUPPORTED_PLATFORM=0)
ENDIF() ENDIF()
# ---[ cpuinfo dependencies: clog ADD_LIBRARY(${PROJECT_NAME}::cpuinfo ALIAS cpuinfo)
IF(NOT DEFINED CLOG_SOURCE_DIR)
SET(CLOG_SOURCE_DIR "${PROJECT_SOURCE_DIR}/deps/clog") # support find_package(cpuinfo CONFIG)
INCLUDE(CMakePackageConfigHelpers)
GET_FILENAME_COMPONENT(CONFIG_FILE_PATH ${CMAKE_CURRENT_BINARY_DIR}/cpuinfo-config.cmake ABSOLUTE)
CONFIGURE_PACKAGE_CONFIG_FILE(
cmake/cpuinfo-config.cmake.in ${CONFIG_FILE_PATH}
INSTALL_DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME})
INSTALL(FILES ${CONFIG_FILE_PATH}
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME}) # cpuinfo_DIR ${prefix}/share/cpuinfo
INSTALL(TARGETS cpuinfo
EXPORT cpuinfo-targets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
INSTALL(EXPORT cpuinfo-targets
NAMESPACE ${PROJECT_NAME}:: # IMPORTED cpuinfo::cpuinfo
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME})
# ---[ cpuinfo micro-benchmarks
IF(CPUINFO_SUPPORTED_PLATFORM AND CPUINFO_BUILD_BENCHMARKS)
# ---[ Build google benchmark
IF(NOT TARGET benchmark AND NOT USE_SYSTEM_GOOGLEBENCHMARK)
SET(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "")
ADD_SUBDIRECTORY(
"${GOOGLEBENCHMARK_SOURCE_DIR}"
"${CONFU_DEPENDENCIES_BINARY_DIR}/googlebenchmark")
ENDIF()
IF(CMAKE_SYSTEM_NAME MATCHES "^(Linux|Android)$")
ADD_EXECUTABLE(get-current-bench bench/get-current.cc)
TARGET_LINK_LIBRARIES(get-current-bench cpuinfo benchmark)
ENDIF()
ADD_EXECUTABLE(init-bench bench/init.cc)
TARGET_LINK_LIBRARIES(init-bench cpuinfo benchmark)
ENDIF() ENDIF()
IF(NOT TARGET clog)
SET(CLOG_BUILD_TESTS OFF CACHE BOOL "") IF(CPUINFO_SUPPORTED_PLATFORM)
SET(CLOG_RUNTIME_TYPE "${CPUINFO_RUNTIME_TYPE}" CACHE STRING "") IF(CPUINFO_BUILD_MOCK_TESTS OR CPUINFO_BUILD_UNIT_TESTS)
ADD_SUBDIRECTORY( # ---[ Build google test
"${CLOG_SOURCE_DIR}") IF(NOT TARGET gtest AND NOT USE_SYSTEM_GOOGLETEST)
# We build static version of clog but a dynamic library may indirectly depend on it IF(MSVC AND NOT CPUINFO_RUNTIME_TYPE STREQUAL "static")
SET_PROPERTY(TARGET clog PROPERTY POSITION_INDEPENDENT_CODE ON) SET(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
ENDIF()
ADD_SUBDIRECTORY(
"${GOOGLETEST_SOURCE_DIR}"
"${CONFU_DEPENDENCIES_BINARY_DIR}/googletest")
ENDIF()
ENDIF()
ENDIF()
# ---[ cpuinfo mock library and mock tests
IF(CPUINFO_SUPPORTED_PLATFORM AND CPUINFO_BUILD_MOCK_TESTS)
SET(CPUINFO_MOCK_SRCS "${CPUINFO_SRCS}")
IF(CPUINFO_TARGET_PROCESSOR MATCHES "^(i[3-6]86|AMD64|x86(_64)?)$")
LIST(APPEND CPUINFO_MOCK_SRCS src/x86/mockcpuid.c)
ENDIF()
IF(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "Android")
LIST(APPEND CPUINFO_MOCK_SRCS src/linux/mockfile.c)
ENDIF()
ADD_LIBRARY(cpuinfo_mock STATIC ${CPUINFO_MOCK_SRCS})
CPUINFO_TARGET_ENABLE_C99(cpuinfo_mock)
CPUINFO_TARGET_RUNTIME_LIBRARY(cpuinfo_mock)
SET_TARGET_PROPERTIES(cpuinfo_mock PROPERTIES PUBLIC_HEADER include/cpuinfo.h)
TARGET_INCLUDE_DIRECTORIES(cpuinfo_mock BEFORE PUBLIC include)
TARGET_INCLUDE_DIRECTORIES(cpuinfo_mock BEFORE PRIVATE src)
TARGET_COMPILE_DEFINITIONS(cpuinfo_mock PUBLIC "CPUINFO_MOCK=1")
TARGET_COMPILE_DEFINITIONS(cpuinfo_mock PRIVATE "CPUINFO_LOG_LEVEL=5")
TARGET_COMPILE_DEFINITIONS(cpuinfo_mock PRIVATE "CPUINFO_LOG_TO_STDIO=1")
IF(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "Android")
TARGET_LINK_LIBRARIES(cpuinfo_mock PUBLIC ${CMAKE_THREAD_LIBS_INIT})
TARGET_COMPILE_DEFINITIONS(cpuinfo_mock PRIVATE _GNU_SOURCE=1)
ENDIF()
IF(CMAKE_SYSTEM_NAME STREQUAL "Android" AND CMAKE_SYSTEM_PROCESSOR MATCHES "^(armv5te|armv7-a)$")
ADD_EXECUTABLE(atm7029b-tablet-test test/mock/atm7029b-tablet.cc)
TARGET_INCLUDE_DIRECTORIES(atm7029b-tablet-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(atm7029b-tablet-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME atm7029b-tablet-test COMMAND atm7029b-tablet-test)
ADD_EXECUTABLE(blu-r1-hd-test test/mock/blu-r1-hd.cc)
TARGET_INCLUDE_DIRECTORIES(blu-r1-hd-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(blu-r1-hd-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME blu-r1-hd-test COMMAND blu-r1-hd-test)
ADD_EXECUTABLE(galaxy-a3-2016-eu-test test/mock/galaxy-a3-2016-eu.cc)
TARGET_INCLUDE_DIRECTORIES(galaxy-a3-2016-eu-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(galaxy-a3-2016-eu-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME galaxy-a3-2016-eu-test COMMAND galaxy-a3-2016-eu-test)
ADD_EXECUTABLE(galaxy-a8-2016-duos-test test/mock/galaxy-a8-2016-duos.cc)
TARGET_INCLUDE_DIRECTORIES(galaxy-a8-2016-duos-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(galaxy-a8-2016-duos-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME galaxy-a8-2016-duos-test COMMAND galaxy-a8-2016-duos-test)
ADD_EXECUTABLE(galaxy-grand-prime-value-edition-test test/mock/galaxy-grand-prime-value-edition.cc)
TARGET_INCLUDE_DIRECTORIES(galaxy-grand-prime-value-edition-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(galaxy-grand-prime-value-edition-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME galaxy-grand-prime-value-edition-test COMMAND galaxy-grand-prime-value-edition-test)
ADD_EXECUTABLE(galaxy-j1-2016-test test/mock/galaxy-j1-2016.cc)
TARGET_INCLUDE_DIRECTORIES(galaxy-j1-2016-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(galaxy-j1-2016-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME galaxy-j1-2016-test COMMAND galaxy-j1-2016-test)
ADD_EXECUTABLE(galaxy-j5-test test/mock/galaxy-j5.cc)
TARGET_INCLUDE_DIRECTORIES(galaxy-j5-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(galaxy-j5-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME galaxy-j5-test COMMAND galaxy-j5-test)
ADD_EXECUTABLE(galaxy-j7-prime-test test/mock/galaxy-j7-prime.cc)
TARGET_INCLUDE_DIRECTORIES(galaxy-j7-prime-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(galaxy-j7-prime-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME galaxy-j7-prime-test COMMAND galaxy-j7-prime-test)
ADD_EXECUTABLE(galaxy-j7-tmobile-test test/mock/galaxy-j7-tmobile.cc)
TARGET_INCLUDE_DIRECTORIES(galaxy-j7-tmobile-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(galaxy-j7-tmobile-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME galaxy-j7-tmobile-test COMMAND galaxy-j7-tmobile-test)
ADD_EXECUTABLE(galaxy-j7-uae-test test/mock/galaxy-j7-uae.cc)
TARGET_INCLUDE_DIRECTORIES(galaxy-j7-uae-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(galaxy-j7-uae-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME galaxy-j7-uae-test COMMAND galaxy-j7-uae-test)
ADD_EXECUTABLE(galaxy-s3-us-test test/mock/galaxy-s3-us.cc)
TARGET_INCLUDE_DIRECTORIES(galaxy-s3-us-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(galaxy-s3-us-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME galaxy-s3-us-test COMMAND galaxy-s3-us-test)
ADD_EXECUTABLE(galaxy-s4-us-test test/mock/galaxy-s4-us.cc)
TARGET_INCLUDE_DIRECTORIES(galaxy-s4-us-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(galaxy-s4-us-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME galaxy-s4-us-test COMMAND galaxy-s4-us-test)
ADD_EXECUTABLE(galaxy-s5-global-test test/mock/galaxy-s5-global.cc)
TARGET_INCLUDE_DIRECTORIES(galaxy-s5-global-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(galaxy-s5-global-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME galaxy-s5-global-test COMMAND galaxy-s5-global-test)
ADD_EXECUTABLE(galaxy-s5-us-test test/mock/galaxy-s5-us.cc)
TARGET_INCLUDE_DIRECTORIES(galaxy-s5-us-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(galaxy-s5-us-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME galaxy-s5-us-test COMMAND galaxy-s5-us-test)
ADD_EXECUTABLE(galaxy-tab-3-7.0-test test/mock/galaxy-tab-3-7.0.cc)
TARGET_INCLUDE_DIRECTORIES(galaxy-tab-3-7.0-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(galaxy-tab-3-7.0-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME galaxy-tab-3-7.0-test COMMAND galaxy-tab-3-7.0-test)
ADD_EXECUTABLE(galaxy-tab-3-lite-test test/mock/galaxy-tab-3-lite.cc)
TARGET_INCLUDE_DIRECTORIES(galaxy-tab-3-lite-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(galaxy-tab-3-lite-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME galaxy-tab-3-lite-test COMMAND galaxy-tab-3-lite-test)
ADD_EXECUTABLE(galaxy-win-duos-test test/mock/galaxy-win-duos.cc)
TARGET_INCLUDE_DIRECTORIES(galaxy-win-duos-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(galaxy-win-duos-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME galaxy-win-duos-test COMMAND galaxy-win-duos-test)
ADD_EXECUTABLE(huawei-ascend-p7-test test/mock/huawei-ascend-p7.cc)
TARGET_INCLUDE_DIRECTORIES(huawei-ascend-p7-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(huawei-ascend-p7-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME huawei-ascend-p7-test COMMAND huawei-ascend-p7-test)
ADD_EXECUTABLE(huawei-honor-6-test test/mock/huawei-honor-6.cc)
TARGET_INCLUDE_DIRECTORIES(huawei-honor-6-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(huawei-honor-6-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME huawei-honor-6-test COMMAND huawei-honor-6-test)
ADD_EXECUTABLE(lenovo-a6600-plus-test test/mock/lenovo-a6600-plus.cc)
TARGET_INCLUDE_DIRECTORIES(lenovo-a6600-plus-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(lenovo-a6600-plus-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME lenovo-a6600-plus-test COMMAND lenovo-a6600-plus-test)
ADD_EXECUTABLE(lenovo-vibe-x2-test test/mock/lenovo-vibe-x2.cc)
TARGET_INCLUDE_DIRECTORIES(lenovo-vibe-x2-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(lenovo-vibe-x2-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME lenovo-vibe-x2-test COMMAND lenovo-vibe-x2-test)
ADD_EXECUTABLE(lg-k10-eu-test test/mock/lg-k10-eu.cc)
TARGET_INCLUDE_DIRECTORIES(lg-k10-eu-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(lg-k10-eu-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME lg-k10-eu-test COMMAND lg-k10-eu-test)
ADD_EXECUTABLE(lg-optimus-g-pro-test test/mock/lg-optimus-g-pro.cc)
TARGET_INCLUDE_DIRECTORIES(lg-optimus-g-pro-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(lg-optimus-g-pro-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME lg-optimus-g-pro-test COMMAND lg-optimus-g-pro-test)
ADD_EXECUTABLE(moto-e-gen1-test test/mock/moto-e-gen1.cc)
TARGET_INCLUDE_DIRECTORIES(moto-e-gen1-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(moto-e-gen1-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME moto-e-gen1-test COMMAND moto-e-gen1-test)
ADD_EXECUTABLE(moto-g-gen1-test test/mock/moto-g-gen1.cc)
TARGET_INCLUDE_DIRECTORIES(moto-g-gen1-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(moto-g-gen1-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME moto-g-gen1-test COMMAND moto-g-gen1-test)
ADD_EXECUTABLE(moto-g-gen2-test test/mock/moto-g-gen2.cc)
TARGET_INCLUDE_DIRECTORIES(moto-g-gen2-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(moto-g-gen2-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME moto-g-gen2-test COMMAND moto-g-gen2-test)
ADD_EXECUTABLE(moto-g-gen3-test test/mock/moto-g-gen3.cc)
TARGET_INCLUDE_DIRECTORIES(moto-g-gen3-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(moto-g-gen3-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME moto-g-gen3-test COMMAND moto-g-gen3-test)
ADD_EXECUTABLE(moto-g-gen4-test test/mock/moto-g-gen4.cc)
TARGET_INCLUDE_DIRECTORIES(moto-g-gen4-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(moto-g-gen4-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME moto-g-gen4-test COMMAND moto-g-gen4-test)
ADD_EXECUTABLE(moto-g-gen5-test test/mock/moto-g-gen5.cc)
TARGET_INCLUDE_DIRECTORIES(moto-g-gen5-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(moto-g-gen5-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME moto-g-gen5-test COMMAND moto-g-gen5-test)
ADD_EXECUTABLE(nexus-s-test test/mock/nexus-s.cc)
TARGET_INCLUDE_DIRECTORIES(nexus-s-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(nexus-s-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME nexus-s-test COMMAND nexus-s-test)
ADD_EXECUTABLE(nexus4-test test/mock/nexus4.cc)
TARGET_INCLUDE_DIRECTORIES(nexus4-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(nexus4-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME nexus4-test COMMAND nexus4-test)
ADD_EXECUTABLE(nexus6-test test/mock/nexus6.cc)
TARGET_INCLUDE_DIRECTORIES(nexus6-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(nexus6-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME nexus6-test COMMAND nexus6-test)
ADD_EXECUTABLE(nexus10-test test/mock/nexus10.cc)
TARGET_INCLUDE_DIRECTORIES(nexus10-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(nexus10-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME nexus10-test COMMAND nexus10-test)
ADD_EXECUTABLE(padcod-10.1-test test/mock/padcod-10.1.cc)
TARGET_INCLUDE_DIRECTORIES(padcod-10.1-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(padcod-10.1-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME padcod-10.1-test COMMAND padcod-10.1-test)
ADD_EXECUTABLE(xiaomi-redmi-2a-test test/mock/xiaomi-redmi-2a.cc)
TARGET_INCLUDE_DIRECTORIES(xiaomi-redmi-2a-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(xiaomi-redmi-2a-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME xiaomi-redmi-2a-test COMMAND xiaomi-redmi-2a-test)
ADD_EXECUTABLE(xperia-sl-test test/mock/xperia-sl.cc)
TARGET_INCLUDE_DIRECTORIES(xperia-sl-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(xperia-sl-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME xperia-sl-test COMMAND xperia-sl-test)
ENDIF()
IF(CMAKE_SYSTEM_NAME STREQUAL "Android" AND CMAKE_SYSTEM_PROCESSOR MATCHES "^(armv5te|armv7-a|aarch64)$")
ADD_EXECUTABLE(alcatel-revvl-test test/mock/alcatel-revvl.cc)
TARGET_INCLUDE_DIRECTORIES(alcatel-revvl-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(alcatel-revvl-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME alcatel-revvl-test COMMAND alcatel-revvl-test)
ADD_EXECUTABLE(galaxy-a8-2018-test test/mock/galaxy-a8-2018.cc)
TARGET_INCLUDE_DIRECTORIES(galaxy-a8-2018-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(galaxy-a8-2018-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME galaxy-a8-2018-test COMMAND galaxy-a8-2018-test)
ADD_EXECUTABLE(galaxy-c9-pro-test test/mock/galaxy-c9-pro.cc)
TARGET_INCLUDE_DIRECTORIES(galaxy-c9-pro-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(galaxy-c9-pro-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME galaxy-c9-pro-test COMMAND galaxy-c9-pro-test)
ADD_EXECUTABLE(galaxy-s6-test test/mock/galaxy-s6.cc)
TARGET_INCLUDE_DIRECTORIES(galaxy-s6-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(galaxy-s6-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME galaxy-s6-test COMMAND galaxy-s6-test)
ADD_EXECUTABLE(galaxy-s7-us-test test/mock/galaxy-s7-us.cc)
TARGET_INCLUDE_DIRECTORIES(galaxy-s7-us-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(galaxy-s7-us-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME galaxy-s7-us-test COMMAND galaxy-s7-us-test)
ADD_EXECUTABLE(galaxy-s7-global-test test/mock/galaxy-s7-global.cc)
TARGET_INCLUDE_DIRECTORIES(galaxy-s7-global-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(galaxy-s7-global-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME galaxy-s7-global-test COMMAND galaxy-s7-global-test)
ADD_EXECUTABLE(galaxy-s8-us-test test/mock/galaxy-s8-us.cc)
TARGET_INCLUDE_DIRECTORIES(galaxy-s8-us-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(galaxy-s8-us-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME galaxy-s8-us-test COMMAND galaxy-s8-us-test)
ADD_EXECUTABLE(galaxy-s8-global-test test/mock/galaxy-s8-global.cc)
TARGET_INCLUDE_DIRECTORIES(galaxy-s8-global-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(galaxy-s8-global-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME galaxy-s8-global-test COMMAND galaxy-s8-global-test)
ADD_EXECUTABLE(galaxy-s9-us-test test/mock/galaxy-s9-us.cc)
TARGET_INCLUDE_DIRECTORIES(galaxy-s9-us-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(galaxy-s9-us-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME galaxy-s9-us-test COMMAND galaxy-s9-us-test)
ADD_EXECUTABLE(galaxy-s9-global-test test/mock/galaxy-s9-global.cc)
TARGET_INCLUDE_DIRECTORIES(galaxy-s9-global-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(galaxy-s9-global-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME galaxy-s9-global-test COMMAND galaxy-s9-global-test)
ADD_EXECUTABLE(huawei-mate-8-test test/mock/huawei-mate-8.cc)
TARGET_INCLUDE_DIRECTORIES(huawei-mate-8-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(huawei-mate-8-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME huawei-mate-8-test COMMAND huawei-mate-8-test)
ADD_EXECUTABLE(huawei-mate-9-test test/mock/huawei-mate-9.cc)
TARGET_INCLUDE_DIRECTORIES(huawei-mate-9-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(huawei-mate-9-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME huawei-mate-9-test COMMAND huawei-mate-9-test)
ADD_EXECUTABLE(huawei-mate-10-test test/mock/huawei-mate-10.cc)
TARGET_INCLUDE_DIRECTORIES(huawei-mate-10-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(huawei-mate-10-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME huawei-mate-10-test COMMAND huawei-mate-10-test)
ADD_EXECUTABLE(huawei-mate-20-test test/mock/huawei-mate-20.cc)
TARGET_INCLUDE_DIRECTORIES(huawei-mate-20-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(huawei-mate-20-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME huawei-mate-20-test COMMAND huawei-mate-20-test)
ADD_EXECUTABLE(huawei-p8-lite-test test/mock/huawei-p8-lite.cc)
TARGET_INCLUDE_DIRECTORIES(huawei-p8-lite-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(huawei-p8-lite-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME huawei-p8-lite-test COMMAND huawei-p8-lite-test)
ADD_EXECUTABLE(huawei-p9-lite-test test/mock/huawei-p9-lite.cc)
TARGET_INCLUDE_DIRECTORIES(huawei-p9-lite-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(huawei-p9-lite-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME huawei-p9-lite-test COMMAND huawei-p9-lite-test)
ADD_EXECUTABLE(huawei-p20-pro-test test/mock/huawei-p20-pro.cc)
TARGET_INCLUDE_DIRECTORIES(huawei-p20-pro-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(huawei-p20-pro-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME huawei-p20-pro-test COMMAND huawei-p20-pro-test)
ADD_EXECUTABLE(iconia-one-10-test test/mock/iconia-one-10.cc)
TARGET_INCLUDE_DIRECTORIES(iconia-one-10-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(iconia-one-10-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME iconia-one-10-test COMMAND iconia-one-10-test)
ADD_EXECUTABLE(meizu-pro-6-test test/mock/meizu-pro-6.cc)
TARGET_INCLUDE_DIRECTORIES(meizu-pro-6-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(meizu-pro-6-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME meizu-pro-6-test COMMAND meizu-pro-6-test)
ADD_EXECUTABLE(meizu-pro-6s-test test/mock/meizu-pro-6s.cc)
TARGET_INCLUDE_DIRECTORIES(meizu-pro-6s-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(meizu-pro-6s-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME meizu-pro-6s-test COMMAND meizu-pro-6s-test)
ADD_EXECUTABLE(meizu-pro-7-plus-test test/mock/meizu-pro-7-plus.cc)
TARGET_INCLUDE_DIRECTORIES(meizu-pro-7-plus-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(meizu-pro-7-plus-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME meizu-pro-7-plus-test COMMAND meizu-pro-7-plus-test)
ADD_EXECUTABLE(nexus5x-test test/mock/nexus5x.cc)
TARGET_INCLUDE_DIRECTORIES(nexus5x-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(nexus5x-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME nexus5x-test COMMAND nexus5x-test)
ADD_EXECUTABLE(nexus6p-test test/mock/nexus6p.cc)
TARGET_INCLUDE_DIRECTORIES(nexus6p-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(nexus6p-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME nexus6p-test COMMAND nexus6p-test)
ADD_EXECUTABLE(nexus9-test test/mock/nexus9.cc)
TARGET_INCLUDE_DIRECTORIES(nexus9-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(nexus9-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME nexus9-test COMMAND nexus9-test)
ADD_EXECUTABLE(oneplus-3t-test test/mock/oneplus-3t.cc)
TARGET_INCLUDE_DIRECTORIES(oneplus-3t-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(oneplus-3t-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME oneplus-3t-test COMMAND oneplus-3t-test)
ADD_EXECUTABLE(oneplus-5-test test/mock/oneplus-5.cc)
TARGET_INCLUDE_DIRECTORIES(oneplus-5-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(oneplus-5-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME oneplus-5-test COMMAND oneplus-5-test)
ADD_EXECUTABLE(oneplus-5t-test test/mock/oneplus-5t.cc)
TARGET_INCLUDE_DIRECTORIES(oneplus-5t-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(oneplus-5t-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME oneplus-5t-test COMMAND oneplus-5t-test)
ADD_EXECUTABLE(oppo-a37-test test/mock/oppo-a37.cc)
TARGET_INCLUDE_DIRECTORIES(oppo-a37-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(oppo-a37-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME oppo-a37-test COMMAND oppo-a37-test)
ADD_EXECUTABLE(oppo-r9-test test/mock/oppo-r9.cc)
TARGET_INCLUDE_DIRECTORIES(oppo-r9-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(oppo-r9-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME oppo-r9-test COMMAND oppo-r9-test)
ADD_EXECUTABLE(oppo-r15-test test/mock/oppo-r15.cc)
TARGET_INCLUDE_DIRECTORIES(oppo-r15-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(oppo-r15-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME oppo-r15-test COMMAND oppo-r15-test)
ADD_EXECUTABLE(pixel-test test/mock/pixel.cc)
TARGET_INCLUDE_DIRECTORIES(pixel-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(pixel-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME pixel-test COMMAND pixel-test)
ADD_EXECUTABLE(pixel-c-test test/mock/pixel-c.cc)
TARGET_INCLUDE_DIRECTORIES(pixel-c-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(pixel-c-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME pixel-c-test COMMAND pixel-c-test)
ADD_EXECUTABLE(pixel-xl-test test/mock/pixel-xl.cc)
TARGET_INCLUDE_DIRECTORIES(pixel-xl-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(pixel-xl-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME pixel-xl-test COMMAND pixel-xl-test)
ADD_EXECUTABLE(pixel-2-xl-test test/mock/pixel-2-xl.cc)
TARGET_INCLUDE_DIRECTORIES(pixel-2-xl-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(pixel-2-xl-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME pixel-2-xl-test COMMAND pixel-2-xl-test)
ADD_EXECUTABLE(xiaomi-mi-5c-test test/mock/xiaomi-mi-5c.cc)
TARGET_INCLUDE_DIRECTORIES(xiaomi-mi-5c-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(xiaomi-mi-5c-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME xiaomi-mi-5c-test COMMAND xiaomi-mi-5c-test)
ADD_EXECUTABLE(xiaomi-redmi-note-3-test test/mock/xiaomi-redmi-note-3.cc)
TARGET_INCLUDE_DIRECTORIES(xiaomi-redmi-note-3-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(xiaomi-redmi-note-3-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME xiaomi-redmi-note-3-test COMMAND xiaomi-redmi-note-3-test)
ADD_EXECUTABLE(xiaomi-redmi-note-4-test test/mock/xiaomi-redmi-note-4.cc)
TARGET_INCLUDE_DIRECTORIES(xiaomi-redmi-note-4-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(xiaomi-redmi-note-4-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME xiaomi-redmi-note-4-test COMMAND xiaomi-redmi-note-4-test)
ADD_EXECUTABLE(xperia-c4-dual-test test/mock/xperia-c4-dual.cc)
TARGET_INCLUDE_DIRECTORIES(xperia-c4-dual-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(xperia-c4-dual-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME xperia-c4-dual-test COMMAND xperia-c4-dual-test)
ENDIF()
IF(CMAKE_SYSTEM_NAME STREQUAL "Android" AND CMAKE_SYSTEM_PROCESSOR MATCHES "^(i686|x86_64)$")
ADD_EXECUTABLE(alldocube-iwork8-test test/mock/alldocube-iwork8.cc)
TARGET_INCLUDE_DIRECTORIES(alldocube-iwork8-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(alldocube-iwork8-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME alldocube-iwork8-test COMMAND alldocube-iwork8-test)
ADD_EXECUTABLE(leagoo-t5c-test test/mock/leagoo-t5c.cc)
TARGET_INCLUDE_DIRECTORIES(leagoo-t5c-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(leagoo-t5c-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME leagoo-t5c-test COMMAND leagoo-t5c-test)
ADD_EXECUTABLE(memo-pad-7-test test/mock/memo-pad-7.cc)
TARGET_INCLUDE_DIRECTORIES(memo-pad-7-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(memo-pad-7-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME memo-pad-7-test COMMAND memo-pad-7-test)
ADD_EXECUTABLE(zenfone-c-test test/mock/zenfone-c.cc)
TARGET_INCLUDE_DIRECTORIES(zenfone-c-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(zenfone-c-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME zenfone-c-test COMMAND zenfone-c-test)
ADD_EXECUTABLE(zenfone-2-test test/mock/zenfone-2.cc)
TARGET_INCLUDE_DIRECTORIES(zenfone-2-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(zenfone-2-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME zenfone-2-test COMMAND zenfone-2-test)
ADD_EXECUTABLE(zenfone-2e-test test/mock/zenfone-2e.cc)
TARGET_INCLUDE_DIRECTORIES(zenfone-2e-test BEFORE PRIVATE test/mock)
TARGET_LINK_LIBRARIES(zenfone-2e-test PRIVATE cpuinfo_mock gtest)
ADD_TEST(NAME zenfone-2e-test COMMAND zenfone-2e-test)
ENDIF()
ENDIF()
# ---[ cpuinfo unit tests
IF(CPUINFO_SUPPORTED_PLATFORM AND CPUINFO_BUILD_UNIT_TESTS)
ADD_EXECUTABLE(init-test test/init.cc)
CPUINFO_TARGET_ENABLE_CXX11(init-test)
CPUINFO_TARGET_RUNTIME_LIBRARY(init-test)
TARGET_LINK_LIBRARIES(init-test PRIVATE cpuinfo gtest gtest_main)
ADD_TEST(NAME init-test COMMAND init-test)
IF(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "Android")
ADD_EXECUTABLE(get-current-test test/get-current.cc)
CPUINFO_TARGET_ENABLE_CXX11(get-current-test)
CPUINFO_TARGET_RUNTIME_LIBRARY(get-current-test)
TARGET_LINK_LIBRARIES(get-current-test PRIVATE cpuinfo gtest gtest_main)
ADD_TEST(NAME get-current-test COMMAND get-current-test)
ENDIF()
IF(CPUINFO_TARGET_PROCESSOR MATCHES "^(i[3-6]86|AMD64|x86(_64)?)$")
ADD_EXECUTABLE(brand-string-test test/name/brand-string.cc)
CPUINFO_TARGET_ENABLE_CXX11(brand-string-test)
CPUINFO_TARGET_RUNTIME_LIBRARY(brand-string-test)
TARGET_LINK_LIBRARIES(brand-string-test PRIVATE cpuinfo_internals gtest gtest_main)
ADD_TEST(NAME brand-string-test COMMAND brand-string-test)
ENDIF()
IF(CMAKE_SYSTEM_NAME STREQUAL "Android" AND CMAKE_SYSTEM_PROCESSOR MATCHES "^(armv[5-8].*|aarch64)$")
ADD_LIBRARY(android_properties_interface STATIC test/name/android-properties-interface.c)
CPUINFO_TARGET_ENABLE_C99(android_properties_interface)
CPUINFO_TARGET_RUNTIME_LIBRARY(android_properties_interface)
TARGET_LINK_LIBRARIES(android_properties_interface PRIVATE cpuinfo_internals)
ADD_EXECUTABLE(chipset-test
test/name/proc-cpuinfo-hardware.cc
test/name/ro-product-board.cc
test/name/ro-board-platform.cc
test/name/ro-mediatek-platform.cc
test/name/ro-arch.cc
test/name/ro-chipname.cc
test/name/android-properties.cc)
CPUINFO_TARGET_ENABLE_CXX11(chipset-test)
CPUINFO_TARGET_RUNTIME_LIBRARY(chipset-test)
TARGET_LINK_LIBRARIES(chipset-test PRIVATE android_properties_interface gtest gtest_main)
ADD_TEST(NAME chipset-test COMMAND chipset-test)
ADD_EXECUTABLE(cache-test test/arm-cache.cc)
CPUINFO_TARGET_ENABLE_CXX11(cache-test)
CPUINFO_TARGET_RUNTIME_LIBRARY(cache-test)
TARGET_COMPILE_DEFINITIONS(cache-test PRIVATE __STDC_LIMIT_MACROS=1 __STDC_CONSTANT_MACROS=1)
TARGET_LINK_LIBRARIES(cache-test PRIVATE cpuinfo_internals gtest gtest_main)
ADD_TEST(NAME cache-test COMMAND cache-test)
ENDIF()
ENDIF()
# ---[ Helper and debug tools
IF(CPUINFO_SUPPORTED_PLATFORM AND CPUINFO_BUILD_TOOLS)
ADD_EXECUTABLE(isa-info tools/isa-info.c)
CPUINFO_TARGET_ENABLE_C99(isa-info)
CPUINFO_TARGET_RUNTIME_LIBRARY(isa-info)
TARGET_LINK_LIBRARIES(isa-info PRIVATE cpuinfo)
INSTALL(TARGETS isa-info RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
ADD_EXECUTABLE(cpu-info tools/cpu-info.c)
CPUINFO_TARGET_ENABLE_C99(cpu-info)
CPUINFO_TARGET_RUNTIME_LIBRARY(cpu-info)
TARGET_LINK_LIBRARIES(cpu-info PRIVATE cpuinfo)
INSTALL(TARGETS cpu-info RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
ADD_EXECUTABLE(cache-info tools/cache-info.c)
CPUINFO_TARGET_ENABLE_C99(cache-info)
CPUINFO_TARGET_RUNTIME_LIBRARY(cache-info)
TARGET_LINK_LIBRARIES(cache-info PRIVATE cpuinfo)
INSTALL(TARGETS cache-info RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
IF(CMAKE_SYSTEM_NAME MATCHES "^(Android|Linux)$" AND CMAKE_SYSTEM_PROCESSOR MATCHES "^(armv[5-8].*|aarch64)$")
ADD_EXECUTABLE(auxv-dump tools/auxv-dump.c)
CPUINFO_TARGET_ENABLE_C99(auxv-dump)
CPUINFO_TARGET_RUNTIME_LIBRARY(auxv-dump)
TARGET_LINK_LIBRARIES(auxv-dump PRIVATE ${CMAKE_DL_LIBS} cpuinfo)
ADD_EXECUTABLE(cpuinfo-dump tools/cpuinfo-dump.c)
CPUINFO_TARGET_ENABLE_C99(cpuinfo-dump)
CPUINFO_TARGET_RUNTIME_LIBRARY(cpuinfo-dump)
ENDIF()
IF(CPUINFO_TARGET_PROCESSOR MATCHES "^(i[3-6]86|AMD64|x86(_64)?)$")
ADD_EXECUTABLE(cpuid-dump tools/cpuid-dump.c)
CPUINFO_TARGET_ENABLE_C99(cpuid-dump)
CPUINFO_TARGET_RUNTIME_LIBRARY(cpuid-dump)
TARGET_INCLUDE_DIRECTORIES(cpuid-dump BEFORE PRIVATE src)
TARGET_INCLUDE_DIRECTORIES(cpuid-dump BEFORE PRIVATE include)
INSTALL(TARGETS cpuid-dump RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
ENDIF()
ENDIF()
# ---[ pkg-config manifest. This is mostly from JsonCpp...
IF(CPUINFO_BUILD_PKG_CONFIG)
FUNCTION(JOIN_PATHS joined_path first_path_segment)
SET(temp_path "${first_path_segment}")
FOREACH(current_segment IN LISTS ARGN)
IF(NOT ("${current_segment}" STREQUAL ""))
IF(IS_ABSOLUTE "${current_segment}")
SET(temp_path "${current_segment}")
ELSE()
SET(temp_path "${temp_path}/${current_segment}")
ENDIF()
ENDIF()
ENDFOREACH()
SET(${joined_path} "${temp_path}" PARENT_SCOPE)
ENDFUNCTION()
JOIN_PATHS(libdir_for_pc_file "\${exec_prefix}" "${CMAKE_INSTALL_LIBDIR}")
JOIN_PATHS(includedir_for_pc_file "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}")
CONFIGURE_FILE(
"libcpuinfo.pc.in"
"libcpuinfo.pc"
@ONLY)
INSTALL(FILES "${CMAKE_CURRENT_BINARY_DIR}/libcpuinfo.pc"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
ENDIF() ENDIF()
TARGET_LINK_LIBRARIES(cpuinfo PRIVATE clog)
TARGET_LINK_LIBRARIES(cpuinfo_internals PRIVATE clog)

View File

@ -48,7 +48,7 @@ Detect if target is a 32-bit or 64-bit ARM system:
#endif #endif
``` ```
Check if the host CPU support ARM NEON Check if the host CPU supports ARM NEON
```c ```c
cpuinfo_initialize(); cpuinfo_initialize();
@ -151,6 +151,36 @@ executable(
) )
``` ```
### Bazel
This project can be built using [Bazel](https://bazel.build/install).
You can also use this library as a dependency to your Bazel project. Add to the `WORKSPACE` file:
```python
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
git_repository(
name = "org_pytorch_cpuinfo",
branch = "master",
remote = "https://github.com/Vertexwahn/cpuinfo.git",
)
```
And to your `BUILD` file:
```python
cc_binary(
name = "cpuinfo_test",
srcs = [
# ...
],
deps = [
"@org_pytorch_cpuinfo//:cpuinfo",
],
)
```
### CMake ### CMake
To use with CMake use the [FindPkgConfig](https://cmake.org/cmake/help/latest/module/FindPkgConfig.html) module. Here is an example: To use with CMake use the [FindPkgConfig](https://cmake.org/cmake/help/latest/module/FindPkgConfig.html) module. Here is an example:
@ -220,12 +250,14 @@ LDFLAGS+= $(pkg-config --libs libcpuinfo)
- [x] x86-64 (iPhone simulator) - [x] x86-64 (iPhone simulator)
- [x] ARMv7 - [x] ARMv7
- [x] ARM64 - [x] ARM64
- [x] OS X - [x] macOS
- [x] x86 - [x] x86
- [x] x86-64 - [x] x86-64
- [x] ARM64 (Apple silicon)
- [x] Windows - [x] Windows
- [x] x86 - [x] x86
- [x] x86-64 - [x] x86-64
- [x] arm64
## Methods ## Methods
@ -234,12 +266,13 @@ LDFLAGS+= $(pkg-config --libs libcpuinfo)
- [x] Using `/proc/cpuinfo` on ARM - [x] Using `/proc/cpuinfo` on ARM
- [x] Using `ro.chipname`, `ro.board.platform`, `ro.product.board`, `ro.mediatek.platform`, `ro.arch` properties (Android) - [x] Using `ro.chipname`, `ro.board.platform`, `ro.product.board`, `ro.mediatek.platform`, `ro.arch` properties (Android)
- [ ] Using kernel log (`dmesg`) on ARM Linux - [ ] Using kernel log (`dmesg`) on ARM Linux
- [x] Using Windows registry on ARM64 Windows
- Vendor and microarchitecture detection - Vendor and microarchitecture detection
- [x] Intel-designed x86/x86-64 cores (up to Sunny Cove, Goldmont Plus, and Knights Mill) - [x] Intel-designed x86/x86-64 cores (up to Sunny Cove, Goldmont Plus, and Knights Mill)
- [x] AMD-designed x86/x86-64 cores (up to Puma/Jaguar and Zen 2) - [x] AMD-designed x86/x86-64 cores (up to Puma/Jaguar and Zen 2)
- [ ] VIA-designed x86/x86-64 cores - [ ] VIA-designed x86/x86-64 cores
- [ ] Other x86 cores (DM&P, RDC, Transmeta, Cyrix, Rise) - [ ] Other x86 cores (DM&P, RDC, Transmeta, Cyrix, Rise)
- [x] ARM-designed ARM cores (up to Cortex-A55, Cortex-A77, and Neoverse E1/N1) - [x] ARM-designed ARM cores (up to Cortex-A55, Cortex-A77, and Neoverse E1/V1/N2/V2)
- [x] Qualcomm-designed ARM cores (Scorpion, Krait, and Kryo) - [x] Qualcomm-designed ARM cores (Scorpion, Krait, and Kryo)
- [x] Nvidia-designed ARM cores (Denver and Carmel) - [x] Nvidia-designed ARM cores (Denver and Carmel)
- [x] Samsung-designed ARM cores (Exynos) - [x] Samsung-designed ARM cores (Exynos)
@ -256,6 +289,7 @@ LDFLAGS+= $(pkg-config --libs libcpuinfo)
- [x] Using `/proc/self/auxv` (Android/ARM) - [x] Using `/proc/self/auxv` (Android/ARM)
- [ ] Using instruction probing on ARM (Linux) - [ ] Using instruction probing on ARM (Linux)
- [ ] Using CPUID registers on ARM64 (Linux) - [ ] Using CPUID registers on ARM64 (Linux)
- [x] Using IsProcessorFeaturePresent on ARM64 Windows
- Cache detection - Cache detection
- [x] Using CPUID leaf 0x00000002 (x86/x86-64) - [x] Using CPUID leaf 0x00000002 (x86/x86-64)
- [x] Using CPUID leaf 0x00000004 (non-AMD x86/x86-64) - [x] Using CPUID leaf 0x00000004 (non-AMD x86/x86-64)
@ -267,6 +301,7 @@ LDFLAGS+= $(pkg-config --libs libcpuinfo)
- [x] Using `sysctlbyname` (Mach) - [x] Using `sysctlbyname` (Mach)
- [x] Using sysfs `typology` directories (ARM/Linux) - [x] Using sysfs `typology` directories (ARM/Linux)
- [ ] Using sysfs `cache` directories (Linux) - [ ] Using sysfs `cache` directories (Linux)
- [x] Using `GetLogicalProcessorInformationEx` on ARM64 Windows
- TLB detection - TLB detection
- [x] Using CPUID leaf 0x00000002 (x86/x86-64) - [x] Using CPUID leaf 0x00000002 (x86/x86-64)
- [ ] Using CPUID leaves 0x80000005-0x80000006 and 0x80000019 (AMD x86/x86-64) - [ ] Using CPUID leaves 0x80000005-0x80000006 and 0x80000019 (AMD x86/x86-64)

View File

@ -0,0 +1,15 @@
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.12 FATAL_ERROR)
PROJECT(googlebenchmark-download NONE)
INCLUDE(ExternalProject)
ExternalProject_Add(googlebenchmark
URL https://github.com/google/benchmark/archive/v1.6.1.zip
URL_HASH SHA256=367e963b8620080aff8c831e24751852cffd1f74ea40f25d9cc1b667a9dd5e45
SOURCE_DIR "${CONFU_DEPENDENCIES_SOURCE_DIR}/googlebenchmark"
BINARY_DIR "${CONFU_DEPENDENCIES_BINARY_DIR}/googlebenchmark"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
)

View File

@ -0,0 +1,15 @@
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.12 FATAL_ERROR)
PROJECT(googletest-download NONE)
INCLUDE(ExternalProject)
ExternalProject_Add(googletest
URL https://github.com/google/googletest/archive/release-1.11.0.zip
URL_HASH SHA256=353571c2440176ded91c2de6d6cd88ddd41401d14692ec1f99e35d013feda55a
SOURCE_DIR "${CONFU_DEPENDENCIES_SOURCE_DIR}/googletest"
BINARY_DIR "${CONFU_DEPENDENCIES_BINARY_DIR}/googletest"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
)

View File

@ -0,0 +1,12 @@
@PACKAGE_INIT@
get_filename_component(_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
file(GLOB CONFIG_FILES "${_DIR}/cpuinfo-config-*.cmake")
foreach(f ${CONFIG_FILES})
include(${f})
endforeach()
# ${_DIR}/cpuinfo-targets-*.cmake will be included here
include("${_DIR}/cpuinfo-targets.cmake")
check_required_components(@PROJECT_NAME@)

View File

@ -30,36 +30,85 @@
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="src\arm\cache.c">
<ExcludedFromBuild Condition="'$(Platform)'!='ARM64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="src\arm\uarch.c">
<ExcludedFromBuild Condition="'$(Platform)'!='ARM64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="src\arm\windows\init-by-logical-sys-info.c">
<ExcludedFromBuild Condition="'$(Platform)'!='ARM64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="src\arm\windows\init.c">
<ExcludedFromBuild Condition="'$(Platform)'!='ARM64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="deps\clog\src\clog.c" /> <ClCompile Include="deps\clog\src\clog.c" />
<ClCompile Include="src\api.c" /> <ClCompile Include="src\api.c" />
<ClCompile Include="src\cache.c" /> <ClCompile Include="src\cache.c" />
<ClCompile Include="src\init.c" /> <ClCompile Include="src\init.c" />
<ClCompile Include="src\x86\cache\descriptor.c" /> <ClCompile Include="src\x86\cache\descriptor.c">
<ClCompile Include="src\x86\cache\deterministic.c" /> <ExcludedFromBuild Condition="'$(Platform)'!='x64'">true</ExcludedFromBuild>
<ClCompile Include="src\x86\cache\init.c" /> </ClCompile>
<ClCompile Include="src\x86\info.c" /> <ClCompile Include="src\x86\cache\deterministic.c">
<ClCompile Include="src\x86\init.c" /> <ExcludedFromBuild Condition="'$(Platform)'!='x64'">true</ExcludedFromBuild>
<ClCompile Include="src\x86\isa.c" /> </ClCompile>
<ClCompile Include="src\x86\name.c" /> <ClCompile Include="src\x86\cache\init.c">
<ClCompile Include="src\x86\topology.c" /> <ExcludedFromBuild Condition="'$(Platform)'!='x64'">true</ExcludedFromBuild>
<ClCompile Include="src\x86\uarch.c" /> </ClCompile>
<ClCompile Include="src\x86\vendor.c" /> <ClCompile Include="src\x86\info.c">
<ClCompile Include="src\x86\windows\init.c" /> <ExcludedFromBuild Condition="'$(Platform)'!='x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="src\x86\init.c">
<ExcludedFromBuild Condition="'$(Platform)'!='x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="src\x86\isa.c">
<ExcludedFromBuild Condition="'$(Platform)'!='x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="src\x86\name.c">
<ExcludedFromBuild Condition="'$(Platform)'!='x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="src\x86\topology.c">
<ExcludedFromBuild Condition="'$(Platform)'!='x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="src\x86\uarch.c">
<ExcludedFromBuild Condition="'$(Platform)'!='x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="src\x86\vendor.c">
<ExcludedFromBuild Condition="'$(Platform)'!='x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="src\x86\windows\init.c">
<ExcludedFromBuild Condition="'$(Platform)'!='x64'">true</ExcludedFromBuild>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="src\arm\api.h">
<ExcludedFromBuild Condition="'$(Platform)'!='ARM64'">true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="src\arm\midr.h">
<ExcludedFromBuild Condition="'$(Platform)'!='ARM64'">true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="src\arm\windows\windows-arm-init.h">
<ExcludedFromBuild Condition="'$(Platform)'!='ARM64'">true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="deps\clog\include\clog.h" /> <ClInclude Include="deps\clog\include\clog.h" />
<ClInclude Include="include\cpuinfo.h" /> <ClInclude Include="include\cpuinfo.h" />
<ClInclude Include="src\cpuinfo\common.h" /> <ClInclude Include="src\cpuinfo\common.h" />
<ClInclude Include="src\cpuinfo\internal-api.h" /> <ClInclude Include="src\cpuinfo\internal-api.h" />
<ClInclude Include="src\cpuinfo\log.h" /> <ClInclude Include="src\cpuinfo\log.h" />
<ClInclude Include="src\cpuinfo\utils.h" /> <ClInclude Include="src\cpuinfo\utils.h" />
<ClInclude Include="src\x86\api.h" /> <ClInclude Include="src\x86\api.h">
<ClInclude Include="src\x86\cpuid.h" /> <ExcludedFromBuild Condition="'$(Platform)'!='x64'">true</ExcludedFromBuild>
<ClInclude Include="src\x86\windows\api.h" /> </ClInclude>
<ClInclude Include="src\x86\cpuid.h">
<ExcludedFromBuild Condition="'$(Platform)'!='x64'">true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="src\x86\windows\api.h">
<ExcludedFromBuild Condition="'$(Platform)'!='x64'">true</ExcludedFromBuild>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemDefinitionGroup> <ItemDefinitionGroup>
<ClCompile> <ClCompile>
<PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>CPUINFO_LOG_LEVEL=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<WarningLevel>TurnOffAllWarnings</WarningLevel> <WarningLevel>TurnOffAllWarnings</WarningLevel>
<AdditionalIncludeDirectories>$(ProjectDir)include;$(ProjectDir)src;$(ProjectDir)deps\clog\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>$(ProjectDir)include;$(ProjectDir)src;$(ProjectDir)deps\clog\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<ObjectFileName>$(IntDir)%(RelativeDir)</ObjectFileName> <ObjectFileName>$(IntDir)%(RelativeDir)</ObjectFileName>

View File

@ -16,6 +16,12 @@
<Filter Include="clog"> <Filter Include="clog">
<UniqueIdentifier>{7f0aba4c-ca06-4a7b-aed1-4f1e6976e839}</UniqueIdentifier> <UniqueIdentifier>{7f0aba4c-ca06-4a7b-aed1-4f1e6976e839}</UniqueIdentifier>
</Filter> </Filter>
<Filter Include="arm">
<UniqueIdentifier>{ac4549d3-f60f-4e60-bf43-86d1c253cf3f}</UniqueIdentifier>
</Filter>
<Filter Include="arm\windows">
<UniqueIdentifier>{41fcb23a-e77b-4b5c-8238-e9b92bf1f3c6}</UniqueIdentifier>
</Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="src\x86\isa.c"> <ClCompile Include="src\x86\isa.c">
@ -57,6 +63,18 @@
<ClCompile Include="deps\clog\src\clog.c"> <ClCompile Include="deps\clog\src\clog.c">
<Filter>clog</Filter> <Filter>clog</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="src\arm\cache.c">
<Filter>arm</Filter>
</ClCompile>
<ClCompile Include="src\arm\uarch.c">
<Filter>arm</Filter>
</ClCompile>
<ClCompile Include="src\arm\windows\init.c">
<Filter>arm\windows</Filter>
</ClCompile>
<ClCompile Include="src\arm\windows\init-by-logical-sys-info.c">
<Filter>arm\windows</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="src\x86\api.h"> <ClInclude Include="src\x86\api.h">
@ -84,5 +102,14 @@
<ClInclude Include="deps\clog\include\clog.h"> <ClInclude Include="deps\clog\include\clog.h">
<Filter>clog</Filter> <Filter>clog</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="src\arm\api.h">
<Filter>arm</Filter>
</ClInclude>
<ClInclude Include="src\arm\midr.h">
<Filter>arm</Filter>
</ClInclude>
<ClInclude Include="src\arm\windows\windows-arm-init.h">
<Filter>arm\windows</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,5 +1,7 @@
CMAKE_MINIMUM_REQUIRED(VERSION 3.1 FATAL_ERROR) CMAKE_MINIMUM_REQUIRED(VERSION 3.1 FATAL_ERROR)
INCLUDE(GNUInstallDirs)
# ---[ Project and semantic versioning. # ---[ Project and semantic versioning.
PROJECT(clog C CXX) PROJECT(clog C CXX)
@ -11,6 +13,14 @@ IF(ANDROID)
ELSE() ELSE()
OPTION(CLOG_LOG_TO_STDIO "Log errors, warnings, and information to stdout/stderr" ON) OPTION(CLOG_LOG_TO_STDIO "Log errors, warnings, and information to stdout/stderr" ON)
ENDIF() ENDIF()
OPTION(CLOG_BUILD_TESTS "Build clog tests" ON)
OPTION(USE_SYSTEM_LIBS "Use system libraries instead of downloading and building them" OFF)
OPTION(USE_SYSTEM_GOOGLETEST "Use system Google Test library instead of downloading and building it" ${USE_SYSTEM_LIBS})
# ---[ CMake options
IF(CLOG_BUILD_TESTS)
ENABLE_TESTING()
ENDIF()
MACRO(CLOG_TARGET_RUNTIME_LIBRARY target) MACRO(CLOG_TARGET_RUNTIME_LIBRARY target)
IF(MSVC AND NOT CLOG_RUNTIME_TYPE STREQUAL "default") IF(MSVC AND NOT CLOG_RUNTIME_TYPE STREQUAL "default")
@ -24,6 +34,26 @@ MACRO(CLOG_TARGET_RUNTIME_LIBRARY target)
ENDIF() ENDIF()
ENDMACRO() ENDMACRO()
# ---[ Download deps
SET(CONFU_DEPENDENCIES_SOURCE_DIR ${CMAKE_SOURCE_DIR}/deps
CACHE PATH "Confu-style dependencies source directory")
SET(CONFU_DEPENDENCIES_BINARY_DIR ${CMAKE_BINARY_DIR}/deps
CACHE PATH "Confu-style dependencies binary directory")
IF(CLOG_BUILD_TESTS)
IF(USE_SYSTEM_GOOGLETEST)
FIND_PACKAGE(GTest REQUIRED)
ELSEIF(NOT DEFINED GOOGLETEST_SOURCE_DIR)
MESSAGE(STATUS "Downloading Google Test to ${CONFU_DEPENDENCIES_SOURCE_DIR}/googletest (define GOOGLETEST_SOURCE_DIR to avoid it)")
CONFIGURE_FILE(cmake/DownloadGoogleTest.cmake "${CONFU_DEPENDENCIES_BINARY_DIR}/googletest-download/CMakeLists.txt")
EXECUTE_PROCESS(COMMAND "${CMAKE_COMMAND}" -G "${CMAKE_GENERATOR}" .
WORKING_DIRECTORY "${CONFU_DEPENDENCIES_BINARY_DIR}/googletest-download")
EXECUTE_PROCESS(COMMAND "${CMAKE_COMMAND}" --build .
WORKING_DIRECTORY "${CONFU_DEPENDENCIES_BINARY_DIR}/googletest-download")
SET(GOOGLETEST_SOURCE_DIR "${CONFU_DEPENDENCIES_SOURCE_DIR}/googletest" CACHE STRING "Google Test source directory")
ENDIF()
ENDIF()
# ---[ clog library # ---[ clog library
ADD_LIBRARY(clog STATIC src/clog.c) ADD_LIBRARY(clog STATIC src/clog.c)
SET_TARGET_PROPERTIES(clog PROPERTIES SET_TARGET_PROPERTIES(clog PROPERTIES
@ -31,7 +61,7 @@ SET_TARGET_PROPERTIES(clog PROPERTIES
C_EXTENSIONS NO) C_EXTENSIONS NO)
CLOG_TARGET_RUNTIME_LIBRARY(clog) CLOG_TARGET_RUNTIME_LIBRARY(clog)
SET_TARGET_PROPERTIES(clog PROPERTIES PUBLIC_HEADER include/clog.h) SET_TARGET_PROPERTIES(clog PROPERTIES PUBLIC_HEADER include/clog.h)
TARGET_INCLUDE_DIRECTORIES(clog BEFORE PUBLIC include) TARGET_INCLUDE_DIRECTORIES(clog PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
IF(CLOG_LOG_TO_STDIO) IF(CLOG_LOG_TO_STDIO)
TARGET_COMPILE_DEFINITIONS(clog PRIVATE CLOG_LOG_TO_STDIO=1) TARGET_COMPILE_DEFINITIONS(clog PRIVATE CLOG_LOG_TO_STDIO=1)
ELSE() ELSE()
@ -40,3 +70,32 @@ ENDIF()
IF(ANDROID AND NOT CLOG_LOG_TO_STDIO) IF(ANDROID AND NOT CLOG_LOG_TO_STDIO)
TARGET_LINK_LIBRARIES(clog PRIVATE log) TARGET_LINK_LIBRARIES(clog PRIVATE log)
ENDIF() ENDIF()
ADD_LIBRARY(cpuinfo::clog ALIAS clog)
INSTALL(TARGETS clog
EXPORT cpuinfo-targets
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
# ---[ clog tests
IF(CLOG_BUILD_TESTS)
# ---[ Build google test
IF(NOT TARGET gtest AND NOT USE_SYSTEM_GOOGLETEST)
IF(MSVC AND NOT CLOG_RUNTIME_TYPE STREQUAL "static")
SET(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
ENDIF()
ADD_SUBDIRECTORY(
"${GOOGLETEST_SOURCE_DIR}"
"${CONFU_DEPENDENCIES_BINARY_DIR}/googletest")
ENDIF()
ADD_EXECUTABLE(clog-test test/clog.cc)
SET_TARGET_PROPERTIES(clog-test PROPERTIES
CXX_STANDARD 11
CXX_EXTENSIONS NO)
CLOG_TARGET_RUNTIME_LIBRARY(clog-test)
TARGET_LINK_LIBRARIES(clog-test PRIVATE clog gtest gtest_main)
ADD_TEST(clog-test clog-test)
ENDIF()

View File

@ -10,6 +10,9 @@
#ifdef __ANDROID__ #ifdef __ANDROID__
#include <android/log.h> #include <android/log.h>
#endif #endif
#ifdef __hexagon__
#include <qurt_printf.h>
#endif
#ifndef CLOG_LOG_TO_STDIO #ifndef CLOG_LOG_TO_STDIO
#ifdef __ANDROID__ #ifdef __ANDROID__
@ -102,12 +105,14 @@ void clog_vlog_fatal(const char* module, const char* format, va_list args) {
out_buffer = heap_buffer; out_buffer = heap_buffer;
} }
out_buffer[prefix_chars + format_chars] = '\n'; out_buffer[prefix_chars + format_chars] = '\n';
#ifdef _WIN32 #if defined(_WIN32)
DWORD bytes_written; DWORD bytes_written;
WriteFile( WriteFile(
GetStdHandle(STD_ERROR_HANDLE), GetStdHandle(STD_ERROR_HANDLE),
out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH, out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH,
&bytes_written, NULL); &bytes_written, NULL);
#elif defined(__hexagon__)
qurt_printf("%s", out_buffer);
#else #else
write(STDERR_FILENO, out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH); write(STDERR_FILENO, out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH);
#endif #endif
@ -178,12 +183,14 @@ void clog_vlog_error(const char* module, const char* format, va_list args) {
out_buffer = heap_buffer; out_buffer = heap_buffer;
} }
out_buffer[prefix_chars + format_chars] = '\n'; out_buffer[prefix_chars + format_chars] = '\n';
#ifdef _WIN32 #if defined(_WIN32)
DWORD bytes_written; DWORD bytes_written;
WriteFile( WriteFile(
GetStdHandle(STD_ERROR_HANDLE), GetStdHandle(STD_ERROR_HANDLE),
out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH, out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH,
&bytes_written, NULL); &bytes_written, NULL);
#elif defined(__hexagon__)
qurt_printf("%s", out_buffer);
#else #else
write(STDERR_FILENO, out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH); write(STDERR_FILENO, out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH);
#endif #endif
@ -254,12 +261,14 @@ void clog_vlog_warning(const char* module, const char* format, va_list args) {
out_buffer = heap_buffer; out_buffer = heap_buffer;
} }
out_buffer[prefix_chars + format_chars] = '\n'; out_buffer[prefix_chars + format_chars] = '\n';
#ifdef _WIN32 #if defined(_WIN32)
DWORD bytes_written; DWORD bytes_written;
WriteFile( WriteFile(
GetStdHandle(STD_ERROR_HANDLE), GetStdHandle(STD_ERROR_HANDLE),
out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH, out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH,
&bytes_written, NULL); &bytes_written, NULL);
#elif defined(__hexagon__)
qurt_printf("%s", out_buffer);
#else #else
write(STDERR_FILENO, out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH); write(STDERR_FILENO, out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH);
#endif #endif
@ -330,12 +339,14 @@ void clog_vlog_info(const char* module, const char* format, va_list args) {
out_buffer = heap_buffer; out_buffer = heap_buffer;
} }
out_buffer[prefix_chars + format_chars] = '\n'; out_buffer[prefix_chars + format_chars] = '\n';
#ifdef _WIN32 #if defined(_WIN32)
DWORD bytes_written; DWORD bytes_written;
WriteFile( WriteFile(
GetStdHandle(STD_OUTPUT_HANDLE), GetStdHandle(STD_OUTPUT_HANDLE),
out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH, out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH,
&bytes_written, NULL); &bytes_written, NULL);
#elif defined(__hexagon__)
qurt_printf("%s", out_buffer);
#else #else
write(STDOUT_FILENO, out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH); write(STDOUT_FILENO, out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH);
#endif #endif
@ -406,12 +417,14 @@ void clog_vlog_debug(const char* module, const char* format, va_list args) {
out_buffer = heap_buffer; out_buffer = heap_buffer;
} }
out_buffer[prefix_chars + format_chars] = '\n'; out_buffer[prefix_chars + format_chars] = '\n';
#ifdef _WIN32 #if defined(_WIN32)
DWORD bytes_written; DWORD bytes_written;
WriteFile( WriteFile(
GetStdHandle(STD_OUTPUT_HANDLE), GetStdHandle(STD_OUTPUT_HANDLE),
out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH, out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH,
&bytes_written, NULL); &bytes_written, NULL);
#elif defined(__hexagon__)
qurt_printf("%s", out_buffer);
#else #else
write(STDOUT_FILENO, out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH); write(STDOUT_FILENO, out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH);
#endif #endif

View File

@ -7,37 +7,35 @@
#include <cpuinfo.h> #include <cpuinfo.h>
#if defined(__linux__) #if defined(__linux__)
#include <sys/types.h> #include <sys/types.h>
#endif #endif
#if !defined(CPUINFO_MOCK) || !(CPUINFO_MOCK) #if !defined(CPUINFO_MOCK) || !(CPUINFO_MOCK)
#error This header is intended only for test use #error This header is intended only for test use
#endif #endif
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
#if CPUINFO_ARCH_ARM #if CPUINFO_ARCH_ARM
void CPUINFO_ABI cpuinfo_set_fpsid(uint32_t fpsid); void CPUINFO_ABI cpuinfo_set_fpsid(uint32_t fpsid);
void CPUINFO_ABI cpuinfo_set_wcid(uint32_t wcid); void CPUINFO_ABI cpuinfo_set_wcid(uint32_t wcid);
#endif /* CPUINFO_ARCH_ARM */ #endif /* CPUINFO_ARCH_ARM */
#if CPUINFO_ARCH_X86 || CPUINFO_ARCH_X86_64 #if CPUINFO_ARCH_X86 || CPUINFO_ARCH_X86_64
struct cpuinfo_mock_cpuid { struct cpuinfo_mock_cpuid {
uint32_t input_eax; uint32_t input_eax;
uint32_t input_ecx; uint32_t input_ecx;
uint32_t eax; uint32_t eax;
uint32_t ebx; uint32_t ebx;
uint32_t ecx; uint32_t ecx;
uint32_t edx; uint32_t edx;
}; };
void CPUINFO_ABI cpuinfo_mock_set_cpuid(struct cpuinfo_mock_cpuid* dump, size_t entries); void CPUINFO_ABI cpuinfo_mock_set_cpuid(struct cpuinfo_mock_cpuid* dump, size_t entries);
void CPUINFO_ABI cpuinfo_mock_get_cpuid(uint32_t eax, uint32_t regs[4]); void CPUINFO_ABI cpuinfo_mock_get_cpuid(uint32_t eax, uint32_t regs[4]);
void CPUINFO_ABI cpuinfo_mock_get_cpuidex(uint32_t eax, uint32_t ecx, uint32_t regs[4]); void CPUINFO_ABI cpuinfo_mock_get_cpuidex(uint32_t eax, uint32_t ecx, uint32_t regs[4]);
#endif /* CPUINFO_ARCH_X86 || CPUINFO_ARCH_X86_64 */ #endif /* CPUINFO_ARCH_X86 || CPUINFO_ARCH_X86_64 */
struct cpuinfo_mock_file { struct cpuinfo_mock_file {
@ -53,22 +51,22 @@ struct cpuinfo_mock_property {
}; };
#if defined(__linux__) #if defined(__linux__)
void CPUINFO_ABI cpuinfo_mock_filesystem(struct cpuinfo_mock_file* files); void CPUINFO_ABI cpuinfo_mock_filesystem(struct cpuinfo_mock_file* files);
int CPUINFO_ABI cpuinfo_mock_open(const char* path, int oflag); int CPUINFO_ABI cpuinfo_mock_open(const char* path, int oflag);
int CPUINFO_ABI cpuinfo_mock_close(int fd); int CPUINFO_ABI cpuinfo_mock_close(int fd);
ssize_t CPUINFO_ABI cpuinfo_mock_read(int fd, void* buffer, size_t capacity); ssize_t CPUINFO_ABI cpuinfo_mock_read(int fd, void* buffer, size_t capacity);
#if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 #if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64
void CPUINFO_ABI cpuinfo_set_hwcap(uint32_t hwcap); void CPUINFO_ABI cpuinfo_set_hwcap(uint32_t hwcap);
#endif #endif
#if CPUINFO_ARCH_ARM #if CPUINFO_ARCH_ARM
void CPUINFO_ABI cpuinfo_set_hwcap2(uint32_t hwcap2); void CPUINFO_ABI cpuinfo_set_hwcap2(uint64_t hwcap2);
#endif #endif
#endif #endif
#if defined(__ANDROID__) #if defined(__ANDROID__)
void CPUINFO_ABI cpuinfo_mock_android_properties(struct cpuinfo_mock_property* properties); void CPUINFO_ABI cpuinfo_mock_android_properties(struct cpuinfo_mock_property* properties);
void CPUINFO_ABI cpuinfo_mock_gl_renderer(const char* renderer); void CPUINFO_ABI cpuinfo_mock_gl_renderer(const char* renderer);
#endif #endif
#ifdef __cplusplus #ifdef __cplusplus

File diff suppressed because it is too large Load Diff

View File

@ -6,13 +6,13 @@
#include <cpuinfo/log.h> #include <cpuinfo/log.h>
#ifdef __linux__ #ifdef __linux__
#include <linux/api.h> #include <linux/api.h>
#include <unistd.h> #include <sys/syscall.h>
#include <sys/syscall.h> #include <unistd.h>
#if !defined(__NR_getcpu) #if !defined(__NR_getcpu)
#include <asm-generic/unistd.h> #include <asm-generic/unistd.h>
#endif #endif
#endif #endif
bool cpuinfo_is_initialized = false; bool cpuinfo_is_initialized = false;
@ -21,55 +21,54 @@ struct cpuinfo_processor* cpuinfo_processors = NULL;
struct cpuinfo_core* cpuinfo_cores = NULL; struct cpuinfo_core* cpuinfo_cores = NULL;
struct cpuinfo_cluster* cpuinfo_clusters = NULL; struct cpuinfo_cluster* cpuinfo_clusters = NULL;
struct cpuinfo_package* cpuinfo_packages = NULL; struct cpuinfo_package* cpuinfo_packages = NULL;
struct cpuinfo_cache* cpuinfo_cache[cpuinfo_cache_level_max] = { NULL }; struct cpuinfo_cache* cpuinfo_cache[cpuinfo_cache_level_max] = {NULL};
uint32_t cpuinfo_processors_count = 0; uint32_t cpuinfo_processors_count = 0;
uint32_t cpuinfo_cores_count = 0; uint32_t cpuinfo_cores_count = 0;
uint32_t cpuinfo_clusters_count = 0; uint32_t cpuinfo_clusters_count = 0;
uint32_t cpuinfo_packages_count = 0; uint32_t cpuinfo_packages_count = 0;
uint32_t cpuinfo_cache_count[cpuinfo_cache_level_max] = { 0 }; uint32_t cpuinfo_cache_count[cpuinfo_cache_level_max] = {0};
uint32_t cpuinfo_max_cache_size = 0; uint32_t cpuinfo_max_cache_size = 0;
#if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 #if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 || CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64
struct cpuinfo_uarch_info* cpuinfo_uarchs = NULL; struct cpuinfo_uarch_info* cpuinfo_uarchs = NULL;
uint32_t cpuinfo_uarchs_count = 0; uint32_t cpuinfo_uarchs_count = 0;
#else #else
struct cpuinfo_uarch_info cpuinfo_global_uarch = { cpuinfo_uarch_unknown }; struct cpuinfo_uarch_info cpuinfo_global_uarch = {cpuinfo_uarch_unknown};
#endif #endif
#ifdef __linux__ #ifdef __linux__
uint32_t cpuinfo_linux_cpu_max = 0; uint32_t cpuinfo_linux_cpu_max = 0;
const struct cpuinfo_processor** cpuinfo_linux_cpu_to_processor_map = NULL; const struct cpuinfo_processor** cpuinfo_linux_cpu_to_processor_map = NULL;
const struct cpuinfo_core** cpuinfo_linux_cpu_to_core_map = NULL; const struct cpuinfo_core** cpuinfo_linux_cpu_to_core_map = NULL;
#if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 #if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 || CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64
const uint32_t* cpuinfo_linux_cpu_to_uarch_index_map = NULL; const uint32_t* cpuinfo_linux_cpu_to_uarch_index_map = NULL;
#endif #endif
#endif #endif
const struct cpuinfo_processor* cpuinfo_get_processors(void) { const struct cpuinfo_processor* cpuinfo_get_processors(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "processors"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "processors");
} }
return cpuinfo_processors; return cpuinfo_processors;
} }
const struct cpuinfo_core* cpuinfo_get_cores(void) { const struct cpuinfo_core* cpuinfo_get_cores(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "core"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "core");
} }
return cpuinfo_cores; return cpuinfo_cores;
} }
const struct cpuinfo_cluster* cpuinfo_get_clusters(void) { const struct cpuinfo_cluster* cpuinfo_get_clusters(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "clusters"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "clusters");
} }
return cpuinfo_clusters; return cpuinfo_clusters;
} }
const struct cpuinfo_package* cpuinfo_get_packages(void) { const struct cpuinfo_package* cpuinfo_get_packages(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "packages"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "packages");
} }
return cpuinfo_packages; return cpuinfo_packages;
@ -79,48 +78,48 @@ const struct cpuinfo_uarch_info* cpuinfo_get_uarchs() {
if (!cpuinfo_is_initialized) { if (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "uarchs"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "uarchs");
} }
#if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 #if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 || CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64
return cpuinfo_uarchs; return cpuinfo_uarchs;
#else #else
return &cpuinfo_global_uarch; return &cpuinfo_global_uarch;
#endif #endif
} }
const struct cpuinfo_processor* cpuinfo_get_processor(uint32_t index) { const struct cpuinfo_processor* cpuinfo_get_processor(uint32_t index) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "processor"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "processor");
} }
if CPUINFO_UNLIKELY(index >= cpuinfo_processors_count) { if CPUINFO_UNLIKELY (index >= cpuinfo_processors_count) {
return NULL; return NULL;
} }
return &cpuinfo_processors[index]; return &cpuinfo_processors[index];
} }
const struct cpuinfo_core* cpuinfo_get_core(uint32_t index) { const struct cpuinfo_core* cpuinfo_get_core(uint32_t index) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "core"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "core");
} }
if CPUINFO_UNLIKELY(index >= cpuinfo_cores_count) { if CPUINFO_UNLIKELY (index >= cpuinfo_cores_count) {
return NULL; return NULL;
} }
return &cpuinfo_cores[index]; return &cpuinfo_cores[index];
} }
const struct cpuinfo_cluster* cpuinfo_get_cluster(uint32_t index) { const struct cpuinfo_cluster* cpuinfo_get_cluster(uint32_t index) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "cluster"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "cluster");
} }
if CPUINFO_UNLIKELY(index >= cpuinfo_clusters_count) { if CPUINFO_UNLIKELY (index >= cpuinfo_clusters_count) {
return NULL; return NULL;
} }
return &cpuinfo_clusters[index]; return &cpuinfo_clusters[index];
} }
const struct cpuinfo_package* cpuinfo_get_package(uint32_t index) { const struct cpuinfo_package* cpuinfo_get_package(uint32_t index) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "package"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "package");
} }
if CPUINFO_UNLIKELY(index >= cpuinfo_packages_count) { if CPUINFO_UNLIKELY (index >= cpuinfo_packages_count) {
return NULL; return NULL;
} }
return &cpuinfo_packages[index]; return &cpuinfo_packages[index];
@ -130,42 +129,42 @@ const struct cpuinfo_uarch_info* cpuinfo_get_uarch(uint32_t index) {
if (!cpuinfo_is_initialized) { if (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "uarch"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "uarch");
} }
#if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 #if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 || CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64
if CPUINFO_UNLIKELY(index >= cpuinfo_uarchs_count) { if CPUINFO_UNLIKELY (index >= cpuinfo_uarchs_count) {
return NULL; return NULL;
} }
return &cpuinfo_uarchs[index]; return &cpuinfo_uarchs[index];
#else #else
if CPUINFO_UNLIKELY(index != 0) { if CPUINFO_UNLIKELY (index != 0) {
return NULL; return NULL;
} }
return &cpuinfo_global_uarch; return &cpuinfo_global_uarch;
#endif #endif
} }
uint32_t cpuinfo_get_processors_count(void) { uint32_t cpuinfo_get_processors_count(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "processors_count"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "processors_count");
} }
return cpuinfo_processors_count; return cpuinfo_processors_count;
} }
uint32_t cpuinfo_get_cores_count(void) { uint32_t cpuinfo_get_cores_count(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "cores_count"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "cores_count");
} }
return cpuinfo_cores_count; return cpuinfo_cores_count;
} }
uint32_t cpuinfo_get_clusters_count(void) { uint32_t cpuinfo_get_clusters_count(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "clusters_count"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "clusters_count");
} }
return cpuinfo_clusters_count; return cpuinfo_clusters_count;
} }
uint32_t cpuinfo_get_packages_count(void) { uint32_t cpuinfo_get_packages_count(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "packages_count"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "packages_count");
} }
return cpuinfo_packages_count; return cpuinfo_packages_count;
@ -175,236 +174,243 @@ uint32_t cpuinfo_get_uarchs_count(void) {
if (!cpuinfo_is_initialized) { if (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "uarchs_count"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "uarchs_count");
} }
#if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 #if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 || CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64
return cpuinfo_uarchs_count; return cpuinfo_uarchs_count;
#else #else
return 1; return 1;
#endif #endif
} }
const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l1i_caches(void) { const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l1i_caches(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l1i_caches"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l1i_caches");
} }
return cpuinfo_cache[cpuinfo_cache_level_1i]; return cpuinfo_cache[cpuinfo_cache_level_1i];
} }
const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l1d_caches(void) { const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l1d_caches(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l1d_caches"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l1d_caches");
} }
return cpuinfo_cache[cpuinfo_cache_level_1d]; return cpuinfo_cache[cpuinfo_cache_level_1d];
} }
const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l2_caches(void) { const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l2_caches(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l2_caches"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l2_caches");
} }
return cpuinfo_cache[cpuinfo_cache_level_2]; return cpuinfo_cache[cpuinfo_cache_level_2];
} }
const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l3_caches(void) { const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l3_caches(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l3_caches"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l3_caches");
} }
return cpuinfo_cache[cpuinfo_cache_level_3]; return cpuinfo_cache[cpuinfo_cache_level_3];
} }
const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l4_caches(void) { const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l4_caches(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l4_caches"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l4_caches");
} }
return cpuinfo_cache[cpuinfo_cache_level_4]; return cpuinfo_cache[cpuinfo_cache_level_4];
} }
const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l1i_cache(uint32_t index) { const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l1i_cache(uint32_t index) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l1i_cache"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l1i_cache");
} }
if CPUINFO_UNLIKELY(index >= cpuinfo_cache_count[cpuinfo_cache_level_1i]) { if CPUINFO_UNLIKELY (index >= cpuinfo_cache_count[cpuinfo_cache_level_1i]) {
return NULL; return NULL;
} }
return &cpuinfo_cache[cpuinfo_cache_level_1i][index]; return &cpuinfo_cache[cpuinfo_cache_level_1i][index];
} }
const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l1d_cache(uint32_t index) { const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l1d_cache(uint32_t index) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l1d_cache"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l1d_cache");
} }
if CPUINFO_UNLIKELY(index >= cpuinfo_cache_count[cpuinfo_cache_level_1d]) { if CPUINFO_UNLIKELY (index >= cpuinfo_cache_count[cpuinfo_cache_level_1d]) {
return NULL; return NULL;
} }
return &cpuinfo_cache[cpuinfo_cache_level_1d][index]; return &cpuinfo_cache[cpuinfo_cache_level_1d][index];
} }
const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l2_cache(uint32_t index) { const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l2_cache(uint32_t index) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l2_cache"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l2_cache");
} }
if CPUINFO_UNLIKELY(index >= cpuinfo_cache_count[cpuinfo_cache_level_2]) { if CPUINFO_UNLIKELY (index >= cpuinfo_cache_count[cpuinfo_cache_level_2]) {
return NULL; return NULL;
} }
return &cpuinfo_cache[cpuinfo_cache_level_2][index]; return &cpuinfo_cache[cpuinfo_cache_level_2][index];
} }
const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l3_cache(uint32_t index) { const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l3_cache(uint32_t index) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l3_cache"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l3_cache");
} }
if CPUINFO_UNLIKELY(index >= cpuinfo_cache_count[cpuinfo_cache_level_3]) { if CPUINFO_UNLIKELY (index >= cpuinfo_cache_count[cpuinfo_cache_level_3]) {
return NULL; return NULL;
} }
return &cpuinfo_cache[cpuinfo_cache_level_3][index]; return &cpuinfo_cache[cpuinfo_cache_level_3][index];
} }
const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l4_cache(uint32_t index) { const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l4_cache(uint32_t index) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l4_cache"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l4_cache");
} }
if CPUINFO_UNLIKELY(index >= cpuinfo_cache_count[cpuinfo_cache_level_4]) { if CPUINFO_UNLIKELY (index >= cpuinfo_cache_count[cpuinfo_cache_level_4]) {
return NULL; return NULL;
} }
return &cpuinfo_cache[cpuinfo_cache_level_4][index]; return &cpuinfo_cache[cpuinfo_cache_level_4][index];
} }
uint32_t CPUINFO_ABI cpuinfo_get_l1i_caches_count(void) { uint32_t CPUINFO_ABI cpuinfo_get_l1i_caches_count(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l1i_caches_count"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l1i_caches_count");
} }
return cpuinfo_cache_count[cpuinfo_cache_level_1i]; return cpuinfo_cache_count[cpuinfo_cache_level_1i];
} }
uint32_t CPUINFO_ABI cpuinfo_get_l1d_caches_count(void) { uint32_t CPUINFO_ABI cpuinfo_get_l1d_caches_count(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l1d_caches_count"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l1d_caches_count");
} }
return cpuinfo_cache_count[cpuinfo_cache_level_1d]; return cpuinfo_cache_count[cpuinfo_cache_level_1d];
} }
uint32_t CPUINFO_ABI cpuinfo_get_l2_caches_count(void) { uint32_t CPUINFO_ABI cpuinfo_get_l2_caches_count(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l2_caches_count"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l2_caches_count");
} }
return cpuinfo_cache_count[cpuinfo_cache_level_2]; return cpuinfo_cache_count[cpuinfo_cache_level_2];
} }
uint32_t CPUINFO_ABI cpuinfo_get_l3_caches_count(void) { uint32_t CPUINFO_ABI cpuinfo_get_l3_caches_count(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l3_caches_count"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l3_caches_count");
} }
return cpuinfo_cache_count[cpuinfo_cache_level_3]; return cpuinfo_cache_count[cpuinfo_cache_level_3];
} }
uint32_t CPUINFO_ABI cpuinfo_get_l4_caches_count(void) { uint32_t CPUINFO_ABI cpuinfo_get_l4_caches_count(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l4_caches_count"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l4_caches_count");
} }
return cpuinfo_cache_count[cpuinfo_cache_level_4]; return cpuinfo_cache_count[cpuinfo_cache_level_4];
} }
uint32_t CPUINFO_ABI cpuinfo_get_max_cache_size(void) { uint32_t CPUINFO_ABI cpuinfo_get_max_cache_size(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "max_cache_size"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "max_cache_size");
} }
return cpuinfo_max_cache_size; return cpuinfo_max_cache_size;
} }
const struct cpuinfo_processor* CPUINFO_ABI cpuinfo_get_current_processor(void) { const struct cpuinfo_processor* CPUINFO_ABI cpuinfo_get_current_processor(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "current_processor"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "current_processor");
} }
#ifdef __linux__ #ifdef __linux__
/* Initializing this variable silences a MemorySanitizer error. */ /* Initializing this variable silences a MemorySanitizer error. */
unsigned cpu = 0; unsigned cpu = 0;
if CPUINFO_UNLIKELY(syscall(__NR_getcpu, &cpu, NULL, NULL) != 0) { if CPUINFO_UNLIKELY (syscall(__NR_getcpu, &cpu, NULL, NULL) != 0) {
return 0; return 0;
} }
if CPUINFO_UNLIKELY((uint32_t) cpu >= cpuinfo_linux_cpu_max) { if CPUINFO_UNLIKELY ((uint32_t)cpu >= cpuinfo_linux_cpu_max) {
return 0; return 0;
} }
return cpuinfo_linux_cpu_to_processor_map[cpu]; return cpuinfo_linux_cpu_to_processor_map[cpu];
#else #else
return NULL; return NULL;
#endif #endif
} }
const struct cpuinfo_core* CPUINFO_ABI cpuinfo_get_current_core(void) { const struct cpuinfo_core* CPUINFO_ABI cpuinfo_get_current_core(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "current_core"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "current_core");
} }
#ifdef __linux__ #ifdef __linux__
/* Initializing this variable silences a MemorySanitizer error. */ /* Initializing this variable silences a MemorySanitizer error. */
unsigned cpu = 0; unsigned cpu = 0;
if CPUINFO_UNLIKELY(syscall(__NR_getcpu, &cpu, NULL, NULL) != 0) { if CPUINFO_UNLIKELY (syscall(__NR_getcpu, &cpu, NULL, NULL) != 0) {
return 0; return 0;
} }
if CPUINFO_UNLIKELY((uint32_t) cpu >= cpuinfo_linux_cpu_max) { if CPUINFO_UNLIKELY ((uint32_t)cpu >= cpuinfo_linux_cpu_max) {
return 0; return 0;
} }
return cpuinfo_linux_cpu_to_core_map[cpu]; return cpuinfo_linux_cpu_to_core_map[cpu];
#else #else
return NULL; return NULL;
#endif #endif
} }
uint32_t CPUINFO_ABI cpuinfo_get_current_uarch_index(void) { uint32_t CPUINFO_ABI cpuinfo_get_current_uarch_index(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "current_uarch_index"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "current_uarch_index");
} }
#if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 #if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 || CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64
#ifdef __linux__ #ifdef __linux__
if (cpuinfo_linux_cpu_to_uarch_index_map == NULL) { if (cpuinfo_linux_cpu_to_uarch_index_map == NULL) {
/* Special case: avoid syscall on systems with only a single type of cores */ /* Special case: avoid syscall on systems with only a single
return 0; * type of cores
} */
/* General case */
/* Initializing this variable silences a MemorySanitizer error. */
unsigned cpu = 0;
if CPUINFO_UNLIKELY(syscall(__NR_getcpu, &cpu, NULL, NULL) != 0) {
return 0;
}
if CPUINFO_UNLIKELY((uint32_t) cpu >= cpuinfo_linux_cpu_max) {
return 0;
}
return cpuinfo_linux_cpu_to_uarch_index_map[cpu];
#else
/* Fallback: pretend to be on the big core. */
return 0;
#endif
#else
/* Only ARM/ARM64 processors may include cores of different types in the same package. */
return 0; return 0;
#endif }
/* General case */
/* Initializing this variable silences a MemorySanitizer error. */
unsigned cpu = 0;
if CPUINFO_UNLIKELY (syscall(__NR_getcpu, &cpu, NULL, NULL) != 0) {
return 0;
}
if CPUINFO_UNLIKELY ((uint32_t)cpu >= cpuinfo_linux_cpu_max) {
return 0;
}
return cpuinfo_linux_cpu_to_uarch_index_map[cpu];
#else
/* Fallback: pretend to be on the big core. */
return 0;
#endif
#else
/* Only ARM/ARM64/RISCV processors may include cores of different types
* in the same package. */
return 0;
#endif
} }
uint32_t CPUINFO_ABI cpuinfo_get_current_uarch_index_with_default(uint32_t default_uarch_index) { uint32_t CPUINFO_ABI cpuinfo_get_current_uarch_index_with_default(uint32_t default_uarch_index) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "current_uarch_index_with_default"); cpuinfo_log_fatal(
"cpuinfo_get_%s called before cpuinfo is initialized", "current_uarch_index_with_default");
} }
#if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 #if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 || CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64
#ifdef __linux__ #ifdef __linux__
if (cpuinfo_linux_cpu_to_uarch_index_map == NULL) { if (cpuinfo_linux_cpu_to_uarch_index_map == NULL) {
/* Special case: avoid syscall on systems with only a single type of cores */ /* Special case: avoid syscall on systems with only a single
return 0; * type of cores
} */
/* General case */
/* Initializing this variable silences a MemorySanitizer error. */
unsigned cpu = 0;
if CPUINFO_UNLIKELY(syscall(__NR_getcpu, &cpu, NULL, NULL) != 0) {
return default_uarch_index;
}
if CPUINFO_UNLIKELY((uint32_t) cpu >= cpuinfo_linux_cpu_max) {
return default_uarch_index;
}
return cpuinfo_linux_cpu_to_uarch_index_map[cpu];
#else
/* Fallback: no API to query current core, use default uarch index. */
return default_uarch_index;
#endif
#else
/* Only ARM/ARM64 processors may include cores of different types in the same package. */
return 0; return 0;
#endif }
/* General case */
/* Initializing this variable silences a MemorySanitizer error. */
unsigned cpu = 0;
if CPUINFO_UNLIKELY (syscall(__NR_getcpu, &cpu, NULL, NULL) != 0) {
return default_uarch_index;
}
if CPUINFO_UNLIKELY ((uint32_t)cpu >= cpuinfo_linux_cpu_max) {
return default_uarch_index;
}
return cpuinfo_linux_cpu_to_uarch_index_map[cpu];
#else
/* Fallback: no API to query current core, use default uarch index. */
return default_uarch_index;
#endif
#else
/* Only ARM/ARM64/RISCV processors may include cores of different types
* in the same package. */
return 0;
#endif
} }

View File

@ -1,9 +1,9 @@
#pragma once #pragma once
#include <cpuinfo.h>
#include <cpuinfo/common.h>
#include <arm/api.h> #include <arm/api.h>
#include <arm/linux/api.h> #include <arm/linux/api.h>
#include <cpuinfo.h>
#include <cpuinfo/common.h>
enum cpuinfo_android_chipset_property { enum cpuinfo_android_chipset_property {
cpuinfo_android_chipset_property_proc_cpuinfo_hardware = 0, cpuinfo_android_chipset_property_proc_cpuinfo_hardware = 0,

View File

@ -1,42 +1,42 @@
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <stddef.h>
#include <string.h> #include <string.h>
#include <sys/system_properties.h> #include <sys/system_properties.h>
#include <linux/api.h>
#include <arm/android/api.h> #include <arm/android/api.h>
#include <arm/linux/api.h> #include <arm/linux/api.h>
#include <cpuinfo/log.h> #include <cpuinfo/log.h>
#include <linux/api.h>
#if CPUINFO_MOCK #if CPUINFO_MOCK
#include <cpuinfo-mock.h> #include <cpuinfo-mock.h>
static struct cpuinfo_mock_property* cpuinfo_mock_properties = NULL; static struct cpuinfo_mock_property* cpuinfo_mock_properties = NULL;
void CPUINFO_ABI cpuinfo_mock_android_properties(struct cpuinfo_mock_property* properties) { void CPUINFO_ABI cpuinfo_mock_android_properties(struct cpuinfo_mock_property* properties) {
cpuinfo_log_info("Android properties mocking enabled"); cpuinfo_log_info("Android properties mocking enabled");
cpuinfo_mock_properties = properties; cpuinfo_mock_properties = properties;
} }
static int cpuinfo_android_property_get(const char* key, char* value) { static int cpuinfo_android_property_get(const char* key, char* value) {
if (cpuinfo_mock_properties != NULL) { if (cpuinfo_mock_properties != NULL) {
for (const struct cpuinfo_mock_property* prop = cpuinfo_mock_properties; prop->key != NULL; prop++) { for (const struct cpuinfo_mock_property* prop = cpuinfo_mock_properties; prop->key != NULL; prop++) {
if (strncmp(key, prop->key, CPUINFO_BUILD_PROP_NAME_MAX) == 0) { if (strncmp(key, prop->key, CPUINFO_BUILD_PROP_NAME_MAX) == 0) {
strncpy(value, prop->value, CPUINFO_BUILD_PROP_VALUE_MAX); strncpy(value, prop->value, CPUINFO_BUILD_PROP_VALUE_MAX);
return (int) strnlen(prop->value, CPUINFO_BUILD_PROP_VALUE_MAX); return (int)strnlen(prop->value, CPUINFO_BUILD_PROP_VALUE_MAX);
}
} }
} }
*value = '\0';
return 0;
} }
*value = '\0';
return 0;
}
#else #else
static inline int cpuinfo_android_property_get(const char* key, char* value) { static inline int cpuinfo_android_property_get(const char* key, char* value) {
return __system_property_get(key, value); return __system_property_get(key, value);
} }
#endif #endif
void cpuinfo_arm_android_parse_properties(struct cpuinfo_android_properties properties[restrict static 1]) { void cpuinfo_arm_android_parse_properties(struct cpuinfo_android_properties properties[restrict static 1]) {
@ -50,18 +50,17 @@ void cpuinfo_arm_android_parse_properties(struct cpuinfo_android_properties prop
const int ro_mediatek_platform_length = const int ro_mediatek_platform_length =
cpuinfo_android_property_get("ro.mediatek.platform", properties->ro_mediatek_platform); cpuinfo_android_property_get("ro.mediatek.platform", properties->ro_mediatek_platform);
cpuinfo_log_debug("read ro.mediatek.platform = \"%.*s\"", cpuinfo_log_debug(
ro_mediatek_platform_length, properties->ro_mediatek_platform); "read ro.mediatek.platform = \"%.*s\"", ro_mediatek_platform_length, properties->ro_mediatek_platform);
const int ro_arch_length = const int ro_arch_length = cpuinfo_android_property_get("ro.arch", properties->ro_arch);
cpuinfo_android_property_get("ro.arch", properties->ro_arch);
cpuinfo_log_debug("read ro.arch = \"%.*s\"", ro_arch_length, properties->ro_arch); cpuinfo_log_debug("read ro.arch = \"%.*s\"", ro_arch_length, properties->ro_arch);
const int ro_chipname_length = const int ro_chipname_length = cpuinfo_android_property_get("ro.chipname", properties->ro_chipname);
cpuinfo_android_property_get("ro.chipname", properties->ro_chipname);
cpuinfo_log_debug("read ro.chipname = \"%.*s\"", ro_chipname_length, properties->ro_chipname); cpuinfo_log_debug("read ro.chipname = \"%.*s\"", ro_chipname_length, properties->ro_chipname);
const int ro_hardware_chipname_length = const int ro_hardware_chipname_length =
cpuinfo_android_property_get("ro.hardware.chipname", properties->ro_hardware_chipname); cpuinfo_android_property_get("ro.hardware.chipname", properties->ro_hardware_chipname);
cpuinfo_log_debug("read ro.hardware.chipname = \"%.*s\"", ro_hardware_chipname_length, properties->ro_hardware_chipname); cpuinfo_log_debug(
"read ro.hardware.chipname = \"%.*s\"", ro_hardware_chipname_length, properties->ro_hardware_chipname);
} }

View File

@ -28,6 +28,7 @@ enum cpuinfo_arm_chipset_vendor {
cpuinfo_arm_chipset_vendor_spreadtrum, cpuinfo_arm_chipset_vendor_spreadtrum,
cpuinfo_arm_chipset_vendor_telechips, cpuinfo_arm_chipset_vendor_telechips,
cpuinfo_arm_chipset_vendor_texas_instruments, cpuinfo_arm_chipset_vendor_texas_instruments,
cpuinfo_arm_chipset_vendor_unisoc,
cpuinfo_arm_chipset_vendor_wondermedia, cpuinfo_arm_chipset_vendor_wondermedia,
cpuinfo_arm_chipset_vendor_max, cpuinfo_arm_chipset_vendor_max,
}; };
@ -62,6 +63,8 @@ enum cpuinfo_arm_chipset_series {
cpuinfo_arm_chipset_series_spreadtrum_sc, cpuinfo_arm_chipset_series_spreadtrum_sc,
cpuinfo_arm_chipset_series_telechips_tcc, cpuinfo_arm_chipset_series_telechips_tcc,
cpuinfo_arm_chipset_series_texas_instruments_omap, cpuinfo_arm_chipset_series_texas_instruments_omap,
cpuinfo_arm_chipset_series_unisoc_t,
cpuinfo_arm_chipset_series_unisoc_ums,
cpuinfo_arm_chipset_series_wondermedia_wm, cpuinfo_arm_chipset_series_wondermedia_wm,
cpuinfo_arm_chipset_series_max, cpuinfo_arm_chipset_series_max,
}; };
@ -78,45 +81,47 @@ struct cpuinfo_arm_chipset {
#define CPUINFO_ARM_CHIPSET_NAME_MAX CPUINFO_PACKAGE_NAME_MAX #define CPUINFO_ARM_CHIPSET_NAME_MAX CPUINFO_PACKAGE_NAME_MAX
#ifndef __cplusplus #ifndef __cplusplus
CPUINFO_INTERNAL void cpuinfo_arm_chipset_to_string( CPUINFO_INTERNAL void cpuinfo_arm_chipset_to_string(
const struct cpuinfo_arm_chipset chipset[restrict static 1], const struct cpuinfo_arm_chipset chipset[restrict static 1],
char name[restrict static CPUINFO_ARM_CHIPSET_NAME_MAX]); char name[restrict static CPUINFO_ARM_CHIPSET_NAME_MAX]);
CPUINFO_INTERNAL void cpuinfo_arm_fixup_chipset( CPUINFO_INTERNAL void cpuinfo_arm_fixup_chipset(
struct cpuinfo_arm_chipset chipset[restrict static 1], uint32_t cores, uint32_t max_cpu_freq_max); struct cpuinfo_arm_chipset chipset[restrict static 1],
uint32_t cores,
uint32_t max_cpu_freq_max);
CPUINFO_INTERNAL void cpuinfo_arm_decode_vendor_uarch( CPUINFO_INTERNAL void cpuinfo_arm_decode_vendor_uarch(
uint32_t midr, uint32_t midr,
#if CPUINFO_ARCH_ARM #if CPUINFO_ARCH_ARM
bool has_vfpv4, bool has_vfpv4,
#endif #endif
enum cpuinfo_vendor vendor[restrict static 1], enum cpuinfo_vendor vendor[restrict static 1],
enum cpuinfo_uarch uarch[restrict static 1]); enum cpuinfo_uarch uarch[restrict static 1]);
CPUINFO_INTERNAL void cpuinfo_arm_decode_cache( CPUINFO_INTERNAL void cpuinfo_arm_decode_cache(
enum cpuinfo_uarch uarch, enum cpuinfo_uarch uarch,
uint32_t cluster_cores, uint32_t cluster_cores,
uint32_t midr, uint32_t midr,
const struct cpuinfo_arm_chipset chipset[restrict static 1], const struct cpuinfo_arm_chipset chipset[restrict static 1],
uint32_t cluster_id, uint32_t cluster_id,
uint32_t arch_version, uint32_t arch_version,
struct cpuinfo_cache l1i[restrict static 1], struct cpuinfo_cache l1i[restrict static 1],
struct cpuinfo_cache l1d[restrict static 1], struct cpuinfo_cache l1d[restrict static 1],
struct cpuinfo_cache l2[restrict static 1], struct cpuinfo_cache l2[restrict static 1],
struct cpuinfo_cache l3[restrict static 1]); struct cpuinfo_cache l3[restrict static 1]);
CPUINFO_INTERNAL uint32_t cpuinfo_arm_compute_max_cache_size( CPUINFO_INTERNAL uint32_t
const struct cpuinfo_processor processor[restrict static 1]); cpuinfo_arm_compute_max_cache_size(const struct cpuinfo_processor processor[restrict static 1]);
#else /* defined(__cplusplus) */ #else /* defined(__cplusplus) */
CPUINFO_INTERNAL void cpuinfo_arm_decode_cache( CPUINFO_INTERNAL void cpuinfo_arm_decode_cache(
enum cpuinfo_uarch uarch, enum cpuinfo_uarch uarch,
uint32_t cluster_cores, uint32_t cluster_cores,
uint32_t midr, uint32_t midr,
const struct cpuinfo_arm_chipset chipset[1], const struct cpuinfo_arm_chipset chipset[1],
uint32_t cluster_id, uint32_t cluster_id,
uint32_t arch_version, uint32_t arch_version,
struct cpuinfo_cache l1i[1], struct cpuinfo_cache l1i[1],
struct cpuinfo_cache l1d[1], struct cpuinfo_cache l1d[1],
struct cpuinfo_cache l2[1], struct cpuinfo_cache l2[1],
struct cpuinfo_cache l3[1]); struct cpuinfo_cache l3[1]);
#endif #endif

File diff suppressed because it is too large Load Diff

View File

@ -1,50 +1,55 @@
#include <stdint.h> #include <stdint.h>
#if CPUINFO_MOCK #if CPUINFO_MOCK
#include <cpuinfo-mock.h> #include <cpuinfo-mock.h>
#endif #endif
#include <arm/linux/api.h> #include <arm/linux/api.h>
#include <arm/linux/cp.h> #include <arm/linux/cp.h>
#include <arm/midr.h> #include <arm/midr.h>
#include <cpuinfo/log.h> #include <cpuinfo/log.h>
#if CPUINFO_MOCK #if CPUINFO_MOCK
uint32_t cpuinfo_arm_fpsid = 0; uint32_t cpuinfo_arm_fpsid = 0;
uint32_t cpuinfo_arm_mvfr0 = 0; uint32_t cpuinfo_arm_mvfr0 = 0;
uint32_t cpuinfo_arm_wcid = 0; uint32_t cpuinfo_arm_wcid = 0;
void cpuinfo_set_fpsid(uint32_t fpsid) { void cpuinfo_set_fpsid(uint32_t fpsid) {
cpuinfo_arm_fpsid = fpsid; cpuinfo_arm_fpsid = fpsid;
} }
void cpuinfo_set_wcid(uint32_t wcid) { void cpuinfo_set_wcid(uint32_t wcid) {
cpuinfo_arm_wcid = wcid; cpuinfo_arm_wcid = wcid;
} }
#endif #endif
void cpuinfo_arm_linux_decode_isa_from_proc_cpuinfo( void cpuinfo_arm_linux_decode_isa_from_proc_cpuinfo(
uint32_t features, uint32_t features,
uint32_t features2, uint64_t features2,
uint32_t midr, uint32_t midr,
uint32_t architecture_version, uint32_t architecture_version,
uint32_t architecture_flags, uint32_t architecture_flags,
const struct cpuinfo_arm_chipset chipset[restrict static 1], const struct cpuinfo_arm_chipset chipset[restrict static 1],
struct cpuinfo_arm_isa isa[restrict static 1]) struct cpuinfo_arm_isa isa[restrict static 1]) {
{ if (architecture_version < 8) {
const uint32_t armv8_features2_mask = CPUINFO_ARM_LINUX_FEATURE2_AES |
CPUINFO_ARM_LINUX_FEATURE2_PMULL | CPUINFO_ARM_LINUX_FEATURE2_SHA1 |
CPUINFO_ARM_LINUX_FEATURE2_SHA2 | CPUINFO_ARM_LINUX_FEATURE2_CRC32;
if (features2 & armv8_features2_mask) {
architecture_version = 8;
}
}
if (architecture_version >= 8) { if (architecture_version >= 8) {
/* /*
* ARMv7 code running on ARMv8: IDIV, VFP, NEON are always supported, * ARMv7 code running on ARMv8: IDIV, VFP, NEON are always
* but may be not reported in /proc/cpuinfo features. * supported, but may be not reported in /proc/cpuinfo features.
*/ */
isa->armv5e = true; isa->armv5e = true;
isa->armv6 = true; isa->armv6 = true;
isa->armv6k = true; isa->armv6k = true;
isa->armv7 = true; isa->armv7 = true;
isa->armv7mp = true; isa->armv7mp = true;
isa->armv8 = true; isa->armv8 = true;
isa->thumb = true; isa->thumb = true;
isa->thumb2 = true; isa->thumb2 = true;
isa->idiv = true; isa->idiv = true;
isa->vfpv3 = true; isa->vfpv3 = true;
@ -54,33 +59,61 @@ void cpuinfo_arm_linux_decode_isa_from_proc_cpuinfo(
isa->neon = true; isa->neon = true;
/* /*
* NEON FP16 compute extension and VQRDMLAH/VQRDMLSH instructions are not indicated in /proc/cpuinfo. * NEON FP16 compute extension and VQRDMLAH/VQRDMLSH
* Use a MIDR-based heuristic to whitelist processors known to support it: * instructions are not indicated in /proc/cpuinfo. Use a
* MIDR-based heuristic to whitelist processors known to support
* it:
* - Processors with Cortex-A55 cores * - Processors with Cortex-A55 cores
* - Processors with Cortex-A65 cores
* - Processors with Cortex-A75 cores * - Processors with Cortex-A75 cores
* - Processors with Cortex-A76 cores * - Processors with Cortex-A76 cores
* - Processors with Cortex-A77 cores * - Processors with Cortex-A77 cores
* - Processors with Cortex-A78 cores
* - Processors with Cortex-A510 cores
* - Processors with Cortex-A710 cores
* - Processors with Cortex-A715 cores
* - Processors with Cortex-X1 cores
* - Processors with Cortex-X2 cores
* - Processors with Cortex-X3 cores
* - Processors with Exynos M4 cores * - Processors with Exynos M4 cores
* - Processors with Exynos M5 cores * - Processors with Exynos M5 cores
* - Neoverse N1 cores * - Neoverse N1 cores
* - Neoverse N2 cores
* - Neoverse V1 cores
* - Neoverse V2 cores
*/ */
if (chipset->series == cpuinfo_arm_chipset_series_samsung_exynos && chipset->model == 9810) { if (chipset->series == cpuinfo_arm_chipset_series_samsung_exynos && chipset->model == 9810) {
/* Only little cores of Exynos 9810 support FP16 & RDM */ /* Only little cores of Exynos 9810 support FP16 & RDM
cpuinfo_log_warning("FP16 arithmetics and RDM disabled: only little cores in Exynos 9810 support these extensions"); */
cpuinfo_log_warning(
"FP16 arithmetics and RDM disabled: only little cores in Exynos 9810 support these extensions");
} else { } else {
switch (midr & (CPUINFO_ARM_MIDR_IMPLEMENTER_MASK | CPUINFO_ARM_MIDR_PART_MASK)) { switch (midr & (CPUINFO_ARM_MIDR_IMPLEMENTER_MASK | CPUINFO_ARM_MIDR_PART_MASK)) {
case UINT32_C(0x4100D050): /* Cortex-A55 */ case UINT32_C(0x4100D050): /* Cortex-A55 */
case UINT32_C(0x4100D060): /* Cortex-A65 */ case UINT32_C(0x4100D0A0): /* Cortex-A75 */
case UINT32_C(0x4100D0B0): /* Cortex-A76 */ case UINT32_C(0x4100D0B0): /* Cortex-A76 */
case UINT32_C(0x4100D0C0): /* Neoverse N1 */ case UINT32_C(0x4100D0C0): /* Neoverse N1 */
case UINT32_C(0x4100D0D0): /* Cortex-A77 */ case UINT32_C(0x4100D0D0): /* Cortex-A77 */
case UINT32_C(0x4100D0E0): /* Cortex-A76AE */ case UINT32_C(0x4100D0E0): /* Cortex-A76AE */
case UINT32_C(0x4800D400): /* Cortex-A76 (HiSilicon) */ case UINT32_C(0x4100D400): /* Neoverse V1 */
case UINT32_C(0x51008020): /* Kryo 385 Gold (Cortex-A75) */ case UINT32_C(0x4100D410): /* Cortex-A78 */
case UINT32_C(0x51008030): /* Kryo 385 Silver (Cortex-A55) */ case UINT32_C(0x4100D440): /* Cortex-X1 */
case UINT32_C(0x51008040): /* Kryo 485 Gold (Cortex-A76) */ case UINT32_C(0x4100D460): /* Cortex-A510 */
case UINT32_C(0x51008050): /* Kryo 485 Silver (Cortex-A55) */ case UINT32_C(0x4100D470): /* Cortex-A710 */
case UINT32_C(0x4100D480): /* Cortex-X2 */
case UINT32_C(0x4100D490): /* Neoverse N2 */
case UINT32_C(0x4100D4D0): /* Cortex-A715 */
case UINT32_C(0x4100D4E0): /* Cortex-X3 */
case UINT32_C(0x4100D4F0): /* Neoverse V2 */
case UINT32_C(0x4800D400): /* Cortex-A76
(HiSilicon) */
case UINT32_C(0x51008020): /* Kryo 385 Gold
(Cortex-A75) */
case UINT32_C(0x51008030): /* Kryo 385 Silver
(Cortex-A55) */
case UINT32_C(0x51008040): /* Kryo 485 Gold
(Cortex-A76) */
case UINT32_C(0x51008050): /* Kryo 485 Silver
(Cortex-A55) */
case UINT32_C(0x53000030): /* Exynos M4 */ case UINT32_C(0x53000030): /* Exynos M4 */
case UINT32_C(0x53000040): /* Exynos M5 */ case UINT32_C(0x53000040): /* Exynos M5 */
isa->fp16arith = true; isa->fp16arith = true;
@ -91,46 +124,93 @@ void cpuinfo_arm_linux_decode_isa_from_proc_cpuinfo(
/* /*
* NEON VDOT instructions are not indicated in /proc/cpuinfo. * NEON VDOT instructions are not indicated in /proc/cpuinfo.
* Use a MIDR-based heuristic to whitelist processors known to support it. * Use a MIDR-based heuristic to whitelist processors known to
* support it:
* - Processors with Cortex-A76 cores
* - Processors with Cortex-A77 cores
* - Processors with Cortex-A78 cores
* - Processors with Cortex-A510 cores
* - Processors with Cortex-A710 cores
* - Processors with Cortex-A715 cores
* - Processors with Cortex-X1 cores
* - Processors with Cortex-X2 cores
* - Processors with Cortex-X3 cores
* - Processors with Exynos M4 cores
* - Processors with Exynos M5 cores
* - Neoverse N1 cores
* - Neoverse N2 cores
* - Neoverse V1 cores
* - Neoverse V2 cores
*/ */
switch (midr & (CPUINFO_ARM_MIDR_IMPLEMENTER_MASK | CPUINFO_ARM_MIDR_PART_MASK)) { if (chipset->series == cpuinfo_arm_chipset_series_spreadtrum_sc && chipset->model == 9863) {
case UINT32_C(0x4100D0B0): /* Cortex-A76 */ cpuinfo_log_warning(
case UINT32_C(0x4100D0D0): /* Cortex-A77 */ "VDOT instructions disabled: cause occasional SIGILL on Spreadtrum SC9863A");
case UINT32_C(0x4100D0E0): /* Cortex-A76AE */ } else if (chipset->series == cpuinfo_arm_chipset_series_unisoc_t && chipset->model == 310) {
case UINT32_C(0x4800D400): /* Cortex-A76 (HiSilicon) */ cpuinfo_log_warning("VDOT instructions disabled: cause occasional SIGILL on Unisoc T310");
case UINT32_C(0x51008040): /* Kryo 485 Gold (Cortex-A76) */ } else if (chipset->series == cpuinfo_arm_chipset_series_unisoc_ums && chipset->model == 312) {
case UINT32_C(0x51008050): /* Kryo 485 Silver (Cortex-A55) */ cpuinfo_log_warning("VDOT instructions disabled: cause occasional SIGILL on Unisoc UMS312");
case UINT32_C(0x53000030): /* Exynos-M4 */ } else {
case UINT32_C(0x53000040): /* Exynos-M5 */ switch (midr & (CPUINFO_ARM_MIDR_IMPLEMENTER_MASK | CPUINFO_ARM_MIDR_PART_MASK)) {
isa->dot = true; case UINT32_C(0x4100D0B0): /* Cortex-A76 */
break; case UINT32_C(0x4100D0C0): /* Neoverse N1 */
case UINT32_C(0x4100D050): /* Cortex A55: revision 1 or later only */ case UINT32_C(0x4100D0D0): /* Cortex-A77 */
isa->dot = !!(midr_get_variant(midr) >= 1); case UINT32_C(0x4100D0E0): /* Cortex-A76AE */
break; case UINT32_C(0x4100D400): /* Neoverse V1 */
case UINT32_C(0x4100D0A0): /* Cortex A75: revision 2 or later only */ case UINT32_C(0x4100D410): /* Cortex-A78 */
isa->dot = !!(midr_get_variant(midr) >= 2); case UINT32_C(0x4100D440): /* Cortex-X1 */
break; case UINT32_C(0x4100D460): /* Cortex-A510 */
case UINT32_C(0x4100D470): /* Cortex-A710 */
case UINT32_C(0x4100D480): /* Cortex-X2 */
case UINT32_C(0x4100D490): /* Neoverse N2 */
case UINT32_C(0x4100D4D0): /* Cortex-A715 */
case UINT32_C(0x4100D4E0): /* Cortex-X3 */
case UINT32_C(0x4100D4F0): /* Neoverse V2 */
case UINT32_C(0x4800D400): /* Cortex-A76
(HiSilicon) */
case UINT32_C(0x51008040): /* Kryo 485 Gold
(Cortex-A76) */
case UINT32_C(0x51008050): /* Kryo 485 Silver
(Cortex-A55) */
case UINT32_C(0x53000030): /* Exynos M4 */
case UINT32_C(0x53000040): /* Exynos M5 */
isa->dot = true;
break;
case UINT32_C(0x4100D050): /* Cortex A55: revision 1
or later only */
isa->dot = !!(midr_get_variant(midr) >= 1);
break;
case UINT32_C(0x4100D0A0): /* Cortex A75: revision 2
or later only */
isa->dot = !!(midr_get_variant(midr) >= 2);
break;
}
} }
} else { } else {
/* ARMv7 or lower: use feature flags to detect optional features */ /* ARMv7 or lower: use feature flags to detect optional features
*/
/* /*
* ARM11 (ARM 1136/1156/1176/11 MPCore) processors can report v7 architecture * ARM11 (ARM 1136/1156/1176/11 MPCore) processors can report v7
* even though they support only ARMv6 instruction set. * architecture even though they support only ARMv6 instruction
* set.
*/ */
if (architecture_version == 7 && midr_is_arm11(midr)) { if (architecture_version == 7 && midr_is_arm11(midr)) {
cpuinfo_log_warning("kernel-reported architecture ARMv7 ignored due to mismatch with processor microarchitecture (ARM11)"); cpuinfo_log_warning(
"kernel-reported architecture ARMv7 ignored due to mismatch with processor microarchitecture (ARM11)");
architecture_version = 6; architecture_version = 6;
} }
if (architecture_version < 7) { if (architecture_version < 7) {
const uint32_t armv7_features_mask = CPUINFO_ARM_LINUX_FEATURE_VFPV3 | CPUINFO_ARM_LINUX_FEATURE_VFPV3D16 | CPUINFO_ARM_LINUX_FEATURE_VFPD32 | const uint32_t armv7_features_mask = CPUINFO_ARM_LINUX_FEATURE_VFPV3 |
CPUINFO_ARM_LINUX_FEATURE_VFPV4 | CPUINFO_ARM_LINUX_FEATURE_NEON | CPUINFO_ARM_LINUX_FEATURE_IDIVT | CPUINFO_ARM_LINUX_FEATURE_IDIVA; CPUINFO_ARM_LINUX_FEATURE_VFPV3D16 | CPUINFO_ARM_LINUX_FEATURE_VFPD32 |
CPUINFO_ARM_LINUX_FEATURE_VFPV4 | CPUINFO_ARM_LINUX_FEATURE_NEON |
CPUINFO_ARM_LINUX_FEATURE_IDIVT | CPUINFO_ARM_LINUX_FEATURE_IDIVA;
if (features & armv7_features_mask) { if (features & armv7_features_mask) {
architecture_version = 7; architecture_version = 7;
} }
} }
if ((architecture_version >= 6) || (features & CPUINFO_ARM_LINUX_FEATURE_EDSP) || (architecture_flags & CPUINFO_ARM_LINUX_ARCH_E)) { if ((architecture_version >= 6) || (features & CPUINFO_ARM_LINUX_FEATURE_EDSP) ||
(architecture_flags & CPUINFO_ARM_LINUX_ARCH_E)) {
isa->armv5e = true; isa->armv5e = true;
} }
if (architecture_version >= 6) { if (architecture_version >= 6) {
@ -141,13 +221,16 @@ void cpuinfo_arm_linux_decode_isa_from_proc_cpuinfo(
isa->armv7 = true; isa->armv7 = true;
/* /*
* ARMv7 MP extension (PLDW instruction) is not indicated in /proc/cpuinfo. * ARMv7 MP extension (PLDW instruction) is not
* Use heuristic list of supporting processors: * indicated in /proc/cpuinfo. Use heuristic list of
* - Processors supporting UDIV/SDIV instructions ("idiva" + "idivt" features in /proc/cpuinfo) * supporting processors:
* - Processors supporting UDIV/SDIV instructions
* ("idiva" + "idivt" features in /proc/cpuinfo)
* - Cortex-A5 * - Cortex-A5
* - Cortex-A9 * - Cortex-A9
* - Dual-Core Scorpion * - Dual-Core Scorpion
* - Krait (supports UDIV/SDIV, but kernels may not report it in /proc/cpuinfo) * - Krait (supports UDIV/SDIV, but kernels may not
* report it in /proc/cpuinfo)
* *
* TODO: check single-core Qualcomm Scorpion. * TODO: check single-core Qualcomm Scorpion.
*/ */
@ -160,15 +243,18 @@ void cpuinfo_arm_linux_decode_isa_from_proc_cpuinfo(
isa->armv7mp = true; isa->armv7mp = true;
break; break;
default: default:
/* In practice IDIV instruction implies ARMv7+MP ISA */ /* In practice IDIV instruction implies
isa->armv7mp = (features & CPUINFO_ARM_LINUX_FEATURE_IDIV) == CPUINFO_ARM_LINUX_FEATURE_IDIV; * ARMv7+MP ISA */
isa->armv7mp = (features & CPUINFO_ARM_LINUX_FEATURE_IDIV) ==
CPUINFO_ARM_LINUX_FEATURE_IDIV;
break; break;
} }
} }
if (features & CPUINFO_ARM_LINUX_FEATURE_IWMMXT) { if (features & CPUINFO_ARM_LINUX_FEATURE_IWMMXT) {
#if !defined(__ARM_ARCH_8A__) && !(defined(__ARM_ARCH) && (__ARM_ARCH >= 8))
const uint32_t wcid = read_wcid(); const uint32_t wcid = read_wcid();
cpuinfo_log_debug("WCID = 0x%08"PRIx32, wcid); cpuinfo_log_debug("WCID = 0x%08" PRIx32, wcid);
const uint32_t coprocessor_type = (wcid >> 8) & UINT32_C(0xFF); const uint32_t coprocessor_type = (wcid >> 8) & UINT32_C(0xFF);
if (coprocessor_type >= 0x10) { if (coprocessor_type >= 0x10) {
isa->wmmx = true; isa->wmmx = true;
@ -176,10 +262,16 @@ void cpuinfo_arm_linux_decode_isa_from_proc_cpuinfo(
isa->wmmx2 = true; isa->wmmx2 = true;
} }
} else { } else {
cpuinfo_log_warning("WMMX ISA disabled: OS reported iwmmxt feature, " cpuinfo_log_warning(
"but WCID coprocessor type 0x%"PRIx32" indicates no WMMX support", "WMMX ISA disabled: OS reported iwmmxt feature, "
"but WCID coprocessor type 0x%" PRIx32 " indicates no WMMX support",
coprocessor_type); coprocessor_type);
} }
#else
cpuinfo_log_warning(
"WMMX ISA disabled: OS reported iwmmxt feature, "
"but there is no iWMMXt coprocessor");
#endif
} }
if ((features & CPUINFO_ARM_LINUX_FEATURE_THUMB) || (architecture_flags & CPUINFO_ARM_LINUX_ARCH_T)) { if ((features & CPUINFO_ARM_LINUX_FEATURE_THUMB) || (architecture_flags & CPUINFO_ARM_LINUX_ARCH_T)) {
@ -200,35 +292,39 @@ void cpuinfo_arm_linux_decode_isa_from_proc_cpuinfo(
isa->jazelle = true; isa->jazelle = true;
} }
/* Qualcomm Krait may have buggy kernel configuration that doesn't report IDIV */ /* Qualcomm Krait may have buggy kernel configuration that
if ((features & CPUINFO_ARM_LINUX_FEATURE_IDIV) == CPUINFO_ARM_LINUX_FEATURE_IDIV || midr_is_krait(midr)) { * doesn't report IDIV */
if ((features & CPUINFO_ARM_LINUX_FEATURE_IDIV) == CPUINFO_ARM_LINUX_FEATURE_IDIV ||
midr_is_krait(midr)) {
isa->idiv = true; isa->idiv = true;
} }
const uint32_t vfp_mask = \ const uint32_t vfp_mask = CPUINFO_ARM_LINUX_FEATURE_VFP | CPUINFO_ARM_LINUX_FEATURE_VFPV3 |
CPUINFO_ARM_LINUX_FEATURE_VFP | CPUINFO_ARM_LINUX_FEATURE_VFPV3 | CPUINFO_ARM_LINUX_FEATURE_VFPV3D16 | \ CPUINFO_ARM_LINUX_FEATURE_VFPV3D16 | CPUINFO_ARM_LINUX_FEATURE_VFPD32 |
CPUINFO_ARM_LINUX_FEATURE_VFPD32 | CPUINFO_ARM_LINUX_FEATURE_VFPV4 | CPUINFO_ARM_LINUX_FEATURE_NEON; CPUINFO_ARM_LINUX_FEATURE_VFPV4 | CPUINFO_ARM_LINUX_FEATURE_NEON;
if (features & vfp_mask) { if (features & vfp_mask) {
const uint32_t vfpv3_mask = CPUINFO_ARM_LINUX_FEATURE_VFPV3 | CPUINFO_ARM_LINUX_FEATURE_VFPV3D16 | \ const uint32_t vfpv3_mask = CPUINFO_ARM_LINUX_FEATURE_VFPV3 |
CPUINFO_ARM_LINUX_FEATURE_VFPD32 | CPUINFO_ARM_LINUX_FEATURE_VFPV4 | CPUINFO_ARM_LINUX_FEATURE_NEON; CPUINFO_ARM_LINUX_FEATURE_VFPV3D16 | CPUINFO_ARM_LINUX_FEATURE_VFPD32 |
CPUINFO_ARM_LINUX_FEATURE_VFPV4 | CPUINFO_ARM_LINUX_FEATURE_NEON;
if ((architecture_version >= 7) || (features & vfpv3_mask)) { if ((architecture_version >= 7) || (features & vfpv3_mask)) {
isa->vfpv3 = true; isa->vfpv3 = true;
const uint32_t d32_mask = CPUINFO_ARM_LINUX_FEATURE_VFPD32 | CPUINFO_ARM_LINUX_FEATURE_NEON; const uint32_t d32_mask =
CPUINFO_ARM_LINUX_FEATURE_VFPD32 | CPUINFO_ARM_LINUX_FEATURE_NEON;
if (features & d32_mask) { if (features & d32_mask) {
isa->d32 = true; isa->d32 = true;
} }
} else { } else {
#if defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_8A__) || defined(__ARM_ARCH) && (__ARM_ARCH >= 7) #if defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_8A__) || defined(__ARM_ARCH) && (__ARM_ARCH >= 7)
isa->vfpv3 = true; isa->vfpv3 = true;
#else #else
const uint32_t fpsid = read_fpsid(); const uint32_t fpsid = read_fpsid();
cpuinfo_log_debug("FPSID = 0x%08"PRIx32, fpsid); cpuinfo_log_debug("FPSID = 0x%08" PRIx32, fpsid);
const uint32_t subarchitecture = (fpsid >> 16) & UINT32_C(0x7F); const uint32_t subarchitecture = (fpsid >> 16) & UINT32_C(0x7F);
if (subarchitecture >= 0x01) { if (subarchitecture >= 0x01) {
isa->vfpv2 = true; isa->vfpv2 = true;
} }
#endif #endif
} }
} }
if (features & CPUINFO_ARM_LINUX_FEATURE_NEON) { if (features & CPUINFO_ARM_LINUX_FEATURE_NEON) {
@ -237,8 +333,9 @@ void cpuinfo_arm_linux_decode_isa_from_proc_cpuinfo(
/* /*
* There is no separate feature flag for FP16 support. * There is no separate feature flag for FP16 support.
* VFPv4 implies VFPv3-FP16 support (and in practice, NEON-HP as well). * VFPv4 implies VFPv3-FP16 support (and in practice, NEON-HP as
* Additionally, ARM Cortex-A9 and Qualcomm Scorpion support FP16. * well). Additionally, ARM Cortex-A9 and Qualcomm Scorpion
* support FP16.
*/ */
if ((features & CPUINFO_ARM_LINUX_FEATURE_VFPV4) || midr_is_cortex_a9(midr) || midr_is_scorpion(midr)) { if ((features & CPUINFO_ARM_LINUX_FEATURE_VFPV4) || midr_is_cortex_a9(midr) || midr_is_scorpion(midr)) {
isa->fp16 = true; isa->fp16 = true;

View File

@ -3,14 +3,14 @@
#include <arm/linux/api.h> #include <arm/linux/api.h>
#include <cpuinfo/log.h> #include <cpuinfo/log.h>
#include <sys/prctl.h>
void cpuinfo_arm64_linux_decode_isa_from_proc_cpuinfo( void cpuinfo_arm64_linux_decode_isa_from_proc_cpuinfo(
uint32_t features, uint32_t features,
uint32_t features2, uint64_t features2,
uint32_t midr, uint32_t midr,
const struct cpuinfo_arm_chipset chipset[restrict static 1], const struct cpuinfo_arm_chipset chipset[restrict static 1],
struct cpuinfo_arm_isa isa[restrict static 1]) struct cpuinfo_arm_isa isa[restrict static 1]) {
{
if (features & CPUINFO_ARM_LINUX_FEATURE_AES) { if (features & CPUINFO_ARM_LINUX_FEATURE_AES) {
isa->aes = true; isa->aes = true;
} }
@ -31,8 +31,10 @@ void cpuinfo_arm64_linux_decode_isa_from_proc_cpuinfo(
} }
/* /*
* Some phones ship with an old kernel configuration that doesn't report NEON FP16 compute extension and SQRDMLAH/SQRDMLSH/UQRDMLAH/UQRDMLSH instructions. * Some phones ship with an old kernel configuration that doesn't report
* Use a MIDR-based heuristic to whitelist processors known to support it: * NEON FP16 compute extension and SQRDMLAH/SQRDMLSH/UQRDMLAH/UQRDMLSH
* instructions. Use a MIDR-based heuristic to whitelist processors
* known to support it:
* - Processors with Cortex-A55 cores * - Processors with Cortex-A55 cores
* - Processors with Cortex-A65 cores * - Processors with Cortex-A65 cores
* - Processors with Cortex-A75 cores * - Processors with Cortex-A75 cores
@ -41,19 +43,28 @@ void cpuinfo_arm64_linux_decode_isa_from_proc_cpuinfo(
* - Processors with Exynos M4 cores * - Processors with Exynos M4 cores
* - Processors with Exynos M5 cores * - Processors with Exynos M5 cores
* - Neoverse N1 cores * - Neoverse N1 cores
* - Neoverse V1 cores
* - Neoverse N2 cores
* - Neoverse V2 cores
*/ */
if (chipset->series == cpuinfo_arm_chipset_series_samsung_exynos && chipset->model == 9810) { if (chipset->series == cpuinfo_arm_chipset_series_samsung_exynos && chipset->model == 9810) {
/* Exynos 9810 reports that it supports FP16 compute, but in fact only little cores do */ /* Exynos 9810 reports that it supports FP16 compute, but in
cpuinfo_log_warning("FP16 arithmetics and RDM disabled: only little cores in Exynos 9810 support these extensions"); * fact only little cores do */
cpuinfo_log_warning(
"FP16 arithmetics and RDM disabled: only little cores in Exynos 9810 support these extensions");
} else { } else {
const uint32_t fp16arith_mask = CPUINFO_ARM_LINUX_FEATURE_FPHP | CPUINFO_ARM_LINUX_FEATURE_ASIMDHP; const uint32_t fp16arith_mask = CPUINFO_ARM_LINUX_FEATURE_FPHP | CPUINFO_ARM_LINUX_FEATURE_ASIMDHP;
switch (midr & (CPUINFO_ARM_MIDR_IMPLEMENTER_MASK | CPUINFO_ARM_MIDR_PART_MASK)) { switch (midr & (CPUINFO_ARM_MIDR_IMPLEMENTER_MASK | CPUINFO_ARM_MIDR_PART_MASK)) {
case UINT32_C(0x4100D050): /* Cortex-A55 */ case UINT32_C(0x4100D050): /* Cortex-A55 */
case UINT32_C(0x4100D060): /* Cortex-A65 */ case UINT32_C(0x4100D060): /* Cortex-A65 */
case UINT32_C(0x4100D0A0): /* Cortex-A75 */
case UINT32_C(0x4100D0B0): /* Cortex-A76 */ case UINT32_C(0x4100D0B0): /* Cortex-A76 */
case UINT32_C(0x4100D0C0): /* Neoverse N1 */ case UINT32_C(0x4100D0C0): /* Neoverse N1 */
case UINT32_C(0x4100D0D0): /* Cortex-A77 */ case UINT32_C(0x4100D0D0): /* Cortex-A77 */
case UINT32_C(0x4100D0E0): /* Cortex-A76AE */ case UINT32_C(0x4100D0E0): /* Cortex-A76AE */
case UINT32_C(0x4100D400): /* Neoverse V1 */
case UINT32_C(0x4100D490): /* Neoverse N2 */
case UINT32_C(0x4100D4F0): /* Neoverse V2 */
case UINT32_C(0x4800D400): /* Cortex-A76 (HiSilicon) */ case UINT32_C(0x4800D400): /* Cortex-A76 (HiSilicon) */
case UINT32_C(0x51008020): /* Kryo 385 Gold (Cortex-A75) */ case UINT32_C(0x51008020): /* Kryo 385 Gold (Cortex-A75) */
case UINT32_C(0x51008030): /* Kryo 385 Silver (Cortex-A55) */ case UINT32_C(0x51008030): /* Kryo 385 Silver (Cortex-A55) */
@ -68,9 +79,11 @@ void cpuinfo_arm64_linux_decode_isa_from_proc_cpuinfo(
if ((features & fp16arith_mask) == fp16arith_mask) { if ((features & fp16arith_mask) == fp16arith_mask) {
isa->fp16arith = true; isa->fp16arith = true;
} else if (features & CPUINFO_ARM_LINUX_FEATURE_FPHP) { } else if (features & CPUINFO_ARM_LINUX_FEATURE_FPHP) {
cpuinfo_log_warning("FP16 arithmetics disabled: detected support only for scalar operations"); cpuinfo_log_warning(
"FP16 arithmetics disabled: detected support only for scalar operations");
} else if (features & CPUINFO_ARM_LINUX_FEATURE_ASIMDHP) { } else if (features & CPUINFO_ARM_LINUX_FEATURE_ASIMDHP) {
cpuinfo_log_warning("FP16 arithmetics disabled: detected support only for SIMD operations"); cpuinfo_log_warning(
"FP16 arithmetics disabled: detected support only for SIMD operations");
} }
if (features & CPUINFO_ARM_LINUX_FEATURE_ASIMDRDM) { if (features & CPUINFO_ARM_LINUX_FEATURE_ASIMDRDM) {
isa->rdm = true; isa->rdm = true;
@ -78,10 +91,14 @@ void cpuinfo_arm64_linux_decode_isa_from_proc_cpuinfo(
break; break;
} }
} }
if (features2 & CPUINFO_ARM_LINUX_FEATURE2_I8MM) {
isa->i8mm = true;
}
/* /*
* Many phones ship with an old kernel configuration that doesn't report UDOT/SDOT instructions. * Many phones ship with an old kernel configuration that doesn't report
* Use a MIDR-based heuristic to whitelist processors known to support it. * UDOT/SDOT instructions. Use a MIDR-based heuristic to whitelist
* processors known to support it.
*/ */
switch (midr & (CPUINFO_ARM_MIDR_IMPLEMENTER_MASK | CPUINFO_ARM_MIDR_PART_MASK)) { switch (midr & (CPUINFO_ARM_MIDR_IMPLEMENTER_MASK | CPUINFO_ARM_MIDR_PART_MASK)) {
case UINT32_C(0x4100D060): /* Cortex-A65 */ case UINT32_C(0x4100D060): /* Cortex-A65 */
@ -89,7 +106,10 @@ void cpuinfo_arm64_linux_decode_isa_from_proc_cpuinfo(
case UINT32_C(0x4100D0C0): /* Neoverse N1 */ case UINT32_C(0x4100D0C0): /* Neoverse N1 */
case UINT32_C(0x4100D0D0): /* Cortex-A77 */ case UINT32_C(0x4100D0D0): /* Cortex-A77 */
case UINT32_C(0x4100D0E0): /* Cortex-A76AE */ case UINT32_C(0x4100D0E0): /* Cortex-A76AE */
case UINT32_C(0x4100D400): /* Neoverse V1 */
case UINT32_C(0x4100D490): /* Neoverse N2 */
case UINT32_C(0x4100D4A0): /* Neoverse E1 */ case UINT32_C(0x4100D4A0): /* Neoverse E1 */
case UINT32_C(0x4100D4F0): /* Neoverse V2 */
case UINT32_C(0x4800D400): /* Cortex-A76 (HiSilicon) */ case UINT32_C(0x4800D400): /* Cortex-A76 (HiSilicon) */
case UINT32_C(0x51008040): /* Kryo 485 Gold (Cortex-A76) */ case UINT32_C(0x51008040): /* Kryo 485 Gold (Cortex-A76) */
case UINT32_C(0x51008050): /* Kryo 485 Silver (Cortex-A55) */ case UINT32_C(0x51008050): /* Kryo 485 Silver (Cortex-A55) */
@ -124,4 +144,51 @@ void cpuinfo_arm64_linux_decode_isa_from_proc_cpuinfo(
if (features2 & CPUINFO_ARM_LINUX_FEATURE2_SVE2) { if (features2 & CPUINFO_ARM_LINUX_FEATURE2_SVE2) {
isa->sve2 = true; isa->sve2 = true;
} }
if (features2 & CPUINFO_ARM_LINUX_FEATURE2_SME) {
isa->sme = true;
}
if (features2 & CPUINFO_ARM_LINUX_FEATURE2_SME2) {
isa->sme2 = true;
}
if (features2 & CPUINFO_ARM_LINUX_FEATURE2_SME2P1) {
isa->sme2p1 = true;
}
if (features2 & CPUINFO_ARM_LINUX_FEATURE2_SME_I16I32) {
isa->sme_i16i32 = true;
}
if (features2 & CPUINFO_ARM_LINUX_FEATURE2_SME_BI32I32) {
isa->sme_bi32i32 = true;
}
if (features2 & CPUINFO_ARM_LINUX_FEATURE2_SME_B16B16) {
isa->sme_b16b16 = true;
}
if (features2 & CPUINFO_ARM_LINUX_FEATURE2_SME_F16F16) {
isa->sme_f16f16 = true;
}
// SVEBF16 is set iff SVE and BF16 are both supported, but the SVEBF16
// feature flag was added in Linux kernel before the BF16 feature flag,
// so we check for either.
if (features2 & (CPUINFO_ARM_LINUX_FEATURE2_BF16 | CPUINFO_ARM_LINUX_FEATURE2_SVEBF16)) {
isa->bf16 = true;
}
if (features & CPUINFO_ARM_LINUX_FEATURE_ASIMDFHM) {
isa->fhm = true;
}
#ifndef PR_SVE_GET_VL
#define PR_SVE_GET_VL 51
#endif
#ifndef PR_SVE_VL_LEN_MASK
#define PR_SVE_VL_LEN_MASK 0xffff
#endif
int ret = prctl(PR_SVE_GET_VL);
if (ret < 0) {
cpuinfo_log_warning("No SVE support on this machine");
isa->svelen = 0; // Assume no SVE support if the call fails
} else {
// Mask out the SVE vector length bits
isa->svelen = ret & PR_SVE_VL_LEN_MASK;
}
} }

Some files were not shown because too many files have changed in this diff Show More