Browse Source

Build `protoc` from source (#1876)

## Motivation

In a recent addition, we included changes to provide `protoc` as a
binary target from the official releases from the upstream `protobuf`
repository. While this solved one of the largest issues in our ecosystem
where folks currently needed to install `protoc` through a system
package manager, it also introduced another set of problems that binary
targets bring with them.

## Modifications

This PR adds `protobuf` and `abseil-cpp` as a submodule to this project
and includes a new target that builds both together from source. It
includes the minimal set of files from both to successfully build
`protoc` and also passes the right compiler and linker flags to build
those warning free.

## Result

We are no longer using a binary target for `protoc` but instead build it
from source which alleviates the problems that we introduced with the
binary target.
Franz Busch 5 months ago
parent
commit
ee4eca97fe

+ 0 - 143
.github/scripts/draft_release_protoc_artifactbundle.sh

@@ -1,143 +0,0 @@
-#!/bin/bash
-
-# This script generates an artifactbundle for protoc. This artifactbundle
-# is used by the Swift package manger. The script is run by a GitHub action
-# to create protoc-vXXX releases with artifactbundles.
-
-set -eux
-
-AUTH="Authorization: token $GITHUB_TOKEN"
-
-# Fetch the latest stable release from protocolbuffers/protobuf
-upstream_response=$(curl -sH "$AUTH" "https://api.github.com/repos/protocolbuffers/protobuf/releases/latest")
-TAG=$(echo "$upstream_response" | grep -m 1 '"tag_name":' | cut -d '"' -f 4)
-
-# Remove 'v' prefix if present
-TAG="${TAG#v}"
-
-if [[ ! "$TAG" =~ ^[0-9]+\.[0-9]+$ ]]; then
-    echo "Error: $TAG does not match the expected pattern"
-    exit 1
-fi
-
-echo "Latest upstream protoc version: $TAG"
-
-# Check for the latest protoc-vXXX release in this swift-protobuf repo
-swift_protobuf_response=$(curl -sH "$AUTH" "$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/releases")
-CURRENT_PROTOC_TAG=$(echo "$swift_protobuf_response" | jq -r '.[] | select(.tag_name | startswith("protoc-v")) | .tag_name' | head -n 1)
-
-if [ -z "$CURRENT_PROTOC_TAG" ] || [ "$CURRENT_PROTOC_TAG" = "null" ]; then
-    echo "No existing protoc-vXXX release found. This will be the initial release."
-    CURRENT_PROTOC_VERSION=""
-else
-    # Extract version from protoc-vX.Y format
-    CURRENT_PROTOC_VERSION="${CURRENT_PROTOC_TAG#protoc-v}"
-    echo "Current swift-protobuf protoc version: $CURRENT_PROTOC_VERSION"
-fi
-
-# Compare versions - if they match, no need to create a new release
-if [ "$CURRENT_PROTOC_VERSION" = "$TAG" ]; then
-    echo "Protoc version $TAG is already released. No action needed."
-    exit 0
-fi
-
-echo "Creating new protoc release: protoc-v$TAG"
-
-# Fetch all protoc release assets from protocolbuffers/protobuf
-curl -LJ --output protoc-$TAG-osx-x86_64.zip -H 'Accept: application/octet-stream' https://github.com/protocolbuffers/protobuf/releases/download/v$TAG/protoc-$TAG-osx-x86_64.zip
-curl -LJ --output protoc-$TAG-osx-aarch_64.zip -H 'Accept: application/octet-stream' https://github.com/protocolbuffers/protobuf/releases/download/v$TAG/protoc-$TAG-osx-aarch_64.zip
-curl -LJ --output protoc-$TAG-linux-aarch_64.zip -H 'Accept: application/octet-stream' https://github.com/protocolbuffers/protobuf/releases/download/v$TAG/protoc-$TAG-linux-aarch_64.zip
-curl -LJ --output protoc-$TAG-linux-x86_64.zip -H 'Accept: application/octet-stream' https://github.com/protocolbuffers/protobuf/releases/download/v$TAG/protoc-$TAG-linux-x86_64.zip
-curl -LJ --output protoc-$TAG-win64.zip -H 'Accept: application/octet-stream' https://github.com/protocolbuffers/protobuf/releases/download/v$TAG/protoc-$TAG-win64.zip
-
-# Fetch and validate license from protocolbuffers/protobuf
-curl -LJ --output LICENSE -H 'Accept: application/vnd.github.v3.raw' https://api.github.com/repos/protocolbuffers/protobuf/contents/LICENSE
-LICENSE_HASH=$(sha256sum LICENSE | cut -d ' ' -f 1)
-EXPECTED_HASH="6e5e117324afd944dcf67f36cf329843bc1a92229a8cd9bb573d7a83130fea7d"
-
-if [ "$LICENSE_HASH" != "$EXPECTED_HASH" ]; then
-    echo "Error: License file has changed. Expected hash: $EXPECTED_HASH, Got: $LICENSE_HASH"
-    exit 1
-fi
-
-# Unzip all assets
-mkdir protoc-$TAG.artifactbundle
-unzip -d protoc-$TAG.artifactbundle/protoc-$TAG-osx-x86_64 protoc-$TAG-osx-x86_64.zip
-unzip -d protoc-$TAG.artifactbundle/protoc-$TAG-osx-aarch_64 protoc-$TAG-osx-aarch_64.zip
-unzip -d protoc-$TAG.artifactbundle/protoc-$TAG-linux-aarch_64 protoc-$TAG-linux-aarch_64.zip
-unzip -d protoc-$TAG.artifactbundle/protoc-$TAG-linux-x86_64 protoc-$TAG-linux-x86_64.zip
-unzip -d protoc-$TAG.artifactbundle/protoc-$TAG-win64 protoc-$TAG-win64.zip
-
-# Copy license file into artifactbundle
-cp LICENSE protoc-$TAG.artifactbundle/
-
-# Create info.json for artifactbundle
-cat > protoc-$TAG.artifactbundle/info.json << EOF
-{
-    "schemaVersion": "1.0",
-    "artifacts": {
-        "protoc": {
-            "type": "executable",
-            "version": "$TAG",
-            "variants": [
-                {
-                    "path": "protoc-$TAG-linux-x86_64/bin/protoc",
-                    "supportedTriples": ["x86_64-unknown-linux-gnu"]
-                },
-                {
-                    "path": "protoc-$TAG-linux-aarch_64/bin/protoc",
-                    "supportedTriples": ["aarch64-unknown-linux-gnu", "arm64-unknown-linux-gnu", "aarch64-unknown-linux", "arm64-unknown-linux"]
-                },
-                {
-                    "path": "protoc-$TAG-osx-x86_64/bin/protoc",
-                    "supportedTriples": ["x86_64-apple-macosx"]
-                },
-                {
-                    "path": "protoc-$TAG-osx-aarch_64/bin/protoc",
-                    "supportedTriples": ["arm64-apple-macosx"]
-                },
-                {
-                    "path": "protoc-$TAG-win64/bin/protoc.exe",
-                    "supportedTriples": ["x86_64-unknown-windows"]
-                },
-            ]
-        }
-    }
-}
-EOF
-
-# Zip artifactbundle
-zip -r protoc-$TAG.artifactbundle.zip protoc-$TAG.artifactbundle
-
-# Create a new draft release for protoc-vXXX
-echo "Creating draft release protoc-v$TAG"
-create_response=$(curl -sH "$AUTH" -X POST "$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/releases" \
-  -d "{
-    \"tag_name\": \"protoc-artifactbundle-v$TAG\",
-    \"name\": \"protoc v$TAG artifactbundle\",
-    \"body\": \"Protoc artifactbundle for version $TAG\",
-    \"draft\": true,
-    \"prerelease\": false,
-    \"make_latest\": \"false\"
-  }")
-
-upload_url=$(echo "$create_response" | jq -r '.upload_url')
-release_id=$(echo "$create_response" | jq -r '.id')
-
-if [ -z "$upload_url" ] || [ "$upload_url" = "null" ] || [ -z "$release_id" ] || [ "$release_id" = "null" ]; then
-    echo "Error: Failed to create draft release"
-    echo "Response: $create_response"
-    exit 1
-fi
-
-# Remove the {?name,label} template from upload_url
-upload_url=$(echo "$upload_url" | sed 's/{?name,label}//')
-echo "Created draft release with ID: $release_id"
-echo "Upload URL: $upload_url"
-
-# Upload asset
-echo "Uploading artifactbundle..."
-upload_response=$(curl --data-binary @protoc-$TAG.artifactbundle.zip -H "$AUTH" -H "Content-Type: application/octet-stream" "$upload_url?name=protoc-$TAG.artifactbundle.zip")
-
-echo "Upload completed successfully!"
-echo "Draft release protoc-v$TAG created with artifactbundle"

+ 18 - 48
.github/workflows/build.yml

@@ -34,59 +34,22 @@ jobs:
         - version: "5.10"
           # No "hook", see https://github.com/apple/swift-protobuf/issues/1560 for the
           # current issue with using -warnings-as-errors on linux.
-        # protobuf_git can reference a commit, tag, or branch
-        # commit: "commits/6935eae45c99926a000ecbef0be20dfd3d159e71"
-        # tag: "ref/tags/v3.11.4"
-        # branch: "ref/heads/main"
-        protobuf_git: ["ref/heads/main"]
     container:
       image: swift:${{ matrix.swift.version }}
     steps:
     - name: Checkout
       uses: actions/checkout@v4
       with:
-        path: swift-protobuf
+        submodules: true
     - name: Update and install dependencies
       # dependencies from https://github.com/protocolbuffers/protobuf/blob/main/src/README.md
-      # this step is run before get-sha because we need curl and jq for get-sha
-      run: apt-get update && apt-get install -y curl make g++ cmake jq
-    - name: Get Protobuf Commit SHA
-      id: get-sha
-      run: |
-        set -eu
-        url="https://api.github.com/repos/protocolbuffers/protobuf/git/${{ matrix.protobuf_git }}"
-        case ${{ matrix.protobuf_git }} in
-        ref/*)
-          echo "sha=$( curl -s -u "u:${{ github.token }}" "${url}" | jq -r .object.sha )" >> $GITHUB_OUTPUT
-          ;;
-        commits/*)
-          echo "sha=$( curl -s -u "u:${{ github.token }}" "${url}" | jq -r .sha )" >> $GITHUB_OUTPUT
-          ;;
-        esac
+      run: apt-get update && apt-get install -y make g++ cmake
     - name: Build
-      working-directory: swift-protobuf
       run: make build ${{ matrix.swift.hook }}
     - name: Test runtime
-      working-directory: swift-protobuf
       run: make test-runtime ${{ matrix.swift.hook }}
-    - name: Cache protobuf
-      id: cache-protobuf
-      uses: actions/cache@v4
-      with:
-        path: protobuf
-        # NOTE: for refs that can float like 'main' the cache might be out of date!
-        key: ${{ runner.os }}-${{ matrix.swift.version}}-protobuf-${{ steps.get-sha.outputs.sha }}
-    - name: Checkout protobuf repo
-      if: steps.cache-protobuf.outputs.cache-hit != 'true'
-      uses: actions/checkout@v4
-      with:
-        repository: protocolbuffers/protobuf
-        ref: ${{ steps.get-sha.outputs.sha }}
-        submodules: true
-        path: protobuf
     - name: Build protobuf
-      if: steps.cache-protobuf.outputs.cache-hit != 'true'
-      working-directory: protobuf
+      working-directory: Sources/protobuf/protobuf
       # https://github.com/protocolbuffers/protobuf/blob/main/cmake/README.md#c-version
       run: |
         mkdir cmake_build
@@ -101,17 +64,13 @@ jobs:
         NUM_CPUS=$(getconf _NPROCESSORS_ONLN)
         make -j "${NUM_CPUS}" protoc conformance_test_runner
     - name: Test plugin
-      working-directory: swift-protobuf
-      run: make test-plugin PROTOC=../protobuf/cmake_build/protoc
+      run: make test-plugin PROTOC=Sources/protobuf/protobuf/cmake_build/protoc
     - name: Test conformance
-      working-directory: swift-protobuf
-      run: make test-conformance CONFORMANCE_TEST_RUNNER=../protobuf/cmake_build/conformance_test_runner
+      run: make test-conformance CONFORMANCE_TEST_RUNNER=Sources/protobuf/protobuf/cmake_build/conformance_test_runner
     - name: Test SPM plugin
-      working-directory: swift-protobuf
-      run: make test-spm-plugin PROTOC=../protobuf/cmake_build/protoc
+      run: make test-spm-plugin PROTOC=Sources/protobuf/protobuf/cmake_build/protoc
     - name: Compilation Tests
-      working-directory: swift-protobuf
-      run: make compile-tests PROTOC=../protobuf/cmake_build/protoc
+      run: make compile-tests PROTOC=Sources/protobuf/protobuf/cmake_build/protoc
 
   api-breakage:
     name: Api Breakage Compared to main branch
@@ -128,6 +87,7 @@ jobs:
       uses: actions/checkout@v4
       with:
         fetch-depth: 0
+        submodules: true
     - name: Mark the workspace as safe
       # https://github.com/actions/checkout/issues/766
       run: git config --global --add safe.directory ${GITHUB_WORKSPACE}
@@ -146,6 +106,8 @@ jobs:
     steps:
     - name: Checkout
       uses: actions/checkout@v4
+      with:
+        submodules: true
     - name: Mark the workspace as safe
       # https://github.com/actions/checkout/issues/766
       run: git config --global --add safe.directory ${GITHUB_WORKSPACE}
@@ -167,11 +129,17 @@ jobs:
       matrix:
         sanitizer: ["address", "thread"]
         swiftpm_config: ["debug", "release"]
+        exclude: # Excluded due to a clang crasher that is fixed in a more recent clang https://github.com/llvm/llvm-project/issues/95928
+         - sanitizer: "address"
+           swiftpm_config: "debug"
     container:
       # Test on the latest Swift release.
       image: swift:latest
     steps:
     - uses: actions/checkout@v4
+      with:
+        submodules: true
+      
     - name: Test
       run: |
         set -eu
@@ -201,5 +169,7 @@ jobs:
       image: swift:latest
     steps:
     - uses: actions/checkout@v4
+      with:
+        submodules: true
     - name: Build
       run: FuzzTesting/do_build.sh --${{ matrix.swiftpm_config }}-only --run-regressions

+ 1 - 7
.github/workflows/check_upstream_protos.yml

@@ -17,12 +17,6 @@ jobs:
     - name: Checkout
       uses: actions/checkout@v4
       with:
-        path: swift-protobuf
-    - name: Checkout protobufbuffers/protobuf
-      uses: actions/checkout@v4
-      with:
-        repository: protocolbuffers/protobuf
-        path: protobuf
+        submodules: true
     - name: Check Upstream Proto Files
-      working-directory: swift-protobuf
       run: make check-proto-files

+ 0 - 19
.github/workflows/draft_release_protoc_artifactbundle.yml

@@ -1,19 +0,0 @@
-name: Draft release protoc artifactbundle
-
-permissions:
-  contents: read
-
-on:
-  workflow_dispatch:
-
-jobs:
-  draft-release:
-    runs-on: ubuntu-latest
-    steps:
-      - name: "Checkout code"
-        uses: actions/checkout@v4
-      - name: Draft release and upload artifactbundle
-        env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-        run: cd ${{ github.workspace }} && .github/scripts/draft_release_protoc_artifactbundle.sh
-  

+ 4 - 40
.github/workflows/regular_conformance.yml

@@ -36,53 +36,18 @@ jobs:
         # which is also what would be desired, so we don't bother listing explicit ones.
         swift:
         - "6.2"
-        # protobuf_git can reference a commit, tag, or branch
-        # commit: "commits/6935eae45c99926a000ecbef0be20dfd3d159e71"
-        # tag: "ref/tags/v3.11.4"
-        # branch: "ref/heads/main"
-        protobuf_git: ["ref/heads/main"]
     container:
       image: swift:${{ matrix.swift }}
     steps:
     - name: Checkout
       uses: actions/checkout@v4
       with:
-        path: swift-protobuf
+        submodules: true
     - name: Update and install dependencies
       # dependencies from https://github.com/protocolbuffers/protobuf/blob/main/src/README.md
-      # this step is run before get-sha because we need curl and jq for get-sha
-      run: apt-get update && apt-get install -y curl make g++ cmake jq
-    - name: Get Protobuf Commit SHA
-      id: get-sha
-      run: |
-        set -eu
-        url="https://api.github.com/repos/protocolbuffers/protobuf/git/${{ matrix.protobuf_git }}"
-        case ${{ matrix.protobuf_git }} in
-        ref/*)
-          echo "sha=$( curl -s -u "u:${{ github.token }}" "${url}" | jq -r .object.sha )" >> $GITHUB_OUTPUT
-          ;;
-        commits/*)
-          echo "sha=$( curl -s -u "u:${{ github.token }}" "${url}" | jq -r .sha )" >> $GITHUB_OUTPUT
-          ;;
-        esac
-    - name: Cache protobuf
-      id: cache-protobuf
-      uses: actions/cache@v4
-      with:
-        path: protobuf
-        # NOTE: for refs that can float like 'main' the cache might be out of date!
-        key: ${{ runner.os }}-${{ matrix.swift }}-protobuf-${{ steps.get-sha.outputs.sha }}
-    - name: Checkout protobuf repo
-      if: steps.cache-protobuf.outputs.cache-hit != 'true'
-      uses: actions/checkout@v4
-      with:
-        repository: protocolbuffers/protobuf
-        ref: ${{ steps.get-sha.outputs.sha }}
-        submodules: true
-        path: protobuf
+      run: apt-get update && apt-get install -y make g++ cmake
     - name: Build protobuf
-      if: steps.cache-protobuf.outputs.cache-hit != 'true'
-      working-directory: protobuf
+      working-directory: Sources/protobuf/protobuf
       # https://github.com/protocolbuffers/protobuf/blob/main/cmake/README.md#c-version
       run: |
         mkdir cmake_build
@@ -97,5 +62,4 @@ jobs:
         NUM_CPUS=$(getconf _NPROCESSORS_ONLN)
         make -j "${NUM_CPUS}" protoc conformance_test_runner
     - name: Test conformance
-      working-directory: swift-protobuf
-      run: make test-conformance CONFORMANCE_TEST_RUNNER=../protobuf/cmake_build/conformance_test_runner
+      run: make test-conformance CONFORMANCE_TEST_RUNNER=Sources/protobuf/protobuf/cmake_build/conformance_test_runner

+ 119 - 0
.github/workflows/update_protobuf.yml

@@ -0,0 +1,119 @@
+name: Update Protobuf and Abseil Submodules
+
+permissions:
+  contents: write
+  pull-requests: write
+
+on:
+  schedule:
+    # Every night at 2am UTC
+    - cron: '0 2 * * *'
+  workflow_dispatch:
+
+jobs:
+  check-and-update:
+    runs-on: ubuntu-latest
+    steps:
+    - name: Checkout
+      uses: actions/checkout@v4
+      with:
+        submodules: true
+        token: ${{ secrets.GITHUB_TOKEN }}
+
+    - name: Check for new protobuf release
+      id: check_release
+      run: |
+        # Get the latest protobuf release tag
+        LATEST_TAG=$(curl -s https://api.github.com/repos/protocolbuffers/protobuf/releases/latest | jq -r '.tag_name')
+        echo "Latest protobuf release: $LATEST_TAG"
+
+        # Get current protobuf submodule commit
+        cd Sources/protobuf/protobuf
+        CURRENT_TAG=$(git describe --tags --exact-match 2>/dev/null || echo "none")
+        echo "Current protobuf version: $CURRENT_TAG"
+        cd ../../..
+
+        # Check if update is needed
+        if [ "$LATEST_TAG" != "$CURRENT_TAG" ] && [ "$LATEST_TAG" != "null" ]; then
+          echo "needs_update=true" >> $GITHUB_OUTPUT
+          echo "latest_tag=$LATEST_TAG" >> $GITHUB_OUTPUT
+          echo "Update needed: $CURRENT_TAG -> $LATEST_TAG"
+        else
+          echo "needs_update=false" >> $GITHUB_OUTPUT
+          echo "No update needed"
+        fi
+
+    - name: Update protobuf submodule
+      if: steps.check_release.outputs.needs_update == 'true'
+      run: |
+        cd Sources/protobuf/protobuf
+        git fetch --tags
+        git checkout ${{ steps.check_release.outputs.latest_tag }}
+        cd ../../..
+
+    - name: Determine required abseil version
+      if: steps.check_release.outputs.needs_update == 'true'
+      id: check_abseil
+      run: |
+        # Extract abseil commit from protobuf_deps.bzl
+        ABSEIL_COMMIT=$(grep -A 10 '"com_google_absl"' Sources/protobuf/protobuf/protobuf_deps.bzl | grep 'commit' | head -1 | sed 's/.*"\([a-f0-9]*\)".*/\1/')
+
+        if [ -z "$ABSEIL_COMMIT" ]; then
+          echo "Error: Could not determine abseil commit"
+          exit 1
+        fi
+
+        echo "Required abseil commit: $ABSEIL_COMMIT"
+        echo "abseil_commit=$ABSEIL_COMMIT" >> $GITHUB_OUTPUT
+
+    - name: Update abseil submodule
+      if: steps.check_release.outputs.needs_update == 'true'
+      run: |
+        cd Sources/protobuf/abseil
+        git fetch
+        git checkout ${{ steps.check_abseil.outputs.abseil_commit }}
+        cd ../../..
+
+    - name: Stage submodule updates
+      if: steps.check_release.outputs.needs_update == 'true'
+      run: |
+        git add Sources/protobuf/protobuf
+        git add Sources/protobuf/abseil
+
+    - name: Get abseil version info
+      if: steps.check_release.outputs.needs_update == 'true'
+      id: abseil_info
+      run: |
+        cd Sources/protobuf/abseil
+        ABSEIL_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "commit ${{ steps.check_abseil.outputs.abseil_commit }}")
+        echo "abseil_version=$ABSEIL_TAG" >> $GITHUB_OUTPUT
+        cd ../../..
+
+    - name: Create Pull Request
+      if: steps.check_release.outputs.needs_update == 'true'
+      uses: peter-evans/create-pull-request@v6
+      with:
+        token: ${{ secrets.GITHUB_TOKEN }}
+        commit-message: |
+          Update protobuf to ${{ steps.check_release.outputs.latest_tag }} and abseil-cpp
+
+          - Update protobuf submodule to ${{ steps.check_release.outputs.latest_tag }}
+          - Update abseil-cpp submodule to ${{ steps.abseil_info.outputs.abseil_version }}
+        branch: automated/update-protobuf-${{ steps.check_release.outputs.latest_tag }}
+        delete-branch: true
+        title: 'Update protobuf to ${{ steps.check_release.outputs.latest_tag }}'
+        body: |
+          ## Automated Protobuf Update
+
+          This PR automatically updates the protobuf and abseil-cpp submodules to their latest versions.
+
+          ### Changes
+          - **Protobuf**: Updated to `${{ steps.check_release.outputs.latest_tag }}`
+          - **Abseil-cpp**: Updated to `${{ steps.abseil_info.outputs.abseil_version }}`
+
+          ### Release Notes
+          See the [protobuf ${{ steps.check_release.outputs.latest_tag }} release notes](https://github.com/protocolbuffers/protobuf/releases/tag/${{ steps.check_release.outputs.latest_tag }}) for details.
+
+          ---
+
+          *This PR was automatically created by the [Update Protobuf workflow](https://github.com/${{ github.repository }}/actions/workflows/update_protobuf.yml)*

+ 8 - 0
.gitmodules

@@ -0,0 +1,8 @@
+[submodule "Sources/protobuf/protobuf"]
+	path = Sources/protobuf/protobuf
+	url = https://github.com/protocolbuffers/protobuf
+	branch = main
+[submodule "Sources/protobuf/abseil"]
+	path = Sources/protobuf/abseil
+	url = https://github.com/abseil/abseil-cpp
+	branch = main

+ 65 - 28
Documentation/RELEASING.md

@@ -69,41 +69,78 @@ When doing a Swift Protobuf library release:
       _Note:_ This uses that local copy of `SwiftProtobuf.podspec`, but checks
       against the sources on github.
 
-## Protoc Artifactbundle Releases
+## Updating Protobuf and Abseil Submodules
 
-Protoc artifactbundle releases are independent of Swift Protobuf library releases and follow
-a `protoc-vX.Y` naming convention that matches the upstream protoc version.
+The swift-protobuf repository uses git submodules for protobuf and abseil-cpp dependencies.
 
-### Creating a protoc release
+### Automatic Updates (Recommended)
 
-1. **Trigger the workflow**
+The repository has an automated workflow that checks for new protobuf releases **every night at 2am UTC**.
+When a new release is detected, the workflow will:
 
-   Go to the [Actions tab](https://github.com/apple/swift-protobuf/actions/workflows/draft_release_protoc_artifactbundle.yml)
-   and manually run the "Draft release protoc artifactbundle" workflow.
+1. Update the protobuf submodule to the latest release tag
+2. Determine the required abseil-cpp version from protobuf's `protobuf_deps.bzl`
+3. Update the abseil-cpp submodule accordingly
+4. Create a pull request with the changes
 
-1. **What the workflow does automatically**
+The workflow can also be triggered manually from the [Actions tab](https://github.com/apple/swift-protobuf/actions/workflows/update_protobuf.yml).
 
-   The workflow will:
-   - Check the latest protoc version from protocolbuffers/protobuf
-   - Check if we already have a matching `protoc-vX.Y` release
-   - If versions differ or no release exists:
-     - Download protoc binaries for all supported platforms
-     - Create a Swift Package Manager compatible artifact bundle
-     - Create a new draft release tagged `protoc-vX.Y`
-     - Upload the artifactbundle to the draft release
-   - If versions match, exit early (no action needed)
+**No manual intervention is typically required** - just review and merge the automated PR after CI passes.
 
-1. **Publish the release**
+### Manual Updates (If Needed)
 
-   After the workflow completes successfully:
-   - Go to the [releases page](https://github.com/apple/swift-protobuf/releases)
-   - Find the draft `protoc-vX.Y` release
-   - Click the pencil icon to Edit the release
-   - Review the release notes and artifactbundle
-   - Uncheck "Set as the latest release"
-   - Click "Publish release"
+If you need to update the submodules manually (e.g., to test a pre-release version), follow these steps:
 
-1. **Use in Swift Protobuf**
+1. **Check for new protobuf releases**
 
-   The protoc release is now available with a stable URL that can be referenced
-   in `Package.swift`. Create a separate PR to update the reference in the `Package.swift`
+   Visit the [protobuf releases page](https://github.com/protocolbuffers/protobuf/releases)
+   to check if a new version is available.
+
+1. **Update the protobuf submodule**
+
+   From the repository root, update the protobuf submodule to the latest release tag:
+
+   ```bash
+   cd Sources/protobuf/protobuf
+   git fetch --tags
+   git checkout vX.Y.Z  # Replace with the actual release tag, e.g., v29.2
+   cd ../../..
+   git add Sources/protobuf/protobuf
+   ```
+
+1. **Check the abseil-cpp version**
+
+   The protobuf library depends on a specific version of abseil-cpp. Check which version
+   is required by examining `Sources/protobuf/protobuf/protobuf_deps.bzl`:
+
+   ```bash
+   grep -A 3 "abseil-cpp" Sources/protobuf/protobuf/protobuf_deps.bzl
+   ```
+
+   Look for the `commit` field in the abseil-cpp section to find the required commit hash.
+
+1. **Update the abseil-cpp submodule**
+
+   Update the abseil-cpp submodule to match the version required by protobuf:
+
+   ```bash
+   cd Sources/protobuf/abseil
+   git fetch
+   git checkout <commit-hash>  # Use the commit from protobuf_deps.bzl
+   cd ../../..
+   git add Sources/protobuf/abseil
+   ```
+
+1. **Create a pull request**
+
+   Commit the submodule updates and create a PR:
+
+   ```bash
+   git commit -m "Update protobuf submodule to vX.Y.Z and abseil-cpp to <version>"
+   git push origin <your-branch>
+   ```
+
+   In the PR description, include:
+   - The protobuf version being updated to
+   - The abseil-cpp version/commit being updated to
+   - Any relevant release notes from the protobuf release

+ 22 - 18
Makefile

@@ -25,10 +25,11 @@
 # 'swift test', etc commands.
 SWIFT=swift
 
-# How to run a working version of protoc. Invoke make with PROTOC=[path] to
-# override this value, i.e. -
+# How to run a working version of protoc. By default, we build our own copy
+# from the submodule using Swift Package Manager. Invoke make with PROTOC=[path]
+# to override this value, i.e. -
 #   make [TARGET] PROTOC=../protobuf/src/protoc
-PROTOC=protoc
+PROTOC?=.build/debug/protoc
 
 # How to run awk on your system
 AWK=awk
@@ -39,11 +40,11 @@ BINDIR=/usr/local/bin
 # Install tool name
 INSTALL=install
 
-# Where to find a google/protobuf checkout. Defaults be being beside this
-# checkout. Invoke make with GOOGLE_PROTOBUF_CHECKOUT=[PATH_TO_CHECKOUT] to
+# Where to find a google/protobuf checkout. Defaults to the submodule.
+# Invoke make with GOOGLE_PROTOBUF_CHECKOUT=[PATH_TO_CHECKOUT] to
 # override this value, i.e. -
 #   make [TARGET] GOOGLE_PROTOBUF_CHECKOUT=[PATH_TO_CHECKOUT]
-GOOGLE_PROTOBUF_CHECKOUT?=../protobuf
+GOOGLE_PROTOBUF_CHECKOUT?=Sources/protobuf/protobuf
 
 # Helpers for the common parts of source generation.
 #
@@ -115,6 +116,9 @@ build:
 # Anything that needs the plugin should do a build.
 ${PROTOC_GEN_SWIFT}: build
 
+# Build our local copy of protoc from the submodule
+${PROTOC}: build
+
 # Does it really make sense to install a debug build, or should this be forcing
 # a release build and then installing that instead?
 install: build
@@ -175,7 +179,7 @@ test-runtime: build
 # Note: Some of these protos define the same package.(message|enum)s, so they
 # can't be done in a single protoc/proto-gen-swift invoke and have to be done
 # one at a time instead.
-test-plugin: build ${PROTOC_GEN_SWIFT}
+test-plugin: build ${PROTOC_GEN_SWIFT} ${PROTOC}
 	@rm -rf _test && mkdir -p _test/upstream
 	for p in `find Protos/upstream -type f -name '*.proto'`; do \
 		${GENERATE_SRCS_BASE} \
@@ -240,7 +244,7 @@ compile-tests-internalimportsbydefault:
 # Note: Some of the upstream protos define the same package.(message|enum)s, so
 # they can't be done in a single protoc/proto-gen-swift invoke and have to be
 # done one at a time instead.
-reference: build ${PROTOC_GEN_SWIFT}
+reference: build ${PROTOC_GEN_SWIFT} ${PROTOC}
 	@rm -rf Reference && mkdir -p Reference/upstream
 	for p in `find Protos/upstream -type f -name '*.proto'`; do \
 		${GENERATE_SRCS_BASE} \
@@ -295,7 +299,7 @@ regenerate: \
 # Rebuild just the protos included in the runtime library
 # NOTE: dependencies doesn't include the source .proto files, should fix that;
 # would also need to list all the outputs.
-regenerate-library-protos: build ${PROTOC_GEN_SWIFT}
+regenerate-library-protos: build ${PROTOC_GEN_SWIFT} ${PROTOC}
 	find Sources/SwiftProtobuf -name "*.pb.swift" -exec rm -f {} \;
 	${GENERATE_SRCS} \
 		--tfiws_opt=FileNaming=DropPath \
@@ -306,7 +310,7 @@ regenerate-library-protos: build ${PROTOC_GEN_SWIFT}
 # Rebuild just the protos used by the plugin
 # NOTE: dependencies doesn't include the source .proto files, should fix that;
 # would also need to list all the outputs.
-regenerate-plugin-protos: build ${PROTOC_GEN_SWIFT}
+regenerate-plugin-protos: build ${PROTOC_GEN_SWIFT} ${PROTOC}
 	find Sources/SwiftProtobufPluginLibrary -name "*.pb.swift" -exec rm -f {} \;
 	${GENERATE_SRCS} \
 	    -I Protos/Sources/SwiftProtobufPluginLibrary \
@@ -317,7 +321,7 @@ regenerate-plugin-protos: build ${PROTOC_GEN_SWIFT}
 
 # Is this based on the upstream bazel rules `compile_edition_defaults` and
 # `embed_edition_defaults`.
-Sources/SwiftProtobufPluginLibrary/PluginLibEditionDefaults.swift: build ${PROTOC_GEN_SWIFT} Protos/Sources/SwiftProtobuf/google/protobuf/descriptor.proto
+Sources/SwiftProtobufPluginLibrary/PluginLibEditionDefaults.swift: build ${PROTOC_GEN_SWIFT} ${PROTOC} Protos/Sources/SwiftProtobuf/google/protobuf/descriptor.proto
 	@${PROTOC} \
 		--edition_defaults_out=PluginLibEditionDefaults.bin \
 		--edition_defaults_minimum=PROTO2 \
@@ -333,7 +337,7 @@ Sources/SwiftProtobufPluginLibrary/PluginLibEditionDefaults.swift: build ${PROTO
 	@echo ']' >> $@
 
 # Some defaults for the testing of custom features
-Tests/SwiftProtobufPluginLibraryTests/PluginLibTestingEditionDefaults.swift: build ${PROTOC_GEN_SWIFT} Protos/Tests/SwiftProtobufPluginLibraryTests/test_features.proto
+Tests/SwiftProtobufPluginLibraryTests/PluginLibTestingEditionDefaults.swift: build ${PROTOC_GEN_SWIFT} ${PROTOC} Protos/Tests/SwiftProtobufPluginLibraryTests/test_features.proto
 	@${PROTOC} \
 		--edition_defaults_out=PluginLibTestingEditionDefaults.bin \
 		--edition_defaults_minimum=PROTO2 \
@@ -352,7 +356,7 @@ Tests/SwiftProtobufPluginLibraryTests/PluginLibTestingEditionDefaults.swift: bui
 # Rebuild just the protos used by the tests
 # NOTE: dependencies doesn't include the source .proto files, should fix that;
 # would also need to list all the outputs.
-regenerate-test-protos: build ${PROTOC_GEN_SWIFT} Protos/Tests/SwiftProtobufTests/generated_swift_names_enums.proto Protos/Tests/SwiftProtobufTests/generated_swift_names_enum_cases.proto Protos/Tests/SwiftProtobufTests/generated_swift_names_fields.proto Protos/Tests/SwiftProtobufTests/generated_swift_names_messages.proto
+regenerate-test-protos: build ${PROTOC_GEN_SWIFT} ${PROTOC} Protos/Tests/SwiftProtobufTests/generated_swift_names_enums.proto Protos/Tests/SwiftProtobufTests/generated_swift_names_enum_cases.proto Protos/Tests/SwiftProtobufTests/generated_swift_names_fields.proto Protos/Tests/SwiftProtobufTests/generated_swift_names_messages.proto
 	find Tests/SwiftProtobufTests -name "*.pb.swift" -exec rm -f {} \;
 	${GENERATE_SRCS} \
 	    -I Protos/Tests/SwiftProtobufTests \
@@ -368,7 +372,7 @@ regenerate-test-protos: build ${PROTOC_GEN_SWIFT} Protos/Tests/SwiftProtobufTest
 
 # Rebuild the protos for FuzzTesting/Sources/FuzzCommon, the file lives in the
 # Protos/Tests/SwiftProtobufTests to have just one copy.
-regenerate-fuzz-protos: build ${PROTOC_GEN_SWIFT}
+regenerate-fuzz-protos: build ${PROTOC_GEN_SWIFT} ${PROTOC}
 	find FuzzTesting/Sources/FuzzCommon -name "*.pb.swift" -exec rm -f {} \;
 	${GENERATE_SRCS} \
 	    -I Protos/Tests/SwiftProtobufTests \
@@ -386,7 +390,7 @@ SWIFT_PLUGINLIB_DESCRIPTOR_TEST_PROTOS= \
 	Protos/Tests/SwiftProtobufPluginLibraryTests/unittest_delimited_import.proto \
 	Protos/Sources/SwiftProtobufPluginLibrary/swift_protobuf_module_mappings.proto
 
-Tests/SwiftProtobufPluginLibraryTests/DescriptorTestData.swift: build ${PROTOC_GEN_SWIFT} ${SWIFT_PLUGINLIB_DESCRIPTOR_TEST_PROTOS}
+Tests/SwiftProtobufPluginLibraryTests/DescriptorTestData.swift: build ${PROTOC_GEN_SWIFT} ${PROTOC} ${SWIFT_PLUGINLIB_DESCRIPTOR_TEST_PROTOS}
 	@${PROTOC} \
 		--include_source_info \
 		--descriptor_set_out=PluginLibDescriptorTestData.bin \
@@ -405,7 +409,7 @@ Tests/SwiftProtobufPluginLibraryTests/DescriptorTestData.swift: build ${PROTOC_G
 SWIFT_PLUGIN_DESCRIPTOR_TEST_PROTOS= \
        Protos/Tests/protoc-gen-swiftTests/plugin_descriptor_test.proto
 
-Tests/protoc-gen-swiftTests/DescriptorTestData.swift: build ${PROTOC_GEN_SWIFT} ${SWIFT_PLUGIN_DESCRIPTOR_TEST_PROTOS}
+Tests/protoc-gen-swiftTests/DescriptorTestData.swift: build ${PROTOC_GEN_SWIFT} ${PROTOC} ${SWIFT_PLUGIN_DESCRIPTOR_TEST_PROTOS}
 	@${PROTOC} \
 		--descriptor_set_out=PluginDescriptorTestData.bin \
 		-I Protos/Tests/protoc-gen-swiftTests \
@@ -513,7 +517,7 @@ Protos/Tests/SwiftProtobufTests/generated_swift_names_enums.proto: Protos/mined_
 	@echo '}' >> $@
 
 # Rebuild just the protos used by the conformance test runner.
-regenerate-conformance-protos: build ${PROTOC_GEN_SWIFT}
+regenerate-conformance-protos: build ${PROTOC_GEN_SWIFT} ${PROTOC}
 	find Sources/Conformance -name "*.pb.swift" -exec rm -f {} \;
 	${GENERATE_SRCS} \
 	    -I Protos/Sources/Conformance \
@@ -529,7 +533,7 @@ regenerate-compiletests-protos: \
 # Update the CompileTests/MultiModule files.
 # NOTE: Any changes here must also be done on the "test-plugin" target so it
 # generates in the same way.
-regenerate-compiletests-multimodule-protos: build ${PROTOC_GEN_SWIFT}
+regenerate-compiletests-multimodule-protos: build ${PROTOC_GEN_SWIFT} ${PROTOC}
 	find CompileTests/MultiModule -name "*.pb.swift" -exec rm -f {} \;
 	${GENERATE_SRCS} \
 	    -I Protos/CompileTests/MultiModule \

+ 338 - 33
Package.swift

@@ -11,15 +11,6 @@
 
 import PackageDescription
 
-// Including protoc as a binary artifact can cause build issues in some environments. As a
-// temporary measure offer an opt-out by setting PROTOBUF_NO_PROTOC=true.
-let includeProtoc: Bool
-if let noProtoc = Context.environment["PROTOBUF_NO_PROTOC"] {
-    includeProtoc = !(noProtoc.lowercased() == "true" || noProtoc == "1")
-} else {
-    includeProtoc = true
-}
-
 let package = Package(
     name: "SwiftProtobuf",
     products: [
@@ -27,6 +18,10 @@ let package = Package(
             name: "protoc-gen-swift",
             targets: ["protoc-gen-swift"]
         ),
+        .executable(
+            name: "protoc",
+            targets: ["protoc"]
+        ),
         .library(
             name: "SwiftProtobuf",
             targets: ["SwiftProtobuf"]
@@ -59,6 +54,337 @@ let package = Package(
             dependencies: ["SwiftProtobuf"],
             swiftSettings: .packageSettings
         ),
+        .executableTarget(
+            name: "protoc",
+            path: "Sources/protobuf",
+            sources: [
+                // protoc main
+                "protobuf/src/google/protobuf/compiler/main_no_generators.cc",
+
+                // libprotoc
+                "protobuf/src/google/protobuf/any.cc",
+                "protobuf/src/google/protobuf/any.pb.cc",
+                "protobuf/src/google/protobuf/any_lite.cc",
+                "protobuf/src/google/protobuf/api.pb.cc",
+                "protobuf/src/google/protobuf/arena.cc",
+                "protobuf/src/google/protobuf/arenastring.cc",
+                "protobuf/src/google/protobuf/arenaz_sampler.cc",
+                "protobuf/src/google/protobuf/arena_align.cc",
+                "protobuf/src/google/protobuf/compiler/code_generator.cc",
+                "protobuf/src/google/protobuf/compiler/command_line_interface.cc",
+                "protobuf/src/google/protobuf/compiler/code_generator_lite.cc",
+                "protobuf/src/google/protobuf/compiler/versions.cc",
+                "protobuf/src/google/protobuf/compiler/subprocess.cc",
+                "protobuf/src/google/protobuf/compiler/importer.cc",
+                "protobuf/src/google/protobuf/compiler/parser.cc",
+                "protobuf/src/google/protobuf/compiler/plugin.cc",
+                "protobuf/src/google/protobuf/compiler/plugin.pb.cc",
+                "protobuf/src/google/protobuf/compiler/zip_writer.cc",
+                "protobuf/src/google/protobuf/compiler/retention.cc",
+
+                // libprotobuf
+                "protobuf/src/google/protobuf/cpp_features.pb.cc",
+                "protobuf/src/google/protobuf/descriptor.cc",
+                "protobuf/src/google/protobuf/descriptor.pb.cc",
+                "protobuf/src/google/protobuf/descriptor_database.cc",
+                "protobuf/src/google/protobuf/duration.pb.cc",
+                "protobuf/src/google/protobuf/dynamic_message.cc",
+                "protobuf/src/google/protobuf/empty.pb.cc",
+                "protobuf/src/google/protobuf/extension_set.cc",
+                "protobuf/src/google/protobuf/extension_set_heavy.cc",
+                "protobuf/src/google/protobuf/feature_resolver.cc",
+                "protobuf/src/google/protobuf/field_mask.pb.cc",
+                "protobuf/src/google/protobuf/generated_enum_util.cc",
+                "protobuf/src/google/protobuf/generated_message_bases.cc",
+                "protobuf/src/google/protobuf/generated_message_reflection.cc",
+                "protobuf/src/google/protobuf/generated_message_tctable_full.cc",
+                "protobuf/src/google/protobuf/generated_message_tctable_gen.cc",
+                "protobuf/src/google/protobuf/generated_message_tctable_lite.cc",
+                "protobuf/src/google/protobuf/generated_message_util.cc",
+                "protobuf/src/google/protobuf/implicit_weak_message.cc",
+                "protobuf/src/google/protobuf/inlined_string_field.cc",
+                "protobuf/src/google/protobuf/internal_feature_helper.cc",
+                "protobuf/src/google/protobuf/io/coded_stream.cc",
+                "protobuf/src/google/protobuf/io/gzip_stream.cc",
+                "protobuf/src/google/protobuf/io/io_win32.cc",
+                "protobuf/src/google/protobuf/io/printer.cc",
+                "protobuf/src/google/protobuf/io/strtod.cc",
+                "protobuf/src/google/protobuf/io/tokenizer.cc",
+                "protobuf/src/google/protobuf/io/zero_copy_sink.cc",
+                "protobuf/src/google/protobuf/io/zero_copy_stream.cc",
+                "protobuf/src/google/protobuf/io/zero_copy_stream_impl.cc",
+                "protobuf/src/google/protobuf/io/zero_copy_stream_impl_lite.cc",
+                "protobuf/src/google/protobuf/json/internal/lexer.cc",
+                "protobuf/src/google/protobuf/json/internal/message_path.cc",
+                "protobuf/src/google/protobuf/json/internal/parser.cc",
+                "protobuf/src/google/protobuf/json/internal/unparser.cc",
+                "protobuf/src/google/protobuf/json/internal/untyped_message.cc",
+                "protobuf/src/google/protobuf/json/internal/writer.cc",
+                "protobuf/src/google/protobuf/json/internal/zero_copy_buffered_stream.cc",
+                "protobuf/src/google/protobuf/json/json.cc",
+                "protobuf/src/google/protobuf/map.cc",
+                "protobuf/src/google/protobuf/map_field.cc",
+                "protobuf/src/google/protobuf/message.cc",
+                "protobuf/src/google/protobuf/message_lite.cc",
+                "protobuf/src/google/protobuf/micro_string.cc",
+                "protobuf/src/google/protobuf/parse_context.cc",
+                "protobuf/src/google/protobuf/port.cc",
+                "protobuf/src/google/protobuf/raw_ptr.cc",
+                "protobuf/src/google/protobuf/reflection_mode.cc",
+                "protobuf/src/google/protobuf/reflection_ops.cc",
+                "protobuf/src/google/protobuf/repeated_field.cc",
+                "protobuf/src/google/protobuf/repeated_ptr_field.cc",
+                "protobuf/src/google/protobuf/service.cc",
+                "protobuf/src/google/protobuf/source_context.pb.cc",
+                "protobuf/src/google/protobuf/struct.pb.cc",
+                "protobuf/src/google/protobuf/stubs/common.cc",
+                "protobuf/src/google/protobuf/text_format.cc",
+                "protobuf/src/google/protobuf/timestamp.pb.cc",
+                "protobuf/src/google/protobuf/type.pb.cc",
+                "protobuf/src/google/protobuf/unknown_field_set.cc",
+                "protobuf/src/google/protobuf/util/delimited_message_util.cc",
+                "protobuf/src/google/protobuf/util/field_comparator.cc",
+                "protobuf/src/google/protobuf/util/field_mask_util.cc",
+                "protobuf/src/google/protobuf/util/message_differencer.cc",
+                "protobuf/src/google/protobuf/util/time_util.cc",
+                "protobuf/src/google/protobuf/util/type_resolver_util.cc",
+                "protobuf/src/google/protobuf/wire_format.cc",
+                "protobuf/src/google/protobuf/wire_format_lite.cc",
+                "protobuf/src/google/protobuf/wrappers.pb.cc",
+
+                // abseil
+                "abseil/absl/base/internal/cycleclock.cc",
+                "abseil/absl/base/internal/low_level_alloc.cc",
+                "abseil/absl/base/internal/raw_logging.cc",
+                "abseil/absl/base/internal/spinlock.cc",
+                "abseil/absl/base/internal/spinlock_wait.cc",
+                "abseil/absl/base/internal/strerror.cc",
+                "abseil/absl/base/internal/sysinfo.cc",
+                "abseil/absl/base/internal/tracing.cc",
+                "abseil/absl/base/internal/thread_identity.cc",
+                "abseil/absl/base/internal/throw_delegate.cc",
+                "abseil/absl/base/internal/unscaledcycleclock.cc",
+                "abseil/absl/container/internal/raw_hash_set.cc",
+                "abseil/absl/container/internal/hashtablez_sampler.cc",
+                "abseil/absl/container/internal/hashtablez_sampler_force_weak_definition.cc",
+                "abseil/absl/crc/crc32c.cc",
+                "abseil/absl/crc/internal/cpu_detect.cc",
+                "abseil/absl/crc/internal/crc.cc",
+                "abseil/absl/crc/internal/crc_cord_state.cc",
+                "abseil/absl/crc/internal/crc_memcpy_fallback.cc",
+                "abseil/absl/crc/internal/crc_memcpy_x86_arm_combined.cc",
+                "abseil/absl/crc/internal/crc_non_temporal_memcpy.cc",
+                "abseil/absl/crc/internal/crc_x86_arm_combined.cc",
+                "abseil/absl/debugging/internal/address_is_readable.cc",
+                "abseil/absl/debugging/internal/decode_rust_punycode.cc",
+                "abseil/absl/debugging/internal/demangle.cc",
+                "abseil/absl/debugging/internal/demangle_rust.cc",
+                "abseil/absl/debugging/internal/elf_mem_image.cc",
+                "abseil/absl/debugging/internal/examine_stack.cc",
+                "abseil/absl/debugging/internal/utf8_for_code_point.cc",
+                "abseil/absl/debugging/internal/vdso_support.cc",
+                "abseil/absl/debugging/leak_check.cc",
+                "abseil/absl/debugging/stacktrace.cc",
+                "abseil/absl/debugging/symbolize.cc",
+                "abseil/absl/hash/internal/city.cc",
+                "abseil/absl/hash/internal/hash.cc",
+                "abseil/absl/hash/internal/low_level_hash.cc",
+                "abseil/absl/log/die_if_null.cc",
+                "abseil/absl/log/globals.cc",
+                "abseil/absl/log/initialize.cc",
+                "abseil/absl/log/internal/check_op.cc",
+                "abseil/absl/log/internal/conditions.cc",
+                "abseil/absl/log/internal/globals.cc",
+                "abseil/absl/log/internal/log_format.cc",
+                "abseil/absl/log/internal/log_message.cc",
+                "abseil/absl/log/internal/log_sink_set.cc",
+                "abseil/absl/log/internal/nullguard.cc",
+                "abseil/absl/log/internal/proto.cc",
+                "abseil/absl/log/internal/structured_proto.cc",
+                "abseil/absl/log/log_sink.cc",
+                "abseil/absl/numeric/int128.cc",
+                "abseil/absl/profiling/internal/exponential_biased.cc",
+                "abseil/absl/status/status.cc",
+                "abseil/absl/status/statusor.cc",
+                "abseil/absl/status/status_payload_printer.cc",
+                "abseil/absl/status/internal/status_internal.cc",
+                "abseil/absl/strings/ascii.cc",
+                "abseil/absl/strings/charconv.cc",
+                "abseil/absl/strings/cord.cc",
+                "abseil/absl/strings/cord_analysis.cc",
+                "abseil/absl/strings/escaping.cc",
+                "abseil/absl/strings/internal/charconv_bigint.cc",
+                "abseil/absl/strings/internal/charconv_parse.cc",
+                "abseil/absl/strings/internal/cordz_functions.cc",
+                "abseil/absl/strings/internal/cordz_handle.cc",
+                "abseil/absl/strings/internal/cordz_info.cc",
+                "abseil/absl/strings/internal/cord_internal.cc",
+                "abseil/absl/strings/internal/cord_rep_btree.cc",
+                "abseil/absl/strings/internal/cord_rep_btree_navigator.cc",
+                "abseil/absl/strings/internal/cord_rep_btree_reader.cc",
+                "abseil/absl/strings/internal/cord_rep_consume.cc",
+                "abseil/absl/strings/internal/cord_rep_crc.cc",
+                "abseil/absl/strings/internal/damerau_levenshtein_distance.cc",
+                "abseil/absl/strings/internal/escaping.cc",
+                "abseil/absl/strings/internal/memutil.cc",
+                "abseil/absl/strings/internal/ostringstream.cc",
+                "abseil/absl/strings/internal/stringify_sink.cc",
+                "abseil/absl/strings/internal/str_format/arg.cc",
+                "abseil/absl/strings/internal/str_format/bind.cc",
+                "abseil/absl/strings/internal/str_format/extension.cc",
+                "abseil/absl/strings/internal/str_format/float_conversion.cc",
+                "abseil/absl/strings/internal/str_format/output.cc",
+                "abseil/absl/strings/internal/str_format/parser.cc",
+                "abseil/absl/strings/internal/utf8.cc",
+                "abseil/absl/strings/match.cc",
+                "abseil/absl/strings/numbers.cc",
+                "abseil/absl/strings/string_view.cc",
+                "abseil/absl/strings/str_cat.cc",
+                "abseil/absl/strings/str_replace.cc",
+                "abseil/absl/strings/str_split.cc",
+                "abseil/absl/strings/substitute.cc",
+                "abseil/absl/synchronization/barrier.cc",
+                "abseil/absl/synchronization/blocking_counter.cc",
+                "abseil/absl/synchronization/internal/create_thread_identity.cc",
+                "abseil/absl/synchronization/internal/graphcycles.cc",
+                "abseil/absl/synchronization/internal/per_thread_sem.cc",
+                "abseil/absl/synchronization/internal/kernel_timeout.cc",
+                "abseil/absl/synchronization/internal/pthread_waiter.cc",
+                "abseil/absl/synchronization/internal/stdcpp_waiter.cc",
+                "abseil/absl/synchronization/internal/futex_waiter.cc",
+                "abseil/absl/synchronization/internal/sem_waiter.cc",
+                "abseil/absl/synchronization/internal/win32_waiter.cc",
+                "abseil/absl/synchronization/internal/waiter_base.cc",
+                "abseil/absl/synchronization/mutex.cc",
+                "abseil/absl/synchronization/notification.cc",
+                "abseil/absl/time/civil_time.cc",
+                "abseil/absl/time/clock.cc",
+                "abseil/absl/time/duration.cc",
+                "abseil/absl/time/format.cc",
+                "abseil/absl/time/internal/cctz/src/time_zone_fixed.cc",
+                "abseil/absl/time/internal/cctz/src/time_zone_format.cc",
+                "abseil/absl/time/internal/cctz/src/time_zone_if.cc",
+                "abseil/absl/time/internal/cctz/src/time_zone_impl.cc",
+                "abseil/absl/time/internal/cctz/src/time_zone_info.cc",
+                "abseil/absl/time/internal/cctz/src/time_zone_libc.cc",
+                "abseil/absl/time/internal/cctz/src/time_zone_lookup.cc",
+                "abseil/absl/time/internal/cctz/src/time_zone_posix.cc",
+                "abseil/absl/time/internal/cctz/src/zone_info_source.cc",
+                "abseil/absl/time/time.cc",
+
+                // utf8 validation
+                "protobuf/third_party/utf8_range/utf8_range.c",
+
+                // UPB files
+                "protobuf/upb/base/status.c",
+                "protobuf/upb/mem/alloc.c",
+                "protobuf/upb/mem/arena.c",
+                "protobuf/upb/wire/decode.c",
+                "protobuf/upb/wire/encode.c",
+                "protobuf/upb/wire/eps_copy_input_stream.c",
+                "protobuf/upb/wire/reader.c",
+                "protobuf/upb/wire/byte_size.c",
+                "protobuf/upb/wire/internal/decoder.c",
+                "protobuf/upb/wire/decode_fast/dispatch.c",
+                "protobuf/upb/wire/decode_fast/field_fixed.c",
+                "protobuf/upb/wire/decode_fast/field_generic.c",
+                "protobuf/upb/wire/decode_fast/field_message.c",
+                "protobuf/upb/wire/decode_fast/field_string.c",
+                "protobuf/upb/wire/decode_fast/field_varint.c",
+                "protobuf/upb/wire/decode_fast/function_array.c",
+                "protobuf/upb/wire/decode_fast/select.c",
+                "protobuf/upb/hash/common.c",
+                "protobuf/upb/message/accessors.c",
+                "protobuf/upb/message/array.c",
+                "protobuf/upb/message/compare.c",
+                "protobuf/upb/message/copy.c",
+                "protobuf/upb/message/map.c",
+                "protobuf/upb/message/map_sorter.c",
+                "protobuf/upb/message/merge.c",
+                "protobuf/upb/message/message.c",
+                "protobuf/upb/message/promote.c",
+                "protobuf/upb/message/internal/compare_unknown.c",
+                "protobuf/upb/message/internal/extension.c",
+                "protobuf/upb/message/internal/iterator.c",
+                "protobuf/upb/message/internal/message.c",
+                "protobuf/upb/mini_table/compat.c",
+                "protobuf/upb/mini_table/extension_registry.c",
+                "protobuf/upb/mini_table/message.c",
+                "protobuf/upb/mini_table/internal/message.c",
+                "protobuf/upb/mini_descriptor/build_enum.c",
+                "protobuf/upb/mini_descriptor/decode.c",
+                "protobuf/upb/mini_descriptor/link.c",
+                "protobuf/upb/mini_descriptor/internal/base92.c",
+                "protobuf/upb/mini_descriptor/internal/encode.c",
+                "protobuf/upb/reflection/def_pool.c",
+                "protobuf/upb/reflection/def_type.c",
+                "protobuf/upb/reflection/desc_state.c",
+                "protobuf/upb/reflection/enum_def.c",
+                "protobuf/upb/reflection/enum_reserved_range.c",
+                "protobuf/upb/reflection/enum_value_def.c",
+                "protobuf/upb/reflection/extension_range.c",
+                "protobuf/upb/reflection/field_def.c",
+                "protobuf/upb/reflection/file_def.c",
+                "protobuf/upb/reflection/message.c",
+                "protobuf/upb/reflection/message_def.c",
+                "protobuf/upb/reflection/message_reserved_range.c",
+                "protobuf/upb/reflection/method_def.c",
+                "protobuf/upb/reflection/oneof_def.c",
+                "protobuf/upb/reflection/service_def.c",
+                "protobuf/upb/reflection/stage0/google/protobuf/descriptor.upb.c",
+                "protobuf/upb/reflection/internal/def_builder.c",
+                "protobuf/upb/reflection/internal/strdup2.c",
+                "protobuf/upb/text/debug_string.c",
+                "protobuf/upb/text/encode.c",
+                "protobuf/upb/text/internal/encode.c",
+
+                // UPB lex files for round trip functions
+                "protobuf/upb/lex/round_trip.c",
+
+                // upb_generator additional files (minimal set)
+                "protobuf/upb_generator/plugin.cc",
+                "protobuf/upb_generator/common.cc",
+                "protobuf/upb_generator/common/names.cc",
+                "protobuf/upb_generator/file_layout.cc",
+                "protobuf/upb_generator/minitable/names.cc",
+                "protobuf/upb_generator/minitable/names_internal.cc",
+            ],
+            cSettings: [
+                .unsafeFlags([
+                    "-Wno-conversion",
+                    "-Wno-deprecated-declarations",
+                ])
+            ],
+            cxxSettings: [
+                .headerSearchPath("abseil/"),
+                .headerSearchPath("protobuf/"),
+                .headerSearchPath("protobuf/third_party/utf8_range/"),
+                .headerSearchPath("protobuf/src/"),
+                .headerSearchPath("protobuf/upb/"),
+                .headerSearchPath("protobuf/upb_generator/"),
+                .define("UPB_BOOTSTRAP_STAGE", to: "0"),
+                .unsafeFlags([
+                    "-Wno-conversion",
+                    "-Wno-deprecated-declarations",
+                ]),
+            ],
+            linkerSettings: [
+                .linkedFramework(
+                    "CoreFoundation",
+                    .when(
+                        platforms: [
+                            .macOS,
+                            .macCatalyst,
+                            .iOS,
+                            .tvOS,
+                            .watchOS,
+                            .visionOS,
+                        ]
+                    )
+                ),
+                .linkedLibrary("m"),
+            ]
+        ),
         .executableTarget(
             name: "protoc-gen-swift",
             dependencies: ["SwiftProtobufPluginLibrary", "SwiftProtobuf"],
@@ -74,7 +400,7 @@ let package = Package(
         .plugin(
             name: "SwiftProtobufPlugin",
             capability: .buildTool(),
-            dependencies: ["protoc-gen-swift"]
+            dependencies: ["protoc-gen-swift", "protoc"]
         ),
         .testTarget(
             name: "SwiftProtobufTests",
@@ -92,31 +418,10 @@ let package = Package(
             swiftSettings: .packageSettings
         ),
     ],
-    swiftLanguageVersions: [.v5]
+    swiftLanguageVersions: [.v5],
+    cxxLanguageStandard: .gnucxx17
 )
 
-if includeProtoc {
-    package.products.append(
-        .executable(
-            name: "protoc",
-            targets: ["protoc"]
-        )
-    )
-
-    package.targets.append(
-        .binaryTarget(
-            name: "protoc",
-            url:
-                "https://github.com/apple/swift-protobuf/releases/download/protoc-artifactbundle-v32.1/protoc-32.1.artifactbundle.zip",
-            checksum: "3cfb7e13baac742c7f7ba2ff835d22356af18af2b8b7c4d630aacea5eb893fb3"
-        )
-    )
-
-    if let target = package.targets.first(where: { $0.name == "SwiftProtobufPlugin" }) {
-        target.dependencies.append("protoc")
-    }
-}
-
 // Settings for every Swift target in this package, like project-level settings
 // in an Xcode project.
 extension Array where Element == PackageDescription.SwiftSetting {

+ 1 - 0
Sources/protobuf/abseil

@@ -0,0 +1 @@
+Subproject commit 76bb24329e8bf5f39704eb10d21b9a80befa7c81

+ 1 - 0
Sources/protobuf/protobuf

@@ -0,0 +1 @@
+Subproject commit a79f2d2e9fadd75e94f3fe40a0399bf0a5d90551