xcmv 0.1.2

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: 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: []