xcmv 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/xcmv +61 -0
- data/lib/xcmv/file.rb +98 -0
- data/lib/xcmv/group_membership.rb +21 -0
- data/lib/xcmv/header_visibility.rb +55 -0
- data/lib/xcmv/project_cache.rb +11 -0
- data/lib/xcmv/version.rb +4 -0
- data/lib/xcmv.rb +41 -0
- metadata +65 -0
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
|
data/lib/xcmv/version.rb
ADDED
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: []
|