github.com/r2d2-ai/cli@v1.20.0/api/build.go (about) 1 package api 2 3 import ( 4 "fmt" 5 "go/parser" 6 "go/printer" 7 "go/token" 8 "io/ioutil" 9 "os" 10 "path/filepath" 11 "strings" 12 13 "github.com/r2d2-ai/cli/common" 14 "github.com/r2d2-ai/cli/util" 15 ) 16 17 const ( 18 fileEmbeddedAppGo string = "embeddedapp.go" 19 ) 20 21 func BuildProject(project common.AppProject, options common.BuildOptions) error { 22 23 err := project.DepManager().AddReplacedContribForBuild() 24 if err != nil { 25 return err 26 } 27 28 buildPreProcessors := common.BuildPreProcessors() 29 30 if len(buildPreProcessors) > 0 { 31 for _, processor := range buildPreProcessors { 32 err = processor.DoPreProcessing(project, options) 33 if err != nil { 34 return err 35 } 36 } 37 } 38 39 var builder common.Builder 40 embedConfig := options.EmbedConfig 41 42 if options.Shim != "" { 43 builder = &ShimBuilder{shim: options.Shim} 44 embedConfig = true 45 } else { 46 builder = &AppBuilder{} 47 } 48 49 if embedConfig { 50 err = createEmbeddedAppGoFile(project) 51 if err != nil { 52 return err 53 } 54 } else { 55 err = cleanupEmbeddedAppGoFile(project) 56 if err != nil { 57 return err 58 } 59 } 60 61 if options.OptimizeImports { 62 if Verbose() { 63 fmt.Println("Optimizing imports...") 64 } 65 err := optimizeImports(project) 66 defer restoreImports(project) 67 68 if err != nil { 69 return err 70 } 71 } 72 73 err = builder.Build(project) 74 if err != nil { 75 return err 76 } 77 78 buildPostProcessors := common.BuildPostProcessors() 79 80 if len(buildPostProcessors) > 0 { 81 for _, processor := range buildPostProcessors { 82 err = processor.DoPostProcessing(project) 83 if err != nil { 84 return err 85 } 86 } 87 } 88 89 return nil 90 } 91 92 func cleanupEmbeddedAppGoFile(project common.AppProject) error { 93 embedSrcPath := filepath.Join(project.SrcDir(), fileEmbeddedAppGo) 94 95 if _, err := os.Stat(embedSrcPath); err == nil { 96 if Verbose() { 97 fmt.Println("Removing embed configuration") 98 } 99 err = os.Remove(embedSrcPath) 100 if err != nil { 101 return err 102 } 103 } 104 return nil 105 } 106 107 func createEmbeddedAppGoFile(project common.AppProject) error { 108 109 embedSrcPath := filepath.Join(project.SrcDir(), fileEmbeddedAppGo) 110 111 if Verbose() { 112 fmt.Println("Embedding configuration in application...") 113 } 114 115 buf, err := ioutil.ReadFile(filepath.Join(project.Dir(), fileFlogoJson)) 116 if err != nil { 117 return err 118 } 119 flogoJSON := string(buf) 120 121 tplFile := tplEmbeddedAppGoFile 122 if !isNewMain(project) { 123 tplFile = tplEmbeddedAppOldGoFile 124 } 125 126 engineJSON := "" 127 128 if util.FileExists(filepath.Join(project.Dir(), fileEngineJson)) { 129 buf, err = ioutil.ReadFile(filepath.Join(project.Dir(), fileEngineJson)) 130 if err != nil { 131 return err 132 } 133 134 engineJSON = string(buf) 135 } 136 137 data := struct { 138 FlogoJSON string 139 EngineJSON string 140 }{ 141 flogoJSON, 142 engineJSON, 143 } 144 145 f, err := os.Create(embedSrcPath) 146 if err != nil { 147 return err 148 } 149 RenderTemplate(f, tplFile, &data) 150 _ = f.Close() 151 152 return nil 153 } 154 155 func isNewMain(project common.AppProject) bool { 156 mainGo := filepath.Join(project.SrcDir(), fileMainGo) 157 buf, err := ioutil.ReadFile(mainGo) 158 if err == nil { 159 mainCode := string(buf) 160 return strings.Contains(mainCode, "cfgEngine") 161 162 } 163 164 return false 165 } 166 167 var tplEmbeddedAppGoFile = `// Do not change this file, it has been generated using flogo-cli 168 // If you change it and rebuild the application your changes might get lost 169 package main 170 171 // embedded flogo app descriptor file 172 const flogoJSON string = ` + "`{{.FlogoJSON}}`" + ` 173 const engineJSON string = ` + "`{{.EngineJSON}}`" + ` 174 175 func init () { 176 cfgJson = flogoJSON 177 cfgEngine = engineJSON 178 } 179 ` 180 181 var tplEmbeddedAppOldGoFile = `// Do not change this file, it has been generated using flogo-cli 182 // If you change it and rebuild the application your changes might get lost 183 package main 184 185 // embedded flogo app descriptor file 186 const flogoJSON string = ` + "`{{.FlogoJSON}}`" + ` 187 188 func init () { 189 cfgJson = flogoJSON 190 } 191 ` 192 193 func initMain(project common.AppProject, backupMain bool) error { 194 195 //backup main if it exists 196 mainGo := filepath.Join(project.SrcDir(), fileMainGo) 197 mainGoBak := filepath.Join(project.SrcDir(), fileMainGo+".bak") 198 199 if backupMain { 200 if _, err := os.Stat(mainGo); err == nil { 201 err = os.Rename(mainGo, mainGoBak) 202 if err != nil { 203 return err 204 } 205 } else if _, err := os.Stat(mainGoBak); err != nil { 206 return fmt.Errorf("project corrupt, main missing") 207 } 208 } else { 209 if _, err := os.Stat(mainGoBak); err == nil { 210 err = os.Rename(mainGoBak, mainGo) 211 if err != nil { 212 return err 213 } 214 } else if _, err := os.Stat(mainGo); err != nil { 215 return fmt.Errorf("project corrupt, main missing") 216 } 217 } 218 219 return nil 220 } 221 222 func optimizeImports(project common.AppProject) error { 223 224 appImports, err := util.GetAppImports(filepath.Join(project.Dir(), fileFlogoJson), project.DepManager(), true) 225 if err != nil { 226 return err 227 } 228 229 var unused []util.Import 230 appImports.GetAllImports() 231 for _, impDetails := range appImports.GetAllImportDetails() { 232 if !impDetails.Referenced() && impDetails.IsCoreContrib() { 233 unused = append(unused, impDetails.Imp) 234 } 235 } 236 237 importsFile := filepath.Join(project.SrcDir(), fileImportsGo) 238 importsFileOrig := filepath.Join(project.SrcDir(), fileImportsGo+".orig") 239 240 err = util.CopyFile(importsFile, importsFileOrig) 241 if err != nil { 242 return err 243 } 244 245 fset := token.NewFileSet() 246 file, err := parser.ParseFile(fset, importsFile, nil, parser.ImportsOnly) 247 if err != nil { 248 return err 249 } 250 251 for _, i := range unused { 252 if Verbose() { 253 fmt.Printf(" Removing Import: %s\n", i.GoImportPath()) 254 } 255 util.DeleteImport(fset, file, i.GoImportPath()) 256 } 257 258 f, err := os.Create(importsFile) 259 defer f.Close() 260 if err := printer.Fprint(f, fset, file); err != nil { 261 return err 262 } 263 264 return nil 265 } 266 267 func restoreImports(project common.AppProject) { 268 269 importsFile := filepath.Join(project.SrcDir(), fileImportsGo) 270 importsFileOrig := filepath.Join(project.SrcDir(), fileImportsGo+".orig") 271 272 if _, err := os.Stat(importsFileOrig); err == nil { 273 err = util.CopyFile(importsFileOrig, importsFile) 274 if err != nil { 275 fmt.Fprintf(os.Stderr, "Error restoring imports file '%s': %v\n", importsFile, err) 276 return 277 } 278 279 var err = os.Remove(importsFileOrig) 280 if err != nil { 281 fmt.Fprintf(os.Stderr, "Error removing backup imports file '%s': %v\n", importsFileOrig, err) 282 fmt.Fprintf(os.Stderr, "Manually remove backup imports file '%s'\n", importsFileOrig) 283 } 284 } 285 }