framework-x-xcodeprojgen 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +12 -0
- data/Rakefile +45 -0
- data/TODO +8 -0
- data/bin/xcodeprojgen +14 -0
- data/lib/xcode.rb +56 -0
- data/lib/xcode/frameworks.rb +100 -0
- data/lib/xcode/iphone.rb +29 -0
- data/lib/xcode/iphone_build_configurations.rb +48 -0
- data/lib/xcode/pbx_build_file.rb +25 -0
- data/lib/xcode/pbx_build_phase.rb +25 -0
- data/lib/xcode/pbx_copy_files_build_phase.rb +7 -0
- data/lib/xcode/pbx_file_reference.rb +66 -0
- data/lib/xcode/pbx_formatter.rb +52 -0
- data/lib/xcode/pbx_frameworks_build_phase.rb +7 -0
- data/lib/xcode/pbx_group.rb +47 -0
- data/lib/xcode/pbx_native_target.rb +24 -0
- data/lib/xcode/pbx_proj.rb +21 -0
- data/lib/xcode/pbx_project.rb +29 -0
- data/lib/xcode/pbx_resources_build_phase.rb +7 -0
- data/lib/xcode/pbx_sources_build_phase.rb +7 -0
- data/lib/xcode/xc_build_configuration.rb +20 -0
- data/lib/xcode/xc_configuration_list.rb +20 -0
- data/lib/xcodeproj_gen.rb +140 -0
- data/test/acceptance/command_line_foundation_test.rb +33 -0
- data/test/acceptance/command_line_in_c_test.rb +33 -0
- data/test/acceptance/test_helper.rb +18 -0
- data/test/unit/test_helper.rb +13 -0
- data/test/unit/xcode/pbx_build_file_test.rb +18 -0
- data/test/unit/xcode/pbx_build_phase_test.rb +27 -0
- data/test/unit/xcode/pbx_file_reference_test.rb +74 -0
- data/test/unit/xcode/pbx_formatter_test.rb +35 -0
- data/test/unit/xcode/pbx_group_test.rb +25 -0
- data/test/unit/xcode/pbx_native_target_test.rb +13 -0
- data/test/unit/xcode/pbx_project_test.rb +21 -0
- data/test/unit/xcode/xc_build_configuration_test.rb +15 -0
- data/test/unit/xcode/xc_configuration_list_test.rb +21 -0
- data/test/unit/xcode_test.rb +7 -0
- 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,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,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
|