xcodeproj 1.7.0 → 1.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/lib/xcodeproj/command/config_dump.rb +5 -1
  3. data/lib/xcodeproj/config.rb +3 -1
  4. data/lib/xcodeproj/config/other_linker_flags_parser.rb +2 -2
  5. data/lib/xcodeproj/constants.rb +70 -44
  6. data/lib/xcodeproj/gem_version.rb +1 -1
  7. data/lib/xcodeproj/project.rb +23 -2
  8. data/lib/xcodeproj/project/object.rb +3 -1
  9. data/lib/xcodeproj/project/object/build_configuration.rb +96 -35
  10. data/lib/xcodeproj/project/object/build_file.rb +9 -1
  11. data/lib/xcodeproj/project/object/build_phase.rb +45 -4
  12. data/lib/xcodeproj/project/object/build_rule.rb +12 -0
  13. data/lib/xcodeproj/project/object/helpers/file_references_factory.rb +11 -4
  14. data/lib/xcodeproj/project/object/helpers/groupable_helper.rb +13 -6
  15. data/lib/xcodeproj/project/object/native_target.rb +52 -9
  16. data/lib/xcodeproj/project/object/root_object.rb +15 -2
  17. data/lib/xcodeproj/project/object/swift_package_product_dependency.rb +19 -0
  18. data/lib/xcodeproj/project/object/swift_package_remote_reference.rb +19 -0
  19. data/lib/xcodeproj/project/object/target_dependency.rb +9 -0
  20. data/lib/xcodeproj/project/project_helper.rb +2 -1
  21. data/lib/xcodeproj/project/uuid_generator.rb +41 -19
  22. data/lib/xcodeproj/scheme.rb +6 -4
  23. data/lib/xcodeproj/scheme/build_action.rb +21 -0
  24. data/lib/xcodeproj/scheme/buildable_reference.rb +3 -1
  25. data/lib/xcodeproj/scheme/launch_action.rb +50 -0
  26. data/lib/xcodeproj/scheme/test_action.rb +36 -1
  27. data/lib/xcodeproj/workspace.rb +16 -7
  28. data/lib/xcodeproj/workspace/file_reference.rb +7 -12
  29. data/lib/xcodeproj/workspace/group_reference.rb +18 -15
  30. data/lib/xcodeproj/workspace/reference.rb +40 -0
  31. metadata +11 -9
@@ -224,8 +224,9 @@ module Xcodeproj
224
224
  cl.build_configurations << release_conf
225
225
  cl.build_configurations << debug_conf
226
226
 
227
+ existing_configurations = cl.build_configurations.map(&:name)
227
228
  project.build_configurations.each do |configuration|
228
- next if cl.build_configurations.map(&:name).include?(configuration.name)
229
+ next if existing_configurations.include?(configuration.name)
229
230
 
230
231
  new_config = project.new(XCBuildConfiguration)
231
232
  new_config.name = configuration.name
@@ -3,43 +3,65 @@ module Xcodeproj
3
3
  class UUIDGenerator
4
4
  require 'digest'
5
5
 
6
- def initialize(project)
7
- @project = project
8
- @new_objects_by_uuid = {}
6
+ def initialize(projects)
7
+ @projects = Array(projects)
9
8
  @paths_by_object = {}
10
9
  end
11
10
 
12
11
  def generate!
13
- all_objects = @project.objects
14
- generate_paths(@project.root_object)
15
- switch_uuids(all_objects)
16
- verify_no_duplicates!(all_objects)
17
- fixup_uuid_references
18
- @project.instance_variable_set(:@generated_uuids, @project.instance_variable_get(:@available_uuids))
19
- @project.instance_variable_set(:@objects_by_uuid, @new_objects_by_uuid)
12
+ generate_all_paths_by_objects(@projects)
13
+
14
+ new_objects_by_project = Hash[@projects.map do |project|
15
+ [project, switch_uuids(project)]
16
+ end]
17
+ all_new_objects_by_project = new_objects_by_project.values.flat_map(&:values)
18
+ all_objects_by_uuid = @projects.map(&:objects_by_uuid).inject(:merge)
19
+ all_objects = @projects.flat_map(&:objects)
20
+ verify_no_duplicates!(all_objects, all_new_objects_by_project)
21
+ @projects.each { |project| fixup_uuid_references(project, all_objects_by_uuid) }
22
+ new_objects_by_project.each do |project, new_objects_by_uuid|
23
+ project.instance_variable_set(:@generated_uuids, project.instance_variable_get(:@available_uuids))
24
+ project.instance_variable_set(:@objects_by_uuid, new_objects_by_uuid)
25
+ end
20
26
  end
