xcoder 0.0.21 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -2,3 +2,6 @@
2
2
  .bundle
3
3
  Gemfile.lock
4
4
  pkg/*
5
+ spec/TestProject/TestProject.xcodeproj/xcuserdata
6
+ spec/TestProject/TestProject.xcodeproj/project.xcworkspace/
7
+ spec/TestProject/build
data/Gemfile CHANGED
@@ -5,4 +5,7 @@ gem 'rspec'
5
5
  gem 'builder'
6
6
  gem 'json'
7
7
  gem 'plist'
8
- gem 'rest-client'
8
+ gem 'rest-client'
9
+ gem 'guard'
10
+ gem 'guard-rspec'
11
+ gem 'ruby-debug19'
data/Guardfile ADDED
@@ -0,0 +1,13 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'rspec', :version => 2, :cli => "--color --format d --fail-fast" do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ # As the registry and resource file affect most every file, the entire
7
+ # suite should be run when they are changed
8
+ watch(%r{^lib/xcode/(?:registry|resource)\.rb$}) { "spec" }
9
+ watch(%r{^lib/xcode/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
10
+ watch('spec/spec_helper.rb') { "spec" }
11
+
12
+ end
13
+
data/README.md CHANGED
@@ -74,6 +74,8 @@ Or, access them by name:
74
74
 
75
75
  Note: The builder behaves the same as the builder for the target/config approach and will force xcodebuild to use the local build/ directory (as per xcode3) rather than a generated temporary directory in DerivedData. This may or may not be a good thing.
76
76
 
77
+ Note: Shared schemes and user (current logged in user) specific schemes are both loaded. They may share names and other similarities that make them hard to distinguish. Currently the priority loading order is shared schemes and then user specific schemes.
78
+
77
79
  ### Provisioning profiles
78
80
 
79
81
  The library provides a mechanism to install/uninstall a provisioning profile. This normally happens as part of a build (if a profile is provided to the builder, see above), but you can do this manually:
data/Rakefile CHANGED
@@ -1 +1,16 @@
1
1
  require "bundler/gem_tasks"
2
+
3
+ task :default => :specs
4
+
5
+ task :specs do
6
+ system "rspec -c spec"
7
+ end
8
+
9
+ namespace :test_project do
10
+
11
+ task :reset do
12
+ system "git co -- spec/TestProject"
13
+ end
14
+
15
+ end
16
+
@@ -0,0 +1,22 @@
1
+ module Xcode
2
+
3
+ #
4
+ # PBXBuildFile are entries within the project that create a link between the
5
+ # file and the PBXFileReference. One is created for each file added to a build
6
+ # target.
7
+ #
8
+ module BuildFile
9
+
10
+ #
11
+ # Create the properties hash for a build file with the given file reference
12
+ # identifier.
13
+ #
14
+ # @param [String] file_identifier the unique identifier for the file
15
+ # @return [Hash] the properties hash for a default BuildFile.
16
+ #
17
+ def self.with_properties file_identifier
18
+ { 'isa' => "PBXBuildFile", 'fileRef' => file_identifier }
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,46 @@
1
+ module Xcode
2
+
3
+ module BuildPhase
4
+
5
+ #
6
+ # Return the files that are referenced by the build files. This traverses
7
+ # the level of indirection to make it easier to get to the FileReference.
8
+ #
9
+ # Another method, file, exists which will return the BuildFile references.
10
+ #
11
+ # @return [Array<FileReference>] the files referenced by the build files.
12
+ #
13
+ def build_files
14
+ files.map {|file| file.fileRef }
15
+ end
16
+
17
+ #
18
+ # Find the first file that has the name or path that matches the specified
19
+ # parameter.
20
+ #
21
+ # @param [String] name the name or the path of the file.
22
+ # @return [FileReference] the file referenced that matches the name or path;
23
+ # nil if no file is found.
24
+ #
25
+ def build_file(name)
26
+ build_files.find {|file| file.name == name || file.path == name }
27
+ end
28
+
29
+ #
30
+ # Add the specified file to the Build Phase.
31
+ #
32
+ # First a BuildFile entry is created for the file and then the build file
33
+ # entry is added to the particular build phase. A BuildFile identifier must
34
+ # exist for each target.
35
+ #
36
+ # @param [FileReference] file the FileReference Resource to add to the build
37
+ # phase.
38
+ #
39
+ def add_build_file(file)
40
+ build_identifier = @registry.add_object BuildFile.with_properties(file.identifier)
41
+ @properties['files'] << build_identifier
42
+ end
43
+
44
+ end
45
+
46
+ end
data/lib/xcode/builder.rb CHANGED
@@ -12,6 +12,8 @@ module Xcode
12
12
  @scheme = config
13
13
  config = config.launch
14
14
  end
15
+
16
+ puts "CONFIG: #{config}"
15
17
  @target = config.target
16
18
  @sdk = @target.project.sdk
17
19
  @config = config
@@ -1,14 +1,115 @@
1
1
  require 'xcode/builder'
2
2
 
3
3
  module Xcode
4
- class Configuration
5
- attr_reader :target
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
+ #
33
+ # The configuration is defined within a target.
34
+ # @see PBXNativeTarget
35
+ #
36
+ attr_accessor :target
37
+
38
+ #
39
+ # @return the location for the InfoPlist file for the configuration.
40
+ # @see InfoPlist
41
+ #
42
+ def info_plist_location
43
+ buildSettings['INFOPLIST_FILE']
44
+ end
6
45
 
7
- def initialize(target, json)
8
- @target = target
9
- @json = json
46
+ #
47
+ # Opens the info plist associated with the configuration and allows you to
48
+ # edit the configuration.
49
+ #
50
+ # @example Editing the configuration
51
+ #
52
+ # config = Xcode.project('MyProject.xcodeproj').target('Application').config('Debug')
53
+ # config.info_plist do |plist|
54
+ # puts plist.version # => 1.0
55
+ # plist.version = 1.1
56
+ # marketing_version = 12.1
57
+ # end
58
+ #
59
+ # @see InfoPlist
60
+ #
61
+ def info_plist
62
+ # puts @json.inspect
63
+ info = Xcode::InfoPlist.new(self, info_plist_location)
64
+ yield info if block_given?
65
+ info.save
66
+ info
10
67
  end
11
68
 
69
+ #
70
+ # @return the name of the product that this configuration will generate.
71
+ #
72
+ def product_name
73
+ substitute(buildSettings['PRODUCT_NAME'])
74
+ end
75
+
76
+ def set_other_linker_flags(value)
77
+ set 'OTHER_LDFLAGS', value
78
+ end
79
+
80
+ def get(name)
81
+ buildSettings[name]
82
+ end
83
+
84
+ def set name, value
85
+ buildSettings[name] = value
86
+ end
87
+
88
+
89
+ #
90
+ # Create a builder for this given project->target->configuration.
91
+ #
92
+ # @return [Builder] this is a builder for the configuration.
93
+ # @see Builder
94
+ #
95
+ def builder
96
+ puts "Making a Builder with #{self} #{self.methods}"
97
+ Xcode::Builder.new(self)
98
+ end
99
+
100
+
101
+ private
102
+
103
+ #
104
+ # Within the configuration properties variables reference the target,
105
+ # i.e."$(TARGET_NAME)". This method will find and replace the target
106
+ # constant with the appropriate value.
107
+ #
108
+ # @param [String] value is a property of the configuration that may contain
109
+ # the target variable to replace.
110
+ #
111
+ # @return [String] a string without the variable reference.
112
+ #
12
113
  def substitute(value)
13
114
  if value=~/\$\(.*\)/
14
115
  value.gsub(/\$\((.*)\)/) do |match|
@@ -23,29 +124,6 @@ module Xcode
23
124
  value
24
125
  end
25
126
  end
26
-
27
- def info_plist_location
28
- @json['buildSettings']['INFOPLIST_FILE']
29
- end
30
-
31
- def product_name
32
- substitute(@json['buildSettings']['PRODUCT_NAME'])
33
- end
34
-
35
- def name
36
- @json['name']
37
- end
38
-
39
- def info_plist
40
- # puts @json.inspect
41
- info = Xcode::InfoPlist.new(self, info_plist_location)
42
- yield info if block_given?
43
- info.save
44
- info
45
- end
46
-
47
- def builder
48
- Xcode::Builder.new(self)
49
- end
127
+
50
128
  end
51
- end
129
+ 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,5 @@
1
+ class Fixnum
2
+ def to_xcplist
3
+ to_s
4
+ end
5
+ 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,11 @@
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
+ end
@@ -0,0 +1,29 @@
1
+ module Xcode
2
+
3
+ #
4
+ # Within the project file the PBXFileReference represents the object
5
+ # representation to the file on the file sytem. This is usually your source
6
+ # files within your project.
7
+ #
8
+ module FileReference
9
+
10
+ # This is the group for which this file is contained within.
11
+ attr_accessor :supergroup
12
+
13
+ def self.with_properties_for_framework(name)
14
+ { 'isa' => "PBXFileReference",
15
+ 'lastKnownFileType' => "wrapper.framework",
16
+ 'name' => "#{name}.framework",
17
+ 'path' => "System/Library/Frameworks/#{name}.framework",
18
+ 'sourceTree' => "SDKROOT" }
19
+ end
20
+
21
+ def self.with_properties_for_path(path)
22
+ { 'isa' => 'PBXFileReference',
23
+ 'path' => path,
24
+ 'sourceTree' => '<group>' }
25
+ end
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,118 @@
1
+ module Xcode
2
+
3
+ #
4
+ # Within the project file there are logical separation of resources into groups
5
+ # these groups may contain subgroups, files, or other objects. They have
6
+ # children.
7
+ #
8
+ # PBXGroup here provides the methods to traverse the groups to find these
9
+ # children resources as well as provide the ability to generate child
10
+ # resources.
11
+ #
12
+ module Group
13
+
14
+ #
15
+ # Return the hash that maps to the properties for a logical group
16
+ #
17
+ # @param [String] name of the logical group
18
+ #
19
+ def self.with_properties_for_logical_group(name)
20
+ { 'isa' => 'PBXGroup',
21
+ 'name' => name,
22
+ 'sourceTree' => '<group>',
23
+ 'children' => [] }
24
+ end
25
+
26
+
27
+ # This is the group for which this file is contained within.
28
+ attr_accessor :supergroup
29
+
30
+ #
31
+ # @example Return all the sub-groups of the main group
32
+ #
33
+ # main_group = Xcode.project('MyProject.xcodeproj').groups
34
+ # main_group.groups
35
+ #
36
+ # @return [Array] the sub-groups contained within this group.
37
+ #
38
+ def groups
39
+ children.map do |group|
40
+ # TODO: this will return all children when it should just return subgroups
41
+ group.supergroup = self
42
+ group
43
+ end
44
+ end
45
+
46
+ #
47
+ # Find all the child groups that have a name that matches the specified name.
48
+ #
49
+ # @param [String] name of the group that you are looking to return.
50
+ # @return [Array<PBXGroup>] the groups with the same matching name. This
51
+ # could be no groups, one group, or multiple groups.
52
+ #
53
+ def group(name)
54
+ groups.find_all {|group| group.name == name }
55
+ end
56
+
57
+
58
+ #
59
+ # Adds a group as a child to current group with the given name.
60
+ #
61
+ # @note A group may be added that has the same name as another group as they
62
+ # are distinguished by a unique identifier and not by name.
63
+ #
64
+ # @param [String] name of the group that you want to add as a child group of
65
+ # the specified group.
66
+ #
67
+ def add_group(name)
68
+
69
+ # Groups that represent a physical path often have the key 'path' with
70
+ # the value being it's path name.
71
+ #
72
+ # Groups that represent a logical group often have the key 'name' with
73
+ # the value being it's group name.
74
+
75
+ new_identifier = @registry.add_object Group.with_properties_for_logical_group(name)
76
+
77
+ # Add the group's identifier to the list of children
78
+
79
+ @properties['children'] << new_identifier
80
+
81
+ # Find the newly added group to return
82
+
83
+ groups.find {|group| group.identifier == new_identifier }
84
+
85
+ end
86
+
87
+ #
88
+ # Add a file to the specified group. Currently the file creation requires
89
+ # the path to the physical file.
90
+ #
91
+ # @param [String] path to the file that is being added.
92
+ #
93
+ def add_file(path)
94
+
95
+ new_identifier = @registry.add_object FileReference.with_properties_for_path(path)
96
+
97
+ @properties['children'] << new_identifier
98
+
99
+ children.find {|file| file.identifier == new_identifier }
100
+
101
+ end
102
+
103
+
104
+ def add_framework framework_name
105
+ new_identifier = @registry.add_object FileReference.with_properties_for_framework(framework_name)
106
+
107
+ # Add the framework to the group
108
+
109
+ @properties['children'] << new_identifier
110
+
111
+ children.find {|file| file.identifier == new_identifier }
112
+
113
+ end
114
+
115
+
116
+ end
117
+
118
+ end