github.com/jd-ly/cmd@v1.0.10/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 "os" 9 "path/filepath" 10 "strings" 11 12 "fmt" 13 "github.com/jd-ly/cmd/harness" 14 "github.com/jd-ly/cmd/model" 15 "github.com/jd-ly/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 63 appImportPath, destPath, mode := c.ImportPath, c.Build.TargetPath, DefaultRunMode 64 if len(c.Build.Mode) > 0 { 65 mode = c.Build.Mode 66 } 67 68 // Convert target to absolute path 69 c.Build.TargetPath, _ = filepath.Abs(destPath) 70 c.Build.Mode = mode 71 c.Build.ImportPath = appImportPath 72 73 revel_paths, err := model.NewRevelPaths(mode, appImportPath, c.AppPath, model.NewWrappedRevelCallback(nil, c.PackageResolver)) 74 if err != nil { 75 return 76 } 77 78 if err = buildSafetyCheck(destPath); err != nil { 79 return 80 } 81 82 // Ensure the application can be built, this generates the main file 83 app, err := harness.Build(c, revel_paths) 84 if err != nil { 85 return err 86 } 87 // Copy files 88 // Included are: 89 // - run scripts 90 // - binary 91 // - revel 92 // - app 93 94 packageFolders, err := buildCopyFiles(c, app, revel_paths) 95 if err != nil { 96 return 97 } 98 err = buildCopyModules(c, revel_paths, packageFolders, app) 99 if err != nil { 100 return 101 } 102 err = buildWriteScripts(c, app) 103 if err != nil { 104 return 105 } 106 return 107 } 108 109 // Copy the files to the target 110 func buildCopyFiles(c *model.CommandConfig, app *harness.App, revel_paths *model.RevelContainer) (packageFolders []string, err error) { 111 appImportPath, destPath := c.ImportPath, c.Build.TargetPath 112 113 // Revel and the app are in a directory structure mirroring import path 114 srcPath := filepath.Join(destPath, "src") 115 destBinaryPath := filepath.Join(destPath, filepath.Base(app.BinaryPath)) 116 tmpRevelPath := filepath.Join(srcPath, filepath.FromSlash(model.RevelImportPath)) 117 if err = utils.CopyFile(destBinaryPath, filepath.Join(revel_paths.BasePath, app.BinaryPath)); err != nil { 118 return 119 } 120 utils.MustChmod(destBinaryPath, 0755) 121 122 // Copy the templates from the revel 123 if err = utils.CopyDir(filepath.Join(tmpRevelPath, "conf"), filepath.Join(revel_paths.RevelPath, "conf"), nil); err != nil { 124 return 125 } 126 if err = utils.CopyDir(filepath.Join(tmpRevelPath, "templates"), filepath.Join(revel_paths.RevelPath, "templates"), nil); err != nil { 127 return 128 } 129 130 // Get the folders to be packaged 131 packageFolders = strings.Split(revel_paths.Config.StringDefault("package.folders", "conf,public,app/views"), ",") 132 for i, p := range packageFolders { 133 // Clean spaces, reformat slash to filesystem 134 packageFolders[i] = filepath.FromSlash(strings.TrimSpace(p)) 135 } 136 137 if c.Build.CopySource { 138 err = utils.CopyDir(filepath.Join(srcPath, filepath.FromSlash(appImportPath)), revel_paths.BasePath, nil) 139 if err != nil { 140 return 141 } 142 } else { 143 for _, folder := range packageFolders { 144 err = utils.CopyDir( 145 filepath.Join(srcPath, filepath.FromSlash(appImportPath), folder), 146 filepath.Join(revel_paths.BasePath, folder), 147 nil) 148 if err != nil { 149 return 150 } 151 } 152 } 153 154 return 155 } 156 157 // Based on the section copy over the build modules 158 func buildCopyModules(c *model.CommandConfig, revel_paths *model.RevelContainer, packageFolders []string, app *harness.App) (err error) { 159 destPath := filepath.Join(c.Build.TargetPath, "src") 160 // Find all the modules used and copy them over. 161 config := revel_paths.Config.Raw() 162 163 // We should only copy over the section of options what the build is targeted for 164 // We will default to prod 165 moduleImportList := []string{} 166 for _, section := range config.Sections() { 167 // If the runmode is defined we will only import modules defined for that run mode 168 if c.Build.Mode != "" && c.Build.Mode != section { 169 continue 170 } 171 options, _ := config.SectionOptions(section) 172 for _, key := range options { 173 if !strings.HasPrefix(key, "module.") { 174 continue 175 } 176 moduleImportPath, _ := config.String(section, key) 177 if moduleImportPath == "" { 178 continue 179 } 180 moduleImportList = append(moduleImportList, moduleImportPath) 181 182 } 183 } 184 185 // Copy the the paths for each of the modules 186 for _, importPath := range moduleImportList { 187 fsPath := app.PackagePathMap[importPath] 188 utils.Logger.Info("Copy files ", "to", filepath.Join(destPath, importPath), "from", fsPath) 189 if c.Build.CopySource { 190 err = utils.CopyDir(filepath.Join(destPath, importPath), fsPath, nil) 191 if err != nil { 192 return 193 } 194 } else { 195 for _, folder := range packageFolders { 196 err = utils.CopyDir( 197 filepath.Join(destPath, importPath, folder), 198 filepath.Join(fsPath, folder), 199 nil) 200 if err != nil { 201 return 202 } 203 } 204 } 205 } 206 207 return 208 } 209 210 // Write the run scripts for the build 211 func buildWriteScripts(c *model.CommandConfig, app *harness.App) (err error) { 212 tmplData := map[string]interface{}{ 213 "BinName": filepath.Base(app.BinaryPath), 214 "ImportPath": c.Build.ImportPath, 215 "Mode": c.Build.Mode, 216 } 217 218 err = utils.GenerateTemplate( 219 filepath.Join(c.Build.TargetPath, "run.sh"), 220 PACKAGE_RUN_SH, 221 tmplData, 222 ) 223 if err != nil { 224 return 225 } 226 utils.MustChmod(filepath.Join(c.Build.TargetPath, "run.sh"), 0755) 227 err = utils.GenerateTemplate( 228 filepath.Join(c.Build.TargetPath, "run.bat"), 229 PACKAGE_RUN_BAT, 230 tmplData, 231 ) 232 if err != nil { 233 return 234 } 235 236 fmt.Println("Your application has been built in:", c.Build.TargetPath) 237 238 return 239 } 240 241 // Checks to see if the target folder exists and can be created 242 func buildSafetyCheck(destPath string) error { 243 244 // First, verify that it is either already empty or looks like a previous 245 // build (to avoid clobbering anything) 246 if utils.Exists(destPath) && !utils.Empty(destPath) && !utils.Exists(filepath.Join(destPath, "run.sh")) { 247 return utils.NewBuildError("Abort: %s exists and does not look like a build directory.", "path", destPath) 248 } 249 250 if err := os.RemoveAll(destPath); err != nil && !os.IsNotExist(err) { 251 return utils.NewBuildIfError(err, "Remove all error", "path", destPath) 252 } 253 254 if err := os.MkdirAll(destPath, 0777); err != nil { 255 return utils.NewBuildIfError(err, "MkDir all error", "path", destPath) 256 } 257 return nil 258 } 259 260 const PACKAGE_RUN_SH = `#!/bin/sh 261 262 SCRIPTPATH=$(cd "$(dirname "$0")"; pwd) 263 "$SCRIPTPATH/{{.BinName}}" -importPath {{.ImportPath}} -srcPath "$SCRIPTPATH/src" -runMode {{.Mode}} 264 ` 265 const PACKAGE_RUN_BAT = `@echo off 266 267 {{.BinName}} -importPath {{.ImportPath}} -srcPath "%CD%\src" -runMode {{.Mode}} 268 `