#2711 FEP 1: 'fan build' command line tool

andy Wed 12 Sep

Summary

The fan build tool is designed to simplify starting new Fantom projects, as well as handle boiler-plate and scaffolding-type code and directory generation.

Goals

  • Create a new command line tool to:
    • Stub a new PathEnv project directory and supporting files
    • Stub a new pod source tree and supporting files
  • Officially recommend new projects be created in their own PathEnv
  • Enhance docIntro chapter to utilize fan build in examples

Non-Goals

This proposal does not attempt to formalize the role and function of the fan.props file outside of creating a PathEnv.

Motivation

The default behavior of Fantom is to install compiled pods into the global lib/ folder. However most modern projects utilize PathEnv to organize their code separately. While the processes for accomplishing this are documented, this is not explicitly recommended, nor is there any comprehensive examples.

A lesser problem is there exists a fair amount of boilerplate to create both new PathEnv folders and creating new source trees for pods within these projects. This results in either copy-and-pasting files from other projects, and/or setting up IDE macros.

This proposal aims to solve both problems by introducing a new command line tool that is easy to understand and use for both beginner and expert users.

In addition, several new FEP's will depend on PathEnv configuration, so this is a foundational FEP for future Fantom enhancements.

Description

A new build::Main class will be created with three commands:

# create new PathEnv and pod source tree
fan build init <pod-name>

# create new PathEnv for a multi-pod project
fan build initenv <env-name>

# create a new pod source tree
fan build initpod <pod-name>

It was considered whether these commands should be created in a new top-level pod (such as init), which could support future functionality and possibly APIs for adding features downstream.

That remains a viable path, however, it is outside the scope of this FEP. This enhancement is focused on adding only the core needs to bootstrap new Fantom projects. By bundling these tools in the build pod we allow both new and third party tools space to create, enhance (and even deprecate) the standard init tools.

1. Command "init"

This is the default mechanism used to create new "projects".

Example use:

$ fan build init hello

Example output (created under current working dir):

hello/
├── fan.props
├── etc/
├── lib/
└── src/
    ├── fan/
    ├── test/
    └── build.fan

Several directories and files are created:

  1. fan.props: this is an empty file that triggers Fantom to recognize this folder as a new PathEnv
  2. Directory stubs for etc, lib, and src
  3. A bare minimum build.fan build script. See Command "initpod" for details on this file.

NOTE: No source files are created in this process.

2. Command "initenv"

This command is used to create a new PathEnv that will be composed of multiple source pods.

Example use:

$ fan build initenv hello

Example output (created under current working dir):

hello/
├── fan.props
├── etc/
├── lib/
└── src/
    └── build.fan

Several directories and files are created:

  1. fan.props: this is an empty file that triggers Fantom to recognize this folder as a new PathEnv
  2. Directory stubs for etc, lib, and src. No pod source trees are created. See Command "initpod" for details on how pods are added to a multi-pod project.
  3. A skeleton build::BuildGroup script used to build child pod source trees:
#! /usr/bin/env fan

using build

class Build : BuildGroup
{
  new make()
  {
    childrenScripts =
    [
      // child 'build.fan' scripts go here
    ]
  }
}

3. Command "initpod"

This command is used to create new pods under a multi-pod env.

Example use:

$ fan build initpod foo
Created src/foo pod source tree
Remember to add 'foo/build.fan' to 'src/build.fan'!

Example output (automatically placed under src/ regardless of current working directory):

foo/
├── fan/
├── test/
└── build.fan

To specify a different path, the <pod-name> argument may be specified as a path relative to src/:

$ fan build initpod backend/foo
Created src/backend/foo pod source tree
Remember to add 'backend/foo/build.fan' to 'src/build.fan'!

Example of build.fan:

#! /usr/bin/env fan

using build

class Build : build::BuildPod
{
  new make()
  {
    podName = "foo"
    summary = "Description of this pod"
    version = Version("1.0")
    // These values are optional, but recommended
    // See: http://fantom.org/doc/docLang/Pods#meta
    // meta = [
    //   "org.name":     "My Org",
    //   "org.uri":      "http://myorg.org/",
    //   "proj.name":    "My Project",
    //   "proj.uri":     "http://myproj.org/",
    //   "license.name": "Apache License 2.0",
    //   "vcs.name":     "Git",
    //   "vcs.uri":      "https://github.com/myorg/myproj"
    // ]
    depends = ["sys 1.0"]
    srcDirs = ['fan/']
    // resDirs  = [,]
    // javaDirs = [,]
    // jsDirs   = [,]
    // docApi   = false   // defaults to 'true'
    // docSrc   = true    // defaults to 'false'
  }
}

