cocoapods-mapfile 0.1.6

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