| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | // Copyright 2015 The Go Authors. All rights reserved. | 
					
						
							|  |  |  | // Use of this source code is governed by a BSD-style | 
					
						
							|  |  |  | // license that can be found in the LICENSE file. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // This program can be used as go_darwin_arm_exec by the Go tool. | 
					
						
							|  |  |  | // It executes binaries on an iOS device using the XCode toolchain | 
					
						
							|  |  |  | // and the ios-deploy program: https://github.com/phonegap/ios-deploy | 
					
						
							| 
									
										
										
										
											2015-03-06 09:45:24 -05:00
										 |  |  | // | 
					
						
							|  |  |  | // This script supports an extra flag, -lldb, that pauses execution | 
					
						
							|  |  |  | // just before the main program begins and allows the user to control | 
					
						
							|  |  |  | // the remote lldb session. This flag is appended to the end of the | 
					
						
							|  |  |  | // script's arguments and is not passed through to the underlying | 
					
						
							|  |  |  | // binary. | 
					
						
							| 
									
										
										
										
											2015-04-13 11:31:41 -07:00
										 |  |  | // | 
					
						
							|  |  |  | // This script requires that three environment variables be set: | 
					
						
							|  |  |  | // 	GOIOS_DEV_ID: The codesigning developer id or certificate identifier | 
					
						
							|  |  |  | // 	GOIOS_APP_ID: The provisioning app id prefix. Must support wildcard app ids. | 
					
						
							|  |  |  | // 	GOIOS_TEAM_ID: The team id that owns the app id prefix. | 
					
						
							|  |  |  | // $GOROOT/misc/ios contains a script, detect.go, that attempts to autodetect these. | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | package main | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							|  |  |  | 	"errors" | 
					
						
							|  |  |  | 	"flag" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"go/build" | 
					
						
							| 
									
										
										
										
											2015-03-06 09:45:24 -05:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 	"io/ioutil" | 
					
						
							|  |  |  | 	"log" | 
					
						
							|  |  |  | 	"os" | 
					
						
							|  |  |  | 	"os/exec" | 
					
						
							|  |  |  | 	"path/filepath" | 
					
						
							|  |  |  | 	"runtime" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2016-03-24 16:03:07 +01:00
										 |  |  | 	"syscall" | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 	"time" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const debug = false | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-30 08:36:37 -04:00
										 |  |  | var errRetry = errors.New("failed to start test harness (retry attempted)") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var tmpdir string | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-13 11:31:41 -07:00
										 |  |  | var ( | 
					
						
							| 
									
										
										
										
											2017-02-01 09:37:47 +01:00
										 |  |  | 	devID    string | 
					
						
							|  |  |  | 	appID    string | 
					
						
							|  |  |  | 	teamID   string | 
					
						
							|  |  |  | 	bundleID string | 
					
						
							| 
									
										
										
										
											2015-04-13 11:31:41 -07:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-25 15:40:44 +01:00
										 |  |  | // lock is a file lock to serialize iOS runs. It is global to avoid the | 
					
						
							|  |  |  | // garbage collector finalizing it, closing the file and releasing the | 
					
						
							|  |  |  | // lock prematurely. | 
					
						
							|  |  |  | var lock *os.File | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | func main() { | 
					
						
							|  |  |  | 	log.SetFlags(0) | 
					
						
							|  |  |  | 	log.SetPrefix("go_darwin_arm_exec: ") | 
					
						
							|  |  |  | 	if debug { | 
					
						
							|  |  |  | 		log.Println(strings.Join(os.Args, " ")) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if len(os.Args) < 2 { | 
					
						
							|  |  |  | 		log.Fatal("usage: go_darwin_arm_exec a.out") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-04 12:41:41 -04:00
										 |  |  | 	// e.g. B393DDEB490947F5A463FD074299B6C0AXXXXXXX | 
					
						
							| 
									
										
										
										
											2015-04-13 11:31:41 -07:00
										 |  |  | 	devID = getenv("GOIOS_DEV_ID") | 
					
						
							| 
									
										
										
										
											2015-05-04 12:41:41 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// e.g. Z8B3JBXXXX.org.golang.sample, Z8B3JBXXXX prefix is available at | 
					
						
							|  |  |  | 	// https://developer.apple.com/membercenter/index.action#accountSummary as Team ID. | 
					
						
							| 
									
										
										
										
											2015-04-13 11:31:41 -07:00
										 |  |  | 	appID = getenv("GOIOS_APP_ID") | 
					
						
							| 
									
										
										
										
											2015-05-04 12:41:41 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// e.g. Z8B3JBXXXX, available at | 
					
						
							|  |  |  | 	// https://developer.apple.com/membercenter/index.action#accountSummary as Team ID. | 
					
						
							| 
									
										
										
										
											2015-04-13 11:31:41 -07:00
										 |  |  | 	teamID = getenv("GOIOS_TEAM_ID") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-01 09:37:47 +01:00
										 |  |  | 	parts := strings.SplitN(appID, ".", 2) | 
					
						
							|  |  |  | 	// For compatibility with the old builders, use a fallback bundle ID | 
					
						
							|  |  |  | 	bundleID = "golang.gotest" | 
					
						
							|  |  |  | 	if len(parts) == 2 { | 
					
						
							|  |  |  | 		bundleID = parts[1] | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-30 08:36:37 -04:00
										 |  |  | 	var err error | 
					
						
							|  |  |  | 	tmpdir, err = ioutil.TempDir("", "go_darwin_arm_exec_") | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		log.Fatal(err) | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-24 16:03:07 +01:00
										 |  |  | 	// This wrapper uses complicated machinery to run iOS binaries. It | 
					
						
							|  |  |  | 	// works, but only when running one binary at a time. | 
					
						
							|  |  |  | 	// Use a file lock to make sure only one wrapper is running at a time. | 
					
						
							|  |  |  | 	// | 
					
						
							|  |  |  | 	// The lock file is never deleted, to avoid concurrent locks on distinct | 
					
						
							|  |  |  | 	// files with the same path. | 
					
						
							|  |  |  | 	lockName := filepath.Join(os.TempDir(), "go_darwin_arm_exec.lock") | 
					
						
							| 
									
										
										
										
											2016-03-25 15:40:44 +01:00
										 |  |  | 	lock, err = os.OpenFile(lockName, os.O_CREATE|os.O_RDONLY, 0666) | 
					
						
							| 
									
										
										
										
											2016-03-24 16:03:07 +01:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		log.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err := syscall.Flock(int(lock.Fd()), syscall.LOCK_EX); err != nil { | 
					
						
							|  |  |  | 		log.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-03-30 08:36:37 -04:00
										 |  |  | 	// Approximately 1 in a 100 binaries fail to start. If it happens, | 
					
						
							|  |  |  | 	// try again. These failures happen for several reasons beyond | 
					
						
							|  |  |  | 	// our control, but all of them are safe to retry as they happen | 
					
						
							| 
									
										
										
										
											2017-01-07 17:41:00 -08:00
										 |  |  | 	// before lldb encounters the initial SIGUSR2 stop. As we | 
					
						
							| 
									
										
										
										
											2015-03-30 08:36:37 -04:00
										 |  |  | 	// know the tests haven't started, we are not hiding flaky tests | 
					
						
							|  |  |  | 	// with this retry. | 
					
						
							|  |  |  | 	for i := 0; i < 5; i++ { | 
					
						
							|  |  |  | 		if i > 0 { | 
					
						
							|  |  |  | 			fmt.Fprintln(os.Stderr, "start timeout, trying again") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		err = run(os.Args[1], os.Args[2:]) | 
					
						
							|  |  |  | 		if err == nil || err != errRetry { | 
					
						
							|  |  |  | 			break | 
					
						
							| 
									
										
										
										
											2015-03-03 17:54:42 -05:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-03-03 17:54:42 -05:00
										 |  |  | 	if !debug { | 
					
						
							| 
									
										
										
										
											2015-03-30 08:36:37 -04:00
										 |  |  | 		os.RemoveAll(tmpdir) | 
					
						
							| 
									
										
										
										
											2015-03-03 17:54:42 -05:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-03-30 08:36:37 -04:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		fmt.Fprintf(os.Stderr, "go_darwin_arm_exec: %v\n", err) | 
					
						
							|  |  |  | 		os.Exit(1) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-13 11:31:41 -07:00
										 |  |  | func getenv(envvar string) string { | 
					
						
							|  |  |  | 	s := os.Getenv(envvar) | 
					
						
							|  |  |  | 	if s == "" { | 
					
						
							| 
									
										
										
										
											2015-11-02 17:37:31 -05:00
										 |  |  | 		log.Fatalf("%s not set\nrun $GOROOT/misc/ios/detect.go to attempt to autodetect", envvar) | 
					
						
							| 
									
										
										
										
											2015-04-13 11:31:41 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return s | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-30 08:36:37 -04:00
										 |  |  | func run(bin string, args []string) (err error) { | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 	appdir := filepath.Join(tmpdir, "gotest.app") | 
					
						
							| 
									
										
										
										
											2015-03-30 08:36:37 -04:00
										 |  |  | 	os.RemoveAll(appdir) | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 	if err := os.MkdirAll(appdir, 0755); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err := cp(filepath.Join(appdir, "gotest"), bin); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	entitlementsPath := filepath.Join(tmpdir, "Entitlements.plist") | 
					
						
							| 
									
										
										
										
											2015-04-13 11:31:41 -07:00
										 |  |  | 	if err := ioutil.WriteFile(entitlementsPath, []byte(entitlementsPlist()), 0744); err != nil { | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-02-01 09:37:47 +01:00
										 |  |  | 	if err := ioutil.WriteFile(filepath.Join(appdir, "Info.plist"), []byte(infoPlist()), 0744); err != nil { | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err := ioutil.WriteFile(filepath.Join(appdir, "ResourceRules.plist"), []byte(resourceRules), 0744); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pkgpath, err := copyLocalData(appdir) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cmd := exec.Command( | 
					
						
							|  |  |  | 		"codesign", | 
					
						
							|  |  |  | 		"-f", | 
					
						
							| 
									
										
										
										
											2015-04-13 11:31:41 -07:00
										 |  |  | 		"-s", devID, | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 		"--entitlements", entitlementsPath, | 
					
						
							|  |  |  | 		appdir, | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	if debug { | 
					
						
							|  |  |  | 		log.Println(strings.Join(cmd.Args, " ")) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	cmd.Stdout = os.Stdout | 
					
						
							|  |  |  | 	cmd.Stderr = os.Stderr | 
					
						
							|  |  |  | 	if err := cmd.Run(); err != nil { | 
					
						
							|  |  |  | 		return fmt.Errorf("codesign: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-30 08:36:37 -04:00
										 |  |  | 	oldwd, err := os.Getwd() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err := os.Chdir(filepath.Join(appdir, "..")); err != nil { | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-03-30 08:36:37 -04:00
										 |  |  | 	defer os.Chdir(oldwd) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-23 16:17:44 +01:00
										 |  |  | 	// Setting up lldb is flaky. The test binary itself runs when | 
					
						
							|  |  |  | 	// started is set to true. Everything before that is considered | 
					
						
							|  |  |  | 	// part of the setup and is retried. | 
					
						
							|  |  |  | 	started := false | 
					
						
							| 
									
										
										
										
											2015-03-30 08:36:37 -04:00
										 |  |  | 	defer func() { | 
					
						
							|  |  |  | 		if r := recover(); r != nil { | 
					
						
							|  |  |  | 			if w, ok := r.(waitPanic); ok { | 
					
						
							|  |  |  | 				err = w.err | 
					
						
							| 
									
										
										
										
											2016-03-23 16:17:44 +01:00
										 |  |  | 				if !started { | 
					
						
							|  |  |  | 					fmt.Printf("lldb setup error: %v\n", err) | 
					
						
							|  |  |  | 					err = errRetry | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2015-03-30 08:36:37 -04:00
										 |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			panic(r) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	defer exec.Command("killall", "ios-deploy").Run() // cleanup | 
					
						
							|  |  |  | 	exec.Command("killall", "ios-deploy").Run() | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-06 09:45:24 -05:00
										 |  |  | 	var opts options | 
					
						
							|  |  |  | 	opts, args = parseArgs(args) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-13 07:18:40 -05:00
										 |  |  | 	// Pass the suffix for the current working directory as the | 
					
						
							|  |  |  | 	// first argument to the test. For iOS, cmd/go generates | 
					
						
							|  |  |  | 	// special handling of this argument. | 
					
						
							|  |  |  | 	args = append([]string{"cwdSuffix=" + pkgpath}, args...) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 	// ios-deploy invokes lldb to give us a shell session with the app. | 
					
						
							| 
									
										
										
										
											2015-11-04 11:21:55 -05:00
										 |  |  | 	s, err := newSession(appdir, args, opts) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer func() { | 
					
						
							|  |  |  | 		b := s.out.Bytes() | 
					
						
							|  |  |  | 		if err == nil && !debug { | 
					
						
							|  |  |  | 			i := bytes.Index(b, []byte("(lldb) process continue")) | 
					
						
							|  |  |  | 			if i > 0 { | 
					
						
							|  |  |  | 				b = b[i:] | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		os.Stdout.Write(b) | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Script LLDB. Oh dear. | 
					
						
							|  |  |  | 	s.do(`process handle SIGHUP  --stop false --pass true --notify false`) | 
					
						
							|  |  |  | 	s.do(`process handle SIGPIPE --stop false --pass true --notify false`) | 
					
						
							|  |  |  | 	s.do(`process handle SIGUSR1 --stop false --pass true --notify false`) | 
					
						
							| 
									
										
										
										
											2017-01-07 17:41:00 -08:00
										 |  |  | 	s.do(`process handle SIGUSR2 --stop true --pass false --notify true`) // sent by test harness | 
					
						
							| 
									
										
										
										
											2016-03-24 10:40:34 +01:00
										 |  |  | 	s.do(`process handle SIGCONT --stop false --pass true --notify false`) | 
					
						
							| 
									
										
										
										
											2015-11-04 11:21:55 -05:00
										 |  |  | 	s.do(`process handle SIGSEGV --stop false --pass true --notify false`) // does not work | 
					
						
							|  |  |  | 	s.do(`process handle SIGBUS  --stop false --pass true --notify false`) // does not work | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if opts.lldb { | 
					
						
							|  |  |  | 		_, err := io.Copy(s.in, os.Stdin) | 
					
						
							|  |  |  | 		if err != io.EOF { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-23 16:17:44 +01:00
										 |  |  | 	started = true | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-07 17:41:00 -08:00
										 |  |  | 	s.doCmd("run", "stop reason = signal SIGUSR2", 20*time.Second) | 
					
						
							| 
									
										
										
										
											2015-11-04 11:21:55 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	startTestsLen := s.out.Len() | 
					
						
							|  |  |  | 	fmt.Fprintln(s.in, `process continue`) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	passed := func(out *buf) bool { | 
					
						
							|  |  |  | 		// Just to make things fun, lldb sometimes translates \n into \r\n. | 
					
						
							|  |  |  | 		return s.out.LastIndex([]byte("\nPASS\n")) > startTestsLen || | 
					
						
							|  |  |  | 			s.out.LastIndex([]byte("\nPASS\r")) > startTestsLen || | 
					
						
							|  |  |  | 			s.out.LastIndex([]byte("\n(lldb) PASS\n")) > startTestsLen || | 
					
						
							| 
									
										
										
										
											2017-02-01 18:41:27 +01:00
										 |  |  | 			s.out.LastIndex([]byte("\n(lldb) PASS\r")) > startTestsLen || | 
					
						
							|  |  |  | 			s.out.LastIndex([]byte("exited with status = 0 (0x00000000) \n")) > startTestsLen || | 
					
						
							|  |  |  | 			s.out.LastIndex([]byte("exited with status = 0 (0x00000000) \r")) > startTestsLen | 
					
						
							| 
									
										
										
										
											2015-11-04 11:21:55 -05:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	err = s.wait("test completion", passed, opts.timeout) | 
					
						
							|  |  |  | 	if passed(s.out) { | 
					
						
							|  |  |  | 		// The returned lldb error code is usually non-zero. | 
					
						
							|  |  |  | 		// We check for test success by scanning for the final | 
					
						
							|  |  |  | 		// PASS returned by the test harness, assuming the worst | 
					
						
							|  |  |  | 		// in its absence. | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type lldbSession struct { | 
					
						
							|  |  |  | 	cmd      *exec.Cmd | 
					
						
							|  |  |  | 	in       *os.File | 
					
						
							|  |  |  | 	out      *buf | 
					
						
							|  |  |  | 	timedout chan struct{} | 
					
						
							|  |  |  | 	exited   chan error | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func newSession(appdir string, args []string, opts options) (*lldbSession, error) { | 
					
						
							|  |  |  | 	lldbr, in, err := os.Pipe() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	s := &lldbSession{ | 
					
						
							|  |  |  | 		in:     in, | 
					
						
							|  |  |  | 		out:    new(buf), | 
					
						
							|  |  |  | 		exited: make(chan error), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-23 16:17:44 +01:00
										 |  |  | 	iosdPath, err := exec.LookPath("ios-deploy") | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-11-04 11:21:55 -05:00
										 |  |  | 	s.cmd = exec.Command( | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 		// lldb tries to be clever with terminals. | 
					
						
							|  |  |  | 		// So we wrap it in script(1) and be clever | 
					
						
							|  |  |  | 		// right back at it. | 
					
						
							|  |  |  | 		"script", | 
					
						
							|  |  |  | 		"-q", "-t", "0", | 
					
						
							|  |  |  | 		"/dev/null", | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-23 16:17:44 +01:00
										 |  |  | 		iosdPath, | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 		"--debug", | 
					
						
							|  |  |  | 		"-u", | 
					
						
							|  |  |  | 		"-r", | 
					
						
							|  |  |  | 		"-n", | 
					
						
							|  |  |  | 		`--args=`+strings.Join(args, " ")+``, | 
					
						
							|  |  |  | 		"--bundle", appdir, | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	if debug { | 
					
						
							| 
									
										
										
										
											2015-11-04 11:21:55 -05:00
										 |  |  | 		log.Println(strings.Join(s.cmd.Args, " ")) | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-04 11:21:55 -05:00
										 |  |  | 	var out io.Writer = s.out | 
					
						
							| 
									
										
										
										
											2015-03-06 09:45:24 -05:00
										 |  |  | 	if opts.lldb { | 
					
						
							| 
									
										
										
										
											2015-11-04 11:21:55 -05:00
										 |  |  | 		out = io.MultiWriter(out, os.Stderr) | 
					
						
							| 
									
										
										
										
											2015-03-06 09:45:24 -05:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-11-04 11:21:55 -05:00
										 |  |  | 	s.cmd.Stdout = out | 
					
						
							|  |  |  | 	s.cmd.Stderr = out // everything of interest is on stderr | 
					
						
							|  |  |  | 	s.cmd.Stdin = lldbr | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-04 11:21:55 -05:00
										 |  |  | 	if err := s.cmd.Start(); err != nil { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("ios-deploy failed to start: %v", err) | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Manage the -test.timeout here, outside of the test. There is a lot | 
					
						
							|  |  |  | 	// of moving parts in an iOS test harness (notably lldb) that can | 
					
						
							|  |  |  | 	// swallow useful stdio or cause its own ruckus. | 
					
						
							| 
									
										
										
										
											2015-03-06 09:45:24 -05:00
										 |  |  | 	if opts.timeout > 1*time.Second { | 
					
						
							| 
									
										
										
										
											2015-11-04 11:21:55 -05:00
										 |  |  | 		s.timedout = make(chan struct{}) | 
					
						
							| 
									
										
										
										
											2015-03-06 09:45:24 -05:00
										 |  |  | 		time.AfterFunc(opts.timeout-1*time.Second, func() { | 
					
						
							| 
									
										
										
										
											2015-11-04 11:21:55 -05:00
										 |  |  | 			close(s.timedout) | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	go func() { | 
					
						
							| 
									
										
										
										
											2015-11-04 11:21:55 -05:00
										 |  |  | 		s.exited <- s.cmd.Wait() | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-04 11:21:55 -05:00
										 |  |  | 	cond := func(out *buf) bool { | 
					
						
							|  |  |  | 		i0 := s.out.LastIndex([]byte("(lldb)")) | 
					
						
							|  |  |  | 		i1 := s.out.LastIndex([]byte("fruitstrap")) | 
					
						
							|  |  |  | 		i2 := s.out.LastIndex([]byte(" connect")) | 
					
						
							|  |  |  | 		return i0 > 0 && i1 > 0 && i2 > 0 | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-03-23 16:17:44 +01:00
										 |  |  | 	if err := s.wait("lldb start", cond, 10*time.Second); err != nil { | 
					
						
							|  |  |  | 		panic(waitPanic{err}) | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-11-04 11:21:55 -05:00
										 |  |  | 	return s, nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-04 11:21:55 -05:00
										 |  |  | func (s *lldbSession) do(cmd string) { s.doCmd(cmd, "(lldb)", 0) } | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-04 11:21:55 -05:00
										 |  |  | func (s *lldbSession) doCmd(cmd string, waitFor string, extraTimeout time.Duration) { | 
					
						
							|  |  |  | 	startLen := s.out.Len() | 
					
						
							|  |  |  | 	fmt.Fprintln(s.in, cmd) | 
					
						
							|  |  |  | 	cond := func(out *buf) bool { | 
					
						
							|  |  |  | 		i := s.out.LastIndex([]byte(waitFor)) | 
					
						
							|  |  |  | 		return i > startLen | 
					
						
							| 
									
										
										
										
											2015-03-06 09:45:24 -05:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-11-04 11:21:55 -05:00
										 |  |  | 	if err := s.wait(fmt.Sprintf("running cmd %q", cmd), cond, extraTimeout); err != nil { | 
					
						
							|  |  |  | 		panic(waitPanic{err}) | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-11-04 11:21:55 -05:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-04 11:21:55 -05:00
										 |  |  | func (s *lldbSession) wait(reason string, cond func(out *buf) bool, extraTimeout time.Duration) error { | 
					
						
							| 
									
										
										
										
											2016-03-23 16:17:44 +01:00
										 |  |  | 	doTimeout := 2*time.Second + extraTimeout | 
					
						
							| 
									
										
										
										
											2015-11-04 11:21:55 -05:00
										 |  |  | 	doTimedout := time.After(doTimeout) | 
					
						
							|  |  |  | 	for { | 
					
						
							|  |  |  | 		select { | 
					
						
							|  |  |  | 		case <-s.timedout: | 
					
						
							|  |  |  | 			if p := s.cmd.Process; p != nil { | 
					
						
							|  |  |  | 				p.Kill() | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return fmt.Errorf("test timeout (%s)", reason) | 
					
						
							|  |  |  | 		case <-doTimedout: | 
					
						
							|  |  |  | 			return fmt.Errorf("command timeout (%s for %v)", reason, doTimeout) | 
					
						
							|  |  |  | 		case err := <-s.exited: | 
					
						
							|  |  |  | 			return fmt.Errorf("exited (%s: %v)", reason, err) | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			if cond(s.out) { | 
					
						
							|  |  |  | 				return nil | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			time.Sleep(20 * time.Millisecond) | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-04 11:21:55 -05:00
										 |  |  | type buf struct { | 
					
						
							|  |  |  | 	mu  sync.Mutex | 
					
						
							|  |  |  | 	buf []byte | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-04 11:21:55 -05:00
										 |  |  | func (w *buf) Write(in []byte) (n int, err error) { | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 	w.mu.Lock() | 
					
						
							|  |  |  | 	defer w.mu.Unlock() | 
					
						
							|  |  |  | 	w.buf = append(w.buf, in...) | 
					
						
							| 
									
										
										
										
											2015-11-04 11:21:55 -05:00
										 |  |  | 	return len(in), nil | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-04 11:21:55 -05:00
										 |  |  | func (w *buf) LastIndex(sep []byte) int { | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 	w.mu.Lock() | 
					
						
							|  |  |  | 	defer w.mu.Unlock() | 
					
						
							| 
									
										
										
										
											2015-11-04 11:21:55 -05:00
										 |  |  | 	return bytes.LastIndex(w.buf, sep) | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-04 11:21:55 -05:00
										 |  |  | func (w *buf) Bytes() []byte { | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 	w.mu.Lock() | 
					
						
							|  |  |  | 	defer w.mu.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-04 11:21:55 -05:00
										 |  |  | 	b := make([]byte, len(w.buf)) | 
					
						
							|  |  |  | 	copy(b, w.buf) | 
					
						
							|  |  |  | 	return b | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-04 11:21:55 -05:00
										 |  |  | func (w *buf) Len() int { | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 	w.mu.Lock() | 
					
						
							|  |  |  | 	defer w.mu.Unlock() | 
					
						
							| 
									
										
										
										
											2015-11-04 11:21:55 -05:00
										 |  |  | 	return len(w.buf) | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-04 11:21:55 -05:00
										 |  |  | type waitPanic struct { | 
					
						
							|  |  |  | 	err error | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-06 09:45:24 -05:00
										 |  |  | type options struct { | 
					
						
							|  |  |  | 	timeout time.Duration | 
					
						
							|  |  |  | 	lldb    bool | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func parseArgs(binArgs []string) (opts options, remainingArgs []string) { | 
					
						
							|  |  |  | 	var flagArgs []string | 
					
						
							|  |  |  | 	for _, arg := range binArgs { | 
					
						
							|  |  |  | 		if strings.Contains(arg, "-test.timeout") { | 
					
						
							|  |  |  | 			flagArgs = append(flagArgs, arg) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if strings.Contains(arg, "-lldb") { | 
					
						
							|  |  |  | 			flagArgs = append(flagArgs, arg) | 
					
						
							|  |  |  | 			continue | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-03-06 09:45:24 -05:00
										 |  |  | 		remainingArgs = append(remainingArgs, arg) | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	f := flag.NewFlagSet("", flag.ContinueOnError) | 
					
						
							| 
									
										
										
										
											2017-02-01 12:28:47 +01:00
										 |  |  | 	f.DurationVar(&opts.timeout, "test.timeout", 10*time.Minute, "") | 
					
						
							| 
									
										
										
										
											2015-03-06 09:45:24 -05:00
										 |  |  | 	f.BoolVar(&opts.lldb, "lldb", false, "") | 
					
						
							|  |  |  | 	f.Parse(flagArgs) | 
					
						
							|  |  |  | 	return opts, remainingArgs | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func copyLocalDir(dst, src string) error { | 
					
						
							|  |  |  | 	if err := os.Mkdir(dst, 0755); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	d, err := os.Open(src) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer d.Close() | 
					
						
							|  |  |  | 	fi, err := d.Readdir(-1) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, f := range fi { | 
					
						
							|  |  |  | 		if f.IsDir() { | 
					
						
							|  |  |  | 			if f.Name() == "testdata" { | 
					
						
							|  |  |  | 				if err := cp(dst, filepath.Join(src, f.Name())); err != nil { | 
					
						
							|  |  |  | 					return err | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if err := cp(dst, filepath.Join(src, f.Name())); err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func cp(dst, src string) error { | 
					
						
							|  |  |  | 	out, err := exec.Command("cp", "-a", src, dst).CombinedOutput() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		os.Stderr.Write(out) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func copyLocalData(dstbase string) (pkgpath string, err error) { | 
					
						
							|  |  |  | 	cwd, err := os.Getwd() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	finalPkgpath, underGoRoot, err := subdir() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	cwd = strings.TrimSuffix(cwd, finalPkgpath) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Copy all immediate files and testdata directories between | 
					
						
							|  |  |  | 	// the package being tested and the source root. | 
					
						
							|  |  |  | 	pkgpath = "" | 
					
						
							|  |  |  | 	for _, element := range strings.Split(finalPkgpath, string(filepath.Separator)) { | 
					
						
							|  |  |  | 		if debug { | 
					
						
							|  |  |  | 			log.Printf("copying %s", pkgpath) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		pkgpath = filepath.Join(pkgpath, element) | 
					
						
							|  |  |  | 		dst := filepath.Join(dstbase, pkgpath) | 
					
						
							|  |  |  | 		src := filepath.Join(cwd, pkgpath) | 
					
						
							|  |  |  | 		if err := copyLocalDir(dst, src); err != nil { | 
					
						
							|  |  |  | 			return "", err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Copy timezone file. | 
					
						
							| 
									
										
										
										
											2015-03-02 16:05:11 -05:00
										 |  |  | 	// | 
					
						
							| 
									
										
										
										
											2017-01-13 22:08:27 -05:00
										 |  |  | 	// Apps have the zoneinfo.zip in the root of their app bundle, | 
					
						
							| 
									
										
										
										
											2015-03-02 16:05:11 -05:00
										 |  |  | 	// read by the time package as the working directory at initialization. | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 	if underGoRoot { | 
					
						
							| 
									
										
										
										
											2015-03-02 16:05:11 -05:00
										 |  |  | 		err := cp( | 
					
						
							| 
									
										
										
										
											2017-01-13 22:08:27 -05:00
										 |  |  | 			dstbase, | 
					
						
							| 
									
										
										
										
											2015-03-02 16:05:11 -05:00
										 |  |  | 			filepath.Join(cwd, "lib", "time", "zoneinfo.zip"), | 
					
						
							|  |  |  | 		) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 			return "", err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return finalPkgpath, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // subdir determines the package based on the current working directory, | 
					
						
							|  |  |  | // and returns the path to the package source relative to $GOROOT (or $GOPATH). | 
					
						
							|  |  |  | func subdir() (pkgpath string, underGoRoot bool, err error) { | 
					
						
							|  |  |  | 	cwd, err := os.Getwd() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", false, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if root := runtime.GOROOT(); strings.HasPrefix(cwd, root) { | 
					
						
							|  |  |  | 		subdir, err := filepath.Rel(root, cwd) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return "", false, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return subdir, true, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, p := range filepath.SplitList(build.Default.GOPATH) { | 
					
						
							|  |  |  | 		if !strings.HasPrefix(cwd, p) { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		subdir, err := filepath.Rel(p, cwd) | 
					
						
							|  |  |  | 		if err == nil { | 
					
						
							|  |  |  | 			return subdir, false, nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return "", false, fmt.Errorf( | 
					
						
							|  |  |  | 		"working directory %q is not in either GOROOT(%q) or GOPATH(%q)", | 
					
						
							|  |  |  | 		cwd, | 
					
						
							|  |  |  | 		runtime.GOROOT(), | 
					
						
							|  |  |  | 		build.Default.GOPATH, | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-01 09:37:47 +01:00
										 |  |  | func infoPlist() string { | 
					
						
							|  |  |  | 	return `<?xml version="1.0" encoding="UTF-8"?> | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | 
					
						
							|  |  |  | <plist version="1.0"> | 
					
						
							|  |  |  | <dict> | 
					
						
							|  |  |  | <key>CFBundleName</key><string>golang.gotest</string> | 
					
						
							|  |  |  | <key>CFBundleSupportedPlatforms</key><array><string>iPhoneOS</string></array> | 
					
						
							|  |  |  | <key>CFBundleExecutable</key><string>gotest</string> | 
					
						
							|  |  |  | <key>CFBundleVersion</key><string>1.0</string> | 
					
						
							| 
									
										
										
										
											2017-02-01 09:37:47 +01:00
										 |  |  | <key>CFBundleIdentifier</key><string>` + bundleID + `</string> | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | <key>CFBundleResourceSpecification</key><string>ResourceRules.plist</string> | 
					
						
							|  |  |  | <key>LSRequiresIPhoneOS</key><true/> | 
					
						
							|  |  |  | <key>CFBundleDisplayName</key><string>gotest</string> | 
					
						
							|  |  |  | </dict> | 
					
						
							|  |  |  | </plist> | 
					
						
							|  |  |  | ` | 
					
						
							| 
									
										
										
										
											2017-02-01 09:37:47 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-13 11:31:41 -07:00
										 |  |  | func entitlementsPlist() string { | 
					
						
							|  |  |  | 	return `<?xml version="1.0" encoding="UTF-8"?> | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | 
					
						
							|  |  |  | <plist version="1.0"> | 
					
						
							|  |  |  | <dict> | 
					
						
							|  |  |  | 	<key>keychain-access-groups</key> | 
					
						
							| 
									
										
										
										
											2017-02-01 09:37:47 +01:00
										 |  |  | 	<array><string>` + appID + `</string></array> | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 	<key>get-task-allow</key> | 
					
						
							|  |  |  | 	<true/> | 
					
						
							|  |  |  | 	<key>application-identifier</key> | 
					
						
							| 
									
										
										
										
											2017-02-01 09:37:47 +01:00
										 |  |  | 	<string>` + appID + `</string> | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 	<key>com.apple.developer.team-identifier</key> | 
					
						
							| 
									
										
										
										
											2015-04-13 11:31:41 -07:00
										 |  |  | 	<string>` + teamID + `</string> | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | </dict> | 
					
						
							| 
									
										
										
										
											2015-05-03 00:13:46 -07:00
										 |  |  | </plist> | 
					
						
							|  |  |  | ` | 
					
						
							| 
									
										
										
										
											2015-04-13 11:31:41 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | const resourceRules = `<?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> | 
					
						
							| 
									
										
										
										
											2015-05-03 00:13:46 -07:00
										 |  |  | 	<key>rules</key> | 
					
						
							|  |  |  | 	<dict> | 
					
						
							|  |  |  | 		<key>.*</key> | 
					
						
							|  |  |  | 		<true/> | 
					
						
							|  |  |  | 		<key>Info.plist</key> | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 		<dict> | 
					
						
							| 
									
										
										
										
											2015-05-03 00:13:46 -07:00
										 |  |  | 			<key>omit</key> | 
					
						
							|  |  |  | 			<true/> | 
					
						
							|  |  |  | 			<key>weight</key> | 
					
						
							|  |  |  | 			<integer>10</integer> | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 		</dict> | 
					
						
							|  |  |  | 		<key>ResourceRules.plist</key> | 
					
						
							|  |  |  | 		<dict> | 
					
						
							| 
									
										
										
										
											2015-05-03 00:13:46 -07:00
										 |  |  | 			<key>omit</key> | 
					
						
							|  |  |  | 			<true/> | 
					
						
							|  |  |  | 			<key>weight</key> | 
					
						
							|  |  |  | 			<integer>100</integer> | 
					
						
							| 
									
										
										
										
											2015-03-01 13:47:54 -05:00
										 |  |  | 		</dict> | 
					
						
							|  |  |  | 	</dict> | 
					
						
							|  |  |  | </dict> | 
					
						
							|  |  |  | </plist> | 
					
						
							|  |  |  | ` |