14 from string
import Template
17 def __init__(self
, fileName
):
19 self
.fileName
= os
.path
.abspath(fileName
)
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
28 dir = os
.path
.dirname(self
.fileName
)
29 if not os
.path
.exists(dir):
31 with open(self
.fileName
, "w") as file:
38 def __init__(self
, name
, value
):
41 def __lt__(self
, other
):
42 return self
.name
.__lt
__(other
.name
)
44 return NinjaFile
.lineWrap("{} = {}".format(self
.name
, self
.value
))
47 def __init__(self
, name
, command
, depfile
):
49 self
.command
= command
50 self
.depfile
= depfile
51 def __lt__(self
, other
):
52 return self
.name
.__lt
__(other
.name
)
54 result
= NinjaFile
.lineWrap("rule {}".format(self
.name
))
55 if self
.command
: result
+= ("\n"+ NinjaFile
.lineWrap(" command = {}".format(self
.command
)))
57 result
+= ("\n" + NinjaFile
.lineWrap(" deps = gcc"))
58 result
+= ("\n" + NinjaFile
.lineWrap(" depfile = {}".format(self
.depfile
)))
61 def __init__(self
, rule
):
66 self
.dependencies
= []
67 def __lt__(self
, other
):
68 return self
.output
.__lt
__(other
.output
)
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
)))
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
)
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
)
93 raise ValueError("input must be a string or NinjaFile.Target")
95 def __init__(self
, file):
97 def __lt__(self
, other
):
98 return self
.file.__lt
__(other
.file)
100 return NinjaFile
.lineWrap("include {}".format(self
.file))
102 def __init__(self
, fileName
):
103 self
.fileName
= os
.path
.abspath(fileName
)
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)
119 self
.targets
.append(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
))
128 self
.variables
.sort()
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
))
138 return string
.Template(
139 """ninja_required_version = 1.6
149 """).safe_substitute(subs
)
150 # wrapper = textwrap.TextWrapper(width = 130, subsequent_indent = " ", break_long_words = False)
152 def lineWrap(cls
, line
):
153 if len(line
) <= 132: return line
156 wrappedLineLeadingSpace
= " "
158 if line
[0].isspace():
161 wrappedLineLeadingSpace
= " "
163 wrappedLineLeadingSpaceLen
= len(wrappedLineLeadingSpace
)
164 lineSpaceAvailable
= 132-(firstLineIndent
+wrappedLineLeadingSpaceLen
)
166 wordsCount
= len(words
)-1
167 for idx
, word
in enumerate(words
):
169 if (wordLen
<= lineSpaceAvailable
and idx
== wordsCount
):
171 elif wordLen
<= lineSpaceAvailable
+2:
172 result
+= "{} ".format(word
)
173 lineSpaceAvailable
-= (wordLen
)
175 result
+= "$\n{}{} ".format(wrappedLineLeadingSpace
, word
)
176 lineSpaceAvailable
= 132-(wrappedLineLeadingSpaceLen
+wordLen
)
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":
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":
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":
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()
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
):
235 linkTarget
= ninja
.newTarget("{}-link".format(tool
), args
[idx
+1])
236 linkTarget
.addDependency("$BUILT_PRODUCTS_DIR/libtest_support.a")
237 testTarget
.addInput(linkTarget
);
240 for idx
, arg
in enumerate(args
):
241 if skipCount
: skipCount
-= 1
244 elif arg
== "$DEPENDS_ON":
246 dependencies
.append(args
[idx
+1])
247 elif arg
in ["-arch"]:
249 nextArg
= args
[idx
+1]
251 ldflags
.append(nextArg
)
253 cflags
.append(nextArg
)
254 elif arg
in ["-install_name","-framework", "-rpath","-compatibility_version","-sub_library", "-undefined", "-current_version"]:
256 nextArg
= args
[idx
+1]
258 ldflags
.append(nextArg
)
259 elif arg
== "-sectcreate":
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"]:
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
)
279 elif arg
[:2] == "-l":
280 candidate
= "{}/lib{}.dylib".format(testDstDir
, arg
[2:])
281 if candidate
in targetNames
: linkTarget
.addDependency(candidate
)
283 elif arg
[:7] == "-weak-l":
284 candidate
= "{}/lib{}.dylib".format(testDstDir
, arg
[7:])
285 if candidate
in targetNames
: linkTarget
.addDependency(candidate
)
287 elif arg
[:9] == "-upward-l":
288 candidate
= "{}/lib{}.dylib".format(testDstDir
, arg
[9:])
289 if candidate
in targetNames
: linkTarget
.addDependency(candidate
)
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]))
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
))
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
))
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
))
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:]))
333 runFile
.write("TEST_OUTPUT=BATS TEST_DYLD_MODE=3 DYLD_USE_CLOSURES=1 {}\n".format(runLine
))
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:]))
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
)
349 if __name__
== "__main__":
350 configPath
= sys
.argv
[1]
352 with open(configPath
) as configFile
:
353 for line
in configFile
.read().splitlines():
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"]
371 derivedFilesDir
= configMap
["DERIVED_FILES_DIR"][0]
372 archs
= configMap
["ARCHS"]
374 if not os
.path
.exists(derivedFilesDir
): os
.makedirs(derivedFilesDir
)
375 if not os
.path
.exists(objRoot
): os
.makedirs(objRoot
)
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
))
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
)
393 if osFlag
== "mmacosx-version-min":
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
)
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)
413 allTarget
= ninja
.newTarget("phony", "all")
414 masterInstallTarget
= ninja
.newTarget("phony", "install")
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"]
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")
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
)
437 "BUILD_DIR": testDstDir
,
438 "RUN_DIR": testRunDir
,
439 "SRC_DIR": testSrcDir
442 "RUN_DIR": testRunDir
,
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:")
457 skippedOS
= line
[idx
+11:].lstrip()
458 if skippedOS
== "MacOSX" and not macOSBuild
: break
460 idx
= string
.find(line
,"BUILD_MIN_OS:")
462 minOS
= "-" + osFlag
+ "=" + line
[idx
+13:].lstrip()
463 idx
= string
.find(line
,"BUILD:")
465 buildLines
.append(string
.Template(line
[idx
+6:]).safe_substitute(buildSubs
))
467 idx
= string
.find(line
,"RUN:")
469 if "$SUDO" in line
: batsTest
["AsRoot"] = True
470 runLines
.append(string
.Template(line
[idx
+4:]).safe_substitute(runSubs
))
472 idx
= string
.find(line
,"RUN_TIMEOUT:")
474 batsTest
["Timeout"] = line
[idx
+12:].lstrip()
476 idx
= string
.find(line
,"BOOT_ARGS:")
478 batsTest
["BootArgsSet"] = ",".join(line
[idx
+9:].split())
480 idx
= string
.find(line
,"NO_CRASH_LOG:")
482 batsSuppressedCrashes
.append(line
[idx
+13:].lstrip())
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",
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")