Skip to content

Commit

Permalink
feat: add kotlin scaffolding (#500)
Browse files Browse the repository at this point in the history
eg.

```
ftl init kotlin examples/foo
```
  • Loading branch information
alecthomas authored Oct 18, 2023
1 parent 5a60f47 commit da80b89
Show file tree
Hide file tree
Showing 44 changed files with 411 additions and 11 deletions.
8 changes: 6 additions & 2 deletions Bitfile
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,22 @@ NODE_MODULES_IN = console/client/package{,-lock}.json
%{RELEASE}/ftl-runner: %{RELEASE} %{GO_SOURCES} cmd/ftl-runner/**/*.go
build: go build -o %{OUT} -tags release -ldflags "-X main.version=%{VERSION} -X main.timestamp=$(date +%s)" ./cmd/ftl-runner

%{RELEASE}/ftl: %{RELEASE} %{GO_SOURCES} cmd/ftl/**/*.go go-runtime/scaffolding.zip
%{RELEASE}/ftl: %{RELEASE} %{GO_SOURCES} cmd/ftl/**/*.go {go,kotlin}-runtime/scaffolding.zip
build: go build -o %{OUT} -tags release -ldflags "-X main.version=%{VERSION} -X main.timestamp=$(date +%s)" ./cmd/ftl

%{RELEASE}/ftl-initdb: %{RELEASE} %{GO_SOURCES} cmd/ftl-initdb/**/*.go
build: go build -o %{OUT} -tags release -ldflags "-X main.version=%{VERSION} -X main.timestamp=$(date +%s)" ./cmd/ftl-initdb

# Release builds include zipped scaffolding becaused raw go:embed doesn't
# preserve permissions or symlinks. Irritating.
go-runtime/scaffolding.zip: go-runtime/scaffolding/*
go-runtime/scaffolding.zip: go-runtime/scaffolding/**/*
cd go-runtime/scaffolding
build: zip -q --symlinks -r ../scaffolding.zip .

kotlin-runtime/scaffolding.zip: kotlin-runtime/scaffolding/**/*
cd kotlin-runtime/scaffolding
build: zip -q --symlinks -r ../scaffolding.zip .

%{SCHEMA_OUT}: %{SCHEMA_IN}
build:
ftl-schema > %{OUT}
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ build/release/%: console/client/dist/index.html

build/release/ftl:
cd go-runtime/scaffolding && zip -q --symlinks -r ../scaffolding.zip .
cd kotlin-runtime/scaffolding && zip -q --symlinks -r ../scaffolding.zip .
go build -o $@ -tags release -ldflags "-X main.version=$(VERSION) -X main.timestamp=$(shell date +%s)" ./cmd/ftl

$(KT_MVN_OUT): $(KT_RUNTIME_IN)
Expand Down
23 changes: 18 additions & 5 deletions cmd/ftl/cmd_init.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/TBD54566975/ftl/backend/common/log"
goruntime "github.com/TBD54566975/ftl/go-runtime"
"github.com/TBD54566975/ftl/internal"
kotlinruntime "github.com/TBD54566975/ftl/kotlin-runtime"
)

