0

I have a question about the import functionality of JSONNet. What I would like is to be able to import one master libsonnet file, which itself is composed of multiple imports, and be able to access everything from that one import.

I have the following example structure:

.
├── library_one
│   └── init.libsonnet
├── library_two
│   └── init.libsonnet
├── init.libsonnet
└── test.jsonnet

With the following contents for each file:

library_one/init.libsonnet

{
  local LibraryOne = self,

  some_function(some_argument='default'):: [
    'echo "The argument was %s"' % some_argument,
  ],
}

library_two/init.libsonnet

{
  local LibraryTwo = self,

  some_other_function(some_other_argument='another_default'):: [
    'echo "The other argument was %s"' % some_other_argument,
  ],
}

And finally, the "master" file init.libsonnet at the root:

local library_one = import 'library_one/init.libsonnet';
local library_two = import 'library_two/init.libsonnet';

{}

However, when I run the file test.jsonnnet with the following contents:

local master = import 'init.libsonnet';

{
  some_key: master.library_one.some_function,
  some_other_key: master.library_two.some_other_function,
}

I get the error:

RUNTIME ERROR: field does not exist: library_one
    test.jsonnet:4:13-31    object <anonymous>
    During manifestation

Is this kind of inheritance not possible?

Scott Crooks
  • 1,523
  • 4
  • 24
  • 39

2 Answers2

3

local symbols are not exported as there's not "global" scope per-se, but rather fields of the {...} main object:

Modified source to use main object fields:

::::::::::::::
library_one/init.libsonnet
::::::::::::::
{
  local LibraryOne = self,

  some_function(some_argument='default'):: [
    'echo "The argument was %s"' % some_argument,
  ],
}
::::::::::::::
library_two/init.libsonnet
::::::::::::::
{
  local LibraryTwo = self,

  some_other_function(some_other_argument='another_default'):: [
    'echo "The other argument was %s"' % some_other_argument,
  ],
}
::::::::::::::
init.libsonnet
::::::::::::::
{
  library_one:: import 'library_one/init.libsonnet',
  library_two:: import 'library_two/init.libsonnet',
}
::::::::::::::
test.jsonnet
::::::::::::::
local master = import 'init.libsonnet';

{
  some_key:: master.library_one.some_function,
  some_other_key:: master.library_two.some_other_function,

  foo: $.some_key("bar")
}

Sample output:

{
   "foo": [
      "echo \"The argument was bar\""
   ]
}
jjo
  • 2,595
  • 1
  • 8
  • 16
3

Please take a look at @jjo's answer first.

I just wanted to add that it is possible and reasonable to structure your library, so that you have local "declarations" and then have it "exported" using an object, which I believe is similar to what you are describing.

library.libsonnet:

local sqr(x) = x * x;

local cube(x): x * x * x;

{
    sqr: sqr,
    cube: cube,
}

And then you can use like this:

local lib = import 'library.libsonnet';

[
    lib.sqr(2),
    lib.cube(3) + lib.sqr(4),
]

This style can also be quite good for performance. You can take a look at a real world example here: https://github.com/sbarzowski/jsonnet-modifiers/blob/master/modifiers.libsonnet.

And as for "master library", you can actually add the parts together in your init.libsonnet:

local library_one = import 'library_one/init.libsonnet';
local library_two = import 'library_two/init.libsonnet';

library_one + library_two

If library_one and library_two contain the same field, library_two will take precedence. You can read more about inheritance rules in Jsonnet on the official website: https://jsonnet.org/learning/tutorial.html#oo.

sbarzowski
  • 2,707
  • 1
  • 20
  • 25