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