framework-x-xcodeprojgen 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. data/README.markdown +12 -0
  2. data/Rakefile +45 -0
  3. data/TODO +8 -0
  4. data/bin/xcodeprojgen +14 -0
  5. data/lib/xcode.rb +56 -0
  6. data/lib/xcode/frameworks.rb +100 -0
  7. data/lib/xcode/iphone.rb +29 -0
  8. data/lib/xcode/iphone_build_configurations.rb +48 -0
  9. data/lib/xcode/pbx_build_file.rb +25 -0
  10. data/lib/xcode/pbx_build_phase.rb +25 -0
  11. data/lib/xcode/pbx_copy_files_build_phase.rb +7 -0
  12. data/lib/xcode/pbx_file_reference.rb +66 -0
  13. data/lib/xcode/pbx_formatter.rb +52 -0
  14. data/lib/xcode/pbx_frameworks_build_phase.rb +7 -0
  15. data/lib/xcode/pbx_group.rb +47 -0
  16. data/lib/xcode/pbx_native_target.rb +24 -0
  17. data/lib/xcode/pbx_proj.rb +21 -0
  18. data/lib/xcode/pbx_project.rb +29 -0
  19. data/lib/xcode/pbx_resources_build_phase.rb +7 -0
  20. data/lib/xcode/pbx_sources_build_phase.rb +7 -0
  21. data/lib/xcode/xc_build_configuration.rb +20 -0
  22. data/lib/xcode/xc_configuration_list.rb +20 -0
  23. data/lib/xcodeproj_gen.rb +140 -0
  24. data/test/acceptance/command_line_foundation_test.rb +33 -0
  25. data/test/acceptance/command_line_in_c_test.rb +33 -0
  26. data/test/acceptance/test_helper.rb +18 -0
  27. data/test/unit/test_helper.rb +13 -0
  28. data/test/unit/xcode/pbx_build_file_test.rb +18 -0
  29. data/test/unit/xcode/pbx_build_phase_test.rb +27 -0
  30. data/test/unit/xcode/pbx_file_reference_test.rb +74 -0
  31. data/test/unit/xcode/pbx_formatter_test.rb +35 -0
  32. data/test/unit/xcode/pbx_group_test.rb +25 -0
  33. data/test/unit/xcode/pbx_native_target_test.rb +13 -0
  34. data/test/unit/xcode/pbx_project_test.rb +21 -0
  35. data/test/unit/xcode/xc_build_configuration_test.rb +15 -0
  36. data/test/unit/xcode/xc_configuration_list_test.rb +21 -0
  37. data/test/unit/xcode_test.rb +7 -0
  38. metadata +89 -0
