cocoapods-mapfile 0.1.6

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: cc6a56eb46579631d759dfdcabf1b96f05291024207fb6d965ff151dd1b4acb9
4
+ data.tar.gz: 5815e25f42759074cddd0c76a08c3f23fd6b56f6fd59c92394ef2adf20c149d7
5
+ SHA512:
6
+ metadata.gz: 9bb22b8025a237b09215a30808d615929f13f2aec2fe2cb96b8dc5f1a20377c7d22ffcc94b1af5d56680d70223128f39707153aa724d7bfb37353425e7260ee8
7
+ data.tar.gz: 0a34f3b7e122d0efa02b976de35d150f6cdab661a3f051864c57e6db63d47b0667a97a2d0fa22608ef60bc338b31f45abc884eb1864b5baa6a4f9fee826c5b71
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 Cat1237
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,106 @@
1
+ # cocoapods-hmap
2
+
3
+ [![License MIT](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://raw.githubusercontent.com/wangson1237/SYCSSColor/master/LICENSE) 
4
+
5
+ A CocoaPods plugin which can gen/read header map file.
6
+
7
+ **hmap-gen** is able to scan the header files of the target referenced components in the specified Cocoapods project, and generates a header map file that public to all the components
8
+ as well as generates a public and private header map file for each referenced component.
9
+
10
+ A hmap file includes four types of headers:
11
+
12
+ - header.h
13
+ - <module/header.h> **based on podspec**
14
+ - <project_name/header.h> **based on podspec**
15
+ - <podspec source header/**/header.h> **based on podspec**
16
+
17
+ At the same time, **hmap-reader** can read the header, bucktes, string_table information saved in the header map file.
18
+
19
+ - ✅ It can read hmap file.
20
+
21
+ - ✅ It can generate header map file.
22
+
23
+ ## Installation
24
+
25
+ Add this line to your application's Gemfile:
26
+
27
+ ```ruby
28
+ gem 'cocoapods-mapfile'
29
+ ```
30
+
31
+ And then execute:
32
+
33
+ ```shell
34
+ # bundle install
35
+ $ bundle install
36
+ ```
37
+
38
+ Or install it yourself as:
39
+
40
+ ```shell
41
+ # gem install
42
+ $ gem install cocoapods-mapfile
43
+ ```
44
+
45
+ ## Usage
46
+
47
+ The command should be executed in directory that contains podfile.
48
+
49
+ ```shell
50
+ # write the hmap file to podfile/Pods/Headers/HMap
51
+ $ pod hmap-gen
52
+
53
+ # write the hmap file to /project/dir/Pods/Headers/HMap
54
+ $ pod hmap-gen --project-directory=/project/dir/
55
+
56
+ # write the hmap file to /project/dir/Pods/Headers/HMap and no save origin [HEADER_SEARCH_PATHS]
57
+ $ pod hmap-gen --project-directory=/project/dir/ --nosave-origin-header-search-paths
58
+
59
+ # cleanup the hmap file
60
+ $ pod hmap-gen --clean-hmap
61
+
62
+ # read the hmap file from /hmap/dir/file
63
+ $ pod hmap-reader --hmap-path=/hmap/dir/file
64
+ ```
65
+
66
+ At same time, you can put this line in your podfile:
67
+
68
+ ```rb
69
+ plugin 'cocoapods-mapfile'
70
+ ```
71
+ This was equl:
72
+ ```rb
73
+ pod hmap-gen --project-directory=/project/dir/ --nosave-origin-header-search-paths
74
+ ```
75
+
76
+ Every time you execute pod install or pod update, `cocoapods-mapfile` will automatically generate a `header map file` for you and modify `HEAD_SEARCH_PATHS`.
77
+
78
+ ### Option && Flags
79
+
80
+ `hmap-gen/hmap-writer`:
81
+
82
+ - `--project-directory=/project/dir/`: The path to the root of the project directory.
83
+ - `--nosave-origin-header-search-paths`: This option will not save xcconfig origin [HEADER_SEARCH_PATHS] and put `hmap file path` first.
84
+ - `--clean-hmap`: This option will clean up all `hmap-gen/hmap-writer` setup for hmap.
85
+
86
+ `hmap-reader`:
87
+
88
+ - `--hmap-path=/hmap/dir/file`: The path of the hmap file.
89
+
90
+ ## Command Line Tool
91
+
92
+ Installing the 'cocoapods-mapfile' gem will also install two command-line tool `hmap_reader` and `hmap-writer` which you can use to generate header map file and read hmap file.
93
+
94
+ For more information consult `hmap_reader --help` or `hmap_writer --help`.
95
+
96
+ ## Contributing
97
+
98
+ Bug reports and pull requests are welcome on GitHub at [cocoapods-hmap](https://github.com/Cat1237/cocoapods-hmap). This project is intended to be a safe, welcoming space for collaboration.
99
+
100
+ ## License
101
+
102
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
103
+
104
+ ## Code of Conduct
105
+
106
+ Everyone interacting in the Cocoapods::Hmap project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/cocoapods-hmap/blob/master/CODE_OF_CONDUCT.md).
data/bin/hmap_reader ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+
5
+ if $PROGRAM_NAME == __FILE__
6
+ ENV['BUNDLE_GEMFILE'] = File.expand_path('../Gemfile', __dir__)
7
+ require 'bundler/setup'
8
+ end
9
+
10
+ require 'cocoapods-hmap/command/hmap_reader'
11
+
12
+ Pod::Command::HMapReader.run(ARGV)
data/bin/hmap_writer ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ if $PROGRAM_NAME == __FILE__
5
+ ENV['BUNDLE_GEMFILE'] = File.expand_path('../Gemfile', __dir__)
6
+ require 'bundler/setup'
7
+ end
8
+
9
+ require 'cocoapods-hmap/command/hmap_gen'
10
+
11
+ Pod::Command::HMapGen.run(ARGV)
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cocoapods_hmap'
4
+ require 'cocoapods'
5
+
6
+ module Pod
7
+ class Command
8
+ # hmap file gen cmd
9
+ class HMapGen < Command
10
+ # summary
11
+ self.summary = 'Analyzes the dependencies and gen each dependencie mapfile.'
12
+
13
+ self.description = <<-DESC
14
+ Analyzes the dependencies of any cocoapods projects and gen each dependencie mapfile.
15
+ DESC
16
+
17
+ def initialize(argv)
18
+ super
19
+ project_directory = argv.option('project-directory')
20
+ @save_origin_header_search_paths = !argv.flag?('nosave-origin-header-search-paths', false)
21
+ @clean_hmap = argv.flag?('clean-hmap', false)
22
+
23
+ return if project_directory.nil?
24
+
25
+ @project_directory = Pathname.new(project_directory).expand_path
26
+ config.installation_root = @project_directory
27
+ end
28
+
29
+ def validate!
30
+ super
31
+ verify_podfile_exists!
32
+ end
33
+
34
+ # help
35
+ def self.options
36
+ [
37
+ ['--project-directory=/project/dir/', 'The path to the root of the project
38
+ directory'],
39
+ ['--nosave-origin-header-search-paths', 'This option will not save xcconfig origin [HEADER_SEARCH_PATHS] and put hmap file first'],
40
+ ['--clean-hmap', 'This option will clean up all hmap-gen setup for hmap.']
41
+ ].concat(super)
42
+ end
43
+
44
+ def run
45
+ UI.section "\n[hmap-gen] start.............." do
46
+ HMap::MapFileWriter.new(@save_origin_header_search_paths, @clean_hmap)
47
+ end
48
+ UI.puts('[hmap-gen] finish..............')
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: false
2
+
3
+ require 'cocoapods_hmap'
4
+ require 'cocoapods'
5
+
6
+ module Pod
7
+ class Command
8
+ # hmap file reader cmd
9
+ class HMapReader < Command
10
+ self.summary = 'Read mapfile and puts result.'
11
+
12
+ self.description = <<-DESC
13
+ Read mapfile and puts result, header, buckets, string_table.
14
+ DESC
15
+
16
+ def initialize(argv)
17
+ super
18
+ mapfile_path = argv.option('hmap-path')
19
+ @mapfile_path = Pathname.new(mapfile_path).expand_path unless mapfile_path.nil?
20
+ end
21
+
22
+ def validate!
23
+ super
24
+ banner! if help?
25
+ raise '[ERROR]: --hmap-path no set'.red unless File.exist?(@mapfile_path)
26
+ end
27
+
28
+ def self.options
29
+ [
30
+ ['--hmap-path=/hmap/dir/file', 'The path of the hmap file']
31
+ ].concat(super)
32
+ end
33
+
34
+ def run
35
+ UI.section "\n[hmap-reader] start..............\n".yellow do
36
+ HMap::MapFileReader.new(@mapfile_path)
37
+ end
38
+ UI.puts("\n[hmap-reader] finish..............\n".yellow)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HMap
4
+ # A generic HMap error in execution.
5
+ class HMapError < RuntimeError
6
+ end
7
+
8
+ # Raised when a file is not a HMap.
9
+ class NotAHMapOError < HMapError
10
+ end
11
+
12
+ # Raised when a file is too short to be a valid HMap file.
13
+ class TruncatedFileError < NotAHMapOError
14
+ def initialize
15
+ super 'File is too short to be a valid HMap'
16
+ end
17
+ end
18
+
19
+ # Raised when a file's magic bytes are not valid HMap magic.
20
+ class MagicError < NotAHMapOError
21
+ # @param num [Integer] the unknown number
22
+ def initialize(magic)
23
+ super format('Unrecognized HMap magic: 0x%02<magic>x', magic: magic)
24
+ end
25
+ end
26
+
27
+ # Raised when a class is not the class of obj.
28
+ class ClassIncludedError < HMapError
29
+ def initialize(cls1, cls2)
30
+ super format('%<cls1>s must be the %<cls2>s of obj', cls1: cls1, cls2: cls2)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HMap
4
+ # hmap file reader
5
+ class MapFileReader
6
+ # @return [String, nil] the filename loaded from, or nil if loaded from a binary
7
+ # string
8
+ attr_reader :filename
9
+ # # @return [Hash] any parser options that the instance was created with
10
+ # attr_reader :options
11
+
12
+ # @return true/false the swapped of the mapfile
13
+ attr_reader :swapped
14
+
15
+ # @return [HMap::HMapHeader]
16
+ attr_reader :header
17
+
18
+ # @return [Hash<HMap::HMapBucket => HMap::HMapBucketStr>] an array of the file's bucktes
19
+ # @note bucktes are provided in order of ascending offset.
20
+ attr_reader :bucktes
21
+
22
+ def initialize(path)
23
+ raise ArgumentError, "#{path}: no such file" unless File.file?(path)
24
+
25
+ @filename = path
26
+ @raw_data = File.open(@filename, 'rb', &:read)
27
+ populate_fields
28
+ puts description
29
+ end
30
+
31
+ # Populate the instance's fields with the raw HMap data.
32
+ # @return [void]
33
+ # @note This method is public, but should (almost) never need to be called.
34
+ def populate_fields
35
+ @header = populate_hmap_header
36
+ string_t = @raw_data[header.strings_offset..-1]
37
+ @bucktes = populate_buckets do |bucket|
38
+ bucket_s = bucket.to_a.map do |key|
39
+ string_t[key..-1].match(/[^\0]+/)[0]
40
+ end
41
+ HMapBucketStr.new(*bucket_s)
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ # The file's HMapheader structure.
48
+ # @return [HMap::HMapHeader]
49
+ # @raise [TruncatedFileError] if the file is too small to have a valid header
50
+ # @api private
51
+ def populate_hmap_header
52
+ raise TruncatedFileError if @raw_data.size < HMapHeader.bytesize + 8 * HMapBucket.bytesize
53
+
54
+ populate_and_check_magic
55
+ HMapHeader.new_from_bin(swapped, @raw_data[0, HMapHeader.bytesize])
56
+ end
57
+
58
+ # Read just the file's magic number and check its validity.
59
+ # @return [Integer] the magic
60
+ # @raise [MagicError] if the magic is not valid HMap magic
61
+ # @api private
62
+ def populate_and_check_magic
63
+ magic = @raw_data[0..3].unpack1('N')
64
+ raise MagicError, magic unless Utils.magic?(magic)
65
+
66
+ version = @raw_data[4..5].unpack1('n')
67
+ @swapped = Utils.swapped_magic?(magic, version)
68
+ end
69
+
70
+ # All buckets in the file.
71
+ # @return [Array<HMap::HMapBucket>] an array of buckets
72
+ # @api private
73
+ def populate_buckets
74
+ bucket_offset = header.class.bytesize
75
+ bucktes = []
76
+ header.num_buckets.times do |i|
77
+ bucket = HMapBucket.new_from_bin(swapped, @raw_data[bucket_offset, HMapBucket.bytesize])
78
+ bucket_offset += HMapBucket.bytesize
79
+ next if bucket.key == HEADER_CONST[:HMAP_EMPTY_BUCKT_KEY]
80
+
81
+ bucktes[i] = { bucket => yield(bucket) }
82
+ end
83
+ bucktes
84
+ end
85
+
86
+ # description
87
+ def description
88
+ sum = " Header map: #{filename}\n" + header.description
89
+ bucktes.each_with_index do |buckte_h, index|
90
+ sum += "\t- Bucket: #{index}" + Utils.safe_encode(buckte_h.values[0].description, 'UTF-8') unless buckte_h.nil?
91
+ sum
92
+ end
93
+ sum
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,267 @@
1
+ # frozen_string_literal: true
2
+
3
+ # require 'cocoapods-hmap/view'
4
+
5
+ module HMap
6
+ HEADER_CONST = {
7
+ HMAP_HEADER_MAGIC_NUMBER: 0x686d6170,
8
+ HMAP_HEADER_VERSION: 0x0001,
9
+ HMAP_EMPTY_BUCKT_KEY: 0,
10
+ HMAP_SWAPPED_MAGIC: 0x70616d68,
11
+ HMAP_SWAPPED_VERSION: 0x0100
12
+ }.freeze
13
+
14
+ # A general purpose pseudo-structure.
15
+ # @abstract
16
+ class HMapStructure
17
+ # The String#unpack format of the data structure.
18
+ # @return [String] the unpacking format
19
+ # @api private
20
+ FORMAT = ''
21
+
22
+ # The size of the data structure, in bytes.
23
+ # @return [Integer] the size, in bytes
24
+ # @api private
25
+ SIZEOF = 0
26
+
27
+ SWAPPED = true
28
+
29
+ # @return [Integer] the size, in bytes, of the represented structure.
30
+ def self.bytesize
31
+ self::SIZEOF
32
+ end
33
+
34
+ def self.format
35
+ self::FORMAT
36
+ end
37
+
38
+ def self.swapped?
39
+ self::SWAPPED
40
+ end
41
+
42
+ # @param endianness [Symbol] either `:big` or `:little`
43
+ # @param bin [String] the string to be unpacked into the new structure
44
+ # @return [HMap::HMapStructure] the resulting structure
45
+ # @api private
46
+ def self.new_from_bin(swapped, bin)
47
+ format = Utils.specialize_format(self::FORMAT, swapped)
48
+ new(*bin.unpack(format))
49
+ end
50
+
51
+ def serialize
52
+ [].pack(format)
53
+ end
54
+
55
+ # @return [Hash] a hash representation of this {HMapStructure}.
56
+ def to_h
57
+ {
58
+ 'structure' => {
59
+ 'format' => self.class::FORMAT,
60
+ 'bytesize' => self.class.bytesize
61
+ }
62
+ }
63
+ end
64
+ end
65
+
66
+ # HMapHeader structure.
67
+ # @see https://clang.llvm.org/doxygen/structclang_1_1HMapHeader.html
68
+ # @abstract
69
+ class HMapHeader < HMapStructure
70
+ # @return [HMap::HMapView, nil] the raw view associated with the load command,
71
+ # or nil if the HMapHeader was created via {create}.
72
+ attr_reader :num_entries, :magic, :version, :reserved, :strings_offset, :num_buckets, :max_value_length
73
+
74
+ FORMAT = 'L=1S=2L4'
75
+ # @see HMapStructure::SIZEOF
76
+ # @api private
77
+ SIZEOF = 24
78
+
79
+ # @api private
80
+ def initialize(magic, version, reserved, strings_offset, num_entries, num_buckets, max_value_length)
81
+ @magic = magic
82
+ @version = version
83
+ @reserved = reserved
84
+ @strings_offset = strings_offset
85
+ @num_entries = num_entries
86
+ @num_buckets = num_buckets
87
+ @max_value_length = max_value_length
88
+ super()
89
+ end
90
+
91
+ # @return [String] the serialized fields of the mafile
92
+ def serialize
93
+ format = Utils.specialize_format(FORMAT, SWAPPED)
94
+ [magic, version, reserved, strings_offset, num_entries, num_buckets, max_value_length].pack(format)
95
+ end
96
+
97
+ def description
98
+ <<-DESC
99
+ Hash bucket count: #{@num_buckets}
100
+ String table entry count: #{@num_entries}
101
+ Max value length: #{@max_value_length}
102
+ DESC
103
+ end
104
+
105
+ def to_h
106
+ {
107
+ 'magic' => magic,
108
+ 'version' => version,
109
+ 'reserved' => reserved,
110
+ 'strings_offset' => strings_offset,
111
+ 'num_entries' => num_entries,
112
+ 'num_buckets' => num_buckets,
113
+ 'max_value_length' => max_value_length
114
+ }.merge super
115
+ end
116
+ end
117
+
118
+ # HMapBucketStr => HMapBucket.
119
+ # @see https://clang.llvm.org/doxygen/structclang_1_1HMapHeader.html
120
+ # @abstract
121
+ class HMapBucketStr
122
+ attr_reader :uuid, :key, :perfix, :suffix
123
+
124
+ def initialize(key, perfix, suffix)
125
+ @uuid = Utils.string_downcase_hash(key)
126
+ @key = key
127
+ @perfix = perfix
128
+ @suffix = suffix
129
+ @str_ins = {}
130
+ end
131
+
132
+ def bucket_to_string(headers, index)
133
+ bucket = [key, perfix, suffix]
134
+ bucket.inject('') do |sum, arg|
135
+ if headers[arg].nil?
136
+ headers[arg] = sum.length + index
137
+ sum += "#{Utils.safe_encode(arg, 'ASCII-8BIT')}\0"
138
+ end
139
+ @str_ins[arg] = headers[arg]
140
+ sum
141
+ end
142
+ end
143
+
144
+ def bucket
145
+ HMapBucket.new(@str_ins[@key], @str_ins[@perfix], @str_ins[@suffix])
146
+ end
147
+
148
+ # @return [String] the serialized fields of the mafile
149
+ def serialize
150
+ bucket.serialize
151
+ end
152
+
153
+ def description
154
+ <<-DESC
155
+ Key #{@key} -> Prefix #{@perfix}, Suffix #{@suffix}
156
+ DESC
157
+ end
158
+
159
+ def to_h
160
+ {
161
+ 'key' => { 'index' => str_ins[@key], 'key' => @key },
162
+ 'perfix' => { 'index' => str_ins[@perfix], 'perfix' => @perfix },
163
+ 'suffix' => { 'index' => str_ins[@suffix], 'suffix' => @suffix }
164
+ }
165
+ end
166
+ end
167
+
168
+ # HMapBucket structure.
169
+ # @see https://clang.llvm.org/doxygen/structclang_1_1HMapHeader.html
170
+ # @abstract
171
+ class HMapBucket < HMapStructure
172
+ attr_accessor :key, :perfix, :suffix
173
+
174
+ SIZEOF = 12
175
+ FORMAT = 'L=3'
176
+
177
+ def initialize(key, perfix, suffix)
178
+ @key = key
179
+ @perfix = perfix
180
+ @suffix = suffix
181
+ super()
182
+ end
183
+
184
+ # @return [String] the serialized fields of the mafile
185
+ def serialize
186
+ format = Utils.specialize_format(FORMAT, SWAPPED)
187
+ [key, perfix, suffix].pack(format)
188
+ end
189
+
190
+ def to_a
191
+ [key, perfix, suffix]
192
+ end
193
+
194
+ def to_h
195
+ {
196
+ 'key' => key,
197
+ 'perfix' => perfix,
198
+ 'suffix' => suffix
199
+ }.merge super
200
+ end
201
+ end
202
+
203
+ # HMap blobs.
204
+ class HMapData
205
+ def initialize(buckets)
206
+ super()
207
+ count = buckets.count
208
+ nums = num_buckets(count, Utils.next_power_of_two(count))
209
+ entries = entries(count, nums)
210
+ @header = populate_hmap_header(nums, entries)
211
+ @buckets = add_bucket(buckets, nums)
212
+ end
213
+
214
+ def num_buckets(count, pow2)
215
+ if count < 8
216
+ pow2 <<= 1 if count * 4 >= pow2 * 3
217
+ pow2 < 8 ? 8 : pow2
218
+ else
219
+ index = count > 341 ? 2 : -3
220
+ padding = count / 85 % 7 + index
221
+ Utils.next_power_of_two(count * 3 + padding)
222
+ end
223
+ end
224
+
225
+ def entries(count, nums)
226
+ return count if nums == 8
227
+
228
+ last_pow = nums >> 1
229
+ index = last_pow < 1024 ? 3 : -2
230
+ count - (last_pow + 1 + index) / 3 + 1 + last_pow
231
+ end
232
+
233
+ # @return [String] the serialized fields of the mafile
234
+ def serialize
235
+ @header.serialize + @buckets.inject('') do |sum, bucket|
236
+ sum += if bucket.nil?
237
+ empty_b = [HEADER_CONST[:HMAP_EMPTY_BUCKT_KEY]]*3
238
+ empty_b.pack('L<3')
239
+ else
240
+ bucket
241
+ end
242
+ sum
243
+ end
244
+ end
245
+
246
+ private
247
+
248
+ def add_bucket(buckets, num)
249
+ buckets.each_with_object(Array.new(num)) do |bucket, sum|
250
+ serialize = bucket.serialize
251
+ i = Utils.index_of_range(bucket.uuid, num)
252
+ loop do
253
+ sum[i] = serialize if sum[i].nil?
254
+ break if serialize == sum[i]
255
+
256
+ i = Utils.index_of_range(i += 1, num)
257
+ end
258
+ end
259
+ end
260
+
261
+ def populate_hmap_header(num_buckets, entries)
262
+ strings_offset = HMapHeader.bytesize + HMapBucket.bytesize * num_buckets
263
+ HMapHeader.new(HEADER_CONST[:HMAP_HEADER_MAGIC_NUMBER],
264
+ HEADER_CONST[:HMAP_HEADER_VERSION], 0, strings_offset, entries, num_buckets, 0)
265
+ end
266
+ end
267
+ end
@@ -0,0 +1,145 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cocoapods'
4
+
5
+ module HMap
6
+ # mapfile dir name
7
+ # @api private
8
+ HMAP_DIR = 'HMap'
9
+ # mapfile type
10
+ # @note public => pods public,
11
+ # private => pods private,
12
+ # all => public + private + extra.
13
+ # @api private
14
+ HMMAP_TYPE = {
15
+ public_header_files: 'public',
16
+ private_header_files: 'private',
17
+ source_files: 'all'
18
+ }.freeze
19
+
20
+ # build setting HEAD_SEARCH_PATHs
21
+ HEAD_SEARCH_PATHS = 'HEADER_SEARCH_PATHS'
22
+ # Helper module which returns handle method from MapFileWriter.
23
+ class MapFileWriter
24
+ # @param save_origin_header_search_paths save_origin_header_search_paths
25
+ # @param clean_hmap clean up all hmap setup
26
+ def initialize(save_origin_header_search_paths, clean_hmap)
27
+ config = Pod::Config.instance
28
+ analyze = Helper::Pods.pod_analyze(config)
29
+ hmap_dir = hmap_dir(config)
30
+ targets = analyze.targets
31
+ pod_targets = analyze.pod_targets
32
+ clean_hmap(clean_hmap, hmap_dir, targets, pod_targets)
33
+ return if clean_hmap
34
+
35
+ @save_origin_header_search_paths = save_origin_header_search_paths
36
+ gen_mapfile(targets, pod_targets, hmap_dir)
37
+ end
38
+
39
+ private
40
+
41
+ def hmap_dir(config)
42
+ puts "Current podfile dir: #{config.installation_root}"
43
+ hmap_dir = File.join(config.sandbox.headers_root, HMAP_DIR)
44
+ puts "Current HMap dir: #{hmap_dir}"
45
+ hmap_dir
46
+ end
47
+
48
+ def clean_hmap(clean_hmap, hmap_dir, *targets)
49
+ return unless clean_hmap
50
+
51
+ FileUtils.rm_rf(hmap_dir)
52
+ targets.each do |tg|
53
+ Utils.clean_target_build_setting(tg, HEAD_SEARCH_PATHS)
54
+ end
55
+ end
56
+
57
+ def gen_mapfile(targets, pod_targets, hmap_d)
58
+ puts('Inspecting targets to integrate ')
59
+ merge_all_pods_target_headers_mapfile(pod_targets, hmap_d)
60
+ merge_all_target_public_mapfile(targets, hmap_d)
61
+ create_each_target_mapfile(pod_targets, hmap_d)
62
+ end
63
+
64
+ def from_header_mappings_by_file_accessor(header_h, buckets, target, hmap_type)
65
+ hmap_s = 'headers'
66
+ hmap_s = "#{HMMAP_TYPE[hmap_type]}_#{hmap_s}" unless hmap_type == :source_files
67
+ headers = target.header_mappings_by_file_accessor.keys.flat_map(&hmap_s.to_sym)
68
+ s_headers = Helper::Pods.pod_target_source_header_map(target, hmap_type)
69
+ headers.each_with_object(buckets) do |header_f, sum|
70
+ keys = header_perfix(target, header_f, s_headers)
71
+ sum[0] += header_to_hash(keys, header_h, sum[0].length, sum[1])
72
+ end
73
+ end
74
+
75
+ def header_perfix(target, file, s_headers)
76
+ key = file.basename.to_s
77
+ project_name = "#{target.project_name}/#{file.basename}"
78
+ product_module_name = "#{target.product_module_name}/#{file.basename}"
79
+ perfix = "#{file.dirname}/"
80
+ keys = [key, project_name, product_module_name] + (s_headers[key].nil? ? [] : s_headers[key])
81
+ keys.compact.uniq.inject([]) do |sum, name|
82
+ sum << [name, perfix, key]
83
+ end
84
+ end
85
+
86
+ def header_to_hash(keys, headers, index, buckets)
87
+ keys.inject('') do |sum, bucket|
88
+ buckte = HMapBucketStr.new(*bucket)
89
+ string_t = buckte.bucket_to_string(headers, index + sum.length)
90
+ buckets.push(buckte)
91
+ sum + string_t
92
+ end
93
+ end
94
+
95
+ def merge_all_target_public_mapfile(targets, hmap_dir)
96
+ method = method(:from_header_mappings_by_file_accessor)
97
+ targets.each do |target|
98
+ hmap_name = "All-Pods-Public-#{target.name}-hmap.hmap"
99
+ single_target_mapfile(target.pod_targets, hmap_dir, hmap_name, method)
100
+ change_target_xcconfig_header_search_path([hmap_name], true, *targets)
101
+ end
102
+ end
103
+
104
+ def merge_all_pods_target_headers_mapfile(pod_targets, hmap_dir)
105
+ method = method(:from_header_mappings_by_file_accessor)
106
+ hmap_name = 'All-Pods-All-Header-hmap.hmap'
107
+ single_target_mapfile(pod_targets, hmap_dir, hmap_name, method, :source_files)
108
+ change_target_xcconfig_header_search_path([hmap_name], true, *pod_targets)
109
+ end
110
+
111
+ def create_each_target_mapfile(pod_targets, hmap_dir)
112
+ pod_targets.each do |target|
113
+ HMMAP_TYPE.flat_map do |key, value|
114
+ hmap_name = "#{target.name}-#{value}-hmap.hmap"
115
+ method = method(:from_header_mappings_by_file_accessor)
116
+ single_target_mapfile([target], File.join(hmap_dir, target.name), hmap_name, method, key)
117
+ "#{target.name}/#{hmap_name}" if key == :source_files
118
+ end.compact
119
+ # change_target_xcconfig_header_search_path(hmap_h, false, target)
120
+ end
121
+ end
122
+
123
+ def single_target_mapfile(pod_targets, hmap_dir, hmap_name, headers, hmap_type = :public_header_files)
124
+ hmap_path = Pathname(File.join(hmap_dir, hmap_name))
125
+ header_h = {}
126
+ buckets = pod_targets.inject(["\0", []]) do |bucktes, target|
127
+ headers.call(header_h, bucktes, target, hmap_type)
128
+ end
129
+ wirte_mapfile_to_path(hmap_path, buckets)
130
+ end
131
+
132
+ def wirte_mapfile_to_path(hmap_path, buckets)
133
+ print "\t - save hmap file to path:"
134
+ puts hmap_path.to_s.yellow
135
+ MapFile.new(*buckets).write(hmap_path)
136
+ end
137
+
138
+ def change_target_xcconfig_header_search_path(hmap_h, use_headermap, *targets)
139
+ Utils.target_xcconfig_path(targets) do |xc|
140
+ Utils.chang_xcconfig_header_search_path(xc, hmap_h, use_headermap: use_headermap,
141
+ save_origin: @save_origin_header_search_paths)
142
+ end
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HMap
4
+ # hmap file writer
5
+ class MapFile
6
+ # @return mapfile string_table
7
+ attr_reader :string_table
8
+
9
+ # @return [Array<HMap::HMapBucketStr>] an array of the file's bucktes
10
+ # @note bucktes are provided in order of ascending offset.
11
+ attr_reader :buckets
12
+
13
+ # @api private
14
+ def initialize(strings, buckets)
15
+ @string_table = strings
16
+ @buckets = buckets
17
+ @map_data = HMapData.new(buckets)
18
+ end
19
+
20
+ # @return [String] the serialized fields of the mafile
21
+ def serialize
22
+ @map_data.serialize + @string_table
23
+ end
24
+
25
+ # Write all mafile data to the given filename.
26
+ # @param filename [String] the file to write to
27
+ # @return [void]
28
+ def write(path)
29
+ contents = serialize
30
+ Utils.update_changed_file(path, contents)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cocoapods'
4
+
5
+ module HMap
6
+ # A collection of Helper functions used throughout cocoapods-hmap.
7
+ module Helper
8
+ # A collection of Pods Helper functions used throughout cocoapods-hmap.
9
+ module Pods
10
+ HEADER_EXTENSIONS = Pod::Sandbox::FileAccessor::HEADER_EXTENSIONS
11
+ def self.pod_analyze(config)
12
+ podfile = Pod::Podfile.from_file(config.podfile_path)
13
+ lockfile = Pod::Lockfile.from_file(config.lockfile_path)
14
+ Pod::Installer::Analyzer.new(config.sandbox, podfile, lockfile).analyze
15
+ end
16
+
17
+ # @!group Private helpers
18
+
19
+ # Returns the list of the paths founds in the file system for the
20
+ # attribute with given name. It takes into account any dir pattern and
21
+ # any file excluded in the specification.
22
+ #
23
+ # @param [Symbol] attribute
24
+ # the name of the attribute.
25
+ #
26
+ # @return [Array<Pathname>] the paths.
27
+ #
28
+ def self.paths_for_attribute(key, attribute, include_dirs: false)
29
+ file_patterns = key.spec_consumer.send(attribute)
30
+ options = {
31
+ exclude_patterns: key.spec_consumer.exclude_files,
32
+ dir_pattern: Pod::Sandbox::FileAccessor::GLOB_PATTERNS[attribute],
33
+ include_dirs: include_dirs
34
+ }
35
+ extensions = HEADER_EXTENSIONS
36
+ key.path_list.relative_glob(file_patterns, options).map do |f|
37
+ [f, key.path_list.root.join(f)] if extensions.include?(f.extname)
38
+ end.compact
39
+ end
40
+
41
+ def self.pod_target_source_header(target, hmap_t)
42
+ target.header_mappings_by_file_accessor.keys.flat_map do |key|
43
+ paths_for_attribute(key, hmap_t)
44
+ end
45
+ end
46
+
47
+ def self.pod_target_source_header_map(target, hmap_t)
48
+ pod_target_source_header(target, hmap_t).each_with_object({}) do |f, sum|
49
+ file = f[1]
50
+ key = f[0].to_s
51
+ r_key = file.basename.to_s
52
+ sum[r_key] = [key, r_key].uniq
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,140 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cocoapods'
4
+
5
+ module HMap
6
+ # A collection of utility functions used throughout cocoapods-hmap.
7
+ module Utils
8
+ HEADER_EXTENSIONS = Pod::Sandbox::FileAccessor::HEADER_EXTENSIONS
9
+
10
+ def self.index_of_range(num, range)
11
+ num &= range - 1
12
+ num
13
+ end
14
+
15
+ def self.power_of_two?(num)
16
+ num != 0 && (num & (num - 1)).zero?
17
+ end
18
+
19
+ def self.next_power_of_two(num)
20
+ num |= (num >> 1)
21
+ num |= (num >> 2)
22
+ num |= (num >> 4)
23
+ num |= (num >> 8)
24
+ num |= (num >> 16)
25
+ num |= (num >> 32)
26
+ num + 1
27
+ end
28
+
29
+ def self.hash_set_value(hash, *args)
30
+ args.each do |arg|
31
+ hash.merge(arg)
32
+ end
33
+ hash
34
+ end
35
+
36
+ def self.specialize_format(format, swapped)
37
+ modifier = swapped ? '<' : '>'
38
+ format.tr('=', modifier)
39
+ end
40
+
41
+ def self.string_downcase_hash(str)
42
+ str.downcase.bytes.inject(0) do |sum, value|
43
+ sum += value * 13
44
+ sum
45
+ end
46
+ end
47
+
48
+ def self.update_changed_file(path, contents)
49
+ if path.exist?
50
+ content_stream = StringIO.new(contents)
51
+ identical = File.open(path, 'rb') { |f| FileUtils.compare_stream(f, content_stream) }
52
+ return if identical
53
+ end
54
+ path.dirname.mkpath
55
+ File.open(path, 'w') { |f| f.write(contents) }
56
+ end
57
+
58
+ def self.swapped_magic?(magic, version)
59
+ magic.eql?(HEADER_CONST[:HMAP_SWAPPED_MAGIC]) && version.eql?(HEADER_CONST[:HMAP_SWAPPED_VERSION])
60
+ end
61
+
62
+ def self.magic?(magic)
63
+ magic.eql?(HEADER_CONST[:HMAP_SWAPPED_MAGIC]) || magic.eql?(HEADER_CONST[:HMAP_HEADER_MAGIC_NUMBER])
64
+ end
65
+
66
+ def self.safe_encode(string, target_encoding)
67
+ string.encode(target_encoding)
68
+ rescue Encoding::InvalidByteSequenceError
69
+ string.force_encoding(target_encoding)
70
+ rescue Encoding::UndefinedConversionError
71
+ string.encode(target_encoding, fallback: lambda { |c|
72
+ c.force_encoding(target_encoding)
73
+ })
74
+ end
75
+
76
+ def self.clean_target_build_setting(targets, build_setting)
77
+ target_xcconfig_path(targets) do |xc|
78
+ clean_build_setting_to_xcconfig(xc, build_setting)
79
+ end
80
+ end
81
+
82
+ def self.target_xcconfig_path(targets)
83
+ targets.each do |target|
84
+ raise ClassIncludedError.new(target.class, Pod::Target) unless target.is_a?(Pod::Target)
85
+
86
+ config_h = Pod::Target.instance_method(:build_settings).bind(target).call
87
+ config_h.each_key do |configuration_name|
88
+ xcconfig = target.xcconfig_path(configuration_name)
89
+ yield(xcconfig) if block_given?
90
+ end
91
+ end
92
+ end
93
+
94
+ def self.chang_xcconfig_header_search_path(xcconfig, hmap_h, use_headermap: true, save_origin: true)
95
+ hmap_header_serach_paths = hmap_h.inject('') do |sum, hmap_n|
96
+ hmap_pod_root_path = "${PODS_ROOT}/Headers/#{HMAP_DIR}/#{hmap_n}"
97
+ sum + "\"#{hmap_pod_root_path}\" "
98
+ end
99
+ save_build_setting_to_xcconfig(xcconfig, hmap_header_serach_paths, HEAD_SEARCH_PATHS,
100
+ save_origin: save_origin) do |xc|
101
+ xc.attributes['USE_HEADERMAP'] = 'NO' unless use_headermap
102
+ end
103
+ end
104
+
105
+ def self.save_build_setting_to_xcconfig(path, value, build_setting, save_origin: true)
106
+ xc = Xcodeproj::Config.new(path)
107
+ save_origin_build_setting = "SAVE_#{build_setting}"
108
+ hmap_build_setting = "HMAP_PODS_#{build_setting}"
109
+ origin_build_setting = xc.attributes[build_setting]
110
+ unless !origin_build_setting.nil? && origin_build_setting.include?(hmap_build_setting)
111
+ xc.attributes[save_origin_build_setting] =
112
+ origin_build_setting
113
+ end
114
+
115
+ value = "#{value} ${#{save_origin_build_setting}}" if save_origin
116
+ xc.attributes[hmap_build_setting] = value
117
+ xc.attributes[build_setting] = "${#{hmap_build_setting}}"
118
+ yield(xc) if block_given?
119
+ xc.save_as(path)
120
+ end
121
+
122
+ def self.clean_build_setting_to_xcconfig(path, build_setting)
123
+ xc = Xcodeproj::Config.new(path)
124
+ save_origin_build_setting = "SAVE_#{build_setting}"
125
+ hmap_build_setting = "HMAP_PODS_#{build_setting}"
126
+ origin_build_setting = xc.attributes[save_origin_build_setting]
127
+ puts "\t -xcconfig path: #{path}"
128
+ if origin_build_setting.nil?
129
+ puts "\t don't have #{save_origin_build_setting} in xcconfig file.".red
130
+ return
131
+ end
132
+ xc.attributes[build_setting] = origin_build_setting
133
+ xc.attributes.delete(save_origin_build_setting)
134
+ xc.attributes.delete(hmap_build_setting)
135
+ xc.attributes['USE_HEADERMAP'] = 'YES'
136
+ xc.save_as(path)
137
+ puts "\t clean finish."
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CocoapodsHMap
4
+ VERSION = '0.1.6'
5
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HMap
4
+ # A representation of some unspecified hmap data.
5
+ class HMapView
6
+ # @return [String] the raw hmap data
7
+ attr_reader :raw_data
8
+
9
+ # @return [Symbol] the endianness of the data (`:big` or `:little`)
10
+ attr_reader :endianness
11
+
12
+ # @return [Integer] the offset of the relevant data (in {#raw_data})
13
+ attr_reader :offset
14
+
15
+ # Creates a new HMapView.
16
+ # @param raw_data [String] the raw hmap data
17
+ # @param endianness [Symbol] the endianness of the data
18
+ # @param offset [Integer] the offset of the relevant data
19
+ def initialize(raw_data, endianness, offset)
20
+ @raw_data = raw_data
21
+ @endianness = endianness
22
+ @offset = offset
23
+ end
24
+
25
+ # @return [Hash] a hash representation of this {HMapView}.
26
+ def to_h
27
+ {
28
+ 'endianness' => endianness,
29
+ 'offset' => offset
30
+ }
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The primary namespace for cocoapods-hmap.
4
+ module HMap
5
+ require_relative 'cocoapods-hmap/version'
6
+ require_relative 'cocoapods-hmap/view'
7
+ require_relative 'cocoapods-hmap/hmap_struct'
8
+ require_relative 'cocoapods-hmap/utils'
9
+ require_relative 'cocoapods-hmap/pods_helper'
10
+ require_relative 'cocoapods-hmap/exceptions'
11
+
12
+ autoload :MapFileReader, 'cocoapods-hmap/hmap_reader'
13
+ autoload :MapFileWriter, 'cocoapods-hmap/hmap_writer'
14
+ autoload :MapFile, 'cocoapods-hmap/mapfile'
15
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cocoapods-hmap/command/hmap_gen'
4
+ require 'cocoapods-hmap/command/hmap_reader'
5
+
6
+ module Pod
7
+ # hook
8
+ module CocoaPodsHMapHook
9
+ HooksManager.register('cocoapods-mapfile', :post_install) do
10
+ Command::HMapGen.run(["--project-directory=#{Config.instance.installation_root}", "--nosave-origin-header-search-paths"])
11
+ end
12
+ end
13
+ end
metadata ADDED
@@ -0,0 +1,133 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cocoapods-mapfile
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.6
5
+ platform: ruby
6
+ authors:
7
+ - Cat1237
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-07-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.1'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: coveralls
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: cocoapods
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '1.6'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '1.6'
83
+ description: header_reader lets your read Xcode header map file. header-writer lets
84
+ your analyze the project pod dependencies and gen header map file for all pods.
85
+ email:
86
+ - wangson1237@outlook.com
87
+ executables:
88
+ - hmap_reader
89
+ - hmap_writer
90
+ extensions: []
91
+ extra_rdoc_files: []
92
+ files:
93
+ - LICENSE
94
+ - README.md
95
+ - bin/hmap_reader
96
+ - bin/hmap_writer
97
+ - lib/cocoapods-hmap/command/hmap_gen.rb
98
+ - lib/cocoapods-hmap/command/hmap_reader.rb
99
+ - lib/cocoapods-hmap/exceptions.rb
100
+ - lib/cocoapods-hmap/hmap_reader.rb
101
+ - lib/cocoapods-hmap/hmap_struct.rb
102
+ - lib/cocoapods-hmap/hmap_writer.rb
103
+ - lib/cocoapods-hmap/mapfile.rb
104
+ - lib/cocoapods-hmap/pods_helper.rb
105
+ - lib/cocoapods-hmap/utils.rb
106
+ - lib/cocoapods-hmap/version.rb
107
+ - lib/cocoapods-hmap/view.rb
108
+ - lib/cocoapods_hmap.rb
109
+ - lib/cocoapods_plugin.rb
110
+ homepage: https://github.com/Cat1237/cocoapods-hmap.git
111
+ licenses:
112
+ - MIT
113
+ metadata: {}
114
+ post_install_message:
115
+ rdoc_options: []
116
+ require_paths:
117
+ - lib
118
+ required_ruby_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: '2.5'
123
+ required_rubygems_version: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ version: '0'
128
+ requirements: []
129
+ rubygems_version: 3.1.6
130
+ signing_key:
131
+ specification_version: 4
132
+ summary: Read or write header map file.
133
+ test_files: []