21
27
 
22
28
  private
23
29
 
24
30
  UUID_ATTRIBUTES = [:remote_global_id_string, :container_portal, :target_proxy].freeze
25
31
 
26
- def verify_no_duplicates!(all_objects)
27
- duplicates = all_objects - @new_objects_by_uuid.values
32
+ def verify_no_duplicates!(all_objects, all_new_objects)
33
+ duplicates = all_objects - all_new_objects
28
34
  UserInterface.warn "[Xcodeproj] Generated duplicate UUIDs:\n\n" <<
29
35
  duplicates.map { |d| "#{d.isa} -- #{@paths_by_object[d]}" }.join("\n") unless duplicates.empty?
30
36
  end
31
37
 
32
- def fixup_uuid_references
38
+ def fixup_uuid_references(target_project, all_objects_by_uuid)
33
39
  fixup = ->(object, attr) do
34
- if object.respond_to?(attr) && link = @project.objects_by_uuid[object.send(attr)]
40
+ if object.respond_to?(attr) && link = all_objects_by_uuid[object.send(attr)]
35
41
  object.send(:"#{attr}=", link.uuid)
36
42
  end
37
43
  end
38
- @project.objects.each do |object|
44
+ target_project.objects.each do |object|
39
45
  UUID_ATTRIBUTES.each do |attr|
40
46
  fixup[object, attr]
41
47
  end
42
48
  end
49
+
50
+ if (project_attributes = target_project.root_object.attributes) && project_attributes['TargetAttributes']
51
+ project_attributes['TargetAttributes'] = Hash[project_attributes['TargetAttributes'].map do |target_uuid, attributes|
52
+ if test_target_id = attributes['TestTargetID']
53
+ attributes = attributes.merge('TestTargetID' => all_objects_by_uuid[test_target_id].uuid)
54
+ end
55
+ if target_object = all_objects_by_uuid[target_uuid]
56
+ target_uuid = target_object.uuid
57
+ end
58
+ [target_uuid, attributes]
59
+ end]
60
+ end
61
+ end
62
+
63
+ def generate_all_paths_by_objects(projects)
64
+ projects.each { |project| generate_paths(project.root_object, project.path.basename.to_s) }
43
65
  end
44
66
 
45
67
  def generate_paths(object, path = '')
@@ -67,13 +89,13 @@ module Xcodeproj
67
89
  end
68
90
  end
69
91
 
70
- def switch_uuids(objects)
71
- @project.mark_dirty!
72
- objects.each do |object|
92
+ def switch_uuids(project)
93
+ project.mark_dirty!
94
+ project.objects.each_with_object({}) do |object, hash|
73
95
  next unless path = @paths_by_object[object]
74
96
  uuid = uuid_for_path(path)
75
97
  object.instance_variable_set(:@uuid, uuid)
76
- @new_objects_by_uuid[uuid] = object
98
+ hash[uuid] = object
77
99
  end
78
100
  end
79
101
 
@@ -31,7 +31,9 @@ module Xcodeproj
31
31
  def initialize(file_path = nil)
32
32
  if file_path
33
33
  @file_path = file_path
34
- @doc = REXML::Document.new(File.new(file_path))
34
+ @doc = File.open(file_path, 'r') do |f|
35
+ REXML::Document.new(f)
36
+ end
35
37
  @doc.context[:attribute_quote] = :quote
36
38
 
37
39
  @scheme = @doc.elements['Scheme']
@@ -193,9 +195,9 @@ module Xcodeproj
193
195
 
194
196
  entry.build_for_testing = true
195
197
  entry.build_for_running = build_for_running
196
- entry.build_for_profiling = true
197
- entry.build_for_archiving = true
198
- entry.build_for_analyzing = true
198
+ entry.build_for_profiling = build_for_running
199
+ entry.build_for_archiving = build_for_running
200
+ entry.build_for_analyzing = build_for_running
199
201
 
200
202
  build_action.add_entry(entry)
201
203
  end
@@ -59,6 +59,20 @@ module Xcodeproj
59
59
  end
60
60
  end
61
61
 
