alexflint / gallium

Build desktop applications in Go and HTML.
MIT License
3.67k stars 135 forks source link

Build a `gallium` tool to build and bundle projects #15

Open ernesto-jimenez opened 8 years ago

ernesto-jimenez commented 8 years ago

The project has a couple of important requirements:

  1. Running runtime.LockOSThread and gallium.Loop on the main function
  2. Bundling the binary for certain features to work. e.g: the menu example only works when run from a bundle.

Instead of having people create their own main files, it might be helpful to have a CLI to manage both compilation and bundling.

The UX would be something like this:

github.com/alexflint/gallium/examples/menu/menu.go

package menu

import (
    "os"

    "github.com/alexflint/gallium"
)

type app struct {}

func New() gallium.Starter {
    return &app{}
}

func (*app) Start(app *gallium.App) {
    app.SetMenu([]gallium.Menu{
        {
            Title: "menudemo",
            Entries: []gallium.MenuEntry{
                gallium.MenuItem{
                    Title:    "Quit",
                    Shortcut: "cmd+q",
                    OnClick:  func() { os.Exit(0) },
                },
            },
        }
    })
}

Building the application

$ gallium build github.com/alexflint/gallium/examples/menu

As part of the build, it will create a temporary main package with a main.go file from a template similar to this:

// AUTOGENERATED CODE
// NEVER EDIT MANUALLY
package main

import (
    "runtime"

    "github.com/alexflint/gallium"
    p "{{ .TargetPackage }}"
)

func main() {
    runtime.LockOSThread()
    app := p.New()
    gallium.Loop(os.Args, app.Start())
}

Once generated, it will build the binary and bundle it and produce a ready menu.app.

Beyond not having to deal with thread locking and initialising the loop, we can check whether the returned satisfies other gallium interfaces to build more elaborate main files.

The CLI could also evolve to offer other features:

$ gallium new           # creates a default app ready to be used with `gallium build`
$ gallium generate menu # generates all the boilerplate to return a new menu
$ gallium sign Menu.app # signs Menu.app for distribution

It should also have a command to generate all the Go code needed to build and bundle the app with within the same repo. Everything that would be required to build the app without the cli, so it can be committed to the repo.

alexflint commented 8 years ago

I like this proposal a lot. I think a unified gallium tool makes a lot of sense, and I think over time we will have a number of different gallium-specific tasks as you point out, and I agree it will be much better to have a unified tool with a unified interface rather than a collection of different tools. I'm not 100% convinced about code generation, though. I worry that if we encourage users to check the generated code into their vcs then it will be difficult to make API changes since we'll have to somehow update generated code to talk to the new APIs, and I worry that if we encourage users not to check the generated code into their vcs then external tools, like ssa and so forth won't work.

I wonder if the solution is to just have what you described as gallium new - ie a boilerplate generator - and then let users modify and manage the code on their own from there. There could be gallium new --menus to generate boilerplate with a menu system, gallium new --statusbar to generate boilerplate with a status bar icon, and so forth. Users who wanted to structure their code in a different way could then simply work from the docs and write everything themselves.

Thanks for writing this up! Are you thinking of working on this?

ernesto-jimenez commented 8 years ago

@alexflint great!

I'm definitely up for working on this :)

I'm not 100% convinced about code generation, though. I worry that if we encourage users to check the generated code into their vcs then it will be difficult to make API changes since we'll have to somehow update generated code to talk to the new APIs,

Checking-in generated code would actually be better for API changes than asking users to code the main package themselves.

We would generate the code with a big comment saying they must never edit it by hand.

The idea is to allow them to build and bundle the app regardless of whether they have the gallium app installed or with version they have installed. If they wanted to upgrade gallium, they would need to re-generate the code.

and I worry that if we encourage users not to check the generated code into their vcs then external tools, like ssa and so forth won't work.

My worry about keeping the generated code outside of the repo is reproducible builds. I would like somebody to checkout my gallium-based app and be able to build it regardless of whether they have the gallium app installed.

I wonder if the solution is to just have what you described as gallium new - ie a boilerplate generator - and then let users modify and manage the code on their own from there. There could be gallium new --menus to generate boilerplate with a menu system, gallium new --statusbar to generate boilerplate with a status bar icon, and so forth.

There's two different things here in my opinion:

Users who wanted to structure their code in a different way could then simply work from the docs and write everything themselves.

That would always be a possibility, yes.

joeblew99 commented 8 years ago

i disagree here.

I feel that codegen is for much later, and there is little point doing it until the actual code is working. First get the code working, where you write code against the common API.

Then if you do want to do codegen, dont reinvent the wheel. Use goa. https://github.com/goadesign/goa Its a proper DSL driven Code Generator. I have to say though for menus and other stuff, its just easier to write some code against an API.

ernesto-jimenez commented 8 years ago

@joeblew99 generating the main automatically would actually help while working out the APIs, since we can regenerate the templated main as gallium evolves instead of having people amend the main they wrote manually when they have to upgrade.

When talking about codegen here I'm referring to templated code, rather than DSLs. You would write code against an API rather than a DSL.

Regarding goa. It has a very different scope, since it is a DSL for microservices. I'm talking about something different here.

joeblew99 commented 8 years ago

@ernesto-jimenez thanks for the answer.

so can you point me to an example of where we DO or WOULD gen the main ?. I think i generalised in my answer, and now i want to see the exact context. Curious :)

joeblew99 commented 8 years ago

thanks @ernesto-jimenez