test.pl 51 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738
  1. #!/usr/bin/perl
  2. # test.pl
  3. # Run unit tests.
  4. use strict;
  5. use File::Basename;
  6. # We use encode_json() to write BATS plist files.
  7. # JSON::PP does not exist on iOS devices, but we need not write plists there.
  8. # So we simply load JSON:PP if it exists.
  9. if (eval { require JSON::PP; 1; }) {
  10. JSON::PP->import();
  11. }
  12. chdir dirname $0;
  13. chomp (my $DIR = `pwd`);
  14. if (scalar(@ARGV) == 1) {
  15. my $arg = $ARGV[0];
  16. if ($arg eq "-h" || $arg eq "-H" || $arg eq "-help" || $arg eq "help") {
  17. print(<<END);
  18. usage: $0 [options] [testname ...]
  19. $0 help
  20. testname:
  21. `testname` runs a specific test. If no testnames are given, runs all tests.
  22. options:
  23. ARCH=<arch>
  24. OS=<sdk name>[sdk version][-<deployment target>[-<run target>]]
  25. ROOT=/path/to/project.roots/
  26. CC=<compiler name>
  27. LANGUAGE=c,c++,objective-c,objective-c++,swift
  28. MEM=mrc,arc
  29. GUARDMALLOC=0|1|before|after
  30. BUILD=0|1 (build the tests?)
  31. RUN=0|1 (run the tests?)
  32. VERBOSE=0|1|2 (0=quieter 1=print commands executed 2=full test output)
  33. BATS=0|1 (build for and/or run in BATS?)
  34. examples:
  35. test installed library, x86_64
  36. $0
  37. test buildit-built root, i386 and x86_64, MRC and ARC, clang compiler
  38. $0 ARCH=i386,x86_64 ROOT=/tmp/objc4.roots MEM=mrc,arc CC=clang
  39. test buildit-built root with iOS simulator, deploy to iOS 7, run on iOS 8
  40. $0 ARCH=x86_64 ROOT=/tmp/objc4.roots OS=iphonesimulator-7.0-8.0
  41. test buildit-built root on attached iOS device
  42. $0 ARCH=arm64 ROOT=/tmp/objc4.roots OS=iphoneos
  43. END
  44. exit 0;
  45. }
  46. }
  47. #########################################################################
  48. ## Tests
  49. # Maps test name => test's filename extension.
  50. # ex: "msgSend" => "m"
  51. # `keys %ALL_TESTS` is also used as the list of all tests found on disk.
  52. my %ALL_TESTS;
  53. #########################################################################
  54. ## Variables for use in complex build and run rules
  55. # variable # example value
  56. # things you can multiplex on the command line
  57. # ARCH=i386,x86_64,armv6,armv7
  58. # OS=macosx,iphoneos,iphonesimulator (plus sdk/deployment/run versions)
  59. # LANGUAGE=c,c++,objective-c,objective-c++,swift
  60. # CC=clang
  61. # MEM=mrc,arc
  62. # GUARDMALLOC=0,1,before,after
  63. # things you can set once on the command line
  64. # ROOT=/path/to/project.roots
  65. # BUILD=0|1
  66. # RUN=0|1
  67. # VERBOSE=0|1|2
  68. # BATS=0|1
  69. # environment variables from the command line
  70. # DSTROOT
  71. # OBJROOT
  72. # (SRCROOT is ignored; test sources are assumed to
  73. # be in the same directory as the test script itself.)
  74. # fixme SYMROOT for dsymutil output?
  75. # Some arguments as read from the command line.
  76. my %args;
  77. my $BUILD;
  78. my $RUN;
  79. my $VERBOSE;
  80. my $BATS;
  81. my $HOST;
  82. my $PORT;
  83. my @TESTLIBNAMES = ("libobjc.A.dylib", "libobjc-trampolines.dylib");
  84. my $TESTLIBDIR = "/usr/lib";
  85. # Top level directory for intermediate and final build products.
  86. # Intermediate files must be kept separate for XBS BATS builds.
  87. my $OBJROOT = $ENV{OBJROOT} || "";
  88. my $DSTROOT = $ENV{DSTROOT} || "";
  89. # Build product directory inside DSTROOT and OBJROOT.
  90. # Each test config gets its own build directory inside this.
  91. my $BUILDDIR;
  92. # Local top-level directory.
  93. # This is the default value for $BUILDDIR.
  94. my $LOCALBASE = "/tmp/test-$TESTLIBNAMES[0]-build";
  95. # Device-side top-level directory.
  96. # This replaces $DSTROOT$BUILDDIR/ for on-device execution.
  97. my $REMOTEBASE = "/AppleInternal/objctest";
  98. # BATS top-level directory.
  99. # This replaces $DSTROOT$BUILDDIR/ for BATS execution.
  100. my $BATSBASE = "/AppleInternal/CoreOS/tests/objc4";
  101. my $crashcatch = <<'END';
  102. // interpose-able code to catch crashes, print, and exit cleanly
  103. #include <signal.h>
  104. #include <string.h>
  105. #include <unistd.h>
  106. // from dyld-interposing.h
  107. #define DYLD_INTERPOSE(_replacement,_replacee) __attribute__((used)) static struct{ const void* replacement; const void* replacee; } _interpose_##_replacee __attribute__ ((section ("__DATA,__interpose"))) = { (const void*)(unsigned long)&_replacement, (const void*)(unsigned long)&_replacee };
  108. static void catchcrash(int sig)
  109. {
  110. const char *msg;
  111. switch (sig) {
  112. case SIGILL: msg = "CRASHED: SIGILL"; break;
  113. case SIGBUS: msg = "CRASHED: SIGBUS"; break;
  114. case SIGSYS: msg = "CRASHED: SIGSYS"; break;
  115. case SIGSEGV: msg = "CRASHED: SIGSEGV"; break;
  116. case SIGTRAP: msg = "CRASHED: SIGTRAP"; break;
  117. case SIGABRT: msg = "CRASHED: SIGABRT"; break;
  118. default: msg = "unknown signal"; break;
  119. }
  120. write(STDERR_FILENO, msg, strlen(msg));
  121. // avoid backslash-n newline due to escaping differences somewhere
  122. // in BATS versus local execution (perhaps different perl versions?)
  123. char newline = 0xa;
  124. write(STDERR_FILENO, &newline, 1);
  125. _exit(1);
  126. }
  127. static void setupcrash(void) __attribute__((constructor));
  128. static void setupcrash(void)
  129. {
  130. signal(SIGILL, &catchcrash);
  131. signal(SIGBUS, &catchcrash);
  132. signal(SIGSYS, &catchcrash);
  133. signal(SIGSEGV, &catchcrash);
  134. signal(SIGTRAP, &catchcrash);
  135. signal(SIGABRT, &catchcrash);
  136. }
  137. static int hacked = 0;
  138. ssize_t hacked_write(int fildes, const void *buf, size_t nbyte)
  139. {
  140. if (!hacked) {
  141. setupcrash();
  142. hacked = 1;
  143. }
  144. return write(fildes, buf, nbyte);
  145. }
  146. DYLD_INTERPOSE(hacked_write, write);
  147. END
  148. #########################################################################
  149. ## Harness
  150. # map language to buildable extensions for that language
  151. my %extensions_for_language = (
  152. "c" => ["c"],
  153. "objective-c" => ["c", "m"],
  154. "c++" => ["c", "cc", "cp", "cpp", "cxx", "c++"],
  155. "objective-c++" => ["c", "m", "cc", "cp", "cpp", "cxx", "c++", "mm"],
  156. "swift" => ["swift"],
  157. "any" => ["c", "m", "cc", "cp", "cpp", "cxx", "c++", "mm", "swift"],
  158. );
  159. # map extension to languages
  160. my %languages_for_extension = (
  161. "c" => ["c", "objective-c", "c++", "objective-c++"],
  162. "m" => ["objective-c", "objective-c++"],
  163. "mm" => ["objective-c++"],
  164. "cc" => ["c++", "objective-c++"],
  165. "cp" => ["c++", "objective-c++"],
  166. "cpp" => ["c++", "objective-c++"],
  167. "cxx" => ["c++", "objective-c++"],
  168. "c++" => ["c++", "objective-c++"],
  169. "swift" => ["swift"],
  170. );
  171. # Run some newline-separated commands like `make` would, stopping if any fail
  172. # run("cmd1 \n cmd2 \n cmd3")
  173. sub make {
  174. my $output = "";
  175. my @cmds = split("\n", $_[0]);
  176. die if scalar(@cmds) == 0;
  177. $? = 0;
  178. foreach my $cmd (@cmds) {
  179. chomp $cmd;
  180. next if $cmd =~ /^\s*$/;
  181. $cmd .= " 2>&1";
  182. print "$cmd\n" if $VERBOSE;
  183. $output .= `$cmd`;
  184. last if $?;
  185. }
  186. print "$output\n" if $VERBOSE;
  187. return $output;
  188. }
  189. sub chdir_verbose {
  190. my $dir = shift || die;
  191. print "cd $dir\n" if $VERBOSE;
  192. chdir $dir || die "couldn't cd $dir";
  193. }
  194. sub rm_rf_verbose {
  195. my $dir = shift || die;
  196. print "mkdir -p $dir\n" if $VERBOSE;
  197. `rm -rf '$dir'`;
  198. die "couldn't rm -rf $dir" if $?;
  199. }
  200. sub mkdir_verbose {
  201. my $dir = shift || die;
  202. print "mkdir -p $dir\n" if $VERBOSE;
  203. `mkdir -p '$dir'`;
  204. die "couldn't mkdir $dir" if $?;
  205. }
  206. # xterm colors
  207. my $red = "\e[41;37m";
  208. my $yellow = "\e[43;30m";
  209. my $nocolor = "\e[0m";
  210. if (! -t STDIN) {
  211. # Not isatty. Don't use colors.
  212. $red = "";
  213. $yellow = "";
  214. $nocolor = "";
  215. }
  216. # print text with a colored prefix on each line
  217. # fixme some callers pass an array of lines and some don't
  218. sub colorprefix {
  219. my $color = shift;
  220. while (defined(my $lines = shift)) {
  221. $lines = "\n" if ($lines eq "");
  222. for my $line (split(/^/, $lines)) {
  223. chomp $line;
  224. print "$color $nocolor$line\n";
  225. }
  226. }
  227. }
  228. # print text colored
  229. # fixme some callers pass an array of lines and some don't
  230. sub colorprint {
  231. my $color = shift;
  232. while (defined(my $lines = shift)) {
  233. $lines = "\n" if ($lines eq "");
  234. for my $line (split(/^/, $lines)) {
  235. chomp $line;
  236. print "$color$line$nocolor\n";
  237. }
  238. }
  239. }
  240. # Return test names from the command line.
  241. # Returns all tests if no tests were named.
  242. sub gettests {
  243. my @tests;
  244. foreach my $arg (@ARGV) {
  245. push @tests, $arg if ($arg !~ /=/ && $arg !~ /^-/);
  246. }
  247. opendir(my $dir, $DIR) || die;
  248. while (my $file = readdir($dir)) {
  249. my ($name, $ext) = ($file =~ /^([^.]+)\.([^.]+)$/);
  250. next if ! $languages_for_extension{$ext};
  251. open(my $in, "< $file") || die "$file";
  252. my $contents = join "", <$in>;
  253. if (defined $ALL_TESTS{$name}) {
  254. colorprint $yellow, "SKIP: multiple tests named '$name'; skipping file '$file'.";
  255. } else {
  256. $ALL_TESTS{$name} = $ext if ($contents =~ m#^[/*\s]*TEST_#m);
  257. }
  258. close($in);
  259. }
  260. closedir($dir);
  261. if (scalar(@tests) == 0) {
  262. @tests = keys %ALL_TESTS;
  263. }
  264. @tests = sort @tests;
  265. return @tests;
  266. }
  267. # Turn a C compiler name into a C++ compiler name.
  268. sub cplusplus {
  269. my ($c) = @_;
  270. if ($c =~ /cc/) {
  271. $c =~ s/cc/\+\+/;
  272. return $c;
  273. }
  274. return $c . "++"; # e.g. clang => clang++
  275. }
  276. # Turn a C compiler name into a Swift compiler name
  277. sub swift {
  278. my ($c) = @_;
  279. $c =~ s#[^/]*$#swift#;
  280. return $c;
  281. }
  282. # Returns an array of all sdks from `xcodebuild -showsdks`
  283. my @sdks_memo;
  284. sub getsdks {
  285. if (!@sdks_memo) {
  286. @sdks_memo = (`xcodebuild -showsdks` =~ /-sdk (.+)$/mg);
  287. }
  288. return @sdks_memo;
  289. }
  290. my %sdk_path_memo = {};
  291. sub getsdkpath {
  292. my ($sdk) = @_;
  293. if (!defined $sdk_path_memo{$sdk}) {
  294. ($sdk_path_memo{$sdk}) = (`xcodebuild -version -sdk '$sdk' Path` =~ /^\s*(.+?)\s*$/);
  295. }
  296. return $sdk_path_memo{$sdk};
  297. }
  298. # Extract a version number from a string.
  299. # Ignore trailing "internal".
  300. sub versionsuffix {
  301. my ($str) = @_;
  302. my ($vers) = ($str =~ /([0-9]+\.[0-9]+)(?:\.?internal)?$/);
  303. return $vers;
  304. }
  305. sub majorversionsuffix {
  306. my ($str) = @_;
  307. my ($vers) = ($str =~ /([0-9]+)\.[0-9]+(?:\.?internal)?$/);
  308. return $vers;
  309. }
  310. sub minorversionsuffix {
  311. my ($str) = @_;
  312. my ($vers) = ($str =~ /[0-9]+\.([0-9]+)(?:\.?internal)?$/);
  313. return $vers;
  314. }
  315. # Compares two SDK names and returns the newer one.
  316. # Assumes the two SDKs are the same OS.
  317. sub newersdk {
  318. my ($lhs, $rhs) = @_;
  319. # Major version wins.
  320. my $lhsMajor = majorversionsuffix($lhs);
  321. my $rhsMajor = majorversionsuffix($rhs);
  322. if ($lhsMajor > $rhsMajor) { return $lhs; }
  323. if ($lhsMajor < $rhsMajor) { return $rhs; }
  324. # Minor version wins.
  325. my $lhsMinor = minorversionsuffix($lhs);
  326. my $rhsMinor = minorversionsuffix($rhs);
  327. if ($lhsMinor > $rhsMinor) { return $lhs; }
  328. if ($lhsMinor < $rhsMinor) { return $rhs; }
  329. # Lexically-last wins (i.e. internal is better than not internal)
  330. if ($lhs gt $rhs) { return $lhs; }
  331. return $rhs;
  332. }
  333. sub rewind {
  334. seek($_[0], 0, 0);
  335. }
  336. # parse name=value,value pairs
  337. sub readconditions {
  338. my ($conditionstring) = @_;
  339. my %results;
  340. my @conditions = ($conditionstring =~ /\w+=(?:[^\s,]+,?)+/g);
  341. for my $condition (@conditions) {
  342. my ($name, $values) = ($condition =~ /(\w+)=(.+)/);
  343. $results{$name} = [split ',', $values];
  344. }
  345. return %results;
  346. }
  347. sub check_output {
  348. my %C = %{shift()};
  349. my $name = shift;
  350. my @output = @_;
  351. my %T = %{$C{"TEST_$name"}};
  352. # Quietly strip MallocScribble before saving the "original" output
  353. # because it is distracting.
  354. filter_malloc(\@output);
  355. my @original_output = @output;
  356. # Run result-checking passes, reducing @output each time
  357. my $xit = 1;
  358. my $bad = "";
  359. my $warn = "";
  360. my $runerror = $T{TEST_RUN_OUTPUT};
  361. filter_hax(\@output);
  362. filter_verbose(\@output);
  363. filter_simulator(\@output);
  364. $warn = filter_warn(\@output);
  365. $bad |= filter_guardmalloc(\@output) if ($C{GUARDMALLOC});
  366. $bad |= filter_valgrind(\@output) if ($C{VALGRIND});
  367. $bad = filter_expected(\@output, \%C, $name) if ($bad eq "");
  368. $bad = filter_bad(\@output) if ($bad eq "");
  369. # OK line should be the only one left
  370. $bad = "(output not 'OK: $name')" if ($bad eq "" && (scalar(@output) != 1 || $output[0] !~ /^OK: $name/));
  371. if ($bad ne "") {
  372. colorprint $red, "FAIL: /// test '$name' \\\\\\";
  373. colorprefix $red, @original_output;
  374. colorprint $red, "FAIL: \\\\\\ test '$name' ///";
  375. colorprint $red, "FAIL: $name: $bad";
  376. $xit = 0;
  377. }
  378. elsif ($warn ne "") {
  379. colorprint $yellow, "PASS: /// test '$name' \\\\\\";
  380. colorprefix $yellow, @original_output;
  381. colorprint $yellow, "PASS: \\\\\\ test '$name' ///";
  382. print "PASS: $name (with warnings)\n";
  383. }
  384. else {
  385. print "PASS: $name\n";
  386. }
  387. return $xit;
  388. }
  389. sub filter_expected
  390. {
  391. my $outputref = shift;
  392. my %C = %{shift()};
  393. my $name = shift;
  394. my %T = %{$C{"TEST_$name"}};
  395. my $runerror = $T{TEST_RUN_OUTPUT} || return "";
  396. my $bad = "";
  397. my $output = join("\n", @$outputref) . "\n";
  398. if ($output !~ /$runerror/) {
  399. $bad = "(run output does not match TEST_RUN_OUTPUT)";
  400. @$outputref = ("FAIL: $name");
  401. } else {
  402. @$outputref = ("OK: $name"); # pacify later filter
  403. }
  404. return $bad;
  405. }
  406. sub filter_bad
  407. {
  408. my $outputref = shift;
  409. my $bad = "";
  410. my @new_output;
  411. for my $line (@$outputref) {
  412. if ($line =~ /^BAD: (.*)/) {
  413. $bad = "(failed)";
  414. } else {
  415. push @new_output, $line;
  416. }
  417. }
  418. @$outputref = @new_output;
  419. return $bad;
  420. }
  421. sub filter_warn
  422. {
  423. my $outputref = shift;
  424. my $warn = "";
  425. my @new_output;
  426. for my $line (@$outputref) {
  427. if ($line !~ /^WARN: (.*)/) {
  428. push @new_output, $line;
  429. } else {
  430. $warn = "(warned)";
  431. }
  432. }
  433. @$outputref = @new_output;
  434. return $warn;
  435. }
  436. sub filter_verbose
  437. {
  438. my $outputref = shift;
  439. my @new_output;
  440. for my $line (@$outputref) {
  441. if ($line !~ /^VERBOSE: (.*)/) {
  442. push @new_output, $line;
  443. }
  444. }
  445. @$outputref = @new_output;
  446. }
  447. sub filter_simulator
  448. {
  449. my $outputref = shift;
  450. my @new_output;
  451. for my $line (@$outputref) {
  452. if (($line !~ /No simulator devices appear to be running/) &&
  453. ($line !~ /CoreSimulator is attempting to unload a stale CoreSimulatorService job/) &&
  454. ($line !~ /Failed to locate a valid instance of CoreSimulatorService/))
  455. {
  456. push @new_output, $line;
  457. }
  458. }
  459. @$outputref = @new_output;
  460. }
  461. sub filter_hax
  462. {
  463. my $outputref = shift;
  464. my @new_output;
  465. for my $line (@$outputref) {
  466. if ($line !~ /Class OS_tcp_/) {
  467. push @new_output, $line;
  468. }
  469. }
  470. @$outputref = @new_output;
  471. }
  472. sub filter_valgrind
  473. {
  474. my $outputref = shift;
  475. my $errors = 0;
  476. my $leaks = 0;
  477. my @new_output;
  478. for my $line (@$outputref) {
  479. if ($line =~ /^Approx: do_origins_Dirty\([RW]\): missed \d bytes$/) {
  480. # --track-origins warning (harmless)
  481. next;
  482. }
  483. if ($line =~ /^UNKNOWN __disable_threadsignal is unsupported. This warning will not be repeated.$/) {
  484. # signals unsupported (harmless)
  485. next;
  486. }
  487. if ($line =~ /^UNKNOWN __pthread_sigmask is unsupported. This warning will not be repeated.$/) {
  488. # signals unsupported (harmless)
  489. next;
  490. }
  491. if ($line !~ /^^\.*==\d+==/) {
  492. # not valgrind output
  493. push @new_output, $line;
  494. next;
  495. }
  496. my ($errcount) = ($line =~ /==\d+== ERROR SUMMARY: (\d+) errors/);
  497. if (defined $errcount && $errcount > 0) {
  498. $errors = 1;
  499. }
  500. (my $leakcount) = ($line =~ /==\d+==\s+(?:definitely|possibly) lost:\s+([0-9,]+)/);
  501. if (defined $leakcount && $leakcount > 0) {
  502. $leaks = 1;
  503. }
  504. }
  505. @$outputref = @new_output;
  506. my $bad = "";
  507. $bad .= "(valgrind errors)" if ($errors);
  508. $bad .= "(valgrind leaks)" if ($leaks);
  509. return $bad;
  510. }
  511. sub filter_malloc
  512. {
  513. my $outputref = shift;
  514. my $errors = 0;
  515. my @new_output;
  516. my $count = 0;
  517. for my $line (@$outputref) {
  518. # Ignore MallocScribble prologue.
  519. # Ignore MallocStackLogging prologue.
  520. if ($line =~ /malloc: enabling scribbling to detect mods to free/ ||
  521. $line =~ /Deleted objects will be dirtied by the collector/ ||
  522. $line =~ /malloc: stack logs being written into/ ||
  523. $line =~ /malloc: stack logs deleted from/ ||
  524. $line =~ /malloc: process \d+ no longer exists/ ||
  525. $line =~ /malloc: recording malloc and VM allocation stacks/)
  526. {
  527. next;
  528. }
  529. # not malloc output
  530. push @new_output, $line;
  531. }
  532. @$outputref = @new_output;
  533. }
  534. sub filter_guardmalloc
  535. {
  536. my $outputref = shift;
  537. my $errors = 0;
  538. my @new_output;
  539. my $count = 0;
  540. for my $line (@$outputref) {
  541. if ($line !~ /^GuardMalloc\[[^\]]+\]: /) {
  542. # not guardmalloc output
  543. push @new_output, $line;
  544. next;
  545. }
  546. # Ignore 4 lines of guardmalloc prologue.
  547. # Anything further is a guardmalloc error.
  548. if (++$count > 4) {
  549. $errors = 1;
  550. }
  551. }
  552. @$outputref = @new_output;
  553. my $bad = "";
  554. $bad .= "(guardmalloc errors)" if ($errors);
  555. return $bad;
  556. }
  557. # TEST_SOMETHING
  558. # text
  559. # text
  560. # END
  561. sub extract_multiline {
  562. my ($flag, $contents, $name) = @_;
  563. if ($contents =~ /$flag\n/) {
  564. my ($output) = ($contents =~ /$flag\n(.*?\n)END[ *\/]*\n/s);
  565. die "$name used $flag without END\n" if !defined($output);
  566. return $output;
  567. }
  568. return undef;
  569. }
  570. # TEST_SOMETHING
  571. # text
  572. # OR
  573. # text
  574. # END
  575. sub extract_multiple_multiline {
  576. my ($flag, $contents, $name) = @_;
  577. if ($contents =~ /$flag\n/) {
  578. my ($output) = ($contents =~ /$flag\n(.*?\n)END[ *\/]*\n/s);
  579. die "$name used $flag without END\n" if !defined($output);
  580. $output =~ s/\nOR\n/\n|/sg;
  581. $output = "^(" . $output . ")\$";
  582. return $output;
  583. }
  584. return undef;
  585. }
  586. sub gather_simple {
  587. my $CREF = shift;
  588. my %C = %{$CREF};
  589. my $name = shift;
  590. chdir_verbose $DIR;
  591. my $ext = $ALL_TESTS{$name};
  592. my $file = "$name.$ext";
  593. return 0 if !$file;
  594. # search file for 'TEST_CONFIG' or '#include "test.h"'
  595. # also collect other values:
  596. # TEST_DISABLED disable test with an optional message
  597. # TEST_CRASHES test is expected to crash
  598. # TEST_CONFIG test conditions
  599. # TEST_ENV environment prefix
  600. # TEST_CFLAGS compile flags
  601. # TEST_BUILD build instructions
  602. # TEST_BUILD_OUTPUT expected build stdout/stderr
  603. # TEST_RUN_OUTPUT expected run stdout/stderr
  604. open(my $in, "< $file") || die;
  605. my $contents = join "", <$in>;
  606. my $test_h = ($contents =~ /^\s*#\s*(include|import)\s*"test\.h"/m);
  607. my ($disabled) = ($contents =~ /\b(TEST_DISABLED\b.*)$/m);
  608. my $crashes = ($contents =~ /\bTEST_CRASHES\b/m);
  609. my ($conditionstring) = ($contents =~ /\bTEST_CONFIG\b(.*)$/m);
  610. my ($envstring) = ($contents =~ /\bTEST_ENV\b(.*)$/m);
  611. my ($cflags) = ($contents =~ /\bTEST_CFLAGS\b(.*)$/m);
  612. my ($buildcmd) = extract_multiline("TEST_BUILD", $contents, $name);
  613. my ($builderror) = extract_multiple_multiline("TEST_BUILD_OUTPUT", $contents, $name);
  614. my ($runerror) = extract_multiple_multiline("TEST_RUN_OUTPUT", $contents, $name);
  615. return 0 if !$test_h && !$disabled && !$crashes && !defined($conditionstring) && !defined($envstring) && !defined($cflags) && !defined($buildcmd) && !defined($builderror) && !defined($runerror);
  616. if ($disabled) {
  617. colorprint $yellow, "SKIP: $name (disabled by $disabled)";
  618. return 0;
  619. }
  620. # check test conditions
  621. my $run = 1;
  622. my %conditions = readconditions($conditionstring);
  623. if (! $conditions{LANGUAGE}) {
  624. # implicit language restriction from file extension
  625. $conditions{LANGUAGE} = $languages_for_extension{$ext};
  626. }
  627. for my $condkey (keys %conditions) {
  628. my @condvalues = @{$conditions{$condkey}};
  629. # special case: RUN=0 does not affect build
  630. if ($condkey eq "RUN" && @condvalues == 1 && $condvalues[0] == 0) {
  631. $run = 0;
  632. next;
  633. }
  634. my $testvalue = $C{$condkey};
  635. next if !defined($testvalue);
  636. # testvalue is the configuration being run now
  637. # condvalues are the allowed values for this test
  638. my $ok = 0;
  639. for my $condvalue (@condvalues) {
  640. # special case: objc and objc++
  641. if ($condkey eq "LANGUAGE") {
  642. $condvalue = "objective-c" if $condvalue eq "objc";
  643. $condvalue = "objective-c++" if $condvalue eq "objc++";
  644. }
  645. $ok = 1 if ($testvalue eq $condvalue);
  646. # special case: CC and CXX allow substring matches
  647. if ($condkey eq "CC" || $condkey eq "CXX") {
  648. $ok = 1 if ($testvalue =~ /$condvalue/);
  649. }
  650. last if $ok;
  651. }
  652. if (!$ok) {
  653. my $plural = (@condvalues > 1) ? "one of: " : "";
  654. print "SKIP: $name ($condkey=$testvalue, but test requires $plural", join(' ', @condvalues), ")\n";
  655. return 0;
  656. }
  657. }
  658. # save some results for build and run phases
  659. $$CREF{"TEST_$name"} = {
  660. TEST_BUILD => $buildcmd,
  661. TEST_BUILD_OUTPUT => $builderror,
  662. TEST_CRASHES => $crashes,
  663. TEST_RUN_OUTPUT => $runerror,
  664. TEST_CFLAGS => $cflags,
  665. TEST_ENV => $envstring,
  666. TEST_RUN => $run,
  667. DSTDIR => "$C{DSTDIR}/$name.build",
  668. OBJDIR => "$C{OBJDIR}/$name.build",
  669. };
  670. return 1;
  671. }
  672. # Test description plist to write when building for BATS execution.
  673. my %bats_plist;
  674. $bats_plist{'Project'} = "objc4";
  675. $bats_plist{'Tests'} = []; # populated by append_bats_test()
  676. # Saves run instructions for a single test in all configurations as a BATS test.
  677. sub append_bats_test {
  678. my $name = shift;
  679. my $arch = join(',', @{$args{ARCH}});
  680. my $os = join(',', @{$args{OSVERSION}});
  681. my $mem = join(',', @{$args{MEM}});
  682. my $language = join(',', @{$args{LANGUAGE}});
  683. push @{$bats_plist{'Tests'}}, {
  684. "TestName" => "$name",
  685. "Command" => [
  686. "/usr/bin/perl",
  687. "$BATSBASE/test/test.pl",
  688. $name,
  689. "ARCH=$arch",
  690. "OS=$os",
  691. "MEM=$mem",
  692. "LANGUAGE=$language",
  693. "BUILD=0",
  694. "RUN=1",
  695. "VERBOSE=1",
  696. "BATS=1",
  697. ]
  698. };
  699. }
  700. # Builds a simple test
  701. sub build_simple {
  702. my %C = %{shift()};
  703. my $name = shift;
  704. my %T = %{$C{"TEST_$name"}};
  705. mkdir_verbose $T{DSTDIR};
  706. chdir_verbose $T{DSTDIR};
  707. # we don't mkdir $T{OBJDIR} because most tests don't use it
  708. my $ext = $ALL_TESTS{$name};
  709. my $file = "$DIR/$name.$ext";
  710. if ($T{TEST_CRASHES}) {
  711. `echo '$crashcatch' > crashcatch.c`;
  712. make("$C{COMPILE_C} -dynamiclib -o libcrashcatch.dylib -x c crashcatch.c");
  713. die "$?" if $?;
  714. }
  715. my $cmd = $T{TEST_BUILD} ? eval "return \"$T{TEST_BUILD}\"" : "$C{COMPILE} $T{TEST_CFLAGS} $file -o $name.exe";
  716. my $output = make($cmd);
  717. # ignore out-of-date text-based stubs (caused by ditto into SDK)
  718. $output =~ s/ld: warning: text-based stub file.*\n//g;
  719. # rdar://10163155
  720. $output =~ s/ld: warning: could not create compact unwind for [^\n]+: does not use standard frame\n//g;
  721. # rdar://37937122
  722. $output =~ s/^warning: Cannot lower [^\n]+\n//g;
  723. $output =~ s/^warning: key: [^\n]+\n//g;
  724. $output =~ s/^warning: discriminator: [^\n]+\n//g;
  725. $output =~ s/^warning: callee: [^\n]+\n//g;
  726. # rdar://38710948
  727. $output =~ s/ld: warning: ignoring file [^\n]*libclang_rt\.bridgeos\.a[^\n]*\n//g;
  728. # ignore compiler logging of CCC_OVERRIDE_OPTIONS effects
  729. if (defined $ENV{CCC_OVERRIDE_OPTIONS}) {
  730. $output =~ s/### (CCC_OVERRIDE_OPTIONS:|Adding argument|Deleting argument|Replacing) [^\n]*\n//g;
  731. }
  732. my $ok;
  733. if (my $builderror = $T{TEST_BUILD_OUTPUT}) {
  734. # check for expected output and ignore $?
  735. if ($output =~ /$builderror/) {
  736. $ok = 1;
  737. } elsif (defined $ENV{CCC_OVERRIDE_OPTIONS} && $builderror =~ /warning:/) {
  738. # CCC_OVERRIDE_OPTIONS manipulates compiler diagnostics.
  739. # Don't enforce any TEST_BUILD_OUTPUT that looks for warnings.
  740. colorprint $yellow, "WARN: /// test '$name' \\\\\\";
  741. colorprefix $yellow, $output;
  742. colorprint $yellow, "WARN: \\\\\\ test '$name' ///";
  743. colorprint $yellow, "WARN: $name (build output does not match TEST_BUILD_OUTPUT; not fatal because CCC_OVERRIDE_OPTIONS is set)";
  744. $ok = 1;
  745. } else {
  746. colorprint $red, "FAIL: /// test '$name' \\\\\\";
  747. colorprefix $red, $output;
  748. colorprint $red, "FAIL: \\\\\\ test '$name' ///";
  749. colorprint $red, "FAIL: $name (build output does not match TEST_BUILD_OUTPUT)";
  750. $ok = 0;
  751. }
  752. } elsif ($?) {
  753. colorprint $red, "FAIL: /// test '$name' \\\\\\";
  754. colorprefix $red, $output;
  755. colorprint $red, "FAIL: \\\\\\ test '$name' ///";
  756. colorprint $red, "FAIL: $name (build failed)";
  757. $ok = 0;
  758. } elsif ($output ne "") {
  759. colorprint $red, "FAIL: /// test '$name' \\\\\\";
  760. colorprefix $red, $output;
  761. colorprint $red, "FAIL: \\\\\\ test '$name' ///";
  762. colorprint $red, "FAIL: $name (unexpected build output)";
  763. $ok = 0;
  764. } else {
  765. $ok = 1;
  766. }
  767. if ($ok) {
  768. foreach my $file (glob("*.exe *.dylib *.bundle")) {
  769. if (!$BATS) {
  770. # not for BATS to save space and build time
  771. # fixme use SYMROOT?
  772. make("xcrun dsymutil $file");
  773. }
  774. if ($C{OS} eq "macosx" || $C{OS} =~ /simulator/) {
  775. # setting any entitlements disables dyld environment variables
  776. } else {
  777. # get-task-allow entitlement is required
  778. # to enable dyld environment variables
  779. make("xcrun codesign -s - --entitlements $DIR/get_task_allow_entitlement.plist $file");
  780. die "$?" if $?;
  781. }
  782. }
  783. }
  784. return $ok;
  785. }
  786. # Run a simple test (testname.exe, with error checking of stdout and stderr)
  787. sub run_simple {
  788. my %C = %{shift()};
  789. my $name = shift;
  790. my %T = %{$C{"TEST_$name"}};
  791. if (! $T{TEST_RUN}) {
  792. print "PASS: $name (build only)\n";
  793. return 1;
  794. }
  795. my $testdir = $T{DSTDIR};
  796. chdir_verbose $testdir;
  797. my $env = "$C{ENV} $T{TEST_ENV}";
  798. if ($T{TEST_CRASHES}) {
  799. $env .= " OBJC_DEBUG_DONT_CRASH=YES";
  800. }
  801. my $output;
  802. if ($C{ARCH} =~ /^arm/ && `uname -p` !~ /^arm/) {
  803. # run on iOS or watchos or tvos device
  804. # fixme device selection and verification
  805. my $remotedir = "$REMOTEBASE/" . basename($C{DSTDIR}) . "/$name.build";
  806. # Add test dir and libobjc's dir to DYLD_LIBRARY_PATH.
  807. # Insert libcrashcatch.dylib if necessary.
  808. $env .= " DYLD_LIBRARY_PATH=$remotedir";
  809. $env .= ":$REMOTEBASE" if ($C{TESTLIBDIR} ne $TESTLIBDIR);
  810. if ($T{TEST_CRASHES}) {
  811. $env .= " DYLD_INSERT_LIBRARIES=$remotedir/libcrashcatch.dylib";
  812. }
  813. my $cmd = "ssh -p $PORT $HOST 'cd $remotedir && env $env ./$name.exe'";
  814. $output = make("$cmd");
  815. }
  816. elsif ($C{OS} =~ /simulator/) {
  817. # run locally in a simulator
  818. # fixme selection of simulated OS version
  819. my $simdevice;
  820. if ($C{OS} =~ /iphonesimulator/) {
  821. $simdevice = 'iPhone X';
  822. } elsif ($C{OS} =~ /watchsimulator/) {
  823. $simdevice = 'Apple Watch Series 4 - 40mm';
  824. } elsif ($C{OS} =~ /tvsimulator/) {
  825. $simdevice = 'Apple TV 1080p';
  826. } else {
  827. die "unknown simulator $C{OS}\n";
  828. }
  829. my $sim = "xcrun -sdk iphonesimulator simctl spawn '$simdevice'";
  830. # Add test dir and libobjc's dir to DYLD_LIBRARY_PATH.
  831. # Insert libcrashcatch.dylib if necessary.
  832. $env .= " DYLD_LIBRARY_PATH=$testdir";
  833. $env .= ":" . $C{TESTLIBDIR} if ($C{TESTLIBDIR} ne $TESTLIBDIR);
  834. if ($T{TEST_CRASHES}) {
  835. $env .= " DYLD_INSERT_LIBRARIES=$testdir/libcrashcatch.dylib";
  836. }
  837. my $simenv = "";
  838. foreach my $keyvalue (split(' ', $env)) {
  839. $simenv .= "SIMCTL_CHILD_$keyvalue ";
  840. }
  841. # Use the full path here so hack_cwd in test.h works.
  842. $output = make("env $simenv $sim $testdir/$name.exe");
  843. }
  844. else {
  845. # run locally
  846. # Add test dir and libobjc's dir to DYLD_LIBRARY_PATH.
  847. # Insert libcrashcatch.dylib if necessary.
  848. $env .= " DYLD_LIBRARY_PATH=$testdir";
  849. $env .= ":" . $C{TESTLIBDIR} if ($C{TESTLIBDIR} ne $TESTLIBDIR);
  850. if ($T{TEST_CRASHES}) {
  851. $env .= " DYLD_INSERT_LIBRARIES=$testdir/libcrashcatch.dylib";
  852. }
  853. $output = make("sh -c '$env ./$name.exe'");
  854. }
  855. return check_output(\%C, $name, split("\n", $output));
  856. }
  857. my %compiler_memo;
  858. sub find_compiler {
  859. my ($cc, $toolchain, $sdk_path) = @_;
  860. # memoize
  861. my $key = $cc . ':' . $toolchain;
  862. my $result = $compiler_memo{$key};
  863. return $result if defined $result;
  864. $result = make("xcrun -toolchain $toolchain -find $cc 2>/dev/null");
  865. chomp $result;
  866. $compiler_memo{$key} = $result;
  867. return $result;
  868. }
  869. sub dirContainsAllTestLibs {
  870. my $dir = shift;
  871. foreach my $testlib (@TESTLIBNAMES) {
  872. my $found = (-e "$dir/$testlib");
  873. my $foundstr = ($found ? "found" : "didn't find");
  874. print "note: $foundstr $testlib in $dir\n" if ($VERBOSE);
  875. return 0 if (!$found);
  876. }
  877. return 1;
  878. }
  879. sub make_one_config {
  880. my $configref = shift;
  881. my $root = shift;
  882. my %C = %{$configref};
  883. # Aliases
  884. $C{LANGUAGE} = "objective-c" if $C{LANGUAGE} eq "objc";
  885. $C{LANGUAGE} = "objective-c++" if $C{LANGUAGE} eq "objc++";
  886. # Interpret OS version string from command line.
  887. my ($sdk_arg, $deployment_arg, $run_arg, undef) = split('-', $C{OSVERSION});
  888. delete $C{OSVERSION};
  889. my ($os_arg) = ($sdk_arg =~ /^([^\.0-9]+)/);
  890. $deployment_arg = "default" if !defined($deployment_arg);
  891. $run_arg = "default" if !defined($run_arg);
  892. my %allowed_os_args = (
  893. "macosx" => "macosx", "osx" => "macosx", "macos" => "macosx",
  894. "iphoneos" => "iphoneos", "ios" => "iphoneos",
  895. "iphonesimulator" => "iphonesimulator", "iossimulator" => "iphonesimulator",
  896. "watchos" => "watchos",
  897. "watchsimulator" => "watchsimulator", "watchossimulator" => "watchsimulator",
  898. "appletvos" => "appletvos", "tvos" => "appletvos",
  899. "appletvsimulator" => "appletvsimulator", "tvsimulator" => "appletvsimulator",
  900. "bridgeos" => "bridgeos",
  901. );
  902. $C{OS} = $allowed_os_args{$os_arg} || die "unknown OS '$os_arg' (expected " . join(', ', sort keys %allowed_os_args) . ")\n";
  903. # set the config name now, after massaging the language and OS versions,
  904. # but before adding other settings
  905. my $configname = config_name(%C);
  906. die if ($configname =~ /'/);
  907. die if ($configname =~ / /);
  908. ($C{NAME} = $configname) =~ s/~/ /g;
  909. (my $configdir = $configname) =~ s#/##g;
  910. $C{DSTDIR} = "$DSTROOT$BUILDDIR/$configdir";
  911. $C{OBJDIR} = "$OBJROOT$BUILDDIR/$configdir";
  912. # Allow tests to see BATS-edness in TEST_CONFIG.
  913. $C{BATS} = $BATS;
  914. if ($C{OS} eq "iphoneos" || $C{OS} eq "iphonesimulator") {
  915. $C{TOOLCHAIN} = "ios";
  916. } elsif ($C{OS} eq "watchos" || $C{OS} eq "watchsimulator") {
  917. $C{TOOLCHAIN} = "watchos";
  918. } elsif ($C{OS} eq "appletvos" || $C{OS} eq "appletvsimulator") {
  919. $C{TOOLCHAIN} = "appletvos";
  920. } elsif ($C{OS} eq "bridgeos") {
  921. $C{TOOLCHAIN} = "bridgeos";
  922. } elsif ($C{OS} eq "macosx") {
  923. $C{TOOLCHAIN} = "osx";
  924. } else {
  925. colorprint $yellow, "WARN: don't know toolchain for OS $C{OS}";
  926. $C{TOOLCHAIN} = "default";
  927. }
  928. if ($BUILD) {
  929. # Look up SDK.
  930. # Try exact match first.
  931. # Then try lexically-last prefix match
  932. # (so "macosx" => "macosx10.7internal")
  933. $sdk_arg =~ s/$os_arg/$C{OS}/;
  934. my @sdks = getsdks();
  935. if ($VERBOSE) {
  936. print "note: Installed SDKs: @sdks\n";
  937. }
  938. my $exactsdk = undef;
  939. my $prefixsdk = undef;
  940. foreach my $sdk (@sdks) {
  941. $exactsdk = $sdk if ($sdk eq $sdk_arg);
  942. $prefixsdk = newersdk($sdk, $prefixsdk) if ($sdk =~ /^$sdk_arg/);
  943. }
  944. my $sdk;
  945. if ($exactsdk) {
  946. $sdk = $exactsdk;
  947. } elsif ($prefixsdk) {
  948. $sdk = $prefixsdk;
  949. } else {
  950. die "unknown SDK '$sdk_arg'\nInstalled SDKs: @sdks\n";
  951. }
  952. # Set deployment target.
  953. # fixme can't enforce version when run_arg eq "default"
  954. # because we don't know it yet
  955. $deployment_arg = versionsuffix($sdk) if $deployment_arg eq "default";
  956. if ($run_arg ne "default") {
  957. die "Deployment target '$deployment_arg' is newer than run target '$run_arg'\n" if $deployment_arg > $run_arg;
  958. }
  959. $C{DEPLOYMENT_TARGET} = $deployment_arg;
  960. $C{SDK_PATH} = getsdkpath($sdk);
  961. } else {
  962. # not $BUILD
  963. $C{DEPLOYMENT_TARGET} = "unknown_deployment_target";
  964. $C{SDK_PATH} = "/unknown/sdk";
  965. }
  966. # Set run target.
  967. $C{RUN_TARGET} = $run_arg;
  968. # Look up test library (possible in root or SDK_PATH)
  969. my $rootarg = $root;
  970. my $symroot;
  971. my @sympaths = ( (glob "$root/*~sym")[0],
  972. (glob "$root/BuildRecords/*_install/Symbols")[0],
  973. "$root/Symbols" );
  974. my @dstpaths = ( (glob "$root/*~dst")[0],
  975. (glob "$root/BuildRecords/*_install/Root")[0],
  976. "$root/Root" );
  977. for(my $i = 0; $i < scalar(@sympaths); $i++) {
  978. if (-e $sympaths[$i] && -e $dstpaths[$i]) {
  979. $symroot = $sympaths[$i];
  980. $root = $dstpaths[$i];
  981. last;
  982. }
  983. }
  984. if ($root ne "") {
  985. # Root specified. Require that it contain our dylibs.
  986. if (dirContainsAllTestLibs("$root$C{SDK_PATH}$TESTLIBDIR")) {
  987. $C{TESTLIBDIR} = "$root$C{SDK_PATH}$TESTLIBDIR";
  988. } elsif (dirContainsAllTestLibs("$root$TESTLIBDIR")) {
  989. $C{TESTLIBDIR} = "$root$TESTLIBDIR";
  990. } elsif (dirContainsAllTestLibs($root)) {
  991. $C{TESTLIBDIR} = "$root";
  992. } else {
  993. die "Didn't find some libs in root '$rootarg' for sdk '$C{SDK_PATH}'\n";
  994. }
  995. }
  996. else {
  997. # No root specified. Use the SDK or / for our dylibs.
  998. if (dirContainsAllTestLibs("$C{SDK_PATH}$TESTLIBDIR")) {
  999. $C{TESTLIBDIR} = "$C{SDK_PATH}$TESTLIBDIR";
  1000. } else {
  1001. # We don't actually check in / because on devices
  1002. # there are no dylib files there.
  1003. $C{TESTLIBDIR} = $TESTLIBDIR;
  1004. }
  1005. }
  1006. @{$C{TESTLIBS}} = map { "$C{TESTLIBDIR}/$_" } @TESTLIBNAMES;
  1007. # convenience for tests that want libobjc.dylib's path
  1008. $C{TESTLIB} = @{$C{TESTLIBS}}[0];
  1009. foreach my $testlibname (@TESTLIBNAMES) {
  1010. if (-e "$symroot/$testlibname.dSYM") {
  1011. push(@{$C{TESTDSYMS}}, "$symroot/$testlibname.dSYM");
  1012. }
  1013. }
  1014. if ($VERBOSE) {
  1015. foreach my $testlib (@{$C{TESTLIBS}}) {
  1016. my @uuids = `/usr/bin/dwarfdump -u '$testlib'`;
  1017. while (my $uuid = shift @uuids) {
  1018. print "note: $uuid";
  1019. }
  1020. }
  1021. }
  1022. # Look up compilers
  1023. my $cc = $C{CC};
  1024. my $cxx = cplusplus($C{CC});
  1025. my $swift = swift($C{CC});
  1026. if (! $BUILD) {
  1027. $C{CC} = $cc;
  1028. $C{CXX} = $cxx;
  1029. $C{SWIFT} = $swift
  1030. } else {
  1031. $C{CC} = find_compiler($cc, $C{TOOLCHAIN}, $C{SDK_PATH});
  1032. $C{CXX} = find_compiler($cxx, $C{TOOLCHAIN}, $C{SDK_PATH});
  1033. $C{SWIFT} = find_compiler($swift, $C{TOOLCHAIN}, $C{SDK_PATH});
  1034. die "No C compiler '$cc' ('$C{CC}') in toolchain '$C{TOOLCHAIN}'\n" if !-e $C{CC};
  1035. die "No C++ compiler '$cxx' ('$C{CXX}') in toolchain '$C{TOOLCHAIN}'\n" if !-e $C{CXX};
  1036. die "No Swift compiler '$swift' ('$C{SWIFT}') in toolchain '$C{TOOLCHAIN}'\n" if !-e $C{SWIFT};
  1037. }
  1038. if ($C{ARCH} eq "i386" && $C{OS} eq "macosx") {
  1039. # libarclite no longer available on i386
  1040. # fixme need an archived copy for bincompat testing
  1041. $C{FORCE_LOAD_ARCLITE} = "";
  1042. } elsif ($C{OS} eq "bridgeos") {
  1043. # no libarclite on bridgeOS
  1044. $C{FORCE_LOAD_ARCLITE} = "";
  1045. } else {
  1046. $C{FORCE_LOAD_ARCLITE} = "-Xlinker -force_load -Xlinker " . dirname($C{CC}) . "/../lib/arc/libarclite_$C{OS}.a";
  1047. }
  1048. # Populate cflags
  1049. my $cflags = "-I$DIR -W -Wall -Wno-objc-weak-compat -Wno-arc-bridge-casts-disallowed-in-nonarc -Wshorten-64-to-32 -Qunused-arguments -fno-caret-diagnostics -Os -arch $C{ARCH} ";
  1050. if (!$BATS) {
  1051. # save-temps so dsymutil works so debug info works.
  1052. # Disabled in BATS to save disk space.
  1053. # rdar://45656803 -save-temps causes bad -Wstdlibcxx-not-found warnings
  1054. $cflags .= "-g -save-temps -Wno-stdlibcxx-not-found";
  1055. }
  1056. my $objcflags = "";
  1057. my $swiftflags = "-g ";
  1058. $cflags .= " -isysroot '$C{SDK_PATH}'";
  1059. $cflags .= " '-Wl,-syslibroot,$C{SDK_PATH}'";
  1060. $swiftflags .= " -sdk '$C{SDK_PATH}'";
  1061. # Set deployment target cflags
  1062. my $target = undef;
  1063. die "No deployment target" if $C{DEPLOYMENT_TARGET} eq "";
  1064. if ($C{OS} eq "iphoneos") {
  1065. $cflags .= " -mios-version-min=$C{DEPLOYMENT_TARGET}";
  1066. $target = "$C{ARCH}-apple-ios$C{DEPLOYMENT_TARGET}";
  1067. }
  1068. elsif ($C{OS} eq "iphonesimulator") {
  1069. $cflags .= " -mios-simulator-version-min=$C{DEPLOYMENT_TARGET}";
  1070. $target = "$C{ARCH}-apple-ios$C{DEPLOYMENT_TARGET}";
  1071. }
  1072. elsif ($C{OS} eq "watchos") {
  1073. $cflags .= " -mwatchos-version-min=$C{DEPLOYMENT_TARGET}";
  1074. $target = "$C{ARCH}-apple-watchos$C{DEPLOYMENT_TARGET}";
  1075. }
  1076. elsif ($C{OS} eq "watchsimulator") {
  1077. $cflags .= " -mwatchos-simulator-version-min=$C{DEPLOYMENT_TARGET}";
  1078. $target = "$C{ARCH}-apple-watchos$C{DEPLOYMENT_TARGET}";
  1079. }
  1080. elsif ($C{OS} eq "appletvos") {
  1081. $cflags .= " -mtvos-version-min=$C{DEPLOYMENT_TARGET}";
  1082. $target = "$C{ARCH}-apple-tvos$C{DEPLOYMENT_TARGET}";
  1083. }
  1084. elsif ($C{OS} eq "appletvsimulator") {
  1085. $cflags .= " -mtvos-simulator-version-min=$C{DEPLOYMENT_TARGET}";
  1086. $target = "$C{ARCH}-apple-tvos$C{DEPLOYMENT_TARGET}";
  1087. }
  1088. elsif ($C{OS} eq "bridgeos") {
  1089. $cflags .= " -mbridgeos-version-min=$C{DEPLOYMENT_TARGET}";
  1090. $target = "$C{ARCH}-apple-bridgeos$C{DEPLOYMENT_TARGET}";
  1091. }
  1092. else {
  1093. $cflags .= " -mmacosx-version-min=$C{DEPLOYMENT_TARGET}";
  1094. $target = "$C{ARCH}-apple-macosx$C{DEPLOYMENT_TARGET}";
  1095. }
  1096. $swiftflags .= " -target $target";
  1097. $C{TESTINCLUDEDIR} = "$C{SDK_PATH}/usr/include";
  1098. $C{TESTLOCALINCLUDEDIR} = "$C{SDK_PATH}/usr/local/include";
  1099. if ($root ne "") {
  1100. if ($C{SDK_PATH} ne "/") {
  1101. $cflags .= " -isystem '$root$C{SDK_PATH}/usr/include'";
  1102. $cflags .= " -isystem '$root$C{SDK_PATH}/usr/local/include'";
  1103. }
  1104. my $library_path = $C{TESTLIBDIR};
  1105. $cflags .= " -L$library_path";
  1106. # fixme Root vs SDKContentRoot
  1107. $C{TESTINCLUDEDIR} = "$root/../SDKContentRoot/usr/include";
  1108. $C{TESTLOCALINCLUDEDIR} = "$root/../SDKContentRoot/usr/local/include";
  1109. $cflags .= " -isystem '$C{TESTINCLUDEDIR}'";
  1110. $cflags .= " -isystem '$C{TESTLOCALINCLUDEDIR}'";
  1111. }
  1112. # Populate objcflags
  1113. $objcflags .= " -lobjc";
  1114. if ($C{MEM} eq "arc") {
  1115. $objcflags .= " -fobjc-arc";
  1116. }
  1117. elsif ($C{MEM} eq "mrc") {
  1118. # nothing
  1119. }
  1120. else {
  1121. die "unrecognized MEM '$C{MEM}'\n";
  1122. }
  1123. # Populate ENV_PREFIX
  1124. $C{ENV} = "LANG=C MallocScribble=1";
  1125. $C{ENV} .= " VERBOSE=$VERBOSE" if $VERBOSE;
  1126. if ($root ne "") {
  1127. die "no spaces allowed in root" if $C{TESTLIBDIR} =~ /\s+/;
  1128. }
  1129. if ($C{GUARDMALLOC}) {
  1130. $C{ENV} .= " GUARDMALLOC=1"; # checked by tests and errcheck.pl
  1131. $C{ENV} .= " DYLD_INSERT_LIBRARIES=/usr/lib/libgmalloc.dylib";
  1132. if ($C{GUARDMALLOC} eq "before") {
  1133. $C{ENV} .= " MALLOC_PROTECT_BEFORE=1";
  1134. } elsif ($C{GUARDMALLOC} eq "after") {
  1135. # protect after is the default
  1136. } else {
  1137. die "Unknown guard malloc mode '$C{GUARDMALLOC}'\n";
  1138. }
  1139. }
  1140. # Populate compiler commands
  1141. $C{XCRUN} = "env LANG=C /usr/bin/xcrun -toolchain '$C{TOOLCHAIN}'";
  1142. $C{COMPILE_C} = "$C{XCRUN} '$C{CC}' $cflags -x c -std=gnu99";
  1143. $C{COMPILE_CXX} = "$C{XCRUN} '$C{CXX}' $cflags -x c++";
  1144. $C{COMPILE_M} = "$C{XCRUN} '$C{CC}' $cflags $objcflags -x objective-c -std=gnu99";
  1145. $C{COMPILE_MM} = "$C{XCRUN} '$C{CXX}' $cflags $objcflags -x objective-c++";
  1146. $C{COMPILE_SWIFT} = "$C{XCRUN} '$C{SWIFT}' $swiftflags";
  1147. $C{COMPILE} = $C{COMPILE_C} if $C{LANGUAGE} eq "c";
  1148. $C{COMPILE} = $C{COMPILE_CXX} if $C{LANGUAGE} eq "c++";
  1149. $C{COMPILE} = $C{COMPILE_M} if $C{LANGUAGE} eq "objective-c";
  1150. $C{COMPILE} = $C{COMPILE_MM} if $C{LANGUAGE} eq "objective-c++";
  1151. $C{COMPILE} = $C{COMPILE_SWIFT} if $C{LANGUAGE} eq "swift";
  1152. die "unknown language '$C{LANGUAGE}'\n" if !defined $C{COMPILE};
  1153. ($C{COMPILE_NOMEM} = $C{COMPILE}) =~ s/ -fobjc-arc\S*//g;
  1154. ($C{COMPILE_NOLINK} = $C{COMPILE}) =~ s/ '?-(?:Wl,|l)\S*//g;
  1155. ($C{COMPILE_NOLINK_NOMEM} = $C{COMPILE_NOMEM}) =~ s/ '?-(?:Wl,|l)\S*//g;
  1156. # Reject some self-inconsistent and disallowed configurations
  1157. if ($C{MEM} !~ /^(mrc|arc)$/) {
  1158. die "unknown MEM=$C{MEM} (expected one of mrc arc)\n";
  1159. }
  1160. if ($C{MEM} eq "arc" && $C{CC} !~ /clang/) {
  1161. print "note: skipping configuration $C{NAME}\n";
  1162. print "note: because CC=$C{CC} does not support MEM=$C{MEM}\n";
  1163. return 0;
  1164. }
  1165. if ($C{ARCH} eq "i386" && $C{OS} eq "macosx") {
  1166. colorprint $yellow, "WARN: skipping configuration $C{NAME}\n";
  1167. colorprint $yellow, "WARN: because 32-bit Mac is dead\n";
  1168. return 0;
  1169. }
  1170. # fixme
  1171. if ($C{LANGUAGE} eq "swift" && $C{ARCH} =~ /^arm/) {
  1172. print "note: skipping configuration $C{NAME}\n";
  1173. print "note: because ARCH=$C{ARCH} does not support LANGUAGE=SWIFT\n";
  1174. return 0;
  1175. }
  1176. # fixme unimplemented run targets
  1177. if ($C{RUN_TARGET} ne "default" && $C{OS} !~ /simulator/) {
  1178. colorprint $yellow, "WARN: skipping configuration $C{NAME}";
  1179. colorprint $yellow, "WARN: because OS=$C{OS} does not yet implement RUN_TARGET=$C{RUN_TARGET}";
  1180. }
  1181. %$configref = %C;
  1182. }
  1183. sub make_configs {
  1184. my ($root, %args) = @_;
  1185. my @results = ({}); # start with one empty config
  1186. for my $key (keys %args) {
  1187. my @newresults;
  1188. my @values = @{$args{$key}};
  1189. for my $configref (@results) {
  1190. my %config = %{$configref};
  1191. for my $value (@values) {
  1192. my %newconfig = %config;
  1193. $newconfig{$key} = $value;
  1194. push @newresults, \%newconfig;
  1195. }
  1196. }
  1197. @results = @newresults;
  1198. }
  1199. my @newresults;
  1200. for my $configref(@results) {
  1201. if (make_one_config($configref, $root)) {
  1202. push @newresults, $configref;
  1203. }
  1204. }
  1205. return @newresults;
  1206. }
  1207. sub config_name {
  1208. my %config = @_;
  1209. my $name = "";
  1210. for my $key (sort keys %config) {
  1211. $name .= '~' if $name ne "";
  1212. $name .= "$key=$config{$key}";
  1213. }
  1214. return $name;
  1215. }
  1216. sub rsync_ios {
  1217. my ($src, $timeout) = @_;
  1218. for (my $i = 0; $i < 10; $i++) {
  1219. make("$DIR/timeout.pl $timeout rsync -e 'ssh -p $PORT' -av $src $HOST:/$REMOTEBASE/");
  1220. return if $? == 0;
  1221. colorprint $yellow, "WARN: RETRY\n" if $VERBOSE;
  1222. }
  1223. die "Couldn't rsync tests to device. Check: device is connected; tcprelay is running; device trusts your Mac; device is unlocked; filesystem is mounted r/w\n";
  1224. }
  1225. sub build_and_run_one_config {
  1226. my %C = %{shift()};
  1227. my @tests = @_;
  1228. # Build and run
  1229. my $testcount = 0;
  1230. my $failcount = 0;
  1231. my $skipconfig = 0;
  1232. my @gathertests;
  1233. foreach my $test (@tests) {
  1234. if ($VERBOSE) {
  1235. print "\nGATHER $test\n";
  1236. }
  1237. if ($ALL_TESTS{$test}) {
  1238. gather_simple(\%C, $test) || next; # not pass, not fail
  1239. push @gathertests, $test;
  1240. } else {
  1241. die "No test named '$test'\n";
  1242. }
  1243. }
  1244. my @builttests;
  1245. if (!$BUILD) {
  1246. @builttests = @gathertests;
  1247. $testcount = scalar(@gathertests);
  1248. } else {
  1249. foreach my $test (@gathertests) {
  1250. if ($VERBOSE) {
  1251. print "\nBUILD $test\n";
  1252. }
  1253. if ($ALL_TESTS{$test}) {
  1254. $testcount++;
  1255. if (!build_simple(\%C, $test)) {
  1256. $failcount++;
  1257. } else {
  1258. push @builttests, $test;
  1259. }
  1260. } else {
  1261. die "No test named '$test'\n";
  1262. }
  1263. }
  1264. }
  1265. if (!$RUN || !scalar(@builttests)) {
  1266. # nothing to do
  1267. }
  1268. else {
  1269. if ($C{ARCH} =~ /^arm/ && `uname -p` !~ /^arm/) {
  1270. # upload timeout - longer for slow watch devices
  1271. my $timeout = ($C{OS} =~ /watch/) ? 120 : 20;
  1272. # upload all tests to iOS device
  1273. rsync_ios($C{DSTDIR}, $timeout);
  1274. # upload library to iOS device
  1275. if ($C{TESTLIBDIR} ne $TESTLIBDIR) {
  1276. foreach my $thing (@{$C{TESTLIBS}}, @{$C{TESTDSYMS}}) {
  1277. rsync_ios($thing, $timeout);
  1278. }
  1279. }
  1280. }
  1281. elsif ($C{OS} =~ /simulator/) {
  1282. # run locally in a simulator
  1283. }
  1284. else {
  1285. # run locally
  1286. if ($BATS) {
  1287. # BATS execution tries to run architectures that
  1288. # aren't supported by the device. Skip those configs here.
  1289. my $machine = `machine`;
  1290. chomp $machine;
  1291. # unsupported:
  1292. # running arm64e on non-arm64e device
  1293. # running arm64 on non-arm64* device
  1294. # running armv7k on non-armv7k device
  1295. # running arm64_32 on armv7k device
  1296. # We don't need to handle all mismatches here,
  1297. # only mismatches that arise within a single OS.
  1298. $skipconfig =
  1299. (($C{ARCH} eq "arm64e" && $machine ne "arm64e") ||
  1300. ($C{ARCH} eq "arm64" && $machine !~ /^arm64/) ||
  1301. ($C{ARCH} eq "armv7k" && $machine ne "armv7k") ||
  1302. ($C{ARCH} eq "arm64_32" && $machine eq "armv7k"));
  1303. if ($skipconfig) {
  1304. print "note: skipping configuration $C{NAME}\n";
  1305. print "note: because test arch $C{ARCH} is not " .
  1306. "supported on device arch $machine\n";
  1307. $testcount = 0;
  1308. }
  1309. }
  1310. }
  1311. if (!$skipconfig) {
  1312. foreach my $test (@builttests) {
  1313. print "\nRUN $test\n" if ($VERBOSE);
  1314. if ($ALL_TESTS{$test}) {
  1315. if (!run_simple(\%C, $test)) {
  1316. $failcount++;
  1317. }
  1318. } else {
  1319. die "No test named '$test'\n";
  1320. }
  1321. }
  1322. }
  1323. }
  1324. return ($testcount, $failcount, $skipconfig);
  1325. }
  1326. # Return value if set by "$argname=value" on the command line
  1327. # Return $default if not set.
  1328. sub getargs {
  1329. my ($argname, $default) = @_;
  1330. foreach my $arg (@ARGV) {
  1331. my ($value) = ($arg =~ /^$argname=(.+)$/);
  1332. return [split ',', $value] if defined $value;
  1333. }
  1334. return [split ',', $default];
  1335. }
  1336. # Return 1 or 0 if set by "$argname=1" or "$argname=0" on the
  1337. # command line. Return $default if not set.
  1338. sub getbools {
  1339. my ($argname, $default) = @_;
  1340. my @values = @{getargs($argname, $default)};
  1341. return [( map { ($_ eq "0") ? 0 : 1 } @values )];
  1342. }
  1343. # Return an integer if set by "$argname=value" on the
  1344. # command line. Return $default if not set.
  1345. sub getints {
  1346. my ($argname, $default) = @_;
  1347. my @values = @{getargs($argname, $default)};
  1348. return [( map { int($_) } @values )];
  1349. }
  1350. sub getarg {
  1351. my ($argname, $default) = @_;
  1352. my @values = @{getargs($argname, $default)};
  1353. die "Only one value allowed for $argname\n" if @values > 1;
  1354. return $values[0];
  1355. }
  1356. sub getbool {
  1357. my ($argname, $default) = @_;
  1358. my @values = @{getbools($argname, $default)};
  1359. die "Only one value allowed for $argname\n" if @values > 1;
  1360. return $values[0];
  1361. }
  1362. sub getint {
  1363. my ($argname, $default) = @_;
  1364. my @values = @{getints($argname, $default)};
  1365. die "Only one value allowed for $argname\n" if @values > 1;
  1366. return $values[0];
  1367. }
  1368. my $default_arch = "x86_64";
  1369. $args{ARCH} = getargs("ARCH", 0);
  1370. $args{ARCH} = getargs("ARCHS", $default_arch) if !@{$args{ARCH}}[0];
  1371. $args{OSVERSION} = getargs("OS", "macosx-default-default");
  1372. $args{MEM} = getargs("MEM", "mrc,arc");
  1373. $args{LANGUAGE} = [ map { lc($_) } @{getargs("LANGUAGE", "c,objective-c,c++,objective-c++")} ];
  1374. $args{CC} = getargs("CC", "clang");
  1375. $HOST = getarg("HOST", "iphone");
  1376. $PORT = getarg("PORT", "10022");
  1377. {
  1378. my $guardmalloc = getargs("GUARDMALLOC", 0);
  1379. # GUARDMALLOC=1 is the same as GUARDMALLOC=before,after
  1380. my @guardmalloc2 = ();
  1381. for my $arg (@$guardmalloc) {
  1382. if ($arg == 1) { push @guardmalloc2, "before";
  1383. push @guardmalloc2, "after"; }
  1384. else { push @guardmalloc2, $arg }
  1385. }
  1386. $args{GUARDMALLOC} = \@guardmalloc2;
  1387. }
  1388. $BUILD = getbool("BUILD", 1);
  1389. $RUN = getbool("RUN", 1);
  1390. $VERBOSE = getint("VERBOSE", 0);
  1391. $BATS = getbool("BATS", 0);
  1392. $BUILDDIR = getarg("BUILDDIR", $BATS ? $BATSBASE : $LOCALBASE);
  1393. my $root = getarg("ROOT", "");
  1394. $root =~ s#/*$##;
  1395. my @tests = gettests();
  1396. if ($BUILD) {
  1397. rm_rf_verbose "$DSTROOT$BUILDDIR";
  1398. rm_rf_verbose "$OBJROOT$BUILDDIR";
  1399. }
  1400. print "note: -----\n";
  1401. print "note: testing root '$root'\n";
  1402. my @configs = make_configs($root, %args);
  1403. print "note: -----\n";
  1404. print "note: testing ", scalar(@configs), " configurations:\n";
  1405. for my $configref (@configs) {
  1406. my $configname = $$configref{NAME};
  1407. print "note: configuration $configname\n";
  1408. }
  1409. my $failed = 0;
  1410. my $testconfigs = @configs;
  1411. my $failconfigs = 0;
  1412. my $skipconfigs = 0;
  1413. my $testcount = 0;
  1414. my $failcount = 0;
  1415. for my $configref (@configs) {
  1416. my $configname = $$configref{NAME};
  1417. print "note: -----\n";
  1418. print "note: \nnote: $configname\nnote: \n";
  1419. (my $t, my $f, my $skipconfig) =
  1420. eval { build_and_run_one_config($configref, @tests); };
  1421. $skipconfigs += $skipconfig;
  1422. if ($@) {
  1423. chomp $@;
  1424. colorprint $red, "FAIL: $configname";
  1425. colorprint $red, "FAIL: $@";
  1426. $failconfigs++;
  1427. } else {
  1428. my $color = ($f ? $red : "");
  1429. print "note:\n";
  1430. colorprint $color, "note: $configname\n";
  1431. colorprint $color, "note: $t tests, $f failures";
  1432. $testcount += $t;
  1433. $failcount += $f;
  1434. $failconfigs++ if ($f);
  1435. }
  1436. }
  1437. print "note: -----\n";
  1438. my $color = ($failconfigs ? $red : "");
  1439. colorprint $color, "note: $testconfigs configurations, " .
  1440. "$failconfigs with failures, $skipconfigs skipped";
  1441. colorprint $color, "note: $testcount tests, $failcount failures";
  1442. $failed = ($failconfigs ? 1 : 0);
  1443. if ($BUILD && $BATS && !$failed) {
  1444. # Collect BATS execution instructions for all tests.
  1445. # Each BATS "test" is all configurations together of one of our tests.
  1446. for my $testname (@tests) {
  1447. append_bats_test($testname);
  1448. }
  1449. # Write the BATS plist to disk.
  1450. my $json = encode_json(\%bats_plist);
  1451. my $filename = "$DSTROOT$BATSBASE/objc4.plist";
  1452. print "note: writing BATS config to $filename\n";
  1453. open(my $file, '>', $filename);
  1454. print $file $json;
  1455. close $file;
  1456. }
  1457. exit ($failed ? 1 : 0);