From 27584d337f392f9e5eb8c300416c057c386db89f Mon Sep 17 00:00:00 2001 From: jld3103 Date: Wed, 11 Sep 2019 21:12:48 +0200 Subject: [PATCH 1/9] Initial cross-compiling support --- cmd/build.go | 223 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 188 insertions(+), 35 deletions(-) diff --git a/cmd/build.go b/cmd/build.go index 94c93575..efc097e6 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -2,6 +2,7 @@ package cmd import ( "fmt" + "io/ioutil" "os" "os/exec" "path/filepath" @@ -31,6 +32,14 @@ var ( const buildPath = "go" +const mingwGccBinName = "x86_64-w64-mingw32-gcc" +const mingwGppBinName = "x86_64-w64-mingw32-g++" +const clangBinName = "o32-clang" + +var crossCompile = false +var windowsCrossCompileToolsAvailable = false +var darwinCrossCompileToolsAvailable = false + func init() { buildCmd.PersistentFlags().StringVarP(&buildTarget, "target", "t", "lib/main_desktop.dart", "The main entry-point file of the application.") buildCmd.PersistentFlags().StringVarP(&buildManifest, "manifest", "m", "pubspec.yaml", "Flutter manifest file of the application.") @@ -146,9 +155,27 @@ func outputBinaryPath(projectName string, targetOS string) string { return outputBinaryPath } -func build(projectName string, targetOS string, vmArguments []string) { - if targetOS != runtime.GOOS { - fmt.Println("hover: Cross-compiling is currently not supported") +func dockerBuild(projectName string, targetOS string, vmArguments []string) { + dockerBin, err := exec.LookPath("docker") + if err != nil { + fmt.Printf("hover: Failed to lookup `docker` executable. Please install Docker.\nhttps://docs.docker.com/install/") + os.Exit(1) + } + tmpDir, err := ioutil.TempDir("", "hover-build-cc") + userCacheDir, err := os.UserCacheDir() + if err != nil { + fmt.Printf("Cannot get the path for the system cache directory %s", err) + os.Exit(1) + } + goPath := filepath.Join(userCacheDir, "hover-cc") + err = os.MkdirAll(goPath, 0755) + if err != nil { + fmt.Printf("Cannot create the hover-cc GOPATH under the system cache directory %s", err) + os.Exit(1) + } + pkgRootDir, err := filepath.Abs(buildPath) + if err != nil { + fmt.Printf("Cannot get the path for current directory %s", err) os.Exit(1) } var engineCachePath string @@ -157,6 +184,85 @@ func build(projectName string, targetOS string, vmArguments []string) { } else { engineCachePath = enginecache.ValidateOrUpdateEngine(targetOS) } + dockerFilePath, err := filepath.Abs(filepath.Join(tmpDir, "Dockerfile")) + if err != nil { + fmt.Printf("hover: Failed to resolve absolute path for Dockerfile %s: %v\n", dockerFilePath, err) + os.Exit(1) + } + + dockerFile, err := os.Create(dockerFilePath) + if err != nil { + fmt.Printf("hover: Failed to create Dockerfile %s: %v\n", dockerFilePath, err) + os.Exit(1) + } + dockerFileContent := []string{ + "FROM dockercore/golang-cross", + "WORKDIR /app", + "CMD " + strings.Join(buildCommand(targetOS, vmArguments, "build/outputs/"+targetOS+"/"+outputBinaryName(projectName, targetOS)), " "), + } + for _, env := range buildEnv(targetOS, "/engine") { + dockerFileContent = append(dockerFileContent, "ENV "+env) + } + + for _, line := range dockerFileContent { + if _, err := dockerFile.WriteString(line + "\n"); err != nil { + fmt.Printf("hover: Could not write Dockerfile: %v\n", err) + os.Exit(1) + } + } + err = dockerFile.Close() + if err != nil { + fmt.Printf("hover: Could not close Dockerfile: %v\n", err) + os.Exit(1) + } + dockerBuildCmd := exec.Command(dockerBin, "build", "-t", "hover-build-cc", ".") + dockerBuildCmd.Stderr = os.Stderr + dockerBuildCmd.Dir = tmpDir + err = dockerBuildCmd.Run() + if err != nil { + fmt.Printf("hover: Docker build failed: %v\n", err) + os.Exit(1) + } + + fmt.Println("hover: Cross-Compiling 'go-flutter' and plugins using docker") + + outputPath, err := filepath.Abs(filepath.Join(buildPath, "build", "outputs")) + dockerRunCmd := exec.Command(dockerBin, "run", "-e", "USERID=$UID", "-v", goPath+":/go", "-v", pkgRootDir+":/app", "-v", engineCachePath+":/engine", "-v", outputPath+":/app/build/outputs", "hover-build-cc") + dockerRunCmd.Stderr = os.Stderr + dockerRunCmd.Stdout = os.Stdout + dockerRunCmd.Dir = tmpDir + err = dockerRunCmd.Run() + if err != nil { + fmt.Printf("hover: Docker run failed: %v\n", err) + os.Exit(1) + } + err = os.RemoveAll(tmpDir) + if err != nil { + fmt.Printf("hover: Could not remove temporary build folder: %v\n", err) + os.Exit(1) + } +} + +func build(projectName string, targetOS string, vmArguments []string) { + mingwGccBin, err := exec.LookPath(mingwGccBinName) + mingwGppBin, err := exec.LookPath(mingwGppBinName) + clangBin, err := exec.LookPath(clangBinName) + crossCompile = targetOS != runtime.GOOS + if crossCompile { + fmt.Printf("hover: Cross-compiling for %s using docker is very experimental\n", targetOS) + if mingwGccBin != "" && mingwGppBin != "" { + windowsCrossCompileToolsAvailable = true + } + if clangBin != "" { + darwinCrossCompileToolsAvailable = true + } + } + var engineCachePath string + if buildCachePath != "" { + engineCachePath = enginecache.ValidateOrUpdateEngineAtPath(targetOS, buildCachePath) + } else { + engineCachePath = enginecache.ValidateOrUpdateEngine(targetOS) + } if !buildOmitFlutterBundle && !buildOmitEmbedder { err := os.RemoveAll(outputDirectoryPath(targetOS)) @@ -167,7 +273,7 @@ func build(projectName string, targetOS string, vmArguments []string) { } } - err := os.MkdirAll(outputDirectoryPath(targetOS), 0775) + err = os.MkdirAll(outputDirectoryPath(targetOS), 0775) if err != nil { fmt.Printf("hover: failed to create output directory %s: %v\n", outputDirectoryPath(targetOS), err) os.Exit(1) @@ -265,19 +371,6 @@ func build(projectName string, targetOS string, vmArguments []string) { return } - var cgoLdflags string - switch targetOS { - case "darwin": - cgoLdflags = fmt.Sprintf("-F%s -Wl,-rpath,@executable_path", engineCachePath) - case "linux": - cgoLdflags = fmt.Sprintf("-L%s", engineCachePath) - case "windows": - cgoLdflags = fmt.Sprintf("-L%s", engineCachePath) - default: - fmt.Printf("hover: Target platform %s is not supported, cgo_ldflags not implemented.\n", targetOS) - os.Exit(1) - } - wd, err := os.Getwd() if err != nil { fmt.Printf("hover: Failed to get working dir: %v\n", err) @@ -323,28 +416,16 @@ func build(projectName string, targetOS string, vmArguments []string) { } } - var ldflags []string - if !buildDebug { - vmArguments = append(vmArguments, "--disable-dart-asserts") - vmArguments = append(vmArguments, "--disable-observatory") - - if targetOS == "windows" { - ldflags = append(ldflags, "-H=windowsgui") - } - ldflags = append(ldflags, "-s") - ldflags = append(ldflags, "-w") + if crossCompile { + dockerBuild(projectName, targetOS, vmArguments) + return } - ldflags = append(ldflags, fmt.Sprintf("-X main.vmArguments=%s", strings.Join(vmArguments, ";"))) - cmdGoBuild := exec.Command(goBin, "build", - "-o", outputBinaryPath(projectName, targetOS), - fmt.Sprintf("-ldflags=%s", strings.Join(ldflags, " ")), - dotSlash+"cmd", - ) + buildCommandString := buildCommand(targetOS, vmArguments, outputBinaryPath(projectName, targetOS)) + cmdGoBuild := exec.Command(buildCommandString[0], buildCommandString[1:]...) cmdGoBuild.Dir = filepath.Join(wd, buildPath) cmdGoBuild.Env = append(os.Environ(), - "GO111MODULE=on", - "CGO_LDFLAGS="+cgoLdflags, + buildEnv(targetOS, engineCachePath)..., ) cmdGoBuild.Stderr = os.Stderr @@ -357,3 +438,75 @@ func build(projectName string, targetOS string, vmArguments []string) { os.Exit(1) } } + +func buildEnv(targetOS string, engineCachePath string) []string { + var cgoLdflags string + switch targetOS { + case "darwin": + cgoLdflags = fmt.Sprintf("-F%s -Wl,-rpath,@executable_path", engineCachePath) + case "linux": + cgoLdflags = fmt.Sprintf("-L%s", engineCachePath) + case "windows": + cgoLdflags = fmt.Sprintf("-L%s", engineCachePath) + default: + fmt.Printf("hover: Target platform %s is not supported, cgo_ldflags not implemented.\n", targetOS) + os.Exit(1) + } + cgoLdflagsString := "" + if crossCompile { + cgoLdflagsString = "\"" + } + cgoLdflagsString = cgoLdflagsString + cgoLdflags + if crossCompile { + cgoLdflagsString = cgoLdflagsString + "\"" + } + env := []string{ + "GO111MODULE=on", + "CGO_LDFLAGS=" + cgoLdflagsString, + "GOOS=" + targetOS, + "GOARCH=amd64", + "CGO_ENABLED=1", + } + if crossCompile { + if targetOS == "windows" { + env = append(env, + "CC="+mingwGccBinName, + "CXX="+mingwGppBinName, + ) + } + if targetOS == "darwin" { + env = append(env, + "CC="+clangBinName, + "CXX="+clangBinName, + ) + } + } + return env +} + +func buildCommand(targetOS string, vmArguments []string, outputBinaryPath string) []string { + var ldflags []string + if !buildDebug { + vmArguments = append(vmArguments, "--disable-dart-asserts") + vmArguments = append(vmArguments, "--disable-observatory") + + if targetOS == "windows" { + ldflags = append(ldflags, "-H=windowsgui") + } + ldflags = append(ldflags, "-s") + ldflags = append(ldflags, "-w") + } + ldflags = append(ldflags, fmt.Sprintf("-X main.vmArguments=%s", strings.Join(vmArguments, ";"))) + outputCommand := []string{ + "go", + "build", + "-o", outputBinaryPath, + } + if crossCompile { + outputCommand = append(outputCommand, fmt.Sprintf("-ldflags=\"%s\"", strings.Join(ldflags, " "))) + } else { + outputCommand = append(outputCommand, fmt.Sprintf("-ldflags=%s", strings.Join(ldflags, " "))) + } + outputCommand = append(outputCommand, dotSlash+"cmd") + return outputCommand +} From 3ecbbd7b07bf738b83f90dbe04f6e097fb55e5c2 Mon Sep 17 00:00:00 2001 From: jld3103 Date: Thu, 12 Sep 2019 11:16:44 +0200 Subject: [PATCH 2/9] Enable cache for docker builds --- cmd/build.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cmd/build.go b/cmd/build.go index efc097e6..b1dd6a2c 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -227,7 +227,7 @@ func dockerBuild(projectName string, targetOS string, vmArguments []string) { fmt.Println("hover: Cross-Compiling 'go-flutter' and plugins using docker") outputPath, err := filepath.Abs(filepath.Join(buildPath, "build", "outputs")) - dockerRunCmd := exec.Command(dockerBin, "run", "-e", "USERID=$UID", "-v", goPath+":/go", "-v", pkgRootDir+":/app", "-v", engineCachePath+":/engine", "-v", outputPath+":/app/build/outputs", "hover-build-cc") + dockerRunCmd := exec.Command(dockerBin, "run", "-e", "USERID=$UID", "-v", goPath+":/go", "-v", pkgRootDir+":/app", "-v", engineCachePath+":/engine", "-v", outputPath+":/app/build/outputs", "-v", filepath.Join(userCacheDir, "go-build")+":/cache", "hover-build-cc") dockerRunCmd.Stderr = os.Stderr dockerRunCmd.Stdout = os.Stdout dockerRunCmd.Dir = tmpDir @@ -468,6 +468,9 @@ func buildEnv(targetOS string, engineCachePath string) []string { "CGO_ENABLED=1", } if crossCompile { + env = append(env, + "GOCACHE=/cache", + ) if targetOS == "windows" { env = append(env, "CC="+mingwGccBinName, @@ -501,6 +504,7 @@ func buildCommand(targetOS string, vmArguments []string, outputBinaryPath string "go", "build", "-o", outputBinaryPath, + "-v", } if crossCompile { outputCommand = append(outputCommand, fmt.Sprintf("-ldflags=\"%s\"", strings.Join(ldflags, " "))) From b6d9bb25c31b3948ad6748fb9fd3a4efe64efd05 Mon Sep 17 00:00:00 2001 From: jld3103 Date: Thu, 12 Sep 2019 11:49:40 +0200 Subject: [PATCH 3/9] Fix cross-builds for linux --- cmd/build.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd/build.go b/cmd/build.go index b1dd6a2c..8fabf469 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -197,6 +197,7 @@ func dockerBuild(projectName string, targetOS string, vmArguments []string) { } dockerFileContent := []string{ "FROM dockercore/golang-cross", + "RUN apt-get install libgl1-mesa-dev xorg-dev -y", "WORKDIR /app", "CMD " + strings.Join(buildCommand(targetOS, vmArguments, "build/outputs/"+targetOS+"/"+outputBinaryName(projectName, targetOS)), " "), } @@ -217,6 +218,7 @@ func dockerBuild(projectName string, targetOS string, vmArguments []string) { } dockerBuildCmd := exec.Command(dockerBin, "build", "-t", "hover-build-cc", ".") dockerBuildCmd.Stderr = os.Stderr + dockerBuildCmd.Stdout = os.Stdout dockerBuildCmd.Dir = tmpDir err = dockerBuildCmd.Run() if err != nil { From ad1da4cf0e047871b4d1218463c282428f0c7868 Mon Sep 17 00:00:00 2001 From: jld3103 Date: Thu, 12 Sep 2019 11:50:21 +0200 Subject: [PATCH 4/9] Add documentation for cross-compiling --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 12d9edc9..d47ec2e5 100644 --- a/README.md +++ b/README.md @@ -81,13 +81,15 @@ If you want to integrate go-flutter with VSCode, read this [issue](https://githu ### Build standalone application -To create a standalone debug build run this command: +To create a standalone release build run this command: ```bash hover build linux # or darwin or windows ``` +You can create a build for any of the supported OSs using cross-compiling which needs [Docker to be installed](https://docs.docker.com/install/). +Then just run the command from above and it will do everything for you. -The output will be in `go/build/outputs/linux` or windows or darwin depending on your OS. Hover does not yet support cross-compilation. +The output will be in `go/build/outputs/linux` or windows or darwin. To start the binary: (replace `yourApplicationName` with your app name) From b154e581322a8ba2de05e2a517e6575b2c319831 Mon Sep 17 00:00:00 2001 From: jld3103 Date: Tue, 17 Sep 2019 17:06:19 +0200 Subject: [PATCH 5/9] Fix requested code changes --- README.md | 2 +- cmd/build.go | 111 +++++++++++++++++++++------------------------------ 2 files changed, 47 insertions(+), 66 deletions(-) diff --git a/README.md b/README.md index d47ec2e5..78ec6007 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ If you want to integrate go-flutter with VSCode, read this [issue](https://githu ### Build standalone application -To create a standalone release build run this command: +To create a standalone release (JIT mode) build run this command: ```bash hover build linux # or darwin or windows diff --git a/cmd/build.go b/cmd/build.go index 8fabf469..469ddca4 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -2,7 +2,6 @@ package cmd import ( "fmt" - "io/ioutil" "os" "os/exec" "path/filepath" @@ -33,12 +32,10 @@ var ( const buildPath = "go" const mingwGccBinName = "x86_64-w64-mingw32-gcc" -const mingwGppBinName = "x86_64-w64-mingw32-g++" const clangBinName = "o32-clang" var crossCompile = false -var windowsCrossCompileToolsAvailable = false -var darwinCrossCompileToolsAvailable = false +var engineCachePath string func init() { buildCmd.PersistentFlags().StringVarP(&buildTarget, "target", "t", "lib/main_desktop.dart", "The main entry-point file of the application.") @@ -161,65 +158,65 @@ func dockerBuild(projectName string, targetOS string, vmArguments []string) { fmt.Printf("hover: Failed to lookup `docker` executable. Please install Docker.\nhttps://docs.docker.com/install/") os.Exit(1) } - tmpDir, err := ioutil.TempDir("", "hover-build-cc") + crossCompilingDir, err := filepath.Abs(filepath.Join(buildPath, "cross-compiling", targetOS)) + err = os.MkdirAll(crossCompilingDir, 0755) + if err != nil { + fmt.Printf("hover: Cannot create the cross-compiling directory: %v\n", err) + os.Exit(1) + } userCacheDir, err := os.UserCacheDir() if err != nil { - fmt.Printf("Cannot get the path for the system cache directory %s", err) + fmt.Printf("hover: Cannot get the path for the system cache directory: %v\n", err) os.Exit(1) } goPath := filepath.Join(userCacheDir, "hover-cc") err = os.MkdirAll(goPath, 0755) if err != nil { - fmt.Printf("Cannot create the hover-cc GOPATH under the system cache directory %s", err) + fmt.Printf("hover: Cannot create the hover-cc GOPATH under the system cache directory: %v\n", err) os.Exit(1) } - pkgRootDir, err := filepath.Abs(buildPath) + wd, err := os.Getwd() if err != nil { - fmt.Printf("Cannot get the path for current directory %s", err) + fmt.Printf("hover: Cannot get the path for current directory %s", err) os.Exit(1) } - var engineCachePath string - if buildCachePath != "" { - engineCachePath = enginecache.ValidateOrUpdateEngineAtPath(targetOS, buildCachePath) - } else { - engineCachePath = enginecache.ValidateOrUpdateEngine(targetOS) - } - dockerFilePath, err := filepath.Abs(filepath.Join(tmpDir, "Dockerfile")) + dockerFilePath, err := filepath.Abs(filepath.Join(crossCompilingDir, "Dockerfile")) if err != nil { fmt.Printf("hover: Failed to resolve absolute path for Dockerfile %s: %v\n", dockerFilePath, err) os.Exit(1) } + if _, err := os.Stat(dockerFilePath); os.IsNotExist(err) { + dockerFile, err := os.Create(dockerFilePath) + if err != nil { + fmt.Printf("hover: Failed to create Dockerfile %s: %v\n", dockerFilePath, err) + os.Exit(1) + } + dockerFileContent := []string{ + "FROM dockercore/golang-cross", + "RUN apt-get install libgl1-mesa-dev xorg-dev -y", + "WORKDIR /app/go", + "CMD " + strings.Join(buildCommand(targetOS, vmArguments, "build/outputs/"+targetOS+"/"+outputBinaryName(projectName, targetOS)), " "), + } + for _, env := range buildEnv(targetOS, "/engine") { + dockerFileContent = append(dockerFileContent, "ENV "+env) + } - dockerFile, err := os.Create(dockerFilePath) - if err != nil { - fmt.Printf("hover: Failed to create Dockerfile %s: %v\n", dockerFilePath, err) - os.Exit(1) - } - dockerFileContent := []string{ - "FROM dockercore/golang-cross", - "RUN apt-get install libgl1-mesa-dev xorg-dev -y", - "WORKDIR /app", - "CMD " + strings.Join(buildCommand(targetOS, vmArguments, "build/outputs/"+targetOS+"/"+outputBinaryName(projectName, targetOS)), " "), - } - for _, env := range buildEnv(targetOS, "/engine") { - dockerFileContent = append(dockerFileContent, "ENV "+env) - } - - for _, line := range dockerFileContent { - if _, err := dockerFile.WriteString(line + "\n"); err != nil { - fmt.Printf("hover: Could not write Dockerfile: %v\n", err) + for _, line := range dockerFileContent { + if _, err := dockerFile.WriteString(line + "\n"); err != nil { + fmt.Printf("hover: Could not write Dockerfile: %v\n", err) + os.Exit(1) + } + } + err = dockerFile.Close() + if err != nil { + fmt.Printf("hover: Could not close Dockerfile: %v\n", err) os.Exit(1) } + fmt.Printf("hover: A Dockerfile for cross-compiling for %s has bee created at %s. You can add it to git.\n", targetOS, filepath.Join(buildPath, "cross-compiling", targetOS)) } - err = dockerFile.Close() - if err != nil { - fmt.Printf("hover: Could not close Dockerfile: %v\n", err) - os.Exit(1) - } - dockerBuildCmd := exec.Command(dockerBin, "build", "-t", "hover-build-cc", ".") + dockerBuildCmd := exec.Command(dockerBin, "build", "-t", "hover-build-cc-"+targetOS, ".") dockerBuildCmd.Stderr = os.Stderr - dockerBuildCmd.Stdout = os.Stdout - dockerBuildCmd.Dir = tmpDir + dockerBuildCmd.Dir = crossCompilingDir err = dockerBuildCmd.Run() if err != nil { fmt.Printf("hover: Docker build failed: %v\n", err) @@ -229,37 +226,21 @@ func dockerBuild(projectName string, targetOS string, vmArguments []string) { fmt.Println("hover: Cross-Compiling 'go-flutter' and plugins using docker") outputPath, err := filepath.Abs(filepath.Join(buildPath, "build", "outputs")) - dockerRunCmd := exec.Command(dockerBin, "run", "-e", "USERID=$UID", "-v", goPath+":/go", "-v", pkgRootDir+":/app", "-v", engineCachePath+":/engine", "-v", outputPath+":/app/build/outputs", "-v", filepath.Join(userCacheDir, "go-build")+":/cache", "hover-build-cc") + dockerRunCmd := exec.Command(dockerBin, "run", "-e", "USERID=$UID", "-v", goPath+":/go", "-v", wd+":/app", "-v", engineCachePath+":/engine", "-v", outputPath+":/app/go/build/outputs", "-v", filepath.Join(userCacheDir, "go-build")+":/cache", "hover-build-cc-"+targetOS) dockerRunCmd.Stderr = os.Stderr dockerRunCmd.Stdout = os.Stdout - dockerRunCmd.Dir = tmpDir + dockerRunCmd.Dir = crossCompilingDir err = dockerRunCmd.Run() if err != nil { fmt.Printf("hover: Docker run failed: %v\n", err) os.Exit(1) } - err = os.RemoveAll(tmpDir) - if err != nil { - fmt.Printf("hover: Could not remove temporary build folder: %v\n", err) - os.Exit(1) - } + fmt.Println("hover: Finished cross-compiling for " + targetOS) } func build(projectName string, targetOS string, vmArguments []string) { - mingwGccBin, err := exec.LookPath(mingwGccBinName) - mingwGppBin, err := exec.LookPath(mingwGppBinName) - clangBin, err := exec.LookPath(clangBinName) crossCompile = targetOS != runtime.GOOS - if crossCompile { - fmt.Printf("hover: Cross-compiling for %s using docker is very experimental\n", targetOS) - if mingwGccBin != "" && mingwGppBin != "" { - windowsCrossCompileToolsAvailable = true - } - if clangBin != "" { - darwinCrossCompileToolsAvailable = true - } - } - var engineCachePath string + if buildCachePath != "" { engineCachePath = enginecache.ValidateOrUpdateEngineAtPath(targetOS, buildCachePath) } else { @@ -275,7 +256,7 @@ func build(projectName string, targetOS string, vmArguments []string) { } } - err = os.MkdirAll(outputDirectoryPath(targetOS), 0775) + err := os.MkdirAll(outputDirectoryPath(targetOS), 0775) if err != nil { fmt.Printf("hover: failed to create output directory %s: %v\n", outputDirectoryPath(targetOS), err) os.Exit(1) @@ -419,6 +400,7 @@ func build(projectName string, targetOS string, vmArguments []string) { } if crossCompile { + fmt.Printf("hover: Because %s is not able to compile for %s out of the box, a cross-compiling container is used\n", runtime.GOOS, targetOS) dockerBuild(projectName, targetOS, vmArguments) return } @@ -439,6 +421,7 @@ func build(projectName string, targetOS string, vmArguments []string) { fmt.Printf("hover: Go build failed: %v\n", err) os.Exit(1) } + fmt.Println("hover: Finished compiling") } func buildEnv(targetOS string, engineCachePath string) []string { @@ -476,13 +459,11 @@ func buildEnv(targetOS string, engineCachePath string) []string { if targetOS == "windows" { env = append(env, "CC="+mingwGccBinName, - "CXX="+mingwGppBinName, ) } if targetOS == "darwin" { env = append(env, "CC="+clangBinName, - "CXX="+clangBinName, ) } } From 7ee1d1d7b8f76ede12b3c4a467a38b6b2795923a Mon Sep 17 00:00:00 2001 From: jld3103 Date: Wed, 18 Sep 2019 15:20:23 +0200 Subject: [PATCH 6/9] Fix log messages --- cmd/build.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/build.go b/cmd/build.go index 469ddca4..f28f21d9 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -212,7 +212,7 @@ func dockerBuild(projectName string, targetOS string, vmArguments []string) { fmt.Printf("hover: Could not close Dockerfile: %v\n", err) os.Exit(1) } - fmt.Printf("hover: A Dockerfile for cross-compiling for %s has bee created at %s. You can add it to git.\n", targetOS, filepath.Join(buildPath, "cross-compiling", targetOS)) + fmt.Printf("hover: A Dockerfile for cross-compiling for %s has been created at %s. You can add it to git.\n", targetOS, filepath.Join(buildPath, "cross-compiling", targetOS)) } dockerBuildCmd := exec.Command(dockerBin, "build", "-t", "hover-build-cc-"+targetOS, ".") dockerBuildCmd.Stderr = os.Stderr @@ -235,7 +235,7 @@ func dockerBuild(projectName string, targetOS string, vmArguments []string) { fmt.Printf("hover: Docker run failed: %v\n", err) os.Exit(1) } - fmt.Println("hover: Finished cross-compiling for " + targetOS) + fmt.Println("hover: Successfully cross-compiled for " + targetOS) } func build(projectName string, targetOS string, vmArguments []string) { @@ -421,7 +421,7 @@ func build(projectName string, targetOS string, vmArguments []string) { fmt.Printf("hover: Go build failed: %v\n", err) os.Exit(1) } - fmt.Println("hover: Finished compiling") + fmt.Println("hover: Successfully compiled") } func buildEnv(targetOS string, engineCachePath string) []string { From c84e6707f65b7c61659ba5b01a5d97d2ec1c025b Mon Sep 17 00:00:00 2001 From: jld3103 Date: Thu, 19 Sep 2019 18:23:07 +0200 Subject: [PATCH 7/9] Add --docker flag and create seperate Dockerfiles for different build modes --- cmd/build.go | 26 +++++++++++++++----------- cmd/common.go | 18 ++++++++++++++++-- cmd/run.go | 1 + 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/cmd/build.go b/cmd/build.go index f28f21d9..0fcd1d54 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -27,6 +27,7 @@ var ( buildCachePath string buildOmitEmbedder bool buildOmitFlutterBundle bool + buildDocker bool ) const buildPath = "go" @@ -43,6 +44,7 @@ func init() { buildCmd.PersistentFlags().StringVarP(&buildBranch, "branch", "b", "", "The 'go-flutter' version to use. (@master or @v0.20.0 for example)") buildCmd.PersistentFlags().BoolVar(&buildDebug, "debug", false, "Build a debug version of the app.") buildCmd.PersistentFlags().StringVarP(&buildCachePath, "cache-path", "", "", "The path that hover uses to cache dependencies such as the Flutter engine .so/.dll (defaults to the standard user cache directory)") + buildCmd.PersistentFlags().BoolVar(&buildDocker, "docker", false, "Compile in Docker container only. No need to install go") buildCmd.AddCommand(buildLinuxCmd) buildCmd.AddCommand(buildLinuxSnapCmd) buildCmd.AddCommand(buildLinuxDebCmd) @@ -153,12 +155,11 @@ func outputBinaryPath(projectName string, targetOS string) string { } func dockerBuild(projectName string, targetOS string, vmArguments []string) { - dockerBin, err := exec.LookPath("docker") - if err != nil { - fmt.Printf("hover: Failed to lookup `docker` executable. Please install Docker.\nhttps://docs.docker.com/install/") - os.Exit(1) + buildMode := "release" + if buildDebug { + buildMode = "debug" } - crossCompilingDir, err := filepath.Abs(filepath.Join(buildPath, "cross-compiling", targetOS)) + crossCompilingDir, err := filepath.Abs(filepath.Join(buildPath, "cross-compiling", targetOS, buildMode)) err = os.MkdirAll(crossCompilingDir, 0755) if err != nil { fmt.Printf("hover: Cannot create the cross-compiling directory: %v\n", err) @@ -240,6 +241,7 @@ func dockerBuild(projectName string, targetOS string, vmArguments []string) { func build(projectName string, targetOS string, vmArguments []string) { crossCompile = targetOS != runtime.GOOS + buildDocker = crossCompile || buildDocker if buildCachePath != "" { engineCachePath = enginecache.ValidateOrUpdateEngineAtPath(targetOS, buildCachePath) @@ -399,8 +401,10 @@ func build(projectName string, targetOS string, vmArguments []string) { } } - if crossCompile { - fmt.Printf("hover: Because %s is not able to compile for %s out of the box, a cross-compiling container is used\n", runtime.GOOS, targetOS) + if buildDocker { + if crossCompile { + fmt.Printf("hover: Because %s is not able to compile for %s out of the box, a cross-compiling container is used\n", runtime.GOOS, targetOS) + } dockerBuild(projectName, targetOS, vmArguments) return } @@ -438,11 +442,11 @@ func buildEnv(targetOS string, engineCachePath string) []string { os.Exit(1) } cgoLdflagsString := "" - if crossCompile { + if buildDocker { cgoLdflagsString = "\"" } cgoLdflagsString = cgoLdflagsString + cgoLdflags - if crossCompile { + if buildDocker { cgoLdflagsString = cgoLdflagsString + "\"" } env := []string{ @@ -452,7 +456,7 @@ func buildEnv(targetOS string, engineCachePath string) []string { "GOARCH=amd64", "CGO_ENABLED=1", } - if crossCompile { + if buildDocker { env = append(env, "GOCACHE=/cache", ) @@ -489,7 +493,7 @@ func buildCommand(targetOS string, vmArguments []string, outputBinaryPath string "-o", outputBinaryPath, "-v", } - if crossCompile { + if buildDocker { outputCommand = append(outputCommand, fmt.Sprintf("-ldflags=\"%s\"", strings.Join(ldflags, " "))) } else { outputCommand = append(outputCommand, fmt.Sprintf("-ldflags=%s", strings.Join(ldflags, " "))) diff --git a/cmd/common.go b/cmd/common.go index 1675ff89..7224f4d1 100644 --- a/cmd/common.go +++ b/cmd/common.go @@ -19,15 +19,29 @@ func init() { var ( goBin string flutterBin string + dockerBin string ) func initBinaries() { var err error + goAvailable := false + dockerAvailable := false goBin, err = exec.LookPath("go") - if err != nil { - fmt.Println("hover: Failed to lookup `go` executable. Please install Go.\nhttps://golang.org/doc/install") + if err == nil { + goAvailable = true + } + dockerBin, err = exec.LookPath("docker") + if err == nil { + dockerAvailable = true + } + if !dockerAvailable && !goAvailable { + fmt.Println("hover: Failed to lookup `go` and `docker` executable. Please install one of them:\nGo: https://golang.org/doc/install\nDocker: https://docs.docker.com/install") os.Exit(1) } + if dockerAvailable && !goAvailable { + fmt.Println("hover: Using Docker instead of go for compiling, because looking up `go` executable failed.") + buildDocker = true + } flutterBin, err = exec.LookPath("flutter") if err != nil { fmt.Println("hover: Failed to lookup `flutter` executable. Please install flutter.\nhttps://flutter.dev/docs/get-started/install") diff --git a/cmd/run.go b/cmd/run.go index b56d4052..1a05b389 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -23,6 +23,7 @@ func init() { runCmd.Flags().StringVarP(&runObservatoryPort, "observatory-port", "", "50300", "The observatory port used to connect hover to VM services (hot-reload/debug/..)") runCmd.Flags().BoolVar(&buildOmitEmbedder, "omit-embedder", false, "Don't (re)compile 'go-flutter' source code, useful when only working with Dart code") runCmd.Flags().BoolVar(&buildOmitFlutterBundle, "omit-flutter", false, "Don't (re)compile the current Flutter project, useful when only working with Golang code (plugin)") + runCmd.PersistentFlags().BoolVar(&buildDocker, "docker", false, "Compile and run in Docker container only. No need to install go") rootCmd.AddCommand(runCmd) } From 8af58308c0cb8a246c913ecb25db448dd7a190a9 Mon Sep 17 00:00:00 2001 From: jld3103 Date: Sat, 21 Sep 2019 16:48:34 +0200 Subject: [PATCH 8/9] Move build command from Dockerfile to docker run and fix file permissions --- cmd/build.go | 49 +++++++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/cmd/build.go b/cmd/build.go index 0fcd1d54..3c103b45 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "os/exec" + "os/user" "path/filepath" "regexp" "runtime" @@ -155,11 +156,7 @@ func outputBinaryPath(projectName string, targetOS string) string { } func dockerBuild(projectName string, targetOS string, vmArguments []string) { - buildMode := "release" - if buildDebug { - buildMode = "debug" - } - crossCompilingDir, err := filepath.Abs(filepath.Join(buildPath, "cross-compiling", targetOS, buildMode)) + crossCompilingDir, err := filepath.Abs(filepath.Join(buildPath, "cross-compiling")) err = os.MkdirAll(crossCompilingDir, 0755) if err != nil { fmt.Printf("hover: Cannot create the cross-compiling directory: %v\n", err) @@ -195,11 +192,6 @@ func dockerBuild(projectName string, targetOS string, vmArguments []string) { dockerFileContent := []string{ "FROM dockercore/golang-cross", "RUN apt-get install libgl1-mesa-dev xorg-dev -y", - "WORKDIR /app/go", - "CMD " + strings.Join(buildCommand(targetOS, vmArguments, "build/outputs/"+targetOS+"/"+outputBinaryName(projectName, targetOS)), " "), - } - for _, env := range buildEnv(targetOS, "/engine") { - dockerFileContent = append(dockerFileContent, "ENV "+env) } for _, line := range dockerFileContent { @@ -215,7 +207,7 @@ func dockerBuild(projectName string, targetOS string, vmArguments []string) { } fmt.Printf("hover: A Dockerfile for cross-compiling for %s has been created at %s. You can add it to git.\n", targetOS, filepath.Join(buildPath, "cross-compiling", targetOS)) } - dockerBuildCmd := exec.Command(dockerBin, "build", "-t", "hover-build-cc-"+targetOS, ".") + dockerBuildCmd := exec.Command(dockerBin, "build", "-t", "hover-build-cc", ".") dockerBuildCmd.Stderr = os.Stderr dockerBuildCmd.Dir = crossCompilingDir err = dockerBuildCmd.Run() @@ -226,8 +218,29 @@ func dockerBuild(projectName string, targetOS string, vmArguments []string) { fmt.Println("hover: Cross-Compiling 'go-flutter' and plugins using docker") - outputPath, err := filepath.Abs(filepath.Join(buildPath, "build", "outputs")) - dockerRunCmd := exec.Command(dockerBin, "run", "-e", "USERID=$UID", "-v", goPath+":/go", "-v", wd+":/app", "-v", engineCachePath+":/engine", "-v", outputPath+":/app/go/build/outputs", "-v", filepath.Join(userCacheDir, "go-build")+":/cache", "hover-build-cc-"+targetOS) + u, err := user.Current() + if err != nil { + fmt.Printf("hover: Couldn't get current user: %v\n", err) + os.Exit(1) + } + args := []string{ + "run", + "-w", "/app/go", + "-v", goPath + ":/go", + "-v", wd + ":/app", + "-v", engineCachePath + ":/engine", + "-v", filepath.Join(userCacheDir, "go-build") + ":/cache", + } + for _, env := range buildEnv(targetOS, "/engine") { + args = append(args, "-e", env) + } + args = append(args, "hover-build-cc") + chownStr := "" + if runtime.GOOS != "windows" { + chownStr = fmt.Sprintf(" && chown %s:%s build/ -R", u.Uid, u.Gid) + } + args = append(args, "bash", "-c", fmt.Sprintf("%s%s", strings.Join(buildCommand(targetOS, vmArguments, "build/outputs/"+targetOS+"/"+outputBinaryName(projectName, targetOS)), " "), chownStr)) + dockerRunCmd := exec.Command(dockerBin, args...) dockerRunCmd.Stderr = os.Stderr dockerRunCmd.Stdout = os.Stdout dockerRunCmd.Dir = crossCompilingDir @@ -441,17 +454,9 @@ func buildEnv(targetOS string, engineCachePath string) []string { fmt.Printf("hover: Target platform %s is not supported, cgo_ldflags not implemented.\n", targetOS) os.Exit(1) } - cgoLdflagsString := "" - if buildDocker { - cgoLdflagsString = "\"" - } - cgoLdflagsString = cgoLdflagsString + cgoLdflags - if buildDocker { - cgoLdflagsString = cgoLdflagsString + "\"" - } env := []string{ "GO111MODULE=on", - "CGO_LDFLAGS=" + cgoLdflagsString, + "CGO_LDFLAGS=" + cgoLdflags, "GOOS=" + targetOS, "GOARCH=amd64", "CGO_ENABLED=1", From 56091f43b70d98d4fbcee179b8b7e1cc85994fcd Mon Sep 17 00:00:00 2001 From: jld3103 Date: Sat, 21 Sep 2019 16:54:13 +0200 Subject: [PATCH 9/9] Change behavior if go not found --- cmd/common.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/common.go b/cmd/common.go index 7224f4d1..59220639 100644 --- a/cmd/common.go +++ b/cmd/common.go @@ -38,9 +38,9 @@ func initBinaries() { fmt.Println("hover: Failed to lookup `go` and `docker` executable. Please install one of them:\nGo: https://golang.org/doc/install\nDocker: https://docs.docker.com/install") os.Exit(1) } - if dockerAvailable && !goAvailable { - fmt.Println("hover: Using Docker instead of go for compiling, because looking up `go` executable failed.") - buildDocker = true + if dockerAvailable && !goAvailable && !buildDocker { + fmt.Println("hover: Failed to lookup `go` executable. Please install go or add '--docker' to force running in Docker container.\nhttps://golang.org/doc/install") + os.Exit(1) } flutterBin, err = exec.LookPath("flutter") if err != nil {