Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactoring Extensions #120

Closed
wants to merge 37 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
219edf9
Initial extension generator code
jimmyaxod Sep 11, 2023
7a2cd0c
First version with guest/host generated in cli working
jimmyaxod Sep 11, 2023
679dd4a
Couple changes
jimmyaxod Sep 14, 2023
e3772cb
First ver compile golang guest
jimmyaxod Sep 14, 2023
301d1ee
Updated to first version working guest+host in go
jimmyaxod Sep 15, 2023
324c26c
Extensions now get reset at start of a run
jimmyaxod Sep 18, 2023
f45bf0a
Updated golang ext generator test to prevent regression
jimmyaxod Sep 19, 2023
ee5a5be
Updated to use scale-extension-interfaces
jimmyaxod Sep 20, 2023
3844e39
Updated extension host go.mod to include correct ver of scale-extensi…
jimmyaxod Sep 20, 2023
1a5849b
Started on rust extension impl
jimmyaxod Sep 21, 2023
45238a4
Fixup from merge error
jimmyaxod Sep 21, 2023
b5b7cf4
Fixup for golang extension
jimmyaxod Sep 22, 2023
30f1d63
Initial rust generator for types from signature to extension
jimmyaxod Sep 22, 2023
4cd65b5
Updated ext generator for rust
jimmyaxod Sep 22, 2023
e8d3baa
Fixed issue with accessors missing
jimmyaxod Sep 25, 2023
95ce3f2
Updated for rust compile
jimmyaxod Sep 25, 2023
3b90c5e
Latest updates
jimmyaxod Sep 28, 2023
2af5b75
Latest rust guest ext working
jimmyaxod Sep 28, 2023
db13195
Added error handling in go ext
jimmyaxod Sep 29, 2023
1e9a64a
Updated rust ext to deal with errors correctly
jimmyaxod Sep 29, 2023
263f845
Updated to use extension hash rather than name in wasm fns
jimmyaxod Oct 3, 2023
86da9c4
Fixing for tests
ShivanshVij Oct 11, 2023
8b9f310
Fixing for tests
ShivanshVij Oct 11, 2023
ddbf96a
Renaming extension info temporarily
ShivanshVij Oct 11, 2023
2071823
Temporary changes
ShivanshVij Nov 7, 2023
c454784
Temporary changes
ShivanshVij Nov 7, 2023
b7b0995
Temporary changes
ShivanshVij Nov 7, 2023
f2d2a27
Temporary changes
ShivanshVij Nov 7, 2023
6300d84
Fixing bugs
ShivanshVij Nov 7, 2023
f680d76
Working golang build with extension support
ShivanshVij Nov 7, 2023
d1d80ee
End to end compilation
ShivanshVij Nov 8, 2023
bc0e713
Pushing fixes
ShivanshVij Nov 14, 2023
ce5cc2e
Fixing lint
ShivanshVij Nov 14, 2023
f45ccb4
Installing deps for integraiton test
ShivanshVij Nov 14, 2023
dfca8aa
Finishing integration
ShivanshVij Nov 20, 2023
221628f
Fixing linter
ShivanshVij Nov 20, 2023
59271bb
Fixing typescript linter
ShivanshVij Nov 20, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 95 additions & 37 deletions build/golang.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ import (
"os/exec"
"path"
"path/filepath"
"strings"

"github.com/loopholelabs/scale/extension"

"github.com/loopholelabs/scale/compile/golang"
"github.com/loopholelabs/scale/scalefile"
Expand All @@ -36,21 +39,34 @@ import (
var (
ErrNoGo = errors.New("go not found in PATH. Please install go: https://golang.org/doc/install")
ErrNoTinyGo = errors.New("tinygo not found in PATH. Please install tinygo: https://tinygo.org/getting-started/")

ErrNoSignatureDependencyGoMod = errors.New("signature dependency not found in go.mod file")
ErrInvalidSignatureDependencyGoMod = errors.New("unable to parse signature dependency in go.mod file")
)

type LocalGolangOptions struct {
// Output is the output writer for the various build commands
Output io.Writer
// Stdout is the output writer for the various build commands
Stdout io.Writer

// Scalefile is the scalefile to be built
Scalefile *scalefile.Schema

// SourceDirectory is the directory where the source code is located
SourceDirectory string

// SignatureSchema is the schema of the signature
//
// Note: The SignatureSchema is only used to embed type information into the scale function,
// and not as part of the build process
SignatureSchema *signature.Schema

// ExtensionSchemas are the schemas of the extensions. The array must be in the same order as the extensions
// are defined in the scalefile
//
// Note: The ExtensionSchemas are only used to embed extension information into the scale function,
// and not as part of the build process
ExtensionSchemas []*extension.Schema

// SourceDirectory is the directory where the source code is located
SourceDirectory string

// Storage is the storage handler to use for the build
Storage *storage.BuildStorage

Expand All @@ -70,7 +86,7 @@ type LocalGolangOptions struct {
Args []string
}

func LocalGolang(options *LocalGolangOptions) (*scalefunc.Schema, error) {
func LocalGolang(options *LocalGolangOptions) (*scalefunc.V1BetaSchema, error) {
var err error
if options.GoBin != "" {
stat, err := os.Stat(options.GoBin)
Expand Down Expand Up @@ -102,6 +118,10 @@ func LocalGolang(options *LocalGolangOptions) (*scalefunc.Schema, error) {
}
}

if len(options.ExtensionSchemas) != len(options.Scalefile.Extensions) {
return nil, fmt.Errorf("number of extension schemas does not match number of extensions in scalefile")
}

if !filepath.IsAbs(options.SourceDirectory) {
options.SourceDirectory, err = filepath.Abs(options.SourceDirectory)
if err != nil {
Expand All @@ -125,29 +145,45 @@ func LocalGolang(options *LocalGolangOptions) (*scalefunc.Schema, error) {
}

if !manifest.HasRequire("signature", "v0.1.0", true) {
return nil, fmt.Errorf("signature dependency not found in go.mod")
return nil, ErrNoSignatureDependencyGoMod
}

if !manifest.HasReplacement("signature", "", "", "", true) {
return nil, fmt.Errorf("signature dependency not found in go.mod")
return nil, ErrNoSignatureDependencyGoMod
}

signatureDependencyVersion, signatureDependencyPath := manifest.GetReplacement("signature")
if signatureDependencyVersion != "" && signatureDependencyPath != "" {
return nil, fmt.Errorf("unable to parse signature dependency in go.mod")
}
signatureInfo := new(golang.SignatureInfo)

if (signatureDependencyVersion != "" && options.Scalefile.Signature.Organization == "local") || (signatureDependencyVersion == "" && options.Scalefile.Signature.Organization != "local") {
return nil, fmt.Errorf("scalefile's signature block does not match go.mod")
signatureInfo.ImportVersion, signatureInfo.ImportPath = manifest.GetReplacement("signature")
if signatureInfo.ImportVersion != "" && signatureInfo.ImportPath != "" {
return nil, ErrInvalidSignatureDependencyGoMod
}

if signatureDependencyVersion == "" && !filepath.IsAbs(signatureDependencyPath) {
signatureDependencyPath, err = filepath.Abs(path.Join(options.SourceDirectory, signatureDependencyPath))
if err != nil {
return nil, fmt.Errorf("unable to parse signature dependency path: %w", err)
switch options.Scalefile.Signature.Organization {
case "local":
signatureInfo.Local = true
if signatureInfo.ImportVersion != "" {
return nil, fmt.Errorf("scalefile's signature block does not match go.mod: signature import version is %s for a local signature", signatureInfo.ImportVersion)
}

if !filepath.IsAbs(signatureInfo.ImportPath) {
signatureInfo.ImportPath, err = filepath.Abs(path.Join(options.SourceDirectory, signatureInfo.ImportPath))
if err != nil {
return nil, fmt.Errorf("unable to parse signature dependency path: %w", err)
}
}
default:
signatureInfo.Local = false
if signatureInfo.ImportVersion == "" {
return nil, fmt.Errorf("scalefile's signature block does not match go.mod: signature import version is empty for a signature with organization %s", options.Scalefile.Signature.Organization)
}
}

functionInfo := &golang.FunctionInfo{
PackageName: strings.ToLower(options.Scalefile.Name),
ImportPath: options.SourceDirectory,
}

build, err := options.Storage.Mkdir()
if err != nil {
return nil, fmt.Errorf("unable to create build directory: %w", err)
Expand All @@ -156,12 +192,12 @@ func LocalGolang(options *LocalGolangOptions) (*scalefunc.Schema, error) {
_ = options.Storage.Delete(build)
}()

modfile, err := golang.GenerateGoModfile(options.Scalefile, signatureDependencyPath, signatureDependencyVersion, options.SourceDirectory)
modfile, err := golang.GenerateGoModfile(signatureInfo, functionInfo)
if err != nil {
return nil, fmt.Errorf("unable to generate go.mod file: %w", err)
}

mainFile, err := golang.GenerateGoMain(options.Scalefile, options.SignatureSchema)
mainFile, err := golang.GenerateGoMain(options.Scalefile, options.SignatureSchema, functionInfo)
if err != nil {
return nil, fmt.Errorf("unable to generate main.go file: %w", err)
}
Expand All @@ -185,8 +221,8 @@ func LocalGolang(options *LocalGolangOptions) (*scalefunc.Schema, error) {

cmd := exec.Command(options.GoBin, "mod", "tidy")
cmd.Dir = compilePath
cmd.Stdout = options.Output
cmd.Stderr = options.Output
cmd.Stdout = options.Stdout
cmd.Stderr = options.Stdout
cmd.Env = os.Environ()
err = cmd.Run()
if err != nil {
Expand All @@ -203,16 +239,16 @@ func LocalGolang(options *LocalGolangOptions) (*scalefunc.Schema, error) {
return nil, fmt.Errorf("unknown build target %d", options.Target)
}

buildArgs := append([]string{"build", "-o", "scale.wasm"}, options.Args...)
buildArgs := append([]string{"build", "-o", "scale.wasm", "-gc=conservative", "-opt=s"}, options.Args...)
if options.Release {
buildArgs = append(buildArgs, "-no-debug")
}
buildArgs = append(buildArgs, "-target", target, "main.go")

cmd = exec.Command(options.TinyGoBin, buildArgs...)
cmd.Dir = compilePath
cmd.Stdout = options.Output
cmd.Stderr = options.Output
cmd.Stdout = options.Stdout
cmd.Stderr = options.Stdout
cmd.Env = os.Environ()
err = cmd.Run()
if err != nil {
Expand All @@ -224,21 +260,43 @@ func LocalGolang(options *LocalGolangOptions) (*scalefunc.Schema, error) {
return nil, fmt.Errorf("unable to read compiled wasm file: %w", err)
}

hash, err := options.SignatureSchema.Hash()
signatureHash, err := options.SignatureSchema.Hash()
if err != nil {
return nil, fmt.Errorf("unable to hash signature: %w", err)
}

return &scalefunc.Schema{
Version: scalefunc.V1Alpha,
Name: options.Scalefile.Name,
Tag: options.Scalefile.Tag,
SignatureName: fmt.Sprintf("%s/%s:%s", options.Scalefile.Signature.Organization, options.Scalefile.Signature.Name, options.Scalefile.Signature.Tag),
SignatureSchema: options.SignatureSchema,
SignatureHash: hex.EncodeToString(hash),
Language: scalefunc.Go,
Stateless: options.Scalefile.Stateless,
Dependencies: nil,
Function: data,
sig := scalefunc.V1BetaSignature{
Name: options.Scalefile.Signature.Name,
Organization: options.Scalefile.Signature.Organization,
Tag: options.Scalefile.Signature.Tag,
Schema: options.SignatureSchema,
Hash: hex.EncodeToString(signatureHash),
}

exts := make([]scalefunc.V1BetaExtension, len(options.Scalefile.Extensions))
for i, ext := range options.Scalefile.Extensions {
extensionHash, err := options.ExtensionSchemas[i].Hash()
if err != nil {
return nil, fmt.Errorf("unable to hash extension %s: %w", ext.Name, err)
}

exts[i] = scalefunc.V1BetaExtension{
Name: ext.Name,
Organization: ext.Organization,
Tag: ext.Tag,
Schema: options.ExtensionSchemas[i],
Hash: hex.EncodeToString(extensionHash),
}
}

return &scalefunc.V1BetaSchema{
Name: options.Scalefile.Name,
Tag: options.Scalefile.Tag,
Signature: sig,
Extensions: exts,
Language: scalefunc.Go,
Manifest: modfileData,
Stateless: options.Scalefile.Stateless,
Function: data,
}, nil
}
Loading