xcactivitylog 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc90141684fae80212880fb48cfb68fa36773c2d27428df4f1e2e7eccb89d988
4
- data.tar.gz: 074f11361b4312d978ac19e14fb6f4c56fc958308866a92f63c0f4a2f4fddac0
3
+ metadata.gz: 86a13d7e86ed544e21d0bf9a1a84fb981671f8100160ec82fb59ba48ee0eb270
4
+ data.tar.gz: '0333681b5f17012ba1ac5850256c81965901262a93357449852874afe5bac997'
5
5
  SHA512:
6
- metadata.gz: 450ac2460c8789d8efba482f009ff7813fe5e238b5f647bc6fe1bbf9ec249a364920aed7c2fce81a13c74c8c30d54407fdff2d981451bf78740b6c8f0bd90dc8
7
- data.tar.gz: f5711987622f8ea0589f4b8249867d447923ebbe6b775384c3113eec42b39960618d2472efecf196e7b9b04f61c8422912b36429c414605139a8d485912cc114
6
+ metadata.gz: 1eb8f4baed133593e91db5554b0d2dd02e1fd52d5b2f7b1cf2a6e957438490a3cb0349c51ed273b36a533fe5730981f5b5fb2cb692c2673f3a00380c5263b108
7
+ data.tar.gz: 2beb9f7c54b34ba0c91a37a044732b53d14e7657127a8aa6219ae1bbc057838fe1bf29b0db8ae3eab2480cce5a3e5f44ed62a2c1061866ed9c0d2c71c507a098
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.2.0
@@ -40,53 +40,62 @@ module SLF0
40
40
  @class_deserializer = class_deserializer
41
41
  end
42
42
 
43
- def int(reason = nil)
44
- shift(Int, reason).value
43
+ def int(&reason_blk)
44
+ shift(Int, &reason_blk).value
45
45
  end
46
46
 
47
- def string(reason = nil)
48
- return if shift_nil?(reason)
47
+ def string(&reason_blk)
48
+ return if shift_nil?(&reason_blk)
49
49
 
50
- shift(String, reason).value
50
+ shift(String, &reason_blk).value
51
51
  end
52
52
 
53
- def double(reason = nil)
53
+ def double(&reason_blk)
54
54
  return if shift_nil?
55
55
 
56
- shift(Double, reason).value
56
+ shift(Double, &reason_blk).value
57
57
  end
58
58
 
59
- def object_list(reason = nil)
60
- return if shift_nil?(reason)
59
+ def object_list(&reason_blk)
60
+ return if shift_nil?(&reason_blk)
61
61
 
62
- shift(ObjectList, reason).length.times.map { object(reason && "object in object list for #{reason}") }
62
+ length = shift(ObjectList, &reason_blk).length
63
+ Array.new(length) do
64
+ object { reason_blk && "object #{length} in object list for #{reason_blk&.call}" }
65
+ end
63
66
  end
64
67
 
65
- def object(reason = nil)
66
- deserializer_for(shift(ClassNameRef, reason).value)[self]
68
+ def object(&reason_blk)
69
+ return if shift_nil?(&reason_blk)
70
+
71
+ deserializer_for(shift(ClassNameRef, &reason_blk).value)[self]
67
72
  end
68
73
 
69
74
  def deserializer_for(class_ref_num)
70
75
  @class_deserializer[class_ref_num].last
71
76
  end
72
77
 
73
- def shift_nil?(reason = nil)
74
- shift(ObjectListNil, reason, raise: false)
78
+ def shift_nil?(&reason_blk)
79
+ shift(ObjectListNil, raise: false, &reason_blk)
75
80
  end
76
81
 
77
- def shift(cls, reason = nil, raise: true)
82
+ def shift(cls, raise: true, &reason_blk)
78
83
  unless (token = @tokens.shift)
79
84
  raise 'no more tokens'
80
85
  end
81
86
 
