Skip to content

addSourceFile types .c, .cpp, .cc and .cxx files as unknown, in build files named for Resources, and removeSourceFile misses the Sources entry Xcode writes for them #200

Open

Description

@YevheniiKotyrlo

Summary

Two related gaps in how xcode handles C and C++ sources, both on master at 354a163, which includes #199:

  1. addSourceFile doesn't know C or C++. FILETYPE_BY_EXTENSION in lib/pbxFile.js has no c, cpp, cc, cxx or hpp, and neither GROUP_BY_FILETYPE nor ENCODING_BY_FILETYPE knows sourcecode.c.c or sourcecode.cpp.cpp. So addSourceFile('Plain.cpp', {}, group) writes the file reference with lastKnownFileType = unknown and fileEncoding = undefined, and comments its build file Plain.cpp in Resources, though it's added to the Sources build phase. This is what addSourceFile records a .mm file as type unknown, in a build file named for Resources #198 reported for .mm, which feat: support objcpp source files ('mm' extensions) #199 fixed by mapping mm alone.
  2. removeSourceFile finds the build phase entry by its comment. It deletes the PBXBuildFile whose fileRef_comment is the file's name, but then looks in the Sources build phase for an entry whose comment is longComment(file), "<name> in <group>", with the group it computes for the file's type. For a C++ file that's in Resources, so the Plain.cpp in Sources entry Xcode writes is left in the build phase, pointing at a build file that no longer exists. The same holds for any entry whose comment differs from the one the running version computes, such as a .mm added before feat: support objcpp source files ('mm' extensions) #199 (commented in Resources) and removed after it.

Steps to reproduce

  1. Download the gist https://gist.github.com/YevheniiKotyrlo/d95ec782d5b4a76269de87e5da337370 into an empty folder: package.json, which depends on xcode from this repository at 354a163, and repro.js.
  2. Add the repository's test project beside them and install:
    curl -sSfLO https://raw.githubusercontent.com/apache/cordova-node-xcode/354a1631d0a448531e4cc0aee8ca60c89262d1f1/test/parser/projects/full.pbxproj
    npm install
  3. Run node repro.js full.pbxproj. It adds Plain.m, Plain.c, Plain.cpp, Plain.cc and Plain.cxx to the Classes group with addSourceFile and prints the build file and file reference written for each. Then it rewrites each build file's in Resources comment to in Sources, as Xcode comments a source in a target, removes AppDelegate.m (already in the project) and Plain.cpp with removeSourceFile, and prints every build phase line still naming them.

Expected: Plain.c typed sourcecode.c.c, and Plain.cpp, Plain.cc and Plain.cxx typed sourcecode.cpp.cpp, each with fileEncoding = 4 and a build file in Sources, as Plain.m gets for .m. After the removal, nothing names Plain.cpp, as nothing names AppDelegate.m.

Actual: each C and C++ file is typed unknown with fileEncoding = undefined and a build file in Resources; after the removal, the Sources build phase keeps Plain.cpp in Sources, whose uuid is defined nowhere.

