depot/third_party/tvl/nix/buildGo2/default.nix

241 lines
7.5 KiB
Nix

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