62
+ # @param [Array<BuildAction::Entry>] entries
63
+ # Sets the list of BuildActionEntry nodes associated with this Build Action.
64
+ #
65
+ def entries=(entries)
66
+ @xml_element.delete_element('BuildActionEntries')
67
+ unless entries.empty?
68
+ entries_element = @xml_element.add_element('BuildActionEntries')
69
+ entries.each do |entry_node|
70
+ entries_element.add_element(entry_node.xml_element)
71
+ end
72
+ end
73
+ entries
74
+ end
75
+
62
76
  # @param [BuildAction::Entry] entry
63
77
  # The BuildActionEntry to add to the list of targets to build for the various actions
64
78
  #
@@ -185,6 +199,13 @@ module Xcodeproj
185
199
  def add_buildable_reference(ref)
186
200
  @xml_element.add_element(ref.xml_element)
187
201
  end
202
+
203
+ # @param [BuildableReference] ref
204
+ # The BuildableReference to remove from the list of targets this entry will build
205
+ #
206
+ def remove_buildable_reference(ref)
207
+ @xml_element.delete_element(ref.xml_element)
208
+ end
188
209
  end
189
210
  end
190
211
  end
@@ -59,10 +59,12 @@ module Xcodeproj
59
59
  # If true, buildable_name will also be updated by computing a name from the target
60
60
  #
61
61
  def set_reference_target(target, override_buildable_name = false, root_project = nil)
62
+ # note, the order of assignment here is important, it determines the order of serialization in the xml
63
+ # this matches the order that Xcode generates
62
64
  @xml_element.attributes['BlueprintIdentifier'] = target.uuid
65
+ self.buildable_name = construct_buildable_name(target) if override_buildable_name
63
66
  @xml_element.attributes['BlueprintName'] = target.name
64
67
  @xml_element.attributes['ReferencedContainer'] = construct_referenced_container_uri(target, root_project)
65
- self.buildable_name = construct_buildable_name(target) if override_buildable_name
66
68
  end
67
69
 
68
70
  # @return [String]
@@ -47,6 +47,40 @@ module Xcodeproj
47
47
  @xml_element.attributes['allowLocationSimulation'] = bool_to_string(flag)
48
48
  end
49
49
 
50
+ # @return [Bool]
51
+ # Whether this Build Action should disable detection of UI API misuse
52
+ # from background threads
53
+ #
54
+ def disable_main_thread_checker?
55
+ string_to_bool(@xml_element.attributes['disableMainThreadChecker'])
56
+ end
57
+
58
+ # @param [Bool] flag
59
+ # Set whether this Build Action should disable detection of UI API misuse
60
+ # from background threads
61
+ #
62
+ def disable_main_thread_checker=(flag)
63
+ @xml_element.attributes['disableMainThreadChecker'] = bool_to_string(flag)
64
+ end
65
+
66
+ # @return [Bool]
67
+ # Whether UI API misuse from background threads detection should pause execution.
68
+ # This flag is ignored when the thread checker disabled
69
+ # ([disable_main_thread_checker] flag).
70
+ #
71
+ def stop_on_every_main_thread_checker_issue?
72
+ string_to_bool(@xml_element.attributes['stopOnEveryMainThreadCheckerIssue'])
73
+ end
74
+
75
+ # @param [Bool] flag
76
+ # Set whether UI API misuse from background threads detection should pause execution.
77
+ # This flag is ignored when the thread checker disabled
78
+ # ([disable_main_thread_checker] flag).
79
+ #
80
+ def stop_on_every_main_thread_checker_issue=(flag)
81
+ @xml_element.attributes['stopOnEveryMainThreadCheckerIssue'] = bool_to_string(flag)
82
+ end
83
+
50
84
  # @return [String]
51
85
  # The launch automatically substyle
52
86
  #
@@ -109,6 +143,22 @@ module Xcodeproj
109
143
  @xml_element.add_element(arguments.xml_element) if arguments
110
144
  arguments
111
145
  end
146
+
147
+ # @return [Array<MacroExpansion>]
148
+ # The list of MacroExpansion bound with this LaunchAction
149
+ #
150
+ def macro_expansions
151
+ @xml_element.get_elements('MacroExpansion').map do |node|
152
+ MacroExpansion.new(node)
153
+ end
154
+ end
155
+
156
+ # @param [MacroExpansion] macro_expansion
157
+ # Add a MacroExpansion to this LaunchAction
158
+ #
159
+ def add_macro_expansion(macro_expansion)
160
+ @xml_element.add_element(macro_expansion.xml_element)
161
+ end
112
162
  end
113
163
  end
114
164
  end
