diff --git a/builder/default.nix b/builder/default.nix index 75f0043..bfe3298 100644 --- a/builder/default.nix +++ b/builder/default.nix @@ -8,11 +8,14 @@ }: let + parseGoMod = import ./parser.nix; + removeExpr = refs: ''remove-references-to ${lib.concatMapStrings (ref: " -t ${ref}") refs}''; buildGoApplication = { modules , src + , pwd ? null , CGO_ENABLED ? "0" , nativeBuildInputs ? [ ] , allowGoReference ? false @@ -23,6 +26,22 @@ let let modulesStruct = builtins.fromTOML (builtins.readFile modules); + goMod = parseGoMod (builtins.readFile "${builtins.toString pwd}/go.mod"); + localReplaceCommands = + let + localReplaceAttrs = lib.filterAttrs (n: v: lib.hasAttr "path" v) goMod.replace; + commands = ( + lib.mapAttrsToList + (name: value: ( + '' + mkdir -p $(dirname vendor/${name}) + ln -s ${pwd + "/${value.path}"} vendor/${name} + '' + )) + localReplaceAttrs); + in + if pwd != null then commands else [ ]; + vendorEnv = runCommand "vendor-env" { nativeBuildInputs = [ go ]; @@ -50,6 +69,9 @@ let export GOPATH="$TMPDIR/go" go run ${./symlink.go} + ${lib.concatStringsSep "\n" localReplaceCommands} + + find vendor mv vendor $out '' diff --git a/builder/parser.nix b/builder/parser.nix new file mode 100644 index 0000000..3c6131d --- /dev/null +++ b/builder/parser.nix @@ -0,0 +1,134 @@ +# Parse go.mod in Nix +# Returns a Nix structure with the contents of the go.mod passed in +# in normalised form. + +let + inherit (builtins) elemAt mapAttrs split foldl' match filter typeOf; + + # Strip lines with comments & other junk + stripStr = s: elemAt (split "^ *" (elemAt (split " *$" s) 0)) 2; + stripLines = initialLines: foldl' (acc: f: f acc) initialLines [ + # Strip comments + (lines: map + (l: + let + m = match "(.*)( |)//.*" l; + hasComment = m != null; + in + stripStr (if hasComment then elemAt m 0 else l)) + lines) + + # Strip leading tabs characters + (lines: map (l: elemAt (match "(\t|)(.*)" l) 1) lines) + + # Filter empty lines + (filter (l: l != "")) + ]; + + # Parse lines into a structure + parseLines = lines: (foldl' + (acc: l: + let + m = match "([^ )]*) *(.*)" l; + directive = elemAt m 0; + rest = elemAt m 1; + + # Maintain parser state (inside parens or not) + inDirective = + if rest == "(" then directive + else if rest == ")" then null + else acc.inDirective + ; + + in + { + data = acc.data // ( + if directive == "" && rest == ")" then { } + else if inDirective != null && rest == "(" then { + ${inDirective} = { }; + } else if inDirective != null then { + ${inDirective} = acc.data.${inDirective} // { ${directive} = rest; }; + } else { + ${directive} = rest; + } + ); + inherit inDirective; + }) + { + inDirective = null; + data = { }; + } + lines + ).data; + + normaliseDirectives = data: ( + let + normaliseString = s: + let + m = builtins.match "([^ ]+) (.+)" s; + in + { + ${elemAt m 0} = elemAt m 1; + }; + require = data.require or { }; + replace = data.replace or { }; + exclude = data.exclude or { }; + in + data // { + require = + if typeOf require == "string" then normaliseString require + else require; + replace = + if typeOf replace == "string" then normaliseString replace + else replace; + } + ); + + parseVersion = ver: + let + m = elemAt (match "([^-]+)-?([^-]*)-?([^-]*)" ver); + v = elemAt (match "([^+]+)\\+?(.*)" (m 0)); + in + { + version = v 0; + versionSuffix = v 1; + date = m 1; + rev = m 2; + }; + + parseReplace = data: ( + data // { + replace = + mapAttrs + (n: v: + let + m = match "=> (.+?) (.+)" v; + m2 = match "=> (.*+)" v; + in + if m != null then { + goPackagePath = elemAt m 0; + version = parseVersion (elemAt m 1); + } else { + path = elemAt m2 0; + }) + data.replace; + } + ); + + parseRequire = data: ( + data // { + require = mapAttrs (n: v: parseVersion v) data.require; + } + ); + + splitString = sep: s: filter (t: t != [ ]) (split sep s); + +in +contents: +foldl' (acc: f: f acc) (splitString "\n" contents) [ + stripLines + parseLines + normaliseDirectives + parseReplace + parseRequire +]