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
data/README.markdown
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require "rake/testtask"
|
2
|
+
|
3
|
+
task :default => :test
|
4
|
+
task :pc => %w[test gemspec:generate]
|
5
|
+
|
6
|
+
task :test => %w[test:unit test:acceptance]
|
7
|
+
namespace :test do
|
8
|
+
Rake::TestTask.new(:acceptance) do |t|
|
9
|
+
t.pattern = "test/acceptance/**/*_test.rb"
|
10
|
+
end
|
11
|
+
|
12
|
+
Rake::TestTask.new(:unit) do |t|
|
13
|
+
t.pattern = "test/unit/**/*_test.rb"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
gem_spec = Gem::Specification.new do |s|
|
18
|
+
s.name = "xcodeprojgen"
|
19
|
+
s.summary = "generates xcodeproj files"
|
20
|
+
s.version = "0.1.0"
|
21
|
+
s.author = "Dan Manges"
|
22
|
+
s.description = s.summary
|
23
|
+
s.email = "daniel.manges@gmail.com"
|
24
|
+
s.homepage = "http://github.com/framework-x/xcodeprojgen"
|
25
|
+
# s.rubyforge_project = "xcodeprojgen"
|
26
|
+
|
27
|
+
s.has_rdoc = false
|
28
|
+
|
29
|
+
s.executables = %w[xcodeprojgen]
|
30
|
+
s.files = FileList['{bin,lib,test}/**/*.rb', 'TODO', 'README.markdown', 'Rakefile'].to_a
|
31
|
+
end
|
32
|
+
|
33
|
+
task :gem => %w[test] do
|
34
|
+
Gem::Builder.new(gem_spec).build
|
35
|
+
end
|
36
|
+
|
37
|
+
namespace :gemspec do
|
38
|
+
desc "generates xcodeprojgen.gemspec"
|
39
|
+
task :generate do
|
40
|
+
File.open("xcodeprojgen.gemspec", "w") do |f|
|
41
|
+
f.puts "# this file is generated by rake gemspec:generate for github"
|
42
|
+
f.write gem_spec.to_ruby
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/TODO
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
* provide a way to add new frameworks
|
2
|
+
* clearer separation between xcodeprojgen options and xcode merge options
|
3
|
+
* including default project configurations for various project/target types
|
4
|
+
* more high-level acceptance tests and examples
|
5
|
+
* find or write an xcodeproj parser for better testing
|
6
|
+
* remove destructive parsing from xcodeprojgen
|
7
|
+
* make ignores configurable
|
8
|
+
* removing hard coding of FrameworksGroup
|
data/bin/xcodeprojgen
ADDED
data/lib/xcode.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require "digest/md5"
|
2
|
+
|
3
|
+
module Xcode
|
4
|
+
def self.objects
|
5
|
+
@objects ||= {}
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.register(object)
|
9
|
+
id = Digest::MD5.hexdigest(rand.to_s + Time.now.usec.to_s).upcase[0,24]
|
10
|
+
objects[id] = object
|
11
|
+
id
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
Symbol.class_eval do
|
16
|
+
def to_proc
|
17
|
+
Proc.new { |o| o.send(self) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
$LOAD_PATH << File.dirname(__FILE__)
|
22
|
+
require "xcode/pbx_formatter"
|
23
|
+
|
24
|
+
require "xcode/pbx_build_file"
|
25
|
+
require "xcode/pbx_file_reference"
|
26
|
+
require "xcode/pbx_group"
|
27
|
+
require "xcode/pbx_proj"
|
28
|
+
require "xcode/pbx_project"
|
29
|
+
require "xcode/pbx_native_target"
|
30
|
+
require "xcode/pbx_build_phase"
|
31
|
+
require "xcode/pbx_resources_build_phase"
|
32
|
+
require "xcode/pbx_copy_files_build_phase"
|
33
|
+
require "xcode/pbx_sources_build_phase"
|
34
|
+
require "xcode/pbx_frameworks_build_phase"
|
35
|
+
require "xcode/xc_configuration_list"
|
36
|
+
require "xcode/xc_build_configuration"
|
37
|
+
|
38
|
+
require "xcode/frameworks"
|
39
|
+
require "xcode/iphone"
|
40
|
+
__END__
|
41
|
+
PBXBuildFile
|
42
|
+
PBXFileReference
|
43
|
+
PBXGroup
|
44
|
+
|
45
|
+
PBXProject
|
46
|
+
- PBXNativeTarget
|
47
|
+
-- PBXFrameworksBuildPhase
|
48
|
+
-- PBXResourcesBuildPhase
|
49
|
+
-- PBXSourcesBuildPhase
|
50
|
+
-- XCConfigurationList
|
51
|
+
--- XCBuildConfiguration
|
52
|
+
|
53
|
+
target has_many :build_phases
|
54
|
+
project has_many :targets
|
55
|
+
configuration_list has_many :build_configurations
|
56
|
+
target has_ony :buildConfigurationList
|
@@ -0,0 +1,100 @@
|
|
1
|
+
module Xcode
|
2
|
+
module Frameworks
|
3
|
+
AddressBook = Xcode::PBXFileReference.new(
|
4
|
+
"lastKnownFileType" => "wrapper.framework",
|
5
|
+
"name" => "AddressBook.framework",
|
6
|
+
"path" => "System/Library/Frameworks/AddressBook.framework",
|
7
|
+
"sourceTree" => "SDKROOT"
|
8
|
+
)
|
9
|
+
AddressBookUI = Xcode::PBXFileReference.new(
|
10
|
+
"lastKnownFileType" => "wrapper.framework",
|
11
|
+
"name" => "AddressBookUI.framework",
|
12
|
+
"path" => "System/Library/Frameworks/AddressBookUI.framework",
|
13
|
+
"sourceTree" => "SDKROOT"
|
14
|
+
)
|
15
|
+
AudioToolbox = Xcode::PBXFileReference.new(
|
16
|
+
"lastKnownFileType" => "wrapper.framework",
|
17
|
+
"name" => "AudioToolbox.framework",
|
18
|
+
"path" => "System/Library/Frameworks/AudioToolbox.framework",
|
19
|
+
"sourceTree" => "SDKROOT"
|
20
|
+
)
|
21
|
+
CoreGraphics = Xcode::PBXFileReference.new(
|
22
|
+
"lastKnownFileType" => "wrapper.framework",
|
23
|
+
"name" => "XCoreGraphics.framework",
|
24
|
+
"path" => "System/Library/Frameworks/CoreGraphics.framework",
|
25
|
+
"sourceTree" => "SDKROOT"
|
26
|
+
)
|
27
|
+
CoreLocation = Xcode::PBXFileReference.new(
|
28
|
+
"lastKnownFileType" => "wrapper.framework",
|
29
|
+
"name" => "CoreLocation.framework",
|
30
|
+
"path" => "System/Library/Frameworks/CoreLocation.framework",
|
31
|
+
"sourceTree" => "SDKROOT"
|
32
|
+
)
|
33
|
+
Foundation = Xcode::PBXFileReference.new(
|
34
|
+
"lastKnownFileType" => "wrapper.framework",
|
35
|
+
"name" => "Foundation.framework",
|
36
|
+
"path" => "System/Library/Frameworks/Foundation.framework",
|
37
|
+
"sourceTree" => "SDKROOT"
|
38
|
+
)
|
39
|
+
FrameworkX = Xcode::PBXFileReference.new(
|
40
|
+
"lastKnownFileType" => "wrapper.framework",
|
41
|
+
"name" => "FrameworkX.framework",
|
42
|
+
"path" => "frameworks/FrameworkX.framework",
|
43
|
+
"sourceTree" => "<group>"
|
44
|
+
)
|
45
|
+
FrameworkXCore = Xcode::PBXFileReference.new(
|
46
|
+
"lastKnownFileType" => "wrapper.framework",
|
47
|
+
"name" => "FrameworkXCore.framework",
|
48
|
+
"path" => "frameworks/FrameworkXCore.framework",
|
49
|
+
"sourceTree" => "<group>"
|
50
|
+
)
|
51
|
+
LibICU = Xcode::PBXFileReference.new(
|
52
|
+
"lastKnownFileType" => "wrapper.dylib",
|
53
|
+
"name" => "libicucore.dylib",
|
54
|
+
"path" => "usr/lib/libicucore.dylib",
|
55
|
+
"sourceTree" => "SDKROOT"
|
56
|
+
)
|
57
|
+
LibSQLite = Xcode::PBXFileReference.new(
|
58
|
+
"lastKnownFileType" => "wrapper.dylib",
|
59
|
+
"name" => "libsqlite3.0.dylib",
|
60
|
+
"path" => "/usr/lib/libsqlite3.0.dylib",
|
61
|
+
"sourceTree" => "SDKROOT"
|
62
|
+
)
|
63
|
+
LibXML = Xcode::PBXFileReference.new(
|
64
|
+
"lastKnownFileType" => "wrapper.dylib",
|
65
|
+
"name" => "libxml2.2.dylib",
|
66
|
+
"path" => "usr/lib/libxml2.2.dylib",
|
67
|
+
"sourceTree" => "SDKROOT"
|
68
|
+
)
|
69
|
+
OpenGLES = Xcode::PBXFileReference.new(
|
70
|
+
"lastKnownFileType" => "wrapper.framework",
|
71
|
+
"name" => "OpenGLES.framework",
|
72
|
+
"path" => "System/Library/Frameworks/OpenGLES.framework",
|
73
|
+
"sourceTree" => "SDKROOT"
|
74
|
+
)
|
75
|
+
Security = Xcode::PBXFileReference.new(
|
76
|
+
"lastKnownFileType" => "wrapper.framework",
|
77
|
+
"name" => "Foundation.framework",
|
78
|
+
"path" => "System/Library/Frameworks/Security.framework",
|
79
|
+
"sourceTree" => "SDKROOT"
|
80
|
+
)
|
81
|
+
SenTestingKit = Xcode::PBXFileReference.new(
|
82
|
+
"lastKnownFileType" => "wrapper.framework",
|
83
|
+
"name" => "SenTestingKit.framework",
|
84
|
+
"path" => "/Developer/Library/Frameworks/SenTestingKit.framework",
|
85
|
+
"sourceTree" => "<absolute>"
|
86
|
+
)
|
87
|
+
QuartzCore = Xcode::PBXFileReference.new(
|
88
|
+
"lastKnownFileType" => "wrapper.framework",
|
89
|
+
"name" => "QuartzCore.framework",
|
90
|
+
"path" => "System/Library/Frameworks/QuartzCore.framework",
|
91
|
+
"sourceTree" => "SDKROOT"
|
92
|
+
)
|
93
|
+
UIKit = Xcode::PBXFileReference.new(
|
94
|
+
"lastKnownFileType" => "wrapper.framework",
|
95
|
+
"name" => "UIKit.framework",
|
96
|
+
"path" => "System/Library/Frameworks/UIKit.framework",
|
97
|
+
"sourceTree" => "SDKROOT"
|
98
|
+
)
|
99
|
+
end
|
100
|
+
end
|
data/lib/xcode/iphone.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
module Xcode
|
2
|
+
module IPhone
|
3
|
+
LinkedFrameworksGroup = Xcode::PBXGroup.new(
|
4
|
+
"name" => "Linked Frameworks",
|
5
|
+
"children" => [
|
6
|
+
# ::Xcode::Frameworks::AddressBook,
|
7
|
+
# ::Xcode::Frameworks::AddressBookUI,
|
8
|
+
# ::Xcode::Frameworks::AudioToolbox,
|
9
|
+
# ::Xcode::Frameworks::CoreGraphics,
|
10
|
+
# ::Xcode::Frameworks::CoreLocation,
|
11
|
+
::Xcode::Frameworks::Foundation,
|
12
|
+
# ::Xcode::Frameworks::FrameworkX,
|
13
|
+
# ::Xcode::Frameworks::FrameworkXCore,
|
14
|
+
::Xcode::Frameworks::LibICU,
|
15
|
+
# ::Xcode::Frameworks::LibSQLite,
|
16
|
+
# ::Xcode::Frameworks::LibXML,
|
17
|
+
# ::Xcode::Frameworks::OpenGLES,
|
18
|
+
# ::Xcode::Frameworks::QuartzCore,
|
19
|
+
# ::Xcode::Frameworks::Security,
|
20
|
+
# ::Xcode::Frameworks::SenTestingKit,
|
21
|
+
# ::Xcode::Frameworks::UIKit
|
22
|
+
].map(&:id)
|
23
|
+
)
|
24
|
+
FrameworksGroup = Xcode::PBXGroup.new(
|
25
|
+
"name" => "Frameworks",
|
26
|
+
"children" => [LinkedFrameworksGroup].map(&:id)
|
27
|
+
)
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Xcode
|
2
|
+
module IPhoneBuildConfigurations
|
3
|
+
TargetDebug = {
|
4
|
+
"COPY_PHASE_STRIP" => "NO",
|
5
|
+
"FRAMEWORK_SEARCH_PATHS" => [%Q("$(inherited)"), %("\\"$(SRCROOT)/frameworks\\"")],
|
6
|
+
"GCC_DYNAMIC_NO_PIC" => "NO",
|
7
|
+
"GCC_ENABLE_FIX_AND_CONTINUE" => "NO",
|
8
|
+
"GCC_OPTIMIZATION_LEVEL" => "0",
|
9
|
+
"GCC_PRECOMPILE_PREFIX_HEADER" => "YES",
|
10
|
+
"GCC_PREFIX_HEADER" => "Prefix.pch",
|
11
|
+
# "GCC_WARN_INHIBIT_ALL_WARNINGS" => "YES",
|
12
|
+
"GCC_WARN_INHIBIT_ALL_WARNINGS" => "NO",
|
13
|
+
"HEADER_SEARCH_PATHS" => "/usr/include/libxml2",
|
14
|
+
"INFOPLIST_FILE" => "Info.plist"
|
15
|
+
}
|
16
|
+
TargetRelease = {
|
17
|
+
"COPY_PHASE_STRIP" => "YES",
|
18
|
+
"FRAMEWORK_SEARCH_PATHS" => [%Q("$(inherited)"), %("\\"$(SRCROOT)/frameworks\\"")],
|
19
|
+
"GCC_PRECOMPILE_PREFIX_HEADER" => "YES",
|
20
|
+
"GCC_PREFIX_HEADER" => "Prefix.pch",
|
21
|
+
"HEADER_SEARCH_PATHS" => "/usr/include/libxml2",
|
22
|
+
"INFOPLIST_FILE" => "Info.plist"
|
23
|
+
}
|
24
|
+
ProjectDebug = {
|
25
|
+
"ALWAYS_SEARCH_USER_PATHS" => "NO",
|
26
|
+
"ARCHS" => %q("$(ARCHS_STANDARD_32_BIT)"),
|
27
|
+
"FRAMEWORK_SEARCH_PATHS" => [%Q("$(inherited)"), %("\\"$(SRCROOT)/frameworks\\""), "/Developer/Library/Frameworks"],
|
28
|
+
"GCC_C_LANGUAGE_STANDARD" => "gnu99",
|
29
|
+
"GCC_WARN_ABOUT_RETURN_TYPE" => "YES",
|
30
|
+
"GCC_WARN_UNUSED_VARIABLE" => "YES",
|
31
|
+
# "GCC_WARN_INHIBIT_ALL_WARNINGS" => "YES",
|
32
|
+
"GCC_WARN_INHIBIT_ALL_WARNINGS" => "NO",
|
33
|
+
"LIBRARY_SEARCH_PATHS" => [%Q("$(inherited)")],
|
34
|
+
"ONLY_ACTIVE_ARCH" => "YES",
|
35
|
+
"PREBINDING" => "NO",
|
36
|
+
"HEADER_SEARCH_PATHS" => "/usr/include/libxml2",
|
37
|
+
"SDKROOT" => "iphoneos2.0"
|
38
|
+
}
|
39
|
+
ProjectRelease = {
|
40
|
+
"ARCHS" => %q("$(ARCHS_STANDARD_32_BIT)"),
|
41
|
+
"GCC_C_LANGUAGE_STANDARD" => "gnu99",
|
42
|
+
"FRAMEWORK_SEARCH_PATHS" => [%Q("$(inherited)"), %("\\"$(SRCROOT)/frameworks\\""), "/Developer/Library/Frameworks"],
|
43
|
+
"PREBINDING" => "NO",
|
44
|
+
"HEADER_SEARCH_PATHS" => "/usr/include/libxml2",
|
45
|
+
"SDKROOT" => "iphoneos2.0"
|
46
|
+
}
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Xcode
|
2
|
+
class PBXBuildFile
|
3
|
+
include PBXFormatter
|
4
|
+
attr_reader :id, :file_ref
|
5
|
+
|
6
|
+
def self.new_from_file(file, file_type = nil)
|
7
|
+
file_ref = Xcode::PBXFileReference.new_from_file(file, file_type)
|
8
|
+
new file_ref, file_type
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(file_ref, file_type = nil)
|
12
|
+
@id = Xcode.register(self)
|
13
|
+
@file_ref = file_ref
|
14
|
+
@file_type = file_type
|
15
|
+
end
|
16
|
+
|
17
|
+
def attributes
|
18
|
+
{"fileRef" => @file_ref.id, "isa" => isa}
|
19
|
+
end
|
20
|
+
|
21
|
+
def isa
|
22
|
+
"PBXBuildFile"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Xcode
|
2
|
+
class PBXBuildPhase
|
3
|
+
include PBXFormatter
|
4
|
+
attr_reader :id, :files
|
5
|
+
|
6
|
+
def initialize(files = [], attributes = {})
|
7
|
+
@id = Xcode.register(self)
|
8
|
+
@files = files
|
9
|
+
@attributes = attributes
|
10
|
+
end
|
11
|
+
|
12
|
+
def attributes
|
13
|
+
{
|
14
|
+
"isa" => isa,
|
15
|
+
"buildActionMask" => "2147483647",
|
16
|
+
"runOnlyForDeploymentPostprocesssing" => "0",
|
17
|
+
"files" => files.map(&:id)
|
18
|
+
}.merge(@attributes)
|
19
|
+
end
|
20
|
+
|
21
|
+
def isa
|
22
|
+
raise NotImplementedError, "subclasses must implement isa"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Xcode
|
2
|
+
class PBXFileReference
|
3
|
+
include Xcode::PBXFormatter
|
4
|
+
attr_reader :id
|
5
|
+
|
6
|
+
def self.file_reference_for_file(file)
|
7
|
+
@file_references ||= {}
|
8
|
+
@file_references[File.expand_path(file)] || raise("cannot find file ref for #{file}")
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.new_from_file(file, file_type = nil)
|
12
|
+
attributes = {}
|
13
|
+
attributes["path"] = File.basename(file)
|
14
|
+
case File.extname(file)
|
15
|
+
when ".h"
|
16
|
+
attributes.merge! "lastKnownFileType" => "sourcecode.c.h", "fileEncoding" => "4"
|
17
|
+
when ".m"
|
18
|
+
attributes.merge! "lastKnownFileType" => "sourcecode.c.objc", "fileEncoding" => "4"
|
19
|
+
when ".a"
|
20
|
+
attributes.merge! "lastKnownFileType" => "archive.ar", "fileEncoding" => "4"
|
21
|
+
when ".plist"
|
22
|
+
attributes.merge! "lastKnownFileType" => "text.plist.xml", "fileEncoding" => "4"
|
23
|
+
when ".strings"
|
24
|
+
attributes.merge! "lastKnownFileType" => "text.plist.strings", "fileEncoding" => "10"
|
25
|
+
when ".png", ".tiff", ".jpg", ".jpeg"
|
26
|
+
ext = File.extname(file)
|
27
|
+
ext = ".jpeg" if ext == ".jpg"
|
28
|
+
attributes.merge! "lastKnownFileType" => "image#{ext}"
|
29
|
+
else
|
30
|
+
attributes.merge! "lastKnownFileType" => "file"
|
31
|
+
end
|
32
|
+
instance = new attributes, file_type
|
33
|
+
@file_references ||= {}
|
34
|
+
@file_references[File.expand_path(file)] = instance
|
35
|
+
instance
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize(attributes, file_type = nil)
|
39
|
+
@file_type = file_type
|
40
|
+
@id = Xcode.register(self)
|
41
|
+
@attributes = attributes
|
42
|
+
end
|
43
|
+
|
44
|
+
def attributes
|
45
|
+
result = {
|
46
|
+
"isa" => isa,
|
47
|
+
"sourceTree" => "<group>"
|
48
|
+
}.merge @attributes
|
49
|
+
result
|
50
|
+
end
|
51
|
+
|
52
|
+
def isa
|
53
|
+
"PBXFileReference"
|
54
|
+
end
|
55
|
+
|
56
|
+
def create_pbx_build_file
|
57
|
+
Xcode::PBXBuildFile.new(self, @file_type)
|
58
|
+
end
|
59
|
+
|
60
|
+
protected
|
61
|
+
|
62
|
+
def file_extension
|
63
|
+
File.extname(attributes["path"]).gsub('"',"")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|