github.com/sapplications/sb@v0.0.0-20240116135441-1a13cafe3497/smartbuilder.go (about) 1 // Copyright 2022 Vitalii Noha vitalii.noga@gmail.com. All rights reserved. 2 3 package sb 4 5 import ( 6 "errors" 7 "fmt" 8 "os" 9 "os/exec" 10 "path/filepath" 11 "runtime" 12 13 "github.com/hashicorp/go-hclog" 14 "github.com/hashicorp/go-plugin" 15 ) 16 17 // Generate generates smart builder unit (.sb) using smart application unit. 18 func (b *SmartBuilder) Generate(application string) error { 19 defer handleError() 20 b.logInfo(fmt.Sprintf("generating \"%s\" application", application)) 21 // load and check application 22 b.ModManager.SetLogger(b.Logger) 23 mod, err := b.ModManager.ReadAll() 24 if err != nil { 25 return err 26 } 27 b.logTrace(fmt.Sprintf("checking \"%s\" application", application)) 28 application, err = b.checkApplication(application, mod) 29 if err != nil { 30 return err 31 } 32 sources := mod.Items() 33 info, err := getApp(application, sources) 34 if err != nil { 35 return err 36 } 37 coder := "" 38 found := false 39 for _, row := range info { 40 if row[0] == coderAttrName { 41 found = true 42 if len(row) > 1 { 43 coder = row[1] 44 } 45 break 46 } 47 } 48 if !found { 49 return fmt.Errorf(AttrIsMissingF, coderAttrName, application) 50 } 51 // process application 52 client, raw, err := b.newPlugin(coder) 53 if err != nil { 54 return err 55 } 56 defer client.Kill() 57 builder := raw.(builder) 58 b.logTrace(fmt.Sprintf("generating \"%s\" application using sgo plugin", application)) 59 if err := builder.Generate(application, &sources); err != nil { 60 return err 61 } 62 return nil 63 } 64 65 // Build builds an application using the generated items. 66 func (b *SmartBuilder) Build(application string) error { 67 defer handleError() 68 b.logInfo(fmt.Sprintf("building \"%s\" application", application)) 69 // load and check application 70 b.ModManager.SetLogger(b.Logger) 71 mod, err := b.ModManager.ReadAll() 72 if err != nil { 73 return err 74 } 75 application, err = b.checkApplication(application, mod) 76 if err != nil { 77 return err 78 } 79 info, err := getApp(application, mod.Items()) 80 if err != nil { 81 return err 82 } 83 coder := "" 84 found := false 85 for _, row := range info { 86 if row[0] == coderAttrName { 87 found = true 88 if len(row) > 1 { 89 coder = row[1] 90 } 91 break 92 } 93 } 94 if !found { 95 return fmt.Errorf(AttrIsMissingF, coderAttrName, application) 96 } 97 // process application 98 client, raw, err := b.newPlugin(coder) 99 if err != nil { 100 return err 101 } 102 defer client.Kill() 103 builder := raw.(builder) 104 b.logTrace(fmt.Sprintf("generating \"%s\" application using sgo plugin", application)) 105 if err := builder.Build(application); err != nil { 106 return err 107 } 108 return nil 109 } 110 111 // Clean removes generated/compiled files. 112 func (b *SmartBuilder) Clean(application string) error { 113 defer handleError() 114 b.logInfo(fmt.Sprintf("cleaning \"%s\" application", application)) 115 // load and check application 116 b.ModManager.SetLogger(b.Logger) 117 mod, err := b.ModManager.ReadAll() 118 if err != nil { 119 return err 120 } 121 application, err = b.checkApplication(application, mod) 122 if err != nil { 123 return err 124 } 125 sources := mod.Items() 126 info, err := getApp(application, sources) 127 if err != nil { 128 return err 129 } 130 coder := "" 131 found := false 132 for _, row := range info { 133 if row[0] == coderAttrName { 134 found = true 135 if len(row) > 1 { 136 coder = row[1] 137 } 138 break 139 } 140 } 141 if !found { 142 return fmt.Errorf(AttrIsMissingF, coderAttrName, application) 143 } 144 // process application 145 client, raw, err := b.newPlugin(coder) 146 if err != nil { 147 return err 148 } 149 defer client.Kill() 150 builder := raw.(builder) 151 if err := builder.Clean(application, &sources); err != nil { 152 return err 153 } 154 return nil 155 } 156 157 // Run runs the application. 158 func (b *SmartBuilder) Run(application string) error { 159 defer handleError() 160 b.logInfo(fmt.Sprintf("running \"%s\" application", application)) 161 // load and check application 162 b.ModManager.SetLogger(b.Logger) 163 mod, err := b.ModManager.ReadAll() 164 if err != nil { 165 return err 166 } 167 application, err = b.checkApplication(application, mod) 168 if err != nil { 169 return err 170 } 171 // run an application 172 folder, _ := filepath.Abs(filepath.Dir(os.Args[0])) 173 folder = filepath.Join(folder, application) 174 if runtime.GOOS == "windows" { 175 application += ".exe" 176 } 177 if _, err = os.Stat(filepath.Join(folder, application)); err != nil { 178 if errors.Is(err, os.ErrNotExist) { 179 return fmt.Errorf(AppIsMissingInSystemF, application) 180 } else { 181 return err 182 } 183 } 184 wd, _ := os.Getwd() 185 if err = os.Chdir(folder); err != nil { 186 return err 187 } 188 cmd := exec.Command(application) 189 output, err := cmd.Output() 190 if err == nil { 191 fmt.Print(string(output)) 192 } 193 os.Chdir(wd) 194 return err 195 } 196 197 // Version displays a version of the application. 198 func (b *SmartBuilder) Version() string { 199 return AppVersionString 200 } 201 202 // Init creates a apps.sb module and initialize it with the apps item. 203 // If the apps item is exist then do nothing. 204 func (b *SmartBuilder) Init() error { 205 b.logInfo("initializing module") 206 b.ModManager.SetLogger(b.Logger) 207 return b.ModManager.AddItem(DefaultModuleName, AppsItemName) 208 } 209 210 // ReadAll loads modules. 211 func (b *SmartBuilder) ReadAll(kind string) (ModReader, error) { 212 defer handleError() 213 b.logInfo(fmt.Sprint("reading modules")) 214 b.ModManager.SetLogger(b.Logger) 215 mod, err := b.ModManager.ReadAll() 216 if err != nil { 217 return nil, err 218 } 219 return mod, nil 220 } 221 222 // AddItem adds an item to the module. 223 func (b *SmartBuilder) AddItem(module, item string) error { 224 defer handleError() 225 b.logInfo(fmt.Sprintf("adding \"%s\" item to \"%s\" module", item, module)) 226 b.ModManager.SetLogger(b.Logger) 227 return b.ModManager.AddItem(module, item) 228 } 229 230 // AddDependency adds a dependency to the item. 231 func (b *SmartBuilder) AddDependency(item, dependency, resolver string, update bool) error { 232 defer handleError() 233 b.logInfo(fmt.Sprintf("adding \"%s\" dependency to \"%s\" item", dependency, item)) 234 b.ModManager.SetLogger(b.Logger) 235 return b.ModManager.AddDependency(item, dependency, resolver, update) 236 } 237 238 // DeleteItem deletes the item from the module. 239 func (b *SmartBuilder) DeleteItem(item string) error { 240 defer handleError() 241 b.logInfo(fmt.Sprintf("deleting \"%s\" item", item)) 242 b.ModManager.SetLogger(b.Logger) 243 return b.ModManager.DeleteItem(item) 244 } 245 246 // DeleteDependency deletes the dependency from the item. 247 func (b *SmartBuilder) DeleteDependency(item, dependency string) error { 248 defer handleError() 249 b.logInfo(fmt.Sprintf("deleting \"%s\" dependency from \"%s\" item", dependency, item)) 250 b.ModManager.SetLogger(b.Logger) 251 return b.ModManager.DeleteDependency(item, dependency) 252 } 253 254 func (b *SmartBuilder) newPlugin(name string) (client *plugin.Client, raw interface{}, err error) { 255 defer func() { 256 if r := recover(); r != nil { 257 fmt.Printf(ErrorMessageF, r) 258 if client != nil { 259 client.Kill() 260 } 261 } 262 }() 263 logger := hclog.New(&hclog.LoggerOptions{ 264 Name: name, 265 Output: os.Stdout, 266 Level: hclog.Error, 267 }) 268 pluginMap := map[string]plugin.Plugin{ 269 name: b.Builder.(plugin.Plugin), 270 } 271 cmd := name 272 if runtime.GOOS == "windows" { 273 cmd += ".exe" 274 } 275 client = plugin.NewClient(&plugin.ClientConfig{ 276 HandshakeConfig: b.PluginHandshake, 277 Plugins: pluginMap, 278 Cmd: exec.Command(cmd), 279 Logger: logger, 280 }) 281 rpcClient, err := client.Client() 282 if err != nil { 283 return nil, nil, err 284 } 285 raw, err = rpcClient.Dispense(name) 286 if err != nil { 287 return nil, nil, err 288 } 289 return 290 } 291 292 func (b *SmartBuilder) checkApplication(application string, reader ModReader) (string, error) { 293 // read the current application if it is not specified and only one is exist 294 if application == "" { 295 apps, err := getApps(reader.Items()) 296 if err != nil { 297 return "", err 298 } 299 // check the number of existing applications 300 if len(apps) == 0 { 301 return "", errors.New(AppIsMissing) 302 } 303 if len(apps) != 1 { 304 return "", fmt.Errorf(AppIsNotSpecified) 305 } 306 // select the existing application 307 application = apps[0][0] 308 } 309 return application, nil 310 } 311 312 //lint:ignore U1000 Ignore unused function 313 func (b *SmartBuilder) logTrace(message string) { 314 if b.Logger != nil { 315 b.Logger.Trace(message) 316 } 317 } 318 319 //lint:ignore U1000 Ignore unused function 320 func (b *SmartBuilder) logDebug(message string) { 321 if b.Logger != nil { 322 b.Logger.Debug(message) 323 } 324 } 325 326 //lint:ignore U1000 Ignore unused function 327 func (b *SmartBuilder) logInfo(message string) { 328 if b.Logger != nil { 329 b.Logger.Info(message) 330 } 331 } 332 333 //lint:ignore U1000 Ignore unused function 334 func (b *SmartBuilder) logWarn(message string) { 335 if b.Logger != nil { 336 b.Logger.Warn(message) 337 } 338 } 339 340 //lint:ignore U1000 Ignore unused function 341 func (b *SmartBuilder) logError(message string) { 342 if b.Logger != nil { 343 b.Logger.Error(message) 344 } 345 }