xcodeproj 1.7.0 → 1.19.0

Sign up to get free protection for your applications and to get access to all the features.
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