184 lines
		
	
	
	
		
			6.1 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
			
		
		
	
	
			184 lines
		
	
	
	
		
			6.1 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 (builtins)
 | |
|     attrNames
 | |
|     baseNameOf
 | |
|     dirOf
 | |
|     elemAt
 | |
|     filter
 | |
|     listToAttrs
 | |
|     map
 | |
|     match
 | |
|     readDir
 | |
|     replaceStrings
 | |
|     toString;
 | |
| 
 | |
|   inherit (pkgs) lib go runCommand fetchFromGitHub protobuf symlinkJoin;
 | |
| 
 | |
|   # Helpers for low-level Go compiler invocations
 | |
|   spaceOut = lib.concatStringsSep " ";
 | |
| 
 | |
|   includeDepSrc = dep: "-I ${dep}";
 | |
|   includeSources = deps: spaceOut (map includeDepSrc deps);
 | |
| 
 | |
|   includeDepLib = dep: "-L ${dep}";
 | |
|   includeLibs = deps: spaceOut (map includeDepLib deps);
 | |
| 
 | |
|   srcBasename = src: elemAt (match "([a-z0-9]{32}\-)?(.*\.go)" (baseNameOf src)) 1;
 | |
|   srcCopy = path: src: "cp ${src} $out/${path}/${srcBasename src}";
 | |
|   srcList = path: srcs: lib.concatStringsSep "\n" (map (srcCopy path) srcs);
 | |
| 
 | |
|   allDeps = deps: lib.unique (lib.flatten (deps ++ (map (d: d.goDeps) deps)));
 | |
| 
 | |
|   xFlags = x_defs: spaceOut (map (k: "-X ${k}=${x_defs."${k}"}") (attrNames x_defs));
 | |
| 
 | |
|   pathToName = p: replaceStrings ["/"] ["_"] (toString p);
 | |
| 
 | |
|   # Add an `overrideGo` attribute to a function result that works
 | |
|   # similar to `overrideAttrs`, but is used specifically for the
 | |
|   # arguments passed to Go builders.
 | |
|   makeOverridable = f: orig: (f orig) // {
 | |
|     overrideGo = new: makeOverridable f (orig // (new orig));
 | |
|   };
 | |
| 
 | |
|   # High-level build functions
 | |
| 
 | |
|   # Build a Go program out of the specified files and dependencies.
 | |
|   program = { name, srcs, deps ? [], x_defs ? {} }:
 | |
|   let uniqueDeps = allDeps deps;
 | |
|   in runCommand name {} ''
 | |
|     ${go}/bin/go tool compile -o ${name}.a -trimpath=$PWD -trimpath=${go} ${includeSources uniqueDeps} ${spaceOut srcs}
 | |
|     mkdir -p $out/bin
 | |
|     ${go}/bin/go tool link -o $out/bin/${name} -buildid nix ${xFlags x_defs} ${includeLibs uniqueDeps} ${name}.a
 | |
|   '';
 | |
| 
 | |
|   # Build a Go library assembled out of the specified files.
 | |
|   #
 | |
|   # This outputs both the sources and compiled binary, as both are
 | |
|   # needed when downstream packages depend on it.
 | |
|   package = { name, srcs, deps ? [], path ? name }:
 | |
|   let uniqueDeps = allDeps deps;
 | |
|   in (runCommand "golib-${name}" {} ''
 | |
|     mkdir -p $out/${path}
 | |
|     ${srcList path (map (s: "${s}") srcs)}
 | |
|     ${go}/bin/go tool compile -o $out/${path}.a -trimpath=$PWD -trimpath=${go} -p ${path} ${includeSources uniqueDeps} ${spaceOut srcs}
 | |
|   '') // { goDeps = uniqueDeps; goImportPath = path; };
 | |
| 
 | |
|   # Build a Go library out of the specified protobuf definition.
 | |
|   proto = { name, proto, path ? name, extraDeps ? [] }: (makeOverridable package) {
 | |
|     inherit name path;
 | |
|     deps = [ protoLibs.goProto ] ++ extraDeps;
 | |
|     srcs = lib.singleton (runCommand "goproto-${name}.pb.go" {} ''
 | |
|       cp ${proto} ${baseNameOf proto}
 | |
|       ${protobuf}/bin/protoc --plugin=${protoLibs.goProto}/bin/protoc-gen-go \
 | |
|         --go_out=plugins=grpc,import_path=${baseNameOf path}:. ${baseNameOf proto}
 | |
|       mv *.pb.go $out
 | |
|     '');
 | |
|   };
 | |
| 
 | |
|   # Build a Go library out of the specified gRPC definition.
 | |
