-
-
Notifications
You must be signed in to change notification settings - Fork 294
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: support adding and removing Localizable.strings file #356
base: master
Are you sure you want to change the base?
Changes from all commits
fd6eca6
a9187b4
16a373c
be404e5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -145,14 +145,15 @@ class ProjectFiles: | |
def __init__(self): | ||
raise EnvironmentError('This class cannot be instantiated directly, use XcodeProject instead') | ||
|
||
def add_file(self, path, parent=None, tree=TreeType.SOURCE_ROOT, target_name=None, force=True, | ||
def add_file(self, path, name=None, parent=None, tree=TreeType.SOURCE_ROOT, target_name=None, force=True, | ||
file_options=FileOptions()): | ||
""" | ||
Adds a file to the project, taking care of the type of the file and creating additional structures depending on | ||
the file type. For instance, frameworks will be linked, embedded and search paths will be adjusted automatically. | ||
Header file will be added to the headers sections, but not compiled, whereas the source files will be added to | ||
the compilation phase. | ||
:param path: Path to the file to be added | ||
:param name: Optional custom name of the file, if None is passed then "path" will be used. | ||
:param parent: Parent group to be added under | ||
:param tree: Tree where the path is relative to | ||
:param target_name: Target name or list of target names where the file should be added (none for every target) | ||
|
@@ -167,7 +168,7 @@ def add_file(self, path, parent=None, tree=TreeType.SOURCE_ROOT, target_name=Non | |
if target_name.__len__() == 0: | ||
return [] | ||
|
||
file_ref, abs_path, path, tree, expected_build_phase = self._add_file_reference(path, parent, tree, force, | ||
file_ref, abs_path, path, tree, expected_build_phase = self._add_file_reference(path, name, parent, tree, force, | ||
file_options) | ||
if path is None or tree is None: | ||
return None | ||
|
@@ -199,7 +200,7 @@ def _filter_targets_without_path(self, path, target_name): | |
build_phase = self.get_object(build_phase_id) | ||
for build_file_id in build_phase.files: | ||
build_file = self.get_object(build_file_id) | ||
if build_file is None: | ||
if build_file is None or not hasattr(build_file, 'fileRef'): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. More |
||
continue | ||
|
||
file_ref = self.get_object(build_file.fileRef) | ||
|
@@ -236,7 +237,7 @@ def add_project(self, path, parent=None, tree=TreeType.GROUP, target_name=None, | |
if not force and self._file_exists(path): | ||
return [] | ||
|
||
file_ref, _, path, tree, expected_build_phase = self._add_file_reference(path, parent, tree, force, | ||
file_ref, _, path, tree, expected_build_phase = self._add_file_reference(path, None, parent, tree, force, | ||
file_options) | ||
if path is None or tree is None: | ||
return None | ||
|
@@ -360,7 +361,8 @@ def remove_file_by_id(self, file_id, target_name=None): | |
return True | ||
|
||
# remove the file from any groups if there is no reference from any target | ||
for group in filter(lambda x: file_ref.get_id() in x.children, self.objects.get_objects_in_section('PBXGroup')): | ||
for group in filter(lambda x: file_ref.get_id() in x.children, | ||
self.objects.get_objects_in_section('PBXGroup', 'PBXVariantGroup')): | ||
Comment on lines
+364
to
+365
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since I haven't decided which one's the best way, so I'm happy to hear. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Related to Variant groups, there is an unmerged branch about adding support for it. The thing is, variant groups are not trivial, multiple operations need to be carried out in order for xcode to deal with the strings properly. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kronenthaler You're right, the code in this pull request requires the user (the other developers) to know what they're doing. It's not possible "just" add a Localizable.strings, but the multiple operations needs to be called. Would you mind sharing the branch so we can move it forward? I don't completely understand this project so I'm depending a lot on trial and error and relying on unit tests to cut my way through. If there's a more designed solution, I'm happy to help. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kronenthaler Hi! Thanks for your help and review so far. I would like to move forward with the
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have just checked the related work so far like https://github.com/kronenthaler/mod-pbxproj/pull/124/files and master...variant-groups and I'm looking forward to learn more about them once you think they're ready. From what I see you're going with the cleaner approach of providing an API that fully supports the I hate to admit this again, but I'm developing this on a Linux machine and it's my first encounter of pbx project files which doesn't help me to get the most suitable solution. |
||
group.remove_child(file_ref) | ||
|
||
# the file is not referenced in any build file, remove it | ||
|
@@ -412,8 +414,8 @@ def add_folder(self, path, parent=None, excludes=None, recursive=True, create_gr | |
# add the top folder as a group, make it the new parent | ||
path = os.path.abspath(path) | ||
if not create_groups and os.path.splitext(path)[1] not in ProjectFiles._SPECIAL_FOLDERS: | ||
return self.add_file(path, parent, target_name=target_name, force=False, tree=TreeType.GROUP, | ||
file_options=file_options) | ||
return self.add_file(path, parent=parent, target_name=target_name, | ||
force=False, tree=TreeType.GROUP, file_options=file_options) | ||
|
||
parent = self.get_or_create_group(os.path.split(path)[1], path, parent, make_relative=file_options.add_groups_relative) | ||
|
||
|
@@ -428,8 +430,8 @@ def add_folder(self, path, parent=None, excludes=None, recursive=True, create_gr | |
if os.path.isfile(full_path) or os.path.splitext(child)[1] in ProjectFiles._SPECIAL_FOLDERS or \ | ||
not create_groups: | ||
# check if the file exists already, if not add it | ||
children = self.add_file(full_path, parent, target_name=target_name, force=False, tree=TreeType.GROUP, | ||
file_options=file_options) | ||
children = self.add_file(full_path, parent=parent, target_name=target_name, | ||
force=False, tree=TreeType.GROUP, file_options=file_options) | ||
else: | ||
# if recursive is true, go deeper, otherwise create the group here. | ||
if recursive: | ||
|
@@ -572,14 +574,14 @@ def add_package_dependency(self, target_name, create_parameters=()): | |
|
||
# miscellaneous functions, candidates to be extracted and decouple implementation | ||
|
||
def _add_file_reference(self, path, parent, tree, force, file_options): | ||
def _add_file_reference(self, path, name, parent, tree, force, file_options): | ||
# decide the proper tree and path to add | ||
abs_path, path, tree = ProjectFiles._get_path_and_tree(self._source_root, path, tree) | ||
if path is None or tree is None: | ||
return None, abs_path, path, tree, None | ||
|
||
# create a PBXFileReference for the new file | ||
file_ref = PBXFileReference.create(path, tree) | ||
file_ref = PBXFileReference.create(path, name, tree) | ||
|
||
# determine the type of the new file: | ||
file_type, expected_build_phase = ProjectFiles._determine_file_type(file_ref, file_options.ignore_unknown_type) | ||
|
@@ -637,7 +639,7 @@ def _create_build_products(self, product_ref, target_name): | |
|
||
@classmethod | ||
def _determine_file_type(cls, file_ref, unknown_type_allowed): | ||
ext = os.path.splitext(file_ref.get_name())[1] | ||
ext = os.path.splitext(file_ref.path)[1] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Name is customizable, so it's better to use |
||
if os.path.isdir(os.path.abspath(file_ref.path)) and ext not in ProjectFiles._SPECIAL_FOLDERS: | ||
file_type = 'folder' | ||
build_phase = 'PBXResourcesBuildPhase' | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
from pbxproj.pbxsections import PBXFileReference, PBXGroup | ||
|
||
|
||
class PBXVariantGroup(PBXGroup): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have added this class and everything works just magically. I have no idea how the parser works. Super magic @kronenthaler! Where's the actual string parser code though? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Neat!
|
||
def add_child(self, child): | ||
# Note: PBXVariantGroup is typically used for Localizable.strings | ||
# If other children type is needed e.g. PBXFileReference, edit this | ||
# function. | ||
if not isinstance(child, PBXFileReference): | ||
return False | ||
|
||
self.children.append(child.get_id()) | ||
return True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kronenthaler I was wondering if
name
should be added toFileOptions
instead?Let me know, I'm happy to refactor it this way.
I suppose this way we'll be much more backward compatible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will need to take a deeper look. At first glance, it should not break backwards compatibility given that provides a default value (and its management is consistent)
The deeper question i have is why is this necessary? Xcode infers the name of the file from the path, and that is what it adds to the project.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kronenthaler I need to take a deeper look as well myself 😅, I developed this on a Linux machine. It's like developing with your eyes closed.
I sent the original picture to my colleague (in another country) so he test and I found the following:
You can see the conversation here: openedx/openedx-app-ios#465 (comment)
So after a couple of attempts I noticed that XCode won't detect the translation files unless they're in the PBXVariantGroup and the name of the would be set to the locale instead of the path. Therefore, I chose to override the name and make it different.
My question, does it make sense to add the name in
FileOptions
? to ensure much more robust backward compatibility or the function parameter makes more sense?