Skip to content

Commit 45acef1

Browse files
authored
Merge pull request mesonbuild#8787 from mesonbuild/xcodeprojecttree
Rework Xcode project navigation tree
2 parents df960b0 + e23fd08 commit 45acef1

File tree

1 file changed

+112
-54
lines changed

1 file changed

+112
-54
lines changed

mesonbuild/backend/xcodebackend.py

Lines changed: 112 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@
5858
BOOL2XCODEBOOL = {True: 'YES', False: 'NO'}
5959
LINKABLE_EXTENSIONS = {'.o', '.a', '.obj', '.so', '.dylib'}
6060

61+
class FileTreeEntry:
62+
63+
def __init__(self):
64+
self.subdirs = {}
65+
self.targets = []
66+
6167
class PbxItem:
6268
def __init__(self, value, comment = ''):
6369
self.value = value
@@ -267,6 +273,7 @@ def generate(self):
267273
self.generate_pbxdep_map()
268274
self.generate_containerproxy_map()
269275
self.generate_target_file_maps()
276+
self.generate_build_file_maps()
270277
self.proj_dir = os.path.join(self.environment.get_build_dir(), self.build.project_name + '.xcodeproj')
271278
os.makedirs(self.proj_dir, exist_ok=True)
272279
self.proj_file = os.path.join(self.proj_dir, 'project.pbxproj')
@@ -321,7 +328,6 @@ def get_xcodetype(self, fname):
321328
xcodetype = XCODETYPEMAP.get(fname.split('.')[-1].lower())
322329
if not xcodetype:
323330
xcodetype = 'sourcecode.unknown'
324-
mlog.warning(f'Unknown file type "{fname}" fallbacking to "{xcodetype}". Xcode project might be malformed.')
325331
return xcodetype
326332

327333
def generate_filemap(self):
@@ -509,6 +515,12 @@ def generate_target_file_maps_impl(self, targets):
509515
else:
510516
raise RuntimeError('Unknown input type ' + str(o))
511517

518+
def generate_build_file_maps(self):
519+
for buildfile in self.interpreter.get_build_def_files():
520+
assert(isinstance(buildfile, str))
521+
self.buildfile_ids[buildfile] = self.gen_id()
522+
self.fileref_ids[buildfile] = self.gen_id()
523+
512524
def generate_source_phase_map(self):
513525
self.source_phase = {}
514526
for t in self.build_targets:
@@ -825,6 +837,18 @@ def generate_pbx_file_reference(self, objects_dict):
825837
custom_dict.add_item('sourceTree', 'SOURCE_ROOT')
826838
objects_dict.add_item(self.custom_target_output_fileref[o], custom_dict)
827839

840+
for buildfile in self.interpreter.get_build_def_files():
841+
basename = os.path.split(buildfile)[1]
842+
buildfile_dict = PbxDict()
843+
typestr = self.get_xcodetype(buildfile)
844+
buildfile_dict.add_item('isa', 'PBXFileReference')
845+
buildfile_dict.add_item('explicitFileType', '"' + typestr + '"')
846+
buildfile_dict.add_item('name', f'"{basename}"')
847+
buildfile_dict.add_item('path', f'"{buildfile}"')
848+
buildfile_dict.add_item('refType', 0)
849+
buildfile_dict.add_item('sourceTree', 'SOURCE_ROOT')
850+
objects_dict.add_item(self.fileref_ids[buildfile], buildfile_dict)
851+
828852
def generate_pbx_frameworks_buildphase(self, objects_dict):
829853
for t in self.build_targets.values():
830854
bt_dict = PbxDict()
@@ -848,33 +872,22 @@ def generate_pbx_group(self, objects_dict):
848872
for t in self.custom_targets:
849873
groupmap[t] = self.gen_id()
850874
target_src_map[t] = self.gen_id()
851-
sources_id = self.gen_id()
875+
projecttree_id = self.gen_id()
852876
resources_id = self.gen_id()
853877
products_id = self.gen_id()
854-
frameworks_id = self.gen_id()
878+
frameworks_id = self.gen_id()
855879
main_dict = PbxDict()
856880
objects_dict.add_item(self.maingroup_id, main_dict)
857881
main_dict.add_item('isa', 'PBXGroup')
858882
main_children = PbxArray()
859883
main_dict.add_item('children', main_children)
860-
main_children.add_item(sources_id, 'Sources')
884+
main_children.add_item(projecttree_id, 'Project tree')
861885
main_children.add_item(resources_id, 'Resources')
862886
main_children.add_item(products_id, 'Products')
863887
main_children.add_item(frameworks_id, 'Frameworks')
864888
main_dict.add_item('sourceTree', '"<group>"')
865889

