Class: Pod::Command::GeneratorProj

Inherits:
Pod::Command show all
Defined in:
lib/cocoapods-generator-minlison/command/generator.rb

Overview

This is an example of a cocoapods plugin adding a top-level subcommand to the ‘pod’ command.

You can also create subcommands of existing or new commands. Say you wanted to add a subcommand to ‘list` to show newly deprecated pods, (e.g. `pod list deprecated`), there are a few things that would need to change.

  • move this file to ‘lib/pod/command/list/deprecated.rb` and update the class to exist in the the Pod::Command::List namespace

  • change this class to extend from ‘List` instead of `Command`. This tells the plugin system that it is a subcommand of `list`.

  • edit ‘lib/cocoapods_plugins.rb` to require this file

Constant Summary collapse

SPEC_SUBGROUPS =
{
  :resources  => 'Resources',
  :frameworks => 'Frameworks',
}
ENABLE_OBJECT_USE_OBJC_FROM =
{
  :ios => Version.new('6'),
  :osx => Version.new('10.8'),
  :watchos => Version.new('2.0'),
  :tvos => Version.new('9.0'),
}
SOURCE_FILE_EXTENSIONS =
Sandbox::FileAccessor::SOURCE_FILE_EXTENSIONS

Instance Method Summary collapse

Constructor Details

#initialize(argv) ⇒ GeneratorProj



46
47
48
49
50
51
52
# File 'lib/cocoapods-generator-minlison/command/generator.rb', line 46

def initialize(argv)
  @spec_name = argv.shift_argument
  @lint_project = argv.shift_argument
  @current_path = Dir.pwd
  @spec_path = @current_path + '/' + @spec_name if @current_path && @spec_name
  super
end

Instance Method Details

#add_file_accessors_paths_to_group(file_accessor_key, group_key = nil) ⇒ Object



187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/cocoapods-generator-minlison/command/generator.rb', line 187

def add_file_accessors_paths_to_group(file_accessor_key, group_key = nil)
  @file_accessors.each do |file_accessor|
    pod_name = file_accessor.spec.name
    paths = file_accessor.send(file_accessor_key)
    paths = allowable_project_paths(paths)
    paths.each do |path|
      if !@app_project.reference_for_path(path)
        relative_pathname = path.relative_path_from(Pathname.new(@current_path))
        relative_dir = relative_pathname.dirname
        lproj_regex = /\.lproj/i
        group = group_for_spec(file_accessor.spec.name, group_key)
        relative_dir.each_filename do|name|
          break if name.to_s =~ lproj_regex
          next if name == '.'
          group = group[name] || group.new_group(name)
        end

        file_path_name = path.is_a?(Pathname) ? path : Pathname.new(path)
        ref = group.new_file(file_path_name.realpath)
      end
    end
  end
end

#add_files_to_build_phasesObject



262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
# File 'lib/cocoapods-generator-minlison/command/generator.rb', line 262

def add_files_to_build_phases
  @file_accessors.each do |file_accessor|
    consumer = file_accessor.spec_consumer

    headers = file_accessor.headers
    public_headers = file_accessor.public_headers
    private_headers = file_accessor.private_headers
    other_source_files = file_accessor.source_files.reject { |sf| SOURCE_FILE_EXTENSIONS.include?(sf.extname) }

    {
      true => file_accessor.arc_source_files,
      false => file_accessor.non_arc_source_files,
    }.each do |arc, files|
      files = files - headers - other_source_files
      flags = compiler_flags_for_consumer(consumer, arc)
      regular_file_refs = files.map { |sf| @app_project.reference_for_path(sf) }
      @framework_target.add_file_references(regular_file_refs, flags)
    end

    header_file_refs = headers.map { |sf| @app_project.reference_for_path(sf) }
    @framework_target.add_file_references(header_file_refs) do |build_file|
      add_header(build_file, public_headers, private_headers)
    end

    other_file_refs = other_source_files.map { |sf| @app_project.reference_for_path(sf) }
    @framework_target.add_file_references(other_file_refs, nil)

    resource_refs = file_accessor.resources.flatten.map do |res|
      @app_project.reference_for_path(res)
    end

    # Some nested files are not directly present in the Xcode project, such as the contents
    # of an .xcdatamodeld directory. These files will return nil file references.
    resource_refs.compact!

    @framework_target.add_resources(resource_refs)
  end
end

#add_framework_target_to_XcodeprojectObject



126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/cocoapods-generator-minlison/command/generator.rb', line 126

def add_framework_target_to_Xcodeproject
  project_path = xcodeproj_path
  podspec_consumer = consumer
  platform_name = consumer.platform_name
  deployment_target = podspec_consumer.spec.deployment platform_name
  target_name = @spec_name
  app_project = xcodeproj::Project.open project_path
  app_project.new_target('static_framework', target_name, platform_name, deployment_target)
  app_project.save
  app_project.recreate_user_schemes
  Xcodeproj::XCScheme.share_scheme(app_project.path, target_name)
end

#add_frameworks_bundlesObject



