vendor 0.0.4 → 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +22 -0
- data/Gemfile.lock +9 -1
- data/Guardfile +12 -0
- data/LICENSE +2 -0
- data/Readme.markdown +39 -23
- data/TODO.md +26 -0
- data/VERSION +1 -0
- data/lib/vendor.rb +6 -0
- data/lib/vendor/api.rb +61 -7
- data/lib/vendor/cli/app.rb +4 -4
- data/lib/vendor/cli/console.rb +7 -0
- data/lib/vendor/spec.rb +98 -0
- data/lib/vendor/templates/Vendorfile +3 -1
- data/lib/vendor/templates/vendorspec +15 -10
- data/lib/vendor/vendor_file.rb +5 -4
- data/lib/vendor/vendor_file/dependency_graph.rb +135 -0
- data/lib/vendor/vendor_file/dsl.rb +2 -0
- data/lib/vendor/vendor_file/library/base.rb +178 -29
- data/lib/vendor/vendor_file/library/git.rb +5 -1
- data/lib/vendor/vendor_file/library/local.rb +11 -1
- data/lib/vendor/vendor_file/library/remote.rb +134 -2
- data/lib/vendor/vendor_file/loader.rb +13 -11
- data/lib/vendor/vendor_spec/builder.rb +4 -7
- data/lib/vendor/version.rb +172 -1
- data/lib/vendor/xcode/project.rb +213 -4
- data/lib/vendor/xcode/proxy.rb +1 -0
- data/lib/vendor/xcode/proxy/pbx_frameworks_build_phase.rb +6 -0
- data/lib/vendor/xcode/proxy/pbx_reference_proxy.rb +7 -0
- data/lib/vendor/xcode/proxy/pbx_resources_build_phase.rb +8 -0
- data/lib/vendor/xcode/proxy/pbx_shell_script_build_phase.rb +8 -0
- data/lib/vendor/xcode/proxy/pbx_sources_build_phase.rb +6 -0
- data/spec/lib/vendor/api_spec.rb +54 -0
- data/spec/lib/vendor/spec_spec.rb +121 -0
- data/spec/lib/vendor/vendor_file/dependency_graph_spec.rb +129 -0
- data/spec/lib/vendor/vendor_file/library/base_spec.rb +174 -14
- data/spec/lib/vendor/vendor_file/library/remote_spec.rb +154 -4
- data/spec/lib/vendor/vendor_file/loader_spec.rb +4 -2
- data/spec/lib/vendor/vendor_spec/builder_spec.rb +2 -2
- data/spec/lib/vendor/version_spec.rb +168 -0
- data/spec/lib/vendor/xcode/project_spec.rb +175 -4
- data/spec/lib/vendor_spec.rb +15 -0
- data/spec/spec_helper.rb +3 -2
- data/spec/support/api_stubs.rb +57 -0
- data/spec/support/resources/cache/base/{DKBenchmark-Manifest → DKBenchmark-0.1-Manifest}/data/DKBenchmark.h +0 -0
- data/spec/support/resources/cache/base/{DKBenchmark-Manifest → DKBenchmark-0.1-Manifest}/data/DKBenchmark.m +0 -0
- data/spec/support/resources/cache/base/DKBenchmark-0.1-Manifest/vendor.json +1 -0
- data/spec/support/resources/cache/base/{DKBenchmark-Vendorspec → DKBenchmark-0.1-Nothing}/DKBenchmark.h +0 -0
- data/spec/support/resources/cache/base/{DKBenchmark-Vendorspec → DKBenchmark-0.1-Nothing}/DKBenchmark.m +0 -0
- data/spec/support/resources/cache/base/DKBenchmark-0.1-Nothing/DKBenchmark.vendorspec +16 -0
- data/spec/support/resources/cache/base/DKBenchmark-0.1-Vendorspec/DKBenchmark.h +18 -0
- data/spec/support/resources/cache/base/DKBenchmark-0.1-Vendorspec/DKBenchmark.m +73 -0
- data/spec/support/resources/cache/base/DKBenchmark-0.1-Vendorspec/DKBenchmark.vendorspec +24 -0
- data/spec/support/resources/projects/MultipleTargets/MultipleTargets.xcodeproj/project.pbxproj +624 -0
- data/spec/support/resources/projects/RestKitProject/RestKitProject.xcodeproj/project.pbxproj +479 -0
- data/spec/support/resources/projects/UtilityApplication/UtilityApplication.xcodeproj/project.pbxproj +16 -7
- data/spec/support/resources/vendors/DKBenchmark/DKBenchmark.vendorspec +24 -8
- data/spec/support/resources/vendors/DKBenchmarkUnsafe/DKBenchmark.vendorspec +17 -8
- data/vendor.gemspec +4 -2
- metadata +93 -39
- data/lib/vendor/vendor_spec/dsl.rb +0 -39
- data/lib/vendor/vendor_spec/loader.rb +0 -23
- data/spec/lib/vendor/vendor_spec/dsl_spec.rb +0 -67
- data/spec/lib/vendor/vendor_spec/loader_spec.rb +0 -41
- data/spec/support/resources/cache/base/DKBenchmark-Manifest/vendor.json +0 -1
- data/spec/support/resources/cache/base/DKBenchmark-Vendorspec/DKBenchmark.vendorspec +0 -11
@@ -44,7 +44,11 @@ module Vendor
|
|
44
44
|
# the sources correctly. So instead of using the name, use a hash of
|
45
45
|
# the URI
|
46
46
|
def cache_path
|
47
|
-
|
47
|
+
File.join(Vendor.library_path, "git", Digest::MD5.hexdigest(uri)) if uri
|
48
|
+
end
|
49
|
+
|
50
|
+
def display_name
|
51
|
+
[ name, "(#{uri}##{tag})" ].join(' ')
|
48
52
|
end
|
49
53
|
|
50
54
|
private
|
@@ -12,9 +12,19 @@ module Vendor
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def cache_path
|
15
|
-
|
15
|
+
expanded_path if path
|
16
16
|
end
|
17
17
|
|
18
|
+
def display_name
|
19
|
+
[ name, "(#{expanded_path})" ].join(' ')
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def expanded_path
|
25
|
+
File.expand_path(path)
|
26
|
+
end
|
27
|
+
|
18
28
|
end
|
19
29
|
|
20
30
|
end
|
@@ -4,13 +4,145 @@ module Vendor
|
|
4
4
|
|
5
5
|
class Remote < Base
|
6
6
|
|
7
|
-
|
7
|
+
require 'zip/zip'
|
8
|
+
|
9
|
+
attr_accessor :equality
|
8
10
|
attr_accessor :sources
|
9
11
|
|
12
|
+
def version
|
13
|
+
@version
|
14
|
+
end
|
15
|
+
|
16
|
+
def version=(value)
|
17
|
+
# Matches (some equality matcher, followed by some spaces, then a version)
|
18
|
+
if value
|
19
|
+
if value.match(/^(\<\=|\>\=|\~\>|\>|\<)?\s*([a-zA-Z0-9\.]+)$/)
|
20
|
+
@equality = $1
|
21
|
+
@version = $2
|
22
|
+
else
|
23
|
+
Vendor.ui.error "Invalid version format '#{value}' for '#{name}'"
|
24
|
+
exit 1
|
25
|
+
end
|
26
|
+
else
|
27
|
+
@equality = nil
|
28
|
+
@version = nil
|
29
|
+
end
|
30
|
+
value
|
31
|
+
end
|
32
|
+
|
33
|
+
def matched_version
|
34
|
+
# If we just have a version, and no equality matcher
|
35
|
+
if version && !equality
|
36
|
+
return version
|
37
|
+
end
|
38
|
+
|
39
|
+
# If we don't have a version, get the latest version
|
40
|
+
# from the remote sources
|
41
|
+
unless version
|
42
|
+
return meta['release']
|
43
|
+
end
|
44
|
+
|
45
|
+
# Try and find a version that matches the lib
|
46
|
+
other_versions = meta['versions'].map { |v| v[0] }
|
47
|
+
found = version_matches_any?(other_versions)
|
48
|
+
|
49
|
+
# Did we actually find something?
|
50
|
+
unless found
|
51
|
+
Vendor.ui.error "Could not find a valid version '#{equality} #{version}' in '#{other_versions}'"
|
52
|
+
exit 1
|
53
|
+
else
|
54
|
+
found
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def version_matches_any?(other_versions)
|
59
|
+
# If we have an equality matcher, we need sort through
|
60
|
+
# the versions, and try and find the best match
|
61
|
+
wants = Vendor::Version.new(version)
|
62
|
+
|
63
|
+
# Sort them from the latest versions first
|
64
|
+
versions = other_versions.map{|v| Vendor::Version.create(v) }.sort.reverse
|
65
|
+
|
66
|
+
# We don't want to include pre-releases if the wants version
|
67
|
+
# isn't a pre-release itself. If we're after "2.5.alpha", then
|
68
|
+
# we should be able to include that, however if we're asking for
|
69
|
+
# "2.5", then pre-releases shouldn't be included.
|
70
|
+
unless wants.prerelease?
|
71
|
+
versions = versions.reject { |v| v.prerelease? }
|
72
|
+
end
|
73
|
+
|
74
|
+
# If this a spermy search, we have a slightly different search mechanism. We use the
|
75
|
+
# version segemets to determine if the version is valid. For example, lets say we're
|
76
|
+
# testing to see if "0.1" is a spermy match to "0.1.5.2", we start by getting the
|
77
|
+
# two segments for each:
|
78
|
+
#
|
79
|
+
# [ 0, 1 ]
|
80
|
+
# [ 0, 1, 5, 2 ]
|
81
|
+
#
|
82
|
+
# We chop the second set of segments to be the same lenth as the first one:
|
83
|
+
#
|
84
|
+
# [ 0, 1, 5, 2 ].slice(0, 2) #=. [ 0, 1 ]
|
85
|
+
#
|
86
|
+
# No we just test to see if they are the same! I think this works...
|
87
|
+
if equality == "~>"
|
88
|
+
segments = wants.segments
|
89
|
+
versions.find do |has|
|
90
|
+
segments == has.segments.slice(0, segments.length)
|
91
|
+
end
|
92
|
+
elsif equality
|
93
|
+
versions.find do |has|
|
94
|
+
wants.send(:"#{equality}", has)
|
95
|
+
end
|
96
|
+
else
|
97
|
+
versions.find do |has|
|
98
|
+
wants == has
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
10
103
|
def download
|
11
|
-
|
104
|
+
# If we haven't already downloaded the vendor
|
105
|
+
unless File.exist?(cache_path)
|
106
|
+
# Download it
|
107
|
+
file = Vendor::API.download(name, matched_version)
|
108
|
+
|
109
|
+
# Unzip the download
|
110
|
+
unzip_file file.path, cache_path
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def cache_path
|
115
|
+
File.join(Vendor.library_path, "remote", name, matched_version.to_s)
|
12
116
|
end
|
13
117
|
|
118
|
+
def ==(other)
|
119
|
+
other.name == @name && other.version == @version && other.equality == @equality
|
120
|
+
end
|
121
|
+
|
122
|
+
def display_name
|
123
|
+
[ name, matched_version ].compact.join(" ")
|
124
|
+
end
|
125
|
+
|
126
|
+
def description
|
127
|
+
[ @name, @equality, @version ].compact.join(" ")
|
128
|
+
end
|
129
|
+
|
130
|
+
private
|
131
|
+
|
132
|
+
def meta
|
133
|
+
@meta ||= Vendor::API.meta(name)
|
134
|
+
end
|
135
|
+
|
136
|
+
def unzip_file (file, destination)
|
137
|
+
Zip::ZipFile.open(file) { |zip_file|
|
138
|
+
zip_file.each { |f|
|
139
|
+
f_path = File.join(destination, f.name)
|
140
|
+
FileUtils.mkdir_p(File.dirname(f_path))
|
141
|
+
zip_file.extract(f, f_path) unless File.exist?(f_path)
|
142
|
+
}
|
143
|
+
}
|
144
|
+
end
|
145
|
+
|
14
146
|
end
|
15
147
|
|
16
148
|
end
|
@@ -6,27 +6,29 @@ module Vendor
|
|
6
6
|
require 'fileutils'
|
7
7
|
|
8
8
|
attr_reader :dsl
|
9
|
+
attr_reader :libraries
|
9
10
|
|
10
11
|
def initialize
|
11
12
|
@dsl = Vendor::VendorFile::DSL.new
|
13
|
+
@libraries = []
|
12
14
|
end
|
13
15
|
|
14
|
-
def
|
15
|
-
@
|
16
|
+
def libraries=(value)
|
17
|
+
@graph = Vendor::VendorFile::DependencyGraph.new(value)
|
18
|
+
@libraries = value
|
16
19
|
end
|
17
20
|
|
18
|
-
def
|
19
|
-
@dsl.
|
20
|
-
|
21
|
-
lib.sources = @dsl.sources
|
22
|
-
end
|
23
|
-
lib.download
|
24
|
-
end
|
21
|
+
def load(filename)
|
22
|
+
@dsl.instance_eval(File.read(filename), filename)
|
23
|
+
self.libraries = @dsl.libraries
|
25
24
|
end
|
26
25
|
|
27
26
|
def install(project)
|
28
|
-
@
|
29
|
-
|
27
|
+
unless @graph.version_conflicts?
|
28
|
+
@graph.libraries_to_install.each do |lib|
|
29
|
+
library, targets = lib
|
30
|
+
library.install project, :targets => targets
|
31
|
+
end
|
30
32
|
end
|
31
33
|
end
|
32
34
|
|
@@ -15,14 +15,11 @@ module Vendor
|
|
15
15
|
attr_reader :vendor_spec
|
16
16
|
|
17
17
|
def initialize(vendor_spec)
|
18
|
-
loader = Vendor::VendorSpec::Loader.new
|
19
|
-
loader.load vendor_spec
|
20
|
-
|
21
18
|
@folder = File.expand_path(File.join(vendor_spec, '..'))
|
22
|
-
@vendor_spec =
|
19
|
+
@vendor_spec = Vendor::Spec.load vendor_spec
|
23
20
|
|
24
|
-
@name = safe_filename(@vendor_spec
|
25
|
-
@version = safe_filename(@vendor_spec
|
21
|
+
@name = safe_filename(@vendor_spec.name)
|
22
|
+
@version = safe_filename(@vendor_spec.version)
|
26
23
|
@filename = "#{@name}-#{@version}.vendor"
|
27
24
|
end
|
28
25
|
|
@@ -49,7 +46,7 @@ module Vendor
|
|
49
46
|
|
50
47
|
# Remove files that are within folders with a ".", such as ".bundle"
|
51
48
|
# and ".frameworks"
|
52
|
-
copy_files = @vendor_spec
|
49
|
+
copy_files = @vendor_spec.files.reject { |file| file =~ /\/?[^\/]+\.[^\/]+\// }
|
53
50
|
|
54
51
|
copy_files.each do |file|
|
55
52
|
dir = File.dirname(file)
|
data/lib/vendor/version.rb
CHANGED
@@ -1,5 +1,176 @@
|
|
1
1
|
module Vendor
|
2
2
|
|
3
|
-
|
3
|
+
class Version
|
4
|
+
|
5
|
+
include Comparable
|
6
|
+
|
7
|
+
VERSION_PATTERN = '[0-9]+(\.[0-9a-zA-Z]+)*' # :nodoc:
|
8
|
+
ANCHORED_VERSION_PATTERN = /\A\s*(#{VERSION_PATTERN})*\s*\z/ # :nodoc:
|
9
|
+
|
10
|
+
##
|
11
|
+
# A string representation of this Version.
|
12
|
+
|
13
|
+
attr_reader :version
|
14
|
+
alias to_s version
|
15
|
+
|
16
|
+
##
|
17
|
+
# True if the +version+ string matches Vendor's requirements.
|
18
|
+
|
19
|
+
def self.correct? version
|
20
|
+
version.to_s =~ ANCHORED_VERSION_PATTERN
|
21
|
+
end
|
22
|
+
|
23
|
+
##
|
24
|
+
# Factory method to create a Version object. Input may be a Version
|
25
|
+
# or a String. Intended to simplify client code.
|
26
|
+
#
|
27
|
+
# ver1 = Version.create('1.3.17') # -> (Version object)
|
28
|
+
# ver2 = Version.create(ver1) # -> (ver1)
|
29
|
+
# ver3 = Version.create(nil) # -> nil
|
30
|
+
|
31
|
+
def self.create input
|
32
|
+
if input === Vendor::Version
|
33
|
+
input
|
34
|
+
elsif input.nil? then
|
35
|
+
nil
|
36
|
+
else
|
37
|
+
new input
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
##
|
42
|
+
# Constructs a Version from the +version+ string. A version string is a
|
43
|
+
# series of digits or ASCII letters separated by dots.
|
44
|
+
|
45
|
+
def initialize version
|
46
|
+
raise ArgumentError, "Malformed version number string #{version}" unless
|
47
|
+
self.class.correct?(version)
|
48
|
+
|
49
|
+
@version = version.to_s
|
50
|
+
@version.strip!
|
51
|
+
end
|
52
|
+
|
53
|
+
##
|
54
|
+
# Return a new version object where the next to the last revision
|
55
|
+
# number is one greater (e.g., 5.3.1 => 5.4).
|
56
|
+
#
|
57
|
+
# Pre-release (alpha) parts, e.g, 5.3.1.b.2 => 5.4, are ignored.
|
58
|
+
|
59
|
+
def bump
|
60
|
+
segments = self.segments.dup
|
61
|
+
segments.pop while segments.any? { |s| String === s }
|
62
|
+
segments.pop if segments.size > 1
|
63
|
+
|
64
|
+
segments[-1] = segments[-1].succ
|
65
|
+
self.class.new segments.join(".")
|
66
|
+
end
|
67
|
+
|
68
|
+
##
|
69
|
+
# A Version is only eql? to another version if it's specified to the
|
70
|
+
# same precision. Version "1.0" is not the same as version "1".
|
71
|
+
|
72
|
+
def eql? other
|
73
|
+
self.class === other and @version == other.version
|
74
|
+
end
|
75
|
+
|
76
|
+
def inspect # :nodoc:
|
77
|
+
"#<#{self.class} #{version.inspect}>"
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# Dump only the raw version string, not the complete object. It's a
|
82
|
+
# string for backwards (RubyGems 1.3.5 and earlier) compatibility.
|
83
|
+
|
84
|
+
def marshal_dump
|
85
|
+
[version]
|
86
|
+
end
|
87
|
+
|
88
|
+
##
|
89
|
+
# Load custom marshal format. It's a string for backwards (RubyGems
|
90
|
+
# 1.3.5 and earlier) compatibility.
|
91
|
+
|
92
|
+
def marshal_load array
|
93
|
+
initialize array[0]
|
94
|
+
end
|
95
|
+
|
96
|
+
##
|
97
|
+
# A version is considered a prerelease if it contains a letter.
|
98
|
+
|
99
|
+
def prerelease?
|
100
|
+
@prerelease ||= @version =~ /[a-zA-Z]/
|
101
|
+
end
|
102
|
+
|
103
|
+
def pretty_print q # :nodoc:
|
104
|
+
q.text "Vendor::Version.new(#{version.inspect})"
|
105
|
+
end
|
106
|
+
|
107
|
+
##
|
108
|
+
# The release for this version (e.g. 1.2.0.a -> 1.2.0).
|
109
|
+
# Non-prerelease versions return themselves.
|
110
|
+
|
111
|
+
def release
|
112
|
+
return self unless prerelease?
|
113
|
+
|
114
|
+
segments = self.segments.dup
|
115
|
+
segments.pop while segments.any? { |s| String === s }
|
116
|
+
self.class.new segments.join('.')
|
117
|
+
end
|
118
|
+
|
119
|
+
def segments # :nodoc:
|
120
|
+
|
121
|
+
# segments is lazy so it can pick up version values that come from
|
122
|
+
# old marshaled versions, which don't go through marshal_load.
|
123
|
+
|
124
|
+
@segments ||= @version.scan(/[0-9]+|[a-z]+/i).map do |s|
|
125
|
+
/^\d+$/ =~ s ? s.to_i : s
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
##
|
130
|
+
# A recommended version for use with a ~> Requirement.
|
131
|
+
|
132
|
+
def spermy_recommendation
|
133
|
+
segments = self.segments.dup
|
134
|
+
|
135
|
+
segments.pop while segments.any? { |s| String === s }
|
136
|
+
segments.pop while segments.size > 2
|
137
|
+
segments.push 0 while segments.size < 2
|
138
|
+
|
139
|
+
"~> #{segments.join(".")}"
|
140
|
+
end
|
141
|
+
|
142
|
+
##
|
143
|
+
# Compares this version with +other+ returning -1, 0, or 1 if the
|
144
|
+
# other version is larger, the same, or smaller than this
|
145
|
+
# one. Attempts to compare to something that's not a
|
146
|
+
# <tt>Vendor::Version</tt> return +nil+.
|
147
|
+
|
148
|
+
def <=> other
|
149
|
+
return unless Vendor::Version === other
|
150
|
+
return 0 if @version == other.version
|
151
|
+
|
152
|
+
lhsegments = segments
|
153
|
+
rhsegments = other.segments
|
154
|
+
|
155
|
+
lhsize = lhsegments.size
|
156
|
+
rhsize = rhsegments.size
|
157
|
+
limit = (lhsize > rhsize ? lhsize : rhsize) - 1
|
158
|
+
|
159
|
+
i = 0
|
160
|
+
|
161
|
+
while i <= limit
|
162
|
+
lhs, rhs = lhsegments[i] || 0, rhsegments[i] || 0
|
163
|
+
i += 1
|
164
|
+
|
165
|
+
next if lhs == rhs
|
166
|
+
return -1 if String === lhs && Numeric === rhs
|
167
|
+
return 1 if Numeric === lhs && String === rhs
|
168
|
+
|
169
|
+
return lhs <=> rhs
|
170
|
+
end
|
171
|
+
|
172
|
+
return 0
|
173
|
+
end
|
174
|
+
end
|
4
175
|
|
5
176
|
end
|
data/lib/vendor/xcode/project.rb
CHANGED
@@ -4,6 +4,11 @@ module Vendor::XCode
|
|
4
4
|
|
5
5
|
class Project
|
6
6
|
|
7
|
+
# Build settings and the format that they should be stored in
|
8
|
+
BUILD_SETTING_TYPES = {
|
9
|
+
"OTHER_LDFLAGS" => :array
|
10
|
+
}
|
11
|
+
|
7
12
|
require 'fileutils'
|
8
13
|
|
9
14
|
attr_reader :name
|
@@ -55,8 +60,13 @@ module Vendor::XCode
|
|
55
60
|
@objects_by_id[id]
|
56
61
|
end
|
57
62
|
|
58
|
-
def find_target(
|
59
|
-
|
63
|
+
def find_target(t)
|
64
|
+
# Are we already a target?
|
65
|
+
if t.kind_of?(Vendor::XCode::Proxy::Base)
|
66
|
+
t
|
67
|
+
else
|
68
|
+
root_object.targets.find { |x| x.name == t }
|
69
|
+
end
|
60
70
|
end
|
61
71
|
|
62
72
|
def find_group(path)
|
@@ -105,6 +115,8 @@ module Vendor::XCode
|
|
105
115
|
# If we have the group
|
106
116
|
if group
|
107
117
|
|
118
|
+
ids_to_remove = []
|
119
|
+
|
108
120
|
# Remove the children from the file system
|
109
121
|
group.children.each do |child|
|
110
122
|
if child.group? # Is it a group?
|
@@ -120,13 +132,27 @@ module Vendor::XCode
|
|
120
132
|
Vendor.ui.error "Couldn't remove object: #{child}"
|
121
133
|
end
|
122
134
|
|
135
|
+
# Remove from the targets (if we can)
|
136
|
+
root_object.targets.each { |t| remove_file_from_target(child, t) }
|
137
|
+
|
123
138
|
# Remove the file from the parent
|
124
139
|
child.parent.attributes['children'].delete child.id
|
140
|
+
|
141
|
+
# Add the id to the list of stuff to remove. If we do this
|
142
|
+
# during the loop, bad things happen - not sure why.
|
143
|
+
ids_to_remove << child.id
|
125
144
|
end
|
126
145
|
|
127
146
|
# Remove the group from the parent
|
128
147
|
group.parent.attributes['children'].delete group.id
|
129
148
|
|
149
|
+
# Add to the list of stuff to remove
|
150
|
+
ids_to_remove << group.id
|
151
|
+
|
152
|
+
ids_to_remove.each do |id|
|
153
|
+
@objects_by_id.delete id
|
154
|
+
end
|
155
|
+
|
130
156
|
# Mark as dirty
|
131
157
|
@dirty = true
|
132
158
|
|
@@ -135,11 +161,101 @@ module Vendor::XCode
|
|
135
161
|
end
|
136
162
|
end
|
137
163
|
|
164
|
+
def add_framework(framework, options = {})
|
165
|
+
# Find targets
|
166
|
+
targets = targets_from_options(options)
|
167
|
+
Vendor.ui.debug %{Adding #{framework} to targets "#{targets.map(&:name)}"}
|
168
|
+
|
169
|
+
targets.each do |t|
|
170
|
+
# Does the framework already exist?
|
171
|
+
build_phase = build_phase_for_file("wrapper.framework", t)
|
172
|
+
|
173
|
+
if build_phase
|
174
|
+
# Does a framework already exist?
|
175
|
+
existing_framework = build_phase.files.map(&:file_ref).find do |file|
|
176
|
+
# Some files have names, some done. Framework references
|
177
|
+
# have names...
|
178
|
+
if file.respond_to?(:name)
|
179
|
+
file.name == framework
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# If an existing framework was found, don't add it again
|
184
|
+
unless existing_framework
|
185
|
+
add_file :targets => t, :file => "System/Library/Frameworks/#{framework}", :path => "Frameworks", :source_tree => :sdkroot
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def add_build_setting(name, value, options = {})
|
192
|
+
|
193
|
+
targets_from_options(options).each do |target|
|
194
|
+
|
195
|
+
target.build_configuration_list.build_configurations.each do |config|
|
196
|
+
|
197
|
+
build_settings = config.build_settings
|
198
|
+
|
199
|
+
debug_key = "#{target.name}/#{config.name}".inspect
|
200
|
+
|
201
|
+
# If the build setting already has the key
|
202
|
+
if build_settings.has_key?(name)
|
203
|
+
|
204
|
+
# Is this setting known to have multiple values?
|
205
|
+
if (setting_type = BUILD_SETTING_TYPES[name])
|
206
|
+
|
207
|
+
# If its an array
|
208
|
+
if setting_type == :array
|
209
|
+
# Is it already an array, if so, check to see if
|
210
|
+
# it already exists, and if it doesn't add it.
|
211
|
+
if build_settings[name].kind_of?(Array)
|
212
|
+
unless build_settings[name].include?
|
213
|
+
Vendor.ui.debug(" Added build setting (#{name.inspect} = #{value.inspect}) to #{debug_key}")
|
214
|
+
|
215
|
+
build_settings[name] << value
|
216
|
+
end
|
217
|
+
else
|
218
|
+
Vendor.ui.debug(" Added build setting (#{name.inspect} = #{value.inspect}) to #{debug_key}")
|
219
|
+
|
220
|
+
# Don't add it if the single build value is already there
|
221
|
+
unless build_settings[name].strip == value.strip
|
222
|
+
build_settings[name] = [ build_settings[name], value ]
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
else
|
228
|
+
# If its an unknown type, then we should just throw a warning
|
229
|
+
# because we should'nt just change stuff willy, nilly.
|
230
|
+
Vendor.ui.warn(" Build setting #{name.inspect} wanted to change to #{value.inspect}, but it was already #{build_settings[name].inspect} in #{debug_key}")
|
231
|
+
end
|
232
|
+
|
233
|
+
else
|
234
|
+
Vendor.ui.debug(" Added build setting (#{name.inspect} = #{value.inspect}) to #{debug_key}")
|
235
|
+
|
236
|
+
build_settings[name] = value
|
237
|
+
end
|
238
|
+
|
239
|
+
end
|
240
|
+
|
241
|
+
end
|
242
|
+
|
243
|
+
@dirty = true
|
244
|
+
|
245
|
+
end
|
246
|
+
|
138
247
|
def add_file(options)
|
139
248
|
require_options options, :path, :file, :source_tree
|
140
249
|
|
141
|
-
# Ensure file exists
|
142
|
-
|
250
|
+
# Ensure file exists if we'nre not using the sdk root source tree.
|
251
|
+
# The SDKROOT source tree has a virtual file on the system, so
|
252
|
+
# File.exist checks will always return false.
|
253
|
+
unless options[:source_tree] == :sdkroot
|
254
|
+
raise StandardError.new("Could not find file `#{options[:file]}`") unless File.exists?(options[:file])
|
255
|
+
end
|
256
|
+
|
257
|
+
# Find targets
|
258
|
+
targets = targets_from_options(options)
|
143
259
|
|
144
260
|
# Create the group
|
145
261
|
group = create_group(options[:path])
|
@@ -174,6 +290,12 @@ module Vendor::XCode
|
|
174
290
|
attributes['name'] = name
|
175
291
|
attributes['path'] = options[:file]
|
176
292
|
|
293
|
+
elsif options[:source_tree] == :sdkroot
|
294
|
+
|
295
|
+
# Set the path and the name of the framework
|
296
|
+
attributes['path'] = options[:file]
|
297
|
+
attributes['sourceTree'] = "SDKROOT"
|
298
|
+
|
177
299
|
else
|
178
300
|
|
179
301
|
# Could not handle that option
|
@@ -192,6 +314,11 @@ module Vendor::XCode
|
|
192
314
|
# Add the file id to the groups children
|
193
315
|
group.attributes['children'] << file.id
|
194
316
|
|
317
|
+
# Add the file to targets
|
318
|
+
targets.each do |t|
|
319
|
+
add_file_to_target file, t
|
320
|
+
end
|
321
|
+
|
195
322
|
# Mark as dirty
|
196
323
|
@dirty = true
|
197
324
|
|
@@ -199,6 +326,50 @@ module Vendor::XCode
|
|
199
326
|
@objects_by_id[file.id] = file
|
200
327
|
end
|
201
328
|
|
329
|
+
def remove_file_from_target(file, target)
|
330
|
+
|
331
|
+
# Search through all the build phases for references to the file
|
332
|
+
build_files = []
|
333
|
+
target.build_phases.each do |phase|
|
334
|
+
build_files << phase.files.find_all do |build_file|
|
335
|
+
build_file.attributes['fileRef'] == file.id
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
# Remove the build files from the references
|
340
|
+
build_files.flatten.each do |build_file|
|
341
|
+
build_file.parent.attributes['files'].delete build_file.id
|
342
|
+
@objects_by_id.delete build_file.id
|
343
|
+
end
|
344
|
+
|
345
|
+
end
|
346
|
+
|
347
|
+
def add_file_to_target(file, target)
|
348
|
+
|
349
|
+
build_phase = build_phase_for_file(file.last_known_file_type, target)
|
350
|
+
|
351
|
+
if build_phase
|
352
|
+
|
353
|
+
Vendor.ui.debug "Adding #{file.attributes} to #{target.name} (build_phase = #{build_phase.class.name})"
|
354
|
+
|
355
|
+
# Add the file to XCode
|
356
|
+
build_file = Vendor::XCode::Proxy::PBXBuildFile.new(:project => self,
|
357
|
+
:id => Vendor::XCode::Proxy::Base.generate_id,
|
358
|
+
:attributes => { 'fileRef' => file.id })
|
359
|
+
|
360
|
+
# Set the parent
|
361
|
+
build_file.parent = build_phase
|
362
|
+
|
363
|
+
# Add the file to the internal index
|
364
|
+
@objects_by_id[build_file.id] = build_file
|
365
|
+
|
366
|
+
# Add the file to the build phase
|
367
|
+
build_phase.attributes['files'] << build_file.id
|
368
|
+
|
369
|
+
end
|
370
|
+
|
371
|
+
end
|
372
|
+
|
202
373
|
def to_ascii_plist
|
203
374
|
plist = { :archiveVersion => archive_version,
|
204
375
|
:classes => {},
|
@@ -240,10 +411,48 @@ module Vendor::XCode
|
|
240
411
|
|
241
412
|
private
|
242
413
|
|
414
|
+
def build_phase_for_file(file_type, target)
|
415
|
+
# Which build phase does this file belong?
|
416
|
+
klass = case file_type
|
417
|
+
when "sourcecode.c.objc"
|
418
|
+
Vendor::XCode::Proxy::PBXSourcesBuildPhase
|
419
|
+
when "wrapper.framework"
|
420
|
+
Vendor::XCode::Proxy::PBXFrameworksBuildPhase
|
421
|
+
end
|
422
|
+
|
423
|
+
if klass
|
424
|
+
# Find the build phase
|
425
|
+
build_phase = target.build_phases.find { |phase| phase.kind_of?(klass) }
|
426
|
+
unless build_phase
|
427
|
+
Vendor.ui.error "Could not find '#{klass.name}' build phase for target '#{target.name}'"
|
428
|
+
exit 1
|
429
|
+
end
|
430
|
+
build_phase
|
431
|
+
else
|
432
|
+
false
|
433
|
+
end
|
434
|
+
end
|
435
|
+
|
243
436
|
def require_options(options, *keys)
|
244
437
|
keys.each { |k| raise StandardError.new("Missing :#{k} option") unless options[k] }
|
245
438
|
end
|
246
439
|
|
440
|
+
def targets_from_options(options)
|
441
|
+
if options[:targets]
|
442
|
+
[ *options[:targets] ].map do |t|
|
443
|
+
if t == :all
|
444
|
+
root_object.targets
|
445
|
+
else
|
446
|
+
target = find_target(t)
|
447
|
+
raise StandardError.new("Could not find target '#{t}' in project '#{name}'") unless target
|
448
|
+
target
|
449
|
+
end
|
450
|
+
end.flatten.uniq
|
451
|
+
else
|
452
|
+
root_object.targets
|
453
|
+
end
|
454
|
+
end
|
455
|
+
|
247
456
|
end
|
248
457
|
|
249
458
|
end
|