├── .dockerignore ├── .editorconfig ├── .gitattributes ├── .github ├── FUNDING.yml ├── dependabot.yml ├── labels.yml └── workflows │ ├── .build.yml │ ├── image.yml │ ├── labels.yml │ ├── test.yml │ └── xgo.yml ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── doc ├── about.md ├── enter-xgo.md ├── installation.md ├── usage.md └── usage │ ├── branch-selection.md │ ├── build-flags.md │ ├── cgo-dependencies.md │ ├── go-releases.md │ ├── limit-build-targets.md │ ├── output-prefixing.md │ ├── package-selection.md │ ├── platform-versions.md │ └── remote-selection.md ├── docker-bake.hcl ├── go.mod ├── rootfs └── usr │ └── local │ └── bin │ ├── semver │ ├── xgo-bootstrap-pure │ ├── xgo-build │ └── xgo-build-deps ├── tests ├── Dockerfile ├── c │ ├── go.mod │ └── main.go ├── cpp │ ├── go.mod │ ├── main.go │ ├── snippet.cpp │ └── snippet.h └── gorm │ ├── go.mod │ ├── go.sum │ └── main.go └── xgo.go /.dockerignore: -------------------------------------------------------------------------------- 1 | /bin 2 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file is for unifying the coding style for different editors and IDEs. 2 | # More information at http://editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | indent_style = space 8 | indent_size = 2 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | 17 | [*.go] 18 | indent_style = tab 19 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | /base/rootfs/usr/local/bin/* linguist-detectable=false 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: crazy-max 2 | custom: https://www.paypal.me/crazyws 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | time: "08:00" 8 | timezone: "Europe/Paris" 9 | labels: 10 | - ":game_die: dependencies" 11 | - ":robot: bot" 12 | -------------------------------------------------------------------------------- /.github/labels.yml: -------------------------------------------------------------------------------- 1 | ## more info https://github.com/crazy-max/ghaction-github-labeler 2 | - # automerge 3 | name: ":bell: automerge" 4 | color: "8f4fbc" 5 | description: "" 6 | - # bot 7 | name: ":robot: bot" 8 | color: "69cde9" 9 | description: "" 10 | - # bug 11 | name: ":bug: bug" 12 | color: "b60205" 13 | description: "" 14 | - # dependencies 15 | name: ":game_die: dependencies" 16 | color: "0366d6" 17 | description: "" 18 | - # documentation 19 | name: ":memo: documentation" 20 | color: "c5def5" 21 | description: "" 22 | - # duplicate 23 | name: ":busts_in_silhouette: duplicate" 24 | color: "cccccc" 25 | description: "" 26 | - # enhancement 27 | name: ":sparkles: enhancement" 28 | color: "0054ca" 29 | description: "" 30 | - # feature request 31 | name: ":bulb: feature request" 32 | color: "0e8a16" 33 | description: "" 34 | - # feedback 35 | name: ":mega: feedback" 36 | color: "03a9f4" 37 | description: "" 38 | - # future maybe 39 | name: ":rocket: future maybe" 40 | color: "fef2c0" 41 | description: "" 42 | - # good first issue 43 | name: ":hatching_chick: good first issue" 44 | color: "7057ff" 45 | description: "" 46 | - # help wanted 47 | name: ":pray: help wanted" 48 | color: "4caf50" 49 | description: "" 50 | - # invalid 51 | name: ":no_entry_sign: invalid" 52 | color: "e6e6e6" 53 | description: "" 54 | - # investigate 55 | name: ":mag: investigate" 56 | color: "e6625b" 57 | description: "" 58 | - # needs more info 59 | name: ":thinking: needs more info" 60 | color: "795548" 61 | description: "" 62 | - # pinned 63 | name: ":pushpin: pinned" 64 | color: "28008e" 65 | description: "" 66 | - # question 67 | name: ":question: question" 68 | color: "3f51b5" 69 | description: "" 70 | - # sponsor 71 | name: ":sparkling_heart: sponsor" 72 | color: "fedbf0" 73 | description: "" 74 | - # stale 75 | name: ":skull: stale" 76 | color: "237da0" 77 | description: "" 78 | - # upstream 79 | name: ":eyes: upstream" 80 | color: "fbca04" 81 | description: "" 82 | - # wontfix 83 | name: ":coffin: wontfix" 84 | color: "ffffff" 85 | description: "" 86 | -------------------------------------------------------------------------------- /.github/workflows/.build.yml: -------------------------------------------------------------------------------- 1 | # reusable workflow 2 | name: .build 3 | 4 | # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions 5 | permissions: 6 | contents: read 7 | 8 | on: 9 | workflow_call: 10 | inputs: 11 | go_version: 12 | type: string 13 | required: true 14 | latest_current: 15 | type: string 16 | required: true 17 | latest_previous: 18 | type: string 19 | required: true 20 | 21 | env: 22 | DOCKERHUB_SLUG: crazymax/xgo 23 | GHCR_SLUG: ghcr.io/crazy-max/xgo 24 | 25 | jobs: 26 | prepare: 27 | runs-on: ubuntu-24.04 28 | outputs: 29 | matrix: ${{ steps.platforms.outputs.matrix }} 30 | steps: 31 | - 32 | name: Checkout 33 | uses: actions/checkout@v4 34 | - 35 | name: Create matrix 36 | id: platforms 37 | run: | 38 | echo "matrix=$(docker buildx bake image-all --print | jq -cr '.target."image-all".platforms')" >>${GITHUB_OUTPUT} 39 | - 40 | name: Show matrix 41 | run: | 42 | echo ${{ steps.platforms.outputs.matrix }} 43 | - 44 | name: Tags 45 | id: tags 46 | uses: actions/github-script@v7 47 | with: 48 | result-encoding: string 49 | script: | 50 | const os = require('os'); 51 | const majorMinor = "${{ inputs.go_version }}".match(/[0-9]+\.[0-9]+/g)[0]; 52 | const tags = ["${{ inputs.go_version }}"]; 53 | switch ("${{ inputs.go_version }}") { 54 | case "${{ inputs.latest_current }}": { 55 | tags.push(`${majorMinor}.x`, majorMinor, "latest"); 56 | break; 57 | } 58 | case "${{ inputs.latest_previous }}": { 59 | tags.push(`${majorMinor}.x`, majorMinor); 60 | break; 61 | } 62 | } 63 | return tags.join(os.EOL); 64 | - 65 | name: Docker meta 66 | id: meta 67 | uses: docker/metadata-action@v5 68 | with: 69 | images: | 70 | ${{ env.DOCKERHUB_SLUG }} 71 | ${{ env.GHCR_SLUG }} 72 | tags: ${{ steps.tags.outputs.result }} 73 | labels: | 74 | org.opencontainers.image.title=xgo (go-${{ inputs.go_version }}) 75 | org.opencontainers.image.description=Go CGO cross compiler 76 | org.opencontainers.image.vendor=CrazyMax 77 | - 78 | name: Rename meta bake definition file 79 | run: | 80 | mv "${{ steps.meta.outputs.bake-file }}" "/tmp/bake-meta.json" 81 | - 82 | name: Upload meta bake definition 83 | uses: actions/upload-artifact@v4 84 | with: 85 | name: bake-meta-${{ inputs.go_version }} 86 | path: /tmp/bake-meta.json 87 | if-no-files-found: error 88 | retention-days: 1 89 | 90 | build: 91 | runs-on: ${{ startsWith(matrix.platform, 'linux/arm') && 'ubuntu-24.04-arm' || 'ubuntu-latest' }} 92 | permissions: 93 | # same as global permissions 94 | contents: read 95 | # required to push to GHCR 96 | packages: write 97 | needs: 98 | - prepare 99 | strategy: 100 | fail-fast: false 101 | matrix: 102 | platform: ${{ fromJson(needs.prepare.outputs.matrix) }} 103 | steps: 104 | - 105 | name: Prepare 106 | run: | 107 | platform=${{ matrix.platform }} 108 | echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV 109 | - 110 | name: Download meta bake definition 111 | uses: actions/download-artifact@v4 112 | with: 113 | name: bake-meta-${{ inputs.go_version }} 114 | path: /tmp 115 | - 116 | name: Login to DockerHub 117 | if: github.event_name != 'pull_request' 118 | uses: docker/login-action@v3 119 | with: 120 | username: ${{ secrets.DOCKER_USERNAME }} 121 | password: ${{ secrets.DOCKER_PASSWORD }} 122 | - 123 | name: Login to GHCR 124 | if: github.event_name != 'pull_request' 125 | uses: docker/login-action@v3 126 | with: 127 | registry: ghcr.io 128 | username: ${{ github.repository_owner }} 129 | password: ${{ secrets.GITHUB_TOKEN }} 130 | - 131 | name: Set up Docker Buildx 132 | uses: docker/setup-buildx-action@v3 133 | - 134 | name: Build 135 | id: bake 136 | uses: docker/bake-action@v6 137 | with: 138 | files: | 139 | ./docker-bake.hcl 140 | cwd:///tmp/bake-meta.json 141 | targets: image 142 | set: | 143 | *.tags= 144 | *.platform=${{ matrix.platform }} 145 | *.args.GO_VERSION=${{ inputs.go_version }} 146 | *.cache-from=type=gha,scope=go-${{ inputs.go_version }}-${{ env.PLATFORM_PAIR }} 147 | *.cache-to=type=gha,scope=go-${{ inputs.go_version }}-${{ env.PLATFORM_PAIR }} 148 | *.output=type=image,"name=${{ env.DOCKERHUB_SLUG }},${{ env.GHCR_SLUG }}",push-by-digest=true,name-canonical=true,push=${{ github.event_name != 'pull_request' }} 149 | - 150 | name: Export digest 151 | run: | 152 | mkdir -p /tmp/digests 153 | digest="${{ fromJSON(steps.bake.outputs.metadata).image['containerimage.digest'] }}" 154 | touch "/tmp/digests/${digest#sha256:}" 155 | - 156 | name: Upload digest 157 | uses: actions/upload-artifact@v4 158 | with: 159 | name: digests-${{ inputs.go_version }}-${{ env.PLATFORM_PAIR }} 160 | path: /tmp/digests/* 161 | if-no-files-found: error 162 | retention-days: 1 163 | 164 | merge: 165 | runs-on: ubuntu-latest 166 | if: github.event_name != 'pull_request' 167 | permissions: 168 | # same as global permissions 169 | contents: read 170 | # required to push to GHCR 171 | packages: write 172 | needs: 173 | - build 174 | steps: 175 | - 176 | name: Download meta bake definition 177 | uses: actions/download-artifact@v4 178 | with: 179 | name: bake-meta-${{ inputs.go_version }} 180 | path: /tmp 181 | - 182 | name: Download digests 183 | uses: actions/download-artifact@v4 184 | with: 185 | path: /tmp/digests 186 | pattern: digests-${{ inputs.go_version }}-* 187 | merge-multiple: true 188 | - 189 | name: Set up Docker Buildx 190 | uses: docker/setup-buildx-action@v3 191 | - 192 | name: Login to DockerHub 193 | uses: docker/login-action@v3 194 | with: 195 | username: ${{ secrets.DOCKER_USERNAME }} 196 | password: ${{ secrets.DOCKER_PASSWORD }} 197 | - 198 | name: Login to GHCR 199 | uses: docker/login-action@v3 200 | with: 201 | registry: ghcr.io 202 | username: ${{ github.repository_owner }} 203 | password: ${{ secrets.GITHUB_TOKEN }} 204 | - 205 | name: Create manifest list and push 206 | working-directory: /tmp/digests 207 | run: | 208 | docker buildx imagetools create $(jq -cr '.target."docker-metadata-action".tags | map(select(startswith("${{ env.DOCKERHUB_SLUG }}")) | "-t " + .) | join(" ")' /tmp/bake-meta.json) \ 209 | $(printf '${{ env.DOCKERHUB_SLUG }}@sha256:%s ' *) 210 | docker buildx imagetools create $(jq -cr '.target."docker-metadata-action".tags | map(select(startswith("${{ env.GHCR_SLUG }}")) | "-t " + .) | join(" ")' /tmp/bake-meta.json) \ 211 | $(printf '${{ env.GHCR_SLUG }}@sha256:%s ' *) 212 | - 213 | name: Inspect image 214 | run: | 215 | tag=$(jq -r '.target."docker-metadata-action".args.DOCKER_META_VERSION' /tmp/bake-meta.json) 216 | docker buildx imagetools inspect ${{ env.DOCKERHUB_SLUG }}:${tag} 217 | docker buildx imagetools inspect ${{ env.GHCR_SLUG }}:${tag} 218 | -------------------------------------------------------------------------------- /.github/workflows/image.yml: -------------------------------------------------------------------------------- 1 | name: image 2 | 3 | concurrency: 4 | group: ${{ github.workflow }}-${{ github.ref }} 5 | cancel-in-progress: true 6 | 7 | # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions 8 | permissions: 9 | contents: read 10 | 11 | on: 12 | schedule: 13 | - cron: '0 8 */6 * *' # every 6 days to keep cache 14 | push: 15 | branches: 16 | - 'master' 17 | tags: 18 | - 'v*' 19 | paths-ignore: 20 | - '**.md' 21 | pull_request: 22 | paths-ignore: 23 | - '**.md' 24 | 25 | jobs: 26 | build: 27 | uses: ./.github/workflows/.build.yml 28 | permissions: 29 | # same as global permissions 30 | contents: read 31 | # required to push to GHCR 32 | packages: write 33 | secrets: inherit 34 | strategy: 35 | fail-fast: false 36 | matrix: 37 | go_version: 38 | - "1.23.0" 39 | - "1.23.1" 40 | - "1.23.2" 41 | - "1.23.3" 42 | - "1.23.4" 43 | - "1.23.5" 44 | - "1.23.6" 45 | - "1.23.7" 46 | - "1.24.0" 47 | - "1.24.1" 48 | with: 49 | go_version: ${{ matrix.go_version }} 50 | latest_current: "1.24.1" 51 | latest_previous: "1.23.7" 52 | -------------------------------------------------------------------------------- /.github/workflows/labels.yml: -------------------------------------------------------------------------------- 1 | name: labels 2 | 3 | concurrency: 4 | group: ${{ github.workflow }}-${{ github.ref }} 5 | cancel-in-progress: true 6 | 7 | # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions 8 | permissions: 9 | contents: read 10 | 11 | on: 12 | push: 13 | branches: 14 | - 'master' 15 | paths: 16 | - '.github/labels.yml' 17 | - '.github/workflows/labels.yml' 18 | pull_request: 19 | paths: 20 | - '.github/labels.yml' 21 | - '.github/workflows/labels.yml' 22 | 23 | jobs: 24 | labeler: 25 | runs-on: ubuntu-latest 26 | permissions: 27 | # same as global permissions 28 | contents: read 29 | # required to update labels 30 | issues: write 31 | steps: 32 | - 33 | name: Checkout 34 | uses: actions/checkout@v4 35 | - 36 | name: Run Labeler 37 | uses: crazy-max/ghaction-github-labeler@v5 38 | with: 39 | dry-run: ${{ github.event_name == 'pull_request' }} 40 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | concurrency: 4 | group: ${{ github.workflow }}-${{ github.ref }} 5 | cancel-in-progress: true 6 | 7 | # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions 8 | permissions: 9 | contents: read 10 | 11 | on: 12 | push: 13 | branches: 14 | - 'master' 15 | paths-ignore: 16 | - '**.md' 17 | pull_request: 18 | paths-ignore: 19 | - '**.md' 20 | 21 | jobs: 22 | test: 23 | runs-on: ubuntu-latest 24 | strategy: 25 | fail-fast: false 26 | matrix: 27 | go_version: 28 | - "1.24.1" 29 | - "1.23.7" 30 | case: 31 | - c 32 | - cpp 33 | - gorm 34 | - ffmerger 35 | steps: 36 | - 37 | name: Build xgo 38 | uses: docker/bake-action@v6 39 | with: 40 | targets: image 41 | set: | 42 | *.tags=xgo:${{ matrix.go_version }} 43 | *.args.GO_VERSION=${{ matrix.go_version }} 44 | *.output=type=docker 45 | *.cache-from=type=gha,scope=go-${{ matrix.go_version }}-linux-amd64 46 | - 47 | name: Test ${{ matrix.case }} for go ${{ matrix.go_version }} 48 | uses: docker/bake-action@v6 49 | with: 50 | targets: test-${{ matrix.case }} 51 | env: 52 | BASE_IMAGE: xgo:${{ matrix.go_version }} 53 | -------------------------------------------------------------------------------- /.github/workflows/xgo.yml: -------------------------------------------------------------------------------- 1 | name: xgo 2 | 3 | concurrency: 4 | group: ${{ github.workflow }}-${{ github.ref }} 5 | cancel-in-progress: true 6 | 7 | # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions 8 | permissions: 9 | contents: read 10 | 11 | on: 12 | push: 13 | branches: 14 | - 'master' 15 | tags: 16 | - 'v*' 17 | paths-ignore: 18 | - '**.md' 19 | pull_request: 20 | branches: 21 | - 'master' 22 | paths-ignore: 23 | - '**.md' 24 | 25 | env: 26 | DESTDIR: ./bin 27 | 28 | jobs: 29 | build: 30 | runs-on: ubuntu-latest 31 | permissions: 32 | # required to create GitHub release 33 | contents: write 34 | steps: 35 | - 36 | name: Set up Docker Buildx 37 | uses: docker/setup-buildx-action@v3 38 | - 39 | name: Build artifacts 40 | uses: docker/bake-action@v6 41 | with: 42 | targets: artifact-all 43 | provenance: false 44 | - 45 | name: Release 46 | uses: docker/bake-action@v6 47 | with: 48 | targets: release 49 | provenance: false 50 | set: | 51 | *.contexts.artifacts=cwd://${{ env.DESTDIR }}/artifact 52 | - 53 | name: Upload artifacts 54 | uses: actions/upload-artifact@v4 55 | with: 56 | name: xgo 57 | path: ${{ env.DESTDIR }}/release/* 58 | if-no-files-found: error 59 | - 60 | name: GitHub Release 61 | uses: softprops/action-gh-release@v2 62 | if: startsWith(github.ref, 'refs/tags/v') 63 | with: 64 | draft: true 65 | files: | 66 | ${{ env.DESTDIR }}/release/* 67 | env: 68 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 69 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /bin 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | ARG GO_VERSION="1.24.1" 4 | ARG OSXCROSS_VERSION="11.3" 5 | ARG GHQ_VERSION="1.6.1" 6 | ARG XX_VERSION="1.6.1" 7 | ARG ALPINE_VERSION="3.21" 8 | ARG PLATFORMS="linux/386 linux/amd64 linux/arm64 linux/arm/v5 linux/arm/v6 linux/arm/v7 linux/mips linux/mipsle linux/mips64 linux/mips64le linux/ppc64le linux/riscv64 linux/s390x windows/386 windows/amd64" 9 | 10 | FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx 11 | FROM --platform=$BUILDPLATFORM golang:1.23-alpine${ALPINE_VERSION} AS base 12 | COPY --from=xx / / 13 | ENV CGO_ENABLED=0 14 | RUN apk add --no-cache file git 15 | WORKDIR /src 16 | 17 | FROM base AS ghq 18 | ARG GHQ_VERSION 19 | RUN --mount=type=cache,target=/go/pkg/mod \ 20 | go install github.com/x-motemen/ghq@v${GHQ_VERSION} 21 | 22 | FROM base AS version 23 | ARG GIT_REF 24 | RUN --mount=target=. </dev/null || cp /artifacts/* /out/ 89 | sha256sum -b xgo_* > ./checksums.txt 90 | sha256sum -c --strict checksums.txt 91 | EOT 92 | 93 | FROM scratch AS release 94 | COPY --link --from=releaser /out / 95 | 96 | FROM crazymax/goxx:${GO_VERSION} AS goxx-base 97 | ARG PLATFORMS 98 | RUN < 4 | Copyright (c) 2019-2024 CrazyMax 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 | [![GitHub release](https://img.shields.io/github/release/crazy-max/xgo.svg?style=flat-square)](https://github.com/crazy-max/xgo/releases/latest) 2 | [![Total downloads](https://img.shields.io/github/downloads/crazy-max/xgo/total.svg?style=flat-square)](https://github.com/crazy-max/xgo/releases/latest) 3 | [![Build Status](https://img.shields.io/github/actions/workflow/status/crazy-max/xgo/image.yml?branch=master&label=build&logo=github&style=flat-square)](https://github.com/crazy-max/xgo/actions?query=workflow%3Aimage) 4 | [![Test Status](https://img.shields.io/github/actions/workflow/status/crazy-max/xgo/test.yml?branch=master&label=test&logo=github&style=flat-square)](https://github.com/crazy-max/xgo/actions?query=workflow%3Atest) 5 | [![Docker Stars](https://img.shields.io/docker/stars/crazymax/xgo.svg?style=flat-square&logo=docker)](https://hub.docker.com/r/crazymax/xgo/) 6 | [![Docker Pulls](https://img.shields.io/docker/pulls/crazymax/xgo.svg?style=flat-square&logo=docker)](https://hub.docker.com/r/crazymax/xgo/) 7 | [![Go Report Card](https://goreportcard.com/badge/github.com/crazy-max/xgo)](https://goreportcard.com/report/github.com/crazy-max/xgo) 8 | 9 | [![Become a sponsor](https://img.shields.io/badge/sponsor-crazy--max-181717.svg?logo=github&style=flat-square)](https://github.com/sponsors/crazy-max) 10 | [![Donate Paypal](https://img.shields.io/badge/donate-paypal-00457c.svg?logo=paypal&style=flat-square)](https://www.paypal.me/crazyws) 11 | 12 | ___ 13 | 14 | * [Fork](#fork) 15 | * [Build](#Build) 16 | * [Documentation](#documentation) 17 | * [Contributing](#contributing) 18 | * [Lisence](#license) 19 | 20 | ## Fork 21 | 22 | This repository is a fork of [karalabe/xgo](https://github.com/karalabe/xgo) to 23 | push images and tags to a single docker repository on several registries to make 24 | things more consistent for users. It uses [`goxx` image](https://github.com/crazy-max/goxx) 25 | as base that provides all the necessary Go tool-chains, C/C++ cross-compilers 26 | and platform headers/libraries. 27 | 28 | | Registry | Image | 29 | |------------------------------------------------------------------------------------------------|-------------------------| 30 | | [Docker Hub](https://hub.docker.com/r/crazymax/xgo/) | `crazymax/xgo` | 31 | | [GitHub Container Registry](https://github.com/users/crazy-max/packages/container/package/xgo) | `ghcr.io/crazy-max/xgo` | 32 | 33 | ``` 34 | $ docker buildx imagetools inspect crazymax/xgo --format "{{json .Manifest}}" | \ 35 | jq -r '.manifests[] | select(.platform.os != null and .platform.os != "unknown") | .platform | "\(.os)/\(.architecture)\(if .variant then "/" + .variant else "" end)"' 36 | 37 | linux/amd64 38 | linux/arm64 39 | ``` 40 | 41 | This also creates a [standalone xgo executable](https://github.com/crazy-max/xgo/releases/latest) 42 | that can be used on many platforms. 43 | 44 | ## Build 45 | 46 | Build xgo yourself using Docker [`buildx bake`](https://github.com/docker/buildx/blob/master/docs/reference/buildx_bake.md): 47 | 48 | ```shell 49 | git clone https://github.com/crazy-max/xgo.git xgo 50 | cd xgo 51 | 52 | # Build xgo image and output to docker with xgo:local tag 53 | docker buildx bake 54 | 55 | # Tests 56 | BASE_IMAGE=xgo:local docker buildx bake test-c 57 | BASE_IMAGE=xgo:local docker buildx bake test-cpp 58 | BASE_IMAGE=xgo:local docker buildx bake test-gorm 59 | 60 | # Create xgo artifacts in ./dist 61 | docker buildx bake artifact-all 62 | ``` 63 | 64 | ## Documentation 65 | 66 | * [About](doc/about.md) 67 | * [Enter xgo](doc/enter-xgo.md) 68 | * [Installation](doc/installation.md) 69 | * [Usage](doc/usage.md) 70 | * [Build flags](doc/usage/build-flags.md) 71 | * [Go releases](doc/usage/go-releases.md) 72 | * [Output prefixing](doc/usage/output-prefixing.md) 73 | * [Branch selection](doc/usage/branch-selection.md) 74 | * [Remote selection](doc/usage/remote-selection.md) 75 | * [Package selection](doc/usage/package-selection.md) 76 | * [Limit build targets](doc/usage/limit-build-targets.md) 77 | * [Platform versions](doc/usage/platform-versions.md) 78 | * [CGO dependencies](doc/usage/cgo-dependencies.md) 79 | 80 | ## Contributing 81 | 82 | Want to contribute? Awesome! The most basic way to show your support is to star 83 | the project, or to raise issues. You can also support this project by [**becoming a sponsor on GitHub**](https://github.com/sponsors/crazy-max) 84 | or by making a [PayPal donation](https://www.paypal.me/crazyws) to ensure this 85 | journey continues indefinitely! 86 | 87 | Thanks again for your support, it is much appreciated! :pray: 88 | 89 | ## License 90 | 91 | MIT. See `LICENSE` for more details. 92 | -------------------------------------------------------------------------------- /doc/about.md: -------------------------------------------------------------------------------- 1 | ## About 2 | 3 | Although Go strives to be a cross platform language, cross compilation from one 4 | platform to another is not as simple as it could be, as you need the Go sources 5 | bootstrapped to each platform and architecture. 6 | 7 | The first step towards cross compiling was Dave Cheney's [golang-crosscompile](https://github.com/davecheney/golang-crosscompile) 8 | package, which automatically bootstrapped the necessary sources based on your 9 | existing Go installation. Although this was enough for a lot of cases, certain 10 | drawbacks became apparent where the official libraries used CGO internally: any 11 | dependency to third party platform code is unavailable, hence those parts don't 12 | cross compile nicely (native DNS resolution, system certificate access, etc). 13 | 14 | A step forward in enabling cross compilation was Alan Shreve's [gonative](https://github.com/inconshreveable/gonative) 15 | package, which instead of bootstrapping the different platforms based on the 16 | existing Go installation, downloaded the official pre-compiled binaries from the 17 | golang website and injected those into the local toolchain. Since the pre-built 18 | binaries already contained the necessary platform specific code, the few missing 19 | dependencies were resolved, and true cross compilation could commence... of pure 20 | Go code. 21 | 22 | However, there was still one feature missing: cross compiling Go code that used 23 | CGO itself, which isn't trivial since you need access to OS specific headers and 24 | libraries. This becomes very annoying when you need access only to some trivial 25 | OS specific functionality (e.g. query the CPU load), but need to configure and 26 | maintain separate build environments to do it. 27 | -------------------------------------------------------------------------------- /doc/enter-xgo.md: -------------------------------------------------------------------------------- 1 | # Enter xgo 2 | 3 | My solution to the challenge of cross compiling Go code with embedded C/C++ snippets 4 | (i.e. CGO_ENABLED=1) is based on the concept of [lightweight Linux containers](http://en.wikipedia.org/wiki/LXC). 5 | All the necessary Go tool-chains, C cross compilers and platform headers/libraries 6 | have been assembled into a single Docker container, which can then be called as if 7 | a single command to compile a Go package to various platforms and architectures. 8 | -------------------------------------------------------------------------------- /doc/installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | Although you could build the container manually, it is available as an automatic 4 | trusted build from Docker's container registry (not insignificant in size): 5 | 6 | ```shell 7 | docker pull crazymax/xgo:latest 8 | ``` 9 | 10 | To prevent having to remember a potentially complex Docker command every time, 11 | a lightweight Go wrapper was written on top of it. 12 | 13 | ```shell 14 | go get github.com/crazy-max/xgo 15 | ``` 16 | 17 | For go 1.17 or up, `go get` is deprecated, so you'll have to use this command: 18 | 19 | ```shell 20 | go install github.com/crazy-max/xgo@latest 21 | ``` 22 | -------------------------------------------------------------------------------- /doc/usage.md: -------------------------------------------------------------------------------- 1 | # Usage 2 | 3 | Simply specify the import path you want to build, and xgo will do the rest: 4 | 5 | ```shell 6 | xgo github.com/project-iris/iris 7 | ... 8 | ls -al 9 | ``` 10 | ```text 11 | -rwxr-xr-x 1 root root 6776500 Nov 24 16:44 iris-darwin-386 12 | -rwxr-xr-x 1 root root 8755532 Nov 24 16:44 iris-darwin-amd64 13 | -rwxr-xr-x 1 root root 10135248 Nov 24 16:44 iris-linux-386 14 | -rwxr-xr-x 1 root root 12598472 Nov 24 16:44 iris-linux-amd64 15 | -rwxr-xr-x 1 root root 10040464 Nov 24 16:44 iris-linux-arm 16 | -rwxr-xr-x 1 root root 7516368 Nov 24 16:44 iris-windows-386.exe 17 | -rwxr-xr-x 1 root root 9549416 Nov 24 16:44 iris-windows-amd64.exe 18 | ``` 19 | 20 | If the path is not a canonical import path, but rather a local path (starts with 21 | a dot `.` or a dash `/`), xgo will use the local GOPATH contents for the cross 22 | compilation. 23 | -------------------------------------------------------------------------------- /doc/usage/branch-selection.md: -------------------------------------------------------------------------------- 1 | # Branch selection 2 | 3 | Similarly to `go get`, xgo also uses the `master` branch of a repository during 4 | source code retrieval. To switch to a different branch before compilation pass 5 | the desired branch name through the `--branch` argument. 6 | 7 | ```shell 8 | xgo --branch release-branch.go1.4 golang.org/x/tools/cmd/goimports 9 | ... 10 | ls -al 11 | ``` 12 | ```text 13 | -rwxr-xr-x 1 root root 4139868 Nov 24 16:40 goimports-darwin-386 14 | -rwxr-xr-x 1 root root 5186720 Nov 24 16:40 goimports-darwin-amd64 15 | -rwxr-xr-x 1 root root 4189456 Nov 24 16:40 goimports-linux-386 16 | -rwxr-xr-x 1 root root 5264136 Nov 24 16:40 goimports-linux-amd64 17 | -rwxr-xr-x 1 root root 4209416 Nov 24 16:40 goimports-linux-arm 18 | -rwxr-xr-x 1 root root 4348416 Nov 24 16:40 goimports-windows-386.exe 19 | -rwxr-xr-x 1 root root 5415424 Nov 24 16:40 goimports-windows-amd64.exe 20 | ``` 21 | -------------------------------------------------------------------------------- /doc/usage/build-flags.md: -------------------------------------------------------------------------------- 1 | # Build flags 2 | 3 | A handful of flags can be passed to `go build`. The currently supported ones are: 4 | 5 | * `-v`: prints the names of packages as they are compiled 6 | * `-x`: prints the build commands as compilation progresses 7 | * `-race`: enables data race detection (supported only on amd64, rest built without) 8 | * `-tags=`: list of build tags to consider satisfied during the build 9 | * `-ldflags=`: arguments to pass on each go tool link invocation 10 | * `-buildmode=`: binary type to produce by the compiler 11 | * `-buildvcs=`: whether to stamp binaries with version control information 12 | * `-trimpath`: remove all file system paths from the resulting executable 13 | * `-hidewindow`: hide the console window during GUI execution on Windows 14 | -------------------------------------------------------------------------------- /doc/usage/cgo-dependencies.md: -------------------------------------------------------------------------------- 1 | ### CGO dependencies 2 | 3 | The main differentiator of xgo versus other cross compilers is support for basic 4 | embedded C/C++ code and target-platform specific OS SDK availability. The current 5 | xgo release introduces an experimental CGO *dependency* cross compilation, enabling 6 | building Go programs that require external C/C++ libraries. 7 | 8 | It is assumed that the dependent C/C++ library is `configure/make` based, was 9 | properly prepared for cross compilation and is available as a tarball download 10 | (`.tar`, `.tar.gz` or `.tar.bz2`). Further plans include extending this to cmake 11 | based projects, if need arises (please open an issue if it's important to you). 12 | 13 | Such dependencies can be added via the `--deps` argument. They will be retrieved 14 | prior to starting the cross compilation and the packages cached to save bandwidth 15 | on subsequent calls. 16 | 17 | A complex sample for such a scenario is building the Ethereum CLI node, which has 18 | the GNU Multiple Precision Arithmetic Library as it's dependency. 19 | 20 | ```shell 21 | $ xgo --deps=https://gmplib.org/download/gmp/gmp-6.1.0.tar.bz2 --targets=windows/* github.com/ethereum/go-ethereum/cmd/geth 22 | ... 23 | ls -al 24 | ``` 25 | ```text 26 | -rwxr-xr-x 1 root root 16315679 Nov 24 16:39 geth-windows-386.exe 27 | -rwxr-xr-x 1 root root 19452036 Nov 24 16:38 geth-windows-amd64.exe 28 | ``` 29 | 30 | Some trivial arguments may be passed to the dependencies' configure script via `--depsargs`. 31 | 32 | ```shell 33 | xgo --deps=https://gmplib.org/download/gmp/gmp-6.1.0.tar.bz2 --targets=ios/* --depsargs=--disable-assembly github.com/ethereum/go-ethereum/cmd/geth 34 | ... 35 | ls -al 36 | ``` 37 | ```text 38 | -rwxr-xr-x 1 root root 14804160 Nov 24 16:32 geth-ios-5.0-arm 39 | ``` 40 | 41 | Note, that since xgo needs to cross compile the dependencies for each platform 42 | and architecture separately, build time can increase significantly. 43 | -------------------------------------------------------------------------------- /doc/usage/go-releases.md: -------------------------------------------------------------------------------- 1 | # Go releases 2 | 3 | As newer versions of the language runtime, libraries and tools get released, 4 | these will get incorporated into xgo too as extensions layers to the base cross 5 | compilation image (only Go 1.3 and above will be supported). 6 | 7 | You can select which Go release to work with through the `-go` command line flag 8 | to xgo and if the specific release was already integrated, it will automatically 9 | be retrieved and installed. 10 | 11 | ```shell 12 | xgo -go 1.16.1 github.com/project-iris/iris 13 | ... 14 | ``` 15 | 16 | Additionally, a few wildcard release strings are also supported: 17 | 18 | * `latest` will use the latest Go release (this is the default) 19 | * `1.16.x` will use the latest point release of a specific Go version 20 | -------------------------------------------------------------------------------- /doc/usage/limit-build-targets.md: -------------------------------------------------------------------------------- 1 | # Limit build targets 2 | 3 | By default `xgo` will try and build the specified package to all platforms and 4 | architectures supported by the underlying Go runtime. If you wish to restrict 5 | the build to only a few target systems, use the comma separated `--targets` CLI 6 | argument: 7 | 8 | * `--targets=linux/arm`: builds only the ARMv5 Linux binaries (`arm-6`/`arm-7` allowed) 9 | * `--targets=windows/*,darwin/*`: builds all Windows and OSX binaries 10 | * `--targets=*/arm`: builds ARM binaries for all platforms 11 | * `--targets=*/*`: builds all suppoted targets (default) 12 | 13 | The supported targets are: 14 | 15 | * Platforms: `darwin`, `linux`, `windows` 16 | * Achitectures: `386`, `amd64`, `arm-5`, `arm-6`, `arm-7`, `arm64`, `mips`, `mipsle`, `mips64`, `mips64le`, `ppc64le`, `s390x` 17 | -------------------------------------------------------------------------------- /doc/usage/output-prefixing.md: -------------------------------------------------------------------------------- 1 | # Output prefixing 2 | 3 | xgo by default uses the name of the package being cross compiled as the output 4 | file prefix. This can be overridden with the `-out` flag. 5 | 6 | ```shell 7 | xgo -out iris-v0.3.2 github.com/project-iris/iris 8 | ... 9 | ls -al 10 | ``` 11 | ```text 12 | -rwxr-xr-x 1 root root 6776500 Nov 24 16:44 iris-v0.3.2-darwin-386 13 | -rwxr-xr-x 1 root root 8755532 Nov 24 16:44 iris-v0.3.2-darwin-amd64 14 | -rwxr-xr-x 1 root root 10135248 Nov 24 16:44 iris-v0.3.2-linux-386 15 | -rwxr-xr-x 1 root root 12598472 Nov 24 16:44 iris-v0.3.2-linux-amd64 16 | -rwxr-xr-x 1 root root 10040464 Nov 24 16:44 iris-v0.3.2-linux-arm 17 | -rwxr-xr-x 1 root root 7516368 Nov 24 16:44 iris-v0.3.2-windows-386.exe 18 | -rwxr-xr-x 1 root root 9549416 Nov 24 16:44 iris-v0.3.2-windows-amd64.exe 19 | ``` 20 | -------------------------------------------------------------------------------- /doc/usage/package-selection.md: -------------------------------------------------------------------------------- 1 | # Package selection 2 | 3 | If you used the above *branch* or *remote* selection machanisms, it may happen 4 | that the path you are trying to build is only present in the specific branch and 5 | not the default repository, causing Go to fail at locating it. To circumvent this, 6 | you may specify only the repository root for xgo, and use an additional `--pkg` 7 | parameter to select the exact package within, honoring any prior *branch* and 8 | *remote* selections. 9 | 10 | ```shell 11 | xgo --pkg cmd/goimports golang.org/x/tools 12 | ... 13 | ls -al 14 | ``` 15 | ```text 16 | -rwxr-xr-x 1 root root 4164448 Nov 24 16:38 goimports-darwin-386 17 | -rwxr-xr-x 1 root root 5223584 Nov 24 16:38 goimports-darwin-amd64 18 | -rwxr-xr-x 1 root root 4217184 Nov 24 16:38 goimports-linux-386 19 | -rwxr-xr-x 1 root root 5295768 Nov 24 16:38 goimports-linux-amd64 20 | -rwxr-xr-x 1 root root 4233120 Nov 24 16:38 goimports-linux-arm 21 | -rwxr-xr-x 1 root root 4373504 Nov 24 16:38 goimports-windows-386.exe 22 | -rwxr-xr-x 1 root root 5450240 Nov 24 16:38 goimports-windows-amd64.exe 23 | ``` 24 | 25 | This argument may at some point be integrated into the import path itself, but for 26 | now it exists as an independent build parameter. Also, there is not possibility 27 | for now to build mulitple commands in one go. 28 | -------------------------------------------------------------------------------- /doc/usage/platform-versions.md: -------------------------------------------------------------------------------- 1 | # Platform versions 2 | 3 | By default `xgo` tries to cross compile to the lowest possible versions of every 4 | supported platform, in order to produce binaries that are portable among various 5 | versions of the same operating system. This however can lead to issues if a used 6 | dependency is only supported by more recent systems. As such, `xgo` supports the 7 | selection of specific platform versions by appending them to the OS target string. 8 | 9 | * `--targets=darwin-11.3/*`: cross compile to Mac OS X Mavericks 10 | * `--targets=windows-6.0/*`: cross compile to Windows Vista 11 | 12 | The supported platforms are: 13 | 14 | * All Windows APIs up to Windows 8.1 limited by `mingw-w64` ([API level ids](https://en.wikipedia.org/wiki/Windows_NT#Releases)) 15 | * OSX APIs in the range of 10.6 - 11.3 16 | -------------------------------------------------------------------------------- /doc/usage/remote-selection.md: -------------------------------------------------------------------------------- 1 | # Remote selection 2 | 3 | Yet again similarly to `go get`, xgo uses the repository remote corresponding to 4 | the import path being built. To switch to a different remote while preserving the 5 | original import path, use the `--remote` argument. 6 | 7 | ```shell 8 | xgo --remote github.com/golang/tools golang.org/x/tools/cmd/goimports 9 | ... 10 | ``` 11 | -------------------------------------------------------------------------------- /docker-bake.hcl: -------------------------------------------------------------------------------- 1 | variable "GO_VERSION" { 2 | default = null 3 | } 4 | 5 | variable "DESTDIR" { 6 | default = "./bin" 7 | } 8 | 9 | # GITHUB_REF is the actual ref that triggers the workflow and used as version 10 | # when tag is pushed: https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables#default-environment-variables 11 | variable "GITHUB_REF" { 12 | default = "" 13 | } 14 | 15 | target "_common" { 16 | args = { 17 | GO_VERSION = GO_VERSION 18 | GIT_REF = GITHUB_REF 19 | BUILDKIT_CONTEXT_KEEP_GIT_DIR = 1 20 | } 21 | } 22 | 23 | target "_platforms" { 24 | platforms = [ 25 | "linux/amd64", 26 | "linux/arm64" 27 | ] 28 | } 29 | 30 | # Special target: https://github.com/docker/metadata-action#bake-definition 31 | target "docker-metadata-action" { 32 | tags = ["xgo:local"] 33 | } 34 | 35 | group "default" { 36 | targets = ["image-local"] 37 | } 38 | 39 | target "image" { 40 | inherits = ["_common", "docker-metadata-action"] 41 | } 42 | 43 | target "image-local" { 44 | inherits = ["image"] 45 | output = ["type=docker"] 46 | } 47 | 48 | target "image-all" { 49 | inherits = ["image", "_platforms"] 50 | } 51 | 52 | target "artifact" { 53 | inherits = ["_common", "docker-metadata-action"] 54 | target = "artifact" 55 | output = ["${DESTDIR}/artifact"] 56 | } 57 | 58 | target "artifact-all" { 59 | inherits = ["artifact"] 60 | platforms = [ 61 | "darwin/amd64", 62 | "darwin/arm64", 63 | "linux/386", 64 | "linux/amd64", 65 | "linux/arm64", 66 | "linux/arm/v5", 67 | "linux/arm/v6", 68 | "linux/arm/v7", 69 | "linux/ppc64le", 70 | "linux/s390x", 71 | "windows/amd64", 72 | "windows/arm64", 73 | "windows/386" 74 | ] 75 | } 76 | 77 | target "release" { 78 | target = "release" 79 | output = ["${DESTDIR}/release"] 80 | contexts = { 81 | artifacts = "${DESTDIR}/artifact" 82 | } 83 | } 84 | 85 | variable "BASE_IMAGE" { 86 | default = "ghcr.io/crazy-max/xgo:latest" 87 | } 88 | 89 | target "test" { 90 | inherits = ["_common"] 91 | context = "./tests" 92 | args = { 93 | BASE_IMAGE = BASE_IMAGE 94 | } 95 | } 96 | 97 | target "test-gorm" { 98 | inherits = ["test"] 99 | args = { 100 | PROJECT = "./gorm" 101 | BRANCH = "" 102 | } 103 | } 104 | 105 | target "test-c" { 106 | inherits = ["test"] 107 | args = { 108 | PROJECT = "./c" 109 | BRANCH = "" 110 | } 111 | } 112 | 113 | target "test-cpp" { 114 | inherits = ["test"] 115 | args = { 116 | PROJECT = "./cpp" 117 | BRANCH = "" 118 | } 119 | } 120 | 121 | target "test-ffmerger" { 122 | inherits = ["test"] 123 | args = { 124 | PROJECT = "github.com/crazy-max/firefox-history-merger" 125 | BRANCH = "master" 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/crazy-max/xgo 2 | 3 | go 1.23.0 4 | -------------------------------------------------------------------------------- /rootfs/usr/local/bin/semver: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # https://raw.githubusercontent.com/fsaintjacques/semver-tool/master/src/semver 4 | 5 | set -o errexit -o nounset -o pipefail 6 | 7 | NAT='0|[1-9][0-9]*' 8 | ALPHANUM='[0-9]*[A-Za-z-][0-9A-Za-z-]*' 9 | IDENT="$NAT|$ALPHANUM" 10 | FIELD='[0-9A-Za-z-]+' 11 | 12 | SEMVER_REGEX="\ 13 | ^[vV]?\ 14 | ($NAT)\\.($NAT)\\.($NAT)\ 15 | (\\-(${IDENT})(\\.(${IDENT}))*)?\ 16 | (\\+${FIELD}(\\.${FIELD})*)?$" 17 | 18 | PROG=semver 19 | PROG_VERSION="3.2.0" 20 | 21 | USAGE="\ 22 | Usage: 23 | $PROG bump (major|minor|patch|release|prerel []|build ) 24 | $PROG compare 25 | $PROG diff 26 | $PROG get (major|minor|patch|release|prerel|build) 27 | $PROG --help 28 | $PROG --version 29 | 30 | Arguments: 31 | A version must match the following regular expression: 32 | \"${SEMVER_REGEX}\" 33 | In English: 34 | -- The version must match X.Y.Z[-PRERELEASE][+BUILD] 35 | where X, Y and Z are non-negative integers. 36 | -- PRERELEASE is a dot separated sequence of non-negative integers and/or 37 | identifiers composed of alphanumeric characters and hyphens (with 38 | at least one non-digit). Numeric identifiers must not have leading 39 | zeros. A hyphen (\"-\") introduces this optional part. 40 | -- BUILD is a dot separated sequence of identifiers composed of alphanumeric 41 | characters and hyphens. A plus (\"+\") introduces this optional part. 42 | 43 | See definition. 44 | 45 | A string as defined by PRERELEASE above. Or, it can be a PRERELEASE 46 | prototype string (or empty) followed by a dot. 47 | 48 | A string as defined by BUILD above. 49 | 50 | Options: 51 | -v, --version Print the version of this tool. 52 | -h, --help Print this help message. 53 | 54 | Commands: 55 | bump Bump by one of major, minor, patch; zeroing or removing 56 | subsequent parts. \"bump prerel\" sets the PRERELEASE part and 57 | removes any BUILD part. A trailing dot in the argument 58 | introduces an incrementing numeric field which is added or 59 | bumped. If no argument is provided, an incrementing numeric 60 | field is introduced/bumped. \"bump build\" sets the BUILD part. 61 | \"bump release\" removes any PRERELEASE or BUILD parts. 62 | The bumped version is written to stdout. 63 | 64 | compare Compare with , output to stdout the 65 | following values: -1 if is newer, 0 if equal, 1 if 66 | older. The BUILD part is not used in comparisons. 67 | 68 | diff Compare with , output to stdout the 69 | difference between two versions by the release type (MAJOR, MINOR, 70 | PATCH, PRERELEASE, BUILD). 71 | 72 | get Extract given part of , where part is one of major, minor, 73 | patch, prerel, build, or release. 74 | 75 | See also: 76 | https://semver.org -- Semantic Versioning 2.0.0" 77 | 78 | function error { 79 | echo -e "$1" >&2 80 | exit 1 81 | } 82 | 83 | function usage_help { 84 | error "$USAGE" 85 | } 86 | 87 | function usage_version { 88 | echo -e "${PROG}: $PROG_VERSION" 89 | exit 0 90 | } 91 | 92 | function validate_version { 93 | local version=$1 94 | if [[ "$version" =~ $SEMVER_REGEX ]]; then 95 | # if a second argument is passed, store the result in var named by $2 96 | if [ "$#" -eq "2" ]; then 97 | local major=${BASH_REMATCH[1]} 98 | local minor=${BASH_REMATCH[2]} 99 | local patch=${BASH_REMATCH[3]} 100 | local prere=${BASH_REMATCH[4]} 101 | local build=${BASH_REMATCH[8]} 102 | eval "$2=(\"$major\" \"$minor\" \"$patch\" \"$prere\" \"$build\")" 103 | else 104 | echo "$version" 105 | fi 106 | else 107 | error "version $version does not match the semver scheme 'X.Y.Z(-PRERELEASE)(+BUILD)'. See help for more information." 108 | fi 109 | } 110 | 111 | function is_nat { 112 | [[ "$1" =~ ^($NAT)$ ]] 113 | } 114 | 115 | function is_null { 116 | [ -z "$1" ] 117 | } 118 | 119 | function order_nat { 120 | [ "$1" -lt "$2" ] && { echo -1 ; return ; } 121 | [ "$1" -gt "$2" ] && { echo 1 ; return ; } 122 | echo 0 123 | } 124 | 125 | function order_string { 126 | [[ $1 < $2 ]] && { echo -1 ; return ; } 127 | [[ $1 > $2 ]] && { echo 1 ; return ; } 128 | echo 0 129 | } 130 | 131 | # given two (named) arrays containing NAT and/or ALPHANUM fields, compare them 132 | # one by one according to semver 2.0.0 spec. Return -1, 0, 1 if left array ($1) 133 | # is less-than, equal, or greater-than the right array ($2). The longer array 134 | # is considered greater-than the shorter if the shorter is a prefix of the longer. 135 | # 136 | function compare_fields { 137 | local l="$1[@]" 138 | local r="$2[@]" 139 | local leftfield=( "${!l}" ) 140 | local rightfield=( "${!r}" ) 141 | local left 142 | local right 143 | 144 | local i=$(( -1 )) 145 | local order=$(( 0 )) 146 | 147 | while true 148 | do 149 | [ $order -ne 0 ] && { echo $order ; return ; } 150 | 151 | : $(( i++ )) 152 | left="${leftfield[$i]}" 153 | right="${rightfield[$i]}" 154 | 155 | is_null "$left" && is_null "$right" && { echo 0 ; return ; } 156 | is_null "$left" && { echo -1 ; return ; } 157 | is_null "$right" && { echo 1 ; return ; } 158 | 159 | is_nat "$left" && is_nat "$right" && { order=$(order_nat "$left" "$right") ; continue ; } 160 | is_nat "$left" && { echo -1 ; return ; } 161 | is_nat "$right" && { echo 1 ; return ; } 162 | { order=$(order_string "$left" "$right") ; continue ; } 163 | done 164 | } 165 | 166 | # shellcheck disable=SC2206 # checked by "validate"; ok to expand prerel id's into array 167 | function compare_version { 168 | local order 169 | validate_version "$1" V 170 | validate_version "$2" V_ 171 | 172 | # compare major, minor, patch 173 | 174 | local left=( "${V[0]}" "${V[1]}" "${V[2]}" ) 175 | local right=( "${V_[0]}" "${V_[1]}" "${V_[2]}" ) 176 | 177 | order=$(compare_fields left right) 178 | [ "$order" -ne 0 ] && { echo "$order" ; return ; } 179 | 180 | # compare pre-release ids when M.m.p are equal 181 | 182 | local prerel="${V[3]:1}" 183 | local prerel_="${V_[3]:1}" 184 | local left=( ${prerel//./ } ) 185 | local right=( ${prerel_//./ } ) 186 | 187 | # if left and right have no pre-release part, then left equals right 188 | # if only one of left/right has pre-release part, that one is less than simple M.m.p 189 | 190 | [ -z "$prerel" ] && [ -z "$prerel_" ] && { echo 0 ; return ; } 191 | [ -z "$prerel" ] && { echo 1 ; return ; } 192 | [ -z "$prerel_" ] && { echo -1 ; return ; } 193 | 194 | # otherwise, compare the pre-release id's 195 | 196 | compare_fields left right 197 | } 198 | 199 | # render_prerel -- return a prerel field with a trailing numeric string 200 | # usage: render_prerel numeric [prefix-string] 201 | # 202 | function render_prerel { 203 | if [ -z "$2" ] 204 | then 205 | echo "${1}" 206 | else 207 | echo "${2}${1}" 208 | fi 209 | } 210 | 211 | # extract_prerel -- extract prefix and trailing numeric portions of a pre-release part 212 | # usage: extract_prerel prerel prerel_parts 213 | # The prefix and trailing numeric parts are returned in "prerel_parts". 214 | # 215 | PREFIX_ALPHANUM='[.0-9A-Za-z-]*[.A-Za-z-]' 216 | DIGITS='[0-9][0-9]*' 217 | EXTRACT_REGEX="^(${PREFIX_ALPHANUM})*(${DIGITS})$" 218 | 219 | function extract_prerel { 220 | local prefix; local numeric; 221 | 222 | if [[ "$1" =~ $EXTRACT_REGEX ]] 223 | then # found prefix and trailing numeric parts 224 | prefix="${BASH_REMATCH[1]}" 225 | numeric="${BASH_REMATCH[2]}" 226 | else # no numeric part 227 | prefix="${1}" 228 | numeric= 229 | fi 230 | 231 | eval "$2=(\"$prefix\" \"$numeric\")" 232 | } 233 | 234 | # bump_prerel -- return the new pre-release part based on previous pre-release part 235 | # and prototype for bump 236 | # usage: bump_prerel proto previous 237 | # 238 | function bump_prerel { 239 | local proto; local prev_prefix; local prev_numeric; 240 | 241 | # case one: no trailing dot in prototype => simply replace previous with proto 242 | if [[ ! ( "$1" =~ \.$ ) ]] 243 | then 244 | echo "$1" 245 | return 246 | fi 247 | 248 | proto="${1%.}" # discard trailing dot marker from prototype 249 | 250 | extract_prerel "${2#-}" prerel_parts # extract parts of previous pre-release 251 | # shellcheck disable=SC2154 252 | prev_prefix="${prerel_parts[0]}" 253 | prev_numeric="${prerel_parts[1]}" 254 | 255 | # case two: bump or append numeric to previous pre-release part 256 | if [ "$proto" == "+" ] # dummy "+" indicates no prototype argument provided 257 | then 258 | if [ -n "$prev_numeric" ] 259 | then 260 | : $(( ++prev_numeric )) # previous pre-release is already numbered, bump it 261 | render_prerel "$prev_numeric" "$prev_prefix" 262 | else 263 | render_prerel 1 "$prev_prefix" # append starting number 264 | fi 265 | return 266 | fi 267 | 268 | # case three: set, bump, or append using prototype prefix 269 | if [ "$prev_prefix" != "$proto" ] 270 | then 271 | render_prerel 1 "$proto" # proto not same pre-release; set and start at '1' 272 | elif [ -n "$prev_numeric" ] 273 | then 274 | : $(( ++prev_numeric )) # pre-release is numbered; bump it 275 | render_prerel "$prev_numeric" "$prev_prefix" 276 | else 277 | render_prerel 1 "$prev_prefix" # start pre-release at number '1' 278 | fi 279 | } 280 | 281 | function command_bump { 282 | local new; local version; local sub_version; local command; 283 | 284 | case $# in 285 | 2) case $1 in 286 | major|minor|patch|prerel|release) command=$1; sub_version="+."; version=$2;; 287 | *) usage_help;; 288 | esac ;; 289 | 3) case $1 in 290 | prerel|build) command=$1; sub_version=$2 version=$3 ;; 291 | *) usage_help;; 292 | esac ;; 293 | *) usage_help;; 294 | esac 295 | 296 | validate_version "$version" parts 297 | # shellcheck disable=SC2154 298 | local major="${parts[0]}" 299 | local minor="${parts[1]}" 300 | local patch="${parts[2]}" 301 | local prere="${parts[3]}" 302 | local build="${parts[4]}" 303 | 304 | case "$command" in 305 | major) new="$((major + 1)).0.0";; 306 | minor) new="${major}.$((minor + 1)).0";; 307 | patch) new="${major}.${minor}.$((patch + 1))";; 308 | release) new="${major}.${minor}.${patch}";; 309 | prerel) new=$(validate_version "${major}.${minor}.${patch}-$(bump_prerel "$sub_version" "$prere")");; 310 | build) new=$(validate_version "${major}.${minor}.${patch}${prere}+${sub_version}");; 311 | *) usage_help ;; 312 | esac 313 | 314 | echo "$new" 315 | exit 0 316 | } 317 | 318 | function command_compare { 319 | local v; local v_; 320 | 321 | case $# in 322 | 2) v=$(validate_version "$1"); v_=$(validate_version "$2") ;; 323 | *) usage_help ;; 324 | esac 325 | 326 | set +u # need unset array element to evaluate to null 327 | compare_version "$v" "$v_" 328 | exit 0 329 | } 330 | 331 | function command_diff { 332 | validate_version "$1" v1_parts 333 | # shellcheck disable=SC2154 334 | local v1_major="${v1_parts[0]}" 335 | local v1_minor="${v1_parts[1]}" 336 | local v1_patch="${v1_parts[2]}" 337 | local v1_prere="${v1_parts[3]}" 338 | local v1_build="${v1_parts[4]}" 339 | 340 | validate_version "$2" v2_parts 341 | # shellcheck disable=SC2154 342 | local v2_major="${v2_parts[0]}" 343 | local v2_minor="${v2_parts[1]}" 344 | local v2_patch="${v2_parts[2]}" 345 | local v2_prere="${v2_parts[3]}" 346 | local v2_build="${v2_parts[4]}" 347 | 348 | if [ "${v1_major}" != "${v2_major}" ]; then 349 | echo "major" 350 | elif [ "${v1_minor}" != "${v2_minor}" ]; then 351 | echo "minor" 352 | elif [ "${v1_patch}" != "${v2_patch}" ]; then 353 | echo "patch" 354 | elif [ "${v1_prere}" != "${v2_prere}" ]; then 355 | echo "prerelease" 356 | elif [ "${v1_build}" != "${v2_build}" ]; then 357 | echo "build" 358 | fi 359 | } 360 | 361 | # shellcheck disable=SC2034 362 | function command_get { 363 | local part version 364 | 365 | if [[ "$#" -ne "2" ]] || [[ -z "$1" ]] || [[ -z "$2" ]]; then 366 | usage_help 367 | exit 0 368 | fi 369 | 370 | part="$1" 371 | version="$2" 372 | 373 | validate_version "$version" parts 374 | local major="${parts[0]}" 375 | local minor="${parts[1]}" 376 | local patch="${parts[2]}" 377 | local prerel="${parts[3]:1}" 378 | local build="${parts[4]:1}" 379 | local release="${major}.${minor}.${patch}" 380 | 381 | case "$part" in 382 | major|minor|patch|release|prerel|build) echo "${!part}" ;; 383 | *) usage_help ;; 384 | esac 385 | 386 | exit 0 387 | } 388 | 389 | case $# in 390 | 0) echo "Unknown command: $*"; usage_help;; 391 | esac 392 | 393 | case $1 in 394 | --help|-h) echo -e "$USAGE"; exit 0;; 395 | --version|-v) usage_version ;; 396 | bump) shift; command_bump "$@";; 397 | get) shift; command_get "$@";; 398 | compare) shift; command_compare "$@";; 399 | diff) shift; command_diff "$@";; 400 | *) echo "Unknown arguments: $*"; usage_help;; 401 | esac 402 | -------------------------------------------------------------------------------- /rootfs/usr/local/bin/xgo-bootstrap-pure: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # Contains the Go tool-chain pure-Go bootstrapper, that as of Go 1.5, initiates 5 | # not only a few pre-built Go cross compilers, but rather bootstraps all of the 6 | # supported platforms from the origin Linux amd64 distribution. 7 | # 8 | # Usage: xgo-bootstrap-pure 9 | # 10 | # Environment variables for remote bootstrapping: 11 | # GO_DIST_URL - 64 bit Linux Go binary distribution package url 12 | # GO_DIST_SHA - 64 bit Linux Go distribution package checksum 13 | # 14 | # Environment variables for local bootstrapping: 15 | # GOROOT - Path to the already installed Go runtime 16 | set -e 17 | 18 | export GOROOT_BOOTSTRAP=$GOROOT 19 | 20 | xgobootstrap() { 21 | if command -v "$3" >/dev/null 2>/dev/null; then 22 | echo "Bootstrapping $1/$2..." 23 | (set -x ; GOOS=$1 GOARCH=$2 CGO_ENABLED=1 CC=$3 go install std) 24 | fi 25 | } 26 | 27 | # Fix last digit 28 | if [ "$(echo "$GO_VERSION" | tr -cd '.' | wc -c)" != "2" ]; then 29 | export GO_VERSION="${GO_VERSION}.0" 30 | fi 31 | echo "GO_VERSION=$GO_VERSION" 32 | 33 | # Pre-build all guest distributions based on the root distribution 34 | xgobootstrap linux 386 i686-linux-gnu-gcc 35 | xgobootstrap linux amd64 x86_64-linux-gnu-gcc 36 | xgobootstrap linux arm64 aarch64-linux-gnu-gcc 37 | 38 | if [ "$(semver compare "$GO_VERSION" "1.7.0")" -ge 0 ]; then 39 | xgobootstrap linux mips64 mips64-linux-gnuabi64-gcc 40 | xgobootstrap linux mips64le mips64el-linux-gnuabi64-gcc 41 | fi 42 | 43 | if [ "$(semver compare "$GO_VERSION" "1.8.0")" -ge 0 ]; then 44 | xgobootstrap linux mips mips-linux-gnu-gcc 45 | xgobootstrap linux mipsle mipsel-linux-gnu-gcc 46 | xgobootstrap linux ppc64le powerpc64le-linux-gnu-gcc 47 | xgobootstrap linux s390x s390x-linux-gnu-gcc 48 | fi 49 | 50 | if [ "$(semver compare "$GO_VERSION" "1.16.0")" -ge 0 ]; then 51 | xgobootstrap linux riscv64 riscv64-linux-gnu-gcc 52 | fi 53 | 54 | xgobootstrap windows amd64 x86_64-w64-mingw32-gcc 55 | xgobootstrap windows 386 i686-w64-mingw32-gcc 56 | 57 | # FIXME: gcc_libinit_windows.c:8:10: fatal error: 'windows.h' file not found 58 | #if [ "$(semver compare "$GO_VERSION" "1.17.0")" -ge 0 ]; then 59 | # echo "Bootstrapping windows/arm64..." 60 | # (set -x ; GOOS=windows GOARCH=arm64 CGO_ENABLED=1 CC=aarch64-w64-mingw32-gcc go install std) 61 | #fi 62 | 63 | xgobootstrap darwin amd64 o64-clang 64 | 65 | if [ "$(semver compare "$GO_VERSION" "1.15.0")" -lt 0 ]; then 66 | xgobootstrap darwin 386 o32-clang 67 | fi 68 | 69 | if [ "$(semver compare "$GO_VERSION" "1.16.0")" -ge 0 ]; then 70 | xgobootstrap darwin arm64 o64-clang 71 | fi 72 | -------------------------------------------------------------------------------- /rootfs/usr/local/bin/xgo-build: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # Contains the main cross compiler, that individually sets up each target build 5 | # platform, compiles all the C dependencies, then build the requested executable 6 | # itself. 7 | # 8 | # Usage: xgo-build 9 | # 10 | # Needed environment variables: 11 | # REPO_REMOTE - Optional VCS remote if not the primary repository is needed 12 | # REPO_BRANCH - Optional VCS branch to use, if not the master branch 13 | # DEPS - Optional list of C dependency packages to build 14 | # ARGS - Optional arguments to pass to C dependency configure scripts 15 | # PACK - Optional sub-package, if not the import path is being built 16 | # OUT - Optional output prefix to override the package name 17 | # FLAG_V - Optional verbosity flag to set on the Go builder 18 | # FLAG_X - Optional flag to print the build progress commands 19 | # FLAG_RACE - Optional race flag to set on the Go builder 20 | # FLAG_TAGS - Optional tag flag to set on the Go builder 21 | # FLAG_LDFLAGS - Optional ldflags flag to set on the Go builder 22 | # FLAG_BUILDMODE - Optional buildmode flag to set on the Go builder 23 | # FLAG_BUILDVCS - Optional buildvcs flag to set on the Go builder 24 | # FLAG_TRIMPATH - Optional trimpath flag to remove all file system paths 25 | # FLAG_HIDEWINDOW - Optional flag to hide the console window on Windows 26 | # TARGETS - Comma separated list of build targets to compile for 27 | # GO_VERSION - Bootstrapped version of Go to disable uncupported targets 28 | # EXT_GOPATH - GOPATH elements mounted from the host filesystem 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 | # Fix last digit 54 | if [ "$(echo "$GO_VERSION" | tr -cd '.' | wc -c)" != "2" ]; then 55 | export GO_VERSION="${GO_VERSION}.0" 56 | fi 57 | 58 | # Detect if we are using go modules 59 | if [[ "$GO111MODULE" == "on" || "$GO111MODULE" == "auto" ]]; then 60 | USEMODULES=true 61 | else 62 | USEMODULES=false 63 | fi 64 | 65 | # Either set a local build environemnt, or pull any remote imports 66 | if [ "$EXT_GOPATH" != "" ]; then 67 | # If local builds are requested, inject the sources 68 | echo "Building locally $1..." 69 | export GOPATH=$GOPATH:$EXT_GOPATH 70 | set -e 71 | 72 | # Find and change into the package folder 73 | cd "$(go list -e -f '{{.Dir}}' $1)" 74 | export GOPATH=$GOPATH:$(pwd)/Godeps/_workspace 75 | elif [[ "$USEMODULES" == true ]]; then 76 | # Go module builds should assume a local repository 77 | # at mapped to /source containing at least a go.mod file. 78 | if [[ ! -d /source ]]; then 79 | echo "Go modules are enabled but go.mod was not found in the source folder." 80 | exit 10 81 | fi 82 | 83 | # set git safe directory, ref: CVE-2022-24765 84 | git config --global --add safe.directory /source 85 | 86 | # Change into the repo/source folder 87 | cd /source 88 | echo "Building /source/go.mod..." 89 | else 90 | # Inject all possible Godep paths to short circuit go gets 91 | GOPATH_ROOT=$GOPATH/src 92 | IMPORT_PATH=$1 93 | while [ "$IMPORT_PATH" != "." ]; do 94 | export GOPATH=$GOPATH:$GOPATH_ROOT/$IMPORT_PATH/Godeps/_workspace 95 | IMPORT_PATH=$(dirname $IMPORT_PATH) 96 | done 97 | 98 | # set git safe directory, ref: CVE-2022-24765 99 | git config --global --add safe.directory "$GOPATH_ROOT" 100 | 101 | # Otherwise download the canonical import path (may fail, don't allow failures beyond) 102 | echo "Fetching main repository $1..." 103 | GHQ_ROOT=$GOPATH_ROOT ghq get "$1" 104 | set -e 105 | 106 | cd "$GOPATH_ROOT/$1" 107 | 108 | # Switch over the code-base to another checkout if requested 109 | if [ "$REPO_REMOTE" != "" ] || [ "$REPO_BRANCH" != "" ]; then 110 | # Detect the version control system type 111 | IMPORT_PATH=$1 112 | while [ "$IMPORT_PATH" != "." ] && [ "$REPO_TYPE" == "" ]; do 113 | if [ -d "$GOPATH_ROOT/$IMPORT_PATH/.git" ]; then 114 | REPO_TYPE="git" 115 | elif [ -d "$GOPATH_ROOT/$IMPORT_PATH/.hg" ]; then 116 | REPO_TYPE="hg" 117 | fi 118 | IMPORT_PATH=$(dirname $IMPORT_PATH) 119 | done 120 | 121 | if [ "$REPO_TYPE" == "" ]; then 122 | echo "Unknown version control system type, cannot switch remotes and branches." 123 | exit 1 124 | fi 125 | # If we have a valid VCS, execute the switch operations 126 | if [ "$REPO_REMOTE" != "" ]; then 127 | echo "Switching over to remote $REPO_REMOTE..." 128 | if [ "$REPO_TYPE" == "git" ]; then 129 | git remote set-url origin "$REPO_REMOTE" 130 | git fetch --all 131 | git reset --hard origin/HEAD 132 | git clean -dxf 133 | elif [ "$REPO_TYPE" == "hg" ]; then 134 | echo -e "[paths]\ndefault = $REPO_REMOTE\n" >> .hg/hgrc 135 | hg pull 136 | fi 137 | fi 138 | if [ "$REPO_BRANCH" != "" ]; then 139 | echo "Switching over to branch $REPO_BRANCH..." 140 | if [ "$REPO_TYPE" == "git" ]; then 141 | git reset --hard "origin/$REPO_BRANCH" 142 | git clean -dxf 143 | elif [ "$REPO_TYPE" == "hg" ]; then 144 | hg checkout "$REPO_BRANCH" 145 | fi 146 | fi 147 | fi 148 | fi 149 | 150 | # Download all the C dependencies 151 | mkdir /deps 152 | DEPS=($DEPS) && for dep in "${DEPS[@]}"; do 153 | if [ "${dep##*.}" == "tar" ]; then cat "/deps-cache/$(basename $dep)" | tar -C /deps -x; fi 154 | if [ "${dep##*.}" == "gz" ]; then cat "/deps-cache/$(basename $dep)" | tar -C /deps -xz; fi 155 | if [ "${dep##*.}" == "bz2" ]; then cat "/deps-cache/$(basename $dep)" | tar -C /deps -xj; fi 156 | done 157 | 158 | DEPS_ARGS=($ARGS) 159 | 160 | # Save the contents of the pre-build /usr/local folder for post cleanup 161 | USR_LOCAL_CONTENTS=$(ls /usr/local) 162 | 163 | # Configure some global build parameters 164 | NAME=$(basename $1/$PACK) 165 | 166 | # Go module-based builds error with 'cannot find main module' when $PACK is defined 167 | if [[ "$USEMODULES" = true ]]; then 168 | PACK_RELPATH="" 169 | NAME=$(sed -n 's/module\ \(.*\)/\1/p' /source/go.mod) 170 | else 171 | PACK_RELPATH="./$PACK" 172 | fi 173 | 174 | # Pack relative path 175 | PACK_RELPATH="./$PACK" 176 | 177 | if [ "$OUT" != "" ]; then 178 | NAME=$OUT 179 | fi 180 | 181 | if [ "$FLAG_V" == "true" ]; then V=-v; fi 182 | if [ "$FLAG_X" == "true" ]; then X=-x; fi 183 | if [ "$FLAG_RACE" == "true" ]; then R=-race; fi 184 | if [ "$FLAG_TAGS" != "" ]; then T=(--tags "$FLAG_TAGS"); fi 185 | if [ "$FLAG_LDFLAGS" != "" ]; then LD="$FLAG_LDFLAGS"; fi 186 | if [ "$FLAG_TRIMPATH" == "true" ]; then TP=-trimpath; fi 187 | if [ "$FLAG_HIDEWINDOW" == "true" ]; then H="-H=windowsgui"; fi 188 | 189 | if [ "$FLAG_BUILDMODE" != "" ] && [ "$FLAG_BUILDMODE" != "default" ]; then BM="--buildmode=$FLAG_BUILDMODE"; fi 190 | if [ "$(semver compare "$GO_VERSION" "1.18.0")" -ge 0 ] && [ "$FLAG_BUILDVCS" != "" ]; then VCS="-buildvcs=$FLAG_BUILDVCS"; fi 191 | if [ "$FLAG_MOD" != "" ]; then MOD="--mod=$FLAG_MOD"; fi 192 | 193 | # If no build targets were specified, inject a catch all wildcard 194 | if [ "$TARGETS" == "" ]; then 195 | TARGETS="./." 196 | fi 197 | 198 | # Build for each requested platform individually 199 | for TARGET in $TARGETS; do 200 | # Split the target into platform and architecture 201 | XGOOS=$(echo $TARGET | cut -d '/' -f 1) 202 | XGOARCH=$(echo $TARGET | cut -d '/' -f 2) 203 | 204 | # Check and build for Linux targets 205 | if ([ $XGOOS == "." ] || [ $XGOOS == "linux" ]) && ([ $XGOARCH == "." ] || [ $XGOARCH == "amd64" ]); then 206 | echo "Compiling for linux/amd64..." 207 | HOST=x86_64-linux PREFIX=/usr/local xgo-build-deps /deps ${DEPS_ARGS[@]} 208 | if [[ "$USEMODULES" == false ]]; then 209 | GOOS=linux GOARCH=amd64 CGO_ENABLED=1 go get $V $X $TP $VCS "${T[@]}" --ldflags="$V $LD" -d $PACK_RELPATH 210 | fi 211 | ext=$(extension linux) 212 | (set -x ; CC=x86_64-linux-gnu-gcc CXX=x86_64-linux-gnu-g++ GOOS=linux GOARCH=amd64 CGO_ENABLED=1 go build $V $X $TP $VCS $MOD "${T[@]}" --ldflags="$V $LD" $R $BM -o "/build/$NAME-linux-amd64$R$ext" $PACK_RELPATH) 213 | fi 214 | if ([ $XGOOS == "." ] || [ $XGOOS == "linux" ]) && ([ $XGOARCH == "." ] || [ $XGOARCH == "386" ]); then 215 | echo "Compiling for linux/386..." 216 | HOST=i686-linux PREFIX=/usr/local xgo-build-deps /deps ${DEPS_ARGS[@]} 217 | if [[ "$USEMODULES" == false ]]; then 218 | GOOS=linux GOARCH=386 CGO_ENABLED=1 go get $V $X $TP $VCS "${T[@]}" --ldflags="$V $LD" -d $PACK_RELPATH 219 | fi 220 | ext=$(extension linux) 221 | (set -x ; CC=i686-linux-gnu-gcc CXX=i686-linux-gnu-g++ GOOS=linux GOARCH=386 CGO_ENABLED=1 go build $V $X $TP $VCS $MOD "${T[@]}" --ldflags="$V $LD" $BM -o "/build/$NAME-linux-386$ext" $PACK_RELPATH) 222 | fi 223 | if ([ $XGOOS == "." ] || [ $XGOOS == "linux" ]) && ([ $XGOARCH == "." ] || [ $XGOARCH == "arm" ] || [ $XGOARCH == "arm-5" ]); then 224 | if [ "$(semver compare "$GO_VERSION" "1.5.0")" -ge 0 ]; then 225 | echo "Bootstrapping linux/arm-5..." 226 | (set -x ; CC=arm-linux-gnueabi-gcc GOOS=linux GOARCH=arm GOARM=5 CGO_ENABLED=1 CGO_CFLAGS="-march=armv5t" CGO_CXXFLAGS="-march=armv5t" go install std) 227 | fi 228 | echo "Compiling for linux/arm-5..." 229 | CC=arm-linux-gnueabi-gcc CXX=arm-linux-gnueabi-g++ HOST=arm-linux-gnueabi PREFIX=/usr/arm-linux-gnueabi CFLAGS="-march=armv5t" CXXFLAGS="-march=armv5t" xgo-build-deps /deps ${DEPS_ARGS[@]} 230 | export PKG_CONFIG_PATH=/usr/arm-linux-gnueabi/lib/pkgconfig 231 | 232 | if [[ "$USEMODULES" == false ]]; then 233 | CC=arm-linux-gnueabi-gcc CXX=arm-linux-gnueabi-g++ GOOS=linux GOARCH=arm GOARM=5 CGO_ENABLED=1 CGO_CFLAGS="-march=armv5t" CGO_CXXFLAGS="-march=armv5t" go get $V $X $TP $VCS "${T[@]}" --ldflags="$V $LD" -d $PACK_RELPATH 234 | fi 235 | ext=$(extension linux) 236 | (set -x ; CC=arm-linux-gnueabi-gcc CXX=arm-linux-gnueabi-g++ GOOS=linux GOARCH=arm GOARM=5 CGO_ENABLED=1 CGO_CFLAGS="-march=armv5t" CGO_CXXFLAGS="-march=armv5t" go build $V $X $TP $VCS $MOD "${T[@]}" --ldflags="$V $LD" $BM -o "/build/$NAME-linux-arm-5$ext" $PACK_RELPATH) 237 | if [ "$(semver compare "$GO_VERSION" "1.5.0")" -ge 0 ]; then 238 | echo "Cleaning up Go runtime for linux/arm-5..." 239 | rm -rf /usr/local/go/pkg/linux_arm 240 | fi 241 | fi 242 | if ([ $XGOOS == "." ] || [ $XGOOS == "linux" ]) && ([ $XGOARCH == "." ] || [ $XGOARCH == "arm-6" ]); then 243 | if [ "$(semver compare "$GO_VERSION" "1.5.0")" -lt 0 ]; then 244 | echo "Go version too low, skipping linux/arm-6..." 245 | else 246 | echo "Bootstrapping linux/arm-6..." 247 | (set -x ; CC=arm-linux-gnueabi-gcc GOOS=linux GOARCH=arm GOARM=6 CGO_ENABLED=1 CGO_CFLAGS="-march=armv6" CGO_CXXFLAGS="-march=armv6" go install std) 248 | 249 | echo "Compiling for linux/arm-6..." 250 | CC=arm-linux-gnueabi-gcc CXX=arm-linux-gnueabi-g++ HOST=arm-linux-gnueabi PREFIX=/usr/arm-linux-gnueabi CFLAGS="-march=armv6" CXXFLAGS="-march=armv6" xgo-build-deps /deps ${DEPS_ARGS[@]} 251 | export PKG_CONFIG_PATH=/usr/arm-linux-gnueabi/lib/pkgconfig 252 | 253 | if [[ "$USEMODULES" == false ]]; then 254 | CC=arm-linux-gnueabi-gcc CXX=arm-linux-gnueabi-g++ GOOS=linux GOARCH=arm GOARM=6 CGO_ENABLED=1 CGO_CFLAGS="-march=armv6" CGO_CXXFLAGS="-march=armv6" go get $V $X $TP $VCS "${T[@]}" --ldflags="$V $LD" -d $PACK_RELPATH 255 | fi 256 | ext=$(extension linux) 257 | (set -x ; CC=arm-linux-gnueabi-gcc CXX=arm-linux-gnueabi-g++ GOOS=linux GOARCH=arm GOARM=6 CGO_ENABLED=1 CGO_CFLAGS="-march=armv6" CGO_CXXFLAGS="-march=armv6" go build $V $X $TP $VCS $MOD "${T[@]}" --ldflags="$V $LD" $BM -o "/build/$NAME-linux-arm-6$ext" $PACK_RELPATH) 258 | 259 | echo "Cleaning up Go runtime for linux/arm-6..." 260 | rm -rf /usr/local/go/pkg/linux_arm 261 | fi 262 | fi 263 | if ([ $XGOOS == "." ] || [ $XGOOS == "linux" ]) && ([ $XGOARCH == "." ] || [ $XGOARCH == "arm-7" ]); then 264 | if [ "$(semver compare "$GO_VERSION" "1.5.0")" -lt 0 ]; then 265 | echo "Go version too low, skipping linux/arm-7..." 266 | else 267 | echo "Bootstrapping linux/arm-7..." 268 | (set -x ; CC=arm-linux-gnueabihf-gcc GOOS=linux GOARCH=arm GOARM=7 CGO_ENABLED=1 CGO_CFLAGS="-march=armv7-a" CGO_CXXFLAGS="-march=armv7-a" go install std) 269 | 270 | echo "Compiling for linux/arm-7..." 271 | CC=arm-linux-gnueabihf-gcc CXX=arm-linux-gnueabihf-g++ HOST=arm-linux-gnueabihf PREFIX=/usr/arm-linux-gnueabihf CFLAGS="-march=armv7-a -fPIC" CXXFLAGS="-march=armv7-a -fPIC" xgo-build-deps /deps ${DEPS_ARGS[@]} 272 | export PKG_CONFIG_PATH=/usr/arm-linux-gnueabihf/lib/pkgconfig 273 | 274 | if [[ "$USEMODULES" == false ]]; then 275 | CC=arm-linux-gnueabihf-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 $TP $VCS "${T[@]}" --ldflags="$V $LD" -d $PACK_RELPATH 276 | fi 277 | ext=$(extension linux) 278 | (set -x ; CC=arm-linux-gnueabihf-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 build $V $X $TP $VCS $MOD "${T[@]}" --ldflags="$V $LD" $BM -o "/build/$NAME-linux-arm-7$ext" $PACK_RELPATH) 279 | 280 | echo "Cleaning up Go runtime for linux/arm-7..." 281 | rm -rf /usr/local/go/pkg/linux_arm 282 | fi 283 | fi 284 | if ([ $XGOOS == "." ] || [ $XGOOS == "linux" ]) && ([ $XGOARCH == "." ] || [ $XGOARCH == "arm64" ]); then 285 | if [ "$(semver compare "$GO_VERSION" "1.5.0")" -lt 0 ]; then 286 | echo "Go version too low, skipping linux/arm64..." 287 | else 288 | echo "Compiling for linux/arm64..." 289 | CC=aarch64-linux-gnu-gcc CXX=aarch64-linux-gnu-g++ HOST=aarch64-linux-gnu PREFIX=/usr/aarch64-linux-gnu xgo-build-deps /deps ${DEPS_ARGS[@]} 290 | export PKG_CONFIG_PATH=/usr/aarch64-linux-gnu/lib/pkgconfig 291 | 292 | if [[ "$USEMODULES" == false ]]; then 293 | CC=aarch64-linux-gnu-gcc CXX=aarch64-linux-gnu-g++ GOOS=linux GOARCH=arm64 CGO_ENABLED=1 go get $V $X $TP $VCS "${T[@]}" --ldflags="$V $LD" -d $PACK_RELPATH 294 | fi 295 | ext=$(extension linux) 296 | (set -x ; CC=aarch64-linux-gnu-gcc CXX=aarch64-linux-gnu-g++ GOOS=linux GOARCH=arm64 CGO_ENABLED=1 go build $V $X $TP $VCS $MOD "${T[@]}" --ldflags="$V $LD" $BM -o "/build/$NAME-linux-arm64$ext" $PACK_RELPATH) 297 | fi 298 | fi 299 | if ([ $XGOOS == "." ] || [ $XGOOS == "linux" ]) && ([ $XGOARCH == "." ] || [ $XGOARCH == "mips64" ]); then 300 | if [ "$(semver compare "$GO_VERSION" "1.7.0")" -lt 0 ]; then 301 | echo "Go version too low, skipping linux/mips64..." 302 | else 303 | if ! command -v "mips64-linux-gnuabi64-gcc" >/dev/null 2>/dev/null; then 304 | echo "mips64-linux-gnuabi64-gcc not found, skipping linux/mips64..." 305 | else 306 | echo "Compiling for linux/mips64..." 307 | CC=mips64-linux-gnuabi64-gcc CXX=mips64-linux-gnuabi64-g++ HOST=mips64-linux-gnuabi64 PREFIX=/usr/mips64-linux-gnuabi64 xgo-build-deps /deps ${DEPS_ARGS[@]} 308 | export PKG_CONFIG_PATH=/usr/mips64-linux-gnuabi64/lib/pkgconfig 309 | 310 | if [[ "$USEMODULES" == false ]]; then 311 | CC=mips64-linux-gnuabi64-gcc CXX=mips64-linux-gnuabi64-g++ GOOS=linux GOARCH=mips64 CGO_ENABLED=1 go get $V $X $TP $VCS "${T[@]}" --ldflags="$V $LD" -d $PACK_RELPATH 312 | fi 313 | ext=$(extension linux) 314 | (set -x ; CC=mips64-linux-gnuabi64-gcc CXX=mips64-linux-gnuabi64-g++ GOOS=linux GOARCH=mips64 CGO_ENABLED=1 go build $V $X $TP $VCS $MOD "${T[@]}" --ldflags="$V $LD" $BM -o "/build/$NAME-linux-mips64$ext" $PACK_RELPATH) 315 | fi 316 | fi 317 | fi 318 | if ([ $XGOOS == "." ] || [ $XGOOS == "linux" ]) && ([ $XGOARCH == "." ] || [ $XGOARCH == "mips64le" ]); then 319 | if [ "$(semver compare "$GO_VERSION" "1.7.0")" -lt 0 ]; then 320 | echo "Go version too low, skipping linux/mips64le..." 321 | else 322 | if ! command -v "mips64el-linux-gnuabi64-gcc" >/dev/null 2>/dev/null; then 323 | echo "mips64el-linux-gnuabi64-gcc not found, skipping linux/mips64le..." 324 | else 325 | echo "Compiling for linux/mips64le..." 326 | CC=mips64el-linux-gnuabi64-gcc CXX=mips64el-linux-gnuabi64-g++ HOST=mips64el-linux-gnuabi64 PREFIX=/usr/mips64el-linux-gnuabi64 xgo-build-deps /deps ${DEPS_ARGS[@]} 327 | export PKG_CONFIG_PATH=/usr/mips64le-linux-gnuabi64/lib/pkgconfig 328 | 329 | if [[ "$USEMODULES" == false ]]; then 330 | CC=mips64el-linux-gnuabi64-gcc CXX=mips64el-linux-gnuabi64-g++ GOOS=linux GOARCH=mips64le CGO_ENABLED=1 go get $V $X $TP $VCS "${T[@]}" --ldflags="$V $LD" -d $PACK_RELPATH 331 | fi 332 | ext=$(extension linux) 333 | (set -x ; CC=mips64el-linux-gnuabi64-gcc CXX=mips64el-linux-gnuabi64-g++ GOOS=linux GOARCH=mips64le CGO_ENABLED=1 go build $V $X $TP $VCS $MOD "${T[@]}" --ldflags="$V $LD" $BM -o "/build/$NAME-linux-mips64le$ext" $PACK_RELPATH) 334 | fi 335 | fi 336 | fi 337 | if ([ $XGOOS == "." ] || [ $XGOOS == "linux" ]) && ([ $XGOARCH == "." ] || [ $XGOARCH == "mips" ]); then 338 | if [ "$(semver compare "$GO_VERSION" "1.8.0")" -lt 0 ]; then 339 | echo "Go version too low, skipping linux/mips..." 340 | else 341 | if ! command -v "mips-linux-gnu-gcc" >/dev/null 2>/dev/null; then 342 | echo "mips-linux-gnu-gcc not found, skipping linux/mips..." 343 | else 344 | echo "Compiling for linux/mips..." 345 | CC=mips-linux-gnu-gcc CXX=mips-linux-gnu-g++ HOST=mips-linux-gnu PREFIX=/usr/mips-linux-gnu xgo-build-deps /deps ${DEPS_ARGS[@]} 346 | export PKG_CONFIG_PATH=/usr/mips-linux-gnu/lib/pkgconfig 347 | 348 | if [[ "$USEMODULES" == false ]]; then 349 | CC=mips-linux-gnu-gcc CXX=mips-linux-gnu-g++ GOOS=linux GOARCH=mips CGO_ENABLED=1 go get $V $X $TP $VCS "${T[@]}" --ldflags="$V $LD" -d $PACK_RELPATH 350 | fi 351 | ext=$(extension linux) 352 | (set -x ; CC=mips-linux-gnu-gcc CXX=mips-linux-gnu-g++ GOOS=linux GOARCH=mips CGO_ENABLED=1 go build $V $X $TP $VCS $MOD "${T[@]}" --ldflags="$V $LD" $BM -o "/build/$NAME-linux-mips$ext" $PACK_RELPATH) 353 | fi 354 | fi 355 | fi 356 | if ([ $XGOOS == "." ] || [ $XGOOS == "linux" ]) && ([ $XGOARCH == "." ] || [ $XGOARCH == "mipsle" ]); then 357 | if [ "$(semver compare "$GO_VERSION" "1.8.0")" -lt 0 ]; then 358 | echo "Go version too low, skipping linux/mipsle..." 359 | else 360 | if ! command -v "mipsel-linux-gnu-gcc" >/dev/null 2>/dev/null; then 361 | echo "mipsel-linux-gnu-gcc not found, skipping linux/mipsle..." 362 | else 363 | echo "Compiling for linux/mipsle..." 364 | CC=mipsel-linux-gnu-gcc CXX=mipsel-linux-gnu-g++ HOST=mipsel-linux-gnu PREFIX=/usr/mipsel-linux-gnu xgo-build-deps /deps ${DEPS_ARGS[@]} 365 | export PKG_CONFIG_PATH=/usr/mipsle-linux-gnu/lib/pkgconfig 366 | 367 | if [[ "$USEMODULES" == false ]]; then 368 | CC=mipsel-linux-gnu-gcc CXX=mipsel-linux-gnu-g++ GOOS=linux GOARCH=mipsle CGO_ENABLED=1 go get $V $X $TP $VCS "${T[@]}" --ldflags="$V $LD" -d $PACK_RELPATH 369 | fi 370 | ext=$(extension linux) 371 | (set -x ; CC=mipsel-linux-gnu-gcc CXX=mipsel-linux-gnu-g++ GOOS=linux GOARCH=mipsle CGO_ENABLED=1 go build $V $X $TP $VCS $MOD "${T[@]}" --ldflags="$V $LD" $BM -o "/build/$NAME-linux-mipsle$ext" $PACK_RELPATH) 372 | fi 373 | fi 374 | fi 375 | if ([ $XGOOS == "." ] || [ $XGOOS == "linux" ]) && ([ $XGOARCH == "." ] || [ $XGOARCH == "ppc64le" ]); then 376 | if [ "$(semver compare "$GO_VERSION" "1.8.0")" -lt 0 ]; then 377 | echo "Go version too low, skipping linux/ppc64le..." 378 | else 379 | echo "Compiling for linux/ppc64le..." 380 | CC=powerpc64le-linux-gnu-gcc CXX=powerpc64le-linux-gnu-g++ HOST=powerpc64le-linux-gnu PREFIX=/usr/powerpc64le-linux-gnu xgo-build-deps /deps ${DEPS_ARGS[@]} 381 | export PKG_CONFIG_PATH=/usr/powerpc64le-linux-gnu/lib/pkgconfig 382 | 383 | if [[ "$USEMODULES" == false ]]; then 384 | CC=powerpc64le-linux-gnu-gcc CXX=powerpc64le-linux-gnu-g++ GOOS=linux GOARCH=ppc64le CGO_ENABLED=1 go get $V $X $TP $VCS "${T[@]}" --ldflags="$V $LD" -d $PACK_RELPATH 385 | fi 386 | ext=$(extension linux) 387 | (set -x ; CC=powerpc64le-linux-gnu-gcc CXX=powerpc64le-linux-gnu-g++ GOOS=linux GOARCH=ppc64le CGO_ENABLED=1 go build $V $X $TP $VCS $MOD "${T[@]}" --ldflags="$V $LD" $BM -o "/build/$NAME-linux-ppc64le$ext" $PACK_RELPATH) 388 | fi 389 | fi 390 | if ([ $XGOOS == "." ] || [ $XGOOS == "linux" ]) && ([ $XGOARCH == "." ] || [ $XGOARCH == "riscv64" ]); then 391 | if [ "$(semver compare "$GO_VERSION" "1.16.0")" -lt 0 ]; then 392 | echo "Go version too low, skipping linux/riscv64..." 393 | else 394 | echo "Compiling for linux/riscv64..." 395 | CC=riscv64-linux-gnu-gcc CXX=riscv64-linux-gnu-g++ HOST=riscv64-linux-gnu PREFIX=/usr/riscv64-linux-gnu xgo-build-deps /deps ${DEPS_ARGS[@]} 396 | export PKG_CONFIG_PATH=/usr/riscv64-linux-gnu/lib/pkgconfig 397 | 398 | if [[ "$USEMODULES" == false ]]; then 399 | CC=riscv64-linux-gnu-gcc CXX=riscv64-linux-gnu-g++ GOOS=linux GOARCH=riscv64 CGO_ENABLED=1 go get $V $X $TP $VCS "${T[@]}" --ldflags="$V $LD" -d $PACK_RELPATH 400 | fi 401 | ext=$(extension linux) 402 | (set -x ; CC=riscv64-linux-gnu-gcc CXX=riscv64-linux-gnu-g++ GOOS=linux GOARCH=riscv64 CGO_ENABLED=1 go build $V $X $TP $VCS $MOD "${T[@]}" --ldflags="$V $LD" $BM -o "/build/$NAME-linux-riscv64$ext" $PACK_RELPATH) 403 | fi 404 | fi 405 | if ([ $XGOOS == "." ] || [ $XGOOS == "linux" ]) && ([ $XGOARCH == "." ] || [ $XGOARCH == "s390x" ]); then 406 | if [ "$(semver compare "$GO_VERSION" "1.8.0")" -lt 0 ]; then 407 | echo "Go version too low, skipping linux/s390x..." 408 | else 409 | echo "Compiling for linux/s390x..." 410 | CC=s390x-linux-gnu-gcc CXX=s390x-linux-gnu-g++ HOST=s390x-linux-gnu PREFIX=/usr/s390x-linux-gnu xgo-build-deps /deps ${DEPS_ARGS[@]} 411 | export PKG_CONFIG_PATH=/usr/s390x-linux-gnu/lib/pkgconfig 412 | 413 | if [[ "$USEMODULES" == false ]]; then 414 | CC=s390x-linux-gnu-gcc CXX=s390x-linux-gnu-g++ GOOS=linux GOARCH=s390x CGO_ENABLED=1 go get $V $X $TP $VCS "${T[@]}" --ldflags="$V $LD" -d $PACK_RELPATH 415 | fi 416 | ext=$(extension linux) 417 | (set -x ; CC=s390x-linux-gnu-gcc CXX=s390x-linux-gnu-g++ GOOS=linux GOARCH=s390x CGO_ENABLED=1 go build $V $X $TP $VCS $MOD "${T[@]}" --ldflags="$V $LD" $BM -o "/build/$NAME-linux-s390x$ext" $PACK_RELPATH) 418 | fi 419 | fi 420 | # Check and build for Windows targets 421 | if [ $XGOOS == "." ] || [[ $XGOOS == windows* ]]; then 422 | # Split the platform version and configure the Windows NT version 423 | PLATFORM=$(echo $XGOOS | cut -d '-' -f 2) 424 | PLATFORM_SUFFIX="-$PLATFORM" 425 | if [ "$PLATFORM" == "" ] || [ "$PLATFORM" == "." ] || [ "$PLATFORM" == "windows" ]; then 426 | PLATFORM=$WINDOWS_DEFAULT_TARGET 427 | PLATFORM_SUFFIX="" 428 | fi 429 | 430 | MAJOR=$(echo $PLATFORM | cut -d '.' -f 1) 431 | if [ "${PLATFORM/.}" != "$PLATFORM" ] ; then 432 | MINOR=$(echo $PLATFORM | cut -d '.' -f 2) 433 | fi 434 | CGO_NTDEF="-D_WIN32_WINNT=0x$(printf "%02d" $MAJOR)$(printf "%02d" $MINOR)" 435 | 436 | # Build the requested windows binaries 437 | if [ $XGOARCH == "." ] || [ $XGOARCH == "amd64" ]; then 438 | echo "Compiling for windows$PLATFORM_SUFFIX/amd64..." 439 | CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ HOST=x86_64-w64-mingw32 PREFIX=/usr/x86_64-w64-mingw32 xgo-build-deps /deps ${DEPS_ARGS[@]} 440 | export PKG_CONFIG_PATH=/usr/x86_64-w64-mingw32/lib/pkgconfig 441 | 442 | if [[ "$USEMODULES" == false ]]; then 443 | CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ GOOS=windows GOARCH=amd64 CGO_ENABLED=1 CGO_CFLAGS="$CGO_NTDEF" CGO_CXXFLAGS="$CGO_NTDEF" go get $V $X $TP $VCS "${T[@]}" --ldflags="$V $LD" -d $PACK_RELPATH 444 | fi 445 | ext=$(extension windows) 446 | (set -x ; CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ GOOS=windows GOARCH=amd64 CGO_ENABLED=1 CGO_CFLAGS="$CGO_NTDEF" CGO_CXXFLAGS="$CGO_NTDEF" go build $V $X $TP $VCS $MOD "${T[@]}" --ldflags="$V $H $LD" $R $BM -o "/build/$NAME-windows-amd64$R$ext" $PACK_RELPATH) 447 | fi 448 | if [ $XGOARCH == "." ] || [ $XGOARCH == "386" ]; then 449 | echo "Compiling for windows$PLATFORM_SUFFIX/386..." 450 | CC=i686-w64-mingw32-gcc CXX=i686-w64-mingw32-g++ HOST=i686-w64-mingw32 PREFIX=/usr/i686-w64-mingw32 xgo-build-deps /deps ${DEPS_ARGS[@]} 451 | export PKG_CONFIG_PATH=/usr/i686-w64-mingw32/lib/pkgconfig 452 | 453 | if [[ "$USEMODULES" == false ]]; then 454 | CC=i686-w64-mingw32-gcc CXX=i686-w64-mingw32-g++ GOOS=windows GOARCH=386 CGO_ENABLED=1 CGO_CFLAGS="$CGO_NTDEF" CGO_CXXFLAGS="$CGO_NTDEF" go get $V $X $TP $VCS "${T[@]}" --ldflags="$V $LD" -d $PACK_RELPATH 455 | fi 456 | ext=$(extension windows) 457 | (set -x ; CC=i686-w64-mingw32-gcc CXX=i686-w64-mingw32-g++ GOOS=windows GOARCH=386 CGO_ENABLED=1 CGO_CFLAGS="$CGO_NTDEF" CGO_CXXFLAGS="$CGO_NTDEF" go build $V $X $TP $VCS $MOD "${T[@]}" --ldflags="$V $H $LD" $BM -o "/build/$NAME-windows-386$ext" $PACK_RELPATH) 458 | fi 459 | # FIXME: gcc_libinit_windows.c:8:10: fatal error: 'windows.h' file not found 460 | # if [ $XGOARCH == "." ] || [ $XGOARCH == "arm64" ]; then 461 | # if [ "$(semver compare "$GO_VERSION" "1.17.0")" -lt 0 ]; then 462 | # echo "Go version too low, skipping windows$PLATFORM_SUFFIX/arm64..." 463 | # else 464 | # echo "Compiling for windows$PLATFORM_SUFFIX/arm64..." 465 | # CC=aarch64-w64-mingw32-gcc CXX=aarch64-w64-mingw32-g++ HOST=aarch64-w64-mingw32 PREFIX=/usr/aarch64-w64-mingw32 xgo-build-deps /deps ${DEPS_ARGS[@]} 466 | # export PKG_CONFIG_PATH=/usr/aarch64-w64-mingw32/lib/pkgconfig 467 | # 468 | # if [[ "$USEMODULES" == false ]]; then 469 | # CC=aarch64-w64-mingw32-gcc CXX=aarch64-w64-mingw32-g++ GOOS=windows GOARCH=386 CGO_ENABLED=1 CGO_CFLAGS="$CGO_NTDEF" CGO_CXXFLAGS="$CGO_NTDEF" go get $V $X $TP $VCS "${T[@]}" --ldflags="$V $LD" -d $PACK_RELPATH 470 | # fi 471 | # ext=$(extension windows) 472 | # (set -x ; CC=aarch64-w64-mingw32-gcc CXX=aarch64-w64-mingw32-gcc GOOS=windows GOARCH=386 CGO_ENABLED=1 CGO_CFLAGS="$CGO_NTDEF" CGO_CXXFLAGS="$CGO_NTDEF" go build $V $X $TP $VCS $MOD "${T[@]}" --ldflags="$V $LD" $BM -o "/build/$NAME-windows-386$ext" $PACK_RELPATH) 473 | # fi 474 | # fi 475 | fi 476 | # Check and build for OSX targets 477 | if [ $XGOOS == "." ] || [[ $XGOOS == darwin* ]]; then 478 | # Split the platform version and configure the deployment target 479 | PLATFORM=$(echo $XGOOS | cut -d '-' -f 2) 480 | PLATFORM_SUFFIX="-$PLATFORM" 481 | if [ "$PLATFORM" == "" ] || [ "$PLATFORM" == "." ] || [ "$PLATFORM" == "darwin" ]; then 482 | PLATFORM=$DARWIN_DEFAULT_TARGET 483 | PLATFORM_SUFFIX="" 484 | fi 485 | export MACOSX_DEPLOYMENT_TARGET=$PLATFORM 486 | 487 | # Strip symbol table below Go 1.6 to prevent DWARF issues 488 | LDSTRIP="" 489 | if [ "$(semver compare "$GO_VERSION" "1.6.0")" -lt 0 ]; then 490 | LDSTRIP="-s" 491 | fi 492 | # Build the requested darwin binaries 493 | if [ $XGOARCH == "." ] || [ $XGOARCH == "amd64" ]; then 494 | echo "Compiling for darwin$PLATFORM_SUFFIX/amd64..." 495 | CC=o64-clang CXX=o64-clang++ HOST=x86_64-apple-darwin15 PREFIX=/usr/local xgo-build-deps /deps ${DEPS_ARGS[@]} 496 | if [[ "$USEMODULES" == false ]]; then 497 | CC=o64-clang CXX=o64-clang++ GOOS=darwin GOARCH=amd64 CGO_ENABLED=1 go get $V $X $TP $VCS "${T[@]}" --ldflags="$LDSTRIP $V $LD" -d $PACK_RELPATH 498 | fi 499 | ext=$(extension darwin) 500 | (set -x ; CC=o64-clang CXX=o64-clang++ GOOS=darwin GOARCH=amd64 CGO_ENABLED=1 go build $V $X $TP $VCS $MOD "${T[@]}" --ldflags="$LDSTRIP $V $LD" $R $BM -o "/build/$NAME-darwin-amd64$R$ext" $PACK_RELPATH) 501 | fi 502 | if [ $XGOARCH == "." ] || [ $XGOARCH == "arm64" ]; then 503 | if [ "$(semver compare "$GO_VERSION" "1.16.0")" -lt 0 ]; then 504 | echo "Go version too low, skipping darwin/arm64..." 505 | else 506 | echo "Compiling for darwin$PLATFORM_SUFFIX/arm64..." 507 | CC=o64-clang CXX=o64-clang++ HOST=arm64-apple-darwin15 PREFIX=/usr/local xgo-build-deps /deps ${DEPS_ARGS[@]} 508 | if [[ "$USEMODULES" == false ]]; then 509 | CC=o64-clang CXX=o64-clang++ GOOS=darwin GOARCH=arm64 CGO_ENABLED=1 go get $V $X $TP $VCS "${T[@]}" --ldflags="$LDSTRIP $V $LD" -d $PACK_RELPATH 510 | fi 511 | ext=$(extension darwin) 512 | (set -x ; CC=o64-clang CXX=o64-clang++ GOOS=darwin GOARCH=arm64 CGO_ENABLED=1 go build $V $X $TP $VCS $TP $MOD "${T[@]}" --ldflags="$LDSTRIP $V $LD" $R $BM -o "/build/$NAME-darwin-arm64$R$ext" $PACK_RELPATH) 513 | fi 514 | fi 515 | if [ $XGOARCH == "." ] || [ $XGOARCH == "386" ]; then 516 | if [ "$(semver compare "$GO_VERSION" "1.15.0")" -lt 0 ]; then 517 | echo "Compiling for darwin$PLATFORM_SUFFIX/386..." 518 | CC=o32-clang CXX=o32-clang++ HOST=i386-apple-darwin15 PREFIX=/usr/local xgo-build-deps /deps ${DEPS_ARGS[@]} 519 | if [[ "$USEMODULES" == false ]]; then 520 | CC=o32-clang CXX=o32-clang++ GOOS=darwin GOARCH=386 CGO_ENABLED=1 go get $V $X $TP $VCS "${T[@]}" --ldflags="$LDSTRIP $V $LD" -d $PACK_RELPATH 521 | fi 522 | ext=$(extension darwin) 523 | (set -x ; CC=o32-clang CXX=o32-clang++ GOOS=darwin GOARCH=386 CGO_ENABLED=1 go build $V $X $TP $VCS $MOD "${T[@]}" --ldflags="$LDSTRIP $V $LD" $BM -o "/build/$NAME-darwin-386$ext" $PACK_RELPATH) 524 | else 525 | echo "Go version too high, skipping darwin$PLATFORM_SUFFIX/386..." 526 | fi 527 | fi 528 | # Remove any automatically injected deployment target vars 529 | unset MACOSX_DEPLOYMENT_TARGET 530 | 531 | fi 532 | done 533 | 534 | # Clean up any leftovers for subsequent build invocations 535 | echo "Cleaning up build environment..." 536 | rm -rf /deps 537 | 538 | for dir in $(ls /usr/local); do 539 | keep=0 540 | 541 | # Check against original folder contents 542 | for old in $USR_LOCAL_CONTENTS; do 543 | if [ "$old" == "$dir" ]; then 544 | keep=1 545 | fi 546 | done 547 | # Delete anything freshly generated 548 | if [ "$keep" == "0" ]; then 549 | rm -rf "/usr/local/$dir" 550 | fi 551 | done 552 | -------------------------------------------------------------------------------- /rootfs/usr/local/bin/xgo-build-deps: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # Contains the a dependency builder to iterate over all installed dependencies 5 | # and cross compile them to the requested target platform. 6 | # 7 | # Usage: xgo-build-deps 8 | # 9 | # Needed environment variables: 10 | # CC - C cross compiler to use for the build 11 | # HOST - Target platform to build (used to find the needed tool-chains) 12 | # PREFIX - File-system path where to install the built binaries 13 | set -e 14 | 15 | # Remove any previous build leftovers, and copy a fresh working set (clean doesn't work for cross compiling) 16 | rm -rf /deps-build && cp -r $1 /deps-build 17 | 18 | # Build all the dependencies (no order for now) 19 | for dep in $(ls /deps-build); do 20 | echo "Configuring dependency $dep for $HOST..." 21 | (cd /deps-build/$dep && ./configure --disable-shared --host=$HOST --prefix=$PREFIX --silent ${@:2}) 22 | 23 | echo "Building dependency $dep for $HOST..." 24 | (cd /deps-build/$dep && make --silent -j install) 25 | done 26 | 27 | # Remove any build artifacts 28 | rm -rf /deps-build 29 | -------------------------------------------------------------------------------- /tests/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | ARG BASE_IMAGE=ghcr.io/crazy-max/xgo:1.17 4 | ARG PROJECT="./c" 5 | ARG BRANCH 6 | 7 | FROM ${BASE_IMAGE} AS base 8 | WORKDIR /src 9 | ARG PROJECT 10 | ARG BRANCH 11 | RUN --mount=type=bind,source=.,target=/src,rw \ 12 | --mount=type=cache,target=/go/pkg/mod \ 13 | ROOTPATH=$(case $PROJECT in \ 14 | .*) echo "." ;; \ 15 | *) echo "$PROJECT" ;; esac) \ 16 | && if [ "$ROOTPATH" = "." ]; then cd $PROJECT; fi \ 17 | && xgo -targets="*/*" -buildvcs="true" -branch="$BRANCH" -out="test" $ROOTPATH \ 18 | && ls -al /build 19 | -------------------------------------------------------------------------------- /tests/c/go.mod: -------------------------------------------------------------------------------- 1 | module tests/c 2 | 3 | go 1.21 4 | -------------------------------------------------------------------------------- /tests/c/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // #include 4 | // 5 | // void sayHi() { 6 | // printf("Hello, embedded C!\n"); 7 | // } 8 | import "C" 9 | 10 | func main() { 11 | C.sayHi() 12 | } 13 | -------------------------------------------------------------------------------- /tests/cpp/go.mod: -------------------------------------------------------------------------------- 1 | module tests/cpp 2 | 3 | go 1.21 4 | -------------------------------------------------------------------------------- /tests/cpp/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // #include "snippet.h" 4 | import "C" 5 | 6 | func main() { 7 | C.sayHi() 8 | } 9 | -------------------------------------------------------------------------------- /tests/cpp/snippet.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "snippet.h" 3 | 4 | void sayHi() { 5 | std::cout << "Hello, embedded C++!" << std::endl; 6 | } 7 | -------------------------------------------------------------------------------- /tests/cpp/snippet.h: -------------------------------------------------------------------------------- 1 | #ifdef __cplusplus 2 | extern "C" { 3 | #endif 4 | 5 | void sayHi(); 6 | 7 | #ifdef __cplusplus 8 | } 9 | #endif 10 | -------------------------------------------------------------------------------- /tests/gorm/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/crazy-max/xgo/test/gorm 2 | 3 | go 1.16 4 | 5 | require ( 6 | gorm.io/driver/sqlite v1.1.4 7 | gorm.io/gorm v1.21.13 8 | ) 9 | -------------------------------------------------------------------------------- /tests/gorm/go.sum: -------------------------------------------------------------------------------- 1 | github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= 2 | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= 3 | github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 4 | github.com/jinzhu/now v1.1.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI= 5 | github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 6 | github.com/mattn/go-sqlite3 v1.14.5 h1:1IdxlwTNazvbKJQSxoJ5/9ECbEeaTTyeU7sEAZ5KKTQ= 7 | github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI= 8 | gorm.io/driver/sqlite v1.1.4 h1:PDzwYE+sI6De2+mxAneV9Xs11+ZyKV6oxD3wDGkaNvM= 9 | gorm.io/driver/sqlite v1.1.4/go.mod h1:mJCeTFr7+crvS+TRnWc5Z3UvwxUN1BGBLMrf5LA9DYw= 10 | gorm.io/gorm v1.20.7/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= 11 | gorm.io/gorm v1.21.13 h1:JU5A4yVemRjdMndJ0oZU7VX+Nr2ICE3C60U5bgR6mHE= 12 | gorm.io/gorm v1.21.13/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= 13 | -------------------------------------------------------------------------------- /tests/gorm/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "gorm.io/driver/sqlite" 5 | "gorm.io/gorm" 6 | ) 7 | 8 | type Product struct { 9 | gorm.Model 10 | Code string 11 | Price uint 12 | } 13 | 14 | func main() { 15 | db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{}) 16 | if err != nil { 17 | panic("failed to connect database") 18 | } 19 | 20 | // Migrate the schema 21 | db.AutoMigrate(&Product{}) 22 | 23 | // Create 24 | db.Create(&Product{Code: "D42", Price: 100}) 25 | 26 | // Read 27 | var product Product 28 | db.First(&product, 1) // find product with integer primary key 29 | db.First(&product, "code = ?", "D42") // find product with code D42 30 | 31 | // Update - update product's price to 200 32 | db.Model(&product).Update("Price", 200) 33 | // Update - update multiple fields 34 | db.Model(&product).Updates(Product{Price: 200, Code: "F42"}) // non-zero fields 35 | db.Model(&product).Updates(map[string]interface{}{"Price": 200, "Code": "F42"}) 36 | 37 | // Delete - delete product 38 | db.Delete(&product, 1) 39 | } 40 | -------------------------------------------------------------------------------- /xgo.go: -------------------------------------------------------------------------------- 1 | // Wrapper around the GCO cross compiler docker container. 2 | package main 3 | 4 | import ( 5 | "flag" 6 | "fmt" 7 | "go/build" 8 | "io" 9 | "log" 10 | "net/http" 11 | "os" 12 | "os/exec" 13 | "path/filepath" 14 | "strconv" 15 | "strings" 16 | ) 17 | 18 | var version = "dev" 19 | var depsCache = filepath.Join(os.TempDir(), "xgo-cache") 20 | 21 | // Cross compilation docker containers 22 | var dockerDist = "ghcr.io/crazy-max/xgo" 23 | 24 | // Command line arguments to fine tune the compilation 25 | var ( 26 | goVersion = flag.String("go", "latest", "Go release to use for cross compilation") 27 | goProxy = flag.String("goproxy", "", "Set a Global Proxy for Go Modules") 28 | srcPackage = flag.String("pkg", "", "Sub-package to build if not root import") 29 | srcRemote = flag.String("remote", "", "Version control remote repository to build") 30 | srcBranch = flag.String("branch", "", "Version control branch to build") 31 | outPrefix = flag.String("out", "", "Prefix to use for output naming (empty = package name)") 32 | outFolder = flag.String("dest", "", "Destination folder to put binaries in (empty = current)") 33 | crossDeps = flag.String("deps", "", "CGO dependencies (configure/make based archives)") 34 | crossArgs = flag.String("depsargs", "", "CGO dependency configure arguments") 35 | targets = flag.String("targets", "*/*", "Comma separated targets to build for") 36 | dockerRepo = flag.String("docker-repo", "", "Use custom docker repo instead of official distribution") 37 | dockerImage = flag.String("docker-image", "", "Use custom docker image instead of official distribution") 38 | ) 39 | 40 | // ConfigFlags is a simple set of flags to define the environment and dependencies. 41 | type ConfigFlags struct { 42 | Repository string // Root import path to build 43 | Package string // Sub-package to build if not root import 44 | Prefix string // Prefix to use for output naming 45 | Remote string // Version control remote repository to build 46 | Branch string // Version control branch to build 47 | Dependencies string // CGO dependencies (configure/make based archives) 48 | Arguments string // CGO dependency configure arguments 49 | Targets []string // Targets to build for 50 | } 51 | 52 | // Command line arguments to pass to go build 53 | var ( 54 | buildVerbose = flag.Bool("v", false, "Print the names of packages as they are compiled") 55 | buildSteps = flag.Bool("x", false, "Print the command as executing the builds") 56 | buildRace = flag.Bool("race", false, "Enable data race detection (supported only on amd64)") 57 | buildTags = flag.String("tags", "", "List of build tags to consider satisfied during the build") 58 | buildLdFlags = flag.String("ldflags", "", "Arguments to pass on each go tool link invocation") 59 | buildMode = flag.String("buildmode", "default", "Indicates which kind of object file to build") 60 | buildVCS = flag.String("buildvcs", "", "Whether to stamp binaries with version control information") 61 | buildTrimPath = flag.Bool("trimpath", false, "Remove all file system paths from the resulting executable") 62 | buildHideWindow = flag.Bool("hidewindow", false, "Optional flag to hide the console window on Windows") 63 | ) 64 | 65 | // BuildFlags is a simple collection of flags to fine tune a build. 66 | type BuildFlags struct { 67 | Verbose bool // Print the names of packages as they are compiled 68 | Steps bool // Print the command as executing the builds 69 | Race bool // Enable data race detection (supported only on amd64) 70 | Tags string // List of build tags to consider satisfied during the build 71 | LdFlags string // Arguments to pass on each go tool link invocation 72 | Mode string // Indicates which kind of object file to build 73 | VCS string // Whether to stamp binaries with version control information 74 | TrimPath bool // Remove all file system paths from the resulting executable 75 | HideWindow bool // Hide the console window on Windows 76 | } 77 | 78 | func main() { 79 | log.SetFlags(0) 80 | defer log.Println("INFO: Completed!") 81 | log.Printf("INFO: Starting xgo/%s", version) 82 | 83 | // Retrieve the CLI flags and the execution environment 84 | flag.Parse() 85 | 86 | xgoInXgo := os.Getenv("XGO_IN_XGO") == "1" 87 | if xgoInXgo { 88 | depsCache = "/deps-cache" 89 | } 90 | // Only use docker images if we're not already inside out own image 91 | image := "" 92 | 93 | if !xgoInXgo { 94 | // Ensure docker is available 95 | if err := checkDocker(); err != nil { 96 | log.Fatalf("ERROR: Failed to check docker installation: %v.", err) 97 | } 98 | // Validate the command line arguments 99 | if len(flag.Args()) != 1 { 100 | log.Fatalf("Usage: %s [options] ", os.Args[0]) 101 | } 102 | // Select the image to use, either official or custom 103 | image = fmt.Sprintf("%s:%s", dockerDist, *goVersion) 104 | if *dockerImage != "" { 105 | image = *dockerImage 106 | } else if *dockerRepo != "" { 107 | image = fmt.Sprintf("%s:%s", *dockerRepo, *goVersion) 108 | } 109 | // Check that all required images are available 110 | found := checkDockerImage(image) 111 | switch { 112 | case !found: 113 | fmt.Println("not found!") 114 | if err := pullDockerImage(image); err != nil { 115 | log.Fatalf("ERROR: Failed to pull docker image from the registry: %v.", err) 116 | } 117 | default: 118 | log.Println("INFO: Docker image found!") 119 | } 120 | } 121 | // Cache all external dependencies to prevent always hitting the internet 122 | if *crossDeps != "" { 123 | if err := os.MkdirAll(depsCache, 0751); err != nil { 124 | log.Fatalf("ERROR: Failed to create dependency cache: %v.", err) 125 | } 126 | // Download all missing dependencies 127 | for _, dep := range strings.Split(*crossDeps, " ") { 128 | if url := strings.TrimSpace(dep); len(url) > 0 { 129 | path := filepath.Join(depsCache, filepath.Base(url)) 130 | 131 | if _, err := os.Stat(path); err != nil { 132 | log.Printf("INFO: Downloading new dependency: %s...", url) 133 | out, err := os.Create(path) 134 | if err != nil { 135 | log.Fatalf("ERROR: Failed to create dependency file: %v", err) 136 | } 137 | res, err := http.Get(url) 138 | if err != nil { 139 | log.Fatalf("ERROR: Failed to retrieve dependency: %v", err) 140 | } 141 | defer res.Body.Close() 142 | 143 | if _, err := io.Copy(out, res.Body); err != nil { 144 | log.Fatalf("INFO: Failed to download dependency: %v", err) 145 | } 146 | out.Close() 147 | 148 | log.Printf("INFO: New dependency cached: %s.", path) 149 | } else { 150 | fmt.Printf("INFO: Dependency already cached: %s.", path) 151 | } 152 | } 153 | } 154 | } 155 | // Assemble the cross compilation environment and build options 156 | config := &ConfigFlags{ 157 | Repository: flag.Args()[0], 158 | Package: *srcPackage, 159 | Remote: *srcRemote, 160 | Branch: *srcBranch, 161 | Prefix: *outPrefix, 162 | Dependencies: *crossDeps, 163 | Arguments: *crossArgs, 164 | Targets: strings.Split(*targets, ","), 165 | } 166 | log.Printf("DBG: config: %+v", config) 167 | flags := &BuildFlags{ 168 | Verbose: *buildVerbose, 169 | Steps: *buildSteps, 170 | Race: *buildRace, 171 | Tags: *buildTags, 172 | LdFlags: *buildLdFlags, 173 | Mode: *buildMode, 174 | VCS: *buildVCS, 175 | TrimPath: *buildTrimPath, 176 | HideWindow: *buildHideWindow, 177 | } 178 | log.Printf("DBG: flags: %+v", flags) 179 | folder, err := os.Getwd() 180 | if err != nil { 181 | log.Fatalf("ERROR: Failed to retrieve the working directory: %v.", err) 182 | } 183 | if *outFolder != "" { 184 | folder, err = filepath.Abs(*outFolder) 185 | if err != nil { 186 | log.Fatalf("ERROR: Failed to resolve destination path (%s): %v.", *outFolder, err) 187 | } 188 | } 189 | // Execute the cross compilation, either in a container or the current system 190 | if !xgoInXgo { 191 | err = compile(image, config, flags, folder) 192 | } else { 193 | err = compileContained(config, flags, folder) 194 | } 195 | if err != nil { 196 | log.Fatalf("ERROR: Failed to cross compile package: %v.", err) 197 | } 198 | } 199 | 200 | // Checks whether a docker installation can be found and is functional. 201 | func checkDocker() error { 202 | log.Println("INFO: Checking docker installation...") 203 | if err := run(exec.Command("docker", "version")); err != nil { 204 | return err 205 | } 206 | fmt.Println() 207 | return nil 208 | } 209 | 210 | // Checks whether a required docker image is available locally. 211 | func checkDockerImage(image string) bool { 212 | log.Printf("INFO: Checking for required docker image %s... ", image) 213 | err := exec.Command("docker", "image", "inspect", image).Run() 214 | return err == nil 215 | } 216 | 217 | // Pulls an image from the docker registry. 218 | func pullDockerImage(image string) error { 219 | log.Printf("INFO: Pulling %s from docker registry...", image) 220 | return run(exec.Command("docker", "pull", image)) 221 | } 222 | 223 | // compile cross builds a requested package according to the given build specs 224 | // using a specific docker cross compilation image. 225 | func compile(image string, config *ConfigFlags, flags *BuildFlags, folder string) error { 226 | // If a local build was requested, find the import path and mount all GOPATH sources 227 | locals, mounts, paths := []string{}, []string{}, []string{} 228 | var usesModules bool 229 | if strings.HasPrefix(config.Repository, string(filepath.Separator)) || strings.HasPrefix(config.Repository, ".") { 230 | if fileExists(filepath.Join(config.Repository, "go.mod")) { 231 | usesModules = true 232 | } 233 | if !usesModules { 234 | // Resolve the repository import path from the file path 235 | config.Repository = resolveImportPath(config.Repository) 236 | if fileExists(filepath.Join(config.Repository, "go.mod")) { 237 | usesModules = true 238 | } 239 | } 240 | if !usesModules { 241 | log.Println("INFO: go.mod not found. Skipping go modules") 242 | } 243 | 244 | gopathEnv := os.Getenv("GOPATH") 245 | if gopathEnv == "" && !usesModules { 246 | log.Printf("INFO: No $GOPATH is set - defaulting to %s", build.Default.GOPATH) 247 | gopathEnv = build.Default.GOPATH 248 | } 249 | 250 | // Iterate over all the local libs and export the mount points 251 | if gopathEnv == "" && !usesModules { 252 | log.Fatalf("INFO: No $GOPATH is set or forwarded to xgo") 253 | } 254 | 255 | if !usesModules { 256 | os.Setenv("GO111MODULE", "off") 257 | for _, gopath := range strings.Split(gopathEnv, string(os.PathListSeparator)) { 258 | // Since docker sandboxes volumes, resolve any symlinks manually 259 | sources := filepath.Join(gopath, "src") 260 | filepath.Walk(sources, func(path string, info os.FileInfo, err error) error { 261 | // Skip any folders that errored out 262 | if err != nil { 263 | log.Printf("WARNING: Failed to access GOPATH element %s: %v", path, err) 264 | return nil 265 | } 266 | // Skip anything that's not a symlink 267 | if info.Mode()&os.ModeSymlink == 0 { 268 | return nil 269 | } 270 | // Resolve the symlink and skip if it's not a folder 271 | target, err := filepath.EvalSymlinks(path) 272 | if err != nil { 273 | return nil 274 | } 275 | if info, err = os.Stat(target); err != nil || !info.IsDir() { 276 | return nil 277 | } 278 | // Skip if the symlink points within GOPATH 279 | if filepath.HasPrefix(target, sources) { 280 | return nil 281 | } 282 | 283 | // Folder needs explicit mounting due to docker symlink security 284 | locals = append(locals, target) 285 | mounts = append(mounts, filepath.Join("/ext-go", strconv.Itoa(len(locals)), "src", strings.TrimPrefix(path, sources))) 286 | paths = append(paths, filepath.ToSlash(filepath.Join("/ext-go", strconv.Itoa(len(locals))))) 287 | return nil 288 | }) 289 | // Export the main mount point for this GOPATH entry 290 | locals = append(locals, sources) 291 | mounts = append(mounts, filepath.Join("/ext-go", strconv.Itoa(len(locals)), "src")) 292 | paths = append(paths, filepath.ToSlash(filepath.Join("/ext-go", strconv.Itoa(len(locals))))) 293 | } 294 | } 295 | } 296 | // Assemble and run the cross compilation command 297 | log.Printf("INFO: Cross compiling %s package...", config.Repository) 298 | 299 | args := []string{ 300 | "run", "--rm", 301 | "-v", folder + ":/build", 302 | "-v", depsCache + ":/deps-cache:ro", 303 | "-e", "REPO_REMOTE=" + config.Remote, 304 | "-e", "REPO_BRANCH=" + config.Branch, 305 | "-e", "PACK=" + config.Package, 306 | "-e", "DEPS=" + config.Dependencies, 307 | "-e", "ARGS=" + config.Arguments, 308 | "-e", "OUT=" + config.Prefix, 309 | "-e", fmt.Sprintf("FLAG_V=%v", flags.Verbose), 310 | "-e", fmt.Sprintf("FLAG_X=%v", flags.Steps), 311 | "-e", fmt.Sprintf("FLAG_RACE=%v", flags.Race), 312 | "-e", fmt.Sprintf("FLAG_TAGS=%s", flags.Tags), 313 | "-e", fmt.Sprintf("FLAG_LDFLAGS=%s", flags.LdFlags), 314 | "-e", fmt.Sprintf("FLAG_BUILDMODE=%s", flags.Mode), 315 | "-e", fmt.Sprintf("FLAG_BUILDVCS=%s", flags.VCS), 316 | "-e", fmt.Sprintf("FLAG_TRIMPATH=%v", flags.TrimPath), 317 | "-e", fmt.Sprintf("FLAG_HIDEWINDOW=%v", flags.HideWindow), 318 | "-e", "TARGETS=" + strings.Replace(strings.Join(config.Targets, " "), "*", ".", -1), 319 | } 320 | if usesModules { 321 | args = append(args, []string{"-e", "GO111MODULE=on"}...) 322 | args = append(args, []string{"-v", build.Default.GOPATH + ":/go"}...) 323 | if *goProxy != "" { 324 | args = append(args, []string{"-e", fmt.Sprintf("GOPROXY=%s", *goProxy)}...) 325 | } 326 | 327 | // Map this repository to the /source folder 328 | absRepository, err := filepath.Abs(config.Repository) 329 | if err != nil { 330 | log.Fatalf("ERROR: Failed to locate requested module repository: %v.", err) 331 | } 332 | args = append(args, []string{"-v", absRepository + ":/source"}...) 333 | 334 | // Check whether it has a vendor folder, and if so, use it 335 | vendorPath := absRepository + "/vendor" 336 | vendorfolder, err := os.Stat(vendorPath) 337 | if !os.IsNotExist(err) && vendorfolder.Mode().IsDir() { 338 | args = append(args, []string{"-e", "FLAG_MOD=vendor"}...) 339 | log.Printf("INFO: Using vendored Go module dependencies") 340 | } 341 | } else { 342 | args = append(args, []string{"-e", "GO111MODULE=off"}...) 343 | for i := 0; i < len(locals); i++ { 344 | args = append(args, []string{"-v", fmt.Sprintf("%s:%s:ro", locals[i], mounts[i])}...) 345 | } 346 | args = append(args, []string{"-e", "EXT_GOPATH=" + strings.Join(paths, ":")}...) 347 | } 348 | 349 | args = append(args, []string{image, config.Repository}...) 350 | log.Printf("INFO: Docker %s", strings.Join(args, " ")) 351 | return run(exec.Command("docker", args...)) 352 | } 353 | 354 | // compileContained cross builds a requested package according to the given build 355 | // specs using the current system opposed to running in a container. This is meant 356 | // to be used for cross compilation already from within an xgo image, allowing the 357 | // inheritance and bundling of the root xgo images. 358 | func compileContained(config *ConfigFlags, flags *BuildFlags, folder string) error { 359 | // If a local build was requested, resolve the import path 360 | local := strings.HasPrefix(config.Repository, string(filepath.Separator)) || strings.HasPrefix(config.Repository, ".") 361 | if local { 362 | // Resolve the repository import path from the file path 363 | config.Repository = resolveImportPath(config.Repository) 364 | 365 | // Determine if this is a module-based repository 366 | usesModules := fileExists(filepath.Join(config.Repository, "go.mod")) 367 | if !usesModules { 368 | os.Setenv("GO111MODULE", "off") 369 | log.Println("INFO: Don't use go modules (go.mod not found)") 370 | } 371 | } 372 | // Fine tune the original environment variables with those required by the build script 373 | env := []string{ 374 | "REPO_REMOTE=" + config.Remote, 375 | "REPO_BRANCH=" + config.Branch, 376 | "PACK=" + config.Package, 377 | "DEPS=" + config.Dependencies, 378 | "ARGS=" + config.Arguments, 379 | "OUT=" + config.Prefix, 380 | fmt.Sprintf("FLAG_V=%v", flags.Verbose), 381 | fmt.Sprintf("FLAG_X=%v", flags.Steps), 382 | fmt.Sprintf("FLAG_RACE=%v", flags.Race), 383 | fmt.Sprintf("FLAG_TAGS=%s", flags.Tags), 384 | fmt.Sprintf("FLAG_LDFLAGS=%s", flags.LdFlags), 385 | fmt.Sprintf("FLAG_BUILDMODE=%s", flags.Mode), 386 | fmt.Sprintf("FLAG_BUILDVCS=%s", flags.VCS), 387 | fmt.Sprintf("FLAG_TRIMPATH=%v", flags.TrimPath), 388 | "TARGETS=" + strings.Replace(strings.Join(config.Targets, " "), "*", ".", -1), 389 | } 390 | if local { 391 | env = append(env, "EXT_GOPATH=/non-existent-path-to-signal-local-build") 392 | } 393 | // Assemble and run the local cross compilation command 394 | log.Printf("INFO: Cross compiling %s package...", config.Repository) 395 | 396 | cmd := exec.Command("xgo-build", config.Repository) 397 | cmd.Env = append(os.Environ(), env...) 398 | 399 | return run(cmd) 400 | } 401 | 402 | // resolveImportPath converts a package given by a relative path to a Go import 403 | // path using the local GOPATH environment. 404 | func resolveImportPath(path string) string { 405 | abs, err := filepath.Abs(path) 406 | if err != nil { 407 | log.Fatalf("ERROR: Failed to locate requested package: %v.", err) 408 | } 409 | stat, err := os.Stat(abs) 410 | if err != nil || !stat.IsDir() { 411 | log.Fatalf("ERROR: Requested path invalid.") 412 | } 413 | pack, err := build.ImportDir(abs, build.FindOnly) 414 | if err != nil { 415 | log.Fatalf("ERROR: Failed to resolve import path: %v.", err) 416 | } 417 | return pack.ImportPath 418 | } 419 | 420 | // Executes a command synchronously, redirecting its output to stdout. 421 | func run(cmd *exec.Cmd) error { 422 | cmd.Stdout = os.Stdout 423 | cmd.Stderr = os.Stderr 424 | 425 | return cmd.Run() 426 | } 427 | 428 | // fileExists checks if given file exists 429 | func fileExists(file string) bool { 430 | if _, err := os.Stat(file); os.IsNotExist(err) { 431 | return false 432 | } 433 | return true 434 | } 435 | --------------------------------------------------------------------------------