39

Xcode 13 gives me a hard time building my project which consists of targets with build phases that generate code.

E.g. one build phase generates the file Secrets+Generated.swift by simply using a shell script that echoes some code into that file.

enter image description here

The build phase defines that file as an output file. No input file, no input file list and no output file list, as only that one file is created/modified.

Almost all the time when building the project, the build fails:

error: input file '[ProjectPath]/Secrets+Generated.swift' was modified during the build
error: input file '[ProjectPath]/Secrets+Generated.swift' was modified during the build
Command CompileSwiftSources failed with a nonzero exit code

Has anyone encountered that issue or knows what to do? I tried toggling the checkbox "Based on dependency analysis", but that didn't help. I didn't have that issue when using Xcode 12. Interesting to note is the duplicate error message despite having only one build phase generating that particular file.

Btw. I get the same problem when using code generation tools like swiftgen, Sourcery or Cuckoo.

Edit: Here are my build phases: enter image description here The three marked build phases all generate one such file. All of them fail occasionally. I do not know if that makes a difference, but these are defined for only one target (Notification Service Extension) which is a dependency of my main app target, so it gets triggered only once when I build the app.

ChaosCoder
  • 3,085
  • 1
  • 22
  • 23
  • 1
    Where in the Build Phases did you put the Run Script phase? – Palle Sep 20 '21 at 16:09
  • @Palle: Please see my edit. – ChaosCoder Sep 20 '21 at 20:08
  • Hmm, I have similar code generation phases and as far as I can remember, Swift never complained. The only difference is that these scripts all run as a phase of the build of the main app. Would it be possible to write the files as part of the main app build and set their target membership to the notification service extension as well? – Palle Sep 21 '21 at 10:53
  • I had this issue just now but after cleaning build folder and rebuilding again, this issue went away. – Frank Goortani Sep 21 '21 at 18:00
  • Seeing this as well for Apollo – Daniel Sep 21 '21 at 18:40
  • Cleaning seems to resolve the issue temporary, but consecutive builds (that modify the files again) fail again. So that is no solution. – ChaosCoder Sep 21 '21 at 22:02
  • 3
    Having the same issue since the early Xcode 13 betas. It's still happening in the official release. In our case, the error happens for R.swift. – Junfeng Sep 22 '21 at 07:52
  • 2
    Still happening in Xcode 13 release... – scaly Sep 25 '21 at 19:16

5 Answers5

45

I'm debugging a similar issue in R.swift, I think I've found the cause of the issue.

Xcode 13 appears to execute Run Script build phases during index builds. This can cause a file to be written to by an index pass, in parallel with a build.

To check if this is happening for you, add this to your build script: say $ACTION
On Xcode 12, it only says "build", but in Xcode 13 I hear it saying "indexbuild" during compilation, whenever I save .swift files or otherwise navigate through my code.

To prevent your script from running during indexing, add this check:

if [ $ACTION != "indexbuild" ]; then
  # your script here 
fi
Tom Lokhorst
  • 13,658
  • 5
  • 55
  • 71
  • Brilliant -- this fixed the issue! Thanks. – JimmyB Oct 08 '21 at 05:55
  • 1
    Thank you, It's fixed my build issue ! – Ded77 Oct 11 '21 at 14:06
  • 4
    Why not ? `if [ $ACTION == "build"]; then ...` – malaba Oct 28 '21 at 13:11
  • 1
    Formidable sleuthing @Tom Lokhorst, I was struggling to get rid of unnecessary rebuilds and occasional build failures today in XC13, this seems to help – Steven Kramer Nov 03 '21 at 09:58
  • 3
    @malaba There's also [ ACTION = "install" ]. When build is initiated by fastlane's gym, it uses "install". Probably it's easier to just exclude indexbuild. – Zmey Nov 04 '21 at 13:22
  • I'm getting this issue when running a code generation step from a XcodeBuildToolPlugin. But the $ACTION environment variable doesn't seem to be available in that context - is there any way to check what type of build it is from a plugin? – Sam Woolf Feb 22 '23 at 12:52
  • @SamWoolf I don't know. But I suggest creating a new question (and linking to this question), to get more visibility. – Tom Lokhorst Feb 23 '23 at 14:44