174
175
176
# File 'lib/cocoapods-generator-minlison/command/generator.rb', line 174

def add_frameworks_bundles
  add_file_accessors_paths_to_group(:vendored_frameworks, :frameworks)
end

#add_header(build_file, public_headers, private_headers) ⇒ Object



322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
# File 'lib/cocoapods-generator-minlison/command/generator.rb', line 322

def add_header(build_file, public_headers, private_headers)
  file_ref = build_file.file_ref
  acl = if public_headers.include?(file_ref.real_path)
          'Public'
        elsif private_headers.include?(file_ref.real_path)
          'Private'
        else
          'Project'
        end

  if header_mappings_dir && acl != 'Project'
    relative_path = file_ref.real_path.relative_path_from(header_mappings_dir)
    sub_dir = relative_path.dirname
    copy_phase_name = "Copy #{sub_dir} #{acl} Headers"
    copy_phase = native_target.copy_files_build_phases.find { |bp| bp.name == copy_phase_name } ||
      native_target.new_copy_files_build_phase(copy_phase_name)
    copy_phase.symbol_dst_subfolder_spec = :products_directory
    copy_phase.dst_path = "$(#{acl.upcase}_HEADERS_FOLDER_PATH)/#{sub_dir}"
    copy_phase.add_file_reference(file_ref, true)
  else
    build_file.settings ||= {}
    build_file.settings['ATTRIBUTES'] = [acl]
  end
end

#add_libraries_to_build_phasesObject



301
302
303
304
305
306
307
308
# File 'lib/cocoapods-generator-minlison/command/generator.rb', line 301

def add_libraries_to_build_phases
  file_accessor = @file_accessors.first
  @framework_target.add_system_framework(file_accessor.spec_consumer.frameworks)
  @framework_target.add_system_library(file_accessor.spec_consumer.libraries)

  add_vendored_library_to_build_phases(:vendored_frameworks)
  add_vendored_library_to_build_phases(:vendored_libraries)
end

#add_resourcesObject



182
183
184
185
# File 'lib/cocoapods-generator-minlison/command/generator.rb', line 182

def add_resources
  add_file_accessors_paths_to_group(:resources, :resources)
  add_file_accessors_paths_to_group(:resource_bundle_files, :resources)
end

#add_source_files_referencesObject



170
171
172
# File 'lib/cocoapods-generator-minlison/command/generator.rb', line 170

def add_source_files_references
  add_file_accessors_paths_to_group(:source_files)
end

#add_vendored_librariesObject



178
179
180
# File 'lib/cocoapods-generator-minlison/command/generator.rb', line 178

def add_vendored_libraries
  add_file_accessors_paths_to_group(:vendored_libraries, :frameworks)
end

#add_vendored_library_to_build_phases(sourcekey) ⇒ Object



310
311
312
313
314
315
316
317
318
319
320
# File 'lib/cocoapods-generator-minlison/command/generator.rb', line 310

def add_vendored_library_to_build_phases(sourcekey)
  file_accessor = @file_accessors.first
  file_accessor.send(sourcekey).each do |path|
    ref = @app_project.reference_for_path(path)
    if ref
      @framework_target.frameworks_build_phase.add_file_reference(ref)
    else
      help! "#{path.basename} no added to project!!"
    end
  end
end

#allowable_project_paths(paths) ⇒ Object



221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
# File 'lib/cocoapods-generator-minlison/command/generator.rb', line 221

def allowable_project_paths(paths)
  lproj_paths = Set.new
  lproj_paths_with_files = Set.new
  allowable_paths = paths.select do |path|
    path_str = path.to_s

    # We add the directory for a Core Data model, but not the items in it.
    next if path_str =~ /.*\.xcdatamodeld\/.+/i

    # We add the directory for a Core Data migration mapping, but not the items in it.
    next if path_str =~ /.*\.xcmappingmodel\/.+/i

    # We add the directory for an asset catalog, but not the items in it.
    next if path_str =~ /.*\.xcassets\/.+/i

    if path_str =~ /\.lproj(\/|$)/i
      # If the element is an .lproj directory then save it and potentially
      # add it later if we don't find any contained items.
      if path_str =~ /\.lproj$/i && path.directory?
        lproj_paths << path
        next
      end

      # Collect the paths for the .lproj directories that contain files.
      lproj_path = /(^.*\.lproj)\/.*/i.match(path_str)[1]
      lproj_paths_with_files << Pathname(lproj_path)

      # Directories nested within an .lproj directory are added as file
      # system references so their contained items are not added directly.
      next if path.dirname.dirname == lproj_path
    end

    true
  end

  # Only add the path for the .lproj directories that do not have anything
  # within them added as well. This generally happens if the glob within the
  # resources directory was not a recursive glob.
  allowable_paths + lproj_paths.subtract(lproj_paths_with_files).to_a
end

#compiler_flags_for_consumer(consumer, arc) ⇒ Object



347
348
349
350
351
352
353
354
355
356
357
358
359
360
# File 'lib/cocoapods-generator-minlison/command/generator.rb', line 347

