The Xcode Fairy
This post describes a bug in Xcode 4.2.1 and a simple way to work around it. If you find it useful, please send me $5,000.00.
Xcode 4.2.1 has a bug (actually a set of related bugs) that can break the automated testing functionality for a project in certain conditions, such that the following things happen:
0.) Xcode gets confused, and uses the wrong executable for the project's Test action. This prevents the automated tests from being executed. This is the actual core of the bug.
1.) Compounding things somewhat, when this bug manifests, Xcode will tell the programmers that all their tests passed, when in fact Xcode didn't actually run any of their tests. That's bad, because it can easily go unnoticed for a while. The programmers may waste significant time writing code, thinking all tests are passing, only to then have to backtrack and rewrite when they realize that their new code is actually all wrong (something the failure of their existing tests would have alerted them to, had the tests actually been run).
2.) But those things are nothing compared to the insidious violence that this bug visits upon the programmers who try to figure out the cause and make it stop happening. This software defect can snare its victims in a web of deception that can take hours to unravel, and can result in the brutal and premature demise of any keyboards, mice, and even monitors that happen to be nearby.
A professional programmer has to have automated tests. Programmers argue about what kinds of tests to write, and when to write them, but not having tests at all is pure fuckery. Writing serious code without tests is for dilettantes and winos. Programmers need tests, which are typically run automatically by the programming toolchain when the code is built or run.
Mac programmers (and those iOS programmer weenies, too, as far as I've heard) pretty much have to use Xcode. There are insane convolutions that one might go through to avoid Xcode -- and indeed, most Mac programmers fantasize about this from time to time -- but generally speaking, Xcode is really the only game in town.
Mac programmers need Xcode and they need tests, so naturally they want Xcode to run their tests.
Therefore, when the programmers eventually notice that Xcode isn't running their tests, they will likely stop whatever else they are doing and try to fix it, by fiddling with the project settings. Most programmers would start by editing or recreating the project's Xcode schemes, which is logical, because the Xcode scheme controls, among other things, how and when to run the tests. They'll make a change or perhaps give up and rebuild the scheme from scratch (a minor pain in the ass), or maybe quit Xcode and revert the project file to the last checked out version. Most likely, they will see that after their changes, Xcode again properly runs their tests. The programmers will resume their work, and life will once again be fine, or at least okay.
But Xcode isn't through with those poor assholes. No, it has only just begun to fuck with them.
A bit later -- minutes, hours, days in some cases -- the bug will hit them again. With a little bad luck, their git commit log might end up looking something like this:
That's a whole Sunday there. Looks like they (*cough* I) didn't get any actual work done all day. That's because this bug is a motherfucking son of a bitch. It comes and goes on a whim, at random, which makes it hard initially to figure out what is going on. A programmer might try something, test it, see that the problem is gone, and think they fixed it. But really what they did had nothing to do with anything. And then the bug comes back. Over and over and over.
"Which of you jackasses merged the beer-goggles topic branch without running the test suites?" one programmer might call out. "Five tests fail. FAIL!"
"No way dude, I ran the tests before I pushed the code. Maybe something... ah, shit, it's not fucking running the tests again. It just says 'Test Succeeded' and 'No issues'. Again! Xcode is a lying ass bitch!"'
Maybe at this point a mouse or empty soda can gets thrown against the wall.
"Okay man, look, tests are working on my machine, let's diff the project files and see if we can find something that might be why its not working on your machine but is on mine."
"Man, this file is just a list of thousands of UUIDs, we're not gonna make sense of this... let me just get a copy of your project file and replace mine. I'll quit Xcode and move it into place... Okay, that worked. Fuck though, why does the project keep getting corrupted? I don't see anything that looks like a clue when I diff the good one and the bad one either."
"Well, at least it's working now."
By that time, though, it might be 4:00 PM, and with energy waning, they might just commit what they've done so far with a message like "Work around Xcode bugs" and leave early to go get drunk.
The next day, though, the situation might well begin to rapidly deteriorate.
"Motherfucker!" one of them might scream. "Did you fuck with the scheme definition in your last commit? Now I have to delete it and re-create it again."
"No, bitch," the other might retort indignantly. "I didn't change jack shit, except for turning off parallel builds. And it is still working fine for me. If you broke the build again, you fix it. Revert your Xcode project file again, it was working fine an hour ago."
"I already did that, genius! There aren't any changes to the project file. Check out the diff right here on the screen, asshole."
At this point, the programmers would probably decide that something is just irredeemably fucked with their on-disk project file itself. It wouldn't be surprising; the project file in question was originally created with the older Xcode 4.0, a notably buggy and half-baked release, and has also been subjected to dozens of instances where Xcode crashed while various operations were in progress. Maybe something got borked.
Concerned about their slipping productivity and the growing amount of time lost to fucking with their IDE settings, they embark on the unenviable task of recreating the project from scratch. Create the project bundle, add each target, set each target's build settings, set the Bundle Loader and the Test Host, the Info.plist path, and other such drudgery.
Finished, they quit Xcode and painstakingly move all the little niggly .xcscheme and .pbxproject files into the correct places within the .xcproj bundle.
"All tests pass, fuck yeah, commit that shit."
And so, back to work.
With a gray and gloomy tone having already been set for the week, what with all this hapless rejiggering of settings and a marked lack of in-the-zone progress, the shit will likely hit the fan and splatter all over everything in the room the next morning, when one of the programmers git-pulls and sets to work.
"What the fucking motherfuck who fucking added another build target to the motherfucking scheme and broke the motherfucking goddamn tests again! Jesus motherfucking Christ!"
"The fuck are you on about? I don't know what you're doing, but you're fucking Doing It Wrong, dude. It works fine on my machine."
"Motherfucker, stop lying! You added another fucking target to the scheme! Look, it's right fucking here in the Build panel! LOOK!"
"Hey man, fuck you--"
"NO FUCK YUO!!!"
Now, the reason that these hypothetical hackers are on the verge of breaking into fisticuffs is that this bug hides itself like fucking octopus in a coral reef. It's really not their fault; it's a hard bug to track down.
Since they live in imagination-land, though, we can just help them out.
Programmer: "Ohmyfuckinghgod, dude, look out for that huge fucking bug above your head!"
Programmer: "If you hit me in the head with that rolled up newspaper I'm gonna kick you in the balls, jackass."
Programmer: "I'm serious, dude!"
Magic Fairy: "I'm not a bug. I'm a magical fairy from the Kingdom of Menlo. I'm here to help you out with your Xcode problem."
Programmer: "Oh shit, you weren't lying. What the fuck is that!"
Programmer: "Look, sweetheart, thanks for the offer and everything, but we're professionals here. I don't believe in fairies, and even though I now see that I am obviously wrong about that, I still don't think there's a whole lot that a flying miniature hottie with butterfly wings and a wand can tell us about--"
Magic Fairy: "Change the name of your Xcode project."
Programmer: "Look, we already did that, and more. It had no effect. We rebuilt the project, we renamed the project, the targets, the schemes, etc. and it had no--"
Magic Fairy: "Change the name of your Xcode project file."
Programmer: "I told you, we--"
Programmer: "Dude, this shit's been biting us in the ass for days on end. If a fucking magical fairy from another dimension appears and tells us to rename our Xcode project file, we might as well fucking try it. Here, I'll do it. What should I rename the project to?"
Magic Fairy: "I'm not from another dimension."
Programmer: "Okay. Rename the project. Here, I'll append 'v2' to the name."
Magic Fairy: "No. Change it to my name."
Programmer: "She's totally fucking with you, dude. Come on, now."
Programmer: "Fine! Whatever. What's your name?"
Magic Fairy: "My name is Slut."
Magic Fairy: "Yeah, Slut. S-l-u-t."
Programmer: "Wow, that's not how we'd tend to pronounce it. But I mean that's cool, totally cool, and stuff. How did you uh, come by that name?"
Magic Fairy: "My name used to be Alianna, but as soon as I entered this programming-related blog post, my name was transformed. An evil golem named Choad'wan cast a spell on the whole IT industry to make sure it remains a totally sexist bro-fest for all eternity."
Programmer: "Yeah, I've been reading a lot about that lately."
Programmer: "But dude, she is actually pretty hot though! I mean kinda too small to uh, I mean, anyway you should really put on your glasses, dude."
Programmer: "Seems like Choad'wan's spell is working fine. Can we get back to the Xcode thing."
Magic Fairy: "You say it like you're the one waiting for me."
Programmer: "Okay, should I rename the file in the Finder, or do it in Xcode?"
Magic Fairy: "In Xcode."
Programmer: "Okay, s-l-u-t it is. But you know, we did this before."
Magic Fairy: "You named your program after me before?"
Programmer: "No, but we renamed it at some point."
Magic Fairy: "Why?"
Programmer: "Well, at some point when the project file got corrupted again, I uh, renamed the project FUCKXCODE.xcodeproj because I thought that Xcode was maybe internally referring to different types of entities by name, instead of uuid, and getting confused. Since the project was named after the the command-line tool it builds, they had the same name. So I changed it to 'FUCKXCODE' and things the tests started running again."
Magic Fairy: "Well, wasn't that a clue?"
Programmer: "I thought so, but no, because later I changed the name back and everything still worked. So it wasn't the project name. But just in case I appended ' project' to the name. But the problem came back. And then later, we renamed all the targets and that fixed it. For a while. Tweaking the target or the scheme seems to fix it for a while, but somehow the project is getting corrupted when Xcode writes it out to disk. But we can't figure out exactly how it's getting corrupted, or why."
Magic Fairy: "My spiritual guru used to say something from time to time that I think applies to your situation."
Programmer: "Who's your spiritual guru?"
Magic Fairy: "Microsoft Word 6.0.1 for Macintosh."
Programmer: "I get it, fuckface. You put LSD in my Dr. Pepper. Really fucking funny."
Programmer: "Wait, your spritual guru is Microsoft Word 6.0.1 for Macintosh?"
Magic Fairy: "She liked to remind me--"
Programmer: "Wait, Microsoft Word is a chick?"
Magic Fairy: "Sometimes things that go away by themselves--"
Programmer: "--can come back by themselves! Of course! Dammit! How could I be so stupid!"
Programmer: "The fuck are you guys talking about?"
Programmer: "I think she's saying all our debugging of this issue has been a total fucking waste of time."
Magic Fairy: "Right."
Programmer: "Okay, I renamed the project like you said. What's next."
Magic Fairy: "Add a single test that asserts 1 equals 0, so tests shouldn't pass. Then relaunch Xcode. Hit Command-U to run the Test action."
Programmer: "Tests fail."
Magic Fairy: "Quit Xcode. The do it again."
Programmer: "Tests fail again."
Magic Fairy: "Again."
Programmer: "Tests fail again. Okay, we get it, it works with your name."
Magic Fairy: "Again!"
Programmer: "Okay! Fuck! Tests fail again! Quit, relaunch, test, fail! Quit, relaunch, test, fail! Same thing again! Again! Again! Again! I've done it ten times now. It's working!"
Magic Fairy: "Now rename it back."
Programmer: "Okay. Renamed it back to the original babe-rater.xcodeproj."
Magic Fairy: "Wait, the name of your program is babe-rater?"
Magic Fairy: "You're writing a command line UNIX utility to rate babes?"
Programmer: "Never mind that! Look! Launch Xcode, test, the tests fail as they should. So what does that prove?"
Magic Fairy: "Again."
Magic Fairy: "Again.
Magic Fairy: "Again!"
Programmer: "Tests... the tests pass. Shit. It didn't run any of the tests. But the project file hasn't changed at all. The project was never actually corrupt in the fist place!"
Programmer: "Motherfucking motherfucker! What the fuck!"
Programmer: "So the bug can go away by itself..."
Magic Fairy: "And come back by itself. Yeah."
Programmer: "And the project file doesn't change at all. So all our recreating he targets and build schemes, which seemed to fix it for a while, wasn't really doing anything."
Magic Fairy: "Yeah."
Programmer: "And you're saying that once the bug goes away, it won't come back until you quit and relaunch Xcode. And vice versa."
Magic Fairy: "Yeah."
Programmer: "And that the bug actually never actually affects the project file on disk."
Magic Fairy: "Right. The bug exists only in the spirit world."
Programmer: "You mean the computer's volatile memory."
Magic Fairy: "Whatever. You can use any abstraction that let's you wrap your little mind around it."
Programmer: "So to really test whether something -- a scheme setting, a target name, the project name -- affects whether or not this bug manifests and breaks Xcode's test functionality, you have to make the change and perform the quit-relaunch-test sequence a bunch of times."
Magic Fairy: "Ten times."
Programmer: "Ten times."
Magic Fairy: "Yeah."
Programmer: "So how did you figure this out?"
Magic Fairy: "Well, chicks tend to be more methodical about this kind of thing. No need get all furious like some testosterone-overloaded roid-rager and smash your mouse down on the desk, or throw your beverage can against the wall. We don't tend to get so... emotional. I mean, it's just a software bug. That makes it easier to keep a clear head and debug what is really going on."
Programmer: "We don't really have to name the project after you, do we."
Magic Fairy: "No."
Programmer: "But the name cannot be the same as the command line executable."
Magic Fairy: "Correct."
Programmer: "How about 'babe-rater project.xcodeproj'?"
Magic Fairy: "No. The project name must not even contain the name of the command line tool that it builds. Otherwise this bug will be summoned. Sometimes."
Programmer: "But that's fucking lame! The project builds a tool called 'babe-rater'. Doesn't it make sense that the project should be named 'babe-rater.xcodeproj', or at least 'babe-rater something'?"
Magic Fairy: "It's not a matter of what makes sense. It's a matter of what stops the bug from being summoned."
Programmer: "Well, did you at least file a bug?"
Magic Fairy: "Sigh. No I did not file a bug. I find Apple's bug reporting process pretty odious, to be frank. Their system is slow and cumbersome. Most bugs are marked 'Duplicate' and they don't even show you the courtesy of giving you read permissions on the bug that yours is a dupe of. It's like throwing your effort into a black hole. A black hole with no manners."
Programmer: "Well, I don't want to have to keep giving my Xcode projects names that don't reflect what the project actually builds!"
Magic Fairy: "Then why don't you file a fucking bug, sweetheart."
Programmer: "Big mouth for such a little girl, eh?"
Programmer: "Shut up, dude. Okay, I'll file a bug."
Programmer: "Okay, let's see... bugreporter.apple.com. Okay, here we go:
Programmer: "You will, huh? You up your Adderall dose or something?"
Magic Fairy: "I think you should add 'Thanks.' at the bottom."
Programmer: "Okay, added. Now I'll just click Submit here, and... fuck!"
Magic Fairy: "Ha ha ha! Typical!"
Programmer: "What? What does it say?"
Programmer: "It says
java.io.EOFException Broken pipe."
Programmer: "Hit Submit again."
Programmer: "Now it says,
Broken pipeBroken pipe."
Magic Fairy: "Ha ha ha! Ha ha ha!"
Programmer: "Okay, I'm gonna log out and log back in and paste that in. And... Okay. Filed. Bug ID# 10532871."
Programmer: "Fuck it dude, I'm outta here. You and your little friend can file all the bug reports you want. All I care about is that we can work around it, which we now can. I'll see you Monday. "
Programmer: "Whatever. Bye."
Magic Fairy: "I don't like your friend."
Programmer: "We're not exactly friends."
Magic Fairy: "Okay."
Programmer: "You wanna like, uh, go get a drink or something?"
Magic Fairy: "I'm not really much of a drinker."
Programmer: "Oh. Well, um, I guess--"
Magic Fairy: "I'd smoke a little weed if you got it, though."
Programmer: "What? Uh, sure, sure! I don't have any on me, but my place is only a few minutes away. Let's go."
Magic Fairy: "What about those other bug reports?"
Programmer: "Fuck it. One good deed per day's enough for me. Come on."
Magic Fairy: "♪ ♬ ☆ ♡・・・"
At this point, as the programmer gathers up his gear and his bag and heads for the exit, from her vantage point floating along behind him, the magical fairy might see him tear off and crumple up and discard a piece of graph paper from his notebook, upon which was scrawled: