diff --git a/go/buildgo2/asm/add.S b/go/buildgo2/asm/add.S new file mode 100644 index 0000000000..afaea15616 --- /dev/null +++ b/go/buildgo2/asm/add.S @@ -0,0 +1,6 @@ +TEXT ·Add(SB),$0-24 + MOVQ x+0(FP), BX + MOVQ y+8(FP), BP + ADDQ BP, BX + MOVQ BX, ret+16(FP) + RET diff --git a/go/buildgo2/asm/buildgo2.go b/go/buildgo2/asm/buildgo2.go new file mode 100644 index 0000000000..945fcc59cc --- /dev/null +++ b/go/buildgo2/asm/buildgo2.go @@ -0,0 +1,3 @@ +package asm + +func Add(x, y int64) int64 diff --git a/go/buildgo2/asm/default.nix b/go/buildgo2/asm/default.nix new file mode 100644 index 0000000000..2d83ae6302 --- /dev/null +++ b/go/buildgo2/asm/default.nix @@ -0,0 +1,7 @@ +{ depot, ... }: + +depot.third_party.buildGo2.package { + name = "asm"; + path = "hg.lukegb.com/lukegb/depot/go/buildgo2/asm"; + srcs = [ ./buildgo2.go ./add.S ]; +} diff --git a/go/buildgo2/cgo/cgo.c b/go/buildgo2/cgo/cgo.c new file mode 100644 index 0000000000..8a10fe5a15 --- /dev/null +++ b/go/buildgo2/cgo/cgo.c @@ -0,0 +1,6 @@ +#include +#include "_cgo_export.h" + +int64_t add(int64_t x, int64_t y) { + return AddInternal(x + y, 0); +} diff --git a/go/buildgo2/cgo/cgo.go b/go/buildgo2/cgo/cgo.go new file mode 100644 index 0000000000..3d2bbfc796 --- /dev/null +++ b/go/buildgo2/cgo/cgo.go @@ -0,0 +1,14 @@ +package cgo + +// #include "cgo.h" +import "C" + +func Add(x, y int64) int64 { + return int64(C.add(C.int64_t(x), C.int64_t(y))) +} + +//export AddInternal +func AddInternal(x, y int64) int64 { + + return x + y +} diff --git a/go/buildgo2/cgo/cgo.h b/go/buildgo2/cgo/cgo.h new file mode 100644 index 0000000000..99d86e7454 --- /dev/null +++ b/go/buildgo2/cgo/cgo.h @@ -0,0 +1,4 @@ + +#include + +int64_t add(int64_t x, int64_t y); diff --git a/go/buildgo2/cgo/default.nix b/go/buildgo2/cgo/default.nix new file mode 100644 index 0000000000..968c4fc6ca --- /dev/null +++ b/go/buildgo2/cgo/default.nix @@ -0,0 +1,8 @@ +{ depot, ... }: + +depot.third_party.buildGo2.package { + name = "cgo"; + path = "hg.lukegb.com/lukegb/depot/go/buildgo2/cgo"; + srcs = [ ./cgo.go ./cgo.h ./cgo.c ]; + cgo = true; +} diff --git a/go/buildgo2/default.nix b/go/buildgo2/default.nix new file mode 100644 index 0000000000..91b625adde --- /dev/null +++ b/go/buildgo2/default.nix @@ -0,0 +1,13 @@ +{ depot, ... }@args: + +let + asm = import ./asm args; + cgo = import ./cgo args; +in + depot.third_party.buildGo2.program { + name = "buildgo2"; + srcs = [ ./main.go ]; + deps = [ asm ]; + } // { + inherit asm cgo; + } diff --git a/go/buildgo2/main.go b/go/buildgo2/main.go new file mode 100644 index 0000000000..d9c8cf52de --- /dev/null +++ b/go/buildgo2/main.go @@ -0,0 +1,14 @@ +package main + +import ( + "fmt" + + "hg.lukegb.com/lukegb/depot/go/buildgo2/asm" + //"hg.lukegb.com/lukegb/depot/go/buildgo2/cgo" +) + +func main() { + fmt.Println("hi") + fmt.Printf("asm: 2 + 3 = %d\n", asm.Add(2, 3)) + //fmt.Printf("cgo: 2 + 3 = %d\n", cgo.Add(2, 3)) +} diff --git a/go/default.nix b/go/default.nix index 034f521e1d..d92bb2b8fa 100644 --- a/go/default.nix +++ b/go/default.nix @@ -15,4 +15,6 @@ args: { access = import ./access args; vault = import ./vault args; tumblrandom = import ./tumblrandom args; + + buildgo2 = import ./buildgo2 args; } diff --git a/third_party/default.nix b/third_party/default.nix index b43d5c6838..fa33d805e9 100644 --- a/third_party/default.nix +++ b/third_party/default.nix @@ -125,6 +125,7 @@ rec { bat_syntaxes = tvlDepot.third_party.bat_syntaxes; cheddar = tvlDepot.tools.cheddar; + buildGo2 = tvlDepot.nix.buildGo2; naersk = nixpkgs.callPackage naerskSrc {}; crate2nix = import "${crate2nixSrc}" { pkgs = ch.depot.pkgs; }; diff --git a/third_party/tvl/nix/buildGo2/default.nix b/third_party/tvl/nix/buildGo2/default.nix new file mode 100644 index 0000000000..407902a6fa --- /dev/null +++ b/third_party/tvl/nix/buildGo2/default.nix @@ -0,0 +1,241 @@ +# Copyright 2019 Google LLC. +# SPDX-License-Identifier: Apache-2.0 +# +# buildGo provides Nix functions to build Go packages in the style of Bazel's +# rules_go. + +{ pkgs ? import { } +, ... +}: + +let + inherit (pkgs) lib; + + go = pkgs.go; + goStdlib = buildStdlib { inherit go; }; + + splitSources = srcs: let + # exts: { typeName = [ extension ] }; + exts = { + go = [ ".go" ]; + asm = [ ".s" ".S" ]; + headers = [ ".h" ".hh" ".hpp" ".hxx" ".inc" ]; + c = [ ".c" ]; + cxx = [ ".cc" ".cxx" ".cpp" ]; + objc = [ ".m" ".mm" ]; + }; + # [ extension ] -> { extension = true; } + extListToAttrs = exts: builtins.listToAttrs (builtins.map (v: lib.attrsets.nameValuePair v true) exts); + # exts': { typeName = { extension = true; } }; + exts' = builtins.mapAttrs (_: extListToAttrs) exts; + + allExts = lib.lists.flatten (builtins.attrValues exts); + allExts' = extListToAttrs allExts; + + matchesExts = exts: src: let + srcExtList = builtins.match ".*([.][^.]+)$" (toString src); + srcExt = builtins.elemAt srcExtList 0; + in + srcExtList != null && exts ? "${srcExt}"; + splitByExt = exts: builtins.filter (matchesExts exts) srcs; + + leftovers = builtins.filter (src: !(matchesExts allExts' src)) srcs; + in assert (lib.assertMsg (builtins.length leftovers == 0) "uncategorisable files: ${toString leftovers}"); builtins.mapAttrs (name: value: splitByExt value) exts'; + + importconfig = { name, deps }: let + # Go through every dep and generate a packagefile importpath=${output}. + depPackagefiles = map (dep: if dep ? importpath then "packagefile ${dep.importpath}=${dep}/pkg.a" else "") deps; + in '' + # nix buildGo ${name} + ${builtins.concatStringsSep "\n" depPackagefiles} + ''; + + buildStdlib = { go }: pkgs.stdenv.mkDerivation { + pname = "go-stdlib"; + inherit (go) version; + + nativeBuildInputs = [ go ]; + + unpackPhase = '' + HOME=$NIX_BUILD_TOP/home + mkdir $HOME + + goroot="$(go env GOROOT)" + cp -R "$goroot/src" "$goroot/pkg" . + ''; + dontConfigure = true; + buildPhase = '' + chmod -R +w . + GODEBUG=installgoroot=all GOROOT=$NIX_BUILD_TOP go install -v --trimpath std + ''; + installPhase = '' + mkdir $out + cp -r pkg/*_*/* $out + + find $out -name '*.a' | while read -r ARCHIVE_FULL; do + ARCHIVE="''${ARCHIVE_FULL#"$out/"}" + PACKAGE="''${ARCHIVE%.a}" + echo "packagefile $PACKAGE=$ARCHIVE_FULL" + done > $out/importcfg + ''; + dontFixup = true; + + passthru.go = go; + }; + + package = { + name, + path, + srcs, + deps ? [], + cgo ? false, + cgodeps ? [], + go ? goStdlib.go, + stdlib ? goStdlib, + }@args: assert stdlib != null -> stdlib.go == go; let + importcfg = importconfig { inherit name deps; stdlib = go; }; + + srcs' = splitSources srcs; + stdenv = if cgo then pkgs.stdenv else pkgs.stdenvNoCC; + + cgoSrcs = srcs'.c ++ srcs'.cxx ++ srcs'.objc; + noCgoNoC = lib.assertMsg (builtins.length cgoSrcs == 0) "cgo source files present, but cgo not set to true: ${toString cgoSrcs}"; + + baseNameOnly = map (f: baseNameOf (toString f)); + in assert !cgo -> noCgoNoC; stdenv.mkDerivation rec { + inherit name; + __structuredAttrs = true; + + buildInputs = cgodeps; + nativeBuildInputs = [ go ] ++ (if cgo then [ pkgs.pkg-config ] else []); + + passthru = { + importpath = path; + inherit importcfg stdlib; + cgo = cgo || lib.lists.any (x: x.cgo) deps; + }; + + importcfg = importconfig { inherit name deps; }; + allSrcs = srcs; + goSrcs = baseNameOnly srcs'.go; + asmSrcs = baseNameOnly srcs'.asm; + cSrcs = baseNameOnly srcs'.c; + cxxSrcs = baseNameOnly srcs'.cxx; + objcSrcs = baseNameOnly srcs'.objc; + + unpackPhase = '' + mkdir src + ${builtins.concatStringsSep "\n" (map (f: '' + cp "${f}" "src/${baseNameOf (toString f)}" + '') srcs)} + cd src + ''; + configurePhase = '' + echo "$importcfg" > $NIX_BUILD_TOP/importcfg + ${if stdlib != null then '' + cat ${stdlib}/importcfg >> $NIX_BUILD_TOP/importcfg + '' else ""} + for dep in deps; do + if [[ ! -f "$dep/importcfg" ]]; then continue; fi + cat $dep/importcfg >> $NIX_BUILD_TOP/importcfg + done + ''; + buildPhase = '' + mkdir -p $out + completeFlag="${if cgo then "" else "-complete"}" + outputFlag="-o $out/pkg.a" + if [ ''${#asmSrcs[@]} -gt 0 ]; then + completeFlag="" + mkdir $NIX_BUILD_TOP/pack $NIX_BUILD_TOP/include + outputFlag="-symabis $NIX_BUILD_TOP/goasm.abi -o $out/pkg.a -asmhdr $NIX_BUILD_TOP/include/go_asm.h" + + touch $NIX_BUILD_TOP/include/go_asm.h + + ASMCMD="go tool asm -trimpath $NIX_BUILD_TOP/src -I $NIX_BUILD_TOP/include -I $(go env GOROOT)/pkg/include -D GOOS=$(go env GOOS) -D GOOS_$(go env GOOS) -D GOARCH=$(go env GOARCH) -D GOARCH_$(go env GOARCH) -p ${path}" + $ASMCMD -gensymabis -o $NIX_BUILD_TOP/goasm.abi "''${asmSrcs[@]}" + fi + ${if cgo then '' + mkdir $NIX_BUILD_TOP/cgo $NIX_BUILD_TOP/cgo_built + go tool cgo -objdir $NIX_BUILD_TOP/cgo -trimpath $NIX_BUILD_TOP/src -importpath "${path}" -- "''${goSrcs[@]}" + for f in "''${cSrcs[@]}"; do + $CC -I$NIX_BUILD_TOP/cgo -I. -c "$f" -o "$NIX_BUILD_TOP/cgo_built/''${f}.o" + done + for f in "''${cxxSrcs[@]}"; do + $CXX -I$NIX_BUILD_TOP/cgo -I. -c "$f" -o "$NIX_BUILD_TOP/cgo_built/''${f}.o" + done + for f in "''${objcSrcs[@]}"; do + $OBJC -I$NIX_BUILD_TOP/cgo -I. -c "$f" -o "$NIX_BUILD_TOP/cgo_built/''${f}.o" + done + pushd $NIX_BUILD_TOP/cgo &>/dev/null + for f in *.cgo2.c _cgo_export.c _cgo_main.c; do + $CC -I$NIX_BUILD_TOP/src -c "$f" -o ''${f}.o + done + $CC -o _cgo_.o _cgo_main.c.o _cgo_export.c.o $NIX_BUILD_TOP/cgo_built/*.o *.cgo2.c.o + popd &>/dev/null + go tool cgo -dynpackage "${name}" -dynimport $NIX_BUILD_TOP/cgo/_cgo_.o -dynout $NIX_BUILD_TOP/cgo/_cgo_imports.go + shopt -s nullglob + goSrcs=($NIX_BUILD_TOP/cgo/*.go) + '' else ""} + go tool compile $completeFlag -importcfg $NIX_BUILD_TOP/importcfg -trimpath $NIX_BUILD_TOP/src -pack -p "${path}" $outputFlag "''${goSrcs[@]}" + if [ ''${#asmSrcs[@]} -gt 0 ]; then + $ASMCMD -o $NIX_BUILD_TOP/pack/goasm.o "''${asmSrcs[@]}" + go tool pack r $out/pkg.a $NIX_BUILD_TOP/pack/*.o + fi + ${if cgo then '' + go tool pack r $out/pkg.a $NIX_BUILD_TOP/cgo/*.cgo2.c.o $NIX_BUILD_TOP/cgo/_cgo_export.c.o $NIX_BUILD_TOP/cgo_built/*.o + '' else ""} + ''; + }; + + program = { + name, + deps ? [], + ... + }@args: let + pkg = package (args // { + name = "${name}-lib"; + path = "main"; + }); + cgo = pkg.cgo; + stdenv = if cgo then pkgs.stdenv else pkgs.stdenvNoCC; + in stdenv.mkDerivation rec { + inherit name; + __structuredAttrs = true; + + nativeBuildInputs = [ go ]; + + lib = pkg; + inherit (pkg) importcfg stdlib; + + dontUnpack = true; + configurePhase = '' + echo "$importcfg" > importcfg + ${if stdlib != null then '' + cat ${stdlib}/importcfg >> importcfg + '' else ""} + ''; + buildPhase = '' + mkdir out + go tool link -importcfg importcfg -tmpdir "$TMPDIR" -o "out/bin" -s -w $lib/pkg.a + ''; + installPhase = '' + mkdir -p $out/bin + cp out/bin "$out/bin/${name}" + ''; + }; + + external = { + src, + path, + deps ? [ ], + tags ? [ ], + cgo ? false, + cgodeps ? [ ], + cgocflags ? [ ], + cgoldflags ? [ ], + }: {}; +in +{ + #inherit program package proto external; + inherit goStdlib package program external; +}