//
// Copyright (c) 2025, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
// 12 May 2025 Brian Frank Creation
//
using build
using compiler
using util
**
** Base class for transpiler commands
**
abstract class TranspileCmd : FancCmd
{
@Opt { help = "Output directory" }
File outDir := Env.cur.workDir + `gen/$name/`
@Arg { help = "Target pod(s)" }
Str[] podNames := [,]
//////////////////////////////////////////////////////////////////////////
// Run
//////////////////////////////////////////////////////////////////////////
** Call compilePod on every target
override final Int run()
{
init
return transpile
}
** Standard initializaton
virtual Void init()
{
// flatten depends
flattenPods
}
** Delete outDir and run compilePod on all pods
virtual Int transpile()
{
// always start fresh
outDir.delete
// generate code for every target pod
pods.each |pod| { compilePod(pod) }
return 0
}
//////////////////////////////////////////////////////////////////////////
// Pods
//////////////////////////////////////////////////////////////////////////
** Expand command line pod names to their full dependency chain.
** We require all podNames to be pre-compiled using normal Fantom compilation
Void flattenPods()
{
// resolve pod names to installed precompiled pods
Pod[] pods := podNames.map |name->Pod| { Pod.find(name) }
pods = Pod.flattenDepends(pods)
pods = Pod.orderByDepends(pods)
// map Pods to TranspilePod instances
pods.each |pod| { this.pods.add(transpilePod(pod)) }
}
** Load transpile pod
private TranspilePod transpilePod(Pod pod)
{
TranspilePod {
it.name = pod.name
it.version = pod.version
it.podFile = pod->loadFile
it.buildScript = buildScriptMap[pod.name] ?: throw Err("No build script found for pod: $pod.name")
}
}
** Map of pod name to build scripts for environment
once Str:File buildScriptMap()
{
// we rely on the convention that all pods are in a directory
// of their name and contain a fan/ source directory
acc := Str:File[:]
Env.cur.path.each |path|
{
path.plus(`src/`).walk |f|
{
if (f.name == "build.fan" && f.plus(`fan/`).exists)
{
podName := f.parent.name
if (acc[podName] == null) acc[podName] = f
}
}
}
return acc.toImmutable
}
//////////////////////////////////////////////////////////////////////////
// Compile
//////////////////////////////////////////////////////////////////////////
** Compile build script into AST
virtual Void compilePod(TranspilePod pod)
{
// info
info("\n## Transpile $this.name [$pod.name]")
// run only the front end
this.compiler = Compiler(stdCompilerInput(pod))
compiler.frontend
// transpile the pod
genPod(compiler.pod)
this.compiler = null
}
** Use the build script to generate the standard compiler input and then
** inovke the callback on it for additonal configuration.
virtual CompilerInput stdCompilerInput(TranspilePod pod, |CompilerInput|? f := null)
{
buildPod := Env.cur.compileScript(pod.buildScript).pod
buildType := buildPod.types.find |t| { t.fits(BuildPod#) }
buildScript := (BuildPod)buildType.make
input := buildScript.stdFanCompilerInput
input.version = pod.version
if (f != null) input.with(f)
return input
}
//////////////////////////////////////////////////////////////////////////
// Generation
//////////////////////////////////////////////////////////////////////////
** Generate pod which calls genType for each non-synthetic
virtual Void genPod(PodDef pod)
{
pod.typeDefs.each |type|
{
genType(type)
}
}
** Generate non-synthetic type
virtual Void genType(TypeDef type)
{
echo("genType $type")
}
//////////////////////////////////////////////////////////////////////////
// Fields
//////////////////////////////////////////////////////////////////////////
** Pod data with flattened dependency chain
TranspilePod[] pods := [,]
** Compiler for current pod
Compiler? compiler
}
**************************************************************************
** TranspilePod
**************************************************************************
**
** Pod data for every pod to comile
**
class TranspilePod
{
new make(|This| f) { f(this) }
const Str name // pod name
const Version version // version
const File podFile // precompiled "foo.pod" file
const File buildScript // "build.fan" file
override Str toStr() { "$name [$podFile.osPath]" }
}