Skip to content

addSourceFile types .c, .cpp, .cc and .cxx files as unknown, in build files named for Resources #200

Closed

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.

  3. erisu commented on Nov 4, 2026

    @erisu
    Member

    I would probably suggest splitting this ticket into two separate tickets to make it easier to track each item independently.


    Ticket 1: Supporting new file extensions

    I believe this task has been completed by the PR I submitted earlier and would be considered a feature request.

    I also updated the PR test case to correctly use addHeaderFile, as you pointed out. Thanks for bringing that to my attention!


    Ticket 2: removeSourceFile...

    I might need more context around this so I can try to better understand the issue.
    To me, it sounds like you're reporting a bug within removeSourceFile, correct?

    If it is a bug, I think it would be helpful to track it separately from the feature request.

    Looking at the sample gist code you provided, I noticed one thing that might be contributing to the behavior you're reporting.

    Based on the comment:

    // npm install, then: node repro.js full.pbxproj (the repository's test/parser/projects/full.pbxproj)

    I am assuming you're using the full.pbxproj file for testing.

    On line 23 of your gist code, you're trying to locate the PBXGroup of Classes:

    const classes = added.findPBXGroupKey({ name: 'Classes' });

    The project file does not have a PBXGroup with a property name with the value of Classes, but it does have one with the property path with the value of Classes:

    https://github.com/apache/cordova-node-xcode/blob/master/test/parser/projects/full.pbxproj#L127-L135

    Since the lookup criteria is using name rather than path, it would return undefined. Updating the criteria to use path should allow it to retrieve the correct ID:

    const classes = added.findPBXGroupKey({ path: 'Classes' });

    I am wondering if this is causing the behavior you're observing.

    Additionally, if your testing against the feature request PR, I don't think the test script needs the replace all "in Resources" to "in Sources" section anymore.

    I ran a test with the changed criteria and noticed that the 'AppDelegate.m' and 'Plain.cpp' entries no longer exist in the final output, as expected.

    Hopefully, updating the findPBXGroupKey criteria is the solution for what you're trying to do.

  4. xavierserrai commented on Nov 4, 2026

    @xavierserrai

    Dears,

    The same happens when adding a xcframework using the

    <framework src="framework.xcframework" custom="true" embed="true" />

    tag in config.xml from Apache cordova ios.

    That tag, produces:

    AF4CFEAF2B3542F2807AB2F5 /* framework.xcframework */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = framework.xcframework; path = "path/to/framework.xcframework"; sourceTree = "<group>"; };

    Setting lastKnownFileType = unknown when it should be wrapper.xcframework

    Seems that in pbxFile.js, the FILETYPE_BY_EXTENSION lacks the xcframework extension.

    Also, in pbxProject.js, there are no references to xcframework but to plain framework.

    Thanks,

  5. YevheniiKotyrlo commented on Nov 4, 2026

    @YevheniiKotyrlo
    Author

    Yes, it's a bug in removeSourceFile, and the lookup isn't what causes it. You're right that findPBXGroupKey({ name: 'Classes' }) finds nothing in full.pbxproj, whose Classes group has only a path, so the gist passed no group and the files went into Plugins; I've fixed the gist to look it up by path. But removeFromPbxSourcesBuildPhase finds the entry by longComment(file), and the in Sources or in Resources in that comment comes from the file's type, not from the group it's added to: with the fixed lookup on master at 354a163, Plain.cpp in Sources still stays in the Sources build phase, pointing at a build file that's gone. With #201 the gist's removal passes because the comment it computes becomes the one in the project.

    I've split it out as #202, reproduced through the calls cordova-ios makes for a plugin's <source-file>, which pass no group either, and without rewriting any comment: a .mm added by 4.0.0 and removed by master leaves its Plain.mm in Resources entry behind. That leaves #200, retitled to match, as the extension half that #201 resolves. Thanks for switching the .hpp case to addHeaderFile.

  6. changed the title [-]`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[/-] [+]`addSourceFile` types `.c`, `.cpp`, `.cc` and `.cxx` files as `unknown`, in build files named for Resources[/+] on Nov 4, 2026
  7. self-assigned this
    on Nov 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

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