82
87
  unless token.is_a?(cls)
83
- raise "expected #{cls} got #{token.inspect} for #{reason}" if raise
88
+ unexpected_token!(cls, token, &reason_blk) if raise
84
89
 
85
90
  @tokens.unshift(token)
86
91
  return
87
92
  end
88
93
  token
89
94
  end
95
+
96
+ def unexpected_token!(expected_class, token, &reason_blk)
97
+ raise "expected #{expected_class} got #{token.inspect} for #{reason_blk&.call}"
98
+ end
90
99
  end
91
100
  end
92
101
  end
@@ -24,7 +24,7 @@ module SLF0
24
24
  def tokenize_body!
25
25
  body = []
26
26
  until scanner.eos?
27
- object = tokenize_field || tokenize_double_field || tokenize_object_list_nil
27
+ object = tokenize_double_field || tokenize_field || tokenize_object_list_nil
28
28
  raise "malformed no object: #{scanner.rest.inspect}\n\nafter: #{body.inspect}" unless object
29
29
 
30
30
  body << object
@@ -39,11 +39,11 @@ module SLF0
39
39
  when '#'
40
40
  SLF0::Token::Int.new int
41
41
  when '%'
42
- SLF0::Token::ClassName.new scanner.scan(/.{#{int}}/).freeze
42
+ SLF0::Token::ClassName.new scan_length(int).freeze
43
43
  when '@'
44
44
  SLF0::Token::ClassNameRef.new int
45
45
  when '"'
46
- SLF0::Token::String.new scanner.scan(/.{#{int}}/).tr("\r", "\n").freeze
46
+ SLF0::Token::String.new scan_length(int).tr("\r", "\n").freeze
47
47
  when '('
48
48
  SLF0::Token::ObjectList.new int
49
49
  else
@@ -52,8 +52,14 @@ module SLF0
52
52
  end
53
53
  end
54
54
 
55
+ def scan_length(length)
56
+ string = scanner.string[scanner.pos, length]
57
+ scanner.pos += length
58
+ string
59
+ end
60
+
55
61
  def tokenize_double_field
56
- return unless (hex = scanner.scan(/[0-9a-fA-F]*\^/)&.chomp('^'))
62
+ return unless (hex = scanner.scan(/\h*\^/)&.chop)
57
63
 
58
64
  double = [hex.to_i(16)].pack('Q<').unpack1('G')
59
65
  SLF0::Token::Double.new double
@@ -2,8 +2,8 @@
2
2
 
3
3
  module XCActivityLog
4
4
  class SerializedObject
5
- Attribute = Struct.new(:name, :type, :first_version, :last_version)
6
- def self.attribute(name, type, first_version = 0, last_version = 99_999)
5
+ Attribute = Struct.new(:name, :type, :first_version, :first_version_without)
6
+ def self.attribute(name, type, first_version = 0, first_version_without = 99_999)
7
7
  attr_reader name
8
8
  alias_method "#{name}?", name if type == :boolean
9
9
  if type == :time
@@ -12,7 +12,7 @@ module XCActivityLog
12
12
  time.to_i * 1_000_000 + time.usec
13
13
  end
14
14
  end
15
- attributes << Attribute.new(name, type, first_version, last_version).freeze
15
+ attributes << Attribute.new(name, type, first_version, first_version_without).freeze
16
16
  end
17
17
 
18
18
  def self.attributes
@@ -47,6 +47,47 @@ module XCActivityLog
47
47
  end
48
48
 
49
49
  class IDEActivityLogSection < SerializedObject
50
+ class Severity
51
+ protected
52
+
53
+ attr_reader :severity
54
+
55
+ public
56
+
57
+ def initialize(severity)
58
+ @severity = severity
59
+ freeze
60
+ end
61
+
62
+ SUCCESS = new(0)
63
+ WARNING = new(1)
64
+ ERROR = new(2)
65
+ TEST_FAILURE = new(3)
66
+
67
+ def to_s
68
+ case severity
69
+ when 0
70
+ 'Success'
71
+ when 1
72
+ 'Warning'
73
+ when 2
74
+ 'Error'
75
+ when 3
76
+ 'Test Failure'
77
+ else
78
+ "Unknown (#{severity})"
79
+ end
80
+ end
81
+
82
+ include Comparable
83
+
84
+ def <=>(other)
85
+ severity <=> other.severity
86
+ end
87
+ end
88
+
89
+ TargetInfo = Struct.new(:name, :configuration, :workspace, keyword_init: true)
90
+
50
91
  include Enumerable
51
92
  def each(&blk)
52
93
  return enum_for(__method__) unless block_given?
@@ -55,9 +96,82 @@ module XCActivityLog
55
96
  subsections&.each { |s| s.each(&blk) }
56
97
  end
57
98
 
99
+ def each_with_parent(parent: nil, &blk)
100
+ return enum_for(__method__) unless block_given?
101
+
102
+ yield self, parent
103
+ subsections&.each { |s| s.each_with_parent(parent: self, &blk) }
104
+ end
105
+
58
106
  def duration_usec
59
107
  time_stopped_recording_usec - time_started_recording_usec
60
108
  end
109
+
110
+ def target_info(parent: nil)
111
+ parent&.target_info ||
112
+ (title =~ /=== BUILD TARGET (.+?) OF PROJECT (.+?) WITH CONFIGURATION (.+?) ===/ &&
113
+ TargetInfo.new(name: Regexp.last_match(1), configuration: Regexp.last_match(3), workspace: Regexp.last_match(2)))
114
+ end
115
+
116
+ def each_trace_event
117
+ thread_id_map_by_section_type = Hash.new { |h, k| h[k] = [] }
118
+ each_with_parent.sort_by { |s, _| s.time_started_recording }.each do |section, parent|
119
+ thread_id_map = thread_id_map_by_section_type[section.section_type]
120
+ best_thread, thread_id = thread_id_map.each_with_index.select do |thread, _tid|
121
+ section.time_started_recording > thread.last.time_stopped_recording
122
+ end.min_by do |thread, _tid|
123
+ (section.time_started_recording - thread.last.time_stopped_recording) +
124
+ (thread.last.time_stopped_recording - thread_id_map.map(&:last).map(&:time_stopped_recording).min)
125
+ end
126
+ unless thread_id
127
+ thread_id = thread_id_map.size
128
+ best_thread = []
129
+ thread_id_map << best_thread
130
+ end
131
+ best_thread << section
132
+
133
+ yield(section: section, parent: parent, thread_id: thread_id)
134
+ end
135
+ end
136
+
137
+ def write_chrome_trace_file(section_type:, to:)
138
+ to << '{"traceEvents":[' << "\n"
139
+ written_comma = false
140
+ each_trace_event do |section:, parent:, thread_id:|
141
+ case section.section_type
142
+ when section_type
143
+ if written_comma
144
+ to << ",\n"
145
+ else
146
+ written_comma = true
147
+ end
148
+ require 'json'
149
+ to << JSON.generate(
150
+ pid: section.section_type.to_s,
151
+ tid: thread_id,
152
+ ts: section.time_started_recording_usec,
153
+ ph: 'X',
154
+ name: section.title.dup&.force_encoding('UTF-8'),
155
+ dur: section.duration_usec,
156
+ args: {
157
+ subtitle: section.subtitle.dup&.force_encoding('UTF-8'),
158
+ target: section.target_info(parent: parent).to_h,
159
+ severity: section.severity
160
+ }
161
+ )
162
+ end
163
+ end
164
+
165
+ to << "\n]}"
166
+
167
+ to
168
+ end
169
+
170
+ def severity
171
+ severity = (messages || []).reduce(Severity::SUCCESS) { |a, e| [a, Severity.new(e.severity)].max }
172
+ (subsections || []).reduce(severity) { |a, e| [a, e.severity].max }
173
+ end
174
+
61
175
  attribute :section_type, :int
62
176
  attribute :domain_type, :string
63
177
  attribute :title, :string
@@ -75,13 +189,28 @@ module XCActivityLog
75
189
  attribute :command_detail_description, :string
76
190
  attribute :unique_identifier, :string
77
191
  attribute :localized_result_string, :string
78
- attribute :xcbuild_signature, :string
79
- attribute :collect_metrics, :boolean, 9
192
+ attribute :xcbuild_signature, :string, 8
193
+ attribute :collect_metrics, :boolean, 9, 10
80
194
  attributes.freeze
81
195
  end
82
196
  class IDECommandLineBuildLog < IDEActivityLogSection
83
197
  attributes.freeze
84
198
  end
199
+ class IDEActivityLogCommandInvocationSection < IDEActivityLogSection
200
+ attributes.freeze
201
+ end
202
+ class IDEActivityLogUnitTestSection < IDEActivityLogSection
203
+ attribute :tests_passes, :string
204
+ attribute :duration, :string
205
+ attribute :summary, :string
206
+ attribute :suite_name, :string
207
+ attribute :test_name, :string
208
+ attribute :performance_test_output, :string
209
+ attributes.freeze
210
+ end
211
+ class IDEActivityLogMajorGroupSection < IDEActivityLogSection
212
+ attributes.freeze
213
+ end
85
214
 
86
215
  class IDEActivityLogMessage < SerializedObject
87
216
  include Enumerable
@@ -98,7 +227,7 @@ module XCActivityLog
98
227
  attribute :submessages, :object_list
99
228
  attribute :severity, :int
100
229
  attribute :type, :string
101
- attribute :location, :object
230
+ attribute :location, :document_location
102
231
  attribute :category_identifier, :string
103
232
  attribute :secondary_locations, :object_list
104
233
  attribute :additional_description, :string
@@ -107,6 +236,34 @@ module XCActivityLog
107
236
  class IDEClangDiagnosticActivityLogMessage < IDEActivityLogMessage
108
237
  attributes.freeze
109
238
  end
239
+ class IDEActivityLogAnalyzerResultMessage < IDEActivityLogMessage
240
+ attribute :result_type, :string
241
+ attribute :key_event_index, :int
242
+ attributes.freeze
243
+ end
244
+ class IDEActivityLogAnalyzerStepMessage < IDEActivityLogMessage
245
+ attribute :parent_index, :int
246
+ attributes.freeze
247
+ end
248
+ class IDEActivityLogAnalyzerEventStepMessage < IDEActivityLogAnalyzerStepMessage
249
+ attribute :result_type, :string
250
+ attribute :key_event_index, :int
251
+ attributes.freeze
252
+ end
253
+ class IDEActivityLogAnalyzerControlFlowStepMessage < IDEActivityLogAnalyzerStepMessage
254
+ attribute :end_location, :document_location
255
+ attribute :edges, :object_list
256
+ attributes.freeze
257
+ end
258
+ class IDEActivityLogAnalyzerWarningMessage < IDEActivityLogMessage
259
+ attributes.freeze
260
+ end
261
+
262
+ class IDEActivityLogAnalyzerControlFlowStepEdge < SerializedObject
263
+ attribute :start_location, :document_location
264
+ attribute :end_location, :document_location
265
+ attributes.freeze
266
+ end
110
267
 
111
268
  class DVTDocumentLocation < SerializedObject
112
269
  attribute :document_url_string, :string
@@ -123,7 +280,7 @@ module XCActivityLog
123
280
  attribute :ending_line_number, :int
124
281
  attribute :ending_column_number, :int
125
282
  attribute :character_range, :nsrange
126
- attribute :location_encoding, :int
283
+ attribute :location_encoding, :int, 7
127
284
  attributes.freeze
128
285
  end
129
286
  end
@@ -8,7 +8,7 @@ module XCActivityLog
8
8
  class S < SLF0::Token::Stream
9
9
  def initialize(tokens, class_deserializer:)
10
10
  super(tokens, class_deserializer: class_deserializer)
11
- @version = int('activity log version')
11
+ @version = int { 'activity log version' }
12
12
  end
13
13
 
14
14
  def deserializer_for(class_ref_num)
@@ -24,34 +24,38 @@ module XCActivityLog
24
24
  def deserialize_instance_of(stream, cls)
25
25
  instance = cls.new
26
26
  cls.attributes.each do |attr|
27
- next if attr.first_version > @version || attr.last_version < @version
27
+ next if attr.first_version > @version || attr.first_version_without <= @version
28
28
 
29
- value = stream.send(attr.type, "#{attr.name} for #{cls.name.split('::').last}")
29
+ value = stream.send(attr.type) { "#{attr.name} for #{cls.name.split('::').last} #{instance.inspect}" }
30
30
  instance.instance_variable_set(:"@#{attr.name}", value)
31
31
  end
32
32
  instance.freeze
33
33
  end
34
34
 
35
- def boolean(reason = nil)
36
- int(reason) != 0
35
+ def boolean(&reason_blk)
36
+ int(&reason_blk) != 0
37
37
  end
38
38
 
39
- def nsrange(_reason = nil)
39
+ def nsrange(&_reason_blk)
40
40
  deserialize_instance_of(self, NSRange)
41
41
  end
42
42
 
43
- def document_location(reason = nil)
43
+ def document_location(&reason_blk)
44
44
  return if shift_nil?
45
45
 
46
- object(reason).tap do |o|
47
- raise "expected location, got #{o.class.name} for #{reason}" unless o.is_a?(DVTDocumentLocation)
46
+ object(&reason_blk).tap do |o|
47
+ raise "expected location, got #{o.class.name} for #{reason_blk&.call}" unless o.is_a?(DVTDocumentLocation)
48
48
  end
49
49
  end
50
50
 
51
51
  EPOCH = Time.new(2001, 1, 1, 0, 0, 0, '+00:00').freeze
52
52
 
53
- def time(reason = nil)
54
- EPOCH.+(double(reason)).freeze
53
+ def time(&reason_blk)
54
+ EPOCH.+(double(&reason_blk)).freeze
55
+ end
56
+
57
+ def unexpected_token!(expected_class, token, &reason_blk)
58
+ raise "expected #{expected_class} got #{token.inspect} for #{reason_blk&.call} (XCActivityLog version #{@version})"
55
59
  end
56
60
  end
57
61
  def make_stream(tokens, class_deserializer)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xcactivitylog
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Giddins
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-05-14 00:00:00.000000000 Z
11
+ date: 2020-08-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -52,7 +52,7 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.0'
55
- description:
55
+ description:
56
56
  email:
57
57
  - segiddins@segiddins.me
58
58
  executables: []
@@ -76,7 +76,7 @@ licenses:
76
76
  metadata:
77
77
  homepage_uri: https://github.com/segiddins/xcactivitylog
78
78
  source_code_uri: https://github.com/segiddins/xcactivitylog
79
- post_install_message:
79
+ post_install_message:
80
80
  rdoc_options: []
81
81
  require_paths:
82
82
  - lib
@@ -91,9 +91,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
91
91
  - !ruby/object:Gem::Version
92
92
  version: '0'
93
93
  requirements: []
94
- rubyforge_project:
95
- rubygems_version: 2.7.6
96
- signing_key:
94
+ rubygems_version: 3.0.1
95
+ signing_key:
97
96
  specification_version: 4
98
97
  summary: Parse Xcode's xcactivitylog files (and other SLF0-serialized files)
99
98
  test_files: []