1{ go, cacert, git, lib, stdenv }:
2
3{ name ? "${args'.pname}-${args'.version}"
4, src
5, nativeBuildInputs ? [ ]
6, passthru ? { }
7, patches ? [ ]
8
9 # Go tags, passed to go via -tag
10, tags ? [ ]
11
12 # A function to override the go-modules derivation
13, overrideModAttrs ? (_oldAttrs: { })
14
15 # path to go.mod and go.sum directory
16, modRoot ? "./"
17
18 # vendorHash is the SRI hash of the vendored dependencies
19 #
20 # if vendorHash is null, then we won't fetch any dependencies and
21 # rely on the vendor folder within the source.
22, vendorHash ? args'.vendorSha256 or (throw "buildGoModule: vendorHash is missing")
23 # Whether to delete the vendor folder supplied with the source.
24, deleteVendor ? false
25 # Whether to fetch (go mod download) and proxy the vendor directory.
26 # This is useful if your code depends on c code and go mod tidy does not
27 # include the needed sources to build or if any dependency has case-insensitive
28 # conflicts which will produce platform dependant `vendorHash` checksums.
29, proxyVendor ? false
30
31 # We want parallel builds by default
32, enableParallelBuilding ? true
33
34 # Do not enable this without good reason
35 # IE: programs coupled with the compiler
36, allowGoReference ? false
37
38, CGO_ENABLED ? go.CGO_ENABLED
39
40, meta ? { }
41
42 # Not needed with buildGoModule
43, goPackagePath ? ""
44
45 # needed for buildFlags{,Array} warning
46, buildFlags ? ""
47, buildFlagsArray ? ""
48
49, ...
50}@args':
51
52assert goPackagePath != "" -> throw "`goPackagePath` is not needed with `buildGoModule`";
53assert (args' ? vendorHash && args' ? vendorSha256) -> throw "both `vendorHash` and `vendorSha256` set. only one can be set.";
54
55let
56 args = removeAttrs args' [ "overrideModAttrs" "vendorSha256" "vendorHash" ];
57
58 go-modules = if (vendorHash == null) then "" else
59 (stdenv.mkDerivation {
60 name = "${name}-go-modules";
61
62 nativeBuildInputs = (args.nativeBuildInputs or [ ]) ++ [ go git cacert ];
63
64 inherit (args) src;
65 inherit (go) GOOS GOARCH;
66
67 # The following inheritence behavior is not trivial to expect, and some may
68 # argue it's not ideal. Changing it may break vendor hashes in Nixpkgs and
69 # out in the wild. In anycase, it's documented in:
70 # doc/languages-frameworks/go.section.md
71 prePatch = args.prePatch or "";
72 patches = args.patches or [ ];
73 patchFlags = args.patchFlags or [ ];
74 postPatch = args.postPatch or "";
75 preBuild = args.preBuild or "";
76 postBuild = args.modPostBuild or "";
77 sourceRoot = args.sourceRoot or "";
78
79 GO111MODULE = "on";
80
81 impureEnvVars = lib.fetchers.proxyImpureEnvVars ++ [
82 "GIT_PROXY_COMMAND"
83 "SOCKS_SERVER"
84 "GOPROXY"
85 ];
86
87 configurePhase = args.modConfigurePhase or ''
88 runHook preConfigure
89 export GOCACHE=$TMPDIR/go-cache
90 export GOPATH="$TMPDIR/go"
91 cd "${modRoot}"
92 runHook postConfigure
93 '';
94
95 buildPhase = args.modBuildPhase or (''
96 runHook preBuild
97 '' + lib.optionalString deleteVendor ''
98 if [ ! -d vendor ]; then
99 echo "vendor folder does not exist, 'deleteVendor' is not needed"
100 exit 10
101 else
102 rm -rf vendor
103 fi
104 '' + ''
105 if [ -d vendor ]; then
106 echo "vendor folder exists, please set 'vendorHash = null;' in your expression"
107 exit 10
108 fi
109
110 ${if proxyVendor then ''
111 mkdir -p "''${GOPATH}/pkg/mod/cache/download"
112 go mod download
113 '' else ''
114 if (( "''${NIX_DEBUG:-0}" >= 1 )); then
115 goModVendorFlags+=(-v)
116 fi
117 go mod vendor "''${goModVendorFlags[@]}"
118 ''}
119
120 mkdir -p vendor
121
122 runHook postBuild
123 '');
124
125 installPhase = args.modInstallPhase or ''
126 runHook preInstall
127
128 ${if proxyVendor then ''
129 rm -rf "''${GOPATH}/pkg/mod/cache/download/sumdb"
130 cp -r --reflink=auto "''${GOPATH}/pkg/mod/cache/download" $out
131 '' else ''
132 cp -r --reflink=auto vendor $out
133 ''}
134
135 if ! [ "$(ls -A $out)" ]; then
136 echo "vendor folder is empty, please set 'vendorHash = null;' in your expression"
137 exit 10
138 fi
139
140 runHook postInstall
141 '';
142
143 dontFixup = true;
144
145 outputHashMode = "recursive";
146 outputHash = vendorHash;
147 outputHashAlgo = if args' ? vendorSha256 || vendorHash == "" then "sha256" else null;
148 }).overrideAttrs overrideModAttrs;
149
150 package = stdenv.mkDerivation (args // {
151 nativeBuildInputs = [ go ] ++ nativeBuildInputs;
152
153 inherit (go) GOOS GOARCH;
154
155 GO111MODULE = "on";
156 GOFLAGS = lib.optionals (!proxyVendor) [ "-mod=vendor" ] ++ lib.optionals (!allowGoReference) [ "-trimpath" ];
157 inherit CGO_ENABLED enableParallelBuilding;
158
159 configurePhase = args.configurePhase or (''
160 runHook preConfigure
161
162 export GOCACHE=$TMPDIR/go-cache
163 export GOPATH="$TMPDIR/go"
164 export GOPROXY=off
165 export GOSUMDB=off
166 cd "$modRoot"
167 '' + lib.optionalString (vendorHash != null) ''
168 ${if proxyVendor then ''
169 export GOPROXY=file://${go-modules}
170 '' else ''
171 rm -rf vendor
172 cp -r --reflink=auto ${go-modules} vendor
173 ''}
174 '' + ''
175
176 # currently pie is only enabled by default in pkgsMusl
177 # this will respect the `hardening{Disable,Enable}` flags if set
178 if [[ $NIX_HARDENING_ENABLE =~ "pie" ]]; then
179 export GOFLAGS="-buildmode=pie $GOFLAGS"
180 fi
181
182 runHook postConfigure
183 '');
184
185 buildPhase = args.buildPhase or (''
186 runHook preBuild
187
188 exclude='\(/_\|examples\|Godeps\|testdata'
189 if [[ -n "$excludedPackages" ]]; then
190 IFS=' ' read -r -a excludedArr <<<$excludedPackages
191 printf -v excludedAlternates '%s\\|' "''${excludedArr[@]}"
192 excludedAlternates=''${excludedAlternates%\\|} # drop final \| added by printf
193 exclude+='\|'"$excludedAlternates"
194 fi
195 exclude+='\)'
196
197 buildGoDir() {
198 local cmd="$1" dir="$2"
199
200 . $TMPDIR/buildFlagsArray
201
202 declare -a flags
203 flags+=($buildFlags "''${buildFlagsArray[@]}")
204 flags+=(''${tags:+-tags=${lib.concatStringsSep "," tags}})
205 flags+=(''${ldflags:+-ldflags="$ldflags"})
206 flags+=("-p" "$NIX_BUILD_CORES")
207
208 if [ "$cmd" = "test" ]; then
209 flags+=(-vet=off)
210 flags+=($checkFlags)
211 fi
212
213 local OUT
214 if ! OUT="$(go $cmd "''${flags[@]}" $dir 2>&1)"; then
215 if ! echo "$OUT" | grep -qE '(no( buildable| non-test)?|build constraints exclude all) Go (source )?files'; then
216 echo "$OUT" >&2
217 return 1
218 fi
219 fi
220 if [ -n "$OUT" ]; then
221 echo "$OUT" >&2
222 fi
223 return 0
224 }
225
226 getGoDirs() {
227 local type;
228 type="$1"
229 if [ -n "$subPackages" ]; then
230 echo "$subPackages" | sed "s,\(^\| \),\1./,g"
231 else
232 find . -type f -name \*$type.go -exec dirname {} \; | grep -v "/vendor/" | sort --unique | grep -v "$exclude"
233 fi
234 }
235
236 if (( "''${NIX_DEBUG:-0}" >= 1 )); then
237 buildFlagsArray+=(-x)
238 fi
239
240 if [ ''${#buildFlagsArray[@]} -ne 0 ]; then
241 declare -p buildFlagsArray > $TMPDIR/buildFlagsArray
242 else
243 touch $TMPDIR/buildFlagsArray
244 fi
245 if [ -z "$enableParallelBuilding" ]; then
246 export NIX_BUILD_CORES=1
247 fi
248 for pkg in $(getGoDirs ""); do
249 echo "Building subPackage $pkg"
250 buildGoDir install "$pkg"
251 done
252 '' + lib.optionalString (stdenv.hostPlatform != stdenv.buildPlatform) ''
253 # normalize cross-compiled builds w.r.t. native builds
254 (
255 dir=$GOPATH/bin/${go.GOOS}_${go.GOARCH}
256 if [[ -n "$(shopt -s nullglob; echo $dir/*)" ]]; then
257 mv $dir/* $dir/..
258 fi
259 if [[ -d $dir ]]; then
260 rmdir $dir
261 fi
262 )
263 '' + ''
264 runHook postBuild
265 '');
266
267 doCheck = args.doCheck or true;
268 checkPhase = args.checkPhase or ''
269 runHook preCheck
270 # We do not set trimpath for tests, in case they reference test assets
271 export GOFLAGS=''${GOFLAGS//-trimpath/}
272
273 for pkg in $(getGoDirs test); do
274 buildGoDir test "$pkg"
275 done
276
277 runHook postCheck
278 '';
279
280 installPhase = args.installPhase or ''
281 runHook preInstall
282
283 mkdir -p $out
284 dir="$GOPATH/bin"
285 [ -e "$dir" ] && cp -r $dir $out
286
287 runHook postInstall
288 '';
289
290 strictDeps = true;
291
292 disallowedReferences = lib.optional (!allowGoReference) go;
293
294 passthru = passthru // { inherit go go-modules vendorHash; } // { inherit (args') vendorSha256; };
295
296 meta = {
297 # Add default meta information
298 platforms = go.meta.platforms or lib.platforms.all;
299 } // meta;
300 });
301in
302lib.warnIf (buildFlags != "" || buildFlagsArray != "")
303 "Use the `ldflags` and/or `tags` attributes instead of `buildFlags`/`buildFlagsArray`"
304 package