diff --git a/CHANGELOG.md b/CHANGELOG.md index 69e6564ab..c1f8e5a26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -66,6 +66,12 @@ ## 0.14.1 +###### Enhancements + +* Add support for absolute, group and container project references in workspaces + [Kyle Fuller](https://github.com/kylef) + [#118](https://github.com/CocoaPods/Xcodeproj/issues/118) + ###### Bug Fixes * [Gem] On MRI 1.8.7 /dev/tty is considered writable when not configured, diff --git a/lib/xcodeproj/workspace.rb b/lib/xcodeproj/workspace.rb index a70fe2979..b4ecb3782 100644 --- a/lib/xcodeproj/workspace.rb +++ b/lib/xcodeproj/workspace.rb @@ -1,5 +1,6 @@ require 'fileutils' require 'rexml/document' +require 'xcodeproj/workspace/file_reference' module Xcodeproj @@ -9,15 +10,16 @@ module Xcodeproj class Workspace # @return [Array] the paths of the projects contained in the + # @return [Array] the paths of the projects contained in the # workspace. # - attr_reader :projpaths + attr_reader :file_references attr_reader :schemes - # @param [Array] projpaths @see projpaths + # @param [Array] file_references @see file_references # - def initialize(*projpaths) - @projpaths = projpaths.flatten + def initialize(*file_references) + @file_references = file_references.flatten @schemes = {} end @@ -50,10 +52,10 @@ def self.new_from_xcworkspace(path) # def self.from_s(xml, workspace_path='') document = REXML::Document.new(xml) - projpaths = document.get_elements("/Workspace/FileRef").map do |node| - node.attribute("location").value.sub(/^group:/, '') + file_references = document.get_elements("/Workspace/FileRef").map do |node| + FileReference.from_node(node) end - instance = new(projpaths) + instance = new(file_references) instance.load_schemes(workspace_path) instance end @@ -69,19 +71,20 @@ def self.from_s(xml, workspace_path='') # @return [void] # def <<(projpath) - @projpaths << projpath + @file_references << projpath load_schemes_from_project File.expand_path(projpath) end - # Checks if the workspace contains the project with the given path. + # Checks if the workspace contains the project with the given file + # reference. # - # @param [String] projpath - # The path of the project to add. + # @param [FileReference] file_reference + # The file_reference to the project. # # @return [Boolean] whether the project is contained in the workspace. # - def include?(projpath) - @projpaths.include?(projpath) + def include?(file_reference) + @file_references.include?(file_reference) end # The template to generate a workspace XML representation. @@ -92,10 +95,8 @@ def include?(projpath) # def to_s REXML::Document.new(TEMPLATE).tap do |document| - @projpaths.each do |projpath| - document.root << REXML::Element.new("FileRef").tap do |el| - el.attributes['location'] = "group:#{projpath}" - end + @file_references.each do |file_reference| + document.root << file_reference.to_node end end.to_s end @@ -124,9 +125,9 @@ def save_as(path) # @return [void] # def load_schemes workspace_dir_path - @projpaths.each do |projpath| - project_full_path = File.expand_path(File.join(workspace_dir_path, projpath)) - load_schemes_from_project project_full_path + @file_references.each do |file_reference| + project_full_path = file_reference.absolute_path(workspace_dir_path) + load_schemes_from_project(project_full_path) end end diff --git a/lib/xcodeproj/workspace/file_reference.rb b/lib/xcodeproj/workspace/file_reference.rb new file mode 100644 index 000000000..5e45a89ec --- /dev/null +++ b/lib/xcodeproj/workspace/file_reference.rb @@ -0,0 +1,87 @@ +module Xcodeproj + class Workspace + # Describes a file reference of a Workspace. + # + class FileReference + # @return [String] the path to the project + # + attr_reader :path + + # @return [String] the type of reference to the project + # + # This can be of the following values: + # - absolute + # - group + # - container + # - developer (unsupported) + # + attr_reader :type + + # @param [#to_s] path @see path + # @param [#to_s] type @see type + # + def initialize(path, type="group") + @path = path.to_s + @type = type.to_s + end + + # @return [Bool] Wether a file reference is equal to another. + # + def ==(other) + path == other.path && type == other.type + end + alias_method :eql?, :== + + # @return [Fixnum] A hash identical for equals objects. + # + def hash + [path, type].hash + end + + # Returns a file reference given XML representation. + # + # @param [REXML::Element] xml_node + # the XML representation. + # + # @return [FileReference] The new file reference instance. + # + def self.from_node(xml_node) + type, path = xml_node.attribute('location').value.split(':', 2) + new(path, type) + end + + # @return [REXML::Element] the XML representation of the file reference. + # + def to_node + REXML::Element.new("FileRef").tap do |element| + element.attributes['location'] = "#{type}:#{path}" + end + end + + # Returns the absolute path of a file reference given the path of the + # directory containing workspace. + # + # @param [#to_s] workspace_dir_path + # The Path of the directory containing the workspace. + # + # @return [String] The absolute path to the project. + # + def absolute_path(workspace_dir_path) + workspace_dir_path = workspace_dir_path.to_s + case type + when 'group' + File.expand_path(File.join(workspace_dir_path, path)) + when 'container' + File.expand_path(File.join(workspace_dir_path, path)) + when 'absolute' + File.expand_path(path) + when 'developer' + raise "Developer workspace file reference type is not yet " \ + "#{self}" + else + raise "Unsupported workspace file reference type #{type}" + end + end + end + end +end diff --git a/spec/workspace/file_reference_spec.rb b/spec/workspace/file_reference_spec.rb new file mode 100644 index 000000000..cda44647a --- /dev/null +++ b/spec/workspace/file_reference_spec.rb @@ -0,0 +1,50 @@ +require File.expand_path('../../spec_helper', __FILE__) + +module Xcodeproj + describe Workspace do + before do + @subject = Workspace::FileReference.new('project.xcodeproj', 'group') + end + + it 'properly implements equality comparison' do + @subject.should == @subject.dup + @subject.should.eql @subject.dup + @subject.hash.should == @subject.dup.hash + end + + it 'can be initialized by the XML representation' do + node = REXML::Element.new("FileRef") + node.attributes['location'] = "group:project.xcodeproj" + result = Workspace::FileReference.from_node(node) + result.should == @subject + end + + it 'returns the XML representation' do + result = @subject.to_node + result.class.should == REXML::Element + result.to_s.should == "" + end + + it 'can be converted back and forth without loss of information' do + result = Workspace::FileReference.from_node(@subject.to_node) + result.should == @subject + end + + it 'returns the absolute path for group types' do + result = @subject.absolute_path('/path/to/') + result.should == "/path/to/project.xcodeproj" + end + + it 'returns the absolute path for container types' do + @subject.stubs(:type).returns('container') + result = @subject.absolute_path('/path/to/') + result.should == "/path/to/project.xcodeproj" + end + + it 'returns the absolute path for absolute types' do + @subject.stubs(:type).returns('absolute') + result = @subject.absolute_path('/path/to/') + result.should == File.expand_path(@subject.path) + end + end +end diff --git a/spec/workspace_spec.rb b/spec/workspace_spec.rb index 603b5c175..32482dddd 100644 --- a/spec/workspace_spec.rb +++ b/spec/workspace_spec.rb @@ -1,6 +1,6 @@ require File.expand_path('../spec_helper', __FILE__) -describe "Xcodeproj::Workspace" do +describe Xcodeproj::Workspace do describe "from new" do before do @workspace = Xcodeproj::Workspace.new('Pods/Pods.xcodeproj', 'App.xcodeproj') @@ -8,13 +8,15 @@ it "accepts new projects" do @workspace << 'Framework.xcodeproj' - @workspace.projpaths.should.include 'Framework.xcodeproj' + @workspace.file_references.should.include 'Framework.xcodeproj' end end describe "converted to XML" do before do - @workspace = Xcodeproj::Workspace.new('Pods/Pods.xcodeproj', 'App.xcodeproj') + pods_project_file_reference = Xcodeproj::Workspace::FileReference.new('Pods/Pods.xcodeproj') + project_file_reference = Xcodeproj::Workspace::FileReference.new('App.xcodeproj') + @workspace = Xcodeproj::Workspace.new(pods_project_file_reference, project_file_reference) @doc = REXML::Document.new(@workspace.to_s) end @@ -35,9 +37,9 @@ end it "contains all of the projects in the workspace" do - @workspace.projpaths.should.include "libPusher.xcodeproj" - @workspace.projpaths.should.include "libPusher-OSX/libPusher-OSX.xcodeproj" - @workspace.projpaths.should.include "Pods/Pods.xcodeproj" + @workspace.file_references.should.include Xcodeproj::Workspace::FileReference.new("libPusher.xcodeproj") + @workspace.file_references.should.include Xcodeproj::Workspace::FileReference.new("libPusher-OSX/libPusher-OSX.xcodeproj") + @workspace.file_references.should.include Xcodeproj::Workspace::FileReference.new("Pods/Pods.xcodeproj") end end @@ -47,36 +49,36 @@ end it "contains no projects" do - @workspace.projpaths.should.be.empty + @workspace.file_references.should.be.empty end end - + describe "load schemes for all projects from workspace file" do before do @workspace = Xcodeproj::Workspace.new_from_xcworkspace(fixture_path("SharedSchemes/SharedSchemes.xcworkspace")) end - + it "returns data type should be hash" do @workspace.schemes.should.instance_of Hash end - - it "schemes count should be greater or equal to projpaths count" do - @workspace.schemes.count.should >= @workspace.projpaths.count + + it "schemes count should be greater or equal to file_references count" do + @workspace.schemes.count.should >= @workspace.file_references.count end - + it "contains only test data schemes" do @workspace.schemes.keys.sort.should == ['Pods', 'SharedSchemes', 'SharedSchemesForTest'] end end - + describe "built from a workspace file with XML entities in a project path" do before do @workspace = Xcodeproj::Workspace.new_from_xcworkspace(fixture_path("Otto's Remote.xcworkspace")) end it "contains all of the projects in the workspace" do - @workspace.projpaths.should.include "Otto's Remote.xcodeproj" - @workspace.projpaths.should.include "Pods/Pods.xcodeproj" + @workspace.file_references.should.include Xcodeproj::Workspace::FileReference.new("Otto's Remote.xcodeproj") + @workspace.file_references.should.include Xcodeproj::Workspace::FileReference.new("Pods/Pods.xcodeproj") end end end