type initCmd struct {
Expand All @@ -21,8 +22,8 @@ type initCmd struct {

type initGoCmd struct {
Dir string `arg:"" default:"." type:"dir" help:"Directory to initialize the module in."`
Name string `help:"Name of the FTL module (defaults to name of directory)."`
GoModule string `required:"" help:"Go module path."`
Name string `short:"n" help:"Name of the FTL module (defaults to name of directory)."`
GoModule string `short:"G" required:"" help:"Go module import path."`
}

func (i initGoCmd) Run(ctx context.Context, parent *initCmd) error {
Expand All @@ -44,9 +45,21 @@ func (i initGoCmd) Run(ctx context.Context, parent *initCmd) error {
}

type initKotlinCmd struct {
Dir string `arg:"" default:"." help:"Directory to initialize the module in."`
Dir string `arg:"" default:"." help:"Directory to initialize the module in."`
Name string `short:"n" help:"Name of the FTL module (defaults to name of directory)."`
}

func (i *initKotlinCmd) Run() error {
panic("??")
func (i *initKotlinCmd) Run(parent *initCmd) error {
if i.Name == "" {
i.Name = filepath.Base(i.Dir)
}
if err := internal.Scaffold(kotlinruntime.Files, i.Dir, i); err != nil {
return errors.WithStack(err)
}
if !parent.Hermit {
if err := os.RemoveAll(filepath.Join(i.Dir, "bin")); err != nil {
return errors.WithStack(err)
}
}
return nil
}
31 changes: 27 additions & 4 deletions internal/scaffolder.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,25 @@ func Scaffold(source *zip.Reader, destination string, ctx any) error {
if err != nil {
return errors.WithStack(err)
}
return errors.WithStack(filepath.WalkDir(destination, func(path string, d fs.DirEntry, err error) error {
return errors.WithStack(walkDir(destination, func(path string, d fs.DirEntry) error {
if err != nil {
return err
return errors.WithStack(err)
}
info, err := d.Info()
if err != nil {
return errors.WithStack(err)
}

// Evaluate path name templates.
newName, err := evaluate(path, ctx)
dir := filepath.Dir(path)
base := filepath.Base(path)
newName, err := evaluate(base, ctx)
if err != nil {
return errors.Wrapf(err, "%s", path)
}
// Rename if necessary.
if newName != path {
if newName != base {
newName = filepath.Join(dir, newName)
err = os.Rename(path, newName)
if err != nil {
return errors.Wrap(err, "failed to rename file")
Expand Down Expand Up @@ -68,6 +71,26 @@ func Scaffold(source *zip.Reader, destination string, ctx any) error {
}))
}

func walkDir(dir string, fn func(path string, d fs.DirEntry) error) error {
entries, err := os.ReadDir(dir)
if err != nil {
return errors.WithStack(err)
}
for _, entry := range entries {
if entry.IsDir() {
err = walkDir(filepath.Join(dir, entry.Name()), fn)
if err != nil {
return errors.WithStack(err)
}
}
err = fn(filepath.Join(dir, entry.Name()), entry)
if err != nil {
return errors.WithStack(err)
}
}
return nil
}

func evaluate(tmpl string, ctx any) (string, error) {
t, err := template.New("scaffolding").Funcs(
template.FuncMap{
Expand Down
47 changes: 47 additions & 0 deletions kotlin-runtime/devel.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//go:build !release

package kotlinruntime

import (
"archive/zip"
"os"
"os/exec"
"path/filepath"
"strings"

"github.com/TBD54566975/ftl/internal"
)

// Files is the FTL Kotlin runtime scaffolding files.
var Files = func() *zip.Reader {
cmd := exec.Command("git", "rev-parse", "--show-toplevel")
out, err := cmd.CombinedOutput()
if err != nil {
panic(err)
}
dir := filepath.Join(strings.TrimSpace(string(out)), "kotlin-runtime", "scaffolding")
w, err := os.CreateTemp("", "")
if err != nil {
panic(err)
}
defer os.Remove(w.Name()) // This is okay because the zip.Reader will keep it open.
if err != nil {
panic(err)
}

err = internal.ZipDir(dir, w.Name())
if err != nil {
panic(err)
}

info, err := w.Stat()
if err != nil {
panic(err)
}
_, _ = w.Seek(0, 0)
zr, err := zip.NewReader(w, info.Size())
if err != nil {
panic(err)
}
return zr
}()
24 changes: 24 additions & 0 deletions kotlin-runtime/release.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//go:build release

package goruntime

import (
"archive/zip"
"bytes"
_ "embed"
)

//go:embed scaffolding.zip
var archive []byte

// Files is the FTL Kotlin runtime scaffolding files.
//
// scaffolding.zip can be generated by running `bit kotlin-runtime/scaffolding.zip`
// or indirectly via `bit build/release/ftl`.
var Files = func() *zip.Reader {
zr, err := zip.NewReader(bytes.NewReader(archive), int64(len(archive)))
if err != nil {
panic(err)
}
return zr
}()
1 change: 1 addition & 0 deletions kotlin-runtime/scaffolding/bin/.maven-3.8.6.pkg
1 change: 1 addition & 0 deletions kotlin-runtime/scaffolding/bin/.openjdk-17.0.8_7.pkg
7 changes: 7 additions & 0 deletions kotlin-runtime/scaffolding/bin/README.hermit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Hermit environment

This is a [Hermit](https://github.com/cashapp/hermit) bin directory.

The symlinks in this directory are managed by Hermit and will automatically
download and install Hermit itself as well as packages. These packages are
local to this environment.
21 changes: 21 additions & 0 deletions kotlin-runtime/scaffolding/bin/activate-hermit
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/bin/bash
# This file must be used with "source bin/activate-hermit" from bash or zsh.
# You cannot run it directly
#
# THIS FILE IS GENERATED; DO NOT MODIFY

if [ "${BASH_SOURCE-}" = "$0" ]; then
echo "You must source this script: \$ source $0" >&2
exit 33
fi

BIN_DIR="$(dirname "${BASH_SOURCE[0]:-${(%):-%x}}")"
if "${BIN_DIR}/hermit" noop > /dev/null; then
eval "$("${BIN_DIR}/hermit" activate "${BIN_DIR}/..")"

if [ -n "${BASH-}" ] || [ -n "${ZSH_VERSION-}" ]; then
hash -r 2>/dev/null
fi

echo "Hermit environment $("${HERMIT_ENV}"/bin/hermit env HERMIT_ENV) activated"
fi
43 changes: 43 additions & 0 deletions kotlin-runtime/scaffolding/bin/hermit
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/bin/bash
#
# THIS FILE IS GENERATED; DO NOT MODIFY

set -eo pipefail

export HERMIT_USER_HOME=~

if [ -z "${HERMIT_STATE_DIR}" ]; then
case "$(uname -s)" in
Darwin)
export HERMIT_STATE_DIR="${HERMIT_USER_HOME}/Library/Caches/hermit"
;;
Linux)
export HERMIT_STATE_DIR="${XDG_CACHE_HOME:-${HERMIT_USER_HOME}/.cache}/hermit"
;;
esac
fi

export HERMIT_DIST_URL="${HERMIT_DIST_URL:-https://github.com/cashapp/hermit/releases/download/stable}"
HERMIT_CHANNEL="$(basename "${HERMIT_DIST_URL}")"
export HERMIT_CHANNEL
export HERMIT_EXE=${HERMIT_EXE:-${HERMIT_STATE_DIR}/pkg/hermit@${HERMIT_CHANNEL}/hermit}

if [ ! -x "${HERMIT_EXE}" ]; then
echo "Bootstrapping ${HERMIT_EXE} from ${HERMIT_DIST_URL}" 1>&2
INSTALL_SCRIPT="$(mktemp)"
# This value must match that of the install script
INSTALL_SCRIPT_SHA256="180e997dd837f839a3072a5e2f558619b6d12555cd5452d3ab19d87720704e38"
if [ "${INSTALL_SCRIPT_SHA256}" = "BYPASS" ]; then
curl -fsSL "${HERMIT_DIST_URL}/install.sh" -o "${INSTALL_SCRIPT}"
else
# Install script is versioned by its sha256sum value
curl -fsSL "${HERMIT_DIST_URL}/install-${INSTALL_SCRIPT_SHA256}.sh" -o "${INSTALL_SCRIPT}"
# Verify install script's sha256sum
openssl dgst -sha256 "${INSTALL_SCRIPT}" | \
awk -v EXPECTED="$INSTALL_SCRIPT_SHA256" \
'$2!=EXPECTED {print "Install script sha256 " $2 " does not match " EXPECTED; exit 1}'
fi
/bin/bash "${INSTALL_SCRIPT}" 1>&2
fi

exec "${HERMIT_EXE}" --level=fatal exec "$0" -- "$@"
Empty file.
1 change: 1 addition & 0 deletions kotlin-runtime/scaffolding/bin/jar
1 change: 1 addition & 0 deletions kotlin-runtime/scaffolding/bin/jarsigner
1 change: 1 addition & 0 deletions kotlin-runtime/scaffolding/bin/java
1 change: 1 addition & 0 deletions kotlin-runtime/scaffolding/bin/javac
1 change: 1 addition & 0 deletions kotlin-runtime/scaffolding/bin/javadoc
1 change: 1 addition & 0 deletions kotlin-runtime/scaffolding/bin/javap
1 change: 1 addition & 0 deletions kotlin-runtime/scaffolding/bin/jcmd
1 change: 1 addition & 0 deletions kotlin-runtime/scaffolding/bin/jconsole
1 change: 1 addition & 0 deletions kotlin-runtime/scaffolding/bin/jdb
1 change: 1 addition & 0 deletions kotlin-runtime/scaffolding/bin/jdeprscan
1 change: 1 addition & 0 deletions kotlin-runtime/scaffolding/bin/jdeps
1 change: 1 addition & 0 deletions kotlin-runtime/scaffolding/bin/jfr
1 change: 1 addition & 0 deletions kotlin-runtime/scaffolding/bin/jhsdb
1 change: 1 addition & 0 deletions kotlin-runtime/scaffolding/bin/jimage
1 change: 1 addition & 0 deletions kotlin-runtime/scaffolding/bin/jinfo
1 change: 1 addition & 0 deletions kotlin-runtime/scaffolding/bin/jlink
1 change: 1 addition & 0 deletions kotlin-runtime/scaffolding/bin/jmap
1 change: 1 addition & 0 deletions kotlin-runtime/scaffolding/bin/jmod
1 change: 1 addition & 0 deletions kotlin-runtime/scaffolding/bin/jpackage
1 change: 1 addition & 0 deletions kotlin-runtime/scaffolding/bin/jps
1 change: 1 addition & 0 deletions kotlin-runtime/scaffolding/bin/jrunscript
1 change: 1 addition & 0 deletions kotlin-runtime/scaffolding/bin/jshell
1 change: 1 addition & 0 deletions kotlin-runtime/scaffolding/bin/jstack
1 change: 1 addition & 0 deletions kotlin-runtime/scaffolding/bin/jstat
1 change: 1 addition & 0 deletions kotlin-runtime/scaffolding/bin/jstatd
1 change: 1 addition & 0 deletions kotlin-runtime/scaffolding/bin/keytool
1 change: 1 addition & 0 deletions kotlin-runtime/scaffolding/bin/mvn
1 change: 1 addition & 0 deletions kotlin-runtime/scaffolding/bin/rmiregistry
1 change: 1 addition & 0 deletions kotlin-runtime/scaffolding/bin/serialver
8 changes: 8 additions & 0 deletions kotlin-runtime/scaffolding/ftl.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module = "{{ .Name | lower }}"
language = "kotlin"
deploy = [
"target/main",
"target/classes",
"target/dependency",
"target/classpath.txt",
]
Loading

0 comments on commit da80b89

Please sign in to comment.