xcmv 0.1.2

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: cf21bac8b1f63f254055f144dbd1e810a777b5cf3b6a1ef7d6ab9d620124a3a8
4
+ data.tar.gz: b8d489130a14b4ca7d126a08358bd44d85fec2be0f0b1ee0379bc5c779ea4d27
5
+ SHA512:
6
+ metadata.gz: 786b74b64ee0d374769e79fd74e2b175e41ca0235da552f4143faa7d188f2db27941941d802fd715ca993a660f0404d168fcca2a2480746608788f028d60ff8d
7
+ data.tar.gz: 60be668e3d0f4405132aafc8bbf9285c5ccac9f08860faac5d900a46925fc2255d92075e10131e9ba41a7d371e8b7169b7a9f77972a592010d2cec43a33a263c
data/bin/xcmv ADDED
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env ruby
2
+ # vim:ft=ruby
3
+
4
+ require 'optparse'
5
+ require 'xcmv'
6
+
7
+ options = {}
8
+ parser = OptionParser.new do |opts|
9
+ opts.banner = "Usage: xcmv src_file [...] dst_file"
10
+
11
+ opts.on("--git=[true|false]", TrueClass, 'Use `git mv` (default: true if in a git repo)') do |git|
12
+ options[:git] = git
13
+ end
14
+
15
+ opts.on("-t[TARGETS]", "--targets=[TARGETS]", String,
16
+ "Comma-separated list of targets to add moved files to (default: guess)") do |targets|
17
+ options[:targets] = targets.split(",")
18
+ end
19
+
20
+ opts.on("-h[HEADERS]", "--headers=[HEADERS]", [:public, :project, :private],
21
+ "Visibility level of moved header files (default: `public` for frameworks, `project` otherwise)") do |visibility|
22
+ map = {:public => XcodeMove::HeaderVisibility::PUBLIC,
23
+ :project => XcodeMove::HeaderVisibility::PROJECT,
24
+ :private => XcodeMove::HeaderVisibility::PRIVATE}
25
+ options[:headers] = map[visibility]
26
+ end
27
+
28
+ opts.on("--help", "This help message")
29
+
30
+ opts.on("-v", "--version") do
31
+ puts XcodeMove::VERSION
32
+ exit
33
+ end
34
+ end
35
+
36
+ begin
37
+ parser.parse!
38
+ rescue OptionParser::MissingArgument, OptionParser::InvalidArgument
39
+ abort parser.help
40
+ end
41
+
42
+ if ARGV.count < 2
43
+ abort parser.help
44
+ end
45
+
46
+ ARGV.map!{ |a| Pathname.new(a) }
47
+
48
+ dst = ARGV.pop
49
+ if dst.directory?
50
+ ARGV.each do |src|
51
+ dst_file = dst + src.basename
52
+ XcodeMove.mv(XcodeMove::File.new(src), XcodeMove::File.new(dst_file), options)
53
+ end
54
+ else
55
+ if ARGV.count > 2
56
+ abort "Error: moving more than one file to #{dst}\n" + parser.help
57
+ end
58
+ src = ARGV[0]
59
+ XcodeMove.mv(XcodeMove::File.new(src), XcodeMove::File.new(dst), options)
60
+ end
61
+
data/lib/xcmv/file.rb ADDED
@@ -0,0 +1,98 @@
1
+
2
+ module XcodeMove
3
+ class File
4
+ attr_reader :path
5
+
6
+ def initialize(path)
7
+ path = Pathname.new path
8
+ @path = path.realdirpath
9
+ end
10
+
11
+ def project
12
+ @project ||= project_load
13
+ end
14
+
15
+ def pbx_file
16
+ @pbx_file ||= pbx_load
17
+ end
18
+
19
+ def header?
20
+ path.extname == '.h'
21
+ end
22
+
23
+
24
+ # Traverses up from the `path` to enumerate over xcodeproj directories
25
+ def reachable_projects
26
+ path.ascend.find_all{ |p| p.directory? }.flat_map do |dir|
27
+ dir.children.select{ |p| p.extname == '.xcodeproj' }
28
+ end
29
+ end
30
+
31
+ def remove_from_project
32
+ project.targets.select{ |t| t.respond_to?(:build_phases) }.each do |native_target|
33
+ native_target.build_phases.each{ |p| p.remove_file_reference(pbx_file) }
34
+ end
35
+ pbx_file.remove_from_project
36
+ @pbx_file = nil
37
+ end
38
+
39
+ # Uses the `path` to create a file reference in `project`, setting
40
+ # `pbx_file` along the way.
41
+ def create_file_reference
42
+ relative_path = path.relative_path_from(project.path.dirname)
43
+ group = project.main_group
44
+ relative_path.descend do |subpath|
45
+ if subpath == relative_path
46
+ @pbx_file = insert_at_group(group)
47
+ else
48
+ group = find_or_create_relative_group(group, subpath.basename)
49
+ end
50
+ end
51
+ end
52
+
53
+ def add_to_targets(target_names, header_visibility)
54
+ unless target_names
55
+ group = GroupMembership.new(pbx_file.parent)
56
+ targets = group.sibling_targets
57
+ else
58
+ name_set = target_names.to_set
59
+ targets = project.targets.select{ |t| name_set.include?(t.name) }
60
+ abort "No targets found in #{target_names}" if targets.empty?
61
+ end
62
+
63
+ targets.each do |target|
64
+ build_file = target.add_file_references([@pbx_file])
65
+ if header?
66
+ visibility = header_visibility || HeaderVisibility.default_for_target(target)
67
+ build_file.each{ |b| b.settings = visibility.file_settings }
68
+ end
69
+ end
70
+ end
71
+
72
+ def save_and_close
73
+ project.save
74
+ @project = nil
75
+ end
76
+
77
+ private
78
+
79
+ def find_or_create_relative_group(parent_group, group_name)
80
+ parent_group.children.find { |g| g.path == group_name.to_s } ||
81
+ parent_group.new_group(group_name.to_s, group_name)
82
+ end
83
+
84
+ def insert_at_group(group)
85
+ group.new_file(path)
86
+ end
87
+
88
+ # Finds a reachable project that contains this file, and sets `project` and `pbx_file`.
89
+ def project_load
90
+ project_dir = reachable_projects.first || abort("Could not find a project file containing #{path}")
91
+ @project = ProjectCache.open(project_dir)
92
+ end
93
+
94
+ def pbx_load
95
+ @pbx_file = project.files.find{ |f| f.real_path == path }
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,21 @@
1
+
2
+ module XcodeMove
3
+ class GroupMembership
4
+ def initialize(group)
5
+ @group = group
6
+ @project = group.project
7
+ @siblings = @group.children.to_set
8
+ end
9
+
10
+ # Returns an array of targets that have build files in `group`.
11
+ def sibling_targets
12
+ compiled_targets = @project.targets.select{ |t| t.respond_to?(:source_build_phase) }
13
+ compiled_targets.select{ |t| t.source_build_phase.files_references.any?{ |f| @siblings.include?(f) } }
14
+ end
15
+
16
+ def max_header_visibility(target)
17
+ sibling_headers = target.headers_build_phase.files.filter{ |f| @siblings.include?(file_ref) }
18
+ sibling_headers.map{ |f| HeaderVisibility.from_file_settings(f.settings) }.max
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,55 @@
1
+
2
+ module XcodeMove
3
+ class HeaderVisibility
4
+ include Comparable
5
+
6
+ private
7
+ def initialize(value)
8
+ @value = value
9
+ end
10
+
11
+ public
12
+
13
+ PRIVATE = HeaderVisibility.new(1)
14
+ PROJECT = HeaderVisibility.new(2)
15
+ PUBLIC = HeaderVisibility.new(3)
16
+
17
+ attr_reader :value
18
+
19
+ def self.default_for_target(native_target)
20
+ case native_target.product_type
21
+ when "com.apple.product-type.framework"
22
+ PUBLIC
23
+ else
24
+ PROJECT
25
+ end
26
+ end
27
+
28
+ def self.from_file_settings(settings)
29
+ case settings["ATTRIBUTES"]
30
+ when "Public"
31
+ PUBLIC
32
+ when "Private"
33
+ PRIVATE
34
+ when nil
35
+ PROJECT
36
+ end
37
+ end
38
+
39
+ def file_settings
40
+ case self
41
+ when PUBLIC
42
+ visibility = "Public"
43
+ when PRIVATE
44
+ visibility = "Private"
45
+ when PROJECT
46
+ visibility = nil
47
+ end
48
+ { "ATTRIBUTES": [visibility] }
49
+ end
50
+
51
+ def <=>(other)
52
+ value <=> other.value
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,11 @@
1
+
2
+ module XcodeMove
3
+ class ProjectCache
4
+ @@cache = Hash.new
5
+
6
+ def self.open(path)
7
+ path = Pathname.new(path).realpath
8
+ @@cache[path] ||= Xcodeproj::Project.open(path)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,4 @@
1
+
2
+ module XcodeMove
3
+ VERSION = '0.1.2'
4
+ end
data/lib/xcmv.rb ADDED
@@ -0,0 +1,41 @@
1
+ require 'xcodeproj'
2
+ require_relative 'xcmv/file'
3
+ require_relative 'xcmv/header_visibility'
4
+ require_relative 'xcmv/group_membership'
5
+ require_relative 'xcmv/project_cache'
6
+ require_relative 'xcmv/version'
7
+
8
+ module XcodeMove
9
+
10
+ # Moves from one `XcodeMove::File` to another
11
+ def self.mv(src, dst, options)
12
+ puts("#{src.path} => #{dst.path}")
13
+
14
+ # Remove files from xcodeproj (including dst if the file is being overwritten)
15
+ if src.pbx_file
16
+ src.remove_from_project
17
+ else
18
+ warn("#{src.path.basename} not found in #{src.project.path.basename}")
19
+ end
20
+ if dst.pbx_file
21
+ dst.remove_from_project
22
+ end
23
+
24
+ # Add to the new xcodeproj
25
+ dst.create_file_reference
26
+ dst.add_to_targets(options[:targets], options[:headers])
27
+
28
+ # Move the actual file
29
+ if options[:git] || system("git rev-parse")
30
+ mover = "git mv"
31
+ else
32
+ mover = "mv"
33
+ end
34
+ command = "#{mover} '#{src.path}' '#{dst.path}'"
35
+ system(command) || abort
36
+
37
+ # Save
38
+ src.save_and_close
39
+ dst.save_and_close
40
+ end
41
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: xcmv
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.2
5
+ platform: ruby
6
+ authors:
7
+ - Elliott Williams
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-08-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: xcodeproj
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ description: mv-like command that moves files between xcode projects
28
+ email: emw@yelp.com
29
+ executables:
30
+ - xcmv
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - bin/xcmv
35
+ - lib/xcmv.rb
36
+ - lib/xcmv/file.rb
37
+ - lib/xcmv/group_membership.rb
38
+ - lib/xcmv/header_visibility.rb
39
+ - lib/xcmv/project_cache.rb
40
+ - lib/xcmv/version.rb
41
+ homepage: https://github.com/elliottwilliams/XcodeMove
42
+ licenses:
43
+ - MIT
44
+ metadata: {}
45
+ post_install_message:
46
+ rdoc_options: []
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ requirements: []
60
+ rubyforge_project:
61
+ rubygems_version: 2.7.6
62
+ signing_key:
63
+ specification_version: 4
64
+ summary: Xcode Move
65
+ test_files: []