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