commit initial version

This commit is contained in:
Dagen Brock 2021-04-26 07:50:38 -05:00
parent b3e7261fb2
commit 95e5685072
16 changed files with 445 additions and 0 deletions

55
README.md Normal file
View File

@ -0,0 +1,55 @@
# Appy
Local project builder for emulator disk images
## Getting Started
Install Appy then create an `appy.yaml` project definition file.
Run Appy to assemble, build, and run your source. Or do all 3 with:
```
appy brun
```
### What this solves
We often need to assemble projects locally, but that is only part of the equation for people writing project to run under emulation.
The second step is taking our assembled binaries, and combining them with any other assets, data files, operating system files, et cetera, into a single disk image for use with an emulator.
Often this is handled by a set of scripts the author has locally in the projects. However this leads to problems when collaborating with other people or building pipelines.
Problem 1: You need to not only provide the scripts, but also the build tools they use, at the correct version.
Problem 2: The scripts often contain bespoke logic for things like handling filetypes or where to place files on the disk image.
Appy abstracts the tools away from a script mindset, and into a project mindset. Currently the tool paths are hardcoded but next versions will include dynamic tool providers, including vendoring from internet sources!
## The project file
Currently it uses an `appy.yaml` file in the current project directory.
### Running Appy
Appy has a verb command structure (sorta like git) for executing the various functions.
Invoke like: `appy command`
Commands:
```
$ appy asm # assemble all files
$ appy disk # create all disk images and add all files
$ appy run # launch an emulator
$ appy build # assemble files and make disk, aka 'asm'+'disk'
$ appy brun # assemble files, make disk, and launch emulator
# aka 'asm'+'disk'+'run'
```
### Version Notes
This is an early experimental version not intended for public use.
Versioning from external sources and custom tool paths not yet implemented but all core functionality exists.

13
appy.yaml Normal file
View File

@ -0,0 +1,13 @@
assemble: [testsrc/sp.s, testsrc/pc.s]
disks:
- name: mydiskimage
file: mydiskimage800.2mg
size: 800KB
files:
- input: ../PRODOS.2.4.2/PRODOS
output: /mydiskimage
- input: testsrc/sp.s
output: /mydiskimage
- input: ../modsearch/potential/eclipse.ntp
output: /mydiskimage
ftaux: #B30000

22
cmd/asm.go Normal file
View File

@ -0,0 +1,22 @@
package cmd
import (
"github.com/digarok/appy/core"
"github.com/spf13/cobra"
)
// 'appy asm' command
var asmCmd = &cobra.Command{
Use: "asm",
Short: "Assemble the source files in your project",
Long: `This will run your 6502/65816 assembler, such as Merlin32,
against all of the files specified for assembly in your project.`,
Run: func(cmd *cobra.Command, args []string) {
core.Assemble()
},
}
func init() {
rootCmd.AddCommand(asmCmd)
}

27
cmd/brun.go Normal file
View File

@ -0,0 +1,27 @@
package cmd
import (
"github.com/digarok/appy/core"
"github.com/spf13/cobra"
)
// brunCmd represents the brun command
var brunCmd = &cobra.Command{
Use: "brun",
Short: "A brief description of your command",
Long: `A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:
Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
Run: func(cmd *cobra.Command, args []string) {
core.Assemble()
core.BuildDisk()
core.Run()
},
}
func init() {
rootCmd.AddCommand(brunCmd)
}

22
cmd/build.go Normal file
View File

@ -0,0 +1,22 @@
package cmd
import (
"github.com/digarok/appy/core"
"github.com/spf13/cobra"
)
// buildCmd represents the build command
var buildCmd = &cobra.Command{
Use: "build",
Short: "This is the equivalent of 'asm' and 'disk' in a single command.",
Long: `This will first assemble your files and then build the disk
image(s) specified in your project file.`,
Run: func(cmd *cobra.Command, args []string) {
core.Assemble()
core.BuildDisk()
},
}
func init() {
rootCmd.AddCommand(buildCmd)
}

22
cmd/disk.go Normal file
View File

@ -0,0 +1,22 @@
package cmd
import (
"github.com/digarok/appy/core"
"github.com/spf13/cobra"
)
// 'appy disk' command
var diskCmd = &cobra.Command{
Use: "disk",
Short: "Create disk images from your project",
Long: `This will launch a disk creation utility (CADIUS), and
create the disk images with the files as specified by
your project configuration file.`,
Run: func(cmd *cobra.Command, args []string) {
core.BuildDisk()
},
}
func init() {
rootCmd.AddCommand(diskCmd)
}

85
cmd/root.go Normal file
View File

@ -0,0 +1,85 @@
package cmd
import (
"fmt"
"os"
homedir "github.com/mitchellh/go-homedir"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/digarok/appy/core/project"
)
var cfgFile string
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "appy",
Short: "A happy little Apple II project application.",
Long: `This will assemble your source files, and build your disk images,
and let you run them in an Apple II emulator in a single command. For example:
./appy run # this will do everything!
`,
// Uncomment the following line if your bare application
// has an action associated with it:
// Run: func(cmd *cobra.Command, args []string) { },
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
// fmt.Println("root.Execute()")
cobra.CheckErr(rootCmd.Execute())
}
func init() {
cobra.OnInitialize(initConfig)
// fmt.Println("root.init()")
// Here you will define your flags and configuration settings.
// Cobra supports persistent flags, which, if defined here,
// will be global for your application.
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.appy.yaml)")
// Cobra also supports local flags, which will only run
// when this action is called directly.
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}
// initConfig reads in config file and ENV variables if set.
func initConfig() {
// fmt.Println("root.initConfig()")
if cfgFile != "" {
// Use config file from the flag.
viper.SetConfigFile(cfgFile)
} else {
viper.SetConfigName("appy")
// Typically look in the current path for the project config.
viper.AddConfigPath(".")
// Find home directory.
home, err := homedir.Dir()
cobra.CheckErr(err)
// Search config in home directory with name ".appy" (without extension).
viper.AddConfigPath(home)
}
viper.AutomaticEnv() // read in environment variables that match
// If a config file is found, read it in.
if err := viper.ReadInConfig(); err == nil {
fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed())
} else {
fmt.Fprintln(os.Stderr, "Error loading config:", err)
}
project.SelfConfigure()
// project.Id()
}

25
cmd/run.go Normal file
View File

@ -0,0 +1,25 @@
package cmd
import (
"github.com/digarok/appy/core"
"github.com/spf13/cobra"
)
// runCmd represents the run command
var runCmd = &cobra.Command{
Use: "run",
Short: "A brief description of your command",
Long: `A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:
Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
Run: func(cmd *cobra.Command, args []string) {
core.Run()
},
}
func init() {
rootCmd.AddCommand(runCmd)
}

