xcoder 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +8 -0
- data/.rvmrc +4 -0
- data/Gemfile +14 -0
- data/Guardfile +14 -0
- data/README.md +142 -0
- data/Rakefile +28 -0
- data/lib/xcode/build_file.rb +22 -0
- data/lib/xcode/build_phase.rb +92 -0
- data/lib/xcode/builder.rb +190 -0
- data/lib/xcode/buildfile.rb +101 -0
- data/lib/xcode/configuration.rb +161 -0
- data/lib/xcode/configuration_list.rb +85 -0
- data/lib/xcode/core_ext/array.rb +23 -0
- data/lib/xcode/core_ext/boolean.rb +21 -0
- data/lib/xcode/core_ext/fixnum.rb +5 -0
- data/lib/xcode/core_ext/hash.rb +27 -0
- data/lib/xcode/core_ext/string.rb +26 -0
- data/lib/xcode/file_reference.rb +77 -0
- data/lib/xcode/group.rb +197 -0
- data/lib/xcode/info_plist.rb +41 -0
- data/lib/xcode/keychain.rb +122 -0
- data/lib/xcode/parsers/plutil_project_parser.rb +20 -0
- data/lib/xcode/project.rb +335 -0
- data/lib/xcode/provisioning_profile.rb +53 -0
- data/lib/xcode/registry.rb +168 -0
- data/lib/xcode/resource.rb +203 -0
- data/lib/xcode/scheme.rb +36 -0
- data/lib/xcode/shell.rb +22 -0
- data/lib/xcode/simple_identifier_generator.rb +17 -0
- data/lib/xcode/target.rb +204 -0
- data/lib/xcode/test/formatters/junit_formatter.rb +50 -0
- data/lib/xcode/test/ocunit_report_parser.rb +68 -0
- data/lib/xcode/test/suite_result.rb +31 -0
- data/lib/xcode/test/test_result.rb +38 -0
- data/lib/xcode/testflight.rb +56 -0
- data/lib/xcode/variant_group.rb +28 -0
- data/lib/xcode/version.rb +3 -0
- data/lib/xcode/workspace.rb +40 -0
- data/lib/xcoder.rb +105 -0
- data/xcoder.gemspec +26 -0
- metadata +52 -12
@@ -0,0 +1,101 @@
|
|
1
|
+
module Xcode
|
2
|
+
class Buildfile
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@values = {}
|
6
|
+
@before = {:clean => [], :build => [], :package => []}
|
7
|
+
@after = {:clean => [], :build => [], :package => []}
|
8
|
+
end
|
9
|
+
|
10
|
+
def method_missing(method, *args)
|
11
|
+
@values[method.to_sym] = args[0]
|
12
|
+
end
|
13
|
+
|
14
|
+
def before(event, &block)
|
15
|
+
@before[event]<< block
|
16
|
+
end
|
17
|
+
|
18
|
+
def after(event, &block)
|
19
|
+
@after[event]<< block
|
20
|
+
end
|
21
|
+
|
22
|
+
def getBinding
|
23
|
+
binding()
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.load(file)
|
27
|
+
file = File.expand_path file
|
28
|
+
raise "Unable to find the buildfile #{file}" if !File.exists? file
|
29
|
+
|
30
|
+
|
31
|
+
puts "Loading Buildfile: #{file}"
|
32
|
+
bc = Xcode::Buildfile.new
|
33
|
+
eval(File.read(file), bc.getBinding, file)
|
34
|
+
bc
|
35
|
+
end
|
36
|
+
|
37
|
+
def build
|
38
|
+
label = "#{@values[:target]}"
|
39
|
+
puts "[#{label}] Loading project #{@values[:project]}, target #{@values[:target]}, config #{@values[:config]}"
|
40
|
+
config = Xcode.project(@values[:project]).target(@values[:target]).config(@values[:config])
|
41
|
+
config.info_plist do |info|
|
42
|
+
puts "[#{label}] Update info plist version to #{@values[:version]}"
|
43
|
+
info.version = @values[:version]
|
44
|
+
end
|
45
|
+
builder = config.builder
|
46
|
+
|
47
|
+
# unless @values[:identity].nil?
|
48
|
+
# builder.identity = @values[:identity]
|
49
|
+
# puts "[#{label}] Set build identity to #{@values[:identity]}"
|
50
|
+
# end
|
51
|
+
|
52
|
+
unless @values[:profile].nil?
|
53
|
+
builder.profile = @values[:profile]
|
54
|
+
puts "[#{label}] Set build profile to #{@values[:profile]}"
|
55
|
+
end
|
56
|
+
|
57
|
+
Keychain.temp_keychain(@values[:project]) do |kc|
|
58
|
+
kc.import @values[:certificate], @values[:password]
|
59
|
+
|
60
|
+
builder.identity = @values[:identity] || kc.certificates.first
|
61
|
+
builder.keychain = kc
|
62
|
+
|
63
|
+
puts "[#{label}] CLEAN"
|
64
|
+
@before[:clean].each do |b|
|
65
|
+
b.call(builder)
|
66
|
+
end
|
67
|
+
builder.clean
|
68
|
+
@after[:clean].each do |b|
|
69
|
+
b.call(builder)
|
70
|
+
end
|
71
|
+
|
72
|
+
puts "[#{label}] BUILD"
|
73
|
+
@before[:build].each do |b|
|
74
|
+
b.call(builder)
|
75
|
+
end
|
76
|
+
builder.build
|
77
|
+
@after[:build].each do |b|
|
78
|
+
b.call(builder)
|
79
|
+
end
|
80
|
+
|
81
|
+
puts "[#{label}] PACKAGE"
|
82
|
+
@before[:package].each do |b|
|
83
|
+
b.call(builder)
|
84
|
+
end
|
85
|
+
builder.package
|
86
|
+
@after[:package].each do |b|
|
87
|
+
b.call(builder)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
if @values.has_key? :testflight_api_token and @values.has_key? :testflight_team_token
|
93
|
+
puts "[#{label}] Uploading to testflight"
|
94
|
+
`curl -X POST http://testflightapp.com/api/builds.json -F file=@"#{builder.ipa_path}" -F dsym=@"#{builder.dsym_zip_path}" -F api_token='#{@values[:testflight_api_token]}' -F team_token='#{@values[:testflight_team_token]}' -F notify=True -F notes=\"#{@values[:testflight_notes]}\" -F distribution_lists='#{@values[:testflight_lists].join(',')}'`
|
95
|
+
end
|
96
|
+
|
97
|
+
builder
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
require 'xcode/builder'
|
2
|
+
|
3
|
+
module Xcode
|
4
|
+
|
5
|
+
#
|
6
|
+
# Projects have a number of build configurations. These configurations are
|
7
|
+
# usually the default 'Debug' and 'Release'. However, custom ones can be
|
8
|
+
# defined.
|
9
|
+
#
|
10
|
+
# @see https://developer.apple.com/library/ios/#documentation/ToolsLanguages/Conceptual/Xcode4UserGuide/Building/Building.html
|
11
|
+
#
|
12
|
+
# Each configuration is defined and then a reference of that configuration is
|
13
|
+
# maintained in the Target through the XCConfigurationList.
|
14
|
+
#
|
15
|
+
# @example Xcode configuration
|
16
|
+
#
|
17
|
+
# E21D8ABB14E0F817002E56AA /* Debug */ = {
|
18
|
+
# isa = XCBuildConfiguration;
|
19
|
+
# buildSettings = {
|
20
|
+
# "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
21
|
+
# GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
22
|
+
# GCC_PREFIX_HEADER = "newtarget/newtarget-Prefix.pch";
|
23
|
+
# INFOPLIST_FILE = "newtarget/newtarget-Info.plist";
|
24
|
+
# PRODUCT_NAME = "$(TARGET_NAME)";
|
25
|
+
# WRAPPER_EXTENSION = app;
|
26
|
+
# };
|
27
|
+
# name = Debug;
|
28
|
+
# };
|
29
|
+
#
|
30
|
+
module Configuration
|
31
|
+
|
32
|
+
def self.default_properties(name)
|
33
|
+
{ 'isa' => 'XCBuildConfiguration',
|
34
|
+
'buildSettings' => {
|
35
|
+
"SDKROOT" => "iphoneos",
|
36
|
+
"OTHER_CFLAGS" => "-DNS_BLOCK_ASSERTIONS=1",
|
37
|
+
"TARGETED_DEVICE_FAMILY" => "1,2",
|
38
|
+
"GCC_C_LANGUAGE_STANDARD" => "gnu99",
|
39
|
+
"ALWAYS_SEARCH_USER_PATHS" => "NO",
|
40
|
+
"GCC_VERSION" => "com.apple.compilers.llvm.clang.1_0",
|
41
|
+
"ARCHS" => "$(ARCHS_STANDARD_32_BIT)",
|
42
|
+
"GCC_WARN_ABOUT_MISSING_PROTOTYPES" => "YES",
|
43
|
+
"GCC_WARN_ABOUT_RETURN_TYPE" => "YES",
|
44
|
+
"CODE_SIGN_IDENTITY[sdk=>iphoneos*]" => "iPhone Developer",
|
45
|
+
"GCC_PRECOMPILE_PREFIX_HEADER" => "YES",
|
46
|
+
"VALIDATE_PRODUCT" => "YES",
|
47
|
+
"IPHONEOS_DEPLOYMENT_TARGET" => "5.0",
|
48
|
+
"COPY_PHASE_STRIP" => "YES",
|
49
|
+
"GCC_PREFIX_HEADER" => "#{name}/#{name}-Prefix.pch",
|
50
|
+
"INFOPLIST_FILE" => "#{name}/#{name}-Info.plist",
|
51
|
+
"PRODUCT_NAME" => "$(TARGET_NAME)",
|
52
|
+
"WRAPPER_EXTENSION" => "app" },
|
53
|
+
"name" => name }
|
54
|
+
end
|
55
|
+
|
56
|
+
#
|
57
|
+
# The configuration is defined within a target.
|
58
|
+
# @see PBXNativeTarget
|
59
|
+
#
|
60
|
+
attr_accessor :target
|
61
|
+
|
62
|
+
#
|
63
|
+
# @return the location for the InfoPlist file for the configuration.
|
64
|
+
# @see InfoPlist
|
65
|
+
#
|
66
|
+
def info_plist_location
|
67
|
+
build_settings['INFOPLIST_FILE']
|
68
|
+
end
|
69
|
+
|
70
|
+
#
|
71
|
+
# Opens the info plist associated with the configuration and allows you to
|
72
|
+
# edit the configuration.
|
73
|
+
#
|
74
|
+
# @example Editing the configuration
|
75
|
+
#
|
76
|
+
# config = Xcode.project('MyProject.xcodeproj').target('Application').config('Debug')
|
77
|
+
# config.info_plist do |plist|
|
78
|
+
# puts plist.version # => 1.0
|
79
|
+
# plist.version = 1.1
|
80
|
+
# marketing_version = 12.1
|
81
|
+
# end
|
82
|
+
#
|
83
|
+
# @see InfoPlist
|
84
|
+
#
|
85
|
+
def info_plist
|
86
|
+
# puts @json.inspect
|
87
|
+
info = Xcode::InfoPlist.new(self, info_plist_location)
|
88
|
+
yield info if block_given?
|
89
|
+
info.save
|
90
|
+
info
|
91
|
+
end
|
92
|
+
|
93
|
+
#
|
94
|
+
# @return the name of the product that this configuration will generate.
|
95
|
+
#
|
96
|
+
def product_name
|
97
|
+
substitute(build_settings['PRODUCT_NAME'])
|
98
|
+
end
|
99
|
+
|
100
|
+
#
|
101
|
+
# Retrieve the configuration value for the given name
|
102
|
+
#
|
103
|
+
# @param [String] name of the configuration settings to return
|
104
|
+
# @return [String,Array,Hash] the value stored for the specified configuration
|
105
|
+
#
|
106
|
+
def get(name)
|
107
|
+
build_settings[name]
|
108
|
+
end
|
109
|
+
|
110
|
+
#
|
111
|
+
# Set the configuration value for the given name
|
112
|
+
#
|
113
|
+
# @param [String] name of the the configuration setting
|
114
|
+
# @param [String,Array,Hash] value the value to store for the specific setting
|
115
|
+
#
|
116
|
+
def set(name, value)
|
117
|
+
build_settings[name] = value
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
#
|
122
|
+
# Create a builder for this given project->target->configuration.
|
123
|
+
#
|
124
|
+
# @return [Builder] this is a builder for the configuration.
|
125
|
+
# @see Builder
|
126
|
+
#
|
127
|
+
def builder
|
128
|
+
puts "Making a Builder with #{self} #{self.methods}"
|
129
|
+
Xcode::Builder.new(self)
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
private
|
134
|
+
|
135
|
+
#
|
136
|
+
# Within the configuration properties variables reference the target,
|
137
|
+
# i.e."$(TARGET_NAME)". This method will find and replace the target
|
138
|
+
# constant with the appropriate value.
|
139
|
+
#
|
140
|
+
# @param [String] value is a property of the configuration that may contain
|
141
|
+
# the target variable to replace.
|
142
|
+
#
|
143
|
+
# @return [String] a string without the variable reference.
|
144
|
+
#
|
145
|
+
def substitute(value)
|
146
|
+
if value=~/\$\(.*\)/
|
147
|
+
value.gsub(/\$\((.*)\)/) do |match|
|
148
|
+
case match
|
149
|
+
when "$(TARGET_NAME)"
|
150
|
+
@target.name
|
151
|
+
else
|
152
|
+
raise "Unknown substitution variable #{match}"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
else
|
156
|
+
value
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module Xcode
|
2
|
+
module ConfigurationList
|
3
|
+
|
4
|
+
#
|
5
|
+
# @example configuration list
|
6
|
+
#
|
7
|
+
# 7165D47D146B4EA100DE2F0E /* Build configuration list for PBXNativeTarget "TestProject" */ = {
|
8
|
+
# isa = XCConfigurationList;
|
9
|
+
# buildConfigurations = (
|
10
|
+
# 7165D47E146B4EA100DE2F0E /* Debug */,
|
11
|
+
# 7165D47F146B4EA100DE2F0E /* Release */,
|
12
|
+
# );
|
13
|
+
# defaultConfigurationIsVisible = 0;
|
14
|
+
# defaultConfigurationName = Release;
|
15
|
+
# };
|
16
|
+
def self.configration_list
|
17
|
+
{ 'isa' => 'XCConfigurationList',
|
18
|
+
'buildConfigurations' => [],
|
19
|
+
'defaultConfigurationIsVisible' => '0',
|
20
|
+
'defaultConfigurationName' => '' }
|
21
|
+
end
|
22
|
+
|
23
|
+
#
|
24
|
+
# @return [Hash] a hash of symbol names to configuration names.
|
25
|
+
#
|
26
|
+
def self.symbol_config_name_to_config_name
|
27
|
+
{ :debug => 'Debug', :release => 'Release' }
|
28
|
+
end
|
29
|
+
|
30
|
+
#
|
31
|
+
# Create a configuration for this ConfigurationList. This configuration needs
|
32
|
+
# to have a name.
|
33
|
+
#
|
34
|
+
# @note unique names are currently not enforced but likely necessary for the
|
35
|
+
# the target to be build successfully.
|
36
|
+
#
|
37
|
+
# @param [Types] name Description
|
38
|
+
#
|
39
|
+
def create_config(name)
|
40
|
+
|
41
|
+
name = ConfigurationList.symbol_config_name_to_config_name[name] if ConfigurationList.symbol_config_name_to_config_name[name]
|
42
|
+
|
43
|
+
# @todo a configuration has additional fields that are ususally set with
|
44
|
+
# some target information for the title.
|
45
|
+
|
46
|
+
new_config = @registry.add_object(Configuration.default_properties(name))
|
47
|
+
@properties['buildConfigurations'] << new_config.identifier
|
48
|
+
|
49
|
+
yield new_config if block_given?
|
50
|
+
|
51
|
+
new_config.save!
|
52
|
+
end
|
53
|
+
|
54
|
+
#
|
55
|
+
# @return [BuildConfiguration] the build configuration that is set to default;
|
56
|
+
# nil if no configuration has been set as default.
|
57
|
+
#
|
58
|
+
def default_config
|
59
|
+
build_configurations.find {|config| config.name == default_configuration_name }
|
60
|
+
end
|
61
|
+
|
62
|
+
#
|
63
|
+
# @return [String] the name of the default build configuration; nil if no
|
64
|
+
# configuration has been set as default.
|
65
|
+
#
|
66
|
+
def default_config_name
|
67
|
+
default_configuration_name
|
68
|
+
end
|
69
|
+
|
70
|
+
#
|
71
|
+
# @todo allow the ability for a configuration to set itself as default and/or
|
72
|
+
# let a configuration be specified as a parameter here. Though we need
|
73
|
+
# to check to see that the configuration is part of the this configuration
|
74
|
+
# list.
|
75
|
+
#
|
76
|
+
# @param [String] name of the build configuration to set as the default
|
77
|
+
# configuration; specify nil if you want to remove any default configuration.
|
78
|
+
#
|
79
|
+
def set_default_config(name)
|
80
|
+
# @todo ensure that the name specified is one of the available configurations
|
81
|
+
@properties['defaultConfigurationName'] = name
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class Array
|
2
|
+
|
3
|
+
#
|
4
|
+
# Arrays in an Xcode project take a particular format.
|
5
|
+
#
|
6
|
+
# @note the last element in the array can have a comma; it is optional.
|
7
|
+
#
|
8
|
+
# @example output format:
|
9
|
+
#
|
10
|
+
# (
|
11
|
+
# ITEM1,
|
12
|
+
# ITEM2,
|
13
|
+
# ITEM3
|
14
|
+
# )
|
15
|
+
#
|
16
|
+
def to_xcplist
|
17
|
+
plist_of_items = map {|item| item.to_xcplist }.join(",\n")
|
18
|
+
|
19
|
+
%{(
|
20
|
+
#{plist_of_items}
|
21
|
+
)}
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
class TrueClass
|
4
|
+
|
5
|
+
#
|
6
|
+
# Xcode project's expect boolean trues to be the word YES.
|
7
|
+
#
|
8
|
+
def to_xcplist
|
9
|
+
"YES"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class FalseClass
|
14
|
+
|
15
|
+
#
|
16
|
+
# Xcode project's expect boolean trues to be the word NO.
|
17
|
+
#
|
18
|
+
def to_xcplist
|
19
|
+
"NO"
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class Hash
|
2
|
+
|
3
|
+
#
|
4
|
+
# Hashes in an Xcode project take a particular format.
|
5
|
+
#
|
6
|
+
# @note the keys are represeted in this output with quotes; this is actually
|
7
|
+
# optional for certain keys based on their format. This is done to ensure
|
8
|
+
# that all keys that do not conform are ensured proper output.
|
9
|
+
#
|
10
|
+
# @example output format:
|
11
|
+
#
|
12
|
+
# {
|
13
|
+
# "KEY" = "VALUE";
|
14
|
+
# "KEY" = "VALUE";
|
15
|
+
# "KEY" = "VALUE";
|
16
|
+
# }
|
17
|
+
#
|
18
|
+
def to_xcplist
|
19
|
+
plist_of_items = map do |k,v|
|
20
|
+
"\"#{k}\" = #{v.to_xcplist};"
|
21
|
+
end.join("\n")
|
22
|
+
|
23
|
+
%{{
|
24
|
+
#{plist_of_items}
|
25
|
+
}}
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
class String
|
4
|
+
|
5
|
+
#
|
6
|
+
# Xcode format for a string is exactly the same as you would expect in JSON
|
7
|
+
#
|
8
|
+
def to_xcplist
|
9
|
+
to_json
|
10
|
+
end
|
11
|
+
|
12
|
+
#
|
13
|
+
# Similar to ActiveRecord's underscore method. Return a string version
|
14
|
+
# underscored. This is used specifically to convert the property keys into
|
15
|
+
# Ruby friendly names as they are used for creating method names.
|
16
|
+
#
|
17
|
+
# @return [String] convert camel-cased words, generating underscored, ruby
|
18
|
+
# friend names.
|
19
|
+
def underscore
|
20
|
+
self.gsub(/::/, '/').
|
21
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
22
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
23
|
+
tr("-", "_").
|
24
|
+
downcase
|
25
|
+
end
|
26
|
+
end
|