@@ -35,6 +35,22 @@ module Xcodeproj
35
35
  @xml_element.attributes['shouldUseLaunchSchemeArgsEnv'] = bool_to_string(flag)
36
36
  end
37
37
 
38
+ # @return [Bool]
39
+ # Whether this Test Action should disable detection of UI API misuse
40
+ # from background threads
41
+ #
42
+ def disable_main_thread_checker?
43
+ string_to_bool(@xml_element.attributes['disableMainThreadChecker'])
44
+ end
45
+
46
+ # @param [Bool] flag
47
+ # Set whether this Test Action should disable detection of UI API misuse
48
+ # from background threads
49
+ #
50
+ def disable_main_thread_checker=(flag)
51
+ @xml_element.attributes['disableMainThreadChecker'] = bool_to_string(flag)
52
+ end
53
+
38
54
  # @return [Bool]
39
55
  # Whether Clang Code Coverage is enabled ('Gather coverage data' turned ON)
40
56
  #
@@ -42,7 +58,7 @@ module Xcodeproj
42
58
  string_to_bool(@xml_element.attributes['codeCoverageEnabled'])
43
59
  end
44
60
 
45
- # @rparam [Bool] flag
61
+ # @param [Bool] flag
46
62
  # Set whether Clang Code Coverage is enabled ('Gather coverage data' turned ON)
47
63
  #
48
64
  def code_coverage_enabled=(flag)
@@ -60,6 +76,18 @@ module Xcodeproj
60
76
  end
61
77
  end
62
78
 
79
+ # @param [Array<TestableReference>] testables
80
+ # Sets the list of TestableReference (test bundles) associated with this Test Action
81
+ #
82
+ def testables=(testables)
83
+ @xml_element.delete_element('Testables')
84
+ testables_element = @xml_element.add_element('Testables')
85
+ testables.each do |testable|
86
+ testables_element.add_element(testable.xml_element)
87
+ end
88
+ testables
89
+ end
90
+
63
91
  # @param [TestableReference] testable
64
92
  # Add a TestableReference (test bundle) to this Test Action
65
93
  #
@@ -203,6 +231,13 @@ module Xcodeproj
203
231
  @xml_element.add_element(ref.xml_element)
204
232
  end
205
233
 
234
+ # @param [BuildableReference] ref
235
+ # The BuildableReference to remove from the list of targets this entry will build
236
+ #
237
+ def remove_buildable_reference(ref)
238
+ @xml_element.delete_element(ref.xml_element)
239
+ end
240
+
206
241
  # @return [Array<Test>]
207
242
  # The list of SkippedTest this action will skip.
208
243
  #
@@ -64,7 +64,7 @@ module Xcodeproj
64
64
  #
65
65
  def self.new_from_xcworkspace(path)
66
66
  from_s(File.read(File.join(path, 'contents.xcworkspacedata')),
67
- File.expand_path(File.dirname(path)))
67
+ File.expand_path(path))
68
68
  rescue Errno::ENOENT
69
69
  new(nil)
70
70
  end
@@ -97,16 +97,19 @@ module Xcodeproj
97
97
  #
98
98
  def <<(path_or_reference)
99
99
  return unless @document && @document.respond_to?(:root)
100
- case
101
- when path_or_reference.is_a?(String)
100
+
101
+ case path_or_reference
102
+ when String
102
103
  project_file_reference = Xcodeproj::Workspace::FileReference.new(path_or_reference)
103
- when path_or_reference.is_a?(Xcodeproj::Workspace::FileReference)
104
+ when Xcodeproj::Workspace::FileReference
104
105
  project_file_reference = path_or_reference
105
106
  projpath = nil
106
107
  else
107
- raise ArgumentError, 'Input to the << operator must be a file path or FileReference'
108
+ raise ArgumentError, "Input to the << operator must be a file path or FileReference, got #{path_or_reference.inspect}"
109
+ end
110
+ unless file_references.any? { |ref| project_file_reference.eql? ref }
111
+ @document.root.add_element(project_file_reference.to_node)
108
112
  end
109
- @document.root.add_element(project_file_reference.to_node)
110
113
  load_schemes_from_project File.expand_path(projpath || project_file_reference.path)
111
114
  end
112
115
 
@@ -191,8 +194,14 @@ module Xcodeproj
191
194
  # @return [void]
192
195
  #
193
196
  def load_schemes(workspace_dir_path)