== 1. addSourceFile writes
C5FE06FC9FBA46658ACB3C78 /* Plain.m in Sources */ = {isa = PBXBuildFile; fileRef = 363B13F5832F4D92B1100D02 /* Plain.m */; };
363B13F5832F4D92B1100D02 /* Plain.m */ = {isa = PBXFileReference; name = "Plain.m"; path = "Plain.m"; sourceTree = "<group>"; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; explicitFileType = undefined; includeInIndex = 0; };
8F2E3FE4C1B74D0DB21B86C6 /* Plain.c in Resources */ = {isa = PBXBuildFile; fileRef = 7674D51FDAED4371A44A44A4 /* Plain.c */; };
7674D51FDAED4371A44A44A4 /* Plain.c */ = {isa = PBXFileReference; name = "Plain.c"; path = "Plain.c"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
CDBFE0E0BE964F059024A767 /* Plain.cpp in Resources */ = {isa = PBXBuildFile; fileRef = 9282A2464EEC4DCBBED1CB85 /* Plain.cpp */; };
9282A2464EEC4DCBBED1CB85 /* Plain.cpp */ = {isa = PBXFileReference; name = "Plain.cpp"; path = "Plain.cpp"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
73966221F0E449798EDDEF79 /* Plain.cc in Resources */ = {isa = PBXBuildFile; fileRef = 5A2F8BC90A9545B898F7F583 /* Plain.cc */; };
5A2F8BC90A9545B898F7F583 /* Plain.cc */ = {isa = PBXFileReference; name = "Plain.cc"; path = "Plain.cc"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
CACF95CFB35240C68D7F38E1 /* Plain.cxx in Resources */ = {isa = PBXBuildFile; fileRef = 53D0098FE6BF4A959BB42BE0 /* Plain.cxx */; };
53D0098FE6BF4A959BB42BE0 /* Plain.cxx */ = {isa = PBXFileReference; name = "Plain.cxx"; path = "Plain.cxx"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };

== 2. removeSourceFile, with each build file commented "in Sources" as Xcode writes it
AppDelegate.m: nothing names it
Plain.cpp: CDBFE0E0BE964F059024A767 /* Plain.cpp in Sources */,    <- CDBFE0E0BE964F059024A767 is defined nowhere

The uuids are random, so they differ from run to run. Node 26.12.1 on Windows 11; xcode from master at 354a163. Whether Xcode then compiles these files by their extension, as it does an untyped .mm, I haven't checked.

Reading

lib/pbxFile.js at 354a163: detectType returns DEFAULT_FILETYPE ('unknown') for an extension FILETYPE_BY_EXTENSION lacks, detectGroup returns DEFAULT_GROUP ('Resources') for a file type GROUP_BY_FILETYPE lacks, and defaultEncoding returns nothing for a file type ENCODING_BY_FILETYPE lacks.

lib/pbxProject.js at 354a163: removeSourceFile calls removeFromPbxBuildFileSection, which deletes every PBXBuildFile whose fileRef_comment is the file's basename and records its uuid on file.uuid, then removeFromPbxSourcesBuildPhase, which splices the first entry whose comment === longComment(file). Matching the entry by the uuid removeFromPbxBuildFileSection just found would remove it whatever its comment says. removeFromPbxResourcesBuildPhase and removeFromPbxFrameworksBuildPhase match by comment the same way.

Activity

  1. erisu commented on Nov 2, 2026

    @erisu
    Member

    Hey @YevheniiKotyrlo

    I submitted a PR to add the following extensions using the same approach as the previous PR.

    One thing I wanted to mention is that the hpp files are added using the addHeaderFile API. This also adds them to Resource, which matches how the h extension files are handled.

    Could you take a look and confirm?

  2. YevheniiKotyrlo commented on Nov 2, 2026

    @YevheniiKotyrlo
    Author

    Thanks! #201 fixes the addSourceFile half of #200 for me; the removeSourceFile half is still there.

    With the reproduction from the issue against #201 at 2b1d000, Plain.c gets lastKnownFileType = sourcecode.c.c and Plain.cpp, Plain.cc and Plain.cxx get sourcecode.cpp.cpp, each with fileEncoding = 4 and a build file in Sources. Its removeSourceFile step passes too, but only because the comment removeSourceFile looks for is now the one Xcode writes: it still finds the Sources entry by comment, so a .cpp added by 4.0.0 (commented in Resources) and removed with #201 leaves that entry in the Sources build phase, pointing at a build file that no longer exists. Resolves #200 would close that along with the rest.

    On hpp: yes, addHeaderFile gives a .hpp exactly what it gives a .h, a file reference with fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h in the group and no build file, so Resources never reaches the project. The new case in test/addHeaderFile.js calls addSourceFile('file.hpp') rather than addHeaderFile, though, so it also puts a file.hpp in Resources build file in the Sources build phase.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions