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.
- 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
|