blob: f4baa13cca2265275540995e1d7606af9cc33294 [file] [log] [blame]
David Crawshawd460b6e2015-03-01 13:47:54 -05001// Copyright 2015 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// This program can be used as go_darwin_arm_exec by the Go tool.
6// It executes binaries on an iOS device using the XCode toolchain
7// and the ios-deploy program: https://github.com/phonegap/ios-deploy
David Crawshaw4345a9f2015-03-06 09:45:24 -05008//
9// This script supports an extra flag, -lldb, that pauses execution
10// just before the main program begins and allows the user to control
11// the remote lldb session. This flag is appended to the end of the
12// script's arguments and is not passed through to the underlying
13// binary.
Josh Bleecher Snyder2d0c9622015-04-13 11:31:41 -070014//
15// This script requires that three environment variables be set:
16// GOIOS_DEV_ID: The codesigning developer id or certificate identifier
17// GOIOS_APP_ID: The provisioning app id prefix. Must support wildcard app ids.
18// GOIOS_TEAM_ID: The team id that owns the app id prefix.
19// $GOROOT/misc/ios contains a script, detect.go, that attempts to autodetect these.
David Crawshawd460b6e2015-03-01 13:47:54 -050020package main
21
22import (
23 "bytes"
24 "errors"
25 "flag"
26 "fmt"
27 "go/build"
David Crawshaw4345a9f2015-03-06 09:45:24 -050028 "io"
David Crawshawd460b6e2015-03-01 13:47:54 -050029 "io/ioutil"
30 "log"
31 "os"
32 "os/exec"
33 "path/filepath"
34 "runtime"
35 "strings"
36 "sync"
37 "time"
38)
39
40const debug = false
41
David Crawshaw00e0fe42015-03-30 08:36:37 -040042var errRetry = errors.New("failed to start test harness (retry attempted)")
43
44var tmpdir string
45
Josh Bleecher Snyder2d0c9622015-04-13 11:31:41 -070046var (
47 devID string
48 appID string
49 teamID string
50)
51
David Crawshawd460b6e2015-03-01 13:47:54 -050052func main() {
53 log.SetFlags(0)
54 log.SetPrefix("go_darwin_arm_exec: ")
55 if debug {
56 log.Println(strings.Join(os.Args, " "))
57 }
58 if len(os.Args) < 2 {
59 log.Fatal("usage: go_darwin_arm_exec a.out")
60 }
61
Josh Bleecher Snyder2d0c9622015-04-13 11:31:41 -070062 devID = getenv("GOIOS_DEV_ID")
63 appID = getenv("GOIOS_APP_ID")
64 teamID = getenv("GOIOS_TEAM_ID")
65
David Crawshaw00e0fe42015-03-30 08:36:37 -040066 var err error
67 tmpdir, err = ioutil.TempDir("", "go_darwin_arm_exec_")
68 if err != nil {
69 log.Fatal(err)
70 }
71
72 // Approximately 1 in a 100 binaries fail to start. If it happens,
73 // try again. These failures happen for several reasons beyond
74 // our control, but all of them are safe to retry as they happen
75 // before lldb encounters the initial getwd breakpoint. As we
76 // know the tests haven't started, we are not hiding flaky tests
77 // with this retry.
78 for i := 0; i < 5; i++ {
79 if i > 0 {
80 fmt.Fprintln(os.Stderr, "start timeout, trying again")
81 }
82 err = run(os.Args[1], os.Args[2:])
83 if err == nil || err != errRetry {
84 break
85 }
86 }
87 if !debug {
88 os.RemoveAll(tmpdir)
89 }
90 if err != nil {
David Crawshawd460b6e2015-03-01 13:47:54 -050091 fmt.Fprintf(os.Stderr, "go_darwin_arm_exec: %v\n", err)
92 os.Exit(1)
93 }
94}
95
Josh Bleecher Snyder2d0c9622015-04-13 11:31:41 -070096func getenv(envvar string) string {
97 s := os.Getenv(envvar)
98 if s == "" {
99 log.Fatalf("%s not set\nrun $GOROOT/misc/ios/detect.go to attempt to autodetect", s)
100 }
101 return s
102}
103
David Crawshaw7ff62542015-03-03 17:54:42 -0500104func run(bin string, args []string) (err error) {
David Crawshawd460b6e2015-03-01 13:47:54 -0500105 appdir := filepath.Join(tmpdir, "gotest.app")
David Crawshaw00e0fe42015-03-30 08:36:37 -0400106 os.RemoveAll(appdir)
David Crawshawd460b6e2015-03-01 13:47:54 -0500107 if err := os.MkdirAll(appdir, 0755); err != nil {
108 return err
109 }
110
111 if err := cp(filepath.Join(appdir, "gotest"), bin); err != nil {
112 return err
113 }
114
115 entitlementsPath := filepath.Join(tmpdir, "Entitlements.plist")
Josh Bleecher Snyder2d0c9622015-04-13 11:31:41 -0700116 if err := ioutil.WriteFile(entitlementsPath, []byte(entitlementsPlist()), 0744); err != nil {
David Crawshawd460b6e2015-03-01 13:47:54 -0500117 return err
118 }
119 if err := ioutil.WriteFile(filepath.Join(appdir, "Info.plist"), []byte(infoPlist), 0744); err != nil {
120 return err
121 }
122 if err := ioutil.WriteFile(filepath.Join(appdir, "ResourceRules.plist"), []byte(resourceRules), 0744); err != nil {
123 return err
124 }
125
126 pkgpath, err := copyLocalData(appdir)
127 if err != nil {
128 return err
129 }
130
131 cmd := exec.Command(
132 "codesign",
133 "-f",
Josh Bleecher Snyder2d0c9622015-04-13 11:31:41 -0700134 "-s", devID,
David Crawshawd460b6e2015-03-01 13:47:54 -0500135 "--entitlements", entitlementsPath,
136 appdir,
137 )
138 if debug {
139 log.Println(strings.Join(cmd.Args, " "))
140 }
141 cmd.Stdout = os.Stdout
142 cmd.Stderr = os.Stderr
143 if err := cmd.Run(); err != nil {
144 return fmt.Errorf("codesign: %v", err)
145 }
146
David Crawshaw00e0fe42015-03-30 08:36:37 -0400147 oldwd, err := os.Getwd()
148 if err != nil {
David Crawshawd460b6e2015-03-01 13:47:54 -0500149 return err
150 }
David Crawshaw00e0fe42015-03-30 08:36:37 -0400151 if err := os.Chdir(filepath.Join(appdir, "..")); err != nil {
152 return err
153 }
154 defer os.Chdir(oldwd)
155
156 type waitPanic struct {
157 err error
158 }
159 defer func() {
160 if r := recover(); r != nil {
161 if w, ok := r.(waitPanic); ok {
162 err = w.err
163 return
164 }
165 panic(r)
166 }
167 }()
168
169 defer exec.Command("killall", "ios-deploy").Run() // cleanup
170
171 exec.Command("killall", "ios-deploy").Run()
David Crawshawd460b6e2015-03-01 13:47:54 -0500172
David Crawshaw4345a9f2015-03-06 09:45:24 -0500173 var opts options
174 opts, args = parseArgs(args)
175
David Crawshawd460b6e2015-03-01 13:47:54 -0500176 // ios-deploy invokes lldb to give us a shell session with the app.
177 cmd = exec.Command(
178 // lldb tries to be clever with terminals.
179 // So we wrap it in script(1) and be clever
180 // right back at it.
181 "script",
182 "-q", "-t", "0",
183 "/dev/null",
184
185 "ios-deploy",
186 "--debug",
187 "-u",
188 "-r",
189 "-n",
190 `--args=`+strings.Join(args, " ")+``,
191 "--bundle", appdir,
192 )
193 if debug {
194 log.Println(strings.Join(cmd.Args, " "))
195 }
196
197 lldbr, lldb, err := os.Pipe()
198 if err != nil {
199 return err
200 }
201 w := new(bufWriter)
David Crawshaw4345a9f2015-03-06 09:45:24 -0500202 if opts.lldb {
203 mw := io.MultiWriter(w, os.Stderr)
204 cmd.Stdout = mw
205 cmd.Stderr = mw
206 } else {
207 cmd.Stdout = w
208 cmd.Stderr = w // everything of interest is on stderr
209 }
David Crawshawd460b6e2015-03-01 13:47:54 -0500210 cmd.Stdin = lldbr
211
212 if err := cmd.Start(); err != nil {
213 return fmt.Errorf("ios-deploy failed to start: %v", err)
214 }
215
216 // Manage the -test.timeout here, outside of the test. There is a lot
217 // of moving parts in an iOS test harness (notably lldb) that can
218 // swallow useful stdio or cause its own ruckus.
219 var timedout chan struct{}
David Crawshaw4345a9f2015-03-06 09:45:24 -0500220 if opts.timeout > 1*time.Second {
David Crawshawd460b6e2015-03-01 13:47:54 -0500221 timedout = make(chan struct{})
David Crawshaw4345a9f2015-03-06 09:45:24 -0500222 time.AfterFunc(opts.timeout-1*time.Second, func() {
David Crawshawd460b6e2015-03-01 13:47:54 -0500223 close(timedout)
224 })
225 }
226
227 exited := make(chan error)
228 go func() {
229 exited <- cmd.Wait()
230 }()
231
David Crawshawed928622015-03-24 20:52:11 -0400232 waitFor := func(stage, str string, timeout time.Duration) error {
David Crawshawd460b6e2015-03-01 13:47:54 -0500233 select {
234 case <-timedout:
235 w.printBuf()
236 if p := cmd.Process; p != nil {
237 p.Kill()
238 }
239 return fmt.Errorf("timeout (stage %s)", stage)
240 case err := <-exited:
241 w.printBuf()
242 return fmt.Errorf("failed (stage %s): %v", stage, err)
David Crawshawed928622015-03-24 20:52:11 -0400243 case i := <-w.find(str, timeout):
David Crawshaw00e0fe42015-03-30 08:36:37 -0400244 if i < 0 {
245 log.Printf("timed out on stage %q, retrying", stage)
246 return errRetry
David Crawshawed928622015-03-24 20:52:11 -0400247 }
David Crawshaw00e0fe42015-03-30 08:36:37 -0400248 w.clearTo(i + len(str))
David Crawshawd460b6e2015-03-01 13:47:54 -0500249 return nil
250 }
251 }
252 do := func(cmd string) {
253 fmt.Fprintln(lldb, cmd)
David Crawshawed928622015-03-24 20:52:11 -0400254 if err := waitFor(fmt.Sprintf("prompt after %q", cmd), "(lldb)", 0); err != nil {
David Crawshaw7ff62542015-03-03 17:54:42 -0500255 panic(waitPanic{err})
256 }
David Crawshawd460b6e2015-03-01 13:47:54 -0500257 }
258
259 // Wait for installation and connection.
David Crawshawe6d52332015-04-15 14:59:46 -0400260 if err := waitFor("ios-deploy before run", "(lldb)", 0); err != nil {
David Crawshaw00e0fe42015-03-30 08:36:37 -0400261 // Retry if we see a rare and longstanding ios-deploy bug.
262 // https://github.com/phonegap/ios-deploy/issues/11
263 // Assertion failed: (AMDeviceStartService(device, CFSTR("com.apple.debugserver"), &gdbfd, NULL) == 0)
264 log.Printf("%v, retrying", err)
265 return errRetry
David Crawshawd460b6e2015-03-01 13:47:54 -0500266 }
267
268 // Script LLDB. Oh dear.
269 do(`process handle SIGHUP --stop false --pass true --notify false`)
270 do(`process handle SIGPIPE --stop false --pass true --notify false`)
271 do(`process handle SIGUSR1 --stop false --pass true --notify false`)
272 do(`process handle SIGSEGV --stop false --pass true --notify false`) // does not work
273 do(`process handle SIGBUS --stop false --pass true --notify false`) // does not work
274
David Crawshaw4345a9f2015-03-06 09:45:24 -0500275 if opts.lldb {
276 _, err := io.Copy(lldb, os.Stdin)
277 if err != io.EOF {
278 return err
279 }
280 return nil
281 }
282
David Crawshawd460b6e2015-03-01 13:47:54 -0500283 do(`breakpoint set -n getwd`) // in runtime/cgo/gcc_darwin_arm.go
David Crawshaw1fdeb6b2015-03-03 14:18:56 -0500284
David Crawshaw7ff62542015-03-03 17:54:42 -0500285 fmt.Fprintln(lldb, `run`)
David Crawshaw00e0fe42015-03-30 08:36:37 -0400286 if err := waitFor("br getwd", "stop reason = breakpoint", 20*time.Second); err != nil {
287 // At this point we see several flaky errors from the iOS
288 // build infrastructure. The most common is never reaching
289 // the breakpoint, which we catch with a timeout. Very
290 // occasionally lldb can produce errors like:
291 //
292 // Breakpoint 1: no locations (pending).
293 // WARNING: Unable to resolve breakpoint to any actual locations.
294 //
295 // As no actual test code has been executed by this point,
296 // we treat all errors as recoverable.
297 if err != errRetry {
298 log.Printf("%v, retrying", err)
299 err = errRetry
300 }
David Crawshawd460b6e2015-03-01 13:47:54 -0500301 return err
302 }
David Crawshawed928622015-03-24 20:52:11 -0400303 if err := waitFor("br getwd prompt", "(lldb)", 0); err != nil {
David Crawshawd460b6e2015-03-01 13:47:54 -0500304 return err
305 }
306
307 // Move the current working directory into the faux gopath.
David Crawshaw4345a9f2015-03-06 09:45:24 -0500308 if pkgpath != "src" {
309 do(`breakpoint delete 1`)
310 do(`expr char* $mem = (char*)malloc(512)`)
311 do(`expr $mem = (char*)getwd($mem, 512)`)
312 do(`expr $mem = (char*)strcat($mem, "/` + pkgpath + `")`)
313 do(`call (void)chdir($mem)`)
314 }
David Crawshawd460b6e2015-03-01 13:47:54 -0500315
David Crawshawd460b6e2015-03-01 13:47:54 -0500316 // Run the tests.
317 w.trimSuffix("(lldb) ")
David Crawshaw7ff62542015-03-03 17:54:42 -0500318 fmt.Fprintln(lldb, `process continue`)
David Crawshawd460b6e2015-03-01 13:47:54 -0500319
320 // Wait for the test to complete.
321 select {
322 case <-timedout:
323 w.printBuf()
324 if p := cmd.Process; p != nil {
325 p.Kill()
326 }
327 return errors.New("timeout running tests")
David Crawshawe6d52332015-04-15 14:59:46 -0400328 case <-w.find("\nPASS", 0):
329 passed := w.isPass()
330 w.printBuf()
331 if passed {
332 return nil
333 }
334 return errors.New("test failure")
David Crawshawd460b6e2015-03-01 13:47:54 -0500335 case err := <-exited:
336 // The returned lldb error code is usually non-zero.
337 // We check for test success by scanning for the final
338 // PASS returned by the test harness, assuming the worst
339 // in its absence.
340 if w.isPass() {
341 err = nil
342 } else if err == nil {
343 err = errors.New("test failure")
344 }
345 w.printBuf()
346 return err
347 }
348}
349
350type bufWriter struct {
351 mu sync.Mutex
352 buf []byte
353 suffix []byte // remove from each Write
354
David Crawshawed928622015-03-24 20:52:11 -0400355 findTxt []byte // search buffer on each Write
356 findCh chan int // report find position
357 findAfter *time.Timer
David Crawshawd460b6e2015-03-01 13:47:54 -0500358}
359
360func (w *bufWriter) Write(in []byte) (n int, err error) {
361 w.mu.Lock()
362 defer w.mu.Unlock()
363
364 n = len(in)
365 in = bytes.TrimSuffix(in, w.suffix)
366
David Crawshawe6d52332015-04-15 14:59:46 -0400367 if debug {
368 inTxt := strings.Replace(string(in), "\n", "\\n", -1)
369 findTxt := strings.Replace(string(w.findTxt), "\n", "\\n", -1)
370 fmt.Printf("debug --> %s <-- debug (findTxt='%s')\n", inTxt, findTxt)
371 }
372
David Crawshawd460b6e2015-03-01 13:47:54 -0500373 w.buf = append(w.buf, in...)
374
375 if len(w.findTxt) > 0 {
376 if i := bytes.Index(w.buf, w.findTxt); i >= 0 {
377 w.findCh <- i
378 close(w.findCh)
379 w.findTxt = nil
380 w.findCh = nil
David Crawshawed928622015-03-24 20:52:11 -0400381 if w.findAfter != nil {
382 w.findAfter.Stop()
383 w.findAfter = nil
384 }
David Crawshawd460b6e2015-03-01 13:47:54 -0500385 }
386 }
387 return n, nil
388}
389
390func (w *bufWriter) trimSuffix(p string) {
391 w.mu.Lock()
392 defer w.mu.Unlock()
393 w.suffix = []byte(p)
394}
395
396func (w *bufWriter) printBuf() {
397 w.mu.Lock()
398 defer w.mu.Unlock()
399 fmt.Fprintf(os.Stderr, "%s", w.buf)
400 w.buf = nil
401}
402
403func (w *bufWriter) clearTo(i int) {
404 w.mu.Lock()
405 defer w.mu.Unlock()
David Crawshawd460b6e2015-03-01 13:47:54 -0500406 w.buf = w.buf[i:]
407}
408
David Crawshawed928622015-03-24 20:52:11 -0400409// find returns a channel that will have exactly one byte index sent
410// to it when the text str appears in the buffer. If the text does not
411// appear before timeout, -1 is sent.
412//
413// A timeout of zero means no timeout.
414func (w *bufWriter) find(str string, timeout time.Duration) <-chan int {
David Crawshawd460b6e2015-03-01 13:47:54 -0500415 w.mu.Lock()
416 defer w.mu.Unlock()
417 if len(w.findTxt) > 0 {
418 panic(fmt.Sprintf("find(%s): already trying to find %s", str, w.findTxt))
419 }
420 txt := []byte(str)
421 ch := make(chan int, 1)
422 if i := bytes.Index(w.buf, txt); i >= 0 {
423 ch <- i
424 close(ch)
425 } else {
426 w.findTxt = txt
427 w.findCh = ch
David Crawshawed928622015-03-24 20:52:11 -0400428 if timeout > 0 {
429 w.findAfter = time.AfterFunc(timeout, func() {
430 w.mu.Lock()
431 defer w.mu.Unlock()
432 if w.findCh == ch {
433 w.findTxt = nil
434 w.findCh = nil
435 w.findAfter = nil
436 ch <- -1
437 close(ch)
438 }
439 })
440 }
David Crawshawd460b6e2015-03-01 13:47:54 -0500441 }
442 return ch
443}
444
445func (w *bufWriter) isPass() bool {
446 w.mu.Lock()
447 defer w.mu.Unlock()
448
449 // The final stdio of lldb is non-deterministic, so we
450 // scan the whole buffer.
451 //
452 // Just to make things fun, lldb sometimes translates \n
453 // into \r\n.
454 return bytes.Contains(w.buf, []byte("\nPASS\n")) || bytes.Contains(w.buf, []byte("\nPASS\r"))
455}
456
David Crawshaw4345a9f2015-03-06 09:45:24 -0500457type options struct {
458 timeout time.Duration
459 lldb bool
460}
461
462func parseArgs(binArgs []string) (opts options, remainingArgs []string) {
463 var flagArgs []string
464 for _, arg := range binArgs {
465 if strings.Contains(arg, "-test.timeout") {
466 flagArgs = append(flagArgs, arg)
David Crawshawd460b6e2015-03-01 13:47:54 -0500467 }
David Crawshaw4345a9f2015-03-06 09:45:24 -0500468 if strings.Contains(arg, "-lldb") {
469 flagArgs = append(flagArgs, arg)
470 continue
471 }
472 remainingArgs = append(remainingArgs, arg)
David Crawshawd460b6e2015-03-01 13:47:54 -0500473 }
474 f := flag.NewFlagSet("", flag.ContinueOnError)
David Crawshaw4345a9f2015-03-06 09:45:24 -0500475 f.DurationVar(&opts.timeout, "test.timeout", 0, "")
476 f.BoolVar(&opts.lldb, "lldb", false, "")
477 f.Parse(flagArgs)
478 return opts, remainingArgs
479
David Crawshawd460b6e2015-03-01 13:47:54 -0500480}
481
482func copyLocalDir(dst, src string) error {
483 if err := os.Mkdir(dst, 0755); err != nil {
484 return err
485 }
486
487 d, err := os.Open(src)
488 if err != nil {
489 return err
490 }
491 defer d.Close()
492 fi, err := d.Readdir(-1)
493 if err != nil {
494 return err
495 }
496
497 for _, f := range fi {
498 if f.IsDir() {
499 if f.Name() == "testdata" {
500 if err := cp(dst, filepath.Join(src, f.Name())); err != nil {
501 return err
502 }
503 }
504 continue
505 }
506 if err := cp(dst, filepath.Join(src, f.Name())); err != nil {
507 return err
508 }
509 }
510 return nil
511}
512
513func cp(dst, src string) error {
514 out, err := exec.Command("cp", "-a", src, dst).CombinedOutput()
515 if err != nil {
516 os.Stderr.Write(out)
517 }
518 return err
519}
520
521func copyLocalData(dstbase string) (pkgpath string, err error) {
522 cwd, err := os.Getwd()
523 if err != nil {
524 return "", err
525 }
526
527 finalPkgpath, underGoRoot, err := subdir()
528 if err != nil {
529 return "", err
530 }
531 cwd = strings.TrimSuffix(cwd, finalPkgpath)
532
533 // Copy all immediate files and testdata directories between
534 // the package being tested and the source root.
535 pkgpath = ""
536 for _, element := range strings.Split(finalPkgpath, string(filepath.Separator)) {
537 if debug {
538 log.Printf("copying %s", pkgpath)
539 }
540 pkgpath = filepath.Join(pkgpath, element)
541 dst := filepath.Join(dstbase, pkgpath)
542 src := filepath.Join(cwd, pkgpath)
543 if err := copyLocalDir(dst, src); err != nil {
544 return "", err
545 }
546 }
547
548 // Copy timezone file.
David Crawshaw66416c02015-03-02 16:05:11 -0500549 //
550 // Typical apps have the zoneinfo.zip in the root of their app bundle,
551 // read by the time package as the working directory at initialization.
552 // As we move the working directory to the GOROOT pkg directory, we
553 // install the zoneinfo.zip file in the pkgpath.
David Crawshawd460b6e2015-03-01 13:47:54 -0500554 if underGoRoot {
David Crawshaw66416c02015-03-02 16:05:11 -0500555 err := cp(
556 filepath.Join(dstbase, pkgpath),
557 filepath.Join(cwd, "lib", "time", "zoneinfo.zip"),
558 )
559 if err != nil {
David Crawshawd460b6e2015-03-01 13:47:54 -0500560 return "", err
561 }
562 }
563
564 return finalPkgpath, nil
565}
566
567// subdir determines the package based on the current working directory,
568// and returns the path to the package source relative to $GOROOT (or $GOPATH).
569func subdir() (pkgpath string, underGoRoot bool, err error) {
570 cwd, err := os.Getwd()
571 if err != nil {
572 return "", false, err
573 }
574 if root := runtime.GOROOT(); strings.HasPrefix(cwd, root) {
575 subdir, err := filepath.Rel(root, cwd)
576 if err != nil {
577 return "", false, err
578 }
579 return subdir, true, nil
580 }
581
582 for _, p := range filepath.SplitList(build.Default.GOPATH) {
583 if !strings.HasPrefix(cwd, p) {
584 continue
585 }
586 subdir, err := filepath.Rel(p, cwd)
587 if err == nil {
588 return subdir, false, nil
589 }
590 }
591 return "", false, fmt.Errorf(
592 "working directory %q is not in either GOROOT(%q) or GOPATH(%q)",
593 cwd,
594 runtime.GOROOT(),
595 build.Default.GOPATH,
596 )
597}
598
599const infoPlist = `<?xml version="1.0" encoding="UTF-8"?>
600<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
601<plist version="1.0">
602<dict>
603<key>CFBundleName</key><string>golang.gotest</string>
604<key>CFBundleSupportedPlatforms</key><array><string>iPhoneOS</string></array>
605<key>CFBundleExecutable</key><string>gotest</string>
606<key>CFBundleVersion</key><string>1.0</string>
607<key>CFBundleIdentifier</key><string>golang.gotest</string>
608<key>CFBundleResourceSpecification</key><string>ResourceRules.plist</string>
609<key>LSRequiresIPhoneOS</key><true/>
610<key>CFBundleDisplayName</key><string>gotest</string>
611</dict>
612</plist>
613`
614
Josh Bleecher Snyder2d0c9622015-04-13 11:31:41 -0700615func entitlementsPlist() string {
616 return `<?xml version="1.0" encoding="UTF-8"?>
David Crawshawd460b6e2015-03-01 13:47:54 -0500617<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
618<plist version="1.0">
619<dict>
620 <key>keychain-access-groups</key>
Josh Bleecher Snyder8fd1ec22015-04-15 17:00:05 -0700621 <array><string>` + appID + `.golang.gotest</string></array>
David Crawshawd460b6e2015-03-01 13:47:54 -0500622 <key>get-task-allow</key>
623 <true/>
624 <key>application-identifier</key>
Josh Bleecher Snyder8fd1ec22015-04-15 17:00:05 -0700625 <string>` + appID + `.golang.gotest</string>
David Crawshawd460b6e2015-03-01 13:47:54 -0500626 <key>com.apple.developer.team-identifier</key>
Josh Bleecher Snyder2d0c9622015-04-13 11:31:41 -0700627 <string>` + teamID + `</string>
David Crawshawd460b6e2015-03-01 13:47:54 -0500628</dict>
629</plist>`
Josh Bleecher Snyder2d0c9622015-04-13 11:31:41 -0700630}
David Crawshawd460b6e2015-03-01 13:47:54 -0500631
632const resourceRules = `<?xml version="1.0" encoding="UTF-8"?>
633<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
634<plist version="1.0">
635<dict>
636 <key>rules</key>
637 <dict>
638 <key>.*</key><true/>
639 <key>Info.plist</key>
640 <dict>
641 <key>omit</key> <true/>
642 <key>weight</key> <real>10</real>
643 </dict>
644 <key>ResourceRules.plist</key>
645 <dict>
646 <key>omit</key> <true/>
647 <key>weight</key> <real>100</real>
648 </dict>
649 </dict>
650</dict>
651</plist>
652`