blob: b14965b0a85d9c738cf28d27b864708c6e7ebfb8 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package fxicfg
import (
"context"
"io/ioutil"
"os"
"path/filepath"
"fuchsia.googlesource.com/infra/infra/fxicfg/builtins"
"fuchsia.googlesource.com/infra/infra/fxicfg/loaders"
"fuchsia.googlesource.com/infra/infra/fxicfg/state"
"go.chromium.org/luci/starlark/starlarkproto"
luci "go.chromium.org/luci/starlark/interpreter"
"go.starlark.net/starlark"
)
const (
// The default output directory, if one is not supplied.
defaultOutputDir = "generated"
)
// Generate executes LUCI's starlark interpreter with Fuchsia packages and globals.
//
// `loader` is the root luci.Loader that knows how to load all client starlark
// files. `main` is the file to execute first.
//
// The interpreter maintains a state.State object that client code modifies. The resulting
// state is returned iff execution completed without error. Otherwise, an empty State is
// returned.
func Generate(loader luci.Loader, main string) (state.State, starlark.StringDict, error) {
// load("@stdlib//..."): loads packages compiled into this go binary from starlark
// sources in fxicfg/starlark/stdlib/...
pkgs := loaders.EmbeddedLoader()
// load("@proto//..."): loads fuchsia-specific protobuf modules.
pkgs["proto"] = loaders.ProtoLoader()
// load("//..."): loads client starlark modules.
pkgs[luci.MainPkg] = loader
// Proto predeclared provides builtin LUCI textproto functionality. These are included
// in the global namespace of all starlark programs.
predecl := make(starlark.StringDict)
predecl["proto"] = starlarkproto.ProtoLib()["proto"]
for k, v := range builtins.All() {
predecl[k] = v
}
tm := state.ThreadModifier(*state.NewState())
interp := luci.Interpreter{
Predeclared: predecl,
Packages: pkgs,
PreExec: func(th *starlark.Thread, pkg, path string) {
tm.WriteTo(th)
},
PostExec: func(th *starlark.Thread, pkg, path string) {
tm.ReadFrom(th)
},
}
ctx := context.Background()
if err := interp.Init(ctx); err != nil {
return state.State{}, nil, err
}
globals, err := interp.ExecModule(ctx, luci.MainPkg, main)
if err != nil {
return state.State{}, nil, err
}
return tm.State(), globals, nil
}
func CommitState(s state.State) error {
dir := s.OutputDir
if dir == "" {
dir = defaultOutputDir
}
if err := os.MkdirAll(dir, 0755); err != nil {
return err
}
for k, v := range s.OutputFiles {
path := filepath.Join(dir, k)
if err := ioutil.WriteFile(path, v, 0644); err != nil {
return err
}
}
return nil
}