<commit_before>require 'xcodeproj'
require 'yaml'
require 'paint'
require 'deep_clone'
require_relative '../spec/spec_file'

module Xcodegen
	class XcodeprojWriter
		CONFIG_PROFILE_PATH = File.join(__dir__, '..', '..', 'res', 'config_profiles')
		TARGET_CONFIG_PROFILE_PATH = File.join(__dir__, '..', '..', 'res', 'target_config_profiles')

		# Sourced from Cocoapods:Xcodeproj project. This should be kept up to date with that gem.
		PRODUCT_TYPE_UTI_INV = {
			'com.apple.product-type.application' => :application,
			'com.apple.product-type.framework' => :framework,
			'com.apple.product-type.library.dynamic' => :dynamic_library,
			'com.apple.product-type.library.static' => :static_library,
			'com.apple.product-type.bundle' => :bundle,
			'com.apple.product-type.bundle.unit-test' => :unit_test_bundle,
			'com.apple.product-type.bundle.ui-testing' => :ui_test_bundle,
			'com.apple.product-type.app-extension' => :app_extension,
			'com.apple.product-type.tool' => :command_line_tool,
			'com.apple.product-type.application.watchapp' => :watch_app,
			'com.apple.product-type.application.watchapp2' => :watch2_app,
			'com.apple.product-type.watchkit-extension' => :watch_extension,
			'com.apple.product-type.watchkit2-extension' => :watch2_extension,
			'com.apple.product-type.tv-app-extension' => :tv_extension,
			'com.apple.product-type.application.messages' => :messages_application,
			'com.apple.product-type.app-extension.messages' => :messages_extension,
			'com.apple.product-type.app-extension.messages-sticker-pack' => :sticker_pack,
			'com.apple.product-type.xpc-service' => :xpc_service
		}.freeze

		# @param spec [Xcodegen::Specfile]
		# @param destination [String]
		def self.write(source_spec, destination)
			# Create a clone of the spec to avoid affecting the original referenced object
			# noinspection RubyResolve
			spec = Marshal.load(Marshal.dump(source_spec))

			unless spec != nil and spec.is_a? Xcodegen::Specfile
				raise StandardError.new 'Invalid spec file'
			end

			unless spec.configurations.length > 0
				raise StandardError.new 'Spec must have at least one configuration'
			end

			if spec.variants.count == 0
				write_xcodeproj spec, File.join(destination, 'project.xcodeproj')
			else
				# Generate a derived spec for each variant and write out the variants
				specs = spec.variants.map { |variant|
					next nil if variant.abstract

					variant_targets = DeepClone.clone variant.targets
					spec_targets = DeepClone.clone spec.targets

					variant_targets.each { |target|
						spec_target = spec_targets.find { |st| st.name == target.name }
						next if spec_target == nil

						spec_target.source_dir = spec_target.source_dir.unshift(*target.source_dir).uniq
						spec_target.res_dir = spec_target.res_dir.unshift(*target.res_dir).uniq
						spec_target.file_excludes = [].unshift(*spec_target.file_excludes).unshift(*target.file_excludes).uniq

						target.configurations.each { |configuration|
							spec_config = spec_target.configurations.find { |sc| sc.name == configuration.name }
							spec_config.settings.merge! configuration.settings
							spec_config.profiles = [].unshift(*configuration.profiles).unshift(*spec_config.profiles).uniq
						}

						spec_target.file_excludes = [].unshift(*spec_target.file_excludes).unshift(*target.file_excludes)
						spec_target.options = [].unshift(*spec_target.options).unshift(*target.options)
						spec_target.references = [].unshift(*spec_target.references).unshift(*target.references)
					}

					[variant.name, Xcodegen::Specfile.new(spec.version, spec_targets, spec.configurations, [], spec.base_dir)]
				}.compact.to_h

				specs.each { |name, variant_spec|
					if name == '$base'
						write_xcodeproj variant_spec, File.join(destination, 'project.xcodeproj')
					else
						write_xcodeproj variant_spec, File.join(destination, "#{name.downcase.gsub(/[^0-9a-z]/, '')}.xcodeproj")
					end
				}
			end
		end

		# @param target [Xcodegen::Specfile::Target]
		# @param project [Xcodeproj::Project]
		# @param target_refs [Hash<String, Xcodeproj::PBXNativeTarget>]
		# @param spec_configuration_type_map [Hash<String, String>]
		# @return [Xcodeproj::PBXNativeTarget]
		def self.add_target(target, project, target_refs, spec_configuration_type_map)
			requested_target_refs = target.references.select { |ref| ref.is_a? Xcodegen::Specfile::Target::TargetReference }
			target_references = requested_target_refs.map { |ref|
				# If the referenced target has not been added to the project yet, skip this target for now
				unless target_refs.has_key? ref.target_name
					return nil
				end

				next target_refs[ref.target_name]
			}.compact

			if requested_target_refs.length != target_references.length
				puts Paint["Warning: Not all target references could be resolved for target: '#{target.name}'.", :yellow]
			end

			target_build_settings = {}
			target.configurations.each { |config|
				build_settings = {}
				config.profiles.map { |profile_name|
					[profile_name, File.join(TARGET_CONFIG_PROFILE_PATH, "#{profile_name.sub(':', '_')}.yml")]
				}.map { |data|
					profile_name, profile_file_name = data
					unless File.exist? profile_file_name
						puts Paint["Warning: unrecognised project configuration profile '#{profile_name}'. Ignoring...", :yellow]
						next nil
					end

					next YAML.load_file(profile_file_name)
				}.each { |profile_data|
					build_settings = build_settings.merge (profile_data || {})
				}

				build_settings = build_settings.merge config.settings
				target_build_settings[config.name] = { :type => spec_configuration_type_map[config.name], :settings => build_settings }
			}

			sdk = target_build_settings[target_build_settings.keys.first][:settings]['SDKROOT']

			unless sdk != nil
				puts Paint["Warning: SDKROOT not found in configuration for target: '#{target.name}'. Ignoring...", :yellow]
				return nil
			end

			if sdk.include? 'iphoneos'
				platform = :ios
			elsif sdk.include? 'macosx'
				platform = :osx
			elsif sdk.include? 'appletvos'
				platform = :tvos
			elsif sdk.include? 'watchos'
				platform = :watchos
			else
				puts Paint["Warning: SDKROOT #{build_settings['SDKROOT']} not recognised in configuration for target: '#{target.name}'. Ignoring...", :yellow]
				return nil
			end

			native_target = project.new_target PRODUCT_TYPE_UTI_INV[target.type], target.name, platform, nil, nil, :swift
			native_target.build_configurations.clear
			target_build_settings.each { |name, data|
				config = native_target.add_build_configuration(name, data[:type])
				config.build_settings = data[:settings]
			}

			target_references.each { |native_ref|
				native_target.add_dependency native_ref
			}

			requested_sys_framework_refs = target.references
				.select { |ref| ref.is_a? Xcodegen::Specfile::Target::SystemFrameworkReference }
				.map { |ref| ref.name }
				.select { |ref| ref != 'Foundation' } # Filter out Foundation as it's already added by default
			native_target.add_system_framework requested_sys_framework_refs

			requested_sys_library_refs = target.references
				.select { |ref| ref.is_a? Xcodegen::Specfile::Target::SystemLibraryReference }
				.map { |ref| ref.name }
			native_target.add_system_library requested_sys_library_refs

			subproj_group = project.frameworks_group.groups.find { |g| g.display_name == '$subproj' }
			if subproj_group == nil
				 subproj_group = project.frameworks_group.new_group '$subproj', nil, '<group>'
			end
			framework_group = project.frameworks_group.groups.find { |group| group.display_name == '$local' }
			if framework_group == nil
				framework_group = project.frameworks_group.new_group '$local', nil, '<group>'
			end
			target.references.select { |ref| ref.is_a? Xcodegen::Specfile::Target::FrameworkReference }.each { |f|
				subproj = subproj_group.new_file f.project_path
				remote_project = Xcodeproj::Project.open f.project_path

				f.settings['frameworks'].map { |f_opts|
					remote_target = remote_project.targets.select { |t|
						t.product_reference.path == f_opts['name'] and t.product_type === 'com.apple.product-type.framework' and t.platform_name == platform
					}.first
					next nil if remote_target == nil

					native_target.add_dependency remote_target<commit_msg>Fixed linking to targets without a specific SDK<commit_after>require 'xcodeproj'
require 'yaml'
require 'paint'
require 'deep_clone'
require_relative '../spec/spec_file'

module Xcodegen
	class XcodeprojWriter
		CONFIG_PROFILE_PATH = File.join(__dir__, '..', '..', 'res', 'config_profiles')
		TARGET_CONFIG_PROFILE_PATH = File.join(__dir__, '..', '..', 'res', 'target_config_profiles')

		# Sourced from Cocoapods:Xcodeproj project. This should be kept up to date with that gem.
		PRODUCT_TYPE_UTI_INV = {
			'com.apple.product-type.application' => :application,
			'com.apple.product-type.framework' => :framework,
			'com.apple.product-type.library.dynamic' => :dynamic_library,
			'com.apple.product-type.library.static' => :static_library,
			'com.apple.product-type.bundle' => :bundle,
			'com.apple.product-type.bundle.unit-test' => :unit_test_bundle,
			'com.apple.product-type.bundle.ui-testing' => :ui_test_bundle,
			'com.apple.product-type.app-extension' => :app_extension,
			'com.apple.product-type.tool' => :command_line_tool,
			'com.apple.product-type.application.watchapp' => :watch_app,
			'com.apple.product-type.application.watchapp2' => :watch2_app,
			'com.apple.product-type.watchkit-extension' => :watch_extension,
			'com.apple.product-type.watchkit2-extension' => :watch2_extension,
			'com.apple.product-type.tv-app-extension' => :tv_extension,
			'com.apple.product-type.application.messages' => :messages_application,
			'com.apple.product-type.app-extension.messages' => :messages_extension,
			'com.apple.product-type.app-extension.messages-sticker-pack' => :sticker_pack,
			'com.apple.product-type.xpc-service' => :xpc_service
		}.freeze

		# @param spec [Xcodegen::Specfile]
		# @param destination [String]
		def self.write(source_spec, destination)
			# Create a clone of the spec to avoid affecting the original referenced object
			# noinspection RubyResolve
			spec = Marshal.load(Marshal.dump(source_spec))

			unless spec != nil and spec.is_a? Xcodegen::Specfile
				raise StandardError.new 'Invalid spec file'
			end

			unless spec.configurations.length > 0
				raise StandardError.new 'Spec must have at least one configuration'
			end

			if spec.variants.count == 0
				write_xcodeproj spec, File.join(destination, 'project.xcodeproj')
			else
				# Generate a derived spec for each variant and write out the variants
				specs = spec.variants.map { |variant|
					next nil if variant.abstract

					variant_targets = DeepClone.clone variant.targets
					spec_targets = DeepClone.clone spec.targets

					variant_targets.each { |target|
						spec_target = spec_targets.find { |st| st.name == target.name }
						next if spec_target == nil

						spec_target.source_dir = spec_target.source_dir.unshift(*target.source_dir).uniq
						spec_target.res_dir = spec_target.res_dir.unshift(*target.res_dir).uniq
						spec_target.file_excludes = [].unshift(*spec_target.file_excludes).unshift(*target.file_excludes).uniq

						target.configurations.each { |configuration|
							spec_config = spec_target.configurations.find { |sc| sc.name == configuration.name }
							spec_config.settings.merge! configuration.settings
							spec_config.profiles = [].unshift(*configuration.profiles).unshift(*spec_config.profiles).uniq
						}

						spec_target.file_excludes = [].unshift(*spec_target.file_excludes).unshift(*target.file_excludes)
						spec_target.options = [].unshift(*spec_target.options).unshift(*target.options)
						spec_target.references = [].unshift(*spec_target.references).unshift(*target.references)
					}

					[variant.name, Xcodegen::Specfile.new(spec.version, spec_targets, spec.configurations, [], spec.base_dir)]
				}.compact.to_h

				specs.each { |name, variant_spec|
					if name == '$base'
						write_xcodeproj variant_spec, File.join(destination, 'project.xcodeproj')
					else
						write_xcodeproj variant_spec, File.join(destination, "#{name.downcase.gsub(/[^0-9a-z]/, '')}.xcodeproj")
					end
				}
			end
		end

		# @param target [Xcodegen::Specfile::Target]
		# @param project [Xcodeproj::Project]
		# @param target_refs [Hash<String, Xcodeproj::PBXNativeTarget>]
		# @param spec_configuration_type_map [Hash<String, String>]
		# @return [Xcodeproj::PBXNativeTarget]
		def self.add_target(target, project, target_refs, spec_configuration_type_map)
			requested_target_refs = target.references.select { |ref| ref.is_a? Xcodegen::Specfile::Target::TargetReference }
			target_references = requested_target_refs.map { |ref|
				# If the referenced target has not been added to the project yet, skip this target for now
				unless target_refs.has_key? ref.target_name
					return nil
				end

				next target_refs[ref.target_name]
			}.compact

			if requested_target_refs.length != target_references.length
				puts Paint["Warning: Not all target references could be resolved for target: '#{target.name}'.", :yellow]
			end

			target_build_settings = {}
			target.configurations.each { |config|
				build_settings = {}
				config.profiles.map { |profile_name|
					[profile_name, File.join(TARGET_CONFIG_PROFILE_PATH, "#{profile_name.sub(':', '_')}.yml")]
				}.map { |data|
					profile_name, profile_file_name = data
					unless File.exist? profile_file_name
						puts Paint["Warning: unrecognised project configuration profile '#{profile_name}'. Ignoring...", :yellow]
						next nil
					end

					next YAML.load_file(profile_file_name)
				}.each { |profile_data|
					build_settings = build_settings.merge (profile_data || {})
				}

				build_settings = build_settings.merge config.settings
				target_build_settings[config.name] = { :type => spec_configuration_type_map[config.name], :settings => build_settings }
			}

			sdk = target_build_settings[target_build_settings.keys.first][:settings]['SDKROOT']

			unless sdk != nil
				puts Paint["Warning: SDKROOT not found in configuration for target: '#{target.name}'. Ignoring...", :yellow]
				return nil
			end

			if sdk.include? 'iphoneos'
				platform = :ios
			elsif sdk.include? 'macosx'
				platform = :osx
			elsif sdk.include? 'appletvos'
				platform = :tvos
			elsif sdk.include? 'watchos'
				platform = :watchos
			else
				puts Paint["Warning: SDKROOT #{build_settings['SDKROOT']} not recognised in configuration for target: '#{target.name}'. Ignoring...", :yellow]
				return nil
			end

			native_target = project.new_target PRODUCT_TYPE_UTI_INV[target.type], target.name, platform, nil, nil, :swift
			native_target.build_configurations.clear
			target_build_settings.each { |name, data|
				config = native_target.add_build_configuration(name, data[:type])
				config.build_settings = data[:settings]
			}

			target_references.each { |native_ref|
				native_target.add_dependency native_ref
			}

			requested_sys_framework_refs = target.references
				.select { |ref| ref.is_a? Xcodegen::Specfile::Target::SystemFrameworkReference }
				.map { |ref| ref.name }
				.select { |ref| ref != 'Foundation' } # Filter out Foundation as it's already added by default
			native_target.add_system_framework requested_sys_framework_refs

			requested_sys_library_refs = target.references
				.select { |ref| ref.is_a? Xcodegen::Specfile::Target::SystemLibraryReference }
				.map { |ref| ref.name }
			native_target.add_system_library requested_sys_library_refs

			subproj_group = project.frameworks_group.groups.find { |g| g.display_name == '$subproj' }
			if subproj_group == nil
				 subproj_group = project.frameworks_group.new_group '$subproj', nil, '<group>'
			end
			framework_group = project.frameworks_group.groups.find { |group| group.display_name == '$local' }
			if framework_group == nil
				framework_group = project.frameworks_group.new_group '$local', nil, '<group>'
			end
			target.references.select { |ref| ref.is_a? Xcodegen::Specfile::Target::FrameworkReference }.each { |f|
				subproj = subproj_group.new_file f.project_path
				remote_project = Xcodeproj::Project.open f.project_path

				f.settings['frameworks'].map { |f_opts|
					remote_target = remote_project.targets.select { |t|
						t.product_reference.path == f_opts['name'] and t.product_type === 'com.apple.product-type.framework' and [nil, platform].include? t.platform_name
					}.first
					next nil if remote_target == nil

					native_target.add_dependency remote_target
{'fraction_non_alphanumeric': 0.12217194570135746, 'fraction_numerical': 0.0009653092006033183, 'mean_word_length': 4.237282780410743, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 53, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4718540', 'n_tokens_mistral': 6267, 'n_tokens_neox': 5262, 'n_words': 1023}