3

EDIT 2: Tom Lokhorst's solution above seems to be the right way to resolve this: https://stackoverflow.com/a/69481200/12117100

EDIT: There is an issue with the original solution. Xcode 12.5 users who try to build the test target will get the error Build input file cannot be found... as the script that generates GeneratedMocks.swift might be run after the build starts.

Something that seems to work for both Xcode 12.5 and Xcode 13 is to add this to a cuckoo-output.xcfilelist (name it whatever you want) file:

${PROJECT_DIR}/${PROJECT_NAME}Tests/GeneratedMocks.swift

and add that xcfilelist to the Run Script's Output File Lists instead of adding the path to GeneratedMocks.swift directly in Output Files.

Original:

I had this issue after setting up Cuckoo for the first time in Xcode 13 and including ${PROJECT_DIR}/${PROJECT_NAME}Tests/GeneratedMocks.swift in the Output Files section of the Run script.

Removing the path from Output Files seems to do the trick for now.

Copripop
  • 51
  • 1
  • 5
  • This seemed to work for me too, with a custom script that generates a file, but I'm concerned about potential side effects. Couldn't this potentially break something? – Carmen Sep 28 '21 at 12:00
  • Yes you're right, I've updated the solution with one that seems to work but any corroboration will be appreciated – Copripop Sep 29 '21 at 09:54
2

Unfortunately the proposed workaround by Copripop does not work at least for the setup with Cuckoo. But the compile error occurs much more seldom now.

But I got an official answer from apple that this error occurs as expected because the input file is modified during the build job. As I understood that happens because the build phases can run in parallel. Before Xcode 13 the parallel execution was avoided when the output file definition fit to the input file of the next phase with the same file. This seems to be broken in Xcode 13.

Nevertheless the answer contained a hint about how to solve the problem for not Cuckoo setups. It recommends to use build rules, which are executed during the compile build phase. Here the link with a time marker for this topic: https://developer.apple.com/videos/play/wwdc2021/10210/?time=379

The build rule can be defined as matching the source file by naming convention and then run the script correctly. The output files from the build rules are automatically set to the compile build phase. So this should not be defined there.

Hopefully this helps some people to solve the problem, when they are able to define a match by naming convention. I did not find a good approach for Cuckoo.

Btw: There is already an issue opened for Cuckoo: https://github.com/Brightify/Cuckoo/issues/400

JavaAdam
  • 21
  • 2
0

Had the exact same problem. I was able to solve it by changing the used shell from /bin/sh to /bin/zsh. Don't ask me why that works, though.

setting the shell

ChaosCoder
  • 3,085
  • 1
  • 22
  • 23
Georg
  • 424
  • 6
  • 6
  • 1
    That seemed to work for me for a while and then it stopped working. Switching back to `/bin/sh` worked for a while again and then stopped working. Also, I noticed that Xcode 13 is always executing that step whereas Xcode 12 would skip it (based on the input and output list) – fphilipe Sep 24 '21 at 11:26
  • Also saw it stopped working. I thought it was a difference in Xcode and from command line (fastlane), but can not confirm. – ChaosCoder Sep 25 '21 at 13:08
-2

I've experienced the same issue in the WooCommerce iOS project the aggregate target we use to generate a .swift file with secrets at build time.

This PR seems to fix the issue. I say seems because I haven't experienced it since, but I am also not 100% sure that the issue is gone, because it wasn't occurring 100% of the builds for me.

What I think might have done the trick was making the build phase run script input and output files definition homogeneous. Previously, we set the inputs via .xcfilelist and outputs via the Xcode UI. Now we use a .xcfilelist for both.

Honestly, this seems like an Xcode bug to me

mokagio
  • 16,391
  • 3
  • 51
  • 58