bazelbuckskylarkstarlark

How to properly load a Starlark script into another?


I'm trying to make a very simple implementation with Starlark:

greeting.bzl

def greet():
    print ("Hello World!")

test.bzl

load (":greeting.bzl", "greet")

greet()

And execute it with: java -jar Starlark_deploy.jar test.bzl

The result of the above call is:

file ':greeting.bzl' was not correctly loaded. Make sure the 'load' statement appears in the global scope in your file

My end goal is to have my own Starlark engine, which depends on Starlark_deploy.jar. I'll then leverage the existence of Bazel rules (such as htt_archive and http_file) and define my own Starlark superset.


I'm using Bazel java implementation for Starlark described in the official documentation. It was obtained by:

  1. Cloning Bazel repository
  2. Running bazel build //src/main/java/com/google/devtools/starlark:Starlark_deploy.jar
  3. The output of the command above is Starlark_deploy.jar

Solution

  • It's not documented because it's not a clean or stable API. Expect API changes in the future. To embed in another tool, the Go implementation is much more mature.

    That being said, if you want to experiment, you can:

    import com.google.devtools.build.lib.syntax.ParserInputSource;
    import com.google.devtools.build.lib.syntax.ParserInputSource;
    import com.google.devtools.build.lib.syntax.StringLiteral;
    import java.util.HashMap;
    import java.util.Map;
    
      public Environment newEnvironment(Map<String, Environment.Extension> imports) {
        return Environment.builder(mutability)
            .useDefaultSemantics()
            .setGlobals(Environment.DEFAULT_GLOBALS)
            .setEventHandler(PRINT_HANDLER)
            .setImportedExtensions(imports)
            .build();
      }
    
      public Environment execute(String path)
          throws InterruptedException, IOException, EvalException {
          String content = new String(Files.readAllBytes(Paths.get(path)), CHARSET);
          ParserInputSource input = ParserInputSource.create(content, PathFragment.EMPTY_FRAGMENT);
          BuildFileAST ast = BuildFileAST.parseSkylarkFileWithoutImports(input, PRINT_HANDLER);
          Map<String, Environment.Extension> imports = new HashMap<>();
          for (StringLiteral imp : ast.getRawImports()) {
            imports.put(
                imp.getValue(),
                new Environment.Extension(execute(imp.getValue())));
          }
          Environment env = newEnvironment(imports);
          ast.eval(env);
          return env;
      }
    

    The returns the environment after execution, so you can inspect the variables or functions that have been defined.

    In the example above, modules are loaded one by one in the for loop. You may do the evaluation in parallel, like Bazel does.

    As I said before, expect breaking changes in the API.