framework-x-xcodeprojgen 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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