197
+ # Normalizes path to directory of workspace needed for file_reference.absolute_path
198
+ workspaces_dir = workspace_dir_path
199
+ if File.extname(workspace_dir_path) == '.xcworkspace'
200
+ workspaces_dir = File.expand_path('..', workspaces_dir)
201
+ end
202
+
194
203
  file_references.each do |file_reference|
195
- project_full_path = file_reference.absolute_path(workspace_dir_path)
204
+ project_full_path = file_reference.absolute_path(workspaces_dir)
196
205
  load_schemes_from_project(project_full_path)
197
206
  end
198
207
 
@@ -1,27 +1,19 @@
1
+ require 'xcodeproj/workspace/reference'
2
+
1
3
  module Xcodeproj
2
4
  class Workspace
3
5
  # Describes a file reference of a Workspace.
4
6
  #
5
- class FileReference
7
+ class FileReference < Reference
6
8
  # @return [String] the path to the project
7
9
  #
8
10
  attr_reader :path
9
11
 
10
- # @return [String] the type of reference to the project
11
- #
12
- # This can be of the following values:
13
- # - absolute
14
- # - group
15
- # - container
16
- # - developer (unsupported)
17
- #
18
- attr_reader :type
19
-
20
12
  # @param [#to_s] path @see path
21
13
  # @param [#to_s] type @see type
22
14
  #
23
15
  def initialize(path, type = 'group')
24
- @path = path.to_s
16
+ @path = Pathname.new(path.to_s).cleanpath.to_s
25
17
  @type = type.to_s
26
18
  end
27
19
 
@@ -47,6 +39,9 @@ module Xcodeproj
47
39
  #
48
40
  def self.from_node(xml_node)
49
41
  type, path = xml_node.attribute('location').value.split(':', 2)
42
+ if type == 'group'
43
+ path = prepend_parent_path(xml_node, path)
44
+ end
50
45
  new(path, type)
51
46
  end
52
47
 
@@ -1,41 +1,39 @@
1
+ require 'xcodeproj/workspace/reference'
2
+
1
3
  module Xcodeproj
2
4
  class Workspace
3
5
  # Describes a group reference of a Workspace.
4
6
  #
5
- class GroupReference
7
+ class GroupReference < Reference
6
8
  # @return [String] the name of the group
7
9
  #
8
10
  attr_reader :name
9
11
 
10
- # @return [String] the type of reference to the project
11
- #
12
- # This can be of the following values:
13
- # - absolute
14
- # - group
15
- # - container (only supported value)
16
- # - developer
12
+ # @return [String] the location of the group on disk
17
13
  #
18
- attr_reader :type
14
+ attr_reader :location
19
15
 
20
16
  # @param [#to_s] name @see name
21
17
  # @param [#to_s] type @see type
18
+ # @param [#to_s] location @see location
22
19
  #
23
- def initialize(name, type = 'container')
20
+ def initialize(name, type = 'container', location = '')
24
21
  @name = name.to_s
25
22
  @type = type.to_s
23
+ @location = location.to_s
26
24
  end
27
25
 
28
26
  # @return [Bool] Whether a group reference is equal to another.
29
27
  #
30
28
  def ==(other)
31
- name == other.name && type == other.type
29
+ name == other.name && type == other.type && location == other.location
32
30
  end
33
31
  alias_method :eql?, :==
34
32
 
35
33
  # @return [Fixnum] A hash identical for equals objects.
36
34
  #
37
35
  def hash
38
- [name, type].hash
36
+ [name, type, location].hash
39
37
  end
40
38
 
41
39
  # Returns a group reference given XML representation.
@@ -46,16 +44,21 @@ module Xcodeproj
46
44
  # @return [GroupReference] The new group reference instance.
47
45
  #
48
46
  def self.from_node(xml_node)
49
- type = xml_node.attribute('location').value.split(':', 2).first
47
+ location_array = xml_node.attribute('location').value.split(':', 2)
48
+ type = location_array.first
49
+ location = location_array[1] || ''
50
+ if type == 'group'
51
+ location = prepend_parent_path(xml_node, location)
52
+ end
50
53
  name = xml_node.attribute('name').value
51
- new(name, type)
54
+ new(name, type, location)
52
55
  end
53
56
 
54
57
  # @return [REXML::Element] the XML representation of the group reference.
55
58
  #
56
59
  def to_node
57
60
  REXML::Element.new('Group').tap do |element|
58
- element.add_attribute('location', "#{type}:")
61
+ element.add_attribute('location', "#{type}:#{location}")
59
62
  element.add_attribute('name', "#{name}")
60
63
  end
61
64
  end