866-
# Sources
867-
source_dict = PbxDict()
868-
objects_dict.add_item(sources_id, source_dict, 'Sources')
869-
source_dict.add_item('isa', 'PBXGroup')
870-
source_children = PbxArray()
871-
source_dict.add_item('children', source_children)
872-
for t in self.build_targets:
873-
source_children.add_item(groupmap[t], t)
874-
for t in self.custom_targets:
875-
source_children.add_item(groupmap[t], t)
876-
source_dict.add_item('name', 'Sources')
877-
source_dict.add_item('sourceTree', '"<group>"')
890+
self.add_projecttree(objects_dict, projecttree_id)
878891

879892
resource_dict = PbxDict()
880893
objects_dict.add_item(resources_id, resource_dict, 'Resources')
@@ -900,44 +913,6 @@ def generate_pbx_group(self, objects_dict):
900913
frameworks_dict.add_item('name', 'Frameworks')
901914
frameworks_dict.add_item('sourceTree', '"<group>"')
902915

903-
# Targets
904-
for tname, t in self.build_targets.items():
905-
target_dict = PbxDict()
906-
objects_dict.add_item(groupmap[tname], target_dict, tname)
907-
target_dict.add_item('isa', 'PBXGroup')
908-
target_children = PbxArray()
909-
target_dict.add_item('children', target_children)
910-
target_children.add_item(target_src_map[tname], 'Source files')
911-
if t.subproject:
912-
target_dict.add_item('name', f'"{t.subproject}{t}"')
913-
else:
914-
target_dict.add_item('name', f'"{t}"')
915-
target_dict.add_item('sourceTree', '"<group>"')
916-
source_files_dict = PbxDict()
917-
objects_dict.add_item(target_src_map[tname], source_files_dict, 'Source files')
918-
source_files_dict.add_item('isa', 'PBXGroup')
919-
source_file_children = PbxArray()
920-
source_files_dict.add_item('children', source_file_children)
921-
for s in t.sources:
922-
if isinstance(s, mesonlib.File):
923-
s = os.path.join(s.subdir, s.fname)
924-
elif isinstance(s, str):
925-
s = os.path.joni(t.subdir, s)
926-
else:
927-
continue
928-
source_file_children.add_item(self.fileref_ids[(tname, s)], s)
929-
for o in t.objects:
930-
if isinstance(o, build.ExtractedObjects):
931-
# Do not show built object files in the project tree.
932-
continue
933-
if isinstance(o, mesonlib.File):
934-
o = os.path.join(o.subdir, o.fname)
935-
else:
936-
o = os.path.join(t.subdir, o)
937-
source_file_children.add_item(self.fileref_ids[(tname, o)], o)
938-
source_files_dict.add_item('name', '"Source files"')
939-
source_files_dict.add_item('sourceTree', '"<group>"')
940-
941916
for tname, t in self.custom_targets.items():
942917
target_dict = PbxDict()
943918
objects_dict.add_item(groupmap[tname], target_dict, tname)
@@ -977,6 +952,89 @@ def generate_pbx_group(self, objects_dict):
977952
product_dict.add_item('name', 'Products')
978953
product_dict.add_item('sourceTree', '"<group>"')
979954

