ambient-xcode 0.6.0 → 0.7.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.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/README.md +13 -3
- data/ambient-xcode.gemspec +1 -1
- data/bin/ambient +32 -2
- data/example/Ambientfile +2 -2
- data/example/Ambientfile-objc +2 -2
- data/lib/ambient.rb +10 -160
- data/lib/ambient/application.rb +165 -0
- data/lib/ambient/capabilities_helper.rb +71 -0
- data/lib/ambient/dsl.rb +4 -0
- data/lib/ambient/dsl/main_scope.rb +160 -0
- data/lib/ambient/dsl/plist_scope.rb +21 -0
- data/lib/ambient/dsl/scheme_scope.rb +40 -0
- data/lib/ambient/dsl/target_scope.rb +36 -0
- data/lib/ambient/init.rb +33 -0
- data/lib/ambient/plist_helper.rb +29 -0
- data/lib/ambient/project_creation.rb +83 -0
- data/lib/ambient/project_helper.rb +113 -0
- data/templates/ios/PRODUCTNAME.xcodeproj/project.pbxproj +295 -0
- data/templates/ios/PRODUCTNAME.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- data/templates/ios/PRODUCTNAME/AppDelegate.swift +33 -0
- data/templates/ios/PRODUCTNAME/Assets.xcassets/AppIcon.appiconset/Contents.json +68 -0
- data/templates/ios/PRODUCTNAME/Base.lproj/LaunchScreen.storyboard +27 -0
- data/templates/ios/PRODUCTNAME/Base.lproj/Main.storyboard +25 -0
- data/templates/ios/PRODUCTNAME/Info.plist +47 -0
- data/templates/ios/PRODUCTNAME/ViewController.swift +13 -0
- metadata +29 -13
- data/lib/capabilities_helper.rb +0 -71
- data/lib/dsl.rb +0 -229
- data/lib/plist_helper.rb +0 -29
- data/lib/project_helper.rb +0 -112
@@ -0,0 +1,71 @@
|
|
1
|
+
module Ambient
|
2
|
+
class CapabilitiesHelper
|
3
|
+
CAPABILITIES = {
|
4
|
+
application_group_ios: "ApplicationGroups.iOS",
|
5
|
+
background_modes: "BackgroundModes",
|
6
|
+
data_protection: "DataProtection",
|
7
|
+
game_center: "GameCenter",
|
8
|
+
healthkit: "HealthKit",
|
9
|
+
health_kit: "HealthKit",
|
10
|
+
homekit: "HomeKit",
|
11
|
+
home_kit: "HomeKit",
|
12
|
+
in_app_purchase: "InAppPurchase",
|
13
|
+
inter_app_audio: "InterAppAudio",
|
14
|
+
keychain: "Keychain",
|
15
|
+
maps: "Maps.iOS",
|
16
|
+
apple_pay: "OMC",
|
17
|
+
passbook: "Passbook",
|
18
|
+
wallet: "Passbook",
|
19
|
+
safari_keychain: "SafariKeychain",
|
20
|
+
personal_vpn: "VPNLite",
|
21
|
+
wireless_accessory_configuration: "WAC",
|
22
|
+
icloud: "iCloud"
|
23
|
+
}
|
24
|
+
|
25
|
+
def initialize(project, target)
|
26
|
+
@project = project
|
27
|
+
@target = target
|
28
|
+
end
|
29
|
+
|
30
|
+
def clear_capabilities
|
31
|
+
capabilities.delete_if { |_, _| true } if capabilities
|
32
|
+
end
|
33
|
+
|
34
|
+
def enable_capability(capability)
|
35
|
+
capabilities[capability_key(capability)] = {"enabled"=>"1"}
|
36
|
+
end
|
37
|
+
|
38
|
+
def disable_capability(capability)
|
39
|
+
capabilities.delete(capability_key(capability))
|
40
|
+
end
|
41
|
+
|
42
|
+
def set_development_team(team)
|
43
|
+
target_attributes["DevelopmentTeam"] = team
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def capabilities
|
49
|
+
target_attributes["SystemCapabilities"]
|
50
|
+
end
|
51
|
+
|
52
|
+
def target_attributes
|
53
|
+
unless @project.root_object.attributes["TargetAttributes"]
|
54
|
+
@project.root_object.attributes["TargetAttributes"] = {}
|
55
|
+
end
|
56
|
+
|
57
|
+
unless @project.root_object.attributes["TargetAttributes"][@target.uuid]
|
58
|
+
@project.root_object.attributes["TargetAttributes"][@target.uuid] = {}
|
59
|
+
end
|
60
|
+
|
61
|
+
@project.root_object.attributes["TargetAttributes"][@target.uuid]
|
62
|
+
end
|
63
|
+
|
64
|
+
def capability_key(capability)
|
65
|
+
capability = CAPABILITIES[capability] || capability.to_s
|
66
|
+
prefix = "com.apple."
|
67
|
+
capability = "#{prefix}#{capability}" unless capability.start_with? prefix
|
68
|
+
capability
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/lib/ambient/dsl.rb
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
module Ambient
|
2
|
+
module DSL
|
3
|
+
class MainScope
|
4
|
+
attr_reader :application
|
5
|
+
|
6
|
+
def initialize(application)
|
7
|
+
@application = application
|
8
|
+
end
|
9
|
+
|
10
|
+
def use_settings_from(filename)
|
11
|
+
application.configure { run_ambientfile(filename) }
|
12
|
+
end
|
13
|
+
|
14
|
+
def option(name, value)
|
15
|
+
application.configure { set_option(name, value) }
|
16
|
+
end
|
17
|
+
|
18
|
+
def base_ios_settings!(project_name, prefix: "", tests: false, ui_tests: false, swift: true, target: nil, test_target: nil, ui_test_target: nil)
|
19
|
+
use_defaults_for_everything_not_specified_in_this_file!
|
20
|
+
enable_default_warnings!
|
21
|
+
|
22
|
+
target ||= "project_name"
|
23
|
+
test_target ||= "#{project_name}Tests"
|
24
|
+
ui_test_target ||= "#{project_name}UITests"
|
25
|
+
tests = true if test_target
|
26
|
+
ui_tests = true if ui_test_target
|
27
|
+
|
28
|
+
option "ALWAYS_SEARCH_USER_PATHS", false
|
29
|
+
option "CLANG_CXX_LANGUAGE_STANDARD", "gnu++0x"
|
30
|
+
option "CLANG_CXX_LIBRARY", "libc++"
|
31
|
+
option "CLANG_ENABLE_MODULES", true
|
32
|
+
option "CLANG_ENABLE_OBJC_ARC", true
|
33
|
+
|
34
|
+
option "CODE_SIGN_IDENTITY[sdk=iphoneos*]", "iPhone Developer"
|
35
|
+
option "COPY_PHASE_STRIP", false
|
36
|
+
|
37
|
+
option "ENABLE_STRICT_OBJC_MSGSEND", true
|
38
|
+
option "GCC_C_LANGUAGE_STANDARD", "gnu99"
|
39
|
+
option "GCC_NO_COMMON_BLOCKS", true
|
40
|
+
option "SDKROOT", "iphoneos"
|
41
|
+
option "IPHONEOS_DEPLOYMENT_TARGET", "9.0"
|
42
|
+
|
43
|
+
scheme "Debug" do
|
44
|
+
option "DEBUG_INFORMATION_FORMAT", "dwarf"
|
45
|
+
option "ENABLE_TESTABILITY", true
|
46
|
+
option "MTL_ENABLE_DEBUG_INFO", true
|
47
|
+
option "ONLY_ACTIVE_ARCH", true
|
48
|
+
option "GCC_DYNAMIC_NO_PIC", false
|
49
|
+
option "GCC_OPTIMIZATION_LEVEL", "0"
|
50
|
+
option "GCC_PREPROCESSOR_DEFINITIONS", ["DEBUG=1", "$(inherited)"]
|
51
|
+
option "SWIFT_OPTIMIZATION_LEVEL", "-Onone" if swift
|
52
|
+
end
|
53
|
+
|
54
|
+
scheme "Release" do
|
55
|
+
option "DEBUG_INFORMATION_FORMAT", "dwarf-with-dsym"
|
56
|
+
option "ENABLE_NS_ASSERTIONS", false
|
57
|
+
option "MTL_ENABLE_DEBUG_INFO", false
|
58
|
+
option "VALIDATE_PRODUCT", true
|
59
|
+
end
|
60
|
+
|
61
|
+
target project_name do
|
62
|
+
option "INFOPLIST_FILE", "#{project_name}/Info.plist"
|
63
|
+
option "PRODUCT_BUNDLE_IDENTIFIER", "#{prefix}#{project_name}"
|
64
|
+
option "ASSETCATALOG_COMPILER_APPICON_NAME", "AppIcon"
|
65
|
+
option "LD_RUNPATH_SEARCH_PATHS", "$(inherited) @executable_path/Frameworks"
|
66
|
+
option "PRODUCT_NAME", "$(TARGET_NAME)"
|
67
|
+
end
|
68
|
+
|
69
|
+
if tests
|
70
|
+
target test_target do
|
71
|
+
option "INFOPLIST_FILE", "#{project_name}Tests/Info.plist"
|
72
|
+
option "BUNDLE_LOADER", "$(TEST_HOST)"
|
73
|
+
option "TEST_HOST", "$(BUILT_PRODUCTS_DIR)/#{project_name}.app/#{project_name}"
|
74
|
+
option "PRODUCT_BUNDLE_IDENTIFIER", "#{prefix}#{project_name}Tests"
|
75
|
+
option "LD_RUNPATH_SEARCH_PATHS", "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"
|
76
|
+
option "PRODUCT_NAME", "$(TARGET_NAME)"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
if ui_tests
|
81
|
+
target ui_test_target do
|
82
|
+
option "INFOPLIST_FILE", "#{project_name}UITests/Info.plist"
|
83
|
+
option "TEST_TARGET_NAME", "#{project_name}"
|
84
|
+
option "PRODUCT_BUNDLE_IDENTIFIER", "#{prefix}#{project_name}UITests"
|
85
|
+
option "LD_RUNPATH_SEARCH_PATHS", "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"
|
86
|
+
option "USES_XCTRUNNER", "YES"
|
87
|
+
option "PRODUCT_NAME", "$(TARGET_NAME)"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def enable_extra_warnings_and_static_analyser!
|
93
|
+
warnings = %w(GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED
|
94
|
+
GCC_WARN_MISSING_PARENTHESES
|
95
|
+
GCC_WARN_ABOUT_RETURN_TYPE
|
96
|
+
GCC_WARN_SIGN_COMPARE
|
97
|
+
GCC_WARN_CHECK_SWITCH_STATEMENTS
|
98
|
+
GCC_WARN_UNUSED_FUNCTION
|
99
|
+
GCC_WARN_UNUSED_LABEL
|
100
|
+
GCC_WARN_UNUSED_VALUE
|
101
|
+
GCC_WARN_UNUSED_VARIABLE
|
102
|
+
GCC_WARN_SHADOW
|
103
|
+
GCC_WARN_64_TO_32_BIT_CONVERSION
|
104
|
+
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS
|
105
|
+
GCC_WARN_UNDECLARED_SELECTOR
|
106
|
+
GCC_WARN_TYPECHECK_CALLS_TO_PRINTF
|
107
|
+
GCC_WARN_UNINITIALIZED_AUTOS
|
108
|
+
CLANG_WARN_INT_CONVERSION
|
109
|
+
CLANG_WARN_ENUM_CONVERSION
|
110
|
+
CLANG_WARN_CONSTANT_CONVERSION
|
111
|
+
CLANG_WARN_BOOL_CONVERSION
|
112
|
+
CLANG_WARN_EMPTY_BODY
|
113
|
+
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION
|
114
|
+
CLANG_WARN__DUPLICATE_METHOD_MATCH
|
115
|
+
GCC_WARN_64_TO_32_BIT_CONVERSION
|
116
|
+
RUN_CLANG_STATIC_ANALYZER
|
117
|
+
GCC_TREAT_WARNINGS_AS_ERRORS)
|
118
|
+
warnings.each { |w| option(w, true) }
|
119
|
+
end
|
120
|
+
|
121
|
+
def enable_default_warnings!
|
122
|
+
truthy = %w(CLANG_WARN_BOOL_CONVERSION
|
123
|
+
CLANG_WARN_CONSTANT_CONVERSION
|
124
|
+
CLANG_WARN_EMPTY_BODY
|
125
|
+
CLANG_WARN_ENUM_CONVERSION
|
126
|
+
CLANG_WARN_INT_CONVERSION
|
127
|
+
CLANG_WARN_UNREACHABLE_CODE
|
128
|
+
CLANG_WARN__DUPLICATE_METHOD_MATCH
|
129
|
+
GCC_WARN_64_TO_32_BIT_CONVERSION
|
130
|
+
GCC_WARN_UNDECLARED_SELECTOR
|
131
|
+
GCC_WARN_UNUSED_FUNCTION
|
132
|
+
GCC_WARN_UNUSED_VARIABLE)
|
133
|
+
error = %w(CLANG_WARN_DIRECT_OBJC_ISA_USAGE
|
134
|
+
CLANG_WARN_OBJC_ROOT_CLASS
|
135
|
+
GCC_WARN_ABOUT_RETURN_TYPE)
|
136
|
+
aggressive = %w(GCC_WARN_UNINITIALIZED_AUTOS)
|
137
|
+
|
138
|
+
truthy.each { |w| option(w, true) }
|
139
|
+
error.each { |w| option(w, "YES_ERROR") }
|
140
|
+
aggressive.each { |w| option(w, "YES_AGGRESSIVE") }
|
141
|
+
end
|
142
|
+
|
143
|
+
def target(name, &block)
|
144
|
+
TargetScope.new(application, name).configure(&block)
|
145
|
+
end
|
146
|
+
|
147
|
+
def use_defaults_for_everything_not_specified_in_this_file!
|
148
|
+
application.configure { @use_defaults = true }
|
149
|
+
end
|
150
|
+
|
151
|
+
def scheme(name, parent: nil, &block)
|
152
|
+
SchemeScope.new(application, nil, name, parent).configure(&block)
|
153
|
+
end
|
154
|
+
|
155
|
+
def plist(path, &block)
|
156
|
+
PlistScope.new(application, path).configure(&block)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Ambient
|
2
|
+
module DSL
|
3
|
+
class PlistScope
|
4
|
+
attr_reader :application,
|
5
|
+
:helper
|
6
|
+
|
7
|
+
def initialize(application, path)
|
8
|
+
@application = application
|
9
|
+
@helper = PlistHelper.new(path)
|
10
|
+
end
|
11
|
+
|
12
|
+
def configure(&block)
|
13
|
+
instance_eval(&block)
|
14
|
+
end
|
15
|
+
|
16
|
+
def entry(key, value)
|
17
|
+
helper.add_entry(key, value)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Ambient
|
2
|
+
module DSL
|
3
|
+
class SchemeScope
|
4
|
+
attr_reader :application
|
5
|
+
|
6
|
+
def initialize(application, target, name, parent)
|
7
|
+
@application = application
|
8
|
+
@target = target
|
9
|
+
@name = name
|
10
|
+
@parent = parent
|
11
|
+
|
12
|
+
application.configure do
|
13
|
+
set_parent_scheme(
|
14
|
+
target: target && target.name,
|
15
|
+
child: name,
|
16
|
+
parent: parent
|
17
|
+
)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def configure(&block)
|
22
|
+
if block
|
23
|
+
instance_eval(&block)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def option(option_name, value)
|
28
|
+
target = @target
|
29
|
+
name = @name
|
30
|
+
parent = @parent
|
31
|
+
|
32
|
+
if target
|
33
|
+
application.configure { set_option(option_name, value, target: target.name, scheme: name, parent: parent) }
|
34
|
+
else
|
35
|
+
application.configure { set_option(option_name, value, scheme: name, parent: parent) }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Ambient
|
2
|
+
module DSL
|
3
|
+
class TargetScope
|
4
|
+
attr_reader :application,
|
5
|
+
:name
|
6
|
+
|
7
|
+
def initialize(application, name)
|
8
|
+
@application = application
|
9
|
+
@name = name
|
10
|
+
end
|
11
|
+
|
12
|
+
def configure(&block)
|
13
|
+
instance_eval(&block)
|
14
|
+
end
|
15
|
+
|
16
|
+
def option(option_name, value)
|
17
|
+
target_name = @name
|
18
|
+
application.configure { set_option(option_name, value, target: target_name) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def scheme(name, parent: nil, &block)
|
22
|
+
SchemeScope.new(application, self, name, parent).configure(&block)
|
23
|
+
end
|
24
|
+
|
25
|
+
def capability(capability_name)
|
26
|
+
target_name = @name
|
27
|
+
application.configure { set_capability(target_name, capability_name) }
|
28
|
+
end
|
29
|
+
|
30
|
+
def development_team(team_name)
|
31
|
+
target_name = @name
|
32
|
+
application.configure { set_development_team(target_name, team_name) }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/ambient/init.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
module Ambient
|
2
|
+
class Init
|
3
|
+
attr_reader :path,
|
4
|
+
:project_name,
|
5
|
+
:project_prefix
|
6
|
+
|
7
|
+
def initialize(path = nil, project_name = nil, project_prefix = nil)
|
8
|
+
@path = path || Dir.pwd
|
9
|
+
@project_name = project_name || "MyProject"
|
10
|
+
@project_prefix = project_prefix || "com.#{@project_name.downcase}."
|
11
|
+
end
|
12
|
+
|
13
|
+
def create_ambientfile
|
14
|
+
puts "# Creating Ambientfile..."
|
15
|
+
write_file
|
16
|
+
puts "File created at #{filepath}"
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def write_file
|
22
|
+
File.open(filepath, 'w') { |file| file.write(contents) }
|
23
|
+
end
|
24
|
+
|
25
|
+
def filepath
|
26
|
+
"#{path}/Ambientfile"
|
27
|
+
end
|
28
|
+
|
29
|
+
def contents
|
30
|
+
"base_ios_settings! \"#{project_name}\", prefix: \"#{project_prefix}\"\n"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Ambient
|
2
|
+
class PlistHelper
|
3
|
+
attr_reader :path
|
4
|
+
|
5
|
+
def initialize(path)
|
6
|
+
@path = path
|
7
|
+
end
|
8
|
+
|
9
|
+
def add_entry(key, value)
|
10
|
+
plist_as_dictionary[key] = value
|
11
|
+
puts "applying to plist: #{path} #{key}"
|
12
|
+
save
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def plist_as_dictionary
|
18
|
+
@plist_as_dictionary ||= Plist::parse_xml(path)
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_plist
|
22
|
+
plist_as_dictionary.to_plist
|
23
|
+
end
|
24
|
+
|
25
|
+
def save
|
26
|
+
File.open(path, 'w') { |file| file.write(to_plist) }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Ambient
|
2
|
+
class ProjectCreation
|
3
|
+
attr_reader :path,
|
4
|
+
:name
|
5
|
+
|
6
|
+
def initialize(path, name)
|
7
|
+
@path = path
|
8
|
+
@name = name.gsub(/\W/, "")
|
9
|
+
check_name!
|
10
|
+
end
|
11
|
+
|
12
|
+
def create_ios_project
|
13
|
+
check_already_exists!
|
14
|
+
puts "# Setting up project..."
|
15
|
+
copy_from_template
|
16
|
+
rename_project_to_name
|
17
|
+
find_and_replace_instances_of_template_to_name
|
18
|
+
create_ambientfile
|
19
|
+
run_ambientfile
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def check_already_exists!
|
25
|
+
return unless File.exists?(project_path)
|
26
|
+
|
27
|
+
puts "😱 #{name} already exists in the current directory"
|
28
|
+
exit 1
|
29
|
+
end
|
30
|
+
|
31
|
+
def check_name!
|
32
|
+
return if name
|
33
|
+
|
34
|
+
puts "😱 You must specify a project name when creating a new project"
|
35
|
+
puts "e.g. `ambient new MyProject`"
|
36
|
+
exit(1)
|
37
|
+
end
|
38
|
+
|
39
|
+
def run_ambientfile
|
40
|
+
Application.new(project_path).run_ambientfile
|
41
|
+
end
|
42
|
+
|
43
|
+
def create_ambientfile
|
44
|
+
Init.new(project_path, name).create_ambientfile
|
45
|
+
end
|
46
|
+
|
47
|
+
def copy_from_template
|
48
|
+
FileUtils.copy_entry ios_template_path, project_path
|
49
|
+
end
|
50
|
+
|
51
|
+
def rename_project_to_name
|
52
|
+
FileUtils.mv "#{project_path}/PRODUCTNAME",
|
53
|
+
"#{project_path}/#{name}"
|
54
|
+
FileUtils.mv "#{project_path}/PRODUCTNAME.xcodeproj",
|
55
|
+
"#{project_path}/#{name}.xcodeproj"
|
56
|
+
end
|
57
|
+
|
58
|
+
def find_and_replace_instances_of_template_to_name
|
59
|
+
files_in_project.each do |file|
|
60
|
+
find_and_replace(file, "PRODUCTNAME", name)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def project_path
|
65
|
+
path + "/#{name}"
|
66
|
+
end
|
67
|
+
|
68
|
+
def ios_template_path
|
69
|
+
File.expand_path("../../../templates/ios", __FILE__)
|
70
|
+
end
|
71
|
+
|
72
|
+
def files_in_project
|
73
|
+
Dir.glob(project_path + '/**/*').select { |path| File.file?(path) }
|
74
|
+
end
|
75
|
+
|
76
|
+
def find_and_replace(path, old, new)
|
77
|
+
text = File.read(path)
|
78
|
+
replace = text.gsub(old, new)
|
79
|
+
return if text == replace
|
80
|
+
File.open(path, "w") { |file| file.puts replace }
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|