|   grpc = args: proto (args // { extraDeps = [ protoLibs.goGrpc ]; });
 | |
| 
 | |
|   # Traverse an externally defined Go library to build up a tree of
 | |
|   # its packages.
 | |
|   #
 | |
|   # TODO(tazjin): Automatically infer which packages depend on which
 | |
|   # other packages, which currently requires overriding.
 | |
|   #
 | |
|   # TODO(tazjin): Add support for rewriting package paths.
 | |
|   external' = { src, path, deps ? [] }:
 | |
|     let
 | |
|       dir = readDir src;
 | |
|       isGoFile = f: (match ".*\.go" f) != null;
 | |
|       isGoTest = f: (match ".*_test\.go" f) != null;
 | |
|       goFileFilter = k: v: (v == "regular") && (isGoFile k) && (!isGoTest k);
 | |
|       goSources =
 | |
|         let goFiles = filter (f: goFileFilter f dir."${f}") (attrNames dir);
 | |
|         in map (f: src + ("/" + f)) goFiles;
 | |
| 
 | |
|       subDirs = filter (n: dir."${n}" == "directory") (attrNames dir);
 | |
|       subPackages = map (name: {
 | |
|         inherit name;
 | |
|         value = external' {
 | |
|           inherit deps;
 | |
|           src = src + ("/" + name);
 | |
|           path = path + ("/" + name);
 | |
|         };
 | |
|       }) subDirs;
 | |
|       subAttrs = listToAttrs (filter (p: p.value != {}) subPackages);
 | |
| 
 | |
|       current = package {
 | |
|         inherit deps path;
 | |
|         name = pathToName path;
 | |
|         srcs = goSources;
 | |
|       };
 | |
|     in if goSources == [] then subAttrs else (current // subAttrs);
 | |
| 
 | |
|   # Build an externally defined Go library using `go build` itself.
 | |
|   #
 | |
|   # Libraries built this way can be included in any standard buildGo
 | |
|   # build.
 | |
|   #
 | |
|   # Contrary to other functions, `src` is expected to point at a
 | |
|   # single directory containing the root of the external library.
 | |
|   external = { path, src, deps ? [], srcOnly ? false, targets ? [ "..." ] }:
 | |
|     let
 | |
|       name = pathToName path;
 | |
|       uniqueDeps = allDeps deps;
 | |
|       srcDir = runCommand "goext-src-${name}" {} ''
 | |
|         mkdir -p $out/${dirOf path}
 | |
|         cp -r ${src} $out/${dirOf path}/${baseNameOf path}
 | |
|       '';
 | |
|       gopathSrc = symlinkJoin {
 | |
|         name = "gopath-${name}";
 | |
|         paths = uniqueDeps ++ [ srcDir ];
 | |
|       };
 | |
|       gopathPkg = runCommand "goext-pkg-${name}" {} ''
 | |
|         mkdir -p gopath $out
 | |
|         export GOPATH=$PWD/gopath
 | |
|         ln -s ${gopathSrc} gopath/src
 | |
|         ${go}/bin/go install ${spaceOut (map (t: path + "/" + t) targets)}
 | |
| 
 | |
|         if [[ -d gopath/pkg/linux_amd64 ]]; then
 | |
|           echo "Installing Go packages for ${path}"
 | |
|           mv gopath/pkg/linux_amd64/* $out
 | |
|         fi
 | |
| 
 | |
|         if [[ -d gopath/bin ]]; then
 | |
|           echo "Installing Go binaries for ${path}"
 | |
|           mv gopath/bin $out/bin
 | |
|         fi
 | |
|       '';
 | |
|     in (if srcOnly then gopathSrc else symlinkJoin {
 | |
|       name = "goext-${name}";
 | |
|       paths = [ gopathSrc gopathPkg ];
 | |
|     }) // { goDeps = uniqueDeps; };
 | |
| 
 | |
|   protoLibs = import ./proto.nix {
 | |
|     inherit external;
 | |
|   };
 | |
| in {
 | |
|   # Only the high-level builder functions are exposed, but made
 | |
|   # overrideable.
 | |
|   program = makeOverridable program;
 | |
|   package = makeOverridable package;
 | |
|   proto = makeOverridable proto;
 | |
|   grpc = makeOverridable grpc;
 | |
|   external = makeOverridable external;
 | |
| 
 | |
|   # TODO: remove
 | |
|   inherit external';
 | |
| 
 | |
|   extTest = external' {
 | |
|     src = /home/tazjin/go/src/cloud.google.com/go;
 | |
|     path = "cloud.google.com/go";
 | |
|   };
 | |
| }
 |