xcoder 0.1.1 → 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.
- 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
|