diff --git a/README.md b/README.md index a534844..1868d13 100644 --- a/README.md +++ b/README.md @@ -185,7 +185,7 @@ USAGE: OPTIONS: --dev install any dev packages (default: false) - --env string directory where ACT-R, pyactr, and other necessary files are installed (default "./env") + --path value, -p value directory for env files (it will be created if it does not exist) (default: "./env") ``` For basic setup, run `./gactar env setup` @@ -203,10 +203,10 @@ Use the `--dev` flag to also install optional developer packages for linting & f $ ./gactar env setup --dev ``` -If you want to change the default environment (`env`), the directory can be specified using the `--env` option: +If you want to change the default environment (`env`), the directory can be specified using the `-path` option: ``` -./gactar env setup --env foo +./gactar env setup -path foo ``` **Note:** If you change the default environment, you will need to specify `--env foo` each time you run gactar. diff --git a/cmd/env.go b/cmd/env.go index 428b627..c38c86d 100644 --- a/cmd/env.go +++ b/cmd/env.go @@ -1,6 +1,7 @@ package cmd import ( + "encoding/json" "errors" "fmt" "net/url" @@ -9,6 +10,7 @@ import ( "runtime" "github.com/spf13/cobra" + "golang.org/x/exp/slices" "github.com/asmaloney/gactar/util/chalk" "github.com/asmaloney/gactar/util/cli" @@ -19,10 +21,19 @@ import ( ) const ( - // ACT-R release version from https://github.com/asmaloney/ACT-R + // JSON file used to override our defaults + SUPPORT_TOOLS_FILE = "install/support-tools.json" + + // Default ACT-R repo + ACTR_REPO = "github.com/asmaloney/ACT-R" + + // Default ACT-R release version ACTR_VERSION = "7.27.7" - // Clozure Common Lisp release version from https://github.com/Clozure/ccl + // Default Clozure Common Lisp repo + CCL_REPO = "github.com/Clozure/ccl" + + // Default Clozure Common Lisp release version CCL_VERSION = "1.12.2" ) @@ -36,6 +47,11 @@ var ( flagUpdatePython = false flagUpdatePythonPackages = false flagUpdateDev = false + + // names we allow in the support-tools file + validTools = []string{"ACT-R", "CCL"} + + defaultToolInfo = make(toolInfoMap, len(validTools)) ) var envCmd = &cobra.Command{ @@ -51,7 +67,7 @@ var setupCmd = &cobra.Command{ Use: "setup", Short: "Setup an environment", Run: func(cmd *cobra.Command, args []string) { - envPath, err := getVirtualEnvironmentPath(cmd.Flags()) + envPath, err := expandPathFlag(cmd.Flags(), "path") if err != nil { chalk.PrintErr(err) return @@ -98,8 +114,26 @@ func (e errCCLSystem) Error() string { return fmt.Sprintf("no CCL compiler available for system %q", e.OSName) } +// toolInfoList is used to read tool version info from a file +type toolInfoList struct { + List []toolInfo `json:"tool-info"` +} + +// toolInfoMap maps a tool name to its info +type toolInfoMap map[string]toolInfo + +// toolInfo stores version info about a tool +type toolInfo struct { + Name string `json:"name"` + RepoURL string `json:"repo-url"` + Version string `json:"version"` +} + func init() { + setDefaultToolInfo() + setupCmd.Flags().BoolVar(&flagSetupDev, "dev", false, "install dev packages") + setupCmd.Flags().StringP("path", "p", "./env", "directory for env files (it will be created if it does not exist)") envCmd.AddCommand(setupCmd) @@ -113,6 +147,57 @@ func init() { rootCmd.AddCommand(envCmd) } +// setDefaultToolInfo fills in the default tool versions as a fallback in case the external +// file doesn't exist or fails to parse. +func setDefaultToolInfo() { + defaultToolInfo["ACT-R"] = toolInfo{ + Name: "ACT-R", + RepoURL: ACTR_REPO, + Version: ACTR_VERSION, + } + + defaultToolInfo["CCL"] = toolInfo{ + Name: "CCL", + RepoURL: CCL_REPO, + Version: CCL_VERSION, + } +} + +func readToolInfo() (toolInfo toolInfoMap, err error) { + file, err := os.ReadFile(SUPPORT_TOOLS_FILE) + if err != nil { + return + } + + data := toolInfoList{} + + err = json.Unmarshal(file, &data) + if err != nil { + return + } + + if len(data.List) == 0 { + chalk.PrintWarningStr(fmt.Sprintf("failed to find version information in %q; using defaults", SUPPORT_TOOLS_FILE)) + return defaultToolInfo, err + } + + toolInfo = defaultToolInfo + + // Get our version from the support-tools file + for _, info := range data.List { + if !slices.Contains(validTools, info.Name) { + chalk.PrintWarningStr(fmt.Sprintf("invalid tool name found in support-tools file: %q", info.Name)) + continue + } + + tool := toolInfo[info.Name] + tool.Version = info.Version + toolInfo[info.Name] = tool + } + + return +} + func runSetup(envPath string, dev bool) (err error) { fmt.Println("gactar Environment Setup\n---") fmt.Printf("Setting up an environment: %q\n", envPath) @@ -121,8 +206,11 @@ func runSetup(envPath string, dev bool) (err error) { if filesystem.DirExists(envPath) { err = fmt.Errorf("cannot set environment path to %q: %w", envPath, errPathExists) return - } else { - err = nil + } + + tools, err := readToolInfo() + if err != nil { + return err } // Create the virtual environment directory @@ -138,15 +226,13 @@ func runSetup(envPath string, dev bool) (err error) { err = setupPython(envPath, dev) if err != nil { - fmt.Println(err.Error()) - err = nil + chalk.PrintErr(err) // Don't return - we can still try to set up the Lisp compiler } - err = setupLisp() + err = setupLisp(tools) if err != nil { - fmt.Println(err.Error()) - err = nil + return err } return @@ -214,38 +300,35 @@ func setupPython(envPath string, dev bool) (err error) { return } -func setupLisp() (err error) { +func setupLisp(tools toolInfoMap) (err error) { fmt.Println() fmt.Println("Setting up Lisp\n---") // Download vanilla ACT-R - repo := "github.com/asmaloney/ACT-R" - extension := "zip" - archiveFile := fmt.Sprintf("actr-super-slim-v%s.%s", ACTR_VERSION, extension) + actrInfo := tools["ACT-R"] + archiveFile := fmt.Sprintf("actr-super-slim-v%s.zip", actrInfo.Version) - err = downloadGitHubRelease("ACT-R", repo, ACTR_VERSION, archiveFile, "actr") + err = downloadGitHubRelease("ACT-R", actrInfo.RepoURL, actrInfo.Version, archiveFile, "actr") if err != nil { return } // Download Clozure Common Lisp compiler (CCL) + cclInfo := tools["CCL"] system := runtime.GOOS if system != "darwin" && system != "linux" && system != "windows" { return &errCCLSystem{OSName: system} } - repo = "github.com/Clozure/ccl" - extension = "tar.gz" - version := CCL_VERSION - + extension := "tar.gz" if system == "windows" { extension = "zip" } - dirName := fmt.Sprintf("ccl-%s-%sx86", version, system) + dirName := fmt.Sprintf("ccl-%s-%sx86", cclInfo.Version, system) archiveFile = fmt.Sprintf("%s.%s", dirName, extension) - err = downloadGitHubRelease("Clozure Common Lisp (ccl)", repo, version, archiveFile, "") + err = downloadGitHubRelease("Clozure Common Lisp (ccl)", cclInfo.RepoURL, cclInfo.Version, archiveFile, "") if err != nil { return } diff --git a/install/support-tools.json b/install/support-tools.json new file mode 100644 index 0000000..0942407 --- /dev/null +++ b/install/support-tools.json @@ -0,0 +1,12 @@ +{ + "tool-info": [ + { + "name": "ACT-R", + "version": "7.27.7" + }, + { + "name": "CCL", + "version": "1.12.2" + } + ] +}