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) + } + 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") } - return meta.Version } -func (meta mavenMetadata) PackageString() string { - return "geth-" + meta.VersionString() +type podMetadata struct { + Name string + Version string + Commit string + Contributors []podContributor +} + +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