Note this command is only valid for multi-pod envs. Users should be notified when run incorrectly in a single-pod env:

$ fan build init example
$ fan build initpod foo
Error: current env is not a multi-pod env
Did you mean to use 'fan build initenv example'?

Enhance Documentation

Maintain the existing content of docIntro::HelloWorld but rework to add in fan build use:

$ fan build init hello

brian Wed 12 Sep

This all looks great.

However now that I re-read it, the separation b/w "env" and "pod" seems a little messy to me. Let's say you want to create a project that has a single pod and it starts off like this:

lib/
src/
  fan/
  test/

But if you later decide you need another pod in that project you are kind of screwed. You have to move that whole src directory into sub-directory.

So the question is this: is it really worth encouraging a non-future proof directory structure?

Maybe we can simplify the three commands into just one single "init" command that generates the top-level project structure if its not created yet, and then generates the pod dir.

For example we run this in a new directory

fan build init hello

That generates a directory structure like this:

hello/
  lib/
  etc/
  src/
    hello/
      build.fan
      fan/
      test/ 

Then if I run that command within a directory all setup:

fan build init anotherPod

hello/
  lib/
  etc/
  src/
    hello/
      build.fan
      fan/
      test/
    anotherPod/
      build.fan
      fan/
      test/

It almost seems like the setup of the environment is just a side-effect to take care of if not already. The main goal is to stub out a pod

andy Thu 13 Sep

If we go down that road -- how is the BuildGroup script handled?

Is one always generated under src -- or only when >1 pods are created?

SlimerDude Thu 13 Sep

But if you later decide you need another pod in that project ... you have to move that whole src directory into sub-directory.

$ mkdir hello
$ mv fan/      hello/fan/
$ mv build.fan hello/build.fan

If someone is computer savvy enough to understand, create, and write multi-pod Fantom projects, they're gonna be able to move 2 files.

If you're creating yet another pod in a multi-pod project then chances are, you're gonna cut and paste the build.fan and build dirs from an exisiting pod anyway as they already contain common pod meta and dependencies.

Advanced developers who grow tiresome of creating 100s of multi-pod projects can always write their own 3rd party init script (as proposed in the FEP - see Description) - because they can.

So instead, this should be aimed at hand holding new-comers with the simplest directory structure that takes the least amount of noggin power to understand - so they can clearly see what's what, where's where, and get coding!

andy Thu 13 Sep

An alternative take is that we keep a single init command -- and the first invocation is as specified originally.

But when its run a second time, we automatically move the src down on level, and add the BuildGroup scrip.

That collapses all three commands and uses cases into a single command.

brian Thu 13 Sep

Moving directories isn't just a file system operation, but also the associated repo move too.

This is how I am seeing the pros and cons to have a simple src/ directory structure as proposed by Steve:

Pros

  1. Save one directory in your file structure

Cons

  1. Inconsistent with more complex projects
  2. Not future proof if you decide to add a new pod
  3. Seems very useful to establish the rule that a pod is always sourced under a directory of its own name (not required today, but I've hacked various tools where that is really convenient)

I am not seeing how all the cons are out-weighed by saving one directory level?

andy Fri 14 Sep

OK -- issues as I see they stand:

  1. Do we want to standardize on always using src/{pod-name}/ structure?
  2. How to handle the root src/build.fan -- always create? or on demand?

I lean towards (1) yes, and (2) always create -- if nothing else than for consistency.

brian Fri 14 Sep

That is my vote too

  1. Standardize on src/{pod-name}
  2. Always create src/build.fan group and fan.props if it looks like a new environment

Only question is the top level project directory. For example:

cd /foo
fan build init hello

That could generate:

foo/
  hello/
    src/
      hello/

Or it could just generate everything under the current directory:

foo/
  src/
    hello/

I actually think the latter makes more sense and works with both project and pod creation. Then maybe add an option -dir option if you want to create/change from the current working directory

andy Wed 19 Sep

This feature is complete and now open in a new PR:

https://bitbucket.org/fantom/fan-1.0/pull-requests/10

Will give it a few days for review -- please let me know comments. If everyone gives the thumbs up, we'll merge into mainline next week.

I think its going to be really nice addition :)

andy Tue 2 Oct

Ticket promoted to #2711 and assigned to andy

andy Tue 2 Oct

Ticket resolved in 1.0.72

PR is now merged

Login or Signup to reply.