├── .gitignore ├── .rspec ├── .ruby-version ├── .travis.yml ├── .yardopts ├── Gemfile ├── LICENSE ├── README.md ├── Rakefile ├── lib ├── xcodeproject.rb └── xcodeproject │ ├── build_phase_node.rb │ ├── data.rb │ ├── exceptions.rb │ ├── extend │ ├── array.rb │ ├── hash.rb │ └── string.rb │ ├── file_node.rb │ ├── formatter.rb │ ├── node.rb │ ├── pbx_build_file.rb │ ├── pbx_file_reference.rb │ ├── pbx_group.rb │ ├── pbx_native_target.rb │ ├── pbx_project.rb │ ├── project.rb │ ├── root_node.rb │ ├── tasks │ └── build_task.rb │ ├── uuid_generator.rb │ ├── version.rb │ ├── xc_build_configuration.rb │ └── xc_configuration_list.rb ├── resources └── example │ ├── dir1a │ └── dir2a │ │ └── dir3a │ │ └── dir4a │ │ ├── file5a-a.h │ │ ├── file5a-a.m │ │ ├── file5a-r.h │ │ └── file5a-r.m │ ├── dir1b │ ├── dir2b │ │ └── file3b.m │ └── file2b.m │ ├── dir1c │ ├── dir2c │ │ └── .keep │ ├── file2c.h │ └── file2c.m │ ├── example.xcodeproj │ └── project.pbxproj │ └── example │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── en.lproj │ └── InfoPlist.strings │ ├── example-Info.plist │ ├── example-Prefix.pch │ └── main.m ├── spec ├── build_phase_node_spec.rb ├── file_node_spec.rb ├── pbx_build_file_spec.rb ├── pbx_file_reference_spec.rb ├── pbx_group_spec.rb ├── pbx_native_target_spec.rb ├── pbx_project_spec.rb ├── project_spec.rb ├── spec_helper.rb ├── xc_build_configuration_spec.rb └── xc_configuration_list_spec.rb └── xcodeproject.gemspec /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | .DS_Store 3 | ._* 4 | .Spotlight-V100 5 | .Trashes 6 | 7 | # vim 8 | .*.sw[a-z] 9 | *.un~ 10 | Session.vim 11 | 12 | # ruby 13 | *.gem 14 | *.rbc 15 | .bundle 16 | .config 17 | .yardoc 18 | Gemfile.lock 19 | InstalledFiles 20 | _yardoc 21 | coverage 22 | doc/ 23 | lib/bundler/man 24 | pkg 25 | rdoc 26 | spec/reports 27 | test/tmp 28 | test/version_tmp 29 | tmp 30 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --format progress 3 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.3.0 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.0.0-p481 4 | - 2.1.0 5 | - 2.1.1 6 | - 2.1.2 7 | - 2.1.3 8 | - 2.1.4 9 | - 2.1.5 10 | - 2.3.1 11 | before_install: gem update --remote bundler 12 | install: 13 | - bundle install --retry=3 14 | -------------------------------------------------------------------------------- /.yardopts: -------------------------------------------------------------------------------- 1 | --markup markdown 2 | --markup-provider redcarpet 3 | --charset utf-8 4 | --readme README.md 5 | - 6 | README.md 7 | LICENSE 8 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in xcodeproject.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Andrei Nesterov 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | XcodeProject 2 | === 3 | The Ruby API for working with Xcode project files. 4 | 5 | [![Pledgie Badge][pledgie_img]][pledgie] 6 | 7 | [![Build Status][travis_img]][travis] 8 | 9 | Installation 10 | --- 11 | `gem install xcodeproject` 12 | 13 | Getting started 14 | --- 15 | A simple example that displays all targets of the project will look like this: 16 | 17 | ```ruby 18 | require 'rubygems' 19 | require 'xcodeproject' 20 | 21 | proj = XcodeProject::Project.new('path/to/example.xcodeproj') 22 | proj.read.targets.each do |target| 23 | puts target.name 24 | end 25 | ``` 26 | 27 | First, you must create an XcodeProject::Project object like this: 28 | 29 | ```ruby 30 | proj = XcodeProject::Project.new('path/to/example.xcodeproj') 31 | ``` 32 | 33 | Or you can find all projects are located in the specified directory: 34 | 35 | ```ruby 36 | projs = XcodeProject::Project.find('path/to/dir') 37 | ``` 38 | 39 | Or by specified directory pattern: 40 | 41 | ```ruby 42 | projs = XcodeProject::Project.find('*/**') 43 | ``` 44 | 45 | After creating the project object, you can read the data from it: 46 | 47 | ```ruby 48 | data = proj.read 49 | p data.target('example').config('Release').build_settings 50 | ``` 51 | 52 | Or rewrite data: 53 | 54 | ```ruby 55 | proj.change do |data| 56 | data.target('example').config('Release').build_settings['GCC_VERSION'] = 'com.apple.compilers.llvmgcc42' 57 | end 58 | ``` 59 | 60 | Files, groups and directories 61 | --- 62 | Displaying all of top-level groups: 63 | 64 | ```ruby 65 | data.main_group.children.each do |child| 66 | p child.name 67 | end 68 | ``` 69 | 70 | Displaying files of specified group: 71 | 72 | ```ruby 73 | group = data.group('path/from/main_group') 74 | group.files.each do |file| 75 | p file.name 76 | end 77 | ``` 78 | 79 | You can get group's (or file's) group path (the path from the main group): 80 | 81 | ```ruby 82 | group.group_path 83 | ``` 84 | 85 | Directories are groups that are explicitly represented in the file system. For them, you can also get a file path: 86 | 87 | ```ruby 88 | group.total_path 89 | ``` 90 | 91 | You can add a group to project by specifying the path from the main group: 92 | 93 | ```ruby 94 | data.add_group('path/from/main_group') 95 | ``` 96 | 97 | Or from the current group: 98 | 99 | ```ruby 100 | group.add_group('path/from/current_group') 101 | ``` 102 | 103 | To add a directory to the project, you must specify the file path: 104 | 105 | ```ruby 106 | data.add_dir('group_path/to/parent', '/file_path/to/dir') 107 | group.add_dir('/file_path/to/dir') 108 | ``` 109 | 110 | Adding files are same: 111 | 112 | ```ruby 113 | data.add_file('group_path/to/parent', '/file_path/to/file') 114 | group.add_file('/file_path/to/file') 115 | ``` 116 | 117 | You can also remove files, groups and directories from the project: 118 | 119 | ```ruby 120 | data.remove_file('path/from/main_group') 121 | data.remove_group('path/from/main_group') 122 | 123 | group.remove_file('path/from/current_group') 124 | group.remove_group('path/from/current_group') 125 | ``` 126 | 127 | Targets 128 | --- 129 | Getting the target object is simple: 130 | 131 | ```ruby 132 | target = data.target('example') 133 | ``` 134 | 135 | After adding a file to the project, you can add it to target's build phase: 136 | 137 | ```ruby 138 | file = main_group.add_file('/file_path/to/file') 139 | target.add_source(file) 140 | ``` 141 | 142 | Or remove from target's build phase: 143 | 144 | ```ruby 145 | target.remove_source(file) 146 | ``` 147 | 148 | Building the project 149 | --- 150 | XcodeProject uses Rake and XcodeBuilder for building projects. 151 | 152 | You need to create a `rakefile`, a simple look like this: 153 | 154 | ```ruby 155 | require 'rubygems' 156 | require 'xcodeproject' 157 | 158 | proj = XcodeProject::Project.new('path/to/example.xcodeproj') 159 | XcodeProject::Tasks::BuildTask.new(proj) 160 | ``` 161 | 162 | You will now have access to a variety of tasks such as clean and build. A full list of tasks can be viewed by running `rake -T`: 163 | 164 | ```sh 165 | $ rake -T 166 | rake example:archive # Creates an archive build of the specified target(s). 167 | rake example:build # Builds the specified target(s). 168 | rake example:clean # Cleans the build using the same build settings. 169 | rake example:cleanbuild # Builds the specified target(s) from a clean slate. 170 | ``` 171 | 172 | Configuring your tasks: 173 | 174 | ```ruby 175 | XcodeProject::Tasks::BuildTask.new(proj) do |t| 176 | t.target = "libexample" 177 | t.configuration = "Release" 178 | end 179 | ``` 180 | 181 | You can find out more about XcodeBuilder [here][xcodebuilder]. 182 | 183 | License 184 | --- 185 | XcodeProject is provided under the terms of the [the MIT license][license] 186 | 187 | [xcodebuilder]:https://github.com/lukeredpath/xcodebuild-rb 188 | [license]:http://www.opensource.org/licenses/MIT 189 | [pledgie]:http://pledgie.com/campaigns/17599 190 | [pledgie_img]:http://www.pledgie.com/campaigns/17599.png?skin_name=chrome 191 | [travis]:http://travis-ci.org/manifest/xcodeproject 192 | [travis_img]:https://secure.travis-ci.org/manifest/xcodeproject.png 193 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | 3 | require 'rubygems' 4 | require 'bundler/gem_tasks' 5 | require 'rspec/core/rake_task' 6 | require 'yard' 7 | 8 | RSpec::Core::RakeTask.new 9 | YARD::Rake::YardocTask.new 10 | 11 | task :default => :build 12 | 13 | -------------------------------------------------------------------------------- /lib/xcodeproject.rb: -------------------------------------------------------------------------------- 1 | require 'xcodeproject/project' 2 | require "xcodeproject/version" 3 | 4 | -------------------------------------------------------------------------------- /lib/xcodeproject/build_phase_node.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # The MIT License 3 | # 4 | # Copyright (c) 2012-2014 Andrei Nesterov 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to 8 | # deal in the Software without restriction, including without limitation the 9 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | # sell copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | # IN THE SOFTWARE. 23 | #++ 24 | 25 | require 'xcodeproject/node' 26 | require 'xcodeproject/pbx_build_file' 27 | 28 | module XcodeProject 29 | class BuildPhaseNode < Node 30 | def initialize (root, uuid, data) 31 | super(root, uuid, data) 32 | @files = data['files'] 33 | end 34 | 35 | def files 36 | build_files.map {|obj| obj.file_ref } 37 | end 38 | 39 | def add_file (file_obj) 40 | case file_obj 41 | when PBXFileReference 42 | add_build_file(file_obj.uuid) 43 | when PBXBuildFile 44 | add_build_file_uuid(file_obj.uuid) 45 | else 46 | raise ArgumentError.new("Wrong argument type, expected the PBXFileReference or PBXBuildFile.") 47 | end 48 | end 49 | 50 | def remove_file (file_obj) 51 | case file_obj 52 | when PBXFileReference 53 | remove_build_file(file_obj.uuid) 54 | when PBXBuildFile 55 | remove_build_file_uuid(file_obj.uuid) 56 | else 57 | raise ArgumentError.new("Wrong argument type, expected the PBXFileReference or PBXBuildFile.") 58 | end 59 | end 60 | 61 | def doctor 62 | @files.each do |uuid| 63 | obj = root.object(uuid) 64 | if obj.nil? 65 | remove_build_file_uuid(uuid) 66 | puts "removed #{uuid}" 67 | end 68 | end 69 | end 70 | 71 | private 72 | 73 | def build_files 74 | @files.map {|uuid| root.object!(uuid) } 75 | end 76 | 77 | def build_file (file_ref_uuid) 78 | build_files.select {|obj| obj.file_ref.uuid == file_ref_uuid }.first 79 | end 80 | 81 | def add_build_file (file_ref_uuid) 82 | obj = build_file(file_ref_uuid) 83 | if obj.nil? 84 | obj = PBXBuildFile.add(root, file_ref_uuid) 85 | add_build_file_uuid(obj.uuid) 86 | end 87 | obj 88 | end 89 | 90 | def remove_build_file (file_ref_uuid) 91 | obj = build_file(file_ref_uuid) 92 | remove_build_file_uuid(obj.uuid) unless obj.nil? 93 | end 94 | 95 | def add_build_file_uuid (build_file_uuid) 96 | @files << build_file_uuid unless @files.include?(build_file_uuid) 97 | end 98 | 99 | def remove_build_file_uuid (build_file_uuid) 100 | @files.delete(build_file_uuid) 101 | end 102 | end 103 | 104 | class PBXSourcesBuildPhase < BuildPhaseNode; end 105 | class PBXHeadersBuildPhase < BuildPhaseNode; end 106 | class PBXResourcesBuildPhase < BuildPhaseNode; end 107 | class PBXFrameworksBuildPhase < BuildPhaseNode; end 108 | class PBXAppleScriptBuildPhase < BuildPhaseNode; end 109 | class PBXShellScriptBuildPhase < BuildPhaseNode; end 110 | class PBXCopyFilesBuildPhase < BuildPhaseNode; end 111 | end 112 | -------------------------------------------------------------------------------- /lib/xcodeproject/data.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # The MIT License 3 | # 4 | # Copyright (c) 2012-2014 Andrei Nesterov 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to 8 | # deal in the Software without restriction, including without limitation the 9 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | # sell copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | # IN THE SOFTWARE. 23 | #++ 24 | 25 | require 'xcodeproject/root_node' 26 | 27 | module XcodeProject 28 | class Data 29 | def initialize (data, wd) 30 | @root = RootNode.new(data, wd) 31 | end 32 | 33 | def project 34 | @root.project 35 | end 36 | 37 | def targets 38 | project.targets 39 | end 40 | 41 | def target (name) 42 | project.target(name) 43 | end 44 | 45 | def main_group 46 | project.main_group 47 | end 48 | 49 | def group (gpath) 50 | main_group.group(gpath) 51 | end 52 | 53 | def add_group (gpath) 54 | main_group.add_group(gpath) 55 | end 56 | 57 | def add_dir(parent_gpath, path) 58 | main_group.add_group(parent_gpath).add_dir(path) 59 | end 60 | 61 | def remove_group (gpath) 62 | main_group.remove_group(gpath) 63 | end 64 | 65 | def file (gpath) 66 | main_group.file(gpath) 67 | end 68 | 69 | def add_file (parent_gpath, path) 70 | main_group.add_group(parent_gpath).add_file(path) 71 | end 72 | 73 | def remove_file (gpath) 74 | main_group.remove_file(gpath) 75 | end 76 | 77 | def doctor 78 | targets.each {|target| target.doctor } 79 | end 80 | 81 | def to_plist (fmtr = Formatter.new) 82 | @root.to_plist 83 | end 84 | 85 | private 86 | attr_accessor :root 87 | 88 | end 89 | end 90 | -------------------------------------------------------------------------------- /lib/xcodeproject/exceptions.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # The MIT License 3 | # 4 | # Copyright (c) 2012-2014 Andrei Nesterov 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to 8 | # deal in the Software without restriction, including without limitation the 9 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | # sell copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | # IN THE SOFTWARE. 23 | #++ 24 | 25 | module XcodeProject 26 | class ParseError < StandardError; end 27 | class FilePathError < StandardError; end 28 | class GroupPathError < StandardError; end 29 | end 30 | -------------------------------------------------------------------------------- /lib/xcodeproject/extend/array.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # The MIT License 3 | # 4 | # Copyright (c) 2012-2014 Andrei Nesterov 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to 8 | # deal in the Software without restriction, including without limitation the 9 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | # sell copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | # IN THE SOFTWARE. 23 | #++ 24 | 25 | require 'xcodeproject/formatter' 26 | 27 | class Array 28 | def to_plist (fmtr = XcodeProject::Formatter.new) 29 | items = map { |item| "#{item.to_plist(fmtr)}" } 30 | %{(#{fmtr.t2}#{items.join(",#{fmtr.t2}")}#{fmtr.t1})} 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/xcodeproject/extend/hash.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # The MIT License 3 | # 4 | # Copyright (c) 2012-2014 Andrei Nesterov 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to 8 | # deal in the Software without restriction, including without limitation the 9 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | # sell copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | # IN THE SOFTWARE. 23 | #++ 24 | 25 | require 'xcodeproject/formatter' 26 | 27 | class Hash 28 | def to_plist (fmtr = XcodeProject::Formatter.new) 29 | fmtr.inc 30 | items = map { |key, value| "#{key.to_plist(fmtr)} = #{value.to_plist(fmtr)};" } 31 | fmtr.dec 32 | 33 | %{{#{fmtr.t2}#{items.join("#{fmtr.t2}")}#{fmtr.t1}}} 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/xcodeproject/extend/string.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # The MIT License 3 | # 4 | # Copyright (c) 2012-2014 Andrei Nesterov 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to 8 | # deal in the Software without restriction, including without limitation the 9 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | # sell copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | # IN THE SOFTWARE. 23 | #++ 24 | 25 | require 'json' 26 | 27 | class String 28 | def to_plist (fmtr = nil) 29 | to_json 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/xcodeproject/file_node.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # The MIT License 3 | # 4 | # Copyright (c) 2012-2014 Andrei Nesterov 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to 8 | # deal in the Software without restriction, including without limitation the 9 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | # sell copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | # IN THE SOFTWARE. 23 | #++ 24 | 25 | require 'xcodeproject/node' 26 | require 'pathname' 27 | 28 | module XcodeProject 29 | class FileNode < Node 30 | attr_reader :name 31 | attr_reader :path 32 | attr_reader :source_tree 33 | 34 | def initialize (root, uuid, data) 35 | super(root, uuid, data) 36 | 37 | @source_tree = data['sourceTree'] 38 | @name ||= data['name'] 39 | @path ||= data['path'] 40 | 41 | @name ||= File.basename(@path) unless @path.nil? 42 | end 43 | 44 | def source_tree 45 | SourceTreeMap[@source_tree] 46 | end 47 | 48 | def parent 49 | root.select_objects do |uuid, data| 50 | (data['children'].include?(self.uuid) if data['isa'] == 'PBXGroup') ? true : false 51 | end.first 52 | end 53 | 54 | def group_path 55 | obj = self 56 | res = '' 57 | begin 58 | pn = obj.name ? obj.name : '' 59 | res = Pathname.new(pn).join(res) 60 | end while obj = obj.parent; 61 | res.cleanpath 62 | end 63 | 64 | def total_path 65 | res = '' 66 | case source_tree 67 | when :source_root 68 | res = path 69 | when :group 70 | pn = path.nil? ? '' : path 71 | res = parent.total_path.join(pn) unless parent.nil? 72 | else 73 | raise ParseError.new("No such '#{source_tree}' source tree type.") 74 | end 75 | root.absolute_path(res) 76 | end 77 | 78 | private 79 | 80 | SourceTreeMap = { 81 | 'SOURCE_ROOT' => :source_root, 82 | '' => :group 83 | } 84 | 85 | end 86 | end 87 | 88 | -------------------------------------------------------------------------------- /lib/xcodeproject/formatter.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # The MIT License 3 | # 4 | # Copyright (c) 2012-2014 Andrei Nesterov 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to 8 | # deal in the Software without restriction, including without limitation the 9 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | # sell copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | # IN THE SOFTWARE. 23 | #++ 24 | 25 | module XcodeProject 26 | class Formatter 27 | def initialize 28 | @counter = 0 29 | end 30 | 31 | def inc 32 | @counter += 1 33 | end 34 | 35 | def dec 36 | @counter -= 1 37 | end 38 | 39 | def t1 40 | "\n" + "\t" * @counter 41 | end 42 | 43 | def t2 44 | "\n" + "\t" * (@counter + 1) 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /lib/xcodeproject/node.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # The MIT License 3 | # 4 | # Copyright (c) 2012-2014 Andrei Nesterov 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to 8 | # deal in the Software without restriction, including without limitation the 9 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | # sell copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | # IN THE SOFTWARE. 23 | #++ 24 | 25 | module XcodeProject 26 | class Node 27 | attr_reader :uuid 28 | attr_reader :isa 29 | 30 | def initialize (root, uuid, data) 31 | @root, @uuid, @data = root, uuid, data 32 | @isa = data['isa'] 33 | end 34 | 35 | protected 36 | attr_accessor :root 37 | attr_accessor :data 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/xcodeproject/pbx_build_file.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # The MIT License 3 | # 4 | # Copyright (c) 2012-2014 Andrei Nesterov 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to 8 | # deal in the Software without restriction, including without limitation the 9 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | # sell copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | # IN THE SOFTWARE. 23 | #++ 24 | 25 | require 'xcodeproject/node' 26 | 27 | module XcodeProject 28 | class PBXBuildFile < Node 29 | attr_reader :file_ref 30 | 31 | def initialize (root, uuid, data) 32 | super(root, uuid, data) 33 | 34 | @file_ref = data['fileRef'] 35 | end 36 | 37 | def file_ref 38 | root.object!(@file_ref) 39 | end 40 | 41 | def remove! 42 | root.project.targets.each {|target| target.remove_source(self) } 43 | root.remove_object(uuid) 44 | end 45 | 46 | def self.add(root, file_ref_uuid) 47 | uuid, data = root.add_object(self.create_object_hash(file_ref_uuid)) 48 | self.new(root, uuid, data) 49 | end 50 | 51 | private 52 | 53 | def self.create_object_hash (file_ref_uuid) 54 | data = [] 55 | data << ['isa', 'PBXBuildFile'] 56 | data << ['fileRef', file_ref_uuid] 57 | 58 | Hash[ data ] 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /lib/xcodeproject/pbx_file_reference.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # The MIT License 3 | # 4 | # Copyright (c) 2012-2014 Andrei Nesterov 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to 8 | # deal in the Software without restriction, including without limitation the 9 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | # sell copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | # IN THE SOFTWARE. 23 | #++ 24 | 25 | require 'xcodeproject/file_node' 26 | require 'xcodeproject/exceptions' 27 | 28 | module XcodeProject 29 | class PBXFileReference < FileNode 30 | def initialize (root, uuid, data) 31 | super(root, uuid, data) 32 | end 33 | 34 | def remove! 35 | root.build_files(uuid).each do |build_file_obj| 36 | build_file_obj.remove! 37 | end 38 | 39 | parent.remove_child_uuid(uuid) 40 | root.remove_object(uuid) 41 | end 42 | 43 | def self.add(root, path) 44 | uuid, data = root.add_object(self.create_object_hash(path)) 45 | self.new(root, uuid, data) 46 | end 47 | 48 | private 49 | 50 | def self.create_object_hash (path) 51 | path = path.to_s 52 | name = File.basename(path) 53 | ext = File.extname(path) 54 | raise ParseError.new("No such file type '#{name}'.") if !FileTypeMap.include?(ext) 55 | 56 | data = [] 57 | data << ['isa', 'PBXFileReference'] 58 | data << ['sourceTree', ''] 59 | # data << ['fileEncoding', '4'] # utf-8 60 | data << ['lastKnownFileType', FileTypeMap[ext]] 61 | data << ['path', path] 62 | data << ['name', name] if name != path 63 | 64 | Hash[ data ] 65 | end 66 | 67 | FileTypeMap = { 68 | '.h' => 'sourcecode.c.h', 69 | '.c' => 'sourcecode.c.c', 70 | '.m' => 'sourcecode.c.objc', 71 | '.mm' => 'sourcecode.cpp.objcpp', 72 | '.hpp' => 'sourcecode.cpp.h', 73 | '.cpp' => 'sourcecode.cpp.cpp', 74 | '.cc' => 'sourcecode.cpp.cpp', 75 | '.swift' => 'sourcecode.swift', 76 | '.mp3' => 'audio.mp3', 77 | '.png' => 'image.png', 78 | '.jpeg' => 'image.jpeg', 79 | '.jpg' => 'image.jpeg', 80 | '.fnt' => 'text', 81 | '.txt' => 'text' 82 | } 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /lib/xcodeproject/pbx_group.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # The MIT License 3 | # 4 | # Copyright (c) 2012-2014 Andrei Nesterov 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to 8 | # deal in the Software without restriction, including without limitation the 9 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | # sell copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | # IN THE SOFTWARE. 23 | #++ 24 | 25 | require 'xcodeproject/pbx_file_reference' 26 | require 'xcodeproject/exceptions' 27 | require 'pathname' 28 | 29 | module XcodeProject 30 | class PBXGroup < FileNode 31 | def initialize (root, uuid, data) 32 | super(root, uuid, data) 33 | @children = data['children'] 34 | end 35 | 36 | def children 37 | @children.map {|uuid| root.object!(uuid)} 38 | end 39 | 40 | def groups 41 | children.select {|child| child.is_a?(PBXGroup) } 42 | end 43 | 44 | def files 45 | children.select {|child| child.is_a?(PBXFileReference) } 46 | end 47 | 48 | def child (gpath) 49 | gpath = Pathname.new(gpath).cleanpath 50 | 51 | if gpath == gpath.basename 52 | name = gpath.to_s 53 | case name 54 | when '.' 55 | self 56 | when '..' 57 | parent 58 | else 59 | chs = children.select {|obj| obj.name == name } 60 | raise GroupPathError.new("The group contains two children with the same name.") if chs.size > 1 61 | chs.first 62 | end 63 | else 64 | pn, name = gpath.split 65 | group = child(pn) 66 | group.child(name) if group.is_a?(PBXGroup) 67 | end 68 | end 69 | 70 | def file_ref (gpath) 71 | obj = child(gpath) 72 | obj.is_a?(PBXFileReference) ? obj : nil 73 | end 74 | 75 | def group? 76 | data['path'].nil? 77 | end 78 | 79 | def dir? 80 | not group? 81 | end 82 | 83 | def group (gpath) 84 | obj = child(gpath) 85 | obj.is_a?(PBXGroup) ? obj : nil 86 | end 87 | 88 | def add_group (gpath) 89 | create_group(gpath) 90 | end 91 | 92 | def add_dir (path, gpath = nil) 93 | path = absolute_path(path) 94 | raise FilePathError.new("No such directory '#{path}'.") unless path.exist? 95 | 96 | gpath ||= path.basename 97 | parent = create_group(gpath, path) 98 | 99 | chs = path.entries.select {|obj| obj.to_s =~ /\A\./ ? false : true } 100 | chs.each do |pn| 101 | parent.absolute_path(pn).directory? ? parent.add_dir(pn) : parent.add_file(pn) 102 | end 103 | parent 104 | end 105 | 106 | def create_group (gpath, path = nil) 107 | gpath = Pathname.new(gpath).cleanpath 108 | 109 | if gpath == gpath.basename 110 | name = gpath.to_s 111 | case name 112 | when '.' 113 | self 114 | when '..' 115 | parent 116 | else 117 | obj = group(name) 118 | if obj.nil? 119 | path = relative_path(path) unless path.nil? 120 | obj = PBXGroup.add(root, name, path) 121 | add_child_uuid(obj.uuid) 122 | end 123 | obj 124 | end 125 | else 126 | pn, name = gpath.split 127 | create_group(pn).create_group(name) 128 | end 129 | end 130 | 131 | def add_child_uuid (uuid) 132 | @children << uuid 133 | end 134 | 135 | def remove_child_uuid (uuid) 136 | @children.delete(uuid) 137 | end 138 | 139 | def absolute_path (path) 140 | path = Pathname.new(path) 141 | path.absolute? ? path : root.absolute_path(total_path.join(path)) 142 | end 143 | 144 | def relative_path (path) 145 | path = Pathname.new(path) 146 | path.relative? ? path : path.relative_path_from(total_path) 147 | end 148 | 149 | def remove_file_ref (gpath) 150 | obj = file(gpath) 151 | obj.remove! unless obj.nil? 152 | end 153 | 154 | def remove_group (gpath) 155 | obj = group(gpath) 156 | obj.remove! unless obj.nil? 157 | end 158 | 159 | def remove! 160 | return if parent.nil? 161 | 162 | children.each do |obj| 163 | obj.remove! 164 | end 165 | 166 | parent.remove_child_uuid(uuid) 167 | root.remove_object(uuid) 168 | end 169 | 170 | def add_file_ref (path) 171 | raise FilePathError.new("No such file '#{absolute_path(path)}'.") unless absolute_path(path).exist? 172 | 173 | name = File.basename(path) 174 | obj = file_ref(name) 175 | if obj.nil? 176 | obj = PBXFileReference.add(root, relative_path(path)) 177 | add_child_uuid(obj.uuid) 178 | end 179 | obj 180 | end 181 | 182 | def self.add(root, name, path = nil) 183 | uuid, data = root.add_object(self.create_object_hash(name, path)) 184 | PBXGroup.new(root, uuid, data) 185 | end 186 | 187 | alias :file :file_ref 188 | alias :add_file :add_file_ref 189 | alias :remove_file :remove_file_ref 190 | 191 | private 192 | 193 | def self.create_object_hash (name, path = nil) 194 | path = path.to_s 195 | 196 | data = [] 197 | data << ['isa', 'PBXGroup'] 198 | data << ['children', []] 199 | data << ['name', name] 200 | data << ['path', path] unless path.empty? 201 | data << ['sourceTree', ''] 202 | 203 | Hash[ data ] 204 | end 205 | end 206 | end 207 | 208 | -------------------------------------------------------------------------------- /lib/xcodeproject/pbx_native_target.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # The MIT License 3 | # 4 | # Copyright (c) 2012-2014 Andrei Nesterov 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to 8 | # deal in the Software without restriction, including without limitation the 9 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | # sell copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | # IN THE SOFTWARE. 23 | #++ 24 | 25 | require 'xcodeproject/xc_configuration_list' 26 | require 'xcodeproject/build_phase_node' 27 | 28 | module XcodeProject 29 | class PBXNativeTarget < Node 30 | attr_reader :name 31 | attr_reader :product_name 32 | attr_reader :product_reference 33 | attr_reader :product_type 34 | attr_reader :dependencies 35 | 36 | def initialize (root, uuid, data) 37 | super(root, uuid, data) 38 | 39 | @name = data['name'] 40 | @product_name = data['productName'] 41 | @product_reference = data['productReference'] 42 | @product_type = data['productType'] 43 | @dependencies = data['dependencies'] 44 | end 45 | 46 | def sources 47 | sources_build_phase.files 48 | end 49 | 50 | def add_source (file) 51 | sources_build_phase.add_file(file) 52 | end 53 | 54 | def remove_source (file) 55 | sources_build_phase.remove_file(file) 56 | end 57 | 58 | def configs 59 | build_configurations_list.build_configurations 60 | end 61 | 62 | def config (name) 63 | build_configurations_list.build_configuration(name) 64 | end 65 | 66 | def build_configurations_list 67 | root.object!(data['buildConfigurationList']) 68 | end 69 | 70 | def build_phases 71 | data['buildPhases'].map {|uuid| root.object!(uuid) } 72 | end 73 | 74 | def sources_build_phase 75 | build_phases.select {|obj| obj.is_a?(PBXSourcesBuildPhase) }.first 76 | end 77 | 78 | def headers_build_phase 79 | build_phases.select {|obj| obj.is_a?(PBXHeadersBuildPhase) }.first 80 | end 81 | 82 | def resources_build_phase 83 | build_phases.select {|obj| obj.is_a?(PBXResourcesBuildPhase) }.first 84 | end 85 | 86 | def frameworks_build_phase 87 | build_phases.select {|obj| obj.is_a?(PBXFrameworksBuildPhase) }.first 88 | end 89 | 90 | def doctor 91 | build_phases.each {|phase| phase.doctor } 92 | end 93 | end 94 | end 95 | -------------------------------------------------------------------------------- /lib/xcodeproject/pbx_project.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # The MIT License 3 | # 4 | # Copyright (c) 2012-2014 Andrei Nesterov 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to 8 | # deal in the Software without restriction, including without limitation the 9 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | # sell copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | # IN THE SOFTWARE. 23 | #++ 24 | 25 | require 'xcodeproject/node' 26 | 27 | module XcodeProject 28 | class PBXProject < Node 29 | attr_reader :main_group 30 | attr_reader :product_ref_group 31 | attr_reader :project_dir_path 32 | attr_reader :compatibility_version 33 | attr_reader :development_region 34 | attr_reader :know_regions 35 | attr_reader :attributes 36 | 37 | def initialize (root, uuid, data) 38 | super(root, uuid, data) 39 | 40 | @main_group = root.object!(data['mainGroup']) 41 | @product_ref_group = root.object!(data['productRefGroup']) 42 | @project_dir_path = data['projectDirPath'] 43 | @compatibility_version = data['compatibilityVersion'] 44 | @development_region = data['developmentRegion'] 45 | @know_regions = data['knownRegions'] 46 | @attributes = data['attributes'] 47 | end 48 | 49 | def targets 50 | data['targets'].map {|uuid| root.object!(uuid)} 51 | end 52 | 53 | def target (name) 54 | root.find_object('PBXNativeTarget', {'name' => name}) 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /lib/xcodeproject/project.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # The MIT License 3 | # 4 | # Copyright (c) 2012-2014 Andrei Nesterov 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to 8 | # deal in the Software without restriction, including without limitation the 9 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | # sell copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | # IN THE SOFTWARE. 23 | #++ 24 | 25 | require 'xcodeproject/tasks/build_task' 26 | require 'xcodeproject/data' 27 | require 'pathname' 28 | require 'find' 29 | 30 | module XcodeProject 31 | class Project 32 | attr_reader :bundle_path 33 | attr_reader :file_path 34 | attr_reader :name 35 | 36 | def self.find (pattern = nil) 37 | pattern = Pathname.new(pattern.to_s) 38 | pattern = pattern.join('*.xcodeproj') if pattern.extname != '.xcodeproj' 39 | 40 | Dir[ pattern ].map {|path| self.new(path) } 41 | end 42 | 43 | def initialize (path) 44 | path = Pathname.new(path) 45 | raise FilePathError.new("No such project file '#{path}'.") unless path.exist? 46 | 47 | @bundle_path = path 48 | @file_path = bundle_path.join('project.pbxproj') 49 | @name = bundle_path.basename('.*').to_s 50 | end 51 | 52 | def change 53 | data = read 54 | yield data 55 | write data 56 | end 57 | 58 | def read 59 | Data.new(JSON.parse(`plutil -convert json -o - "#{file_path}"`), bundle_path.dirname) 60 | end 61 | 62 | def write (data) 63 | File.open(file_path, "w") do |file| 64 | file.write(data.to_plist) 65 | end 66 | end 67 | 68 | def doctor 69 | change {|data| data.doctor } 70 | end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /lib/xcodeproject/root_node.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # The MIT License 3 | # 4 | # Copyright (c) 2012-2014 Andrei Nesterov 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to 8 | # deal in the Software without restriction, including without limitation the 9 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | # sell copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | # IN THE SOFTWARE. 23 | #++ 24 | 25 | require 'xcodeproject/xc_build_configuration' 26 | require 'xcodeproject/pbx_native_target' 27 | require 'xcodeproject/pbx_project' 28 | require 'xcodeproject/pbx_group' 29 | require 'xcodeproject/build_phase_node' 30 | require 'xcodeproject/uuid_generator' 31 | require 'xcodeproject/exceptions' 32 | require 'xcodeproject/extend/string' 33 | require 'xcodeproject/extend/array' 34 | require 'xcodeproject/extend/hash' 35 | 36 | module XcodeProject 37 | class RootNode 38 | def initialize (data, wd) 39 | @data = data 40 | 41 | @wd = Pathname.new(wd) 42 | @wd = Pathname.new(ENV['PWD']).join(@wd) if @wd.relative? 43 | 44 | @objects = data['objects'] 45 | @uuid_generator = UUIDGenerator.new 46 | end 47 | 48 | def project 49 | find_object!('PBXProject') 50 | end 51 | 52 | def build_files (file_ref_uuid) 53 | find_objects('PBXBuildFile', {'fileRef' => file_ref_uuid}) 54 | end 55 | 56 | def object (uuid) 57 | data = @objects[uuid] 58 | XcodeProject.const_get(data['isa']).new(self, uuid, data) unless data.nil? 59 | end 60 | 61 | def object! (uuid) 62 | obj = object(uuid) 63 | raise ParseError.new("Object with uuid = #{uuid} not found.") if obj.nil?; obj 64 | end 65 | 66 | def select_objects 67 | objs = @objects.select {|uuid, data| yield uuid, data } 68 | objs.map {|uuid, data| object(uuid) } 69 | end 70 | 71 | def find_objects (isa, hash = Hash.new) 72 | hash.merge!(Hash[ 'isa', isa ]) 73 | select_objects {|uuid, data| data.values_at(*hash.keys) == hash.values } 74 | end 75 | 76 | def find_objects! (isa, hash = Hash.new) 77 | objs = find_objects(isa, hash) 78 | raise ParseError.new("Object with isa = #{isa} and #{hash} not found.") if objs.empty?; objs 79 | end 80 | 81 | def find_object (isa, hash = Hash.new) 82 | find_objects(isa, hash).first 83 | end 84 | 85 | def find_object! (isa, hash = Hash.new) 86 | obj = find_object(isa, hash) 87 | raise ParseError if obj.nil?; obj 88 | end 89 | 90 | def find_object2 (isa, h1 = Hash.new, h2 = Hash.new) 91 | obj = find_object(isa, h1) 92 | obj.nil? ? find_object(isa, h2) : obj 93 | end 94 | 95 | def find_object2! (isa, h1 = Hash.new, h2 = Hash.new) 96 | obj = find_object2(isa, h1, h2) 97 | raise ParseError if obj.nil?; obj 98 | end 99 | 100 | def add_object (data) 101 | @objects.merge!(Hash[ uuid = generate_object_uuid, data ]); [uuid, data] 102 | end 103 | 104 | def remove_object (uuid) 105 | @objects.delete(uuid) 106 | end 107 | 108 | def absolute_path (path) 109 | path = Pathname.new(path) 110 | path = path.absolute? ? path : @wd.join(path) 111 | path.cleanpath 112 | end 113 | 114 | def generate_object_uuid 115 | @uuid_generator.generate 116 | end 117 | 118 | def to_plist (fmtr = Formatter.new) 119 | @data.to_plist 120 | end 121 | end 122 | end 123 | -------------------------------------------------------------------------------- /lib/xcodeproject/tasks/build_task.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # The MIT License 3 | # 4 | # Copyright (c) 2012-2014 Andrei Nesterov 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to 8 | # deal in the Software without restriction, including without limitation the 9 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | # sell copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | # IN THE SOFTWARE. 23 | #++ 24 | 25 | require 'xcodebuild' 26 | 27 | module XcodeProject 28 | module Tasks 29 | class BuildTask < XcodeBuild::Tasks::BuildTask 30 | attr_accessor :with_build_opts 31 | attr_accessor :build_to 32 | 33 | def initialize (project, namespace = nil, &block) 34 | namespace ||= project.name 35 | super(namespace, &block) 36 | 37 | @project_name = project.bundle_path.basename.to_s 38 | @invoke_from_within = project.bundle_path.dirname 39 | 40 | @formatter ||= XcodeBuild::Formatters::ProgressFormatter.new 41 | @with_build_opts ||= [] 42 | 43 | unless @build_to.nil? 44 | build_tmp_to = Pathname.new(@build_to).join('.tmp') 45 | 46 | @with_build_opts << %{ CONFIGURATION_BUILD_DIR="#{@build_to}" } 47 | @with_build_opts << %{ CONFIGURATION_TEMP_DIR="#{build_tmp_to}" } 48 | @with_build_opts << %{ SYMROOT="#{build_tmp_to}" } 49 | end 50 | end 51 | 52 | private 53 | 54 | def build_opts 55 | super + with_build_opts 56 | end 57 | end 58 | end 59 | end 60 | 61 | -------------------------------------------------------------------------------- /lib/xcodeproject/uuid_generator.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # The MIT License 3 | # 4 | # Copyright (c) 2012-2014 Andrei Nesterov 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to 8 | # deal in the Software without restriction, including without limitation the 9 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | # sell copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | # IN THE SOFTWARE. 23 | #++ 24 | 25 | require 'uuid' 26 | 27 | module XcodeProject 28 | class UUIDGenerator 29 | def initialize 30 | @generator = UUID.new 31 | end 32 | 33 | def generate 34 | @generator.generate(:compact).slice(0..23).upcase 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/xcodeproject/version.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # The MIT License 3 | # 4 | # Copyright (c) 2012-2014 Andrei Nesterov 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to 8 | # deal in the Software without restriction, including without limitation the 9 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | # sell copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | # IN THE SOFTWARE. 23 | #++ 24 | 25 | module XcodeProject 26 | VERSION = "0.3.13" 27 | end 28 | -------------------------------------------------------------------------------- /lib/xcodeproject/xc_build_configuration.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # The MIT License 3 | # 4 | # Copyright (c) 2012-2014 Andrei Nesterov 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to 8 | # deal in the Software without restriction, including without limitation the 9 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | # sell copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | # IN THE SOFTWARE. 23 | #++ 24 | 25 | require 'xcodeproject/node' 26 | require 'rexml/document' 27 | 28 | module XcodeProject 29 | class XCBuildConfiguration < Node 30 | attr_reader :name 31 | attr_accessor :build_settings 32 | 33 | def initialize (root, uuid, data) 34 | super(root, uuid, data) 35 | 36 | @name = data['name'] 37 | @build_settings = data['buildSettings'] 38 | end 39 | 40 | def version (type = :major) 41 | major = read_property('CFBundleShortVersionString') 42 | minor = read_property('CFBundleVersion') 43 | 44 | case type 45 | when :major ; major 46 | when :minor ; minor 47 | when :both ; major + '.' + minor 48 | else raise ArgumentError.new('Wrong argument type, expected :major, :minor or :both.') 49 | end 50 | end 51 | 52 | def change_version (value, type = :major) 53 | case type 54 | when :major ; write_property('CFBundleShortVersionString', value) 55 | when :minor ; write_property('CFBundleVersion', value) 56 | else raise ArgumentError.new('Wrong argument type, expected :major or :minor.') 57 | end 58 | end 59 | 60 | def increment_version (type = :major) 61 | cv = version(type) 62 | nv = cv.nil? ? '0' : cv.next 63 | 64 | change_version(nv, type) 65 | end 66 | 67 | private 68 | 69 | def plist_path 70 | root.absolute_path(build_settings['INFOPLIST_FILE']) 71 | end 72 | 73 | def read_property (key) 74 | file = File.new(plist_path) 75 | doc = REXML::Document.new(file) 76 | 77 | doc.elements.each("plist/dict/key") do |e| 78 | return e.next_element.text if e.text == key 79 | end 80 | nil 81 | end 82 | 83 | def write_property (key, value) 84 | file = File.new(plist_path) 85 | doc = REXML::Document.new(file) 86 | 87 | doc.elements.each("plist/dict/key") do |e| 88 | e.next_element.text = value if e.text == key 89 | end 90 | 91 | formatter = REXML::Formatters::Default.new 92 | File.open(plist_path, 'w') do |data| 93 | formatter.write(doc, data) 94 | end 95 | nil 96 | end 97 | 98 | end 99 | end 100 | -------------------------------------------------------------------------------- /lib/xcodeproject/xc_configuration_list.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # The MIT License 3 | # 4 | # Copyright (c) 2012-2014 Andrei Nesterov 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to 8 | # deal in the Software without restriction, including without limitation the 9 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | # sell copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | # IN THE SOFTWARE. 23 | #++ 24 | 25 | require 'xcodeproject/node' 26 | 27 | module XcodeProject 28 | class XCConfigurationList < Node 29 | attr_reader :default_configuration_name 30 | attr_reader :default_configuration_is_visible 31 | 32 | def initialize (root, uuid, data) 33 | super(root, uuid, data) 34 | 35 | @default_configuration_name = data['defaultConfigurationName'] 36 | @default_configuration_is_visible = data['defaultConfigurationIsVisible'] 37 | end 38 | 39 | def build_configuration (name) 40 | build_configurations.select {|obj| obj.name == name }.first 41 | end 42 | 43 | def build_configurations 44 | data['buildConfigurations'].map {|uuid| root.object!(uuid) } 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /resources/example/dir1a/dir2a/dir3a/dir4a/file5a-a.h: -------------------------------------------------------------------------------- 1 | // file4a-a.h -------------------------------------------------------------------------------- /resources/example/dir1a/dir2a/dir3a/dir4a/file5a-a.m: -------------------------------------------------------------------------------- 1 | // file4a-a.m -------------------------------------------------------------------------------- /resources/example/dir1a/dir2a/dir3a/dir4a/file5a-r.h: -------------------------------------------------------------------------------- 1 | // file4a-r.h -------------------------------------------------------------------------------- /resources/example/dir1a/dir2a/dir3a/dir4a/file5a-r.m: -------------------------------------------------------------------------------- 1 | // file4a-r.m -------------------------------------------------------------------------------- /resources/example/dir1b/dir2b/file3b.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manifest/xcodeproject/752d9d157268ba76e2f038206f014e18d14d9b4a/resources/example/dir1b/dir2b/file3b.m -------------------------------------------------------------------------------- /resources/example/dir1b/file2b.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manifest/xcodeproject/752d9d157268ba76e2f038206f014e18d14d9b4a/resources/example/dir1b/file2b.m -------------------------------------------------------------------------------- /resources/example/dir1c/dir2c/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manifest/xcodeproject/752d9d157268ba76e2f038206f014e18d14d9b4a/resources/example/dir1c/dir2c/.keep -------------------------------------------------------------------------------- /resources/example/dir1c/file2c.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manifest/xcodeproject/752d9d157268ba76e2f038206f014e18d14d9b4a/resources/example/dir1c/file2c.h -------------------------------------------------------------------------------- /resources/example/dir1c/file2c.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manifest/xcodeproject/752d9d157268ba76e2f038206f014e18d14d9b4a/resources/example/dir1c/file2c.m -------------------------------------------------------------------------------- /resources/example/example.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 78BBAAF4157A984700D8AC61 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 78BBAAF3157A984700D8AC61 /* UIKit.framework */; }; 11 | 78BBAAF6157A984700D8AC61 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 78BBAAF5157A984700D8AC61 /* Foundation.framework */; }; 12 | 78BBAAF8157A984700D8AC61 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 78BBAAF7157A984700D8AC61 /* CoreGraphics.framework */; }; 13 | 78BBAAFE157A984700D8AC61 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 78BBAAFC157A984700D8AC61 /* InfoPlist.strings */; }; 14 | 78BBAB00157A984700D8AC61 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 78BBAAFF157A984700D8AC61 /* main.m */; }; 15 | 78BBAB04157A984700D8AC61 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 78BBAB03157A984700D8AC61 /* AppDelegate.m */; }; 16 | 78BBAB16157A997800D8AC61 /* file2c.m in Sources */ = {isa = PBXBuildFile; fileRef = 78BBAB14157A997800D8AC61 /* file2c.m */; }; 17 | 78C3658C1584A4ED004077AB /* file5a-a.m in Sources */ = {isa = PBXBuildFile; fileRef = 78C3658B1584A4ED004077AB /* file5a-a.m */; }; 18 | 78C3658F1584A50A004077AB /* file5a-r.m in Sources */ = {isa = PBXBuildFile; fileRef = 78C3658E1584A50A004077AB /* file5a-r.m */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXFileReference section */ 22 | 7891BAE01580EF8B0043E606 /* file2c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = file2c.h; path = dir1c/file2c.h; sourceTree = ""; }; 23 | 78BBAAEF157A984700D8AC61 /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 24 | 78BBAAF3157A984700D8AC61 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 25 | 78BBAAF5157A984700D8AC61 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 26 | 78BBAAF7157A984700D8AC61 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 27 | 78BBAAFB157A984700D8AC61 /* example-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "example-Info.plist"; sourceTree = ""; }; 28 | 78BBAAFD157A984700D8AC61 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 29 | 78BBAAFF157A984700D8AC61 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 30 | 78BBAB01157A984700D8AC61 /* example-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "example-Prefix.pch"; sourceTree = ""; }; 31 | 78BBAB02157A984700D8AC61 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 32 | 78BBAB03157A984700D8AC61 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 33 | 78BBAB14157A997800D8AC61 /* file2c.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = file2c.m; path = dir1c/file2c.m; sourceTree = ""; }; 34 | 78C365891584A4E6004077AB /* file5a-a.h */ = {isa = PBXFileReference; fileEncoding = 4; name = "file5a-a.h"; path = "dir1a/dir2a/dir3a/dir4a/file5a-a.h"; sourceTree = SOURCE_ROOT; }; 35 | 78C3658B1584A4ED004077AB /* file5a-a.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "file5a-a.m"; path = "dir1a/dir2a/dir3a/dir4a/file5a-a.m"; sourceTree = SOURCE_ROOT; }; 36 | 78C3658D1584A4FC004077AB /* file5a-r.h */ = {isa = PBXFileReference; fileEncoding = 4; path = "file5a-r.h"; sourceTree = ""; }; 37 | 78C3658E1584A50A004077AB /* file5a-r.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "file5a-r.m"; sourceTree = ""; }; 38 | /* End PBXFileReference section */ 39 | 40 | /* Begin PBXFrameworksBuildPhase section */ 41 | 78BBAAEC157A984700D8AC61 /* Frameworks */ = { 42 | isa = PBXFrameworksBuildPhase; 43 | buildActionMask = 2147483647; 44 | files = ( 45 | 78BBAAF4157A984700D8AC61 /* UIKit.framework in Frameworks */, 46 | 78BBAAF6157A984700D8AC61 /* Foundation.framework in Frameworks */, 47 | 78BBAAF8157A984700D8AC61 /* CoreGraphics.framework in Frameworks */, 48 | ); 49 | runOnlyForDeploymentPostprocessing = 0; 50 | }; 51 | /* End PBXFrameworksBuildPhase section */ 52 | 53 | /* Begin PBXGroup section */ 54 | 78BBAAE4157A984700D8AC61 = { 55 | isa = PBXGroup; 56 | children = ( 57 | 78BBAB0A157A984B00D8AC61 /* group1a */, 58 | 78BBAAF9157A984700D8AC61 /* example */, 59 | 78BBAAF2157A984700D8AC61 /* Frameworks */, 60 | 78BBAAF0157A984700D8AC61 /* Products */, 61 | ); 62 | sourceTree = ""; 63 | }; 64 | 78BBAAF0157A984700D8AC61 /* Products */ = { 65 | isa = PBXGroup; 66 | children = ( 67 | 78BBAAEF157A984700D8AC61 /* example.app */, 68 | ); 69 | name = Products; 70 | sourceTree = ""; 71 | }; 72 | 78BBAAF2157A984700D8AC61 /* Frameworks */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | 78BBAAF3157A984700D8AC61 /* UIKit.framework */, 76 | 78BBAAF5157A984700D8AC61 /* Foundation.framework */, 77 | 78BBAAF7157A984700D8AC61 /* CoreGraphics.framework */, 78 | ); 79 | name = Frameworks; 80 | sourceTree = ""; 81 | }; 82 | 78BBAAF9157A984700D8AC61 /* example */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | 78BBAB02157A984700D8AC61 /* AppDelegate.h */, 86 | 78BBAB03157A984700D8AC61 /* AppDelegate.m */, 87 | 78BBAAFA157A984700D8AC61 /* Supporting Files */, 88 | ); 89 | path = example; 90 | sourceTree = ""; 91 | }; 92 | 78BBAAFA157A984700D8AC61 /* Supporting Files */ = { 93 | isa = PBXGroup; 94 | children = ( 95 | 78BBAAFB157A984700D8AC61 /* example-Info.plist */, 96 | 78BBAAFC157A984700D8AC61 /* InfoPlist.strings */, 97 | 78BBAAFF157A984700D8AC61 /* main.m */, 98 | 78BBAB01157A984700D8AC61 /* example-Prefix.pch */, 99 | ); 100 | name = "Supporting Files"; 101 | sourceTree = ""; 102 | }; 103 | 78BBAB0A157A984B00D8AC61 /* group1a */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | 78BBAB0B157A985300D8AC61 /* group2a */, 107 | 78BBAB12157A997800D8AC61 /* dir2c */, 108 | 78BBAB14157A997800D8AC61 /* file2c.m */, 109 | 7891BAE01580EF8B0043E606 /* file2c.h */, 110 | ); 111 | name = group1a; 112 | sourceTree = ""; 113 | }; 114 | 78BBAB0B157A985300D8AC61 /* group2a */ = { 115 | isa = PBXGroup; 116 | children = ( 117 | ); 118 | name = group2a; 119 | sourceTree = ""; 120 | }; 121 | 78BBAB12157A997800D8AC61 /* dir2c */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | 78C365821584A3B4004077AB /* dir3a */, 125 | ); 126 | name = dir2c; 127 | path = dir1c/dir2c; 128 | sourceTree = ""; 129 | }; 130 | 78C365821584A3B4004077AB /* dir3a */ = { 131 | isa = PBXGroup; 132 | children = ( 133 | 78C365831584A3B4004077AB /* dir4a */, 134 | ); 135 | name = dir3a; 136 | path = dir1a/dir2a/dir3a; 137 | sourceTree = SOURCE_ROOT; 138 | }; 139 | 78C365831584A3B4004077AB /* dir4a */ = { 140 | isa = PBXGroup; 141 | children = ( 142 | 78C3658B1584A4ED004077AB /* file5a-a.m */, 143 | 78C365891584A4E6004077AB /* file5a-a.h */, 144 | 78C3658D1584A4FC004077AB /* file5a-r.h */, 145 | 78C3658E1584A50A004077AB /* file5a-r.m */, 146 | ); 147 | path = dir4a; 148 | sourceTree = ""; 149 | }; 150 | /* End PBXGroup section */ 151 | 152 | /* Begin PBXNativeTarget section */ 153 | 78BBAAEE157A984700D8AC61 /* example */ = { 154 | isa = PBXNativeTarget; 155 | buildConfigurationList = 78BBAB07157A984700D8AC61 /* Build configuration list for PBXNativeTarget "example" */; 156 | buildPhases = ( 157 | 78BBAAEB157A984700D8AC61 /* Sources */, 158 | 78BBAAEC157A984700D8AC61 /* Frameworks */, 159 | 78BBAAED157A984700D8AC61 /* Resources */, 160 | ); 161 | buildRules = ( 162 | ); 163 | dependencies = ( 164 | ); 165 | name = example; 166 | productName = example; 167 | productReference = 78BBAAEF157A984700D8AC61 /* example.app */; 168 | productType = "com.apple.product-type.application"; 169 | }; 170 | /* End PBXNativeTarget section */ 171 | 172 | /* Begin PBXProject section */ 173 | 78BBAAE6157A984700D8AC61 /* Project object */ = { 174 | isa = PBXProject; 175 | attributes = { 176 | LastUpgradeCheck = 0430; 177 | }; 178 | buildConfigurationList = 78BBAAE9157A984700D8AC61 /* Build configuration list for PBXProject "example" */; 179 | compatibilityVersion = "Xcode 3.2"; 180 | developmentRegion = English; 181 | hasScannedForEncodings = 0; 182 | knownRegions = ( 183 | en, 184 | ); 185 | mainGroup = 78BBAAE4157A984700D8AC61; 186 | productRefGroup = 78BBAAF0157A984700D8AC61 /* Products */; 187 | projectDirPath = ""; 188 | projectRoot = ""; 189 | targets = ( 190 | 78BBAAEE157A984700D8AC61 /* example */, 191 | ); 192 | }; 193 | /* End PBXProject section */ 194 | 195 | /* Begin PBXResourcesBuildPhase section */ 196 | 78BBAAED157A984700D8AC61 /* Resources */ = { 197 | isa = PBXResourcesBuildPhase; 198 | buildActionMask = 2147483647; 199 | files = ( 200 | 78BBAAFE157A984700D8AC61 /* InfoPlist.strings in Resources */, 201 | ); 202 | runOnlyForDeploymentPostprocessing = 0; 203 | }; 204 | /* End PBXResourcesBuildPhase section */ 205 | 206 | /* Begin PBXSourcesBuildPhase section */ 207 | 78BBAAEB157A984700D8AC61 /* Sources */ = { 208 | isa = PBXSourcesBuildPhase; 209 | buildActionMask = 2147483647; 210 | files = ( 211 | 78BBAB00157A984700D8AC61 /* main.m in Sources */, 212 | 78BBAB04157A984700D8AC61 /* AppDelegate.m in Sources */, 213 | 78BBAB16157A997800D8AC61 /* file2c.m in Sources */, 214 | 78C3658C1584A4ED004077AB /* file5a-a.m in Sources */, 215 | 78C3658F1584A50A004077AB /* file5a-r.m in Sources */, 216 | ); 217 | runOnlyForDeploymentPostprocessing = 0; 218 | }; 219 | /* End PBXSourcesBuildPhase section */ 220 | 221 | /* Begin PBXVariantGroup section */ 222 | 78BBAAFC157A984700D8AC61 /* InfoPlist.strings */ = { 223 | isa = PBXVariantGroup; 224 | children = ( 225 | 78BBAAFD157A984700D8AC61 /* en */, 226 | ); 227 | name = InfoPlist.strings; 228 | sourceTree = ""; 229 | }; 230 | /* End PBXVariantGroup section */ 231 | 232 | /* Begin XCBuildConfiguration section */ 233 | 78BBAB05157A984700D8AC61 /* Debug */ = { 234 | isa = XCBuildConfiguration; 235 | buildSettings = { 236 | ALWAYS_SEARCH_USER_PATHS = NO; 237 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 238 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 239 | COPY_PHASE_STRIP = NO; 240 | GCC_C_LANGUAGE_STANDARD = gnu99; 241 | GCC_DYNAMIC_NO_PIC = NO; 242 | GCC_OPTIMIZATION_LEVEL = 0; 243 | GCC_PREPROCESSOR_DEFINITIONS = ( 244 | "DEBUG=1", 245 | "$(inherited)", 246 | ); 247 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 248 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 249 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 250 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 251 | GCC_WARN_UNUSED_VARIABLE = YES; 252 | IPHONEOS_DEPLOYMENT_TARGET = 5.1; 253 | SDKROOT = iphoneos; 254 | TARGETED_DEVICE_FAMILY = "1,2"; 255 | }; 256 | name = Debug; 257 | }; 258 | 78BBAB06157A984700D8AC61 /* Release */ = { 259 | isa = XCBuildConfiguration; 260 | buildSettings = { 261 | ALWAYS_SEARCH_USER_PATHS = NO; 262 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 263 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 264 | COPY_PHASE_STRIP = YES; 265 | GCC_C_LANGUAGE_STANDARD = gnu99; 266 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 267 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 268 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 269 | GCC_WARN_UNUSED_VARIABLE = YES; 270 | IPHONEOS_DEPLOYMENT_TARGET = 5.1; 271 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 272 | SDKROOT = iphoneos; 273 | TARGETED_DEVICE_FAMILY = "1,2"; 274 | VALIDATE_PRODUCT = YES; 275 | }; 276 | name = Release; 277 | }; 278 | 78BBAB08157A984700D8AC61 /* Debug */ = { 279 | isa = XCBuildConfiguration; 280 | buildSettings = { 281 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 282 | GCC_PREFIX_HEADER = "example/example-Prefix.pch"; 283 | INFOPLIST_FILE = "example/example-Info.plist"; 284 | PRODUCT_NAME = "$(TARGET_NAME)"; 285 | WRAPPER_EXTENSION = app; 286 | }; 287 | name = Debug; 288 | }; 289 | 78BBAB09157A984700D8AC61 /* Release */ = { 290 | isa = XCBuildConfiguration; 291 | buildSettings = { 292 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 293 | GCC_PREFIX_HEADER = "example/example-Prefix.pch"; 294 | INFOPLIST_FILE = "example/example-Info.plist"; 295 | PRODUCT_NAME = "$(TARGET_NAME)"; 296 | WRAPPER_EXTENSION = app; 297 | }; 298 | name = Release; 299 | }; 300 | /* End XCBuildConfiguration section */ 301 | 302 | /* Begin XCConfigurationList section */ 303 | 78BBAAE9157A984700D8AC61 /* Build configuration list for PBXProject "example" */ = { 304 | isa = XCConfigurationList; 305 | buildConfigurations = ( 306 | 78BBAB05157A984700D8AC61 /* Debug */, 307 | 78BBAB06157A984700D8AC61 /* Release */, 308 | ); 309 | defaultConfigurationIsVisible = 0; 310 | defaultConfigurationName = Release; 311 | }; 312 | 78BBAB07157A984700D8AC61 /* Build configuration list for PBXNativeTarget "example" */ = { 313 | isa = XCConfigurationList; 314 | buildConfigurations = ( 315 | 78BBAB08157A984700D8AC61 /* Debug */, 316 | 78BBAB09157A984700D8AC61 /* Release */, 317 | ); 318 | defaultConfigurationIsVisible = 0; 319 | defaultConfigurationName = Release; 320 | }; 321 | /* End XCConfigurationList section */ 322 | }; 323 | rootObject = 78BBAAE6157A984700D8AC61 /* Project object */; 324 | } 325 | -------------------------------------------------------------------------------- /resources/example/example/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface AppDelegate : UIResponder 4 | 5 | @property (strong, nonatomic) UIWindow *window; 6 | 7 | @end 8 | -------------------------------------------------------------------------------- /resources/example/example/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | 3 | @implementation AppDelegate 4 | 5 | @synthesize window = _window; 6 | 7 | - (void)dealloc 8 | { 9 | [_window release]; 10 | [super dealloc]; 11 | } 12 | 13 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 14 | { 15 | self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease]; 16 | // Override point for customization after application launch. 17 | self.window.backgroundColor = [UIColor whiteColor]; 18 | [self.window makeKeyAndVisible]; 19 | return YES; 20 | } 21 | 22 | - (void)applicationWillResignActive:(UIApplication *)application 23 | { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application 29 | { 30 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 31 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 32 | } 33 | 34 | - (void)applicationWillEnterForeground:(UIApplication *)application 35 | { 36 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 37 | } 38 | 39 | - (void)applicationDidBecomeActive:(UIApplication *)application 40 | { 41 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 42 | } 43 | 44 | - (void)applicationWillTerminate:(UIApplication *)application 45 | { 46 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 47 | } 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /resources/example/example/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /resources/example/example/example-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | org.yanot.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 345 25 | LSRequiresIPhoneOS 26 | 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /resources/example/example/example-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'example' target in the 'example' project 3 | // 4 | 5 | #import 6 | 7 | #ifndef __IPHONE_3_0 8 | #warning "This project uses features only available in iOS SDK 3.0 and later." 9 | #endif 10 | 11 | #ifdef __OBJC__ 12 | #import 13 | #import 14 | #endif 15 | -------------------------------------------------------------------------------- /resources/example/example/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | @autoreleasepool { 8 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /spec/build_phase_node_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe XcodeProject::BuildPhaseNode do 4 | let(:root) { prepare_example_project.read.send(:root) } 5 | let(:file_ref) { root.project.main_group.file_ref('group1a/file2c.m') } 6 | let(:build_file) { root.project.target('example').sources_build_phase.send(:build_file, file_ref.uuid) } 7 | let(:obj) { obj = root.project.target('example').sources_build_phase } 8 | 9 | describe "#files" do 10 | it "returns an array of files" do 11 | obj.files.should be_an_instance_of(Array) 12 | obj.files.each {|obj| obj.should be_an_instance_of(XcodeProject::PBXFileReference) } 13 | end 14 | end 15 | 16 | describe "#add_file" do 17 | context "if passed an object of the PBXFileReference type" do 18 | it "=> add_build_file" do 19 | mock(obj).add_build_file(file_ref.uuid) 20 | obj.add_file(file_ref) 21 | end 22 | end 23 | context "if passed an object of the PBXBuildFile type" do 24 | it "=> add_build_file_uuid" do 25 | mock(obj).add_build_file_uuid(build_file.uuid) 26 | obj.add_file(build_file) 27 | end 28 | end 29 | context "if passed the object of unsupported type" do 30 | it "raise an exception" do 31 | lambda { obj.add_file(stub) }.should raise_exception(ArgumentError) 32 | end 33 | end 34 | end 35 | 36 | describe "#remove_file" do 37 | context "if passed an object of the PBXFileReference type" do 38 | it "=> remove_build_file_uuid" do 39 | mock(obj).remove_build_file_uuid(root.project.target('example').sources_build_phase.send(:build_file, file_ref.uuid).uuid) 40 | obj.remove_file(file_ref) 41 | end 42 | end 43 | context "if passed an object of the PBXBuildFile type" do 44 | it "=> remove_build_file_uuid" do 45 | mock(obj).remove_build_file_uuid(build_file.uuid) 46 | obj.remove_file(build_file) 47 | end 48 | end 49 | context "if passed the object of unsupported type" do 50 | it "raise an exception" do 51 | lambda { obj.remove_file(stub) }.should raise_exception(ArgumentError) 52 | end 53 | end 54 | end 55 | 56 | describe "#build_files" do 57 | it "returns an array of files" do 58 | obj.send(:build_files).should be_an_instance_of(Array) 59 | obj.send(:build_files).each {|obj| obj.should be_an_instance_of(XcodeProject::PBXBuildFile) } 60 | end 61 | end 62 | 63 | describe "#build_file" do 64 | it "returns the object" do 65 | obj.send(:build_file, file_ref.uuid).should be_an_instance_of(XcodeProject::PBXBuildFile) 66 | end 67 | end 68 | 69 | describe "#add_build_file" do 70 | it "adds the build file, returns the object" do 71 | obj.send(:add_build_file, file_ref.uuid).should be_an_instance_of(XcodeProject::PBXBuildFile) 72 | end 73 | end 74 | 75 | describe "#remove_build_file" do 76 | it "=> remove_build_file_uuid" do 77 | mock(obj).remove_build_file_uuid(build_file.uuid) 78 | obj.send(:remove_build_file, file_ref.uuid) 79 | end 80 | end 81 | 82 | describe "#add_build_file_uuid" do 83 | it "adds the build file uuid to the build list" do 84 | obj.send(:add_build_file_uuid, build_file.uuid) 85 | obj.send(:build_files).map {|obj| obj.uuid }.should include(build_file.uuid) 86 | end 87 | end 88 | 89 | describe "#remove_build_file_uuid" do 90 | it "removes the build file uuid from the build list" do 91 | obj.send(:remove_build_file_uuid, build_file.uuid) 92 | obj.send(:build_files).map {|obj| obj.uuid }.should_not include(build_file.uuid) 93 | end 94 | end 95 | end 96 | -------------------------------------------------------------------------------- /spec/file_node_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe XcodeProject::FileNode do 4 | let(:data_name) { Hash['name', 'file.name'] } 5 | let(:data_path) { Hash['path', 'path/to/file.name'] } 6 | let(:data_path_name) { data_path.merge!(data_name) } 7 | let(:data_empty) { Hash.new } 8 | 9 | describe "#name" do 10 | context "if the name is initialized " do 11 | it "returns the name as is" do 12 | [data_name, data_path_name].each do |data| 13 | XcodeProject::FileNode.new(stub, stub, data).name.should eql(data['name']) 14 | end 15 | end 16 | end 17 | context "if the name isn't initialized" do 18 | context "and the path is initialized " do 19 | it "returns the name as basename of the path" do 20 | XcodeProject::FileNode.new(stub, stub, data_path).name.should eql(File.basename(data_path['path'])) 21 | end 22 | end 23 | context "and the path isn't initialized" do 24 | it "returns nil" do 25 | XcodeProject::FileNode.new(stub, stub, data_empty).name.should be_nil 26 | end 27 | end 28 | end 29 | end 30 | 31 | describe "#path" do 32 | context "if the path is initialized " do 33 | it "returns the path as is" do 34 | [data_path, data_path_name].each do |data| 35 | XcodeProject::FileNode.new(stub, stub, data_path).path.should eql(data['path']) 36 | end 37 | end 38 | end 39 | context "if the path isn't initialized" do 40 | it "returns nil" do 41 | XcodeProject::FileNode.new(stub, stub, data_empty).path.should be_nil 42 | end 43 | end 44 | end 45 | 46 | describe "#parent" do 47 | let(:root) { prepare_example_project.read.send(:root) } 48 | 49 | context "if the current object is the main group" do 50 | it "returns nil" do 51 | root.project.main_group.parent.should be_nil 52 | end 53 | end 54 | end 55 | 56 | shared_examples_for "a file node" do 57 | describe "#parent" do 58 | context "if the object is nested file node" do 59 | it "returns the parent object" do 60 | file_nodes_gpaths do |gpath| 61 | main_group.child(gpath).parent.should be_an_instance_of(XcodeProject::PBXGroup) 62 | end 63 | end 64 | end 65 | end 66 | 67 | describe "#group_path" do 68 | it "returns the abstract path from project main group" do 69 | file_nodes_gpaths.map do |gpath| 70 | main_group.child(gpath).group_path 71 | end.should eql(file_nodes_gpaths.map {|gpath| Pathname.new(gpath) }) 72 | end 73 | end 74 | 75 | describe "#total_path" do 76 | it "returns the absolute file path" do 77 | file_nodes_gpaths.map do |gpath| 78 | main_group.child(gpath).total_path 79 | end.should eql(file_nodes_total_paths.map {|gpath| Pathname.new(gpath) }) 80 | end 81 | end 82 | end 83 | end 84 | -------------------------------------------------------------------------------- /spec/pbx_build_file_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe XcodeProject::PBXBuildFile do 4 | let(:root) { prepare_example_project.read.send(:root) } 5 | let(:obj_file_ref) { root.project.main_group.file_ref("group1a/file2c.m") } 6 | let(:obj) { root.project.target('example').sources_build_phase.send(:build_file, obj_file_ref.uuid) } 7 | 8 | describe "#file_ref" do 9 | it "returns the object" do 10 | obj.file_ref.should be_an_instance_of(XcodeProject::PBXFileReference) 11 | end 12 | end 13 | 14 | describe "#remove!" do 15 | it "removes the current build file" do 16 | obj.remove! 17 | root.project.target('example').sources_build_phase.send(:build_file, obj_file_ref.uuid).should be_nil 18 | end 19 | it "removes the current build file from all targets" do 20 | root.project.targets.each do |target| 21 | target.sources.map {|obj| obj.uuid }.should_not include(obj.uuid) 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /spec/pbx_file_reference_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe XcodeProject::PBXFileReference do 4 | let(:root) { prepare_example_project.read.send(:root) } 5 | let(:obj_gpath) { "group1a/file2c.m" } 6 | let(:obj) { root.project.main_group.file_ref(obj_gpath) } 7 | 8 | describe "#remove!" do 9 | it "removes the current file reference" do 10 | obj.remove! 11 | root.project.main_group.file_ref(obj_gpath).should be_nil 12 | end 13 | it "removes build files which has been bonded with the current file reference" do 14 | obj.remove! 15 | root.build_files(obj.uuid).should be_empty 16 | end 17 | end 18 | 19 | it_behaves_like "a file node" do 20 | let(:main_group) { 21 | root.project.main_group 22 | } 23 | let(:file_nodes_gpaths) {[ 24 | "group1a/dir2c/dir3a/dir4a/file5a-a.m", 25 | "group1a/dir2c/dir3a/dir4a/file5a-r.m", 26 | "group1a/file2c.m" 27 | ]} 28 | let(:file_nodes_total_paths) {[ 29 | "#{example_project_dir}/dir1a/dir2a/dir3a/dir4a/file5a-a.m", 30 | "#{example_project_dir}/dir1a/dir2a/dir3a/dir4a/file5a-r.m", 31 | "#{example_project_dir}/dir1c/file2c.m" 32 | ]} 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /spec/pbx_group_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe XcodeProject::PBXGroup do 4 | let(:root) { prepare_example_project.read.send(:root) } 5 | let(:obj) { root.project.main_group.group('group1a') } 6 | 7 | describe "#children" do 8 | context "if children exists" do 9 | it "returns an array of children objects" do 10 | obj.children.should be_an_instance_of(Array) 11 | obj.children.size.should > 0 12 | end 13 | end 14 | context "if children doesn't exist" do 15 | let(:empty_group_obj) { root.project.main_group.group("group1a/group2a") } 16 | 17 | it "returns an empty array" do 18 | empty_group_obj.children.should be_an_instance_of(Array) 19 | empty_group_obj.children.size.should eql(0) 20 | end 21 | end 22 | end 23 | 24 | describe "#files" do 25 | context "if children exists" do 26 | it "Returns an array of only those children who are files" do 27 | chs = obj.files 28 | chs.should be_an_instance_of(Array) 29 | chs.size.should > 0 30 | chs.each do |child| 31 | child.should be_an_instance_of(XcodeProject::PBXFileReference) 32 | end 33 | end 34 | end 35 | context "if children doesn't exist" do 36 | let(:obj) { root.project.main_group.group("group1a/dir2c") } 37 | 38 | it "returns an empty array" do 39 | chs = obj.files 40 | chs.should be_an_instance_of(Array) 41 | chs.size.should eql(0) 42 | end 43 | end 44 | end 45 | 46 | describe "#groups" do 47 | context "if children exists" do 48 | it "Returns an array of only those children who are group" do 49 | chs = obj.groups 50 | chs.should be_an_instance_of(Array) 51 | chs.size.should > 0 52 | chs.each do |child| 53 | child.should be_an_instance_of(XcodeProject::PBXGroup) 54 | end 55 | end 56 | end 57 | context "if children doesn't exist" do 58 | let(:obj) { root.project.main_group.group("group1a/dir2c/dir3a/dir4a") } 59 | 60 | it "returns an empty array" do 61 | chs = obj.groups 62 | chs.should be_an_instance_of(Array) 63 | chs.size.should eql(0) 64 | end 65 | end 66 | end 67 | 68 | describe "#child" do 69 | context "if passed '.'" do 70 | it "returns the self" do 71 | obj.child('.').uuid.should eql(obj.uuid) 72 | end 73 | end 74 | context "if passed '..'" do 75 | it "returns the parent" do 76 | obj.child('..').uuid.should eql(obj.parent.uuid) 77 | end 78 | end 79 | context "if passed a child name" do 80 | it "returns the child object" do 81 | [ 82 | 'group2a', 83 | 'dir2c', 84 | 'file2c.m' 85 | ].each do |name| 86 | obj.child(name).should_not be_nil 87 | end 88 | end 89 | end 90 | context "if passed the group path" do 91 | it "returns the child object" do 92 | [ 93 | 'dir2c/dir3a', 94 | 'dir2c/dir3a/dir4a/file5a-a.m', 95 | 'dir2c/dir3a/dir4a/file5a-r.m', 96 | 'dir2c/../dir2c/dir3a/./dir4a/file5a-r.m' 97 | ].each do |gpath| 98 | obj.child(gpath).should_not be_nil 99 | end 100 | end 101 | end 102 | context "if child doesn't exist" do 103 | it "returns nil" do 104 | [ 105 | 'group2a_ghost', 106 | 'group2a_ghost/file3c_ghost.m', 107 | 'dir2c/file3c_ghost.m' 108 | ].each do |gpath| 109 | obj.child(gpath).should be_nil 110 | end 111 | end 112 | end 113 | end 114 | 115 | describe "#group?" do 116 | let(:dir_obj) { obj.child('dir2c') } 117 | 118 | context "if the current group is abstract" do 119 | it "returns true" do 120 | obj.group?.should eql(true) 121 | end 122 | end 123 | context "if the current group is directory" do 124 | it "returns false" do 125 | dir_obj.group?.should eql(false) 126 | end 127 | end 128 | end 129 | 130 | describe "#dir?" do 131 | let(:dir_obj) { obj.child('dir2c') } 132 | 133 | context "if the current group is directory" do 134 | it "returns true" do 135 | dir_obj.dir?.should eql(true) 136 | end 137 | end 138 | context "if the current group is abstract" do 139 | it "returns false" do 140 | obj.dir?.should eql(false) 141 | end 142 | end 143 | end 144 | 145 | describe "#group" do 146 | context "if the group exists" do 147 | it "returns the object" do 148 | obj.group('group2a').should be_an_instance_of(XcodeProject::PBXGroup) 149 | end 150 | end 151 | context "if the group doesn't exist" do 152 | it "returns nil" do 153 | obj.group('group2a_ghost').should be_nil 154 | end 155 | end 156 | end 157 | 158 | describe "#file_ref" do 159 | context "if the file reference exists" do 160 | it "returns the object" do 161 | obj.file_ref('file2c.m').should be_an_instance_of(XcodeProject::PBXFileReference) 162 | end 163 | end 164 | context "if the file reference doesn't exist" do 165 | it "returns nil" do 166 | obj.file_ref('file2c_ghost.m').should be_nil 167 | end 168 | end 169 | end 170 | 171 | describe "#add_file_ref" do 172 | context "if passed the relative path" do 173 | it "adds the file reference, returns the object" do 174 | obj.send(:add_file_ref, 'dir1b/file2b.m').should be_an_instance_of(XcodeProject::PBXFileReference) 175 | end 176 | end 177 | context "if passed the absolute path" do 178 | it "adds the file reference, returns the object" do 179 | obj.send(:add_file_ref, "#{example_project_dir}/dir1b/file2b.m").should be_an_instance_of(XcodeProject::PBXFileReference) 180 | end 181 | end 182 | context "if file reference already exists" do 183 | it "new object has same uuid as existing" do 184 | uuid = obj.file_ref('file2c.m').uuid 185 | obj.send(:add_file_ref, 'dir1c/file2c.m').uuid.should eql(uuid) 186 | end 187 | end 188 | context "if file doesn't exit" do 189 | it "raise an exception " do 190 | lambda { obj.send(:add_file_ref, "file2c_ghost.m") }.should raise_exception(XcodeProject::FilePathError) 191 | end 192 | end 193 | end 194 | 195 | describe "#add_group" do 196 | it "adds the group, returns the object" do 197 | group_obj = obj.add_group("group2a_ghost") 198 | group_obj.should be_an_instance_of(XcodeProject::PBXGroup) 199 | group_obj.group?.should eql(true) 200 | end 201 | end 202 | 203 | describe "#add_dir" do 204 | it "adds the group, returns the object" do 205 | group_obj = obj.add_dir("dir1c") 206 | group_obj.should be_an_instance_of(XcodeProject::PBXGroup) 207 | group_obj.dir?.should eql(true) 208 | end 209 | it "adds all dir's children" do 210 | obj.add_dir('dir1c') 211 | [ 212 | obj.group('dir1c'), 213 | obj.group('dir1c/dir2c'), 214 | obj.file('dir1c/file2c.m') 215 | ].each {|obj| obj.should_not be_nil } 216 | end 217 | context "if dir doesn't exit" do 218 | it "raise an exception " do 219 | lambda { obj.add_dir("dir2c_ghost") }.should raise_exception(XcodeProject::FilePathError) 220 | end 221 | end 222 | end 223 | 224 | describe "#create_group" do 225 | context "if passed a group name" do 226 | it "creates and returns the group object" do 227 | obj.create_group('group2a_ghost').should be_an_instance_of(XcodeProject::PBXGroup) 228 | end 229 | end 230 | context "if passed a group path" do 231 | it "creates missed groups and returns the last group object" do 232 | [ 233 | 'group2a/group3a_ghost', 234 | 'group2a_ghost/group3a_ghost', 235 | 'group2a_ghost/../create_group2a_ghost/./group3a_ghost' 236 | ].each do |gpath| 237 | obj.create_group(gpath).should be_an_instance_of(XcodeProject::PBXGroup) 238 | end 239 | end 240 | end 241 | context "if group already exists" do 242 | it "new object has same uuid as existing" do 243 | uuid = obj.child('group2a').uuid 244 | obj.create_group('group2a').uuid.should eql(uuid) 245 | end 246 | end 247 | end 248 | 249 | describe "#remove_file_ref" do 250 | context "if the file reference exists" do 251 | it "removes the object" do 252 | obj.remove_file_ref('group1a/file2c.m') 253 | obj.file_ref('group1a/file2c.m').should be_nil 254 | end 255 | end 256 | context "if the file reference doesn't exist" do 257 | it "returns nil" do 258 | obj.remove_file_ref('group1a/file2c_ghost.m').should be_nil 259 | end 260 | end 261 | end 262 | 263 | describe "#remove_group" do 264 | context "if the group exists" do 265 | it "removes the object" do 266 | obj.remove_group('group2a') 267 | obj.group('group2a').should be_nil 268 | end 269 | end 270 | context "if the group doesn't exist" do 271 | it "returns nil" do 272 | obj.remove_group('group2a_ghost').should be_nil 273 | end 274 | end 275 | end 276 | 277 | describe "#remove!" do 278 | it "removes the current group" do 279 | obj.remove! 280 | root.project.main_group.group('group1a').should be_nil 281 | end 282 | end 283 | 284 | describe "#absolute_path" do 285 | let(:dir_obj) { root.project.main_group.group('group1a/dir2c/dir3a') } 286 | 287 | it "makes the absolute path" do 288 | [ 289 | "dir4a/file5a-a.m", 290 | "#{example_project_dir}/dir1a/dir2a/dir3a/dir4a/file5a-a.m" 291 | ].map {|path| dir_obj.absolute_path(path) }.should eql([ 292 | Pathname.new("#{example_project_dir}/dir1a/dir2a/dir3a/dir4a/file5a-a.m"), 293 | Pathname.new("#{example_project_dir}/dir1a/dir2a/dir3a/dir4a/file5a-a.m") 294 | ]) 295 | end 296 | end 297 | 298 | describe "#relative_path" do 299 | let(:dir_obj) { root.project.main_group.group('group1a/dir2c/dir3a') } 300 | 301 | it "makes the relative path" do 302 | [ 303 | "dir4a/file5a-a.m", 304 | "#{example_project_dir}/dir1a/dir2a/dir3a/dir4a/file5a-a.m" 305 | ].map {|path| dir_obj.relative_path(path) }.should eql([ 306 | Pathname.new("dir4a/file5a-a.m"), 307 | Pathname.new("dir4a/file5a-a.m") 308 | ]) 309 | end 310 | end 311 | 312 | it_behaves_like "a file node" do 313 | let(:main_group) { 314 | root.project.main_group 315 | } 316 | let(:file_nodes_gpaths) {[ 317 | "group1a/dir2c/dir3a/dir4a", 318 | "group1a/dir2c/dir3a", 319 | "group1a/dir2c", 320 | "group1a/group2a", 321 | "group1a" 322 | ]} 323 | let(:file_nodes_total_paths) {[ 324 | "#{example_project_dir}/dir1a/dir2a/dir3a/dir4a", 325 | "#{example_project_dir}/dir1a/dir2a/dir3a", 326 | "#{example_project_dir}/dir1c/dir2c", 327 | example_project_dir, 328 | example_project_dir 329 | ]} 330 | end 331 | end 332 | 333 | -------------------------------------------------------------------------------- /spec/pbx_native_target_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe XcodeProject::PBXNativeTarget do 4 | before(:each) { @data = prepare_example_project.read } 5 | let(:obj) { obj = @data.target('example') } 6 | 7 | describe "#sources" do 8 | it "=> PBXSourcesBuildPhase#files" do 9 | files = mock 10 | mock(obj).sources_build_phase.mock!.files { files } 11 | obj.sources.should eql(files) 12 | end 13 | end 14 | 15 | describe "#add_source" do 16 | it "=> PBXSourcesBuildPhase#add_file" do 17 | file = mock 18 | mock(obj).sources_build_phase.mock!.add_file(file) 19 | obj.add_source(file) 20 | end 21 | end 22 | 23 | describe "#remove_source" do 24 | it "=> PBXSourcesBuildPhase#remove_file" do 25 | file = mock 26 | mock(obj).sources_build_phase.mock!.remove_file(file) 27 | obj.remove_source(file) 28 | end 29 | end 30 | 31 | describe "#build_configurations_list" do 32 | it "returns the build configuration list object" do 33 | obj.build_configurations_list.should be_an_instance_of(XcodeProject::XCConfigurationList) 34 | end 35 | end 36 | 37 | describe "#configs" do 38 | it "=> XCConfigurationList#build_configurations" do 39 | res = mock 40 | mock(obj).build_configurations_list.mock!.build_configurations { res } 41 | obj.configs.should eql(res) 42 | end 43 | end 44 | 45 | describe "#config" do 46 | it "=> XCConfigurationList#build_configuration" do 47 | name, res = mock, mock 48 | mock(obj).build_configurations_list.mock!.build_configuration(name) { res } 49 | obj.config(name).should eql(res) 50 | end 51 | end 52 | 53 | describe "#build_phases" do 54 | it "returns the array of build phase objects" do 55 | obj.build_phases.should be_an_instance_of(Array) 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /spec/pbx_project_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe XcodeProject::PBXProject do 4 | before(:each) { @data = prepare_example_project.read } 5 | let(:obj) { @data.project } 6 | 7 | describe "#targets" do 8 | it "returns the array of target objects" do 9 | targets = obj.targets 10 | targets.should be_an_instance_of(Array) 11 | targets.each {|obj| obj.should be_an_instance_of(XcodeProject::PBXNativeTarget) } 12 | end 13 | end 14 | 15 | describe "#target" do 16 | context "if the target exists" do 17 | it "returns the object" do 18 | obj.target('example').should be_an_instance_of(XcodeProject::PBXNativeTarget) 19 | end 20 | end 21 | context "if the target doesn't exist" do 22 | it "returns nil" do 23 | obj.target('ghost-target').should be_nil 24 | end 25 | end 26 | end 27 | 28 | describe "#main_group" do 29 | it "returns the main group object" do 30 | obj.main_group.should be_an_instance_of(XcodeProject::PBXGroup) 31 | end 32 | end 33 | end 34 | 35 | -------------------------------------------------------------------------------- /spec/project_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe XcodeProject::Project do 4 | let(:proj) { prepare_example_project } 5 | 6 | describe "#new" do 7 | context "if the project file exists" do 8 | it "constructs an project object" do 9 | XcodeProject::Project.new(example_project_bundle_path).should be_an_instance_of(XcodeProject::Project) 10 | end 11 | end 12 | 13 | context "if the project file doesn't exist" do 14 | let(:proj_ne) { "#{example_empty_sandbox_path}/ghost.xcodeproj" } 15 | it "trows the exception" do 16 | lambda { XcodeProject::Project.new(proj_ne) }.should raise_exception(XcodeProject::FilePathError) 17 | end 18 | end 19 | end 20 | 21 | describe "#file_path" do 22 | it "returns the project's file path" do 23 | proj.file_path.should eql(example_project_file_path) 24 | end 25 | end 26 | 27 | describe "#bundle_path" do 28 | it "returns the project's bundle path" do 29 | proj.bundle_path.should eql(example_project_bundle_path) 30 | end 31 | end 32 | 33 | describe "#find" do 34 | context "if a specified directory contains project files" do 35 | it "returns an array of project objects" do 36 | projs = XcodeProject::Project.find(example_project_bundle_path) 37 | projs.size.should eql(1) 38 | projs.first.bundle_path.should eql(proj.bundle_path) 39 | end 40 | end 41 | 42 | context "if a specified directory pattern contains project files" do 43 | it "returns an array of project objects" do 44 | projs = XcodeProject::Project.find("#{example_sandbox_path}/*") 45 | projs.size.should eql(1) 46 | projs.first.bundle_path.should eql(proj.bundle_path) 47 | end 48 | end 49 | 50 | context "if a specified directory doesn't contain project files" do 51 | it "returns an empty array" do 52 | projs = XcodeProject::Project.find(example_empty_sandbox_path) 53 | projs.size.should eql(0) 54 | end 55 | end 56 | end 57 | 58 | describe "#read" do 59 | it "returns the data object" do 60 | proj.read.should be_an_instance_of(XcodeProject::Data) 61 | end 62 | 63 | it "reads the project file" do 64 | mock.proxy(proj).file_path 65 | proj.read 66 | end 67 | end 68 | 69 | describe "#write" do 70 | it "writes the project file" do 71 | data = proj.read 72 | 73 | mock.proxy(proj).file_path 74 | proj.write(data) 75 | end 76 | end 77 | 78 | describe "#change" do 79 | it "reads the file file and then writes it" do 80 | proj_mock = mock.proxy(proj) 81 | proj_mock.read 82 | proj_mock.write(is_a(XcodeProject::Data)) 83 | proj.change {|data| } 84 | end 85 | end 86 | end 87 | 88 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # This file was generated by the `rspec --init` command. Conventionally, all 2 | # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. 3 | # Require this file using `require "spec_helper.rb"` to ensure that it is only 4 | # loaded once. 5 | # 6 | # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration 7 | 8 | require 'xcodeproject/project' 9 | require 'xcodeproject/exceptions' 10 | require 'rr' 11 | 12 | RSpec.configure do |config| 13 | config.treat_symbols_as_metadata_keys_with_true_values = true 14 | config.run_all_when_everything_filtered = true 15 | config.filter_run :focus 16 | config.mock_with :rr 17 | end 18 | 19 | # Example project file hierarhy 20 | # 21 | # main 22 | # \--group1a 23 | # |--group2a 24 | # |--dir2c 25 | # | \--dir3a 26 | # | \--dir4a 27 | # | |--file5a-a.m 28 | # | |--file5a-a.h 29 | # | |--file5a-r.m 30 | # | \--file5a-r.h 31 | # | 32 | # |--file2c.m 33 | # \--file2c.h 34 | 35 | def prepare_sandbox 36 | %x{ mkdir -p #{example_sandbox_path} } unless File.exist?(example_sandbox_path) 37 | end 38 | 39 | def prepare_example_project 40 | prepare_sandbox 41 | 42 | proj_source_dir = "#{File.dirname(__FILE__)}/../resources/example" 43 | %x{ rm -vr #{example_project_dir} } 44 | %x{ cp -vr #{proj_source_dir} #{example_sandbox_path} } 45 | 46 | XcodeProject::Project.new(example_project_bundle_path) 47 | end 48 | 49 | def example_sandbox_path; Pathname.new('/tmp/example_sandbox') end 50 | def example_empty_sandbox_path; Pathname.new('/tmp/example_sandbox_empty') end 51 | def example_project_dir; example_sandbox_path.join('example') end 52 | def example_project_bundle_path; example_project_dir.join('example.xcodeproj') end 53 | def example_project_file_path; example_project_bundle_path.join('project.pbxproj') end 54 | 55 | -------------------------------------------------------------------------------- /spec/xc_build_configuration_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe XcodeProject::XCBuildConfiguration do 4 | before(:each) { @data = prepare_example_project.read } 5 | let(:obj) { obj = @data.target('example').config('Release') } 6 | 7 | describe "#plist_path" do 8 | it "returns plist file path" do 9 | obj.send(:plist_path).should eql(example_project_dir.join('example/example-Info.plist')) 10 | end 11 | end 12 | 13 | describe "#version" do 14 | context "by default" do 15 | it "returns major version" do 16 | obj.version.should eql(obj.version(:major)) 17 | end 18 | end 19 | context "if passed :major" do 20 | it "returns major version" do 21 | obj.version(:major).should eql('1.0') 22 | end 23 | end 24 | context "if passed :minor" do 25 | it "returns minor version" do 26 | obj.version(:minor).should eql('345') 27 | end 28 | end 29 | context "if passed :both" do 30 | it "returns both, major and minor versions" do 31 | obj.version(:both).should eql('1.0.345') 32 | end 33 | end 34 | end 35 | 36 | describe "#change_version" do 37 | let(:version) { '2.0' } 38 | 39 | context "by default" do 40 | it "changes major version" do 41 | obj.change_version(version) 42 | obj.version(:major).should eql(version) 43 | end 44 | end 45 | 46 | context "if passed :major" do 47 | it "changes major version" do 48 | obj.change_version(version, :major) 49 | obj.version(:major).should eql(version) 50 | end 51 | end 52 | 53 | context "if passed :minor" do 54 | it "changes minor version" do 55 | obj.change_version(version, :minor) 56 | obj.version(:minor).should eql(version) 57 | end 58 | end 59 | end 60 | 61 | describe "#increment_version" do 62 | context "by default" do 63 | let(:next_version) { '1.1' } 64 | 65 | it "increments major version" do 66 | obj.increment_version 67 | obj.version(:major).should eql(next_version) 68 | end 69 | end 70 | 71 | context "if passed :major" do 72 | let(:next_version) { '1.1' } 73 | 74 | it "increments major version" do 75 | obj.increment_version(:major) 76 | obj.version(:major).should eql(next_version) 77 | end 78 | end 79 | 80 | context "if passed :minor" do 81 | let(:next_version) { '346' } 82 | 83 | it "increments minor version" do 84 | obj.increment_version(:minor) 85 | obj.version(:minor).should eql(next_version) 86 | end 87 | end 88 | end 89 | 90 | describe "#read_property" do 91 | let(:key) { 'CFBundleShortVersionString' } 92 | let(:value) { '1.0' } 93 | 94 | it "read a property by key from plist file" do 95 | obj.send(:read_property, key).should eql(value) 96 | end 97 | end 98 | 99 | describe "#write_property" do 100 | let(:key) { 'CFBundleShortVersionString' } 101 | let(:value) { '1.1' } 102 | 103 | it "write value by key to plist file" do 104 | obj.send(:write_property, key, value) 105 | obj.send(:read_property, key).should eql(value) 106 | end 107 | end 108 | end 109 | -------------------------------------------------------------------------------- /spec/xc_configuration_list_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe XcodeProject::XCConfigurationList do 4 | before(:each) { @data = prepare_example_project.read } 5 | let(:obj) { obj = @data.target('example').build_configurations_list } 6 | 7 | describe "#build_configuration" do 8 | let(:name) { 'Release' } 9 | 10 | it "returns build configuration object" do 11 | obj.build_configuration(name).should be_an_instance_of(XcodeProject::XCBuildConfiguration) 12 | end 13 | end 14 | 15 | describe "#build_configurations" do 16 | it "returns an array of build configuration objects" do 17 | obj.build_configurations.should be_an_instance_of(Array) 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /xcodeproject.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | require File.expand_path('../lib/xcodeproject/version', __FILE__) 3 | 4 | Gem::Specification.new do |gem| 5 | gem.name = 'xcodeproject' 6 | gem.version = XcodeProject::VERSION 7 | gem.summary = 'Read, write and build xcode projects' 8 | gem.description = 'XcodeProject is the Ruby API for working with Xcode project files' 9 | gem.author = 'Andrei Nesterov' 10 | gem.email = 'ae.nesterov@gmail.com' 11 | gem.homepage = 'https://github.com/manifest/xcodeproject' 12 | gem.rubyforge_project = 'xcodeproject' 13 | gem.license = 'MIT' 14 | 15 | gem.files = `git ls-files`.split($\) 16 | gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) } 17 | gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) 18 | gem.require_paths = ["lib"] 19 | 20 | gem.required_ruby_version = '>= 2.0.0' 21 | gem.add_runtime_dependency 'rake', '>= 10.0' 22 | gem.add_runtime_dependency 'json', '~> 1.8' 23 | gem.add_runtime_dependency 'uuid', '~> 2.3' 24 | gem.add_runtime_dependency 'xcodebuild-rb', '~> 0.2.0' 25 | gem.add_development_dependency 'rspec', '~> 2.0' 26 | gem.add_development_dependency 'rr', '~> 1.0.4' 27 | gem.add_development_dependency 'redcarpet', '~> 2.1.1' 28 | gem.add_development_dependency 'yard', '~> 0.8.2' 29 | end 30 | 31 | --------------------------------------------------------------------------------