@@ -0,0 +1,52 @@
1
+ module Xcode
2
+ module PBXFormatter
3
+ def to_pbx
4
+ attributes.to_pbx
5
+ end
6
+ end
7
+ end
8
+
9
+ Array.class_eval do
10
+ def to_pbx
11
+ "(" + map { |e| e.to_pbx }.join(", ") + ")"
12
+ end
13
+ end
14
+
15
+ Hash.class_eval do
16
+ def to_pbx
17
+ pieces = inject([]) do |pieces, (key, value)|
18
+ pieces << "#{key.to_pbx} = #{value.to_pbx};"
19
+ end
20
+ "{#{pieces.join(" ")}}"
21
+ end
22
+ end
23
+
24
+ String.class_eval do
25
+ def to_pbx
26
+ if ['$', '(', ')', '<', '>', '-', '.', ' '].any? { |char| include? char }
27
+ %("#{self}")
28
+ elsif empty?
29
+ "\"\""
30
+ else
31
+ self
32
+ end
33
+ end
34
+ end
35
+
36
+ TrueClass.class_eval do
37
+ def to_pbx
38
+ "YES"
39
+ end
40
+ end
41
+
42
+ FalseClass.class_eval do
43
+ def to_pbx
44
+ "NO"
45
+ end
46
+ end
47
+
48
+ Fixnum.class_eval do
49
+ def to_pbx
50
+ to_s
51
+ end
52
+ end
@@ -0,0 +1,7 @@
1
+ module Xcode
2
+ class PBXFrameworksBuildPhase < PBXBuildPhase
3
+ def isa
4
+ "PBXFrameworksBuildPhase"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,47 @@
1
+ module Xcode
2
+ class PBXGroup
3
+ include Xcode::PBXFormatter
4
+
5
+ attr_reader :id, :groups
6
+
7
+ def self.new_from_directory(directory, file_type)
8
+ children = []
9
+ Dir.entries(directory).each do |entry|
10
+ next if entry =~ /^\./
11
+ filetype = File.ftype(directory + "/" + entry)
12
+ filetype = "file" if File.extname(entry) == ".framework"
13
+ case filetype
14
+ when "directory"
15
+ group = PBXGroup.new_from_directory(directory + "/" + entry, file_type)
16
+ children << group
17
+ when "file"
18
+ file_ref = Xcode::PBXFileReference.new_from_file(directory + "/" + entry, file_type)
19
+ children << file_ref
20
+ else
21
+ raise "wtf? #{directory} #{entry} #{File.ftype(directory + "/" + entry)}"
22
+ end
23
+ end
24
+ new "children" => children.map(&:id), "path" => File.basename(directory)
25
+ end
26
+
27
+ def self.new_from_subdirectory(directory, file_type)
28
+ paths = directory.split("/")
29
+ bottom = new_from_directory(directory, file_type)
30
+ paths[0..-2].reverse.inject(bottom) do |child, path|
31
+ new "path" => path, "children" => [child.id]
32
+ end
33
+ end
34
+
35
+ def initialize(attributes)
36
+ @id = Xcode.register(self)
37
+ @attributes = attributes
38
+ end
39
+
40
+ def attributes
41
+ {
42
+ "isa" => "PBXGroup",
43
+ "sourceTree" => "<group>"
44
+ }.merge @attributes
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,24 @@
1
+ module Xcode
2
+ class PBXNativeTarget
3
+ include PBXFormatter
4
+ attr_reader :id, :build_configuration_list
5
+
6
+ def initialize(name, attributes = {})
7
+ @id = Xcode.register(self)
8
+ @name = name
9
+ @attributes = attributes
10
+ end
11
+
12
+ def attributes
13
+ {
14
+ "isa" => "PBXNativeTarget",
15
+ "productName" => @name,
16
+ "name" => @name,
17
+ "productType" => "com.apple.product-type.application",
18
+ "buildRules" => [],
19
+ "dependencies" => [],
20
+ "buildPhases" => []
21
+ }.merge(@attributes)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,21 @@
1
+ module Xcode
2
+ class PBXProj
3
+ include PBXFormatter
4
+ attr_reader :groups, :project
5
+
6
+ def initialize(name)
7
+ @name = name
8
+ @groups = []
9
+ end
10
+
11
+ def attributes
12
+ {
13
+ "objects" => Xcode.objects,
14
+ "rootObject" => @project.id,
15
+ "archiveVersion" => "1",
16
+ "objectVersion" => "45",
17
+ "classes" => {}
18
+ }
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,29 @@
1
+ module Xcode
2
+ class PBXProject
3
+ include PBXFormatter
4
+ attr_reader :id, :build_configuration_list
5
+
6
+ def initialize(attributes = {})
7
+ @id = Xcode.register(self)
8
+ @attributes = attributes
9
+ end
10
+
11
+ def attributes
12
+ {
13
+ "isa" => isa,
14
+ "compatibilityVersion" => "Xcode 3.1",
15
+ "projectDirPath" => "",
16
+ "projectRoot" => "",
17
+ "targets" => [],
18
+ }.merge @attributes
19
+ end
20
+
21
+ def compatibility_version
22
+ "Xcode 3.1"
23
+ end
24
+
25
+ def isa
26
+ "PBXProject"
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,7 @@
1
+ module Xcode
2
+ class PBXResourcesBuildPhase < PBXBuildPhase
3
+ def isa
4
+ "PBXResourcesBuildPhase"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module Xcode
2
+ class PBXSourcesBuildPhase < PBXBuildPhase
3
+ def isa
4
+ "PBXSourcesBuildPhase"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,20 @@
1
+ module Xcode
2
+ class XCBuildConfiguration
3
+ include PBXFormatter
4
+ attr_reader :id, :build_settings
5
+
6
+ def initialize(name, build_settings)
7
+ @id = Xcode.register(self)
8
+ @name = name
9
+ @build_settings = build_settings
10
+ end
11
+
12
+ def attributes
13
+ {
14
+ "isa" => "XCBuildConfiguration",
15
+ "name" => @name,
16
+ "buildSettings" => @build_settings
17
+ }
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ module Xcode
2
+ class XCConfigurationList
3
+ include PBXFormatter
4
+ attr_reader :id, :build_configurations
5
+
6
+ def initialize(build_configurations = [])
7
+ @id = Xcode.register(self)
8
+ @build_configurations = build_configurations
9
+ end
10
+
11
+ def attributes
12
+ {
13
+ "isa" => "XCConfigurationList",
14
+ "defaultConfigurationIsVisible" => "0",
15
+ "defaultConfigurationName" => "Release",
16
+ "buildConfigurations" => @build_configurations.map(&:id)
17
+ }
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,140 @@
1
+ class XcodeprojGen
2
+ def self.generate(project_name = nil, config = nil)
3
+ project_name ||= File.basename(Dir.pwd)
4
+ config ||= YAML.load_file("xcodeproj.yml")
5
+ new(project_name, config).send(:generate)
6
+ end
7
+
8
+ class << self
9
+ protected :new
10
+ end
11
+
12
+ def initialize(project_name, config)
13
+ @project_name, @config = project_name, config
14
+ end
15
+
16
+ protected
17
+
18
+ def generate
19
+ pbx_proj = Xcode::PBXProj.new @project_name
20
+ @products = {}
21
+
22
+ files = build_files
23
+ products, targets = build_products_and_targets
24
+ products_group = build_products_group(products.values)
25
+ main_group = build_main_group([files, products_group].flatten)
26
+ project = build_project(main_group, targets)
27
+
28
+ pbx_proj.instance_variable_set "@project", project
29
+ FileUtils.mkdir_p "#{@project_name}.xcodeproj"
30
+ File.open("#{@project_name}.xcodeproj/project.pbxproj", "w") { |f| f.write pbx_proj.to_pbx }
31
+ end
32
+
33
+ def build_products_group(products)
34
+ Xcode::PBXGroup.new(
35
+ "name" => "Products",
36
+ "children" => products.flatten.map(&:id)
37
+ )
38
+ end
39
+
40
+ def build_main_group(children)
41
+ Xcode::PBXGroup.new(
42
+ "name" => "CustomTemplate",
43
+ "children" => [children, Xcode::IPhone::FrameworksGroup].flatten.map(&:id)
44
+ )
45
+ end
46
+
47
+ def build_project(main_group, targets)
48
+ _project_build_configurations = @config["build_configurations"].map do |name, settings|
49
+ Xcode::XCBuildConfiguration.new(name.to_s.capitalize, settings)
50
+ end
51
+ project_build_configurations = Xcode::XCConfigurationList.new(_project_build_configurations)
52
+
53
+ project = Xcode::PBXProject.new(
54
+ "mainGroup" => main_group.id,
55
+ "targets" => targets.map(&:id),
56
+ "buildConfigurationList" => project_build_configurations.id
57
+ )
58
+ end
59
+
60
+ def build_files
61
+ directory = File.expand_path(Dir.pwd)
62
+ children = []
63
+ file_type = nil
64
+ Dir.entries(Dir.pwd).each do |entry|
65
+ next if entry =~ /^\./
66
+ next if entry == "build"
67
+ next if entry =~ /xcodeproj$/
68
+ filetype = File.ftype(entry)
69
+ case filetype
70
+ when "directory"
71
+ group = Xcode::PBXGroup.new_from_directory(directory + "/" + entry, file_type)
72
+ children << group
73
+ when "file"
74
+ file_ref = Xcode::PBXFileReference.new_from_file(directory + "/" + entry, file_type)
75
+ children << file_ref
76
+ else
77
+ raise "wtf? #{directory} #{entry} #{File.ftype(directory + "/" + entry)}"
78
+ end
79
+ end
80
+ children
81
+ end
82
+
83
+ def build_products_and_targets
84
+ products = {}
85
+ targets = []
86
+ @config["targets"].each do |target_config|
87
+ x, y, z = build_target target_config, products
88
+ products[x] = y
89
+ targets << z
90
+ end
91
+ [products, targets]
92
+ end
93
+
94
+ def build_target(target_config, products_map)
95
+ target_attributes = {}
96
+ target_attributes["buildPhases"] = target_config.delete("build_phases").map do |phase_type, phase_config|
97
+ klass = case phase_type
98
+ when "sources" : Xcode::PBXSourcesBuildPhase
99
+ when "copy_files" : Xcode::PBXCopyFilesBuildPhase
100
+ when "frameworks" : Xcode::PBXFrameworksBuildPhase
101
+ else raise("#{phase_type} is not yet supported")
102
+ end
103
+ if phase_type == "frameworks"
104
+ klass.new(
105
+ phase_config.map do |framework|
106
+ if products_map[framework]
107
+ products_map[framework].create_pbx_build_file
108
+ else
109
+ Xcode::Frameworks.const_get(framework).create_pbx_build_file
110
+ end
111
+ end
112
+ )
113
+ else
114
+ klass.new(
115
+ phase_config.delete("files").map do |file_pattern|
116
+ Dir.glob(file_pattern).map do |file|
117
+ Xcode::PBXFileReference.file_reference_for_file(file).create_pbx_build_file
118
+ end
119
+ end.flatten,
120
+ phase_config
121
+ )
122
+ end
123
+ end.map(&:id)
124
+ target_attributes["buildConfigurationList"] = Xcode::XCConfigurationList.new(
125
+ target_config.delete("build_configurations").inject([]) do |configs, (name, settings)|
126
+ configs << Xcode::XCBuildConfiguration.new(name.to_s.capitalize, settings)
127
+ end
128
+ ).id
129
+ product_path = target_config["product"]["path"]
130
+ product = Xcode::PBXFileReference.new({
131
+ "includeInIndex" => "0",
132
+ "sourceTree" => "BUILT_PRODUCTS_DIR"
133
+ }.merge(target_config.delete("product")))
134
+ target_attributes["productReference"] = product.id
135
+ target = Xcode::PBXNativeTarget.new(target_config.delete("name"),
136
+ target_attributes.merge(target_config)
137
+ )
138
+ [product_path, product, target]
139
+ end
140
+ end
@@ -0,0 +1,33 @@
1
+ require File.dirname(__FILE__) + "/test_helper"
2
+
3
+ class CommandLineFoundationTest < Test::Unit::TestCase
4
+ PROJECT_DIR = File.expand_path(EXAMPLES_DIR + "/clu_foundation")
5
+
6
+ def setup
7
+ FileUtils.rm_rf "#{PROJECT_DIR}/build"
8
+ FileUtils.rm_rf "#{PROJECT_DIR}/clu_foundation.xcodeproj"
9
+ chdir { run_xcodeprojgen }
10
+ end
11
+
12
+ test "building with debug configuration" do
13
+ chdir do
14
+ system "xcodebuild -project clu_foundation.xcodeproj -sdk macosx10.5 -configuration Debug -target clu_foundation > /dev/null"
15
+ raise "failed to build debug config" unless $?.success?
16
+ end
17
+ output = `#{PROJECT_DIR}/build/Debug/clu_foundation 2>&1`
18
+ assert_equal "Hello, World!\n", output
19
+ end
20
+
21
+ test "building with release configuration" do
22
+ chdir do
23
+ system "xcodebuild -project clu_foundation.xcodeproj -sdk macosx10.5 -configuration Release -target clu_foundation > /dev/null"
24
+ raise "failed to build release config" unless $?.success?
25
+ end
26
+ output = `#{PROJECT_DIR}/build/Release/clu_foundation 2>&1`
27
+ assert_equal "Hello, World!\n", output
28
+ end
29
+
30
+ def chdir
31
+ Dir.chdir(PROJECT_DIR) { yield }
32
+ end
33
+ end
@@ -0,0 +1,33 @@
1
+ require File.dirname(__FILE__) + "/test_helper"
2
+
3
+ class CommandLineInCTest < Test::Unit::TestCase
4
+ PROJECT_DIR = File.expand_path(EXAMPLES_DIR + "/clu_c")
5
+
6
+ def setup
7
+ FileUtils.rm_rf "#{PROJECT_DIR}/build"
8
+ FileUtils.rm_rf "#{PROJECT_DIR}/clu_c.xcodeproj"
9
+ chdir { run_xcodeprojgen }
10
+ end
11
+
12
+ test "building with debug configuration" do
13
+ chdir do
14
+ system "xcodebuild -project clu_c.xcodeproj -sdk macosx10.5 -configuration Debug -target clu_c > /dev/null"
15
+ raise "failed to build debug config" unless $?.success?
16
+ end
17
+ output = `#{PROJECT_DIR}/build/Debug/clu_c`
18
+ assert_equal "Hello, World!\n", output
19
+ end
20
+
21
+ test "building with release configuration" do
22
+ chdir do
23
+ system "xcodebuild -project clu_c.xcodeproj -sdk macosx10.5 -configuration Release -target clu_c > /dev/null"
24
+ raise "failed to build release config" unless $?.success?
25
+ end
26
+ output = `#{PROJECT_DIR}/build/Release/clu_c`
27
+ assert_equal "Hello, World!\n", output
28
+ end
29
+
30
+ def chdir
31
+ Dir.chdir(PROJECT_DIR) { yield }
32
+ end
33
+ end