experiment with buildGo2, a potential replacement for buildGo

This commit is contained in:
Luke Granger-Brown 2024-02-11 04:23:47 +00:00
parent 8141fbfccf
commit d666e85223
12 changed files with 319 additions and 0 deletions

6
go/buildgo2/asm/add.S Normal file
View file

@ -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

View file

@ -0,0 +1,3 @@
package asm
func Add(x, y int64) int64

View file

@ -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 ];
}

6
go/buildgo2/cgo/cgo.c Normal file
View file

@ -0,0 +1,6 @@
#include <stdint.h>
#include "_cgo_export.h"
int64_t add(int64_t x, int64_t y) {
return AddInternal(x + y, 0);
}

14
go/buildgo2/cgo/cgo.go Normal file
View file

@ -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
}

4
go/buildgo2/cgo/cgo.h Normal file
View file

@ -0,0 +1,4 @@
#include <stdint.h>
int64_t add(int64_t x, int64_t y);

View file

@ -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;
}

13
go/buildgo2/default.nix Normal file
View file

@ -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;
}

14
go/buildgo2/main.go Normal file
View file

@ -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))
}

View file

@ -15,4 +15,6 @@ args: {
access = import ./access args; access = import ./access args;
vault = import ./vault args; vault = import ./vault args;
tumblrandom = import ./tumblrandom args; tumblrandom = import ./tumblrandom args;
buildgo2 = import ./buildgo2 args;
} }

View file

@ -125,6 +125,7 @@ rec {
bat_syntaxes = tvlDepot.third_party.bat_syntaxes; bat_syntaxes = tvlDepot.third_party.bat_syntaxes;
cheddar = tvlDepot.tools.cheddar; cheddar = tvlDepot.tools.cheddar;
buildGo2 = tvlDepot.nix.buildGo2;
naersk = nixpkgs.callPackage naerskSrc {}; naersk = nixpkgs.callPackage naerskSrc {};
crate2nix = import "${crate2nixSrc}" { pkgs = ch.depot.pkgs; }; crate2nix = import "${crate2nixSrc}" { pkgs = ch.depot.pkgs; };

241
third_party/tvl/nix/buildGo2/default.nix vendored Normal file
View file

@ -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 <nixpkgs> { }
, ...
}:
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;
}