mirror of https://github.com/digarok/Appy.git
commit initial version
This commit is contained in:
parent
b3e7261fb2
commit
95e5685072
|
@ -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.
|
|
@ -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
|
|
@ -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)
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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()
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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
|
||||||
|
)
|
|
@ -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()
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
org $300
|
||||||
|
rts
|
|
@ -0,0 +1,2 @@
|
||||||
|
org $2000
|
||||||
|
rts
|
Loading…
Reference in New Issue