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  }