提交 bb47187e 编写于 作者: R Richard Wilkes

Add dist command

上级 82f550a0
#include <stdlib.h>
#include "include/capi/cef_app_capi.h"
int main(int argc, char **argv) {
cef_main_args_t *args = (cef_main_args_t *)calloc(1, sizeof(cef_main_args_t));
args->argc = argc;
args->argv = argv;
return cef_execute_process(args, NULL, NULL);
}
package cmd
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path"
"runtime"
"strconv"
"time"
"github.com/richardwilkes/toolbox/atexit"
"github.com/richardwilkes/toolbox/cmdline"
"github.com/richardwilkes/toolbox/xio/fs"
)
type dist struct {
root string
displayName string
bundleName string
bundleID string
exeName string
icon string
version string
shortVersion string
copyrightYears string
copyrightOwner string
}
// NewDist returns the dist command.
func NewDist() cmdline.Cmd {
d := &dist{
root: "dist",
displayName: "Example",
bundleName: "Example",
bundleID: "com.example",
exeName: "example",
version: "1.0.0",
shortVersion: "1.0",
copyrightYears: strconv.Itoa(time.Now().Year()),
copyrightOwner: "Unknown",
}
switch runtime.GOOS {
case "darwin":
d.root = path.Join(d.root, "macos")
d.icon = "AppIcon.icns"
default:
d.root = path.Join(d.root, runtime.GOOS)
}
return d
}
func (d *dist) Name() string {
return "dist"
}
func (d *dist) Usage() string {
return "Creates a distribution tree, adding the necessary CEF libraries."
}
func (d *dist) Run(cl *cmdline.CmdLine, args []string) error {
cl.NewStringOption(&d.root).SetSingle('d').SetName("dir").SetUsage("Set the root distribution directory")
cl.NewStringOption(&d.displayName).SetSingle('n').SetName("name").SetUsage("Set the display name (macOS-only)")
cl.NewStringOption(&d.bundleName).SetSingle('b').SetName("bundle").SetUsage("Set the bundle name (macOS-only)")
cl.NewStringOption(&d.bundleID).SetSingle('B').SetName("id").SetUsage("Set the bundle ID (macOS-only)")
cl.NewStringOption(&d.exeName).SetSingle('e').SetName("executable").SetUsage("Set the executable name (macOS-only)")
cl.NewStringOption(&d.icon).SetSingle('i').SetName("icon").SetUsage("Set the icon (macOS-only)")
cl.NewStringOption(&d.version).SetSingle('r').SetName("release").SetUsage("Set the release version (macOS-only)")
cl.NewStringOption(&d.shortVersion).SetSingle('s').SetName("short-release").SetUsage("Set the short release version (macOS-only)")
cl.NewStringOption(&d.copyrightYears).SetSingle('y').SetName("year").SetUsage("Set the copyright year(s) (macOS-only)")
cl.NewStringOption(&d.copyrightOwner).SetSingle('o').SetName("owner").SetUsage("Set the copyright owner (macOS-only)")
cl.Parse(args)
checkPlatform()
if err := os.RemoveAll(d.root); err != nil {
fmt.Printf("Unable to remove old dist at %s\n", d.root)
fmt.Println(err)
atexit.Exit(1)
}
createDir(d.root, 0755)
switch runtime.GOOS {
case "darwin":
d.distMacOS()
case "windows":
d.distWindows()
default:
return fmt.Errorf("Unhandled OS: %s", runtime.GOOS)
}
return nil
}
func (d *dist) distMacOS() {
appBundleContentsDir := path.Join(d.root, d.displayName+".app", "Contents")
createDir(path.Join(appBundleContentsDir, "MacOS"), 0755)
appBundleResourcesDir := path.Join(appBundleContentsDir, "Resources")
createDir(appBundleResourcesDir, 0755)
appFrameworksDir := path.Join(appBundleContentsDir, "Frameworks")
helperAppBundleContentsDir := path.Join(appFrameworksDir, d.exeName+" Helper.app", "Contents")
helperAppBundleMacOSDir := path.Join(helperAppBundleContentsDir, "MacOS")
createDir(helperAppBundleMacOSDir, 0755)
createDir(path.Join(helperAppBundleContentsDir, "Frameworks"), 0755)
releaseDir := path.Join(installPrefix, "Release")
cc := exec.Command("cc", "-I", installPrefix, path.Join(installPrefix, "helper", "helper.c"), "-F", releaseDir, "-framework", "Chromium Embedded Framework", "-o", path.Join(helperAppBundleMacOSDir, d.exeName+" Helper"))
if result, err := cc.CombinedOutput(); err != nil {
fmt.Println("Failed to compile the helper.")
fmt.Println(err)
fmt.Println(string(result))
atexit.Exit(1)
}
if err := fs.Copy(path.Join(releaseDir, "Chromium Embedded Framework.framework"), path.Join(appFrameworksDir, "Chromium Embedded Framework.framework")); err != nil {
fmt.Println(err)
atexit.Exit(1)
}
if err := os.Symlink("../../../Chromium Embedded Framework.framework", path.Join(helperAppBundleContentsDir, "Frameworks", "Chromium Embedded Framework.framework")); err != nil {
fmt.Println(err)
atexit.Exit(1)
}
if err := fs.Copy(d.icon, path.Join(appBundleResourcesDir, "AppIcon.icns")); err != nil {
fmt.Println(err)
atexit.Exit(1)
}
plist := path.Join(appBundleContentsDir, "Info.plist")
f, err := os.Create(plist)
checkFileError(err, "create", plist)
_, err = fmt.Fprintf(f, `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleDisplayName</key>
<string>%s</string>
<key>CFBundleName</key>
<string>%s</string>
<key>CFBundleExecutable</key>
<string>%s</string>
<key>CFBundleIconFile</key>
<string>AppIcon.icns</string>
<key>CFBundleIdentifier</key>
<string>%s</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleVersion</key>
<string>%s</string>
<key>CFBundleShortVersionString</key>
<string>%s</string>
<key>NSHumanReadableCopyright</key>
<string>© %s by %s. All rights reserved.</string>
<key>NSHighResolutionCapable</key>
<true/>
<key>NSSupportsAutomaticGraphicsSwitching</key>
<true/>
</dict>
</plist>
`, d.displayName, d.bundleName, d.exeName, d.bundleID, d.version, d.shortVersion, d.copyrightYears, d.copyrightOwner)
checkFileError(err, "write", plist)
checkFileError(f.Close(), "write", plist)
plist = path.Join(helperAppBundleContentsDir, "Info.plist")
f, err = os.Create(plist)
checkFileError(err, "create", plist)
_, err = fmt.Fprintf(f, `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleDisplayName</key>
<string>%s Helper</string>
<key>CFBundleName</key>
<string>%s Helper</string>
<key>CFBundleExecutable</key>
<string>%s Helper</string>
<key>CFBundleIdentifier</key>
<string>%s.helper</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleVersion</key>
<string>%s</string>
<key>CFBundleShortVersionString</key>
<string>%s</string>
<key>NSHumanReadableCopyright</key>
<string>© %s by %s. All rights reserved.</string>
<key>NSHighResolutionCapable</key>
<true/>
<key>NSSupportsAutomaticGraphicsSwitching</key>
<true/>
</dict>
</plist>
`, d.displayName, d.bundleName, d.exeName, d.bundleID, d.version, d.shortVersion, d.copyrightYears, d.copyrightOwner)
checkFileError(err, "write", plist)
checkFileError(f.Close(), "write", plist)
}
func (d *dist) distWindows() {
copyDirContents(path.Join(installPrefix, "Release"), d.root)
copyDirContents(path.Join(installPrefix, "Resources"), d.root)
}
func copyDirContents(srcdir, dstdir string) {
list, err := ioutil.ReadDir(srcdir)
if err != nil {
fmt.Println(err)
atexit.Exit(1)
}
for _, one := range list {
name := one.Name()
if err := fs.Copy(path.Join(srcdir, name), path.Join(dstdir, name)); err != nil {
fmt.Println(err)
atexit.Exit(1)
}
}
}
package cmd
import (
"fmt"
"os"
"github.com/richardwilkes/toolbox/atexit"
)
func createDir(dir string, mode os.FileMode) {
if err := os.MkdirAll(dir, mode); err != nil {
fmt.Println(err)
fmt.Println("You may need to run this as root.")
atexit.Exit(1)
}
}
func checkFileError(err error, op, name string) {
if err != nil {
fmt.Printf("Unable to %s file %s\n", op, name)
fmt.Println(err)
atexit.Exit(1)
}
}
package cmd
import (
"archive/tar"
"bufio"
"bytes"
"compress/bzip2"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"path"
"regexp"
"runtime"
"strings"
"time"
"github.com/richardwilkes/toolbox/atexit"
"github.com/richardwilkes/toolbox/cmdline"
"github.com/richardwilkes/toolbox/xio"
)
var cefVersionRegex = regexp.MustCompile(`^\s*#define\s+CEF_VERSION\s+"(\d+\.\d+\.\d+\.\w+)"\s*$`)
// Install holds the install command.
type Install struct {
Version string
}
// Name returns the name of the command as it needs to be entered on the command line.
func (c *Install) Name() string {
return "install"
}
// Usage returns a description of what the command does.
func (c *Install) Usage() string {
return "Downloads and installs the headers and libraries necessary use the github.com/richardwilkes/cef/cef package."
}
// Run the command.
func (c *Install) Run(cl *cmdline.CmdLine, args []string) error {
var needInstall bool
cl.NewBoolOption(&needInstall).SetSingle('f').SetName("force").SetUsage("Force an install")
cl.Parse(args)
checkPlatform()
if !needInstall {
var existingCEFVersion string
if f, err := os.Open(path.Join(installPrefix, "include/cef_version.h")); err == nil {
s := bufio.NewScanner(f)
for s.Scan() {
line := s.Text()
if result := cefVersionRegex.FindStringSubmatch(line); len(result) == 2 {
existingCEFVersion = result[1]
break
}
}
xio.CloseIgnoringErrors(f)
}
needInstall = existingCEFVersion != c.Version
}
if needInstall {
fmt.Printf("Installing into %s...\n", installPrefix)
if err := os.RemoveAll(installPrefix); err != nil {
fmt.Printf("Unable to remove old installation at %s\n", installPrefix)
fmt.Println(err)
fmt.Println("You may need to run this as root.")
atexit.Exit(1)
}
createDir(path.Join(installPrefix, "helper"), 0755)
name := path.Join(installPrefix, "helper", "helper.c")
checkFileError(ioutil.WriteFile(name, []byte(`#include <stdlib.h>
#include "include/capi/cef_app_capi.h"
int main(int argc, char **argv) {
cef_main_args_t *args = (cef_main_args_t *)calloc(1, sizeof(cef_main_args_t));
args->argc = argc;
args->argv = argv;
return cef_execute_process(args, NULL, NULL);
}
`), 0644), "write", name)
c.untar(bytes.NewBuffer(c.downloadAndUncompressArchive()))
if runtime.GOOS == "windows" {
dir := path.Join(path.Dir(os.Getenv("MINGW_PREFIX")), "lib/pkgconfig")
createDir(dir, 0755)
name = path.Join(dir, "cef.pc")
f, err := os.Create(name)
checkFileError(err, "create", name)
_, err = fmt.Fprintf(f, `Name: cef
Description: Chromium Embedded Framework
Version: %[1]s
Requires:
Libs: -L%[2]s/Release -lcef
Cflags: -I%[2]s
`, c.Version, installPrefix)
checkFileError(err, "write", name)
checkFileError(f.Close(), "write", name)
}
}
return nil
}
func (c *Install) archiveName() string {
return fmt.Sprintf("cef_binary_%s_%s_minimal", c.Version, cefPlatform)
}
func (c *Install) downloadAndUncompressArchive() []byte {
client := http.Client{Timeout: 10 * time.Minute}
url := fmt.Sprintf("http://opensource.spotify.com/cefbuilds/%s.tar.bz2", c.archiveName())
fmt.Println(" Downloading...")
resp, err := client.Get(url)
if err != nil {
fmt.Printf("Unable to download CEF archive at %s\n", url)
fmt.Println(err)
atexit.Exit(1)
}
buffer, err := ioutil.ReadAll(resp.Body)
xio.CloseIgnoringErrors(resp.Body)
if err != nil {
fmt.Printf("Unable to download CEF archive at %s\n", url)
fmt.Println(err)
atexit.Exit(1)
}
if resp.StatusCode > 299 {
fmt.Printf("Unable to download CEF archive at %s\n", url)
fmt.Printf("Status: %s\n", resp.Status)
atexit.Exit(1)
}
fmt.Println(" Uncompressing...")
buffer, err = ioutil.ReadAll(bzip2.NewReader(bytes.NewReader(buffer)))
if err != nil {
fmt.Printf("Unable to uncompress CEF archive from %s\n", url)
fmt.Println(err)
atexit.Exit(1)
}
return buffer
}
func (c *Install) untar(in io.Reader) {
prefix := c.archiveName()
fmt.Println(" Unarchiving...")
r := tar.NewReader(in)
for {
h, err := r.Next()
if err != nil {
if err == io.EOF {
break
}
fmt.Println("Unable to read tar entry from archive")
fmt.Println(err)
atexit.Exit(1)
}
name := strings.Trim(strings.TrimPrefix(h.Name, prefix), "/")
if name != "" && !strings.Contains(name, "..") {
name = path.Join(installPrefix, name)
switch h.Typeflag {
case tar.TypeDir:
createDir(name, os.FileMode(h.Mode|0555))
case tar.TypeReg:
buffer, err := ioutil.ReadAll(r)
checkFileError(err, "read archive data for", name)
checkFileError(ioutil.WriteFile(name, buffer, os.FileMode(h.Mode|0444)), "write", name)
default:
fmt.Printf("Unexpected type flag: %d\n", h.Typeflag)
atexit.Exit(1)
}
}
}
}
package cmd
import (
"fmt"
"os"
"path"
"runtime"
"github.com/richardwilkes/toolbox/atexit"
)
var (
installPrefix = "/usr/local/cef"
cefPlatform string
)
func checkPlatform() {
switch runtime.GOOS {
case "darwin":
cefPlatform = "macosx64"
case "windows":
if os.Getenv("MSYSTEM") != "MINGW64" {
fmt.Println("Windows is only supported through the use of MINGW64")
atexit.Exit(1)
}
cefPlatform = "windows64"
installPrefix = path.Join(path.Dir(os.Getenv("MINGW_PREFIX")), installPrefix)
default:
fmt.Println("Unsupported OS: ", runtime.GOOS)
atexit.Exit(1)
}
}
package main
import (
"archive/tar"
"bufio"
"bytes"
"compress/bzip2"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"path"
"regexp"
"runtime"
"strings"
"time"
"github.com/richardwilkes/cef/internal/cmd"
"github.com/richardwilkes/toolbox/atexit"
"github.com/richardwilkes/toolbox/cmdline"
"github.com/richardwilkes/toolbox/xio"
)
const desiredCEFVersion = "3.3538.1852.gcb937fc"
var (
cefVersionRegex = regexp.MustCompile(`^\s*#define\s+CEF_VERSION\s+"(\d+\.\d+\.\d+\.\w+)"\s*$`)
installPrefix = "/usr/local/cef"
cefPlatform string
)
func main() {
cmdline.CopyrightYears = "2018"
cmdline.CopyrightHolder = "Richard A. Wilkes"
cmdline.AppIdentifier = "com.trollworks.cef"
cl := cmdline.New(true)
cl.Description = "Downloads and installs the headers and libraries necessary use the github.com/richardwilkes/cef/cef package."
var needInstall bool
cl.NewBoolOption(&needInstall).SetSingle('f').SetName("force").SetUsage("Force an install")
cl.Parse(os.Args[1:])
switch runtime.GOOS {
case "darwin":
cefPlatform = "macosx64"
case "windows":
if os.Getenv("MSYSTEM") != "MINGW64" {
fmt.Println("Windows is only supported through the use of MINGW64")
atexit.Exit(1)
}
cefPlatform = "windows64"
installPrefix = path.Join(path.Dir(os.Getenv("MINGW_PREFIX")), installPrefix)
default:
fmt.Println("Unsupported OS: ", runtime.GOOS)
atexit.Exit(1)
}
if !needInstall {
var existingCEFVersion string
if f, err := os.Open(path.Join(installPrefix, "include/cef_version.h")); err == nil {
s := bufio.NewScanner(f)
for s.Scan() {
line := s.Text()
if result := cefVersionRegex.FindStringSubmatch(line); len(result) == 2 {
existingCEFVersion = result[1]
break
}
}
xio.CloseIgnoringErrors(f)
}
needInstall = existingCEFVersion != desiredCEFVersion
}
if needInstall {
fmt.Printf("Installing into %s...\n", installPrefix)
if err := os.RemoveAll(installPrefix); err != nil {
fmt.Printf("Unable to remove old installation at %s\n", installPrefix)
fmt.Println(err)
fmt.Println("You may need to run this as root.")
atexit.Exit(1)
}
createDir(installPrefix, 0755)
untar(bytes.NewBuffer(downloadAndUncompressArchive()))
if runtime.GOOS == "windows" {
dir := path.Join(path.Dir(os.Getenv("MINGW_PREFIX")), "lib/pkgconfig")
createDir(dir, 0755)
name := path.Join(dir, "cef.pc")
f, err := os.Create(name)
checkFileError(err, "create", name)
_, err = fmt.Fprintf(f, `Name: cef
Description: Chromium Embedded Framework
Version: %[1]s
Requires:
Libs: -L%[2]s/Release -lcef
Cflags: -I%[2]s
`, desiredCEFVersion, installPrefix)
checkFileError(err, "write", name)
checkFileError(f.Close(), "write", name)
}
}
atexit.Exit(0)
}
func createDir(dir string, mode os.FileMode) {
if err := os.MkdirAll(dir, mode); err != nil {
fmt.Println(err)
fmt.Println("You may need to run this as root.")
atexit.Exit(1)
}
}
func checkFileError(err error, op, name string) {
if err != nil {
fmt.Printf("Unable to %s file %s\n", op, name)
fmt.Println(err)
atexit.Exit(1)
}
}
func archiveName() string {
return fmt.Sprintf("cef_binary_%s_%s_minimal", desiredCEFVersion, cefPlatform)
}
func downloadAndUncompressArchive() []byte {
client := http.Client{Timeout: 10 * time.Minute}
url := fmt.Sprintf("http://opensource.spotify.com/cefbuilds/%s.tar.bz2", archiveName())
fmt.Println(" Downloading...")
resp, err := client.Get(url)
if err != nil {
fmt.Printf("Unable to download CEF archive at %s\n", url)
fmt.Println(err)
atexit.Exit(1)
}
buffer, err := ioutil.ReadAll(resp.Body)
xio.CloseIgnoringErrors(resp.Body)
if err != nil {
fmt.Printf("Unable to download CEF archive at %s\n", url)
fmt.Println(err)
atexit.Exit(1)
}
if resp.StatusCode > 299 {
fmt.Printf("Unable to download CEF archive at %s\n", url)
fmt.Printf("Status: %s\n", resp.Status)
atexit.Exit(1)
}
fmt.Println(" Uncompressing...")
buffer, err = ioutil.ReadAll(bzip2.NewReader(bytes.NewReader(buffer)))
if err != nil {
fmt.Printf("Unable to uncompress CEF archive from %s\n", url)
fmt.Println(err)
cl.Description = "Utilities for managing setup of the Chromium Embedded Framework."
cl.AddCommand(&cmd.Install{Version: desiredCEFVersion})
cl.AddCommand(cmd.NewDist())
if err := cl.RunCommand(cl.Parse(os.Args[1:])); err != nil {
fmt.Fprintln(os.Stderr, err)
atexit.Exit(1)
}
return buffer
}
func untar(in io.Reader) {
prefix := archiveName()
fmt.Println(" Unarchiving...")
r := tar.NewReader(in)
for {
h, err := r.Next()
if err != nil {
if err == io.EOF {
break
}
fmt.Println("Unable to read tar entry from archive")
fmt.Println(err)
atexit.Exit(1)
}
name := strings.Trim(strings.TrimPrefix(h.Name, prefix), "/")
if name != "" && !strings.Contains(name, "..") {
name = path.Join(installPrefix, name)
switch h.Typeflag {
case tar.TypeDir:
createDir(name, os.FileMode(h.Mode|0555))
case tar.TypeReg:
buffer, err := ioutil.ReadAll(r)
checkFileError(err, "read archive data for", name)
checkFileError(ioutil.WriteFile(name, buffer, os.FileMode(h.Mode|0444)), "write", name)
default:
fmt.Printf("Unexpected type flag: %d\n", h.Typeflag)
atexit.Exit(1)
}
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册