diff --git a/.travis.yml b/.travis.yml
index 3841a78c06..c9b8b50259 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -63,7 +63,11 @@ matrix:
- go run build/ci.go install
- go run build/ci.go archive -type tar -signer OSX_SIGNING_KEY -upload gethstore/builds
- # Build the Android archives and upload them to Maven Central
+ # Build the iOS framework and upload it to CocoaPods and Azure
+ - gem install cocoapods --pre
+ - go run build/ci.go xcode -signer IOS_SIGNING_KEY -deploy trunk -upload gethstore/builds
+
+ # Build the Android archive and upload it to Maven Central and Azure
- brew update
- brew install android-sdk maven
- export ANDROID_HOME=/usr/local/opt/android-sdk
diff --git a/build/ci.go b/build/ci.go
index cc99e35219..b4dbd7dbdc 100644
--- a/build/ci.go
+++ b/build/ci.go
@@ -29,7 +29,8 @@ Available commands are:
importkeys -- imports signing keys from env
debsrc [ -signer key-id ] [ -upload dest ] -- creates a debian source package
nsis -- creates a Windows NSIS installer
- aar [ -sign key-id ] [ -upload dest ] -- creates an android archive
+ aar [ -sign key-id ] [-deploy repo] [ -upload dest ] -- creates an Android archive
+ xcode [ -sign key-id ] [-deploy repo] [ -upload dest ] -- creates an iOS XCode framework
xgo [ options ] -- cross builds according to options
For all commands, -n prevents execution of external programs (dry run mode).
@@ -130,6 +131,8 @@ func main() {
doWindowsInstaller(os.Args[2:])
case "aar":
doAndroidArchive(os.Args[2:])
+ case "xcode":
+ doXCodeFramework(os.Args[2:])
case "xgo":
doXgo(os.Args[2:])
default:
@@ -339,14 +342,21 @@ func archiveBasename(arch string, env build.Environment) string {
if arch == "android" {
platform = "android-all"
}
- archive := platform + "-" + build.VERSION()
+ if arch == "ios" {
+ platform = "ios-all"
+ }
+ return platform + "-" + archiveVersion(env)
+}
+
+func archiveVersion(env build.Environment) string {
+ version := build.VERSION()
if isUnstableBuild(env) {
- archive += "-unstable"
+ version += "-unstable"
}
if env.Commit != "" {
- archive += "-" + env.Commit[:8]
+ version += "-" + env.Commit[:8]
}
- return archive
+ return version
}
func archiveUpload(archive string, blobstore string, signer string) error {
@@ -638,14 +648,15 @@ func doWindowsInstaller(cmdline []string) {
if err := archiveUpload(installer, *upload, *signer); err != nil {
log.Fatal(err)
}
+}
// Android archives
func doAndroidArchive(cmdline []string) {
var (
signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. ANDROID_SIGNING_KEY)`)
- deploy = flag.String("deploy", "", `Where to upload the deploy the archive (usually "https://oss.sonatype.org")`)
- upload = flag.String("upload", "", `Destination to upload the archives (usually "gethstore/builds")`)
+ deploy = flag.String("deploy", "", `Destination to deploy the archive (usually "https://oss.sonatype.org")`)
+ upload = flag.String("upload", "", `Destination to upload the archive (usually "gethstore/builds")`)
)
flag.CommandLine.Parse(cmdline)
env := build.Env()
@@ -656,13 +667,13 @@ func doAndroidArchive(cmdline []string) {
build.MustRun(gomobileTool("bind", "--target", "android", "--javapkg", "org.ethereum", "-v", "github.com/ethereum/go-ethereum/mobile"))
meta := newMavenMetadata(env)
- build.Render("build/mvn.pom", meta.PackageString()+".pom", 0755, meta)
+ build.Render("build/mvn.pom", meta.Package+".pom", 0755, meta)
// Skip Maven deploy and Azure upload for PR builds
maybeSkipArchive(env)
// Sign and upload all the artifacts to Maven Central
- os.Rename("geth.aar", meta.PackageString()+".aar")
+ os.Rename("geth.aar", meta.Package+".aar")
if *signer != "" && *deploy != "" {
// Import the signing key into the local GPG instance
if b64key := os.Getenv(*signer); b64key != "" {
@@ -676,16 +687,16 @@ func doAndroidArchive(cmdline []string) {
}
// Upload the artifacts to Sonatype and/or Maven Central
repo := *deploy + "/service/local/staging/deploy/maven2"
- if meta.Unstable {
+ if meta.Develop {
repo = *deploy + "/content/repositories/snapshots"
}
build.MustRunCommand("mvn", "gpg:sign-and-deploy-file",
"-settings=build/mvn.settings", "-Durl="+repo, "-DrepositoryId=ossrh",
- "-DpomFile="+meta.PackageString()+".pom", "-Dfile="+meta.PackageString()+".aar")
+ "-DpomFile="+meta.Package+".pom", "-Dfile="+meta.Package+".aar")
}
// Sign and upload the archive to Azure
archive := "geth-" + archiveBasename("android", env) + ".aar"
- os.Rename(meta.PackageString()+".aar", archive)
+ os.Rename(meta.Package+".aar", archive)
if err := archiveUpload(archive, *upload, *signer); err != nil {
log.Fatal(err)
@@ -708,9 +719,9 @@ func gomobileTool(subcmd string, args ...string) *exec.Cmd {
}
type mavenMetadata struct {
- Env build.Environment
Version string
- Unstable bool
+ Package string
+ Develop bool
Contributors []mavenContributor
}
@@ -740,23 +751,101 @@ func newMavenMetadata(env build.Environment) mavenMetadata {
}
}
}
+ // Render the version and package strings
+ version := build.VERSION()
+ if isUnstableBuild(env) {
+ version += "-SNAPSHOT"
+ }
return mavenMetadata{
- Env: env,
- Version: build.VERSION(),
- Unstable: isUnstableBuild(env),
+ Version: version,
+ Package: "geth-" + version,
+ Develop: isUnstableBuild(env),
Contributors: contribs,
}
}
-func (meta mavenMetadata) VersionString() string {
- if meta.Unstable {
- return meta.Version + "-SNAPSHOT"
+// XCode frameworks
+
+func doXCodeFramework(cmdline []string) {
+ var (
+ signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. IOS_SIGNING_KEY)`)
+ deploy = flag.String("deploy", "", `Destination to deploy the archive (usually "trunk")`)
+ upload = flag.String("upload", "", `Destination to upload the archives (usually "gethstore/builds")`)
+ )
+ flag.CommandLine.Parse(cmdline)
+ env := build.Env()
+
+ // Build the iOS XCode framework
+ build.MustRun(goTool("get", "golang.org/x/mobile/cmd/gomobile"))
+ build.MustRun(gomobileTool("init"))
+
+ archive := "geth-" + archiveBasename("ios", env)
+ if err := os.Mkdir(archive, os.ModePerm); err != nil {
+ log.Fatal(err)
}
- return meta.Version
+ bind := gomobileTool("bind", "--target", "ios", "--tags", "ios", "--prefix", "GE", "-v", "github.com/ethereum/go-ethereum/mobile")
+ bind.Dir, _ = filepath.Abs(archive)
+ build.MustRun(bind)
+ build.MustRunCommand("tar", "-zcvf", archive+".tar.gz", archive)
+
+ // Skip CocoaPods deploy and Azure upload for PR builds
+ maybeSkipArchive(env)
+
+ // Sign and upload the framework to Azure
+ if err := archiveUpload(archive+".tar.gz", *upload, *signer); err != nil {
+ log.Fatal(err)
+ }
+ // Prepare and upload a PodSpec to CocoaPods
+ if *deploy != "" {
+ meta := newPodMetadata(env)
+ build.Render("build/pod.podspec", meta.Name+".podspec", 0755, meta)
+ build.MustRunCommand("pod", *deploy, "push", meta.Name+".podspec")
+ }
+}
+
+type podMetadata struct {
+ Name string
+ Version string
+ Commit string
+ Contributors []podContributor
}
-func (meta mavenMetadata) PackageString() string {
- return "geth-" + meta.VersionString()
+type podContributor struct {
+ Name string
+ Email string
+}
+
+func newPodMetadata(env build.Environment) podMetadata {
+ // Collect the list of authors from the repo root
+ contribs := []podContributor{}
+ if authors, err := os.Open("AUTHORS"); err == nil {
+ defer authors.Close()
+
+ scanner := bufio.NewScanner(authors)
+ for scanner.Scan() {
+ // Skip any whitespace from the authors list
+ line := strings.TrimSpace(scanner.Text())
+ if line == "" || line[0] == '#' {
+ continue
+ }
+ // Split the author and insert as a contributor
+ re := regexp.MustCompile("([^<]+) <(.+>)")
+ parts := re.FindStringSubmatch(line)
+ if len(parts) == 3 {
+ contribs = append(contribs, podContributor{Name: parts[1], Email: parts[2]})
+ }
+ }
+ }
+ name := "Geth"
+ if isUnstableBuild(env) {
+ name += "Develop"
+ }
+ return podMetadata{
+ Name: name,
+ Version: archiveVersion(env),
+ Commit: env.Commit,
+ Contributors: contribs,
+ }
}
// Cross compilation
diff --git a/build/mvn.pom b/build/mvn.pom
index 0092268765..7670246ba9 100644
--- a/build/mvn.pom
+++ b/build/mvn.pom
@@ -6,7 +6,7 @@
org.ethereum
geth
- {{.VersionString}}
+ {{.Version}}
aar
Android Ethereum Client
diff --git a/build/pod.podspec b/build/pod.podspec
new file mode 100644
index 0000000000..2718522db3
--- /dev/null
+++ b/build/pod.podspec
@@ -0,0 +1,22 @@
+Pod::Spec.new do |spec|
+ spec.name = '{{.Name}}'
+ spec.version = '{{.Version}}'
+ spec.license = { :type => 'GNU Lesser General Public License, Version 3.0' }
+ spec.homepage = 'https://github.com/ethereum/go-ethereum'
+ spec.authors = { {{range .Contributors}}
+ '{{.Name}}' => '{{.Email}}',{{end}}
+ }
+ spec.summary = 'iOS Ethereum Client'
+ spec.source = { :git => 'https://github.com/ethereum/go-ethereum.git', :commit => '{{.Commit}}' }
+
+ spec.platform = :ios
+ spec.ios.deployment_target = '9.0'
+ spec.ios.vendored_frameworks = 'Frameworks/Geth.framework'
+
+ spec.prepare_command = <<-CMD
+ curl https://gethstore.blob.core.windows.net/builds/geth-ios-all-{{.Version}}.tar.gz | tar -xvz
+ mkdir Frameworks
+ mv geth-ios-all-{{.Version}}/Geth.framework Frameworks
+ rm geth-ios-all-{{.Version}}
+ CMD
+end