├── .github ├── FUNDING.yml └── workflows │ ├── build_images.yml │ ├── cron_generate_pr.yml │ └── test_pr.yml ├── .gitignore ├── .golang_hash ├── .golang_version ├── LICENSE ├── README.md ├── docker ├── build │ ├── Dockerfile │ └── build.sh ├── go-1.23.10 │ └── Dockerfile ├── go-1.23.x │ └── Dockerfile ├── go-1.24.4 │ └── Dockerfile ├── go-1.24.x │ └── Dockerfile ├── go-latest │ └── Dockerfile └── toolchain │ ├── Dockerfile │ ├── bootstrap_pure.sh │ ├── build_deps.sh │ ├── fetch.sh │ ├── patch.tar.xz │ ├── patches │ └── autogen.patch │ └── prep_freebsd.sh ├── generate_docker_images.py ├── go.mod ├── tests ├── embedded_c │ └── main.go └── embedded_cpp │ ├── main.go │ ├── snippet.cpp │ └── snippet.h ├── xgo.bats └── xgo.go /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: techknowlogick 2 | -------------------------------------------------------------------------------- /.github/workflows/build_images.yml: -------------------------------------------------------------------------------- 1 | name: Build Images 2 | on: 3 | push: 4 | branches: 5 | - master 6 | - main 7 | workflow_dispatch: 8 | 9 | jobs: 10 | build: 11 | runs-on: namespace-profile-xgo 12 | permissions: 13 | contents: read 14 | packages: write 15 | attestations: write 16 | id-token: write 17 | steps: 18 | - uses: actions/checkout@v4 19 | with: 20 | fetch-depth: 2 21 | - name: get golang version 1 22 | run: | 23 | export VERSION=$(cat .golang_version | awk -F',' '{print $1}' | sed 's/go1/go-1/') 24 | DOTS=$(echo -n $VERSION | awk -F"." '{print NF-1}') 25 | if [ "$DOTS" == "1" ]; then 26 | echo "value=$VERSION.0" >> $GITHUB_OUTPUT 27 | echo "wildcard=$VERSION.x" >> $GITHUB_OUTPUT 28 | else 29 | echo "value=$VERSION" >> $GITHUB_OUTPUT 30 | echo "wildcard=$(echo -n $VERSION | sed 's/\.[^.]*$/.x/')" >> $GITHUB_OUTPUT 31 | fi 32 | id: golang_version_1 33 | - name: get golang version 2 34 | run: | 35 | export VERSION=$(cat .golang_version | awk -F',' '{print $2}' | sed 's/go1/go-1/') 36 | DOTS=$(echo -n $VERSION | awk -F"." '{print NF-1}') 37 | if [ "$DOTS" == "1" ]; then 38 | echo "value=$VERSION.0" >> $GITHUB_OUTPUT 39 | echo "wildcard=$VERSION.x" >> $GITHUB_OUTPUT 40 | else 41 | echo "value=$VERSION" >> $GITHUB_OUTPUT 42 | echo "wildcard=$(echo -n $VERSION | sed 's/\.[^.]*$/.x/')" >> $GITHUB_OUTPUT 43 | fi 44 | id: golang_version_2 45 | - name: get if toolchain should be built 46 | run: | 47 | export VAL=$(git diff-tree --no-commit-id --name-only -r ${{ github.sha }} | grep "docker/toolchain" | wc -l | awk '{print $1}') 48 | echo $VAL 49 | echo "value=$(echo -n $VAL)" >> $GITHUB_OUTPUT 50 | id: toolchain_build 51 | - name: Set up QEMU 52 | uses: docker/setup-qemu-action@v3 53 | with: 54 | platforms: linux/amd64,linux/arm64 55 | - name: Set up Docker Buildx 56 | uses: docker/setup-buildx-action@v3 57 | - name: Login to Docker Hub 58 | uses: docker/login-action@v3 59 | with: 60 | username: ${{ secrets.DOCKER_USERNAME }} 61 | password: ${{ secrets.DOCKER_PASSWORD }} 62 | - name: Log in to the Container registry 63 | uses: docker/login-action@v3 64 | with: 65 | registry: ghcr.io 66 | username: ${{ github.actor }} 67 | password: ${{ secrets.GITHUB_TOKEN }} 68 | - name: build toolchain image 69 | uses: docker/build-push-action@v6 70 | if: steps.toolchain_build.outputs.value != '0' 71 | with: 72 | context: docker/toolchain/ 73 | platforms: linux/amd64,linux/arm64 74 | tags: | 75 | techknowlogick/xgo:toolchain 76 | ghcr.io/techknowlogick/xgo:toolchain 77 | push: true 78 | file: docker/toolchain/Dockerfile 79 | - name: build golang ${{ steps.golang_version_1.outputs.value }} image base 80 | uses: docker/build-push-action@v6 81 | with: 82 | context: docker/${{ steps.golang_version_1.outputs.value }}/ 83 | platforms: linux/amd64,linux/arm64 84 | tags: | 85 | techknowlogick/xgo:${{ steps.golang_version_1.outputs.value }}-base 86 | ghcr.io/techknowlogick/xgo:${{ steps.golang_version_1.outputs.value }}-base 87 | push: true 88 | file: docker/${{ steps.golang_version_1.outputs.value }}/Dockerfile 89 | build-contexts: | 90 | toolchain=docker-image://techknowlogick/xgo:toolchain 91 | - name: build golang ${{ steps.golang_version_1.outputs.value }} image 92 | uses: docker/build-push-action@v6 93 | with: 94 | context: . 95 | platforms: linux/amd64,linux/arm64 96 | tags: | 97 | techknowlogick/xgo:${{ steps.golang_version_1.outputs.value }} 98 | techknowlogick/xgo:${{ steps.golang_version_1.outputs.wildcard }} 99 | techknowlogick/xgo:latest 100 | ghcr.io/techknowlogick/xgo:${{ steps.golang_version_1.outputs.value }} 101 | ghcr.io/techknowlogick/xgo:${{ steps.golang_version_1.outputs.wildcard }} 102 | ghcr.io/techknowlogick/xgo:latest 103 | push: true 104 | build-args: | 105 | VERSION=${{ steps.golang_version_1.outputs.value }} 106 | file: docker/build/Dockerfile 107 | build-contexts: | 108 | ${{ steps.golang_version_1.outputs.value }}-base=docker-image://ghcr.io/techknowlogick/xgo:${{ steps.golang_version_1.outputs.value }}-base 109 | - name: build golang ${{ steps.golang_version_2.outputs.value }} image 110 | uses: docker/build-push-action@v6 111 | with: 112 | context: docker/${{ steps.golang_version_2.outputs.value }}/ 113 | platforms: linux/amd64,linux/arm64 114 | tags: | 115 | techknowlogick/xgo:${{ steps.golang_version_2.outputs.value }}-base 116 | ghcr.io/techknowlogick/xgo:${{ steps.golang_version_2.outputs.value }}-base 117 | push: true 118 | file: docker/${{ steps.golang_version_2.outputs.value }}/Dockerfile 119 | build-contexts: | 120 | toolchain=docker-image://techknowlogick/xgo:toolchain 121 | - name: build golang ${{ steps.golang_version_2.outputs.value }} image 122 | uses: docker/build-push-action@v6 123 | with: 124 | context: . 125 | platforms: linux/amd64,linux/arm64 126 | tags: | 127 | techknowlogick/xgo:${{ steps.golang_version_2.outputs.value }} 128 | techknowlogick/xgo:${{ steps.golang_version_2.outputs.wildcard }} 129 | ghcr.io/techknowlogick/xgo:${{ steps.golang_version_2.outputs.value }} 130 | ghcr.io/techknowlogick/xgo:${{ steps.golang_version_2.outputs.wildcard }} 131 | push: true 132 | build-args: | 133 | VERSION=${{ steps.golang_version_2.outputs.value }} 134 | file: docker/build/Dockerfile 135 | build-contexts: | 136 | ${{ steps.golang_version_2.outputs.value }}-base=docker-image://ghcr.io/techknowlogick/xgo:${{ steps.golang_version_2.outputs.value }}-base 137 | -------------------------------------------------------------------------------- /.github/workflows/cron_generate_pr.yml: -------------------------------------------------------------------------------- 1 | name: Generate PR when new golang versions released 2 | 3 | on: 4 | schedule: 5 | - cron: '33 * * * *' # every hour at 33 minute past 6 | workflow_dispatch: 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | permissions: 12 | issues: write 13 | pull-requests: write 14 | contents: write 15 | steps: 16 | - uses: actions/checkout@v4 17 | with: 18 | ref: main 19 | persist-credentials: false 20 | 21 | - name: Get the hash of the current release JSON and exit if the value is the same 22 | run: | 23 | rm -rf docker/go-1* 24 | pip3 install jsonpath_ng 25 | python3 generate_docker_images.py 26 | 27 | - name: Get golang version 28 | run: echo "value=$(cat .golang_version)" >> $GITHUB_OUTPUT 29 | id: golang_version 30 | 31 | - name: Create Pull Request 32 | id: cpr 33 | uses: peter-evans/create-pull-request@v3 34 | with: 35 | token: ${{ secrets.GITHUB_TOKEN }} 36 | signoff: false 37 | branch: golang-${{ hashFiles('.golang_version') }} 38 | commit-message: ${{ steps.golang_version.outputs.value }} 39 | committer: GitHub 40 | author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com> 41 | delete-branch: true 42 | title: ${{ steps.golang_version.outputs.value }} 43 | assignees: techknowlogick 44 | reviewers: techknowlogick 45 | 46 | - name: Check outputs 47 | run: | 48 | echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}" 49 | echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}" 50 | -------------------------------------------------------------------------------- /.github/workflows/test_pr.yml: -------------------------------------------------------------------------------- 1 | name: Test PR 2 | on: 3 | pull_request: 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-20.04 8 | steps: 9 | - uses: actions/checkout@v4 10 | with: 11 | fetch-depth: 2 12 | - name: Free Disk Space (Ubuntu) 13 | uses: jlumbroso/free-disk-space@main 14 | with: 15 | # this might remove tools that are actually needed, 16 | # if set to "true" but frees about 6 GB 17 | tool-cache: false 18 | 19 | # all of these default to true, but feel free to set to 20 | # "false" if necessary for your workflow 21 | android: true 22 | dotnet: true 23 | haskell: true 24 | large-packages: true 25 | docker-images: false 26 | swap-storage: true 27 | - name: get golang version 1 28 | run: | 29 | export VERSION=$(cat .golang_version | awk -F',' '{print $1}' | sed 's/go1/go-1/') 30 | DOTS=$(echo -n $VERSION | awk -F"." '{print NF-1}') 31 | if [ "$DOTS" == "1" ]; then 32 | echo "value=$VERSION.0" >> $GITHUB_OUTPUT 33 | echo "wildcard=$VERSION.x" >> $GITHUB_OUTPUT 34 | else 35 | echo "value=$VERSION" >> $GITHUB_OUTPUT 36 | echo "wildcard=$(echo -n $VERSION | sed 's/\.[^.]*$/.x/')" >> $GITHUB_OUTPUT 37 | fi 38 | id: golang_version_1 39 | - name: get golang version 2 40 | run: | 41 | export VERSION=$(cat .golang_version | awk -F',' '{print $2}' | sed 's/go1/go-1/') 42 | DOTS=$(echo -n $VERSION | awk -F"." '{print NF-1}') 43 | if [ "$DOTS" == "1" ]; then 44 | echo "value=$VERSION.0" >> $GITHUB_OUTPUT 45 | echo "wildcard=$VERSION.x" >> $GITHUB_OUTPUT 46 | else 47 | echo "value=$VERSION" >> $GITHUB_OUTPUT 48 | echo "wildcard=$(echo -n $VERSION | sed 's/\.[^.]*$/.x/')" >> $GITHUB_OUTPUT 49 | fi 50 | id: golang_version_2 51 | - name: Setup BATS 52 | uses: mig4/setup-bats@v1 53 | with: 54 | bats-version: 1.2.1 55 | - name: cache toolchain 56 | id: cache-toolchain 57 | uses: actions/cache@v3 58 | env: 59 | cache-name: cache-toolchain 60 | with: 61 | path: /tmp/toolchain.tar 62 | key: ${{ runner.os }}-toolchain-${{ hashFiles('docker/toolchain/**') }} 63 | - if: ${{ steps.cache-toolchain.outputs.cache-hit != 'true' }} 64 | name: build toolchain 65 | run: | 66 | echo "Build toolchain docker" 67 | docker buildx build --file docker/toolchain/Dockerfile \ 68 | --tag toolchain --load docker/toolchain/ 69 | docker save -o /tmp/toolchain.tar toolchain 70 | - name: build images and tests 71 | run: | 72 | echo "Load toolchain docker" 73 | docker load -i /tmp/toolchain.tar 74 | 75 | echo "Bootstrap go ${{ steps.golang_version_1.outputs.value }}" 76 | docker buildx build --file docker/${{ steps.golang_version_1.outputs.value }}/Dockerfile \ 77 | --tag ${{ steps.golang_version_1.outputs.value }}-base --load docker/${{ steps.golang_version_1.outputs.value }}/ 78 | 79 | echo "Build xgo for go ${{ steps.golang_version_1.outputs.value }} " 80 | docker buildx build --file docker/build/Dockerfile \ 81 | --tag techknowlogick/xgo:${{ steps.golang_version_1.outputs.value }} \ 82 | --tag techknowlogick/xgo:${{ steps.golang_version_1.outputs.wildcard }} \ 83 | --tag techknowlogick/xgo:latest \ 84 | --build-arg VERSION=${{ steps.golang_version_1.outputs.value }} \ 85 | --load . 86 | 87 | echo "Bootstrap go ${{ steps.golang_version_2.outputs.value }}" 88 | docker buildx build --file docker/${{ steps.golang_version_2.outputs.value }}/Dockerfile \ 89 | --tag ${{ steps.golang_version_2.outputs.value }}-base --load docker/${{ steps.golang_version_2.outputs.value }}/ 90 | 91 | echo "Build xgo for go ${{ steps.golang_version_2.outputs.value }}" 92 | docker buildx build --file docker/build/Dockerfile \ 93 | --tag techknowlogick/xgo:${{ steps.golang_version_2.outputs.value }} \ 94 | --tag techknowlogick/xgo:${{ steps.golang_version_2.outputs.wildcard }} \ 95 | --build-arg VERSION=${{ steps.golang_version_2.outputs.value }} \ 96 | --load . 97 | 98 | mkdir -p .xgo-cache 99 | mkdir -p ~/go/src 100 | 101 | echo "Run tests on latest go ${{ steps.golang_version_1.outputs.value }}" 102 | env IMAGEID="techknowlogick/xgo:${{ steps.golang_version_1.outputs.value }}" bats xgo.bats 103 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .DS_Store 3 | xgo 4 | -------------------------------------------------------------------------------- /.golang_hash: -------------------------------------------------------------------------------- 1 | 7eae060c6a0cd2e1635a5ccbdc948704f527e39ba1970093897a3954c8da9a9a -------------------------------------------------------------------------------- /.golang_version: -------------------------------------------------------------------------------- 1 | go1.24.4,go1.23.10 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2014 Péter Szilágyi 4 | Copyright (c) 2019 techknowlogick 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # xgo - Go CGO Cross Compiler 2 | 3 | [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) 4 | 5 | A Docker-based Go cross compiler that enables easy compilation of Go projects with CGO dependencies across multiple platforms and architectures. 6 | 7 | ## Table of Contents 8 | 9 | - [xgo - Go CGO Cross Compiler](#xgo---go-cgo-cross-compiler) 10 | - [Table of Contents](#table-of-contents) 11 | - [Overview](#overview) 12 | - [Quick Start](#quick-start) 13 | - [Installation](#installation) 14 | - [Docker Image](#docker-image) 15 | - [Go Wrapper](#go-wrapper) 16 | - [Usage](#usage) 17 | - [Basic Usage](#basic-usage) 18 | - [Build Flags](#build-flags) 19 | - [Go Releases](#go-releases) 20 | - [Limit Build Targets](#limit-build-targets) 21 | - [Platform Versions](#platform-versions) 22 | - [CGO Dependencies](#cgo-dependencies) 23 | - [Hooks](#hooks) 24 | - [Supporters](#supporters) 25 | - [Contributing](#contributing) 26 | - [License](#license) 27 | - [Acknowledgments](#acknowledgments) 28 | 29 | ## Overview 30 | 31 | Although Go strives to be a cross-platform language, cross-compilation with CGO enabled isn't straightforward. You need Go sources bootstrapped to each platform and architecture, plus access to OS-specific headers and libraries. 32 | 33 | xgo solves this by packaging all necessary Go toolchains, C cross compilers, and platform headers/libraries into a single Docker container. This enables seamless cross-compilation of Go code with embedded C/C++ snippets (`CGO_ENABLED=1`) to various platforms and architectures. 34 | 35 | ## Quick Start 36 | 37 | 1. Install Docker and pull the xgo image: 38 | ```bash 39 | docker pull techknowlogick/xgo:latest 40 | ``` 41 | 42 | 2. Install the xgo wrapper: 43 | ```bash 44 | go install src.techknowlogick.com/xgo@latest 45 | ``` 46 | 47 | 3. Cross-compile your project: 48 | ```bash 49 | cd your-project 50 | xgo . 51 | ``` 52 | 53 | That's it! You'll get binaries for all supported platforms and architectures. 54 | 55 | ## Installation 56 | 57 | ### Docker Image 58 | 59 | Pull the pre-built Docker image: 60 | 61 | ```bash 62 | docker pull techknowlogick/xgo:latest 63 | ``` 64 | 65 | ### Go Wrapper 66 | 67 | Install the xgo command-line wrapper: 68 | 69 | ```bash 70 | go install src.techknowlogick.com/xgo@latest 71 | ``` 72 | 73 | ## Usage 74 | 75 | ### Basic Usage 76 | 77 | Simply specify the import path you want to build: 78 | 79 | ```bash 80 | $ xgo -out iris-v0.3.2 github.com/project-iris/iris 81 | ... 82 | 83 | $ ls -al 84 | -rwxr-xr-x 1 root root 6776500 Nov 24 16:44 iris-v0.3.2-darwin-10.6-386 85 | -rwxr-xr-x 1 root root 8755532 Nov 24 16:44 iris-v0.3.2-darwin-10.6-amd64 86 | -rwxr-xr-x 1 root root 10135248 Nov 24 16:44 iris-v0.3.2-linux-386 87 | -rwxr-xr-x 1 root root 12598472 Nov 24 16:44 iris-v0.3.2-linux-amd64 88 | -rwxr-xr-x 1 root root 10040464 Nov 24 16:44 iris-v0.3.2-linux-arm 89 | -rwxr-xr-x 1 root root 7516368 Nov 24 16:44 iris-v0.3.2-windows-4.0-386.exe 90 | -rwxr-xr-x 1 root root 9549416 Nov 24 16:44 iris-v0.3.2-windows-4.0-amd64.exe 91 | ``` 92 | 93 | For local projects, use paths starting with `.` or `/`: 94 | 95 | ```bash 96 | xgo . 97 | ``` 98 | 99 | ### CLI Flags 100 | 101 | xgo supports the following command-line flags: 102 | 103 | | Flag | Description | Default | 104 | |------|-------------|---------| 105 | | `-go` | Go release to use for cross compilation | `latest` | 106 | | `-out` | Prefix to use for output naming | Package name | 107 | | `-dest` | Destination folder to put binaries in | Current directory | 108 | | `-pkg` | Sub-package to build if not root import | | 109 | | `-remote` | Version control remote repository to build | | 110 | | `-branch` | Version control branch to build | | 111 | | `-targets` | Comma separated targets to build for | `*/*` (all) | 112 | | `-deps` | CGO dependencies (configure/make based archives) | | 113 | | `-depsargs` | CGO dependency configure arguments | | 114 | | `-image` | Use custom docker image instead of official | | 115 | | `-env` | Comma separated custom environments for docker | | 116 | | `-dockerargs` | Comma separated arguments for docker run | | 117 | | `-volumes` | Volume mounts in format `source:target[:mode]` | | 118 | | `-hooksdir` | Directory with user hook scripts | | 119 | | `-ssh` | Enable ssh agent forwarding | `false` | 120 | 121 | ### Build Flags 122 | 123 | The following `go build` flags are supported: 124 | 125 | | Flag | Description | 126 | |------|-------------| 127 | | `-v` | Print package names as they are compiled | 128 | | `-x` | Print build commands as compilation progresses | 129 | | `-race` | Enable data race detection (amd64 only) | 130 | | `-tags='tag list'` | Build tags to consider satisfied | 131 | | `-ldflags='flag list'` | Arguments for go tool link | 132 | | `-gcflags='flag list'` | Arguments for go tool compile | 133 | | `-buildmode=mode` | Binary type to produce | 134 | | `-trimpath` | Remove all file system paths from the resulting executable | 135 | | `-buildvcs` | Whether to stamp binaries with version control information | 136 | | `-obfuscate` | Obfuscate build using garble | 137 | | `-garbleflags` | Arguments to pass to garble (e.g. `-seed=random`) | 138 | 139 | ### Go Releases 140 | 141 | Select specific Go versions using the `-go` flag: 142 | 143 | ```bash 144 | xgo -go go-1.24.x github.com/your-username/your-project 145 | ``` 146 | 147 | Supported release strings: 148 | - `latest` - Latest Go release (default) 149 | - `go-1.24.x` - Latest point release of Go 1.24 150 | - `go-1.24.3` - Specific Go version 151 | 152 | ### Limit Build Targets 153 | 154 | Restrict builds to specific platforms/architectures: 155 | 156 | ```bash 157 | # Build only ARM Linux binaries 158 | xgo --targets=linux/arm github.com/your-username/your-project 159 | 160 | # Build all Windows and macOS binaries 161 | xgo --targets=windows/*,darwin/* github.com/your-username/your-project 162 | 163 | # Build ARM binaries for all platforms 164 | xgo --targets=*/arm github.com/your-username/your-project 165 | ``` 166 | 167 | **Supported targets:** 168 | - **Platforms:** `darwin`, `linux`, `windows`, `freebsd` 169 | - **Architectures:** `386`, `amd64`, `arm-5`, `arm-6`, `arm-7`, `arm64`, `mips`, `mipsle`, `mips64`, `mips64le`, `riscv64` 170 | 171 | ### Platform Versions 172 | 173 | Target specific platform versions: 174 | 175 | ```bash 176 | # Cross-compile to macOS Monterey 177 | xgo --targets=darwin-12.0/* github.com/your-username/your-project 178 | 179 | # Cross-compile to Windows 10 180 | xgo --targets=windows-10.0/* github.com/your-username/your-project 181 | ``` 182 | 183 | **Supported platforms:** 184 | - **Windows:** All APIs up to Windows 11 (limited by mingw-w64) 185 | - **macOS:** APIs from 10.6 to latest 186 | 187 | ### CGO Dependencies 188 | 189 | Build projects with external C/C++ library dependencies using `--deps`: 190 | 191 | ```bash 192 | $ xgo --deps=https://gmplib.org/download/gmp/gmp-6.1.0.tar.bz2 \ 193 | --targets=windows/* github.com/ethereum/go-ethereum/cmd/geth 194 | ... 195 | 196 | $ ls -al 197 | -rwxr-xr-x 1 root root 16315679 Nov 24 16:39 geth-windows-4.0-386.exe 198 | -rwxr-xr-x 1 root root 19452036 Nov 24 16:38 geth-windows-4.0-amd64.exe 199 | ``` 200 | 201 | Pass arguments to dependency configure scripts: 202 | 203 | ```bash 204 | $ xgo --deps=https://gmplib.org/download/gmp/gmp-6.1.0.tar.bz2 \ 205 | --targets=ios/* --depsargs=--disable-assembly \ 206 | github.com/ethereum/go-ethereum/cmd/geth 207 | ... 208 | 209 | $ ls -al 210 | -rwxr-xr-x 1 root root 14804160 Nov 24 16:32 geth-ios-5.0-arm 211 | ``` 212 | 213 | Supported dependency formats: `.tar`, `.tar.gz`, `.tar.bz2` 214 | 215 | ### Hooks 216 | 217 | Use custom build hooks by providing a hooks directory: 218 | 219 | ```bash 220 | xgo --hooksdir ./hooks github.com/your-username/your-project 221 | ``` 222 | 223 | Available hook scripts: 224 | - `setup.sh` - Sourced after environment setup (install additional packages) 225 | - `build.sh` - Sourced before each target build 226 | 227 | Environment variables in `build.sh`: 228 | - `XGOOS` and `XGOARCH` - Target OS and architecture 229 | - `CC` - C cross compiler for the target 230 | - `HOST` - Target platform identifier 231 | - `PREFIX` - Installation path for built binaries 232 | 233 | ## Supporters 234 | 235 | Thanks to these projects for supporting xgo: 236 | 237 | - [Gitea](https://about.gitea.com/) - A painless self-hosted Git service 238 | - [Offen](https://www.offen.dev/) - Fair and lightweight web analytics 239 | - [Vikunja](https://vikunja.io/) - The to-do app to organize your life 240 | - [Woodpecker CI](https://woodpecker-ci.org/) - Simple CI engine with great extensibility 241 | 242 | You can [sponsor this project](https://github.com/sponsors/techknowlogick/) to ensure its continued maintenance. 243 | 244 | ## Contributing 245 | 246 | Contributions are welcome! Please feel free to submit issues and enhancement requests. 247 | 248 | ## License 249 | 250 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. 251 | 252 | ## Acknowledgments 253 | 254 | Special thanks to [@karalabe](https://github.com/karalabe) for starting this project and making Go cross-compilation with CGO seamless. 255 | -------------------------------------------------------------------------------- /docker/build/Dockerfile: -------------------------------------------------------------------------------- 1 | # Go cross compiler (xgo): Base cross-compilation layer 2 | # Copyright (c) 2014 Péter Szilágyi. All rights reserved. 3 | # 4 | # Released under the MIT license. 5 | ARG VERSION=latest 6 | FROM ${VERSION}-base 7 | 8 | LABEL maintainer="techknowlogick " 9 | 10 | COPY go.mod /xgo-src/ 11 | COPY xgo.go /xgo-src/ 12 | 13 | # Mark the image as xgo enabled to support xgo-in-xgo 14 | ENV XGO_IN_XGO 1 15 | 16 | # Install xgo within the container to enable internal cross compilation 17 | WORKDIR /xgo-src 18 | RUN \ 19 | echo "Installing xgo-in-xgo..." && \ 20 | go install xgo.go && \ 21 | ln -s /go/bin/xgo /usr/bin/xgo && \ 22 | rm -r /xgo-src 23 | 24 | WORKDIR / 25 | 26 | # Inject the container entry point, the build script 27 | COPY docker/build/build.sh /build.sh 28 | ENV BUILD /build.sh 29 | RUN chmod +x "$BUILD" 30 | 31 | ENTRYPOINT ["/build.sh"] 32 | -------------------------------------------------------------------------------- /docker/build/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Contains the main cross compiler, that individually sets up each target build 4 | # platform, compiles all the C dependencies, then build the requested executable 5 | # itself. 6 | # 7 | # Usage: build.sh 8 | # 9 | # Needed environment variables: 10 | # REPO_REMOTE - Optional VCS remote if not the primary repository is needed 11 | # REPO_BRANCH - Optional VCS branch to use, if not the master branch 12 | # DEPS - Optional list of C dependency packages to build 13 | # ARGS - Optional arguments to pass to C dependency configure scripts 14 | # PACK - Optional sub-package, if not the import path is being built 15 | # OUT - Optional output prefix to override the package name 16 | # FLAG_V - Optional verbosity flag to set on the Go builder 17 | # FLAG_X - Optional flag to print the build progress commands 18 | # FLAG_RACE - Optional race flag to set on the Go builder 19 | # FLAG_TAGS - Optional tag flag to set on the Go builder 20 | # FLAG_LDFLAGS - Optional ldflags flag to set on the Go builder 21 | # FLAG_GCFLAGS - Optional gcflags flag to set on the Go builder 22 | # FLAG_BUILDMODE - Optional buildmode flag to set on the Go builder 23 | # FLAG_TRIMPATH - Optional trimpath flag to set on the Go builder 24 | # FLAG_BUILDVCS - Optional buildvcs flag to set on the Go builder 25 | # FLAG_OBFUSCATE - Optional flag to obfuscate builds using garble 26 | # TARGETS - Comma separated list of build targets to compile for 27 | # EXT_GOPATH - GOPATH elements mounted from the host filesystem 28 | # GARBLE_FLAGS - Flags to pass to garble (e.g. -seed=random) 29 | 30 | # Define a function that figures out the binary extension 31 | function extension { 32 | if [ "$FLAG_BUILDMODE" == "archive" ] || [ "$FLAG_BUILDMODE" == "c-archive" ]; then 33 | if [ "$1" == "windows" ]; then 34 | echo ".lib" 35 | else 36 | echo ".a" 37 | fi 38 | elif [ "$FLAG_BUILDMODE" == "shared" ] || [ "$FLAG_BUILDMODE" == "c-shared" ]; then 39 | if [ "$1" == "windows" ]; then 40 | echo ".dll" 41 | elif [ "$1" == "darwin" ]; then 42 | echo ".dylib" 43 | else 44 | echo ".so" 45 | fi 46 | else 47 | if [ "$1" == "windows" ]; then 48 | echo ".exe" 49 | fi 50 | fi 51 | } 52 | 53 | function do_build { 54 | if [ -f "/hooksdir/build.sh" ]; then echo "source build.sh hook"; source "/hooksdir/build.sh"; fi 55 | # call dedicated build script 56 | $BUILD_DEPS /deps "${DEPS_ARGS[@]}" 57 | } 58 | 59 | GO_VERSION_MAJOR=$(go version | sed -e 's/.*go\([0-9]\+\)\..*/\1/') 60 | GO_VERSION_MINOR=$(go version | sed -e 's/.*go[0-9]\+\.\([0-9]\+\)\..*/\1/') 61 | GO111MODULE=$(go env GO111MODULE) 62 | 63 | # if GO_VERSION_MINOR starts with '.' then trim it 64 | if [[ "$GO_VERSION_MINOR" == .* ]]; then 65 | GO_VERSION_MINOR=${GO_VERSION_MINOR:1} 66 | fi 67 | 68 | # Detect if we are using go modules 69 | if [[ "$GO_VERSION_MAJOR" -le 1 && "$GO_VERSION_MINOR" -le 17 ]]; then 70 | if [[ "$GO111MODULE" == "on" || "$GO111MODULE" == "auto" ]]; then 71 | USEMODULES=true 72 | else 73 | USEMODULES=false 74 | fi 75 | else 76 | if [[ "$GO111MODULE" != "off" ]]; then 77 | USEMODULES=true 78 | else 79 | USEMODULES=false 80 | fi 81 | fi 82 | 83 | # Either set a local build environment, or pull any remote imports 84 | if [ "$EXT_GOPATH" != "" ]; then 85 | # If local builds are requested, inject the sources 86 | echo "Building locally $1..." 87 | export GOPATH=$GOPATH:$EXT_GOPATH 88 | set -e 89 | 90 | # Find and change into the package folder 91 | cd "$(go list -e -f "{{.Dir}}" "$1")" 92 | GODEPS_WORKSPACE="$(pwd)/Godeps/_workspace" 93 | export GOPATH="$GOPATH":"$GODEPS_WORKSPACE" 94 | elif [[ "$USEMODULES" == true && -d /source ]]; then 95 | # Go module build with a local repository mapped to /source containing at least a go.mod file. 96 | 97 | # Change into the repo/source folder 98 | cd /source 99 | echo "Building /source/go.mod..." 100 | else 101 | # Inject all possible Godep paths to short circuit go gets 102 | GOPATH_ROOT="$GOPATH/src" 103 | IMPORT_PATH="$1" 104 | while [ "$IMPORT_PATH" != "." ]; do 105 | export GOPATH="$GOPATH":"$GOPATH_ROOT/$IMPORT_PATH"/Godeps/_workspace 106 | IMPORT_PATH=$(dirname "$IMPORT_PATH") 107 | done 108 | 109 | # Otherwise download the canonical import path (may fail, don't allow failures beyond) 110 | echo "Fetching main repository $1..." 111 | GO111MODULE="off" go get -v -d "$1" 112 | set -e 113 | 114 | cd "$GOPATH_ROOT/$1" 115 | 116 | # Switch over the code-base to another checkout if requested 117 | if [ "$REPO_REMOTE" != "" ] || [ "$REPO_BRANCH" != "" ]; then 118 | # Detect the version control system type 119 | IMPORT_PATH=$1 120 | while [ "$IMPORT_PATH" != "." ] && [ "$REPO_TYPE" == "" ]; do 121 | if [ -d "$GOPATH_ROOT/$IMPORT_PATH/.git" ]; then 122 | REPO_TYPE="git" 123 | elif [ -d "$GOPATH_ROOT/$IMPORT_PATH/.hg" ]; then 124 | REPO_TYPE="hg" 125 | fi 126 | IMPORT_PATH=$(dirname "$IMPORT_PATH") 127 | done 128 | 129 | if [ "$REPO_TYPE" == "" ]; then 130 | echo "Unknown version control system type, cannot switch remotes and branches." 131 | exit 255 132 | fi 133 | # If we have a valid VCS, execute the switch operations 134 | if [ "$REPO_REMOTE" != "" ]; then 135 | echo "Switching over to remote $REPO_REMOTE..." 136 | if [ "$REPO_TYPE" == "git" ]; then 137 | git remote set-url origin "$REPO_REMOTE" 138 | git fetch --all 139 | git reset --hard origin/HEAD 140 | git clean -dxf 141 | elif [ "$REPO_TYPE" == "hg" ]; then 142 | echo -e "[paths]\ndefault = $REPO_REMOTE\n" >> .hg/hgrc 143 | hg pull 144 | fi 145 | fi 146 | if [ "$REPO_BRANCH" != "" ]; then 147 | echo "Switching over to branch $REPO_BRANCH..." 148 | if [ "$REPO_TYPE" == "git" ]; then 149 | git reset --hard "origin/$REPO_BRANCH" 150 | git clean -dxf 151 | elif [ "$REPO_TYPE" == "hg" ]; then 152 | hg checkout "$REPO_BRANCH" 153 | fi 154 | fi 155 | fi 156 | fi 157 | 158 | # Download all the C dependencies 159 | mkdir /deps 160 | DEPS=("$DEPS") && for dep in "${DEPS[@]}"; do 161 | if [ "${dep##*.}" == "tar" ]; then tar -C /deps -x < "/deps-cache/$(basename "$dep")"; fi 162 | if [ "${dep##*.}" == "gz" ]; then tar -C /deps -xz < "/deps-cache/$(basename "$dep")"; fi 163 | if [ "${dep##*.}" == "bz2" ]; then tar -C /deps -xj < "/deps-cache/$(basename "$dep")"; fi 164 | done 165 | 166 | DEPS_ARGS=("$ARGS") 167 | 168 | # Save the contents of the pre-build /usr/local folder for post cleanup 169 | shopt -s nullglob 170 | USR_LOCAL_CONTENTS=(/usr/local/*) 171 | shopt -u nullglob 172 | 173 | 174 | # Configure some global build parameters 175 | NAME="$OUT" 176 | 177 | 178 | if [ "$NAME" == "" ]; then 179 | if [[ "$USEMODULES" = true && -d /source && -f /source/go.mod ]]; then 180 | NAME="$(sed -n 's/module\ \(.*\)/\1/p' /source/go.mod)" 181 | 182 | if [[ "$NAME" != "" && "$PACK" != "" ]]; then 183 | NAME="$NAME/$PACK" 184 | fi 185 | fi 186 | fi 187 | 188 | if [[ "$NAME" == "" ]]; then 189 | NAME="$(basename "$1/$PACK")" 190 | fi 191 | 192 | if [[ "$NAME" == "" || "$NAME" == "." ]]; then 193 | NAME="main" 194 | fi 195 | 196 | # Support go module package 197 | PACK_RELPATH="./$PACK" 198 | 199 | if [ "$FLAG_V" == "true" ]; then V=-v; LD+='-v'; fi 200 | if [ "$FLAG_X" == "true" ]; then X=-x; fi 201 | if [ "$FLAG_RACE" == "true" ]; then R=-race; fi 202 | if [ "$FLAG_TAGS" != "" ]; then T=(--tags "$FLAG_TAGS"); fi 203 | if [ "$FLAG_LDFLAGS" != "" ]; then LD=("${LD[@]}" "${FLAG_LDFLAGS[@]}"); fi 204 | if [ "$FLAG_GCFLAGS" != "" ]; then GC=(--gcflags="$(printf "%s " "${FLAG_GCFLAGS[@]}")"); fi 205 | 206 | if [ "$FLAG_BUILDMODE" != "" ] && [ "$FLAG_BUILDMODE" != "default" ]; then BM=(--buildmode="${FLAG_BUILDMODE[@]}"); fi 207 | if [ "$FLAG_TRIMPATH" == "true" ]; then TP=-trimpath; fi 208 | if [ "$FLAG_BUILDVCS" != "" ]; then BV=(--buildvcs="$FLAG_BUILDVCS"); fi 209 | if [ "$FLAG_MOD" != "" ]; then MOD=(--mod="$FLAG_MOD"); fi 210 | if [ "$FLAG_OBFUSCATE" == "true" ] && [ "$GO_VERSION_MAJOR" -eq 1 ] && [ "$GO_VERSION_MINOR" -ge 20 ]; then 211 | if [ "$GARBLE_FLAGS" != "" ]; then 212 | GOBIN="garble ${GARBLE_FLAGS[@]}" 213 | else 214 | GOBIN=garble 215 | fi 216 | else 217 | GOBIN=go 218 | fi 219 | 220 | # If no build targets were specified, inject a catch all wildcard 221 | if [ "$TARGETS" == "" ]; then 222 | TARGETS="./." 223 | fi 224 | 225 | if [ "${#LD[@]}" -gt 0 ]; then LDF=(--ldflags="$(printf "%s " "${LD[@]}")"); fi 226 | 227 | # source setup.sh if existing 228 | if [ -f "/hooksdir/setup.sh" ]; then echo "source setup.sh hook"; source "/hooksdir/setup.sh"; fi 229 | 230 | # Build for each requested platform individually 231 | for TARGET in $TARGETS; do 232 | # Split the target into platform and architecture 233 | XGOOS=$(echo $TARGET | cut -d '/' -f 1) 234 | XGOARCH=$(echo $TARGET | cut -d '/' -f 2) 235 | 236 | # Check and build for Linux targets 237 | if { [ "$XGOOS" == "." ] || [ "$XGOOS" == "linux" ]; } && { [ "$XGOARCH" == "." ] || [ "$XGOARCH" == "amd64" ]; }; then 238 | echo "Compiling for linux/amd64..." 239 | mkdir -p /gocache/linux/amd64 240 | XGOOS="linux" XGOARCH="amd64" GOCACHE=/gocache/linux/amd64 HOST=x86_64-linux PREFIX=/usr/local do_build 241 | if [[ "$USEMODULES" == false ]]; then 242 | GOCACHE=/gocache/linux/amd64 GOOS=linux GOARCH=amd64 CGO_ENABLED=1 go get $V $X "${T[@]}" -d "$PACK_RELPATH" 243 | fi 244 | GOCACHE=/gocache/linux/amd64 CC=x86_64-linux-gnu-gcc CXX=x86_64-linux-gnu-g++ GOOS=linux GOARCH=amd64 CGO_ENABLED=1 $GOBIN build $V $X $TP $BV "${MOD[@]}" "${T[@]}" "${LDF[@]}" "${GC[@]}" $R "${BM[@]}" -o "/build/$NAME-linux-amd64$R$(extension linux)" "$PACK_RELPATH" 245 | fi 246 | if { [ "$XGOOS" == "." ] || [ "$XGOOS" == "linux" ]; } && { [ "$XGOARCH" == "." ] || [ "$XGOARCH" == "386" ]; }; then 247 | echo "Compiling for linux/386..." 248 | mkdir -p /gocache/linux/386 249 | XGOOS="linux" XGOARCH="386" GOCACHE=/gocache/linux/386 CC="gcc -m32" CXX="g++ -m32" HOST=i686-linux PREFIX=/usr/local do_build 250 | if [[ "$USEMODULES" == false ]]; then 251 | GOCACHE=/gocache/linux/386 GOOS=linux GOARCH=386 CGO_ENABLED=1 go get $V $X "${T[@]}" -d "$PACK_RELPATH" 252 | fi 253 | GOCACHE=/gocache/linux/386 GOOS=linux GOARCH=386 CGO_ENABLED=1 $GOBIN build $V $X $TP $BV "${MOD[@]}" "${T[@]}" "${LDF[@]}" "${GC[@]}" "${BM[@]}" -o "/build/$NAME-linux-386$(extension linux)" "$PACK_RELPATH" 254 | fi 255 | if { [ "$XGOOS" == "." ] || [ "$XGOOS" == "linux" ]; } && { [ "$XGOARCH" == "." ] || [ "$XGOARCH" == "arm" ] || [ "$XGOARCH" == "arm-5" ]; }; then 256 | mkdir -p /gocache/linux/arm-5 257 | if [ "$GO_VERSION_MAJOR" -gt 1 ] || { [ "$GO_VERSION_MAJOR" == 1 ] && [ "$GO_VERSION_MINOR" -ge 15 ]; }; then 258 | ln -s /usr/local/go/pkg/linux_arm-5 /usr/local/go/pkg/linux_arm 259 | fi 260 | echo "Compiling for linux/arm-5..." 261 | XGOOS="linux" XGOARCH="arm-5" GOCACHE=/gocache/linux/arm-5 CC=arm-linux-gnueabi-gcc CXX=arm-linux-gnueabihf-g++ HOST=arm-linux-gnueabi-gcc PREFIX=/usr/arm-linux-gnueabihf CFLAGS="-march=armv5t" CXXFLAGS="-march=armv5t" do_build 262 | export PKG_CONFIG_PATH=/usr/arm-linux-gnueabi/lib/pkgconfig 263 | 264 | if [[ "$USEMODULES" == false ]]; then 265 | GOCACHE=/gocache/linux/arm-5 CC=arm-linux-gnueabi-gcc CXX=arm-linux-gnueabihf-g++ GOOS=linux GOARCH=arm GOARM=5 CGO_ENABLED=1 CGO_CFLAGS="-march=armv5t" CGO_CXXFLAGS="-march=armv5t" go get $V $X "${T[@]}" -d "$PACK_RELPATH" 266 | fi 267 | GOCACHE=/gocache/linux/arm-5 CC=arm-linux-gnueabi-gcc CXX=arm-linux-gnueabihf-g++ GOOS=linux GOARCH=arm GOARM=5 CGO_ENABLED=1 CGO_CFLAGS="-march=armv5t" CGO_CXXFLAGS="-march=armv5t" $GOBIN build $V $X $TP $BV "${MOD[@]}" "${T[@]}" "${LDF[@]}" "${GC[@]}" "${BM[@]}" -o "/build/$NAME-linux-arm-5$(extension linux)" "$PACK_RELPATH" 268 | if [ "$GO_VERSION_MAJOR" -gt 1 ] || { [ "$GO_VERSION_MAJOR" == 1 ] && [ "$GO_VERSION_MINOR" -ge 15 ]; }; then 269 | rm /usr/local/go/pkg/linux_arm 270 | fi 271 | fi 272 | if { [ "$XGOOS" == "." ] || [ "$XGOOS" == "linux" ]; } && { [ "$XGOARCH" == "." ] || [ "$XGOARCH" == "arm-6" ]; }; then 273 | mkdir -p /gocache/linux/arm-6 274 | ln -s /usr/local/go/pkg/linux_arm-6 /usr/local/go/pkg/linux_arm 275 | 276 | echo "Compiling for linux/arm-6..." 277 | XGOOS="linux" XGOARCH="arm-6" GOCACHE=/gocache/linux/arm-6 CC=arm-linux-gnueabi-gcc CXX=arm-linux-gnueabihf-g++ HOST=arm-linux-gnueabi-gcc PREFIX=/usr/arm-linux-gnueabihf CFLAGS="-march=armv6" CXXFLAGS="-march=armv6" do_build 278 | export PKG_CONFIG_PATH=/usr/arm-linux-gnueabi/lib/pkgconfig 279 | 280 | if [[ "$USEMODULES" == false ]]; then 281 | GOCACHE=/gocache/linux/arm-6 CC=arm-linux-gnueabi-gcc CXX=arm-linux-gnueabihf-g++ GOOS=linux GOARCH=arm GOARM=6 CGO_ENABLED=1 CGO_CFLAGS="-march=armv6" CGO_CXXFLAGS="-march=armv6" go get $V $X "${T[@]}" -d "$PACK_RELPATH" 282 | fi 283 | GOCACHE=/gocache/linux/arm-6 CC=arm-linux-gnueabi-gcc CXX=arm-linux-gnueabihf-g++ GOOS=linux GOARCH=arm GOARM=6 CGO_ENABLED=1 CGO_CFLAGS="-march=armv6" CGO_CXXFLAGS="-march=armv6" $GOBIN build $V $X $TP $BV "${MOD[@]}" "${T[@]}" "${LDF[@]}" "${GC[@]}" "${BM[@]}" -o "/build/$NAME-linux-arm-6$(extension linux)" "$PACK_RELPATH" 284 | 285 | rm /usr/local/go/pkg/linux_arm 286 | fi 287 | if { [ "$XGOOS" == "." ] || [ "$XGOOS" == "linux" ]; } && { [ "$XGOARCH" == "." ] || [ "$XGOARCH" == "arm-7" ]; }; then 288 | mkdir -p /gocache/linux/arm-7 289 | ln -s /usr/local/go/pkg/linux_arm-7 /usr/local/go/pkg/linux_arm 290 | 291 | echo "Compiling for linux/arm-7..." 292 | XGOOS="linux" XGOARCH="arm-7" GOCACHE=/gocache/linux/arm-7 CC=arm-linux-gnueabi-gcc CXX=arm-linux-gnueabihf-g++ HOST=arm-linux-gnueabi-gcc PREFIX=/usr/arm-linux-gnueabi CFLAGS="-march=armv7-a -fPIC" CXXFLAGS="-march=armv7-a -fPIC" do_build 293 | export PKG_CONFIG_PATH=/usr/arm-linux-gnueabi/lib/pkgconfig 294 | 295 | if [[ "$USEMODULES" == false ]]; then 296 | GOCACHE=/gocache/linux/arm-7 CC=arm-linux-gnueabi-gcc CXX=arm-linux-gnueabihf-g++ GOOS=linux GOARCH=arm GOARM=7 CGO_ENABLED=1 CGO_CFLAGS="-march=armv7-a -fPIC" CGO_CXXFLAGS="-march=armv7-a -fPIC" go get $V $X "${T[@]}" -d "$PACK_RELPATH" 297 | fi 298 | GOCACHE=/gocache/linux/arm-7 CC=arm-linux-gnueabi-gcc CXX=arm-linux-gnueabihf-g++ GOOS=linux GOARCH=arm GOARM=7 CGO_ENABLED=1 CGO_CFLAGS="-march=armv7-a -fPIC" CGO_CXXFLAGS="-march=armv7-a -fPIC" $GOBIN build $V $X $TP $BV "${MOD[@]}" "${T[@]}" "${LDF[@]}" "${GC[@]}" "${BM[@]}" -o "/build/$NAME-linux-arm-7$(extension linux)" "$PACK_RELPATH" 299 | 300 | rm /usr/local/go/pkg/linux_arm 301 | fi 302 | if { [ "$XGOOS" == "." ] || [ "$XGOOS" == "linux" ]; } && { [ "$XGOARCH" == "." ] || [ "$XGOARCH" == "arm64" ]; }; then 303 | echo "Compiling for linux/arm64..." 304 | mkdir -p /gocache/linux/arm64 305 | XGOOS="linux" XGOARCH="arm64" GOCACHE=/gocache/linux/arm64 CC=aarch64-linux-gnu-gcc CXX=aarch64-linux-gnu-g++ PREFIX=/usr/aarch64-linux-gnu-gcc/ do_build 306 | export PKG_CONFIG_PATH=/usr/aarch64-linux-gnu-gcc/lib/pkgconfig 307 | 308 | if [[ "$USEMODULES" == false ]]; then 309 | GOCACHE=/gocache/linux/arm64 CC=aarch64-linux-gnu-gcc CXX=aarch64-linux-gnu-g++ GOOS=linux GOARCH=arm64 CGO_ENABLED=1 go get $V $X "${T[@]}" -d "$PACK_RELPATH" 310 | fi 311 | GOCACHE=/gocache/linux/arm64 CC=aarch64-linux-gnu-gcc CXX=aarch64-linux-gnu-g++ GOOS=linux GOARCH=arm64 CGO_ENABLED=1 $GOBIN build $V $X $TP $BV "${MOD[@]}" "${T[@]}" "${LDF[@]}" "${GC[@]}" "${BM[@]}" -o "/build/$NAME-linux-arm64$(extension linux)" "$PACK_RELPATH" 312 | fi 313 | if { [ "$XGOOS" == "." ] || [ "$XGOOS" == "linux" ]; } && { [ "$XGOARCH" == "." ] || [ "$XGOARCH" == "mips64" ]; }; then 314 | echo "Compiling for linux/mips64..." 315 | mkdir -p /gocache/linux/mips64 316 | XGOOS="linux" XGOARCH="mips64" GOCACHE=/gocache/linux/mips64 CC=mips64-linux-gnuabi64-gcc CXX=mips64-linux-gnuabi64-g++ HOST=mips64-linux-gnuabi64 PREFIX=/usr/mips64-linux-gnuabi64 do_build 317 | export PKG_CONFIG_PATH=/usr/mips64-linux-gnuabi64/lib/pkgconfig 318 | 319 | if [[ "$USEMODULES" == false ]]; then 320 | GOCACHE=/gocache/linux/mips64 CC=mips64-linux-gnuabi64-gcc CXX=mips64-linux-gnuabi64-g++ GOOS=linux GOARCH=mips64 CGO_ENABLED=1 go get $V $X "${T[@]}" -d "$PACK_RELPATH" 321 | fi 322 | GOCACHE=/gocache/linux/mips64 CC=mips64-linux-gnuabi64-gcc CXX=mips64-linux-gnuabi64-g++ GOOS=linux GOARCH=mips64 CGO_ENABLED=1 $GOBIN build $V $X $TP $BV "${MOD[@]}" "${T[@]}" "${LDF[@]}" "${GC[@]}" "${BM[@]}" -o "/build/$NAME-linux-mips64$(extension linux)" "$PACK_RELPATH" 323 | fi 324 | if { [ "$XGOOS" == "." ] || [ "$XGOOS" == "linux" ]; } && { [ "$XGOARCH" == "." ] || [ "$XGOARCH" == "mips64le" ]; }; then 325 | echo "Compiling for linux/mips64le..." 326 | mkdir -p /gocache/linux/mips64le 327 | XGOOS="linux" XGOARCH="mips64le" GOCACHE=/gocache/linux/mips64le CC=mips64el-linux-gnuabi64-gcc CXX=mips64el-linux-gnuabi64-g++ HOST=mips64el-linux-gnuabi64 PREFIX=/usr/mips64el-linux-gnuabi64 do_build 328 | export PKG_CONFIG_PATH=/usr/mips64le-linux-gnuabi64/lib/pkgconfig 329 | 330 | if [[ "$USEMODULES" == false ]]; then 331 | GOCACHE=/gocache/linux/mips64le CC=mips64el-linux-gnuabi64-gcc CXX=mips64el-linux-gnuabi64-g++ GOOS=linux GOARCH=mips64le CGO_ENABLED=1 go get $V $X "${T[@]}" -d "$PACK_RELPATH" 332 | fi 333 | GOCACHE=/gocache/linux/mips64le CC=mips64el-linux-gnuabi64-gcc CXX=mips64el-linux-gnuabi64-g++ GOOS=linux GOARCH=mips64le CGO_ENABLED=1 $GOBIN build $V $X $TP $BV "${MOD[@]}" "${T[@]}" "${LDF[@]}" "${GC[@]}" "${BM[@]}" -o "/build/$NAME-linux-mips64le$(extension linux)" "$PACK_RELPATH" 334 | fi 335 | if { [ "$XGOOS" == "." ] || [ "$XGOOS" == "linux" ]; } && { [ "$XGOARCH" == "." ] || [ "$XGOARCH" == "mips" ]; }; then 336 | echo "Compiling for linux/mips..." 337 | mkdir -p /gocache/linux/mips 338 | XGOOS="linux" XGOARCH="mips" GOCACHE=/gocache/linux/mips CC=mips-linux-gnu-gcc CXX=mips-linux-gnu-g++ HOST=mips-linux-gnu PREFIX=/usr/mips-linux-gnu do_build 339 | export PKG_CONFIG_PATH=/usr/mips-linux-gnu/lib/pkgconfig 340 | 341 | if [[ "$USEMODULES" == false ]]; then 342 | GOCACHE=/gocache/linux/mips CC=mips-linux-gnu-gcc CXX=mips-linux-gnu-g++ GOOS=linux GOARCH=mips CGO_ENABLED=1 go get $V $X "${T[@]}" -d "$PACK_RELPATH" 343 | fi 344 | GOCACHE=/gocache/linux/mips CC=mips-linux-gnu-gcc CXX=mips-linux-gnu-g++ GOOS=linux GOARCH=mips CGO_ENABLED=1 $GOBIN build $V $X $TP $BV "${MOD[@]}" "${T[@]}" "${LDF[@]}" "${GC[@]}" "${BM[@]}" -o "/build/$NAME-linux-mips$(extension linux)" "$PACK_RELPATH" 345 | fi 346 | if { [ "$XGOOS" == "." ] || [ "$XGOOS" == "linux" ]; } && { [ "$XGOARCH" == "." ] || [ "$XGOARCH" == "s390x" ]; }; then 347 | echo "Compiling for linux/s390x..." 348 | mkdir -p /gocache/linux/s390x 349 | XGOOS="linux" XGOARCH="s390x" GOCACHE=/gocache/linux/s390x CC=s390x-linux-gnu-gcc CXX=s390x-linux-gnu-g++ HOST=s390x-linux-gnu PREFIX=/usr/s390x-linux-gnu do_build 350 | export PKG_CONFIG_PATH=/usr/s390x-linux-gnu/lib/pkgconfig 351 | 352 | if [[ "$USEMODULES" == false ]]; then 353 | GOCACHE=/gocache/linux/s390x CC=s390x-linux-gnu-gcc CXX=s390x-linux-gnu-g++ GOOS=linux GOARCH=s390x CGO_ENABLED=1 go get $V $X "${T[@]}" -d "$PACK_RELPATH" 354 | fi 355 | GOCACHE=/gocache/linux/s390x CC=s390x-linux-gnu-gcc CXX=s390x-linux-gnu-g++ GOOS=linux GOARCH=s390x CGO_ENABLED=1 $GOBIN build $V $X $TP $BV "${MOD[@]}" "${T[@]}" "${LDF[@]}" "${GC[@]}" "${BM[@]}" -o "/build/$NAME-linux-s390x$(extension linux)" "$PACK_RELPATH" 356 | fi 357 | if { [ "$XGOOS" == "." ] || [ "$XGOOS" == "linux" ]; } && { [ "$XGOARCH" == "." ] || [ "$XGOARCH" == "riscv64" ]; }; then 358 | echo "Compiling for linux/riscv64..." 359 | mkdir -p /gocache/linux/riscv64 360 | XGOOS="linux" XGOARCH="riscv64" GOCACHE=/gocache/linux/riscv64 CC=riscv64-linux-gnu-gcc CXX=riscv64-linux-gnu-g++ HOST=riscv64-linux-gnu PREFIX=/usr/riscv64-linux-gnu do_build 361 | export PKG_CONFIG_PATH=/usr/riscv64-linux-gnu/lib/pkgconfig 362 | 363 | if [[ "$USEMODULES" == false ]]; then 364 | GOCACHE=/gocache/linux/riscv64 CC=riscv64-linux-gnu-gcc CXX=riscv64-linux-gnu-g++ GOOS=linux GOARCH=riscv64 CGO_ENABLED=1 go get $V $X "${T[@]}" -d "$PACK_RELPATH" 365 | fi 366 | GOCACHE=/gocache/linux/riscv64 CC=riscv64-linux-gnu-gcc CXX=riscv64-linux-gnu-g++ GOOS=linux GOARCH=riscv64 CGO_ENABLED=1 $GOBIN build $V $X $TP $BV "${MOD[@]}" "${T[@]}" "${LDF[@]}" "${GC[@]}" "${BM[@]}" -o "/build/$NAME-linux-riscv64$(extension linux)" "$PACK_RELPATH" 367 | fi 368 | if { [ "$XGOOS" == "." ] || [ "$XGOOS" == "linux" ]; } && { [ "$XGOARCH" == "." ] || [ "$XGOARCH" == "ppc64le" ]; }; then 369 | echo "Compiling for linux/ppc64le..." 370 | mkdir -p /gocache/linux/ppc64le 371 | XGOOS="linux" XGOARCH="ppc64le" GOCACHE=/gocache/linux/ppc64le CC=powerpc64le-linux-gnu-gcc CXX=powerpc64le-linux-gnu-g++ HOST=ppc64le-linux-gnu PREFIX=/usr/ppc64le-linux-gnu do_build 372 | export PKG_CONFIG_PATH=/usr/ppc64le-linux-gnu/lib/pkgconfig 373 | 374 | if [[ "$USEMODULES" == false ]]; then 375 | GOCACHE=/gocache/linux/ppc64le CC=powerpc64le-linux-gnu-gcc CXX=powerpc64le-linux-gnu-g++ GOOS=linux GOARCH=ppc64le CGO_ENABLED=1 go get $V $X "${T[@]}" -d "$PACK_RELPATH" 376 | fi 377 | GOCACHE=/gocache/linux/ppc64le CC=powerpc64le-linux-gnu-gcc CXX=powerpc64le-linux-gnu-g++ GOOS=linux GOARCH=ppc64le CGO_ENABLED=1 $GOBIN build $V $X $TP $BV "${MOD[@]}" "${T[@]}" "${LDF[@]}" "${GC[@]}" "${BM[@]}" -o "/build/$NAME-linux-ppc64le$(extension linux)" "$PACK_RELPATH" 378 | fi 379 | if { [ "$XGOOS" == "." ] || [ "$XGOOS" == "linux" ]; } && { [ "$XGOARCH" == "." ] || [ "$XGOARCH" == "mipsle" ]; }; then 380 | echo "Compiling for linux/mipsle..." 381 | mkdir -p /gocache/linux/mipsle 382 | XGOOS="linux" XGOARCH="mipsle" GOCACHE=/gocache/linux/mipsle CC=mipsel-linux-gnu-gcc CXX=mipsel-linux-gnu-g++ HOST=mipsel-linux-gnu PREFIX=/usr/mipsel-linux-gnu do_build 383 | export PKG_CONFIG_PATH=/usr/mipsle-linux-gnu/lib/pkgconfig 384 | 385 | if [[ "$USEMODULES" == false ]]; then 386 | GOCACHE=/gocache/linux/mipsle CC=mipsel-linux-gnu-gcc CXX=mipsel-linux-gnu-g++ GOOS=linux GOARCH=mipsle CGO_ENABLED=1 go get $V $X "${T[@]}" -d "$PACK_RELPATH" 387 | fi 388 | GOCACHE=/gocache/linux/mipsle CC=mipsel-linux-gnu-gcc CXX=mipsel-linux-gnu-g++ GOOS=linux GOARCH=mipsle CGO_ENABLED=1 $GOBIN build $V $X $TP $BV "${MOD[@]}" "${T[@]}" "${LDF[@]}" "${GC[@]}" "${BM[@]}" -o "/build/$NAME-linux-mipsle$(extension linux)" "$PACK_RELPATH" 389 | fi 390 | # Check and build for Windows targets 391 | if [ "$XGOOS" == "." ] || [[ "$XGOOS" == windows* ]]; then 392 | # Split the platform version and configure the Windows NT version 393 | PLATFORM=$(echo "$XGOOS" | cut -d '-' -f 2) 394 | if [ "$PLATFORM" == "" ] || [ "$PLATFORM" == "." ] || [ "$PLATFORM" == "windows" ]; then 395 | PLATFORM=4.0 # Windows NT 396 | fi 397 | 398 | MAJOR=$(echo "$PLATFORM" | cut -d '.' -f 1) 399 | if [ "${PLATFORM/.}" != "$PLATFORM" ] ; then 400 | MINOR=$(echo "$PLATFORM" | cut -d '.' -f 2) 401 | fi 402 | CGO_NTDEF="-D_WIN32_WINNT=0x$(printf "%02d" "$MAJOR")$(printf "%02d" "$MINOR")" 403 | 404 | # Build the requested windows binaries 405 | if [ "$XGOARCH" == "." ] || [ "$XGOARCH" == "amd64" ]; then 406 | echo "Compiling for windows-$PLATFORM/amd64..." 407 | mkdir -p /gocache/windows-$PLATFORM/amd64 408 | XGOOS="windows-$PLATFORM" XGOARCH="amd64" GOCACHE=/gocache/windows-$PLATFORM/amd64 CC=x86_64-w64-mingw32-gcc-posix CXX=x86_64-w64-mingw32-g++-posix HOST=x86_64-w64-mingw32 PREFIX=/usr/x86_64-w64-mingw32 do_build 409 | export PKG_CONFIG_PATH=/usr/x86_64-w64-mingw32/lib/pkgconfig 410 | 411 | if [[ "$USEMODULES" == false ]]; then 412 | GOCACHE=/gocache/windows-$PLATFORM/amd64 CC=x86_64-w64-mingw32-gcc-posix CXX=x86_64-w64-mingw32-g++-posix GOOS=windows GOARCH=amd64 CGO_ENABLED=1 CGO_CFLAGS="$CGO_NTDEF" CGO_CXXFLAGS="$CGO_NTDEF" go get $V $X "${T[@]}" -d "$PACK_RELPATH" 413 | fi 414 | GOCACHE=/gocache/windows-$PLATFORM/amd64 CC=x86_64-w64-mingw32-gcc-posix CXX=x86_64-w64-mingw32-g++-posix GOOS=windows GOARCH=amd64 CGO_ENABLED=1 CGO_CFLAGS="$CGO_NTDEF" CGO_CXXFLAGS="$CGO_NTDEF" $GOBIN build $V $X $TP $BV "${MOD[@]}" "${T[@]}" "${LDF[@]}" "${GC[@]}" $R "${BM[@]}" -o "/build/$NAME-windows-$PLATFORM-amd64$R$(extension windows)" "$PACK_RELPATH" 415 | fi 416 | if [ "$XGOARCH" == "." ] || [ "$XGOARCH" == "386" ]; then 417 | echo "Compiling for windows-$PLATFORM/386..." 418 | mkdir -p /gocache/windows-$PLATFORM/386 419 | XGOOS="windows-$PLATFORM" XGOARCH="386" GOCACHE=/gocache/windows-$PLATFORM/386 CC=i686-w64-mingw32-gcc-posix CXX=i686-w64-mingw32-g++-posix HOST=i686-w64-mingw32 PREFIX=/usr/i686-w64-mingw32 do_build 420 | export PKG_CONFIG_PATH=/usr/i686-w64-mingw32/lib/pkgconfig 421 | 422 | if [[ "$USEMODULES" == false ]]; then 423 | GOCACHE=/gocache/windows-$PLATFORM/386 CC=i686-w64-mingw32-gcc-posix CXX=i686-w64-mingw32-g++-posix GOOS=windows GOARCH=386 CGO_ENABLED=1 CGO_CFLAGS="$CGO_NTDEF" CGO_CXXFLAGS="$CGO_NTDEF" go get $V $X "${T[@]}" -d "$PACK_RELPATH" 424 | fi 425 | GOCACHE=/gocache/windows-$PLATFORM/386 CC=i686-w64-mingw32-gcc-posix CXX=i686-w64-mingw32-g++-posix GOOS=windows GOARCH=386 CGO_ENABLED=1 CGO_CFLAGS="$CGO_NTDEF" CGO_CXXFLAGS="$CGO_NTDEF" $GOBIN build $V $X $TP $BV "${MOD[@]}" "${T[@]}" "${LDF[@]}" "${GC[@]}" "${BM[@]}" -o "/build/$NAME-windows-$PLATFORM-386$(extension windows)" "$PACK_RELPATH" 426 | fi 427 | fi 428 | # Check and build for OSX targets 429 | if [ "$XGOOS" == "." ] || [[ "$XGOOS" == darwin* ]]; then 430 | # Split the platform version and configure the deployment target 431 | PLATFORM=$(echo "$XGOOS" | cut -d '-' -f 2) 432 | if [ "$PLATFORM" == "" ] || [ "$PLATFORM" == "." ] || [ "$PLATFORM" == "darwin" ]; then 433 | PLATFORM=10.12 # OS X Sierra (min version support for golang) 434 | fi 435 | export MACOSX_DEPLOYMENT_TARGET=$PLATFORM 436 | 437 | # Strip symbol table below Go 1.6 to prevent DWARF issues 438 | LDS=("${LD[@]}") 439 | if [ "$GO_VERSION_MAJOR" -lt 1 ] || { [ "$GO_VERSION_MAJOR" == 1 ] && [ "$GO_VERSION_MINOR" -lt 16 ]; }; then 440 | LDS=("-s" "${LDS[@]}") 441 | fi 442 | if [ ${#LDS[@]} -gt 0 ]; then 443 | LDFS=(--ldflags="$(printf "%s " "${LD[@]}")") 444 | fi 445 | # Build the requested darwin binaries 446 | if [ "$XGOARCH" == "." ] || [ "$XGOARCH" == "amd64" ]; then 447 | echo "Compiling for darwin-$PLATFORM/amd64..." 448 | mkdir -p /gocache/darwin-$PLATFORM/amd64 449 | XGOOS="darwin-$PLATFORM" XGOARCH="amd64" GOCACHE=/gocache/darwin-$PLATFORM/amd64 CC=o64-clang CXX=o64-clang++ HOST=x86_64-apple-darwin15 PREFIX=/usr/local do_build 450 | if [[ "$USEMODULES" == false ]]; then 451 | GOCACHE=/gocache/darwin-$PLATFORM/amd64 CC=o64-clang CXX=o64-clang++ GOOS=darwin GOARCH=amd64 CGO_ENABLED=1 go get $V $X "${T[@]}" "${LDFS[@]}" "${GC[@]}" -d "$PACK_RELPATH" 452 | fi 453 | GOCACHE=/gocache/darwin-$PLATFORM/amd64 CC=o64-clang CXX=o64-clang++ GOOS=darwin GOARCH=amd64 CGO_ENABLED=1 $GOBIN build $V $X $TP $BV "${MOD[@]}" "${T[@]}" "${LDFS[@]}" "${GC[@]}" $R "${BM[@]}" -o "/build/$NAME-darwin-$PLATFORM-amd64$R$(extension darwin)" "$PACK_RELPATH" 454 | fi 455 | if [ "$XGOARCH" == "." ] || [ "$XGOARCH" == "arm64" ]; then 456 | if [ "$GO_VERSION_MAJOR" -lt 1 ] || { [ "$GO_VERSION_MAJOR" == 1 ] && [ "$GO_VERSION_MINOR" -lt 16 ]; }; then 457 | echo "Go version too low, skipping darwin-$PLATFORM/arm64..." 458 | else 459 | echo "Compiling for darwin-$PLATFORM/arm64..." 460 | mkdir -p /gocache/darwin-$PLATFORM/arm64 461 | XGOOS="darwin-$PLATFORM" XGOARCH="arm64" GOCACHE=/gocache/darwin-$PLATFORM/arm64 CC=o64-clang CXX=o64-clang++ HOST=arm64-apple-darwin15 PREFIX=/usr/local do_build 462 | if [[ "$USEMODULES" == false ]]; then 463 | GOCACHE=/gocache/darwin-$PLATFORM/arm64 CC=o64-clang CXX=o64-clang++ GOOS=darwin GOARCH=arm64 CGO_ENABLED=1 go get $V $X "${T[@]}" "${LDFS[@]}" "${GC[@]}" -d "$PACK_RELPATH" 464 | fi 465 | GOCACHE=/gocache/darwin-$PLATFORM/arm64 CC=o64-clang CXX=o64-clang++ GOOS=darwin GOARCH=arm64 CGO_ENABLED=1 $GOBIN build $V $X $TP $BV "${MOD[@]}" "${T[@]}" "${LDFS[@]}" "${GC[@]}" $R "${BM[@]}" -o "/build/$NAME-darwin-$PLATFORM-arm64$R$(extension darwin)" "$PACK_RELPATH" 466 | fi 467 | fi 468 | # Remove any automatically injected deployment target vars 469 | unset MACOSX_DEPLOYMENT_TARGET 470 | fi 471 | # Check and build for freebsd targets 472 | if [ "$XGOOS" == "." ] || [[ "$XGOOS" == freebsd* ]]; then 473 | # Build the requested freebsd binaries 474 | if [ "$XGOARCH" == "." ] || [ "$XGOARCH" == "amd64" ]; then 475 | echo "Compiling for freebsd/amd64..." 476 | XGOOS="freebsd" XGOARCH="amd64" CC=x86_64-pc-freebsd13-gcc HOST=x86_64-pc-freebsd13 PREFIX=/freebsdcross/x86_64-pc-freebsd13 do_build 477 | export PKG_CONFIG_PATH=/freebsdcross/x86_64-pc-freebsd13/lib/pkgconfig 478 | 479 | if [[ "$USEMODULES" == false ]]; then 480 | CC=x86_64-pc-freebsd13-gcc CXX=x86_64-pc-freebsd13-g++ GOOS=freebsd GOARCH=amd64 CGO_ENABLED=1 go get $V $X "${T[@]}" -d "$PACK_RELPATH" 481 | fi 482 | CC=x86_64-pc-freebsd13-gcc CXX=x86_64-pc-freebsd13-g++ GOOS=freebsd GOARCH=amd64 CGO_ENABLED=1 $GOBIN build $V $X $TP $BV "${MOD[@]}" "${T[@]}" "${LDF[@]}" "${GC[@]}" "${BM[@]}" -o "/build/$NAME-freebsd13-amd64$(extension freebsd)" "$PACK_RELPATH" 483 | fi 484 | if [ "$XGOARCH" == "." ] || [ "$XGOARCH" == "arm64" ]; then 485 | echo "skipping freebsd/arm64... as it is not yet supported" 486 | # TODO: add arm64 support 487 | fi 488 | fi 489 | done 490 | 491 | # Clean up any leftovers for subsequent build invocations 492 | echo "Cleaning up build environment..." 493 | rm -rf /deps 494 | 495 | for dir in /usr/local/*; do 496 | keep=0 497 | 498 | # Check against original folder contents 499 | for old in "${USR_LOCAL_CONTENTS[@]}"; do 500 | if [ "$old" == "$dir" ]; then 501 | keep=1 502 | fi 503 | done 504 | # Delete anything freshly generated 505 | if [ "$keep" == "0" ]; then 506 | rm -rf "$dir" 507 | fi 508 | done 509 | 510 | # set owner of created executables to owner of the /build directory (all executables and created directories start with $NAME) 511 | chown -R --reference /build /build/"$NAME"* 512 | -------------------------------------------------------------------------------- /docker/go-1.23.10/Dockerfile: -------------------------------------------------------------------------------- 1 | ## GENERATED. DO NOT EDIT DIRECTLY. 2 | FROM toolchain 3 | 4 | ARG TARGETPLATFORM 5 | ENV GO_VERSION=12310 6 | 7 | RUN \ 8 | if [ "$TARGETPLATFORM" = "linux/amd64" ]; then \ 9 | export ROOT_DIST=https://dl.google.com/go/go1.23.10.linux-amd64.tar.gz && \ 10 | export ROOT_DIST_SHA=535f9f81802499f2a7dbfa70abb8fda3793725fcc29460f719815f6e10b5fd60;\ 11 | elif [ "$TARGETPLATFORM" = "linux/arm64" ]; then \ 12 | export ROOT_DIST=https://dl.google.com/go/go1.23.10.linux-arm64.tar.gz && \ 13 | export ROOT_DIST_SHA=bfb1f1df7173f44648ee070a39ab0481068632f595305a699d89cd56a33b8081;\ 14 | else \ 15 | echo "Unsupported architecture: $TARGETPLATFORM" && exit 1; \ 16 | fi && \ 17 | $BOOTSTRAP_PURE 18 | -------------------------------------------------------------------------------- /docker/go-1.23.x/Dockerfile: -------------------------------------------------------------------------------- 1 | ## GENERATED. DO NOT EDIT DIRECTLY. 2 | FROM go-1.23.10 3 | -------------------------------------------------------------------------------- /docker/go-1.24.4/Dockerfile: -------------------------------------------------------------------------------- 1 | ## GENERATED. DO NOT EDIT DIRECTLY. 2 | FROM toolchain 3 | 4 | ARG TARGETPLATFORM 5 | ENV GO_VERSION=1244 6 | 7 | RUN \ 8 | if [ "$TARGETPLATFORM" = "linux/amd64" ]; then \ 9 | export ROOT_DIST=https://dl.google.com/go/go1.24.4.linux-amd64.tar.gz && \ 10 | export ROOT_DIST_SHA=77e5da33bb72aeaef1ba4418b6fe511bc4d041873cbf82e5aa6318740df98717;\ 11 | elif [ "$TARGETPLATFORM" = "linux/arm64" ]; then \ 12 | export ROOT_DIST=https://dl.google.com/go/go1.24.4.linux-arm64.tar.gz && \ 13 | export ROOT_DIST_SHA=d5501ee5aca0f258d5fe9bfaed401958445014495dc115f202d43d5210b45241;\ 14 | else \ 15 | echo "Unsupported architecture: $TARGETPLATFORM" && exit 1; \ 16 | fi && \ 17 | $BOOTSTRAP_PURE 18 | -------------------------------------------------------------------------------- /docker/go-1.24.x/Dockerfile: -------------------------------------------------------------------------------- 1 | ## GENERATED. DO NOT EDIT DIRECTLY. 2 | FROM go-1.24.4 3 | -------------------------------------------------------------------------------- /docker/go-latest/Dockerfile: -------------------------------------------------------------------------------- 1 | ## GENERATED. DO NOT EDIT DIRECTLY. 2 | FROM techknowlogick/xgo:go-1.24.x 3 | -------------------------------------------------------------------------------- /docker/toolchain/Dockerfile: -------------------------------------------------------------------------------- 1 | # Go cross compiler (xgo): toolchain cross-compilation layer 2 | # Copyright (c) 2014 Péter Szilágyi. All rights reserved. 3 | # Copyright (c) 2019 techknowlogick 4 | # 5 | # Released under the MIT license. 6 | 7 | FROM ubuntu:24.04 8 | 9 | LABEL maintainer="techknowlogick " 10 | 11 | # Configure the Go environment, since it's not going to change 12 | ENV PATH=/usr/local/go/bin:$PATH 13 | ENV GOPATH=/go 14 | ENV DEBIAN_FRONTEND=noninteractive 15 | 16 | # Inject the remote file fetcher and checksum verifier 17 | COPY fetch.sh /fetch.sh 18 | ENV FETCH=/fetch.sh 19 | RUN chmod +x $FETCH 20 | 21 | # Make sure apt-get is up to date and dependent packages are installed 22 | SHELL ["/bin/bash", "-o", "pipefail", "-c"] 23 | # trunk-ignore(hadolint/DL3008) 24 | # trunk-ignore(hadolint/DL3015) 25 | RUN apt-get update -y && \ 26 | apt-get install --fix-broken -y \ 27 | automake autogen build-essential clang gcc-aarch64-linux-gnu g++-aarch64-linux-gnu \ 28 | gcc-arm-linux-gnueabi g++-arm-linux-gnueabi libc6-dev-armel-cross \ 29 | gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf libc6-dev-armhf-cross \ 30 | gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu \ 31 | gcc-mingw-w64 g++-mingw-w64 llvm-dev gcc-multilib-mipsel-linux-gnu \ 32 | gcc-multilib-mipsisa32r6-linux-gnu gcc-multilib-mipsisa32r6el-linux-gnu \ 33 | gcc-multilib-mipsisa64r6-linux-gnuabi64 gcc-multilib-mipsisa64r6el-linux-gnuabi64 \ 34 | gcc-multilib-powerpc-linux-gnu \ 35 | gcc-multilib-s390x-linux-gnu gcc-multilib-sparc64-linux-gnu \ 36 | gcc-multilib-x86-64-linux-gnux32 \ 37 | gcc-mips-linux-gnu g++-mips-linux-gnu libc6-dev-mips-cross \ 38 | gcc-mips64-linux-gnuabi64 g++-mips64-linux-gnuabi64 libc6-dev-mips64-cross \ 39 | gcc-multilib-mips64el-linux-gnuabi64 gcc-mips64el-linux-gnuabi64 g++-mips64el-linux-gnuabi64 \ 40 | libc6-dev-mips64el-cross \ 41 | gcc-mipsel-linux-gnu g++-mipsel-linux-gnu libc6-dev-mipsel-cross \ 42 | gcc-multilib-i686-linux-gnu gcc-multilib-mips-linux-gnu gcc-multilib-mips64-linux-gnuabi64 \ 43 | gcc-multilib-x86-64-linux-gnux32 \ 44 | gcc-powerpc64le-linux-gnu g++-powerpc64le-linux-gnu libc6-dev-powerpc-ppc64-cross \ 45 | gcc-riscv64-linux-gnu g++-riscv64-linux-gnu libc6-dev-riscv64-cross gcc-9-riscv64-linux-gnu \ 46 | gcc-s390x-linux-gnu g++-s390x-linux-gnu libc6-dev-s390x-cross \ 47 | gcc-9-mipsel-linux-gnu gcc-9-mips-linux-gnu gcc-9-mips64-linux-gnuabi64 gcc-9-mips64el-linux-gnuabi64 \ 48 | zlib1g-dev 49 | 50 | # gcc-multilib has no arch-agnostic library but must be installed with arch-specific libraries 51 | RUN if [ $(arch) = "aarch64" ] ; then apt-get install --fix-broken -y gcc-multilib-x86-64-linux-gnu; fi 52 | 53 | # https://bugs.launchpad.net/ubuntu/+source/gcc-defaults/+bug/1300211/comments/31 54 | # second line of packages is to allow 32bit binaries to build on x86 arches 55 | RUN if [ $(arch) = "x86_64" ] ; then apt install -y --fix-broken gcc-multilib-powerpc64-linux-gnu \ 56 | libc6-dev-i386 gcc-9-mips-linux-gnu gcc-9-mips64-linux-gnuabi64 gcc-9-mips64el-linux-gnuabi64 gcc-9-mipsel-linux-gnu gcc-9-riscv64-linux-gnu lib32gcc-13-dev && \ 57 | ln -s /usr/include/x86_64-linux-gnu/asm/ /usr/local/include/asm; fi 58 | 59 | RUN apt-get update -y && apt-get install -y automake autogen build-essential bzr \ 60 | ca-certificates clang cmake cpio curl git help2man \ 61 | libgmp-dev libssl-dev libtool libxml2-dev llvm-dev mercurial \ 62 | openjdk-8-jdk openssl p7zip pkg-config swig texinfo unzip ca-certificates \ 63 | uuid-dev wget xz-utils zip zlib1g-dev && apt-get clean && rm -rf \ 64 | /var/lib/apt/lists/* /tmp/* /var/tmp/* && find /var/log -type f | while read -r f; \ 65 | do echo -ne '' > "$f"; done; 66 | 67 | RUN \ 68 | # Fix any stock package issues 69 | ln -s /usr/include/asm-generic /usr/include/asm && \ 70 | # Fix git safe.directory 71 | git config --global --add safe.directory '*' 72 | 73 | # Add patches directory for patching later 74 | COPY patches /patches 75 | 76 | ########################## 77 | # Darwin Toolchain build # 78 | ########################## 79 | 80 | # Configure the container for OSX cross compilation 81 | ENV OSX_SDK=MacOSX11.1.sdk 82 | ENV OSX_SDK_PATH=https://github.com/phracker/MacOSX-SDKs/releases/download/11.0-11.1/${OSX_SDK}.tar.xz 83 | 84 | # Make libxar known to the ld64 and cctools build 85 | ENV LD_LIBRARY_PATH=/osxcross/target/lib 86 | 87 | # Download the osx sdk and build the osx toolchain 88 | # We download the osx sdk, patch it and pack it again to be able to throw the patched version at osxcross 89 | RUN \ 90 | $FETCH $OSX_SDK_PATH 9b86eab03176c56bb526de30daa50fa819937c54b280364784ce431885341bf6 && \ 91 | tar -xf "$(basename $OSX_SDK_PATH)" && rm -f "$(basename $OSX_SDK_PATH)" 92 | ADD patch.tar.xz $OSX_SDK/usr/include/c++ 93 | SHELL ["/bin/bash", "-o", "pipefail", "-c"] 94 | # trunk-ignore(hadolint/DL3003) 95 | RUN tar -cf - $OSX_SDK/ | xz -c - > $OSX_SDK.tar.xz && rm -rf $OSX_SDK && \ 96 | # Get a cross compiler 97 | mkdir osxcross && cd osxcross && git init && \ 98 | git remote add origin https://github.com/tpoechtrager/osxcross.git && \ 99 | git fetch --depth 1 origin 0f87f567dfaf98460244471ad6c0f4311d62079c && \ 100 | git checkout FETCH_HEAD && cd ../ && \ 101 | # Move the SDK in to the cross compiler 102 | mv $OSX_SDK.tar.xz /osxcross/tarballs/ && \ 103 | # Actually build the toolchain 104 | OSX_VERSION_MIN=10.13 UNATTENDED=1 LD_LIBRARY_PATH=/osxcross/target/lib /osxcross/build.sh 105 | 106 | ENV PATH=/osxcross/target/bin:$PATH 107 | 108 | ########################### 109 | # FREEBSD TOOLCHAIN BUILD # 110 | ########################### 111 | 112 | COPY prep_freebsd.sh /prep_freebsd.sh 113 | RUN if [ $(arch) = "x86_64" ] ; then chmod +x /prep_freebsd.sh && \ 114 | /prep_freebsd.sh; fi 115 | 116 | ENV PATH=/freebsdcross/x86_64-pc-freebsd13/bin:$PATH 117 | 118 | # Inject the new Go root distribution downloader and bootstrapper 119 | COPY bootstrap_pure.sh /bootstrap_pure.sh 120 | ENV BOOTSTRAP_PURE=/bootstrap_pure.sh 121 | RUN chmod +x $BOOTSTRAP_PURE 122 | 123 | # Inject the C dependency cross compiler 124 | COPY build_deps.sh /build_deps.sh 125 | ENV BUILD_DEPS=/build_deps.sh 126 | RUN chmod +x $BUILD_DEPS 127 | 128 | ENTRYPOINT [ "/bin/bash", "-l", "-c" ] 129 | -------------------------------------------------------------------------------- /docker/toolchain/bootstrap_pure.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Contains the Go tool-chain pure-Go bootstrapper, that as of Go 1.5, initiates 4 | # not only a few pre-built Go cross compilers, but rather bootstraps all of the 5 | # supported platforms from the origin Linux amd64 distribution. 6 | # 7 | # Usage: bootstrap_pure.sh 8 | # 9 | # Environment variables for remote bootstrapping: 10 | # FETCH - Remote file fetcher and checksum verifier (injected by image) 11 | # ROOT_DIST - 64 bit Linux Go binary distribution package 12 | # ROOT_DIST_SHA - 64 bit Linux Go distribution package checksum 13 | # 14 | # Environment variables for local bootstrapping: 15 | # GOROOT - Path to the lready installed Go runtime 16 | set -e 17 | 18 | # Download, verify and install the root distribution if pulled remotely 19 | if [ "$GOROOT" == "" ]; then 20 | $FETCH "$ROOT_DIST" "$ROOT_DIST_SHA" 21 | 22 | tar -C /usr/local -xzf "$(basename "$ROOT_DIST")" 23 | rm -f "$(basename "$ROOT_DIST")" 24 | 25 | export GOROOT=/usr/local/go 26 | fi 27 | export GOROOT_BOOTSTRAP=$GOROOT 28 | 29 | # Install garble (go1.20+) for obfuscated builds 30 | echo "Installing garble..." 31 | go install mvdan.cc/garble@latest 32 | cp /go/bin/garble /usr/bin/garble 33 | -------------------------------------------------------------------------------- /docker/toolchain/build_deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Contains the a dependency builder to iterate over all installed dependencies 4 | # and cross compile them to the requested target platform. 5 | # 6 | # Usage: build_deps.sh 7 | # 8 | # Needed environment variables: 9 | # CC - C cross compiler to use for the build 10 | # HOST - Target platform to build (used to find the needed tool-chains) 11 | # PREFIX - File-system path where to install the built binaries 12 | set -e 13 | 14 | # Remove any previous build leftovers, and copy a fresh working set (clean doesn't work for cross compiling) 15 | rm -rf /deps-build && cp -r "$1" /deps-build 16 | 17 | # Build all the dependencies (no order for now) 18 | for dep in $(ls /deps-build/); do 19 | echo "Configuring dependency $dep for $HOST..." 20 | (cd "$dep" && ./configure --disable-shared --host="$HOST" --prefix="$PREFIX" --silent "${@:2}") 21 | 22 | echo "Building dependency $dep for $HOST..." 23 | (cd "$dep" && make --silent -j install) 24 | done 25 | 26 | # Remove any build artifacts 27 | rm -rf /deps-build 28 | -------------------------------------------------------------------------------- /docker/toolchain/fetch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Contains a simple fetcher to download a file from a remote URL and verify its 4 | # SHA1 or SHA256 checksum (selected based on provided length). 5 | # 6 | # Usage: fetch.sh 7 | set -e 8 | 9 | # Skip the download if no operands specified 10 | if [ "$1" == "" ] || [ "$2" == "" ]; then 11 | echo "Fetch operands missing, skipping..." 12 | exit 13 | fi 14 | 15 | # Pull the file from the remote URL 16 | file=$(basename "$1") 17 | echo "Downloading $1..." 18 | wget -q "$1" 19 | 20 | # Generate a desired checksum report and check against it 21 | echo "$2 $file" > "$file.sum" 22 | if [ "${#2}" == "40" ]; then 23 | sha1sum -c "$file.sum" 24 | else 25 | sha256sum -c "$file.sum" 26 | fi 27 | rm "$file.sum" 28 | -------------------------------------------------------------------------------- /docker/toolchain/patch.tar.xz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techknowlogick/xgo/7694f8a8ff059a2c34ba6003feccf99ba0e9e29d/docker/toolchain/patch.tar.xz -------------------------------------------------------------------------------- /docker/toolchain/patches/autogen.patch: -------------------------------------------------------------------------------- 1 | diff --git a/cctools/autogen.sh b/cctools/autogen.sh 2 | index 9258d0d..ca899f5 100755 3 | --- a/cctools/autogen.sh 4 | +++ b/cctools/autogen.sh 5 | @@ -19,7 +19,7 @@ fi 6 | export LIBTOOLIZE 7 | mkdir -p m4 8 | 9 | -$LIBTOOLIZE -c -i 10 | +$LIBTOOLIZE -c -i --force 11 | aclocal -I m4 12 | autoconf 13 | 14 | -------------------------------------------------------------------------------- /docker/toolchain/prep_freebsd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # big thanks to: 4 | # @mcandre 5 | # @samm-git 6 | # @mcilloni 7 | # @marcelog 8 | # @bijanebrahimi 9 | # for their work on this subject which 10 | # I have been able to expand upon for cgo/golang 11 | 12 | freebsd_ver=13 13 | freebsd_full_ver=13.4 14 | binutils_ver=2.39 15 | gmp_ver=6.2.1 16 | mpfr_ver=4.1.1 17 | mpc_ver=1.3.1 18 | gcc_ver=7.5.0 19 | 20 | mkdir -p /freebsdcross/x86_64-pc-freebsd${freebsd_ver} 21 | 22 | # binutils 23 | mkdir /tmp/freebsdbuild && cd /tmp/freebsdbuild && \ 24 | wget https://ftp.gnu.org/gnu/binutils/binutils-${binutils_ver}.tar.xz && \ 25 | tar -xf binutils-${binutils_ver}.tar.xz && cd binutils-${binutils_ver} && \ 26 | ./configure --enable-libssp --enable-gold --enable-ld \ 27 | --target=x86_64-pc-freebsd${freebsd_ver} --prefix=/freebsdcross/x86_64-pc-freebsd${freebsd_ver} && make -j4 && \ 28 | make install && \ 29 | rm -rf /tmp/freebsdbuild && mkdir /tmp/freebsdbuild 30 | 31 | # freebsd specific lbs 32 | cd /tmp/freebsdbuild && \ 33 | wget https://download.freebsd.org/ftp/releases/amd64/${freebsd_full_ver}-RELEASE/base.txz && \ 34 | cd /freebsdcross/x86_64-pc-freebsd${freebsd_ver} && \ 35 | tar -xf /tmp/freebsdbuild/base.txz ./lib/ ./usr/lib/ ./usr/include/ && \ 36 | cd /freebsdcross/x86_64-pc-freebsd${freebsd_ver}/usr/lib && \ 37 | find . -xtype l|xargs ls -l|grep ' /lib/' \ 38 | | awk '{print "ln -sf /freebsdcross/x86_64-pc-freebsd13"$11 " " $9}' \ 39 | | /bin/sh && \ 40 | rm -rf /tmp/freebsdbuild && mkdir /tmp/freebsdbuild 41 | 42 | # Compile GMP 43 | cd /tmp/freebsdbuild && \ 44 | wget https://ftp.gnu.org/gnu/gmp/gmp-${gmp_ver}.tar.xz && \ 45 | tar -xf gmp-${gmp_ver}.tar.xz && \ 46 | cd gmp-${gmp_ver} && \ 47 | ./configure --prefix=/freebsdcross/x86_64-pc-freebsd${freebsd_ver} --enable-shared --enable-static \ 48 | --enable-fft --enable-cxx --host=x86_64-pc-freebsd${freebsd_ver} && \ 49 | make -j4 && make install && \ 50 | rm -rf /tmp/freebsdbuild && mkdir /tmp/freebsdbuild 51 | 52 | # Compile MPFR 53 | cd /tmp/freebsdbuild && \ 54 | wget https://ftp.gnu.org/gnu/mpfr/mpfr-${mpfr_ver}.tar.xz && tar -xf mpfr-${mpfr_ver}.tar.xz && \ 55 | cd mpfr-${mpfr_ver} && \ 56 | ./configure --prefix=/freebsdcross/x86_64-pc-freebsd${freebsd_ver} --with-gnu-ld --enable-static \ 57 | --enable-shared --with-gmp=/freebsdcross/x86_64-pc-freebsd${freebsd_ver} --host=x86_64-pc-freebsd${freebsd_ver} && \ 58 | make -j4 && make install && \ 59 | rm -rf /tmp/freebsdbuild && mkdir /tmp/freebsdbuild 60 | 61 | # Compile MPC 62 | cd /tmp/freebsdbuild && \ 63 | wget https://ftp.gnu.org/gnu/mpc/mpc-${mpc_ver}.tar.gz && tar -xf mpc-${mpc_ver}.tar.gz && \ 64 | cd mpc-${mpc_ver} && \ 65 | ./configure --prefix=/freebsdcross/x86_64-pc-freebsd${freebsd_ver} --with-gnu-ld --enable-static \ 66 | --enable-shared --with-gmp=/freebsdcross/x86_64-pc-freebsd${freebsd_ver} \ 67 | --with-mpfr=/freebsdcross/x86_64-pc-freebsd${freebsd_ver} --host=x86_64-pc-freebsd${freebsd_ver} && \ 68 | make -j4 && make install && \ 69 | rm -rf /tmp/freebsdbuild && mkdir /tmp/freebsdbuild 70 | 71 | # gcc (change LD_LIBRARY_PATH to /freebsdcross or something) 72 | cd /tmp/freebsdbuild && \ 73 | wget https://ftp.gnu.org/gnu/gcc/gcc-${gcc_ver}/gcc-${gcc_ver}.tar.xz && \ 74 | tar xf gcc-${gcc_ver}.tar.xz && \ 75 | cd gcc-${gcc_ver} && mkdir build && cd build && \ 76 | ../configure --without-headers --with-gnu-as --with-gnu-ld --disable-nls \ 77 | --enable-languages=c,c++ --enable-libssp --enable-gold --enable-ld \ 78 | --disable-libitm --disable-libquadmath --target=x86_64-pc-freebsd${freebsd_ver} \ 79 | --prefix=/freebsdcross/x86_64-pc-freebsd${freebsd_ver} --with-gmp=/freebsdcross/x86_64-pc-freebsd${freebsd_ver} \ 80 | --with-mpc=/freebsdcross/x86_64-pc-freebsd${freebsd_ver} --with-mpfr=/freebsdcross/x86_64-pc-freebsd${freebsd_ver} --disable-libgomp \ 81 | --with-sysroot=/freebsdcross/x86_64-pc-freebsd${freebsd_ver} \ 82 | --with-build-sysroot=/freebsdcross/x86_64-pc-freebsd${freebsd_ver} && \ 83 | cd /tmp/freebsdbuild/gcc-${gcc_ver} && \ 84 | echo '#define HAVE_ALIGNED_ALLOC 1' >> libstdc++-v3/config.h.in && \ 85 | cd /tmp/freebsdbuild/gcc-${gcc_ver}/build && \ 86 | make -j4 && make install && \ 87 | rm -rf /tmp/freebsdbuild 88 | -------------------------------------------------------------------------------- /generate_docker_images.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import os 3 | import hashlib 4 | import re 5 | from jsonpath_ng import parse 6 | 7 | def generate_image(image): 8 | version = image[0]["version"].replace("go1","go-1") 9 | wildcard = version 10 | if version.count('.') > 1: 11 | wildcard = re.sub("\.\d+$", ".x", version) 12 | else: 13 | version = version + ".0" 14 | wildcard = wildcard + ".x" 15 | try: 16 | os.mkdir("docker/"+version) 17 | except: 18 | print("version folder already exists") 19 | try: 20 | os.mkdir("docker/"+wildcard) 21 | except: 22 | print("wildcard folder already exists") 23 | # now that folders have been created, the Dockerfile needs to be created 24 | f = open("docker/"+version+"/Dockerfile", "w") 25 | f.write("## GENERATED. DO NOT EDIT DIRECTLY.\n") 26 | f.write("FROM toolchain\n\n") 27 | f.write("ARG TARGETPLATFORM\n") 28 | f.write("ENV GO_VERSION="+version.replace("go-","").replace(".","")+"\n\n") 29 | f.write("RUN \\\n") 30 | f.write('if [ "$TARGETPLATFORM" = "linux/amd64" ]; then \\\n') 31 | f.write(" export ROOT_DIST=https://dl.google.com/go/"+image[0]["filename"]+" && \\\n") 32 | f.write(" export ROOT_DIST_SHA="+image[0]["sha256"]+";\\\n") 33 | f.write('elif [ "$TARGETPLATFORM" = "linux/arm64" ]; then \\\n') 34 | f.write("export ROOT_DIST=https://dl.google.com/go/"+image[1]["filename"]+" && \\\n") 35 | f.write("export ROOT_DIST_SHA="+image[1]["sha256"]+";\\\n") 36 | f.write(" else \\\n") 37 | f.write('echo "Unsupported architecture: $TARGETPLATFORM" && exit 1; \\\n') 38 | f.write("fi && \\\n") 39 | f.write("$BOOTSTRAP_PURE\n") 40 | f.close() 41 | # now wildcard version 42 | f = open("docker/"+wildcard+"/Dockerfile", "w") 43 | f.write("## GENERATED. DO NOT EDIT DIRECTLY.\n") 44 | f.write("FROM "+version+"\n") 45 | f.close() 46 | 47 | r = requests.get('https://go.dev/dl/?mode=json') 48 | 49 | if r.status_code != requests.codes.ok: 50 | print("error fetching golang versions") 51 | exit(1) 52 | 53 | try: 54 | golangJson = r.json() 55 | except: 56 | print("failed to parse json") 57 | exit(1) 58 | 59 | if len(golangJson) != 2: 60 | # the script below assumes only two stable versions 61 | print("unexpected number of golang versions returned") 62 | exit(1) 63 | 64 | fileExpr = parse('$.[*].files') 65 | files = [match.value for match in fileExpr.find(golangJson)] 66 | versionExpr = parse('$.[*].version') 67 | versions = [match.value for match in versionExpr.find(golangJson)] 68 | docker_images = {} 69 | for file in files: 70 | x = [f for f in file if (f['os'] == "linux" and f['arch'] == "amd64" ) ][0] 71 | y = [f for f in file if (f['os'] == "linux" and f['arch'] == "arm64" ) ][0] 72 | docker_images[x['version']] = [x, y] 73 | 74 | # loop through each key in dict and pass value to generate_image function 75 | first = {} 76 | for docker_image in docker_images: 77 | if len(first) < 1: 78 | first = docker_images[docker_image] 79 | generate_image(docker_images[docker_image]) 80 | 81 | # write latest image 82 | if first[0]["version"].count('.') > 1: 83 | wildcard = re.sub("\.\d+$", ".x", first[0]["version"]) 84 | else: 85 | wildcard = first[0]["version"] + ".x" 86 | try: 87 | os.mkdir("docker/go-latest") 88 | except: 89 | print("go-latest folder already exists") 90 | 91 | f = open("docker/go-latest/Dockerfile", "w") 92 | f.write("## GENERATED. DO NOT EDIT DIRECTLY.\n") 93 | f.write("FROM techknowlogick/xgo:"+wildcard.replace("go1", "go-1")+"\n") 94 | f.close() 95 | 96 | hs = hashlib.sha256(r.text.encode('utf-8')).hexdigest() 97 | f = open(".golang_hash", "w") 98 | f.write(hs) 99 | f.close() 100 | f = open(".golang_version", "w") 101 | f.write(",".join(versions)) 102 | f.close() 103 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module src.techknowlogick.com/xgo 2 | 3 | go 1.12 4 | -------------------------------------------------------------------------------- /tests/embedded_c/main.go: -------------------------------------------------------------------------------- 1 | // Go cross compiler (xgo): Test file for embedded C snippets. 2 | // Copyright (c) 2015 Péter Szilágyi. All rights reserved. 3 | // 4 | // Released under the MIT license. 5 | 6 | package main 7 | 8 | // #include 9 | // 10 | // void sayHi() { 11 | // printf("Hello, embedded C!\n"); 12 | // } 13 | import "C" 14 | 15 | func main() { 16 | C.sayHi() 17 | } 18 | -------------------------------------------------------------------------------- /tests/embedded_cpp/main.go: -------------------------------------------------------------------------------- 1 | // Go cross compiler (xgo): Test file for embedded C++ snippets. 2 | // Copyright (c) 2015 Péter Szilágyi. All rights reserved. 3 | // 4 | // Released under the MIT license. 5 | 6 | package main 7 | 8 | // #include "snippet.h" 9 | import "C" 10 | 11 | func main() { 12 | C.sayHi() 13 | } 14 | -------------------------------------------------------------------------------- /tests/embedded_cpp/snippet.cpp: -------------------------------------------------------------------------------- 1 | // Go cross compiler (xgo): Test implementation for embedded C++ snippets. 2 | // Copyright (c) 2015 Péter Szilágyi. All rights reserved. 3 | // 4 | // Released under the MIT license. 5 | 6 | #include 7 | #include "snippet.h" 8 | 9 | void sayHi() { 10 | std::cout << "Hello, embedded C++!" << std::endl; 11 | } 12 | -------------------------------------------------------------------------------- /tests/embedded_cpp/snippet.h: -------------------------------------------------------------------------------- 1 | // Go cross compiler (xgo): Test header for embedded C++ snippets. 2 | // Copyright (c) 2015 Péter Szilágyi. All rights reserved. 3 | // 4 | // Released under the MIT license. 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | void sayHi(); 11 | 12 | #ifdef __cplusplus 13 | } 14 | #endif 15 | -------------------------------------------------------------------------------- /xgo.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | @test "embedded c" { 4 | export GO111MODULE=auto 5 | run go run xgo.go --image="${IMAGEID}" ./tests/embedded_c 6 | echo "$output" 7 | [ "$status" -eq 0 ] 8 | } 9 | 10 | @test "embedded cpp" { 11 | run go run xgo.go --image="${IMAGEID}" ./tests/embedded_cpp 12 | echo "$output" 13 | [ "$status" -eq 0 ] 14 | } 15 | 16 | @test "has mod" { 17 | skip "this test doesn't yet exist" 18 | run go run xgo.go --image="${IMAGEID}" src.techknowlogick.com/xgo/tests/hasmod 19 | [ "$status" -eq 0 ] 20 | } 21 | 22 | @test "has mod and vendor" { 23 | skip "this test doesn't yet exist" 24 | run go run xgo.go --image="${IMAGEID}" src.techknowlogick.com/xgo/tests/hasmodandvendor 25 | [ "$status" -eq 0 ] 26 | } 27 | 28 | # FIXME: does not work, see https://github.com/techknowlogick/xgo/issues/260 29 | #@test "branches" { 30 | # run go run xgo.go --remote https://github.com/rwcarlsen/cyan --branch memprof --targets "linux/amd64" --image="${IMAGEID}" github.com/rwcarlsen/cyan/cmd/cyan 31 | # echo "$output" 32 | # [ "$status" -eq 0 ] 33 | #} 34 | 35 | @test "eth smoke" { 36 | git clone --depth 1 https://github.com/ethereum/go-ethereum.git /tmp/eth 37 | run go run xgo.go --targets "linux/amd64" --image="${IMAGEID}" /tmp/eth/cmd/geth 38 | echo "$output" 39 | [ "$status" -eq 0 ] 40 | } 41 | 42 | @test "gitea smoke" { 43 | git clone --depth 1 https://github.com/go-gitea/gitea.git /tmp/gitea 44 | run go run xgo.go --image="${IMAGEID}" --targets "darwin-10.6/amd64" -tags 'netgo osusergo sqlite sqlite_unlock_notify' /tmp/gitea 45 | echo "$output" 46 | [ "$status" -eq 0 ] 47 | } 48 | 49 | @test "vikunja smoke" { 50 | export vikunja_path=/tmp/vikunja 51 | git clone --depth 1 https://kolaente.dev/vikunja/api $vikunja_path 52 | mkdir -p $vikunja_path/frontend/dist/ 53 | touch $vikunja_path/frontend/dist/index.html 54 | run go run xgo.go --image="${IMAGEID}" --targets "darwin-10.6/amd64" $vikunja_path 55 | echo "$output" 56 | [ "$status" -eq 0 ] 57 | } 58 | -------------------------------------------------------------------------------- /xgo.go: -------------------------------------------------------------------------------- 1 | // Go CGO cross compiler 2 | // Copyright (c) 2014 Péter Szilágyi. All rights reserved. 3 | // Copyright (c) 2019 techknowlogick. All rights reserved. 4 | // 5 | // Released under the MIT license. 6 | 7 | // Wrapper around the GCO cross compiler docker container. 8 | package main // import "src.techknowlogick.com/xgo" 9 | 10 | import ( 11 | "bytes" 12 | "flag" 13 | "fmt" 14 | "go/build" 15 | "io" 16 | "log" 17 | "net/http" 18 | "os" 19 | "os/exec" 20 | "os/user" 21 | "path/filepath" 22 | "regexp" 23 | "strconv" 24 | "strings" 25 | ) 26 | 27 | // Path where to cache external dependencies 28 | var depsCache string 29 | 30 | func init() { 31 | // Initialize the external dependency cache path to a few possible locations 32 | if home := os.Getenv("HOME"); home != "" { 33 | depsCache = filepath.Join(home, ".xgo-cache") 34 | return 35 | } 36 | if usr, err := user.Current(); usr != nil && err == nil && usr.HomeDir != "" { 37 | depsCache = filepath.Join(usr.HomeDir, ".xgo-cache") 38 | return 39 | } 40 | depsCache = filepath.Join(os.TempDir(), "xgo-cache") 41 | } 42 | 43 | // Cross compilation docker containers 44 | var ( 45 | dockerDist = "ghcr.io/techknowlogick/xgo:" 46 | ) 47 | 48 | // Command line arguments to fine tune the compilation 49 | var ( 50 | goVersion = flag.String("go", "latest", "Go release to use for cross compilation") 51 | srcPackage = flag.String("pkg", "", "Sub-package to build if not root import") 52 | srcRemote = flag.String("remote", "", "Version control remote repository to build") 53 | srcBranch = flag.String("branch", "", "Version control branch to build") 54 | outPrefix = flag.String("out", "", "Prefix to use for output naming (empty = package name)") 55 | outFolder = flag.String("dest", "", "Destination folder to put binaries in (empty = current)") 56 | crossDeps = flag.String("deps", "", "CGO dependencies (configure/make based archives)") 57 | crossArgs = flag.String("depsargs", "", "CGO dependency configure arguments") 58 | targets = flag.String("targets", "*/*", "Comma separated targets to build for") 59 | dockerImage = flag.String("image", "", "Use custom docker image instead of official distribution") 60 | dockerEnv = flag.String("env", "", "Comma separated custom environments added to docker run -e") 61 | dockerArgs = flag.String("dockerargs", "", "Comma separated arguments added to docker run") 62 | volumes = flag.String("volumes", "", "Comma separated list of volume mounts in format source:target[:mode]") 63 | hooksDir = flag.String("hooksdir", "", "Directory with user hook scripts (setup.sh, build.sh)") 64 | forwardSsh = flag.Bool("ssh", false, "Enable ssh agent forwarding") 65 | ) 66 | 67 | // ConfigFlags is a simple set of flags to define the environment and dependencies. 68 | type ConfigFlags struct { 69 | Repository string // Root import path to build 70 | Package string // Sub-package to build if not root import 71 | Prefix string // Prefix to use for output naming 72 | Remote string // Version control remote repository to build 73 | Branch string // Version control branch to build 74 | Dependencies string // CGO dependencies (configure/make based archives) 75 | Arguments string // CGO dependency configure arguments 76 | Targets []string // Targets to build for 77 | DockerEnv []string // Custom environments added to docker run -e 78 | DockerArgs []string // Custom options added to docker run 79 | Volumes []string // Volume mounts for docker run -v 80 | ForwardSsh bool // Enable ssh agent forwarding 81 | } 82 | 83 | // Command line arguments to pass to go build 84 | var ( 85 | buildVerbose = flag.Bool("v", false, "Print the names of packages as they are compiled") 86 | buildSteps = flag.Bool("x", false, "Print the command as executing the builds") 87 | buildRace = flag.Bool("race", false, "Enable data race detection (supported only on amd64)") 88 | buildTags = flag.String("tags", "", "List of build tags to consider satisfied during the build") 89 | buildLdFlags = flag.String("ldflags", "", "Arguments to pass on each go tool link invocation") 90 | buildGcFlags = flag.String("gcflags", "", "Arguments to pass on each go tool compile invocation") 91 | buildMode = flag.String("buildmode", "default", "Indicates which kind of object file to build") 92 | buildTrimpath = flag.Bool("trimpath", false, "Indicates if trimpath should be applied to build") 93 | buildBuildVCS = flag.Bool("buildvcs", true, "Whether to stamp binaries with version control information") 94 | obfuscate = flag.Bool("obfuscate", false, "Obfuscate build using garble") 95 | garbleFlags = flag.String("garbleflags", "", "Arguments to pass to garble (e.g. -seed=random)") 96 | ) 97 | 98 | // BuildFlags is a simple collection of flags to fine tune a build. 99 | type BuildFlags struct { 100 | Verbose bool // Print the names of packages as they are compiled 101 | Steps bool // Print the command as executing the builds 102 | Race bool // Enable data race detection (supported only on amd64) 103 | Tags string // List of build tags to consider satisfied during the build 104 | LdFlags string // Arguments to pass on each go tool link invocation 105 | GcFlags string // Arguments to pass on each go tool compile invocation 106 | Mode string // Indicates which kind of object file to build 107 | Trimpath bool // Indicates if trimpath should be applied to build 108 | BuildVCS bool // Whether to stamp binaries with version control information 109 | Obfuscate bool // Obfuscate build using garble 110 | GarbleFlags string // Arguments to pass to garble 111 | } 112 | 113 | func main() { 114 | // Retrieve the CLI flags and the execution environment 115 | flag.Parse() 116 | 117 | xgoInXgo := os.Getenv("XGO_IN_XGO") == "1" 118 | if xgoInXgo { 119 | depsCache = "/deps-cache" 120 | } 121 | // Only use docker images if we're not already inside out own image 122 | image := "" 123 | 124 | if !xgoInXgo { 125 | // Ensure docker is available 126 | if err := checkDocker(); err != nil { 127 | log.Fatalf("Failed to check docker installation: %v.", err) 128 | } 129 | // Validate the command line arguments 130 | if len(flag.Args()) != 1 { 131 | log.Fatalf("Usage: %s [options] ", os.Args[0]) 132 | } 133 | // Select the image to use, either official or custom 134 | image = dockerDist + *goVersion 135 | if *dockerImage != "" { 136 | image = *dockerImage 137 | } 138 | // Check that all required images are available 139 | found, err := checkDockerImage(image) 140 | switch { 141 | case err != nil: 142 | log.Fatalf("Failed to check docker image availability: %v.", err) 143 | case !found: 144 | fmt.Println("not found!") 145 | if err := pullDockerImage(image); err != nil { 146 | log.Fatalf("Failed to pull docker image from the registry: %v.", err) 147 | } 148 | default: 149 | fmt.Println("found.") 150 | } 151 | } 152 | // Cache all external dependencies to prevent always hitting the internet 153 | if *crossDeps != "" { 154 | if err := os.MkdirAll(depsCache, 0750); err != nil { 155 | log.Fatalf("Failed to create dependency cache: %v.", err) 156 | } 157 | // Download all missing dependencies 158 | for _, dep := range strings.Split(*crossDeps, " ") { 159 | if url := strings.TrimSpace(dep); len(url) > 0 { 160 | path := filepath.Join(depsCache, filepath.Base(url)) 161 | 162 | if _, err := os.Stat(path); err != nil { 163 | fmt.Printf("Downloading new dependency: %s...\n", url) 164 | 165 | out, err := os.Create(path) 166 | if err != nil { 167 | log.Fatalf("Failed to create dependency file: %v.", err) 168 | } 169 | res, err := http.Get(url) 170 | if err != nil { 171 | log.Fatalf("Failed to retrieve dependency: %v.", err) 172 | } 173 | defer res.Body.Close() 174 | 175 | if _, err := io.Copy(out, res.Body); err != nil { 176 | log.Fatalf("Failed to download dependency: %v", err) 177 | } 178 | out.Close() 179 | 180 | fmt.Printf("New dependency cached: %s.\n", path) 181 | } else { 182 | fmt.Printf("Dependency already cached: %s.\n", path) 183 | } 184 | } 185 | } 186 | } 187 | // Assemble the cross compilation environment and build options 188 | config := &ConfigFlags{ 189 | Repository: flag.Args()[0], 190 | Package: *srcPackage, 191 | Remote: *srcRemote, 192 | Branch: *srcBranch, 193 | Prefix: *outPrefix, 194 | Dependencies: *crossDeps, 195 | Arguments: *crossArgs, 196 | Targets: strings.Split(*targets, ","), 197 | DockerEnv: strings.Split(*dockerEnv, ","), 198 | DockerArgs: strings.Split(*dockerArgs, ","), 199 | Volumes: strings.Split(*volumes, ","), 200 | ForwardSsh: *forwardSsh, 201 | } 202 | flags := &BuildFlags{ 203 | Verbose: *buildVerbose, 204 | Steps: *buildSteps, 205 | Race: *buildRace, 206 | Tags: *buildTags, 207 | LdFlags: *buildLdFlags, 208 | GcFlags: *buildGcFlags, 209 | Mode: *buildMode, 210 | Trimpath: *buildTrimpath, 211 | BuildVCS: *buildBuildVCS, 212 | Obfuscate: *obfuscate, 213 | GarbleFlags: *garbleFlags, 214 | } 215 | folder, err := os.Getwd() 216 | if err != nil { 217 | log.Fatalf("Failed to retrieve the working directory: %v.", err) 218 | } 219 | if *outFolder != "" { 220 | folder, err = filepath.Abs(*outFolder) 221 | if err != nil { 222 | log.Fatalf("Failed to resolve destination path (%s): %v.", *outFolder, err) 223 | } 224 | } 225 | if *hooksDir != "" { 226 | dir, err := filepath.Abs(*hooksDir) 227 | if err != nil { 228 | log.Fatalf("Failed to resolve hooksdir path (%s): %v.", *hooksDir, err) 229 | } 230 | if i, err := os.Stat(dir); err != nil { 231 | log.Fatalf("Failed to resolve hooksdir path (%s): %v.", *hooksDir, err) 232 | } else if !i.IsDir() { 233 | log.Fatalf("Given hooksdir (%s) is not a directory.", *hooksDir) 234 | } 235 | config.DockerArgs = append(config.DockerArgs, "--mount", fmt.Sprintf(`type=bind,source=%s,target=/hooksdir`, dir)) 236 | } 237 | // Execute the cross compilation, either in a container or the current system 238 | if !xgoInXgo { 239 | err = compile(image, config, flags, folder) 240 | } else { 241 | err = compileContained(config, flags, folder) 242 | } 243 | if err != nil { 244 | log.Fatalf("Failed to cross compile package: %v.", err) 245 | } 246 | } 247 | 248 | // Checks whether a docker installation can be found and is functional. 249 | func checkDocker() error { 250 | fmt.Println("Checking docker installation...") 251 | if err := run(exec.Command("docker", "version")); err != nil { 252 | return err 253 | } 254 | fmt.Println() 255 | return nil 256 | } 257 | 258 | // Checks whether a required docker image is available locally. 259 | func checkDockerImage(image string) (bool, error) { 260 | fmt.Printf("Checking for required docker image %s... ", image) 261 | out, err := exec.Command("docker", "images", "--no-trunc").Output() 262 | if err != nil { 263 | return false, err 264 | } 265 | return compareOutAndImage(out, image) 266 | } 267 | 268 | // compare output of docker images and image name 269 | func compareOutAndImage(out []byte, image string) (bool, error) { 270 | if strings.Contains(image, ":") { 271 | // get repository and tag 272 | res := strings.SplitN(image, ":", 2) 273 | r, t := res[0], res[1] 274 | match, _ := regexp.Match(fmt.Sprintf(`%s\s+%s`, r, t), out) 275 | return match, nil 276 | } 277 | 278 | // default find repository without tag 279 | return bytes.Contains(out, []byte(image)), nil 280 | } 281 | 282 | // Pulls an image from the docker registry. 283 | func pullDockerImage(image string) error { 284 | fmt.Printf("Pulling %s from docker registry...\n", image) 285 | return run(exec.Command("docker", "pull", image)) 286 | } 287 | 288 | // compile cross builds a requested package according to the given build specs 289 | // using a specific docker cross compilation image. 290 | func compile(image string, config *ConfigFlags, flags *BuildFlags, folder string) error { 291 | // We need to consider our module-aware status 292 | go111module := os.Getenv("GO111MODULE") 293 | localBuild := strings.HasPrefix(config.Repository, string(filepath.Separator)) || strings.HasPrefix(config.Repository, ".") 294 | if !localBuild { 295 | fmt.Printf("Cross compiling non-local repository: %s...\n", config.Repository) 296 | args := toArgs(config, flags, folder) 297 | if go111module == "" { 298 | // We're going to be kind to our users and let an empty GO111MODULE fall back to auto mode. 299 | go111module = "auto" 300 | } 301 | args = append(args, []string{ 302 | "-e", "GO111MODULE=" + go111module, 303 | }...) 304 | args = append(args, []string{image, config.Repository}...) 305 | 306 | cmd := exec.Command("docker", args...) 307 | if config.ForwardSsh { 308 | cmd.Stdin = os.Stdin 309 | } 310 | 311 | return run(cmd) 312 | } 313 | 314 | usesModules := true 315 | if go111module == "off" { 316 | usesModules = false 317 | } else if go111module != "on" { 318 | usesModules = false 319 | // we need to look at the current config and determine if we should use modules... 320 | if _, err := os.Stat(config.Repository + "/go.mod"); err == nil { 321 | usesModules = true 322 | } 323 | if !usesModules { 324 | // Walk the parents looking for a go.mod file! 325 | absRepository, err := filepath.Abs(config.Repository) 326 | if err == nil { 327 | goModDir := absRepository 328 | // now walk backwards as per go behaviour 329 | for { 330 | if stat, err := os.Stat(filepath.Join(goModDir, "go.mod")); err == nil { 331 | usesModules = !stat.IsDir() 332 | break 333 | } 334 | parent := filepath.Dir(goModDir) 335 | if len(parent) >= len(goModDir) { 336 | break 337 | } 338 | goModDir = parent 339 | } 340 | 341 | if usesModules { 342 | sourcePath, _ := filepath.Rel(goModDir, absRepository) 343 | if config.Package == "" { 344 | config.Package = sourcePath 345 | } else { 346 | config.Package = filepath.Join(sourcePath, config.Package) 347 | } 348 | 349 | config.Repository = goModDir 350 | } 351 | } 352 | } 353 | if !usesModules { 354 | // Resolve the repository import path from the file path 355 | config.Repository = resolveImportPath(config.Repository) 356 | 357 | if _, err := os.Stat(config.Repository + "/go.mod"); err == nil { 358 | usesModules = true 359 | } 360 | } 361 | } 362 | 363 | // Assemble and run the cross compilation command 364 | fmt.Printf("Cross compiling local repository: %s : %s...\n", config.Repository, config.Package) 365 | args := toArgs(config, flags, folder) 366 | 367 | if usesModules { 368 | args = append(args, []string{"-e", "GO111MODULE=on"}...) 369 | gopathEnv := getGOPATH() 370 | if gopathEnv != "" { 371 | args = append(args, []string{"-v", gopathEnv + ":/go"}...) 372 | } 373 | // FIXME: consider GOMODCACHE? 374 | 375 | fmt.Printf("Enabled Go module support\n") 376 | 377 | // Map this repository to the /source folder 378 | absRepository, err := filepath.Abs(config.Repository) 379 | if err != nil { 380 | log.Fatalf("Failed to locate requested module repository: %v.", err) 381 | } 382 | 383 | args = append(args, []string{"-v", absRepository + ":/source"}...) 384 | 385 | // Check if there is a vendor folder, and if so, use it 386 | vendorPath := absRepository + "/vendor" 387 | vendorfolder, err := os.Stat(vendorPath) 388 | if !os.IsNotExist(err) && vendorfolder.Mode().IsDir() { 389 | args = append(args, []string{"-e", "FLAG_MOD=vendor"}...) 390 | fmt.Printf("Using vendored Go module dependencies\n") 391 | } 392 | } else { 393 | // If we're performing a local build and we're not using modules we need to map the gopath over to the docker 394 | args = append(args, []string{"-e", "GO111MODULE=off"}...) 395 | args = append(args, goPathExports()...) 396 | } 397 | 398 | args = append(args, []string{image, config.Repository}...) 399 | 400 | cmd := exec.Command("docker", args...) 401 | if config.ForwardSsh { 402 | cmd.Stdin = os.Stdin 403 | } 404 | 405 | return run(cmd) 406 | } 407 | 408 | func toArgs(config *ConfigFlags, flags *BuildFlags, folder string) []string { 409 | // Alter paths so they work for Windows 410 | // Does not affect Linux paths 411 | re := regexp.MustCompile("([A-Z]):") 412 | folder_w := filepath.ToSlash(re.ReplaceAllString(folder, "/$1")) 413 | depsCache_w := filepath.ToSlash(re.ReplaceAllString(depsCache, "/$1")) 414 | gocache := filepath.Join(depsCache, "gocache") 415 | if err := os.MkdirAll(gocache, 0750); err != nil { // 0750 = rwxr-x--- 416 | log.Fatalf("Failed to create gocache dir: %v.", err) 417 | } 418 | gocache_w := filepath.ToSlash(re.ReplaceAllString(gocache, "/$1")) 419 | 420 | args := []string{ 421 | "run", "--rm", 422 | "-v", folder_w + ":/build", 423 | "-v", depsCache_w + ":/deps-cache:ro", 424 | "-v", gocache_w + ":/gocache:rw", 425 | "-e", "REPO_REMOTE=" + config.Remote, 426 | "-e", "REPO_BRANCH=" + config.Branch, 427 | "-e", "PACK=" + config.Package, 428 | "-e", "DEPS=" + config.Dependencies, 429 | "-e", "ARGS=" + config.Arguments, 430 | "-e", "OUT=" + config.Prefix, 431 | "-e", fmt.Sprintf("FLAG_V=%v", flags.Verbose), 432 | "-e", fmt.Sprintf("FLAG_X=%v", flags.Steps), 433 | "-e", fmt.Sprintf("FLAG_RACE=%v", flags.Race), 434 | "-e", fmt.Sprintf("FLAG_TAGS=%s", flags.Tags), 435 | "-e", fmt.Sprintf("FLAG_LDFLAGS=%s", flags.LdFlags), 436 | "-e", fmt.Sprintf("FLAG_GCFLAGS=%s", flags.GcFlags), 437 | "-e", fmt.Sprintf("FLAG_BUILDMODE=%s", flags.Mode), 438 | "-e", fmt.Sprintf("FLAG_TRIMPATH=%v", flags.Trimpath), 439 | "-e", fmt.Sprintf("FLAG_BUILDVCS=%v", flags.BuildVCS), 440 | "-e", fmt.Sprintf("FLAG_OBFUSCATE=%v", flags.Obfuscate), 441 | "-e", fmt.Sprintf("GARBLE_FLAGS=%s", flags.GarbleFlags), 442 | "-e", "TARGETS=" + strings.Replace(strings.Join(config.Targets, " "), "*", ".", -1), 443 | "-e", fmt.Sprintf("GOPROXY=%s", os.Getenv("GOPROXY")), 444 | "-e", fmt.Sprintf("GOPRIVATE=%s", os.Getenv("GOPRIVATE")), 445 | } 446 | 447 | // Set custom environment variables 448 | for _, s := range config.DockerEnv { 449 | if s != "" { 450 | args = append(args, []string{"-e", s}...) 451 | } 452 | } 453 | // Set custom args 454 | for _, s := range config.DockerArgs { 455 | if s != "" { 456 | args = append(args, s) 457 | } 458 | } 459 | // Set custom volume mounts 460 | for _, s := range config.Volumes { 461 | if s != "" { 462 | args = append(args, []string{"-v", s}...) 463 | } 464 | } 465 | 466 | if config.ForwardSsh && os.Getenv("SSH_AUTH_SOCK") != "" { 467 | // Keep stdin open and allocate pseudo tty 468 | args = append(args, "-i", "-t") 469 | // Mount ssh agent socket 470 | args = append(args, "-v", fmt.Sprintf("%[1]s:%[1]s", os.Getenv("SSH_AUTH_SOCK"))) 471 | // Set ssh agent socket environment variable 472 | args = append(args, "-e", fmt.Sprintf("SSH_AUTH_SOCK=%s", os.Getenv("SSH_AUTH_SOCK"))) 473 | } 474 | return args 475 | } 476 | 477 | func goPathExports() (args []string) { 478 | var locals, mounts, paths []string 479 | log.Printf("Preparing GOPATH src to be shared with xgo") 480 | 481 | // First determine the GOPATH 482 | gopathEnv := getGOPATH() 483 | if gopathEnv == "" { 484 | log.Printf("No $GOPATH is set or forwarded to xgo") 485 | return 486 | } 487 | 488 | // Iterate over all the local libs and export the mount points 489 | for _, gopath := range strings.Split(gopathEnv, string(os.PathListSeparator)) { 490 | // Since docker sandboxes volumes, resolve any symlinks manually 491 | sources := filepath.Join(gopath, "src") 492 | absSources, err := filepath.Abs(sources) 493 | if err != nil { 494 | log.Fatalf("Unable to generate absolute path for source directory %s. %v", sources, err) 495 | } 496 | absSources = filepath.ToSlash(filepath.Join(absSources, string(filepath.Separator))) 497 | _ = filepath.Walk(sources, func(path string, info os.FileInfo, err error) error { 498 | // Skip any folders that errored out 499 | if err != nil { 500 | log.Printf("Failed to access GOPATH element %s: %v", path, err) 501 | return nil 502 | } 503 | // Skip anything that's not a symlink 504 | if info.Mode()&os.ModeSymlink == 0 { 505 | return nil 506 | } 507 | // Resolve the symlink and skip if it's not a folder 508 | target, err := filepath.EvalSymlinks(path) 509 | if err != nil { 510 | return nil 511 | } 512 | if info, err = os.Stat(target); err != nil || !info.IsDir() { 513 | return nil 514 | } 515 | // Skip if the symlink points within GOPATH 516 | absTarget, err := filepath.Abs(target) 517 | if err == nil { 518 | absTarget = filepath.ToSlash(filepath.Join(absTarget, string(filepath.Separator))) 519 | if strings.HasPrefix(absTarget, absSources) { 520 | return nil 521 | } 522 | } 523 | 524 | // Folder needs explicit mounting due to docker symlink security 525 | locals = append(locals, target) 526 | mounts = append(mounts, filepath.Join("/ext-go", strconv.Itoa(len(locals)), "src", strings.TrimPrefix(path, sources))) 527 | paths = append(paths, filepath.Join("/ext-go", strconv.Itoa(len(locals)))) 528 | return nil 529 | }) 530 | // Export the main mount point for this GOPATH entry 531 | locals = append(locals, sources) 532 | mounts = append(mounts, filepath.Join("/ext-go", strconv.Itoa(len(locals)), "src")) 533 | paths = append(paths, filepath.Join("/ext-go", strconv.Itoa(len(locals)))) 534 | } 535 | 536 | for i := 0; i < len(locals); i++ { 537 | args = append(args, []string{"-v", fmt.Sprintf("%s:%s:ro", locals[i], mounts[i])}...) 538 | } 539 | args = append(args, []string{"-e", "EXT_GOPATH=" + strings.Join(paths, ":")}...) 540 | return args 541 | } 542 | 543 | func getGOPATH() string { 544 | // First determine the GOPATH 545 | gopathEnv := os.Getenv("GOPATH") 546 | if gopathEnv == "" { 547 | log.Printf("No $GOPATH is set - defaulting to %s", build.Default.GOPATH) 548 | gopathEnv = build.Default.GOPATH 549 | } 550 | 551 | if gopathEnv == "" { 552 | log.Printf("No $GOPATH is set or forwarded to xgo") 553 | } 554 | return gopathEnv 555 | } 556 | 557 | // compileContained cross builds a requested package according to the given build 558 | // specs using the current system opposed to running in a container. This is meant 559 | // to be used for cross compilation already from within an xgo image, allowing the 560 | // inheritance and bundling of the root xgo images. 561 | func compileContained(config *ConfigFlags, flags *BuildFlags, folder string) error { 562 | // If a local build was requested, resolve the import path 563 | local := strings.HasPrefix(config.Repository, string(filepath.Separator)) || strings.HasPrefix(config.Repository, ".") 564 | if local { 565 | config.Repository = resolveImportPath(config.Repository) 566 | } 567 | // Fine tune the original environment variables with those required by the build script 568 | env := []string{ 569 | "REPO_REMOTE=" + config.Remote, 570 | "REPO_BRANCH=" + config.Branch, 571 | "PACK=" + config.Package, 572 | "DEPS=" + config.Dependencies, 573 | "ARGS=" + config.Arguments, 574 | "OUT=" + config.Prefix, 575 | fmt.Sprintf("FLAG_V=%v", flags.Verbose), 576 | fmt.Sprintf("FLAG_X=%v", flags.Steps), 577 | fmt.Sprintf("FLAG_RACE=%v", flags.Race), 578 | fmt.Sprintf("FLAG_TAGS=%s", flags.Tags), 579 | fmt.Sprintf("FLAG_LDFLAGS=%s", flags.LdFlags), 580 | fmt.Sprintf("FLAG_GCFLAGS=%s", flags.GcFlags), 581 | fmt.Sprintf("FLAG_BUILDMODE=%s", flags.Mode), 582 | fmt.Sprintf("FLAG_TRIMPATH=%v", flags.Trimpath), 583 | fmt.Sprintf("FLAG_BUILDVCS=%v", flags.BuildVCS), 584 | fmt.Sprintf("FLAG_OBFUSCATE=%v", flags.Obfuscate), 585 | fmt.Sprintf("GARBLE_FLAGS=%s", flags.GarbleFlags), 586 | "TARGETS=" + strings.Replace(strings.Join(config.Targets, " "), "*", ".", -1), 587 | } 588 | if local { 589 | env = append(env, "EXT_GOPATH=/non-existent-path-to-signal-local-build") 590 | } 591 | // Assemble and run the local cross compilation command 592 | fmt.Printf("Cross compiling %s...\n", config.Repository) 593 | 594 | cmd := exec.Command("/build.sh", config.Repository) 595 | cmd.Env = append(os.Environ(), env...) 596 | 597 | return run(cmd) 598 | } 599 | 600 | // resolveImportPath converts a package given by a relative path to a Go import 601 | // path using the local GOPATH environment. 602 | func resolveImportPath(path string) string { 603 | abs, err := filepath.Abs(path) 604 | if err != nil { 605 | log.Fatalf("Failed to locate requested package: %v.", err) 606 | } 607 | stat, err := os.Stat(abs) 608 | if err != nil || !stat.IsDir() { 609 | log.Fatalf("Requested path invalid.") 610 | } 611 | pack, err := build.ImportDir(abs, build.FindOnly) 612 | if err != nil { 613 | log.Fatalf("Failed to resolve import path: %v.", err) 614 | } 615 | return pack.ImportPath 616 | } 617 | 618 | // Executes a command synchronously, redirecting its output to stdout. 619 | func run(cmd *exec.Cmd) error { 620 | cmd.Stdout = os.Stdout 621 | cmd.Stderr = os.Stderr 622 | 623 | return cmd.Run() 624 | } 625 | --------------------------------------------------------------------------------