242 lines
7.5 KiB
Nix
242 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;
|
||
|
}
|