Repository navigation
Description
Summary
Two related gaps in how xcode handles C and C++ sources, both on master at 354a163, which includes #199:
addSourceFiledoesn't know C or C++.FILETYPE_BY_EXTENSIONinlib/pbxFile.jshas noc,cpp,cc,cxxorhpp, and neitherGROUP_BY_FILETYPEnorENCODING_BY_FILETYPEknowssourcecode.c.corsourcecode.cpp.cpp. SoaddSourceFile('Plain.cpp', {}, group)writes the file reference withlastKnownFileType = unknownandfileEncoding = undefined, and comments its build filePlain.cpp in Resources, though it's added to the Sources build phase. This is whataddSourceFilerecords a.mmfile as typeunknown, in a build file named for Resources #198 reported for.mm, which feat: support objcpp source files ('mm' extensions) #199 fixed by mappingmmalone.removeSourceFilefinds the build phase entry by its comment. It deletes thePBXBuildFilewhosefileRef_commentis the file's name, but then looks in the Sources build phase for an entry whose comment islongComment(file),"<name> in <group>", with the group it computes for the file's type. For a C++ file that'sin Resources, so thePlain.cpp in Sourcesentry 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.mmadded before feat: support objcpp source files ('mm' extensions) #199 (commentedin Resources) and removed after it.
Steps to reproduce
- Download the gist https://gist.github.com/YevheniiKotyrlo/d95ec782d5b4a76269de87e5da337370 into an empty folder:
package.json, which depends onxcodefrom this repository at 354a163, andrepro.js. - 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
- Run
node repro.js full.pbxproj. It addsPlain.m,Plain.c,Plain.cpp,Plain.ccandPlain.cxxto theClassesgroup withaddSourceFileand prints the build file and file reference written for each. Then it rewrites each build file'sin Resourcescomment toin Sources, as Xcode comments a source in a target, removesAppDelegate.m(already in the project) andPlain.cppwithremoveSourceFile, 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
erisu commentedon Nov 2, 2026
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
hppfiles are added using theaddHeaderFileAPI. This also adds them toResource, which matches how thehextension files are handled.Could you take a look and confirm?
YevheniiKotyrlo commentedon Nov 2, 2026
Thanks! #201 fixes the
addSourceFilehalf of #200 for me; theremoveSourceFilehalf is still there.With the reproduction from the issue against #201 at 2b1d000,
Plain.cgetslastKnownFileType = sourcecode.c.candPlain.cpp,Plain.ccandPlain.cxxgetsourcecode.cpp.cpp, each withfileEncoding = 4and a build filein Sources. ItsremoveSourceFilestep passes too, but only because the commentremoveSourceFilelooks for is now the one Xcode writes: it still finds the Sources entry by comment, so a.cppadded by 4.0.0 (commentedin Resources) and removed with #201 leaves that entry in the Sources build phase, pointing at a build file that no longer exists.Resolves #200would close that along with the rest.On
hpp: yes,addHeaderFilegives a.hppexactly what it gives a.h, a file reference withfileEncoding = 4; lastKnownFileType = sourcecode.cpp.hin the group and no build file, soResourcesnever reaches the project. The new case intest/addHeaderFile.jscallsaddSourceFile('file.hpp')rather thanaddHeaderFile, though, so it also puts afile.hpp in Resourcesbuild file in the Sources build phase.