github.com/wiselike/revel-cmd@v1.2.1/revel/build.go (about) 1 // Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved. 2 // Revel Framework source code and usage is governed by a MIT style 3 // license that can be found in the LICENSE file. 4 5 package main 6 7 import ( 8 "fmt" 9 "os" 10 "path/filepath" 11 "strings" 12 13 "github.com/wiselike/revel-cmd/harness" 14 "github.com/wiselike/revel-cmd/model" 15 "github.com/wiselike/revel-cmd/utils" 16 ) 17 18 var cmdBuild = &Command{ 19 UsageLine: "revel build [-r [run mode]] [import path] [target path] ", 20 Short: "build a Revel application (e.g. for deployment)", 21 Long: ` 22 Build the Revel web application named by the given import path. 23 This allows it to be deployed and run on a machine that lacks a Go installation. 24 25 For example: 26 27 revel build github.com/revel/examples/chat /tmp/chat 28 29 `, 30 } 31 32 func init() { 33 cmdBuild.RunWith = buildApp 34 cmdBuild.UpdateConfig = updateBuildConfig 35 } 36 37 // The update config updates the configuration command so that it can run. 38 func updateBuildConfig(c *model.CommandConfig, args []string) bool { 39 c.Index = model.BUILD 40 if c.Build.TargetPath == "" { 41 c.Build.TargetPath = "target" 42 } 43 if len(args) == 0 && c.Build.ImportPath != "" { 44 return true 45 } 46 // If arguments were passed in then there must be two 47 if len(args) < 2 { 48 fmt.Fprintf(os.Stderr, "%s\n%s", cmdBuild.UsageLine, cmdBuild.Long) 49 return false 50 } 51 52 c.Build.ImportPath = args[0] 53 c.Build.TargetPath = args[1] 54 if len(args) > 2 { 55 c.Build.Mode = args[2] 56 } 57 return true 58 } 59 60 // The main entry point to build application from command line. 61 func buildApp(c *model.CommandConfig) (err error) { 62 appImportPath, destPath, mode := c.ImportPath, c.Build.TargetPath, DefaultRunMode 63 if len(c.Build.Mode) > 0 { 64 mode = c.Build.Mode 65 } 66 67 // Convert target to absolute path 68 c.Build.TargetPath, _ = filepath.Abs(destPath) 69 c.Build.Mode = mode 70 c.Build.ImportPath = appImportPath 71 72 revelPaths, err := model.NewRevelPaths(mode, appImportPath, c.AppPath, model.NewWrappedRevelCallback(nil, c.PackageResolver)) 73 if err != nil { 74 return 75 } 76 77 if err = buildSafetyCheck(destPath); err != nil { 78 return 79 } 80 81 // Ensure the application can be built, this generates the main file 82 app, err := harness.Build(c, revelPaths) 83 if err != nil { 84 return err 85 } 86 // Copy files 87 // Included are: 88 // - run scripts 89 // - binary 90 // - revel 91 // - app 92 93 packageFolders, err := buildCopyFiles(c, app, revelPaths) 94 if err != nil { 95 return 96 } 97 err = buildCopyModules(c, revelPaths, packageFolders, app) 98 if err != nil { 99 return 100 } 101 err = buildWriteScripts(c, app) 102 if err != nil { 103 return 104 } 105 return 106 } 107 108 // Copy the files to the target. 109 func buildCopyFiles(c *model.CommandConfig, app *harness.App, revelPaths *model.RevelContainer) (packageFolders []string, err error) { 110 appImportPath, destPath := c.ImportPath, c.Build.TargetPath 111 112 // Revel and the app are in a directory structure mirroring import path 113 srcPath := filepath.Join(destPath, "src") 114 destBinaryPath := filepath.Join(destPath, filepath.Base(app.BinaryPath)) 115 tmpRevelPath := filepath.Join(srcPath, filepath.FromSlash(model.RevelImportPath)) 116 if err = utils.CopyFile(destBinaryPath, filepath.Join(revelPaths.BasePath, app.BinaryPath)); err != nil { 117 return 118 } 119 utils.MustChmod(destBinaryPath, 0755) 120 121 // Copy the templates from the revel 122 if err = utils.CopyDir(filepath.Join(tmpRevelPath, "conf"), filepath.Join(revelPaths.RevelPath, "conf"), nil); err != nil { 123 return 124 } 125 if err = utils.CopyDir(filepath.Join(tmpRevelPath, "templates"), filepath.Join(revelPaths.RevelPath, "templates"), nil); err != nil { 126 return 127 } 128 129 // Get the folders to be packaged 130 packageFolders = strings.Split(revelPaths.Config.StringDefault("package.folders", "conf,public,app/views"), ",") 131 for i, p := range packageFolders { 132 // Clean spaces, reformat slash to filesystem 133 packageFolders[i] = filepath.FromSlash(strings.TrimSpace(p)) 134 } 135 136 if c.Build.CopySource { 137 err = utils.CopyDir(filepath.Join(srcPath, filepath.FromSlash(appImportPath)), revelPaths.BasePath, nil) 138 if err != nil { 139 return 140 } 141 } else { 142 for _, folder := range packageFolders { 143 err = utils.CopyDir( 144 filepath.Join(srcPath, filepath.FromSlash(appImportPath), folder), 145 filepath.Join(revelPaths.BasePath, folder), 146 nil) 147 if err != nil { 148 return 149 } 150 } 151 } 152 153 return 154 } 155 156 // Based on the section copy over the build modules. 157 func buildCopyModules(c *model.CommandConfig, revelPaths *model.RevelContainer, packageFolders []string, app *harness.App) (err error) { 158 destPath := filepath.Join(c.Build.TargetPath, "src") 159 // Find all the modules used and copy them over. 160 config := revelPaths.Config.Raw() 161 162 // We should only copy over the section of options what the build is targeted for 163 // We will default to prod 164 moduleImportList := []string{} 165 for _, section := range config.Sections() { 166 // If the runmode is defined we will only import modules defined for that run mode 167 if c.Build.Mode != "" && c.Build.Mode != section { 168 continue 169 } 170 options, _ := config.SectionOptions(section) 171 for _, key := range options { 172 if !strings.HasPrefix(key, "module.") { 173 continue 174 } 175 moduleImportPath, _ := config.String(section, key) 176 if moduleImportPath == "" { 177 continue 178 } 179 moduleImportList = append(moduleImportList, moduleImportPath) 180 } 181 } 182 183 // Copy the the paths for each of the modules 184 for _, importPath := range moduleImportList { 185 fsPath := app.PackagePathMap[importPath] 186 utils.Logger.Info("Copy files ", "to", filepath.Join(destPath, importPath), "from", fsPath) 187 if c.Build.CopySource { 188 err = utils.CopyDir(filepath.Join(destPath, importPath), fsPath, nil) 189 if err != nil { 190 return 191 } 192 } else { 193 for _, folder := range packageFolders { 194 err = utils.CopyDir( 195 filepath.Join(destPath, importPath, folder), 196 filepath.Join(fsPath, folder), 197 nil) 198 if err != nil { 199 return 200 } 201 } 202 } 203 } 204 205 return 206 } 207 208 // Write the run scripts for the build. 209 func buildWriteScripts(c *model.CommandConfig, app *harness.App) (err error) { 210 tmplData := map[string]interface{}{ 211 "BinName": filepath.Base(app.BinaryPath), 212 "ImportPath": c.Build.ImportPath, 213 "Mode": c.Build.Mode, 214 } 215 216 err = utils.GenerateTemplate( 217 filepath.Join(c.Build.TargetPath, "run.sh"), 218 PACKAGE_RUN_SH, 219 tmplData, 220 ) 221 if err != nil { 222 return 223 } 224 utils.MustChmod(filepath.Join(c.Build.TargetPath, "run.sh"), 0755) 225 err = utils.GenerateTemplate( 226 filepath.Join(c.Build.TargetPath, "run.bat"), 227 PACKAGE_RUN_BAT, 228 tmplData, 229 ) 230 if err != nil { 231 return 232 } 233 234 fmt.Println("Your application has been built in:", c.Build.TargetPath) 235 236 return 237 } 238 239 // Checks to see if the target folder exists and can be created. 240 func buildSafetyCheck(destPath string) error { 241 // First, verify that it is either already empty or looks like a previous 242 // build (to avoid clobbering anything) 243 if utils.Exists(destPath) && !utils.Empty(destPath) && !utils.Exists(filepath.Join(destPath, "run.sh")) { 244 return utils.NewBuildError("Abort: %s exists and does not look like a build directory.", "path", destPath) 245 } 246 247 if err := os.RemoveAll(destPath); err != nil && !os.IsNotExist(err) { 248 return utils.NewBuildIfError(err, "Remove all error", "path", destPath) 249 } 250 251 if err := os.MkdirAll(destPath, 0777); err != nil { 252 return utils.NewBuildIfError(err, "MkDir all error", "path", destPath) 253 } 254 return nil 255 } 256 257 const PACKAGE_RUN_SH = `#!/bin/sh 258 259 SCRIPTPATH=$(cd "$(dirname "$0")"; pwd) 260 "$SCRIPTPATH/{{.BinName}}" -importPath {{.ImportPath}} -srcPath "$SCRIPTPATH/src" -runMode {{.Mode}} 261 ` 262 263 const PACKAGE_RUN_BAT = `@echo off 264 265 {{.BinName}} -importPath {{.ImportPath}} -srcPath "%CD%\src" -runMode {{.Mode}} 266 `