mirror of
https://github.com/tweag/gomod2nix.git
synced 2024-11-22 21:25:00 +00:00
Add support for local replace directives
This fixes relative path modules not being correctly vendored by parsing go.mod in Nix and vendoring them into the correct directory in the Nix sandbox. Ideally this should also come with a pluggable source filtering mechanism and/or maybe use any present gitignore files like poetry2nix does. To use this new feature you have to pass a new `pwd` param to `buildGoApplication` as such: ``` buildGoApplication { pname = "foo"; version = "bar"; pwd = ./.; src = lib.cleanSource ./.; } ``` Fixes #2
This commit is contained in:
parent
5dffe7b6ef
commit
442b4154e2
2 changed files with 156 additions and 0 deletions
|
@ -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
|
||||
''
|
||||
|
|
134
builder/parser.nix
Normal file
134
builder/parser.nix
Normal file
|
@ -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
|
||||
]
|
Loading…
Reference in a new issue