955+
def write_group_target_entry(self, objects_dict, t):
956+
tid = t.get_id()
957+
group_id = self.gen_id()
958+
target_dict = PbxDict()
959+
objects_dict.add_item(group_id, target_dict, tid)
960+
target_dict.add_item('isa', 'PBXGroup')
961+
target_children = PbxArray()
962+
target_dict.add_item('children', target_children)
963+
target_dict.add_item('name', f'"{t} · target"')
964+
target_dict.add_item('sourceTree', '"<group>"')
965+
source_files_dict = PbxDict()
966+
for s in t.sources:
967+
if isinstance(s, mesonlib.File):
968+
s = os.path.join(s.subdir, s.fname)
969+
elif isinstance(s, str):
970+
s = os.path.joni(t.subdir, s)
971+
else:
972+
continue
973+
target_children.add_item(self.fileref_ids[(tid, s)], s)
974+
for o in t.objects:
975+
if isinstance(o, build.ExtractedObjects):
976+
# Do not show built object files in the project tree.
977+
continue
978+
if isinstance(o, mesonlib.File):
979+
o = os.path.join(o.subdir, o.fname)
980+
else:
981+
o = os.path.join(t.subdir, o)
982+
target_children.add_item(self.fileref_ids[(tid, o)], o)
983+
source_files_dict.add_item('name', '"Source files"')
984+
source_files_dict.add_item('sourceTree', '"<group>"')
985+
return group_id
986+
987+
def add_projecttree(self, objects_dict, projecttree_id):
988+
root_dict = PbxDict()
989+
objects_dict.add_item(projecttree_id, root_dict, "Root of project tree")
990+
root_dict.add_item('isa', 'PBXGroup')
991+
target_children = PbxArray()
992+
root_dict.add_item('children', target_children)
993+
root_dict.add_item('name', '"Project root"')
994+
root_dict.add_item('sourceTree', '"<group>"')
995+
996+
project_tree = self.generate_project_tree()
997+
self.write_tree(objects_dict, project_tree, target_children, '')
998+
999+
def write_tree(self, objects_dict, tree_node, children_array, current_subdir):
1000+
subdir_dict = PbxDict()
1001+
subdir_children = PbxArray()
1002+
for subdir_name, subdir_node in tree_node.subdirs.items():
1003+
subdir_id = self.gen_id()
1004+
objects_dict.add_item(subdir_id, subdir_dict)
1005+
children_array.add_item(subdir_id)
1006+
subdir_dict.add_item('isa', 'PBXGroup')
1007+
subdir_dict.add_item('children', subdir_children)
1008+
subdir_dict.add_item('name', f'"{subdir_name}"')
1009+
subdir_dict.add_item('sourceTree', '"<group>"')
1010+
self.write_tree(objects_dict, subdir_node, subdir_children, os.path.join(current_subdir, subdir_name))
1011+
for target in tree_node.targets:
1012+
group_id = self.write_group_target_entry(objects_dict, target)
1013+
children_array.add_item(group_id)
1014+
potentials = [os.path.join(current_subdir, 'meson.build'),
1015+
os.path.join(current_subdir, 'meson_options.txt')]
1016+
for bf in potentials:
1017+
i = self.fileref_ids.get(bf, None)
1018+
if i:
1019+
children_array.add_item(i)
1020+
1021+
1022+
def generate_project_tree(self):
1023+
tree_info = FileTreeEntry()
1024+
for tname, t in self.build_targets.items():
1025+
self.add_target_to_tree(tree_info, t)
1026+
return tree_info
1027+
1028+
def add_target_to_tree(self, tree_root, t):
1029+
current_node = tree_root
1030+
path_segments = t.subdir.split('/')
1031+
for s in path_segments:
1032+
if not s:
1033+
continue
1034+
if s not in current_node.subdirs:
1035+
current_node.subdirs[s] = FileTreeEntry()
1036+
current_node = current_node.subdirs[s]
1037+
current_node.targets.append(t)
9801038

9811039
def generate_pbx_native_target(self, objects_dict):
9821040
for tname, idval in self.native_targets.items():

0 commit comments

Comments
 (0)