mirror of
https://github.com/vsariola/sointu.git
synced 2025-07-18 13:04:25 -04:00
feat(cli): Re-engineer CLIs, split play & compile
Play depends on bridge and compile on compiler package. Before, the compiler depended on bridge, but we could not use the compiler to build the library, as the bridge depends on the library. Also, play can now start having slightly more options e.g. wav out etc.
This commit is contained in:
@ -12,70 +12,73 @@ import (
|
||||
"github.com/vsariola/sointu"
|
||||
)
|
||||
|
||||
//go:generate go run generate.go
|
||||
|
||||
type Compiler struct {
|
||||
Template *template.Template
|
||||
Amd64 bool
|
||||
OS string
|
||||
DisableSections bool
|
||||
Template *template.Template
|
||||
OS string
|
||||
Arch string
|
||||
}
|
||||
|
||||
// New returns a new compiler using the default .asm templates
|
||||
func New() (*Compiler, error) {
|
||||
func New(os string, arch string) (*Compiler, error) {
|
||||
_, myname, _, _ := runtime.Caller(0)
|
||||
templateDir := filepath.Join(path.Dir(myname), "..", "templates")
|
||||
compiler, err := NewFromTemplates(templateDir)
|
||||
compiler, err := NewFromTemplates(os, arch, templateDir)
|
||||
return compiler, err
|
||||
}
|
||||
|
||||
func NewFromTemplates(directory string) (*Compiler, error) {
|
||||
globPtrn := filepath.Join(directory, "*.*")
|
||||
func NewFromTemplates(os string, arch string, templateDirectory string) (*Compiler, error) {
|
||||
globPtrn := filepath.Join(templateDirectory, "*.*")
|
||||
tmpl, err := template.New("base").Funcs(sprig.TxtFuncMap()).ParseGlob(globPtrn)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`could not create template based on directory "%v": %v`, directory, err)
|
||||
return nil, fmt.Errorf(`could not create template based on directory "%v": %v`, templateDirectory, err)
|
||||
}
|
||||
return &Compiler{Template: tmpl, Amd64: runtime.GOARCH == "amd64", OS: runtime.GOOS}, nil
|
||||
}
|
||||
|
||||
func (com *Compiler) compile(templateName string, data interface{}) (string, error) {
|
||||
result := bytes.NewBufferString("")
|
||||
err := com.Template.ExecuteTemplate(result, templateName, data)
|
||||
return result.String(), err
|
||||
return &Compiler{Template: tmpl, OS: os, Arch: arch}, nil
|
||||
}
|
||||
|
||||
func (com *Compiler) Library() (map[string]string, error) {
|
||||
if com.Arch != "386" && com.Arch != "amd64" {
|
||||
return nil, fmt.Errorf(`compiling as a library is supported only on 386 and amd64 architectures (targeted architecture was %v)`, com.Arch)
|
||||
}
|
||||
templates := []string{"library.asm", "library.h"}
|
||||
features := AllFeatures{}
|
||||
m := NewMacros(*com, features)
|
||||
m.Library = true
|
||||
asmCode, err := com.compile("library.asm", m)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`could not execute template "library.asm": %v`, err)
|
||||
retmap := map[string]string{}
|
||||
for _, templateName := range templates {
|
||||
macros := NewMacros(*com, features)
|
||||
macros.Library = true
|
||||
populatedTemplate, extension, err := com.compile(templateName, macros)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`could not execute template "%v": %v`, templateName, err)
|
||||
}
|
||||
retmap[extension] = populatedTemplate
|
||||
}
|
||||
|
||||
m = NewMacros(*com, features)
|
||||
m.Library = true
|
||||
header, err := com.compile("library.h", &m)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`could not execute template "library.h": %v`, err)
|
||||
}
|
||||
return map[string]string{"asm": asmCode, "h": header}, nil
|
||||
return retmap, nil
|
||||
}
|
||||
|
||||
func (com *Compiler) Player(song *sointu.Song, maxSamples int) (map[string]string, error) {
|
||||
func (com *Compiler) Song(song *sointu.Song) (map[string]string, error) {
|
||||
if com.Arch != "386" && com.Arch != "amd64" {
|
||||
return nil, fmt.Errorf(`compiling a song player is supported only on 386 and amd64 architectures (targeted architecture was %v)`, com.Arch)
|
||||
}
|
||||
templates := []string{"player.asm", "player.h"}
|
||||
features := NecessaryFeaturesFor(song.Patch)
|
||||
retmap := map[string]string{}
|
||||
encodedPatch, err := Encode(&song.Patch, features)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`could not encode patch: %v`, err)
|
||||
}
|
||||
asmCode, err := com.compile("player.asm", NewPlayerMacros(*com, features, song, encodedPatch, maxSamples))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`could not execute template "player.asm": %v`, err)
|
||||
for _, templateName := range templates {
|
||||
macros := NewPlayerMacros(*com, features, song, encodedPatch)
|
||||
populatedTemplate, extension, err := com.compile(templateName, macros)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`could not execute template "%v": %v`, templateName, err)
|
||||
}
|
||||
retmap[extension] = populatedTemplate
|
||||
}
|
||||
|
||||
header, err := com.compile("player.h", NewPlayerMacros(*com, features, song, encodedPatch, maxSamples))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`could not execute template "player.h": %v`, err)
|
||||
}
|
||||
return map[string]string{"asm": asmCode, "h": header}, nil
|
||||
return retmap, nil
|
||||
}
|
||||
|
||||
func (com *Compiler) compile(templateName string, data interface{}) (string, string, error) {
|
||||
result := bytes.NewBufferString("")
|
||||
err := com.Template.ExecuteTemplate(result, templateName, data)
|
||||
extension := filepath.Ext(templateName)
|
||||
return result.String(), extension, err
|
||||
}
|
||||
|
@ -1,61 +0,0 @@
|
||||
// The following directive is necessary to make the package coherent:
|
||||
|
||||
// +build ignore
|
||||
|
||||
// This program generates the library headers and assembly files for the library
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"github.com/vsariola/sointu/compiler"
|
||||
)
|
||||
|
||||
func main() {
|
||||
targetArch := flag.String("arch", runtime.GOARCH, "Target architecture. Defaults to Go architecture. Possible values: amd64, 386 (anything else is assumed 386)")
|
||||
targetOs := flag.String("os", runtime.GOOS, "Target OS. Defaults to current Go OS. Possible values: windows, darwin, linux (anything else is assumed linux)")
|
||||
flag.Usage = printUsage
|
||||
flag.Parse()
|
||||
|
||||
if flag.NArg() != 1 {
|
||||
flag.Usage()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
comp, err := compiler.New()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, `error creating compiler: %v`, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
comp.Amd64 = *targetArch == "amd64"
|
||||
comp.OS = *targetOs
|
||||
|
||||
library, err := comp.Library()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, `error compiling library: %v`, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
filenames := map[string]string{"h": "sointu.h", "asm": "sointu.asm"}
|
||||
|
||||
for t, contents := range library {
|
||||
filename := filenames[t]
|
||||
err := ioutil.WriteFile(filepath.Join(flag.Args()[0], filename), []byte(contents), os.ModePerm)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, `could not write to file "%v": %v`, filename, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func printUsage() {
|
||||
fmt.Fprintf(os.Stderr, "Sointu command line utility for generating the library .asm and .h files.\nUsage: %s [flags] outputDirectory\n", os.Args[0])
|
||||
flag.PrintDefaults()
|
||||
}
|
@ -14,21 +14,23 @@ type OplistEntry struct {
|
||||
}
|
||||
|
||||
type Macros struct {
|
||||
Stacklocs []string
|
||||
Output16Bit bool
|
||||
Clip bool
|
||||
Library bool
|
||||
Sine int // TODO: how can we elegantly access global constants in template, without wrapping each one by one
|
||||
Trisaw int
|
||||
Pulse int
|
||||
Gate int
|
||||
Sample int
|
||||
usesFloatConst map[float32]bool
|
||||
usesIntConst map[int]bool
|
||||
floatConsts []float32
|
||||
intConsts []int
|
||||
calls map[string]bool
|
||||
stackframes map[string][]string
|
||||
Stacklocs []string
|
||||
Output16Bit bool
|
||||
Clip bool
|
||||
Library bool
|
||||
Amd64 bool
|
||||
DisableSections bool
|
||||
Sine int // TODO: how can we elegantly access global constants in template, without wrapping each one by one
|
||||
Trisaw int
|
||||
Pulse int
|
||||
Gate int
|
||||
Sample int
|
||||
usesFloatConst map[float32]bool
|
||||
usesIntConst map[int]bool
|
||||
floatConsts []float32
|
||||
intConsts []int
|
||||
calls map[string]bool
|
||||
stackframes map[string][]string
|
||||
FeatureSet
|
||||
Compiler
|
||||
}
|
||||
@ -44,6 +46,7 @@ func NewMacros(c Compiler, f FeatureSet) *Macros {
|
||||
Pulse: sointu.Pulse,
|
||||
Gate: sointu.Gate,
|
||||
Sample: sointu.Sample,
|
||||
Amd64: c.Arch == "amd64",
|
||||
Compiler: c,
|
||||
FeatureSet: f,
|
||||
}
|
||||
@ -449,10 +452,8 @@ type PlayerMacros struct {
|
||||
EncodedPatch
|
||||
}
|
||||
|
||||
func NewPlayerMacros(c Compiler, f FeatureSet, s *sointu.Song, e *EncodedPatch, maxSamples int) *PlayerMacros {
|
||||
if maxSamples == 0 {
|
||||
maxSamples = s.SamplesPerRow() * s.TotalRows()
|
||||
}
|
||||
func NewPlayerMacros(c Compiler, f FeatureSet, s *sointu.Song, e *EncodedPatch) *PlayerMacros {
|
||||
maxSamples := s.SamplesPerRow() * s.TotalRows()
|
||||
macros := *NewMacros(c, f)
|
||||
macros.Output16Bit = s.Output16Bit // TODO: should we actually store output16bit in Songs or not?
|
||||
p := PlayerMacros{Song: s, Macros: macros, MaxSamples: maxSamples, EncodedPatch: *e}
|
||||
|
Reference in New Issue
Block a user