-
Notifications
You must be signed in to change notification settings - Fork 464
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 errors when using mutually recursive build settings #727
Changes from 4 commits
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 | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -6,6 +6,10 @@ module Object | |||||||||
# {PBXProject} or a {PBXNativeTarget}. | ||||||||||
# | ||||||||||
class XCBuildConfiguration < AbstractObject | ||||||||||
MUTUAL_RECURSION_SENTINEL = 'xcodeproj.mutual_recursion_sentinel'.freeze | ||||||||||
|
||||||||||
private_constant :MUTUAL_RECURSION_SENTINEL | ||||||||||
|
||||||||||
# @!group Attributes | ||||||||||
|
||||||||||
# @return [String] the name of the Target. | ||||||||||
|
@@ -79,16 +83,19 @@ def type | |||||||||
# the key of the build setting. | ||||||||||
# | ||||||||||
# @param [PBXNativeTarget] root_target | ||||||||||
# use this to resolve complete recursion between project and targets | ||||||||||
# use this to resolve complete recursion between project and targets. | ||||||||||
# | ||||||||||
# @param [String] previous_key | ||||||||||
# use this to resolve complete recursion between different build settings. | ||||||||||
# | ||||||||||
# @return [String] The value of the build setting | ||||||||||
# | ||||||||||
def resolve_build_setting(key, root_target = nil) | ||||||||||
def resolve_build_setting(key, root_target = nil, previous_key = nil) | ||||||||||
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. is it always sufficient to have a single previous key, or do we need a list of all of them? 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. Do you mean for cases like this: Xcodeproj/spec/project/object/build_configuration_spec.rb Lines 75 to 76 in 12cca46
? Case in which both Xcodeproj/spec/project/object/build_configuration_spec.rb Lines 122 to 123 in 12cca46
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. Or if you have another specific case in mind, I could make a test for it and see if it works. |
||||||||||
setting = build_settings[key] | ||||||||||
setting = resolve_variable_substitution(key, setting, root_target) | ||||||||||
setting = resolve_variable_substitution(key, setting, root_target, previous_key) | ||||||||||
|
||||||||||
config_setting = config[key] | ||||||||||
config_setting = resolve_variable_substitution(key, config_setting, root_target) | ||||||||||
config_setting = resolve_variable_substitution(key, config_setting, root_target, previous_key) | ||||||||||
|
||||||||||
project_setting = project.build_configuration_list[name] | ||||||||||
project_setting = nil if equal?(project_setting) | ||||||||||
|
@@ -99,6 +106,12 @@ def resolve_build_setting(key, root_target = nil) | |||||||||
'SRCROOT' => project.project_dir.to_s, | ||||||||||
} | ||||||||||
|
||||||||||
# if previous_key is nil, it means that we're back at the first call, so we can replace our sentinel string | ||||||||||
# used to prevent recursion with nil | ||||||||||
if previous_key.nil? && setting == MUTUAL_RECURSION_SENTINEL | ||||||||||
setting = nil | ||||||||||
end | ||||||||||
|
||||||||||
[defaults[key], project_setting, config_setting, setting, ENV[key]].compact.reduce(nil) do |inherited, value| | ||||||||||
expand_build_setting(value, inherited) | ||||||||||
end | ||||||||||
|
@@ -142,7 +155,7 @@ def expand_build_setting(build_setting_value, config_value) | |||||||||
build_setting_value.flat_map { |value| Constants::INHERITED_KEYWORDS.include?(value) ? inherited : value } | ||||||||||
end | ||||||||||
|
||||||||||
def resolve_variable_substitution(key, value, root_target) | ||||||||||
def resolve_variable_substitution(key, value, root_target, previous_key = nil) | ||||||||||
case value | ||||||||||
when Array | ||||||||||
return value.map { |v| resolve_variable_substitution(key, v, root_target) } | ||||||||||
|
@@ -168,9 +181,19 @@ def resolve_variable_substitution(key, value, root_target) | |||||||||
when key | ||||||||||
# to prevent infinite recursion | ||||||||||
nil | ||||||||||
when previous_key | ||||||||||
# to prevent infinite recursion; we don't return nil as for the self recursion because it | ||||||||||
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. missing some words at the end? 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. Dang, and I forgot what exactly I wanted to say. I pushed an update. |
||||||||||
MUTUAL_RECURSION_SENTINEL | ||||||||||
else | ||||||||||
configuration_to_resolve_against = root_target ? root_target.build_configuration_list[name] : self | ||||||||||
resolved_value_for_variable = configuration_to_resolve_against.resolve_build_setting(variable, root_target) || '' | ||||||||||
resolved_value_for_variable = configuration_to_resolve_against.resolve_build_setting(variable, root_target, key) || '' | ||||||||||
|
||||||||||
# we use this sentinel string instead of nil, because, otherwise, it would be swallowed by the default empty | ||||||||||
# string from the preceding line, and we want to distinguish between mutual recursion and other cases | ||||||||||
if resolved_value_for_variable == MUTUAL_RECURSION_SENTINEL | ||||||||||
return MUTUAL_RECURSION_SENTINEL | ||||||||||
end | ||||||||||
|
||||||||||
value = value.gsub(variable_reference, resolved_value_for_variable) | ||||||||||
resolve_variable_substitution(key, value, root_target) | ||||||||||
end | ||||||||||
|
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.
Might actually be more performant to make this
= Object.new.freeze
. Also, I think it can be aprivate_constant
?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.
Object.new.freeze
(actually::Object.new
), becauseString
specific methods are called on it, and so it crashes.private_constant
.