From 72b715fa99780e33e2d69615db43bc56834c9a5b Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Fri, 13 Oct 2023 21:56:22 +0200 Subject: [PATCH] all: add Nix flake file This adds a flake.nix file that makes it possible to quickly create a development environment. You can download Nix here, for use on your Linux or macOS system: https://nixos.org/download.html After you have installed Nix, you can enter the development environment as follows: nix develop This drops you into a bash shell, where you can install TinyGo simply using the following command: go install That's all! Assuming you've set up your $PATH correctly, you can now use the tinygo command as usual: tinygo version You can also do many other things from this environment. Building and flashing should work as you're used to: it's not a VM or container so there are no access restrictions. --- .github/workflows/nix.yml | 43 ++++++++++++++++++++++++++ compileopts/config.go | 6 ++-- flake.lock | 60 +++++++++++++++++++++++++++++++++++++ flake.nix | 63 +++++++++++++++++++++++++++++++++++++++ goenv/goenv.go | 15 ++++++++++ 5 files changed, 185 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/nix.yml create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml new file mode 100644 index 00000000..7d77d16d --- /dev/null +++ b/.github/workflows/nix.yml @@ -0,0 +1,43 @@ +name: Nix + +on: + pull_request: + push: + branches: + - dev + - release + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + nix-test: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Pull musl + run: | + git submodule update --init lib/musl + - name: Restore LLVM source cache + uses: actions/cache/restore@v3 + id: cache-llvm-source + with: + key: llvm-source-16-linux-nix-v1 + path: | + llvm-project/compiler-rt + - name: Download LLVM source + if: steps.cache-llvm-source.outputs.cache-hit != 'true' + run: make llvm-source + - name: Save LLVM source cache + uses: actions/cache/save@v3 + if: steps.cache-llvm-source.outputs.cache-hit != 'true' + with: + key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }} + path: | + llvm-project/compiler-rt + - uses: cachix/install-nix-action@v22 + - name: Test + run: | + nix develop --ignore-environment --keep HOME --command bash -c "go install && ~/go/bin/tinygo version && ~/go/bin/tinygo build -o test ./testdata/cgo" diff --git a/compileopts/config.go b/compileopts/config.go index 578534a1..36f7d8ed 100644 --- a/compileopts/config.go +++ b/compileopts/config.go @@ -275,7 +275,8 @@ func (c *Config) CFlags(libclang bool) []string { case "darwin-libSystem": root := goenv.Get("TINYGOROOT") cflags = append(cflags, - "--sysroot="+filepath.Join(root, "lib/macos-minimal-sdk/src"), + "-nostdlibinc", + "-isystem", filepath.Join(root, "lib/macos-minimal-sdk/src/usr/include"), ) case "picolibc": root := goenv.Get("TINYGOROOT") @@ -304,7 +305,8 @@ func (c *Config) CFlags(libclang bool) []string { root := goenv.Get("TINYGOROOT") path, _ := c.LibcPath("mingw-w64") cflags = append(cflags, - "--sysroot="+path, + "-nostdlibinc", + "-isystem", filepath.Join(path, "include"), "-isystem", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "crt"), "-isystem", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "defaults", "include"), "-D_UCRT", diff --git a/flake.lock b/flake.lock new file mode 100644 index 00000000..4dd5f2e9 --- /dev/null +++ b/flake.lock @@ -0,0 +1,60 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1694529238, + "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1696983906, + "narHash": "sha256-L7GyeErguS7Pg4h8nK0wGlcUTbfUMDu+HMf1UcyP72k=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "bd1cde45c77891214131cbbea5b1203e485a9d51", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "ref": "nixos-23.05", + "type": "indirect" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 00000000..9116fd2d --- /dev/null +++ b/flake.nix @@ -0,0 +1,63 @@ +# A Nix flake file, mainly intended for developing TinyGo. +# You can download Nix here, for use on your Linux or macOS system: +# https://nixos.org/download.html +# After you have installed Nix, you can enter the development environment as +# follows: +# +# nix develop +# +# This drops you into a bash shell, where you can install TinyGo simply using +# the following command: +# +# go install +# +# That's all! Assuming you've set up your $PATH correctly, you can now use the +# tinygo command as usual: +# +# tinygo version +# +# You can also do many other things from this environment. Building and flashing +# should work as you're used to: it's not a VM or container so there are no +# access restrictions and you're running in the same host environment - just +# with a slightly different set of tools available. +{ + inputs = { + # Use a recent stable release, but fix the version to make it reproducible. + # This version should be updated from time to time. + nixpkgs.url = "nixpkgs/nixos-23.05"; + flake-utils.url = "github:numtide/flake-utils"; + }; + outputs = { self, nixpkgs, flake-utils }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + in + with pkgs; + { + devShells.default = mkShell { + buildInputs = [ + # These dependencies are required for building tinygo (go install). + go + llvmPackages_16.llvm + llvmPackages_16.libclang + # Additional dependencies needed at runtime, for building and/or + # flashing. + llvmPackages_16.lld + avrdude + binaryen + # Additional dependencies needed for on-chip debugging. + # These tools are rather big (especially GDB) and not frequently + # used, so are commented out. On-chip debugging is still possible if + # these tools are available in the host environment. + #gdb + #openocd + ]; + shellHook= '' + # Ugly hack to make the Clang resources directory available. + # Perhaps there is a cleaner way to do it, but this works. + export GOFLAGS="\"-ldflags=-X github.com/tinygo-org/tinygo/goenv.clangResourceDir=${llvmPackages_16.clang.cc.lib}/lib/clang/16"\" + ''; + }; + } + ); +} diff --git a/goenv/goenv.go b/goenv/goenv.go index 5e2a3753..4715b876 100644 --- a/goenv/goenv.go +++ b/goenv/goenv.go @@ -43,6 +43,12 @@ var hasBuiltinTools = false // directory. var TINYGOROOT string +// If a particular Clang resource dir must always be used and TinyGo can't +// figure out the directory using heuristics, this global can be set using a +// linker flag. +// This is needed for Nix. +var clangResourceDir string + // Variables read from a `go env` command invocation. var goEnvVars struct { GOPATH string @@ -298,6 +304,15 @@ func isSourceDir(root string) bool { // In that case, the resource dir is always returned (even when linking // dynamically against LLVM) because libclang always needs this directory. func ClangResourceDir(libclang bool) string { + if clangResourceDir != "" { + // The resource dir is forced to a particular value at build time. + // This is needed on Nix for example, where Clang and libclang don't + // know their own resource dir. + // Also see: + // https://discourse.nixos.org/t/why-is-the-clang-resource-dir-split-in-a-separate-package/34114 + return clangResourceDir + } + if !hasBuiltinTools && !libclang { // Using external tools, so the resource dir doesn't need to be // specified. Clang knows where to find it.