require'xcodeproj/project/object/file_system_synchronized_root_group'moduleXcodeprojclassProjectmoduleObject# Encapsulates the information a specific build configuration referenced# by a {XCConfigurationList} which in turn might be referenced by a# {PBXProject} or a {PBXNativeTarget}.#classXCBuildConfiguration<AbstractObjectMUTUAL_RECURSION_SENTINEL='xcodeproj.mutual_recursion_sentinel'.freezeprivate_constant:MUTUAL_RECURSION_SENTINEL# @!group Attributes# @return [String] the name of the configuration.#attribute:name,String# @return [Hash] the build settings to use for building the target.#attribute:build_settings,Hash,{}# @return [PBXFileReference] an optional file reference to a# configuration file (`.xcconfig`).#has_one:base_configuration_reference,PBXFileReference# @return [PBXFileSystemSynchronizedRootGroup] an optional reference to a group# synchronized with the file system that contains a configuration file (`.xcconfig`).## @note the configuration file relative path must be provided in `base_configuration_reference_relative_path`#has_one:base_configuration_reference_anchor,PBXFileSystemSynchronizedRootGroup# @return [String] the relative path of a configuration file (`.xcconfig`)# inside a group synchronized with the file system.## @note the configuration file group must be provided in `base_configuration_reference_anchor`#attribute:base_configuration_reference_relative_path,Stringpublic# @!group AbstractObject Hooks#---------------------------------------------------------------------## @return [Hash{String => Hash}] A hash suitable to display the object# to the user.#defpretty_printdata={}data['Build Settings']=sorted_build_settingsifbase_configuration_referencedata['Base Configuration']=base_configuration_reference.pretty_printend{name=>data}enddefto_hash_as(method=:to_hash)super.tapdo|hash|normalize_array_settings(hash['buildSettings'])endend# Sorts the build settings. Valid only in Ruby > 1.9.2 because in# previous versions the hash are not sorted.## @return [void]#defsort(_options=nil)self.build_settings=sorted_build_settingsend# @return [Boolean] Whether this configuration is configured for# debugging.#defdebug?gcc_preprocessor_definitions=resolve_build_setting('GCC_PREPROCESSOR_DEFINITIONS')gcc_preprocessor_definitions&&gcc_preprocessor_definitions.include?('DEBUG=1')end# @return [Symbol] The symbolic type of this configuration, either# `:debug` or `:release`.#deftypedebug??:debug::releaseend# @!group Helpers#---------------------------------------------------------------------## Gets the value for the given build setting considering any configuration# file present and resolving inheritance between them. It also takes in# consideration environment variables.## @param [String] key# the key of the build setting.## @param [PBXNativeTarget] root_target# 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#defresolve_build_setting(key,root_target=nil,previous_key=nil)setting=build_settings[key]setting=resolve_variable_substitution(key,setting,root_target,previous_key)config_setting=config[key]config_setting=resolve_variable_substitution(key,config_setting,root_target,previous_key)project_setting=project.build_configuration_list[name]project_setting=nilifequal?(project_setting)project_setting&&=project_setting.resolve_build_setting(key,root_target)defaults={'CONFIGURATION'=>name,'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 nilifprevious_key.nil?&&setting==MUTUAL_RECURSION_SENTINELsetting=nilend[defaults[key],project_setting,config_setting,setting,ENV[key]].compact.reduce(nil)do|inherited,value|expand_build_setting(value,inherited)endend#---------------------------------------------------------------------#privateVARIABLE_NAME_PATTERN='( # capture block
[_a-zA-Z0-9]+? # non-greedy lookup for everything contained in this list
)'.freezeprivate_constant:VARIABLE_NAME_PATTERNCAPTURE_VARIABLE_IN_BUILD_CONFIG=/
\$ # matches dollar sign literally
(?: # non-capturing group
[{] # matches a single character on this list
#{VARIABLE_NAME_PATTERN}
[}] # matches a single character on this list
| # or
[(] # matches a single character on this list
#{VARIABLE_NAME_PATTERN}
[)] # matches a single character on this list
)
/xprivate_constant:CAPTURE_VARIABLE_IN_BUILD_CONFIGdefexpand_build_setting(build_setting_value,config_value)ifbuild_setting_value.is_a?(Array)&&config_value.is_a?(String)config_value=split_build_setting_array_to_string(config_value)elsifbuild_setting_value.is_a?(String)&&config_value.is_a?(Array)build_setting_value=split_build_setting_array_to_string(build_setting_value)enddefault=build_setting_value.is_a?(String)?'':[]inherited=config_value||defaultreturnbuild_setting_value.gsub(Regexp.union(Constants::INHERITED_KEYWORDS),inherited)ifbuild_setting_value.is_a?Stringbuild_setting_value.flat_map{|value|Constants::INHERITED_KEYWORDS.include?(value)?inherited:value}enddefresolve_variable_substitution(key,value,root_target,previous_key=nil)casevaluewhenArrayreturnvalue.map{|v|resolve_variable_substitution(key,v,root_target)}whennilreturnwhenString# we know how to resolve strings!nilelseraiseArgumentError,"Settings values can only be nil, string, or array, got #{value.inspect} for #{key}"endunlessvariable_match_data=value.match(CAPTURE_VARIABLE_IN_BUILD_CONFIG)# no variables left, return the value unchangedreturnvalueendvariable_reference,variable=*variable_match_data.values_at(0,1,2).compactcasevariablewhen'inherited'# this is handled separately, after resolving all other variable referencesvaluewhenkey# to prevent infinite recursionnilwhenprevious_key# to prevent infinite recursion; we don't return nil as for the self recursion because it needs to be# distinguished outside this method tooMUTUAL_RECURSION_SENTINELelseconfiguration_to_resolve_against=root_target?root_target.build_configuration_list[name]:selfresolved_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 casesifresolved_value_for_variable==MUTUAL_RECURSION_SENTINELreturnMUTUAL_RECURSION_SENTINELendvalue=value.gsub(variable_reference,resolved_value_for_variable)resolve_variable_substitution(key,value,root_target)endenddefsorted_build_settingssorted={}build_settings.keys.sort.eachdo|key|sorted[key]=build_settings[key]endsortedenddefnormalize_array_settings(settings)returnunlesssettingsarray_settings=BuildSettingsArraySettingsByObjectVersion[project.object_version]settings.keys.eachdo|key|nextunlessvalue=settings[key]stripped_key=key.sub(/\[[^\]]+\]$/,'')casevaluewhenStringnextunlessarray_settings.include?(stripped_key)array_value=split_build_setting_array_to_string(value)nextunlessarray_value.size>1settings[key]=array_valuewhenArraynextifvalue.size>1&&array_settings.include?(stripped_key)settings[key]=value.join(' ')endendenddefsplit_build_setting_array_to_string(string)regexp=/ *((['"]?).*?[^\\]\2)(?=( |\z))/string.scan(regexp).map(&:first)enddefconfigreturn{}unlessbase_configuration_reference@config||=ifbase_configuration_reference.real_path.exist?Xcodeproj::Config.new(base_configuration_reference.real_path).to_hash.tapdo|hash|normalize_array_settings(hash)endelse{}endend#---------------------------------------------------------------------#endendendendrequire'xcodeproj/project/object/helpers/build_settings_array_settings_by_object_version'