From e440b4c28ff1c094ca9315a35cff46304cd8ff97 Mon Sep 17 00:00:00 2001 From: jld3103 Date: Thu, 26 Sep 2019 11:12:33 +0200 Subject: [PATCH 1/2] Move packaging to containers and add windows-msi --- cmd/build.go | 139 +++++------ cmd/common.go | 68 +----- cmd/init.go | 22 +- cmd/packaging.go | 437 ++--------------------------------- cmd/packaging/linux-deb.go | 166 +++++++++++++ cmd/packaging/linux-snap.go | 132 +++++++++++ cmd/packaging/linux.go | 39 ++++ cmd/packaging/packaging.go | 169 ++++++++++++++ cmd/packaging/windows-msi.go | 263 +++++++++++++++++++++ cmd/run.go | 9 +- cmd/upgrade.go | 11 +- internal/build/binaries.go | 7 + internal/build/build.go | 46 ++++ internal/pubspec/pubspec.go | 51 ++++ 14 files changed, 981 insertions(+), 578 deletions(-) create mode 100644 cmd/packaging/linux-deb.go create mode 100644 cmd/packaging/linux-snap.go create mode 100644 cmd/packaging/linux.go create mode 100644 cmd/packaging/packaging.go create mode 100644 cmd/packaging/windows-msi.go create mode 100644 internal/build/binaries.go create mode 100644 internal/build/build.go create mode 100644 internal/pubspec/pubspec.go diff --git a/cmd/build.go b/cmd/build.go index cbdc5815..9e66c642 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -16,6 +16,9 @@ import ( "github.com/go-flutter-desktop/hover/internal/enginecache" "github.com/go-flutter-desktop/hover/internal/versioncheck" + "github.com/go-flutter-desktop/hover/internal/build" + "github.com/go-flutter-desktop/hover/cmd/packaging" + "github.com/go-flutter-desktop/hover/internal/pubspec" ) var dotSlash = string([]byte{'.', filepath.Separator}) @@ -32,8 +35,6 @@ var ( buildDocker bool ) -const buildPath = "go" - const mingwGccBinName = "x86_64-w64-mingw32-gcc" const clangBinName = "o32-clang" @@ -53,6 +54,7 @@ func init() { buildCmd.AddCommand(buildLinuxDebCmd) buildCmd.AddCommand(buildDarwinCmd) buildCmd.AddCommand(buildWindowsCmd) + buildCmd.AddCommand(buildWindowsMsiCmd) rootCmd.AddCommand(buildCmd) } @@ -65,10 +67,9 @@ var buildLinuxCmd = &cobra.Command{ Use: "linux", Short: "Build a desktop release for linux", Run: func(cmd *cobra.Command, args []string) { - projectName := getPubSpec().Name assertHoverInitialized() - build(projectName, "linux", nil) + buildNormal("linux", nil) }, } @@ -76,12 +77,15 @@ var buildLinuxSnapCmd = &cobra.Command{ Use: "linux-snap", Short: "Build a desktop release for linux and package it for snap", Run: func(cmd *cobra.Command, args []string) { - projectName := getPubSpec().Name assertHoverInitialized() - assertPackagingFormatInitialized("linux-snap") + packaging.AssertPackagingFormatInitialized("linux-snap") + + if !packaging.DockerInstalled() { + os.Exit(1) + } - build(projectName, "linux", nil) - buildLinuxSnap(projectName) + buildNormal("linux", nil) + packaging.BuildLinuxSnap() }, } @@ -89,12 +93,15 @@ var buildLinuxDebCmd = &cobra.Command{ Use: "linux-deb", Short: "Build a desktop release for linux and package it for deb", Run: func(cmd *cobra.Command, args []string) { - projectName := getPubSpec().Name assertHoverInitialized() - assertPackagingFormatInitialized("linux-deb") + packaging.AssertPackagingFormatInitialized("linux-deb") + + if !packaging.DockerInstalled() { + os.Exit(1) + } - build(projectName, "linux", nil) - buildLinuxDeb(projectName) + buildNormal("linux", nil) + packaging.BuildLinuxDeb() }, } @@ -102,10 +109,9 @@ var buildDarwinCmd = &cobra.Command{ Use: "darwin", Short: "Build a desktop release for darwin", Run: func(cmd *cobra.Command, args []string) { - projectName := getPubSpec().Name assertHoverInitialized() - build(projectName, "darwin", nil) + buildNormal("darwin", nil) }, } @@ -113,52 +119,30 @@ var buildWindowsCmd = &cobra.Command{ Use: "windows", Short: "Build a desktop release for windows", Run: func(cmd *cobra.Command, args []string) { - projectName := getPubSpec().Name assertHoverInitialized() - build(projectName, "windows", nil) + buildNormal("windows", nil) }, } -func outputDirectoryPath(targetOS string) string { - outputDirectoryPath, err := filepath.Abs(filepath.Join(buildPath, "build", "outputs", targetOS)) - if err != nil { - fmt.Printf("hover: Failed to resolve absolute path for output directory: %v\n", err) - os.Exit(1) - } - if _, err := os.Stat(outputDirectoryPath); os.IsNotExist(err) { - err = os.MkdirAll(outputDirectoryPath, 0775) - if err != nil { - fmt.Printf("hover: Failed to create output directory %s: %v\n", outputDirectoryPath, err) +var buildWindowsMsiCmd = &cobra.Command{ + Use: "windows-msi", + Short: "Build a desktop release for windows and package it for msi", + Run: func(cmd *cobra.Command, args []string) { + assertHoverInitialized() + packaging.AssertPackagingFormatInitialized("windows-msi") + + if !packaging.DockerInstalled() { os.Exit(1) } - } - return outputDirectoryPath -} - -func outputBinaryName(projectName string, targetOS string) string { - var outputBinaryName = projectName - switch targetOS { - case "darwin": - // no special filename - case "linux": - // no special filename - case "windows": - outputBinaryName += ".exe" - default: - fmt.Printf("hover: Target platform %s is not supported.\n", targetOS) - os.Exit(1) - } - return outputBinaryName -} -func outputBinaryPath(projectName string, targetOS string) string { - outputBinaryPath := filepath.Join(outputDirectoryPath(targetOS), outputBinaryName(projectName, targetOS)) - return outputBinaryPath + buildNormal("windows", nil) + packaging.BuildWindowsMsi() + }, } -func dockerBuild(projectName string, targetOS string, vmArguments []string) { - crossCompilingDir, err := filepath.Abs(filepath.Join(buildPath, "cross-compiling")) +func buildInDocker(targetOS string, vmArguments []string) { + crossCompilingDir, err := filepath.Abs(filepath.Join(build.BuildPath, "cross-compiling")) err = os.MkdirAll(crossCompilingDir, 0755) if err != nil { fmt.Printf("hover: Cannot create the cross-compiling directory: %v\n", err) @@ -207,9 +191,10 @@ 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 been 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(build.BuildPath, "cross-compiling", targetOS)) } - dockerBuildCmd := exec.Command(dockerBin, "build", "-t", "hover-build-cc", ".") + dockerBuildCmd := exec.Command(build.DockerBin, "build", "-t", "hover-build-cc", ".") + dockerBuildCmd.Stdout = os.Stdout dockerBuildCmd.Stderr = os.Stderr dockerBuildCmd.Dir = crossCompilingDir err = dockerBuildCmd.Run() @@ -241,8 +226,8 @@ func dockerBuild(projectName string, targetOS string, vmArguments []string) { 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...) + args = append(args, "bash", "-c", fmt.Sprintf("%s%s", strings.Join(buildCommand(targetOS, vmArguments, "build/outputs/"+targetOS+"/"+build.OutputBinaryName(pubspec.GetPubSpec().Name, targetOS)), " "), chownStr)) + dockerRunCmd := exec.Command(build.DockerBin, args...) dockerRunCmd.Stderr = os.Stderr dockerRunCmd.Stdout = os.Stdout dockerRunCmd.Dir = crossCompilingDir @@ -254,7 +239,7 @@ func dockerBuild(projectName string, targetOS string, vmArguments []string) { fmt.Println("hover: Successfully cross-compiled for " + targetOS) } -func build(projectName string, targetOS string, vmArguments []string) { +func buildNormal(targetOS string, vmArguments []string) { crossCompile = targetOS != runtime.GOOS buildDocker = crossCompile || buildDocker @@ -265,21 +250,21 @@ func build(projectName string, targetOS string, vmArguments []string) { } if !buildOmitFlutterBundle && !buildOmitEmbedder { - err := os.RemoveAll(outputDirectoryPath(targetOS)) + err := os.RemoveAll(build.OutputDirectoryPath(targetOS)) fmt.Printf("hover: Cleaning the build directory\n") if err != nil { - fmt.Printf("hover: failed to clean output directory %s: %v\n", outputDirectoryPath(targetOS), err) + fmt.Printf("hover: failed to clean output directory %s: %v\n", build.OutputDirectoryPath(targetOS), err) os.Exit(1) } } - err := os.MkdirAll(outputDirectoryPath(targetOS), 0775) + err := os.MkdirAll(build.OutputDirectoryPath(targetOS), 0775) if err != nil { - fmt.Printf("hover: failed to create output directory %s: %v\n", outputDirectoryPath(targetOS), err) + fmt.Printf("hover: failed to create output directory %s: %v\n", build.OutputDirectoryPath(targetOS), err) os.Exit(1) } - cmdCheckFlutter := exec.Command(flutterBin, "--version") + cmdCheckFlutter := exec.Command(build.FlutterBin, "--version") cmdCheckFlutterOut, err := cmdCheckFlutter.Output() if err != nil { fmt.Printf("hover: failed to check your flutter channel: %v\n", err) @@ -303,8 +288,8 @@ func build(projectName string, targetOS string, vmArguments []string) { trackWidgetCreation = "--track-widget-creation" } - cmdFlutterBuild := exec.Command(flutterBin, "build", "bundle", - "--asset-dir", filepath.Join(outputDirectoryPath(targetOS), "flutter_assets"), + cmdFlutterBuild := exec.Command(build.FlutterBin, "build", "bundle", + "--asset-dir", filepath.Join(build.OutputDirectoryPath(targetOS), "flutter_assets"), "--target", buildTarget, "--manifest", buildManifest, trackWidgetCreation, @@ -331,7 +316,7 @@ func build(projectName string, targetOS string, vmArguments []string) { engineFile = "flutter_engine.dll" } - outputEngineFile := filepath.Join(outputDirectoryPath(targetOS), engineFile) + outputEngineFile := filepath.Join(build.OutputDirectoryPath(targetOS), engineFile) err = copy.Copy( filepath.Join(engineCachePath, engineFile), outputEngineFile, @@ -350,7 +335,7 @@ func build(projectName string, targetOS string, vmArguments []string) { err = copy.Copy( filepath.Join(engineCachePath, "artifacts", "icudtl.dat"), - filepath.Join(outputDirectoryPath(targetOS), "icudtl.dat"), + filepath.Join(build.OutputDirectoryPath(targetOS), "icudtl.dat"), ) if err != nil { fmt.Printf("hover: Failed to copy icudtl.dat: %v\n", err) @@ -358,11 +343,11 @@ func build(projectName string, targetOS string, vmArguments []string) { } err = copy.Copy( - filepath.Join(buildPath, "assets"), - filepath.Join(outputDirectoryPath(targetOS), "assets"), + filepath.Join(build.BuildPath, "assets"), + filepath.Join(build.OutputDirectoryPath(targetOS), "assets"), ) if err != nil { - fmt.Printf("hover: Failed to copy %s/assets: %v\n", buildPath, err) + fmt.Printf("hover: Failed to copy %s/assets: %v\n", build.BuildPath, err) os.Exit(1) } @@ -378,7 +363,7 @@ func build(projectName string, targetOS string, vmArguments []string) { } if buildBranch == "" { - currentTag, err := versioncheck.CurrentGoFlutterTag(filepath.Join(wd, buildPath)) + currentTag, err := versioncheck.CurrentGoFlutterTag(filepath.Join(wd, build.BuildPath)) if err != nil { fmt.Printf("hover: %v\n", err) os.Exit(1) @@ -402,7 +387,7 @@ func build(projectName string, targetOS string, vmArguments []string) { } else { // when the buildBranch is empty and the currentTag is a release. // Check if the 'go-flutter' needs updates. - versioncheck.CheckFoGoFlutterUpdate(filepath.Join(wd, buildPath), currentTag) + versioncheck.CheckFoGoFlutterUpdate(filepath.Join(wd, build.BuildPath), currentTag) } } else { @@ -415,21 +400,17 @@ func build(projectName string, targetOS string, vmArguments []string) { } } - if buildOpenGlVersion == "none" { - fmt.Println("hover: The '--opengl=none' flag makes go-flutter incompatible with texture plugins!") - } - 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) + buildInDocker(targetOS, vmArguments) return } - buildCommandString := buildCommand(targetOS, vmArguments, outputBinaryPath(projectName, targetOS)) + buildCommandString := buildCommand(targetOS, vmArguments, build.OutputBinaryPath(pubspec.GetPubSpec().Name, targetOS)) cmdGoBuild := exec.Command(buildCommandString[0], buildCommandString[1:]...) - cmdGoBuild.Dir = filepath.Join(wd, buildPath) + cmdGoBuild.Dir = filepath.Join(wd, build.BuildPath) cmdGoBuild.Env = append(os.Environ(), buildEnv(targetOS, engineCachePath)..., ) @@ -485,7 +466,7 @@ func buildEnv(targetOS string, engineCachePath string) []string { } func buildCommand(targetOS string, vmArguments []string, outputBinaryPath string) []string { - currentTag, err := versioncheck.CurrentGoFlutterTag(buildPath) + currentTag, err := versioncheck.CurrentGoFlutterTag(build.BuildPath) if err != nil { fmt.Printf("hover: %v\n", err) os.Exit(1) @@ -509,9 +490,9 @@ func buildCommand(targetOS string, vmArguments []string, outputBinaryPath string " -X github.com/go-flutter-desktop/go-flutter.PlatformVersion=%s "+ " -X github.com/go-flutter-desktop/go-flutter.ProjectName=%s "+ " -X github.com/go-flutter-desktop/go-flutter.ProjectOrganizationName=%s", - getPubSpec().Version, + pubspec.GetPubSpec().Version, currentTag, - getPubSpec().Name, + pubspec.GetPubSpec().Name, androidOrganizationName())) outputCommand := []string{ diff --git a/cmd/common.go b/cmd/common.go index 00b88b8c..c1eee9e3 100644 --- a/cmd/common.go +++ b/cmd/common.go @@ -11,28 +11,24 @@ import ( "strings" "github.com/spf13/cobra" - "gopkg.in/yaml.v2" + + "github.com/go-flutter-desktop/hover/internal/pubspec" + "github.com/go-flutter-desktop/hover/internal/build" ) func init() { cobra.OnInitialize(initBinaries) } -var ( - goBin string - flutterBin string - dockerBin string -) - func initBinaries() { var err error goAvailable := false dockerAvailable := false - goBin, err = exec.LookPath("go") + build.GoBin, err = exec.LookPath("go") if err == nil { goAvailable = true } - dockerBin, err = exec.LookPath("docker") + build.DockerBin, err = exec.LookPath("docker") if err == nil { dockerAvailable = true } @@ -44,69 +40,25 @@ func initBinaries() { 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") + build.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") os.Exit(1) } } -type PubSpec struct { - Name string - Description string - Version string - Author string - Dependencies map[string]interface{} -} - -var pubspec = PubSpec{} - -func getPubSpec() PubSpec { - { - if pubspec.Name == "" { - file, err := os.Open("pubspec.yaml") - if err != nil { - if os.IsNotExist(err) { - fmt.Println("hover: Error: No pubspec.yaml file found.") - goto Fail - } - fmt.Printf("hover: Failed to open pubspec.yaml: %v\n", err) - os.Exit(1) - } - defer file.Close() - - err = yaml.NewDecoder(file).Decode(&pubspec) - if err != nil { - fmt.Printf("hover: Failed to decode pubspec.yaml: %v\n", err) - goto Fail - } - if _, exists := pubspec.Dependencies["flutter"]; !exists { - fmt.Println("hover: Missing `flutter` in pubspec.yaml dependencies list.") - goto Fail - } - } - - return pubspec - } - -Fail: - fmt.Println("hover: This command should be run from the root of your Flutter project.") - os.Exit(1) - return PubSpec{} -} - // assertInFlutterProject asserts this command is executed in a flutter project func assertInFlutterProject() { - getPubSpec() + pubspec.GetPubSpec() } func assertHoverInitialized() { - _, err := os.Stat(buildPath) + _, err := os.Stat(build.BuildPath) if os.IsNotExist(err) { if hoverMigration() { return } - fmt.Println("hover: Directory '" + buildPath + "' is missing, did you run `hover init` in this project?") + fmt.Println("hover: Directory '" + build.BuildPath + "' is missing, did you run `hover init` in this project?") os.Exit(1) } if err != nil { @@ -128,7 +80,7 @@ func hoverMigration() bool { fmt.Printf("hover: Let hover do the migration? ") if askForConfirmation() { - err := os.Rename(oldBuildPath, buildPath) + err := os.Rename(oldBuildPath, build.BuildPath) if err != nil { fmt.Printf("hover: Migration failed: %v\n", err) return false diff --git a/cmd/init.go b/cmd/init.go index cd76fc69..c98fc8c5 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -3,6 +3,8 @@ package cmd import ( "errors" "fmt" + "github.com/go-flutter-desktop/hover/internal/build" + "github.com/go-flutter-desktop/hover/internal/pubspec" "io" "os" "os/exec" @@ -29,27 +31,27 @@ var initCmd = &cobra.Command{ var projectPath string if len(args) == 0 || args[0] == "." { - projectPath = getPubSpec().Name + projectPath = pubspec.GetPubSpec().Name } else { projectPath = args[0] } - err := os.Mkdir(buildPath, 0775) + err := os.Mkdir(build.BuildPath, 0775) if err != nil { if os.IsExist(err) { - fmt.Println("hover: A file or directory named `" + buildPath + "` already exists. Cannot continue init.") + fmt.Println("hover: A file or directory named `" + build.BuildPath + "` already exists. Cannot continue init.") os.Exit(1) } } - desktopCmdPath := filepath.Join(buildPath, "cmd") + desktopCmdPath := filepath.Join(build.BuildPath, "cmd") err = os.Mkdir(desktopCmdPath, 0775) if err != nil { fmt.Printf("hover: Failed to create `%s`: %v\n", desktopCmdPath, err) os.Exit(1) } - desktopAssetsPath := filepath.Join(buildPath, "assets") + desktopAssetsPath := filepath.Join(build.BuildPath, "assets") err = os.Mkdir(desktopAssetsPath, 0775) if err != nil { fmt.Printf("hover: Failed to create `%s`: %v\n", desktopAssetsPath, err) @@ -59,7 +61,7 @@ var initCmd = &cobra.Command{ copyAsset("app/main.go", filepath.Join(desktopCmdPath, "main.go")) copyAsset("app/options.go", filepath.Join(desktopCmdPath, "options.go")) copyAsset("app/icon.png", filepath.Join(desktopAssetsPath, "icon.png")) - copyAsset("app/gitignore", filepath.Join(buildPath, ".gitignore")) + copyAsset("app/gitignore", filepath.Join(build.BuildPath, ".gitignore")) wd, err := os.Getwd() if err != nil { @@ -67,8 +69,8 @@ var initCmd = &cobra.Command{ os.Exit(1) } - cmdGoModInit := exec.Command(goBin, "mod", "init", projectPath+"/"+buildPath) - cmdGoModInit.Dir = filepath.Join(wd, buildPath) + cmdGoModInit := exec.Command(build.GoBin, "mod", "init", projectPath+"/"+build.BuildPath) + cmdGoModInit.Dir = filepath.Join(wd, build.BuildPath) cmdGoModInit.Env = append(os.Environ(), "GO111MODULE=on", ) @@ -80,8 +82,8 @@ var initCmd = &cobra.Command{ os.Exit(1) } - cmdGoModTidy := exec.Command(goBin, "mod", "tidy") - cmdGoModTidy.Dir = filepath.Join(wd, buildPath) + cmdGoModTidy := exec.Command(build.GoBin, "mod", "tidy") + cmdGoModTidy.Dir = filepath.Join(wd, build.BuildPath) fmt.Println(cmdGoModTidy.Dir) cmdGoModTidy.Env = append(os.Environ(), "GO111MODULE=on", diff --git a/cmd/packaging.go b/cmd/packaging.go index 4a4724fc..c5503f27 100644 --- a/cmd/packaging.go +++ b/cmd/packaging.go @@ -1,24 +1,15 @@ package cmd import ( - "fmt" - "io/ioutil" - "os" - "os/exec" - "os/user" - "path/filepath" - "runtime" - "strings" - - "github.com/otiai10/copy" "github.com/spf13/cobra" -) -var packagingPath = filepath.Join(buildPath, "packaging") + "github.com/go-flutter-desktop/hover/cmd/packaging" +) func init() { initPackagingCmd.AddCommand(initLinuxSnapCmd) initPackagingCmd.AddCommand(initLinuxDebCmd) + initPackagingCmd.AddCommand(initWindowsMsiCmd) rootCmd.AddCommand(initPackagingCmd) } @@ -31,10 +22,10 @@ var initLinuxSnapCmd = &cobra.Command{ Use: "linux-snap", Short: "Create configuration files for snap packaging", Run: func(cmd *cobra.Command, args []string) { - projectName := getPubSpec().Name assertHoverInitialized() + packaging.DockerInstalled() - initLinuxSnap(projectName) + packaging.InitLinuxSnap() }, } @@ -42,418 +33,20 @@ var initLinuxDebCmd = &cobra.Command{ Use: "linux-deb", Short: "Create configuration files for deb packaging", Run: func(cmd *cobra.Command, args []string) { - projectName := getPubSpec().Name assertHoverInitialized() + packaging.DockerInstalled() - initLinuxDeb(projectName) + packaging.InitLinuxDeb() }, } -var linuxPackagingDependencies = []string{"libx11-6", "libxrandr2", "libxcursor1", "libxinerama1"} - -func packagingFormatPath(packagingFormat string) string { - directoryPath, err := filepath.Abs(filepath.Join(packagingPath, packagingFormat)) - if err != nil { - fmt.Printf("hover: Failed to resolve absolute path for %s directory: %v\n", packagingFormat, err) - os.Exit(1) - } - return directoryPath -} - -func createPackagingFormatDirectory(packagingFormat string) { - if _, err := os.Stat(packagingFormatPath(packagingFormat)); !os.IsNotExist(err) { - fmt.Printf("hover: A file or directory named `%s` already exists. Cannot continue packaging init for %s.\n", packagingFormat, packagingFormat) - os.Exit(1) - } - err := os.MkdirAll(packagingFormatPath(packagingFormat), 0775) - if err != nil { - fmt.Printf("hover: Failed to create %s directory %s: %v\n", packagingFormat, packagingFormatPath(packagingFormat), err) - os.Exit(1) - } -} - -func assertPackagingFormatInitialized(packagingFormat string) { - if _, err := os.Stat(packagingFormatPath(packagingFormat)); os.IsNotExist(err) { - fmt.Printf("hover: %s is not initialized for packaging. Please run `hover init-packaging %s` first.\n", packagingFormat, packagingFormat) - os.Exit(1) - } -} - -func assertCorrectOS(packagingFormat string) { - if runtime.GOOS != strings.Split(packagingFormat, "-")[0] { - fmt.Printf("hover: %s only works on %s\n", packagingFormat, strings.Split(packagingFormat, "-")[0]) - os.Exit(1) - } -} - -func removeDashesAndUnderscores(projectName string) string { - return strings.ReplaceAll(strings.ReplaceAll(projectName, "-", ""), "_", "") -} - -func printInitFinished(packagingFormat string) { - fmt.Printf("hover: go/packaging/%s has been created. You can modify the configuration files and add it to git.\n", packagingFormat) - fmt.Printf("hover: You now can package the %s using `hover build %s`\n", strings.Split(packagingFormat, "-")[0], packagingFormat) -} - -func getTemporaryBuildDirectory(projectName string, packagingFormat string) string { - tmpPath, err := ioutil.TempDir("", "hover-build-"+projectName+"-"+packagingFormat) - if err != nil { - fmt.Printf("hover: Couldn't get temporary build directory: %v\n", err) - os.Exit(1) - } - return tmpPath -} - -func initLinuxSnap(projectName string) { - packagingFormat := "linux-snap" - assertCorrectOS(packagingFormat) - createPackagingFormatDirectory(packagingFormat) - snapDirectoryPath := packagingFormatPath(packagingFormat) - - snapLocalDirectoryPath, err := filepath.Abs(filepath.Join(snapDirectoryPath, "snap", "local")) - if err != nil { - fmt.Printf("hover: Failed to resolve absolute path for snap local directory: %v\n", err) - os.Exit(1) - } - err = os.MkdirAll(snapLocalDirectoryPath, 0775) - if err != nil { - fmt.Printf("hover: Failed to create snap local directory %s: %v\n", snapDirectoryPath, err) - os.Exit(1) - } - - snapcraftFilePath, err := filepath.Abs(filepath.Join(snapDirectoryPath, "snap", "snapcraft.yaml")) - if err != nil { - fmt.Printf("hover: Failed to resolve absolute path for snapcraft.yaml file %s: %v\n", snapcraftFilePath, err) - os.Exit(1) - } - - snapcraftFile, err := os.Create(snapcraftFilePath) - if err != nil { - fmt.Printf("hover: Failed to create snapcraft.yaml file %s: %v\n", snapcraftFilePath, err) - os.Exit(1) - } - snapcraftFileContent := []string{ - "name: " + removeDashesAndUnderscores(projectName), - "base: core18", - "version: '" + getPubSpec().Version + "'", - "summary: " + getPubSpec().Description, - "description: |", - " " + getPubSpec().Description, - "confinement: devmode", - "grade: devel", - "apps:", - " " + removeDashesAndUnderscores(projectName) + ":", - " command: " + projectName, - " desktop: local/" + projectName + ".desktop", - "parts:", - " desktop:", - " plugin: dump", - " source: snap", - " assets:", - " plugin: dump", - " source: assets", - " app:", - " plugin: dump", - " source: build", - " stage-packages:", - } - for _, dependency := range linuxPackagingDependencies { - snapcraftFileContent = append(snapcraftFileContent, " - "+dependency) - } - - for _, line := range snapcraftFileContent { - if _, err := snapcraftFile.WriteString(line + "\n"); err != nil { - fmt.Printf("hover: Could not write snapcraft.yaml: %v\n", err) - os.Exit(1) - } - } - err = snapcraftFile.Close() - if err != nil { - fmt.Printf("hover: Could not close snapcraft.yaml: %v\n", err) - os.Exit(1) - } - - desktopFilePath, err := filepath.Abs(filepath.Join(snapLocalDirectoryPath, projectName+".desktop")) - if err != nil { - fmt.Printf("hover: Failed to resolve absolute path for desktop file %s: %v\n", desktopFilePath, err) - os.Exit(1) - } - desktopFile, err := os.Create(desktopFilePath) - if err != nil { - fmt.Printf("hover: Failed to create desktop file %s: %v\n", desktopFilePath, err) - os.Exit(1) - } - desktopFileContent := []string{ - "[Desktop Entry]", - "Encoding=UTF-8", - "Version=" + getPubSpec().Version, - "Type=Application", - "Terminal=false", - "Exec=/" + projectName, - "Name=" + projectName, - "Icon=/icon.png", - } - - for _, line := range desktopFileContent { - if _, err := desktopFile.WriteString(line + "\n"); err != nil { - fmt.Printf("hover: Could not write %s.desktop: %v\n", projectName, err) - os.Exit(1) - } - } - err = desktopFile.Close() - if err != nil { - fmt.Printf("hover: Could not close %s.desktop: %v\n", projectName, err) - os.Exit(1) - } - - printInitFinished(packagingFormat) -} - -func buildLinuxSnap(projectName string) { - packagingFormat := "linux-snap" - assertCorrectOS(packagingFormat) - snapcraftBin, err := exec.LookPath("snapcraft") - if err != nil { - fmt.Println("hover: Failed to lookup `snapcraft` executable. Please install snapcraft.\nhttps://tutorials.ubuntu.com/tutorial/create-your-first-snap#1") - os.Exit(1) - } - tmpPath := getTemporaryBuildDirectory(projectName, packagingFormat) - fmt.Printf("hover: Packaging snap in %s\n", tmpPath) - - err = copy.Copy(filepath.Join(buildPath, "assets"), filepath.Join(tmpPath, "assets")) - if err != nil { - fmt.Printf("hover: Could not copy assets folder: %v\n", err) - os.Exit(1) - } - err = copy.Copy(outputDirectoryPath("linux"), filepath.Join(tmpPath, "build")) - if err != nil { - fmt.Printf("hover: Could not copy build folder: %v\n", err) - os.Exit(1) - } - err = copy.Copy(packagingFormatPath(packagingFormat), filepath.Join(tmpPath)) - if err != nil { - fmt.Printf("hover: Could not copy packaging configuration folder: %v\n", err) - os.Exit(1) - } - - cmdBuildSnap := exec.Command(snapcraftBin) - cmdBuildSnap.Dir = tmpPath - cmdBuildSnap.Stdout = os.Stdout - cmdBuildSnap.Stderr = os.Stderr - cmdBuildSnap.Stdin = os.Stdin - err = cmdBuildSnap.Run() - if err != nil { - fmt.Printf("hover: Failed to package snap: %v\n", err) - os.Exit(1) - } - outputFilePath := filepath.Join(outputDirectoryPath("linux-snap"), removeDashesAndUnderscores(projectName)+"_"+runtime.GOARCH+".snap") - err = os.Rename(filepath.Join(tmpPath, removeDashesAndUnderscores(projectName)+"_"+getPubSpec().Version+"_"+runtime.GOARCH+".snap"), outputFilePath) - if err != nil { - fmt.Printf("hover: Could not move snap file: %v\n", err) - os.Exit(1) - } - err = os.RemoveAll(tmpPath) - if err != nil { - fmt.Printf("hover: Could not remove packaging configuration folder: %v\n", err) - os.Exit(1) - } -} - -func initLinuxDeb(projectName string) { - packagingFormat := "linux-deb" - assertCorrectOS(packagingFormat) - author := getPubSpec().Author - if author == "" { - fmt.Println("hover: Missing author field in pubspec.yaml") - u, err := user.Current() - if err != nil { - fmt.Printf("hover: Couldn't get current user: %v\n", err) - os.Exit(1) - } - author = u.Username - fmt.Printf("hover: Using this username from system instead: %s\n", author) - } - createPackagingFormatDirectory(packagingFormat) - debDirectoryPath := packagingFormatPath(packagingFormat) - debDebianDirectoryPath, err := filepath.Abs(filepath.Join(debDirectoryPath, "DEBIAN")) - if err != nil { - fmt.Printf("hover: Failed to resolve absolute path for DEBIAN directory: %v\n", err) - os.Exit(1) - } - err = os.MkdirAll(debDebianDirectoryPath, 0775) - if err != nil { - fmt.Printf("hover: Failed to create DEBIAN directory %s: %v\n", debDebianDirectoryPath, err) - os.Exit(1) - } - - binDirectoryPath, err := filepath.Abs(filepath.Join(debDirectoryPath, "usr", "bin")) - if err != nil { - fmt.Printf("hover: Failed to resolve absolute path for bin directory: %v\n", err) - os.Exit(1) - } - err = os.MkdirAll(binDirectoryPath, 0775) - if err != nil { - fmt.Printf("hover: Failed to create bin directory %s: %v\n", binDirectoryPath, err) - os.Exit(1) - } - applicationsDirectoryPath, err := filepath.Abs(filepath.Join(debDirectoryPath, "usr", "share", "applications")) - if err != nil { - fmt.Printf("hover: Failed to resolve absolute path for applications directory: %v\n", err) - os.Exit(1) - } - err = os.MkdirAll(applicationsDirectoryPath, 0775) - if err != nil { - fmt.Printf("hover: Failed to create applications directory %s: %v\n", applicationsDirectoryPath, err) - os.Exit(1) - } - - controlFilePath, err := filepath.Abs(filepath.Join(debDebianDirectoryPath, "control")) - if err != nil { - fmt.Printf("hover: Failed to resolve absolute path for control file %s: %v\n", controlFilePath, err) - os.Exit(1) - } - - controlFile, err := os.Create(controlFilePath) - if err != nil { - fmt.Printf("hover: Failed to create control file %s: %v\n", controlFilePath, err) - os.Exit(1) - } - controlFileContent := []string{ - "Package: " + removeDashesAndUnderscores(projectName), - "Architecture: " + runtime.GOARCH, - "Maintainer: @" + getPubSpec().Author, - "Priority: optional", - "Version: " + getPubSpec().Version, - "Description: " + getPubSpec().Description, - "Depends: " + strings.Join(linuxPackagingDependencies, ","), - } - - for _, line := range controlFileContent { - if _, err := controlFile.WriteString(line + "\n"); err != nil { - fmt.Printf("hover: Could not write control file: %v\n", err) - os.Exit(1) - } - } - err = controlFile.Close() - if err != nil { - fmt.Printf("hover: Could not close control file: %v\n", err) - os.Exit(1) - } - - binFilePath, err := filepath.Abs(filepath.Join(binDirectoryPath, removeDashesAndUnderscores(projectName))) - if err != nil { - fmt.Printf("hover: Failed to resolve absolute path for bin file %s: %v\n", binFilePath, err) - os.Exit(1) - } - - binFile, err := os.Create(binFilePath) - if err != nil { - fmt.Printf("hover: Failed to create bin file %s: %v\n", controlFilePath, err) - os.Exit(1) - } - binFileContent := []string{ - "#!/bin/sh", - "/usr/lib/" + projectName + "/" + projectName, - } - for _, line := range binFileContent { - if _, err := binFile.WriteString(line + "\n"); err != nil { - fmt.Printf("hover: Could not write bin file: %v\n", err) - os.Exit(1) - } - } - err = binFile.Close() - if err != nil { - fmt.Printf("hover: Could not close bin file: %v\n", err) - os.Exit(1) - } - err = os.Chmod(binFilePath, 0777) - if err != nil { - fmt.Printf("hover: Failed to change file permissions for bin file: %v\n", err) - os.Exit(1) - } - - desktopFilePath, err := filepath.Abs(filepath.Join(applicationsDirectoryPath, projectName+".desktop")) - if err != nil { - fmt.Printf("hover: Failed to resolve absolute path for desktop file %s: %v\n", desktopFilePath, err) - os.Exit(1) - } - desktopFile, err := os.Create(desktopFilePath) - if err != nil { - fmt.Printf("hover: Failed to create desktop file %s: %v\n", desktopFilePath, err) - os.Exit(1) - } - desktopFileContent := []string{ - "[Desktop Entry]", - "Encoding=UTF-8", - "Version=" + getPubSpec().Version, - "Type=Application", - "Terminal=false", - "Exec=/usr/bin/" + projectName, - "Name=" + projectName, - "Icon=/usr/lib/" + projectName + "/assets/icon.png", - } - for _, line := range desktopFileContent { - if _, err := desktopFile.WriteString(line + "\n"); err != nil { - fmt.Printf("hover: Could not write %s.desktop file: %v\n", projectName, err) - os.Exit(1) - } - } - err = desktopFile.Close() - if err != nil { - fmt.Printf("hover: Could not close %s.desktop file: %v\n", projectName, err) - os.Exit(1) - } - - printInitFinished(packagingFormat) -} - -func buildLinuxDeb(projectName string) { - packagingFormat := "linux-deb" - assertCorrectOS(packagingFormat) - dpkgDebBin, err := exec.LookPath("dpkg-deb") - if err != nil { - fmt.Println("hover: Failed to lookup `dpkg-deb` executable. Please install dpkg-deb.") - os.Exit(1) - } - tmpPath := getTemporaryBuildDirectory(projectName, packagingFormat) - fmt.Printf("hover: Packaging deb in %s\n", tmpPath) - - libDirectoryPath, err := filepath.Abs(filepath.Join(tmpPath, "usr", "lib")) - if err != nil { - fmt.Printf("hover: Failed to resolve absolute path for bin directory: %v\n", err) - os.Exit(1) - } - err = copy.Copy(outputDirectoryPath("linux"), filepath.Join(libDirectoryPath, projectName)) - if err != nil { - fmt.Printf("hover: Could not copy build folder: %v\n", err) - os.Exit(1) - } - err = copy.Copy(packagingFormatPath(packagingFormat), filepath.Join(tmpPath)) - if err != nil { - fmt.Printf("hover: Could not copy packaging configuration folder: %v\n", err) - os.Exit(1) - } - outputFileName := removeDashesAndUnderscores(projectName) + "_" + runtime.GOARCH + ".deb" - outputFilePath := filepath.Join(outputDirectoryPath("linux-deb"), outputFileName) +var initWindowsMsiCmd = &cobra.Command{ + Use: "windows-msi", + Short: "Create configuration files for msi packaging", + Run: func(cmd *cobra.Command, args []string) { + assertHoverInitialized() + packaging.DockerInstalled() - cmdBuildDeb := exec.Command(dpkgDebBin, "--build", ".", outputFileName) - cmdBuildDeb.Dir = tmpPath - cmdBuildDeb.Stdout = os.Stdout - cmdBuildDeb.Stderr = os.Stderr - cmdBuildDeb.Stdin = os.Stdin - err = cmdBuildDeb.Run() - if err != nil { - fmt.Printf("hover: Failed to package deb: %v\n", err) - os.Exit(1) - } - err = os.Rename(filepath.Join(tmpPath, outputFileName), outputFilePath) - if err != nil { - fmt.Printf("hover: Could not move deb file: %v\n", err) - os.Exit(1) - } - err = os.RemoveAll(tmpPath) - if err != nil { - fmt.Printf("hover: Could not remove packaging configuration folder: %v\n", err) - os.Exit(1) - } + packaging.InitWindowsMsi() + }, } diff --git a/cmd/packaging/linux-deb.go b/cmd/packaging/linux-deb.go new file mode 100644 index 00000000..0b5805c6 --- /dev/null +++ b/cmd/packaging/linux-deb.go @@ -0,0 +1,166 @@ +package packaging + +import ( + "fmt" + "os" + "path/filepath" + "runtime" + "strings" + + "github.com/otiai10/copy" + + "github.com/go-flutter-desktop/hover/internal/build" + "github.com/go-flutter-desktop/hover/internal/pubspec" +) + +func InitLinuxDeb() { + projectName := pubspec.GetPubSpec().Name + packagingFormat := "linux-deb" + createPackagingFormatDirectory(packagingFormat) + debDirectoryPath := packagingFormatPath(packagingFormat) + debDebianDirectoryPath, err := filepath.Abs(filepath.Join(debDirectoryPath, "DEBIAN")) + if err != nil { + fmt.Printf("hover: Failed to resolve absolute path for DEBIAN directory: %v\n", err) + os.Exit(1) + } + err = os.MkdirAll(debDebianDirectoryPath, 0775) + if err != nil { + fmt.Printf("hover: Failed to create DEBIAN directory %s: %v\n", debDebianDirectoryPath, err) + os.Exit(1) + } + + binDirectoryPath, err := filepath.Abs(filepath.Join(debDirectoryPath, "usr", "bin")) + if err != nil { + fmt.Printf("hover: Failed to resolve absolute path for bin directory: %v\n", err) + os.Exit(1) + } + err = os.MkdirAll(binDirectoryPath, 0775) + if err != nil { + fmt.Printf("hover: Failed to create bin directory %s: %v\n", binDirectoryPath, err) + os.Exit(1) + } + applicationsDirectoryPath, err := filepath.Abs(filepath.Join(debDirectoryPath, "usr", "share", "applications")) + if err != nil { + fmt.Printf("hover: Failed to resolve absolute path for applications directory: %v\n", err) + os.Exit(1) + } + err = os.MkdirAll(applicationsDirectoryPath, 0775) + if err != nil { + fmt.Printf("hover: Failed to create applications directory %s: %v\n", applicationsDirectoryPath, err) + os.Exit(1) + } + + controlFilePath, err := filepath.Abs(filepath.Join(debDebianDirectoryPath, "control")) + if err != nil { + fmt.Printf("hover: Failed to resolve absolute path for control file %s: %v\n", controlFilePath, err) + os.Exit(1) + } + + controlFile, err := os.Create(controlFilePath) + if err != nil { + fmt.Printf("hover: Failed to create control file %s: %v\n", controlFilePath, err) + os.Exit(1) + } + controlFileContent := []string{ + "Package: " + removeDashesAndUnderscores(projectName), + "Architecture: amd64", + "Maintainer: @" + getAuthor(), + "Priority: optional", + "Version: " + pubspec.GetPubSpec().Version, + "Description: " + pubspec.GetPubSpec().Description, + "Depends: " + strings.Join(linuxPackagingDependencies, ","), + } + + for _, line := range controlFileContent { + if _, err := controlFile.WriteString(line + "\n"); err != nil { + fmt.Printf("hover: Could not write control file: %v\n", err) + os.Exit(1) + } + } + err = controlFile.Close() + if err != nil { + fmt.Printf("hover: Could not close control file: %v\n", err) + os.Exit(1) + } + + binFilePath, err := filepath.Abs(filepath.Join(binDirectoryPath, removeDashesAndUnderscores(projectName))) + if err != nil { + fmt.Printf("hover: Failed to resolve absolute path for bin file %s: %v\n", binFilePath, err) + os.Exit(1) + } + + binFile, err := os.Create(binFilePath) + if err != nil { + fmt.Printf("hover: Failed to create bin file %s: %v\n", controlFilePath, err) + os.Exit(1) + } + binFileContent := []string{ + "#!/bin/sh", + "/usr/lib/" + projectName + "/" + projectName, + } + for _, line := range binFileContent { + if _, err := binFile.WriteString(line + "\n"); err != nil { + fmt.Printf("hover: Could not write bin file: %v\n", err) + os.Exit(1) + } + } + err = binFile.Close() + if err != nil { + fmt.Printf("hover: Could not close bin file: %v\n", err) + os.Exit(1) + } + err = os.Chmod(binFilePath, 0777) + if err != nil { + fmt.Printf("hover: Failed to change file permissions for bin file: %v\n", err) + os.Exit(1) + } + + desktopFilePath, err := filepath.Abs(filepath.Join(applicationsDirectoryPath, projectName+".desktop")) + if err != nil { + fmt.Printf("hover: Failed to resolve absolute path for desktop file %s: %v\n", desktopFilePath, err) + os.Exit(1) + } + createLinuxDesktopFile(desktopFilePath, packagingFormat, "/usr/bin/"+projectName, "/usr/lib/"+projectName+"/assets/icon.png") + createDockerfile(packagingFormat) + + printInitFinished(packagingFormat) +} + +func BuildLinuxDeb() { + projectName := pubspec.GetPubSpec().Name + packagingFormat := "linux-deb" + tmpPath := getTemporaryBuildDirectory(projectName, packagingFormat) + fmt.Printf("hover: Packaging deb in %s\n", tmpPath) + + libDirectoryPath, err := filepath.Abs(filepath.Join(tmpPath, "usr", "lib")) + if err != nil { + fmt.Printf("hover: Failed to resolve absolute path for lib directory: %v\n", err) + os.Exit(1) + } + err = copy.Copy(build.OutputDirectoryPath("linux"), filepath.Join(libDirectoryPath, projectName)) + if err != nil { + fmt.Printf("hover: Could not copy build folder: %v\n", err) + os.Exit(1) + } + err = copy.Copy(packagingFormatPath(packagingFormat), filepath.Join(tmpPath)) + if err != nil { + fmt.Printf("hover: Could not copy packaging configuration folder: %v\n", err) + os.Exit(1) + } + + outputFileName := removeDashesAndUnderscores(projectName) + "_" + runtime.GOARCH + ".deb" + outputFilePath := filepath.Join(build.OutputDirectoryPath("linux-deb"), outputFileName) + runDockerPackaging(tmpPath, packagingFormat, []string{"dpkg-deb", "--build", ".", outputFileName}) + + err = os.Rename(filepath.Join(tmpPath, outputFileName), outputFilePath) + if err != nil { + fmt.Printf("hover: Could not move deb file: %v\n", err) + os.Exit(1) + } + err = os.RemoveAll(tmpPath) + if err != nil { + fmt.Printf("hover: Could not remove temporary build directory: %v\n", err) + os.Exit(1) + } + printPackagingFinished(packagingFormat) +} diff --git a/cmd/packaging/linux-snap.go b/cmd/packaging/linux-snap.go new file mode 100644 index 00000000..b837c65a --- /dev/null +++ b/cmd/packaging/linux-snap.go @@ -0,0 +1,132 @@ +package packaging + +import ( + "fmt" + "os" + "path/filepath" + "runtime" + + "github.com/otiai10/copy" + + "github.com/go-flutter-desktop/hover/internal/build" + "github.com/go-flutter-desktop/hover/internal/pubspec" +) + +func InitLinuxSnap() { + projectName := pubspec.GetPubSpec().Name + packagingFormat := "linux-snap" + createPackagingFormatDirectory(packagingFormat) + snapDirectoryPath := packagingFormatPath(packagingFormat) + + snapLocalDirectoryPath, err := filepath.Abs(filepath.Join(snapDirectoryPath, "snap", "local")) + if err != nil { + fmt.Printf("hover: Failed to resolve absolute path for snap local directory: %v\n", err) + os.Exit(1) + } + err = os.MkdirAll(snapLocalDirectoryPath, 0775) + if err != nil { + fmt.Printf("hover: Failed to create snap local directory %s: %v\n", snapDirectoryPath, err) + os.Exit(1) + } + + snapcraftFilePath, err := filepath.Abs(filepath.Join(snapDirectoryPath, "snap", "snapcraft.yaml")) + if err != nil { + fmt.Printf("hover: Failed to resolve absolute path for snapcraft.yaml file %s: %v\n", snapcraftFilePath, err) + os.Exit(1) + } + + snapcraftFile, err := os.Create(snapcraftFilePath) + if err != nil { + fmt.Printf("hover: Failed to create snapcraft.yaml file %s: %v\n", snapcraftFilePath, err) + os.Exit(1) + } + snapcraftFileContent := []string{ + "name: " + removeDashesAndUnderscores(projectName), + "base: core18", + "version: '" + pubspec.GetPubSpec().Version + "'", + "summary: " + pubspec.GetPubSpec().Description, + "description: |", + " " + pubspec.GetPubSpec().Description, + "confinement: devmode", + "grade: devel", + "apps:", + " " + removeDashesAndUnderscores(projectName) + ":", + " command: " + projectName, + " desktop: local/" + projectName + ".desktop", + "parts:", + " desktop:", + " plugin: dump", + " source: snap", + " assets:", + " plugin: dump", + " source: assets", + " app:", + " plugin: dump", + " source: build", + " stage-packages:", + } + for _, dependency := range linuxPackagingDependencies { + snapcraftFileContent = append(snapcraftFileContent, " - "+dependency) + } + + for _, line := range snapcraftFileContent { + if _, err := snapcraftFile.WriteString(line + "\n"); err != nil { + fmt.Printf("hover: Could not write snapcraft.yaml: %v\n", err) + os.Exit(1) + } + } + err = snapcraftFile.Close() + if err != nil { + fmt.Printf("hover: Could not close snapcraft.yaml: %v\n", err) + os.Exit(1) + } + + desktopFilePath, err := filepath.Abs(filepath.Join(snapLocalDirectoryPath, projectName+".desktop")) + if err != nil { + fmt.Printf("hover: Failed to resolve absolute path for desktop file %s: %v\n", desktopFilePath, err) + os.Exit(1) + } + createLinuxDesktopFile(desktopFilePath, packagingFormat, "/"+projectName, "/icon.png") + createDockerfile(packagingFormat) + + printInitFinished(packagingFormat) +} + +func BuildLinuxSnap() { + projectName := pubspec.GetPubSpec().Name + packagingFormat := "linux-snap" + tmpPath := getTemporaryBuildDirectory(projectName, packagingFormat) + fmt.Printf("hover: Packaging snap in %s\n", tmpPath) + + err := copy.Copy(filepath.Join(build.BuildPath, "assets"), filepath.Join(tmpPath, "assets")) + if err != nil { + fmt.Printf("hover: Could not copy assets folder: %v\n", err) + os.Exit(1) + } + err = copy.Copy(build.OutputDirectoryPath("linux"), filepath.Join(tmpPath, "build")) + if err != nil { + fmt.Printf("hover: Could not copy build folder: %v\n", err) + os.Exit(1) + } + err = copy.Copy(packagingFormatPath(packagingFormat), filepath.Join(tmpPath)) + if err != nil { + fmt.Printf("hover: Could not copy packaging configuration folder: %v\n", err) + os.Exit(1) + } + + outputFileName := removeDashesAndUnderscores(projectName) + "_" + pubspec.GetPubSpec().Version + "_" + runtime.GOARCH + ".snap" + outputFilePath := filepath.Join(build.OutputDirectoryPath("linux-snap"), outputFileName) + runDockerPackaging(tmpPath, packagingFormat, []string{"snapcraft"}) + + err = os.Rename(filepath.Join(tmpPath, outputFileName), outputFilePath) + if err != nil { + fmt.Printf("hover: Could not move snap file: %v\n", err) + os.Exit(1) + } + err = os.RemoveAll(tmpPath) + if err != nil { + fmt.Printf("hover: Could not remove temporary build directory: %v\n", err) + os.Exit(1) + } + printPackagingFinished(packagingFormat) +} diff --git a/cmd/packaging/linux.go b/cmd/packaging/linux.go new file mode 100644 index 00000000..ae6987fa --- /dev/null +++ b/cmd/packaging/linux.go @@ -0,0 +1,39 @@ +package packaging + +import ( + "fmt" + "github.com/go-flutter-desktop/hover/internal/pubspec" + "os" +) + +var linuxPackagingDependencies = []string{"libx11-6", "libxrandr2", "libxcursor1", "libxinerama1", "libglu1-mesa", "libgcc1", "libstdc++6", "libtinfo5", "zlib1g"} + +func createLinuxDesktopFile(desktopFilePath string, packagingFormat string, exec string, icon string) { + desktopFile, err := os.Create(desktopFilePath) + if err != nil { + fmt.Printf("hover: Failed to create %s.desktop %s: %v\n", pubspec.GetPubSpec().Name, desktopFilePath, err) + os.Exit(1) + } + desktopFileContent := []string{ + "[Desktop Entry]", + "Encoding=UTF-8", + "Version=" + pubspec.GetPubSpec().Version, + "Type=Application", + "Terminal=false", + "Exec=" + exec, + "Name=" + pubspec.GetPubSpec().Name, + "Icon=" + icon, + } + + for _, line := range desktopFileContent { + if _, err := desktopFile.WriteString(line + "\n"); err != nil { + fmt.Printf("hover: Could not write %s.desktop: %v\n", pubspec.GetPubSpec().Name, err) + os.Exit(1) + } + } + err = desktopFile.Close() + if err != nil { + fmt.Printf("hover: Could not close %s.desktop: %v\n", pubspec.GetPubSpec().Name, err) + os.Exit(1) + } +} diff --git a/cmd/packaging/packaging.go b/cmd/packaging/packaging.go new file mode 100644 index 00000000..4ba5ae56 --- /dev/null +++ b/cmd/packaging/packaging.go @@ -0,0 +1,169 @@ +package packaging + +import ( + "fmt" + "github.com/go-flutter-desktop/hover/internal/pubspec" + "io/ioutil" + "os" + "os/exec" + "os/user" + "path/filepath" + "runtime" + "strings" + + "github.com/go-flutter-desktop/hover/internal/build" +) + +var packagingPath = filepath.Join(build.BuildPath, "packaging") + +func packagingFormatPath(packagingFormat string) string { + directoryPath, err := filepath.Abs(filepath.Join(packagingPath, packagingFormat)) + if err != nil { + fmt.Printf("hover: Failed to resolve absolute path for %s directory: %v\n", packagingFormat, err) + os.Exit(1) + } + return directoryPath +} + +func createPackagingFormatDirectory(packagingFormat string) { + if _, err := os.Stat(packagingFormatPath(packagingFormat)); !os.IsNotExist(err) { + fmt.Printf("hover: A file or directory named `%s` already exists. Cannot continue packaging init for %s.\n", packagingFormat, packagingFormat) + os.Exit(1) + } + err := os.MkdirAll(packagingFormatPath(packagingFormat), 0775) + if err != nil { + fmt.Printf("hover: Failed to create %s directory %s: %v\n", packagingFormat, packagingFormatPath(packagingFormat), err) + os.Exit(1) + } +} + +func AssertPackagingFormatInitialized(packagingFormat string) { + if _, err := os.Stat(packagingFormatPath(packagingFormat)); os.IsNotExist(err) { + fmt.Printf("hover: %s is not initialized for packaging. Please run `hover init-packaging %s` first.\n", packagingFormat, packagingFormat) + os.Exit(1) + } +} + +func removeDashesAndUnderscores(projectName string) string { + return strings.ReplaceAll(strings.ReplaceAll(projectName, "-", ""), "_", "") +} + +func printInitFinished(packagingFormat string) { + fmt.Printf("hover: go/packaging/%s has been created. You can modify the configuration files and add it to git.\n", packagingFormat) + fmt.Printf("hover: You now can package the %s using `hover build %s`\n", strings.Split(packagingFormat, "-")[0], packagingFormat) +} + +func printPackagingFinished(packagingFormat string) { + fmt.Printf("hover: Successfully packaged %s\n", strings.Split(packagingFormat, "-")[1]) +} + +func getTemporaryBuildDirectory(projectName string, packagingFormat string) string { + tmpPath, err := ioutil.TempDir("", "hover-build-"+projectName+"-"+packagingFormat) + if err != nil { + fmt.Printf("hover: Couldn't get temporary build directory: %v\n", err) + os.Exit(1) + } + return tmpPath +} + +func DockerInstalled() bool { + if build.DockerBin == "" { + fmt.Println("hover: To use packaging, Docker needs to be installed.\nhttps://docs.docker.com/install") + } + return build.DockerBin != "" +} + +func getAuthor() string { + author := pubspec.GetPubSpec().Author + if author == "" { + fmt.Println("hover: Missing author field in pubspec.yaml") + u, err := user.Current() + if err != nil { + fmt.Printf("hover: Couldn't get current user: %v\n", err) + os.Exit(1) + } + author = u.Username + fmt.Printf("hover: Using this username from system instead: %s\n", author) + } + return author +} + +func createDockerfile(packagingFormat string) { + dockerFilePath, err := filepath.Abs(filepath.Join(packagingFormatPath(packagingFormat), "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{} + if packagingFormat == "linux-snap" { + dockerFileContent = []string{ + "FROM snapcore/snapcraft", + } + } else if packagingFormat == "linux-deb" { + dockerFileContent = []string{ + "FROM ubuntu:bionic", + } + } else if packagingFormat == "windows-msi" { + dockerFileContent = []string{ + "FROM ubuntu:bionic", + "RUN apt-get update && apt-get install wixl -y", + } + } else { + fmt.Printf("hover: Tried to create Dockerfile for unknown packaging format %s\n", packagingFormat) + os.Exit(1) + } + + 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) + } +} + +func runDockerPackaging(path string, packagingFormat string, command []string) { + dockerBuildCmd := exec.Command(build.DockerBin, "build", "-t", "hover-build-packaging-"+packagingFormat, ".") + dockerBuildCmd.Stdout = os.Stdout + dockerBuildCmd.Stderr = os.Stderr + dockerBuildCmd.Dir = packagingFormatPath(packagingFormat) + err := dockerBuildCmd.Run() + if err != nil { + fmt.Printf("hover: Docker build failed: %v\n", err) + os.Exit(1) + } + 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", + "-v", path + ":/app", + } + args = append(args, "hover-build-packaging-"+packagingFormat) + chownStr := "" + if runtime.GOOS != "windows" { + chownStr = fmt.Sprintf(" && chown %s:%s * -R", u.Uid, u.Gid) + } + args = append(args, "bash", "-c", fmt.Sprintf("%s%s", strings.Join(command, " "), chownStr)) + dockerRunCmd := exec.Command(build.DockerBin, args...) + dockerRunCmd.Stderr = os.Stderr + dockerRunCmd.Stdout = os.Stdout + dockerRunCmd.Dir = path + err = dockerRunCmd.Run() + if err != nil { + fmt.Printf("hover: Docker run failed: %v\n", err) + os.Exit(1) + } +} diff --git a/cmd/packaging/windows-msi.go b/cmd/packaging/windows-msi.go new file mode 100644 index 00000000..18186255 --- /dev/null +++ b/cmd/packaging/windows-msi.go @@ -0,0 +1,263 @@ +package packaging + +import ( + "fmt" + "github.com/otiai10/copy" + "io/ioutil" + "log" + "os" + "path/filepath" + "strings" + + "github.com/go-flutter-desktop/hover/internal/build" + "github.com/go-flutter-desktop/hover/internal/pubspec" +) + +var directoriesFileContent = []string{} +var directoryRefsFileContent = []string{} +var componentRefsFileContent = []string{} + +func InitWindowsMsi() { + projectName := pubspec.GetPubSpec().Name + packagingFormat := "windows-msi" + createPackagingFormatDirectory(packagingFormat) + msiDirectoryPath := packagingFormatPath(packagingFormat) + + wxsFilePath, err := filepath.Abs(filepath.Join(msiDirectoryPath, projectName+".wxs")) + if err != nil { + fmt.Printf("hover: Failed to resolve absolute path for %s.wxs file %s: %v\n", projectName, wxsFilePath, err) + os.Exit(1) + } + wxsFile, err := os.Create(wxsFilePath) + if err != nil { + fmt.Printf("hover: Failed to create %s.wxs file %s: %v\n", projectName, wxsFilePath, err) + os.Exit(1) + } + wxsFileContent := []string{ + ``, + ``, + fmt.Sprintf(` `, pubspec.GetPubSpec().Version, projectName, getAuthor()), + ` `, + fmt.Sprintf(` `, projectName), + ` `, + ` `, + fmt.Sprintf(` `, projectName), + ` `, + ` `, + ` `, + ` `, + ` `, + ` `, + ` `, + fmt.Sprintf(` `, projectName), + ` `, + ` `, + ` `, + ` `, + fmt.Sprintf(` `, projectName), + fmt.Sprintf(` `, projectName, projectName), + ` `, + ` `, + ` `, + ` `, + ` `, + ` `, + ` `, + ` `, + ` `, + ` `, + ` `, + ` `, + ` `, + ` `, + ` `, + ` `, + ` `, + ` `, + fmt.Sprintf(` `, getAuthor(), projectName), + ` `, + ` `, + fmt.Sprintf(` `, projectName), + fmt.Sprintf(` `, projectName), + ` `, + ` `, + ` `, + ` `, + ` `, + ` `, + ` `, + ``, + } + + for _, line := range wxsFileContent { + if _, err := wxsFile.WriteString(line + "\n"); err != nil { + fmt.Printf("hover: Could not write %s.wxs: %v\n", projectName, err) + os.Exit(1) + } + } + err = wxsFile.Close() + if err != nil { + fmt.Printf("hover: Could not close %s.wxs: %v\n", projectName, err) + os.Exit(1) + } + + createDockerfile(packagingFormat) + + printInitFinished(packagingFormat) +} + +func BuildWindowsMsi() { + projectName := pubspec.GetPubSpec().Name + packagingFormat := "windows-msi" + tmpPath := getTemporaryBuildDirectory(projectName, packagingFormat) + fmt.Printf("hover: Packaging msi in %s\n", tmpPath) + + buildDirectoryPath, err := filepath.Abs(filepath.Join(tmpPath, "build")) + if err != nil { + fmt.Printf("hover: Failed to resolve absolute path for build directory: %v\n", err) + os.Exit(1) + } + err = copy.Copy(build.OutputDirectoryPath("windows"), filepath.Join(buildDirectoryPath)) + if err != nil { + fmt.Printf("hover: Could not copy build folder: %v\n", err) + os.Exit(1) + } + err = copy.Copy(packagingFormatPath(packagingFormat), filepath.Join(tmpPath)) + if err != nil { + fmt.Printf("hover: Could not copy packaging configuration folder: %v\n", err) + os.Exit(1) + } + directoriesFilePath, err := filepath.Abs(filepath.Join(tmpPath, "directories.wxi")) + if err != nil { + fmt.Printf("hover: Failed to resolve absolute path for directories.wxi file %s: %v\n", projectName, err) + os.Exit(1) + } + directoriesFile, err := os.Create(directoriesFilePath) + if err != nil { + fmt.Printf("hover: Failed to create directories.wxi file %s: %v\n", projectName, err) + os.Exit(1) + } + directoryRefsFilePath, err := filepath.Abs(filepath.Join(tmpPath, "directory_refs.wxi")) + if err != nil { + fmt.Printf("hover: Failed to resolve absolute path for directory_refs.wxi file %s: %v\n", projectName, err) + os.Exit(1) + } + directoryRefsFile, err := os.Create(directoryRefsFilePath) + if err != nil { + fmt.Printf("hover: Failed to create directory_refs.wxi file %s: %v\n", projectName, err) + os.Exit(1) + } + componentRefsFilePath, err := filepath.Abs(filepath.Join(tmpPath, "component_refs.wxi")) + if err != nil { + fmt.Printf("hover: Failed to resolve absolute path for component_refs.wxi file %s: %v\n", projectName, err) + os.Exit(1) + } + componentRefsFile, err := os.Create(componentRefsFilePath) + if err != nil { + fmt.Printf("hover: Failed to create component_refs.wxi file %s: %v\n", projectName, err) + os.Exit(1) + } + directoriesFileContent = append(directoriesFileContent, ``) + directoryRefsFileContent = append(directoryRefsFileContent, ``, ) + componentRefsFileContent = append(componentRefsFileContent, ``, ) + processFiles(filepath.Join(buildDirectoryPath, "flutter_assets")) + directoriesFileContent = append(directoriesFileContent, ``) + directoryRefsFileContent = append(directoryRefsFileContent, ``, ) + componentRefsFileContent = append(componentRefsFileContent, ``, ) + + for _, line := range directoriesFileContent { + if _, err := directoriesFile.WriteString(line + "\n"); err != nil { + fmt.Printf("hover: Could not write directories.wxi: %v\n", projectName, err) + os.Exit(1) + } + } + err = directoriesFile.Close() + if err != nil { + fmt.Printf("hover: Could not close directories.wxi: %v\n", projectName, err) + os.Exit(1) + } + for _, line := range directoryRefsFileContent { + if _, err := directoryRefsFile.WriteString(line + "\n"); err != nil { + fmt.Printf("hover: Could not write directory_refs.wxi: %v\n", projectName, err) + os.Exit(1) + } + } + err = directoryRefsFile.Close() + if err != nil { + fmt.Printf("hover: Could not close directory_refs.wxi: %v\n", projectName, err) + os.Exit(1) + } + for _, line := range componentRefsFileContent { + if _, err := componentRefsFile.WriteString(line + "\n"); err != nil { + fmt.Printf("hover: Could not write component_refs.wxi: %v\n", projectName, err) + os.Exit(1) + } + } + err = componentRefsFile.Close() + if err != nil { + fmt.Printf("hover: Could not close component_refs.wxi: %v\n", projectName, err) + os.Exit(1) + } + + outputFileName := projectName + ".msi" + outputFilePath := filepath.Join(build.OutputDirectoryPath("windows-msi"), outputFileName) + runDockerPackaging(tmpPath, packagingFormat, []string{"wixl", "-v", projectName + ".wxs"}) + + err = os.Rename(filepath.Join(tmpPath, outputFileName), outputFilePath) + if err != nil { + fmt.Printf("hover: Could not move msi file: %v\n", err) + os.Exit(1) + } + err = os.RemoveAll(tmpPath) + if err != nil { + fmt.Printf("hover: Could not remove temporary build directory: %v\n", err) + os.Exit(1) + } + + printPackagingFinished(packagingFormat) +} + +func processFiles(path string) { + files, err := ioutil.ReadDir(path) + if err != nil { + log.Fatal(err) + } + + for _, f := range files { + p := filepath.Join(path, f.Name()) + relativePath := strings.Split(strings.Split(p, "flutter_assets"+string(filepath.Separator))[1], string(filepath.Separator)) + if f.IsDir() { + directoriesFileContent = append(directoriesFileContent, + ``, + ) + processFiles(p) + directoriesFileContent = append(directoriesFileContent, + ``, + ) + } else { + if len(relativePath) > 1 { + directoryRefsFileContent = append(directoryRefsFileContent, + ``, + ) + } else { + directoryRefsFileContent = append(directoryRefsFileContent, + ``, + ) + } + directoryRefsFileContent = append(directoryRefsFileContent, + ``, + ``, + ``, + ``, + ) + componentRefsFileContent = append(componentRefsFileContent, + ``, + ) + } + } +} diff --git a/cmd/run.go b/cmd/run.go index 67abbc6e..1d995fe4 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -3,6 +3,8 @@ package cmd import ( "bufio" "fmt" + "github.com/go-flutter-desktop/hover/internal/build" + "github.com/go-flutter-desktop/hover/internal/pubspec" "io" "os" "os/exec" @@ -20,7 +22,6 @@ func init() { runCmd.Flags().StringVarP(&buildManifest, "manifest", "m", "pubspec.yaml", "Flutter manifest file of the application.") runCmd.Flags().StringVarP(&buildBranch, "branch", "b", "", "The 'go-flutter' version to use. (@master or @v0.20.0 for example)") runCmd.Flags().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)") - runCmd.Flags().StringVar(&buildOpenGlVersion, "opengl", "3.3", "The OpenGL version specified here is only relevant for external texture plugin (i.e. video_plugin).\nIf 'none' is provided, texture won't be supported. Note: the Flutter Engine still needs a OpenGL compatible context.") 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)") @@ -32,7 +33,7 @@ var runCmd = &cobra.Command{ Use: "run", Short: "Build and start a desktop release, with hot-reload support", Run: func(cmd *cobra.Command, args []string) { - projectName := getPubSpec().Name + projectName := pubspec.GetPubSpec().Name assertHoverInitialized() // ensure we have something to build @@ -47,14 +48,14 @@ var runCmd = &cobra.Command{ // forcefully enable --debug (which is not an option for `hover run`) buildDebug = true - build(projectName, targetOS, []string{"--observatory-port=" + runObservatoryPort}) + buildNormal(targetOS, []string{"--observatory-port=" + runObservatoryPort}) fmt.Println("hover: build finished, starting app...") runAndAttach(projectName, targetOS) }, } func runAndAttach(projectName string, targetOS string) { - cmdApp := exec.Command(dotSlash + filepath.Join(buildPath, "build", "outputs", targetOS, projectName)) + cmdApp := exec.Command(dotSlash + filepath.Join(build.BuildPath, "build", "outputs", targetOS, projectName)) cmdFlutterAttach := exec.Command("flutter", "attach") stdoutApp, err := cmdApp.StdoutPipe() diff --git a/cmd/upgrade.go b/cmd/upgrade.go index bbcdd18e..93ebae71 100644 --- a/cmd/upgrade.go +++ b/cmd/upgrade.go @@ -2,6 +2,7 @@ package cmd import ( "fmt" + "github.com/go-flutter-desktop/hover/internal/build" "os" "os/exec" "path/filepath" @@ -64,8 +65,8 @@ func upgradeGoFlutter(targetOS string, engineCachePath string) (err error) { return } - cmdGoGetU := exec.Command(goBin, "get", "-u", "github.com/go-flutter-desktop/go-flutter"+buildBranch) - cmdGoGetU.Dir = filepath.Join(wd, buildPath) + cmdGoGetU := exec.Command(build.GoBin, "get", "-u", "github.com/go-flutter-desktop/go-flutter"+buildBranch) + cmdGoGetU.Dir = filepath.Join(wd, build.BuildPath) cmdGoGetU.Env = append(os.Environ(), "GO111MODULE=on", "CGO_LDFLAGS="+cgoLdflags, @@ -83,8 +84,8 @@ func upgradeGoFlutter(targetOS string, engineCachePath string) (err error) { return } - cmdGoModDownload := exec.Command(goBin, "mod", "download") - cmdGoModDownload.Dir = filepath.Join(wd, buildPath) + cmdGoModDownload := exec.Command(build.GoBin, "mod", "download") + cmdGoModDownload.Dir = filepath.Join(wd, build.BuildPath) cmdGoModDownload.Env = append(os.Environ(), "GO111MODULE=on", ) @@ -97,7 +98,7 @@ func upgradeGoFlutter(targetOS string, engineCachePath string) (err error) { return } - currentTag, err := versioncheck.CurrentGoFlutterTag(filepath.Join(wd, buildPath)) + currentTag, err := versioncheck.CurrentGoFlutterTag(filepath.Join(wd, build.BuildPath)) if err != nil { fmt.Printf("hover: %v\n", err) os.Exit(1) diff --git a/internal/build/binaries.go b/internal/build/binaries.go new file mode 100644 index 00000000..be21cda8 --- /dev/null +++ b/internal/build/binaries.go @@ -0,0 +1,7 @@ +package build + +var ( + GoBin string + FlutterBin string + DockerBin string +) diff --git a/internal/build/build.go b/internal/build/build.go new file mode 100644 index 00000000..a42ff34d --- /dev/null +++ b/internal/build/build.go @@ -0,0 +1,46 @@ +package build + +import ( + "fmt" + "os" + "path/filepath" +) + +const BuildPath = "go" + +func OutputDirectoryPath(targetOS string) string { + outputDirectoryPath, err := filepath.Abs(filepath.Join(BuildPath, "build", "outputs", targetOS)) + if err != nil { + fmt.Printf("hover: Failed to resolve absolute path for output directory: %v\n", err) + os.Exit(1) + } + if _, err := os.Stat(outputDirectoryPath); os.IsNotExist(err) { + err = os.MkdirAll(outputDirectoryPath, 0775) + if err != nil { + fmt.Printf("hover: Failed to create output directory %s: %v\n", outputDirectoryPath, err) + os.Exit(1) + } + } + return outputDirectoryPath +} + +func OutputBinaryName(projectName string, targetOS string) string { + var outputBinaryName = projectName + switch targetOS { + case "darwin": + // no special filename + case "linux": + // no special filename + case "windows": + outputBinaryName += ".exe" + default: + fmt.Printf("hover: Target platform %s is not supported.\n", targetOS) + os.Exit(1) + } + return outputBinaryName +} + +func OutputBinaryPath(projectName string, targetOS string) string { + outputBinaryPath := filepath.Join(OutputDirectoryPath(targetOS), OutputBinaryName(projectName, targetOS)) + return outputBinaryPath +} diff --git a/internal/pubspec/pubspec.go b/internal/pubspec/pubspec.go new file mode 100644 index 00000000..ae14d496 --- /dev/null +++ b/internal/pubspec/pubspec.go @@ -0,0 +1,51 @@ +package pubspec + +import ( + "fmt" + "gopkg.in/yaml.v2" + "os" +) + +type PubSpec struct { + Name string + Description string + Version string + Author string + Dependencies map[string]interface{} +} + +var pubspec = PubSpec{} + +func GetPubSpec() PubSpec { + { + if pubspec.Name == "" { + file, err := os.Open("pubspec.yaml") + if err != nil { + if os.IsNotExist(err) { + fmt.Println("hover: Error: No pubspec.yaml file found.") + goto Fail + } + fmt.Printf("hover: Failed to open pubspec.yaml: %v\n", err) + os.Exit(1) + } + defer file.Close() + + err = yaml.NewDecoder(file).Decode(&pubspec) + if err != nil { + fmt.Printf("hover: Failed to decode pubspec.yaml: %v\n", err) + goto Fail + } + if _, exists := pubspec.Dependencies["flutter"]; !exists { + fmt.Println("hover: Missing `flutter` in pubspec.yaml dependencies list.") + goto Fail + } + } + + return pubspec + } + +Fail: + fmt.Println("hover: This command should be run from the root of your Flutter project.") + os.Exit(1) + return PubSpec{} +} From 3aa03cd1f8dd4cdd4a9ed3fabb6cd1f98783555c Mon Sep 17 00:00:00 2001 From: jld3103 Date: Thu, 26 Sep 2019 18:59:25 +0200 Subject: [PATCH 2/2] Add AppImage --- cmd/build.go | 21 +++++++- cmd/packaging.go | 14 ++++- cmd/packaging/linux-appimage.go | 93 ++++++++++++++++++++++++++++++++ cmd/packaging/linux.go | 8 +-- cmd/packaging/packaging.go | 13 +++++ cmd/run.go | 3 +- cmd/upgrade.go | 11 ++-- internal/versioncheck/version.go | 4 +- 8 files changed, 153 insertions(+), 14 deletions(-) create mode 100644 cmd/packaging/linux-appimage.go diff --git a/cmd/build.go b/cmd/build.go index 939c62c1..b6eff016 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -51,6 +51,7 @@ func init() { buildCmd.AddCommand(buildLinuxCmd) buildCmd.AddCommand(buildLinuxSnapCmd) buildCmd.AddCommand(buildLinuxDebCmd) + buildCmd.AddCommand(buildLinuxAppImageCmd) buildCmd.AddCommand(buildDarwinCmd) buildCmd.AddCommand(buildWindowsCmd) buildCmd.AddCommand(buildWindowsMsiCmd) @@ -104,6 +105,22 @@ var buildLinuxDebCmd = &cobra.Command{ }, } +var buildLinuxAppImageCmd = &cobra.Command{ + Use: "linux-appimage", + Short: "Build a desktop release for linux and package it for AppImage", + Run: func(cmd *cobra.Command, args []string) { + assertHoverInitialized() + packaging.AssertPackagingFormatInitialized("linux-appimage") + + if !packaging.DockerInstalled() { + os.Exit(1) + } + + buildNormal("linux", nil) + packaging.BuildLinuxAppImage() + }, +} + var buildDarwinCmd = &cobra.Command{ Use: "darwin", Short: "Build a desktop release for darwin", @@ -374,7 +391,7 @@ func buildNormal(targetOS string, vmArguments []string) { } if semver.Prerelease() != "" { - log.Infof("Upgrade 'go-flutter' to the latest release") + log.Infof("Upgrading 'go-flutter' to the latest release") // no buildBranch provided and currentTag isn't a release, // force update. (same behaviour as previous version of hover). err = upgradeGoFlutter(targetOS, engineCachePath) @@ -385,7 +402,7 @@ func buildNormal(targetOS string, vmArguments []string) { } else { // when the buildBranch is empty and the currentTag is a release. // Check if the 'go-flutter' needs updates. - versioncheck.CheckFoGoFlutterUpdate(filepath.Join(wd, build.BuildPath), currentTag) + versioncheck.CheckForGoFlutterUpdate(filepath.Join(wd, build.BuildPath), currentTag) } } else { diff --git a/cmd/packaging.go b/cmd/packaging.go index 0dc9bdc4..bab95b06 100644 --- a/cmd/packaging.go +++ b/cmd/packaging.go @@ -9,6 +9,7 @@ import ( func init() { initPackagingCmd.AddCommand(initLinuxSnapCmd) initPackagingCmd.AddCommand(initLinuxDebCmd) + initPackagingCmd.AddCommand(initLinuxAppImageCmd) initPackagingCmd.AddCommand(initWindowsMsiCmd) rootCmd.AddCommand(initPackagingCmd) } @@ -40,6 +41,17 @@ var initLinuxDebCmd = &cobra.Command{ }, } +var initLinuxAppImageCmd = &cobra.Command{ + Use: "linux-appimage", + Short: "Create configuration files for AppImage packaging", + Run: func(cmd *cobra.Command, args []string) { + assertHoverInitialized() + packaging.DockerInstalled() + + packaging.InitLinuxAppImage() + }, +} + var initWindowsMsiCmd = &cobra.Command{ Use: "windows-msi", Short: "Create configuration files for msi packaging", @@ -49,4 +61,4 @@ var initWindowsMsiCmd = &cobra.Command{ packaging.InitWindowsMsi() }, -} \ No newline at end of file +} diff --git a/cmd/packaging/linux-appimage.go b/cmd/packaging/linux-appimage.go new file mode 100644 index 00000000..16821304 --- /dev/null +++ b/cmd/packaging/linux-appimage.go @@ -0,0 +1,93 @@ +package packaging + +import ( + "github.com/go-flutter-desktop/hover/internal/build" + "github.com/go-flutter-desktop/hover/internal/log" + "github.com/go-flutter-desktop/hover/internal/pubspec" + "github.com/otiai10/copy" + "os" + "path/filepath" +) + +func InitLinuxAppImage() { + projectName := pubspec.GetPubSpec().Name + packagingFormat := "linux-appimage" + createPackagingFormatDirectory(packagingFormat) + appImageDirectoryPath := packagingFormatPath(packagingFormat) + appRunFilePath, err := filepath.Abs(filepath.Join(appImageDirectoryPath, "AppRun")) + if err != nil { + log.Errorf("Failed to resolve absolute path for AppRun file %s: %v", appRunFilePath, err) + os.Exit(1) + } + + appRunFile, err := os.Create(appRunFilePath) + if err != nil { + log.Errorf("Failed to create AppRun file %s: %v", appRunFilePath, err) + os.Exit(1) + } + appRunFileContent := []string{ + `#!/bin/sh`, + `cd "$(dirname "$0")"`, + `exec ./build/` + projectName, + } + for _, line := range appRunFileContent { + if _, err := appRunFile.WriteString(line + "\n"); err != nil { + log.Errorf("Could not write AppRun file: %v", err) + os.Exit(1) + } + } + err = appRunFile.Close() + if err != nil { + log.Errorf("Could not close AppRun file: %v", err) + os.Exit(1) + } + err = os.Chmod(appRunFilePath, 0777) + if err != nil { + log.Errorf("Failed to change file permissions for AppRun file: %v", err) + os.Exit(1) + } + + desktopFilePath, err := filepath.Abs(filepath.Join(appImageDirectoryPath, projectName+".desktop")) + if err != nil { + log.Errorf("Failed to resolve absolute path for desktop file %s: %v", desktopFilePath, err) + os.Exit(1) + } + createLinuxDesktopFile(desktopFilePath, packagingFormat, "", "/build/assets/icon") + createDockerfile(packagingFormat) + + printInitFinished(packagingFormat) +} + +func BuildLinuxAppImage() { + projectName := pubspec.GetPubSpec().Name + packagingFormat := "linux-appimage" + tmpPath := getTemporaryBuildDirectory(projectName, packagingFormat) + log.Infof("Packaging AppImage in %s", tmpPath) + + err := copy.Copy(build.OutputDirectoryPath("linux"), filepath.Join(tmpPath, "build")) + if err != nil { + log.Errorf("Could not copy build folder: %v", err) + os.Exit(1) + } + err = copy.Copy(packagingFormatPath(packagingFormat), filepath.Join(tmpPath)) + if err != nil { + log.Errorf("Could not copy packaging configuration folder: %v", err) + os.Exit(1) + } + + outputFileName := projectName + "-x86_64.AppImage" + outputFilePath := filepath.Join(build.OutputDirectoryPath("linux-appimage"), outputFileName) + runDockerPackaging(tmpPath, packagingFormat, []string{"appimagetool", ".",}) + + err = os.Rename(filepath.Join(tmpPath, outputFileName), outputFilePath) + if err != nil { + log.Errorf("Could not move AppImage file: %v", err) + os.Exit(1) + } + err = os.RemoveAll(tmpPath) + if err != nil { + log.Errorf("Could not remove temporary build directory: %v", err) + os.Exit(1) + } + printPackagingFinished(packagingFormat) +} diff --git a/cmd/packaging/linux.go b/cmd/packaging/linux.go index 945d18aa..cbbcd75e 100644 --- a/cmd/packaging/linux.go +++ b/cmd/packaging/linux.go @@ -16,13 +16,15 @@ func createLinuxDesktopFile(desktopFilePath string, packagingFormat string, exec } desktopFileContent := []string{ "[Desktop Entry]", - "Encoding=UTF-8", - "Version=" + pubspec.GetPubSpec().Version, + "Version=1.0", "Type=Application", "Terminal=false", - "Exec=" + exec, "Name=" + pubspec.GetPubSpec().Name, "Icon=" + icon, + "Categories=", + } + if exec != "" { + desktopFileContent = append(desktopFileContent, "Exec="+exec) } for _, line := range desktopFileContent { diff --git a/cmd/packaging/packaging.go b/cmd/packaging/packaging.go index e8f72f95..ab9cb906 100644 --- a/cmd/packaging/packaging.go +++ b/cmd/packaging/packaging.go @@ -109,6 +109,19 @@ func createDockerfile(packagingFormat string) { dockerFileContent = []string{ "FROM ubuntu:bionic", } + } else if packagingFormat == "linux-appimage" { + dockerFileContent = []string{ + "FROM ubuntu:bionic", + "WORKDIR /opt", + "RUN apt-get update && \\", + "apt-get install libglib2.0-0 curl file -y", + "RUN curl -LO https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage && \\", + "chmod a+x appimagetool-x86_64.AppImage && \\", + "./appimagetool-x86_64.AppImage --appimage-extract && \\", + "mv squashfs-root appimagetool && \\", + "rm appimagetool-x86_64.AppImage", + "ENV PATH=/opt/appimagetool/usr/bin:/opt/linuxdeploy/usr/bin:$PATH", + } } else if packagingFormat == "windows-msi" { dockerFileContent = []string{ "FROM ubuntu:bionic", diff --git a/cmd/run.go b/cmd/run.go index 8e9fc172..b8b4e5d4 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -2,6 +2,7 @@ package cmd import ( "bufio" + "fmt" "io" "os" "os/exec" @@ -76,7 +77,7 @@ func runAndAttach(projectName string, targetOS string) { scanner := bufio.NewScanner(reader) for scanner.Scan() { text := scanner.Text() - log.Printf(text) + fmt.Println(text) match := re.FindStringSubmatch(text) if len(match) == 1 { startHotReloadProcess(cmdFlutterAttach, buildTarget, match[0]) diff --git a/cmd/upgrade.go b/cmd/upgrade.go index f6f544ef..21ba46e3 100644 --- a/cmd/upgrade.go +++ b/cmd/upgrade.go @@ -65,9 +65,14 @@ func upgradeGoFlutter(targetOS string, engineCachePath string) (err error) { return } + if buildBranch == "" { + buildBranch = "@latest" + } + cmdGoGetU := exec.Command(build.GoBin, "get", "-u", "github.com/go-flutter-desktop/go-flutter"+buildBranch) cmdGoGetU.Dir = filepath.Join(wd, build.BuildPath) cmdGoGetU.Env = append(os.Environ(), + "GOPROXY=direct", // github.com/golang/go/issues/32955 (allows '/' in branch name) "GO111MODULE=on", "CGO_LDFLAGS="+cgoLdflags, ) @@ -76,11 +81,7 @@ func upgradeGoFlutter(targetOS string, engineCachePath string) (err error) { err = cmdGoGetU.Run() if err != nil { - versionName := buildBranch - if versionName == "" { - versionName = "latest" - } - log.Errorf("Updating go-flutter to %s version failed: %v", versionName, err) + log.Errorf("Updating go-flutter to %s version failed: %v", buildBranch, err) return } diff --git a/internal/versioncheck/version.go b/internal/versioncheck/version.go index 16fa1df0..1255a212 100644 --- a/internal/versioncheck/version.go +++ b/internal/versioncheck/version.go @@ -14,11 +14,11 @@ import ( "github.com/tcnksm/go-latest" ) -// CheckFoGoFlutterUpdate check the last 'go-flutter' timestamp we have cached +// CheckForGoFlutterUpdate check the last 'go-flutter' timestamp we have cached // for the current project. If the last update comes back to more than X days, // fetch the last Github release semver. If the Github semver is more recent // than the current one, display the update notice. -func CheckFoGoFlutterUpdate(goDirectoryPath string, currentTag string) { +func CheckForGoFlutterUpdate(goDirectoryPath string, currentTag string) { cachedGoFlutterCheckPath := filepath.Join(goDirectoryPath, ".last_goflutter_check") cachedGoFlutterCheckBytes, err := ioutil.ReadFile(cachedGoFlutterCheckPath) if err != nil && !os.IsNotExist(err) {