Defer inside loop; deferred calls accumulate until the function returns, not until the loop iteration ends. This can cause resource leaks
defer d.Close()
1// Copyright 2024 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.45// This program can be used as go_ios_$GOARCH_exec by the Go tool. It executes6// binaries on the iOS Simulator using the XCode toolchain.7package main89import (10 "fmt"11 "go/build"12 "log"13 "os"14 "os/exec"15 "path/filepath"16 "runtime"17 "strings"18 "syscall"19)2021const debug = false2223var tmpdir string2425var (26 devID string27 appID string28 teamID string29 bundleID string30 deviceID string31)3233// lock is a file lock to serialize iOS runs. It is global to avoid the34// garbage collector finalizing it, closing the file and releasing the35// lock prematurely.36var lock *os.File3738func main() {39 log.SetFlags(0)40 log.SetPrefix("go_ios_exec: ")41 if debug {42 log.Println(strings.Join(os.Args, " "))43 }44 if len(os.Args) < 2 {45 log.Fatal("usage: go_ios_exec a.out")46 }4748 // For compatibility with the old builders, use a fallback bundle ID49 bundleID = "golang.gotest"5051 exitCode, err := runMain()52 if err != nil {53 log.Fatalf("%v\n", err)54 }55 os.Exit(exitCode)56}5758func runMain() (int, error) {59 var err error60 tmpdir, err = os.MkdirTemp("", "go_ios_exec_")61 if err != nil {62 return 1, err63 }64 if !debug {65 defer os.RemoveAll(tmpdir)66 }6768 appdir := filepath.Join(tmpdir, "gotest.app")69 os.RemoveAll(appdir)7071 if err := assembleApp(appdir, os.Args[1]); err != nil {72 return 1, err73 }7475 // This wrapper uses complicated machinery to run iOS binaries. It76 // works, but only when running one binary at a time.77 // Use a file lock to make sure only one wrapper is running at a time.78 //79 // The lock file is never deleted, to avoid concurrent locks on distinct80 // files with the same path.81 lockName := filepath.Join(os.TempDir(), "go_ios_exec-"+deviceID+".lock")82 lock, err = os.OpenFile(lockName, os.O_CREATE|os.O_RDONLY, 0666)83 if err != nil {84 return 1, err85 }86 if err := syscall.Flock(int(lock.Fd()), syscall.LOCK_EX); err != nil {87 return 1, err88 }8990 err = runOnSimulator(appdir)91 if err != nil {92 return 1, err93 }94 return 0, nil95}9697func runOnSimulator(appdir string) error {98 if err := installSimulator(appdir); err != nil {99 return err100 }101102 return runSimulator(appdir, bundleID, os.Args[2:])103}104105func assembleApp(appdir, bin string) error {106 if err := os.MkdirAll(appdir, 0755); err != nil {107 return err108 }109110 if err := cp(filepath.Join(appdir, "gotest"), bin); err != nil {111 return err112 }113114 pkgpath, err := copyLocalData(appdir)115 if err != nil {116 return err117 }118119 entitlementsPath := filepath.Join(tmpdir, "Entitlements.plist")120 if err := os.WriteFile(entitlementsPath, []byte(entitlementsPlist()), 0744); err != nil {121 return err122 }123 if err := os.WriteFile(filepath.Join(appdir, "Info.plist"), []byte(infoPlist(pkgpath)), 0744); err != nil {124 return err125 }126 if err := os.WriteFile(filepath.Join(appdir, "ResourceRules.plist"), []byte(resourceRules), 0744); err != nil {127 return err128 }129 return nil130}131132func installSimulator(appdir string) error {133 cmd := exec.Command(134 "xcrun", "simctl", "install",135 "booted", // Install to the booted simulator.136 appdir,137 )138 if out, err := cmd.CombinedOutput(); err != nil {139 os.Stderr.Write(out)140 return fmt.Errorf("xcrun simctl install booted %q: %v", appdir, err)141 }142 return nil143}144145func runSimulator(appdir, bundleID string, args []string) error {146 xcrunArgs := []string{"simctl", "spawn",147 "booted",148 appdir + "/gotest",149 }150 xcrunArgs = append(xcrunArgs, args...)151 cmd := exec.Command("xcrun", xcrunArgs...)152 cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr153 err := cmd.Run()154 if err != nil {155 return fmt.Errorf("xcrun simctl launch booted %q: %v", bundleID, err)156 }157158 return nil159}160161func copyLocalDir(dst, src string) error {162 if err := os.Mkdir(dst, 0755); err != nil {163 return err164 }165166 d, err := os.Open(src)167 if err != nil {168 return err169 }170 defer d.Close()171 fi, err := d.Readdir(-1)172 if err != nil {173 return err174 }175176 for _, f := range fi {177 if f.IsDir() {178 if f.Name() == "testdata" {179 if err := cp(dst, filepath.Join(src, f.Name())); err != nil {180 return err181 }182 }183 continue184 }185 if err := cp(dst, filepath.Join(src, f.Name())); err != nil {186 return err187 }188 }189 return nil190}191192func cp(dst, src string) error {193 out, err := exec.Command("cp", "-a", src, dst).CombinedOutput()194 if err != nil {195 os.Stderr.Write(out)196 }197 return err198}199200func copyLocalData(dstbase string) (pkgpath string, err error) {201 cwd, err := os.Getwd()202 if err != nil {203 return "", err204 }205206 finalPkgpath, underGoRoot, err := subdir()207 if err != nil {208 return "", err209 }210 cwd = strings.TrimSuffix(cwd, finalPkgpath)211212 // Copy all immediate files and testdata directories between213 // the package being tested and the source root.214 pkgpath = ""215 for element := range strings.SplitSeq(finalPkgpath, string(filepath.Separator)) {216 if debug {217 log.Printf("copying %s", pkgpath)218 }219 pkgpath = filepath.Join(pkgpath, element)220 dst := filepath.Join(dstbase, pkgpath)221 src := filepath.Join(cwd, pkgpath)222 if err := copyLocalDir(dst, src); err != nil {223 return "", err224 }225 }226227 if underGoRoot {228 // Copy timezone file.229 //230 // Typical apps have the zoneinfo.zip in the root of their app bundle,231 // read by the time package as the working directory at initialization.232 // As we move the working directory to the GOROOT pkg directory, we233 // install the zoneinfo.zip file in the pkgpath.234 err := cp(235 filepath.Join(dstbase, pkgpath),236 filepath.Join(cwd, "lib", "time", "zoneinfo.zip"),237 )238 if err != nil {239 return "", err240 }241 // Copy src/runtime/textflag.h for (at least) Test386EndToEnd in242 // cmd/asm/internal/asm.243 runtimePath := filepath.Join(dstbase, "src", "runtime")244 if err := os.MkdirAll(runtimePath, 0755); err != nil {245 return "", err246 }247 err = cp(248 filepath.Join(runtimePath, "textflag.h"),249 filepath.Join(cwd, "src", "runtime", "textflag.h"),250 )251 if err != nil {252 return "", err253 }254 }255256 return finalPkgpath, nil257}258259// subdir determines the package based on the current working directory,260// and returns the path to the package source relative to $GOROOT (or $GOPATH).261func subdir() (pkgpath string, underGoRoot bool, err error) {262 cwd, err := os.Getwd()263 if err != nil {264 return "", false, err265 }266 cwd, err = filepath.EvalSymlinks(cwd)267 if err != nil {268 log.Fatal(err)269 }270 goroot, err := filepath.EvalSymlinks(runtime.GOROOT())271 if err != nil {272 return "", false, err273 }274 if strings.HasPrefix(cwd, goroot) {275 subdir, err := filepath.Rel(goroot, cwd)276 if err != nil {277 return "", false, err278 }279 return subdir, true, nil280 }281282 for _, p := range filepath.SplitList(build.Default.GOPATH) {283 pabs, err := filepath.EvalSymlinks(p)284 if err != nil {285 return "", false, err286 }287 if !strings.HasPrefix(cwd, pabs) {288 continue289 }290 subdir, err := filepath.Rel(pabs, cwd)291 if err == nil {292 return subdir, false, nil293 }294 }295 return "", false, fmt.Errorf(296 "working directory %q is not in either GOROOT(%q) or GOPATH(%q)",297 cwd,298 runtime.GOROOT(),299 build.Default.GOPATH,300 )301}302303func infoPlist(pkgpath string) string {304 return `<?xml version="1.0" encoding="UTF-8"?>305<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">306<plist version="1.0">307<dict>308<key>CFBundleName</key><string>golang.gotest</string>309<key>CFBundleSupportedPlatforms</key><array><string>iPhoneOS</string></array>310<key>CFBundleExecutable</key><string>gotest</string>311<key>CFBundleVersion</key><string>1.0</string>312<key>CFBundleShortVersionString</key><string>1.0</string>313<key>CFBundleIdentifier</key><string>` + bundleID + `</string>314<key>CFBundleResourceSpecification</key><string>ResourceRules.plist</string>315<key>LSRequiresIPhoneOS</key><true/>316<key>CFBundleDisplayName</key><string>gotest</string>317<key>GoExecWrapperWorkingDirectory</key><string>` + pkgpath + `</string>318</dict>319</plist>320`321}322323func entitlementsPlist() string {324 return `<?xml version="1.0" encoding="UTF-8"?>325<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">326<plist version="1.0">327<dict>328 <key>keychain-access-groups</key>329 <array><string>` + appID + `</string></array>330 <key>get-task-allow</key>331 <true/>332 <key>application-identifier</key>333 <string>` + appID + `</string>334 <key>com.apple.developer.team-identifier</key>335 <string>` + teamID + `</string>336</dict>337</plist>338`339}340341const resourceRules = `<?xml version="1.0" encoding="UTF-8"?>342<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">343<plist version="1.0">344<dict>345 <key>rules</key>346 <dict>347 <key>.*</key>348 <true/>349 <key>Info.plist</key>350 <dict>351 <key>omit</key>352 <true/>353 <key>weight</key>354 <integer>10</integer>355 </dict>356 <key>ResourceRules.plist</key>357 <dict>358 <key>omit</key>359 <true/>360 <key>weight</key>361 <integer>100</integer>362 </dict>363 </dict>364</dict>365</plist>366`
Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.