32
core/assembler.go Normal file
View File

@ -0,0 +1,32 @@
package core
import (
"fmt"
"log"
"os/exec"
"github.com/spf13/viper"
)
var filesToAssemble []string
const Merlin32Path = "/usr/local/bin/merlin32"
func Assemble() {
//fmt.Println("asm called")
filesToAssemble = viper.GetViper().GetStringSlice("assemble")
//fmt.Fprintln(os.Stderr, "HEY", filesToAssemble)
for _, filename := range filesToAssemble {
fmt.Printf("Assembling %v\n", filename)
cmd := exec.Command(Merlin32Path, "-V", filename)
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
}
}

46
core/diskbuilder.go Normal file
View File

@ -0,0 +1,46 @@
package core
import (
"fmt"
"log"
"os/exec"
"github.com/digarok/appy/core/project"
)
const CadiusPath = "/usr/local/bin/cadius"
func CreateDisk(name string, file string, size string) {
fmt.Printf("Creating Disk: \"%s\" -> %s \tSize: %s\n", name, file, size)
cmd := exec.Command(CadiusPath, "CREATEVOLUME", file, name, size)
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
}
func AddFiles(disk project.Disk) {
fmt.Printf("Add files to: \"%s\"\n", disk.Name)
for _, file := range disk.Files {
// fmt.Printf("%s ADDFILE %s %s %s\n", CadiusPath, disk.File, file.Output, file.Input)
fmt.Printf(" Adding file: -----> %s\n", file.Input)
cmd := exec.Command(CadiusPath, "ADDFILE", disk.File, file.Output, file.Input)
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
}
}
func BuildDisk() {
var p = project.AppyProj
for _, disk := range p.Disks {
CreateDisk(disk.Name, disk.File, disk.Size)
AddFiles(disk)
}
}

39
core/project/project.go Normal file
View File

@ -0,0 +1,39 @@
package project
import (
"fmt"
"log"
"github.com/spf13/viper"
)
type Project struct {
name string
Disks []Disk
}
type Disk struct {
Name string
File string
Size string
Files []File
}
type File struct {
Input string
Output string
}
var AppyProj Project
func SelfConfigure() {
AppyProj.name = "Selfie"
err := viper.Unmarshal(&AppyProj)
if err != nil {
log.Fatalf("unable to decode into struct, %v", err)
}
}
func Id() {
fmt.Println("HI FROM:", AppyProj.name)
}

22
core/runner.go Normal file
View File

@ -0,0 +1,22 @@
package core
import (
"fmt"
"log"
"os/exec"
)
const emulatorPath = "gsplus"
func Run() {
fmt.Println("Running an emulator")
cmd := exec.Command(emulatorPath)
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
}

9
go.mod Normal file
View File

@ -0,0 +1,9 @@
module github.com/digarok/appy
go 1.16
require (
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/spf13/cobra v1.1.3 // indirect
github.com/spf13/viper v1.7.1 // indirect
)

22
main.go Normal file
View File

@ -0,0 +1,22 @@
/*
Copyright © 2021 NAME HERE <EMAIL ADDRESS>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import "github.com/digarok/appy/cmd"
func main() {
cmd.Execute()
}

2
testsrc/pc.s Normal file
View File

@ -0,0 +1,2 @@
org $300
rts

2
testsrc/sp.s Normal file
View File

@ -0,0 +1,2 @@
org $2000
rts