dyld-750.5.tar.gz
[apple/dyld.git] / testing / build_ninja.py
1 #!/usr/bin/python2.7
2
3 import plistlib
4 import string
5 import argparse
6 import sys
7 import os
8 import tempfile
9 import shutil
10 import subprocess
11 import re
12 import hashlib
13 import textwrap
14 from string import Template
15
16 class BufferedFile:
17 def __init__(self, fileName):
18 self.data = ""
19 self.fileName = os.path.abspath(fileName)
20 def __enter__(self):
21 return self
22 def __exit__(self, type, value, traceback):
23 if os.path.exists(self.fileName):
24 with open(self.fileName, "r") as file:
25 fileData = file.read()
26 if fileData == self.data: return
27 else:
28 dir = os.path.dirname(self.fileName)
29 if not os.path.exists(dir):
30 os.makedirs(dir)
31 with open(self.fileName, "w") as file:
32 file.write(self.data)
33 def write(self, str):
34 self.data += str
35
36 class NinjaFile:
37 class Variable:
38 def __init__(self, name, value):
39 self.name = name
40 self.value = value
41 def __lt__(self, other):
42 return self.name.__lt__(other.name)
43 def __str__(self):
44 return NinjaFile.lineWrap("{} = {}".format(self.name, self.value))
45
46 class Rule:
47 def __init__(self, name, command, depfile):
48 self.name = name
49 self.command = command
50 self.depfile = depfile
51 def __lt__(self, other):
52 return self.name.__lt__(other.name)
53 def __str__(self):
54 result = NinjaFile.lineWrap("rule {}".format(self.name))
55 if self.command: result += ("\n"+ NinjaFile.lineWrap(" command = {}".format(self.command)))
56 if self.depfile:
57 result += ("\n" + NinjaFile.lineWrap(" deps = gcc"))
58 result += ("\n" + NinjaFile.lineWrap(" depfile = {}".format(self.depfile)))
59 return result
60 class Target:
61 def __init__(self, rule):
62 self.rule = rule
63 self.output = ""
64 self.inputs = []
65 self.variables = []
66 self.dependencies = []
67 def __lt__(self, other):
68 return self.output.__lt__(other.output)
69 def __str__(self):
70 self.inputs.sort()
71 self.variables.sort()
72 self.dependencies.sort()
73 buildLine = "build {}: {}".format(self.output, self.rule)
74 if self.inputs: buildLine += " {}".format(" ".join(self.inputs))
75 if self.dependencies: buildLine += " | {}".format(" ".join(self.dependencies))
76 result = NinjaFile.lineWrap(buildLine)
77 for variable in self.variables: result += ("\n" + NinjaFile.lineWrap(" " + str(variable)))
78 return result
79 def addVariable(self, name, value): self.variables.append(NinjaFile.Variable(name, value))
80 def addDependency(self, dependency):
81 if isinstance(dependency, str):
82 self.dependencies.append(dependency)
83 elif isinstance(dependency, NinjaFile.Target):
84 self.dependencies.append(dependency.output)
85 else:
86 raise ValueError("dependency must be a string or NinjaFile.Target")
87 def addInput(self, input):
88 if isinstance(input, str):
89 self.inputs.append(input)
90 elif isinstance(input, NinjaFile.Target):
91 self.inputs.append(input.output)
92 else:
93 raise ValueError("input must be a string or NinjaFile.Target")
94 class Include:
95 def __init__(self, file):
96 self.file = file
97 def __lt__(self, other):
98 return self.file.__lt__(other.file)
99 def __str__(self):
100 return NinjaFile.lineWrap("include {}".format(self.file))
101
102 def __init__(self, fileName):
103 self.fileName = os.path.abspath(fileName)
104 self.rules = []
105 self.variables = []
106 self.targets = []
107 self.includes = []
108 def __enter__(self):
109 return self
110 def __exit__(self, type, value, traceback):
111 with BufferedFile(self.fileName) as file:
112 file.write(str(self))
113 def addRule(self, name, command, deps): self.rules.append(NinjaFile.Rule(name, command, deps))
114 def addVariable(self, name, value): self.variables.append(NinjaFile.Variable(name, value))
115 def addInclude(self, file): self.includes.append(NinjaFile.Include(file))
116 def newTarget(self, type, name):
117 target = NinjaFile.Target(type)
118 target.output = name
119 self.targets.append(target)
120 return target
121 def findTarget(self, name):
122 #PERF If this gets to be significant we can sort the array and binary search it
123 for target in self.targets:
124 if target.output == name: return target
125 raise ValueError("Target \"{}\" not found".format(name))
126 def deleteTarget(self, name): self.targets.remove(self.findTarget(name))
127 def __str__(self):
128 self.variables.sort()
129 self.rules.sort()
130 self.targets.sort()
131 self.includes.sort()
132 subs = {
133 "VARIABLES": "\n".join(map(str, self.variables)),
134 "RULES": "\n\n".join(map(str, self.rules)),
135 "TARGETS": "\n\n".join(map(str, self.targets)),
136 "INCLUDES": "\n\n".join(map(str, self.includes))
137 }
138 return string.Template(
139 """ninja_required_version = 1.6
140
141 $INCLUDES
142
143 $VARIABLES
144
145 $RULES
146
147 $TARGETS
148
149 """).safe_substitute(subs)
150 # wrapper = textwrap.TextWrapper(width = 130, subsequent_indent = " ", break_long_words = False)
151 @classmethod
152 def lineWrap(cls, line):
153 if len(line) <= 132: return line
154 result = ""
155 currentIdx = 0
156 wrappedLineLeadingSpace = " "
157 firstLineIndent = 0
158 if line[0].isspace():
159 firstLineIndent = 4
160 result = " "
161 wrappedLineLeadingSpace = " "
162 trailer = " $"
163 wrappedLineLeadingSpaceLen = len(wrappedLineLeadingSpace)
164 lineSpaceAvailable = 132-(firstLineIndent+wrappedLineLeadingSpaceLen)
165 words = line.split()
166 wordsCount = len(words)-1
167 for idx, word in enumerate(words):
168 wordLen = len(word)
169 if (wordLen <= lineSpaceAvailable and idx == wordsCount):
170 result += word
171 elif wordLen <= lineSpaceAvailable+2:
172 result += "{} ".format(word)
173 lineSpaceAvailable -= (wordLen)
174 else:
175 result += "$\n{}{} ".format(wrappedLineLeadingSpace, word)
176 lineSpaceAvailable = 132-(wrappedLineLeadingSpaceLen+wordLen)
177 return result
178
179 def processBuildLines(ninja, buildLines, testName, macOSBuild, minOS, testDstDir, testSrcDir):
180 testInstallTarget = ninja.newTarget("phony", "install-{}".format(testName))
181 testTarget = ninja.newTarget("phony", testName)
182 ninja.findTarget("all").addInput(testTarget)
183 ninja.findTarget("install").addInput(testInstallTarget)
184 for buildLine in buildLines:
185 args = buildLine.split()
186 if args[0] == "$DTRACE":
187 target = None
188 for idx, arg in enumerate(args):
189 if arg == "-o": target = ninja.newTarget("dtrace", args[idx+1])
190 for idx, arg in enumerate(args):
191 if arg == "-s": target.addInput(testSrcDir + "/" + args[idx+1])
192 elif args[0] == "$CP":
193 target = ninja.newTarget("cp", args[2])
194 target.addInput(testSrcDir + "/" + args[1])
195 testTarget.addInput(target)
196 installTarget = ninja.newTarget("install", "$INSTALL_DIR/AppleInternal/CoreOS/tests/dyld/{}".format(args[2][9:]))
197 installTarget.addInput(target.output)
198 testInstallTarget.addInput(installTarget)
199 elif args[0] == "$SYMLINK":
200 target = ninja.newTarget("symlink", args[2])
201 target.addVariable("source", args[1])
202 testTarget.addInput(target)
203 installTarget = ninja.newTarget("symlink", "$INSTALL_DIR/AppleInternal/CoreOS/tests/dyld/{}".format(args[2][9:]))
204 installTarget.addVariable("source", args[1])
205 testInstallTarget.addInput(installTarget)
206 elif args[0] == "$STRIP":
207 target = ninja.findTarget(args[1])
208 target.addVariable("extraCmds", "&& strip {}".format(target.output))
209 elif args[0] == "$SKIP_INSTALL":
210 target = "$INSTALL_DIR/AppleInternal/CoreOS/tests/dyld/{}".format(args[1][9:])
211 ninja.deleteTarget(target)
212 testInstallTarget.inputs.remove(target)
213 elif args[0] == "$DYLD_ENV_VARS_ENABLE":
214 if not macOSBuild:
215 target = ninja.findTarget(args[1])
216 target.addVariable("entitlements", "--entitlements $SRCROOT/testing/get_task_allow_entitlement.plist")
217 elif args[0] == "$TASK_FOR_PID_ENABLE":
218 if not macOSBuild:
219 target = ninja.findTarget(args[1])
220 target.addVariable("entitlements", "--entitlements $SRCROOT/testing/task_for_pid_entitlement.plist")
221 elif args[0] in ["$CC", "$CXX"]:
222 tool = args[0][1:].lower()
223 sources = []
224 cflags = []
225 ldflags = []
226 dependencies = []
227 skipCount = 0
228 linkTarget = None
229 isMainExecutable = True
230 targetNames = [target.output for target in ninja.targets]
231 args = [escapedArg.replace("\"", "\\\"") for escapedArg in args[1:]]
232 #First find the target
233 for idx, arg in enumerate(args):
234 if arg == "-o":
235 linkTarget = ninja.newTarget("{}-link".format(tool), args[idx+1])
236 linkTarget.addDependency("$BUILT_PRODUCTS_DIR/libtest_support.a")
237 testTarget.addInput(linkTarget);
238 break
239 skipCount = 0
240 for idx, arg in enumerate(args):
241 if skipCount: skipCount -= 1
242 elif arg == "-o":
243 skipCount = 1
244 elif arg == "$DEPENDS_ON":
245 skipCount = 1
246 dependencies.append(args[idx+1])
247 elif arg in ["-arch"]:
248 skipCount = 1
249 nextArg = args[idx+1]
250 ldflags.append(arg)
251 ldflags.append(nextArg)
252 cflags.append(arg)
253 cflags.append(nextArg)
254 elif arg in ["-install_name","-framework", "-rpath","-compatibility_version","-sub_library", "-undefined", "-current_version"]:
255 skipCount = 1
256 nextArg = args[idx+1]
257 ldflags.append(arg)
258 ldflags.append(nextArg)
259 elif arg == "-sectcreate":
260 skipCount = 3
261 ldflags.append(arg)
262 ldflags.append(args[idx+1])
263 ldflags.append(args[idx+2])
264 ldflags.append(args[idx+3])
265 elif arg[:2] == "-L": ldflags.append(arg)
266 elif arg in ["-nostdlib", "-flat_namespace"]: ldflags.append(arg)
267 elif arg in ["-dynamiclib","-bundle"]:
268 ldflags.append(arg)
269 isMainExecutable = False
270 elif arg.endswith((".s", ".c", ".cpp", ".cxx", ".m", ".mm")):
271 sources.append(testSrcDir + "/" +arg)
272 elif arg in targetNames:
273 linkTarget.addInput(arg)
274 elif arg[:4] == "-Wl,":
275 linkerArgs = arg.split(",")
276 for linkerArg in linkerArgs:
277 if linkerArg in targetNames: linkTarget.addDependency(linkerArg)
278 ldflags.append(arg)
279 elif arg[:2] == "-l":
280 candidate = "{}/lib{}.dylib".format(testDstDir, arg[2:])
281 if candidate in targetNames: linkTarget.addDependency(candidate)
282 ldflags.append(arg)
283 elif arg[:7] == "-weak-l":
284 candidate = "{}/lib{}.dylib".format(testDstDir, arg[7:])
285 if candidate in targetNames: linkTarget.addDependency(candidate)
286 ldflags.append(arg)
287 elif arg[:9] == "-upward-l":
288 candidate = "{}/lib{}.dylib".format(testDstDir, arg[9:])
289 if candidate in targetNames: linkTarget.addDependency(candidate)
290 ldflags.append(arg)
291 else:
292 cflags.append(arg)
293 if isMainExecutable:
294 ldflags.append("-force_load $BUILT_PRODUCTS_DIR/libtest_support.a")
295 for source in sources:
296 objectHash = hashlib.sha1(linkTarget.output+source+tool+"".join(cflags)).hexdigest()
297 target = ninja.newTarget(tool, "$OBJROOT/dyld_tests.build/Objects-normal/" + objectHash + ".o")
298 target.addInput(source)
299 target.dependencies = dependencies
300 if cflags: target.addVariable("cflags", " ".join(cflags))
301 if minOS: target.addVariable("minOS", minOS)
302 linkTarget.addInput(target)
303 if ldflags: linkTarget.addVariable("ldflags", " ".join(ldflags))
304 if minOS: linkTarget.addVariable("minOS", minOS)
305 installTarget = ninja.newTarget("install", "$INSTALL_DIR/AppleInternal/CoreOS/tests/dyld/{}".format(linkTarget.output[9:]))
306 installTarget.addInput(linkTarget)
307 testTarget.addInput(linkTarget)
308 testInstallTarget.addInput(installTarget)
309 else: raise ValueError("Unknown Command: {}".format(args[0]))
310
311 def processRunLines(ninja, runLines, testName, macOSBuild, symRoot, xcTestInvocations):
312 runFilePath = "{}/{}/run.sh".format(symRoot, testName)
313 for runLine in runLines:
314 xcTestInvocations.append("{{ \"{}\", \"{}\" }}".format(testName, runLine.replace("\"","\\\"").replace("sudo","")))
315 with BufferedFile(runFilePath) as runFile:
316 runFile.write("#!/bin/sh\n")
317 runFile.write("cd {}\n".format(testRunDir))
318
319 runFile.write("echo \"run in dyld2 mode\" \n");
320 for runLine in runLines:
321 runFile.write("TEST_OUTPUT=BATS TEST_DYLD_MODE=2 DYLD_USE_CLOSURES=0 {}\n".format(runLine))
322
323 if macOSBuild:
324 runFile.write("echo \"run in dyld2 mode with no shared cache\" \n");
325 for runLine in runLines:
326 runFile.write("TEST_OUTPUT=BATS TEST_DYLD_MODE=2 DYLD_SHARED_REGION=avoid {}\n".format(runLine))
327
328 runFile.write("echo \"run in dyld3 mode\" \n");
329 for runLine in runLines:
330 if runLine.startswith("sudo "):
331 runFile.write("sudo TEST_OUTPUT=BATS TEST_DYLD_MODE=3 DYLD_USE_CLOSURES=1 {}\n".format(runline[5:]))
332 else:
333 runFile.write("TEST_OUTPUT=BATS TEST_DYLD_MODE=3 DYLD_USE_CLOSURES=1 {}\n".format(runLine))
334
335 if macOSBuild:
336 runFile.write("echo \"run in dyld3 mode with no shared cache\" \n");
337 for runLine in runLines:
338 if runLine.startswith("sudo "):
339 runFile.write("sudo TEST_OUTPUT=BATS TEST_DYLD_MODE=3 DYLD_SHARED_REGION=avoid DYLD_USE_CLOSURES=1 {}\n".format(runline[5:]))
340 else:
341 runFile.write("TEST_OUTPUT=BATS TEST_DYLD_MODE=3 DYLD_SHARED_REGION=avoid DYLD_USE_CLOSURES=1 {}\n".format(runLine))
342 os.chmod(runFilePath, 0755)
343 installPath = "$INSTALL_DIR/AppleInternal/CoreOS/tests/dyld/{}/run.sh".format(testName)
344 target = ninja.newTarget("install", installPath)
345 target.addInput(runFilePath)
346 ninja.findTarget("install-{}".format(testName)).addInput(installPath)
347
348
349 if __name__ == "__main__":
350 configPath = sys.argv[1]
351 configMap = {}
352 with open(configPath) as configFile:
353 for line in configFile.read().splitlines():
354 args = line.split()
355 configMap[args[0]] = args[2:]
356 sys.stderr.write("CONFIG: {}\n".format(configMap))
357 srcRoot = configMap["SRCROOT"][0]
358 symRoot = configMap["SYMROOT"][0]
359 sdkRoot = configMap["SDKROOT"][0]
360 objRoot = configMap["OBJROOT"][0]
361 osFlag = configMap["OSFLAG"][0]
362 osVers = configMap["OSVERSION"][0]
363 linkerFlags = configMap["LDFLAGS"][0];
364 installOwner = configMap["INSTALL_OWNER"][0];
365 installGroup = configMap["INSTALL_GROUP"][0];
366 installMode = configMap["INSTALL_MODE_FLAG"][0];
367 installDir = configMap["INSTALL_DIR"][0];
368 userHeaderSearchPaths = configMap["USER_HEADER_SEARCH_PATHS"]
369 systemHeaderSearchPaths = configMap["SYSTEM_HEADER_SEARCH_PATHS"]
370
371 derivedFilesDir = configMap["DERIVED_FILES_DIR"][0]
372 archs = configMap["ARCHS"]
373
374 if not os.path.exists(derivedFilesDir): os.makedirs(derivedFilesDir)
375 if not os.path.exists(objRoot): os.makedirs(objRoot)
376
377 sys.stderr.write("srcRoot = {}\n".format(srcRoot))
378 sys.stderr.write("sdkRoot = {}\n".format(sdkRoot))
379 sys.stderr.write("objRoot = {}\n".format(objRoot))
380 sys.stderr.write("osFlag = {}\n".format(osFlag))
381 sys.stderr.write("osVers = {}\n".format(osVers))
382 sys.stderr.write("archs = {}\n".format(archs))
383 sys.stderr.write("derivedFilesDir = {}\n".format(derivedFilesDir))
384
385 testSrcRoot = os.path.abspath(srcRoot + "/testing/test-cases")
386 ccTool = os.popen("xcrun --sdk " + sdkRoot + " --find clang").read().rstrip()
387 cxxTool = os.popen("xcrun --sdk " + sdkRoot + " --find clang++").read().rstrip()
388 headerPaths = " -isysroot " + sdkRoot
389 for headerPath in userHeaderSearchPaths: headerPaths += " -I{}".format(headerPath)
390 for headerPath in systemHeaderSearchPaths: headerPaths += " -I{}".format(headerPath)
391 macOSBuild = False
392 sudoCmd = ""
393 if osFlag == "mmacosx-version-min":
394 macOSBuild = True
395 sudoCmd = "sudo"
396
397 with NinjaFile(derivedFilesDir + "/build.ninja") as ninja:
398 ninja.addInclude("config.ninja")
399 ninja.addVariable("minOS", "-" + osFlag + "=" + osVers)
400 ninja.addVariable("archs", " ".join(["-arch {}".format(arch) for arch in archs]))
401 ninja.addVariable("mode", "0755")
402 ninja.addVariable("headerpaths", headerPaths)
403
404 ninja.addRule("cc", "{} -g -MMD -MF $out.d $archs -o $out -c $in $minOS $headerpaths $cflags".format(ccTool), "$out.d")
405 ninja.addRule("cxx", "{} -g -MMD -MF $out.d $archs -o $out -c $in $minOS $headerpaths $cflags".format(cxxTool), "$out.d")
406 ninja.addRule("cc-link", "{} -g $archs -o $out -ltest_support $in $minOS -isysroot {} {} $ldflags && dsymutil -o $out.dSYM $out $extraCmds && codesign --force --sign - $entitlements $out".format(ccTool, sdkRoot, linkerFlags), False)
407 ninja.addRule("cxx-link", "{} -g $archs -o $out -ltest_support $in $minOS -isysroot {} {} $ldflags && dsymutil -o $out.dSYM $out $extraCmds && codesign --force --sign - $entitlements $out".format(cxxTool, sdkRoot, linkerFlags), False)
408 ninja.addRule("dtrace", "/usr/sbin/dtrace -h -s $in -o $out", False)
409 ninja.addRule("cp", "/bin/cp -p $in $out", False)
410 ninja.addRule("install", "/usr/bin/install -m $mode -o {} -g {} $install_flags $in $out".format(installOwner, installGroup), False)
411 ninja.addRule("symlink", "ln -sfh $source $out", False)
412
413 allTarget = ninja.newTarget("phony", "all")
414 masterInstallTarget = ninja.newTarget("phony", "install")
415
416 runAllScriptPath = "{}/run_all_dyld_tests.sh".format(derivedFilesDir)
417 xctestPath = "{}/dyld_xctest.h".format(derivedFilesDir)
418 if "XCTestGenPath" in os.environ: xctestPath = os.environ["XCTestGenPath"]
419 batsTests = []
420 batsSuppressedCrashes = []
421 xctestInvocations = []
422 with BufferedFile(runAllScriptPath) as runAllScript:
423 runAllScript.write("#!/bin/sh\n")
424 for entry in os.listdir(testSrcRoot):
425 if entry.endswith((".dtest")):
426 testName = entry[:-6]
427 sys.stdout.write("Processing " + testName + "\n")
428 runLines = []
429 buildLines = []
430 minOS = None
431
432 for file in os.listdir(testSrcRoot + "/" + entry):
433 testSrcDir = "$SRCROOT/testing/test-cases/{}.dtest".format(testName)
434 testDstDir = "$SYMROOT/{}".format(testName)
435 testRunDir = "/AppleInternal/CoreOS/tests/dyld/{}".format(testName)
436 buildSubs = {
437 "BUILD_DIR": testDstDir,
438 "RUN_DIR": testRunDir,
439 "SRC_DIR": testSrcDir
440 }
441 runSubs = {
442 "RUN_DIR": testRunDir,
443 "SUDO": sudoCmd,
444 }
445 batsTest = {}
446 batsTest["TestName"] = testName
447 batsTest["Arch"] = "platform-native"
448 batsTest["WorkingDirectory"] = testRunDir
449 batsTest["ShowSubtestResults"] = True
450 batsTest["Command"] = []
451 batsTest["Command"].append("./run.sh")
452 if file.endswith((".c", ".cpp", ".cxx", ".m", ".mm")):
453 with open(testSrcRoot + "/" + entry + "/" + file) as f:
454 for line in f.read().splitlines():
455 idx = string.find(line,"BUILD_ONLY:")
456 if idx != -1:
457 skippedOS = line[idx+11:].lstrip()
458 if skippedOS == "MacOSX" and not macOSBuild: break
459 else: continue
460 idx = string.find(line,"BUILD_MIN_OS:")
461 if idx != -1:
462 minOS = "-" + osFlag + "=" + line[idx+13:].lstrip()
463 idx = string.find(line,"BUILD:")
464 if idx != -1:
465 buildLines.append(string.Template(line[idx+6:]).safe_substitute(buildSubs))
466 continue
467 idx = string.find(line,"RUN:")
468 if idx != -1:
469 if "$SUDO" in line: batsTest["AsRoot"] = True
470 runLines.append(string.Template(line[idx+4:]).safe_substitute(runSubs))
471 continue
472 idx = string.find(line,"RUN_TIMEOUT:")
473 if idx != -1:
474 batsTest["Timeout"] = line[idx+12:].lstrip()
475 continue
476 idx = string.find(line,"BOOT_ARGS:")
477 if idx != -1:
478 batsTest["BootArgsSet"] = ",".join(line[idx+9:].split())
479 continue
480 idx = string.find(line,"NO_CRASH_LOG:")
481 if idx != -1:
482 batsSuppressedCrashes.append(line[idx+13:].lstrip())
483 continue
484 if buildLines and runLines:
485 processBuildLines(ninja, buildLines, testName, macOSBuild, minOS, testDstDir, testSrcDir)
486 processRunLines(ninja, runLines, testName, macOSBuild, symRoot, xctestInvocations)
487 runAllScript.write("/AppleInternal/CoreOS/tests/dyld/{}/run.sh\n".format(testName))
488 batsTests.append(batsTest)
489 sys.stderr.write("Wrote test config to: {}".format(xctestPath))
490 with BufferedFile(xctestPath) as xcTestFile:
491 xcTestFile.write("static const TestInfo sTests[] = {\n")
492 xcTestFile.write(",\n".join(xctestInvocations))
493 xcTestFile.write("\n};")
494 os.chmod(runAllScriptPath, 0755)
495 runAllFilesInstallTarget = ninja.newTarget("install", "$INSTALL_DIR/AppleInternal/CoreOS/tests/dyld/run_all_dyld_tests.sh")
496 runAllFilesInstallTarget.addInput("$DERIVED_FILES_DIR/run_all_dyld_tests.sh")
497 masterInstallTarget.addInput(runAllFilesInstallTarget)
498 batsFilePath = derivedFilesDir + "/dyld.plist"
499 batsTests.sort(key=lambda test: test["TestName"])
500 with BufferedFile(batsFilePath) as batsFile:
501 batsConfig = { "BATSConfigVersion": "0.1.0",
502 "Project": "dyld_tests",
503 "Tests": batsTests }
504 if batsSuppressedCrashes: batsConfig["IgnoreCrashes"] = batsSuppressedCrashes
505 batsFile.write(plistlib.writePlistToString(batsConfig))
506 os.system('plutil -convert binary1 ' + batsFilePath) # convert the plist in place to binary
507 batsConfigInstallTarget = ninja.newTarget("install", "$INSTALL_DIR/AppleInternal/CoreOS/BATS/unit_tests/dyld.plist")
508 batsConfigInstallTarget.addInput(batsFilePath)
509 batsConfigInstallTarget.addVariable("mode", "0644")
510 masterInstallTarget.addInput(batsConfigInstallTarget)
511 sys.stdout.write("DONE\n")
512