def compiler_flags_for_consumer(consumer, arc)
  flags = consumer.compiler_flags.dup
  if !arc
    flags << '-fno-objc-arc'
  else
    platform_name = consumer.platform_name
    spec_deployment_target = consumer.spec.deployment_target(platform_name)
    if spec_deployment_target.nil? || Version.new(spec_deployment_target) < ENABLE_OBJECT_USE_OBJC_FROM[platform_name]
      flags << '-DOS_OBJECT_USE_OBJC=0'
    end
  end

  flags * ' '
end

#create_file_accessorsObject



156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/cocoapods-generator-minlison/command/generator.rb', line 156

def create_file_accessors
  [@framework_target].each do |target|

    path_list = Sandbox::PathList.new(Pathname.new(Dir.new(@current_path)))
    specs = [@spec_content]
    specs.concat @spec_content.subspecs
    platform = Platform.new(target.platform_name, target.deployment_target)
    @file_accessors = specs.map do |spec|
      file_accessor = Sandbox::FileAccessor.new(path_list, spec.consumer(platform))
      file_accessor
    end
  end
end

#create_spec_contentObject



113
114
115
# File 'lib/cocoapods-generator-minlison/command/generator.rb', line 113

def create_spec_content
  @spec_content = Specification::from_file @spec_path
end

#group_for_spec(spec_name, subgroup_key = nil) ⇒ Object



211
212
213
214
215
216
217
218
219
# File 'lib/cocoapods-generator-minlison/command/generator.rb', line 211

def group_for_spec(spec_name, subgroup_key = nil)
if subgroup_key
  group_name = SPEC_SUBGROUPS[subgroup_key]
else
  group_name = spec_name
end

@app_project[group_name] || @app_project.new_group(group_name)
end

#header_mappings_dirObject



362
363
364
365
366
367
# File 'lib/cocoapods-generator-minlison/command/generator.rb', line 362

def header_mappings_dir
  file_accessor = @file_accessors.first
  header_mappings_dir = if dir = file_accessor.spec_consumer.header_mappings_dir
                           file_accessor.path_list.root + dir
                         end
end

#installObject



139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/cocoapods-generator-minlison/command/generator.rb', line 139

def install
  project_path = xcodeproj_path
  @app_project = Xcodeproj::Project.open(project_path)
  @framework_target = @app_project.targets.find { |target| target.name == @spec_content.name }

  create_file_accessors
  add_source_files_references
  add_frameworks_bundles
  add_vendored_libraries
  add_resources

  add_files_to_build_phases
  add_libraries_to_build_phases

  @app_project.save
end

#results_message(results) ⇒ Object



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/cocoapods-generator-minlison/command/generator.rb', line 80

def results_message(results)
  message = ''
  results.each do |result|
    if result.platforms == [:ios]
      platform_message = '[iOS] '
    elsif result.platforms == [:osx]
      platform_message = '[OSX] '
    elsif result.platforms == [:watchos]
      platform_message = '[watchOS] '
    elsif result.platforms == [:tvos]
      platform_message = '[tvOS] '
    end

    subspecs_message = ''
    if result.is_a?(Result)
      subspecs = result.subspecs.uniq
      if subspecs.count > 2
        subspecs_message = '[' + subspecs[0..2].join(', ') + ', and more...] '
      elsif subspecs.count > 0
        subspecs_message = '[' + subspecs.join(',') + '] '
      end
    end

    case result.type
    when :error   then type = 'ERROR'
    when :warning then type = 'WARN'
    when :note    then type = 'NOTE'
    else raise "#{result.type}" end
    message << "    - #{type.ljust(5)} | #{platform_message}#{subspecs_message}#{result.attribute_name}: #{result.message}\n"
  end
  message << "\n"
end

#runObject



63
64
65
66
67
# File 'lib/cocoapods-generator-minlison/command/generator.rb', line 63

def run
  create_spec_content
  validatePodspec
  install
end

#validate!Object



54
55
56
57
58
59
60
61
# File 'lib/cocoapods-generator-minlison/command/generator.rb', line 54

def validate!
  super

  if @spec_name.nil? || File.extname(@spec_name) != ".podspec"
    help! 'A *.podspec file is required.'
    Process.exit! false
  end
end

#validatePodspecObject



69
70
71
72
73
74
75
76
77
78
# File 'lib/cocoapods-generator-minlison/command/generator.rb', line 69

def validatePodspec
  if @lint_project.nil? || @lint_project == 'no'
    return
  end
  linter = Specification::Linter.new(@spec_path)
  linter.lint
  results = []
  results.concat(linter.results.to_a)
  puts results_message results
end

#xcodeproj_pathObject



117
118
119
120
121
122
123
124
# File 'lib/cocoapods-generator-minlison/command/generator.rb', line 117

def xcodeproj_path
  project_path = File.expand_path File.basename(@spec_name, ".podspec") + '.xcodeproj', @current_path
  if !File.exists? project_path
    help! "Please make sure has #{File.basename project_path} in current directory."
    Process.exit! false
  end
  project_path
end