1// Copyright 2020 Google LLC 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// https://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package client 16 17import ( 18 "fmt" 19 20 "android.googlesource.com/platform/tools/treble.git/hacksaw/bind" 21 "android.googlesource.com/platform/tools/treble.git/hacksaw/codebase" 22 "android.googlesource.com/platform/tools/treble.git/hacksaw/workspace" 23) 24 25const usage = `Usage: hacksaw <command> <options> 26 27Commands: 28 help: show this message 29 codebase add <name> <path>: Add codebase to hacksaw's list. 30 codebase list: List all codebases that hacksaw knows about. 31 codebase default <name>: Change the default codebase. 32 codebase remove <name>: Remove a codebase from hacksaw's list. 33 workspace new <workspace_name> <codebase_name>: Create a new workspace 34 from a codebase. The codebase is optional if a default codebase 35 has already been set. 36 workspace recreate <name>: Recreate an existing workspace. 37 This will recreate any read-only bind mounts which may be necessary 38 when a machine is rebooted. 39 workspace list: List all known workspaces. 40 workspace remove <name>: Remove a workspace. 41 edit <path>: Make a workspace path editable by checking out the parent git project.` 42 43type Command struct { 44 workspace workspace.Workspace 45} 46 47func NewCommand(bm bind.PathBinder, wtd string) Command { 48 return Command{workspace.New(bm, wtd)} 49} 50 51func (c Command) addCodebase(args []string) error { 52 if len(args) < 5 { 53 return fmt.Errorf("Codebase name and path are required\n"+ 54 "Usage: %s %s %s <codebase_name> <path>", 55 args[0], args[1], args[2]) 56 } 57 name := args[3] 58 path := args[4] 59 _, err := codebase.Add(name, path) 60 if err != nil { 61 return err 62 } 63 fmt.Println("Added codebase", name) 64 return err 65} 66 67func (c Command) defaultCodebase(args []string) error { 68 if len(args) < 4 { 69 return fmt.Errorf("Codebase name is required\n"+ 70 "Usage: %s %s %s <codebase_name>", 71 args[0], args[1], args[2]) 72 } 73 name := args[3] 74 if err := codebase.SetDefault(name); err != nil { 75 return err 76 } 77 fmt.Println("Default codebase set to", name) 78 return nil 79} 80 81func (c Command) listCodebases() { 82 def := codebase.Default() 83 if def == "" { 84 def = "None" 85 } 86 fmt.Println("Default codebase:") 87 fmt.Println("\t", def) 88 89 list := codebase.List() 90 fmt.Println("Codebases:") 91 for name, path := range list { 92 fmt.Println("\t", name, path) 93 } 94} 95 96func (c Command) removeCodebase(args []string) error { 97 if len(args) < 4 { 98 return fmt.Errorf("Codebase name required\n"+ 99 "Usage: %s %s %s <codebase>", 100 args[0], args[1], args[2]) 101 } 102 name := args[3] 103 _, err := codebase.Remove(name) 104 if err != nil { 105 return err 106 } 107 fmt.Println("Removed codebase", name) 108 return nil 109} 110 111func (c Command) createWorkspace(args []string) error { 112 var codebaseName string 113 defaultCodebase := codebase.Default() 114 switch len(args) { 115 case 4: 116 if defaultCodebase == "" { 117 return fmt.Errorf("Codebase name is required\n"+ 118 "Usage: %s %s %s <name> <codebase>", 119 args[0], args[1], args[2]) 120 } else { 121 codebaseName = defaultCodebase 122 } 123 case 5: 124 codebaseName = args[4] 125 default: 126 return fmt.Errorf("Unexpected number of arguments\n"+ 127 "Usage: %s %s %s <name> <codebase>", 128 args[0], args[1], args[2]) 129 } 130 131 workspaceName := args[3] 132 dir, err := c.workspace.Create(workspaceName, codebaseName) 133 if err != nil { 134 return err 135 } 136 fmt.Println("Created", workspaceName, "at", dir) 137 return nil 138} 139 140func (c Command) recreateWorkspace(args []string) error { 141 if len(args) < 4 { 142 return fmt.Errorf("Workspace name is required\n"+ 143 "Usage: %s %s %s <name>", 144 args[0], args[1], args[2]) 145 } 146 147 workspaceName := args[3] 148 dir, err := c.workspace.Recreate(workspaceName) 149 if err != nil { 150 return err 151 } 152 fmt.Println("Recreated", workspaceName, "at", dir) 153 return nil 154} 155 156func (c Command) listWorkspaces() { 157 list := c.workspace.List() 158 fmt.Println("Workspaces:") 159 for name, codebase := range list { 160 fmt.Println("\t", name, codebase) 161 } 162} 163 164func (c Command) removeWorkspace(args []string) error { 165 if len(args) < 4 { 166 return fmt.Errorf("Workspace name required\n"+ 167 "Usage: %s %s %s <name>", 168 args[0], args[1], args[2]) 169 } 170 name := args[3] 171 _, err := c.workspace.Remove(name) 172 if err != nil { 173 return err 174 } 175 fmt.Println("Removed workspace", name) 176 return nil 177} 178 179func (c Command) editProject(args []string) error { 180 if len(args) < 3 { 181 return fmt.Errorf("Edit path required\n"+ 182 "Usage: %s %s <path>", 183 args[0], args[1]) 184 } 185 path := args[2] 186 branchName, projectPath, err := c.workspace.Edit(path) 187 if err != nil { 188 return err 189 } 190 fmt.Printf("Created branch %s on project %s\n", 191 branchName, projectPath) 192 return nil 193} 194 195//Handle parses all command line arguments 196func (c Command) Handle(args []string) error { 197 if len(args) < 2 || args[1] == "help" { 198 fmt.Println(usage) 199 return nil 200 } 201 202 command := args[1] 203 204 switch command { 205 case "codebase", "cb": 206 if len(args) < 3 { 207 return fmt.Errorf("Not enough arguments for codebase command") 208 } 209 subcommand := args[2] 210 switch subcommand { 211 case "add": 212 return c.addCodebase(args) 213 case "remove", "rm": 214 return c.removeCodebase(args) 215 case "default", "def": 216 return c.defaultCodebase(args) 217 case "list", "ls": 218 c.listCodebases() 219 return nil 220 default: 221 return fmt.Errorf("Command \"%s %s\" not found", command, subcommand) 222 } 223 case "workspace", "ws": 224 if len(args) < 3 { 225 return fmt.Errorf("Not enough arguments for workspace command") 226 } 227 subcommand := args[2] 228 switch subcommand { 229 case "new": 230 return c.createWorkspace(args) 231 case "recreate": 232 return c.recreateWorkspace(args) 233 case "remove", "rm": 234 return c.removeWorkspace(args) 235 case "list", "ls": 236 c.listWorkspaces() 237 return nil 238 default: 239 return fmt.Errorf("Command \"%s %s\" not found", command, subcommand) 240 } 241 case "edit": 242 return c.editProject(args) 243 default: 244 return fmt.Errorf("Command \"%s\" not found", command) 245 } 246 return nil 247} 248