go_ios_exec.go 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909
  1. // 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. // This program can be used as go_ios_$GOARCH_exec by the Go tool.
  5. // It executes binaries on an iOS device using the XCode toolchain
  6. // and the ios-deploy program: https://github.com/phonegap/ios-deploy
  7. //
  8. // This script supports an extra flag, -lldb, that pauses execution
  9. // just before the main program begins and allows the user to control
  10. // the remote lldb session. This flag is appended to the end of the
  11. // script's arguments and is not passed through to the underlying
  12. // binary.
  13. //
  14. // This script requires that three environment variables be set:
  15. // GOIOS_DEV_ID: The codesigning developer id or certificate identifier
  16. // GOIOS_APP_ID: The provisioning app id prefix. Must support wildcard app ids.
  17. // GOIOS_TEAM_ID: The team id that owns the app id prefix.
  18. // $GOROOT/misc/ios contains a script, detect.go, that attempts to autodetect these.
  19. package main
  20. import (
  21. "bytes"
  22. "encoding/xml"
  23. "errors"
  24. "fmt"
  25. "go/build"
  26. "io"
  27. "log"
  28. "net"
  29. "os"
  30. "os/exec"
  31. "os/signal"
  32. "path/filepath"
  33. "runtime"
  34. "strconv"
  35. "strings"
  36. "syscall"
  37. "time"
  38. )
  39. const debug = false
  40. var tmpdir string
  41. var (
  42. devID string
  43. appID string
  44. teamID string
  45. bundleID string
  46. deviceID string
  47. )
  48. // lock is a file lock to serialize iOS runs. It is global to avoid the
  49. // garbage collector finalizing it, closing the file and releasing the
  50. // lock prematurely.
  51. var lock *os.File
  52. func main() {
  53. log.SetFlags(0)
  54. log.SetPrefix("go_ios_exec: ")
  55. if debug {
  56. log.Println(strings.Join(os.Args, " "))
  57. }
  58. if len(os.Args) < 2 {
  59. log.Fatal("usage: go_ios_exec a.out")
  60. }
  61. // For compatibility with the old builders, use a fallback bundle ID
  62. bundleID = "golang.gotest"
  63. exitCode, err := runMain()
  64. if err != nil {
  65. log.Fatalf("%v\n", err)
  66. }
  67. os.Exit(exitCode)
  68. }
  69. func runMain() (int, error) {
  70. var err error
  71. tmpdir, err = os.MkdirTemp("", "go_ios_exec_")
  72. if err != nil {
  73. return 1, err
  74. }
  75. if !debug {
  76. defer os.RemoveAll(tmpdir)
  77. }
  78. appdir := filepath.Join(tmpdir, "gotest.app")
  79. os.RemoveAll(appdir)
  80. if err := assembleApp(appdir, os.Args[1]); err != nil {
  81. return 1, err
  82. }
  83. // This wrapper uses complicated machinery to run iOS binaries. It
  84. // works, but only when running one binary at a time.
  85. // Use a file lock to make sure only one wrapper is running at a time.
  86. //
  87. // The lock file is never deleted, to avoid concurrent locks on distinct
  88. // files with the same path.
  89. lockName := filepath.Join(os.TempDir(), "go_ios_exec-"+deviceID+".lock")
  90. lock, err = os.OpenFile(lockName, os.O_CREATE|os.O_RDONLY, 0666)
  91. if err != nil {
  92. return 1, err
  93. }
  94. if err := syscall.Flock(int(lock.Fd()), syscall.LOCK_EX); err != nil {
  95. return 1, err
  96. }
  97. if goarch := os.Getenv("GOARCH"); goarch == "arm64" {
  98. err = runOnDevice(appdir)
  99. } else {
  100. err = runOnSimulator(appdir)
  101. }
  102. if err != nil {
  103. // If the lldb driver completed with an exit code, use that.
  104. if err, ok := err.(*exec.ExitError); ok {
  105. if ws, ok := err.Sys().(interface{ ExitStatus() int }); ok {
  106. return ws.ExitStatus(), nil
  107. }
  108. }
  109. return 1, err
  110. }
  111. return 0, nil
  112. }
  113. func runOnSimulator(appdir string) error {
  114. if err := installSimulator(appdir); err != nil {
  115. return err
  116. }
  117. return runSimulator(appdir, bundleID, os.Args[2:])
  118. }
  119. func runOnDevice(appdir string) error {
  120. // e.g. B393DDEB490947F5A463FD074299B6C0AXXXXXXX
  121. devID = getenv("GOIOS_DEV_ID")
  122. // e.g. Z8B3JBXXXX.org.golang.sample, Z8B3JBXXXX prefix is available at
  123. // https://developer.apple.com/membercenter/index.action#accountSummary as Team ID.
  124. appID = getenv("GOIOS_APP_ID")
  125. // e.g. Z8B3JBXXXX, available at
  126. // https://developer.apple.com/membercenter/index.action#accountSummary as Team ID.
  127. teamID = getenv("GOIOS_TEAM_ID")
  128. // Device IDs as listed with ios-deploy -c.
  129. deviceID = os.Getenv("GOIOS_DEVICE_ID")
  130. if _, id, ok := strings.Cut(appID, "."); ok {
  131. bundleID = id
  132. }
  133. if err := signApp(appdir); err != nil {
  134. return err
  135. }
  136. if err := uninstallDevice(bundleID); err != nil {
  137. return err
  138. }
  139. if err := installDevice(appdir); err != nil {
  140. return err
  141. }
  142. if err := mountDevImage(); err != nil {
  143. return err
  144. }
  145. // Kill any hanging debug bridges that might take up port 3222.
  146. exec.Command("killall", "idevicedebugserverproxy").Run()
  147. closer, err := startDebugBridge()
  148. if err != nil {
  149. return err
  150. }
  151. defer closer()
  152. return runDevice(appdir, bundleID, os.Args[2:])
  153. }
  154. func getenv(envvar string) string {
  155. s := os.Getenv(envvar)
  156. if s == "" {
  157. log.Fatalf("%s not set\nrun $GOROOT/misc/ios/detect.go to attempt to autodetect", envvar)
  158. }
  159. return s
  160. }
  161. func assembleApp(appdir, bin string) error {
  162. if err := os.MkdirAll(appdir, 0755); err != nil {
  163. return err
  164. }
  165. if err := cp(filepath.Join(appdir, "gotest"), bin); err != nil {
  166. return err
  167. }
  168. pkgpath, err := copyLocalData(appdir)
  169. if err != nil {
  170. return err
  171. }
  172. entitlementsPath := filepath.Join(tmpdir, "Entitlements.plist")
  173. if err := os.WriteFile(entitlementsPath, []byte(entitlementsPlist()), 0744); err != nil {
  174. return err
  175. }
  176. if err := os.WriteFile(filepath.Join(appdir, "Info.plist"), []byte(infoPlist(pkgpath)), 0744); err != nil {
  177. return err
  178. }
  179. if err := os.WriteFile(filepath.Join(appdir, "ResourceRules.plist"), []byte(resourceRules), 0744); err != nil {
  180. return err
  181. }
  182. return nil
  183. }
  184. func signApp(appdir string) error {
  185. entitlementsPath := filepath.Join(tmpdir, "Entitlements.plist")
  186. cmd := exec.Command(
  187. "codesign",
  188. "-f",
  189. "-s", devID,
  190. "--entitlements", entitlementsPath,
  191. appdir,
  192. )
  193. if debug {
  194. log.Println(strings.Join(cmd.Args, " "))
  195. }
  196. cmd.Stdout = os.Stdout
  197. cmd.Stderr = os.Stderr
  198. if err := cmd.Run(); err != nil {
  199. return fmt.Errorf("codesign: %v", err)
  200. }
  201. return nil
  202. }
  203. // mountDevImage ensures a developer image is mounted on the device.
  204. // The image contains the device lldb server for idevicedebugserverproxy
  205. // to connect to.
  206. func mountDevImage() error {
  207. // Check for existing mount.
  208. cmd := idevCmd(exec.Command("ideviceimagemounter", "-l", "-x"))
  209. out, err := cmd.CombinedOutput()
  210. if err != nil {
  211. os.Stderr.Write(out)
  212. return fmt.Errorf("ideviceimagemounter: %v", err)
  213. }
  214. var info struct {
  215. Dict struct {
  216. Data []byte `xml:",innerxml"`
  217. } `xml:"dict"`
  218. }
  219. if err := xml.Unmarshal(out, &info); err != nil {
  220. return fmt.Errorf("mountDevImage: failed to decode mount information: %v", err)
  221. }
  222. dict, err := parsePlistDict(info.Dict.Data)
  223. if err != nil {
  224. return fmt.Errorf("mountDevImage: failed to parse mount information: %v", err)
  225. }
  226. if dict["ImagePresent"] == "true" && dict["Status"] == "Complete" {
  227. return nil
  228. }
  229. // Some devices only give us an ImageSignature key.
  230. if _, exists := dict["ImageSignature"]; exists {
  231. return nil
  232. }
  233. // No image is mounted. Find a suitable image.
  234. imgPath, err := findDevImage()
  235. if err != nil {
  236. return err
  237. }
  238. sigPath := imgPath + ".signature"
  239. cmd = idevCmd(exec.Command("ideviceimagemounter", imgPath, sigPath))
  240. if out, err := cmd.CombinedOutput(); err != nil {
  241. os.Stderr.Write(out)
  242. return fmt.Errorf("ideviceimagemounter: %v", err)
  243. }
  244. return nil
  245. }
  246. // findDevImage use the device iOS version and build to locate a suitable
  247. // developer image.
  248. func findDevImage() (string, error) {
  249. cmd := idevCmd(exec.Command("ideviceinfo"))
  250. out, err := cmd.Output()
  251. if err != nil {
  252. return "", fmt.Errorf("ideviceinfo: %v", err)
  253. }
  254. var iosVer, buildVer string
  255. lines := bytes.Split(out, []byte("\n"))
  256. for _, line := range lines {
  257. key, val, ok := strings.Cut(string(line), ": ")
  258. if !ok {
  259. continue
  260. }
  261. switch key {
  262. case "ProductVersion":
  263. iosVer = val
  264. case "BuildVersion":
  265. buildVer = val
  266. }
  267. }
  268. if iosVer == "" || buildVer == "" {
  269. return "", errors.New("failed to parse ideviceinfo output")
  270. }
  271. verSplit := strings.Split(iosVer, ".")
  272. if len(verSplit) > 2 {
  273. // Developer images are specific to major.minor ios version.
  274. // Cut off the patch version.
  275. iosVer = strings.Join(verSplit[:2], ".")
  276. }
  277. sdkBase := "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport"
  278. patterns := []string{fmt.Sprintf("%s (%s)", iosVer, buildVer), fmt.Sprintf("%s (*)", iosVer), fmt.Sprintf("%s*", iosVer)}
  279. for _, pattern := range patterns {
  280. matches, err := filepath.Glob(filepath.Join(sdkBase, pattern, "DeveloperDiskImage.dmg"))
  281. if err != nil {
  282. return "", fmt.Errorf("findDevImage: %v", err)
  283. }
  284. if len(matches) > 0 {
  285. return matches[0], nil
  286. }
  287. }
  288. return "", fmt.Errorf("failed to find matching developer image for iOS version %s build %s", iosVer, buildVer)
  289. }
  290. // startDebugBridge ensures that the idevicedebugserverproxy runs on
  291. // port 3222.
  292. func startDebugBridge() (func(), error) {
  293. errChan := make(chan error, 1)
  294. cmd := idevCmd(exec.Command("idevicedebugserverproxy", "3222"))
  295. var stderr bytes.Buffer
  296. cmd.Stderr = &stderr
  297. if err := cmd.Start(); err != nil {
  298. return nil, fmt.Errorf("idevicedebugserverproxy: %v", err)
  299. }
  300. go func() {
  301. if err := cmd.Wait(); err != nil {
  302. if _, ok := err.(*exec.ExitError); ok {
  303. errChan <- fmt.Errorf("idevicedebugserverproxy: %s", stderr.Bytes())
  304. } else {
  305. errChan <- fmt.Errorf("idevicedebugserverproxy: %v", err)
  306. }
  307. }
  308. errChan <- nil
  309. }()
  310. closer := func() {
  311. cmd.Process.Kill()
  312. <-errChan
  313. }
  314. // Dial localhost:3222 to ensure the proxy is ready.
  315. delay := time.Second / 4
  316. for attempt := 0; attempt < 5; attempt++ {
  317. conn, err := net.DialTimeout("tcp", "localhost:3222", 5*time.Second)
  318. if err == nil {
  319. conn.Close()
  320. return closer, nil
  321. }
  322. select {
  323. case <-time.After(delay):
  324. delay *= 2
  325. case err := <-errChan:
  326. return nil, err
  327. }
  328. }
  329. closer()
  330. return nil, errors.New("failed to set up idevicedebugserverproxy")
  331. }
  332. // findDeviceAppPath returns the device path to the app with the
  333. // given bundle ID. It parses the output of ideviceinstaller -l -o xml,
  334. // looking for the bundle ID and the corresponding Path value.
  335. func findDeviceAppPath(bundleID string) (string, error) {
  336. cmd := idevCmd(exec.Command("ideviceinstaller", "-l", "-o", "xml"))
  337. out, err := cmd.CombinedOutput()
  338. if err != nil {
  339. os.Stderr.Write(out)
  340. return "", fmt.Errorf("ideviceinstaller: -l -o xml %v", err)
  341. }
  342. var list struct {
  343. Apps []struct {
  344. Data []byte `xml:",innerxml"`
  345. } `xml:"array>dict"`
  346. }
  347. if err := xml.Unmarshal(out, &list); err != nil {
  348. return "", fmt.Errorf("failed to parse ideviceinstaller output: %v", err)
  349. }
  350. for _, app := range list.Apps {
  351. values, err := parsePlistDict(app.Data)
  352. if err != nil {
  353. return "", fmt.Errorf("findDeviceAppPath: failed to parse app dict: %v", err)
  354. }
  355. if values["CFBundleIdentifier"] == bundleID {
  356. if path, ok := values["Path"]; ok {
  357. return path, nil
  358. }
  359. }
  360. }
  361. return "", fmt.Errorf("failed to find device path for bundle: %s", bundleID)
  362. }
  363. // Parse an xml encoded plist. Plist values are mapped to string.
  364. func parsePlistDict(dict []byte) (map[string]string, error) {
  365. d := xml.NewDecoder(bytes.NewReader(dict))
  366. values := make(map[string]string)
  367. var key string
  368. var hasKey bool
  369. for {
  370. tok, err := d.Token()
  371. if err == io.EOF {
  372. break
  373. }
  374. if err != nil {
  375. return nil, err
  376. }
  377. if tok, ok := tok.(xml.StartElement); ok {
  378. if tok.Name.Local == "key" {
  379. if err := d.DecodeElement(&key, &tok); err != nil {
  380. return nil, err
  381. }
  382. hasKey = true
  383. } else if hasKey {
  384. var val string
  385. var err error
  386. switch n := tok.Name.Local; n {
  387. case "true", "false":
  388. // Bools are represented as <true/> and <false/>.
  389. val = n
  390. err = d.Skip()
  391. default:
  392. err = d.DecodeElement(&val, &tok)
  393. }
  394. if err != nil {
  395. return nil, err
  396. }
  397. values[key] = val
  398. hasKey = false
  399. } else {
  400. if err := d.Skip(); err != nil {
  401. return nil, err
  402. }
  403. }
  404. }
  405. }
  406. return values, nil
  407. }
  408. func installSimulator(appdir string) error {
  409. cmd := exec.Command(
  410. "xcrun", "simctl", "install",
  411. "booted", // Install to the booted simulator.
  412. appdir,
  413. )
  414. if out, err := cmd.CombinedOutput(); err != nil {
  415. os.Stderr.Write(out)
  416. return fmt.Errorf("xcrun simctl install booted %q: %v", appdir, err)
  417. }
  418. return nil
  419. }
  420. func uninstallDevice(bundleID string) error {
  421. cmd := idevCmd(exec.Command(
  422. "ideviceinstaller",
  423. "-U", bundleID,
  424. ))
  425. if out, err := cmd.CombinedOutput(); err != nil {
  426. os.Stderr.Write(out)
  427. return fmt.Errorf("ideviceinstaller -U %q: %s", bundleID, err)
  428. }
  429. return nil
  430. }
  431. func installDevice(appdir string) error {
  432. attempt := 0
  433. for {
  434. cmd := idevCmd(exec.Command(
  435. "ideviceinstaller",
  436. "-i", appdir,
  437. ))
  438. if out, err := cmd.CombinedOutput(); err != nil {
  439. // Sometimes, installing the app fails for some reason.
  440. // Give the device a few seconds and try again.
  441. if attempt < 5 {
  442. time.Sleep(5 * time.Second)
  443. attempt++
  444. continue
  445. }
  446. os.Stderr.Write(out)
  447. return fmt.Errorf("ideviceinstaller -i %q: %v (%d attempts)", appdir, err, attempt)
  448. }
  449. return nil
  450. }
  451. }
  452. func idevCmd(cmd *exec.Cmd) *exec.Cmd {
  453. if deviceID != "" {
  454. // Inject -u device_id after the executable, but before the arguments.
  455. args := []string{cmd.Args[0], "-u", deviceID}
  456. cmd.Args = append(args, cmd.Args[1:]...)
  457. }
  458. return cmd
  459. }
  460. func runSimulator(appdir, bundleID string, args []string) error {
  461. cmd := exec.Command(
  462. "xcrun", "simctl", "launch",
  463. "--wait-for-debugger",
  464. "booted",
  465. bundleID,
  466. )
  467. out, err := cmd.CombinedOutput()
  468. if err != nil {
  469. os.Stderr.Write(out)
  470. return fmt.Errorf("xcrun simctl launch booted %q: %v", bundleID, err)
  471. }
  472. var processID int
  473. var ignore string
  474. if _, err := fmt.Sscanf(string(out), "%s %d", &ignore, &processID); err != nil {
  475. return fmt.Errorf("runSimulator: couldn't find processID from `simctl launch`: %v (%q)", err, out)
  476. }
  477. _, err = runLLDB("ios-simulator", appdir, strconv.Itoa(processID), args)
  478. return err
  479. }
  480. func runDevice(appdir, bundleID string, args []string) error {
  481. attempt := 0
  482. for {
  483. // The device app path reported by the device might be stale, so retry
  484. // the lookup of the device path along with the lldb launching below.
  485. deviceapp, err := findDeviceAppPath(bundleID)
  486. if err != nil {
  487. // The device app path might not yet exist for a newly installed app.
  488. if attempt == 5 {
  489. return err
  490. }
  491. attempt++
  492. time.Sleep(5 * time.Second)
  493. continue
  494. }
  495. out, err := runLLDB("remote-ios", appdir, deviceapp, args)
  496. // If the program was not started it can be retried without papering over
  497. // real test failures.
  498. started := bytes.HasPrefix(out, []byte("lldb: running program"))
  499. if started || err == nil || attempt == 5 {
  500. return err
  501. }
  502. // Sometimes, the app was not yet ready to launch or the device path was
  503. // stale. Retry.
  504. attempt++
  505. time.Sleep(5 * time.Second)
  506. }
  507. }
  508. func runLLDB(target, appdir, deviceapp string, args []string) ([]byte, error) {
  509. var env []string
  510. for _, e := range os.Environ() {
  511. // Don't override TMPDIR, HOME, GOCACHE on the device.
  512. if strings.HasPrefix(e, "TMPDIR=") || strings.HasPrefix(e, "HOME=") || strings.HasPrefix(e, "GOCACHE=") {
  513. continue
  514. }
  515. env = append(env, e)
  516. }
  517. lldb := exec.Command(
  518. "python",
  519. "-", // Read script from stdin.
  520. target,
  521. appdir,
  522. deviceapp,
  523. )
  524. lldb.Args = append(lldb.Args, args...)
  525. lldb.Env = env
  526. lldb.Stdin = strings.NewReader(lldbDriver)
  527. lldb.Stdout = os.Stdout
  528. var out bytes.Buffer
  529. lldb.Stderr = io.MultiWriter(&out, os.Stderr)
  530. err := lldb.Start()
  531. if err == nil {
  532. // Forward SIGQUIT to the lldb driver which in turn will forward
  533. // to the running program.
  534. sigs := make(chan os.Signal, 1)
  535. signal.Notify(sigs, syscall.SIGQUIT)
  536. proc := lldb.Process
  537. go func() {
  538. for sig := range sigs {
  539. proc.Signal(sig)
  540. }
  541. }()
  542. err = lldb.Wait()
  543. signal.Stop(sigs)
  544. close(sigs)
  545. }
  546. return out.Bytes(), err
  547. }
  548. func copyLocalDir(dst, src string) error {
  549. if err := os.Mkdir(dst, 0755); err != nil {
  550. return err
  551. }
  552. d, err := os.Open(src)
  553. if err != nil {
  554. return err
  555. }
  556. defer d.Close()
  557. fi, err := d.Readdir(-1)
  558. if err != nil {
  559. return err
  560. }
  561. for _, f := range fi {
  562. if f.IsDir() {
  563. if f.Name() == "testdata" {
  564. if err := cp(dst, filepath.Join(src, f.Name())); err != nil {
  565. return err
  566. }
  567. }
  568. continue
  569. }
  570. if err := cp(dst, filepath.Join(src, f.Name())); err != nil {
  571. return err
  572. }
  573. }
  574. return nil
  575. }
  576. func cp(dst, src string) error {
  577. out, err := exec.Command("cp", "-a", src, dst).CombinedOutput()
  578. if err != nil {
  579. os.Stderr.Write(out)
  580. }
  581. return err
  582. }
  583. func copyLocalData(dstbase string) (pkgpath string, err error) {
  584. cwd, err := os.Getwd()
  585. if err != nil {
  586. return "", err
  587. }
  588. finalPkgpath, underGoRoot, err := subdir()
  589. if err != nil {
  590. return "", err
  591. }
  592. cwd = strings.TrimSuffix(cwd, finalPkgpath)
  593. // Copy all immediate files and testdata directories between
  594. // the package being tested and the source root.
  595. pkgpath = ""
  596. for _, element := range strings.Split(finalPkgpath, string(filepath.Separator)) {
  597. if debug {
  598. log.Printf("copying %s", pkgpath)
  599. }
  600. pkgpath = filepath.Join(pkgpath, element)
  601. dst := filepath.Join(dstbase, pkgpath)
  602. src := filepath.Join(cwd, pkgpath)
  603. if err := copyLocalDir(dst, src); err != nil {
  604. return "", err
  605. }
  606. }
  607. if underGoRoot {
  608. // Copy timezone file.
  609. //
  610. // Typical apps have the zoneinfo.zip in the root of their app bundle,
  611. // read by the time package as the working directory at initialization.
  612. // As we move the working directory to the GOROOT pkg directory, we
  613. // install the zoneinfo.zip file in the pkgpath.
  614. err := cp(
  615. filepath.Join(dstbase, pkgpath),
  616. filepath.Join(cwd, "lib", "time", "zoneinfo.zip"),
  617. )
  618. if err != nil {
  619. return "", err
  620. }
  621. // Copy src/runtime/textflag.h for (at least) Test386EndToEnd in
  622. // cmd/asm/internal/asm.
  623. runtimePath := filepath.Join(dstbase, "src", "runtime")
  624. if err := os.MkdirAll(runtimePath, 0755); err != nil {
  625. return "", err
  626. }
  627. err = cp(
  628. filepath.Join(runtimePath, "textflag.h"),
  629. filepath.Join(cwd, "src", "runtime", "textflag.h"),
  630. )
  631. if err != nil {
  632. return "", err
  633. }
  634. }
  635. return finalPkgpath, nil
  636. }
  637. // subdir determines the package based on the current working directory,
  638. // and returns the path to the package source relative to $GOROOT (or $GOPATH).
  639. func subdir() (pkgpath string, underGoRoot bool, err error) {
  640. cwd, err := os.Getwd()
  641. if err != nil {
  642. return "", false, err
  643. }
  644. cwd, err = filepath.EvalSymlinks(cwd)
  645. if err != nil {
  646. log.Fatal(err)
  647. }
  648. goroot, err := filepath.EvalSymlinks(runtime.GOROOT())
  649. if err != nil {
  650. return "", false, err
  651. }
  652. if strings.HasPrefix(cwd, goroot) {
  653. subdir, err := filepath.Rel(goroot, cwd)
  654. if err != nil {
  655. return "", false, err
  656. }
  657. return subdir, true, nil
  658. }
  659. for _, p := range filepath.SplitList(build.Default.GOPATH) {
  660. pabs, err := filepath.EvalSymlinks(p)
  661. if err != nil {
  662. return "", false, err
  663. }
  664. if !strings.HasPrefix(cwd, pabs) {
  665. continue
  666. }
  667. subdir, err := filepath.Rel(pabs, cwd)
  668. if err == nil {
  669. return subdir, false, nil
  670. }
  671. }
  672. return "", false, fmt.Errorf(
  673. "working directory %q is not in either GOROOT(%q) or GOPATH(%q)",
  674. cwd,
  675. runtime.GOROOT(),
  676. build.Default.GOPATH,
  677. )
  678. }
  679. func infoPlist(pkgpath string) string {
  680. return `<?xml version="1.0" encoding="UTF-8"?>
  681. <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
  682. <plist version="1.0">
  683. <dict>
  684. <key>CFBundleName</key><string>golang.gotest</string>
  685. <key>CFBundleSupportedPlatforms</key><array><string>iPhoneOS</string></array>
  686. <key>CFBundleExecutable</key><string>gotest</string>
  687. <key>CFBundleVersion</key><string>1.0</string>
  688. <key>CFBundleShortVersionString</key><string>1.0</string>
  689. <key>CFBundleIdentifier</key><string>` + bundleID + `</string>
  690. <key>CFBundleResourceSpecification</key><string>ResourceRules.plist</string>
  691. <key>LSRequiresIPhoneOS</key><true/>
  692. <key>CFBundleDisplayName</key><string>gotest</string>
  693. <key>GoExecWrapperWorkingDirectory</key><string>` + pkgpath + `</string>
  694. </dict>
  695. </plist>
  696. `
  697. }
  698. func entitlementsPlist() string {
  699. return `<?xml version="1.0" encoding="UTF-8"?>
  700. <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
  701. <plist version="1.0">
  702. <dict>
  703. <key>keychain-access-groups</key>
  704. <array><string>` + appID + `</string></array>
  705. <key>get-task-allow</key>
  706. <true/>
  707. <key>application-identifier</key>
  708. <string>` + appID + `</string>
  709. <key>com.apple.developer.team-identifier</key>
  710. <string>` + teamID + `</string>
  711. </dict>
  712. </plist>
  713. `
  714. }
  715. const resourceRules = `<?xml version="1.0" encoding="UTF-8"?>
  716. <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
  717. <plist version="1.0">
  718. <dict>
  719. <key>rules</key>
  720. <dict>
  721. <key>.*</key>
  722. <true/>
  723. <key>Info.plist</key>
  724. <dict>
  725. <key>omit</key>
  726. <true/>
  727. <key>weight</key>
  728. <integer>10</integer>
  729. </dict>
  730. <key>ResourceRules.plist</key>
  731. <dict>
  732. <key>omit</key>
  733. <true/>
  734. <key>weight</key>
  735. <integer>100</integer>
  736. </dict>
  737. </dict>
  738. </dict>
  739. </plist>
  740. `
  741. const lldbDriver = `
  742. import sys
  743. import os
  744. import signal
  745. platform, exe, device_exe_or_pid, args = sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4:]
  746. env = []
  747. for k, v in os.environ.items():
  748. env.append(k + "=" + v)
  749. sys.path.append('/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python')
  750. import lldb
  751. debugger = lldb.SBDebugger.Create()
  752. debugger.SetAsync(True)
  753. debugger.SkipLLDBInitFiles(True)
  754. err = lldb.SBError()
  755. target = debugger.CreateTarget(exe, None, platform, True, err)
  756. if not target.IsValid() or not err.Success():
  757. sys.stderr.write("lldb: failed to setup up target: %s\n" % (err))
  758. sys.exit(1)
  759. listener = debugger.GetListener()
  760. if platform == 'remote-ios':
  761. target.modules[0].SetPlatformFileSpec(lldb.SBFileSpec(device_exe_or_pid))
  762. process = target.ConnectRemote(listener, 'connect://localhost:3222', None, err)
  763. else:
  764. process = target.AttachToProcessWithID(listener, int(device_exe_or_pid), err)
  765. if not err.Success():
  766. sys.stderr.write("lldb: failed to connect to remote target %s: %s\n" % (device_exe_or_pid, err))
  767. sys.exit(1)
  768. # Don't stop on signals.
  769. sigs = process.GetUnixSignals()
  770. for i in range(0, sigs.GetNumSignals()):
  771. sig = sigs.GetSignalAtIndex(i)
  772. sigs.SetShouldStop(sig, False)
  773. sigs.SetShouldNotify(sig, False)
  774. event = lldb.SBEvent()
  775. running = False
  776. prev_handler = None
  777. def signal_handler(signal, frame):
  778. process.Signal(signal)
  779. def run_program():
  780. # Forward SIGQUIT to the program.
  781. prev_handler = signal.signal(signal.SIGQUIT, signal_handler)
  782. # Tell the Go driver that the program is running and should not be retried.
  783. sys.stderr.write("lldb: running program\n")
  784. running = True
  785. # Process is stopped at attach/launch. Let it run.
  786. process.Continue()
  787. if platform != 'remote-ios':
  788. # For the local emulator the program is ready to run.
  789. # For remote device runs, we need to wait for eStateConnected,
  790. # below.
  791. run_program()
  792. while True:
  793. if not listener.WaitForEvent(1, event):
  794. continue
  795. if not lldb.SBProcess.EventIsProcessEvent(event):
  796. continue
  797. if running:
  798. # Pass through stdout and stderr.
  799. while True:
  800. out = process.GetSTDOUT(8192)
  801. if not out:
  802. break
  803. sys.stdout.write(out)
  804. while True:
  805. out = process.GetSTDERR(8192)
  806. if not out:
  807. break
  808. sys.stderr.write(out)
  809. state = process.GetStateFromEvent(event)
  810. if state in [lldb.eStateCrashed, lldb.eStateDetached, lldb.eStateUnloaded, lldb.eStateExited]:
  811. if running:
  812. signal.signal(signal.SIGQUIT, prev_handler)
  813. break
  814. elif state == lldb.eStateConnected:
  815. if platform == 'remote-ios':
  816. process.RemoteLaunch(args, env, None, None, None, None, 0, False, err)
  817. if not err.Success():
  818. sys.stderr.write("lldb: failed to launch remote process: %s\n" % (err))
  819. process.Kill()
  820. debugger.Terminate()
  821. sys.exit(1)
  822. run_program()
  823. exitStatus = process.GetExitStatus()
  824. exitDesc = process.GetExitDescription()
  825. process.Kill()
  826. debugger.Terminate()
  827. if exitStatus == 0 and exitDesc is not None:
  828. # Ensure tests fail when killed by a signal.
  829. exitStatus = 123
  830. sys.exit(exitStatus)
  831. `