github.com/ezbuy/gauge@v0.9.4-0.20171013092048-7ac5bd3931cd/build/make.go (about) 1 // Copyright 2015 ThoughtWorks, Inc. 2 3 // This file is part of Gauge. 4 5 // Gauge is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 10 // Gauge is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU General Public License for more details. 14 15 // You should have received a copy of the GNU General Public License 16 // along with Gauge. If not, see <http://www.gnu.org/licenses/>. 17 18 package main 19 20 import ( 21 "bytes" 22 "flag" 23 "fmt" 24 "log" 25 "os" 26 "os/exec" 27 "path/filepath" 28 "runtime" 29 "strings" 30 "time" 31 32 "github.com/getgauge/common" 33 "github.com/getgauge/gauge/version" 34 ) 35 36 const ( 37 CGO_ENABLED = "CGO_ENABLED" 38 GOARCH = "GOARCH" 39 GOOS = "GOOS" 40 X86 = "386" 41 X86_64 = "amd64" 42 darwin = "darwin" 43 linux = "linux" 44 windows = "windows" 45 bin = "bin" 46 gauge = "gauge" 47 deploy = "deploy" 48 installShellScript = "install.sh" 49 CC = "CC" 50 pkg = ".pkg" 51 packagesBuild = "packagesbuild" 52 nightlyDatelayout = "2006-01-02" 53 ) 54 55 var darwinPackageProject = filepath.Join("build", "install", "macosx", "gauge-pkg.pkgproj") 56 57 var deployDir = filepath.Join(deploy, gauge) 58 59 func runProcess(command string, arg ...string) { 60 cmd := exec.Command(command, arg...) 61 cmd.Stdout = os.Stdout 62 cmd.Stderr = os.Stderr 63 log.Printf("Execute %v\n", cmd.Args) 64 err := cmd.Run() 65 if err != nil { 66 panic(err) 67 } 68 } 69 70 func runCommand(command string, arg ...string) (string, error) { 71 cmd := exec.Command(command, arg...) 72 bytes, err := cmd.Output() 73 return strings.TrimSpace(fmt.Sprintf("%s", bytes)), err 74 } 75 76 func signExecutable(exeFilePath string, certFilePath string, certFilePwd string) { 77 if getGOOS() == windows { 78 if certFilePath != "" && certFilePwd != "" { 79 log.Printf("Signing: %s", exeFilePath) 80 runProcess("signtool", "sign", "/f", certFilePath, "/p", certFilePwd, exeFilePath) 81 } else { 82 log.Printf("No certificate file passed. Executable won't be signed.") 83 } 84 } 85 } 86 87 var buildMetadata string 88 var commitHash string 89 90 func getBuildVersion() string { 91 if buildMetadata != "" { 92 return fmt.Sprintf("%s.%s", version.CurrentGaugeVersion.String(), buildMetadata) 93 } 94 return version.CurrentGaugeVersion.String() 95 } 96 97 func compileGauge() { 98 executablePath := getGaugeExecutablePath(gauge) 99 ldflags := fmt.Sprintf("-X github.com/getgauge/gauge/version.BuildMetadata=%s -X github.com/getgauge/gauge/version.CommitHash=%s", buildMetadata, commitHash) 100 args := []string{ 101 "build", 102 fmt.Sprintf("-gcflags=-trimpath=%s", os.Getenv("GOPATH")), 103 fmt.Sprintf("-asmflags=-trimpath=%s", os.Getenv("GOPATH")), 104 "-ldflags", ldflags, "-o", executablePath, 105 } 106 runProcess("go", args...) 107 } 108 109 func runTests(coverage bool) { 110 if coverage { 111 runProcess("go", "test", "-covermode=count", "-coverprofile=count.out") 112 if coverage { 113 runProcess("go", "tool", "cover", "-html=count.out") 114 } 115 } else { 116 runProcess("go", "test", "./...", "-v") 117 } 118 } 119 120 // key will be the source file and value will be the target 121 func installFiles(files map[string]string, installDir string) { 122 for src, dst := range files { 123 base := filepath.Base(src) 124 installDst := filepath.Join(installDir, dst) 125 log.Printf("Install %s -> %s\n", src, installDst) 126 stat, err := os.Stat(src) 127 if err != nil { 128 panic(err) 129 } 130 if stat.IsDir() { 131 _, err = common.MirrorDir(src, installDst) 132 } else { 133 err = common.MirrorFile(src, filepath.Join(installDst, base)) 134 } 135 if err != nil { 136 panic(err) 137 } 138 } 139 } 140 141 func copyGaugeBinaries(installPath string) { 142 files := make(map[string]string) 143 files[getGaugeExecutablePath(gauge)] = bin 144 files = getInstallScripts(files) 145 installFiles(files, installPath) 146 } 147 148 func getInstallScripts(files map[string]string) map[string]string { 149 if (getGOOS() == darwin || getGOOS() == linux) && (*distro) { 150 files[filepath.Join("build", "install", installShellScript)] = "" 151 } 152 return files 153 } 154 155 func setEnv(envVariables map[string]string) { 156 for k, v := range envVariables { 157 os.Setenv(k, v) 158 } 159 } 160 161 var test = flag.Bool("test", false, "Run the test cases") 162 var coverage = flag.Bool("coverage", false, "Run the test cases and show the coverage") 163 var install = flag.Bool("install", false, "Install to the specified prefix") 164 var nightly = flag.Bool("nightly", false, "Add nightly build information") 165 var gaugeInstallPrefix = flag.String("prefix", "", "Specifies the prefix where gauge files will be installed") 166 var allPlatforms = flag.Bool("all-platforms", false, "Compiles for all platforms windows, linux, darwin both x86 and x86_64") 167 var targetLinux = flag.Bool("target-linux", false, "Compiles for linux only, both x86 and x86_64") 168 var binDir = flag.String("bin-dir", "", "Specifies OS_PLATFORM specific binaries to install when cross compiling") 169 var distro = flag.Bool("distro", false, "Create gauge distributable") 170 var skipWindowsDistro = flag.Bool("skip-windows", false, "Skips creation of windows distributable on unix machines while cross platform compilation") 171 var certFile = flag.String("certFile", "", "Should be passed for signing the windows installer along with the password (certFilePwd)") 172 var certFilePwd = flag.String("certFilePwd", "", "Password for certificate that will be used to sign the windows installer") 173 174 // Defines all the compile targets 175 // Each target name is the directory name 176 var ( 177 platformEnvs = []map[string]string{ 178 map[string]string{GOARCH: X86, GOOS: darwin, CGO_ENABLED: "0"}, 179 map[string]string{GOARCH: X86_64, GOOS: darwin, CGO_ENABLED: "0"}, 180 map[string]string{GOARCH: X86, GOOS: linux, CGO_ENABLED: "0"}, 181 map[string]string{GOARCH: X86_64, GOOS: linux, CGO_ENABLED: "0"}, 182 map[string]string{GOARCH: X86, GOOS: windows, CC: "i586-mingw32-gcc", CGO_ENABLED: "1"}, 183 map[string]string{GOARCH: X86_64, GOOS: windows, CC: "x86_64-w64-mingw32-gcc", CGO_ENABLED: "1"}, 184 } 185 osDistroMap = map[string]distroFunc{windows: createWindowsDistro, linux: createLinuxPackage, darwin: createDarwinPackage} 186 ) 187 188 func main() { 189 flag.Parse() 190 commitHash = revParseHead() 191 if *nightly { 192 buildMetadata = fmt.Sprintf("nightly-%s", time.Now().Format(nightlyDatelayout)) 193 } 194 fmt.Println("Build: " + buildMetadata) 195 if *test { 196 runTests(*coverage) 197 } else if *install { 198 installGauge() 199 } else if *distro { 200 createGaugeDistributables(*allPlatforms) 201 } else { 202 if *allPlatforms { 203 crossCompileGauge() 204 } else { 205 compileGauge() 206 } 207 } 208 } 209 210 func revParseHead() string { 211 if _, err := os.Stat(".git"); err != nil { 212 return "" 213 } 214 cmd := exec.Command("git", "rev-parse", "--short", "HEAD") 215 var hash bytes.Buffer 216 cmd.Stdout = &hash 217 err := cmd.Run() 218 if err != nil { 219 log.Fatal(err) 220 } 221 return fmt.Sprintf("%s", strings.TrimSpace(hash.String())) 222 } 223 224 func filteredPlatforms() []map[string]string { 225 filteredPlatformEnvs := platformEnvs[:0] 226 for _, x := range platformEnvs { 227 if *targetLinux { 228 if x[GOOS] == linux { 229 filteredPlatformEnvs = append(filteredPlatformEnvs, x) 230 } 231 } else { 232 filteredPlatformEnvs = append(filteredPlatformEnvs, x) 233 } 234 } 235 return filteredPlatformEnvs 236 } 237 238 func crossCompileGauge() { 239 for _, platformEnv := range filteredPlatforms() { 240 setEnv(platformEnv) 241 log.Printf("Compiling for platform => OS:%s ARCH:%s \n", platformEnv[GOOS], platformEnv[GOARCH]) 242 compileGauge() 243 } 244 } 245 246 func installGauge() { 247 updateGaugeInstallPrefix() 248 copyGaugeBinaries(deployDir) 249 if _, err := common.MirrorDir(filepath.Join(deployDir, bin), filepath.Join(*gaugeInstallPrefix, bin)); err != nil { 250 panic(fmt.Sprintf("Could not install gauge : %s", err)) 251 } 252 } 253 254 func createGaugeDistributables(forAllPlatforms bool) { 255 if forAllPlatforms { 256 for _, platformEnv := range filteredPlatforms() { 257 setEnv(platformEnv) 258 log.Printf("Creating distro for platform => OS:%s ARCH:%s \n", platformEnv[GOOS], platformEnv[GOARCH]) 259 createDistro() 260 } 261 } else { 262 createDistro() 263 } 264 } 265 266 type distroFunc func() 267 268 func createDistro() { 269 osDistroMap[getGOOS()]() 270 } 271 272 func createWindowsDistro() { 273 if !*skipWindowsDistro { 274 createWindowsInstaller() 275 } 276 } 277 278 func createWindowsInstaller() { 279 pName := packageName() 280 distroDir, err := filepath.Abs(filepath.Join(deploy, pName)) 281 installerFileName := filepath.Join(filepath.Dir(distroDir), pName) 282 if err != nil { 283 panic(err) 284 } 285 copyGaugeBinaries(distroDir) 286 runProcess("makensis.exe", 287 fmt.Sprintf("/DPRODUCT_VERSION=%s", getBuildVersion()), 288 fmt.Sprintf("/DGAUGE_DISTRIBUTABLES_DIR=%s", distroDir), 289 fmt.Sprintf("/DOUTPUT_FILE_NAME=%s.exe", installerFileName), 290 filepath.Join("build", "install", "windows", "gauge-install.nsi")) 291 createZipFromUtil(deploy, pName, pName) 292 os.RemoveAll(distroDir) 293 signExecutable(installerFileName+".exe", *certFile, *certFilePwd) 294 } 295 296 func createDarwinPackage() { 297 distroDir := filepath.Join(deploy, gauge) 298 copyGaugeBinaries(distroDir) 299 createZipFromUtil(deploy, gauge, packageName()) 300 runProcess(packagesBuild, "-v", darwinPackageProject) 301 runProcess("mv", filepath.Join(deploy, gauge+pkg), filepath.Join(deploy, fmt.Sprintf("%s-%s-%s.%s%s", gauge, getBuildVersion(), getGOOS(), getPackageArchSuffix(), pkg))) 302 os.RemoveAll(distroDir) 303 } 304 305 func createLinuxPackage() { 306 distroDir := filepath.Join(deploy, packageName()) 307 copyGaugeBinaries(distroDir) 308 createZipFromUtil(deploy, packageName(), packageName()) 309 os.RemoveAll(distroDir) 310 } 311 312 func packageName() string { 313 return fmt.Sprintf("%s-%s-%s.%s", gauge, getBuildVersion(), getGOOS(), getPackageArchSuffix()) 314 } 315 316 func removeUnwatedFiles(dir, currentOS string) error { 317 fileList := []string{ 318 ".DS_STORE", 319 ".localized", 320 "$RECYCLE.BIN", 321 } 322 if currentOS == "windows" { 323 fileList = append(fileList, []string{ 324 "desktop.ini", 325 "Thumbs.db", 326 }...) 327 } 328 for _, f := range fileList { 329 err := os.RemoveAll(filepath.Join(dir, f)) 330 if err != nil && !os.IsNotExist(err) { 331 return err 332 } 333 } 334 return nil 335 } 336 337 func createZipFromUtil(dir, zipDir, pkgName string) { 338 wd, err := os.Getwd() 339 if err != nil { 340 panic(err) 341 } 342 absdir, err := filepath.Abs(dir) 343 if err != nil { 344 panic(err) 345 } 346 currentOS := getGOOS() 347 348 windowsZipScript := filepath.Join(wd, "build", "create_windows_zipfile.ps1") 349 350 err = removeUnwatedFiles(filepath.Join(dir, zipDir), currentOS) 351 352 if err != nil { 353 panic(fmt.Sprintf("Failed to cleanup unwanted file(s): %s", err)) 354 } 355 356 err = os.Chdir(filepath.Join(dir, zipDir)) 357 if err != nil { 358 panic(fmt.Sprintf("Failed to change directory: %s", err)) 359 } 360 361 zipcmd := "zip" 362 zipargs := []string{"-r", filepath.Join("..", pkgName+".zip"), "."} 363 if currentOS == "windows" { 364 zipcmd = "powershell.exe" 365 zipargs = []string{"-noprofile", "-executionpolicy", "bypass", "-file", windowsZipScript, filepath.Join(absdir, zipDir), filepath.Join(absdir, pkgName+".zip")} 366 } 367 output, err := runCommand(zipcmd, zipargs...) 368 fmt.Println(output) 369 if err != nil { 370 panic(fmt.Sprintf("Failed to zip: %s", err)) 371 } 372 os.Chdir(wd) 373 } 374 375 func updateGaugeInstallPrefix() { 376 if *gaugeInstallPrefix == "" { 377 if runtime.GOOS == "windows" { 378 *gaugeInstallPrefix = os.Getenv("PROGRAMFILES") 379 if *gaugeInstallPrefix == "" { 380 panic(fmt.Errorf("Failed to find programfiles")) 381 } 382 *gaugeInstallPrefix = filepath.Join(*gaugeInstallPrefix, gauge) 383 } else { 384 *gaugeInstallPrefix = "/usr/local" 385 } 386 } 387 } 388 389 func getGaugeExecutablePath(file string) string { 390 return filepath.Join(getBinDir(), getExecutableName(file)) 391 } 392 393 func getBinDir() string { 394 if *binDir != "" { 395 return *binDir 396 } 397 return filepath.Join(bin, fmt.Sprintf("%s_%s", getGOOS(), getGOARCH())) 398 } 399 400 func getExecutableName(file string) string { 401 if getGOOS() == windows { 402 return file + ".exe" 403 } 404 return file 405 } 406 407 func getGOARCH() string { 408 goArch := os.Getenv(GOARCH) 409 if goArch == "" { 410 goArch = runtime.GOARCH 411 } 412 return goArch 413 } 414 415 func getGOOS() string { 416 goOS := os.Getenv(GOOS) 417 if goOS == "" { 418 goOS = runtime.GOOS 419 } 420 return goOS 421 } 422 423 func getPackageArchSuffix() string { 424 if strings.HasSuffix(*binDir, "386") { 425 return "x86" 426 } 427 428 if strings.HasSuffix(*binDir, "amd64") { 429 return "x86_64" 430 } 431 432 if arch := getGOARCH(); arch == X86 { 433 return "x86" 434 } 435 return "x86_64" 436 }